GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Ändern Sie /proc/PID/environ nach dem Prozessstart

Unter Linux können Sie den Wert der Umgebungszeichenfolgen auf dem Stack überschreiben.

Sie können den Eintrag also ausblenden, indem Sie ihn mit Nullen oder irgendetwas anderem überschreiben:

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

Ausführen als:

$ 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

der k=v wurde mit \0\0\0 überschrieben .

Beachten Sie, dass setenv("k", "", 1) den Wert zu überschreiben funktioniert nicht, da in diesem Fall ein neuer "k=" Zeichenfolge zugewiesen.

Wenn Sie den k nicht anderweitig geändert haben Umgebungsvariable mit setenv() /putenv() , dann sollten Sie auch so etwas tun können, um die Adresse der k=v zu erhalten String auf dem Stack (naja, von einem von ihnen):

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

Beachten Sie jedoch, dass nur eine entfernt wird der k=v Einträge in der Umgebung empfangen. Normalerweise gibt es nur einen, aber nichts hindert jemanden daran, beide k=v1 zu bestehen und k=v2 (oder k=v zweimal) in der env-Liste, die an execve() übergeben wird . Dies war in der Vergangenheit die Ursache für Sicherheitslücken wie CVE-2016-2381. Es könnte wirklich mit bash passieren vor Shellshock, wenn sowohl eine Variable als auch eine Funktion mit demselben Namen exportiert werden.

In jedem Fall wird es immer ein kleines Fenster geben, in dem der env var-String noch nicht überschrieben wurde, also möchten Sie vielleicht einen anderen Weg finden, um das Geheimnis zu übergeben Informationen zum Befehl (wie zum Beispiel eine Pipe), wenn sie über /proc/pid/environ verfügbar gemacht werden ist ein Anliegen.

Beachten Sie auch das Gegenteil von /proc/pid/cmdline , /proc/pid/environment ist nur für Prozesse mit der gleichen euid oder root zugänglich (oder root nur, wenn euid und ruid des Prozesses anscheinend nicht gleich sind).

Sie können diesen Wert in /proc/pid/environ vor ihnen verbergen , aber sie können möglicherweise immer noch jede andere Kopie erhalten, die Sie von der Zeichenfolge im Speicher erstellt haben, beispielsweise indem sie einen Debugger daran anhängen.

Unter https://www.kernel.org/doc/Documentation/security/Yama.txt finden Sie Möglichkeiten, zumindest Nicht-Root-Benutzer daran zu hindern.


Es war nicht notwendig, die obigen Zeichenfolgen zu überschreiben (nicht wirklich on ) der Stack des Haupt-Threads unter Linux seit 2010.

Beide /proc/self/cmdline und /proc/self/environ können vom Prozess selbst zur Laufzeit geändert werden, indem prctl() aufgerufen wird Funktion jeweils mit PR_SET_MM_ARG_START +PR_SET_MM_ARG_END oder PR_SET_MM_ENV_START +PR_SET_MM_ENV_END . Diese setzen die Speicherzeiger direkt in den Anwendungsspeicherbereich des Prozesses, der vom Kernel für jeden Prozess gehalten wird, die verwendet werden, um den Inhalt von /proc/${PID}/cmdline abzurufen und /proc/${PID}/environ , und daher die Befehlszeile und Umgebung, die von ps gemeldet werden Befehl.

Man muss also einfach ein neues Argument oder eine neue Umgebungszeichenfolge konstruieren (nicht Vektor, Achtung – der Speicher, auf den gezeigt wird, muss die eigentliche Zeichenfolge sein, verkettet und -delimited) und teilen Sie dem Kernel mit, wo es sich befindet.

Dies ist in der Linux-Handbuchseite für prctl(2) dokumentiert Funktion sowie der environ(7) Handbuchseite. Was ist nicht dokumentiert ist, dass der Kernel jeden Versuch ablehnt, die Startadresse über die Endadresse oder die Endadresse unter die Startadresse zu setzen; oder um eine der Adressen (zurück) auf Null zu setzen. Außerdem ist dies nicht der ursprüngliche Mechanismus, der 2009 von Bryan Donlan vorgeschlagen wurde und der es ermöglichte, Start und Ende in einem einzigen Vorgang atomar festzulegen. Außerdem bietet der Kernel keine Möglichkeit, get die aktuellen Werte dieser Zeiger.

