Unwrap
Unwrap
Unwrap
Unwrapping PLSQL
Unwrapping
Oracle
PLSQL
Author: G S Chapman
Date: October 2009
Version: 1.0
Location of Document:
unwrap
Macrotone Consulting Ltd
Unwrap PLSQL
DOCUMENT HISTORY
Version Date Changed By: Remarks
DOCUMENT DISTRIBUTION
i
unwrap
Macrotone Consulting Ltd
Unwrap PLSQL
DOCUMENT REFERENCES
Document Name Originator Part Number Version Date
ii
Macrotone Consulting Ltd
Unwrap PLSQL
TABLE OF CONTENTS
1 Introduction............................................................................. 1
1.1 History ................................................................................................. 1
1.2 Wrapped code display ............................................................................ 1
2 Code......................................................................................... 5
2.1 Java package code................................................................................. 5
2.2 PLSQL wrapper for java code .................................................................. 5
2.3 Unwrap package.................................................................................... 6
2.4 Test code ........................................................................................... 10
2.5 Script to generate substation table. ....................................................... 11
TABLE OF FIGURES
Figure 1- Unwrap display ....................................................................................... 1
Figure 2 - Unwrap display 2 ................................................................................... 2
Figure 3 - Trying a line feed. .................................................................................. 3
Figure 4 - Trying a space character ......................................................................... 4
TABLES
Table 1 - Wrap database type codes........................................................................ 2
Appendices
A. Notes
iii
Macrotone Consulting Ltd
Unwrap PLSQL
PURPOSE OF DOCUMENT
A description of how to unwrap Oracle ‘wrapped’ database code.
iv
Macrotone Consulting Ltd
Unwrap PLSQL
1 Introduction
1.1 History
This document started out as an investigation into how secure the database wrapped
code is in practise as a means of securing account passwords for remote web service
connection or accessing a Microsoft Active Directory LDAP service. Since the database in
use was an Oracle 10g database it was decided to concentrate upon this and later
database and not investigate Oracle 9 or earlier. The code developed has also been
successfully tested on an Oracle 11g database.
Investigation on the web reveals that many people have tried to unwrap wrapped
PL/SQL. Most people were unsuccessful but a presentation by Pete Finnegan’s
presentation at the 2006 Black Hat conference indicated that is possible. Additionally
David Litchfield, in his book “The Oracle Hacker’s Handbook”, described a method to
unwrap code on a 10G database. This described how the code is base64 decoded, and
then, each byte is re-substituted with a second corresponding substitution table. Finally
the text is decompressed, leaving the clear text of the PL/SQL.
The key to the mechanism is the substitution table, and this document describes how
this was discovered and how a few procedures have been written to enable the
unwrapping of both code stored in the database and also operating system flat files.
In this output we see a line with a000000 and 15 lines with ‘abcd’. This display is typical
of all inspected wrapped code. The third line, in this example 367, is probably related
with the database version. Upon an 10.2.0.4 database it seems to always be a value of
367, upon an 10.2.0.1 database is seems to be 2e. With an 11g release 1 database the
values seems to be a 1.
1
Macrotone Consulting Ltd
Unwrap PLSQL
The 19th line, appears to be and indication of the type of the wrapped object .
Table 1 - Wrap database type codes
Code Database type
7 Procedure
8 Function
b Package Body
The following line contains 2 hex numbers. The first is the length from the unwrapped
text without the create + 1, and the second is the length of the base64-encoded text.
From Litchfieldss’ book we can if we base64-decode this, skip the first 20 bytes (the size of a
SHA1-hash) there are only 18 bytes left to decipher. For the base64-decoding one uses the
package utl_encode. There is also an Oracle package utl_compress, which can be used for
Lempel-Ziv compression/decompression.
Find a substitution table, apply it, and decompress the result to get unwrapped PL/SQL
code.
Compress PL/SQL code, and compare this to the base64-decoded output to FIND (a part
of) the substitution table.
Using the second option we can use different inputs to the dbms_ddl.wrap package.
2
Macrotone Consulting Ltd
Unwrap PLSQL
Unfortunately the output is one byte short, but remember the two hex numbers in the
wrapped output. The first hex number was the length of the unwrapped code + 1. So if
one adds a newline character to the input of the compression, that will give 1 more byte
for the zipped code. This reveals another small problem in that the output of the
wrapped column we has 2 lines with the value of 166. These lines should both have the
same value in the zipped column which is no so. One alternative is to add a space
character.
3
Macrotone Consulting Ltd
Unwrap PLSQL
The above display thus provides 16 entries of the possible 256, for the substitution
table. Changing the test code and retesting reveals other values but unfortunately there
are different “zipped values” for the same “wrapped values”. This indicates that the use
of a space character is incorrect. Further testing reveals that the character to be used
has to be a byte 0.
If an attempt is made to create the additional values in a PLSQL loop more errors are
discovered, this time related with the second byte. This is where the compression level
is stored, and changing it to a value of 9 enables the whole substitution table to be
generated.
4
Macrotone Consulting Ltd
Unwrap PLSQL
2 Code
2.1 Java package code
The following java source package is called by a database PLSQL package supplied
below. It uses a java supplied compression algorithm.
DROP JAVA SOURCE MY_COMPRESS;
CREATE OR REPLACE AND RESOLVE JAVA SOURCE NAMED MY_COMPRESS as import java.io.*;
import java.util.zip.*;
5
Macrotone Consulting Ltd
Unwrap PLSQL
Note that the procedure to accept a named package from the dba_source view assumes
that the package owner has the correct grants. Using all_source does not always find
the package being requested.
END UNWRAP;
/
CREATE OR REPLACE PACKAGE BODY UNWRAP
AS
/*
Make into a package with the base functions included below and a few
procedures.
DBMS_OUTPUT output is probably more than suitable since there would be a need
to add comments and modify the header in some way if only to insert the
phrase 'CREATE OR REPLACE'.
*/
not_wrapped EXCEPTION;
6
Macrotone Consulting Ltd
Unwrap PLSQL
BEGIN
dbms_output.put_line(p_text);
END Print;
*/
BEGIN
RETURN UTL_RAW.TRANSLATE ( v_inp,
'000102030405060708090A0B0C0D0E0F'
|| '101112131415161718191A1B1C1D1E1F'
|| '202122232425262728292A2B2C2D2E2F'
|| '303132333435363738393A3B3C3D3E3F'
|| '404142434445464748494A4B4C4D4E4F'
|| '505152535455565758595A5B5C5D5E5F'
|| '606162636465666768696A6B6C6D6E6F'
|| '707172737475767778797A7B7C7D7E7F'
|| '808182838485868788898A8B8C8D8E8F'
|| '909192939495969798999A9B9C9D9E9F'
|| 'A0A1A2A3A4A5A6A7A8A9AAABACADAEAF'
|| 'B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF'
|| 'C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF'
|| 'D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF'
|| 'E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF'
|| 'F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF',
'3D6585B318DBE287F152AB634BB5A05F'
|| '7D687B9B24C228678ADEA4261E03EB17'
|| '6F343E7A3FD2A96A0FE935561FB14D10'
|| '78D975F6BC4104816106F9ADD6D5297E'
|| '869E79E505BA84CC6E278EB05DA8F39F'
|| 'D0A271B858DD2C38994C480755E4538C'
|| '46B62DA5AF322240DC50C3A1258B9C16'
|| '605CCFFD0C981CD4376D3C3A30E86C31'
|| '47F533DA43C8E35E1994ECE6A39514E0'
|| '9D64FA5915C52FCABB0BDFF297BF0A76'
|| 'B449445A1DF0009621807F1A82394FC1'
|| 'A7D70DD1D8FF139370EE5BEFBE09B977'
|| '72E7B254B72AC7739066200E51EDF87C'
|| '8F2EF412C62B83CDACCB3BC44EC06936'
|| '6202AE88FCAA4208A64557D39ABDE123'
|| '8D924A1189746B91FBFEC901EA1BF7CE'
);
END;
7
Macrotone Consulting Ltd
Unwrap PLSQL
DBMS_OUTPUT.PUT_LINE ('invalid_path');
WHEN UTL_FILE.INVALID_MODE
THEN
DBMS_OUTPUT.PUT_LINE ('invalid_mode');
WHEN UTL_FILE.INVALID_FILEHANDLE
THEN
DBMS_OUTPUT.PUT_LINE ('invalid_filehandle');
WHEN UTL_FILE.INVALID_OPERATION
THEN
DBMS_OUTPUT.PUT_LINE ('invalid_operation');
WHEN UTL_FILE.READ_ERROR
THEN
DBMS_OUTPUT.PUT_LINE ('read_error');
WHEN UTL_FILE.WRITE_ERROR
THEN
DBMS_OUTPUT.PUT_LINE ('write_error');
WHEN UTL_FILE.INTERNAL_ERROR
THEN
DBMS_OUTPUT.PUT_LINE ('internal_error');
END OpenFile;
UTL_FILE.FCLOSE_ALL;
EXCEPTION
WHEN UTL_FILE.INVALID_PATH
THEN
DBMS_OUTPUT.PUT_LINE ('invalid_path');
WHEN UTL_FILE.INVALID_MODE
THEN
DBMS_OUTPUT.PUT_LINE ('invalid_mode');
WHEN UTL_FILE.INVALID_FILEHANDLE
THEN
DBMS_OUTPUT.PUT_LINE ('invalid_filehandle');
WHEN UTL_FILE.INVALID_OPERATION
THEN
DBMS_OUTPUT.PUT_LINE ('invalid_operation');
WHEN UTL_FILE.READ_ERROR
THEN
DBMS_OUTPUT.PUT_LINE ('read_error');
WHEN UTL_FILE.WRITE_ERROR
THEN
DBMS_OUTPUT.PUT_LINE ('write_error');
WHEN UTL_FILE.INTERNAL_ERROR
THEN
DBMS_OUTPUT.PUT_LINE ('internal_error');
END CloseFile;
SELECT count(line)
INTO nlines
FROM dba_source
WHERE name = p_name
AND owner = p_owner
AND type = p_type;
-- dbms_output.put_line('Lines found '||nlines);
FOR i IN 1..nlines
LOOP
SELECT text
8
Macrotone Consulting Ltd
Unwrap PLSQL
INTO v_t
FROM dba_source
WHERE name = p_name
AND owner = p_owner
AND type = p_type
AND line = i;
fhandle UTL_FILE.FILE_TYPE;
ufhandle UTL_FILE.FILE_TYPE;
dir_in VARCHAR2 (100);
fname VARCHAR2 (40);
BEGIN
v_t := trans(v_x);
v_t := trans(v_x);
9
Macrotone Consulting Ltd
Unwrap PLSQL
END UNWRAP;
/
';
begin
dbms_output.put_line ('Test 1');
unwrap.table_source ('GSC', 'OSC_ALERT_COMPL1','PROCEDURE');
dbms_output.put_line('Test 2');
unwrap.text_source(v_text);
end;
/
Output displayed:
Test 1
PROCEDURE osc_alert_compl1 ( TNSALIAS VARCHAR2 DEFAULT NULL) AS
C_STMT VARCHAR2(4000) ;
C_TIME OWA_UTIL.VC_ARR ;
C_MESSAGE OWA_UTIL.VC_ARR ;
BEGIN
OSC_HTP('TABLEOPEN','NAME');
HTP.TABLEROWOPEN;
OSC_HTP('TABLEDATA_HEAD','MESSAGE');
HTP.TABLEROWCLOSE;
C_STMT := 'SELECT TIME,text
FROM
(SELECT last_value(to_char(TIME,'||''''||'DD.MM.YY
Hh24:MI:SS'||''''||') ignore nulls)
over(ORDER BY rownum ASC ROWS unbounded preceding) AS
TIME,text
FROM
(SELECT rownum,
CASE
WHEN(text LIKE '||''''||'___ ___ __ __:__:__ 20__'||''''||') THEN
TO_DATE(text, '||''''||'DY MON DD HH24:MI:SS
YYYY'||''''||','||''''||'nls_date_language=american'||''''||')
END AS
TIME,
CASE
WHEN(text NOT LIKE '||''''||'___ ___ __ __:__:__ 20__'||''''||')
THEN
10
Macrotone Consulting Ltd
Unwrap PLSQL
text
END AS
text
FROM sys.osc_alert_text@'||TNSALIAS||')
)
WHERE text IS NOT NULL';
t varchar2(512);
cnt number;
procedure fill( p_txt in varchar2, p_from in number, p_to in number, p_extra in
varchar2 := null )
is
begin
for i in p_from .. p_to
loop
for r_fill in c_fill( p_txt || chr( i ) || p_extra )
loop
11
Macrotone Consulting Ltd
Unwrap PLSQL
if ( t2( r_fill.xr ) != -1
and t2( r_fill.xr ) != r_fill.dr
)
then
dbms_output.put_line( 'error: value maps to two different values ' || p_txt
);
dbms_output.put_line( chr( i ) || ' ' || r_fill.xr || ' ' || t2( r_fill.xr )
|| ' ' || r_fill.dr );
raise no_data_found;
end if;
t2( r_fill.xr ) := r_fill.dr;
end loop;
end loop;
end;
procedure fill2( p_txt in varchar2 )
is
begin
for i in 0 .. 99
loop
fill( p_txt, ascii( 'a' ), ascii( 'z' ), to_char( i, 'fm999' ) );
fill( p_txt, ascii( 'A' ), ascii( 'Z' ), to_char( i, 'fm999' ) );
end loop;
end;
--
begin
for i in 0 .. 255
loop
t2( i ) := -1;
end loop;
--
dbms_output.put_line( to_char( sysdate, 'hh24:mi:ss' ) );
fill2( 'PACKAGE ' );
-- fill2( 'PACKAGE BODY ' );
-- fill2( 'FUNCTION ' );
-- fill2( 'PROCEDURE ' );
-- fill2( 'TYPE BODY ' );
--
dbms_output.put_line( to_char( sysdate, 'hh24:mi:ss' ) );
cnt := 0;
for i in 0 .. 255
loop
if t2( i ) != -1
then
dbms_output.put_line(cnt||': '||t2(i)|| ' : '||to_char( t2(i), 'xxxx' ));
cnt := cnt + 1;
end if;
end loop;
dbms_output.put_line( 'cnt ' || cnt );
end;
/
12
Macrotone Consulting Ltd
Unwrap PLSQL
A. Notes
A.1 Oracle 9i wrap mechanism
The 10g wrap mechanism seems a lot weaker than the 9i and lower mechanism. The
main problem with the 9i mechanism is that the symbol table is visible, with 10g to 11g
that is not so BUT is weaker as full reversal is possible. With 9i it is simply the internal
state of the PL/SQL compiler, i.e. DIANA written out to disk as IDL. The unwrap process
for 9i is a feature of the design of DIANA which was intended for low memory older
machines where code would be stored in an intermediate format and it should be
possible to reconstruct the source code. Writing an un-wrapper for 9i and lower is a
bigger task than with Oracle 10g. With Oracle 10g there is the addition of a hidden
symbol table but a much weaker mechanism to hide the code.
A- 1