SkillAgentSearch skills...

Fjta

FJTA (Forensic Journal Timeline Analyzer) is a tool that analyzes Linux filesystem (ext4, XFS) journals (not systemd-journald logs), generates timelines, and detects suspicious activities.

Install / Use

/learn @mnrkbys/Fjta
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

FJTA - Forensic Journal Timeline Analyzer

FJTA (Forensic Journal Timeline Analyzer) is a tool that analyzes Linux filesystem (ext4, XFS) journals (not systemd-journald logs), generates timelines, and detects suspicious activities.

[!CAUTION] Since testing is only being done with simple disk images, there may be many issues when analyzing more practical disk images.

Features

  • Journal Analysis: Scans ext4 and XFS journals to visualize modification history.
  • Timeline Generation: Organizes events within the journal in chronological order.
  • Suspicious Activity Detection: Identifies deleted files and potentially tampered operations.
  • Cross-Platform: Written in Python, allowing analysis on any operating system.

Supported Artifacts in Filesystem Journals

| Artifacts | ext4 | XFS | |----------------------------------------|:------:|:-----:| | inode | ✅ | ✅ | | Directories with few entries | ✅ | ✅ | | Directories with many entries | ✅[^1] | ✅ | | Short symlink target names | ✅ | ✅ | | Long symlink target names[^2] | ✅ | ❌ | | Short extended attributes | ✅ | ✅ | | Long extended attributes[^3] | ✅[^4] | ❌ | | Non-regular files (e.g. block devices) | ✅ | ✅ | | Year 2038 problem | ✅ | ✅ | | Exported journals | ✅ | ✅ |

[^1]: Currently, only linear directories can be parsed. Support for hash tree directories will be added in future versions. [^2]: Symlink target names stored outside an inode. [^3]: Extended attributes stored outside an inode. [^4]: Only the first data block assigned to the extended attribute is recognized. The EXT4_FEATURE_INCOMPAT_EA_INODE flag is not supported.

Detectable Activities

| Activities | ext4 | XFS | |---------------------------------------|:------:|:-----:| | Creating files | ✅ | ✅ | | Deleting files | ✅ | ✅ | | Modification of extended attributes | ✅ | ✅ | | Timestomping (timestamp manipulation) | ✅ | ✅ | | Other inode metadata changes[^5] | ✅ | ✅ |

[^5]: "Other inode metadata changes" include updates to MACB timestamps (mtime, atime, ctime, and crtime), file size changes, and setting file flags, and more.

Requirements

Tested with the following software and libraries:

Installation From Source

Compile and install the TSK.

[!NOTE] TSK also requires other libraries such as libewf, libvmdk, and so on.

wget https://github.com/sleuthkit/sleuthkit/releases/download/sleuthkit-4.14.0/sleuthkit-4.14.0.tar.gz
tar xvzf sleuthkit-4.14.0.tar.gz
cd sleuthkit-4.14.0
./configure
make
sudo make install
sudo echo /usr/local/lib > /etc/ld.so.conf.d/local-lib.conf
sudo ldconfig

Then, clone FJTA.

git clone https://github.com/mnrkbys/fjta.git
cd fjta

Finally, install required Python packages.

python3 -m venv .venv
source .venv/bin/activate
pip install pytsk3 construct python-magic libewf-python libvmdk-python libvhdi-python

Installation From Packages

Install the TSK package from the Linux distribution you are using.

[!NOTE] In older versions of libvmdk, you cannot open VMDK files created with VMware Workstation for Windows (Japanese edition). The patch was integrated in 2022.

sudo apt install sleuthkit python3-tsk libewf2 libvmdk1 libvhdi1 python3-libewf python3-libvmdk python3-libvhdi

Then, clone FJTA.

git clone https://github.com/mnrkbys/fjta.git
cd fjta

Finally, install required Python packages.

python3 -m venv .venv
source .venv/bin/activate
pip install construct python-magic

Usage

Basic

python ./fjta.py -i ~/ext4.img | jq

Filtering with an inode number

python ./fjta.py -s 0 -i ~/xfs.img | jq 'select(.inode == 101040435)' | less

Filtering with crtime

The to_epoch() function is defined in the helper.sh file, so you need to import it before executing the following command.

source scripts/helper.sh
python ./fjta.py -s 0 -i ~/xfs.img | jq --argjson threshold $(to_epoch "2025-06-23 07:33:20.123456789") 'select(.crtime >= $threshold)'

Filtering with a filename

python ./fjta.py -s 0 -i ~/xfs.img | jq 'select(.names? and ([.names[][]] | index("backdoor.c")))'

Filtering with a string

python ./fjta.py -s 0 -i ~/xfs.img | jq 'select(.info | contains("Added EA: security.selinux"))'

Filtering with a regex pattern

python ./fjta.py -s 0 -i ~/xfs.img | jq 'select(.info | test("added ea: security\\.selinux"; "i"))'

Sample Output (timestomping)

...
{
  "transaction_id": 3,
  "action": "CREATE_INODE|CREATE_HARDLINK",
  "inode": 12,
  "file_type": "REGULAR_FILE",
  "names": {
    "2": [
      "test.txt"
    ]
  },
  "mode": 420,
  "uid": 0,
  "gid": 0,
  "size": 0,
  "atime": 1729038807.9101748,
  "ctime": 1729038807.9101748,
  "mtime": 1729038807.9101748,
  "crtime": 1729038807.9101748,
  "dtime": 0.0,
  "flags": 524288,
  "link_count": 1,
  "symlink_target": "",
  "extended_attributes": [],
  "device_number": {
    "major": 0,
    "minor": 0
  },
  "info": "Crtime: 2024-10-16 00:33:27.910174879 UTC|Link Count: 1"
}
...
{
  "transaction_id": 23,
  "action": "CREATE_INODE|ACCESS|CHANGE|MODIFY|TIMESTOMP",
  "inode": 12,
  "file_type": "REGULAR_FILE",
  "names": {
    "2": [
      "test.txt"
    ]
  },
  "mode": 420,
  "uid": 0,
  "gid": 0,
  "size": 0,
  "atime": 978312225.8287878,
  "ctime": 978312225.8287878,
  "mtime": 978312225.8287878,
  "crtime": 978312225.8287878,
  "dtime": 0.0,
  "flags": 524288,
  "link_count": 1,
  "symlink_target": "",
  "extended_attributes": [],
  "device_number": {
    "major": 0,
    "minor": 0
  },
  "info": "Atime: 2024-10-18 08:25:51.385837319 UTC -> 2001-01-01 01:23:45.828787850 UTC (Timestomp)|Ctime: 2024-10-18 08:25:51.385837319 UTC -> 2001-01-01 01:23:45.828787850 UTC (Timestomp)|Mtime: 2024-10-18 08:25:51.385837319 UTC -> 2001-01-01 01:23:45.828787850 UTC (Timestomp)|Crtime: 2024-10-16 00:33:27.910174879 UTC -> 2001-01-01 01:23:45.828787850 UTC (Timestomp)"
}
...

Detecting data exfiltration by filtering and formatting with jq, awk, and column

In the following output example, you can also see a list of the exfiltrated files.

$ python ./fjta.py -i xfs_data_exfiltration.img | jq -r '
  select(
    (.action == "ACCESS")
    or (.names | to_entries | any(.value[] | test("\\.(zip|rar|7z|gz|bz2)$"; "i")))
  )
  | [
      .inode,
      (.names | tostring),
      .size,
      .action,
      .mode,
      (.mtime  | strftime("%Y-%m-%d %H:%M:%S")),
      (.atime  | strftime("%Y-%m-%d %H:%M:%S")),
      (.ctime  | strftime("%Y-%m-%d %H:%M:%S")),
      (.crtime | strftime("%Y-%m-%d %H:%M:%S"))
    ]
  | @tsv
' |
awk -F'\t' '{printf "%s\t%s\t%s\t%s\t%04o\t%s\t%s\t%s\t%s\n", $1, $2, $3, $4, $5, $6, $7, $8, $9}' | column -s $'\t' -t -N inode,names,size,action,mode,mtime,atime,ctime,crtime
inode    names                       size       action                        mode  mtime                atime                ctime                crtime
132      {"128":["dummy_data"]}      30         ACCESS                        0755  2025-12-19 01:54:10  2025-12-19 01:55:20  2025-12-19 01:54:10  2025-12-19 01:54:10
524416   {"132":["dir1"]}            66         ACCESS                        0755  2025-12-19 01:54:10  2025-12-19 01:55:20  2025-12-19 01:54:10  2025-12-19 01:54:10
1179776  {"524416":["dir2"]}         137        ACCESS                        0755  2025-12-19 01:54:10  2025-12-19 01:55:20  2025-12-19 01:54:10  2025-12-19 01:54:10
1572992  {"524416":["dir3"]}         146        ACCESS                        0755  2025-12-19 01:54:10  2025-12-19 01:55:20  2025-12-19 01:54:10  2025-12-19 01:54:10
...
1179777  {"1179776":["file1"]}       1048576    ACCESS                        0644  2025-12-19 01:54:10  2025-12-19 01:55:20  2025-12-19 01:54:10  2025-12-19 01:54:10
1179778  {"1179776":["file2"]}       1048576    ACCESS                        0644  2025-12-19 01:54:10  2025-12-19 01:55:20  2025-12-19 01:54:10  2025-12-19 01:54:10
1179779  {"1179776":["file3"]}       1048576    ACCESS                        0644  2025-12-19 01:54:10  2025-12-19 01:55:20  2025-12-19 01:54:10  2025-12-19 01:54:10
...
164      {"155":["file99"]}          1048576    ACCESS                        0644  2025-12-19 01:54:11  2025-12-19 01:55:22  2025-12-19 01:54:11  2025-12-19 01:54:11
165      {"155":["file100"]}         1048576    ACCESS                        0644  2025-12-19 01:54:11  2025-12-19 01:55:22  2025-12-19 01:54:11  2025-12-19 01:54:11
167      {"128":["takeout.zip"]}     0          CREATE_INODE|CREATE_HARDLINK  0644  2025-12-19 01:55:22  2025-12-19 01:55:20  2025-12-19 01:55:22  2025-12-19 01:55:20
167      {"128":["takeout.zip"]}     104894079  SIZE_UP                       0644  2025-12-19 01:55:22  2025-12-19 01:55:20  2025-12-19 01:55:22  2025-12-19 01:55:20

How to export filesystem journals

FJTA can analyze exported journals. However, some parameters required for analysis are not included in the journal itself. Therefore, you must also export the corresponding superblock (or filesystem metadata) info

View on GitHub
GitHub Stars104
CategoryDevelopment
Updated6d ago
Forks9

Languages

Python

Security Score

100/100

Audited on Mar 24, 2026

No findings