Wednesday, November 25, 2020

Troubleshooting HTTPS - SSH Connectivity to IBM QRadar with TShark

Had a little issue today, where the team could not connect to an IBM QRadar appliance via SSH or HTTPS. This is somewhat strange as we expect these services to be available for us to be able to do our job. I Assigned the task to an Analyst to take a quick look. here is what we got.

1. I am unable to access QRadar via SSH
2. I am unable to access QRadar via HTTPS

Current solution:
Report to the team that supports QRadar, that we are unable to access the environment via SSH or HTTPS.

What is the QRadar team’s solution?
Allow the IPs (and I assume ports) to the QRadar device. In my opinion, this was not needed, unless they recently blocked it. However, with that step done, the problem still exists.

Working as an Analyst, you are always expected to put your supporting evidence of the issue. This is true whether it is a security issue you are reporting or connectivity issue. Think device up/down (via Nagios, Solarwinds, etc.), monitoring as an example outside of security.

Considering the above, let’s use packet analysis with TShark to see what we can learn about these two issue.

Starting with TCP Port 22 associated with SSH.

Loading up the first 7 frames of the PCAP in TShark, we see:

kali@securitynik:~$ tshark -r ssh.pcapng -c 7
    1   0.000000     10.0.0.1 → 10.0.0.2     TCP 66 57157 → 22 [SYN, ECN, CWR] Seq=0 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
    2   0.029507     10.0.0.2 → 10.0.0.1     TCP 66 22 → 57157 [SYN, ACK] Seq=0 Ack=1 Win=42340 Len=0 MSS=1380 SACK_PERM=1 WS=4096
    3   0.029570     10.0.0.1 → 10.0.0.2     TCP 54 57157 → 22 [ACK] Seq=1 Ack=1 Win=66048 Len=0
    4   0.029698     10.0.0.1 → 10.0.0.2     SSH 96 Client: Protocol (SSH-2.0-SecureCRT_8.5.4 (x64 build 1942))
    5   0.057867     10.0.0.2 → 10.0.0.1     TCP 60 22 → 57157 [ACK] Seq=1 Ack=43 Win=45056 Len=0
    6  58.098216     10.0.0.1 → 10.0.0.2     TCP 54 57157 → 22 [FIN, ACK] Seq=43 Ack=1 Win=66048 Len=0
    7  58.166203     10.0.0.2 → 10.0.0.1     TCP 60 22 → 57157 [ACK] Seq=1 Ack=44 Win=45056 Len=0

Breaking this down further below, we see frames 1 to 3 showing the 3-way handshake completed successfully. Don't let's worry about ECN and CWR in this post. Focus on the SYN -> SYN/ACK -> ACK.

kali@securitynik:~$ tshark -r ssh.pcapng -c 3
    1   0.000000     10.0.0.1 → 10.0.0.2     TCP 66 57157 → 22 [SYN, ECN, CWR] Seq=0 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
    2   0.029507     10.0.0.2 → 10.0.0.1     TCP 66 22 → 57157 [SYN, ACK] Seq=0 Ack=1 Win=42340 Len=0 MSS=1380 SACK_PERM=1 WS=4096
    3   0.029570     10.0.0.1 → 10.0.0.2     TCP 54 57157 → 22 [ACK] Seq=1 Ack=1 Win=66048 Len=0

Looking at above, we conclude the host is available and that port TCP 22, typically associated with SSH is available. 

Looking closer at the SSH communication in frame 4, we see our client begins communication at the application layer, sending its SSH identification information. 

kali@securitynik:~$ tshark -n -r ssh.pcap -Y 'frame.number==4'
    4   0.029698     10.0.0.1 → 10.0.0.2     SSH 96 Client: Protocol (SSH-2.0-SecureCRT_8.5.4 (x64 build 1942))

Transitioning to the response from the server, we see:

kali@securitynik:~$ tshark -n -r ssh.pcapng -Y '(frame.number==5) || (frame.number==6) || (frame.number==7)'     
    5   0.057867     10.0.0.2 → 10.0.0.1     TCP 60 22 → 57157 [ACK] Seq=1 Ack=43 Win=45056 Len=0
    6  58.098216     10.0.0.1 → 10.0.0.2     TCP 54 57157 → 22 [FIN, ACK] Seq=43 Ack=1 Win=66048 Len=0
    7  58.166203     10.0.0.2 → 10.0.0.1     TCP 60 22 → 57157 [ACK] Seq=1 Ack=44 Win=45056 Len=0

What is interesting about above, is the SSH Client starts the SSH connection with the client’s QRadar, sending its protocol and client information (Identification information).

The Client’s QRadar responds with “ACK”, then our SSH Client initiates a graceful teardown of the communication by setting its “FIN, ACK” flags. Looking at this, we can see our SSH Client is responsible for initiating the teardown of the communication. But why is this so? 

Looking at RFC 4253 (RFC 4253 - The Secure Shell (SSH) Transport Layer Protocol (ietf.org) ) it states …

When the connection has been established, both sides MUST send an

   identification string.  This identification string MUST be

      SSH-protoversion-softwareversion SP comments CR LF


Note the MUST in the wording above. Looking back above, we see our SSH client sends its identification string as “SSH-2.0-SecureCRT_8.5.4 (x64 build 1942)”. However, the Client’s QRadar, rather than sending its identification string, simply responded with “ACK”. We don’t see its identification string. I believe this is the reason why our SSH client requested to close the connection as it is unable to transition to the Key Exchange phase of the SSH connection. I believe if we run the SSH client in Debug mode if available, we may see this as the issue.

Issue Number 2: Looking at HTTPS. This is less complicated to see what the issue is.

kali@securitynik:~$ tshark -r 443-4.pcapng -Y "(tcp.port==57129) && (tcp.port==443)"
   23   6.960410     10.0.0.1 → 10.0.0.2     TCP 66 57129 → 443 [SYN, ECN, CWR] Seq=0 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
   24   6.989644     10.0.0.2 → 10.0.0.1     TCP 60 443 → 57129 [RST, ACK] Seq=1 Ack=1 Win=0 Len=0
   29   7.489974     10.0.0.1 → 10.0.0.2     TCP 66 [TCP Retransmission] 57129 → 443 [SYN] Seq=0 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
   30   7.519994     10.0.0.2 → 10.0.0.1     TCP 60 443 → 57129 [RST, ACK] Seq=4184603348 Ack=1 Win=0 Len=0
   35   8.019747     10.0.0.1 → 10.0.0.2     TCP 62 [TCP Retransmission] 57129 → 443 [SYN] Seq=0 Win=8192 Len=0 MSS=1460 SACK_PERM=1
   36   8.049017     10.0.0.2 → 10.0.0.1     TCP 60 443 → 57129 [RST, ACK] Seq=739404838 Ack=1 Win=0 Len=0

As you look above, you see the HTTPS client sends its “SYN”. However, it is unable to complete the 3-way handshake. How do we know this? Look at the session above where we discussed SSH. However, staying with the HTTPS connection, we see the QRadar responds with “RST, ACK”. This tells me it is not listening on port 443. 


With the above analysis, what have we concluded:
1. The QRadar devce is UP. We know this because SSH 3-way handshake completed successfully on TCP port 22 and the QRadar also sent “RST” stating that HTTPS on TCP port 443 is not “listening”
2. SSH does not seems to be fully available on the QRadar device as it did not return its identification string.
3. HTTPS is not available/listening on the QRadar device.


Recommended solutions based on the analysis above:
1. Work with the remote team to restart the SSH and HTTPS services on the QRadar device.
2. If recommendation 1 fails, there is going to be a need to restart the QRadar console.


Takeaway for you the reader.

As a Security Analyst, ensure you have enough evidence to support your positions. This is true whether you are reporting on a security incident or addressing a device down.


If you wish to learn more about the importance of being able to analyze packets, come hangout with us at an upcoming SEC503 - Intrusion Detection in Depth or at an upcoming SEC582 - Mastering TShark Packet Analysis class. Alternatively grab a copy of my book Mastering TShark Network Forensics.


Additional read to help you get going in the meantime.

Learning by practicing: Windump basics by examples (securitynik.com)
Learning by practicing: a few not so basic windump examples (securitynik.com)
Man page of TCPDUMP
RFC 4253 - SSH


Wednesday, November 4, 2020

Continuing DLL Injection via CreateRemoteThread

In a previous post on DLL injection, I hardcoded the code for the DLL. In this post, I am giving you the opportunity to specify your process ID and DLL to be injected.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/*
* DLL Injection - using create remote thread 
* This is a follow-up to my previous blog posts and as in the previous instance, 
*  this is purely for educational purposes.
* https://www.securitynik.com/2019/01/beginning-dll-injection-with-windows_23.html
* https://www.securitynik.com/2019/01/beginning-dll-injection-with-windows.html
* 
* In those previous posts, I required admin privileges. In this post, I'm working without admin privileges
* Author Nik Alleyne
* Author Blog: www.securitynik.com
* File: dllInjection-CreateRemoteThread.c
* Date: October 4, 2020
*/


#include <windows.h>
#include <Psapi.h>
#include <stdio.h>

int main(int argc, char* argv[])
{
	// Clear the screen before getting started
	system("cls");

	// Check to see if the number of arguments, equals 3.
	// First argument is the program, second is the process PID and third is the full path to the DLL
	
	if (argc != 3)
	{
		printf(" ===========================================================================\n");
		printf("[*] Usage info. | dllInjection-CreateRemoteThread.exe \n");
		printf("[*]");
		printf("dllInjection-CreateRemoteThread.exe PID Path to DLL \n");
		printf("\t eg. dllInjection-CreateRemoteThread.exe 3000 c:\\tmp\\mydll.dll \n");
		printf(" ===========================================================================\n");
		return -1;
	}

	printf("                  Beginning the DLL Injection Process ... \n");
	printf("------------------------------------------------------------------------------ \n");
	Sleep(1000);

	printf("[*] Enumerating all processes ... \n");
	Sleep(1000);

	// Setup point to the array which will hold the processes
	DWORD myProcessList[4096], sizeOfArray, totalProcesses;
	if (!EnumProcesses(myProcessList, sizeof(myProcessList), &sizeOfArray))
	{
		printf(" [!] Error Code: %u was encountered while retrieving processes information \n", GetLastError());
		return -1;
	}

	// calculate the number of processes returned
	totalProcesses = sizeOfArray / sizeof(DWORD);
	printf(" [*] There were %u processes found \n", totalProcesses);
	Sleep(1000);

	printf("[*] Searching for process with PID:%u \n", atoi(argv[1]));
	// Create a variable to track if the PID was found
	BOOL pidFound = FALSE;

	// Find the process with PID of interest
	// first declare a counter for the array
	unsigned int count = 0;
	for (count; count < totalProcesses; count++)
		if (myProcessList[count] == atoi(argv[1]))
		{
			pidFound = TRUE;
			break;
		}
	

	if (!pidFound)
	{
		printf(" [!] Unable to locate the process with PID:%u \n", atoi(argv[1]));
		return -1;
	}
		
	printf(" [*] Process with PID:%u Found \n", atoi(argv[1]));
	Sleep(1000);

	// Now that the PID exists, open a handle to it.
	printf("[+] Opening a handle to process with PID:%u  ...\n", atoi(argv[1]));

	HANDLE myRemoteProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, NULL, atoi(argv[1]));
	if (!myRemoteProcessHandle)
	{
		printf(" [!] Error %u occured while attempting to get a handle \n", GetLastError());
		return -1;
	}
	
	printf(" [+] Handle 0x%p successfully opened \n", myRemoteProcessHandle);
	Sleep(1000);

	// Allocating space in memory
	printf("[+] Allocating space in the memory of process with PID: %u \n", atoi(argv[1]));
	VOID* myRemoteProcessAddress = VirtualAllocEx(myRemoteProcessHandle, NULL, 256, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
	if (!myRemoteProcessAddress)
	{
		printf(" [!] Unable to allocate space in the process. Error code was %u \n", GetLastError());
		return -1;
	}

	printf(" [+] Space successfully allocated \n");
	Sleep(1000);

	// Writing DLL Path to process Memory
	printf("[+] Writing to the process memory ... \n");
	if (!WriteProcessMemory(myRemoteProcessHandle, myRemoteProcessAddress, argv[2], strlen(argv[2]), nullptr))
	{
		printf(" [!] Error %u encountered while writing to the process with PID:%u memory \n", GetLastError(), atoi(argv[1]));
		return -1;
	}
	printf(" [+] Process memory successfully written ... \n");
	Sleep(1000);

	// Executing the remote threat
	printf("[+] Creating the remote thread ...\n");
	HANDLE myRemoteProcessNewThread = CreateRemoteThread(myRemoteProcessHandle, nullptr, 0, (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryA"), myRemoteProcessAddress, 0, nullptr);

	if (!myRemoteProcessNewThread)
	{
		printf(" [!] Error occurred while creating remote thread!\n");
		return -1;
	}

	printf(" [+] Remote thread successfully created \n");
	Sleep(1000);

	// Close up shop
	CloseHandle(myRemoteProcessHandle);
	printf("[*] I'm done my work. See ya and hope you enjoyed that demo! \n");
	
	return 0;
}



/*
* References:
* https://docs.microsoft.com/en-us/windows/win32/psapi/enumerating-all-modules-for-a-process
* https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-enumprocesses
* https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess
* https://docs.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights
* https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-writeprocessmemory
* 
*/

Have fun!

Beginning File System Forensics - learning about the disk and the Master Boot Record (MBR)

In a previous post, we acquired the image. In this post, let's learn about the disk image which was acquired. 

Let's verify the MD5 hash of the image:

kali@securitynik:~/forensics$ md5sum linux_mint_usb.raw
e995c8773f355b895792fafdc24e80d4  linux_mint_usb.raw

Looks like a match with what we saw in the previous post. 

kali@securitynik:~/forensics$ cat linux_mint_usb.hash | grep md5
Total (md5): e995c8773f355b895792fafdc24e80d4

Before mounting the disk, let's see what we learn from "fdisk --list"

kali@securitynik:~/forensics$ sudo fdisk --list linux_mint_usb.raw
[sudo] password for kali: 
Disk linux_mint_usb.raw: 29.3 GiB, 31457280000 bytes, 61440000 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x00051443

Device              Boot Start      End  Sectors  Size Id Type
linux_mint_usb.raw1       2048 61437951 61435904 29.3G  c W95 FAT32 (LBA)

Above, we see we have a disk reported as "29.3" GiB

We see the sectors are of size 512 bytes
Units: sectors of 1 * 512 = 512 bytes

We also see the disk seems to have one partition which is Win95 FAT32.

Device                  Boot    Start      End  Sectors  Size Id Type
linux_mint_usb.raw1       2048 61437951 61435904 29.3G  c W95 FAT32 (LBA)

Let's now look at the raw bytes, to confirm some of the information above. 

What we know so far, is the disk has sectors of 512 bytes. The first sector  contains the Master Boot Record (MBR). The MBR is considered the most important data structure on the disk and is created when the disk is partitioned. It loads code from the bootable partitions which in turn execute your operating system bootloader. While every hard disk contains an MBR, its code is only used if the disk has an active primary partition. 

Let's look at the first 512 bytes where the MBR resides.

00000000: fab8 0010 8ed0 bc00 b0b8 0000 8ed8 8ec0  ................
00000010: fbbe 007c bf00 06b9 0002 f3a4 ea21 0600  ...|.........!..
00000020: 00be be07 3804 750b 83c6 1081 fefe 0775  ....8.u........u
00000030: f3eb 16b4 02b0 01bb 007c b280 8a74 018b  .........|...t..
00000040: 4c02 cd13 ea00 7c00 00b0 a103 0000 0000  L.....|.........
00000050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000060: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000070: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000080: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000090: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000100: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000110: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000120: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000130: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000140: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000150: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000160: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000170: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000180: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000190: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001b0: 0000 0000 0000 0000 4314 0500 0000 0020  ........C...... 
000001c0: 2100 0cfe ffff 0008 0000 0070 a903 0000  !..........p....
000001d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001f0: 0000 0000 0000 0000 0000 0000 0000 55aa  ..............U.

Above shows the first 512 bytes of the master boot record. 

Here is a breakdown of those 512 bytes
Offset 0 - 445 (length 446 bytes): 
This is the boot code area. However, this may be broken down further. It may instead be offset 0-439 representing the boot code, with offset 440-444 (4 bytes) representing the disk signature and this is finally followed by 2 bytes of NULL bytes (0x00 0x00) starting at offset 444. As you see we still have 446 bytes. 

Let's verify this using "xxd":

First we tell "xxd" to start at offset 0, span a length of 439 bytes and group the results in groups of 4 bytes.

kali@securitynik:~/forensics$ xxd --seek 0 --len 439 --groupsize 4 linux_mint_usb.raw
00000000: fab80010 8ed0bc00 b0b80000 8ed88ec0  ................
00000010: fbbe007c bf0006b9 0002f3a4 ea210600  ...|.........!..
00000020: 00bebe07 3804750b 83c61081 fefe0775  ....8.u........u
00000030: f3eb16b4 02b001bb 007cb280 8a74018b  .........|...t..
00000040: 4c02cd13 ea007c00 00b0a103 00000000  L.....|.........
00000050: 00000000 00000000 00000000 00000000  ................
00000060: 00000000 00000000 00000000 00000000  ................
00000070: 00000000 00000000 00000000 00000000  ................
00000080: 00000000 00000000 00000000 00000000  ................
00000090: 00000000 00000000 00000000 00000000  ................
000000a0: 00000000 00000000 00000000 00000000  ................
000000b0: 00000000 00000000 00000000 00000000  ................
000000c0: 00000000 00000000 00000000 00000000  ................
000000d0: 00000000 00000000 00000000 00000000  ................
000000e0: 00000000 00000000 00000000 00000000  ................
000000f0: 00000000 00000000 00000000 00000000  ................
00000100: 00000000 00000000 00000000 00000000  ................
00000110: 00000000 00000000 00000000 00000000  ................
00000120: 00000000 00000000 00000000 00000000  ................
00000130: 00000000 00000000 00000000 00000000  ................
00000140: 00000000 00000000 00000000 00000000  ................
00000150: 00000000 00000000 00000000 00000000  ................
00000160: 00000000 00000000 00000000 00000000  ................
00000170: 00000000 00000000 00000000 00000000  ................
00000180: 00000000 00000000 00000000 00000000  ................
00000190: 00000000 00000000 00000000 00000000  ................
000001a0: 00000000 00000000 00000000 00000000  ................
000001b0: 00000000 000000 

After the 439 bytes, above I stated the next 4 bytes represents an optional disk signature. Let's verify this.

kali@securitynik:~/forensics$ xxd --seek 440 --len 4 --groupsize 4 linux_mint_usb.raw
000001b8: 43140500

If we look above at the "fdisk --list" output, we see the disk label is "0x00051443". This is basically the same value we see above from our last "xxd" output, with the exception that the bytes have been reversed, with the least significant byte being written first. This CPU is Little Endian byte order as shown below.

kali@securitynik:~/forensics$ lscpu | grep Endian
Byte Order:                      Little Endian

Let's now verify offset 444-446. Above we stated, these two bytes are usually null bytes (0x00 0x00)

kali@securitynik:~/forensics$ xxd --seek 444 --len 2 --groupsize 4 linux_mint_usb.raw
000001bc: 0000

Good stuff, we have verified the first 446 bytes of the MBR.

Following that 446 bytes is 64 bytes, which represents the partition table. Each partition is represented as 16 bytes. Hence 16*4 is where the 64.

Let's now look at 64 bytes, starting at offset 446. This will take us to offset 510.

kali@securitynik:~/forensics$ xxd --seek 446 --len 64 --groupsize 16 linux_mint_usb.raw
000001be: 002021000cfeffff000800000070a903  . !..........p..
000001ce: 00000000000000000000000000000000  ................
000001de: 00000000000000000000000000000000  ................
000001ee: 00000000000000000000000000000000  ................

Above, I started at offset 446, which represents where the boot code region ends, spanning 64 bytes while grouping the output in 16 bytes sequences. Each of those 16 bytes (row) represents a partition. At this point, we are able to confirm the disk has one partition, similar to what was presented to us via "fdisk --list".

Let's now peak into that partition. The partition table follows a standard layout, which is independent of the OS. 

From a Hexadecimal view, those 16 bytes of partition 1 are:

kali@securitynik:~/forensics$ xxd --seek 446 --len 16 --groupsize 1 linux_mint_usb.raw
000001be: 00 20 21 00 0c fe ff ff 00 08 00 00 00 70 a9 03  . !..........p..

Breaking those bytes down further
offset 446: 0x00 above indicates this partition should not be used for booting. Valid values are "0x80" can be used for booting and "0x00". 

offset 447: 0x20: "Starting Head"

offset: 448: 0x21: 6 of these bits (bits 0-5) are used for the "Starting Sector". Bits 6 and 7 are used by the "Starting Cylinder" field. Note and according to Microsoft "do not accurately represent the value of the fields, because the fields are either 6 bits or 10 bits and the data is recorded in bytes."

If we look at offset 448, from a bits (binary) perspective, we see:

kali@securitynik:~/forensics$ xxd --seek 448 --len 1 --groupsize 1 --bits linux_mint_usb.raw
000001c0: 00100001

Offset 449: 0x000c: 10 bits are used by the "Starting Cylinder" value. The maximum value across these 10 bits is 1023. Here is what the bits look like for this field. Note and according to Microsoft "do not accurately represent the value of the fields, because the fields are either 6 bits or 10 bits and the data is recorded in bytes."

kali@securitynik:~/forensics$ xxd --seek 449 --len 1 --groupsize 1 --bits linux_mint_usb.raw
000001c1: 00000000

Offset  450: 0x0c: defines the volume "System ID". if we look at the "fdisk --list" output again, we see "Id" has "c" and this is associated with "Type" W95 FAT32

kali@securitynik:~/forensics$ xxd --seek 450 --len 1 --groupsize 1 linux_mint_usb.raw
000001c2: 0c

Offset 451: 0xfe: "Ending Head"
kali@securitynik:~/forensics$ xxd --seek 451 --len 1 --groupsize 1 linux_mint_usb.raw
000001c3: fe

Offset 452: 0xff: "Ending Sector". Only bits 0-5 are used. Bits 6 and 7 are used by the "Ending Cylinder" field. Note and according to Microsoft "do not accurately represent the value of the fields, because the fields are either 6 bits or 10 bits and the data is recorded in bytes."

kali@securitynik:~/forensics$ xxd --seek 452 --len 1 --groupsize 1 linux_mint_usb.raw
000001c4: ff 

Offset 453: 0xff: Another 10 bits field and once again according to Microsoft "do not accurately represent the value of the fields, because the fields are either 6 bits or 10 bits and the data is recorded in bytes."

kali@securitynik:~/forensics$ xxd --seek 453 --len 1 --groupsize 1 linux_mint_usb.raw
000001c5: ff

Offset 454: 0x00080000: This double word (DWORD) or 32 bits, represent the number of "Relative Sectors" starting at the beginning of the disk to the beginning of the volume. 

kali@securitynik:~/forensics$ xxd --seek 454 --len 4 --groupsize 4 linux_mint_usb.raw
000001c6: 00080000 

If you remember, we mentioned previously, this value is in Little Endian. This means we need to reverse these bytes to find the real decimal values. Therefore bytes "0x00080000" now becomes "0x00000800" or simply "0x800" as we can remove the leading 0s. This "0x800" when converted to decimal, represents the "2048" reported by "fdisk --list" as the "Boot Start"

Offset 458: 0x0070a903: The final 4 bytes of the partition represents the total number of sectors on this volume.

kali@securitynik:~/forensics$ xxd --seek 458 --len 4 --groupsize 4 linux_mint_usb.raw
000001ca: 0070a903

Similarly to what we did above, let's convert "0x0070a903" to become "0x03a97000" as we need to change the byte order.  When "0x03a97000" is converted to decimal, we get "61435904" which represents the "Sectors" as returned by "fdisk --list"

Finally, multiply "61435904" * 512 (number of sectors) and we get the number of bytes as reported by "fdisk --list" for this partition. My calculation produced "29.294921398‬‬" which equates to the "29.3G" as reported by "fdisk --list"

We will ignore the 3 other partitions which falls at 459 - 509 as they are empty. The final 2 bytes at offset 510-512 reparents the signature or the end of sector marker. This signature is also used to mark the end of an extended boot record (EBR) and the boot sector.

kali@securitynik:~/forensics$ xxd --seek 510 --len 2 --groupsize 2 linux_mint_usb.raw
000001fe: 55aa 

Now that we have a better understanding of the drive, let's now move to mounting it in the next post.

PS. I have to admit, the destination disk I was copying to ran out of disk space. I thought about redoing the post. However, I thought that would not add or take away from the analysis. However, I do believe by letting you know about my error, you will ensure that you don't fall for the same trap. As a result, always ensure your destination drive has at least 10% more disk space than your source drive.

References: