Table of Content
Intro
Arab Security Conference was held this year from 18th till 19th of September and as always the last day was for the cyber wargames competition where ten qualified teams compete with each other and solve sophisticated real life challenges in 6 different categories. In this write-up, we’re going to discuss and solve the 5 crypto challenges in the competition. The challenges was easy-medium and hard ones, but full of tricks for those who aren’t aware of the encryption algorithms and techniques. However, I’ll show you my thought process and how to divide the challenge into sub-challenges in order to attack the algorithm or find a solution.
:DISCLAIMER:
As usual, you’re not gonna find here all the tips and tricks in cryptography since I wanted to leave some space for you to think and ask. So, feel free to think more about the challenge before jumping right into the solution.
Challenge | Difficulty | Points |
---|---|---|
Randomeness | Warmup | 150 |
Double | Easy | 300 |
Log Out | Medium | 600 |
In Between | Medium | 600 |
Square Group | Hard | 900 |
Randomness
Description:
Random everywhere.
It’s a warmup challnege and pretty known attack, but need to be
aware of the algorithm used in the encryption, so let’s have a
look at the the files given
output.txt
text file and
randomness.py
a python script.
The text file contains around 63 hex encoded lines. Let’s look at the script:
The script contains one class called GDSA with
2 functions
sign
and
genPrime
. It first generate a radom prime with the
genPrime
function, then it computes some parameters \(A = g^x \pmod p\)
where \(x\) is a random 256-bit integer, \(g=7\) kind of a base,
and \(p\) is a random odd prime. It also calcultes a random
128-bit integer \(k\) and check the \(gcd(k, p-1) = 1\).
Next, the
genPrime
function calculates 464-bit odd prime and it seems that the
integers generated the prime are 50-bit long so it most probably
a weak prime. The next function
sign
,
it takes a plain message \(m\) and calculate \(r = g^k \pmod p\)
and \(sk = m-xr \pmod{p-1}\), then it returns \(r\) and \(s\).
Actually, the last thing this script does is, it signs the
plains
that maybe contains a list of plain texts and the
plain = b'I wanna look closer, but my glasses are
broken!'
looks one of them.
Solution
The challenge has 2 vulnerablilities the first one is the weak prime and you can use it to calculate the random variable \(k\) and the second one is using the same \(k\) to sign multiple messages. Apparantly, the easy way is that you can search about this knwon attack on El Gamal signature scheme.
\(r_i = g^k \pmod p\) \(s_ik = m_i - xr_i \pmod{p-1}\)
So, if you take 2 signatures signed with the same random key, you can get the message back \(s_1k = m_1 - xr_1 \pmod{p-1}\) and \(s_2k = m_2 - xr_1 \pmod{p-1}\), then by subtracting the eqautions will give you \(s_1k - s_2k = m_1 - m_2 \pmod{p-1}\). Finally, you have all the variables except one of the \(m_i\), but you can bruteforce the other message to get the flag.
- Using DLP to get the random key \(k = 45252513931146808668347235459955660091\).
- Bruteforce the list of signatures to get the flag.
Flag:
ASCWG{H17719_7H3_W0r1d_0f_W33k_Pr1m3_E1G4m@1_$16n47ur3s}
Double
Description:
I’ve tried to mess up with the scheme a little bit, but I think using 2 ciphertexts is secure enough.
This one was easy given a powershell
double.ps1
script and an
output.txt
files. Let’s look at the script first:
The function
_Gen
it first parse a redacted primes \(p\) and \(q\), then generates
a random integer \(e\) and checks the \(gcd(e, N) = 1\), then
generates integer pairs \(r_1\) and \(r_2\), and calculates
\(a_1 = e^{r_1(p-1)} \pmod{N}\), \(a_2 = e^{r_2(q-1)}
\pmod{N}\). Finally, returns \((a_1, a_2, N)\) as public key and
\((p, q, r_1, r_2)\) as private key.
Second function
Encrypt
it takes an integer \(m\) and generates a random pair \(s_1\)
and \(s_2\), then computes \(d_1 = a_1^{s_1} \pmod N\) and \(d_2
= a_2^{s_2} \pmod N\). After that, it calculate the ciphertexts
\(c_1 = md_1 \pmod N\) and \(c_2 = md_2 \pmod N\) and write out
the output into a text file
output.txt
.
Solution
From the
_Gen
function you can notice that \(a_1 = e^{r_1(p-1)} \pmod{N}\) and
if you can recall that
Fermat’s Little Theorem
that \(x^{p-1} = 1 \pmod p\) for any arbirary \(x\). So, if we
can use it to some how get one of the factors of \(N\).
First, we can reorder the equation in order to get it exactly what we need \(a_1 = e^{r_1(p-1)} = 1 \pmod p\) and vice versa for \(a_2\), that is \(a_1 - 1 = 0 \pmod p\) which means that this value can divide \(p\), so subtracting \(a_1\) by 1 it gives a multiple of \(p\), thus \(gcd(a_1 - 1, N)\) is one of the factors.
Second, you know that \(c_i = md_i \pmod N\) and \(d_i = a_i^{s_i} \pmod N\), then you can compute \(c_1 = m \pmod p\) and \(c_2 = m \pmod q\), since \(a_1 = 1 \pmod p\) and \(a_2 = 1 \pmod q\).
Finaly, \(m = c_1 \pmod p\) and \(m = c_2 \pmod q\) this is a linear modular equation that can be solved by CRT to get the flag.
Flag:
ASCWG{D3_F3rm47_7h3_l1ttl3_7H30rY_0f_R5A}
Log Out
Description:
A cryptographer has invented a protocol for authentication, but when he tried to test it out something went wrong.
This one needs a solid understanding of how modular
exponentiation work. Given a
log_out.py
script that has the following code.
The script has only one function
gen
that takes a prime \(p\) and generates a random 179-bit integer
\(e\) and an integer \(e_id_i = 1 \pmod{p-1}\). Then, the script
gets 2 painrs of (\(e_1,d_1\)) and \((e_2, d_2)\) and encrypts
the flag as
\(one = m^{e_1} \pmod p\) \(two = one^{e_2} \pmod p\) \(three = two^{d_1} \pmod p\)
Solution
Actually, if you take a closer look at the script it’s just implements 3 Pass Protocol and if you know how things can get wrong here you can easly break it. So, from here we know that it uses modualr exponentiation over prime field and if this prime was weak it can break the protocol and get the plain message. A weak prime is an odd prime that its euler totient can be factored as small prime numbers and you can check this one easly on your machine or Alpertron.
Now, we can use the defined equations to solve the DLP to get the random key \(e_2\), since \(two = one^{e_1} = m^{e_1e_2} \pmod p\), then use the third equation to get the flag.
\(three = one^{e_2d_1} = m^{e_1e_2d_1} = m^{e_2} \pmod p\) \(m = three^{d_2} = m^{e_2d_2} \pmod p\)
Flag:
ASCWG{3Pa$$$$_7h3_5h4m1r_l095_Pr070c0l}
In Between
Description:
I found some sort of smooth encryption scheme until I noticed what I’ve did wrong.
This one was an interesting challenge uses broken RSA, the
description has a big hint and you have to dig a little deeper
researching how this works. Given 3 files 2 encrypted ones and
between.py
a python script.
Looking at the script, it has 2 functions the first one
encrypt
that takes a plain text and a secret key and encryptes the text
with AES-CBC and the other
gen
function takes a prime \(p\) and generates another odd prime
based on it.
So, it generates a 512-bit prime \(p\) and another prime \(q =
2r_1pr_i + 1\), then continues to generate the public RSA key
\(e=65537\) and \(N=pq\). Next, it generates a secret 512-bit
prime and encrypts it using the generated public key. The last
step, it encrypts the flag image file using the generated secret
prime and write the encrypted secret and the encryped flag into
another files
flag.jpg,enc
and
secret.enc
respectively.
Solution
If you know exactly how RSA works, you would notice that \(gcd(pq, (p-1)(q-1)) \neq 1\) which violates the RSA scheme. Now, obviously \(q-1\) cannot divide \(p\) and so we must have \(p\) divides \(q-1\), therefore \(q-1 = kp\) for some integer \(k > 1\). Next, the description says that it uses a smooth encryption, so it has something to do with smooth numbers. Digging deeper about this you would find that RSA under some conditions has a weakness called powersmooth. A B-powersmooth number is a product of coprime numbers less than B.
Suppose that \(k\) is B-powersmooth for small B, then we have \(M = \alpha k\) and \(M\) is a product of prime powers up to \(B\). Thus, for any arbitrary number \(a^{MN} = a^{Mpq} = a^{\alpha (kp)q} = a^{(q-1)p} = 1\pmod q\), so we can get the one of the factor as \(gcd(a^{MN} - 1, N) = q\).
The attack constructed as follows:
- Find the correct \(B\).
- Get all the primes powers less that \(B\).
- Compute the \(gcd(a^{MN} - 1, N)\) and check if it’s one of the factors or not.
After almost 10 minutes you’ll get the correct factor with \(B=3e8\).
This attack works since the product of primes of a B-powersmooth number contains a subset that equal to a one of prime factors.
Flag:
ASCWG{P0wer1n9_7h3_$m00th_0f_W33k_R5A_$ch3m3}
Square Group
I’ve implemented this super secure algorithm can you break it?
Actually, this challenge is the hardest one and it uses a El
Gamal encryption using galois field. Given 2 files
output.txt
file and
square_group.py
a python sage script, let’s have a look.
Obviously, the script generates a 292-bit odd prime \(p\), then creates a finite field of size \(p^2\) with a group generator \(g\). After that, it calculates \(A = g^a\) and \(B = g^b\) in the galois group. A finite field \(F_{p^2}\) is a group of finite number of elements (of size \(p^2\)) and each element on the form \(F = {x+yg : x,y \in GF(p)}\) where \(g\) is the group generator.
Next, it converts the flag to an integer and generates a random 256-bit integer \(k\), then calculates \(c_1 = g^k\) and \(c_2 = mA^k\) also in the group which means addition, multiplication and subtraction operations are calculated inside the group \(F = GF(p^2)\).
Let’s have a look at the other file.
Solution
It’s obvious that the script uses El Gamal encryption scheme, but you would notice that the flag has 319 bits while the prime has only 292 bit, thus the encryption here is not correct and the decryption will get a compressed integere rather than the flag. Now, the attack goes as following:
- Try to get the values of \(a\) and \(b\) using DLP.
- Decrypt the corrupted flag, then bruteforcing to get the correct numbers of bits.
If you start searching about
DLP
over finite field in Sage, you would get that it has 2 functions
the first one is direct DLP
discrete_log
and the other solves it under some condition
linear_relation
. So, of course you shall first try the easier one in case it
can get the correct solution quick. (You may be stuck at this
point if you don’t know sage or the condition that the second
function would work under. So, during the competition you should
ask the admin about any hints he would give).
Now, let’s see what happpens if we decrypt the values \(c_1\) and \(c_2\) by
\(c_2c_1^{a}^{-1} = mA^kg^{ak}^{-1} = mg^{ak}g^{ak}^{-1} = m\).
Until that moment everything is going well and we already knew
that this values if the compressed flag bits module \(p\).
Suppose that \(d\) is the compressed bits, then \(m = rp + d\)
and you know that the corerect flag is 319-bit long. Now,
319-292=27
is the remaining bits of the multiple \(r\) to get the correct
value of the flag.
Finally, we can bruteforce this value and get the correct flag bits.
Flag:
ASCWG{b17in9_0v3r_ElG@m41_G4l0i$_Gr0up5}
Conclusion
In this writeup, I’ve demostrated a detailed analysis of all the crypto challenges I’ve created in the competition as well as, my thought process of how I could solve the problem. I hope you’ve enjoyed the competition and learned something from the challenges that are created by me or even my teammates. Lastly, congrats to the first 3 teams, to every team who played fairly until the end and wish you all the best.