Python黑客绝技05:UNIX口令破解

Python黑客绝技学习笔记05:Unix口令破解

在上一期中,我们学习了条件选择语句、异常处理、函数、迭代、文件输入/输出、sys模块、OS模块等内容,我们还将Python的基础知识全部运用到我们的漏洞扫描脚本中。本期我们将学习一个特别有趣的Python程序——Unix口令破解器。

1.4 第一个程序——Unix口令破解

1.4.1 背景材料

Clifford Stoll 的《杜鹃蛋》(《The Cuckoo’s Egg》1989)堪称新派武侠的开山之作。它第一次把黑客活动与国家安全联系在一起。黑客极具破坏性的黑暗面也浮出海面,并且永远改变了黑客的形象。迄今仍是经久不衰的畅销书。
  Clifford Stoll 是Lawrence Berkley国家实验室的系统管理员。1986 年夏,一个区区75 美分的帐目错误引起了他的警觉,在追查这次未经授权的入侵过程中,他开始卷入一个错综复杂的电脑间谍案。神秘的入侵者是西德混沌俱乐部的成员。他们潜入美国,窃取敏感的军事和安全情报。出售给克格勃,以换取现金及可卡因。一场网络跨国大搜索开始了,并牵涉出FBI、CIA、克格勃、西德邮电部等。
《杜鹃蛋》为后来的黑客作品奠定了一个主题:追捕与反追捕的惊险故事。而且也开始了新模式:一个坚韧和智慧的孤胆英雄,成为国家安全力量的化身,与狡猾的对手展开传奇的较量。

他还在1988年5月出版的《美国计算机协会通信》中撰文详细描述了攻击和追捕的技术细节。
黑客的攻击手段与行动令Stoll非常着迷,他将打印机连接到一台被黑的服务器上,记录下攻击者的每个按键操作。
在一个记录中,Stoll发现了一些有趣的事情:几乎在攻入服务器的同时,攻击者就立即下载了加密的口令文件。这些文件对攻击者会有什么用?要知道,服务器系统是使用UNIX
Crypt算法
对用户口令进行加密的。然而,在窃取了加密口令文件后的一周,Stoll就发现了攻击者用偷来的账号登录的情况。后来在接触了一些被黑主机的用户后,Stoll才明白,原来这些被黑主机的用户都是使用字典中的常用词作为口令的。
知道这一点后,Stoll意识到黑客曾使用字典攻击法去破解加密的口令。黑客穷举了字典中的所有单词,并用UNIX Crypt算法函数对它们加密,然后将结果与偷来的加密密文进行对比。如果能对比成功,就破解了一个口令。

下面我们就来复原一下这个过程,写出自己的UNIX口令破解器。

1.4.2 UNIX口令破解思路

比如下面这个口令文件,被黑主机的用户使用的口令的明文是单词egg,salt(盐)是开关的两个字节HXUNIX Crypt函数计算的加密口令为crypt('egg',‘HX’) = HX9LLTdc/jiDE。我们使用python自带的crypt模块的.crypt方法可以实现这个功能,我们先看一下这个模块的帮助文档。

>>> help('crypt')
Help on module crypt:                                                                                              
                                                                                                                   
NAME                                                                                                               
    crypt - Wrapper to the POSIX crypt library call and associated functionality.                                  
FUNCTIONS                                                                                                          
    crypt(word, salt=None)                                                                                         
        Return a string representing the one-way hash of a password, with a salt                                   
        prepended.                                                                                                 
                                                                                                                   
        If ``salt`` is not specified or is ``None``, the strongest                                                 
        available method will be selected and a salt generated.  Otherwise,                                        
        ``salt`` may be one of the ``crypt.METHOD_*`` values, or a string as                                       
        returned by ``crypt.mksalt()``.                                                                            
                                                                                                                   
    mksalt(method=None, *, rounds=None)                                                                            
        Generate a salt for the specified method.                                                                  
                                                                                                                   
        If not specified, the strongest available method will be used.                                             
