10
2
Fork 0

writeup for 0x20

This commit is contained in:
Erin Moon 2020-05-27 22:00:15 -05:00
parent cbfcadb0bd
commit 687ebbc6a1
3 changed files with 163 additions and 0 deletions

View File

@ -3,6 +3,7 @@
- [aaaa/attitude-adjustment](aaaa/attitude-adjustment)
- [aaaa/digital-filters-meh](aaaa/digital-filters-meh)
- [aaaa/like-to-watch](aaaa/like-to-watch)
- [aaaa/my-0x20](aaaa/my-0x20)
- [aaaa/seeing-stars](aaaa/seeing-stars)
- [aaaa/spacebook](aaaa/spacebook)
- [comms/56k](comms/56k)

92
aaaa/my-0x20/README.md Normal file
View File

@ -0,0 +1,92 @@
# My 0x20
**Category**: Astronomy, Astrophysics, Astrometry, Astrodynamics, AAAA
**Points (final)**: 142 points
**Solves**: 24
> This social media app is back with a vengeance!
**Given files**: myspace-mike33686zulu.tar.bz2
## Writeup
by [erin (`barzamin`)](https://imer.in).
0x20 is SpaceBook, except without a backdoor. If you haven't read that writeup yet, go take a look at it.
That is, the trick in SpaceBook -- magnitudes are trivially matchable -- is gone. Additionally, we have less superbright outliers in the provided unknown star set. We can no longer match specific stars trivially. However, we can still solve the problem: this is what _real_ star trackers have to do (although they have to deal with stuff like measurement and catalog error that don't show up here).
Since the observed (unknown) stars are all one simple rigid transformation (the observation orientation!) away from their positions in the catalog, relative distances and angles are preserved. Assuming that the local neighborhood of each star is represented relatively well in the observation set, we can look at the neighborhood of each star -- hopefully -- to determine what star it is.
Vaguely inspired by _Pattern Recognition of Star Constellations for Spacecraft Applications_, C. C. Liebe 1993 (thanks, google), I designed a simple star fingerprint: every star is represented by a 3-vector containing:
- the distance to the closest neighbor
- the distance to the second-closest neighbor
- the angle between the two closest neighbors, relative to the star under consideration.
To make finding nearest neighbors fast, I used scikit-learn's [ball tree implementation](https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.BallTree.html). I could then easily generate fingerprints for any given set of stars and its ball tree (warning: CTF-quality ML code from here on, sorry):
```{.python}
def angle(x, y):
x_ = x/np.linalg.norm(x)
y_ = y/np.linalg.norm(y)
return np.arccos(np.clip(np.dot(x_, y_), -1.0, 1.0))
def gen_fingerprints(X, bt):
dist, ind = bt.query(X[:], k=3)
fingerprints = []
for i in range(X.shape[0]):
a = X[i,:]
[bi, ci] = ind[i,1:]
b = X[bi,:]
c = X[ci,:]
fingerprints.append([
*dist[i, 1:],
angle(b-a, c-a),
])
return fingerprints
```
We could then fingerprint the star catalog:
```{.python}
with open('./myspace-mike33686zulu/test.txt') as f:
catalog = read_starfile(f.read())
X = np.vstack([s['v'] for s in catalog])
bt_catalog = BallTree(X, leaf_size=30)
fingerprints = gen_fingerprints(X, bt_catalog)
```
You could do something probably more noise resistant, like finding $N$ best-match fingerprints, doing Kabsch on those pairs, and then finding the rest by L2 norm similarity between boresight vectors after rotating to the catalog orientation. I just spat out the index of the closest catalog fingerprint match for each unknown star, throwing out fingerprint matches with error that was too high (L2 distance between fingerprints > $1\times10^{-4}$). Do that for every challenge, and you're done:
```{.python}
for _ in range(5):
refstars = read_starfile(r.recvuntil('\n\n').decode())
Y = np.vstack([s['v'] for s in refstars])
bt_unknown = BallTree(Y, leaf_size=30)
fingerprints_unknown = gen_fingerprints(Y, bt_unknown)
matches = []
for query in fingerprints_unknown:
error = [np.linalg.norm(np.array(fp_known) - np.array(query))
for fp_known in fingerprints]
match_idx = np.argmin(error)
if error[match_idx] < 1e-4:
print(match_idx, error[match_idx])
matches.append(match_idx)
r.send(','.join(map(str,matches)) + '\n')
r.recvuntil('Left...\n')
print(r.clean())
```
Fun fact! We got second or third on this (I don't remember), even though I had the code done in _literally this state_ before anyone else submitted a solution. This was entirely my fault, since I was *accidentally using the SpaceBook star catalog*.
### Full code
```{.python include=zoxtwenty.py}
```
## Resources
- https://ieeexplore.ieee.org/document/180383
- https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.BallTree.html

70
aaaa/my-0x20/zoxtwenty.py Normal file
View File

@ -0,0 +1,70 @@
import numpy as np
from pwnlib import tubes
import time
import matplotlib.pyplot as plt
from rmsd import calculate_rmsd
from scipy.spatial.transform import Rotation
import seaborn as sb
import itertools
from sklearn.neighbors import BallTree
def read_starfile(data):
stars = []
for line in data.strip().split('\n'):
[x,y,z,m] = [float(s.strip()) for s in line.split(',')]
stars.append({'v': np.array([x,y,z]), 'm':m})
return stars
with open('./myspace-mike33686zulu/test.txt') as f:
catalog = read_starfile(f.read())
def angle(x, y):
x_ = x/np.linalg.norm(x)
y_ = y/np.linalg.norm(y)
return np.arccos(np.clip(np.dot(x_, y_), -1.0, 1.0))
def gen_fingerprints(X, bt):
dist, ind = bt.query(X[:], k=3)
fingerprints = []
for i in range(X.shape[0]):
a = X[i,:]
[bi, ci] = ind[i,1:]
b = X[bi,:]
c = X[ci,:]
fingerprints.append([
*dist[i, 1:],
angle(b-a, c-a),
])
return fingerprints
TICKET = 'THE_TICKET'
r = tubes.remote.remote('myspace.satellitesabove.me', 5016)
r.send(TICKET+'\n')
time.sleep(0.5)
r.recvuntil('Ticket please:\n', drop=True)
X = np.vstack([s['v'] for s in catalog])
bt_catalog = BallTree(X, leaf_size=30)
fingerprints = gen_fingerprints(X, bt_catalog)
for _ in range(5):
refstars = read_starfile(r.recvuntil('\n\n').decode())
Y = np.vstack([s['v'] for s in refstars])
bt_unknown = BallTree(Y, leaf_size=30)
fingerprints_unknown = gen_fingerprints(Y, bt_unknown)
matches = []
for query in fingerprints_unknown:
error = [np.linalg.norm(np.array(fp_known) - np.array(query))
for fp_known in fingerprints]
match_idx = np.argmin(error)
if error[match_idx] < 1e-4:
print(match_idx, error[match_idx])
matches.append(match_idx)
r.send(','.join(map(str,matches)) + '\n')
r.recvuntil('Left...\n')
print(r.clean())