This year I sadly didn't have a lot of time to play, so I did just the crypto challenges. I managed to get all four of them for 1400 points. This was enough to reach 23rd place on the scoreboard :)
The writeups are below. Also, I've written some helpful comments inside the solve scripts.
In this challenge, the flag is simply encrypted with 2048-bit RSA. What can you do? Isn't RSA-2048 tough to break? Oops - the key generation uses a single prime instead of a product of two primes for
This is a re-do of "Lost Basis" from BSidesTLV CTF 2023. If you played last year, you may remember there was an unintended solution due to an unplanned bug (left as an exercise 🙃).
The solution to this challenge is presumably the same as the intended solution of last year's challenge. SHA256 seems strong, but XORring the results of SHA256 renders it quite a bit weaker!
Notice that you can request the signature (tag) of a harmless command such as "greet" with whatever number of additional parameters you wish. Therefore, you can XOR as many SHA256 outputs as you wish. You can control which ones go into the XOR and which ones don't. Of course, you can't control what the SHA256 values themselves are going to be -- however, this is still quite powerful.
The XOR operation between a pair of single bits is equivalent to the addition operation in the field
It seems we can solve the challenge using linear algebra: All we have to do is to find a set of elements that can be thrown into the tag request, each one representing a vector in hash_content("greet", "command")
and introduce instead the value of hash_content("get_flag", "command")
. Once we've done that, we can just ask for a tag for greet
with the bogus parameters that represent the working linear combination, and just copy that tag over to get_flag
and solve the challenge.
Here we have DES, a cipher from like 50 years ago (no, seriously 😱), encrypting a message which contains the flag but also a bunch of known plaintext. The number of DES rounds seems to have been reduced from the standard 16 to just 2 in what looks like an LLM-hallucinated (or human-hallucinated?) piece of code (I looked everywhere for "flexDes
" and couldn't find it, is it a thing?), but which is pretty clear.
It's worth looking at diagrams to follow this one. I haven't drawn any. The diagrams on Wikipedia are pretty good :)
Looking at the Feistel scheme of DES, with only two rounds on our hands, it's worth it to write down the expressions for all the half-blocks explicitly:
By
Rearranging / plugging in as necessary, we can eliminate
Going over the known part of the plaintext, we can get many such pairs and try to discover the round keys. Doing this requires going into the function $f$ itself, which comprises a XOR with the round key, a bunch of 6->4 substitution boxes (S-boxes), and a fixed permutation. With our known pairs, we can work our way from both ends and reach a list of input-output pairs to the S-boxes, where the inputs are XOR an unknown key (but only a 6-bit key for each S-box). With the amount of known plaintext we have, we can collect enough pairs in order to only have one possibility for the round keys.
If this is unclear - read my solution script, it might be clearer than the explanation.
This was an exciting challenge, because it reminded me of the Windows vulnerability known as Curveball (a name coined by Tal Be'ery, who has written about it). It is one of my favorite vulnerabilities, and I've also had some chances to write about it, including recently in Paged Out! #4, and used it as an example in a couple of workshops and training exercises (closed source, sorry).
In the Windows Curveball vulnerability, essentially what you had (at least from a math point of view) was the ability to view an elliptic curve public key from a different point of view: Relative to another generator of your choice, instead of the original curve generator. The CTF Curveball challenge is not the same -- but not too different either! In this challenge, we have an implementation of ECDSA on your choice of two standard curves: Brainpool BP-384 or NIST P-384. You can get the flag if you're able to demonstrate your ability to sign a particular message, "I really wanna flag"
(well, technically, you need to sign the SHA-384 hash of this message, but ok). The server generates a key pair for you to use, but then it throws the private key in the trash.
Discovering the private key is out of the question. These are both standard curves used in ECDSA, considered to be strong, so finding out the private key given the public key comes down to solving the Elliptic Curve Discrete Logarithm Problem (ECDLP) on those curves. Yeeeeah, no.
One can immediately notice that you can generate a key on one curve and then apply it to the other curve. The bug here is that the server is missing a check that the public key, a curve point, is actually on the curve. Since the curve equation is
Let's say we're working with some elliptic curve,
Those curve operations are:
- Adding two distinct points
$P$ and$Q$ . - Doubling a point
$P$ (basically adding it to itself). - Multiplying a point
$P$ by a scalar$k$ (basically adding it to itself$k$ times).
Multiplying by a scalar doesn't involve any curve math, just the double-and-add algorithm. Let's focus on the addition and doubling.
Implementations of point addition and doubling (including the implementation in the challenge) use the values of the input points' coordinates, the curve parameter
then you get an alternate curve
which satisfies those conditions.
The order of an elliptic curve, that is -- the number of points on it, has a lot of importance due to the curve's group structure. Due to a basic group-theoretic fact - Lagrange's theorem, multiplying any point on the curve by the curve's order will send it to the neutral group element - the point at infinity,
What is the order of an elliptic curve? Unlike simpler groups (like integers mod
This is great for us - it means that if we change the curve, we are likely to get a different curve order. Fortunately, it is easy to compute the curve's order using Schoof's algorithm or the slightly more modern SEA algorithm. In SageMath, it is implemented in EllipticCurve.order
.
Let's take a look at the main formula used for ECDSA verification:
where
In our case, we can get
What if in fact
This can be done by an attacker by choosing