5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/sqldialects.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.9-slim-buster
2 |
3 | WORKDIR /app
4 |
5 | # Install system dependencies
6 | RUN apt-get update && apt-get install -y \
7 | libpq-dev \
8 | gcc
9 |
10 | # Install Python dependencies
11 | COPY requirements.txt .
12 | RUN pip install --no-cache-dir -r requirements.txt
13 |
14 | # Copy application files
15 | COPY . .
16 |
17 | # Expose port
18 | EXPOSE 5000
19 |
20 | # Run the application
21 | CMD ["flask", "run", "--host=0.0.0.0"]
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 RedRays, Inc
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # SAP Threat Modeling Tool - Readme
6 |
7 | This tool helps you analyze and visualize connections between your SAP systems, enabling identification of potential security risks and vulnerabilities.
8 |
9 | For more detailed information about potential threats in SAP systems related to inter-SAP connections, visit [this page](https://redrays.io/blog/securing-sap-server-connections-threat-modeling/).
10 | ## Features:
11 |
12 | - **Input SAP Credentials:** Enter system details (SID, username, password, etc.) to establish initial connections.
13 | - **Scan for Inter-SAP Connections:** Discover and display connections between your SAP systems, including usernames and password storage flags.
14 | - **Visualize the Network:** View a graph representation of your SAP network, highlighting connections with potential security concerns.
15 | - **Filter Connections:** Focus on specific connections, such as those with stored passwords.
16 |
17 | ## Usage:
18 |
19 | 1. **Initiate Connections:**
20 | - Navigate to the "Init Connections" page.
21 | - Enter SAP system credentials similar to the format shown in this .
22 | - Save the connection details and click "Scan" to start the analysis.
23 |
24 | 2. **View Existing Connections:**
25 | - After the scan is completed, go to the "Connections Table" page.
26 | - Here, you can view all existing connections in a tabular format as depicted in this .
27 |
28 | 3. **Explore Inter-SAP Connections:**
29 | - Alternatively, visit the "Graph Connection" page to visualize the inter-SAP connections in a graph format like shown in this .
30 | - You can filter the connections to display only those with saved passwords, as demonstrated in this .
31 |
32 |
33 | ## Installation:
34 |
35 | 1. Clone the repository:
36 | ```bash
37 | git clone https://github.com/redrays-io/SAP-Threat-Modeling-Tool.git
38 | ```
39 | Use code with caution.
40 |
41 | 2. Install dependencies:
42 | ```bash
43 | pip install -r requirements.txt
44 | ```
45 | Use code with caution.
46 |
47 | 3. Run the application:
48 | ```bash
49 | python app.py
50 | ```
51 | Use code with caution.
52 |
53 | 4. Access the web interface: Open [http://localhost:5000](http://localhost:5000) in your browser.
54 |
55 |
56 | ## Docker Setup:
57 | Alternatively, you can set up the tool using Docker with the following command:
58 |
59 | ```bash
60 | docker pull ghcr.io/redrays-io/sap-threat-modeling:latest
61 | ```
62 |
63 | ## Security Considerations:
64 |
65 | - **Credential Storage:** This tool stores SAP credentials in a local SQLite database. Ensure proper security measures for the database file.
66 | - **Data Sensitivity:** The tool exposes information about SAP connections and potential vulnerabilities. Limit access to authorized personnel only.
67 | - **Production Use:** This tool is intended for development and testing purposes. Evaluate its suitability before using it in production environments.
68 |
--------------------------------------------------------------------------------
/app.py:
--------------------------------------------------------------------------------
1 | import sqlite3
2 | from flask import Flask, request, render_template, jsonify
3 | from sap_utils.scanning.scan import scan_systems
4 |
5 | app = Flask(__name__)
6 |
7 | # Constants
8 | DB_FILE = "db.sqlite"
9 |
10 |
11 | # Database initialization
12 | def initialize_database(db_file):
13 | with sqlite3.connect(db_file) as conn:
14 | cursor = conn.cursor()
15 |
16 | # Create credentials table if not exists
17 | cursor.execute('''CREATE TABLE IF NOT EXISTS credentials (
18 | id INTEGER PRIMARY KEY AUTOINCREMENT,
19 | sid TEXT,
20 | client TEXT,
21 | host TEXT,
22 | port TEXT,
23 | use_https INTEGER,
24 | username TEXT,
25 | password TEXT
26 | )''')
27 |
28 | # Create sap_connections table if not exists
29 | cursor.execute('''CREATE TABLE IF NOT EXISTS sap_connections (
30 | id INTEGER PRIMARY KEY AUTOINCREMENT,
31 | connection_name TEXT,
32 | connection_type TEXT,
33 | connection_from TEXT,
34 | connection_to TEXT,
35 | username TEXT,
36 | password_flag BOOLEAN
37 | )''')
38 |
39 |
40 | # Initialize the database
41 | initialize_database(DB_FILE)
42 |
43 |
44 | # Routes
45 | @app.route('/')
46 | def index():
47 | return render_template('index.html')
48 |
49 |
50 | @app.route('/remove_connection', methods=['POST'])
51 | def remove_connection():
52 | data = request.get_json()
53 | print(data)
54 | sid = data['sid']
55 | host = data['host']
56 | port = data['port']
57 |
58 | with sqlite3.connect(DB_FILE) as conn:
59 | cursor = conn.cursor()
60 | cursor.execute('''DELETE FROM credentials WHERE sid = ? AND host = ? AND port = ?''', (sid, host, port))
61 | conn.commit()
62 |
63 | return jsonify({'message': 'Connection removed successfully!'})
64 |
65 |
66 | @app.route('/launchpad')
67 | def launchpad():
68 | with sqlite3.connect(DB_FILE) as conn:
69 | cursor = conn.cursor()
70 | cursor.execute('''SELECT sid, username, password, client, use_https, host, port FROM credentials''')
71 | connections = cursor.fetchall()
72 |
73 | return render_template('launchpad.html', connections=connections)
74 |
75 |
76 | @app.route('/connectionsTable')
77 | def connectionsTable():
78 | with sqlite3.connect(DB_FILE) as conn:
79 | cursor = conn.cursor()
80 | cursor.execute('''SELECT * FROM sap_connections''')
81 | connections = cursor.fetchall()
82 |
83 | return render_template('connectionsTable.html', connections=connections)
84 |
85 |
86 | @app.route('/threatMap')
87 | def threatMap():
88 | return render_template('threatMap.html')
89 |
90 |
91 | @app.route('/save_data', methods=['POST'])
92 | def save_data():
93 | data_list = request.get_json()
94 |
95 | with sqlite3.connect(DB_FILE) as conn:
96 | cursor = conn.cursor()
97 | for data in data_list:
98 | cursor.execute('''INSERT INTO credentials (sid, client, host, port, use_https, username, password)
99 | VALUES (?, ?, ?, ?, ?, ?, ?)''',
100 | (
101 | data['sid'], data['client'], data['host'], data['port'], data['use_https'], data['username'],
102 | data['password']))
103 | conn.commit()
104 |
105 | return jsonify({'message': 'Data saved successfully!'})
106 |
107 |
108 | @app.route('/scan', methods=['GET'])
109 | def scan():
110 | with sqlite3.connect(DB_FILE) as conn:
111 | cursor = conn.cursor()
112 | cursor.execute('''SELECT sid, client, host, port, use_https, username, password FROM credentials''')
113 | credentials = cursor.fetchall()
114 | cursor.execute('''DELETE FROM sap_connections''')
115 |
116 | sap_connection_array = []
117 | for credential in credentials:
118 | sap_connection_array.append({
119 | 'sid': credential[0],
120 | 'client': credential[1],
121 | 'host': credential[2],
122 | 'port': credential[3],
123 | 'use_https': credential[4],
124 | 'username': credential[5],
125 | 'password': credential[6],
126 | })
127 | for sap_connection in sap_connection_array:
128 | scan_result_list = scan_systems(sap_connection['host'], sap_connection['port'],
129 | sap_connection['client'], sap_connection['username'],
130 | sap_connection['password'], sap_connection['use_https'])
131 | for scan_result in scan_result_list:
132 | cursor.execute('''INSERT INTO sap_connections
133 | (connection_name, connection_type, connection_from, connection_to, username, password_flag)
134 | VALUES (?, ?, ?, ?, ?, ?)''',
135 | (scan_result.connection_name, scan_result.connection_type,
136 | scan_result.connection_from, scan_result.connection_to, scan_result.username,
137 | scan_result.password_flag))
138 | print(scan_result)
139 | conn.commit()
140 |
141 | return jsonify('')
142 |
143 |
144 | @app.route('/get_connections')
145 | def get_connections():
146 | try:
147 | password_only = request.args.get('password_only')
148 |
149 | with sqlite3.connect(DB_FILE) as conn:
150 | cursor = conn.cursor()
151 | if password_only and password_only.lower() == 'true':
152 | cursor.execute(
153 | '''SELECT connection_from, connection_to, connection_name FROM sap_connections WHERE password_flag = 1''')
154 | else:
155 | cursor.execute('''SELECT connection_from, connection_to, connection_name FROM sap_connections''')
156 | connections = cursor.fetchall()
157 |
158 | connection_data = [
159 | {'connection_from': connection[0], 'connection_to': connection[1], 'connection_name': connection[2]} for
160 | connection in connections]
161 | return jsonify(connection_data)
162 | except Exception as e:
163 | return jsonify({'error': str(e)})
164 |
165 |
166 | if __name__ == '__main__':
167 | app.run(debug=False)
168 |
--------------------------------------------------------------------------------
/images/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redrays-io/SAP-Threat-Modeling/083ceb25d4325709bf50d0357fd3a85f2657a451/images/1.png
--------------------------------------------------------------------------------
/images/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redrays-io/SAP-Threat-Modeling/083ceb25d4325709bf50d0357fd3a85f2657a451/images/2.png
--------------------------------------------------------------------------------
/images/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redrays-io/SAP-Threat-Modeling/083ceb25d4325709bf50d0357fd3a85f2657a451/images/3.png
--------------------------------------------------------------------------------
/images/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redrays-io/SAP-Threat-Modeling/083ceb25d4325709bf50d0357fd3a85f2657a451/images/4.png
--------------------------------------------------------------------------------
/images/RedRays_Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redrays-io/SAP-Threat-Modeling/083ceb25d4325709bf50d0357fd3a85f2657a451/images/RedRays_Logo.png
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redrays-io/SAP-Threat-Modeling/083ceb25d4325709bf50d0357fd3a85f2657a451/requirements.txt
--------------------------------------------------------------------------------
/sap_utils/connections/sap_connections.py:
--------------------------------------------------------------------------------
1 | class SAPConnection:
2 | def __init__(self, connection_name, connection_type, connection_from, connection_to, username, password_flag):
3 | self.connection_name = connection_name
4 | self.connection_type = connection_type
5 | self.connection_from = connection_from
6 | self.connection_to = connection_to
7 | self.username = username
8 | self.password_flag = password_flag
9 |
10 | def display_info(self):
11 | print("Connection Name:", self.connection_name)
12 | print("Connection Type:", self.connection_type)
13 | print("Connection From:", self.connection_from)
14 | print("Connection To:", self.connection_to)
15 | print("Connection username:", self.username)
16 | print("Connection Password saved:", self.password_flag)
17 |
--------------------------------------------------------------------------------
/sap_utils/scanning/scan.py:
--------------------------------------------------------------------------------
1 | import base64
2 | import re
3 | import xml.etree.ElementTree as ET
4 |
5 | import requests
6 |
7 | from sap_utils.connections.sap_connections import SAPConnection
8 |
9 | """
10 | --------------------------------------------------------------------------------
11 | | Key | Meaning / Explanation |
12 | |-------------|------------------------------------------------------------------------------|
13 | | H= | Hostname or IP address |
14 | | S= | System number |
15 | | M= | Client number |
16 | | U= | RFC user |
17 | | L= | Language |
18 | | X= | Load balancing (LB=ON) |
19 | | I= | System ID |
20 | | N= | Logon group |
21 | | Z= | Various authentication-related parameters |
22 | | g= | Gateway server |
23 | | F= | Enforce codepage |
24 | | f=Y | RFC Ticket for Java |
25 | | J=... | SSL Client Application |
26 | | j=Y | RFC Unicode-Support |
27 | | m=Y | MDMP Settings (Multi-Display/Multi-Processing) |
28 | | rfcsnc: | SNC activated (secure connection) |
29 | | t=... | SSL Client Application |
30 | | r=... | Proxy password |
31 | | R=... | Proxy user |
32 | | v=... | Saved password |
33 | | z=... | KEEPALIVE timeout |
34 | | y=... | CPIC timeout |
35 | | h=... | REACT or Trace option |
36 | | e=... | GW Starttype (internal gateway param) |
37 | | q= | qRFCVERS start (queued RFC version) |
38 | | w=... | RFCCATEGORY start (custom category) |
39 | | F=. | RFCHTTP-Structure (overload of the key “F” in some configurations) |
40 | | N=. | Path-Prefix for RFC requests |
41 | | d=. | Unicode-Flag |
42 | | k=Y | rfcwan: slow connection |
43 | | x=. | Authority checks |
44 | | b=Y | RFCLOGON -> Login screen |
45 | | i=Y | SAVESERVER as IP |
46 | | u=Y | RFC same user prepared |
47 | | O=X | ARFC-Option prepared |
48 | | l=X | Display unchangeable (locked) |
49 | --------------------------------------------------------------------------------
50 | """
51 |
52 | xml_request_body_rfc_read_table = """
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | |
62 |
63 |
64 | RFCDEST
65 |
66 |
67 | RFCTYPE
68 |
69 |
70 | RFCOPTIONS
71 |
72 |
73 | RFCDES
74 |
75 |
76 | """
77 | delimiter = "|"
78 | connect_to_regexp = r"H=([^,]+)"
79 | saved_password_regexp = r"v=([^,]+)"
80 | username_regexp = "[U D]=([^,]+)"
81 |
82 |
83 | def send_request(host, port, sap_client, username, password, secure=True):
84 | # Determine the scheme based on the 'secure' parameter
85 | scheme = "https" if secure else "http"
86 |
87 | # Construct the URL
88 | url = f"{scheme}://{host}:{port}/sap/bc/soap/rfc?sap-client={sap_client}"
89 |
90 | # Prepare headers
91 | headers = {
92 | "Content-Type": "text/xml",
93 | "User-Agent": "RedRays Threat Modeling OSS/1.0.0"
94 | }
95 |
96 | # Encode username and password for Basic Authentication
97 | encoded_auth = base64.b64encode(f"{username}:{password}".encode("utf-8")).decode("ascii")
98 | headers["Authorization"] = f"Basic {encoded_auth}"
99 |
100 | # Send the request
101 | response = requests.post(url, headers=headers, data=xml_request_body_rfc_read_table)
102 |
103 | # Print response
104 | print("Response Status Code:", response.status_code)
105 | return response.text
106 |
107 |
108 | def parse_xml(xml_input):
109 | wa_values = []
110 | try:
111 | # Parse the XML string
112 | root = ET.fromstring(xml_input)
113 |
114 | for sid in root.iterfind('.//item//WA'):
115 | wa_values.append(sid.text)
116 |
117 | except Exception as e:
118 | print("An error occurred while parsing XML:", e)
119 |
120 | return wa_values
121 |
122 |
123 | def scan_systems(host, port, sap_client, username, password, secure=True):
124 | sap_connection_array = []
125 |
126 | response_text = send_request(host, port, sap_client, username, password, secure=False)
127 | wa_values = parse_xml(response_text)
128 | print(wa_values)
129 | for wa_value in wa_values:
130 | if wa_value is not None:
131 | connection_array = wa_value.split(delimiter)
132 | match_connection_to = re.search(connect_to_regexp, connection_array[2])
133 | saved_password_match = re.search(saved_password_regexp, connection_array[2])
134 | match_username = re.search(username_regexp, connection_array[2])
135 |
136 | if match_connection_to is not None:
137 | saved_password = saved_password_match.group(1) if saved_password_match else ''
138 | username = match_username.group(1) if match_username else ''
139 |
140 | sap_connection_array.append(SAPConnection(connection_array[0], connection_array[1], host,
141 | match_connection_to.group(1), username,
142 | saved_password_match is not None))
143 |
144 | return sap_connection_array
145 |
146 |
147 | # Example usage
148 | if __name__ == "__main__":
149 | host = "sap.local"
150 | port = 50000
151 | sap_client = "000"
152 | username = "SAP*"
153 | password = "asdQWE123#"
154 | sap_connections = scan_systems(host, port, sap_client, username, password)
155 | print(sap_connections)
156 |
--------------------------------------------------------------------------------
/templates/base.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {% block title %}{% endblock %}
7 |
8 |
9 |
12 |
13 |
14 |
40 |
41 |
This application helps you analyze and visualize the connections between your SAP systems, enabling you to identify potential security risks and vulnerabilities.
9 |
10 |
How it Works:
11 |
12 |
13 |
14 | **Initial Connections:**
15 | Start by inputting the credentials of your SAP systems on the "Init Connections" page. This includes the System ID (SID), username, password, client number, host address, and port number. You can add multiple systems and easily remove them if needed.
16 |
17 |
18 | **Scanning for Inter-SAP Connections:**
19 | Once you've added your initial connections, click the "Scan" button. The tool will then analyze your SAP systems and discover the connections between them. This information, including connection names, types, source and destination systems, usernames, and password storage flags, is displayed on the "Inter-SAP Connections" page.
20 |
21 |
22 | **Visualizing the Network:**
23 | The "Graph Connection" page provides a visual representation of your SAP network. Each system is represented as a node, and the connections between them are shown as lines. You can filter the graph to only show connections with stored passwords, highlighting potential security concerns.
24 |
25 |
26 |
27 |
Benefits:
28 |
29 |
30 |
Identify systems with saved passwords, which pose a significant security risk.
31 |
Understand the flow of data between systems to assess potential breach impact.
32 |
Evaluate the overall security posture of your SAP environment.
33 |
34 |
35 |
Start exploring your SAP network today and take control of your system's security!