Gizlenmiş JavaScript’te İmzalı Bir İstek Karmasını Tersine Çevirme ve Araç İşleme


Yakın zamanda bir hata ödül programını hackliyordum ve web sitesinin her isteği imzaladığını ve GET parametre değerleri de dahil olmak üzere URL’yi değiştirmenizi engellediğini keşfettim. Bunu nasıl yaptıklarını keşfetmek ve bunun üstesinden gelmenin bir yolunu bulmak istedim. Biraz çaba gerektiriyorsa, muhtemelen çok fazla kişi bunu test etmemiştir. Şirketin güvenliğini azaltmak istemediğim için kimliklerini korumak için bilgileri çıkaracağım.

Başlangıçta hedefi test ederken, URL ve GET parametre değerlerini değiştirirken yanıtta genel hata mesajları aldım. Sonunda, POST parametrelerini değil, yalnızca GET parametrelerini değiştirirken bu hataları gördüğümü fark ettim. Sunucuya gönderilen iki başlık vardır ve sunucu bunların eşleştiğinden emin olmak için bunları doğrular.

Başlıklar:

  • Zaman: 1703010077113
  • İşaret: 16428:088d7f8c3eaa175c94d1ab016be9a0c1132e329f:7a5:6581a7f6

Bu başlıkları güncellemeden URL’yi değiştirmeye çalışmak şu hatayla sonuçlanır:

{"error":{"code":401,"message":"Please refresh the page"}}

İsteklere baktığımızda, bu başlık değerlerinin sunucudan herhangi bir yere gönderildiğini görmüyoruz. İstemcinin bunları oluşturması gerektiğini biliyoruz, dolayısıyla bunlar muhtemelen JavaScript’te mevcuttur. Yaptığımız ilk şey, tarayıcı geliştirme araçlarını açmak ve başlıkları aramaktır.

Firefox’ta Arama altında Ctrl+Shift+F tuşlarını kullanarak o anda DOM’da yüklü olan tüm JavaScript kaynaklarını arayabiliriz. İşaret ve Zaman terimleri oldukça geneldir, dolayısıyla pek çok sonuç vardır. Ne yazık ki, tüm sonuçları inceledikten sonra hala bulamadım. Bu, bu değerlerin karıştırıldığını gösteriyor.

Tüm JavaScript kitaplıklarını araştırdıktan sonra sonunda oldukça karmaşık bir dosyayla karşılaştım:
https://[cdn]/[path]/33415.js?rev=5d210e7-2023-11-29

Çevrimiçi olarak her biri kendi tekniklerine sahip olan ve kodun nasıl gizlendiğine bağlı olarak farklı sonuçlara sahip olan pek çok JavaScript kod çözme aracı ve kütüphanesi vardır.

Örnekler:

Ne yazık ki, kod kod gizleme araçlarıyla çalıştırılsa bile, kod hala oldukça karmaşık hale geldi. Belki daha temiz çıktı alabilecek belirli bir araç vardır, ancak ben devam etmeye ve bu sorunu kendim çözmeye karar verdim. Araçların yardımcı olamayacağı durumlarda takılıp kalırsanız, bunu nasıl yapacağınızı öğrenmek önemli olabilir.

Karmaşık kodda gezinmeye çalışırken en iyi sonucu verdiğini bulduğum yöntemlerden biri, öncelikle sözde kodu olabildiğince anlamaya çalışmak ve kesme noktaları yerleştirmeye başlamaktır. Bu istekleri imzaladığını biliyoruz ve başlangıçta iki şeyi arıyoruz:

  • Kodda çekirdek JavaScript işlev dizeleri bulunmadığından tüm dize değerlerinin karartılmasına neden oldular. Gizlenmiş kodun neresinde saklandıklarını ve onları nasıl çağırdıklarını bulmak, kodda neler olup bittiğini anlamak için önemli bir ilk adım olacaktır.
  • Dize değerlerinin olduğunu biliyoruz İmza Ve Zaman aynı zamanda karışıktır, dolayısıyla muhtemelen aynı konumdadır.
  • İmzalamak için istekten gelen bilgiye ihtiyacı var; kodun herhangi bir yerinde URL dizesini de kullanması gerektiğini biliyoruz.

