סדנת אבטחת מידע - Hash Length Extension Attack

קוד השרת שנרצה לפרוץ:

import hashlib
import secrets
from flask import Flask, request, jsonify
import urllib.parse

app = Flask(__name__)

SECRET = secrets.token_hex(4).encode('latin-1')

@app.route("/")
def hello():
    parts = request.query_string.split(b'&')
    for p in parts:
        name, val = p.split(b'=')
        if name == b'data':
            data = urllib.parse.unquote_to_bytes(val)
        elif name == b'sig':
            sig = val

    expected_sig = hashlib.md5(SECRET + data).hexdigest().encode('latin-1') 
    if expected_sig == sig:
        p = {w.split(b'=')[0]: w.split(b'=')[1] for w in data.split(b'&')}
        if int(p[b'uid']) == 1:
            return "Welcome Master. The secret key is: {}\n".format(SECRET)
        else:
            return "Welcome User\n"
    
    print('expected sig = {}'.format(expected_sig))
    print('got sig = {}'.format(sig))
    print('got data = {}'.format(data))

    return "INTRUDER ALERT\n"

@app.route("/tip")
def tip():
    data = b'uid=0'
    return jsonify({
        'data': data,
        'sig': hashlib.md5(SECRET + data).hexdigest(),
        })


if __name__ == '__main__':
    app.run()

כדי להריץ יש לשמור את הקוד לקובץ בשם server.py, להתקין flask באמצעות:

pip install flask

ואז להריץ משורת הפקודה:

python server.py

המשימה שלכם: לגלות מהו המפתח הסודי של השרת.

ניתן ורצוי להיעזר במאמר כאן:
https://blog.skullsecurity.org/2012/everything-you-need-to-know-about-hash-length-extension-attacks

שתי תמונות שונות עם אותו ערך MD5

brownwhite

תוכנית פייתון שמשתמשת ב Hash Length Extension כדי לפרוץ לשרת:

from __future__ import print_function
import sys
from socket import htonl
from pymd5 import md5
import requests
import textwrap
import struct
import urllib

BASE = 'http://localhost:5000'
SECRET_LENGTH = 8
fake_data = '&uid=1'

r = requests.get(BASE + '/tip')
r.raise_for_status()
existing_hash = r.json()[u'sig'].encode('utf-8')
data = r.json()[u'data'].encode('utf-8')
a, b, c, d = [int(x, 16) for x in textwrap.wrap(existing_hash, 8)]

h = md5("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
h.state = [
    htonl(a),
    htonl(b),
    htonl(c),
    htonl(d),
    ]

h.update(fake_data)
fake_sig = ''.join(['{:02x}'.format(x) for x in struct.unpack('16B', h.digest())])
print('sig: {}'.format(fake_sig))

filler = 56 - SECRET_LENGTH - len(data)
msg_len = 8 * (56 - filler)
msg = data + "\x80" + ("\x00" * (filler - 1)) + struct.pack("q", msg_len) + fake_data

post_data = {
        'data': msg,
        'sig': fake_sig,
        }
r = requests.get(BASE + '/?data={}&sig={}'.format(urllib.quote(msg), fake_sig))
r.raise_for_status()
print(r.text)