Skip to content

Commit 3172ff4

Browse files
author
Robert Nikolai Reith
committed
first version
1 parent 56c6ccd commit 3172ff4

File tree

5 files changed

+140
-0
lines changed

5 files changed

+140
-0
lines changed

Diff for: README.md

+26
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,28 @@
11
# jsmon
22
JavaScript Change Monitor for BugBounty
3+
4+
## Installation
5+
6+
`git clone https://github.com/robre/jsmon.git & cd jsmon & TODO`
7+
8+
9+
## Features
10+
11+
- Keep Track of endpoints - check them in a configurable interval
12+
- when endpoints change - send a notification via slack/telegram/mail?
13+
14+
15+
16+
## Structure
17+
18+
- Provide Endpoints via files in $ENDPOINTS directory (line seperated endpoints)
19+
- Per Endpoint
20+
- Download Endpoints and save whole or hash? (save as its own hash.js)
21+
- Everyday request huge number of js files..
22+
- have a file that tracks hash - endpoint relations. Simple JSON
23+
24+
25+
## Usage
26+
27+
jsmon is designed to keep track of javascript files on websites - but it can be used for any filetype
28+
To add endpoints

Diff for: jsmon.json

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"https://www.heise.de/assets/akwa/v19/js/akwa.js?aeb8be20b00473bc2941": ["fb6fd508d8"], "https://www.heise.de/assets/heise/hohomepage/js/hohomepage.js?92b41930cad76c61c7bc": ["1baa84caf7"], "http://r0b.re/test.js": ["d8e8fca2dc", "71f74d0894", "8a4641220d", "380b224dba"]}

Diff for: jsmon.py

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
#!/usr/bin/env python3
2+
3+
import requests
4+
import re
5+
import os
6+
import hashlib
7+
import json
8+
9+
gEndpoints = {} # global Endpoint List
10+
11+
def is_valid_endpoint(endpoint):
12+
regex = re.compile(
13+
r'^(?:http|ftp)s?://' # http:// or https://
14+
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' #domain...
15+
r'localhost|' #localhost...
16+
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
17+
r'(?::\d+)?' # optional port
18+
r'(?:/?|[/?]\S+)$', re.IGNORECASE)
19+
# check if valid url
20+
return re.match(regex, endpoint) is not None
21+
22+
def get_endpoint_list(endpointdir):
23+
endpoints = []
24+
filenames = []
25+
for (dp, dirnames, files) in os.walk(endpointdir):
26+
filenames.extend(files)
27+
28+
for file in filenames:
29+
with open("{}/{}".format(endpointdir,file), "r") as f:
30+
endpoints.extend(f.readlines())
31+
32+
33+
# Load all endpoints from a dir into a list
34+
return list(map(lambda x: x.strip(), endpoints))
35+
36+
def get_endpoint(endpoint):
37+
# get an endpoint, return its content
38+
r = requests.get(endpoint)
39+
return r.text
40+
41+
def get_hash(string):
42+
# Hash a string
43+
return hashlib.md5(string.encode("utf8")).hexdigest()[:10]
44+
45+
def save_endpoint(endpoint, ephash, eptext):
46+
# save endpoint content to file
47+
# add it to list of
48+
with open("jsmon.json", "r") as jsm:
49+
jsmd = json.load(jsm)
50+
if endpoint in jsmd.keys():
51+
jsmd[endpoint].append(ephash)
52+
else:
53+
jsmd[endpoint] = [ephash]
54+
55+
with open("jsmon.json", "w") as jsm:
56+
json.dump(jsmd,jsm)
57+
58+
with open("downloads/{}".format(ephash), "w") as epw:
59+
epw.write(eptext)
60+
61+
62+
def get_previous_endpoint_hash(endpoint):
63+
# get previous endpoint version
64+
# or None if doesnt exist
65+
with open("jsmon.json", "r") as jsm:
66+
jsmd = json.load(jsm)
67+
if endpoint in jsmd.keys():
68+
return jsmd[endpoint][-1]
69+
else:
70+
return None
71+
72+
73+
def get_file_stats(fhash):
74+
return os.stat("downloads/{}".format(fhash))
75+
76+
def notify(endpoint,prev, new):
77+
print("[!!!] Endpoint [ {} ] has changed from {} to {}".format(endpoint, prev, new))
78+
TELEGRAM_TOKEN = '1216105549:AAHEfqRMGjenWQsFTp5ZXaE3ap-BK8BUBBE'
79+
TELEGRAM_CHAT_ID = '115041299'
80+
81+
prevsize = get_file_stats(prev).st_size
82+
newsize = get_file_stats(new).st_size
83+
log_entry = "{} has been updated from {}({}) to {}({})".format(endpoint, prev,prevsize, new,newsize)
84+
payload = {
85+
'chat_id': TELEGRAM_CHAT_ID,
86+
'text': log_entry,
87+
'parse_mode': 'HTML'
88+
}
89+
return requests.post("https://api.telegram.org/bot{token}/sendMessage".format(token=TELEGRAM_TOKEN),
90+
data=payload).content
91+
92+
def main():
93+
allendpoints = get_endpoint_list('targets')
94+
print(allendpoints)
95+
96+
for ep in allendpoints:
97+
prev_hash = get_previous_endpoint_hash(ep)
98+
ep_text = get_endpoint(ep)
99+
ep_hash = get_hash(ep_text)
100+
if ep_hash == prev_hash:
101+
continue
102+
else:
103+
save_endpoint(ep, ep_hash, ep_text)
104+
if prev_hash is not None:
105+
notify(ep,prev_hash, ep_hash)
106+
else:
107+
print("New Endpoint enrolled: {}".format(ep))
108+
109+
110+
main()

Diff for: targets/heise

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
https://www.heise.de/assets/akwa/v19/js/akwa.js?aeb8be20b00473bc2941
2+
https://www.heise.de/assets/heise/hohomepage/js/hohomepage.js?92b41930cad76c61c7bc

Diff for: targets/r0bre

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
http://r0b.re/test.js

0 commit comments

Comments
 (0)