Oracle listener static service hi-jacking

Franck Pachot
4 min readJan 5, 2019

--

We must be careful about the services that are registered to a listener because the user connects to them with a good idea of the database she wants to connect to, but another database or PDB can dynamically register a service with the same name, and then get the connections which were expected another destination. Of course, as a security best practice, user/password should not be the same in different databases, but what if the connection is done by a common user in multitenant? By creating a service, you can hi-jack the connections to CDB$ROOT and have them connected to your PDB.

You may think that static registration (the SID_LIST_LISTENER in listener.ora) is a solution, especially with the (STATIC_LISTENER=TRUE) introduced in 12cR2, but this defines only the target instance. The PDB is resolved dynamically.

This post is there only to raise awareness. I’ll not expose the possible exploits. But you can imagine: something that is scheduled to run as SYSDBA on CDB$ROOT can be high-jacked to be run on a PDB where you have more privileges to attempt some privilege escalation.

Here is my CDB1 database with PDB1 pluggable database. In addition to the default services, I’ve defined a static service CDB1_DGMGRL.

# lsnrctl statusLSNRCTL for Linux: Version 19.0.0.0.0 - Development on 05-JAN-2019 18:06:26Copyright (c) 1991, 2018, Oracle.  All rights reserved.Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=0.0.0.0)(PORT=1521)))
STATUS of the LISTENER
------------------------
Alias LISTENER
Version TNSLSNR for Linux: Version 19.0.0.0.0 - Development
Start Date 16-DEC-2018 17:33:58
Uptime 20 days 0 hr. 32 min. 28 sec
Trace Level off
Security ON: Local OS Authentication
SNMP OFF
Listener Parameter File /u01/app/oracle/product/19.0.0/network/admin/listener.ora
Listener Log File /u01/app/oracle/diag/tnslsnr/VM190/listener/alert/log.xml
Listening Endpoints Summary...
(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=VM190)(PORT=1521)))
Services Summary...
Service "7cd707c6f0a7108ce053be38a8c0058b" has 1 instance(s).
Instance "CDB1", status READY, has 1 handler(s) for this service...
Service "CDB1" has 1 instance(s).
Instance "CDB1", status READY, has 1 handler(s) for this service...
Service "CDB1XDB" has 1 instance(s).
Instance "CDB1", status READY, has 1 handler(s) for this service...
Service "CDB1_DGMGRL" has 1 instance(s).
Instance "CDB1", status UNKNOWN, has 1 handler(s) for this service...
Service "pdb1" has 1 instance(s).
Instance "CDB1", status READY, has 1 handler(s) for this service...
The command completed successfully

I am the PDB administrator, connecting with the local user DEMO to PDB1 and create a service here, with the same name CDB1_DGMGRL

SQL> connect demo/demo@//localhost/PDB1
Connected.
SQL> show con_nameCON_NAME
------------------------------
PDB1
SQL> exec dbms_service.create_service(service_name=>'CDB1_DGMGRL', network_name=>'CDB1_DGMGRL');PL/SQL procedure successfully completed.SQL> exec dbms_service.start_service('CDB1_DGMGRL');PL/SQL procedure successfully completed.

As I have started the service, it is registered by the listener, in addition to the static one.

Services Summary...
Service "7cd707c6f0a7108ce053be38a8c0058b" has 1 instance(s).
Instance "CDB1", status READY, has 1 handler(s) for this service...
Service "CDB1" has 1 instance(s).
Instance "CDB1", status READY, has 1 handler(s) for this service...
Service "CDB1XDB" has 1 instance(s).
Instance "CDB1", status READY, has 1 handler(s) for this service...
Service "CDB1_DGMGRL" has 2 instance(s).
Instance "CDB1", status UNKNOWN, has 1 handler(s) for this service...
Instance "CDB1", status READY, has 1 handler(s) for this service...
Service "pdb1" has 1 instance(s).
Instance "CDB1", status READY, has 1 handler(s) for this service...
The command completed successfully

