破解旧机顶盒,随手挖漏洞

#

1. 机顶盒信息

#

家里换了宽带,闲置了一个IPTV的安卓机顶盒。

淘宝全新的 8+64G 30块,这个 1+8G 估计10块钱都卖不上?!

1.1 机顶盒的硬件信息

#

生产厂家:九州电子

产品名称:PTV-8608 4K超高清IP机顶盒

产品概述:采用国科微GK6323主芯片,该芯片集成四核64位Cortex A53处理器及四核高性能3D GPU;支持4KP60 HDR解码,支持HDMI 2.0 ,支持1个FE内置PHY,支持1路USB,支持1路AV等。

1.2 技术规格

#

国科微GK6323V100A主芯片。

芯片集成四核64位Cortex A53处理器及四核高性能3D GPU。

支持Android4.4智能操作系统。

支持DLNA和Miracast多屏互动相关协议。

内置2.4G WiFi软路由,支持IEEE 802.11 b/g/n。

支持全4K功能。

支持HDR解码。

支持4KP60,支持各种主流音视频格式,支持4KP60,超高清音视频解码播放。

USB2.0接口,支持移动硬盘,鼠标和键盘。

支持Android4.4智能操作系统。

支持全4K业务部署,满足视频通信、卡拉OK、云游戏、多屏互动等增值业务需求。

支持H.265视频编码。

支持HDMI2.0。

内置标准蓝牙模块,支持蓝牙遥控器。

现在的情况是能正常开机进入桌面,但是帐号已经注销了,IPTV的业务用不了:

2. 获取系统控制权

#

机顶盒的系统是深度定制的安卓系统,安卓原生的设置调不出来,默认给的设置能做的非常有限:

插上U盘没有显示,也不能自由的安装apk。

我们首先要做的就是获取系统控制权,包括但不限于:

adb调试

root shell

TTL调试

其它

2.1 获取adb

#

信息收集:

在网上找了一下,没有找到可以用的卡刷固件。

一些唤出隐藏设置的后门,比如在遥控器上输入特定密码,或者连续按键,都没有效果。

插上U盘,也不识别U盘里面的apk文件。

检索关键字GK6323V100A,全网只找到一篇帖子:

https://dmm.ink/2024/12/01/apg0100gk6323v100a/

帖子里面的主板跟我的主板型号相同,板子的细节有出入,可能产品出厂又做了调整。

红框里面的TTL调试针脚,帖子的主板没有,我的主板上是全的。

TTL调试模块:

下单了一个TTL转USB的模块,由于我的主板TTL针脚都在,电烙铁就下次再买。

AI辅助:

我自号C/C++程序员,搞系统开发/安全开发,软件写得多,搞硬件还是第一次。

全程用DeepSeek做信息检索。DeepSeek访问人太多了,服务经常报错。

主观用下来的感受,DeepSeek的中文交互理解能力比chatGBT强一些。

接入TTL串口调试:

TTL调试中RX是接收数据,TX是发送数据,GND是共地,接入方法是交叉连接RX和TX线,即模块TX接主板的RX,模块RX接主板的TX,剩下的GND对接。

我这里用的终端工具是MobaXterm,选择串口,设置波特率为115200。

加电开机直接跑码成功,获取的就是root shell。

开启adb调试端口:

直接在终端里面执行:adbd &

adbd:注意这里运行是adb服务,所以是adbd。后面的d表示是一个系统服务(daemon),属于linux系统下约定熟成的一个命名方式,比如sshd、ntpd。

&:表示后台运行。

执行后续的操作之前,先把系统目录重新挂载为可读可写(非常关键):

adb mount -o remount,rw /

adb mount -o remount,rw /system

备注:某些命令执行失败,可以先执行一下su。

2.2 adb操作

#

查看系统信息:

adb shell getprop

adb shell getprop ro.build.version.release

连接/查看/屏幕/日志/重启/shell:

# 连接到设备

