手把手教你制作漏洞复现环境

前言

在学习的过程中, 是否看到别人搭建的 Exploit 练习平台心痒痒呢? 通过本篇教程的学习, 将手把手教你搭建属于自己的漏洞测试利用环境, 不管是自己学习还有分享给小伙伴都将轻而易举.

Ps: 这是一篇伪 Docker 学习指南. (首发在信安之路)

为什么选择Docker

现在市面上的 漏洞测试利用环境 大体分三种, 当然这只是我自己的理解. 分别是: Web安装版, VM虚拟机镜像docker镜像. 本次教程就已 docker镜像 为例, 不含其他两项.

相比其他两项, docker 的夸平台及针对性是最好的. 不知道你们有遇到过 VM虚拟机镜像 因为CPU不同而无法启动运行? Web安装版 因为版本或平台不同, 运行发布后的项目发现 url目录大小写 不一致而无法运行? 我的意见是给相关开发寄刀片, 你认为呢?

当然缺点也是有的, 比如asp,jsp等…

蛤? docker是什么? , 可以看一看 Docker从入门到实践:https://www.gitbook.com/book/yeasy/docker_practice/details

对这方面感兴趣就去阅读一遍吧, 对你会有提升的. 知识本来是很普通常见, 有的人却喜欢挑上那些外表华丽光鲜的.

必须知道的东西

Docker 目前的定义是容器引擎, 可以方便的管理容器. 常用于整合统一整个开发, 测试, 和部署环境的流程, 节省运维成本.

  • 虚拟机和容器

    每个容器都是轻量, 独立, 可执行的文件包, 其中包括软件运行所需的一切内容. 包括运行所需要的代码, 库, 环境变量和配置文件.

    对于容器没有一个严格的定义, 目前普遍认同其是一个相对独立的运行环境.

下面引用官方图简单讲解下它们之间的差异

  1. VM

    VM

  2. Docker

    Docker

