Lab 01 - Introduction. Basic Exploration Tools


Supporting files


You will spend a large part of the labs and assignments working with *binaries*; in some of the cases we will also provide you with source code. You will have to find vulnerabilities in those binaries, then (possibly) exploit them and (possibly) fix the vulnerabilities in order to illustrate various secure coding practices. To achieve this, first you need to get comfortable with at least some of the common tools that are right for the job.

In the introductory lab we'll spice things up a bit by providing some simple binaries (with no source code) for you to play with. In order to solve the lab, you'll have to perform both static analysis and dynamic analysis on said binaries.

If you're too comfortable with the x86 architecture and feel that you could use some challenge, the lab archive also contains the binaries compiled for ARM. You can try them out by running a Raspbian image on QEMU (Raspbian image, Linux kernel image).

1. even-password

GLaDOS' binary even-password is asking you to provide a password:

$ ./even-password
Enter password:

Unfortunately, the program isn't very well thought out, the password being hard-coded. There are a few possible approaches to this (try them all):

  • Using strings to see if the password is hard-coded as an ASCII string
  • Using objdump to see if the password is hard-coded as a set of instructions (e.g. copying characters into a buffer)
  • Using Radare2 to get a user-readable disassembly of the program

Here's a short tutorial on using Radare2. We can analyze a binary using r2 [binary_name], where [binary_name] is a given binary. To get online help, we can use the following commands:

$ r2 even-password
[0x00001090]> ?
Usage: [.][times][cmd][~grep][@[@iter]addr!size][|>pipe] ; ...
Append '?' to any char command to get detailed help
[0x08048400]> p? #provide help for p command
Usage: p[=68abcdDfiImrstuxz] [arg|len]

To disassemble the file, we can use the pd command, e.g.

[0x00001090]> pd@main
            ;-- main:
            0x00001175      55             push rbp
            0x00001176      4889e5         mov rbp, rsp
            0x00001179      4883ec10       sub rsp, 0x10
            0x0000117d      488d3d840e00.  lea rdi, qword str.Enter_password: ; 0x2008 ; "Enter password: "
            0x00001184      b800000000     mov eax, 0
            0x00001189      e8b2feffff     call sym.imp.printf

2. odd-password

Same as the previous task, only this time the password is a non-ASCII string. The following approach should work:

  1. Disassemble the binary and look at main. What functions does it call?
  2. Once you have the program flow figured out, look at how the password checking is done and figure out the password.
  3. Use python or perl to output the string.

Note: You can use Radare2 to disassemble individual functions with the pdf command. You can also use it to output control flow graphs, using the ag command.

Note: to output raw bytes, use the following snippets as reference:

$ # python
$ python -c 'print ("\x02"*20 + "\x03")' # output 0x02 20 times, followed by 0x03 and a newline

$ # perl
$  perl -e 'print "\x02"x20 . "\x03"' # output 0x02 20 times, followed by 0x03

For more complex strings, consider putting everything in a (Python or Perl) script.

3. halting-problem

Disassemble halting-problem and take a look at it:

[0x08048330]> af@main
[0x08048330]> pdf@main
            ;-- main:
/ (fcn) sym.main 57
|   sym.main (int argc, char **argv, char **envp);
|           0x00001145      55             push rbp
|           0x00001146      4889e5         mov rbp, rsp
|           0x00001149      488d3db40e00.  lea rdi, qword str.Brb__I_m_out_to_get_cookies. ; 0x2004 ; "Brb, I'm out to get cookies."
|           0x00001150      e8dbfeffff     call sym.imp.puts           ; int puts(const char *s)
|           0x00001155      488d3dc50e00.  lea rdi, qword str.Going_to_halt_anytime_now... ; 0x2021 ; "Going to halt anytime now..."
|           0x0000115c      e8cffeffff     call sym.imp.puts           ; int puts(const char *s)
|           0x00001161      bfa0860100     mov edi, 0x186a0
|           0x00001166      e8d5feffff     call sym.imp.sleep          ; int sleep(int s)
|           0x0000116b      488d3dcc0e00.  lea rdi, qword str.Done     ; 0x203e ; "Done!"
|           0x00001172      e8b9feffff     call sym.imp.puts           ; int puts(const char *s)
|           0x00001177      b800000000     mov eax, 0
|           0x0000117c      5d             pop rbp
\           0x0000117d      c3             ret

Notice that it calls sleep with the value 0x186a0:

$ rax2 0x186a0

Use a hex editor of your choice (Bless, HTE etc.) to edit the value to something more convenient. If you prefer using vim, then you can convert the current buffer to hex and back to binary. Load the executable in vim, then use the following:

<edit stuff>
:%!xxd -r

To find the code, you can search for the opcodes (use pd 2@addr and px to see the exact values).

4. straceme

Use only dynamic analysis to figure out what straceme does. First strace it:

$ strace ./straceme

It doesn't seem to be doing anything relevant, so let's run it in gdb. We use layout asm to display the current location in the assembly code. It is not necessary when peda is enabled, you will need this for ARM analysis in QEMU VM where peda is not available. stepi/si is used to step into individual instruction.

$ gdb ./straceme
(gdb) b main
(gdb) r
(gdb) stepi
<repeat this a few times, to observe the program's control flow>

If we run the program a couple of times, we observe that the condition for cmpl $0x2,-0x44(%rbp) fails. Let's inspect that address:

(gdb) x $rbp-0x44
0x7fffffffe47c: 0x00000001

The rbp register holds the base pointer for the current stack frame. We notice that $rbp - 0x44 holds the value 1, while our program expects the value 2. What is that value?

After figuring that out, run the program with strace again to determine the password.

5. guesser

guesser reads an unsigned int from /dev/urandom and asks you to guess it.

  1. Disassemble the binary using objdump or Radare2 and examine its control flow.
  2. Run the binary using gdb and place a breakpoint after the read call.
  3. Inspect the memory location of the variable where the random value was placed by read. If in doubt, consult the GDB documentation and the read (2) manpage.
  4. Resume the program's execution and input the random value

6. Extra: ARM Tasks

Try the above tasks using the ARM binaries. For static analysis you can use Radare2 directly on the host machine. For the other tools (gdb, strace, objdump) you can use the QEMU setup described in the introduction.

  1. For scrolling in the QEMU VM you can use Shift PageUp and Shift PageDown.
  2. In order to copy the lab binaries in the QEMU machine, you can temporary mount and update the RPI image.
$ file 2015-05-05-raspbian-wheezy.img
;; From the output of the file command, take the partition 2 'startsector'
;; value an multiply by 512, and use this figure as the offset value in the mount command below.
$ sudo mount 2015-05-05-raspbian-wheezy.img -o offset=62914560 /mnt
;; Add the tasks in the filesystem mounted in /mnt.
$ sudo umount 2015-05-05-raspbian-wheezy.img /mnt
cns/labs/lab-01.txt ยท Last modified: 2019/10/05 12:39 by cristina.popescu
CC Attribution-Share Alike 3.0 Unported Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0