Exploit-DB #39194
Description: AVM FRITZ!Box < 6.30 - Remote Buffer Overflow
Type: remote
Platform: hardware
Date: 2016-01-07
Author: RedTeam Pentesting

Exploit:
Advisory: AVM FRITZ!Box: Remote Code Execution via Buffer Overflow

RedTeam Pentesting discovered that several models of the AVM FRITZ!Box
are vulnerable to a stack-based buffer overflow, which allows attackers
to execute arbitrary code on the device.


Details
=======

Product: AVM FRITZ!Box 3272/7272, 3370/3390/3490, 7312/7412,
                       7320/7330 (SL), 736x (SL) and 7490
Affected Versions: versions prior to 6.30 (all models) [0]
Fixed Versions: >= 6.30 (all models) [0]
Vulnerability Type: Buffer Overflow
Security Risk: high
Vendor URL: http://avm.de/
Vendor Status: fixed version released
Advisory URL: https://www.redteam-pentesting.de/advisories/rt-sa-2015-001
Advisory Status: published
CVE: GENERIC-MAP-NOMATCH
CVE URL: https://cve.mitre.org/cgi-bin/cvename.cgi?name=GENERIC-MAP-NOMATCH


Introduction
============

FRITZ!Box is the brand name of SOHO routers/CPEs manufactured by AVM
GmbH. The FRITZ!Box usually combines features such as an xDSL modem, a
wifi access point, routing, VoIP, NAS and DECT.


More Details
============

When examining the running processes on a FRITZ!Box, it was discovered
that the program dsl_control listens on TCP port 8080:

# netstat -anp | grep dsl_control
tcp   0   0 0.0.0.0:8080   0.0.0.0:*   LISTEN   849/dsl_control

By sending an HTTP request to the service, it can be seen in the
server's response that the daemon expects SOAP messages (output
shortened):

$ curl --silent http://fritz.box:8080/ | xmllint -format -
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope [...]>
  <SOAP-ENV:Body>
    <SOAP-ENV:Fault SOAP-ENV:encodingStyle="[...]">
      <faultcode>SOAP-ENV:Client</faultcode>
      <faultstring>HTTP GET method not implemented</faultstring>
    </SOAP-ENV:Fault>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

After examining the dsl_control binary by using GNU strings and
performing a web search for some of the resulting values, it was quickly
discovered that parts of the daemon's source code can be found in the
Git repository of the dd-wrt firmware[1].

In order to retrieve the list of all commands that are implemented by
the daemon, the following SOAP message can be sent to the server,
specifying an ifx:DslCpeCliAccess element containing an empty command
element (output shortened):

$ curl --silent http://fritz.box:8080/ --data '
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/[...]"
 xmlns:ifx="urn:dsl_api">
  <SOAP-ENV:Body>
      <ifx:DslCpeCliAccess>
          <command></command>
      </ifx:DslCpeCliAccess>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>' | xmllint -format -
<?xml version="1.0" encoding="UTF-8"?>
[...]
    <ifx:DslCpeCliAccessResponse>
      <result>avmcr, avmcrmr, avmcrms, avmcw, avmdsmmcs, avmhwrfit,
