Showing posts with label reverse engineering. Show all posts
Showing posts with label reverse engineering. Show all posts

Monday, September 3, 2012

nebula level14

Level details:
This program resides in /home/flag14/flag14 . It encrypts input and writes it to standard output. An encrypted token file is also in that home directory, decrypt it :)
This time we will have to figure out how the encryption routine works to come up with a decryption routine in order to decrypt the token.
The encryption algorithm turns out to be so easy that you can figure it out by simply experimenting with the binary. But if we were to employ a systematic approach, we'd have to analyze the binary, which is exactly what we will do. This time we'll use objdump as our disassembler. Using objdump -D to disassemble, we find that the encryption routine lies in <main>:
level14@nebula:~$ objdump -dMintel ~flag14/flag14

/home/flag14/flag14:     file format elf32-i386

...

08048464 <main>:
...
 8048482:       c7 44 24 2c 00 00 00    mov    DWORD PTR [esp+0x2c],0x0
...
 80484e1:       90                      nop
 80484e2:       c7 44 24 08 40 00 00    mov    DWORD PTR [esp+0x8],0x40
 80484e9:       00
 80484ea:       8d 44 24 3c             lea    eax,[esp+0x3c]
 80484ee:       89 44 24 04             mov    DWORD PTR [esp+0x4],eax
 80484f2:       c7 04 24 00 00 00 00    mov    DWORD PTR [esp],0x0
 80484f9:       e8 52 fe ff ff          call   8048350 <read@plt>
 80484fe:       89 44 24 34             mov    DWORD PTR [esp+0x34],eax
 8048502:       83 7c 24 34 00          cmp    DWORD PTR [esp+0x34],0x0
 8048507:       7f 0c                   jg     8048515 <main+0xb1>
 8048509:       c7 04 24 00 00 00 00    mov    DWORD PTR [esp],0x0
 8048510:       e8 6b fe ff ff          call   8048380 <exit@plt>
 8048515:       c7 44 24 30 00 00 00    mov    DWORD PTR [esp+0x30],0x0
 804851c:       00
 804851d:       eb 29                   jmp    8048548 <main+0xe4>
 804851f:       8d 44 24 3c             lea    eax,[esp+0x3c]
 8048523:       03 44 24 30             add    eax,DWORD PTR [esp+0x30]
 8048527:       0f b6 00                movzx  eax,BYTE PTR [eax]
 804852a:       89 c2                   mov    edx,eax
 804852c:       8b 44 24 2c             mov    eax,DWORD PTR [esp+0x2c]
 8048530:       01 d0                   add    eax,edx
 8048532:       89 c2                   mov    edx,eax
 8048534:       8d 44 24 3c             lea    eax,[esp+0x3c]
 8048538:       03 44 24 30             add    eax,DWORD PTR [esp+0x30]
 804853c:       88 10                   mov    BYTE PTR [eax],dl
 804853e:       83 44 24 2c 01          add    DWORD PTR [esp+0x2c],0x1
 8048543:       83 44 24 30 01          add    DWORD PTR [esp+0x30],0x1
 8048548:       8b 44 24 30             mov    eax,DWORD PTR [esp+0x30]
 804854c:       3b 44 24 34             cmp    eax,DWORD PTR [esp+0x34]
 8048550:       7c cd                   jl     804851f <main+0xbb>
 8048552:       8b 44 24 34             mov    eax,DWORD PTR [esp+0x34]
 8048556:       89 44 24 08             mov    DWORD PTR [esp+0x8],eax
 804855a:       8d 44 24 3c             lea    eax,[esp+0x3c]
 804855e:       89 44 24 04             mov    DWORD PTR [esp+0x4],eax
 8048562:       c7 04 24 01 00 00 00    mov    DWORD PTR [esp],0x1
 8048569:       e8 32 fe ff ff          call   80483a0 <write@plt>
 804856e:       89 44 24 38             mov    DWORD PTR [esp+0x38],eax
 8048572:       83 7c 24 38 00          cmp    DWORD PTR [esp+0x38],0x0
 8048577:       0f 8f 64 ff ff ff       jg     80484e1 <main+0x7d>
...
The above is the C equivalent of a for loop inside a do-while loop. The input is being read in 64 (0x40) byte chunks, then each byte is added the value of it's (global) offset. (by global offset I mean that the offset is preserved across different chunks.)

The C equivalent code would look like:
int c = 0;
int i;
int rd;
unsigned char buffer[64];

do {
    rd = read(STDIN_FILENO, buffer, sizeof(buffer));
    if(rd <= 0)
        exit(0);

    for(i = 0; i < rd; i++)
        buffer[i] += c++;
} while(write(STDOUT_FILENO, buffer, rd) > 0);
Like I said, we could deduce the above routine by using purely observational analysis:
level14@nebula:~$ ~flag14/flag14 -e
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmno9
Constructing the decryption routine consists of changing the += to -=
If you've noticed, we can reuse the flag14 binary to do the decryption for us by applying a simple patch here:
 8048530:       01 d0                   add    eax,edx
 8048532:       89 c2                   mov    edx,eax
