Read More

SDR Challenge at DEFCON!

 

As the title implies we’re going to DEFCON! We’ll have a booth again this year, so come check us out in the vendor area.

In honor of going to DEFCON, we would like to announce our the bladeRF SDR challenge which we will be hosting at our booth. As the following code snippets imply, winners will be award prizes.

bladeRF SDR challenge

 

Bob communicates a message to Alice every 3 minutes. Bob uses these messages to authenticate himself to Alice (via a mutually agreed upon PRN) and instruct Alice who she should send prizes to.

The message is a FSK modulate and Manchester encoded. Bob’s messages contain a packet ID and a pseudo random number (PRN), that is used to validate the authenticity of the packet. By ignoring any repeated packet ID and PRN pairs, Alice is able to protect against replay attacks. Both Alice and Bob have the same PRN generator so Alice knows what to expect from Bob. If the ID and PRN pairs match what Alice is expecting, and the prize field is non-zero, Alice will declare a winner and automatically award a prize.

The challenge lies in assuming the position of Eve and figuring out a way to use the modulation and/or coding scheme to come up with a way of leveraging Bob’s packet to trick Alice.

NOTE: Do not try bruteforcing the PRN, srand() will have a different seed at DEFCON.

 

Alice – the message receiving, prize dispensing program

#include <libbladeRF.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
 
#define NUM_SAMPLES (512)
 
#define NUM_XFERS 1
 
struct bladerf *dev = NULL ;
int16_t rx_samples[2*2*NUM_SAMPLES] ;
 
/***************************************************************************/
/* Moving average functions                                                */
/***************************************************************************/
 
struct ma {
    int num;
    int idx;
    int total;
    int *samps;
};
 
void ma_clear(struct ma *ma) {
    memset(ma->samps, 0, sizeof(int) * ma->num);
    ma->idx = 0;
    ma->total = 0;
}
 
int ma_create(struct ma *ma) {
    ma->samps = (int *)malloc(sizeof(int) * ma->num);
    if (ma->samps == NULL)
        return 1;
    ma_clear(ma);
 
    return 0;
}
 
int g = 0;
int moving_avg(struct ma *ma, int sample) {
    ma->total -= ma->samps[ma->idx];
    ma->samps[ma->idx] = sample;
    ma->idx++;
    ma->idx %= ma->num;
    ma->total += sample;
    return ma->total/ma->num;
}
 
/***************************************************************************/
/* Packet deserialization functions                                        */
/***************************************************************************/
 
void deserialize(unsigned char *ptr, char *ser_buf, int len) {
    int i, j, p;
    p = 0;
    len /= 8;
    for (i = 0; i < len; i++) {
        ptr[i] = 0;
        for (j = 0; j < 8; j++) {
            ptr[i] |= ((ser_buf[p++] == '1' ? 1 : 0) << (7 - j));
        }
    }
    ptr[i] = 0;
}
 
struct packet {
    unsigned int preamble;
    unsigned int id;
    unsigned int prn;
#define PRIZE_NONE    0
#define PRIZE_PAYPAL  1
#define PRIZE_BITCOIN 2
    int prize;
    char recipient[40];
};
 
#define PACKET_SZ sizeof(struct packet)
 
/***************************************************************************/
/* Packet decoding and modulation functions                                */
/***************************************************************************/
 
char demod[1000];
char demanchester[500];
unsigned int pkt_id = 0;
unsigned int pkt_prn = 0;
unsigned int victory_time = 0;
void analyze() {
    struct packet pkt;
    int len;
    int i, j;
    int word;
    int good;
 
    len = strlen(demod);
    printf("len: %d\n", len);
    printf("raw: %s\n", demod);
 
    good = 1;
 
    printf("Manchester:\n");
    j = 0;
    for (i = 0; i < len; i+=2) {
        printf("%c%c", demod[i], (demod[i] == demod[i+1]) ? 'X' : '.');
        /* Manchester decoding found an error bit */
        if (demod[i] == demod[i+1])
            good = 0;
        demanchester[j++] = demod[i];
    }
    printf("\n");
 
    printf("Demanchester:\n");
    for (i = 0; i < len; i+=2) {
        printf("%c", demod[i]);
    }
    printf("\n");
 
    printf("Grouped binary:\n");
    for (i = 0; i < len; i+=2) {
        if (((len - i) % 16) == 0) printf(" ");
        printf("%c", demod[i]);
    }
    printf("\n");
 
    printf("Grouped binary in hex:\n");
    word = 0;
    for (i = 0; i < len; i+=2) {
        if (((len - i) % 16) == 0) {
            printf(" %.2x", word);
            word = 0;
        }
        word = (word<<1) | (demod[i] - '0');
    }
    printf("\n");
 
    /* The packet passed Manchester decoding, so deserialize it */
    if (good) {
        deserialize((unsigned char *)&pkt, demanchester, sizeof(pkt));
        /* Make sure no one is replaying a previous transmission */
        if (pkt.id == pkt_id && pkt.prn == pkt_prn) {
            /* Enforce 5 minutes between victories */
            if (victory_time + 5 * 60 < time(NULL)) {
                pkt_id++;
                pkt_prn = rand();
                if (pkt.prize != PRIZE_NONE) {
                    victory_time = time(NULL);
                    printf("WINNER! Good packet from Bob:");
                } else {
                    printf("Good packet received from Bob:");
                }
            } else {
                printf("Victory timer hasn't yet elapsed: ");
            }
            printf("id(%d) prn(%d) prize(%d) recipient(%s)\n",
                    pkt.id, pkt.prn, pkt.prize, pkt.recipient);
        }
    }
}
 
int main(int argc, char *argv[])
{
   int status;
   unsigned int actual;
   struct bladerf_rational_rate rational_actual, samplerate = { 0, 1500000, 1 } ;
 
   int lock, cnt;
   int i, j, idx, pwr, ma;
 
   int dc_i, dc_q;
 
   struct ma detect;
   struct ma phase;
   int falling;
   int sp;
   int done;
   int demodidx;
   float phi, prev_phi, dphi_dt;
 
 
   /* Increase verbosity */
   bladerf_log_set_verbosity(BLADERF_LOG_LEVEL_DEBUG);
 
   /* Open the device */
   status = bladerf_open(&dev, "") ;
   if( status < 0 ) {
       fprintf( stderr, "bladerf_open() error: %s\n", bladerf_strerror(status) );
       goto close_device;
   }
 
   /* Set the frequency */
   status = bladerf_set_frequency(dev, BLADERF_MODULE_RX, 314500000) ;
   if( status < 0 ) {
       fprintf( stderr, "bladerf_set_frequency() RX error: %s\n", bladerf_strerror(status) );
       goto close_device;
   }
 
   /* Set the bandwidth */
   status = bladerf_set_bandwidth(dev, BLADERF_MODULE_RX, 1500000, &actual) ;
   if( status < 0 ) {
       fprintf( stderr, "bladerf_set_bandwidth() RX error: %s\n", bladerf_strerror(status) );
       goto close_device;
   }
 
   /* Set the samplerate */
 
   status = bladerf_set_rational_sample_rate(dev, BLADERF_MODULE_RX, &samplerate, &rational_actual) ;
   if( status < 0 ) {
       fprintf( stderr, "bladerf_set_sample_rate() RX error: %s\n", bladerf_strerror(status) );
       goto close_device;
   }
   bladerf_set_lna_gain(dev, BLADERF_LNA_GAIN_MAX);
   bladerf_set_rxvga1(dev, BLADERF_RXVGA1_GAIN_MAX);
   bladerf_set_rxvga2(dev, 6);
 
   /* Configure the sync stream */
   status = bladerf_sync_config(
       dev,
       BLADERF_MODULE_RX,
       BLADERF_FORMAT_SC16_Q11,
       128,
       2048,
       NUM_XFERS,
       2000);
   if( status < 0 ) {
       fprintf( stderr, "bladerf_sync_config() RX error: %s\n", bladerf_strerror(status) ) ;
   }
 
   status = bladerf_enable_module(dev, BLADERF_MODULE_RX, true) ;
   if( status < 0 ) {
       fprintf( stderr, "bladerf_enable_module() RX error: %s\n", bladerf_strerror(status) );
       goto close_device;
   }
 
 
   /* Determine DC offset to increase dynamic range and mitigate effects of DC offset */
   dc_i = dc_q = 0;
   status = bladerf_sync_rx(dev, (void *)rx_samples, NUM_SAMPLES, NULL, 1000);
   idx = 0;
   for (i = 0; i < NUM_SAMPLES; i++, idx+=2) {
       dc_i += rx_samples[idx];
       dc_q += rx_samples[idx+1];
   }
 
   dc_i /= NUM_SAMPLES;
   dc_q /= NUM_SAMPLES;
 
   /* Detection moving average is 100 samples long */
   detect.num = 100;
   ma_create(&detect);
   /* Demoulation moving average is 35 samples long */
   phase.num = 35;
   ma_create(&phase);
 
   cnt = 0;
 
   // prep the PRNG
   srand(0x1337BEEF);
   // skip ahead
   for (i = 0; i < 1337; i++)
       rand();
 
   pkt_prn = rand();
   lock=0;
   while(1) {
       status = bladerf_sync_rx(dev, (void *)rx_samples, NUM_SAMPLES, NULL, 1000);
       idx = 0;
       for (i = 0; i < NUM_SAMPLES; i++, idx += 2) {
           /* Account for DC offset */
           rx_samples[idx]   -= dc_i;
           rx_samples[idx+1] -= dc_q;
           cnt++;
 
           if (lock == 0) {
               /* Currently not receiving a packet, look for an signal whose
                * moving averaged energy goes abouve "10000" */
 
               /* the energy of the current sample || I + j * Q || is given by:
                * energy = sqrt(I * I + Q * Q) */
               ma = moving_avg(&detect, rx_samples[idx] * rx_samples[idx] + rx_samples[idx+1] * rx_samples[idx+1]);
 
               if (ma > 10000) {
                   /* Strong energy signal detected start looking for its
                    * falling edge then start demodulation */
                   prev_phi = 0;
 
                   /* Ensure that no other packet is accidentally decoded for
                    * another 350000 samples */
                   lock = 350000;
                   ma_clear(&detect);
                   ma_clear(&phase);
                   cnt = 0;
                   falling = 0;
                   done = 0;
                   demodidx = 0;
               }
           } else {
               lock--;
 
               /* If the packet has been decoded wait a few more samples to
                * ensure that ISI and transmitter anomalies don't accidentally
                * trigger another packet detection */
               if (done) continue;
 
               /* Find the angle of the IQ sample by computing tan^-1(I/Q) */
               phi = atan2f(rx_samples[idx], rx_samples[idx+1]);
 
               /* Unwind the phasor */
               dphi_dt = prev_phi - phi;
               if (dphi_dt < - M_PI)
                   dphi_dt += 2 * M_PI;
               else if (dphi_dt > M_PI)
                   dphi_dt -= 2 * M_PI;
               prev_phi = phi;
 
               ma = moving_avg(&phase, 1000 * dphi_dt);
 
               /* `falling` is used to find the falling edge of the first long
                * synchronization pulse */
               if (falling == 0 && ma < 2100 && cnt > 100) {
                   falling = 1;
                   /* the first symbol is sampled 187 samples after the falling 
                    * edge */
                   sp = 750/4;
               }
 
               if (falling) {
                   if (sp == 0) {
                       /* Demodulate 2000+/-100 radians/s as '1'
                        * demodulate 2200+/-100 radians/s as '0'
                        * detect anything else as an end of packet */
                       if (ma > 1900 && ma < 2100)
                           demod[demodidx++] = '1';
                       else if (ma > 2100 && ma < 2300)
                           demod[demodidx++] = '0';
                       else {
                           /* As previously described, the modem has detected an
                            * invalid symbol so it means the packet may have
                            * ended, so analyze the packet */
                           demod[demodidx++] = 0;
                           analyze();
                           done = 1;
                       }
 
                       /* Safety check */
                       if (demodidx >= 1000)
                           demodidx = 0;
 
                       /* Reload the sampling countdown variable */
                       sp = 750/2;
                   } else {
                       /* Continue counting down the sampling time variable */
                       sp--;
                   }
               }
 
           }
       }
   }
 
close_device:
   bladerf_close(dev) ;
   return status ;
}

Bob – the program responsible for sending authenticated messages to Alice every 5 minutes.

#include <libbladeRF.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <iso646.h>
 
#define NUM_SAMPLES (1024)
#define NUM_XFERS 1
 
void serialize(char *ser_buf, unsigned char *ptr, size_t len) {
    int i, j, p;
    p = 0;
    for (i = 0; i < len; i++) {
        for (j = 0; j < 8; j++) {
            ser_buf[p++] = '0' + ((ptr[i] >> (7 - j)) & 1);
        }
    }
    ser_buf[p] = 0;
}
 
struct bladerf *dev = NULL;
 
struct packet {
    unsigned int preamble;
    unsigned int id;
    unsigned int prn;
#define PRIZE_NONE    0
#define PRIZE_PAYPAL  1
#define PRIZE_BITCOIN 2
    int prize;
    char recipient[40];
};
 
#define PACKET_SZ sizeof(struct packet)
 
int send_pkt(struct packet *pkt) {
    float theta;
    int16_t *tx_samples;
    int num_tx_samps;
    int msg_len;
    int samp_idx;
    int status;
    int i, j;
    char msg[(PACKET_SZ * 16) + 1];
 
    msg_len = PACKET_SZ * 8;
 
    // convert from packed binary to ASCII binary representation
    serialize(msg, (unsigned char *)pkt, PACKET_SZ);
    printf("Pre-Machester: (%d) %s\n", (int)strlen(msg), msg);
 
    // do the manchester encoding
    for (i = (PACKET_SZ * 8) - 1; i >= 0; i--) {
        msg[2 * i] = msg[i];
        msg[2 * i + 1] = (msg[i] == '1') ? '0' : '1';
    }
    // message is now 2x as long!
    msg_len *= 2;
 
    // print out the machester encoded string
    msg[PACKET_SZ * 16] = 0;
    printf("Machester: (%d) %s\n", (int)strlen(msg), msg);
 
    // do the FSK modulation
    theta = 0;
    samp_idx = 0;
 
    num_tx_samps = (3500 + (msg_len * 750/2) + (NUM_SAMPLES - 1) ) & ~(NUM_SAMPLES - 1);
    tx_samples = (int16_t *)malloc(sizeof(int16_t) * 2 * num_tx_samps);
    memset(tx_samples, 0, sizeof(int16_t) * 2 * num_tx_samps);
 
    for (i = 0; i < 350 * 10; i++) {
        theta += 2200.0f/1500000.0f;
        tx_samples[samp_idx++] = 2000*sin(theta); // I sample
        tx_samples[samp_idx++] = 2000*cos(theta); // Q sample
    }
 
    for (i = 0; i < msg_len;  i++) {
        for (j = 0; j < (750/2); j++) {
            // do the actual modulation by figuring out the rate of change of
            // theta based on the bit
            theta += 2 * MP_I * (msg[i] == '1' ? 2200.0f : 2000.0f)/1.5e6f;
 
            tx_samples[samp_idx++] = sin(theta); // I sample
            tx_samples[samp_idx++] = cos(theta); // Q sample
 
            // sines and cosines are cyclic
            if (theta > 2 * M_PI)
                theta -= 2 * M_PI;
            if (theta < 2 * M_PI)
                theta += 2 * M_PI;
        }
    }
 
    status = bladerf_sync_tx(dev, (void *)tx_samples, num_tx_samps, NULL, 1000);
    free(tx_samples);
    return status;
}
 
