Semmle 1.20
Skip to end of metadata
Go to start of metadata

Name: Use of a version of OpenSSL with Heartbleed

Description: Using an old version of OpenSSL can allow remote attackers to retrieve portions of memory.

ID: cpp/openssl-heartbleed

Kind: problem

Severity: error

Precision: very-high

Query: OpenSslHeartbleed.ql
/**
 * @name Use of a version of OpenSSL with Heartbleed
 * @description Using an old version of OpenSSL can allow remote
 *              attackers to retrieve portions of memory.
 * @kind problem
 * @problem.severity error
 * @precision very-high
 * @id cpp/openssl-heartbleed
 * @tags security
 *       external/cwe/cwe-327
 *       external/cwe/cwe-788
 */

import cpp

/**
 * Holds if `v` and `w` are ever compared to each other.
 */
predicate comparedTo(Variable v, Variable w) {
  v.getAnAssignedValue() = w.getAnAccess() or
  exists (ComparisonOperation comp |
    comp = v.getAnAccess().getParent+() and
    comp = w.getAnAccess().getParent+()
  )
}

class DataVariable extends Variable {
  DataVariable() {
    exists(Struct ssl3_record_st |
           ssl3_record_st.hasName("ssl3_record_st") and
           this = ssl3_record_st.getAField() and
           this.hasName("data"))
  }
}

/**
 * Holds if expression `e` might evaluate to a pointer
 * into the memory region pointed to by `v`.
 */
predicate pointsInto(Expr e, DataVariable v) {
  e = v.getAnAccess() or
  e.(AddressOfExpr).getOperand().(ArrayExpr).getArrayBase() = v.getAnAccess() or
  varPointsInto(e.(VariableAccess).getTarget(), v)
}

pragma[nomagic]
predicate varPointsInto(Variable tainted, DataVariable src) {
  pointsInto(tainted.getAnAssignedValue(), src)
}

from FunctionCall fc, Struct ssl3_record_st, Field data, Field length
where fc.getTarget().getName().matches("%memcpy%") and
      ssl3_record_st.hasName("ssl3_record_st") and
      data = ssl3_record_st.getAField() and data.hasName("data") and
      length = ssl3_record_st.getAField() and length.hasName("length") and
      pointsInto(fc.getArgument(1), data) and
      not comparedTo(fc.getArgument(2).(VariableAccess).getTarget(), length)
select fc, "This call to memcpy is insecure (Heartbleed vulnerability)."

Earlier versions of the popular OpenSSL library suffer from a buffer overflow in its "heartbeat" code. Because of the location of the problematic code, this vulnerability is often called "Heartbleed".

Software that includes a copy of OpenSSL should be sure to use a current version of the library. If it uses an older version, it will be vulnerable to any network site it connects with.

Recommendation

Upgrade to the latest version of OpenSSL. This problem was fixed in version 1.0.1g.

Example

The following code is present in earlier versions of OpenSSL. The payload variable is the number of bytes that should be copied from the request back into the response. The call to memcpy does this copy. The problem is that payload is supplied as part of the remote request, and there is no code that checks the size of it. If the caller supplies a very large value, then the memcpy call will copy memory that is outside the request packet.

int
tls1_process_heartbeat(SSL *s)
    {
    unsigned char *p = &s->s3->rrec.data[0], *pl;
    unsigned short hbtype;
    unsigned int payload;
 
    /* ... */
 
    hbtype = *p++;
    n2s(p, payload);
    pl = p;
 
    /* ... */
 
    if (hbtype == TLS1_HB_REQUEST)
            {
            /* ... */
            memcpy(bp, pl, payload);  // BAD: overflow here
            /* ... */
            }
 
 
    /* ... */
 
    }

References