Vừa ngộ ra sự vi diệu của Padding Oracle Attack thì được tin crush hồi lớp 12 sắp cưới.

Biết viết từ đâu nhỉ! Đầu tiên là vài hôm trước, mình bắt đầu tìm hiểu về cảnh báo SSLv3 POODLE Attack. Trong quá trình đọc về POODLE, mình thấy có một kỹ thuật tấn công mới mà chưa được biết, có tên là Padding Oracle. Mình đọc qua qua thì thấy toàn Toán là Toán. Có vẻ phê; thế là quyết định dùng 2 hôm liền để tìm đọc hết gần như mọi thứ trên Google về "Padding Oracle Attack". 

Đến ngày hôm nay, mình cuối cùng đã ngộ ra được sự vi diệu của Padding Oracle Attack. Như mọi tối thứ 6 cuối tuần, mình ngồi viết báo cáo công việc tự nghiên cứu trong tuần qua để gửi cho mentor. Vừa xong xuôi, mình vào facebook thì thấy trên bảng tin là crush hồi THPT đăng ảnh sắp cưới! 😂 Thật sự là lâu lắm rồi chưa có cái j có thể làm mình bất ngờ đến mức như này! Chả biết có phải thật hay ko, nhưng mà thế quái nào 1 người luôn bình tĩnh - đôi khi hơi lâu la - (nhưng chưa bao giờ tạ nhé) - như mình đến mức thế này rồi mà còn thấy có j đó hơi mất kiểm soát 1 chút khi nhìn thấy tin này! Trước giờ luôn tự nghĩ mình ko con SÂN SI j, xem ra nay mới lòi ra là vẫn còn có thể SÂN SI. Phải luyện tập tiếp thôi! 😄

Mặc dù cũng thoải mái vs nhau lâu rồi, nhưng 1 cảm giác thất tình lại đang tiến đến với mình. Ôi yêu đương thời trẻ trâu, đúng là vớ vẩn mà! Với mấy cái cảm xúc kiểu thất tình, từ lâu rồi mình ko thấy sợ nữa. Không những vậy, mình còn tận dụng cảm giác "buồn rầu" lúc đó để đưa bản thân vào trạng thái "thư giãn"! 😆 Vậy là mình quyết định mở mấy bản nhạc thất tình rồi lên viết ngay bài viết này. Trong bài viết này, mình sẽ trình bày đầy đủ về kỹ thuật Padding Oracle Attack.

Khái quát.

Padding Oracle Attack là một kỹ thuật tấn công phát lại (replay attack) khai thác điểm yếu của hệ mã chuỗi khối CBC (Cipher Block Chaining). CBC là một trong những kỹ thuật mã hóa khối, thực hiện mã hóa từng khối bản rõ của văn bản cần mã hóa. Đây là kỹ thuật tấn công chọn bản mã; với giả định là kẻ tấn công (attacker) tuy không biết giải mã, nhưng có thể tùy chỉnh thay đổi từng bản mã.

Trong kỹ thuật tấn công này, attacker thực hiện replay attack tối đa 256 lần để xác định chính xác 1 byte trong một khối bản mã bất kỳ mà không thèm quan tâm đến khóa bí mật. Hay nói cách khác, chúng ta đếch cần j nhiều ngoài bản mã! Nghe có vẻ hư cấu, nhưng thực chất nó rất vi diệu các bạn ạ!

Sau khi giải mã được 1 byte của bản mã, attacker tiếp tục sử dụng lại tương tự kỹ thuật để xác định nốt các byte còn lại trong một khối bản mã. Dần dần, attacker có thể giải mã được càng nhiều bản mã để thu được nhiều cặp bản rõ - bản mã (plain text - cipher text). Kết hợp với kỹ thuật Tấn công vi phân (Differential Cryptanalysis), attacker có khi còn phá giải được khóa của thuật toán mã hóa ấy chứ!

Lưu ý rằng kỹ thuật này cho phép giải mã độc lập từng khối (block) bản mã (cipher); miễn là biết được khối bản mã cần giải mã và 1 khối bản mã ngay trước nó khi mã hóa các khối bản rõ.

