Playing a very basic level of a known wargame the other day I was trying to solve the following problem:
Given an executable (ELF, Linux) which accepts a password as an argument and verifies if this is correct, find this string. As most of the problems, it’s quite easy if you have the right tools (and knowledge of course).
Anyway, I thought the solution(s) would make a nice blog entry, so here it is!
In order to play around with it in my own Ubuntu, I wrote a very simple C program, named pass_cli.c
carlos@dell:~/Dropbox/hacking/wargames$ cat pass_cli.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int
main(int argc, char **argv)
{
char password[] = “yomama”;
if(argc < 2)
{
printf(“Usage: %s <password>\n”, argv[0]);
exit(1);
}
if(strncmp(argv[1], password, strlen(password)))
{
printf(“FAIL\n”);
exit(1);
} else {
printf(“WIN\n”);
return(0);
}
return(0); // never reached
}
I compiled the program with no special options.
carlos@dell:~/Dropbox/hacking/wargames$ file pass_cli
pass_cli: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, not stripped
A first tool of the trade when you are looking for strings in a binary is strings, which will look for strings of printable characters inside the binary.
carlos@dell:~/Dropbox/hacking/wargames$ strings pass_cli
/lib/ld-linux.so.2
`:6K*
__gmon_start__
libc.so.6
_IO_stdin_used
exit
strncmp
puts
printf
strlen
__libc_start_main
GLIBC_2.0
PTRh`
yomaf
[^_]
Usage: %s <password>
FAIL
Well, we cannot find the password here (and even if it were displayed here, how could you identify it?) but that gave us some preliminar information about the program. We can see some familiar libc functions (printf, strlen, strncmp, etc.), strncmp will be of interest a bit later.
Another method you could have tried is for example to dump the contents of the .text section inside the binary. This is actually the same idea behind the use of strings, just a lot messier 
Remember that the .text section of a binary contains the machine instructions (the compiled code) but that doesn’t mean the strings are neccesarily stored in a readable way. Anyway we could check the contents of this section with the help of objdump.
carlos@dell:~/Dropbox/hacking/wargames$ objdump
Usage: objdump <option(s)> <file(s)>
Display information from object <file(s)>.
At least one of the following switches must be given:
-a, –archive-headers Display archive header information
-f, –file-headers Display the contents of the overall file header
-p, –private-headers Display object format specific file header contents
-h, –[section-]headers Display the contents of the section headers
-x, –all-headers Display the contents of all headers
-d, –disassemble Display assembler contents of executable sections
-D, –disassemble-all Display assembler contents of all sections
-S, –source Intermix source code with disassembly
-s, –full-contents Display the full contents of all sections requested
-g, –debugging Display debug information in object file
-e, –debugging-tags Display debug information using ctags style
-G, –stabs Display (in raw form) any STABS info in the file
-W[lLiaprmfFsoR] or
–dwarf[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=str,=loc,=Ranges]
Display DWARF info in the file
-t, –syms Display the contents of the symbol table(s)
-T, –dynamic-syms Display the contents of the dynamic symbol table
-r, –reloc Display the relocation entries in the file
-R, –dynamic-reloc Display the dynamic relocation entries in the file
@<file> Read options from <file>
-v, –version Display this program’s version number
-i, –info List object formats and architectures supported
-H, –help Display this information
carlos@dell:~/Dropbox/hacking/wargames$ objdump -h pass_cli
pass_cli: file format elf32-i386
Sections:
Idx Name Size VMA LMA File off Algn
0 .interp 00000013 08048134 08048134 00000134 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .note.ABI-tag 00000020 08048148 08048148 00000148 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
[...]
11 .init 00000030 0804834c 0804834c 0000034c 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
12 .plt 00000080 0804837c 0804837c 0000037c 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
13 .text 000001fc 08048400 08048400 00000400 2**4 <—- here
CONTENTS, ALLOC, LOAD, READONLY, CODE
14 .fini 0000001c 080485fc 080485fc 000005fc 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
[...]
This section starts at the address 0×08048400, that is, the offset is 0×400 (the start virtual address is 0×08048000)
Armed with this knowledge we could hexdump this section and manually inspect it
carlos@dell:~/Dropbox/hacking/wargames$ hexdump -C -s 0×400 pass_cli | less
00000400 31 ed 5e 89 e1 83 e4 f0 50 54 52 68 60 85 04 08 |1.^…..PTRh`…|
00000410 68 70 85 04 08 51 56 68 b4 84 04 08 e8 7b ff ff |hp…QVh…..{..|
[...]
000004a0 00 00 00 00 85 c0 74 09 c7 04 24 1c 9f 04 08 ff |……t…$…..|
000004b0 d0 c9 c3 90 55 89 e5 83 e4 f0 83 ec 20 c7 44 24 |….U……. .D$|
000004c0 19 79 6f 6d 61 66 c7 44 24 1d 6d 61 c6 44 24 1f |.yomaf.D$.ma.D$.| <— 
000004d0 00 83 7d 08 01 7f 22 8b 45 0c 8b 10 b8 20 86 04 |..}…”.E…. ..|
000004e0 08 89 54 24 04 89 04 24 e8 cf fe ff ff c7 04 24 |..T$…$…….$|
[...]
00000600 83 ec 04 e8 00 00 00 00 5b 81 c3 ec 19 00 00 e8 |……..[.......|
00000610 1c fe ff ff 59 5b c9 c3 03 00 00 00 01 00 02 00 |....Y[..........|
00000620 55 73 61 67 65 3a 20 25 73 20 3c 70 61 73 73 77 |Usage: %s <passw|
00000630 6f 72 64 3e 0a 00 46 41 49 4c 00 57 49 4e 00 00 |ord>..FAIL.WIN..|
00000640 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
As we can see, the string isn't stored in a readable format, as expected 
A very useful tool in this case is "ltrace" for we can monitor every system call (with the corresponding arguments!)
carlos@dell:~/Dropbox/hacking/wargames$ ltrace ./pass_cli AAAAA
__libc_start_main(0x80484b4, 2, 0xbf952854, 0x8048570, 0x8048560 <unfinished ...>
strlen("yomama") = 6
strncmp("AAAAA", "yomama", 6) = -1 <--- TA-DA!!!!
puts("FAIL"FAIL
) = 5
exit(1 <unfinished ...>
+++ exited (status 1) +++
But what if our system doesn't have this tool? Then we have to take the heavy weapons and perform some elegant debugging
carlos@dell:~/Dropbox/hacking/wargames$ objdump -T pass_cli
pass_cli: file format elf32-i386
DYNAMIC SYMBOL TABLE:
00000000 w D *UND* 00000000 __gmon_start__
00000000 DF *UND* 00000000 GLIBC_2.0 __libc_start_main
00000000 DF *UND* 00000000 GLIBC_2.0 strlen
00000000 DF *UND* 00000000 GLIBC_2.0 printf
00000000 DF *UND* 00000000 GLIBC_2.0 puts
00000000 DF *UND* 00000000 GLIBC_2.0 strncmp <---- idea!
00000000 DF *UND* 00000000 GLIBC_2.0 exit
0804861c g DO .rodata 00000004 Base _IO_stdin_used
We have very good reasons to suspect that strncmp is being used to check our input against the password so let's start the program in gdb and quickly disassemble it in order to localize the call to strncmp.
carlos@dell:~/Dropbox/hacking/wargames$ gdb -q ./pass_cli
Reading symbols from /home/carlos/Dropbox/hacking/wargames/pass_cli...(no debugging symbols found)...done.
(gdb) set disassembly-flavor intel <---- PLEASE 
(gdb) disass main
Dump of assembler code for function main:
0x080484b4 <+0>: push ebp
0x080484b5 <+1>: mov ebp,esp
0x080484b7 <+3>: and esp,0xfffffff0
0x080484ba <+6>: sub esp,0x20
0x080484bd <+9>: mov DWORD PTR [esp+0x19],0x616d6f79
0x080484c5 <+17>: mov WORD PTR [esp+0x1d],0x616d
0x080484cc <+24>: mov BYTE PTR [esp+0x1f],0×0
0x080484d1 <+29>: cmp DWORD PTR [ebp+0x8],0×1
0x080484d5 <+33>: jg 0x80484f9
0x080484d7 <+35>: mov eax,DWORD PTR [ebp+0xc]
0x080484da <+38>: mov edx,DWORD PTR [eax]
0x080484dc <+40>: mov eax,0×8048620
0x080484e1 <+45>: mov DWORD PTR [esp+0x4],edx
0x080484e5 <+49>: mov DWORD PTR [esp],eax
0x080484e8 <+52>: call 0x80483bc
0x080484ed <+57>: mov DWORD PTR [esp],0×1
0x080484f4 <+64>: call 0x80483ec
0x080484f9 <+69>: lea eax,[esp+0x19]
0x080484fd <+73>: mov DWORD PTR [esp],eax
0×08048500 <+76>: call 0x80483ac
0×08048505 <+81>: mov edx,eax
0×08048507 <+83>: mov eax,DWORD PTR [ebp+0xc]
0x0804850a <+86>: add eax,0×4
0x0804850d <+89>: mov eax,DWORD PTR [eax]
0x0804850f <+91>: mov DWORD PTR [esp+0x8],edx
0×08048513 <+95>: lea edx,[esp+0x19]
0×08048517 <+99>: mov DWORD PTR [esp+0x4],edx
0x0804851b <+103>: mov DWORD PTR [esp],eax
0x0804851e <+106>: call 0x80483dc
0×08048523 <+111>: test eax,eax
0×08048525 <+113>: je 0x804853f
0×08048527 <+115>: mov DWORD PTR [esp],0×8048636
[...]
Now I will place a breakpoint exactly on the address of this call instruction.
(gdb) b *0x0804851e
Breakpoint 1 at 0x804851e
Remember to use the asterisk (*) right in front of the address, so gdb understands what follows isn’t a literal.
Why did I do it this way? Think about it the other way. What happens when the call instruction is executed? The whole stack frame stup parafernalia, namely
- EIP is pushed to the stack (by the call itself)
- The function prologue is executed, that is
- the current value of EBP is pushed to the stack (from now on, known as SFP)
- the current value of ESP passes to be the new EBP (“a new stack frame starts here”)
- a value is subtracted from ESP (to allocate space for local variables)
That is too messy to keep track of, so if we stop the execution flow right before the call instruction, what we have is the stack right before all this stuff… the function arguments at the top of the stack
(gdb) run AAAAAA
Starting program: /home/carlos/Dropbox/hacking/wargames/pass_cli AAAAAA
Breakpoint 1, 0x0804851e in main () <--- breakpoint is hit
(gdb) info reg
eax 0xbffff620 -1073744352
ecx 0x6 6
edx 0xbffff3c9 -1073744951
ebx 0x283ff4 2637812
esp 0xbffff3b0 0xbffff3b0 <--- "top" of the stack
ebp 0xbffff3d8 0xbffff3d8
esi 0x0 0
edi 0x0 0
eip 0x804851e 0x804851e
eflags 0×286 [ PF SF IF ]
cs 0×73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0×0 0
gs 0×33 51
Let’s examine the top of the stack. We are actually interested in the first three words.
(gdb) x/12x $esp
0xbffff3b0: 0xbffff620 0xbffff3c9 0×00000006 0xbffff3d8
0xbffff3c0: 0x0015d4a5 0x0011e0c0 0x6d6f797b 0x00616d61
0xbffff3d0: 0×08048570 0×00000000 0xbffff458 0x00144bd6
Without further inspection we can see a 6, the number of char the function has to compare. It looks good at least
Let’s remember the exact syntaxis of strncmp()
carlos@dell:~/Dropbox/hacking/wargames$ man 3 strncmp
[...]
SYNOPSIS
#include
int strcmp(const char *s1, const char *s2);
int strncmp(const char *s1, const char *s2, size_t n); <--- allright, three arguments...
[...]
Also one of the first two words must be a pointer to the hardcoded password, the other a pointer to the user input (argv[1] in this case)
(gdb) x/4x 0xbffff620
0xbffff620: 0×41414141 0x4f004141 0×54494252 0x434f535f
Well, this is pretty clearly our string of 6 “A’s” null-terminated. The other address must be the location of our password!
(gdb) x/4x 0xbffff3c9
0xbffff3c9: 0x616d6f79 0×7000616d 0×00080485 0×58000000
OK, another string of 6 characters, null-terminated. The ASCII char associated to these values can be displayed in gdb as follows:
(gdb) p/c *0xbffff3c9
$16 = 121 ‘y‘
(gdb) p/c *0xbffff3ca
$17 = 111 ‘o‘
(gdb) p/c *0xbffff3cb
$18 = 109 ‘m‘
(gdb) p/c *0xbffff3cc
$19 = 97 ‘a‘
(gdb) p/c *0xbffff3cd
$20 = 109 ‘m‘
(gdb) p/c *0xbffff3ce
$21 = 97 ‘a‘
(gdb) p/c *0xbffff3cf
$22 = 0 ‘\000‘
So here’s the magic password! YOMAMA!