Skip to main content

The Legacy of VB6 and the ClientSide Auth Bypass

Illustration of The Legacy of VB6 and the ClientSide Auth Bypass
Robert Kruczek

In the modern era of microservices and single page applications, we sometimes forget the „old times” of desktop development. Recently, we had the pleasure of testing a legacy desktop application written in Visual Basic 6 (VB6). What started as a routine assessment ended with us dusting off our reverse engineering skills to write a classic “crack”, proving that client side logic is never to be trusted.

The Setup: A Blast from the Past

The engagement scope was a desktop application used internally by the company. As soon as we inspected the binary, the unmistakable signature of a Visual Basic 6 application stared back at us.

For the uninitiated, testing these applications is often a binary experience: either they are impenetrable fortresses of obscurity, or they fall apart like a house of cards. This one was definitely the latter.

The First Thread: Direct Database Connection

Our first step was traffic analysis. We fired up Wireshark to see who the application was talking to. We expected to see HTTP/HTTPS traffic communicating with a backend API. Instead, we saw direct TCP traffic on port 1433.

The application was communicating directly with an MSSQL database. If the client talks directly to the DB, it must possess the credentials to do so.

A quick strings analysis of the binary (and later confirmed via decompilation) revealed the “keys to the kingdom”: hardcoded database credentials.

At this point, we could simply connect to the database using a tool like DBeaver or HeidiSQL and dump all the data. Technically, we had already won. But we wanted to see how deep the rabbit hole went. Specifically, regarding the application’s authentication logic.

The „Authentication” Mechanism

We asked ourselves: “If the application connects to the DB using hardcoded credentials, how does it verify the actual human user logging in?”

We loaded the binary into Ghidra to analyze the login routine. What we found was a textbook example of Insecure Client-Side Authentication. The logic went roughly like this:

  1. User enters Login and Password in the GUI.

  2. The application (client-side) constructs a raw SQL query:

  1. The application sends this query to the DB.

  2. If the DB returns a row, the application says: “Access Granted.”

  3. If the DB returns nothing, the application shows a “Bad Password” error.

The critical flaw: The server (database) isn’t deciding if you are logged in. The application running on your computer decides. And since we control the computer, we control the decision. Developing the Crack

To demonstrate the severity of this flaw to the client, we decided to go beyond a simple report. We decided to create a crack - a modified version of the executable that bypasses the password check entirely.

Using Ghidra, we located the assembly instructions responsible for the logic described above.

We identified a conditional jump instruction (specifically a JZ / JNZ - Jump if Zero/Not Zero). This instruction checked the result of the SQL query.

  • Original Logic: If the query result is empty -> Jump to “Error Message”.

We didn’t need a valid password. We just needed to stop the application from jumping to the error message.

The Patch: We replaced the conditional jump instruction with NOPs (No Operation - 0x90). In assembly, this effectively tells the processor: “Do nothing here, just continue to the next line.”

By overwriting the jump, we forced the code execution to fall through directly into the “Login Successful” routine, regardless of what the database returned.

The Result

We saved the modified binary and launched it.

Username: admin

Password: I_dont_know_the_password

Click: Login

Result: We were logged in as the administrator.

The application, now “lobotomized” by our NOPs, didn’t care that the database returned zero results for that password. It simply proceeded to grant us access to the main interface. Because the application trusted the client to handle security, removing that check on the client removed the security entirely.

Summary

This finding highlights a critical architectural rule: Never trust the client.

Validation of credentials must happen on a trusted server (via an API or Middleware), or through native database authentication where the database itself rejects the connection. Relying on a boolean check inside a binary that the user controls is equivalent to asking a burglar to please check if the door is locked and believing them when they say “yes”.

Key Takeaways

  • Do not embed database credentials in client binaries. They can always be extracted.
  • Do not use direct SQL connections from client apps. Use a middleware API (REST/gRPC) to handle business logic and authentication.
  • Client-side logic is mutable. Attackers can patch, hook, or modify your application code to bypass checks that don’t have a server-side counterpart.

Other Insights

Illustration of Don’t forget to track your vendors’ security advisories

Don’t forget to track your vendors’ security advisories

Adam Borczyk

Every now and then a new critical vulnerability emerges, that has a worldwide impact and is easy to exploit. We’ve all seen Log4Shell, Spring4Shell… and now React2Shell. Despite media coverage, application teams often miss the importance of security patches. This particular vulnerability was found during a recent pentest in a production environment of our client, and it does not require any authentication. Global public disclosure of React2Shell happened just 5 weeks before this pentest.

READ article
Illustration of Active Directory Killchain: How three misconfigurations led to a Domain Compromise

Active Directory Killchain: How three misconfigurations led to a Domain Compromise

Marek Rzepecki

In the world of Active Directory (AD), an administrator needs to keep an eye on countless factors: keeping servers up to date, disabling redundant services, and managing privilege granulation across user groups. However, sometimes, to take over the whole domain (and log into all machines as any user), an attacker doesn't need to exploit technical vulnerabilities or search for CVE’s. It is often enough if an administrator makes a few small mistakes in configuring the relations between different objects in AD. The killchain presented below is a textbook example of such exploitation, where three separate issues were chained together to achieve a critical impact.

READ article
Illustration of From Username to RCE

From Username to RCE

SZYMON STODTKO

"Never trust data sent by the user." This sentence is repeated like a mantra at every security training. Today, we will look at a case where a general lack of field filtration in an application, particularly the username combined with weak file validation, led to a critical Remote Code Execution (RCE) vulnerability. During web application penetration testing, I came across an interesting chain of vulnerabilities. The application had a function for creating users and assigning them disk space for video files. This mechanism, though seemingly standard, turned out to be the system's Achilles' heel.

READ article
A professional cybersecurity consultant ready to assist with your inquiry.

Any questions?

Happy to get a call or email
and help!