Reverse engineering DB Link password decryption in PL/SQL

This is my third blog post about DB Link encryption/decryption.In the first one i demonstrated how we can find the database link password in clear text using GDB and Intel pin tools.In the second one i have given more information about how it was encrypted/decrypted (AES in CBC encryption mode).It’s now time to reverse engineer it !

NOTE : All the test are done in oracle 12.1.0.2.6/OEL6/UEK4

Update 08/03/2017 : The script also work in oracle 12.2.0.1

As explained in my previous post to decrypt the password we need three things.

  • Initialization vector
  • Ciphertext
  • Encryption Key

After some investigation it appeared that this parameters are not depending only on “PASSWORDX” of “sys.link$” but also in two other parameters :

  • NO_USERID_VERIFIER_SALT in table sys.props$ (Database dependent).

SELECT VALUE$
FROM SYS.PROPS$
WHERE name = 'NO_USERID_VERIFIER_SALT';

  • Variable “ztcshpl_v6” fixed across different databases(note:same value in 11.2.0.4 was named ztcshpl_v6.0)

readelf -s oracle |  grep ztcshpl_v6
112105: 0000000010b9ee40 16384 OBJECT  LOCAL  DEFAULT   29 ztcshpl_v6

Here is some info about how this different parameters combine :

  • Initialization vector : Using the second byte of passwordx to lookup in ztcshpl_v6
  • Ciphertext : Using ztcshpl_v6 and passwordx
  • Encryption Key : Calculated as a XOR between two keys :
    • Key 1 : Using ztcshpl_v6 and passwordx
    • Key 2 : Hash sha256 of NO_USERID_VERIFIER_SALT

Installation : (This script use Connor McDonald Package “bitops2” for bit operation like “XOR” bitops2.bitxor )

Update 08/03/2017 : the script bitops2.sql is not needed any more just install db_link_password_decrypt.sql

Time for a DEMO :

The procedure “db_link_password_decrypt” take two parameters :

  • NO_USERID_VERIFIER_SALT of your database
  • passwordx from sys.link$

Let’s create two database link and test it !


CREATE DATABASE LINK HATEM_TEST1
CONNECT TO HATEM
IDENTIFIED BY "you_can_not_hide_from_me"
USING 'testdb';

CREATE DATABASE LINK HATEM_TEST2
CONNECT TO HATEM
IDENTIFIED BY "oups%you$found_me"
USING 'testdb';

Password decryption :

First Database Link :


