Table of Contents

CS3235 Computer Security

Pinned resources

  • Textbooks:
    • OSTEP on operating systems
    • Goodrich, "Introduction to Computer Security"

Honestly a pretty good lecture on first principles.

Also the lecturer opened the floor to suggest topics for security stuff. Infrastructure security module come AY2023/24 Sem 2.


Week 13

Virtualization

Main motivation is to subvert almost all forms of malicious threats (even up to kernel rootkits) by isolating into a sandbox.

The concept of virtual machines actually came out from 1960s to share machines between users (e.g. IBM). The late 1990s focused on RISC/CISC virtualization to mitigate under-utilization, ease-of-maintenance, security (e.g. VMWare). Today the virtualization model is heavily utilized in cloud computing.

Two-fold security goals for virtualization, specifically the isolation of code, data and resources between:

  1. Guest VM and Host VMM
  2. Between guest VMs

assuming that the TCB (i.e. Host OS and VMM) are bug-free, and that guest VMs may contain arbitrary malware. These translate to the following enforcement goals for a VMM:

Although virtualization was historically designed for performance rather than for security, there are some main security uses of virtualization, including:

Virtualization techniques

The guest OS, from its perspective, runs in (privileged) ring 0, but actually runs in ring 1 on the host OS. The VMM, which actually runs in ring 0, traps the instructions called by the guest and emulates these instructions (e.g. accesses to page table). This is known as trap-and-emulate.

There is a problem: x86 architecture has a few instructions that are privileged but do not generate traps, e.g. cli (clear interrupt) traps, but popf (pop flag) does not trap for performance reasons.

This can be resolved using direct binary translation (DBT), which has the VMM inspecting all privileged x86 instructions to be executed and replacing non-trapping instructions with explicit traps. This was first implemented in Dynamo that led to the foundation of VMWare. See link. Transparency is not violated, since the memory accesses itself can also be trapped.

To get rid of the significant overhead from instruction inspection, other techniques include:

VMM attack surface

Commercial VMMs are not fully transparent, which makes it possible to detect (e.g. for malware to use anti-VM techniques, or copy protection via VM-duplication). Known as the red-pill attack, and there are many vectors:

Despite inter-VM containment, because shared states are utilized, convert channels will exist. One example is via the use of shared cache latencies (e.g. sender deterministically performs a random memory access, and the receiver records bit 1 if long read time for fixed memory location, and 0 otherwise - CCS'09). Other channels via disk, I/O, virtualization latencies, exist as well.

Other readings:

System time can vary in VMs, will it affect virtualization detection techniques?

Trusted Execution Environments

To thwart the possibility of malicious OS layers loading before user OS layers, we can implement a hardware root-of-trust via the use of Trusted Execution Environments (TEEs), to allow assumptions of malicious software.

Examples of TEE implementations: TPM 1.2 (2004), TrustZone (2005), Intel TXT (2006), TPM 2.0 (2014), Intel SGX (2016).

Remote attestation is the first primitive implemented in TEE. During loading of each OS layer, a measurement is triggered on them and hashes are derived from them and stored in the TPM registers. The CPU cannot tamper with the TPM directly. All these hashes are digitally-signed by the TPM using a hardware AIK signing key, and then sent to a remote server for hash validation.

Loading of hardware key

There are methods where the manufacturer cannot tell what the signing key is, using physically-uncloneable functions (PUFs), e.g. via randomness in CMOS manufacturing process.

Trusted boot is enforced by implementing this remote attestation with a local secure storage.

Data sealing can also be performed by computing the hash of the software requesting the seal, and storing both the hashes and the disk encryption key is both stored in the secure storage. Unseals only if hash matches.

With these primitives, we can construct root-of-trust applications, e.g. Windows BitLocker performs block-level encryption of volumes, Linux TrustedGrub, eXtensible Modular Hypervisor Framework.

As opposed to static RTM which performs verification at load time, dynamic RTM is also possible using Intel TXT SKINT and SENTER instructions. See also the use of enclaves in iOS.


Week 12

Access control

Some definitions:

"Protection and access control in operating systems," Lampson72, defines the use of an explicit access control matrix to assign rights between users and resources. But this is not the only way to think about access control.

Problems with this OS model is with the presence of programs with ambient authority, e.g. cp program that is used by almost every user and has the authority to write to any file on the Android system, which then violates the principle of least privilege.

Capabilities

A different way of implementing access rights is to use a capability model, which is a (pointer, metadata) tuple embedding access rights. These are sufficient and necessary for access to objects, and can be created or modified only by querying the security monitor. Such capabilities however must be unforgeable.

cp < foo.txt > bar.txt
     |         +--- capability 2
     +--- capability 1

Note that while the security monitor is effectively an ambient authority, the security monitor can be embedded in hardware or enclaves for stronger protection guarantees.

A direct comparison:

Access control Capabilities
Pre-specified policies Needed, implemented centrally by security monitor None, follows natural flow of access rights (e.g. programs can pass on capabilities)
Revocation Yes, and easy to implement Possible by changing the capability on the object, but requires some careful design
Ambient authority Yes No
Assumptions Complete mediation (syscalls must make access checks) Unforgeability

The bulk of syscalls introduced into later versions of Android seem to come from Java semantics, e.g. for stack inspection, isolation. The base number of syscalls originates from UNIX (BSD-variant) syscalls. In contrast, the ioctl library seems to introduce less syscalls, likely because of the use of the Java higher level language for programs.

Types of access control

Checks for enforcement (e.g. process sandboxing, IRMs, capabilities, virtualization, TEE) come in the form of policy mechanisms. Three main types of access controls:

Difficulties in defining security policies

Confused deputy problem


Week 11

Reference monitors

Let's talk about the second line of defense, which is to minimize the impact of attacks that we know will happen. Two distinct methods:

  1. Reference monitors: A piece of code that checks all references to an object (e.g. memory space). These are typically inline, i.e. compiled/instrumented into the program, and thus share the same privileges as the program.
  2. System call sandboxing: A reference monitor with kernel privileges for protecting OS resource objects from an app.

Some checks that an inline reference monitor can check for includes:

Software Fault Isolation (SFI)

We illustrate one such inline reference monitor, whose goal is fault isolation (also known as "address sandboxing").

Threat model: Attacker controls all memory values in M.

Main idea is to insert some inline instrument inside the program (e.g. via recompiling), such that all memory accesses are forced to point to the memory region M only.

; Original code
mov	(ebp),eax
 
; Modified (instrumented) code
call	(InRange(ebp))	; checks if in defined memory range
jz	error_label	; jumps if Z flag set
mov	(ebp),eax	; move memory pointed by ebp

The checking function call can be made faster, by constraining the possible values of the memory address (i.e. coerce rather than check). This is known as Fast-SFI.

; Original code
mov	(ebp),eax
 
; Faster instrumented code
and	ebp,0x00ff
or	ebp,0xbe00
mov	(ebp),eax

This is still problematic, since one can force instructions to jump directly to the mov (ebp),eax. This can be prevented by using dedicated registers, e.g. reg1, that is outside the control of the attacker - the argument is that regardless of which program instruction you jump to, the memory addressed by reg1 will *always* be safe.

mov	ebp,reg1
and	reg1,0x00ff
or	reg1,0xbe00
mov	(reg1),eax

This simplifies the verification for Fast-SFI, which only involves checking three points:

  1. IRM instructions (after the coercion) exist before memory access
  2. All memory accesses uses the dedicated register
  3. Dedicated registers are only used in IRM instructions

Rule of thumb: We need to minimize the size of the TCB (trusted computing base) that we need to trust in order to verify the correctness of the program, and/or the security properties of the program.

Security principle 1: Minimize TCB

Reduce what one needs to trust, e.g., by separating verifier from the enforcement.

and a collorary:

Security principle 2: Separation of concerns

Separate the policy from its enforcement.

syscall sandboxing

Involves using the higher privileges of the OS/kernel to define syscall policies, e.g. "no exec() system calls", or even an exploit pattern "no exec()-after-read() system calls". This also has standard enumerations in Linux:

Security principle 3: Principle of Least Privilege

Each component should only have smallest set of necessary privileges.

A collorary implies that allowlist policies (e.g. "seccomp") are preferred over denylist policies (e.g. "no exec-after-read") which are much broader.

Privilege separation

The problem in many software involves the philosophy of bundling functionality. We want to avoid cascading failures when one component fails:

The solution is the use of privilege separation, i.e. compartmentalization and assignment of least privilege. This principle is used in the Google Chrome browser:


Week 10

Recall the goals of memory safety:

  1. Create memory pointers via permitted operations (as per usual)
  2. Only access memory allocated to pointer, with spatial (allocated memory range) and temporal (memory in scope) bounds

Implementation of spatial memory safety

We can achieve this either by compilation, binary rewriting, or more simply, insertion of memory tracking metadata and inline monitors (assuming such metadata can only be accessed by monitors, and cannot be corrupted).

Referent objects

Use of referent objects implementation for spatial memory safety involves using shadow memory, e.g. JK-Tree, Baggy BC.

struct { char str[8]; void (*func)(); } node;
char* ptr = node.str;
strcpy(ptr, "overflow...");  // bound for 'node' used for 'ptr'

Fat pointers

The problem of overlapping objects in assigning bounds is avoided by using fat pointers, i.e. pointers that directly store the bounds metadata. One such implementation is Softbound.

Basic implementation for fat pointer

Unsafe typecasting under fat pointers

In both approaches, runtime overhead is rather high, up to twice as long.

Implementation of temporal safety

We also need to track the creation and destruction of pointers, to ensure that deallocated pointers are not accessed. Simple mechanisms like a quick and dirty pointer nullification does not achieve temporal safety (there can be other objects that reference the same memory space, e.g. char* p = (char*) malloc(SIZE); char* q = p;).

Lock-and-key

An alternative implementation is to have a matching lock and key values iff the memory object is temporally safe to access. An overview implementation is shown below. Note that this mechanism provides complete temporal safety, so long as program has spatial safety (to ensure data does not overflow).

Basic implementation of lock-and-key

Performance overhead is still close to 200%, due dynamic checking.

Implementation of full memory safety

We can incorporate the concepts from both lock+key and fat pointers for full memory safety, but this incurs high performance overheads.

Smart pointers

This is the basis for C++17 smart pointers, and in Rust. Such smart pointers wrap pointer objects, with automatic allocation and deallocation, as well as preserve metadata about the resource object. Smart pointers own the resource object (and so can be solely responsible for freeing objects when the pointer goes out of scope), performs bounds-checking.

In C++17, this is implemented as:

In Rust, smart pointers are used by default. This allows for static rule baked into the language to avoid runtime checking.


Week 9

Temporal memory safety


Week 8

Spatial memory safety

Memory safety, with some useful references:

Buffer overflows

Format string vulnerabilities

In the code below, because printf first argument is treated as a format string, the user can inject format strings to read directly from the stack, i.e. AAAA %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x.

#include <stdio.h>
 
int main() {
    srand(time(NULL));
 
    char localStr[100];
    int magicNumber = rand() % 100;
    int userCode = 0xBBBBBBBB;
 
    printf("Username? ");
    fgets(localStr, sizeof(localStr), stdin);
 
    printf("Hello ");
    printf(localStr);
    printf("What is the access code? ");
 
    scanf("%d", &userCode);
    if (userCode == magicNumber) {
        printf("You win!\n");
    }
}

Other integer overflow bugs

Because C tries to be as generic as possible with data widths, implementation details are often left to the compilers, which can have different behavior depending on the platform. For integers, since the use of two's complement allows the use of the same operation,

Code injection

There are usually constraints to modifying the underlying binary, e.g. read-execute-only. Separate channels exist to modify writable sections of the running code at run time, e.g. by modifying the stack. Suppose an attacker wants to inject a payload that forks a new bash shell:

int main(int argc, char *argv[]) {
    char *sh;
    char *args[2];
 
    sh = "/bin/bash";
    args[0] = sh;
    args[1] = NULL;
    execve(sh, args, NULL);
}

which translates into the following shell code

During the buffer overflow earlier, we:

  1. inject said shellcode to store as instructions, and
  2. hijack the control flow by overriding the return address to the start of the shellcode.

Since it is difficult to determine the correct offset to reach the start of the shellcode, one can prepend a series of NOP instructions to the shellcode (execution will reach shellcode as long as return address points to somewhere within this NOP sled).

return-to-libc attack

Sometimes we don't even require the attack payload, such as in the return-to-libc attack that changes the return address to the execve line within the C library.


Week 6

How to attack system?

HTTP downgrade attacks

UI confusion attacks

CA / certificate attacks

Note

Interesting exercise hosted by Prateek: Ask how compromised CAs can be detected and avoided, then reflect and expand on suggestions.

Slides are a little old...? Certificate pinning is not commonly supported today.

Side-channel leakage

Side-channels are information leakage which are not intended, e.g. consider the RSA exponentiation algorithm:

$$$$ y = g^k \text{mod}\;N $$$$

can be decomposed into squaring-and-multiplying operations.

r0 = 1; r1 = g
for j = len(s)

Another example is the "Lucky Thirteen".

Compromise of crypto primitives

This is a pretty good resource to start getting into some legacy attacks:


Week 5

Computational hardness

Perfect secrecy is impractical due to the large key size requirement. We reduce the threat model into an attacker that is computationally bounded, i.e. the adversary can use all deterministic, efficient algorithms that run in polynomial time. This allows for constructions using PRGs that are computationally hard,

$$$$ \text{Enc}(k, m) = \text{PRG}(k) \oplus m $$$$

We can show that pseudorandom generators (stream ciphers) exist from the existence of one-way functions.

Side-note on the use of hard one-way functions:

Key exchange protocols

Two pioneers in key exchange:

Perfect forward secrecy achieved by discarding messages that performs the key generation (what about an on-path attacker that can passively log all transactions?).

Note that Diffie-Hellman (DH) is not a secure key exchange protocol, since it is susceptible to an MitM attack. Adding of signatures in DH Station-to-Station (STS) to sign the messages being sent, we can achieve an authenticated key exchange.

With computational hardness, we can distill more primitives, i.e. digital signatures, and authenticated key DHKE + signatures.

SSL/TLS and HTTPS

A high level overview of SSL/TLS goes roughly as follows:

  1. Negotiation phase (determine ciphersuite to use, making this protocol rather 'meta')
  2. Key exchange phase (distill share symmetric key)
  3. Symmetric key session generation
  4. (re-negotiation)

This is a nice diagrammatic overview:

Some notes:

When performing security analysis, we state the assumptions in the threat model (which should be likely to be true), and then formulate security arguments. In HTTPS, the assumptions of the threat model are:


Week 4

How to define a threat model?

Instead of having an attack-centric viewpoint, we focus on the capabilities of the attacker instead.

Strong threat model of network attacker: All traffic between parties can be intercepted. Additionally, eavesdropper can listen in, while malicious actor can modify as well.

Basic cryptographic primitives are elements like encryption, MAC and digital signatures. These can, in turn, provide security goals such as confidentiality, integrity, and authenticity.

To discuss security, we can adopt the following procedure:

  1. Define the (1) setup, (2) adversary capability, (3) security goal
  2. Construct a situation that satisfy the definition

To ensure a secure channel, we need to distill some sort of advantage over any network attacker, either:

  1. by assuming the eavesdropper has a poorer network
  2. by sharing some pre-shared information (and use probabilistic mechanisms)

Symmetric key encryption

Correctness is straightforward to define -> decryption of encrypted message returns the original message. Security on the other hand needs to be less dependent on attacker capabilities, e.g. "attacker cannot guess key from ciphertext" is dependent on attacker prior knowledge (such as cryptanalysis methods).

Threat model:

  1. Adversary knows all algorithms required to establish the secure channel
  2. Adversary knows any distribution of the message set M

In the context of a chosen plaintext attacker (CPA), the security goal is then defined as perfect secrecy, where probability of guessing message is independent of the given ciphertext:

$$$$ Pr[m_{guess}|c] = Pr[m_{guess}] $$$$

Please read the work of Shannon's.

We first consider a minimal one-bit encryption scheme, where $$ M, K, C \in \{0,1\} $$, to derive a scheme that exhibits both correctness and perfect secrecy. The space of possible functions is $$ 2^4 $$, but not all share correctness nor perfect secrecy:

Function Correctness Perfect secrecy
AND No: given c=0, m=? No: given c=1, we know m=1
c=m Yes No: given c=x, we know m=x
c=k No: given c=x, m=? Yes
random No Yes
XOR Yes: bijective function Yes: with uniform k, each value of m equally probable with each value of c

Main limitation is that the key space must be larger than message space.

Key expansion

In practical deployment, we run key expansion over a much smaller pre-shared key. This no longer fulfills the perfect secrecy condition (why?).

In a weaker threat model where the attacker does not have infinite computation power (i.e. bounded by computation in polynomial time), a weaker form of secrecy can still be guaranteed.

Message Authentication Code (MAC)

Integrity is provided by MAC, which has the following definition over the message, key and tag space $$ (K,M,T) $$:

The goal of the attacker is existential forgery under a chosen message attack (CMA), i.e. guess tag t from message m. The security goal is tend to

Consider again the one-bit MAC, where the tag space $$ |T| = 2^n = 1 $$. The signing algorithm cannot be XOR (as in OTP), because the key can be identified under a CMA (supply m = 0).

Note that the publicly available information now spans the space $$ (M,T) $$, which is two bits. The secret required to have a perfectly secure MAC is then to also have a two-bit secret. We can define the following signing algorithm with two one-bit secrets $$a$$ and $$b$$, and this falls under the family of universal hashes:

$$$$ t = (a\cdot{}m + b) \text{mod} 2 $$$$

Note again that if two combinations of (m,t) are generated with the same secrets a and b, then the secret can be extracted -> randomness over choice of (a,b).

Proof for \

This is the definit

Need to read through this again.


Week 2

Attacks on TCP

To elaborate on the securities afforded by the TCP protocol. Overview of TCP handshake - to authenticate the client to the server.

Need to distinguish what the threat model is when looking at vulnerabilities of the protocol, for example the TCP protocol is not designed to provide authentication on the OSI layer.

Attacks on DNS

Some vulnerabilities:

Here's a nice resource on security of DNS.

Overview of firewalls

A stateless firewall inspects packets and checks if they match rules. These devices need to have high overall throughput. Note that the device on which the firewall sits must also be able to support the TCP reconstruction process (why?). Other types of firewalls include stateful firewalls and application-layer firewalls.

On Linux, firewall is implemented within the netfiler framework (iptables), with different hooks for packets, e.g. INPUT, FORWARD, POST-ROUTING.

Some vulnerabilities:

Some mitigations:

Firewall threat model

Adversary capabilties:

  1. Adversary can send malicious network packets
  2. Adversary is outside the network perimeter

Assumptions:

  1. The network perimeter is correctly defined, which is not necessarily true in the context of "Bring-Your-Own-Device"
  2. Firewall is not compromised
  3. Firewall sees the same data as the endpoint, so for deeper packet inspection, the firewall itself needs to perform packet reconstruction as well (there is still always a semantic gap between firewall and end application)
  4. Defender's policy can distinguish good from bad traffic

Weakness is this threat model: defender needs to know every specific attack pattern for setting policy, and adversary can easily evade these assumptions.

The point of this section is to emphasize that thinking of the threat model conceptually is important for defending against attacks.


Week 1

Module admin

Lecturer: Prateek Saxena (consider the following publications: HTML5 privilege separation (Dropbox), Finding and preventing script injection vulnerabilities).

Content focuses on exploring the attack surface across multiple OSI layers, examples of attacks on each, and more importantly, the defenses that can be deployed. Different threat models can be explored1).

Assumed knowledge:

  1. Memory safety exploits
  2. Web security exploits
  3. Programming in C
  4. Access control in OS

We need to address not the perceived security risks (e.g. infant abduction vs infant mortality), but address the real security risks instead. Q: What security measure is worth deploying?

Other important information:

Introduction

The internet consisting of autonomous systems (AS) which function like a cloud of routers, that can intercommunicate using the BGP protocol (seems like an internal routing table within routers, with inter-router communication over 179/tcp). This is related to the concept of gateways.

Attacks on BGP

A good high-level overview of BGP. Some vulnerabilities:

There are mitigations, from Cloudflare advertises its nodes as a single ASN globally, then

Attacks on IP Procotol

1)
Threat model defines the desired security property / goal, the capabilities of the attacker, as well as assumptions of the setup