177 lines
5.5 KiB
Markdown
177 lines
5.5 KiB
Markdown
---
|
||
date: 2022-04-30
|
||
toc: true
|
||
title: Mommy morse
|
||
tags: ["writeup", "FCSC2022", "hardware"]
|
||
---
|
||
|
||
The little warm up with daddy morse is now over, let's see what mommy has to offer !
|
||
|
||
## What is this about ?
|
||
|
||
Daddy morse was just about transmitting on or off signals (well, kindof).
|
||
Mommy morse wants us to fiddle with frequencies and all: I'm pretty sure that this whole thing is about Frequency modulation/Frequency shift keying.
|
||
We have to encode data the following way:
|
||
- sample rate: 24kHz
|
||
- `.` : 5kHz carrier wave for 1ms
|
||
- `-`: 5kHz carrier wave for 5ms
|
||
- Space between two letters: 1kHz carrier wave for 5ms
|
||
- Space between two words: 1kHz carrier wave for 20ms
|
||
|
||
To flag this challenge, I was at first trying to do everything in python with numpy, just as I did with Daddy morse.
|
||
In the end, I just kept the message -> AM part and did the rest in Gnu Radio.
|
||
|
||
## Message -> AM
|
||
|
||
Here is the script that I used to generate my AM signal:
|
||
```python
|
||
import numpy as np
|
||
|
||
SAMP_RATE = 24e3
|
||
TIMING_DOT = 1/1000
|
||
TIMING_DASH = 5/1000
|
||
TIMING_SEP_LETTER = 5/1000
|
||
TIMING_SPACE = 20/1000
|
||
|
||
dot_count = int(TIMING_DOT * SAMP_RATE)
|
||
dash_count = int(TIMING_DASH * SAMP_RATE)
|
||
letters_count = int(TIMING_SEP_LETTER * SAMP_RATE)
|
||
word_count = int(TIMING_SPACE * SAMP_RATE)
|
||
|
||
alphabet = { 'A':'.-', 'B':'-...',
|
||
'C':'-.-.', 'D':'-..', 'E':'.',
|
||
'F':'..-.', 'G':'--.', 'H':'....',
|
||
'I':'..', 'J':'.---', 'K':'-.-',
|
||
'L':'.-..', 'M':'--', 'N':'-.',
|
||
'O':'---', 'P':'.--.', 'Q':'--.-',
|
||
'R':'.-.', 'S':'...', 'T':'-',
|
||
'U':'..-', 'V':'...-', 'W':'.--',
|
||
'X':'-..-', 'Y':'-.--', 'Z':'--..',
|
||
'1':'.----', '2':'..---', '3':'...--',
|
||
'4':'....-', '5':'.....', '6':'-....',
|
||
'7':'--...', '8':'---..', '9':'----.',
|
||
'0':'-----', ', ':'--..--', '.':'.-.-.-',
|
||
'?':'..--..', '/':'-..-.', '-':'-....-',
|
||
'(':'-.--.', ')':'-.--.-'}
|
||
|
||
def build(phrase):
|
||
res = []
|
||
for w in phrase.split(" "):
|
||
tmp = ""
|
||
for l in w:
|
||
if l not in alphabet:
|
||
pass
|
||
else:
|
||
tmp += "{} ".format(alphabet[l])
|
||
res.append(tmp[:-1])
|
||
|
||
return res
|
||
|
||
def generate(morse):
|
||
res = []
|
||
for w in morse:
|
||
for c in w:
|
||
if c == ".":
|
||
data = [(1+1j) for _ in range(dot_count)] + [0j for _ in range(dot_count)]
|
||
elif c == "-":
|
||
data = [(1+1j) for _ in range(dash_count)] + [0j for _ in range(dot_count)]
|
||
else:
|
||
data = [0j for _ in range(letters_count)]
|
||
res = res[:-dot_count]
|
||
res.extend(data)
|
||
|
||
if w[-1] == '.' or w[-1] == '-':
|
||
res = res[:-dot_count]
|
||
res.extend([0j] * word_count)
|
||
|
||
res = res[:-word_count]
|
||
return np.array(res,dtype=np.complex64)
|
||
|
||
sentence = "CAN I GET THE FLAG"
|
||
morse = build(sentence)
|
||
data = generate(morse)
|
||
filename = sentence.replace(" ", "-").lower()
|
||
|
||
print(f"Writing data to {filename}.iq")
|
||
with open(f"{filename}.iq", "wb") as file:
|
||
file.write(data)
|
||
```
|
||
|
||
Quick launch:
|
||
```sh
|
||
1 ❯ python hardware/mommymorse/script.py
|
||
Writing data to can-i-get-the-flag.iq
|
||
0 ❯
|
||
```
|
||
And let's see if URH appreciates what I've done:
|
||

|
||
|
||
It does !
|
||
|
||
## AM -> FM
|
||
|
||
Now that is the fun part of the challenge! Here's what I've done:
|
||

|
||
|
||
|
||
You can open and try this circuit by yourself with `script.grc`.
|
||
|
||
## Kesseussai ????
|
||
|
||
Okay sorry, please bear with me.
|
||
|
||
First and foremost, set the samp_rate variable to 24KHz as specified by the challenge, **this is important**.
|
||
|
||
Then, load the AM signal with a *File Source* block (do not forget to turn off repeat on this block or your FM file won't work with this challenge).
|
||
|
||
Create a constant source block that always outputs `(1 + 1j)` and link both that block and the file source to a subtract block: this will give us the opposite of our AM signal.
|
||
|
||
Create two signal sources, one at 1KHz (for the spaces) and one at 5KHz (for the non-spaces chars), they will be used to convert that AM signal to FM.
|
||
Now create two multiply blocks:
|
||
- Link the subtract block and the 1KHz signal source with the first multiply
|
||
- Link the File source and the 5KHz signal with the second multiply
|
||
|
||
This will transform this (AM):
|
||

|
||
|
||
Into this (FM):
|
||

|
||
|
||
We still need to add these two multiply blocks, so create add Add block and link them with it.
|
||
|
||
And that's pretty much it, link your add block to a file sink (and some graphs if you like to visualize your hard work) and voila:
|
||
|
||

|
||
|
||
|
||
# Lemme get the flag
|
||
|
||
Just a little edit to the `client.py` a bit:
|
||
|
||
```python
|
||
from pwn import *
|
||
import numpy as np
|
||
import base64
|
||
|
||
HOST = args.HOST or "challenges.france-cybersecurity-challenge.fr"
|
||
PORT = args.PORT or 2252
|
||
|
||
c = remote(HOST, PORT)
|
||
|
||
hello_signal = np.fromfile("res.iq", dtype = np.complex64)
|
||
|
||
encoded_signal = base64.b64encode(hello_signal.tobytes())
|
||
|
||
c.recvuntil(b"> ")
|
||
c.sendline(encoded_signal)
|
||
print(c.recvline())
|
||
```
|
||
|
||
Now just go ahead and execute it:
|
||
```sh
|
||
0 hardware/mommy morse❯ python client.py
|
||
[+] Opening connection to challenges.france-cybersecurity-challenge.fr on port 2252: Done
|
||
b'Well done: FCSC{490b88345a22d35554b3e319b1200b985cc7683e975969d07841cd56dd488649}\n'
|
||
[*] Closed connection to challenges.france-cybersecurity-challenge.fr port 2252
|
||
```
|