Das macht es schwierig, zu ändern die Umgebungs- und Kommandozeilenbereiche mit prctl() . Man muss die prctl() anrufen Funktion bis zu viermal, da die ersten Versuche dazu führen können, dass versucht wird, den Startzeiger höher als den Endzeiger zu setzen, je nachdem, wo sich die alten und neuen Daten im Speicher befinden. Man muss es weiter nennen viermal, wenn man sicherstellen will, dass dies nicht zu einem Gelegenheitsfenster für andere Prozesse auf dem System führt, um einen beliebigen Bereich des Speicherplatzes des Prozesses in dem Zeitraum zu inspizieren, in dem der neue Start/das neue Ende gesetzt wurde, aber das neue Ende /start war nicht.

Ein einzelner atomarer Systemaufruf, der den gesamten Bereich auf einmal festlegt, wäre für Anwendungsprogramme viel einfacher gewesen, ihn sicher zu verwenden.

Ein weiterer Wermutstropfen ist, dass ohne wirklich triftigen Grund (angesichts der Prüfungen im Kernel) die Überschreibbarkeit der Originaldatenbereiche sowieso , und die Tatsache, dass die Äquivalente auf keinem der BSDs privilegierte Operationen sind), erfordert dies unter Linux Superuser-Privilegien.

Ich habe ziemlich einfach setprocargv() geschrieben und setprocenvv() Funktionen für meine Toolsets, die dies verwenden. Kettenladen von Programmen aus den integrierten Toolsets, wie setenv und foreground , spiegeln also die verketteten Befehlsargumente und die Umgebung wider, wo Linux dies zulässt.

# /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
# 

Beachten Sie, dass dies nicht gegen Dinge spricht, die den Prozess verfolgen und auf andere Weise (statt über diese beiden Pseudodateien) direkt auf seinen Speicher zugreifen, und natürlich ein Fenster hinterlässt, bevor die Zeichenfolgen geändert werden, in dem diese Informationen nur angezeigt werden können wie das Überschreiben der Daten über dem Stack des Haupt-Threads. Und genau wie beim Überschreiben der Daten berücksichtigt dies keine Sprachlaufzeitbibliotheken, die unter verschiedenen Umständen Kopien der Umgebung (auf dem Heap) erstellen. Betrachten Sie dies im Allgemeinen nicht als einen so guten Mechanismus, um "Geheimnisse" an ein Programm zu übergeben, wie (sagen wir), es einen offenen Dateideskriptor an das Leseende einer unbenannten Pipe erben zu lassen, der vollständig unter Ihrer Kontrolle in einen Eingabepuffer eingelesen wird die Sie dann löschen.

Weiterführende Literatur

  • Timo Sirainen (2009-10-02). Option PR_SET_PROCTITLE_AREA für prctl() hinzugefügt . Linux-Kernel-Mailingliste.
  • https://unix.stackexchange.com/a/432681/5132
  • Daniel J. Bernstein. Das checkpassword-Interface . cr.yp.to.
  • https://github.com/jdebp/nosh/blob/master/source/setprocargv.cpp
  • https://github.com/jdebp/nosh/blob/master/source/setprocenvv.cpp

Linux
  1. Linux – Wie liest man unter Linux aus /proc/$pid/mem?

  2. Linux – /proc/pid/environ nach Prozessstart ändern?

  3. Linux – Informationen über die Speichernutzung eines Prozesses von /proc/pid/smaps abrufen?

  4. /proc/[pid]/pagemaps und /proc/[pid]/maps | Linux

  5. Wie finde ich heraus, aus welchem ​​Ordner ein Prozess läuft?

Eine Anleitung zum Dateisystem „/proc“ unter Linux

/proc/cpuinfo- und /proc/meminfo-Dateien unter Linux

Die Dateien /proc/mounts, /etc/mtab und /proc/partitions verstehen

Wann sollte ich /dev/shm/ verwenden und wann sollte ich /tmp/?

Warum ändert sich MemTotal in /proc/meminfo?

Sollten Websites gemäß der empfohlenen Verwendung in /var/ oder /usr/ leben?