The challenge description says that they used Rubinius 2.5.2 to compile the
Ruby files to bytecode. However, we were unable to get hold of a version of rbx
(the Rubinius interpreter) that was actually able to load the files as is. We
therefore
patched the version and signature checks out of the loader
to make it work.
This
is the version of Rubinius that we used.
Now we can actually run the program:
[*] Greetings to all of the eiltes here [*]
--- I heard you guys all hate ruby, so I have decided to be nice---
--- enjoy this great bytecode instead! ---
So, you can tell me now... what's the flag? foo
nop
Okay, let’s see what we can figure out just by loading the two classes and
entering a nice shell:
When run:
So apparently Cipher implements some kind of monoalphabetic
substitution. We still don’t know what permutation of characters the program
uses to instantiate the cipher though.
At this point we decided to write a simple bytecode
disassembler to figure out the basics of
how the script works. Here is the relevant part of the trie_harder.rbc
script:
The VM is a stack machine. We can see that the script calls
Marshal.load(File.read("trie.dump")) to deserialize an object from the
trie.dump file. Let’s try to do the same thing and inspect the result:
As expected, the object represents a trie
with values associated with the leafs. It is organized as ternary tree, where the
children are ordered by character (thanks to the anonymous commenter who
pointed this out). Our best guess is that our input is
used to look up a leaf in the trie and the value of that leaf is printed out.
Let’s write some code to read the keys and values of the trie, using
depth-first traversal:
Output:
6A01IvFSCF3IWFvIIDC => not the flag but a true statement ;)
D8wM9VHFciF9CHCFaabyF01cVFyHMvqFC688X => wow not this either
WDTFcqFHMqAF2FW8MX => 1 c4n r34d th15 ju57 l1k3 x86 0r 3n6L15h!
WDwM6WpVpvcA => this isnt the flag
XcXAp9FWMXMW8FAp9WFW9DA => nope lots of fake flags
XcXFAp9F0Wc8FDHcveFypMWF288i => good boy this is the flag
XcXFAp9F1MTXFciFA80 => stop being such a n00b and get the flag
XcXFAp9FgvpzF0Wc8XFvpiFD8cvGFKFvppD => lole n1c3 try
yM0qFSVFyAF18Wp => neither is this
This looks promising. It seems like the trie uses a Cipher to encrypt the
keys before lookup/insertion. There’s at least two ways to extract the
permutation used for the cipher:
Create a new trie and insert the value abcd...xyzABC...XYZ012..89, check
out the resulting trie
Use the disassembly to extract the constructor argument for Cipher
We went with option 2:
We can easily extract the permutation from here and decode the
keys:
Output:
python is for noobs => not the flag but a true statement ;)
b3c4u5e 17 uses llvm th15 me4nz sp33d => wow not this either
rbx 1z e4zy 2 r34d => 1 c4n r34d th15 ju57 l1k3 x86 0r 3n6L15h!
rbc4pr050n1y => this isnt the flag
d1dy0u r4d4r3 y0ur ruby => nope lots of fake flags
d1d y0u tr13 be1ng m04r 2337 => good boy this is the flag
d1d y0u h4xd 17 y3t => stop being such a n00b and get the flag
d1d y0u kn0w tr13d n07 b31n6 a n00b => lole n1c3 try
m4tz i5 my h3r0 => neither is this
And indeed, d1d y0u tr13 be1ng m04r 2337 is the flag!