The challenge begins with a link to a password-protected ZIP archive (password: t216).
The archive contains 17 files - 11 e-mails and 6 attachments.
All files have the same date and time: 2016-09-06 18:18:20 . (6:18 PM? that's the Judgment Day time from "Terminator 3"!)
The puzzles can be solved in any order.
Tools used:
This is an USB capture. It was done on 2016-08-11 between 20:34:50 and 20:39:10 EEST.
There were two devices: Logitech UltraX keyboard (Y-BL49) and Logitech optical wheel mouse.
I export the data to a text file for further processing.
As stated in Wireshark Q&A, the actual data are
in the field "Leftover Capture Data" (8 bytes per field for keyboard; 4 bytes per field for mouse).
I hack together a decoder for keyboard data (see page #53 in this spec) and get this:
{Alt}+{Tab}Note the deleted words - "NEVER GONNA GIVE YOU UP". That's a song by Rick Astley.
NEVER{Backspace} {Backspace} {Backspace} {Backspace} {Backspace}
When it rains it puo{Backspace} {Backspace} ours:
GONNA{Backspace} {Backspace} {Backspace} {Backspace} {Backspace}
the guys investigating the incident are now convinced that someone has installed physical keyloggers to our systems...
GIVE{Backspace} {Backspace} {Backspace} {Backspace}
Many seem to think AAA is behind this but I can't believe even Astley could stoop so low.
YOU{Backspace} {Backspace} {Backspace}
Anyway, the new instt{Backspace} ructions are to use the on-screen keyboard for typing all the sensitive stuff.
UP{Backspace} {Backspace}
Here's the new master key:{Enter} {Enter}
{Win}+r{Enter}
{Alt}+{Tab} {Alt}+{Tab} {Alt}+{Tab} {Alt}+{Tab} {Alt}+{Tab} {Alt}+{Tab} {Alt}+{Tab} {Alt}+{Tab} {Alt}+{Tab}
So the task is probably to get the master key. But it was entered using virtual keyboard! (Note the WIN+R combo - it was probably used to launch osk.exe)
I add code for decoding mouse movements/clicks (see this page for more info) and plotting them (tiny dots for mouse movements, squares for clicks). The result:
Oh, what a mess! Nevertheless, clicks on the virtual keys are visible.
I modify the code:
Wait... what is that {Enter} keypress doing here???
This is wrong.
Needless to say, the site does not accept my answer.
I carefully re-analyze the keypresses on the real USB keyboard and notice that dot and colon are on the same key.
This isn't the case for US keyboard layout.
Which keyboard layout has . and : on the same key? German!
Oh, and Gruber is German surname!
I decode the virtual keypresses again using German keyboard layout and get this:
Now there's no {Enter} key, the password makes sense and is of flag<text> format! Hurray!
However, the site still does not accept my answer!!
So it's almost right, but not quite. The layout isn't German, but it's similar - in particular, there's a key with "less than" and "greater than" signs next to the Shift key, and there are 12 keys between CAPS LOCK and ENTER.
I find this site with keyboard layouts and start to examine them. Finally, I get to the Finnish layout and...
Compare the password ("We're no strangers to USB PCAP love") with the first line from "Never Gonna Give You Up" ("We're no strangers to love").
Obviously the task is to find the password.
But it contains 35 characters! That's a bit too much to bruteforce.
I need to reduce the search space first by applying rules from mail0007-attachment.bin.
Some observations:
So I take all this info and write a program. (See Appendix B for the code.) The program applies rules and outputs results.
The first results: char #18 is 'w'. So 0ddd is 0111. I add this to the program and re-run.
Char #14 is 'p'. Now look at char #29 (or #32). It's 0x22 or 0x44. So 0bbb = 2 or 4. But if 0bbb = 2, there are more than two magic chars! So 0bbb = 4. Add this to the program, re-run.
Char #11 is 't', char #29/#32 is 'D'. Now look at char #13 and note that 0ccc is either 3, 5 or 6. Now look at char #9. Only 2 bits set, and both belong to 0ccc, so eeee is 0000. This also means that 0ccc can't be 6, because then char #9 will be 0x60 (magic char > 0x3F). So 0ccc = 3 or 5, i.e. 0ccc = 0cc1. Add, re-run.
Now look at char #6. It has 0aaa in both nibbles. That means it has 4 bits set (not 5), and 0aaa has 2 bits set, so 0aaa is 3, 5 or 6. Add, re-run.
Umm... now what? 0cc1 = 3 or 5, and 0aaa = 3, 5 or 6.
Now all five digits have been found! Good. And yet there are more than 6.5 sextillion variants. How to proceed?
I recall that "All flags are of the format of flag<text>". The answer to USB PCAP task was in this format.
Maybe this password, too, begins with "flag<" and ends with ">"?
(This also would explain why chars #1..#5 and #35 weren't mentioned in equality rules - that would be a giveaway!)
I add this info to program. And 01aa, hhhh and 0ggg are now known, so I add them too. Re-run.
Now there are only 21168000 variants left. Cool! This can be bruteforced.
Without SHA256 check there are 1050 passwords.
With SHA256 check added there's only one password:
Compare the password ("You know the password rules and so do I") with the second line from "Never Gonna Give You Up" ("You know the rules and so do I").
mail0009-attachment.bin is a text file containing data for 9395 rides. Each ride is identified by the driver # (there are 123 drivers), starting point and ending point.
I write code to plot all points in one picture:
Wait, what's that?!
I add code to generate separate pictures for each driver. I view each picture.
And this is picture #88:
Looks like "FLAG..."! However, the text isn't completely legible,
so I add code to generate an SVG picture (with a line for each ride):
That's Rick Astley's birthday!
mail0004-attachment.bin is an audio file in RIFF WAVE format (72000 Hz, mono, 2082884 samples, 16 bits per sample, 2082884 / 72000 ≈ 28.93 seconds).
First of all, why 72000 Hz? Probably because the highest frequency in the recorded signal was 36000 Hz (see Nyquist-Shannon theorem).
I try to denoise the signal in Audacity, but to no avail.
I do some googling on infrared transmission and find this page with pictures and explanations. So now I know how clean IR signal looks like. It's square.
OK, time to create some pictures from sample data.
First, let's simply plot each sample (this is a fragment):
What if I plot the absolute difference between two neighboring samples?
Interesting. Now what if I choose some cutoff value? If Y is less than cutoff value, set it to 0; else, set it to maximum.
Looks square enough! How wide are those pulses? About 60 pixels, or 60/72000 = 0.83 microseconds. (Some are twice that width.) And data are transmitted in packets, each packet is about 1700..1800 pixels wide, or roughly 25 microseconds. But I still don't know the protocol.
I add code to remove stray dots, to locate all packets and to generate separate picture for each packet.
It turns out that there are 210 packets. I notice that pictures #0, #1 and #2 are very similar. I check other pictures. Yes, each packet is transmitted three times. I google for infrared protocol "three times" and find this page with the phrase "The Sony and RC5/6 protocols specify that messages must be sent three times". So there are, in fact, 70 packets.
I then google for "RC5 protocol" and find this page. I compare my pictures with the description - yes, looks like I have to decode RC5 packets.
So I write a decoder for Manchester coding, decode 70 packets and get 70 commands. But I have no idea what they mean, so I simply print them as digits:
And then I realize that each pair of digits is a code for an ASCII char! I decode the characters and get the following string:
So I have source code for the taximeter emulator - but not for the firmware (j1.bin)!
Apparently, the task is to reverse-engineer the firmware to get the flag.
I google some strings from the emulator source ("dstack", "rstack", "dsp", "rsp") and find info on
J1 Forth CPU.
I take the emulator code and turn it into a disassembler.
I try to find some strings in the firmware, but there's only *SAESTROI NAFLIDE* (i.e. "*ASSERTION FAILED*"). But there are ports for writing data to 7-segment indicators. So I look for addresses of these ports (0x5010 and others) in the disassembled code and find routines that output data to indicators. Then I find some other code that pushes constants to stack and calls the aforementioned routines to display data. I spend some time converting these constants into normal digits and letters. Yes, there are strings - "StAt UnSUPP", "dAtE", "brIdGE", "AIrPrt", "tUnnEL", "rESEt", "GEt tHE JACPot" (OMG! the taximeter is also a CASINO!)... and then 'fLAG' (in the routine that starts at 0x083F)!
The routine at 0x083F takes data from 0x0016..0x0019 and writes to indicator. So I look for code that writes data to these variables and find routine at 0x081F. After some trial and error I recreate this code in C and get the flag:
The tune played when the casino starts is a refrain from "Never Gonna Give You Up".
mail0005-attachment.bin is a ZIP archive.
It contains two files (both are dated 2016-06-28):
SCHEMATIC.PNG - picture of a circuit diagram;
NETLIST.NAND - text file (a netlist).
Citing Wikipedia: "In electronic design, a netlist is a description of the connectivity of an electronic circuit." There's a "custom ASIC" in the diagram.
Note the names of its inputs/outputs (ROWx/COLx/OPEN/RESET) - they appear in the netlist, so the netlist describes that custom ASIC.
The whole ASIC is built from NAND gates. Logically, NAND can be expressed as follows: dst = !(src_A & src_B).
Also note that Vcc, OSC1, OSC2 and GND are not mentioned in the netlist. Instead there's a CLK (a clock signal).
I want to visualize the scheme somehow, so I write a program that generates an SVG file from the netlist.
Note the structures in lower part of the scheme. E.g. look at four NAND gates 236, 237, 238 and 239.
They make a gated D-latch, or D-flip-flop.
Each D-flip-flop has two inputs (one for data, one for clock - which enables/disables data input) and one output.
(Well, actually TWO outputs, but the second one is never used in this scheme.)
There are 16 D-flip-flops. And they are organized in pairs: two D-flip-flops make one master-slave flip-flop.
So there are 8 master-slave flip-flops. Each master-slave flip-flop stores 1 bit of information - the scheme contains whopping 8 bits of memory!
Data input from gate # | Gates that make the flip-flop | Data output |
---|---|---|
42 | 236, 237, 238, 239, 240, 241, 242, OPEN | OPEN |
171 | 229, 230, 231, 232, 233, 234, 235, 24 | 24 |
155 | 222, 223, 224, 225, 226, 227, 228, 25 | 25 |
133 | 215, 216, 217, 218, 219, 220, 221, 33 | 33 |
101 | 208, 209, 210, 211, 212, 213, 214, 32 | 32 |
186 | 201, 202, 203, 204, 205, 206, 207, 4 | 4 |
179 | 194, 195, 196, 197, 198, 199, 200, 3 | 3 |
18 | 187, 188, 189, 190, 191, 192, 193, 20 | 20 |
CLK is needed only for the flip-flops. All this means I can abstract these flip-flops away and treat them simply as 1-bit registers (boolean variables)!
I write a simple program that can create an expression for calculating output of any gate.
Recall that the NAND gate can be expressed as dst = !(src_A and src_B).
To calculate an expression for dst I simply substitute src_A and src_B with recursively calculated expressions.
The recursion stops at registers and scheme inputs (i.e. they aren't substituted).
Using this program I create expressions for all outputs (ROWs) and all gates that write data to the registers:
ROW0 = !(!(!(REG3) & !(REG4)));
ROW1 = !(!(REG3 & !(REG4)));
ROW2 = !(!(!(REG3) & REG4));
ROW3 = !(!(REG3 & REG4));
OPEN = GATE42 = !(!(!(!(!(!(!(!(!(!(REG32 & REG33)) & !(!(REG3 & !(REG4))))) & !(RESET))) & !(!(!(!(COL2 & !(COL1))) & !(!(REG24 & REG25)))))) & !(!(!(!(!(!(COL2) & !(COL1))) & !(COL0)) & !(REG20)))));
REG24 = GATE171 = !(!(!(!(!(!(!(!(!(!(!(!(!(!(!(REG24) & REG25)) & !(!(!(COL2) & !(COL1))))) & !(!(!(!(REG32 & REG33)) & ROW1))) & !(!(!(!(!(!(COL2) & COL1)) & !(!(REG24 & !(REG25))))) & !(!(!(!(REG32 & REG33)) & ROW1))))) & !(!(!(!(!(!(!(REG32 & !(REG33))) & ROW0)) & !(!(!(!(REG24 & REG25)) & !(!(!(COL2) & !(COL1)))))) & !(!(!(!(!(REG32 & !(REG33))) & ROW2)) & !(!(!(!(REG24 & !(REG25))) & !(!(!(COL2) & !(COL1)))))))))) & !(!(!(!(!(!(!(REG24 & !(REG25))) & !(!(!(!(!(COL2) & !(COL1))) & ROW0)))) & !(REG32)) & !(!(!(!(!(!(!(!(REG32) & REG33)) & ROW0)) & !(!(!(!(COL2 & !(COL1))) & !(!(REG24 & REG25))))) & !(!(!(!(!(!(REG32) & !(REG33))) & !(!(REG24 & REG25)))) & !(!(!(!(!(COL2) & !(COL1))) & ROW0)))))))) & !(!(!(!(!(!(COL2) & !(COL1))) & !(COL0)) & !(REG20)))) & !(!(!(!(!(!(COL2) & !(COL1))) & !(COL0)) & !(REG20)) & REG24)) & !(RESET)));
REG25 = GATE155 = !(!(!(!(!(!(!(!(!(!(!(!(!(!(!(REG24) & REG25)) & !(!(!(REG32) & !(REG33))))) & !(!(!(!(!(COL2) & COL1)) & ROW2))) & !(!(!(!(!(!(REG32) & REG33)) & ROW2)) & !(!(!(!(!(REG24) & REG25)) & !(!(!(COL2) & !(COL1)))))))) & !(!(!(!(!(!(REG24) & !(REG25))) & !(!(REG32 & REG33)))) & !(!(!(!(!(COL2) & !(COL1))) & ROW2))))) & !(!(!(!(!(!(!(!(!(!(COL2) & COL1)) & !(!(REG24 & !(REG25))))) & !(!(!(!(REG32 & REG33)) & ROW1))) & !(!(!(!(!(REG32 & !(REG33))) & ROW0)) & !(!(!(!(!(COL2) & !(COL1))) & REG25))))) & !(!(!(!(!(!(!(!(REG32) & REG33)) & ROW0)) & !(!(!(!(COL2 & !(COL1))) & !(!(REG24 & REG25))))) & !(!(!(!(!(!(REG32) & !(REG33))) & !(!(REG24 & REG25)))) & !(!(!(!(!(COL2) & !(COL1))) & ROW0)))))))) & !(!(!(!(!(!(COL2) & !(COL1))) & !(COL0)) & !(REG20)))) & !(!(!(!(!(!(COL2) & !(COL1))) & !(COL0)) & !(REG20)) & REG25)) & !(RESET)));
REG33 = GATE133 = !(!(!(!(!(!(!(!(!(!(!(!(!(!(COL2 & !(COL1))) & !(REG24))) & !(!(!(!(REG32 & !(REG33))) & ROW2))) & !(!(!(!(!(REG32 & !(REG33))) & ROW2)) & !(!(!(!(REG24 & !(REG25))) & !(!(!(COL2) & !(COL1)))))))) & !(!(!(!(!(!(!(!(REG24) & !(REG25))) & !(!(!(REG32) & REG33)))) & !(!(!(!(!(COL2) & !(COL1))) & ROW0))) & !(!(!(!(!(!(REG32) & REG33)) & ROW2)) & !(!(!(!(!(REG24) & REG25)) & !(!(!(COL2) & !(COL1)))))))))) & !(!(!(!(!(!(!(!(!(REG24 & !(REG25))) & !(!(!(REG32) & REG33)))) & !(!(!(!(!(COL2) & !(COL1))) & ROW0))) & !(!(!(!(!(!(REG32) & REG33)) & ROW0)) & !(!(!(!(COL2 & !(COL1))) & !(!(REG24 & REG25))))))) & !(!(!(!(!(REG32 & !(REG33))) & ROW0)) & !(!(!(!(!(COL2) & !(COL1))) & REG25)))))) & !(!(!(!(!(!(COL2) & !(COL1))) & !(COL0)) & !(REG20)))) & !(!(!(!(!(!(COL2) & !(COL1))) & !(COL0)) & !(REG20)) & REG33)) & !(RESET)));
REG32 = GATE101 = !(!(!(!(!(!(!(!(!(!(!(!(!(!(!(REG24) & !(REG25))) & !(!(!(REG32) & REG33)))) & !(!(!(!(!(COL2) & !(COL1))) & ROW0))) & !(!(!(!(!(!(REG32) & REG33)) & ROW2)) & !(!(!(!(!(REG24) & REG25)) & !(!(!(COL2) & !(COL1)))))))) & !(!(!(!(!(!(!(!(REG24) & !(REG25))) & !(!(!(REG32) & !(REG33))))) & !(!(!(!(!(COL2) & COL1)) & ROW2))) & !(!(!(!(!(!(REG24) & REG25)) & !(!(!(REG32) & !(REG33))))) & !(!(!(!(!(COL2) & COL1)) & ROW2))))))) & !(!(!(!(!(!(!(REG24 & !(REG25))) & !(!(!(!(!(COL2) & !(COL1))) & ROW0)))) & !(REG32)) & !(!(!(!(!(!(!(!(REG32) & REG33)) & ROW0)) & !(!(!(!(COL2 & !(COL1))) & !(!(REG24 & REG25))))) & !(!(!(!(!(!(REG32) & !(REG33))) & !(!(REG24 & REG25)))) & !(!(!(!(!(COL2) & !(COL1))) & ROW0)))))))) & !(!(!(!(!(!(COL2) & !(COL1))) & !(COL0)) & !(REG20)))) & !(!(!(!(!(!(COL2) & !(COL1))) & !(COL0)) & !(REG20)) & REG32)) & !(RESET)));
REG4 = GATE186 = !(!(!(!(!(!(!(!(REG3 & !(REG4))) & !(REG20))) & !(!(!(!(!(COL2) & !(COL1))) & !(COL0)))) & !(!(!(!(REG20) & !(REG3)) & !(!(!(!(!(COL2) & !(COL1))) & !(COL0)))) & REG4)) & !(RESET)));
REG3 = GATE179 = !(!(!(!(!(!(!(!(!(COL2) & !(COL1))) & !(COL0)) & !(REG3)) & !(RESET))) & !(!(!(REG20) & !(REG3)) & !(!(!(!(!(COL2) & !(COL1))) & !(COL0))))));
REG20 = GATE18 = !(!(!(!(!(!(COL2) & !(COL1))) & !(COL0)) & !(RESET)));
Some observations:
Now I write code to generate truth tables for some of these expressions. If the function contains N arguments, the truth table will contain 2^N lines. However, there's no need to read the whole table - grep can be used to find interesting lines!
Analysis of REG4_REG3 pair truth table gives the following:
So REG4_REG3 pair is simply a 2-bit counter that switches ROWs via binary decoder. This is necessary for keyboard matrix to work.
Now let's look at truth table for OPEN. It has 9 arguments (not counting RESET - it's assumed to be 0), so it contains 512 lines. But only lines with res=1 are interesting. And there are only TWO such lines:
truth-open.exe | grep res=1 COL=001 ROW1=1 REG20=0 REG24_REG25_REG32_REG33=1111 res=1 COL=101 ROW1=1 REG20=0 REG24_REG25_REG32_REG33=1111 res=1So the door will open when REG24, REG25, REG32 and REG33 are all 1 _and_ the last pressed digit is '6'. The last digit of the PIN code is '6'!
Initially registers REG24..REG33 contain zeros. How to get from 0000 to 1111? By entering the correct PIN code, of course!
truth-4regs.exe | grep OLD=0000 | grep -v NEW=0000 OLD=0000 REG20=0 COL=010 ROW=001 NEW=0001 Buttons=8 OLD=0000 REG20=0 COL=110 ROW=001 NEW=0001 Buttons=78So the first digit of the PIN code must be '8'.
truth-4regs.exe | grep OLD=0001 | grep -v NEW=0001 | grep -v NEW=0000 OLD=0001 REG20=0 COL=001 ROW=001 NEW=0010 Buttons=9 OLD=0001 REG20=0 COL=101 ROW=001 NEW=0010 Buttons=79The second digit is '9'. Note: I specify grep -v NEW=0000 because the scheme will write 0 to all 4 registers if the wrong digit has been entered, and I want to exclude such cases.
And so on...
OLD=0010 REG20=0 COL=100 ROW=100 NEW=0011 Buttons=1 OLD=0011 REG20=0 COL=100 ROW=001 NEW=0100 Buttons=7 OLD=0100 REG20=0 COL=010 ROW=001 NEW=0101 Buttons=8 OLD=0100 REG20=0 COL=110 ROW=001 NEW=0101 Buttons=78 OLD=0101 REG20=0 COL=100 ROW=100 NEW=0110 Buttons=1 OLD=0101 REG20=0 COL=001 ROW=001 NEW=0010 Buttons=9 WOW! OLD=0101 REG20=0 COL=101 ROW=001 NEW=0010 Buttons=79 WHAT'S THAT? OLD=0110 REG20=0 COL=100 ROW=001 NEW=0111 Buttons=7 OLD=0111 REG20=0 COL=100 ROW=010 NEW=1000 Buttons=4 OLD=1000 REG20=0 COL=100 ROW=100 NEW=1001 Buttons=1 OLD=1001 REG20=0 COL=100 ROW=001 NEW=1010 Buttons=7 OLD=1010 REG20=0 COL=100 ROW=100 NEW=1011 Buttons=1 OLD=1011 REG20=0 COL=010 ROW=010 NEW=1100 Buttons=5 OLD=1011 REG20=0 COL=110 ROW=010 NEW=1100 Buttons=45 OLD=1100 REG20=0 COL=100 ROW=100 NEW=1101 Buttons=1 OLD=1101 REG20=0 COL=100 ROW=100 NEW=1110 Buttons=1 OLD=1110 REG20=0 COL=001 ROW=100 NEW=1111 Buttons=3 OLD=1110 REG20=0 COL=101 ROW=100 NEW=1111 Buttons=13So the PIN code is '891781741715113'... oh, and recall that the last digit must be '6'. Also note that there's a surprise - we may repeat first four digits as many times as we want before entering the rest! For example, '8917891789178917817417151136'.
truth-4regs.c: