reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
# OpenBSD:KARL 重链接 / autoconf / pledge·unveil
|
||||
|
||||
<CORE RULE>
|
||||
**同一版本的内核,每次启动内部地址都可能不同。**
|
||||
|
||||
OpenBSD 的 **KARL**(kernel address randomized link)会在重启前用 link kit 里的 `.o` **重新随机链接**内核,使内核内部布局发生变化。于是:
|
||||
|
||||
```
|
||||
机器 A: /bsd 里 foo() = X
|
||||
机器 B: /bsd 里 foo() = Y
|
||||
同一台机器的两次 boot 之间也变
|
||||
```
|
||||
|
||||
**不能据此判断"内核被 patch 了"或"版本不同"。**
|
||||
|
||||
第二条:设备不是"代码里挂上去的",而是**运行时递归匹配/attach 出来的**——`foo_attach()` 没有普通调用者完全正常。
|
||||
</CORE RULE>
|
||||
|
||||
## 一、KARL:不是 KASLR
|
||||
|
||||
| | KARL | 普通 KASLR |
|
||||
|---|---|---|
|
||||
| 什么在变 | **二进制内部布局**(对象之间的相对位置) | 同一个二进制被加载到**随机基址** |
|
||||
| 内核加载位置 | 仍在同一 KVA 位置 | 每次不同 |
|
||||
|
||||
机制要点:
|
||||
|
||||
- 内核被链接成:**启动汇编(locore.o)固定**已知位置 → 一个**随机大小的间隙** → **其余 `.o` 随机重排**;`.rodata` / `.data` / `.bss` 之前也各有随机间隙
|
||||
- 因为**函数与变量之间的距离全新**,泄漏一个指针无法推导出其他对象的位置;嵌套偏移变化也破坏了指令流的多态,可能减少变长架构上的 gadget
|
||||
- 运行时内核可以**反映射(unmap)自己的启动代码**;在因大页映射而无法 unmap 的架构上,改为把这段代码**破坏(trash)掉**
|
||||
- 基础系统里带 **link kit**(构建内核所需的全部 `.o`),由 rc 脚本在**每次启动后后台链接一个新的内核**装给下次启动——每次重启都运行一个新链接出来的内核,并顺手生成再下一个
|
||||
- 安装时与升级时也会构建一个唯一内核;**若开发者把内核拷到 `/`(例如 `/bsd`)做调试,重链接机制会脱开,直到执行 `make install` 或升级**
|
||||
|
||||
**RE 判据**:
|
||||
|
||||
```
|
||||
/bsd 逐字节不同
|
||||
→ 先问:是不是 KARL/重链接内核?
|
||||
→ 再做 symbol / semantic comparison
|
||||
```
|
||||
|
||||
**不要**把逐字节差异直接当异常。
|
||||
|
||||
## 二、autoconf:设备是运行时匹配出来的
|
||||
|
||||
- 设备树是**运行时递归匹配/attach** 出来的:`config_found()` / `config_attach()` 依据配置表调用各驱动的 **match / attach**
|
||||
- `detach` 允许**等待正在使用该设备的进程退出**(不是立即拔掉)
|
||||
- 因此
|
||||
|
||||
```
|
||||
foo_attach() ← 没有普通 call xref
|
||||
```
|
||||
|
||||
**完全可能正常。** 真正的边是:
|
||||
|
||||
```
|
||||
autoconf 表 → match → config_attach → foo_attach
|
||||
```
|
||||
|
||||
## 三、用户态侧:pledge / unveil 会让"正常程序"突然失败
|
||||
|
||||
这一节是**用户态**机制,但**极易被误判成"EDR/沙箱在拦截"**,所以放在这里。
|
||||
|
||||
- **`pledge()` 是单向棘轮**:后续调用**只能削减**可用子系统,**已被撤销的子系统不能重新获得**;试图**提升**权限会以 `EPERM` 失败(另有一种情形——继承 execpromises 的子进程提升会**被忽略并返回成功**)
|
||||
- 违反 promise 时,进程通常**被以不可捕获的 SIGABRT 杀掉**(尽可能留下 core)——**不是可捕获的错误返回**
|
||||
- 注意:**`pledge("everything", NULL)` 与"不调用 pledge"不是一回事**——promise 列表未必覆盖每一个 ioctl
|
||||
- `unveil()` 把文件系统视图缩小到若干路径;**`unveil` 本身也是一个 promise**——移除它(或传入两个空字符串)可禁止后续再调用
|
||||
- 另有 `error` promise:违反时**不杀进程**,改为返回 `ENOSYS`
|
||||
- 现场线索:运行中的进程在 `ps` 里显示状态 `p`;因 pledge 违规被终止的进程由 `lastcomm` 以 `P` 标志记账
|
||||
|
||||
**典型现象**:
|
||||
|
||||
```
|
||||
程序启动正常
|
||||
→ 过一段时间 open("/etc/foo") 失败
|
||||
→ 甚至直接 SIGABRT
|
||||
```
|
||||
|
||||
**先查是否调用过 pledge() / unveil(),而不是先怀疑外部拦截**——"**启动时能访问、之后突然不能**"恰恰是这类机制的常见设计模式。
|
||||
|
||||
## 决策树
|
||||
|
||||
```
|
||||
拿到 OpenBSD 目标
|
||||
├─ 比较两份 /bsd(或同一机器的两次 boot)
|
||||
│ ├─ 逐字节不同 → 先按 KARL/重链接解释,别判被 patch
|
||||
│ └─ 要做内核 diff → 走符号/语义比较,而不是字节比较
|
||||
├─ 分析环境里内核来源可疑
|
||||
│ └─ 手工拷到 / 的内核会让重链接脱开(直到 make install/升级)——注意与生产环境不同
|
||||
├─ 某个 attach 函数没有调用者
|
||||
│ └─ autoconf:配置表 → match → config_attach → foo_attach
|
||||
├─ 用户态程序行为"中途变化"
|
||||
│ ├─ 查 pledge():单向棘轮、不能恢复、违规不可捕获 SIGABRT
|
||||
│ ├─ 查 unveil():文件系统视图被缩小
|
||||
│ └─ 别先归因于 EDR/沙箱
|
||||
└─ 进程莫名死掉且无常规错误
|
||||
└─ ps 状态 p / lastcomm 的 P 标志是线索
|
||||
```
|
||||
|
||||
## 工具与验证
|
||||
|
||||
- 内核:link kit 与 rc 侧的重链接脚本(判断这份内核怎么来的);符号/语义层面的 diff(而非字节)
|
||||
- 设备:autoconf 配置表与 match/attach 关系;`detach` 的等待语义
|
||||
- 用户态:进程的 pledge/unveil 调用点与 promise 串;`ps` 状态与 `lastcomm` 记账
|
||||
- 验证:能同时说清「内核是怎么来的(重链接 vs 手工安装)+ 设备是怎么挂上的(autoconf)+ 权限是怎么被削的(pledge/unveil 时点)」
|
||||
|
||||
## 该平台的坑(汇总)
|
||||
|
||||
- **拿字节差异当"内核被篡改/版本不同"**:KARL 使同一版本每次 boot 布局都不同
|
||||
- **把 KARL 当 KASLR**:前者变内部布局,后者变加载基址
|
||||
- **以为内核加载位置会随机**:KARL 下内核仍在同一 KVA 位置
|
||||
- **找不到 attach 的调用者就判死代码**:autoconf 在运行时按配置表匹配并 attach
|
||||
- **以为 detach 会立即移除设备**:它可能等待使用者退出
|
||||
- **把 pledge 违规当外部拦截**:那是不可捕获的 SIGABRT;且撤销的能力无法恢复
|
||||
- **以为不调用 pledge 与 `pledge("everything")` 等价**:后者仍可能不覆盖某些操作
|
||||
- **忽略了 unveil 之后文件系统视图已缩小**:"路径存在但打不开"可能是设计
|
||||
- **在分析环境里用手工安装的内核推断生产行为**:手工拷内核会让重链接脱开
|
||||
Reference in New Issue
Block a user