Di Linux, Anda dapat menimpa nilai string lingkungan pada stack.
Jadi, Anda dapat menyembunyikan entri tersebut dengan menimpanya dengan nol atau yang lainnya:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char* argv[], char* envp[]) {
char cmd[100];
while (*envp) {
if (strncmp(*envp, "k=", 2) == 0)
memset(*envp, 0, strlen(*envp));
envp++;
}
sprintf(cmd, "cat /proc/%u/environ", getpid());
system(cmd);
return 0;
}
Jalankan sebagai:
$ env -i a=foo k=v b=bar ./wipe-env | hd
00000000 61 3d 66 6f 6f 00 00 00 00 00 62 3d 62 61 72 00 |a=foo.....b=bar.|
00000010
k=v
telah ditimpa dengan \0\0\0
.
Perhatikan bahwa setenv("k", "", 1)
untuk menimpa nilai tidak akan berfungsi seperti dalam kasus itu, "k="
baru string dialokasikan.
Jika Anda belum mengubah k
variabel lingkungan dengan setenv()
/putenv()
, maka Anda juga harus dapat melakukan hal seperti ini untuk mendapatkan alamat k=v
string pada tumpukan (yah, salah satunya):
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char* argv[]) {
char cmd[100];
char *e = getenv("k");
if (e) {
e -= strlen("k=");
memset(e, 0, strlen(e));
}
sprintf(cmd, "cat /proc/%u/environ", getpid());
system(cmd);
return 0;
}
Namun perhatikan bahwa ini hanya menghapus satu dari k=v
entri yang diterima di lingkungan. Biasanya, hanya ada satu, tetapi tidak ada yang menghentikan siapa pun untuk meneruskan keduanya k=v1
dan k=v2
(atau k=v
dua kali) dalam daftar env diteruskan ke execve()
. Itulah penyebab kerentanan keamanan di masa lalu seperti CVE-2016-2381. Itu benar-benar bisa terjadi dengan bash
sebelum shellshock saat mengekspor variabel dan fungsi dengan nama yang sama.
Bagaimanapun, akan selalu ada jendela kecil di mana string env var belum diganti, jadi Anda mungkin ingin mencari cara lain untuk meneruskan rahasia informasi ke perintah (seperti pipa misalnya) jika memaparkannya melalui /proc/pid/environ
adalah masalah.
Perhatikan juga bahwa bertentangan dengan /proc/pid/cmdline
, /proc/pid/environment
hanya dapat diakses oleh proses dengan euid atau root yang sama (atau root hanya jika tampaknya euid dan ruid dari proses tersebut tidak sama).
Anda dapat menyembunyikan nilai tersebut dari mereka di /proc/pid/environ
, tetapi mereka mungkin masih bisa mendapatkan salinan string lainnya yang Anda buat di memori, misalnya dengan melampirkan debugger ke string tersebut.
Lihat https://www.kernel.org/doc/Documentation/security/Yama.txt untuk cara mencegah setidaknya pengguna non-root melakukannya.
Tidak perlu menimpa string di atas (tidak benar-benar on ) tumpukan utas utama di Linux sejak 2010.
Keduanya /proc/self/cmdline
dan /proc/self/environ
dapat dimodifikasi oleh proses itu sendiri saat runtime, dengan memanggil prctl()
fungsi dengan masing-masing PR_SET_MM_ARG_START
+PR_SET_MM_ARG_END
atau PR_SET_MM_ENV_START
+PR_SET_MM_ENV_END
. Ini secara langsung mengatur pointer memori ke dalam ruang memori aplikasi proses, yang dipegang oleh kernel untuk setiap proses, yang digunakan untuk mengambil konten /proc/${PID}/cmdline
dan /proc/${PID}/environ
, dan karenanya baris perintah dan lingkungan dilaporkan oleh ps
perintah.
Jadi seseorang hanya perlu membuat argumen baru atau string lingkungan (bukan vektor, perhatikan — memori yang ditunjuk haruslah data string aktual, digabungkan dan ␀
-delimited) dan beri tahu kernel di mana tempatnya.
Ini didokumentasikan di halaman manual Linux untuk prctl(2)
fungsi serta environ(7)
halaman manual. Apa yang tidak didokumentasikan adalah bahwa kernel menolak setiap upaya untuk mengatur alamat awal di atas alamat akhir, atau alamat akhir di bawah alamat awal; atau untuk (kembali) menyetel salah satu alamat ke nol. Juga, ini bukan mekanisme asli yang diusulkan oleh Bryan Donlan pada tahun 2009, yang mengizinkan pengaturan awal dan akhir dalam satu operasi, secara atomik. Selain itu, kernel tidak menyediakan cara untuk mendapatkan nilai saat ini dari pointer ini.
Hal ini menyulitkan untuk memodifikasi lingkungan dan area baris perintah dengan prctl()
. Seseorang harus memanggil prctl()
berfungsi hingga empat kali karena upaya pertama dapat mengakibatkan upaya untuk menyetel penunjuk awal lebih tinggi dari penunjuk akhir, tergantung dari mana data lama dan baru berada di memori. Seseorang harus menyebutnya selanjutnya empat kali jika seseorang ingin memastikan bahwa ini tidak menghasilkan jendela peluang bagi proses lain pada sistem untuk memeriksa rentang ruang memori proses yang sewenang-wenang pada periode ketika awal/akhir baru telah ditetapkan tetapi akhir baru /mulai belum.
Panggilan sistem atom tunggal yang menyetel seluruh rentang sekaligus akan jauh lebih mudah bagi program aplikasi untuk digunakan dengan aman.
Kerutan lebih lanjut adalah, tanpa alasan yang benar-benar bagus (mengingat pemeriksaan di kernel, overwritability dari area data asli pokoknya , dan fakta bahwa padanannya bukanlah operasi dengan hak istimewa di salah satu BSD), di Linux hal ini memerlukan hak istimewa pengguna super.
Saya menulis setprocargv()
yang cukup sederhana dan setprocenvv()
fungsi untuk perangkat saya, yang menggunakan ini. Program pemuatan berantai dari kumpulan alat bawaan, seperti setenv
dan foreground
, dengan demikian mencerminkan argumen dan lingkungan yang dirantai ke perintah, di mana Linux mengizinkan.
# /package/admin/nosh/command/clearenv setenv WIBBLE wobble foreground pause \; true & [1] 1057 # hexdump -C /proc/1057/cmdline 00000000 66 6f 72 65 67 72 6f 75 6e 64 00 70 61 75 73 65 |foreground.pause| 00000010 00 3b 00 74 72 75 65 00 |.;.true.| 00000018 # hexdump -C /proc/1057/environ 00000000 57 49 42 42 4c 45 3d 77 6f 62 62 6c 65 00 |WIBBLE=wobble.| 0000000e # hexdump -C /proc/1058/cmdline 00000000 70 61 75 73 65 00 |pause.| 00000006 # hexdump -C /proc/1058/environ 00000000 57 49 42 42 4c 45 3d 77 6f 62 62 6c 65 00 |WIBBLE=wobble.| 0000000e #
Perhatikan bahwa ini tidak menghalangi hal-hal yang melacak proses dan mengakses memorinya secara langsung dengan cara lain (daripada melalui dua file semu ini), dan tentu saja meninggalkan jendela sebelum string diubah di mana informasi ini dapat dilihat, hanya seperti menimpa data di atas tumpukan utas utama. Dan seperti halnya menimpa data, ini tidak memperhitungkan pustaka runtime bahasa yang membuat salinan lingkungan (di heap) dalam berbagai keadaan. Secara umum, jangan menganggap ini sebagai mekanisme yang baik untuk meneruskan "rahasia" ke program seperti (katakanlah) mewarisi deskriptor file terbuka ke ujung baca pipa yang tidak disebutkan namanya, baca ke buffer input sepenuhnya di bawah kendali Anda yang kemudian Anda hapus.
Bacaan lebih lanjut
- Timo Sirainen (2009-10-02). Menambahkan opsi PR_SET_PROCTITLE_AREA untuk prctl() . Milis Kernel Linux.
- https://unix.stackexchange.com/a/432681/5132
- Daniel J. Bernstein. Antarmuka kata sandi . cr.yp.to.
- https://github.com/jdebp/nosh/blob/master/source/setprocargv.cpp
- https://github.com/jdebp/nosh/blob/master/source/setprocenvv.cpp