xenia 0141d1190b | ||
---|---|---|
.. | ||
README.md |
README.md
SpaceDB
Category: Payload Modules
Points (final): 79 points
Solves: 53
The last over-the-space update seems to have broken the housekeeping on our satellite. Our satellite's battery is low and is running out of battery fast. We have a short flyover window to transmit a patch or it'll be lost forever. The battery level is critical enough that even the task scheduling server has shutdown. Thankfully can be fixed without without any exploit knowledge by using the built in APIs provied[sic] by kubOS. Hopefully we can save this one!
Note: When you're done planning, go to low power mode to wait for the next transmission window
Write-up
by Cameron and haskal
Upon connecting to the provided TCP service via netcat, we see that it spawns a telemetry service accessible via HTTP based off the following console output:
critical-tel-check info: Detected new telemetry values.
critical-tel-check info: Checking recently inserted telemetry values.
critical-tel-check info: Checking gps subsystem
critical-tel-check info: gps subsystem: OK
critical-tel-check info: reaction_wheel telemetry check.
critical-tel-check info: reaction_wheel subsystem: OK.
critical-tel-check info: eps telemetry check.
critical-tel-check warn: VIDIODE battery voltage too low.
critical-tel-check warn: Solar panel voltage low
critical-tel-check warn: System CRITICAL.
critical-tel-check info: Position: GROUNDPOINT
critical-tel-check warn: Debug telemetry database running at: 3.19.141.137:19369/tel/graphiql
Connecting to the provided endpoint yields a graphiql graphql console from which we can run queries against the telemetry database. Using the following query, we can dump the database schema to get an idea of the capabilities of the telemetry interface:
fragment FullType on __Type {
kind
name
description
fields(includeDeprecated: true) {
name
description
args {
...InputValue
}
type {
...TypeRef
}
isDeprecated
deprecationReason
}
inputFields {
...InputValue
}
interfaces {
...TypeRef
}
enumValues(includeDeprecated: true) {
name
description
isDeprecated
deprecationReason
}
possibleTypes {
...TypeRef
}
}
fragment InputValue on __InputValue {
name
description
type {
...TypeRef
}
defaultValue
}
fragment TypeRef on __Type {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
}
}
}
}
}
}
}
}
query IntrospectionQuery {
__schema {
queryType {
name
}
mutationType {
name
}
types {
...FullType
}
directives {
name
description
locations
args {
...InputValue
}
}
}
}
Dumping the schema reveals two things of interest:
- We may query
telemetry
- We may mutate
telemetry
viadelete
andinsertBulk
From the schema, we see that telemetry data has the following shape:
{
"name": "telemetry",
"description": "Telemetry entries in database",
"args": [
{
"name": "timestampGe",
"description": null,
"type": {
"kind": "SCALAR",
"name": "Float",
"ofType": null
},
"defaultValue": null
},
{
"name": "timestampLe",
"description": null,
"type": {
"kind": "SCALAR",
"name": "Float",
"ofType": null
},
"defaultValue": null
},
{
"name": "subsystem",
"description": null,
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "parameter",
"description": null,
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "parameters",
"description": null,
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
},
"defaultValue": null
},
{
"name": "limit",
"description": null,
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "Entry",
"ofType": null
}
}
}
},
"isDeprecated": false,
"deprecationReason": null
}
Thus, we can dump the telemetry information via the following command:
query {
telemetry{timestamp, subsystem, parameter, value}
}
From the console output, we can see that the issue plaguing the system is a low VIDIODE
voltage alarm. Thus, in order to fix the alarm, we must spoof the proper VIDIODE
voltage and trigger a reset of the alarm system. In order to do this, we run the following mutation:
mutation spoof {
delete(timestampGe: 1590232427.582683){success, errors}
insertBulk(timestamp: 1590232427.582683, entries: [
{subsystem: "eps", parameter: "VIDIODE", value: "8.0"},
{subsystem: "eps", parameter: "RESETS_MANUAL", value: "1.0"},
{subsystem: "eps", parameter: "RESETS_BROWNOUT", value: "1.0"},
{subsystem: "eps", parameter: "RESETS_AUTO_SOFTWARE", value: "1.0"},
{subsystem: "eps", parameter: "BATTERY_1_RESETS_MANUAL", value: "1.0"},
{subsystem: "eps", parameter: "BATTERY_1_RESETS_BROWNOUT", value: "1.0"},
{subsystem: "eps", parameter: "BATTERY_1_RESETS_AUTO_SOFTWARE", value: "1.0"},
{subsystem: "eps", parameter: "BATTERY_0_RESETS_MANUAL", value: "1.0"},
{subsystem: "eps", parameter: "BATTERY_0_RESETS_BROWNOUT", value: "1.0"},
{subsystem: "eps", parameter: "BATTERY_0_RESETS_AUTO_SOFTWARE", value: "1.0"},
]){success, errors}
}
The correct value for VIDIODE
was determined through trial and error. The reset flags were asserted because we didn't know which one we needed, so we just triggered all of them. Were this to be a real satellite, I'm sure nothing bad could possibly happen... We had to run the delete mutation on the most recent telemetry item in order to avoid triggering an alarm for duplicate telemetry data.
After successfully spoofing the telemetry data, we notice in the console output from our session that the scheduler has been activated:
critical-tel-check info: Scheduler service comms started successfully at: 3.19.61.44:14764/sch
/graphiql
We visit the provided URL and find another graphiql interface. According to the KubOS documentation, we may issue the following query to enter safe mode and stop any subsequent checks which might kill the scheduler and bring us back to where we started:
mutation safe {
safeMode{success, errors}
}
We also see that we can dump the task lists for all available modes with the following query:
query dump {
availableModes{name, path, lastRevised, schedule{tasks{description, delay, time, period,
app{name, args, config}}, path, filename, timeImported}, active},
activeMode{name}
}
From the information from the dumped task lists, we can see that there are several tasks we may run. First and foremost, we need to fix our power situation by orienting the solar panels towards the sun. We may do this by running the following mutation:
mutation patch {
createMode(name: "patch"){success, errors}
importRawTaskList(name: "patch", mode: "patch", json: "{\"tasks\":[{\"description\":\"Orien
t solar panels at sun.\",\"delay\":\"0s\",\"time\":null,\"period\":null,\"app\":{\"name\":\"sun
point\",\"args\":null,\"config\":null}},{\"description\":\"Update system telemetry\",\"delay\":
\"1s\",\"time\":null,\"period\":null,\"app\":{\"name\":\"update_tel\",\"args\":null,\"config\":
null}}]}"){success, errors}
activateMode(name: "patch"){success, errors}
}
Subsequently, we need to create a mode which will aim the transmission antenna at the ground, activate the antenna, print the flag to the log, transmit the comms buffer, power down the antenna, and reorient the solar panels. We may do this via the following mutation:
mutation {
importRawTaskList(json:"{\"tasks\":[{\"description\":\"Orient antenna to ground.\",\"delay\
":null,\"time\":\"2020-05-23 16:40:49\",\"period\":null,\"app\":{\"name\":\"groundpoint\",\"arg
s\":null,\"config\":null}},{\"description\":\"Power-up downlink antenna.\",\"delay\":null,\"tim
e\":\"2020-05-23 16:41:09\",\"period\":null,\"app\":{\"name\":\"enable_downlink\",\"args\":null
,\"config\":null}},{\"description\":\"Prints flag to log\",\"delay\":null,\"time\":\"2020-05-23
16:41:19\",\"period\":null,\"app\":{\"name\":\"request_flag_telemetry\",\"args\":null,\"config
\":null}},{\"description\":\"Power-down downlink antenna.\",\"delay\":null,\"time\":\"2020-05-2
3 16:41:34\",\"period\":null,\"app\":{\"name\":\"disable_downlink\",\"args\":null,\"config\":nu
ll}},{\"description\":\"Orient solar panels at sun.\",\"delay\":null,\"time\":\"2020-05-23 16:4
1:39\",\"period\":null,\"app\":{\"name\":\"sunpoint\",\"args\":null,\"config\":null}}]}",name:"
nominal-op",mode:"transmission"){success,errors}
}
Finally, per the challenge directive, we must have the satellite enter low-power mode:
mutation low_power {
activateMode(name: "low_power"){success, errors}
}