ubah / proc / PID / environment setelah proses dimulai

11
$ k=v p &
[1] 3028

apakah ada cara untuk pmengubah konten /proc/3028/environagar tidak menyebutkan k=v saat p masih berjalan?

Cetin Sert
sumber
Apakah Anda mencoba mengedit file?
123
File apa yang Anda tanyakan tentang diri Anda?
ctrl-alt-delor

Jawaban:

12

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=vtelah 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 kvariabel lingkungan dengan setenv()/ putenv(), maka Anda juga harus dapat melakukan sesuatu seperti ini untuk mendapatkan alamat k=vstring 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=ventri yang diterima di lingkungan. Biasanya, hanya ada satu, tetapi tidak ada yang menghentikan siapa pun melewati keduanya k=v1dan k=v2(atau k=vdua 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 bashsebelum 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/environadalah masalah.

Juga catat bahwa bertentangan dengan /proc/pid/cmdline, /proc/pid/environmenthanya 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.

Stéphane Chazelas
sumber
8

Belum perlu menimpa string di atas (tidak benar-benar aktif ) tumpukan utas di Linux sejak 2010.

Keduanya /proc/self/cmdlinedan /proc/self/environdapat 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_ENDatau 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}/cmdlinedan /proc/${PID}/environ, dan karenanya baris perintah dan lingkungan dilaporkan oleh psperintah.

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 setenvdan 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.

Bacaan lebih lanjut

JdeBP
sumber
2
Sejak kernel 3.18, dimungkinkan untuk menggunakan PR_SET_MM_MAP yang mengambil prctl_mm_map struct dan tidak memerlukan root.
filbranden
2
JdeBP, @filbranden Sejak kernel 3.5 Anda dapat membaca nilai saat ini dari env / argv pointer dari /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?
maxschlepzig