VIII. CBC bitflipping attacks
1. Điểm yếu của vector khởi tạo iv
Trong phần này, chúng ta sẽ cùng thảo luận về một dạng tấn công nhắm vào mode CBC trong mật mã đối xứng AES - CBC bitflipping attacks. Trước hết, cùng quan sát lại sơ đồ giải mã của AES CBC:
Mỗi block ciphertext sau khi trải qua quá trình "block cipher decryption" (có sự tham gia của key) đều sẽ cần một phép XOR cuối cùng để thu được plaintext. Và Block đầu tiền đóng vai trò đặc biệt nhất vì nó sẽ được XOR với vector khởi tạo iv
. Bởi đặc điểm của phép XOR, nên trong trường hợp attacker có thể thay đổi tùy ý giá trị của iv
, sẽ có thể thay đổi nội dung của plaintext. CBC bitflipping attacks thường là nhắm vào điểm yếu của vector khởi tạo iv
, đôi khi có thể sẽ thay đổi một phần ciphertext để thu được nội dung plaintext mong muốn.
2. Challenge CTF
Bạn đọc có thể thử sức và luyện tập kỹ thuật tấn công bitflipping attacks thông qua bài CTF bitflipping attacks. Challenge mô phỏng một tình huống ứng dụng mã hóa AES CBC vào giá trị cookie của người dùng trong một website. Có mã nguồn như sau:
from Crypto.Cipher import AES
import os
from Crypto.Util.Padding import pad, unpad
from datetime import datetime, timedelta KEY = ?
FLAG = ? @chal.route('/flipping_cookie/check_admin/<cookie>/<iv>/')
def check_admin(cookie, iv): cookie = bytes.fromhex(cookie) iv = bytes.fromhex(iv) try: cipher = AES.new(KEY, AES.MODE_CBC, iv) decrypted = cipher.decrypt(cookie) unpadded = unpad(decrypted, 16) except ValueError as e: return {"error": str(e)} if b"admin=True" in unpadded.split(b";"): return {"flag": FLAG} else: return {"error": "Only admin can read the flag"} @chal.route('/flipping_cookie/get_cookie/')
def get_cookie(): expires_at = (datetime.today() + timedelta(days=1)).strftime("%s") cookie = f"admin=False;expiry={expires_at}".encode() iv = os.urandom(16) padded = pad(cookie, 16) cipher = AES.new(KEY, AES.MODE_CBC, iv) encrypted = cipher.encrypt(padded) ciphertext = iv.hex() + encrypted.hex() return {"cookie": ciphertext}
Route /flipping_cookie/get_cookie/
sẽ cung cấp giá trị cookie cho chúng ta. Biến cookie
gồm hai phần: admin=False
và một tham số expiry
chỉ thời gian hết hạn của cookie được xác định qua:
expires_at = (datetime.today() + timedelta(days=1)).strftime("%s")
Kéo dài ngày được thể hiện dưới dạng UTC timestamp.
Tiếp theo, website sử dụng kỹ thuật padding và AES CBC mã hóa cookie. Phần ciphertext trả về có block đầu tiên là vector khởi tạo iv
ở dạng hex. Có thể cài đặt một hàm get_ciphertext()
:
def get_ciphertext(url, path): r = requests.get(url = url + path) response = r.text.strip() data = json.loads(response) return data['cookie']
Route kiểm tra vai trò admin /flipping_cookie/check_admin/<cookie>/<iv>/
nhận hai giá trị cookie
và iv
trong URL, cho phép attacker kiểm soát giá trị iv
. Sau đó, giải mã với iv
từ URL và kiểm tra chuỗi admin=True
có tồn tại trong cookie hay không để xác định người dùng mang vai trò admin hoặc người dùng thường.
Do attacker có thể thay đổi giá trị vector khởi tạo iv
nên hàm check_admin()
có thể bị tấn công bởi kỹ thuật bitflipping, bằng cách thay đổi giá trị iv
để làm thay đổi nội dung cookie
ở dạng plaintext.
Phần giá trị admin=False
đang nằm tại block thứ hai của ciphertext, chúng ta mong muốn khi đưa nó vào hàm sẽ nằm ở block đầu tiên của ciphertext, để có thể thực hiện phép XOR với iv
. Bởi vậy, cookie
truyền vào route kiểm tra vai trò admin là block thứ hai của ciphertext. Sau khi quá trình "block cipher decryption" diễn ra, nó mang giá trị 'admin=False;expi' ^ iv
(iv
ở đây là giá trị ban đầu được đưa ra bởi website).
Tiếp theo nó sẽ được XOR với iv_attacker
được control bởi attacker. Để đánh lừa server nhận biết chúng ta là admin, cần có:
cookie sau decryption ^ iv_attacker = 'admin=True...'
Biến đổi:
'admin=False...' ^ iv ^ iv_attacker = 'admin=True...'
iv_attacker = 'admin=False;expi' ^ iv ^ 'admin=True...'
Khai báo giá trị cho cookie và iv sẽ gửi cho route check admin qua Python:
cookie = get_ciphertext(BASE_URL, cipher_path) iv = bytes.fromhex(cookie[:32])
cookie_send = bytes.fromhex(cookie[32:64])
old_value = b'admin=False;expi'
goal_value = b'admin=True;\x01\x01\x01\x01\x01'
iv_attacker = long_to_bytes(bytes_to_long(old_value) ^ bytes_to_long(goal_value))
iv_attacker = long_to_bytes(bytes_to_long(iv_attacker) ^ bytes_to_long(iv))
Chương trình đầy đủ tham khảo:
import requests
import json
from Crypto.Util.number import long_to_bytes, bytes_to_long BASE_URL = 'https://aes.cryptohack.org'
cipher_path = '/flipping_cookie/get_cookie/' def get_ciphertext(url, path): r = requests.get(url = url + path) response = r.text.strip() data = json.loads(response) return data['cookie'] def get_flag(url, path): r = requests.get(url = url + path) response = r.text.strip() data = json.loads(response) return data['flag'] cookie = get_ciphertext(BASE_URL, cipher_path) iv = bytes.fromhex(cookie[:32])
cookie_send = bytes.fromhex(cookie[32:64])
old_value = b'admin=False;expi'
goal_value = b'admin=True;\x01\x01\x01\x01\x01'
iv_attacker = long_to_bytes(bytes_to_long(old_value) ^ bytes_to_long(goal_value))
iv_attacker = long_to_bytes(bytes_to_long(iv_attacker) ^ bytes_to_long(iv)) flag = get_flag(BASE_URL, '/flipping_cookie/check_admin/%s/%s/' % (cookie_send.hex(), iv_attacker.hex()))
print(flag)
IX. Brute force attack in AES - hướng tấn công liệu có khả thi?
1. Thực hiện và thời gian
Brute force attack hay hiểu đơn giản là tấn công vét cạn, tức mỗi lần sẽ sử dụng một khóa bí mật khác nhau thực hiện tính toán giải mã, cho tới khi tìm ra khóa bí mật trùng khớp với mật mã. Thông thường, trung bình mỗi lần phá mã thành công cần duyệt qua khối lượng bằng một nửa không gian khóa. Với độ lớn của khóa bí mật càng tăng, độ lớn không gian khóa cần thử cũng tăng lên, khả năng kháng tấn công vét cạn của AES cũng càng cao.
Lấy AES-128 làm ví dụ, lấy theo tỉ lệ thành công phía trên, trung bình mỗi "dự án tấn công" chúng ta cần thử lần khóa ngẫu nhiên có độ lớn bit và thực hiện giải mã.
Chúng ta vẫn thường nghe "thiên hạ võ công, duy khoái bất phá". Về khía cạnh mật mã học, có thể hiểu với độ tính toán nhanh, trên lý thuyết sẽ có thể tấn công thành công bất kỳ loại mật mã nào.
Bảng dưới cho thấy thời gian trung bình để phá mã thành công AES trong mỗi trường hợp độ lớn của khóa bí mật.
Độ lớn khóa bí mật (bit) | Không gian khóa | Thời gian thành công - mỗi ns thực hiện một lần giải mã | Thời gian thành công - mỗi ns thực hiện 10000 lần giải mã |
---|---|---|---|
128 | năm | năm | |
192 | năm | năm | |
256 | năm | năm |
(1s = 1 000 000 000 ns)
Từ bảng trên có thể thấy, kể cả bạn có một bộ máy với sức mạnh tính toán mỗi ns thực hiện lần giải mã, thì thời gian để tấn công thành công một mật mã AES chỉ với độ lớn khóa bí mật bit cũng phải mất tới năm! Hiện tại các nhà khoa học tính toán từ khi vũ trũ được hình thành, cho tới nay thời gian khoảng 13,787 ± 0,020 tỉ năm. Các bạn hiểu ý tôi chứ, so với năm, điều đó là không tưởng!
2. Máy tính lượng tử
Có thể dễ dàng nhận thấy rằng, thời gian và sức mạnh tính toán là hai đại lượng tỉ lệ nghịch. Để giảm thời gian tấn công, chúng ta có thể tăng sức mạnh tính toán lên. Sức mạnh tính toán mạnh nhất tại thời điểm hiện nay có thể kể đến công nghệ máy tính lượng tử.
Chúng ta không thể phủ nhận sức mạnh của máy tính lượng tử, tuy nhiên điều này liên quan tới chi phí rất lớn. Dựa trên lý thuyết thì có thể tính toán thời gian tiết kiệm được khi sử dụng sức mạnh máy tính lượng tử tấn công thuật toán AES. Tuy nhiên có thể dễ dàng ngăn chặn bằng cách tăng độ lớn của khóa bí mật. Trong thực tế sẽ chẳng bao giờ xảy ra câu chuyện sử dụng máy tính lượng tử để thực hiện những công việc không đủ tầm quan trọng như tấn công một thuật toán AES với khóa bí mật nhỏ.