The registration is not a problem, as it goes to the same instance. The problem is that the instance knows that service as belonging to PDB1. Then, when a common user connects with this service his session switches to the PDB1 container:

# sqlplus -s sys/oracle@"(DESCRIPTION=(CONNECT_DATA=(SERVICE_NAME=CDB1_DGMGRL))(ADDRESS=(PROTOCOL=tcp)(HOST=127.0.0.1)(PORT=1521)))" as sysdba <<<'show con_name'CON_NAME
------------------------------
PDB1

Do not expect (STATIC_SERVICE=TRUE) to change anything, because the static registration has no information about the PDB:

# sqlplus -s sys/oracle@"(DESCRIPTION=(CONNECT_DATA=(SERVICE_NAME=CDB1_DGMGRL)(INSTANCE_NAME=CDB1)(STATIC_SERVICE=TRUE))(ADDRESS=(PROTOCOL=TCP)(HOST=127.0.0.1)(PORT=1521)))" as sysdba <<<'show con_name'CON_NAME
------------------------------
PDB1

By the way, even when the service is stopped:

SQL> connect demo/demo@//localhost/PDB1
Connected.
SQL> exec dbms_service.stop_service('CDB1_DGMGRL');
PL/SQL procedure successfully completed.

and then the listener knows only the static declaration:

Services Summary...
Service "7cd707c6f0a7108ce053be38a8c0058b" has 1 instance(s).
Instance "CDB1", status READY, has 1 handler(s) for this service...
Service "CDB1" has 1 instance(s).
Instance "CDB1", status READY, has 1 handler(s) for this service...
Service "CDB1XDB" has 1 instance(s).
Instance "CDB1", status READY, has 1 handler(s) for this service...
Service "CDB1_DGMGRL" has 1 instance(s).
Instance "CDB1", status UNKNOWN, has 1 handler(s) for this service...
Service "pdb1" has 1 instance(s).
Instance "CDB1", status READY, has 1 handler(s) for this service...
The command completed successfully

the instance will still redirect to PDB1 even if the session is not connected with a service:

SQL> connect sys/oracle@"(DESCRIPTION=(CONNECT_DATA=(SERVICE_NAME=CDB1_DGMGRL))(ADDRESS=(PROTOCOL=tcp)(HOST=127.0.0.1)(PORT=1521)))" as sysdba
Connected.

SQL> select sys_context('userenv','service_name') from dual;
SYS_CONTEXT('USERENV','SERVICE_NAME')
--------------------------------------------------------------------
SYS$USERS
SQL> show con_nameCON_NAME
------------------------------
PDB1

Only when the service is deleted, the connections will stay in CDB$ROOT:

SQL> exec dbms_service.delete_service('CDB1_DGMGRL');PL/SQL procedure successfully completed.# sqlplus -s sys/oracle@"(DESCRIPTION=(CONNECT_DATA=(SERVICE_NAME=CDB1_DGMGRL)(INSTANCE_NAME=CDB1)(STATIC_SERVICE=TRUE))(ADDRESS=(PROTOCOL=TCP)(HOST=127.0.0.1)(PORT=1521)))" as sysdba <<<'show con_name'CON_NAME
------------------------------
CDB$ROOT

Solution(s)

Basics: be careful with powerful privileges, such as INHERIT PRIVILEGES, which can allow a PDB DBA to overwrite a common user procedure with authid current_user.

Safe attitude: when you expect your connection to run in CDB$ROOT be sure to do it explicitely with ALTER SESSION SET CONTAINER=CDB$ROOT

Isolation: dedicate a listener for your system administration. Another listener will be the target of REMOTE_LISTENER for dynamic registration.

Least privileges: when you need only to access to CDB$ROOT connect with a user that has no CONNECT privilege on the PDBs.

DBaaS: if you are using multitenant for database on-demand provisioning where the user can choose the name of the PDB then be sure that the name cannot be the same as a static service you use. Naming conventions may help there.

--

--

Franck Pachot

Developer Advocate for YugabyteDB (Open-Source, PostgreSQL-compatible Distributed SQL Database. Oracle Certified Master and AWS Data Hero.