Python自动化,破解zip密码,提取密码,自动解压

使用john,破解简单的6位纯数字密码 + 身份证后6位密码

  • 安装:
git clone https://github.com/magnumripper/JohnTheRipper.git

cd JohnTheRipper/src

sudo apt-get update

./configure(如果报错,跳过openssl: ./configure --without-openssl)

make -s clean && make -sj4

 

  • 生成hash文件

JohnTheRipper/run/zip2john test.zip > test.hash

 如下图:

 

  • 破解密码

        最简单版本

JohnTheRipper/run/john  test.hash

  • 指定规则破解

        破解6位纯数字

JohnTheRipper/run/john  --mask=?d?d?d?d?d?d  test.hash 

        破解6位纯小写字母(注意,问号后不是竖线,是L的小写字母)

JohnTheRipper/run/john  --mask=?l?l?l?l?l?l  test.hash 

        破解5位纯数字 + 1位小写字母(比如身份证后6位)

JohnTheRipper/run/john  --mask=?d?d?d?d?d?l  test.hash 

  • 指定密码文件

        首先,用Python生成密码(这里只生成了6位纯数数字 + 身份证后6位),然后写入文件

import itertools
import pandas as pd



def create_password_txt():
    # 纯6位数字
    df_password = pd.Series(range(0, 1000000)).astype(str).str.rjust(6, fillchar='0')
    

    # 身份证后六位
    birthday = [str(i).rjust(2, '0') for i in range(1, 32)]  # 生日:01、02、03......30、31
    other = '0123456789'
    check = 'xX'  # 身份证最后一位,校验位(为什么不包含数字,因为上面的6位纯数字密码集已经包含了)
    df = pd.DataFrame(list(itertools.product(birthday, other, other, other, check)))
    df_birthday = df[0].str.cat([df[1], df[2], df[3], df[4]], sep='')

    # 合并
    df_all = df_password.append(df_birthday)

    # 验证
    print(df_all.str.len().value_counts())
    # 如果没错的话,数量应该是 1062000
    print(len(df_all.unique()))
    
    
    # 写入文件 ==> auto_job_config.cfg ==> password_file_path
    with open('password.txt', 'w') as file:
        file.write('\n'.join(df_all))
    print('ok')

生成的密码文件 password.txt 如下:

Python自动化,破解zip密码,提取密码,自动解压_第1张图片 

 

--wordlist 参数指定密码文件,只会尝试密码文件中的密码 ,如果知道密码位数和类型,使用这种请会提升破解效率。

JohnTheRipper/run/john  --wordlist=password.txt  test.hash

测试案例:

Python自动化,破解zip密码,提取密码,自动解压_第2张图片

 

--log-stderr 参数,忽略日志输出

JohnTheRipper/run/john  --log-stderr  test.hash

        

--session 参数,如果你想同时破解多个文件,需要指定不同进程名称,要不然会报错。

 如果机器性能足够好,只要进程名称不同,就可以并行解析很多个zip文件。

# 进程1

JohnTheRipper/run/john  --session=name1 test1.hash

# 进程2

JohnTheRipper/run/john  --session=name2  test2.hash

  • 查看解密运行日志

tail  -f  JohnTheRipper/run/john.log

 

  • 清除缓存

当重复执行同一个hash文件的时候,只有第一次会显示密码,因为有缓存机制。

可以用shell重定向清空缓存文件,即可每次都显示密码,但是每次也都会重新耗时破解密码,看使用需求。

true >  JohnTheRipper/run/john.pot

>  JohnTheRipper/run/john.pot

: >  JohnTheRipper/run/john.pot

cp  /dev/null  JohnTheRipper/run/john.pot

或者用Python实现

with open('JohnTheRipper/run/john.pot', 'w') as f:
    pass

  • 使用Python调用 shell 破解命令

        首先写一个连串的命令模板,路径用参数替换。

