1201 alarm: sucks
This commit is contained in:
parent
a63a569a54
commit
f74e6e08cc
|
@ -13,4 +13,5 @@
|
|||
- [payload/leakycrypto](payload/leakycrypto)
|
||||
- [satellite-bus/bytes-away](satellite-bus/bytes-away)
|
||||
- [satellite-bus/magic-bus](satellite-bus/magic-bus)
|
||||
- [space/1201-alarm](space/1201-alarm)
|
||||
- [space/good-plan-great-plan](space/good-plan-great-plan)
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
# 1201 Alarm
|
||||
|
||||
**Category**: Space and Things
|
||||
**Points (final)**: 197 points
|
||||
**Solves**: 14
|
||||
|
||||
> Step right up, here's one pulled straight from the history books. See if you can DSKY your way through this challenge! (Thank goodness VirtualAGC is a thing…)
|
||||
|
||||
## Writeup
|
||||
by [erin (`barzamin`)](https://imer.in).
|
||||
|
||||
*Note*: I went into this challenge without knowing much about the Apollo Guidance Computer beyond the existence of a fanatic preservation community and the fact that it used a weird verb-noun interface. Thanks to this challenge, utterly unrelated to any extant satellites I know of, I've gained the ability to read core rope memory words off of an Apollo 11 guidance computer, word-by-word from the DSKY. Useful, right?
|
||||
|
||||
|
||||
Connecting to the challenge, we get some flavor text and a problem, as well as an IP/port to connect to a virtual Apollo Guidance Computer:
|
||||
|
||||
```
|
||||
λ ~
|
||||
» nc apollo.satellitesabove.me 5024
|
||||
Ticket please:
|
||||
THE_TICKET
|
||||
The rope memory in the Apollo Guidance Computer experienced an unintended 'tangle' just
|
||||
prior to launch. While Buzz Aldrin was messing around with the docking radar and making Neil
|
||||
nervous; he noticed the value of PI was slightly off but wasn’t exactly sure by how much. It
|
||||
seems that it was changed to something slightly off 3.14 although still 3 point something.
|
||||
The Comanche055 software on the AGC stored the value of PI under the name "PI/16", and
|
||||
although it has always been stored in a list of constants, the exact number of constants in
|
||||
that memory region has changed with time.
|
||||
|
||||
Help Buzz tell ground control the floating point value PI by connecting your DSKY to the
|
||||
AGC Commanche055 instance that is listening at 3.15.213.229:18364
|
||||
|
||||
What is the floating point value of PI?:
|
||||
```
|
||||
|
||||
The Apollo Guidance Computer (AGC) used a head module called the DSKY as its user-faciing interface. The VirtualAGC suite has tools for simulating the AGC as well as controlling it with a tool called yaDSKY. Since yaDSKY communicates over a TCP socket, it's reasonable to assume the address given can be connected to with yaDSKY, which will let us control the remote, simulated AGC.
|
||||
|
||||
I downloaded and built the VirtualAGC suite, and threw together a little script (`start_dsky.py`) to grab a new challenge and connect to it:
|
||||
```{.python}
|
||||
from pwn import *
|
||||
import time
|
||||
|
||||
TICKET = 'THE_TICKET'
|
||||
r = remote('apollo.satellitesabove.me', 5024)
|
||||
time.sleep(0.1)
|
||||
r.clean()
|
||||
r.send(TICKET+'\n')
|
||||
|
||||
r.readuntil('listening at ')
|
||||
[ip, port] = r.readuntil('\n').decode().strip().split(':')
|
||||
os.spawnl(os.P_NOWAIT, cmd := f'./yaDSKY2 --ip={ip} --port={port}')
|
||||
log.info(cmd)
|
||||
r.interactive()
|
||||
```
|
||||
|
||||
Unfortunately, `apollo.satellitesabove.me` is now down and I can't provide screenshots of my exact solvepath. However, I can vaguely illustrate with a DSKY screenshot.
|
||||
|
||||
![A yaDSKY2 winow](dsky.png){ width=50% }
|
||||
|
||||
_The real DSKY looks much cooler; the _fake_ DSKY looks like this._
|
||||
|
||||
The important thing to know about the DKSY is that it uses a weird verb-noun UI. Effectively, to do anything in the running program, you press **VERB** and type two digits to select an action, **NOUN** and two digits to select that action's target, and hit **ENTR**. Depending on the noun, you might have to key in some additional information and press enter again.
|
||||
|
||||
By grepping the source code in the `Comanche055` directory of the VirtualAGC repo, I found the location of the `PI/16` constant: the `TIME_OF_FREE_FALL` file, near the end in a table of constants:
|
||||
```
|
||||
060455,000703: 27,3355 06220 37553 PI/16 2DEC 3.141592653 B-4
|
||||
```
|
||||
|
||||
The important things here are the address `27,3355` (bank 27, address 3355 octal), and the values `06220` and `37553`, the raw octal words making up the double-precision representation of $\pi/16$. The AGC's memory is split into banks of 1024 words; each word is fifteen bits long. Address `27,3355` thus means "address $3355_8$ in bank $27_8$"; the addressing for each bank starts at $2000_8$, so this address is actually the $3355_8-2000_8 = 1355_8$th word in bank 27. Given the problem description, I assumed that the constant we're looking for (something close to the true value of $\pi$) would be around this location.
|
||||
|
||||
I immediately started looking for verbs in the Comanche055 default program which could read memory, and found 'V27 DISPLAY FIXED MEMORY' in verb tables for Apollo 11, which seems like it should be able to read the read-only ("fixed") rope memory. Looking for a noun to use with this, I found `N02 SPECIFY MACHINE ADDRESS (WHOLE)`. I didn't initially understand how to use this, but I googled `V27N02E` (the shorthand for pressing verb, 27, noun, 02, and hitting enter), and realized that the VirtualAGC website frontpage [shows how to do this](https://www.ibiblio.org/apollo/index.html#Playing_with_Colossus_); effectively, after keying `V27N02E`, you just enter an octal "machine address" and hit enter again; the machine address shows up on the third line of the DSKY and the word's value shows up on the first line in octal.
|
||||
|
||||
Machine addresses are just `bank*1024 + word` according to the VirtualAGC site; we can compute `27,3355`'s machine address easily as $27_8 \times 1024 + 3355_8 = 57355_8$. Keying a read for $57355_8$ and advancing with `V15E` (verb 15, INCREMENT MACHINE ADDRESS), we see the following values:
|
||||
```
|
||||
27,3355: 01333
|
||||
27,3356: 00075
|
||||
```
|
||||
|
||||
Time for a digression! How does the AGC represent fractional values? Thanks to VirtualAGC's [assembler manual](https://www.ibiblio.org/apollo/assembly_language_manual.html#Data_Representation), I don't have to trawl NASA PDFs. A single precision number is just 1's complement with the sign in the MSB; the magnitude is thought of as a fraction out of the maximum expressible magnitude. Locations storing numbers thus carry metadata in the assembler indicating the scaling required to fit the number into $[-1,1]$ and properly manipulate them doing when fixed-point math.
|
||||
|
||||
Double precision has the following layout:
|
||||
|
||||
![bit layout of double-precision number in AGC](double-precision.png)
|
||||
|
||||
The first word's value bits are higher-significance; the second word's are lower. Signs (`sn2`, `sn1`) are independent and can *cancel* for some awful reason, which thankfully wasn't relevant in this CTF. I threw some (miserable) code together using the Python library `bitstrings` to decode these, testing on the original `PI/16` values:
|
||||
```{.python}
|
||||
from bitstring import Bits, BitArray
|
||||
|
||||
TRUE_WORDS = [0o06220, 0o37553]
|
||||
|
||||
def sp(bits):
|
||||
sign = -1 if bits[0] else +1
|
||||
data = bits[1:].uint
|
||||
return sign * data/((1<<14) -1)
|
||||
|
||||
def dp(bits):
|
||||
sign1 = -1 if bits[0] else +1
|
||||
sign2 = -1 if bits[15] else +1
|
||||
|
||||
data1 = bits[1:15]
|
||||
data2 = bits[16:]
|
||||
|
||||
if sign1 == -1:
|
||||
data1 = ~data1
|
||||
if sign2 == -1:
|
||||
data2 = ~data2
|
||||
|
||||
d1 = (data1+Bits('0b00000000000000')).uint
|
||||
d2 = data2.uint
|
||||
return (d1*sign1+d2*sign2)/((1<<28) - 1)
|
||||
|
||||
print(dp(sum(BitArray(uint=w, length=15) for w in TRUE_WORDS))*16)
|
||||
```
|
||||
|
||||
This prints out `3.1415926931112734`, which is close enough to the `3.141592653` given in the listing for ~~government~~ ~~NASA~~ CTF work.
|
||||
However, the values of $1333_8$, $75_8$ we found where `PI/16` is supposed to be decode to $0.71387$: not at all what we want. Moreover, it looks like we've been visited by a haxor at these addresses.
|
||||
|
||||
Scanning further through core rope memory (with `V25E` to increment and repeated presses of `E` to increment further), I eventually stumbled upon two addresses which held different words every time I got a contest instance. They also happened to have magnitude similar to the original words for `PI/16`. Unfortunately, the contest is now down, and I don't remember the exact address, but the words I found right before I got the flag were $7440_8, 2122_8$; let's try decoding them!
|
||||
|
||||
```{.python}
|
||||
print(dp(sum(BitArray(uint=w, length=15) for w in [0o7440, 0o2122]))*16)
|
||||
```
|
||||
|
||||
This gives `3.781315936823621`; pasting this into the contest, we got the flag.
|
||||
|
||||
## Resources and other writeups
|
||||
- https://www.ibiblio.org/apollo/listings/Comanche051/TIME_OF_FREE_FALL.agc.html#50492F3136
|
||||
- https://www.ibiblio.org/apollo/CMC_data_cards_15_Fabrizio_Bernardini.pdf
|
||||
- https://www.ibiblio.org/apollo/index.html#Playing_with_Colossus_
|
||||
- https://www.ibiblio.org/apollo/Documents/Apollo15_Colossus3_CMC_Data_Cards.pdf
|
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="42" viewBox="0 0 800 42" class="WaveDrom" xmlns:xlink="http://www.w3.org/1999/xlink"><g transform="translate(0.5,0.5)" text-anchor="middle" font-size="14" font-family="sans-serif" font-weight="normal"><g transform="translate(4,14)"><g stroke="black" stroke-width="1" stroke-linecap="round"><line x2="791"></line><line y2="26"></line><line x2="791" y1="26" y2="26"></line><line x1="791" x2="791" y2="26"></line><line x1="765" x2="765" y2="3"></line><line x1="765" x2="765" y1="26" y2="23"></line><line x1="738" x2="738" y2="3"></line><line x1="738" x2="738" y1="26" y2="23"></line><line x1="712" x2="712" y2="3"></line><line x1="712" x2="712" y1="26" y2="23"></line><line x1="686" x2="686" y2="3"></line><line x1="686" x2="686" y1="26" y2="23"></line><line x1="659" x2="659" y2="3"></line><line x1="659" x2="659" y1="26" y2="23"></line><line x1="633" x2="633" y2="3"></line><line x1="633" x2="633" y1="26" y2="23"></line><line x1="606" x2="606" y2="3"></line><line x1="606" x2="606" y1="26" y2="23"></line><line x1="580" x2="580" y2="3"></line><line x1="580" x2="580" y1="26" y2="23"></line><line x1="554" x2="554" y2="3"></line><line x1="554" x2="554" y1="26" y2="23"></line><line x1="527" x2="527" y2="3"></line><line x1="527" x2="527" y1="26" y2="23"></line><line x1="501" x2="501" y2="3"></line><line x1="501" x2="501" y1="26" y2="23"></line><line x1="475" x2="475" y2="3"></line><line x1="475" x2="475" y1="26" y2="23"></line><line x1="448" x2="448" y2="3"></line><line x1="448" x2="448" y1="26" y2="23"></line><line x1="422" x2="422" y2="26"></line><line x1="396" x2="396" y2="26"></line><line x1="369" x2="369" y2="3"></line><line x1="369" x2="369" y1="26" y2="23"></line><line x1="343" x2="343" y2="3"></line><line x1="343" x2="343" y1="26" y2="23"></line><line x1="316" x2="316" y2="3"></line><line x1="316" x2="316" y1="26" y2="23"></line><line x1="290" x2="290" y2="3"></line><line x1="290" x2="290" y1="26" y2="23"></line><line x1="264" x2="264" y2="3"></line><line x1="264" x2="264" y1="26" y2="23"></line><line x1="237" x2="237" y2="3"></line><line x1="237" x2="237" y1="26" y2="23"></line><line x1="211" x2="211" y2="3"></line><line x1="211" x2="211" y1="26" y2="23"></line><line x1="185" x2="185" y2="3"></line><line x1="185" x2="185" y1="26" y2="23"></line><line x1="158" x2="158" y2="3"></line><line x1="158" x2="158" y1="26" y2="23"></line><line x1="132" x2="132" y2="3"></line><line x1="132" x2="132" y1="26" y2="23"></line><line x1="105" x2="105" y2="3"></line><line x1="105" x2="105" y1="26" y2="23"></line><line x1="79" x2="79" y2="3"></line><line x1="79" x2="79" y1="26" y2="23"></line><line x1="53" x2="53" y2="3"></line><line x1="53" x2="53" y1="26" y2="23"></line><line x1="26" x2="26" y2="26"></line></g><g><g></g><g transform="translate(13,-3)"><text x="765">0</text><text x="422">13</text><text x="396">14</text><text x="369">15</text><text x="26">28</text><text>29</text></g><g transform="translate(13,18)"><text x="593"><tspan>value_lsbs</tspan></text><text x="396"><tspan>sn1</tspan></text><text x="198"><tspan>value_msbs</tspan></text><text><tspan>sn2</tspan></text></g><g transform="translate(13,39)"></g></g></g></g></svg>
|
After Width: | Height: | Size: 3.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 78 KiB |
Loading…
Reference in New Issue