代码审计系列题目CTFD部署(上)
关于简单部署题目请参考:https://www.cnblogs.com/Cl0ud/p/13783325.html
如果需要进行较复杂部署,可参考本篇
PHP代码审计系列题目的部署,较之前的部署方案,改变的地方除了题目代码之外,还将题目权限进行了限制,题目结构更正规化,基础镜像没有进行修改,还是原来的 php:5.6-fpm-alpine,其不足在于该环境没有php.ini 文件,如果你出题不需要修改这个这一点可以直接忽略,优点在于该环境相比直接使用ubuntu的镜像环境占用的空间更小,这里当然就直接选择使用占用空间更小的php:5.6-fpm-alpine
目录结构如下:
-模板
--dockerfile
--docker-compose.yml
--start.sh
--www(这里面放题目的源代码)
---...
---...
这里以CTFTraining中0ctf_2016_unserialize题目的Dockerfile为例进行讲解:
其题目目录环境为:
FROM php:5.6-fpm-alpine
LABEL Author="Virink <virink@outlook.com>"
LABEL Blog="https://www.virzz.com"
COPY files /tmp/
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories \
&& apk add --update --no-cache nginx mysql mysql-client \
&& docker-php-source extract \
&& docker-php-ext-install mysql \
&& docker-php-source delete \
&& mysql_install_db --user=mysql --datadir=/var/lib/mysql \
&& sh -c 'mysqld_safe &' \
&& sleep 5s \
&& mysqladmin -uroot password 'qwertyuiop' \
&& mysql -e "source /tmp/db.sql;" -uroot -pqwertyuiop \
&& mkdir /run/nginx \
&& mv /tmp/docker-php-entrypoint /usr/local/bin/docker-php-entrypoint \
&& mv /tmp/nginx.conf /etc/nginx/nginx.conf \
&& mv /tmp/vhost.nginx.conf /etc/nginx/conf.d/default.conf \
&& mv /tmp/src/* /var/www/html \
&& chmod -R -w /var/www/html \
&& chmod -R 777 /var/www/html/upload \
&& chown -R www-data:www-data /var/www/html \
&& rm -rf /tmp/* \
&& rm -rf /etc/apk
EXPOSE 80
VOLUME ["/var/log/nginx"]
CMD ["/bin/sh", "-c", "docker-php-entrypoint"]
简单查看这段dockerfile,很容易注意到这一点:(简单截取一部分
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories \
&& apk add --update --no-cache nginx mysql mysql-client \
为什么这里要用两个&&而不是直接使用两个RUN
原因在于从 Docker 1.10 开始,COPY、ADD 和 RUN 语句会向镜像中添加新层。前面的示例创建了两个层而不是一个。
镜像的层就像 Git 的提交(commit)一样。
Docker 的层用于保存镜像的上一版本和当前版本之间的差异。就像 Git 的提交一样,如果你与其他存储库或镜像共享它们,就会很方便。
实际上,当你向注册表请求镜像时,只是下载你尚未拥有的层。这是一种非常高效地共享镜像的方式。
但额外的层并不是没有代价的。
层仍然会占用空间,你拥有的层越多,最终的镜像就越大。Git 存储库在这方面也是类似的,存储库的大小随着层数的增加而增加,因为 Git 必须保存提交之间的所有变更。
所以在这里我们将其合并成一条命令,就是为了减少其层数而降低占用空间的大小。
现在从头开始阅读Dockerfile代码
FROM php:5.6-fpm-alpine
LABEL Author="Virink <virink@outlook.com>"
LABEL Blog="https://www.virzz.com"
FROM 命令指定基础镜像
LABEL指令用于为镜像添加元数据,多用于声明构建信息,作者、机构、组织等,每一个LABEL指令会生成一个新的镜像层,如果你使用多个label,将导致构建出一个低效的镜像,跟RUN命令类似,同时对我们几乎没啥帮助,可以略过了。
COPY files /tmp/
复制主机当前文件夹下的files文件夹到容器/tmp文件夹下
详细讲解一下RUN命令下的各条指令
sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
在国内访问 apk
仓库较缓慢,修改docker容器下载源为中科大镜像,加速后面几步的更新和下载
apk add --update --no-cache nginx mysql mysql-client
Alpine
提供了自己的包管理工具 apk
,可以通过 https://pkgs.alpinelinux.org/packages
网站上查询包信息,也可以直接通过 apk
命令直接查询和安装各种软件。
这里使用apk进行下载nginx,mysql,mysqli-client
接着是三条:
docker-php-source extract
docker-php-ext-install mysql
docker-php-source delete
关于docker-php-source , docker-php-ext-install ,docker-php-enable-docker-configure,详情参考:https://www.cnblogs.com/yinguohai/p/11329273.html
这里简单介绍:
docker-php-source extract
在php容器中创建一个/usr/src/php目录,里面放了一些自带的文件
docker-php-ext-install mysql
安装并启动PHP扩展,命令格式:
docker-php-ext-install “源码包目录名”
这里即安装并启动了mysql服务
docker-php-source delete
这里对 docker-php-source extract 初始化的 /usr/src/php目录进行了删除
接着是一段mysql的操作
mysql_install_db --user=mysql --datadir=/var/lib/mysql
sh -c 'mysqld_safe &'
sleep 5s
mysqladmin -uroot password 'qwertyuiop'
mysql -e "source /tmp/db.sql;" -uroot -pqwertyuiop
使用mysql_install_db命令初始化MySQL数据库目录
mysql_install_db --user=mysql --datadir=/var/lib/mysql
mysqld_safe是服务端工具,用于启动mysqld,并且是mysqld的守护进程,在后面加&符号令其在后台运行,而因为mysqld_safe是mysqld的守护进程,所以mysqld_safe会在启动MySQL服务器后继续监控其运行情况,并在其死机时重新启动它。
sh -c 'mysqld_safe &'
等待五秒
sleep 5s
一般dockerfile里面的sleep都是为了等待一些服务启动,防止运行到后面的命令时前面的服务还没启动,这样就会导致报错退出,sleep很大程度上降低了出现这种情况的概率。
mysqladmin -uroot password 'springbirdtcl11111'
mysqladmin命令是mysql服务器管理任务的客户端工具,它可以检查mytsql服务器的配置和当前工作状态,创建和删除数据库,创建用户和修改用户密码等操作。
这里用于修改密码了
mysql -e "source /tmp/db.sql;" -uroot -pqwertyuiop
导入并执行sql文件
mkdir /run/nginx
mv /tmp/docker-php-entrypoint /usr/local/bin/docker-php-entrypoint
mv /tmp/nginx.conf /etc/nginx/nginx.conf
mv /tmp/vhost.nginx.conf /etc/nginx/conf.d/default.conf
这一段是创建nginx文件夹以及导入我们自己写的配置文件
mv /tmp/src/* /var/www/html
chmod -R -w /var/www/html
chmod -R 777 /var/www/html/upload
chown -R www-data:www-data /var/www/html
移动源代码到html文件夹下,这样外部可访问
mv /tmp/src/* /var/www/html
进行权限限制
chmod -R -w /var/www/html
chmod -R 777 /var/www/html/upload
chown -R www-data:www-data /var/www/html
-R : 对目前目录下的所有文件与子目录进行相同的权限变更(即以递归的方式逐个变更)
+
表示增加权限、- 表示取消权限、= 表示唯一设定权限。
r 表示可读取,w 表示可写入,x 表示可执行,X 表示只有当该文件是个子目录或者该文件已经被设定过为可执行。
这里递归取消了html目录下的可写入权限
因为7的二进制数为111,即递归增加了任意用户对于/upload目录下的读写执行权限
然后将/html目录下的权限限制为www-data权限
rm -rf /tmp/*
rm -rf /etc/apk
删除之前产生的源文件,以及清除apk安装包们
对外暴露80端口
EXPOSE 80
接着声明容器中/var/log/nginx为匿名卷
VOLUME ["/var/log/nginx"]
当Dockerfile中声明了匿名卷但是run的时候没有使用 -v绑定匿名卷的话那么docker就会在/var/lib/docker/volumes这个目录下创建一个目录来绑定匿名卷。
也就是容器内的nginx的log日志会被挂载到主机的/var/lib/docker/volumes的一个新的目录下
最后是
CMD ["/bin/sh", "-c", "docker-php-entrypoint"]
该docker-php-entrypoint文件在原目录的files下,其内容为:
#!/bin/sh
sed -i "s/flag{0ctf_2016_unserialize_is_very_good!}/$FLAG/" /var/www/html/config.php
export FLAG=not_flag
FLAG=not_flag
php-fpm &
nginx &
mysqld_safe &
tail -F /var/log/nginx/error.log /var/log/nginx/access.log
其实就是实现动态flag,并且启动相关服务,校内平台暂时还没有用到动态flag,所以暂时不需要考虑该问题。
下面的部分摘抄自v0n师傅的博客(写到这里的时候才发现v0n师傅写过相关的文章了,早知道分析另外一个dockerfile了呜呜呜
摘抄:
从上面的过程中,我们看到对于一道题目来说,除了源码以外,最大的不方便之处就是还要有相关的nginx文件配置,在这里我推荐virink写的base_image_nginx_mysql_php_56来辅助我们快速出题。 这个镜像主要好在不需要我们去配置其他的nginx设置,而且还支持自动导入db.sql文件,支持自动执行flag.sh文件。以我在minil出的一道题为例。题目中主要是需要配置数据库.
我们只需要src文件夹中放入源码、flag.sh、db.sql(flag.sh、db.sql文件名不能变),现在我们dockerfile只需要这么写:
FROM ctftraining/base_image_nginx_mysql_php_56
COPY src /var/www/html
RUN mv /var/www/html/flag.sh / \
&& chmod +x /flag.sh
这个镜像就会自动为我们配置相关的nginx文件,同时自动导入要执行的db.sql文件来配置数据库(默认用户为root,密码也为root,执行后db.sql会被删掉)
同时镜像还会自动执行flash.sh来从环境变量中读取flag写入到flag.php中(需要注意的是,flag.sh必需在根目录下,也就是我们需要执行mv /var/www/html/flag.sh /这一步的原因所在)。 可以看到,这个镜像极大的方便了我们对Dockerfile的编写,把Dockerfile简化到只需要两三句话就能搞定。
如果是要在php7的环境下出题,可以采用base_image_nginx_mysql_php_73
至于python还是java环境的出题,我暂时还没尝试过,就不班门弄斧了。
如果还想通过更多的环境学习如何出题,可以在这个项目中查看更多的题目。
参考链接:
- https://www.mrkaixin.top/posts/ace025e8/
- https://github.com/CTFTraining/0ctf_2016_unserialize/blob/9c624fad8b7dd380b0e653a1f67cadef741db6c9/Dockerfile
- https://github.com/bowu678/php_bugs
- https://www.k0rz3n.com/2018/05/17/搭建docker的 CTF getshell环境/
- https://www.infoq.cn/article/3-simple-tricks-for-smaller-docker-images
- https://github.com/hongriSec/PHP-Audit-Labs
- https://0clickjacking0.github.io/2020/08/24/CTF中关于md5的一些总结/
- https://juejin.cn/post/6844903999032786952
- https://yeasy.gitbook.io/docker_practice/os/alpine
- https://www.cnblogs.com/yinguohai/p/11329273.html
- https://www.v0n.top/2020/05/01/如何正确使用Docker出一道CTF题目/