-
-
[原创]FortiWeb CVE-2025-64446漏洞分析
-
发表于: 2025-11-25 16:25 1927
-
环境:Based on 7.0.11 & 7.0.12
Root Shell
然后使用这个账号登录即可:
或者:
httpsd
涉及到漏洞的配置文件:
这个模块是httpd的静态模块,也就是编译到二进制文件中了:
请求路径 /api/v2.0/../../cgi-bin/fwbcgi 包含路径遍历序列 ../..,试图从/api/v2.0/向上跳转两级目录,到达/cgi-bin/fwbcgi,Apache 会在处理请求前对路径进行规范化处理,将 /api/v2.0/../../cgi-bin/fwbcgi 规范化为/cgi-bin/fwbcgi。根据配置文件:
规范化后的路径 /cgi-bin/fwbcgi 会匹配 ScriptAlias /cgi-bin/ 规则,该路径会被映射到物理路径 /migadmin/cgi-bin/fwbcgi,该目录启用了 CGI 执行(Options +ExecCGI 和 SetHandler cgi-script)如果 fwbcgi 文件存在且可执行,请求会成功执行:


使用strace观察,两次请求其实都访问到了fwbcgi,所以根本没有经过fwbcgi-handler:
注意在调试http的这种模式的fork + execve的cgi调用时,设置gdb:
在调用fwbcgi时环境变量为:
正常情况下创建一个用户:
再次审查/api/v2.0/回调函数sub_630990:
在将请求转发到/cgi-bin/fwbcgi之前会从cookie中获取操作用户的关键凭证即username、profname、vdom、loginname,转为json并base64编码作为HTTP_CGIINFO头部成员。
对ap_internal_redirect下断点:
结合fwbcgi鉴权逻辑在cgi_auth中其实只是提取HTTP_CGIINFO进行校验而没有涉及cookie字段,然后直接调用cgi_process后端具体实现:
所以由于配置问题导致关键后端CGI暴露,user可以未授权访问fwbcgi并且伪造CGIINFO绕过鉴权,可以实现相当于授权状态下对各种/api/v2.0/开头的API功能访问,其中包括/api/v2.0/cmdb/system/admin创建用户。