exec db_link_password_decrypt('17B21F905CE739E2759E6FB99745650E','076C8B8463098676
E408141A154CF4C20AAF19A52CF7FEF3CD8A99830A82F9D8F129665C2F395DF4F30EBE1
5E48D78096E9BD8AC1012D24525982CE3C828BE298EA798D6FBD8A18379DEE803692B
773B86570AB2DA90EA121124D6D0F228818D18812B42C77AE6D0F4BF257627643EEC34F
B9A6C2EF6185D7302EF30673A2F4C');

capture-01

Second Database Link :

exec db_link_password_decrypt('17B21F905CE739E2759E6FB99745650E','072D8692F86F55A14
96141309CBEED0DFF2F03D90B3EBF1E3AE7518DAF66B768DC171779ECB7A8546C19AD83A1CF0878667D
22EC9550AD8785E8A3A48016883C8CA085E87A6121462825FEF94866EC8869E04F0E5D73FAB39A005A
09EA98945B43C06F48B2DD8E1D6943AA693C36E945B005464EF13C257F6D085530C5D038AC');

capture-02

UPDATE  25/05/2018 : The script can now be run from sqlplus and the noise from the recovered value has been eliminated. Thanks Adric Norris for you contribution.

Capture test5

That’s it 😀

40 thoughts on “Reverse engineering DB Link password decryption in PL/SQL

    • i have oracle version :

      Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 – Production
      PL/SQL Release 11.2.0.3.0 – Production
      CORE 11.2.0.3.0 Production
      TNS for Linux: Version 11.2.0.3.0 – Production
      NLSRTL Version 11.2.0.3.0 – Production

      I Got error when I run db_link_password_decrypt.sql
      “PROCEDURE HRPROD.DB_LINK_PASSWORD_DECRYPT
      On line: 37
      PLS-00302: component ‘HASH_SH256’ must be declared”

      how to solve this ?

      thanks in advance

  1. for someone not very deep into “playing with bytes”
    – how did you come from readelf output to your very long number in plsql ?

    For me in 12.1.0.4 seems not to work and only the in doubt input is ztcshpl_v6″.

  2. Hi basile , I used gdb and the command x to examine the memory address content and as stated i only tested in specific oracle versions/platform so it may need some adjustment for other versions.The idea here is to show what can be done 🙂

  3. Just Amazing! 🙂

    Tested it on a 12.1.0.2 database on windows and it worked!!

    This script cannot be run using SQLPLUS which has a limit of 2000 characters per line.

    I used Oracle SQLCL which doesn’t have that limitation.

  4. i have oracle version :

    Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 – Production
    PL/SQL Release 11.2.0.3.0 – Production
    CORE 11.2.0.3.0 Production
    TNS for Linux: Version 11.2.0.3.0 – Production
    NLSRTL Version 11.2.0.3.0 – Production

    I Got error when I run db_link_password_decrypt.sql
    “PROCEDURE HRPROD.DB_LINK_PASSWORD_DECRYPT
    On line: 37
    PLS-00302: component ‘HASH_SH256’ must be declared”

    how to solve this ?

    thanks in advance

  5. @hatem-mahmoud

    It turns out that the binary value of the first decrypted byte indicates the plaintext password length, which makes it possible to eliminate the noise from the recovered value. I’ve submitted a PR which adds this tweak.

    • Minor clarification… the length component indicates the number of *bytes* used by the plaintext password, rather than characters. So if the password is set to “€”, for example, the length will indicate 3 (since it’s a 3-byte UTF-8 character) rather than 1.

      This doesn’t make any functional difference to the PR which you merged, as it already handles the scenario correctly, but does mean that my commit message was slightly misleading.

    • The only workaround I’m aware of is to import the dblink (via datapump) into a 12.x database, and then decrypt it there.

  6. Hi,
    I have problem :

    ORA-06502: PL/SQL: numeric or value error
    ORA-06512: at “SYS.UTL_RAW”, line 380
    ORA-06512: at “SYS.DB_LINK_PASSWORD_DECRYPT”, line 92
    ORA-06512: at line 1
    can you help me ?

    Martin

  7. all above error need to solve like
    ORA-06502: PL/SQL: numeric or value error
    ORA-06512: at “SYS.UTL_RAW”, line 380
    ORA-06512: at “SYS.DB_LINK_PASSWORD_DECRYPT”, line 92
    ORA-06512: at line 1

    is no DBMS_CRYPTO.HASH_SH256 not available ?

    nothing is working

  8. Hi ,
    I’m getting eror:
    Error at line 1
    ORA-06502: PL/SQL: numeric or value error
    ORA-06512: at “SYS.UTL_RAW”, line 380
    ORA-06512: at “SYS.DB_LINK_PASSWORD_DECRYPT”, line 99
    ORA-06512: at line 1

    select * from v$version;
    Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 – 64bit Production
    PL/SQL Release 12.2.0.1.0 – Production
    CORE 12.2.0.1.0 Production
    TNS for Solaris: Version 12.2.0.1.0 – Production
    NLSRTL Version 12.2.0.1.0 – Production

    • If second byte of passwordx less the 7, you will get error:
      ORA-06502: PL/SQL: numeric or value error
      ORA-06512: at “SYS.UTL_RAW”, line 380

      • Sorry i haven’t gotten the time to check it further : Can you please try replacing this line
        ———————————
        output_string := UTL_I18N.RAW_TO_CHAR ( utl_raw.substr( decrypted_raw, 2,
        to_number( rawtohex( utl_raw.substr( decrypted_raw, 1, 1 ) ), ‘XX’ )
        )
        );
        ———————————
        with
        ——————————–

        output_string := UTL_I18N.RAW_TO_CHAR (decrypted_raw, ‘AL32UTF8’);
        ——————————–

        and retry

  9. Thanks for reply…
    I got following error with replacing output_string :

    Error at line 1
    ORA-01890: NLS error detected
    ORA-06512: at “SYS.UTL_I18N”, line 72
    ORA-06512: at “SYS.UTL_I18N”, line 353
    ORA-06512: at “SYS.DB_LINK_PASSWORD_DECRYPT”, line 100
    ORA-06512: at line 1

    • Try removing the src_charset : output_string := UTL_I18N.RAW_TO_CHAR (decrypted_raw) ; see also (Doc ID 2456883.1) (i don’t have access to oracle support so haven’t read it)

  10. Changing the line to: output_string := UTL_I18N.RAW_TO_CHAR (decrypted_raw);

    Result on sqlplus empty screen.

    With sqlclient:

    coranew:/home/oracle >cat dec.sql | sql / as sysdba

    SQLcl: Release 12.2.0.1.0 RC on Mon May 27 11:16:29 2019

    Copyright (c) 1982, 2019, Oracle. All rights reserved.

    Connected to:
    Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 – 64bit Production

    Procedure DB_LINK_PASSWORD_DECRYPT compiled

    PL/SQL procedure successfully completed.

    129
    Disconnected from Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 – 64bit Production
    coranew:/home/oracle >

    The initial problem comes from UTL_RAW I guess. I think you have to cast it.

  11. Worked like a charm for me, Oracle 12.1.0.2.0.
    Thanks, you saved my day!

    Got a ORA-31603: object “SYS_HUB” of type DB_LINK not found in schema “PUBLIC” on 19.3.0.0.0. Just testing.

  12. Hi,

    Database version —

    SQL> select * from v$version;

    BANNER CON_ID
    ——————————————————————————– ———-
    Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 – 64bit Production 0
    PL/SQL Release 12.2.0.1.0 – Production 0
    CORE 12.2.0.1.0 Production 0
    TNS for 64-bit Windows: Version 12.2.0.1.0 – Production 0
    NLSRTL Version 12.2.0.1.0 – Production 0

    I am receving below error when i tried to decrypt after creating the procedure as it was..

    output_string := UTL_I18N.RAW_TO_CHAR ( utl_raw.substr( decrypted_raw, 2,to_number( rawtohex( utl_raw.substr( decrypted_raw, 1, 1 ) ), ‘XX’ )));

    *
    ERROR at line 1:
    ORA-06502: PL/SQL: numeric or value error
    ORA-06512: at “SYS.UTL_RAW”, line 380
    ORA-06512: at “SYS.DB_LINK_PASSWORD_DECRYPT”, line 94
    ORA-06512: at line 1

    Per your comments and DOC ID – UTL_I18N.RAW_TO_CHAR Is Failing With ORA-01890 In 12.2 When src_charset Value Is Different From DB Character Set (Doc ID 2456883.1), I tried below options but still i don’t see the password in plain text. Please assist.

    output_string := UTL_I18N.RAW_TO_CHAR (decrypted_raw, ‘AL32UTF8’);

    Decrypted string: ┐┐9eA┐┐┐┐┐┐R┐┐+0┐\┐^y

    output_string := UTL_I18N.RAW_TO_CHAR (decrypted_raw, ‘WE8MSWIN1252’);
    Decrypted string: ┘e╟ê9eA╥╛τén∩Tó╟▌╨j╬┼Rƒü+0║\╙ÿ^y

    output_string := UTL_I18N.RAW_TO_CHAR (decrypted_raw);
    Decrypted string: ┘e╟ê9eA╥╛τén∩Tó╟▌╨j╬┼Rƒü+0║\╙ÿ^y

    Thanks.

  13. Hello guys, need some help, trying to execute, the procedure was created without errors, when i execute it returns no output and no errors.
    version 12.2.0.1
    Thanks

  14. Just for your information, the first byte of passwordx gives the version of the algorithm that generated its value : 0x05 for pre-11.2.0.2 version, 0x06 for 11.2.0.2 to 11.2.0.4 versions, and 0x07 for 12.1 to 19.8 versions.
    Your code, many thanks for it, is for the 0x07 algorithm (and works all 12-19 versions) and can’t decrypt 0x06 ciphered values. It then returns the previously reported error (same one in all these versions) :
    ORA-06502: PL/SQL: numeric or value error
    ORA-06512: at “SYS.UTL_RAW”, line 380

    I don’t know the differences between the 0x06 and 0x07 algorithm. A clue may be that, as some saw, HASH_SHA256 did not exist in these 11.2 versions but this is not the only point (changing to SH1 or MD5 is not sufficient to get the result). Same thing when you just change for another older encryption algorithm (AES128 or AES192 for instance).

    Thanks

  15. Hi!,
    I’ve tried it with Oracle 19.9 versión and it seems it doesn’t work.

    set serveroutput on;
    exec db_link_password_decrypt(’61FE9A498A8FB6995B77028E02D6C276′,’06661EA2BB2519883DF747A386F1045E46E8F723BB5ABBC22550A7E8F179EFDCA688F89205C5FAC18F56A8CFF797810C06B9B353CAF4655324B3A77088E288E0ACD63FE0965DF0A185BE62FD584BCDE23268B4B82678469846AFA9E06F5092A4535BFFF8D06681E0132A30CC97AF3906DA1A49CC52A8CBF25767462EAD5270F’);
    SQL> ———-Decrypting DB Link password————–
    Initialization_vector :
    01000400010000010000000501010101
    —————————————————
    Ciphertext :
    AC3F5DA1BEFD68B82646AFA9E0535BFFD066E0132ACC97AF06DA1ACC52574652
    —————————————————
    Encryption key part 1 :
    A2BBF74786F10446E8F72350E879DC88C5FAC1CFF7978106B9B353CAA770E288
    Encryption key part 2 :
    9E551FEB0674CE5E799F17327B3D8D24B0771E289DE525E6F86A52635D05438E
    —————————————————
    Encryption key (Part 1 XOR Part 2) :
    3CEEE8AC8085CA1891683462934451AC758DDFE76A72A4E041D901A9FA75A106
    —————————————————
    Decrypted string: u!?J(oc?
    K` “Nca?I? G =?e ?E
    Decrypted string: FBA1B24A28F2634F7F850B4B602012224E63E4069CCFB00047A03DF7EAA08745

    PL/SQL procedure successfully completed.

    Decrypted string isn’t the correct password.

    Workaround about output_string is applied:

    output_string := UTL_I18N.RAW_TO_CHAR (decrypted_raw) ;

    Thanks!

    • I didn’t install 19.9 yet but it seems they fixed the “ORA-06502: PL/SQL: numeric or value error”.

      Regards

      • First line of my reply hasn’t been posted here it is:

        “Read my previous reply about first byte of passwordx and 0x06 algorithm.”

        Regards
        Michel

  16. Hi Michel ,

    Thanks for yout reply, If the workaorund with UTL_I18N.RAW_TO_CHAR is not aplied the error “ORA-06502: PL/SQL: numeric or value error” is raised in 19.9.

    This database was upgraded from 11.2.0.4 to 19.9.0.0 but the db_link was created after upgrade process in 19.9.

    Anyway it seems that the algorithm is 0x06 and not 0x07.
    select PASSWORDX from sys.link$ where NAME=’DBL’;

    PASSWORDX
    ——————————————————————————————————————————————-
    06661EA2BB2519883DF747A386F1045E46E8F723BB5ABBC22550A7E8F179EFDCA688F89205C5FAC18F56A8CFF797810C06B9B353CAF4655324B3A77088E288E0ACD63FE0965
    D06681E0132A30CC97AF3906DA1A49CC52A8CBF25767462EAD5270F

    PROPERTY_NAME PROPERTY_VALUE
    ———————– —————————–
    NO_USERID_VERIFIER_SALT 61FE9A498A8FB6995B77028E02D6C276

Leave a reply to arnaud presles Cancel reply