0x01 前言
之所以整理一篇Docker搭建CTF中的靶机文章,主要是因为最近断断续续遇到需要自己搭建一个服务器端镜像的事,出题或者是部署一些服务,出于安全或者是可移植性的一些考虑,都是需要用到Docker的,然后每次使用Docker总要查阅一些资料,在这里整理并把经历分享一下。
0x02 Docker介绍
整理了一下Docker|菜鸟教程上的介绍
- Docker 是一个开源的应用容器引擎。
- Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。
- 容器是完全使用沙箱机制,相互之间不会有任何接口,更重要的是容器性能开销极低。
Docker 的架构主要分为
- Docker 镜像(Images):Docker 镜像是用于创建 Docker 容器的模板。
- Docker 容器(Container):容器是独立运行的一个或一组应用。
- Docker 客户端(Client):Docker 客户端通过命令行或者其他工具使用 Docker API (https://docs.docker.com/reference/api/docker_remote_api) 与 Docker 的守护进程通信。
- Docker 主机(Host):一个物理或者虚拟的机器用于执行 Docker 守护进程和容器。
- Docker 仓库(Registry):Docker 仓库用来保存镜像,可以理解为代码控制中的代码仓库Docker Hub(https://hub.docker.com) 提供了庞大的镜像集合供使用。
- Docker Machine:Docker Machine是一个简化Docker安装的命令行工具,通过一个简单的命令行即可在相应的平台上安装Docker,比如VirtualBox、 Digital Ocean、Microsoft Azure。
0x02 Docker安装
首先我找了一个没有安装Docker的服务器
以CentOS Docker 安装为例,其他系统服务器请参考菜鸟教程https://www.runoob.com/docker/centos-docker-install.html
- 设置仓库
安装所需的软件包。yum-utils 提供了 yum-config-manager ,并且 device mapper 存储驱动程序需要 device-mapper-persistent-data 和 lvm2。
sudo yum install -y yum-utils \
device-mapper-persistent-data \
lvm2
- 使用以下命令来设置稳定的仓库。
sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
- 安装最新版本的 Docker Engine-Community 和 containerd
sudo yum install docker-ce docker-ce-cli containerd.io
- 安装成功
0x03 Docker使用
常用docker命令
- 启动docker
service docker start
- 查看当前镜像列表
docker images
选项说明:
- REPOSITORY:表示镜像的仓库源
- TAG:镜像的标签
- IMAGE ID:镜像ID
- CREATED:镜像创建时间
- SIZE:镜像大小
- import导入镜像
docker import [options] file|URL|- [REPOSITORY[:TAG]]
实例如下:
docker import music.tar music:centos
导入之前导出的用Dockerfile生成的镜像,然后修改它的REPOSITORY为music,TAG为centos
- export导出镜像
docker export [OPTIONS] CONTAINER
实例如下
docker export -o test-mysql.tar <容器 ID>
- 进入镜像的终端
docker run -t -i music:centos /bin/bash
因为镜像本身就是要用到操作系统的,当我们在用dockerfile生成镜像后,想要继续去修改镜像的内容,本质就和我们连服务器是一样的。要退出终端,直接输入exit退出即可。
参数说明:
- -i: 交互式操作。
- -t: 终端。
- music:centos 这是指的我之前导入的镜像。
- /bin/bash:放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是 /bin/bash。
- docker网络端口映射
docker run -d -p 1001:80 music:centos /run.sh
这边80是我music这个镜像在创建的时候设置开放的端口,1001是我当前服务器的端口
-d: 后台运行容器,并返回容器ID;
-p: 指定端口映射,格式为:主机(宿主)端口:容器端口
- docker删除容器进程
docker rm -f 70119596feee
后面的70119596feee是容器id,可以不打全
- docker查看所有的容器:
docker ps -a
- docker停止一个容器
docker stop <容器 ID>
- docker开启已关闭容器
docker start 7a432b5f80a9
- docker删除镜像
docker rmi <镜像 ID>
- docker使用Dockerfile搭建镜像
docker build . -t test-mysql
docker build为创建镜像命令,名称为test-mysql
’.'表示当前目录,即Dockerfile文件所在的目录
- docker进入一个已经开启的容器
docker exec -it containerID /bin/bash
- docker从宿主机复制文件到容器中
docker cp 文件名 容器ID:路径
0x04 Apache+PHP+Mysql服务
Mysql数据库容器
以下构建mysql镜像参考博客
注:这篇博客有一个坑。在他的Dockerfile中没有给setup.sh执行权限。
- Dockerfile
FROM mysql:5.7.20
#设置免密登录
ENV MYSQL_ALLOW_EMPTY_PASSWORD yes
#将所需文件放到容器中
COPY setup.sh /mysql/setup.sh
COPY test.sql /mysql/test.sql
COPY privileges.sql /mysql/privileges.sql
#为/mysql/setup.sh添加执行权限
RUN chmod u+x /mysql/setup.sh
#设置容器启动时执行的命令
CMD ["sh", "/mysql/setup.sh"]
- test.sql
create database `test` default character set utf8 collate utf8_general_ci;
use test;
SET FOREIGN_KEY_CHECKS=0;
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `user`
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`uname` varchar(128) COLLATE utf8_bin NOT NULL,
`passwd` varchar(128) COLLATE utf8_bin DEFAULT NULL,
PRIMARY KEY (`uname`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('user1', 'pass1');
INSERT INTO `user` VALUES ('user2', 'pass2');
- privileges.sql
use mysql;
select host, user from user;
-- 因为mysql版本是5.7,因此新建用户为如下命令:
create user test identified by '123456';
-- 将test数据库的权限授权给创建的test用户,密码为123456:
grant all on test.* to test@'%' identified by '123456' with grant option;
-- 这一条命令一定要有:
flush privileges;
- setup.sh
#!/bin/bash
set -e
#查看mysql服务的状态,方便调试,这条语句可以删除
echo `service mysql status`
echo '1.启动mysql....'
#启动mysql
service mysql start
sleep 3
echo `service mysql status`
echo '2.开始导入数据....'
#导入数据
mysql < /mysql/test.sql
echo '3.导入数据完毕....'
sleep 3
echo `service mysql status`
#重新设置mysql密码
echo '4.开始修改密码....'
mysql < /mysql/privileges.sql
echo '5.修改密码完毕....'
#sleep 3
echo `service mysql status`
echo `mysql容器启动完毕,且数据导入成功`
tail -f /dev/null
- 使用Dockerfile构建镜像
docker build . -t test-mysql
docker build 为创建镜像命令,名称为test-mysql
’.'表示当前目录,即Dockerfile文件所在的目录
- 将镜像启动,从镜像mysql的默认3306端口映射到本地的端口上
docker run -d -p 1235:3306 test-mysql
通过PORTS可以看到成功映射到了本地的1235端口,这是我的kali虚拟机,我用真机上的Mysql管理工具Navicat尝试连接
- 导出镜像包
docker export -o test-mysql.tar 7e7
在使用镜像包的时候,导入后启动镜像映射端口的时候,是需要命令的
docker run -d -p 20001:3306 test-mysql /mysql/setup.sh
启动的时候让他执行setup.sh
Web容器
- Dockerfile
#基于ubuntu镜像
FROM ubuntu:16.04
#维护人的信息
MAINTAINER <785691921@qq.com>
#============
# 安装apache2 php软件等
#============
RUN apt-get update && \
apt-get install -y apache2 software-properties-common && \
LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php && \
apt-get update && \
apt-get install -y \
libapache2-mod-php5.6 \
php5.6 \
php5.6-cli \
php5.6-curl \
php5.6-dev \
php5.6-gd \
php5.6-imap \
php5.6-mbstring \
php5.6-mcrypt \
php5.6-mysql \
php5.6-pgsql \
php5.6-pspell \
php5.6-xml \
php5.6-xmlrpc \
php-apcu \
php-memcached \
php-pear \
php-redis \
&& apt-get clean \
&& rm -fr /var/lib/apt/lists/*
#===========================
# 拷贝同目录下的WEB进入容器
#===========================
COPY test.php /var/www/html/
#===========================
# 拷贝启动服务脚本
#===========================
COPY run.sh /run.sh
RUN chmod 775 /run.sh
CMD ["/run.sh"]
- run.sh
#!/bin/bash
#清空httpd缓存文件
rm -rf /run/httpd/*
#启动httpd服务
exec /usr/sbin/apachectl -D FOREGROUND
- test.php
<?php
$con = mysql_connect(getenv("MYSQL_ADDR"),getenv("MYSQL_USER"),getenv("MYSQL_PASS"));
if (!$con)
{
die('Fail:' . mysql_error());
}
else
{
mysql_query("SET NAMES utf8");
mysql_select_db("test", $con);
$result = mysql_query("SELECT * FROM user");
while($row = mysql_fetch_array($result))
{
echo $row['uname'] . " " . $row['passwd'];
echo "";
}
}
mysql_close($con);
?>
- 使用Dockerfile构建镜像
docker build . -t test-php
- 运行容器并连接数据库
docker run -d -p 1236:80 -e MYSQL_ADDR=ip:端口 -e MYSQL_USER=test -e MYSQL_PASS='123456' test-php
0x05 常见交互式服务器靶机搭建(python环境为例)
- 简单的python交互脚本test.py
BUFSIZE = ''
if __name__ == "__main__":
print("Please input something:")
buf = raw_input(BUFSIZE).replace('\n', '')
print("What you entered is:"+buf)
- Dockerfile
FROM python:2-slim
RUN sed -i s@/deb.debian.org/@/mirrors.aliyun.com/@g /etc/apt/sources.list && \
sed -i s@/security.debian.org/@/mirrors.aliyun.com/@g /etc/apt/sources.list && \
apt-get update -y && \
apt-get install socat -y
COPY test.py /test.py
EXPOSE 10000
ENTRYPOINT socat TCP4-LISTEN:10000,tcpwrap=script,reuseaddr,fork EXEC:"/usr/bin/env python2 -u /test.py"
- 使用Dockerfile构建镜像
- 开启容器
docker run -d -p 1234:10000 test_python
成功将容器的10000端口映射到本地的1234端口
- nc连接实现交互
- 导入镜像开启容器时的命令
‘’’
docker run -d -p 10000:10000 test_python /bin/sh -c “socat TCP4-LISTEN:10000,tcpwrap=script,reuseaddr,fork EXEC:”/usr/bin/env python2 -u /test.py""
‘’’
0x06 使用Dockerfile配置环境更换基础镜像的源
- 首先更换源的原因主要是随着Debian 9 Stretch国内常用镜像源的普及,但由于伟大的墙的存在,所以需要更换为国内的镜像站点。
- 使用sed命令实现替换
sed命令介绍 https://www.runoob.com/linux/linux-comm-sed.html
- 以更换 python:2-slim基础镜像源为例
FROM python:2-slim
RUN sed -i s@/deb.debian.org/@/mirrors.aliyun.com/@g /etc/apt/sources.list && \
sed -i s@/security.debian.org/@/mirrors.aliyun.com/@g /etc/apt/sources.list && \
apt-get update -y
不同的基础镜像被替换的有可能不同,一般都是Debian的默认源地址http://deb.debian.org
需要根据具体镜像源地址进行替换
主要是执行
RUN sed -i s@/deb.debian.org/@/mirrors.aliyun.com/@g /etc/apt/sources.list
将Debian的默认源地址改成阿里云的地址即可,比如将http://deb.debian.org改成https://mirrors.xxx.com