之前那篇终于是写完了 ,但是由于原因(上篇文章里),所以来开续集
web611
网上github下载到源码 gayhub上的源码 然后把thinkphp文件夹换到刚才审计非强制路由的源码里面就可以快乐审计了,把application/index/controller/Index.php
改成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?php namespace app\index\controller;
class Index { public function index() { $u = unserialize($_GET['c']); return 'hhh'; }
public function hello($name = 'ThinkPHP5') { return 'hello,' . $name; } }
|
那么既然已经有反序列化的口子,那我们正常的挖掘一条利用链即可,进行全局搜索__destruct
,在thinkphp/library/think/process/pipes/Windows.php
找到了可以利用的方法
1 2 3 4 5
| public function __destruct() { $this->close(); $this->removeFiles(); }
|
close
没什么用,就是调用一个父类的close来处理文件避免溢出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public function close() { foreach ($this->pipes as $pipe) { fclose($pipe); } $this->pipes = []; }
public function close() { parent::close(); foreach ($this->fileHandles as $handle) { fclose($handle); } $this->fileHandles = []; }
|
那接着看removeFiles()
1 2 3 4 5 6 7 8 9
| private function removeFiles() { foreach ($this->files as $filename) { if (file_exists($filename)) { @unlink($filename); } } $this->files = []; }
|
这很明显会触发__toString
,继续找有没有可利用的这个魔术方法,找到两处感觉可以利用的
/QQ20250220-161729.jpg)
/QQ20250220-161843.jpg)
虽然但是这个东西,怎么都一样的,我们先看thinkphp/library/think/Collection.php
里面的,会到
1 2 3 4 5 6
| public function toArray() { return array_map(function ($value) { return ($value instanceof Model || $value instanceof self) ? $value->toArray() : $value; }, $this->items); }
|
看着好像没有什么可以利用的,看另一个文件
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 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
| public function toArray() { $item = []; $hasVisible = false;
foreach ($this->visible as $key => $val) { if (is_string($val)) { if (strpos($val, '.')) { list($relation, $name) = explode('.', $val); $this->visible[$relation][] = $name; } else { $this->visible[$val] = true; $hasVisible = true; } unset($this->visible[$key]); } }
foreach ($this->hidden as $key => $val) { if (is_string($val)) { if (strpos($val, '.')) { list($relation, $name) = explode('.', $val); $this->hidden[$relation][] = $name; } else { $this->hidden[$val] = true; } unset($this->hidden[$key]); } }
$data = array_merge($this->data, $this->relation);
foreach ($data as $key => $val) { if ($val instanceof Model || $val instanceof ModelCollection) { if (isset($this->visible[$key]) && is_array($this->visible[$key])) { $val->visible($this->visible[$key]); } elseif (isset($this->hidden[$key]) && is_array($this->hidden[$key])) { $val->hidden($this->hidden[$key]); } if (!isset($this->hidden[$key]) || true !== $this->hidden[$key]) { $item[$key] = $val->toArray(); } } elseif (isset($this->visible[$key])) { $item[$key] = $this->getAttr($key); } elseif (!isset($this->hidden[$key]) && !$hasVisible) { $item[$key] = $this->getAttr($key); } }
if (!empty($this->append)) { foreach ($this->append as $key => $name) { if (is_array($name)) { $relation = $this->getRelation($key);
if (!$relation) { $relation = $this->getAttr($key); if ($relation) { $relation->visible($name); } }
$item[$key] = $relation ? $relation->append($name)->toArray() : []; } elseif (strpos($name, '.')) { list($key, $attr) = explode('.', $name); $relation = $this->getRelation($key);
if (!$relation) { $relation = $this->getAttr($key); if ($relation) { $relation->visible([$attr]); } }
$item[$key] = $relation ? $relation->append([$attr])->toArray() : []; } else { $item[$name] = $this->getAttr($name, $item); } } }
return $item; }
|
这里更是没有什么想法,挨着跟进其中的方法
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
| public function getAttr($name, &$item = null) { try { $notFound = false; $value = $this->getData($name); } catch (InvalidArgumentException $e) { $notFound = true; $value = null; }
$fieldName = Loader::parseName($name); $method = 'get' . Loader::parseName($name, 1) . 'Attr';
if (isset($this->withAttr[$fieldName])) { if ($notFound && $relation = $this->isRelationAttr($name)) { $modelRelation = $this->$relation(); $value = $this->getRelationData($modelRelation); }
$closure = $this->withAttr[$fieldName]; $value = $closure($value, $this->data); } elseif (method_exists($this, $method)) { if ($notFound && $relation = $this->isRelationAttr($name)) { $modelRelation = $this->$relation(); $value = $this->getRelationData($modelRelation); }
$value = $this->$method($value, $this->data); } elseif (isset($this->type[$name])) { $value = $this->readTransform($value, $this->type[$name]); } elseif ($this->autoWriteTimestamp && in_array($name, [$this->createTime, $this->updateTime])) { if (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), [ 'datetime', 'date', 'timestamp', ])) { $value = $this->formatDateTime($this->dateFormat, $value); } else { $value = $this->formatDateTime($this->dateFormat, $value, true); } } elseif ($notFound) { $value = $this->getRelationAttribute($name, $item); }
return $value; }
|
这里进行属性获取,其中触发了getData
继续跟进,