Terminologi

Bug - Fejl i software (fejlberegninger, knapper som ikke reagerer, crash)

Sårbarhed (vulnerability) - Fejl som har sikkerhedsmæssige koncekvenser (kode eksekvering, omgåelse af sikkerhedstjeks, informationslæk, overskrivning af filer, osv.)

Exploit - Software som udnytter en (eller flere) specifik sårbarhed

Shellcode - En stump maskinkode som man ønsker at få udført via en sårbarhed i et program

Writeup - Beskrivelse af hvordan man er kommet frem til en løsning

Exploit

  • Proof of Concept
  • Weaponized

Exploit - PoC

Skal bare bevise eksistensen af en sårbarhed

  • Skrevet imod én version af softwaren
  • Virker kun nogen gange
  • Støjer i logs/cpu/memory
  • Begrænset
  • Crasher processen/operativ systemet

Exploit - Weaponized

Skal kunne bruges aktivt

  • Virker HVER gang
  • Virker mod alle sårbare versioner
  • Stealth!
  • Konfigurerbart payload
  • Ødelægger intet i processen/reparerer processen

Typer af sårbarheder

  • Buffer overflow
    • Stack
    • Heap
  • Integer conversion/overflow/underflow
  • Arbitrary read/write
  • Double free
  • Use-after-free
  • Uninitialized variables
  • Format string
  • Injection
    • SQL
    • Cmd. line
    • xpath
    • ….
  • XML External Entity
  • Cross site scripting
    • Ikke kun på web sites!
  • Race conditions
  • Sikkert mange flere

Typer af sårbarheder - som vi holder os til

  • Buffer overflow
    • Stack
  • Integer conversion/overflow/underflow

Buffer?

…er et område i hukommelsen af en hvis størrelse til data. Ofte men ikke nødvendigvis et array.

Buffer overflow

…er hvad der sker, når man hælder mere data i bufferen, end den kan indeholde.

Dette kan under de rette omstændigheder give mulighed for at eksekvere arbitrær maskinkode.

Gammelt problem

Første kendte udnyttelse var i 1988 da Morris ormen spredte sig bl.a. vha. et buffer overflow i fingerd.

Første gang beskrevet i 1995, da Mudge skrev "How to write Buffer Overflows".

Beskrevet bedre i 1996, da Aleph One skrev "Smashing the Stack for Fun and Profit" i Phrack.

I dag?

Stadig et af de oftest forekomne sikkerhedsproblemer i den binære verden.

Operativ systemer, hardware designere og compiler udviklere forsøger at gøre det sværere at udnytte fejlen, men det sker stadig.

Første opgave

Simpelt eksempel. Findes i assignments/simple_login.

#include <stdio.h>
#include <string.h>

void login() {
    int authenticated = 0;
    char name[32];
    char password[32];
    printf("Enter your name: ");
    fflush(stdout);
    gets(name);
    printf("Enter your password: ");
    fflush(stdout);
    gets(password);

    if (strcmp(name, "admin") == 0 && strcmp(password, "secret") == 0) {
        authenticated = 1;
    }

    if (authenticated) {
        printf("Hello sweet admin.\n");
        fflush(stdout);
    } else {
        printf("Piss off!\n");
        fflush(stdout);
    }
}

int main(int argc, char *argv[]) {
    login();
    return 0;
}

Første opgave

Simpelt eksempel. Findes i assignments/simple_login.

../images/simple_login_stack.png
#include <stdio.h>
#include <string.h>

void login() {
    int authenticated = 0;
    char name[32];
    char password[32];
    printf("Enter your name: ");
    fflush(stdout);
    gets(name);
    printf("Enter your password: ");
    fflush(stdout);
    gets(password);

    if (strcmp(name, "admin") == 0 && strcmp(password, "secret") == 0) {
        authenticated = 1;
    }

    if (authenticated) {
        printf("Hello sweet admin.\n");
        fflush(stdout);
    } else {
        printf("Piss off!\n");
        fflush(stdout);
    }
}

int main(int argc, char *argv[]) {
    login();
    return 0;
}

Første opgave

Hvad sker der, hvis vi skriver 33 tegn i vores navn?

../images/simple_login_stack.png
#include <stdio.h>
#include <string.h>

void login() {
    int authenticated = 0;
    char name[32];
    char password[32];
    printf("Enter your name: ");
    fflush(stdout);
    gets(name);
    printf("Enter your password: ");
    fflush(stdout);
    gets(password);

    if (strcmp(name, "admin") == 0 && strcmp(password, "secret") == 0) {
        authenticated = 1;
    }

    if (authenticated) {
        printf("Hello sweet admin.\n");
        fflush(stdout);
    } else {
        printf("Piss off!\n");
        fflush(stdout);
    }
}

int main(int argc, char *argv[]) {
    login();
    return 0;
}

Første opgave

Hvad sker der, hvis vi skriver 52 tegn i vores navn?

../images/simple_login_stack.png
#include <stdio.h>
#include <string.h>

void login() {
    int authenticated = 0;
    char name[32];
    char password[32];
    printf("Enter your name: ");
    fflush(stdout);
    gets(name);
    printf("Enter your password: ");
    fflush(stdout);
    gets(password);

    if (strcmp(name, "admin") == 0 && strcmp(password, "secret") == 0) {
        authenticated = 1;
    }

    if (authenticated) {
        printf("Hello sweet admin.\n");
        fflush(stdout);
    } else {
        printf("Piss off!\n");
        fflush(stdout);
    }
}

int main(int argc, char *argv[]) {
    login();
    return 0;
}

Første opgave

Den lidt sværere. Hvad sker der, hvis vi skriver 45-48 tegn i vores navn?

../images/simple_login_stack.png
#include <stdio.h>
#include <string.h>

void login() {
    int authenticated = 0;
    char name[32];
    char password[32];
    printf("Enter your name: ");
    fflush(stdout);
    gets(name);
    printf("Enter your password: ");
    fflush(stdout);
    gets(password);

    if (strcmp(name, "admin") == 0 && strcmp(password, "secret") == 0) {
        authenticated = 1;
    }

    if (authenticated) {
        printf("Hello sweet admin.\n");
        fflush(stdout);
    } else {
        printf("Piss off!\n");
        fflush(stdout);
    }
}

int main(int argc, char *argv[]) {
    login();
    return 0;
}

"Problematiske" funktioner

gets → ALTID et problem

strcpy, strcat, sprintf → medmindre man selv tjekker input længde

strncpy, strncat, fgets, snprintf, memcpy → hvis man beregner størrelsen og regner forkert

Ingen funktioner er sikre, hvis man ikke bruger dem rigtigt!

Alle funktioner, som skriver til hukommelsen har potentiale for at være problematisk!

Anden opgave

#define MAX_USERS_PER_CHUNK 8

typedef struct {
    char name[32];
    time_t birthdate;
    enum { MALE, FEMALE } gender;
} user_t;

void handle_client(int socket) {
    user_t users[MAX_USERS_PER_CHUNK];
    int num_users;

    read(socket, &num_users, sizeof(num_users));
    if (num_users <= MAX_USERS_PER_CHUNK) {
        read(socket, users, num_users * sizeof(user_t));
        /* Do stuff with users */
    }
}

Anden opgave

Integer conversion!

int num_users; /* <- Signed integer */

if (num_users <= MAX_USERS_PER_CHUNK) /* <- Signed comparison */

/* Prototype for read. 'count' er unsigned! */
ssize_t read(int fd, void *buf, size_t count);

/* Her går det galt */
read(socket, users, num_users * sizeof(user_t));

Anden opgave

Beregning Binær repræsentation Signed værdi Unsigned værdi

-1

0xffffffff

-1

4294967295

-1 * 40

0xffffffd8

-40

4294967256

/* Vi sender -1 */
read(socket, &num_users, sizeof(num_users));
if (-1 <= 8) {
    read(socket, users, 4294967256);
}

Anden opgave

Lytter på localhost:10001

Find processen på jeres Vagrant box med:

$ ps aux | grep exploitation | grep 10001 | awk '{print $2}'
3461

Attach gdb med:

$ sudo gdb $A 3461

$A er en environment variable som peger på vores executable.

Anden opgave - VirtualBox bug

Hvis I ikke kan forbinde til processen, så er det nok pga. en fejl i VirtualBox som indtræder, når man har suspenderet og startet sin VM. Fix det sålades:

$ sudo service docker restart

Tredje opgave

#define MAX_USERS_PER_CHUNK 8

typedef struct {
    char name[32];
    time_t birthdate;
    enum { MALE, FEMALE } gender;
} user_t;

int read_full(int socket, void * buf, size_t count) {
    size_t total = 0;
    ssize_t r;
    while (total < count && (r = read(socket, ((char*)buf) + total, count - total)) > 0) {
        if (r <= 0) return 0;
        total += r;
    }
    return 1;
}

void handle_client(int socket) {
    user_t users[MAX_USERS_PER_CHUNK];
    int num_users;

    if (read_full(socket, &num_users, sizeof(num_users))) {
        if (num_users <= MAX_USERS_PER_CHUNK) {
            if (read_full(socket, users, num_users * sizeof(user_t))) {
                /* Do stuff with users... */
            } else {
                exit(0);
            }
        }
    }
}

Tredje opgave

Samme problem som i anden opgave, men her afslutter read loopet ikke før alle bytes er modtaget…og fire gigabytes er meget data!

Tredje opgave

Integer overflow!

Beregning Binær repræsentation Signed værdi Unsigned værdi

0x14

0x14

20

20

0x14 | 0x80000000

0x80000014

-2147483628

2147483668

0x80000014 * 40

0x1400000320 eller 0x320

800

800

/* Vi sender 0x80000014 */
if (read_full(socket, &num_users, sizeof(num_users))) {
    if (-2147483628 <= 8) {
        if (read_full(socket, users, 800)) {

Tredje opgave

Lytter på localhost:10002

Find processen på jeres Vagrant box med:

$ ps aux | grep exploitation | grep 10002 | awk '{print $2}'
3945

Attach gdb med:

$ sudo gdb $B 3945

Address Space Layout Randomization

Fra Wikipedia:

"Address space layout randomization (ASLR) is a computer security technique involved in protection from buffer overflow attacks. In order to prevent an attacker from reliably jumping to, for example, a particular exploited function in memory, ASLR randomly arranges the address space positions of key data areas of a process, including the base of the executable and the positions of the stack, heap and libraries."

Address Space Layout Randomization

$ cat t.c
#include <stdio.h>
#include <malloc.h>
int main(int argc, char *argv[]) {
    printf("%p %p %p\n", &argc, malloc(10), main);
    return 0;
}
$ gcc -m32 -o t t.c

Address Space Layout Randomization

$ echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
0
$ ./t && ./t && ./t
0xffffce30 0x804b008 0x804844d
0xffffce30 0x804b008 0x804844d
0xffffce30 0x804b008 0x804844d

Address Space Layout Randomization

$ echo 1 | sudo tee /proc/sys/kernel/randomize_va_space
1
$ ./t && ./t && ./t
0xffd8e120 0x804b008 0x804844d
0xffe1d070 0x804b008 0x804844d
0xff8b1a60 0x804b008 0x804844d

Address Space Layout Randomization

$ echo 2 | sudo tee /proc/sys/kernel/randomize_va_space
2
$ ./t && ./t && ./t
0xffc806f0 0x8e63008 0x804844d
0xffc5dbb0 0x985b008 0x804844d
0xffb62320 0x94bb008 0x804844d

Address Space Layout Randomization

$ checksec t
[*] '/media/code/ProsaBinExp/presentations/02-exploitation/t'
    Arch:          i386-32-little
    RELRO:         Partial RELRO
    Stack Canary:  No canary found
    NX:            NX enabled
    PIE:           No PIE

Address Space Layout Randomization

$ gcc -m32 -fpic -pie -o t t.c
$ checksec t
[*] '/media/code/ProsaBinExp/presentations/02-exploitation/t'
    Arch:          i386-32-little
    RELRO:         Partial RELRO
    Stack Canary:  No canary found
    NX:            NX enabled
    PIE:           PIE enabled
$ ./t && ./t && ./t
0xffef8ac0 0xf96f9008 0xf778863b
0xff868d30 0xf7e6e008 0xf775f63b
0xffa5f1d0 0xf81c8008 0xf772663b

Address Space Layout Randomization

Statistik fra 100.000 samplinger afslører at 32 bit binaries randomizer således:

Område ASLR bitmask Tilfældige bits Antal muligheder

Stak

0x00fffff0

20

1.048.576

Heap

0x0ffff000

16

65.536

PIE

0x003ff000

10

1.024

For de interesserede er her samme data for 64 bit binaries:

Område ASLR bitmask Tilfældige bits Antal muligheder

Stak

0x00000003fffffff0

30

1.073.741.824

Heap

0x000001fffffff000

29

536.870.912

PIE

0x000001fffffff000

29

536.870.912

Tip
ASLR bliver "genereret" under execve. Intet bliver ændret efter en fork, så forbinder du to gange til en forkende server, vil hukommelseslayoutet være éns for begge forbindelser.

Overvind ASLR

  • Leak hukommelse
  • Brug ikke randomiserede områder. Non PIE executables f.eks.
  • Brug en NOP slæde
  • Brute force til en vis grænse
  • ulimit -s unlimited (32 bit local exploits)

NOP slæde

Hvis man ikke kender den præcise adresse på sin shellcode men kan komme nogenlunde tæt på, kan man ligge en stribe NOP instruktioner foran.

Måske kender man ikke de mindst betydende 10 bits, hvilket giver en usikkerhed på 1024 bytes. Ligger man 1024 NOP instruktioner foran sin shellcode skal man altså bare ramme én af disse adresser, og så "glider" man ned til shellcoden.

shellcode = asm(shellcraft.nop() * 1024 + shellcraft.findpeersh())

Trampolin

Hvis EAX peger på en buffer, som vi har placeret shellcode i, kan vi returnere til en jmp eax eller call eax instruktion. En sådan instruktion kaldes for en trampolin.

Sådanne instruktioner kan findes med ROPgadget (et meget kraftfulgt værktøj!):

$ gcc -m32 -o t t.c
$ ROPgadget --binary t|grep -E ': ((jmp)|(call)) eax'
0x080483b6 : call eax

Fjerde opgave

Magen til anden opgave men med ASLR. Lytter på localhost:10003.

$ ps aux | grep assignment | grep 10003 | awk '{print $2}'
3712

Åben i gdb med:

$ gdb $A 3712

Femte opgave

Magen til tredje opgave men med ASLR. Lytter på localhost:10004.

Denne klarer I selv eller i grupper.

Problemer med PIE

Under udviklingen af et exploit vil vi gerne

  • sætte et breakpoint i slutningen af en funktion
  • benytte en trampolin
  • noget tredje som kræver at vi kender en adresse

Problemer med PIE

Først find funktionens/trampolinens offset:

$ readelf -s integer_conversion_canary_pie | grep handle_client
    49: 0000099b   156 FUNC    GLOBAL DEFAULT   13 handle_client
$ ROPgadget --binary integer_conversion_canary_pie | grep -E ': ((jmp)|(call)) esp'
0x00000db4 : call esp

Problemer med PIE

Find processens ID:

$ ps aux|grep integer_conversion_canary_pie|grep -v grep|awk '{print $2}'
24128

Problemer med PIE

Find så ud af, hvor filen er mappet:

$ grep integer_conversion_canary_pie /proc/24128/maps
f7712000-f7714000 r-xp 00000000 00:1a 305   integer_conversion_canary_pie
f7714000-f7715000 r-xp 00001000 00:1a 305   integer_conversion_canary_pie
f7715000-f7716000 rwxp 00002000 00:1a 305   integer_conversion_canary_pie

Problemer med PIE

$ readelf -s integer_conversion_canary_pie | grep handle_client
    49: 0000099b   156 FUNC    GLOBAL DEFAULT   13 handle_client

0xf7712000 + 0x99b = 0xf771299b

$ ROPgadget --binary integer_conversion_canary_pie | grep -E ': ((jmp)|(call)) esp'
0x00000db4 : call esp

0xf7712000 + 0xdb4 = 0xf7712db4

Problemer med PIE

Husk at i en PIE bliver disse bits tilfældigt udvalgt, når processen eksekveres: 0x003ff000

Canary

../images/CanaryInACoalMine_2.jpg

Canary

Fra Wikipedia:

"Stack canaries, named for their analogy to a canary in a coal mine, are used to detect a stack buffer overflow before execution of malicious code can occur. This method works by placing a small integer, the value of which is randomly chosen at program start, in memory just before the stack return pointer. Most buffer overflows overwrite memory from lower to higher memory addresses, so in order to overwrite the return pointer (and thus take control of the process) the canary value must also be overwritten. This value is checked to make sure it has not changed before a routine uses the return pointer on the stack.[2] This technique can greatly increase the difficulty of exploiting a stack buffer overflow because it forces the attacker to gain control of the instruction pointer by some non-traditional means such as corrupting other important variables on the stack."

Simple login - nu med canary

../images/simple_login_stack_canary.png
0804854d <main>:
 804854d: push   ebp
 804854e: mov    ebp,esp
 8048550: and    esp,0xfffffff0
 8048553: sub    esp,0x60
 8048556: mov    eax,DWORD PTR [ebp+0xc]
 8048559: mov    DWORD PTR [esp+0xc],eax
 804855d: mov    eax,gs:0x14
 8048563: mov    DWORD PTR [esp+0x5c],eax
 8048567: xor    eax,eax
 .....
 .....
 .....
 8048633: mov    edx,DWORD PTR [esp+0x5c]
 8048637: xor    edx,DWORD PTR gs:0x14
 804863e: je     8048645 <main+0xf8>
 8048640: call   8048410 <__stack_chk_fail@plt>
 8048645: leave
 8048646: ret
Tip
Læg mærke til at de lokale variable er blevet omarrangeret så vi kan ikke længere overskrive authenticated.

Simple login - nu med canary

$ ./assignments/simple_login_canary
Enter your name: poul
Enter your password: 012345678901234567890123456789012
Piss off!
*** stack smashing detected ***: ./assignments/simple_login_canary terminated
Aborted (core dumped)

Simple login - nu med canary

../images/simple_login_canary_gdb.png

Overvind canaries

  • Leak hukommelse (ja, den bliver ved med at være nyttig)
  • Brute force (kan i nogle tilfælde gøres intelligent)
  • Overskriv kun lokale variable…ikke kanariefuglen
  • Overskriv noget, som bruges, inden kanariefuglen tjekkes
  • Ingen kanariefugle på heapen!
Tip
gcc placerer pr. default ikke canaries i alle funktioner (pga. performance og kode størrelse). Vores handle_client fik ikke en kanariefugl før jeg compilede med -fstack-protector-all

fork() vs. xinetd

fork() system kaldet kloner processen i forælder og barn.

Forælder processen tager imod klienter og forker. Barnet håndterer klienten, forælderen fortsætter med at tage imod nye klienter.

fork() vs. xinetd

xinetd forker når en klient modtages. Barnet udfører et execve systemkald og lader et eksternt program håndtere forbindelsen.

fork() vs. xinetd

I xinetd håndteres forbindelsen af en "frisk" proces med nyt adresse layout og nye canaries.

I en "simpel" forking server (som dem vi har arbejdet med) arves adresse layoutet og dermed også canaries.

Tip
xinetd bruges ofte i CTF sammenhænge, men ikke så meget i den "virkelige verden".

Sjette opgave

Magen til opgave fire men nu med canary. Lytter på localhost:10005.

Find med:

$ ps aux | grep assignment | grep 10005 | awk '{print $2}'
3728

Attach med gdb:

$ gdb $C 3728

Syvende opgave

…er ikke opgave fem plus canary, som dog lytter på localhost:10006. Hvorfor er den "upraktisk" at løse?

Prøv istedet sjette opgave plus PIE. Find trampolinen i én eksekvering og brute force så alle adresser, som ASLR kunne finde på at ligge den (kun 1024 forskellige husker I nok).

Denne klarer I selv. Den ligger på localhost:10007.

Ottende opgave

Prøv med "bof" opgaven fra pwnable.kr. Den burde være triviel nu:

Nana told me that buffer overflow is one of the most common software vulnerability. Is that true?

Running at : nc pwnable.kr 9000

Niende opgave

Level 5 og 7 er format string opgaver, men resten er "simple" buffer overflows.

Jeg kan levere passwords, så I kan springe over 5 og 7…dem kan vi evt. kigge på, hvis der er interesse i en "exploitation 102".

Selvstudie - Bøger

../images/shellcoders2.jpg

Selvstudie - Bøger

../images/hacking_2E_big.png

Selvstudie - Bøger

../images/gray_hat_hacking.jpg

Selvstudie - Online

Corelan exploit writing tutorial

Selvstudie - Video

OpenSecurityTraining