Kĩ thuật mã hóa trong linux

Xin chào,

Trong bài viết hôm nay, chúng ta sẽ học căn bản về Cryptography (Mã Hóa). Cụ thể mình sẽ giới thiệu cho các bạn căn bản về encoding, hashing, các hình thức tấn công và cách phòng chống.

Đây là một nhánh chính thức và đóng vai trò cực kỳ quan trọng trong bảo mật nên kiến thức về nhánh này rất nhiều và phức tạp. Do đó mình đã cố tình giản lược khá nhiều thứ để giữ cho nội dung sao cho đơn giản nhất dành cho những bạn có trình độ căn bản. Bạn nào hứng thú vào muốn tìm hiểu thêm về nhánh này thì có thể Google thêm nhé.


1 – Mã hóa dữ liệu căn bản?

Hashing và encoding là 2 hình thức mã hóa dữ liệu đang được sử dụng phổ biến hiện nay.

1a – Encoding

Encoding là một hình thức mã hóa dữ liệu được sử dụng khá phổ biến. Mục đích là để bảo vệ dữ liệu tránh bị tiếp cận bởi những đối tượng không được cho phép. Bạn nào từng sinh hoạt đoàn đội hay các câu lạc bộ Huynh Trưởng Công Giáo chắc sẽ không xa lạ gì với các câu đổ giải mật thư có kèm theo từ khóa để giải mật thư. Các mật thư này chính là một ví dụ của encoding.

Sự khác biệt giữa encoding và hashing nằm ở chỗ nếu hashing là hình thức mã hóa dữ liệu một chiều và không có cách nào có thể truy ngược lại dữ liệu gốc, thì với encoding, dù không biết từ khóa để giải mật thư, nhưng nếu mật thư được mã hóa không quá khó thì bạn vẫn có thể tìm được dữ liệu gốc sau một hồi thử một số cách phá mật thư phổ biến.

Để cho dễ hiểu chúng ta hãy thử encode một dữ liệu theo thuật toán Ceasar’s Cipher. Đây là cách mã hóa dữ liệu đã được hoàng đế Julius Ceasar sáng tạo ra để bảo vệ thông tin chiến trường trên đường vận chuyển tránh rơi vào tay quân địch.

Cách mã hóa này rất đơn giản. Ví dụ chúng ta có một đoạn tin nhắn như sau:

Dữ liệu gốc: HAY FOLLOW TRANG HUONG DAN TU HOC NETWORK SECURITY VA CYBER-ATTACK PENETRATION

Từ khóa: 3 trái

Để encode dữ liệu trên theo thuật toán Ceasar’s cipher và dựa vào từ khóa đã cho, chúng ta sẽ dời tất cả các ký tự trong dữ liệu gốc về bên phải (về sau) nó 3 ký tự.

Chúng ta sẽ có bảng chữ cái tiếng Anh như sau:

Kĩ thuật mã hóa trong linux
H1b.1

Chúng ta sẽ bắt đầu encode dữ liệu nhé.

  1. Chữ cái đầu tiên trong dữ liệu gốc là chữ H tương đương với vị trí 2b trong bảng chữ cái H1b.1. Chúng ta sẽ dời nó về phía bên phải 3 ký tự. Vậy H (2b) -> K (5b).
  2. Chữ cái thứ hai trong dữ liệu gốc là chữ A tương đương với vị trí 1a trong bảng chữ cái H1b.1. Chúng ta sẽ dời nó về phía bên phải 3 ký tự. Vậy A (1a) -> D (4a).

Cứ tuần tự làm theo cách trên cho đến hết giá trị gốc chúng ta sẽ được giá trị mã hóa như sau.

Dữ liệu gốc: HAY FOLLOW TRANG HUONG DAN TU HOC NETWORK SECURITY VA CYBER-ATTACK PENETRATION

Dữ liệu mã hóa: KDB IROORZ WUDQJ KXRQJ GDQ WX KRF QHWZRUN VHFXULWB YD FBEHU-DWWDFN SHQHWUDWLRQ

