md5(unix)原理分析

看到t00ls上有同学在问这个问题: https://www.t00ls.net/thread-31914-1-1.html
里面有说到通过注入拿到网站的密码,加密方式是md5(unix),破解不了于是很尴尬。我们通过他文中给出的hash入手,来分析一下unix(md5)的原理与破解方法。

目标hash:
$1$Dx1bONFt$Hsrx102ek28d03B5dqgAv/

实际上,我们要先明白一点。无论是何种哈希,说到底是摘要算法,是将任意长度的任意字节对应成固定长度的一段字节。
这段摘要字节因为包含很多不易显示的字符,所以人们通常使用hex或者base64等类似方法将它转换成可见字符显示出来。
所以这个hash也一样,我们用$将hash切割成三部分:”1“、”Dx1bONFt“、”Hsrx102ek28d03B5dqgAv/“ ,给这三部分分别起个名字:magic、salt、password。
其中password实际上就是哈希完成后的字符串,再通过类似base64的算法转换成了可见字符串。

Magic

magic是表明这一段哈希是通过什么算法得到的,对应关系如下:

$0 = DES
$1 = MD5
$2a(2y) = Blowfish
$5 = SHA-256
$6 = SHA-512

目标hash的magic==1,说明是md5加密。
当然内部实现不会是单纯单次md5,但总体来说是以MD5为hash函数,通过多次计算得到的最终值。

类似,这个是sha-256的哈希(明文 admin):
$5$DnnkiE71Scb5$lHT.SBfgQKoiTi8cF.cbuxlZ9ZBVFG8CGDxh8CpgPe8
这个是sha-512的哈希(明文 admin):
$6$I7iRFjXdW9rZA2$/4WJ35KCqtrfc3BFmoargIm8WiKhY5cSBuJIb7ItjO0I7Dj99ZVIPZ3fgKvxaDgZqrWNWwL5aSVwQUkd8D7LT0

对比发现,magic值确实不同。除了通过magic来判断密文的加密方式以外,通过哈希的长度也可以判断。比如原哈希Hsrx102ek28d03B5dqgAv/,我们可以用以下代码来看看其长度:

php -r "echo strlen(base64_decode('Hsrx102ek28d03B5dqgAv/'));"
Paste_Image.png

可见结果为16,正是md5的摘要的长度(hex后长度为32),这样也能佐证这个哈希的加密方式为md5。

Salt

salt是此次哈希的盐值,长度是8位,超过8的后面的位数将不影响哈希的结果。
在正常情况下,进行加密的时候,这个盐值是随机字符串,所以说其实这个哈希:
$1$Dx1bONFt$Hsrx102ek28d03B5dqgAv/
我们可以类比为
1ecaf1d74d9e936f1dd3707976a800bf:Dx1bONFt

这个值1ecaf1d74d9e936f1dd3707976a800bf也不是我胡编的,是将原hash用base64解码后再转换为hex得到的。
而实际上原hash并不是base64编码,只是用类似base64编码的一种算法。这里用base64举例,具体算法后面会讲到

所以很多同学一看到$1$xxx$abcdef这样的密码就懵逼了,其实完全不必,你可就把他理解为abcdef:xxx。

Password

password就是加密完成后得到的hash。
我这里给出其php实现的具体算法:

namespace Md5Crypt;
class Md5Crypt 
{
    static public $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; 
            // [a-zA-Z0-9./]
    
    static protected function to64($v, $n) 
    {
        $itoa64 = self::$itoa64;
        $ret = '';
        
        while(--$n >= 0) {
            $ret .= $itoa64{$v & 0x3f};   
            $v = $v >> 6;
        }
        return $ret;
    }
    
    static public function apache($pw, $salt = NULL) 
    {
        $Magic = '$apr1$';
        
        return self::unix($pw, $salt, $Magic);
    }
    
    static public function unix($pw, $salt = NULL, $Magic = '$1$') 
    {
        $itoa64 = self::$itoa64;
        
        if($salt !== NULL) {
            // Take care of the magic string if present
            if(substr($salt, 0, strlen($Magic)) == $Magic) {
                $salt = substr($salt, strlen($Magic), strlen($salt));
            }
            // Salt can have up to 8 characters
            $parts = explode('$', $salt, 1);
            $salt = substr($parts[0], 0, 8);
        } else {
            $salt = '';
            mt_srand((double)(microtime() * 10000000));
            
            while(strlen($salt) < 8) {
                $salt .= $itoa64{mt_rand(0, strlen($itoa64)-1)};
            }
        }
        
        $ctx = $pw . $Magic . $salt;
        
        $final = pack('H*', md5($pw . $salt . $pw));
        
        for ($pl = strlen($pw); $pl > 0; $pl -= 16) {
           $ctx .= substr($final, 0, ($pl > 16) ? 16 : $pl);
        }
            
        // Now the 'weird' xform
        for($i = strlen($pw); $i; $i >>= 1) {   
            if($i & 1) {                // This comes from the original version,
                $ctx .= pack("C", 0);   // where a memset() is done to $final
            } else {                    // before this loop
                $ctx .= $pw{0};
            }
        }
        
        $final = pack('H*', md5($ctx)); // The following is supposed to make
                                        // things run slower
        
        for($i = 0; $i < 1000; $i++) {
            $ctx1 = '';
            if($i & 1) {
                $ctx1 .= $pw;
            } else {
                $ctx1 .= substr($final, 0, 16);
            }
            if($i % 3) {
                $ctx1 .= $salt;
            }
            if($i % 7) {
                $ctx1 .= $pw;
            }
            if($i & 1) {
                $ctx1 .= substr($final, 0, 16);
            } else {
                $ctx1 .= $pw;
            }
            $final = pack('H*', md5($ctx1));
        }
        
        // Final xform
        $passwd = '';
        $passwd .= self::to64((intval(ord($final{0})) << 16)
                        |(intval(ord($final{6})) << 8)
                        |(intval(ord($final{12}))),4);
        $passwd .= self::to64((intval(ord($final{1})) << 16)
                        |(intval(ord($final{7})) << 8)
                        |(intval(ord($final{13}))), 4);
        $passwd .= self::to64((intval(ord($final{2})) << 16)
                        |(intval(ord($final{8})) << 8)
                        |(intval(ord($final{14}))), 4);
        $passwd .= self::to64((intval(ord($final{3})) << 16)
                        |(intval(ord($final{9})) << 8)
                        |(intval(ord($final{15}))), 4);
        $passwd .= self::to64((intval(ord($final{4}) << 16)
                        |(intval(ord($final{10})) << 8)
                        |(intval(ord($final{5})))), 4);
        $passwd .= self::to64((intval(ord($final{11}))), 2);
        
        // Return the final string
        return $Magic . $salt . '$' . $passwd;
    }
}

