“Cadence in the Cord” was a simple challenge from HTB’s Cyber Apocalypse CTF in 2026.
This was part of the “hardware” category and the challenge started with a “capture.sr” file.

This could be opened with PulseView (or other things. This is just what I already had installed) and it contained a single signal.

The original signal

There is a single signal so the number of protocols this could correspond to is also quite low.
I started with the most common one: UART.
I added a decoder, set the RX (receive) line to D1 (the original signal) and then tried common baud rates like 4800 and 9600. With 4800 the data didn’t look quite right.
However, with 9600 it looked like I could see one byte being transmitted at a time.

UART data

By putting the first 2 hex values in cyberchef and using “From hex” I got “To”.
Then by extending this to the next few values I got “To the”. This is probably correct and I should export all the data.

For that, I used the “Export all annotations for this row” function from PulseView.

Exporting the data

The data looked like this

4001195-4001404 UART: RX data: Start bit
4001403-4003071 UART: RX data: 54
4003070-4003279 UART: RX data: Stop bit
4007289-4007498 UART: RX data: Start bit
4007497-4009165 UART: RX data: 6F
4009164-4009373 UART: RX data: Stop bit
4029387-4029596 UART: RX data: Start bit
4029595-4031263 UART: RX data: 20
4031262-4031471 UART: RX data: Stop bit
4051485-4051694 UART: RX data: Start bit
4051693-4053361 UART: RX data: 74
4053360-4053569 UART: RX data: Stop bit

I used the following bash command in order to retrieve only the lines that did not contain ‘Stop bit’ or ‘Start bit’ (that did not contain word ‘bit’, actually).
And then I used awk to print the last part of the line: the hex value I was after.
I could’ve probably done this only using awk if I wasn’t so lazy.

cat uart_data.txt | grep --invert-match "bit" | awk '{print $5}'

The result looked like this (I replaced the newlines with spaces):

54 6F 20 74 68 65 20 62 75 79 65 72 20 77 68 6F 20 70 61 69 64 20 69 6E 20 73 65 63 72 65 74 73 3A 20 77 68 61 74 20 66 6F 6C 6C 6F 77 73 20 69 73 20 74 68 65 20 70 6C 65 61 73 61 6E 74 20 74 6F 6E 65 2C 20 74 68 65 20 67 6F 6F 64 73 20 49 20 73 65 6C 6C 20 69 6E 20 64 61 79 6C 69 67 68 74 20 61 6E 64 20 6E 65 76 65 72 20 6D 69 73 73 2E 20 4C 6F 72 64 20 56 61 72 6F 27 73 20 64 65 62 74 2C 20 64 75 65 20 61 74 20 74 68 65 20 73 65 63 6F 6E 64 20 74 68 61 77 2C 20 79 6F 75 72 73 20 66 6F 72 20 61 20 6D 61 72 72 69 61 67 65 20 79 6F 75 20 61 6C 72 65 61 64 79 20 6F 77 6E 2E 20 54 68 65 20 48 61 72 6C 6F 77 20 69 6E 68 65 72 69 74 61 6E 63 65 2C 20 63 6F 6E 74 65 73 74 65 64 20 62 79 20 61 20 63 6F 75 73 69 6E 20 77 68 6F 73 65 20 77 69 74 6E 65 73 73 65 73 20 49 20 61 72 72 61 6E 67 65 64 2E 20 54 61 6B 65 20 74 68 65 6D 20 61 6E 64 20 74 68 61 6E 6B 20 6D 65 2E 20 42 75 74 20 77 68 61 74 20 69 73 20 77 72 69 74 74 65 6E 20 69 73 20 77 6F 72 74 68 20 6E 6F 74 68 69 6E 67 2E 20 54 68 65 20 64 72 61 67 6F 6E 27 73 20 74 72 75 65 20 6E 6F 74 65 20 64 6F 65 73 20 6E 6F 74 20 6C 69 76 65 20 69 6E 20 74 68 65 20 77 6F 72 64 73 3B 20 69 74 20 6C 69 76 65 73 20 69 6E 20 74 68 65 20 72 65 73 74 73 20 62 65 74 77 65 65 6E 20 74 68 65 6D 2E 20 41 20 6C 6F 6E 67 20 72 65 73 74 20 72 61 69 73 65 73 20 74 68 65 20 6D 61 72 6B 20 74 6F 20 6F 6E 65 2C 20 61 20 73 68 6F 72 74 20 72 65 73 74 20 6C 65 74 73 20 69 74 20 66 61 6C 6C 20 74 6F 20 6E 6F 74 68 69 6E 67 3B 20 63 6F 75 6E 74 20 65 69 67 68 74 20 72 65 73 74 73 20 74 6F 20 65 76 65 72 79 20 6C 65 74 74 65 72 20 62 65 66 6F 72 65 20 74 68 65 20 6E 6F 74 65 20 77 69 6C 6C 20 73 70 65 61 6B 2E 20 52 65 61 64 20 74 68 65 20 73 69 6C 65 6E 63 65 2C 20 6E 6F 74 20 74 68 65 20 73 6F 6E 67 2C 20 61 6E 64 20 70 61 79 2

