aspnet_membership password decryption via PHP - php

I've spent better half of the day trying to figure out the problem I have, and I'm at a dead end it seems.
I have a ASP application(no access to actual code, just database), in which the user passwords are stored in aspnet_membership > Password column, it also has a salt.
I've also got a copy of the machine key file, which from what I understand contains the keys neede to decryot the password?
<machineKey validationKey="**validation key**" decryptionKey="**decryption key**" validation="SHA1" decryption="AES"/>
i've tried a bunch of different ways of doing this, with open ssl, with different libraries, etc. However I seem to lack knowledge when it comes to this. I'm currently trying to use https://github.com/phpseclib/phpseclib library to decrypt the password:
$cipher = new AES(); // could use AES::MODE_CBC
// keys are null-padded to the closest valid size
// longer than the longest key and it's truncated
//$cipher->setKeyLength(128);
$cipher->setKey(**decrypt key**);
// the IV defaults to all-NULLs if not explicitly defined
$cipher->setIV($salt);
echo $cipher->decrypt($password);
However any way i'm trying todo this, I get either random return or false. I've got a very limited amount of info about the version of AES running on the ASP application or any other encryption info. Any help would be appreciated!

Hi This MachineKey has nothing to do with Salt, the salt is generating by the code at run-time using the Password provided.
.NET framework using Rfc2898DeriveBytes for encryption
Something like this
using (Rfc2898DeriveBytes rfc2898DeriveByte = new Rfc2898DeriveBytes(password, 16, 1000))
{
salt = rfc2898DeriveByte.Salt;
bytes = rfc2898DeriveByte.GetBytes(32);
}

Related

Laravel Crypt value changing on each reload

I'm trying to use Laravel's Crypt functionality, to simply store a value in a database and grab it later on to use. However I noticed that I was unable to decrypt this value.
My application key is a random, 32 character string. My cipher is MCRYPT_RIJNDAEL_128.
From the PHP info, MCRYPT is installed, and RIJNDAEL_128 is supported.
To test, I do the following on a GET rou:
$t = "123456789";
var_dump(Crypt::encrypt($t));
See: http://laravel.io/bin/2e9Xr#
On each page refresh, the output is a different value, which is obviously incorrect - however I have no idea why.
I'm using an EasyPHP as my dev server. However one thing I have noticed is that the application requests are significantly slow on this environment as compared to the production, Apache web server.
This makes me wonder if there is some sort of environment refresh going on each time, potentially resetting the "resources" MCRYPT is using to encrypt, and thus is different each time.
Any clues?
That is normal behavior. Every Crypt::encrypt call should produce a different output for security reasons.
Crypt is incredibly inefficient for small strings. For example, Crypt::encrypt("Hello World") outputs something like the following: eyJpdiI6Imhnb2hRazVabUNZUnVRVzFBSEExVkE9PSIsInZhbHVlIjoiTHJ4c05zcjdJZkZwWU1vRVVRMEcwZE5nTUdjQnhyM2RKWTMzSW04b1cxYz0iLCJtYWMiOiIyZjRmNDc3NGEyNGQyOGJjZGQ4MWQxYWViYzI1MjNjZTU0MmY4YTIxYTEyNWVjNDVlZDc4ZWEzNzRmN2QwM2ZiIn0=
Immediately recognizable as a base 64 string. When decoded, it becomes {"iv":"hgohQk5ZmCYRuQW1AHA1VA==","value":"LrxsNsr7IfFpYMoEUQ0G0dNgMGcBxr3dJY33Im8oW1c=","mac":"2f4f4774a24d28bcdd81d1aebc2523ce542f8a21a125ec45ed78ea374f7d03fb"}
Using Crypt, you can encrypt and decrypt large plaintexts easily without worrying about the details. But if you want to store or transmit a lot of separately encrypted entities, then you might want to consider a different approach.
So why is it like this?
(Note: the directory structures are valid for Laravel 4.2).
For one, most secure block cipher modes of operation require an IV (initialization vector), which is a bunch of random bytes with length matching the block size. Using a different IV for every ciphertext is important for thwarting cryptanalysis and replay attacks. But let's look a bit at the actual Crypt code.
Starting with the config/app.php aliases array, we see 'Crypt' => 'Illuminate\Support\Facades\Crypt'
So we check the vendor/laravel/framework/src/Support/Facades directory, and we find Crypt.php which says the module accessor name is actually "encrypter". Checking the config/app.php providers array shows 'Illuminate\Encryption\EncryptionServiceProvider'.
vendor/laravel/framework/src/Illuminate/Encryption has several files of interest: Encrypter.php and EncryptionServiceProvider.php. The service provider binds the accessor with a function that creates, configures, and returns an instance of Encrypter.
In the Encrypter class, we find the encrypt method:
public function encrypt($value)
{
$iv = mcrypt_create_iv($this->getIvSize(), $this->getRandomizer());
$value = base64_encode($this->padAndMcrypt($value, $iv));
// Once we have the encrypted value we will go ahead base64_encode the input
// vector and create the MAC for the encrypted value so we can verify its
// authenticity. Then, we'll JSON encode the data in a "payload" array.
$mac = $this->hash($iv = base64_encode($iv), $value);
return base64_encode(json_encode(compact('iv', 'value', 'mac')));
}
And there you have it. Each time you call Crypt::encrypt, it generates a new IV, encrypts the value, creates a MAC of the IV and ciphertext, and then returns a base 64 encoded JSON string of an associative array of the IV, MAC, and ciphertext. Each IV will be different, which means every ciphertext and MAC will also be different--even for the same value. Really smart if all plaintexts are large, but pretty impractical for a lot of smaller plaintexts where MACs are unnecessary overhead.
tl;dr version:
16 bytes of randomness is generated for every encrypt call, and it cascades into the ciphertext and MAC, all of which is returned in a base 64 encoded JSON associative array. Thus, every Crypt::encrypt call produces different output.
That's how mcrypt works - http://mnshankar.wordpress.com/2014/03/29/laravel-hash-make-explained/
$test = 'test';
$crypted = Crypt::encrypt($test);
echo $crypted.'<br />'; // encrypted string
echo Crypt::decrypt($crypted); // "test"

