Payload XSS non latin

Tadi saya melihat ada yang sharing payload XSS yang hanya menggunakan karakter dalam Hangul (alfabet bahasa Korea), tanpa huruf latin. Biasanya payload butuh huruf latin karena berbagai fungsi (termasuk fungsi “alert”) butuh karakter latin.

Payload utamanya seperti ini:

([,하,,,,훌]=[]+{},[한,글,페,이,,로,드,ㅋ,,,ㅎ]=[!!하]+!하+하.ㅁ)
[훌+=하+ㅎ+ㅋ+한+글+페+훌+한+하+글][훌](로+드+이+글+한+'(45)’)()

Saya tidak mengerti bahasa Korea, tapi kita bisa menggunakan search and replace untuk menggantikan tiap bagian dengan huruf latin seperti ini:

([,A,,,,B]=[]+{},[C,D,E,F,,G,H,I,,,J]=[!!A]+!A+A.K) [B+=A+J+I+C+D+E+B+C+A+D][B](G+H+F+D+C+'(45)' )()

Hasilnya: payload di atas tetap akan bisa jalan dengan output yang sama. Tapi bagaimana caranya, kok bisa? pertama kita pecahkan dulu menjadi beberapa bagian seperti ini:

(X) [Y][Y](Z)()

Bagian X adalah:

([,A,,,,B]=[]+{},[C,D,E,F,,G,H,I,,,J]=[!!A]+!A+A.K)

Bagian Y adalah seperti ini (di sini nilai B di kiri sama dengan di kanan)

[B+=A+J+I+C+D+E+B+C+A+D][B]

dan bagian Z adalah

G+H+F+D+C+'(45)'

Mari kita mulai bagian X, ini terdiri dari dua bagian, seperti ini (R=S, T=U)

Subbagian pertama adalah mengassign: [,A,,,,B] dengan []+{}. Di sisi kanan ekspresi [] + {} akan menghasilkan string "[object Object]". Di JavaScript kita bisa mengisi beberapa variabel sekaligus dengan cara: [A,B,C]=[1,2,3], (A=1, B=2, C=3) atau jika ada elemen di kiri dikosongkan, maka nilainya akan diabaikan. Ini istilahnya adalah Destructuring assignment.

Jadi subbagian itu akan mengisi A dengan huruf kedua para string yaitu huruf ‘o’ (huruf pertama adalah ‘[‘) dan B diisi dengan string ‘c’. Jadi bagian R=S bisa disederhanakan jadi [A,B]="oc".

Subbagian kedua di sisi kanannya berisi: !!A (akan menjadi string “true”), !A (akan menjadi string “false”), dan A.K yang akan mengakses property K dari object A. Karena properti ini tidak ada, hasilnya adalah “undefined”. Jadi subbagian kedua adalah mengisi variabel

[C,D,E,F,,G,H,I,,,J]="truefalseundefined""

Atau singkatnya akan mengisi variabel berikut:

 C = t
D = r
E = u
F = e
G = a
H = l
I = s

Jadi bisa kita sederhanakan:

[C,D,E,F,G,H,I] = "trueals"

Kalau digabung keduanya

([A,B]="oc", [C,D,E,F,G,H,I] = "trueals")

atau bisa juga disederhanakan lagi, intinya adalah mengisi variabel seperti ini

([A,B,C,D,E,F,G,H,I] = "octrueals")

Bagian Y seperti ini: dengan nilai B=’c’, A=’o’

[B+=A+J+I+C+D+E+B+C+A+D][B]

Jadi jika digabung menjadi:

["constructor"] ["constructor"]

Bagian terakhir Z, seperti ini: dengan nilai G=’a’, H=’l’, F=’e’, D=’r’ dan C=’t’

G+H+F+D+C+'(45)'

Jadi Z isinya adalah alert(45). Jika digabung penyederhanaan X,Y, Z di atas:

([A,B,C,D,E,F,G,H,I] = "octrueals") ["constructor"] ["constructor"] ("alert(45)")()

karena variabel A,B,C dst sudah disubstitusikan, jadi tidak terpakai lagi:

("octrueals") ["constructor"] ["constructor"]  
("alert(45)")()

String paling kiri sebenarnya tidak penting, karena objek string apapun boleh

("yohanes") ["constructor"] ["constructor"]  
("alert(45)")()

Kenapa itu bisa menampilkan alert?

Bagian pertama "string" ["constructor"] sama saja dengan "string".constructor, yang hasilnya adalah sebuah fungsi konstruktor untuk String. Sebuah fungsi juga merupakan instance sebuah kelas yaitu Function. Function juga memiliki construtor (yang juga merupakan sebuah fungsi yang bisa dipanggil).

Jadi "string".construtor.construtor adalah konstruktor untuk Function. Di Javascript kita bisa membuat fungsi dengan memakai konstruktor Function, seperti ini

f = Function.constructor("alert(1)")

lalu kita bisa memanggil fungsi f(). Atau bisa juga langsung:

Function.constructor("alert(1)")()

Jadi pada dasarnya payload XSS di atas adalah:

"string" ["constructor"] ["constructor"] ("alert(45)")()

Atau:

String.constructor["constructor"]("alert(45)")()

Atau

Function.constructor("alert(45)")()

Jadi sebenarnya payloadnya sederhana, karakter Hangul pun bisa digantikan karakter bahasa apapun. Membuat payload XSS dengan karakter Jawa atau emoji juga bisa.

Jika teks di atas tidak terbaca karena masalah font, seharusnya seperti ini tampilannya dalam bentuk HTML lengkap:

Security dari level bit (Bagian 2: Encoding Teks)

Saat ini masih banyak user dan juga programmer yang masih bingung dengan masalah “encoding” teks (misalnya ASCII, ISO8859-1, UTF-8, UTF-16, UTF32, dsb). Ini merupakan hal dasar yang penting, baik untuk keperluan sehari-hari, maupun dalam bidang security. Ada beberapa attack yang berhubungan dengan encoding teks ini.

Encoding teks dan security

Sekilas topik encoding sepertinya hal yang membosankan dari sudut pandang security, tapi ada ada banyak masalah security yang berhubungan dengan encoding teks. Beberapa contohnya:

  • phishing memanfaatkan Unicode characther yang mirip (character ambiguity)
  • phishing menggunakan karakter khusus untuk marker RTL (right to left) dan LTR (left to right)
  • Encoding alternatif untuk bypass filter
  • Overlong UTF-8 encoding attack
  • Membuat shell code yang bisa lolos encoding tertentu
  • Buffer overflow karena kesalahan penanganan encoding

Berbagai hal di atas sulit dijelaskan jika tidak memiliki pemahaman yang baik mengenai encoding, jadi di tulisan kali ini saya akan membahas mengenai text encoding.

Hadiah ke Hong Kong yang saya menangkan tahun lalu pada dasarnya adalah masalah encoding shellcode supaya lolos dari filter UTF-8. Lengkapnya bisa dibaca di blog saya yang lain.

Sekilas mengenai berbagai encoding

Di bagian ini saya hanya akan bercerita dulu mengenai sejarah encoding. Setalah itu di bagian berikutnya saya akan menuliskan lebih detail mengenai berbagai encoding yang disinggung di bagian ini. Memahami sejarah akan menjawab pertanyaan penting: kenapa sih kok ribet banget harus ada banyak encoding? kalau dari dulu cuma ada satu kan nggak ribet.

Dulu teknologi komputer berkembang pesat dimulai dari Amerika, dan ini memberi pengaruh besar terhadap encoding teks. Ketika membaca berbagai buku atau artikel komputer biasanya dijelaskan bahwa ada tabel ASCII yang memetakan huruf ke angka (atau sebaliknya). ASCII ini merupakan singkatan dari American Standard Code for Information Interchange, dan sesuai namanya, ini asalnya dari Amerika.

Sejarah ASCII ini cukup panjang, tapi singkatnya ini adalah standard yang memetakan angka ke simbol (huruf, angka, spasi, tanda baca) serta beberapa karakter kontrol (seperti tanda end of file, new line, dsb). Standard ini hanya memetakan 128 simbol (hanya butuh 7 bit, atau disebut juga sebagai 7-bit character set), karena banyak komputer awal yang memakai 8 bit, maka masih ada 128 simbol yang bisa ditambahkan (istilahnya 8-bit character set).

Berbagai negara menggunakan 128 karakter pertama dari ASCII dan sisanya dipetakan ke karakter masing-masing negara/wilayah. Jadi biasanya kode 65 berarti huruf A di negara manapun, tapi kode 150 bisa menjadi karakter yang berbeda di negara lain. Jadi tiap negara memilki “code page”, atau “character set” yang berbeda. Ada standar ISO-8859 (ISO-8859-1 sampai ISO-8859-16) yang menyatakan “character set” untuk tiap wilayah/negara.

Dengan cara ini kita masih bisa dengan cukup mudah mencampurkan teks dua bahasa antara bahasa Inggris dengan satu bahasa lain (misalnya Thai), tapi jika harus mencampurkan lebih dari itu makan akan jadi sangat sulit. Untuk mengatasi ini maka dibuatlah standard Unicode.

Dalam Standard Unicode, hanya ada satu tabel besar untuk semua huruf di dunia ini, setiap simbol dipetakan ke sebuah nomor code point. Tentunya tabel ini sangat besar, jadi 1 karakter tidak bisa lagi dipetakan ke 8 bit (1 byte). Salah satu cara menyimpan karakter unicode adalah dalam bentuk integer 32 bit (4 byte). Cara ini boros memori (1 huruf butuh 4 byte) tapi mudah diproses.

Dalam kehidupan sehari-hari, jarang sekali kita memakai semua huruf di dunia ini dalam satu dokumen, jadi ada cara untuk menghemat memori: kita hanya memakai 16 bit saja. Karena 16 bit hanya bisa dipetakan ke 65536 karakter maka hanya sebagian unicode saja yang dipakai. Unicode dibagi menjadi banyak “plane” (ini sekedar range code point saja), yang pertama adalah Basic Multilingual Plane (BMP) yang berisi hampir semua karakter di bahasa modern saat iin. Dengan 16 bit, kita bisa memakai BMP saja, dan ada cara khusus untuk memasukkan karakter dari plane lain dengan menggunakan surrogate.

Tapi 16 bit pun kadang masih terlalu boros memori. Berbagai sistem komputer masih sangat berpusat pada American/English, ini meliputi: berbagai perintah command line, berbagai bahasa pemrograman, dan berbagai standar lain. Jadi seringnya kita hanya perlu menyimpan karakter ASCII, dan kadang baru butuh karakter lain. Dengan pemikiran ini maka ada yang namanya encoding UTF-8 (Unicode Transformation Format-8 bit). Dengan encoding ini, karakter yang ada di tabel ASCII hanya perlu 1 byte, karakter lain bisa memakai 2, 3, atau 4 byte. Ini istilahnya adalah variable length encoding. Sebagai catatan ada encoding lain (UTF-1 yang tidak lagi dipakai dan UTF-7 yang juga bukan standar).

Byte order marker

Dalam hampir semua komputer modern, data dibagi dalam byte dan ketika kita berurusan dengan bilangan yang lebih dari 1 byte, kita punya pilihan urutan: little endian dan big endian. Contohnya angka 299 desimal adalah 0x12b dalam heksadesimal. Di memori, ini bisa dituliskan 0x12 0x0b (little endian), atau 0x0b 0x12 (big endian).

Untuk encoding UTF16 dan UTF32, ada pilihan big endian (BE) atau little endian (LE) ketika menyimpan file. Jika data big endian dibaca menjadi little endian, maka hasilnya akan salah, misalnya angka 299 desimal tadi bisa terbaca jadi 2834 desimal. Pada teks Unicode, kita bisa menambahkan BOM (byte order marker) di awal sebuah file untuk menunjukkan file ini disimpan dalam big endian atau little endian.

UTF-32


Seperti telah disinggung di atas:

  • Ini merupakan encoding paling sederhana, hanya array of 32 bit integer
  • ini merupakan encoding paling boros memori
  • untuk mengakses karakter ke n, kita tinggal mengakses elemen integer ke n
UTF32-LE dengan Byte order marker

Kita bisa mencoba membuat file dengan BabelPad (teks editor gratis yang mendukung Unicode dengan baik) dan menyimpannya dalam UTF-32 lalu membukanya dengan editor teks. Ada beberapa variasi yang bisa dicoba:

  • little endian atau big endian
  • dengan atau tanpa byte order marker. Untuk Little endian akan ada FF FE 00 00 di awal dokumen dan untuk big endian 00 00 FE FF
UTF32-LE dengan BOM

Perhatikan urutan byte-byte untuk big endian dan little endian. Untuk teks latin, terlihat sekali boros memori karena banyak byte 00 (dalam huruf latin tiap 1 huruf hanya memakai 1 byte). Sekarang saya contohkan isi teks yang berisi bahasa lain (Thai). Terlihat bahwa karakter Thai memerlukan 2 byte, dan karakter emoji senyum bahagia memakai 3 byte.

UTF-16

Encoding UTF-16 atau UCS-2 (Universal Character Set, 2 Byte) menggunakan 2 byte (16 bit) sebagai unit penyimpanannya. Karena sebagian besar huruf di dunia ini masuk ke basic multilingual plane (BMP), maka sering kali 16 bit sudah cukup (contoh tulisan/bahasa yang masuk ke BMP: Inggris, Arab, Thai, Kanji, Bali, Sunda, Batak). Hanya di kasus tertentu kita perlu menyimpan dengan cara khusus (misalnya emoji yang baru). Di dalam Unicode surrogates.

UTF-16-LE dengan BOM

Kita bisa menyimpan teks sebelumnya dengan encoding UTF-16 dan melihat harsilnya. Terlihat bahwa untuk karakter latin dan Thai, hanya dibutuhkan 2 byte untuk satu karakter, sedangkan untuk karakter emoji dibutuhkan 4 karakter. Empat karakter ini terdiri dari high surrogate (H) dan low surrogate (L) yang perlu dipasangkan untuk merepresentasikan karakter di luar BMP. Formulanya:

0x10000 + (H – 0xD800) x 0x400 + (L – 0xDC00)

Contoh: karakter emoji di contoh tersebut adalah Face with Tears of Joy. Emoji ini memiliki Code Point 128514 atau 0x1F602 dan dengan surrogate dapat direpresentasikan sebagai: 0xD83D 0xDE02. Ini bisa dicek dengan memasukkan angka pertama dan kedua ke formula di atas:

0x10000 + (0xD83D – 0xD800) *0x400 + ( 0xDE02 – 0xDC00)

Sebaliknya kita bisa mendapatkan nilai H dan L untuk sebuah code point C dengan:

L = 0xDC00 + ((C – 0x10000) mod 0x400) H = 0xD800

UTF-16-BE dengan BOM

Di sini mulai ada masalah security: apa jadinya jika ada high surrogate yang tidak memiliki pairing low surrogate? Beberapa sistem mungkin akan mengabaikan bagian itu saja lalu meneruskan memproses datanya, sedangkan sistem lain mungkin akan menolak data yang masuk atau di kasus lain justu meneruskan data apa adanya.

Dalam encoding 16 bit, di bahasa low level seperti C biasanya untuk mengakses karakter ke N kita bisa langsung menggunakan X[n], tapi jika ada karakter surrogate ini tidak benar. Program yang berusaha mengakses dengan cara ini bisa memiliki bug. Cara yang benar biasanya dengan menggunakan iterator, menggunakan accessor khusus (charAt) atau membuat representasi 32 bit di memori agar lebih mudah diakses.

UTF-8

Saat ini UTF-8 merupakan encoding yang palign banyak digunakan, karena sangat hemat penyimpanan (untuk berbagai protokol Internet) dan kompatibel dengan ASCII (untuk 128 huruf pertama). Encoding ini sedikit lebih rumit dari yang lain: untuk code point 0 sampai 0x7f dibutuhkan hanya 1 byte, 0x80 sampai 0x7ff butuh 2 byte, 0x800 sampai 0xffff butuh 3 byte, dan sisanya butuh 4 byte.

Sebagai catatan: UTF-8 tidak memiliki endiannes, tapi kadang file teks di beri byte order mark untuk sekedar menandai bahwa file tersebut menggunakan encoding UTF-8. Adanya BOM kadang membingungkan aplikasi tertentu, jadi kadang ini perlu dihilangkan.

UTF-8 dengan BOM
Tabel diambil dari Wikipedia

Kali ini saya tidak akan membahas detail mengenai UTF-8 karena artikel ini sudah terlalu panjang, dan topik UTF-8 sendiri bisa cukup panjang. Ada beberapa artikel security yang berhubungan dengan UTF-8 ini, misalnya mengenai overly long encoding dan mengenai shellcode yang merupakan teks UTF-8 valid.

ISO 8859 dan encoding lain

Seperti telah dijelaskan sebelumnya bahwa 8 bit tidak cukup untuk mengencode semuanya, maka beberapa konversi bisa dilakukan dan beberapa tidak bisa dilakukan.

  • Teks dalam encoding ISO-8859-X apapun bisa dijadikan Unicode (dengan encoding apa saja)
  • Teks dalam encoding ISO-8859-X hanya bisa dipetakan ke ISO-8859-Y jika semua karakter di teks itu kebetulan ada di target
  • Teks Unicode yang hanya mengandung satu bahasa (atau Inggris dan satu bahasa lain) biasanya bisa diencode ke salah satu ISO-8859-X

Sekarang ini UTF-8 sudah sangat populer dan biasanya encoding lama ini hanya digunakan untuk berinteraksi dengan hardware ataupun software lama.

Penutup

Semoga tulisan ini bisa memberi penjelasan yang bisa dipahami mengenai masalah encoding teks. Saya merasa ilmu encoding teks ini sangat berguna dalam masalah security dan non security, semoga hal tersebut juga berlaku untuk Anda.

Security dari level bit (Bagian 1: Encoding Base N)

Di masa awal kuliah ilmu komputer, mahasiswa diajari berbagai macam hal dasar mengenai komputer. Hal dasar pertama adalah bahwa data bisa dikonversi menjadi bilangan (data di sini berupa teks, foto, video, suara, dsb). Lalu setiap bilangan bisa dikonversi dalam representasi biner supaya bisa diproses oleh komputer digital. Beberapa pelajaran awal arsitektur komputer akan membahas mengenai gerbang logika, lalu bagaimana gerbang logika ini bisa dipakai untuk menjumlahkan. Setelah kita bisa menjumlahkan maka kita juga bisa mengurangi (di sini akan diajarkan mengenai komplemen 2). Setelah bisa menjumlahkan, kita akan bisa mengalikan.

Biasanya mahasiswa masih mengerti konsep-konsep tersebut ketika diajarkan, walau mungkin belum bisa menyambungkan ilmunya dengan komputer yang ditemui sehari-hari. Di masa-masa akhir kuliah, berbagai topik praktis diajarkan. Di sini mahasiswa akan bisa membuat web atau aplikasi praktis. Tapi biasanya ada gap pengetahuan dari hal yang dasar di tahun pertama dengan hal praktis di tahun terakhir.

Hex editor bukan benda ajaib

Contoh yang sederhana adalah masalah manipulasi bit atau pemahaman bahwa berbagai manipulasi data bisa dipahami dari level biner. Kebanyakan orang akan bingung berhadapan dengan hex editor untuk melihat data sebuah file, dan bahkan menganggap kalau hex editor (saja) cukup untuk menghack apa saja.

Supaya praktis, di posting ini saya hanya akan membahas manipulasi bit untuk encoding base N. Di posting lain saya akan membahas encoding teks di level biner (terutama teks Unicode).

Banyak orang yang akan bingung jika diminta mengimplementasikan base64 tanpa memakai library, meskipun sering kali melihat data dalam bentuk base64 dan mengerti pemakaiannya di berbagai tempat (misalnya di aplikasi web). Dalam kebanyakan bahasa pemrograman, programmer memang tinggal pake dan tidak penting untuk tahu detailnya.

Lalu kenapa perlu tahu implementasi base64 (atau base N secara umum) di bidang security? Jika bidangnya adalah analisis malware, banyak malware memakai tabel base64 yang custom. Untuk satu topik khusus ini (base64) sudah ada banyak pembahasan, tool, dan triknya sehingga gampang dicari. Tapi jika tidak paham prinsipnya, jika ada kasus yang sedikit berbeda maka Anda akan kebingungan.

Base64 saya ambil sebagai contoh untuk satu hal yang berhubungan dengan representasi/manipulasi bit tapi masih cukup sederhana. Banyak hal security yang berhubungan dengan representasi bit, contoh praktis sederhana adalah format private key di sebuah SSL certificate. Contoh yang lebih kompleks adalah row hammer attack untuk melakukan bit-flipping pada RAM atau SPA (simple power analysis) untuk mendapatkan key RSA jika implementasinya tidak aman.

Masih kembali ke contoh praktis pemakaian base N:

  • base85: dipakai dalam format PDF
  • base64: untuk berbagai data di web (misalnya data URI di web)
  • base58: dipakai untuk bitcoin address
  • base36: untuk safe url encoding
  • base32: dipakai untuk geohash dan di beberapa game Nintendo lama
  • base16: representasi heksadesimal (hex), dipakai di mana-mana
  • base8: representasi oktal dipakai di mode/permission file di OS UNIX based

Sebenarnya semua di atas hanyalah masalah “basis bilangan”. Dalam biner kita memakai dua simbol saja: 0 dan 1. Dalam oktal kita memakai simbol dari 0 sampai 7. Dalam desimal kita memakai sepuluh simbol dari 0 sampai 9. Dalam heksadesimal, kita menyetujui untuk memakai 0 sampai 9, ditambah dengan a sampai f. Tapi untuk basis bilangan selain itu, ada banyak varian huruf yang bisa kita gunakan.

Untuk contoh berikutnya, saya akan menggunakan data ini yang akan di encode: “Hello”. Untuk memudahkan, saya akan menggunakan encoding ASCII. Di tulisan berikutnya saya akan bahas mengenai berbagai encoding teks yang lain.

Angka di bawah masing-masing huruf adalah nomor huruf tersebut dalam tabel ASCII. Di bawahnya saya tampilkan juga representasi biner dari angka tersebut. Konversi basis 2 ke basis lain yang bisa direpresentasikan dalam 2 pangkat n (8, 16, 32, 64) bisa dilakukan sangat mudah. Representasi oktal jarang digunakan untuk mengencode data, jadi saya akan langsung ke heksadesimal dan base 2 pangkat n berikutnya (base32, base64).

Heksadesimal (base 16)

Dalam heksadesimal (2 pangkat 4), tiap 4 bit kita translasikan menjadi 1 simbol.

Atau singkatnya, kata hello bisa diencode dalam heksadesimal menjadi “48656c6c6f”. Secara praktis ini bisa dilakukan dengan berbagai cara. Misalnya dengan shell command ini:

echo -n Hello| xxd -p

Dan untuk mengembalikannya

echo -n 48656c6c6f|xxd -r -p

Konversi dari biner ke heksadesimal bisa dilakukan dengan tabel semacam ini. Tapi supaya singkat saya bisa mengatakan bahwa simbol untuk base16 adalah: “0123456789abcdef”. Menggunakan string seperti ini akan lebih mudah ketika kita memakai encoding base N yang lebih besar.

Base32

Berikutnya ke base32: untuk ini kita bisa memakai simbol “ABCDEFGHIJKLMNOPQRSTUVWXYZ234567”. Kenapa dipilih karakter-karakter itu, kenapa bukan “ABCDEFGHIJKLMNOPQRSTUVWXYZ012345” (atau angka dulu dan sisanya huruf). Ini dipilih karena beberapa alasan:

  • Supaya tidak tertukar antara 0/O, 1/I
  • Dalam kasus tertentu jika 0..9 ada di depan, nanti disangka heksadesimal

Tentunya jika kita membuat aplikasi sendiri yang tidak perlu interoperasi dengan aplikasi lain, kita bisa membuat tabel sendiri. Tabel yang standar ini diambil dari http://www.rfc-editor.org/rfc/rfc4648.txt

Karena 32 merupakan 2 pangkat 5, kita bisa membagi per 5 bit. Gambar di bawah ini adalah serangkaian bit di gambar pertama, tapi dikelompokkan jadi 5 bit per group. Jadi di kotak pertama tercantum ‘01001’ atau 9 dalam desimal. Karakter ke 9 (dihitung dari 0) dari string “ABCDEFGHIJKLMNOPQRSTUVWXYZ234567” adalah huruf J. Demikian seterusnya.

Kita bisa mengecek hal di atas dengan menggunakan modul base64 (meski namanya base64, modul ini mendukung base 16, 32, 64, dan 85).

import base64 print 
base64.b32encode("Hello") #hasilnya: JBSWY3DP print base64.b32decode("JBSWY3DP") #hasilnya: Hello

Base64

Contoh base64 juga sangat serupa karena 64 bit juga merupakan 2 pangkat n, dengan n = 6. Di contoh base32 kebetulan jumlah bitnya kelipatan 5, nah bagaimana jika jumlah bit tidak kelipatan n? kita biarkan bagian terakhir kurang dari n bit. Berikut ini contohnya dalam base64:

Sebenarnya string di atas sudah cukup sebagai representasi base64, tapi dalam konvensi biasanya ditambahkan padding karakter ‘' jika input bukan kelipatan 3. Contohnya karakter 'A' jika diencode akan jadi 'QQ=‘, dan ‘AA’ jadi ‘QUE=’, tapi AAA karena kelipatan 3 jadi ‘QUFB’.

Kita bisa memakai program base64 untuk mengencode dan decode base64:

echo -n Hello| base64

Dan untuk mendecodenya (gunakan kapital -D jika Anda memakai OS X atau BSD):

echo -n SGVsbG8= | base64 -d

Atau dalam Python:

import base64 
print base64.b64encode("Hello") #hasilnya: SGVsbG8=
print base64.b64decode("JBSWY3DP") #hasilnya: Hello

Sebagai catatan: ada banyak varian base64, tergantung dari karakter yang digunakan (lengkapnya bisa dilihat di Wikipedia: https://en.wikipedia.org/wiki/Base64)

Base58

Seperti sudah dibahas sebelumnya: jika basisnya dalam bentuk 2 pangkat n, maka ini mudah dilakukan, cukup dengan memecah per n bit. Dalam implementasinya ini biasanya dilakukan dengan menggunakan operator SHIFT dan AND. Untuk basis lain, kita bisa menggunakan pembagian berulang. Karena pembagian berulang ini kurang efisien, base yang lain biasanya dipakai untuk menyimpan data yang tidak besar (contohnya: bitcoin address memakai base58, integer dalam PDF memakai base85).

Saya akan memberikan contoh encoding base58 untuk data di atas. Pertama kita gabungkan semua digit biner, lalu konversi bilangan biner menjadi desimal. Konversi ke desimal ini agar sekedar mudah dibaca manusia saja. Angka biner 0100100001100101011011000110110001101111 dalam desimal adalah 310939249775. Kita lakukan pembagian berulang dengan 58 dan catat sisanya. Sisanya ini dipakai untuk menjadi indeks bagi tabel: 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz

Hasilnya kita balik dari bawah ke atas yaitu: 9Ajdvzr. Kita bisa mengecek ini dengan modul base58 di python (perlu diinstall terpisah dengan pip install base58).

import base58 
print base58.b58encode("Hello")
print base58.b58decode("9Ajdvzr")

Penutup

Ini merupakan satu bagian saja dari berbagai tulisan dasar mengenai ilmu komputer dan security. Semoga saya bisa terus sabar menuliskan keseluruhan topiknya.

Adakah satu buku yang bisa mengajarkan topik security ini dari level bit? setahu saya tidak ada. Buku Silence on the wire memberikan overview yang bagus mengenai banyak masalah security dari level bit (bahkan membahas singkat dari mulai algabar boolean), tapi topik-topik lain perlu dipelajari di berbagai teks dasar mengenai ilmu komputer.

Ilmu komputer dan security

Seringkali ketika ingin menuliskan topik security, saya bingung mulai dari mana karena banyak sekali topik security yang butuh dasar ilmu komputer yang baik. Tanpa satu dasar yang baik, penjelasan topik security bisa jauh ke mana-mana. Selain itu saya juga sering dapat pertanyaan yang aneh-aneh.

Contoh pertanyaan konyol yang sering saya dapatkan adalah: saya diberi string dalam base64 dan ditanya apa artinya. Atau bagaimana mendecode string heksa yang adalah sebuah hash. Ini sama saja dengan bertanya: 763748 itu angka apa? bisa berupa apa saja, mungkin nomor Induk mahasiswa, mungkin sisa saldo rekening Anda, 6 digit terakhir nomoer telepon gebetan Anda, PIN iPad kakek Anda, dsb.

Kalau bisa melihat isi gembok, lebih gampang membukanya tanpa kunci

Di sisi lain ada pertanyaan yang benar, tapi sulit dijawab dengan singkat, misalnya mengenai SSL pinning. Saya tadinya ingin menuliskan tentang SSL pinning dan bagaimana caranya membypass hal tersebut. Supaya pembahasannya tuntas, saya perlu membahas banyak hal.

Hal pertama adalah cara kerja SSL, yang melibatkan penggunaan public key cryptography. Di sini perlu diketahui mengenai kunci publik, kunci privat. Kemudian perlu dijelaskan mengapa SSL pinning ini diperlukan. Lalu perlu dijelaskan mengenai certificate (yang bisa ada dalam berbagai format). Perlu dijelaskan juga bagaimana mendapatkan sertifikat SSL ini atau bagaimana membuat sendiri (untuk self signed certificate). Dari sertifikat itu, kita perlu mengekstrak informasi hash public key untuk melakukan pinning.

Itu baru sampai topik dasarnya, belum masuk ke bagian: bagaimana SSL pinning diimplementasikan. Untuk Android bisa dengan kode Java (dengan berbagai library), bisa dengan native code (misalnya dengan libcurl + openssl), bisa dengan fitur Android OS 7 ike atas (Network Security Configuration). Untuk iOS pembahasannya juga akan banyak.

Setelah itu baru bisa masuk ke topik bagaimana bypass SSL pinning. Inipun bisa dengan banyak teknik, bisa dengan patching APK/IPA, bisa dengan Frida, bisa dengan berbagai tool yang sudah ada. Biasanya 99% orang yang bertanya ke saya cuma membaca: pakai tool ini atau pakai skrip frida “universal” ini, dan hasilnya nggak jalan, dan mereka bingung harus meneruskan dari mana.

Untuk memudahkan pembahasan berbagai topik security, saya akan mulai menulis berbagai topik kecil yang terpisah. Sebagian topik ini mungkin akan terlalu dasar bagi banyak orang, tapi daripada saya harus menjelaskan ulang puluhan kali, akan lebhi baik jika saya tuliskan saja.

Rencananya topik-topik yang akan saya bahas yang praktis saja, walau kenyataannya dalam hidup ini kita perlu tahu sangat banyak hal untuk dapat mengevaluasi security sebuah sistem. Contohnya baru-baru ini saya membaca mengenai pentingnya ilmu geometri dalam menemukan bug di library grafik.

Maaf, saya tidak bisa membantu masalah driver Go-jek/Grab

Sejak tulisan saya mengenai bug Go-jek tahun 2016, sudah ada banyak sekali driver yang meminta bantuan saya untuk: unsuspend status driver atau membantu hack aplikasi Go-jek. Saya masih bisa sedikit mengerti kalau ada yang bertanya soal Go-jek, tapi banyak juga driver Grab yang bertanya hal serupa. Semua jawaban saya sama: saya tidak bisa membantu masalah Anda. Silakan diselesaikan dengan pihak Go-jek/Grab.

Biasanya jawaban tersebut dianggap kurang memuaskan, jadi perlu dijawab panjang. Saya sudah capek menjelaskan, jadi akan saya tuliskan jawaban saya di sini agar gampang dilink untuk menjawab. Biasanya pertanyaan awal disambung dengan: Kan dulu Anda bisa ngehack Go-jek, pasti bisa lagi dong?. Begini ya: dulu keamanan mereka itu lemah sekali. Kira-kira keamanannya seperti ini:

Intinya saat itu siapa saja bisa masuk dan mengambil data driver, dan juga data penumpang. Artinya dari mulai KTP, foto wajah, sampai nama Ibu kandung driver bisa diambil, demikian juga informasi penumpang dan berbagai rute yang diambil juga bisa dilihat siapa saja.

Waktu itu penting bagi saya memberitahu bug itu ke dunia, karena penumpang dan driver perlu tahu bahwa data mereka bocor. Kalau tiba-tiba ada penagih hutang kartu kredit datang ke rumah karena ada yang mengambil alih identitas (berpura-pura jadi seseorang, istilahnya: identitiy theft) dari informasi yang bocor tersebut dan berhutang kartu kredit atas nama orang tersebut, maka driver atau penumpang bisa sangat rugi

Setiap kali menemukan bug, saya beritahu bug itu ke pihak terkait, lalu mereka akan menambal bugnya. Di contoh gambar di atas: gemboknya sudah diganti dan dikunci lebih baik. Tidak berarti sistemnya 100% aman, tapi sekarang tidak sembarang orang bisa mengambil data.

Go-jek sekarang sudah lebih aman dari ini

Ada juga yang bertanya: apakah tidak mau mencoba ngehack Go-jek lagi? Jawabannya: saya tidak tertarik. Saya sudah punya banyak kerjaan pentesting yang jelas pasti dibayar, daripada mencari bug Go-jek yang mungkin ada, mungkin tidak ketemu, dan tidak dibayar. Bahkan seumur hidup saya sampai saat ini, baru memakai Gojek di bulan lalu ketika liburan di Indonesia.

Bug Go-jek itu sudah 3 tahun yang lalu dan cuma satu episode yang pernah terjadi di hidup saya, dan saya sudah move on ke hal-hal lain baik yang berhubungan dengan security ataupun tidak. Ada banyak cerita security yang saya ceritakan di sini, dan banyak juga yang tidak diceritakan. Beberapa hal seputar security setelah go-jek yang ditulis di blog saya tapi mungkin tidak dibaca kebanyakan orang misalnya: saya menemukan bug di Mastercard (dapat 8500 USD), saya mendapatkan training security gratis di Belanda, saya menang CTF sehingga bisa jalan-jalan gratis sekeluarga ke Disneyland Hong Kong, membongkar algoritma keamanan Pokemon Go Plus, dsb. Dan masih banyak lagi hal-hal yang tidak saya tuliskan.

Nasihat saya: jika ada masalah, selesaikanlah dengan pihak Go-jek/Grab. Liburan terakhir saya ke Indonesia, saya sempat “dikerjai” driver yang menyatakan statusnya sudah sampai tempat penjemputan, dan tiba-tiba jadi “dalam perjalanan”, dan sekian menit kemudian “tiba di tujuan”, padahal drivernya belum nongol. Apakah saya ngehack langsung sistemnya? atau menghubungi teman yang kerja di Grab? nggak, saya memakai jalur resmi untuk melaporkan kelakuan driver tersebut dan semua bisa beres dalam waktu singkat.

Melakukan sesuatu yang tidak sesuai aturan juga berisiko bagi orang yang membantu Anda. Misalnya Anda ketemu orang dalam yang bisa membantu unlock status driver, maka jika ketahuan, maka orang tersebut kemungkinan besar akan dipecat. Jika orang eksternal berhasil menemukan jalan masuk dan ketahuan maka bisa ditangkap polisi. Andaikan saya bisa masuk pun, saya tidak akan mengambil risiko hidup saya demi hidup Anda (apalagi saya tidak kenal Anda).

Saya tahu bahwa sebuah sistem kadang tidak sempurna, dan kadang mungkin Anda benar disuspend karena kesalahan sistem. Tapi sistem Go-jek/Grab bukanlah milik saya, jadi mohon selesaikanlah dengan pemilik sistem dengan cara yang legal.

Root check dan jailbreak check pada aplikasi mobile

Artikel ini akan membas mengenai metode untuk mengecek root (pada Android) atau jailbreak (pada iOS) dan mengapa pemeriksaan ini diperlukan. Berikut saya akan membahas bagaimana melakukan bypass pengecekan ini supaya seorang pentester bisa melakukan testing pada aplikasi mobile.

Apa itu rooting/jailbreak?

Baik rooting maupun jailbreak gunanya adalah untuk mendapatkan hak akses setara administrator pada mobile device yang kita miliki. Di Android kita bebas menginstall aplikasi apa saja, di iOS sebuah aplikasi hanya bisa diinstall dalam mode development, dengan jailbreak maka aplikasi tambahan bisa diinstall.

Jika device sudah dijailbreak/root maka sebuah aplikasi kemudian bisa diberi hak untuk melakukan apa saja, termasuk juga membaca data privat aplikasi lain. “Melakukan apa saja” bisa berarti positif, misalnya kita bisa mengganti berbagai aplikasi sistem dengan versi yang lebih baik, menghapus aplikasi bawaan sistem yang tidak terpakai, dsb.

Biasanya seseorang dengan sengaja melakukan rooting/jailbreaking pada devicenya sendiri supaya bisa melakukan hal-hal yang sebelumnya tidak bisa diijinkan oleh sistem. Contohnya: kita bisa mengganti isi file hosts untuk memblokir iklan di berbagai aplikasi, atau meningkatkan privasi dengan menggunakan aplikasi tertentu yang mencegat berbagai kebocoran informasi (misalnya dengan XPrivacyLua), atau bahkan sekedar untuk mencurangi game tertentu.

Tapi proses rooting/jailbreak ini juga bisa dilakukan oleh pihak lain, misalnya penjahat atau agen pemerintahan agar bisa menginstall aplikasi untuk mengendalikan ponsel kita, memata-matai menggunakan kamera/mikrofon, mencuri data bank, dsb. Proses jailbreak/rooting ini kadang bisa dilakukan secara remote (diinstall menggunakan eksploit tertentu tanpa sepengetahuan user), dan bisa juga melalui aplikasi yang diinstall dengan menipu user (contohnya di Android bisa berpura-pura jadi aplikasi cheat untuk game populer).

Memeriksa Jailbreak/rooting

Sebuah aplikasi yang penting (misalnya aplikasi bank) tidak ingin uang nasabahnya dicuri oleh program lain. Jadi logikanya begini:

  • Jika device dijailbreak/root, maka ada program lain yang bisa mencuri/mengubah data di aplikasi banking
  • Ada kemungkinan user tidak tahu bahwa devicenya dijailbreak/root, jadi aplikasi perlu mendeteksi dan memberitahu user

Setelah bisa mendeteksi device dijailbreak atau tidak, maka aplikasi bisa melakukan ini:

  • Meneruskan program, tapi memberi peringatan dulu ke user bahwa device dijailbreak
  • Tidak mau meneruskan, karena ada risiko bahwa user tidak mengerti apa itu root/jailbreak serta risikonya dan jika diberi pilihan dalam bentuk pertanyaan apapun maka user akan menjawab apapun asalkan programnya jalan terus

Proses pemeriksaan jailbreak/rooting

Proses pemeriksaan bisa banyak, ini bisa dilakukan dengan coding manual, atau menggunakan library yang sudah ada untuk mengecek ini (misalnya rootbeer). Apapun yang digunakan, biasanya pemeriksaannya seperti ini:

  • Cek apakah file tertentu ada atau tidak, ada file-file standar yang diciptakan oleh program root/jailbreak
  • Cek apakah ada aplikasi tertentu yang biasanya diinstall bersama dengan root/jailbreak
  • Cek apakah direktori tertentu bisa dibaca/ditulis

Untuk Android, ada juga pemeriksaan level sistem yang bisa dilakukan dengan menggunakan Safety Net.

Rooting/Jailbreak untuk pentesting

Sebagai pentester, memiliki HP dengan akses root/jailbreak sangat membantu untuk banyak hal:

  • Di iOS jailbreak dibutuhkan untuk mendekrip agar aplikasi dari app store bisa dianalisis dengan menggunakan disassembler/decompiler
  • Di Android rooting bisa dipakai untuk menginstall berbagai aplikasi seperti wireshark atau nmap (atau bahkan sebuah distribusi seperti Kali Linux) yang memungkinkan kita menggunakan Android sebagai device pentesting

Anti Rooting/Jailbreak detection

Nah sekarang kita memiliki masalah:

  • Memiliki HP yang diroot/jailbreak membantu proses pentesting
  • Aplikasi tertentu tidak mau jalan karena sudah kita root, dan aplikasi tersebut melalukan root/jailbreak detection

Ada beberapa pendekatan agar aplikasi menyangka bahwa device saat ini tidak diroot/jailbreak

  1. Memakai aplikasi penyembunyi, misalnya di Android ada RootCloak, Magisk dsb, di iOS ada Flex 2, Liberty Lite dsb. Tool-tool ini saling berlomba dengan pembuat detektor jailbreak, jadi di masa depan mungkin akan muncul tool-tool lain
  2. Modifikasi aplikasi agar aplikasi tersebut terus jalan walaupun device sudah diroot/jailbreak. Modifikasi bisa dilakukan temporer (dengan skrip Frida) atau permanen (dengan mengedit file smali-nya).

Cara pertama merupakan cara termudah, tinggal install aplikasi tertentu dan harapannya aplikasi akan mau terus berjalan. Sayangnya ini tidak selalu berhasil. Beberapa aplikasi memiliki proses deteksi yang sering tetap bisa mendeteksi anti jailbreak ini. Sebagai catatan: untuk saat ini Magisk bisa tersembunyi dari hampir semua aplikasi. Kelemahan Magisk adalah: kita tidak bisa menginstallnya bersamaan dengan XPosed Framework, jika diinstall bersamaan, maka akan terdeteksi dengan metode Safety Net.

Cara patching selalu berhasil, tapi masalahnya adalah ini tidak mudah. Diperlukan skill reverse engineering untuk mencari tahu di mana proses deteksi dilakukan, dan bagaimana modifikasinya agar aplikasi tetap jalan terus.

Jika aplikasi sifatnya tidak diobfuskasi (not obfuscated), maka pemeriksaan ini biasanya mudah dicari, apalagi jika namanya sangat jelas. Contohnya jika aplikasi memakai library RootBeer, pemeriksaannya sangat jelas karena nama methodnya “isRooted” mudah dilihat.

Pemeriksaan root dengan RootBeer

Tapi jika sebuah aplikasi sudah diobfuskasi (obfuscated), maka semua nama menjadi kurang jelas. Jika kode diatas sudah obfuscated, maka bisa muncul kira-kira seperti ini:

Kode yang sama, obfuscated, tentunya komentar tidak akan muncul ketika kode didekompilasi

Biasanya kode seperti ini bisa dicari dengan melihat berbagai string yang ada. Contohnya, jika root ketemu, maka aplikasi menampilkan dialog dengan string “Ponsel Anda diroot”. Untuk ini kita hanya perlu mencari string “Ponsel Anda diroot” dan bisa ditebak kondisi “if” mana yang perlu diubah.

Tentunya programmer bisa membuat pengecekan ini ini jadi semakin sulit. String bisa dienkripsi, jadi jika dicari tidak akan ketemu (kecuali kita dekrip dulu). Proses pemeriksaan root dan pengecekan juga bisa dipisah. Misalnya di kelas utama hanya mengeset variabel “z” menjadi true jika diroot. Di View yang lain baru kita cek isi variabel z. Proses pemeriksaan bisa dipanggil beberapa kali (jadi perlu dicek dibypass di banyak tempat, misalnya di waktu login, awal transaksi dan di akhir transaksi). Dan masih banyak lagi cara mempersulit agar pemeriksaan root tidak gampang dibypass.

Meski tidak gampang, tapi cara patching ini selalu berhasil jika penyerang cukup bisa memahami aplikasnya. Agar lebih sulit lagi, sebuah aplikasi kadang melakukan pemeriksaan terhadap signature packagenya, agar yakin bahwa aplikasi tersebut tidak diubah (ketika di-sign ulang, signature akan berubah). Dalam kasus ini pemeriksaan signature ini harus dipatch juga oleh pentester.

Protektor

Saat ini ada beberapa software protektor komersial yang memiliki proteksi yang cukup sulit. Contoh bentuk proteksinya:

  • File DEX dienkrip dan diload ketika aplikasi berjalan (runtime)
  • Pemeriksaan dilakukan di native library (harus memahami assembly)
  • Digunakan berbagai teknik antidebug dan anti tamper untuk mempersulit modifikasi

Semua proteksi ini bisa dibypass, tapi akan butuh waktu. Kadang ada orang yang menuliskan dengan detail teknik bypass protektor tertentu, tapi menurut saya ini agak sia-sia: setiap kali ada tulisan seperti itu, pembuat protektor akan mengubah sesuatu (kadang kecil sekali) sehingga langkah detail yang diberikan tidak lagi bisa dipakai.

Menurut saya yang lebih perlu adalah mengajarkan langkah umum bagaimana melakukan reversing. Dengan ini jika ada perubahan maka ilmunya akan tetap terpakai. Saya pernah menuliskan ini di blog saya yang lain.

Penutup

Banyak pentester pemula yang masih bingung dengan teknik pengecekan rooting seperti ini, jadi semoga artikel ini bisa membantu. Saat ini jika Anda tidak memakai XPosed Framework, maka Magisk bisa menyelesaikan 99% masalah deteksi rooting.

Di kesempatan lain saya berencana akan menulis lebih banyak mengenai SSL Pinning dan bagaimana melakukan unpinning. Topik SSL pinning ini merupakan topik sejenis yang sering dianggap sulit oleh pentester pemula. Tidak seperti rooting yang bisa diselesaikan dengan Magisk, berbagai aplikasi memakai teknik SSL pinning yang tidak standar sehingga butuh lebih banyak teknik manual.

Cerita pengalaman pentesting

Sudah beberapa tahun terakhir saya melakukan pentesting eksternal, menjadi freelancer melalui salah satu perusahaan security di Jakarta. Kali ini saya ingin menuliskan cerita pengalaman, dari mulai kenapa pentesting itu fun, dan beberapa pelajaran yang bisa dipetik (lesson learned) dari pekerjaan pentesting ini. Mengenai pembahasan apa itu pentesting dan serba-serbinya (terutama untuk orang yang ingin produknya ditest), bisa baca tulisan saya sebelumnya serba serbi pentest.

Berbagi ilmu bukan berarti saya sudah jagoan. Saya tidak merasa diri saya sangat jago, dan tidak merasa bahwa apa yang saya pentest pasti sudah aman. Tapi saya merasa ada beberapa hal yang saya tahu yang bisa saya bagikan. Sebagian pentester sangat pelit dengan ilmunya supaya kelihatan jagoan (padahal ilmu pentesting ya itu-itu aja).

Karena pekerjaan saya lakukan secara remote, scopenya biasanya terbatas: web dan aplikasi mobile. Tapi kadang ada juga pekerjaan yang melibatkan hardware misalnya kartu NFC (bendanya dikirim ke sini dan dikirimkan balik setelah selesai), atau masuk ke jaringan internal via VPN (yang ini jarang).

Sejauh ini saya tidak pernah berhadapan langsung dengan client, kecuali membalas beberapa hal teknis via email. Jadi lesson learned di sini tidak akan mencakup interaksi dengan client. Saya juga tidak membuat laporan resmi atau melakukan presentasi untuk client, ini dilakukan orang lain, jadi itu juga tidak masuk ke scope.

Saya memulai pekerjaan pentest dengan dasar ilmu yang mungkin berbeda dengan orang lain:

  • Dulu saya pernah bandel waktu di semester pertama kuliah, belasan tahun yang lalu. Saya dan temen saya pernah melakukan beberapa keisengan digital di kampus, seperti mengeksploitasi sistem, memasang keylogger, dsb
  • Setelah hampir DO gara-gara kasus tersebut (bukan gara-gara nilai kuliah), saya menjadi administrator sistem, jadi setelah itu punya dasar sistem operasi dan juga jaringan yang baik untuk OS Linux dan Windows. Sejak saat itu saya hanya hacking for fun, misalnya crack software untuk dipakai sendiri atau mengakali website yang saya pakai
  • Saya punya hobi reverse engineering, tapi tidak benar-benar saya dalami. Contohnya tahun 2006 saya pernah menulis reverse engineering virus Brontok. Saya kerja dengan C++ dan sering kali dalam debugging perlu sampai ke level assembly, jadi masih dengan berbagai hal low level.
  • Saya punya hobi programming dan sudah membuat berbagai macam aplikasi dari mulai web app, mobile app, dan bahkan porting kernel FreeBSD

Ketika mulai pentesting:

  • Saya tidak punya sertifikasi apa-apa
  • Saya cuma kenal sedikit orang security
  • Saya tidak kenal banyak tool yang populer, bahkan waktu itu belum pernah memakai zaproxy ataupun burp suite

Dari hasil ngobrol dengan beberapa orang, kebanyakan pentester mulai dari otodidak. Ada yang mulai dari iseng positif (CTF), ada juga yang masuk dunia security karena hal negatif (tertangkap hacking sesuatu). Beberapa orang berkembang dengan belajar lebih banyak, ada yang kuliah IT, ada yang mengambil sertifikasi, dsb. Sebagian lagi ilmunya tidak berkembang

Kelebihan saya menurut saya adalah: kemampuan membaca kode (baik biner maupun tekstual) dan pengetahuan kriptografi yang cukup baik sehingga bisa menemukan bug kriptografi sejenis hashing bug Mastercard. Bug sejenis ini sudah saya temukan di beberapa payment gateway di Indonesia (sayangnya karena NDA, tidak bisa diceritakan, jadi untuk bug semacam ini saya hanya bilang: bug sejenis Mastercard, tanpa menyebut detail varian masalahnya).

Bug seperti Bug Gojek bisa dengan mudah ditemukan banyak pentester, tapi jika saya membuat aplikasi dengan bug sejenis bug MasterCard atau E-Money Mandiri, saya cukup yakin hanya sedikit pentester yang mampu menemukannya.

Kenapa pentesting?

Kenapa tidak melakukan software development saja? kenapa tertarik pentesting?

  1. Saya memang suka ngoprek apa aja, jadi bagus untuk penyaluran hobi
  2. Memaksa saya belajar banyak teknologi baru. Misalnya ketika ada Angular XSS injection, mau tidak mau saya perlu mengenal angular untuk belajar eksploitasi itu
  3. Pekerjaan pentest sifatnya singkat. Software development biasanya lama. Setelah selesai pun perlu disupport
  4. Seru dan bangga jika bisa menemukan bug fatal. Ini terutama jika dilakukan pada sistem yang sudah live bertahun-tahun, dan baru ditemukan bugnya sekarang.

Apakah selalu menyenangkan?

Tidak ada hal yang semuanya fun, meskipun sebagian besar pekerjaan pentesting sifatnya fun tapi banyak juga yang membosankan. Contohnya jika kita perlu memeriksa sistem informasi yang punya form 10 halaman dan hanya jalan di tablet (jadi tidak bisa memakai skrip auto fill form, atau harus menulis sendiri skrip baru).

Terkadang developer juga membuat gemes karena tidak mengerti bug fatal yang sudah dijelaskan, sehingga kadang sampai perlu membuat video untuk menjelaskan bugnya. Lebih mengesalkan lagi jika mereka dikejar deadline, dan jadinya pentester ikutan dikejar deadline.

Di kasus lain, kadang aplikasinya belum selesai di tanggal yang dijadwalkan, jadi pentester malah jadi tester fungsionalitas aplikasi. Salah satu kasus terparah waktu diberi website dengan halaman HTML statik tanpa https, jadi hal yang bisa dicek sangat minim sekali.

Hal yang membuat sedih adalah Non Disclosure Agreement: kadang saya tidak bisa sharing bug-bug menarik ke orang lain jika itu menyangkut sistem mereka. Bug menarik di sini maksudnya bukan sekedar SQL Injection, XXE atau XSS. Jika bugnya di luar sistem mereka maka saya bisa sharing (contoh: bug alternatif firewall Palo Alto ini juga bisa dianggap bug di luar sistem mereka).

Lesson Learned

Ini adalah beberapa catatan random mengenai berbagai pelajaran yang saya petik

Belajarlah dari orang lain

Belajarlah dari orang lain karena ini cara yang baik untuk memulai. Pelajaran ini bisa didapat dari berbagai cerita orang ataupun dari berbagai tulisan/writeup di web. Setelah mendapatkan gambaran topiknya kita bisa mendalami topik dengan membaca berbagai macam buku atau mencoba berbagai tool.

Karena saya jarang melakukan testing internal (on site), saya juga masih terus belajar dengan melihat report dari orang lain yang mengerjakan testing internal. Tentunya jika kita belajar dari orang lain, kita sebaiknya juga mengajari orang lain, baik dengan memberikan ilmu via obrolan ataupun tulisan.

Saya belum pernah ketemu pentester atau hacker yang punya teknik luar biasa yang tidak diketahui siapapun. Semua ilmu mereka dipelajari dari berbagai writeup dan sharing yang dilakukan orang lain.

Satu orang biasanya tidak cukup

Banyak aplikasi bisa dipentest oleh satu orang, tapi untuk aplikasi super penting, misalnya yang melibatkan uang, sebaiknya pentest dilakukan oleh lebih dari satu orang.

Satu orang pentester bisa membuat kesalahan, dan sumbernya bisa banyak:

  • Skill orang tersebut memang kurang
  • Orang tersebut memiliki skill, tapi pada saat testing mungkin sedang mengantuk, kurang konsentrasi, dsb
  • Jumlah target ada banyak atau fungsionalitas aplikasi terlalu banyak
  • Ada masalah jaringan yang diakses dari jaringan orang tersebut sehingga tool yang dipakai tidak bekerja benar (contoh: bisa saja ISP tertentu menyisipkan iklan jadi eksploitasi gagal), tapi pentester dari jaringan lain ternyata berhasil mengeksploitasi
  • Masalah juga bisa ada di komputer pentester atau tool yang dipakai pentester. Contoh nyata yang pernah saya alami adalah: semua tool berbasis Java (burp suite dan zaproxy) selalu gagal melakukan koneksi ke server tertentu, tapi pentester lain yang memakai Fiddler ternyata bisa melakukan koneksi dengan normal.

Beberapa perusahaan menyewa beberapa group pentester, baik secara bersamaan, ataupun bergantian supaya yakin hasilnya aman. Pekerjaan pentest biasanya dibatasi waktu yang singkat, jadi jumlah bug yang ditemukan terbatas. Harapannya bug-bug besar ditemukan oleh pentester, tapi bukan jaminan bahwa nanti ada hacker atau bug hunter yang berusaha berminggu-minggu bisa tembus ke sebuah aplikasi.

Dari mengerjakan berbagai pekerjaan bareng orang lain, saya jadi belajar memperhatikan bug-bug tertentu yang kadang terlewat oleh saya dan juga tahu tools-tools yang populer dipakai.

Belajar terus informasi terbaru

Di awal kita bisa bertanya secara langsung pada seseorang untuk belajar berbagai hal dasar, tapi setelah itu saatnya kita perlu belajar sendiri “langsung dari sumbernya”. Biasanya sumber awal berbagai teknik baru adalah dari presentasi di security conference. Berbagai security conference akan mengupload file dan video presentasinya sehingga bisa kita baca, jadi tidak perlu ikutan untuk mendapatkan ilmunya.

Berbagai varian dari teknik yang sudah ada juga bisa dibaca dari writeup orang-orang yang melakukan bug bounty. Terkadang isinya membosankan, bug yang sudah sangat sering ditemukan (misalnya XSS), tapi sesekali ada teknik eksploitasi yang menarik yang bisa diambil ilmunya.

Sumber saya mendapatkan berbagai info security terbaru adalah dari reddit /r/netsec dan /r/reversengineering. Biasanya ini sudah cukup untuk merangkum berbagai informasi terbaru. Untuk info tambahan, saya subcribe ke security updates Debian (ada berbagai bug, kadang yang kurang signifikan saya ketahui dari adanya security update package tertentu)

Belajar mencari dan mendalami tool

Seperti saya ceritakan di awal: saya dulu kurang tahu banyak tool. Di awal saya banyak mengimplementasikan tool custom saya sendiri. Setelah memperhatikan pentester lain, jadi tahu berbagai tool yang sudah siap pakai dan lebih matang dari tool buatan saya.

Untuk satu tujuan, biasanya ada banyak sekali pilihan tool. Biasanya saya akan mencoba beberapa tool sejenis lalu memutuskan yang mana yang akan saya pakai, dan kadang saya pakai beberapa sekaligus. Contoh: untuk vulnerability scanner (yang sekedar mencari bug-bug sederhana) saya memakai Nexpose, OpenVAS, dan Arachni.

Sekedar catatan: Jika ada pentester yang hanya menyerahkan hasil scanner, tanpa melakukan testing manual, bisa dipastikan pentester tersebut tidak bekerja. Dalam kasus ini kemungkinan besar ada bug tidak ditemukan, dan Anda akan pusing membaca hasil scan yang masih butuh validasi lagi.

Untuk intercepting proxy, saya akhirnya memilih zaproxy dengan pertimbangan:

  • Gratis (burp professional tidak gratis, sementara versi free tidak bisa menyimpan session)
  • Open source. Fiddler dan burp suite (versi free) gratis tapi tidak open source
  • Cross platform (Fiddler tidak cross platform, belum lama ini baru ada versi beta untuk non Windows)

Setelah saya putuskan, saya kemudian berusaha mendalami jika tool tersebut cukup kompleks. Contohnya dalam kasus zaproxy di atas, setelah paham pemakaiannya, saya mencari tips dan trik di Internet dan ketemu bahwa kita bisa memakai berbagai list dari SecLists untuk input zaproxy dan kita bisa menambah berbagai skrip dan list kita sendiri.

Contoh skrip yang bisa kita buat misalnya:

  • skrip payload generator custom (mencoba-coba berbagai payload yang paling sering berhasil)
  • skrip untuk meng-exclude berbagai situs tidak penting (seperti facebook/google/dsb) dari log

Log IP saat ini

Saya melakukan testing dari rumah dengan IP dinamik, artinya IP eksternal bisa berubah setiap waktu. Kadang pihak yang ditest ingin mengecek apakah benar sebuah request berasal dari penyerang sesungguhnya atau dari kerjaan pentesting.

Dulu saya tidak mencatat ini, tapi untungnya saya masih bisa mengecek beberapa IP terakhir melalui log activity di Gmail. Sekarang supaya aman, saya membuat skrip yang mencatat setiap ada perubahan alamat IP.

Log semua sesi pentesting

Alasan saya tidak memakai burp versi gratis adalah karena tidak bisa menyimpan session. Kadang ada client yang menanyakan laporan beberapa bulan yang lalu, padahal website yang ditest sudah berubah. Dengan menyimpan semua sesi pentesting, bisa dilihat semua request yang pernah diberikan, dan juga bisa dijelaskan jika memang ada bagian website yang sudah berubah.

Lakukan otomasi

Banyak pekerjaan pentest sifatnya membosankan dan bisa diotomasi. Otomasi ini bisa dilakukan dengan menginstall berbagai software atau browser extension untuk tujuan khusus atau dengan membuat skrip sendiri. Contoh sederhana: beberapa aplikasi bank memiliki form yang terdiri atas beberapa halaman (misalnya form aplikasi kartu kredit). Dengan browser extension untuk mengisi form secara otomatis (atau menyimpan isian saat ini), maka kita bisa menghemat waktu ketika testing hal semacam itu.

Saya sendiri sekarang ini punya berbagai kategori skrip:

  • berbagai skrip python custom untuk target tertentu
  • berbagai skrip zaproxy
  • browser extension custom
  • skrip Frida untuk pentest Android dan iOS

Ilmu programming sangat terpakai

Secara umum ilmu programming sangat terpakai dalam pentesting. Selain untuk otomasi seperti yang disebutkan sebelumnya, juga terpakai untuk hal-hal berikut:

  • Source code review: keahlian membaca dan memahami program sangat diperlukan
  • API test. Beberapa perusahaan pernah meminta testing apakah API mereka bisa bocor (sejenis bug yang saya temui di kasus Mastercard), jadi mau tidak mau harus membuat program
  • memperbaiki eksploit (contoh: pernah saya temui eksploit Java yang hanya berjalan di Java versi baru karena memakai kelas yang hanya ada di Java terbaru, sedangkan client masih memakai Java versi sebelumnya)
  • Untuk modifikasi kode. Dalam pentest mobile app, kadang kode perlu dipatch untuk memudahkan testing

Dengan dasar programming yang baik, jumlah bug yang ditemukan dan dieksploitasi bisa lebih banyak.

Kreativitas CTF kadang diperlukan

Mungkin sekitar 90% eksploit dari Internet bisa langsung dijalankan, dan biasanya orang-orang akan menyerah jika eksploitnya tidak jalan hanya karena perbedaan versi software. Biasanya hal seperti ini justru jadi tantangan buat saya.

Hal-hal seperti ini bisa dipelajari dengan mengikuti CTF. Banyak soal CTF luar negeri adalah varian dari bug yang nyata. Dalam kasus ini kita perlu paham eksploitnya dan menerapkannya sesuai dengan situasi

Skill administrasi sistem sangat terpakai

Tidak selamanya kita bisa langsung testing ke target yang live. Kadang pentest dibatasi waktunya, kadang koneksi terlalu lambat dari Thailand ke Indonesia. Di saat seperti itu kadang saya perlu menginstall secara lokal berbagai software supaya bisa ditest eksplotasi secara lokal.

Selain contoh kasus di atas, kadang saya menemui mesin dengan sistem operasi yang sangat tua. Saya biasanya menginstall dulu versi yang sama di sistem lokal untuk mengetes eksploit supaya yakin apakah tidak akan membuat sistem crash.

Pengalaman dan ilmu administrasi sistem yang saya miliki ternyata sangat berguna untuk setup semacam ini. Saran saya jika Anda ingin jadi pentester, minimal bisa mensetup sistem operasi dan berbagai software secara lokal untuk ditest sendiri.

Penutup

Salah satu alasan saya dulu meninggalkan dunia security adalah: saya pikir software akan menjadi semakin aman. Ketika saya belajar mengenai SQL injection belasan tahun lalu, saya punya pikiran seperti ini: bug ini mudah sekali dihindari, dalam beberapa tahun ke depan pasti bug seperti ini sudah nggak akan ditemui lagi.

Bug sql injection ini baru saya temukan Januari 2019, di sebuah subdomain situs sebuah bank yang bisa diakses dari publik

Setelah melakukan pentesting beberapa tahun, ternyata saya salah besar, sampai saat ini bug SQL injection masih cukup sering saya temui. Bug buffer overflow dan sejenisnya juga masih ditemui. Sekarang ini banyak jenis bug baru yang sepertinya akan tetap ada sampai belasan tahun ke depan.

Jadi sepertinya sampai cukup jauh ke depan, pentester masih sangat dibutuhkan. Berbagai tools akan membuat beberapa pekerjaan pentesting jadi sederhana, tapi masih banyak yang lain yang tetap harus manual. Tentunya ilmu pentesting ini harus diupdate terus supaya mengikuti perkembangan teknologi. Tiap teknologi bisa membawa banyak masalah baru misalnya yang trend sekarang:

  • Cloud: sering ada misconfiguration
  • IOT: banyak masalah dasar seperti 20 tahun yang lalu (buffer overflow dsb) dan juga ada berbagai serangan menarik seperti glitch attack para microcontroller
  • BlockChain: masalah pada implementasi, pada enkripsi, pada smart contract
  • machine learning (adversarial machine learning)

Dilihat dari contoh di atas, di masa depan ilmu programming dan devops makin diperlukan untuk memahami masalah security yang lebih kompleks.