關於簡單部署題目請參考: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題目/