
Atlassian Jira unauthen template injection
4.4.x
5.x.x
6.x.x
7.0.x
7.1.x
7.2.x
7.3.x
7.4.x
7.5.x
7.6.x before 7.6.14 (the fixed version for 7.6.x)
7.7.x
7.8.x
7.9.x
7.10.x
7.11.x
7.12.x
7.13.x before 7.13.5 (the fixed version for 7.13.x)
8.0.x before 8.0.3 (the fixed version for 8.0.x)
8.1.x before 8.1.2 (the fixed version for 8.1.x)
8.2.x before 8.2.3 (the fixed version for 8.2.x)
set JVM_SUPPORT_RECOMMENDED_ARGS= in the file ./bin/setenv.bat (or similarly for ./bin/setenv.sh on Linux) to enable remote debuggingset JVM_SUPPORT_RECOMMENDED_ARGS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
Remote JVM Debug with host and port set to localhost:5005 and Command line arguments for remote JVM-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005
Run ./bin/config.bat (or ./bin/config.sh on Linux) and set "Jira home" to your jira home directory

Run ./bin/start-jira.bat (or ./bin/start-jira.sh on Linux). Check that Java version, ports 8080 and 5005 are free if it fails to start!
Select private mode

At this step, remember to use an email address that can receive emails :)

Configure and test connection properly

At http://localhost:8080/secure/admin/EditApplicationProperties!default.jspa enable the Contact Administrators Form

I only note some tips when building, you can refer to building jira from source
After reading the Advisory we know this bug is in ContactAdministrators and SendBulkMail (I skip the latter because it requires authentication). So I start testing the ContactAdministrators feature and capture the request

I see the request goes to /secure/ContactAdministrators.jspa so I check ./atlassian-jira/WEB-INF/web.xml to see which class handles this request


So the request is handled by JiraWebworkActionDispatcher, therefore I set a breakpoint at init and server in this class and run debug

The program stops at the server function, after tracing a bit it jumps into ContactAdministrators.doExecute()

Then into send, here the program lists all active admin accounts

Then the program goes into the sendTo function. Here we see it creates a MailQueueItem and adds it to the mailQueue.

The program calls EmailBuilder.withSubject. Here the email subject string (sent by attacker) is converted from String to TemplateSources and assigned to the subjectTemplate parameter of EmailBuilder.

In the renderLater function, the program creates an EmailRenderer and uses it to create a RenderingMailQueueItem

From here, when we return to ContactAdministrators.sendTo. After creating a MailQueueItem, the program adds it to the mailQueue and then returns to doExecute to Redirect. If we follow this debug flow, we cannot jump to the part where the email is rendered, so we cannot reach the template injection point. So what should I do to trace the email processing?
I see that EmailRenderer has a renderNow function (the program only calls renderLater). I guess that when the email in the queue is called to be rendered, it will follow a similar flow as renderNow, so I decide to trace from the renderNow function

From renderNow, the program calls EmailRenderer.render(). I set a breakpoint here and re-request to see if the program actually reaches this point

Fortunately, the program follows the path I guessed. Next, the program calls renderEmailSubject

Next, the program calls DefaultVelocityTemplatingEngine.render(this.subjectTemplate)

To DefaultVelocityTemplatingEngine.applying and DefaultVelocityTemplatingEngine.asPlainText

Continue to call asPlainText(Writer writer)

To toWriterImpl because the writer I passed in is a Fragment, so the program jumps to else
private void toWriterImpl(Writer writer, boolean attachCartridge) throws IOException {
if (this.source instanceof File) {
File template = (File)this.source;
if (attachCartridge) {
this.context.attachEventCartridge(DefaultVelocityTemplatingEngine.this.createDefaultCartridge());
}
DefaultVelocityTemplatingEngine.this.velocityManager.writeEncodedBody(writer, template.getPath(), "", DefaultVelocityTemplatingEngine.this.applicationProperties.getEncoding(), this.context);
} else if (this.source instanceof Fragment) {
Fragment fragment = (Fragment)this.source;
if (attachCartridge) {
this.context.attachEventCartridge(DefaultVelocityTemplatingEngine.this.createDefaultCartridge());
}
DefaultVelocityTemplatingEngine.this.velocityManager.writeEncodedBodyForContent(writer, fragment.getContent(), this.context);
}