Share this post

Link to post

Share on other sites

There is still potential in dictionary attacks. I know Netrix tried it previously, but he appeared to have limited his attempt to a single word with mixed capitalization. My guess is that whatever this password is, it's probably a short sentence with correct grammar. I wouldn't be surprised if a dictionary generated from the in-game strings exclusively would be sufficient for the job.

Also, I just had another idea: is it possible to deduce an AES-256 key if we already know a chuck of the original data big enough to cover the key length? Based on the Lua source code and the game files, if we assume that the original SecretRoom.lua is compiled and was done so at the same location than the encrypted version, then I believe the first 69 bytes would be:

Share this post

Link to post

Share on other sites

I was going through all of my old folders to organize my data and I came across the files related to this. For brute forcing, I did only try single words with symbols and numbers. I didn't get around to trying proper sentences from the game's strings. There are quite a bit of strings within the game files though so I don't know how long it will take to try up to even just 3 word sentences. I'll at least make an attempt though I do agree that this is almost certainly not the intended way to solve it.

Share this post

Link to post

Share on other sites

Also, I just had an idea watching keybounce's stream. There's a few empty chests in the game. I originally thought it was unfinished content, but it is possible it's a hint for the secret room somehow.

Share this post

Link to post

Share on other sites

I was thinking about the wishes that Smash had me use after completing the game, to get the encrypted secret room book.

What if the decryption key is one of those wish strings?

We've got a number of "useless" things in the game to consider, such as a treasure chest that has nothing in it. What if the chest itself is the decryption string -- or more accurately, the wish name that refers to that chest?

Share this post

Link to post

Share on other sites

Also, I just had an idea watching keybounce's stream. There's a few empty chests in the game. I originally thought it was unfinished content, but it is possible it's a hint for the secret room somehow.

Since any in-game script would be incredibly slow and useless for brute forcing, I'm using a C program that uses the same decryption process as the game that I've highly optimized. I can try about 6 million passwords per second, but if the password has special characters, has 8 or more characters, or is more than 3 words in length, it is practically impossible to brute force with current technology. Maybe some day in the future when we have quantum computing it will be possible to break the encryption, but it doesn't look like it will be any time soon. I've been busy recently but maybe at some point I'll package what I have so other people can play with it.

11 hours ago, 0xffe3 said:

So, just played through the game and tried to decrypt secret.lua

I got nothin'.

I'd glad to see this is still active as of last month, though, do you folks have any methods of attack or any progress?

I've just tried brute forcing and various guesses that other people have made.

Share this post

Link to post

Share on other sites

AES is unfortunately not susceptible to known-plain-text attacks. But it might help when checking for a valid key.

I've been questioning this statement recently.

Here's what I've gathered so far:

The key string is hashed using SHA-256, which is used as the key for AES-256-CBC with a null IV, to generate the output.

We already know the first 4 blocks of 16 bytes each of the original plaintext due to the Lua file structure and the game's directory structure (see my post from June 19, 2016).

Due to the properties of CBC, deciphering a block only requires the key, the block's ciphertext and the previous block's ciphertext, or the IV if there is no previous block.

With this information, we have 4 sets of AES-256 "equations" with only the hashed key as the unknown variable. The question is, is deducing the key from those "equations" realistic?

It sounds unlikely to me, but I could not find a definitive answer to this question. I've seen claims that terabytes of known plaintext block matches wouldn't be enough to do so, but could not find the mathematical arguments to support them.

This describes a 'key recovery attack', which assumes you know the method, all the plaintext, and all the ciphertext, and it concludes that for AES-256, we would need 2^254.4 time to recover the key, provided we have sufficient plaintext/ciphertext known matches.

That's around 10^60, or a trillion of a trillion of a trillion of a trillion of a trillion operations, so that's not a viable method anytime soon. It *is* slightly faster than brute forcing, though. And they assume full plaintext access.

I've thought a bit about the structure of AES and the fact that we're expecting code structure, but there's absolute confusion and absolute diffusion, so that doesn't help us.