Kỹ thuật Mã hóa chuỗi khối CBC.

Đầu tiên, phải kể đến thuật toán mã hóa được sử dụng trong kỹ thuật này. Người ta thường sử dụng các thuật toán mã hóa với khóa bí mật như DES, 3-DES hay AES. Nhưng thường ít sử dụng DES vì độ bảo mật dữ liệu của DES được cho là yếu hơn so với 3-DES hay AES.

Kỹ thuật mã hóa chuỗi khối CBC tiến hành chia văn bản cần mã hóa thành từng khối bản rõ có kích thước bằng nhau. Kích thước này được quy định trước để phù hợp với INPUT của một thuật toán mã hóa được sử dụng. Đặc biệt, khối đầu tiên được gọi là Vector khởi tạo (Init Vector), ký hiệu là IV. IV cũng có kích thước tương tự với từng khối bản rõ.

Đây là quy tắc mã hóa, riêng IV không cần mã hóa.
\[E\left( {IV \oplus {P_1}} \right) = {C_1}\]
\[E\left( {{C_1} \oplus {P_2}} \right) = {C_2}\]
...
\[E\left( {{C_{N - 1}} \oplus {P_N}} \right) = {C_N}\]
Đây là quy tắc giải mã:
\[D\left( {{C_N}} \right) \oplus {C_{N - 1}} = {P_N}\]
\[D\left( {{C_{N - 1}}} \right) \oplus {C_{N - 2}} = {P_{N - 1}}\]
...
\[D\left( {{C_1}} \right) \oplus IV = {P_1}\]

Nguyên tắc Padding.

Vấn đề là ở khối bản rõ cuối cùng, sẽ có khả năng xảy ra trường hợp kích thước khối bản rõ cuối cùng không đủ để lấp đầy như độ dài của các khối bản rõ trước đó. Do đó, nguyên tắc Padding (lấp đầy) được đưa ra để thực hiện thêm các byte vào vị trí còn trống trong bản rõ. Mục đích là lấp đầy khối bản rõ cuối cùng này trước khi đưa vào mã hóa.

Nguyên tắc Padding là một trong những nguyên tắc được quy định trong tập các nguyên tắc về mật mã học PKCS (Public Key Cryptography Standard). Nguyên tắc này quy định giá trị được sử dụng để lấp đầy vào khối bản rõ cuối cùng. Một cách tổng quát, nếu khối bản rõ cuối cùng còn cần phải lấp đầy thêm x byte thì giá trị được điền đầy vào x byte này sẽ chính bằng x. Lưu ý rằng x phải là biểu diễn dưới dạng hexe của số byte cần lấp đầy!

Lợi dụng điểm yếu.

Từ nguyên tắc mã hóa của CBC, ta thấy rằng CBC thực hiện giải mã với INPUT là khối hiện tại và khối trước đó, do đó ta chỉ cần 2 khối để thực hiện tấn công phát lại. Chỉ cần đúng 2 khối thôi nhé, nghe nguy hiểm chưa!

Tính chất của việc mã hóa cũng như phép toán XOR với tính chất:
\[A = B \oplus C \Leftrightarrow B = A \oplus C\]
Điểm yếu tiếp theo, cũng là điểm yếu chí mạng giúp ta có thể Padding Oracle Attack; đó chính là nguyên tắc Padding được Server kiểm tra và đáp lại bằng việc phần Padding có đúng định dạng hay không. Do đó dựa vào tín hiệu hồi đáp này, ta có thể vét cạn và phát lại để xác định được khi nào thì Padding có giá trị là “1” (\x01).

Tấn công.

Đặt:
  • k là độ dài (số lượng byte) từng khối bản mã Ci cũng như bản rõ Pi.
  • CN là khối bản mã cần giải mã; giả sử giải mã từ cuối lên.
  • C' là khối byte mà ta tạo ra để đóng vai trò giống như khối CN-1 trong việc giải mã CN; nhưng nó không phải CN-1, mà ta có thể thoải mái điều chỉnh giá trị từng byte của khối C' này.
  • P'N là bản rõ mà Server thu được sau khi giải mã Cvới khối C' mà ta cung cấp. Lưu ý rằng P'N này không phải bản rõ chính xác Pcủa khối CN.
