Environment Setup
下载代码文件,并且关闭 ubuntu
针对竞态条件攻击的内置保护。
sudo sysctl -w fs.protected_symlinks=0 |
创建 /tmp/XYZ
文件,并将其链接到 /dev/null
文件。然后编译代码什么的。
# Task 1: Choosing Our Target
如果我们向 /etc/passwd
文件中写入一个条目,就相当于添加了一个用户,把第三个参数设置为 0 就相当于把用户设为了 root
用户。第二个参数如果是 x
那么就说明从 /etc/shadow
中查找密码,也可以直接设置密码,但是需要是密码的哈希值。在 Ubuntu live CD
中有一个用于无密码帐户的神奇值,这个神奇值是 U6aMy0wojraho
,我们把第二个参数设置为这个就可以得到一个不需要密码的 root
用户。
我们可以切换到 root
用户,在 /etc/passwd
文件最后面添加 test:U6aMy0wojraho:0:0:test:/root:/bin/bash
然后执行 su test
(跳出输入密码直接回车即可),查看这个神奇值在我们的设备上是否生效。
注意,记得把 passwd
文件备份,避免后续实验导致文件丢失。
Task 2: Launching the Race Condition Attack
# Task 2.A: Simulating a Slow Machine
我们在被攻击代码 access
和 fopen
之间添加一个 sleep(10);
,然后编译执行代码,在 sleep
期间,执行 ln -sf /etc/passwd /tmp/XYZ
,这样,在确认用户可以访问 XYZ
后,XYZ
被软链接到了 passwd
文件,后续因为被攻击程序是个特权程序,因此 open
和 write
都会以特权程序执行。从而实现了对 passwd
文件的恶意修改。
# Task 2.B: The Real Attack
首先写我们的攻击程序,这段代码不断的把 XYZ
链接到特权程序和非特权程序:
int main() { |
然后,执行脚本,脚本的作用就是不断向 XYZ
中写入 test:U6aMy0wojraho:0:0:test:/root:/bin/bash
(注意自己修改),在大部分时间要么写到了 /dev/null
中,要么不能写入,因为没有权限。
但是,当两个程序并发时(通过开两个终端同时执行之),在 XYZ
是 /dev/null
时通过了 access
检查,然后 XYZ
切换到了 passwd
,再执行 fopen
和后面的 fwrite
,那么就修改了 passwd
,得到了 root
权限。
下面的截图结果我做了点弊,我在 access
和 fopen
之间添加了一点时间,用来让竞态漏洞更好的被执行。如果不加时间,太容易 XYZ
被修改为 root
程序导致我们得把他删了重做。很麻烦,需要大量的重复工作来碰运气。
# Task 2.C: An Improved Attack Method
上面说到,XYZ
太容易变成 root
所属的文件,这个结果也是因为竞态漏洞。当 XYZ
链接到 null
时通过了 access
,然后执行了 unlink
,这时 XYZ
没有链接到任何一个文件,CPU
切换到 fopen
,这时一个特权程序打开这个空文件,将创建一个以 root
作为所有者的新文件。
因此,我们需要让 XYZ
从链接到 null
到 passwd
这个过程是原子的,我们可以使用 renameat2
这个系统调用来交换两个文件的软链接,这个操作是原子操作。这样就不会导致上面那样的问题了。
|
这样的话,执行之后一定能够成功:
Task 3: Countermeasures
# Task 3.A: Applying the Principle of Least Privilege
最小权限原则,我们可以把这个特权程序的权限降为真实用户权限,然后再执行:
FILE* fp; |
这样,就算我们上面分析的竞态条件发生,调用 open
函数也会没有权限打开 passwd
文件,因为这已经不是个特权程序了。
# Task 3.B: Using Ubuntu’s Built-in Scheme
和上面一样,在竞态条件发生的情况下,会报 open failed
(我确信我已经注释过了刚刚添加的代码,并且重新编译过了)。但是,这个比上面 open failed
的比例高得多。测试后发现,XYZ -> /dev/null
的情况也会报这个错,所以这里是 1:1 的报错。上面是少数的 open failed
报错。