I played DEF CON CTF Quals once again with @Shellphish and we ended up at the 10th place. This blog post describes what actually was going on from my side. If you are looking for an informative blog post, this is not a good-read for you. There would be a little useful information, since I am just hanging around most of the time. I will even shamelessly copy some memes online to make the blog post looked rich in content.

After all, I am only able to solve nooombers (on day 1), qoo-or-ooo and back-to-qoo (on day 2). What happened on day 2 from my point of view is considerably more interesting.

Challenge: nooombers

nooombers (44 solves, 127 points)

I really do not understand what this is about, but my cousin told me that once he managed to got a flag out of this. I was able to copy from my cousin's shell parts of his interactions with the server. You can find them in interaction1.txt and interaction2.txt. Unfortunately, they are incomplete.

nooombers.challenges.ooo 8765

Files: interaction1.txt, interaction2.txt

Solved with Paul, Subwire and more.

Part I: Understanding the challenge

⏲️ ⏲️ Day 1, 10:45 (165 minutes in) ⏲️ ⏲️

It is good to be greeted in the morning with a crypto challenge. I am pretty excited to see a crypto challenge in a pwn-intensive CTF. Let's see what is it about. I bet it is attributed to fish since I am seeing a bunch of 魚's (i.e., the Chinese word of fish).

⏲️ ⏲️ Day 1, 11:08 (188 minutes in) ⏲️ ⏲️

After a while, I learned how to interact with the server. In interaction1.txt, there are multiple copies of the below string:

髛髛髛   # Title, maybe?
鬆      # Operation 0
魢      # Operation 1
魀      # Operation 2
鯞      # Operation 3
鮂      # Operation 4
魻      # Operation 5
鬼      # Operation 6
鮳      # Operation 7
鯑      # Operation 8
鮽      # Operation 9
魵      # Operation 10
髛      # Send me something!

They are actually the menu. On top of that, they have defined 11 operations, namely , , ... up to . Furthermore, is somewhere asking for inputs. At this moment, the only operation I knew it , which is an exit. Also, worth noting that the character set changes on each nc connection.

We are then able to send one of the above operations. For instance, . It then return a line of gibberish:

鬆 鱷鳘鰬鰛鳘鰑鳌鲯鳗鲳...鱈鲒鲲鱱鳣鰂鳇鲫鳃鳾

One thing I suspected is the long strings are actually used repeatedly. Therefore, I tried to translate interaction1.txt into something that is slightly more readable. Each OP_* is a single character representing an operation, and RET_*_* is a 144-character string represents the return values of the operations.

Welcome to nooombers! Press enter to start...

MENU
INPUT OP_0
OP_0 RET_0_1

MENU
INPUT OP_1
INPUT RET_0_1
OP_1 RET_0_1 RET_1_1 RET_1_2

MENU
INPUT OP_3
INPUT RET_0_1
INPUT RET_1_2
OP_3 RET_0_1 RET_1_2 RET_1_1 RET_3_1

MENU
INPUT OP_2
INPUT RET_0_1
OP_2 RET_0_1 RET_1_1 RET_2_1

MENU
INPUT OP_4
INPUT RET_0_1
INPUT RET_2_1
OP_4 RET_0_1 RET_2_1 RET_1_1 RET_4_1

MENU
INPUT OP_3
INPUT RET_3_1
INPUT RET_4_1
OP_3 RET_3_1 RET_4_1 RET_1_1 RET_4_1

MENU
INPUT OP_3
INPUT RET_4_1
INPUT RET_4_1
OP_3 RET_4_1 RET_4_1 RET_1_1 RET_3_2

MENU
INPUT OP_3
INPUT RET_3_2
INPUT RET_4_1
OP_3 RET_3_2 RET_4_1 RET_1_1 RET_3_3

MENU
INPUT OP_3
INPUT RET_3_3
INPUT RET_4_1
OP_3 RET_3_3 RET_4_1 RET_1_1 RET_3_4

MENU
INPUT OP_3
INPUT RET_3_4
INPUT RET_4_1
OP_3 RET_3_4 RET_4_1 RET_1_1 RET_3_5

MENU
INPUT OP_3
INPUT RET_3_5
INPUT RET_4_1
OP_3 RET_3_5 RET_4_1 RET_1_1 RET_3_6

