LeonOS Documentation

历史快照:本文记录当时布局;现行 rootfs 路径见 docs/ROOTFS_LAYOUT_AND_MIGRATION.md。

LeonOS 4 周期安全审计报告(2026-08-30)

范围与方法

本报告为周期性源码安全审计。严格执行以下准入规则:

字段值
模式Repository 静态源码审计 + 结构化回归检测
工作区路径/workspace(LeonOS 4 主仓库)
审计提交基线当前 HEAD(未使用固定 SHA,因本次为周期扫描)
检测工具人工源码追踪 + tools/test_security_regressions.py(只读)
运行状态未启动虚拟机、未发送网络帧、未做破坏性 PoC;所有结论均以可复核的源码路径为依据
覆盖路径kernel/ntclks/、middlelayer/osmlayer/、userland/libc/src/、userland/apps/browser/、drivers/bootstrap/storage/、userland/libc/src/tar.c、los2w/、buildsystem/、tools/
准入后存活项7 项(全部:中等严重度,高置信度,端到端利用链已在源码层论证)
同期已修复(不再存活)7 项(July Finding 1/2/4/5、August Finding 1、Net Connections 可见性隔离、stat() 授权从 EXEC 改为 READ)

与前两期报告的关系

威胁模型

信任边界:

  1. Ring-3 → Ring-0 syscall/ioctl:普通用户态任务通过 int $0x80 ABI 进入内核。
  2. 不受信任网络输入 → 内核协议栈 / libc HTTP 解析器 / 浏览器。
  3. 普通进程 → 桌面/系统服务 → 持久化配置与电源状态(混淆代理/权限代理)。
  4. 二层同网段恶意节点 → DHCP/ARP/TCP。
  5. 受保护 HTTPS 端点 → HTTP(协议降级)。

攻击者能力:

安全目标:


准入后存活发现

#标题严重性置信度攻击面
1电源控制 IOCTL 缺少授权检查(任意进程可触发内核级重启/关机)中等高本地任意用户进程 → 内核电源
2HTTPS → HTTP 降级重定向未被拒绝(下载与浏览传输完整性破坏)中等高远程 HTTPS 响应 → libc HTTP 客户端 → 明文
3HTTP chunked 解码 chunk_size 乘法回绕 + 加法型边界检查绕过导致响应缓冲区越界读取中等高远程 HTTP 响应 → 任意 HTTP 客户端进程内存
4DHCP ACK 未绑定已选择的 OFFER(同网段可抢 ACK 劫持 DNS/网关/租约)中等高二层同网段 → 内核 DHCP 状态机 → 全局 net_config
5浏览器 Basic 认证键不包含 URL 方案(HTTPS 凭据可发送到明文 HTTP)中等高浏览器 + HTTPS 登录 → 同主机端口 HTTP 导航
6ARP 缓存无条件存储入站 sender 映射(无需匹配未完成查询或链路一致性)中等高二层同网段 → 内核 ARP 缓存 → 中间人/DoS
7TCP RST 缺少接收窗口序号验证直接关闭已建立连接中等高同链路可伪造四元组的节点 → 内核连接表 → 会话中断

说明:本期未出现严重(Critical/High)严重度存活项。前一期严重发现「GUI 几何整数回绕内核越界写」(August Finding 1)在当前源码中已通过 gui_ipc_validate_surface_geometry + 显式尺寸上限 + 64 位 checked 乘 + 页数缩窄前 UINT32_MAX 检查被全面阻断,已验证不再满足准入。


发现详情(准入严格版)

[F1] 电源控制 IOCTL 缺少授权检查(任意进程可触发内核级重启/关机)

字段值
严重性中等(本地全系统 DoS,跨用户边界)
置信度高(直接源码控制流证据 + 邻近对比反例)
分类CWE-862(缺少授权)
受影响位置kernel/ntclks/syscall.c:3861-3867
邻近授权正例同文件 LEONOS_GUI_IOCTL_TASK_KILL(syscall.c:3804)调用 authz_check_path(..., LEONOS_AUTHZ_KILL_TASK, ...);LEONOS_IOCTL_NET_DHCP(syscall.c:4306)调用 require_network_config_access()

