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:
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:
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!
Flag
SAH{SONIC_SECRETS}