10
2
Fork 0

Merge remote-tracking branch 'origin/master'

This commit is contained in:
5225225 2020-05-26 17:07:13 +01:00
commit e105f3176c
28 changed files with 456 additions and 363 deletions

282
.gitignore vendored
View File

@ -1,284 +1,6 @@
## Core latex/pdflatex auxiliary files:
*.pdf
*.aux
*.lof
*.log
*.lot
*.fls
*.out
*.toc
*.fmt
*.fot
*.cb
*.cb2
.*.lb
## Intermediate documents:
*.dvi
*.xdv
*-converted-to.*
# these rules might exclude image files for figures etc.
# *.ps
# *.eps
# *.pdf
## Generated if empty string is given at "Please type another file name for output:"
.pdf
## Bibliography auxiliary files (bibtex/biblatex/biber):
*.bbl
*.bcf
*.blg
*-blx.aux
*-blx.bib
*.run.xml
## Build tool auxiliary files:
*.fdb_latexmk
*.synctex
*.synctex(busy)
*.synctex.gz
*.synctex.gz(busy)
*.pdfsync
## Build tool directories for auxiliary files
# latexrun
latex.out/
## Auxiliary and intermediate files from other packages:
# algorithms
*.alg
*.loa
# achemso
acs-*.bib
# amsthm
*.thm
# beamer
*.nav
*.pre
*.snm
*.vrb
# changes
*.soc
# comment
*.cut
# cprotect
*.cpt
# elsarticle (documentclass of Elsevier journals)
*.spl
# endnotes
*.ent
# fixme
*.lox
# feynmf/feynmp
*.mf
*.mp
*.t[1-9]
*.t[1-9][0-9]
*.tfm
#(r)(e)ledmac/(r)(e)ledpar
*.end
*.?end
*.[1-9]
*.[1-9][0-9]
*.[1-9][0-9][0-9]
*.[1-9]R
*.[1-9][0-9]R
*.[1-9][0-9][0-9]R
*.eledsec[1-9]
*.eledsec[1-9]R
*.eledsec[1-9][0-9]
*.eledsec[1-9][0-9]R
*.eledsec[1-9][0-9][0-9]
*.eledsec[1-9][0-9][0-9]R
# glossaries
*.acn
*.acr
*.glg
*.glo
*.gls
*.glsdefs
*.lzo
*.lzs
# uncomment this for glossaries-extra (will ignore makeindex's style files!)
# *.ist
# gnuplottex
*-gnuplottex-*
# gregoriotex
*.gaux
*.gtex
# htlatex
*.4ct
*.4tc
*.idv
*.lg
*.trc
*.xref
# hyperref
*.brf
# knitr
*-concordance.tex
# TODO Uncomment the next line if you use knitr and want to ignore its generated tikz files
# *.tikz
*-tikzDictionary
# listings
*.lol
# luatexja-ruby
*.ltjruby
# makeidx
*.idx
*.ilg
*.ind
# minitoc
*.maf
*.mlf
*.mlt
*.mtc[0-9]*
*.slf[0-9]*
*.slt[0-9]*
*.stc[0-9]*
# minted
_minted*
*.pyg
# morewrites
*.mw
# nomencl
*.nlg
*.nlo
*.nls
# pax
*.pax
# pdfpcnotes
*.pdfpc
# sagetex
*.sagetex.sage
*.sagetex.py
*.sagetex.scmd
# scrwfile
*.wrt
# sympy
*.sout
*.sympy
sympy-plots-for-*.tex/
# pdfcomment
*.upa
*.upb
# pythontex
*.pytxcode
pythontex-files-*/
# tcolorbox
*.listing
# thmtools
*.loe
# TikZ & PGF
*.dpth
*.md5
*.auxlock
# todonotes
*.tdo
# vhistory
*.hst
*.ver
# easy-todo
*.lod
# xcolor
*.xcp
# xmpincl
*.xmpi
# xindy
*.xdy
# xypic precompiled matrices and outlines
*.xyc
*.xyd
# endfloat
*.ttt
*.fff
# Latexian
TSWLatexianTemp*
## Editors:
# WinEdt
*.bak
*.sav
# Texpad
.texpadtmp
# LyX
*.lyx~
# Kile
*.backup
# gummi
.*.swp
# KBibTeX
*~[0-9]*
# TeXnicCenter
*.tps
# auto folder when using emacs and auctex
./auto/*
*.el
# expex forward references with \gathertags
*-tags.tex
# standalone packages
*.sta
# Makeindex log files
*.lpz
# xwatermark package
*.xwm
# REVTeX puts footnotes in the bibliography by default, unless the nofootinbib
# option is specified. Footnotes are the stored in a file with suffix Notes.bib.
# Uncomment the next line to have this generated file ignored.
#*Notes.bib
/build/

27
Makefile Normal file
View File

@ -0,0 +1,27 @@
.PHONY: all clean genreadme
DOCS_ALL=$(wildcard **/**/README.md)
WRITEUP_PDF=writeup.pdf
all: README.md $(WRITEUP_PDF)
clean:
$(RM) -r $(WRITEUP_PDF) build/
README.md: $(DOCS_ALL)
@echo "GEN README.md"
@(printf "# BLAHAJ Hack-a-Sat 2020 Writeups\n\n"; for file in $(DOCS_ALL); do \
printf -- "- [%s](%s)\n" "$$(dirname $$file)" "$$(dirname $$file)"; done) > README.md
$(WRITEUP_PDF): $(DOCS_ALL) fonts.tex top.md
@echo "GEN writeup.md"
@mkdir build/ 2>/dev/null || true
@find . -mindepth 3 -iname '*.png' -exec cp {} build/ \;
@find . -mindepth 3 -iname '*.py' -exec cp {} build/ \;
@cp fonts.tex jork.png build/
@for md in top.md $(DOCS_ALL); do cat $$md; printf "\n\n\\\\newpage\n\n"; done > build/writeup.md
@echo "PDF writeup.pdf"
@cd build && \
pandoc --filter pandoc-include-code --toc --toc-depth 1 -f markdown-implicit_figures \
-i writeup.md --pdf-engine=lualatex -o writeup.pdf
@cp build/writeup.pdf $@