端到端利用路径(Source → Sink)

  1. 入口:任意可执行 int $0x80 / LeonOS libc 的 Ring-3 任务调用 LINUX_SYS_IOCTL,将 a1 设为 LEONOS_GUI_IOCTL_REBOOT(常量 0x4c524254)或 LEONOS_GUI_IOCTL_SHUTDOWN(0x4c534844)。a2 未被使用,无需有效用户指针。
  2. 分发:内核进入 syscall_dispatch_regs,匹配分支:
  1. 授权检查缺失:这两个分支直接调用平台电源例程;不经过 require_window_server()、require_network_config_access()、authz_check_path()、require_driver_management() 或任何 task_effective_role() 检查。对比文件内其它特权操作(TASK_KILL、DRIVER_CONTROL、NET_DHCP、FB_、DISPLAY_ 轮询/发布、POLL_WINDOW、SEND_WINDOW_EVENT)均有明确守卫,表明该系统调用层具备统一授权模式但未被电源分支采用。
  2. SINK:power_reboot()/power_shutdown() 触发平台级重启或关机(内核内部 platform_power_* 终端不可控)。

影响与可信边界

最小复现假设

修复建议


[F2] HTTPS → HTTP 降级重定向未被拒绝(下载与浏览传输完整性破坏)

字段值
严重性中等(破坏用户预期的传输完整性;可辅助安装包/更新包替换)
置信度高(静态调用链 + 位置值匹配)
分类CWE-319(明文传输敏感信息)、CWE-757(算法/协议降级攻击)
受影响位置userland/libc/src/libc.c:3253-3316(leonos_http_request 重定向循环)+ 浏览器重用该库的全部浏览/下载路径,以及 downloadmgr(userland/apps/downloadmgr/main.c:302 附近)

端到端利用路径(Source → Sink)

  1. 受害者触发:调用 leonos_http_request(libc HTTP 入口)或通过 leonos_http_get、浏览器、下载管理器发出 HTTPS 请求。current_url 初始为形如 https://example.com/package.zip 的 URL。
  2. HTTP fetch 完成:http_fetch_once(current_url, request, response, location, ...) 于 libc.c:3286 成功返回,响应状态满足重定向判断 http_is_redirect(response->http_status)(301/302/303/307/308),且 Location 头部被写入 location(长度上限 LEONOS_HTTP_URL_LEN)。
  3. 恶意 Location 构造:攻击者控制的 https://example.com/ 响应中写入 Location: http://example.com/package.zip。leonos_http_resolve_url(libc.c:2687)在 location_text 以 http:// 或 https:// 开头时直接原样拷贝到 next_url(2713-2716)。该函数没有方案安全性比较语义。
  4. 降级被无条件接受:返回 leonos_http_request 的重定向循环,代码只做了 URL 解析失败与重定向次数检查(libc.c:3300-3311),随后执行 http_copy_text(current_url, sizeof(current_url), next_url);(libc.c:3312)。
  5. SINK:循环的下一次迭代 http_fetch_once(current_url, ...) 发起的请求为明文 HTTP。TCP 连接建立后,链路攻击者(同网段、恶意热点、BGP 劫持、上游代理)即可查看和替换下载内容。

影响论证

修复建议

在 leonos_http_request 的重定向循环中(libc.c:3307-3312 之间),在跟随 next_url 之前:


[F3] HTTP chunked 解码 chunk_size 乘法回绕 + 加法型边界绕过导致响应缓冲区越界读取

字段值
严重性中等(用户态进程受控越界读取;可致崩溃与敏感内存读取)
置信度高(算术级回绕证明 + 源码位置)
分类CWE-190(整数回绕/溢出)、CWE-125(越界读取)
受影响位置userland/libc/src/libc.c:2894-2944(http_decode_chunked)

端到端利用路径(Source → Sink)

