前言
在学习的过程中, 是否看到别人搭建的 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 目前的定义是容器引擎, 可以方便的管理容器. 常用于整合统一整个开发, 测试, 和部署环境的流程, 节省运维成本.
-
虚拟机和容器
每个容器都是轻量, 独立, 可执行的文件包, 其中包括软件运行所需的一切内容. 包括运行所需要的代码, 库, 环境变量和配置文件.
对于容器没有一个严格的定义, 目前普遍认同其是一个相对独立的运行环境.
下面引用官方图简单讲解下它们之间的差异
-
VM
-
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 功能图:
环境准备安装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://<id>.mirror.aliyuncs.com"]
}
其他安装的见相关文档.
链接:
阿里 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
来退出这个容器.
实例一:
-
实例一中参数讲解
docker images
: 用于列出已经下载至本地的镜像列表. 列表包含仓库名、标签、镜像 ID、创建时间以及所占用的空间
docker ps
: 用来查看运行中的容器状态.
实例二:
-
实例二中参数讲解:
--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
: 用于指定基础镜像, 这里用的是 nginxRUN
: 用于在容器中运行交互 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.yml
用 YAML语法格式
描述所需要的单个或多个镜像, 定义成一组相关联的应用容器为一个项目.
运行 $ 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.yml
是Compose
的配置文件, 主要用来构建基于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
最终输出:
最终效果图
此时我们打开浏览器浏览 localhost
, 会跳转至 http://localhost/install/index.php
, 说明安装成功.
复现getshell
进入运行中的容器, 查看下该后门是否存在:
OK~, 接下来便是验证是是否能被利用.
url:http://tp.com/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
poc:<?php echo exec("pwd"); ?>
完整文件打包链接: : https://pan.baidu.com/s/1c13Yd4 密码: t9qf