2016-06-09 17 views
10

Czy można wydrukować adres pamięci zmiennej środowiska?Python środowisko drukowania zmienna adres pamięci

Z Mam adres pamięci wyglądający jak 0xbffffcd6 z searchmem i wiem, że to jest właściwa forma. (0xbfff????), ale gdb przeniósł stos z inną zmienną środowiskową.

Chciałbym za pomocą mojego skryptu Pythona uzyskać ten adres, a następnie zrobić moją sztuczkę i dołączyć mój kod powłoki.

próbowałem (z Python):

print hex(id(os.environ["ENVVAR"])) 
print memoryview(os.environ["ENVVAR"]) 

# output : 
# 0xb7b205c0L 
# <memory at 0xb7b4dd9c> 

z Ruby:

puts (ENV['PATH'].object_id << 1).to_s(16) 
# output : 
# -4836c38c 

Jeśli ktoś ma pomysł, z Python lub Ruby.

+0

Chciałbym dowiedzieć się więcej o przyczynie tego/potrzebującego. Jaka jest sztuczka? Jaki jest kod powłoki, który chcesz dołączyć? Dlaczego kod powłoki wymaga adresu pamięci zmiennej środowiskowej. itd. –

+0

@ScottS. Dzięki za zainteresowanie, to jest dla prostego exploita przepełnienia BSS, wiem, że mógłbym użyć innego rozwiązania, znalazłem sposób, aby uzyskać go w C, ale jest o wiele łatwiejszy dostęp i praca z moim skryptem z pythonem. Aby uzyskać adres zmiennej środowiskowej, połącz ją z moim kodem powłoki i wykonaj wszystkie moje czynności. Zaczynam obserwować kod 'gdb-peda', szukając jak to działa. –

+0

@ eki-al Czy możesz wysłać łącze do przykładu w C? – VirtualVDX

Odpowiedz

4

Funkcja wbudowana cpython id() zwraca unikalny identyfikator dla dowolnego obiektu, który nie jest dokładnie jego adresem pamięci, ale jest tak blisko, jak to możliwe.

Na przykład mamy zmienną x. id (x) nie zwraca adresu pamięci zmiennej x, a raczej zwraca adres pamięci obiektu, na który wskazuje x.

Istnieje ścisły podział na "zmienne" i "obiekty pamięci". W standardowej implementacji python przydziela zestaw locali i stos, na którym maszyna wirtualna może działać. Wszystkie lokalne sloty są rozłączne, więc jeśli załadujesz obiekt z lokalnego slotu x na stos i zmodyfikujesz ten obiekt, "lokalizacja" gniazda X nie zmieni się.

enter image description here http://docs.python.org/library/functions.html#id

+0

Dziękuję za to miłe wyjaśnienie, ale myślę, że może to być inny sposób dostępu do adresu pamięci, szybko rzuciłem okiem na 'peda' github, dla funkcji' searchmem' to jest to, jak sobie poradzi, będę testować w ten weekend: https://github.com/longld/ peda/blob/b7c7d7aeeba65a467fe982787b4f72e017774905/peda.py # L1889 –

+0