http_decode_chunked(buffer, body_offset, raw_len, capacity, flags) 的每一步均可被远程 HTTP 响应完全控制:

  1. 响应触发:只要 HTTP 响应的 Transfer-Encoding: chunked 被检测(libc.c:3227 http_contains_ignore_case(transfer_encoding, "chunked")),response->body_len = http_decode_chunked(...) 即被调用。所有通过 leonos_http_request/leonos_http_get 的 HTTPS/HTTP 客户端(浏览器、下载管理器、httpget.elf、ping 之外的联网工具)都到达该解码器。
  2. chunk_size 回绕:chunk_size 为 uint32_t(libc.c:2901)。解析循环:

while (src < raw_len && http_is_hex(buffer[src])) {
    chunk_size = chunk_size * 16U + http_hex_value(buffer[src]);  // line 2907
    ++src; saw_hex = 1;
}
  1. 边界检查失效:紧接着使用加法型上界 if (src + chunk_size > raw_len)(libc.c:2923)。当 src 接近缓冲区尾且 chunk_size 经回绕后变成可让 src + chunk_size 再次回绕为小值的量时,该比较为假,chunk_size 不会被截断为 raw_len - src。
  2. SINK 越界读取 + 前向写:随后循环 for (uint32_t i = 0; i < chunk_size; ++i) buffer[dst++] = buffer[src + i];(libc.c:2929-2937)从 buffer + src + i 读取,i 上限可达理论上非常大的回绕值。buffer 是调用者传入的响应体缓冲区(通常为 request->response_body,堆或栈上);src + i 超出 raw_len 后继续向后访问该缓冲区边界之外:

只要提交 ≥ 9 个十六进制字符(例如长度前缀 100000000 = 2^32,或更多 F),chunk_size * 16U 会对 UINT32_MAX + 1 模回绕,使 chunk_size 最终为一个远小于解析位数声明的小值。

  1. 返回值:dst 作为 response->body_len 返回给上层。上层 UI 或应用会按该长度消费 response_body,泄露的字节可被显示、写盘、再次通过网络回传等。

影响等级说明

修复建议


if (chunk_size64 > (UINT32_MAX - hex_val) / 16) return 0 /* or TRUNCATED */ ;
chunk_size64 = chunk_size64 * 16U + hex_val;
if (chunk_size64 > HTTP_CHUNK_MAX_ALLOWED) reject;

if (src > raw_len || chunk_size64 > raw_len - src) { truncated; break; }

[F4] DHCP ACK 未绑定已选择的 OFFER(同网段可抢 ACK 劫持全局配置)

字段值
严重性中等(可替换网关/DNS/租约地址 → 全系统中间人/拒绝服务)
置信度高(源码函数级 ACK 等待控制流)
分类CWE-345(身份/来源验证不足)
受影响位置kernel/ntclks/net.c:1837-1864(net_dhcp_wait)、1872-1922(net_dhcp_request)、801-813(net_apply_dhcp_offer)

端到端利用路径(Source → Sink)

前置:内核发起 DHCP 续租/首次获取(OOBE 期间管理员服务、netctl 管理员、OOBE DHCP 白名单路径调用 LEONOS_IOCTL_NET_DHCP;该 ioctl 入口现在受 require_network_config_access() 守卫,不再是任意进程可调用——July Finding-2 已修复)。

利用链:

  1. DISCOVER 发出后,合法与恶意 DHCP 服务器(同二层)均可返回 OFFER。net_dhcp_request 在 net.c:1895 等待第一个匹配 xid 的 NET_DHCP_OFFER。内核只等待“第一个匹配的 OFFER”,随后记录其 offer.yiaddr 和 offer.server_ip,并在 net.c:1903 以 NET_DHCP_REQUEST 明确发送到该 offer.server_ip。
  2. 随后在 net.c:1909 调用 net_dhcp_wait(xid, timeout_ms, NET_DHCP_ACK, &offer)。该函数实现(net.c:1837-1864):

