From ff7e92dcd8a86ebcf17c2e69d103e4315724c13b Mon Sep 17 00:00:00 2001 From: Yannic Nevado Hidalgo <yannic.hidalgo@stud.h-da.de> Date: Mon, 24 Jan 2022 22:49:02 +0100 Subject: [PATCH] Initial commit --- .idea/workspace.xml | 78 +++++++++++++++++++++++++++++++++++++++++++++ README.md | 32 +++++++++++++++++-- main.py | 17 +++------- 3 files changed, 111 insertions(+), 16 deletions(-) create mode 100644 .idea/workspace.xml diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..c46bf30 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="BranchesTreeState"> + <expand> + <path> + <item name="ROOT" type="e8cecc67:BranchNodeDescriptor" /> + <item name="LOCAL_ROOT" type="e8cecc67:BranchNodeDescriptor" /> + </path> + </expand> + <select /> + </component> + <component name="ChangeListManager"> + <list default="true" id="f54bb93f-1ebd-4e7e-a94e-f90b5ecaf90a" name="Default Changelist" comment=""> + <change afterPath="$PROJECT_DIR$/docker-compose.yml" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/app.py" beforeDir="false" /> + <change beforePath="$PROJECT_DIR$/main.py" beforeDir="false" afterPath="$PROJECT_DIR$/main.py" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/requirements.txt" beforeDir="false" afterPath="$PROJECT_DIR$/requirements.txt" afterDir="false" /> + </list> + <option name="SHOW_DIALOG" value="false" /> + <option name="HIGHLIGHT_CONFLICTS" value="true" /> + <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> + <option name="LAST_RESOLUTION" value="IGNORE" /> + </component> + <component name="Git.Settings"> + <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" /> + </component> + <component name="ProjectId" id="249a0qEgfC8HD9PTFP9mlVwxDOa" /> + <component name="ProjectViewState"> + <option name="hideEmptyMiddlePackages" value="true" /> + <option name="showLibraryContents" value="true" /> + </component> + <component name="PropertiesComponent"> + <property name="RunOnceActivity.OpenProjectViewOnStart" value="true" /> + <property name="last_opened_file_path" value="$PROJECT_DIR$" /> + <property name="settings.editor.selected.configurable" value="com.jetbrains.python.configuration.PythonContentEntriesConfigurable" /> + </component> + <component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" /> + <component name="TaskManager"> + <task active="true" id="Default" summary="Default task"> + <changelist id="f54bb93f-1ebd-4e7e-a94e-f90b5ecaf90a" name="Default Changelist" comment="" /> + <created>1643045037022</created> + <option name="number" value="Default" /> + <option name="presentableId" value="Default" /> + <updated>1643045037022</updated> + </task> + <servers /> + </component> + <component name="Vcs.Log.Tabs.Properties"> + <option name="TAB_STATES"> + <map> + <entry key="MAIN"> + <value> + <State /> + </value> + </entry> + </map> + </option> + </component> + <component name="WindowStateProjectService"> + <state x="-1496" y="270" key="#com.intellij.execution.impl.EditConfigurationsDialog" timestamp="1643045084941"> + <screen x="-1920" y="71" width="1920" height="1080" /> + </state> + <state x="-1496" y="270" key="#com.intellij.execution.impl.EditConfigurationsDialog/-1920.71.1920.1080/0.0.1920.1200@-1920.71.1920.1080" timestamp="1643045084941" /> + <state x="-1237" y="417" key="#com.intellij.fileTypes.FileTypeChooser" timestamp="1643052698194"> + <screen x="-1920" y="71" width="1920" height="1080" /> + </state> + <state x="-1237" y="417" key="#com.intellij.fileTypes.FileTypeChooser/-1920.71.1920.1080/0.0.1920.1200@-1920.71.1920.1080" timestamp="1643052698194" /> + <state x="-1451" y="247" key="SettingsEditor" timestamp="1643045118299"> + <screen x="-1920" y="71" width="1920" height="1080" /> + </state> + <state x="-1451" y="247" key="SettingsEditor/-1920.71.1920.1080/0.25.1920.1175@-1920.71.1920.1080" timestamp="1643045118299" /> + <state x="-1295" y="312" width="670" height="676" key="search.everywhere.popup" timestamp="1643052713613"> + <screen x="-1920" y="71" width="1920" height="1080" /> + </state> + <state x="-1295" y="312" width="670" height="676" key="search.everywhere.popup/-1920.71.1920.1080/0.0.1920.1200@-1920.71.1920.1080" timestamp="1643052713613" /> + </component> +</project> \ No newline at end of file diff --git a/README.md b/README.md index d2dbac8..132fe26 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # challenge-security-web-applications -## Datenbank, tabelle und Benutzer anlegen +## Datenbank, Tabelle und Benutzer anlegen Die Datenbank wird in einem PostgreSQL Docker Container erstellt, der über `docker-compose` gestartet wird. @@ -21,13 +21,39 @@ Im nächsten Schritt wird die Tabelle `users` angelegt und drei Benutzer hinzuge ```sql CREATE TABLE users ( username varchar(10), - admin boolean + is_admin boolean ); INSERT INTO users - (username, admin) + (username, is_admin) VALUES ('alice', true), ('bob', false), ('mallory', false); ``` + + +## Erklärung + +Das Python Programm wird wie folgt aufgerufen: `python main.py "<username>"` + +Mit der Übergabe eines Benutzernamens, wird überprüft, ob dieser ein Administrator ist. Aufgrund einer Schwachstelle in der `SELECT` Abfrage des Programm Codes, besteht nun aber die Möglichkeit Benutzern Administrator Rechte zu geben, ohne sich auf der Datenbank einzuloggen. + +``` +# Überprüfen ob der Benutzer 'mallory' ein Adminstrator ist +python main.py "mallory" + +# Schwachstelle ausnutzen und 'mallory' Administrator Rechte geben +python main.py "'; update users set is_admin = 'true' where username = 'mallory'; select true; --" +``` + +Ruft man nun die Funktion wieder mit dem Benutzernamen auf `python main.py "mallory"` bekommt man statt `False` nun ein `True` zurückgegeben. +Die Schwachstelle in der Abfrage besteht darin, da keine Überprüfung erfolgt, welcher `string` in die Abfrage übergeben wird. Das Semikolon innerhalb der Abfrage spielt eine Entscheide Rolle, da es SQL Abfragen terminiert. Da keine weitere Überprüfung, des Inhalts erfolgt, den wir an die Funktion übergeben, können wir nun mithilde eines `UPDATE` Befehls Werte innerhalb der Tabelle anpassen. + +Die Schwachstelle im Code (`SQL Abfrage`) kann wie folgt behoben werden: + +``` +"""SELECT is_admin FROM users WHERE username = %(username)s """, {'username': username} +``` + +Die Query wird nicht mehr mittels String Interpolation (`'%s' """ % username`) zusammengesetzt und an den Datenbankserver gesendet. Durch Abfrageparameter `%(username)s """, {'username': username}` wird nun sichergestellt, das der richtige Typ und Wert als `Parameter` an die Abfrage übergeben wird. \ No newline at end of file diff --git a/main.py b/main.py index 59d8061..c59eb4d 100644 --- a/main.py +++ b/main.py @@ -3,27 +3,19 @@ import psycopg2 def get_connection(): - db_connect = psycopg2.connect( - host="localhost", - database="postgres", - user="postgres", - password="postgres", - ) + db_connect = psycopg2.connect(host="localhost", database="postgres", user="postgres", password="postgres") db_connect.set_session(autocommit=True) return db_connect def is_admin(username: str) -> bool: conn = get_connection() - with conn.cursor() as cursor: - # UNSECURE - cursor.execute("""SELECT admin FROM users WHERE username = '%s' """ % username) - - # SECURE - #cursor.execute("""SELECT admin FROM users WHERE username = %(username)s """, {'username': username}) + with conn.cursor() as cursor: + cursor.execute("""SELECT is_admin FROM users WHERE username = '%s' """ % username) result = cursor.fetchone() + # fetchone gibt NONE zurück, falls kein Benutzer gefunden wird if result is None: return False @@ -33,5 +25,4 @@ def is_admin(username: str) -> bool: if __name__ == "__main__": username = str(argv[1]) - # is_admin("'; update users set admin = 'true' where username = 'mallory'; select true; --") print(username + " is an admin: " + str(is_admin(username))) \ No newline at end of file -- GitLab