Intro to Information Security 1 | Modular Calculation and Software Vulnerabilities

0. Mod Calculation

(1) A mod B

If A is a negative number, then the result should be,

Result = A - B * floor(A/B)

(2) Congruence

If A == B mod C, then A mod C == B mod C
If A mod C == B mod C, then A == B mod C

(3) Modular Addition

(A + B) mod C = (A mod C + B mod C) mod C

(4) Modular Subtraction

(A - B) mod C = (A mod C - B mod C) mod C

(5) Modular Multiplication

(A * B) mod C = (A mod C * B mod C) mod C

(6) Exponentiation

A^n mod C = (A mod C)^n mod C

(7) Quick Trick of 2-Based Exponentiation

A^(2^n) mod C = (A^(2^(n-1)) mod C)^2 mod C

(8) Exponentiation Trick

Suppose we have to calculate M^N mod C, where N is a large number, then we can calculate the result by

  • Rewrite N to binary form bin(N)

  • Convert the exponentials to polynominals with powers of 2

  • Calculate each term with the quick trick of 2-based exonentiation

  • Combine the individual powers to find the final answer

(9) Modular Inverse

The modular inverse of A mod C is B satisifies,

(A B) mod C == 1

(10) Co-prime

Two integers a and b are said to be co-prime if the only positive integer integer that evenly divides both of them is 1.

(11) Prime Root

A number M is a prime root of a number N if every number co-prime to N is congtuent to a power of M mod N.

(12) Relative Prime

It describes two numbers for which the only common factor is 1. In other words, relatively prime numbers have a greatest common factor (gcf) of 1.

(13) Totients

The totient function φ(n), also called Euler’s totient function, which is defined as the number of positive integers <= n that are relatively prime to n, where 1 is counted as being relatively prime to all numbers.

Since a number less than or equal to and relatively prime to a given number is called a totative, the totient function phi(n) can be simply defined as the number of totatives of n.

1. Introduction

(1) Cyber Security Requirements (CIA rule)

  • Confidentiality: only some people can view it

  • Integrity: only some people can modify it

  • Availability: the data must exist somewhere

(2) Role of the Good Guys

  • Prevention

  • Detection

  • Respnse

  • Recovery and remediation

  • Establish policies and mechanisms

(3) Ways to address cyber security

  • Complexity is our enemy

  • Fail-safe defaults: allow only if someone really need the data

  • Complete mediation: no one should be able to bypass the monitor

  • Open design: don’t count on someone who will not find out your design

  • Least privilage: only have the privilege to the resource someone absoluately need

  • Psychological acceptability

2. Software Vulnerabilities

(1) Common Software Vulnerabilities

  • Memory overflow

  • Stack buffer overflow

    • Stacks are used in function/procedure calls

    • Stacks are also used for allocating memory

      • local variables

      • parameteres

      • control information (return address)

(2) Vulnerable Program Example

#include <stdio.h>
#include <strings.h>

int main(int argc, char *argv[]) {
    int allow_login = 0;
    char pwdstr[12];
    char targetpwd[12] = "MyPwd123";
    
    gets(pwdstr);
    
    if (strncmp(pwdstr, targetpwd, 12) == 0)
        allow_login = 1;
        
    if (allow_login == 0) 
        printf("Login request rejected");
    else
        printf("Login request allowed");
}

Now let’s look into the stack of this program.

(4) Understanding the Stack

---------------------- <- High address (addr)
argc = 4 bytes
---------------------- <- (addr - 4)
argv = 4 bytes
---------------------- <- (addr - 8)
return address = 4 bytes
---------------------- <- (addr - 12)
allowlogin = 4 bytes
---------------------- <- (addr - 16)
pwdstr = 12 bytes
---------------------- <- (addr - 28)
targetpwd = 12 byte
---------------------- <- (addr - 40)
...
---------------------- <- Low address

(5) Bypass Case

The problem of this program is that when we have the function gets read a string, it will not check the length of that string so that the input string can be longer than the memory we have allocated for the variable pwdstr. Therefore, the variable memory will overflow and it will continue writing to allowlogin variable. For example, if the input string is BadPasswordHere!, then

---------------------- <- High address (addr)
argc = 4 bytes
---------------------- <- (addr - 4)
argv = 4 bytes
---------------------- <- (addr - 8)
return address = 4 bytes
---------------------- <- (addr - 12)
allowlogin = ere!
---------------------- <- (addr - 16)
pwdstr = BadPasswordH
---------------------- <- (addr - 28)
targetpwd = MyPwd123
---------------------- <- (addr - 40)
...
---------------------- <- Low address

Then if we compare pwdstr and targetpwd, even though those two varibles will not match and the program will not assign 1 to allowlogin, this variable has already been overwritten by the input string that exceed 12 bytes (aka. ere!). In this case, we can still pass the check for this login.

(6) Shellcode

Shellcode creates a shell which allows it to execute any code the attacker wants. Basically, the attackers would like to allocate the following privileges,

  • the host program

  • system service

  • OS root privileges

(7) Variations of Buffer Overflow

  • “return-to-libc” Attack: means to rewrite the return address to a standard library function. This works because if we can return to a standard library function and we are also able to setup some arguments or parameters for it on the stack, then we can execute a function with the argument of our choice.

  • “Heap Overflow” Attack: Longer variables like the global variables are stored on the heap. The difference between a stack and a heap is that the heap doesn’t have a return address. However, the heap do have some function pointers and the data can be the tables of function pointers. So the data stored in the heap is overwritten and we are able to corrupt the memory in some sense as we have done before.

  • “OpenSSL Heartbleed Vulnerability” Attack: the attacks above is about writing too much and overwriting the data. However, the overflow attacks don’t necessarily to be assoicated with writing data. OpenSSL heartbleed vulnerability happens when we read too much. It keeps reading beyond the variables that we are supposed to read.

(8) Defense Against Overflow

  • Programming language is crucial. Common safe programming languages are C++, Java, etc. A safe language should have the following traits:

    • should be strongly typed

    • should do automatic bounds checks

    • should do automatic memory management

  • Check inputs (All input is evil)

  • Use safer functions that do bounds checking

  • Use automatic tools to analyze code to flag potential unsafe vulnerabilities. These tools can be found at OWASP.

    • they can flag potential unsafe fucntions/constructs

    • they can help mitigate security lapses (but can hardly eliminate all buffer overflow vulnerabilities)

  • Use canary values: a canary value is written when a return address is stored in a stack frame and any attempts to rewrite the address using buffer overflow will result in the canary being rewritten and an overflow will be detected.

  • Address Space Layout Randomization (ASLR): this is a feature supported by the operating system that randomizes stack, heap, libc, etc. This makes it harder for the attacker to find important locations like libc function addresses.