全部
常见问题
产品动态
精选推荐
功能建议

已处理 待处理 {{opt.name}}
已处理 待处理
分析中 已回复 待规划 {{opt.name}}
分析中 已回复 待规划
关于接口 :api:image_base64 会被注入文件的修复

管理 管理 编辑 删除

CRMEB-BZ:图片转 Base64 / Phar 反序列化 WebShell — 漏洞说明与修改明细

============================================================


路径均相对站点 crmeb/ 根目录(与修复包内路径一致)。


----------------------------------------


一、修改了哪些文件(总览)

----------------------------------------


序号  |  修改文件  |  改动类型  |  修改位置(方法 / 函数)  |  改了什么

1  |  app/api/controller/v1/PublicController.php  |  重写方法  |  get_image_base64()  |  白名单校验、去掉错误 phar 判断、去掉 put_image 回退

2  |  app/common.php  |  新增函数  |  normalize_image_host()  |  规范化域名 host

3  |  app/common.php  |  新增函数  |  image_url_is_allowed()  |  校验 URL 协议与白名单

4  |  app/common.php  |  新增函数  |  image_ip_in_range()  |  CIDR 判断

5  |  app/common.php  |  新增函数  |  image_ip_is_public()  |  判断是否公网 IP

6  |  app/common.php  |  新增函数  |  resolve_public_image_host()  |  DNS 解析并拒绝内网 IP

7  |  app/common.php  |  新增函数  |  image_data_to_base64()  |  校验真实图片 MIME 后转 Data URI

8  |  app/common.php  |  重写函数  |  image_to_base64()  |  安全读取本站/远端图片

9  |  app/common.php  |  重写函数  |  put_image()  |  不再对 URL 直接 file_get_contents


修复包内仅含上述 2 个文件:


crmeb/app/api/controller/v1/PublicController.php

crmeb/app/common.php


----------------------------------------


二、漏洞简述

----------------------------------------


攻击者上传恶意 JPG 后,调用 POST /api/image_base64。旧代码在失败时走 put_image() → file_get_contents($url),可触发 Phar 反序列化并写出 x2.php 等后门。


----------------------------------------


三、按文件 / 按位置的详细修改

----------------------------------------


文件 1:app/api/controller/v1/PublicController.php


位置:get_image_base64() 方法


项目  |  说明

5 月约行号  |  约第 327 行起

修复后约行号  |  约第 354 行起

调用路由  |  POST /api/image_base64(app/api/route/v1.php)


修改点 A — 域名白名单构造(方法前半段)


* 改前:合并对象存储域名 + $request->host(),用 str_replace 去 http(s)://;再拼 mp.weixin.qq.com。

* 改后:合并对象存储域名 + sys_config('site_url') + mp.weixin.qq.com,用 normalize_image_host() 得到纯 host 列表。

* 原因:避免 Host 头干扰;统一 host 比较。


修改点 B — URL 合法性判断(白名单校验处)


* 改前:只比较 parse_url(...)['host'] 是否在数组中;另有一段错误逻辑:


// 仅当「扩展名不像图片」并且「URL 含 phar://」时才拒绝

if ($imageUrl !== '' && !preg_match('/.*(\.png|\.jpg|\.jpeg|\.gif)$/', $imageUrl)

   && strpos(strtolower($imageUrl), "phar://") !== false) { ... }


* 改后:删除上述 phar 判断;改为:


if (($imageUrl !== '' && !image_url_is_allowed($imageUrl, $domainArr))

   || ($codeUrl !== '' && !image_url_is_allowed($codeUrl, $domainArr))) {

   return app('json')->success(['code' => false, 'image' => false]);

}


* 原因:旧判断对 .jpg 结尾的恶意 URL 无效;新逻辑只允许 http/https + 白名单 host。


修改点 C — 取图与缓存(try 代码块内)


* 改前:CacheService::remember($url, ...);先 image_to_base64,失败再 put_image($url),再读本地转 base64 并 unlink。

* 改后:缓存键改为 'safe_image_base64:' . hash('sha256', $url);只调用 image_to_base64($url, 9, $domainArr);不再调用 put_image。

* 原因:切断「失败后对任意 URL 做 file_get_contents」的利用路径。


修改点 D — 异常捕获


* 改前:catch (\Exception $e)

* 改后:catch (\Throwable $e),仍返回「操作失败」。


----------------------------------------


文件 2:app/common.php


位置 A(新增):约第 547–719 行,插在 path_to_url() 与原 image_to_base64() 之间


新增函数  |  约行号  |  修改内容

normalize_image_host()  |  ~552  |  新增。从域名/URL 解析小写 host

image_url_is_allowed()  |  ~579  |  新增。限制 scheme 为 http/https,禁止 userinfo,host 必须在白名单

image_ip_in_range()  |  ~605  |  新增。CIDR 匹配

image_ip_is_public()  |  ~631  |  新增。拒绝私网/保留地址及常见绕过网段

resolve_public_image_host()  |  ~651  |  新增。解析 DNS,要求全部 IP 为公网

image_data_to_base64()  |  ~707  |  新增。getimagesizefromstring 校验 MIME 后生成 Data URI,限 10MB


位置 B(重写):image_to_base64()


项目  |  说明

5 月约行号  |  约第 552 行起

修复后约行号  |  约第 729 行起


具体改动:


1. 函数签名

  * 改前:image_to_base64($avatar = '', $timeout = 9)

  * 改后:增加第三参数 array $allowedHosts = []


2. 入口校验(函数开头)

  * 新增:非字符串 / 空串直接 false;若传入 $allowedHosts 则先 image_url_is_allowed。


