Đăng trong Crypto, CTF

[Crypto] – Breaking RSA in CTF Challenges – (Part 1)

Hệ mã RSA được giới thiệu vào năm 1977 bởi 3 nhà khoa học Ron Rivest, Adi Shamir, Len Adlerman. Đây là một trong những hệ mã được sử dụng phổ biến nhất hiện nay, ứng dụng cho truyền dữ liệu an toàn qua internet, email. RSA còn là nền tảng mật mã cho các giao thức SSL/TLS, SET, SSH, PGP, … RSA cũng được ứng dụng trong chữ ký số Digial Signature.

Breaking RSA là dạng bài thường xuyên gặp phải trong các cuộc thi CTF dưới nhiều hình thức. Bài viết này tôi sẽ mô tả ngắn gọn về hệ mã này, liệt kê các điểm yếu có thể khai thác của nó, cũng như các công cụ toán học, các tool cần thiết giúp giải quyết các bài RSA hay gặp khi chơi CTF.


RSA Cryptosystem

rsa.gif

RSA thuộc nhóm hệ mã khóa công khai, dựa vào độ khó của bài toán phân tích 1 số ra thừa số nguyên tố (factoring problem). Để tạo cặp khóa Public keyPrivate key, Alice cần:

    • Chọn 2 số nguyên tố lớn p, q với p ≠ q
    • Tính n = pq
    • Tính giá trị hàm số Ơle φ(n) = (p-1)(q-1)
    • Chọn 1 số e sao cho 1 < e < φ(n)gcd(e,φ(n)) = 1
    • Tính d = e-1 (mod φ(n)), số d thỏa mãn ed ≡ 1 (mod φ(n))

Public Key gồm:

  • n – module.
  • e – số mũ mã hóa.

Private Key gồm:

  • n – module.
  • d – số mũ giải mã.

Khi Bob muốn gửi một tin nhắn M cho Alice, Bob chuyển M thành một số m < n theo 1 cách thỏa thuận trước. Bob sẽ tính ra bản mã c từ bản rõ m theo công thức:

c = me (mod n)

Để giải mã, Alice dùng Private Key của mình để tính ngược lại:

m = cd (mod n)

Quá trình giải mã có thể thu lại được m ban đầu là do:

cd ≡ (me)d ≡ med (mod n) ≡ m (mod n) hay m = cd (mod n)

Dấu cuối cùng là tôi đã áp dụng định lý Euler. Chi tiết hơn về thiết kế hệ mã cũng như ví dụ có thể đọc ở đây RSA – Wikipedia

Độ mạnh của hệ mã RSA dựa trên việc bạn cần phân tích được n ra thừa số nguyên tố để tính d nếu muốn phá mã, và đến nay chưa có giải thuật nào hiệu quả trong thời gian đa thức giúp ta phân tích thừa số nguyên tố đối với các số lớn.

Hệ mã RSA nếu được thiết kế một cách đúng đắn với việc chọn các tham số n, p, q, e hợp lý thì sẽ rất an toàn, thế nhưng trong các bài CTF, các tham số này thường được chọn theo một cách nào đó khiến cho hệ mã yếu đi và dễ bị tấn công. Các điểm yếu thực thi của RSA sẽ được trình bày dưới đây.


Attack 1 – Small n

Nếu n nhỏ (chiều dài n < 256 bit), ta có thể dễ dàng factorize n bằng cách brute-force số p. Chiều dài n được khuyến cáo là 1024 bit.

Kể cả khi n lớn, đôi khi factorize của n đã có sẵn trong các database online như factordb. Hoặc dễ dàng factorize bằng các công cụ online như alpertron, trang web này sử dụng phương pháp Elliptic Curve Method để factorize. Vậy nên, việc đầu tiên bạn cần làm khi gặp các bài RSA là thử các trang web này trước 🙂


Attack 2 – Small e, small m

Để tối ưu hóa thời gian mã hóa, số e được chọn thường có dạng e = 2n+1. Số e = 21+1 = 3 là số nhỏ nhất. Khi số e = 216+1 = 65537 được sử dụng, hàm mã hóa chỉ cần 17 phép nhân để tính ra ciphertext trong khi nếu chọn e ngẫu nhiên thì số phép tính trung bình là ≈ 1000.

Nếu ta chọn số e nhỏ, ví dụ e = 3. Giả sử Alice gửi cho Bob 1 mẫu tin ngắn M (m nhỏ). Lúc này ciphertext là c = m3 (mod n). Vì m nhỏ nên m3 < n, khi đó phép toán module không có tác dụng. Vì vậy để tìm ngược lại c, ta chỉ việc tính m = c1/3.


Attack 3 – Fermat Attack (p and q are too close)

rsa9.jpg

