关于TOCTTOU攻击的简介

2024-08-31 04:48
文章标签 攻击 简介 tocttou

本文主要是介绍关于TOCTTOU攻击的简介,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

关于TOCTTOU攻击的简介

转载自: https://www.cnblogs.com/liqiuhao/p/9450093.html

前言

最近看到了一些以 at 结尾的Linux系统调用,在维基百科上面说这可以防御一些特定的TOCTTOU攻击,而在TOCTTOU对应页面中并没有中文版的介绍,而且百度的结果也比较少,于是决定抽空写一个关于 TOCTTOU攻击的简介,其中部分参考了英文版的维基百科。

 
 

什么是竞争条件与TOCTTOU?

在电路设计、软件开发、系统构建中,如果一个模块的输出与多个不可控事件发生的先后时间相关,则称这种现象为“竞争条件”(Race condition),又称“竞争冒险”(race hazard)。

 

在大多数情况下,我们希望模块的行为/输出是确定可靠的,这时竞争条件就会成为bug。例如,假设有两个线程都希望将某个为0全局整型变量加1,理想情况下,我们期望的操作会是这样:

线程 1线程 2全局变量值
  0
读入值到寄存器 0
对读入的值加1 0
将寄存器中的值回写 1
 读入值到寄存器1
 对读入的值加11
 将寄存器中的值回写2

最终结果为2. 但是,如果这两个线程的原子操作交替进行(没有进行同步),输出的结果就可能出错:

线程 1线程 2全局变量值
  0
读入值到寄存器 0
 读入值到寄存器0
对读入的值加1 0
 对读入的值加10
将寄存器中的值回写 1
 将寄存器中的值回写1

(在少数情况下,竞争条件也是有益的,例如它在硬件随机数产生器或物理不可克隆函数中的应用)

 

从计算机安全考虑,许多竞争条件都会产生漏洞——攻击者通过访问/竞争共享资源,从而使利用该资源的其他参与者出现故障,导致包括拒绝服务和违法权限提升等后果。例如有名的Dirty_Cow漏洞就是由竞争条件引起的。

而一种常见的起因就是代码先检查某个前置条件(例如认证),然后基于这个前置条件进行某项操作,但是在检查和操作的时间间隔内条件却可能被改变,如果代码的操作与安全相关,那么就很可能产生漏洞。这种安全问题也被称做TOCTTOU(time of check and the time of use)。

 

TOCTTOU通常出现在类Unix系统对文件系统的操作上,但是也可能在别的环境下发生,例如对本地sockets或数据库事务的使用。例如,下面这个setuid程序就有TOCTTOU bug:

// NOTE: This program has setuid access rights flagif (access("filePathName", W_OK))
{exit(EXIT_FAILURE);
}fd = open("filePathName", O_WRONLY);
write(fd, buffer, sizeof(buffer));

在这个程序中,先用 access() 检查当前程序/进程的真实用户是否对制定的文件具有写权限,如果有,则对其写入相应的内容,否则异常退出。

 

要注意的是,access() 检查的是进程的real user id (ruid),而非effective user id (euid)。而> 对于set-user-ID程序来说,其进程的real user id 会是真正发起者的uid,以便检查某一些操作是否合法,而effective user id会被设置为0(root),以便进行某些特权操作,例如普通用户只能使用 passwd 程序修改自己的密码。也就是说,access() 解决的问题不是“本进程是否能访问该文件”,而是“发起本进程的用户是否能访问该文件”。

而到了 open() 这类真正的访问操作时,其检查的是effective user id (此处为root)。

更多有关于User identifier的内容可以参考:

  1. User identifier
  2. Difference between Real User ID, Effective User ID and Saved User ID
  3. effective-user-id-and-group-id-vs-real-user-id-and-group-id

个人感觉这和早期操作系统分段保护中内核对CPL、RPL和DPL位的设置检查相似——普通进程调用内核I\O操作时,内核负责把进程提供的数据段选择子RPL位设置为用户的CPL,而内核访问数据段时的CPL为内核本身的代码段RPL,处理器最终会检查访问时CPL、RPL是否都满足和数据段DPL的关系,从而即为普通进程提供了硬件操作,又可以防止违法访问。

 

