Files
MeowOS-For-R3P2/patch_v2/meow_netmgrd/nm_wifi_args.h
T
AskaEth 7b32d775c0 fix(security): M64 P1 三条 root 命令注入 — 外部串改 execv 传 argv、电源键下线、press_ms clamp
P1-1 Wi-Fi SSID/PSK 注入(netmgrd + launcherd 回退两处同步):
- 不再拼 `set_network %d ssid '"%s"'` 交给 sh -c;新增 wpa_exec()/nm_direct_wpa_exec()
  以 fork+execv 传 argv(含 resolve_bin 解析绝对路径,替代 `PATH=...` 前缀)。
- 共用 nm_wifi_args.h:nm_wifi_ssid_ok/n_wifi_psk_ok/nm_wpa_quote/nm_wpa_set_argv,
  两份实现同一套规则(只修一处等于没修)。
- 约束层:SSID 1..32B、禁控制字符;PSK ≤63 或 64 hex、禁控制字符;`"`/`\` 按
  wpa_supplicant 语法转义。shell 元字符不再需要拒绝——execv 下没有 shell 解析。

P1-2 MeowPanel App 安装/导出/卸载注入:
- install/export/remove 改 run_argv()(fork+execv,PATH 只在子进程 setenv)。
- 包名统一 mp_pkg_ok()(仅 [a-z0-9._-],拒 `..`,与 nyai.c valid_pkg 同规则)→ 修 P2-7。
- `{path}` 追加 mp_path_unsafe()(与 mp_fonts/nyai 同一拒绝集)作第二道。

P1-3 /api/key/* 电源键:
- keycode_to_device 删除 KEY_POWER;116 进红线(403),前端遥控板电源/长按电源按钮移除。
- press_ms 全局 clamp 0..1000(单键与批量),`long` 缺省取上限。
- 按下→等待→抬起改独立线程(inject_key_async),不再阻塞单线程 8080 服务。

测试(宿主,反向控制):test_nm_wifi_args.c / test_mp_argsafe.c —— 同一套审计恶意串
(`a' ; id ; '`、`x';id;'`、`/tmp/a';id>/tmp/pwn;'.mpk`、`MeowAPPs/..`)在修复前实现下
注入成功(stdout 含 uid=)/放行,在修复后实现下不执行/拒绝。另含「两处实现同步」检查。

未刷机、未连设备、未发 /api/key 请求;只改源码,未提交二进制。
2026-11-12 08:50:10 +08:00

109 lines
4.5 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* nm_wifi_args.h — wpa_cli 参数安全(M64 P1-1:SSID/PSK → root 命令注入)。
*
* 修复前:SSID/PSK 被拼成 `set_network <id> ssid '"<ssid>"'`,再交给
* `execl("/bin/sh","sh","-c",cmd)` 执行(meow_netmgrd.c:3307/3154/325;同一实现复制在
* meow_launcherd/netmgr_ipc.c:475 的直连回退)。单引号里套双引号,SSID 自己带 `'` 就能
* 闭合引号——附近恶意 AP 广播 SSID=`a' ; id ; '`,用户点连接即 root 执行。
*
* 修复分两层,本头只放「可在宿主上直接单测」的纯函数:
* 1) 彻底层:调用方改用 fork+execv 传 argv(见 meow_netmgrd.c:wpa_exec /
* netmgr_ipc.c:nm_direct_wpa_exec)。不再有 shell,`' ; ` $ & | < >` 全部只是
* 普通字节,既不需要也不敢拒绝("Bob's WiFi" 是合法 SSID)。
* 2) 约束层:SSID/PSK 按 802.11 与 wpa_supplicant 语法收口(长度、控制字符);
* 拼 wpa 字面量时转义 `"` 与 `\`——这两个字符破坏的是 supplicant 的引号解析,
* 与 shell 无关,属于正确性要求。
*
* 两份实现(netmgrd 与 launcherd 回退)共用本头:只修一处等于没修——launcherd 那条
* 路径正是 netmgrd 不可达时的回退,攻击者可以制造 netmgrd 不可达(M64 任务书 P1-1)。
*/
#ifndef NM_WIFI_ARGS_H
#define NM_WIFI_ARGS_H
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#define NM_WIFI_SSID_MAX 32 /* 802.11 SSID 字节上限 */
#define NM_WIFI_PSK_MAX 63 /* WPA passphrase 上限(8..63 字符);64 位 hex PSK 另算 */
/* wpa_supplicant 的 ctrl_iface 是行协议:值里出现 \n/\r 会截断或伪造命令,
* 其它控制字符也进不了 SSID/PSK 的合法字符集。 */
static inline int nm_wifi_ctl_free(const char *s, size_t n)
{
for (size_t i = 0; i < n; i++) {
unsigned char c = (unsigned char)s[i];
if (c < 0x20 || c == 0x7f) return 0;
}
return 1;
}
static inline int nm_wifi_ssid_ok(const char *s)
{
if (!s) return 0;
size_t n = strlen(s);
if (n == 0 || n > NM_WIFI_SSID_MAX) return 0;
return nm_wifi_ctl_free(s, n);
}
/* 空串 = 开放网络(调用方按 key_mgmt NONE 处理);≤63 字节口令;64 位 hex PSK。
* 不强制 8 字符下限:WPA 会自行拒绝过短口令,而面板/设置页对「密码错」的既有文案依赖
* 把它交给 supplicant 判定(M64 只收口注入面与行协议,不改失败语义)。 */
static inline int nm_wifi_psk_ok(const char *s)
{
if (!s || !s[0]) return 1;
size_t n = strlen(s);
if (!nm_wifi_ctl_free(s, n)) return 0;
if (n <= NM_WIFI_PSK_MAX) return 1;
if (n == 64) {
for (size_t i = 0; i < n; i++) {
char c = s[i];
if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')))
return 0;
}
return 1;
}
return 0;
}
/* 把值拼成 wpa_supplicant 的字面量 `"..."`(ssid/psk 这两个字段要求带引号)。
* 只转义 `"` 与 `\`——它们在 supplicant 的 parse_string 里有特殊含义。
* 返回 0 成功;-1 = 含控制字符或缓冲不足。 */
static inline int nm_wpa_quote(const char *val, char *out, size_t cap)
{
if (!val || !out) return -1;
size_t n = strlen(val);
if (!nm_wifi_ctl_free(val, n)) return -1;
size_t need = 3; /* 开引号 + 闭引号 + NUL */
for (size_t i = 0; i < n; i++) need += (val[i] == '"' || val[i] == '\\') ? 2 : 1;
if (need > cap) return -1;
size_t o = 0;
out[o++] = '"';
for (size_t i = 0; i < n; i++) {
char c = val[i];
if (c == '"' || c == '\\') out[o++] = '\\';
out[o++] = c;
}
out[o++] = '"';
out[o] = 0;
return 0;
}
/* 组装 `set_network <id> <field> <"value">` 的 argv(argv[0] 由调用方补 wpa_cli 路径)。
* 生产代码(netmgrd wpa_set_string / launcherd nm_direct_wpa_set)与宿主测试共用这一份,
* 保证测试断言的就是实际下发的那条命令。返回 0 成功;-1 = 值不合法/缓冲不足。 */
static inline int nm_wpa_set_argv(char *idbuf, size_t idcap, char *quoted, size_t qcap,
int id, const char *field, const char *val, char *argv[5])
{
if (!field || !argv) return -1;
if (nm_wpa_quote(val, quoted, qcap) < 0) return -1;
snprintf(idbuf, idcap, "%d", id);
argv[0] = (char *)"set_network";
argv[1] = idbuf;
argv[2] = (char *)field;
argv[3] = quoted;
argv[4] = NULL;
return 0;
}
#endif /* NM_WIFI_ARGS_H */