Chrome Notification Analysis
Chrome Notification Analysis
gh
Ri
ll
Fu
ns
ai
et
rR
Google Chrome Notification Analysis in Depth
ho
GIAC (GCIH) Gold Certification
ut
,A
Author: Vincent Lo, [email protected]
Advisor: Dr. Johannes Ullrich
te
Accepted: June 7th 2021
itu
st
In
Abstract
NS
SA
reminders, email notifications or message notifications to users in the system tray. It has
Th
been discovered its “Push” API has been abused to deliver spam, erotic images or
malicious content to the victims. Unfortunately, the notification history is not recorded in
21
Chrome's browsing history. The notification detail appears to be stored in a separate file,
20
which is not in the format that can be easily parsed by the known open source tools or
freeware. This situation impacts incident response's triaging exercise. This paper will
©
look into the Google Chrome's notification feature and its storage structure in order to
reveal its content for the incident response purpose.
gh
Ri
1. Introduction
ll
Fu
Google Chrome browser’s notification function allows the websites to show email
ns
notifications or meeting reminders to users in the system tray, shown as below. It also
allows the websites to deliver various messages to the users. Since then, it has been
ai
et
discovered this feature is abused to deliver spam or malicious content to the victims.
rR
ho
ut
,A
During the investigation, the detail of the spam notifications would help incident
te
responders understand where they come from, which can be considered in the
itu
containment strategy. However, at the time of writing this paper, the information around
st
how to extract the notification history from Chrome and parse its content is not publicly
In
available on internet. This paper will look into Chrome notification function and its
NS
2. Web Notifications
Th
Some websites would like to enrich the user experience by adding the
21
notifications to provide the extra content to the user. If a website wants to use the
20
notification feature, it will need to send a request to the browser for the user’s approval.
©
Once the request is accepted, the notification will start to appear in the system tray. This
preference will be recorded in the browser setting, which can be modified anytime.
gh
Ri
2.1. Web Notifications Technologies
ll
Fu
Web notifications can be created by two technologies, Push API and Notification
API. Since version 42, released on 4 April 2015, Google Chrome browser supports Push
ns
API and Notification API (Gaunt, 2020).
ai
et
2.1.1. Notification API
rR
Email notifications or meeting reminders tend to use Notification API. This API
ho
allows them to be displayed in the system tray event if the user switch to a different
ut
browser tab or a different program (MDN Contributors, 2020).
messages to the web applications. Even when the websites are not loaded in the browser,
st
the messages can still be displayed in the system tray (MDN Contributors, 2020).
In
NS
security” ® “Site settings”, shown as below. By default, websites can request to send
21
notifications to Chrome. If requests are accepted, the websites are allowed to show the
20
notifications in the system tray. The Notifications setting lists those websites in the
©
“Allow” section. If the requests are rejected, those websites will be listed in the “Block”
section.
After the notification feature starts being abused to distribute spam or malicious
content, Google has been trying to address it. In Chrome 80, a quieter notification UI is
introduced to silently block the notification requests. This feature can be enabled under
one of the followings conditions (McLachlan, 2020).
gh
Ri
ll
Fu
ns
ai
et
rR
1. Manual enrollment
ho
Users can enable the setting manually by checking the “Use quieter
messaging” checkbox or disable it.
ut
,A
2. User behavior te
If users frequently deny notification requests, the quieter notifications UI will
itu
be enabled automatically.
st
Chrome doesn’t store the notification in the browser history. It stores the
notifications, created by the Push API, in a LevelDB database instead, which can be
found in the following folders.
gh
Ri
4. LevelDB
ll
Fu
In order to extract the notifications from a LevelDB database, it is important to
ns
know how it stores the data. LevelDB is an open-source key-value database program
developed by Google. Keys and values are stored in arbitrary byte arrays. Unlike the
ai
et
traditional relational databases, LevelDB databases don’t support indexes nor SQL
rR
queries. (Ghemawat & Dean, 2020).
ho
4.1. LevelDB File Structure
ut
A LevelDB database is supported by multiple files (Google, 2020). This section
will go through them.
,A
te
itu
st
In
NS
SA
• Log Files
When a notification is created through the Push API, its information and its
e
Th
updates are recorded in a log (*.log) file. A copy of this log file is also stored in the
memory with the data structure named MemTable. Once this log file reaches a predefined
21
size (around 4 MB by default) , it will be saved in a sorted table file and a new log file
20
will be created to store the new data (Google, 2020). The experiment conducted with
©
Chrome 88 shows the predefined log size in Chrome remains 4 MB. The structure of the
log file will be explained in the following section.
gh
Ri
• MANIFEST
ll
Fu
A MANIFEST file stores the metadata of the database. Every time when the
ns
database is opened, a new manifest will be created with a new number in the filename.
ai
• Current
et
rR
CURRENT is the file that contains the latest Manifest file name.
ho
• Information Logs
ut
LOG and LOG.old store the database messages. An example is shown below.
,A
2021/01/07-09:35:17.350 6703 Reusing MANIFEST /home/[user
te
name]/.config/google-chrome/Default/Platform Notifications/MANIFEST-000001
itu
chrome/Default/Platform Notifications/000003.log
SA
e
• Others
Th
The .ldb/.sst file contains the file structure, shown in the diagram below. The data
blocks store the key-value pairs. The following meta blocks store filter policy and stats.
The Metaindex block stores entries or those meta blocks. The index block stores the
entries of the data blocks (Google, 2017).
gh
Ri
ll
Fu
ns
ai
et
rR
ho
ut
,A
te
itu
The footer has a fixed length, which is 48 bytes. It contains the block handle for
st
In
metaindex, block handle for index, padding and file signature, 0xdb4775248b80fb57
NS
(Google, 2017).
SA
Currently, many open source tools are available to read the .ldb/.sst file. However,
the CURRENT file and MANIFEST file may need to be present in the same folder to
e
contains the notifications that are copied from the log file once the log file exceeds 4MB.
20
However, the value is the notification data or its update serialised with Protocol Buffer
©
gh
Ri
ll
Fu
ns
ai
et
rR
ho
ut
,A
te
itu
Depending on the op type, some records may contain a record header, key length,
key. Some records may contain a record header, key length, key, value length and value.
st
In
Key length and value length are encoded in varint format. The diagram below shows the
NS
gh
Ri
Byte Offset Field Length Field Name
ll
0x00 4 CRC
Fu
0x04 2 Record Length
0x06 1 Record Type
ns
0x07 8 Sequence Number
ai
0x15 4 Count
et
0x19 1 Op Type
rR
Record Length stores the length of the record in the little endian format. Record
ho
type shows whether the record is complete in the block.
ut
Record Type Description
,A
1 The whole record is stored in this block.
2 The data is stored across multiple blocks. This record contains the
te
first part of the data.
itu
3 The data is stored across multiple blocks. This record contains the
middle part of the data.
st
4 The data is stored across multiple blocks. This record contains the
In
The diagrams illustrates when the whole record is stored in the block, the record
SA
type would be 1. However, when the data is stored across two blocks, the data will be
stored in two records. The first record’s record type will be 2 and the next one’s record
e
Th
type will be 4.
21
20
©
However, if the data is stored across more than two blocks, shown in the diagram
below, data will be stored in more than two records. The first record’s record type will be
2. The middle record’s record type will be 3. The last one’s record type will be 4. Please
note if the record type is 2, 3 or 4, the Sequence Number field, the Count field and the Op
Type field won’t be present in the record.
gh
Ri
ll
Fu
ns
ai
et
rR
ho
ut
,A
The Sequence Number field stores the sequence number of the first key-value pair
te
in the record. The Count field in the record header shows how many key-value pairs are
itu
stored in the record. The Op Type field shows whether the key pair is added or deleted. If
st
it is 1, the key-value pair will be added. If it is 0, the key will be deleted. Please note if
In
In the record, the value is encoded with the protocol buffer format. The definition
e
which can be found in Chromium’s source code repository. The link can be found in
21
7. Protocol Buffers
©
Protobuf stores the encoding structure in a definition (*.proto) file, which can
allow the users to declare the field, type and the structure. An example, a snippet of
“notification_database_data.proto”, is shown as below. A full copy can be found in
Appendix A.
gh
Ri
ll
Fu
ns
ai
et
rR
ho
ut
7.1. Encoding
,A
In the Protobuf definition file, the data is defined in the format below. For
te
instance, in the snippet above, the first line is “optional string title = 1”. It means the type
itu
When Protobuf follows the definition file to encode the value, the encoded data
NS
Protobuf puts the field and type in one byte. The first five bits stores the field
21
number. The following three bits store type number. The following is an example.
20
00001000
©
The first five bits are 00001, which means the field number is 1. The following
three bits are 000, which means the type number is 0. It suggests this field’s type is
Varint, which is used for int32, int64, uint32, uint64, sint32, sint64, bool and num. The
list below shows the types and their meanings (Google, 2020).
gh
Ri
7.1.2. Varint
ll
Fu
Varint is an important encoding method used in many areas. Its flexibility can
allow it to represent a large integer.
ns
ai
This encoding method uses the first bit to determine whether the next byte is part
et
of value. Hence, it can help us determine how many bytes are used for this integer. The
rR
following is an example.
ho
0000 0100
ut
The first bit is 0. That means the next byte is not part of the value. In other words,
,A
this value only uses this byte. It uses two's complement to store the value, which is 4. The
te
following is another example.
itu
This first bit of the first byte is 1. It means the next byte is also part of the value.
In
The first bit of the second byte is 0. It means the following byte is not part of the value. In
NS
other words, only the first two bytes, 1000 0000 and 0000 0100, are used to store the
SA
value. In order to decode these two bytes, it is important to know how Varint stores the
value.
e
Th
Since the first bit is used to indicate whether the following byte is part the value,
21
it is not used to part of the value representation. Hence, the first bits of those two bytes
20
Varint stores the least significant group first. The order of these bytes needs to be
reversed, shown as below. Through two’s complement, the final value is 512.
000 0100 000 0000 = 0000 0010 0000 0000 = 512
7.1.3. Length-Limited
Protobuf uses the length-limited type to store strings, bytes, and Protobuf
structure data. This type is composed of two parts, data length in varint and data shown as
below.
[data length in Varint] [data]
gh
Ri
The following hexadecimal numerals is an example. The first hexadecimal
ll
Fu
numeral is 04, which means the length of the data is 4. The following four numbers is
data, which is “test” represented in hexadecimal.
ns
ai
04 74 65 73 74
et
7.1.4. Signed Integer
rR
Protobuf uses “zig-zags” encoding for signed integers. This encoding algorithm is
ho
also used in the V8 serialization mechanism, which will be covered in the following
ut
section.
,A
The following is a short list of the “zig-zags” encoding examples. It interleaves
te
positive numbers and negative numbers to corresponding numbers.
itu
0 0
In
-1 1
1 2
NS
-2 3
2 4
SA
-3 5
3 6
-4 7
e
4 8
Th
21
This encoding concept can be translated into the following encoding algorithm
20
Zig-Zag Algorithm
Encoding ([signed integer] >> [length of bits] - 1) ^ ([signed integer] << 1)
Decoding ([encoded code] >> 1) ^ -([encoded code] & 1)
gh
Ri
8. V8 Serialization
ll
Fu
V8 is a JavaScript and WebAssemby engine, used in Chrome & Node.js (V8,
ns
n.d.). V8 contains an encoding mechanism that can serialise the data. It has been
observed Chrome’s notification log file that may contain the data serialised with V8
ai
et
serialization mechanism.
rR
V8 serialization mechanism is similar to Protobuf. However, V8 serialization uses
ho
the “serialization tag” to define the data type. The following is an example.
ut
22 04 74 65 73 74
,A
In Protobuf, the field number and type are defined in the first byte. In V8
te
serialization, the field number is not included. The first byte defines the type of the data.
itu
In V8’s source code, it is called “serialization tag.” A list of the serialization tags is
st
included in Appendix B. In the example, the first byte is 22. That tag means “One Byte
In
String” data type. The following byte, 04, is the length of data. That means the following
NS
The field name and JavaScript object syntax can also be included in V8’s
e
6f 22 04 74 65 73 74 49 02 7b 01
21
The first tag, 6f, means the beginning of the JavaScript object. The following five
20
following tag, 49, means the int32 data type with “zig-zags” encoding algorithm. Hence,
the following byte, 02, means 1. The following tag, 7b, means the end of the JavaScript
object. The following byte, 01, means only one element in this object. To put the result in
the JavaScript object syntax, the following is what it looks like.
{ test: 1 }
6f 22 04 74 65 73 74 49 02 6f 22 03 61 62 63 49 06 7b 01 7b 02
The following is the decoded result represented in the JavaScript object syntax.
{ test: 1, { abc: 3 } }
gh
Ri
9. Conclusion
ll
Fu
Chrome stores notification data created by Push API in a LevelDB database. The
ns
relevant files of this database are placed in the “Platform Notifications” folder. The
experiment conducted with Chrome 88 reveals when the internet history is cleared, the
ai
et
data in that folder is not deleted.
rR
In order to parse Chrome’s notification data, the relevant values in the .ldb file
ho
or .log file need to be extracted. Depending on the tools, some supporting files, such as
ut
CURRENT and MANIFEST, may be required to perform this task.
,A
te
itu
st
In
NS
SA
e
Figure 1: notification_database_data.proto
Th
Those values are serialized data with Protobuf mechanism. Once the values are
21
Figure 2: 000003.log
gh
Ri
The notification data contains a lot of information. The example above shows the
ll
Fu
data contains the notification message, its origin, its URL for the icon and its creation
date. Those details can be very useful for the incident response purpose. The notification
ns
data also records whether the user clicks the notification. The full list of the content can
ai
be found in “notification_database_data.proto.” It is recommended to read though it if the
et
rR
notification-related incident occurs. The notification data may provide the critical
information for the investigation.
ho
ut
,A
te
itu
st
In
NS
SA
e
Th
21
20
©
gh
Ri
References
ll
Fu
MDN Contributors. (2020, December 18). Notification API. MDN Web Docs.
ns
https://developer.mozilla.org/en-US/docs/Web/API/Notifications_API
ai
MDN Contributors. (2021, January 3). Using the Notifications API. MDN Web Docs.
et
https://developer.mozilla.org/en-
rR
US/docs/Web/API/Notifications_API/Using_the_Notifications_API
ho
MDN Contributors. (2020, December 21). Push API. MDN Web Docs.
ut
https://developer.mozilla.org/en-US/docs/Web/API/Push_API
,A
Mineer, A. (2015, April 14). Stable Channel Update. Chrome Releases.
te
https://chromereleases.googleblog.com/2015/04/stable-channel-update_14.html
itu
Bommana, P. (2020, October 6). Stable Channel Update for Desktop. Chrome Releases.
st
https://chromereleases.googleblog.com/2020/10/stable-channel-update-for-
In
desktop.html
NS
Google. (n.d.). Turn notifications on or off. Google Chrome Help. Retrieved January 17,
2021, from
SA
https://support.google.com/chrome/answer/3220216?co=GENIE.Platform%3DDe
e
sktop&hl=en
Th
Medley, J. (2019, February 12). Web Push Notifications: Timely, Relevant, and Precise.
21
notifications
©
gh
Ri
Gaunt, M. (2020, July 24). Push Notifications on the Open Web. Web Updates.
ll
Fu
https://developers.google.com/web/updates/2015/03/push-notifications-on-the-
open-web
ns
Google. (n.d.). User Data Directory. Chromium Docs. Retrieved January 17, 2021, from
ai
https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md
et
rR
Ghemawat, S., & Dean, J. (2020, December 17). README.md. leveldb.
https://github.com/google/leveldb
ho
Google. (2020, December 17). impl.md. leveldb.
ut
https://github.com/google/leveldb/blob/master/doc/impl.md
,A
Google. (2017, March 1). leveldb File format. leveldb.
te
https://github.com/google/leveldb/blob/master/doc/table_format.md
itu
https://github.com/google/leveldb/commit/0b9a89f40efdd143fa1426e7d5cd997f6
In
7ba6361
NS
Grigorik, Ilya. (2012, February 06). SSTable and Log Structured Storage: LevelDB.
SA
igvita.com. https://www.igvita.com/2012/02/06/sstable-and-log-structured-
storage-leveldb/
e
Th
Microsoft. (2018, May 31). Monitored File Name Extensions. Microsoft Docs.
https://docs.microsoft.com/en-us/windows/win32/sr/monitored-file-
21
extensions?redirectedfrom=MSDN
20
Grogan, David. (2013, September 17). Changing file extension from .sst to .ldb. Google
©
Groups. https://groups.google.com/g/leveldb/c/u9izbG-pDis
Mumford, Chris. (2017, March 1). leveldb Log format. LevelDB.
https://github.com/google/leveldb/blob/master/doc/log_format.md
Google. (2020, Oct 27). notification_database_data.proto. Chromium.
https://github.com/chromium/chromium/blob/master/content/browser/notification
s/notification_database_data.proto
Google. (2020, June 23). Frequently Asked Questions. Protocol Buffers.
https://developers.google.com/protocol-buffers/docs/faq
gh
Ri
Google. (2020, December 21). Encoding. Protocol Buffers.
ll
Fu
https://developers.google.com/protocol-buffers/docs/encoding
V8. (n.d.). What is V8?. Home. Retrieved January 25, 2021, from https://v8.dev/
ns
ai
et
rR
ho
ut
,A
te
itu
st
In
NS
SA
e
Th
21
20
©
gh
Ri
Appendix A
ll
Fu
notification_database_data.proto
ns
A copy of “notification_database_data.proto” from
ai
https://github.com/chromium/chromium/blob/master/content/browser/notifications/notific
et
ation_database_data.proto is provided as below.
rR
// Copyright 2015 The Chromium Authors. All rights reserved.
ho
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
ut
syntax = "proto2";
// Next tag: 15
message NotificationDatabaseDataProto {
NS
enum ClosedReason {
USER = 0;
DEVELOPER = 1;
SA
UNKNOWN = 2;
}
e
gh
Ri
optional Type type = 4;
ll
optional string placeholder = 5;
Fu
}
ns
// buffer meant to serialize the blink::PlatformNotificationData structure.
//
ai
// Next tag: 17
message NotificationData {
et
enum Direction {
rR
LEFT_TO_RIGHT = 0;
RIGHT_TO_LEFT = 1;
AUTO = 2;
ho
}
ut
optional string title = 1;
optional Direction direction = 2;
,A
optional string lang = 3;
optional string body = 4;
optional string tag = 5;
te
optional string image = 15;
itu
optional string icon = 6;
optional string badge = 14;
repeated int32 vibration_pattern = 9 [packed=true];
st
gh
Ri
Appendix B
ll
Fu
V8’s Serialization Tags
ns
A list of serialization tags from https://github.com/v8/v8/blob/master/src/objects/value-
ai
serializer.cc is provided as below.
et
rR
enum class SerializationTag : uint8_t {
// version:uint32_t (if at beginning of data, sets version > 0)
ho
kVersion = 0xFF,
// ignore
ut
kPadding = '\0',
// refTableSize:uint32_t (previously used for sanity checks; safe to ignore)
,A
kVerifyObjectCount = '?',
// Oddballs (no data).
te
kTheHole = '-',
itu
kUndefined = '_',
kNull = '0',
st
kTrue = 'T',
kFalse = 'F',
In
kInt32 = 'I',
// Number represented as 32-bit unsigned integer, varint-encoded
SA
// Host byte order is used (N.B. this makes the format non-portable).
kDouble = 'N',
// BigInt. Bitfield:uint32_t, then raw digits storage.
21
kBigInt = 'Z',
// byteLength:uint32_t, then raw data
20
kUtf8String = 'S',
kOneByteString = '"',
©
kTwoByteString = 'c',
// Reference to a serialized object. objectID:uint32_t
kObjectReference = '^',
// Beginning of a JS object.
kBeginJSObject = 'o',
// End of a JS object. numProperties:uint32_t
kEndJSObject = '{',
// Beginning of a sparse JS array. length:uint32_t
// Elements and properties are written as key/value pairs, like objects.
kBeginSparseJSArray = 'a',
// End of a sparse JS array. numProperties:uint32_t length:uint32_t
kEndSparseJSArray = '@',
// Beginning of a dense JS array. length:uint32_t
// |length| elements, followed by properties as key/value pairs
kBeginDenseJSArray = 'A',
// End of a dense JS array. numProperties:uint32_t length:uint32_t
gh
Ri
kEndDenseJSArray = '$',
ll
// Date. millisSinceEpoch:double
Fu
kDate = 'D',
// Boolean object. No data.
ns
kTrueObject = 'y',
kFalseObject = 'x',
ai
// Number object. value:double
et
kNumberObject = 'n',
// BigInt object. Bitfield:uint32_t, then raw digits storage.
rR
kBigIntObject = 'z',
// String object, UTF-8 encoding. byteLength:uint32_t, then raw data.
ho
kStringObject = 's',
// Regular expression, UTF-8 encoding. byteLength:uint32_t, raw data,
ut
// flags:uint32_t.
,A
kRegExp = 'R',
// Beginning of a JS map. te
kBeginJSMap = ';',
// End of a JS map. length:uint32_t.
itu
kEndJSMap = ':',
// Beginning of a JS set.
st
kBeginJSSet = '\'',
In
// For typed arrays, byteOffset and byteLength must be divisible by the size
// of the element.
21
// The following tags are reserved because they were in use in Chromium before
// the kHostObject tag was introduced in format version 13, at
// v8 refs/heads/master@{#43466}
gh
Ri
// chromium/src refs/heads/master@{#453568}
ll
//
Fu
// They must not be reused without a version check to prevent old values from
// starting to deserialize incorrectly. For simplicity, it's recommended to
ns
// avoid them altogether.
//
ai
// This is the set of tags that existed in SerializationTag.h at that time and
et
// still exist at the time of this writing (i.e., excluding those that were
// removed on the Chromium side because there should be no real user data
rR
// containing them).
//
ho
// It might be possible to also free up other tags which were never persisted
// (e.g. because they were used only for transfer) in the future.
ut
kLegacyReservedMessagePort = 'M',
,A
kLegacyReservedBlob = 'b',
kLegacyReservedBlobIndex = 'i', te
kLegacyReservedFile = 'f',
kLegacyReservedFileIndex = 'e',
itu
kLegacyReservedDOMFileSystem = 'd',
kLegacyReservedFileList = 'l',
st
kLegacyReservedFileListIndex = 'L',
In
kLegacyReservedImageData = '#',
kLegacyReservedImageBitmap = 'g',
NS
kLegacyReservedImageBitmapTransfer = 'G',
kLegacyReservedOffscreenCanvas = 'H',
SA
kLegacyReservedCryptoKey = 'K',
kLegacyReservedRTCCertificate = 'k',
};
e
Th
21
20
©