Python print environment variable memory address
是否可以打印我的环境变量内存地址?
使用
我想用我的python脚本获取这个地址,然后做我的技巧,包括我的shellcode。
我试过(用Python):
1 2 3 4 5 6 | print hex(id(os.environ["ENVVAR"])) print memoryview(os.environ["ENVVAR"]) # output : # 0xb7b205c0L # <memory at 0xb7b4dd9c> |
与露比:
1 2 3 | puts (ENV['PATH'].object_id << 1).to_s(16) # output : # -4836c38c |
如果有人有主意,用python或ruby。
cpython内置函数id()为任何对象返回一个唯一的id,它不是内存地址,而是尽可能接近的地址。
例如,我们有变量x。id(x)不返回变量x的内存地址,而是返回x指向的对象的内存地址。
"变量"和"内存对象"之间有严格的分隔。在标准实现中,python为虚拟机分配一组本地变量和一个堆栈以供操作。所有本地槽都是不相交的,因此如果将对象从本地槽X加载到堆栈并修改该对象,则X槽的"位置"不会更改。
http://docs.python.org/library/functions.html_id
我想您可以使用
1 2 3 4 5 6 7 8 | import ctypes libc = ctypes.CDLL("libc.so.6") getenv = libc.getenv getenv.restype = ctypes.c_voidp print('%08x' % getenv('PATH')) |
至少在Python中,这似乎是一项不可能完成的任务。从这个问题上考虑到以下几点:
- ASLR将使这完全不可能
- 每个二进制文件都有它自己的开销,不同的argv,因此,唯一可靠的选择是执行二进制文件并跟踪它的内存,直到找到我们要查找的环境变量为止。基本上,即使我们可以在Python进程中找到环境地址,它在您试图利用的二进制文件中的位置也会不同。
回答这个问题的最佳方法是使用http://python3-pwntools.readthedocs.io/en/latest/elf.html,它在一个coredump文件中很容易找到地址。
请记住,系统环境变量不是可以通过其内存地址访问的对象。每个进程,如运行脚本的python或ruby进程,都将收到自己的环境副本。这就是为什么Python和Ruby解释器返回的结果如此不同的原因。
如果要修改系统环境变量,应使用编程语言提供的API。请看这个或那个关于python解决方案的帖子。
感谢@mickal9,我编写了一个函数来计算程序中环境变量的地址:
1 2 3 4 5 6 7 8 9 | 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 |
例如:
1 2 3 4 5 | user@host:~$ ./getenvaddr.elf PATH /bin/ls PATH will be at 0xbfffff22 in /bin/ls user@host:~$ python getenvaddr.py PATH /bin/ls PATH will be at 0xbfffff22 in /bin/ls user@host:~$ |
注意:此功能仅在Linux系统中工作。
The getenv() function is inherently not reentrant because it returns a value pointing to static data.
In fact, for higher performance of getenv(), the implementation could also maintain a separate copy of the environment in a data structure that could be searched much more quickly (such as an indexed hash table, or a binary tree), and update both it and the linear list at environ when setenv() or unsetenv() is invoked.
因此,getenv返回的地址不一定来自环境。
进程内存布局;
http://static.duartes.org/img/blogposts/linuxflexibleddressspacelayout.png
http://d1gjlxt8vb0knt.cloudfront.net//wp-content/uploads/memory-layout-300x255.gif
内存映射
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | 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: \tstart:\t0x{} \tend:\t0x{} \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: \tstart:\t0x{} \tend:\t0x{} \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('') |
输出;
1 2 3 4 5 6 7 8 9 10 11 12 | 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 |
环境在堆栈之上。所以它的地址应该高于堆栈。但
这是检查对象在内存中的位置的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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')) |
输出;
1 2 | 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 |
上面的输出中匿名的是什么?
It is also possible to create an anonymous memory mapping that does not correspond to any files, being used instead for program data. In Linux, if you request a large block of memory via malloc(), the C library will create such an anonymous mapping instead of using heap memory. ‘Large’ means larger than MMAP_THRESHOLD bytes, 128 kB by default and adjustable via mallopt().
记忆中程序的解剖
因此,
在Ruby中,这是可能的——这篇文章涵盖了一般情况:访问Ruby中的对象内存地址….?"通过获取对象ID并向左按位移动,可以获得对象的实际指针值。"
1 2 | puts (ENV['RAILS_ENV'].object_id << 1).to_s(16) > 7f84598a8d58 |