![](/content/images/2020/05/image-43.png)
Obscurity was a medium rated Linux machine that required some fuzzing to find a hidden web directory containing a python file. This file was the source code for the web server and contained a vulnerability allowing RCE which lead to a reverse shell as www-data. We had read and execute permissions on an overconfident user's script that encrypted his password - this was cracked allowing SSH access as Robert. From here, Robert had sudo access to a python script which I found two ways of exploiting to gain root access. I added obscurity.htb to my /etc/hosts file and got started.
Enumeration
nmap scan:
![](/content/images/2020/05/image-44.png)
Let's see what's going on with port 8080:
![](/content/images/2020/05/image-45.png)
Scrolling through the page reveals some interesting things:
![](/content/images/2020/05/image-46.png)
![](/content/images/2020/05/image-47.png)
We are very kindly provided with the filename 'SuperSecureServer.py' and know that it's hidden in a secret directory somewhere so let's use wfuzz
to find it:
![](/content/images/2020/05/image-48.png)
Let's download the file:
![](/content/images/2020/05/image-49.png)
Initial Foothold
My python skills aren't that great but I've learned that using 'exec' in a webapp is a no-no and so this part of the code caught my eye:
![](/content/images/2020/05/image-50.png)
It seems that whatever you put as the web path gets executed. I tried putting in my usual python reverse shell but it didn't work. After a couple of hours filled with a lot of experimentation and cursing, I figured out that enclosing the reverse shell commands in single quotes as well as a new line did the trick. The request looked like this in Burp before URL encoding:
![](/content/images/2020/05/image-51.png)
With a netcat listener, I got a shell as www-data:
![](/content/images/2020/05/image-52.png)
User Pivot
While enumerating the system, I found interesting files in Robert's home directory:
![](/content/images/2020/05/image-53.png)
Of course I checked 'passwordreminder.txt' first but found it was encrypted:
![](/content/images/2020/05/image-54.png)
Check.txt:
![](/content/images/2020/05/image-55.png)
Out.txt is encrypted as well:
![](/content/images/2020/05/image-57.png)
It's pretty clear that 'SuperSecureCrypt.py' is used to encrypt his password so let's take a look at the encryption/decryption functions:
![](/content/images/2020/05/image-56.png)
I'm still on the beginning stages of learning about cryptography but I can see that the encryption technique is quite basic. At this point we have check.txt, out.txt and the decrypt/encrypt function - we just need the key. I thought about this talk I saw at BSides SF and figured swapping things around could possibly output the key. I downloaded the script to my machine to play with and not leave breadcrumbs behind. Let's see what the script expects:
![](/content/images/2020/05/image-59.png)
Giving it 'out.txt' as the input and the contents of 'check.txt' as the key should result in the real key being output so I tried that:
![](/content/images/2020/05/image-60.png)
It worked:
![](/content/images/2020/05/image-61.png)
Now let's get Robert's password:
![](/content/images/2020/05/image-62.png)
![](/content/images/2020/05/image-63.png)
The password works and we now have SSH access as Robert:
![](/content/images/2020/05/image-64.png)
User flag:
![](/content/images/2020/05/image-65.png)
Privilege Escalation
Robert has sudo access to run BetterSSH.py:
![](/content/images/2020/05/image-66.png)
Route #1 - intended?
Let's see what happens when we try to run BetterSSH.py:
![](/content/images/2020/05/image-70.png)
Hmm, let's look at some of the code:
![](/content/images/2020/05/image-68.png)
It looks like /etc/shadow is being opened, written to a randomly named file in /tmp/SSH then this file is then deleted. Let's see what happens if we create the /tmp/SSH directory and run the script:
![](/content/images/2020/05/image-71.png)
No more errors at least. With the BetterSSH script deleting the randomly named file very quickly, there's a race condition - if we can copy the file somewhere else before it's deleted, we should be able to read the contents of /etc/shadow. I made a new directory and used the watch
command to copy anything in /tmp/SSH to /tmp/tmp2/ every 0.1 seconds:
![](/content/images/2020/05/image-72.png)
In another SSH session, I ran the BetterSSH script twice to make sure I caught it:
![](/content/images/2020/05/image-73.png)
There are now files in /tmp/tmp2:
![](/content/images/2020/05/image-74.png)
We now have password hashes:
![](/content/images/2020/05/image-75.png)
I saved the root hash to root.hash on my machine and cracked it:
![](/content/images/2020/05/image-76.png)
This password works and let me su to root:
![](/content/images/2020/05/image-77.png)
Root flag:
![](/content/images/2020/05/image-78.png)
Route #2 - unintended?
It turns out that while Robert does not have write permissions on the BetterSSH.py file, he can rename the 'BetterSSH' directory so I renamed it to 'TMP' and created a new 'BetterSSH' directory:
![](/content/images/2020/05/image-79.png)
In it, I created my own BetterSSH.py with reverse shell code as below:
![](/content/images/2020/05/image-80.png)
I ran it with sudo privileges:
![](/content/images/2020/05/image-81.png)
With netcat listening, I caught a root shell:
![](/content/images/2020/05/image-82.png)