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:
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.
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:
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:
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 Ok
.
Starting the application with dnSpy
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
).
"Break All" button
We break in System.Windows.Forms.dll > MessageBox
.
MessageBox break
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
.
Viewing call Stack
Call stack allows us to see how we got here.
Call stack displayed in dnSpy
Login.btnLogin_Click
is in the call chain. We can double-click on it to get to the code.
btnLogin_Click
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):
Breakpoint set
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.
Breakpoint triggered
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);
.
text cannot be modified
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
.
Breakpoint at return triggered
This time, we want to jump inside the function call. Click Step Into
.
Inside SqlCommand constructor
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:
SQL query modified
Press Continue
and let this query run. We are logged in as admin.
Logged in as admin
Note that we can change this query to anything we want (e.g. INSERT
or DELETE
).
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.
dnSpy after Break All
Next, use the call stack to discover where it was called.
btnReg_Click
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
.
RegisterUser
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
:
Register user SQL statement
The statement looks like this:
"insert into users values('user2','pass2','[email protected]','0')"
We already know the last value is isAdmin
. We can modify this to create a new admin.
Modified register payload
Press Continue
and login as admin with user2:pass2
.
New admin user in the database
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
.
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.
DB password
To see the complete connection string, expand conn
and scroll down to _connectionString
:
Connection string
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: