Software Reverse Engineering: Diffusing Phase 5

Abhinav Thakur
5 min readApr 11, 2022

Baby, I hate little secrets you keep from me x_x

The article continues the previous one where we diffused phase 4 of bomb binary. Let’s get started with phase 5 on Windows platform using Windbg.

This writeup is a part of the article series —

Rescue Mission: Phase_5

Looking at the disassembly for phase_5() (uf bomb!phase_4).

uf phase_5

We see a similar call for sscanf() while diffusing phase_4() . Let’s quicky have a look at the format string so that we know what type of user input it expects.

0:001> da 00007ff6`0377c220
00007ff6`0377c220 "%d %d"

Nice, it expects 2 integers as input and stores them at [rbp+64] (let’s say a) and [rbp+84h] (let’s say b) respectively. If we enter 2 or more than 2 integer as inputs the program takes the jump — jge bomb!phase_5+0x79 (00007ff6`037724d9) otherwise it calls explode_bomb() .

int a, b;// rbp+0x1a0 is &input_string[0]
if (sscanf ( input_string, "%d %d", a, b) < 2)
explode_bomb();
a = a & 0x0f;
if (a[0] == 0xf) explode_bomb(); // check #2
[rbp+4] = [rbp+24] = 0;

check #2 constraint forces the user to enter any value except 0xf (15) as the first integer in input string.

Validating Input

We now know where integer inputs are getting stored, [rbp+64h] is a and [rbp+84h] is b . We aim to reverse the below disassembly.

202 00007ff6`037724f6   cmp   dword ptr [rbp+64h],0Fh
202 00007ff6`037724fa je bomb!phase_5+0xc4 (00007ff6`03772524)
...
208 00007ff6`03772524 cmp dword ptr [rbp+4],0Fh
208 00007ff6`03772528 jne bomb!phase_5+0xd5 (00007ff6`03772535)
208 00007ff6`0377252a mov eax,dword ptr [rbp+84h]
208 00007ff6`03772530 cmp dword ptr [rbp+24h],eax
208 00007ff6`03772533 je bomb!phase_5+0xda (00007ff6`0377253a)
...
209 00007ff6`03772535 call bomb!ILT+945(explode_bomb)
A safe way out

We find a safe way out. To identify the inputs, we need to increment [rbp+4] until it reaches 0xf and peek into the value of [rbp+24] when the code flow reaches the SAFE EXIT.

There is one obstruction here, before reaching SAFE EXIT, it has to pass through if (a == 0xf) condition. The question now is —

How can a possibly store the value of 0xf after it passes check #2 (as seen before) ?

Solving 'a' mystery

Demystifying more disassembly —

203 00007ff6`037724fc   mov     eax,dword ptr [rbp+4]
203 00007ff6`037724ff inc eax
203 00007ff6`03772501 mov dword ptr [rbp+4],eax
204 00007ff6`03772504 movsxd rax,dword ptr [rbp+64h]
204 00007ff6`03772508 lea rcx,[bomb!n1+0x20 (00007ff6`0377f1d0)]
204 00007ff6`0377250f mov eax,dword ptr [rcx+rax*4]
204 00007ff6`03772512 mov dword ptr [rbp+64h],eax
205 00007ff6`03772515 mov eax,dword ptr [rbp+64h]
205 00007ff6`03772518 mov ecx,dword ptr [rbp+24h]
205 00007ff6`0377251b add ecx,eax
205 00007ff6`0377251d mov eax,ecx
205 00007ff6`0377251f mov dword ptr [rbp+24h],eax
206 00007ff6`03772522 jmp bomb!phase_5+0x96 (00007ff6`037724f6) way_out
Tr

Our pseudocode till now uses goto statements with labels. Let’s write a final pseudocode that replaces this goto control-flow-transfers into do-while loop code construct.

Pseudocode for phase 5

Pseudocode for bomb!phase_5

This is how I managed to identify a do-while loop code construct. This loop increments [rbp+4] on each iteration and terminates using SAFE EXIT when a == 0xf . This is all we wanted, we just need to make sure that the loop iterates exactly 0xf times (i.e. expected value of [rbp+4] for SAFE EXIT) which depends on value of a .

Escape through the SAFE EXIT, fast !

To reach SAFE EXIT we need —

  • To ensure that loop iterates exactly 0xf times. To control the number of iterations we need to control the value inside a.
  • Initial value of a is in our control whereas further value of a is controlled directly by value inside base[a].

Therefore, the first integer we input (value of a) will act as an index into base[] after which the value read from base[a] will act as index for the next iteration (somewhat like base[base[a]]).

Let’s have a look at first 0xf values (that we can leverage to iterate the loop) at location base[].

0:001> dd 00007ff6`0377f1d0 Lf
00007ff6`0377f1d0 0000000a 00000002 0000000e 00000007
00007ff6`0377f1e0 00000008 0000000c 0000000f 0000000b
00007ff6`0377f1f0 00000000 00000004 00000001 0000000d
00007ff6`0377f200 00000003 00000009 00000006

We now need to chain the indices of these 15 values in such a way that 0x0000000f is indexed at last. A smart approach will be to start backwards, from value 0x0000000f itself. Remember, this value will mark as an index from this base (00007ff6`0377f1d0) itself. Initial value of a (index) will be 6 (base[5]).

Index Chain (reverse order)

Therefore, to reach value of 0xf after 15 iterations, we need to supply an initial value of index : a = 5 . Let’s supply value of a and break at instruction — 00007ff6`03772530 cmp dword ptr [rbp+24h],eax which compares [rbp+24h] with user-supplied integer in b (stored inside eax here). I am supplying a dummy string of 5 7337 to phase_5 as input. Let’s examine the value at [rbp+24] to find the exact value it compares with b .

0:000> .restart
0:000> bp bomb!phase_5
0:000> g
0:000> pa 00007ff6`03772530
value for second integer input

There we see, it compares eax (0x1ca9 or 7337) with 0x73 (115 in decimals). Let’s try giving this as input !

phase_5 diffued x_x

Finally, we successfully diffuse phase_5() of bomb binary.

Epilogue

Phase 5 was more interesting than phase 4 that involved us to not only identify a do-while loop but also forced us to control the code flow of the loop by examining memory and analyzing how our input can effect the chain of memory reads to escape through a SAFE EXIT. Controlling code flow is the most important attribute of a true attacker x_x

Let’s continue right into the writeup for diffusing phase_6.

Cheers,
Abhinav Thakur
(a.k.a
compilepeace)

Connect on Linkedin

--

--