Skip to main content
  1. Posts/

Tornado Writeup

·836 words·4 mins
Writeups UIUCTF 2023 ctf writeup

Tornado was a misc challenge from UIUCTF 2023 that involved a singular wav file with the hint that it was encoded using SAME encoding. If you want to learn more about this encoding, you can read some more here, but essentially it boils down to being the way that emergency weather alerts are sent within the US (and other some other countries as well).

Upon doing some research on how to actually read this file, I came across sameold which contains a SAME decoder, samedec, written in rust, meant to be used with a Raspberry Pi or PC to receive these emergency alerts. It took a little bit of trial and error to realize that there is a specific way to read in wav files:

sox 'Same.wav' -t raw -r 22.05k -e signed -b 16 -c 1 - | samedec -r 22050

I probably would’ve figured this out a lot sooner if I actually read the README. Anyways, if you’re on Kali (or probably most distros) you can install sox with a quick:

apt install sox

If you run samedec with no flags, it will just figure out the actual SAME message:

ZCZC-WXR-TOR-018007+0045-0910115-KIND/NWS-

Which is not what we want. (Fun fact: the message is a tornado warning for Benton County Indiana from April 1st, effective from 1:15AM to 2:00AM UTC)

So instead, we add the -v flag to get more info and it will actually give us the bits we need to decode the flag:

/home/kali/CTF/UIUCTF23/misc/tornado [git::uiuctf23 *] [kali@kali] [18:54]
> sox warning.wav -t raw -r 22.5k -e signed -b 16 -c 1 - | ./samedec-x86_64-unknown-linux-gnu -r 22050 -v
 INFO  samedec > SAME decoder reading standard input
 INFO  sameold::receiver > [1400          ]: event [link]: searching
 INFO  sameold::receiver::framing > burst: started: after 20 bytes
 INFO  sameold::receiver          > [7992          ]: event [link]: reading
 INFO  sameold::receiver          > [22467         ]: event [link]: decoded burst: "ZCZC-UXU-TFR-R18007ST_45-0910BR5-KIND3RWS-"
 INFO  sameold::receiver          > [22467         ]: event [transport]: assembling
 INFO  sameold::receiver          > [22488         ]: event [link]: no carrier
 INFO  sameold::receiver          > [44369         ]: event [link]: searching
 INFO  sameold::receiver::framing > burst: started: after 19 bytes
 INFO  sameold::receiver          > [50614         ]: event [link]: reading
 INFO  sameold::receiver          > [65087         ]: event [link]: decoded burst: "ZCZC-WIR-TO{3018W0R+00T5-09UT115-K_EV/NWS-"
 INFO  sameold::receiver          > [65108         ]: event [link]: no carrier
 INFO  sameold::receiver          > [86644         ]: event [link]: searching
 INFO  sameold::receiver::framing > burst: started: after 20 bytes
 INFO  sameold::receiver          > [93236         ]: event [link]: reading
 INFO  sameold::receiver          > [107708        ]: event [link]: decoded burst: "ZCZC-WXRCTOR-0D_007+004OR_O1011E@KIND/N}S-"
 INFO  sameold::receiver          > [107729        ]: event [link]: no carrier
 INFO  sameold::receiver          > [136628        ]: event [transport]: message: (100.0% voting, 120 errors) "ZCZC-WXR-TOR-018007+0045-0910115-KIND/NWS-"
ZCZC-WXR-TOR-018007+0045-0910115-KIND/NWS-
 INFO  sameold::receiver          > [136671        ]: event [transport]: assembling
 INFO  sameold::receiver          > [347741        ]: event [transport]: idle
 INFO  sameold::receiver          > [2006035       ]: event [link]: searching
 INFO  sameold::receiver::framing > burst: started: after 20 bytes
 INFO  sameold::receiver          > [2012622       ]: event [link]: reading
 INFO  sameold::receiver          > [2013916       ]: event [link]: decoded burst: "NNNNfff"
 INFO  sameold::receiver          > [2013916       ]: event [transport]: message: (0.0% voting, 0 errors) "NNNN"
NNNN
 INFO  sameold::receiver          > [2013937       ]: event [link]: no carrier
 INFO  sameold::receiver          > [2013937       ]: event [transport]: assembling
 INFO  sameold::receiver          > [2035822       ]: event [link]: searching
 INFO  sameold::receiver::framing > burst: started: after 19 bytes
 INFO  sameold::receiver          > [2042061       ]: event [link]: reading
 INFO  sameold::receiver          > [2043355       ]: event [link]: decoded burst: "NNNNfff"
 INFO  sameold::receiver          > [2043377       ]: event [link]: no carrier
 INFO  sameold::receiver          > [2064914       ]: event [link]: searching
 INFO  sameold::receiver::framing > burst: started: after 20 bytes
 INFO  sameold::receiver          > [2071503       ]: event [link]: reading
 INFO  sameold::receiver          > [2072798       ]: event [link]: decoded burst: "NNNNfff"
 INFO  sameold::receiver          > [2072819       ]: event [link]: no carrier

That gives us three different messages:

ZCZC-UXU-TFR-R18007ST_45-0910BR5-KIND3RWS-
ZCZC-WIR-TO{3018W0R+00T5-09UT115-K_EV/NWS-
ZCZC-WXRCTOR-0D_007+004OR_O1011E@KIND/N}S-

If you look closely, you can probably already see the flag, but the trick is to grab the unique character at each position out of the three strings.

I wrote this quick python script to recover it:

a = "ZCZC-UXU-TFR-R18007ST_45-0910BR5-KIND3RWS-"
b = "ZCZC-WIR-TO{3018W0R+00T5-09UT115-K_EV/NWS-"
c = "ZCZC-WXRCTOR-0D_007+004OR_O1011E@KIND/N}S-"
d = ""

for x in range(len(a)):
    # Grab the character at this position from all three strings
    chars = [a[x], b[x], c[x]]
    # Convert the list to a set which will filter out duplicates
    # and then back to a list so we can use indices to access it
    uniq = list(set(chars))
    # If there is only one uniq character then add it
    if len(uniq) == 1:
        d += uniq[0]
        continue
    # Check how many times the first item in the list appears in the
    # three strings, if it appears once then its the unique character
    if chars.count(uniq[0]) == 1:
        d += uniq[0]
    # If it appears more than once than the other character is the unique one
    else:
        d += uniq[1]

print(d)

And we get the flag:

ZCZC-UIUCTF{3RD_W0RST_TOR_OUTBRE@K_EV3R}S-

I enjoyed this challenge and while the solution is simple to explain, it took quite a bit of time to find the correct tool and figure out how to actually get the flag. Let me know in the comments if you found a better way to do it or if you have something to add!