md5 Decryption in PHP

I am developing a system for Online Hotel booking system which I did not start from scratch. The information of customers(bookers) in the system are encrypted using MD5 but unlike normal md5() php function the system is quite complicated as you can see here :
$psw = md5("vhdsxnjuobef");
$t_cred_num = md5_encrypt($t_cred_num, $psw, 16);
and for Decryption it goes like :
$psw = md5("vhdsxnjuobef");
$t_credit_num = md5_decrypt($t_cred_num, $psw, 16);
this code is not Working though on my Server and there is alot of Customer's information Encrypted.
Example of hash of t_cred_num variable =>
fdRucZHctr7vIX+U400xGHq53Qemze0YQH1sAUjvmaC1P+XaRadI9CaX0wrkDXu6
Any Ideas on how to Decrypt these hashes ? When I use md5_decrypt with the hashes nothing happens.
I think the md5_crypt and md5_encrypt are functions hand crafted by the previous developer. md5 isn't supposed to be decryptable. Hash's are supposed to be one way functions: http://en.wikipedia.org/wiki/Hash_function
So, you'll need to find the definition of those functions. A search for "function md5_" in the code files should find the place in the code where they are defined.
there's no way to decrypt an md5. there are two things you can do:
if you have other account or know the password of another account then copy the md5 characters of it and place it on yours.
search for an md5 of let's say "admin". Paste the md5 equivalent of the word "admin" on your account. You can also use other words.
After that, login to your account then change back your password

Decrypting the .ASPXAUTH Cookie WITH protection=validation