sudo file -s /dev/sdb*[sudo] password for user: /dev/sdb: DOS/MBR boot sector; partition 1 : ID=0x83, active, start-CHS (0x0,0,0), end-CHS (0x0,0,0), startsector 12194, 800000 sectors; partition 2 : ID=0x83, start-CHS (0x0,0,0), end-CHS (0x0,0,0), startsector 812194, 800000 sectors; partition 3 : ID=0x83, start-CHS (0x0,0,0), end-CHS (0x0,0,0), startsector 1612194, 200000 sectors; partition 4 : ID=0x83, start-CHS (0x0,0,0), end-CHS (0x0,0,0), startsector 1812194, 65296670 sectors/dev/sdb1: DOS/MBR boot sector/dev/sdb2: data/dev/sdb3: Linux rev 1.0 ext3 filesystem data, UUID=8c4d311d-58e4-475a-bdde-b32da9cf668c (large files)/dev/sdb4: Linux rev 1.0 ext4 filesystem data, UUID=e2db5aaa-d698-4873-8a98-bbb6e3c5b961, volume name "FWB_LOGDISK" (extents) (64bit) (large files) (huge files)sudo mount -o ro,loop /dev/sdb1 /mnt➜ ls /mntbackup_config_file config extlinux.conf ldlinux.c32 lib rootfs.gz var vmlinuz.chkbin etc krootfs.gz ldlinux.sys lost+found rootfs.gz.chk vmlinuz vmlinuz.kdumpsudo mount -o ro,loop /dev/sdb4 /mnt ➜ ls /mntapache_logs dbg_cli.log gui_upload maxminddb proxyd_statistic.db upd_dirapi debug hasync messages redis upd-logbotd dlog_indexd hcdb.dat ml-backup Reports_adom url_record.dbbot_management dlog_logd irdb_sig.db ml-backup-api scanner_integration virclustering.log dmesg known_engines_adom ml-backup-bot shell_accessd_log virextcmdb fds-fpm lib mysql shibboleth wadcmdb.log filebeat libav.so mysqld_monitor.sh sig-func wassd_debug_logcmdb_refcnt filebeat_registry log_global_manage.db nic_affinity_log statistic_adom wvscmdb_refcnt.log fwlog logo openapi tmprpts_adomdbg_acmed.log garbage_access.log lost+found outgoing update_history.dbxz -d -k -f -c ./rootfs.gz > rootfs.binsudo mount -o ro,loop rootfs.bin /mntls /mnt bin data dev etc home lib lib64 migadmin mnt modules proc script share sys tmp usr var VERSIONcat /mnt/VERSION 7.0.11-B0181sudo file -s /dev/sdb*[sudo] password for user: /dev/sdb: DOS/MBR boot sector; partition 1 : ID=0x83, active, start-CHS (0x0,0,0), end-CHS (0x0,0,0), startsector 12194, 800000 sectors; partition 2 : ID=0x83, start-CHS (0x0,0,0), end-CHS (0x0,0,0), startsector 812194, 800000 sectors; partition 3 : ID=0x83, start-CHS (0x0,0,0), end-CHS (0x0,0,0), startsector 1612194, 200000 sectors; partition 4 : ID=0x83, start-CHS (0x0,0,0), end-CHS (0x0,0,0), startsector 1812194, 65296670 sectors/dev/sdb1: DOS/MBR boot sector/dev/sdb2: data/dev/sdb3: Linux rev 1.0 ext3 filesystem data, UUID=8c4d311d-58e4-475a-bdde-b32da9cf668c (large files)/dev/sdb4: Linux rev 1.0 ext4 filesystem data, UUID=e2db5aaa-d698-4873-8a98-bbb6e3c5b961, volume name "FWB_LOGDISK" (extents) (64bit) (large files) (huge files)sudo mount -o ro,loop /dev/sdb1 /mnt➜ ls /mntbackup_config_file config extlinux.conf ldlinux.c32 lib rootfs.gz var vmlinuz.chkbin etc krootfs.gz ldlinux.sys lost+found rootfs.gz.chk vmlinuz vmlinuz.kdumpsudo mount -o ro,loop /dev/sdb4 /mnt ➜ ls /mntapache_logs dbg_cli.log gui_upload maxminddb proxyd_statistic.db upd_dirapi debug hasync messages redis upd-logbotd dlog_indexd hcdb.dat ml-backup Reports_adom url_record.dbbot_management dlog_logd irdb_sig.db ml-backup-api scanner_integration virclustering.log dmesg known_engines_adom ml-backup-bot shell_accessd_log virextcmdb fds-fpm lib mysql shibboleth wadcmdb.log filebeat libav.so mysqld_monitor.sh sig-func wassd_debug_logcmdb_refcnt filebeat_registry log_global_manage.db nic_affinity_log statistic_adom wvscmdb_refcnt.log fwlog logo openapi tmprpts_adomdbg_acmed.log garbage_access.log lost+found outgoing update_history.dbxz -d -k -f -c ./rootfs.gz > rootfs.binsudo mount -o ro,loop rootfs.bin /mntls /mnt bin data dev etc home lib lib64 migadmin mnt modules proc script share sys tmp usr var VERSIONcat /mnt/VERSION 7.0.11-B0181config system global set shell-access enable set shell-username testadmin set shell-password passforadmin set shell-timeout 1200 --> The shell-access will be disabled in 1200 minutes. set shell-history-size 1024 --> Record 1024 operations. set shell-trusthostv4 0.0.0.0/0 --> Source ip (ipv4) should in the trust-host address. set shell-trusthostv6 ::/0 --> Source ip (ipv6) should in the trust-host address.endconfig system global set shell-access enable set shell-username testadmin set shell-password passforadmin set shell-timeout 1200 --> The shell-access will be disabled in 1200 minutes. set shell-history-size 1024 --> Record 1024 operations. set shell-trusthostv4 0.0.0.0/0 --> Source ip (ipv4) should in the trust-host address. set shell-trusthostv6 ::/0 --> Source ip (ipv6) should in the trust-host address.endssh debug@192.168.139.135The authenticity of host '192.168.139.135 (192.168.139.135)' can't be established.ED25519 key fingerprint is SHA256:sOFOLiOCYxDuyr2nRQz/wtps/Q/p/tb2q11TNBJJHqM.This key is not known by any other names.Are you sure you want to continue connecting (yes/no/[fingerprint])? yesWarning: Permanently added '192.168.139.135' (ED25519) to the list of known hosts.debug@192.168.139.135's password:-- WARNING! All configurations should be done through CLI shell.-- You now have full access./# lsVERSION data etc lib migadmin modules script sys usrbin dev home lib64 mnt proc share tmp var/#ssh debug@192.168.139.135The authenticity of host '192.168.139.135 (192.168.139.135)' can't be established.ED25519 key fingerprint is SHA256:sOFOLiOCYxDuyr2nRQz/wtps/Q/p/tb2q11TNBJJHqM.This key is not known by any other names.Are you sure you want to continue connecting (yes/no/[fingerprint])? yesWarning: Permanently added '192.168.139.135' (ED25519) to the list of known hosts.debug@192.168.139.135's password:-- WARNING! All configurations should be done through CLI shell.-- You now have full access./# lsVERSION data etc lib migadmin modules script sys usrbin dev home lib64 mnt proc share tmp var/#nmap -Pn -T4 -p- 192.168.139.135 Starting Nmap 7.80 ( https://nmap.org ) at 2025-11-20 16:31 HKTNmap scan report for 192.168.139.135Host is up (0.0011s latency).Not shown: 65528 closed portsPORT STATE SERVICE8/tcp filtered unknown9/tcp filtered discard22/tcp open ssh43/tcp filtered whois80/tcp open http443/tcp open https995/tcp open pop3snmap -Pn -T4 -p- 192.168.139.135 Starting Nmap 7.80 ( https://nmap.org ) at 2025-11-20 16:31 HKTNmap scan report for 192.168.139.135Host is up (0.0011s latency).Not shown: 65528 closed portsPORT STATE SERVICE8/tcp filtered unknown9/tcp filtered discard22/tcp open ssh43/tcp filtered whois80/tcp open http443/tcp open https995/tcp open pop3s/# /bin/httpsd -VServer version: xxxxxxx/2.4.53 (Unix)Server built: Mar 26 2025 18:38:56Server's Module Magic Number: 20120211:124Server loaded: APR 1.5.1, APR-UTIL 1.5.3, PCRE 8.43 2019-02-23Compiled using: APR 1.4.6, APR-UTIL 1.4.1, PCRE 8.43 2019-02-23Architecture: 64-bitServer MPM: prefork threaded: no forked: yes (variable process count)Server compiled with.... -D BIG_SECURITY_HOLE -D APR_HAS_SENDFILE -D APR_HAS_MMAP -D APR_HAVE_IPV6 (IPv4-mapped addresses enabled) -D APR_USE_SYSVSEM_SERIALIZE -D APR_USE_PTHREAD_SERIALIZE -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT -D APR_HAS_OTHER_CHILD -D AP_HAVE_RELIABLE_PIPED_LOGS -D DYNAMIC_MODULE_LIMIT=256 -D HTTPD_ROOT="/migadmin" -D SUEXEC_BIN="/migadmin/bin/suexec" -D DEFAULT_PIDLOG="/var/run/httpsd.pid" -D DEFAULT_SCOREBOARD="/var/run/apache_runtime_status" -D DEFAULT_ERRORLOG="logs/error_log" -D AP_TYPES_CONFIG_FILE="conf/mime.types" -D SERVER_CONFIG_FILE="conf/httpd.conf"/# /bin/httpsd -VServer version: xxxxxxx/2.4.53 (Unix)Server built: Mar 26 2025 18:38:56Server's Module Magic Number: 20120211:124Server loaded: APR 1.5.1, APR-UTIL 1.5.3, PCRE 8.43 2019-02-23Compiled using: APR 1.4.6, APR-UTIL 1.4.1, PCRE 8.43 2019-02-23Architecture: 64-bitServer MPM: prefork threaded: no forked: yes (variable process count)Server compiled with.... -D BIG_SECURITY_HOLE -D APR_HAS_SENDFILE -D APR_HAS_MMAP -D APR_HAVE_IPV6 (IPv4-mapped addresses enabled) -D APR_USE_SYSVSEM_SERIALIZE -D APR_USE_PTHREAD_SERIALIZE -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT -D APR_HAS_OTHER_CHILD -D AP_HAVE_RELIABLE_PIPED_LOGS -D DYNAMIC_MODULE_LIMIT=256 -D HTTPD_ROOT="/migadmin" -D SUEXEC_BIN="/migadmin/bin/suexec" -D DEFAULT_PIDLOG="/var/run/httpsd.pid" -D DEFAULT_SCOREBOARD="/var/run/apache_runtime_status" -D DEFAULT_ERRORLOG="logs/error_log" -D AP_TYPES_CONFIG_FILE="conf/mime.types" -D SERVER_CONFIG_FILE="conf/httpd.conf"<Location /api/v2.0/> SetHandler fwbcgi-handler</Location><Location /api/v2.0/> SetHandler fwbcgi-handler</Location><IfModule alias_module> ScriptAlias /cgi-bin/ "/migadmin/cgi-bin/"</IfModule><Directory "/migadmin/cgi-bin"> Options +ExecCGI SetHandler cgi-script</Directory><IfModule alias_module> ScriptAlias /cgi-bin/ "/migadmin/cgi-bin/"</IfModule><Directory "/migadmin/cgi-bin"> Options +ExecCGI SetHandler cgi-script</Directory>/tmp # strace -ff -e trace=fork,execve -p 26730strace: Process 26730 attachedstrace: Process 26772 attached[pid 26772] execve("/migadmin/cgi-bin/fwbcgi", ["/cgi-bin/fwbcgi", "/migadmin/cgi-bin/fwbcgi"], 0xe02e08 /* 38 vars */) = 0[pid 26772] +++ exited with 0 +++strace: Process 28042 attached[pid 28042] execve("/migadmin/cgi-bin/fwbcgi", ["/cgi-bin/fwbcgi", "/migadmin/cgi-bin/fwbcgi"], 0xe02e08 /* 38 vars */) = 0[pid 28042] +++ exited with 0 +++/tmp # strace -ff -e trace=fork,execve -p 26730strace: Process 26730 attachedstrace: Process 26772 attached[pid 26772] execve("/migadmin/cgi-bin/fwbcgi", ["/cgi-bin/fwbcgi", "/migadmin/cgi-bin/fwbcgi"], 0xe02e08 /* 38 vars */) = 0[pid 26772] +++ exited with 0 +++strace: Process 28042 attached[pid 28042] execve("/migadmin/cgi-bin/fwbcgi", ["/cgi-bin/fwbcgi", "/migadmin/cgi-bin/fwbcgi"], 0xe02e08 /* 38 vars */) = 0[pid 28042] +++ exited with 0 +++───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────00:0000│ rsp 0x7ffe239fa9a0 ◂— 201:0008│ 0x7ffe239fa9a8 —▸ 0x7ffe239fcb3a ◂— '/cgi-bin/fwbcgi'02:0010│ 0x7ffe239fa9b0 —▸ 0x7ffe239fcb4a ◂— '/migadmin/cgi-bin/fwbcgi'03:0018│ 0x7ffe239fa9b8 ◂— 004:0020│ 0x7ffe239fa9c0 —▸ 0x7ffe239fcb63 ◂— 'no_gzip=1'05:0028│ 0x7ffe239fa9c8 —▸ 0x7ffe239fcb6d ◂— 'HTTPS=on'06:0030│ 0x7ffe239fa9d0 —▸ 0x7ffe239fcb76 ◂— 'HTTP_HOST=192.168.139.147'07:0038│ 0x7ffe239fa9d8 —▸ 0x7ffe239fcb90 ◂— 'HTTP_SEC_CH_UA_PLATFORM="Windows"'─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── ► 0 0x7fc42a5a8090 None 1 0x2 None 2 0x7ffe239fcb3a None 3 0x7ffe239fcb4a None 4 0x0 None────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────pwndbg> stack00:0000│ rsp 0x7ffe239fa9a0 ◂— 201:0008│ 0x7ffe239fa9a8 —▸ 0x7ffe239fcb3a ◂— '/cgi-bin/fwbcgi'02:0010│ 0x7ffe239fa9b0 —▸ 0x7ffe239fcb4a ◂— '/migadmin/cgi-bin/fwbcgi'03:0018│ 0x7ffe239fa9b8 ◂— 004:0020│ 0x7ffe239fa9c0 —▸ 0x7ffe239fcb63 ◂— 'no_gzip=1'05:0028│ 0x7ffe239fa9c8 —▸ 0x7ffe239fcb6d ◂— 'HTTPS=on'06:0030│ 0x7ffe239fa9d0 —▸ 0x7ffe239fcb76 ◂— 'HTTP_HOST=192.168.139.147'07:0038│ 0x7ffe239fa9d8 —▸ 0x7ffe239fcb90 ◂— 'HTTP_SEC_CH_UA_PLATFORM="Windows"'pwndbg> 08:0040│ 0x7ffe239fa9e0 —▸ 0x7ffe239fcbb2 ◂— 'HTTP_ACCEPT_LANGUAGE=zh-CN,zh;q=0.9'09:0048│ 0x7ffe239fa9e8 —▸ 0x7ffe239fcbd6 ◂— 'HTTP_ACCEPT=application/json, text/plain, */*'0a:0050│ 0x7ffe239fa9f0 —▸ 0x7ffe239fcc04 ◂— 'HTTP_SEC_CH_UA="Not_A Brand";v="99", "Chromium";v="142"'0b:0058│ 0x7ffe239fa9f8 —▸ 0x7ffe239fcc3c ◂— 'HTTP_USER_AGENT=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36'0c:0060│ 0x7ffe239faa00 —▸ 0x7ffe239fccbc ◂— 'HTTP_SEC_CH_UA_MOBILE=?0'0d:0068│ 0x7ffe239faa08 —▸ 0x7ffe239fccd5 ◂— 'HTTP_SEC_FETCH_SITE=same-origin'0e:0070│ 0x7ffe239faa10 —▸ 0x7ffe239fccf5 ◂— 'HTTP_SEC_FETCH_MODE=cors'0f:0078│ 0x7ffe239faa18 —▸ 0x7ffe239fcd0e ◂— 'HTTP_SEC_FETCH_DEST=empty'pwndbg> 10:0080│ 0x7ffe239faa20 —▸ 0x7ffe239fcd28 ◂— 'HTTP_REFERER=4a6K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1j5I4z5e0u0Q4x3X3f1I4y4U0S2Q4x3X3f1I4x3K6W2Q4x3X3f1I4y4o6N6Q4x3V1k6J5L8$3!0@1i4K6u0r3M7%4W2K6N6r3g2E0i4K6u0r3k6r3q4K6K9r3u0G2j5i4u0V1i4K6u0r3x3g2)9J5y4H3`.`.11:0088│ 0x7ffe239faa28 —▸ 0x7ffe239fcd65 ◂— 'HTTP_ACCEPT_ENCODING=gzip, deflate, br'12:0090│ 0x7ffe239faa30 —▸ 0x7ffe239fcd8c ◂— 'HTTP_PRIORITY=u=1, i'13:0098│ 0x7ffe239faa38 —▸ 0x7ffe239fcda1 ◂— 'HTTP_CONNECTION=keep-alive'14:00a0│ 0x7ffe239faa40 —▸ 0x7ffe239fcdbc ◂— 'PATH=/bin:/data/bin'15:00a8│ 0x7ffe239faa48 —▸ 0x7ffe239fcdd0 ◂— 'PYTHONHOME=/'16:00b0│ 0x7ffe239faa50 —▸ 0x7ffe239fcddd ◂— 'SERVER_SIGNATURE='17:00b8│ 0x7ffe239faa58 —▸ 0x7ffe239fcdef ◂— 'SERVER_SOFTWARE=xxxxxxx'pwndbg> 18:00c0│ 0x7ffe239faa60 —▸ 0x7ffe239fce07 ◂— 'SERVER_NAME=192.168.139.147'19:00c8│ 0x7ffe239faa68 —▸ 0x7ffe239fce23 ◂— 'SERVER_ADDR=192.168.139.147'1a:00d0│ 0x7ffe239faa70 —▸ 0x7ffe239fce3f ◂— 'SERVER_PORT=43'1b:00d8│ 0x7ffe239faa78 —▸ 0x7ffe239fce4e ◂— 'REMOTE_ADDR=192.168.139.1'1c:00e0│ 0x7ffe239faa80 —▸ 0x7ffe239fce68 ◂— 'DOCUMENT_ROOT=/migadmin/new_gui'1d:00e8│ 0x7ffe239faa88 —▸ 0x7ffe239fce88 ◂— 'REQUEST_SCHEME=https'1e:00f0│ 0x7ffe239faa90 —▸ 0x7ffe239fce9d ◂— 'CONTEXT_PREFIX=/cgi-bin/'1f:00f8│ 0x7ffe239faa98 —▸ 0x7ffe239fceb6 ◂— 'CONTEXT_DOCUMENT_ROOT=/migadmin/cgi-bin/'pwndbg> 20:0100│ 0x7ffe239faaa0 —▸ 0x7ffe239fcedf ◂— 'SERVER_ADMIN=support@fortinet.com'21:0108│ 0x7ffe239faaa8 —▸ 0x7ffe239fcf01 ◂— 'SCRIPT_FILENAME=/migadmin/cgi-bin/fwbcgi'22:0110│ 0x7ffe239faab0 —▸ 0x7ffe239fcf2a ◂— 'REMOTE_PORT=22761'23:0118│ 0x7ffe239faab8 —▸ 0x7ffe239fcf3c ◂— 'GATEWAY_INTERFACE=CGI/1.1'24:0120│ 0x7ffe239faac0 —▸ 0x7ffe239fcf56 ◂— 'SERVER_PROTOCOL=HTTP/1.1'25:0128│ 0x7ffe239faac8 —▸ 0x7ffe239fcf6f ◂— 'REQUEST_METHOD=GET'26:0130│ 0x7ffe239faad0 —▸ 0x7ffe239fcf82 ◂— 'QUERY_STRING='27:0138│ 0x7ffe239faad8 —▸ 0x7ffe239fcf90 ◂— 'REQUEST_URI=/api/v2.0/cmdb/../../../cgi-bin/fwbcgi'pwndbg> 28:0140│ 0x7ffe239faae0 —▸ 0x7ffe239fcfc3 ◂— 'SCRIPT_NAME=/cgi-bin/fwbcgi'───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────00:0000│ rsp 0x7ffe239fa9a0 ◂— 201:0008│ 0x7ffe239fa9a8 —▸ 0x7ffe239fcb3a ◂— '/cgi-bin/fwbcgi'02:0010│ 0x7ffe239fa9b0 —▸ 0x7ffe239fcb4a ◂— '/migadmin/cgi-bin/fwbcgi'03:0018│ 0x7ffe239fa9b8 ◂— 004:0020│ 0x7ffe239fa9c0 —▸ 0x7ffe239fcb63 ◂— 'no_gzip=1'05:0028│ 0x7ffe239fa9c8 —▸ 0x7ffe239fcb6d ◂— 'HTTPS=on'06:0030│ 0x7ffe239fa9d0 —▸ 0x7ffe239fcb76 ◂— 'HTTP_HOST=192.168.139.147'07:0038│ 0x7ffe239fa9d8 —▸ 0x7ffe239fcb90 ◂— 'HTTP_SEC_CH_UA_PLATFORM="Windows"'