141 lines
4.7 KiB
Python
Executable File
141 lines
4.7 KiB
Python
Executable File
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
|
Keysword is a utility for extracting Filevault2 keys from JAMF JSS
|
|
~~~~~~~~~~~~
|
|
This module implements the Requests API.
|
|
:copyright: (c) 2019 by Mike Mackintosh.
|
|
:license: Apache2, see LICENSE for more details.
|
|
"""
|
|
|
|
import requests
|
|
import cookielib
|
|
import argparse
|
|
from os import getenv
|
|
from xml.dom.minidom import parseString
|
|
|
|
__VERSION__ = "1.0"
|
|
|
|
JAMF_HOST = getenv("JAMF_HOST")
|
|
JAMF_USERNAME = getenv("JAMF_USERNAME")
|
|
JAMF_PASSWORD = getenv("JAMF_PASSWORD")
|
|
|
|
|
|
"""
|
|
getComputerID
|
|
-------------
|
|
Converts a computer name to a computer ID using the JAMF API
|
|
:param name string
|
|
:return id string
|
|
"""
|
|
def getComputerID(name):
|
|
auth = requests.auth.HTTPBasicAuth(JAMF_USERNAME, JAMF_PASSWORD)
|
|
headers = {"Accept": "application/json"}
|
|
resp = requests.get("{}/JSSResource/computers/name/{}".format(JAMF_HOST, name), auth=auth, headers=headers)
|
|
|
|
# TODO: add error checking here
|
|
return resp.json()["computer"]["general"]["id"]
|
|
|
|
|
|
"""
|
|
getSessionToken
|
|
-------------
|
|
Gets a session token from the legacy pages to get access to the ajax API
|
|
:param s requests.Session
|
|
:param jar cookie jar
|
|
:return token string
|
|
"""
|
|
def getSessionToken(s, jar, id):
|
|
resp = s.post('{}/?failover'.format(JAMF_HOST), cookies=jar, data={'username':JAMF_USERNAME, 'password':JAMF_PASSWORD, 'resetUsername':''})
|
|
if resp.status_code != 200:
|
|
print "Looks like you failed to authenticate"
|
|
exit(1)
|
|
|
|
params = {"id": id, "o": "r", "v": "management"}
|
|
resp = s.get('{}/legacy/computers.html'.format(JAMF_HOST), params=params, cookies=jar)
|
|
session_token = ""
|
|
|
|
# TODO: add error checking here
|
|
for line in resp.content.splitlines():
|
|
if "session-token" in line:
|
|
return line.encode('utf-8').translate(None, '<>"').split('=')[-1]
|
|
print "Unable to find session token"
|
|
|
|
|
|
"""
|
|
main
|
|
-------------
|
|
Connects to the JAMF JSS API and extracts a filevault key for the provided device ID
|
|
:param id string
|
|
:param name string
|
|
"""
|
|
def main(id, name):
|
|
""" Create the cookie jar and session for requests """
|
|
jar = cookielib.CookieJar()
|
|
s = requests.Session()
|
|
s.cookies = jar
|
|
|
|
""" Get the computer id if a hostname was provided """
|
|
if len(name) > 0:
|
|
id = getComputerID(name)
|
|
|
|
""" Get the session token """
|
|
session_token = getSessionToken(s, jar, id)
|
|
if len(session_token) == 0:
|
|
print "Unable to find session token"
|
|
exit()
|
|
|
|
""" Make the request against the computers.ajax endpoint """
|
|
data = "&ajaxAction=AJAX_ACTION_READ_FILE_VAULT_2_KEY&session-token={}".format(session_token)
|
|
resp = s.post('{}/computers.ajax?id={}&o=r&v=management'.format(JAMF_HOST, id), data="{}".format(data), cookies=jar, headers={
|
|
"X-Requested-With": "XMLHttpRequest",
|
|
"Origin": JAMF_HOST,
|
|
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
|
|
"Accept": "*/*",
|
|
"Content-Length": "{}".format(len(data)),
|
|
"Accept-Encoding": "gzip, deflate, br",
|
|
"Accept-Language": "en-US,en;q=0.9",
|
|
"Connection": "keep-alive",
|
|
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.81 Safari/537.36",
|
|
"Referer": "{}/legacy/computers.html?id={}&o=r&v=management".format(JAMF_HOST, id)
|
|
})
|
|
|
|
# TODO: add error checking here
|
|
e = parseString(resp.content)
|
|
if len(e.getElementsByTagName("individualKey")) > 0:
|
|
for n in e.getElementsByTagName("individualKey")[0].childNodes:
|
|
if n.nodeType == n.TEXT_NODE:
|
|
print n.data
|
|
return
|
|
|
|
""" If we couldn't find the key, print """"
|
|
print "Unable to find filevault key"
|
|
|
|
|
|
""" main """
|
|
if __name__ == "__main__":
|
|
|
|
""" Create Argparser """
|
|
parser = argparse.ArgumentParser(prog='keysword')
|
|
parser.add_argument('-id', action="store", default="", dest="id")
|
|
parser.add_argument('-name', action="store", default="", dest="name")
|
|
parser.add_argument('--version', action='version', version='%(prog)s {}'.format(__VERSION__))
|
|
results = parser.parse_args()
|
|
|
|
""" Quick validation of arguments """
|
|
if len(results.id) == 0 and len(results.name) == 0:
|
|
print "Please provide a computer id with -id or computer name with -name"
|
|
exit()
|
|
|
|
elif len(results.id) != 0 and len(results.name) != 0:
|
|
print "Please provide only one computer id with -id or with -name, but not both"
|
|
exit()
|
|
|
|
if len(getenv("JAMF_USERNAME")) == 0 \
|
|
or len(getenv("JAMF_USERNAME")) == 0 \
|
|
or len(getenv("JAMF_HOST")) == 0:
|
|
print "Please set your environment variables appropriately"
|
|
exit()
|
|
|
|
""" run main """
|
|
main(results.id, results.name) |