Manipulate/bypass md5 in php? - php

There is a programm from my University (IT-Securiy) where you have to try capturing "flags" by manipulating php or html code for example. We only see the code below and the programm runs on a website of them. So we need to somehow manipulate the url or something like this... but I really stuck on this one here:
<?php
require_once '_flags.php';
highlight_file(__FILE__);
if (isset($_GET['pw']) &&
md5($_GET['pw']) == '0e13371337133713371337133713371337') {
echo $doyouphp3_flag;
}
I know, that I have to submit a password by adding ...
?pw=...
... at the end of the url, but I just dont know what...
Is there any way to bypass the md5 function for example, because I dont think they want me to brutforce the password...
Hope someone can help me or at least give me a hint.
Marius

You just need to observe that 0e13371337133713371337133713371337 is a number, and it isn't a valid md5 hash.
Then you need to know how php loose comparison (with the == operator) involving numerical strings works. You can read that on the documentation:
If you compare a number with a string or the comparison involves
numerical strings, then each string is converted to a number and the
comparison performed numerically.
So, now you know that
var_dump('0' == '0e13371337133713371337133713371337'); // true
var_dump('0e123' == '0e13371337133713371337133713371337'); // true
var_dump('0e65165165165165' == '0e13371337133713371337133713371337'); // true
So, you just need to find a md5 hash that's also a number.
The md5 hash of 240610708 is 0e462097431906509019562988736854, and "0e13371337133713371337133713371337" == "0e462097431906509019562988736854" is true. So you can use that. But also QNKCDZO should works just fine.
What's the lesson here? That md5('240610708') == md5('QNKCDZO') is true and it's dangerous, and that you should use === instead of ==.

Related

CTF Type Juggling with ripemd160 hash

