【问题标题】:Python print environment variable memory addressPython打印环境变量内存地址
【发布时间】:2016-10-10 02:32:07
【问题描述】:

是否可以打印我的环境变量内存地址?

使用gdb-peda,我的内存地址看起来像0xbffffcd6searchmem,我知道这是正确的形式。 (0xbfff????) 但 gdb 使用其他环境变量移动了堆栈。

我想用我的 python 脚本来获取这个地址,然后做我的伎俩并包含我的 shellcode。

我尝试过(使用 Python):

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

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

使用红宝石:

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

如果有人有想法,用 python 或 ruby​​。

【问题讨论】:

  • 我很想知道更多关于需要/这样做的原因。诀窍是什么?您要包含的 shell 代码是什么?为什么shell代码需要环境变量的内存地址。等
  • @ScottS。感谢您的关注,这是一个简单的 BSS 溢出漏洞利用,我知道我可以使用另一种解决方案,我找到了在 C 中获取它的方法,但是使用 python 访问和使用我的脚本要容易得多。要获取我的环境变量地址,请将其连接到我的 shellcode 并做我所有的事情。我开始看gdb-peda 的代码来搜索它是如何工作的。
  • @eki-al 你能发送一个指向 C 语言示例的链接吗?
  • @eki-al 可能正在使用ctypes 加载libc,并且可以选择从Python 调用C getenv。在此处查看ctypes 上的文档:docs.python.org/2/library/ctypes.html

标签: python ruby memory environment-variables


【解决方案1】:

getenv() 函数本质上是不可重入的,因为它返回一个指向静态数据的值。

事实上,为了 getenv() 的更高性能,实现还可以在可以更快搜索的数据结构(例如索引哈希表或二叉树)中维护环境的单独副本,并在调用 setenv() 或 unsetenv() 时更新它和 environ 的线性列表。

所以getenv返回的地址不一定来自环境。

进程内存布局;


(来源:duartes.org


(来源:cloudfront.net

内存映射

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('')

输出;

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

环境在堆栈之上。所以它的地址应该高于堆栈。但是id 显示的地址不在堆栈中,不在堆中,也不在堆栈之上。真的是地址吗?或者我的计算是错误的!

这是检查对象在内存中的位置的代码。

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'))

输出;

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

以上输出中的匿名是什么?

也可以创建不对应任何文件的匿名内存映射,用于程序数据。在 Linux 中,如果您通过 malloc() 请求大块内存,C 库将创建这样一个匿名映射,而不是使用堆内存。 “大”表示大于 MMAP_THRESHOLD 字节,默认为 128 kB,可通过 mallopt() 进行调整。

Anatomy of a Program in Memory

所以os.environ['PATH'] 位于malloced 区域中。

【讨论】:

    【解决方案2】:

    感谢@mickael9,我写了一个函数来计算程序中环境变量的地址:

    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
    

    例如:

    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系统。

    【讨论】:

      【解决方案3】:

      我想您可以使用ctypes 模块直接调用本机getenv 来做到这一点:

      import ctypes
      
      libc = ctypes.CDLL("libc.so.6")
      
      getenv = libc.getenv
      getenv.restype = ctypes.c_voidp
      
      print('%08x' % getenv('PATH'))
      

      【讨论】:

        【解决方案4】:

        至少在 python 中,这似乎是一项不可能完成的任务。 这个问题有几件事需要考虑:

        • ASLR 会让这完全不可能
        • 每个二进制文件都有自己的开销,不同的 argv,因此,唯一可靠的选择是执行二进制文件并跟踪它的内存,直到找到我们正在寻找的环境变量。基本上,即使我们可以在 python 进程中找到环境地址,它也会位于您尝试利用的二进制文件中的不同位置。

        最适合回答这个问题的方法是使用http://python3-pwntools.readthedocs.io/en/latest/elf.html,它会获取一个容易找到地址的核心转储文件。

        【讨论】:

        • 谢谢,很好的回答,刚刚发现 ASLR,但是如果我没有设置 ASLR,它会使用堆栈保护标志编译吗?
        • 不确定是否理解您的评论。 ASLR 是系统范围的保护,它没有在二进制文件中配置。您可以执行cat /proc/sys/kernel/randomize_va_space 来查看它是否已启用(值 > 0)。否则,您还拥有 PIE 保护,可以改变 ENV 在内存中的位置。没有其他保护应该在其他地方。尽管大多数二进制文件都使用 NX 保护,并且堆栈被标记为不可执行,因此在这种情况下,您将无法在 env 中放置 shellcode。
        • 堆栈中有 ENV 吗?
        • ENV 在 Linux 上总是在堆栈中。堆栈布局如下,从最高地址开始:[内核保留空间(进程看不到)] [ENV] [程序参数] [程序堆栈]。
        【解决方案5】:

        请记住,系统环境变量不是您可以通过其内存地址访问的对象。每个进程,例如运行脚本的 Python 或 Ruby 进程,都会收到自己的环境副本。这就是 Python 和 Ruby 解释器返回的结果如此不同的原因。

        如果您想修改系统环境变量,您应该使用您的编程语言提供的 API。 有关 Python 解决方案,请参阅 thisthat 帖子。

        【讨论】:

          【解决方案6】:

          cpython 内置函数 id() 为任何对象返回一个唯一的 id,这并不完全是它的内存地址,而是尽可能接近。

          例如,我们有变量 x。 id(x) 不返回变量x的内存地址,而是返回x指向的对象的内存地址。

          “变量”和“内存对象”之间有严格的区分。在标准实现中,python 分配了一组本地变量和一个堆栈供虚拟机操作。所有本地插槽都是不相交的,因此如果您将本地插槽 x 中的对象加载到堆栈上并修改该对象,则 x 插槽的“位置”不会改变。

          http://docs.python.org/library/functions.html#id

          【讨论】:

          • 感谢您的精彩解释,但我认为这可能是访问内存地址的另一种方式,我快速浏览了peda github,对于searchmem 函数,它是这样的处理,这个周末我会测试:github.com/longld/peda/blob/…
          • CPython implementation detail:这是对象在内存中的地址。
          【解决方案7】:

          在 ruby​​ 中这是可能的 - 这篇文章涵盖了一般情况: Accessing objects memory address in ruby..? "你可以通过获取对象id,然后按位向左移动来获取对象的实际指针值"

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

          【讨论】:

          • 它不工作,我得到一个负值-4836c38c
          猜你喜欢
          • 2013-04-30
          • 1970-01-01
          • 1970-01-01
          • 2014-07-26
          • 1970-01-01
          • 2019-07-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多