The arbitrary read and arbitrary write from the Lua vulnerability makes it quite easy to patch the vulnerability itself. I have written a proof of concept that should work on OSX Yosemite with any of the Homebrew bottled Redis versions that are vulnerable. It should also work on Mavericks and other compiled versions of Redis but I have not tested it on them (on versions other than Yosemite it may crash if the format of the long jump buffer is not the same). The Hot Patcher looks for the instruction cmp r15, 0x1b and replaces it with ‘cmpq rsp,0x1b’. This comparison should never be true because the stack typically lives around 0x7fxxxxxxxxxxxxxx and will never reach such a low address. However, looking for this exact instruction makes the patcher quite brittle and it may not work on Redis versions compiled with different compilers.

The Hot Patcher proof of concept uses the CSRF technique to run so you can run it directly from your browser. However, before running it you should understand:

it may crash your Redis server and you may lose your data (i think the exploit used to crash when the shellcode was allocated across two pages and i was only mprotect’ing the bottom page. but if this wasn’t the cause of the crash then the exploit is still flakey. or there could still be bugs.)

it might not crash your Redis server but may silently corrupt the data in your Redis server

you MUST own the Redis server running on 127.0.0.1:6379

this is not a full fix for the Lua sandbox bypass and only disables the loading of bytecode

it does not permanently patch Redis and the patch will be reverted after a restart

