Points: 160 Solves: 68 Category: Reverse Engineering Description:

It’s-a me, Mario! We’re there. All I can see are turtle tracks. Whaddaya say we give Bowser the old Brooklyn one-two? hello-joe

Write-up

$ file hellojoe-d4fc787734bc9d0fb867aa7ade904f07   
hellojoe-d4fc787734bc9d0fb867aa7ade904f07: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, stripped   

If we start the binary, we see usage information. It takes the FLAG / password as command line argument.

➜  9447  ./hellojoe-d4fc787734bc9d0fb867aa7ade904f07    
Usage: ./hellojoe-d4fc787734bc9d0fb867aa7ade904f07 FLAG   
➜  9447    

When we look at the disassembly we see memcpy() being called several times in main() to copy chunks of data from the Data Section to the Stack Section.

memcpy

Little after that we see a random chunk being called and executed from the stack.

calls

What happens here is data from /dev/urandom is used as seed for rand(), followed by with modulo 6 to determine which of the previously copied chunks of data is going to be executed. Each of those chunks are individual flag checking functions. Because it will take too long to step through all 6 of the functions I will just give you an example snippet of how they check for right flag. Each function checks a specific index character from our input against 3 valid characters like so:

00000000006014e6         movzx      rax, byte [ds:rdi]      # Pick char from input
00000000006014ea         inc        rdi
00000000006014ed         cmp        al, 0x30                # '0' is valid char
00000000006014ef         je         0x601508

00000000006014f5         cmp        al, 0x37                # '7' is valid char
00000000006014f7         je         0x601508

00000000006014fd         cmp        al, 0x38                # '8' is valid char
00000000006014ff         je         0x601508

0000000000601505         xor        eax, eax                # If incorrect return 0 (fail)
0000000000601507         ret

As you can see 0, 7 and 8 are all valid characters at that index. None of the functions check all of the characters at once, some indexes are going to be skipped but all indexes are going to be checked at least twice against different values but a single common character. Here is a table of which function checks which index with what characters.

char | function 2 | function 3 | function 4 | function 5 | function 6 |   
-----|------------|------------|------------|------------|------------|   
1    | ?          | ?          | ?          | ?          | ?          |  
2    | ?          | ?          | ?          | ?          | ?          |
3    | ?          | ?          | ?          | ?          | ?          |
4    | ?          | ?          | ?          | ?          | ?          |
5    | ?          | ?          | ?          | ?          | ?          |
6    | ?          | 29f        | 79b        | ?          | 189        |
7    | ?          | 14c        | 47f        | ?          | 04f        |
8    | 5de        | 16e        | ?          | 7ef        | ?          |
9    | aef        | ?          | ?          | 02a        | 9ae        |
10   | ?          | 59a        | 5ab        | 57b        | ?          |
11   | ?          | 19e        | ade        | ?          | 5ae        |
12   | 023        | ?          | ?          | 038        | 36e        |
13   | 27d        | 278        | ?          | 02e        | ?          |
14   | ?          | 78f        | 57f        | ?          | 8cf        |
15   | ?          | ?          | 23b        | 2bf        | 269        |
16   | bdf        | 57b        | bdf        | ?          | ?          |
17   | ?          | ?          | 358        | 35d        | 5ef        |
18   | ?          | abe        | abf        | 7be        | ?          |
19   | ?          | 35d        | ?          | 35b        | 349        |
20   | ?          | 7af        | 67f        | 7de        | ?          |
21   | 25d        | ?          | bdf        | ?          | 6cd        |
22   | ?          | 49b        | 9ae        | ?          | 39e        |
23   | 24a        | ?          | 49a        | ?          | 14d        |
24   | 17d        | 67f        | ?          | 79a        | ?          |
25   | ?          | 2ce        | 9de        | ?          | 25e        |
26   | 3ef        | ?          | 16e        | 47e        | ?          |
27   | 18a        | 7ae        | 13a        | ?          | ?          |
28   | ?          | 39b        | 34a        | ?          | 3bf        |
29   | 59a        | 1ad        | ?          | ?          | 2af        |
30   | ?          | 37a        | 136        | 3ad        | ?          |
31   | 078        | ?          | ?          | 28c        | 078        |
32   | 49a        | 29d        | ?          | ?          | 79b        |
33   | 39f        | 037        | ?          | ?          | 023        |
34   | 236        | ?          | 26e        | ?          | 24b        |
35   | ?          | 7ad        | 34a        | ?          | ade        |
36   | 8ce        | 78e        | ?          | 2ce        | ?          |
37   | 145        | ?          | 179        | ?          | 1be        |
38   | ?          | ?          | ?          | ?          | ?          |
39   | nul        | nul        | nul        | nul        | nul        |

Question marks are characters that are being checked at that index by each specific function. Function 1 checks if the input starts with ‘9447{‘, consists only of chars ‘0123456789abcdef’ and ends with ‘}’. Knowing each valid pair characters at each index we can build the only valid flag ‘9447{94ea5e32f2b5b37d947eea3a38932ae1}’.

To hinder our analysis there were anti-stepping instruction after each character check/skip using the following method:

00000000006015e4         rdtsc
00000000006015e6         test       eax, 0xfffff
00000000006015eb         jne        0x6015e4

Instruction ‘rdtsc’ returns CPU time-stamp counter, which is being checked if it ends with 0xfffff, if not it jumps back 9 bytes making it infinite loop. As we single-step our chances of hitting 0xfffff are probably less than winning the lotto :). So for the most part I just used static analysis by converting the functions in the Data Segment to executable code.

Overall it was a fun challenge. My thanks to the guys from 9447.