With another qualifier done, definitely much more fun than the last one, I believe there were some nice basic low level challenges as well as some difficult challenges which I want to share their solution so no other person has to suffer the same way I did.


Mousesack’s Spell(150)

Mousesack, the mighty druid of Cintra was summoned to cast a powerful spell against Nilfgard troops. Apparently, mathematics and witchcraft are more related than you think. A special type of spells called Discrete Logarithmus is sealed by Elliptic Curve mathematics. Help Mousesack solve this Elliptic Curve seal in order to cast his spell!

This challenge was to solve an elliptic curve by finding the discreet logs of the two points given, so while doing some research I found out the equations needed and coming up with this code in sagemath:

p = 2410094710780013 #Modulus
E = EllipticCurve(GF(p),[0,7])
r = E.cardinality()
P = E() <- point one, copy when website opens
Q = E() <- point two
n = P.discrete_log(q)

Which in detail, we declare a variable which is going to be the modulus, then we generate a elliptic curve with the modulus and specify the parameters of 0 and 7.

This is due to the formula being

y^2 = x^3 + a * x + b

And since we have the formula of “y² = x³+7” we replace it with a = 0 and b = 7

We set two points in the curve and find the discrete of those two points resulting in n.

Then we find the md5 of the n and boom, we have the flag.

Routes from Sodden to Amell (450)

There are many common routes in the Sodden Kingdom that lead to the Amell mountains. A lot of encryption keys were intercepted in those routes over time, along with an encrypted ciphertext. You are tasked with decrypting the secret message. Apart from the routes, maybe these keys have something else in common…?

This was a tough one, although you could solve it within some minutes using RsaCtfTools, I did the hard and noble way.

We are given several keys and a ciphertext. Normally, if they were different public keys we would need to have some luck in order to find the correct prime factors. However, this is not the case as we have the same exponential(e) indicating that most likely the keys were made by the same user.

This is good, as we know that to solve this we can find the greatest common divider between key pairs resulting to p. Then easily find q by having q = n/p and finding d.

If you want to read more about this you can see here. However, here is my code on solving this challenge using SageMath.

(I removed the keys because otherwise it would be too long, you can find full code here)

from Crypto.Util.number import long_to_bytes, bytes_to_long
ct = 6140004927221288708158116240514085214969244287499689950607411687643765968888073594297230472143004146662264388473794641486304929751613265751665570248005167869572584779545079745775062338340629699669660227799131054504322471061157204111877809206276019141658128153020613392871728956055647061005155586831000894817173015428829171992855011271290929676085425337205413441866389617555857996641860866456919690533934844683031201725332111553231565580108156866434502557021993299816767620552948912718661194183489867226719943254856181689258435351059269728239525694750330053829753591673987028413341212605403204876979527712442204243951
keys = [[n,e],[n,e]]
# Returns LCM of arr[0..n-1]
g = 0
for i in range(0,len(l)): #we test all pairs through nested loops
for z in range(0,len(l)):
gcde=gcd(l[i][0],l[z][0]) #find the gcd of the pair
if gcde > 1: #if it exists
q = l[i][0] / gcde #find q
phi = (gcde - 1)*(q - 1)
bezout = xgcd(l[i][1], phi);
d = Integer(mod(bezout[1], phi)) #find d
m = power_mod(ct, d, l[i][0]) #decrypt
tm = long_to_bytes(m)#turn it into text

However, if you are reading this trying to solve another but similar challenge with a very large amount of keys.It is impossible to find the gcd for all the pairs,but you can read this paper here on solving it.


Abnormal Communications (150):

The file in “https://drive.google.com/open?id=1PN4Ztmfkw1eX03g-IwL5oLK52O_uDySH" is gathered from an abnormal communication. Can you detect the flag inside?

This challenge was unique and interesting as it hides data in an abnormal way.

Opening the file, we can’t see rather than the classic red herring traffic. I check if there are any http objects and they are plenty of them as you can see below.

HTTP objects found

Although when I investigated the zip file, I found some CV templates inside. Checking their contents and strings I realised I was wasting my time (although I would rate the templates themselves as 6/10). I kept digging until I saw the following packets:

3 RST packets in such a short time? That is abnormal.I soon realised that this wasn’t the only case and went to export information and filtered by rst packets and I found multiple in a row. Although I didn’ find the last part during the ctf, you can correlate the ports of each new connection to ascii characters, which according to the picture below,

the new ports translate to:

067 067 083 067 123 077 051 036 036 064 103 051 125 010 => CCSC{M3$$@g3}

and voilà we have the flag!

Royal Huntsman’s Dump (190)

During a hunt, Gerald and Ciri found traces that a Striga had infiltrated the royal huntsman’s camp. Strigas’ tactics are brutal and gruesome so it is truly hard to be dumped from ones memories. Hurry! Help them find where it is hidden and kill it to avoid any grisly deaths! Download the traces from https://drive.google.com/open?id=1k-z8onAb4akdJpWV9F_K2EQCW5nCN_sq and and start hunting! Can you find the flag?

Right, so malware analysis is my jam. Since the challenge was low in points I did not expect any good obsufication/hiding technique so I cut corners when it comes to my malware identification process.

I began with a basic psxview scan to see if any process stands out in its attempt to hide. Nothing came up as too odd so I move on with a cmdscan in hopes that the malware was really basic.

And I was right! This line is obviously the command used by the malware.

To break it down:
“rundll32.exe” is a windows file, however it can be easily replaced in order to act as a backdoor and execute other dll files in the system on boot. Such as update.dll in this case.

“shell32.dll” opens a shell, although usually you don’t do this unless you want to bait a forensics investigator for various reasons, caused the command to show up in the cmdscan.

“update.dll” is the malicious dll file causing the following command to execute. It’s obvious as windows dlls never are in the downloads folder and the fact it’s executed by a backdoor. Normally, you would try to extract update.dll for further analysis but I did not expect anything hidden there as the flag is in the next command anyway.

So the dll tries to get the contents of the ip address, which if we follow we get the flag. Easy.

For the second part of the question:

Can you identify the name of the malicious file used by Striga?

The answer is “update.dll” as its the ‘malicious’ file.



Will this treasure be worth it though?

So an apk RE chall? Bring it on.

We start by decompiling the apk, checking the strings which show nothing interesting and then moving on to the main java file.

We can see a lot of code at first glance, so lets take it step by step:

We begin with the onCreate function. The program upon launching will do initialize some content views and proceed by creating an onClick listener.

Which means that when somebody taps the screen the app will do a http request to “” and it appends the final hex to it. I assume it will give us the flag if the hex correct is given.

We continue with the two following if statements:

The first and second if selection checks whether your device has either supersu or busybox on as it checks various paths which their files of su and busybox are usually stored.

private String[] binaryPaths = {"/data/local/", "/data/local/bin/", "/data/local/xbin/", "/sbin/", "/su/bin/", "/system/bin/", "/system/bin/.ext/", "/system/bin/failsafe/", "/system/sd/xbin/", "/system/usr/we-need-root/", "/system/xbin/", "/system/app/Superuser.apk", "/cache", "/data", "/dev"};private boolean checkForBinary2(String filename) {
for (String path : this.binaryPaths) {
if (new File(path, filename).exists()) {
return true;

Then, it opens up an internal db, fills it with some random stuff, closes it and commits.

SharedPreferences.Editor b = getApplicationContext().getSharedPreferences("MyPref", 0).edit();
b.putBoolean("key_name", true);
b.putString("secret", "e86c1268-08e4-4529-8758-4589bde0e854");
b.putInt("num", 124);

So you would think “How do I get to the second selection if the first one is always chosen?”.

Well, its simple, you are supposed to root the device and install supersu,modify the apk into having the first condition to be always false and then wait for the flag. But all of this takes too much time and its not necessary, at all.

I usually go with the motto of “Complying to the apks/executables demands is showing weakness”. So you can solve it under 10 minutes with these simple steps:

  • Decompile the apk into a java fail
  • Copy the main function which gets the “MyPref” db filled up.
  • Open up androidstudio on a blank form and paste this code:
  • Send off the hex value to the website and profit!


I have to say it was a fun little ctf. Although I should have spent a bit more time than 4 nights when the ctf is 2 weeks but it was still a fun, beginner to medium defficulity ctf. Kudos to the authos.

Malware Researcher,coder and Musician