After doing network recon in part three, it’s time to do some traffic manipulation. We will learn how to capture and modify network traffic using dnSpy. This is much easier than trying to intercept and modify traffic after it’s transmitted.
Previous parts are at:
General Traffic Manipulation Intro
Previously we used Wireshark to capture network traffic. Passive sniffing is usually easy but only useful to a degree. If the application was using TLS, we would have seen garbage after the TLS handshake 1. In these cases, Man-in-the-Middling (MitM-ing) the traffic with a proxy tool (e.g. Burp) is usually the way to go. But that introduces new challenges.
- Redirecting the traffic to the proxy.
- Masquerading as the server (e.g. make client accept our proxy’s certificate instead of server).
- Modifying packets.
I will need a lot of pages to talk about these and document what I have learned through the years. This is not the place for it.
Depending on the interception method, you can bypass some of these challenges. For example, by hooking application’s function calls that send the data, you can omit the first two (traffic redirection and server emulation). This is exactly what we are going to do to manipulate traffic in two ways:
- Debugging with dnSpy - this part.
Hooking with WinAppDbg - next part.Seems like this is much harder than I expected. I need to learn about hooking .NET functions with WinAppDbg (or in general). Added to my TODO list.
Debugging with dnSpy
My first interaction with dnSpy was when version 1 was released. I used it to modify the outgoing traffic and make myself admin. It was one of my first thick client tests and I was so proud of myself. We are going to do the same here. We will debug the application with dnSpy and then view/modify the outgoing data. We need to:
- Identify the function/code where data is assembled before transmission.
- Set a breakpoint.
- Debug the application with dnSpy.
- Use the application.
- Modify the traffic when the breakpoint is triggered.
Putting a breakpoint where the traffic is being transmitted is also viable in some use-cases. But in this case with the direct connection to MSSQL server, we want to manipulate queries.
We will start with the login request. We already know where it happens but let’s pretend we do not2. Drag and drop
dvta.exe into dnSpy. Then click on
Start. Note the dialog box allows you to enter command line parameters and set initial breakpoints. None is needed in our case so we will just press
The anti-debug does not get triggered. We could have easily removed it anyway. Fetch the login token and try to login with dummy credentials. After it fails, do not close the
Invalid Login button.
In dnSpy click on the pause button (tooltip says
We break in
System.Windows.Forms.dll > MessageBox.
This is a system DLL and not part of the application. Time for another useful dnSpy feature. Use
Debug (menu) > Windows > Call Stack or
Call stack allows us to see how we got here.
Login.btnLogin_Click is in the call chain. We can double-click on it to get to the code.
Username and password are passed to
db.checkLogin. Click on it:
Query is created in a way that is vulnerable to SQL injection (but we were expecting that in a damn vulnerable application). Put a breakpoint here to see the query in action.
Right-click on the
string text = line and select
Add Breakpoint or click on the grey edge to the left of the line number (where the red circle is in the following image):
Continue and try to login again. The breakpoint will get triggered. Close the call stack window and you should see a new window named
Locals. This window is used to view and modify the value of variables in scope.
Like any other debugger, we can
Step Over, and the rest of the usual control. You can navigate with the shortcut keys or the buttons to the right of
Step Over to get to the next decompiled instruction which is
We have a problem inside dnSpy. We cannot modify the value of
text. The cs0103 error means variable does not exist (e.g. not in scope). I am not sure why this is happening but we can modify the value in a different place. Set a breakpoint on
return new SqlCommand ... and click
This time, we want to jump inside the function call. Click
Here we can modify the value of the query. Double-click on the value in the
Locals window and type the following (don’t forget the double quotes because we are modifying a string):
"SELECT * FROM users where username='admin'"
Enter and notice the modified value is highlighted:
Continue and let this query run. We are logged in as admin.
Note that we can change this query to anything we want (e.g.
Messing with the register function is similar. Run the application with dnSpy and attempt to register any user. Do not close the message box and stop dnSpy with
Break All like we saw before.
Next, use the call stack to discover where it was called.
RegisterUser in line 64
if (dbaccessClass.RegisterUser(username, password, email)) to see the query being created. Set a breakpoint on line 93
cmd.ExecuteNonQuery(); and press
New users cannot be admin. The admin account is hardcoded. We can bypass this restriction and register a new admin.
Try to register again. When the breakpoint is reached, expand the
cmd object in the
Locals window to see the
The statement looks like this:
"insert into users values('user2','pass2','firstname.lastname@example.org','0')"
We already know the last value is
isAdmin. We can modify this to create a new admin.
Continue and login as admin with
Note: We could have done this in different ways. Another way (because in the real world you are not usually creating queries client-side and contacting the DB directly), was to put a breakpoint where the SQL statement is created and flip the value of
Grabbing the Database Credentials
Database credentials are hardcoded in the application. It’s very easy to see them using dnSpy.
We already know where the SQL queries are created. Go back to the
cmd.ExecuteNonQuery() line from last section. Run the application again and try to register a user. We want the breakpoint to be reached.
After the breakpoint is triggered, open the
Locals window and expand
this. We can see a variable called
decryptedDBPassword with value
p@ssw0rd. This means the password was stored in some encrypted format. In future sections we will return to figure out how it’s encrypted.
To see the complete connection string, expand
conn and scroll down to
In this part, we learned how to debug with dnSpy. We used our new power to manipulate the outgoing traffic, made ourselves admin, and managed to discover the database credentials.
In the next part, we will focus on client-side and break some encryption. By client-side I mean what is stored on the machine, where, and how it can be accessed.
In next part, we will use WinAppDbg to hook function calls and intercept/modify traffic. To get started see my WinAppDbg posts: