Orienting

Two Sporcs are conspiring at the top of the ladder down to the next level:

image-20230105150000337

Brozeek:

Cro! Slicmer got me on the BSRS pre-sale!

Now all we gotta do is swap outfits, then you can go back in there as me.

Tell Slicmer you lost your wallet key, so you made a new wallet and need to add it to the list.

Then give him your wallet address, and we’ll both be able to buy an NFT!

Social engineering at its finest, Cro.

Crozag:

Bro, you usually have good ideas, but this one is really terrible.

Manipulating friends with social engineering isn’t cool, Bro.

Let’s do it!

At the bottom of the ladder, there’s a door to the Burning Ring of Fire:

image-20230105150620647

Buy a Hat

Challenge

The badge defines the next challenge:

Travel to the Burning Ring of Fire and purchase a hat from the vending machine with KringleCoin. Find hints for this objective hidden throughout the tunnels.

Inside the Burning Ring of Fire Wombley Cube is standing next to the HATS vending machine, and informs me of the mission he’s on for Santa:

image-20230105151022313

Hey there! I’m Wombley Cube. It’s so nice to see a friendly face.

What’s an elf doing all the way down here with all these sporcs, you ask?

I’m selling snazzy, fancy-pants hats! You can buy them with Kringlecoin.

The reason I set up shop here is to gather intel on that shady Luigi.

I’m a member of the STINC: Santa’s Team of Intelligent Naughty Catchers.

He and his gang are up to no good, I’m sure of it. We’ve got a real Code Brown here.

Purchase a hat so we look inconspicuous, and I’ll clue you in on what we think they’re scheming.

Of course, have a look at my inventory!

Oh, and if you haven’t noticed, I’ve slipped hints for defeating these Sporcs around the tunnels!

Keep your eyes open, and you’ll find all five of them. Wait, maybe it’s six?

The hints in the tunnels are useful later.

A bit further down, Palzari is next to another KTM, and greets me condescendingly:

image-20230105151119422

Hello, dear. Come down to visit your tiddly elf friend?

You two are just adorable, playing hero and braving our flaming domain.

Sure, we’ll tolerate you playing here, but please behave, won’t you?

Use this KTM to buy your darling little hats, and nothing more. If you decide to be a brat, well…

I’ll disappear you into the Devnull Chasm, and nobody will ever see you again. Do we have an understanding?

Very good. Run along now, dear.

The badge has three hints for buying a hat:

  • To purchase a hat, first find the hat vending machine in the Burning Ring of Fire. Select the hat that you think will give your character a bold and jaunty look, and click on it. A window will open giving you instructions on how to proceed with your purchase.
  • Before you can purchase something with KringleCoin, you must first approve the financial transaction. To do this, you need to find a KTM; there is one in the Burning Ring of Fire. Select the Approve a KringleCoin transfer button. You must provide the target wallet address, the amount of the transaction you’re approving, and your private wallet key.
  • You should have been given a target address and a price by the Hat Vending machine. You should also have been given a Hat ID #. Approve the transaction and then return to the Hat Vending machine. You’ll be asked to provide the Hat ID and your wallet address. Complete the transaction and wear your hat proudly!

Solution

Get Hat Info

Clicking on the vending machine pops a window with the interface:

image-20230105152342401

There are many types of hats. Clicking on one loads a window of hat options:

image-20230105152410121

Clicking on a hat gives the details about the hat:

image-20230105152449384

I’ll note the address and the Hat ID.

Pay For Hat

Clicking on the KTM opens the interface:

image-20230105152911719

I’ll click “Approve a KringleCoin transfer”, and fill in the form, and hit “Approve Transfer”. It works:

image-20230105153112732

Get Hat

Now back to the vending machine, I’ll click “Approved a transaction? Know your Hat ID? Click here to buy”, which loads this form:

image-20230105153654882

Clicking “Make your purchase!” writes the transaction to the blockchain:

image-20230105153732858

And completes the task:

image-20230105153807096

Chests

Background

There are several clues about the hidden chests throughout the subterranean tunnels Grinchum first mentions them after I get the Tolkien Ring. Some are quite noticeable while walking around in the tunnels. There are six in total:

Click for full size image

All the rings can be discovered walking around, zooming out a bit, and brute forcing when walking blind. But I can also get more information about them to learn the exact paths and their numbering (see Hacking Holiday Hack).