MENU
INPUT OP_3
INPUT RET_3_6
INPUT RET_4_1
OP_3 RET_3_6 RET_4_1 RET_1_1 RET_3_7

MENU
INPUT OP_3
INPUT RET_3_7
INPUT RET_4_1
OP_3 RET_3_7 RET_4_1 RET_1_1 RET_3_8

MENU
INPUT OP_3
INPUT RET_3_8
INPUT RET_4_1
OP_3 RET_3_8 RET_4_1 RET_1_1 RET_3_9
Too much! Disabling one option...

MENU (OP_9 REMOVED)
INPUT OP_4
INPUT RET_3_2
INPUT RET_3_3
OP_4 RET_3_2 RET_3_3 RET_1_1 RET_3_6

MENU (OP_9 REMOVED)
INPUT OP_6
INPUT RET_3_3
INPUT RET_3_2

I have also translated interaction2.txt and knew that OP_9 is the operation that win us the flag. That said, if we are too annoying (i.e., sending too much queries), we're not able to get flag anymore.

Part II: A wild but legit guess

⏲️ ⏲️ Day 1, 11:51 (231 minutes in) ⏲️ ⏲️

While I am looking at the translated interaction1.txt, a strange idea popped up to my mind. Would the below operation represent $2\times3 = 6$? We can observe, from below, that OP_4(RET_3_2, RET_3_3) = (RET_1_1, RET_3_6).

MENU (OP_9 REMOVED)
INPUT OP_4
INPUT RET_3_2
INPUT RET_3_3
OP_4 RET_3_2 RET_3_3 RET_1_1 RET_3_6

Hence, we assume that OP_4 is the multiplication operation. Also, RET_3_2 = 2, RET_3_3 = 3, RET_3_6 = 6. Base on the assumption, I don't think we are ambitious to assume that RET_3_k = k for all $k > 1$, RET_3_1 = 0 and RET_4_1 = 1. Last but not least, OP_3 is the addition operation.

Part III: Recovering the functions

⏲️ ⏲️ Day 1, 12:37 (277 minutes in) ⏲️ ⏲️

I experimented for a while and I was pretty convinced that my guess is correct. It was pretty unbelieveable that I am able to guess well.

Let's translate interaction1.txt mathematically and fill in what we knew. Since RET_1_1 is returned in every call (except the first), I am going to skip it. I am using $x_{i, j}$ as a replacement for RET_i_j:

  • $\text{OP}_0() = x_{0, 1}$
  • $\text{OP}_1(x_{0, 1}) = x_{1, 2}$
  • $x_{0, 1} + x_{1, 2} = x_{3, 1}$
  • $\text{OP}_2(x_{0, 1}) = x_{2, 1}$
  • $x_{0, 1} \times x_{2, 1} = 1$
  • $x_{3, 1} + 1 = 1$
  • $1 + 1 = 2$
  • $2 + 1 = 3$
  • $3 + 1 = 4$
  • $4 + 1 = 5$
  • $5 + 1 = 6$
  • $6 + 1 = 7$
  • $7 + 1 = 8$
  • $8 + 1 = 9$
  • $2 \times 3 = 6$

From above, it is evident that $x_{3, 1} = 0$ and $x_{2, 1} = {x_{0, 1}}^{-1}$. Hence $x_{1, 2} = -x_{0, 1}$. We are able to further derive that $\text{OP}_1$ and $\text{OP}_2$ is an inverse operator for addition and multiplication, respectively (i.e., $\text{OP}_1(x) = -x$ and $\text{OP}_2(x) = x^{-1}$). With more tests (for example, knowing that $x_{1, 1} \times x_{1, 1} = x_{1, 1} + x_{1, 1} = 0$), it is verified that those operations are using $x_{1, 1}$ is the modulo. Let's summarize what we have in the below table:

Operation Description
$\text{OP}_0()$ return a random number... maybe
$\text{OP}_1(x)$ return $-x\ \text{mod}\ q$
$\text{OP}_2(x)$ return $x^{-1}\ \text{mod}\ q$
$\text{OP}_3(x, y)$ return $x + y\ \text{mod}\ q$
$\text{OP}_4(x, y)$ return $xy\ \text{mod}\ q$