Trong thực tế, ta cần chọn p, q có cùng độ dài bit để tạo được 1 mã RSA mạnh, tuy nhiên nếu p, q quá gần nhau thì lại tạo ra lỗ hổng bảo mật khi mà attacker có thể dễ dàng factorize n.

Điều kiện cần

p - q < n¼

Fermat’s factorizing algorithm

Ta có: rsa10.png

Vậy n có thể được phân tích thừa số nguyên tố như sau: n = (x-y)(x+y).

Giải thuật Fermat giúp tìm p, q:

rsa11.png

Sourcecode dưới đây lấy từ 1 bài trong Pragyan CTF 2015: Weak RSA

sourcecode

 


Attack 4 – Hastad Broadcast Attack (same e, small e)

rsa8.jpg

Đặt bối cảnh một mạng nội bộ sử dụng RSA làm phương thức bảo mật truyền tin. Mỗi máy tính trong mạng LAN sẽ có một bộ Public Key (ni, ei) riêng. Giả sử rằng, quản trị viên muốn sử dụng hệ thống mã hóa đơn giản nên anh ta chọn 1 số e nhỏ (e = 3) để dùng chung cho tất cả các máy trong mạng LAN, hay nói cách khác e1 = e2 = ... = e = 3.

Kịch bản tấn công xảy ra nếu máy chủ gửi cùng 1 tin nhắn broadcast m (đã được mã hóa thành c1, c2, ... cho nhiều máy tính trong mạng, và ta bắt được ít nhất e ciphertext c1, c2, ..., ce. Lúc này, ta sẽ có thể khôi phục lại plaintext m không mấy khó khăn.

Giả sử e = 3, đặt M = m3. Nhiệm vụ của ta là giải hệ phương trình đồng dư:

rsa7

HPT đồng dư trên có thể giải dễ dàng bằng Chinese Remainder Theorem. Sau khi tính được M, ta sẽ tìm lại được m (vì m < ni nên M = m3 < N, ta chỉ cần tính căn bậc 3 của M).

Sourcecode bên dưới lấy từ một bài trong Qiwi CTF 2016:

sourcecode

 


Attack 5 – Common modulus (same n)

Tiếp tục với hệ thống mạng nội bộ như bài Attack 4, tuy nhiên lần này quản trị viên lại chọn cùng số n cho toàn bộ mạng lưới, nghĩa là n1 = n2 = ... = n và số e được chọn ngẫu nhiên. Như vậy mỗi thành viên trong mạng lưới sẽ được cấp một bộ tham số (n,ei,di)riêng.

Kịch bản 1: Internal Attack.

rsa14

Giả sử bạn là 1 thành viên của hệ thống mạng. Trong trường hợp này, bạn sẽ được cấp bộ tham số (n,e,d). Từ (n,e,d)của mình, ta dễ dàng tìm được φ(n) theo cách sau:

ed ≡ 1 (mod φ(n)) nên tồn tại số k sao cho ed - kφ(n) = 1. Do đó:

k = (ed-1)/φ(n) > (ed-1)/n

Vậy ta sẽ brute-force số k từ (ed-1)/n trở lên, tính ngược lại φ(n) = (ed-1)/k, cho đến khi thu được kết quả φ(n) là số nguyên. Có φ(n)ta dễ dàng tính được Private Key của victim:

dvictim = evictim-1 mod φ(n)

Bạn hoàn toàn có thể tiếp tục factorize n khi đã biết φ(n), nhưng lúc này thật sự không cần thiết nữa 🙂

sourcecode

Kịch bản 2: External Attack.

Giả sử bạn, với vai trò attacker, lại không phải là thành viên của hệ thống mạng, lẽ dĩ nhiên sẽ không được cấp Private Key d. Tuy nhiên bạn biết được các bộ (n,e) của các thành viên trong mạng (vì chúng là Public Key). Một lần tình cờ, server gửi cùng 1 message m cho 2 máy A, B, bạn nghe lén đường truyền và thu được (cA, cB). Thế là đủ để cho bạn khôi phục message m ban đầu.

rsa13.jpg

Đầu tiên, dùng Extended Euclidean Algorithm để tìm u, v sao cho eA*u + eB*v = 1.

Ta tính m như sau:

m = m1 = m(eA*u + eB*v) = (meA)u * (meB)v = cAu * cBv

Như vậy ta đã có thể khôi phục message m.

sourcecode

 


Attack 6 – Wiener Attack (small d)

Để mã hóa hay giải mã, ta thực hiện các phép tính c = me (mod n)m = cd (mod n). Các phép tính này khá hao tốn thời gian xử lý bởi nó chứa phép toán lũy thừa. Thời gian mã hóa phụ thuộc vào độ lớn của e, trong khi thời gian giải mã phụ thuộc độ lớn của d. Để giảm thời gian giải mã (ví dụ trong các tác vụ của 1 smartcard với CPU giới hạn), người ta thường chọn d nhỏ. Thế nhưng việc chọn d nhỏ lại khiến cho hệ thống trở nên yếu đi và có thể bị phá vỡ hoàn toàn bởi phương pháp tấn công Wiener Attack.

Điều kiện cần:

    • d < 1/3 n¼
    • q < p < 2q
    • e' < n3/2 với e' = e (mod n)hay Public Key không quá lớn

Nếu thỏa mãn các điều kiện trên (nhận biết thông qua việc đề cho e rất lớn), ta có thể dễ dàng tìm được d và phá vỡ toàn bộ hệ thống mã hóa.

Chứng minh:

Phép chứng minh sử dụng tính chất của liên phân số. Vì ed ≡ 1 (mod φ(n)) nên tồn tại một số nguyên k sao cho ed - kφ(n) = 1. Vì vậy:

rsa1.png

Do đó k/d ≈ e/φ(n). Ta không biết φ(n) nhưng có thể dùng n để xấp xỉ. Thật vậy, φ(n) = n-p-q+1p+q-1 < 3√n nên n-φ(n) < 3√n. Sử dụng n thay cho φ(n)ta thu được:

rsa2

k < d < 1/3 n¼ nên ta được:

rsa3

Từ đó, áp dụng định lý về dãy hội tụ liên phân số, ta tìm trong dãy hội tụ của khai triển liên phân số e/n sẽ tìm được k/d. Bằng cách thử từng cặp k/d này, ta tính φ(n) = (ed - 1)/k. Lúc này ta có:

p+q = n-φ(n)+1pq = n

Dùng định lý Vi-et đảo tính được p, q. Xác nhận lại pq = n để tìm ra cặp k/d đúng. Tìm được p, q sẽ tính được d và … done, RSA broken 🙂

rsa6.jpg

Ví dụ

Cho (n,e) = (90581,17993). Khai triển liên phân số của e/n là:

[0; 5, 29, 4, 1, 3, 2, 4, 3]

Bạn có thể tự tính hoặc dùng tool online wolframalpha. Từ đó ta có được dãy hội tụ của e/n. Có thể kiểm tra bằng WolframAlpha:

rsa4.png

Thử với phân số 1/5, ta giả sử k = 1d = 5. Tính được φ(n) = (ed - 1)/k = (17993*5 - 1)/1 = 89964. Do đó p+q = n-φ(n)+1 = 618pq = n = 90581. Nên p, q là nghiệm của PT: x2 - 618x + 90581 = 0. Giải PT tìm được q = 239; p = 379. Kiểm tra lại thấy rằng n = 90581 = 239 * 379 = pq. Như vậy có thể khẳng đinh được d = 5private key đúng.

Security Tip

Để tránh bị tấn công bởi phương pháp Wiener Attack, ta có thể dùng e nhỏ, nhưng lúc này bù lại d sẽ lớn và tốc độ giải mã là khá chậm. Nếu n là một số dài 1024 bit thì d phải dài ít nhất 256 bit.

M. Wiener đưa ra 2 giải pháp hiệu quả để phòng tránh hình thức tấn công trên mà vẫn giữ được tốc độ tính toán:

  1.  Sử dụng định lý số dư Trung Hoa (Chinese Remainder Theorem – CRT).
    • Giả sử ta chọn d sao cho dp = d mod (p-1)dq = d mod (q-1) đều là số nhỏ (khoảng 128 bit) nhưng d vẫn là số lớn. Quá trình giải mã vẫn có thể thực hiện nhanh chóng theo cách sau: Đầu tiên tính mp = Cdp (mod p)mq = Cdq (mod q). Dùng định lý CRT để tìm số m thỏa mãn m = mp (mod p)m = mq (mod q). Kết quả m tính được sẽ thỏa mãn tính chất m = cd (mod n) như yêu cầu. Vì d lớn nên Wiener Attack không hiệu quả nữa.
  2. Thay e bằng e' với e' = e + kφ(n) với k là một số nguyên đủ lớn sao cho e' > n3/2, lúc này Wiener Attack sẽ không áp dụng được bất kể d nhỏ thế nào

sourcecode

 


Lời kết

Vẫn còn một số phương pháp attack RSA nữa nhưng bài viết đến đây đã khá dài, tôi xin dừng tại đây. Phần tiếp theo xin hẹn lại vào một ngày khác 🙂

Tham khảo:

 

rsa15.jpg

Một suy nghĩ 4 thoughts on “[Crypto] – Breaking RSA in CTF Challenges – (Part 1)

  1. em mới tập tành chơi ctf thôi, blog này giúp em nhiều lắm luôn đó anh, chi tiết cực. Hóng part2 ạ hihi. Chỉ muốn nói là cảm ơn anh nhiều lắm ❤

    Thích

Bình luận về bài viết này