// adding a new system call : sys_upper
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <asm/unistd.h>
#include <asm/page.h>
#include <linux/syscalls.h>
#define SYS_CALL_TABLE 0x8000e348 // manually configure this address!!
#define NR_SYS_UNUSED 223
//Pointers to re-mapped writable pages
unsigned int** sct;
asmlinkage long sys_upper(char *in, char* out){
int len = strlen(in);
int i;
for(i=0; i<len; i++){
if(in[i]>=0x61 && in[i]<=0x7a){
out[i] = in[i] - 0x20;
}
else{
out[i] = in[i];
}
}
return 0;
}
static int __init initmodule(void ){
sct = (unsigned int**)SYS_CALL_TABLE;
sct[NR_SYS_UNUSED] = sys_upper;
printk("sys_upper(number : 223) is added\n");
return 0;
}
static void __exit exitmodule(void ){
return;
}
module_init( initmodule );
module_exit( exitmodule );
这题就是提供了一个可以write-anything-anywhere
的系统调用(也不算anything,有点限制),系统调用的地址存在0x8000e348+223 = 0x8000e6c4
, flag在/root/flag
.
按理说应该不难,但是我做了很久。后来想了一下,主要是内联汇编不熟(没有写好 clobbers 导致各种崩)。再就是没有深入理解 Linux 的权限控制机制,一开始想当然的觉得 kernel space 就肯定有 root 权限,后来发现就算用了系统调用,跑到奇怪的地址上执行,uid 还是这个进程的 uid1. 所以尝试的 open-read-write
, chown()
各种都已失败告终,权限不够。直接在 kernel space execve()
的我也是想多了。
其实思路还是挺简单的,首先修改 223 号系统调用的内容,然后调用这个修改过的 223 号系统调用,在 kernel space 把 uid 改掉,之后在 user space execve()
就好了。
在现在版本的 Linux 内核修改 uid,需要通过prepare_creds()
和commit_creds()
两步2。这两个函数的地址存在/proc/kallsyms
:
$ cat /proc/kallsyms | grep 'prepare_creds\|commit_creds'
8003f44c T prepare_creds
8003f56c T commit_creds
...
我参考 @acama 的版本3写了一个( @acama 的版本prepare_creds()
之后直接就commit_creds()
, 这估计只在老版本可以).prepare_creds()
返回的结构体定义可以看参考4.
这个生成的指令是不能用原先的 223 号系统调用直接写进内存的,所以我准备了一个真正的write-anything-anywhere
的跳板:
先把 waa 写进内存,然后把 cred 写进内存。至于写到哪里,我随手写了两个地址: 0x83f5cafe, 0x83f6beee.
exp.c