$ k=v p &
[1] 3028
apakah ada cara untuk p
mengubah konten /proc/3028/environ
agar tidak menyebutkan k=v
saat p
masih berjalan?
$ k=v p &
[1] 3028
apakah ada cara untuk p
mengubah konten /proc/3028/environ
agar tidak menyebutkan k=v
saat p
masih berjalan?
Jawaban:
Di Linux, Anda dapat menimpa nilai string lingkungan pada stack.
Jadi, Anda dapat menyembunyikan entri dengan menimpanya dengan nol atau apa pun:
#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
yang k=v
telah ditimpa dengan \0\0\0
.
Perhatikan bahwa setenv("k", "", 1)
untuk menimpa nilai tidak akan berfungsi seperti dalam kasus itu, "k="
string baru dialokasikan.
Jika Anda tidak mengubah k
variabel lingkungan dengan setenv()
/ putenv()
, maka Anda juga harus dapat melakukan sesuatu seperti ini untuk mendapatkan alamat k=v
string pada stack (well, 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 perlu dicatat bahwa itu hanya menghapus salah satu k=v
entri yang diterima di lingkungan. Biasanya, hanya ada satu, tetapi tidak ada yang menghentikan siapa pun melewati keduanya k=v1
dan k=v2
(atau k=v
dua kali) dalam daftar env diteruskan execve()
. Itu telah menjadi penyebab kerentanan keamanan di masa lalu seperti CVE-2016-2381 . Itu bisa benar-benar terjadi dengan bash
sebelum shellshock ketika 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 informasi rahasia ke perintah (seperti pipa misalnya) jika mengeksposnya melalui /proc/pid/environ
adalah masalah.
Juga catat bahwa bertentangan dengan /proc/pid/cmdline
, /proc/pid/environment
hanya dapat diakses oleh proses dengan cairan atau akar yang sama (atau root hanya jika cairan dan bentuk proses tidak sama kelihatannya).
Anda dapat menyembunyikan nilai itu dari mereka /proc/pid/environ
, tetapi mereka mungkin masih bisa mendapatkan salinan lain yang Anda buat dari string dalam memori, misalnya dengan melampirkan debugger ke dalamnya.
Lihat https://www.kernel.org/doc/Documentation/security/Yama.txt untuk mengetahui cara mencegah setidaknya pengguna yang bukan root melakukan hal itu.
Belum perlu menimpa string di atas (tidak benar-benar aktif ) tumpukan utas di Linux sejak 2010.
Keduanya /proc/self/cmdline
dan /proc/self/environ
dapat dimodifikasi oleh proses itu sendiri pada saat runtime, dengan tidak 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 kita hanya perlu membuat argumen baru atau string lingkungan (bukan vektor, pemberitahuan - memori yang ditunjuk harus berupa data string yang sebenarnya, digabung dan ␀
-ditinjau) dan beri tahu kernel di mana letaknya.
Ini didokumentasikan di halaman manual Linux untuk prctl(2)
fungsi serta environ(7)
halaman manual. Apa yang tidak didokumentasikan adalah bahwa kernel menolak upaya untuk mengatur alamat awal di atas alamat akhir, atau alamat akhir di bawah alamat awal; atau untuk (kembali) mengatur salah satu alamat ke nol. Juga, ini bukan mekanisme asli yang diusulkan oleh Bryan Donlan pada 2009, yang memungkinkan pengaturan mulai dan berakhir dalam satu operasi, secara atom. Selain itu, kernel tidak memberikan cara untuk mendapatkan nilai saat ini dari pointer ini.
Ini membuatnya sulit untuk memodifikasi lingkungan dan area baris perintah prctl()
. Kita harus memanggil prctl()
fungsi hingga empat kali karena upaya pertama dapat menghasilkan upaya untuk mengatur pointer awal lebih tinggi dari pointer akhir, tergantung dari tempat data lama dan baru berada dalam memori. Kita harus menyebutnya empat kali lebih jauh jika kita ingin memastikan bahwa ini tidak menghasilkan jendela peluang bagi proses lain pada sistem untuk memeriksa rentang ruang memori proses yang sewenang-wenang dalam periode ketika awal / akhir baru telah ditetapkan tetapi akhir / awal baru belum.
Panggilan sistem atom tunggal yang menetapkan seluruh rentang dalam sekali jalan akan jauh lebih mudah bagi program aplikasi untuk digunakan dengan aman.
Kerutan lebih lanjut adalah bahwa, tanpa alasan yang benar-benar baik (mengingat pemeriksaan di kernel, toh kemampuan menimpa area data asli , dan fakta bahwa padanannya bukan operasi istimewa pada BSD manapun), di Linux ini membutuhkan superuser hak istimewa.
Saya menulis fungsi setprocargv()
dan setprocenvv()
fungsi yang cukup sederhana untuk perangkat saya, yang menggunakan ini. Program pemuatan rantai dari perangkat yang dibangun, seperti setenv
dan foreground
, dengan demikian mencerminkan argumen perintah dan lingkungan berantai, di mana Linux mengizinkan.
# / package / admin / nosh / command / clearenv setenv WIBBLE goyangan foreground jeda \; benar & [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 /viron 00000000 57 49 42 42 4c 45 3d 77 6f 62 62 6c 65 00 | WIBBLE = goyangan. | 0000000e # hexdump -C / proc / 1058 / cmdline 00000000 70 61 75 73 65 00 | jeda. | 00000006 # hexdump -C / proc / 1058 /viron 00000000 57 49 42 42 4c 45 3d 77 6f 62 62 6c 65 00 | WIBBLE = goyangan. | 0000000e #
Perhatikan bahwa ini tidak menghalangi hal-hal yang melacak proses dan mengakses ingatannya secara langsung dengan cara lain (daripada melalui dua file pseudo ini), dan tentu saja meninggalkan jendela sebelum string dimodifikasi di mana informasi ini dapat dilihat, hanya saja seperti menimpa data di atas tumpukan utas utama. Dan seperti halnya dengan menimpa data, ini tidak memperhitungkan perpustakaan runtime bahasa yang membuat salinan lingkungan (pada heap) dalam berbagai keadaan. Secara umum, jangan menganggap ini sebagai mekanisme yang baik untuk meneruskan "rahasia" ke program karena (katakanlah) memiliki itu mewarisi deskriptor file terbuka ke ujung baca dari pipa yang tidak disebutkan namanya, membaca ke buffer input sepenuhnya di bawah kendali Anda yang kemudian Anda bersihkan.
/proc/$pid/stat
(selain nilai-nilai lain yang Anda mungkin perlu struct prctl_mm_map
). Lihat juga contoh filter_env.c saya untuk demo kecil. JdeBP, dapatkah Anda menambahkan tautan ke setprocargv()
/ setprocenvv()
fungsi Anda?