我们可以如下调用这个类,获得"elon11:Dx1bONFt"的哈希:

include_once("php-crypt-md5/library/Md5Crypt/Md5Crypt.php");

$password = "elon11";
$salt = "Dx1bONFt";

echo \Md5Crypt\Md5Crypt::unix($password, $salt);

得到的结果其实就是最开始给出的目标哈希 $1$Dx1bONFt$Hsrx102ek28d03B5dqgAv/

分析一下这个类,你会发现实际上它的核心算法是1002次循环md5,中间再进行一些截断、移位等过程。
在密码学中,对于防范哈希暴力破解的一种方式就是“密钥延伸”,简单来说就是利用多次hash计算,来延长暴力破解hash的时间,比如这里的1002次md5,就等于将单次md5破解时间延长了1002倍。
然而,在当今的计算机速度下,1002次md5,其实速度也是秒速。我用hashcat尝试破解上述hash,

7510个字典,仅用1秒不到跑完,速度为18.28k/s。
相对的,现代linux系统使用的hash方法为SHA-512(Unix),算法核心为sha512,我们可以通过cat /etc/shadow来获得之,通过hashcat来跑:

速度明显降下来了,只有656 words/s
前两天爆出的Joomla注入,获取到的hash值使用的加密方法是Bcrypt + Blowfish 。我们可以利用如下命令来跑这个密码:

hashcat --hash-type=3200 --attack-mode=0 joomla.txt less.dict

可见,速度已经降到45 words/s了,7510个密码的字典需要跑2分半才能全部跑完。足以见得joomla密码的安全性。
不过,这却不是最慢的,Minos(https://github.com/phith0n/Minos) 使用的也是Bcrypt + Blowfish,但我将其cost设置为12。
cost在Blowfish算法中就是延缓其速度,增加破解难度的选项,如果将cost设置为12,生成的hash,破解起来速度可以降到10 words/s:

基本达到这样的速度,就可以满足安全需求了。这样的话,即使黑客拿到密码的hash,跑一万个密码的字典需要用16分钟,极大地增加了密码碰撞的难度。

开发与渗透中如何生成hash

那么,这些hash是怎么生成的呢?
我用php举例说明。
生成一个普通的unix(md5),直接用上面给出的源码即可。当然php也有自带的方法可以办到:

echo crypt("admin", '$1$12345678');

生成一个sha512(unix)

echo crypt("admin", '$6$12345678');

生成一个bcrypt+blowfish(cost=10默认)(joomla的加密方式)

echo password_hash("123123", CRYPT_BLOWFISH);

生成一个bcrypt+blowfish(cost=12)(minos的加密方式)

echo password_hash("123123", CRYPT_BLOWFISH, ["cost" => 12]);

在渗透过程中,我们也可以直接用工具生成这类密码。比如htpasswd工具,以下是生成密码的一些方法:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • 这篇文章主要讲述在Mobile BI(移动商务智能)开发过程中,在网络通信、数据存储、登录验证这几个方面涉及的加密...
    雨_树阅读 2,235评论 0 6
  • 立秋已过,让我放松了警惕。 其实在广州,夏天的热,蔓延到11月份都不稀奇,何况现在才八月初。晚起导致早晨七点才买回...
    好馨勤阅读 180评论 0 0
  • 本来计划今天带孩子出去玩的,但临时有事需要去一参加一个业委会作咨询和解答。上午和孩子看了会儿绘本,又玩了会儿...
    无敌疯婆娘阅读 117评论 0 0
  • 「任务」 提问主题:如果没有wifi的日子 作业 如果没有wifi的日子,我会不会很无聊? 1、转为正面如果没有w...
    李真71阅读 252评论 0 0
  • 来到了梁山,看到老婆心里始终担心,家里的那两个孩子,最主要的是我的事情不知道什么时候能完事,于是控制住对老婆的思念...
    白天有多白阅读 170评论 0 0