I recently attended Simulations Arcade Hack CTF! It was an individual competition that was designed for beginners, but I wanted some challenges so I decided to play. I managed to get first blood on this challenge and was also the only participant who succefully solved it. It boiled down to getting a copy of the .wav file with rtmpdump and viewing the waveform to see that it is a binary encoding.

Listen Up! (1 solve, 100 points)

Description

Cerby seems to like listening to radio from his website a lot, but we cannot figure out why. Do you have any idea what he is up to?

Notes: Don’t take everything at surface value. Maybe the listening doesn’t matter either. Also save this challenge for last to save you trouble :)

Solution

So the context of this challenge is a follow up to one of the previous OSINT challenges in the competition. In that challenge you find Cerby’s github account and look at the commit history to find the flag. There is another tidbit of information in the commit history though. Cerby makes a change to the README.md file that says new site: cerby.space Upon visiting this site there is a simple message displayed: rtmp://64.227.11.108/live/numbers

I had not seen this type of address before so I looked up rtmp on google and visited this wikipedia article. Browsing through the article we find the software implementations section which reads The open-source RTMP client command-line tool rtmpdump is designed to play back or save to disk the full RTMP stream, including the RTMPE protocol Adobe uses for encryption.

Great! This sounds perfect! With sudo apt install rtmpdump we can get this on our ubuntu VM. Next I ran rtmpdump -h to get the usage message:

RTMPDump v2.4

(c) 2010 Andrej Stepanchuk, Howard Chu, The Flvstreamer Team; license: GPL



rtmpdump: This program dumps the media content streamed over RTMP.



--help|-h               Prints this help screen.

--url|-i url            URL with options included (e.g. rtmp://host[:port]/path swfUrl=url tcUrl=url)

--rtmp|-r url           URL (e.g. rtmp://host[:port]/path)

--host|-n hostname      Overrides the hostname in the rtmp url

--port|-c port          Overrides the port in the rtmp url

--socks|-S host:port    Use the specified SOCKS proxy

--protocol|-l num       Overrides the protocol in the rtmp url (0 - RTMP, 2 - RTMPE)

--playpath|-y path      Overrides the playpath parsed from rtmp url

--playlist|-Y           Set playlist before playing

--swfUrl|-s url         URL to player swf file

--tcUrl|-t url          URL to played stream (default: "rtmp://host[:port]/app")

--pageUrl|-p url        Web URL of played programme

--app|-a app            Name of target app on server

--swfhash|-w hexstring  SHA256 hash of the decompressed SWF file (32 bytes)

--swfsize|-x num        Size of the decompressed SWF file, required for SWFVerification

--swfVfy|-W url         URL to player swf file, compute hash/size automatically

--swfAge|-X days        Number of days to use cached SWF hash before refreshing

--auth|-u string        Authentication string to be appended to the connect string

--conn|-C type:data     Arbitrary AMF data to be appended to the connect string

                        B:boolean(0|1), S:string, N:number, O:object-flag(0|1),

                        Z:(null), NB:name:boolean, NS:name:string, NN:name:number

--flashVer|-f string    Flash version string (default: "LNX 10,0,32,18")

--live|-v               Save a live stream, no --resume (seeking) of live streams possible

--subscribe|-d string   Stream name to subscribe to (otherwise defaults to playpath if live is specifed)

--realtime|-R           Don't attempt to speed up download via the Pause/Unpause BUFX hack

--flv|-o string         FLV output file name, if the file name is - print stream to stdout

--resume|-e             Resume a partial RTMP download

--timeout|-m num        Timeout connection num seconds (default: 30)

--start|-A num          Start at num seconds into stream (not valid when using --live)

--stop|-B num           Stop at num seconds into stream

--token|-T key          Key for SecureToken response

--jtv|-j JSON           Authentication token for Justin.tv legacy servers

--hashes|-#             Display progress with hashes, not with the byte counter

--buffer|-b             Buffer time in milliseconds (default: 36000000)

--skip|-k num           Skip num keyframes when looking for last keyframe to resume from. Useful if resume fails (default: 0)



--quiet|-q              Suppresses all command output.

--verbose|-V            Verbose command output.

--debug|-z              Debug level command output.

If you don't pass parameters for swfUrl, pageUrl, or auth these properties will not be included in the connect packet.

This line seems to match what we need:

--rtmp|-r url           URL (e.g. rtmp://host[:port]/path)

Experimenting with the command I could see that it just dumps the recieved bytes to stdout. I redirected the output to a file and saved it as flag.wav. This makes the final command rtmpdump -r rtmp://64.227.11.108/live/numbers > ./flag.wav. I let this run until it momentarily stopped recieving input which I assumed was the end of the transmition. This assumption was correct.

I played the file and didn’t really hear anything. As is typical with audio steg challenges, the next step is to open the file in a waveform viewer for more carefull analysis. I use audacity for this type of thing. This is what we get: image

It doesn’t look like much at first but it’s always a good idea to zoom in to look at the details of the wave form: image

This tells a different story. I noticed some key things about this waveform:

  • There are two different audio pulses
    • Short
    • Tall
  • There is a small gap in between groups
  • These pulses are in groups of 8

This all fits with binary encoding! For those unfamiliar, characters in the computer are represented in memory using sets of binary bits (1s and 0s). There are 256 different characters that can be represented this way, and because of this, we need a set of 8 bits to represent one character. For example, the A character is 01000001 in binary. If we enterpret the small pulses as 0’s and the big ones as 1’s, then we get sets of 8 bits, each encoding a character. Starting from left to right we get 01010011 01000001 01001000. This just so happens to translate to SAH which is our flag prefix!

I use CyberChef for quick online encoding and decoding like this so typing the rest of our binary data there gives us the flag! image

Flag

SAH{SONIC_SECRETS}