CVE-2020-28349 (Go, published 2020-11-09T01:15Z)
Description: value: An inaccurate frame deduplication process in ChirpStack Network Server 3.9.0 allows a malicious gateway to perform uplink Denial of Service via malformed frequency attributes in CollectAndCallOnceCollect in internal/uplink/collect.go. NOTE: the vendor's position is that there are no "guarantees that allowing untrusted LoRa gateways to the network should still result in a secure network.
Severity: MEDIUM (CVSS v3 base score: 6.5)
Weakness: CWE-20 \u2014 Improper Input Validation
The product receives input or data, but it does
        not validate or incorrectly validates that the input has the
        properties that are required to process the data safely and
        correctly.
Repository: https://github.com/brocaar/chirpstack-network-server
Files changed:
internal/gateway/rx_info.go
internal/gateway/stats/stats.go
internal/uplink/uplink.go
Commit message: Improve error handling of unknown gateways.


Vulnerable code:
func UpdateMetaDataInRxInfoSet(ctx context.Context, db sqlx.Queryer, rxInfo []*gw.UplinkRXInfo) error {
	for i := range rxInfo {
		id := helpers.GetGatewayID(rxInfo[i])
			log.WithFields(log.Fields{
				"ctx_id":     ctx.Value(logging.ContextIDKey),
				"gateway_id": id,
			}).WithError(err).Error("get gateway error")
		rxInfo[i].Location = &common.Location{
		if int(rxInfo[i].Board) < len(g.Boards) {
			board = g.Boards[int(rxInfo[i].Board)]
		if rxInfo[i].FineTimestampType == gw.FineTimestampType_ENCRYPTED && board.FPGAID != nil {
			tsInfo := rxInfo[i].GetEncryptedFineTimestamp()
			if tsInfo == nil {
				log.WithFields(log.Fields{
					"ctx_id":     ctx.Value(logging.ContextIDKey),
					"gateway_id": id,
				}).Error("encrypted_fine_timestamp must not be nil")
				continue
			}

		if rxInfo[i].FineTimestampType == gw.FineTimestampType_ENCRYPTED && board.FineTimestampKey != nil {
			tsInfo := rxInfo[i].GetEncryptedFineTimestamp()
			if tsInfo == nil {
				log.WithFields(log.Fields{
					"ctx_id":     ctx.Value(logging.ContextIDKey),
					"gateway_id": id,
				}).Error("encrypted_fine_timestamp must not be nil")
				continue
			}

			if rxInfo[i].Time == nil {
				log.WithFields(log.Fields{
					"ctx_id":     ctx.Value(logging.ContextIDKey),
					"gateway_id": id,
				}).Error("time must not be nil")
				continue
			}

			rxTime, err := ptypes.Timestamp(rxInfo[i].Time)
			rxInfo[i].FineTimestampType = gw.FineTimestampType_PLAIN
			rxInfo[i].FineTimestamp = &gw.UplinkRXInfo_PlainFineTimestamp{
	return nil
		return errors.Wrap(err, "get gateway error")
	// update the gateway meta-data
	if err := gateway.UpdateMetaDataInRxInfoSet(ctx, storage.DB(), rxPacket.RXInfoSet); err != nil {
		log.WithError(err).Error("uplink: update gateway meta-data in rx-info set error")
	}


Fixed code:
func UpdateMetaDataInRxInfoSet(ctx context.Context, db sqlx.Queryer, rxInfoSet []*gw.UplinkRXInfo) []*gw.UplinkRXInfo {
	var out []*gw.UplinkRXInfo

	for i := range rxInfoSet {
		rxInfo := rxInfoSet[i]

		id := helpers.GetGatewayID(rxInfo)
			if errors.Cause(err) == storage.ErrDoesNotExist {
				log.WithFields(log.Fields{
					"ctx_id":     ctx.Value(logging.ContextIDKey),
					"gateway_id": id,
				}).Warning("uplink received by unknown gateway")
			} else {
				log.WithFields(log.Fields{
					"ctx_id":     ctx.Value(logging.ContextIDKey),
					"gateway_id": id,
				}).WithError(err).Error("get gateway error")
			}
		rxInfo.Location = &common.Location{
		if int(rxInfo.Board) < len(g.Boards) {
			board = g.Boards[int(rxInfo.Board)]
		if tsInfo := rxInfo.GetEncryptedFineTimestamp(); tsInfo != nil && board.FPGAID != nil {
		if tsInfo := rxInfo.GetEncryptedFineTimestamp(); tsInfo != nil && board.FineTimestampKey != nil {
			rxTime, err := ptypes.Timestamp(rxInfo.GetTime())
			rxInfo.FineTimestampType = gw.FineTimestampType_PLAIN
			rxInfo.FineTimestamp = &gw.UplinkRXInfo_PlainFineTimestamp{

		out = append(out, rxInfo)
	return out
var ErrAbort = errors.New("abort")

			if err == ErrAbort {
				return nil
			}
		if errors.Cause(err) == storage.ErrDoesNotExist {
			log.WithFields(log.Fields{
				"ctx_id":     ctx.ctx.Value(logging.ContextIDKey),
				"gateway_id": gatewayID,
			}).Warning("gateway/stats: stats received by unknown gateway")
			return ErrAbort
		} else {
			return errors.Wrap(err, "get gateway error")
		}
	// update the gateway meta-data
	rxPacket.RXInfoSet = gateway.UpdateMetaDataInRxInfoSet(ctx, storage.DB(), rxPacket.RXInfoSet)

	// Return if the RXInfoSet is empty.
	if len(rxPacket.RXInfoSet) == 0 {
		return nil
	}