CTF Circle - Hack-A-Sat 2020 CTF
"Good Plan? Great Plan!" Challenge Writeup
Written by sen (@sarahemm)

###############
### Summary ###

The 'Good Plan? Great Plan!' challenge listed a server and port number only,
with no other files or resources. Upon connecting to the port, it listed a
location on Earth to photograph, a location where data needed to be
downlinked, TLEs for the satellite you would be using, and the command set
you had to use to accomplish the mission. The goal was to build a mission
plan to capture 120MB worth of imaging data over the target and downlink that
information to the ground station within 48 hours, without exceeding any
operational parameters (such as running the battery dead or exceeding a 60C
temperature on any component).


############################
### Tools/Infrastructure ###

Our team has a couple servers we use as a launching point for CTF work,
so my work was done on these. No other tooling was needed other than
Ruby, which is the primary language I prefer for challenges like this.
Python with the 'pyephem' package would have worked just as well however.


###############
### Preface ###

To start off, a few satellite concepts which will be important later. The
orbit of our satellite is provided as a Two Line Element Set, a standard
format for specifying all of the parameters that define the orbit a satellite
is in, and where on that orbit it was at a given point in time. We'll need
this later to be able to figure out where the satellite is at each point in
time so that we'll know what action to be performing.

The other important detail for this challenge are reaction wheels. These are
spinning wheels found on some satellites, the changes in speed of which cause
the attitude of the satellite to change in the opposite direction. This allows
a computer on-board the satellite to keep it pointed at a specific point by
making small changes to the speed of 3 separate reaction wheels (one in each
axis). Over time the wheels may have to spin faster and faster to counteract
external forces, and eventually the wheel will be spinning as fast as it can
(it will have reached "saturation"). At this point, attitude control would be
lost as it can no longer apply any further force in one direction of rotation,
it can only slow down. Either a thruster or some other method would need to
be used to transfer the stored momentum out of the wheels to some other place.

