This shows you the differences between two versions of the page.
ic:teme:tema2022 [2022/11/07 21:22] mihai.bogatu |
ic:teme:tema2022 [2022/11/12 03:36] (current) razvan.smadu [Task 2 - Differential Cryptanalysis (50p)] |
||
---|---|---|---|
Line 1: | Line 1: | ||
===== Tema ===== | ===== Tema ===== | ||
- | Deadline: TODO | + | Deadline: **21.11.2022**, ora **23:59** |
+ | |||
+ | === Actualizări === | ||
+ | |||
+ | **12.11.2022** - Am adăugat cerința și scheletul pentru exercițiul 2. | ||
==== Task 1 - Break authentication (50p) ==== | ==== Task 1 - Break authentication (50p) ==== | ||
- | Capture-the-flag: doriti ca sa capturati flag-ul unui server care este protejat sub un cont administrativ. Va trebui sa obinem un tag valid de administrator. Se observa ca serverul ne da un cont de guest care insa nu are access la flag. Din fericire am reusit sa extragem codul sursa al serverului dar se pare ca secretul nu este in codul sursa. | + | Capture-the-flag: Doriți să capturați flagul unui server care este protejat sub un cont administrativ. Va trebui să obțineți un tag valid de administrator. Serverul ne oferă și un cont de guest care însă nu are access la acest flag. Din fericire, ați reușit să extrageți codul sursă de pe server, dar se pare că nu conține flagul secret. |
<note> | <note> | ||
- | Flag format: "CTF{(secret)}" | + | Formatul flagului: "CTF{''(secret)''}" |
</note> | </note> | ||
- | Acest exercitiu va testa capacitatea de a intelege constructii criptografice analizand cod sursa si folosirea de criptanaliza pentru a demonstra ca un cipher este nesigur. In final va fi nevoie sa construiti un proof-of-concept care demonstreaza un atac eficient, executand atacul impotriva unui server. | + | Acest exercițiu vă va testa capacitatea de a înțelege construcții criptografice, analizând codul sursă și folosirea de criptanaliză pentru a demonstra că un cipher este nesigur. În final, va fi nevoie să construiți un proof-of-concept care demonstrează un atac eficient, executând atacul împotriva unui server. |
Recomandare de rezolvare: | Recomandare de rezolvare: | ||
- | - Intelegerea codului sursa si a parametriilor care lipsesc din el. | + | - Înțelegerea codului sursă și a parametrilor care lipsesc din el |
- | - Notarea parametriilor constanti cunoscuti si necunoscuti (eg: siruri de caractere, lungimi, etc.) | + | - Notarea parametrilor constanți cunoscuți și necunoscuți (de exemplu: șiruri de caractere, lungimi, etc.) |
- | - Extragerea parametriilor de pe server | + | - Extragerea parametrilor de pe server |
- | - Atac impotriva algoritmului | + | - Atac împotriva algoritmului |
- | - In prima faza puteti descarca serverul | + | - În primă fază, puteți descărca codul sursă care rulează pe server (vedeți mai jos fișierul server.py) |
- | - Creati un fisier cu secretz, adaugati parametrii extrasi din pasul 2, si inlocuiti flag-ul cu o valoare dummy | + | - Creați un fișier secretz.py, în care adăugați parametrii extrași din pasul 2 și înlocuiți flagul cu o valoare dummy |
- | - Rulati serverul local. Puteti sa modificati codul sursa al serverului offline. Recomandarea este sa folositi metode de debugging si sa atacati algoritmul pe bucati | + | - Rulați serverul local. Puteți să modificați codul sursă al serverului pentru a rula offline. Recomandarea este să folosiți metode de debugging și să atacați algoritmul pe bucăți |
- | - Replicati atacul pe serverul online de la adresa mentionata | + | - Replicați atacul pe serverul online de la adresa menționată |
- | - ??? | + | - Coffee break ☕ |
- | - Capture the flag $ | + | - Capture the flag 😎 |
- | Source-code server: | + | Codul sursă care rulează și pe server: |
<code python server.py> | <code python server.py> | ||
Line 47: | Line 51: | ||
AES_KEY_SIZE = 16 | AES_KEY_SIZE = 16 | ||
- | def ctr_xor(txt, rnd): | + | |
+ | def byte_xor(txt, rnd): | ||
return bytes(a ^ b for a, b in zip(txt, rnd)) | return bytes(a ^ b for a, b in zip(txt, rnd)) | ||
Line 55: | Line 60: | ||
KEY = Random.get_random_bytes(AES_KEY_SIZE) | KEY = Random.get_random_bytes(AES_KEY_SIZE) | ||
self.IV = Random.get_random_bytes(AES_KEY_SIZE) | self.IV = Random.get_random_bytes(AES_KEY_SIZE) | ||
- | self.C = AES.new(KEY,AES.MODE_ECB) | + | self.C = AES.new(KEY, AES.MODE_ECB) |
+ | self.INTEGRITY = AES.new(KEY, AES.MODE_ECB) | ||
- | KEY2 = Random.get_random_bytes(AES_KEY_SIZE) | + | def getIntegrity(self, plain): |
- | self.CRC = AES.new(KEY,AES.MODE_ECB) | + | return self.INTEGRITY.encrypt(b'\x00' * (AES_KEY_SIZE - len(plain)) + plain)[0:INTEGRITY_LEN] |
- | def getCRC(self,plain): | + | def encrypt(self, plain): |
- | return self.CRC.encrypt(b'\x00'*(AES_KEY_SIZE-len(plain))+plain)[0:CRC_LEN] | + | |
- | + | ||
- | def encrypt(self,plain): | + | |
rnd = self.C.encrypt(self.IV) | rnd = self.C.encrypt(self.IV) | ||
- | cipher = ctr_xor(plain,rnd) + SERVER_PUBLIC_BANNER + self.getCRC(plain) | + | cipher = byte_xor(plain, rnd) + SERVER_PUBLIC_BANNER + self.getIntegrity(plain) |
return cipher | return cipher | ||
- | | + | |
- | def decrypt(self,input): | + | def decrypt(self, input): |
rnd = self.C.encrypt(self.IV) | rnd = self.C.encrypt(self.IV) | ||
- | secret_len = (CRC_LEN+len(SERVER_PUBLIC_BANNER)) | + | secret_len = INTEGRITY_LEN + len(SERVER_PUBLIC_BANNER) |
- | cipher,secret,crc = input[:-secret_len],input[-secret_len:-CRC_LEN], input[-CRC_LEN:] | + | cipher, secret, tag = input[:-secret_len], input[-secret_len:-INTEGRITY_LEN], input[-INTEGRITY_LEN:] |
- | plain = ctr_xor(cipher,rnd) | + | plain = byte_xor(cipher, rnd) |
if secret != SERVER_PUBLIC_BANNER: | if secret != SERVER_PUBLIC_BANNER: | ||
return -1 | return -1 | ||
- | if self.getCRC(plain) != crc: | + | if self.getIntegrity(plain) != tag: |
return None | return None | ||
- | | + | |
return plain | return plain | ||
+ | |||
def get_guest_token(): | def get_guest_token(): | ||
Line 84: | Line 88: | ||
token = C.encrypt(GUEST_NAME) | token = C.encrypt(GUEST_NAME) | ||
print(b64.b64encode(token).decode('raw_unicode_escape')) | print(b64.b64encode(token).decode('raw_unicode_escape')) | ||
+ | |||
def login(): | def login(): | ||
global C | global C | ||
try: | try: | ||
- | s = input("Token:") # Python3. Don't get funny RCE ideas | + | s = input("Token:") # Python3. Don't get funny RCE ideas |
cipher = b64.b64decode(s) | cipher = b64.b64decode(s) | ||
- | if(len(cipher)>16): | + | if(len(cipher) > 16): |
print("Tokens must be smaller than 16 bytes!") | print("Tokens must be smaller than 16 bytes!") | ||
plain = C.decrypt(cipher) | plain = C.decrypt(cipher) | ||
Line 100: | Line 105: | ||
print("Failed integrity check!") | print("Failed integrity check!") | ||
elif plain == GUEST_NAME: | elif plain == GUEST_NAME: | ||
- | print("Secret:","No secrets for anonymous") | + | print("Secret:", "No secrets for anonymous") |
elif plain == b"Ephvuln": | elif plain == b"Ephvuln": | ||
- | print("Secret:",FLAG) | + | print("Secret:", FLAG) |
else: | else: | ||
- | print("I don't have an answer for",plain.decode('utf-8')) | + | print("I don't have an answer for", plain.decode('utf-8')) |
except: | except: | ||
print("No h3k1ng allowed") | print("No h3k1ng allowed") | ||
exit() | exit() | ||
- | | + | |
- | + | ||
def invalid(): | def invalid(): | ||
print("! Invalid option.") | print("! Invalid option.") | ||
+ | |||
def menu(): | def menu(): | ||
Line 120: | Line 126: | ||
while True: | while True: | ||
print(options_txt, end='') | print(options_txt, end='') | ||
- | switch={ | + | switch = { |
- | "1":get_guest_token, | + | "1": get_guest_token, |
- | "2":login, | + | "2": login, |
- | "3":exit | + | "3": exit |
} | } | ||
- | func = switch.get(input(),invalid) | + | func = switch.get(input(), invalid) |
func() | func() | ||
print() | print() | ||
- | if __name__=="__main__": | + | |
+ | if __name__ == "__main__": | ||
menu() | menu() | ||
</code> | </code> | ||
- | Serverul se găsește la adresa: [[TODO]] | + | Serverul se găsește la adresa: [[|141.85.224.117:1337]] |
<note> | <note> | ||
- | Pentru testare conexiune incercati sa folositi netcat: <code>nc 141.85.224.117 1337</code> | + | Pentru testarea conexiunii, încercați să folosiți netcat: <code>nc 141.85.224.117 1337</code> |
</note> | </note> | ||
- | Pentru interactiune automata cu serverul aveti la dispozitie urmatorul schelet de cod care necesita pachetul pwntools. | + | Pentru interacțiunea automată cu serverul, aveți la dispoziție următorul schelet de cod, care necesită pachetul pwntools. |
Pentru instalare: <code> pip3 install pwntools </code> | Pentru instalare: <code> pip3 install pwntools </code> | ||
<code python skel.py> | <code python skel.py> | ||
Line 151: | Line 158: | ||
return bytes([_a ^ _b for _a, _b in zip(ba1, ba2)]) | return bytes([_a ^ _b for _a, _b in zip(ba1, ba2)]) | ||
- | LOCAL = True # Local means that you run binary directly | + | LOCAL = True # Local means that you run binary directly |
if LOCAL: | if LOCAL: | ||
- | r = process("<PATH_TO_PYTHON_CHALLENGE>") # Complete this if you want to test locally | + | # Complete this if you want to test locally |
+ | r = process("<PATH_TO_PYTHON_CHALLENGE>") | ||
else: | else: | ||
- | r = remote("141.85.224.117",1337) # Complete this if changed | + | r = remote("141.85.224.117", 1337) # Complete this if changed |
def read_options(): | def read_options(): | ||
Line 174: | Line 182: | ||
read_options() | read_options() | ||
r.sendline(b"2") | r.sendline(b"2") | ||
- | #sleep(0.01) # Uncoment this if server rate-limits you too hard | + | # sleep(0.01) # Uncoment this if server rate-limits you too hard |
r.sendline(b64.b64encode(tag)) | r.sendline(b64.b64encode(tag)) | ||
r.readuntil(b"Token:") | r.readuntil(b"Token:") | ||
- | response= r.readline().strip() | + | response = r.readline().strip() |
return response | return response | ||
Line 186: | Line 194: | ||
# .. | # .. | ||
# .. | # .. | ||
- | # response = login(b64.b64encode(payload)).decode('utf-8') | + | # response = login(payload).decode('utf-8') |
# if "CTF" in response: | # if "CTF" in response: | ||
# print("[*] Found flag:",response) | # print("[*] Found flag:",response) | ||
Line 194: | Line 202: | ||
r.close() | r.close() | ||
</code> | </code> | ||
+ | |||
<note important> | <note important> | ||
- | Atentie! In cazul in care se trimit prea multe request-uri serverul va actiona un rate-limiting. Recomandarea este ca sa puneti un *sleep* atunci cand trimiteti pachete in cadrul unei structuri repetitive. | + | Atenție! În cazul în care se trimit prea multe request-uri, serverul va acționa un [[https://en.wikipedia.org/wiki/Rate_limiting|rate-limiting]]. Recomandarea este să puneți un **sleep** atunci când trimiteți pachete în cadrul unei structuri repetitive. |
</note> | </note> | ||
- | ==== Task 2 - TODO (50p) ==== | + | ==== Task 2 - Differential Cryptanalysis (50p) ==== |
- | TODO | + | |
+ | Descrierea task-ului se poate găsi în fișierul {{ic:teme:tema2022_ex2.pdf}}, iar scheletul de la acest exercițiu este diponibil în {{ic:teme:skel2022_ex2.zip}}. | ||
+ | |||
+ | <note tip> | ||
+ | Serverul se găsește la adresa: [[|141.85.224.119:1337]] | ||
+ | </note> | ||
+ | ==== Trimitere Temă ==== | ||
+ | |||
+ | Rezolvarea trebuie trimisă într-o arhivă pe Moodle [1] care să conțină câte un folder pentru fiecare task în care să aveți un script care rulează tema și un fișier README în care descrieți pe scurt abordarea voastra. Puteți avea și fișiere adiționale pe lângă script-ul principal, dar trebuie să scrieți în README cum trebuie rulată soluția. | ||
+ | |||
+ | [1] https://curs.upb.ro/2022/mod/assign/view.php?id=77709 | ||
+ |