Chest #1

Chest #1 is in the Hall of Talks, and can be reached by walking through the wall at the left end of the hall:

This chest offers:

  • 13 KringleCoins
  • A hint for the Blockchain Divination objective

Chest #2

Chest #2 is off the ladder between the talks level and the Tolkien Ring level:

The tunnel (highlighted in yellow) is a bit winding. The chest provides:

  • 27 KringleCoins
  • A hint for the Blockchain Divination objective

Chest #3

Chest #3 is in the Tolkien Ring, at a trap door under the table:

This chest contains:

  • 15 KringleCoins
  • A hint for the Smart Contract Objective

Chest #4

Chest #4 is hidden in the main tunnels, between the Elfen Ring and Web Ring levels:

The chest provides:

  • 25 KringleCoins
  • A hint for the Smart Contract objective

Chest #5

Chest #5 is very obvious going down the ladder to the Burning Ring of Fire, but it’s less obvious how to access it. The path starts on level with the Burning Ring of Fire door, and winds around to reach the chest:

This chest contains:

  • 20 KringleCoins
  • Special Hat

The special hat is pretty cool:

img

Chest #6

Chest #6 is off the left side of the Cloud Ring, starting on the ground, then going up a few steps before continuing to the left:

This chest contains:

  • 10 KringleCoins

Blockchain Divination

Hints

Wombley is happy and offers the context for the rest of the challenges in this ring:

Nice hat! I think Ed Skoudis would say the same. It looks great on you.

So, here’s what we’ve uncovered so far. Keep this confidential, ok?

Earlier, I overheard that disgruntled customer in the office saying he wanted in on the “rug pull”.

If our suspicions are correct, that’s why the sporcs want an invite to the presale so badly.

Once the “Bored Sporc Rowboat Society” NFTs officially go on sale, the sporcs will upsell them.

After most of the NFTs are purchased by unwitting victims, the Sporcs are going to take the money and abandon the project.

Mission #1 is to find a way to get on that presale list to confirm our suspicions and thwart their dastardly scheme!

We also think there’s a Ring hidden there, so drop Mission #2 on them and rescue that ring!

Thank you for your business, dear customer!

Challenge

At the bottom of the Burning Ring of Fire, Slicmer is standing by the Blockchain Explorer, watching the blockchain:

image-20230105154201311

Don’t bug me, kid. Luigi needs me to keep an eye on these offers you can’t refute.

The boss told me to watch them for any shifty transactions from wallets that aren’t on the pre-sale list.

He said to use this Block Explo… Exploder… thing.

With this, I can see all the movement of the uh… non-fungusable tokens.

Once on the blockchain, it’s there forever for the whole world to see.

So if I spot anything that don’t look right, I can let Luigi know, and Palzari will get to the bottom of it.

She looks sweet, but she’s actually the boss’ enforcer. Have you talked to her yet? She even scares me!

It sure would be fun to watch you get on her bad side. Heh heh.

There are two hints collected from chests (see above for details):

  • Look at the transaction information. There is a From: address and a To: address. The To: address lists the address of the KringleCoin smart contract.
  • Find a transaction in the blockchain where someone sent or received KringleCoin! The Solidity Source File is listed as KringleCoin.sol. Tom’s Talk might be helpful!

The badge challenge has an input box for me to enter the address of the KringleCoin smart contract:

Use the Blockchain Explorer in the Burning Ring of Fire to investigate the contracts and transactions on the chain. At what address is the KringleCoin smart contract deployed? Find hints for this objective hidden throughout the tunnels.

Solution

The Blockchain Explorer opens a window (also accessible at prod-blockbrowser.kringle.co.in) that shows blocks on the blockchain.

Putting in the block where I purchased the hat (107795), I can see the KringleCoin.sol file that defines this contract, as well as the parameters passeed to the transferFrom function, like my address and the hats address:

image-20230105154813410

I’ll go to block number 1, and scrolling down a bit, in the transactions section, there’s one that “creates a contract”:

image-20230105154933734

The contract address is there, 0xc27A2D3DE339Ce353c0eFBa32e948a88F1C86554.

Looking at the block where I bought the hat, the contract address is the to address in the transaction (not to be confused with the _to parameter passed to the function):

image-20230105155450009

Entering this solves the task:

image-20230105155630590

