thay đổi / Proc / PID / môi trường sau khi quá trình bắt đầu


Câu trả lời:


12

Trên Linux, bạn có thể ghi đè giá trị của các chuỗi môi trường trên ngăn xếp.

Vì vậy, bạn có thể ẩn mục nhập bằng cách ghi đè nó bằng số không hoặc bất cứ thứ gì khác:

#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;
}

Chạy như:

$ 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

các k=vđã được ghi đè bằng \0\0\0.

Lưu ý rằng setenv("k", "", 1)để ghi đè giá trị sẽ không hoạt động như trong trường hợp đó, một "k="chuỗi mới được phân bổ.

Nếu bạn không sửa đổi biến kmôi trường bằng setenv()/ putenv(), thì bạn cũng có thể làm một cái gì đó như thế này để lấy địa chỉ của k=vchuỗi trên ngăn xếp (tốt, của một trong số chúng):

#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;
}

Tuy nhiên, lưu ý rằng nó chỉ xóa một trong các k=vmục nhận được trong môi trường. Thông thường, chỉ có một, nhưng không có gì ngăn cản bất cứ ai vượt qua cả hai k=v1k=v2(hoặc k=vhai lần) trong danh sách env được chuyển đến execve(). Đó là nguyên nhân của các lỗ hổng bảo mật trong quá khứ như CVE-2016-2381 . Nó thực sự có thể xảy ra với bashtrước shellshock khi xuất cả biến và hàm cùng tên.

Trong mọi trường hợp, sẽ luôn có một cửa sổ nhỏ trong đó chuỗi var env chưa bị ghi đè, vì vậy bạn có thể muốn tìm một cách khác để truyền thông tin bí mật cho lệnh (ví dụ như đường ống) nếu để lộ thông qua /proc/pid/environlà một mối quan tâm

Cũng lưu ý rằng trái với /proc/pid/cmdline, /proc/pid/environmentchỉ có thể truy cập được bởi các quy trình có cùng euid hoặc root (hoặc chỉ root nếu euid và ruid của quá trình không giống nhau).

Bạn có thể ẩn giá trị đó với chúng /proc/pid/environ, nhưng chúng vẫn có thể lấy bất kỳ bản sao nào khác mà bạn đã tạo chuỗi trong bộ nhớ, ví dụ bằng cách đính kèm trình gỡ lỗi vào nó.

Xem https://www.kernel.org/doc/Documentation/security/Yama.txt để biết cách ngăn chặn ít nhất người dùng không phải root thực hiện điều đó.


8

Không cần thiết phải ghi đè các chuỗi ở trên (không thực sự bật ) ngăn xếp của luồng chính trên Linux kể từ năm 2010.

Cả hai /proc/self/cmdline/proc/self/environlà sửa đổi bởi quá trình tự lúc chạy, do dint gọi các prctl()chức năng tương ứng với PR_SET_MM_ARG_START+ PR_SET_MM_ARG_ENDhoặc PR_SET_MM_ENV_START+ PR_SET_MM_ENV_END. Chúng trực tiếp đặt các con trỏ bộ nhớ vào không gian bộ nhớ ứng dụng của tiến trình, được giữ bởi kernel cho mọi tiến trình, được sử dụng để truy xuất nội dung của /proc/${PID}/cmdline/proc/${PID}/environ, do đó dòng lệnh và môi trường được báo cáo bởi pslệnh.

Vì vậy, người ta chỉ cần xây dựng một đối số hoặc chuỗi môi trường mới (không phải vectơ, thông báo - bộ nhớ được trỏ phải là dữ liệu chuỗi thực tế, được nối và phân tách) và cho hạt nhân biết nó ở đâu.

Điều này được ghi lại trong trang hướng dẫn Linux cho prctl(2)chức năng cũng như environ(7)trang hướng dẫn. Điều không được ghi lại là kernel từ chối mọi nỗ lực đặt địa chỉ bắt đầu bên trên địa chỉ kết thúc hoặc địa chỉ kết thúc bên dưới địa chỉ bắt đầu; hoặc thành (re-) đặt địa chỉ thành 0. Ngoài ra, đây không phải là cơ chế ban đầu được đề xuất bởi Bryan Donlan vào năm 2009, cho phép thiết lập bắt đầu và kết thúc trong một hoạt động, về mặt nguyên tử. Hơn nữa, kernel cung cấp không có cách nào để có được các giá trị hiện tại của các con trỏ này.