在上面的程序中,access()这个检查和open()这个实际访问操作中可能会有其他(恶意)程序对文件系统进行更改,从而导致恶意访问发生,以下代码按照从上到下的时间执行:

// --------------process of setuid program-------
//
if (access("filePathName", W_OK))
{exit(EXIT_FAILURE);
}
// ----------------------------------------------// --------------process of attacer--------------
//
// After the access check
unlink("filePathName");
symlink("/etc/passwd", "filePathName");
// Before the open, "file" points to the password database
// ----------------------------------------------// --------------process of setuid program-------
//
fd = open("filePathName", O_WRONLY);
// Actually writing over /etc/passwd
write(fd, buffer, sizeof(buffer));
// ----------------------------------------------

可以看到,攻击者在 access 和 open 之间的时间片中将 setuid 程序的写入点改变为了 /etc/passwd,而open的检查可以顺利通过( euid 为0),从而向敏感文件写入数据,最终达到提权等目的。

 

这里要注意的是,由于access会在文件不存在的时候返回ENOENT错误,而symlink会在链接文件名存在的时候返回EEXIST错误,所以attacker进程应该在symlink之前删除"filePathName"(此处使用的是unlink)。

维基百科此处(last edited on 16 April 2018, at 17:23 (UTC) )没有删除操作,似乎有些问题。

 

虽然这种对竞争条件的攻击需要对CPU时间分片有一定的判断,但通常攻击者不会遇到很多麻烦(后文中会就如何把握攻击时间进行分析)。总之,应用程序不能假设在系统调用之间操作系统管理的某个状态不发生变换(在上例中是文件系统中的文件/文件名)。

接下来我们就一个典型(而古老)的TOCTTOU漏洞进行分析。

 
 

4.3BSD /bin/mail中的TOCTTOU漏洞

 

漏洞代码及工作原理

在4.3BSD部分实现及其之前的版本中,/bin/mail 基本实现流程如下,详细的代码可参见2.9BSD :

// ...... omitted
char	lettmp[] = "/tmp/maXXXXX";
char	maildir[] = "/usr/spool/mail/";
char	mailfile[] = "/usr/spool/mail/xxxxxxxxxxxxxxxxxxxxxxx";
// ...... omitted
main(argc, argv)
char **argv;
{// ...... omittedsetbuf(stdout, sobuf);mktemp(lettmp);unlink(lettmp);// ...... omittedtmpf = fopen(lettmp, "w");if (tmpf == NULL) {fprintf(stderr, "mail: cannot open %s for writing\n", lettmp);done();}if (argv[0][0] != 'r' &&	/* no favors for rmail*/(argc == 1 || argv[1][0] == '-'))printmail(argc, argv);elsesendmail(argc, argv);done();
}
// ...... omitted
 

注:在一二行将参数类型声明在外的写法是“old-style K&R C ”的风格,现在的C标准也允许这样写。

但是要注意这样写只是函数声明,不会提供函数原型,即编译器无法对传参类型进行检查,在gcc开启“-Wstrict-prototypes”选项后,会输出“warning: function declaration isn’t a prototype”的警告。

关于函数声明和函数原型的区别可以参考declaration-and-prototype-difference

另外,在现在的类unix中,/usr/spool 一般都变为了(或指向)/var/spool,代表一些待处理文件。

 

上述代码的基本工作原理为首先调用mktemp得到临时文件名,然后使用unlink删除重名的文件以避免冲突,而后判断是发现还是读信。

若是发信,则从标准输入读入信件正文,写入一个临时文件 /tmp/ma*****,而后转发到 /usr/spool/mail/账号中。若是读信,则从文件 /usr/spool/mail/账号 中读,写入临时文件,然后将信件内容打印在终端上,等待用户输入命令。

 

漏洞成因

在部分4.xBSD中,mktemp 的实现仅仅会将提供的 xxxxx 序列替换为当前进程的PID,并将最后一个字符替换为一个字母,所以最多只有26个名字会被返回。这导致了攻击者非常容易猜测mktemp的返回值。

另一方面,虽然编程者使用 unlink 避免了冲突,但是在 unlink 和 fopen 之间的时间中可能会和别的进程产生竞争,例如攻击者可以在 /tmp 下创建一个猜测的链接文件指向敏感文件,从而使程序在 fopen 的时候实际打开敏感文件。

