fox-it / citrix-netscaler-triage

Dissect triage script for Citrix NetScaler devices
https://blog.fox-it.com/2023/08/15/approximately-2000-citrix-netscalers-backdoored-in-mass-exploitation-campaign/
Apache License 2.0
22 stars 6 forks source link

ValueError: Target not recognized as a citrix-netscaler #2

Open Michael-Hennemann opened 1 year ago

Michael-Hennemann commented 1 year ago

Hi! :) I am getting an error when I try to check one of my Netscaler vmdks (NetScaler NS13.0: Build 91.13.nc)

Traceback (most recent call last):
  File "/home/user/TEMP/virtual-env/iocitrix.py", line 267, in <module>
    main()
  File "/home/user/TEMP/virtual-env/iocitrix.py", line 263, in main
    check_targets(args.targets)
  File "/home/user/TEMP/virtual-env/iocitrix.py", line 227, in check_targets
    raise ValueError(f"Target not recognized as a citrix-netscaler: {target.path}: {target.os}")
ValueError: Target not recognized as a citrix-netscaler: netscaler.vmx: default

With the same setup I was able to check another one successfully so I am cautiously optimistic that I am not per se dooing something wrong... ^^

any ideas ?

cheers Michael

maxdevaine commented 1 year ago

I have same problem: ArchLinux, python 3.11.3 NS13.0: Build 91.13.nc

python3 iocitrix.py md0.img+da0.img
Traceback (most recent call last):
  File "/mnt/datastore/temp/netscaller/nsg1-clone/citrix-netscaler-triage/iocitrix.py", line 267, in 
    main()
  File "/mnt/datastore/temp/netscaller/nsg1-clone/citrix-netscaler-triage/iocitrix.py", line 263, in main
    check_targets(args.targets)
  File "/mnt/datastore/temp/netscaller/nsg1-clone/citrix-netscaler-triage/iocitrix.py", line 227, in check_targets
    raise ValueError(f"Target not recognized as a citrix-netscaler: {target.path}: {target.os}")
ValueError: Target not recognized as a citrix-netscaler: md0.img+da0.img: default

I already tried load vmx too, but same problem.

Max

yunzheng commented 1 year ago

It looks it doesn't recognise it as a NetScaler device, can you try loading it with target-shell and then ls?:

$ target-shell netscaler.vmx
...<snipped>

netscaler.vmx /> ls
.nscli_history
.snap
bin
colorful
...<snipped>
Michael-Hennemann commented 1 year ago

Here are the two examples run from the same laptop (manjaro linux)

non-working

target-shell ./netscaler.vmx                                                                 
2023-08-17T14:48:05.688147Z [warning  ] <Target netscaler.vmx>: Can't identify filesystem: <Volume name='part_00007e00' size=1677721600 fs=None> [dissect.target.target] 
2023-08-17T14:48:05.688750Z [warning  ] <Target netscaler.vmx>: Can't identify filesystem: <Volume name='part_64007e00' size=4404019200 fs=None> [dissect.target.target] 
2023-08-17T14:48:05.689265Z [warning  ] <Target netscaler.vmx>: Can't identify filesystem: <Volume name='part_16a807e00' size=2097152 fs=None> [dissect.target.target] 
2023-08-17T14:48:05.689781Z [warning  ] <Target netscaler.vmx>: Can't identify filesystem: <Volume name='part_16aa07e00' size=15384110592 fs=None> [dissect.target.target] 
2023-08-17T14:48:06.118262Z [error    ] Unable to import dissect.target.plugins.filesystem.yara [dissect.target.plugin] 
2023-08-17T14:48:06.185497Z [warning  ] <Target netscaler.vmx>: Failed to find OS plugin, falling back to default [dissect.target.target] 

working

target-shell other-netscaler.vmx                                                                  
2023-08-17T14:48:52.960770Z [warning  ] <Target other-netscaler.vmx>: Can't identify filesystem: <Volume name='part_66600000' size=4401922048 fs=None> [dissect.target.target] 
2023-08-17T14:48:52.962487Z [warning  ] <Target other-netscaler.vmx>: Can't identify filesystem: <Volume name='part_16cc00000' size=2097152 fs=None> [dissect.target.target] 
2023-08-17T14:48:53.344259Z [error    ] Unable to import dissect.target.plugins.filesystem.yara [dissect.target.plugin] 
ns /> ls
flash
var
ns /> 
maxdevaine commented 1 year ago
$ target-shell da0.img 
2023-08-17T14:51:31.344873Z [warning  ] : Can't identify filesystem:  [dissect.target.target] 
2023-08-17T14:51:31.345339Z [warning  ] : Can't identify filesystem:  [dissect.target.target] 
2023-08-17T14:51:31.345666Z [warning  ] : Can't identify filesystem:  [dissect.target.target] 
2023-08-17T14:51:31.346011Z [warning  ] : Can't identify filesystem:  [dissect.target.target] 
2023-08-17T14:51:31.398467Z [error    ] Unable to import dissect.target.plugins.filesystem.yara [dissect.target.plugin] 
2023-08-17T14:51:31.663025Z [warning  ] : Failed to find OS plugin, falling back to default [dissect.target.target]
$ target-shell nsg1-clone_3-flat.vmdk 
2023-08-17T14:54:02.007151Z [warning  ] : Can't identify filesystem:  [dissect.target.target] 
2023-08-17T14:54:02.007616Z [warning  ] : Can't identify filesystem:  [dissect.target.target] 
2023-08-17T14:54:02.007962Z [warning  ] : Can't identify filesystem:  [dissect.target.target] 
2023-08-17T14:54:02.008331Z [warning  ] : Can't identify filesystem:  [dissect.target.target] 
2023-08-17T14:54:02.060458Z [error    ] Unable to import dissect.target.plugins.filesystem.yara [dissect.target.plugin] 
2023-08-17T14:54:02.329494Z [warning  ] : Failed to find OS plugin, falling back to default [dissect.target.target]
$ target-shell nsg1-clone.vmx 
2023-08-17T14:54:37.019520Z [warning  ] : Can't identify filesystem:  [dissect.target.target] 
2023-08-17T14:54:37.019983Z [warning  ] : Can't identify filesystem:  [dissect.target.target] 
2023-08-17T14:54:37.020318Z [warning  ] : Can't identify filesystem:  [dissect.target.target] 
2023-08-17T14:54:37.020679Z [warning  ] : Can't identify filesystem:  [dissect.target.target] 
2023-08-17T14:54:37.075745Z [error    ] Unable to import dissect.target.plugins.filesystem.yara [dissect.target.plugin] 
2023-08-17T14:54:37.354086Z [warning  ] : Failed to find OS plugin, falling back to default [dissect.target.target]
yunzheng commented 1 year ago

@Michael-Hennemann @maxdevaine

It looks like dissect doesn't recognise the partitions or disk, can you run gpart show on the NetScaler in question, and paste the output? Example:

$ ssh netscaler
 Done
> shell
root@ns# gpart show
=>      63  41942977  da0  MBR  (20G)
        63  41942943    1  freebsd  [active]  (20G)
  41943006        34       - free -  (17K)

=>       0  41942943  da0s1  BSD  (20G)
         0   3354624      1  freebsd-ufs  (1.6G)
   3354624   8597504      2  freebsd-swap  (4.1G)
  11952128      4096      4  freebsd-ufs  (2.0M)
  11956224  29986719      5  freebsd-ufs  (14G)
maxdevaine commented 1 year ago

Life NetScaller:

root@ns# gpart show
=>      63  41942977  da0  MBR  (20G)
        63  41929587    1  freebsd  [active]  (20G)
  41929650     13390       - free -  (6.6M)

=>       0  41929587  da0s1  BSD  (20G)
         0   3276800      1  freebsd-ufs  (1.6G)
   3276800   8601600      2  freebsd-swap  (4.1G)
  11878400      4096      4  freebsd-ufs  (2.0M)
  11882496  30047091      5  freebsd-ufs  (14G)

Disk da0.img

$ gdisk -l da0.img
GPT fdisk (gdisk) version 1.0.9.1

Partition table scan:
  MBR: MBR only
  BSD: not present
  APM: not present
  GPT: not present

***************************************************************
Found invalid GPT and valid MBR; converting MBR to GPT format
in memory. 
***************************************************************

Disk da0.img: 41943040 sectors, 20.0 GiB
Sector size (logical): 512 bytes
Disk identifier (GUID): 78D1B62E-EE34-41F1-A6D7-D91F9FE2864C
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 41943006
Partitions will be aligned on 1-sector boundaries
Total free space is 13386 sectors (6.5 MiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1              63        41929649   20.0 GiB    A500  FreeBSD disklabel

$ fdisk -l da0.img 
Disk da0.img: 20 GiB, 21 474 836 480 bajtů, 41 943 040 sektorů
Jednotky: sektorů po 1 * 512 = 512 bajtech
Velikost sektoru (logického/fyzického): 512 bajtů / 512 bajtů
Velikost I/O (minimální/optimální): 512 bajtů / 512 bajtů
Typ popisu disku: dos
Identifikátor disku: 0x0f800000

Zařízení   Zaveditelný Začátek    Konec  Sektory Velikost ID Druh
da0.img1   *                63 41929649 41929587      20G a5 FreeBSD

vmdk image:

$ gdisk -l nsg1-clone_3-flat.vmdk 
GPT fdisk (gdisk) version 1.0.9.1

Partition table scan:
  MBR: MBR only
  BSD: not present
  APM: not present
  GPT: not present

***************************************************************
Found invalid GPT and valid MBR; converting MBR to GPT format
in memory. 
***************************************************************

Disk nsg1-clone_3-flat.vmdk: 41943040 sectors, 20.0 GiB
Sector size (logical): 512 bytes
Disk identifier (GUID): 1607B779-D7A9-4E83-A577-5DB05485D246
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 41943006
Partitions will be aligned on 1-sector boundaries
Total free space is 13386 sectors (6.5 MiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1              63        41929649   20.0 GiB    A500  FreeBSD disklabel

$ fdisk -l nsg1-clone_3-flat.vmdk
Disk nsg1-clone_3-flat.vmdk: 20 GiB, 21 474 836 480 bajtů, 41 943 040 sektorů
Jednotky: sektorů po 1 * 512 = 512 bajtech
Velikost sektoru (logického/fyzického): 512 bajtů / 512 bajtů
Velikost I/O (minimální/optimální): 512 bajtů / 512 bajtů
Typ popisu disku: dos
Identifikátor disku: 0x0f800000

Zařízení                Zaveditelný Začátek    Konec  Sektory Velikost ID Druh
nsg1-clone_3-flat.vmdk1 *                63 41929649 41929587      20G a5 FreeBSD
Michael-Hennemann commented 1 year ago

Netscaler Live Non-Working

root@ns# gpart show
=>      63  41942977  da0  MBR  (20G)
        63  41929587    1  freebsd  [active]  (20G)
  41929650     13390       - free -  (6.6M)

=>       0  41929587  da0s1  BSD  (20G)
         0   3276800      1  freebsd-ufs  (1.6G)
   3276800   8601600      2  freebsd-swap  (4.1G)
  11878400      4096      4  freebsd-ufs  (2.0M)
  11882496  30047091      5  freebsd-ufs  (14G)

Other-Netscaler Live Working

root@ns# gpart show
=>      63  41942977  da0  MBR  (20G)
        63  41942943    1  freebsd  [active]  (20G)
  41943006        34       - free -  (17k)

=>       0  41942943  da0s1  BSD  (20G)
         0   3354624      1  freebsd-ufs  (1.6G)
   3354624   8597504      2  freebsd-swap  (4.1G)
  11952128      4096      4  freebsd-ufs  (2.0M)
  11956224  29986719      5  freebsd-ufs  (14G)
Michael-Hennemann commented 1 year ago

Netsclaer Non-Working

gdisk -l netscaler-flat.vmdk                                                                 
GPT fdisk (gdisk) version 1.0.9.1

Warning: File size is not a multiple of 512 bytes! Misbehavior is likely!
Warning: File size is not a multiple of 512 bytes! Misbehavior is likely!
Warning: File size is not a multiple of 512 bytes! Misbehavior is likely!
Partition table scan:
  MBR: MBR only
  BSD: not present
  APM: not present
  GPT: not present

***************************************************************
Found invalid GPT and valid MBR; converting MBR to GPT format
in memory. 
***************************************************************

Warning! Secondary partition table overlaps the last partition by
1145765 blocks!
You will need to delete this partition or resize it in another utility.
Disk netscaler-flat.vmdk: 40783918 sectors, 19.4 GiB
Sector size (logical): 512 bytes
Disk identifier (GUID): 6D3124B7-28FB-4CEB-AE38-582DC99ED742
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 40783884
Partitions will be aligned on 1-sector boundaries
Total free space is 29 sectors (14.5 KiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1              63        41929649   20.0 GiB    A500  FreeBSD disklabel

Other-Netscaler Working

gdisk -l other-netscaler-flat.vmdk                                                          
GPT fdisk (gdisk) version 1.0.9.1

Partition table scan:
  MBR: MBR only
  BSD: not present
  APM: not present
  GPT: not present

***************************************************************
Found invalid GPT and valid MBR; converting MBR to GPT format
in memory. 
***************************************************************

Disk other-netscaler-flat.vmdk: 41943040 sectors, 20.0 GiB
Sector size (logical): 512 bytes
Disk identifier (GUID): D37BC93D-33D2-4CC6-A60A-AB26822D541B
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 41943006
Partitions will be aligned on 1-sector boundaries
Total free space is 30 sectors (15.0 KiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1              63        41943005   20.0 GiB    A500  FreeBSD disklabel
yunzheng commented 1 year ago

Hi, we are investigating the issues but we are unable to reproduce with a clean NS13.0-91.13 build. Were these systems upgraded from an older version? and what were those versions?

In /flash directory of the NetScaler you can find the older kernels and therefore see the possible upgrade path, example here:

> shell
root@ns# ls /flash/*.gz
/flash/ns-13.0-52.24.gz
/flash/ns-13.0-58.32.gz
/flash/ns-13.1-49.13.gz
Michael-Hennemann commented 1 year ago

Hm, is it possible that this gets cleaned up after some updates ?

The one that is non working says "/flash/ns-12.0-63.21.gz /flash/ns-13.0-91.13.gz" but I am pretty sure that I have done at least 10-20 updates...

The same with the one that is working /flash/ns-12.1-52.15.gz /flash/ns-12.1-55.13.gz /flash/ns-12.1-55.18.gz /flash/ns-13.0-91.13.gz

yunzheng commented 1 year ago

Yes, that's possible. Maybe it only keeps the last 4 kernels. But good to know it's been upgraded from a 12.1.

We've been investigating a bit and think there may be something funky going on with the parsing of the disklabel partition.

From the Dissect logging you provided, we can tell that in the broken case, Dissect thinks that the "swap" partition starts at offset 0x64007e00. However, from your gpart output we can tell it's supposed to be 0x64000000. Would you be able to provide us with the first 1MB of the disk to aid in debugging this behaviour? You can do this with the following command on the flat VMDK of the non-working image:

dd if=netscaler-flat.vmdk of=debug-file.bin bs=1M count=1

You could then attach debug-file.bin to this ticket, however if you're not comfortable with that, feel free to email it to srt@fox-it.com!

@maxdevaine your Dissect logs do not contain the <Volume name= bit in the log line, did you maybe remove these yourself? It's likely the same problem, but it would be nice to confirm.

Michael-Hennemann commented 1 year ago

I can probably send it via email. I guess the first 1 MB won't contain any personal information... :-)

Schamper commented 1 year ago

I can probably send it via email. I guess the first 1 MB won't contain any personal information... :-)

You can double check with a hexdump on your own file of course, but it only contains the partition tables, the superblock, root inode and some other miscellaneous metadata of the first UFS filesystem!

yunzheng commented 1 year ago

@Michael-Hennemann It should be fixed by above merged patch. Thank you providing the test data, it really helped with debugging this issue!

You need to install the updated dissect.volume using:

pip install --upgrade --pre dissect.volume

It might complain about pinned dependencies, but you can ignore this. If you can verify if this works, we can probably release a new patch version of Dissect.

Michael-Hennemann commented 1 year ago

Hi, I got the following error:

Installing collected packages: dissect.volume
  Attempting uninstall: dissect.volume
    Found existing installation: dissect.volume 3.6
    Uninstalling dissect.volume-3.6:
      Successfully uninstalled dissect.volume-3.6
ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
dissect 3.8.1 requires dissect.volume==3.6, but you have dissect-volume 3.7.dev1 which is incompatible.
Successfully installed dissect.volume-3.7.dev1

target-shell netscaler-flat.vmdk still cannot "ls" the netscaler disk.

But the iocitrix script seems to work right now ! :-D

I will test it and get back to you. Thank you very much so far !!

Michael-Hennemann commented 1 year ago

Hm, the script throws an error and exits during execution:

*** Checking for SUID Binaries (this takes a while) ***

Traceback (most recent call last):
  File "/home/user/TEMP/virtual-env/iocitrix.py", line 267, in <module>
    main()
  File "/home/user/TEMP/virtual-env/iocitrix.py", line 263, in main
    check_targets(args.targets)
  File "/home/user/TEMP/virtual-env/iocitrix.py", line 230, in check_targets
    target_findings = ioc_check_target(target)
                      ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/TEMP/virtual-env/iocitrix.py", line 216, in ioc_check_target
    for finding in check_suid_binaries(target):
  File "/home/user/TEMP/virtual-env/iocitrix.py", line 143, in check_suid_binaries
    for suid_binary_record in target.suid_binaries():
  File "/home/user/TEMP/virtual-env/lib/python3.11/site-packages/dissect/target/plugins/filesystem/unix/suid.py", line 28, in suid_binaries
    for record in self.target.walkfs():
  File "/home/user/TEMP/virtual-env/lib/python3.11/site-packages/dissect/target/plugins/filesystem/walkfs.py", line 34, in walkfs
    for _, record in self.walkfs_ext():
  File "/home/user/TEMP/virtual-env/lib/python3.11/site-packages/dissect/target/plugins/filesystem/walkfs.py", line 40, in walkfs_ext
    for entry in fs.path(root).rglob(pattern):
  File "/usr/lib/python3.11/pathlib.py", line 968, in rglob
    for p in selector.select_from(self):
  File "/usr/lib/python3.11/pathlib.py", line 408, in _select_from
    for p in successor_select(starting_point, is_dir, exists, scandir):
  File "/usr/lib/python3.11/pathlib.py", line 356, in _select_from
    entries = list(scandir_it)
              ^^^^^^^^^^^^^^^^
  File "/home/user/TEMP/virtual-env/lib/python3.11/site-packages/dissect/target/filesystems/ffs.py", line 81, in scandir
    for entry in self._iterdir():
  File "/home/user/TEMP/virtual-env/lib/python3.11/site-packages/dissect/target/filesystems/ffs.py", line 70, in _iterdir
    for entry in self.entry.iterdir():
  File "/home/user/TEMP/virtual-env/lib/python3.11/site-packages/dissect/ffs/ffs.py", line 264, in iterdir
    buf = self.open()
          ^^^^^^^^^^^
  File "/home/user/TEMP/virtual-env/lib/python3.11/site-packages/dissect/ffs/ffs.py", line 333, in open
    return RunlistStream(self.fs.fh, self.dataruns(), self.size, self.fs.fragment_size)
                                     ^^^^^^^^^^^^^^^
  File "/home/user/TEMP/virtual-env/lib/python3.11/site-packages/dissect/ffs/ffs.py", line 297, in dataruns
    for block_num in self._iter_blocks():
  File "/home/user/TEMP/virtual-env/lib/python3.11/site-packages/dissect/ffs/ffs.py", line 336, in _iter_blocks
    num_blocks = (self.size + (self.fs.block_size - 1)) // self.fs.block_size
                  ^^^^^^^^^
  File "/usr/lib/python3.11/functools.py", line 1001, in __get__
    val = self.func(instance)
          ^^^^^^^^^^^^^^^^^^^
  File "/home/user/TEMP/virtual-env/lib/python3.11/site-packages/dissect/ffs/ffs.py", line 183, in size
    return self.inode.di_size
           ^^^^^^^^^^
  File "/usr/lib/python3.11/functools.py", line 1001, in __get__
    val = self.func(instance)
          ^^^^^^^^^^^^^^^^^^^
  File "/home/user/TEMP/virtual-env/lib/python3.11/site-packages/dissect/ffs/ffs.py", line 179, in inode
    return self._read_inode()
           ^^^^^^^^^^^^^^^^^^
  File "/home/user/TEMP/virtual-env/lib/python3.11/site-packages/dissect/ffs/ffs.py", line 171, in _read_inode
    return self.fs._inode_type(self.fs.fh)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/TEMP/virtual-env/lib/python3.11/site-packages/dissect/cstruct/types/base.py", line 24, in __call__
    return self.read(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/TEMP/virtual-env/lib/python3.11/site-packages/dissect/cstruct/types/base.py", line 73, in read
    return self._read(obj)
           ^^^^^^^^^^^^^^^
  File "<compiled ufs2_dinode>", line 15, in _read
EOFError
maxdevaine commented 1 year ago

@yunzheng

Thank you, new patch for dissect.volume solved my problem. All works now.

Max

yunzheng commented 1 year ago

@Michael-Hennemann this looks a bit more difficult to troubleshoot using GitHub issues, let's discuss this further over email.

SnaXen commented 1 year ago

Any news on this issue? I have almost the same.

~/citrix-netscaler-triage$ python3 iocitrix.py da0.img Traceback (most recent call last): File "/home/user/citrix-netscaler-triage/iocitrix.py", line 267, in main() File "/home/user/citrix-netscaler-triage/iocitrix.py", line 263, in main check_targets(args.targets) File "/home/user/citrix-netscaler-triage/iocitrix.py", line 227, in check_targets raise ValueError(f"Target not recognized as a citrix-netscaler: {target.path}: {target.os}") ValueError: Target not recognized as a citrix-netscaler: da0.img: default

disk -l da0.img

GPT fdisk (gdisk) version 1.0.9

Warning: File size is not a multiple of 512 bytes! Misbehavior is likely! Warning: File size is not a multiple of 512 bytes! Misbehavior is likely! Warning: File size is not a multiple of 512 bytes! Misbehavior is likely! Partition table scan: MBR: not present BSD: not present APM: not present GPT: not present

Creating new GPT entries in memory. Disk da0.img: 41943040 sectors, 20.0 GiB Sector size (logical): 512 bytes Disk identifier (GUID): 0AC8CB5C-94D7-4236-915F-5D5C2BB6DFC1 Partition table holds up to 128 entries Main partition table begins at sector 2 and ends at sector 33 First usable sector is 34, last usable sector is 41943006 Partitions will be aligned on 2048-sector boundaries Total free space is 41942973 sectors (20.0 GiB) Number Start (sector) End (sector) Size Code Name

target-shell da0.img

2023-08-21T18:32:38.235729Z [warning ] : Can't identify volume system, adding as raw volume instead: [dissect.target.target]

2023-08-21T18:32:38.357247Z [warning ] : Can't identify filesystem: [dissect.target.target]

2023-08-21T18:32:38.434419Z [error ] Unable to import dissect.target.plugins.filesystem.yara [dissect.target.plugin]

2023-08-21T18:32:38.844824Z [warning ] : Failed to find OS plugin, falling back to default [dissect.target.target]

da0.img /> ls

da0.img />

yunzheng commented 1 year ago

@SnaXen Strange, what command did you use to acquire the image? We recently made our instructions more concise in our README. Can you post the first few bytes of the da0.img using the following command?

$ hexdump -C da0.img | head
SnaXen commented 1 year ago
hexdump -C da0.img | head

Here:

hexdump -C da0.img | head

00000000  67 3a 20 4f 6e 65 20 6f  72 20 6d 6f 72 65 20 52  |g: One or more R|
00000010  50 43 20 6e 6f 64 65 73  20 61 72 65 20 63 6f 6e  |PC nodes are con|
00000020  66 69 67 75 72 65 64 20  77 69 74 68 20 64 65 66  |figured with def|
00000030  61 75 6c 74 20 70 61 73  73 77 6f 72 64 73 2e 20  |ault passwords. |
00000040  46 6f 72 20 65 6e 68 61  6e 63 65 64 20 73 65 63  |For enhanced sec|
00000050  75 72 69 74 79 2c 20 79  6f 75 20 6d 75 73 74 20  |urity, you must |
00000060  63 68 61 6e 67 65 20 74  68 65 20 64 65 66 61 75  |change the defau|
00000070  6c 74 20 52 50 43 20 6e  6f 64 65 20 70 61 73 73  |lt RPC node pass|
00000080  77 6f 72 64 2e 0a 20 44  6f 6e 65 0a fc 31 c0 8e  |word.. Done..1..|
00000090  c0 8e d8 8e d0 bc 00 7c  be 1a 7c bf 1a 06 b9 e6  |.......|..|.....|

It was a colleague og mine that made the image a few days ago. I will try to recreate the image, and then check.

yunzheng commented 1 year ago

@SnaXen Somehow your NetScaler outputted an error message to stdout before the command. Looking from the hexdump bytes I can also see the Done\n message before the image data, you can use the following command to strip it:

cat da0.img | tail -c +141 > da0.img.fixed

Also, did your colleague create an image of md0 ? If you so, you will probably need to strip the error message from that one as well. Then you can load both using:

$ python3 iocitrix.py md0.img.fixed+da0.img.fixed
SnaXen commented 1 year ago

@SnaXen Somehow your NetScaler outputted an error message to stdout before the command. Looking from the hexdump bytes I can also see the Done\n message before the image data, you can use the following command to strip it:

cat da0.img | tail -c +141 > da0.img.fixed

Also, did your colleague create an image of md0 ? If you so, you will probably need to strip the error message from that one as well. Then you can load both using:

$ python3 iocitrix.py md0.img.fixed+da0.img.fixed

@yunzheng Thank you. This fixed my da0.img, but not md0.img.

Here is a hexdump from md0.img, Is it the same fix? $ hexdump -C md0.img | head

00000000  67 3a 20 4f 6e 65 20 6f  72 20 6d 6f 72 65 20 52  |g: One or more R|
00000010  50 43 20 6e 6f 64 65 73  20 61 72 65 20 63 6f 6e  |PC nodes are con|
00000020  66 69 67 75 72 65 64 20  77 69 74 68 20 64 65 66  |figured with def|
00000030  61 75 6c 74 20 70 61 73  73 77 6f 72 64 73 2e 20  |ault passwords. |
00000040  46 6f 72 20 65 6e 68 61  6e 63 65 64 20 73 65 63  |For enhanced sec|
00000050  75 72 69 74 79 2c 20 79  6f 75 20 6d 75 73 74 20  |urity, you must |
00000060  63 68 61 6e 67 65 20 74  68 65 20 64 65 66 61 75  |change the defau|
00000070  6c 74 20 52 50 43 20 6e  6f 64 65 20 70 61 73 73  |lt RPC node pass|
00000080  77 6f 72 64 2e 0a 20 44  6f 6e 65 0a 00 00 00 00  |word.. Done.....|
00000090  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

And here are the dump from md0.img.fixed: Hexdump -C md0.img.fixed | head

00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
0000ffe0  00 00 00 00 00 00 00 00  00 00 00 00 19 01 54 19  |..............T.|
0000fff0  03 00 00 00 18 00 00 00  c8 6d 00 00 04 00 00 00  |.........m......|
00010000  00 00 00 00 00 00 00 00  18 00 00 00 20 00 00 00  |............ ...|
00010010  28 00 00 00 98 03 00 00  00 00 00 00 00 00 00 00  |(...............|
00010020  00 00 00 00 00 00 00 00  00 00 00 00 04 00 00 00  |................|
00010030  00 80 00 00 00 10 00 00  08 00 00 00 02 00 00 00  |................|
00010040  00 00 00 00 00 00 00 00  00 80 ff ff 00 f0 ff ff  |................|
00010050  0f 00 00 00 0c 00 00 00  04 00 00 00 00 10 00 00  |................|
yunzheng commented 1 year ago

Yes same fix, your md0.img.fixed looks correct now. Does the image give an error?

SnaXen commented 1 year ago

Yes same fix, your md0.img.fixed looks correct now. Does the image give an error?

Yes.

python3 iocitrix.py md0.img.fixed

Disks - Volumes - > Hostname : None Domain : None IPs : None OS family : citrix-netscaler (CitrixBsdPlugin) Traceback (most recent call last): File "/home/user/citrix-netscaler-triage/iocitrix.py", line 267, in main() File "/home/user/citrix-netscaler-triage/iocitrix.py", line 263, in main check_targets(args.targets) File "/home/user/citrix-netscaler-triage/iocitrix.py", line 228, in check_targets print_target_info(target) File "/home/user/.local/lib/python3.10/site-packages/dissect/target/tools/info.py", line 130, in print_target_info print(f"OS version : {target.version}") File "/home/user/.local/lib/python3.10/site-packages/dissect/target/target.py", line 622, in __getattr__ result = func.__get__(p) File "/home/user/.local/lib/python3.10/site-packages/dissect/target/helpers/cache.py", line 228, in cache_wrapper return cache.call(*args, **kwargs) File "/home/user/.local/lib/python3.10/site-packages/dissect/target/helpers/cache.py", line 104, in call func_cache[key] = self.func(*args, **kwargs) File "/home/user/.local/lib/python3.10/site-packages/dissect/target/plugins/os/unix/bsd/citrix/_os.py", line 76, in version version = version_path.read_text().strip() File "/usr/lib/python3.10/pathlib.py", line 1134, in read_text with self.open(mode='r', encoding=encoding, errors=errors) as f: File "/home/user/.local/lib/python3.10/site-packages/dissect/target/helpers/fsutil.py", line 734, in open return self._accessor.open(self, mode, buffering, encoding, errors, File "/home/user/.local/lib/python3.10/site-packages/dissect/target/helpers/fsutil.py", line 438, in open raw = path.get().open() File "/home/user/.local/lib/python3.10/site-packages/dissect/target/helpers/fsutil.py", line 674, in get self._entry = self._fs.get(str(self)) File "/home/user/.local/lib/python3.10/site-packages/dissect/target/filesystem.py", line 1259, in get raise FileNotFoundError(full_path) dissect.target.exceptions.FileNotFoundError: /flash/.version
yunzheng commented 1 year ago

@SnaXen make sure to run it with both md0.img.fixed and ad0.img.fixed as they are different disks mounted on different paths. Run it with the + sign between the image files to load them as one Target:


python3 iocitrix.py md0.img.fixed+ad0.img.fixed
SnaXen commented 1 year ago

No errors when running python3 iocitrix.py da0.img.fixed But when I run :

python3 iocitrix.py md0.img.fixed+da0.img.fixed I get the following :

<Target md0.img.fixed+da0.img.fixed> Disks

yunzheng commented 1 year ago

ah this error was fixed yesterday (see also #4). please do the following:

pip install --upgrade --pre dissect.target

And then run iocitrix.py again.

Sorry for the inconvience, we will check what the best way is to ensure these fixes go into a stable release or if we update the README.

SnaXen commented 1 year ago
pip install --upgrade --pre dissect.target

Thank you for all the assistance. The upgrade failed, but after I run pip install --upgrade dissect.target (without --pre) it seems to be ok. I am running python3 iocitrix.py md0.img.fixed+da0.img.fixed without any errors atm.

With --pre I get this error :

ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts. dissect 3.8.1 requires dissect.target[full]==3.11.1, but you have dissect-target 3.11.2.dev16 which is incompatible. dissect 3.8.1 requires dissect.volume==3.6, but you have dissect-volume 3.7.dev1 which is incompatible.

yunzheng commented 1 year ago

The --pre is necessary as the fix is only in the development builds now and not yet in the stable release.

So you can ignore that error, it's just complaining that the pinned versions of the stable release are now mismatching and can cause issues.

Good to hear it's working now :)

jaymahannah commented 1 year ago

How big are these .img files? Mine are all coming out as 10MB.

SnaXen commented 1 year ago

How big are these .img files? Mine are all coming out as 10MB.

My da0.img's are about 20GB's and md0.img's are about 439 MB's.

jaymahannah commented 1 year ago

Thank you; I figured out that the files were not getting dumped to my local machine and I got similar sizes as yours. Next issue:

target-shell: command not found

Any ideas here?

yunzheng commented 1 year ago

Thank you; I figured out that the files were not getting dumped to my local machine and I got similar sizes as yours. Next issue:

target-shell: command not found

Any ideas here?

It’s most likely installed in ~/.local/bin which is not in your $PATH by default. Try executing it directly using ~/.local/bin/target-shell or add the directory to your PATH environment.

preppietechie commented 1 year ago

Even after running the cat command to snip the error that was written to stdout in my md0 and da0 files, I still get the "Target not recognized as a citrix-netscaler" error. When I look at the file in hex, I see the following, followed by a buch of zero-space before what looks like the beginning of some content. Do I need to trim more?

Warning: One or more RPC nodes are configured with default passwords. For enhanced security, you must change the default RPC node password. Done ü1ÀŽÀŽØŽÐ¼|¾|¿¹æó¤éŠ1ö»¾±8/tu…öuq‰Þ€Ãâï…öuÍ€ú€r Š6u€Æ€8òrŠ‰çŠt‹L»|ö½€t-QS»ªU´AÍr ûUªuöÁt[fjfÿtSjj‰æ¸Bë[Y¸Í‰ür¿þUªu ÿã¾¹ë¾Ñë ¾ð뻴ͬ„ÀuôëþInvalid partition tableError loading operating systemMissing operating system€€¥ÿÿÿ?ŸÿUª