forked from saleemrashid/frida-sslkeylog
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsslkeylog
executable file
·127 lines (94 loc) · 3.75 KB
/
sslkeylog
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#!/usr/bin/env python3
import os
from frida_tools.application import ConsoleApplication
from asn1crypto.core import Sequence, Integer, OctetString
AGENT_FILENAME = os.path.join(os.path.dirname(os.path.abspath(__file__)), "agent.js")
class Application(ConsoleApplication):
SESSION_ID_LENGTH = 32
MASTER_KEY_LENGTH = 48
def _add_options(self, parser):
# https://docs.python.org/zh-cn/3/library/argparse.html argparse uses add_argument
parser.add_argument("-o", "--output", help="SSL keylog file to write")
def _initialize(self, parser, options, args):
self._file = open(options.output, "a")
def _usage(self):
return "usage: %prog [options] target"
def _needs_target(self):
return True
def _write(self, text):
self._file.write(text)
self._file.flush()
def _start(self):
self._update_status("Attached")
def on_message(message, data):
self._reactor.schedule(lambda: self._on_message(message, data))
self._session_cache = set()
self._script = self._session.create_script(self._agent())
self._script.on("message", on_message)
self._update_status("Loading script...")
self._script.load()
self._update_status("Loaded script")
def _on_message(self, message, data):
if message["type"] == "send":
if message["payload"] == "session":
self._on_session(data)
return
print(message)
def _on_session(self, data):
# decodes the SSL session data, extracts the session ID and master key
try:
asn1_data = PartialSSLSession.load(data)
session_id = asn1_data['sessionId'].native
master_key = asn1_data['masterKey'].native
self._keylog(session_id, master_key)
except Exception as e:
self._log("error", "Exception on decoding: {}".format(e))
# with open("sessionDataDebug.asn1", "wb") as file:
# file.write(data)
def _cache_session(self, session_id):
if session_id in self._session_cache:
return False
self._session_cache.add(session_id)
return True
def _keylog(self, session_id, master_key):
# The hooks can catch the SSL session in an uninitialized state
if not session_id:
self._log("warning", "Uninitialized Session ID: {}".format(master_key.hex()))
return False
if not self._cache_session(session_id):
return
try:
keylog_str = self._keylog_str(session_id, master_key)
except ValueError as e:
self._log("warning", "Ignored key log: {}".format(e))
return
self._log("info", "Logging SSL session: {}".format(keylog_str))
self._write(keylog_str + "\n")
@classmethod
def _keylog_str(cls, session_id, master_key):
if len(session_id) != cls.SESSION_ID_LENGTH:
raise ValueError("Session ID length is incorrect")
if len(master_key) != cls.MASTER_KEY_LENGTH:
raise ValueError("Master Key length is incorrect")
return "RSA Session-ID:{} Master-Key:{}".format(
session_id.hex(),
master_key.hex(),
)
@staticmethod
def _agent():
with open(AGENT_FILENAME) as f:
return f.read()
class PartialSSLSession(Sequence):
_fields = [
('version', Integer),
('privateKeyAlgorithm', Integer),
('privateKey', OctetString), # 2bytes
('sessionId', OctetString), # 32 bytes
('masterKey', OctetString) # 48 bytes
# this is enough for extracting sessionId and masterKey
]
def main():
app = Application()
app.run()
if __name__ == "__main__":
main()