View File

@ -0,0 +1,11 @@
# BLAHAJ Hack-a-Sat 2020 Writeups
- [aaaa/attitude-adjustment](aaaa/attitude-adjustment)
- [aaaa/digital-filters-meh](aaaa/digital-filters-meh)
- [aaaa/seeing-stars](aaaa/seeing-stars)
- [comms/56k](comms/56k)
- [ground-segment/phasors-to-stun](ground-segment/phasors-to-stun)
- [payload/leakycrypto](payload/leakycrypto)
- [satellite-bus/bytes-away](satellite-bus/bytes-away)
- [satellite-bus/magic-bus](satellite-bus/magic-bus)
- [space/good-plan-great-plan](space/good-plan-great-plan)

View File

View File

@ -50,10 +50,10 @@ def solve_orientation(stars, catalog):
Note that I threw in an inversion of the rotation matrix; this is because I should've been aligning from the catalog *to* the current star locations. Switching P to be the catalog and Q to be stars would've done the same thing.
Then we just grabbed each challenge from the computer, aligned the sets, and spat the orientation of the satellite back at the server to get
Then we just grabbed each challenge from the computer, aligned the sets, and spat the orientation of the satellite back at the server:
```{.python}
TICKET = 'ticket{papa21503yankee:GGntHycE-_FqMqGbIoz7rKZD-MzEraoec3dRGU21ExVYGGYFLJQQlRLqNLWn8D4ghQ}'
TICKET = 'THE_TICKET'
r = tubes.remote.remote('attitude.satellitesabove.me', 5012)
r.send(TICKET+'\n')
time.sleep(0.5)
@ -73,6 +73,6 @@ The flag should get printed out on stdout by the final line.
```
## Resources and other writeups
- https://en.wikipedia.org/wiki/Orthogonal_Procrustes_problem
- https://en.wikipedia.org/wiki/Kabsch_algorithm
- https://github.com/charnley/rmsd/tree/master
- <https://en.wikipedia.org/wiki/Orthogonal_Procrustes_problem>
- <https://en.wikipedia.org/wiki/Kabsch_algorithm>
- <https://github.com/charnley/rmsd/tree/master>

View File

@ -30,7 +30,7 @@ def solve_orientation(stars, catalog):
rotation = Rotation.from_matrix(np.linalg.inv(rotation_mtx))
return rotation
TICKET = 'ticket{papa21503yankee:GGntHycE-_FqMqGbIoz7rKZD-MzEraoec3dRGU21ExVYGGYFLJQQlRLqNLWn8D4ghQ}'
TICKET = 'THE_TICKET'
r = tubes.remote.remote('attitude.satellitesabove.me', 5012)
r.send(TICKET+'\n')
time.sleep(0.5)

View File

@ -54,7 +54,7 @@ q_att = startracker(actual);
```
Looking inside `startracker()`, we see that it pretty clearly indicates that we *are* the star tracker; every timestep, the code tells us, the adversary, what the true physical orientation is as a quaternion. We can act as the star tracker and send back a wxyz-format quaternion on stdin, which it will use as the star-tracker output (note that, for some reason, the code they give us uses space-separated floats and the actual challenge uses comma-separated floats):
```{.matplotlib}
```{.matlab}
% Model must have a q_att member
function [ q ] = startracker(model)
@ -78,7 +78,7 @@ endfunction
```
Also note that in `challenge.m`, immediately after the star tracker call, checking the return value for consistency with the physical model is _commented out_. We can tell the satellite that it's pointing anywhere we like and it will believe us, although Kalman error might be bad:
```
```{.matlab}
%err = quat2eul(quat_diff(q_att, target.q_att))';
%if max(abs(err)) > err_thresh
% disp("Error: No way, you are clearly lost, Star Tracker!");
@ -99,7 +99,7 @@ Hook up to the server:
sep = ','
r = remote('filter.satellitesabove.me', 5014)
r.clean()
r.send('ticket{foxtrot78531papa:GLliqPNOiBYZl7OwLiZCOx2yfdmbyO6cdgrfcNRC8iMPcJgy0YQ_H1kBeWTloVB_-w}\n')
r.send('THE_TICKET')
time.sleep(0.1)
```
@ -115,7 +115,7 @@ def adversary(true_pose):
```
And talk to the server, pretending to be the tracker every indication, until we see a string indicating we got the flag:
```
```{.python}
while True:
rl = r.readline(timeout=3)
if rl.startswith(b'Uh oh,'):

View File

