This commit is contained in:
2022-08-09 16:00:04 +01:00
commit 8e2093187a
4 changed files with 720 additions and 0 deletions

176
writeups/mommymorse.md Normal file
View File

@@ -0,0 +1,176 @@
---
date: 2022-04-30
toc: true
title: Mommy morse
tags: ["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:
![img](https://notes.alxczl.fr/uploads/3d47d9b47a6ee23685015020e.png)
It does !
## AM -> FM
Now that is the fun part of the challenge! Here's what I've done:
![img](https://notes.alxczl.fr/uploads/3d47d9b47a6ee23685015020f.png)
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):
![img](https://notes.alxczl.fr/uploads/3d47d9b47a6ee236850150210.png)
Into this (FM):
![img](https://notes.alxczl.fr/uploads/3d47d9b47a6ee236850150211.png)
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:
![img](https://notes.alxczl.fr/uploads/3d47d9b47a6ee236850150212.png)
# 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
```

385
writeups/xfactor2.md Normal file
View File

@@ -0,0 +1,385 @@
---
date: 2022-04-30
title: Xfactor 2/2
toc: true
tags: ["FCSC2022", "hardware"]
---
## Quick recap
Great ! We've finally HACKED the first part of the user logon in Xfactor1/2 and we're now facing a 2FA form.
<figure style='text-align: center;'>
<img src="https://notes.alxczl.fr/uploads/3d47d9b47a6ee236850150204.jpeg" style="width: 50%">
<figcaption style='color: grey'>Me when I absolutely HACK my way into the Hypersecret company</figcaption>
</figure>
## Let's get started !
The FCSC team gave us a pcap file, let's see what's inside.
![img](https://notes.alxczl.fr/uploads/3d47d9b47a6ee236850150200.png)
<figure style='text-align: center;'>
<img style="width: 100%; height: 350px" src="https://notes.alxczl.fr/uploads/3d47d9b47a6ee236850150201.png">
</figure>
Well alright, I already knew that this capture contained USB-related data, but I'm still lost here; I'm going to need something to make this clearer.
Since we're dealing with an hardware token, I'm guessing that the packets in the capture might be related to U2F stuff.
I have found two potential dissectors that should (I hope they will) help me analyse these packets a bit faster :
- https://gist.github.com/woodrow/cb1496975e131e37d5dd716127a250a4
- https://gist.github.com/z4yx/218116240e2759759b239d16fed787ca
After using both of them, I decided to continue the challenge using the first one as I just found it easier to work with.
### Installing and using a new dissector
To install a dissector, just run these commands:
```sh
mkdir -p ~/.local/lib/wireshark/plugins
cd ~/.local/lib/wireshark/plugins
wget https://gist.github.com/woodrow/cb1496975e131e37d5dd716127a250a4
```
Then either restart Wireshark or just press **Ctrl+Caps+L** to reload your Lua plugins, then right-click one of the _URB_INTERRUPT_ packets, click on _Decode as_, then, under the _Current_ column, select _CTAPHID_.
There we go ! Much better (right ?!?!?)
![img](https://notes.alxczl.fr/uploads/3d47d9b47a6ee236850150202.png)
You might notice a few differences if you actually use the aforementionned dissector and that's normal; I patched it to show a bit more information, I'll post it on [my gitea](https://gitea.alxczl.fr) after the FCSC2022 ends !
EDIT: [Here](https://gitea.alxczl.fr/alexandre/wireshark-ctaphid-dissector) it is
### CTAP...HID ? What does that mean ?!?!?!?!
_CTAP_ means **C**lient **T**o **A**uthenticator **P**rotocol and _HID_ means **H**uman **I**nterface **D**evice (quick note: your keyboard and your mouse are HIDs, more infos about that [here](https://www.usb.org/hid)).
_CTAP_ is a part of the FIDO2 project and there are 2 different version of CTAP right now :
- CTAP1/U2F: This is the one that will be used in the challenge
- CTAP2: Authenticators that use this one are called CTAP2 authenticators, FIDO2 authenticators, or WebAuthn Authenticators (and you surely have heard about WebAuthn before)
If you want more informations about CTAP and such, please click [here](https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html).
## Packet buildin'
Thanks to the dissector, I can now at least see the U2F conversations.
![img](https://notes.alxczl.fr/uploads/3d47d9b47a6ee236850150203.png)
That sure is a lot of _CTAPHID Initialization_ and _CTAPHID Continuation_ packets !
Judging from what's being said [here](https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#usb-message-and-packet-structure), an _CTAPHID Initialization packet_ comes first and one or more _CTAPHID Continuation packets_ follow to complete the payload.
## U2F Requests and Reponses
[This section](https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html#u2f-message-framing) tells us that there are two ways of encoding messages in U2F, one for the requests and one for the responses.
### Requests
Thanks to my best friend (the new dissector), Wireshark now shows the content of the U2F messages, so here's an example of an U2F request:
![img](https://notes.alxczl.fr/uploads/3d47d9b47a6ee236850150205.png)
As you can see right there, a request contains these elements:
- **CLA**: 1 byte, reserved for the transport protocol (if applicable), set to zero by the host.
- **INS**: 1 byte, contains the U2F command code (we'll talk more about some of them a bit later)
- **P1**: 1 byte, first parameter
- **P2**: 1 byte, second parameter
- **LC**: 3 bytes, length of the request's data
- **Request data**: **LC** Bytes, the request's data, omitted if **LC** is 0
- **LE**: 3 bytes, expected length of the response, optionnal if no response if expected
A quick note about the various lengths: as the data is transmitted via **CTAPHID**, the messages are encoded using *Extended length encoding*, so **LC** and **LE** are 3 bytes long.
Using *Short encoding* would make them fit on 1 byte, but that would reduce the maximal request/response data size to 255 bytes (compared to 65535 byte).
### Responses
Here's a response (that contains data, and that is not always the case):
![img](https://notes.alxczl.fr/uploads/3d47d9b47a6ee236850150207.png)
As you can see, a responses are a bit simpler:
- **Response data**: **LE** bytes if specified, n bytes if not
- **SW1**: 1 byte
- **SW2**: 1 byte
**SW1** (MSB) and **SW2** (LSB) are status word bytes 1 and 2, together they form a 16-bit status word.
We'll just care about **SW_NO_ERROR (0x9000)** and **SW_CONDITIONS_NOT_SATISFIED (0x6985)**, as they are these are the only status code that appear in this challenge.
<figure style='text-align: center;'>
<img src="https://notes.alxczl.fr/uploads/3d47d9b47a6ee236850150206.jpg">
<figcaption style='color: grey'>That's all folks !</figcaption>
</figure>
## U2F command codes
*Chose promise chose due*, I will now talk about the various commands code.
### U2F_VERSION (0x03)
This command (sent by the host), is used to retrieve the U2F version that is supported by the hardware token. In this case, the token sent back `U2F_V2`, which means that it supports *CTAP1/U2F*.
More information about that command here: https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html#getversion-request-and-response---u2f_version
### U2F_AUTHENTICATE (0x02)
This command (also sent by the host), is used to iniate an U2F authentication.
Its payload is contained in the following structure:
<figure style='text-align: center;'>
<img src="https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/img/auth-request.png">
<figcaption style='color: grey'>Shamelessly borrowed from the Fido Alliance's website</figcaption>
</figure>
To be honest, I only care about the control byte here, and I've only seen two specific values used in this challenge:
- **CHECK-ONLY**: 0x07, makes the U2F token check whether the provided key handle was originally created by this token, and whether it was created for the provided application parameter
- **ENFORCE-USER-PRESENCE-AND-SIGN**: 0x03, asks the U2F token to perform a real signature and respond
As always, you can get more info here: https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html#u2f-message-framing
## Ok but what do I do with this ???
Ok sorry I may have gone a bit too far with this U2F mess, and you're right: reading specs and RFCs is great but it won't make the flag pop out of the capture.
Let's login on https://x-factor.france-cybersecurity-challenge.fr/login, right click and inspect the **Check Token** button: it's just a link that launches the **beginAuthen** function with a parameter that never changes!
On to that **beginAuthen** function:
```javascript
function beginAuthen(keyHandle) {
$.getJSON(
"/beginAuthen",
{ keyHandle: keyHandle },
function(startAuthen) {
// call U2F API to generate response for the challenge
u2f.sign(startAuthen.appId, startAuthen.challenge,
[ { version: startAuthen.version, keyHandle: startAuthen.keyHandle } ],
function(data) {
logU2FResponse(logger, data);
logMsg(logger, "Calling server to finish the authentication...");
finishAuthen(data);
}, U2F_TIMEOUT_SEC);
});
}
```
By opening the network inspector of my browser's developper tools, I'll inspect the requests/responses sent to https://x-factor.france-cybersecurity-challenge.fr/beginAuthen.
Here's what I've got:
```json
[
{
"version": "U2F_V2",
"appId": "https://x-factor.france-cybersecurity-challenge.fr",
"keyHandle": "MiXECXjEbxAAe7QOH2gsiNiK7bXeuGJnLUGO7kbJutdODZvuqV-T1TPpTVEVIrynmScyNOjQaRAUi0PSH8LUtQ",
"challenge": "9rlDOo98PIKIiubib97v4IDCJ1FBB2uRUhNgwH89wqw"
},
{
"version": "U2F_V2",
"appId": "https://x-factor.france-cybersecurity-challenge.fr",
"keyHandle": "MiXECXjEbxAAe7QOH2gsiNiK7bXeuGJnLUGO7kbJutdODZvuqV-T1TPpTVEVIrynmScyNOjQaRAUi0PSH8LUtQ",
"challenge": "L8tsCkDErRPzV9SAOlOj2JzFMXAOjmUs7JnimkH9_gI"
},
{
"version": "U2F_V2",
"appId": "https://x-factor.france-cybersecurity-challenge.fr",
"keyHandle": "MiXECXjEbxAAe7QOH2gsiNiK7bXeuGJnLUGO7kbJutdODZvuqV-T1TPpTVEVIrynmScyNOjQaRAUi0PSH8LUtQ",
"challenge": "D5CxgaFPGIQu5fGYPEjo-YA9Dqd6y2PBoWP6p56TpFw"
}
]
```
<figure style='text-align: center;'>
<img style="max-width: 60%" src="https://notes.alxczl.fr/uploads/3d47d9b47a6ee236850150209.png">
<figcaption style='color: grey'>HOLD UP, SOMETHING AINT RIGHT</figcaption>
</figure>
Only 3 different challenges ? But I sent at least 12 requests just to make sure...
Alright, let's try replaying the responses from the capture then!
## The fun part
<i style="">
I had almost drained of Google's resources, my determination was beginning to falter and I thought that the only way to get this flag was to code an U2F emulator from scratch...
But I found the light; my savior, Cesar aka MattGorko, appeared on my last googling attempt with his Github repo.
</i>
<p style="text-align: center">
I present to you, <a href="https://github.com/MattGorko/U2F-Emulated">U2F-Emulated</a>.
</p>
<figure style='text-align: center;'>
<img style="max-width: 60%" src="https://notes.alxczl.fr/uploads/3d47d9b47a6ee23685015020a.jpg">
<figcaption style='color: grey'>Rare picture of Cesar the GOAT making this first blood possible</figcaption>
</figure>
### Let's patch this thing
Having an emulator is great an all, but it won't help much without some modifications.
We first need to go back to the pcap and retrieve the values contained in the **SW_NO_ERROR** reponses.
Here is the array of responses (with its associated array of response lengths) that made me get the flag:
```c
size_t respsize[] = {76, 76, 75, 76, 76, 76, 77, 75, 76};
char *responses[] = {
"\x01\x00\x00\x00\x00\x30\x45\x02\x21\x00\xb2\x8f\x52\xe2\xe8\xc1" \
"\x9a\x2c\xe1\xd8\xf6\x93\x7a\x14\x56\xfa\xd2\x78\x5d\x35\xa0\xa2" \
"\xa3\xa2\x0c\x73\x55\x63\x4c\x52\x22\x76\x02\x20\x24\xc1\xb7\x9a" \
"\xb0\x9a\x46\xa7\x75\x1b\x35\x4f\x31\x3f\x24\x71\xcb\x98\xa3\x52" \
"\xd0\xfe\x22\x99\x4d\xdb\x4f\x3d\x01\xff\xd9\x9e",
"\x01\x00\x00\x00\x00\x30\x45\x02\x20\x09\xba\x7e\xb1\xbd\xb2\xaf" \
"\x07\x03\x8e\x60\xc9\x1f\x84\xf4\x09\x62\xf3\xe0\x88\xdb\xba\x3c" \
"\xb3\x4b\x69\x87\xa6\xa4\xc8\xab\x1a\x02\x21\x00\xf5\x73\x8d\xaf" \
"\xa5\xc6\x1a\xd7\xef\xe6\x90\xbf\xe7\x74\x66\x94\x51\xfe\x52\xe1" \
"\x6e\xec\xf0\x6a\xf1\x2a\x6a\x12\xe7\x8e\x84\x51",
"\x01\x00\x00\x00\x00\x30\x44\x02\x20\x67\x40\xe2\x46\x34\xbb\xaf" \
"\x0b\x0a\x54\x5e\x9d\x8d\xa1\xac\x71\x75\xc9\x3c\xc5\xae\xaa\xf2" \
"\x89\xb9\x3b\x8b\xba\x00\x3d\x74\xd8\x02\x20\x7b\x00\x8c\xfc\xbc" \
"\xf5\x16\x78\xd9\x11\x95\x96\x8e\x98\x39\xed\x02\xad\xbc\xc4\xd3" \
"\x47\x03\xb6\xbe\x5a\xe8\x8e\x1e\x97\x0b\xac",
"\x01\x00\x00\x00\x00\x30\x45\x02\x20\x22\xbf\xdb\x52\xc6\x94\x41" \
"\x6b\x2c\x39\xbb\x4f\x33\x51\x26\x53\xe6\x87\x43\x52\xfb\xfb\x51" \
"\x33\x1f\xc0\x24\x2a\x24\x51\xe5\xbb\x02\x21\x00\xaa\x61\x0d\xa5" \
"\x3e\xd8\x09\xb2\x30\x32\x6c\x1b\xa3\x33\xc2\xd5\xd5\xb0\xe6\x05" \
"\x2c\xe1\xc6\x07\xfd\xb7\x5c\x5b\x3d\x2d\x25\x66",
"\x01\x00\x00\x00\x00\x30\x45\x02\x20\x36\xfa\x3a\xc1\x25\x93\xbe" \
"\x5f\x31\xe6\x49\x9f\x24\xdc\xaf\x57\xd3\x8e\x18\x5e\x16\x10\x99" \
"\x6c\x04\xbd\x71\xa5\xd1\x12\x58\x57\x02\x21\x00\xa7\xec\x50\xe8" \
"\xbb\x73\x98\xd3\xe0\x08\x8a\xbf\xab\x26\x53\x69\xa8\x13\x39\x53" \
"\x2b\xc9\x9c\x2e\xc5\x5a\x77\x62\x9d\x8c\xd7\xed",
"\x01\x00\x00\x00\x00\x30\x45\x02\x20\x7b\xea\xf7\x96\xa9\x34\xdf" \
"\xb7\xc7\x13\x06\x94\x7b\x59\x90\x63\xb2\x51\x65\x13\x00\x3f\x34" \
"\x97\xd3\xfc\xa4\x1a\x7f\x38\xa0\xa4\x02\x21\x00\xaf\xcc\x79\x20" \
"\x05\xac\x3c\x60\x1a\x0f\xf3\x9b\x43\xec\xae\x9d\x74\x1c\xde\x39" \
"\x8b\x0b\x70\x48\x0c\xc9\xc4\xa6\x13\x64\xd5\xb6",
"\x01\x00\x00\x00\x00\x30\x46\x02\x21\x00\xf6\x0a\xf8\x4c\xfc\x0f" \
"\x3d\x9f\x33\xae\x8a\xd0\x4b\x61\x7a\xb7\xcf\x78\x2f\x70\xcd\x08" \
"\x3a\x71\xaa\xd7\x38\xb4\xe7\x5d\x51\xc0\x02\x21\x00\xb4\x3a\x62" \
"\x93\x49\xce\xa1\x34\x15\x26\x3b\x86\xa1\xaf\xc6\x37\xcd\x4c\x92" \
"\xdc\x86\x73\xa3\x60\x57\x73\x11\x71\x05\x82\xf9\xda",
"\x01\x00\x00\x00\x00\x30\x44\x02\x20\x50\xac\xfd\x22\xab\x96\xe9" \
"\x7e\xf4\x9d\xe4\xff\x5c\x12\xf1\x4d\xd1\xdb\x99\x3e\x6c\x62\x4a" \
"\x42\xab\x27\xd9\x41\xd6\x80\x8a\x4d\x02\x20\x53\x7b\x98\x9a\x10" \
"\x2b\x22\x8b\x51\x17\x50\x10\xf3\x8e\xaa\x4d\x55\x0a\x9c\xaf\xbc" \
"\x14\x11\x0f\xfd\xa2\xe3\x90\x8c\x2f\xc5\xb0",
"\x01\x00\x00\x00\x00\x30\x45\x02\x20\x6f\x1b\xfc\x40\x2f\xe8\x91" \
"\x1c\xdc\x9a\x92\xec\xc7\x13\x1e\xeb\xa3\x17\x06\xce\xc4\x81\x0d" \
"\x8e\x60\xef\x75\x9d\x1e\x87\x42\x6c\x02\x21\x00\xca\x29\x24\x79" \
"\xb5\xc4\x42\x67\x79\x45\x30\x6e\x3f\xcd\x17\x5e\x87\x57\x7c\x9e" \
"\x45\xd8\xc6\xa3\xdf\x5d\x4b\xb0\x00\x97\x40\xc3"};
```
I have patched (or ruined) the `raw_authenticate_enforce` function in `src/raw/authenticate.c`, here it is (bear with me, my solution is horrendous):
```c
int auth_enforce_count = 0;
// This int is important, don't forget to put it outside the function
static struct payload *raw_authenticate_enforce(u2f_emu_vdev *vdev,
const uint8_t *apdu, size_t size)
{
(void) size;
/* Parmas */
struct authentification_params params;
memcpy(&params, apdu + 7, sizeof(params));
size_t respsize[] = {76, 76, 75, 76, 76, 76, 77, 75, 76};
char *responses[] = {
"\x01\x00\x00\x00\x00\x30\x45\x02\x21\x00\xb2\x8f\x52\xe2\xe8\xc1" \
"\x9a\x2c\xe1\xd8\xf6\x93\x7a\x14\x56\xfa\xd2\x78\x5d\x35\xa0\xa2" \
"\xa3\xa2\x0c\x73\x55\x63\x4c\x52\x22\x76\x02\x20\x24\xc1\xb7\x9a" \
"\xb0\x9a\x46\xa7\x75\x1b\x35\x4f\x31\x3f\x24\x71\xcb\x98\xa3\x52" \
"\xd0\xfe\x22\x99\x4d\xdb\x4f\x3d\x01\xff\xd9\x9e",
"\x01\x00\x00\x00\x00\x30\x45\x02\x20\x09\xba\x7e\xb1\xbd\xb2\xaf" \
"\x07\x03\x8e\x60\xc9\x1f\x84\xf4\x09\x62\xf3\xe0\x88\xdb\xba\x3c" \
"\xb3\x4b\x69\x87\xa6\xa4\xc8\xab\x1a\x02\x21\x00\xf5\x73\x8d\xaf" \
"\xa5\xc6\x1a\xd7\xef\xe6\x90\xbf\xe7\x74\x66\x94\x51\xfe\x52\xe1" \
"\x6e\xec\xf0\x6a\xf1\x2a\x6a\x12\xe7\x8e\x84\x51",
"\x01\x00\x00\x00\x00\x30\x44\x02\x20\x67\x40\xe2\x46\x34\xbb\xaf" \
"\x0b\x0a\x54\x5e\x9d\x8d\xa1\xac\x71\x75\xc9\x3c\xc5\xae\xaa\xf2" \
"\x89\xb9\x3b\x8b\xba\x00\x3d\x74\xd8\x02\x20\x7b\x00\x8c\xfc\xbc" \
"\xf5\x16\x78\xd9\x11\x95\x96\x8e\x98\x39\xed\x02\xad\xbc\xc4\xd3" \
"\x47\x03\xb6\xbe\x5a\xe8\x8e\x1e\x97\x0b\xac",
"\x01\x00\x00\x00\x00\x30\x45\x02\x20\x22\xbf\xdb\x52\xc6\x94\x41" \
"\x6b\x2c\x39\xbb\x4f\x33\x51\x26\x53\xe6\x87\x43\x52\xfb\xfb\x51" \
"\x33\x1f\xc0\x24\x2a\x24\x51\xe5\xbb\x02\x21\x00\xaa\x61\x0d\xa5" \
"\x3e\xd8\x09\xb2\x30\x32\x6c\x1b\xa3\x33\xc2\xd5\xd5\xb0\xe6\x05" \
"\x2c\xe1\xc6\x07\xfd\xb7\x5c\x5b\x3d\x2d\x25\x66",
"\x01\x00\x00\x00\x00\x30\x45\x02\x20\x36\xfa\x3a\xc1\x25\x93\xbe" \
"\x5f\x31\xe6\x49\x9f\x24\xdc\xaf\x57\xd3\x8e\x18\x5e\x16\x10\x99" \
"\x6c\x04\xbd\x71\xa5\xd1\x12\x58\x57\x02\x21\x00\xa7\xec\x50\xe8" \
"\xbb\x73\x98\xd3\xe0\x08\x8a\xbf\xab\x26\x53\x69\xa8\x13\x39\x53" \
"\x2b\xc9\x9c\x2e\xc5\x5a\x77\x62\x9d\x8c\xd7\xed",
"\x01\x00\x00\x00\x00\x30\x45\x02\x20\x7b\xea\xf7\x96\xa9\x34\xdf" \
"\xb7\xc7\x13\x06\x94\x7b\x59\x90\x63\xb2\x51\x65\x13\x00\x3f\x34" \
"\x97\xd3\xfc\xa4\x1a\x7f\x38\xa0\xa4\x02\x21\x00\xaf\xcc\x79\x20" \
"\x05\xac\x3c\x60\x1a\x0f\xf3\x9b\x43\xec\xae\x9d\x74\x1c\xde\x39" \
"\x8b\x0b\x70\x48\x0c\xc9\xc4\xa6\x13\x64\xd5\xb6",
"\x01\x00\x00\x00\x00\x30\x46\x02\x21\x00\xf6\x0a\xf8\x4c\xfc\x0f" \
"\x3d\x9f\x33\xae\x8a\xd0\x4b\x61\x7a\xb7\xcf\x78\x2f\x70\xcd\x08" \
"\x3a\x71\xaa\xd7\x38\xb4\xe7\x5d\x51\xc0\x02\x21\x00\xb4\x3a\x62" \
"\x93\x49\xce\xa1\x34\x15\x26\x3b\x86\xa1\xaf\xc6\x37\xcd\x4c\x92" \
"\xdc\x86\x73\xa3\x60\x57\x73\x11\x71\x05\x82\xf9\xda",
"\x01\x00\x00\x00\x00\x30\x44\x02\x20\x50\xac\xfd\x22\xab\x96\xe9" \
"\x7e\xf4\x9d\xe4\xff\x5c\x12\xf1\x4d\xd1\xdb\x99\x3e\x6c\x62\x4a" \
"\x42\xab\x27\xd9\x41\xd6\x80\x8a\x4d\x02\x20\x53\x7b\x98\x9a\x10" \
"\x2b\x22\x8b\x51\x17\x50\x10\xf3\x8e\xaa\x4d\x55\x0a\x9c\xaf\xbc" \
"\x14\x11\x0f\xfd\xa2\xe3\x90\x8c\x2f\xc5\xb0",
"\x01\x00\x00\x00\x00\x30\x45\x02\x20\x6f\x1b\xfc\x40\x2f\xe8\x91" \
"\x1c\xdc\x9a\x92\xec\xc7\x13\x1e\xeb\xa3\x17\x06\xce\xc4\x81\x0d" \
"\x8e\x60\xef\x75\x9d\x1e\x87\x42\x6c\x02\x21\x00\xca\x29\x24\x79" \
"\xb5\xc4\x42\x67\x79\x45\x30\x6e\x3f\xcd\x17\x5e\x87\x57\x7c\x9e" \
"\x45\xd8\xc6\xa3\xdf\x5d\x4b\xb0\x00\x97\x40\xc3"};
payload_add_data(payload,
(uint8_t *) responses[auth_enforce_count],
respsize[auth_enforce_count]);
printf("Sending hardcoded enforce number %d\n", auth_enforce_count);
authenticate_response_sw(payload, SW_NO_ERROR);
auth_enforce_count = (auth_enforce_count + 1) %
(sizeof(datasize) / sizeof(size_t));
/* Increment counter */
vdev->counter->counter_increment(vdev->counter);
return payload;
}
```
I have also modified `raw_authenticate_check` to make it send the same `SW_CONDITIONS_NOT_SATISFIED` response everytime, just as in the capture:
```c
static struct payload *raw_authenticate_check(u2f_emu_vdev *vdev,
const uint8_t *apdu, size_t size)
{
/* Parmas */
struct authentification_params params;
memcpy(&params, apdu + 7, sizeof(params));
/* Start Response payload */
struct payload *payload = payload_new();
printf("Sending hardcoded check\n");
authenticate_response_sw(payload, SW_CONDITIONS_NOT_SATISFIED);
return payload;
}
```
I am not showing every single modifications, so you **WILL** need to remove the `static` keyword from some definitions to make the whole project compile normally (or change the _CFLAGS_).
### BRING ME TO LIFE
Let's compile my frankenmulator. From the project root (I use `cmake` btw :sunglasses:):
```sh
mkdir build && cd build
cmake ..
make && make examples
```
Here's the magic formula to bring him to life:
```sh
cd examples/usb/
sudo ./u2f-emu-usb
```
And it now waits indefinitely for requests.
<figure style='text-align: center;'>
<img style="width: 30vw" src="https://notes.alxczl.fr/uploads/3d47d9b47a6ee23685015020b.jpeg">
</figure>
## FLAGGITTY FLAG FLAG
Launch the frankenmulator, hop on the website, login, click on **Check Token** and
![img](https://notes.alxczl.fr/uploads/3d47d9b47a6ee23685015020d.png)
<p style="text-align: center; color: grey">
Bruh moment
</p>
Woopsie, sorry but this solution needs you to repeat these instructions until you get the flag.
I know I know, this isn't very fancy, but it works. I think that it might be able to optimise this solution even further by inspecting the requests and responding with the correct answer (that actually was the next step if this didn't work).
Anyway, after a few tries, the screen looks a bit different:
![img](https://notes.alxczl.fr/uploads/3d47d9b47a6ee23685015020c.png)
YAY ! Now onto the next challenge ! (Oh and don't forget to kill the frankenmulator if you don't want it to eat all your U2F challenges :wink:)
## Useful links
- Structures paquets CTAP1/U2F : https://doc.riot-os.org/structctap__hid__pkt__t.html
- Infos sur FIDO : https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html
- MSG command : https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#usb-hid-msg
- WINK command (blinks the LED): https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html
- AUTHENTICATE MSG : https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html#introduction