Peki tarayıcıya nasıl bir kesme noktası yerleştiririz ve ne işe yarar? YouTube’da bunu derinlemesine açıklayan güzel videolar var, ancak basitçe söylemek gerekirse:

  1. Tarayıcınızın Geliştirici Araçlarını açmak için F12 tuşuna (veya eşdeğer tuş takımına) basın
  2. Firefox’ta “Hata Ayıklayıcı”ya gidin. Chrome’da “Kaynaklar” sekmesidir.
  3. Buradan itibaren işler tarayıcıya özel olacaktır, ancak çoğunlukla aynı şekilde çalışırlar
  4. Firefox için Kaynaklar sekmesine gidin ve JavaScript kaynak dosyalarından birini seçin.
  5. “‘yi tıklayın{} Küçültülmüşse kaynağı güzelleştirmek için düğme.
  6. Her kod satırının sol tarafındaki sayıların üzerine geldiğinizde üzerlerine tıklayabileceğinizi göreceksiniz.
  7. Bu sayılardan birine tıklamak bir kesme noktası belirleyecektir.
  8. Tarayıcı bu kodu her çalıştırdığında, tüm yürütmeyi duraklatır.

Bu, mühendislerin kodlarında meydana gelen sorunları gerçek zamanlı olarak anlamalarına yardımcı olur. Bilgisayar korsanlarının kodu tersine mühendislik yaparken nasıl çalıştığını daha iyi anlamaları faydalıdır.

Gizlenmiş JavaScript’i güzelleştirdikten, birkaç kesme noktası yerleştirdikten ve istekleri tetikledikten sonra, sonunda kodun sonundaki bu değişkenlerin isteği imzalayanla ilişkili olduğunu görüyoruz:

Kesme noktası, kod yürütmeye ulaştığında tetiklenir ve geliştirme araçları, kesme noktası sırasında DOM’da saklanan değişken değerlerini görüntüler. Artık kodun bu kısmının aradığımız şeyle ilgili olduğunu biliyoruz.

Artık kodumuzun genel alanını kesme noktaları aracılığıyla keşfettiğimize göre, bu bölümün nasıl çalıştığını bulmamız kaldı:


        t = n[o( - 570, 'nY58')](u(), W, n[o( - 555, 'U[zo')], '');
        function o(W, n) {
          return d(W - - 774, n)
        }
        const c = n[o( - 467, 'lMAW')](u(), window, n[o( - 557, 'EJC^')], null),
        i = {};
        i[o( - 444, 'BF4)')] = + new Date;
        const f = n[o( - 493, 'jUU[')](u(), e.default, n[o( - 565, '2tt4')], null),
        k = n[o( - 579, 'FRHE')](
          r(),
          [
            n[o( - 501, 'We4x')],
            i[o( - 444, 'BF4)')],
            t,
            f ||
            0
          ][o( - 519, 'r83A')]('\n')
        );

Bu, karmaşık kodunuzu okunması daha kolay bir şeye dönüştürmeye başladığınızda yararlı bir numaradır. Bu kod parçası göz önüne alındığında, ilk satıra bir kesme noktası ayarlayabiliriz (değişken k):


        k = n[o( - 579, 'FRHE')](
          r(),
          [
            n[o( - 501, 'We4x')],
            i[o( - 444, 'BF4)')],
            t,
            f ||
            0
          ][o( - 519, 'r83A')]('\n')
        );

Tarayıcı bu satırda durakladığında değerleri kopyalayıp konsola gönderebiliriz:

Bu, gizlemenin dize değerlerini veya işlev adlarını gizlemeye çalıştığı durumlarda yararlı olabilir.

Bir kesme noktası belirleyerek bu karışık değerlerin ne olduğunu anlamaya başlayabiliriz. şunu görebiliriz değişken istek hakkında bilgi içeren bir nesnedir. Bu daha sonra geçerli URL yolunu atamak için kullanılır. sabit t.

Devam edersek şunu görebiliriz: sabit c isteklerimizin Kullanıcı Aracısını saklıyor:

şunu görebiliriz değişkenim muhtemelen istekteki Zaman başlığı için kullanılan, bir unix zaman damgası olan “zamanı” saklayan bir nesnedir.

görebiliriz f değişkeni 379578839 değerini saklıyor:

k değişken bir karma değerdir, ancak nasıl oluşturulduğunu bilmiyoruz. Karmayı oluşturan kod:


        k = n[o( - 579, 'FRHE')](
          r(),
          [
            n[o( - 501, 'We4x')],
            i[o( - 444, 'BF4)')],
            t,
            f ||
            0
          ][o( - 519, 'r83A')]('\n')
        );

Bir kesme noktası ayarlama kdaha sonra “Step In” (Firefox’ta F11) kullanmaya başlayabiliriz. Bu bizi her seferinde bir adım kod çalıştırmaya götürecektir. Bu, gizlenmiş kodun ne yaptığını anlamamıza yardımcı olur, ancak sonunda ne karma yaptıklarını göreceğiz. Yaklaşık 25 kez adım attıktan sonra, sonunda aşağıdaki görüntüde, şüpheli dizelerimizden bazılarını içeren bir dizeyle createOutputMethod adlı bir işlevi çağırdığını görüyoruz.

Değer N şu:

"NQ4UQIjeSeFbaORiNgZEt0AVXvwYYGQP\n1703012009162\n/api2/v2/users/notifications/count\n379578839"

değişken K başka bir kütüphaneden “createOutputMethod” isimli bir fonksiyondur:

https://[cdn]/[path]/chunk-vendors-b49fab05.js

Bu JavaScript dosyasını incelediğimizde fonksiyonun js-sha1 adlı harici bir kütüphanenin parçası olduğunu görebiliriz:


 /*
 * [js-sha1]{@link https://github.com/emn178/js-sha1}
 *
 * @version 0.6.0
 * @author Chen, Yi-Cyuan [[email protected]]
 * @copyright Chen, Yi-Cyuan 2014-2017
 * @license MIT
 */

Artık karmanın aşağıdaki olduğunu biliyoruz:

js-sha1([string]\ntimestamp\npath\n[number])

Ne olabileceğine dair daha iyi bir fikir edinmek için bu değerleri isteğe göre kontrol edebiliriz:

Hash’in sonundaki sayının (379578839) isteğin User_Id’si olduğunu görebiliriz.

Şu anda sahip olduğumuz bilgiler göz önüne alındığında, karmaşık kodu anlaşılması daha kolay bir şekilde yeniden yazabiliriz:


	const c = W["url"];

	// const d = window.navigator.userAgent;
	const d = userAgent;

  	  f["time"] = +new Date;
	
	const i = W["headers"]["user-id"];

	const k = sha1(
		[
			n["frWIg"], // pE5CRmAhC8fvaWy6u58tKDTEKCZyTKLA
			f["time"], // time
			c, // url
			i || // user-id
			0
		]["join"]('\n')
	);

Henüz işimiz bitmedi, kodun nasıl çalıştığını artık biraz anlıyoruz, ancak Sign başlığında hala henüz belirlemediğimiz ek değerler var. Sınıfın sonunda, iç içe geçmiş işlev çağrılarının yer aldığı devasa bir geri dönüş var. Kısa tutmak için iç içe geçmiş işlevleri kaldırdım.


  return i[o( - 442, 'WQdV')] = [
          o( - 560, 'r83A'),
          k,
          function (W) {
            function t(W, n) {
              return o(W - 583, n)
            }
            return Math[t(89, 'BF4)')](
…
}(k),
          n[o( - 483, 'Trv&')]
        ][o( - 458, '$LL1')](':'),
        i
      }
    }
  }

İşaret başlığının : ile ayrılmış değerlere sahip olduğu göz önüne alındığında, işlevlerden birinde ‘:’ ifadesinin geçtiğini görebiliriz, bunun değerleri birleştirme olduğunu varsayabiliriz. Bunu gerçekten de doğru olduğunu görmek için kesme noktamız ve konsol numaramızla kontrol edebiliriz. katılmak işlev.

Birleştirilen değerlerin kontrol edilmesi:

Sign başlık değerinin şöyle göründüğünü unutmayın:

Sign: 16428:b866803f2316ba4682c03cf401039bc1abc068c9:770:6581a7f6

Devasa işlev çağrıları kümesi, muhtemelen, o son sayıyı (örneğin 770) bulmak için hash değerini değiştiren matematik işlemleridir.

Bu noktada dikkate almamız gereken birkaç seçenek var:

  • Bunu tamamen tersine çevirmeyi bitirecek miyiz, buna ihtiyacımız var mı? Başka bir dile dönüştürmek istiyorsak muhtemelen bunu yapmak zorundayız.
  • İstediğimiz değerleri işlemek için kodun nasıl çalıştığını yeterince belirledik mi?
  • İstekleri imzalamak için kodu manuel olarak çalıştırmak istemiyoruz, bu testimizi yavaşlatacaktır. Bunun otomatik olarak çalışmasını nasıl sağlayabiliriz?

Elimizdeki seçeneklerden biri, Kaynak Geçersiz Kılma (Firefox, Chrome) gibi bir tarayıcı uzantısı veya Hata Ayıklayıcı’daki kaynaklara sağ tıklanarak erişilebilen tarayıcının yerleşik komut dosyası geçersiz kılmaları kullanmaktır.

Ancak bu süper verimli değildir, eğer Burp Suite’teki istekleri değiştirmek istiyorsak o zaman kodu Python veya Java için yeniden yazmamız gerekir. Karmaşıklaştırılmış kodu tersine çevirmeye devam etmek ve onu başka bir dilde yeniden yazmak çok daha fazla çaba gerektirecektir. Daha hızlı bir seçenek, kodu kopyalamak, istediğimiz değişiklikleri yapmak, onu bir NodeJS sunucusu olarak ayarlamak ve Burp’taki istekler sırasında bu hizmeti eklenti olarak kullanmaktır.

İşte konseptin bir diyagramı:

sunucu.js

Artık URL’yi değiştirebildiğimizi ve doğru karmaları oluşturabildiğimizi doğruladığımıza göre, bu verileri Burp’a otomatik olarak aktarmanın bir yolunu bulmamız gerekiyor. Daha önce hiç Burp eklentisi yazmamıştım, bu yüzden API uzantısına aşina değildim. Neyse ki 2023 yılında işleri hızlandıracak ChatGPT’miz var.

Şaşırtıcı bir şekilde, yaklaşık %60 işlevsel olan ve Burp genişleticide yapılan API değişiklikleri nedeniyle küçük ayarlamalar gerektiren oldukça doğru kod üretti.

Düzenlenen son eklenti kodunu burada bulabilirsiniz, reqsigner.py:

Eklentiyi kullanmak için bir Jython jar’ımız ve kurulu Python modüllerimiz için modül klasörümüz olduğundan emin olmalıyız:

Uzantı yüklendikten sonra devam edip Burp Suite’te istekleri değiştirmeye başlayabiliriz:

GET “limit” parametre değerini değiştirebildim ve artık 401 hata kodunu almıyorum.

Bu aşılması eğlenceli bir engeldi ve sonuçta bir API çağrısının GET parametresinde bir güvenlik açığı bulunmasına yol açtı. Sorun çok azdı, ancak öncelikle bunu test etmek için biraz çaba harcamanız gerekiyordu. Buradan çıkarılabilecek en önemli sonuç, kimsenin yapmak istemediği bir şeyi test etmek için her zaman çaba göstermeye istekli olmanız gerektiğidir. Aşağıdaki fikri düşünün:

  • Kimliği doğrulanmamış bir güvenlik açığı varsa, muhtemelen birileri bunu tarama sırasında bulmuştur.
  • Eğer güvenlik açığı varsa. bir hesap gerektiriyor ama pek de kolay değil, muhtemelen birisi tarayıcı olmadan bulmuş olabilir.
  • Ödemelerin ayarlanması, ek erişim sağlanması veya gizlenmiş kodun okunması gibi giriş için ek engeller gerektiriyorsa, insanların bulmak için asla çaba göstermediği keşfedilmemiş güvenlik açıklarının bulunması ihtimali yüksektir.

Ekstra yol kat etmek, güvenlik testi ve araştırması yaparken altını vuracağınız yerdir.

Her ne kadar karmaşık kodu tersine çevirmek bazen göz korkutucu olsa da, bugün mevcut olan araçlar bunu her zamankinden daha kolay hale getiriyor. Hata ayıklayıcıyı nasıl kullanacağınızı ve DOM’u nasıl okuyacağınızı öğrenerek biraz pratik yaparak karmaşık kodda gezinebilir ve onu kolaylıkla anlayabilirsiniz.

Daha fazla JavaScript tersine çevirme işlemi için “DEFCON 29 CTF Niteleyicisi: 3FACTOOORX Yazımı”na göz atın.



Source link
Write an article about Gizlenmiş JavaScript’te İmzalı Bir İstek Karmasını Tersine Çevirme ve Araç İşleme
in Turkish