Difference between revisions of "Useful SQL Queries"

From truxwiki.com
Jump to navigation Jump to search
 
(110 intermediate revisions by the same user not shown)
Line 1: Line 1:
 
The following SQL queries were found to be useful when developing/debugging Truxton.
 
The following SQL queries were found to be useful when developing/debugging Truxton.
 +
 +
=Database Server=
 +
This section deals with tasks required at the server level.
 +
 +
==Number of Connections==
 +
<source lang="sql">
 +
SELECT COUNT(DISTINCT(numbackends)) FROM pg_stat_database
 +
</source>
 +
 +
==Maximum Number of Connections==
 +
Run this query as an administrator.
 +
<source lang="sql">
 +
SHOW max_connections
 +
</source>
 +
 +
You can change this by:
 +
<source lang="sql">
 +
ALTER SYSTEM SET max_connections = '500';
 +
</source>
 +
 +
You must restart the Postgres Service in order for the change to take affect.
 +
 +
==All Connection Information==
 +
This query will show you the number of active connections, maximum number of connections (server configuration) and the number of connections reserved for the super user.
 +
<source lang="sql">
 +
SELECT * FROM
 +
(SELECT COUNT(*) "Current Number of Connections" FROM pg_stat_activity) q1,
 +
(SELECT setting::int "Reserved for Superuser" FROM pg_settings WHERE name=$$superuser_reserved_connections$$) q2,
 +
(SELECT setting::int "Maximum Number of Connections" FROM pg_settings WHERE name=$$max_connections$$) q3;
 +
</source>
 +
 +
==Current Connections==
 +
This query will show you the current connections.
 +
<source lang="sql">
 +
SELECT datname,
 +
      usename,
 +
      application_name,
 +
      client_addr,
 +
      backend_start
 +
FROM pg_stat_activity
 +
</source>
 +
 +
==Current Activity==
 +
Ever wonder what the server is doing?
 +
This will show you what each connection is doing.
 +
<source lang="sql">
 +
SELECT state_change,
 +
      age(localtimestamp, state_change),
 +
      datname,
 +
      client_addr,
 +
      application_name,
 +
      state,
 +
      query
 +
FROM pg_stat_activity
 +
ORDER BY state,
 +
        age DESC,
 +
        datname ASC
 +
</source>
 +
 +
==Terminate Connection==
 +
PostgreSQL 12 will sometimes leave zombie connections open for days.
 +
Here's how to kill them.
 +
Each connection is a different process on the server.
 +
 +
===Kill Connection By Process ID===
 +
This will kill a connection to the database given the process id of the offending <code>postgres.exe</code> process.
 +
<source lang="sql">
 +
SELECT pg_terminate_backend(pid)
 +
FROM pg_stat_activity
 +
WHERE pid = '29564'
 +
</source>
 +
 +
===Kill Connection By Application Name===
 +
Truxton processes will identify themselves via the application name, making it easier to kill them.
 +
<source lang="sql">
 +
SELECT pg_terminate_backend(pid)
 +
FROM pg_stat_activity
 +
WHERE application_name like 'Truxton Carve%'
 +
</source>
 +
 +
==Find Unused Indexes==
 +
<source lang="sql">
 +
  SELECT relname AS table,
 +
        indexrelname AS index,
 +
        pg_size_pretty(pg_relation_size(i.indexrelid)) AS index_size,
 +
        idx_scan AS index_scans
 +
    FROM pg_stat_user_indexes ui
 +
    JOIN pg_index i ON ui.indexrelid = i.indexrelid
 +
  WHERE NOT indisunique
 +
    AND idx_scan = 0
 +
    AND pg_relation_size(relid) > 5 * 8192
 +
ORDER BY pg_relation_size(i.indexrelid) / nullif(idx_scan, 0) DESC NULLS FIRST,
 +
        pg_relation_size(i.indexrelid) DESC;
 +
</source>
 +
 +
==Rename a Database==
 +
Sometimes, you may want to stash your current instance of Truxton so you can stand up another and do some testing.
 +
You can rename the database, do your work, then rename it back when you are done.
 +
Here's the SQL to rename a database.
 +
 +
<source lang="sql">
 +
ALTER DATABASE "Truxton" RENAME TO "SavedTruxton";
 +
</source>
 +
 +
Where it gets tricky is you can't rename a database you have a connection to.
 +
You can get around this problem by stopping [[Truxton Service|Truxton services]] on the network, closing all Analyst desktops, and use the <code>[https://www.postgresql.org/docs/current/app-psql.html psql.exe]</code> command line tool.
 +
 +
<source lang="bat">
 +
"C:\Program Files\PostgreSQL\15\bin\psql.exe" --host=localhost --port=5432 --username=postgres --quiet "--command=ALTER DATABASE ""Truxton"" RENAME TO ""SavedTruxton"";"
 +
</source>
 +
 +
==Create a Database==
 +
Here are the steps to manually create a Truxton database.
 +
 +
Create the database.
 +
<source lang="bat">
 +
"C:\Program Files\PostgreSQL\15\bin\psql.exe" --host=localhost --port=5432 --username=postgres --quiet "--command=CREATE DATABASE ""Truxton"" TEMPLATE = template0 ENCODING = 'UTF8' LC_COLLATE = 'C' LC_CTYPE = 'C' CONNECTION LIMIT = -1;"
 +
</source>
 +
 +
Never create something without documenting it.
 +
Add a comment to the newly created database.
 +
<source lang="bat">
 +
"C:\Program Files\PostgreSQL\15\bin\psql.exe" --host=localhost --port=5432 --username=postgres --quiet "--command=COMMENT ON DATABASE ""Truxton"" IS 'This is the Truxton database. It holds all of the data about files, artifacts and investigative work.';"
 +
</source>
 +
 +
Now that the database is created, we must add the tables to it to store the Truxton data.
 +
All of the SQL scripts required to create a new instance of Truxton are in the <code>C:\Program Files\Truxton\Database</code> folder.
 +
<source lang="bat">
 +
"C:\Program Files\PostgreSQL\15\bin\psql.exe" --dbname=Truxton --host=localhost --port=5432 --username=postgres --quiet "--file=C:\Program Files\Truxton\Database\create-tables.sql"
 +
"C:\Program Files\PostgreSQL\15\bin\psql.exe" --dbname=Truxton --host=localhost --port=5432 --username=postgres --quiet "--file=C:\Program Files\Truxton\Database\create-tables_1.sql"
 +
"C:\Program Files\PostgreSQL\15\bin\psql.exe" --dbname=Truxton --host=localhost --port=5432 --username=postgres --quiet "--file=C:\Program Files\Truxton\Database\create-tables_2.sql"
 +
</source>
 +
 +
The last step in the process is to populate the tables with default data.
 +
<source lang="bat">
 +
"C:\Program Files\PostgreSQL\15\bin\psql.exe" --dbname=Truxton --host=localhost --port=5432 --username=postgres --quiet "--file=C:\Program Files\Truxton\Database\insert-data.sql"
 +
"C:\Program Files\PostgreSQL\15\bin\psql.exe" --dbname=Truxton --host=localhost --port=5432 --username=postgres --quiet "--file=C:\Program Files\Truxton\Database\insert-data_1.sql"
 +
"C:\Program Files\PostgreSQL\15\bin\psql.exe" --dbname=Truxton --host=localhost --port=5432 --username=postgres --quiet "--file=C:\Program Files\Truxton\Database\insert-data_2.sql"
 +
"C:\Program Files\PostgreSQL\15\bin\psql.exe" --dbname=Truxton --host=localhost --port=5432 --username=postgres --quiet "--file=C:\Program Files\Truxton\Database\insert-data_3.sql"
 +
"C:\Program Files\PostgreSQL\15\bin\psql.exe" --dbname=Truxton --host=localhost --port=5432 --username=postgres --quiet "--file=C:\Program Files\Truxton\Database\insert-data_4.sql"
 +
</source>
 +
 +
The next time you start the [[Truxton Service]], the rest of the tables will be populated with dynamic.
 +
 +
==Determine Table Packing==
 +
Postgres will pad pages in the database.
 +
Pad bytes are wasted space.
 +
To minimize the wasted space, use the following query to help guide the order in which you create your columns.
 +
<source lang="sql">
 +
SELECT a.attname,
 +
      t.typname,
 +
      t.typalign,
 +
      t.typlen
 +
  FROM pg_class c
 +
  JOIN pg_attribute a ON (a.attrelid = c.oid)
 +
  JOIN pg_type t ON (t.oid = a.atttypid)
 +
WHERE c.relname = 'File'
 +
  AND a.attnum >= 0
 +
ORDER BY t.typlen DESC, t.typname ASC, a.attname ASC
 +
</source>
  
 
=Files=
 
=Files=
Line 6: Line 166:
 
==Human Readable List==
 
==Human Readable List==
 
This will list files in a human readable format. The <code>WHERE</code> clause is commented out so you can add your own filter criteria.
 
This will list files in a human readable format. The <code>WHERE</code> clause is commented out so you can add your own filter criteria.
<syntaxhighlight lang="sql">
+
<source lang="sql">
SELECT
+
SELECT "File"."ID",
"File"."ID",
+
      "File"."MediaID",
"File"."MediaID",
+
      "File"."ParentFileID",
"File"."ParentFileID",
+
      "Origin"."Name" AS "Origin",
"Origin"."Name" AS "Origin",
+
      "FileType"."ShortName" AS "FileType",
"FileType"."ShortName" AS "FileType",
+
      "FN2"."Name",
"FN2"."Name",
+
      "File"."OSLength",
"File"."OSLength",
+
      "File"."HashID",
"File"."HashID",
+
      "File"."RawEntropy" * 0.00024414502395702993865779595324564 AS "Entropy",
"File"."RawEntropy" * 0.00024414502395702993865779595324564 AS "Entropy",
+
      "Filename"."Name" AS "Path",
"Filename"."Name" AS "Path",
+
      "File"."FullPathID",
"File"."FullPathID",
+
      "File"."FileTypeID",
"File"."FileTypeID",
+
      to_hex( "File"."Signature" ) AS "Signature",
to_hex( "File"."Signature" ) AS "Signature",
+
      to_hex( "File"."PhysicalDiskOffset" ) AS "DiskOffset",
to_hex( "File"."PhysicalDiskOffset" ) AS "DiskOffset",
+
      "FileType"."LongName" AS "FileTypeDescription",
"FileType"."LongName" AS "FileTypeDescription",
+
      "File"."Created",
"File"."Created",
+
      "File"."LastWrite",
"File"."LastWrite",
+
      "ContentStatus"."Name" AS "CStatus",
"ContentStatus"."Name" AS "CStatus",
+
      "File"."NumberOfChildren",
"File"."NumberOfChildren",
+
      "File"."FilenameID"
"File"."FilenameID"
 
 
FROM "File"
 
FROM "File"
 
LEFT OUTER JOIN "FileType" ON ( "File"."FileTypeID" = "FileType"."ID" )
 
LEFT OUTER JOIN "FileType" ON ( "File"."FileTypeID" = "FileType"."ID" )
Line 36: Line 195:
 
--WHERE "File"."Signature" = -623191334
 
--WHERE "File"."Signature" = -623191334
 
ORDER BY "File"."ID"
 
ORDER BY "File"."ID"
</syntaxhighlight>
+
</source>
  
 
==Files with Depot Locations==
 
==Files with Depot Locations==
<syntaxhighlight lang="sql">
+
<source lang="sql">
 
SELECT "File"."ID",
 
SELECT "File"."ID",
"File"."ParentFileID",
+
      "File"."ParentFileID",
"File"."FileTypeID",
+
      "File"."FileTypeID",
"FileType"."ShortName",
+
      "FileType"."ShortName",
"Filename"."Name",
+
      "Filename"."Name",
"Content"."Offset",
+
      "Content"."Offset",
"Content"."Length",
+
      "Content"."Length",
"Depot"."Filename"
+
      "Depot"."Filename"
FROM
+
FROM "File",
"File",  
+
    "Depot",
"Depot",
+
    "Filename",
"Filename",
+
    "Content",
"Content",
+
    "FileType"
"FileType"
+
WHERE "File"."HashID" = "Content"."Hash"
 +
  AND "File"."FilenameID" = "Filename"."ID"
 +
  AND "Content"."DepotID" = "Depot"."ID"
 +
  AND "File"."FileTypeID" = "FileType"."ID"
 +
</source>
 +
 
 +
==Carved Files==
 +
This query is useful when examining Truxton's file carving quality.
 +
This particular query is looking for [[Origin|carved]] [[Type_JPEG|JPEG]] and [[Type_JPEGWithExif|JPEG with EXIF]] files in a specific piece of media.
 +
<source lang="sql">
 +
SELECT DISTINCT("File"."ID"),
 +
      "FileType"."ShortName",
 +
      "Filename"."Name",
 +
      "Content"."Length",
 +
      "Content"."Offset",
 +
      "File"."PhysicalDiskOffset",
 +
      "File"."HashID" AS "MD5",
 +
      "MD5Hash"."SHA1",
 +
      "Depot"."Filename" AS "Depot Path"
 +
FROM "File",
 +
    "Depot",
 +
    "Filename",
 +
    "Content",
 +
    "FileType",
 +
    "MD5Hash"
 
WHERE "File"."HashID" = "Content"."Hash"  
 
WHERE "File"."HashID" = "Content"."Hash"  
AND "File"."FilenameID" = "Filename"."ID"
+
  AND "File"."FilenameID" = "Filename"."ID"
AND "Content"."DepotID" = "Depot"."ID"
+
  AND "Content"."DepotID" = "Depot"."ID"
AND "File"."FileTypeID" = "FileType"."ID"
+
  AND "File"."FileTypeID" = "FileType"."ID"
</syntaxhighlight>
+
  AND "MD5Hash"."ID" = "File"."HashID"
 +
  AND "File"."OriginID" = 3
 +
  AND "File"."FileTypeID" IN (203, 204)
 +
  AND "File"."MediaID" = '27cfac98-af34-1cc9-6812-a18573e44e0e'::uuid
 +
  ORDER BY "Depot Path" ASC, "Offset" ASC
 +
</source>
  
 
==Files Truxton Can Exploit==
 
==Files Truxton Can Exploit==
<syntaxhighlight lang="sql">
+
<source lang="sql">
SELECT
+
SELECT "ShortName",
"ShortName",
+
      "LongName"
"LongName"
 
 
FROM "FileType"
 
FROM "FileType"
 
WHERE "ID" IN
 
WHERE "ID" IN
Line 73: Line 260:
 
)
 
)
 
