Software Update @ 34C3
The 34C3 CTF
34C3 has just ended and the year is quickly coming to an end. As usual I had the pleasure of playing the CTF at CCC. What I particularly like about the C3 CTF is the ingenuity and variety of challenges (not just binary reversing + exploitation and web).
The “Software Update” challenge
We are given 3 files:
- installer.py
- public_key.der
- sw_update.zip
The challenge is a firmware updating service (provided in installer.py
)
and an example of a signed update (provided in sw_update.zip
).
The challenge is similar to flash
from 32C3.
Inside sw_update.zip
we find:
|
|
The installer checks the size of the update (before + after decompression),
then verifies the authenticity by hashing all data in signed_data
and verify the signature included in the .zip against the hash
using the RSA key in public_key.der
:
- Check size of zip file
- Unpack zip file
- Check size again
- Verify signature
signature.bin
against hash ofsigned_data
usingpublic_key.der
- Run
pre_copy.py
script - Copy files
- Run
post_copy.py
script
Vulnerability
The vulnerability lies in the way signed_data/
is hashed during the signature check:
|
|
The code hashes every file & directory name separately, then xors the individual hashes to compute the final hash passed to the signature verification procedure (note also that sorting the file names is superfluous).
If we modify the content of signed_data
such that
the xor of all hashes of the new data
matches the original hash,
then the original signature is valid for the new data.
We can obtain arbitrary code execution easily by modifying
the pre_copy.py
and post_copy.py
scripts.
Solution
We update the pre_copy.py
script to os.system('/bin/sh')
and turn our attention to generating the files which will “fix”
the hash value.
Observe that xor for 256-bit values corresponds to
the addition of vectors in \(F = GF(2)^{256}\) (\(GF(2)\) in 256 dimensions).
We compute the hash \(H_{mal} \in F\) of all the data that must be present
in the malicious sw_update.zip
file
and the hash of the original data \(H_{org} \in F\).
The goal is then to find:
\[ v_{0}, v_{1} \ldots, v_{n} : H(v_{0}) + H(v_{1}) + \ldots + H(v_{n}) = H_{org} - H_{mal} \]
Where addition and subtraction in the field is xor. I do this by computing a basis for the entire vector space \(F\), where the basis vectors corresponds to the images of empty files with random names under \(H\). For this I use SageMath:
|
|
After computing the basis, I convert the element \(H_{org} - H_{mal}\) to the new basis and extract the corresponding pre-images (file names) from all basis vectors with non-zero coefficients in the decomposition of \(H_{org} - H_{mal}\).
|
|
Lastly you zip and upload the malicious update to the service:
|
|
Full challenge and doit on Github
Thanks to the Eat, Sleep, Pwn, Repeat team for a wonderful CTF (as always).