avmpet, avmvig, acog, acos, acs, alf, asecg, asecs, asg, aufg, alig,
bbsg, bpstg, bpsg, ccadbgmlg, ccadbgmls, dbgmlg, dbgmls, dsmcg, dsmcs,
dsmmcg, dsmmcs, dsmstatg, dsmsg, dsnrg, dmms, dms, esmcg, esmcs, fddg,
fdsg, fpsg, g997amdpfcg, g997amdpfcs, g997amlfcg, g997amlfcs, g997bang,
g997bansg, g997cdrtcg, g997cdrtcs, g997csg, g997dpfsg, g997dfr,
g997dhling, g997dhlinsg, g997dhlogg, g997dqlng, g997dsnrg, g997fpsg,
g997gang, g997gansg, g997lstg, g997lacg, g997lacs, g997lfsg, g997lisg,
g997lig, g997listrg, g997lis, g997lsg, g997lspbg, g997ltsg, g997lpmcg,
g997lpmcs, g997pmsft, g997pmsg, g997racg, g997racs, g997sang, g997sansg,
g997upbosg, g997xtusecg, g997xtusecs, g997xtusesg, help, hsdg, ics, isg,
lecg, lfcg, lfcs, lfsg, locg, locs, lsg, llsg, llcg, llcs, mlsg, nsecg,
nsecs, osg, pm15meet, pmbms, pmcc15mg, pmcc1dg, pmccsg, pmcctg,
pmchs15mg, pmchs1dg, pmct15mg, pmct15ms, pmct1dg, pmct1ds, pmcg, pmcs,
pmdpc15mg, pmdpc1dg, pmdpcsg, pmdpctg, pmdpfc15mg, pmdpfc1dg, pmdpfcsg,
pmdpfctg, pmdpfhs15mg, pmdpfhs1dg, pmdphs15mg, pmdphs1dg, pmdpt15mg,
pmdpt15ms, pmdpt1dg, pmdpt1ds, pmetr, pmlesc15mg, pmlesc1dg, pmlescsg,
pmlesctg, pmleshs15mg, pmleshs1dg, pmlic15mg, pmlic1dg, pmlicsg,
pmlictg, pmlihs15mg, pmlihs1dg, pmlit15mg, pmlit15ms, pmlit1dg,
pmlit1ds, pmlsc15mg, pmlsc1dg, pmlscsg, pmlsctg, pmlshs15mg, pmlshs1dg,
pmlst15mg, pmlst15ms, pmlst1dg, pmlst1ds, pmrtc15mg, pmrtc1dg, pmrtcsg,
pmrtctg, pmrths15mg, pmrths1dg, pmrtt15mg, pmrtt15ms, pmrtt1dg,
pmrtt1ds, pmr, pmsmg, pmsms, ptsg, quit, rtsg, rccg, rccs, rsss, rusg,
se, sicg, sics, sisg, tcpmistart, tcpmistop, tmcs, tmsg, vig, </result>
    </ifx:DslCpeCliAccessResponse>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

As can be seen in the listing, the server implements several commands.
Many of them can be accessed without any authentication. One of the
commands which was further examined is the 'se' or 'ScriptExecute'
command. It is defined by the file dsl_cpe_cli_access.c, which registers
the function DSL_CPE_CLI_ScriptExecute as the corresponding handler:

[...]
   DSL_CPE_CLI_CMD_ADD_COMM (
      "se",
      "ScriptExecute",
      DSL_CPE_CLI_ScriptExecute,
      g_sSe);
[...]

The following listing shows dd-wrt's implementation of the command,
which is also part of the file dsl_cpe_cli_access.c (shortened):

DSL_CLI_LOCAL DSL_int_t DSL_CPE_CLI_ScriptExecute(
   DSL_int_t fd,
   DSL_char_t *pCommands,
   DSL_CPE_File_t *out)
{
   DSL_int_t ret = 0;
   DSL_char_t sFileName[DSL_MAX_COMMAND_LINE_LENGTH] = {0};

   if (DSL_CPE_CLI_CheckParamNumber(pCommands, 1, DSL_CLI_EQUALS) ==
      DSL_FALSE)
   {
      return -1;
   }

   DSL_CPE_sscanf (pCommands, "%s", sFileName);

   [...]

   return 0;
}

As can be seen in the listing, the function first checks whether
another parameter is given by calling the function
DSL_CPE_CLI_CheckParamNumber(). If this is the case, the code proceeds
to call the function DSL_CPE_sscanf() in order to copy the value of the
parameter pCommands to the local char array sFileName. Because the
format string "%s" is provided to the DSL_CPE_sscanf() function, no
re