Để giải đoạn dữ liệu mã hóa trên, các bạn sẽ sử dụng từ khóa đã được cho là 3 trái. Nghĩa là để tìm được dữ liệu gốc, các bạn sẽ dời tất cả các ký tự có trong dữ liệu mã hóa về phía bên trái (về trước) 3 ký tự. Nghĩa là:

  • Chữ K trong dữ liệu mã hóa ở vị trí 5b trong bảng chữ cái H1b.1 sẽ được dời về trước nó 3 ký tự. K (5b) -> H (2b)

Chúng ta cứ làm như thế cho đến hết dữ liệu mã hóa thì sẽ tìm được dữ liệu gốc. Tuy nhiên giả dụ như chúng ta không biết từ khóa được cho thì một trong những cách chúng ta có thể thử nghiệm để tìm lại dữ liệu gốc dời ký tự của dữ liệu mã hóa sang trái hoặc sang phải cho tới khi các ký tự nối với nhau thành từ có nghĩa.

Tuy nhiên, giải pháp phá mật thư này chỉ áp dụng được ở cấp độ những câu hỏi đánh đố đơn giản mà thôi. Đối với những thuật toán encode dữ liệu đang được sử dụng trong các giao thức mạng thì việc crack được thuật toán và tìm được dữ liệu gốc là điều mà không phải ai cũng có khả năng làm được. Và nếu người làm được điều đó là các tội phạm mạng thì đó sẽ là một đại hoạ cho toàn nhân loại cho đến khi thuật toán bị crack được thay thế hoàn toàn.

Có vô số cách khác nhau để encode một dữ liệu có thể kể ra như:

  • Dời ký tự
  • Đổi thứ tự ký tự
  • Thêm ký tự lạ hoặc lặp lại ký tự
  • v.v

1b – Hashing

1b-1 – Khái niệm hashing

Hashing hay còn gọi là mã băm, nói một cách đơn giản, là một hình thức mã hóa dữ liệu một chiều dùng để bảo vệ dữ liệu gốc (dữ liệu trước khi mã hóa), tránh cho dữ liệu gốc lọt vào tay của những cá nhân hoặc tổ chức không được cấp phép sử dụng dữ liệu đó.

Để hash một dữ liệu, dữ liệu đó sẽ được chạy qua một thuật toán hash và đầu ra sẽ là một chuỗi ký tự vô nghĩa (thường là ở dạng hexadecimal). Chuỗi ký tự vô nghĩa này được gọi bằng những cái tên như hash values, hashcodes, digets hay chỉ đơn giản là hashes. Các thuật toán hash dữ liệu thường cho ra các kết quả có cùng một độ dài (tính theo bytes) bất kể độ dài của giá trị đầu vào là bao nhiêu.

Một trong những tính năng đặc biệt của hash dữ liệu đó là mã hóa một chiều. Nghĩa là dù có được giá trị hash bạn cũng sẽ không có bất kỳ cách nào lật ngược lại quy trình hash để tìm lại dữ liệu trước khi được mã hóa.

Tất cả các thuật toán được dùng để hash dữ liệu đều phải đảm bảo các yêu cầu cơ bản sau:

  1. Deterministic: Vào bất cứ thời điểm nào, cùng một dữ liệu đầu vào luôn luôn cho ra cùng một dữ liệu đầu ra.
  2. Non-invertible: Việc đảo ngược quy trình hash để tìm được dữ liệu gốc từ dữ liệu được hash là điều gần như bất khả thi.
  3. Quick to compute: Thời gian hash nhanh.
  4. Use the Avalanche Effect: Chỉ một thay đổi nhỏ giá trị đầu vào sẽ thay đổi tối thiểu 50% giá trị đầu ra.
  5. Should avoid or eliminate collisions: Vì chúng ta nhận giá trị đầu vào bất kể độ dài và thường sẽ xuất ra các giá trị có cùng độ dài, nên việc có 2 giá trị khác nhau nhưng lại có cùng kết quả xuất ra là điều tuy hiếm nhưng hoàn toàn có thể xảy ra. Do đó một thuật toán hash tốt phải tránh được điều đó.