⏲️ ⏲️ Day 1, 13:30 (330 minutes in) ⏲️ ⏲️

We have further guessed (and experimented) that $\text{OP}_5$ is another multiplication and $\text{OP}_6$ is exponentation. At first, I didn't know why are there two multiplications, it took me some time that they are operated on a different modulus (denote it as $p$).

⏲️ ⏲️ Day 1, 13:33 (333 minutes in) ⏲️ ⏲️

Me: What is the deal for operation 7? There are so much asterisks... Let's skip it.

⏲️ ⏲️ Day 1, 14:03 (363 minutes in) ⏲️ ⏲️

Instead of a simple mathematical operation, $\text{OP}_8$ takes three parameters and runs several arithmetic operations, and eventually return one value. Let's translate a sample response of the function call...

MENU
INPUT OP_8
INPUT X1 
INPUT X2
INPUT X3
MODINV X3 Q Y1
MUL X1 Y1 Q Y2
MUL X2 Y1 Q Y3
POW R1 Y2 P Y4
POW R2 Y3 P Y5
MUL Y4 Y5 P Y6
OP_0 Y7
NEG Y7 Q Y8
ADD Y6 Y7 Q Y9
ADD Y9 Y8 Q Y10
OP_8 X1 X2 X3 Y10

Let's express $y_{10}$ (the output) in terms of $x_1, x_2, x_3$ (the inputs) mathematically.

\[\begin{aligned} y_{10} &= (y_9 + y_8)\ \text{mod}\ q \\ &= [(y_6 + y_7) + (-y_7)]\ \text{mod}\ q \\ &= y_6\ \text{mod}\ q \\ &= (y_4y_5\ \text{mod}\ p)\ \text{mod}\ q \\ &= ({r_1}^{y_2} {r_2}^{y_3}\ \text{mod}\ p)\ \text{mod}\ q \\ &= ({r_1}^{x_1 y_1\ \text{mod}\ q} {r_2}^{x_2 y_1\ \text{mod}\ q}\ \text{mod}\ p)\ \text{mod}\ q \\ &= ({r_1}^{x_1/x_3\ \text{mod}\ q} {r_2}^{x_2/x_3\ \text{mod}\ q}\ \text{mod}\ p)\ \text{mod}\ q \\ \end{aligned}\]

Here they have introduced two more unknowns $r_1$ and $r_2$ those are kept unchanged in the same connection.

What is that? During the contest, I feel like this is implementing something but I could not recall. After the contest, I was by N00bcak and rbtree that was actually the data signature algorithm (DSA). Turns out operation 8 is a function verifying if your signature $(x_2, x_3)$ is correct, given the digest $x_1$. Hereby $r_1$ and $r_2$ corresponds to the generator and public key, respectively.

Part IV: Identifing the objective and getting through

⏲️ ⏲️ Day 1, 14:36 (396 minutes in) ⏲️ ⏲️

Operation 9 is literally the most important function that we gotta look into. Since we knew most of the operations, it is pretty easy to understand what it does. Well, it is just $\text{OP}_9(x_2, x_3) = \text{OP}_8(r_0, x_2, x_3)$ for some fixed $r_0$. Now let's bring in our translated interaction2.txt:

MENU
INPUT OP_9
INPUT X2
INPUT X3
MODINV X3 Q Y1
MUL R0 Y1 Q Y2
MUL X2 Y1 Q Y3
POW R1 Y2 P Y4
POW R2 Y3 P Y5
MUL Y4 Y5 P Y6
OP_0 Y7
NEG Y7 Q Y8
ADD Y6 Y7 Q Y9
ADD Y9 Y8 Q X2
OP_9 R0 X2 X3 X2
Correct signature! This is the flag:
...

Since the output for $\text{OP}_9$ is $x_2$, I assumed that our objective is to find $x_2, x_3$ such that

\[({r_1}^{r_0/x_3\ \text{mod}\ q}{r_2}^{x_2/x_3\ \text{mod}\ q}\ \text{mod}\ p)\ \text{mod}\ q = x_2.\]

⏲️ ⏲️ Day 1, 15:08 (428 minutes in) ⏲️ ⏲️

