Bootstrap

代码审计入门学习

简介

HadSky轻论坛程序为个人原创PHP系统,作者为蒲乐天,后端基于puyuetianPHP框架驱动,前端基于 puyuetianUI框架驱动,默认编辑器为puyuetianEditor富文本编辑器,其他非原创框架及驱动JQuery.js 及Font-Awesome字体库,前后端框架以及编辑器都由作者自研,使用此框架进行开发的系统还有 Yodati答题系统、JvHuo聚货网等。

路由分析

查看入口文件index.php

发现其包含了一个puyuetian.php的文件,跟进看看

发现前台路由文件,跟进一下

阅读代码可知其通过get传入c参数来控制路由,如果不传入该参数,则默认访问phpscript目录下 main.php,否则包含phpscript目录下相应文件实现相应功能。

后台路由在/admin/index.php为入口文件

这里的app其实代表app文件夹, superadmin:index则表示superadmin文件夹下的index.php文件,这样路由就定位到该php文件

这里传递另一个参数 s 来控制调用后台的某些功能点

任意文件写入

全局搜索file_put_contents

这里我们文件内容可控,现在看看哪调用了file_put_contents

switch ($type) {
    case 'edit' :
       if (filetype($path) != 'file') {
          if ($_G['GET']['JSON']) {
             ExitJson('不存在的文件');
          }
          PkPopup('{content:"不存在的文件",icon:2,shade:1,hideclose:1,submit:function(){location.href="index.php?c=app&a=filesmanager:index&path="}}');
       }
       $suffix = substr($path, strrpos($path, '.') + 1);
       if (!InArray($suffixs, $suffix)) {
          if ($_G['GET']['JSON']) {
             ExitJson('不支持的文件格式');
          }
          PkPopup('{content:"不支持的文件格式",icon:2,shade:1,hideclose:1,submit:function(){location.href="index.php?c=app&a=filesmanager:index&path="}}');
       }
       $filecontent1 = file_get_contents($path);
       $filecontent = htmlspecialchars($filecontent1, ENT_QUOTES);
       if ($filecontent1 && !$filecontent) {
          if ($_G['GET']['JSON']) {
             ExitJson('不支持该文件编码,仅支持UTF-8');
          }
          PkPopup('{content:"不支持该文件编码,仅支持UTF-8",icon:2,shade:1,hideclose:1,submit:function(){location.href="index.php?c=app&a=filesmanager:index&path="}}');
       }
       if ($_G['GET']['JSON']) {
          ExitJson($filecontent1, TRUE);
       }
       $path = str_replace('\\', '/', $path);
       $paths = explode('/', $path);
       $path = '';
       for ($i = 0; $i < count($paths); $i++) {
          if ($i == count($paths) - 1) {
             $filename = $paths[$i];
          } else {
             $path .= $paths[$i] . '/';
          }
       }
       ExitGourl('index.php?c=app&a=filesmanager:index&path=' . urlencode(realpath($path)) . '&editbtn=' . md5($filename));
       break;
    case 'save' :
       if (filetype($path) != 'file') {
          ExitJson('不存在的文件');
       }
       $suffix = substr($path, strrpos($path, '.') + 1);
       if (!InArray($suffixs, $suffix)) {
          ExitJson('不支持的文件格式');
       }
       $r = file_put_contents($path, $_POST['filecontent']);
       ExitJson('保存失败:' . $path, $r);
       break;

这里有个switch语句,需要$type=save

这里我们也是可控

再看看$path

这里的path参数有 realpath() 函数包裹和下面代码进行限制,以至于这个点不能直接创建新文件,而只能将任意内容写到存在的php文件中

如果不想覆盖原有php文件其实还可以通过先创建php文件再将文件内容写入到创建的文件中,代码如 下: 通过$type=mkfile,并且传入 mkname 参数即可创建文件,这样我们就可以通过创建的文件进行文件内容的写入

漏洞复现

之所以会说保存错误,是因为这给关键代码并没有被限制

任意文件读取

全局搜索file_get_contents

当$type=edit时会调用file_get_contents函数读取文件内容,而且当我们GET传入json参数时,就会进入ExitJson函数中,该函数将我们读取到的内容通过 json形式进行回显

漏洞复现

文件上传getshell

通过上传.zip压缩包然后自动解压缩来进行模板部署,而且这里可能会检测压缩包中内容是否合法,如果这里检测不完善或者该处压缩包可以伪造的话,那么大概率我们可以通过上传压缩文件来绕过该处的后缀检测,从而上传我们想要上传的文件。

定位文件app\superadmin\phpscript\app.php

这里就是上传zip文件的代码,可以看到这里代码121行的后缀白名单,然后通过ZipArchive class(php自带类库,可以对文件进行压缩与解压缩处理) 下的open方法打开压缩文件;然你通过ZipArchive::extractTo()方法将.zip文件解压到根目录。在此过程 中,并没有对解压后的文件进行后缀的校验

漏洞复现

上传一个php文件的压缩包

;