Friday, April 9, 2010

Unwrapping Oracle PL/SQL with unwrap.py

The Oracle wrap utility can be used to obfuscate PL/SQL code, to ensure it can't be easily read. Pete Finnigan described (pdf) the wrapping process for Oracle 9g, but for 10g and 11g it still remains a bit of a mystery. I decided to release my Python unwrapping utility (supports 10g and 11g).

The unwrapping steps for 10g are nicely described in the Oracle Hacker's Handbook, but the actual substitution table needed to decode the package is omitted. Nobody (as far as I know) has published it. A lot of people seem to know how to do it though, there is even an online unwrapper available (and I'm sure everyone seriously involved in Oracle security knows how to do it). A Russian-made closed source tool is also available, but tends to upset virus scanners.

So to save everyone a couple of hours of figuring it out, here it is: unwrap.py

It's easy to use (I've used the wrapped procedure from this article as an example):

$ ./unwrap.py wrapped.txt

=== Oracle 10g/11g PL/SQL unwrapper - by Niels Teusink - blog.teusink.net ===

PROCEDURE WRAP_IT (SEED_IN NUMBER)
IS
  V_RAND INTEGER;
BEGIN
  DBMS_RANDOM.INITIALIZE (SEED_IN);
  FOR I IN 1..5 LOOP
   V_RAND := MOD(ABS(DBMS_RANDOM.RANDOM),45);
   DBMS_OUTPUT.PUT_LINE(I||': '||V_RAND);
  END LOOP;
END;
$


Update: one excellent resource I forgot to mention is this blog post by Anton Scheffer from Amis. I did not use his code to create my script but he describes how to get the substitution table as well. I also noticed Oracle security expert Pete Finnigan has mentioned my script on his blog (his older blogpost Unwrapping PL/SQL is also a good source of information).

8 comments:

Martin Nash said...

This is great. Thank you very much for sharing.

Martin

heavenmaster said...

Hi,

This code work fine with an example but I am not able to unwrap the package:

=== Oracle 10g/11g PL/SQL unwrapper - by Niels Teusink - blog.teusink.net ===

Traceback (most recent call last):
File "unwrap.py", line 47, in
outfile.write(decode_base64_package(base64str) + "\n")
File "unwrap.py", line 17, in decode_base64_package
base64dec = base64.b64decode(base64str)[20:] # we strip the first 20 chars (SHA1 hash, I don't bother checking it at the moment)
File "#######\usr\local\python\Lib\base64.py", line 76, in b64decode
raise TypeError(msg)
TypeError: Incorrect padding

Niels Teusink said...

Hi heavenmaster,

The script seems to be having trouble with the base64 encoding. But it's hard to say why without having the package.

If you want me to take a look at it, you can mail me your package at niels at teusink dot net.

Niels

brunner.florian said...

Thank you for providing some easy available code. I am still looking for some ways to make it harder for adversaries to analyze my plsql code, although it may never work.

Niels Teusink said...

I have updated the code. Now it also works on Python 2.3. The change is I use the decodestring instead of the b64decode function). They do the same, but the b64decode function was introduced in Python 2.4.

Use the new version if you get this error:

AttributeError: 'module' object has no attribute 'b64decode'

Lam said...

I had the same problem with unwrapping a package
..
TypeError: Incorrect padding.

Can I email you a package for your analysis. It was probably wrapped before Oracle 10.

Thank you.

Niels Teusink said...

Sure, you can always email me (niels at teusink dot net). So far everyone who got that error was trying to unwrap pre-10g packages. I probably should improve the error handling to detect that.

Petra said...

Hi!

Does anybody know about a complete tool (free) that unwraps PLSQL packages for Oracle 9i.
I have read the article by Finnigan but I'd rather not have to write the code myself :-)

Petra