Tags: misc 

Rating: 3.0

You're given a picture of a Lego Mindstorms EV3 display block with the flag partially displayed on it. You're also given a macOS PacketLogger file.

If you open the PacketLogger file, you'll find a capture of packets as they were being sent back and forth to the device. You can export that to a format that's easier to write a parser for via File -> Export -> Payload as Text...

Once you do that, you can use the EV3 developer kit documentation to find the specifications for the protocol used. To get the flag, you only need to parse outgoing packets, and you only really need to pay attention to one thing: the TEXT command of the opUI_DRAW instruction.

We can write a crude Go parser like so:

package main

import (
    "bufio"
    "encoding/binary"
    "encoding/hex"
    "fmt"
    "os"
    "sort"
    "strings"
)

func parseParameter(b *[]byte) interface{} {
    switch (*b)[0] & 0xc0 {
    case 0:
        // short constant
        var ret int
        if (*b)[0]&0x20 == 0 {
            // positive
            ret = int((*b)[0] & 0x1f)
        } else {
            // negative
            ret = -int((*b)[0] & 0x1f)
        }
        *b = (*b)[1:]
        return ret
    case 0x80:
        // long constant
        if (*b)[0]&0xf0 == 0x80 {
            // value
            switch (*b)[0] & 0x07 {
            case 0, 4:
                // null-terminated
                s := ""
                *b = (*b)[1:]
                for (*b)[0] != 0 {
                    s += string(rune((*b)[0]))
                    *b = (*b)[1:]
                }
                *b = (*b)[1:]
                return s
            case 1:
                // 1 byte
                ret := (*b)[1]
                *b = (*b)[2:]
                return int(ret)
            case 2:
                // 2 bytes
                ret := binary.LittleEndian.Uint16((*b)[1:])
                *b = (*b)[3:]
                return int(ret)
            case 3:
                // 4 bytes
                ret := binary.LittleEndian.Uint32((*b)[1:])
                *b = (*b)[5:]
                return int(ret)
            }
        }
    }
    return nil
}

func main() {
    f, err := os.Open("Extracted Data/RFCOMM-Oct 18 16_06_54.995.txt")
    if err != nil {
        panic(err)
    }
    defer f.Close()

    type Text struct {
        X int
        Y int
        S string
    }
    var texts []Text

    scanner := bufio.NewScanner(f)
    for scanner.Scan() {
        line := scanner.Text()
        if !strings.HasPrefix(line, "0B EF") {
            continue
        }
        line = strings.Replace(line[9:], " ", "", -1)
        b, err := hex.DecodeString(line)
        if err != nil {
            panic(err)
        }
        byteCodes := b[7:]
        byteCodes = byteCodes[:len(byteCodes)-2]

        for len(byteCodes) > 0 {
            switch byteCodes[0] {
            case 0x80:
                // FLUSH
                byteCodes = byteCodes[1:]
            case 0x84:
                // DRAW
                switch byteCodes[1] {
                case 0x00:
                    // UPDATE
                    byteCodes = byteCodes[2:]
                case 0x05:
                    // TEXT
                    byteCodes = byteCodes[2:]
                    parseParameter(&byteCodes)
                    x := parseParameter(&byteCodes).(int)
                    y := parseParameter(&byteCodes).(int)
                    s := parseParameter(&byteCodes).(string)
                    texts = append(texts, Text{x, y, s})
                case 0x12:
                    // TOPLINE
                    byteCodes = byteCodes[3:]
                case 0x13:
                    // FILLWINDOW
                    byteCodes = byteCodes[4:]
                default:
                    panic(fmt.Errorf("unexpected byte code: %x", byteCodes[1]))
                }
            default:
                panic(fmt.Errorf("unexpected byte code: %x", byteCodes[0]))
            }
        }
    }

    if err := scanner.Err(); err != nil {
        panic(err)
    }

    sort.Slice(texts, func(i, j int) bool {
        if texts[i].Y < texts[j].Y {
            return true
        }
        if texts[i].Y == texts[j].Y {
            return texts[i].X < texts[j].X
        }
        return false
    })

    y := 0
    for _, t := range texts {
        if t.Y != y {
            fmt.Printf("\n")
            y = t.Y
        }
        fmt.Printf("%s", t.S)
    }
    fmt.Printf("\n")
}

The result:

hitcon{m1nd5t0rm
_communication_a
nd_firmware_deve
loper_kit}