By
Securitynik on 2020-05-09 17:23:34
echo get_avatar(get_the_author_email())?>
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.