# SummaryThe '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/InfrastructureOur 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.
# PrefaceTo 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 - DiscoveryTo 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 - OrbitsThe 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 - ToolingManually 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 - SolvingAt 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. 