3. URL 解析段

  * 改前:str_replace('https', 'http', $avatar) 强制降级。

  * 改后:必须有合法 scheme/host;scheme ∈ {http, https};禁止 userinfo;去掉强制降级。


4. 本站文件读取段(与 site_url 同 host 时)

  * 改前:file_get_contents(public_path() . substr($url['path'], 1)),再用正则删 <?php...?>。

  * 改后:realpath + 必须落在 public/ 目录内 + 大小限制 + image_data_to_base64 校验真实图片。


5. 远端 curl 段

  * 改前:CURLOPT_FOLLOWLOCATION = true,CURLOPT_SSL_VERIFYPEER = false,结果固定当 jpeg。

  * 改后:先 resolve_public_image_host;FOLLOWLOCATION = false;限制协议为 HTTP/HTTPS;校验证书;限 10MB;用 image_data_to_base64。


位置 C(重写):put_image()


项目  |  说明

5 月约行号  |  约第 602 行起

修复后约行号  |  约第 833 行起


具体改动:


改前(危险点)  |  改后

$imgData = file_get_contents($url);  |  改为 image_to_base64($url)

正则去掉 PHP 标签后直接落盘  |  仅接受合法 data:image/...;base64,...,按 MIME 选扩展名

文件名较多依赖 URL 扩展名  |  文件名过滤为安全字符,目录固定 uploads/qrcode/


原因:此处直接 file_get_contents($url) 是帖子攻击链高度吻合的触发点。


----------------------------------------


四、修改位置速查(给实施同学)

----------------------------------------


打开修复后源码,按下列符号核对是否已打上补丁:


PublicController.php

 └─ get_image_base64()

      ├─ normalize_image_host / image_url_is_allowed   ← 应存在

      ├─ 旧的 preg_match + phar:// 判断               ← 应已删除

      └─ put_image( 回退调用                            ← 应已删除


common.php

 ├─ normalize_image_host / image_url_is_allowed / ... ← 应新增

 ├─ image_to_base64(..., array $allowedHosts = [])  ← 应有第三参数

 └─ put_image()

      └─ file_get_contents($url)                      ← 应已不存在


----------------------------------------


五、如何覆盖

----------------------------------------


1. 备份原文件。

2. 解压 zip,将包内两个文件按路径覆盖到站点 crmeb/。

3. 清缓存 / 重启 PHP-FPM。

4. 建议禁止 uploads 目录执行 PHP;排查 x2.php、.sys_cache.php。


无数据库脚本。


----------------------------------------


下载附件传到到服务器覆盖app文件

附件

被注入文件的修复.zip

{{voteData.voteSum}} 人已参与
支持
反对
请登录后查看

吴小汐 最后编辑于2026-11-02 14:44:15

快捷回复
{{replySubmitting ? '提交中...' : '回复'}}
{{replySubmitting ? '提交中...' : '回复'}}
回复({{post_count}}) {{!is_user ? '我的回复' :'全部回复'}}
排序 默认正序 回复倒序 点赞倒序

{{item.user_info.nickname ? item.user_info.nickname : item.user_name}} LV.{{ item.user_info.bbs_level || item.bbs_level }}

作者 管理员 企业

{{item.floor}}# 同步到gitee 已同步到gitee {{item.is_suggest == 1? '取消推荐': '推荐'}}
{{item.is_suggest == 1? '取消推荐': '推荐'}} 【已收集】
{{item.floor}}# 沙发 板凳 地板 {{item.floor}}# 【已收集】
{{item.user_info.title || '暂无简介'}}
附件

{{itemf.name}}

{{item.created_at}}  {{item.ip_address}}
打赏
已打赏¥{{item.reward_price}}
{{item.like_count}}
分享
{{item.showReply ? '取消回复' : '回复'}}
删除
{{replySubmitting ? '提交中...' : '回复'}}
{{replySubmitting ? '提交中...' : '回复'}}

{{itemc.user_info.nickname}}

{{itemc.user_name}}

回复 {{itemc.comment_user_info.nickname}}

附件

{{itemf.name}}

{{itemc.created_at}}
打赏
已打赏¥{{itemc.reward_price}}
{{itemc.like_count}}
{{itemc.showReply ? '取消回复' : '回复'}}
删除
{{replySubmitting ? '提交中...' : '回复'}}
{{replySubmitting ? '提交中...' : '回复'}}
收起 展开更多
查看更多
打赏
已打赏¥{{reward_price}}
133
{{like_count}}
{{collect_count}}
添加回复 ({{post_count}})

相关推荐

{{replySubmitting ? '提交中...' : '回复'}}
{{replySubmitting ? '提交中...' : '回复'}}
问题:
问题自动获取的帖子内容,不准确时需要手动修改. [获取答案]
答案:
提交
bug 需求 取 消 确 定
打赏金额
当前余额:¥{{rewardUserInfo.reward_price}}
{{item.price}}元
请输入 0.1-{{reward_max_price}} 范围内的数值
打赏成功
¥{{price}}
完成 确认打赏

微信登录/注册

{{ wechatLoginError }}
切换手机号登录

{{ bind_phone ? '绑定手机' : '手机登录'}}

{{codeText}}
切换微信登录/注册
暂不绑定
CRMEB客服
CRMEB咨询热线 400-8888-794

扫码领取产品资料

功能清单
思维导图
安装教程
CRMEB开源商城下载 源码下载 CRMEB帮助文档 帮助文档
返回顶部 返回顶部
CRMEB客服