|
| 1 | +--- |
| 2 | +title: Very Serious Cryptography |
| 3 | +date: 2025-03-10T03:15:35+03:00 |
| 4 | +description: Writeup for Very Serious Cryptography [KalmarCTF 2025] |
| 5 | +author: h3pha |
| 6 | +tags: |
| 7 | +- crypto |
| 8 | +draft: false |
| 9 | +--- |
| 10 | +___ |
| 11 | + |
| 12 | +>This writeup is just a better explanation of [this](https://connor-mccartney.github.io/cryptography/other/KalmarCTF2025#very-serious-cryptography) one. Make sure to check it too! |
| 13 | +
|
| 14 | +## Challenge Description |
| 15 | + |
| 16 | +As CTF becomes more mainstream, a troubling new trend is emerging of player fanclubs becoming so large that top players and challenge authors are having their lives disrupted from the sheer volume of valentines gifts they are receiving! With some instances of the extreme valentines pressure even leading to the last minute postponement of major CTFs!?! |
| 17 | + |
| 18 | +As such, we have decided to expand our traditional CTF valentines cards service, to provide a utility for efficiently generating meaningful, romantic gifts. We hope this will enable busy CTF players to be all set for the upcoming white day, and the huge number of return gifts they will inevitably have to send back, ensuring that no more CTF's will have to be postponed this year! |
| 19 | + |
| 20 | +Note: Our infra team was worried that the sheer number of gifts required could take down our servers. But luckily i stumbled upon a solution that lets me generate them much more efficiently! Thanks to [https://x.com/veorq/status/1805877920306499868](https://x.com/veorq/status/1805877920306499868) |
| 21 | + |
| 22 | +nc very-serious.chal-kalmarc.tf 2257 |
| 23 | + |
| 24 | +## Intuition |
| 25 | + |
| 26 | +Challenge file: |
| 27 | +```python |
| 28 | +from Crypto.Cipher import AES |
| 29 | +from Crypto.Util.Padding import pad |
| 30 | +import os |
| 31 | + |
| 32 | +with open("flag.txt", "rb") as f: |
| 33 | + flag = f.read() |
| 34 | + |
| 35 | +key = os.urandom(16) |
| 36 | + |
| 37 | +# Efficient service for pre-generating personal, romantic, deeply heartfelt white day gifts for all the people who sent you valentines gifts |
| 38 | +for _ in range(1024): |
| 39 | + # Which special someone should we prepare a truly meaningful gift for? |
| 40 | + recipient = input("Recipient name: ") |
| 41 | + |
| 42 | + # whats more romantic than the abstract notion of a securely encrypted flag? |
| 43 | + romantic_message = f'Dear {recipient}, as a token of the depth of my feelings, I gift to you that which is most precious to me. A {flag}' |
| 44 | + |
| 45 | + aes = AES.new(key, AES.MODE_CBC, iv=b'preprocessedlove') |
| 46 | + print(f'heres a thoughtful and unique gift for {recipient}: {aes.decrypt(pad(romantic_message.encode(), AES.block_size)).hex()}') |
| 47 | +``` |
| 48 | + |
| 49 | +The idea behind this one is to use the decryption property of `AES-CBC` that uses the IV to decrypt the first block and then it uses the past blocks to decrypt next blocks of data. |
| 50 | + |
| 51 | +This means that we can brute force each character of the flag like this: |
| 52 | + |
| 53 | +Text to encrypt: `Dear {our input} , as a token of the depth of my feelings, I gift to you that which is most precious to me. A {flag}` |
| 54 | + |
| 55 | +To brute force the first character we ensure that the input we give will pad the text in such a way so that the first character of the flag is the last character in a block. |
| 56 | + |
| 57 | +=> `len("Dear {our input} , as a token of the depth of my feelings, I gift to you that which is most precious to me. A") == 15 mod 16` => `padding` |
| 58 | + |
| 59 | +We encrypt the text and then we can use as input this: |
| 60 | +`Dear {padding} , as a token of the depth of my feelings, I gift to you that which is most precious to me. A ` + character to brute force. |
| 61 | + |
| 62 | +Now if we compare all the encrypted messages with the original encrypted text we can find the character from the flag. |
| 63 | + |
| 64 | +Repeating this for all the characters until we reach `}` will give us the whole flag. |
| 65 | + |
| 66 | +## Solution |
| 67 | + |
| 68 | +Solver: |
| 69 | +```python |
| 70 | +from pwn import * |
| 71 | + |
| 72 | +charset = "abcdefghijklmnopqrstuvwxyz'{}_" |
| 73 | +prefix = "Dear " |
| 74 | +middle = ", as a token of the depth of my feelings, I gift to you that which is most precious to me. A " |
| 75 | +flag = "" |
| 76 | +p = process(["python", "chal.py"]) |
| 77 | +# p = remote("very-serious.chal-kalmarc.tf", 2257) |
| 78 | + |
| 79 | +def send_input_list(p, input_list): |
| 80 | + output_list = [] |
| 81 | + for i in input_list: |
| 82 | + p.sendline(i.encode()) |
| 83 | + # takes only the encrypted text |
| 84 | + output = bytes.fromhex(p.recvline().decode().split()[-1]) |
| 85 | + output_list.append(output) |
| 86 | + return output_list |
| 87 | + |
| 88 | +while "}" not in flag: |
| 89 | + try: |
| 90 | + # ensure that the character we are searching is at the end of the block |
| 91 | + padding = "_" * ((15 - len(prefix + middle + flag)) % 16) |
| 92 | + # this is where the original flag is encrypted |
| 93 | + original = send_input_list(p, [padding])[0] |
| 94 | + # create all possible variants of the characters withing the flag |
| 95 | + brute_input = [padding + middle + flag + c for c in charset] |
| 96 | + # send the variants, and receive all encryptions |
| 97 | + brute_output = send_input_list(p, brute_input) |
| 98 | + # this is the position of the end of the block |
| 99 | + character_position = len(prefix + padding + middle + flag) + 1 |
| 100 | + for i in range(len(brute_output)): |
| 101 | + if brute_output[i][:character_position] == original[:character_position]: |
| 102 | + flag += charset[i] |
| 103 | + print(flag) |
| 104 | + except EOFError: |
| 105 | + p = process(["python", "chal.py"]) |
| 106 | +``` |
| 107 | + |
| 108 | +### Flag |
| 109 | + |
| 110 | +`kalmar{i_wonder_how_many_challenges_have_been_made_based_off_this_tweet}` |
0 commit comments