Blockchain Investigation Part 2

Objective11b

This final objective includes no new elves to talk with, so I have to rely on previous intel from Tinsel and Tangle who shared quite a lot already as part of Objective11a.

The main new piece of information for 11b is the SHA256 of the block that I need to inspect0 closer. It’s suspected that Jack has somehow managed to do the impossible and modify its content without breaking the blockchain. The hash of this block is:

58a3b9335a6ceb0234c12d35a0564c4ef0e90152d0eb2ce2082383b38028a90f.

This final objective mentions that Jack possibly got away with this just by tweaking 4 bytes, so my focus is now to figure out where those 4 bytes may be hidingso that I can try to undo these changes.

There is a small amount of new information in the badge hints section:

  • 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.
  • 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.

These two hint at the fact that Jack has made two modifications, one in the block data structure and one in the attached PDF itself; and secondly that he has may have used the UNICOLL technique which has some very special properties when applied to MD5. The hint about UNICOLL only clicked for me after going through the lengthy slide deck from this presentation by Ange Albertini.

Armed with this knowledge I set out to inspect the block in question using the provided python script. I write a bit of code to loop through the whole chain until the block with correct SHA256 is found and then print it to the terminal and save it to file as block129459.dat:

1for i in range(len(chain.blocks)):
2     h = SHA256.new()
3     h.update(chain.blocks[i].block_data_signed())
4     if h.hexdigest() == '58a3b9335a6ceb0234c12d35a0564c4ef0e90152d0eb2ce2082383b38028a90f':
5          print(chain.blocks[i])
6          chain.save_a_block(i, f"block{chain.blocks[i].index}.dat")

Below is a redacted version of this block as printed to terminal:

 1root@c288761e5038:/usr/src/app# python3 naughty_nice.py
 2Chain Index: 129459
 3              Nonce: a9447e5771c704f4
 4                PID: 0000000000012fd1
 5                RID: 000000000000020f
 6     Document Count: 2
 7              Score: ffffffff (4294967295)
 8               Sign: 1 (Nice)
 9         Data item: 1
10               Data Type: ff (Binary blob)
11             Data Length: 0000006c
12                    Data: b'ea4...8d8f09'
13         Data item: 2
14               Data Type: 05 (PDF)
15             Data Length: 00009f57
16                    Data: b'255...019a43'
17               Date: 03/24
18               Time: 13:21:41
19       PreviousHash: 4a91947439046c2dbaa96db38e924665
20  Data Hash to Sign: 347979fece8d403e06f89f8633b5231a
21          Signature: b'MJIx...MCtHfw=='

There were several lines that stand at immediately as suspicious:

  • Line 7 is suspicious because of the maxed-out integer value. In the hints a different value is mentioned by Tinsel: 4,294,935,958 which corresponds to FFFF8596 which has two bytes difference from FFFFFFFF. However, this turns out to be a dead-end after realizing that this is not compatible with what I’ve learnt about the UNICOLL technique.
  • Line 8 is suspicious because Jack does not seem to be a trustworthy character, so perhaps he may have flipped the Nice/Naughty switch in the block to cheat the system. This suspicion is confirmed later when I extract the PDF and unlock the hidden content originally written by Shinny as a report on Jack’s Naughty behaviour!
  • Lines 10-11-12 are suspicious just because no other block contained two files, but I cannot immediately explain why this is significant
  • Line 17 is suspicious because I think that Jack might have tweaked the date to hide his activity and switched the month from December 24 to March 24. However, this seems to be a dead-end as well because it’s inconsistent with the UNICOLLL technique.

Next I set out to inspect the PDF document closer, which I extract to filesystem using the provided python script. After opening it in HexFiend I do not find anything suspicious at first, so I go back to the slide-deck from Ange to see if I can find some clues there, and I sure do:

PDF Trickery

This slide shows a trick that can be used to tweak contents of the PDF by changing which object or page is referenced. I try the same on the extracted PDF, and it’s content changes dramatically. It goes from a glowing report on Jack to this:

Earlier today, I saw this bloke Jack Frost climb into one of our cages and repeatedly kick a wombat. 
I don’t know what’s with him... it’s like he’s a few stubbies short of a six-pack or somethin’.
I don’t think the wombat was actually hurt... but I tell ya, it was more ‘n a bit shook up. 
Then the bloke climbs outtathe cage all laughin’ and cacklin’ like it was some kind of bonza joke. 
Never in my life have I seen someone who was that bloody evil...
  - ”Quote from a Sidney (Australia) Zookeeper

I have reviewed a surveillance video tape showing the incident and found that it does, indeed, show that 
Jack Frost deliberately traveled to Australia just to attack this cute, helpless animal. It was appalling.

I tracked Frost down and found him in Nepal. I confronted him with the evidence and, surprisingly, he seems 
to actually be incredibly contrite. He even says that he’ll give me access to a digital photo that shows his 
“utterly regrettable” actions. Even more remarkably, he’s allowing me to use his laptop to generate this 
report – because for some reason, my laptop won’t connect to the WiFi here. 

He says that he’s sorry and needs to be “held accountable for his actions.” He’s even said that I should 
give him the biggest Naughty/Nice penalty possible. I suppose he believes that by cooperating with me, 
that I’ll somehow feel obliged to go easier on him. That’s not going to happen... I’m WAAAAY smarter than old Jack.

Oh man... while I was writing this up, I received a call from my wife telling me that one of the pipes inour 
house back in the North Pole has frozen and water is leaking everywhere. How could that have happened?

Jack is telling me that I should hurry back home. He says I should save this document and then he’ll go ahead and submit the full report for me.  

I’m not completely sure I trust him, but I’ll make myself a note and go in and check to make absolutely sure he submits this properly.

Shinny Upatree3/24/2020

Note: on MacOS I have to open this PDF in either Firefox or Chrome, because the built-in reader detects some corruption that prevents it from opening properly.

Once I have the hidden content I am sure that this is one of the 4 bytes that Jack has tweaked in the block.

Next I open the whole block in HexFiend to see it in its entirety. I spend many hours trying to find the two remaining bytes that Jack had tweaked. I am also in touch with some fellow HHC attendees who provide some great insight to help me avoid dead ends in my endeavors.

Eventually what helps me tremendously is the slide-deck from Ange and a tip from joergen to remember the 64 byte chunk sizes for MD5. This information combined with slide 113 eventually helps me find the remaining two bytes:

Slide 113

Turns out, due to some cryptographic properties of MD5, if you change some byte in Chunk N to +1, then you need to change the byte on the same location in Chunk N+1 to keep the MD5 hash the same. Finally, it clicks why there is a random binary file attached to the block. It is the extra random garbage that helps with the flipping of the Sign from Naughty to Nice while keeping the hash of the whole block unchanged.

Next I go back to HexFiend with the whole block open and modify the 4 bytes using this rule:

Hex Fiend Solution

Tweaks explained:

  1. byte 73 was 0x31 and I set it to 0x30 so that it becomes Naughty again
  2. to keep the MD5 from changing, I then had to increase byte 137 by 1 from 0xD6 to 0xD7
  3. byte 265 was 0x32 and I changed it to 0x33 to fix the PDF document
  4. to keep the MD5 from changing, I then had to decrease byte 329 from 0x1C to 0x1B

Next, I use the docker image to get the MD5 and SHA256 values of the fixed block, and to my big surprise the MD5 remains unchanged:

1root@c288761e5038:/usr/src/app# md5sum block129459.dat
2b10b4a6bd373b61f32f4fd3a0cdfbf84
3root@c288761e5038:/usr/src/app# sha256sum block129459.dat
4fff054f33c2134e0230efb29dad515064ac97aa8c68d33c58c01213a0d408afb

I then submit the SHA256 hash of the fixed block fff054f33c2134e0230efb29dad515064ac97aa8c68d33c58c01213a0d408afb and it is accepted as correct! 😎 🎉

Finally, I go up to the balcony in Santa’s Office to complete the narrative and join the party:

Victory

Previous
Next