Có rất nhiều thuật toán hash đang được sử dụng để bảo vệ dữ liệu người dùng như BCRYPT, SCRYPT, Argon2, v.v.

Hashing có rất nhiều ứng dụng trong đời sống, một trong những ứng dụng đó là quản lý mật khẩu người dùng đối với các nền tảng cho phép người dùng tạo mật khẩu để đăng nhập, ví dụ như Facebook chẳng hạn.

Chúng ta sẽ bàn ngoài lề một tí về cách các trang mạng xác nhận xem bạn có đúng là người dùng sở hữu tài khoản đã đăng ký hay không nhé.

Thông thường khi bạn tạo một tài khoản, ví dụ như tài khoản FB đi. Thì FB sẽ lưu lại thông tin username và password của bạn; để mỗi lần bạn đăng nhập, FB sẽ đối chiếu username và password mà bạn nhập với username và password mà bạn đã cung cấp khi tạo tài khoản. Nếu thông tin khớp nhau thì bạn sẽ được cấp quyền truy cập tài khoản của bạn.

Tuy nhiên, vấn đề ở đây chính là việc lưu lại password người dùng ở dạng ký tự bình thường không được mã hóa (plain text) là một điều rất nguy hiểm. Nếu chẳng may hackers thành công chiếm được quyền truy cập database của FB, toàn bộ username và password của người dùng sẽ nằm trong tay hackers. Và vì đại đa số người dùng thường có thói quen sử dụng một mật khẩu cho nhiều tài khoản khác nhau (Gmail, ngân hàng, Apple ID, v.v.), hackers có thể lợi dụng thói quen này của người dùng để tấn công sang các tài khoản khác thuộc sở hữu của người dùng đó.

Để tránh điều đó xảy ra, thay vì lưu mật khẩu của người dùng ở dạng không mã hóa, các công ty như FB sẽ lưu hash của password. Nghĩa là, khi tạo tài khoản, thay vì lưu lại password của bạn, FB sẽ hash password của bạn và lưu lại giá trị hash này vào cơ sở dữ liệu của họ. Khi bạn đăng nhập tài khoản FB, password bạn dùng để đăng nhập cũng sẽ được hash và FB sẽ so sánh giá trị hash mà họ lưu với giá trị hash họ nhận được từ bạn. Nếu hai giá trị này trùng nhau, nghĩa là bạn đã cung cấp mật khẩu đúng và sẽ được cấp quyền truy cập vào hệ thống.

1b-2 – Thực hành hashing

Chúng ta sẽ thực hành hash dữ liệu bằng thuật toán MD5. MD5 là một thuật toán hash dữ liệu đã ra đời từ năm 1992 nhưng đã bị ngừng sử dụng từ hơn 10 năm qua sau khi bị phát hiện thuật toán này không tránh được hash collisions.

MD5 sẽ nhận giá trị đầu vào với bất cứ độ dài nào và sẽ luôn trả về một chuỗi hash với 16 bytes ký tự hexadecimal (bạn nào quên hexadecimal thì xin xem lại phần 1c của bài này nhé).

Mình cũng xin nhắc lại một chút. Số hexadecimal ( hex hay số thập lục phân) là một bộ số đếm với cơ số là 16. Các giá trị trong bộ số đếm này là:

0 1 2 3 4 5 6 7 8 9 A B C D E F

Khi biểu diễn số hex dưới dạng số nhị phân (chỉ bao gồm 0 và 1), chúng ta sẽ cần 4 bit nhị phân để biểu diễn một giá trị thập lục phân. Vì:

Kĩ thuật mã hóa trong linux
H1b-2.1

Kiến thức bên trên nằm ở bài sau nếu bạn nào muốn xem lại.

Chúng ta cũng biết 1 byte = 8 bits = 2 ký tự hex. Vậy, MD5 xuất ra giá trị có độ dài 16 bytes hex sẽ tương đương với 32 ký tự hex.

