Saturday, May 9, 2020

Finding passwords via GDB and strncmp

Recently while mentoring the Rogers Cyber Secure Catalyst Program. A program supported through the generous partnership of the Government of Canada, Rogers Communications and Royal Bank of Canada and executed by Ryerson University in conjunction with the SANS Institute, there was a lab which the candidates had to complete. I completed the lab using a method different from the documentation presented to the students. I did it that way to show there are different ways to solve the same problem. However, one of the students asked if I could instead solve the problem similarly to the lab documentation. In reality I could, but after trying a few tricks and considering time, I figured I would do it and show it to them later.

What was the lab?
Basically, the lab was about finding the password within compiled code, by using GDB/pwndbg.

Here is an idea of the code. This is not the exact code that the lab had but the idea remains the same.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/* 
Sample code to compare password
Author: Nik Alleyne
Blog: www.securitynik.com

Compile with debug symbols
$gcc -m32 -ggdb -mpreferred-stack-boundary=2 -o comparePassword comparePassword.c

Compile without debug symbols
$gcc -m32 -mpreferred-stack-boundary=2 -o comparePassword comparePassword.c
*/

#include <stdio.h>
#include <string.h>

// Function to compare password
void compare_string()
    {
        // allocate space for the password
        char USER_PASS[254];
        char PASSWORD[] = "SecurityNik";

        printf("Enter the password: \n");
        
        // read the user's password
        fgets(USER_PASS, sizeof(USER_PASS), stdin);

        // Compare what the user entered with what we expect
        if (strncmp(USER_PASS, PASSWORD, sizeof(PASSWORD)-1) == 0)
                printf("Welcome to my world! :-) \n");
        else
                printf("Stay Out! \n");      
            
    }

int main()
    {
        // Call the compare_string function
        compare_string();
        return 0;
    }

What was missing from above is the "strrev" function, which reverses a string. Anyhow, I don't believe it's absence if of major concern.

The code is compiled as follows:

1
└──╼ $gcc -m32 -mpreferred-stack-boundary=2 -o comparePassword32 comparePassword.c

When this code is run, we get the following:

1
2
3
4
5
┌─[securitynik@securitynik]─[~/c-code]
└──╼ $./comparePassword
Enter the password: 
Testing1
Stay Out! 

Above, an incorrect password was entered.

The challenge at this point is to find the correct password using DBG. The way I solved the challenge was to use the "strings" command along with "rev" command. However, as mentioned above, the students wanted me to solve the problem using GDB/pwndbg.

There are many ways to solve this problem. Here is one:

Load the program into GDB.

1
2
3
4
5
└──╼ $gdb comparePassword32 -q
pwndbg: loaded 187 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Reading symbols from comparePassword32...
(No debugging symbols found in comparePassword32)

Next step, look at the functions being used in the program to understand what the program might be doing.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
pwndbg> info functions
All defined functions:

Non-debugging symbols:
0x00001000  _init
0x00001030  fgets@plt
0x00001040  puts@plt
0x00001050  __libc_start_main@plt
0x00001060  strncmp@plt
0x00001070  __cxa_finalize@plt
0x00001080  _start
0x000010c0  __x86.get_pc_thunk.bx
0x000010d0  deregister_tm_clones
0x00001110  register_tm_clones
0x00001160  __do_global_dtors_aux
0x000011b0  frame_dummy
0x000011b5  __x86.get_pc_thunk.dx
0x000011b9  compare_string
0x0000125a  main
0x00001273  __x86.get_pc_thunk.ax
0x00001280  __libc_csu_init
0x000012e0  __libc_csu_fini
0x000012e1  __x86.get_pc_thunk.bp
0x000012e8  _fini

Of greatest importance to us above is the "strncmp" functions. This function can be used to compare two strings and can be considered as the place where the password is being validated.

Next up, we set a breakpoint on our function of interest

1
2
3
4
5
pwndbg> break strncmp
Breakpoint 1 at 0x1060
pwndbg> info breakpoints 
Num     Type           Disp Enb Address    What
1       breakpoint     keep y   0x00001060 <strncmp@plt>

Now that we have the function, let's run the program by executing the "run" command.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
pwndbg> run
Starting program: /home/securitynik/c-code/comparePassword32 
Enter the password: 
Testing1

Breakpoint 1, 0xf7f2b0b0 in ?? () from /lib32/libc.so.6
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────[ REGISTERS ]──────────────────────────────────
 EAX  0xffffd13e ◂— 'Testing1\n'
 EBX  0x56559000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x3ef8
 ECX  0x0
 EDX  0xfbad2288
 EDI  0xf7fac000 ◂— 0x1dfd6c
 ESI  0xf7fac000 ◂— 0x1dfd6c
 EBP  0xffffd240 —▸ 0xffffd248 ◂— 0x0
 ESP  0xffffd120 —▸ 0x5655622d (compare_string+116) ◂— add    esp, 0xc
 EIP  0xf7f2b0b0 ◂— push   ebp
───────────────────────────────────[ DISASM ]───────────────────────────────────
  0xf7f2b0b0    push   ebp
   0xf7f2b0b1    mov    edx, dword ptr [esp + 8]
   0xf7f2b0b5    mov    eax, dword ptr [esp + 0xc]
   0xf7f2b0b9    mov    ebp, dword ptr [esp + 0x10]
   0xf7f2b0bd    test   ebp, ebp
   0xf7f2b0bf    je     0xf7f2b2b2
 
   0xf7f2b0c5    mov    cx, dx
   0xf7f2b0c8    and    cx, 0xfff
   0xf7f2b0cd    cmp    cx, 0xff0
   0xf7f2b0d2    ja     0xf7f2b129
 
   0xf7f2b0d4    movdqu xmm2, xmmword ptr [edx]
───────────────────────────────────[ STACK ]────────────────────────────────────
00:0000 esp    0xffffd120 —▸ 0x5655622d (compare_string+116) ◂— add    esp, 0xc
01:0004        0xffffd124 —▸ 0xffffd13e ◂— 'Testing1\n'
02:0008        0xffffd128 —▸ 0xffffd132 ◂— 'SecurityNik'
03:000c│        0xffffd12c ◂— 0xb /* '\x0b' */
04:0010        0xffffd130 ◂— 0x6553d000
05:0014        0xffffd134 ◂— 'curityNik'
06:0018        0xffffd138 ◂— 'tyNik'
07:001c│ eax-2  0xffffd13c ◂— 0x6554006b /* 'k' */
─────────────────────────────────[ BACKTRACE ]──────────────────────────────────
  f 0 f7f2b0b0
   f 1 5655622d compare_string+116
   f 2 5655626c main+18
   f 3 f7deaef1 __libc_start_main+241
────────────────────────────────────────────────────────────────────────────────
pwndbg> 

Once I entered the invalid password, we then see that GDB/pwndbg reported the string I entered along with the another string below it. Could this other string be the password? Let's test that theory.

Executed the program again, with string which we now find during debugging.


1
2
3
4
└──╼ $./comparePassword32 
Enter the password: 
SecurityNik
Welcome to my world! :-) 

Looks like we are now in. This means the password is "SecurityNik".

Obviously there was lots of testing I had to do to get to this point. However, hopefully the students reading can make sense of what I did.

No comments:

Post a Comment