Skip to content

Commit

Permalink
v.0.7.0
Browse files Browse the repository at this point in the history
  • Loading branch information
rbidou committed May 25, 2024
1 parent 96c839e commit ba6f49b
Show file tree
Hide file tree
Showing 11 changed files with 92 additions and 18 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
![](pyrasp.png)

<p>
<img src="https://img.shields.io/badge/Version-0.6.2-green?style=for-the-badge" alt="version 0.6.2"/>
<img src="https://img.shields.io/badge/Version-0.7.0-green?style=for-the-badge" alt="version 0.7.0"/>
<a href="https://www.paracyberbellum.io">
<img src="https://img.shields.io/badge/A%20project%20by-ParaCyberBellum-blue?style=for-the-badge" alt="A project by ParaCyberBellum"/>
</a>
Expand All @@ -20,6 +20,7 @@ One specificity of `pyrasp` relies on the fact that it does not use signatures.
# Documentation
[Full documentation](https://paracyberbellum.gitbook.io/pyrasp)
<br>[Release Notes](https://github.com/rbidou/pyrasp/blob/main/RELEASE-NOTES.md)
<br>[Web Site](https://pyrasp.paracyberbellum.io)

# Contacts
Renaud Bidou - [email protected]
Expand Down
14 changes: 14 additions & 0 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
# v0.7.0

## New features
- PyRASP classes API

## Improvements
- **Improved ML engines for SQL Injection and XSS detection**
- Default SQL Injection detection probabilities raised to 0.85
- Default XSS detection probabilities raised to 0.70
- Attack payloads are now base64 encoded in logs

## Bug fix
- Flask agent was still processing page, even if attack was detected

# v0.6.2

## New features
Expand Down
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "pyrasp"
version = "0.6.2"
version = "0.7.0dev2"
authors = [
{ name = "Renaud Bidou", email = "[email protected]" }
]
Expand All @@ -24,8 +24,9 @@ dependencies = [
]

[project.urls]
Homepage = "https://github.com/rbidou/pyrasp"
Homepage = "https://pyrasp.paracyberbellum.io"
Documentation = "https://paracyberbellum.gitbook.io/pyrasp"
GitHub = "https://github.com/rbidou/pyrasp"

[tool.setuptools.package-data]
pyrasp = ["data/*"]
Expand Down
Binary file removed pyrasp/data/sqli_model-1.0.0
Binary file not shown.
Binary file removed pyrasp/data/sqli_model-1.1.0
Binary file not shown.
Binary file added pyrasp/data/sqli_model-2.0.0
Binary file not shown.
Binary file removed pyrasp/data/xss_model-1.1.0
Binary file not shown.
Binary file removed pyrasp/data/xss_model-1.2.0
Binary file not shown.
Binary file added pyrasp/data/xss_model-2.0.0
Binary file not shown.
80 changes: 69 additions & 11 deletions pyrasp/pyrasp.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION = '0.6.2'
VERSION = '0.7.0'

from pprint import pprint
import time
Expand All @@ -22,7 +22,7 @@

# Flask
try:
from flask import request
from flask import request, redirect, url_for
from flask import Response as FlaskResponse
from flask.wrappers import Response as FlaskResponseType
from werkzeug.utils import import_string
Expand Down Expand Up @@ -308,6 +308,17 @@ class PyRASP():
'errors': 0,
'attacks': 0
}

# API DATA
API_CONFIG = {}
API_BLACKLIST = []
API_STATUS = {
'version': '',
'blacklist': 0,
'xss_loaded': False,
'sqli_loaded': False,
'config': 'Default'
}

####################################################
# CONSTRUCTOR & DESTRUCTOR
Expand Down Expand Up @@ -362,6 +373,8 @@ def __init__(self, app = None, app_name = None, hosts = [], conf = None, key = N

if not self.load_cloud_config():
self.print_screen('[!] Could not load configuration from cloud server. Running default configuration.', init=True, new_line_up = True)
else:
self.API_STATUS['config'] = 'Cloud'

# Load configuration file
if not conf is None:
Expand All @@ -370,7 +383,8 @@ def __init__(self, app = None, app_name = None, hosts = [], conf = None, key = N
self.CONF_FILE = os.environ.get('PYRASP_CONF')

if not self.CONF_FILE is None:
self.load_file_config(self.CONF_FILE)
if self.load_file_config(self.CONF_FILE):
self.API_STATUS['config'] = 'Local'

# Default config customization from
if all([
Expand Down Expand Up @@ -462,6 +476,10 @@ def __init__(self, app = None, app_name = None, hosts = [], conf = None, key = N
else:
self.print_screen('[+] SQLI model loaded', init=True, new_line_up = False)

# Agent status
self.API_STATUS['version'] = VERSION
self.API_STATUS['xss_loaded'] = xss_model_loaded
self.API_STATUS['sqli_loaded'] = sqli_model_loaded

# AWS, GCP & Azure
if self.PLATFORM in CLOUD_FUNCTIONS:
Expand Down Expand Up @@ -607,8 +625,7 @@ def send_beacon(self):
remove_blacklist_entries = blacklist_update.get('remove') or []
for remove_entry in remove_blacklist_entries:
if remove_entry in self.BLACKLIST:
del self.BLACKLIST[remove_entry]

del self.BLACKLIST[remove_entry]

# Set configuration
if not error and server_data.get('config'):
Expand Down Expand Up @@ -750,20 +767,26 @@ def load_cloud_config(self):

def load_file_config(self, conf_file):

result = True

self.print_screen(f'[+] Loading configuration from {conf_file}', init = True, new_line_up = False)

try:
with open(conf_file) as f:
config = json.load(f)
except Exception as e:
self.print_screen(f'[!] Error reading {conf_file}: {str(e)}', init = True, new_line_up = False)
result = False
else:
self.load_config(config)

return result

def load_config(self, config):

# Load parameters
config_params = config.get('config') or config
self.API_CONFIG = config_params

for key in config_params:
setattr(self, key, config_params[key])
Expand All @@ -780,6 +803,7 @@ def load_config(self, config):
config_blacklist = config.get('blacklist')

if config_blacklist:

self.BLACKLIST = config_blacklist

return True
Expand All @@ -793,28 +817,41 @@ def handle_attack(self, attack, host, request_path, source_ip, timestamp):
attack_id = attack['type']
attack_check = ATTACKS_CHECKS[attack_id]
attack_details = attack.get('details') or {}
attack_payload = None
if attack_details and attack_details.get('payload'):
attack_payload = attack_details['payload']
try:
attack_payload_b64 = base64.b64encode(attack_details['payload'].encode()).decode()
attack_details['payload'] = attack_payload_b64
except:
pass

action = None

# Generic case
# Action
## Generic case
if not attack_id == 0:
action = self.SECURITY_CHECKS[attack_check]
# Blacklist
## Blacklist
else:
action = 2

attack_details['action'] = action

# Attack type
if ATTACKS_CODES.get(attack_id):
attack_details['codes'] = ATTACKS_CODES[attack_id]

if not self.BLACKLIST_OVERRIDE and action == 2:
self.blacklist_ip(source_ip, timestamp, attack_check)


# Print screen
try:
self.print_screen(f'[!] {ATTACKS[attack_id]}: {attack["details"]["location"]} -> {attack["details"]["payload"]}')
self.print_screen(f'[!] {ATTACKS[attack_id]}: {attack["details"]["location"]} -> {attack_payload}')
except:
self.print_screen(f'[!] Attack - No details')

# Log
if self.LOG_ENABLED:
self.log_security_event(attack_check, source_ip, None, attack_details)

Expand Down Expand Up @@ -1735,6 +1772,26 @@ def check_pattern(self, text, pattern, match_type):

return match

####################################################
# API
####################################################

def get_config(self):

return self.API_CONFIG

def get_blacklist(self):

self.API_BLACKLIST = [ i for i in self.BLACKLIST ]

return self.API_BLACKLIST

def get_status(self):

self.API_STATUS['blacklist'] = len(self.BLACKLIST)

return self.API_STATUS

class FlaskRASP(PyRASP):

CURRENT_ATTACKS = {}
Expand Down Expand Up @@ -1768,8 +1825,9 @@ def before_request_callback():
# Send attack status in status code for handling by @after_request
if not attack == None:
attack_id = '::'.join([host, request_method, request_path, source_ip])
self.CURRENT_ATTACKS[attack_id] = attack

self.CURRENT_ATTACKS[attack_id] = attack
return FlaskResponse()

# Outgoing responses
def set_after_security_checks(self, app):
@app.after_request
Expand Down
8 changes: 4 additions & 4 deletions pyrasp/pyrasp_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
#

DATA_VERSION = '1.1.0'
XSS_MODEL_VERSION = '1.2.0'
SQLI_MODEL_VERSION = '1.1.0'
XSS_MODEL_VERSION = '2.0.0'
SQLI_MODEL_VERSION = '2.0.0'

#
# PLATFORMS
Expand Down Expand Up @@ -210,10 +210,10 @@

"EXCEPTIONS" : [],

"XSS_PROBA" : 0.60,
"XSS_PROBA" : 0.70,
"MIN_XSS_LEN": 16,

"SQLI_PROBA" : 0.725,
"SQLI_PROBA" : 0.85,
"MIN_SQLI_LEN": 8,

"DLP_PHONE_NUMBERS": False,
Expand Down

0 comments on commit ba6f49b

Please sign in to comment.