Details
-
Bug
-
Resolution: Fixed
-
Blocker
-
5.3-milestone-2
-
None
Description
ZDI-CAN-23994: XWiki.org XWiki SolrSearchMacros text Command Injection Remote Code Execution Vulnerability
– CVSS -----------------------------------------
9.8: AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
– ABSTRACT -------------------------------------
Trend Micro's Zero Day Initiative has identified a vulnerability affecting the following products:
XWiki.org - XWiki
– VULNERABILITY DETAILS ------------------------
- Version tested: 16.2.0
- Installer file: hxxps://www.xwiki.org/xwiki/bin/view/Download/
- Platform tested: Linux
—
-
-
- Analysis
-
Overview
XWiki.org XWiki does not properly validate the "text" parameter. This parameter is used to create and execute a Solr query on the application's database. When requested with the "media" parameter equal to "rss", the "text" parameter is then rendered as part of the resulting RSS feed's title and description.
A remote, unauthenticated attacker could exploit this vulnerability to run arbitrary commands in the target server.
------------------------------------------------------------------------------
Product Information
XWiki.org 16.2.0 (15.10.8 is also vulnerable from testing)
------------------------------------------------------------------------------
Detailed description
When an HTTP GET or POST request is sent to the Request-URI "/xwiki/bin/get/Main/Search" or "/xwiki/bin/get/Main/Search", the Main.Search document is loaded and rendered as a template, interpreting code written in the template utilzling XWiki's scripting feature set. In default installations of the application, the Main.Search template calls the Solr Search UI Extension, leading to the rendering of the Main.SolrSearch, Main.SolrSearchMacros, and Main.SolrSearchConfig templates in the server's response.
In the Main.SolrSearchMacros template, a search is performed utilizing the "text" Request parameter as the query string. If the "media" Request paramater is equal to "rss", a com.rometools.rome.feed.synd.SyndFeedImpl object, representing the RSS feed, is populated with the search results alongside other metadata such as a title and description. Both the title and description are set utilizing the "text" Request parameter's value without the sanitization of the scripting language's special characters. The RSS feed is subsequently written back into the template's contents through the com.xpn.xwiki.plugin.feed.FeedPluginApi.getFeedOutput() function, which similary lacks a form of sanitization. As such, if the contents of the "text" Request parameter contains script instructions, the template renderer would proceed to interpret and run these instructions.
------------------------------------------------------------------------------
Code Flow
HTTP GET and POST requests to the Request-URIs "/xwiki/bin/get/Main/Search" or "/xwiki/bin/view/Main/Search" result in the rendering of the Main.Search template, stored in the Search.xml file in /data/extension/repository/org%2Exwiki%2Eplatform%3Axwiki-platform-search-ui/16%2E2%2E0/org%2Exwiki%2Eplatform%3Axwiki-platform-search-ui-16%2E2%2E0.xar which then calls the "handleSolrSearchRequest" macro from Main.SolrSearchMacros
```
<content>include reference="Main.SolrSearchConfig" /
include reference="Main.SolrSearchMacros" /
velocity
#handleSolrSearchRequest
/velocity</content>
```
The Main.SolrSearchMacros template, stored in the SolrSearchMacros.xml file in /data/extension/repository/org%2Exwiki%2Eplatform%3Axwiki-platform-search-solr-ui/16%2E2%2E0/org%2Exwiki%2Eplatform%3Axwiki-platform-search-solr-ui-16%2E2%2E0.xar utilizes the "text" parameter file
```
-
- Perform Solr query utilizing user provided "text" parameter
#macro (getSearchResults)
#set ($queryString = "$!{text}")
## - Create the query and set the query string.
#set ($query = $services.query.createQuery($queryString, 'solr'))
## - Set query parameters.
#set ($discard = $query.setLimit($rows))
#set ($discard = $query.setOffset($start))
#set ($discard = $query.bindValue('sort', "${sort} ${sortOrder}"))
#set ($discard = $query.bindValue('tie', $solrConfig.tieBreaker))
#set ($discard = $query.bindValue('mm', $solrConfig.minShouldMatch))
#setQueryFields($query)
#setPhraseFields($query)
#setFacetFields($query)
#setFilterQuery($query)
#setHighlightQuery($query)
#if ($debug)
#set ($discard = $query.bindValue('debugQuery', 'on'))
#end
## - Execute the query.
#set ($searchResponse = $query.execute()[0])
#end
- Perform Solr query utilizing user provided "text" parameter
-
- Process request parameters
#macro (processRequestParameters) - Initialize $text to be the "text" Request parameter
#set ($text = "$!request.text")
#set ($boost = "$!request.boost")
#set ($debug = "$!request.debug" != '')
- Process request parameters
#end
-
- Perform search and return result as RSS Feed
#macro (outputRSSFeed)
## - Get the search results.
##
#processRequestParameters()
#getSearchResults()
#set ($list = [])
#set ($results = $searchResponse.results)
#foreach ($searchResult in $results)
#set ($searchResultDocumentReference = $services.solr.resolveDocument($searchResult))
#set ($discard = $list.add("$searchResultDocumentReference"))
#end
## - Compute the feed URI.
##
#set ($parameters = {})
#set ($discard = $parameters.putAll($request.getParameterMap()))
#set ($discard = $parameters.remove('outputSyntax'))
#set ($discard = $parameters.remove('media'))
#set ($feedURI = $doc.getExternalURL('view', $escapetool.url($parameters)))
## - Configure the feed.
##
#set ($feed = $xwiki.feed.getDocumentFeed($list, {}))
#set ($discard = $feed.setLink($feedURI))
#set ($discard = $feed.setUri($feedURI))
#set ($discard = $feed.setAuthor('XWiki')) - $title is set to be the value of the "text" request parameter without additional sanitation
#set ($title = $services.localization.render('search.rss', ["[$text]"]))
#set ($discard = $feed.setTitle($title))
#set ($discard = $feed.setDescription($title))
#set ($discard = $feed.setLanguage("$xcontext.locale"))
#set ($discard = $feed.setCopyright($xwiki.getXWikiPreference('copyright')))
## - Output the feed.
##
#set ($discard = $response.setContentType('application/rss+xml')) - The raw feed output is printed onto the template page without additional sanitation
$xwiki.feed.getFeedOutput($feed, 'rss_2.0')
#end
- Perform search and return result as RSS Feed
#macro (handleSolrSearchRequest)
-
- Preselect facet values only for the facets that are enabled.
#set ($discard = $solrConfig.facetQuery.keySet().retainAll($solrConfig.facetFields))
#if ($request.media == 'rss')
#outputRSSFeed()
#elseif ("$!request.r" == '1' || $solrConfig.facetQuery.isEmpty())
#displaySearchUI()
#else - Redirect using preselected facet values.
#set ($extraParams = {})
#foreach ($entry in $solrConfig.facetQuery.entrySet())
#set ($discard = $extraParams.put("f_$entry.key", $entry.value))
#end - Prevent redirect loop.
#set ($extraParams.r = 1)
#extendQueryString($url $extraParams)
$response.sendRedirect($url)
#end
#end
```
When the com.xpn.xwiki.plugin.feed.FeedPluginApi.getFeedOutput() function is called from Main.SolrSearchMacros's outputRSSFeed macro, the contents of the RSS feed is returned as a string without performing sanitation on the scripting language's special characters
```
public String getFeedOutput(List<Object> list, SyndEntrySourceApi sourceApi, Map<String, Object> sourceParams, Map<String, Object> metadata, String type) {
this.getXWikiContext().remove("FeedPluginException");
- Preselect facet values only for the facets that are enabled.
try
{ // Calls com.xpn.xwiki.plugin.feed.FeedPlugin.getFeedOutput() return ((FeedPlugin)this.getProtectedPlugin()).getFeedOutput(list, sourceApi.getSyndEntrySource(), sourceParams, metadata, type, this.getXWikiContext()); }catch (XWikiException var7)
{ this.getXWikiContext().put("FeedPluginException", var7); return null; }}
```
Proof of concept instructions
The poc.py script can be run as follows:
python poc.py client <host> [-p <port>] [-c <command>]
where <host> is the host and <port> is the port (default:8080) running XWIki. The optional -c argument can be used to pass a custom shell command payload to the vulnerable endpoint (default: "touch /tmp/poc.txt").
Upon running the script, the server's response will be printed, displaying the status of the process run to execute the shell command. Additionally, the file "poc.txt" will have been created in the "/tmp/" folder of the server running XWiki.
------------------------------------------------------------------------------
Software download link
https://www.xwiki.org/xwiki/bin/view/Download/
– CREDIT ---------------------------------------
This vulnerability was discovered by:
John Kwak of Trend Micro
– FURTHER DETAILS ------------------------------
Supporting files:
If supporting files were contained with this report they are provided within a password protected ZIP file. The password is the ZDI candidate number in the form: ZDI-CAN-XXXX where XXXX is the ID number.
Please confirm receipt of this report. We expect all vendors to remediate ZDI vulnerabilities within 120 days of the reported date. If you are ready to release a patch at any point leading up to the deadline, please coordinate with us so that we may release our advisory detailing the issue. If the 120-day deadline is reached and no patch has been made available we will release a limited public advisory with our own mitigations, so that the public can protect themselves in the absence of a patch. Please keep us updated regarding the status of this issue and feel free to contact us at any time:
Zero Day Initiative
zdi-disclosures@trendmicro.com
The PGP key used for all ZDI vendor communications is available from:
http://www.zerodayinitiative.com/documents/disclosures-pgp-key.asc
– INFORMATION ABOUT THE ZDI --------------------
Established by TippingPoint and acquired by Trend Micro, the Zero Day Initiative (ZDI) neither re-sells vulnerability details nor exploit code. Instead, upon notifying the affected product vendor, the ZDI provides its Trend Micro TippingPoint customers with zero day protection through its intrusion prevention technology. Explicit details regarding the specifics of the vulnerability are not exposed to any parties until an official vendor patch is publicly available.
Please contact us for further details or refer to:
http://www.zerodayinitiative.com
– DISCLOSURE POLICY ----------------------------
Our vulnerability disclosure policy is available online at:
http://www.zerodayinitiative.com/advisories/disclosure_policy/