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.
- ???
- Profit.
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.
Login
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 Ok
.
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 Break All
).
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 Ctrl+Alt+C
.
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):
Click on 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 Into
, Step Over
, and the rest of the usual control. You can navigate with the shortcut keys or the buttons to the right of Start/Continue
. Press F10
or Step Over
to get to the next decompiled instruction which is Console.WriteLine(text);
.
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 Continue
.
Bypassing Login
This time, we want to jump inside the function call. Click Step Into
.
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'"
Then press Enter
and notice the modified value is highlighted:
Press 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. INSERT
or DELETE
).
Register
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.
Click on 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 Continue
.
Registering Admins
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 CommandText
:
The statement looks like this:
"insert into users values('user2','pass2','user2@example.com','0')"
We already know the last value is isAdmin
. We can modify this to create a new admin.
Press Continue
and login as admin with user2:pass2
.
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 isadmin
to 1
.
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 _connectionString
:
Conclusion
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: