Naughty/Nice List with Blockchain Investigation Part 2⚓︎
The SHA256 of Jack's altered block is: 58a3b9335a6ceb0234c12d35a0564c4e f0e90152d0eb2ce2082383b38028a90f. If you're clever, you can recreate the original version of that block by changing the values of only 4 bytes. Once you've recreated the original block, what is the SHA256 of that block?
Imposter Block Event
Shinny Upatree swears that he doesn't remember writing the contents of the document found in that block. Maybe looking closely at the documents, you might find something interesting.
Unique Hash Collision
If Jack was somehow able to change the contents of the block AND the document without changing the hash... that would require a very UNIque hash COLLision.
Blockchain ... Chaining
A blockchain works by "chaining" blocks together - each new block includes a hash of the previous block. That previous hash value is included in the data that is hashed - and that hash value will be in the next block. So there's no way that Jack could change an existing block without it messing up the chain...
Apparently Jack was able to change just 4 bytes in the block to completely change everything about it. It's like some sort of evil game to him.
The idea that Jack could somehow change the data in a block without invalidating the whole chain just collides with the concept of hashes and blockchains. While there's no way it could happen, maybe if you look at the block that seems like it got changed, it might help.
Qwerty Petabyte is giving a talk about blockchain tomfoolery!
Understanding the Naughty/Nice Blockchain format
Professor Qwerty Petabyte explains the architecture and technical background of the Naughty/Nice Blockchain in great detail but two key points to remember are (1) that each block in the blockchain contains the MD5 hash of the previous signed block data (i.e., block data + signature) and (2) that the signature is created based on the MD5 of the block data.
Dumping the block
Before determining what exactly happened to Jack's altered block we first need to find it and save the signed block data to a file. Grab the
blockchain.dat file from the desk in Santa's office and the
naughty_nice.py script from the official Naughty/Nice Blockchain education pack. We need the latter to read the
blockchain.dat file and export block data to a file.
dump_blocks.py to load the
blockchain.dat file, find the block with the matching SHA256 value, print the block details, and save the signed block data and any attached documents to
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
The block data contains the highest possible nice score (lines 6-7) and a glowing PDF report about Jack Frost (lines 13-15). Any tampering with the blockchain should cause a verification failure, but it doesn't. It seems like the MD5 hash of the unsigned block data, the signature, and the md5 hash of the signed block data didn't change at all. More on that later.
The objective also mentions that only 4 bytes were modified. The
sign field, which indicates if the score is naughty or nice, only takes up 1 byte. Replacing a whole PDF file would requires far more than the remaining 3 bytes.
Manipulating the PDF
In reality, the PDF document wasn't replaced at all. It already contained both the scathing report and its glowing alternative prior to being added to the blockchain. You can toggle between both versions by changing the object the PDF catalog points to. The default value of 2 will display the nice content. Changing the value to 3 will render the PDF starting from a different object tree and show the original report which is far less positive.
Enter... hash collissions!
While it's now clear what was changed, we still need to find 2 of the 4 modified bytes and explain how changing the
sign from 0 to 1 and the PDF catalog from 3 to 2 didn't change the MD5 hash of the data, triggering a verification failure of the blockchain as a result. That's where hash collisions, and more specifically UniColl, come into the picture.
Using a UniColl hash collision, 2 sets of data can be created which share the same MD5 yet differ by 2 bytes. While the location of these 2 bytes is predefined (i.e. the 10th char of the prefix and the 10th char of the 2nd block) adding redundant data to the prefix ensures the data we want to modify neatly aligns with the location of the first byte. That's why the PDF catalog entry is set to
<</Type/Catalog/_Go_Away/Santa/Pages 2 0 R instead of
<</Type/Catalog/Pages 2 0 R. The
_Go_Away/Santa/ part was added to align the catalog reference with the first collision byte. Jack got lucky with the
sign value as the block's data structure puts that byte in the expected location out of the box.
A second important characteristic of a UniColl hash collision is that the way both bytes change is predefined as well. The 10th byte of the prefix will either be +1 or -1, while the 10th byte of the second block will change by the same amount but in the other direction. In other words, increasing
sign from 0 to 1 requires the 10th byte of the second block to decrease by 1.
Recreating the original block
We can now modify the signed data block we extracted from the blockchain and revert it to the state it was in when Shinny Upatree was tricked into adding Jack's tampered PDF to the blockchain. The below images show the locations and values of the 2 collision bytes for both the
sign field and the PDF catalog entry. The modified version of the signed block data (i.e.,
129459.dat) is shown at the top while the original version is shown on the bottom (i.e.,
What about the signature?
Because of the UniColl hash collisions the MD5 hash of the unsigned block data remains the same regardless of the the 4-byte change. This causes the signature to remain valid as it's created using the MD5 hash of the unsigned block data. As the signature is appended at the end of the unsigned block data, the MD5 of the signed block data (which is stored as part of the next block in the chain) remains the same as well.
Finally, calculate the SHA256 of the original signed block data.