@ -16,7 +16,7 @@ else:
sep = ','
r = remote('filter.satellitesabove.me', 5014)
r.clean()
r.send('ticket{foxtrot78531papa:GLliqPNOiBYZl7OwLiZCOx2yfdmbyO6cdgrfcNRC8iMPcJgy0YQ_H1kBeWTloVB_-w}\n')
r.send('THE_TICKET')
time.sleep(0.1)
def adversary(true_pose):

View File

@ -53,30 +53,13 @@ return solve
We then automated this entire process using pwnlib to connect to the server and
read the data.
### Full code
```{.python include=seeing-stars.py}
```
Run it:
```
λ has-writeup/aaaa/seeing-stars python seeing-stars.py
b'flag{juliet73678uniform:GDy7YZdtCL9mcLgYuLceK_zwgwSAoT6ui5aMGo1IMYcic9tiI8EXUHLfQDcUsjjt5KqsIvOWUYeM8IS6631Vppw}\n'
Traceback (most recent call last):
File "seeing-stars.py", line 42, in <module>
rawdat = r.recvuntil('Enter', drop=True)
File "/usr/lib/python3.8/site-packages/pwnlib/tubes/tube.py", line 310, in recvuntil
res = self.recv(timeout=self.timeout)
File "/usr/lib/python3.8/site-packages/pwnlib/tubes/tube.py", line 82, in recv
return self._recv(numb, timeout) or b''
File "/usr/lib/python3.8/site-packages/pwnlib/tubes/tube.py", line 160, in _recv
if not self.buffer and not self._fillbuffer(timeout):
File "/usr/lib/python3.8/site-packages/pwnlib/tubes/tube.py", line 131, in _fillbuffer
data = self.recv_raw(self.buffer.get_fill_size())
File "/usr/lib/python3.8/site-packages/pwnlib/tubes/sock.py", line 56, in recv_raw
raise EOFError
EOFError
```
Run it, and the flag should be printed as a bytestring.
## Resources and other writeups
- https://docs.opencv.org/trunk/d9/d61/tutorial_py_morphological_ops.html
- https://docs.opencv.org/trunk/dd/d49/tutorial_py_contour_features.html
- <https://docs.opencv.org/trunk/d9/d61/tutorial_py_morphological_ops.html>
- <https://docs.opencv.org/trunk/dd/d49/tutorial_py_contour_features.html>

View File

@ -33,7 +33,7 @@ def solve(rawdat):
solve += (str(cX) + "," + str(cY)+'\n')
return solve
TICKET = 'ticket{juliet73678uniform:GIE7kv0AbxY1I813dEkfLYg2PhHMnJcdqhouI2zuDG1md_2TQ1Ikgab5osJoTRFzIg}'
TICKET = 'THE_TICKET'
r = tubes.remote.remote('stars.satellitesabove.me', 5013)
r.recvline()
r.send(TICKET+'\n')

View File

257
comms/56k/README.md Normal file
View File

