Skip to content

Commit cf8eb86

Browse files
committed
XWIKI-20386: CSRF privilege escalation/RCE via the edit action
1 parent e62a4b1 commit cf8eb86

File tree

2 files changed

+51
-13
lines changed

2 files changed

+51
-13
lines changed

xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/EditAction.java

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.slf4j.Logger;
3030
import org.slf4j.LoggerFactory;
3131
import org.xwiki.component.annotation.Component;
32+
import org.xwiki.csrf.CSRFToken;
3233
import org.xwiki.model.reference.DocumentReference;
3334
import org.xwiki.rendering.syntax.Syntax;
3435
import org.xwiki.user.UserReference;
@@ -58,6 +59,9 @@ public class EditAction extends XWikiAction
5859
@Named("document")
5960
private UserReferenceResolver<DocumentReference> documentReferenceUserReferenceResolver;
6061

62+
@Inject
63+
private CSRFToken csrf;
64+
6165
/**
6266
* Default constructor.
6367
*/
@@ -116,19 +120,29 @@ protected XWikiDocument prepareEditedDocument(XWikiContext context) throws XWiki
116120
updateDocumentTitleAndContentFromRequest(editedDocument, context);
117121
editedDocument.readAddedUpdatedAndRemovedObjectsFromForm(editForm, context);
118122

119-
// If the metadata is modified, modify the effective metadata author
120-
if (editedDocument.isMetaDataDirty()) {
121-
UserReference userReference =
122-
this.documentReferenceUserReferenceResolver.resolve(context.getUserReference());
123-
editedDocument.getAuthors().setEffectiveMetadataAuthor(userReference);
124-
editedDocument.getAuthors().setOriginalMetadataAuthor(userReference);
125-
}
123+
// Check if the document in modified
124+
if (editedDocument.isMetaDataDirty() || editedDocument.isContentDirty()) {
125+
// If the document is modified make sure a valid CSRF is provided
126+
String token = context.getRequest().getParameter("form_token");
127+
if (!this.csrf.isTokenValid(token)) {
128+
// or make the document restricted
129+
editedDocument.setRestricted(true);
130+
}
126131

127-
// If the content is modified, modify the content author
128-
if (editedDocument.isContentDirty()) {
129-
UserReference userReference =
130-
this.documentReferenceUserReferenceResolver.resolve(context.getUserReference());
131-
editedDocument.getAuthors().setContentAuthor(userReference);
132+
// If the metadata is modified, modify the effective metadata author
133+
if (editedDocument.isMetaDataDirty()) {
134+
UserReference userReference =
135+
this.documentReferenceUserReferenceResolver.resolve(context.getUserReference());
136+
editedDocument.getAuthors().setEffectiveMetadataAuthor(userReference);
137+
editedDocument.getAuthors().setOriginalMetadataAuthor(userReference);
138+
}
139+
140+
// If the content is modified, modify the content author
141+
if (editedDocument.isContentDirty()) {
142+
UserReference userReference =
143+
this.documentReferenceUserReferenceResolver.resolve(context.getUserReference());
144+
editedDocument.getAuthors().setContentAuthor(userReference);
145+
}
132146
}
133147

134148
// Set the current user as creator, author and contentAuthor when the edited document is newly created to avoid

xwiki-platform-core/xwiki-platform-oldcore/src/test/java/com/xpn/xwiki/web/EditActionTest.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,14 @@
1919
*/
2020
package com.xpn.xwiki.web;
2121

22+
import java.util.Map;
23+
2224
import javax.inject.Named;
2325

2426
import org.junit.jupiter.api.BeforeEach;
2527
import org.junit.jupiter.api.Test;
2628
import org.mockito.Mock;
29+
import org.xwiki.csrf.CSRFToken;
2730
import org.xwiki.model.reference.DocumentReference;
2831
import org.xwiki.store.TemporaryAttachmentSessionsManager;
2932
import org.xwiki.test.junit5.mockito.InjectMockComponents;
@@ -39,6 +42,7 @@
3942
import com.xpn.xwiki.test.junit5.mockito.OldcoreTest;
4043
import com.xpn.xwiki.test.reference.ReferenceComponentList;
4144

45+
import static org.junit.jupiter.api.Assertions.assertEquals;
4246
import static org.junit.jupiter.api.Assertions.assertSame;
4347
import static org.mockito.Mockito.mock;
4448
import static org.mockito.Mockito.when;
@@ -75,6 +79,9 @@ public class EditActionTest
7579
@Named("document")
7680
private UserReferenceSerializer<DocumentReference> documentReferenceUserReferenceSerializer;
7781

82+
@MockComponent
83+
private CSRFToken csrf;
84+
7885
@Mock
7986
private XWikiRequest request;
8087

@@ -85,6 +92,9 @@ public void beforeEach()
8592
when(this.documentReferenceUserReferenceSerializer.serialize(USER_REFERENCE)).thenReturn(USER_DOCUMENT_REFERENCE);
8693

8794
this.oldcore.getXWikiContext().setUserReference(USER_DOCUMENT_REFERENCE);
95+
96+
this.oldcore.getXWikiContext().setRequest(new XWikiServletRequestStub.Builder().
97+
setRequestParameters(Map.of("form_token", new String[] {"tokenvalue"})).build());
8898
}
8999

90100
private String initAndRenderAction() throws XWikiException
@@ -141,7 +151,18 @@ void documentAuthorsWhenDocumentExist() throws XWikiException
141151
}
142152

143153
@Test
144-
void documentAuthorsWhenDocumentExistAndContentIsModified() throws XWikiException
154+
void documentAuthorsWhenDocumentExistAndContentIsModifiedAndInvalidValidCSRF() throws XWikiException
155+
{
156+
documentAuthorsWhenDocumentExistAndContentIsModified(false);
157+
}
158+
159+
@Test
160+
void documentAuthorsWhenDocumentExistAndContentIsModifiedAndValidCSRF() throws XWikiException
161+
{
162+
documentAuthorsWhenDocumentExistAndContentIsModified(true);
163+
}
164+
165+
void documentAuthorsWhenDocumentExistAndContentIsModified(boolean validToken) throws XWikiException
145166
{
146167
XWikiDocument document = this.oldcore.getSpyXWiki().getDocument(new DocumentReference("wiki", "space", "page"),
147168
this.oldcore.getXWikiContext());
@@ -158,6 +179,8 @@ void documentAuthorsWhenDocumentExistAndContentIsModified() throws XWikiExceptio
158179

159180
when(this.request.getParameter("content")).thenReturn("modified content");
160181

182+
when(this.csrf.isTokenValid("tokenvalue")).thenReturn(validToken);
183+
161184
initAndRenderAction();
162185

163186
document = this.oldcore.getXWikiContext().getDoc();
@@ -166,5 +189,6 @@ void documentAuthorsWhenDocumentExistAndContentIsModified() throws XWikiExceptio
166189
assertSame(OTHERUSER_REFERENCE, document.getAuthors().getCreator());
167190
assertSame(OTHERUSER_REFERENCE, document.getAuthors().getEffectiveMetadataAuthor());
168191
assertSame(OTHERUSER_REFERENCE, document.getAuthors().getOriginalMetadataAuthor());
192+
assertEquals(!validToken, document.isRestricted());
169193
}
170194
}

0 commit comments

Comments
 (0)