it is probably better to write a script for GDB to do the live patching. But I don’t understand GDB scripting :(

It is best to run the Redis server from your terminal then you can see the output from the patcher. It should look something like:

It is possible to break out of the Lua sandbox in Redis and execute arbitrary
code. This vulnerability is not new and is heavily based on
Peter Cawley’s work with Lua bytecode
type confusion.

This shouldn’t pose a threat to users under the trusted Redis security model
where only trusted users can connect to the database. However, in real
deployments there could be databases that can be accessed by untrusted users.
The main deployments that are vulnerable are developers machines, places where
redis servers can be reached via SSRF attacks and cloud hosting.

Vulnerable Deployments

Developers Machines

Developers machines may be vulnerable because they bind Redis to all interfaces
which used to be the default listen directive in the Redis configuration.

Developers may also be vulnerable even if they bind to 127.0.0.1 because Redis
is effectively a HTTP server. The first mention of attacking Redis via HTTP I
could find is by Nicolas Grégoire
where he documents attacking a Redis server on a Facebook property using a SSRF
vulnerability.

Because Redis is a HTTP server the same origin policies of browsers will allow
any website on the internet to send a POST request to it. When using XHR the
body is completely controllable. For example if you run the following in the
console of your webbrowser while running wireshark:

The attacker is not able to read the response from the server because of the
same origin policy. However, this might be worked around by using a DNS rebinding attack.
Even with DNS rebinding it might not be possible to read the response because
the response is not valid HTTP.

However, reading the response is not necessary because you can package a super generic
exploit that checks the result of the redis.call(“INFO”) command and then
launches a OS/architecture specific payload.

SSRF attacks

This is similar to attacking developers except a trusted server is tricked into
making a request to the Redis server However, you need a lot of control over
the body which might not often be possible depending on how the body is encoded.

Redis Cloud Hosting

This will only effect providers where people running arbitrary code from the
Redis process is not part of their threat model. The major players in this area
look like they are using sandboxing. For example the pids returned by ‘INFO’ on
heroku are very low <10 which indicates they are running the Redis servers in
containers. You can already run arbitrary code in containers via dynos on Heroku
so running arbitrary code in a Redis container is probably not useful for an
attacker. Amazon Elasticcache also looks like it uses linux containers.

Similarily, it looks like Microsoft’s hosted Redis solution runs in an
isolated VM. Redis ‘INFO’ returns a virtual os string and it takes ~15
minutes to launch an instance. If MS aren’t running in an isolated VM then the
15 minute startup time is very weird.

This will be a problem if a hosting provider runs a whole bunch of redis
processes on the same machine/same VM from different customers without any
kind of isolation.

Exploit

Peter Cawley has found that the
loadstring function can be used to load bytecode that is unsafe. He has
created three very useful lua exploit primitives that make exploitation easy.

First is a way of reading the Value contained in a TValue struct. This allows
reading the pointer value from a lua tagged value. Some pointer values are
already public (using tostring) but there doesn’t seem to be a way to get the
pointer value for a lua string so this is useful.

Second is a way of reading 8 bytes from an arbitrary memory address.

Third is a way of writing 8 bytes to an arbitrary memory address.

Using the arbitrary memory read it is possible to leak the address of a known
C function. From the address of this c-function it is possible to find the base
address of the redis-server binary. From this base address it is possible to
find pointers to libc/libsystem_c functions and to find the base address of the
libc/libsystem_c libraries. From these libraries it is possible to find the
addresses of useful exported functions (longjump/system) and ROP gadgets. This
technique is similar to pwntools dynelf

The arbitrary memory read is also used to leak an address inside the stack. The
lua_State object holds a long_jump variable that references a long_jump buffer
that is allocated on the stack. This leaks the stack address which can be useful
if you just want to corrupt the stack or the rsp and rip can be overwritten in
the longjump buf to directly take control when longjump is called. OSX has no
pointer mangling protections so this is quite easy to corrupt.

On linux the rip and rsp (and rbp) values are mangled. However, if you have full
read access to the memory you can reverse the secret cookie value to corrupt
the values. Also, linux prevents you from longjmp’ing to an invalid stack frame
(ie: the heap) but you can longjump to point the stack inside the longjump
buffer then pivot the stack into the heap. This is not really necessary if you
don’t care about corrupting the stack and crashing the redis process but if you
longjump and don’t corrupt the stack then you can resume normal execution of
redis after the exploit has finished running.

Exploitability

I have exploits for Linux 64 bit and OSX 64 bit. Both exploits take care to not
crash the redis server during successful execution. They will make a call to
system() then go back to normal redis execution.

I have run the Linux exploit on the Amazon RHEL Image (PIE enabled) and the
Amazon 14.04 Ubuntu Image (no PIE). I believe the exploit will work on most
modern Linux 64 bit systems (I suspect it will not work if you compile libc with
fomit-frame-pointer but this can be worked around). It does not use any
hardcoded addresses from libc or the Redis binary.

The OSX version I have only tested on Yosemite but an earlier version was
working on Mavericks and I believe the Yosemite version works on both. This has
been tested with two different Redis versions and similarily does not depend on
hardcoded address from libsystem_c or the Redis binary. However, it uses
addresses from libsystem_c to speed up the exploit.

Workarounds

The best option is to set a strong password on Redis. Systems that are reachable
via HTTP without a password are a problem waiting to happen.

It is also possible to rename the EVAL command. If you are not using EVAL this
is a good option but you still might be at risk of someone modifying your Redis
data via HTTP SSRF attacks.

Upgrading to Redis 2.8.21 and 3.0.2
will also fix this issue but I still strongly recommend using password
authentication on Redis systems.

Be careful with Riak HTTP API (CVE-2012-3586)

I would recommend not running the Riak HTTP API on a machine that you browse the internet on or on a machine that is reachable by machines that can browse the internet.

This is heavily based on Aphyr’s work. I’ve taken his work and used it in a cross site scripting attack. When you click the attack me button your riak process will attempt to connect to localhost:6666. If you run nc -l 6666 and wait for a connection you will have a shell with the privileges of the user running riak.

The attack will perform the following actions

Write the value lols=lols to the key i_can_run_better in bucket everything_you_can_run

Evalute the contents of /tmp/evil.erl using the erlang function file:path_eval. This will cause your machine to try and open a connection to localhost:6666 that is backed by a shell running under the riak user.

By clicking ‘Hack Me’ you agree that you have reviewed the source code of this page and understand what the attack will do and will not hold the author of this page liable for any damage the attack may cause.

Most Rails applications don’t properly sanitise user input when passing it to queries (UPDATE: Rails has fixed the problems raised in this article so it was mostly a Rails problem rather than an application programmer problem). I’m going to use an example to illustrate this problem.

The Scenario

Johnny has been tasked to add a password reset feature to his Rails application. So he adds a reset_token to his User model and a PasswordsController class to the application. When the user forgets their password they type in their email and a reset_token is generated and saved on the User model and a url containing the reset token is sent to the users email address. The url looks like /users/1/passwords/edit?reset_token=kjksldjflskdjf. This reset token is then checked when the user resets their password. Johnny writes the following code in the PasswordsController:

Johnny deploys this new feature to the staging environment and Mary is given the task to test it. Now Mary is quite clever and checks what happens if she removes the reset_token parameter from the url and changes the user id. She visits the url /users/2/passwords/edit and finds that she can change the password for any user that has not requested their password to be reset. She raises this as a critical bug.

Johnny reproduces the problem on his machine and notices it is is doing the following query:

He realises he needs to stop users from not sending the reset_token parameter because if params[:reset_token] is nil then they can update any user who hasn’t requested a password reset. He updates the code in PasswordController to the following:

Mary tries her trick again but it doesn’t work this time. But Mary has more tricks in her bag and this time she uses the url /users/2/passwords/edit?reset_token[] . Again she is able to change the password for any user that has not had a reset_token generated.

Johnny reproduces the problem on his machine and sees it doing the same query:

Johnny is completely stumped as to how nil.blank? could be false. He adds some logging and finds the params[:reset_token] is actually an array containing a nil element: [nil]. He decides to fix the problem by calling to_s on the query parameters.

The user is able to change the token filter to a filter on a column of their choice. On previous versions of Rails this attack can be escalated
to arbitrary SQL injection. This attack uses the previously fixed issue of SQL injection in table names and columns. This bug was originally not
as serious because you would not normally let a user choose arbitrary columns or table names in a query. However, with the SQL Manipulation bug an
attacker is now able to change table and column names to perform SQL injection.

This Hash problem is actually a security bug in rails and the rails team has released a patch for it.

Underlying Problem

The problem is developers expect the user input to be a String but it can also be an Array or a Hash and Rails has quite different behaviour if a Hash or an Array is passed in. The Hash is particularly troubling because if you have a filter on column X then a user can change it to be a filter on column Y. Example:

Fixes

Rails has released 3.2.6 that fixes both the nil issue and SQL manipulation/injection problems with Hash.

Clearance has released a new version 0.6.13 which fixes the problem with nil parameters

Mitigation

It is recommended that you install the Rails patches to fix the Hash problem and nil problem. Also, with security sensitive code I strongly recommend all query
parameters be coerced to the type you expect them to be. For example if you expect a parameter to be a String you should call to_s on it.

Previous Work

The Devise team seem to have been aware of the general problem of users being able to send non-string parameters. They have a ParamFilter
class that forces all parameters to be Strings. It looks like they did this because they had an injection problem with mongoid.

# Force keys to be string to avoid injection on mongoid related database.defstringify_params(conditions)returnconditionsunlessconditions.is_a?(Hash)conditions.eachdo|k,v|conditions[k]=v.to_sifparam_requires_string_conversion?(v)endend

Stay Tuned

We only covered the issues fixed in 3.2.5 and 3.2.4 in this article. There was another variant of the Hash attack that was fixed in 3.2.6. I will cover
that in a future article and show how to exploit it.