介绍
条件竞争漏洞发生在多个线程同时访问同一个共享代码、变量、文件等,但没有进行锁操作或者同步操作的场景中。这个漏洞存在于操作系统、数据库、web等多个层面,像有名的脏牛(dirty cow)。
条件竞争漏洞属于服务器端漏洞,由于大多服务端框架在处理不同用户的请求时是并发进行的,而开发者在进行代码开发时常常倾向于认为代码会以线性的方式执行,而忽视了并行服务器会并发执行多个线程,这就会导致意想不到的结果;
[!tip]
简单来说,就是多线程同时操作一个对象,而没有对对象进行加锁等保证一致性的操作
举例
以python多线程代码举一个不恰当的例子
#!/usr/bin/env python
from concurrent.futures import ThreadPoolExecutor
def test():
global globalNum
if globalNum >= 5:
print("globalNum >= 5 now")
globalNum -= 1
if __name__ == '__main__':
globalNum = 5
pool = ThreadPoolExecutor(max_workers=10)
for _ in range(5):
pool.submit(test)
咋一看,globalNum
初始值为5,一个5次的for循环,每次globalNum-1
,那么按理说只有第一次调用test
函数的时候print
会有输出才对,但是我们运行起来看看结果,却有2次print
输出
而产生这种结果的原因,就是多线程同时操作变量globalNum
,globalNum
还没来得及完成修改就被带入到另一个test()
函数中,也就是对并发操作的敏感变量没有加锁保护等。
应用场景
[!note]
结合参数在后端的处理过程,所有后端应该对数据进行加锁或者同步的功能点,都可能存在此漏洞,如购买、签到、转账、兑换等
总的来说有如下几类:
- 购买/兑换操作
- 绕过次数限制
- 绕过多过程处理
下面举几个例子抛砖引玉
购买
假设:用户A有100元,要买一件100元的商品
且后端处理流程:判断A的余额是否>=100
==> A的余额-100,商品数量+1
(没有对A的余额进行加锁操作)
正常情况下,A购买完一件商品余额就清零了,但攻击者通过并发发起20个请求,后端接收到后,也会并发发起20次上述的处理流程,而在同一时间(A的余额-100之前
)去判断A的余额是否>=100
,肯定都是满足的,那么商品数量就会多次+1
,也就达到了100元购买多件100元商品的目的
[!tip]
这个漏洞具有偶现性,很受环境因素的影响,比如网络延迟、服务器的处理能力等,所以只执行一次可能并不会成功,尽量多尝试几次
绕过次数操作
最容易想到的就是绕过签到次数限制,大多数平台都是一天只允许签到一次,如果后端对是否签到
的判断不严,那么我们就可以通过并发达到一天签到多次的目的。这里举一个之前类似的挖到的某网盘绕过大小限制的操作。
前提:该云盘普通用户只允许上传10G空间,想要获得更大的空间就需要开会员
猜测后端处理过程:识别上传文件大小
==> 当前已用空间+上传文件大小是否>=10G
==> <=10G,上传成功 / >10G,上传失败
我的测试过程:怀疑上传过程中,后端可能没有对已用空间
做加锁机制来防止并发操作带来的条件竞争问题,因此我先上传了1G的文件,然后50个线程并发复制这1个文件,最终成功上传了超过10G的文件到服务器中。
绕过多过程处理
这个也是大家听说过最多的,常用的场景:上传webshell时,服务端会先存储该文件,然后判断文件内容是否包含恶意内容,如果包含就删除;
我们可以通过并发不停的向服务器上传webshell,虽然服务器会不断的检查并删除我们上传的文件,但由于我们在一直不间断的上传,服务器可能会还没来得及删除webshell,我们就已经执行了相关的命令达到目的了。