ORDER BY "ShortName"
 
ORDER BY "ShortName"
</syntaxhighlight>
+
</source>
  
This one produces a single line of output that is easier for a human to read"
+
This one produces a single line of output that is easier for a human to read.
<syntaxhighlight lang="sql">
+
<source lang="sql">
SELECT CONCAT( "ShortName", ' - ', "LongName")
+
SELECT CONCAT( "ShortName", ' - ', "LongName" )
 
FROM "FileType"
 
FROM "FileType"
 
WHERE "ID" IN
 
WHERE "ID" IN
Line 86: Line 273:
 
)
 
)
 
ORDER BY "ShortName"
 
ORDER BY "ShortName"
</syntaxhighlight>
+
</source>
 
'''NOTE''' the <code>pfe</code> item is a bug. There should NOT be spaces in that name.
 
'''NOTE''' the <code>pfe</code> item is a bug. There should NOT be spaces in that name.
 +
 +
==Number of Files of Type in Media==
 +
This will produce a list of the count of files of a particular type in media.
 +
<source lang="sql">
 +
  SELECT "MediaID",
 +
          COUNT("FileTypeID") AS "FileCount"
 +
    FROM "File"
 +
  WHERE "FileTypeID" = 201
 +
GROUP BY "MediaID",
 +
        "FileTypeID"
 +
ORDER BY COUNT("FileTypeID") DESC
 +
</source>
 +
 +
==Find Investigation Name given File ID==
 +
<source lang="sql>
 +
SELECT "Investigation"."Name"
 +
FROM "Investigation"
 +
LEFT OUTER JOIN "InvestigationMedia" ON ("InvestigationMedia"."InvestigationID" = "Investigation"."ID")
 +
LEFT OUTER JOIN "Media" ON ("Media"."ID" = "InvestigationMedia"."MediaID")
 +
LEFT OUTER JOIN "File" ON ("File"."MediaID" = "Media"."ID")
 +
WHERE "File"."ID" = '645ff09a-6425-1bf8-8542-9bdc00035f8c'::uuid
 +
</source>
 +
 +
==Find Investigations given File Signature==
 +
The following query will find all investigations that contain a file that starts with the byte sequence of <code>00 00 03 00</code>
 +
<source lang="sql">
 +
SELECT DISTINCT( "Investigation"."Name" )
 +
FROM "Investigation"
 +
LEFT OUTER JOIN "InvestigationMedia" ON ("InvestigationMedia"."InvestigationID" = "Investigation"."ID")
 +
LEFT OUTER JOIN "Media" ON ("Media"."ID" = "InvestigationMedia"."MediaID")
 +
LEFT OUTER JOIN "File" ON ("File"."MediaID" = "Media"."ID")
 +
WHERE "File"."Signature" = cast(x'00000300' as int)
 +
</source>
 +
 +
==Find Investigations given File Name Pattern==
 +
The following query will find all investigations that contain a CSV file.
 +
<source lang="sql">
 +
SELECT DISTINCT "Investigation"."Name"
 +
FROM "Investigation"
 +
LEFT OUTER JOIN "InvestigationMedia" ON ("InvestigationMedia"."InvestigationID" = "Investigation"."ID")
 +
LEFT OUTER JOIN "Media" ON ("Media"."ID" = "InvestigationMedia"."MediaID")
 +
LEFT OUTER JOIN "File" ON ("File"."MediaID" = "Media"."ID")
 +
LEFT OUTER JOIN "Filename" ON ("Filename"."ID" = "File"."FilenameID" )
 +
WHERE "Filename"."Name" ILIKE  '%.csv'
 +
</source>
 +
 +
==Find File Names for File Type==
 +
This query will show you all of the names of files of type [[Type_Android_Battery_History|Android Battery History]]
 +
<source lang="sql">
 +
SELECT DISTINCT "Filename"."Name"
 +
FROM "File"
 +
LEFT OUTER JOIN "Filename" ON ("Filename"."ID" = "File"."FilenameID")
 +
WHERE "FileTypeID" = 1021
 +
</source>
 +
 +
==Make a Hashset From Loaded Data==
 +
When you get a lot of data loaded, you will notice that there's lots of files that are repeated.
 +
These files will probably never make it into the ]https://www.nist.gov/itl/ssd/software-quality-group/national-software-reference-library-nsrl NSRL].
 +
Using this query, you can create your own hashset to eliminate these files in future loads.
 +
 +
This queries the database for hashes of files that haven't already been eliminated and occurs more than 50 times.
 +
<source lang="sql">
 +
SELECT UPPER(SUBSTRING( "HashID"::text FROM 1 FOR 8 ) ||
 +
SUBSTRING( "HashID"::text FROM 10 FOR 4 ) ||
 +
SUBSTRING( "HashID"::text FROM 15 FOR 4 ) ||
 +
SUBSTRING( "HashID"::text FROM 20 FOR 4 ) ||
 +
SUBSTRING( "HashID"::text FROM 25 FOR 12 )) AS "Hash"
 +
FROM "File"
 +
WHERE "OriginID" = 1 AND
 +
"ContentStatusID" = 1 AND
 +
"HashID" NOT IN ( '00000000-0000-0000-0000-000000000000'::uuid, 'd41d8cd9-8f00-b204-e980-0998ecf8427e'::uuid )
 +
GROUP BY "HashID"
 +
HAVING COUNT("HashID") > 50
 +
ORDER BY "Hash" ASC</source>
  
 
=Logs=
 
=Logs=
 
==All Messages==
 
==All Messages==
<syntaxhighlight lang="sql">
+
<source lang="sql">
SELECT
+
SELECT "Log"."MediaID",
"Log"."MediaID",
+
      "Log"."When",
"Log"."When",
+
      "T"."String" AS "Type",
"T"."String" AS "Type",
+
      "S"."String" AS "Source",
"S"."String" AS "Source",
+
      "Log"."ProcessID",
"Log"."ProcessID",
+
      "Log"."ThreadID",
"Log"."ThreadID",
+
      "C"."String" AS "Client",
"C"."String" AS "Client",
+
      "U"."String" AS "User",
"U"."String" AS "User",
+
      "M"."String" AS "Message"
"M"."String" AS "Message"
+
FROM "Log",
FROM
+
    "LogString" AS "S",
"Log",
+
    "LogString" AS "C",
"LogString" AS "S",
+
    "LogString" AS "U",
"LogString" AS "C",
+
    "LogString" AS "M",
"LogString" AS "U",
+
    "LogString" AS "T"
"LogString" AS "M",
+
WHERE "Log"."LogStringSourceID" = "S"."ID"  
"LogString" AS "T"
+
  AND "Log"."LogStringClientID" = "C"."ID"  
WHERE
+
  AND "Log"."LogStringUserID" = "U"."ID"  
"Log"."LogStringSourceID" = "S"."ID"  
+
  AND "Log"."LogStringMessageID" = "M"."ID"  
AND "Log"."LogStringClientID" = "C"."ID"  
+
  AND "Log"."LogStringTypeID" = "T"."ID"
AND "Log"."LogStringUserID" = "U"."ID"  
 
AND "Log"."LogStringMessageID" = "M"."ID"  
 
AND "Log"."LogStringTypeID" = "T"."ID"
 
 
-- AND "Log"."LogStringTypeID" <> 2 -- Don't show Info messages
 
-- AND "Log"."LogStringTypeID" <> 2 -- Don't show Info messages
 
-- AND "Log"."MediaID" = '11111111-1111-1111-1111-000000000001'::uuid
 
-- AND "Log"."MediaID" = '11111111-1111-1111-1111-000000000001'::uuid
ORDER BY
+
ORDER BY "Log"."MediaID",
"Log"."MediaID",
+
        "Log"."When"
"Log"."When"
+
</source>
</syntaxhighlight>
 
  
 
==Only Error and Warnings==
 
==Only Error and Warnings==
<syntaxhighlight lang="sql">
+
<source lang="sql">
SELECT
+
SELECT "Log"."MediaID",
"Log"."MediaID",
+
      "Log"."When",
"Log"."When",
+
      "Log"."LogStringTypeID",
"Log"."LogStringTypeID",
+
      "T"."String" AS "Type",
"T"."String" AS "Type",
+
      "S"."String" AS "Source",
"S"."String" AS "Source",
+
      "Log"."ProcessID",
"Log"."ProcessID",
+
      "Log"."ThreadID",
"Log"."ThreadID",
+
      "C"."String" AS "Client",
"C"."String" AS "Client",
+
      "U"."String" AS "User",
"U"."String" AS "User",
+
      "M"."String" AS "Message"
"M"."String" AS "Message"
+
FROM "Log",
FROM
+
    "LogString" AS "S",
"Log",
+
    "LogString" AS "C",
"LogString" AS "S",
+
    "LogString" AS "U",
"LogString" AS "C",
+
    "LogString" AS "M",
"LogString" AS "U",
+
    "LogString" AS "T"
"LogString" AS "M",
+
WHERE "Log"."LogStringSourceID" = "S"."ID"  
"LogString" AS "T"
+
  AND "Log"."LogStringClientID" = "C"."ID"  
WHERE
+
  AND "Log"."LogStringUserID" = "U"."ID"  
"Log"."LogStringSourceID" = "S"."ID"  
+
  AND "Log"."LogStringMessageID" = "M"."ID"  
AND "Log"."LogStringClientID" = "C"."ID"  
+
  AND "Log"."LogStringTypeID" = "T"."ID"
AND "Log"."LogStringUserID" = "U"."ID"  
+
  AND "Log"."LogStringTypeID" IN ( 0, 1 ) -- Don't show Info messages
AND "Log"."LogStringMessageID" = "M"."ID"  
 
AND "Log"."LogStringTypeID" = "T"."ID"
 
AND "Log"."LogStringTypeID" IN ( 0, 1 ) -- Don't show Info messages
 
 
-- AND "Log"."MediaID" = '11111111-1111-1111-1111-000000000001'::uuid
 
-- AND "Log"."MediaID" = '11111111-1111-1111-1111-000000000001'::uuid
ORDER BY
+
ORDER BY "Log"."MediaID",
"Log"."MediaID",
+
        "Log"."When"
"Log"."When"
+
</source>
</syntaxhighlight>
+
 
 +
==Load Log==
 +
The following query is used to generate the Load Log.
 +
You can run this query as media is being loaded to peek at the log.
 +
 
 +
<source lang="sql">
 +
SELECT "Log"."When",
 +
      "T"."String" AS "Type",
 +
      "S"."String" AS "Source",
 +
      "Log"."ProcessID",
 +
      "Log"."ThreadID",
 +
      "C"."String" AS "Client",
 +
      "U"."String" AS "User",
 +
      "M"."String" AS "Message",
 +
      "Log"."LogStringMessageID"
 +
FROM "Log",
 +
    "LogString" AS "S",
 +
    "LogString" AS "C",
 +
    "LogString" AS "U",
 +
    "LogString" AS "M",
 +
    "LogString" AS "T"
 +
WHERE "Log"."MediaID" = '11111111-1111-1111-1111-000000000001'::uuid
 +
  AND "Log"."LogStringSourceID" = "S"."ID"
 +
  AND "Log"."LogStringClientID" = "C"."ID"
 +
  AND "Log"."LogStringUserID" = "U"."ID"
 +
  AND "Log"."LogStringMessageID" = "M"."ID"
 +
  AND "Log"."LogStringTypeID" = "T"."ID"
 +
ORDER BY "Log"."When",
 +
        "Source";
 +
</source>
  
 
=Depots=
 
=Depots=
 
These queries will tell you if you're missing depot file entries.
 
These queries will tell you if you're missing depot file entries.
 
==Missing Content Depots==
 
==Missing Content Depots==
<syntaxhighlight lang="sql">
+
<source lang="sql">
 
SELECT DISTINCT( "DepotID" )
 
SELECT DISTINCT( "DepotID" )
 
FROM "Content"
 
FROM "Content"
WHERE
+
WHERE "DepotID" <> '00000000-0000-0000-0000-000000000000'::uuid
"DepotID" <> '00000000-0000-0000-0000-000000000000'::uuid
+
  AND "DepotID" NOT IN ( SELECT "ID" FROM "Depot" )
AND "DepotID" NOT IN ( SELECT "ID" FROM "Depot" )
 
 
ORDER BY "DepotID"
 
ORDER BY "DepotID"
</syntaxhighlight>
+
</source>
  
 
==Missing Image Depots==
 
==Missing Image Depots==
<syntaxhighlight lang="sql">
+
<source lang="sql">
ELECT DISTINCT( "DepotID" )
+
SELECT DISTINCT( "DepotID" )
 
FROM "Image"
 