while (!timeout) {
    udp_wait = { src_port = NET_DHCP_SERVER_PORT, dst_port = NET_DHCP_CLIENT_PORT, ... };
    net_poll_once(0, 0, &udp_wait, 0);
    if (udp_wait.done && net_dhcp_parse_packet(payload, udp_wait.length, xid, offer) == 0) {
        if (offer->msg_type == NET_DHCP_ACK) return 0;      // 接受
        if (offer->msg_type == NET_DHCP_NAK) return -3;
    }
}
  1. 抢发送者优势:攻击者只要位于同一二层、能发 UDP 67/68、观测或猜测 xid(DISCOVER/REQUEST 广播中 xid 是明文,攻击者可直接嗅探),即可在合法服务器 ACK 到达之前抢先发一个 msg_type=ACK 的报文,offer 出参被 net_dhcp_parse_packet 重写为攻击者提供的 yiaddr/router_ip/dns_ip/server_ip/lease_seconds。
  2. 全局配置写入:net_dhcp_request 不做复核,紧接着于 net.c:1917 调用 net_apply_dhcp_offer(&offer),将:
  1. SINK 影响:

关键缺口:

为何仍只评为中等

修复建议


if (offer->msg_type == NET_DHCP_ACK) {
    if (expected_server_ip && offer->server_ip != expected_server_ip) continue_drop;
    if (expected_yiaddr && offer->yiaddr != expected_yiaddr) continue_drop;
    return 0;
}

[F5] 浏览器 Basic 认证键不包含 URL 方案(HTTPS 凭据可发送到明文 HTTP)

字段值
严重性中等
置信度高(字段/缓存结构级证明)
分类CWE-523(凭据保护不足)、CWE-319(明文泄露)
受影响位置userland/apps/browser/auth.c:60-84(auth_entry_for 与 browser_auth_append_header)、auth.c:116-142(写入条目,仅写入 host/port)、userland/apps/browser/cookies.c:746(请求头构造点调用上述函数)

端到端利用路径(Source → Sink)

  1. 凭据录入:用户访问 https://corp.example.com:443/admin,服务端返回 WWW-Authenticate: Basic。浏览器弹 UI → 用户输入 admin:Correct-Horse-Battery-Staple → browser_auth_retry_from_challenge(auth.c:86-142)生成 Authorization: Basic YWRtaW46Q29ycmVjdC1Ib3JzZS1CYXR0ZXJ5LVN0YXBsZQ== 并存入缓存:
  1. 导航到同主机端口的 HTTP:可能触发情形之一:
  1. 请求头生成:browser_http_prepare_headers → browser_auth_append_header(url, out, &pos, cap)(auth.c:71-84):

parse_http_url(url, &parsed);
index = auth_entry_for(parsed.host, parsed.port);   // 仅 host/port
if (index >= 0) append_text(out, pos, cap, header); // Authorization: Basic ...
  1. SINK:该请求以明文 HTTP 发送,Authorization 头中的 Base64 可逆凭据被同网段嗅探者完整读取。凭据可被攻击者直接重放到 HTTPS 端点(反向仍可用),完成会话接管或敏感资源访问。

组合增益

修复建议


[F6] ARP 缓存无条件存储入站 sender 映射(无需匹配未完成查询或链路一致性)

字段值
严重性中等(本地网段可稳定毒化网关 MAC)
置信度高(源码行级控制流)
分类CWE-345(邻居身份不充分校验)
受影响位置kernel/ntclks/net.c:1041-1067(net_handle_arp),写缓存点 net.c:1058

端到端利用路径

  1. 入站帧:攻击者在相同二层广播域发送任意格式合法的 ARP 帧(Ethernet type 0x0806、HTYPE=1、PTYPE=0x0800、HLEN=6、PLEN=4)。
  2. 控制流:net_handle_arp(frame, len, arp_wait):
  1. 关键缺口:
  1. SINK:攻击者以广播 sender_ip=GATEWAY_IP, sender_mac=ATTACKER_MAC 的单个 ARP 请求(op=ARP_OPER_REQUEST,target_ip=任意)即可把 net_arp_cache_store 改写为攻击者地址。之后内核将所有非本地流量 dst_mac = ATTACKER_MAC。
  2. 组合:与 F4(DHCP ACK 抢注)或后续的网关欺骗结合 → 出流量全部被攻击者中继,可进行中间人嗅探/篡改;若攻击者不转发,则直接造成全系统网络不可用 DoS。

