护网杯 2018 - Web - easy_laravel

护网杯 2018 - Web - easy_laravel
Hunter题目信息
| 项 | 内容 |
|---|---|
| 比赛 | 护网杯 2018 |
| 方向 | Web |
| 难度 | 中等偏难 |
| 环境 | 授权靶场(已脱敏) |
信息收集
源码泄露
首页 HTML 注释泄露源码地址:<!-- code: https://github.com/qqqqqqvq/easy_laravel -->。审计源码发现:
NoteController存在 SQL 注入(name字段直接拼入查询)。UploadController::check()的file_exists()两个参数可控,可触发 phar 反序列化。FlagController::showFlag()读取/th1s1s_F14g_2333333。
环境差异(关键)
原题使用 nginx + mysql,buuoj 复现环境使用 apache + sqlite(PHP/5.6.40 + apache + sqlite)。这导致两个关键差异:
- web 根目录从
/usr/share/nginx/html变为/var/www/html。 - SQL 注入注释符不能用
#(sqlite),需用--。
题目 note 里给出的提示「apache是坠吼的(好麻烦,默认配置也是坠吼的)」正是暗示这一差异。
漏洞分析
SQL 注入(NoteController)
注册时 name 字段存在注入。管理员账号信息(来自 ModelFactory.php):
1 | name: '4uuu Nya' |
注册用户名 admin' or 1=1-- 登录后,note 页面显示 admin 的 note(提示 apache 环境)。注入 admin' union select 1,'ZZZ',3,4,5-- 确定查询为 5 列,回显位是第 2 列。
Blade 模板缓存过期
登录后台访问 /flag 显示 no flag。原因是 Blade 编译缓存未更新。FlagController::showFlag() 实际逻辑:
1 | $flag = file_get_contents('/th1s1s_F14g_2333333'); |
视图 auth.flag 对应 resources/views/auth/flag.blade.php,旧的编译缓存导致渲染结果不对。Blade 缓存文件名规则(CompilerEngine::getCompiledPath)为 sha1(视图完整路径).php:
1 | return $this->cachePath . '/' . sha1($path) . '.php'; |
计算 sha1('/var/www/html/resources/views/auth/flag.blade.php') = 73eb5933be1eb2293500f4a74b45284fd453f0bb,对应缓存文件 /var/www/html/storage/framework/views/73eb5933be1eb2293500f4a74b45284fd453f0bb.php。
注:原题 nginx 环境对应值为
34e41df0934a75437873264cd28e2d835bc38772(sha1('/usr/share/nginx/html/resources/views/auth/flag.blade.php'))。
PHP 反序列化 POP 链
Swift_ByteStream_TemporaryFileByteStream::__destruct 存在 unlink(),可删除任意文件:
1 | class Swift_ByteStream_TemporaryFileByteStream extends Swift_ByteStream_FileByteStream { |
利用过程
- 源码泄露 → 定位 SQL 注入点、phar 触发点与 flag 文件路径。
- SQL 注入登录:注册
admin' or 1=1--,note 页提示 apache 环境。 - 确定回显位:
admin' union select 1,'ZZZ',3,4,5--→ 第 2 列。 - 密码重置 token 注入:先
POST /password/email,email=admin@qvq.im(返回 500 但 token 已插入password_resets表);再注入admin' union select 1,(select token from password_resets limit 0,1),3,4,5--dump 出 token;访问/password/reset/{token}重置 admin 密码为AdminNew123。 - 登录后台:upload、files、flag、note 四个功能,
/flag显示no flag(Blade 缓存过期)。 - 计算缓存路径:按
sha1(视图完整路径)得到上文的.php缓存文件。 - 生成 phar:构造
Swift_ByteStream_TemporaryFileByteStream对象,metadata 指向缓存文件路径,stub 伪装GIF89a(完整生成脚本见「完整 EXP」)。生成时需关闭phar.readonly:php -d phar.readonly=0 gen_phar.php。 - 上传并触发反序列化:上传
1.gif(phar 伪装)→ 存储到storage/app/public/1.gif;check功能触发file_exists(path + filename),path参数可控,file_exists('phar:///var/www/html/storage/app/public/1.gif')触发反序列化,__destruct执行unlink删除 Blade 缓存。 - 重新访问 /flag:缓存删除后视图重新渲染,
file_get_contents读出 flag。
完整 EXP
生成 phar 的 PHP 脚本(metadata 为删缓存的对象,stub 伪装 GIF89a):
1 | class Swift_ByteStream_AbstractFilterableInputStream {} |
上传后触发反序列化的请求(path 参数指向 phar 伪协议):
1 | POST /check |
Flag
1 | flag{xxxx} |
(缓存删除后重新渲染 /flag 读出,真实值已脱敏。)
踩坑与总结
完整攻击链:
1 | 源码泄露 → SQL 注入(name 字段) |
关键点:
- buuoj 环境是 apache + sqlite,注意路径和注释符差异(
- -注释、/var/www/html根目录)。 - Blade 缓存文件名是
sha1(视图完整路径),视图名auth.flag带命名空间目录。 - phar 反序列化通过
file_exists(phar://...)触发,check()的path参数可控;PHP 析构中的unlink是删缓存的 sink。