DATA
    METHOD_BLOWFISH = <crypt.METHOD_BLOWFISH>
    METHOD_CRYPT = <crypt.METHOD_CRYPT>
    METHOD_MD5 = <crypt.METHOD_MD5>
    METHOD_SHA256 = <crypt.METHOD_SHA256>
    METHOD_SHA512 = <crypt.METHOD_SHA512>
    methods = [<crypt.METHOD_SHA512>, <crypt.METHOD_SHA256>, <crypt.METHOD...

FILE
    /usr/lib/python3.8/crypt.py

更加详细内容可以查看https://docs.python.org/3.8/library/crypt
我们尝试使用crypt方法实现哈希(hash)值的计算,代码如下:

>>> import crypt
>>> crypt.crypt('egg', 'HX')
'HX9LLTdc/jiDE'

这里我们可以看到,计算出来的结果'HX9LLTdc/jiDE'的前两位就是salt的值。所以我们可以通过加密后的hash密码获得salt的值。
按照背景材料的思路,我们可以使用上面的函数对口令字典中的每个口令进行hash,之后与已知的加密口令hash进行比对,一致的话我们就破解了该口令啦!

接下来,我们写一下这个程序。为了增加程序的可重用性和易读性,我们使用函数的方式对单一功能的代码进行封装。
我们创建两个函数,maintestpass
其中main函数打开加密口令文件password.txt,并逐行读取口令文件中的内容。每一行中的用户名和口令hash值是分开的。对每个口令hash,main函数调用testPass()函数,尝试用字典中的单词进行比对破解。
testPass()函数则以参数形式获得加密口令hash,并在找到密码或遍历无果返回空值。要注意的是,该函数首先将加密的口令hash值的前两个字符视为salt,并提取出来。然后,它打开字典并遍历字典中的每个单词,用每个单词和salt计算一个新的加密口令hash。如果计算结果与我们加密口令hash匹配,函数将全打印一条消息显示找到密码,并返回,继续遍历直到结尾并打印未找到。

1.4.3 UNIX口令破解程序

程序如下:

#!/usr/bin/python3
#coding=utf-8
"""
UNIX口令破解器
"""
import crypt
# 找到密码或遍历无果返回空值
def testPass(cryptPass):
    salt = cryptPass[0:2]  # 口令hash值的前两个字符是为salt
    dictfile = open('dictionary.txt', 'r')  # 只读方式打开字典文件
    for word in dictfile.readlines():
        # word = word.replace('\n', '').replace('\r', '')     # 保留原始的字符,取出换行符,不去空格
        word = word.strip('\n')    # 保留原始的字符,取出换行符,不去空格
        cryptWord = crypt.crypt(word, salt)  # 加密
        if cryptPass == cryptWord:  # 密文一致
            print('Found passed : ', word)  # 打印出正确密码
            return
    print('Password not found !')
    return
# 打开读取加密口令文件,调用testPass()函数破解口令
def main():
    passfile = open('passwords.txt', 'r')  #读取密码文件
    for line in passfile.readlines():  # 逐行读取
        user = line.split(':')[0]  # 提取用户名
        cryptPass = line.split(':')[1].strip('')  # 提取hash值
        print("Cracking Password For :", user)
        testPass(cryptPass)  # 破解

if __name__ == '__main__':
    main()

其中password.txt中的内容如下:

$ cat password.txt
victim: HX9LLTdc/jiDE: 503:100:Iama Victim:/home/victim:/bin/sh
root: DFNFxgW7C05fo: 504:100: Markus Hess:/root:/bin/bash

dictionary.txt文件内容如下:

$ cat dictionary.txt 
111111
123456
123
root
egg

1.4.3 现代Linux系统口令破解程序

我们尝试在实际的系统中进行破解,就拿我们使用的kali虚拟机来做实验吧。查看kali系统中的passwd文件,可以看到如下内容:

$ sudo cat /etc/passwd | grep kali
kali:x:1000:1000:Kali,,,:/home/kali:/bin/bash

可以看到并没有存储口令相关的hash值,这是因为现代的*NIX操作系统将口令的hash存储在/etc/shadow文件中,查看该文件:

$ sudo cat /etc/shadow | grep kali
kali:$6$BiEAYF4pzDp5QJXH$00iURpImugngJWdAraUxBFfU76xovc6mwuQMe479uN28NesybQxOW7TiLxqrn99nJCfvISJtJiXgIjtTicF720:18390:0:99999:7:::

在现代系统中,除了口令hash值存储位置不一样外,现代系统还使用了更多更安全的哈希散列算法,如SHA-512算法,该算法函数可以在Python的标准库的hashlib模块中找到。
shadow文件中,不同字段含意不同,但是具有固定格式:
$id$salt$encrypted
id 表示加密算法,1 代表 MD55 代表 SHA-2566 代表 SHA-512salt 表示密码学中的 Salt, 系统随机生成 encrypted 表示密码的 hash值。
在上例中,使用的算法为SHA-512,salt为BiEAYF4pzDp5QJXH,hash值为00iURpImugngJWdAraUxBFfU76xovc6mwuQMe479uN28NesybQxOW7TiLxqrn99nJCfvISJtJiXgIjtTicF720
了解了这些知识后,我们就可以按照同样的思路来更新我们的脚本,实现对现在使用的Linux系统口令进行破解。
我们先将需要破解的hash值输入到文件中去。

$ sudo cat /etc/shadow | grep kali > /home/kali/ViolentPython/01/shadow
$ cat /home/kali/ViolentPython/01/shadow
kali:$6$BiEAYF4pzDp5QJXH$00iURpImugngJWdAraUxBFfU76xovc6mwuQMe479uN28NesybQxOW7TiLxqrn99nJCfvISJtJiXgIjtTicF720:18390:0:99999:7:::

更新脚本,变化的内容也不多,但是这里踩过一个坑,主要是对Linux系统计算口令密文还是不太了解,绕了很多路。之前想直接使用Python的hashlib中的函数,但是尝试一圈,并不是那么简单。最后发现我们在计算哈希值的时候还需要使用crypt.crypt()函数,不同之处在于salt的内容。之前是两个字符,我们这里是需要把$id$salt作为salt整体,然后crypt()函数会自动识别算法ID和盐值,直接计算出完整内容。如下面的代码:

>>> import crypt
>>> pw="kali"
>>> print(crypt.crypt(pw, "$6$BiEAYF4pzDp5QJXH"))
$6$BiEAYF4pzDp5QJXH$00iURpImugngJWdAraUxBFfU76xovc6mwuQMe479uN28NesybQxOW7TiLxqrn99nJCfvISJtJiXgIjtTicF720

所以,我们只需要更新计算salt的程序即可。
我们可以使用正则表达式来匹配$6$BiEAYF4pzDp5QJXH,或者直接使用string模块的find()rfind()方法。

str.find()
描述
find() 方法检测字符串中是否包含子字符串 str ,如果指定 beg(开始) 和 end(结束) 范围,则检查是否包含在指定范围内,如果指定范围内如果包含指定索引值,返回的是索引值在字符串中的起始位置。如果不包含索引值,返回-1。
语法
find()方法语法:
str.find(str, beg=0, end=len(string))
参数
str -- 指定检索的字符串
beg -- 开始索引,默认为0。
end -- 结束索引,默认为字符串的长度。
返回值
如果包含子字符串返回开始的索引值,否则返回-1
str.rfind()
描述
Python rfind() 返回字符串最后一次出现的位置,如果没有匹配项则返回-1。

语法
rfind()方法语法:

str.rfind(str, beg=0 end=len(string))
参数
str -- 查找的字符串
beg -- 开始查找的位置,默认为0
end -- 结束查找位置,默认为字符串的长度。
返回值
返回字符串最后一次出现的位置,如果没有匹配项则返回-1

代码如下

>>> import crypt
>>> pw="kali"
>>> hash = crypt.crypt(pw, "$6$BiEAYF4pzDp5QJXH")
>>> hash
'$6$BiEAYF4pzDp5QJXH$00iURpImugngJWdAraUxBFfU76xovc6mwuQMe479uN28NesybQxOW7TiLxqrn99nJCfvISJtJiXgIjtTicF720'
>>> hash[hash.find('$'):hash.rfind('$')]
'$6$BiEAYF4pzDp5QJXH'

所以,最终的更新后的代码如下:

#!/usr/bin/python3
#coding=utf-8
"""
UNIX口令破解器
"""
import crypt
# 找到密码或遍历无果返回空值
def testPass(cryptPass):
    salt = cryptPass[cryptPass.find('$'):cryptPass.rfind('$')]  # 计算salt
    dictfile = open('dictionary.txt', 'r')  # 只读方式打开字典文件
    print(cryptPass)
    for word in dictfile.readlines():
        word = word.strip('\n')    # 保留原始的字符,取出换行符,不去空格
        cryptWord = crypt.crypt(word, salt)  # 加密
        if cryptPass  == cryptWord:  # 密文一致
            print('Found passed : ', word)  # 打印出正确密码
            return
    print('Password not found !')
    return
# 打开读取加密口令文件,调用testPass()函数破解口令
def main():
    
    passfile = open('shadow', 'r')  #读取密码文件
    for line in passfile.readlines():  # 逐行读取
        user = line.split(':')[0]  # 提取用户名
        cryptPass = line.split(':')[1].strip('')  # 提取hash值
        print("Cracking Password For :", user)
        testPass(cryptPass)  # 破解

if __name__ == '__main__':
    main()

以上就是本期的内容。接下来,我们将一起学习另外一个有趣的Python程序——ZIP文件密码破解。
更多精彩,请👇。
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值