This page has been robot translated, sorry for typos if any. Original content here.

SQL injection in Oracle

  • [Introduction]
  • [Oracle Features]
  • [Column selection]
  • [Definition of printable columns]
  • [Receiving the information]
  • [Tables and Columns]
  • [Passwords]
  • [Receiving the information]

  • [Introduction]

    Recently, when researching various web projects on vulnerabilities, I began to stumble upon sql injection into Oracle. Although at the present time it is rare to find the use of this DBMS in Web programming, it still happens. All research ended with a simple detection of bugs, what to do next was not clear. A search for an article that well describes the practical aspects of exploiting this vulnerability in Oracle, such as the cash and spyder articles that describe injections in MSSQL and PostgreSQL, could not be found.
    As a result of the search, only a series of articles k00p3r was found, which was completely copied from third-party sources and was a simple translation, the reading of which did not give a clear idea of ​​how the whining was conducted in Oracle, since articles are essentially large, theoretical, and for the most part contain water.
    In the end, I had to remember the institutional skills of working with Oracle and reread a bunch of scattered information on the Internet. In this article I would like to share with you what I was able to dig up for conducting sql injection in Oracle and try to combine it into one.
    Well, try to fill the gap, and add a series of articles cash and spyder.

    [Oracle Features]

    First, I will give some properties that must be considered when conducting an injection into Oracle. Just want to make a reservation that the article deals with the injection in the SELECT statement. Although injections in the INSERT, UPDATE and DELETE statements are also possible.
    It is also important that the article discusses the issues of conducting an injection in Oracle SQL queries, and not in Oracle PL / SQL procedures. The significant difference between injections in PL / SQL procedures is the possibility of using the query separator - the semicolon character ";". But about this IMHO you need to write a separate article, with a description of all the ensuing consequences. Moreover, the author believes that in Web applications the most frequent injections are found in SQL queries (at least I have come across only such).
    In Oracle, as in MySQL and PostgreSQL, the injection is carried out by using the UNION operator, i.e. with the compilation of the combination of two queries (hereinafter, for ease of understanding, the term is used - the subquery). But besides the coincidence of the number of columns in the main query and the subquery, it is necessary to take into account that Oracle does not perform automatic type casting in the subquery. Therefore, when selecting columns, it is necessary to substitute null, unlike, for example, from MySQL.
    It is also a very important property that all SELECT queries must be made from some table, i.e. The query syntax should always contain the word FROM and the name of the table. For simple arithmetic calculations or other operations that do not require a real table, there is a pseudo table SYS.DUAL in Oracle.
    An important property is the absence of the LIMIT operator.
    To truncate a query, the comment characters “-” (two dashes) and "/ *" (forward slash and asterisk) are used in Oracle SQL. The first type of comments is single line. The second type is multiline.
    There is no possibility of using multiple queries in SQL Oracle using the delimiter “;”, in contrast to PL / SQL procedures.
    When an error is detected, you can uniquely identify Oracle by the presence of the word ORA in the text of the error message, for example:

    Macromedia][Oracle JDBC Driver][Oracle]ORA-00933: SQL command not properly ended

    Not always in the text of the error is the word Oracle, for example

    Warning: OCIStmtExecute: ORA-01722: invalid number in

    [Column selection]

    Let the error be present in the id parameter:'
    Determining the number of columns present in the main query is the same as in MySQL. Since the UNION operator requires the same number of columns, both in the main query and in the subquery we need to determine the number of these columns. If the columns in the subquery are not specified correctly, the standard error message is displayed:

    ORA-XXXXX: query block has incorrect number of result columns
    For the selection of columns, there are 2 known methods and are well described in the article spyder. But I will bring them again, so that the reader does not run through the articles:

    1. Simple bust.
    We make the following request

    If an error occurs, increase the number of columns by one., null+from+sys.dual--

    and so on until the error disappears.

    2. Using the ORDER BY operator
    The second method is much faster and more pleasant if the number of columns is large enough. We make the following request
    if there is no error, then columns 1 or more 1
    With such a query, an error should appear, which means the columns are less than 99999. Further, in the same way, narrow the boundaries of the selected interval to the left and to the right and ultimately determine the actual number of columns in the main query.

    [Definition of printable columns]

    Suppose we have determined the exact number of columns in the main query, suppose they are 4., null, null, null+from+sys.dual--
    Now we need to determine the columns that are displayed on the page. Typically, columns with data types int, char, and data are involved in the output. We will have enough printable columns with types int and char, and we will look for them.
    As noted earlier, Oracle does not perform automatic type casting in a subquery. Therefore, when trying to substitute the values ​​of an inappropriate type in any column, we will get the following type mismatch error

    ORA-XXXXX: expression must have same datatype as corresponding expression
    Next, we begin to make queries, alternately replacing each column with any number, null, null, null+from+sys.dual--
    ...., 123, null, null+from+sys.dual--
    Thus, we will identify printable columns with type int. In that case, if we get a type mismatch error, we can use the type conversion functions to_char (), to_date () and identify printable columns with types char and data., to_char(123), null, null+from+sys.dual--
    For reference, here is the syntax of the to_char () function:
    to_char (value, [format_mask], [nls_language])

    [Receiving the information]

    After we have learned the number of columns and which of them are printable, we can safely move on to getting the necessary information from the database. Well, if we know certain tables in the database and columns in them, then obtaining information will not be difficult. For example, if there is a USERS table with the columns ID, LOGIN and PASSWORD, then the request for obtaining this data will look like this, login, password, null+from+users+where+id=123--
    Just as in MySQL, for convenience of displaying and overcoming various problems with encodings, you can use the functions concat (), to_char ().
    To overcome the filter quotes or other necessary characters, there is a function chr ().

    [Tables and Columns]

    If the user tables are unknown to us, then we can get various information from the well-known Oracle system tables.
    You can find out the name of the user under which the interface works, and therefore you can, by calling the user or sys.login_user functions, user, null, null+from+sys.dual--
    You can get a list of sessions like this: select * from V $ session

    Of great interest are the SYS.USER_TABLES and SYS.USER_TAB_COLUMNS tables, which contain all the tables and their columns accessible to the user. We take out the names of the tables and columns:, table_name, null, null+from+sys.user_tables--, column_name, null, null+from+sys.user_tab_columns--
    Also, in my opinion, in the SYS.USER_TABLES table besides table_name, the following columns are of interest: tablespace_name, num_rows, freelist_groups.
    But, unfortunately, the above queries will output us only one by one - the first record from the entire table. There is an overwhelming desire to use the LIMIT operator, as in MySQL or PostgreSQL. To the great general disappointment, this operator is not supported in Oracle, and moreover it does not have a worthy equivalent in the form of another operator.
    “ALL MISSED !!!” - you will say.
    “NO !!!” - I will answer you.
    Having tormented a pretty google, I still found the opportunity to make a complex query at least somehow distantly realizing the semantic load of the LIMIT operator. Unfortunately, it was not possible to restore its capabilities in full., table_name, null, null+from+sys.user_tables+where+rownum+<=+5--
    Thus, going through a different number of records in the sample, we can look at all the table names in turn. We can use the same construction when viewing the SYS.USER_TAB_COLUMNS table when getting all the column names available to the user.
    Also in Oracle, there is the concept of an object prefix (a table is an object), which is present in the name or name of a table:
    ALL_ - all available to the user (the owner may not be),
    USER_ - objects owned by this user.
    Therefore, we can simplify our task and pull out the names of only those tables to which we have access., table_name, null, null+from+sys.all_tables
    Information from the following standard tables may also be of interest: SYS.USER_OBJECTS, SYS.USER_VIEWS, SYS.USER_VIEWS, SYS.USER_CATALOG, SYS.USER_TRIGGERS, SYS.TAB.


    If we are lucky and the user under which we work with the database has sysdba rights, then we can get the hashes of all users of the database.
    The main storage location of the password convolution (hash) is the table of the SYS.USER $ dictionary-directory. Above this table as a base built derivative, SYS.DBA_USERS. If PASSWORD_REUSE_TIME is enabled in the user profile, password convolutions are also stored in SYS.USER_HISTORY $. You can get hashes and usernames like this, username, password, null+from+sys.dba_users
    For completeness of information, I will also provide an algorithm for calculating the convolution of a password, just in case, it may be useful to someone:
    1. To the user name is pasted on the right text of the password.
    2. In the resulting string, the letters increase the register.
    3. The characters of the string are converted to a two-byte format with a left padding with a zero value of 0x00 (for ASCII characters), and the string to the right is appended with zero bytes up to a total length of 80.
    4. The resulting string is encrypted using the DES algorithm in the ciphertext block concatenation mode (CBC) with the key 0x0123456789ABCDEF.
    5. The parity bits are removed from the last result block and the resulting string (56 bits) is used to re-encrypt the source string in the same way.
    6. The last block of the result is translated into hexadecimal arithmetic signs and declared the final result - convolution.