HTB - Intense
Overview - TODO: finish cleaning up notes

Short description to include any strange things to be dealt with - Linux hard difficulty
Useful Skills and Tools
Useful thing 1
description with generic example
Useful thing 2
description with generic example
Enumeration
Nmap scan
I started my enumeration with an nmap scan of 10.10.10.195. The options I regularly use are: -p-, which is a shortcut which tells nmap to scan all ports, -sC is the equivalent to --script=default and runs a collection of nmap enumeration scripts against the target, -sV does a service scan, and -oA <name> saves the output with a filename of <name>.
My nmap scan showed that only ports 22 - SSH and 80 - HTTP were open.
Port 80 - HTTP

With so little to work with, I loaded the HTTP site hosted on port 80, and found a site that greeted me with a message with guest logon credentials.
Hello ! You can login with the username and password guest.
The site also pointed out that it was open source, and had a link that let me download src.zip which contained the source code for the site.
This app is opensource !

After logging in with the guest credentials, there was a message that said:
One day, an old man said "there is no point using automated tools, better to craft his own".
This appeared to be a hint that automated tools would not work to get whatever I needed from this site.

On the /submit page I found an input box, and of course I had to see what kind of vulnerabilities it might have! First I tried the basic <script>alert('test')</script> test, and got an interesting error right away.

While testing for XSS, I found that the input box seemed to hint at SQL injection vulnerability since it seemed to have problems with me using single quotes. After testing this for a short time I decided to look into the code from the src.zip I downloaded from the main page to find out what kind of queries I might need to formulate.
Source Code Review
The file src.zip contained source code templates for the website, in a folder called app. The most interesting files were the python code files which ran the site using the Flask framework.

In the file admin.py I found a few new directory paths to check out.

The /admin page was forbidden, as expected.

As noted in the code, the two /admin/log paths required POST rather than GET requests. It looked like I would need an admin session token to get anything out of these sites.
I checked out the request to the page in Burp and found that there was a cookie header, with a base64 encoded string value for the auth parameter.
I decoded the base64 and found the auth cookie contained the username, a hex secret, and some kind of binary garbage appended to the end of the string. It looked like I needed a way to get the secret for a user admin (from the source code).
The file app.py contained a few interesting methods. It looked like submitmessage restricted the message submissions to less than 140 characters and also did some sort of "bad word" check to filter input. Afterwards it would place the message in the database if it passed those checks.
The code file lwt.py contained code for creating the session and the cookie. It also contained the code which explained the garbage at the end of the string, it was a signature comprised of the sha256 digest of the rest of the auth string.
The final file utils.py also contained some interesting methods. The method is_admin() told me that the admin user has a role of 1 in the database, get_user() and try_login() gave me some example SQL queries to test, and badword_in_str() gives me a list of filtered words ["rand", "system", "exec", "date"] to avoid using. It looked like I would not be able to execute code directly with my SQL injection and would have to pull out the data I wanted instead.
SQLite SQL Injection

Using information from utils.py I crafted the query: ' AND select secret from users where username = admin and role =1, but got another syntax error.

a') UNION SELECT password FROM users -- resulted in the error no such column: password. However, substituting 'password' with 'secret' made it so the message was submitted with no error messages. From the source code I could see that the backend database was sqlite3, so I did some research into doing SQL injection on this type of database. I found a few resources that helped explain what I was doing wrong.
Apparently the errors I received while inserting a semicolon were because I could only execute one query at a time. The first source above supplies a work-around for this problem from Stack Overflow.
Ok so I've spent some time working on this and there is a way to make it work. You can interrogate sqlite on queries like: "SELECT CASE WHEN (SELECT SUBSTRING(password, 1, 1)) = 'a' THEN 1 END". You can write a simple python script that changes the 1 inside substring and the 'a' char. In this way you can pretty much bruteforce the output of the column. – RobertM Jul 16 at 19:11
I seemed like I would have to brute force each character of the secret string. I did some more reading to see how to craft this type of SQL query since it was new to me.
I was encountering a problem with my output only matching a zero '0' for the secret until I searched for SQLite3 error-based injection and found a (russian-language) site that showed how to use MATCH to get this to work properly. https://translate.google.com/translate?hl=en&sl=ru&u=https://rdot.org/forum/showthread.php%3Fp%3D26419&prev=search

However I still encountered a problem, since it seemed as if I wasn't able to use the MATCH() method in this context.

I also made a mistake when typing in the method SUBSTR, and I got a bit frustrated with sending individual queries through the website so I moved on to Burp suite to optimize my query testing. After awhile I finally worked out the kinks and got a working query.

To test my theory I used Burp's Intruder to test a brute force of all alpha-numeric characters on the first character of the 'secret' string.

I set Intruder to only fuzz the single character at a time in my query.

