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

11
$ k=v p &
[1] 3028

Gibt es eine Möglichkeit p, den Inhalt von zu ändern, /proc/3028/environum nicht zu erwähnen, k=v während p noch läuft?

Cetin Sert
quelle
Haben Sie versucht, die Datei zu bearbeiten?
123
Nach welcher Datei fragst du dich?
Strg-Alt-Delor

Antworten:

12

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

Sie können den Eintrag also ausblenden, indem Sie ihn mit Nullen oder etwas 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;
}

Rennen wie:

$ 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

das k=vwurde überschrieben mit \0\0\0.

Beachten Sie, dass setenv("k", "", 1)das Überschreiben des Werts nicht funktioniert. In diesem Fall wird eine neue "k="Zeichenfolge zugewiesen.

Wenn Sie die kUmgebungsvariable nicht anderweitig mit setenv()/ geändert haben putenv(), sollten Sie auch in der Lage sein, die Adresse der k=vZeichenfolge auf dem Stapel (also einer von ihnen) abzurufen:

#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 einer der k=vin der Umgebung empfangenen Einträge entfernt wird . Normalerweise gibt es nur einen, aber nichts hindert jemanden daran, beide k=v1und k=v2(oder k=vzweimal) die an übergebene env-Liste zu übergeben execve(). Dies war in der Vergangenheit die Ursache für Sicherheitslücken wie CVE-2016-2381 . Es könnte tatsächlich bashvor dem Shellshock passieren , 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 die Zeichenfolge env var noch nicht überschrieben wurde. Daher möchten Sie möglicherweise einen anderen Weg finden, um die geheimen Informationen an den Befehl zu übergeben (z. B. eine Pipe), wenn Sie sie über verfügbar machen /proc/pid/environist ein Anliegen.

Beachten Sie auch , dass im Gegensatz zu /proc/pid/cmdline, /proc/pid/environmentdurch Prozesse mit demselben euid oder root nur zugänglich ist (oder root nur , wenn die euid und ruid des Prozesses nicht das Gleiche sind wie es scheint).

Sie können diesen Wert vor ihnen verbergen /proc/pid/environ, aber sie können möglicherweise weiterhin jede andere Kopie der Zeichenfolge im Speicher abrufen, indem Sie beispielsweise einen Debugger an sie anhängen.

Unter https://www.kernel.org/doc/Documentation/security/Yama.txt finden Sie Möglichkeiten, um zu verhindern, dass zumindest Nicht-Root-Benutzer dies tun.

Stéphane Chazelas
quelle
8

Seit 2010 war es nicht erforderlich, die Zeichenfolgen über (nicht wirklich auf ) dem Stack des Hauptthreads unter Linux zu überschreiben .

Beide /proc/self/cmdlineund können /proc/self/environvom Prozess selbst zur Laufzeit geändert werden, indem die prctl()Funktion mit PR_SET_MM_ARG_START+ PR_SET_MM_ARG_ENDbzw. PR_SET_MM_ENV_START+ aufgerufen wird PR_SET_MM_ENV_END. Diese setzen die Speicherzeiger direkt in den Anwendungsspeicherbereich des Prozesses, der vom Kernel für jeden Prozess gehalten wird und zum Abrufen des Inhalts von /proc/${PID}/cmdlineund /proc/${PID}/environund damit der vom Befehl gemeldeten Befehlszeile und Umgebung verwendet wird ps.

Man muss also einfach ein neues Argument oder eine neue Umgebungszeichenfolge erstellen (kein Vektor, beachten Sie - der Speicher, auf den verwiesen wird, muss die tatsächlichen Zeichenfolgendaten sein, verkettet und begrenzt) und dem Kernel mitteilen, wo er sich befindet.

