
You Cant C Me adalah tantangan CTF kategori Reversing dengan tingkat kesulitan easy dari HTB Challenges. Soal ini dibuat oleh MinatoTW dan telah dirilis pada 13 April 2021.
Berikut adalah deskripsi dari tantangan ini:
Can you see me?
Pendahuluan
Setelah mengesktrak archive tantangan, saya menemukan satu file bernama auth
.
➜ ls -lahtotal 28Kdrwxrwxr-x 3 kali kali 4.0K Mar 5 05:06 .drwxrwxr-x 5 kali kali 4.0K Mar 5 05:03 ..-rwxr-xr-x 1 kali kali 15K Dec 1 2020 auth
➜ file authauth: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, stripped
File ini merupakan executable Linux berformat ELF 64-bit LSB PIE
. Selain itu, file ini telah di-strip sehingga nama-nama fungsi tidak tersimpan.
Ketika menjalankan program ini, saya mencoba menginput “test” dan hasilnya program memberikan respons:
I said, you can't c me!
Penggunaan perintah strings
tidak menemukan hal yang menarik. Terdapat string “this_is_the_password”, ketika saya mencoba menginputkannya, program tetap memberikan respons yang sama (gagal).
➜ strings auth
..<SNIPPED>..Welcome!I said, you can't c me!HTB{%s}this_is_the_passwordm^&&fiUo&kUZ'ZUYUc);*3$"..<SNIPPED>..➜ strings auth
..<SNIPPED>..Welcome!I said, you can't c me!HTB{%s}this_is_the_passwordm^&&fiUo&kUZ'ZUYUc);*3$"..<SNIPPED>..
Proof of Concept
1. Analisis dinamis dengan ltrace
Untuk mendapatkan string yang valid, saya melakukan pengujian dinamis menggunakan ltrace
. Dengan alat ini, saya dapat melihat fungsi-fungsi dari shared library yang dipanggil ketika program berjalan.
➜ ltrace ./authprintf("Welcome!\n"Welcome!) = 9malloc(21) = 0x229cc6b0fgets(test"test\n", 21, 0x7f17c8dcc8e0) = 0x229cc6b0strcmp("wh00ps!_y0u_d1d_c_m3", "test\n") = 3printf("I said, you can't c me!\n"I said, you can't c me!) = 24+++ exited (status 0) +++
Terlihat bahwa program melakukan validasi inputan pengguna dengan valid key (“wh00ps!_y0u_d1d_c_m3”) menggunakan fungsi strcmp
. Ketika saya menginputkan key tersebut, program akan menampilkan flag.
➜ ./authWelcome!wh00ps!_y0u_d1d_c_m3HTB{wh00ps!_y0u_d1d_c_m3}
2. Analisis statik dengan decompiler
Karena program telah di-strip, saya harus melihat semua fungsi untuk menentukan mana yang merupakan fungsi utama. Setelah melihat-lihat, saya menemukan fungsi FUN_00401160(void)
yang memiliki string “I said, you can’t c me!”.
undefined4 FUN_00401160(void)
{ int iVar1; undefined8 local_48; undefined8 local_40; undefined4 local_38; char *local_30; undefined8 local_28; undefined8 local_20; undefined4 local_18; undefined local_14; int local_10; undefined4 local_c;
local_c = 0; local_10 = 0; printf("Welcome!\n"); local_28._0_1_ = 't'; local_28._1_1_ = 'h'; local_28._2_1_ = 'i'; local_28._3_1_ = 's'; local_28._4_1_ = '_'; local_28._5_1_ = 'i'; local_28._6_1_ = 's'; local_28._7_1_ = '_'; local_20._0_1_ = 't'; local_20._1_1_ = 'h'; local_20._2_1_ = 'e'; local_20._3_1_ = '_'; local_20._4_1_ = 'p'; local_20._5_1_ = 'a'; local_20._6_1_ = 's'; local_20._7_1_ = 's'; local_18 = 0x64726f77; local_14 = 0; local_30 = (char *)malloc(0x15); local_48 = 0x5517696626265e6d; local_40 = 0x555a275a556b266f; local_38 = 0x29635559; for (local_10 = 0; local_10 < 0x14; local_10 = local_10 + 1) { *(char *)((long)&local_28 + (long)local_10) = *(char *)((long)&local_48 + (long)local_10) + '\n' ; } fgets(local_30,0x15,stdin); iVar1 = strcmp((char *)&local_28,local_30); if (iVar1 == 0) { printf("HTB{%s}\n",local_30); } else { printf("I said, you can\'t c me!\n"); } return local_c;}
Untuk mempermudah analisis, saya mengubah beberapa nama variabel sehingga terlihat seperti berikut:
undefined4 FUN_00401160(void)
{ int isInputValid; undefined8 encrypted; undefined8 local_40; undefined4 local_38; char *input; undefined8 decrypted; undefined8 local_20; undefined4 local_18; undefined local_14; int i; undefined4 local_c;
local_c = 0; i = 0; printf("Welcome!\n"); decrypted._0_1_ = 't'; decrypted._1_1_ = 'h'; decrypted._2_1_ = 'i'; decrypted._3_1_ = 's'; decrypted._4_1_ = '_'; decrypted._5_1_ = 'i'; decrypted._6_1_ = 's'; decrypted._7_1_ = '_'; local_20._0_1_ = 't'; local_20._1_1_ = 'h'; local_20._2_1_ = 'e'; local_20._3_1_ = '_'; local_20._4_1_ = 'p'; local_20._5_1_ = 'a'; local_20._6_1_ = 's'; local_20._7_1_ = 's'; local_18 = 0x64726f77; local_14 = 0; input = (char *)malloc(21); encrypted = 0x5517696626265e6d; encrypted2 = 0x555a275a556b266f; encrypted3 = 0x29635559; for (i = 0; i < 20; i = i + 1) { *(char *)((long)&decrypted + (long)i) = *(char *)((long)&encrypted + (long)i) + '\n'; } fgets(input,21,stdin); isInputValid = strcmp((char *)&decrypted,input); if (isInputValid == 0) { printf("HTB{%s}\n",input); } else { printf("I said, you can\'t c me!\n"); } return local_c;}
Dari sini, saya dapat menyimpulkan bahwa terdapat string yang terenkripsi, yaitu:
- 0x5517696626265e6d
- 0x555a275a556b266f
- 0x29635559
Untuk mendapatkan hasil dekripsi, kita harus menambahkan nilai \n
(nilai ASCII 10) ke setiap karakter.
Dari hasil analisis ini, saya mencoba menulis ulang kode program tersebut menggunakan bahasa C sehingga terlihat seperti berikut:
#include <stdio.h>#include <string.h>
int main() {
// set variable int isInputValid; char input[21]; char decrypted[20]; char encrypted[20] = {0x6d, 0x5e, 0x26, 0x26, 0x66, 0x69, 0x17, 0x55, // 0x5517696626265e6d 0x6f, 0x26, 0x6b, 0x55, 0x5a, 0x27, 0x5a, 0x55, // 0x555a275a556b266f 0x59, 0x55, 0x63, 0x29 }; // 0x29635559
for (int i = 0; i < 20; i = i + 1) { decrypted[i] = encrypted[i] + '\n'; }
printf("decrypted: %s\n", decrypted); // for debugs
printf("Welcome!\n"); fgets(input, sizeof(input), stdin);
isInputValid = strcmp(decrypted, input);
if (isInputValid == 0) { printf("HTB{%s}", decrypted); } else { printf("I said, you can\'t c me!\n"); }
return 0;}
Hasilnya:
➜ gcc main.c -o main; sleep 2 ; ./maindecrypted: wh00ps!_y0u_d1d_c_m3Welcome!wh00ps!_y0u_d1d_c_m3HTB{wh00ps!_y0u_d1d_c_m3}
Penutup
Itulah cara menyelesaikan tantangan You Cant C Me dari HTB Challenges dengan menggunakan teknik analisis statik dan dinamis. Semoga bermanfaat!