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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-restit
3
- Version: 4.2.77
3
+ Version: 4.2.79
4
4
  Summary: A Rest Framework for DJANGO
5
5
  License: MIT
6
6
  Author: Ian Starnes
@@ -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=WRNzvjo0jypdnQNBksOOyi-_0kVT4qWUrDZf0Aw_MPM,7355
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=p1ptr-8lnaj1EP_VmPR58b2LmaYBGaYYKAMqhWK5yZM,2227
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=jBvZh5RAYSIRSm-sLrvwdIVDfJxNULxzanoaYI-Z2Tw,7881
117
+ incident/parsers/ossec.py,sha256=8SUma8wb2KmrcgACc6jESD_gXklCsMnKVyY7GrXYrtY,10812
118
118
  incident/periodic.py,sha256=eX1rQK6v65A9ugofTvJPSmAWei6C-3EYgzCMuGZ03jM,381
119
- incident/rpc.py,sha256=3y0rfxRR9DikmCmj3IRcMaCLtzLCMrtH64lrjY1w2Og,7992
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=MIZUQStR5Y2ndSjmOSu-NSIg3SZs9IFoMlRQ2re75OE,69565
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.77.dist-info/LICENSE.md,sha256=VHN4hhEeVOoFjtG-5fVv4jesA4SWi0Z-KgOzzN6a1ps,1068
506
- django_restit-4.2.77.dist-info/METADATA,sha256=uVn8E8Ts3Hiv_mvZKMK3nZois8Z1zy79o0BhcdbtGrY,7645
507
- django_restit-4.2.77.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
508
- django_restit-4.2.77.dist-info/RECORD,,
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(1)
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
- irule = int(alert.rule_id)
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
- def parseAlert(request, data):
62
- # helpers.log_print(data)
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
- data = objict.fromJSON(data.replace('\n', '\\n'))
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
- if data.rule_id in IGNORE_RULES:
71
- return None
72
- if "test" in data.hostname and data.rule_id == "533":
73
- # bug on test ossec falsely report 533 events
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
- if data.rule_id == "510" and "/dev/.mount/utab" in data.text:
265
+ alert.update(parse_rule_details(alert.text))
266
+ if alert.title is None:
76
267
  return None
77
- # we care not for this field for now
78
- data.pop("logfile", None)
79
- if not data.text:
80
- raise Exception("invalid or missing json")
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
- if alert.src_ip is not None and len(alert.src_ip) > 6:
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(ossec.extractMetaData(od))
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
- metadata["url"] = ssh_sig
86
- metadata["domain"] = ossec.extractDomain(ssh_sig)
87
- metadata["path"] = ossec.extractUrlPath(ssh_sig)
88
- metadata.pop("ssh_sig")
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'''