为何不升严重

修复建议


[F7] TCP RST 缺少接收窗口序号验证直接关闭已建立连接

字段值
严重性中等(可跨连接泛洪中断会话)
置信度高(行级控制流)
分类CWE-345(协议状态机不充分)
受影响位置kernel/ntclks/net.c:1557-1560(net_socket_handle_tcp 中的 RST 处理)

端到端利用路径

  1. 任意已建立的 TCP 连接(socket->state == LEONOS_NET_TCP_ESTABLISHED 或 TIME_WAIT)均可被伪造的 TCP 报文关闭。
  2. 内核在 TCP 入口(net.c:1186-1192)解析完 flags, seq, ack, payload_len 后调用 net_socket_handle_tcp(src_ip, src_port, dst_port, seq, ack, flags, payload, payload_len)。
  3. 状态转换:net_socket_handle_tcp 首先执行匹配套接字查找:

socket = net_socket_match(src_ip, src_port, dst_port);
  1. 缺口:函数到达 if (flags & TCP_FLAG_RST)(net.c:1557)即调用 net_socket_mark_closed(socket, LEONOS_NET_STATUS_TCP_RESET); return;——完全不读取/验证 seq 参数(该参数作为函数形参传入但此分支内未使用),也不使用 socket->remote_seq(期望接收序号)做接收窗口判定。
  1. SINK:攻击者发送一个四元组匹配、TCP 头含 RST 标志、任意序号的报文即导致连接状态置为 LEONOS_NET_TCP_CLOSED、状态 TCP_RESET。效果:

这要求(src_ip, src_port, dst_port)匹配(本地端口是公开的;远端 IP/端口可通过浏览器会话、HTTP 请求从观测到的元数据获得,或在共享链路上直接嗅探)。

置信与严重度

修复建议


if (flags & TCP_FLAG_RST) {
    // 仅当 RST seq 落在当前可接受接收窗口内才关闭;否则忽略。
    if (!net_tcp_seq_in_window(socket->remote_seq, socket->recv_window, seq)) {
        return;
    }
    net_socket_mark_closed(socket, LEONOS_NET_STATUS_TCP_RESET);
    return;
}

同期已修复 / 不再满足准入(对照 2026-07-07 与 2026-08-04 报告)

旧编号(来源)标题状态证据(当前源码中的修复信号)
July Finding 1普通用户 task 可调用 GUI/window-server 专用 ioctl(FB_*、窗口轮询、fetch、event 注入)已修复syscall.c 中 FB_FILL/FB_RECT/FB_TEXT/FB_PIXEL/FB_BLIT/FB_SET_MODE/POLL_WINDOW/FETCH_WINDOW/SEND_WINDOW_EVENT/POLL_DISPLAY_REQUEST/PUBLISH_DISPLAY_STATE/POLL_APPEARANCE_REQUEST/PUBLISH_APPEARANCE_STATE 全部前置 require_window_server() 守卫并返回 -LEONOS_EPERM。
July Finding 2任意用户可调用 DHCP ioctl 改全局网络配置已修复LEONOS_IOCTL_NET_DHCP(syscall.c:4305-4313)前置 require_network_config_access()(syscall.c:238-255),仅 installer / admin / SERVICE(非桌面)/ OOBE DHCP 白名单可通过。
July Finding 4stat() 用 LEONOS_AUTHZ_EXEC 替代文档声明的 Read/List 权限已修复LINUX_SYS_STAT(syscall.c:3103)改为 authz_check_path(task, LEONOS_AUTHZ_READ, path, 0, 0),与 docs/FILESYSTEM.md 权限映射一致。
July Finding 5网络连接列表暴露所有用户 TCP socket 元数据已修复net_connections 新增 viewer 参数(net.c:3137-3170)且每次枚举前调用 net_connection_visible(socket, viewer)(net.c:3115-3129),仅 admin/非桌面 service/或 socket.owner_uid 匹配 viewer->uid 的条目可见。调用方在 syscall.c:4400 传入 sched_current_task()。
August Finding 1GUI 几何整数回绕导致内核越界写已修复新增 gui_ipc_validate_surface_geometry(gui_ipc.c:239-256)含 GUI_IPC_MAX_WINDOW_WIDTH/HEIGHT/STRIDE 上限与 checked_mul_u64;ensure_window_buffer(gui_ipc.c:285-311)在窄化前强制 64 位页数 + UINT32_MAX 上限;CREATE / PRESENT / FETCH 三条 ioctl 均调用该校验,并在 PRESENT 前置用户缓冲区 user_range_ok(bytes)。 SEC-001 在回归脚本中已显示「未检测到已知缺陷模式」。
SEC-003Display Request 混淆代理(驱动受信任桌面持久化)降级为低入口缺少授权守卫(仍与 2026-08-04 相同);但桌面消费侧实际写入 /system/config/display.conf 受 ACL 与桌面运行身份约束,且 display_request 单槽机制无法定向到“任意设置字段”,缺乏可论证的端到端完整性破坏链。本轮按「准入严格版」排除。
SEC-004登录缺少失败限速和锁定降级为低缺乏登录 API 的远程暴露入口证明(登录 API 仅本地 GUI 登录界面 / 终端 login 调用,未发现网络暴露的登录入口);密码策略与 UI 实际尝试窗口未知,置信度未达到中等。排除。

