当前位置: 代码迷 >> 综合 >> ThinkPHP6.0.1_任意文件写入漏洞分析
  详细解决方案

ThinkPHP6.0.1_任意文件写入漏洞分析

热度:91   发布时间:2023-11-26 21:08:09.0

ThinkPHP6.0.1_任意文件写入漏洞分析

  • ThinkPHP6.0.0-ThinkPHP6.0.1
  • 漏洞代码
    <?php
    namespace app\controller;
    use app\BaseController;
    class Index extends BaseController{
          public function index(){
          $a = $_GET['a'];$b = $_GET['b'];session($a,$b);}
    }
    
  • POC
    /index.php?a=1&b=<?php phpinfo(); ?>
    Cookie
    PHPSESSID=/../../../public/aaaaaaaaaaaa.php
    

漏洞分析

  1. 先在middleware.php开启session初始化
    image-20211128234223046
  2. GET传参就不跟进了,这里直接跟进session方法,$name就是传入的GET参数a,$value就是传入的GET参数b,前面那些if条件都不满足,直接进入else语句块,调用Store::set方法。
    image-20211128234242621
  3. 跟进Store::set方法,其调用了Arr::set方法
    image-20211129001029593
  4. 继续跟进,注意到$array是引用传参,所以在此方法上对$array的操作就相当于对Store类的$this->data的操作。此时$key为GET传参a,$value为GET传参b,因为传参的参数a没有.,所以while条件不满足,然后就是一个键值对赋值,也就是把GET[a]=GET[b]这个数组赋值给到Store类的$this->data。总的来说,index.php文件里的session方法就一个作用,就对Store类的$this->data赋值。
    image-20211129001052788
  5. 这里Session方法就结束了,好像啥也没干,但是别忘了我们最开始设置的session初始化。开启了session初始化,那么就会执行SessionInit::handle方法,那么跟进这个方法
    image-20211129001114628
  6. $varSessionId获取的是session配置中的var_session_id,配置在config/session.php文件,此配置默认为空
    image-20211129001138786
  7. $cookieName获取的是配置中的session name,默认为PHPSESSID,然后第一个if条件判断不满足,进入else分支,这里就是把PHPSESSID这个COOKIE值赋值给$sessionId变量
    image-20211129001154485
  8. 然后进入Store::setId方法,当我们$id,也就是$sessionId为32位的字符串,则把$sessionId赋值给$this->id
    image-20211128234350428
  9. 这好像就结束了?没有写入操作呀,这时利用到public/index.php文件的end方法
    image-20211128234358182
  10. 跟进end方法,其调用了middleware::end方法
    image-20211128234406649
  11. 继续跟进,打断点,这里发现在foreach时有一个$instance值为SessionInit,因为我们在middleware.php文件中定义了SessionInit中间件,那么就会调用SessionInit::end方法
    image-20211128234417476
  12. 跟进,其继续调用了Store::save方法
    image-20211128234426759
  13. 跟进,$sessionId通过getId方法获取后就是前面设置的$this->id,也就是PHPSESSID键的COOKIE值,这里$this->data其实就是前面控制的键值对写入的数组数据,也就是GET[a]=GET[b],然后进行序列化再写入
    image-20211128234435858
  14. 调用File::write进行文件写入,$filename通过getFileName方法获取
    image-20211128234445607
  15. 跟进getFileName方法,prefix默认为空,那么$name就是在前面拼接上sess_。然后加上路径就返回了,也就是$filename我们部分可控
    image-20211128234455762
  16. 回到File::write方法进行文件写入,此时文件名部分可控,文件内容完全可控,可以进行写马
    image-20211128234504361
  17. 注意一下关键点的执行顺序,先进行SessionInit::handle,再执行Stroe::session方法,最后执行SessionInit::end方法。
  18. 但是正常情况下,Session的内容我们是不可控的,那么怎么利用呢?这里利用Store::save方法进行任意文件删除
    image-20211129001258581
  19. 文件删除POC如下
    /index.php
    Cookie
    PHPSESSID=/../../../public/1111111111a.php
    

写在后面

  • 写这个分析过程的时候是边学习边写的,中间有些理解有问题,学习完这条链才发现,然后回去改的。