Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Secure Programming Practices in C++ (NDC Oslo 2018)

673 views

Published on

Bjarne Stroustrup, the creator of C++, once said : “C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off.” He has also said : “Within C++, there is a much smaller and cleaner language struggling to get out.” Both are true.

This talk is for programmers wishing to feel more comfortable navigating the C++ landscape. Motivated by going through well known vulnerability patterns that have been used in exploits for decades, we will explore the programming culture that has developed around the C++ language. Specifically, we will look at programming patterns that navigate around or through some of the dangerous parts of the C++ language. The goal is to build a set of programming practices based in the “smaller and cleaner language” inside C++. And by doing so, we will also build an awareness around code constructs that can potentially “blow your whole leg off”.

Published in: Software
  • Be the first to comment

Secure Programming Practices in C++ (NDC Oslo 2018)

  1. 1. @pati_gallardo
  2. 2. Secure Programming Practices in C++ @pati_gallardo Patricia Aas NDC { Oslo } 2018
  3. 3. Patricia Aas - Vivaldi Browser Programmer - mainly in C++ Currently : Vivaldi Technologies Previously : Cisco Systems, Knowit, Opera Software Master in Computer Science Twitter : @pati_gallardo Photos: pixabay.com - CC0
  4. 4. Bjarne Stroustrup “C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off.” @pati_gallardo
  5. 5. Bjarne Stroustrup “Within C++, there is a much smaller and cleaner language struggling to get out.” @pati_gallardo
  6. 6. What specs exist? Undefined Behavior Compiler Optimizations Exploitability Take your vitamins The Eight I'd Really Rather You Didn'ts @pati_gallardo
  7. 7. What specs exist? @pati_gallardo
  8. 8. CG: C++ Core Guidelines (328 pages!) @pati_gallardo
  9. 9. C++ Core Guidelines “The C++ Core Guidelines are a collaborative effort led by Bjarne Stroustrup, much like the C++ language itself.” https://github.com/isocpp/CppCoreGuidelines/blob/master/README.md @pati_gallardo
  10. 10. @pati_gallardo SEI: CERT C++ Coding Standard (435 pages!)
  11. 11. SEI CERT C Coding Standard “The SEI CERT C [and C++] Coding Standard is a software coding standard for the C [and C++] programming language, developed by the CERT Coordination Center to improve the safety, reliability, and security of software systems.” https://en.wikipedia.org/wiki/CERT_C_Coding_Standard @pati_gallardo
  12. 12. CWE : Common Weakness Enumeration (1572 pages!) @pati_gallardo
  13. 13. Common Weakness Enumeration (CWE) “The Common Weakness Enumeration (CWE) is a category system for software weaknesses and vulnerabilities. It is sustained by a community project with the goals of understanding flaws in software and creating automated tools that can be used to identify, fix, and prevent those flaws.” https://en.wikipedia.org/wiki/Common_Weakness_Enumeration @pati_gallardo
  14. 14. What specs exist? Undefined Behavior Compiler Optimizations Exploitability Take your vitamins The Eight I'd Really Rather You Didn'ts @pati_gallardo
  15. 15. @pati_gallardo Undefined Behaviour
  16. 16. undefined behavior “Examples of undefined behavior are memory accesses outside of array bounds, signed integer overflow, null pointer dereference, modification of the same scalar more than once in an expression without sequence points, access to an object through a pointer of a different type, etc. Compilers are not required to diagnose undefined behavior (although many simple situations are diagnosed), and the compiled program is not required to do anything meaningful.” http://en.cppreference.com/w/cpp/language/ub @pati_gallardo
  17. 17. - Don’t reason about undefined behaviour - Assume that it crashes or is never executed - Changing compiler, compiler version or optimization level can break your application Undefined Behaviour
  18. 18. Infinite Loop (Undefined Behavior) #include <iostream> #include <complex> using namespace std; int main(void) { complex<int> delta; complex<int> mc[4] = {0}; for(int di = 0; di < 4; di++, delta = mc[di]) { cout << di << endl; } } @pati_gallardo (Thanks to @shafikyaghmour) https://stackoverflow.com/questions/32506643/c-compilation-bug Undefined Behavior!
  19. 19. Infinite Loop (Undefined Behavior) Should we Godbolt this? https://godbolt.org/g/TDjM8h @pati_gallardo (Thanks to @shafikyaghmour) https://stackoverflow.com/questions/32506643/c-compilation-bug
  20. 20. What specs exist? Undefined Behavior Compiler Optimizations Exploitability Take your vitamins The Eight I'd Really Rather You Didn'ts @pati_gallardo
  21. 21. @pati_gallardo Compiler Optimization
  22. 22. @pati_gallardo The Case Of The Disappearing Memset
  23. 23. 0) CWE-14: Compiler Removal of Code to Clear Buffers void GetData(char *MFAddr) { char pwd[64]; if (GetPasswordFromUser(pwd, sizeof(pwd))) { if (ConnectToMainframe(MFAddr, pwd)) { // Interaction with mainframe } } memset(pwd, 0, sizeof(pwd)); // <- Removed by the optimizer } @pati_gallardo SEI: MSC06-C. Beware of compiler optimizations SEI: MEM03-C. Clear sensitive information stored in reusable resources
  24. 24. 0) CWE-14: Compiler Removal of Code to Clear Buffers Should we Godbolt this? https://godbolt.org/g/FpEsht @pati_gallardo SEI: MSC06-C. Beware of compiler optimizations SEI: MEM03-C. Clear sensitive information stored in reusable resources
  25. 25. Memset_s : Zeroing Memory // Compliant Solution (C11) memset_s(pwd, 0, sizeof(pwd)); // Windows Solution SecureZeroMemory(pwd, sizeof(pwd)); @pati_gallardo SEI: MSC06-C. Beware of compiler optimizations SEI: MEM03-C. Clear sensitive information stored in reusable resources
  26. 26. What specs exist? Undefined Behavior Compiler Optimizations Exploitability Take your vitamins The Eight I'd Really Rather You Didn'ts @pati_gallardo
  27. 27. @pati_gallardo Exploitability
  28. 28. 1. Unsigned Integer Wraparound 2. Signed Integer Overflow 3. Numeric Truncation 4. Stack Buffer Overflow 5. Heap Buffer Overflow 6. Buffer Underflow 7. Use After Free 8. Double Free 9. Incorrect Type Conversion 10. Uncontrolled Format String @pati_gallardo
  29. 29. Code is on GitHub: https://github.com/patricia-gallardo/insecure-coding-examples Disclaimer: The concat buffer examples aren’t really fair because if you did concatenation of strings in this way you would have to take into consideration 0 termination of strings and that doesn’t fit on a slide, so... sigh @pati_gallardo
  30. 30. 1) Unsigned Integer Wraparound 2) Signed Integer Overflow 3) Numeric Truncation Error
  31. 31. 1) CWE-190: Unsigned Integer Wraparound int main(void) { unsigned int first_len = UINT_MAX; unsigned int second_len = 256; unsigned int buf_len = 256; char first[first_len], second[second_len], buf[buf_len]; if((first_len + second_len) <= 256) { // <- sum == 255 memcpy(buf, first, first_len); memcpy(buf + first_len, second, second_len); } } @pati_gallardo SEI-INT30-C. Ensure that unsigned integer operations do not wrap
  32. 32. 2) CWE-190: Signed Integer Overflow int main(void) { int first_len = INT_MAX; int second_len = 256; int buf_len = 256; char first[first_len], second[second_len], buf[buf_len]; if((first_len + second_len) <= 256) { // <- UB (negative) memcpy(buf, first, first_len); memcpy(buf + first_len, second, second_len); } } @pati_gallardo SEI-INT32-C. Ensure that operations on signed integers do not result in
  33. 33. 3) CWE-197: Numeric Truncation Error int main(void) { unsigned int first_len = UINT_MAX - 256; unsigned int second_len = 256; unsigned int buf_len = 256; char first[first_len], second[second_len], buf[buf_len]; int new_len = (first_len+second_len); // <- IDB (negative) if(new_len <= 256) { memcpy(buf, first, first_len); memcpy(buf + first_len, second, second_len); } } @pati_gallardo SEI-INT31-C. Ensure that integer conversions do not result in lost or misinterpreted data
  34. 34. 4) Stack-based Buffer Overflow 5) Heap-based Buffer Overflow 6) Buffer Underwrite/Underflow
  35. 35. 4) CWE-121: Stack-based Buffer Overflow @pati_gallardo int main(void) { char buffer[10]; // CWE-242 : Inherently Dangerous Function gets(buffer); // <- Write outside } SEI-STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator
  36. 36. 5) CWE-122: Heap-based Buffer Overflow int main(int argc, char * argv[]) { char* buf = (char*) malloc(sizeof(char)*10); strcpy(buf, argv[1]); // <- Write outside free(buf); } @pati_gallardo SEI-ARR38-C. Guarantee that library functions do not form invalid pointers
  37. 37. 6) CWE-124: Buffer Underwrite / Underflow int main(void) { char src[12]; strcpy(src, "Hello World"); size_t length = strlen(src); int index = (length -1); while (src[index] != ':') { src[index] = '0'; index--; } } @pati_gallardo SEI-ARR30-C. Do not form or use out-of-bounds pointers or array subscripts
  38. 38. 7) Use After Free 8) Double Free
  39. 39. 7) CWE-416: Use After Free @pati_gallardo int main(void) { char* buffer = (char*)malloc (256); bool error = true; if (error) free(buffer); // [...] if (error) printf("%lun", strlen(buffer)); //<- Use after free } SEI-MEM30-C. Do not access freed memory
  40. 40. 8) CWE-415: Double Free @pati_gallardo int main(void) { char* buffer = (char*)malloc (256); bool error = true; if (error) free(buffer); // [...] free(buffer); // second free } SEI-MEM51-CPP. Properly deallocate dynamically allocated resources
  41. 41. 9) Incorrect Type Conversion/Cast 10) Use of External Format String
  42. 42. 9) CWE-704: Incorrect Type Conversion/Cast @pati_gallardo struct A {}; struct B {}; int main(void) { struct A * a = (struct A *) malloc (sizeof (struct A)); struct B * b = (struct B *) a; // cast to unrelated type } SEI-EXP05-CPP. Do not use C-style casts
  43. 43. 10) CWE-134: Use of External Format String @pati_gallardo int main(int argc, char * argv[]) { char * format = argv[1]; char * str = argv[2]; printf(format, str); } $ ./format_string "%s %d" "Hello World" Hello World 1745066888 SEI-FIO47-C. Use valid format strings
  44. 44. What specs exist? Undefined Behavior Compiler Optimizations Exploitability Take your vitamins The Eight I'd Really Rather You Didn'ts @pati_gallardo
  45. 45. Use Your Tools @pati_gallardo
  46. 46. Classes of Tools - Several compilers - Warnings / Errors - Instrumentation - Static Analysis - Automated Tests - Fuzzing - Continuous Integration - Libraries @pati_gallardo
  47. 47. What specs exist? Undefined Behavior Compiler Optimizations Exploitability Take your vitamins The Eight I'd Really Rather You Didn'ts @pati_gallardo
  48. 48. The Eight I'd Really Rather You Didn'ts* *The Eight Condiments (Pastafarianism) @pati_gallardo
  49. 49. Caution: Don’t take me too seriously. But seriously, think about it! *wink* @pati_gallardo
  50. 50. The Eight I'd Really Rather You Didn'ts 1. Use C 2. Allocate with new 3. Do math a lot 4. Trust your external input 5. Use pointers a lot 6. Write “clever” code 7. Use shared_ptr a lot 8. Use share state a lot @pati_gallardo
  51. 51. 1. I'd Really Rather You Didn't: Use C @pati_gallardo CG : CPL.1: Prefer C++ to C
  52. 52. Std::string - Concatenate Strings int main() { std::string first = "Hello "; std::string second = "World"; std::string buffer = first + second; std::cout << buffer << "n"; } @pati_gallardo
  53. 53. Std::cout/cin : Using the Command Line int main(int argc, char * argv[]) { std::string second; std::cin >> second; std::string first = argv[1]; std::string buffer = first + second; std::cout << buffer << "n"; } $ ./command_line "Hello " World Hello World @pati_gallardo
  54. 54. Algorithms : Strip after Colon int main() { string str = "Hello:World"; auto isColon = [](int ch) { return ch == ':'; }; auto first = find_if(rbegin(str), rend(str), isColon); str.erase(first.base(), end(str)); } @pati_gallardo
  55. 55. C++ Casts : Safe Downcasting class Spiderman {}; class Ironman {}; int main() { Spiderman * peter = new Spiderman; Ironman * tony = static_cast<Ironman*>(peter); } inheritance.cpp:6:20: error: static_cast from 'Spiderman *' to 'Ironman *', which are not related by inheritance, is not allowed Ironman * tony = static_cast<Ironman*>(peter); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1 error generated. @pati_gallardo
  56. 56. @pati_gallardo CG : R: Resource management CG : R.11: Avoid calling new and delete explicitly 2. I'd Really Rather You Didn't: Allocate With New
  57. 57. Allocating on the Stack #include "Hero.h" int main() { Hero h; } @pati_gallardo
  58. 58. Where is it? Stack Hero stackHero; Heap unique_ptr<Hero> heapHero = make_unique<Hero>(); Hero * heapHero = new Hero(); @pati_gallardo
  59. 59. Loving the Stack #include <iostream> #include <string> using namespace std; int main() { { string s("Hello World!"); cout << s; } // <- GC happens here! } @pati_gallardo
  60. 60. Using the Stack To Manage Resource Lifetimes Destroyed when exiting scope Deterministic Garbage Collection @pati_gallardo
  61. 61. Hold a Value on the Stack that Controls The Lifetime of Your Heap Allocated Object using namespace std; { unique_ptr<Hero> myHero = make_unique<Hero>(); shared_ptr<Hero> ourHero = make_shared<Hero>(); } Smart Pointers @pati_gallardo
  62. 62. @pati_gallardo 3. I'd Really Rather You Didn't: Do Math A Lot
  63. 63. Primitive types have no semantics, only limits Reduce the value space Keep it within defined behavior Enum class, string literals, user defined literals, size_t @pati_gallardo
  64. 64. Enum Class @pati_gallardo enum class Direction : char { NORTH = 'N', EAST = 'E', WEST = 'W', SOUTH = 'S' }; std::ostream& operator << (std::ostream& os, const Direction& obj) { os << static_cast<std::underlying_type<Direction>::type>(obj); return os; } int main() { std::cout << "t" << Direction::NORTH << "n" << "t" << Direction::EAST << "n" << "t" << Direction::WEST << "n" << "t" << Direction::SOUTH << "n"; }
  65. 65. String Literals @pati_gallardo using namespace std::literals::string_literals; int main() { auto heroes = {"Spiderman"s, "Ironman"s, "Wonder Woman"s}; for(auto const & hero : heroes) { std::cout << "t" << hero << "n"; } }
  66. 66. 1) User Defined Literals @pati_gallardo int main() { auto h = 24_hours; auto d = 7_days; auto err = h + d; } user_defined_literals.cpp:25:21: error: invalid operands to binary expression ('Hours' and 'Days') auto err = hours + days; ~~~~~ ^ ~~~~ 1 error generated.
  67. 67. 2) User Defined Literals @pati_gallardo struct Hours { explicit Hours(unsigned long long n) : num(n) {} unsigned long long num = 0; }; struct Days { explicit Days(unsigned long long n) : num(n) {} unsigned long long num = 0; };
  68. 68. 3) User Defined Literals @pati_gallardo Hours operator "" _hours(unsigned long long num) { return Hours(num); } Days operator "" _days(unsigned long long num) { return Days(num); }
  69. 69. Use Size_t for Sizes - Unsigned integer type - Result of the sizeof operator - Use for object sizes - Use for array indexing and loop counting @pati_gallardo
  70. 70. @pati_gallardo 4. I'd Really Rather You Didn't: Trust Your External Input
  71. 71. Taint - Is the source of this value in your code? - Command line args, size fields in headers, exported functions, APIs @pati_gallardo
  72. 72. 5. I'd Really Rather You Didn't: Use Pointers a Lot @pati_gallardo
  73. 73. @pati_gallardo 6. I'd Really Rather You Didn't: Write “clever” code
  74. 74. 7. I'd Really Rather You Didn't: Use shared_ptr a Lot @pati_gallardo
  75. 75. 8. I'd Really Rather You Didn't: Share State a Lot @pati_gallardo
  76. 76. So… what should I remember from this presentation? @pati_gallardo
  77. 77. Well, I'd Really Rather You Didn't: Use C @pati_gallardo
  78. 78. Learn some Modern C++ Instead! @pati_gallardo
  79. 79. @pati_gallardo

×