Defcon Quals 19 - Retro Revisited 300

This weekend we participated in the defcon quals and I'd like to share some of my solutions.

After downloading the file and extracting the archive we can see that there are two files.

The first one is a linux binary:

ptdeb:/tmp# file retro300
retro300: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, stripped

The second one is a sqlite3 database:

ptdeb:/tmp# file auth.db 
auth.db: SQLite 3.x database

When you try to run it you'll see that the user "retro300" is needed to launch the binary.

Let's have a quick look what data is available in the sqlite3 database:

ptdeb:/tmp# sqlite3 auth.db 
SQLite version 3.7.3
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> select * from sqlite_master;
table|users|users|2|CREATE TABLE users (id INTEGER PRIMARY KEY, user TEXT, pin TEXT)
sqlite> select * from users;
1|aaron|7345
2|dave|3245
3|bob|
6|merc|2345
7|mars|3473
8|jupi|1234
9|jeff|1315
sqlite> 

Ok nice we have some authentication credentials handy.

Now let's run the binary and connect to it:

ptdeb:/tmp# nc 127.0.0.1 5500
aaaa
ptdeb:/tmp# 

nothing happens, no login procedure so it's waiting for some other input first.
If we look in IDA we can see that before the login window is displayed the input is compared to "letmenpls"

.text:08049D5F                 mov     byte ptr [eax], 0
.text:08049D62                 mov     eax, s2
.text:08049D67                 mov     [esp+4], eax    ; letmeinpls
.text:08049D6B                 lea     eax, [ebp+s1]
.text:08049D71                 mov     [esp], eax      ; s1
.text:08049D74                 call    _strcmp
.text:08049D79                 test    eax, eax

let's try again:

