Contents

A week with Angstromctf 2020

Contents

So this post’s gonna be in English. Why? I don’t know, just feel like writing in English 🙂

I spent the whole week doing nothing but solving the RE challenges from Angstromctf (so far, I only managed to solve half of them, the easy half, that is). So I figured I should write a little bit about those challenges and what I’ve learned trying to solve them. Ok, here we go.


Patcherman

The first thing I did was opening the binary in IDA, and then, this thing popped up on my screen.

../assets/img/angstrom2020/angstrom1.png{: .mx-auto.d-block :}

So I just went on Google and searched for “SHT table”. It turned out that an ELF file consists of many sections, such as .got, .text, .rodata,… and the SHT (section header table) is kinda like an index for these sections. It contains information like: address, size,… of a section. In the file’s header, we have the “SHT file offset”, which shows us the “distance” between SHT and the start of our binary. The challenge did say something about the header, so maybe this offset was modified.

And indeed it was. The offset of our file was 0, which is really weird compared to other files. While searching for a way to calculate the right offset (a senior in my team solved this chall already and he brute-forced the correct offset, but I wanted to find another way because…well, I don’t know how to brute-force this), I came across a CTF write-up. I learned that the SHT table usually locates at the end of the binary and long story short I got this:

SHT_offset=(binary_length)-(entry_size)(number_of_entries)*

I tried that and it worked. Problem solved? Nope 😦 I checked the file one more time and see this:

../assets/img/angstrom2020/angstrom2.png{: .mx-auto.d-block :}

The value of “dword_601050” is not “1337BEEFh”, so it will always jump to the false branch. Obviously, I tried to patch “dword_601050” to “1337BEEF”. Luckily, that was the solution, and I got the flag.

Note:

– Fixing the header was totally unnecessary 🙂 . IDA can run just fine with a wrong SHT offset. What interesting is, gdb can’t, and I still don’t know why.


A happy family

This is a 2-in-1 challenge: a pwn and a RE (of course I only solved the RE one), both have the same source code and this led me to a mistake. First, I ran the binary.

../assets/img/angstrom2020/angstrom3.png{: .mx-auto.d-block :}

Then checked the source:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
int main() {
    setvbuf(stdout, NULL, _IONBF, 0);
    int fval = fork();
    alarm(300);  // sometimes the child processes don't die
    prctl(PR_SET_PDEATHSIG, SIGTERM);
    if (fval == 0) {
        setfif(getpid());
        child();
    } else {
        setfif(fval);
        parent();
    }
1
2
3
4
5
6
7
8
void parent() {
    char inp[PS + 1];
    puts("  \\   /\n /-----\\\n |  o  |\n | \\|/ |\n | / \\ |\n \\-----/\n  |   |");
    puts("This TV show is so boring, it's just a guy standing still.");
    puts("I need a GOOD TV show to watch with my child.");
    puts("Which TV show do you recommend?");
    fgets(inp, PS + 1, stdin);
  }

The string that got printed and the fgets() was all in parent() funcion. So I assumed that child() function was for the pwn challenge and ignored it. Of course that was wrong. If it hasn’t been for the hint from Catafact, I couldn’t have solved this 😀 .

But there was still something I don’t understand: How can both parent() and child() get executed? (There’s an if statement there). So of course, Google.

And then I learned about fork(). This function call another process (we call it the “child process”) that runs at the same time with the caller. So basically after f0rk(), we will have 2 processes running together. In the “child process” the return value of fork() is 0, and in the caller, the id of our “child process” is returned.

So in this challenge, we have 2 different process: the “parent process” (the caller) will execute function parent() and the child process will execute child().

After I got that, the challenge didn’t seem hard anymore. The program takes my input, and converts them to 4 “number” in “base 13”. Instead of “012345…”, this “base 13” has the letters in “angstromctf20” for digits. After the conversion, the program performs a comparison:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
if (strcmp(c1, "artomtf2srn00tgm2f") || strcmp(c2, "ng0fa0mat0tmmmra0c") ||
        strcmp(c3, "ngnrmcornttnsmgcgr") || strcmp(c4, "a0fn2rfa00tcgctaot")) 
    {
        printf("Oh I've watched that show, I don't really like it.\n");
        exit(1);
    }
    // This is the flag for the rev challenge
    puts("Wait that name sounds familiar...");
    sleep(1);
    puts("...");
    sleep(1);
    printf("Oh I know why! It's because the flag is actf{% raw  %}{%s}{% endraw %}!\n", inp);

All I needed to do was writing a script to convert those 4 strings into the correct flag. Done!


Autorev, Assemble!

Before this challenge, I had never heard about anything that can help me solve RE challenge automatically, so I was pretty confused at first. But thanks to Cothan, a senior in the team, I learned about Angr and what can it do. I won’t write much about this challenge now because I haven’t fully understand the way Angr works and its syntax yet. So maybe later, then.