Để thực hành, chúng ta sẽ sử dụng lệnh md5sum trên Kali Linux.

Ví dụ 1: Hash giá trị “hello”

Mở giao diện CLI trên Kali Linux và gõ lệnh sau:

vincent@kali:~$ echo -n "hello" | md5sum

Câu lệnh trên có nghĩa là chúng ta sẽ xuất ra giá trị “hello” bằng lệnh echo. Flag -n dùng để không xuống dòng sau khi đã xuất ra giá trị “hello”. Sau đó giá trị “hello” được xuất ra sẽ được cho chạy qua lệnh md5sum để hash giá trị “hello”.

Sau khi chạy câu lệnh trên, chúng ta sẽ được kết quả như hình bên dưới.

Kĩ thuật mã hóa trong linux
H1b-2.2

Bạn nào quên câu lệnh echo và pipe line thì hãy xem lại 2 bài này nhé (echo, pipe line).

Ví dụ 2: Hash giá trị “hello1”

Chúng ta sẽ thử thay đổi 1 giá trị trong “hello” để xem giá trị hash thay đổi như thế nào nhé.

vincent@kali:~$ echo -n "hello1" | md5sum

Kĩ thuật mã hóa trong linux
H1b-2.3

Như các bạn thấy, chỉ thêm giá trị “1” vào “hello” chúng ta thấy hiệu ứng Avalanche hoạt động như thế nào. Giá trị hash của “hello” và “hello1” gần như hoàn toàn khác nhau và dù thêm 1 ký tự, cả hai chuỗi hash đều có cùng độ dài như bên dưới:

hello:  5d41402abc4b2a76b9719d911017c592 
hello1: 203ad5ffa1d7c650ad681fdff3965cd2

Ví dụ 3: Hash ký tự “a”

Chúng ta sẽ thử hash ký tự “a” xem kết quả hash sẽ như thế nào nhé

vincent@kali:~$ echo -n "a" | md5sum

Kĩ thuật mã hóa trong linux
H1b-2.4

Chúng ta có thể thấy, dù chỉ hash có 1 ký tự, chúng ta vẫn nhận được một chuỗi hash đủ 32 ký tự hex.

hello:  5d41402abc4b2a76b9719d911017c592 
hello1: 203ad5ffa1d7c650ad681fdff3965cd2
a:      0cc175b9c0f1b6a831c399e269772661

Ví dụ 4: Hash lại giá trị “hello”

Sau khi đã thực hành ví dụ 1, 2 và 3, chúng ta sẽ hash lại giá trị “hello” để xem giá trị hash có gì thay đổi không nhé.

vincent@kali:~$ echo -n "hello" | md5sum

Kĩ thuật mã hóa trong linux
H1b-2.5

Ta thấy hai giá trị hash “hello” đều như nhau.

2 – Các hình thức hash cracking phổ biến

Như ở phần 1 mình đã trình bày hashing là hình thức mã hóa dữ liệu một chiều và việc lật ngược quy trình hash từ dữ liệu đã được hash để tìm ra dữ liệu gốc gần như là điều bất khả thi. Vì lẽ đó các hackers và pentesters thường sử dụng giải pháp “đoán mò” mật khẩu để crack hash. Tuy nhiên, để tối ưu hóa việc “đoán mò” mật khẩu sao cho hiệu quả và ít tốn thời gian nhất, chúng ta sẽ có các giải pháp sau:

Brute force attacks:

Hackers hoặc pentesters sử dụng những phần mềm chuyên dụng và dựa vào bảng chữ cái và bảng chữ số (từ 1 đến 10) để tạo ra vô hạn các kết hợp. Các kết hợp này sẽ được gửi đến server mục tiêu để hash và so sánh với dữ liệu hash đã được lưu trong cơ sở dữ liệu của mục tiêu. Quá trình này diễn ra cho đến khi tìm được password thật của account. Cách này cực kỳ mất thời gian. Và để tối ưu hóa, người ta thường sử dụng nhiều GPU siêu mạnh cùng một lúc để giảm thời gian bruteforce.

