拓冰建站拓冰建站
首页 / 资讯中心 / 正文

CTF Web源码环境搭建与Flag挖掘实战指南

简介这是一套面向网络安全初学者与CTF爱好者的Web方向练习源码环境以备份形式打包适合用来搭建本地靶场、熟悉常见Web漏洞的成因与利用方式。压缩包共13个文件约135KB以php脚本为主辅以html页面、log日志、pcap流量包、txt文本与gif图片分别对应题目入口、页面结构、运行记录、流量分析与线索素材整体轻量、便于快速部署和反复调试。资源中预设了信息泄露、文件包含、编码线索等典型题型读者可通过分析服务端配置信息、梳理日志与流量记录、追踪隐藏路径逐步还原出题思路并完成解题。目前已有715人学习下载适合作为入门级Web安全实战素材帮助理解常见安全问题的排查与防御方法。1. 拿到一套 CTF Web 源码环境先别急着翻 flag很多人第一次接触 CTF Web 方向卡住的地方不是不会做题而是环境跑不起来。你从题库或者别人手里拿到一个ctf_web源码环境.zip解压出来一堆目录有index.php、有flag.php、有docker-compose.yml也可能只有一个孤零零的www文件夹然后呢直接双击打开用浏览器访问本地文件大概率是一片空白或者源码被当文本读出来。这套东西的本质是把一道 Web 题从「线上靶机」搬到「本地可控」。它解决的核心问题是你不需要等比赛开放、不需要担心靶机被别人打崩、可以随便改源码看逻辑、可以反复重置数据库。适合两类人——刚入门 CTF Web 想搞懂题目怎么构造的新手以及想自己出题、搭常态化训练环境的老手。但「简单」两个字是陷阱。简单意味着它省略了部署文档省略了依赖说明甚至省略了入口提示。你得自己判断这是什么类型的题、用什么语言写的、需不需要数据库、跑在哪个端口。这一章先把判断思路立住后面几章再动手。2. 解压之后先做三件事认语言、找入口、判依赖拿到压缩包不要上来就docker-compose up。我一般会先花五分钟做静态侦察这三件事做完后面能省掉大量翻车时间。2.1 用文件后缀和目录结构判断技术栈先看目录树。CTF Web 题常见的技术栈就那么几种后缀基本能暴露一切文件特征技术栈常见题型.php为主有flag.phpPHP文件包含、反序列化、命令执行.pyrequirements.txtPythonFlask/DjangoSSTI、反序列化、逻辑漏洞.jspackage.jsonNode.js原型链污染、SSRF.javapom.xmlJava反序列化、SpEL 注入只有.html.js纯前端原型链、XSS、JS 逆向判断完语言再看有没有Dockerfile或docker-compose.yml。有的话说明作者已经帮你把运行环境定义好了这是最省事的情况。没有的话你得自己配 PHP 或者 Python 环境。# 先看目录结构两层足够 find . -maxdepth 2 -type f | head -50 # 找关键文件入口、配置、flag 相关 find . -name index.* -o -name flag* -o -name *.conf -o -name Dockerfile -o -name docker-compose*这两条命令的逻辑很简单第一条限制深度避免在大项目里刷屏第二条把「入口文件、flag 文件、配置文件、容器定义」一次性捞出来。参数-maxdepth 2可以根据实际情况调整如果目录很深就加到 3。2.2 找入口文件从 Web 服务器配置反推入口文件不一定是index.php。有些题故意把入口藏在admin/或者api/下面甚至通过.htaccess或者 Nginx 配置做重定向。所以更可靠的做法是先找 Web 服务器配置。如果压缩包里带了nginx.conf或者apache2.conf直接看root和location指令。root指向哪个目录那个目录就是 Web 根目录location里的rewrite规则决定了 URL 怎么映射到文件。# 找 Web 服务器配置文件 find . -name *.conf -o -name .htaccess -o -name nginx* -o -name apache* # 如果找到 nginx.conf看 root 和 location grep -E root|location|rewrite nginx.conf如果没有任何配置文件那就默认index.php或index.html在根目录。这时候直接起一个 PHP 内置服务器就能跑# 在源码根目录执行-t 指定 Web 根目录-S 指定监听地址和端口 php -S 0.0.0.0:8080 -t ./www-t ./www表示把www目录当作 Web 根目录如果你的入口文件就在当前目录改成-t .。0.0.0.0允许局域网访问自己本机调试用127.0.0.1也行。2.3 判断是否需要数据库和额外服务很多 CTF Web 题不是纯文件就能跑的它需要 MySQL 存用户数据、需要 Redis 存 session、甚至需要单独的 flag 服务。判断方法很简单搜源码里的连接字符串。# 搜数据库连接相关关键词 grep -rE mysqli|PDO|mysql_connect|redis|mongodb --include*.php --include*.py . # 搜配置文件里的 host、port、password grep -rE DB_HOST|DB_PORT|REDIS_HOST|MYSQL_ .如果搜出来有DB_HOSTdb这种说明它原本是跑在 Docker 网络里的db是容器名。你本地直接跑的话要么改配置指向127.0.0.1要么用docker-compose把数据库一起拉起来。提示如果源码里写死了localhost但你的数据库没跑页面会报连接错误。这时候先别改代码优先看有没有docker-compose.yml有的话直接用它环境变量和网络都配好了。3. 用 Docker 把环境跑起来从 compose 文件到可访问页面确认完技术栈和依赖接下来就是让它跑起来。CTF Web 源码环境最标准的跑法就是 Docker因为作者通常会把所有依赖打包好。这一章把 Docker 跑环境的完整流程拆开讲包括端口映射、环境变量、常见启动失败的处理。3.1 读懂 docker-compose.yml 里的三个关键字段拿到docker-compose.yml不要直接up先看三个地方services下面有哪些服务、每个服务的ports映射、environment里的变量。# 一个典型的 CTF Web 题 compose 文件结构 services: web: build: ./web # 构建上下文说明 web 目录里有 Dockerfile ports: - 8080:80 # 宿主机 8080 映射到容器 80 environment: - FLAGflag{test_flag_here} # 环境变量注入 flag depends_on: - db # 依赖 db 服务启动顺序有保证 db: image: mysql:5.7 environment: - MYSQL_ROOT_PASSWORDroot - MYSQL_DATABASEctfports的格式是宿主机端口:容器端口。上面这个例子你访问http://127.0.0.1:8080就能打到容器的 80 端口。如果写的是80:80那就直接访问http://127.0.0.1。environment里的FLAG很关键。很多题把 flag 通过环境变量注入源码里用getenv(FLAG)读取。这意味着你改 compose 文件里的FLAG值就能改题目的答案自己出题或者改题的时候非常方便。depends_on只保证启动顺序不保证 db 已经初始化完成。如果 web 启动时报「数据库连接失败」大概率是 db 还没初始化完web 就急着连了。解决办法是在 web 的启动脚本里加等待逻辑或者手动重启一次 web 容器。3.2 启动、验证、重置的标准命令序列# 1. 构建并后台启动所有服务 docker-compose up -d --build # 2. 看容器状态确认没有 exited docker-compose ps # 3. 看 web 服务日志找报错 docker-compose logs web # 4. 验证页面可访问 curl -I http://127.0.0.1:8080 # 5. 如果需要重置环境比如数据库被改乱了 docker-compose down -v docker-compose up -d--build强制重新构建镜像适合你改了源码之后。-d是后台运行。down -v里的-v会删除数据卷数据库会回到初始状态这是「后悔药」级别的操作改乱了就用它。curl -I只拿响应头用来快速判断服务是否活着。返回200或者302都算正常返回Connection refused说明端口没映射对或者容器没起来。3.3 端口冲突和权限问题的处理最常见的启动失败是端口被占用。比如你本机已经跑了 Nginx 占了 80compose 里又映射80:80就会报port is already allocated。解决办法是改宿主机端口比如改成8081:80然后访问8081。# 查看本机端口占用情况 ss -tlnp | grep -E 80|8080|3306 # 如果 80 被占用改 compose 文件里的端口映射 # 把 80:80 改成 8081:80然后重新 up docker-compose up -d另一个坑是文件权限。有些题目的源码目录挂载到容器里容器内用户和宿主机用户 UID 不一致导致写文件失败。表现是页面报「Permission denied」或者「无法写入 session」。解决办法是在 compose 文件里加user: 1000:1000或者直接chmod -R 777源码目录仅限本地调试别在生产环境这么干。注意docker-compose down -v会删除所有数据卷包括数据库数据。如果你已经往数据库里插了测试数据这个操作会全部清掉。确认不需要保留再执行。4. 源码里找 flag 的四个切入点从入口到敏感函数环境跑起来之后页面能访问了接下来才是真正的 CTF Web 解题环节。这一章不讲具体某道题而是讲一套通用的源码审计切入点。你拿到任何一套 CTF Web 源码按这四个方向去搜大概率能找到 flag 的藏身之处或者利用链的起点。4.1 从入口文件顺着请求流往下追入口文件是index.php的话先把它完整读一遍。重点看它include或require了哪些文件这些文件里有没有处理用户输入的逻辑。// 典型的入口文件结构 ?php include config.php; // 数据库配置 include functions.php; // 公共函数 $action $_GET[action] ?? home; switch ($action) { case login: include login.php; break; case upload: include upload.php; break; default: include home.php; }这种结构下action参数控制加载哪个文件。如果include的路径拼接了用户输入就可能存在文件包含漏洞。比如include $_GET[page] . .php你可以通过page../../../../etc/passwd%00来读取任意文件PHP 5.3 以下版本。追请求流的技巧是从 URL 参数出发看这个参数最终传到了哪个函数。用grep搜参数名比如grep -rn action --include*.php .把所有用到action的地方列出来逐个看。4.2 搜敏感函数命令执行、文件读取、反序列化CTF Web 题的核心利用点通常集中在几类敏感函数上。直接在源码目录里搜这些函数名能快速定位到可疑代码。# 命令执行相关 grep -rnE system|exec|passthru|shell_exec|popen|proc_open --include*.php . # 文件读取相关 grep -rnE file_get_contents|fopen|readfile|include|require --include*.php . # 反序列化相关 grep -rnE unserialize|serialize|__wakeup|__destruct|__toString --include*.php . # SQL 注入相关 grep -rnE mysql_query|mysqli_query|-query|SELECT.*FROM --include*.php .搜出来之后重点看这些函数的参数是否可控。比如system($_GET[cmd])就是典型的命令执行漏洞你传?cmdcat /flag就能拿到 flag。但实际题目不会这么直白通常会加过滤比如str_replace(cat, , $cmd)这时候你就得用c\at或者tac绕过。passthru这个函数在热词里出现过它和system的区别是passthru直接输出二进制数据适合执行会返回图片或者二进制内容的命令。用法上没区别都是命令执行。4.3 看配置文件和环境变量里的 flag有些题目的 flag 不在源码里而是通过环境变量注入。这时候你要看config.php或者.env文件里有没有getenv调用。// config.php 里常见的 flag 读取方式 $flag getenv(FLAG); if ($flag false) { $flag flag{default_flag}; }这种情况下flag 的值在docker-compose.yml的environment里。你改那个值页面显示的 flag 就跟着变。如果你是在做一道线上题flag 在靶机的环境变量里你需要通过漏洞读到环境变量比如命令执行env或者文件包含/proc/self/environ。还有一种情况是 flag 写在数据库里。这时候你需要先通过 SQL 注入或者弱口令进后台然后查数据库。常见表名是flag、secrets、users字段名是flag、value、secret。4.4 用 diff 对比原始源码和修改后的源码如果你自己改过源码或者想确认题目有没有被改动过可以用diff对比。把原始压缩包解压一份把你正在跑的源码复制一份然后对比。# 对比两个目录的差异-r 递归-q 只显示有差异的文件名 diff -rq original/ running/ # 如果只想看具体差异内容去掉 -q diff -r original/ running/ | head -100这个技巧在「题目环境被前人改过」或者「你想确认自己有没有改错」的时候特别有用。比如你改了FLAG环境变量但页面显示的还是旧 flagdiff 一下就知道是不是改错了文件。提示diff -rq只告诉你哪些文件不同不告诉你具体哪里不同。要定位具体行用diff -u original/file.php running/file.php。5. 避坑与排查环境跑不起来、flag 读不到、页面报错的常见原因这一章集中讲踩坑记录。CTF Web 源码环境看起来简单但实际跑的时候会遇到各种玄学问题。下面五条是我自己踩过或者见别人踩过的每条按「现象 → 原因 → 解决」写。5.1 页面返回 500 但日志里没有 PHP 错误现象浏览器访问显示500 Internal Server Error但docker-compose logs web里只有 Nginx 的访问日志没有 PHP 的报错信息。原因PHP 的错误显示被关掉了或者错误日志输出到了容器内的其他文件。CTF 题为了「干净」经常在php.ini里设置display_errors Off。解决进容器改php.ini或者直接在源码入口文件顶部加两行临时开启错误显示。?php ini_set(display_errors, 1); error_reporting(E_ALL); // 下面才是原来的代码如果不想改源码可以进容器执行php -i | grep error_log找到错误日志路径然后tail -f那个文件。5.2 数据库连接失败但 db 容器明明是 Up 状态现象web 页面报SQLSTATE[HY000] [2002] Connection refused但docker-compose ps显示 db 容器是Up。原因MySQL 容器启动后需要几秒钟初始化depends_on只保证容器启动顺序不保证服务就绪。web 容器在 db 还没准备好连接的时候就发起了连接。解决重启 web 容器或者手动等几秒再访问。更彻底的办法是在 web 的启动脚本里加一个等待循环。# 在 web 容器的启动脚本里加这段等待 db 端口可连 until nc -z db 3306; do echo waiting for db... sleep 1 done # 然后再启动 web 服务5.3 flag 文件存在但读不出来提示 Permission denied现象你知道flag.php在某个目录下但通过文件包含或者命令执行读的时候报权限错误。原因flag 文件的权限设置成了400或者440只有特定用户能读。Web 服务运行的用户比如www-data没有读权限。解决如果是本地环境直接chmod 644 flag.php。如果是线上题你需要通过提权或者找到以 root 运行的进程来读。CTF 里常见的做法是找 SUID 程序或者写定时任务。5.4 命令执行没有回显但命令确实执行了现象你通过漏洞执行了cat /flag但页面上什么都没显示。原因system和passthru会直接输出但exec和shell_exec只返回结果不输出。如果你用的是exec需要echo出来。另外有些题目的 PHP 配置了disable_functions把system、exec都禁了。解决先确认用的是哪个函数。如果是exec改成echo exec(cat /flag);。如果disable_functions禁了常用函数试试pcntl_exec、imap_open或者LD_PRELOAD绕过。# 查看当前 PHP 禁用了哪些函数 php -i | grep disable_functions5.5 端口映射正确但浏览器访问超时现象docker-compose ps显示端口映射是0.0.0.0:8080-80/tcp但浏览器访问127.0.0.1:8080一直转圈最后超时。原因Web 服务在容器内监听的是127.0.0.1:80而不是0.0.0.0:80。Docker 的端口映射需要服务监听在0.0.0.0才能从宿主机访问。解决改 Web 服务器配置把listen 127.0.0.1:80改成listen 0.0.0.0:80。Nginx 的话改nginx.confApache 的话改ports.conf。注意如果你是在 macOS 或者 Windows 上跑 Docker127.0.0.1可能不通试试host.docker.internal或者直接用localhost。6. 把环境改造成自己的出题模板改 flag、加漏洞、导出镜像跑通别人的环境只是第一步。真正有价值的是把这套环境改造成你自己的出题模板或者改造成一个可以反复练习的靶场。这一章讲三个进阶操作改 flag 值、加一个自定义漏洞、把环境导出成镜像分发给别人。6.1 改 flag 的三种方式第一种是改docker-compose.yml里的environment。这是最干净的因为源码不用动。environment: - FLAGflag{my_custom_flag_2025}第二种是改源码里的硬编码。搜flag{找到所有出现的地方替换掉。# 搜所有包含 flag{ 的文件 grep -rn flag{ --include*.php --include*.py --include*.js . # 批量替换先备份 sed -i s/flag{old_flag}/flag{new_flag}/g $(grep -rl flag{old_flag} .)第三种是改数据库里的 flag 记录。先进 db 容器然后UPDATE对应的表。# 进 db 容器 docker-compose exec db mysql -uroot -proot ctf # 在 MySQL 里执行 UPDATE flags SET valueflag{db_flag} WHERE id1;三种方式优先级是环境变量 数据库 源码硬编码。因为环境变量在运行时注入优先级最高。6.2 加一个自定义漏洞以文件包含为例如果你想在这个环境里加一个文件包含漏洞用来练习可以在入口文件里加一段代码。// 在 index.php 的 switch 之前加 if (isset($_GET[page])) { $page $_GET[page]; // 故意不加过滤制造文件包含漏洞 include $page . .php; exit; }加完之后你就可以通过?page../../../../etc/passwd来测试文件包含。如果想让它能读 flag把 flag 文件放在 Web 目录下然后?pageflag就能读到。但要注意PHP 的include默认会拼接.php后缀所以?pageflag实际包含的是flag.php。如果你想包含非 PHP 文件需要用%00截断PHP 5.3 以下或者用php://filter协议。# 用 php://filter 读源码base64 编码后输出 curl http://127.0.0.1:8080/?pagephp://filter/convert.base64-encode/resourceflag这个 payload 会把flag.php的内容 base64 编码后输出你解码就能看到源码。这是 CTF Web 里非常经典的读源码手法。6.3 导出镜像和分发环境如果你想把改造好的环境发给别人有两种方式。一种是直接打包整个目录别人拿到后docker-compose up就行。另一种是导出镜像。# 导出镜像为 tar 文件 docker save -o ctf_web_env.tar ctf_web:latest # 别人导入镜像 docker load -i ctf_web_env.tar # 然后直接 docker run 或者用 compose 启动导出镜像的好处是别人不需要重新构建坏处是文件大。一个 PHP MySQL 的环境镜像 tar 包大概 500MB 到 1GB。如果只是源码级别的分发直接打包目录更轻量。我自己的习惯是源码目录 docker-compose.yml一起打包写一个README.md说明端口和 flag 位置。这样别人拿到就能跑不需要问我任何问题。这个习惯帮我省了很多重复解释的时间也让我的出题模板可以反复复用。希望帮到你。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门