Ever tried opening a door with the wrong key, only to realise the lock was broken all along? That’s exactly what happened when I stumbled upon a fascinating vulnerability while working on a CTF challenge. I was facing a login page, knowing the username was “admin” but completely clueless about the password. Brute force? Nah, too obvious. What I discovered instead was way more interesting — a subtle PHP quirk that literally lets you waltz past authentication by sending… an empty array. Yep, you read that right.

Let me take you through this wild ride where a simple function called strcmp() becomes the weakest link in the security chain, and more importantly, how you can spot and fix this vulnerability before hackers do.
Why Should You Care About This?
If you’re building web applications with PHP, learning this could save you from a devastating security breach. If you’re learning cybersecurity, this is one of those “aha!” moments that makes you think differently about how applications handle data. Here’s what’s at stake:
🔓 For Developers: One loose comparison operator could expose your entire authentication system
🎯 For Security Learners: Understanding type juggling vulnerabilities opens doors to finding similar issues
💡 For Everyone: Real-world example of how seemingly innocent code becomes a critical security flaw
The Setup: A Normal-Looking Login Page (Or So I Thought)
Picture this: I’m working on a CTF exercise with a basic web application. There’s a login page asking for username and password. I knew the username was admin (classic!), but the password? Complete mystery.

I tried the usual suspects — admin, password, 123456—nothing worked. Brute forcing didn't feel right for this challenge, so I started poking around. That's when I noticed an FTP server running on the same machine.
The Plot Thickens: Anonymous FTP Access
I logged into the FTP server using the anonymous user (yes, sometimes it's that easy in CTF challenges!). Inside, I found something golden—the actual PHP source code for the login page:
if($_GET['login']==="1"){
if (strcmp($_POST['username'], "admin") == 0 && strcmp($_POST['password'], $pass) == 0) {
echo "Welcome! </br> Go to the <a href=\"dashboard.php\">dashboard</a>";
setcookie('pass', $pass, time() + 365*24*3600);
}else{
echo "<p>Bad login/password! </br> Return to the <a href=\"index.php\">login page</a> <p>";
}
exit();
}At first glance, this looks perfectly reasonable. The code uses strcmp() to compare the submitted username and password against expected values. But here's where things get spicy.
The Vulnerability: When strcmp() Goes Rogue
The strcmp() function in PHP is designed to compare two strings. It returns:
0if strings are identical (what we want for login!)- A negative number if the first string is “less than” the second
- A positive number if the first string is “greater than” the second
But here’s the kicker: What happens when you DON’T pass it a string?
The Magic Trick: Array to the Rescue
After some Googling, I found an incredible article that explained the vulnerability. When you pass an array instead of a string to strcmp(), PHP throws a fit internally and returns NULL.
Now, here’s where PHP’s “loose comparison” operator (==) becomes the villain. In PHP:
NULL == 0 // This evaluates to TRUE!So instead of sending:

password=wrongpasswordI sent:

password[]=This tiny change made PHP interpret the password as an empty array instead of a string. The strcmp() function received an array, couldn't process it, returned NULL, and because the code used == instead of ===, that NULL was treated as 0—which is exactly what we needed to bypass authentication!

Real-World Example: The Attack
Here’s what the HTTP request looked like:
Normal (failed) request:
POST /index.php?login=1
username=admin&password=myguessExploit request:
POST /index.php?login=1
username=admin&password[]=Boom! The login check passes, and I’m in. No password needed.
Why This Works: PHP Type Juggling Explained Simply
Think of PHP’s type system like a very accommodating friend who tries too hard to make things work. When you compare different data types using ==, PHP attempts to convert them to compatible types before comparing.
Loose comparison (==): "Are these kinda similar if I squint?"
Strict comparison (===): "Are these EXACTLY the same, same type and value?"
When strcmp() receives unexpected input (like an array), it triggers an error and returns NULL. The loose comparison then says, "Well, NULL and 0 are basically the same thing, right?" And just like that, your security crumbles.
The Fix: One Character That Saves Everything
The solution is beautifully simple — use strict comparison (===) instead of loose comparison (==):
Vulnerable code:
if (strcmp($_POST['username'], "admin") == 0 && strcmp($_POST['password'], $pass) == 0)Fixed code:
if (strcmp($_POST['username'], "admin") === 0 && strcmp($_POST['password'], $pass) === 0)With strict comparison, NULL === 0 evaluates to FALSE because they're different types (NULL vs integer). The authentication fails as it should, and hackers go home disappointed.
Bonus Protection: Type Checking
For extra safety, validate your inputs before passing them to strcmp():
if (is_string($_POST['username']) && is_string($_POST['password'])) {
if (strcmp($_POST['username'], "admin") === 0 && strcmp($_POST['password'], $pass) === 0) {
// Login successful
}
} else {
// Reject the request - someone's trying something fishy
}Other Functions Affected by This Issue
This isn’t just about strcmp(). Several PHP functions exhibit similar behavior when receiving unexpected data types:
strcasecmp()- Case-insensitive string comparisonstrpos()- Find position of substringsubstr()- Get substringmd5()- MD5 hash (when used with==comparison)
Always validate input types and use strict comparisons when dealing with security-critical operations!
Try It Yourself: Hands-On Practice
Want to test this vulnerability in a safe environment? Check out the OffSec practice machine where I discovered this:
🔗 Practice Lab: https://portal.offsec.com/machine/potato-445/overview/details
Quick steps to reproduce:
- Identify the login page
- Find the FTP server (use
nmapor similar) - Access FTP anonymously
- Review the PHP source code
- Craft your payload with
password[]= - Bypass authentication like a pro!
Key Takeaways: Your Security Checklist
✅ Always use strict comparison (===) for security-critical checks
✅ Validate input types before processing them
✅ Never trust user input—even seemingly harmless form fields
✅ Review legacy code for loose comparisons in authentication
✅ Use modern password verification functions like password_verify()
✅ Test your applications with unexpected input types (arrays, objects, etc.)
TLDR Cheat Sheet
The Vulnerability:
// VULNERABLE - Don't use this!
strcmp($_POST['password'], $correctPass) == 0The Exploit:
Send: password[]= instead of password=something
Result: strcmp() returns NULL, NULL == 0 is TRUE, authentication bypassedThe Fix:
// SECURE - Use this instead!
if (is_string($_POST['password']) && strcmp($_POST['password'], $correctPass) === 0)Remember: The difference between == and === isn't just syntax—it's the difference between a secure application and a hacker's playground. One extra character, massive security improvement.
Now go forth and write more secure PHP code! And maybe check your old projects for this sneaky little bug. Your future self will thank you. 🛡️