RE

Reversing and Exploiting Dr. von Noizemans Nuclear Bomb

12 min read
By 0x434b
Reversing and Exploiting Dr. von Noizemans Nuclear Bomb

Note: Re-upload due to dead links :)

Yo! Life kept me more than busy, but now I've got a little more time on my hands. I decided to do a write up on the following binary, because it taught me some new things, compared to the easy reversemes I did before.

Binary

The binary is the one below and can reversed into its binary form by doing:

base64 -d < bomb.base64 | gunzip > bomb.bin && chmod +x bomb.bin
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

Initial Analysis

The file command reveals that it is a 32-bit ELF binary that is not stripped.

$ file bomb
bomb: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.15, BuildID[sha1]=e6a360ee322cefe6f3bb6110b5b587bc891d08fe, not stripped

First run

When first running the binary we can directly see that it is a multi stage binary consisting of 4 phases. It looks like only when we are able to figure out all 4 phases we will solve the challenge

So let's start one by one

Analysis phase 1 - Yellow

When selecting the yellow option in the main menu we're prompted for a password. So I loaded the whole thing into BinaryNinja and took a look at the yellow routine.

As yellow_preflight shows, this subroutine reads 10 bytes (0xa) user input from stdin via fgets and stores it into something BinaryNinja labeled as buffer. Afterwards, the input is compared byte for byte against a "fixed" sequence. So we can just convert the hex values to ASCII as all seven of them are in valid ASCII range.

res = ''
for i, v in enumerate([0x38, 0x34, 0x33, 0x37, 0x31, 0x30, 0x36, 0x35]):
	res += ''.join(chr(v))
print(res)
84371065

That's that!

Analysis Phase 2 - Green

So next up to the green phase. Same routine again. We're prompted for a password once again.

Let's dive back into BinaryNinja again.

So basically the green_preflight one again uses fgets to prompt for user input from stdin again. At most 0x14 bytes. However, only the first 0x8 bytes are taken into consideration in the call to strncmp against a hardcoded value. As dcaotdae is already 0x8 bytes in length we can just use this one as a password without having to care about filling the remaining free bytes in the user controlled input buffer...

That said, when using dcaotdae as a password it is getting accepted but the decision is overriden by good old NOIZEV so we did not solve that one yet..

So what did we overlook?

When double checking the responsible assembly we notice that whatever is returned by the strncmp operation (stored in EAX!) is undergoing the test eax, eax before deciding where to jump.

In case of us using dcaotdae as the password the strncmp is returning 0 since:

The strcmp() and strncmp() functions return an integer less than, equal to, or greater  than  zero if  s1  (or  the  first  n bytes thereof) is found, respectively, to be less than, to match, or be
greater than s2.

The directly following test eax, eax, which is doing a bitwise and on the two provided arguments and sets register flags accordingly:

TEST sets the zero flag, ZF, when the result of the AND operation is zero. If two operands are equal, their bitwise AND is zero when both are zero. TEST also sets the sign flag, SF, when the most significant bit is set in the result, and the parity flag,PF, when the number of set bits is even.

As test eax, eax == test 0, 0 == 0 & 0 which according to basic boolean logic is also 0. As a result the ZERO flag is set. Hence the jne 0x804998e is not taken and instead the control flow is redirected to 0x8049946, which is responsible for us still being stuck on the green phase.

call strncmp	; set eax==0 if match, else to 1
test eax, eax	; set ZF==1 if eax==0, else ZF==0
jne 0x804998e	; jump to 0x803998e if ZF==0

So the game plan is to take the jump to 0x804998e by forcing the strncmp to return != 0 right?

Wrong! So what is the deal here? Turns out us providing dcaotdae as the password is partially correct. To understand how to solve this properly we need to look very closely at what is responsible for re-engaging the 'fuse' and how local variables are setup on a function stack frame. Right now our situation looks like this:

Let's backtrack! Assuming we're still providing dcaotdae as the password, control flow would be redirected to this basic block:

At this point the following happens:

mov eax, [ebp-0x8]   ; eax=1
and eax, 1           ; yields eax=1
test eax, eax        ; doesn't set any flag
sete al              ; al=0 because it sets the byte in the operand to 1 if ZF is set, otherwise sets the operand to 0.
movzx eax, al	     ; eax=0
mov [ebp-0x8], eax   ; [ebp-0x8]=0
[...]
mov eax, [ebp-0x8]   ; eax=0
and eax, 0x1		 ; and 0,1 --> eax=0
test eax, eax		 ; 0 & 0 --> ZF set
sete al				 ; al=1, because ZF=1
movzx eax, al		 ; eax=1
mov [ebp-0x8], eax	 ; re-set [ebp-0x8]=1

So in this situation both eax and [ebp-0x8] are set to 1. The next direct jump to 0x804999a leads here:

mov eax, [ebp-0x8]	; eax=1 (still)
test eax, eax		; ZF=0
jne 0x80499ad		; goto 0x80499ad

And with that we would return to the main menu and get another chance with the green fuse still in place. So how do we solve this?

