I was reviewing some PHP code and stumbled across this:
if(($this->int & 0b111111111) === 0){ //blah
I was very puzzled at where 0b111111111 came from. What is this and how was it generated? I var_dumped it and it turned out to be 511, but why was this used instead of the number 511? Where did it come from?
It is an integer using binary notation, and equals to 511 in decimal notation.
The coder is using it as mask for a binary AND (the &).
The test equals 0 only if $this->int == 0, as the binary AND operator compares each bit of the binary value, and returns 1 at the corresponding position where both the left and right numbers have a 1 at that position.
I have no special idea of the use here though: depending on the context, it could be for consistency. Or it's just "clever code" for writing
if($this->int === 0){ // Dumber blah
Related
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.
So I'm doing some CTF and one of the challenge is about php type juggling. The code looks something like this
if($_GET['var1'] == hash('md4', $_GET['var1']))
{
//print flag
}
So I was 80% sure that I need to pass in an integer so it'll be true but all I can manipulate is the url. I tried using python request to do it but still failed.
import requests
url = 'http://example.com'
params = dict(var1=0)
r = requests.get(url=url, params=params)
Is there any special case where php will treat the variable pass into GET array as int? Or there is other way to do so?
The answer to this CTF can be found here https://medium.com/#Asm0d3us/part-1-php-tricks-in-web-ctf-challenges-e1981475b3e4
The general idea is to find a value which starts with 0e and its hash also starts with 0e. The page gives an example of 0e001233333333333334557778889 which produces a hash of 0e434041524824285414215559233446
This notation is called scientific notation. To understand why this kind of type juggling works you need to understand what both numbers are. If we convert from scientific notation to decimal it becomes obvious.
0e001233333333333334557778889 = 0×101233333333333334557778889
0e434041524824285414215559233446 = 0×10434041524824285414215559233446
From primary school maths we know that anything multiplied by 0 is 0, which means both numbers are 0.
All input provided in the $_GET or $_POST superglobals is of type string.
Thanks to PHP's type juggling system both strings are treated as floating point numbers written in scientific notation and when cast to float they both equal 0.
Today I just made an interesting discovery while testing what happens calculating bitwisely in php like INF ^ 0 (^ => Bitwise Operator for Exclusive OR (XOR)) what gave me int(-9223372036854775808) => greatest possible negative value in a 64-Bit system.
But then I was asking myself: "Why is the result going negative in XOR when the "positive infinit" means 9223372036854775807 (63 Bits on 1 with a leading 0) and 0 (64 Bits on 0 => 0 xor 0 = 0) What is PHP's infinit value though and what is the calculation behind it? And why do I get a (correct?) negative value when I use "negative infinit"(A leading 1 against a leading 0 on 0 => 1 xor 0 = 1?".
Another interesting point is that this just happens on PHP Version 5.5.9-1, and not e.g. on 5.3.x. and 5.6.x (where i've tested it)! Maybe someone has an idea what happens there? Tested it on three versions but just mine (5.5.9-1) gives those results:
Just to let you guys know, it's just an abstract playaround i've done for fun but I find it's interesting. Maybe someone can help here or explain me a wrong thought I have? Just tell me if someone needs more informations about anything!
EDIT: Accordingly to jbafford it would be great to get a complete answere, so i'll just quote him: why does 5.5 and 5.6 result in PHP_INT_MIN, and everything else return 0?
First off, ^ itself isn't what's special here. If you XOR anything with zero, or OR anything with zero, you just get back the original answer. What you're seeing here is not part of the operation itself, but rather what happens before the operation: the bitwise operators take integers, so PHP converts the float to an integer. It's in the float-to-integer conversion that the weird behaviour appears, and it's not exclusive to the bitwise operators. It also happens for (int), for example.
Why does it produce these weird results? Simply because that's what the C code PHP is written in produces when converting a float to an integer. In the C standard, C's behaviour for float-to-integer conversions is undefined for the special values of INF, -INF and NAN (or, more accurately, for "integral parts" an integer can't represent: §6.3.1.4). This undefined behaviour means the compiler is free to do whatever it wants. It just so happens in this case that the code it generates produces the minimum integer value here, but there's no guarantee that will always happen, and it's not consistent across platforms or compilers.1 Why did the behaviour change between 5.4 and 5.5? Because PHP's code for converting floats to integers changed to always perform a modulo conversion. This fixed the undefined behaviour for very large floating-point numbers,2 but it still didn't check for special values, so for that case it still produced undefined behaviour, just slightly different this time.
In PHP 7, I decided to clean up this part of PHP's behaviour with the Integer Semantics RFC, which makes PHP check for the special values (INF, -INF and NAN) and convert them consistently: they always convert to integer 0. There's no longer undefined behaviour at work here.
1 For example, a test program I wrote in C to try to convert Infinity to an integer (specifically a C long) has different results on 32-bit and 64-bit builds. The 64-bit build always produces -9223372036854775808, the minimum integer value, while the 32-bit build always produces 0. This behaviour is the same for GCC and clang, so I guess they're both producing very similar machine code.
2 If you tried to convert a float to an integer, and that float's value was too big to fit in an integer (e.g. PHP_INT_MAX * 2, or PHP_INT_MIN * 2), the result was undefined. PHP 5.5 makes the result consistent, though unintuitive (it acts if the float was converted to a very large integer, and the most significant bits were discarded).
Your float(INF) gets implicitly casted to an Integer.
and XOR with 0 does not change the first parameter. So basically this is just a cast from float to int which is undefined for values which are not in the integer range. (for all other values it will be truncated towards zero)
https://3v4l.org/52bA5
I stumbled upon a very strange bit of PHP code. Could someone explain why this is happening? *****BONUS POINTS***** if you can tell my why this is useful.
<?php
if(0=='a'){
print ord(0)." should NEVER equal ".ord('a')."<br>";
}
if(false==0){
print "false==0<br>";
}
if('a'==false){
print "a==false<br>";
}
?>
And the resulting output:
48 should NEVER equal 97
false==0
In PHP, 'a' is not the ASCII character a, but the string a. In a numeric context, it is equal to 0. For instance intval('a') results in a value of 0.
This is useful because PHP is primarily used for processing text, and one might want to try the test (123 == '123'), which is true. And given that a number in single (or double) quotation marks is treated as the number, it doesn't make sense for a string with no numeric value to be treated as anything other than 0.
Oh yeah, one more thing. 'a' in a boolean context is true, not false. I believe this makes some types of text processing more natural, but I honestly can't think of an example at this late hour.
Well, there's always the PHP type cheat sheet for that!
This is a basic principle of weakly/dynamically typed languages called type juggling. Types will be cast to other types in certain circumstances. When you compare a string to a number, the string will be cast into a number. When comparing anything to a boolean, that value will be cast to a boolean.
There are rules for every type as to how it will be cast into another type or how it compares to other types. 'a' happens to be converted to 0 when cast to a number (the only logical choice, really). To avoid this type casting, test not with the equality operator ==, but with the identity operator ===.
As James pointed out, this is useful since PHP deals a lot with strings that are really numbers. For example, HTML forms only submit strings, even if the value is a number. It also allows for some really terse code, like:
$result = someOperation();
if (!$result) {
// $result may be null, false, 0, '' or array(),
// all of which we're not interested in
error();
}
It also means you have to be really careful about what to check for in which circumstances though, since a value might unexpectedly cast into something else. And admittedly, 'a' == 0 in itself is really a pitfall of type juggling rather than helpful. It's one of the situations where you have to be careful and test like if (is_numeric($var) && $var == 0).
ord() takes characters, so PHP turns 0 into '0'. And 0 is equal to false, even though it is not identical (===).
Check out the PHP type comparison tables from the manual. It's a really handy thing to have close at hand until you've internalised it and has been invaluable to my understanding of exactly what will evaluate to true and when.
Others have already answered the core of the question, but I think it's important to state that in PHP, the only non-empty string that does not evaluate to "true" with the == operator is "0" as PHP treats any string containing only numbers as an integer or float.
The rationale for this is that PHP is fairly loosely typed and tries to allow integers, strings, floats and boolean values to be interchangeable. A real-world and extremely common example of this is if you're using the mysql or PDO functions, strings are returned for everything, even if the underlying column is an integer.
Consider the following sql:
CREATE TABLE `test`.`pants` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`some_other_int` INT NOT NULL
) ENGINE = InnoDB;
INSERT INTO `test`.`pants` (`id`, `some_other_int`)
VALUES ('1', '1'), ('2', '0');
And the following code:
<?php
$c = mysql_connect('127.0.0.1', 'user', 'password');
mysql_select_db('test', $c);
$r = mysql_query('SELECT * FROM pants', $c);
while ($row = mysql_fetch_assoc($r)) {
var_dump($row);
foreach($row as $k=>$v) {
if (!is_string($v))
echo "field {$v} was not a string!\n";
}
}
The "field x was not a string!" message is never printed, even though every column in the database is an integer. Suppose you want to actually use the second row in that table.
<?php
$id = 2;
$r = mysql_query(sprintf('SELECT * FROM pants WHERE id=%s', mysql_real_esacpe_string($id)), $c);
$row = mysql_fetch_assoc($r);
// this is the important bit
if (0 == $row['some_other_int']) {
echo "It was zero!";
}
If the string "0" was not treated as the integer 0 for the comparison, the above code would never print "It was zero!". The programmer would be required to take responsibility for juggling the type of the value which comes out of the database. This is not desirable for a loosely typed language.
Strict equality including type is tested using the "Is really, truly, honest to god equal to" operator, which is represented by the symbol "===".
I don't see how ('a'==0) is helpful
$var = '123abc';
if (123 == $var)
{
echo 'Whoda thunk it?';
}
It comes down to PHP's implicit conversion rules.
I'm failing at thinking of a practical example, but that's the basic reason why you're seeing that behavior.
Expansion:
In your example, 'a' is converted to 0 (zero), for the comparison. Imagine that for the purpose of the comparison, it's equivalent to '0a'. (That's the numeral zero, not the letter 'o.')
Further expansion:
I thought there was a good example use case for this in the manual, but I failed to find it. What I did come across should help shed some light on this "illogical" situation.
PHP is first and foremost a Web
language, not a general-purpose
scripting language. Since the Web is
not typed and everything is a string,
I had to do things slightly
differently early on to make PHP do
what people expected. Specifically,
"123"==123 needs to be true in order
to not have to type cast every single
numeric user input.
http://bugs.php.net/bug.php?id=48012
That doesn't exactly answer the question, but it points in the general direction.
PHP is a loosely typed language, and allows you to compare values of different types without throwing errors, which makes it very easy to use but as you have found can cause some weird but logical outputs.
Your first example:
if(0=='a'){
print ord(0)." should NEVER equal ".ord('a')."<br>";
}
When two different types of values are compared, one value is first turned into the same type as another via a cast and then compared. In the example of Int and String the string is converted into Int. When PHP turns a letter into a string it takes all the first numeric characters and then chops of the rest: i.e '123123afraa' becomes 123123, '9a9' becomes 9. If the string does not start with numerals it is given the value of 0.
Therefor your example is really: 0===(string)'a' which is really 0===0 since 'a' does not start with a numeric. I think you were expecting PHP to return the value of 'a' in ASCII which it does not! This is really useful to sanitise strings, php rarely needs to deal with ascii values it is too high level for that. (Its for making websites!)
When a string is compared to a boolean a value of '' or '0' are false, all other values are true. This is useful so you can check if a value is 'empty':
url http://domain.com/?foo=
if ($_GET['foo']))
{
// do something
}
When an integer is compared to a boolean the values of 0 is false other values are true, this is pretty standard.
So, all in all you need to understand what happens when different types of variables are compared with the == operator. Also it is probably wise to realise that == is almost never what you want and using === (which will not typecast your values) is ALOT safer.
The code seems to emanate from a unit test for the purpose of catching failures, hence the seemingly weird comparisons. In the same light, it may be preparatory to the main unit test to confirm that the == operator is working properly - as it should.
I am having trouble with a complex script which sometimes (About 2 or 3 times while calulating about 90'000 values), generates a '-0' and writes it into the database. I suspect it's a string (The values which are calulated can result in integers, floats or strings.)*
Is there any PHP calculation which might result in a '-0'?
* = Oh, how I miss strong typing sometimes...
Rounding a negative number toward positive infinity, as ceil() does, can produce -0.
echo ceil(-.7);
// -0
The same result comes with, e.g., round(-.2).
Both of these will resolve to true:
(-0 == 0)
(ceil(-.7) == 0)
While these will resolve to true and false, respectively:
(-0 === 0)
(ceil(-.7) === 0)
Edit: An interesting (and implemented) rfc can be read here.
As Gurdas says, you can have your strong typing in the database. That aside, I don't know the answer to your question but I know how would I approach the problem.
The problem, as I understand it, is that you don't know in which cases you get the '-0', which is a valid floating point representation of 0, by the way. So you have to find in which cases you are getting that. I'd take one of two routes:
Use Xdebug, raise an error in the database insertion code when the value is '-0' to get a stack_trace with arguments (use xdebug.collect_params=1)
Create an empty string at the beginning of the script, populating it with all the operations and operands being done as they are, with the result and line. Afterwards, in the insertion clause add an if ($value == '-0') { print $string; }