传统虚拟机技术是虚拟出一套硬件后, 在其上运行一个完整操作系统, 在该系统上再运行所需应用进程. 而容器内的应用进程直接运行于宿主的内核, 容器内没有自己的内核, 而且也没有进行硬件虚拟. 因此容器要比
传统虚拟机更为轻便, 所以能实现 秒级 甚至是 毫秒级 启动.

  • 功能和组件

    • 客户端(Docker Client)
    • 守护进程(Docker Server)
    • 容器(Docker Container画重点
    • 镜像(Docker Images画重点
    • 仓库(Registry)

Docker Client

linux 中, Docker 将客户端和服务端统一在同一个二进制文件中. 其他平台则只提供 Client.

Docker Server

它是驱动整个 Docker 功能的核心引擎, 作用是接收客户端发送的请求, 实现请求中要求的功能并返回相应额结果.

Docker Container

镜像( Image ) 和容器( Container) 的关系, 就像是面向对象程序设计中的 类 和 实例 一
样, 镜像是静态的定义, 容器是镜像运行时的实体. 容器可以被创建、启动、停止、删除、
暂停等.

容器的实质是进程, 但与直接在宿主执行的进程不同, 容器进程运行于属于自己的独立的命名空间。因此容器可以拥有自己的 root 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间. 容器内的进程是运行在一个隔离的环境里, 使用起来, 就好像是在一个独立于宿主的系统下操作一样. 这种特性使得容器封装的应用比直接在宿主运行更加安全. 也因为这种隔离的特性, 很多人初学 Docker 时常常会把容器和虚拟机搞混.

上述引用《Docker 从入门到实践》中关于容器的介绍

Docker Images

如果说容器提供了一个完整的、隔离的运行环境, 那么镜像则是这个运行环境的静态体现, 是一个还没运行起来的 运行环境.

Registry

和名字一样, 它就是一个存放镜像的仓库, 通常部署在网络中的服务器上或云中. 官方仓库:Docker Hub

附网上一张 docker 功能图:

dockerImage

环境准备安装docker

前提条件是满足内核版本, 官方建议为 3.10 以上版本及 Cgroup 和 Namespace 相关选项.

  • Ubuntu or Debian

    我用的是debian, 你可以自行选择用那一个. 进入 debian 官网, 直接点右上角的下载 Debian 9.* 即可. 或者点 取得 Debian 再进入 下载一个安装映像 选择你所需的iso.

    系统安装就不累赘了, 任意百度一篇文章均可.

  • Docker CE

    官方为了简化安装流, 提供了一套便捷的安装脚本, Debian 系统上可以使用这套脚本安装:

    $ curl -fsSL get.docker.com -o get-docker.sh

    or

    $ sudo sh get-docker.sh --mirror Aliyun

执行这个命令后,脚本就会自动的将 `最新版 Docker CE` 安装至系统.

想加入开启自启动则:

`$ sudo systemctl enable docker.service`

验证是否存在, 如果看到存在 `docker.service` 就说明成功:

`$ sudo systemctl list-units --type=service`
  • 镜像加速

    Docker 运行前需要本地存在对应的镜像, 本地没有则会去远程镜像仓库下载. 默认是国外的下载源, 下载的时候会很慢. 建议配置加速器:

    $ curl -sSL https://get.daocloud.io/daotools/set_mirror.sh | sh -s http://c44a6190.m.daocloud.io

    该脚本可以将 --registry-mirror 加入到你的 Docker 配置文件 /etc/docker/daemon.json 中.

    或者将daemon.json内容改为阿里加速:

{
    "registry-mirrors": ["https://mqodggij.mirror.aliyuncs.com"]
}

其他安装的见相关文档.

链接:

docker-ce官方安装文档:https://docs.docker.com/engine/installation/linux/docker-ce/debian/#install-docker-ce-1

阿里 docker-CE 安装帮助:https://yq.aliyun.com/articles/110806

基本示范

前面说到 镜像 在运行时我们称之为 容器, 而 镜像 均需要从网络的仓库拉取一个基础镜像本地, 根据需要, 再填充或修改所需的配置等.

获取镜像的指令是 $ docker pull, 常用格式 docker pull 仓库名[:标签]. 比如从官方仓库下载一个最新版本的PHP镜像, 则运行$ docker pull ubuntu:latest.

当镜像下载好后, 我们就可以已该镜像启动容器, 并进入容器做相应操作.( run 容器时, docker 会先试图在本地找运行容器的指定镜像, 如果没有则会从远程仓库拉取. )

$ docker run -it ubuntu:latest bash

  • run 表示运行
  • -it 这是常用的两个参数, 组合起来表示运行交互式终端
  • ubuntu:latest 这是指用 ubuntu 最新的镜像tag来启动容器.
  • bash 命令运行一个交互 Shell, 完整是 /bin/bash

最后通过 exit 来退出这个容器.

实例一:

demo1

  • 实例一中参数讲解

    • docker images : 用于列出已经下载至本地的镜像列表. 列表包含 仓库名、标签、镜像 ID、创建时间以及所占用的空间
    • docker ps : 用来查看运行中的容器状态.

实例二:

demo2

  • 实例二中参数讲解:

    • --name apache : 将启动的容器名称命名为 apache.
    • -d : 该参数用于表示容器在后台运行
    • -p 8080:80 : 指定映射端口, 将本地8080端口映射至容器80端口
    • httpd : 指定容器依赖 apache 镜像运行

其余详细指令和参数请查看官网或《Docker从入门到实践》, 此处不再做过多概述.

除了上述 shell 交互方式, Docker 还提供一种脚本文件的方式来构建所需镜像, 减少在不同机器上重复构建镜像、配置文件等操作, 也方便团队交流时整理成文案, 已阅读文件的形式知道该镜像包含那些修改及操作.

Dockerfile 是一个文本文件, 可以逐行向里面写指令, 每一条指令的内容, 用来描述镜像如何构建.

引用《Docker从入门到实践》中的实例, 文件中内容如下:

FROM nginx
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
  • 该文件内容很简单就两行(其他指令及含义本文未涉及到均不注解, 请自行查询学习. 蟹蟹~).

    • FROM : 用于指定基础镜像, 这里用的是 nginx
    • RUN : 用于在容器中运行交互 shell 指令, 此处是用一段字符串替换默认的 index.html 文件内容

Dockerfile 所在文件目录执行构造镜像指令: $ docker build .

docker 会自己去找当前文件夹中的 Dockerfile 文件, 并执行里面的命令.

注意, 命令中的 . 符号不能省略, 它用于制定上下文路径, 代表当前目录, 也就是指 Dockerfile 文件内用到带有路径的指令, 均已它为起点向上或向下寻找路径内文件.

除了 Dockerfile 文件用来定制单个镜像, 日常中还会碰到多个容器相互配合来运行容器的场景 此时就用到了 docker-compose.yml. 它负责快速在集群中部署或运行分
布式应用, 使用它的话则需要安装 Docker Compose, 所幸它的安装步骤并不复杂.

  • 安装Compose服务编排工具
$ sudo curl -L https://github.com/docker/compose/releases/download/1.18.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose`

$ sudo chmod +x /usr/local/bin/docker-compose

$ docker-compose --version
docker-compose version 1.18.0, build 1719ceb

docker-compose.ymlYAML语法格式 描述所需要的单个或多个镜像, 定义成一组相关联的应用容器为一个项目.

运行 $ docker-compose up -d 指令后, 会拉取文档内指定镜像并且运行容器.

up 指令用于创建并启动容器, 为防止容器启动后, 有服务在前台运行造成交互操作中断, 通常配合 -d 参数在让其在后台运行.

文档内需要填写的内容, 请看面章节, 相关注释已经写好

构建利用环境

前面的都是废话, 下面开始 基本操作 .

tpshop的getshell镜像

  • 确认环境

在任意目录下新建一个文件夹, 用于存储该项目, 本项目包含三个容器, 内含2个子目录.

项目内的目录结构:

.
├── conf
│   └── nginx
│       └── defaulf.conf    # ngin配置文件
├── docker-compose.yml      # Compose 配置文件
├── mysql
│   ├── Dockerfile          # 镜像相关配置文件
│   └── my.cnf              # mysql配置文件
└── php
    ├── Dockerfile
    └── tpshop_2_0_7.tar.gz # 网站源码压缩包
  • docker-compose.ymlCompose 的配置文件, 主要用来构建基于 Docker 的复杂应用, Compose 通过一个配置文件来管理多个 Docker 容器, 非常适合组合使用多个容器进行复合使用的场景。帮助文档:docker-compose:https://docs.docker.com/compose/

内容如下:

# 申明配置文件版本, 注意`Compose`版本要高于1.13+ 实践一下踩踩坑就知道了
version: "3"
# 告知是一个应用容器, 下面镜像均包含在这个应用容器内
services:
    # 镜像一 名称可以自己定义
    web:
        # 指定该镜像 nginx , 版本为 alpine 最小体积版. 如果镜像在本地不存在, 会尝试拉取仓库中的镜像
        image: nginx:alpine
        # 挂载数据卷, 可将物理主机内的某个目录挂载至容器内指定位置
        volumes:
            # 将当前路径下的conf/nginx文件夹挂载至容器内的conf.d目录, 目的是替换nginx虚拟主机配置文件
            - "./conf/nginx:/etc/nginx/conf.d"
            # 容器之间共享一个挂载路径, 用于放置解压后的Web页面源代码
            - "wwwdir:/var/www/html/"
        # 建立映射端口
        ports:
            # 将容器内80端口与主机80端口建立映射关系
            - "80:80"
        # 定义容器名称, 默认名称
        container_name: "web"
        # 建立依赖关系
        depends_on:
            # 声明容器间的依赖. 这点很重要mysql在php的前面才能link生效
            - mysqldb
            - php

    # 镜像二
    php:
        # 注意区分 build 和 image. 这里是运行当前目录下php文件夹中的 Dockerfile 配置文件
        # bulid : 指 Dockerfile 所在文件夹路径, Compose 将会利用它自动构建这个镜像,然后使用这个镜像
        # image : 指定为镜像名称或镜像id, 如果镜像在本地不存在, Compose 将会尝试拉去这个镜像.
        build: "./php/"
        volumes:
            - "wwwdir:/var/www/html/"
        ports:
            - "9000:9000"
        # 连接到其他的容器, 这里指PHP容器链接MYSQL容器, 实现容器与容器之间的交互
        links:
            - mysqldb
        container_name: "php"

    # 镜像三
    mysqldb:
        build: "./mysql/"
        # 设置环境变量, MYSQL提供几个环境变量用于指定连续参数: https://hub.docker.com/_/mysql/
        environment:
            # 设置数据访问密码
            MYSQL_ROOT_PASSWORD: "root"
            MYSQL_DATABASE: "tpshop"
        ports:
            - "3306:3306"
        volumes:
            - "./mysql_db:/var/lib/mysql"
        container_name: "mysqldb"

volumes:
    # 挂载一个全局共享卷, 用于容器之间的数据共享
    wwwdir:
  • ~/tpshop_getshell/php/Dockerfile 文件里面会记录一些基础指令, 这些指令是用于稍后定制 php-fpm 镜像中的内容.

内容如下:

# 指定镜像为php5最新版本的fpm
FROM php:5-fpm

MAINTAINER 0x584a xjiek2010[at]icloud.com

# 将本地压缩包解压至容器对应文件夹内
ADD tpshop_2_0_7.tar.gz /var/www/html/

# 定义环境变量
ENV WWW_DIR /var/www/html

# 执行相关shell, 等同于在命令行下直接抒写代码
RUN apt-get update && apt-get install -y \
        libfreetype6-dev \
        libjpeg62-turbo-dev \
        libmcrypt-dev \
        libpng-dev \
    && docker-php-ext-install -j$(nproc) iconv mcrypt \
    && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
    && docker-php-ext-install -j$(nproc) gd \
    && docker-php-ext-install mysqli \
    && chmod 777 -R ${WWW_DIR}/install \
    && chmod 777 -R ${WWW_DIR}/public/upload \
    && chmod 777 -R ${WWW_DIR}/application/admin/conf \
    && chmod 777 ${WWW_DIR}/application/database.php \
    && chmod 777 ${WWW_DIR}/application/config.php \
    && mkdir -p -m 777  ${WWW_DIR}/runtime \
    && apt-get purge -y --auto-remove
  • ~/tpshop_getshell/mysql/Dockerfile
FROM mysql:5

MAINTAINER 0x584a xjiek2010[at]icloud.com

# 拷贝配置文件至容器内位置
COPY my.cnf /etc/mysql/my.cnf
  • ~/tpshop_getshell/conf/nginx/defaulf.conf 文件内容如下:
server {
    listen 80;
    server_name  _;
    index index.php index.html index.htm;
    root /var/www/html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
        autoindex on;
    }

    location ~ \.php$ {
        try_files $uri /index.php =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}
  • ~/tpshop_getshell/mysql/my.cnf 文件内容如下:
[client]
port        = 3306
socket      = /var/run/mysqld/mysqld.sock

[mysqld]
port        = 3306
socket      = /var/run/mysqld/mysqld.sock

sql_mode=NO_ENGINE_SUBSTITUTION,NO_AUTO_CREATE_USER

上述文件内容均配置无误后, 通过运行 docker-compose 完成镜像的安装及容器的启动.

# 运行shell
$ docker-compose up -d

最终输出:

1

最终效果图

此时我们打开浏览器浏览 localhost, 会跳转至 http://localhost/install/index.php, 说明安装成功.

2

3

复现getshell

进入运行中的容器, 查看下该后门是否存在:

4

OK~, 接下来便是验证是是否能被利用.

url:http://tp.com/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php

poc:<?php echo exec("pwd"); ?>

5

完整文件打包链接: : https://pan.baidu.com/s/1c13Yd4 密码: t9qf

参考