The answer lies within how local function variables are set up and that the provided user input via the fgets call is able to corrupt the value stored at [ebp-0x8]. How? Check this out:

It shows that the buffer, which is used for the fgets call and is provided to the green_preflight function is located at [ebp-0x14]. [ebp-0x8], which as we saw is determining the outcome of this function is pretty darn close to the buffer:

> pcalc 0x14 - 0x8
	12              	0xc               	0y1100

The difference between the buffer and the value stored at [ebp-0x8] is only 12 bytes, which logically makes it the buffer size too. However, remember that we're able to provide 20 bytes (0x14) to fgets. That means we can easily overflow into [ebp-0x8]! So we just saw that the value of [ebp-0x8] is set to 1 and even when providing the correct password (dcaotdae) this, let's say "flag" value is breaking it for us. As it seemingly is inconvient for us when this [ebp-0x8] is 1 overflowing into it and setting it to 0 should "reverse" the actions and keep the defused green phase in tact!

Why does adding "AAA" work? The answer lies in man fgets:

[...]. Reading stops after an EOF or a  newline. If  a  newline  is  read, it is stored into the buffer.  A terminating null byte ('\0') is stored after the last character in the buffer.

The added NULL byte is the one overflowing into [ebp-0x8]! This also shows that any 3 additional bytes do the job! And with that we solved phase 2 of 4!

Analysis phase 3 - Blue

Right when selection the blue phase we're greeted with:

As we do not know what the heck a circuit traversal path is at this point we just have to check the disassembly. Back to BinaryNinja:

blue_preflight offers nothing new as it just reads in up to 16 bytes into a buffer again. The main blue routine is quite a bit longer than all phases before but essentially it teaches us another very common data structure. So let's step through this:

This part sets up a memory address labeled as graph in [ebp-0x4]. So what the graph look like?

The start address, which is put into [ebp-0x8] directly points to the greyish marked area. Let's beautify this by quickly examining this memory in GDB:

This is already way more readable. But still looks kinda random. If you look closely you can see that the referenced addresses starting with 0x804cXXX are all within the graph space:

Afterwards the same memory address is loaded into eax and a value I labeled as start_val is put 4 bytes after the graph memory address.

int32_t start_val = 0x47bbfa96;

Then a loop construct is entered with at most 0xe iterations. As initially the loop counter is set to 0 control flow is continuing here:

Essentially, it loops over the user input via the loop counter and checks whether the current byte is an L, an R, or a newline character (==EOF).

Depending on the result the graph is accessed in different ways:

As an example, let's say our user input would have been a single 'L':

mov eax, [ebp-0x4]	; eax = 0x804c160 —▸ 0x804c19c —▸ ...
mov eax, [eax]		; eax = 0x804c19c —▸ ...
mov [ebp-0x4], eax	; set new head of graph to 0x804c19c —▸ ...

As an example, let's say our user input would have been a single 'R':

mov eax, [ebp-0x4]	; eax = 0x804c160 —▸ 0x804c19c —▸ ...
mov eax, [eax+0x8]	; eax = 0x804c168 —▸ 0x804c178 —▸ ...
mov [ebp-0x4], eax	; set new head of graph to 0x804c168 —▸ 0x804c178 —▸ ...

Then with the new head (start of the graph) set there are basically two interesting parts left.

The red box shows that whatever is the head of the graph in the current iteration is de-referenced at offset +0x4 and the value is XOR'd with the current value stored in start_val (I labeled it as "our_solution" in the screenshot). In the case of the loop breaking, either by fulfilling the loop condition or encountering a newline character the calculated value via multiple XOR operations is compared against a hard coded solution value:

uint32_t solution = 0x40475194;

So ultimately, we can narrow down the algorithm to something like this:

blue:
    start = 0x47bbfa96
    goal = 0x40475194
    loop_ctr = 0
    while loop_ctr < 0x14
        if L: head = Node->left
        else if R: head = Node->right
        else if \n: goto check
        else: boom

        start = start ^ *head+0x4
        loop_ctr += 1
 

check:
	return start == goal

We could manually calculate each step by following the head in GDB and XOR'ing it with the current value. As we do not know how to reach the expected value at the end brute-forcing this is a viable option. I did this with an IDAPython (for IDA7.5) script:

'''The number of possible combinations is 14 L or R’s because 1 byte is for ‘\n’ and the final byte for the string terminator, therefore 2^14 which is 16384 possible combinations.'''
from idc import get_wide_dword

def evaluate(string):
    ea = 0x0804c160
    x = 0x47bbfa96
    for i in string:
        if i == 'L':
            ea = get_wide_dword(ea)
        if i == 'R':
            ea = get_wide_dword(ea+8)
        if i == '\n':
            break
        x = get_wide_dword(ea+4) ^ x
    return x

ans = 0x40475194

for i in range(2 ** 14):
    string = ''.join(map(lambda a: 'L' if int(a) else 'R', bin(i)[2:]))
    if evaluate(string) == ans:
        print(string)

This outputs a ton of combinations:

Python>from idc import get_wide_dword