For quite sometime I've been trying to decipher the ASP .ASPXAUTH cookie and decrypt it using PHP. My reasons are huge and I need to do this, there is no alternative. In PHP so far I have successfully managed to read the data from this cookie, but I cannot seem to do it while it is encrypted. Anyway, here it goes...
First you need to alter your servers Web.config file (protection needs to be set to Validation):
<authentication mode="None">
<forms name=".ASPXAUTH" protection="Validation" cookieless="UseCookies" timeout="10080" enableCrossAppRedirects="true"/>
</authentication>
Then in a PHP script on the same domain, you can do the following to read the data, this is a very basic example, but is proof:
$authCookie = $_COOKIE['_ASPXAUTH'];
echo 'ASPXAUTH: '.$authCookie.'<br />'."\n";//This outputs your plaintext hex cookie
$packed = pack("H*",$authCookie);
$packed_exp = explode("\0",$packed);//This will separate your data using NULL
$random_bytes = array_shift($packed_exp);//This will shift off the random bytes
echo print_r($packed_exp,TRUE); //This will return your cookies data without the random bytes
This breaks down the cookie, or at least the unencrypted data:
Now that I know I can get the data, I removed the 'protection="validation"' string from my Web.config and I tried to decrypt it using PHP mcrypt. I have tried countless methods, but here is a promising example (which fails)...
define('ASP_DECRYPT_KEY','0BC95D748C57F6162519C165E0C5DEB69EA1145676F453AB93DA9645B067DFB8');//This is a decryption key found in my Machine.config file (please note this is forged for example)
$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC), MCRYPT_RAND);
$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, ASP_DECRYPT_KEY, $authCookie, MCRYPT_MODE_CBC, $iv);//$authCookie is the pack()'d cookie data
This however fails. I've tried variations of IV with all zeros # 16 bytes. I've tried different Rijndael sizes (128 vs 256). I've tried base64_decode()ing, nothing seems to work. I've found this stackoverflow post here and started using variations of the key/iv that are made using sha256, but that isn't really working either.
Anybody have a clue what I should do?
I don't know how encryption is made in .NET AuthCookies, but I can try to answer.
Assuming the encryption occurs in AES CBC-IV mode, with randomly generated IVs, you need to first find out where the IV is.
The code snippet you show cannot work, as you are generating a random IV (which will be incorrect). That being said, even if you get the IV wrong, in CBC mode you will only have the first 16 bytes of your decrypted ciphertext "garbled" and the rest will decrypt properly - you can use this as a test to know if you're doing the rest correctly. In practice when using random IVs, it's very likely that it's prepended to the ciphertext. To check if this correct, you can try to check if len(ciphertext) = len(plaintext) + 16. This would mean that most likely the first 16 bytes are your IV (and therefore it should be removed from the ciphertext before attempting to decrypt it).
Also on your code snippet, it seems you are using the key as an ascii-string, whereas it should be a byte array. Try:
define('ASP_DECRYPT_KEY',hex2bin('0BC95D748C57F6162519C165E0C5DEB69EA1145676F453AB93DA9645B067DFB8'));
Also, this seems to be a 32 byte key, so you need to use AES-256. I don't know how the authcookie looks like, but if it's base64 encoded, you also need to decode it first obviously.
Hope this helps!
Note: I don't recomment doing this for important production code, however - because there are many things that can go wrong if you try to implement even your own decryption routine as you are doing here. In particular, I would guess there should be a MAC tag somewhere that you have to check before attempting decryption, but there are many other things that can go wrong implementing your own crypto.
I understand this may not have been possible for the OP but for other people heading down this route here is a simple alternative.
Create a .net web service with a method like:
public FormsAuthenticationTicket DecryptFormsAuthCookie(string ticket)
{
return FormsAuthentication.Decrypt(ticket);
}
Pass cookie to web service from PHP:
$authCookie = $_COOKIE['.ASPXAUTH'];
$soapClient = new SoapClient("http://localhost/Service1.svc?wsdl");
$params= array(
"ticket" => $authCookie
);
$result = $soapClient->DecryptFormsAuthCookie($params);
I know what a pain is to decrypt in PHP something encrypted in .NET and vice versa.
I had to end up coding myself the Rijndael algorithm ( translated it from another language ).
Here is the link to the source code of the algorithm: http://pastebin.com/EnCJBLSY
At the end of the source code there is some usage example.
But on .NET, you should use zero padding when encrypting. Also test it with ECB mode, I'm not sure if CBC works.
Good luck and hope it helps
edit: the algorithm returns the hexadecimal string when encrypts, and also expects hexadecimal string when decrypting.

AES encryption in php and then decryption with Javascript (cryptojs)

I'm searching for a way to make a 2 way encryption of a simple text (5 to 6 numbers and/or characters). The catch is that i want to make the encryption in php and then decrypt it via Javascript. For php i've tested using mcrypt_encode and have gotten it to work, hence when i try to decrypt it with javascript (i'm using the Crypto-js library - http://code.google.com/p/crypto-js/ ) i get no results. Here is the php code i'm using:
$key = "oijhd981727783hy18274";
$text = "1233";
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_256, '', MCRYPT_MODE_CBC, '');
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_CBC,$iv);
echo base64_encode($crypttext);
and then here is the Javascript code i'm using:
var encrypted = CryptoJS.enc.Base64.parse("LiJU5oYHXRSNsrjMtCr5o2ev7yDFGZId85gh9MEXPeg=");
var key = 'oijhd981727783hy18274';
var decrypted = CryptoJS.AES.decrypt(encrypted, key);
document.write( decrypted.toString(CryptoJS.enc.Utf8) );
As i'm just testing, i copy/paste the output from the php straight into the JS and see if it would return any results, however that doesnt happen. As i'm new to the encryption/decryption part i might be missing something. Any suggestions will be greatly appreciated.
On a side note, as i read a lot of suggestions here about using other types of communication to transfer the data, that would not be possible in this case, as i need to pass this string to a third party software, which will bring it over on a secure area, where i have access to edit only the javascript, this is why i'm trying to encrypt the text in php and place it inside the website's source, from where the third party software will read it as it is encrypted and will transfer it to the secure section, where i will need to decrypt it back via Javascript (i dont have access to php there).
So, after some more digging i came to the following online encryptor/decryptor which led me to the gibberish-aes at GitHub repository.
Inside one of the comments on the first link i found that this JS library has a php equivalent, which seems to be working reasonably well and is fairly easy to deploy:
https://github.com/ivantcholakov/gibberish-aes-php
So thanks to Lars for the answer he provided, i would encourage him to open the repository, i'm sure he'll make someone's life a little bit easier :)
From the CryptoJS documentation:
For the key, when you pass a string, it's treated as a passphrase and used to derive an actual key and IV. Or you can pass a WordArray that represents the actual key. If you pass the actual key, you must also pass the actual IV.
So in your line
var decrypted = CryptoJS.AES.decrypt(encrypted, key);
"oijhd981727783hy18274" is treated as a passphrase to create a key and not as the actual key.
Passing an IV works like this:
var key = CryptoJS.enc.Hex.parse('000102030405060708090a0b0c0d0e0f');
var iv = CryptoJS.enc.Hex.parse('101112131415161718191a1b1c1d1e1f');
var encrypted = CryptoJS.AES.encrypt("Message", key, { iv: iv });
Some time ago I had the same problem. I finally got to use SlowAES from http://code.google.com/p/slowaes/ with some fixes and ported it to PHP.
Note: The official sources are broken, just as the official PHP port.
Let me know if you're interested. Then I'd open a new repository at GitHub where you can grab everything you need...