ptdeb:/tmp# nc 127.0.0.1 5500
letmeinpls
                                           _____ ______ _____ _     _ 
               __    ____     ____/ ____(_)   | |
          ____/ /___/ / /____  / /__     | (___ | |__ | |     _  __| |
         / __  / __  / __/ _ \/ //_/      \___ \|  __|| |    | |/ _` |
        / /_/ / /_/ / /_/  __/ ,<         ____) | |___| |____| | (_| |
        \__,_/\__,_/\__/\___/_/|_|       |_____/|______\_____|_|\__,_|    (beta)


    ( cause everyone is looking for a new provider right?!)

Username:^C

Good we can access the login now, so let's see if we can use the informations retrieved from the auth.db file.

I wanted to strace the program, to see what's happening, but was stopped by a small antidebugging measurement:

child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0xb75f5728) = 7766
[pid  7739] close(4)                    = 0
[pid  7739] accept(3,  <unfinished ...>
[pid  7766] close(3)                    = 0
[pid  7766] read(4, "l", 1)             = 1
[pid  7766] read(4, "e", 1)             = 1
[pid  7766] read(4, "t", 1)             = 1
[pid  7766] read(4, "m", 1)             = 1
[pid  7766] read(4, "e", 1)             = 1
[pid  7766] read(4, "i", 1)             = 1
[pid  7766] read(4, "n", 1)             = 1
[pid  7766] read(4, "p", 1)             = 1
[pid  7766] read(4, "l", 1)             = 1
[pid  7766] read(4, "s", 1)             = 1
[pid  7766] read(4, "\n", 1)            = 1
[pid  7766] ptrace(PTRACE_TRACEME, 0, 0x1, 0) = -1 EPERM (Operation not permitted)
[pid  7766] exit_group(1)               = ?
Process 7766 detached

If we look in IDA what's happening after the first input is read, we can find a ptrace anti debugging mechanism:

 //----- (080494A3) --------------------------------------------------------
 __int32 __cdecl sub_80494A3()
 {
   __int32 result; // eax@1
   result = ptrace(0, 0, 1, 0);
   if ( result &lt; 0 )
     exit(1);
   return result;
 }

Just patch the trace call with nops and all is fine.

Comparing files retro300 and RETRO30CHED
000014C8: E8 90
000014C9: 87 90
000014CA: F5 90
000014CB: FF 90
000014CC: FF 90

Now we can try again to login with the credentials we got from auth.db:

ptdeb:/tmp# nc 127.0.0.1 5500
letmeinpls
                                           _____ ______ _____ _     _ 
               __    ____       __        / ____|  ____/ ____(_)   | |
          ____/ /___/ / /____  / /__     | (___ | |__ | |     _  __| |
         / __  / __  / __/ _ \/ //_/      \___ \|  __|| |    | |/ _` |
        / /_/ / /_/ / /_/  __/ ,<         ____) | |___| |____| | (_| |
        \__,_/\__,_/\__/\___/_/|_|       |_____/|______\_____|_|\__,_|    (beta)


    ( cause everyone is looking for a new provider right?!)

Username:jupi
Passcode:1234
Bad Username or Pin

as we can see the login was not successful, so another check must be implemented.

If we look at the login check function, we can see it checks if the input is 14:

.text:08049E73                 mov     [ebp+var_18], eax
.text:08049E76                 cmp     [ebp+var_18], 14
.text:08049E7A                 jz      short loc_8049EAC

so we can check and see what happens if we enter 14 characters.
This time you can see that it goes further:

[pid  7775] send(4, "Passcode:", 9, 0)  = 9
[pid  7775] read(4, "1", 1)             = 1
[pid  7775] read(4, "2", 1)             = 1
[pid  7775] read(4, "3", 1)             = 1
[pid  7775] read(4, "4", 1)             = 1
[pid  7775] read(4, "1", 1)             = 1
[pid  7775] read(4, "1", 1)             = 1
[pid  7775] read(4, "1", 1)             = 1
[pid  7775] read(4, "1", 1)             = 1
[pid  7775] read(4, "1", 1)             = 1
[pid  7775] read(4, "1", 1)             = 1
[pid  7775] read(4, "1", 1)             = 1
[pid  7775] read(4, "1", 1)             = 1
[pid  7775] read(4, "1", 1)             = 1
[pid  7775] read(4, "1", 1)             = 1
[pid  7775] read(4, "\n", 1)            = 1
[pid  7775] getcwd("/home/retro300", 512) = 15
[pid  7775] stat64("/home/retro300/auth.db", {st_mode=S_IFREG|0755, st_size=2048, ...}) = 0
[pid  7775] open("/home/retro300/auth.db", O_RDWR|O_CREAT|O_LARGEFILE, 0644) = 3
[pid  7775] fcntl64(3, F_GETFD)         = 0

therefore the passcode input needs to be 14, but we don't know what the other 10 characters need to be.

If we analyse the code further we will see one function doing some time computation and a strcmp, I checked by single stepping through the code that this function is the correct function to check for the 10 additional numbers :)

  v3 = time(0);
  v18 = v3;
  v19 = v3 - v3 % 0x258u;
  v20 = 0;
  while ( v20 &lt;= 149999 )
  {
    v17 = sub_80494E0(v17, v11);
    if ( v11 == v19 )
    {
      v4 = sub_80494F8(v17);
      sprintf((char *)&amp;v6, &quot;%0.10u&quot;, v4 * (a1 + 1));
      v9 = 0;
    }
    v11 += v10;
    ++v20;
  }
  if ( !strncmp((const char *)&amp;v6, a2, 0xAu) )
    v5 = 0;
  return v5;

Add a break point at 0xa and run it again:

[0073:08049D0A]---------------------------------------------------------[ code]
0x8049d0a:	call   0x8048c84 <strncmp@plt>
0x8049d0f:	test   eax,eax
0x8049d11:	jne    0x8049d1a
0x8049d13:	mov    DWORD PTR [ebp-0xc],0x0
0x8049d1a:	mov    eax,DWORD PTR [ebp-0xc]
0x8049d1d:	leave  
------------------------------------------------------------------------------

Breakpoint 1, 0x08049d0a in ?? ()

now we can check what our input is compared to:

gdb> x/s 0xbffffa65
0xbffffa65:	 "3407789347"
gdb> x/s 0xbffffbb4
0xbffffbb4:	 "1111111111"

our string "1111111111" and expected was "3407789347".

now we can run the binary again and use "3407789347" as the second part.

ptdeb:/tmp# nc 127.0.0.1 5500
letmeinpls
                                           _____ ______ _____ _     _ 
               __    ____       __        / ____|  ____/ ____(_)   | |
          ____/ /___/ / /____  / /__     | (___ | |__ | |     _  __| |
         / __  /  / __/ _ \/ //_/      \___ \|  __|| |    | |/ _` |
        / /_/ / /_/ / /_/  __/ ,<         ____) | |___| |____| | (_| |
        \__,_/\__,_/\__/\___/_/|_|       |_____/|______\_____|_|\__,_|    (beta)


    ( cause everyone is looking for a new provider right?!)

Username:jupi
Passcode:12343407789347
   DDTEK VPN console         
                             
    Choose an option:        
       1:  change pin        
       2:  re-sync sec token 
       3:  add user          
       4:  change username   
       5:  exit 

there we go, now we have access to the menu.
The only part missing is to figure out how to get the key.

If you look further in IDA you will see that there's a function that will read the key for you:

.text:080496F7                 push    ebp
.text:080496F8                 mov     ebp, esp
.text:080496FA                 sub     esp, 48h
.text:080496FD                 mov     dword ptr [esp+4], 0 ; oflag
.text:08049705                 dword ptr [esp], offset file ; &quot;/home/retro300/key&quot;
.text:0804970C                 call    _open

If we look at the menu handling function, we can see that if we choose something that's not there we get a "please select from the options present".

which is handled here:

.text:0804A08C loc_804A08C:                            ; CODE XREF: sub_8049D1F+34B^Xj
.text:0804A08C                 lea     eax, [ebp+nptr]
.text:0804A08F                 mov     [esp], eax      ; nptr
.text:0804A092                 call    _atoi
.text:0804A097                 mov     [ebp+var_C], eax
.text:0804A09A                 cmp     [ebp+var_C], 9
.text:0804A09E                 ja      short loc_804A111
.text:0804A0A0                 mov     eax, [ebp+var_C]
.text:0804A0A3                 shl     eax, 2
.text:0804A0A6                 add     eax, offset off_804A918
.text:0804A0AB                 mov     eax, [eax]
.text:0804A0AD                 jmp     eax

we can look at off_804A918 to see a list of available options:

.rodata:0804A918 off_804A918     dd offset loc_804A111   ; DATA XREF: sub_8049D1F+387^Xo
.rodata:0804A91C                 dd offset loc_804A0AF
.rodata:0804A920                 dd offset loc_804A0C9
.rodata:0804A924                 dd offset loc_804A0EA
.rodata:0804A928                 dd offset loc_804A0F7
.rodata:0804A92C                 dd offset loc_804A104
.rodata:0804A930                 dd offset loc_804A111
.rodata:0804A934                 dd offset loc_804A111
.rodata:0804A938                 dd offset loc_804A0D6
.rodata:0804A93C                 dd offset loc_804A0BC

We have 9 options to select from and if we go through the ones that are not printed to the console we will find that option 8 will lead to:

.text:0804A0D6 loc_804A0D6:
...
.text:0804A0DC                 call    sub_80496F7
....

and this will lead you to our special function:

.text:080496F7                 push    ebp
.text:080496F8                 mov     ebp, esp
.text:080496FA                 sub     esp, 48h
.text:080496FD                 mov     dword ptr [esp+4], 0 ; oflag
.text:08049705                 mov     dword ptr [esp], offset file ; &quot;/home/retro300/key&quot;
.text:0804970C                 call    _open
.text:08049711                 mov     [ebp+fd], eax
.text:08049714                 cmp     [ebp+fd], 0
.text:08049718                 jns     short loc_8049737
.text:0804971A                 mov     dword ptr [esp+8], 0 ; int
.text:08049722                 mov     dword ptr [esp+4], offset aKeyFileMissing ; &quot;key file missing!&quot;
.text:0804972A                 mov     eax, [ebp+arg_0]
.text:0804972D                 mov     [esp], eax      ; fd
.text:08049730                 call    sub_8048F25<br />
.text:08049735                 jmp     short locret_80497A6<br />

so we can have our key printed.
Let's try it out:

ptdeb:/tmp# nc pwn512.ddtek.biz 5500
le                                   _____ ______ _____ _     _ 
               __    ____       __        / ____|  ____/ ____(_)   | |
          ____/ /___/ / /____  / /__     | (___ | |__ | |     _  __| |
         / __  / __  / __/ _ \/ //_/      \___ \|  __|| |    | |/ _` |
        / /_/ / /_/ / /_/  __/ ,<         ____) | |___| |____| | (_| |
        \__,_/\__,_/\__/\___/_/|_|       |_____/|______\_____|_|\__,_|    (beta)


    ( cause everyone is looking for a new provider right?!)

Username:jupi
Passcode:12343831316763
   DDTEK VPN console         
                             
    Choose an option:        
       1:  change pin        
       2:  re-sync sec token 
       3:  add user          
       4:  change username   
       5:  exit 

8
lookheedsurLovesemsumAPT

I enjoyed this challenge and hope that this write up can help some other guys to understand what was going on.

© 2015 coma. All rights reserved.
Disclaimer: There are NO warranties, implied or otherwise, with regard to this information or its use. Any use of this information is at the user's risk.
In no event shall the author be held liable for any damages whatsoever arising out of or in connection with the use or spread of this information.