After letting the fuzzer run, I found that the first character in the admin's secret was 'f'. This was the only request that received an HTTP 200 OK message.
Using python to brute force
I used the cookie I already had to pull out the secret string 84983c60f7daadc1cb8698621f802c0d9f9a3c3c295c810748fb048115c186ec which was 64 characters long. This let me know how many characters I needed to brute force for the admin secret. From this I used Python to write a brute force program to iterate through all 64 characters in the secret. The following sources helped me:
To get all alpha-numeric chars: https://stackoverflow.com/questions/5891453/is-there-a-python-library-that-contains-a-list-of-all-the-ascii-characters
To print output dynamically on one line: https://stackoverflow.com/questions/3249524/print-in-one-line-dynamically
To get the run-time of a program or method: https://stackoverflow.com/questions/1557571/how-do-i-get-time-of-a-python-programs-execution
My finalized python script was fairly short, and mostly consisted of creating a request with the SQL injection query in it. I then iterated over all printable ASCII characters for each of the 64 positions in the secret. I also added a little timer to see how long it would take to brute force the whole secret.
The whole brute force went pretty quickly! From the timer I found that it took less than 50 seconds to go through the whole string.
Next I crafted my new auth cookie, base64'd it, and got the result: dXNlcm5hbWU9YWRtaW47c2VjcmV0PWYxZmMxMjAxMGMwOTQwMTZkZWY3OTFlMTQzNWRkZmRjYWVjY2Y4MjUwZTM2NjMwYzBiYzkzMjg1YzI5NzExMDU7yUJDSrHY6MXeDWIMvm6WVBrBiI11ILXthKcNc22KYMY=

Using this cookie, however, broke the whole site and made it so no pages would load. I figured it had something to do with the unreadable signature characters that were appended to the end of the secret in the cookie.
I went back to the source code file lwt.py , which gave me the answer. The data after the ; was a signature created by running sha256 on secret + MSG.
In order to create the signature, I needed to run the create_cookie() method above to encode and sign the username and secret.
https://github.com/bwall/HashPump
TODO: find out what happened to this script on import...should be below
implementing hashpumpy...
The final admin cookie was:
For some reason the hashpumpy module added the guest cookie to the admin cookie, then appended the signature of them both together.

However this mega-cookie worked and I was able to login to the /admin page successfully.
Initial Foothold
Remote Code Execution (Limited)
Back in the admin.py file it mentioned using the logfile and logdir properties on their respective directories, along with the POST method after logging in as admin. This looked like a task for Burp Repeater.

The logfile property was susceptible to directory traversal, and through Burp I was able to download /etc/passwd. There were only two users that had the ability to login: root and user. I noticed an unusual user named debian_snmp, so I decided to see what I could find using the SNMP service. (Another nmap scan revealed that UDP port 161 was open, which is the default SNMP port!)

While looking at the SNMP configuration files, I found a read/write community string of SuP3RPrivCom90 in snmpd.conf.

ssh.conf - nothing useful

I also used the logdir property to enumerate the contents of /home/user. This folder contained the file user.txt, so I knew I was on the right track.
User.txt

This was interesting...it isn't very often that I am able to get the user flag through web requests.

I also checked for the presence of the authorized_keys file, since this is a great way to gain persistence.
Enumerating SNMP
Next, I spend some time trying to find information on how to use that community string I had found to gain access to the machine. I found a nice blog that showed me exactly what I needed to do to get a shell through SNMP.
installed snmp MIBs
Not much information gained from SNMP walk
https://medium.com/rangeforce/snmp-arbitrary-command-execution-19a6088c888e
snmpset -m +NET-SNMP-EXTEND-MIB -v 2c -c host-with-snmpd.lan 'nsExtendStatus."command"' = createAndGo 'nsExtendCommand."command"' = /bin/echo 'nsExtendArgs."command"' = 'hello world'
created my command to send nc reverse shell
Unfortunately the version of nc on the victim's computer did not have -e functionality, so I wasn't able to get it to send me a reverse shell.
Getting a shell
I tried sending a reverse shell, but got an End-of-Line error
connected to my
after trying a few things realized that some of the internal quotes needed to be escaped for it to run properly
got a shell back on my waiting nc listener
Path to Power (Gaining Administrator Access)
Enumeration as Debian-snmp
Debian-snmpDownloaded a few interesting files from user's home folder...then lost my shell again when I cancelled the http server (right after I realized I should have put my ssh key there!)
TODO: Where is the note_server.c code?
Analysis of the note_server.c code showed me that the program was looking for a connection to 127.0.0.1 on port 5001.
I tried echoing my ssh key to user but got a permission denied error, so I tried to see if I could do the same for the Debian-snmp user, and got partial success
I was successful in copying my key, but I wasn't able to login and get a shell. I tried a few bypass methods, but it seemed as if they had it locked down.
Even though I couldn't login, I was still able to use SSH to create a tunnel to the machine without running any commands. This came in handy later when I wanted to connect to a port that was only open on the local host.
note-server was running as root
Binary Exploitation
kept getting errors when trying to set break points in gdb. I got frustrated with this and moved on to other machines until the box retired and I was able to watch Ippsec's video, and in the end used the exploit from the official write-up.
gdb ./note_server -ex 'set follow-fork-mode child' -ex 'break 82' -ex 'run'
Got address of /xf54
I did learn something very useful for the future - compiling with -ggdb will compile with source code intact - very useful for analysis and debugging
Wrote a few different Python scripts trying to exploit this, but in the end I needed to look at the official writeup to find out what I had been doing wrong.
Copied and cleaned up the code from the official writeup, then ran it TODO: explain what it does
Root.txt
sdg

Thanks to sokafr for something interesting or useful about this machine.
If you like this content and would like to see more, please consider buying me a coffee!
Last updated
Was this helpful?