最后,/bin/mail 是一个setuid程序,所以其effective user id为0(root).

 

漏洞利用

1991年,浙江大学的周尚德先生(首先?)发表了这个漏洞并给出了相应的Poc:

UNIX的一个漏洞

针对漏洞入侵Unix的程序实现

我们可以建立两个线程,一个运行 mail 程序,另一个暴力猜测和尝试占用CPU分片时间,从而改写/读出敏感文件(例如写入 /etc/passwd 来提权)。这里只给出简化后的核心代码:

int try(void)
{switch (pid = fork()){case 0:execl("/bin/mail", "mail", NULL);case -1:exit(EXIT_FAILURE);default:{tmp_file_name = guess_name(pid);for(char c = 'a'; c <= 'z'; ++c){tmp_file_name[7] = c;symlink(sys_file, tmp_file_name);}wait(NULL);}}
}			
 

题外话:在周先生的代码中,出现了几次return_t function()这样的声明,以表示该函数不需要参数,这是一个不好的编程习惯,还是应该使用 void 关键词明确。

关于两者的区别可以参考function declaration isn't a prototype

 

漏洞修复

解决问题的本质方法是消除/阻止竞争,例如采取不使用临时文件,或者对临时文件产生的目录进行权限限制等。

当然也可以采取监视的措施,在竞争发生的时候立即失败(fail fast)。例如,mktemp 后在调用 open 的时候使用 O_EXCL (C库中对应 fopen的 x标志,内核能够保证创建操作是原子的),以确保该文件确实是由应用程序创建的。不过C库中已经有封装好的函数了——mkstemp .

下面是4.3BSD修复这个漏洞后的大致写法,可以看到它使用了 mkstemp 而非 mktemp :

	// ...... omittedi = mkstemp(lettmp);tmpf = fdopen(i, "r+w");if (i < 0 || tmpf == NULL)panic("mail: %s: cannot open for writing", lettmp);/** This protects against others reading mail from temp file and* TOCTTOU attack. And if we exit, the file will be deleted already.*/unlink(lettmp);if (argv[0][0] == 'r')rmail++;if (argv[0][0] != 'r' &&	/* no favors for rmail*/(argc == 1 || argv[1][0] == '-' && !any(argv[1][1], "rhd")))printmail(argc, argv);elsebulkmail(argc, argv);done();
}
 
 

TOCTTOU攻击之single-stepping

可以看到,TOCTTOU攻击要求攻击者能够准确的把握时间(CPU分片),例如在上节的例子中,攻击者需要让 symlink 在应用程序 unlink 和 fopen 中执行——我们采取的是暴力的尝试,但这也仅需要几十秒(参见周先生的论文)。这种对程序间的操作/调用进行先后安排的行为被称为“single-stepping”。

single-stepping的技术包括“文件系统迷惑(file system mazes)”和”算法复杂度攻击(algorithmic complexity attacks)“等。其中文件系统迷惑是强制应用程序读取一个不再操作系统缓冲中的目录入口,以此让操作系统将应用程序置于睡眠状态(随后从磁盘中读取)。而算法复杂度攻击是强制应用程序浪费掉操作系统为它分配的CPU资源,例如攻击者可以创建一大堆hash值相同的文件,从而导致内核hash表的链非常长,然后让用户去索引相关的文件,耗费其资源(在拒绝服务的漏洞中也有这种攻击)。

 
 

如何防范TOCTTOU漏洞

尽管概念上很简单,但是TOCTTOU通常很难被避免。一种广泛使用的策略是遵循EAFP "It is easier to ask for forgiveness than permission"(不去检查而是引起并捕获异常)而不是 LBYL"look before you leap"(判断后再决策)。在上面的例子中,我们使用 mkstemp 就是一种”ask for forgiveness“的行为,只不过C没有专门的异常处理机制罢了。

对于文件系统中的TOCTTOU,最迫切的挑战是如何确保两个系统调用之间文件系统不发生改变——但在2004年的一篇会议报告显示,并不可能存在可移植的技术能完全避免TOCTTOU在unix上的存在。

不过,IBM的研究员在随后开发出了一些能够追踪并检查 文件描述符库,他们能够一定程度上避免TOCTTOU。