Which I could decode using CyberChef:

To the buyer who paid in secrets: what follows is the pleasant tone, the goods I sell in daylight and never miss. Lord Varo's debt, due at the second thaw, yours for a marriage you already own. The Harlow inheritance, contested by a cousin whose witnesses I arranged. Take them and thank me. But what is written is worth nothing. The dragon's true note does not live in the words; it lives in the rests between them. A long rest raises the mark to one, a short rest lets it fall to nothing; count eight rests to every letter before the note will speak. Read the silence, not the song, and pay

This is not the flag, but it’s a clear hint on how to get the flag.
I have to look at the spaces between the bytes that are being transmitted over UART: a small space is a binary 0 and a long space is a binary 1.
Something like this: The hidden message

Fortunately, those annoying lines that announced the start & end bits will become useful now.
I can look for two consecutive lines that end in “Stop bit” and “Start bit” and then use the difference between their timestamps.

I wrote this script to decode the message based on those lines:

import binascii

UART_DATA = './uart_data.txt'

def extract_timestamp(raw_line:str) -> int:
    # We know how the lines look like so we'll use split to get the data we need

    # First, split by space and take the first part. This is where the timestamps are
    timestamp_data = raw_line.split(' ')[0]

    # We only want the first bit. The difference between the small & big interval is quite large anyway
    timestamp_str = timestamp_data.split('-')[0]

    # Convert to int and return
    return int(timestamp_str)


def main():
    f = open(UART_DATA, 'r')

    last_line = None
    current_line = None

    result = []

    for line in f:
        line = line.strip()
        last_line = current_line
        current_line = line

        # both are valid/non-empty lines
        if last_line and current_line:
            # We want to compute the time difference between two transmissions (so between a stop & a start)
            if last_line.endswith('Stop bit') and current_line.endswith('Start bit'):
                ts1 = extract_timestamp(last_line)
                ts2 = extract_timestamp(current_line)
                dif = ts2 - ts1
                result.append(dif)

    f.close()
    print(f'Obtained {len(result)} intervals')
    print('Analyzing...')


    # The data is actually quite easy to interpret: the small interval is around 4220 and the big one is 20220
    # Bit I wanted to be fancy & computed the average value then used that
    avg = int(sum(result) / len(result))
    print(f'Average of the interval values: {avg}')

    message_binary = []
    for index, value in enumerate(result):
        if value < avg:
            # Small interval => 0
            message_binary.append('0')
        else:
            # Large interval => 1
            message_binary.append('1')
        # Every 8th character, add a space
        if index % 8 == 7:
            message_binary.append(' ')

    print('Decoded message (binary):')
    print(''.join(message_binary))

    message_ascii = []
    binary_chars = ''.join(message_binary).split(' ')
    for character in binary_chars:
        # There's a space at the end that we want to ignore
        if character:
            decoded = binascii.unhexlify('%x' % int(character, 2))
            message_ascii.append(decoded.decode('utf-8'))

    print('Decoded message (ascii):')
    print(''.join(message_ascii))


if __name__=='__main__':
    main()

And by running it I got the final message and the flag. Flag retrieved