Citrix Ptrace问题

Firmware

Based on 14.1-47.46_nc_64
内核文件似乎非常大:

1
2
root@ns# ls -lh /flash/ns-14.1-47.46.gz 
-rwxr-xr-x 1 root wheel 137M Jun 7 15:02 /flash/ns-14.1-47.46.gz

但是实际上应该是嵌入了一个rootfs:

1
2
3
4
5
6
7
8
9
10
11
12
root@ns# dmesg | grep md0
md0: Embedded image 427819008 bytes at 0xffffffff816784a0
NS-KERN: md0: Preloaded image 427819008 bytes
NS-KERN skipping /dev/md0c...
Trying to mount root from ufs:/dev/md0 []...
root@ns# df -h
Filesystem Size Used Avail Capacity Mounted on
/dev/md0 395M 379M 8.2M 98% /
devfs 1.0K 1.0K 0B 100% /dev
procfs 4.0K 4.0K 0B 100% /proc
/dev/da0s1a 1.5G 141M 1.3G 10% /flash
/dev/da0s1e 14G 1.9G 11G 15% /var

msf相当于插入了一个新的段,用于存放rootfs。

image-20251010163902865

image-20251010163919064

如果直接把这个内核文件放入IDA,会花费很多时间用于解析,可以使用objcopy来把这一段无需解析的数据删除:

1
objcopy --remove-section=mfs kernel.nc.zion_47_46 47_46.kernel.no_mfs

也可以直接进入shell将文件系统dump下来:

image-20251010164104073

Ptrace

在shell中使用本地gdb调试任意可执行文件得到如下报错:

image-20251010164152494

在14.1版本kdb模式中调试ptrace系统调用时发现:

image-20251010164215035

对应kern_ptrace代码中的:

image-20251010164235900

可以看到这里返回一个非零值,而在13.1版本中返回的是一个0值。并且在gdb尝试获取寄存器值时在kdb中设置这里的rax值为0可以成功获取:

image-20251010164301793

image-20251010164307953

继续深入发现和p_candebug调用的mac_proc_check_debug函数有关:

image-20251010164321585

mac_proc_check_debug是 FreeBSD MAC(强制访问控制)框架中的一个安全检查钩子函数。它的核心作用是:当一个进程(例如调试器 GDB)试图附加(attach)到另一个进程进行调试时,由 MAC 框架调用此函数,以查询并强制执行当前系统安全策略,决定是否允许这次调试操作。

FreeBSD Verified execution

FreeBSD Verified execution也就是mac_veriexec是基于FreeBSD MAC框架实现的一个策略,它提供文件完整性验证(File Integrity Verification),类似于其他系统中的“防篡改”功能。具体实现参考:⚙ D8554 Verified execution (veriexec) as a MAC module.

核心数据结构有:

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
//***/sys/security/mac_veriexec/mac_veriexec.c***
static struct mac_policy_ops mac_veriexec_ops =
{
.mpo_init = mac_veriexec_init,
.mpo_syscall = mac_veriexec_syscall,
.mpo_kld_check_load = mac_veriexec_kld_check_load,
.mpo_mount_destroy_label = mac_veriexec_mount_destroy_label,
.mpo_mount_init_label = mac_veriexec_mount_init_label,
.mpo_priv_check = mac_veriexec_priv_check,
.mpo_proc_check_debug = mac_veriexec_proc_check_debug,
.mpo_vnode_check_exec = mac_veriexec_vnode_check_exec,
.mpo_vnode_check_open = mac_veriexec_vnode_check_open,
.mpo_vnode_copy_label = mac_veriexec_copy_label,
.mpo_vnode_destroy_label = mac_veriexec_vnode_destroy_label,
.mpo_vnode_init_label = mac_veriexec_vnode_init_label,
};

MAC_POLICY_SET(&mac_veriexec_ops, mac_veriexec, MAC_VERIEXEC_FULLNAME,
MPC_LOADTIME_FLAG_NOTLATE, &mac_veriexec_slot);

//sys/security/mac/mac_policy.h
struct mac_policy_conf {
char *mpc_name; /* policy name */
char *mpc_fullname; /* policy full name */
struct mac_policy_ops *mpc_ops; /* policy operations */
int mpc_loadtime_flags; /* flags */
int *mpc_field_off; /* security field */
int mpc_runtime_flags; /* flags */
int _mpc_spare1; /* Spare. */
uint64_t _mpc_spare2; /* Spare. */
uint64_t _mpc_spare3; /* Spare. */
void *_mpc_spare4; /* Spare. */
LIST_ENTRY(mac_policy_conf) mpc_list; /* global list */
};

