Binary Fun: With basic binary file analysis
A little bit ago I decided CTFs looked like alot of fun and that I want to get involved. At least for me it was a little daunting picking where to start. Some people would start with things they are familiar with but I thought Reverse Engineering sounded cool so I went with that. I got a small binary made by a friend, intended to be similar to a basic reversing challenge, to start with.
First steps
The file is titled pwd2 and we can run a couple of quick bash commands to see if we can find any useful information. I went ahead and spun up a fresh Ubuntu VM for obvious reasons. Of course lets run it first and see what it wants.
user@ubuntu:~$./pwd2
Please supply 3 digit passcode
222
Nope
The next thing to do would be to run the file command and see if we can grab any other basic information.
user@ubuntu:~$file pwd2
ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID<br />
[sha1]=82ef1e7313ec65f084871b449de99df70ba27282, stripped
Alright so it is pretty apparent what it wants. At the time I first received this I didnt know about String Format vulns otherwise I wouldve tried seeing if I couldve gained the password value that way. Additionally I didnt think to try integer underflow or overflow, but I did try a simple buffer overflow and can say that did not work.
At least now Ive got some more ideas of things to look for. The one useful thing I did get from that file command was that debug symbols are stripped.
Despite seeing that the binary is stripped, I am still going to try gdb on it. Ive used it before with varying degrees of success and still want to become better with it.
user@ubuntu:~$ gdb ./pwd2
(gdb) r
Starting program: /home/user/ex1/pwd2
denied.
[Inferior 1 (process 7311) exited with code 01]
(gdb)
Having seemingly hit a wall here, I put a good bit of googling to work. Most on obfuscation of C binaries and things that would try to prevent debuggers. Fortunately I found an article on Securing iOS apps that mentioned a bit about how one may prevent a debugger using dlsym. Dlsym would be dynamically opening a library and calling some sort of function to prevent our debugger. Ubuntu has a nifty tool called ltrace, or library trace, that will call the program and run it until exit. All the while recording all of the dynamic library calls.
user@ubuntu:~$ ltrace ./pwd2
__libc_start_main(0x4007ed, 1, 0x7fff898547b8, 0x400850 <unfinished ...>
dlopen(nil, 258) = 0x7f32fd4971c8
dlsym(0x7f32fd4971c8, "ptrace") = 0x7f32fcd9c4f0
puts("denied.\n"denied.
So there we have it, ptrace is preventing our debugger. Which is neat new knowledge but Im going to exhaust all other low hanging fruit before I begin to look into what more I can do with that.
Lets run strings, because sometimes, it really does work.
user@ubuntu:~$ strings ./pwd2
/lib64/ld-linux-x86-64.so.2
libdl.so.2
_ITM_deregisterTMCloneTable
__gmon_start__
_Jv_RegisterClasses
_ITM_registerTMCloneTable
dlclose
dlsym
dlopen
libc.so.6
exit
__isoc99_scanf
puts
__libc_start_main
GLIBC_2.2.5
GLIBC_2.7
UH-h
UH-h
=1
[]A\A]A^A_
ptrace
denied.
Please supply 3 digit passcode
Nope
Win.
;*3$
It was unlikely that an int would be stored as a string but it only takes quick moment to verify.
A big part of learning reverse engineering has been attemping to become comfortable with assembly. I did have to learn and even write some in college so I have a slight heading on where to start. I figure we can objdump the binary to a file and grep through the assembly looking for a cmp instruction.
Looking at the cmp instruction and the surrodning instructions could give away the spot where the conditional is checking for the passcode. Of course since the binary is stripped, it is going to be quite the ridiculous garbled output.
520 4007ed: 55 push %rbp
521 4007ee: 48 89 e5 mov %rsp,%rbp
522 4007f1: 48 83 ec 20 sub $0x20,%rsp
523 4007f5: 89 7d ec mov %edi,-0x14(%rbp)
524 4007f8: b8 00 00 00 00 mov $0x0,%eax
525 4007fd: e8 7b ff ff ff callq 40077d <dlsym@plt+0xfd>
526 400802: bf e8 08 40 00 mov $0x4008e8,%edi
527 400807: e8 04 fe ff ff callq 400610 <puts@plt>
528 40080c: 48 8d 45 fc lea -0x4(%rbp),%rax
529 400810: 48 89 c6 mov %rax,%rsi
530 400813: bf 07 09 40 00 mov $0x400907,%edi
531 400818: b8 00 00 00 00 mov $0x0,%eax
532 40081d: e8 3e fe ff ff callq 400660 <__isoc99_scanf@plt>
533 400822: 8b 45 fc mov -0x4(%rbp),%eax
534 400825: 3d 8f 01 00 00 cmp $0x18f,%eax
535 40082a: 74 11 je 40083d <dlsym@plt+0x1bd>
536 40082c: bf 0a 09 40 00 mov $0x40090a,%edi
537 400831: e8 da fd ff ff callq 400610 <puts@plt>
538 400836: b8 00 00 00 00 mov $0x0,%eax
539 40083b: eb 0f jmp 40084c <dlsym@plt+0x1cc>
540 40083d: bf 0f 09 40 00 mov $0x40090f,%edi
541 400842: e8 c9 fd ff ff callq 400610 <puts@plt>
542 400847: b8 00 00 00 00 mov $0x0,%eax</
The assembly code was quite massive with over 1033 lines but I managed to ferret out the above block as one warranting closer examination. Simply because puts and scanf caught my eye. Looking two below the scanf(532) we have cmp at 534. The first value 0x18f is compared against whatever is at %eax. 533 is a mov that takes our value and pushes it to %eax so we can conclude our value at %eax is compared against 0x18f. A quick hex to base 10 conversion reveals 0x18f to be 399. Lets give it a try.
user@ubuntu:~$ ./pwd2
Please supply 3 digit passcode
399
Win.
Conclusion
It worked but Im quite fortunate in the fact that puts and scanf werent also put under dlsym as that wouldve made it a good bit harder to identify.
Additionally Im fortunate in that the passcode was stored simply and not obfuscated any further.
It was a fun challenge and Im looking forward to doing more of these
All and all a great foray into the world of RE.