adb connect ip:port

# 断开连接

adb disconnect ip:port

# 查看设备

adb device

# 截屏(保存到设备)

adb shell screencap /sdcard/screenshot.png

# 录屏(需 Android 4.4+)

adb shell screenrecord /sdcard/video.mp4

# 实时查看日志

adb logcat

# 过滤特定标签的日志(如 "ActivityManager")

adb logcat -s ActivityManager

# 普通重启

adb reboot

# 进入 Bootloader 模式

adb reboot bootloader

# 进入 recovery 模式

adb reboot recovery

# 进入 adb shell

adb shell

文件上传/下载:

# 上传

adb push [本地文件路径] [设备目标路径]

# 下载

adb pull [设备文件路径] [电脑本地路径]

应用安装/卸载/禁用:

# 安装 APK

adb install app.apk

# 卸载应用(需包名,如 com.example.app)

adb uninstall com.example.app

# 列出已安装包名

adb shell pm list package

# 仅显示第三方应用(用户安装的APP)

adb shell pm list packages -3

# 仅显示系统应用

adb shell pm list packages -s

# 显示已启用的应用

adb shell pm list packages -e

# 显示被禁用的应用

adb shell pm list packages -d

# 显示包名及关联的APK路径

adb shell pm list packages -f

# 禁用用户应用

adb shell pm disable-user com.android.browser

# 禁用系统核心组件(谨慎操作)

adb shell pm disable com.google.android.gms/.update.SystemUpdateService

# 重新启用应用

adb shell pm enable <包名>

应用启动/停止:

通过包名启动主Activity,需要首先获取主Activity的名称:

# 手动启动一次应用,观察日志中的 Activity 名

adb logcat | grep "ActivityTaskManager: START"

# 使用 dumpsys 查看已安装包信息

adb shell dumpsys package <包名> | grep "MAIN"

启动应用:

# 示例:启动微信

adb shell am start -n com.tencent.mm/.ui.LauncherUI

强制停止应用:

adb shell am force-stop <包名>

终止进程(需谨慎),可能需要 root 权限:

adb shell am kill <包名>

运行桌面启动器:

adb shell am start -a android.intent.action.MAIN -c android.intent.category.HOME

2.3 添加sshserver

#

成功获取root shell,单纯启动adb服务还不够,还要再添加一个sshserver。

这里机顶盒的CPU架构是ARMv7,用C/C++搭一个交叉编译环境太费时间了,这里选择用Golang。

Golang的sshserver开源项目就用这个:reverse-ssh

项目直接用会有bug,需要针对安卓平台修改一下:

安卓的环境变量跟其它linux发行版不太一样,这里需要添加一个环境,指向命令的路径:

Makefile文件修改一下,一个是ssh连接密码,它这里原本是动态随机的,这里写死为root;另外一个是交叉编译命令,添加一条编译ARMv7架构的命令。

利用adb上传编译后的文件到/system/bin/目录,chmod赋予执行权限:

运行sshserver,-s指定shell的路径,&后台运行:

reverse-ssh-armv7 -s /system/bin/sh &

ssh客户端登录,默认端口是31337,密码是root:

2.4 添加开机启动

#

上面所有配置开机之后都会重置,需要添加开机启动。

可以修改的点:/system/bin/init.kunlun.sh,添加三条开机启动命令:

启动adbd

启动sshserver

重新挂载系统目录

系统bin目录没有vi命令,busybox有vi参数,执行busybox vi /system/bin/init.kunlun.sh修改文件:

3. 系统改造

#

经过上面的步骤,至此我们已经完全拥有了系统的控制权。

改造目标:

使用dd命令备份系统

更换系统启动器(Launcher)

精简系统组件,禁用系统更新,删除一些不需要的应用

机器是有wifi模块,系统只开放了无线热点共享,没有主动连接的功能。尝试修复一下

机顶盒作为瘦终端串流游戏主机

3.1 备份系统

#