@ -0,0 +1,257 @@
# 56K Flex Magic
**Category:** Communication Systems
**Points (final):** 205
**Solves:** 13
>Anyone out there speak modem anymore? We were able to listen in to on, maybe you can ask it for a
>flag…
>
>UPDATE: Since everyone is asking, yes...a BUSY signal when dialing the ground station is expected
>behavior.
## Write-up
by [haskal](https://awoo.systems)
An audio file is included that contains a session where a number is dialed, and then some modem data
is exchanged (it's a very distinctive sound). Additionally, there is a note included with the
following text.
```
---=== MY SERVER ===---
Phone #: 275-555-0143
Username: hax
Password: hunter2
* Modem implements a (very small) subset of 'Basic' commands as
described in the ITU-T V.250 spec (Table I.2)
---=== THEIR SERVER ===---
Ground station IP: 93.184.216.34
Ground station Phone #: 458-XXX-XXXX ...?
Username: ?
Password: ?
* They use the same model of modem as mine... could use +++ATH0
ping of death
* It'll have an interactive login similar to my server
* Their official password policy: minimum requirements of
FIPS112 (probably just numeric)
* TL;DR - section 4.1.1 of 'NBS Special Publication 500-137'
```
ITU-T V.250 is essentially a formalization of the [Hayes command
set](https://en.wikipedia.org/wiki/Hayes_command_set), so we can use basic Hayes commands to
interact with our local modem, such as
```
ATDTXXXXXXXXXX - dial number XXX...
ATH0 - hang up
+++ - get the local modem's attention while in a remote session
```
The first step is to try to get information about the ground station server. We can decode the dial
tones from the audio file, which are
[DTMF](https://en.wikipedia.org/wiki/Dual-tone_multi-frequency_signaling) tones. Once decoded we
obtain a phone number for the ground station of `458-555-0142`. However dialing this number results
in an error - `BUSY`. Presumably, the ground station is already dialed into somewhere, and we need
to disconnect it.
The "ping of death" refers to injecting a modem command into a packet sent to a remote server in
order to cause the server's modem to interpret a hang up command contained in the packet. This can
be achieved by pinging with the data `+++ATH0`, because as the server replies to the ping with the
same data, its local modem will interpret the command inside the ping. But we need to escape it with
hex to avoid having our local modem hang up instead. Once in the session, we dial the number in the
text file to get an initial shell session
```
ATDT2755550143
```
Next, issue a ping of death to the provided server IP
```bash
ping -v 0x2b2b2b415448290d 93.184.216.34
```
Now the ground station should be disconnected so it is available for us to dial.
```
+++ATH0
ATDT45845550142
```
We get a login prompt for SATNET
```
* * . * * * . * * . * * .
. * * . * . . * . *
* +------------------------------+
. | SATNET | *
+------------------------------+ .
. | UNAUTHORIZED ACCESS IS |
| STRICTLY PROHIBITED |
. +------------------------------+ .
. .
.
Setting up - this will take a while...
LOGIN
Username:
```
However we still need the username and password. Maybe the provided audio file has the credentials
somewhere in the dialup modem exchange. By analyzing the spectrum in Audacity (or any analyzer of
choice) we discover that it has peaks around 980 Hz, 1180 Hz, 1650 Hz, and 1850 Hz. This is
consistent with the [ITU V.21 standard](https://www.itu.int/rec/T-REC-V.21-198811-I/en) which uses
dual-channel Frequency Shift Keying at 300 bits/second. We can use
[minimodem](https://github.com/kamalmostafa/minimodem) to decode the modem traffic. We can provide
the two FSK frequencies (the "mark" and "space", representing each bit of the data) for channel 1
and then for channel 2 to get both sides of the exchange. We also need to provide the bit rate.
```bash
minimodem -8 -S 980 -M 1180 -f recording.wav 300
minimodem -8 -S 1650 -M 1850 -f recording.wav 300
```
This data looks like garbage but it contains some strings, notably `rocketman2674`. We assume from
the notes file that the password is a 4-digit number, but trying the username `rocketman` and
password `2674` didn't work. We need to look closer. This is the beginning of one side of the
exchange in hex:
```hexdump
00000000: 7eff 7d23 c021 7d21 7d20 7d20 7d34 7d22 ~.}#.!}!} } }4}"
00000010: 7d26 7d20 7d20 7d20 7d20 7d25 7d26 28e5 }&} } } } }%}&(.
00000020: 4c21 7d27 7d22 7d28 7d22 e193 7e7e ff7d L!}'}"}(}"..~~.}
00000030: 23c0 217d 217d 217d 207d 347d 227d 267d #.!}!}!} }4}"}&}
00000040: 207d 207d 207d 207d 257d 2628 e54c 217d } } } }%}&(.L!}
```
It starts with `7eff`, which is characteristic of [Point-to-Point
Protocol](https://en.wikipedia.org/wiki/Point-to-Point_Protocol). We can decode the packets with
[scapy](https://github.com/secdev/scapy), a framework for network protocol analysis. However, first
we have to de-frame the PPP frames since there doesn't seem to be a tool for this automatically.
There are two main tasks, first split up the frames by the `7e` delimiters, and then remove the byte
stuffing within the frame, since PPP will escape certain bytes with the `7d` prefix followed by the
byte XOR `0x20`. Finally, the frame can be passed to scapy for analysis. This is a VERY lax
de-framer because sometimes frames seemed to not be started or terminated properly.
```python
def decode(ch):
buf2 = b""
esc = False
for x in ch:
if x == 0x7e:
if buf2 != b"\xFF" and buf2 != b"":
PPP(buf2).show()
buf2 = b""
esc = False
elif esc:
esc = False
buf2 += bytes([x^0x20])
elif x == 0x7d:
esc = True
else:
buf2 += bytes([x])
if len(buf2) > 0:
PPP(buf2).show()
```
(This code is really awful CTF code, please ignore the 200 awful spaghetti things I'm doing in this
snippet.)
Now we can see what the packets mean. In particular, we spot these ones:
```
###[ HDLC ]###
address = 0xff
control = 0x3
###[ PPP Link Layer ]###
proto = Link Control Protocol
###[ PPP Link Control Protocol ]###
code = Configure-Ack
id = 0x2
len = 28
\options \
.....
|###[ PPP LCP Option ]###
| type = Authentication-protocol
| len = 5
| auth_protocol= Challenge-response authentication protocol
| algorithm = MS-CHAP
.....
###[ PPP Link Layer ]###
proto = Challenge Handshake Authentication Protocol
###[ PPP Challenge Handshake Authentication Protocol ]###
code = Response
id = 0x0
len = 67
value_size= 49
value = 0000000000000000000000000000000000000000000000006c2e3af0f2f7760
2e9831310b56924f3428b05ad60c7a2b401
optional_name= 'rocketman2674'
```
and
```
###[ PPP Link Layer ]###
proto = Challenge Handshake Authentication Protocol
###[ PPP Challenge Handshake Authentication Protocol ]###
code = Challenge
id = 0x0
len = 26
value_size= 8
value = 12810ab88c7f1c74
optional_name= 'GRNDSTTNA8F6C'
###[ PPP Link Layer ]###
proto = Challenge Handshake Authentication Protocol
###[ PPP Challenge Handshake Authentication Protocol ]###
code = Success
id = 0x0
len = 4
data = ''
```
We can see in this exchange that the client has negotiated `MS-CHAP` authentication and then
authenticates to the server successfully. MS-CHAP uses NetNTLMv1 hashes, which can be cracked very
easily. We just need the username (`rocketman2674`), the "challenge" which is used as a salt for the
hash, and the hash itself. The format of the response in MS-CHAP (according to RFC2433) is 49 bytes,
including 24 bytes of stuff we ignore, 24 bytes of hash, and one byte of stuff we also ignore. We
can now convert the data into a [John-the-Ripper](https://www.openwall.com/john/) compatible hash
like
```
username:$NETNTLM$challenge$hash
rocketman2674:$NETNTLM$12810ab88c7f1c74$6c2e3af0f2f77602e9831310b56924f3428b05ad60c7a2b4
```
Technically, you can use hashcat as well but I didn't want to bother with the hashcat flags. Put
this hash in a text file and run `john file.txt`. No need to specify 4 digit pins because john will
complete in literal seconds anyway.
```
Proceeding with incremental:ASCII
9435 (rocketman2674)
1g 0:00:00:08 DONE 3/3 (2020-05-26 03:07) 0.1212g/s 10225Kp/s 10225Kc/s 10225KC/s 97xx..94b4
Use the "--show --format=netntlm" options to display all of the cracked passwords reliably
Session completed
```
Use `rocketman2674` with password `9435` to log in to the ground station, then execute the `flag`
command to get the flag.
## Resources and other writeups
* <https://en.wikipedia.org/wiki/Hayes_command_set>
* <https://en.wikipedia.org/wiki/Dual-tone_multi-frequency_signaling>
* <https://github.com/kamalmostafa/minimodem>
* <https://en.wikipedia.org/wiki/Point-to-Point_Protocol>
* <https://tools.ietf.org/html/rfc2433>
* <https://www.openwall.com/john/>

38
comms/56k/decode.py Normal file
View File

@ -0,0 +1,38 @@
#!/usr/bin/env python3
from scapy.all import *
with open("ch1", "rb") as f:
ch1 = f.read()
with open("ch2", "rb") as f:
ch2 = f.read()
# print(PPP(ch2[0x16a:0x186]).show())
# print(PPP(ch1[0x171:0x170+70]).show())
def decode(ch):
buf2 = b""
esc = False
for x in ch:
if x == 0x7e:
if buf2 != b"\xFF" and buf2 != b"":
print(PPP(buf2).__repr__())
buf2 = b""
esc = False
elif esc:
esc = False
buf2 += bytes([x^0x20])
elif x == 0x7d:
esc = True
else:
buf2 += bytes([x])
if len(buf2) > 0:
print(PPP(buf2).__repr__())
print("\n", "=====================", "CH 1", "\n")
decode(ch1)
print("\n", "=====================", "CH 2", "\n")
decode(ch2)

18
fonts.tex Normal file
View File

@ -0,0 +1,18 @@
% why are we in a separate file here?
% pandoc really likes to mess with these commands when they're in the yaml config so, it's a
% separate file out of pandoc's evil clutches now
% i literally had to force upgrade to absolute bleeding edge lualatex to get this working ok
\usepackage{fontspec}
\usepackage{newunicodechar}
\newfontfamily{\emojifont}{Noto Color Emoji}[Renderer=Harfbuzz]
\DeclareTextFontCommand{\textemoji}{\emojifont}
\newunicodechar{🧃}{\textemoji{🧃}}
\newfontfamily{\symbolfont}{DejaVu Sans}[Renderer=Harfbuzz]
\DeclareTextFontCommand{\textsymbol}{\symbolfont}
\newunicodechar{}{\textsymbol{}}
\newunicodechar{}{\textsymbol{}}
\setmonofont{Noto Sans Mono}[Scale=0.9]

View File

@ -0,0 +1,35 @@
# Phasors to Stun
**Category:** Ground Segment
**Points (final):** 62
**Solves:** 71
>Demodulate the data from an SDR capture and you will find a flag. It is a wav file, but that
>doesn't mean its audio data.
## Write-up
by [haskal](https://awoo.systems)
The provided WAV file contains a signal that looks like this:
![Audacity showing the signal](signal.png)
This looks suspiciously like Phase Shift Keying (PSK) and it's a very clean signal (this is also
hinted at by the challenge name). We can use [Universal Radio Hacker](https://github.com/jopohl/urh)
to demod this with very little effort.
![URH with the signal open](urh1.png)
Select PSK modulation, then click "Autodetect parameters". Then move to Analysis:
![URH Analysis pane](urh2.png)
We discovered that the signal is NRZI (non-return-to-zero inverted) coded, and after selecting this
in URH the flag is decoded in the data view.
## Resources and other writeups
* <https://github.com/jopohl/urh>
* <https://en.wikipedia.org/wiki/Phase-shift_keying>
* <https://en.wikipedia.org/wiki/Non-return-to-zero#NRZI>

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

BIN
jork.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 623 KiB

View File

View File

View File

@ -26,6 +26,7 @@ instance automatically by writing the configuration file at
`cosmos/config/tools/cmd_tlm_server/cmd_tlm_server.txt`.
When COSMOS is successfully connected to the CTF instance it looks like this (no themes were
installed in the Docker container so it looks like Windows 95, I'm so sorry,)
![COSMOS main window](COSMOS.png)
COSMOS can be used to send commands with the Command Sender, and we can send for example a command
@ -64,7 +65,9 @@ at any actual telemetry.
```ruby
12.upto(212) { |off|
offset = off
cmd("MM PEEK_MEM with CCSDS_STREAMID 6280, CCSDS_SEQUENCE 49152, CCSDS_LENGTH 73, CCSDS_FUNCCODE 2, CCSDS_CHECKSUM 0, DATA_SIZE 8, MEM_TYPE 1, PAD_16 0, ADDR_OFFSET #{offset}, ADDR_SYMBOL_NAME 'KitToFlagPkt'")
cmd("MM PEEK_MEM with CCSDS_STREAMID 6280, CCSDS_SEQUENCE 49152, CCSDS_LENGTH 73, "
+ "CCSDS_FUNCCODE 2, CCSDS_CHECKSUM 0, DATA_SIZE 8, MEM_TYPE 1, PAD_16 0, "
+ "ADDR_OFFSET #{offset}, ADDR_SYMBOL_NAME 'KitToFlagPkt'")
}
```

View File

@ -74,7 +74,8 @@ def decode_pkt(b):
field4 = to_hex(b[27:30])
if b[30] != ord('?'):
print('b[30] is not ?')
print(': 00:00:00 > {} {} {} @ {} {} {} ?'.format(field1, field1end, field2, field3, field3end, field4))
print(': 00:00:00 > {} {} {} @ {} {} {} ?'.format(field1, field1end, field2,
field3, field3end, field4))
elif b[0] == ord(';'):
print('delimiter') # end of previous packet?
else:
@ -138,8 +139,26 @@ tried, this was extremely unlikely.
We then tried reading the data sequentially from the buffer, from Juicy Data 00
to 04. Here's the entire string:
```
Juicy Data 00\x00\xc8\xf7\xeb\x15\x96=kp\\\xc9,^\xd5\xcf\\1\x99\x19w\x9a\xc6\xa9\x08e\x8dU\x92j7,\x00\xff#\xeb\x14\xb9)\x7f)\x85HV\xe3\x1d%?O\xbeY\xc6Juicy Data 01\x00R\x01\x1e{\x81G\x00\xc9\x9d\xe3\xe7\xc2#6\x81|\xfc\xd9\x9bk:\x1fh\xf05\xce\xddw5\xca\xdc\x87\xcc\xfa\x02MA\x02\x16\xdf\xe5\xfd\xa1\x083"\x84/\xfc\x1fJuicy Data 02\x00\xc0\x8f\xe7\x02\x91\xfd\xe1w\xfb\x82\x7f.\xa5\x04^\xa1#\xf9\xd7b\xfc\xfd\xd5\xcd\x00\xc0\xd4\xce\x86ahG\xf1OI\x82M*\xf9H\xacyvQ}\xd4\xf2\xa0\xcd\xc9Juicy Data 03\x00M\xae@\x9a\xd89\xe2\x85\xb2Y\xd6/-\xc9\xd0\xfb\x92\xd2\xc4Y\xaa[ B\xc6\xb5a\x93\xb3\xc6P\x01u\x90\x9bM\xca~\xd2|\xd7\xa9\xac\x04r|\xff\x04N\xc4Juicy Data 04\x00Z\x83%$\x01\xf8\xa0\xd8\xa1L\xdc\x13\xc8\xdc\x17\x17\xa0u\x10\xbf\xf2K\xa5%\xe8\x1e\x0cK\xe8\xf3
```hexdump
00000000: 4a75 6963 7920 4461 7461 2030 3000 c8f7 Juicy Data 00...
00000010: eb15 963d 6b70 5cc9 2c5e d5cf 5c31 9919 ...=kp\.,^..\1..
00000020: 779a c6a9 0865 8d55 926a 372c 00ff 23eb w....e.U.j7,..#.
00000030: 14b9 297f 2985 4856 e31d 2558 58be 59c6 ..).).HV..%XX.Y.
00000040: 4a75 6963 7920 4461 7461 2030 3100 5201 Juicy Data 01.R.
00000050: 1e7b 8147 00c9 9de3 e7c2 2336 817c fcd9 .{.G......#6.|..
00000060: 9b6b 3a1f 68f0 35ce dd77 35ca dc87 ccfa .k:.h.5..w5.....
00000070: 024d 4102 16df e5fd a108 3322 842f fc1f .MA.......3"./..
00000080: 4a75 6963 7920 4461 7461 2030 3200 c08f Juicy Data 02...
00000090: e702 91fd e177 fb82 7f2e a504 5ea1 23f9 .....w......^.#.
000000a0: d762 fcfd d5cd 00c0 d4ce 8661 6847 f14f .b.........ahG.O
000000b0: 4982 4d2a f948 ac79 7651 7dd4 f2a0 cdc9 I.M*.H.yvQ}.....
000000c0: 4a75 6963 7920 4461 7461 2030 3300 4dae Juicy Data 03.M.
000000d0: 409a d839 e285 b259 d62f 2dc9 d0fb 92d2 @..9...Y./-.....
000000e0: c459 aa5b 2042 c6b5 6193 b3c6 5001 7590 .Y.[ B..a...P.u.
000000f0: 9b4d ca7e d27c d7a9 ac04 727c ff04 4ec4 .M.~.|....r|..N.
00000100: 4a75 6963 7920 4461 7461 2030 3400 5a83 Juicy Data 04.Z.
00000110: 2524 01f8 a0d8 a14c dc13 c8dc 1717 a075 %$.....L.......u
00000120: 10bf f24b a525 e81e 0c4b e8f3 ...K.%...K..
```
Unfortunately, nothing meaningful was derived from this. There are a `{` and `}`
with bytes between them, but they aren't flag length.
@ -168,7 +187,7 @@ of the form of `^3b+00+00+XX+.` where XX<38 shuts it down, but only 37 enables
dump mode. This can probably be done with a fuzzer. Why has God abandoned us?
What accursed malfunction did we do to deserve this fate?
If you send the packet `^ca+00+44+79+20+44+61+74+61+20+30+31+00+52+01+1e+7b+81+47+00+c9+9d+e3+e7+c2+23+36+81+7c+fc+d9+9b+6b+3a+1f+68+f0+35+ce+dd+77+35+ca+dc+87+cc+.`,
If you send the packet `^ca+00+44+79+20+44+61+74+61+20+30+31+00+52+01+1e+7b+81+47+00+.....+87+cc+.`,
the same packet is sent back.
This means that the juice packets deliminated with `\xca` are actually instructions.
@ -181,7 +200,7 @@ By playing with the packet, the format appears to go:
With the inject:
```
b"^3b+00+00+37+."
b"^ca+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+."
b"^ca+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+....+00+00+."
```
we can query for everything in memory. And we did.
@ -191,43 +210,11 @@ we can query for everything in memory. And we did.
Run it:
```
λ has-writeup/satellite-bus/magic-bus python magic-bus.py master 2h ⬡
Injection: ^3b+00+00+37+.
b'^3a+00+00+3e+00+00+00+33+17+43+3c+0b+68+40+41+4e+dc+a0+c0+3e+6c+a0+40+c4+96+3f+c1+f8+fc+e6+3f+.'
START: 0000003317433c0b6840414edca0c03e6ca040c4963fc1f8fce63f
: 00:00:00 > 33:17:43:3c:b:68 @ 4e:dc:a0:c0:3e:6c:a0 @ c4:96 ? f8:fc:e6 ?
λ has-writeup/satellite-bus/magic-bus python magic-bus.py
....
b'^3b+00+00+3f+.'
ONCE CALL
delimiter
b'^3a+00+00+3f+00+00+00+38+94+53+40+c8+2e+40+41+01+3a+a0+c0+69+11+a1+40+7c+2e+40+c1+9b+1c+e6+3f+.'
ONCE: 00000038945340c82e4041013aa0c06911a1407c2e40c19b1ce63f
: 00:00:00 > 38:94:53:40:c8:2e @ 1:3a:a0:c0:69:11:a1 @ 7c:2e @ 9b:1c:e6 ?
b'^ca+00+44+79+20+44+61+74+61+20+30+31+00+52+01+1e+7b+81+47+00+c9+9d+e3+e7+c2+23+36+81+7c+fc+d9+9b+6b+3a+1f+68+f0+35+ce+dd+77+35+ca+dc+87+cc+.'
JUICE: b'\xca\x00Dy Data 01\x00R\x01\x1e{\x81G\x00\xc9\x9d\xe3\xe7\xc2#6\x81|\xfc\xd9\x9bk:\x1fh\xf05\xce\xddw5\xca\xdc\x87\xcc'
b'^3b+00+00+3e+.'
END CALL
delimiter
b'^3a+00+00+3e+00+00+00+ce+49+d5+3b+e9+6b+3f+41+8f+71+a0+c0+fa+72+a0+40+17+e5+3f+c1+51+0a+e7+3f+.'
INJECTING
END: 000000ce49d53be96b3f418f71a0c0fa72a04017e53fc1510ae73f
: 00:00:00 > ce:49:d5:3b:e9:6b ? 8f:71:a0:c0:fa:72:a0 @ 17:e5 ? 51:a:e7 ?
b'^3b+00+00+37+.'
SHUT DOWN SUCCESSFUL
INJECTING AGAIN
b'^ca+00+00+4a+75+69+63+79+20+44+61+74+61+20+30+30+00+c8+f7+eb+15+96+3d+6b+70+5c+c9+2c+5e+d5+cf+5c+31+99+19+77+9a+c6+a9+08+65+8d+55+92+6a+37+2c+00+ff+23+eb+14+b9+29+7f+29+85+48+56+e3+1d+25+3f+4f+be+59+c6+4a+75+69+63+79+20+44+61+74+61+20+30+31+00+52+01+1e+7b+81+47+00+c9+9d+e3+e7+c2+23+36+81+7c+fc+d9+9b+6b+3a+1f+68+f0+35+ce+dd+77+35+ca+dc+87+cc+fa+02+4d+41+02+16+df+e5+fd+a1+08+33+22+84+2f+fc+1f+4a+75+69+63+79+20+44+61+74+61+20+30+32+00+c0+8f+e7+02+91+fd+e1+77+fb+82+7f+2e+a5+04+5e+a1+23+f9+d7+62+fc+fd+d5+cd+00+c0+d4+ce+86+61+68+47+f1+4f+49+82+4d+2a+f9+48+ac+79+76+51+7d+d4+f2+a0+cd+c9+4a+75+69+63+79+20+44+61+74+61+20+30+33+00+4d+ae+40+9a+d8+39+e2+85+b2+59+d6+2f+2d+c9+d0+fb+92+d2+c4+59+aa+5b+20+42+c6+b5+61+93+b3+c6+50+01+75+90+9b+4d+ca+7e+d2+7c+d7+a9+ac+04+72+7c+ff+04+4e+c4+4a+75+69+63+79+20+44+61+74+61+20+30+34+00+5a+83+25+24+01+f8+a0+d8+a1+4c+dc+13+c8+dc+17+17+a0+75+10+bf+f2+4b+a5+25+e8+1e+0c+4b+e8+f3+23+42+76+48+66+77+40+06+4f+e1+53+2c+f4+1b+08+0c+32+a8+81+42+4a+75+69+63+79+20+44+61+74+61+20+30+35+00+2c+bb+86+6d+c2+d6+4e+15+02+43+30+0a+4f+63+b2+d0+a5+19+43+33+26+dc+a9+52+81+6a+65+1a+4e+bb+29+7b+76+af+e8+38+85+36+4d+66+6c+61+67+7b+6f+73+63+61+72+33+39+36+31+36+6b+69+6c+6f+3a+47+43+78+6d+68+4f+52+59+61+36+35+59+30+50+6d+52+74+46+6d+6c+46+53+42+6d+6e+76+49+6d+45+69+57+67+63+6f+47+32+70+6f+73+49+5f+6e+56+51+51+39+5a+4b+35+44+65+4b+76+56+53+76+69+6f+2d+4c+4c+2d+36+58+32+6a+66+52+46+77+39+42+34+58+71+34+6f+56+51+44+69+71+46+44+74+50+4d+7d+00+0d+70+a9+16+2e+df+4e+64+76+e3+91+15+87+6b+ad+72+22+af+71+ad+6c+91+9d+bd+3e+5e+34+67+.'
JUICE: b'\xca\x00\x00Juicy Data 00\x00\xc8\xf7\xeb\x15\x96=kp\\\xc9,^\xd5\xcf\\1\x99\x19w\x9a\xc6\xa9\x08e\x8dU\x92j7,\x00\xff#\xeb\x14\xb9)\x7f)\x85HV\xe3\x1d%?O\xbeY\xc6Juicy Data 01\x00R\x01\x1e{\x81G\x00\xc9\x9d\xe3\xe7\xc2#6\x81|\xfc\xd9\x9bk:\x1fh\xf05\xce\xddw5\xca\xdc\x87\xcc\xfa\x02MA\x02\x16\xdf\xe5\xfd\xa1\x083"\x84/\xfc\x1fJuicy Data 02\x00\xc0\x8f\xe7\x02\x91\xfd\xe1w\xfb\x82\x7f.\xa5\x04^\xa1#\xf9\xd7b\xfc\xfd\xd5\xcd\x00\xc0\xd4\xce\x86ahG\xf1OI\x82M*\xf9H\xacyvQ}\xd4\xf2\xa0\xcd\xc9Juicy Data 03\x00M\xae@\x9a\xd89\xe2\x85\xb2Y\xd6/-\xc9\xd0\xfb\x92\xd2\xc4Y\xaa[ B\xc6\xb5a\x93\xb3\xc6P\x01u\x90\x9bM\xca~\xd2|\xd7\xa9\xac\x04r|\xff\x04N\xc4Juicy Data 04\x00Z\x83%$\x01\xf8\xa0\xd8\xa1L\xdc\x13\xc8\xdc\x17\x17\xa0u\x10\xbf\xf2K\xa5%\xe8\x1e\x0cK\xe8\xf3#BvHfw@\x06O\xe1S,\xf4\x1b\x08\x0c2\xa8\x81BJuicy Data 05\x00,\xbb\x86m\xc2\xd6N\x15\x02C0\nOc\xb2\xd0\xa5\x19C3&\xdc\xa9R\x81je\x1aN\xbb){v\xaf\xe88\x856Mflag{oscar39616kilo:GCxmhORYa65Y0PmRtFmlFSBmnvImEiWgcoG2posI_nVQQ9ZK5DeKvVSvio-LL-6X2jfRFw9B4Xq4oVQDiqFDtPM}\x00\rp\xa9\x16.\xdfNdv\xe3\x91\x15\x87k\xadr"\xafq\xadl\x91\x9d\xbd>^4g'
JUICE: b'.....v\xaf\xe88\x856Mflag{oscar39616kilo:GCxmhORYa65Y0PmRtFmlFSBmnvImEiWg.....'
```
Hey look, a flag!

View File

@ -4,7 +4,7 @@ import sys
from pwnlib import tubes
TICKET = 'ticket{oscar39616kilo:GPvmwTTzj5JlUEWS4qze0U3-MIIoybpJ5VAClSr3D6-3uOBwYuvsP1RK8jfQixupxQ}'
TICKET = 'THE_TICKET'
r = tubes.remote.remote('bus.satellitesabove.me', 5041)
r.send(TICKET+'\n')
time.sleep(0.5)
@ -31,7 +31,8 @@ def decode_pkt(b):
field4 = to_hex(b[27:30])
if b[30] != ord('?'):
print('b[30] is not ?')
print(': 00:00:00 > {} {} {} @ {} {} {} ?'.format(field1, field1end, field2, field3, field3end, field4))
print(': 00:00:00 > {} {} {} @ {} {} {} ?'.format(field1, field1end, field2,
field3, field3end, field4))
elif b[0] == ord(';'):
print('delimiter') # end of previous packet?
else:
@ -41,7 +42,7 @@ def decode_pkt(b):
start = True
inj = b"^3b+00+00+37+."
inj2 = b"^ca+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+00+."
inj2 = b"^ca+" + (b"00+" * 512) + b"."
dont = False
inj2_b = False

View File

View File

@ -54,5 +54,5 @@ The rest of the strategy is pretty much just to use trial and error:
```
## Resources and other writeups
- http://gpredict.oz9aec.net/
- https://en.wikipedia.org/wiki/Two-line_element_set
- <http://gpredict.oz9aec.net/>
- <https://en.wikipedia.org/wiki/Two-line_element_set>

11
top.md Normal file
View File

@ -0,0 +1,11 @@
---
title: BLAHAJ
mainfont: Noto Serif
geometry: margin=0.7in
header-includes: |
\input{fonts.tex}
include-before: \begin{center}\includegraphics[width=0.7\pagewidth]{jork.png}\end{center}\newpage
---