Skip to main content
  1. Posts/

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

本文阅读量
·4 mins·
硬件 安卓 机顶盒 漏洞挖掘
udcode
Author
udcode
程序员A-DU
Table of Contents

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

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对接。

    Img

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

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

开启adb调试端口: 直接在终端里面执行:adbd &

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

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

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发行版不太一样,这里需要添加一个环境,指向命令的路径:
    Img
  • Makefile文件修改一下,一个是ssh连接密码,它这里原本是动态随机的,这里写死为root;另外一个是交叉编译命令,添加一条编译ARMv7架构的命令。
    Img

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

Img

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

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

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

Img

2.4 添加开机启动
#

  • 上面所有配置开机之后都会重置,需要添加开机启动。
  • 可以修改的点:/system/bin/init.kunlun.sh,添加三条开机启动命令:
    • 启动adbd
    • 启动sshserver
    • 重新挂载系统目录
  • 系统bin目录没有vi命令,busybox有vi参数,执行busybox vi /system/bin/init.kunlun.sh修改文件:
    Img

3. 系统改造
#

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

改造目标:

  • 使用dd命令备份系统
  • 更换系统启动器(Launcher)
  • 精简系统组件,禁用系统更新,删除一些不需要的应用
  • 机器是有wifi模块,系统只开放了无线热点共享,没有主动连接的功能。尝试修复一下
  • 机顶盒作为瘦终端串流游戏主机

3.1 备份系统
#

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

Img

查看存储信息:

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

Img

常见分区参考:

分区名 路径示例 作用
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 字节)。

Img

dd还原操作:

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

Img

3.2 更换系统桌面
#

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

Img
Img

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

Img

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

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

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

  • 冻结CATV_Auth默认桌面,当贝桌面就不需要切换了
  • 精简其它系统应用:系统更新、其它杂项
    Img

3.4 设置桌面开机自启动
#

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

sleep 10  # 等待系统初始化
adb shell am start com.dangbei.tvlauncher # 运行当贝桌面

Img

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

Img

3.5 解决wifi连接问题
#

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

Img

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

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

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

Img

4.1 外设支持
#

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

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

cat /proc/bus/input/devices

北通的手柄正常:

Img

罗技的鼠标正常:

Img

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

Img

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

4.2 游戏实测
#

在机顶盒安装Moonlight:

Img

发现局域网的串流主机:

Img

steam启动:

Img

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

Img

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

Img

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

Img

5. 网络安全/逆向分析
#

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

5.1 获取恢复出厂设置密码
#

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

Img

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

Img

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

Img

5.2 调出隐藏设置界面
#

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

在系统设置界面显示隐藏界面
原本系统设置:

Img

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

Img

相关代码如下:

@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
    Img

命令注入生效的只有一个,就是Ping。
Traceroute会把输入的域名先解析成ip地址,所以Traceroute这里注入不了。

Img

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

Img

执行效果:

Img

命令注入调用栈:

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

(1)NetworkPingActivity:接收输入域名

Img

(2)NetworkPingActivity:拼接命令

Img

(3)CommonUtils:调用Java方法executeCommand1

Img

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

Img

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

Img

6. 参考文档
#

Related

升级10年前的旧笔记本
·1 min
硬件 Laptop
hugo主题Blowfish添加busuanzi访问统计
·1 min
Web Hugo Blowfish Busuanzi