Technical Analysis of Oracle10g unwrap

Source: Internet
Author: User
Tags 0xc0 deflater sha1 hash

0x01 Preface

Oracle provides PL/SQL developers with a tool to encrypt the code they write (the objects Package, Procedure, Function, and Java Source in oracle. After PL/SQL code is encrypted, it is described as "encapsulated ". Use the wrap tool to wrap the code. The wrap tool extracts the file name containing the Code to be packaged and outputs the following file:
Wrap iname = plain. SQL oname = encrypted. plb
Because the code is encrypted, its details are hidden, and Oracle does not provide unwrapped devices. But we can write our own unwrap program.

0x02 object Encryption

Oracle also made wrap to prevent leakage of its own object program source code. Many programs such as PACKAGE and PROCEDURE may have SQL injection vulnerabilities, but it is very difficult to audit them if the source code is not available. In this way, wrap also plays a protective role to a certain extent.
Here we can find an Oracle PACKAGE as an example. Here we find the dbms_hs PACKAGE program and its code:

The code is obviously encrypted.

0x03 Oracle Wrap algorithm Mechanism

The Oracle encryption principle is to first perform lz compression on the source code lzstr, and then perform the SHA-1 operation on the compressed data to obtain the 40-bit encrypted string shstr, then, the encrypted string and the compressed string are spliced to get shstr + lzstr, and then Oracle dual-character conversion (conversion table) is performed on the concatenated string ). Finally, encode the converted string with base64 to obtain the encrypted string of the wrap.
For LZ compression, a JAVA package provided by foreigners is used here. The Code is as follows:

 

create or replace java source named MY_COMPRESSasimport java.io.*;import java.util.zip.*;public class MY_COMPRESS{  public static String Inflate( byte[] src )  {    try    {      ByteArrayInputStream bis = new ByteArrayInputStream( src );      InflaterInputStream iis = new InflaterInputStream( bis );      StringBuffer sb = new StringBuffer();      for( int c = iis.read(); c != -1; c = iis.read() )      {        sb.append( (char) c );      }      return sb.toString();    } catch ( Exception e )    {    }    return null;  }  public static byte[] Deflate( String src, int quality )  {    try    {      byte[] tmp = new byte[ src.length() + 100 ];      Deflater defl = new Deflater( quality );      defl.setInput( src.getBytes( "UTF-8" ) );      defl.finish();      int cnt = defl.deflate( tmp );      byte[] res = new byte[ cnt ];      for( int i = 0; i < cnt; i++ )        res[i] = tmp[i];      return res;    } catch ( Exception e )    {    }    return null;  }}/alter java source MY_COMPRESS compile/create or replace package mycompressis  function deflate( src in varchar2 )  return raw;--  function deflate( src in varchar2, quality in number )  return raw;--  function inflate( src in raw )  return varchar2;--end;/create or replace package body mycompressis  function deflate( src in varchar2 )  return raw  is  begin    return deflate( src, 6 );  end;--  function deflate( src in varchar2, quality in number )  return raw  as language java  name 'MY_COMPRESS.Deflate( java.lang.String, int ) return byte[]';--  function inflate( src in raw )  return varchar2  as language java  name 'MY_COMPRESS.Inflate( byte[] ) return java.lang.String';--end;/


 

Here, the deflate function implements LZ compression, while inflate implements decompression.
First, we use a small PL/SQL code to test the structure of the encrypted string. Here I will talk about Program Encryption ., Here, the create PACKAGE a code wrap is changed to the following code:

To further test and analyze the structure of the encrypted string, we use the following code:

with src as( select 'PACKAGE a' txt from dual ), wrap as( select src.txt, dbms_ddl.wrap( 'create ' || src.txt ) wrap from src )Select rtrim( substr( wrap.wrap, instr(wrap.wrap, chr(10), 1, 20 ) + 1 ),chr(10) ) from wrap;

 

This code gets the encrypted string,

The code is further base64 decoded to obtain the string after Oracle dual-character conversion ,:

Because the string is composed of two parts, it is first compressed by LZ, then processed by SHA-1, and then spliced by the SHA-1 string and the compressed string, because SHA-1 has a total of 40 characters, therefore, after 40 bits, all the compression strings converted by Oracle dual-character conversion, that is, 308399B8F5339FF5BF5CB891A6A6CBBFE1DC