We could change the add to a sub, but then we'd have to negate the result (because we would essentially be computing c - buffer[i], while we need -(c - buffer[i]) = buffer[i] - c). But we can't fit the 'neg eax' (2 bytes) and still put the result in edx. We'll have to come up with a better patch.
What if we could simply patch in 'sub edx, eax' and nop out the 'mov edx, eax' that follows? 2 bytes for the sub and 2 bytes for the nops, seems good.
But we can still do better... We can nop out the 'add eax, edx' and put the 'sub edx, eax' instead of 'mov edx, eax'. That's still 4 bytes, but notice that we can reuse the ModR/M byte of the mov instruction for our sub. That's 3 bytes:
level14@nebula:~$ # nop = 0x90
level14@nebula:~$ # sub edx, eax = 0x29 0xc2
level14@nebula:~$ echo -ne '\x90\x90\x29' > patch
level14@nebula:~$ cp ~flag14/flag14 flag14_decrypt
level14@nebula:~$ dd if=patch of=flag14_decrypt bs=1 seek=1328 conv=notrunc
3+0 records in
3+0 records out
3 bytes (3 B) copied, 0.000137174 s, 21.9 kB/s
level14@nebula:~$ ./flag14_decrypt -e < ~flag14/token
8457c118-887c-4e40-a5a6-33a25353165
                                   ▒level14@nebula:~$ su flag14 -c getflag
Password:
You have successfully executed getflag on a target account
We got another flag for just 3 bytes ;)

~ Dmitry

nebula level13

Level details:
There is a security check that prevents the program from continuing execution if the user invoking it does not match a specific user id.
This one is quite nice. Source code:

This program requires us to run it with a UID of 1000 in order to print the token. But let's think where that token actually comes from. The fact that not all of the source code is shown indicates that the token may be somehow generated (or even hardcoded) and then printed to us. The flag13 binary being SUID serves no purpose. We can easily reverse engineer how the token is generated. You could go for a static analysis approach and use a disassembler (I prefer IDA). This is however one of the cases where a dynamic approach is ideal. Since we don't need the elevated UID from the setuid bit, we can simply make getuid return 1000 ourselves. This could be done using LD_PRELOAD and writing our own getuid function in a library, but there's an even easier way. Simply use a debugger and modify the return value:
level13@nebula:~$ gdb -q ~flag13/flag13
Reading symbols from /home/flag13/flag13...(no debugging symbols found)...done.
(gdb) disas main
Dump of assembler code for function main:
   0x080484c4 <+0>:     push   %ebp
   0x080484c5 <+1>:     mov    %esp,%ebp
   0x080484c7 <+3>:     push   %edi
   0x080484c8 <+4>:     push   %ebx
   0x080484c9 <+5>:     and    $0xfffffff0,%esp
   0x080484cc <+8>:     sub    $0x130,%esp
   0x080484d2 <+14>:    mov    0xc(%ebp),%eax
   0x080484d5 <+17>:    mov    %eax,0x1c(%esp)
   0x080484d9 <+21>:    mov    0x10(%ebp),%eax
   0x080484dc <+24>:    mov    %eax,0x18(%esp)
   0x080484e0 <+28>:    mov    %gs:0x14,%eax
   0x080484e6 <+34>:    mov    %eax,0x12c(%esp)
   0x080484ed <+41>:    xor    %eax,%eax
   0x080484ef <+43>:    call   0x80483c0 
   0x080484f4 <+48>:    cmp    $0x3e8,%eax
   0x080484f9 <+53>:    je     0x8048531 
0x080484fb <+55>: call 0x80483c0 0x08048500 <+60>: mov $0x80486d0,%edx 0x08048505 <+65>: movl $0x3e8,0x8(%esp) 0x0804850d <+73>: mov %eax,0x4(%esp) 0x08048511 <+77>: mov %edx,(%esp) 0x08048514 <+80>: call 0x80483a0 ---Type to continue, or q to quit---q Quit (gdb) break *main+48 Breakpoint 1 at 0x80484f4 (gdb) commands 1 Type commands for breakpoint(s) 1, one per line. End with a line saying just "end". >set $eax = 1000 >c >end (gdb) r Starting program: /home/flag13/flag13 Breakpoint 1, 0x080484f4 in main () your token is b705702b-76a8-42b0-8844-3adabbe5ac58 [Inferior 1 (process 1330) exited with code 063] (gdb)
And there's our token :)
Keep in mind that when debugging a SUID binary, it actually runs as a normal binary, effectively dismissing the setuid bit. And LD_PRELOAD wouldn't work with a SUID binary.

Now go ahead and get the flag:
level13@nebula:~$ su flag13 -c getflag
Password:
You have successfully executed getflag on a target account

~ Dmitry

Wednesday, August 1, 2012

Google PageRank checksum algorithm

The Google PageRank functionality in Google Toolbar works by querying Google's server for information on the PageRank of a specific page. This might seem easy enough to implement in your own program/website, but the problem is that the toolbar calculates a checksum on the page URL before querying the server, and the server only responds if the checksum is correct. Fortunately the checksum algorithm was reverse engineered from Google Toolbar 7. I was provided the hand decompiled version of the algorithm in C from a friend. Then I went ahead and rewrote it in PHP for web development usage. You can find both versions below.

As an example, the query URL for the page 'http://en.wikipedia.org/wiki/Cypherpunk' is http://toolbarqueries.google.com/tbr?client=navclient-auto&features=Rank&q=info:http://en.wikipedia.org/wiki/Cypherpunk&ch=783735859783

Any other query with a checksum other than 783735859783 will result in a '403 forbidden' response.
Enjoy.

C Version (original): PHP Version:

~ Dmitry