另一种解决方案是在文件系统或/内核中引入事务机制(transaction),以此为操作系统提供 并发控制 的抽象层。不过目前这种方案没有得到广泛的使用(微软在NTFS中引入了这种机制,但并不推荐客户使用,并且可能在未来的版本中去除掉)。

文件锁 是另一种广泛使用的防止竞争的机制。但是它不会对元数据或者在文件系统的名称空间中起作用(也不能对网络文件进行操作)。

对于 setuid 程序,另一种可能的解决方案是在 open() 前使用 seteuid()修改用户的effective user id,但是这会带来一些兼容性上的问题。

 
 

回到开头的 at 后缀

那么,"前言"中提到的带 at 后缀的系统调用是防止哪一类的TOCTTOU攻击的呢?又是怎么防止的呢?

简单点说,它们是为了防止在用相对路径使用访问文件时,不同进程/线程对文件夹访问的竞争

 

我们就拿 openat() 来解释:

int open(const char *path, int oflag, ...);
int openat(int fd, const char *path, int oflag, ...);

可以看到其就比 open() 多一个文件描述符,而这个文件描述符是一个文件夹的句柄,以作为 path 的相对路径。POSIX 定义的openat() 工作原理如下,这里就不翻译了:

The openat() function shall be equivalent to the open() function except in the case where path specifies a relative path. In this case the file to be opened is determined relative to the directory associated with the file descriptor fd instead of the current working directory. If the file descriptor was opened without O_SEARCH, the function shall check whether directory searches are permitted using the current permissions of the directory underlying the file descriptor. If the file descriptor was opened with O_SEARCH, the function shall not perform the check.

The oflag parameter and the optional fourth parameter correspond exactly to the parameters of open().

If openat() is passed the special value AT_FDCWD in the fd parameter, the current working directory shall be used and the behavior shall be identical to a call to open().

要注意的是,O_SEARCH在Linux内核中没有实现。

 

当我们运行一个进程时,内核会为其维护一个链表结构的数据(图片来自维基百科):

File_table_and_inode_table

其中描述符是进程可以拿来访问资源的句柄(handle),File table中记录了进程的权限和相关资料,而Inode table记录了文件在磁盘中的地址。我们知道,在unix文件系统中,很少进行“真正”的删除,而是像 unlink() 这样删除文件夹中的链接;对于移动和重命名也是一样,也是改变文件夹中的链接和内容,数据没有移动。所以当我们打开一个文件并获得描述符后,如果这个文件被另一个进程“删除”或者“重命名”或者“移动”,我们依然可以通过上面数据结构的索引访问到打开时对应的数据。也就是说,在文件描述符被修改(例如dup)和关闭前,这个句柄是不可能指向其他位置的数据/inode的。

 

现在考虑这样一种攻击,“setuid受害者”进程会在 open() 中使用了相对路径 ./foo.txt 打开一个文件,并随后进行操作。攻击者可以将“受害者”进程的工作目录修改指向为敏感目录,从而修改不同文件。但如果我们能够保证有一个确定合法的文件夹句柄(文件描述符),然后使用 openat() 访问相对的文件,那么即使攻击者修改了“那个”目录,我们在进程中依然可以确保访问文件夹/文件是合法的。

另外,在 Advanced Programming in the UNIX 3rd中,作者也提到 openat 可以让不同的线程对不同文件夹中文件的操作变得方便,因为 open 只能相对于当前工作目录,而所有线程的工作目录是一样的。

 
 

参考:

  1. Race condition
  2. Time of check to time of use
  3. mktemp(3) - Linux man page
  4. mkdtemp(3) - Linux manual page
  5. Fixing races for fun and profit: how to abuse atime
  6. A Security Loophole in Unix
  7. opengroup
  8. Advanced Programming in the UNIX 3rd

这篇关于关于TOCTTOU攻击的简介的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1122934

相关文章

ASIO网络调试助手之一:简介

