条件竞争
多个线程同时访问一个共享变量或文件时,由于线程的执行顺序不符合预期而导致最后的执行结果不符合开发者的预期。
session
session,被称为“会话控制”。Session对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的Web页之间跳转时,存储在Session对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。
当用户请求来自应用程序的Web页时,如果该用户还没有会话,则Web服务器将自动创建一个 Session对象。当会话过期或被放弃后,服务器将终止该会话。
session条件竞争
在PHP中,session以文件的形式被存储在服务器中。而session条件竞争就是在服务器创建session文件与删除session文件之间的这段时间内,成功调用session文件。
php.ini中有如下关于session的默认配置:
session.auto_start = off
如果开启这个选项,则PHP在接收请求的时候会自动初始化Session,不再需要执行session_start()。但默认情况下,也是通常情况下,这个选项都是关闭的
session.upload_progress.enabled = on
表示upload_progress功能开始。意味着当浏览器向服务器上传一个文件时,php将会把此次文件上传的详细信息(如上传时间、上传进度等)存储在session当中。
session.upload_progress.cleanup = on
表示当文件上传结束后,php将会立即清空对应session文件中的内容。
session.upload_progress.name = “PHP_SESSION_UPLOAD_PROGRESS”
当一个上传在处理中,同时POST一个与设置的session.upload_progress.name同名变量时(这部分数据用户可控),上传进度可以在SESSION中获得。
session.use_strict_mode=off
选项默认值为off,表示我们对Cookie中sessionid可控。
利用方法
要想利用session文件,我们首先要让服务器端创建session文件。
但是session.auto_start = off
,导致PHP在接收请求时不会自动初始化session,我们的请求没法在后台生成session文件。
但又由于session.use_strict_mode=off
,我们可以在Cookie中自定义Session ID,而当我们定义好Session ID后,php后台也会自动初始化session文件。
比如在Cookie中自定义PHPSESSID=shell,PHP将会在服务器上创建一个文件:/tmp/sess_shell
接下来就可以向服务器发起请求了:
<!DOCTYPE html>
<html>
<body>
<form action="http://065a815f-dc94-4c2f-815f-438de33b2fb1.challenge.ctf.show/" method="POST" enctype="multipart/form-data"><input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="<?php system('ls')?>" /><input type="file" name="file" /><input type="submit" value="submit" />
</form>
</body>
</html>
先创建一个这样的文件上传界面。其中PHP_SESSION_UPLOAD_PROGRESS的值就是我们要执行的恶意命令。进入页面:
随便上传一个文件,抓包,在请求头中添加Cookie,并在Cookie中输入PHPSESSID=shell:
不知道为什么我这里PHP_SESSION_UPLOAD_PROGRESS的值是空的,手动添加一下即可:
然后设置payload值为空,勾选无限重复发包,然后设置上传线程为30,设置方式如下:
然后发包即可。这样就相当于我们不断向服务器发送请求,然后在后台就会不断生成包含了我们输入的恶意代码的session文件。
session文件已经生成,接下来就是进行条件竞争,也就是要在文件被立即删除之前成功包含文件,执行其中的恶意代码。
我们知道文件的位置在/tmp/sess_shell
。我这里使用的是ctfshow一道文件包含题目的靶机,包含的参数为file,所以请求头如下:
依旧设置payload值为空,勾选无限重复发包。不同的是设置上传线程为80.这样包含session文件的线程多于创建session文件的线程,就更可能读取到删除之前的session文件。
找到与其他包长度不同的包,其中就有我们恶意代码执行成功的结果。
执行不同的恶意代码,在请求包中修改恶意代码的形式即可。