decrypt_zip_command = ' ; '.join([

    # 生成hash文件
    "/Users/mk/JohnTheRipper/run/zip2john {zip_path} > {hash_path}", 
 
    # 解密,日志不写入文件,指定会话ID可以一次执行多个,不重复即可,指定密码文件
    "/Users/mk/JohnTheRipper/run/john --no-log --session={random_str} --wordlist={password_path} {hash_path}", 

    # 清空缓存密码
    "true > /Users/mk/JohnTheRipper/run/john.pot", 
 
    # 删除当前hash文件
    "rm {hash_path}", 
 
])

  • 函数功能:执行shell命令,破解密码,返回执行结果文本

def exec_command(self, cmd: str, in_put: str = None, timeout=120) -> bool and str:
    t1 = time.time()
    self.logger.info(f'执行命令: "{cmd}"\n')
    try:
        output_str = subprocess.check_output(cmd, input=in_put, stderr=subprocess.STDOUT, shell=True, timeout=timeout).decode('utf-8').strip()
    except subprocess.CalledProcessError as e:
        error_output = e.output.decode('utf-8')
        self.logger.error(f'执行命令失败: "{error_output}"\n')
        return False, ''
    else:
        return True, output_str
    finally:
        if 'run/john' in cmd:
            use_total = time.time() - t1
            self.logger.info(f'破解密码用时: {use_total:.2f}s')

  • 使用正则,清洗出密码

password_result_str 是 运行命令返回的所有字符串

# 清洗密码
match_object = re.search(r"\n(?P[\dxX]{6})\s+\(.*?\)", password_result_str)
if match_object:
    password = match_object.group('password').strip()
    print(f'解析密码成功: "{password}"')

不止自动化破解zip文件,还可以破解PDF文件

多线程处理

自动去除PDF文件密码

全部功能代码都在下文:

全文代码:

# -*- coding: utf-8 -*-
#   @Author : mk
#   @Time   : 2022/07/22 09:50

import os
import re
import time
import zipfile
import threading
import subprocess
import pdfplumber
from PyPDF2 import PdfFileReader, PdfFileWriter
from pdfminer.pdfdocument import PDFPasswordIncorrect

from parse_base import Base


class TestThread(threading.Thread):
    def __init__(self, target, args=()):
        super(TestThread, self).__init__()
        self.func = target
        self.args = args
        self.result = None

    def run(self):
        try:
            self.result = self.func(*self.args)
        except:
            self.result = []

    def get_result(self):
        try:
            return self.result
        except Exception:
            return []