Dictionary attacks:

Hackers hoặc pentesters sẽ có một danh sách những username và password hay được sử dụng nhất. Hoặc một từ điển của ngôn ngữ mà đối tượng sử dụng. Sau đó, họ sử dụng những phần mềm chuyên dụng để thử mọi kết quả username và password có trong danh sách hoặc từ điển đó cho đến khi nào tìm ra được username và password mới thôi.

Cách này rất mất thời gian vì danh sách để thử thường rất dài và thành công hay không phụ thuộc hoàn toàn vào việc username và password của mục tiêu có nằm bên trong danh sách mà hackers có hay không?

Do đó để tối ưu hóa, hackers hoặc pentesters thường nghiên cứu mục tiêu rất kỹ và càng nhiều thông tin được công bố trên mạng xã hội ví dụ như:

  • Tên thật và ngày tháng năm sinh
  • Nguyên quán
  • Sở thích
  • Tên người yêu, tên thú cưng, tên bố mẹ
  • Những sự kiện quan trọng

Đây đều sẽ là những thông tin hữu ích giúp hacker hoặc pentester thêm được các từ khóa quan trọng vào danh sách của mình nhằm tăng khả năng tấn công thành công vì người dùng thường hay đặt password dựa vào các thông tin này để dễ nhớ.

Rainbow table attacks:

Trong quá trình crack hash, phần lớn thời gian tiêu tốn nằm ở việc hash username và password. Để giải quyết vấn đề đó, rainbow table đã ra đời.

Rainbow table là một cơ sở dữ liệu tập hợp các kết hợp username và password đã được hash ở nhiều thuật toán hash khác nhau. Vì lẽ đó kích thước của một rainbow table thường rất lớn, có thể lên tới hàng trăm GB.

Để sử dụng được rainbow table, bạn cần phải bằng cách nào đó, có được danh sách chứa password hash của tất cả người dùng được chứa trong cơ sở dữ liệu của mục tiêu. Sau đó bạn sẽ sử dụng các phần mềm chuyên dụng để so sánh các kết quả hash từ danh sách thu được với cơ sở dữ liệu của rainbow table cho đến khi tìm ra được hash có cùng giá trị.

Lúc này, chúng ta có thể dễ dàng dựa vào rainbow table để tìm ra được password hoặc chuỗi ký tự cho ra cùng giá trị hash (trong trường hợp hash collision xảy ra). Vì dữ liệu được dùng để xác thực người dùng là dữ liệu hash, nên dù khác mật khẩu nhưng lại xảy ra hash collision dẫn đến hai giá trị hash giống nhau, chúng ta vẫn có thể đăng nhập vào tài khoản mục tiêu được như bình thường.

3 – Cách phòng chống hash cracking

Để chống lại brute force attack và dictionary attack, các hệ thống hiện đại ngày nay sẽ tự động khóa tài khoản nếu người dùng nhập sai mật khẩu quá số lần quy định. Lần đầu có thể là khóa trong 15 hay 30 phút, nếu tình trạng vẫn tiếp diễn, tài khoản sẽ bị khóa cho đến khi người sở hữu tài khoản liên lạc với nhà cung cấp dịch vụ.

Để chống lại rainbow table attacks, người ta thường hay dùng salt và pepper. Salt và pepper là 2 chuỗi giá trị được hệ thống tạo ra ngẫu nhiên để thêm vào password trước khi hash nhằm tăng cường sức mạnh cho password.

Ứng dụng thực tế của salt và pepper như sau:

Với salt:

1 – Khi lưu password vào cơ sở dữ liệu, sẽ có trường hợp 2 hoặc nhiều hơn 2 người dùng sử dụng password giống nhau. Điều này dẫn đến việc nếu hacker có được danh sách password hash, và tìm ra được password trước khi hash của 1 trong số những người có password giống nhau này, thì khả năng tất cả những người này bị mất tài khoản là điều chắc chắn.

