-
Bug
-
Resolution: Fixed
-
Highest
-
None
The JIRAIFrameAction action is vulnerable to reflected XSS when passing an unsanitized url parameter to the Jira-iframe.vm velocity template. Exploitation of the issue first requires a JIRA issue id with read-access.
File: src\main\Resources\Atlassian-plugin.xml
<action name="com.pyxis.greenhopper.jira.actions.JIRAIFrameAction" alias="JIRAIFrameAction"> ... <command name="genericIssueIFrame" alias="GenericIssueIFrame"> <view name="success">/templates/greenhopper/jira/jira-iframe/jira-iframe.vm</view> <view name="error">/templates/greenhopper/jira/boards/error.vm</view> </command> ... </action>
File: greenhopper\src\main\java\com\pyxis\greenhopper\jira\Actions\JIRAIFrameAction.java
JIRAIframeAction.java
package com.pyxis.greenhopper.jira.actions; import com.atlassian.jira.config.SubTaskManager; import com.atlassian.jira.issue.link.IssueLinkManager; import com.pyxis.greenhopper.GreenHopper; import com.pyxis.greenhopper.GreenHopperException; import com.pyxis.greenhopper.jira.license.GreenHopperLicenseManager; import com.pyxis.greenhopper.jira.util.JiraUtil; @SuppressWarnings("serial") public class JIRAIFrameAction extends BoardAction { private String url; private Long issueType; private long displayedTime; private boolean absoluteUrl; ... /** In this action the url is provided by the caller already. */ public String doGenericIssueIFrame() { try { if(JiraUtil.getIssue(getId()) == null) { throw new GreenHopperException("Issue not found", "gh.issue.notfound"); } } catch (Exception e) { addError("Issue not found", "Issue not found"); } try { if (url == null) { throw new GreenHopperException("URL not defined", "gh.issue.notfound"); } } catch (Exception e) { addError("URL not defined", "URL not defined"); } return hasAnyErrors() ? ERROR : SUCCESS; } ... public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public boolean isAbsoluteUrl() { return absoluteUrl; } public void setAbsoluteUrl(boolean absoluteUrl) { this.absoluteUrl = absoluteUrl; } }
File: greenhopper\src\main\resources\templates\greenhopper\jira\Jira-iframe\Jira-iframe.vm
Jira-iframe.vm
#disable_html_escaping() ##POSSIBLEXSS <script type="text/javascript"> Boards.dynAJSPopup(1050, 620); Boards.AJSpopup.addHeader("$action.getText('gh.boards.iframe')"); AJS.$('.aui-dialog').addClass('gh-dialog gh-dialog-iframe'); Boards.AJSpopup.addPanel("", "panel1"); Boards.AJSpopup.getCurrentPanel().html(AJS.$("#jira-iframe")); Boards.AJSpopup.addCloseAction('$action.getText('gh.boards.close')', '${action.modifierKey}'); Boards.AJSpopup.cancelCallback = function() { Boards.closeIFrame('$action.issueObject.id', '$action.displayedTime'); ## don't close the popup yet return true; } ## set the src here, as otherwise a stale request is issued for the non-visible html added to the dom AJS.$("iframe", Boards.AJSpopup.popup.element).attr('src', '#if(!$action.absoluteUrl)${requestContext.baseUrl}/#end$action.url'); Boards.showAJSPopup(0, 0); </script> <div style="display:none;"> <div id="jira-iframe" style="height:100%;"> <div class="body" style="height:100%;"> <div class="iframecontainer" style="height:100%;"> <iframe id="iframe" name="iframe" style="height:100%;width:100%;"></iframe> </div> </div> </div><!-- // \#jira-iframe--> </div>
An example URL that will exploit the issue:
http://10.211.55.9:8082/secure/GenericIssueIFrame.jspa?url=');</script><script>alert(document.cookie);</script>&absoluteUrl=true&id=10028
Screenshot of the XSS attack:
Just check out classic mode. user preferences et al.