Oracle UTL_ENCODE Built-In Package
Versions 9.0.1 - 21c

Security Advisory
This package contains BASE64, Mime, Text, and UU encode and decode RAW data string functions that encode data into a standard encoded format so that the data can be transported between hosts. This package's objects can be used to encode the body of email text. The package also contains the decode counterpart functions of the encode functions. The functions follow published standards for encoding to accommodate non-Oracle utilities on the sending or receiving ends.
 
Recommended Security Rules

 NEVER
  • Let any user or schema without documented justification or escalated privileges gain access to this package by revoking EXECUTE from PUBLIC
 WITH GREAT CARE
  • Identify legitimate requirements for access to this package and grant EXECUTE explicitly to only justified schemas
  • Query the data dictionary after EXECUTE has been revoked from PUBLIC to verify the equivalence created is the equivalence approved by IT management and your CISO
 CAUTIONS
  • Some usage may be in the form of dynamic SQL so carefully verify usage requirements in source code as well as in DBA_DEPENDENCIES
 
How Oracle Works
How Oracle intended UTL_ENCODE to be utilized. The demo at right copied from Erik Wramner's blog. A few small edits made to comply with our site guidelines. CREATE OR REPLACE PACKAGE utl_base64 AUTHID DEFINER IS
  FUNCTION decode_base64(p_clob_in IN CLOB) RETURN BLOB;
  FUNCTION encode_base64(p_blob_in IN BLOB) RETURN CLOB;
END;
/

CREATE OR REPLACE PACKAGE BODY utl_base64 IS
--------------------------------------------------------
FUNCTION decode_base64(p_clob_in IN CLOB) RETURN BLOB IS;
 v_buffer_size CONSTANT BINARY_INTEGER := 48;
 v_blob                 BLOB;
 v_result               BLOB;
 v_offset               INTEGER;
 v_buffer_varchar       VARCHAR2(48);
 v_buffer_raw           RAW(48);
BEGIN
  IF p_clob_in IS NULL THEN
    RETURN NULL;
  END IF;
  dbms_lob.createtemporary(v_blob, TRUE);
  v_offset := 1;

  FOR i IN 1 .. ceil(dbms_lob.getlength(p_clob_in) / v_buffer_size) LOOP
    dbms_lob.read(p_clob_in, v_buffer_size, v_offset, v_buffer_varchar);
    v_buffer_raw := utl_raw.cast_to_raw(v_buffer_varchar);
    v_buffer_raw := utl_encode.base64_decode(v_buffer_raw);
    dbms_lob.writeappend(v_blob, utl_raw.length(v_buffer_raw), v_buffer_raw);
    v_offset := v_offset + v_buffer_size;
  END LOOP;
  v_result := v_blob;
  dbms_lob.freetemporary(v_blob);

  RETURN v_result;
END decode_base64;
--------------------------------------------------------
FUNCTION encode_base64(p_blob_in IN BLOB) RETURN CLOB IS
 v_chunk_size CONSTANT BINARY_INTEGER := (48/4)*3;
 v_clob                CLOB;
 v_result              CLOB;
 v_offset              INTEGER;
 v_buffer_varchar      VARCHAR(48);
 v_buffer_raw          RAW(48);
BEGIN
  IF p_blob_in IS NULL THEN
    RETURN NULL;
  END IF;

  dbms_lob.createtemporary(v_clob, TRUE);
  v_offset := 1;

  FOR i IN 1 .. CEIL(dbms_lob.getLength(p_blob_in) / v_chunk_size) LOOP
    dbms_lob.read(p_blob_in, v_chunk_size, v_offset, v_buffer_raw);
    v_buffer_raw := utl_encode.base64_encode(v_buffer_raw);
    v_buffer_varchar := utl_raw.cast_to_varchar2(v_buffer_raw);
    dbms_lob.writeappend(v_clob, length(v_buffer_varchar), v_buffer_varchar);
    v_offset := v_offset + v_chunk_size;
  END LOOP;
  v_result := v_clob;
  dbms_lob.freetemporary(v_clob);

  RETURN v_result;
END encode_base64;
--------------------------------------------------------
END;
/
How UTL_ENCODE can be utilized by an attacker The following demo code is from the BASE 64 exploit page here at DBSecWorx

DECLARE
 r      RAW(32767);
 sqlStr VARCHAR2(60);
BEGIN
  r := utl_raw.cast_to_raw('SELECT dummy FROM dual');
  dbms_output.put_line(r);

  r := utl_encode.base64_encode(r);
  dbms_output.put_line(r);

  execute immediate utl_raw.cast_to_varchar2(utl_encode.base64_decode(r)) INTO sqlStr;
  dbms_output.put_line(sqlStr);
END;
/
53454C4543542064756D6D792046524F4D206475616C
5530564D52554E55494752316257313549455A53543030675A48566862413D3D
X


PL/SQL procedure successfully completed.

There are further examples of using this package available: Click on the Base 64 Exploit link at page bottom.
 
UTL_ENCODE Package Information
AUTHID DEFINER
Constants
Name Data Type Value
base64 PLS_INTEGER 1
complete (header & footer) PLS_INTEGER 1
end_piece (includes footer text) PLS_INTEGER 4
header_piece (includes heaer text) PLS_INTEGER 2
middle_piece (body text only) PLS_INTEGER 3
quoted_printable PLS_INTEGER 2
Dependencies
DBMS_AW_EXP DBMS_ISCHED_REMOTE_ACCESS UTL_MAIL
DBMS_ISCHED UTL_ENC_LIB UTL_SMTP
Documented Yes
First Available 9.0.1
Security Model Owned by SYS with EXECUTE granted to PUBLIC
Source {ORACLE_HOME}/rdbms/admin/utlenc.sql
Subprograms
 
