django-restit 4.2.77__py3-none-any.whl → 4.2.79__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {django_restit-4.2.77.dist-info → django_restit-4.2.79.dist-info}/METADATA +1 -1
- {django_restit-4.2.77.dist-info → django_restit-4.2.79.dist-info}/RECORD +10 -9
- incident/models/event.py +1 -0
- incident/models/ossec.py +2 -0
- incident/parsers/ossec.py +236 -159
- incident/rpc.py +11 -7
- incident/server.py +0 -0
- rest/models/base.py +4 -0
- {django_restit-4.2.77.dist-info → django_restit-4.2.79.dist-info}/LICENSE.md +0 -0
- {django_restit-4.2.77.dist-info → django_restit-4.2.79.dist-info}/WHEEL +0 -0
@@ -108,15 +108,16 @@ incident/migrations/0013_rulecheck_is_required.py,sha256=cL7tOj5XGPpKd2f5BojIKfN
|
|
108
108
|
incident/migrations/0014_event_group_alter_rulecheck_index.py,sha256=v3gm5k0LVoas27qUDOt7el7YtK4yjFVLeEpuFUCoXaQ,724
|
109
109
|
incident/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
110
110
|
incident/models/__init__.py,sha256=NMphuhb0RTMf7Ov4QkNv7iv6_I8Wtr3xQ54yjX_a31M,209
|
111
|
-
incident/models/event.py,sha256=
|
111
|
+
incident/models/event.py,sha256=i02lRMdBcjIty5w_wdZnYnW-RNXDANNfnsFouwlDYO8,7414
|
112
112
|
incident/models/incident.py,sha256=HPbi6J9qm7_-FMjnDUPV9NcbmP_60WU-IO9HJSpoLTY,19360
|
113
|
-
incident/models/ossec.py,sha256=
|
113
|
+
incident/models/ossec.py,sha256=eUDRGawzuLWobKEVGKfdZisDnyjS_Hlxi0T_GCSLCCI,2252
|
114
114
|
incident/models/rules.py,sha256=SMlDRw_r3fGv-vmRojRLmsklqRRxDcjrSLVBIz-gadA,6884
|
115
115
|
incident/models/ticket.py,sha256=S3kqGQpYLE6Y4M9IKu_60sgW-f592xNr8uufqHnvDoU,2302
|
116
116
|
incident/parsers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
117
|
-
incident/parsers/ossec.py,sha256=
|
117
|
+
incident/parsers/ossec.py,sha256=8SUma8wb2KmrcgACc6jESD_gXklCsMnKVyY7GrXYrtY,10812
|
118
118
|
incident/periodic.py,sha256=eX1rQK6v65A9ugofTvJPSmAWei6C-3EYgzCMuGZ03jM,381
|
119
|
-
incident/rpc.py,sha256=
|
119
|
+
incident/rpc.py,sha256=viJt873b8T8SiAq10EM57lF8g7ghyj3ymdkaXzh2Ass,8181
|
120
|
+
incident/server.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
120
121
|
incident/templates/email/incident_change.html,sha256=tQYphypwLukkVdwH0TB2Szz2VEJ7GnsfRS3_ZJ-MYeE,13895
|
121
122
|
incident/templates/email/incident_msg.html,sha256=MZdKhTddUF2MpiH8Z3RTQEmW_ko1n3ajeZ11KLtiLlU,13780
|
122
123
|
incident/templates/email/incident_new.html,sha256=W6nwFQROnyDfMlXub8s02ws4hGnJp16pfgp9xTm_aEc,15185
|
@@ -401,7 +402,7 @@ rest/middleware/request.py,sha256=JchRNy5L-bGd-7h-KFYekGRvREe2eCkZXKOYqIkP2hI,41
|
|
401
402
|
rest/middleware/session.py,sha256=zHSoQpIzRLmpqr_JvW406wzpvU3W3gDbm5JhtzLAMlE,10240
|
402
403
|
rest/middleware/session_store.py,sha256=1nSdeXK8PyuYgGgIufqrS6j6QpIrQ7zbMNT0ol75e6U,1901
|
403
404
|
rest/models/__init__.py,sha256=M8pvFDq-WCF-QcM58X7pMufYYe0aaQ3U0PwGe9TKbbY,130
|
404
|
-
rest/models/base.py,sha256=
|
405
|
+
rest/models/base.py,sha256=bPcTeCX7KvhkcVMKEyLmu9eYc7ccJGkIP1n3cctJPVE,69675
|
405
406
|
rest/models/cacher.py,sha256=eKz8TINVhWEqKhJGMsRkKZTtBUIv5rN3NHbZwOC56Uk,578
|
406
407
|
rest/models/metadata.py,sha256=65GvfFbc26_7wJz8qEAzU7fEOZWVz0ttO5j5m_gs4hk,12860
|
407
408
|
rest/net.py,sha256=LcB2QV6VNRtsSdmiQvYZgwQUDwOPMn_VBdRiZ6OpI-I,2974
|
@@ -502,7 +503,7 @@ ws4redis/servers/uwsgi.py,sha256=VyhoCI1DnVFqBiJYHoxqn5Idlf6uJPHvfBKgkjs34mo,172
|
|
502
503
|
ws4redis/settings.py,sha256=K0yBiLUuY81iDM4Yr-k8hbvjn5VVHu5zQhmMK8Dtz0s,1536
|
503
504
|
ws4redis/utf8validator.py,sha256=S0OlfjeGRP75aO6CzZsF4oTjRQAgR17OWE9rgZdMBZA,5122
|
504
505
|
ws4redis/websocket.py,sha256=R0TUyPsoVRD7Y_oU7w2I6NL4fPwiz5Vl94-fUkZgLHA,14848
|
505
|
-
django_restit-4.2.
|
506
|
-
django_restit-4.2.
|
507
|
-
django_restit-4.2.
|
508
|
-
django_restit-4.2.
|
506
|
+
django_restit-4.2.79.dist-info/LICENSE.md,sha256=VHN4hhEeVOoFjtG-5fVv4jesA4SWi0Z-KgOzzN6a1ps,1068
|
507
|
+
django_restit-4.2.79.dist-info/METADATA,sha256=7Hya_J4qnHewCMwlz5m2Y2JLnN2b4TpVX2pVekbpjCw,7645
|
508
|
+
django_restit-4.2.79.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
509
|
+
django_restit-4.2.79.dist-info/RECORD,,
|
incident/models/event.py
CHANGED
@@ -73,6 +73,7 @@ class Event(JSONMetaData, rm.RestModel):
|
|
73
73
|
|
74
74
|
level = models.IntegerField(default=0, db_index=True)
|
75
75
|
category = models.CharField(max_length=124, db_index=True)
|
76
|
+
# code = models.IntegerField(default=0, db_index=True)
|
76
77
|
|
77
78
|
group = models.ForeignKey(
|
78
79
|
"account.Group", on_delete=models.SET_NULL,
|
incident/models/ossec.py
CHANGED
@@ -47,5 +47,7 @@ class ServerOssecAlert(models.Model, rm.RestModel):
|
|
47
47
|
title = models.CharField(max_length=200, blank=True, null=True, default=None)
|
48
48
|
geoip = models.ForeignKey("location.GeoIP", blank=True, null=True, default=None, on_delete=models.DO_NOTHING)
|
49
49
|
|
50
|
+
metadata = None
|
51
|
+
|
50
52
|
def __str__(self):
|
51
53
|
return f'{self.hostname}: {self.title}'
|
incident/parsers/ossec.py
CHANGED
@@ -13,6 +13,25 @@ LEVEL_REMAP_BY_RULE = {
|
|
13
13
|
5710: 5
|
14
14
|
}
|
15
15
|
|
16
|
+
NGINX_PARSE_PATTERN = re.compile(
|
17
|
+
r'(?P<src_ip>\d+\.\d+\.\d+\.\d+) - - \[(?P<http_time>.+?)\] '
|
18
|
+
r'(?P<http_method>\w+) (?P<http_url>.+?) (?P<http_protocol>[\w/.]+) '
|
19
|
+
r'(?P<http_status>\d+) (?P<http_bytes>\d+) (?P<http_referer>.+?) '
|
20
|
+
r'(?P<user_agent>.+?) (?P<http_elapsed>\d\.\d{3})'
|
21
|
+
)
|
22
|
+
|
23
|
+
def parse_nginx_line(line):
|
24
|
+
if "\n" in line:
|
25
|
+
for l in line.split('\n'):
|
26
|
+
match = NGINX_PARSE_PATTERN.match(l)
|
27
|
+
if match:
|
28
|
+
return match.groupdict()
|
29
|
+
return None
|
30
|
+
match = NGINX_PARSE_PATTERN.match(line)
|
31
|
+
if match:
|
32
|
+
return match.groupdict()
|
33
|
+
return None
|
34
|
+
|
16
35
|
|
17
36
|
def removeNonAscii(input_str):
|
18
37
|
"""Remove all non-ASCII characters and escaped byte sequences from the input string."""
|
@@ -23,9 +42,9 @@ def removeNonAscii(input_str):
|
|
23
42
|
|
24
43
|
|
25
44
|
def extractURL(text):
|
26
|
-
match = re.search(r"GET\s+(https?://[^\s]+)\s+HTTP/\d\.\d", text)
|
45
|
+
match = re.search(r"(GET|POST|DELETE|PUT)\s+(https?://[^\s]+)\s+HTTP/\d\.\d", text)
|
27
46
|
if match:
|
28
|
-
return match.group(
|
47
|
+
return match.group(2)
|
29
48
|
return None
|
30
49
|
|
31
50
|
|
@@ -36,6 +55,13 @@ def extractDomain(text):
|
|
36
55
|
return None
|
37
56
|
|
38
57
|
|
58
|
+
def extractIP(text):
|
59
|
+
match = re.search(r"\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b", text)
|
60
|
+
if match:
|
61
|
+
return match.group(1)
|
62
|
+
return None
|
63
|
+
|
64
|
+
|
39
65
|
def extractUrlPath(text):
|
40
66
|
match = re.search(r"https?://[^/]+(/[^?]*)", text)
|
41
67
|
if match:
|
@@ -43,174 +69,225 @@ def extractUrlPath(text):
|
|
43
69
|
return None
|
44
70
|
|
45
71
|
|
72
|
+
def extractUserAgent(text):
|
73
|
+
# this only works if the referrer is '-'
|
74
|
+
match = re.search(r"' - (.+?) \d+\.\d+ \d+'", text)
|
75
|
+
if match:
|
76
|
+
return match.group(1)
|
77
|
+
return None
|
78
|
+
|
79
|
+
|
46
80
|
def extractMetaData(alert):
|
47
|
-
|
48
|
-
if irule == 31301:
|
49
|
-
patterns = {
|
50
|
-
"src_ip": re.compile(r"Src IP: (\S+)"),
|
51
|
-
"path": re.compile(r"request: (\S+ \S+)"),
|
52
|
-
"http_server": re.compile(r"server: (\S+)"),
|
53
|
-
"http_host": re.compile(r"host: (\S+)"),
|
54
|
-
"http_referrer": re.compile(r"referrer: (\S+)")
|
55
|
-
}
|
56
|
-
# Search for matches in the text
|
57
|
-
return {key: pattern.search(alert.text).group(1) for key, pattern in patterns.items() if pattern.search(alert.text)}
|
58
|
-
return {}
|
81
|
+
return parse_alert_metadata(alert)
|
59
82
|
|
60
83
|
|
61
|
-
|
62
|
-
|
84
|
+
DEFAULT_META_PATTERNS = {
|
85
|
+
"src_ip": re.compile(r"Src IP: (\S+)"),
|
86
|
+
"src_port": re.compile(r"Src Port: (\S+)"),
|
87
|
+
"user": re.compile(r"User: (\S+)"),
|
88
|
+
"path": re.compile(r"request: (\S+ \S+)"),
|
89
|
+
"http_server": re.compile(r"server: (\S+),"),
|
90
|
+
"http_host": re.compile(r"host: (\S+)"),
|
91
|
+
"http_referrer": re.compile(r"referrer: (\S+),"),
|
92
|
+
"client": re.compile(r"client: (\S+),"),
|
93
|
+
"upstream": re.compile(r"upstream: (\S+),")
|
94
|
+
}
|
95
|
+
|
96
|
+
|
97
|
+
def parse_alert_metadata(alert):
|
98
|
+
patterns = DEFAULT_META_PATTERNS
|
99
|
+
if alert.rule_id == "2501":
|
100
|
+
match = re.search(r'user (\w+) (\d+\.\d+\.\d+\.\d+) port (\d+)', alert.text)
|
101
|
+
if match:
|
102
|
+
return dict(username=match.group(1), src_ip=match.group(2), src_port=match.group(3))
|
103
|
+
elif alert.rule_id.startswith("311") or alert.rule_id in ["31516", "31508", "31516"]:
|
104
|
+
data = parse_nginx_line(alert.text)
|
105
|
+
if data:
|
106
|
+
return data
|
107
|
+
elif alert.rule_id in ["31301"]:
|
108
|
+
match = re.search(r'\((?P<error_code>\d+): (?P<error_message>.*?)\)', alert.text)
|
109
|
+
data = match_patterns(patterns, alert.text)
|
110
|
+
data["action"] = "error"
|
111
|
+
if match:
|
112
|
+
data.update(match.groupdict())
|
113
|
+
return data
|
114
|
+
elif alert.rule_id in ["31302", "31303"]:
|
115
|
+
match = re.search(r'\[(warn|crit|error)\].*?: (.*?),', alert.text)
|
116
|
+
data = match_patterns(patterns, alert.text)
|
117
|
+
|
118
|
+
if match:
|
119
|
+
data["action"] = match.group(1)
|
120
|
+
emsg = match.group(2)
|
121
|
+
if emsg[0] == "*":
|
122
|
+
emsg = emsg[emsg.find(' ')+1:]
|
123
|
+
data["error_message"] = emsg
|
124
|
+
return data
|
125
|
+
elif alert.rule_id == "551":
|
126
|
+
match = re.search(r"Integrity checksum changed for: '(\S+)'", alert.text)
|
127
|
+
if match:
|
128
|
+
return dict(filename=match.group(1), action="changed")
|
129
|
+
elif alert.rule_id == "554":
|
130
|
+
match = re.search(r"New file '(\S+)' added", alert.text)
|
131
|
+
if match:
|
132
|
+
return dict(filename=match.group(1), action="added")
|
133
|
+
elif alert.rule_id == "5402":
|
134
|
+
match = re.search(r'(?P<username>\w+) : PWD=(?P<pwd>\S+) ; USER=(?P<user>\w+) ; COMMAND=(?P<command>.+)', alert.text)
|
135
|
+
return match.groupdict()
|
136
|
+
elif alert.rule_id in ["5501", "5502"]:
|
137
|
+
match = re.search(r"session (?P<action>\S+) for user (?P<username>\S+)*", alert.text)
|
138
|
+
if match:
|
139
|
+
return match.groupdict()
|
140
|
+
elif alert.rule_id in ["5704", "5705"]:
|
141
|
+
match = re.search(r"(?P<src_ip>\d{1,3}(?:\.\d{1,3}){3}) port (?P<src_port>\d+)", alert.text)
|
142
|
+
if match:
|
143
|
+
return match.groupdict()
|
144
|
+
elif alert.rule_id == "5715":
|
145
|
+
match = re.search(r'Accepted publickey for (?P<username>\S+) from (?P<src_ip>\d+\.\d+\.\d+\.\d+) .*: (?P<ssh_key_type>\S+) (?P<ssh_signature>\S+)', alert.text)
|
146
|
+
if match:
|
147
|
+
return match.groupdict()
|
148
|
+
elif alert.rule_id == "2932":
|
149
|
+
match = re.search(r"Installed: (\S+)", alert.text)
|
150
|
+
if match:
|
151
|
+
return dict(package=match.group(1))
|
152
|
+
return match_patterns(patterns, alert.text)
|
153
|
+
|
154
|
+
def match_patterns(patterns, text):
|
155
|
+
# Search for matches in the text
|
156
|
+
return {key: pattern.search(text).group(1) for key, pattern in patterns.items() if pattern.search(text)}
|
157
|
+
|
158
|
+
|
159
|
+
def parse_alert_json(data):
|
63
160
|
try:
|
64
|
-
|
161
|
+
if isinstance(data, str):
|
162
|
+
data = objict.fromJSON(data.replace('\n', '\\n'))
|
65
163
|
except Exception:
|
66
164
|
data = objict.fromJSON(removeNonAscii(data))
|
67
165
|
for key in data:
|
68
166
|
data[key] = data[key].strip()
|
167
|
+
return data
|
69
168
|
|
70
|
-
|
71
|
-
|
72
|
-
if
|
73
|
-
|
169
|
+
|
170
|
+
def ignore_alert(alert):
|
171
|
+
if alert.rule_id in IGNORE_RULES:
|
172
|
+
return True
|
173
|
+
if alert.rule_id == "510" and "/dev/.mount/utab" in alert.text:
|
174
|
+
return True
|
175
|
+
return False
|
176
|
+
|
177
|
+
|
178
|
+
def parse_alert_id(details):
|
179
|
+
match = re.search(r"Alert (\d+\.\d+):", details)
|
180
|
+
if match:
|
181
|
+
return match.group(1)
|
182
|
+
return ""
|
183
|
+
|
184
|
+
|
185
|
+
def parse_rule_details(details):
|
186
|
+
alert_id = parse_alert_id(details)
|
187
|
+
rule_pattern = r"Rule: (\d+) \(level (\d+)\) -> '([^']+)'"
|
188
|
+
match = re.search(rule_pattern, details)
|
189
|
+
if match:
|
190
|
+
return objict(
|
191
|
+
rid=int(match.group(1)), level=int(match.group(2)),
|
192
|
+
title=match.group(3), alert_id=alert_id)
|
193
|
+
return objict(alert_id=alert_id)
|
194
|
+
|
195
|
+
|
196
|
+
def parse_when(alert):
|
197
|
+
return datetime.utcfromtimestamp(int(alert.alert_id[:alert.alert_id.find(".")]))
|
198
|
+
|
199
|
+
|
200
|
+
def truncate_str(text, length):
|
201
|
+
if len(text) > length:
|
202
|
+
text = text[:length]
|
203
|
+
text = text[:text.rfind(' ')] + "..."
|
204
|
+
return text
|
205
|
+
|
206
|
+
|
207
|
+
def update_by_rule(data, geoip=None):
|
208
|
+
if data.rule_id == "2501":
|
209
|
+
data.title = f"SSH Auth Attempt {data.username}@{data.hostname} from {data.src_ip}"
|
210
|
+
elif data.rule_id == "2503" and data.src_ip:
|
211
|
+
data.title = f"SSH Auth Blocked from {data.src_ip}"
|
212
|
+
elif data.rule_id == "31101" and data.http_status:
|
213
|
+
data.title = f"Web {data.http_status} {data.http_method} {data.http_url} from {data.src_ip}"
|
214
|
+
elif data.rule_id == "31104" and data.http_status:
|
215
|
+
data.title = f"Web Attack {data.http_status} {data.http_method} {data.http_url} from {data.src_ip}"
|
216
|
+
elif data.rule_id == "31111" and data.http_status:
|
217
|
+
if geoip and geoip.isp:
|
218
|
+
data.title = f"No referrer for .js - {data.http_status} {data.http_method} {data.http_url} from {data.src_ip}({geoip.isp})"
|
219
|
+
else:
|
220
|
+
data.title = f"No referrer for .js - {data.http_status} {data.http_method} {data.http_url} from {data.src_ip}"
|
221
|
+
elif data.rule_id in ["31151", "31152", "31153"] and data.http_status:
|
222
|
+
url = truncate_str(data.http_url, 50)
|
223
|
+
data.title = f"Suspected Web Scan {url} from {data.src_ip}"
|
224
|
+
elif data.rule_id == "31120" and data.http_status:
|
225
|
+
url = truncate_str(data.http_url, 50)
|
226
|
+
data.title = f"Web Error {data.http_status} {data.http_method} {url} from {data.src_ip}"
|
227
|
+
elif data.rule_id.startswith("311") and data.http_status:
|
228
|
+
url = truncate_str(data.http_url, 50)
|
229
|
+
data.title = f"Web {data.http_status} {data.http_method} {url} from {data.src_ip}"
|
230
|
+
elif data.rule_id in ["31301", "31302", "31303"] and data.error_message:
|
231
|
+
if data.upstream and "ws/events" in data.upstream:
|
232
|
+
data.title = f"Websocket Error: {data.error_message} on {data.hostname}"
|
233
|
+
else:
|
234
|
+
emsg = truncate_str(data.error_message, 50)
|
235
|
+
data.title = f"Nginx {data.action}: {emsg} from {data.src_ip}"
|
236
|
+
elif data.rule_id == "31516" and data.http_url:
|
237
|
+
url = truncate_str(data.http_url, 50)
|
238
|
+
data.title = f"Web Suspicious {data.http_status} {data.http_method} {url} from {data.src_ip}"
|
239
|
+
elif data.rule_id == "533":
|
240
|
+
data.title = f"Network Open Port Change Detected on {data.hostname}"
|
241
|
+
elif data.rule_id == "5402":
|
242
|
+
data.title = f"Sudo(user: {data.user}) executed on {data.hostname}"
|
243
|
+
elif data.rule_id in ["551", "554"] and data.filename:
|
244
|
+
name = truncate_str(data.filename, 50)
|
245
|
+
data.title = f"File {data.action.capitalize()} on {data.hostname}: {name}"
|
246
|
+
elif data.rule_id in ["5501", "5502"]:
|
247
|
+
if "sudo" in data.text:
|
248
|
+
data.title = f"Server Login {data.action} via sudo on {data.hostname}"
|
249
|
+
else:
|
250
|
+
data.title = f"Server Login {data.action} on {data.hostname}"
|
251
|
+
elif data.rule_id == "5715":
|
252
|
+
data.title = f"SSH Login Detected: {data.username}@{data.hostname} from {data.src_ip}"
|
253
|
+
elif data.rule_id == "2932" and data.package:
|
254
|
+
package = truncate_str(data.package, 60)
|
255
|
+
data.title = f"Package Installed on {data.hostname}: {package}"
|
256
|
+
elif data.src_ip and data.src_ip not in data.title:
|
257
|
+
data.title = f"{data.title} Source IP: {data.src_ip}"
|
258
|
+
if len(data.title) > 199:
|
259
|
+
data.title = data.title[:199]
|
260
|
+
|
261
|
+
def parse_incoming_alert(data):
|
262
|
+
alert = parse_alert_json(data)
|
263
|
+
if ignore_alert(alert):
|
74
264
|
return None
|
75
|
-
|
265
|
+
alert.update(parse_rule_details(alert.text))
|
266
|
+
if alert.title is None:
|
76
267
|
return None
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
alert = am.ServerOssecAlert(**data)
|
82
|
-
alert.when = datetime.utcfromtimestamp(int(data.alert_id[:data.alert_id.find(".")]))
|
83
|
-
# now lets parse the title
|
84
|
-
title = alert.text[alert.text.find("Rule:") + 5:]
|
85
|
-
# level to int
|
86
|
-
level = title[title.find('(level') + 7:]
|
87
|
-
alert.level = int(level[:level.find(')')].strip())
|
88
|
-
title = title[:title.find('\n')].strip()
|
89
|
-
pos = title.find("->")
|
90
|
-
if pos > 0:
|
91
|
-
title = title[pos + 2:]
|
92
|
-
alert.title = title
|
93
|
-
if alert.title.startswith("'"):
|
94
|
-
alert.title = alert.title[1:-1]
|
95
|
-
|
96
|
-
if data.hostname == "test":
|
97
|
-
if data.rule_id == "31120" or "Web server" in title:
|
98
|
-
return None
|
99
|
-
|
100
|
-
# helpers.log_print(title, alert.title)
|
101
|
-
# source ip (normally public ip of host)
|
102
|
-
pos = alert.text.find("Src IP:")
|
103
|
-
if pos > 1:
|
104
|
-
src_ip = alert.text[alert.text.find("Src IP:") + 7:]
|
105
|
-
alert.src_ip = src_ip[:src_ip.find('\n')].strip()
|
106
|
-
|
107
|
-
irule = int(alert.rule_id)
|
108
|
-
if irule == 5710:
|
109
|
-
m = re.search(r"Invalid user (\S+) from (\S+)", data.text)
|
110
|
-
if m and m.groups():
|
111
|
-
alert.username = m.group(1)
|
112
|
-
alert.src_ip = m.group(2).strip()
|
113
|
-
alert.title = f"Attempt to login with invalid user: {alert.username}"
|
114
|
-
else:
|
115
|
-
m = re.search(r"Invalid user from (\S+)", data.text)
|
116
|
-
if m and m.groups():
|
117
|
-
alert.username = "(empty string)"
|
118
|
-
alert.src_ip = m.group(1).strip()
|
119
|
-
elif irule == 2932:
|
120
|
-
m = re.search(r"Installed: (\S+)", data.text)
|
121
|
-
if m and m.groups():
|
122
|
-
package = m.group(1)
|
123
|
-
alert.title = "Yum Package Installed: {}".format(package)
|
124
|
-
elif irule == 551:
|
125
|
-
# Integrity checksum changed for: '/etc/ld.so.cache'
|
126
|
-
m = re.search(r"Integrity checksum changed for: '(\S+)'", data.text)
|
127
|
-
if m and m.groups():
|
128
|
-
action = m.group(1)
|
129
|
-
alert.title = "File Changed: {}".format(action)
|
130
|
-
elif irule == 5715:
|
131
|
-
m = re.search(r"Accepted publickey for (\S+).*ssh2: ([^\n\r]*)", data.text)
|
132
|
-
if m and m.groups():
|
133
|
-
ssh_sig = m.group(2)
|
134
|
-
if " " in ssh_sig:
|
135
|
-
kind, ssh_sig = ssh_sig.split(' ')
|
136
|
-
alert.level = 8
|
137
|
-
alert.username = m.group(1)
|
138
|
-
alert.ssh_sig = ssh_sig
|
139
|
-
alert.ssh_kind = kind
|
140
|
-
alert.title = f"SSH LOGIN:{alert.username}@{alert.hostname} from {alert.src_ip}"
|
141
|
-
# member = findUserBySshSig(ssh_sig)
|
142
|
-
# if member:
|
143
|
-
# alert.title = "SSH LOGIN user: {}".format(member.username)
|
144
|
-
elif irule == 5501 or irule == 5502:
|
145
|
-
# pam_unix(sshd:session): session opened for user git by (uid=0)
|
146
|
-
m = re.search(r"session (\S+) for user (\S+)*", data.text)
|
147
|
-
if m and m.groups():
|
148
|
-
alert.action = m.group(1)
|
149
|
-
alert.username = m.group(2)
|
150
|
-
alert.title = f"session {alert.action} for user {alert.username}"
|
151
|
-
elif irule == 5402:
|
152
|
-
# TTY=pts/0 ; PWD=/opt/mm_protector ; USER=root ; COMMAND=/sbin/iptables -F
|
153
|
-
m = re.search(r"sudo(?:\[\d+\])?:\s*(\S+).*?COMMAND=([^\n\r]*)", data.text)
|
154
|
-
# m = re.search(r"sudo:\s*(\S+).*COMMAND=([^\n\r]*)", data.text)
|
155
|
-
if m and m.groups():
|
156
|
-
alert.username = m.group(1)
|
157
|
-
alert.title = "sudo {}".format(m.group(2)).replace("#040", " ")
|
158
|
-
alert.level = 7
|
159
|
-
elif irule == 5706:
|
160
|
-
m = re.search(r"identification string from (\S+) port (\S+)", data.text)
|
161
|
-
if m and m.groups():
|
162
|
-
alert.src_ip = m.group(1)
|
163
|
-
elif irule == 5702:
|
164
|
-
m = re.search(r"getaddrinfo for (\S+)", data.text)
|
165
|
-
if m and m.groups():
|
166
|
-
remote_host = m.group(1)
|
167
|
-
alert.title = f"Reverse lookup failed for '{remote_host}'"
|
168
|
-
elif irule == 554:
|
169
|
-
m = re.search(r"New file '(\S+)' added", data.text)
|
170
|
-
if m and m.groups():
|
171
|
-
remote_file = m.group(1)
|
172
|
-
alert.title = f"New file detected: '{remote_file}'"
|
173
|
-
elif irule == 31101:
|
174
|
-
m = re.search(r"GET\s+(http://[^\s]+)\s+HTTP/\d\.\d\s+(\d+)", data.text)
|
175
|
-
if m and m.groups():
|
176
|
-
code = m.group(2)
|
177
|
-
request_path = m.group(1)
|
178
|
-
alert.title = f"HTTP {code}: {request_path}"
|
179
|
-
elif irule == 31301:
|
180
|
-
m = re.search(r"(\[error\]|\[crit\])[^\*]*\*\d*\s+(.*?),", text)
|
181
|
-
if m and len(m.groups()) >=2:
|
182
|
-
alert.title = error
|
183
|
-
elif irule == 100020:
|
184
|
-
m = re.search(r"\[(\S+)\]", data.text)
|
185
|
-
if m and m.groups():
|
186
|
-
alert.src_ip = m.group(1)
|
187
|
-
elif "web,accesslog," in data.text and "https:" in data.text:
|
188
|
-
alert.ssh_sig = extractURL(data.text)
|
189
|
-
if alert.ssh_sig:
|
190
|
-
alert.hostname = extractDomain(alert.ssh_sig)
|
191
|
-
|
192
|
-
if alert.ext_ip is None:
|
268
|
+
alert.update(parse_alert_metadata(alert))
|
269
|
+
if alert.src_ip in ["-", None] and alert.client:
|
270
|
+
alert.src_ip = alert.client
|
271
|
+
if alert.ext_ip in ["-", None]:
|
193
272
|
alert.ext_ip = alert.src_ip
|
194
|
-
|
195
|
-
# lets do a lookup for the src
|
196
|
-
alert.geoip = GeoIP.lookup(alert.src_ip)
|
197
|
-
|
198
|
-
if irule == 31111:
|
199
|
-
url = alert.ssh_sig
|
200
|
-
hostname = alert.hostname
|
201
|
-
if url:
|
202
|
-
hostname = extractDomain(url)
|
203
|
-
if alert.geoip and alert.geoip.isp:
|
204
|
-
alert.title = f"Suspicious fetch of .js, {hostname} ISP: {alert.geoip.isp}"
|
205
|
-
else:
|
206
|
-
alert.title = f"Suspicious fetch of .js, {hostname}"
|
207
|
-
# finally here we change the alert level
|
208
|
-
if irule in LEVEL_REMAP_BY_RULE:
|
209
|
-
alert.level = LEVEL_REMAP_BY_RULE[irule]
|
210
|
-
if alert.title[0] in ["'", '"']:
|
211
|
-
alert.title = alert.title[1:-1]
|
212
|
-
if len(alert.title) > 80:
|
213
|
-
alert.title = alert.title[:80] + "..."
|
214
|
-
alert.save()
|
273
|
+
update_by_rule(alert)
|
215
274
|
return alert
|
216
275
|
|
276
|
+
|
277
|
+
def parseAlert(request, data):
|
278
|
+
pdata = parse_incoming_alert(data)
|
279
|
+
if pdata is None:
|
280
|
+
return None
|
281
|
+
field_names = am.ServerOssecAlert.get_model_field_names()
|
282
|
+
soa = am.ServerOssecAlert(**{key:value for key, value in pdata.items() if key in field_names})
|
283
|
+
soa.when = parse_when(pdata)
|
284
|
+
soa.metadata = pdata
|
285
|
+
if soa.src_ip is not None and len(soa.src_ip) > 6 and soa.src_ip != "127.0.0.1":
|
286
|
+
soa.geoip = GeoIP.lookup(soa.src_ip)
|
287
|
+
update_by_rule(pdata, soa.geoip)
|
288
|
+
soa.title = pdata.title
|
289
|
+
soa.save()
|
290
|
+
return soa
|
291
|
+
|
292
|
+
|
293
|
+
|
incident/rpc.py
CHANGED
@@ -61,6 +61,7 @@ def ossec_alert_creat_from_request(request):
|
|
61
61
|
if payload:
|
62
62
|
try:
|
63
63
|
# TODO make this a task (background it)
|
64
|
+
rh.log_error("parsing payload", payload)
|
64
65
|
od = ossec.parseAlert(request, payload)
|
65
66
|
# lets now create a local event
|
66
67
|
if od is not None:
|
@@ -78,14 +79,14 @@ def ossec_alert_creat_from_request(request):
|
|
78
79
|
elif od.level <= 3:
|
79
80
|
level = 8
|
80
81
|
metadata = od.toDict(graph="default")
|
81
|
-
metadata.update(
|
82
|
+
metadata.update(od.metadata)
|
82
83
|
# we reuse the ssh_sig because it is a text field to store urls
|
83
|
-
ssh_sig = metadata.get("ssh_sig", None)
|
84
|
-
if ssh_sig is not None and ssh_sig.startswith("http"):
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
84
|
+
# ssh_sig = metadata.get("ssh_sig", None)
|
85
|
+
# if ssh_sig is not None and ssh_sig.startswith("http"):
|
86
|
+
# metadata["url"] = ssh_sig
|
87
|
+
# metadata["domain"] = ossec.extractDomain(ssh_sig)
|
88
|
+
# metadata["path"] = ossec.extractUrlPath(ssh_sig)
|
89
|
+
# metadata.pop("ssh_sig")
|
89
90
|
if od.geoip:
|
90
91
|
metadata["country"] = od.geoip.country
|
91
92
|
metadata["city"] = od.geoip.city
|
@@ -103,7 +104,9 @@ def ossec_alert_creat_from_request(request):
|
|
103
104
|
"reporter_ip": od.src_ip,
|
104
105
|
"metadata": metadata
|
105
106
|
})
|
107
|
+
return rv.restStatus(request, True)
|
106
108
|
except Exception as err:
|
109
|
+
rh.log_exception()
|
107
110
|
stack = rh.getStackString()
|
108
111
|
# rh.log_exception("during ossec alert", payload)
|
109
112
|
metadata = dict(ip=request.ip, payload=payload)
|
@@ -115,6 +118,7 @@ def ossec_alert_creat_from_request(request):
|
|
115
118
|
"category": "ossec_error",
|
116
119
|
"metadata": metadata
|
117
120
|
})
|
121
|
+
rh.log_error("ossec alert", request.DATA.asDict())
|
118
122
|
return rv.restStatus(request, False, error="no alert data")
|
119
123
|
|
120
124
|
|
incident/server.py
ADDED
File without changes
|
rest/models/base.py
CHANGED
@@ -1718,6 +1718,10 @@ class RestModel(object):
|
|
1718
1718
|
def get_related_name_fields(cls):
|
1719
1719
|
return [f.related_name for f in cls._meta.related_objects]
|
1720
1720
|
|
1721
|
+
@classmethod
|
1722
|
+
def get_model_field_names(cls):
|
1723
|
+
return [f.name for f in cls._meta.get_fields()]
|
1724
|
+
|
1721
1725
|
@classmethod
|
1722
1726
|
def get_fk_model(cls, fieldname):
|
1723
1727
|
'''returns None if not foreignkey, otherswise the relevant model'''
|
File without changes
|
File without changes
|