Dies ist in der Linux-Handbuchseite für die prctl(2)Funktion sowie auf der environ(7)Handbuchseite dokumentiert. Was nicht dokumentiert ist, ist, dass der Kernel jeden Versuch ablehnt, die Startadresse über der Endadresse oder die Endadresse unter der Startadresse festzulegen. oder um eine der Adressen auf Null (zurück) zu setzen. Dies ist auch nicht der ursprüngliche Mechanismus, den Bryan Donlan 2009 vorgeschlagen hatte und der es ermöglichte, Start und Ende in einer einzigen Operation atomar festzulegen. Darüber hinaus bietet der Kernel keine Möglichkeit , die aktuellen Werte dieser Zeiger abzurufen.

Dies macht es schwierig, die Umgebung und die Befehlszeilenbereiche mit zu ändernprctl() . Die prctl()Funktion muss bis zu vier Mal aufgerufen werden, da die ersten Versuche dazu führen können, dass der Startzeiger höher als der Endzeiger gesetzt wird, je nachdem, wo sich die alten und neuen Daten im Speicher befinden. Man muss es noch vier Mal aufrufen, wenn man sicherstellen will, dass andere Prozesse im System nicht die Möglichkeit haben, einen beliebigen Bereich des Speicherplatzes des Prozesses in der Zeit des neuen Starts / Endes zu untersuchen wurde gesetzt, aber das neue Ende / Start wurde nicht gesetzt.

Ein einziger Aufruf eines atomaren Systems, der den gesamten Bereich auf einmal festlegt, wäre für Anwendungsprogramme weitaus einfacher zu verwenden gewesen.

Eine weitere Falten ist , dass, ohne wirklich guten Grund (die Kontrollen im Kernel gegeben, die Überschreibbarkeit der ursprünglichen Datenbereiche sowieso , und die Tatsache , dass die Mittel nicht privilegierte Operationen auf einem der BSDs), auf Linux erfordert diese Super - User Privilegien.

Ich habe ziemlich einfach setprocargv()und setprocenvv()Funktionen für meine Toolsets geschrieben, die dies verwenden. Kettenladeprogramme aus den eingebauten Toolsets wie setenvund foregroundspiegeln somit die verketteten Befehlsargumente und die Umgebung wider, sofern Linux dies zulässt.

# / package / admin / nosh / command / clearenv setenv WIBBLE Wobble Vordergrundpause \; wahr &
[1] 1057
# hexdump -C / proc / 1057 / cmdline
00000000 66 6f 72 65 67 72 6f 75 6e 64 00 70 61 75 73 65 | Vordergrundpause |
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 direkt auf seinen Speicher zugreifen (anstatt über diese beiden Pseudodateien), und natürlich ein Fenster verlässt, bevor die Zeichenfolgen geändert werden, in dem diese Informationen nur angezeigt werden wie das Überschreiben der Daten über dem Stapel des Hauptthreads. Und genau wie beim Überschreiben der Daten werden hier keine Sprachlaufzeitbibliotheken berücksichtigt, die unter verschiedenen Umständen Kopien der Umgebung (auf dem Heap) erstellen. Betrachten Sie dies im Allgemeinen nicht als einen so guten Mechanismus zum Übergeben von "Geheimnissen" an ein Programm, wie wenn Sie beispielsweise einen offenen Dateideskriptor an das Leseende einer unbenannten Pipe erben, der vollständig unter Ihrer Kontrolle in einen Eingabepuffer eingelesen wird dass du dann abwischst.

Weiterführende Literatur

JdeBP
quelle
2
Seit Kernel 3.18 ist es möglich, PR_SET_MM_MAP zu verwenden, das eine Struktur prctl_mm_map verwendet und kein Root benötigt.
Filbranden
2
JdeBP, @filbranden Seit Kernel 3.5 können Sie die aktuellen Werte der env / argv-Zeiger von lesen /proc/$pid/stat(neben anderen Werten, die Sie möglicherweise benötigen struct prctl_mm_map). Siehe auch mein Beispiel filter_env.c für eine kleine Demo. JdeBP, können Sie Links zu Ihren setprocargv()/ setprocenvv()Funktionen hinzufügen ?
Maxschlepzig