安全矩阵

 找回密码
 立即注册
搜索
查看: 677|回复: 0

PHP代码审计-某cms逻辑漏洞导致getshell

[复制链接]

252

主题

252

帖子

1309

积分

金牌会员

Rank: 6Rank: 6

积分
1309
发表于 2023-3-17 22:54:56 | 显示全部楼层 |阅读模式
原文链接:PHP代码审计-某cms逻辑漏洞导致getshell

文章首发在:奇安信攻防社区
https://forum.butian.net/share/2142
前言 如果存在exec进行拼接的漏洞,该如何绕过 <mark>一黑俩匹配 </mark>?当前如果是拼接和编码这种手法就不说了,现在在看的师傅您是审计大牛的话,这文章可以忽略不看。黑名单...
前言
如果存在exec进行拼接的漏洞,该如何绕过 <mark>一黑俩匹配 </mark>?
当前如果是拼接和编码这种手法就不说了,现在在看的师傅您是审计大牛的话,这文章可以忽略不看。
黑名单
  1. $_GET[
  2. $_POST[
  3. $_REQUEST[
  4. $_COOKIE[
  5. $_SESSION[
  6. file_put_contents
  7. file_get_contents
  8. fwrite
  9. phpinfo
  10. base64
  11. `
  12. shell_exec
  13. eval
  14. assert
  15. system
  16. exec
  17. passthru
  18. pcntl_exec
  19. popen
  20. proc_open
  21. print_r
  22. print
  23. urldecode
  24. chr
  25. include
  26. request
  27. __FILE__
  28. __DIR__
  29. copy
  30. call_user_
  31. preg_replace
  32. array_map
  33. array_reverse
  34. array_filter
  35. getallheaders
  36. get_headers
  37. decode_string
  38. htmlspecialchars
  39. session_id
  40. strrev
  41. substr
  42. php.info
复制代码

第一个匹配:
  1. /([\w]+)([\x00-\x1F\x7F\/\*\<\>\%\w\s\\\\]+)?\(/i
复制代码

第二个匹配:
这里不能有$符号,这里是重点 ,当然如果你想编码也可以,或者啥的手法都行,不过我在此之前没想到过,可以继续往下看
  1. /\{pboot:if\(([^}^\$]+)\)\}([\s\S]*?)\{\/pboot:if\}/
复制代码

正文
先看效果


审计流程
通过审计工具半自动筛选出漏洞点。
跟进该漏洞点文件:apps/home/controller/ParserController.php
最终是通过了$matches[1][$i]进入到eval函数中。
第一个黑名单
接着往上看,这里有黑名单,如果$matches[1][$i]有黑名单就会跳出解析
<mark>这里的\\是社区编辑器默认加上防止转义,太多就懒得改了</mark>
  1. // 过滤特殊字符串

  2. if (preg\_match('/(\\(\[\\w\\s\\.\]+\\))|(\\$\_GET\\\[)|(\\$\_POST\\\[)|(\\$\_REQUEST\\\[)|(\\$\_COOKIE\\\[)|(\\$\_SESSION\\\[)|(file\_put\_contents)|(file\_get\_contents)|(fwrite)|(phpinfo)|(base64)|(\`)|(shell\_exec)|(eval)|(assert)|(system)|(exec)|(passthru)|(pcntl\_exec)|(popen)|(proc\_open)|(print\_r)|(print)|(urldecode)|(chr)|(include)|(request)|(\_\_FILE\_\_)|(\_\_DIR\_\_)|(copy)|(call\_user\_)|(preg\_replace)|(array\_map)|(array\_reverse)|(array\_filter)|(getallheaders)|(get\_headers)|(decode\_string)|(htmlspecialchars)|(session\_id)|(strrev)|(substr)|(php.info)/i', $matches\[1\]\[$i\])) {

  3. $danger = true;

  4. }

  5. // 如果有危险函数,则不解析该IF

  6. if ($danger) {

  7. continue;

  8. }
复制代码
黑名单分别是拦截以下内容:
  1. $\_GET\[

  2. $\_POST\[

  3. $\_REQUEST\[

  4. $\_COOKIE\[

  5. $\_SESSION\[

  6. file\_put\_contents

  7. file\_get\_contents

  8. fwrite

  9. phpinfo

  10. base64

  11. \`

  12. shell\_exec

  13. eval

  14. assert

  15. system

  16. exec

  17. passthru

  18. pcntl\_exec

  19. popen

  20. proc\_open

  21. print\_r

  22. print

  23. urldecode

  24. chr

  25. include

  26. request

  27. \_\_FILE\_\_

  28. \_\_DIR\_\_

  29. copy

  30. call\_user\_

  31. preg\_replace

  32. array\_map

  33. array\_reverse

  34. array\_filter

  35. getallheaders

  36. get\_headers

  37. decode\_string

  38. htmlspecialchars

  39. session\_id

  40. strrev

  41. substr

  42. php.info
复制代码

这可以看出过滤了好多函数,当然既然是黑名单就有绕过的方式,这里可以是加密形式绕过,不过加密后的密文做成payload就逆向解密不了了,因为是由特殊不可见数据流存在就会导致反解密会不到原来的明文。
这里可以用file和fputs函数绕过
第一个过滤
继续往上看,看到这个if判断,这里也是将$matches[1][$i]进行过滤,保证用户输入的字符串是无危害的,简单来说就是‘括号前面不能有字母、数字字符串’。

  1. // 带有函数的条件语句进行安全校验

  2. if (preg\_match\_all('/(\[\\w\]+)(\[\\x00-\\x1F\\x7F\\/\\\*\\<\\>\\%\\w\\s\\\\\\\\\]+)?\\(/i', $matches\[1\]\[$i\], $matches2)) {

  3. foreach ($matches2\[1\] as $value) {

  4. if (function\_exists(trim($value)) && ! in\_array($value, $white\_fun)) {

  5. $danger = true;

  6. break;

  7. }

  8. }

  9. foreach ($matches2\[2\] as $value) {

  10. if (function\_exists(trim($value)) && ! in\_array($value, $white\_fun)) {

  11. $danger = true;

  12. break;

  13. }

  14. }

  15. }
复制代码




当然这里也是黑名单,直接/*--*/绕过

4.3. 第三个过滤
这个就比较好过了就是指定的标签语法,使用这个{pboot:if(312313)}(13123){/pboot:if}
4.3.1. 注意:
这里不能有$符号,这里是重点

  1. $pattern = '/\\{pboot:if\\((\[^}^\\$\]+)\\)\\}(\[\\s\\S\]\*?)\\{\\/pboot:if\\}/';

  2. if (preg\_match\_all($pattern, $content, $matches)) {

  3. }
复制代码



构建payload
由于这里不能用美元符号”$“,前面第一个过滤说过,可用file函数绕过,如下图:



通过上面file函数获取的美元符号,并且通过fputs进行写文件,当然需要绝对路径才能读取美元符,这里就比较简单了,直接让cms报错就好了。


调用链

  1. ParserController.php:84, app\\home\\controller\\ParserController->parserAfter()

  2. TagController.php:47, app\\home\\controller\\TagController->index()

  3. IndexController.php:50, app\\home\\controller\\IndexController->\_empty()

  4. 2:2, core\\basic\\Kernel::axqjlxzuuxaapu328937ae1368b88e8bf79cb6b342866a()

  5. 2:2, core\\basic\\Kernel::run()

  6. start.php:17, require()

  7. index.php:23, {main}()
复制代码

访问首页就会进入到apps/home/controller/IndexController.php的_empty()方法,需要get的参数带有tag就可进入该判断


跟进到apps/home/controller/TagController.php的inde()方法,跟进第47行并跟进到apps/home/controller/ParserController.phpparserAfter()方法,最后就会到84行的漏洞方法中。
目前新版本未发现有该处函数


















本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-11-28 21:40 , Processed in 0.016209 second(s), 19 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表