# read_req() ~~~~~{.c} void read_req(void) { char buf[128]; int i; gets(buf); i = atoi(buf); } ~~~~~ # Exploit (if stack grows down)

    0x00
      ^         ....
      |  +------------------+ <- esp
         |   return addr.   |
         +------------------+
         |      &buf        |
         +------------------+
         |        i         |
         +------------------+            +----------+<--+
         |     buf[0:3]     |            | shell    |   |
         |      ...         |            | code     |   |
         +------------------+ <- ebp     |          |   |
         |    saved ebp     |            |          |   |
         +------------------+            |          |   |
         |   return addr    |            |//////////|---+
         +------------------+            +----------+
# Exploit (if stack grows up) - Can't overwrite the return addr (of read_req)?

      0x00
         +------------------+
         |   return addr.   |
         +------------------+
         |    saved ebp     |
         +------------------+            +----------+
         |     buf[0:3]     |            | shell    |
         |      ...         |            | code     |
         +------------------+            |          |
         |        i         |            |          |
       | +------------------+            +----------+
       v        ...
# Exploit (if stack grows up)

      0x00
         +------------------+
         |   return addr.   |
         +------------------+
         |    saved ebp     |
         +------------------+ <---+      +----------+<--+
         |     buf[0:3]     |     |      | shell    |   |
         |      ...         |     |      | code     |   |
         +------------------+     |      |          |   |
         |        i         |     |      |          |   |
         +------------------+     |      |          |   |
         |      &buf        | ----+      |          |   |
         +------------------+            |          |   |
         |   return addr.   |            |//////////|---+
         +------------------+            +----------+
         |    saved ebp     |
       | +------------------+
       v        ...
# Heartbleed (CVE-2014-0160) - OpenSSL: popular SSL/TLS library - Heartbeat: keep connections alive even if no data to be transmitted - Send <-> response (identical copy of the payload) ~~~~~{.c} struct Msg { u8 type; /* 1 bytes */ u16 length; /* 2 bytes */ char* payload; /* len(payload) = length */ char* padding[]; /* minimum: 16 */ }; ~~~~~ # Code (@ssl/d1_both.c) ~~~~~{.c} // Msg = [type][len][payload][padding] type = *p++; // p = Msg n2s(p, length); // length = p.length payload = p; // payload = p.payload if (type == TLS1_HB_REQUEST) { unsigned char *buf, *bp; buf = OPENSSL_malloc(1 + 2 + length + padding); bp = buf; ~~~~~ # Code (@ssl/d1_both.c) ~~~~~{.c} // bp = Msg (type/length) *bp++ = TLS1_HB_RESPONSE; s2n(length, bp); // copy payload to the buffer memcpy(bp, payload, length); ssl3_write_bytes(s, TLS1_RT_HEARTBEAT, buf, 1 + 2 + length + padding); ~~~~~ # {.cover .w} ![](xkcd.png) # Consequences - Dump upto 64KB of memory - It might contain sensitive data (e.g., secrete key, passwd &c) - What should we do? # Fixes (@ssl/d1_both.c) ~~~~~{.c} /* discard zero-size heartbeat */ if (1 + 2 + 16 > s->s3->rrec.length) return 0; type = *p++; n2s(p, payload); /* discard if it lies */ if (1 + 2 + payload + 16 > s->s3->rrec.length) return 0; ~~~~~ # Lessons - A naive mistake can destroy the entire trust chains - Don't write own parser? - Don't use C? # CS101: quiz
(by John Regehr) {.shout} # Q1. What does the expression 1 > 0 evaluate to? 1. 0 1. 1 1. undefined # Q2. 1U > -1? 1. 0 1. 1 1. undefined # Q3. (unsigned short)1 > -1? 1. 0 1. 1 1. undefined # Q4. -1L > 1U? on x86-64? on x86? 1. 0 on both platforms 1. 1 on both platforms 1. 0 on x86-64, 1 on x86 1. 1 on x86-64, 0 on x86 # Q5. SCHAR_MAX == CHAR_MAX? 1. 0 1. 1 1. undefined # Q6. UINT_MAX + 1? 1. 0 1. 1 1. INT_MAX 1. UINT_MAX 1. undefined # Q7. INT_MAX + 1? 1. 0 1. 1 1. INT_MAX 1. UINT_MAX 1. INT_MIN 1. undefined # Q8. -INT_MIN? 1. 0 1. 1 1. INT_MAX 1. UINT_MAX 1. INT_MIN 1. undefined # Q9. Assume x has type int. Is the expression x<<0... 1. defined for all values of x 1. defined for some values of x 1. defined for no values of x # Q10. Assume x has type int. Is the expression x<<1... 1. defined for all values of x 1. defined for some values of x 1. defined for no values of x # Q11. Assume x has type int. Is the expression x<<31... 1. defined for all values of x 1. defined for some values of x 1. defined for no values of x # Q12. Assume x has type int. Is the expression x<<32... 1. defined for all values of x 1. defined for some values of x 1. defined for no values of x # Q13. Assume x has type short. Is the expression x<<29... 1. defined for all values of x 1. defined for some values of x 1. defined for no values of x # Q14. Assume x has type unsigned. Is the expression x<<31... 1. defined for all values of x 1. defined for some values of x 1. defined for no values of x # Q15. Assume x has type unsigned short. Is the expression x<<31... 1. defined for all values of x 1. defined for some values of x 1. defined for no values of x # Q16. Assume x has type int. Is the expression x + 1... 1. defined for all values of x 1. defined for some values of x 1. defined for no values of x # Q17. Assume x has type int. Is the expression x - 1 + 1... 1. defined for all values of x 1. defined for some values of x 1. defined for no values of x # Q18. Assume x has type int. Is the expression (short)x + 1... 1. defined for all values of x 1. defined for some values of x 1. defined for no values of x # Q19. Assume x has type int. Is the expression (short)(x + 1)... 1. defined for all values of x 1. defined for some values of x 1. defined for no values of x # kint {.shout} # Q1 - First, ignoring range metadata, what constraint would KINT generate for the count variable in the code from Figure 3? # Q2. - How can you simplify the snippet of code in Figure 1 using the NaN integers as described in Section 7?