Course Overview
Your application written in C and C++ works as intended, so you are done, right? But did you consider feeding in incorrect values? 16Gbs of data? A null? An apostrophe? Negative numbers, or specifically -1 or -2^31? Because that’s what the bad guys will do – and the list is far from complete.
Handling security needs a healthy level of paranoia, and this is what this course provides: a strong emotional engagement by lots of hands-on labs and stories from real life, all to substantially improve code hygiene. Mistakes, consequences, and best practices are our blood, sweat and tears.
All this is put in the context of C and C++, and extended by core programming issues, discussing security pitfalls of these languages.
So that you are prepared for the forces of the dark side.
So that nothing unexpected happens.
Nothing.
Who should attend
C/C++ developers
Prerequisites
General C/C++ development
Course Objectives
- Getting familiar with essential cyber security concepts
- Correctly implementing various security features
- Identify vulnerabilities and their consequences
- Learn the security best practices in C and C++
- Input validation approaches and principles
Course Content
Day 1
Security testing
- Security testing vs functional testing
- Manual and automated methods
- Black box, white box, and hybrid testing
- Security testing methodology
- Security testing – goals and methodologies
- Overview of security testing processes
- Identifying and rating assets
- Preparation and scoping
- Identifying assets
- Identifying the attack surface
- Assigning security requirements
- Lab – Identifying and rating assets
- Threat modeling
- SDL threat modeling
- Mapping STRIDE to DFD
- DFD example
- Attack trees
- Attack tree example
- Lab – Crafting an attack tree
- Misuse cases
- Misuse case examples
- Risk analysis
- Lab – Risk analysis
- Accomplishing the tests
- Reporting, recommendations, and review
- Security testing techniques and tools
- Code analysis
- Static Application Security Testing (SAST)
- Lab – Using static analysis tools
- Dynamic analysis
- Security testing at runtime
- Penetration testing
- Memory inspection and analysis
- Lab – Dumping process memory
- Stress testing
- Dynamic Application Security Testing (DAST)
- Fuzzing
- Fuzzing techniques
- Fuzzing – Observing the process
- American Fuzzy Lop (AFL)
- Common software security weaknesses
- Errors
- Error and exception handling principles
- Error handling
- Returning a misleading status code
- Error handling in C
- Error handling in C++
- Using std::optional safely
- Information exposure through error reporting
- Case study – Information leakage via errors in Apache Superset
- Exception handling
- In the catch block. And now what?
- Empty catch block
- Exception handling in C++
- Lab – Exception handling mess
- Testing for error and exception handling problems
- Denial of service
- Flooding
- Resource exhaustion
- Sustained client engagement
- Denial of service problems in C/C++
- Infinite loop
- Economic Denial of Sustainability (EDoS)
- Amplification
- Some amplification examples
- Algorithmic complexity issues
- Regular expression denial of service (ReDoS)
- Lab – ReDoS
- Dealing with ReDoS
- Hash table collision
- How do hash tables work?
- Hash collision against hash tables
- Errors
- Wrap up
- Secure coding principles
- Principles of robust programming by Matt Bishop
- Secure design principles of Saltzer and Schroeder
- And now what?
- Software security sources and further reading
- C and C++ resources
- Security testing resources
Day 2
Memory management hardening
- Securing the toolchain
- Securing the toolchain in C and C++
- Using FORTIFY_SOURCE
- Lab – Effects of FORTIFY
- AddressSanitizer (ASan)
- Using AddressSanitizer (ASan)
- Lab – Using AddressSanitizer
- Stack smashing protection
- Detecting BoF with a stack canary
- Argument cloning
- Stack smashing protection on various platforms
- SSP changes to the prologue and epilogue
- Lab – Effects of stack smashing protection
- Runtime protections
- Runtime instrumentation
- Address Space Layout Randomization (ASLR)
- ASLR on various platforms
- Lab – Effects of ASLR
- Circumventing ASLR – NOP sleds
- Circumventing ASLR – memory leakage
- Non-executable memory areas
- The NX bit
- Write XOR Execute (W^X)
- NX on various platforms
- Lab – Effects of NX
- NX circumvention – Code reuse attacks
- Return-to-libc / arc injection
- Return Oriented Programming (ROP)
- Protection against ROP
- ARM-specific ROP protection techniques
- Common software security weaknesses
- Security features
- Authentication
- Password management
- Inbound password management
- Storing account passwords
- Password in transit
- Lab – Is just hashing passwords enough?
- Dictionary attacks and brute forcing
- Salting
- Adaptive hash functions for password storage
- Outbound password management
- Hard coded passwords
- Best practices
- Lab – Hardcoded password
- Protecting sensitive information in memory
- Challenges in protecting memory
- Heap inspection
- Compiler optimization challenges
- Case study – Microsoft secret key theft via dump files
- Code quality
- Code quality and security
- Data handling
- Type mismatch
- Lab – Type mismatch
- Initialization and cleanup
- Constructors and destructors
- Initialization of static objects
- Lab – Initialization cycles
- Array disposal in C++
- Lab – Mixing delete and delete[]
- Memory and pointers
- Memory and pointer issues
- Pointer handling pitfalls
- Null pointers
- NULL dereference
- NULL dereference in pointer-to-member operators
- Pointer usage in C and C++
- Use after free
- Lab – Use after free
- Lab – Runtime instrumentation
- Double free
- Smart pointers
- Security features
Day 3
Common software security weaknesses
- Input validation
- Input validation principles
- Denylists and allowlists
- What to validate – the attack surface
- Where to validate – defense in depth
- When to validate – validation vs transformations
- Validation with regex
- Regular expression denial of service (ReDoS)
- Lab – ReDoS
- Dealing with ReDoS
- Injection
- Code injection
- OS command injection
- Lab – Command injection
- OS command injection best practices
- Avoiding command injection with the right APIs
- Lab – Command injection best practices
- Case study – Shellshock
- Lab – Shellshock
- Case study – Command injection in AVTECH IP cameras
- Process control
- Library injection
- Lab – Library hijacking
- Library injection best practices
- Integer handling problems
- Representing signed numbers
- Integer visualization
- Integer promotion
- Integer overflow
- Lab – Integer overflow
- Signed / unsigned confusion
- Case study – The Stockholm Stock Exchange
- Lab – Signed / unsigned confusion
- Integer truncation
- Lab – Integer truncation
- Case study – WannaCry
- Best practices
- Upcasting
- Precondition testing
- Postcondition testing
- Best practices in C
- Lab – Handling integer overflow on the toolchain level in C and C++
- Best practices in C++
- Lab – Integer handling best practices in C++
- Files and streams
- Path traversal
- Lab – Path traversal
- Path traversal best practices
- Lab – Path canonicalization
- Wrap up
- Secure coding principles
- Principles of robust programming by Matt Bishop
- Secure design principles of Saltzer and Schroeder
- And now what?
- Software security sources and further reading
- C and C++ resources