Muszę ukryć niektóre wrażliwe argumenty w programie, który uruchamiam, ale nie mam dostępu do kodu źródłowego. Używam tego również na serwerze współdzielonym, więc nie mogę używać czegoś takiego, hidepid
ponieważ nie mam uprawnień sudo.
Oto kilka rzeczy, które próbowałem:
export SECRET=[my arguments]
, a następnie wezwanie do./program $SECRET
, ale to nie pomaga../program `cat secret.txt`
gdziesecret.txt
zawiera moje argumenty, ale Wszechmocnyps
jest w stanie odkryć moje sekrety.
Czy jest jakiś inny sposób na ukrycie moich argumentów, który nie wymaga interwencji administratora?
ps
nie robi nic magicznego, aby „wykopać swoje sekrety”. W każdym razie rozsądnie napisane programy powinny zamiast tego oferować opcję wiersza polecenia do odczytywania sekretu z określonego pliku lub ze standardowego wejścia zamiast bezpośredniego przyjmowania go jako argumentu.Odpowiedzi:
Jak wyjaśniono tutaj , Linux umieszcza argumenty programu w przestrzeni danych programu i utrzymuje wskaźnik na początku tego obszaru. To jest używane przez
ps
i tak dalej, aby znaleźć i pokazać argumenty programu.Ponieważ dane znajdują się w przestrzeni programu, można nimi manipulować. Wykonanie tego bez zmiany samego programu wymaga załadowania podkładki z
main()
funkcją, która zostanie wywołana przed prawdziwą funkcją główną programu. Ta podkładka może skopiować prawdziwe argumenty na nowe miejsce, a następnie zastąpić oryginalne argumenty, abyps
po prostu zobaczyć null.Robi to następujący kod C.
Nie można interweniować
main()
, ale można interweniować w standardową funkcję biblioteki C__libc_start_main
, która następnie wywołuje main. Skompiluj ten plikshim_main.c
zgodnie z uwagą na początku i uruchom go, jak pokazano. Zostawiłamprintf
w kodzie, więc sprawdzasz, czy faktycznie jest wywoływany. Na przykład uruchomnastępnie wykonaj a,
ps
a zobaczysz puste polecenie i argumenty.Nadal jest mało czasu, aby argumenty poleceń mogły być widoczne. Aby tego uniknąć, możesz na przykład zmienić podkładkę, aby odczytać swój sekret z pliku i dodać go do argumentów przekazanych do programu.
źródło
/proc/pid/cmdline
pokaże sekret (tak samo, jak gdycurl
próbuje ukryć hasło podane w wierszu poleceń). Podczas korzystania z LD_PRELOAD możesz owinąć main, aby sekret został skopiowany ze środowiska do argv, który otrzymuje main. Jak wezwanieLD_PRELOAD=x SECRET=y cmd
gdzie dzwoniszmain()
zargv[]
bycia[argv[0], getenv("SECRET")]
/proc/pid/environ
. Może to być nadpisywalne w taki sam sposób jak argumenty, ale pozostawia to samo okno./proc/pid/cmdline
jest publiczny,/proc/pid/environ
nie jest. Było kilka systemów, w którychps
(plik wykonywalny setuid) eksponował środowisko dowolnego procesu, ale nie sądzę, żebyś mógł je teraz spotkać. Środowisko jest ogólnie uważane za wystarczająco bezpieczne . Nie można bezpiecznie wścibić się w procesy z tym samym euidem, ale i tak często potrafią odczytać pamięć procesów przez tego samego euida, więc niewiele można na to poradzić.main
metody opakowanego programu, usuwa również zmienną środowiskową, aby uniknąć przypadkowego wycieku do procesów potomnych. Alternatywnie opakowanie może odczytać wszystkie argumenty wiersza polecenia z pliku.Przeczytaj dokumentację interfejsu wiersza poleceń danej aplikacji. Może istnieć opcja podania tajnego klucza z pliku zamiast bezpośrednio jako argumentu.
Jeśli to się nie powiedzie, zgłoś zgłoszenie błędu do aplikacji, uzasadniając to tym, że nie ma bezpiecznego sposobu na podanie jej tajemnicy.
Zawsze możesz ostrożnie (!) Dostosować rozwiązanie w odpowiedzi Meuh do swoich konkretnych potrzeb. Zwróć szczególną uwagę na komentarz Stéphane'a i dalsze działania.
źródło
Jeśli potrzebujesz przekazać argumenty do programu, aby działał, nie będziesz miał szczęścia, bez względu na to, co zrobisz, jeśli nie będziesz mógł używać go
hidepid
w procfs.Ponieważ wspomniałeś, że jest to skrypt bash, powinieneś już mieć dostępny kod źródłowy, ponieważ bash nie jest językiem kompilowanym.
Jeżeli to niemożliwe, to może być w stanie przepisać CMDLINE procesu korzystania
gdb
lub podobne i zabawy zargc
/argv
gdy to już się rozpoczęła, ale:Naprawdę po prostu zaleciłbym uzyskanie kodu źródłowego lub rozmowę z dostawcą w celu zmodyfikowania kodu. Podawanie sekretów w wierszu poleceń w systemie operacyjnym POSIX jest niezgodne z bezpieczną operacją.
źródło
Gdy proces wykonuje polecenie (poprzez
execve()
wywołanie systemowe), jego pamięć jest czyszczona. Aby przekazać pewne informacje w trakcie wykonywania,execve()
wywołania systemowe wymagają dwóch argumentów:argv[]
ienvp[]
tablic.Są to dwie tablice ciągów:
argv[]
zawiera argumentyenvp[]
zawiera definicje zmiennych środowiskowych jako ciągi znaków wvar=value
formacie (zgodnie z konwencją).Kiedy to zrobisz:
(tutaj dodano brakujące cudzysłowy wokół rozszerzenia parametru).
Wykonujesz
cmd
z sekretem (value
) przekazanym zarówno do, jakargv[]
i doenvp[]
.argv[]
będzie["cmd", "value"]
ienvp[]
coś w tym rodzaju[..., "PATH=/bin:...", "HOME=...", ..., "SECRET=value", "TERM=xterm", ...]
. Ponieważcmd
nie robi się nic,getenv("SECRET")
ani nie można pobrać wartości tajnej z tejSECRET
zmiennej środowiskowej, umieszczenie jej w środowisku nie jest przydatne.argv[]
to wiedza publiczna. Pokazuje na wyjściups
.envp[]
obecnie nie jest. W systemie Linux pokazuje się w/proc/pid/environ
. Pokazuje się wps ewww
wynikach BSD (i procps-ng wps
Linuksie), ale tylko dla procesów działających z tym samym efektywnym UID (i z większymi ograniczeniami dla plików wykonywalnych setuid / setgid). Może być wyświetlany w niektórych dziennikach kontroli, ale te dzienniki kontroli powinny być dostępne tylko dla administratorów.Krótko mówiąc, środowisko przekazywane do pliku wykonywalnego ma być prywatne lub przynajmniej tak prywatne jak wewnętrzna pamięć procesu (do którego w niektórych okolicznościach inny proces z odpowiednimi uprawnieniami może również uzyskać dostęp za pomocą debuggera i może również zrzucony na dysk).
Ponieważ
argv[]
jest to wiedza publiczna, polecenie, które oczekuje, że dane, które mają być tajne w wierszu poleceń, jest zepsute przez projekt.Zazwyczaj polecenia, które wymagają tajnego klucza, zapewniają inny interfejs, np. Poprzez zmienną środowiskową. Na przykład:
Lub za pomocą dedykowanego deskryptora pliku, takiego jak stdin:
(
echo
jest wbudowany, nie wyświetla się w wynikachps
)Lub plik, taki jak
.netrc
forftp
i kilka innych poleceń lubNiektóre aplikacje, takie jak
curl
(i takie podejście przyjęła tutaj @meuh ), próbują ukryć hasło, które otrzymaliargv[]
przed wścibskimi oczami (w niektórych systemach, nadpisując część pamięci, w którejargv[]
przechowywano ciągi znaków). Ale to naprawdę nie pomaga i daje fałszywą obietnicę bezpieczeństwa. To pozostawia okno pomiędzyexecve()
i nadpisywaniem, gdzieps
nadal będzie pokazywany sekret.Na przykład, jeśli osoba atakująca wie, że uruchamiasz skrypt wykonujący
curl -u user:somesecret https://...
(na przykład zadanie crona), wszystko, co musi zrobić, to eksmitować z pamięci podręcznej (wiele) bibliotek, którecurl
używają (na przykład uruchamiając ash -c 'a=a;while :; do a=$a$a;done'
), więc aby spowolnić jego uruchomienie, a nawet zrobienie bardzo nieefektywnegountil grep 'curl.*[-]u' /proc/*/cmdline; do :; done
wystarczy, aby złapać to hasło w moich testach.Jeśli argumenty to jedyny sposób na przekazanie sekretu komendom, nadal możesz spróbować kilku rzeczy.
W niektórych systemach, w tym starszych wersjach Linuksa,
argv[]
można przeszukiwać tylko kilka pierwszych bajtów (4096 w Linuksie 4.1 i wcześniejszych) ciągów .Tam możesz zrobić:
I sekret zostałby ukryty, ponieważ przekroczyłby pierwsze 4096 bajtów. Teraz ludzie, którzy używali tej metody, muszą teraz tego żałować, ponieważ Linux od wersji 4.2 nie obcina już listy argumentów
/proc/pid/cmdline
. Zauważ też, że nie dlatego,ps
że nie będzie wyświetlał więcej niż tak wielu bajtów linii poleceń (jak na FreeBSD, gdzie wydaje się być ograniczony do 2048), których nie można użyć do tego samego interfejsu API,ps
aby uzyskać więcej. Podejście to jest jednak ważne w systemach, w którychps
zwykły użytkownik jest jedynym sposobem na uzyskanie tych informacji (na przykład, gdy interfejs API jest uprzywilejowany ips
jest ustawiony jako setgid lub setuid, aby z niego skorzystać), ale wciąż nie jest tam potencjalnie zabezpieczony na przyszłość.Innym podejściem byłoby nie przekazywanie sekretu,
argv[]
ale wstrzykiwanie kodu do programu (za pomocągdb
lub$LD_PRELOAD
włamania) przed jegomain()
uruchomieniem, które wstawia sekret doargv[]
otrzymanego zexecve()
.Z
LD_PRELOAD
, dla dynamicznie połączonych plików wykonywalnych innych niż setuid / setgid w systemie GNU:Następnie:
W żadnym momencie nie
ps
pokazałbym tegops -opid,args
(co-opid,args
jest tajemnicą w tym przykładzie). Zauważ, że zastępujemy elementyargv[]
tablicy wskaźników , nie zastępując ciągów wskazanych przez te wskaźniki, dlatego nasze modyfikacje nie pokazują się na wyjściups
.Z
gdb
, wciąż dla dynamicznie powiązanych plików wykonywalnych innych niż setuid / setgid oraz w systemach GNU:Mimo
gdb
to podejście nie specyficzne dla GNU, które nie polega na dynamicznym łączeniu plików wykonywalnych lub symboli debugowania, i powinno działać przynajmniej dla każdego pliku wykonywalnego ELF w systemie Linux:Testowanie ze statycznie połączonym plikiem wykonywalnym:
Gdy plik wykonywalny może być statyczny, nie mamy niezawodnego sposobu na przydzielenie pamięci do przechowywania sekretu, więc musimy uzyskać sekret z innego miejsca, które jest już w pamięci procesu. Właśnie dlatego środowisko jest tutaj oczywistym wyborem. Ukrywamy również
SECRET
zmienną env var procesu (zmieniając ją naSECRE=
), aby uniknąć wycieku, jeśli proces z jakiegoś powodu zdecyduje się zrzucić swoje środowisko lub uruchomić niezaufane aplikacje.Który działa również w systemie Solaris 11 (pod warunkiem, gdb i binutils GNU są zainstalowane (być może trzeba będzie zmienić nazwę
objdump
nagobjdump
).W FreeBSD (przynajmniej x86_64 nie jestem pewien, jakie są te pierwsze 24 bajty (które stają się 16, gdy gdb (8.0.1) jest interaktywny, co sugeruje, że może tam być błąd w gdb) na stosie), zamień
argc
iargv
definicje z:(może być również konieczne zainstalowanie
gdb
pakietu / portu, ponieważ wersja dostarczana z systemem jest starodawna).źródło
Co możesz zrobić, to
następnie, zakładając, że piszesz
./program
w C (lub robi to ktoś inny i możesz go zmienić lub ulepszyć), użyj getenv (3) w tym programie, być może jakoa po tym
export
biegniesz./program
w tej samej powłoce. Lub nazwa zmiennej środowiskowej może być do niej przekazana (uruchamiając./program --secret-var=SECRET
etc ...)ps
nie zdradzi twojego sekretu, ale proc (5) wciąż może przekazać wiele informacji (przynajmniej innym procesom tego samego użytkownika).Zobacz także to, aby pomóc zaprojektować lepszy sposób przekazywania argumentów programu.
Zobacz tę odpowiedź, aby uzyskać lepsze wyjaśnienie na temat globowania i roli powłoki.
Być może twój
program
inne sposoby pozyskiwania danych (lub mądrzejszego korzystania z komunikacji międzyprocesowej ) niż zwykłe argumenty programu (z pewnością powinno, jeśli jest przeznaczone do przetwarzania poufnych informacji). Przeczytaj dokumentację. A może nadużywasz tego programu (który nie jest przeznaczony do przetwarzania tajnych danych).Ukrywanie tajnych danych jest naprawdę trudne. Nie przekazanie go przez argumenty programu nie wystarczy.
źródło
./program
, więc pierwsza połowa tej odpowiedzi nie wydają się być istotne.