开搞之前先把系统备份一下,这篇帖子 https://dmm.ink/2024/12/01/apg0100gk6323v100a/ 说没找到dd命令,实际上dd命令是有的,由busybox实现:

查看存储信息:

busybox df -h

cat /proc/partitions # 安卓设备查看块设备(如 /dev/block/mmcblk0)

常见分区参考:

分区名

路径示例

作用

boot

/dev/block/mmcblk0p5

内核和初始化程序

system

/dev/block/mmcblk0p16

系统主文件

vendor

/dev/block/mmcblk0p17

厂商驱动和配置

recovery

/dev/block/mmcblk0p6

恢复模式分区

userdata

/dev/block/mmcblk0p20

用户数据(可选择性备份)

dd备份系统分区:

busybox dd if=/dev/block/platform/goke_mci.0/by-name/system of=/mnt/sda/sda1/system.img bs=4096

if: 输入文件(分区路径)。

of: 输出文件(备份路径)。

bs: 块大小(可选,默认 512 字节)。

dd还原操作:

busybox dd if=/mnt/sda/sda1/system.img of=/dev/block/platform/goke_mci.0/by-name/system

3.2 更换系统桌面

#

原本想装Emotn UI启动器,Emotn UI最低需要安卓5.0,机顶盒系统版本是安卓4.4.2,这里装不上:

支持安卓4.4.2的Emotn UI旧版本安装包没有找到,我们换一下安装当贝桌面:

设置桌面启动器,在弹出来的选择框里设置始终以当贝桌面运行:

adb shell am start -a android.intent.action.MAIN -c android.intent.category.HOME

3.3 精简系统服务,冻结应用

#

冻结CATV_Auth默认桌面,当贝桌面就不需要切换了

精简其它系统应用:系统更新、其它杂项

3.4 设置桌面开机自启动

#

设置开机启动当贝桌面,在/system/etc/dtvlib_init.sh的最后添加两条命令:

sleep 10 # 等待系统初始化

adb shell am start com.dangbei.tvlauncher # 运行当贝桌面

完成的效果:

天气插件识别的地点有误,就不打码了,我在湖南但是并不在怀化:

3.5 解决wifi连接问题

#

重新安装一个WiFi连接管理器

4. 机顶盒作为瘦终端串流游戏主机

#

机顶盒设计的初衷就是用来进行直播串流的,原本是从直播服务器拉取视频流,现在是从本地局域网的游戏主机拉取视频流,作为瘦终端运行串流程序正当其用。

我这里用的串流工具是Moonlight,最低支持安卓4.1,机顶盒是安卓4.4,完全适用:

4.1 外设支持

#

作为瘦终端需要插外设,比如键盘、鼠标、手柄等等。

查看已连接的输入设备列表:

cat /proc/bus/input/devices

北通的手柄正常:

罗技的鼠标正常:

绿联拓展坞正常使用,普通的薄膜键盘正常:

备注:拓展坞即便接上辅助供电,机械键盘接上用不了。

4.2 游戏实测

#

在机顶盒安装Moonlight:

发现局域网的串流主机:

steam启动:

插上鼠标以后,鼠标指针变成这种大指针:

魂like/密药猎人/除客刀,怪猎肝不动了,dota排位好久不玩了,现在偶尔玩点战棋类的地图,比如自走棋:

平常用笔记本玩游戏,看的是小屏幕。在客厅的电视上串流,游戏体验又不一样了。

1080p 60帧的串流效果:

5. 网络安全/逆向分析

#

回归主线,搞点网络安全相关的东西。

5.1 获取恢复出厂设置密码

#

系统设置里面可以恢复出厂设置,密码在网上没有搜索到:

把文件/system/app/Hunan_Setting_IPTVsettings.apk提取出来:

jadx-gui解包,关键字定位,恢复出厂设置密码:96531

5.2 调出隐藏设置界面

#

这种机顶盒设备好像都有隐藏设置界面,通过特定的按键顺序唤出。