Exploit a Smart Contract

Hints

There are two more hints in the badge collected from chests around the tunnels (details above):

  • You’re going to need a Merkle Tree of your own. Math is hard. Professor Petabyte can help you out.
  • You can change something that you shouldn’t be allowed to change. This repo might help!

Challenge

Two more Sporcs are standing by a computer with the Bored Sporc Rowboat Society page up. Luigi tells me about the pre-sale:

image-20230105160029722

Psst. Hey, slick - over here. Myeah.

You look like a sucker ahem I mean, savvy.

I got some exclusive, very rare, very valuable NFTs for sale.

But I run a KringleCoin-only business. Kapeesh?

Ever buy somethin’ with cryptocurrency before?

Didn’t think so, but if you wheel and deal with ya’ pal Luigi here, now you can!

But we’re currently in pre-sale, and you gotta be on the list. Myeah, see?

BSRS NFTs are a swell investment. They’ll be worth a pretty penny, and that’s a promise.

So when they’re purchasable, you better snatch ‘em up before the other boneheads ahem I mean, eggheads do.

I got a business to run. You can’t buy nothin’ right now, so scram. Kapeesh?

Chorizo is just angry not to be on the list:

Do you not have the slightest inclination of who I am?

How did I, Count Chorizo, Herald of Rrrrepugnance, not receive an invitation to the presale?

I could purchase every one of your wares, but now you shan’t have a single cent from me!

I will see to it that you nevah do business in these warrens agayn!

My badge says I need to figure out how to buy one of these presale NFTs:

Exploit flaws in a smart contract to buy yourself a Bored Sporc NFT. Find hints for this objective hidden throughout the tunnels.

The computer opens up the BSRS page (shown in it’s entrity because it’s awesome):

image-20230105160317400

The Presale page lays out what I need to do to acquire one:

  1. Validate in the form on this page that my wallet is on the presale list, giving it both the wallet address and the “Proof Values”.
  2. Approve a 100 KC transaction to their provided wallet address at a KTM.
  3. Come back and enter my wallet address and “Proof Values” again (without the “Validate Only” box checked).

Merkle Trees

A Merkle tree is a data structure for storing hashes in such a way that with limited information, the validity of an item on the tree can be checked. The link from the hints and Professor Petabyte’s KringleCon talk go into more detail.

If we build a tree where all the objects we care about at at the lowest layer, and then each layer above is the hash of two items below it concatenated, it might look something like this:

image-20230105165039222

For 8 items, this tree will have four levels. For 16 it’ll have five, 32 will have six, etc.

If I want to verify that h3 (the hash of object 3) is valid, I can send just that hash, along with h4, h1,2, h5,8, and the root value (h1,8).

The verifier can then compute h3,4 with h3 and h4. Then it can compute h1,4 with h1,2 and h3,4. Then it can compute h1,8 with that result and h5,8. If it matches the known good root, then the hashes are all valid.

As long as there are no ways to exploit the hash function, and the root is trusted as known good, this system is much less overhead than having to compute a hash across something like all nodes.

BSRS Smart Contract

I’ll want a copy of the .sol file associated with the BSRS NFT. In the blockchain explorer , I can scroll through until I find a transaction, or jump back to block 2, where it is deployed:

image-20230105161606444

I’ll download a copy of the contract. There’s a function called presale_mint that’s interesting:

function presale_mint(address to, bytes32 _root, bytes32[] memory _proof) public virtual {
    bool _preSaleIsActive = preSaleIsActive;
    require(_preSaleIsActive, "Presale is not currently active.");
    bytes32 leaf = keccak256(abi.encodePacked(to));
    require(verify(leaf, _root, _proof), "You are not on our pre-sale allow list!");
    _mint(to, _tokenIdTracker.current());
    _tokenIdTracker.increment();
}

It calls verify on a hash of the to address, the _root, and the _proof.

The verify function implements the Merkle Tree verification, combining items up the tree until it reaches the root, and then seeing if the roots match:

function verify(bytes32 leaf, bytes32 _root, bytes32[] memory proof) public view returns (bool) {
    bytes32 computedHash = leaf;
    for (uint i = 0; i < proof.length; i++) {
        bytes32 proofElement = proof[i];
        if (computedHash <= proofElement) {
            computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
        } else {
            computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
        }
    }
    return computedHash == _root;
}

