Aug 18, 2016
PermaNulled

OSSEC let me down…

First let me explain what OSSEC is and why a vulnerability in this system is important.

OSSEC is an host based open-source intrusion detection system…

Most recently there’s been a few vulnerabilities found and disclosed in it that have gotten rather concerning to me
http://ossec.github.io/blog/posts/2014-09-09-cve-2014-5284-fix.markdown.html
http://ossec.github.io/blog/posts/2015-06-11-cve-2015-3222-fix.markdown.html

These become a larger issue when the vulnerability I’ve found requires you to have access to the agent at a level where you can modify the configuration file, I consider what I’ve found to be slightly more severe in larger environments because depending on the configuration of the server system it could allow a full-scale breach instead of a single agent being compromised.

In theory once someone was to exploit/hack or gain access to an agent in any way the only thing you’re concerned with is that agent where the SQL injection takes place in the central server where the agents report to, in some cases this central server is within a corporate network that’s meant to be segregated from the rest of the agents… in theory once one was to compromise the central server of something like this access to additional systems or all of the systems/agents is to follow…

The idea of someone gaining access to an entire enterprise network, but because the system itself is meant to detect intrusions… with the SQL injection that I’ve discovered it would be possible to wipe evidence and make sure people weren’t able to see it given they weren’t recording to email regardless I feel it nearly renders the system useless in a sense.

More concerning is that a lot of people are recommended to use this system post-failure of PCI audits ( Meaning consumer credit card data should be protected by a system with vulnerabilities in it ).

