## Netscreen of the Dead #### Developing a Trojaned Firmware for Juniper Netscreen Appliances ----- # Cast ##### Graeme Neilson Security Consultant Aura Software Security graeme@aurasoftwaresecurity.co.nz ----- # Trailer ##### • What if a core network security device was compromised? – an attacker has exploited a vulnerability – malicious appliance supplier – malicious third party support – malicious employee • This is a POST EXPLOIT, SERIAL CONSOLE or MITM attack. • Goal is hidden root control of the appliance. – Discuss reversing and modifying the firmware code ----- # Opening Scene ##### Netscreens are manufactured by Juniper Inc • All in one Firewall, VPN, Router security appliance. • SME to Datacentre scale (NS5XP – NS5000). • Common Criteria and FIPS certified. • Run a closed source, real time OS called ScreenOS. • ScreenOS is supplied as a binary firmware 'blob'. NS5XT Model: • PowerPC 405 GP RISC processor 64MB Flash • Serial console, Telnet, SSH, HTTP/HTTPS admin interfaces ----- # Attack ##### Attacking firmware - two vectors of attack: • Live evisceration: debugging with remote GDB debugger over serial line • Feeding on the remains: dead listing / static binary analysis using disassembler and hex editor PowerPC architecture • fixed instruction size of 4 bytes • flat memory model • 32 GP registers, no explicit stack, link register ----- # Live Evisceration ##### • Embedded Linux Development Kit has GDB compiled for PowerPC 405 processor • No source so create custom .gdbinit for PPC registers and 'stack' to provide 'SoftICE' like context on breaks. • Network connection to the Netscreen and run: set gdb enable • Connect remote gdb via serial console ----- ##### • Worked: – Memory dumps – Query memory addresses • Didn't work: – Breakpoints – Single stepping ----- # Feeding on the Remains /--------------------------\ - ###### Compared many different versions of ScreenOS firmware. | HEADER | - ###### Revealed a 4 section structure |--------------------------| | | - ###### Header: |--------------------------| **sig** **sysinfo** | STUB | **00000000: EE16BA81** **00110A12 00000020 02860000** |--------------------------| **00000010: 004E6016 15100050 29808000 C72C15F7** | | **size** **checksum** |--------------------------| | UNKNOWN | size = compressed image size – 79 bytes |--------------------------| sysinfo = 00, platform, cpu, version | | |--------------------------| - ###### Stub contains strings relating to LZMA compression | COMPRESSED BINARY | algorithm. | UPDATE BLOB [BUB] | ----- # Bub ##### • The header of the Bub appears to be a customised LZMA header. • Comparative analysis again of different Bub headers. • The standard LZMA header has 3 fields: options, dictionary_size, uncompressed_size • 'Bub' header has 3 fields: signature bytes, options, dictionary_size ###### 00012BF0: 00000000 00000000 00000000 00000000 00012C00: 01440598 5D002000 00007705 92C63DFC ----- # Bub Can Change . ###### Uncompress Bub - ###### Cut out the Bub from firmware file. - ###### Insert an uncompressed_size field of value -1 == unknown size - ###### Modify the dictionary_size from 0x00200000 to 0x00008000 - ###### Then we can decompress the Bub using freely available LZMA utilities Compress Bub - ###### Compress the binary with standard LZMA utilities. - ###### Modify the dictionary_size field from 0x00002000 to 0x00200000. - ###### Delete the uncompressed_size field of 8 bytes. - ###### Insert new Bub into firmware file replacing original compressed blob. ----- # Night of the Living Netscreen - ##### Cut out the compressed Bub section of the image. - ##### Uncompress the Bub. - ##### Modify the resulting binary to add or change code and / or data. - ##### Re-compress the modified binary into a new Bub. - ##### Prepend the original firmware header to the modified Bub. - ##### Upload the modified firmware over serial = SUCCESS. - ----- # Autopsy ##### • Uncompressed Bub is ~20Mb ScreenOS binary with a header. • Want to load into IDA but need a loading address so that references within the program point to the correct locations. • From header: program_entry = address – offset ###### signature offset address 00000000: EE16BA81 00010110 00000020 00060000 00000010: 01440578 00000000 00000000 F8A2FA6F ##### • Confirm with live debugging ----- # Autopsy ii /--------------------------\ - ##### Use IDA scripts to find function prologs | HEADER | ##### (0x9421F*) and mark as code. |--------------------------| - | SCREENOS CODE | Mark strings in data section for cross |--------------------------| ##### references. | SCREENOS DATA | - ##### Use error strings to identify functions and |--------------------------| | BOOT LOADER CODE | rename. |--------------------------| - ##### Search for str_cmp, file_read, file_write, | BOOT LOADER DATA | login etc. |--------------------------| - ##### Build up a picture of the binary structure | 0xFFs | |--------------------------| and functions. | other stuff! | - ##### Need to cut out boot loader and \--------------------------/ ##### disassemble separately with loading ----- # Netscreen of the Dead ##### • ScreenOS Trojaned Firmware required functionality: – Install/Upgrade: Load trojan firmware via serial, tftp and web – Maintain Access: Include a back door login mechanism – Payload: Execute arbitrary code injected into the image ----- # First Bite ##### Install / Upgrade • Checksum and size in header are checked when images loaded over the network via TFTP or Web ###### 00000000: EE16BA81 00110A12 00000020 02860000 00000010: 004E6016 15100050 29808000 C72C15F7 checksum ##### • Checksum is calculated, could reverse the algorithm... but on loading any bad checksum value is printed to the console. • If we modify the firmware to print out the correct checksum value we would have a 'checksum calculator' firmware which we load modified firmware against. ----- # First Bite ii ###### 008B60E4 lwz %r4, 0x1C(%r31) # %r4 contains header checksum 008B60E8 cmpw %r3, %r4 # %r3 contains calculated checksum 008B60EC beq loc_8B6110 # branch away if checksums matched #008B60EC mr %r4,%r3 # print out calculated checksum 008B60F0 lis %r3, aCksumXSizeD@h # " cksum :%x size :%d\n" 008B60F4 addi %r3, %r3, aCksumXSizeD@l 008B60F8 lwz %r5, 0x10(%r31) 008B60FC bl Print_to_Console # %r4 is printed to console 008B6100 lis %r3, aIncorrectFirmw@h # "Incorrect firmware data, 008B6104 addi %r3, %r3, aIncorrectFirmw@l ----- # One Bit{e} ##### Maintain Access • Console, Telnet, Web and SSH all compare password hashes and use the same function. • SSH falls back to password if client does not supply a key unless password authentication has been disabled. • One bit patch provides login with any password if a valid username is supplied. ----- # One Bit{e} ii ###### 003F7F04 mr %r4, %r27 003F7F08 mr %r5, %r30 003F7F0C bl COMPARE_HASHES # does a string compare 003F7F10 cmpwi %r3, 0 # equal if match #0x397F30 cmpwi %r3, 1 # equal if they don't match 003F7F14 bne loc_3F7F24 # login fails if not equal (branch) 003F7F18 li %r0, 2 003F7F1C stw %r0, 0(%r29) ----- # Infection ##### Injecting code into the binary • ScreenOS code section contains a block of nulls • Proof of concept code injected into nulls Proof of Concept Code :: motd • Patch a branch in ScreenOS to call our code • Call ScreenOS functions from our code • Create new code and functionality ----- # Infection ii ###### stwu %sp, -0x20(%sp) mflr %r0 lis %r3, string_msb_address addi %r3, %r3, string_lsb_address bl Print_To_Console mtlr %r0 addi %sp, 0x20 bl callee_function ----- # Zombie Loader ##### • All Juniper ScreenOS images signed. • Administrator can load a Juniper certificate to validate firmware • Certificate NOT installed by default. • Administrator can delete this certificate. • Check is done in the BOOT LOADER which we can modify to authenticate all images or only non-Juniper images ----- # Zombie Loader ii ###### 0000D68C bl sub_98B8 0000D690 cmpwi %r3, 0 # %r3 has result of image validation 0000D694 beq loc_D6B0 # branch if passed #0000D694 b loc_D6B0 # always branch, all images authenticated #0000D694 bne loc_D6B0 # ...or only bogus images authenticated 0000D698 lis %r3, aBogusImageNotA@h # Bogus image not authenticated" 0000D69C addi %r3, %r3, aBogusImageNotA@l 0000D6A0 crclr 4*cr1+eq 0000D6A4 bl sub_C8D0 0000D6A8 li %r31, -1 0000D6AC b loc_D6E0 ----- # Demo: ScreamOS ----- # 28 Hacks Later ##### • Hidden shadow configuration file – allowing all traffic from one IP through Netscreen – network traffic tap • Persistent infection via boot loader on ScreenOS upgrade. Patch boot loader and login mechanism. • Javascript code injection in web console... ----- # Victim ##### 04-07-08: Sent white-paper and firmware to Juniper recommending: • Install firmware authentication certificate at factory • Prevent certificate deletion • Encrypt firmware rather than using LZMA compression Juniper: 13-09-08: “This is expected” 28-10-08: “I saw you are presenting at RUXCON on Nov 30th. Cool.” 24-11-08: Publish JTAC Bulletin PSN-2008-11-111 “ScreenOS Firmware Image Authenticity Notification” ----- # Victim ii ##### “All Juniper ScreenOS Firewall Platforms are susceptible to circumstances in which a maliciously modified ScreenOS image can be installed.” Juniper recommend: – Install the imagekey.cer certificate – Utilize the “Manager-IP” feature to control which hosts (via their IP addresses) can manage your firewall. – Change the TCP port by which the device listens for ----- # Remove the Brain ##### • Install known firmware before deployment (Who is your Juniper vendor?) • Admin via SSH key authentication only (disable Telnet, HTTP and HTTPS) • Out of band management network • Limit number of administrators. • Strong passwords. ----- # Roll the Credits #### Andy and Mark @ Aura Software Security George Romero Simon Pegg ----- # Script by ScreenOS Dev ### BOB: “Code should never reach here by design” -----