V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
guyeuro
V2EX  ›  问与答

黑客是如何利用所谓的“进程内存转储”偷密码的?

  •  
  •   guyeuro · 2017-06-24 18:16:45 +08:00 · 3129 次点击
    这是一个创建于 2704 天前的主题,其中的信息可能已经有所发展或是发生改变。

    网上说密码要用 char[]而不是 String 储存的原因是:

    虽然 String 加载密码之后可以把这个变量扔掉,但是字符串并不会马上被 GC 回收,一但进程在 GC 执行到这个字符串之前被 dump,dump 出的的转储中就会含有这个明文的字符串。

    这个黑客是如何利用所谓的“进程内存转储”偷密码啊? 感觉不可思议啊

    27 条回复    2017-06-25 13:38:15 +08:00
    nevin47
        1
    nevin47  
       2017-06-24 19:02:43 +08:00 via Android
    看这意思是趁 GC 之前构造一个场景把进程 core 掉,然后 dump 文件里面去找明文密码?不过感觉没啥软用啊,难道是去套软件内私钥的?这时代谁不把私钥先加个密啊
    wevsty
        2
    wevsty  
       2017-06-24 19:45:04 +08:00   ❤️ 1
    string 的本质是帮你在堆上创建了空间储存字符串的,string 自己析构的时候是把空间还给了内存分配器,但是可能没有对空间填充内容(这取决于 string 的实现),并且内存分配器也可能没有对这些空间做任何操作,所以析构以后 dump 内存仍然有可能找到密码。
    你用 malloc 分配或者在栈上的数组也会有一样的问题这取决于有关的实现本身。
    关键问题不在于你用的到底是什么,而是使用完成后对于需要保密的数据应该手动覆盖填充有关的空间。
    ech0x
        3
    ech0x  
       2017-06-24 20:04:19 +08:00 via iPad
    @wevsty 请教一下,java char[] 的实现不是直接分配一块固定的内存的吗?如果是的话,用 char[] 不是照样可以被获取嘛。
    geelaw
        4
    geelaw  
       2017-06-24 20:09:23 +08:00
    实际上可以直接用 Process Explorer 查看进程里面长得像字符串的东西,这也是可能看到密码的。


    @ech0x 但是 char[] 是可变的,因此你可以用完之后手动填充为空白。这样做是为了保证用完之后内存里不再有这个串。

    然而很糟糕的是,对于复制 GC 这一招还是会被攻击。


    @nevin47 如果没有解密私钥的密码怎么解密私钥……?而且要用的时候仍然需要把私钥解密。
    ech0x
        5
    ech0x  
       2017-06-24 20:15:14 +08:00 via iPad
    @geelaw 谢谢了解了。 这么看来这个攻击方式几乎是无解了的嘛。对方只要能获取你的进程基本上就可以被攻击。如果像 c 用指针数组,密码被分散在内存的各处,能不能防范这个攻击呢?
    geelaw
        6
    geelaw  
       2017-06-24 20:18:12 +08:00   ❤️ 1
    @ech0x 不能,只要你的程序能做的,能控制你的程序的程序都能做。
    nevin47
        7
    nevin47  
       2017-06-24 20:21:42 +08:00 via Android
    @geelaw 私钥可以拆分后再做二次加密,虽然理论可以继续追着去破译,但是这种破译成本已经成指数扩大了
    geelaw
        8
    geelaw  
       2017-06-24 20:31:15 +08:00 via iPhone
    @nevin47 你的想法有很多值得二次思考的地方……比如为什么再次加密后破解难度指数增加?用于二次加密的密码存放在哪里?如果也在进程内存,只要读出二次加密的密码再解密私钥密文不就行了?
    gamexg
        9
    gamexg  
       2017-06-24 20:32:06 +08:00
    @nevin47 #7 除非自己实现加密库,不然传递给加密库时必须时解密后的,一样存在被拦截的可能。
    wevsty
        10
    wevsty  
       2017-06-24 20:57:51 +08:00   ❤️ 1
    @ech0x
    推荐使用 char[]我觉得是一种偏见。
    大概会有人认为使用 char[]的话在不再需要密钥之后手动填充这段内存空间会比较方便,覆盖了原本密钥的内容那么就不用担心在这个时刻之后会被人内存 dump 了。
    但是实际上,string 和 char[]没有什么不同的地方,可以在析构之前手动填充一下每个元素,甚至可以继承一个自己的 string 然后在析构函数里面强制填充。
    geelaw
        11
    geelaw  
       2017-06-24 21:09:53 +08:00
    @wevsty String 是不可变,且 String 是 final,因此这条路走不通。

    而且一个程序不可以依赖 finaliser 来做清理,因为在内存足够的硬件上,GC 可以选择永远不回收内存。
    billlee
        12
    billlee  
       2017-06-24 21:17:59 +08:00
    这和 coredump 有什么关系,如果黑客能读取到 coredump, 也就能直接读你的进程内存,或者给你的进程上调试器。
    binux
        13
    binux  
       2017-06-24 21:21:18 +08:00
    如果黑客能接触到你的内存,他干点别的什么不好?
    wevsty
        14
    wevsty  
       2017-06-24 21:26:44 +08:00
    @geelaw
    您说得是 JAVA 么?这方面我不太清楚。
    从 C++的角度上,string 是可以被继承的,当一个对象的生命周期结束的时候,析构函数必然会被调用。当然重点不在这里,而在于不再需要密钥的时候密钥是否被正确销毁,不管会不会回收内存,只要用完了正确的覆盖掉原始数据就可以了。
    owt5008137
        15
    owt5008137  
       2017-06-24 21:30:33 +08:00 via Android
    啥语言? c 艹的话 char[]被删除了也不一定回立刻被回收
    sinxccc
        16
    sinxccc  
       2017-06-24 21:33:26 +08:00   ❤️ 2
    从安全的角度,能被对方物理接触到和远程登录进去的系统是毫无安全性可言的,一旦到那个地步,唯一能做的就是自毁关键数据和销毁相关的密钥,证书等等。在这个情况下还在比较 char [] 和 string 的安全性就好像关心一个重病垂死的人吃东西太少会不会对胃不好一样。

    但总有一些例外比如之前的 heartbleed 漏洞,还有一个常见的情况就是从机器上传递出来的 coredump 文件,这些可能会带出一部分内存的情况。针对这种一般是用加密一部分堆的方式来解决,愿意了解细节的话可以搜搜 Akamai secure heap 和 heartbleed 的故事,很是一波三折。
    gamexg
        17
    gamexg  
       2017-06-24 21:44:57 +08:00
    看到后面才知道说的时 java,已开始以为时 c,c 无所谓,强制转换然后置 0 即可。
    java 不熟悉,看起来没有 SecureString,如果考虑到这个的话的确需要用 char[] 。
    geelaw
        18
    geelaw  
       2017-06-24 21:50:10 +08:00
    @wevsty 最开始有 GC,所以假定不是自己管理内存 - - 如果是 C/C++ 那就没这个问题了,毕竟可以自己控制。
    guyeuro
        19
    guyeuro  
    OP
       2017-06-24 21:58:31 +08:00
    @sinxccc

    对啊
    所以我问的就是,密码要用 char[]而不是 String 储存有啥意义?黑客能接触到内存还去内存上找密码明文?
    lrxiao
        20
    lrxiao  
       2017-06-24 22:06:55 +08:00
    看 SERT C++指导有一条注意跨越可信边界时候注意 padding bits,感觉和这个案例差不多
    nevin47
        21
    nevin47  
       2017-06-24 22:29:28 +08:00 via Android
    @geelaw 核心那个密钥拆分成片,使用时不做拼接,实时连续读取
    @gamexg
    是的,如果最后传递给加密库,那确实成了一个连续的了,所以有时候考虑自己去定制实现加密算法
    guyeuro
        22
    guyeuro  
    OP
       2017-06-24 23:06:50 +08:00
    @wevsty 我问的不是为何要用 char[],而是说就算密码明文在内存里
    黑客怎么拿到的?
    lcdtyph
        23
    lcdtyph  
       2017-06-24 23:23:30 +08:00 via iPhone
    @guyeuro
    OpenProcess ReadProcessMemory
    可以远程遍历完整性级别比你低的进程的内存
    xenme
        24
    xenme  
       2017-06-24 23:44:37 +08:00 via iPhone
    默认情况下不管是 char[]还是 string 在内存中对于你要这种短小的内容来说基本都是连续分布,没有特别保护。。
    所以攻击的时候很容易通过遍历内存来攻击,也就是直接 dump 后扫一遍可能就扫出来了。。
    而一般的处理是对于这些敏感的内容使用流式处理,使用过程中经过处理,使用后立即清理来尽可能的减少明文存在的时间。

    但是你只要输入或者输出需要使用到明文,那么必然会有一个时刻可以看到明文,如果此时 dump 出来,那么依然可以被攻击。
    mengskysama
        25
    mengskysama  
       2017-06-25 01:21:23 +08:00
    @guyeuro 黑客既然能 dump 内存基本上应该就是有很高的权限了,有些工具更甚,密码常驻内存
    感兴趣可以看看这个 https://github.com/huntergregal/mimipenguin
    guyeuro
        26
    guyeuro  
    OP
       2017-06-25 10:34:12 +08:00
    @xenme 那么黑客是如何远程遍历服务器内存的?
    xenme
        27
    xenme  
       2017-06-25 13:38:15 +08:00 via iPhone
    @guyeuro 还要远程的话基本就是漏洞了啊
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1040 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 32ms · UTC 22:32 · PVG 06:32 · LAX 14:32 · JFK 17:32
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.