Post

Cryptography CTFs - Basics

Cryptography CTFs - Basics

Introduzione

Per risolvere challenge CTF di crittografia serve mettere in pratica competenze di lettura e scrittura codice e comprensione matematica più o meno avanzata.

La mia attualmente è meno avanzata, ma è un work in progress

Encoding

Per questioni di comodità, le stesse informazioni posso essere codificate in modalità diverse.

Nelle challenge di crittografia, è importantissimo saper manipolare stringhe e numeri e allo stesso modo conoscere e lavorare con i diversi tipi di encoding.

Manipolazione Ascii

Il codice ascii codifica i carattere che vediamo stampati a schermo ad un numero (o codice). https://it.wikipedia.org/wiki/ASCII

python:

1
2
chr(c) # from ascii (Dec) to char (es. chr(117) -> 'u')
ord(n) # from char to ascii (Dec) (es. ord('u') -> 117)

c:

1
2
3
4
5
char* stringa = "una serie di caratteri";
int ascii = stringa[0]; // conversione implicita
printf("%d\n",ascii);

//output: 117 (ascii decimale di u)

Sappi che questi numeri sono la rappresentazione decimale di ascii. Altre rappresentazioni sono esadecimale, binario.

Spesso avremo a che fare con file testuali che conterranno numeri. Una funzione per ottenere una stringa (char*) che contiene il file in input Qui

Conversioni Stringa-Intero

C:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// --- Da stringa a intero ---

// Fino a 10 cifre (32-bit)
char* str_piccola = "345678";
int num_piccolo = atoi(str_piccola);

// Fino a 19 cifre (64-bit)
char* str_media = "123456789012345678";
unsigned long long int num_medio = strtoull(str_media, NULL, 10);

// 20 cifre in poi -> vai al capitolo Grandi Numeri


// --- Da intero a stringa ---

int num = 345678;
char buffer[12]; // cifre + eventuale segno + '\0'
snprintf(buffer, sizeof(buffer), "%d", num);

Python:

1
2
3
4
5
# Nessun limite di grandezza in Python

# Da stringa a intero
testo = "123456789012345678901234567890" 
numero = int(testo)

Manipolazione Esadecimale

L’esadecimale (base 16) è ovunque nei CTF, specialmente per rappresentare dati grezzi e ciphertext.

Python:

1
2
3
4
5
6
7
8
9
# Da numero a hex e viceversa
numero = 255
valore_hex = hex(numero) # '0xff'
nuovo_numero = int(valore_hex, 16) # 255

# Da stringa/bytes a hex e viceversa
testo = b"CTF"
testo_hex = testo.hex() # '435446'
testo_originale = bytes.fromhex(testo_hex) # b'CTF

Manipolazione Bytes

In Python 3 c’è una netta differenza tra le stringhe ("testo") e i byte (b"testo"). La crittografia opera quasi esclusivamente sui byte.

1
2
3
4
5
6
stringa = "ciao"
# Codificare in bytes
dati_bytes = stringa.encode() # b'ciao'

# Decodificare da bytes a stringa
stringa_recuperata = dati_bytes.decode() # 'ciao'

Una delle librerie più utili per i CTF crypto è pycryptodome. Contiene due funzioni essenziali per convertire interi testi in numeri e viceversa:

1
2
3
4
5
from Crypto.Util.number import bytes_to_long, long_to_bytes

testo = b"flag{test}"
numero = bytes_to_long(testo) # Trasforma l'intera frase in un blocco numerico
testo_orig = long_to_bytes(numero)

Base64

Un encoding che trasforma qualsiasi dato binario in una stringa di testo leggibile composta da 64 caratteri (A-Z, a-z, 0-9, +, /). Riconoscibile perché spesso finisce con uno o due segni di uguale (= o ==) usati come padding.

Prende l’input a blocchi di 3 byte alla volta. Poiché 1 byte = 8 bit, un blocco intero è composto da 24 bit (3 x 8). Spezza quei 24 bit in 4 gruppi da 6 bit ciascuno (4 x 6 = 24) e mappa ogni gruppo (6 bit possono rappresentare 64 caratteri) al suo carattere decimale corrispondente (0=A, 1=B, …, 62=+, 63=/).

1
2
3
4
5
6
import base64

dati_segreti = b"SuperSegreto"
codificato = base64.b64encode(dati_segreti) # b'U3VwZXJTZWdyZXRv'

decodificato = base64.b64decode(codificato) # b'SuperSegreto'

Grandi numeri

Nella crittografia asimmetrica (come RSA) si usano numeri lunghi centinaia di cifre.

  • Python: Python gestisce interi di grandezza infinita in modo nativo.
  • C: I tipi base (int, long long) non bastano. Bisogna usare librerie esterne specifiche, come la GNU Multiple Precision Arithmetic Library (GMP). Utilizzerò principalmente questa nei prossimi articoli e ne farò di appositi, tanto per velocità quanto per puro sport.

Qui un mio piccolo cheatsheet per libgmp su C:

XOR

L’operatore logico bit a bit (Exclusive OR). È la base di tantissimi cifrari. In codice si rappresenta col simbolo ^.

Proprietà matematiche fondamentali dello XOR:

XOR di un elemento con zero ritorna l’elemento stesso

\[A \oplus 0 = A\]

XOR di un elemento con sè stesso dà zero:

\[A \oplus A = 0\]

esempi:


\[A \oplus A \oplus B = B\] \[A \oplus B \oplus C \oplus B \oplus C = A\]

proprietà di invertibilità

\[A \oplus B = C \implies C \oplus B = A\]
1
2
3
4
5
6
7
8
9
# XOR singolo
a = 5  # 0101
b = 3  # 0011

c = a ^ b # 6 (0110)

# XOR tra bytestrings (usando pwntools)
from pwn import xor
cifrato = xor(b"flag", b"key")

Server

Spesso bisogna interagire con server remoto (es. nc indirizzo.del.server.ctf 1337) che genera challenge dinamiche a cui devi rispondere in tempo reale.

esempio di interazione con un server usando pwntools:

1
2
3
4
5
6
7
8
9
10
11
from pwn import *

# Connessione al server
p = remote('indirizzo.del.server.ctf', 1337)

# manda b"2" dopo la ricezione della sequenza b"> "
p.sendlineafter(b"> ", b"2")

text = p.recvuntil(b"Round")
num = int(text.split(b"was ")[1].split(b"\n")[0])
print(p.recvregex(b"flag{.*}"))

Tools

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