-4

Salt: can be anything.
Work factor: can be anything.
All of the following generate the same hash!

$pad = base64_decode('/gB=');
$data = array(
    'LegitimatePayload',
    'LaterSwitchedToMaliciousPayload',
    'Abracadabra',
    'hatIsGoingOn',
    'CanBeAlmostAnything',
);

foreach($data as $str){
    echo crypt($pad.$str, '$2a$04$AnySaltHere')."<br>\n";
}


Output:

$2a$04$AnySaltHere$$$$$$$$$$.m/QKi19jyBmSuP2VMcVuFRw.weCNRBa
$2a$04$AnySaltHere$$$$$$$$$$.m/QKi19jyBmSuP2VMcVuFRw.weCNRBa
$2a$04$AnySaltHere$$$$$$$$$$.m/QKi19jyBmSuP2VMcVuFRw.weCNRBa
$2a$04$AnySaltHere$$$$$$$$$$.m/QKi19jyBmSuP2VMcVuFRw.weCNRBa
$2a$04$AnySaltHere$$$$$$$$$$.m/QKi19jyBmSuP2VMcVuFRw.weCNRBa
$2a$04$AnySaltHere$$$$$$$$$$.m/QKi19jyBmSuP2VMcVuFRw.weCNRBa

Edit:
Here is a string that has the same first two bytes but has a different hash:
base64_decode('/gBQyoK71jVY/J7QuBNJuFdxyf2eTBCs42chkx6ZvpJYszpzg===')
If php stopped at first NUL byte, then how do you explain this?

Roman
  • 503
  • 4
  • 15
  • Did you mean `base64_encode`? – maaudet Feb 19 '12 at 20:12
  • 1
    Either you have discovered a bug no one else ever has, or it's your code. Hint, it's the 2nd one. –  Feb 19 '12 at 20:16
  • I also don't see how inserting a malicious payload makes sense here. `crypt` isn't designed for integrity checks. It's designed for password hashing. So even if it were broken in the way you claim, the only consequence would be: Don't choose a password starting with these characters. – CodesInChaos Feb 19 '12 at 20:22
  • Stop voting down the question! This vulnerability is not present in jBcrypt. And I do have strings that start the same way but produce different hashes. – Roman Feb 19 '12 at 20:22
  • 3
    Some languages use binary safe strings. It is well known that most php functions are not binary safe, and break for strings containing `\0`. So unless you demonstrate that then problem isn't just php stopping at the first `\0` char, the downvotes are deserved, IMO. – CodesInChaos Feb 19 '12 at 20:25
  • 2
    I really don't know why this question got so many downvotes. +1. – Madara's Ghost Feb 20 '12 at 15:32

3 Answers3

10

All your strings have a prefix that will - when run through base64_decode - result in a 0xfe character and a 0x00 character with the extra - varying - characters after the 0x00. Since standard crypt will stop at a 0x00 character, all your crypt calls only encrypt the 0xfe character.

You can verify it by just calling

echo crypt("\376", '$2a$04$AnySaltHere')."<br>\n";

which will give the same result.

I'm assuming you used base64_decode by mistake meaning to actually call base64_encode.

Edit: As Roman points out, the string

"/gBQyoK71jVY/J7QuBNJuFdxyf2eTBCs42chkx6ZvpJYszpzg==="

will actually - despite the same prefix - crypt to something else entirely. This is due to that string actually being invalid base64 and base64_decode returning false. That results in the string crypt'ing to the same hash as the empty string does instead.

Joachim Isaksson
  • 176,943
  • 25
  • 281
  • 294
  • The crypt doesn't stop at NUL character, I have strings that start same as above but produce different hashes. – Roman Feb 19 '12 at 20:18
  • +1 this is the right answer. `\0` bytes are a PITA in PHP's link to the lowlevel functions, and even a security issue. The problem is that PHP keeps string content and length as separate things, while the typical libc call takes zero terminated strings. – mvds Feb 19 '12 at 20:24
  • 2
    Use `for ( $i=0;$i – mvds Feb 19 '12 at 20:25
  • 1
    I commented on the security issues of this some time ago on http://stackoverflow.com/questions/3115559/exploitable-php-functions/5442372#5442372 – mvds Feb 19 '12 at 20:28
  • Here is a string that starts the same way but has different hash: base64_decode('/gBQyoK71jVY/J7QuBNJuFdxyf2eTBCs42chkx6ZvpJYszpzg==='). How do you explain this? – Roman Feb 19 '12 at 20:30
  • 1
    @Roman That string - ending with 3 equals signs - actually is invalid base64 so base64_decode returns false. It crypts to the exact same hash as the empty string does, try `echo crypt("", '$2a$04$AnySaltHere')."
    \n";`
    – Joachim Isaksson Feb 19 '12 at 20:45
5

You're not providing any valid base64 encoded strings, so base64_decode will probably just return false for all of your test cases, and thus it will encrypt them all the same. Why are you using base64_decode anyway?

wimvds
  • 12,790
  • 2
  • 41
  • 42
  • To show that it works for binary data, I've edited the code for clarity. – Roman Feb 19 '12 at 20:16
  • 2
    You're not getting the point : you're providing strings that are *not* base64 encoded, and thus base64_decode will *always* return false. There is no vulnerability, it's just your code that doesn't make any sense. – wimvds Feb 19 '12 at 20:30
  • They do not return false I assure you. I have verified this by writing the output to file and viewing via hex editor. – Roman Feb 19 '12 at 20:33
0

You probably want base64_encode and not base64_decode. the reason it all returns the same is because the result is always false.

Madara's Ghost
  • 172,118
  • 50
  • 264
  • 308
  • Doesn't return false, I've checked. Returns different strings. Question edited for clarity. – Roman Feb 19 '12 at 20:15