class Zip(Base):
    def __init__(self):
        super().__init__()
        # 爆破pdf命令
        self.decrypt_pdf_command = r"pdfcrack -q -c 0123456789 -n 6 -m 6 -f {pdf_path}"
        # 解压zip命令,指定密码,指定解压路径,强制覆盖文件
        self.unzip_with_7z_command = '7z x -p{zip_password} -aoa {zip_path} -o{output_path}'
        # 爆破zip命令
        if self.IS_DEV == 'test':
            self.decrypt_zip_command = ' ; '.join([
                "/Users/mk/mk/学习/JohnTheRipper/run/zip2john {zip_path} > {hash_path}",  # 生成hash文件
                "/Users/mk/mk/学习/JohnTheRipper/run/john --no-log --session={random_str} --wordlist={password_path} {hash_path}",  # 解密,日志不写入文件,指定会话ID可以一次执行多个,不重复即可,指定密码文件
                "true > /Users/mk/mk/学习/JohnTheRipper/run/john.pot",  # 清空缓存密码
                "rm {hash_path}",  # 删除当前hash文件
            ])
        else:
            self.decrypt_zip_command = ' ; '.join([
                "/home/souche/JohnTheRipper/run/zip2john {zip_path} > {hash_path}",  # 生成hash文件
                "/home/souche/JohnTheRipper/run/john --no-log --session={random_str} --wordlist={password_path} {hash_path}",  # 解密
                "true > /home/souche/JohnTheRipper/run/john.pot",  # 清空缓存密码
                "rm {hash_path}",  # 删除当前hash文件,防止占内存
            ])

    # 判断是否图片类型
    def is_image_type(self, pdf_file_path: str, pw='') -> bool:
        with pdfplumber.open(pdf_file_path, password=pw) as pdf:
            # 首页文本
            first_page_text = pdf.pages[0].extract_words()
            # 获取不到文字,应该就是图片类型了
            if not first_page_text:
                self.logger.error('PDF可能是图片类型,已经中止后续操作')
                return True
            else:
                return False

    # 去除PDF加密
    def decrypt_and_move_file(self, old_pdf_path: str, password: str, new_pdf_path: str) -> None:
        try:
            pdf_reader = PdfFileReader(old_pdf_path)
            pdf_reader.decrypt(password)  # 解密
            pdf_writer = PdfFileWriter()
            for page in range(pdf_reader.getNumPages()):
                pdf_writer.addPage(pdf_reader.getPage(page))
            with open(new_pdf_path, "wb") as new_pdf:
                pdf_writer.write(new_pdf)  # 未加密的pdf
        except:
            self.logger.error('解除PDF加密失败')
        else:
            self.logger.info('解除PDF加密成功')

    # 执行shell命令,破解密码,返回密码
    def exec_command(self, cmd: str, in_put: str = None, timeout=120) -> bool and str:
        t1 = time.time()
        self.logger.info(f'执行命令: "{cmd}"\n')
        try:
            output_str = subprocess.check_output(cmd, input=in_put, stderr=subprocess.STDOUT, shell=True, timeout=timeout).decode('utf-8').strip()
        except subprocess.CalledProcessError as e:
            error_output = e.output.decode('utf-8')
            self.logger.error(f'执行命令失败: "{error_output}"\n')
            return False, ''
        else:
            return True, output_str
        finally:
            if 'run/john' in cmd:
                use_total = time.time() - t1
                self.logger.info(f'破解密码用时: {use_total:.2f}s')

    # zip 解密 ==> PDF
    def zip_to_pdf(self, zip_file_name: str) -> list:
        self.logger.info(f'【{zip_file_name}】开始解析密码')
        is_success = False
        # 随机字符串
        random_str = self.get_random_str()
        # hash文件
        hash_path = os.path.join(self.hash_file_path_root, f'{random_str}.hash')
        # 密码文件
        password_path = self.password_file_path
        # 输入文件
        zip_file_path = os.path.join(self.zip_file_path_root, zip_file_name)
        # 输出文件夹
        pdf_file_path_folder = os.path.join(self.pdf_file_path_root, zip_file_name.replace('.zip', ''))  # 注意,这是文件夹,不是文件

        if not os.path.exists(zip_file_path):
            self.logger.error(f'zip文件不存在: {zip_file_path}')
            return []

        # 先判断是否有加密
        with zipfile.ZipFile(zip_file_path) as zfp:
            # 遍历,查看每个文件是否加密,有一个加密都算加密
            is_encrypted = any([i.flag_bits & 0x1 for i in zfp.infolist()])
            # is_encrypted = zfp.infolist()[0].flag_bits & 0x1
            # 如果加密
            if is_encrypted:
                # 爆破命令
                decrypt_zip_command = self.decrypt_zip_command.format(zip_path=zip_file_path,
                                                                      hash_path=hash_path,
                                                                      random_str=random_str,
                                                                      password_path=password_path,
                                                                      )
                shell_success, password_result_str = self.exec_command(decrypt_zip_command)
                # 如果执行成功
                if shell_success:
                    # 清洗密码
                    match_object = re.search(r"\n(?P[\dxX]{6})\s+\(.*?\)", password_result_str)
                    if match_object:
                        password = match_object.group('password').strip()
                        self.logger.info(f'【{zip_file_name}】解析密码成功: "{password}"')
                        # 解压文件夹
                        unzip_with_7z_command = self.unzip_with_7z_command.format(zip_password=password,
                                                                                  zip_path=zip_file_path,
                                                                                  output_path=pdf_file_path_folder,
                                                                                  )
                        shell_success, unzip_result_str = self.exec_command(unzip_with_7z_command)
                        if shell_success and 'Everything is Ok' in unzip_result_str:
                            self.logger.info(f'【{zip_file_name}】解压成功 ==>【{pdf_file_path_folder}】')
                            is_success = True
                    else:
                        self.logger.error(f'【{zip_file_name}】解密失败,shell返回信息: {password_result_str}')
            else:
                # 如果没加密,就直接解压(几乎不可能。。。)
                zfp.extractall(pdf_file_path_folder)  # 解压到
                is_success = True
                self.logger.info(f'【{zip_file_name}】无密码')

        if is_success:
            # 返回绝对路径
            pdf_file_abs_path_list = []
            mode = str(zip_file_name)[0].upper()
            for pdf_file_name in os.listdir(pdf_file_path_folder):
                # 添加前缀,区分BC端
                old_pdf_file_abs_path = os.path.join(pdf_file_path_folder, pdf_file_name)
                new_pdf_file_abs_path = os.path.join(pdf_file_path_folder, f'{mode}_{pdf_file_name}')
                # 文件重命名
                os.rename(old_pdf_file_abs_path, new_pdf_file_abs_path)
                if pdf_file_name.endswith('.pdf'):
                    pdf_file_abs_path_list.append(new_pdf_file_abs_path)
                else:
                    self.logger.error(f'非PDF文件:{new_pdf_file_abs_path}')
            return pdf_file_abs_path_list
        else:
            return []

    # PDF 解密 ==> PDF
    def pdf_to_pdf(self, pdf_file_name: str) -> list:
        is_success = False
        self.logger.info(f'【{pdf_file_name}】开始解析密码')

        # 输入
        old_pdf_file_path = os.path.join(self.zip_file_path_root, pdf_file_name)
        # 输出
        new_pdf_file_path = os.path.join(self.pdf_file_path_root, pdf_file_name)

        if not os.path.exists(old_pdf_file_path):
            self.logger.error(f'pdf文件不存在: {old_pdf_file_path}')
            return []

        # 判断是否加密
        try:
            with pdfplumber.open(old_pdf_file_path):
                pass
        # 如果加密
        except PDFPasswordIncorrect:
            # 传入文件路径,开始解密
            shell_success, password_result_str = self.exec_command(self.decrypt_pdf_command.format(pdf_path=old_pdf_file_path))
            # 如果执行命令成功
            if shell_success:
                # 清洗密码
                match_object = re.search(r"found user-password: '(?P\d+)'", password_result_str)
                # 如果解析出密码
                if match_object:
                    password = match_object.group('password').strip()
                    self.logger.info(f'【{pdf_file_name}】解密成功: "{password}"')
                    # 判断是否图片类型PDF
                    pdf_is_image = self.is_image_type(old_pdf_file_path, password)
                    # 如果是图片,不做后续动作
                    if pdf_is_image:
                        is_success = False
                    # 如果不是图片类型
                    else:
                        # 解除密码,并且移动文件
                        self.decrypt_and_move_file(old_pdf_file_path, password, new_pdf_file_path)
                        is_success = True
                else:
                    self.logger.error(f'【{pdf_file_name}】解密失败,shell返回信息: {password_result_str}')
        # 如果无加密
        else:
            self.logger.info(f'【{pdf_file_name}】无密码')
            # 是否图片
            pdf_is_image = self.is_image_type(old_pdf_file_path)
            if pdf_is_image:
                is_success = False
            else:
                # 没密码,且不是图片,直接移动
                os.rename(old_pdf_file_path, new_pdf_file_path)
                is_success = True

        if is_success:
            return [new_pdf_file_path]
        else:
            return []

    # 多线程处理文件
    def to_pdf(self, file_name_list: list) -> list:
        pdf_abs_path_list = []
        if file_name_list and isinstance(file_name_list, list):
            job_list = list()
            # 遍历zip/PDF文件名称,非绝对路径
            for file_name in file_name_list:
                # 邮件附件是压缩包
                if file_name.endswith('.zip'):
                    job_list.append(TestThread(target=self.zip_to_pdf, args=(file_name,)))
                # 邮件附件是PDF文件
                elif file_name.endswith('.pdf'):
                    job_list.append(TestThread(target=self.pdf_to_pdf, args=(file_name,)))
                else:
                    self.logger.error(f'未知文件类型:{file_name}')
            # 多线程处理
            if job_list:
                for job in job_list:
                    job.start()
                for job in job_list:
                    job.join()
                    pdf_abs_path_list.extend(job.get_result())
        # 返回pdf文件绝对路径
        return pdf_abs_path_list

你可能感兴趣的:(python,安全,python,pandas,自动化)