def evaluate(string):
    ea = 0x0804c160
    x = 0x47bbfa96
    for i in string:
        if i == 'L':
            ea = get_wide_dword(ea)
        if i == 'R':
            ea = get_wide_dword(ea+8)
        if i == '\n':
            break
        x = get_wide_dword(ea+4) ^ x
    return x

ans = 0x40475194

for i in range(2 ** 14):
    string = ''.join(map(lambda a: 'L' if int(a) else 'R', bin(i)[2:]))
    if evaluate(string) == ans:
        print(string)
Python>
LLRR
LLRRRRRR
LLRRLRLR
LLRRRLLRLL
LLRLLRRRLL
LLRLLRLLRR
LRLRRRLRLRLL
LRLRRRLLRLRL
LRLRLRLRRRLL
LRLRLRLRLLRR
LRLRLLRRRLRL
LRLRLLRLRLRR
LLRRRRRRRRRR
LLRRRRRRLRLR
LLRRRRLRRRLR
LLRRRRLRLRRR
LLRRRLRLRLRL
LLRRRLLLLLLL
LLRRLRRRRRLR
LLRRLRRRLRRR
LLRRLRLRRRRR
LLRRLRLRLRLR
LLRLRLRRRLRL
LLRLRLRLRLRR
LLRLLLLRRLLL
LLRLLLLLLLLR
LLLLRRLLRLLL
LLLLRRLLLLLL
LLLLLLRRRLLL
LLLLLLRLLLLR
LLLLLLLLRRLL
LLLLLLLLLLRR
LRLRRRLRLLLLLL
LRLRRRLLLLLRLL
LRLRLRLLLRRLLL
LRLRLRLLLLLLLR
LRLRLLLLRRLRLL
LRLRLLLLLRLLLR
LRLLLRRLRLRLLL
LRLLLRRLRLLLLL
LRLLLRRLLRLRLL
LRLLLRRLLLLLRL
LRLLLLRLRRRLLL
LRLLLLRLRLLLLR
LRLLLLRLLLRRLL
LRLLLLRLLLLLRR
LRLLLLLRRRLRLL
LRLLLLLRLRLLLR
LRLLLLLLLRRLRL
LRLLLLLLLLRLRR
LLRRRRRRRLLRLL
LLRRRRRLLRRRLL
LLRRRRRLLRLLRR
LLRRRRLRLLLRLL
LLRRRRLLLRRLLL
LLRRRLRLLLLRLL
LLRRRLLRRRRRLL
LLRRRLLRRRLLRR
LLRRRLLRRLRLLL
LLRRRLLRLLRRRR
LLRRLRRRLLLRLL
LLRRLRRLLRRLLL
LLRRLRLRRLLRLL
LLRRLRLLLRRRLL
LLRRLRLLLRLLRR
LLRRLLLRRRRLLL
LLRRLLLRRLRRLL
LLRRLLLRRLLLRR
LLRRLLLRLLRRLR
LLRLRLLLRRLRLL
LLRLRLLLLRLLLR
LLRLLRRRRRRRLL
LLRLLRRRRRLLRR
LLRLLRRRRLRLLL
LLRLLRRRLLRRRR
LLRLLRRLRRRLLL
LLRLLRRLRLRRLL
LLRLLRRLRLLLRR
LLRLLRRLLLRRLR
LLRLLRLLRRRRRR
LLRLLRLLRRLRLR
LLLRLLLLRLLLRR
LLLLRRLRLRLRLL
LLLLRRLRLLLLRL
LLLLLRLRRRLRLL
LLLLLRLRLRLLLR
LLLLLRLLLRRLRL
LLLLLRLLLLRLRR

One possible solution to finish this phase is 'LLRR'.

Analysis phase 4 - Red

If you made it up to here: Welcome to the last Phase! Don't worry this one is way shorter. We will be done soon!

red_preflight calls rand() three times without seeding the random number generator, which according to the man 3 rand page results in the seed always defaulting to 1 and hence stable values across invocations. The resulting random values are used to fill an array r[3]. At the end here the array is being looped over with each of the array fields being accessed by [loop_ctr * 4 + 0x804c264] with 0 < loop_ctr <= 2. Also we can see the array "field width" indicated by the offset 4 meaning that each value in the array is 4 bytes (== 32 bit integer size).

The main routine back in red is a simple algorithm that we can directly translate into a python script like this:

data_set = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"

r = [0x6B8B4567, 0x327B23C6, 0x643C9869]
flag = ""

for i in range(19):
    flag += data_set[r[2] & 0x1f]
    r[2] = (r[2] >> 5) | (r[1] << 27)
    r[1] = (r[1] >> 5) | (r[0] << 27)
    result = r[0] >> 5
    r[0] = r[0] >> 5

print(flag)
KDG3DU32D38EVVXJM64

Final words

That's the end. We managed to get through all 4 phases and enjoy this little reverseme style binary that teaches A LOT of fundamentals. If you're reading this and are new to binary analysis and reversing make sure to understand each step!

Flags:

  • yellow: 84371065
  • green: dcaotdaeXXX
  • blue: LLRR
  • red: KDG3DU32D38EVVXJM64