Puny-Code, 0-Click Account Takeover


Hello! This blog post is a detailed version of the talk given by Amir and me at Nahamcon 2025. We usually choose a topic to focus on, spending time on it - it might turn into a 0day or just a simple checklist. Then we apply our findings to our daily hunting. Last year, we found an interesting inconsistency between mail servers and databases - there was a parsing disagreement on some characters. We immediately set up a testbed to investigate, and it led to discovering a neat attack. Actually, it had been discovered before us; we just put it into action and made around $50k from it. I’m not saying it was a 0day, but many programs were vulnerable. My most recent bug using this technique was about two weeks ago.
When it comes to inconsistency, I believe this is one of the most important root causes in security. It's at the center of many bugs and even led to the discovery of a whole class of vulnerabilities: HTTP Request Smuggling. Now, take a look at these URLs:
https://attacker.com%bf:@benign.com
https://attacker.com\@benign.com
Imagine there's a security function that checks if the host is legitimate, and a cURL function that sends the HTTP request. So, what's the host here? attacker.com
or site.com
? Honestly speaking, it doesn't even matter. The key point is that the layers should never disagree on host extraction. If they do, there's a high chance of a vulnerability.
Amir tweeted about this case about a year ago as a white-box challenge, as he often does. Many hunters got involved, but we never disclosed that it's actually a profitable vulnerability. I call it a money-maker because it's easy to test - not a complex gadget-chaining exploit or anything like that. It can happen in different parts of web applications. I'm focusing on the reset password functionality using an arbitrary mail server and MySQL. So, let's review a reset password function together:
The user enters their email address
The web app checks the database to see if the user exists or not?
To check, it runs a select query with the user’s input
If the user is found, a token gets saved in the database
The token will also be emailed to the user. So, that’s the workflow — simple and clear.
The question is, which email is passed to the SMTP server? The one the user typed in, or the one in stored in the database?
If it’s pulled from the database, then the web app is safe. If it’s pulled from the user input, then the web app is vulnerable. You might be wondering why this happens, and how? Let’s check out SMTP servers behaviour when they encounter a different character set. As you can see, the SMTP server treats “a” and the odd “a” as completely different. They’re two separate email addresses and never conflict with each other:
Now, let’s take a look at MySQL in a same situation. MySQL casts the odd “a” to the normal “a,” and that’s where the story begins. Let’s dig into it a little bit more:
In the first query, MySQL casts the odd “a” to the normal “a” so they end up equal. But in the second query, “a” doesn’t match the odd “a” because of the collation settings. The good news for hunters is that MySQL’s default settings handle that casting automatically - so if developers just code things the usual way, that inconsistency comes in. Where did this odd “a” come from? Just pick a letter—like “a”—and run a simple fuzzer:
You’ll quickly find all the characters that get treated like “a.” It’s really simple, i’ll show you the fuzzer code:
import mysql.connector
conn = mysql.connector.connect(
host="localhost",
user="test",
password="test",
database="test"
)
cursor = conn.cursor()
for i in range(0, 0x10ffff + 1):
char = chr(i)
cursor.execute("SELECT %s = 'a' AS is_equal", (char,))
result = cursor.fetchone()
if result[0]:
print(f"Unicode Character {i} ({char}) is equal to 'a' in MySQL")
cursor.close()
conn.close()
There are various attack scenarios, I’m picking three to discuss:
Forgot Password Section
OAuth Provider Email Trust
OAuth Provider Redirect URL
Forgot Password Section
Let’s go for the first one, the scenario is simple: find an email to take over. In the forgot password section, enter the victim’s email address, intercept the HTTP request, and change the email to the puny-coded version. The reset password link for the victim will then be emailed to the puny-coded email, which is under your control. The attacker enters victim@gmail.com, but with the odd “a” which is not a normal “a”. This email actually belongs to the attacker. In the next step, the web app runs a SQL query to check if the email exists. Here it gets interesting: MySQL casts the odd “a” into a normal “a,” so the attacker’s email turns into the real victim’s email address. Since the email does exist in the database, a token is issued and saved. Then, the SMTP server sends the reset link to victim@gmail.com - but with the odd “a,” which goes to the attacker’s mailbox and that’s it. The attacker now has the victim’s reset link.
Simple. Practical. 0-click ATO. Here’s an example from a public program on HackerOne. As you can see, I laid out the full attack scenario in the report. We’ve actually found a bunch of websites that were vulnerable to this:
In this case, they should’ve paid 25k - but unfortunately, the asset wasn’t considered a main one. Still, 6k is 6k and i’m good with it
Is WordPress Safe?
We went a little bit too far and even started checking WordPress. Turns out, it’s not vulnerable - even when using the risky collation. I wanna show you this secure case, it might be interesting.
WordPress uses this collation and as you can see, “a” is equal to the odd “a”, at the first glance it might be dangerous:
But it’s not vulnerable. WordPress uses the user’s input to query the database, but when it comes to sending the email, it uses the email pulled from the database. This is so important, So even if you enter a puny-coded version of victim@gmail.com, WordPress generates the reset link for the real victim@gmail.com and sends reset password link to the legitimate email address:
OAuth Provider Email Trust
Now let’s look at the OAuth provider email trust issue. Some websites are safe in the forgot password flow, but they’re still vulnerable in other areas - like OAuth login. If the provider responds with a puny-coded version of the email, i’m talking about the callback phase during login, the web application can become vulnerable to the same attack. Here’s the OAuth flow:
In the final step, the web app calls the provider’s API and grabs the malicious email. That’s where the vulnerability kicks in. The app runs a query to find that email in the database, and if MySQL casts the odd “a” to a normal “a,” the attacker ends up logging in as the victim.
We checked Google - it delivers the email safely. I’m not gonna comment on Apple and Facebook; you can test those yourself. But what’s surprising is that Login with GitLab is actually vulnerable. It delivers the puny-coded email to the application, which leads to the vulnerability, Of course, that’s only if the application doesn’t validate the email properly or is using a risky collation. but the result is: if you go in a website and see “login with gitlab” button, it’s likely vulnerable.
We've created a web application that is vulnerable to this attack. It's simple to set up with just a docker compose up
command. You can find it here. Feel free to practice with it.
OAuth Provider Redirect URL
This attack can also be carried out in the OAuth provider’s callback URL. I’m not gonna dive into it—I just want to introduce the idea. The concept is basically the same as the previous one.
The End
There are also more attack vectors with puny-code. If you find anything new, it’d be great to share it online. In conclusion, while we've discovered several bugs, there are likely many more yet to be found. Puny-code presents additional attack vectors that need attention. Sharing new findings online can help improve security. Stay vigilant and continue exploring potential vulnerabilities. I hope you found this blog post useful, thanks for the reading.
Subscribe to my newsletter
Read articles from Voorivex directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Voorivex
Voorivex
I work as an instructor and hunter, leading a small team to grow, learn, and innovate.