Skip to content

Commit

Permalink
add bwctf 2024 - kyber noncense
Browse files Browse the repository at this point in the history
  • Loading branch information
josephsurin committed Oct 14, 2024
1 parent cf011a5 commit 509f60c
Show file tree
Hide file tree
Showing 16 changed files with 277 additions and 0 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,15 @@ Fri, 05 July 2024, 19:30 AEST — Sun, 07 July 2024, 19:30 AEST
|[pkijs=](downunderctf-2024/pkijs-eq)|misc|⭐️⭐️⭐️|1|
|[kyber decryption oracle](downunderctf-2024/kyber-decryption-oracle)|crypto|⭐️⭐️⭐️⭐️|1|

# Blue Water CTF 2024
Sun, 13 Oct. 2024, 01:00 AEDT — Mon, 14 Oct. 2024, 13:00 AEDT

[**Website**](https://bw.ctf.ing) | [**CTFTime**](https://ctftime.org/event/2479/)

|Name|Category|Difficulty|Solves|
|---|---|---|---|
|[Kyber Noncense](blue-water-ctf-2024/kyber-noncense)|crypto|⭐️⭐️|4|

# Cyber Apocalypse 2023
Sun, 19 March 2023, 00:00 AEDT — Thu, 23 March 2023, 23:59 AEDT

Expand Down
30 changes: 30 additions & 0 deletions blue-water-ctf-2024/kyber-noncense/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Blue Water CTF 2024 - Kyber Noncense

- **Category:** crypto
- **Solves:** 4/790
- **Difficulty:** ⭐️⭐️
- **Hosting type:** file
- **Tags:** Kyber

---

> Nonce reuse is an issue that affects many different cryptographic schemes. It can render an otherwise secure system completely trivial to break.

Handout files:

- [./publish/build-kyber.sh](./publish/build-kyber.sh)
- [./publish/kyber-noncense.py](./publish/kyber-noncense.py)
- [./publish/libpqcrystals_kyber1024_ref.so](./publish/libpqcrystals_kyber1024_ref.so)
- [./publish/my.patch](./publish/my.patch)
- [./publish/output.txt](./publish/output.txt)

## Solution

Flag: `bwctf{homework_1:solve_with_nonce--_instead_of_--nonce,_homework_2:solve_without_removal_of_compression}`


- [**Solver**](./solve/solv.sage)



20 changes: 20 additions & 0 deletions blue-water-ctf-2024/kyber-noncense/details.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
id: bwctf-2024-kyber-noncense
name: Kyber Noncense
category: crypto
ctf: Blue Water CTF 2024
difficulty: 2
tags: ['Kyber']
notes: ''
description: Nonce reuse is an issue that affects many different cryptographic schemes. It can render an otherwise secure system completely trivial to break.
hosting: file
handout_files:
- ./publish/build-kyber.sh
- ./publish/kyber-noncense.py
- ./publish/libpqcrystals_kyber1024_ref.so
- ./publish/my.patch
- ./publish/output.txt
flag: 'bwctf{homework_1:solve_with_nonce--_instead_of_--nonce,_homework_2:solve_without_removal_of_compression}'
solver: ./solve/solv.sage
solve_stats:
solved_teams: 4
num_teams: 790
1 change: 1 addition & 0 deletions blue-water-ctf-2024/kyber-noncense/publish/build-kyber.sh
1 change: 1 addition & 0 deletions blue-water-ctf-2024/kyber-noncense/publish/my.patch
3 changes: 3 additions & 0 deletions blue-water-ctf-2024/kyber-noncense/publish/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
kyber_pk: 31b05c63f95317f8856e53133af09655876aae742066c4ba2854c664c0a7f5fb8f69768042f196f2051d55272caca355f3bc2d37d6ce3367278cb57b93872b91502da54a19f2d67f7dc5a8b485c2fc479f76f6c66640990290ce32404ef9f1a982669b6901be096b2d82351d733238a942725d687b598b58bbeab24d152454e47eac277ad3975bc527b5eaa497e4c58a4ffac60c72729c231c6bc374f674c9a8a722d7973eba77b7404572323322764b1a52506da669514c270c51c09b19ecc6336c344fd86b75c4163467451eb00883a77b8cd44e71956603242d9204902de3b2a7d467fbeab2eda3996754b30496aa95f665bc70757228bf752abdc89133584b75206ca1d6799b69f9542284627666add1499fadf83ca2760a1a30a32c0a08fe6643a62b9f1950ade90b262058a14bc8960bdb1239128d6696717ba795d85a056d9c8e956532adab0372129be2dcce3eb586a02c8539a5ad5104070ada4a0a65a13982c5d33857ed851d2d24413477831447c3b879b9ec68bdbd148732b357eb885074e29c5ca227ca266b7d655d69424c265c5ee6ecaacf1153fd995bb3d59c8811170df51f4b48591c9c132ab94729fb0c1d3bbd8f1b10fe817963b4c740665426888434d415882428ee4562f04b43c692a06e998771754468ec646c93ce6284737b88791f8ca86b4b0147e7bc2c6a27d5663d2c47bf4b852eb4eacd9589455c22562db65122c3a9d410a9c6384f3f5c499422cb574b765a96a110eb79ffe79cd1200e20200217b78517a01fc0c6cee18c4fabf26de03bcee1c043ff0943279c662b160f90bc744619bb438522bcdb80f840942848017c4579159005e242ac4af01920f39d1bd7507fb105ac5c295b334701575116d2ccfe7c47518a97f8b947b040c6586420d6070510f55e692410b81958edc4392778563d12430392b5f1566cc734a0a5aabd5e587f8eb2aa24e78890b30ab0057d9c0154e938599899c0c272033594bc2b8985c85445c0f129f230b615e3413b3a406632425bb147788b1ee665943306a008daa54281ac588a62ee09c3a45a1fe9da31233a650988a74c7001be838679b14e645874be373ebe787ec9529c4c84a148a41855009196a5a2a8e97ee5c69b6a9743a4b5adcc91187bf4bf84b11c590a15f6b86ed8960c4d14631f347ebb378b3b0843cc068149208bea734dd828671f286498834234b11b415255343975f7a88dcd0972742bab41dcc4d398218c0452493921edf4334c7ac3aa3a3d7905a2e8427e99d10334c2bdc3525e1e9ccd961a4ebaa14e2f6b25cfdaa71435bbad94a35cd669bfea9997169ee148c596232e2d574b13a82f147a85ae329b153957f38261d620187e1b79c1007e77341da012bad7a577e4fbbf499c61a2378fcbb5748cdb242d21abc1969df7385ea6aa5cc085c33a2917bb57173f3c17bfb1cde72608d6da85be008b8d7cc7482543b8d5b3541a1038d9af93c067f4912e4a188b15a8b8a2434c82f0449088c25e543e19d2bc009b54a5968c142068e3555a5e6b12b65394b2818cf904ace62b872019be40264bd4e09f0fd80fe344966845b6b1943ddd96bc366278839311c5a57b5137155a08ba63c814a23268d9e36b55a681cd25c9705a7ab892c9e4dabd751952d8e704c9e2ba5737990b20c50d1b7f5145aa27349848f67c70b02fd77bb2406a91879815896049ea3a474e737063031b62874d8e6cce9d1a3936949f256c98a4419bda33927367483c01c636f9cc41933494305c62259cbbc6bd97e04f9b8c5dde0c6ee4f6670dd0799811671f187d0045c7ca5a0dfd4113298bb4415857a06b76920b30be415a15f25372d52341828b129b575297bc22b38c19458dbc0195d0fc421bfb0c85743a975277266562a5415a491170c8f70a35d357b650a5d4142c27f0bc0c6c7d0a6736bbe636bc42b2cb9a50a723122f649f1545aeb375746f08b512e752c2a5907c387850b0622e1345d1d957c00a8100558c4969642c6535276a188cc9bc342a70651413e1f5ad69b2205c53a94a42ca769c9f41195f8d3b1699867dfda2c299f68d5e5a6c928ac05d0b19898775552b2c2db28e714bc6a8699b60069386034028a5bfc8405378eac410575a39609d2c5167f933a2de8066ad0871c34b6966283f2bbb5d3751f27b4afe203f177e0f3c28567aa62e04a8ad7db1ef4d1f4f95409507fa44
kyber_ct: 2b70b6696642b6486991c20b2e82001e598e8c4c9e8d3531cbbbb135b8072f947dcb140198935179a81c411bc7c8bc01cd014d4aaa205e1203ad2a5dc6d6ced7e42e0f76b2235656ef33513c0956a2dbc93b5015295c3a43948e70e67e02c634de806bcadbad8e795370f89756ec0428586c94acb98f6710be82c1d43b63115bac722705c20915d7295818eb24aaa35405342c11db72e935b180b24362c0c1339b8c472c5668540097dbbb5b98ccb852bbd2daba2680bfea36a9a7614109d2207ea9c0a2512d4c1629b8e32016947f545c61de37a0ed9118721173f9b03d09424227d51bfb27979d47adf7c55fdda2c37228af1629759dcc2884d89b9d334e817bb1c4200172316b58a0b5ff133ea6f372ed7c888f88aed0f752bc592b4e6550ed2b279d1cb5e367605a5350fcd51258bac395c6216f0995a5061700e587206a6512983e71a1925f178b8c389768cc9e8788433e16006cb05a256c8d3e1439f38c4972d6c087f4aa94a77888451c9caa3164b03f57128f962a871c99beebb30ee9912ededcbe06ecb3eba23603eb736fb42101bb0e84bab741b82d2438c964557546b3cb15478a8fc57cb3655af50c0ea070af9a4182a919c710b39d21d3a321650cce17578707218d4a001516671f19710d20b025262e22885d91391b581880bf1a7de0352f0b1c5b9755677809a5944489310816ac3734ece7496636a8b05083891489790681cf747dc5120c804a2408940033f8418849b46157a850693c6a648280637c8897b0495b5ca5ea2e99e80dde65037c7466fe253c78c1b29be041a4bc8acf7a8ea47abbffa65804613a10e771df5771b25527eedb7ec847bd18590c2b5604db23bf23cb43a67c2b2e88cdb6c9b44e671973357d96b7184a363a8fe9153b117321208dabf60168440d800a6e829386e788383a22c937c45ee56c59777a1975ab7b79f46156f1cf682595e7cb81d6e3cf25b4a450552681684c911c8667c242894357cc513196f5c7ed171ffc370ecf7c76c82406692c2972a981c383ad2ba951c9e5a58fdac40f058ee6ea9b3fb130f6b51784b2a5613594d64b161da217502215832c3b0a5c56dd668d16652b5f7c31c25565d2a246ce1a616046135d5a2c4e7a89fa224f668c9a58e5a5fe0415e4e6bf4aba34e8148e11351e8e44cf492331952225f05065b2a638689b02b4434de0d52d90a623fcd3970f7441db7889f7661c9e30c35205ad60492cbfd639fdaa9aa7e02f24f988efb7b8e3841b33a212da1b115cd7baeed409d6f39caee1b9e386c9fc6696603675562c896ac76815910a6b1c778d24207944c52e8580e61455b0b114c400a09aba0ecf26a607c4af8a3a491c357f30a1152184c9365acdf3c518b73c8b8c1377a92363237a1098b98ec3a8a5362cb37cf49bbb4b335fe7cbe9a98a123b25c39b77395524da941256244c2a184f4f6930d83b4618bac8c9117fd1752e01647d214b275c284538964b48378cac7697a83a3772a052d49b1120ebab821a9d35f93271f6ad66480400c95b63bc6a81047950d92936c58bb2280923b56046a8b1a17760a35181dc199664b3c306b488cda38f6cc27b37d96b4e40bdec020162a092bf2370d9dbc35778207f644dbc943f15e44185656b946a480959c323896ea48ab8ee99b281359ddfca41b5a4206918ce4c928f8ac15bcd255f23f927fbc6888b1aca37083eae27678bfa3ac50a19266378b647961aa0745c499736f76a55496386b83a726380a06b3f97f630a022b7a3501165621f77034c6bec7ca5ab9326b521e1f8b1ca3018861c0ae3135baac167772c02b75baddb5a96cc2b52d5769c1583af97f43df151161da8769756b427782180fc23ac9b74a0d98ee28123a0e6c279d32c613176726b754ec7739ec73aa53118a5cb3501258669643ef7ac9fdb280c5a286ffc8b638bf2354df918ba6656e84b022961cf5ca536d2c8ab376b52816a6ad3bcb032bb1f99550eb5e8c09221299d1896af648ff14615dc6c31b64a3d8d6c3fd5c6c188049352375d57ea76a72715728847d1d069ad616e4e6255df40b86b5a5335da251ee99f08653bb9a494c6a2aff310671e150f4fcb831ffa4e34b7b9e9a95c24b08eb784bd6733087b90ac964c496fb042a7169f6e3c61ab9cb954c123890342f3c857f607d53fb2d9f1326ae7fad7b40ce6091498127f63bd06bc9697833ba8801ac97c8417caa5f088a97b3051a97d40a271caa6938fbf5b1d16cc48fe752b155588ecafaab4891cd234b6ad1f395b566221145bc1175121166001d5b11dc136d40aa1b23d1da531b76ef94ad3e3d531c2a00b57dbe8b223136095c0bc84a4eea11df6b92940b246268ba496401a25401f7885317e0e77c5ceb715846fa5e5
flag_ct: 51b15dda6af16ceb54b5159cd21fa871a7c6ea5f72829d2d4500b92767d9049edc20c9812405be9825be95f7f9d93dd07d4ef212623eb241469024ab709ef6b3514e6435bdb6e9259112cf6196241227ae62af68222d8428c5eb855f6863ce159e9add1b9acc8f76
69 changes: 69 additions & 0 deletions blue-water-ctf-2024/kyber-noncense/solve/kyber1024_util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from sage.all import *
import ctypes
import hashlib

kyber_lib = ctypes.CDLL('../publish/libpqcrystals_kyber1024_ref.so')
q = 3329
k = 4
F = GF(q)
P = PolynomialRing(F, 'X')
P.inject_variables()
R = P.quotient_ring(X**256 + 1, 'Xbar')

def hash_h(m):
return hashlib.sha3_256(m).digest()

def hash_g(m):
return hashlib.sha3_512(m).digest()

def kdf(m):
return hashlib.shake_256(m).digest(32)

def bytes_to_polyvec(b):
polyvec = (ctypes.c_int16 * int(k * 256))()
kyber_lib.pqcrystals_kyber1024_ref_polyvec_frombytes(polyvec, ctypes.c_buffer(b))
return vector(R, [R(list(polyvec)[:256]), R(list(polyvec)[256:512]), R(list(polyvec)[512:768]), R(list(polyvec)[768:1024])])

def compressed_bytes_to_poly(b):
poly = (ctypes.c_int16 * int(256))()
kyber_lib.pqcrystals_kyber1024_ref_poly_decompress(poly, ctypes.c_buffer(b))
return R(list(poly))

def poly_frommsg(m):
poly = (ctypes.c_int16 * int(256))()
kyber_lib.pqcrystals_kyber1024_ref_poly_frommsg(poly, ctypes.c_buffer(m))
return R(list(poly))

def poly_tomsg(p):
poly = (ctypes.c_int16 * int(256))(*list(p))
buf = ctypes.c_buffer(32)
kyber_lib.pqcrystals_kyber1024_ref_poly_tomsg(buf, poly)
return bytes(buf)

def unpack_pk(pk_bytes):
buf = pk_bytes[:k * 384]
pv = bytes_to_polyvec(buf)
seed = pk_bytes[k * 384:]
return pv, seed

def unpack_sk(sk_bytes):
return bytes_to_polyvec(sk_bytes)

def unpack_ciphertext(ct_bytes):
b = bytes_to_polyvec(ct_bytes[:k*384])
v = compressed_bytes_to_poly(ct_bytes[k*384:k*384+160])
return (b, v)

def gen_matrix(seed, transposed=0):
out = ((ctypes.c_int16 * int(k * 256)) * int(k))()
kyber_lib.pqcrystals_kyber1024_ref_gen_matrix(out, ctypes.c_buffer(seed), transposed)
return Matrix(R, [vector(R, [R(list(out)[i][j*256:(j+1)*256])for j in range(4)]) for i in range(k)])

def poly_invntt(p):
t = (ctypes.c_int16 * int(256))(*list(p))
kyber_lib.pqcrystals_kyber1024_ref_invntt(t)
t = R(list(t)) / 2**16
return t

def polyvec_invntt(pv):
return vector(R, [poly_invntt(p) for p in pv])
62 changes: 62 additions & 0 deletions blue-water-ctf-2024/kyber-noncense/solve/solv.sage
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from Crypto.Cipher import AES

import kyber1024_util as kyber_util

"""
Let v_i be the elements returned by each call to poly_getnoise_eta2 within the
encapsulation procedure. Because the nonce is decremented, some of the random
error term values are the same. We have:
r = (v0, v1, v2, v3)
e1 = (v3, v2, v1, v0)
e2 = v0
u = Ar + e1
v = t*r + e2 + m
u = (
A00 v0 + A01 v1 + A02 v2 + A03 v3 + v3,
A10 v0 + A11 v1 + A12 v2 + A13 v3 + v2,
A20 v0 + A21 v1 + A22 v2 + A23 v3 + v1,
A30 v0 + A31 v1 + A32 v2 + A33 v3 + v0,
) = (
A00 v0 + A01 v1 + A02 v2 + (A03 + 1) v3,
A10 v0 + A11 v1 + (A12 + 1) v2 + A13 v3,
A20 v0 + (A21 + 1) v1 + A22 v2 + A23 v3,
(A30 + 1) v0 + A31 v1 + A32 v2 + A33 v3,
)
v = t0 v0 + t1 v1 + t2 v2 + t3 v3 + v0 + m
(v0, v1, v2, v3) can be recovered from u by solving a system of equations.
These then allow for recovery of m.
Then we can follow the procedure to compute the shared secret from the
decrypted randomness and use it to decrypt the flag.
"""

data = open('../publish/output.txt', 'r').read().splitlines()
kyber_pk = bytes.fromhex(data[0].split(': ')[1])
kyber_ct = bytes.fromhex(data[1].split(': ')[1])
flag_ct = bytes.fromhex(data[2].split(': ')[1])

t, A_seed = kyber_util.unpack_pk(kyber_pk)
A = kyber_util.gen_matrix(A_seed)
t = kyber_util.polyvec_invntt(t)
A = Matrix(kyber_util.R, [kyber_util.polyvec_invntt(a) for a in A])

c1, c2 = kyber_util.unpack_ciphertext(kyber_ct)

A_plus_e1 = Matrix(A).T
A_plus_e1 += Matrix([
[0, 0, 0, 1],
[0, 0, 1, 0],
[0, 1, 0, 0],
[1, 0, 0, 0],
])
r = ~A_plus_e1 * c1
m = c2 - t * r - r[0]
msg = kyber_util.poly_tomsg(m)
kr = kyber_util.hash_g(msg + kyber_util.hash_h(kyber_pk))
ss = kr[:32]

flag = AES.new(bytes(ss), AES.MODE_CTR, nonce=b'x'*12).decrypt(flag_ct)
print(flag.decode())
10 changes: 10 additions & 0 deletions blue-water-ctf-2024/kyber-noncense/src/build-kyber.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/sh

git clone https://github.com/pq-crystals/kyber.git
cd kyber/ref
git checkout 10b478fc3cc4ff6215eb0b6a11bd758bf0929cbd
git apply ../../my.patch
gcc -shared -O0 -fPIC -DKYBER_K=4 randombytes.c fips202.c symmetric-shake.c indcpa.c polyvec.c poly.c ntt.c cbd.c reduce.c verify.c kem.c -o libpqcrystals_kyber1024_ref.so
cp libpqcrystals_kyber1024_ref.so ../../libpqcrystals_kyber1024_ref.so
cd ../../
rm -rf kyber
1 change: 1 addition & 0 deletions blue-water-ctf-2024/kyber-noncense/src/flag.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bwctf{homework_1:solve_with_nonce--_instead_of_--nonce,_homework_2:solve_without_removal_of_compression}
27 changes: 27 additions & 0 deletions blue-water-ctf-2024/kyber-noncense/src/kyber-noncense.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import os
from ctypes import CDLL, c_buffer
from Crypto.Cipher import AES

FLAG = os.getenv('FLAG', 'bwctf{testflag}').encode()

kyber_lib = CDLL('./libpqcrystals_kyber1024_ref.so')
class Kyber:
def __init__(self):
self.pk_buf = c_buffer(1568)
self.sk_buf = c_buffer(3168)
kyber_lib.pqcrystals_kyber1024_ref_keypair(self.pk_buf, self.sk_buf)

def kem_enc(self):
ct_buf = c_buffer(1696)
ss_buf = c_buffer(32)
kyber_lib.pqcrystals_kyber1024_ref_enc(ct_buf, ss_buf, self.pk_buf)
return bytes(ct_buf), bytes(ss_buf)

kyber = Kyber()
kyber_ct, ss = kyber.kem_enc()

flag_ct = AES.new(ss, AES.MODE_CTR, nonce=b'x'*12).encrypt(FLAG)

print('kyber_pk:', bytes(kyber.pk_buf).hex())
print('kyber_ct:', kyber_ct.hex())
print('flag_ct:', flag_ct.hex())
Binary file not shown.
37 changes: 37 additions & 0 deletions blue-water-ctf-2024/kyber-noncense/src/my.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
diff --git a/ref/indcpa.c b/ref/indcpa.c
index 9a78c09..8aeb3bc 100644
--- a/ref/indcpa.c
+++ b/ref/indcpa.c
@@ -85,8 +85,8 @@ static void unpack_sk(polyvec *sk, const uint8_t packedsk[KYBER_INDCPA_SECRETKEY
**************************************************/
static void pack_ciphertext(uint8_t r[KYBER_INDCPA_BYTES], polyvec *b, poly *v)
{
- polyvec_compress(r, b);
- poly_compress(r+KYBER_POLYVECCOMPRESSEDBYTES, v);
+ polyvec_tobytes(r, b);
+ poly_compress(r+KYBER_POLYVECBYTES, v);
}

/*************************************************
@@ -275,7 +275,7 @@ void indcpa_enc(uint8_t c[KYBER_INDCPA_BYTES],
for(i=0;i<KYBER_K;i++)
poly_getnoise_eta1(sp.vec+i, coins, nonce++);
for(i=0;i<KYBER_K;i++)
- poly_getnoise_eta2(ep.vec+i, coins, nonce++);
+ poly_getnoise_eta2(ep.vec+i, coins, --nonce);
poly_getnoise_eta2(&epp, coins, nonce++);

polyvec_ntt(&sp);
diff --git a/ref/params.h b/ref/params.h
index 0802c74..365a358 100644
--- a/ref/params.h
+++ b/ref/params.h
@@ -45,7 +45,7 @@
#define KYBER_INDCPA_MSGBYTES (KYBER_SYMBYTES)
#define KYBER_INDCPA_PUBLICKEYBYTES (KYBER_POLYVECBYTES + KYBER_SYMBYTES)
#define KYBER_INDCPA_SECRETKEYBYTES (KYBER_POLYVECBYTES)
-#define KYBER_INDCPA_BYTES (KYBER_POLYVECCOMPRESSEDBYTES + KYBER_POLYCOMPRESSEDBYTES)
+#define KYBER_INDCPA_BYTES (KYBER_POLYVECBYTES + KYBER_POLYCOMPRESSEDBYTES)

#define KYBER_PUBLICKEYBYTES (KYBER_INDCPA_PUBLICKEYBYTES)
/* 32 bytes of additional space to save H(pk) */
5 changes: 5 additions & 0 deletions ctfs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@
description: ""
date: "Fri, 05 July 2024, 19:30 AEST — Sun, 07 July 2024, 19:30 AEST"
repo: https://github.com/DownUnderCTF/Challenges_2024_Public
- name: Blue Water CTF 2024
website: https://bw.ctf.ing
ctftime: https://ctftime.org/event/2479/
description: ""
date: "Sun, 13 Oct. 2024, 01:00 AEDT — Mon, 14 Oct. 2024, 13:00 AEDT"
- name: Cyber Apocalypse 2023
website: https://ctf.hackthebox.com/event/details/cyber-apocalypse-2023-the-cursed-mission-821
ctftime: https://ctftime.org/event/1889/
Expand Down

0 comments on commit 509f60c

Please sign in to comment.