0x04 computing and conversion table

After the sha-1 string and the compressed string are spliced in wrap, Oracle dual-character conversion is performed against a character instead of a table. This table may be an Oracle trade secret, so the official website does not provide information about this table.
However, since we have obtained the LZ compression string after character conversion, we can also get the pure LZ compression string through the JAVA package mentioned above, by comparing the two strings, we can calculate the conversion table.
When using this java package, a compression level parameter is involved. This level parameter is different, and the compressed string is completely different. Someone may ask, isn't it possible to get a replacement table? Yes, but not exactly. Because the available level parameters are limited, we can start testing one by one from level 0. After testing, we found that ORACLE uses the "9" level. So we use the following code to compare two strings:

with src as( select 'PACKAGE c' txt from dual ), wrap as( select src.txt, dbms_ddl.wrap( 'create ' || src.txt ) wrap from src ), subst as( select substr( utl_encode.base64_decode( utl_raw.cast_to_raw( rtrim( substr( wrap.wrap, instr(wrap.wrap, chr(10), 1, 20 ) + 1 ),chr(10) ) ) ), 41 ) x, mycompress.deflate( wrap.txt || chr(0),9 ) dfrom wrap )select to_number( substr( x, r * 2 - 1, 2),'xx' ) wrapped     , to_number( substr( d, r * 2 - 1, 2),'xx' ) zippedfrom subst    , ( select rownum r from dual connect by rownum < 19 );

 

The result is as follows:

By sorting the results, there is no situation where the same BASE64 encoding corresponds to different hexadecimal formats. Therefore, we know that this SQL can be used as the basis, replace table content with different SOURCE strings.
Create a table based on the preceding SQL statement to store the content of the replace table,

CREATE TABLE SYS.IDLTRANSLATE(    C_BASE64DECODE  VARCHAR2(2) NOT NULL,    C_LZDEFLATECODE VARCHAR2(2)     NULL    )

 

Then write a PLSQL block to generate the content of the replace table:

declare  nCnt integer;  nLoop integer;  nSLoop integer;  nCharmax integer;  nCharmin  integer;  vChar     Varchar2(3);  cursor getchar is    with src AS ( select 'PACKAGE '||vChar txt  from dual   ),     wrap as   ( select src.txt , dbms_ddl.wrap( 'create ' || src.txt ) wrap  from src  ),     subst as  (select substr( utl_encode.base64_decode( utl_raw.cast_to_raw(rtrim( substr( wrap.wrap, instr( wrap.wrap, chr( 10 ), 1, 20 ) + 1 ), chr(10) )  ) ), 41 ) x,         mycompress.deflate( wrap.txt || chr(0), 9 ) d       from wrap  )    select  substr( x, r * 2 - 1, 2 )  xr ,    substr( d, r * 2 - 1, 2 )  dr            from subst  , ( select rownum r from dual connect by rownum <= ( select length( x ) / 2 from subst ) );begin    nCharmax:=97;nCharmin:=122;For nLoop In 97..122 Loop  For nSloop In 0..99 Loop   vChar := chr(nLoop)||to_char(nSloop);   For abc In getchar Loop    Select Count(*) Into nCnt From sys.idltranslate WHERE c_base64decode = abc.xr;    If nCnt < 1 Then     Insert INTO sys.idltranslate VALUES (abc.xr,abc.dr);     Commit;    Else     Select Count(*) Into ncnt From sys.idltranslate WHERE c_base64decode = abc.xr AND c_lzdeflatecode=abc.dr;     If nCnt < 1 Then      DBMS_OUTPUT.PUT_LINE('wrong orginal char:'||vchar||'         hex base64:'||abc.xr);     End If;    End If;       End Loop;  End Loop;End Loop;end;

 

Running the preceding SQL statement will generate more than 100 records, which has not met the requirements of 256 records in a total of-FF records, we recommend that you replace the select 'package' | the PACKAGE password in vChar txt from dual is procedure or similar to the function. Continue running until there are no repeated 256 records in the replace table. With the content of the replace table, it is no longer difficult to unwrap the plaintext.
Here we provide the content of the replace table. I will write it into a variable, as shown below:

int charmap[] = {0x3d, 0x65, 0x85, 0xb3, 0x18, 0xdb, 0xe2, 0x87, 0xf1, 0x52, 0xab, 0x63, 0x4b, 0xb5, 0xa0, 0x5f, 0x7d, 0x68, 0x7b, 0x9b, 0x24, 0xc2, 0x28, 0x67, 0x8a, 0xde, 0xa4, 0x26, 0x1e, 0x03, 0xeb, 0x17, 0x6f, 0x34, 0x3e, 0x7a, 0x3f, 0xd2, 0xa9, 0x6a, 0x0f, 0xe9, 0x35, 0x56, 0x1f, 0xb1, 0x4d, 0x10, 0x78, 0xd9, 0x75, 0xf6, 0xbc, 0x41, 0x04, 0x81, 0x61, 0x06, 0xf9, 0xad, 0xd6, 0xd5, 0x29, 0x7e, 0x86, 0x9e, 0x79, 0xe5, 0x05, 0xba, 0x84, 0xcc, 0x6e, 0x27, 0x8e, 0xb0, 0x5d, 0xa8, 0xf3, 0x9f, 0xd0, 0xa2, 0x71, 0xb8, 0x58, 0xdd, 0x2c, 0x38, 0x99, 0x4c, 0x48, 0x07, 0x55, 0xe4, 0x53, 0x8c, 0x46, 0xb6, 0x2d, 0xa5, 0xaf, 0x32, 0x22, 0x40, 0xdc, 0x50, 0xc3, 0xa1, 0x25, 0x8b, 0x9c, 0x16, 0x60, 0x5c, 0xcf, 0xfd, 0x0c, 0x98, 0x1c, 0xd4, 0x37, 0x6d, 0x3c, 0x3a, 0x30, 0xe8, 0x6c, 0x31, 0x47, 0xf5, 0x33, 0xda, 0x43, 0xc8, 0xe3, 0x5e, 0x19, 0x94, 0xec, 0xe6, 0xa3, 0x95, 0x14, 0xe0, 0x9d, 0x64, 0xfa, 0x59, 0x15, 0xc5, 0x2f, 0xca, 0xbb, 0x0b, 0xdf, 0xf2, 0x97, 0xbf, 0x0a, 0x76, 0xb4, 0x49, 0x44, 0x5a, 0x1d, 0xf0, 0x00, 0x96, 0x21, 0x80, 0x7f, 0x1a, 0x82, 0x39, 0x4f, 0xc1, 0xa7, 0xd7, 0x0d, 0xd1, 0xd8, 0xff, 0x13, 0x93, 0x70, 0xee, 0x5b, 0xef, 0xbe, 0x09, 0xb9, 0x77, 0x72, 0xe7, 0xb2, 0x54, 0xb7, 0x2a, 0xc7, 0x73, 0x90, 0x66, 0x20, 0x0e, 0x51, 0xed, 0xf8, 0x7c, 0x8f, 0x2e, 0xf4, 0x12, 0xc6, 0x2b, 0x83, 0xcd, 0xac, 0xcb, 0x3b, 0xc4, 0x4e, 0xc0, 0x69, 0x36, 0x62, 0x02, 0xae, 0x88, 0xfc, 0xaa, 0x42, 0x08, 0xa6, 0x45, 0x57, 0xd3, 0x9a, 0xbd, 0xe1, 0x23, 0x8d, 0x92, 0x4a, 0x11, 0x89, 0x74, 0x6b, 0x91, 0xfb, 0xfe, 0xc9, 0x01, 0xea, 0x1b, 0xf7, 0xce}

 

There are 256 records in order: 00 ~ FF corresponds to each element of the array.

0x05 cracking program

Use the preceding JAVA package and the conversion table, and combine the following code unwrap to generate the plaintext, as shown below:

Set serveroutput on; create or replace procedure unwrap (o in varchar, n in varchar, t in varchar) as vWrappedtext Varchar2 (32767); vtrimtext Varchar2 (32767 ); vChar Varchar2 (2); vRepchar Varchar2 (2); vLZinflatestr Varchar2 (32767); nLen Integer; nLoop Integer; nCnt Integer; code varchar (512); Begincode: = 'commandid 00000000f7ce '; -- sys. the content in the idltranslate table is saved to the character array vtrimtext: = ''; select count (*) into ncnt from DBA_SOURCE Where owner = o And Name = n And Type = t; if ncnt> 0 and ncnt <= 5 thenfor I in 1 .. ncnt loopif I = 1 thenselect rtrim (substr (TEXT, instr (TEXT, chr (10), 1, 20) + 1), chr (10 )) -- save the BASE64 text body into vLZinflatestr from DBA_SOURCE Where owner = o And Name = n And Type = t and line = I; elseselect text into vLZinflatestr from DBA_SOURCE Where owner = o And Name = n And Type = t and line = I; end if; vtrimtext: = vtrimtext | vLZinflatestr; end loop; end if; vtrimtext: = replace (vtrimtext, chr (10), ''); nLen: = Length (vtrimtext)/256; vWrappedtext: =''; for I in 0 .. nLen loop -- if I <nLen thenvWrappedtext: = vWrappedtext | substring (utl_raw.cast_to_raw (substrb (vtrimtext, 256 * I + 1,256); -- else -- vWrappedtext: = vWrappedtext | encode (utl_raw.cast_to_raw (substrb (vtrimtext, 64 * I + 1); -- end if; -- DBMS_OUTPUT.PUT_LINE (vWrappedtext); End Loop; -- vWrappedtext: = substr (vWrappedtext, 41); nLen: = Length (vWrappedtext)/2-1; vLZinflatestr: = ''; For nLoop In 20 .. nLen Loop -- vChar: = Substrb (vWrappedtext, nLoop * 2 + 41st); vLZinflatestr: = vLZinflatestr | substr (code, to_number (vChar, 'xx ') * 2 +); -- match from string variables -- DBMS_OUTPUT.PUT_LINE (vLZinflatestr); End Loop; -- DBMS_OUTPUT.PUT_LINE (vLZinflatestr); DBMS_OUTPUT.PUT_LINE (mycompress. inflate (vLZinflatestr); End;/exec unwrap ('sys ', 'AA', 'Procedure ');

 

Here we will convert the IDLTRANSLATE content storage variable code to avoid creating the IDLTRANSLATE table during the unwrap process.
Run the above Code to check the output content. So far, no password is available for all Oracle PACKAGE programs. Of course, other object program stored procedures and functions can also be obtained in plaintext.
Run a program after unwrap, save the code as code. SQL, and decode it to obtain the plaintext.


A python script provided by foreigners is also collected. The Code is as follows:

#!/usr/bin/python ## This script unwraps Oracle wrapped plb packages, does not support 9g# Contact: niels at teusink net / blog.teusink.net## License: Public domain#import reimport base64import zlibimport sys# simple substitution tablecharmap = [0x3d, 0x65, 0x85, 0xb3, 0x18, 0xdb, 0xe2, 0x87, 0xf1, 0x52, 0xab, 0x63, 0x4b, 0xb5, 0xa0, 0x5f, 0x7d, 0x68, 0x7b, 0x9b, 0x24, 0xc2, 0x28, 0x67, 0x8a, 0xde, 0xa4, 0x26, 0x1e, 0x03, 0xeb, 0x17, 0x6f, 0x34, 0x3e, 0x7a, 0x3f, 0xd2, 0xa9, 0x6a, 0x0f, 0xe9, 0x35, 0x56, 0x1f, 0xb1, 0x4d, 0x10, 0x78, 0xd9, 0x75, 0xf6, 0xbc, 0x41, 0x04, 0x81, 0x61, 0x06, 0xf9, 0xad, 0xd6, 0xd5, 0x29, 0x7e, 0x86, 0x9e, 0x79, 0xe5, 0x05, 0xba, 0x84, 0xcc, 0x6e, 0x27, 0x8e, 0xb0, 0x5d, 0xa8, 0xf3, 0x9f, 0xd0, 0xa2, 0x71, 0xb8, 0x58, 0xdd, 0x2c, 0x38, 0x99, 0x4c, 0x48, 0x07, 0x55, 0xe4, 0x53, 0x8c, 0x46, 0xb6, 0x2d, 0xa5, 0xaf, 0x32, 0x22, 0x40, 0xdc, 0x50, 0xc3, 0xa1, 0x25, 0x8b, 0x9c, 0x16, 0x60, 0x5c, 0xcf, 0xfd, 0x0c, 0x98, 0x1c, 0xd4, 0x37, 0x6d, 0x3c, 0x3a, 0x30, 0xe8, 0x6c, 0x31, 0x47, 0xf5, 0x33, 0xda, 0x43, 0xc8, 0xe3, 0x5e, 0x19, 0x94, 0xec, 0xe6, 0xa3, 0x95, 0x14, 0xe0, 0x9d, 0x64, 0xfa, 0x59, 0x15, 0xc5, 0x2f, 0xca, 0xbb, 0x0b, 0xdf, 0xf2, 0x97, 0xbf, 0x0a, 0x76, 0xb4, 0x49, 0x44, 0x5a, 0x1d, 0xf0, 0x00, 0x96, 0x21, 0x80, 0x7f, 0x1a, 0x82, 0x39, 0x4f, 0xc1, 0xa7, 0xd7, 0x0d, 0xd1, 0xd8, 0xff, 0x13, 0x93, 0x70, 0xee, 0x5b, 0xef, 0xbe, 0x09, 0xb9, 0x77, 0x72, 0xe7, 0xb2, 0x54, 0xb7, 0x2a, 0xc7, 0x73, 0x90, 0x66, 0x20, 0x0e, 0x51, 0xed, 0xf8, 0x7c, 0x8f, 0x2e, 0xf4, 0x12, 0xc6, 0x2b, 0x83, 0xcd, 0xac, 0xcb, 0x3b, 0xc4, 0x4e, 0xc0, 0x69, 0x36, 0x62, 0x02, 0xae, 0x88, 0xfc, 0xaa, 0x42, 0x08, 0xa6, 0x45, 0x57, 0xd3, 0x9a, 0xbd, 0xe1, 0x23, 0x8d, 0x92, 0x4a, 0x11, 0x89, 0x74, 0x6b, 0x91, 0xfb, 0xfe, 0xc9, 0x01, 0xea, 0x1b, 0xf7, 0xce]#print charmap[124]def decode_base64_package(base64str):    base64dec = base64.decodestring(base64str)[20:] # we strip the first 20 chars (SHA1 hash, I don't bother checking it at the moment)    decoded = ''    for byte in range(0, len(base64dec)):        decoded += chr(charmap[ord(base64dec[byte])])    return zlib.decompress(decoded)    sys.stderr.write("=== Oracle 10g/11g PL/SQL unwrapper 0.2 - by Niels Teusink - blog.teusink.net ===\n\n" )if len(sys.argv) < 2:    sys.stderr.write("Usage: %s infile.plb [outfile]\n" % sys.argv[0])    sys.exit(1)infile = open(sys.argv[1])outfile = Noneif len(sys.argv) == 3:    outfile = open(sys.argv[2], 'w')lines = infile.readlines()for i in range(0, len(lines)):    # this is really naive parsing, but works on every package I've thrown at it    matches = re.compile(r"^[0-9a-f]+ ([0-9a-f]+)$").match(lines[i])    #print matches    if matches:        base64len = int(matches.groups()[0], 16)        base64str = ''        j = 0        while len(base64str) < base64len:            j+=1            base64str += lines[i+j]        base64str = base64str.replace("\n","")        if outfile:            outfile.write(decode_base64_package(base64str) + "\n")        else:            print decode_base64_package(base64str)

 

Execution result ,:

0x06 Summary

The wrap process of Oracle 10g PL/SQL is to compress lzstr on the source code lz first, and then compress the data into SHA-1 to obtain a 40-bit encrypted string shstr, then, the encrypted string and the compressed string are spliced to get shstr + lzstr. Then, Oracle dual-character conversion (conversion table) is performed on the concatenated string, and the converted string is base64-encoded, finally, the encrypted string of the wrap is obtained.

The unwrap process is the opposite. The key to cracking is to obtain the ORACLE dual-character conversion table, because the contents of the conversion table cannot be viewed in Oracle after 10 Gb. Here refer to the wrap Technology of oracle 9i, which uses a completely different method than 10 Gb. 9i wrap, the code is converted to DIANA (ADA's intermediate Description Language), which is a very complex process, for more information, see How to Unwrap PL/SQL in Pete Finnegan. You can download it at http://www.blackhat.com/presentations/bh-usa-06/bh-us-06-finnian.w.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.