Defuse the bomb
Description
- Category: Cryptography
- Points: 250 / 500
- Goal: Be aware of the Merkle–Damgård weaskness, called Length Extension Attack.
Statement
Un grupo apodado MonkeyBananas nos hackeó. En algun lado que no sabemos donde, dejaron una "bomba". No sabemos que hace esa "bomba", pero creemos que una vez que "explote" puede llegar a borrar todos los datos que tenemos almacenados en nuestros servidores. Necesitamos que nos ayudes a evitar que esto pase, tenemos sólo 3 días.
Claramente, nos están poniendo a prueba.. nos dieron el código de un servidor que hace de "Interruptor". Si enviamos el request correcto a ese servidor vamos a poder desactivar la bomba, pero.. no parece tan facil ya que están usando algún tipo de algoritmo criptográfico para evitar que puedas cambiar los parámetros del request a gusto. No está el código completo del servidor que recibe dicho request, pero nos aseguraron que con lo que nos pasaron es suficiente.
De casualidad, uno de nuestros sistemas de defensa, pudo capturar el trafico de el request que hicieron ellos al interruptor para activar la alarma, espero que sirva de algo!
El flag es el SHA1 del TAG que deberías mandar para desactivar la bomba. Suerte!
===========================================================
A group called MonkeyBananas hacked us. They left a "bomb" somewhere, but we don't know wehere. Furthermore, we don't know what this "bomb" does, but we believe that once it "blows" its gonna delete all data stored in our servers. We need your help, we have only 3 days.
Clearly, they're challenging us, due to they gave us the source code of a server which works as a "switch". If we send the correct request to that server we will be able to defuse the bomb, but... it doesn't seems that easy because they're using some kind of crypto algorithm to avoid arbitrary tampering the parameters. The source code is not complete, but they ensure us that in that file we have all we need.
Fortunately, one of our defense systems, was able to capture the request that this guys sent to the "switch" when they activated the bomb, hope this is useful!
The flag is the SHA1 of the TAG value you should submit in order to defuse the bomb. Good luck!
File
These are the files that were provided by MonkeyBananas:
Server pseudocode
#!/usr/bin/env python3
import urllib
# NOOO! Alguien eliminó esto!
import %%%%%% as remolino
class Server:
def __init__(self):
...
self.generate_mac_key()
...
def generate_mac_key(self):
self.mac_key = b"Th1s1sS3cure4sH3ll" + os.urandom(14)
def check_tag(self, tag, params):
decoded_params = urllib.parse.unquote_to_bytes(params)
return tag == remolino.new(self.mac_key+decoded_params).hexdigest()
def parse_params(self, body):
return dict(param_value.split("=") for param_value in body.split("&"))
def serve_for_ever(self):
...
def doget(self, request):
...
def dopost(self, request):
...
ct = request.headers.get("Content-type")
if not form_urlencoded(ct.lower()):
return error()
tag = request.headers.get("Tag")
params = request.body()
if self.check_tag(tag, params):
params = parse_params(urllib.parse.unquote(params))
if params['action'] == 'activate':
activate_bomb()
elif params['action'] == 'defuse':
defuse_bomb()
else:
....
else:
return error()
...
...
server = Server()
server.serve_for_ever()
Request
POST /bombing HTTP/1.1
Host: www.dibombjasbinplanted.com
Content-Type: application/x-www-form-urlencoded
Tag: d293e656353386647bef31070414e85dee1b35d8e3a2237f330277b824679b9955c086247a148295ba9df68f763b31dded09475da0fd5cd5bfbed0a29562f06b
User-Agent: Mozilla/5.0 (Windows; U; Win 9x 4.90; SG; rv:1.9.2.4) Gecko/20101104 Netscape/9.1.0285
Connection: keep-alive
Accept: text/html,application/xhtml
bombing=true&bananas=missing&action=activate&monkeys=ontheway
Solution
We received two files an HTTP request and the source code of the "interruptor" to defuse the bomb.
The HTTP request shows how this hacker team did the activation of the bomb. They basically sent a POST request to the host where this interruptor was hosted. The most important parts of it are:
- The HTTP header Tag
- The body used.
The body has a parameter called action which seems to be the one that really activates the bomb.
Reading the pseudocode of the server, in particular the lines:
if self.check_tag(tag, params):
params = parse_params(urllib.parse.unquote(params))
if params['action'] == 'activate':
activate_bomb()
elif params['action'] == 'defuse':
defuse_bomb()
we can confirm, that the action=activate is the necessary parameter to activate the bomb. Therefore, if we manage to send action=defuse we would be able to defuse it!.
However our problem is the check function self.check_tag(tag, params). This function is taking as input, the tag header the parameters sent in the body and performs the following action:
def check_tag(self, tag, params):
decoded_params = urllib.parse.unquote_to_bytes(params)
return tag == remolino.new(self.mac_key+decoded_params).hexdigest()
First, it makes some url-decoding and then it compares the sent tag against the result of applying a function call remolino.new to the concatenation of a key and the sent (and decoded) parameters. Then it calculates the hexdigest of it, which indicates that this function is potentially related to some hashing algorithm.
The key appended to the params, is partially known:
self.mac_key = b"Th1s1sS3cure4sH3ll" + os.urandom(14)
but still there are 14 bytes (112 bits) that are random. So there's no chance we can bruteforce that.
remolino its a library but we cannot know which one, cause:
# NOOO! Alguien eliminó esto!
import %%%%%% as remolino
This is clearly the structure of a MAC (in fact the name self.mac_key it's a little spoiler).
This means that if we want to send a message to the 'interruptor' we should also send the tag header with the value of applying the hash function to the parameters together with the key (that we don't have). Seems to be almost impossible.
Let's try to guess which hashing algorithm they are using. From the example request, we can get the a valid tag:
d293e656353386647bef31070414e85dee1b35d8e3a2237f330277b824679b9955c086247a148295ba9df68f763b31dded09475da0fd5cd5bfbed0a29562f06b
This is the value of applying the hash function to "\<key>"+"bombing=true&bananas=missing&action=activate&monkeys=ontheway"
Let's how long this hash digest is:
len("d293e656353386647bef31070414e85dee1b35d8e3a2237f330277b824679b9955c086247a148295ba9df68f763b31dded09475da0fd5cd5bfbed0a29562f06b")
128
128 bytes as an output it's pretty long. I think that the most famous algorithm which it digest is of 128 bytes is sha-512.
If you have a little background of crypto you may've already realize what you need to use here. If you lack of it, still, you may understand that you need to create a valid tag, but you need the MAC key for that, which seems to be impossible to retrieve. Unless there is another kind of attack to create a valid tag.
The length extension attack is one of the most famous attacks known for hashes. It basically states that:
If in a structure like hash_function(secret+data) = H, you know:
- Length of secret
- data
- H
It is possible to create H2 which asserts that: hash_function(secret+data+data2) = H2 with data2 being some arbitrary data you want to append.
In other words, knowing the previous hash value, the data that was used to hash and only the length of the secret appended, you can append any arbitrary data and a valid hash without never knowing the actual value of the secret.
HOW?! I won't explain the full details of it, for that you can visit something like this link, or any other explaination of it. But in quick words this happens only in hashes that are based on some common techinique called Merkle–Damgård. It basically divides the input in blocks and it process each block separately (it's really not separately because it has dependences of previous blocks). This technique has an internal state which is updated each time a new block is processed. The resulting hash value is the internal state after processing the final block. The length extension attack leverages this and continues from the last "checkpoint" (which was the internal state of the final block) and adds more data to it.
The requirements of this attack seems to fit in our case, but we still have some issues:
- We don't know yet the actual hash function that is being used
- What data should we append ?
For 1. we can make a search in google to see what hashes are vulnerable to this attack (or are based in Merkle–Damgård, which is the same) and also have a 128 bytes of digest. The answer to this is:
- SHA512
- WHIRPOOL
Well at this moment we can continue with both algorithms doing the tests, but remember that parts?:
import %%%%%% as remolino
...
return tag == remolino.new(self.mac_key+decoded_params).hexdigest()
...
"remolino" in spanish means "whirpool", so most probably the actual hash used is whirpool.
For 2. we would want to send something like action=defuse, but we can't just send the data we want, but we have to append it.
However, let's pay attention to the way the server reads the params:
def parse_params(self, body):
return dict(param_value.split("=") for param_value in body.split("&"))
It basically creates a dictionary and updates it with each key value gathered from the body. This means that this way is vulnerable to HPP (Parameter Polution). Therefore we can add our parameter to the end part of the body and when the server process it first will assing the value activate to action but afterwards will overwrite this value with defuse and BINGO!
Fortunately, we don't have to do all the forging (padding, etc) of the data we want to append. There are some tools that already exist and will make our life easier like hash extender.
This tool will do all the job for us:
- -d holds the data that was hashed previously
- -s has the hash value of it (the valid tag)
- -a holds the data we want to append
- -f is the name of the algorithm we want to use
- -l is the length of the secret
./hash_extender -d "bombing=true&bananas=missing&action=activate&monkeys=ontheway" -s d293e656353386647bef31070414e85dee1b35d8e3a2237f330277b824679b9955c086247a148295ba9df68f763b31dded09475da0fd5cd5bfbed0a29562f06b -a "&action=defuse" -f whirlpool -l 32
Type: whirlpool
Secret length: 32
New signature: f52a0a8b9899458985b7aad687763c5a4dd37d6ceebd49d131b92bd6416eba1781ffd31ba222d9a544d83a64f7c2b119262e58d576b5d645cae59d3dd352a09b
New string: 626f6d62696e673d747275652662616e616e61733d6d697373696e6726616374696f6e3d6163746976617465266d6f6e6b6579733d6f6e74686577617980000000000000000000000000000000000000000000000000000000000000000002e826616374696f6e3d646566757365
We now have the new signature for our new body "bombing=true&bananas=missing&action=activate&monkeys=ontheway&action=defuse" which should be the way to defuse the bomb!
If we get the sha1 of this output, we finally get the correct flag
ONA{dcc78fb1416748d7f8eeb001342856e492cbd877}
DISCLAIMER: This challenge was expecting to just append "&action=defuse". In a real scenario if you add even more data after like "&action=defuse&blabla=blabla", it would have worked anyways.