多年前,写过几篇《Boost.Asio C++网络编程》的学习文章,一直没机会实践。最近项目中用到了Asio,于是抽空写了个网络调试助手。 开发环境: Win10 Qt5.12.6 + Asio(standalone) + spdlog 支持协议: UDP + TCP Client + TCP Server 独立的Asio(http://www.think-async.com)只包含了头文件,不依

业务协同平台--简介

一、使用场景         1.多个系统统一在业务协同平台定义协同策略,由业务协同平台代替人工完成一系列的单据录入         2.同时业务协同平台将执行任务推送给pda、pad等执行终端,通知各人员、设备进行作业执行         3.作业过程中,可设置完成时间预警、作业节点通知,时刻了解作业进程         4.做完再给你做过程分析,给出优化建议         就问你这一套下

容器编排平台Kubernetes简介

目录 什么是K8s 为什么需要K8s 什么是容器(Contianer) K8s能做什么? K8s的架构原理  控制平面(Control plane)         kube-apiserver         etcd         kube-scheduler         kube-controller-manager         cloud-controlle

速盾高防cdn是怎么解决网站攻击的?

速盾高防CDN是一种基于云计算技术的网络安全解决方案,可以有效地保护网站免受各种网络攻击的威胁。它通过在全球多个节点部署服务器,将网站内容缓存到这些服务器上,并通过智能路由技术将用户的请求引导到最近的服务器上,以提供更快的访问速度和更好的网络性能。 速盾高防CDN主要采用以下几种方式来解决网站攻击: 分布式拒绝服务攻击(DDoS)防护:DDoS攻击是一种常见的网络攻击手段,攻击者通过向目标网

【Tools】AutoML简介

摇来摇去摇碎点点的金黄 伸手牵来一片梦的霞光 南方的小巷推开多情的门窗 年轻和我们歌唱 摇来摇去摇着温柔的阳光 轻轻托起一件梦的衣裳 古老的都市每天都改变模样                      🎵 方芳《摇太阳》 AutoML(自动机器学习)是一种使用机器学习技术来自动化机器学习任务的方法。在大模型中的AutoML是指在大型数据集上使用自动化机器学习技术进行模型训练和优化。

SaaS、PaaS、IaaS简介

云计算、云服务、云平台……现在“云”已成了一个家喻户晓的概念,但PaaS, IaaS 和SaaS的区别估计还没有那么多的人分得清,下面就分别向大家普及一下它们的基本概念: SaaS 软件即服务 SaaS是Software-as-a-Service的简称,意思是软件即服务。随着互联网技术的发展和应用软件的成熟, 在21世纪开始兴起的一种完全创新的软件应用模式。 它是一种通过Internet提供

LIBSVM简介

LIBSVM简介 支持向量机所涉及到的数学知识对一般的化学研究者来说是比较难的,自己编程实现该算法难度就更大了。但是现在的网络资源非常发达,而且国际上的科学研究者把他们的研究成果已经放在网络上,免费提供给用于研究目的,这样方便大多数的研究者,不必要花费大量的时间理解SVM算法的深奥数学原理和计算机程序设计。目前有关SVM计算的相关软件有很多,如LIBSVM、mySVM、SVMLight等,这些

urllib与requests爬虫简介

urllib与requests爬虫简介 – 潘登同学的爬虫笔记 文章目录 urllib与requests爬虫简介 -- 潘登同学的爬虫笔记第一个爬虫程序 urllib的基本使用Request对象的使用urllib发送get请求实战-喜马拉雅网站 urllib发送post请求 动态页面获取数据请求 SSL证书验证伪装自己的爬虫-请求头 urllib的底层原理伪装自己的爬虫-设置代理爬虫coo

新一代车载(E/E)架构下的中央计算载体---HPC软件架构简介

老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节能减排。 无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事.而不是让内心的烦躁、焦虑、毁掉你本就不多的热情和定力。 时间不知不觉中,快要来到夏末秋初。一年又过去了一大半,成

AI学习指南深度学习篇-带动量的随机梯度下降法简介

AI学习指南深度学习篇 - 带动量的随机梯度下降法简介 引言 在深度学习的广阔领域中,优化算法扮演着至关重要的角色。它们不仅决定了模型训练的效率,还直接影响到模型的最终表现之一。随着神经网络模型的不断深化和复杂化,传统的优化算法在许多领域逐渐暴露出其不足之处。带动量的随机梯度下降法(Momentum SGD)应运而生,并被广泛应用于各类深度学习模型中。 在本篇文章中,我们将深入探讨带动量的随