Tags: crypto 

Rating:

![Plutonian Crypto](https://github.com/NEMO246/WRITEUP_CTF/raw/main/SunshineCTF%202025/crypto/Plutonian%20Crypto/Title.png)

### Solve [solve.py](https://github.com/NEMO246/WRITEUP_CTF/blob/main/SunshineCTF%202025/crypto/Plutonian%20Crypto/solve.py)

```python
import socket
import binascii

def xor(b1, b2):
return bytes([_a ^ _b for _a, _b in zip(b1, b2)])

HOST = "chal.sunshinectf.games"
PORT = 25403
BLOCK_SIZE = 16

KNOWN_PLAINTEXT = b"Greetings, Earthlings."
KNOWN_PLAINTEXT_BLOCK_0 = KNOWN_PLAINTEXT[:BLOCK_SIZE]

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))

for _ in range(16):
s.recv(1024)

ciphertexts = []
try:
while len(ciphertexts) < 200:
line = s.recv(4096).strip()
if not line:
break
parts = line.split(b'\n')
for part in parts:
if part:
ciphertexts.append(binascii.unhexlify(part))
except Exception as e:
print(f"Error receiving data: {e}")

msg_len = len(ciphertexts[0])
num_blocks = (msg_len + BLOCK_SIZE - 1) // BLOCK_SIZE

print(f"Message length: {msg_len} bytes ({num_blocks} blocks)")
print(f"Collected {len(ciphertexts)} ciphertexts.")

if len(ciphertexts) < num_blocks:
print("Not enough ciphertexts to fully recover the key!")
exit()

keystream_blocks = []
for i in range(num_blocks):
ct_i_block_0 = ciphertexts[i][:BLOCK_SIZE]
ks_block = xor(ct_i_block_0, KNOWN_PLAINTEXT_BLOCK_0)
keystream_blocks.append(ks_block)

full_keystream = b"".join(keystream_blocks)

decrypted_message = xor(ciphertexts[0], full_keystream)

print("\n--- Decrypted message ---\n")
print(decrypted_message.decode('utf-8', 'replace'))

```

Original writeup (https://github.com/NEMO246/WRITEUP_CTF/tree/main/SunshineCTF%202025/crypto/Plutonian%20Crypto).