change / proc / PID / environment dopo l'avvio del processo


Risposte:


12

Su Linux, è possibile sovrascrivere il valore delle stringhe di ambiente nello stack.

Quindi puoi nascondere la voce sovrascrivendola con zeri o qualsiasi altra cosa:

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

Correre come:

$ 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

il k=vè stato sovrascritto con \0\0\0.

Si noti che setenv("k", "", 1)per sovrascrivere il valore non funzionerà come in quel caso, "k="viene allocata una nuova stringa.

Se non hai altrimenti modificato la kvariabile d'ambiente con setenv()/ putenv(), dovresti anche essere in grado di fare qualcosa del genere per ottenere l'indirizzo della k=vstringa nello stack (beh, di una di esse):

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

Si noti tuttavia che rimuove solo una delle k=vvoci ricevute nell'ambiente. Di solito, ce n'è solo uno, ma nulla impedisce a nessuno di passare entrambi k=v1e k=v2(o k=vdue volte) nell'elenco env passato execve(). Questa è stata la causa delle vulnerabilità della sicurezza in passato come CVE-2016-2381 . Potrebbe realmente accadere con bashprima dello shellshock quando si esportano sia una variabile che una funzione con lo stesso nome.

In ogni caso, ci sarà sempre una piccola finestra durante la quale la stringa var env non è stata ancora ignorata, quindi potresti voler trovare un altro modo per passare le informazioni segrete al comando (come una pipe per esempio) se esponendole tramite /proc/pid/environè una preoccupazione.

Si noti inoltre che /proc/pid/cmdline, al contrario , /proc/pid/environmentè accessibile solo da processi con lo stesso euid o root (o root solo se l'euid e il ruid del processo non sono gli stessi sembrerebbero).

Puoi nascondere quel valore da loro in /proc/pid/environ, ma potrebbero essere ancora in grado di ottenere qualsiasi altra copia che hai fatto della stringa in memoria, ad esempio collegando un debugger ad esso.

Vedi https://www.kernel.org/doc/Documentation/security/Yama.txt per i modi per impedire almeno agli utenti non root di farlo.


8

Dal 2010 non è stato necessario sovrascrivere le stringhe sopra (non proprio su ) lo stack del thread principale su Linux.

Entrambi /proc/self/cmdlinee /proc/self/environsono modificabili dal processo stesso in fase di esecuzione, a forza di chiamare la prctl()funzione rispettivamente con PR_SET_MM_ARG_START+ PR_SET_MM_ARG_ENDo PR_SET_MM_ENV_START+ PR_SET_MM_ENV_END. Questi settano direttamente i puntatori di memoria nello spazio di memoria dell'applicazione del processo, trattenuto dal kernel per ogni processo, che sono usati per recuperare il contenuto di /proc/${PID}/cmdlinee /proc/${PID}/environ, quindi, la riga di comando e l'ambiente riportati dal pscomando.

Quindi bisogna semplicemente costruire un nuovo argomento o una stringa di ambiente (non un vettore, un avviso: la memoria indicata deve essere i dati della stringa effettiva, concatenati e delimitati) e dire al kernel dove si trova.

Questo è documentato nella pagina del manuale di Linux per la prctl(2)funzione e nella environ(7)pagina del manuale. Ciò che non è documentato è che il kernel rifiuta qualsiasi tentativo di impostare l'indirizzo iniziale sopra l'indirizzo finale, o l'indirizzo finale sotto l'indirizzo iniziale; o per (ri) impostare l'indirizzo su zero. Inoltre, questo non è il meccanismo originale proposto da Bryan Donlan nel 2009, che ha permesso di impostare l'inizio e la fine in un'unica operazione, atomicamente. Inoltre, il kernel non fornisce alcun modo per ottenere i valori correnti di questi puntatori.

Ciò rende difficile modificare l'ambiente e le aree della riga di comando con prctl(). È necessario chiamare la prctl()funzione fino a quattro volte perché i primi tentativi possono comportare tentativi di impostare il puntatore iniziale più in alto rispetto al puntatore finale, a seconda di dove si trovano i vecchi e nuovi dati nella memoria. Bisogna chiamarlo altre quattro volte se si vuole garantire che ciò non si traduca in una finestra di opportunità per altri processi sul sistema di ispezionare un intervallo arbitrario dello spazio di memoria del processo nel periodo in cui il nuovo inizio / fine è stato impostato ma il nuovo end / start non lo è stato.

Una singola chiamata al sistema atomico che imposta l'intero intervallo in una volta sarebbe stata molto più semplice da utilizzare in modo sicuro per i programmi applicativi.

Un ulteriore antirughe è che, per nessuna buona ragione (visti i controlli nel kernel, l'overwritability delle aree di dati originali comunque , e il fatto che gli equivalenti non sono operazioni privilegiate su uno dei sistemi BSD), su Linux questo richiede superutente privilegi.

Ho scritto abbastanza semplici setprocargv()e setprocenvv()funzioni per i miei set di strumenti, che lo utilizzano. Programmi di caricamento a catena dai set di strumenti che sono incorporati, come setenve foreground, quindi, riflettono gli argomenti e l'ambiente concatenati per comandare, dove Linux consente.

# / package / admin / nosh / command / clearenv setenv WIBBLE wobble foreground pause \; vero &
[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 / ambi
00000000 57 49 42 42 4c 45 3d 77 6f 62 62 6c 65 00 | WIBBLE = oscillazione. |
0000000e
# hexdump -C / proc / 1058 / cmdline
00000000 70 61 75 73 65 00 | pausa. |
00000006
# hexdump -C / proc / 1058 / ambi
00000000 57 49 42 42 4c 45 3d 77 6f 62 62 6c 65 00 | WIBBLE = oscillazione. |
0000000e
# 

Nota che questo non milita contro cose che tracciano il processo e accedono alla sua memoria direttamente con altri mezzi (piuttosto che attraverso questi due pseudo-file), e ovviamente lascia una finestra prima che le stringhe vengano modificate dove queste informazioni possono essere visualizzate, solo come sovrascrive i dati sopra lo stack del thread principale. E proprio come nel caso della sovrascrittura dei dati, ciò non tiene conto delle librerie di runtime linguistico che eseguono copie dell'ambiente (sull'heap) in varie circostanze. In generale, non considerare questo come un buon meccanismo per passare "segreti" a un programma come (diciamo) avere ereditato un descrittore di file aperto all'estremità di lettura di una pipe senza nome, letto in un buffer di input interamente sotto il tuo controllo che poi cancelli.

Ulteriori letture


2
Dal kernel 3.18, è possibile usare PR_SET_MM_MAP che accetta una struct prctl_mm_map e non richiede root.
filbranden,

2
JdeBP, @filbranden Dal kernel 3.5 è possibile leggere i valori correnti della ENV / argv puntatori da /proc/$pid/stat(oltre ad altri valori che potrebbe essere necessario in struct prctl_mm_map). Vedi anche il mio esempio filter_env.c per una piccola demo. JdeBP, puoi aggiungere collegamenti alle tue setprocargv()/ setprocenvv()funzioni?
maxschlepzig,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.