During the last 2 days, I participated in the 33c3 CTF. This post contains the second part of my writeups for this CTF, covering the smartfridge1 and smartfridge2 challenges.
Note that the content provided for smartfridge2 (a crypto challenge!) speeds up solving smartfridge1 considerably, so I will provide the original files for both challenges together.
These two challenges were presented as a binary (smartfridge1), and a pcap of someone communicating with said binary (smartfridge2), which you can download here. The challenge text indicates that this is something to do with the Bluetooth LE 4.0 protocol:
Our task appears to be to reverse engineer the provided binary, build something which can interface with it (and thus, the remote server), and send a series of commands to “retrieve” the flag.
On first inspection, we notice that the binary makes heavy use of OpenSSL AES functions, and is littered with printf debugging. As a starting point, I decided to use the pcap provided for smartfridge2, and simply send the first packet in the pcap to the binary. Instant success:
We can look for these printf statements in IDA (the second SEND below is an SCONFIRM packet):
Our next step is to carefully gdb our way through the “c1” function, which appears to set an AES encryption key and encrypt *something*, to determine what’s being encrypted:
In this instance, the key is somewhat recognizable (the hexadecimal representation of atoi(“123456” – which appears to be a test PIN)), but the plaintext (0xe6…) is not. It doesn’t help that despite sending a static message to the server, this plaintext seems to change every time. Let’s set this aside for now.
Going back through the disassembly, we can shed some light on the structure of the packet: breakpoints at 0x40158B and 0x40159A reveal that the first DWORD is a packet length, which must be above 3 and below 84. Also, we can see a “state variable” being used to control program flow – packets must be sent in order:
Continuing with this avenue of investigation, we add in the second packet from the pcap (the one starting with “\x14\x00\x00\x00”) to our client:
This time, something more interesting comes up: once more, we return to the disassembly to find out what’s causing our “invalid pincode” message:
Here, we go back to dynamic analysis to find what’s being passed to c1, and what it’s being compared against. To make life easier for ourselves, we replace the “body” of each packet with clearly recognizable strings like “\x01″*16 (MConfirm, packet 1) and “\x02″*16 (MRand, packet 2).
This time, we have some success:
Here, we can see that upon receiving the “MRand” packet, “c1” is called to encrypt “\x02\x02\x02\x02…” with “123456”, our test PIN code, the result of which is “d86d0cfbc7bb0343f09fad5ca6824429”. We then proceed to the memcmp call at 0x4017D7:
Hang on, we know those values! It’s comparing the content of our MConfirm packet with AES(key=”123456″,content=MRand)! Knowing this, it is trivial to pass the MRand check, by sending the correct MConfirm packet first:
Success! At this point, we are sending:
- Packet 1 – MConfirm: \x15\x00\x00\x00\x00\x02 + aes(key=”123456″,text=”\x02″*16)
- Packet 2 – MRand: \x14\x00\x00\x00\x00 + “\x02” * 16
Now that we’re paired, let’s try sending another packet. As the process logic appears to be controlled via state variable instead of any “command specifier”, let’s just try sending a packet containing “\x03″*16 and let’s see what happens:
Going back through the disassembly, we can see that this comes from a decryption function at 0x4015F2:
At a glance, this function appears to attempt an AES decryption, and if the decryption is successful, the decrypted data gets passed to strtok (0x40162E), and from there, to command handlers which try to do string comparisons against known commands (e.g. “OPEN” at 0x4018B3), giving us some clue as to what the plaintext needs to be.
Stepping through this decryption function, I quickly noticed that the key was not the “PIN code” key. Perplexed, I turned to the Bluetooth Low Energy documentation:
From here, we can make a reasonable guess that the decrypt() function is using the STK instead of the TK / pincode. After reading through the documentation, we can determine that the STK is calculated thusly:
STK = AES(key=pin,text=second_half_of_srand + first_half_of_mrand)
Going through the disassembly, we can see that the “s1” function is present in the binary, at 0x40188F – we’ll need to emulate this function in our client, so we can use the same STK session key to encrypt command traffic to the server. In gdb:
Stepping through this function, we can confirm the documentation, and thus, correctly emulate s1 and derive our own STK (see the solution Python script).
With the STK, we can now send correctly encrypted commands to the server. The commands “OPEN 2”, “LIST” and “TAKE 0” can be used to retrieve a flag – in our test binary, this is “33C3_NOT_FLAG_2” – the only remaining change is to modify the PIN code, and send traffic to the actual server instead:
A little trial and error later, and I realized that the failed pairing was due of the initial MConfirm message. When we prefixed the packet with “\x15\x00\x00\x00\x02”, we were selecting user 2, who seemed to have a different PIN code. Moments later (and after changing “OPEN 2” to “OPEN 1”, the flag:
You can find the complete solution script here – it’s a functional implementation of Bluetooth LE pairing over TCP! 🙂
This challenge was presented as a PCAP (download together with smartfridge1 binary above), and we were tasked with finding the flag belonging to “user 2” on another shelf in the smart fridge.
With the knowledge of the Bluetooth LE pairing protocol gained during the last challenge, this challenge was trivial. We had a PCAP of a Bluetooth pairing, for “user 2” (as indicated by “\x15\x00\x00\x00\x02” in the MConfirm packet).
With the MConfirm and MRand packets, and knowing that:
MConfirm = AES(key=pin,cleartext=MRand)
it is trivial to brute force the key. A few modifications to the client script later, and we have the second flag:
You can find the modified solution script here.
As always, I’d like to thank the organisers of 33c3’s CTF, Eat Sleep Pwn Repeat, for putting together a fantastic event. While I was not able to solve as many challenges as I had hoped, I had a lot of fun and learned alot solving the ones I was able to complete, and look forward to doing the others in my own time.
If I might offer some feedback, the point values seem imbalanced, and this was reflected by other participants in this event. For example, it is clear that smartfridge2 required a miniscule fraction of the effort required by smartfridge1, yet was worth 50% of the points. Still, this by no means detracted from the excellence of the event, as it didn’t make the challenges less fun.
A safe and happy New Year’s to you all, and see you all in a few weeks for the Insomni’hack Teaser CTF.