Website Requests

I’ll try to validate my wallet:

image-20230105165648256

It fails.

Looking in Burp at the request that is sent when I click “Go!”, it’s a POST to /cgi-bin/presale, with the following parameters:


{
    "WalletID":"0xF4040fDea1d60705AA0b2118240A683f83Fc72df",
    "Root":"0x52cfdfdcba8efebabd9ecc2c60e6f482ab30bdc6acf8f9bd0600de83701e15f1",
    "Proof":"0xdf,0xdf",
    "Validate":"true",
    "Session":"70ff2cc8-4858-41b4-9486-2d95c3afe8cf"
}

Looking at the JavaScript loaded by the page, this request comes from bsrs.js:

function do_presale(){
...[snip]...
		var address = document.getElementById("wa").value;
		var proof = document.getElementById('proof').value;
		var root = '0x52cfdfdcba8efebabd9ecc2c60e6f482ab30bdc6acf8f9bd0600de83701e15f1';
		var xhr = new XMLHttpRequest();

		xhr.open('Post', 'cgi-bin/presale', true);
		xhr.setRequestHeader('Content-Type', 'application/json');
		xhr.onreadystatechange = function(){
			if(xhr.readyState === 4){
	            var jsonResponse = JSON.parse(xhr.response);
	            ovr.style.display = 'none';
	            in_trans = false;
	            resp.innerHTML = jsonResponse.Response;
			};
		};
	    xhr.send(JSON.stringify({"WalletID": address, "Root": root, "Proof": proof, "Validate": val, "Session": guid}));
	};
}

The root is hard-coded into the page. That’s not good.

Strategy

It’s safe to assume that the server is taking the proof, root, and wallet variables and making a request to the blockchain to either verify or presale_mint. If can control the root that’s being checked against, then I can generate any Merkle tree with proof and my own address, and send it all, and it will return valid.

Generate Merkle Tree

I’ll use the Merkle Tree repo from Professor Petabyte. After cloning it, I’ll take a look at merkle_tree.py. It has a bunch of mathy functions, and then at the bottom the code that runs as main. It hardcodes the list of nodes to build the list from:

allowlist = ['0x1337133713371337133713371337133713371337','0x0000000000000000000000000000000000000000']

Then it loops over them, recording the Keccak hash of each into the leaves list, using the leaves to generate a tree:

for address in allowlist:
    leaves.append(Web3.solidityKeccak(['bytes'], [address]))
mt = MerkleTreeKeccak(leaves)

Then it prints the root and the proofs needed for the first item in the list:

print('Root:', mt.root_hash)
print('Proof:', mt.get_proof(Web3.solidityKeccak(['bytes'], [allowlist[0]])))

Running it like this returns a single proof (which makes sense for a two node tree):

$ python merkle_tree.py 
Root: 0x431aa5796d9dcb4f660d5693a60130628c39fcbe6b83648a572929b1625f5332
Proof: ['0x3fc27be7d8c4428e63a6da81ea42dbde2939babb79dac2b3bab1afd02c5a711c']

I’ll replace the first allow list item with my address, and run:

$ python merkle_tree.py 
Root: 0x5fb06db010a8061bb2c53c6b2d4fd67945bc931c0273177c1d1ad76072ad1aef
Proof: ['0x5380c7b7ae81a58eb98d9c78de4a1fd7fd9535fc953ed2be602daaa41767312a']

Buy an NFT

Validate

To test this theory, I’ll run the page through Burp Proxy with interception on. I’ll enter my wallet and the proof from the script:

image-20230105171602915

Clicking “Go!” leads to a request at Burp:

image-20230105171635529

I’ll change the Root value to what was output by the script, and forward the request.

image-20230105171725335

It worked!

Approve Transfer

I’ll grab the wallet address of 0xe8fC6f6a76BE243122E3d01A1c544F87f1264d3a from the page, and head to the KTM. My wallet has plenty of KC to make a 100KC approval:

image-20230105172003175

I’ll approve a transfer of 100 coins to the Sporcs:

image-20230105172104899

Buy

I’ll head back and do the same thing again, turning on Burp Proxy intercept, and this time not checking the “Validate Only” box. I’ll enter my address and the same proof values from the script output:

image-20230105172245775

I’ll submit, catching the request in Burp, and editing the Root value to what the script generated:

image-20230105172345963

