We were ranked 185/1942, which means we were at Top 10% of the game.
This is my second time playing CTF, I'm quite happy with the result.
Although I think we could have jumped into TOP 100 but we'll wait until
next time! :D
When I first looked at this problem, I thought this would be a bit
operation problem which, if you are a applied cryptography engineer, you
should be really familiar with. But this misled me so much that I paid
more time working on this problem.
% 256 is equivalent to & 255; and
& 0xff means getting the lowest 8 bits. I also tried
lots of bit operation technique such as &1 is judging
odd/even, right shift(resp. left shift) are \(\lfloor \frac{a}{2^{b}} \rfloor\) (resp.
\(a\cdot 2^{b}\))... and so on. But
they just didn't work.
So at the end of the day, I thought, oh, I'd just try z3 solver. And
the result just popped up.
In fact, it can be solved by z3 solver, but I think it actually does
have its arithmetical solution. I just didn't find it. Like the answer
by the end:
DUCTF{i_d0nt_th1nk_th4ts_h0w_1t_w0rks_actu4lly_92f45fb961ecf420}
from os import urandom, path from Crypto.Cipher import AES
FLAG = open(path.join(path.dirname(__file__), 'flag.txt'), 'r').read().strip() MESSAGE = f'Decrypt this... {urandom(300).hex()}{FLAG}'
defmain(): key = urandom(16) for _ inrange(2): iv = bytes.fromhex(input('iv: ')) aes = AES.new(key, iv=iv, mode=AES.MODE_OFB) ct = aes.encrypt(MESSAGE.encode()) print(ct.hex())
if __name__ == '__main__': main()
Solution
I really enjoyed this one because I didn't refer any previous
solution, and I didn't play block cipher type problem before. I solved
it by my own. But being a cryptography engineer, you always deal with
those block cipher, so this problem is not only meaningful for obtaining
some points in a CTF game, but also helped me understand how to avoid
unsafely implementing cryptography algorithm. (Although I always have a
fear that I might implement something wrong. It's inevitable. That's why
it's also important to add security check for cryptography
implementation in code review.)
Anyway, it's crystal clear that OFB will be vulnerable when you use
the key twice. Because you can always have this so-called "bit-flipped"
attack (I think I read it somewhere else, but can't remember).
The attack for this problem can be summarised by this photo
below:
p = 55899879511190230528616866117179357211 V = GF(p)^3 R = PolynomialRing(GF(p)) x = R.gen() f = x^3 + 36174005300402816514311230770140802253*x^2 + 35632245244482815363927956306821829684*x + 10704085182912790916669912997954900147 Q = R.quotient(f)
defV_pow(A, n): return V([a^n for a inlist(A)])
n, m = randint(1, p), randint(1, p) A = Q.random_element() B = Q.random_element() C = A^n * B^m
if phi_C == check_phi_C: print(open('./flag.txt', 'r').read().strip())
solution
First of all, this challenge can be solved by simply sending
0 0 0, 1 1 1, or null. But I didn't come up
with this during the competition! What a pity! So, I didn't solve this
challenge. The official solution is a good challenge, though. It
involves homomorphism and the using of Sage.
I personally have an issue with reading text, so every time I do Sage
challenge, I translate them into more maths syntax. \[
\begin{aligned}
p &= 55899879511190230528616866117179357211\\
V &: \mathbb{Z}_p^3\\
R &: R_p=\mathbb{Z}_p[x]\\
x &: \text{variate in } R_p\\
f &= x^3 + 36174005300402816514311230770140802253x^2 \\
&+ 35632245244482815363927956306821829684x \\
&+ 10704085182912790916669912997954900147\\
Q &:R_p/f
\end{aligned}
\] We are also given random \(A, B\in
Q\), and \(C:=A^nB^m\), where
random integers \(n,m \in [1,p)\).
The function V_pow(A, n) take exponent \(n\) and: \[
A = a_0+a_1x+a_2x^2,
\] where \((a_1,a_2,a_3) \in
\mathbb{Z}_p^3\) as inputs, then calculate \(A'= a_0^n+a_1^nx+a_2^nx^2\).
We are now asked to provide 3 vector
phi_A, phi_B, phi_C, each of which contains 3 elements
corresponding to coefficients in degree 3 polynomials. To obtain the
flag, the condition is:
\[
\begin{aligned}
\phi_A &= a_0+a_1x+a_2x^2, \;{\phi_A}'= a_0^n+a_1^nx+a_2^nx^2\\
\phi_B &= b_0+b_1x+b_2x^2, \;{\phi_B}'= b_0^n+b_1^nx+b_2^nx^2\\
\phi_C &= c_0+c_1x+c_2x^2\\
&= a_0'b_0' + a_1'b_1'x + a_2'b_2'x^2
\end{aligned}
\] The point is,