est
V2EX  ›  PHP

PHP 探测任意网站密码明文/加密手段办法: md5('240610708') == md5('QNKCDZO')

  •  5
     
  •   est · May 4, 2015 · 28954 views
    This topic created in 4208 days ago, the information mentioned may be changed or developed.
    var_dump(md5('240610708') == md5('QNKCDZO'));
    var_dump(md5('aabg7XSs') == md5('aabC9RqS'));
    var_dump(sha1('aaroZmOk') == sha1('aaK1STfY'));
    var_dump(sha1('aaO8zKZF') == sha1('aa3OFF9m'));
    var_dump('0010e2' == '1e3');
    var_dump('0x1234Ab' == '1193131');
    var_dump('0xABCdef' == ' 0xABCdef');

    感觉这个不科学啊

    https://news.ycombinator.com/item?id=9484757
    79 replies  •  2015-12-08 15:16:31 +08:00
    ywisax
    ywisax
    May 4, 2015❤️2
    1
    观察下最终的hash就明白了,都是0eXXXXXXXX 。
    再加上php是弱语言,会自动判断数据类型。
    零=零,明白了吧?
    anyforever
    anyforever
    May 4, 2015
    2
    md5('240610708'); // 0e462097431906509019562988736854
    md5('QNKCDZO'); // 0e830400451993494058024219903391

    怎么可能这么容易碰撞
    anyforever
    anyforever
    May 4, 2015
    3
    var_dump(md5('240610708') === md5('QNKCDZO'));
    ywisax
    ywisax
    May 4, 2015❤️2
    4
    准确点说,是“==”对比的时候会进行数据转换,0eXXXXXXXXXX 转成0了。
    了解php的“==”和“===“就更加清晰了。
    yangff
    yangff
    May 4, 2015❤️3
    5
    aliang032
    aliang032
    May 4, 2015❤️1
    6
    找了下手册说明贴给大家:

    如果比较一个数字和字符串或者比较涉及到数字内容的字符串,则字符串会被转换为数值并且比较按照数值来进行

    http://php.net/manual/zh/language.operators.comparison.php
    est
    est
    OP
    May 4, 2015
    7
    @ywisax
    @yangff

    脑洞够大!
    xifangczy
    xifangczy
    May 4, 2015
    8
    卧槽 这脑洞。。太赞了
    raincious
    raincious
    May 4, 2015
    9
    这是个坑没错,所以密码比较的时候需要用 ===。但,怎么用它来:

    > PHP 探测任意网站密码明文/加密手段办法

    ?不解。
    est
    est
    OP
    May 4, 2015❤️16
    10
    @raincious 把你的密码设成 0x1234Ab,然后退出登录再登录,换密码 1193131 登录,如果登录成功,那么密码绝对是明文保存的没跑。


    同理,密码设置为 240610708,换密码 QNKCDZO 登录能成功,那么密码没加盐直接md5保存的。
    cat9life
    cat9life
    May 4, 2015
    11
    长见识啊...
    avtester
    avtester
    May 4, 2015
    12
    知道了又能如何?
    bingu
    bingu
    May 4, 2015
    13
    呃,这个略迪奥
    xierch
    xierch
    May 4, 2015
    14
    PHP 的大坑哈哈哈
    tabris17
    tabris17
    May 4, 2015
    15
    能强等的地方我都用强等的
    NeoAtlantis
    NeoAtlantis
    May 4, 2015 via Android❤️1
    16
    散列不叫加密!
    要不谁给我解密看看?XD
    wy315700
    wy315700
    May 4, 2015
    17
    收藏了 ,好牛逼的攻击
    raincious
    raincious
    May 4, 2015
    18
    @est

    恍然大悟,感谢!
    Citrus
    Citrus
    May 4, 2015 via iPhone
    19
    第一次见到如此能唬人的标题党。。。看标题以为楼主完全攻破了 MD5 可以批量找碰撞了。。。
    rwalle
    rwalle
    May 4, 2015
    20
    PHP中的==是挺坑的
    但是PHP中还有很多同样大的坑。。。
    codejay
    codejay
    May 4, 2015
    21
    这不是两个字符串比较吗 为什么要转换成数字
    wy315700
    wy315700
    May 4, 2015
    22
    @codejay php会转义一部分字符串到数字的。。。
    gDD
    gDD
    May 4, 2015 via iPhone
    23
    @est @raincious 不一定,我所有判断都用 === 或者 !== 判断,只有不专业选手用 == 判断才会出你说的脑洞…
    gDD
    gDD
    May 4, 2015 via iPhone
    24
    还有判断密码,secret 等要用 hash_equals(),直接用 === 会出 Timing Attack 漏洞的。
    lincanbin
    lincanbin
    May 4, 2015
    25
    @gDD hash_equals版本要求太高了,只能自己项目用着玩,开源项目用不了。
    gDD
    gDD
    May 4, 2015 via iPhone
    26
    @lincanbin 开源项目我觉得反而更容易解决,composer 一个 symfony security core 就好,里面有 hash_equals 的 shim,反而是公司内没用 composer 管理的的私有项目比较难维护。
    noanti
    noanti
    May 4, 2015
    27
    这是php的feature?
    php还真是最好的语言。。。
    tSQghkfhTtQt9mtd
    tSQghkfhTtQt9mtd
    May 4, 2015 via Android
    28
    password_hash() 路过......
    icanfork
    icanfork
    May 4, 2015 via Android
    29
    md5,加盐,md5,取前30位
    xiaozi
    xiaozi
    May 4, 2015
    30
    这完全是没有 ===,如果是 ===, 根本就没办法检测吧。
    lyf362345
    lyf362345
    May 4, 2015
    31
    @yangff 好大
    lyf362345
    lyf362345
    May 4, 2015
    32
    @est 这个有意思
    est
    est
    OP
    May 4, 2015
    33
    @gDD wordpress专业不?
    invite
    invite
    May 4, 2015
    34
    从另一方面印证了:PHP 是全宇宙最牛逼的语言。
    barbery
    barbery
    May 4, 2015
    35
    官方都建议直接用password_hash加密,用md5还不加salt,我就真的没话说了
    Jaylee
    Jaylee
    May 4, 2015
    36
    搞不懂这个贴子为啥到处都是,做PHP的人基础都这么差? 都没看过手册?
    est
    est
    OP
    May 4, 2015
    37
    @Jaylee 这个bug是最近wordpress爆出来的。。你是想说wordpress的人基础差么。。。
    Kabie
    Kabie
    May 4, 2015
    38
    @est 基础好的人用PHP吗。。。
    kofj
    kofj
    May 4, 2015
    40
    楼主的语法有问题.fix:探测任意php网站密码明文/加密手段办法
    Jaylee
    Jaylee
    May 4, 2015
    41
    @est 不管是不是wordpress基础差,至少我能看出是PHP弱类型导致的。再说,这也不过是一个小bug而已,有必要往这里发?
    bdnet
    bdnet
    May 4, 2015
    42
    除了用 === ,还可以用 strcmp ,返回 0 是相等
    bdnet
    bdnet
    May 5, 2015
    43
    PHP是世界上最好的语言

    2333
    iyaozhen
    iyaozhen
    May 5, 2015❤️1
    44
    @est 不是一般都数据库查询比较吗?SELECT count(*) FROM user WHERE username = '$username' AND password = '$password';
    czb
    czb
    May 5, 2015 via Android
    45
    evlos
    evlos
    May 5, 2015
    46
    看到 #44 我好像突然知道为什么很多网站连密码都有格式限制了。。。
    rwalle
    rwalle
    May 5, 2015
    47
    44楼。。。
    djyde
    djyde
    May 5, 2015
    48
    作为一个 JavaScripter,我表示用 === 是个良好的习惯
    lincanbin
    lincanbin
    May 5, 2015 via Android
    49
    @iyaozhen 你这样写要注入了……
    kfll
    kfll
    May 5, 2015 via iPhone
    50
    @iyaozhen 如果要告诉用户用户名不存在还是密码错误的话,
    hahastudio
    hahastudio
    May 5, 2015
    51
    太可怕
    HN 的这条回复很能说明问题:
    They're comparing two things of the same type: two strings!
    chaucerling
    chaucerling
    May 5, 2015
    52
    语言设计上的坑太多233
    fashioncj
    fashioncj
    May 5, 2015
    53
    作为一个渗透学习者最喜欢44楼这样的程序猿了~
    66beta
    66beta
    May 5, 2015
    54
    44楼处世未深,你们别欺负他
    tuoxie007
    tuoxie007
    May 5, 2015
    55
    PHP是世界上最好的语言 +1
    yxzblue
    yxzblue
    May 5, 2015
    56
    看来dz生成一个随机的salt值进行二次加密再保存,果然是要好一些啊。
    gDD
    gDD
    May 5, 2015
    57
    @est 一开始我以为 (string) == (string) 是绝对以字符串形式做对比的,但是查了 PHP Manual 之后,发现If you compare a number with a string **or the comparison involves numerical strings, then each string is converted to a number and the comparison performed numerically**. 确实也业余了一把,简直宇宙最大坑!xD -- http://php.net/manual/en/language.operators.comparison.php

    说几个相关项目:

    Laravel 4 的时候也爆出过一个更直接的漏洞,直接传入 'true' 就足够。 -- http://barryvdh.nl/laravel/2015/02/21/csrf-protection-in-laravel-explained/

    jQuery 的规范里规定了 Strict equality checks (===) must be used in favor of abstract equality checks (==). The only exception is when checking for undefined and null by way of null. -- https://contribute.jquery.org/style-guide/js/

    而 PHP 的 == 比 JavaScript 的 == 更散漫,本身 PHP 里 null 和 false 就是要用 === 来判断的,这回 OP 的问题一受到重视,连 (string) 也必须强制 === 了。
    zhengkai
    zhengkai
    May 5, 2015❤️2
    58
    上午研究了一下这个问题,根本原因是科学计数法这个 feature

    php > var_dump('0e1' == '0e2');
    bool(true)

    “数字 e 数字”格式的字符串有时被作为数字,有时被作为字符串

    如

    php > echo intval('1e10');
    1
    php > echo '1e10' + 0;
    10000000000

    但是混蛋就混蛋在 (string) == (string) 时也会做这个转换,这就没法搞了(更何况都是字符串还要这么转换,也会降低性能)

    比方说 switch((string)$s) { 的时候就完全没法规避这个问题

    前同事从源码里看到这个判断的函数叫 zendi_smart_strcmp

    下一个补丁应该能修正这个问题(纯字符串比较不做转换),但是这个问题感觉还是挺伤的
    bravecarrot
    bravecarrot
    May 5, 2015 via iPad
    59
    @est 脑洞大,蛮有道理
    iyaozhen
    iyaozhen
    May 5, 2015
    60
    @evlos
    @rwalle
    @lincanbin
    @66beta
    @fashioncj
    汗。。。求说清楚呀,我感觉我老板要开除我了。
    肯定不直接拼sql,我也知道一点 sql 注入,但是一直浮于表面,求指点。
    $password 拼 sql 之前肯定会 $password = sha1/md5($password+$salt) 。
    账号($username)的话一般有限制只能数字或者字母,然后自我安慰 mysql_real_escape_string() 一下(老感觉这个不靠谱)。
    当然这样就只能提示账号或密码错误(不匹配)。

    要看账号是不是存在,SELECT count(*) FROM user WHERE username = '$username'; 这样?看来账号也不能明文存。是不是可以可逆加密或者简单的 base64 编码一下。
    Daniel65536
    Daniel65536
    May 5, 2015 via iPad
    61
    @iyaozhen username= " ' or 1=1 or ' "
    iyaozhen
    iyaozhen
    May 5, 2015
    62
    @Daniel65536 这个我知道,一般账号也只允许字母或数字。而且分号会被mysql_real_escape_string过滤掉吧。

    我的本意是若网站使用 SELECT count(*) FROM user WHERE username = '$username' AND password = '$password'; 这种方式的话“把你的密码设成 0x1234Ab,然后退出登录再登录,换密码 1193131 登录,如果登录成功,那么密码绝对是明文保存的没跑。”就不能说明网站是明文保存呀。
    lincanbin
    lincanbin
    May 5, 2015
    63
    http://www.94cb.com/t/2516

    昨晚睡觉前写了一篇来解释PHP中隐式转换的这个问题。
    lincanbin
    lincanbin
    May 5, 2015
    64
    @iyaozhen 引号过滤然后直接拼接SQL语句是个不推荐的做法了,现在PHP官方建议使用PDO类或者mysqli类来替代mysql系列函数。
    也就是参数绑定,这样做的话,SQL命令与参数会分两次传到MySQL的socket,MySQL根据接收顺序就可以识别出命令和参数,从而避免在参数中构造命令(也就是SQL注入)。
    PDO的话可以参考这个
    https://github.com/lincanbin/PHP-PDO-MySQL-Class
    est
    est
    OP
    May 5, 2015
    65
    @iyaozhen 逻辑老师呢?

    “只要改成A密码用B密码也能登录,说明是该php网站一定是明文保存密码的。”

    反过来一定成立吗?
    iyaozhen
    iyaozhen
    May 5, 2015
    66
    @lincanbin 嗯,谢谢。这个我了解过。产品线上是使用mysqli类或者框架自身的参数绑定的方式。拼 sql 的方式几年前已经交过学费了,不过感觉学费还是交的不够。。。
    iyaozhen
    iyaozhen
    May 5, 2015
    67
    @est 非常抱歉,逻辑错误了。

    还有就是才疏学浅,以为都是使用 sql 直接判断的。
    WKPlus
    WKPlus
    May 5, 2015
    68
    @est
    把你的密码设成 0x1234Ab,然后退出登录再登录,换密码 1193131 登录,如果登录成功,那么密码绝对是明文保存的没跑。

    这句话补充是不是这样:
    1. 把你的密码设成 0x1234Ab,然后退出登录再登录,换密码 1193131 登录,如果登录成功,那么密码绝对是明文保存的且密码对比的时候用的是php的==符号
    2. 但是如果密码比较的时候用的是===,那么就算是明文保存的,上述探测手段也是发现不了的
    est
    est
    OP
    May 5, 2015
    69
    @WKPlus 第一句话也不完全一定。万一别人恰好特别写了 1193131 是万能登录密码呢?

    第二句话理论是这样的。 但是既然都知道用 === 怎么还会用明文密码。。。。。。。
    WKPlus
    WKPlus
    May 5, 2015
    70
    @est 如果1193131是万能密码的话,你“把你的密码设成 0x1234Ab,然后退出登录再登录,换密码 1193131 登录,如果登录成功,那么密码绝对是明文保存的没跑”这句话就不成立了呀

    我的那两句解释只是想把你原来那句话的隐含意思说出来
    est
    est
    OP
    May 5, 2015
    71
    @WKPlus 这不很容易解决么。把密码改成任意密码,如果1193131也能登录,那么说明1193131就是万能密码。


    准确的说,顶楼给出的几个密码对,可以衍生展开,如果尝试几组都符合特征,那么可以得到明文密码/md5无盐加密的结论。
    RemRain
    RemRain
    May 5, 2015
    72
    @gDD 所以说 PHP 密码比较要这样:"X$pass1" == "X$pass2"
    gDD
    gDD
    May 5, 2015
    73
    @RemRain 天……上面的帖子你都白看了吗?最次最次用 "$pass1" === "$pass2" 或者 strcmp(),最好用 hash_equals() 啊。
    123123
    123123
    May 7, 2015
    74
    @gDD 只要最前方加个X就不会转换成数字进行比较,说的也没错就是了,不过加个=不是更方便么。。
    mahone3297
    mahone3297
    May 7, 2015
    75
    这个帖子很赞,学习了。。。
    mingyun
    mingyun
    May 11, 2015
    76
    果真脑洞打开,不过'240610708' ,'QNKCDZO'这种字符是怎么发现的?
    fhefh
    fhefh
    Oct 17, 2015
    77
    学习了 mark~~
    wusuopuBUPT
    wusuopuBUPT
    Oct 17, 2015
    78
    @est 原来如此!
    Kratoshy
    Kratoshy
    Dec 8, 2015
    79
    var_dump(md5('240610708') === md5('QNKCDZO'));
    这个是 false 吧?
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   2437 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 147ms · UTC 12:21 · PVG 20:21 · LAX 04:21 · JFK 07:21
    ♥ Do have faith in what you're doing.