#define MAC_POLICY_SET(mpops, mpname, mpfullname, mpflags, privdata_wanted) \
static struct mac_policy_conf mpname##_mac_policy_conf = { \
.mpc_name = #mpname, \
.mpc_fullname = mpfullname, \
.mpc_ops = mpops, \
.mpc_loadtime_flags = mpflags, \
.mpc_field_off = privdata_wanted, \
}; \
static moduledata_t mpname##_mod = { \
#mpname, \
mac_policy_modevent, \
&mpname##_mac_policy_conf \
}; \
MODULE_DEPEND(mpname, kernel_mac_support, MAC_VERSION, \
MAC_VERSION, MAC_VERSION); \
DECLARE_MODULE(mpname, mpname##_mod, SI_SUB_MAC_POLICY, \
SI_ORDER_MIDDLE)

其中struct mac_policy_ops结构体是一个巨大的钩子函数表,该结构体包含了所有 mpo_*钩子函数,如mpo_proc_check_debug.

FreeBSD 的 MAC 框架是一个安全基础设施,它允许加载不同的安全模块(如 SELinux, BSM, 其他自定义策略)来强制执行访问控制策略。这个框架通过在内核的关键操作点(如文件访问、进程间通信、网络操作等)插入“钩子函数”(Hook Functions)来实现。
mac_proc_check_debug就是这样一个钩子函数,它被插入到 ptrace(PT_ATTACH, ...)或类似的进程调试系统调用路径中。

对应内核中的静态模块定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.data:FFFFFFFF811B4600 mac_veriexec_mod dq offset aMacVeriexec ; "mac_veriexec"
.data:FFFFFFFF811B4608 dq offset mac_policy_modevent
.data:FFFFFFFF811B4610 dq offset mac_veriexec_mac_policy_conf ; struct mac_policy_conf
.data:FFFFFFFF811B4618 mac_veriexec_mac_policy_conf dq offset aMacVeriexec
.data:FFFFFFFF811B4618 ; DATA XREF: .data:FFFFFFFF811B4610↑o
.data:FFFFFFFF811B4618 ; struct mac_policy_conf
.data:FFFFFFFF811B4620 dq offset aMacVeriexec_0 ; "MAC/veriexec"
.data:FFFFFFFF811B4628 dq offset mac_veriexec_ops
.data:FFFFFFFF811B4630 dq 1
.data:FFFFFFFF811B4638 dq offset mac_veriexec_slot
.data:FFFFFFFF811B4640 dq 0
.data:FFFFFFFF811B4648 dq 0
.data:FFFFFFFF811B4650 dq 0
.data:FFFFFFFF811B4658 dq 0
.data:FFFFFFFF811B4660 dq 0
.data:FFFFFFFF811B4668 dq 0
.data:FFFFFFFF811B4670 mac_veriexec_ops dq 0 ; DATA XREF: .data:FFFFFFFF811B4628↑o
.data:FFFFFFFF811B4678 dq offset mac_veriexec_init
.data:FFFFFFFF811B4680 dq offset mac_veriexec_syscall

image-20251010164445507

mac_veriexec_proc_check_debug对应mpo_proc_check_debug槽位,其功能为:Check if a process is allowed to be debugged. If a process is not flagged with **VERIEXEC_NOTRACE**, then debugging is allowed:

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
/**
* @internal
* @brief Check if the requested process can be debugged
*
* @param cred credentials to use
* @param p process to debug
*
* @return 0 if debugging is allowed, otherwise an error code.
*/
static int
mac_veriexec_proc_check_debug(struct ucred *cred, struct proc *p)
{
int error, flags;

/* If we are not enforcing veriexec, nothing for us to check */
if ((mac_veriexec_state & VERIEXEC_STATE_ENFORCE) == 0)
return (0);

error = mac_veriexec_metadata_get_executable_flags(cred, p, &flags, 0);
if (error != 0)
return (0);

return ((flags & VERIEXEC_NOTRACE) ? EACCES : 0);
}

暴露的ioctl接口参考:⚙ D8561 Verified execution (veriexec) device interface to MAC/veriexec,并且不只是ptrace无法使用,execve一个二进制文件也会检测hash,类似可行执行,所以没办法直接编译一个程序来操控该ioctl接口。

image-20251010164514424

可以让内核进入kdb状态,然后直接修改mac_veriexec_state值为0:

1
root@ns# sysctl debug.kdb.enter=1

image-20251010164526771

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
root@ns# sysctl -a | grep veriexec
device veriexec
security.mac.veriexec.algorithms: SHA256
security.mac.veriexec.state:
security.mac.veriexec.debug: 0
root@ns# gdb /bin/ls
GNU gdb (GDB) 10.1
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-unknown-freebsd11.4".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /bin/ls...
(No debugging symbols found in /bin/ls)
(gdb) start
Function "main" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Temporary breakpoint 1 (-qualified main) pending.
Starting program: /bin/ls
.nsva .version configdb nsconfig
.snap boot dm0.img secboot
.testing boot.config ns-14.1-47.46.gz
[Inferior 1 (process 9857) exited normally]
(gdb)

这样就可以调试了。