How do mobile authenticators work

I was wondering how mobile authenticators work (like Battle.net, rift, some banks have one, etc.), so I can make one for my own site (just for fun).
I understand the basics: authenticator has code related to the phone and code related to the website. Users enters the phone code on the website. Can then generate a token related (using the phone and website code).
I'm just wondering how the tokens are created. Is there a standard algorithm for this? How does the algorithm work? Any existing PHP libraries that can do something like this (as an example)?
Have a look at Google Authenticator. There are already iPhone, Android and Blackberry apps for that and it's an established protocol.
They have implemented it as an open-source PAM module which you may be able to use with the PECL PAM package.
There is a pure PHP version but I haven't used that so can't vouch for it.
The spec isn't that complex so you could probably implement it yourself, especially if you converted the C module. The specification linked there explains its working in full detail.
Edit: I guess to answer the original question, that's an RFC, so it's somewhat standardised, and it's a fully open specification and the tools to use it are fully open-source. The protocols are known as HOTP and TOTP. The former is HMAC based on a counter (so the nth password is used) whereas the latter is time-based (so the password cycles every 30 seconds).
Concerning the Blizzad Battle.Net authenticator, you can find an open source implementation in PHP : https://github.com/krtek4/php-bma
The implementation is used to provide a online authentication service for Battle.Net : https://authenticator.me
If you want to do something like it for your website, it's pretty simple. The only thing to share between the server and client part are the secret generated by the server. So when a client is requesting for a new secret, just store it and you will be able to compute the code at any moment to compare with what is sent to you.
I implemented this once. I use a 4 digit key with a subset of characters (notice that potentially confusing characters like 0oO and l1L are removed. I used 4 characters because the potential space of 4 digits from the characters set was larger than the 6 digits of an RSA key.
Anyway, I let the user log in with their username and password. If that is correct, generate a key and send it to the phone and save it in the session and show the user the next page, which requires the key be entered. The user gets the 4 digit key from their phone and enters it into the page. Then check what they entered against the session-saved key and there you go.
Some handy features to have: make the key expire after a few minutes, but long enough that text message delays don't make it impossible. Make it expire after a few bad tries. Give the users a link to resend the key or to send a new key.
//pick a random 4 digit string
$chars = "abcdefghjkrstwxyzABCDEFGHJKRSTWXYZ23456789";
$key = "";
for($i=0;$i<4;$i++){
//here, rand is used, but any generator could be used
//to choose the characters.
$key .= $chars[rand(0,strlen($chars)-1)];
}
//save it to the session
$_SESSION['test']['KEY'] = $key;
If it were me I'd go with generating a hash based on the previously used hash and a common nonce, the tricky bit would be keeping the two systems in sync. e.g.
<?php
class otp {
var $salt;
var $previous_hash;
var $user_id;
function __construct($user_id)
{
$this->user_id=$user_id;
list($this->$salt, $this->$previous_hash)
=unserialize(file_get_contents(BASE_PATH . $user_id));
}
function authenticate($submitted_otp)
{
if (!$this->salt) {
// user does not exist
return false;
}
$new_hash=$this->previous_hash;
// allow for the sequence to get out of sync by 5 steps....
for ($x=0; $x<5; $x++) {
$new_hash=md5($this->salt, $new_hash);
if ($new_hash===$submitted_otp) {
$this->update_token($new_hash);
return true;
}
}
// none of the next N iterations of the local password match
return false;
}
function create_user($user_id, $salt, $init_hash)
{
return file_put_contents(BASE_PATH . $user_id, array($salt, $init_hash));
}
function update_token($new_hash)
{
file_put_contents(BASE_PATH . $user_id, array($this->salt, $new_hash));
}
}
Of course, in practice you probably wouldn't want to use a whole 32 char md5 hash (just, say, the first 6 characters, and applying cleansing such as changing 'S' to '5' etc).

Categories