Our best bet is either to attack the key, knowing it will probably make human-sense, or to continue looking for clues.

I have to admit, it would be fun to become recognized as the mathematician who provided a practical break for AES for the purposes of a videogame, but I trust that my skills in the area aren't up to match with the mathematicians who've been trying for the past decades.

(That said, I do see a potential 'attack the zeros' vector, and I don't see what's been done to prevent that from working, so I'm tinkering. Unfortunately, that would only enable partial decryption of 'null' characters, which can't be verified unless the code sample has null characters, so it's useless for us anyways)

Share this post

Link to post

Share on other sites

Heh, I thought for weeks about the P versus NP problem just for the kicks myself. No dice.

I'm surprised this paper doesn't reference the controversial eXtended Sparse Linearization (XSL) attack. Not sure if that's important or not though.

I don't know if attacking the zeroes matter, but if it does then I should point out that the last bytes at the end of the file immediately before the PKCS #7 padding should be:

00 00 00 00 00 00 00

Being able to guess the key string itself would be great, but if it's a long sentence like the normal PrincessChambers.lua key then we might be in trouble unless it's based on a string in the game or a known incantation.

As for clues, if they exist in the game, they are either truly well hidden, hidden in plain sight, or we overlooked them somehow. Otherwise, there could be clues hiding anywhere, including the following locations:

Share this post

Link to post

Share on other sites

Mmm, as soon as I started to implement AES-256-CBC I realized my mistake.

So first, it adds a roundkey at each cycle, so now our zero is swapped . . . okay, whatever, we can still work with that.
And then it also performs a substitution, so our zero is swapped again.

Which would all be fine, but then for AES-256, it performs the cycle 14 times. Our zeros won't keep their properties all through that. If it was only the substitution, I could do something, if it was only the addition, I could do something, but both together on multiple cycles means that a zero gets multiply-transformed probably 13 out of 14 times. We require a multiply-transform to work 0 times in order for an attack-the-zeros vector.

The odd number means that there IS a potential miss-in-the-middle attack using an attack-the-zeros vector, but having looked into some more papers, people have 100% tried that, and I think that's actually fully encapsulated by the use of bicliques. Which is handy.

My brain is still chewing on it, but like I said, I'm 100% not as good as, and 200% not as familiar with AES as the mathematicians who've been chewing on it for the last decade plus.

Share this post

Link to post

Share on other sites

Every part of the ciphertext block relies on every letter of plaintext block, (diffusion), and every piece of the key (confusion).

So the key is broken up into 14 round keys using rotations and a substitution table. (For aes 256)

Then we take our block input, add the 1st roundkey to it, make substitutions, rotate the rows, and mix the columns.

edit: sorry, that entered before I finished. Continuing:

At this point, say, b(1,1), (block input at coordinates (1,1)), is now transformed by factors of: The roundkey portion that corresponds to (1,1), the block input from (2,4), (3,3), and (4,2), and the roundkey portions from those as well.

And the roundkey portions will correspond to different parts of the key.

Now to make the next roundkey, we start XORing against the previous roundkey

And we take our current block-in-memory, add the 2nd round key, make substitutions, rotate the rows, and mix the columns.

So, b(1,1) was transformed by b(1,1), b(2,4), b(3,3), b(4,2), and now it's going to be effected by block-in-memory (1,1), (2,4), (3,3), (4,2), which have each been transformed by their entire columns in the previous step. They are all in different columns.

Which means that b(1,1) has now been transformed by b(1,1), b(1,2), b(1,3), b(1,4), b(2,1), b(2,2), b(2,3), b(2,4), b(3,1), b(3,2), b(3,3), b(3,4), b(4,1), b(4,2), b(4,3), b(4,4) and the corresponding parts of the two roundkeys, which means it has been effected by ALL input, and in AES-256, some of the key. In AES-128, that would be all of the key.

There are now twelve more rounds after this.

If we change just one bit of the input, or one bit of the key, every single bit of the output is effected, (and on average, 50% of them will change).

nb: I've still only done a cursory examination of the algorithm, having never looked at it before, I'm probably still getting details wrong.

Feel free to derive the equation from that all . . . it'll take a lot of tedious work and it won't be helpful. That substitution step will do you dirty.

Edited May 28, 2018 by 0xffe3

Share this post

Link to post

Share on other sites

Hmm you're right. I read through the entire AES standard, and if we would skip the finite-field multiplicative inverse substitution in the SubBytes step, each output bit could be represented as a series of XOR operations.

I have no idea what a formula for this multiplicative inverse would look like... if one can even be written.

Share this post

Link to post

Share on other sites

It's what we call a 'non-linear algorithm' . . . it can't be represented by normal means without the use of "if-then" statements, so it doesn't follow a linear path and can't be (easily) expressed as a single equation.

(Many of them can theoretically be expressed as single equations through very hack-y means that wouldn't help to simplify the problem any)

You can see why a non-linear step is considered essential for cryptographic purposes. It just thwarts analysis by making the algorithm itself the simplest way to express the transformation.

If you want to understand some of the theoretical concerns because ciphers like AES better, I can skeleton out a brief road map?

First: if you don't already have a basic understanding of substitution ciphers, how to solve and etc. play with a tool like http://rumkin.com/tools/cipher/cryptogram.php, and some cryptograms you can find anywhere on the web.

Second: One of the earliest ciphers which demonstrates an understanding of confusion and diffusion is the Hill Cipher. It doesn't contain any non-linear steps, and it does contain some linear algebra, so it's an excellent starting point into modern cryptography. I would start with 2-letter and 3-letter Hill Ciphers, and then extend your learned techniques to 6-letter Ciphers.

Once you know how to crack that, consider a block cipher version of it. Say, a 2-letter version. First, the message is padded out until the length is a power of 2. Then each pair is encoded as normal. Then we go up a layer and encode each pair of pairs, pairwise. Then we go up a layer, and encode each pair of (pair of pairs), (pair of pair) wise. In the end that'll be log(n)*n encodings, and every cipher-letter will depend on every plaintext-letter.

Fourth, then start on Hill ciphers as they are meant to be used: In combination with other techniques. In the original patent, Hill mentions using a non-linear substitution cipher on the text before and after the application of the Hill Cipher . . . so it appears he saw it as providing diffusion to the ciphertext. Diffusion and Confusion would not be named terms until partway through WWII, however.

Fifth, start on now-broken block ciphers, like some early Feistal ciphers. Those will also contain non-linear steps, and some that are 'broken' are still only 'academically broken' in the sense that the breaks are still non-practical to ever realistically attempt.

By that point, you will have a workable understanding of each component of the AES cipher, and why it's constructed the way it is. The roundkeys, the rotations, the non-linear step, etc., everything is designed to bring a particular strength to the algorithm, and it's very little more than plug-together in that respect . . . that it was a top contender in encryption that was heavily analysed by many people, and many of its other contenders were struck down for discovered or suspected weaknesses, is where its strength comes from. Not from a derivable difficultly value, but from the sense that it was remaining after tough examination that eliminated its competitors.

That all said, do we have the implementation of the AES encryption in the game somewhere? I want to comb through it for differences from other implementations and see if I sniff an implementation weakness.

Share this post

Link to post

Share on other sites

The AES functions in the game are exposed in the Lua environment through the DFHack object as encipherBuffer and decipherBuffer. Their implementation are in the Hack.exe x86 binary. tjablin apparently did a disassembly, but the code he posted as reference is no longer accessible, and I'm not sure if the C version he wrote is an exact match or not.

Share this post

Link to post

Share on other sites

The AES functions in the game are exposed in the Lua environment through the DFHack object as encipherBuffer and decipherBuffer. Their implementation are in the Hack.exe x86 binary. tjablin apparently did a disassembly, but the code he posted as reference is no longer accessible, and I'm not sure if the C version he wrote is an exact match or not.

It can still be accessed here: https://bitbucket.org/tjablin/dfhack/src/default/ His version is mostly good except that it does two rounds of decryption when it should only be one due to a bug in the game that caused tjablin's .lua file to be double-encrypted. When I have a chance, I'll post my version which does one round of decryption and is optimized (at least partially) for speed and works on Windows as well. While I tried bruteforcing and used dictionary attacks with it, its most useful function would be to quickly try various passphrases (and variations of them) to see if they are correct without having to open the game to try it manually.

Share this post

Link to post

Share on other sites

I was under the impression that tjablin's posted C code was not actual disassembly, but only reproduced the output? Unless Hack.exe also uses LibTomCrypt?

As the encryption is triggered by entering DRMRoof, the trick to avoid multiple encryption is to not exit DRMRoof while a book is on the pedestal, including with PrincessChambers already there by default.

Share this post

Link to post

Share on other sites

First of all, I got as a gift an official Hack 'n' Slash T-shirt, and I figured it might be interesting to describe the package contents here for completeness. The T-shirt itself looks exactly like the pictures in the store, except the inner part has the T-shirt size instead of "MD". There's no hidden label hidden inside it or anything. The plastic wrap had a nice little sticker with the same design, the T-shirt size, the Double Fine URL... and the string "HACK N' SLASH", with a missing apostrophe before the "N". Inside the plastic wrap, there was also a small Fangamer swag package, including a Fangamer marketing brochure, an untitled holiday 2017 postcard by Laura Wilson, a Super Smash Bros. pin, and a Final Fantasy VII... um... thing - it feels plastic-y on the printed side and like paper on the other side, and if it's a sticker I can't peel it off. Interestingly, the postcard's "From" and "To" labels are printed using the same retro font as the one used on the T-shirt.

So pretty cool, but at first glance, nothing interesting that might relate to this puzzle. However, upon closer inspection, I realized that the Hack 'n' Slash logo printed inside the T-shirt actually contains a hidden barcode! You can read it on its top and bottom edges - it's the same on both. Heck, it's even visible on this official store picture and we all overlooked it:

So I blew up the picture, rotated it horizontally, put a few monochrome filters onto it, and send it to an online barcode decoder for analysis. Turns out it's a Code 128 barcode which reads:

+thegame.com/shirt

I'm assuming you're supposed to concatenate "hacknslash" with "thegame.com/shirt" for the URL, which is unfortunately the same URL than one from a previous puzzle, which redirected to the store page to purchase the T-shirt in the first place, and it doesn't work now anyway. So in other words, it's a really cool discovery, which revealed absolutely nothing. Oh well. But the fact that we missed this for so long suggests that we might have overlooked other hints.

The other thing that I wanted to mention is that I continued looking into AES, specifically the multiplicative inverse in the finite field GF(2^8) transformation step in SubBytes() as it is the only non-linear transformation of AES. The official specifications mentions using the extended Euclidean algorithm to perform this, but I couldn't wrap my head around that concept.

Instead, after playing with equations for a little while, I came up with a nice way to find the multiplicative inverse, which is as follows: if abcdefgh is a non-null byte, then it's multiplicative inverse in GF(2^8) ABCDEFGH can be found by resolving the following set of equations (addition is the XOR operator, and multiplication is the AND operator):

It's possible to solve this system of linear equations using elimination of variables or Gaussian elimination, but doing so appears to cause formulas to blow up, so I haven't done so for the general case. I'm interested to see what the final solution looks like to see how strong is the nonlinearity of AES is, but I don't have a good way to do so right now.

In any case, with that it's possible in theory to remove all references to GF(2^8)'s polynomial representation for mathematical analysis and focus exclusively on pure bit formulas to represent the result of AES, which is pretty neat.

Share this post

Link to post

Share on other sites

I just realized there's another official Hack 'n' Slash merch in the Double Fine store in addition to the T-shirt. I completely forgot it existed because it's not showing up in the "All" category of the shop.