Điều này làm cho nó khó khăn để sửa đổi môi trường và các khu vực dòng lệnh với prctl(). Người ta phải gọi prctl()hàm này tới bốn lần vì những lần thử đầu tiên có thể dẫn đến các nỗ lực đặt con trỏ bắt đầu cao hơn con trỏ cuối, tùy thuộc vào nơi dữ liệu cũ và mới nằm trong bộ nhớ. Người ta phải gọi nó thêm bốn lần nữa nếu muốn đảm bảo rằng điều này không dẫn đến cơ hội cho các quá trình khác trên hệ thống kiểm tra phạm vi tùy ý của không gian bộ nhớ trong quá trình khi bắt đầu / kết thúc mới đã được thiết lập nhưng kết thúc / bắt đầu mới chưa được.

Một cuộc gọi hệ thống nguyên tử duy nhất đặt toàn bộ phạm vi trong một lần sẽ giúp các chương trình ứng dụng sử dụng một cách an toàn dễ dàng hơn nhiều.

Một nếp nhăn nữa là, vì không có lý do thực sự tốt (do các kiểm tra trong nhân, dù sao thì khả năng ghi đè của các vùng dữ liệu gốc và thực tế là các tương đương không phải là hoạt động đặc quyền trên bất kỳ BSD nào), trên Linux, điều này đòi hỏi siêu người dùng đặc quyền.

Tôi đã viết khá đơn giản setprocargv()và các setprocenvv()chức năng cho bộ công cụ của tôi, sử dụng điều này. Các chương trình tải chuỗi từ các bộ công cụ được xây dựng, như setenvforeground, do đó phản ánh các đối số và môi trường được xâu chuỗi, nơi Linux cho phép.

# / gói / quản trị viên / nosh / lệnh / Clearenv setenv WIBBLE lắc lư tạm dừng tiền cảnh \; thật &
[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 |.;. Đúng. |
00000018
# hexdump -C / Proc / 1057 / môi trường
00000000 57 49 42 42 4c 45 3d 77 6f 62 62 6c 65 00 | WIBBLE = lắc lư. |
0000000e
# hexdump -C / Proc / 1058 / cmdline
00000000 70 61 75 73 65 00 | tạm dừng. |
00000006
# hexdump -C / Proc / 1058 / môi trường
00000000 57 49 42 42 4c 45 3d 77 6f 62 62 6c 65 00 | WIBBLE = lắc lư. |
0000000e
# 

Lưu ý rằng điều này không chống lại những thứ theo dõi quá trình và truy cập trực tiếp vào bộ nhớ của nó bằng các phương tiện khác (chứ không phải thông qua hai tệp giả này), và tất nhiên để lại một cửa sổ trước khi các chuỗi được sửa đổi, nơi có thể nhìn thấy thông tin này như ghi đè dữ liệu lên trên ngăn xếp của luồng chính. Và cũng giống như trường hợp ghi đè dữ liệu, điều này không tính đến các thư viện thời gian chạy ngôn ngữ tạo ra các bản sao của môi trường (trên heap) trong các trường hợp khác nhau. Nói chung, đừng coi đây là một cơ chế tốt để truyền "bí mật" cho một chương trình vì nó nói rằng nó thừa hưởng một bộ mô tả tệp mở vào đầu đọc của một ống không tên, đọc vào bộ đệm đầu vào dưới sự kiểm soát của bạn mà sau đó bạn lau.

đọc thêm


2
Vì kernel 3.18, có thể sử dụng PR_SET_MM_MAP, có cấu trúc prctl_mm_map và không yêu cầu root.
filbranden

2
JdeBP, @filbranden Vì kernel 3.5, bạn có thể đọc các giá trị hiện tại của con trỏ env / argv từ /proc/$pid/stat(bên cạnh các giá trị khác bạn có thể cần struct prctl_mm_map). Xem thêm ví dụ về bộ lọc của tôi để biết một bản demo nhỏ. JdeBP, bạn có thể thêm liên kết đến setprocargv()/ setprocenvv()chức năng của mình không?
maxschlepzig
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.