Biến đổi:
  • Giải mã CN, ta được:
    \[D\left( {{C_N}} \right) \oplus C' = P{'_N}\]
  • Mà theo công thức tổng quát của việc mã hóa:
    \[E\left( {{C_{N - 1}} \oplus {P_N}} \right) = {C_N}\]
  • Ốp vào, ta thu được:
    \[D\left( {E\left( {{C_{N - 1}} \oplus {P_N}} \right)} \right) \oplus C' = P{'_N} \Leftrightarrow P{'_N} = {C_{N - 1}} \oplus {P_N} \oplus C'\,\,\left( {{1^*}} \right)\]
Trong công thức (1*), ta thấy rằng:
  • P'N: là giá trị bản rõ ta không biết, là giải mã của Server sau khi ta gửi {C' || CN} lên.
  • PN: là bản rõ ta không biết, và cần phải mò ra.
  • CN-1: là khối bản mã thật, ngay trước CN; ta biết giá trị này!
  • C': là khối bản mã giả, đóng vai trò như khối CN-1;  ta có full quyền chỉnh sửa với khối này.
Như bạn đã thấy, với một phương trình nghiệm nguyên có 2 ẩn chưa biết, cách đơn giản nhất là thực hiện “thử chọn” với 1 ẩn để tìm ra ẩn còn lại. Do đó cách thức sẽ là thử chọn với một biến bản rõ để mò ra khối bản rõ còn lại.

Vì cần xác định byte cuối cùng của PN trước; mà công thức (1*) lại là công thức đại số boolean, nên nó cũng đúng với từng byte độc lập trong mỗi khối của các bản ghi. Nên nếu áp dụng công thức (1*) cho byte cuối cùng trong từng bản ghi, ta sẽ thu được cái này:

\[P{'_N}\left[ k \right] = {C_{N - 1}}\left[ k \right] \oplus {P_N}\left[ k \right] \oplus C'\left[ k \right]\,\,\left( {{2^*}} \right)\]
Khi Server nhận được{C' || CN}, Server sẽ giải mã để lấy ra P'N. Server sẽ kiểm tra phần Padding của  P'N (chứ không cần biết P'có đúng bằng Pkhông; vì thực chất Server cũng đâu có biết PN). Nếu Padding hợp lệ, Server sẽ hồi đáp bằng thông điệp VALID_PADDING; ngược lại, Server sẽ hồi đáp bằng thông điệp INVALID_PADDING.

Do đó, ta hoàn toàn có thể giả sử Padding của Pcó giá trị bằng 0x01; nghĩa là chỉ cần lấp đầy byte cuối cùng. Sau khi thử chọn phát lại (tối đa 256 lần) đối với sự thay đổi của C'[k] trong C', ta sẽ tìm được giá trị chính xác byte cuối của D(CN); từ đó đem XOR với CN-1ta có thể tìm ra được chính xác byte cuối của PN!

Trong (2*) ta thấy:
  • P'N[k]: là byte ta giả định nó phải bằng 0x01 để thực hiện tấn công phát lại đến khi nhận được tín hiệu VALID_PADDING.
  • PN[k]: vẫn là byte cuối cùng mà ta chưa biết. Lưu ý là chưa biết nhé; vì sắp biết rồi! 😁
  • CN-1[k] là byte cuối cùng của bản mã CN-1, đương nhiên ta đã biết giá trị này.
  • C'[k]: là byte chúng ta có full quyền điều khiển. Ta sẽ điều khiển giá trị của byte này (tối đa 256 lần) cho đến khi nào nhận được VALID_PADDING
Từ đó, biến đổi lại (2*), ta được:
\[\left( {{2^*}} \right) \Leftrightarrow {P_N}\left[ k \right] = P{'_N}\left[ k \right] \oplus {C_{N - 1}}\left[ k \right] \oplus C'\left[ k \right]\]
\[ \Leftrightarrow {P_N}\left[ k \right] = 1 \oplus {C_{N - 1}}\left[ k \right] \oplus C'\left[ k \right]\,\,\left( {{3^*}} \right)\]
Với các byte tiếp theo, ta thực hiện tương tự quy trình tấn công phát lại, chỉ cần lưu ý:
  • Theo tiêu chuẩn PKCS, ta phải thay đổi sao cho hướng đến mục địch Padding của P'N phải đúng định dạng; ví dụ như 0x02|0x02 hoặc 0x03|0x03|0x03, ...
  • Sau khi đã giải mã được các byte trước, ta phải cố định những byte cuối cùng tương ứng của C' sao cho thỏa mãn Padding của P'N.

Đánh giá xác suất.

Việc đoán 256 lần cho mỗi byte thật ra chỉ là giá trị tồi nhất. Thực theo tính toán xác suất kỳ vọng, attacker không cần phải đoán nhiều như thế. Với không gian mẫu Ω = 256 cho một lần xác định 1 byte, theo “Nghịch lý Ngày sinh nhật”, attacker chỉ cần tấn công 20 lần là có thể hoàn thành việc xác định 1 byte, với xác suất là 50%; mà không cần phải đoán đến 128 lần. Cụ thể, gọi số lần tấn công phát lại là x, xác suất để xác định thành công 1 byte trong x lần đoán đó là:
\[P\left( x \right) = 1 - {e^{ - \frac{{x\left( {x - 1} \right)}}{{2*256}}}}\]
Với chuẩn mật mã DES, độ dài khóa là 56bit; độ dài INPUT (bản rõ) và OUTPUT (bản mã) là 64bit, tương đương 8byte; thay vì phải phá DES bằng vét cạn với không gian khóa Ω = 256 , ta chỉ cần Padding Oracle Attack với 8 * 256 = 2048 lần.

Hạn chế Padding Oracle.

Từ những j đã tìm hiểu, tấn công cũng như đánh giá trên, bản thân tôi nghĩ chúng ta có thể hạn chế Padding Oracle Attack bằng cách rất đơn giản đó là ko phản hồi lại bất cứ cái con khỉ j liên quan đến việc Padding có hợp lệ hay ko! Tại vì đây chính là điểm chí mạng để attacker có thể khai thác được điểm yếu của CBC bằng việc sử dụng replay attack.

Tuy nhiên, cách mặc kệ trên sẽ gây ảnh hưởng đến những người dùng thật với các kết nối sạch! Do đó, ngoài ra, việc thêm xác thực MAC (Message Authentication Code) vào bản mã trước khi gửi thông điệp. Server sẽ check MAC trước, nếu MAC không hợp lệ, sẽ không giải mã. Đây là cách xử lý tuy phức tạp hơn nhưng đảm bảo không bỏ sót những người dùng hợp lệ. Tuy nhiên yêu cầu là phải giữ bí mật được MAC. Hoặc nếu MAC ko bí mật thì phải có cách bảo vệ MAC, không cho attacker lấy được MAC hợp lệ để tiếp tục lừa server!

Kết.

Cuối cùng cũng viết xong về Padding Oracle; tuy hơi cần nhiều não nhưng đáng để động não các bạn ạ. Nói thêm vs các bạn là lỗ hổng SSLv3 cùng POODLE Attack này là do nhóm kỹ sư của Google phát hiện ra. Trong đó có 1 người, chính là anh Dương Ngọc Thái mà mình đã từng nhắc đến ở bài viết Muối - Tiêu trước.

Công nhận quá là bất ngờ. Xem nào, em ấy cùng xã nhưng khác thôn vs mình; kém 1 tuổi. Tóc nâu để dài, nhìn có nét tây tây; giọng nói hơi trầm. Ngoan, hiền (ít nhất là vs mình), có tinh thần học tập.

Hồi cấp 1, năm lớp 5; mình "được" chọn vào đội văn nghệ của trường để tập văn nghệ rồi biểu diễn cho 1 chương trình thi thố quái j đó ở dưới hội trường huyện. Quen em ấy từ hồi đó; hồi đó em ấy hình như giọng hát tốt nên đứng hát chính, còn mình thì đứng "múa". Nghĩ lại sao thấy trường Tiểu học hồi đó bựa vãi. Sao bao nhiêu đứa ko chọn, lại đi chọn 2 thằng trong đội tuyển học sinh giỏi vào đội văn nghệ nhỉ! 😠 Mà cũng chả hiểu sao hồi đó mình vs thằng bạn nối khố lại thích thích cái thứ dở hơi đó mà vào đội văn nghệ. Rồi xuống múa có mỗi bài hát xong về. Hình như vụ đó được trường cho mấy chục ngàn j đó, bắt tội Bố mình phải nai xe máy từ nhà xuống huyện, 12km chứ ít j đâu; xong còn phải đứng chờ mình nữa. 😐 Hồi đó múa hát bài j mà "Bay lên bay lên đi nòi giống Tiên Rồng, nào bay lên bay lên con cháu Lạc Hồng; mang theo trong tim dòng máu anh hùng; dựng xây Tổ quốc Việt Nam!" 😁

Cấp 2, mình xuống dưới huyện học, em ấy cũng xuống dưới huyện học. Hồi đó mình mải ganh đua môn Toán vs bạn bè trong lớp quá nên chả để tâm chút j. 😀 Lên THPT, mình vẫn học dưới huyện, em cũng vào cùng trường. Cả 2 cùng đi xe bus đi học suốt mấy năm cấp 2 và cấp 3. Bắt đầu crush em ấy từ hồi ôn thi đội tuyển Toán, từ cuối hè lớp 11 cho đến sau Tết lớp 12 😂 Móa hồi đó em ấy yêu cái thằng nào đó ở trường dân lập bên cạnh; mình thì cục xúc kiêu ngạo; có ngại bất cứ thành phần nào đâu, từ học giỏi cho đến học kém, từ ngoan ngoãn cho đến lưu manh, trừ mỗi mấy thằng bạn hay chơi thân vs nhau. 💪 Kết quả của sự cục xúc thì ko bao giờ hay cả, nên thôi ko cần kể thêm; chỉ đem lại thỏa mãn nhất thời chứ về sau sẽ thấy thật thiếu suy nghĩ! 😅 Hồi đó hay nghe bài này:
Thời gian sau, mình vào đại học, em ấy cũng ôn thi đại học. Cả 2 cũng bình thường hóa quan hệ. Em học ngôn ngữ Hàn ở ĐHQGHN, mình thì học CNTT ở BK. Lần trò chuyện cuối cùng gần nhất mà mình vừa xem lại facebook chat là từ hồi 20/10/16. Thế quái nào hôm đó mình lại đi chúc 20/10 em nó nhỉ, nghe giả tạo vãi! 😂

Giờ là time to thư giãn. Cảm giác thất tình sẽ đưa tâm trí chúng ta vào trạng thái lênh đênh vô định, bay bổng trên mây! Do đó từ lâu rồi, tôi đã tận dụng đặc điểm này để giúp đầu óc trở lên thư giãn, rời xa mọi suy nghĩ căng thẳng hại não ở hiện tại; nhất là khi vừa giác ngộ được Padding Oracle sau 2 ngày tìm hiểu! 😁 Cảm giác cứ như đang phê ấy, đầu óc trống trơn chả nghĩ được j nữa; đó chính là lúc chúng ta ko có tí căng thẳng nào! 

Mình cũng có vài bài nhạc thất tình hay nghe.
 

 

 

 


Nhận xét

Bài đăng phổ biến từ blog này

Trên con đường tu đạo luôn cực kỳ theo đuổi!

C++ Con trỏ (Pointer) toàn thư: Phần 4: Con trỏ "đa cấp". Đánh nhau bằng con trỏ.