int main(int argc, char *argv[])
{
    int status;
    unsigned int actual;
    struct bladerf_rational_rate rational_actual, samplerate = { 0, 1500000, 1 } ;
    struct packet pkt;
    int pkt_id, i;
 
    /* Increase verbosity */
    bladerf_log_set_verbosity(BLADERF_LOG_LEVEL_DEBUG);
 
    /* Open the device */
    status = bladerf_open(&dev, NULL);
    if( status < 0 ) {
        fprintf( stderr, "bladerf_open() error: %s\n", bladerf_strerror(status) );
        goto close_device;
    }
 
    /* Set the frequency */
    status = bladerf_set_frequency(dev, BLADERF_MODULE_TX, 314500000) ;
    if( status < 0 ) {
        fprintf( stderr, "bladerf_set_frequency() RX error: %s\n", bladerf_strerror(status) );
        goto close_device;
    }
 
    /* Set the bandwidth */
    status = bladerf_set_bandwidth(dev, BLADERF_MODULE_TX, 1500000, &actual) ;
    if( status < 0 ) {
        fprintf( stderr, "bladerf_set_bandwidth() RX error: %s\n", bladerf_strerror(status) );
        goto close_device;
    }
 
    /* Set the samplerate */
    status = bladerf_set_rational_sample_rate(dev, BLADERF_MODULE_TX, &samplerate, &rational_actual) ;
    if( status < 0 ) {
        fprintf( stderr, "bladerf_set_sample_rate() RX error: %s\n", bladerf_strerror(status) );
        goto close_device;
    }
 
    /* Max out the gain */
    status = bladerf_set_txvga2(dev, BLADERF_TXVGA2_GAIN_MAX);
    if (status < 0)
        return status;
 
    status = bladerf_set_txvga1(dev, BLADERF_TXVGA1_GAIN_MAX);
    if (status < 0)
        return status;
 
    /* Configure the sync stream */
    status = bladerf_sync_config(
            dev,
            BLADERF_MODULE_TX,
            BLADERF_FORMAT_SC16_Q11,
            128,
            2048,
            NUM_XFERS,
            2000);
    if( status < 0 ) {
        fprintf( stderr, "bladerf_sync_config() RX error: %s\n", bladerf_strerror(status) ) ;
    }
 
    status = bladerf_enable_module(dev, BLADERF_MODULE_TX, true) ;
    if( status < 0 ) {
        fprintf( stderr, "bladerf_enable_module() RX error: %s\n", bladerf_strerror(status) );
        goto close_device;
    }
 
    // prep the PRNG
    srand(0x1337BEEF);
    // skip ahead
    for (i = 0; i < 1337; i++)
        rand();
 
    pkt_id = 0;
    while (1) {
        memset(&pkt, 0, sizeof(pkt));
        pkt.preamble = 0xffffffff;
        pkt.id = pkt_id++;
        pkt.prn = rand();
        pkt.prize = PRIZE_NONE;
 
        send_pkt(&pkt);
 
        sleep(3 * 60);
    }
 
close_device:
    bladerf_close(dev);
    return status;
}