March 2, 2023

记一次基于H1VE的动态靶场搭建

前言

关于动态靶场的问题也是搞得十分困扰啊,由于各种python的不兼容问题,导致2022年8月前的教程和一些模板都受到了影响,并且没有做处理,导致前期不是很顺利,不过好在最后还是柳暗花明了(虽然与BUG共存了)但能RUN起来就是好东西
参考:https://5ime.cn/h1ve.html

1、下载H1ve

git clone [https://github.com/D0g3-Lab/H1ve.git](https://github.com/D0g3-Lab/H1ve.git)
下载完后可以看到以下目录结构:
image.png
有3个yml文件,我们选择single.yml即可,single-nginx是加了一层反向代理的容器

2、搭建(*坑1)

在官网上给的是直接docker-compose up即可,可实际上不行,会报一层namerror的错误,貌似是啥bug,但是不影响运行,实际上假如你按着README走是出不来的,他会永远卡在这一步的报错:
image.png
你需要docker-compose -f single.yml up -d

假如不后台搭建,前台会一直卡在这,这是一个小坑点,然后搭建完后docker结构如下:
image.png
其中frpc和frps是用来穿透docker容器的:
image.png
docker network inspect h1ve_frp_containers:
image.png
frp的服务端和ctfd绑定在一起,方便穿透,之后我们题目容器也是创建在这个网络中的,这里是介绍一下整个架构
一些常用指令:

1
2
docker-compose -f xxx up -d #后台搭建
docker-compose -f xxx stop #停止搭建的所有容器

3、出题(Web)——*大坑

到了这里你看到了官网炫酷的样子,你以为一切都尘埃落定,出题只是分分钟的事情,那就特错大错啦!
image.png
不出意外的话进入这个界面一开始全部都是NONE.点UPDATE也不好使,这也是我觉得最操蛋的一个地方
应该是python搭建的导致请求过快,然后后端没接受到,所以这里需要手动burp抓包,然后放包给他改了,我就是直接一个无语
image.png

image.png
这一部分把DirectIP改为你VPS的地址,上面的是假如你有域名就可以填
mini port和max port指的是开放的最小和最大端口,下面的config对着填就好,是frpc.ini的配置内容,在linux中也可以查看到:
image.png
将这些配置好后就是出题目了,出题需要编写Dockerfile和docker-compose.yml,题目目录结构如下(参考5ime师傅):

1
2
3
4
5
6
7
test # 题目存放文件夹必须小写英文
├─ Dockerfile
├─ docker-compose.yml
├─ files
│ ├─ index.php
│ └─ start.sh
└─ flag

Dockerfile

1
2
3
4
5
6
7
FROM php:7.3-apache
COPY files /var/www/html
RUN chmod 755 /var/www/html && \
chown root:root /var/www/html && \
chmod +x /var/www/html/start.sh
CMD /var/www/html/start.sh
EXPOSE 80 #对外暴露的端口

docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
version: '2'
services:
service:
# build 该置顶目录下的dockerfile
build: .
# image 指定build Dockerfile生成镜像的名称
image: test
ports:
- 9999:80
volumes:
# 挂载的 Flag
- "$PWD/flag:/flag"
tty: true
networks:
- net

networks:
# 配置docker network
net:
external:
name: h1ve_frp_containers

这里的flag有点讲究,我们在搭建的时候在当前文件夹准备了一个flag,内容无所谓,只要前缀和大括号包裹起来就行,然后在启动动态容器的时候,他会把容器内的flag进行替换,达到动态flag的效果
然后就是ports的问题,前面的9999可以随便改,但是绝对不能去掉,去掉了的话开启的容器端口就和frp映射的对应不上了(BUG)

files/start.sh

1
2
3
4
5
6
#!/bin/bash

v=`cat /flag`
cat /var/www/html/index.php | sed -i "s/flag{test_flag}/$v/" /var/www/html/index.php # 用于正则匹配flag并替换

apache2-foreground

这是启动容器后需要做的事情

files/index.php

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>测试题目</title>
</head>
<body>
flag{test_flag}<!--这里会被start.sh替换成动态flag-->
</body>
</html>

flag

1
HnuSec{f4af4a4fg485a6w4f89a489zv}

放入题目文件夹

image.png
我们题目需要放在source目录下(相对路径)
在上面的docker-compose.yml文件中可以看到volumes是挂载了我们当前所在目录,和容器内同步了,因此在外边改就能改到里面,这很方便,我们需要把题目放在source目录里,比如里面的test/JavaMaster_final
image.png
这样我们在部署题目的时候只需要填一个文件夹test/JavaMaster_final即可完成部署
image.png
题目配置如上即可,FPR PORT是需要暴露的容器端口,tomcat就是8080,一般都是80

4、复杂环境出题经验

就拿这次招新赛的WonderfulのSQL来看,一个由javaweb搭建的有JDBC的项目,实际上考的也就是双写绕过的注入,只不过我想练一下自己的部署能力
在这就有很多坑点啊

Dockerfile的编写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
FROM java8-tomcat-8-mysql:1
ENV MYPATH /opt/tomcat/webapps/ROOT
ENV JAVA_OPTS="-Xmx512m"
RUN rm -r $MYPATH
RUN mkdir -p $MYPATH
EXPOSE 8080
WORKDIR $MYPATH
ADD ./MavenDemo/ $MYPATH
ADD ./wait-for-it.sh /
ADD ./start.sh /
ADD ./flag /
ADD ./wonderful.sql /
RUN chmod +x /start.sh
RUN chmod +x /wait-for-it.sh
CMD ["/start.sh"]

start.sh

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
set -ex
cd /
./wait-for-it.sh -t 0 mysql:3306 -- echo "mysql is up"
v=`cat /flag`
mysql -uroot -pjiayou357753 -h mysql -e "create database wonderful"
mysql -uroot -pjiayou357753 -h mysql wonderful -e "source /wonderful.sql"
mysql -uroot -pjiayou357753 -h mysql wonderful -e "USE wonderful;INSERT INTO secret(name,value) values('flag_here','$v')"

catalina.sh run

我们需要捋一捋思路,在tomcat容器和mysql容器中,谁应该先搭建好呢?那肯定是mysql啊,所以假如mysql和tomcat同时搭建好的话,那肯定会出现一些问题
这次就遇到了sql初始化的问题,start.sh中准备的是对mysql初始化的工作,但是在我不用wait-for-it.sh 脚本的时候,tomcat执行mysql指令是不会等mysql容器初始化好再执行的,所以就导致失败

docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
version: '2'
services:
service:
build: .
image: javasql:1
ports:
- 9999:8080
volumes:
- "$PWD/flag:/flag"
tty: true
restart: always
networks:
- net
depends_on:
- mysql


mysql:
image: boogisql:1
restart: always
networks:
- net2



networks:
net:
external:
name: h1ve_frp_containers
net2:
external:
name: h1ve_frp_containers

其次就是docker-compose的编写,其中service 依赖了mysql容器,意味着需要等mysql创建好了,再创建tomcat容器,但这还不够,所以要加wait-for-it.sh去等待
然后就是docker网络的问题,这里由于mysql和tomcat容器需要互相能ping通,因此把他们都放入了容器网络中(在这里mysql就默认是主机名字了)

Maven项目

对于部署java项目呢,maven打包一定也要把依赖打包进来哦,要不然是无效的

flag

flag内容和上面一样,大括号内的东西随便取就行了

5、动态flag

动态flag的问题就和一开始说了,在启动容器时会对容器挂载的flag进行替换,可以利用这一点进行操作,就可上述的Java项目一样,令v=cat /flag``,把v赋值flag,在进行后续操作

flag格式

在默认的条件下flag中的-是被去掉了的,想要加回去得修改一下模板文件:
/opt/H1ve/CTFd/plugins/ctfd-owl/docker_utils.py中产生了flag:
image.png
这里flag原本是flag=prefix+flag.replace("-","")把中间的横杠去掉了,这里我去掉了replace,这就使得flag更加美观

总结

大致就这么多啦,有问题会补上的

bug集合

填坑按钮无效

这是谷歌浏览器的问题,用Firefox和国内的!!!妈的气死我了,又发现火狐其实也不能解决,这是他架构的问题

500错误

因为redis中max client在后端处理中没有清空,因此会导致达到最大值
解决方法指标不治本:把max client拉满CONFIG SET max client 100000,满了之后就重启H1ve容器

About this Post

This post is written by Boogipop, licensed under CC BY-NC 4.0.

#开发#靶场搭建