{"id":56,"date":"2020-05-27T20:24:52","date_gmt":"2020-05-28T01:24:52","guid":{"rendered":"http:\/\/theshrouded.com\/wp\/?p=56"},"modified":"2024-12-19T15:35:56","modified_gmt":"2024-12-19T20:35:56","slug":"hack-a-sat-2020-i-see-what-you-did-there-write-up","status":"publish","type":"post","link":"https:\/\/theshrouded.com\/wp\/?p=56","title":{"rendered":"Hack-A-Sat 2020 &#8220;I See What You Did There&#8221; Write-Up"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">Description:<\/h2>\n\n\n\n<p>The challenge provides you with two rather large example zip files, one with example data and one with the real data for the flag. The challenge states: <\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-style-default is-layout-flow wp-block-quote-is-layout-flow\"><p>Your rival seems has been tracking satellites with a hobbiest antenna, and it&#8217;s causing a lot of noise on my ground lines. Help me figure out what he&#8217;s tracking so I can see what she&#8217;s tracking <\/p><cite>(Errors appeared in the original)<\/cite><\/blockquote>\n\n\n\n<p>The useful details are in the example zip&#8217;s READMEs. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">RF background<\/h2>\n\n\n\n<p>In this challenge we receive traces of RF signals captured from two separate PWM signals controlling motors that point antennas at satellites. A PWM signal is simply a square wave that has a fixed <em>frequency <\/em>for its rising edge and its <em>duty-cycle<\/em> determines the falling edge. The duty cycle is typically expressed as a percent, and represents the amount of time in each period that the signal is high. A good visualization of this is available <a rel=\"noreferrer noopener\" href=\"https:\/\/developer.android.com\/things\/sdk\/pio\/pwm\" target=\"_blank\">here<\/a>.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"910\" height=\"505\" src=\"http:\/\/theshrouded.com\/wp\/wp-content\/uploads\/2020\/05\/sample_0_plot.png\" alt=\"\" class=\"wp-image-58\" srcset=\"https:\/\/theshrouded.com\/wp\/wp-content\/uploads\/2020\/05\/sample_0_plot.png 910w, https:\/\/theshrouded.com\/wp\/wp-content\/uploads\/2020\/05\/sample_0_plot-300x166.png 300w, https:\/\/theshrouded.com\/wp\/wp-content\/uploads\/2020\/05\/sample_0_plot-768x426.png 768w, https:\/\/theshrouded.com\/wp\/wp-content\/uploads\/2020\/05\/sample_0_plot-326x181.png 326w, https:\/\/theshrouded.com\/wp\/wp-content\/uploads\/2020\/05\/sample_0_plot-700x388.png 700w\" sizes=\"auto, (max-width: 910px) 100vw, 910px\" \/><figcaption>2 peaks per period<\/figcaption><\/figure><\/div>\n\n\n\n<p>When we plot these RF signal values we can see that there is a repeating pattern: there is a peak, followed by some decreasing noise and then another spike. Whats important to recognize is that when a PWM signal changes states, from low to high or high to low, that is a rapid change in current. And according to electromagnetism, this results in a change in magnetic field, which causes EM waves: our RF signals! The repetitions should correspond to the frequency and each spike would be a rising or falling edge.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Solving<\/h2>\n\n\n\n<p>This challenge is reminiscent of other power analysis challenges, but I found that most of those were not well suited to be easily adapted to this challenge. We should be able to map the RF signals we have to PWM and from that we can use this detail in the README to figure out where the antennas were pointed:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p>We believe the azimuth and elevation motors of each antenna are controlled the same way as the earlier groundstation we compromised, using a PWM signal that varies between 5% and 35% duty cycle to move one axis from 0 degrees to 180 degrees.<\/p><\/blockquote>\n\n\n\n<p>I originally interpreted this to mean, &#8220;the duty cycle determines how fast the motors move, and we can correlate the duty cycles to the speed at which a satellite crosses the field of view of the ground station&#8221;. I implemented this and had no luck correlating with the example data. I learned from my teammates that in some motors, the PWM directly controls the position of the motor, which was the correct way to think about this.<\/p>\n\n\n\n<p>From here we can do the following: calculate the time between each peak, determine the period and calculate a duty cycle. If we get values between 5 and 35% we&#8217;re probably on the right track.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; auto-links: false; title: ; notranslate\" title=\"\">\n# find_peaks is a bit overkill in this case, but I didn't\n# know at the time that the peaks were single points\n# height was determined by looking at the data in Excel\npeaks = scipy.signal.find_peaks(az, height=-60.0)\n# I needed to insert the first value, since find_peaks would skip it\npeak_indices = np.insert(peaks&#x5B;0],0,&#x5B;0])\n\n# Get the duty cycle of the first pair\nstart_duty_az = (peak_indices&#x5B;1] - peak_indices&#x5B;0])\/2048\n# and of the last pair for the azimuth signal\nend_duty_az = (peak_indices&#x5B;-1] - peak_indices&#x5B;-2])\/2048\n\n# And do it again for the elevation\nstart_duty_el = (peak_indices&#x5B;1] - peak_indices&#x5B;0])\/2048\nend_duty_el = (peak_indices&#x5B;-1] - peak_indices&#x5B;-2])\/2048\n<\/pre><\/div>\n\n\n<p>This gave me duty cycle percentages that were within our range, so things are looking good. Now, the ground station <em>could <\/em>have been offset, so my thinking was that if we calculate the deltas, the math would cancel out the offsets if we look at the corresponding deltas of the satellites. We know that there is a 30% range of duty cycles for the motors which correspond to 180 degrees of motion, so we need to multiply our duty cycles by 60 to get degrees.<\/p>\n\n\n\n<p>We used PyEphem to calculate the angles needed to look at the satellites from a given location at a given time.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\nwith open(&quot;active.txt&quot;, &quot;r&quot;) as f:\n    tle_data = f.read().split(&quot;\\n&quot;)\n\nfor i in range(int(len(tle_data)\/3)):\n    sat = ephem.readtle(tle_data&#x5B;i*3], tle_data&#x5B;i*3+1], tle_data&#x5B;i*3+2])\n    sats&#x5B;tle_data&#x5B;i*3].strip()] = sat\n\nbase = ephem.Observer()\n# These were from our examples\nbase.lat = ephem.degree * 32.4907\nbase.lon = ephem.degree * 45.8304 \n\u200b\nSTART_TIME = ephem.Date('2020-04-07 08:57:43.726371')\n<\/pre><\/div>\n\n\n<p>From here we can simply compare the deltas of the azimuth and elevation with the expected values from each satellite in the list and find a threshold in the sample data that only returns the correct results.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; auto-links: false; title: ; notranslate\" title=\"\">\na_factor = 180\/30\n\nfor fname in duties.keys():\n    for sat_name, sat in sats.items():\n        sat = sats&#x5B;sat_name]\n        \n        base.date = START_TIME\n        sat.compute(base)\n        start_az = sat.az\/ephem.degree\n        start_el = sat.alt\/ephem.degree\n        \n        base.date = START_TIME + 120 * ephem.second\n        sat.compute(base)\n        end_az = sat.az\/ephem.degree\n        end_el = sat.alt\/ephem.degree\n\n        predicted_start_az = a_factor * (duties&#x5B;fname]&#x5B;0]&#x5B;0]-5)\n        predicted_end_az = a_factor * (duties&#x5B;fname]&#x5B;0]&#x5B;1]-5)\n        predicted_start_el = a_factor * (duties&#x5B;fname]&#x5B;1]&#x5B;0]-5)\n        predicted_end_el = a_factor * (duties&#x5B;fname]&#x5B;1]&#x5B;1]-5)\n\n        pred_del_az = abs(predicted_end_az-predicted_start_az)\n        del_az = abs(end_az-start_az)\n        pred_del_el = abs(predicted_end_el-predicted_start_el)\n        del_el = abs(end_el-start_el)\n       \n        # Determined reasonable error limit from samples\n        if abs(pred_del_az - del_az) &lt; 2 and abs(pred_del_el - del_el) &lt; 2 :\n            print(sat_name)\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">Extra notes<\/h2>\n\n\n\n<p>We had a sample satellite named SORTIE that causes the ground station to flip its azimuth and elevation over one of the 180 degree points, which did not want to correlate. If you looked at a graph of the duty cycles for azimuth, it looks like this: <\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"462\" height=\"220\" src=\"https:\/\/theshrouded.com\/wp\/wp-content\/uploads\/2020\/05\/sortie_discontinuity-1.png\" alt=\"\" class=\"wp-image-68\" srcset=\"https:\/\/theshrouded.com\/wp\/wp-content\/uploads\/2020\/05\/sortie_discontinuity-1.png 462w, https:\/\/theshrouded.com\/wp\/wp-content\/uploads\/2020\/05\/sortie_discontinuity-1-300x143.png 300w, https:\/\/theshrouded.com\/wp\/wp-content\/uploads\/2020\/05\/sortie_discontinuity-1-326x155.png 326w\" sizes=\"auto, (max-width: 462px) 100vw, 462px\" \/><figcaption>The azimuth can&#8217;t go past 180 degrees so it has to wrap around, here we see it as a discontinuity at 35% duty cycle<\/figcaption><\/figure><\/div>\n\n\n\n<p>But in the real data used for the flag no such discontinuity existed, so we could just ignore this case. In order to get around this issue we could have simply skipped the section before the discontinuity.<\/p>\n\n\n\n<p>In the end we did also find one other gotcha: because we had the &#8220;real&#8221; time in a different format we had to specify the timezone because Python used the locale&#8217;s timezone instead of GMT.<\/p>\n\n\n\n<p> <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Description: The challenge provides you with two rather large example zip files, one with example data and one with the real data for the flag. The challenge states: Your rival seems has been tracking satellites with a hobbiest antenna, and it&#8217;s causing a lot of noise on my ground lines. Help me figure out what [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[22],"tags":[4,5,7,6,8,9],"class_list":["post-56","post","type-post","status-publish","format-standard","hentry","category-write-ups","tag-ctf","tag-hack-a-sat","tag-pwm","tag-rf","tag-security","tag-write-up"],"_links":{"self":[{"href":"https:\/\/theshrouded.com\/wp\/index.php?rest_route=\/wp\/v2\/posts\/56","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/theshrouded.com\/wp\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/theshrouded.com\/wp\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/theshrouded.com\/wp\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/theshrouded.com\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=56"}],"version-history":[{"count":24,"href":"https:\/\/theshrouded.com\/wp\/index.php?rest_route=\/wp\/v2\/posts\/56\/revisions"}],"predecessor-version":[{"id":88,"href":"https:\/\/theshrouded.com\/wp\/index.php?rest_route=\/wp\/v2\/posts\/56\/revisions\/88"}],"wp:attachment":[{"href":"https:\/\/theshrouded.com\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=56"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/theshrouded.com\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=56"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/theshrouded.com\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=56"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}