[Detekcja wdrożenia CPython] (https://docs.python.org/3/library/functions.html#id) l: To jest adres obiektu w pamięci. –

-1

W Ruby jest to możliwe - Ten post dotyczy przypadku ogólnym: Accessing objects memory address in ruby..? „Można uzyskać rzeczywistą wartość wskaźnika obiektu poprzez identyfikator obiektu i robi przesunięcie bitowe w lewo”

puts (ENV['RAILS_ENV'].object_id << 1).to_s(16) 
> 7f84598a8d58 
+2

To nie działa, otrzymuję wartość ujemną' -4836c38c' –

2

Proszę pamiętać, że zmienna system nie jest obiektem, można uzyskać dostęp za pomocą swojego adresu pamięci. Każdy proces, taki jak proces Pythona lub Ruby uruchamiający twój skrypt, otrzyma własną kopię środowiska. To dlatego wyniki zwracane przez interpreterów Python i Ruby są tak różne.

Jeśli chcesz zmodyfikować zmienną środowiskową systemu, powinieneś użyć API dostarczonego przez twój język programowania. Zobacz post this lub that dla rozwiązania Python.

3

To wydaje się niemożliwe, przynajmniej w python. Istnieje kilka rzeczy, aby zwrócić uwagę od tego pytania:

  • ASLR stałaby to całkowicie niemożliwe
  • Każdy binarny może mieć swoją własną głową, inny argv, więc jedynym wiarygodnym rozwiązaniem jest wykonanie binarnego i śledź jego pamięć, dopóki nie znajdziemy zmiennej środowiskowej, której szukamy. Zasadniczo, nawet jeśli uda nam się znaleźć adres środowiska w procesie Pythona, znajdzie się on na innej pozycji w systemie binarnym, który próbujesz wykorzystać.

Najlepszym rozwiązaniem, aby odpowiedzieć na to pytanie, jest skorzystanie z http://python3-pwntools.readthedocs.io/en/latest/elf.html, który pobiera plik coredump, w którym łatwo jest znaleźć adres.

+0

Dziękuję, dobra odpowiedź, po prostu odkryj ASLR, ale jeśli nie mam zestawu ASLR, jest on skompilowany z flagą stosu obroży? –

+0

Nie rozumiem Twojego komentarza. ASLR jest zabezpieczeniem systemowym, nie jest skonfigurowany w systemie binarnym. Możesz zrobić polecenie 'cat/proc/sys/kernel/randomize_va_space', aby sprawdzić, czy jest włączone (wartość> 0). W przeciwnym razie masz również ochronę PIE, która może przetasować położenie ENV w pamięci. Żadna inna ochrona nie powinna sprawić, że będzie w innych miejscach. Większość plików binarnych korzysta jednak z ochrony NX, a stos jest oznaczony jako niepoprawny, więc w tym przypadku nie będzie można umieścić kodu powłoki w env. – laxa

+0

to ENV w stosie? –

3

Przypuszczam, że można to zrobić za pomocą modułu ctypes zadzwonić rodzimą getenv bezpośrednio:

import ctypes 

libc = ctypes.CDLL("libc.so.6") 

getenv = libc.getenv 
getenv.restype = ctypes.c_voidp 

print('%08x' % getenv('PATH')) 
1

getenv() funkcja nie jest z natury reentrant ponieważ zwraca wartość wskazuje na statycznej danych .

W rzeczywistości, dla większej wydajności getenv(), implementacja może również zachować oddzielną kopię środowiska w strukturze danych, która może być przeszukiwana znacznie szybciej (np. Indeksowana tablica asocjacyjna lub drzewo binarne) i zaktualizuj zarówno go, jak i listę liniową w environ, gdy wywoływana jest setenv() lub unsetenv().

Tak więc adres zwracany przez getenv niekoniecznie musi pochodzić ze środowiska.

Układ pamięci procesu;

http://static.duartes.org/img/blogPosts/linuxFlexibleAddressSpaceLayout.png

http://d1gjlxt8vb0knt.cloudfront.net//wp-content/uploads/Memory-Layout-300x255.gif

mapa pamięci

import os 

def mem_map(): 
    path_hex = hex(id(os.getenv('PATH'))).rstrip('L') 
    path_address = int(path_hex, 16) 
    for line in open('/proc/self/maps'): 
     if 'stack' in line: 
      line = line.split() 
      first, second = line[0].split('-') 
      first, second = int(first, 16), int(second, 16) 
      #stack grows towards lower memory address 
      start, end = max(first, second), min(first, second) 
      print('stack:\n\tstart:\t0x{}\n\tend:\t0x{}\n\tsize:\t{}'.format(start, end, start - end)) 
      if path_address in range(end, start+1): 
       print('\tgetenv("PATH") ({}) is in the stack'.format(path_hex)) 
      else: 
       print('\tgetenv("PATH") ({}) is not in the stack'.format(path_hex)) 
      if path_address > start: 
       print('\tgetenv("PATH") ({}) is above the stack'.format(path_hex)) 
      else: 
       print('\tgetenv("PATH") ({}) is not above the stack'.format(path_hex)) 
      print('') 
      continue 
     if 'heap' in line: 
      line = line.split() 
      first, second = line[0].split('-') 
      first, second = int(first, 16), int(second, 16) 
      #heap grows towards higher memory address 
      start, end = min(first, second), max(first, second) 
      print('heap:\n\tstart:\t0x{}\n\tend:\t0x{}\n\tsize:\t{}'.format(start, end, end - start)) 
      if path_address in range(start, end+1): 
       print('\tgetenv("PATH") ({}) in the heap'.format(path_hex)) 
      else: 
       print('\tgetenv("PATH") ({}) is not in the heap'.format(path_hex)) 
      print('') 

wyjścia;

heap: 
     start: 0x170364928 
     end: 0x170930176 
     size: 565248 
     getenv("PATH") (0xb74d2330) is not in the heap 

stack: 
     start: 0x0xbffa8000L 
     end: 0x0xbff86000L 
     size: 139264 
     getenv("PATH") (0xb74d2330) is not in the stack 
     getenv("PATH") (0xb74d2330) is not above the stack 

Środowisko znajduje się powyżej stosu. Więc jego adres powinien być wyższy niż stos. Ale pokazuje, że adres nie jest w stosie, a nie w stercie, a nie powyżej stosu. Czy to naprawdę adres? albo moje obliczenia są błędne!

Oto kod, aby sprawdzić, gdzie znajduje się obiekt w pamięci.

def where_in_mem(obj): 
    maps = {} 
    for line in open('/proc/self/maps'): 
     line = line.split() 
     start, end = line[0].split('-') 

     key = line[-1] if line[-1] != '0' else 'anonymous' 
     maps.setdefault(key, []).append((int(start, 16), int(end, 16))) 

    for key, pair in maps.items(): 
     for start, end in pair: 
      # stack starts at higher memory address and grows towards lower memory address 
      if 'stack' in key: 
       if start >= id(obj) >= end: 
        print('Object "{}" ({}) in the range {} - {}, mapped to {}'.format(obj, hex(id(obj)), hex(start), hex(end), key)) 
        continue 
      if start <= id(obj) <= end: 
       print('Object "{}" ({}) in the range {} - {}, mapped to {}'.format(obj, hex(id(obj)), hex(start), hex(end), key)) 

where_in_mem(1) 
where_in_mem(os.getenv('PATH')) 

Wyjście;

Object "1" (0xa17f8b0) in the range 0xa173000 - 0xa1fd000, mapped to [heap] 
Object "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games" (0xb74a1330L) in the range 0xb7414000L - 0xb74d6000L, mapped to anonymous 

Co anonimowy w powyższym wyjście?

Możliwe jest również, aby utworzyć odwzorowanie anonimowy pamięci, który nie odpowiada żadnemu plików, używany jest zamiast danych programowych. W Linuksie, jeśli zażądasz dużego bloku pamięci za pośrednictwem funkcji malloc(), biblioteka C utworzy takie anonimowe mapowanie zamiast korzystania z pamięci sterty. "Duże" oznacza większe niż MMAP_THRESHOLD bajty, domyślnie 128 kB i można je regulować za pomocą mallopt().

Anatomy of a Program in Memory

Więc os.environ['PATH'] jest w regionie malloc ed.

1

Dzięki za @ mickael9, mam napisane funkcję do obliczania adresu zmiennej środowiskowej w programie:

def getEnvAddr(envName, ELFfile): 
    import ctypes 
    libc = ctypes.CDLL('libc.so.6') 
    getenv = libc.getenv 
    getenv.restype = ctypes.c_voidp 

    ptr = getenv(envName) 
    ptr += (len('/usr/bin/python') - len(ELFfile)) * 2 
    return ptr 

Na przykład:

[email protected]:~$ ./getenvaddr.elf PATH /bin/ls 
PATH will be at 0xbfffff22 in /bin/ls 
[email protected]:~$ python getenvaddr.py PATH /bin/ls 
PATH will be at 0xbfffff22 in /bin/ls 
[email protected]:~$ 

Note: tylko ta funkcja działa w systemie Linux.