上表之外,July Finding 3(service flag 由可执行路径派生)与 July Finding 6(loader SHA256 非可信根)仍为架构加固议题但缺少明确的端到端利用链(依赖未来文件系统漏洞或离线篡改),因此本周期按「准入严格版」不列入存活发现。


结构性建议(仅基于存活 7 项归纳)

维度覆盖建议
单一授权层F1 + 已修复的 FB/NET/DRIVER ioctl对所有“系统影响型” ioctl(电源、display_request、appearance_request、网络配置、driver 控制、任务终止)引入统一的 require_* 守卫表,由静态回归脚本确保“每个 ioctl 常量在白名单内或被守卫调用”,避免未来新增分支再次裸调用。
协议长度安全基元F3在 userland/libc/src/libc.c 和 kernel/ntclks/net.c 建立项目级 uint32_t * / + 安全包装;强制在 HTTP、TCP、DHCP、TLS 记录、GUI 几何、文件系统读块中使用差值型边界检查并对超长输入做 fail-closed。
状态绑定F4、F6、F7网络状态机对每次“接受外部输入作为新状态”的动作,要求同时满足:(a) 身份绑定(服务器 IP/seq/查询 IP/MAC 一致性);(b) 事务绑定(xid/等待中的查询/已请求地址);(c) 协议正确性(接收窗口/RFC 兼容序号)。
明确不变量F2、F5对“安全→不安全”方案/标志的退化建立项目级不变量:HTTPS 永不跟随至 HTTP;Secure Cookie 与 HTTPS-only Basic Auth 永不被明文请求加载;回归脚本在 libc.c/cookies.c/auth.c 增加 3 条“是否存在降级守卫”的严格模式检测。
回归门禁全部 7 项保持 tools/test_security_regressions.py 为源码只读检测,修复后以 --strict 纳入 CI;在此基础上为 F3/F4/F7 补充边界单元测试(chunked hex 回绕、DHCP 多服务器 ACK、TCP RST 序号窗口),以及隔离 VM/网络烟测(非破坏性)。

已验证与未验证边界声明(严格区分「源码已检查」与「运行已验证」)

阶段完成证据
源码已检查(source)✔所有 7 项存活均提供精确文件:行号、受影响函数、对比反例和可走通的 source→sink 叙述。
源码修改(modify)❌本报告为周期审计,不实施补丁。
编译/打包(build)❌未调用 build.py。
VM 运行与 PoC(runtime)❌未启动 QEMU/VMware、未发真实网络帧、未执行会破坏内核或进程内存的 PoC。本期结论全部基于源码级证明;真实运行环境中利用的稳定性与概率可能高或低于源码分析。

文档归属:docs/security/2026-08-30.md。 生成日期:2026-08-30(按系统提醒当日日期)。