FORTIFY_SOURCE
FORTIFY_SOURCE(源码增强),这个其实有点类似与Windows中用新版Visual Studio进行开发的时候,当你用一些危险函数比如strcpy、sprintf、strcat,编译器会提示你用xx_s加强版函数。
FORTIFY_SOURCE本质上一种检查和替换机制,对GCC和glibc的一个安全补丁。
目前支持memcpy, memmove, memset, strcpy, strncpy, strcat, strncat,sprintf, vsprintf, snprintf, vsnprintf, gets等。
默认Ubuntu16.04下是关闭的,测试发现Ubuntu18.04是开启的
gcc -D_FORTIFY_SOURCE=1 仅仅只在编译时进行检查(尤其是#include <string.h>这种文件头)
gcc -D_FORTIFY_SOURCE=2 程序执行时也会进行检查(如果检查到缓冲区溢出,就会终止程序)
FORTIFY_SOURCE(代码增强)
-D 1(开启缓冲区溢出攻击检查)
-D 2(开启缓冲区溢出以及格式化字符串攻击检查) ,通过数组大小来判断替换strcpy、memcpy、memset等函数名,来防止缓冲区溢出。
pwn32
我们首先还是先将pwn文件下载下来,然后拖进虚拟机加上可执行权限使用checksec命令查看文件的信息。
chmod +x pwn
checksec pwn
文件是64位的我们先拖进ida64反编译一下。
// main
int __cdecl main(int argc, const char **argv, const char **envp)
{
__gid_t v3; // eax
const char *v4; // rax
int v5; // eax
int num; // [rsp+4h] [rbp-44h] BYREF
char buf2[11]; // [rsp+Ah] [rbp-3Eh] BYREF
char buf1[11]; // [rsp+15h] [rbp-33h] BYREF
v3 = getegid();
setresgid(v3, v3, v3);
logo();
v4 = argv[1];
*(_QWORD *)buf1 = *(_QWORD *)v4;
*(_WORD *)&buf1[8] = *((_WORD *)v4 + 4);
buf1[10] = v4[10];
strcpy(buf2, "CTFshowPWN");
printf("%s %s\n", buf1, buf2);
v5 = strtol(argv[3], 0LL, 10);
memcpy(buf1, argv[2], v5);
strcpy(buf2, argv[1]);
printf("%s %s\n", buf1, buf2);
fgets(buf1, 11, _bss_start);
printf(buf1, &num);
if ( argc > 4 )
Undefined();
return 0;
}
// Undefined
void __cdecl Undefined()
{
FILE *v0; // rax
char flag[64]; // [rsp+0h] [rbp-48h] BYREF
puts(
"The source code of these three programs is the same, and the results of turning on different levels of protection are understood\n");
puts("You should understand the role of these protections!But don't just get a flag\nHere is your flag:\n");
v0 = fopen("/ctfshow_flag", "r");
if ( !v0 )
{
puts("/ctfshow_flag: No such file or directory.");
exit(0);
}
fgets(flag, 64, v0);
puts(flag);
}
我们先来分析一下代码的逻辑:
程序打印出logo的信息,然后然后将第一个参数argv[1]也就是我们启动程序输入的第一个参数(其实还有argv[0]的,这个参数是每个程序的都一定会有的,并且值为程序名称)赋给v4,并且没有长度限制。之后程序将v4[10]的数组内容赋给buf1[10]数组,再将CTFshowPWN通过strcpy函数赋给buf2,接着打印输出buf1和buf2。再然后通过strtol(argv[3], 0LL, 10)将我们启动程序传入的第三个参数转为10进制赋给v5,紧接着使用memcpy函数将argv2的v5个字符赋给buf1,v5是int型,数值就是上条语句的得来的;然后再通过strcpy函数argv[1]我们输入第一个参数赋给buf2,之后再打印输出buf1和buf2,最后读取_bss_start 11个字符给buf1,打印输出buf1和num的信息。最最后通过if判断argc是否大于4(默认情况下argc是等于1的,因为argv[0]必定存在,我们输入一个参数,argc就会等于2,依次递推),如果大于4,就会进入Undefined函数,Undefined函数的作用就是输出flag了。
因为本题目FORTIFY_SOURCE没有开启,代表我们启动函数直接输入4个参数(这时argc=5 > 4)就行了,而且这4个参数没有长度限制,如果开启FORTIFY_SOURCE就不好说了,因为开启了之后,由于程序存在strcpy和memcpy函数会检测长度,如果长度超过了限制,可能会使程序抛出异常而退出执行。
那么这道题目我们就直接输入4个参数就行了。
先ssh连接上主机。
ssh ctfshow@pwn.challenge.ctf.show -p28160
这里我们使用ls看了下目录存在pwnme文件这个文件就给我们之前下载的pwn文件是一样的,我们运行随便加上4个参数就行了。
./pwnme 1 2 3 4
这时程序会停在这里,我们直接一个回车就行了。
OK,成功拿到了flag。
pwn33
题目已经告诉我们开启了FORTIFY_SOURCE=1,并说明在编译时进行一些安全检查,如缓冲区边界检查、格式化字符串检查等。 在运行时进行某些检查,如检测函数返回值和大小的一致性。 如果检测到潜在的安全问题,会触发运行时错误,并终止程序执行。
本题目的文件跟上道一样,pwn都是64位的,并且反编译出的代码也大致相似,这里就不详细分析反编译的代码了,但是它开启了FORTIFY_SOURCE=1,所以我们要注意源代码里的memcpy和strcpy函数。
memcpy和strcpy这两个函数被替换成了__mencpy_chk和__strcpy__chk安全函数,可以看到这两个函数相比前两个函数只是加上了11LL这个参数加以限制,因为buf1和buf2在声明的时候的长度就是11,所以程序为了防止溢出,使用后两个函数加上这两个数组的长度加以限制以防溢出。
那这个东西对于我们想要和上道题目一样输出flag完全是不没有任何阻碍的呀,我们就保证我们输入的第一个参数和第二个参数的长度不超过11就行了啊,只是上到题目没有开启FORTIFY_SOURCE,我们输入参数的长度是任意的,这道题目对我们的参数加以限制了而以。
我们直接ssh连接,运行pwnme加上不超过11长度的4个参数就行了(主要是参数1和参数2不超过11),那我们就和上道题目一样每个参数长度都为1呗。
ssh ctfshow@pwn.challenge.ctf.show -p28161
./pwnme 1 2 3 4
成功拿到了flag。
pwn34
本道题目指明了FORTIFY_SOURCE=2,在虚拟机用checksec命令检查如下:
题目描述也说了该程序包括基本级别的安全检查,并添加了更多的检查。 在编译时进行更严格的检查,如更精确的缓冲区边界检查。 提供更丰富的编译器警告和错误信息。
使用ida64反编译的加过略微与前两道题目有所不同,大部分还是一样的,还是把危险函数替换成了安全函数。如下:
__mencpy_chk和__strcpy__chk安全函数上道题目已经说过与memcpy和strcpy的区别,这里不再赘述。
__printf__chk该函数与printf的区别在于:
不能使用 %x$n 不连续地打印,也就是说如果要使用 %3$n,则必须同时使用 %1$n 和 %2$n。在使用 %n 的时候会做一些检查。
这个涉及到格式化字符串漏洞,本题涉及不到,所以对本道题也几乎阻碍不大,所以我们还是ssh之后运行文件时输入的4个长度为1的参数,不出意外还能到拿到flag!
ssh ctfshow@pwn.challenge.ctf.show -p28177
文章来源:https://www.toymoban.com/news/detail-493713.html
./pwnme 1 2 3 4
果然没有出意外hhh,拿到了flag!文章来源地址https://www.toymoban.com/news/detail-493713.html
到了这里,关于CTFshow-pwn入门-前置基础pwn32-pwn34的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!