This post is also available in:
Português
During my last presentation on GUOB Tech Tour 2016 – Oracle Technology Tour LA – Brazil, I demonstrate how we could easily use the CREATE PUBLIC SYNONYM privilege to escalate and get DBA privilege.
In this article, I will give you a package that I’ve created and I use in my systems to allow users to create their own public synonyms without compromising the security.
So, let’s begin.
How does it work?
This package works allowing the user to only create synonyms to his own objects or dropping public synonyms that are pointing to one of his objects.
Give the necessary grants to the package owner
First of all, this package should be created on a user with the following minimal privileges given directly (not via ROLE) :
- SELECT on DBA_SYNONYMS
- CREATE PUBLIC SYNONYM
- DROP PUBLIC SYNONYM
1. Create the package
CREATE OR REPLACE PACKAGE MANAGE_PUBLIC_SYNONYM AS -- Created by Rodrigo Jorge - www.dbarj.com.br -- PROCEDURE CREATE_SYNONYM(SYNONYM_NAME IN VARCHAR2, OBJECT_NAME IN VARCHAR2); PROCEDURE DROP_SYNONYM(SYNONYM_NAME IN VARCHAR2); END; /
2 Create the package body
CREATE OR REPLACE PACKAGE BODY MANAGE_PUBLIC_SYNONYM AS
-- Created by Rodrigo Jorge - www.dbarj.com.br --
FUNCTION CHECK_EXISTS(SYN_NAME IN VARCHAR2) RETURN BOOLEAN IS
OUT_RESULT NUMBER;
BEGIN
SELECT 1
INTO OUT_RESULT
FROM DBA_SYNONYMS
WHERE OWNER = 'PUBLIC'
AND SYNONYM_NAME = SYN_NAME;
RETURN TRUE;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN FALSE;
END;
FUNCTION GET_PUBLIC_SYN_OWNER(SYN_NAME IN VARCHAR2) RETURN VARCHAR2 IS
OUT_RESULT VARCHAR2(30);
BEGIN
SELECT TABLE_OWNER
INTO OUT_RESULT
FROM DBA_SYNONYMS
WHERE OWNER = 'PUBLIC'
AND SYNONYM_NAME = SYN_NAME;
RETURN OUT_RESULT;
END;
PROCEDURE RAISE_ERROR(IN_CODE IN NUMBER) IS
BEGIN
CASE IN_CODE
WHEN -20001 THEN
RAISE_APPLICATION_ERROR(IN_CODE, 'Synonym already exists.');
WHEN -20002 THEN
RAISE_APPLICATION_ERROR(IN_CODE, 'Synonym does not exist.');
WHEN -20003 THEN
RAISE_APPLICATION_ERROR(IN_CODE, 'Synonym is not yours.');
ELSE
RAISE_APPLICATION_ERROR(-20999, 'Generic error.');
END CASE;
END;
PROCEDURE CREATE_SYNONYM(SYNONYM_NAME IN VARCHAR2, OBJECT_NAME IN VARCHAR2) IS
SESS_USER VARCHAR2(30);
BEGIN
IF CHECK_EXISTS(SYNONYM_NAME) = TRUE
THEN
RAISE_ERROR(-20001);
END IF;
SESS_USER := SYS_CONTEXT('USERENV', 'SESSION_USER');
EXECUTE IMMEDIATE 'CREATE PUBLIC SYNONYM ' || DBMS_ASSERT.ENQUOTE_NAME(SYNONYM_NAME, FALSE) || ' FOR ' || DBMS_ASSERT.ENQUOTE_NAME(SESS_USER, FALSE) || '.' || DBMS_ASSERT.ENQUOTE_NAME(OBJECT_NAME, FALSE);
END;
PROCEDURE DROP_SYNONYM(SYNONYM_NAME IN VARCHAR2) IS
OBJ_OWNER VARCHAR2(30);
SESS_USER VARCHAR2(30);
BEGIN
IF CHECK_EXISTS(SYNONYM_NAME) = FALSE
THEN
RAISE_ERROR(-20002);
END IF;
OBJ_OWNER := GET_PUBLIC_SYN_OWNER(SYNONYM_NAME);
SESS_USER := SYS_CONTEXT('USERENV', 'SESSION_USER');
IF OBJ_OWNER <> SESS_USER
THEN
RAISE_ERROR(-20003);
END IF;
EXECUTE IMMEDIATE 'DROP PUBLIC SYNONYM ' || DBMS_ASSERT.ENQUOTE_NAME(SYNONYM_NAME, FALSE);
END;
END;
/
3 Grant Privilege to this package to the users that need Public Synonyms
Example:
GRANT EXECUTE ON MANAGE_PUBLIC_SYNONYM TO SCOTT;
Optionally, you can also create a synonym to avoid typing the owner of the package every time:
CREATE SYNONYM SCOTT.MANAGE_PUBLIC_SYNONYM FOR MANAGE_PUBLIC_SYNONYM;
And that’s it.
How to use?
To create a public synonym
BEGIN
MANAGE_PUBLIC_SYNONYM.CREATE_SYNONYM('EMP','EMP');
END;
/
To drop a public synonym
BEGIN
MANAGE_PUBLIC_SYNONYM.DROP_SYNONYM('EMP');
END;
/
And remember: you will not be able to touch on another user’s public synonym.
Have you enjoyed? Please leave a comment or give a 👍!