FROM "Image"
WHERE  
+
WHERE "DepotID" <> '00000000-0000-0000-0000-000000000000'::uuid
"DepotID" <> '00000000-0000-0000-0000-000000000000'::uuid
+
  AND "DepotID" NOT IN
AND "DepotID" NOT IN
+
  (
(
+
    SELECT "ID" FROM "Depot"
  SELECT "ID" FROM "Depot"
+
  )
)
 
 
ORDER BY "DepotID"
 
ORDER BY "DepotID"
</syntaxhighlight>
+
</source>
  
 
==Depot IDs in Use==
 
==Depot IDs in Use==
<syntaxhighlight lang="sql">
+
<source lang="sql">
 
SELECT DISTINCT "DepotID" AS "ID" FROM
 
SELECT DISTINCT "DepotID" AS "ID" FROM
 
(
 
(
Line 194: Line 475:
 
   SELECT "ThumbnailDepotID" AS "DepotID" FROM "ReviewItem"
 
   SELECT "ThumbnailDepotID" AS "DepotID" FROM "ReviewItem"
 
) AS "SubSelect"
 
) AS "SubSelect"
</syntaxhighlight>
+
</source>
  
 
==Depot IDs Not in Use==
 
==Depot IDs Not in Use==
<syntaxhighlight lang="sql">
+
<source lang="sql">
SELECT "ID" FROM "Depot" WHERE "ID" NOT IN ( SELECT "ID" FROM get_id_of_depots_in_use() )
+
SELECT "ID"
</syntaxhighlight>
+
FROM "Depot"
 +
WHERE "ID" NOT IN
 +
(
 +
SELECT DISTINCT "DepotID" AS "ID" FROM
 +
(
 +
  SELECT "DepotID" FROM "Content"
 +
  UNION
 +
  SELECT "DepotID" FROM "Free"
 +
  UNION
 +
  SELECT "DepotID" FROM "Image"
 +
  UNION
 +
  SELECT "DepotID" FROM "Slack"
 +
  UNION
 +
  SELECT "ThumbnailDepotID" AS "DepotID" FROM "ReviewItem"
 +
) AS "SubSelect"
 +
)
 +
</source>
 +
 
 +
==URI in Filename Column==
 +
To detect when an ETL has put bad data into the <code>[Depot]</code> table.
 +
<source lang="sql">
 +
SELECT "ID",
 +
      "Filename",
 +
      "URI",
 +
      "DepotTypeID",
 +
      "MaximumSize",
 +
      "Size",
 +
      "IsLongTerm",
 +
      "CryptoKey",
 +
      "DepotStatusID"
 +
FROM "Depot"
 +
WHERE "Filename" LIKE '\\%'
 +
</source>
 +
 
 +
==Change Depot Folder==
 +
If you move your depot folder, the path will have to be updated in the database.
 +
This statement will change the depot drive letter from D to E.
  
 +
<source lang="sql">
 +
UPDATE "Depot"
 +
SET "Filename" = REPLACE( "Filename", 'D:\\', 'E:\\')
 +
WHERE "Filename" LIKE 'D:\\%'
 +
</source>
 
==Change Depot URI==
 
==Change Depot URI==
 
When you move depots to a new share, the database needs to be fixed up.
 
