Building

Reading the BlueCoat FileSystem

You may remember our last post regarding the SGOS system and the proprietary file system. Since then, we got access to a newer version of the system (6.6.4.2). Still not the most current one (which seems to be 6.7.1.1) nor of the 6.6.x branch (which seems to be 6.6.5.1) though. As this system version also used the same proprietary filesystem (although it initially booted from a FAT32 partition), I decided to take a deeper look into this.

By comparing the different images and with the information of previous researchers [1] I was able to reconstruct (a part of) the filesystem and develop a fuse based driver.

As already described in the last post, the filesystem primarily is built upon multiple headers with two magic fields:

struct cp_xx_entry {
    union {
        char c[4];
        u_int32_t i;
    } magic1;

    int32_t unknown;
    u_int16_t size;
    u_int16_t unknown2;

    union {
        char c[4];
        u_int32_t i;
    } magic2;
};

The current magic values I identified during the analysis are:

Magic 1 Magic 2 Possible Name
_CP_ _HP_ Partition
_CP_ _CZK SubPartition
_CP_ _CE_ Element
_CP_ _VE_ Config
_CP_ _IE_ File
_CP_ BCWZ Boot
_CP_ RHDP PartitionTable
_CP_ YEDP PartitionTableInfo
_CP_ EEDP PartitionTableEntry

For a complete list of the specific items, please take a look at the bcfs.c file.

Whilst the old version of the firmware is using this filesystem over the complete disk, the new version consists of a FAT32 filesystem with the following file structure:

sgos
└── boot
    ├── cmpnts
    │   ├── boot.exe
    │   └── starter.si
    ├── meta.txt
    └── systems
        └── system1

The files starter.si and system1 are actual images containing the proprietary filesystem, starting with a Partition-Header.

This is how this header looks like (first as normal hexdump, then decoded in radare2):

00000000  5f 43 50 5f 88 00 00 00  00 0c 00 00 5f 48 50 5f  |_CP_........_HP_|
00000010  e3 f1 33 99 74 66 1f e3  df 7c 0c f4 1b a8 9a 0b  |..3.tf...|......|
00000020  66 ba 85 a7 b8 05 8f ef  0d 36 07 cb 00 00 00 00  |f........6......|
00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000050  00 00 00 00 00 00 00 00  0d 48 ba bb 9e 24 2a fd  |.........H...$*.|
00000060  6f 4d dc 19 1c 0c c6 3f  fd 40 0d 33 00 00 00 00  |oM.....?.@.3....|
[0x00000000]> pf.PartitionHeader [4]cdww[4]cdd[3048]b magic1 hdr_unknown size hdr_unknown2 magic2 header_checksum data_checksum unknown
[0x00000000]> pf.PartitionHeader
         magic1 : 0x00000000 = [ '_', 'C', 'P', '_' ]
    hdr_unknown : 0x00000004 = 136
           size : 0x00000008 = 0x0c00
   hdr_unknown2 : 0x0000000a = 0x0000
         magic2 : 0x0000000c = [ '_', 'H', 'P', '_' ]
header_checksum : 0x00000010 = 2570318307
  data_checksum : 0x00000014 = 3810485876
        unknown : 0x00000018 = [ 0xdf, 0x7c, 0x0c, 0xf4, 0x1b,... ]

This header is immediately followed by a SubPartition-Header. This header has the following format:

struct cp_ce_entry {
    struct cp_xx_entry hdr;
    int32_t num_elements;
    int32_t unknown2;
    u_int32_t offset;
    int32_t unknown3[9];
};

struct cp_czk_entry {
    struct cp_xx_entry hdr;
    char unknown[0xc0];
    struct cp_ce_entry entries[16];
    char unknown2[0x34];
    /* signature */
    char pkcs7[0x706+0x27f5];
};

Besides some still unknown fields, it consists of a list of Element entries and a signature. This signature seems to be used as a secure boot mechanism and is verified by the loader of the particular filesystem.

The element entries are specifying the location of different parts of the filesystem (as describted by Raphaël Rigo [1]). The offsets are based on the start of the “partition”.

The first entry points to a global string table which contains all strings used in the filesystem (filenames, informations?). This table consists of length/offset pairs as shown below:

00004000  00 00 00 00 00 00 00 00  a0 27 00 00 00 00 00 00  |.........'......|
00004010  04 00 00 00 00 00 00 00  a8 27 00 00 00 00 00 00  |.........'......|
00004020  08 00 00 00 00 00 00 00  b0 27 00 00 00 00 00 00  |.........'......|
00004030  6d 00 00 00 00 00 00 00  c0 27 00 00 00 00 00 00  |m........'......|
00004040  14 00 00 00 00 00 00 00  30 28 00 00 00 00 00 00  |........0(......|
00004050  86 00 00 00 00 00 00 00  48 28 00 00 00 00 00 00  |........H(......|
00004060  0a 00 00 00 00 00 00 00  d0 28 00 00 00 00 00 00  |.........(......|
[0x00004000]> pf 7qq size offset
0x00004000 [0] {
    size : 0x00004000 = (qword)0x0000000000000000
  offset : 0x00004008 = (qword)0x00000000000027a0
}
0x00004010 [1] {
    size : 0x00004010 = (qword)0x0000000000000004
  offset : 0x00004018 = (qword)0x00000000000027a8
}
0x00004020 [2] {
    size : 0x00004020 = (qword)0x0000000000000008
  offset : 0x00004028 = (qword)0x00000000000027b0
}
0x00004030 [3] {
    size : 0x00004030 = (qword)0x000000000000006d
  offset : 0x00004038 = (qword)0x00000000000027c0
}
0x00004040 [4] {
    size : 0x00004040 = (qword)0x0000000000000014
  offset : 0x00004048 = (qword)0x0000000000002830
}
0x00004050 [5] {
    size : 0x00004050 = (qword)0x0000000000000086
  offset : 0x00004058 = (qword)0x0000000000002848
}
0x00004060 [6] {
    size : 0x00004060 = (qword)0x000000000000000a
  offset : 0x00004068 = (qword)0x00000000000028d0
}

The offsets are this time based on the start of the table instead of the partition (0x4000 +0x27a0 = 0x67a0):

000067a0  00 00 00 00 00 00 00 00  53 47 4f 53 00 00 00 00  |........SGOS....|
000067b0  53 63 6f 72 70 69 75 73  00 00 00 00 00 00 00 00  |Scorpius........|
000067c0  2f 57 6f 72 6b 73 70 61  63 65 73 2f 6a 65 6e 6b  |/Workspaces/jenk|
000067d0  69 6e 73 2f 77 6f 72 6b  73 70 61 63 65 2f 53 47  |ins/workspace/SG|
000067e0  4f 53 36 5f 73 67 5f 36  5f 36 5f 78 78 33 2f 73  |OS6_sg_6_6_xx3/s|
000067f0  63 6f 72 70 69 75 73 2f  73 67 5f 36 5f 36 5f 78  |corpius/sg_6_6_x|
00006800  78 33 2f 62 6f 6f 74 63  68 61 69 6e 2f 78 38 36  |x3/bootchain/x86|

In the second section, the filesystem options are stored. For a detailed description, take a look at Rigo’s talk (slide 12) [1].

The third sections points to a list of files stored in this filesystem. Those files are represented by the File-Header:

struct cp_ie_entry {
    struct cp_xx_entry hdr;
    u_int64_t offset;
    u_int64_t size;
    u_int32_t path_idx;
    u_int32_t filename_idx;
    u_uint8_t unknown[256-44];
};

The offsets are (again) based on the start of the section. The index fields are refering to the previously described string table. For example for the first entry:

      magic1 : 0x00015000 = [ '_', 'C', 'P', '_' ]
 hdr_unknown : 0x00015004 = 0x00000070
        size : 0x00015008 = 0x0100
hdr_unknown2 : 0x0001500a = 0x0000
      magic2 : 0x0001500c = [ '_', 'I', 'E', '_' ]
      offset : 0x00015010 = (qword)0x0000000000014000
    filesize : 0x00015018 = (qword)0x0000000000007e1c
     path_id : 0x00015020 = 0x00000030
 filename_id : 0x00015024 = 0x00000000

Filename @ index 0x0:

(emptystring)

Path @ index 0x30:

/Workspaces/jenkins/workspace/SGOS6_sg_6_6_xx3/scorpius/sg_6_6_xx3/bootchain/x86/release/x86_64_prekernel.exe

With this information, it was actually possible to write a fuse filesystem driver to mount those images:

$ ./bcfs_fuse --imagefile=./sgos/boot/cmpnts/starter.si /tmp/starter.si
Mounting imagefile ./sgos/boot/cmpnts/starter.si
$ tree /tmp/starter.si
/tmp/starter.si
├── main.cfg
├── var
│   └── lib
│       └── jenkins
│           └── workspace
│               └── SGOS6_scorpius_main
│                   └── scorpius
│                       └── toolchain
│                           └── linux
│                               └── x86_64_host
│                                   └── gcc-cross
│                                       └── x86_target
│                                           └── v4.4.2
│                                               └── i386-bcsi-sgos
│                                                   └── lib
│                                                       ├── libgcc_s_sgos.so
│                                                       └── libstdc++_sgos.so
└── Workspaces
    └── jenkins
        └── workspace
            └── SGOS6_scorpius_main
                └── scorpius
                    └── main
                        └── bin
                            └── x86
                                └── sgos_native
                                    └── release
                                        └── gcc_v4.4.2
                                            └── stripped
                                                ├── boot
                                                │   └── kernel.exe
                                                ├── console_rdr.exe
                                                ├── libbooting_crypto.so
                                                ├── libchar_output.so
                                                ├── libc.so
                                                ├── libgcc_support.so
                                                ├── libknl_api.so
                                                ├── libmemory.so
                                                ├── libm.so
                                                ├── libosd_api.so
                                                ├── osd.exe
                                                ├── sequencer.exe
                                                ├── starter.exe
                                                ├── storage
                                                │   ├── aic79xx.exe
                                                │   ├── ata.exe
                                                │   ├── libadmin.exe.so
                                                │   ├── mpt.exe
                                                │   ├── scsi.exe
                                                │   └── vioblk.exe
                                                └── sysimg_partition.exe

As told before, the filesystem is protected by a secure boot mechanism. This means that the fuse driver is currently read only. Nevertheless, the 5.x version of the firmware does not use a signature. But based on the work of Rigo and my research, a HMAC is still used to secure the integrity. As long as the key for the HMAC is not available (which was not by simply analyzing the data structures) the stored data couldn’t be modified.

The source code of the (readonly) fuse driver is published together with this blogpost at github.

Best,

Timo

[1] https://www.blackhat.com/docs/eu-15/materials/eu-15-Rigo-A-Peek-Under-The-Blue-Coat.pdf