Vai al contenuto
Home » Getting root: Brainpan 1

Getting root: Brainpan 1

Brainpan 1 è stata la macchina
più divertente che ho risolto questo inverno, mentre mi preparavo per l’esame
per
l’OSCP.

Questa macchina è l’ideale per chi vuole esercitarsi nella scrittura di exploit
per buffer overflow molto simili a quelli che si trovano nel corso di Offensive
Security.

Enumeration

Come sempre tutto parte da una scansione per trovare porte aperte e servizi in
ascolto. Nel mio piccolo lab, la vittima ha l’indirizzo IP 192.168.252.129.

# nmap -sV 192.168.252.129

...

# Nmap 7.70 scan initiated Tue Jun 26 08:27:06 2018 as: nmap -sV -oA brainpan 192.168.252.129
Nmap scan report for brainpan (192.168.252.129)
Host is up (1.7s latency).
Not shown: 998 closed ports
PORT      STATE SERVICE VERSION
9999/tcp  open  abyss?
10000/tcp open  http    SimpleHTTPServer 0.6 (Python 2.7.3)
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port9999-TCP:V=7.70%I=7%D=6/26%Time=5B31DCC3%P=x86_64-pc-linux-gnu%r(NU
SF:LL,298,"_|x20x20x20x20x20x20x20x20x20x20x20x20x20x20x20
SF:x20x20x20x20x20x20x20x20x20x20x20x20x20_|x20x20x20x20
SF:x20x20x20x20x20x20x20x20x20x20x20x20x20x20x20x20x20x2
SF:0x20x20x20x20x20x20x20x20x20x20x20x20x20x20x20x20x20x
SF:20n_|_|_|x20x20x20x20_|x20x20_|_|x20x20x20x20_|_|_|
SF:x20x20x20x20x20x20_|_|_|x20x20x20x20_|_|_|x20x20x20
SF:x20x20x20_|_|_|x20x20_|_|_|x20x20n_|x20x20x20x20_|x
SF:20x20_|_|x20x20x20x20x20x20_|x20x20x20x20_|x20x20_|x
SF:20x20_|x20x20x20x20_|x20x20_|x20x20x20x20_|x20x20_|x
SF:20x20x20x20_|x20x20_|x20x20x20x20_|n_|x20x20x20x20_|
SF:x20x20_|x20x20x20x20x20x20x20x20_|x20x20x20x20_|x20x
SF:20_|x20x20_|x20x20x20x20_|x20x20_|x20x20x20x20_|x20x
SF:20_|x20x20x20x20_|x20x20_|x20x20x20x20_|n_|_|_|x20x
SF:20x20x20_|x20x20x20x20x20x20x20x20x20x20_|_|_|x20x20_
SF:|x20x20_|x20x20x20x20_|x20x20_|_|_|x20x20x20x20x20x
SF:20_|_|_|x20x20_|x20x20x20x20_|nx20x20x20x20x20x20x20
SF:x20x20x20x20x20x20x20x20x20x20x20x20x20x20x20x20x20x2
SF:0x20x20x20x20x20x20x20x20x20x20x20x20x20x20x20x20x20x
SF:20x20_|x20x20x20x20x20x20x20x20x20x20x20x20x20x20x20x
SF:20x20x20x20x20x20x20x20x20x20x20nx20x20x20x20x20x20x2
SF:0x20x20x20x20x20x20x20x20x20x20x20x20x20x20x20x20x20x
SF:20x20x20x20x20x20x20x20x20x20x20x20x20x20x20x20x20x20
SF:x20x20_|nn[________________________x20WELCOMEx20TOx20BRAINPANx
SF:20_________________________]nx20x20x20x20x20x20x20x20x20x20
SF:x20x20x20x20x20x20x20x20x20x20x20x20x20x20x20x20ENTERx
SF:20THEx20PASSWORDx20x20x20x20x20x20x20x20x20x20x20x20x20x
SF:20x20x20x20x20x20x20x20x20x20x20x20x20x20x20x20x20nn
SF:x20x20x20x20x20x20x20x20x20x20x20x20x20x20x20x20x20x20
SF:x20x20x20x20x20x20x20x20>>x20")

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Tue Jun 26 08:27:46 2018 -- 1 IP address (1 host up) scanned in 39.99 seconds

Il servizio in ascolto sulla porta 9999 è programma custom che chiede una
password all’utente.

"Servizio

Sulla porta 10000 è in ascolto un web server con un’infografica di Veracode come
homepage.

"Veracode"

Con dirb sulla porta 10000 troviamo una directory /bin/ che contiene un
binario, brainpan.exe

Il file brainpan.exe apre una socket sulla porta 9999 sulla mia macchina… è
l’eseguibile del demone in ascolto sulla vittima.

A questo punto, possiamo iniziare a fare un reverse per capire qualcosa di più
sul comportamento di questo binario. Usando il comando ‘strings’ cerco stringhe
ascii intelleggibili, contenute all’interno del file eseguibile, nella speranza
che la password sia salvata in chiaro da qualche parte.

Nonostante abbia la password, ci posso fare veramente poco. Devo sperare che
brainpan.exe abbia qualche vulnerabilità, come un buffer overflow, che mi
permetta di entrare sulla macchina.

Mi scrivo un semplice fuzzer che prova, valori sempre crescenti di password,
nella speranza di causare un errore di segmentazione, o meglio ancora un
illegal instruction.

#!/usr/bin/env python

import socket
import sys

def fuzz(ip):
    for i in range(30):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            s.connect((ip, 9999))
            payload = int(i) * ("A"*100)
            s.send(payload+"rn")
            print "[*} sending " + str(len(str(payload))) + " bytes"
            s.close

        except socket.error, msg:
            print "Service die at " + str(len(str(payload))) + " bytes"
            sys.exit(1)


if __name__ == "__main__":
    if len(sys.argv) < 2:
        print "[!] usage: " + sys.argv[0] + " ip_address"
        sys.exit(0)
    else:
        fuzz(sys.argv[1])

Il fuzzer si ferma dopo aver inviato 800 byte quando il demone smette di
rispondere andando in crash. Il messaggio di errore ci dice che abbiamo
sovrascritto il valore di un registro che veniva utilizzato per referenziare
un’area di memoria.

Questo è un buon punto di partenza ma non abbiamo ancora la shell in tasca.

Modificando il mio semplice fuzzer, provo a mandare un buffer di 1000 byte
generato con pattern_create.rb per capire dopo quanti caratteri ho la
sovrascrittura dell’EIP.

Il valore del EIP è 0x35724134. Per calcolare l’offset entro il quale io
sovrascrivo il mio EIP, uso il tool pattern_offset. Ottengo che si trova dopo
524 caratteri.

Il prossimo obiettivo è quello di avere la conferma di poter sovrascrivere
ulteriormente il mio EIP. Modifico quindi il codice del mio fuzzer in maniera
tale da mandare 524 byte di padding e la stringa “BBBB” allo scopo di causare
la sovrascrittura dell’EIP con 0x42424242.

Debugger alla mano posso vedere lo stato dei miei registri. Il mio shellcode
viene copiato nello stack e referenziato dai registri opportuni.

"OllyDBG

Usando il comando objdump, cerco all’interno del binario la stringa “ff e4” che
corrisponde all’istruzione JMP ESP. Quello che voglio fare è quindi è cambiare
il valore dell’EIP in maniera tale che, la prossima istruzione da eseguire,
sarà la JMP ESP, contenuta all’indirizzo 0x311712f3. In questo modo
l’esecuzione continuerà dalla zona nello stack che sto riempiendo con il mio
shellcode.

Per evitare soprese, durante la sovrascrittura dello stack, dovrò andare a
verificare se ci sono caratteri particolari che non vengono gestiti
correttamente. Ad esempio, un ‘n’ potrebbe essere interpretato come fine del
flusso di dati ed interrompere quindi la scrittura di byte nello stack di
brainpan.exe.
Lo scopo è quindi quello di costruire uno shellcode che venga copiato nella sua
interezza all’interno dello stack.

Come trovo i bad characters? Abbastanza semplice, cambio il mio payload,
mettendo dopo la stringa “BBBB” tutti e 256 codici ascii, dallo 0x00 a 0xFF e
vedo chi viene copiato e chi invece deve essere scartato perché interrompe la
mia sequenza.

Per brainpan.exe, l’unico bad character sarà 0x00. Quando feci a Gennaio questa
macchina, usai msfvenom per creare un payload, ora però, visto che sto
studiando per la SLAE, uso lo shellcode creato per il secondo assignment per
l’esame
.

Lanciando l’exploit, dopo averlo provato sul exe scaricato dal sito, ottengo una
reverse shell non privilegiata sulla macchina.

Provandolo sul mio target reale, invece non succede niente. Perché?

Passa un giorno, mi scervello, ascolto musica neu metal, cade una bomba d’acqua
sull’Italia settentrionale, l’Inghilterra passa sulla Colombia ai rigori agli
ottavi di Russia 2018 e non riesco a dormire per l’afa.

Poi la soluzione. Quando scrissi lo shellcode per la SLAE, usai la nuova
numerazione introdotta dal kernel 4.3 in poi per le chiamate che si occupano
del networking.

In pratica, prima del kernel 4.3 tutte le system call come socket(), bind(),
connect(), … erano multiplexate dalla chiamata socketcall() che, grazie al
numero di funzione da richiamare, specificato nel registro EAX, ti redirigeva
sulla funzione corretta.

Potete approfondire qui, quando faccio la PoC analysis

Quindi, ho fatto uno shellcode troppo moderno per Brainpan. Torniamo a
msfvenom.

Da qui, il takeover della macchina prende molteplici strade. A Gennaio, presi
la strada panoramica, mentre a Giugno, quando stavo usando questa macchina per
un talk al Richmond Cyber Resilience
Forum
ho trovato una via
direttissima, quasi per caso.

Takeover percorso panoramico

Nella home dell’utente ho uno script eseguito in crontab per verificare il
funzionamento dei servizi sulle porte 9999 e 10000. Il crontab è quello
dell’utente puck, quindi non posso sfruttare questa strada per elevare i miei
privilegi.

Carico sulla macchina, usando wget ed un web server sulla mia kali, lo scrip
linuxprivchecker.py. Gli exploit per questa versione del kernel non funzionano.

In /usr/local/bin, ho trovato un programma, /usr/local/bin/validate che è SUID e
dell’utente anansi… probabilmente la strada per root è più lunga di quanto
sembri. Con peda, estensioe di gdb, verifico le feature di sicurezza di
validate… nessuna. Se è vulnerabile ad un bof, posso scrivere un exploit
semplice semplice.

Con GDB, verifico che con un input di 1000 A, causo la sovrascrittura dell’EIP e
dello stack e del registro EBX. Non ho jmp o call a esp nel mio binario e non ho
neanche jmp ebx (FF E3), quindi devo usare un ROP gadget per sovrascrivere il
mio EIP. Posso usare lo stesso EIP del bof precedente, sfruttando quella
chiamata a JMP ESP.

L’offset a cui sovrascrivo l’EIP è 116 bytes. Noto che EAX punta al mio
shellcode, nel eseguibile validate trovo una call eax (FF D0) all’indirizzo
0x804862b. Metto questo nel mio EIP, in maniera tale da saltare all’interno del
mio stack. Ora sono l’utente anansi.

Dall’output del comando ‘sudo -l’ sappiamo che il comando
/home/anansi/bin/anansi_util può essere eseguito come utente root, via sudo
senza password. Copio /bin/bash e lo eseguo come sudo -u root ./anansi_util

E siamo root.

Takeover percorso direttissimo

Il takeover per direttissima sfrutta sempre anansi_util, che può essere
eseguito anche dall’utente puck, con i privilegi di root attraverso sudo, senza
password.

Il comando attraverso il parametro manual ed un comando a caso, apre la man
page corrispondente. A questo punto, non resta altro che chiedere al nostro
pager preferito, di aprire una shell per noi e, visto che anansi_util ha
mantenuto il privilegio di root per eseguire man, mi trovo root, quasi per
caso.

Off by one

Questo post è nei miei draft da fine marzo. La SLAE e altri stravolgimenti
stanno portando via veramente tanto al mio tempo ma eccoci ancora qui.

Brainpan 1 è stata una macchina veramente divertente e non vedo l’ora di
provare la seconda parte. Questa volta, magari facendovi aspettare un po’ meno.

Enjoy it!

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.