VYPR
High severityNVD Advisory· Published Jul 2, 2025· Updated Jul 2, 2025

Graylog vulnerable to privilege escalation through API tokens

CVE-2025-53106

Description

Graylog is a free and open log management platform. In versions 6.2.0 to before 6.2.4 and 6.3.0-alpha.1 to before 6.3.0-rc.2, Graylog users can gain elevated privileges by creating and using API tokens for the local Administrator or any other user for whom the malicious user knows the ID. For the attack to succeed, the attacker needs a user account in Graylog. They can then proceed to issue hand-crafted requests to the Graylog REST API and exploit a weak permission check for token creation. This issue has been patched in versions 6.2.4 and 6.3.0-rc.2. A workaround involves disabling the respective configuration found in System > Configuration > Users > "Allow users to create personal access tokens".

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.graylog2:graylog2-serverMaven
>= 6.2.0, < 6.2.46.2.4
org.graylog2:graylog2-serverMaven
>= 6.3.0-alpha.1, < 6.3.0-rc.26.3.0-rc.2

Affected products

1

Patches

2
9215b8f1fd32

Merge commit from fork

https://github.com/Graylog2/graylog2-serverFlorian PetersenJun 23, 2025via ghsa
3 files changed · +17 24
  • changelog/unreleased/ghsa-3m86-c9x3-vwm9.toml+2 0 added
    @@ -0,0 +1,2 @@
    +type = "security"
    +message = "Fix permission check for creating a new API-token. See [GHSA-3m86-c9x3-vwm9](https://github.com/Graylog2/graylog2-server/security/advisories/GHSA-3m86-c9x3-vwm9) for details."
    
  • graylog2-server/src/main/java/org/graylog2/rest/resources/users/UsersResource.java+2 6 modified
    @@ -727,13 +727,9 @@ public Token generateNewToken(
                 @ApiParam(name = "name", value = "Descriptive name for this token (e.g. 'cronjob') ", required = true) @PathParam("name") String name,
                 @ApiParam(name = "JSON Body", value = "Can optionally contain the token's TTL.", defaultValue = "{\"token_ttl\":null}") GenerateTokenTTL body) {
             final User futureOwner = loadUserById(userId);
    -        final User currentUser = getCurrentUser();
     
    -        if (currentUser == null) {
    -            throw new ForbiddenException("Not allowed to create tokens for unknown user.");
    -        }
    -        if (!isPermitted(USERS_TOKENCREATE, currentUser.getName())) {
    -            throw new ForbiddenException(currentUser.getName() + " is not allowed to create token.");
    +        if (!isPermitted(USERS_TOKENCREATE, futureOwner.getName())) {
    +            throw new ForbiddenException("You are not allowed to create a token for user " + futureOwner.getName() + ".");
             }
     
             if (body == null) {
    
  • graylog2-server/src/test/java/org/graylog2/rest/resources/users/UsersResourceTest.java+13 18 modified
    @@ -93,7 +93,6 @@ public class UsersResourceTest {
         private static final String TOKEN_NAME = "tokenName";
     
         private static final String ADMIN_OBJECT_ID = new ObjectId().toHexString();
    -    private static final PeriodDuration PD_30_DAYS = PeriodDuration.parse("P30D");
     
         @Rule
         public MockitoRule rule = MockitoJUnit.rule();
    @@ -212,7 +211,6 @@ public void createTokenFailsIfCreateNotAllowed() {
                 final UsersResource.GenerateTokenTTL ttl = new UsersResource.GenerateTokenTTL(Optional.of(PeriodDuration.of(Duration.ofDays(30))));
                 assertThrows(ForbiddenException.class, () -> usersResource.generateNewToken(USERNAME, TOKEN_NAME, ttl));
             } finally {
    -            verify(subject).getPrincipal();
                 verify(subject).isPermitted(USERS_TOKENCREATE + ":" + USERNAME);
                 verifyNoMoreInteractions(clusterConfigService, accessTokenService);
             }
    @@ -227,7 +225,6 @@ public void createTokenSucceedsEvenWithNULLBody() {
                 final Token actual = usersResource.generateNewToken(USERNAME, TOKEN_NAME, null);
                 assertEquals(expected, actual);
             } finally {
    -            verify(subject).getPrincipal();
                 verify(subject).isPermitted(USERS_TOKENCREATE + ":" + USERNAME);
                 verify(clusterConfigService).getOrDefault(UserConfiguration.class, UserConfiguration.DEFAULT_VALUES);
                 verify(accessTokenService).create(USERNAME, TOKEN_NAME, PeriodDuration.of(Duration.ofDays(30)));
    @@ -246,8 +243,7 @@ public void adminCanCreateTokensForOtherUsers() {
                 final Token actual = usersResource.generateNewToken(USERNAME, TOKEN_NAME, null);
                 assertEquals(expected, actual);
             } finally {
    -            verify(subject).getPrincipal();
    -            verify(subject).isPermitted(USERS_TOKENCREATE + ":" + adminUserName);
    +            verify(subject).isPermitted(USERS_TOKENCREATE + ":" + USERNAME);
                 verify(clusterConfigService).getOrDefault(UserConfiguration.class, UserConfiguration.DEFAULT_VALUES);
                 verify(accessTokenService).create(USERNAME, TOKEN_NAME, PeriodDuration.of(Duration.ofDays(30)));
                 verifyNoMoreInteractions(clusterConfigService, accessTokenService);
    @@ -264,8 +260,7 @@ public void regularUserCannotCreateTokensForOtherUsers() {
             try {
                 assertThrows(ForbiddenException.class, () -> usersResource.generateNewToken(USERNAME, TOKEN_NAME, null));
             } finally {
    -            verify(subject).getPrincipal();
    -            verify(subject).isPermitted(USERS_TOKENCREATE + ":" + otherUserName);
    +            verify(subject).isPermitted(USERS_TOKENCREATE + ":" + USERNAME);
                 verifyNoMoreInteractions(clusterConfigService, accessTokenService);
             }
         }
    @@ -290,14 +285,14 @@ private Token createTokenAndPrepareMocks(Map<String, Object> owningUser, Map<Str
                 user.setRoleIds(Set.of(ADMIN_OBJECT_ID));
             }
     
    -        when(subject.getPrincipal()).thenReturn(callingUserName);
    -        when(userService.loadById(callingUserName)).thenReturn(user);
    -        when(roleService.getAdminRoleObjectId()).thenReturn(ADMIN_OBJECT_ID);
    +        final boolean allowedToCreateToken = callingUserName.equals(owningUserName) || isAdmin;
             when(userManagementService.loadById(USERNAME)).thenReturn(userImplFactory.create(owningUser));
    -        when(subject.isPermitted(USERS_TOKENCREATE + ":" + callingUserName)).thenReturn(callingUserName.equals(owningUserName) || isAdmin);
    -        when(clusterConfigService.getOrDefault(UserConfiguration.class, UserConfiguration.DEFAULT_VALUES))
    -                .thenReturn(UserConfiguration.create(false, Duration.of(8, ChronoUnit.HOURS), false, false, PeriodDuration.of(Duration.ofDays(30))));
    -        when(accessTokenService.create(USERNAME, UsersResourceTest.TOKEN_NAME, PeriodDuration.of(Duration.ofDays(30)))).thenReturn(accessToken);
    +        when(subject.isPermitted(USERS_TOKENCREATE + ":" + owningUserName)).thenReturn(allowedToCreateToken);
    +        if (allowedToCreateToken) {
    +            when(clusterConfigService.getOrDefault(UserConfiguration.class, UserConfiguration.DEFAULT_VALUES))
    +                    .thenReturn(UserConfiguration.create(false, Duration.of(8, ChronoUnit.HOURS), false, false, PeriodDuration.of(Duration.ofDays(30))));
    +            when(accessTokenService.create(USERNAME, UsersResourceTest.TOKEN_NAME, PeriodDuration.of(Duration.ofDays(30)))).thenReturn(accessToken);
    +        }
     
             return Token.create(tokenId.toHexString(), TOKEN_NAME, token, lastAccess);
     
    @@ -308,7 +303,7 @@ private Token createTokenAndPrepareMocks(Map<String, Object> userProps, boolean
             final DateTime lastAccess = Tools.nowUTC();
             final Map<String, Object> tokenProps = Map.of(AccessTokenImpl.NAME, TOKEN_NAME, AccessTokenImpl.TOKEN, token, AccessTokenImpl.LAST_ACCESS, lastAccess);
             final ObjectId tokenId = new ObjectId();
    -        final AccessToken accessToken = new AccessTokenImpl(tokenId, tokenProps);
    +        final AccessToken accessToken = allowCreateToken ? new AccessTokenImpl(tokenId, tokenProps) : null;
     
             prepareMocks(userProps, accessToken, allowCreateToken);
     
    @@ -317,11 +312,11 @@ private Token createTokenAndPrepareMocks(Map<String, Object> userProps, boolean
     
         private void prepareMocks(Map<String, Object> userProps, AccessToken accessToken, boolean allow) {
             final User user = userImplFactory.create(userProps);
    -        when(subject.getPrincipal()).thenReturn(USERNAME);
    -        when(userService.loadById(USERNAME)).thenReturn(user);
             when(userManagementService.loadById(USERNAME)).thenReturn(user);
             when(subject.isPermitted(USERS_TOKENCREATE + ":" + USERNAME)).thenReturn(allow);
    -        when(clusterConfigService.getOrDefault(UserConfiguration.class, UserConfiguration.DEFAULT_VALUES)).thenReturn(UserConfiguration.DEFAULT_VALUES);
    +        if (allow) {
    +            when(clusterConfigService.getOrDefault(UserConfiguration.class, UserConfiguration.DEFAULT_VALUES)).thenReturn(UserConfiguration.DEFAULT_VALUES);
    +        }
             if (accessToken != null) {
                 when(accessTokenService.create(USERNAME, UsersResourceTest.TOKEN_NAME, PeriodDuration.of(Duration.ofDays(30)))).thenReturn(accessToken);
             }
    
6936bd16a783

Merge commit from fork

https://github.com/Graylog2/graylog2-serverFlorian PetersenJun 23, 2025via ghsa
3 files changed · +17 24
  • changelog/unreleased/ghsa-3m86-c9x3-vwm9.toml+2 0 added
    @@ -0,0 +1,2 @@
    +type = "security"
    +message = "Fix permission check for creating a new API-token. See [GHSA-3m86-c9x3-vwm9](https://github.com/Graylog2/graylog2-server/security/advisories/GHSA-3m86-c9x3-vwm9) for details."
    
  • graylog2-server/src/main/java/org/graylog2/rest/resources/users/UsersResource.java+2 6 modified
    @@ -726,13 +726,9 @@ public Token generateNewToken(
                 @ApiParam(name = "name", value = "Descriptive name for this token (e.g. 'cronjob') ", required = true) @PathParam("name") String name,
                 @ApiParam(name = "JSON Body", value = "Can optionally contain the token's TTL.", defaultValue = "{\"token_ttl\":null}") GenerateTokenTTL body) {
             final User futureOwner = loadUserById(userId);
    -        final User currentUser = getCurrentUser();
     
    -        if (currentUser == null) {
    -            throw new ForbiddenException("Not allowed to create tokens for unknown user.");
    -        }
    -        if (!isPermitted(USERS_TOKENCREATE, currentUser.getName())) {
    -            throw new ForbiddenException(currentUser.getName() + " is not allowed to create token.");
    +        if (!isPermitted(USERS_TOKENCREATE, futureOwner.getName())) {
    +            throw new ForbiddenException("You are not allowed to create a token for user " + futureOwner.getName() + ".");
             }
     
             if (body == null) {
    
  • graylog2-server/src/test/java/org/graylog2/rest/resources/users/UsersResourceTest.java+13 18 modified
    @@ -88,7 +88,6 @@ public class UsersResourceTest {
         private static final String TOKEN_NAME = "tokenName";
     
         private static final String ADMIN_OBJECT_ID = new ObjectId().toHexString();
    -    private static final PeriodDuration PD_30_DAYS = PeriodDuration.parse("P30D");
     
         @Rule
         public MockitoRule rule = MockitoJUnit.rule();
    @@ -196,7 +195,6 @@ public void createTokenFailsIfCreateNotAllowed() {
                 final UsersResource.GenerateTokenTTL ttl = new UsersResource.GenerateTokenTTL(Optional.of(PeriodDuration.of(Duration.ofDays(30))));
                 assertThrows(ForbiddenException.class, () -> usersResource.generateNewToken(USERNAME, TOKEN_NAME, ttl));
             } finally {
    -            verify(subject).getPrincipal();
                 verify(subject).isPermitted(USERS_TOKENCREATE + ":" + USERNAME);
                 verifyNoMoreInteractions(clusterConfigService, accessTokenService);
             }
    @@ -211,7 +209,6 @@ public void createTokenSucceedsEvenWithNULLBody() {
                 final Token actual = usersResource.generateNewToken(USERNAME, TOKEN_NAME, null);
                 assertEquals(expected, actual);
             } finally {
    -            verify(subject).getPrincipal();
                 verify(subject).isPermitted(USERS_TOKENCREATE + ":" + USERNAME);
                 verify(clusterConfigService).getOrDefault(UserConfiguration.class, UserConfiguration.DEFAULT_VALUES);
                 verify(accessTokenService).create(USERNAME, TOKEN_NAME, PeriodDuration.of(Duration.ofDays(30)));
    @@ -230,8 +227,7 @@ public void adminCanCreateTokensForOtherUsers() {
                 final Token actual = usersResource.generateNewToken(USERNAME, TOKEN_NAME, null);
                 assertEquals(expected, actual);
             } finally {
    -            verify(subject).getPrincipal();
    -            verify(subject).isPermitted(USERS_TOKENCREATE + ":" + adminUserName);
    +            verify(subject).isPermitted(USERS_TOKENCREATE + ":" + USERNAME);
                 verify(clusterConfigService).getOrDefault(UserConfiguration.class, UserConfiguration.DEFAULT_VALUES);
                 verify(accessTokenService).create(USERNAME, TOKEN_NAME, PeriodDuration.of(Duration.ofDays(30)));
                 verifyNoMoreInteractions(clusterConfigService, accessTokenService);
    @@ -248,8 +244,7 @@ public void regularUserCannotCreateTokensForOtherUsers() {
             try {
                 assertThrows(ForbiddenException.class, () -> usersResource.generateNewToken(USERNAME, TOKEN_NAME, null));
             } finally {
    -            verify(subject).getPrincipal();
    -            verify(subject).isPermitted(USERS_TOKENCREATE + ":" + otherUserName);
    +            verify(subject).isPermitted(USERS_TOKENCREATE + ":" + USERNAME);
                 verifyNoMoreInteractions(clusterConfigService, accessTokenService);
             }
         }
    @@ -274,14 +269,14 @@ private Token createTokenAndPrepareMocks(Map<String, Object> owningUser, Map<Str
                 user.setRoleIds(Set.of(ADMIN_OBJECT_ID));
             }
     
    -        when(subject.getPrincipal()).thenReturn(callingUserName);
    -        when(userService.loadById(callingUserName)).thenReturn(user);
    -        when(roleService.getAdminRoleObjectId()).thenReturn(ADMIN_OBJECT_ID);
    +        final boolean allowedToCreateToken = callingUserName.equals(owningUserName) || isAdmin;
             when(userManagementService.loadById(USERNAME)).thenReturn(userImplFactory.create(owningUser));
    -        when(subject.isPermitted(USERS_TOKENCREATE + ":" + callingUserName)).thenReturn(callingUserName.equals(owningUserName) || isAdmin);
    -        when(clusterConfigService.getOrDefault(UserConfiguration.class, UserConfiguration.DEFAULT_VALUES))
    -                .thenReturn(UserConfiguration.create(false, Duration.of(8, ChronoUnit.HOURS), false, false, PeriodDuration.of(Duration.ofDays(30))));
    -        when(accessTokenService.create(USERNAME, UsersResourceTest.TOKEN_NAME, PeriodDuration.of(Duration.ofDays(30)))).thenReturn(accessToken);
    +        when(subject.isPermitted(USERS_TOKENCREATE + ":" + owningUserName)).thenReturn(allowedToCreateToken);
    +        if (allowedToCreateToken) {
    +            when(clusterConfigService.getOrDefault(UserConfiguration.class, UserConfiguration.DEFAULT_VALUES))
    +                    .thenReturn(UserConfiguration.create(false, Duration.of(8, ChronoUnit.HOURS), false, false, PeriodDuration.of(Duration.ofDays(30))));
    +            when(accessTokenService.create(USERNAME, UsersResourceTest.TOKEN_NAME, PeriodDuration.of(Duration.ofDays(30)))).thenReturn(accessToken);
    +        }
     
             return Token.create(tokenId.toHexString(), TOKEN_NAME, token, lastAccess);
     
    @@ -292,7 +287,7 @@ private Token createTokenAndPrepareMocks(Map<String, Object> userProps, boolean
             final DateTime lastAccess = Tools.nowUTC();
             final Map<String, Object> tokenProps = Map.of(AccessTokenImpl.NAME, TOKEN_NAME, AccessTokenImpl.TOKEN, token, AccessTokenImpl.LAST_ACCESS, lastAccess);
             final ObjectId tokenId = new ObjectId();
    -        final AccessToken accessToken = new AccessTokenImpl(tokenId, tokenProps);
    +        final AccessToken accessToken = allowCreateToken ? new AccessTokenImpl(tokenId, tokenProps) : null;
     
             prepareMocks(userProps, accessToken, allowCreateToken);
     
    @@ -301,11 +296,11 @@ private Token createTokenAndPrepareMocks(Map<String, Object> userProps, boolean
     
         private void prepareMocks(Map<String, Object> userProps, AccessToken accessToken, boolean allow) {
             final User user = userImplFactory.create(userProps);
    -        when(subject.getPrincipal()).thenReturn(USERNAME);
    -        when(userService.loadById(USERNAME)).thenReturn(user);
             when(userManagementService.loadById(USERNAME)).thenReturn(user);
             when(subject.isPermitted(USERS_TOKENCREATE + ":" + USERNAME)).thenReturn(allow);
    -        when(clusterConfigService.getOrDefault(UserConfiguration.class, UserConfiguration.DEFAULT_VALUES)).thenReturn(UserConfiguration.DEFAULT_VALUES);
    +        if (allow) {
    +            when(clusterConfigService.getOrDefault(UserConfiguration.class, UserConfiguration.DEFAULT_VALUES)).thenReturn(UserConfiguration.DEFAULT_VALUES);
    +        }
             if (accessToken != null) {
                 when(accessTokenService.create(USERNAME, UsersResourceTest.TOKEN_NAME, PeriodDuration.of(Duration.ofDays(30)))).thenReturn(accessToken);
             }
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.