Table of Contents

Lab 05 - Application Security

Objectives

Resources

Setup

If you're not using the OpenStack VM:

To check if everything is OK, run the command gdb with no arguments. The prompt should be similar to this:

➜ gdb
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1
...
pwndbg>

Enter q to exit GDB. We are using PwnDbg instead of the classic GDB because it is much more user friendly. Hope you'll like it ;)

Overview

A buffer overflow occurs when data written to a buffer overruns its boundary and overwrites adjacent memory locations, due to insufficient bounds checking. Thus, an exploiting this will be able to alter the execution flow of the program by overriding the previous instruction pointer register (prior to issuing the current function call) saved on stack and execute its own malicious code!

Also check out one of the resources linked on top ^^ !

This representation of the stack is valid for 32 bit programs. The calling convention is to save the parameters on the stack.

To find out what's different for a 64 bit program check this website.

GDB primer

Please check out a GDB cheatsheet for the most common operations.

You should see how to:

Please read GDB tutorial if you're missing any of the above knowledge.

PwnDbg is a very nice GDB plugin (written in Python) which displays a plethora of information at each breakpoint and provides many useful commands (some which may prove to be useful when writing exploits!); here's an example output:

22              if (argc == 1) {
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────────────────
*EAX  0xffffd880 ◂— 0x2
*EBX  0x56558fcc (_GLOBAL_OFFSET_TABLE_) ◂— 0x3ed4
*ECX  0xffffd880 ◂— 0x2
*EDX  0xffffd8a0 —▸ 0xf7fa9000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x229dac
*EDI  0xf7ffcb80 (_rtld_global_ro) ◂— 0x0
*ESI  0xffffd934 —▸ 0xffffdabd ◂— '/home/student/appsec/buggy'
*EBP  0xffffd868 —▸ 0xf7ffd020 (_rtld_global) —▸ 0xf7ffda40 —▸ 0x56555000 ◂— 0x464c457f
*ESP  0xffffd850 —▸ 0xffffd890 —▸ 0xf7fa9000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x229dac
*EIP  0x56556281 (main+31) ◂— cmp dword ptr [eax], 1
───────────────────────────────────[ DISASM / i386 / set emulate on ]────────────────────────────────────
 ► 0x56556281 <main+31>    cmp    dword ptr [eax], 1
   0x56556284 <main+34>    jne    main+61                    <main+61>
    ↓
   0x5655629f <main+61>    mov    dword ptr [ebp - 0xc], 0x796568
   0x565562a6 <main+68>    mov    eax, dword ptr [eax + 4]
   0x565562a9 <main+71>    add    eax, 4
   0x565562ac <main+74>    mov    eax, dword ptr [eax]
   0x565562ae <main+76>    sub    esp, 4
   0x565562b1 <main+79>    push   eax
   0x565562b2 <main+80>    lea    eax, [ebp - 0xc]
   0x565562b5 <main+83>    push   eax
   0x565562b6 <main+84>    lea    eax, [ebx - 0x1f6e]
────────────────────────────────────────────[ SOURCE (CODE) ]────────────────────────────────────────────
In file: /home/student/appsec/buggy.c
   17         gets(name);
   18         printf("bye\n");
   19 }
   20
   21 int main(int argc, char **argv) {
 ► 22         if (argc == 1) {
   23                 puts("Usage: %s <name>\n");
   24                 return 1;
   25          }
   26         char buf[] = "hey";
   27
────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────
00:0000│ esp 0xffffd850 —▸ 0xffffd890 —▸ 0xf7fa9000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x229dac
01:0004│-014 0xffffd854 —▸ 0xf7fbe66c —▸ 0xf7ffdba0 —▸ 0xf7fbe780 —▸ 0xf7ffda40 ◂— ...
02:0008│-010 0xffffd858 —▸ 0xf7fbeb20 —▸ 0xf7d99cc6 ◂— 'GLIBC_PRIVATE'
03:000c│-00c 0xffffd85c ◂— 0x1
04:0010│-008 0xffffd860 —▸ 0xffffd880 ◂— 0x2
05:0014│-004 0xffffd864 —▸ 0xf7fa9000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x229dac
06:0018│ ebp 0xffffd868 —▸ 0xf7ffd020 (_rtld_global) —▸ 0xf7ffda40 —▸ 0x56555000 ◂— 0x464c457f
07:001c│+004 0xffffd86c —▸ 0xf7da0519 (__libc_start_call_main+121) ◂— add esp, 0x10
──────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────
 ► 0 0x56556281 main+31
   1 0xf7da0519 __libc_start_call_main+121
   2 0xf7da05f3 __libc_start_main+147
   3 0x565560cb _start+43
─────────────────────────────────────────────────────────────────────────────────────────────────────────

Let's take a look at the previous output that PwnDbg prints. You can see it is seprated into 5 sections: REGISTERS, DISASM, SOURCE, STACK and TRACE. With the original GDB you would have to manually print registers, disassemble code and inspect the stack. Thanks, God, for PwnDbg!

Exercises

00. Preparation

Download the lab archive and unpack it somewhere in your home.

Run make.

[20p] 01. Obfuscated Code

To change a variable without typing info built-in (not compiled using -g), take the variable's address, cast to pointer of desired type then dereference:

set variable *(int *)&myvar = value

In case of emergency, expand

In case of emergency, expand

If this seems too difficult or you wasted too much time, just add -g to the gcc rule inside the Makefile, recompile and try it this way :(

[50p] 02. Stack overflow (EZ)

You can use run args < <(python3 -c 'import sys; sys.stdout.buffer.write(b"A" * N)') for stdin redirection directly within GDB! ;)

Do not use print in Python for this purpose as some installations (especially on Ubuntu) use a default UTF-8 encoding and auto-correct any unknown binary string to a valid sequence. This is why sys.stdout.buffer.write() was used in the snippet above, but beware: it requires a bytes object as argument! You can test the binary output using xxd: python3 -c '… write here …' | xxd -g 1

[30p] 03. Format string leak + pwntools

Beware of this behavior: if you overwrite just 1-2 extra bytes after the saved EIP, you will invalidate the get_user_info function's arguments, thus your program will crash earlier on one of those memcpy lines and won't get to return properly! Use the attached GDB and set breakpoints before these lines to debug (or just reverse-* your execution to discover the crash reason ;) ).

Feedback

Please take a minute to fill in the feedback form for this lab.