Tema

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)

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.

Formatul flagului: “CTF{(secret)}”

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:

  1. Înțelegerea codului sursă și a parametrilor care lipsesc din el
  2. Notarea parametrilor constanți cunoscuți și necunoscuți (de exemplu: șiruri de caractere, lungimi, etc.)
  3. Extragerea parametrilor de pe server
  4. Atac împotriva algoritmului
    1. În primă fază, puteți descărca codul sursă care rulează pe server (vedeți mai jos fișierul server.py)
    2. 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
    3. 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
    4. Replicați atacul pe serverul online de la adresa menționată
  5. Coffee break ☕
  6. Capture the flag 😎

Codul sursă care rulează și pe server:

server.py
#!/usr/bin/python3
from Crypto import Random
from Crypto.Cipher import AES
import base64 as b64
from secretz import *
 
options_txt = """Options:
1. Get guest token
2. Login
3. Exit
Input:"""
 
welcome_txt = """Welcome to SRY, most confidential platform with top-notch integrity.
To get public data login as guest."""
 
GUEST_NAME = b"Anonymous"
AES_KEY_SIZE = 16
 
 
def byte_xor(txt, rnd):
    return bytes(a ^ b for a, b in zip(txt, rnd))
 
 
class Crypt:
    def __init__(self):
        KEY = 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.INTEGRITY = AES.new(KEY, AES.MODE_ECB)
 
    def getIntegrity(self, plain):
        return self.INTEGRITY.encrypt(b'\x00' * (AES_KEY_SIZE - len(plain)) + plain)[0:INTEGRITY_LEN]
 
    def encrypt(self, plain):
        rnd = self.C.encrypt(self.IV)
        cipher = byte_xor(plain, rnd) + SERVER_PUBLIC_BANNER + self.getIntegrity(plain)
        return cipher
 
    def decrypt(self, input):
        rnd = self.C.encrypt(self.IV)
        secret_len = INTEGRITY_LEN + len(SERVER_PUBLIC_BANNER)
        cipher, secret, tag = input[:-secret_len], input[-secret_len:-INTEGRITY_LEN], input[-INTEGRITY_LEN:]
        plain = byte_xor(cipher, rnd)
        if secret != SERVER_PUBLIC_BANNER:
            return -1
        if self.getIntegrity(plain) != tag:
            return None
 
        return plain
 
 
def get_guest_token():
    global C
    token = C.encrypt(GUEST_NAME)
    print(b64.b64encode(token).decode('raw_unicode_escape'))
 
 
def login():
    global C
    try:
        s = input("Token:")  # Python3. Don't get funny RCE ideas
        cipher = b64.b64decode(s)
 
        if(len(cipher) > 16):
            print("Tokens must be smaller than 16 bytes!")
        plain = C.decrypt(cipher)
 
        if plain == -1:
            print("Wrong server secret!")
        elif plain == None:
            print("Failed integrity check!")
        elif plain == GUEST_NAME:
            print("Secret:", "No secrets for anonymous")
        elif plain == b"Ephvuln":
            print("Secret:", FLAG)
        else:
            print("I don't have an answer for", plain.decode('utf-8'))
    except:
        print("No h3k1ng allowed")
        exit()
 
 
def invalid():
    print("! Invalid option.")
 
 
def menu():
    global C
    C = Crypt()
 
    print(welcome_txt)
    while True:
        print(options_txt, end='')
        switch = {
            "1": get_guest_token,
            "2": login,
            "3": exit
        }
        func = switch.get(input(), invalid)
        func()
        print()
 
 
if __name__ == "__main__":
    menu()

Serverul se găsește la adresa: 141.85.224.117:1337

Pentru testarea conexiunii, încercați să folosiți netcat:

nc 141.85.224.117 1337

Pentru interacțiunea automată cu serverul, aveți la dispoziție următorul schelet de cod, care necesită pachetul pwntools. Pentru instalare:

 pip3 install pwntools 
skel.py
from pwn import *
import base64 as b64
from time import sleep
 
def byte_xor(ba1, ba2):
    return bytes([_a ^ _b for _a, _b in zip(ba1, ba2)])
 
LOCAL = True  # Local means that you run binary directly
 
if LOCAL:
    # Complete this if you want to test locally
    r = process("<PATH_TO_PYTHON_CHALLENGE>")
else:
    r = remote("141.85.224.117", 1337)  # Complete this if changed
 
def read_options():
    """Reads server options menu."""
    r.readuntil(b"Input:")
 
def get_token():
    """Gets anonymous token as bytearray."""
    read_options()
    r.sendline(b"1")
    token = r.readline()[:-1]
    return b64.b64decode(token)
 
def login(tag):
    """Expects bytearray. Sends base64 tag."""
    r.readline()
    read_options()
    r.sendline(b"2")
    # sleep(0.01) # Uncoment this if server rate-limits you too hard
    r.sendline(b64.b64encode(tag))
    r.readuntil(b"Token:")
    response = r.readline().strip()
    return response
 
 
# TODO: Solve challenge
# ..
# ..
# ..
# ..
# response = login(payload).decode('utf-8')
# if "CTF" in response:
#        print("[*] Found flag:",response)
#
 
 
r.close()

Atenție! În cazul în care se trimit prea multe request-uri, serverul va acționa un rate-limiting. Recomandarea este să puneți un sleep atunci când trimiteți pachete în cadrul unei structuri repetitive.

Task 2 - Differential Cryptanalysis (50p)

Descrierea task-ului se poate găsi în fișierul tema2022_ex2.pdf, iar scheletul de la acest exercițiu este diponibil în skel2022_ex2.zip.

Serverul se găsește la adresa: 141.85.224.119:1337

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

ic/teme/tema2022.txt · Last modified: 2022/11/12 03:36 by razvan.smadu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0