Thursday, January 1, 2015

31c3ctf - Crypto - sso

In a world, where everybody and their mom rolls out their own crypto implemented PHP, Joe plays it safe with Standard Crypto.
http://188.40.18.87:5144/


Here's what each file returns initially.





admin.php and info.php need a token, and register.php needs a user. I presume we can register a user by passing register.php a user variable, and hope it takes get variables.



This seems to be the case, so we add a password as well.



We now have a token for the other pages.

Note: Passing credentials via the GET method such as this is a security design flaw as information can be bookmarked in the user's browser, or stored in cache of the brower or third-party proxy. Even in this screenshot you can see my high-entropy password. This flaw wont be of use to us for the challenge, but take note of it when coding your own authentication page.



Alright, I'm going to guess we need to register a user admin.



Except admin is reserved, so we'll have to trick it some how.


(Since the url is too long for the address bar, I'll include them so the reader can see better what I'm doing)

http://188.40.18.87:5144/info.php?token=69222e97316b9dd8f7d408b55a4c1af983ed2153dd3bab2057641b60a7cbf93ea37d1b9817ff4231916970bb7351ca3864d1368d170f68d46689e8fc9ede9d44ed606916be42289aa12725d2aab33248e9ea456df8c0e70b541f7abde18099a5f46857830c2d59a259fbeb122793ef13c0d23f528b3c79db881d310dbd353a076254

Doesn't tell us a lot we don't already know. Perhaps though this information is stored in the hash itself.


Doesn't appear to be directly related, but there's one more test we can do.


http://188.40.18.87:5144/info.php?token=69222e97316b9dd8f7d408b55a4c1af983ed2153dd3bab2057641b60a7cbf93ea37d1b9817ff4231916970bb7351ca3864d1368d170f68d46689e8fc9ede9d44ed606916be42289aa12725d2aab33248e9ea456df8c0e70b541f7abde18099a5f46857830c2d59a259fbeb122793ef13c0d23f528b3c79db881d310dbd353a076255

Basic cryptoanalysis. If we change the value of the ciphertext, do we get a different plaintext? Notice that by changing the final hex of the token from 54 to 55 how the end of the info string changes from } to I.

There are two reasons why I attack the tail end of a suspected ciphertext. 

First, we should expect to get a different result with a different or malformed token, so changing the beginning and getting completely different result wouldn't indicate the presence of crypto. This way, as most of the plain text stayed the same except the end, its clear that the token contains this information in an encoded way.

The second reason becomes apparent in the next screenshot.


http://188.40.18.87:5144/info.php?token=69222e97316b9dd8f7d408b55a4c1af983ed2153dd3bab2057641b60a7cbf93ea37d1b9817ff4231916970bb7351ca3864d1368d170f68d46689e8fc9ede9d44ed606916be42289aa12725d2aab33248e9ea456df8c0e70b541f7abde18099a5f46857830c2d59a259fbeb122793ef13c0d23f528b3c79db881d310dbd353a076354

By modifying the second to last hex (changing 62 to 63), and keeping the last hex (54) the same as the original (which produced a final }), we find evidence that preceding values affect later values. An artifact of a stream cipher mode

Another design flaw, storing credentials in a token. Instead, after authentication, assign the user a non-deterministic random sessionid that is tracked in a database. This way if the sessionid is leaked, sensitive credentials cannot be decrypted from it.

Since this isn't a non-deterministic random sessionid, I'm going to assume that there's a third design flaw, namely that the admin.php page trusts the information provided in the token. All we have to do is break the crypto and change the user from bob to admin....or do we?

I've broken crypto like this before, but its a pain in the but, and...I'm lazy. So I'm going to make another cryptoanalysis check by making another user.


Here's a newly registered user called john. Notice that the token of john begins similarily with the token of bob. Namely, they both start with 69222e97316b9dd8f7. This is a crypto design flaw. Whenever you're using a cipher mode, you need a non-deterministic random initialization vector, that isn't additionally sent along with the ciphertext (if has low entropy, and any stream cipher initialization vector will by definition have low entropy). At this point we could easily break the cipher, but again I'm lazy, so I make yet another user.


Say hello to my little friend admim. There I figured out the ciphertext of the first 4 letters I need, and if the admin.php has another design flaw, I will only have to bruteforce 3 characters.

I'm going to assume now that the author is very lax and only checks the user name, not even comparing password hashes against a stored has like some people (you know who you are), allowing me to truncate the token to look like...



http://188.40.18.87:5144/info.php?token=69222e97316b9dd8f7d74ae66e18

Now we change the last hex value (18), until info gives us a n.



http://188.40.18.87:5144/info.php?token=69222e97316b9dd8f7d74ae66e1b

In 3 tries (very lucky by incrementing 18) we get the next letter. Now we need to close the token with a " and } (json formating).



http://188.40.18.87:5144/info.php?token=69222e97316b9dd8f7d74ae66e1bc3


This took a lot longer, almost making me question why I don't simply crack the cipher itself, and should I needed to have to brute force the password hash, cracking the cipher would have been a better strategy.


http://188.40.18.87:5144/info.php?token=69222e97316b9dd8f7d74ae66e1bc30a

There, we have (relatively quickly) manufactured an admin token.


http://188.40.18.87:5144/admin.php?token=69222e97316b9dd8f7d74ae66e1bc30a

And now the flag.

Note: if it did compare password hashes, I'd have to bruteforce and correctly encode a hash, which would really be a headache, but still possible. At which point, the author would want to include lock-out mechanism or throttle the connection based on the number of bad attempts (or both!).

No comments:

Post a Comment