[ 
https://issues.apache.org/jira/browse/KNOX-3413?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Tamás Hanicz updated KNOX-3413:
-------------------------------
    Description: 
Reported by n0mi1k Security 

 

When KnoxToken passcodes are in use, AbstractJWTFilter validates a presented 
(tokenId, passcode) pair, and at AbstractJWTFilter.java:504 the guard is 
`hasSignatureBeenVerified(passcode) || validatePasscode(tokenId, passcode)`; 
validatePasscode (:544-553) is the only check that binds the passcode to the 
token identifier — it compares tokenMAC.hash(tokenId, issueTime, userName, 
passcode) against the passcode stored for that specific tokenId — and on a 
successful check it records the passcode STRING in a shared cache via 
recordSignatureVerification(passcode). Because the cache is keyed on the 
passcode string alone (SignatureVerificationCache.hasSignatureBeenVerified / 
verifiedTokens.getIfPresent at :96, populated by verifiedTokens.put(passcode, 
true) at :105), a client that has once verified its own passcode can then 
present that same passcode paired with a different, victim token identifier: 
hasSignatureBeenVerified(passcode) returns true, the tokenId-binding MAC check 
is skipped entirely, and the authenticated subject is built from the victim 
tokenId via createSubjectFromTokenIdentifier (:361). Token identifiers are not 
secret — they are returned in token-generation responses, are enumerable via 
the getUserTokens endpoint, and equal the public client_id in the 
client-credentials flow — so a low-privilege KnoxToken holder can authenticate 
as any other still-valid token's principal, which is an identity-assertion / 
authentication bypass, the keystone property a perimeter gateway is meant to 
uphold. The attached PoC drives the real AbstractJWTFilter.validateToken + real 
SignatureVerificationCache + real TokenMAC: after seeding the cache with 
(tokenA, PASSCODE_A), the call validateToken(tokenB, PASSCODE_A) returns true 
and createSubjectFromTokenIdentifier(tokenB) yields "bob" (tokenB's owner), 
while the same call against an empty cache returns false (the real MAC check 
rejects it). 

>  KnoxToken passcode verification accepts a valid passcode for a different 
> token
> -------------------------------------------------------------------------------
>
>                 Key: KNOX-3413
>                 URL: https://issues.apache.org/jira/browse/KNOX-3413
>             Project: Apache Knox
>          Issue Type: Bug
>          Components: Server
>    Affects Versions: 2.1.0, 3.0.0
>            Reporter: Tamás Hanicz
>            Assignee: Tamás Hanicz
>            Priority: Blocker
>             Fix For: 3.0.0, 3.1.0
>
>          Time Spent: 0.5h
>  Remaining Estimate: 0h
>
> Reported by n0mi1k Security 
>  
> When KnoxToken passcodes are in use, AbstractJWTFilter validates a presented 
> (tokenId, passcode) pair, and at AbstractJWTFilter.java:504 the guard is 
> `hasSignatureBeenVerified(passcode) || validatePasscode(tokenId, passcode)`; 
> validatePasscode (:544-553) is the only check that binds the passcode to the 
> token identifier — it compares tokenMAC.hash(tokenId, issueTime, userName, 
> passcode) against the passcode stored for that specific tokenId — and on a 
> successful check it records the passcode STRING in a shared cache via 
> recordSignatureVerification(passcode). Because the cache is keyed on the 
> passcode string alone (SignatureVerificationCache.hasSignatureBeenVerified / 
> verifiedTokens.getIfPresent at :96, populated by verifiedTokens.put(passcode, 
> true) at :105), a client that has once verified its own passcode can then 
> present that same passcode paired with a different, victim token identifier: 
> hasSignatureBeenVerified(passcode) returns true, the tokenId-binding MAC 
> check is skipped entirely, and the authenticated subject is built from the 
> victim tokenId via createSubjectFromTokenIdentifier (:361). Token identifiers 
> are not secret — they are returned in token-generation responses, are 
> enumerable via the getUserTokens endpoint, and equal the public client_id in 
> the client-credentials flow — so a low-privilege KnoxToken holder can 
> authenticate as any other still-valid token's principal, which is an 
> identity-assertion / authentication bypass, the keystone property a perimeter 
> gateway is meant to uphold. The attached PoC drives the real 
> AbstractJWTFilter.validateToken + real SignatureVerificationCache + real 
> TokenMAC: after seeding the cache with (tokenA, PASSCODE_A), the call 
> validateToken(tokenB, PASSCODE_A) returns true and 
> createSubjectFromTokenIdentifier(tokenB) yields "bob" (tokenB's owner), while 
> the same call against an empty cache returns false (the real MAC check 
> rejects it). 



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to