I’ve also since writing this article submitted a pull-request to attempt to fix the vulnerability mentioned in this article (https://github.com/ossec/ossec-hids/pull/923), and requested a CVE which I’ll add to this article once I have it.

All of that aside…
A few days ago I’m upgrading OSSEC on some machines and install a non-stable development/release candidate on one of the systems connecting back to my 2.8.3 (Latest stable/release instance) immediately I notice that my agent isn’t reporting…
I check logs and notice SQL syntax errors, now at first I think nothing of this, it’s strange but not alarming yet….
Then I notice what the syntax errors are actually being caused by and I find that I’ve just discovered an SQL Injection in OSSEC’s server system when using a database (Yes this includes Postgresql).

2016/08/17 12:52:03 ossec-dbd(5203): ERROR: Error executing query ‘SELECT id FROM location WHERE name = ‘thedefaced->netstat -tan |grep LISTEN |grep -v ‘127.0.0.1’ | sort’ AND server_id = ‘1’ LIMIT 1′. Error: ‘You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘127.0.0.1’ | sort’ AND server_id = ‘1’ LIMIT 1′ at line 1′.

That being said I’ve done some research on this and even attempted exploiting it to gain a proof of concept considering the latest release candidate ships with the configuration file that caused this ordeal in the first place I would assume that the latest version is patched against this, yet was not back ported to the latest stable release…

To start lets discuss what could happen with this,

Theoretically someone could gain full access to your server instance under specific conditions…

So far the only conditions I’ve been able to achieve this under is if I gave the OSSEC MySQL user permissions to write to file, and read from file.
Now given with this attack I already knew an area which was world readable and write-able and accessible via the web this attack is very staged and not something I would expect to see in the wild, and I would hope to not see OSSEC running under a root user or user with these kinds of permissions on MySQL.

Looking at the code in db_op.c, at both mysql_real_connect (line 187) with the parameter “CLIENT_MULTI_STATEMENTS” not being set and mysql_set_server_option not existing we can easily determine that multi-statement or ‘stacked queries’ via exploitation will not be possible, meaning any data manipulation via stacking delete, insert, update, etc will be out of the question.

For more information about why stacked queries with MySQL were not possible read the MySQL C API Documentation on mysql_real_connect and mysql_set_server_option without “CLIENT_MULTI_STATEMENTS” being set via either of these functions it’s not possible with mysql_query (http://dev.mysql.com/doc/refman/5.6/en/c-api-multiple-queries.html)

PostgreSQL however while not tested I can assume that due to the fact PQExec (line 360, db_op.c) is in use to execute the queries that stacking queries would be possible for a PostgreSQL setup,

https://www.postgresql.org/docs/9.1/static/libpq-exec.html

int postgresql_osdb_query_select(void *db_conn, char *query)
{
int result_int = 0;
PGresult *result;

result = PQexec(db_conn,query);

With such one could also assume that data manipulation (Deleting, Updating, and Inserting) would be completely possible, beyond that using “Copy” one could then write files to the system and with such possibly gain code execution if they knew enough about a web-server running and if it supported whatever language/file was being output into.

So given certain circumstances I would consider the vulnerability moderate to critical, the issue being that it can be difficult to exploit and the attacker needs specific details about the remote system to gain any sort of access not to mention the entire attack would be done blindly and requires the user to have complete access to one of the agents.

Similarly MySQL queries are processed without being sanitized in it’s wrapper either just like postgresql_osdb_query_select, via (line 241, db_op.c) mysql_osdb_query_select.

int mysql_osdb_query_select(void *db_conn, char *query)
{
int result_int = 0;
MYSQL_RES *result_data;
MYSQL_ROW result_row;


/* Sending the query. It can not fail. */
if(mysql_query(db_conn, query) != 0)
{

From here both commands are wrapped into “osdb_query” and they’re set dynamically based on a few things such as if DB is enabled at all or which DB specifically has been enabled upon installation/compilation, there is no additional processing done before this point and I should also note that with the _select the _insert is also vulnerable to the same attack and does not sanitize the query before execution either.

The issue I’ve personally noticed so far takes place in alert.c, and happens in several places what I’ve specifically exploited so far was the “location” portion of the system, looking at other database queries it seems most of which are sanitized via “osdb_escapestr” I’ll elaborate more on that later and what I specifically discovered was sanitized.

So on to what I’m actually attacking looking at the code it’s plain and simply obvious that it’s injectable and I didn’t take a step back further to see if it there was any attempts to sanitize but here we are at “__DBSelectLocation” – alert.c ( line 53 ) and “__DBInsertLocation” – alert.c (line 79) where the parameter “location” is based as a character array which gets passed directly here with no additional processing other then adding the hostname of the agent, the calls are made from “OS_Alert_InsertDB” where “al_data->location” is the passed parameter lines (115, 118, 119) are all sent “al_data->location” with no additional processing while at line 108 “al_data->user” is sanitized via “osdb_strescape” and 152 “fulllog” is sanitized the same way.

Seeing that “al_data->user” and “fulllog” were sanitized was actually relieving because if these points were injectable it would’ve allowed someone to use a username via a service like vsftpd or even protocol mismatch logs in SSHD to trigger an injection in the server from the agent without ever actually needing any type of access… while the attack I’m describing still requires not only local access but access to re-configure and restart an agent.
I’ve attached the vulnerable lines of code below, or a point rather where the data could’ve been escaped before being sent to osdb_query and I believe it would’ve been a suitable workaround.

int __DBSelectLocation(char *location, DBConfig *db_config)
{
int result = 0;
char sql_query[OS_SIZE_1024];

memset(sql_query, '\0', OS_SIZE_1024);


/* Generating SQL */
snprintf(sql_query, OS_SIZE_1024 -1,
"SELECT id FROM "
"location WHERE name = '%s' AND server_id = '%d' "
"LIMIT 1",
location, db_config->server_id); // location is pushed into the sql_query buffer without being sanitized.


/* Checking return code. */
result = osdb_query_select(db_config->conn, sql_query); // osdb_query_select is passed to with no additional processing of sql_query.

return(result);
}
int __DBInsertLocation(char *location, DBConfig *db_config)
{
char sql_query[OS_SIZE_1024];

memset(sql_query, '\0', OS_SIZE_1024);

/* Generating SQL */
snprintf(sql_query, OS_SIZE_1024 -1,
"INSERT INTO "
"location(server_id, name) "
"VALUES ('%u', '%s')",
db_config->server_id, location); // Location parameter is pushed into the sql_query buffer without being sanitized.


/* Checking return code. */
if(!osdb_query_insert(db_config->conn, sql_query)) // sql_query buffer is then pushed to osdb_query_insert where we already know no additional processing happens.
{
merror(DB_GENERROR, ARGV0);
}

return(0);
}

Any additional auditing I’ve done has revealed that the “al_data->location” is the only SQL injectable point in the 2.8.3 build,
Now onto the actual triggering of the injection…

It’s actually quite simple,
In the configuration file for an agent there is a “command” specifier that can be used and this is the “location” where the output of such would be the “fulllog” variable,
The default command is something along the lines of “netstat -tan |grep LISTEN”.

The caveat with exploitation is the fact the command must remain valid or produce an output (Worth noting you could re-compile the agent or write your own communication library to send the data to the server, avoiding several caveats using the configuration file like <? being parsed as XML and giving you an bad config.)…

The solution to the caveat?, Well I actually just piggy backed off an already existing command in the Release candidate which was “netstat -tan|grep LISTEN|grep -v ‘127.0.0.1’” where the injection initially takes place at ‘127.0.0.1’ meaning that 127.0.0.1 is now being added to our query.

Any form of commenting I had attempted failed, and null characters I don’t believe would work due to the fact al_data->location or location later on are being pushed to the buffer using snprintf which would terminate at the null regardless and not actually copy it into the query string cause it to terminate early.

The first approach I took was confirming that my command was valid before passing it to the system,

[email protected]:/var/ossec/logs# netstat -tan|grep LISTEN |grep -v ‘UNION ALL SELECT 1,2’
tcp        0      0 0.0.0.0:XXXX            0.0.0.0:*               LISTEN

Now that I knew my command was valid I applied it to the configuration file,

<localfile>
<log_format>full_command</log_format>
<command>netstat -tan |grep LISTEN |grep -v ‘UNION ALL SELECT 1,2′</command>
</localfile>

I was no longer producing SQL errors on the first select statement while I was still generating errors on the INSERT but this is ok, because I only needed to inject the select…

From here I was able to use into outfile to produce a PHP script on the server in a world-writeable folder and gain a shell on the system I was attempting to exploit as a PoC.

The workaround currently would be upgrading the to the source version where I’ve supplied a patch for the vulnerability, https://github.com/ossec/ossec-hids/commit/2145db53718d1c8f986e6c88e8765b2660754286

 

 

Leave a comment