在系统设置界面显示隐藏界面

原本系统设置:

显示隐藏界面的按键顺序:上 → 下 → 左 → 右 → 音量下 → 音量下:

相关代码如下:

@Override // android.app.Activity, android.view.KeyEvent.Callback

public boolean onKeyDown(int keyCode, KeyEvent keyEvent) {

Log.d("SystemInfoActivity", "keyCode == " + keyCode + "--count_console==" + this.count_console + "keyStr==" + ((Object) this.keyStr) + "Factory_count=" + this.Factory_count);

if (keyCode == 19) {

this.keyStr.setLength(0);

this.keyStr.append("1");

}

if (keyCode == 20) {

this.keyStr.append("2");

}

if (keyCode == 21) {

this.keyStr.append(SettingsData.NETWORKTYPE_STATIC);

if (this.count_console == 4 || this.count_console == 5) {

this.count_console++;

if (this.count_console == 6) {

startconsole();

this.count_console = 0;

}

} else {

this.count_console = 0;

}

}

if (keyCode == 22) {

this.keyStr.append(SettingsData.NETWORKTYPE_DOUBLESTACK);

if (this.count_console == 2 || this.count_console == 3) {

this.count_console++;

} else {

this.count_console = 0;

}

}

if (keyCode == 24) {

this.keyStr.append("5");

if (this.count_console == 1) {

this.count_console++;

} else {

this.count_console = 0;

}

}

if (keyCode == 25) {

Log.v("SystemInfoActivity", "keyStr.toString()=" + this.keyStr.toString());

this.keyStr.append("6");

if (this.keyStr.toString().equals(this.keyStrEquals)) {

Intent i = new Intent(this, HindingSetting.class);

startActivity(i);

this.keyStr.setLength(0);

}

if (this.count_console == 0) {

this.count_console++;

} else {

this.count_console = 0;

}

}

if (keyCode == 16) {

if (this.Factory_count == 0) {

this.Factory_count++;

} else {

this.Factory_count = 0;

}

} else if (keyCode == 13) {

if (this.Factory_count == 1) {

this.Factory_count++;

} else {

this.Factory_count = 0;

}

} else if (keyCode == 12) {

if (this.Factory_count == 2) {

this.Factory_count++;

} else {

this.Factory_count = 0;

}

} else if (keyCode == 10) {

if (this.Factory_count == 3) {

this.Factory_count++;

} else {

this.Factory_count = 0;

}

} else if (keyCode == 8) {

if (this.Factory_count == 4) {

this.Factory_count = 0;

} else {

this.Factory_count = 0;

}

} else {

this.Factory_count = 0;

}

return super.onKeyDown(keyCode, keyEvent);

}

5.3 再随手挖一个命令注入漏洞

#

漏洞挖掘的突破口一般是找用户输入点,这个设备有两个用户输入点:

Ping

Traceroute

命令注入生效的只有一个,就是Ping。

Traceroute会把输入的域名先解析成ip地址,所以Traceroute这里注入不了。

Ping功能命令注入的poc非常简单,就一条命令:

执行效果:

命令注入调用栈:

接收输入(Java层)-> 拼接字符串pingHost_start(Java层)-> 调用executeCommand1(Java层)-> 调用executeCommand(C/C++层)-> 发送到系统服务RootShellServer执行命令

(1)NetworkPingActivity:接收输入域名

(2)NetworkPingActivity:拼接命令

(3)CommonUtils:调用Java方法executeCommand1

(4)CommonUtils:调用libRootShell_jni.so导出函数executeCommand

(5)libRootShellService.so导出函数android::RootShellService::executeCommand

6. 参考文档

#

全程使用DeepSeek作为辅助搜索

(湖南广电)艾米格_蜗牛TV-APG0100_GK6323V100A_1+8_RTL8723DU


中国为什么会被称为“中国”呢?
香港回大陸什麼不能帶?