I was expecting that everything would be simpler if I can make

\[x_2 = {r_1}^{r_0/x_3} = {r_2}^{x_2/x_3} = 1.\]

It did happen. I was randomly messing around with the operations and found that they have mistakenly defined MODINV(0) = 0. That said, I can set $x_2 = 1$ and $x_3 = 0$...

⏲️ ⏲️ Day 1, 15:10 (430 minutes in) ⏲️ ⏲️

The flag pops right away: OOO{D0Y0uUnd3rstandWh4tANumberIs?}! Not so crypto, eh?

Slack alert! I am definitely being slack since there are no more crypto challenges in the rest of the day. 95% of the content onwards is pointless.

Part V: Postmortem

The crypto involved in this challenge is digital signature algorithm (DSA). I went through some discussions after the contest, and turns out operation 7 is the signing process for DSA, for a given digest. I think the intentional solution would be the poor random number it generates with $\text{OP}_0$ that is used in signing.

Challenge: exploit-for-dummies

⏲️ ⏲️ Day 1, 15:33 (453 minutes in) ⏲️ ⏲️

Since I have nothing to do, I hopped into the challenge and tried to find the answers for the unsolved trivia questions. Seeing no crypto, I decided to have a break.

Side story: Inspecting the /challenge API

⏲️ ⏲️ Day 1, 16:06 (486 minutes in) ⏲️ ⏲️

I took some time integrating ctfbot to ping teammates when there is a challenge update. While I was inspecting the /challenge API, I saw something fishy...

{"message": { /* omitted... */ "unopened_by_category": {"haiku": 9}}}

Wait, does it mean that there are only nine challenges yet-to-be released?

Challenge: mooosl

⏲️ ⏲️ Day 1, 19:51 (711 minutes in) ⏲️ ⏲️

kylebot — Today 19:51
This is a hash collision+musl libc
The bug is that the next pointer is not cleared after delete
What we can do is: find a pair of different keys hashed into a same bucket, one of them of length 1

Samuel — Today 19:52
hash collision! need help?

kylebot — Today 19:52
And then bruteforce the byte by querying it
I'm not on a computer rn
But yes. We need many collisions

Shortly after, Robin send me the code for the hash.

__int64 __fastcall sub_13DB(__int64 a1, unsigned __int64 a2)
{
  int i; // [rsp+14h] [rbp-Ch]
  __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = 2021LL;
  for ( i = 0; a2 > i; ++i )
    v4 = 322401073 * v4 + *(i + a1);
  return v4;
}

Wow, it is pretty easy! This would be a little practice on lattices, right?

kylebot — Today 19:54
@Samuel also
Mod 0x1000

Bummer, it is a different story under modulo 0x1000. Everyone can find collisions easily...

⏲️ ⏲️ Day 1, 21:00 (780 minutes in) ⏲️ ⏲️

I finished the collider that finds collision in $\mathcal{O}(1)$. It finds infinitely many preimages those have the same digest. Well, I don't even know what is it used.

Challenge: rick

⏲️ ⏲️ Day 1, 21:02 (782 minutes in) ⏲️ ⏲️

I was once baited by crypto-related terms. This time it is rick.

Paul — Today 21:02
if you could undo the encryption though, that'd be super helpful

R3x — Today 21:03
isn't the encryption just xoring with letters RICK?

Ooof. This challenge actually looked fun but I did not investigate any further. I have strong teammates, after all.

Side story: Creating a crypto challenge

Good that you read up to here. Maybe you will be slightly benefited for the next CTF I help organizing!

I was inspired by nooombers to create a CTF challenge. Of course, I am not going to mention what is it. I am still unable to write an exploit after until now...

End of day 1

The organizers decided to release two more challenges near the end of the day (under GMT+8). Well, both of them are categorized as reverse-pwn. I rather go to bed...

Samuel — Today 23:52
darn you ooo
wake me up when there is a crypto

GH0S1 — Today 23:52
@Samuel cryptocurrency?

GH0S7_1N_7H3_F1NG3RS — Today 23:53
@Samuel this is cryptocurrency

Never mind, good night. Wait, no, it actually matters - crypto is not cryptocurrency but cryptography.