ActivID 管理员账户接管:幕后故事
2025年9月,我们受客户委托对特定产品进行安全评估:HID公司的ActivID Appliance。据供应商称,该产品在全球范围内用于保护关键基础设施和数据的访问,支持多种身份验证方法,包括推送验证、OTP、PKI凭证和静态凭证。本文将详细介绍我们发现HID-PSA-2025-002漏洞的方法论,这是一个存在于SOAP API中的身份验证绕过漏洞,可导致获取应用程序的管理员访问权限。
目录
前言
本文有意提供详细内容,以展示与白盒代码分析相关的尽可能多的方法论方面。如果您只对解释漏洞的部分感兴趣,请参考HID-PSA-2025-002安全公告或直接跳转到调查可疑行为部分。
产品识别
我们的首要目标是识别产品的确切版本,因为HID下载页面上提供了多个版本。我们客户提供的信息只有应用程序的URL。从那里,我们注意到的唯一版本标识符是客户实例HTML页脚许可协议中的以下标识符:v080620。该标识符仅能在其网站上可下载的PDF格式的EULA 8.5版本中找到。因此,按照安装指南,我们获得了该设备8.5版本的适当VMware虚拟化实例,从而获得了对机器的完全root访问权限。
攻击面发现
这部分起初可能看起来很枯燥,但正确理解暴露的路由和服务是进行适当白盒审查的关键步骤。在本案例中,我们认为对每个服务的细致分析极大地促进了漏洞的发现。
我们的研究聚焦于远程攻击面,因此首先进行经典的nmap扫描:
1
2
3
4
5
6
|
$ nmap -p- -sV hid --open
PORT STATE SERVICE VERSION
40/tcp open ssh OpenSSH 7.4 (protocol 2.0)
443/tcp open ssl/http nginx
1005/tcp open ssl/http nginx
8443/tcp open ssl/http nginx
|
登录VM后,我们开始枚举监听服务:
1
2
3
4
5
|
$ ss -ntalp
State Local Address:Port Process
LISTEN 0.0.0.0:3000 users:(("java",pid=3038,fd=58))
LISTEN 127.0.0.1:7800 users:(("java",pid=3038,fd=55))
[...]
|
- 监听端口8443、8445、1005和1006的Java进程
- nmap中在端口443上可见的nginx反向代理
可以注意到,使用nmap时这些Java进程并未显示为开放状态,这可以通过列出防火墙规则来解释:
1
2
3
4
5
6
7
|
$ iptables -nvL
[...]
Chain IN_public_allow (1 references)
pkts bytes target prot opt in out source destination
15719 943K ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:443 ctstate NEW,UNTRACKED
17 1020 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:1005 ctstate NEW,UNTRACKED
[...]
|
对于TCP,仅允许访问端口40、443(https)、1005和1006。
由于端口443在互联网上开放,我们开始理解nginx代理的配置。
调查nginx
Nginx审查从读取/etc/nginx.conf文件开始:
1
2
3
4
5
6
|
user nginx;
worker_processes 2;
http {
include /etc/nginx/conf.d/*.conf;
}
|
在我们的案例中,这个主配置文件只包含/etc/nginx/conf.d/文件夹内的其他配置文件:
1
2
3
|
$ ls -l /etc/nginx/conf.d/*.conf
-rwx------ 1 root root 2005 Jun 20 2017 /etc/nginx/conf.d/default.conf
-rw-r--r-- 1 root root 652 Dec 18 2020 /etc/nginx/conf.d/web_activid.conf
|
这里有趣的文件是web_activid.conf:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
upstream weblogic8445 {
keepalive 30;
server 127.0.0.1:8445;
}
upstream weblogic8443 {
keepalive 30;
server 127.0.0.1:8443;
}
upstream webmin {
server 127.0.0.1:1007;
}
include /etc/nginx/include/TLS1.conf;
include /etc/nginx/include/TLS2.conf;
[...]
|
定义了两个上游字段:weblogic8443和weblogic8445,分别指向本地主机8445和8443端口的HTTP服务。然后包含了一些其他配置文件,根据其命名,这些文件可能定义了TLS服务和相互TLS服务。
/etc/nginx/include/TLS1.conf定义如下:
1
2
3
4
5
6
7
8
9
|
server {
listen 443 ssl ;
listen [::]:443 ssl ;
ssl_verify_client off;
# ...
set $upstream weblogic8445;
# ...
include /etc/nginx/include/app/TLS1/*.conf;
}
|
回到TLS1配置,这里定义了一个监听443端口的服务器,将变量$upstream设置为weblogic8445,并包含位于/etc/nginx/include/app/TLS1中的一些服务器配置:
1
2
3
4
5
|
$ ls -l /etc/nginx/include/app/TLS1
total 0
lrwxrwxrwx 1 root root 48 Sep 2 17:11 AuthenticationPortal.conf -> /etc/nginx/include/app/AuthenticationPortal.conf
[...]
lrwxrwxrwx 1 root root 35 Sep 2 17:11 SoapAPI.conf -> /etc/nginx/include/app/SoapAPI.conf
|
它们都是指向/etc/nginx/include/app/中对应文件的符号链接。例如,AuthenticationPortal.conf文件:
1
2
3
4
5
6
7
8
9
|
location /idp/ {
proxy_pass https://$upstream;
}
# OpenID
location ~ /idp/[^/]+/authn/ {
proxy_pass https://$upstream;
include /etc/nginx/include/openiderrorhandling.conf;
}
|
类似地,SoapAPI.conf文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
location ^~ /4TRESS/ {
proxy_pass https://$upstream;
limit_except POST {
deny all;
}
if ($request_uri ~* "(?:wsdl|xsd)$") {
return 404;
}
include /etc/nginx/include/soaperrorhandling.conf;
}
location ^~ /ac-iasp-backend-jaxws/ {
proxy_pass https://$upstream;
limit_except POST {
deny all;
}
if ($request_uri ~* "(?:wsdl|xsd)$") {
return 404;
}
include /etc/nginx/include/soaperrorhandling.conf;
}
|
可以观察到这些配置文件遵循相同的逻辑:使用location指令(例如/idp/),将HTTP请求从nginx重路由到由nginx变量$upstream定义的服务器。幸运的是,该变量先前已定义,在本例中为weblogic8445,它指向在本地主机8445端口上托管的HTTP服务器!
提取所有路径和目标后,可以观察到以下路由:
1
2
3
|
nginx:443 -> /idp/* -> weblogic8445:8445 -> idp WAR
nginx:443 -> /ac-iasp-backend-jaxws/* -> weblogic8445:8445 -> JAXWS services
[...]
|
调查Java服务
列出进程时,端口8445确实被列为Java进程。完整的Java进程信息及其参数检索如下:
1
2
3
4
5
6
7
|
$ lsof -i :8445
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
java 3301 ftuser 1005u IPv4 50538 0t0 TCP hid:copy (LISTEN)
$ ps -fwwp 3301
UID PID PPID C STIME TTY TIME CMD
ftuser 3301 3247 0 14:19 ? 00:02:29 /usr/java/default/bin/java -server -Xms2048m -Xmx2048m -Dweblogic.Name=ActivIDServer [...]
|
简而言之,这个Java命令:
- 通过
weblogic.Server类启动Weblogic web服务器的实例
- 设置其一些属性和功能(stdin、stdout、内存使用、密码套件等)
- 配置位于
/opt/hid/weblogic/config/activid_domain的域
- 配置一些特定于应用程序的变量,例如
activid.enable.monitoring.servlet
对于Weblogic,域不过是一个包含实例化一个或多个Java应用程序所需的所有资源的文件夹。在这样的文件夹中,根据文档,config文件夹内应该有一个config.xml文件:
1
2
|
$ ls -lah /opt/hid/weblogic/config/activid_domain/config/config.xml
-rw-rw---- 1 ftadmin ftgroup 12K Sep 2 17:15 /opt/hid/weblogic/config/activid_domain/config/config.xml
|
这样的XML文件包含域内启动的每个Java应用程序的列表,以及一些其他参数(监听端口、日志格式等)。这些应用程序可以部署为:
- WAR(Web应用程序归档):包含单个Java Web应用程序的归档文件:静态HTML文件、Servlet中的业务逻辑、JSP文件、Java库(jar扩展名)等。此类应用程序由配置文件
web.xml配置,该文件位于提取.war文件后的WEB-INF/web.config中。
- EAR(企业应用程序归档):包含一个或多个要部署的WAR文件或其他Java功能(如EJB)的归档文件。当需要在应用程序之间共享代码,甚至只是分组部署时,会使用这种格式。此类应用程序由提取
.ear文件后的/WEB-INF/application.xml配置文件配置。
在我们的案例中,Weblogic config.xml配置如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<server>
<name>ActivIDServer</name>
<ssl>
<name>ActivIDServer</name>
<listen-port>8445</listen-port>
</ssl>
<!-- ... -->
<app-deployment>
<name>4TRESS-EAR</name>
<module-type>ear</module-type>
<source-path>/opt/hid/weblogic/product/Oracle_Home/user_projects/applications/activid_domain/activid-authentication-services-weblogic.ear</source-path>
</app-deployment>
<app-deployment>
<name>ACTIVID-MC</name>
<module-type>war</module-type> <source-path>/opt/hid/weblogic/product/Oracle_Home/user_projects/applications/activid_domain/activid-management-console-weblogic.war</source-path>
</app-deployment>
[...]
</app-deployment>
|
分析此文件确认Java应用程序确实在8445端口上监听,最重要的是提供了部署的EAR和WAR的文件路径,其中包含应用程序正在执行的Java代码。
理解基本的Java Web应用程序架构
EAR文件格式在深入研究Java代码之前,让我们了解一下Java应用程序的工作原理。为此,我们来看一下activid-authentication-services-weblogic.ear的架构:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
$ unzip activid-authentication-services-weblogic.ear -d activid-authentication-services-weblogic
$ tree ./activid-authentication-services-weblogic/
./activid-authentication-services-weblogic
├── ac-4tress-core.jar
├── ac-4tress-scim-configuration.war
├── ac-4tress-scim.war
├── ac-iasp-backend.jar
├── activid-authentication-portal-weblogic.war
├── activid-health-check.war
├── lib
│ ├── ac-4tadap-samlproc.jar
│ ├── ac-4tadap-spi.jar
│ ├── ac-4tcore-api.jar
...
│ ├── commons-discovery-0.5.jar
│ ├── commons-fileupload-1.3.3.jar
│ └── xmlsec-2.2.0.jar
└── META-INF
├── application.xml
├── jboss-deployment-structure.xml
├── jboss-permissions.xml
├── MANIFEST.MF
├── was.policy
└── weblogic-application.xml
|
解压EAR时,您看到的是打包为多个模块的完整企业应用程序。.war文件是Web应用程序(REST端点、UI、Servlet),而像ac-4tress-core.jar和ac-iasp-backend.jar这样的.jar文件是EJB模块,为这些Web应用程序提供后端服务。lib/目录包含对EAR中每个模块可见的共享库,允许将公共代码集中而不是重复。META-INF/目录保存部署描述符:weblogic-application.xml用于WebLogic特定调优,其他容器的各种供应商描述符,最重要的是application.xml,它通过列出每个模块并告诉应用程序服务器如何部署它们来定义EAR的结构。该文件如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
<application>
<display-name>ActivID Authentication Services</display-name>
<application-name>4TRESS-EAR</application-name>
<initialize-in-order>true</initialize-in-order>
<module>
<ejb>ac-4tress-core.jar</ejb>
</module>
<module>
<ejb>ac-iasp-backend.jar</ejb>
</module>
<module>
<web>
<web-uri>activid-health-check.war</web-uri>
<context-root>AIHealthCheck</context-root>
</web>
</module>
<module>
<web>
<web-uri>ac-4tress-scim.war</web-uri>
<context-root>scim</context-root>
</web>
</module>
[...]
<module>
<web>
<web-uri>activid-authentication-portal-weblogic.war</web-uri>
<context-root>idp</context-root>
</web>
</module>
</application>
|
这里,多个Web模块,例如activid-authentication-portal-weblogic.war Web应用程序。context-root元素表示URL内的起始点,在本例中为idp。因此,任何形式为http://hid/idp/的HTTP请求将被WebLogic转发到相关联的WAR:activid-authentication-portal-weblogic.war。
在审查此XML后,我们现在对应用程序及其架构的理解如下:
1
2
3
4
5
|
nginx -> weblogic8445:8445 -> 4TRESS-EAR
├── ac-4tress-core.jar (EJB)
├── ac-iasp-backend.jar (EJB)
├── activid-authentication-portal-weblogic.war (Web app, /idp/*)
└── ...
|
WAR文件格式以activid-authentication-portal-weblogic.war为例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
$ mkdir activid-authentication-portal-weblogic
$ cd activid-authentication-portal-weblogic $ unzip ../activid-authentication-portal-weblogic.war
[...]
$ tree .
.
├── about.xhtml
├── auth
│ ├── actions-body.xhtml
│ [...]
│ └── reset-password.xhtml
├── authn
│ ├── login.xhtml
│ └── token.xhtml
├── binding
│ ├── login-artifact.xhtml
│ [...]
│ ├── nameid-redirect.xhtml
│ └── single-logout-post.xhtml
├── common
│ ├── applet
│ │ └── applet.xhtml
│ ├── error.xhtml
│ └── required.xhtml
│ [...]
├── index.html
├── license.xhtml
├── META-INF
│ ├── MANIFEST.MF
│ └── was.policy
├── nocookie.xhtml
├── resources
│ ├── csrfguard.js
│ ├── css
│ │ └── theme.css
│ └── js
│ ├── base64url-arraybuffer.js
│ └── webauthn.js
├── timeout.xhtml
└── WEB-INF
├── beans.xml
├── ejb-jar.xml
├── jboss-web.xml
├── lib
│ ├── ac-iasp-frontend.jar
│ ├── ac-iasp-frontend-jaxws.jar
│ ├── ac-inputval-esapi.jar
│ ├── ac-oauth20sdk.jar
│ ├── ai-4tress-samlidp.jar
│ ├── commons-lang3-3.7.jar
│ ├── csrfguard.jar
│ ├── esapi-2.1.0.1.jar
│ ├── lang-tag-1.4.3.jar
│ ├── oauth2-oidc-sdk-5.63.jar
│ └── primefaces-6.2.jar
├── weblogic-ejb-jar.xml
├── weblogic.xml
└── web.xml
|
我们终于开始看到与Web应用程序相关的文件,例如一些呈现UI的HTML(xhtml和html)文件。例如,浏览到https://hid/idp/common/error.xhtml页面呈现以下内容:
此页面正确对应于我们刚刚从war中提取的activid-authentication-portal-weblogic/common/error.xhtml文件。
在文件布局中,WEB-INF目录很突出。它包含所有应用程序本地依赖项的jar,例如将处理由xhtml文件触发的操作或针对暴露的Java Web Servlet的请求的类。Java Servlet是通过HTTP可访问的类:所有基于Java Web的应用程序的基本构建块。
虽然触发xhtml文件是直接的,因为只需要知道文件的路径和Java faces模块的配置,但对于Servlet,我们必须首先查看web.xml文件以了解发生了什么。该文件为我们提供了大量信息以映射攻击面,因为它定义了:
- Servlet,在
servlet标签内,与其Java类相关联,以及它们在servlet-mapping标签内的URL
- 在servlet操作之前(HTTP请求)和之后(HTTP响应)应用的过滤器(
filter标签)(理解为中间件),以及它们适用的URL(filter-mapping标签)
- 类和参数实例化
让我们剖析来自activid-authentication-portal-weblogic WAR的那个(为清晰起见部分被截断):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
$ cat activid-authentication-portal-weblogic/WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
<display-name>ActivID Authentication Portal</display-name>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
<!-- ... other servlets are declared -->
<servlet>
<description></description>
<display-name>SamlArtResolverServlet</display-name>
<servlet-name>SamlArtResolverServlet</servlet-name>
<servlet-class>com.actividentity.idp.servlet.SamlArtResolverServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SamlArtResolverServlet</servlet-name>
<url-pattern>/binding/artifact-resolution</url-pattern>
</servlet-mapping>
<filter>
<filter-name>ProtocolFilter</filter-name>
<filter-class>javax.faces.webapp.FacesServlet</filter-class>
</filter>
<!-- ... other filters are declared -->
<filter>
<filter-name>SecurityWrapper</filter-name>
<filter-class>com.hidglobal.ia.security.inputval.esapi.SecurityWrapper</filter-class>
</filter>
<filter-mapping>
<filter-name>ProtocolFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- ... other filter mappings are declared -->
<filter-mapping>
<filter-name>Security Filter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
<!-- ... -->
</web-app>
|
这里定义了多个servlet。例如,任何形式为/idp/binding/artifact-resolution的请求将被转发到com.actividentity.idp.servlet.SamlArtResolverServlet servlet,如映射中所引用。同样,任何匹配/idp/*.xhtml的URL将被转发到javax.faces.webapp.FacesServlet servlet。然而,在到达这些类之前,HTTP请求将通过声明的过滤器列表(如果它们匹配filter-mapping)。在此示例中,任何请求(/*)将进入ProtocolFilter,然后进入Security Filter,其代码分别声明在javax.faces.webapp.FacesServlet和com.hidglobal.ia.security.inputval.esapi.SecurityWrapper类中。
最后,为了找到哪个Java库(jar文件)声明了已识别的类,一个简单的grep就可以完成工作。例如,对于com.actividentity.idp.servlet.SamlArtResolverServlet:
1
2
3
4
5
6
|
$ cd activid-authentication-portal-weblogic/WEB-INF/lib/
$ rg -al 'com.actividentity.idp.servlet.SamlArtResolverServlet'
ai-4tress-samlidp.jar
$ unzip -l ai-4tress-samlidp.jar
[...]
2961 2020-12-18 17:10 com/actividentity/idp/servlet/SamlArtResolverServlet.class
|
现在我们理解了web.xml如何将URL映射到Java类,我们现在的理解如下(注意我们仅部分详细说明先前的activid-authentication-portal-weblogic WAR布局):
1
2
3
4
|
URL /idp/binding/artifact-resolution
-> web.xml mapping
-> SamlArtResolverServlet class
-> ai-4tress-samlidp.jar
|
反编译101
在这一点上,我们现在能够理解从nginx到特定servlet的路由,从而理解负责处理业务逻辑的Java类。然而,我们现在需要能够阅读Java代码以识别可能的漏洞。这就是反编译的用武之地。
多种工具可用于反编译Java字节码。在我们的研究期间,我们使用了vineflower,一个基于JetBrain的Fernflower反编译器的现代Java反编译器。
对于反编译,我们将针对EJB jar文件以及提取的WAR文件的lib目录中的所有jar:
1
2
3
4
5
6
7
8
9
10
|
$ ls -l
-rw-r--r-- 1 user user 3203575 Nov 17 16:08 ac-4tress-core.jar
-rw-r--r-- 1 user user 125721 Nov 17 16:08 ac-iasp-backend.jar
[...]
$ fdfind '\.jar$' | parallel java -jar ~/tools/vineflower.jar {} ./out/
[...]
INFO: Decompiling class com/actividentity/service/iasp/frontend/wallet/jaxws/WalletService
INFO: ... done
[...]
|
最后使用诸如VSCode之类的IDE,可以访问刚刚获得的代码,例如SamlArtResolverServlet Java源代码:
完成我们的调试环境
现在我们已经澄清了攻击面并且能够阅读Java,我们想要添加一个调试存根,以便能够从我们最喜欢的IDE设置断点以进行进一步分析。进程检查指出我们的Java应用程序是从PPID为3315的进程启动的:
1
2
3
4
5
6
7
8
9
10
|
$ ps -fwwp 3315
UID PID PPID C STIME TTY TIME CMD
ftuser 3315 1 0 02:40 ? 00:00:00 /bin/sh /opt/hid/weblogic/config/activid_domain/bin/startWebLogic.sh
$ cat /opt/hid/weblogic/config/activid_domain/bin/startWebLogic.sh
[...]
DOMAIN_HOME="/opt/hid/weblogic/config/activid_domain"
. ${DOMAIN_HOME}/bin/setDomainEnv.sh $*
SAVE_JAVA_OPTIONS="${JAVA_OPTIONS}"
[...]
${JAVA_HOME}/bin/java ${JAVA_VM} ${MEM_ARGS} -Dweblogic.Name=${SERVER_NAME} -Djava.security.policy=${WLS_POLICY_FILE} ${JAVA_OPTIONS} ${PROXY_SETTINGS} ${SERVER_CLASS} >"${WLS_REDIRECT_LOG}" 2>&1
|
这个startWebLogic.sh bash脚本设置了一些环境变量和选项,然后传递给java命令。因此,我们可以劫持此脚本以在我们的weblogic服务器上添加调试存根:
1
|
JAVA_OPTIONS="${SAVE_JAVA_OPTIONS} -agentlib:jdwp=transport=dt_socket,server=y,address=5005,suspend=n"
|
重新启动Web服务器后,我们可以确认JDWP侦听器正确运行:
1
2
3
|
$ reboot
$ ss -untalp | grep 5005
tcp LISTEN 0 1 0.0.0.0:5005 0.0.0.0:* users:(("java",pid=3446,fd=628)
|
我们还配置了我们的IDE以附加到远程JVM:
使用CodeQL进行快速SAST分析
在进行深入手动审查之前,我们通常会对代码进行快速SAST分析。我们特别喜欢用于Java源代码的CodeQL,因为它是免费的并且由社区维护良好。它允许使用基于逻辑的语言编写查询来查找模式:不安全的反序列化路径、中断的身份验证流程、SQL中危险的字符串连接或任何其他遵循代码中结构性模式的内容。
构建数据库
使用CodeQL的第一步是构建数据库。该数据库将包含从代码库中提取的可查询数据。更具体地说,它将包含代码的完整层次表示,包括抽象语法树、数据流图和控制流图的表示。大多数情况下,对于编译语言,运行SAST分析涉及使用源代码构建项目。
这对我们来说可能是个问题,因为我们没有源代码,只有从已识别的应用程序存档中提取的反编译版本。
然而,在2025年6月,CodeQL引入了一项革命性的新功能,称为build-mode none。build-mode none选项可用于在不构建源代码的情况下创建数据库(目前支持仅限于C/C++、C#、Java和Rust)。
成功提取应用程序源后,我们使用以下命令创建了一个CodeQL数据库:
1
|
$ codeql database create ~/codeql/databases/activid-authentication-services-weblogic-8.5 --language java --build-mode none -s activid-authentication-services-weblogic_vineflower/
|
注意:在处理大型项目时,将分析限制在自定义库和源代码通常是个好主意。为此,只需在创建数据库之前避免反编译第三方库。
运行分析
下一步是在我们新构建的数据库上运行CodeQL查询。按原样运行CodeQL分析器,没有任何进一步参数,将自动检测数据库语言并在目标上运行默认查询,这是一个良好的起点。然而,我们通常喜欢运行额外的查询,例如来自GithubSecurityLab的查询,它们允许识别更多的安全问题。
首先下载适当的包:
1
2
3
|
$ codeql pack download "codeql/java-all@*"
$ codeql pack download "codeql/java-queries@*"
$ codeql pack download "githubsecuritylab/codeql-java-queries"
|
然后,为了启动我们刚刚下载的包中的预构建安全查询,我们可以使用CodeQL查询套件。为此,我们创建了一个名为javasec.qls的查询套件文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
- queries: .
from: codeql/java-queries
- queries: '.'
from: githubsecuritylab/codeql-java-queries
- include:
kind:
- problem
- path-problem
tags contain: security
- exclude:
tags contain:
- debugging
- audit
- template
- exclude:
query path:
- /testing\/.*/
|
最后,我们针对数据库启动查询套件:
1
|
$ codeql database analyze ~/codeql/databases/activid-authentication-services-weblogic-8.5/ --format=sarif-latest -o activid-authentication-services-weblogic-8.5.sarif javasec.qls
|
这里我们选择SARIF(静态分析结果交换格式)输出格式。如其名称所示,此格式受到SAST工具的广泛支持。
注意:数据库分析可能消耗资源,您可以使用--ram和--threads选项调整传递给CodeQL的资源。
解释结果
我们喜欢使用带有SARIF查看器扩展的VSCode来解释结果:
虽然一些结果起初看起来很有希望,但经过深入审查后,大多数报告的漏洞被认定为误报,要么是因为我们找不到任何方法到达提到的代码,要么是因为污染的数据在到达检测到的接收器之前被清理。
手动审查应用程序源代码
这部分通常非常耗时。为了将这篇博文保持在合理的大小,我们不会在这里进行太多细节讨论。基本上有两种可以结合的方法。第一种,自上而下的方法,涉及跟踪在攻击面发现阶段识别的每条路由。第二种,自底向上的方法,涉及grep已知易受攻击的函数或模式,并尝试识别到达这些位置的路径,正如我们首先使用CodeQL所做的那样。最后,两种方法可以结合成一种混合方法,您获取尽可能多的信息以尝试将入口点链接到接收器。调试器在尝试理解如何到达代码的特定部分时可能非常有帮助。
我们在/ssp和/idp端点上花费了大量时间,因为这两个端点被最终用户大量使用。
在花了一些时间审查代码但没有结果后,我们决定尝试不同的方法,并开始动态地与迄今为止检测到的所有端点进行交互。
在我们的攻击面分析期间,我们记得我们识别了一些SOAP相关端点,这些端点由/etc/nginx/include/app/SoapAPI.conf nginx配置的命名指出,但直到现在我们还无法与这些端点交互:
1
2
3
4
5
6
7
8
9
10
11
|
# ...
location ^~ /ac-iasp-backend-jaxws/ {
proxy_pass https://$upstream;
limit_except POST {
deny all;
}
if ($request_uri ~* "(?:wsdl|xsd)$") {
return 404;
}
include /etc/nginx/include/soaperrorhandling.conf;
}
|
经过一些谷歌搜索,我们意识到JAXWS代表Jakarta XML Web Services,这是一种通过定义Java类来暴露SOAP服务的方式。例如,UserManager服务定义如下:
1
2
3
4
5
6
7
8
9
10
11
|
File: com/actividentity/service/iasp/backend/bean/UserManagerBean.java
34: @WebService(
35: name = "UserManager",
36: serviceName = "UserService",
37: targetNamespace = "http://jaxws.user.frontend.iasp.service.actividentity.com"
38: )
39: @HandlerChain(
40: file = "/LoginHandlerChain.xml"
41: )
42: @TransactionAttribute(TransactionAttributeType.NEVER)
43: public class UserManagerBean extends ProcessManager implements UserManagerLocal, UserManagerRemote
|
如文档所示,这个WebService装饰器应该创建一个名为UserService的端点,其WSDL(Web服务描述语言)文档应该可以在/ac-iasp-backend-jaxws/UserService?wsdl访问。因此,首先,我们修改了nginx配置以允许发送除POST之外的其他HTTP动词,并允许访问以wsdl或xsd结尾的URL:
1
2
3
4
|
location ^~ /ac-iasp-backend-jaxws/ {
proxy_pass https://$upstream;
include /etc/nginx/include/soaperrorhandling.conf;
}
|
现在访问UserService SOAP文档端点给出以下内容:
拥有SOAP端点的WSDL格式允许我们启动Wsdler Burp扩展,以解析WSDL和引用的XSD并拥有预构建的HTTP请求:
生成的请求远非完美,但该扩展使整个生成过程变得容易得多,并允许与SOAP端点无缝交互。以下是UserManager端点的示例:
在这一点上,只有我们中的一个人查看了应用程序的这一部分,因此发生了以下情况:
确实,在Vincent的实例上,以下请求有效:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
POST /ac-iasp-backend-jaxws/UserManager HTTP/1.1
SOAPAction:
Content-Type: text/xml;charset=UTF-8
Host: hid:443
Content-Length: 391
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:jax="http://jaxws.user.frontend.iasp.service.actividentity.com">
<soapenv:Header/>
<soapenv:Body>
<jax:findUserIds>
<arg0></arg0>
<!--type: long-->
<arg1>testu7</arg1>
<!--type: long-->
</jax:findUserIds>
</soapenv:Body>
</soapenv:Envelope>
HTTP/1.1 200 OK
Server: nginx
Date: Tue, 09 Sep 2025 17:49:05 GMT
Content-Type: text/xml; charset=utf-8
Connection: keep-alive
X-ORACLE-DMS-ECID: 9eb94a90-9da2-4982-bc84-65fb42ed1688-0000029f
X-ORACLE-DMS-RID: 0
Content-Length: 953
<?xml version='1.0' encoding='UTF-8'?>
<S:Envelope
xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns1:findUserIdsResponse
xmlns:ns1="http://jaxws.user.frontend.iasp.service.actividentity.com">
<!-- ... -->
<return>
<id>spl-helpdesk</id>
<type>User</type>
</return>
<return>
|