-
Notifications
You must be signed in to change notification settings - Fork 10
/
snap-intercepter.py
114 lines (92 loc) · 2.79 KB
/
snap-intercepter.py
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
import urlparse
from Crypto.Cipher import AES
# Globals
LOG_FILE = 'logs.txt'
snapchat_aes_key = 'M02cnQ51Ji97vwT4'
DOMAINS = ['feelinsonice-hrd.appspot.com', 'data.flurry.com']
def start(context, flow):
myfile = open(LOG_FILE, 'a')
try:
myfile.write('----- starting interception -----\n')
finally:
myfile.close()
return
def response(context, flow):
# If the data is not from Snapchat, skip analysis.
if not (flow.response.request.host in DOMAINS):
return
# opens log file
myfile = open(LOG_FILE, 'a')
try:
path = str(flow.request.path)
# received snaps
if path == '/bq/blob':
content = str(flow.response.request.content)
form = urlparse.parse_qs(content)
# lets get our picId to have a filename
picId = str(form['id'][0])
# actual encrypted blob
blob = str(flow.response.content)
# saves and decrypts snap to a given pictId
saveSnap(blob, picId)
finally:
myfile.close()
def request(context, flow):
# If the data is not from Snapchat, skip analysis.
if not (flow.request.host in DOMAINS):
return
myfile = open(LOG_FILE, 'a')
try:
path = str(flow.request.path)
form = str(flow.request.content)
# Start processing the request.
if path == '/bq/upload':
# Handling multipart form is a pain
# trimming down to encrypted blob
mediaIdString = 'Content-Disposition: form-data; name="media_id"'
tmp = form.find(mediaIdString)
mediaId = form[(tmp + len(mediaIdString)):]
# more trimming down to encrypted blob
mediaIdStringAfter = 'Content-Disposition: form-data; name="req_token"'
tmp = mediaId.find(mediaIdStringAfter)
mediaId = mediaId[:(tmp - len('--Boundary+XXXXXXXXXXXXXXXXX') - 2)].strip()
# more trimming!
dataString = 'Content-Type: application/octet-stream'
test = form.find(dataString)
data = form[(test + len(dataString)):]
# and even more trimming
extras = '--Boundary'
loc = data.find(extras)
tmp = data[:loc]
data = tmp
data = data.strip()
# saves and decrypts snap
saveSnap(data, mediaId)
finally:
myfile.close()
def end(context, flow):
myfile = open(LOG_FILE, 'a')
try:
myfile.write('----- ending interception -----\n')
finally:
myfile.close()
def decrypt(data):
# using AES_KEY and ECB to decrypt
c = AES.new(snapchat_aes_key, AES.MODE_ECB)
# pkcs5 is used to pad the data
padCount = 16 - len(data) % 16
# actually decrypt data with pad and key
decryptedData = c.decrypt(data + (chr(padCount) * padCount).encode('utf-8'))
return decryptedData
# saves a given snapchat
def saveSnap(data, path):
decryptedData = decrypt(data)
# checks if .jpg
if (decryptedData[:4] == '\xFF\xD8\xFF\xE0'):
ext = '.jpg'
# checks if .mp4
elif (decryptedData[:2] == '\x00\x00'):
ext = '.mp4'
outFile = open("./saved_media/" + path + ext, "w")
outFile.write(decrypt(data))
outfile.close()