From 8e2093187a64e1008fef472a9eb507f3849d80b5 Mon Sep 17 00:00:00 2001 From: Alexandre CHAZAL Date: Tue, 9 Aug 2022 16:00:04 +0100 Subject: [PATCH] init --- posts/internalca.md | 117 +++++++++++++ posts/proxmoxsetup.md | 42 +++++ writeups/mommymorse.md | 176 +++++++++++++++++++ writeups/xfactor2.md | 385 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 720 insertions(+) create mode 100644 posts/internalca.md create mode 100644 posts/proxmoxsetup.md create mode 100644 writeups/mommymorse.md create mode 100644 writeups/xfactor2.md diff --git a/posts/internalca.md b/posts/internalca.md new file mode 100644 index 0000000..7c6fccc --- /dev/null +++ b/posts/internalca.md @@ -0,0 +1,117 @@ +--- +date: 2022-07-29 +toc: true +title: Setup a local CA for your homelab +--- + +## Why ? + +I hate seeing red locks on the website I browse. + + +That's it. + + +And since I have friends that like to do very smart pranks involving MITM attacks, I want to protect myself (and my passwords, as long as they might be) from them. + + +Ok I may be the "friend" that play these pranks but I always ask for their consent! + +Jokes aside, we will use [step-ca](https://smallstep.com/docs/step-ca) as it's lightweight, easy to deploy and it just works. + +## Machine + +I have deployed my instance on a separate LXC on proxmox: +- OS: Debian (I used Buster but chose what you want) +- CPU: 1 +- RAM: 2G (Might be overkill, but I have loads of RAM) +- Storage: 15 GB (Bit too much, but I don't care) + +Its IP address is assigned by DHCP but its lease is static; make sure to assign it an entry in your local DNS server! + +## Installation + +First, download the latest release of `step` [here](https://github.com/smallstep/cli/releases/latest) and install it. +This is pretty straightforward, just follow this example (on an amd64 platform): +```sh +wget https://dl.step.sm/gh-release/cli/gh-release-header/v0.21.0/step-cli_0.21.0_amd64.deb +sudo dpkg -i step-cli_0.21.0_amd64.deb +``` + +Then, do the same thing with the latest version `step-ca` of (https://github.com/smallstep/certificates/releases/latest). +Again, just do: +```sh +wget https://dl.step.sm/gh-release/certificates/gh-release-header/v0.21.0/step-ca_0.21.0_amd64.deb +sudo dpkg -i step-ca_0.21.0_amd64.deb +``` + +`step-ca` can be setup as a systemd service, but instead of copy pasting their instructions like a moron, I'll just tell you to follow them [here](https://smallstep.com/docs/step-ca/certificate-authority-server-production#running-step-ca-as-a-daemon). +There are a lot of steps so please be sure to read the instructions carefully. + +## Configuration + +As I am sure that you've carefully followed the instructions on how to make your CA a daemon, your latest creation should be correctly initialized, so no need to `step ca init` :wink:. +Congratulations: you can now generate and distribute TLS certificates to whomever you want! +You might want to continue reading this though as we'll see how to setup the ACME server and that, I can assure you, is really cool. + +### ACME server ? + +Come on, you've probably already heard about that; ever heard of _Let's Encrypt_ ? Maybe their _certbot_ script ? Well, it can requests certs as an ACME client. +If you love reading documentations, go ahead and do so [here](https://letsencrypt.org/docs/client-options/). + +TLDR: deploying an ACME server on you CA will allow you to requests cetrificates for your local services using scripts like certbot or even [Traefik](https://traefik.io/) (and we'll even talk about this later on). + +### I'm sold, show me the magic + +I knew you'd like it! +Setting up the server isn't that hard, you'll need to add an ACME provisionner: +```sh +step ca provisioner add acme --type ACME +``` +then restart your server (the following command only works if you've made your CA a systemd daemon): +```sh +sudo systemctl restart step-ca +``` + +You can try to request a certificate from your ACME server with: +```sh +step ca certificate --provisioner acme +``` +Please note that this last command might not work if you already have a service listening on port 80 as the `step` command. + +## Actual usage + +Remember that your ACME server must be able to find your servers to give them certificates; you might be good by just requesting certs with IPs but, in case you want to distribute named certificates (don't know if that's how they're called, but you get me), don't forget to add a DNS entry to your local resolver for each server that needs named certs. + +### Traefik + +I have been proxying my services with [Traefik](https://traefik.io/) for almost a year now and I don't regret switching to it one bit; it is easier to expose my conteneurised service, easier to get new certificates, easier to use in general. +I might have had a hard time setting up all the TLS params at the begging but it was all worth it in the end. +I won't do a tutorial on how to setup a Traefik reverse proxy, but I'll just show you how to use it like you'd normally use the Letsencrypt resolver. +To add your ACME server as an SSL certs resolver, add these commands to your Traefik container (you don't have to delete the other resolver to do so, just in case you were thinking about it): +```yaml +# Lets call the resolver myca +# The HTTP entrypoint is called http +# Your caServer's addresse in myca.lan +- "--certificatesresolvers.myca.acme.httpchallenge=true" +- "--certificatesresolvers.myca.acme.httpchallenge.entrypoint=http" +- "--certificatesresolvers.myca.acme.caServer=https://myca.lan/acme/lan.alxczl.fr/directory" +- "--certificatesresolvers.myca.acme.email=me@mail.lan" +- "--certificatesresolvers.myca.acme.storage=/myca/acme.json" +``` +You'll have to: +- Mount the host certs folder to the container's certs folder so that the root CA cert can be retrieved by the container +- Add a volume to the container and mount it to `/myca/`; it'll contain the certs, etc. + +Once your Traefik container is setup, requesting certs from the CA is quite easy; just add the following label: +```yaml +# The router here is called myservice +- "traefik.http.routers.myservice.tls.certresolver=myca" +``` +This will make Traefik automatically fetch the certificates from your CA, just like it would with Let's Encrypt. + +### Certbot + +Using your ACME server with certbot is also pretty easy; you just have to add `--server https://myca.lan/acme/acme/directory` to the command (e.g `certbot certonly -n --standalone -d test.lan --server https://myca.lan/acme/acme/directory`). + + diff --git a/posts/proxmoxsetup.md b/posts/proxmoxsetup.md new file mode 100644 index 0000000..58f92de --- /dev/null +++ b/posts/proxmoxsetup.md @@ -0,0 +1,42 @@ +--- +date: 2022-07-27 +toc: true +title: Proxmox setup +--- + +## Introduction + +This document lists all of the modifications I apply to my proxmox instance after an install. +Feel free to copy them or to send me suggestions to improve/grow this list. + +## Power consumption + +Running a server 24/7 can eat up a lot of watts and proxmox uses a pretty power hungry cpu governor: `performance`, which just puts the CPU at its max freq 100% of the time. +Chosing the `powersave` governor can drastically reduce the power usage of the machine. +First, check your available governors with: +```shell +cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governor +``` + +Then, if `powersave` is available, do: +```shell +echo "powersave" | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor +``` + +This modification will not be persistent after a reboot (it happens sometimes), adding it to your `crontab` will fix that. To do so, add the following entry: +```shell +@reboot echo "powersave" | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor >/dev/null 2>&1 +``` + +You can also install `powertop` to see what process uses the most energy. I had totally forgotten that I had `suricata` running on my pve node, and it was the second hungriest process on the machine. + +## Subscription nag + +Sorry to the Proxmox team, but I can't really afford the license right now, and the nag is rather annoying. +If you can buy it, please do: this software runs all your services and you're actually getting all of this for free; please support the devs by purchasing a license. +I have removed the nag by running this command (as `root`): +```sh +cat << EOF > /etc/apt/apt.conf.d/no-nag-script +DPkg::Post-Invoke { "dpkg -V proxmox-widget-toolkit | grep -q '/proxmoxlib\.js$'; if [ $? -eq 1 ]; then { echo 'Removing subscription nag from UI...'; sed -i '/data.status/{s/\!//;s/Active/NoMoreNagging/}' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js; }; fi"; }; +EOF +``` \ No newline at end of file diff --git a/writeups/mommymorse.md b/writeups/mommymorse.md new file mode 100644 index 0000000..430744c --- /dev/null +++ b/writeups/mommymorse.md @@ -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 +``` diff --git a/writeups/xfactor2.md b/writeups/xfactor2.md new file mode 100644 index 0000000..0984a0a --- /dev/null +++ b/writeups/xfactor2.md @@ -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. + +
+ +
Me when I absolutely HACK my way into the Hypersecret company
+
+ +## 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) + +
+ +
+ + +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. + +
+ +
That's all folks !
+
+ + +## 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: +
+ +
Shamelessly borrowed from the Fido Alliance's website
+
+ +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" + } +] +``` + +
+ +
HOLD UP, SOMETHING AINT RIGHT
+
+ +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 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 present to you, U2F-Emulated. +

+
+ +
Rare picture of Cesar the GOAT making this first blood possible
+
+ +### 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(¶ms, 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(¶ms, 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. + +
+ +
+ +## FLAGGITTY FLAG FLAG +Launch the frankenmulator, hop on the website, login, click on **Check Token** and +![img](https://notes.alxczl.fr/uploads/3d47d9b47a6ee23685015020d.png) +

+ Bruh moment +

+ +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