Ví dụ, chúng ta có 4 người dùng với MD5 password hash và password gốc nằm bên trong dấu ngoặc () như sau:

  • John: 161bcd8552b9944d9bbf3e9b05961881 (password123)
  • Kat: 37b4e2d82900d5e94b8da524fbeb33c0 (football)
  • Thomas: 161bcd8552b9944d9bbf3e9b05961881 (password123)
  • Vincent: b53a666371c9022f9ec539db07022076 (qwerty123456789)

Nhìn vào 2 giá trị hashes của John và Thomas, chúng ta có thể phần nào đoán được 2 người dùng này có password giống nhau. Nếu hacker tấn công và truy cập vào được database và bằng cách nào đó có được password của John, chúng có thể sử dụng password đã tìm được này để tấn công Thomas.

Để tránh điều đó xảy ra, trước khi hash password, hệ thống sẽ tự động tạo và thêm một chuỗi giá trị được gọi là salt vào password trước khi hash.

Mỗi người dùng sẽ có một chuỗi salt khác biệt và chuỗi salt này chỉ có duy nhất hệ thống tạo ra nó biết. Chuỗi salt này có thể được thêm vào đầu hoặc cuối của password trước khi hash và được lưu trữ tại cùng cơ sỡ dữ liệu với nơi lữu trữ password hash.

Ví dụ trong trường hợp bên trên, John và Thomas sẽ cùng được gán giá trị salt vào password như sau

  • John: ahd17843
  • Thomas: hjki54120

Sau khi thêm salt, password của họ sẽ trở thành:

  • John: password123ahd17843
  • Thomas: password123hjki54120

Và điều này chắc chắn dẫn đến chuyện password hash của họ sẽ khác nhau:

  • John : 0a09fd9b6a4e0c07a2ba5d520336aa13
  • Thomas: acdc3e83eea43fbd7781156b6e5d8718

Như vậy, dù có lấy được password hash list, hacker cũng không tài nào đoán ra được John và Thomas có password giống nhau.

2 – Salt được dùng để chống lại rainbow table attacks. Vì giá trị của chuỗi salt chỉ duy nhất có hệ thống tạo ra nó biết được và được tạo ra theo quy tắc kết hợp các ký tự và chữ số một cách ngẫu nhiên, nên khả năng rất cao là rainbow table sẽ không có giá trị hash nào là kết quả của password + salt cả.

Với pepper:

Pepper (hay còn được gọi là secret salt) có chức năng tương tự như salt, tuy nhiên sẽ có vài điểm khác biệt như sau:

Salt:

  • Đảm bảo trong cùng một ứng dụng, hai password giống nhau sẽ có hash khác nhau
  • Giá trị của salt nằm bên trong hash và được lưu trong cơ sỡ dữ liệu

Pepper:

  • Đảm bảo hai ứng dụng trong cùng một hệ thống dù có cùng password và salt nhưng giá trị hash sẽ khác nhau. Ví dụ: một công ty có 1 web nội bộ và 1 web công cộng, mỗi web sử dụng một cơ sở dữ liệu riêng để lưu thông tin người dùng. Pepper sẽ đảm bảo dù người dùng dùng chung password cho cả hai web, sau khi đã thêm cùng một salt, giá trị hash sẽ khác nhau.
  • Giá trị của pepper nằm bên trong hash và không được lưu trong cơ sở dữ liệu

Như vậy, dù cho hackers có nắm được password hash và salt, chúng cũng không thể tìm ra password gốc dựa vào rainbow table attacks được vì thiếu giá trị của pepper.


Cám ơn các bạn đã theo dõi,

Vincent Nguyễn

Source:

https://securityboulevard.com/2019/07/insecure-default-password-hashing-in-cmss/#:~:text=The%20SHA1%2C%20SHA256%2C%20and%20SHA512,should%20always%20use%20a%20salt.
https://blogs.getcertifiedgetahead.com/rainbow-table-attacks/
https://auth0.com/blog/adding-salt-to-hashing-a-better-way-to-store-passwords/