When you move depots to a new share, the database needs to be fixed up.
<syntaxhighlight lang="sql">
+
This sample is what you would run if you need to change the machine name <code>SOFS-01</code> to a fully qualified [https://en.wikipedia.org/wiki/Domain_Name_System DNS] name.
 +
<source lang="sql">
 
UPDATE "Depot"
 
UPDATE "Depot"
 
SET "URI" = REPLACE( "URI", '\\SOFS-01\', '\\sofs-01.truxton.lab\Depot\')
 
SET "URI" = REPLACE( "URI", '\\SOFS-01\', '\\sofs-01.truxton.lab\Depot\')
</syntaxhighlight>
+
WHERE "URI" LIKE '\\\\sofs-01.truxton.lab%'
 +
</source>
 +
 
 +
If you move depots to Amazon S3 Storage:
 +
<source lang="sql">
 +
UPDATE "Depot"
 +
SET "URI" = REPLACE( "URI", '\\Machine\Share\Depot\', 'https://depot-bucket-a730.s3.amazonaws.com/' )
 +
</source>
  
 
==Change Depot Path==
 
==Change Depot Path==
If you move depots to a new folder, you must update the <code>Filename</code> column of the <code>Depot</code> table.
+
If you move depots to a new folder, you must update the <code>[Filename]</code> column of the <code>[Depot]</code> table.
<syntaxhighlight lang="sql">
+
This sample is what you would do if you moved the depot folder from <code>C:\Truxton Data</code> to <code>C:\Storage</code>
 +
<source lang="sql">
 
UPDATE "Depot"
 
UPDATE "Depot"
SET "Filename" = REPLACE( "Filename", 'C:\Truxton Data\Depot\', 'C:\Storage\Depot\')
+
SET "Filename" = REPLACE( "Filename", 'C:\Truxton Data\Depot\', 'C:\Storage\Depot\' )
</syntaxhighlight>
+
</source>
 +
===Python Script to Change Depot Path===
 +
This is can also be performed in a Python script.
 +
It might be handy to save this script to your computer as <code>ChangeDepotPaths.py</code>
 +
<source lang="python">
 +
import sys
 +
sys.path.append('C:/Program Files/Truxton/SDK')
 +
import truxton
 +
# To get psycopg use pip:
 +
# pip install "psycopg[binary,pool]"
 +
import psycopg
 +
 
 +
def main() -> None:
 +
  t = truxton.create()
 +
 
 +
  with psycopg.connect(t.connectionstring) as database_connection:
 +
    with database_connection.cursor() as database_cursor:
 +
      database_cursor.execute("UPDATE \"Depot\" SET \"Filename\" = REPLACE(\"Filename\", %s, %s )", (sys.argv[1],sys.argv[2]))
 +
      database_connection.commit()
 +
      database_cursor.close()
 +
 
 +
  return None
 +
 
 +
if __name__ == "__main__":
 +
    sys.exit(main())
 +
</source>
  
 
=File Type Counts=
 
=File Type Counts=
<syntaxhighlight lang="sql">
+
<source lang="sql">
SELECT
+
SELECT "FileTypeID",
"FileTypeID",
+
      "FileType"."ShortName",
"FileType"."ShortName",
+
      COUNT( "FileTypeID" ) AS "NumberOfThatType"
COUNT( "FileTypeID" ) AS "NumberOfThatType"
 
 
FROM "File"
 
FROM "File"
 
INNER JOIN "FileType" ON ( "FileType"."ID" = "File"."FileTypeID" )
 
INNER JOIN "FileType" ON ( "FileType"."ID" = "File"."FileTypeID" )
GROUP BY
+
GROUP BY "FileTypeID",
"FileTypeID",
+
        "FileType"."ShortName"
"FileType"."ShortName"
 
 
ORDER BY "NumberOfThatType" DESC
 
ORDER BY "NumberOfThatType" DESC
</syntaxhighlight>
+
</source>
  
=Messages=
+
=Email/SMS/Communications=
 
==Messages and Subjects==
 
==Messages and Subjects==
<syntaxhighlight lang="sql">
+
<source lang="sql">
SELECT
+
SELECT "Message"."ID",
"Message"."ID",
+
      "MessageType"."ShortName" AS "Type",
"MessageType"."ShortName" AS "Type",
+
      "Sent", "Received",  
"Sent", "Received",  
+
      "MessageSubject"."Text" AS "Subject",
"MessageSubject"."Text" AS "Subject",
+
      "NumberOfBodies",
"NumberOfBodies",
+
      "NumberOfAttachments"
"NumberOfAttachments"
 
 
FROM "Message"
 
FROM "Message"
 
LEFT OUTER JOIN "MessageSubject" ON ( "Message"."MessageSubjectID" = "MessageSubject"."ID" )
 
LEFT OUTER JOIN "MessageSubject" ON ( "Message"."MessageSubjectID" = "MessageSubject"."ID" )
LEFT OUTER JOIN "MessageType" ON ("Message"."MessageTypeID" = "MessageType"."ID" )
+
LEFT OUTER JOIN "MessageType" ON ( "Message"."MessageTypeID" = "MessageType"."ID" )
 
ORDER BY "Message"."Sent"
 
ORDER BY "Message"."Sent"
</syntaxhighlight>
+
</source>
  
 
==Find Messages that did not Parse==
 
==Find Messages that did not Parse==
<syntaxhighlight lang="sql">
+
<source lang="sql">
SELECT
+
SELECT "File"."ID",
"File"."ID",
+
      "FN2"."Name",
"FN2"."Name",
+
      "File"."OSLength",
"File"."OSLength",
+
      "File"."HashID",
"File"."HashID",
+
      "ContentStatus"."Name" AS "CStatus",
"ContentStatus"."Name" AS "CStatus",
+
      "File"."NumberOfChildren"
"File"."NumberOfChildren"
 
 
FROM "File"
 
FROM "File"
 
LEFT OUTER JOIN "Filename" AS "FN2" ON ( "File"."FilenameID" = "FN2"."ID" )
 
LEFT OUTER JOIN "Filename" AS "FN2" ON ( "File"."FilenameID" = "FN2"."ID" )
 
LEFT OUTER JOIN "Origin" ON ( "File"."OriginID" = "Origin"."ID" )
 
LEFT OUTER JOIN "Origin" ON ( "File"."OriginID" = "Origin"."ID" )
 
LEFT OUTER JOIN "ContentStatus" ON ( "File"."ContentStatusID" = "ContentStatus"."ID" )
 
LEFT OUTER JOIN "ContentStatus" ON ( "File"."ContentStatusID" = "ContentStatus"."ID" )
WHERE
+
WHERE "File"."FileTypeID" = 199
"File"."FileTypeID" = 199
+
  AND "File"."ID" NOT IN
AND "File"."ID" NOT IN
+
      (
(
+
        SELECT "FileID" FROM "Message"
  SELECT "FileID" FROM "Message
+
      )
)
+
</source>
</syntaxhighlight>
+
 
 +
==Human Readable Message Addresses==
 +
<source lang="sql">
 +
SELECT "MessageAddress"."ID",
 +
      "MessageAddress"."AccountID", "Account"."Name",
 +
      "MessageAddress"."DomainID", "Domain"."Name",
 +
      to_hex("MessageAddress"."CombinedID") AS "CombinedID (Hex)",
 +
      "MessageAddress"."DescriptionID", "MessageAddressDescription"."Description"
 +
FROM "MessageAddress"
 +
JOIN "Account" ON ( "Account"."ID" = "MessageAddress"."AccountID" )
 +
JOIN "Domain" ON ( "Domain"."ID" = "MessageAddress"."DomainID" )
 +
JOIN "MessageAddressDescription" ON ( "MessageAddressDescription"."ID" = "MessageAddress"."DescriptionID" )
 +
</source>
  
 
=SSID=
 
=SSID=
 
==SSID and Associated Passwords==
 
==SSID and Associated Passwords==
<syntaxhighlight lang="sql">
+
This will produce a list of WiFi connection points with their password in a given piece of media.
SELECT
+
<source lang="sql">
DISTINCT "A_ID" AS "PasswordEntityID",
+
SELECT DISTINCT "A_ID" AS "PasswordEntityID",
"B_ID" AS "SSIDEntityID",
+
      "B_ID" AS "SSIDEntityID",
"SSID"."Value" AS "SSID",
+
      "SSID"."Value" AS "SSID",
"Password"."Value" AS "Password"
+
      "Password"."Value" AS "Password"
 
FROM "Relation"
 
FROM "Relation"
 
INNER JOIN "Entity" AS "PasswordEntity" ON ( "Relation"."A_ID" = "PasswordEntity"."ID" )
 
INNER JOIN "Entity" AS "PasswordEntity" ON ( "Relation"."A_ID" = "PasswordEntity"."ID" )
Line 281: Line 645:
 
WHERE "B_ID" IN  
 
WHERE "B_ID" IN  
 
(
 
(
   SELECT "ID" FROM "Entity"
+
   SELECT "ID"
  WHERE
+
    FROM "Entity"
  "MediaID" = '0e8beeee-1c42-91c3-e0b1-93edf6ed2d73'::uuid
+
  WHERE "MediaID" = '0e8beeee-1c42-91c3-e0b1-93edf6ed2d73'::uuid
  AND "EntityTypeID" = 25
+
    AND "EntityTypeID" = 25
 
)
 
)
 
ORDER BY "SSID"."Value"
 
ORDER BY "SSID"."Value"
</syntaxhighlight>
+
</source>
  
 
=Entity=
 
=Entity=
 +
These queries are for entities.
 +
 
==Human Readable==
 
==Human Readable==
How to get the entities to reprocess a piece of media.
+
<source lang="sql">
<syntaxhighlight lang="sql">
+
SELECT "EntityString"."Value",
SELECT  
+
      "EntityType"."LongName",
"Entity"."ID",
+
      "Entity"."ID",
"Entity"."FileID",  
+
      "Entity"."FileID",  
"Entity"."EntityTypeID",  
+
      "Entity"."EntityTypeID",  
"Entity"."EntityStringID",
+
      "Entity"."EntityStringID",
"Entity"."Offset",
+
      "Entity"."Offset",
"Entity"."ObjectID",  
+
      "Entity"."ObjectID",  
"Entity"."ObjectTypeID",
+
      "Entity"."ObjectTypeID",
"Entity"."Length",
+
      "Entity"."Length",
"Entity"."DataTypeID",
+
      "Entity"."DataTypeID",
"ObjectType"."Name",
+
      "ObjectType"."Name"
"EntityString"."Value",
 
"EntityType"."LongName"
 
 
FROM "Entity"
 
FROM "Entity"
 
INNER JOIN "EntityType" ON ( "Entity"."EntityTypeID" = "EntityType"."ID" )
 
INNER JOIN "EntityType" ON ( "Entity"."EntityTypeID" = "EntityType"."ID" )
Line 311: Line 675:
 
INNER JOIN "ObjectType" ON ( "Entity"."ObjectTypeID" = "ObjectType"."ID" )
 
INNER JOIN "ObjectType" ON ( "Entity"."ObjectTypeID" = "ObjectType"."ID" )
 
WHERE "Entity"."MediaID" = '139db997-1f71-58d2-46ae-e052bb67a946'::uuid
 
WHERE "Entity"."MediaID" = '139db997-1f71-58d2-46ae-e052bb67a946'::uuid
</syntaxhighlight>
+
ORDER BY "EntityString"."Value"
 +
</source>
 +
 
 +
==List Accounts with Passwords==
 +
This will produce a list of accounts and their associated passwords for a given media.
 +
<source lang="sql">
 +
SELECT "B"."Value" AS "Account",
 +
      "A"."Value" AS "Password"
 +
FROM "Relation"
 +
LEFT OUTER JOIN "Entity" AS "AEntity" ON "Relation"."A_ID" = "AEntity"."ID"
 +
LEFT OUTER JOIN "EntityString" AS "A" ON "AEntity"."EntityStringID" = "A"."ID"
 +
LEFT OUTER JOIN "Entity" AS "BEntity" ON "Relation"."B_ID" = "BEntity"."ID"
 +
LEFT OUTER JOIN "EntityString" AS "B" ON "BEntity"."EntityStringID" = "B"."ID"
 +
WHERE "Relation"."RelationTypeID" = 11
 +
AND "Relation"."B_ID" IN
 +
(
 +
  SELECT "ID" FROM "Entity" WHERE "EntityTypeID" = 1
 +
)
 +
AND "Relation"."SourceID" IN
 +
(
 +
  SELECT "ID" FROM "File" WHERE "MediaID" = '1b35dc5e-aa4b-a36f-6318-2465775e6df8'::uuid
 +
)
 +
</source>
 +
 
 +
==List NTHashes and Associated Passwords==
 +
This will produce a list of passwords and their [https://en.wikipedia.org/wiki/LAN_Manager#Workarounds NTHash] in a given media.
 +
 
 +
A relation type of 11 is a [[Relation Types|password]] relation.
 +
An entity type of 38 is an [[Entity Types|NT hash]] entity.
 +
<source lang="sql">
 +
SELECT "B"."Value" AS "NTHash",
 +
      "A"."Value" AS "Password"
 +
FROM "Relation"
 +
LEFT OUTER JOIN "Entity" AS "AEntity" ON "Relation"."A_ID" = "AEntity"."ID"
 +
LEFT OUTER JOIN "EntityString" AS "A" ON "AEntity"."EntityStringID" = "A"."ID"
 +
LEFT OUTER JOIN "Entity" AS "BEntity" ON "Relation"."B_ID" = "BEntity"."ID"
 +
LEFT OUTER JOIN "EntityString" AS "B" ON "BEntity"."EntityStringID" = "B"."ID"
 +
WHERE "Relation"."RelationTypeID" = 11
 +
AND "B_ID" IN
 +
(
 +
  SELECT "ID" FROM "Entity" WHERE "EntityTypeID" = 38
 +
)
 +
AND "SourceID" IN
 +
(
 +
  SELECT "ID" FROM "File" WHERE "MediaID" = '1b35dc5e-aa4b-a36f-6318-2465775e6df8'::uuid
 +
)
 +
</source>
 +
 
 +
This will produce a unique list of hashes and passwords for all of Truxton.
 +
 
 +
A relation type of 11 is a [[Relation Types|password]] relation.
 +
An entity type of 38 is an [[Entity Types|NT hash]] entity.
 +
<source lang="sql">
 +
SELECT DISTINCT "B"."Value" AS "NTHash",
 +
                "A"."Value" AS "Password"
 +
FROM "Relation"
 +
LEFT OUTER JOIN "Entity" AS "AEntity" ON "Relation"."A_ID" = "AEntity"."ID"
 +
LEFT OUTER JOIN "EntityString" AS "A" ON "AEntity"."EntityStringID" = "A"."ID"
 +
LEFT OUTER JOIN "Entity" AS "BEntity" ON "Relation"."B_ID" = "BEntity"."ID"
 +
LEFT OUTER JOIN "EntityString" AS "B" ON "BEntity"."EntityStringID" = "B"."ID"
 +
WHERE "Relation"."RelationTypeID" = 11
 +
AND "B_ID" IN
 +
(
 +
  SELECT "ID" FROM "Entity" WHERE "EntityTypeID" = 38
 +
)
 +
ORDER BY "Password"
 +
</source>
 +
 
 +
==Finding a Password by NTHash==
 +
This will find a password for a given [https://en.wikipedia.org/wiki/LAN_Manager#Workarounds NTHash].
 +
<source lang="sql">
 +
SELECT DISTINCT "B"."Value" AS "NTHash",
 +
                "A"."Value" AS "Password"
 +
FROM "Relation"
 +
LEFT OUTER JOIN "Entity" AS "AEntity" ON "Relation"."A_ID" = "AEntity"."ID"
 +
LEFT OUTER JOIN "EntityString" AS "A" ON "AEntity"."EntityStringID" = "A"."ID"
 +
LEFT OUTER JOIN "Entity" AS "BEntity" ON "Relation"."B_ID" = "BEntity"."ID"
 +
LEFT OUTER JOIN "EntityString" AS "B" ON "BEntity"."EntityStringID" = "B"."ID"
 +
WHERE "Relation"."RelationTypeID" = 11
 +
AND "B_ID" IN
 +
(
 +
  SELECT "ID" FROM "Entity" WHERE "EntityTypeID" = 38
 +
)
 +
AND "B"."Value" = 'becedb42ec3c5c7f965255338be4453c'
 +
</source>
 +
 
 +
==List of NTHashes That Need Cracking==
 +
This will produce a unique list of NT Logon Password Hashes that have not been cracked.
 +
<source lang="sql">
 +
SELECT DISTINCT "EntityString"."Value" AS "NTHash"
 +
FROM "Entity"
 +
INNER JOIN "EntityString" ON ( "Entity"."EntityStringID" = "EntityString"."ID" )
 +
WHERE "Entity"."EntityTypeID" = 38
 +
AND "Entity"."ID" NOT IN
 +
(
 +
  SELECT "B_ID"
 +
  FROM "Relation"
 +
  LEFT OUTER JOIN "Entity" ON ("Entity"."ID" = "Relation"."B_ID")
 +
  WHERE "Relation"."RelationTypeID" = 11
 +
  AND "Entity"."EntityTypeID" = 38
 +
)
 +
</source>
 +
 
 +
Here's a couple of websites that give you a taste of hash cracking:
 +
* [https://hashkiller.io/listmanager HashKiller] -- The Most Useful
 +
* [https://hashes.com/en/decrypt/hash Hashes.com]
 +
* [https://crackstation.net/ CrackStation]
 +
 
 +
==List of All Passwords Found==
 +
If you wanted to create a [[Entity Types|password]] dictionary...
 +
<source lang="sql">
 +
SELECT DISTINCT "EntityString"."Value" AS "Password"
 +
FROM "Entity"
 +
INNER JOIN "EntityString" ON ( "Entity"."EntityStringID" = "EntityString"."ID" )
 +
WHERE "Entity"."EntityTypeID" = 26
 +
</source>
 +
 
 +
==Global List of Wifi Access Points with MAC Addresses==
 +
This will produce a list of [[Entity Types|WiFi Access points]] and their associated [[Entity Types|MAC addresses]].
 +
<source lang="sql">
 +
SELECT DISTINCE "EntityA"."MediaID",
 +
                "EntityStringA"."Value" AS "MACAddress",
 +
                "EntityStringB"."Value" AS "SSID"
 +
FROM "Relation"
 +
INNER JOIN "Entity" AS "EntityA" ON ( "Relation"."A_ID" = "EntityA"."ID" )
 +
INNER JOIN "Entity" AS "EntityB" ON ( "Relation"."B_ID" = "EntityB"."ID" )
 +
INNER JOIN "EntityString" AS "EntityStringA" ON ("EntityA"."EntityStringID" = "EntityStringA"."ID")
 +
INNER JOIN "EntityString" AS "EntityStringB" ON ("EntityB"."EntityStringID" = "EntityStringB"."ID")
 +
WHERE
 +
(
 +
  "Relation"."A_ID" IN
 +
  (
 +
    SELECT "Entity"."ID"
 +
    FROM "Entity"
 +
    WHERE "Entity"."EntityTypeID" = 4
 +
  )
 +
  AND
 +
  "Relation"."B_ID" IN
 +
  (
 +
    SELECT "Entity"."ID"
 +
    FROM "Entity"
 +
    WHERE "Entity"."EntityTypeID" = 25
 +
  )
 +
)</source>
  
 
=Loads=
 
=Loads=
 
==How Long did the Loads Take==
 
==How Long did the Loads Take==
 
This query is useful only when you are loading a bunch of media for an investigation at the same time.
 
This query is useful only when you are loading a bunch of media for an investigation at the same time.
<syntaxhighlight lang="sql">
+
<source lang="sql">
SELECT
+
SELECT MIN("Event"."Start"), MAX("Event"."End")
MIN("Start"),
+
FROM "Event"
MAX("End")
+
LEFT OUTER JOIN "InvestigationMedia" ON ("InvestigationMedia"."MediaID" = "Event"."MediaID")
FROM public."Event"
+
WHERE "Event"."EventTypeID" = 1002
WHERE  
+
AND "InvestigationMedia"."InvestigationID" = '20171021-0000-1111-2222-333333333333'::uuid
"EventTypeID" = 1002
+
</source>
  AND "MediaID" IN
+
 
  (
+
==Load Times and File Count==
  SELECT "MediaID" FROM "InvestigationMedia" WHERE "InvestigationID" = '20171021-0000-1111-2222-333333333333'::uuid
+
This will give you a list of media loads from shortest to longest load time with the number of files in that media.
)
+
<source lang="sql">
</syntaxhighlight>
+
  SELECT "Event"."Start",
 +
        "Event"."End",
 +
        "Event"."Title",
 +
        "Event"."Description",
 +
        ((DATE_PART('day',    "Event"."End" - "Event"."Start") * 24 +
 +
          DATE_PART('hour',  "Event"."End" - "Event"."Start")) * 60 +
 +
          DATE_PART('minute', "Event"."End" - "Event"."Start")) AS "Duration",
 +
          COUNT("File"."ID") AS "FileCount"
 +
    FROM "Event",
 +
        "File"
 +
  WHERE "Event"."EventTypeID" = 1002
 +
    AND "File"."MediaID" = "Event"."MediaID"
 +
GROUP BY "Event"."Start",
 +
        "Event"."End",
 +
        "Event"."Title",
 +
        "Event"."Description"
 +
ORDER BY "Duration" ASC
 +
</source>
 +
 
 +
==Load Speed==
 +
This will calculate the speed at which Truxton loaded data.
 +
It assumes that the loads were done in a single batch.
 +
The time window is from the beginning of the load of the first media until the completion of the load of the last media to finish.
 +
This is not a good query for everyday use, rather, it is used during test and evaluation to determine optimal configuration of your loader systems.
 +
<source lang="sql">
 +
SELECT COUNT("Media"."ID") AS "Media",
 +
(SUM("Media"."Size") / 1073741824) AS "Gigabytes",
 +
(SUM("Media"."Size") / 1099511627776) AS "Terabytes",
 +
EXTRACT( EPOCH FROM (MAX("Event"."End") - MIN("Event"."Start"))) / 3600 AS "Hours",
 +
(((SUM("Media"."Size") / EXTRACT( EPOCH FROM (MAX("Event"."End") - MIN("Event"."Start")))) * 3600 ) / 1073741824) AS "Gigabytes per hour"
 +
  FROM "Media"
 +
  LEFT OUTER JOIN "Event" ON ( "Media"."ID" = "Event"."MediaID" )
 +
WHERE "Media"."MediaStatusID" = 230
 +
  AND "Event"."EventTypeID" = 1002
 +
</source>
 +
 
 +
=Reset=
 +
If you want to quickly wipe out the data on a Truxton node you must clear out the <code>[Truxton]</code> database and <code>[TruxtonMessageBus]</code> database.
 +
 
 +
==Clear Truxton Database==
 +
The following script will clear the tables in Truxton and re-initialize tables to their defaults.
 +
<source lang="sql">
 +
TRUNCATE TABLE "Account";
 +
TRUNCATE TABLE "Alert";
 +
TRUNCATE TABLE "Content";
 +
TRUNCATE TABLE "Depot";
 +
TRUNCATE TABLE "DepotLocalName";
 +
TRUNCATE TABLE "EXIF";
 +
TRUNCATE TABLE "Entity";
 +
TRUNCATE TABLE "EntityString";
 +
TRUNCATE TABLE "Event";
 +
TRUNCATE TABLE "ExpandedFile";
 +
TRUNCATE TABLE "File";
 +
TRUNCATE TABLE "Filename";
 +
TRUNCATE TABLE "Free";
 +
TRUNCATE TABLE "Hash";
 +
TRUNCATE TABLE "HashList";
 +
TRUNCATE TABLE "Image";
 +
TRUNCATE TABLE "Investigation";
 +
TRUNCATE TABLE "InvestigationEvent";
 +
TRUNCATE TABLE "InvestigationMedia";
 +
TRUNCATE TABLE "InvestigationSuspect";
 +
TRUNCATE TABLE "Investigator";
 +
TRUNCATE TABLE "InvestigatorAction";
 +
TRUNCATE TABLE "InvestigatorNote";
 +
TRUNCATE TABLE "InvestigatorSetting";
 +
TRUNCATE TABLE "Location";
 +
TRUNCATE TABLE "Log";
 +
TRUNCATE TABLE "LogString";
 +
TRUNCATE TABLE "MD5Hash";
 +
TRUNCATE TABLE "Media";
 +
TRUNCATE TABLE "Message";
 +
TRUNCATE TABLE "MessageAddress";
 +
TRUNCATE TABLE "MessageAddressDescription";
 +
TRUNCATE TABLE "MessageAddress_Message";
 +
TRUNCATE TABLE "MessagePiece";
 +
TRUNCATE TABLE "MessageSubject";
 +
TRUNCATE TABLE "NormalizedPhoneNumber";
 +
TRUNCATE TABLE "QueryResult";
 +
TRUNCATE TABLE "Relation";
 +
TRUNCATE TABLE "Review";
 +
TRUNCATE TABLE "ReviewItem";
 +
TRUNCATE TABLE "ReviewSource";
 +
TRUNCATE TABLE "ReviewTag";
 +
TRUNCATE TABLE "Slack";
 +
TRUNCATE TABLE "Statistics";
 +
TRUNCATE TABLE "Suspect";
 +
TRUNCATE TABLE "Tagged";
 +
TRUNCATE TABLE "URL";
 +
TRUNCATE TABLE "USBDevice";
 +
TRUNCATE TABLE "WebsiteVisit";
 +
 
 +
INSERT INTO "Account"
 +
("ID", "Name")
 +
VALUES
 +
(-7327889212648916261, ''),
 +
(0, '');
 +
 
 +
INSERT INTO "Filename"
 +
("ID", "Name")
 +
VALUES
 +
(-7327889212648916261, '{{Empty}}'),
 +
(-866105518799526845, 'Carved'),
 +
(0, '{{Root}}');
 +
 
 +
INSERT INTO "LogString"
 +
("ID", "String")
 +
VALUES
 +
(0, 'Error'),
 +
(1, 'Warning' ),
 +
(2, 'Info' ),
 +
(3, 'Debug' ),
 +
(4, 'Message Queue' ),
 +
(5, 'Black Box' ),
 +
(6, 'SQL' ),
 +
(7, 'Fatal Error' ),
 +
(-8085263649674490830, 'Truxton Registry Expander' ),
 +
(-6832967734355980866, 'Truxton Text Extract' ),
 +
(-6062509211486031558, 'Truxton Archive Expander' ),
 +
(-5747447535886389339, 'Truxton Thumbnail Generator' ),
 +
(-2906983147363067615, 'Truxton Carve' ),
 +
(-377266439974675442, 'Truxton Expand' ),
 +
(428013036636956400, 'Load' ),
 +
(432078912489116784, 'Report' ),
 +
(1224517872063755206, 'Truxton Finished Loads Monitor' ),
 +
(2162449389329439753, 'Truxton SOLR Contents Indexer' ),
 +
(2883055283884316118, 'Truxton Email' ),
 +
(4453584294307222386, 'Truxton Identify' ),
 +
(4630287248337094523, 'RegRipper' ),
 +
(5974441673585158901, 'Truxton SOLR File Indexer' ),
 +
(8079766870631284876, 'Truxton Contact Sheet Creator' );
 +
 
 +
INSERT INTO "URL"
 +
("ID", "URL")
 +
VALUES
 +
(0, '');
 +
</source>
 +
 
 +
==Clear Message Bus Database==
 +
This script will clear all of the message queues.
 +
The first three queues are the high volume queues.
 +
 
 +
<source lang="sql">
 +
TRUNCATE TABLE "expand";
 +
TRUNCATE TABLE "status";
 +
TRUNCATE TABLE "load";
 +
TRUNCATE TABLE "alert";
 +
TRUNCATE TABLE "archives";
 +
TRUNCATE TABLE "carve";
 +
TRUNCATE TABLE "contactsheet";
 +
TRUNCATE TABLE "email";
 +
TRUNCATE TABLE "finished";
 +
TRUNCATE TABLE "identify";
 +
TRUNCATE TABLE "langid";
 +
TRUNCATE TABLE "maintenance";
 +
TRUNCATE TABLE "notify";
 +
TRUNCATE TABLE "pfe";
 +
TRUNCATE TABLE "poly";
 +
TRUNCATE TABLE "pst";
 +
TRUNCATE TABLE "registry";
 +
TRUNCATE TABLE "regripper";
 +
TRUNCATE TABLE "remoteexpand";
 +
TRUNCATE TABLE "report";
 +
TRUNCATE TABLE "solrcontentstage";
 +
TRUNCATE TABLE "solrfile";
 +
TRUNCATE TABLE "stitch";
 +
TRUNCATE TABLE "thumbnail";
 +
TRUNCATE TABLE "tqueue";
 +
TRUNCATE TABLE "yara";
 +
</source>
 +
 
 +
=Yellowbrick=
 +
==Find Duplicate Records==
 +
Tables like <code>[EntityString]</code> are supposed to be unique by the <code>[ID]</code> column.
 +
However, since [https://www.yellowbrick.com/ Yellowbrick] is a data warehouse, uniqueness is not enforced.
 +
You can detect duplicate primary keys by:
 +
<source lang="sql">
 +
  SELECT "ID",
 +
        COUNT("ID") AS "Copies"
 +
    FROM "EntityString"
 +
GROUP BY "ID"
 +
  HAVING COUNT("ID") > 1
 +
ORDER BY "Copies" DESC,
 +
        "ID" ASC
 +
</source>
 +
 
 +
==Remove Duplicate Records==
 +
Now that you found some duplicates, you probably want to delete them.
 +
<source lang="sql">
 +
WITH rows(rid, rn) AS
 +
(
 +
  SELECT rowid, row_number()
 +
  OVER
 +
  (
 +
    PARTITION BY "ID" ORDER BY "ID"
 +
  )
 +
  FROM "EntityString"
 +
)
 +
DELETE FROM "EntityString" USING rows WHERE rows.rn > 1 and "EntityString".rowid=rows.rid
 +
</source>
 +
 
 +
=Message Bus=
 +
These queries will work only when connected to the Message Bus database.
 +
 
 +
==Number of Messages for Media==
 +
This query will give you a list of media and how many messages are queue for that media in a message queue.
 +
 
 +
<source lang="sql">
 +
  SELECT "MediaID",
 +
        COUNT("MediaID") AS "MediaCount"
 +
    FROM contactsheet
 +
GROUP BY "MediaID"
 +
</source>
 +
 
 +
==See the Coming Messages==
 +
This query will show you the next messages to be processed in a message queue
 +
<source lang="sql">
 +
  SELECT "QueueItemID",
 +
        "MediaID"
 +
    FROM contactsheet
 +
  WHERE "QueueItemAvailable" = true::boolean
 +
    AND "Priority" >= 0::integer
 +
ORDER BY "Priority" DESC,
 +
        "MediaID" ASC,
 +
        "QueueItemAvailable" ASC,
 +
        "QueueItemID" ASC
 +
  LIMIT 100
 +
</source>
 +
 
 +
==How Many Media are Waiting==
 +
This will tell you how many media have messages in the queue.
 +
<source lang="sql">
 +
SELECT COUNT(DISTINCT("MediaID")) FROM contactsheet
 +
</source>
 +
 
 +
=Tags=
 +
==Export Tags as Python Calls==
 +
This will export all user generated tags on files, unique by hash to Python calls.
 +
It is useful when you want to play with tags.
 +
<source lang="sql">
 +
SELECT CONCAT( ' t.taghash("', REPLACE(CAST("HashID" as text), '-', ''), '", "', "Name", '", "', "Description", '")') AS "Code"
 +
FROM
 +
(
 +
          SELECT DISTINCT
 +
                  "File"."HashID",
 +
                  "Tag"."Name",
 +
                  "Tag"."Description"
 +
            FROM "Tagged"
 +
  LEFT OUTER JOIN "File" ON "File"."ID" = "Tagged"."ItemID"
 +
  LEFT OUTER JOIN "Tag" ON "Tag"."ID"  = "Tagged"."TagID"
 +
            WHERE "Tagged"."Source" = 2
 +
              AND "Tagged"."ObjectTypeID" = 9
 +
        ORDER BY "File"."HashID" ASC
 +
)
 +
AS A
 +
</source>
 +
 
 +
=Locations=
 +
==Find Sensitive Site Violations==
 +
This is the query Truxton uses to find Sensitive Site violations in a piece of media.
 +
<source lang="sql">
 +
SELECT "SensitiveSite"."ID",
 +
    "SensitiveSite"."SensitiveSiteListID",
 +
    "SensitiveSite"."Latitude",
 +
    "SensitiveSite"."Longitude",
 +
    "SensitiveSite"."Distance",
 +
    "SensitiveSite"."Name",
 +
    "Location"."ID" AS "LocationID",
 +
    "Location"."FileID",
 +
    "Location"."MediaID",
 +
    "Location"."Label",
 +
    "SensitiveSiteList"."Name" AS "ListName",
 +
    "SensitiveSiteList"."Description" AS "ListDescription",
 +
    earth_distance( ll_to_earth( "SensitiveSite"."Latitude", "SensitiveSite"."Longitude" ),
 +
                    ll_to_earth( "Location"."Latitude", "Location"."Longitude" ) ) AS "OffendingDistance",
 +
    "Location"."LocationTypeID",
 +
    "Location"."Latitude",
 +
    "Location"."Longitude"
 +
FROM "SensitiveSite"
 +
INNER JOIN "SensitiveSiteList" ON ( "SensitiveSite"."SensitiveSiteListID" = "SensitiveSiteList"."ID" )
 +
INNER JOIN "Location" ON earth_box( ll_to_earth( "SensitiveSite"."Latitude", "SensitiveSite"."Longitude" ), "SensitiveSite"."Distance" ) @> ll_to_earth( "Location"."Latitude", "Location"."Longitude" )
 +
WHERE "Location"."MediaID" = '1b6df4c7-9c11-bd10-44a5-05ab0f09b9e1'::uuid
 +
ORDER BY "ListName", "Label"
 +
</source>
 +
 
 +
=External Links=
 +
The following links have proven useful:
 +
* [https://pgtune.leopard.in.ua/#/ PGTune]
 +
* [http://pgconfigurator.cybertec.at/ PostgreSQL Configurator]

Latest revision as of 07:11, 5 April 2026

The following SQL queries were found to be useful when developing/debugging Truxton.

Contents

Database Server

This section deals with tasks required at the server level.

Number of Connections

SELECT COUNT(DISTINCT(numbackends)) FROM pg_stat_database

Maximum Number of Connections

Run this query as an administrator.

SHOW max_connections

You can change this by:

ALTER SYSTEM SET max_connections = '500';

You must restart the Postgres Service in order for the change to take affect.

All Connection Information

This query will show you the number of active connections, maximum number of connections (server configuration) and the number of connections reserved for the super user.

SELECT * FROM
(SELECT COUNT(*) "Current Number of Connections" FROM pg_stat_activity) q1,
(SELECT setting::int "Reserved for Superuser" FROM pg_settings WHERE name=$$superuser_reserved_connections$$) q2,
(SELECT setting::int "Maximum Number of Connections" FROM pg_settings WHERE name=$$max_connections$$) q3;

Current Connections

This query will show you the current connections.

SELECT datname,
       usename,
       application_name,
       client_addr,
       backend_start
FROM pg_stat_activity

Current Activity

Ever wonder what the server is doing? This will show you what each connection is doing.

SELECT state_change,
       age(localtimestamp, state_change),
       datname,
       client_addr,
       application_name,
       state,
       query
FROM pg_stat_activity 
ORDER BY state,
         age DESC,
         datname ASC

Terminate Connection

PostgreSQL 12 will sometimes leave zombie connections open for days. Here's how to kill them. Each connection is a different process on the server.

Kill Connection By Process ID

This will kill a connection to the database given the process id of the offending postgres.exe process.

SELECT pg_terminate_backend(pid) 
FROM pg_stat_activity
WHERE pid = '29564'

Kill Connection By Application Name

Truxton processes will identify themselves via the application name, making it easier to kill them.

SELECT pg_terminate_backend(pid) 
FROM pg_stat_activity
WHERE application_name like 'Truxton Carve%'

Find Unused Indexes

  SELECT relname AS table,
         indexrelname AS index,
         pg_size_pretty(pg_relation_size(i.indexrelid)) AS index_size,
         idx_scan AS index_scans
    FROM pg_stat_user_indexes ui
    JOIN pg_index i ON ui.indexrelid = i.indexrelid
   WHERE NOT indisunique 
     AND idx_scan = 0
     AND pg_relation_size(relid) > 5 * 8192
ORDER BY pg_relation_size(i.indexrelid) / nullif(idx_scan, 0) DESC NULLS FIRST,
         pg_relation_size(i.indexrelid) DESC;

Rename a Database

Sometimes, you may want to stash your current instance of Truxton so you can stand up another and do some testing. You can rename the database, do your work, then rename it back when you are done. Here's the SQL to rename a database.

ALTER DATABASE "Truxton" RENAME TO "SavedTruxton";

Where it gets tricky is you can't rename a database you have a connection to. You can get around this problem by stopping Truxton services on the network, closing all Analyst desktops, and use the psql.exe command line tool.

"C:\Program Files\PostgreSQL\15\bin\psql.exe" --host=localhost --port=5432 --username=postgres --quiet "--command=ALTER DATABASE ""Truxton"" RENAME TO ""SavedTruxton"";"

Create a Database

Here are the steps to manually create a Truxton database.

Create the database.

"C:\Program Files\PostgreSQL\15\bin\psql.exe" --host=localhost --port=5432 --username=postgres --quiet "--command=CREATE DATABASE ""Truxton"" TEMPLATE = template0 ENCODING = 'UTF8' LC_COLLATE = 'C' LC_CTYPE = 'C' CONNECTION LIMIT = -1;"

Never create something without documenting it. Add a comment to the newly created database.

"C:\Program Files\PostgreSQL\15\bin\psql.exe" --host=localhost --port=5432 --username=postgres --quiet "--command=COMMENT ON DATABASE ""Truxton"" IS 'This is the Truxton database. It holds all of the data about files, artifacts and investigative work.';"

Now that the database is created, we must add the tables to it to store the Truxton data. All of the SQL scripts required to create a new instance of Truxton are in the C:\Program Files\Truxton\Database folder.

"C:\Program Files\PostgreSQL\15\bin\psql.exe" --dbname=Truxton --host=localhost --port=5432 --username=postgres --quiet "--file=C:\Program Files\Truxton\Database\create-tables.sql"
"C:\Program Files\PostgreSQL\15\bin\psql.exe" --dbname=Truxton --host=localhost --port=5432 --username=postgres --quiet "--file=C:\Program Files\Truxton\Database\create-tables_1.sql"
"C:\Program Files\PostgreSQL\15\bin\psql.exe" --dbname=Truxton --host=localhost --port=5432 --username=postgres --quiet "--file=C:\Program Files\Truxton\Database\create-tables_2.sql"

The last step in the process is to populate the tables with default data.

"C:\Program Files\PostgreSQL\15\bin\psql.exe" --dbname=Truxton --host=localhost --port=5432 --username=postgres --quiet "--file=C:\Program Files\Truxton\Database\insert-data.sql"
"C:\Program Files\PostgreSQL\15\bin\psql.exe" --dbname=Truxton --host=localhost --port=5432 --username=postgres --quiet "--file=C:\Program Files\Truxton\Database\insert-data_1.sql"
"C:\Program Files\PostgreSQL\15\bin\psql.exe" --dbname=Truxton --host=localhost --port=5432 --username=postgres --quiet "--file=C:\Program Files\Truxton\Database\insert-data_2.sql"
"C:\Program Files\PostgreSQL\15\bin\psql.exe" --dbname=Truxton --host=localhost --port=5432 --username=postgres --quiet "--file=C:\Program Files\Truxton\Database\insert-data_3.sql"
"C:\Program Files\PostgreSQL\15\bin\psql.exe" --dbname=Truxton --host=localhost --port=5432 --username=postgres --quiet "--file=C:\Program Files\Truxton\Database\insert-data_4.sql"

The next time you start the Truxton Service, the rest of the tables will be populated with dynamic.

Determine Table Packing

Postgres will pad pages in the database. Pad bytes are wasted space. To minimize the wasted space, use the following query to help guide the order in which you create your columns.

SELECT a.attname,
       t.typname,
       t.typalign,
       t.typlen
  FROM pg_class c
  JOIN pg_attribute a ON (a.attrelid = c.oid)
  JOIN pg_type t ON (t.oid = a.atttypid)
 WHERE c.relname = 'File'
   AND a.attnum >= 0
ORDER BY t.typlen DESC, t.typname ASC, a.attname ASC

Files

Queries to get file lists.

Human Readable List

This will list files in a human readable format. The WHERE clause is commented out so you can add your own filter criteria.

SELECT "File"."ID",
       "File"."MediaID",
       "File"."ParentFileID",
       "Origin"."Name" AS "Origin",
       "FileType"."ShortName" AS "FileType",
       "FN2"."Name",
       "File"."OSLength",
       "File"."HashID",
       "File"."RawEntropy" * 0.00024414502395702993865779595324564 AS "Entropy",
       "Filename"."Name" AS "Path",
       "File"."FullPathID",
       "File"."FileTypeID",
       to_hex( "File"."Signature" ) AS "Signature",
       to_hex( "File"."PhysicalDiskOffset" ) AS "DiskOffset",
       "FileType"."LongName" AS "FileTypeDescription",
       "File"."Created",
       "File"."LastWrite",
       "ContentStatus"."Name" AS "CStatus",
       "File"."NumberOfChildren",
       "File"."FilenameID"
FROM "File"
LEFT OUTER JOIN "FileType" ON ( "File"."FileTypeID" = "FileType"."ID" )
LEFT OUTER JOIN "Filename" AS "FN2" ON ( "File"."FilenameID" = "FN2"."ID" )
LEFT OUTER JOIN "Filename" ON ( "File"."FullPathID" = "Filename"."ID" )
LEFT OUTER JOIN "Origin" ON ( "File"."OriginID" = "Origin"."ID" )
LEFT OUTER JOIN "ContentStatus" ON ( "File"."ContentStatusID" = "ContentStatus"."ID" )
--WHERE "File"."Signature" = -623191334
ORDER BY "File"."ID"

Files with Depot Locations

SELECT "File"."ID",
       "File"."ParentFileID",
       "File"."FileTypeID",
       "FileType"."ShortName",
       "Filename"."Name",
       "Content"."Offset",
       "Content"."Length",
       "Depot"."Filename"
FROM "File", 
     "Depot",
     "Filename",
     "Content",
     "FileType"
WHERE "File"."HashID" = "Content"."Hash" 
  AND "File"."FilenameID" = "Filename"."ID"
  AND "Content"."DepotID" = "Depot"."ID"
  AND "File"."FileTypeID" = "FileType"."ID"

Carved Files

This query is useful when examining Truxton's file carving quality. This particular query is looking for carved JPEG and JPEG with EXIF files in a specific piece of media.

SELECT DISTINCT("File"."ID"),
       "FileType"."ShortName",
       "Filename"."Name",
       "Content"."Length",
       "Content"."Offset",
       "File"."PhysicalDiskOffset",
       "File"."HashID" AS "MD5",
       "MD5Hash"."SHA1",
       "Depot"."Filename" AS "Depot Path"
FROM "File", 
     "Depot",
     "Filename",
     "Content",
     "FileType",
     "MD5Hash"
WHERE "File"."HashID" = "Content"."Hash" 
  AND "File"."FilenameID" = "Filename"."ID"
  AND "Content"."DepotID" = "Depot"."ID"
  AND "File"."FileTypeID" = "FileType"."ID"
  AND "MD5Hash"."ID" = "File"."HashID"
  AND "File"."OriginID" = 3
  AND "File"."FileTypeID" IN (203, 204)
  AND "File"."MediaID" = '27cfac98-af34-1cc9-6812-a18573e44e0e'::uuid
  ORDER BY "Depot Path" ASC, "Offset" ASC

Files Truxton Can Exploit

SELECT "ShortName",
       "LongName"
FROM "FileType"
WHERE "ID" IN
(
  SELECT DISTINCT("FileTypeID")
  FROM "ETLRoute"
  WHERE "ETLQueueName" IN ('expand', 'email', 'remoteexpand', 'archives', 'pfe                             ')
)
ORDER BY "ShortName"

This one produces a single line of output that is easier for a human to read.

SELECT CONCAT( "ShortName", ' - ', "LongName" )
FROM "FileType"
WHERE "ID" IN
(
  SELECT DISTINCT("FileTypeID")
  FROM "ETLRoute"
  WHERE "ETLQueueName" IN ('expand', 'email', 'remoteexpand', 'archives', 'pfe                             ')
)
ORDER BY "ShortName"

NOTE the pfe item is a bug. There should NOT be spaces in that name.

Number of Files of Type in Media

This will produce a list of the count of files of a particular type in media.

  SELECT "MediaID",
          COUNT("FileTypeID") AS "FileCount"
    FROM "File"
   WHERE "FileTypeID" = 201
GROUP BY "MediaID",
         "FileTypeID"
ORDER BY COUNT("FileTypeID") DESC

Find Investigation Name given File ID

SELECT "Investigation"."Name"
FROM "Investigation"
LEFT OUTER JOIN "InvestigationMedia" ON ("InvestigationMedia"."InvestigationID" = "Investigation"."ID")
LEFT OUTER JOIN "Media" ON ("Media"."ID" = "InvestigationMedia"."MediaID")
LEFT OUTER JOIN "File" ON ("File"."MediaID" = "Media"."ID")
WHERE "File"."ID" = '645ff09a-6425-1bf8-8542-9bdc00035f8c'::uuid

Find Investigations given File Signature

The following query will find all investigations that contain a file that starts with the byte sequence of 00 00 03 00

SELECT DISTINCT( "Investigation"."Name" )
FROM "Investigation"
LEFT OUTER JOIN "InvestigationMedia" ON ("InvestigationMedia"."InvestigationID" = "Investigation"."ID")
LEFT OUTER JOIN "Media" ON ("Media"."ID" = "InvestigationMedia"."MediaID")
LEFT OUTER JOIN "File" ON ("File"."MediaID" = "Media"."ID")
WHERE "File"."Signature" = cast(x'00000300' as int)

Find Investigations given File Name Pattern

The following query will find all investigations that contain a CSV file.

SELECT DISTINCT "Investigation"."Name"
FROM "Investigation"
LEFT OUTER JOIN "InvestigationMedia" ON ("InvestigationMedia"."InvestigationID" = "Investigation"."ID")
LEFT OUTER JOIN "Media" ON ("Media"."ID" = "InvestigationMedia"."MediaID")
LEFT OUTER JOIN "File" ON ("File"."MediaID" = "Media"."ID")
LEFT OUTER JOIN "Filename" ON ("Filename"."ID" = "File"."FilenameID" )
WHERE "Filename"."Name" ILIKE  '%.csv'

Find File Names for File Type

This query will show you all of the names of files of type Android Battery History

SELECT DISTINCT "Filename"."Name"
FROM "File"
LEFT OUTER JOIN "Filename" ON ("Filename"."ID" = "File"."FilenameID")
WHERE "FileTypeID" = 1021

Make a Hashset From Loaded Data

When you get a lot of data loaded, you will notice that there's lots of files that are repeated. These files will probably never make it into the ]https://www.nist.gov/itl/ssd/software-quality-group/national-software-reference-library-nsrl NSRL]. Using this query, you can create your own hashset to eliminate these files in future loads.

This queries the database for hashes of files that haven't already been eliminated and occurs more than 50 times.

SELECT UPPER(SUBSTRING( "HashID"::text FROM 1 FOR 8 ) ||
SUBSTRING( "HashID"::text FROM 10 FOR 4 ) ||
SUBSTRING( "HashID"::text FROM 15 FOR 4 ) ||
SUBSTRING( "HashID"::text FROM 20 FOR 4 ) ||
SUBSTRING( "HashID"::text FROM 25 FOR 12 )) AS "Hash"
FROM "File"
WHERE "OriginID" = 1 AND
"ContentStatusID" = 1 AND
"HashID" NOT IN ( '00000000-0000-0000-0000-000000000000'::uuid, 'd41d8cd9-8f00-b204-e980-0998ecf8427e'::uuid )
GROUP BY "HashID"
HAVING COUNT("HashID") > 50
ORDER BY "Hash" ASC

Logs

All Messages

SELECT "Log"."MediaID",
       "Log"."When",
       "T"."String" AS "Type",
       "S"."String" AS "Source",
       "Log"."ProcessID",
       "Log"."ThreadID",
       "C"."String" AS "Client",
       "U"."String" AS "User",
       "M"."String" AS "Message"
FROM "Log",
     "LogString" AS "S",
     "LogString" AS "C",
     "LogString" AS "U",
     "LogString" AS "M",
     "LogString" AS "T"
WHERE "Log"."LogStringSourceID" = "S"."ID" 
  AND "Log"."LogStringClientID" = "C"."ID" 
  AND "Log"."LogStringUserID" = "U"."ID" 
  AND "Log"."LogStringMessageID" = "M"."ID" 
  AND "Log"."LogStringTypeID" = "T"."ID"
-- AND "Log"."LogStringTypeID" <> 2 -- Don't show Info messages
-- AND "Log"."MediaID" = '11111111-1111-1111-1111-000000000001'::uuid
ORDER BY "Log"."MediaID",
         "Log"."When"

Only Error and Warnings

SELECT "Log"."MediaID",
       "Log"."When",
       "Log"."LogStringTypeID",
       "T"."String" AS "Type",
       "S"."String" AS "Source",
       "Log"."ProcessID",
       "Log"."ThreadID",
       "C"."String" AS "Client",
       "U"."String" AS "User",
       "M"."String" AS "Message"
FROM "Log",
     "LogString" AS "S",
     "LogString" AS "C",
     "LogString" AS "U",
     "LogString" AS "M",
     "LogString" AS "T"
WHERE "Log"."LogStringSourceID" = "S"."ID" 
  AND "Log"."LogStringClientID" = "C"."ID" 
  AND "Log"."LogStringUserID" = "U"."ID" 
  AND "Log"."LogStringMessageID" = "M"."ID" 
  AND "Log"."LogStringTypeID" = "T"."ID"
  AND "Log"."LogStringTypeID" IN ( 0, 1 ) -- Don't show Info messages
-- AND "Log"."MediaID" = '11111111-1111-1111-1111-000000000001'::uuid
ORDER BY "Log"."MediaID",
         "Log"."When"

Load Log

The following query is used to generate the Load Log. You can run this query as media is being loaded to peek at the log.

SELECT "Log"."When",
       "T"."String" AS "Type",
       "S"."String" AS "Source",
       "Log"."ProcessID",
       "Log"."ThreadID",
       "C"."String" AS "Client",
       "U"."String" AS "User",
       "M"."String" AS "Message",
       "Log"."LogStringMessageID"
FROM "Log",
     "LogString" AS "S",
     "LogString" AS "C",
     "LogString" AS "U",
     "LogString" AS "M",
     "LogString" AS "T"
WHERE "Log"."MediaID" = '11111111-1111-1111-1111-000000000001'::uuid
  AND "Log"."LogStringSourceID" = "S"."ID"
  AND "Log"."LogStringClientID" = "C"."ID"
  AND "Log"."LogStringUserID" = "U"."ID"
  AND "Log"."LogStringMessageID" = "M"."ID"
  AND "Log"."LogStringTypeID" = "T"."ID"
ORDER BY "Log"."When",
         "Source";

Depots

These queries will tell you if you're missing depot file entries.

Missing Content Depots

SELECT DISTINCT( "DepotID" )
FROM "Content"
WHERE "DepotID" <> '00000000-0000-0000-0000-000000000000'::uuid
  AND "DepotID" NOT IN ( SELECT "ID" FROM "Depot" )
ORDER BY "DepotID"

Missing Image Depots

SELECT DISTINCT( "DepotID" )
FROM "Image"
WHERE "DepotID" <> '00000000-0000-0000-0000-000000000000'::uuid
  AND "DepotID" NOT IN
  (
    SELECT "ID" FROM "Depot"
  )
ORDER BY "DepotID"

Depot IDs in Use

SELECT DISTINCT "DepotID" AS "ID" FROM
(
  SELECT "DepotID" FROM "Content"
  UNION
  SELECT "DepotID" FROM "Free"
  UNION
  SELECT "DepotID" FROM "Image"
  UNION
  SELECT "DepotID" FROM "Slack"
  UNION
  SELECT "ThumbnailDepotID" AS "DepotID" FROM "ReviewItem"
) AS "SubSelect"

Depot IDs Not in Use

SELECT "ID"
FROM "Depot"
WHERE "ID" NOT IN
(
 SELECT DISTINCT "DepotID" AS "ID" FROM
 (
  SELECT "DepotID" FROM "Content"
  UNION
  SELECT "DepotID" FROM "Free"
  UNION
  SELECT "DepotID" FROM "Image"
  UNION
  SELECT "DepotID" FROM "Slack"
  UNION
  SELECT "ThumbnailDepotID" AS "DepotID" FROM "ReviewItem"
 ) AS "SubSelect"
)

URI in Filename Column

To detect when an ETL has put bad data into the [Depot] table.

SELECT "ID",
       "Filename",
       "URI",
       "DepotTypeID",
       "MaximumSize",
       "Size",
       "IsLongTerm",
       "CryptoKey",
       "DepotStatusID"
FROM "Depot"
WHERE "Filename" LIKE '\\%'

Change Depot Folder

If you move your depot folder, the path will have to be updated in the database. This statement will change the depot drive letter from D to E.

UPDATE "Depot"
SET "Filename" = REPLACE( "Filename", 'D:\\', 'E:\\')
WHERE "Filename" LIKE 'D:\\%'

Change Depot URI

When you move depots to a new share, the database needs to be fixed up. This sample is what you would run if you need to change the machine name SOFS-01 to a fully qualified DNS name.

UPDATE "Depot"
SET "URI" = REPLACE( "URI", '\\SOFS-01\', '\\sofs-01.truxton.lab\Depot\')
WHERE "URI" LIKE '\\\\sofs-01.truxton.lab%'

If you move depots to Amazon S3 Storage:

UPDATE "Depot"
SET "URI" = REPLACE( "URI", '\\Machine\Share\Depot\', 'https://depot-bucket-a730.s3.amazonaws.com/' )

Change Depot Path

If you move depots to a new folder, you must update the [Filename] column of the [Depot] table. This sample is what you would do if you moved the depot folder from C:\Truxton Data to C:\Storage

UPDATE "Depot"
SET "Filename" = REPLACE( "Filename", 'C:\Truxton Data\Depot\', 'C:\Storage\Depot\' )

Python Script to Change Depot Path

This is can also be performed in a Python script. It might be handy to save this script to your computer as ChangeDepotPaths.py

import sys
sys.path.append('C:/Program Files/Truxton/SDK')
import truxton
# To get psycopg use pip:
# pip install "psycopg[binary,pool]"
import psycopg

def main() -> None:
  t = truxton.create()

  with psycopg.connect(t.connectionstring) as database_connection:
    with database_connection.cursor() as database_cursor:
      database_cursor.execute("UPDATE \"Depot\" SET \"Filename\" = REPLACE(\"Filename\", %s, %s )", (sys.argv[1],sys.argv[2]))
      database_connection.commit()
      database_cursor.close()

  return None

if __name__ == "__main__":
    sys.exit(main())

File Type Counts

SELECT "FileTypeID",
       "FileType"."ShortName",
       COUNT( "FileTypeID" ) AS "NumberOfThatType"
FROM "File"
INNER JOIN "FileType" ON ( "FileType"."ID" = "File"."FileTypeID" )
GROUP BY "FileTypeID",
         "FileType"."ShortName"
ORDER BY "NumberOfThatType" DESC

Email/SMS/Communications

Messages and Subjects

SELECT "Message"."ID",
       "MessageType"."ShortName" AS "Type",
       "Sent", "Received", 
       "MessageSubject"."Text" AS "Subject",
       "NumberOfBodies",
       "NumberOfAttachments"
FROM "Message"
LEFT OUTER JOIN "MessageSubject" ON ( "Message"."MessageSubjectID" = "MessageSubject"."ID" )
LEFT OUTER JOIN "MessageType" ON ( "Message"."MessageTypeID" = "MessageType"."ID" )
ORDER BY "Message"."Sent"

Find Messages that did not Parse

SELECT "File"."ID",
       "FN2"."Name",
       "File"."OSLength",
       "File"."HashID",
       "ContentStatus"."Name" AS "CStatus",
       "File"."NumberOfChildren"
FROM "File"
LEFT OUTER JOIN "Filename" AS "FN2" ON ( "File"."FilenameID" = "FN2"."ID" )
LEFT OUTER JOIN "Origin" ON ( "File"."OriginID" = "Origin"."ID" )
LEFT OUTER JOIN "ContentStatus" ON ( "File"."ContentStatusID" = "ContentStatus"."ID" )
WHERE "File"."FileTypeID" = 199
  AND "File"."ID" NOT IN
      (
        SELECT "FileID" FROM "Message"
      )

Human Readable Message Addresses

SELECT "MessageAddress"."ID", 
       "MessageAddress"."AccountID", "Account"."Name",
       "MessageAddress"."DomainID", "Domain"."Name",
       to_hex("MessageAddress"."CombinedID") AS "CombinedID (Hex)", 
       "MessageAddress"."DescriptionID", "MessageAddressDescription"."Description"
FROM "MessageAddress"
JOIN "Account" ON ( "Account"."ID" = "MessageAddress"."AccountID" )
JOIN "Domain" ON ( "Domain"."ID" = "MessageAddress"."DomainID" )
JOIN "MessageAddressDescription" ON ( "MessageAddressDescription"."ID" = "MessageAddress"."DescriptionID" )

SSID

SSID and Associated Passwords

This will produce a list of WiFi connection points with their password in a given piece of media.

SELECT DISTINCT "A_ID" AS "PasswordEntityID",
       "B_ID" AS "SSIDEntityID",
       "SSID"."Value" AS "SSID",
       "Password"."Value" AS "Password"
FROM "Relation"
INNER JOIN "Entity" AS "PasswordEntity" ON ( "Relation"."A_ID" = "PasswordEntity"."ID" )
INNER JOIN "Entity" AS "SSIDEntity" ON ( "Relation"."B_ID" = "SSIDEntity"."ID" )
INNER JOIN "EntityString" AS "Password" ON ("PasswordEntity"."EntityStringID" = "Password"."ID")
INNER JOIN "EntityString" AS "SSID" ON ("SSIDEntity"."EntityStringID" = "SSID"."ID")
WHERE "B_ID" IN 
(
  SELECT "ID"
    FROM "Entity"
   WHERE "MediaID" = '0e8beeee-1c42-91c3-e0b1-93edf6ed2d73'::uuid
     AND "EntityTypeID" = 25
)
ORDER BY "SSID"."Value"

Entity

These queries are for entities.

Human Readable

SELECT "EntityString"."Value", 
       "EntityType"."LongName",
       "Entity"."ID",
       "Entity"."FileID", 
       "Entity"."EntityTypeID", 
       "Entity"."EntityStringID",
       "Entity"."Offset",
       "Entity"."ObjectID", 
       "Entity"."ObjectTypeID",
       "Entity"."Length",
       "Entity"."DataTypeID",
       "ObjectType"."Name"
FROM "Entity"
INNER JOIN "EntityType" ON ( "Entity"."EntityTypeID" = "EntityType"."ID" )
INNER JOIN "EntityString" ON ( "Entity"."EntityStringID" = "EntityString"."ID" )
INNER JOIN "ObjectType" ON ( "Entity"."ObjectTypeID" = "ObjectType"."ID" )
WHERE "Entity"."MediaID" = '139db997-1f71-58d2-46ae-e052bb67a946'::uuid
ORDER BY "EntityString"."Value"

List Accounts with Passwords

This will produce a list of accounts and their associated passwords for a given media.

SELECT "B"."Value" AS "Account",
       "A"."Value" AS "Password"
FROM "Relation"
LEFT OUTER JOIN "Entity" AS "AEntity" ON "Relation"."A_ID" = "AEntity"."ID"
LEFT OUTER JOIN "EntityString" AS "A" ON "AEntity"."EntityStringID" = "A"."ID"
LEFT OUTER JOIN "Entity" AS "BEntity" ON "Relation"."B_ID" = "BEntity"."ID"
LEFT OUTER JOIN "EntityString" AS "B" ON "BEntity"."EntityStringID" = "B"."ID"
WHERE "Relation"."RelationTypeID" = 11
AND "Relation"."B_ID" IN
(
   SELECT "ID" FROM "Entity" WHERE "EntityTypeID" = 1
)
AND "Relation"."SourceID" IN
(
   SELECT "ID" FROM "File" WHERE "MediaID" = '1b35dc5e-aa4b-a36f-6318-2465775e6df8'::uuid
)

List NTHashes and Associated Passwords

This will produce a list of passwords and their NTHash in a given media.

A relation type of 11 is a password relation. An entity type of 38 is an NT hash entity.

SELECT "B"."Value" AS "NTHash",
       "A"."Value" AS "Password"
FROM "Relation"
LEFT OUTER JOIN "Entity" AS "AEntity" ON "Relation"."A_ID" = "AEntity"."ID"
LEFT OUTER JOIN "EntityString" AS "A" ON "AEntity"."EntityStringID" = "A"."ID"
LEFT OUTER JOIN "Entity" AS "BEntity" ON "Relation"."B_ID" = "BEntity"."ID"
LEFT OUTER JOIN "EntityString" AS "B" ON "BEntity"."EntityStringID" = "B"."ID"
WHERE "Relation"."RelationTypeID" = 11
AND "B_ID" IN
(
   SELECT "ID" FROM "Entity" WHERE "EntityTypeID" = 38
)
AND "SourceID" IN
(
   SELECT "ID" FROM "File" WHERE "MediaID" = '1b35dc5e-aa4b-a36f-6318-2465775e6df8'::uuid
)

This will produce a unique list of hashes and passwords for all of Truxton.

A relation type of 11 is a password relation. An entity type of 38 is an NT hash entity.

SELECT DISTINCT "B"."Value" AS "NTHash",
                "A"."Value" AS "Password"
FROM "Relation"
LEFT OUTER JOIN "Entity" AS "AEntity" ON "Relation"."A_ID" = "AEntity"."ID"
LEFT OUTER JOIN "EntityString" AS "A" ON "AEntity"."EntityStringID" = "A"."ID"
LEFT OUTER JOIN "Entity" AS "BEntity" ON "Relation"."B_ID" = "BEntity"."ID"
LEFT OUTER JOIN "EntityString" AS "B" ON "BEntity"."EntityStringID" = "B"."ID"
WHERE "Relation"."RelationTypeID" = 11
AND "B_ID" IN
(
   SELECT "ID" FROM "Entity" WHERE "EntityTypeID" = 38
)
ORDER BY "Password"

Finding a Password by NTHash

This will find a password for a given NTHash.

SELECT DISTINCT "B"."Value" AS "NTHash",
                "A"."Value" AS "Password"
FROM "Relation"
LEFT OUTER JOIN "Entity" AS "AEntity" ON "Relation"."A_ID" = "AEntity"."ID"
LEFT OUTER JOIN "EntityString" AS "A" ON "AEntity"."EntityStringID" = "A"."ID"
LEFT OUTER JOIN "Entity" AS "BEntity" ON "Relation"."B_ID" = "BEntity"."ID"
LEFT OUTER JOIN "EntityString" AS "B" ON "BEntity"."EntityStringID" = "B"."ID"
WHERE "Relation"."RelationTypeID" = 11
AND "B_ID" IN
(
   SELECT "ID" FROM "Entity" WHERE "EntityTypeID" = 38
)
AND "B"."Value" = 'becedb42ec3c5c7f965255338be4453c'

List of NTHashes That Need Cracking

This will produce a unique list of NT Logon Password Hashes that have not been cracked.

SELECT DISTINCT "EntityString"."Value" AS "NTHash"
FROM "Entity"
INNER JOIN "EntityString" ON ( "Entity"."EntityStringID" = "EntityString"."ID" )
WHERE "Entity"."EntityTypeID" = 38
AND "Entity"."ID" NOT IN
(
  SELECT "B_ID"
  FROM "Relation"
  LEFT OUTER JOIN "Entity" ON ("Entity"."ID" = "Relation"."B_ID") 
  WHERE "Relation"."RelationTypeID" = 11
  AND "Entity"."EntityTypeID" = 38
)

Here's a couple of websites that give you a taste of hash cracking:

List of All Passwords Found

If you wanted to create a password dictionary...

SELECT DISTINCT "EntityString"."Value" AS "Password"
FROM "Entity"
INNER JOIN "EntityString" ON ( "Entity"."EntityStringID" = "EntityString"."ID" )
WHERE "Entity"."EntityTypeID" = 26

Global List of Wifi Access Points with MAC Addresses

This will produce a list of WiFi Access points and their associated MAC addresses.

SELECT DISTINCE "EntityA"."MediaID",
                "EntityStringA"."Value" AS "MACAddress",
                "EntityStringB"."Value" AS "SSID"
FROM "Relation"
INNER JOIN "Entity" AS "EntityA" ON ( "Relation"."A_ID" = "EntityA"."ID" )
INNER JOIN "Entity" AS "EntityB" ON ( "Relation"."B_ID" = "EntityB"."ID" )
INNER JOIN "EntityString" AS "EntityStringA" ON ("EntityA"."EntityStringID" = "EntityStringA"."ID")
INNER JOIN "EntityString" AS "EntityStringB" ON ("EntityB"."EntityStringID" = "EntityStringB"."ID")
WHERE 
(
  "Relation"."A_ID" IN 
  (
    SELECT "Entity"."ID"
    FROM "Entity" 
    WHERE "Entity"."EntityTypeID" = 4
  )
  AND 
  "Relation"."B_ID" IN 
  (
    SELECT "Entity"."ID"
    FROM "Entity" 
    WHERE "Entity"."EntityTypeID" = 25
  )
)

Loads

How Long did the Loads Take

This query is useful only when you are loading a bunch of media for an investigation at the same time.

SELECT MIN("Event"."Start"), MAX("Event"."End")
FROM "Event"
LEFT OUTER JOIN "InvestigationMedia" ON ("InvestigationMedia"."MediaID" = "Event"."MediaID")
WHERE "Event"."EventTypeID" = 1002
AND "InvestigationMedia"."InvestigationID" = '20171021-0000-1111-2222-333333333333'::uuid

Load Times and File Count

This will give you a list of media loads from shortest to longest load time with the number of files in that media.

  SELECT "Event"."Start",
         "Event"."End",
         "Event"."Title",
         "Event"."Description",
        ((DATE_PART('day',    "Event"."End" - "Event"."Start") * 24 +
          DATE_PART('hour',   "Event"."End" - "Event"."Start")) * 60 +
          DATE_PART('minute', "Event"."End" - "Event"."Start")) AS "Duration",
          COUNT("File"."ID") AS "FileCount"
    FROM "Event",
         "File"
   WHERE "Event"."EventTypeID" = 1002
     AND "File"."MediaID" = "Event"."MediaID"
GROUP BY "Event"."Start",
         "Event"."End",
         "Event"."Title",
         "Event"."Description"
ORDER BY "Duration" ASC

Load Speed

This will calculate the speed at which Truxton loaded data. It assumes that the loads were done in a single batch. The time window is from the beginning of the load of the first media until the completion of the load of the last media to finish. This is not a good query for everyday use, rather, it is used during test and evaluation to determine optimal configuration of your loader systems.

SELECT COUNT("Media"."ID") AS "Media",
(SUM("Media"."Size") / 1073741824) AS "Gigabytes",
(SUM("Media"."Size") / 1099511627776) AS "Terabytes",
EXTRACT( EPOCH FROM (MAX("Event"."End") - MIN("Event"."Start"))) / 3600 AS "Hours",
(((SUM("Media"."Size") / EXTRACT( EPOCH FROM (MAX("Event"."End") - MIN("Event"."Start")))) * 3600 ) / 1073741824) AS "Gigabytes per hour"
  FROM "Media"
  LEFT OUTER JOIN "Event" ON ( "Media"."ID" = "Event"."MediaID" )
 WHERE "Media"."MediaStatusID" = 230
   AND "Event"."EventTypeID" = 1002

Reset

If you want to quickly wipe out the data on a Truxton node you must clear out the [Truxton] database and [TruxtonMessageBus] database.

Clear Truxton Database

The following script will clear the tables in Truxton and re-initialize tables to their defaults.

TRUNCATE TABLE "Account";
TRUNCATE TABLE "Alert";
TRUNCATE TABLE "Content";
TRUNCATE TABLE "Depot";
TRUNCATE TABLE "DepotLocalName";
TRUNCATE TABLE "EXIF";
TRUNCATE TABLE "Entity";
TRUNCATE TABLE "EntityString";
TRUNCATE TABLE "Event";
TRUNCATE TABLE "ExpandedFile";
TRUNCATE TABLE "File";
TRUNCATE TABLE "Filename";
TRUNCATE TABLE "Free";
TRUNCATE TABLE "Hash";
TRUNCATE TABLE "HashList";
TRUNCATE TABLE "Image";
TRUNCATE TABLE "Investigation";
TRUNCATE TABLE "InvestigationEvent";
TRUNCATE TABLE "InvestigationMedia";
TRUNCATE TABLE "InvestigationSuspect";
TRUNCATE TABLE "Investigator";
TRUNCATE TABLE "InvestigatorAction";
TRUNCATE TABLE "InvestigatorNote";
TRUNCATE TABLE "InvestigatorSetting";
TRUNCATE TABLE "Location";
TRUNCATE TABLE "Log";
TRUNCATE TABLE "LogString";
TRUNCATE TABLE "MD5Hash";
TRUNCATE TABLE "Media";
TRUNCATE TABLE "Message";
TRUNCATE TABLE "MessageAddress";
TRUNCATE TABLE "MessageAddressDescription";
TRUNCATE TABLE "MessageAddress_Message";
TRUNCATE TABLE "MessagePiece";
TRUNCATE TABLE "MessageSubject";
TRUNCATE TABLE "NormalizedPhoneNumber";
TRUNCATE TABLE "QueryResult";
TRUNCATE TABLE "Relation";
TRUNCATE TABLE "Review";
TRUNCATE TABLE "ReviewItem";
TRUNCATE TABLE "ReviewSource";
TRUNCATE TABLE "ReviewTag";
TRUNCATE TABLE "Slack";
TRUNCATE TABLE "Statistics";
TRUNCATE TABLE "Suspect";
TRUNCATE TABLE "Tagged";
TRUNCATE TABLE "URL";
TRUNCATE TABLE "USBDevice";
TRUNCATE TABLE "WebsiteVisit";

INSERT INTO "Account"
("ID", "Name")
VALUES
(-7327889212648916261, ''),
(0, '');

INSERT INTO "Filename"
("ID", "Name")
VALUES
(-7327889212648916261, '{{Empty}}'),
(-866105518799526845, 'Carved'),
(0, '{{Root}}');

INSERT INTO "LogString"
("ID", "String")
VALUES
(0, 'Error'),
(1, 'Warning' ),
(2, 'Info' ),
(3, 'Debug' ),
(4, 'Message Queue' ),
(5, 'Black Box' ),
(6, 'SQL' ),
(7, 'Fatal Error' ),
(-8085263649674490830, 'Truxton Registry Expander' ),
(-6832967734355980866, 'Truxton Text Extract' ),
(-6062509211486031558, 'Truxton Archive Expander' ),
(-5747447535886389339, 'Truxton Thumbnail Generator' ),
(-2906983147363067615, 'Truxton Carve' ),
(-377266439974675442, 'Truxton Expand' ),
(428013036636956400, 'Load' ),
(432078912489116784, 'Report' ),
(1224517872063755206, 'Truxton Finished Loads Monitor' ),
(2162449389329439753, 'Truxton SOLR Contents Indexer' ),
(2883055283884316118, 'Truxton Email' ),
(4453584294307222386, 'Truxton Identify' ),
(4630287248337094523, 'RegRipper' ),
(5974441673585158901, 'Truxton SOLR File Indexer' ),
(8079766870631284876, 'Truxton Contact Sheet Creator' );

INSERT INTO "URL"
("ID", "URL")
VALUES
(0, '');

Clear Message Bus Database

This script will clear all of the message queues. The first three queues are the high volume queues.

TRUNCATE TABLE "expand";
TRUNCATE TABLE "status";
TRUNCATE TABLE "load";
TRUNCATE TABLE "alert";
TRUNCATE TABLE "archives";
TRUNCATE TABLE "carve";
TRUNCATE TABLE "contactsheet";
TRUNCATE TABLE "email";
TRUNCATE TABLE "finished";
TRUNCATE TABLE "identify";
TRUNCATE TABLE "langid";
TRUNCATE TABLE "maintenance";
TRUNCATE TABLE "notify";
TRUNCATE TABLE "pfe";
TRUNCATE TABLE "poly";
TRUNCATE TABLE "pst";
TRUNCATE TABLE "registry";
TRUNCATE TABLE "regripper";
TRUNCATE TABLE "remoteexpand";
TRUNCATE TABLE "report";
TRUNCATE TABLE "solrcontentstage";
TRUNCATE TABLE "solrfile";
TRUNCATE TABLE "stitch";
TRUNCATE TABLE "thumbnail";
TRUNCATE TABLE "tqueue";
TRUNCATE TABLE "yara";

Yellowbrick

Find Duplicate Records

Tables like [EntityString] are supposed to be unique by the [ID] column. However, since Yellowbrick is a data warehouse, uniqueness is not enforced. You can detect duplicate primary keys by:

  SELECT "ID", 
         COUNT("ID") AS "Copies"
    FROM "EntityString"
GROUP BY "ID"
  HAVING COUNT("ID") > 1
ORDER BY "Copies" DESC,
         "ID" ASC

Remove Duplicate Records

Now that you found some duplicates, you probably want to delete them.

WITH rows(rid, rn) AS
(
  SELECT rowid, row_number()
  OVER
  (
    PARTITION BY "ID" ORDER BY "ID"
  )
  FROM "EntityString"
)
DELETE FROM "EntityString" USING rows WHERE rows.rn > 1 and "EntityString".rowid=rows.rid

Message Bus

These queries will work only when connected to the Message Bus database.

Number of Messages for Media

This query will give you a list of media and how many messages are queue for that media in a message queue.

  SELECT "MediaID",
         COUNT("MediaID") AS "MediaCount"
    FROM contactsheet
GROUP BY "MediaID"

See the Coming Messages

This query will show you the next messages to be processed in a message queue

  SELECT "QueueItemID",
         "MediaID"
    FROM  contactsheet
   WHERE "QueueItemAvailable" = true::boolean 
     AND "Priority" >= 0::integer
ORDER BY "Priority" DESC,
         "MediaID" ASC,
         "QueueItemAvailable" ASC,
         "QueueItemID" ASC 
   LIMIT 100

How Many Media are Waiting

This will tell you how many media have messages in the queue.

SELECT COUNT(DISTINCT("MediaID")) FROM contactsheet

Tags

Export Tags as Python Calls

This will export all user generated tags on files, unique by hash to Python calls. It is useful when you want to play with tags.

SELECT CONCAT( ' t.taghash("', REPLACE(CAST("HashID" as text), '-', ''), '", "', "Name", '", "', "Description", '")') AS "Code"
FROM
(
           SELECT DISTINCT
                  "File"."HashID",
                  "Tag"."Name",
                  "Tag"."Description"
             FROM "Tagged"
  LEFT OUTER JOIN "File" ON "File"."ID" = "Tagged"."ItemID"
  LEFT OUTER JOIN "Tag"  ON "Tag"."ID"  = "Tagged"."TagID"
            WHERE "Tagged"."Source" = 2
              AND "Tagged"."ObjectTypeID" = 9
         ORDER BY "File"."HashID" ASC
)
AS A

Locations

Find Sensitive Site Violations

This is the query Truxton uses to find Sensitive Site violations in a piece of media.

SELECT "SensitiveSite"."ID", 
    "SensitiveSite"."SensitiveSiteListID", 
    "SensitiveSite"."Latitude", 
    "SensitiveSite"."Longitude", 
    "SensitiveSite"."Distance", 
    "SensitiveSite"."Name", 
    "Location"."ID" AS "LocationID", 
    "Location"."FileID", 
    "Location"."MediaID", 
    "Location"."Label", 
    "SensitiveSiteList"."Name" AS "ListName", 
    "SensitiveSiteList"."Description" AS "ListDescription", 
    earth_distance( ll_to_earth( "SensitiveSite"."Latitude", "SensitiveSite"."Longitude" ),
                    ll_to_earth( "Location"."Latitude", "Location"."Longitude" ) ) AS "OffendingDistance", 
    "Location"."LocationTypeID", 
    "Location"."Latitude", 
    "Location"."Longitude" 
FROM "SensitiveSite"
INNER JOIN "SensitiveSiteList" ON ( "SensitiveSite"."SensitiveSiteListID" = "SensitiveSiteList"."ID" )
INNER JOIN "Location" ON earth_box( ll_to_earth( "SensitiveSite"."Latitude", "SensitiveSite"."Longitude" ), "SensitiveSite"."Distance" ) @> ll_to_earth( "Location"."Latitude", "Location"."Longitude" ) 
WHERE "Location"."MediaID" = '1b6df4c7-9c11-bd10-44a5-05ab0f09b9e1'::uuid
ORDER BY "ListName", "Label"

External Links

The following links have proven useful: