2021 Hack-A-Sat DEFCON Space Security Challenge CTF Qualifiers Writeup - Take out the Trash, pt. 1

Take out the Trash, pt. 1


"A cloud of space junk is in your constellation's orbital plane. Use the space lasers on your satellites to vaporize it! "

Take out the trash involved parsing two sets of orbital elements, one for satellites "under our control" with lasers to shoot trash, and a second, containing the trash to be shot. They were provided in the now-familiar Two Line Element format. We were also given two parameters that needed to be taken into consideration when shooting upon the given trash:

  • The lasers have a range of 100 km and must be provided range and attitude to lock onto the space junk.
  • Don't allow any space junk to approach closer than 10 km.

The first order of business for solving this challenge will be establishing where the two constellations are at any given moment. In the past, we've used both PyEphem and SkyField to load TLE sets and determine positions. Given the deprecation warnings on PyEphem, and not requiring more complicated orbital body calculations, this time around it look as if Skyfield would be an appropriate choice.

Loading the TLE sets is fairly straight forward:

from skyfield.api import load

sats = load.tle_file("sats.tle")
junk = load.tle_file("spacejunk.tle")

We can then naively iterate through the pieces of junk, searching for a satellite within range that can fire upon it. Given that we can't allow any junk to come within 10km of any of our satellites, it makes sense to lineraly search forward through time for when trash first comes close.

ts = load.timescale()  # Loads skyfield's time model


def check_for_firing_range(junk, t):
    current_pos = junk.at(t)
    for sat in sats:
        alt, az, dist = (sat.at(t) - current_pos).radec()
        dist = dist.km
        if dist < 100:
            print("{year}{day_of_year}.{hour:02d}{minute:02d}{second:02d} {satelite} FIRE {qx} {qy} {qz} {qw} {range}".format(
                year=t.utc.year,
                day_of_year=176, # Days since Jan - we can edit this should we take longer to fire upon the junk.
                hour=t.utc.hour,
                minute=t.utc.minute,
                second=floor(t.utc.second),
                satelite=sat.name.upper(), 
                qx=0, # TODO
                qy=0, # TODO
                qz=0, # TODO
                qw=0, # TODO
                range=dist
                )
                )
            
            return True
    return False

def find_time_where_sat_in_range(junk):
    for hr in range(24):
        for min in range(0,60,5):  # May need to tune this should junk come too close, or a satellite becomes too busy firing on junk
            t = ts.utc(2021, 6, 26, hr, min, 0)
            if check_for_firing_range(j, t):
                return # if we found a firing solution, dont keep looping.

for j in junk:
    find_time_where_sat_in_range(j)

And we're rewarded with a list of potential firing options:

python solution.py | head
2021176.002500 SAT1 FIRE 0 0 0 0 49.210536263656124
2021176.003000 SAT1 FIRE 0 0 0 0 81.69819776889929
2021176.020500 SAT2 FIRE 0 0 0 0 89.18592559600607
2021176.012500 SAT1 FIRE 0 0 0 0 97.28314628913203
2021176.020000 SAT2 FIRE 0 0 0 0 82.94699268947561
2021176.012000 SAT1 FIRE 0 0 0 0 96.64724903274347
2021176.002500 SAT1 FIRE 0 0 0 0 52.43018384155453
2021176.002500 SAT1 FIRE 0 0 0 0 53.27933948562728
2021176.002500 SAT1 FIRE 0 0 0 0 84.68927310689409
2021176.002500 SAT1 FIRE 0 0 0 0 51.36542778250229

As suspected, we're getting a few too many commands for one satellite, and some junk is coming too close for comfort - 50km! Tuning the time parameters gives us a bigger margin, and decreases the likelihood we need to fire on the same second, at the expense of more iterations of our code loop:

python solution.py | head
2021176.002100 SAT1 FIRE 0 0 0 0 98.45315622551395
2021176.002600 SAT1 FIRE 0 0 0 0 96.75031376122156
2021176.020300 SAT2 FIRE 0 0 0 0 98.92483176706027
2021176.012400 SAT1 FIRE 0 0 0 0 99.9047062367082
2021176.015800 SAT2 FIRE 0 0 0 0 99.99319926160916
2021176.011800 SAT1 FIRE 0 0 0 0 98.48275434745105
2021176.002100 SAT1 FIRE 0 0 0 0 98.34199585482715
2021176.002200 SAT1 FIRE 0 0 0 0 92.8376910745587
2021176.002400 SAT1 FIRE 0 0 0 0 93.97676719658202
2021176.002200 SAT1 FIRE 0 0 0 0 92.78420458157404

We're getting much better with the distance, but we still have SAT1 firing on the same minute - we're not told if this is going to be a problem, but looks like we'll need to check more often to not fire on the exact same second.

At this stage, I handed over to @voidmercy given my lack of experience with quaternions and requirement for sleep.

Comments

Popular posts from this blog

2020 Hack-A-Sat DEFCON Space Security Challenge CTF Qualifiers 2020 - Part 1

Man-in-the-middling SSL / TLS on Windows

2021 Hack-A-Sat DEFCON Space Security Challenge CTF Qualifiers Writeup - Linky