So this is the way blogging looks like… :)


If you are watching Gynvael’s EN streams, you know what it’s all about. After every stream, Gynvael is publishing one mission aka easy CTF task. And here is my “writeup-wannabe” for challenge number 009. Honestly that was the hardest challenge made by Gynvael. I love it, and wait for more!


Welcome back agent 1336.
No mail.
> mission --take
MISSION 009                  DIFFICULTY: ████████░░ [8╱10]

We've received an encrypted message from one of the agents-in-training. While we
are always happy with good use of modern cryptograpical protocols, it turned out
the agents-in-training didn't use one. They said it was too hard and they didn't
understand the settings and options.

"So what did you use" we asked and got the answer "oh, it was a OTP XOR". Hmm,
that's actually not so bad. We looked at the message again:

   9a 60 76 14 8b 36 5a 10 2b 91 c4 6c ab 27 92 99 f8 6a ec 5d 32 20 3d 61 8f
   c7 fb dd 02 72 bf

And then we asked for the key.

"Oh? OTP requires us to store the key? We... we didn't know that."

There we go again.

Trying to be patient we asked "what do you have" and we've received an object
file with with the cipher implementation, but without the original message (we've
been told it has been removed for security reasons, of course).

Well, it looks like a hopeless case, but take a look anyway - maybe you can
decipher the message. Here's the binary:

GOOD LUCK!                                                  Wednesday, 2017-07-19

If you decode the answer, put it in the comments under this video! If you write
a blogpost / post your solution online, please add a link in the comments too!

P.S. I'll show/explain the solution on the stream next week.

0x01 Let’s go to work!

XOR schould never be considered as an encryption… Unless it’s OTP. There is a lot of usefull infromation about OTP, if you need small introduction please start with click and follow links in foot section of this article. The most important thing to remember is that without key, we cannot retrive this encrypted message. As I mentioned previously in my other Gynvael’s wirteups, XOR has reallu fun feature: given E - encrypted message, M - message, K - key: K^M=E but also E^K=M and E^M=K

But fortunately under this link there is a binary file with cipher implementation.

Basic intel

I will use radare2 setup for analysis.

mv 15579e83de01a559ddcf39104c20902bb2f94c53_mission009 mission009 for easier typing

Open file with:

radare2 mission009.o

and i command will give basic information (like rabin2 -I mission009.o)

This is an output:


Good for us, no anti-RE stuff was used, no encryption but binary is stripped. This is a *.o file, you can read about them here Object files.

"An object file is a file containing object code, meaning relocatable format machine code that is usually not directly executable. There are various formats for object files, and the same object code can be packaged in different object files. An object file may also work like a shared library."

strings command will also give us some usefull information:


Some readable strings:

|$ H

And what’s more important some tip:


Someone used some randmoization…

It’s time to look inside that code!

0x02 Getting hands dirty

Let’s see what do we have inside. There is a lot of tools to do this, but due to my long struggle to master radare2, it will be my toolchain of choice. This dump was created by set of commands: ([0xXXXXXXXX] is the prompt)

[0x00000164]> aaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze len bytes of instructions for references (aar)
[x] Analyze function calls (aac)
[ ] [*] Use -AA or aaaa to perform additional experimental analysis.
[x] Constructing a function name for fcn.* and sym.func.* functions (aan))

[0x00000164]> afl
0x00000164    5 323          entry0

[0x00000164]> pdf @ entry0 // this prints the disassembly of fumction referenced by entry but I prefer to have it visual...

[0x00000164]> VV @ entry0

//And this is done by some awsome trick and tool dot from package graphviz
:> (':' gives command prompt in Visual Mode) ag sym.main >
:> !!dot -Tpng -o graph.png // for more references I will provide link in last section of this post.


Ok, that’s a very huge image. But I like it! Fortunately I do not have to do PrtScr screen by screen in my terminal.

General analysis of first two blocks, gives us general view of key generation. In first block, some strings (shown by strings command) are stored in the memory, then funciton _time64 is callend and then function srand is called. Key generation (second block) depends on system timestamp, so the time since January 1, 1970. That’s a lot of values to check, but what came to my mind was that date in mission description.

 Wednesday, 2017-07-19

This was the only one time, when so called agents mentioned date in their message. This might be a lead (of course it could be misdirection too), but I’ve assumed, for security, probable timestamp would come from the interval (12 july 2017 21:00, 19 july 2017 21:00). So time period between Gynvael’s streams 25’s and 26’s, because for me this is the most probable time of origin of this mission.

So this block:


Shows the loop of key generation. The key is then xored with message and… that’s it, then there is this OTP message we are trying to decrypt.

Each call of rand() function, gives us another state od pseudo-random number generator. What’s important, pseudo-random function in C/C++ is not cryptographically secure, end with given seed (first, initialization value) we can reproduce value by value. In glibc (used by gcc) is called LCG. What’s more, when given enough of generated numbers, there is really easy way to retrive ALL random numbers in seqence and next values that will be generated in the future! But it’s not the topic of this task.

Be careful! As rabin2, or radare2 -i option told us, agents compiled their work on Windows, implementation of LCG is slightly different on Windows than in Linux.

The function flow goes this way:

string s = ""; //key-to-be variable
for (int i = 0; i<32; i++)
	foo = rand();
	bar = foo - (( foo * 2139127681) >>39) - (foo >> 31);
	s += char(bar);

32 is the lenght of encrypted message, so it’s the lenght of OTP too.

This values comes from converting them in radare2 from hex to dec.

2139127681 = 0x7f807f81
39 = 0x27
31 = 0x17

Ok, so no the task becomes to be easy… We have tu guess timestamp. How to do this? In probable-timestamp period for each timestamp we generate all 32 values and create probable key. After xoring with message if all given data is printable that migh be the key. Remove all garbish looking output and tadadadadam!

For timestamp = 1500483661

The message is…. Who needs to store keys anyway.

Full code is here

That’s all. Thank you for your attention. To the next time! /me bye!

More cracking stuff comming soon...