Solusi 1: C (Mac OS X x86_64), 109 byte
Sumber untuk golf_sol1.c
main[]={142510920,2336753547,3505849471,284148040,2370322315,2314740852,1351437506,1208291319,914962059,195};
Program di atas perlu dikompilasi dengan akses eksekusi pada segmen __DATA.
clang golf_sol1.c -o golf_sol1 -Xlinker -segprot -Xlinker __DATA -Xlinker rwx -Xlinker rwx
Kemudian untuk menjalankan program jalankan yang berikut:
./golf_sol1 $(ruby -e 'puts "\xf5\xff\xff\xfe\xff\xff\x44\x82\x57\x7d\xff\x7f"')
Hasil:
Sayangnya Valgrind tidak melihat memori yang dialokasikan dari panggilan sistem, jadi saya tidak dapat menunjukkan kebocoran yang terdeteksi dengan baik.
Namun kita dapat melihat vmmap untuk melihat sebagian besar memori yang dialokasikan (MALLOC metadata).
VIRTUAL REGION
REGION TYPE SIZE COUNT (non-coalesced)
=========== ======= =======
Kernel Alloc Once 4K 2
MALLOC guard page 16K 4
MALLOC metadata 16.2M 7
MALLOC_SMALL 8192K 2 see MALLOC ZONE table below
MALLOC_TINY 1024K 2 see MALLOC ZONE table below
STACK GUARD 56.0M 2
Stack 8192K 3
VM_ALLOCATE (reserved) 520K 3 reserved VM address space (unallocated)
__DATA 684K 42
__LINKEDIT 70.8M 4
__TEXT 5960K 44
shared memory 8K 3
=========== ======= =======
TOTAL 167.0M 106
TOTAL, minus reserved VM space 166.5M 106
Penjelasan
Jadi saya pikir saya perlu menggambarkan apa yang sebenarnya terjadi di sini, sebelum pindah ke solusi yang ditingkatkan.
Fungsi utama ini adalah menyalahgunakan deklarasi tipe C yang hilang (sehingga default ke int tanpa kita perlu membuang karakter menulisnya), serta bagaimana simbol bekerja. Linker hanya peduli apakah mereka tidak dapat menemukan simbol yang dipanggil main
untuk dipanggil. Jadi di sini kita membuat array int utama yang kita inisialisasi dengan shellcode kita yang akan dieksekusi. Karena itu, main tidak akan ditambahkan ke segmen __TEXT melainkan segmen __DATA, alasan kami perlu mengkompilasi program dengan segmen __DATA yang dapat dieksekusi.
Shellcode yang ditemukan di main adalah sebagai berikut:
movq 8(%rsi), %rdi
movl (%rdi), %eax
movq 4(%rdi), %rdi
notl %eax
shrq $16, %rdi
movl (%rdi), %edi
leaq -0x8(%rsp), %rsi
movl %eax, %edx
leaq -9(%rax), %r10
syscall
movq (%rsi), %rsi
movl %esi, (%rsi)
ret
Apa yang dilakukan adalah memanggil fungsi syscall untuk mengalokasikan halaman memori (syscall mach_vm_allocate menggunakan internal). RAX harus sama dengan 0x100000a (memberitahu syscall fungsi mana yang kita inginkan), sementara RDI memegang target untuk alokasi (dalam kasus kami kami ingin ini mach_task_self ()), RSI harus memegang alamat untuk menulis pointer ke memori yang baru dibuat (jadi kami hanya mengarahkannya ke bagian pada stack), RDX memegang ukuran alokasi (kami hanya meneruskan dalam RAX atau 0x100000a hanya untuk menghemat byte), R10 memegang bendera (kami menunjukkan itu bisa dialokasikan di mana saja).
Sekarang tidak jelas dari mana RAX dan RDI mendapatkan nilai-nilai mereka. Kami tahu RAX harus 0x100000a, dan RDI harus menjadi nilai mach_task_self () yang dikembalikan. Untungnya mach_task_self () sebenarnya adalah makro untuk variabel (mach_task_self_), yang berada di alamat memori yang sama setiap kali (harus berubah saat reboot). Dalam contoh khusus saya, mach_task_self_ kebetulan berada di 0x00007fff7d578244. Jadi untuk mengurangi instruksi, kami akan meneruskan data ini dari argv. Inilah sebabnya kami menjalankan program dengan ungkapan ini$(ruby -e 'puts "\xf5\xff\xff\xfe\xff\xff\x44\x82\x57\x7d\xff\x7f"')
untuk argumen pertama. String adalah dua nilai yang digabungkan, di mana nilai RAX (0x100000a) hanya 32 bit dan telah memiliki komplemen seseorang yang diterapkan padanya (jadi tidak ada byte nol; kami hanya BUKAN nilai untuk mendapatkan yang asli), nilai berikutnya adalah RDI (0x00007fff7d578244) yang telah bergeser ke kiri dengan 2 byte sampah tambahan ditambahkan ke akhir (lagi untuk mengecualikan byte nol, kami hanya menggesernya kembali ke kanan untuk mendapatkannya kembali ke aslinya).
Setelah syscall, kami menulis ke memori yang baru dialokasikan. Alasannya adalah karena memori yang dialokasikan menggunakan mach_vm_allocate (atau syscall ini) sebenarnya adalah halaman VM, dan tidak secara otomatis dipetakan ke dalam memori. Sebaliknya mereka dicadangkan sampai data dituliskan kepada mereka, dan kemudian halaman-halaman itu dipetakan ke dalam memori. Tidak yakin apakah itu akan memenuhi persyaratan jika itu hanya dicadangkan.
Untuk solusi selanjutnya, kami akan memanfaatkan fakta bahwa shellcode kami tidak memiliki byte nol, sehingga dapat memindahkannya ke luar kode program kami untuk mengurangi ukurannya.
Solusi 2: C (Mac OS X x86_64), 44 byte
Sumber untuk golf_sol2.c
main[]={141986632,10937,1032669184,2,42227};
Program di atas perlu dikompilasi dengan akses eksekusi pada segmen __DATA.
clang golf_sol2.c -o golf_sol2 -Xlinker -segprot -Xlinker __DATA -Xlinker rwx -Xlinker rwx
Kemudian untuk menjalankan program jalankan yang berikut:
./golf_sol2 $(ruby -e 'puts "\xb8\xf5\xff\xff\xfe\xf7\xd0\x48\xbf\xff\xff\x44\x82\x57\x7d\xff\x7f\x48\xc1\xef\x10\x8b\x3f\x48\x8d\x74\x24\xf8\x89\xc2\x4c\x8d\x50\xf7\x0f\x05\x48\x8b\x36\x89\x36\xc3"')
Hasilnya harus sama seperti sebelumnya, karena kami membuat alokasi dengan ukuran yang sama.
Penjelasan
Mengikuti konsep yang sama seperti solusi 1, dengan pengecualian bahwa kami telah memindahkan potongan kode bocor kami di luar program.
Shellcode yang ditemukan di main sekarang adalah sebagai berikut:
movq 8(%rsi), %rsi
movl $42, %ecx
leaq 2(%rip), %rdi
rep movsb (%rsi), (%rdi)
Ini pada dasarnya menyalin shellcode yang kami berikan dalam argv untuk menjadi setelah kode ini (jadi setelah itu disalin, itu akan menjalankan shellcode yang dimasukkan). Apa yang menguntungkan kami adalah bahwa segmen __DATA akan berukuran setidaknya satu halaman, jadi meskipun kode kami tidak sebesar itu, kami masih dapat "dengan aman" menulis lebih banyak. Kelemahannya adalah solusi ideal di sini, bahkan tidak perlu salinannya, melainkan hanya akan memanggil dan menjalankan shellcode di argv secara langsung. Namun sayangnya, memori ini tidak memiliki hak eksekusi. Kita dapat mengubah hak memori ini, namun itu membutuhkan lebih banyak kode daripada hanya menyalinnya. Strategi alternatif adalah mengubah hak dari program eksternal (tetapi lebih lanjut tentang itu nanti).
Shellcode yang kami berikan ke argv adalah sebagai berikut:
movl $0xfefffff5, %eax
notl %eax
movq $0x7fff7d578244ffff, %rdi
shrq $16, %rdi
movl (%rdi), %edi
leaq -0x8(%rsp), %rsi
movl %eax, %edx
leaq -9(%rax), %r10
syscall
movq (%rsi), %rsi
movl %esi, (%rsi)
ret
Ini hampir sama dengan kode kami sebelumnya, hanya perbedaannya adalah kami menyertakan nilai untuk EAX dan RDI secara langsung.
Kemungkinan Solusi 1: C (Mac OS X x86_64), 11 byte
Gagasan memodifikasi program secara eksternal, memberi kita kemungkinan solusi untuk memindahkan leaker ke program eksternal. Di mana program aktual kami (pengiriman) hanyalah program tiruan, dan program leaker akan mengalokasikan sebagian memori dalam program target kami. Sekarang saya tidak yakin apakah ini akan masuk dalam aturan untuk tantangan ini, tetapi tetap membagikannya.
Jadi jika kita menggunakan mach_vm_allocate dalam program eksternal dengan target yang ditetapkan untuk program tantangan kita, itu bisa berarti program tantangan kita hanya perlu menjadi sesuatu seperti:
main=65259;
Di mana shellcode itu hanyalah lompatan pendek ke dirinya sendiri (lompatan / loop tak terbatas), sehingga program tetap terbuka dan kita dapat merujuknya dari program eksternal.
Kemungkinan Solusi 2: C (Mac OS X x86_64), 8 byte
Lucunya ketika saya melihat output valgrind, saya melihat bahwa setidaknya menurut valgrind, memori kebocoran bocor. Jadi secara efektif setiap program membocorkan sejumlah memori. Dengan demikian, kita sebenarnya bisa membuat program yang tidak melakukan apa-apa (hanya keluar), dan itu sebenarnya akan membocorkan memori.
Sumber:
main(){}
==55263== LEAK SUMMARY:
==55263== definitely lost: 696 bytes in 17 blocks
==55263== indirectly lost: 17,722 bytes in 128 blocks
==55263== possibly lost: 0 bytes in 0 blocks
==55263== still reachable: 0 bytes in 0 blocks
==55263== suppressed: 16,316 bytes in 272 blocks