It works:

image-20230105172415586

And the challenge is solved:

image-20230105172522919

I can get details about my NFT:

$ curl -s https://boredsporcrowboatsociety.com/TOKENS/BSRS662 | jq .
{
  "name": "BSRS Token #000662",
  "description": "Official Bored Sporc Rowboat Society Sporc #000662",
  "image": "https://boredsporcrowboatsociety.com/TOKENS/TOKENIMAGES/BSRS662.png",
  "external_url": "https://boredsporcrowboatsociety.com/TOKENS/BSRS662",
  "token_id": 662
}

And see it:

img

Story

Burning Ring of Fire

I’ve now recovered the fifth ring, the Burning Ring of Fire:

image-20230105173101140

The story is 89% complete:

image-20230105173156505

Five Rings for the Christmas king immersed in cold

Each Ring now missing from its zone

The first with bread kindly given, not sold

Another to find ‘ere pipelines get owned

One beneath a fountain where water flowed

Into clouds Grinchum had the fourth thrown

The fifth on blockchains where shadows be bold

One hunt to seek them all, five quests to find them

Chorizo is furious:

Well…I…never…

How was a plebeian such as yourself granted access to the pre-sale?

I present thee with a proffer to purchase the NFT you’ve acquired for twice the price.

Hwhat? You shan’t vend to me? Have you any idea who I am?

You just refused the abhorrent Count Chorizo!

I shall ensure you are nevah able to transact with that NFT agayn!

Luigi suspects foul play:

What!? How’d you get on the list? What’s that? You’s a double agent, and you’re actually workin’ for us?

I don’t know if I buy that, but you’re on the list, so… myeah.

Somethin’ about this ain’t sittin’ right with me, but there’s no reversing transactions with cryptocurrency.

That NFT is yours to keep, but if I find out you’re lyin’ to me, Palzari’s gonna pay you a visit. Kapeesh?

Slicmer might be on to me:

Hmph… this is so boring

“This is a serious task” he said, “not a sporc headbutting-party” he said.

“Mess this up, Slicmer, and I’ll tie a rock to your feet and throw you down a well!” he said.

I think this job was just to keep me out of his way. Luigi thinks I’m a blockhead.

Well I think he’s a – Huh? Wait a minute…

Hey! Boss! I think I see somethin’!

Grinchum tells me to go to the castle:

😠 We wants them… we needs them… Must.. have.. the Preciouses.

They stole them from us, sneaky little humanses.

🙂 No, not the humanses, they’re my friends.

😏 You don’t have any friends. NOBODY likes YOU. You’re a liar, and a thief, and a…. grriiiiiiinch.

😢 Go away… we don’t need you anymore. The humanses protect us now.

😠 Go away? I protected us. The preciouses are safe because of ME!

🙂 Leave now, and never.. come back. 😃 Leave now, and never.. come back!

😁LEAVE NOW, AND NEVER.. COME BACK!😬

Friendly human, please go to jolly human’s castle! Go on, we will meet you there!

Conclusion

The castle doors are now clear:

image-20230105173756175

Inside I’ll find Santa, elves and Fobbins, the birds from last year, and Smilegol, transformed from Grinchum now that the rings are recovered and no longer controlling him.

image-20230105173859044

Santa congratulates me:

image-20230105174002865

Congratulations! You have foiled Grinchum’s foul plan and recovered the Golden Rings!

And by the magic of the rings, Grinchum has been restored back to his true, merry self: Smilegol!

You see, all Flobbits are drawn to the Rings, but somehow, Smilegol was able to snatch them from my castle.

To anyone but me, their allure becomes irresistible the more Rings someone possesses.

That allure eventually tarnishes the holder’s Holiday Spirit, which is about giving, not possessing.

That’s exactly what happened to Smilegol; that selfishness morphed him into Grinchum.

But thanks to you, Grinchum is no more, and the holiday season is saved!

Ho ho ho, happy holidays!

And the story is complete:

image-20230105203549740

Five Rings for the Christmas king immersed in cold

Each Ring now missing from its zone

The first with bread kindly given, not sold

Another to find ‘ere pipelines get owned

One beneath a fountain where water flowed

Into clouds Grinchum had the fourth thrown

The fifth on blockchains where shadows be bold

One hunt to seek them all, five quests to find them

One player to bring them all, and Santa Claus to bind them