---
# System prepended metadata

title: RITSEC CTF 2024 Crypto Writeup

---

# RITSEC CTF 2024 Crypto Writeup


> ## Looking for team
>
>Taiwanese. Is Interesting in Crypto.
If your team is looking for members, please contanct my email: 1999119wang@gmail.com.
>
>Below is my result in RITSEC CTF 2024.
https://ctfd.ritsec.club/users/931
![image](https://hackmd.io/_uploads/HJSZiSxxR.png)


## [Warm Up] Words
Pigpen cipher
![image](https://hackmd.io/_uploads/HJH94V-g0.png)

```
Flag: RS{TIME_IS_OF_THE_ESSENCE}
```

## [Warm Up] Emails

By plaintext email, we know the ciphertext is encrypted by xor with a repeated 32 bytes key.

The last 33 characters of the emails are always the same, we can use it to get the key.

```
Key : 0x1c3fa62d927325456154f71b539e3b0143c519912c343755ec35e2cee187827a
Flag: RS{Th4T's_n0T_h0w_Y0u_u53_OTP}
```

## Dastardly Evil Scientists

The only useful information is that the flag is encrypted in DES with ECB mode. I can only try [DES Weak Key](https://en.wikipedia.org/wiki/Weak_key).

Encrypt the ciphertext with key `e0e0e0e0f1f1f1f1` will get the plaintext.

```
Flag: RS{W0W_TH1S_K3YSP4C3_1S_4S_FL4T_4S_34RTH}
```

## Old Fashioned Crypto

The flag is encrypted by AES. And the symmetric key is encrypted by [Merkle–Hellman knapsack cryptosystem](https://en.wikipedia.org/wiki/Merkle%E2%80%93Hellman_knapsack_cryptosystem)(I will use the same term shown in the link), and the public key is provided.

Our goal is to decrypt the symmetric key.

Observe that the public key sequence is strictly increasing until the last 10 elements. This indicate that the $r$ is not big enough so most of the $w_i r < q$.

```
640152,
1168382,
2253084,
4674574,
9047900,
18456670,
.
.
.
47834067073570057125014913206716985424894,
95668134147140114250029826413433970529712,
4024280977508761976100681034599517010909,  <-- strictly increasing until here
8048561955017523952201362069199034004036,
16097123910035047904402724138398067786320,
32194247820070095808805448276796135496282,
64388495640140191617610896553592270815790,
128776991280280383235221793107184541765468,
70241995243789299946484614422100659064021,
140483990487578599892969228844201318336196,
93655993658385733261979485896134212196063,
187311987316771466523958971792268424462208
```

<!-- By definition,

$640152 = w_1 r$
$1168382 = w_2 r$

So r should by a common divisor of $640152$ and $1168382$, and their GCD is $1046$, which is a possible $r$. -->

If every $w_i r < q$, we can solve the subset sum problem directly from the public key and ciphertext. However, the last 10 $w_i r$ are larger than $q$, so we have to try all possible value of the last 10 bits and solve the rest 118 bits.

With this method, we can find the symmetric key successfully.

```
Symmetric key: 0xca977e277a5f80bc9fcd40d142b7a138
Flag         : RS{You'll_Need_A_Super_Increasing_Knapsack}
```

> Note that we need not to know the exact $q$.

## Failed File Transfer

By the challenge description and decoding `.pem` files, we can know that a same plaintext is encrypted by RSA with different $N$ or $e$. The `file1.txt` and `file2.txt` is encrypt with a same $N$. Thus we can perform [RSA Common Mudulus Attack](https://infosecwriteups.com/rsa-attacks-common-modulus-7bdb34f331a5). 

```
Flag: RS{Y0U_C4NT_R3AD_M3_R1GHT??}
```

## Flag Distribution Server

The flag is encrypted and we can pad some arbitrary text before or after the flag and encrypt it. By some observation, we can know that the cipher is a block cipher, its block size, and the structure of plaintext.


To be specific, I add a repeated `a` before the flag. For example, the format string can be `{{"flag": "aaa{}"}}`. 

| Repeated `a` | Output length (byte)| Identical proportion with previous output (byte)|
| -------- | -------- | -------- |
| 0     | 48     | 0     |
| 1     | 48     | 0     |
| 2     | 48     | 0     |
| 3     | 48     | 0     |
| 4     | 48     | 0     |
| 5     | 48     | 0     |
| 6     | 48     | 1     |
| 7     | 48     | 16     |
| 8     | 64     | 16     |
| 9     | 64     | 16     |
| 10    | 64     | 16     |
| 11    | 64     | 16     |
| 12    | 64     | 16     |
| 13    | 64     | 16     |
| 14    | 64     | 16     |
| 15    | 64     | 16     |
| 16    | 64     | 16     |

It is clear that the block size is 128 bit. If given the format string `{{"flag": "aaa{}"}}`, the plaintext is probably something like `{unknown string with length 11}aaa{flag}`.

We can pad the paintext so that only the first character is in the first block, as shown in below (`x` means the unknown character).

```
xxxxxxxxxxxaaaax xxxxxxxxxxxxxxxx ...
```

Use the ciphertext of the first block, enumerate all possible value of `?` to get the same cipghertext.

```
xxxxxxxxxxxaaaa? xxxxxxxxxxxxxxxx ...
```

Found out that the first character is `R`.
Then repeat the procedure.

```
xxxxxxxxxxxaaaRx xxxxxxxxxxxxxxxx ...
xxxxxxxxxxxaaaR? xxxxxxxxxxxxxxxx ...
```

Continue this procedure, you can get the whole flag. Remember to escape `{` and `}` when you guess you encounter one.

```
Flag: RS{0n3_Ch4r4cT3R_@t_4_t1Me}
```

> Note that we need not to know the exact encrypt algorithm and mode of operation.

## MEaaS

Three plaintexts is encrypt by a same RSA public key. The challenge provides a API but I didn't use it.

```
n = 1970384981
c1 = 184323288
c2 = 306942680
c3 = 1791553791
```

$n$ is way too small, we can factorize it and try all possible $e$ and corresponding $d$ to decrypt the ciphertexts.
Assuming that each ciphertexts is a proportion of flag, flag contains only printable characters and ends with `}`. We can easily locate the valid plaintext during the brute-force. Luckily, it appeared early during the brute force.

```
p = 56437, q = 34913
e = 63997, d = 1700258389
Flag: RC{b3etr007}
```