Skip to content

Commit f112ecc

Browse files
committed
Create Post “2023-03-17-how-to-read-dpapi-encrypted-chrome-credentials-offline”
1 parent f86a89e commit f112ecc

File tree

3 files changed

+112
-0
lines changed

3 files changed

+112
-0
lines changed
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
---
2+
title: How to read DPAPI encrypted chrome credentials offline
3+
desc: Reading DPAPI encrypted secrets is easy using tools like mimikatz but what
4+
if the secrets are stored offline in a disk image
5+
layout: post
6+
date: March 18, 2023 1:32 AM
7+
image: /images/posts/dpapi_ctf.jpg
8+
---
9+
<h1 class="cyan-text">How to read DPAPI encrypted chrome credentials offline</h1>
10+
11+
<div class="content white-text"><p>Recently we were playing DavinciCTF 2023 and I came across a forensics challenge, David Cicode, which was based on DPAPI encrypted chrome credentials. In a real life scenario we use tools like mimikatz to easily get master keys and read chrome secrets but in this challenge all I had was an image file which means offline extraction of secrets without the use of mimikatz!</p></div>
12+
13+
<div class="center-img"> <img class="responsive-img post-img" src="https://i.imgur.com/1JhkZUk.png"> </div>
14+
15+
<h2 class="cyan-text">Mount EWF image file</h2>
16+
17+
<div class="content white-text"><p>Mounting an EWF image file is simple. Make sure you have `libewf` or `ewf-tools` depending on the OS you are using. You will have access to `ewfmount` command :</p></div>
18+
19+
<div class="center-img"> <img class="responsive-img post-img" src="https://i.imgur.com/KYeHL3R.png"> </div>
20+
21+
<div class="content white-text"><p>I copied the image found inside for easier access and mounted it using the following mount command :</p></div>
22+
23+
<pre><code>sudo mount ewf1 mpoint -o ro,loop,show_sys_files,streams_interace=windows</code></pre>
24+
25+
<div class="center-img"> <img class="responsive-img post-img" src="https://i.imgur.com/meUNyG5.png"> </div>
26+
27+
<div class="content white-text"><p>yes I know, colors gone wrong, I still haven't fixed that. Anyways now the Users directory was readable. According to the challenge description goal was to find YouTube credentials.</p></div>
28+
29+
<h2 class="cyan-text">Chrome secrets</h2>
30+
31+
<div class="content white-text"><p>In windows you will find juicy stuff under the following directory :</p></div>
32+
33+
<pre><code>/Users/USERNAME/AppData/Local/Google/Chrome/User Data</code></pre>
34+
35+
<div class="content white-text"><p>We are interested in two files, `Local State` and `Login Data`. State is a JSON file which contains browser information, profile information and encrypted keys for our logins. Login data is a sqlite3 database which contains encrypted passwords.</p></div>
36+
37+
<h2 class="cyan-text">The problem</h2>
38+
39+
<div class="content white-text"><p> The AES key in state file is protected by Windows DPAPI. We need to decrypt the DPAPI blob to get the AES key and then use it to read the passwords from database.</p></div>
40+
41+
<h2 class="cyan-text">Crack user password</h2>
42+
43+
<div class="content white-text"><p>Using `DPAPImk2john.py` we can generate a hash based on SID and masterkey of the user. To get SID and masterkey we can simply browse to the following path :</p></div>
44+
45+
<pre><code>/Users/DavidCicode/AppData/Roaming/Microsoft/Protect</code></pre>
46+
47+
<div class="center-img"> <img class="responsive-img post-img" src="https://i.imgur.com/bpI7ebM.png"> </div>
48+
49+
<div class="content white-text"><p>Directory name is the SID and the filename inside is the masterkey value</p></div>
50+
51+
<pre><code>DPAPImk2john --sid="S-1-5-21-3100841193-2683041198-3967357107-1000" --masterkey="1acbbc52-02e6-404d-b139-43d93da54db8" --context="local" > hash.txt
52+
53+
# Crack hash using john
54+
55+
john hash.txt -w=/usr/share/wordlists/rockyou.txt
56+
Created directory: /home/twh/.john/opencl
57+
Using default input encoding: UTF-8
58+
Loaded 1 password hash (DPAPImk, DPAPI masterkey file v1 and v2 [SHA1/MD4 PBKDF2-(SHA1/SHA512)-DPAPI-variant 3DES/AES256 128/128 AVX 4x])
59+
Cost 1 (iteration count) is 8000 for all loaded hashes
60+
Will run 4 OpenMP threads
61+
Press 'q' or Ctrl-C to abort, 'h' for help, almost any other key for status
62+
ilovedavid (?)</code></pre>
63+
64+
<h2 class="cyan-text">Decrypt masterkey file</h2>
65+
66+
<div class="content white-text"><p>Using the password and power of impacket tools we can now read the masterkey file</p></div>
67+
68+
<pre><code>dpapi.py masterkey -file mpoint/Users/DavidCicode/AppData/Roaming/Microsoft/Protect/S-1-5-21-3100841193-2683041198-3967357107-1000/1acbbc52-02e6-404d-b139-43d93da54db8 -sid S-1-5-21-3100841193-2683041198-3967357107-1000 -password ilovedavid</code></pre>
69+
70+
<div class="center-img"> <img class="responsive-img post-img" src="https://i.imgur.com/rr32wdU.png"> </div>
71+
72+
<h2 class="cyan-text">David deleted everything!</h2>
73+
74+
<div class="content white-text"><p>At this point the challenge took a turn and it looks like both state file and login data file were deleted, so now first we need to recover them. For this step I used `TestDisk` utility :</p></div>
75+
76+
<pre><code>sudo testdisk ewf1</code></pre>
77+
78+
<div class="center-img"> <img class="responsive-img post-img" src="https://i.imgur.com/Vvzy5s6.png"> </div>
79+
80+
<div class="content white-text"><p>Here is the Login Data database file :</p></div>
81+
82+
<div class="center-img"> <img class="responsive-img post-img" src="https://i.imgur.com/5zrNEk1.png"> </div>
83+
84+
<h2 class="cyan-text">Decrypting AES key</h2>
85+
86+
<div class="content white-text"><p>In order to get the AES key first we need to extract it from state file and decode it :</p></div>
87+
88+
<pre><code>import json
89+
import base64
90+
91+
with open('/home/twh/ctf/dvc_2023/cicode/recovered/local_state', 'rb') as state_file:
92+
encrypted_key = json.load(state_file)
93+
94+
encrypted_key = encrypted_key['os_crypt']['encrypted_key']
95+
decrypted_key = base64.b64decode(encrypted_key)
96+
97+
with open('dec_data', 'wb') as outfile:
98+
outfile.write(decrypted_key[5:])</code></pre>
99+
100+
<div class="content white-text"><p>next we need to decrypt this DPAPI blob using the masterkey :</p></div>
101+
102+
<pre><code>dpapi.py unprotect -file dec_data -key 0x89c9d658351ff09f6507c089320a70302d94a462b1365d2e946af928caa80d39d87be29a7ec0429949391590595b6f9ccdf0675f20e9d91f5d534f8489949e1c</code></pre>
103+
104+
<div class="center-img"> <img class="responsive-img post-img" src="https://i.imgur.com/3JOCq9E.png"> </div>
105+
106+
<div class="content white-text"><p>now we need to convert this hexdump, I used cyberchef for this :</p></div>
107+
108+
<div class="center-img"> <img class="responsive-img post-img" src="https://i.imgur.com/khDG7sK.png"> </div>
109+
110+
<div class="content white-text"><p>finally I used <a href="https://github.com/ohyicong/decrypt-chrome-passwords" target="_blank">decrypt-chrome-passwords</a> project to decrypt the password, few modifications are required like the path to login data database, and here is the flag :</p></div>
111+
112+
<div class="center-img"> <img class="responsive-img post-img" src="https://i.imgur.com/asY6yvr.png"> </div>

images/posts/dpapi_ctf.jpg

46.8 KB
Loading

images/posts/dpapi_ctf.webp

20.8 KB
Loading

0 commit comments

Comments
 (0)