I am trying to solve a CTF in which the juggling type should be used. The code is:
if ($_GET["hash"] == hash("ripemd160", $_GET["hash"]))
{
echo $flag;
}
else
{
echo "<h1>Bad Hash</h1>";
}
I made a script in python which checks random hashes in ripemd160 that begins with "0e" and ends with only numbers. The code is:
def id_generator(size, chars=string.digits):
return ''.join(random.choice(chars) for _ in range(size))
param = "0e"
results = []
while True:
h = hashlib.new('ripemd160')
h.update("{0}".format(str(param)).encode('utf-8'))
hashed = h.hexdigest()
if param not in results:
print(param)
if hashed.startswith("0e") and hashed[2:].isdigit():
print(param)
print(hashed)
break
results.append(param)
else:
print("CHECKED")
param = "0e" + str(id_generator(size=10))
Any suggestions on how to solve it? Thank you!
There seems to be a bit of misunderstanding in the comments, so I'll start by explaining the problem a little more:
Type juggling refers to the behaviour of PHP whereby variables are implicitly cast to different data types under certain conditions. For example, all the following logical expressions will evaluate to true in PHP:
0 == 0 // int vs. int
"0" == 0 // str -> int
"abc" == 0 // any non-numerical string -> 0
"1.234E+03" == "0.1234E+04" // string that looks like a float -> float
"0e215962017" == 0 // another string that looks like a float
The last of these examples is interesting because its MD5 hash value is another string consisting of 0e followed by a bunch of decimal digits (0e291242476940776845150308577824). So here's another logical expression in PHP that will evaluate to true:
"0e215962017" == md5("0e215962017")
To solve this CTF challenge, you have to find a string that is "equal" to its own hash value, but using the RIPEMD160 algorithm instead of MD5. When this is provided as a query string variable (e.g., ?hash=0e215962017), then the PHP script will disclose the value of a flag.
Fake hash collisions like this aren't difficult to find. Roughly 1 in every 256 MD5 hashes will start with '0e', and the probability that the remaining 30 characters are all digits is (10/16)^30. If you do the maths, you'll find that the probability of an MD5 hash equating to zero in PHP is approximately one in 340 million. It took me about a minute (almost 216 million attempts) to find the above example.
Exactly the same method can be used to find similar values that work with RIPEMD160. You just need to test more hashes, since the extra hash digits mean that the probability of a "collision" will be approximately one in 14.6 billion. Quite a lot, but still tractable (in fact, I found a solution to this challenge in about 15 minutes, but I'm not posting it here).
Your code, on the other hand, will take much, much longer to find a solution. First of all, there is absolutely no point in generating random inputs. Sequential values will work just as well, and will be much faster to generate.
If you use sequential input values, then you also won't need to worry about repeating the same hash calculations. Your code uses a list structure to store previously hashed values. This is a terrible idea. Searching for an item in a list is an O(n) operation, so once your code has (unsuccessfully) tested a billion inputs, it will have to compare every new input against each of these billion inputs at each iteration, causing your code to grind to a complete standstill. Your code would actually run a lot faster if you didn't bother checking for duplicates. When you have time, I suggest you learn when to use lists, dicts and sets in Python.
Another problem is that your code only tests 10-digit numbers, which means it can only test a maximum of 10 billion possible inputs. Based on the numbers given above, are you sure this is a sensible limit?
Finally, your code is printing every single input string before you calculate its hash. Before your program outputs a solution, you can expect it to print out somewhere in the order of a billion screenfuls of incorrect guesses. Is there any point in doing this? No.
Here's the code I used to find the MD5 collision I mentioned earlier. You can easily adapt it to work with RIPEMD160, and you can convert it to Python if you like (although the PHP code is much simpler):
$n = 0;
while (1) {
$s = "0e$n";
$h = md5($s);
if ($s == $h) break;
$n++;
}
echo "$s : $h\n";
Note: Use PHP's hash_equals() function and strict comparison operators to avoid this sort of vulnerability in your own code.

Is possible to find a string that satisfy this condition?

$myStr = $_GET['myStr'];
if ($myStr == md5($myStr)) echo "ok\n";
I know there is a type jugglying in the code, but in my tests I couldn't find an input that satisfies the condition.
No, you cannot find that myStr value as it would come down to finding a (first degree) pre-image for MD5. Although MD5 has been broken for collision resistance, you should not be able to find a pre-image. More information here.
I'm presuming there that your code amounts to finding y = md5(y). y = md5(x) is a more general assumption and it is described in the Wikipedia article linked to above that it is impossible to find such H(x), even for MD5.
That doesn't mean that you should use MD5. Please use SHA-256, SHA-512 or indeed one of the SHA-3 functions. Even if MD5 hasn't been broken that far, it has been broken enough not to be used anymore; "Attacks always get better; they never get worse."
Let's start from the beginning. I will provide an example so i may help you maybe understand better.
In the first line you have $myStr = $_GET['myStr'];
I will just assume you will get this variable from your url like this :
http://localhost/md5Project.php?myStr=test
This will give your variable $myStr the value "test".
Moving forward in your if statement you have:
if ($myStr == md5($myStr)
this will never be true because $myStr value is "test" and md5($myStr) value is 098f6bcd4621d373cade4e832627b4f6 so basically you compare 2 strings with values "test" and "098f6bcd4621d373cade4e832627b4f6problem" which will always lead to false.

Explanation about constant-time algorithm and string comparision

I've a problem to understand two different ways of string comparison. Given is the following function which compares two strings.
This function is used in the Symfony-Framework security component to compare passwords in the user-login process.
/**
* Compares two strings.
*
* This method implements a constant-time algorithm to compare strings.
*
* #param string $knownString The string of known length to compare against
* #param string $userInput The string that the user can control
*
* #return Boolean true if the two strings are the same, false otherwise
*/
function equals($knownString, $userInput)
{
// Prevent issues if string length is 0
$knownString .= chr(0);
$userInput .= chr(0);
$knownLen = strlen($knownString);
$userLen = strlen($userInput);
$result = $knownLen - $userLen;
// Note that we ALWAYS iterate over the user-supplied length
// This is to prevent leaking length information
for ($i = 0; $i < $userLen; $i++) {
// Using % here is a trick to prevent notices
// It's safe, since if the lengths are different
// $result is already non-0
$result |= (ord($knownString[$i % $knownLen]) ^ ord($userInput[$i]));
}
// They are only identical strings if $result is exactly 0...
return 0 === $result;
}
origin: origin snippet
I've problem to understand the difference between the equals() function and a simple comparison ===. I wrote a simple working example to explain my problem.
Given strings:
$password1 = 'Uif4yQZUqmCWRbWFQtdizZ9/qwPDyVHSLiR19gc6oO7QjAK6PlT/rrylpJDkZaEUOSI5c85xNEVA6JnuBrhWJw==';
$password2 = 'Uif4yQZUqmCWRbWFQtdizZ9/qwPDyVHSLiR19gc6oO7QjAK6PlT/rrylpJDkZaEUOSI5c85xNEVA6JnuBrhWJw==';
$password3 = 'iV3pT5/JpPhIXKmzTe3EOxSfZSukpYK0UC55aKUQgVaCgPXYN2SQ5FMUK/hxuj6qZoyhihz2p+M2M65Oblg1jg==';
Example 1 (act as expected)
echo $password1 === $password2 ? 'True' : 'False'; // Output: True
echo equals($password1, $password2) ? 'True' : 'False'; // Output: True
Example 2 (act as expected)
echo $password1 === $password3 ? 'True' : 'False'; // Output: False
echo equals($password1, $password3) ? 'True' : 'False'; // Output: False
I read about the Karp Rabin Algorithm but I'm not sure if the equals() function represent
the Karp Rabin Algorithm, and in general I didn't understand the Wikipedia article.
On the other hand I read that the equals() function will prevent brute force attacks is that right? Can someone explain what the advantage for equals() is?
Or can someone give me an example where === will fail and equals() does the correct work, so I can understand the advantage?
And what does constant-time Algorithm mean? I think constant-time has nothing to do with the real time, or if I'm wrong?
This function is just a normal string comparison function. It is not Rabin Karp. It is NOT constant time, it's linear time, regardless of what the comment says. It also does not prevent brute force attacks.
How it works:
if the correct and user-provided passwords are of different length, make $result != 0
iterate over the user-provided password, xor each of its characters with the corresponding character of the correct password (if the correct password is shorter, keep going through it in a circle), and bitwise or each result with $result.
Since only bitwise or is used, if any of the characters are different, $result will be != 0. Step 1 is needed because otherwise, user input "abca" would be accepted if the real password was "abc".
Why such string comparison functions are sometimes used
Let's assume we compare strings the usual way, and the correct password is "bac". Let's also assume I can precisely measure how long it takes for the password check to complete.
I (the user) try a, b, c... They don't work.
Then, I try aa. The algorithm compares the first 2 letters - b vs a, sees it's wrong, and returns false.
I now try with bb. The algorithm compares b vs b, they match, so it goes on to letter #2, compares a vs b, sees it's wrong, returns false. Now, since I am able to time the execution of the algorithm precisely, I know the password starts with "b", because the second pass took more time than the first one - I know the first letter matched.
So I try ba, bb, bc... They fail.
Now I check baa, bbb, see baa runs slower so second letter is a. This way, letter by letter, I can determine the password in an O(cN) number of attempts instead of O(c^N) that brute force would take.
It usually isn't as much of a concern as this explanation might make it sound, because it is unlikely an attacker will time a string comparison to such a degree of accuracy. But sometimes it can be.

Why PHP casts two numerical strings to numbers before [loosely] comparing them?

I browsed through several similar questions, but they all only state the fact:
If ... comparison involves numerical strings, then each string is converted to a number and the comparison performed numerically.
Okay, I got it. It explains what is going on when '00001' == '1' returns TRUE.
The question is: Why PHP does so?
What is the reason for probing strings for being numeric, and then casting if so? Why can't we just compare two strings already?
I can fairly understand what casting is required if two operands has different types. But why it does "usual math" when both are strings?
You can compare two strings.
"00001" === "1" // false
Remember == means equivalent === means equal
My only guess as to why this is the case is because in the beginning PHP strived to be a type-less language before they went the route of becoming a loosely typed language.
PHP was originally string processor that allowed for a little scripting inside of it. Since all inputs on the web are textual in nature it had to work hard given textual input to behave sanely when it came to dealing with numbers.
I could be wrong about this but I don't believe the ability to explicitly cast data types entered the stage until PHP 4 with the introduction of the *val functions (like intval) etc. and I think the casting notation, like (int) came around after that.
For the non comparison operations it was pretty easy because they all have a type associated with them (+ - / * all deal with numbers whereas . deals with strings) so a clear path to how things should be cast in those cases is apparent.
But with equality or equivalence checks between variables then the only way to do that was to treat everything that looked like a number as a number because at the time the only way it could be gotten would be by externally would be as a string and there was no way to make it otherwise.
Because, PHP produce a product for End-Users, not for Application Developers.
So when you produce such product like below:
if (isset($_POST['submit'])){
if ($_POST['myinput'] == "1") echo 'Yes'; //or == 1
else echo 'NO';
}
?>
<form name="myform" method="POST" action="">
<input name="myinput">
<input name="submit" type="submit">
</form>
If the user enter 1 or 0001, what do you expect to print in both case? Yes or NO?
the answer is clear. we expect to see Yes. so this is why PHP does so
If for any rare reason, we need to definitely compare them, then we should change == to ===
Ah, finally I got it. It was quite stupid of me.
Comparison involves not only "is equal" but also "less than" and "greater than". And for the latter two it is obviously critical to cast numerical operands before comparison, because numbers often being represented in PHP as strings, and 11 have to be greater than 9 even if both stored in strings.
So, as compare_function() does all the comparisons at once, returns either 1, 0, -1 to tell if first operand is bigger, equal or less than second respectively - well, it's fairly explains, why operands being cast.

PHP - usage of is_numeric() necessary, or can use comparison signs work for all positive numeric cases?

It seems that simple comparison signs >,>= and their reverse components can evaluate if a certain variable is a number or not. Example $whatami='beast'; ($whatami<0)?echo 'NaN':echo 'is numeric!';
Are there cases where is_numeric() usage is necessary for positive values (number >0)? It seems that using comparison signs above would determine if the variable is numeric..
As I have been finding out, a lot of these helper functions are really necessary because PHP isn't strongly typed. I posted a similar question (although not that similar) about isset earlier this week. One thing to note is that PHP will change your string to its integer value for comparisons during some instances (when there are mixed types). This can't be overlooked. I think this is a strong case for is_numeric
from PHP Manual
If you compare a number with a string
or the comparison involves numerical
strings, then each string is converted
to a number and the comparison
performed numerically. These rules
also apply to the switch statement.
The type conversion does not take
place when the comparison is === or
!== as this involves comparing the
type as well as the value.
Another thing to think about is that "what is 0" in PHP. It means a lot. It's not always numeric. It may be a numeric string, boolean false, integer, etc... This is why those helper functions exist.
To add to my answer:
change your example:
$whatami='beast';
($whatami<5) ? echo 'less than 5' : echo 'more than 5';
PHP would change 'beast' to its integer equivalent and then do the comparison. This would give unintended results. If you really wanted something similar, you'd have to wrap it in another conditional:
$whatami='beauty';
if(is_numeric($whatami){
($whatami<5) ? echo 'less than 5' : echo 'more than 5';
} else {
exit('what, am I not pretty enough for a beast?');
}
Then you would get your intended result (as weird as it may be).
There is a big difference between "can evaluate if a certain variable is a number or not" and "evaluate if a certain variable is a positive number". Using the comparison signs require you to test it twice (Both > & <= or >= & <) and may not be immediately obvious. is_numeric means you only need a single test and makes it quite obvious what you are doing.
Also, a string will evaluate as 0, meaning it throws your idea out. Stick with the proper commands :)
As per comment: Well, in this case, you are asking for comparing is_numeric against a test for positive numbers, excluding 0. This is not the intent for is_numeric, so naturally it may not be necessary. If you do a mathematical check that involves 0 as the answer or as part of the range, you will need is_numeric, otherwise you won't need it. The first part of your question asks a different question, so:
It seems that simple comparison signs >,>= and their reverse components can evaluate if a certain variable is a number or not - Incorrect
Are there cases where is_numeric() usage is necessary for positive values (number >0)? - No
It seems that using comparison signs above would determine if the variable is numeric - No. They can determine if a variable is either a non-zero number or unknown, not numeric.
Comparison will depend on the type of data on the left side of the operator.
The important thing to remember is that PHP is not a strongly typed language. If you want to compare a number and ensure it is a number, then yes, is_numeric() would be a good check. For example,
echo (is_numeric($whatami) && $whatami < 0) ? 'number greater than zero' : 'NaN or negative';
However, this shouldn't be generalized. If you can comment more on what you are wanting to do, you may find a more detailed answer.
Yes, there are cases.
For instance:
var_dump("5aa" > 4); //bool(true)
var_dump("5aa" > 6); //bool(false)
As you can see, the conversion of "5aa" to int(5). Let's see what is_numeric gives:
var_dump(is_numeric("5aa")); //bool(false)
So, is_numeric is more strict. Whether it's necessary depends on your application.
Notice that are cases where a numeric string and a number are not exactly the same thing:
var_dump("255" & "2"); //string(1) "2"
var_dump(255 & 2); //int(2)
See bitwise operations:
Be aware of data type conversions. If both the left-hand and right-hand parameters are strings, the bitwise operator will operate on the characters' ASCII values.

Categories