
Demonstra uma vulnerabilidade crítica de previsibilidade da chave de assinatura JWT no PowerJob Server, permitindo a derivação offline da chave e a falsificação de tokens para assumir o controle de administrador e contornar a autenticação da OpenAPI.
A chave de assinatura JWT (HS256) do servidor PowerJob é derivada pela decodificação BASE64 de uma constante codificada concatenada com o MD5 da URL JDBC padrão (DefaultSecretProvider / JwtServiceImpl). A URL JDBC é pública na implantação padrão (docker-compose / application-daily.properties), portanto um atacante pode derivar a chave totalmente offline e forjar um JWT válido para qualquer usuário (incluindo o administrador); o Token do App OpenAPI também pode ser forjado para contornar a autenticação OpenAPI.
application-daily.properties expõe em texto simplesjdbc:mysql://powerjob-mysql:3306/powerjob-daily?...powerjob-server-auth/.../jwt/impl/DefaultSecretProvider.java:31-45: return md5(environment.getProperty("spring.datasource.core.jdbc-url"));powerjob-server-auth/.../jwt/impl/JwtServiceImpl.java:44-102: constante BASE_SECURITY codificada; genSecretKey(secret) = Decoders.BASE64.decode(BASE_SECURITY.concat(secret)) → Keys.hmacShaKeyForusername/encryptedToken) possui uma verificação secundária para contas PWJB (DigestUtils.rePassword(password, username)), mas:
appId/password/encryptType) pode ser forjado diretamente para contornar a autenticação (OpenApiSecurityServiceImpl:87-105);Ambiente: servidor PowerJob local (127.0.0.1:7700, URL JDBC jdbc:mysql://127.0.0.1:3307/powerjob_daily?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true). O atacante deriva tudo offline apenas a partir de informações públicas:
# ① Derivar encryptedToken (= rePassword(password, username)); computável com a senha padrão
rePassword("powerjob_admin", "ADMIN")
= ADMIN_b82a450701f6a71723fb99931fe35e18_b # byte a byte igual ao user_info.token_login_verify_info do banco ✅
# ② Derivar a chave de assinatura HS256 = BASE64(BASE_SECURITY + MD5(JDBC_URL))
secret = BASE_SECURITY + md5(jdbc_url) # chave de 101 bytes
# ③ Forjar um JWT web de ADMIN (claims: username=PWJB_ADMIN, encryptedToken=resultado de ①, sub=PowerJob)
Resultado real (token forjado passa na autenticação):
[*] /namespace/list sem token :
{"success":false,"data":null,"message":"PowerJobAuthException: UserNotLoggedIn","code":"-100"}
[*] /namespace/list JWT forjado :
{"success":true,"data":{"index":0,"pageSize":10,"totalPages":1,"totalItems":1,"data":[{"id":1,"code":"default_namespace","name":"default_namespace","dept":null,"tags":null,"extra":null,"status":1,"statusStr":"ENABLE","gmtCreate":"2026-08-07T13:07:32.000+00:00","gmtCreateStr":"2026-08-07 21:07:32","gmtModified":"2026-08-07T13:07:32.000+00:00","gmtModifiedStr":"2026-08-07 21:07:32","showName":"default_namespace(default_namespace)","token":"14e4ebff-9a79-4845-ac66-8b20c4dff2ee","componentUserRoleInfo":{"observer":[],"qa":[],"developer":[],"admin":[]},"creatorShowName":null,"modifierShowName":null}]},"message":null}
→ o token forjado autentica como administrador e retorna dados reais (a linha de base é rejeitada com UserNotLoggedIn).
Pontos-chave:
encryptedToken derivado offline corresponde ao valor armazenado no banco byte a byte (ADMIN_b82a450701f6a71723fb99931fe35e18_b).BASE_SECURITY + MD5 da URL JDBC padrão pública → derivável offline sem credenciais.encryptedToken PWJB é recomputável offline com a senha padrão (powerjob_admin, achado PJ-02) → comprometimento total do administrador.encryptedToken, mas o caminho do Token do App OpenAPI (claims appId/password/encryptType, OpenApiSecurityServiceImpl:87-105) ainda pode ser forjado de forma independente para contornar a autenticação OpenAPI.Mesmo que a senha padrão seja alterada, a vulnerabilidade persiste por outros vetores de ataque:
encryptedToken de qualquer usuário válido por outros meios (por exemplo, injeção de SQL, exposição de backup do banco de dados, vazamento de logs ou divulgação de informações de outros endpoints) pode combinar esse token com a chave de assinatura previsível para forjar um JWT para esse usuário específico.encryptedToken, porque o token forjado agora contém um encryptedToken legítimo derivado do registro real do banco de dados.PoC: powerjob_jwt_key.py 192.168.49.128:7700


encryptedToken para todas as contas.CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N = 9.1 Crítico (combinado com a senha padrão / OpenAPI)