BASE64_DECODE
Reads the base 64-encoded RAW input string and decodes it to its original RAW value utl_encode.base64_decode(r IN RAW) RETURN RAW;
See base64_encode Demo Below
 
BASE64_ENCODE
Encodes the binary representation of the RAW value into base 64 elements and returns it in the form of a RAW string utl_encode.base64_encode(r IN RAW) RETURN RAW;
set serveroutput on

DECLARE
 r RAW(32767);
BEGIN
  r := utl_raw.cast_to_raw('University of Washington');
  dbms_output.put_line(r);

  r := utl_encode.base64_encode(r);
  dbms_output.put_line(r);

  r := utl_encode.base64_decode(r);
  dbms_output.put_line(r);
END;
/
556E6976657273697479206F662057617368696E67746F6E
56573570646D567963326C30655342765A69425859584E6F6157356E64473975
556E6976657273697479206F662057617368696E67746F6E


PL/SQL procedure successfully completed.
 
MIMEHEADER_DECODE
Decodes a string from mime header format utl_encode.mimeheader_decode(buf IN VARCHAR2 CHARACTER SET ANY_CS)
RETURN data VARCHAR2 CHARACTER SET buf%CHARSET;
See mimeheader_encode Demo Below
 
MIMEHEADER_ENCODE
Encodes a string into mime header format utl_encode.mimeheader_encode(
buf            IN VARCHAR2 CHARACTER SET ANY_CS,
encode_charset IN VARCHAR2    DEFAULT NULL,
encoding       IN PLS_INTEGER DEFAULT NULL);
RETURN string VARCHAR2 CHARACTER SET buf%CHARSET;
set serveroutput on

DECLARE
 t VARCHAR2(100);
BEGIN
  t := utl_encode.mimeheader_encode('MLIB');
  dbms_output.put_line(t);

  t := utl_encode.mimeheader_decode(t);
  dbms_output.put_line(t);
END;
/
=?UTF-8?Q?MLIB?=
MLIB
 
QUOTED_PRINTABLE_DECODE
Reads the varchar2 quoted printable format input string and decodes it to the corresponding RAW string utl_encode.quoted_printable_decode(r IN RAW) RETURN RAW;
See quoted_printable_encode Demo Below
 
QUOTED_PRINTABLE_ENCODE
Reads the RAW input string and encodes it to the corresponding quoted printable format string utl_encode.quoted_printable_encode(r IN RAW) RETURN RAW;
set serveroutput on

DECLARE
r RAW(32767);
BEGIN
  r := utl_raw.cast_to_raw('Begin' || chr(13) || 'End');
  dbms_output.put_line(r);

  r := utl_encode.quoted_printable_encode(r);
  dbms_output.put_line(r);

  r := utl_encode.quoted_printable_decode(r);
  dbms_output.put_line(r);
END;
/
426567696E0D456E64
426567696E3D3044456E64
426567696E0D456E64
 
TEXT_DECODE
Decodes a character set sensitive text string utl_encode.text_decode(
buf            IN VARCHAR2 CHARACTER SET ANY_CS,
encode_charset IN VARCHAR2    DEFAULT NULL,
encoding       IN PLS_INTEGER DEFAULT NULL)
RETURN string VARCHAR2 CHARACTER SET buf%CHARSET;
See text_encode Demo Below
 
TEXT_ENCODE
Encodes a character set sensitive text string utl_encode.text_encode(
buf            IN VARCHAR2 CHARACTER SET ANY_CS,
encode_charset IN VARCHAR2    DEFAULT NULL,
encoding       IN PLS_INTEGER DEFAULT NULL)
RETURN string VARCHAR2 CHARACTER SET buf%CHARSET;
set serveroutput on

DECLARE
 c VARCHAR2(100);
BEGIN
  c := utl_encode.text_encode('Here is some text', 'WE8ISO8859P1', UTL_ENCODE.BASE64);

  dbms_output.put_line(c);
END;
/
SGVyZSBpcyBzb21lIHRleHQ=

DECLARE
 c VARCHAR2(100);
BEGIN
  c := utl_encode.text_decode('SGVyZSBpcyBzb21lIHRleHQ=',
  'WE8ISO8859P1', UTL_ENCODE.BASE64);

  dbms_output.put_line(c);
END;
/
Here is some text
 
UUDECODE
Reads the RAW uuencode format input string and decodes it to the corresponding RAW string utl_encode.uudecode(r IN RAW) RETURN RAW;
See uuencode Demo Below
 
UUENCODE
Reads the RAW input string and encodes it to the corresponding uuencode format string utl_encode.uuencode(
r          IN RAW,
type       IN PLS_INTEGER DEFAULT 1,
filename   IN VARCHAR2    DEFAULT NULL,
permission IN VARCHAR2    DEFAULT NULL)
RETURN RAW;
set serveroutput on

DECLARE
 r RAW(32767);
BEGIN
  r := utl_encode.uuencode('ABCFED', 1, 'uuencode.txt', 0);
  dbms_output.put_line('Encoded: ' || r);

  r := utl_encode.uudecode(r);
  dbms_output.put_line('Decoded: ' || r);
END;
/
Encoded: 626567696E2030207575656E636F64652E7478740D0A244A5C5F4D0D0A0A656E64
Decoded: ABCFED

Related Topics
Base 64 Exploit
DBMS_CRYPTO
NoSpaces Exploit
Substitution Exploits
UTL_I18N
UTL_RAW
UTL_SMTP