In this simulation, magnetorquers are used for this (though the specifics
don't end up mattering). Magnetorquers (or "torque rods") are essentially
just electromagnets that allow a satellite to interface with earth's magnetic
field to stabilize itself. In this example, when the reaction wheels reach
saturation the magnetorquer would be used to transfer some of the stored
momentum to earth through the magnetic fields, thus allowing the wheels to slow
back down without the satellite losing attitude control.


###########################
### Phase 1 - Discovery ###

To start off, I connected to the provided IP/port with netcat to see what the
challenge was in the first place. It explained about needing to photograph
the Iranian space port, then downlink the data to Fairbanks within 48 hours.
To input the mission, you needed to submit a list of times and commands to
run at those specific times, with a resolution of one minute. There were
4 commands listed:
- sun_point: Point solar panels at the sun to charge the batteries.
- imaging: Collect imaging data (photos/video/etc.)
- data_downlink: Transmit data stored onboard to the ground station.
- wheel_desaturate: Desaturate the reaction wheels using the magnetorquers.

I assumed based on the description of the challenge that there weren't any
secret commands that needed discovering, and that we could complete the
challenge using only these four.

The easiest way to complete the challenge seemed to be to spend one day of
orbits imaging, then one day of orbits downlinking, assuming enough data
could be collected and offloaded in one orbit each to complete the challenge.
I didn't really expect this to be successful especially given the provided
commands to sunpoint and desaturate, but figured it was a good starting point
and would give more details about what the plans actually needed to take into
account.

Entering this simple plan, I asked the satellite to execute it. Immediately
it errored out saying that the target site was not visible, so imaging was
impossible. This told me that I could only switch to imaging mode while over
the target site, and likely the same would apply to downlinking data to the
ground station. Time for a more detailed plan.


########################
### Phase 2 - Orbits ###

The information given upon connection included TLEs for the satellite you
were to be controlling. Given this and the latitude/longitude of the site
to be imaged as well as the ground station, I knew I could then figure out
what time the satellite will be over the two sites. If this weren't for a
CTF, I'd likely be looking for information about what satellites were visible
at the current time at my current location, but since this is a fictional
situation I needed something a little more flexible than my normal tracking
application.

A quick search found the 'orbit' ruby gem
(https://github.com/jeffmcfadden/orbit) which seemed like it would do exactly
what I need. With it installed, I threw together a tiny program
(https://github.com/sarahemm/ctf-tools/blob/master/2020-hackasat
/good_plan-great_plan/orbit-calc.rb) that would use the TLE plus locations
of the two sites to tell me when the satellite was visible from either site.
What "visible" means can vary, since depending on the terrain around the site,
the type of images required, antennas in use, etc. it may require a different
elevation above the horizon to actually be functional, but I figured I'd start
with "above zero" and go from there.

I took some of the values out of the orbit calculator I'd thrown together
and came up with the beginnings of a mission plan. Pasting that into the
console of the challenge, it still complained that the site wasn't in view
when I tried to switch to imaging. I bumped the time up little by little
until it was accepted, which came to around 6 degrees elevation above the
horizon. I updated the calculator to only report passes once they were above
that elevation so that I wouldn't have to worry about that part anymore. The
orbit calculator tool then output this type of information:
- At 2020-04-22 01:13:00 UTC, sat is at 8.36 deg to the downlink antenna
- At 2020-04-22 01:14:00 UTC, sat is at 14.28 deg to the downlink antenna
- At 2020-04-22 01:15:00 UTC, sat is at 17.90 deg to the downlink antenna
- At 2020-04-22 01:16:00 UTC, sat is at 15.31 deg to the downlink antenna
- At 2020-04-22 01:17:00 UTC, sat is at 10.16 deg to the downlink antenna


###########################
### Phase 2.5 - Tooling ###

Manually reading YAML telemetry was getting old, and the mission plan was
getting long and complicated enough that copy/pasting it into the challenge
server for each attempt wasn't ideal. I put a little Ruby app together which
handled submitting the token, storing and sending the plan, and converting
the telemetry coming back into more of a human readable format
(https://github.com/sarahemm/ctf-tools/blob/master/2020-hackasat
/good_plan-great_plan/mission.rb).

I built the first part of the mission plan, which involved charging batteries
until the target was within range, collecting imaging data until it almost
went out of view, switching back to charging until within radio reach of the
ground station, and downlinking the data. Running this errored out when the
camera overheated, so I reduced the amount of imaging by a couple minutes to
keep it within temperature range, then re-ran. Success, ish! Didn't collect
enough data in one pass, but at least it got some data and sent it to the
ground station.


#########################
### Phase 3 - Solving ###

At this point I had the basics down, and the rest was just tweaking the plan
stored in mission.rb until I had it solved. I took the next passes that
orbit-calc.rb gave me and added another imaging/downlink pass, but upon
executing the plan I realized it was night time, so the camera on the
satellite wasn't going to be much use! I had to skip that pass, and instead
capture more imaging data on the next pass once the sun came back up.

The last thing to figure out was that the reaction wheels reached saturation
mid-morning on the second day, which would have resulted in the satellite
losing attitude control (or in our simulation, an error message). I added
a mission step to desaturate the wheels using the onboard magnetorquers
before and after the second imaging/downlink passes, which kept them safely
in the operational RPM range. I re-ran the mission and after 48 simulated
hours, the flag came out! Accompanied by some ASCII art of a satellite this
time.


##################################
### Lessons Learned/Reinforced ###

- The "light automation" approach I try to take during CTFs worked well here.
  I could have built the orbit calculator into the mission app, and I could
  have had the mission app learn how fast the battery ran down, how fast the
  camera heated up, etc. to automate the whole thing. When time is at a
  premium during a CTF however, I find it quicker to automate a few things
  then manually take care of the rest to get the solve. Often building a
  couple independent small tools and moving data between them by hand can be
  faster to build and troubleshoot than building one large thing, at least
  for me.

- If you look at the mission.rb tool, it includes some code to display
  warnings ahead of things going out of range but it's only implemented
  for the reaction wheels. As I got into building this function I realized
  that, while it might be useful if this was a tool to be used frequently,
  it didn't make sense in the context of a CTF as I could just catch these
  situations by eye and resolve them that way. Implementing the warning
  functionality would have taken more time than it ever would have saved
  in the single use this tool would ever be used for. I tend to implement
  too many features that aren't really useful enough to justify in a CTF
  situation, and I need to try to do better thinking about each feature
  I'm implementing and whether it's really needed in that context.