0% found this document useful (0 votes)
16 views56 pages

Introduction To Software Security (2025)

The document introduces software security fundamentals, emphasizing the importance of meeting both functional and non-functional requirements, including security. It discusses software vulnerabilities, their lifecycle, and the distinction between vulnerabilities and exploits, as well as the methods for collecting and categorizing vulnerabilities. Additionally, it provides examples of vulnerabilities in UNIX-like systems and the implications of privilege escalation through SUID programs.

Uploaded by

u1978867
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
16 views56 pages

Introduction To Software Security (2025)

The document introduces software security fundamentals, emphasizing the importance of meeting both functional and non-functional requirements, including security. It discusses software vulnerabilities, their lifecycle, and the distinction between vulnerabilities and exploits, as well as the methods for collecting and categorizing vulnerabilities. Additionally, it provides examples of vulnerabilities in UNIX-like systems and the implications of privilege escalation through SUID programs.

Uploaded by

u1978867
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

5.

Introduction to
Software Security
Computer Security Courses @ POLIMI
Software security fundamentals
Good software engineering: meet requirements
● Functional requirements
○ Software must do what it is designed for.
● Non-functional requirements
○ Usability
○ Safety
○ Security
● Creating inherently secure applications is a
fundamental, yet often unknown, skill for a good
developer or software engineer.
○ Creating secure software is hard.
○ Proof: see next slides. 2
Software has Vulnerabilities
Software should implement the specifications
● Unmet specification == software bug
● Unmet security specification == vulnerability

A way to leverage a vulnerability to violate the


CIA is called exploit.
● Vulnerability != exploit.

3
Life of a SW vulnerability
Known Software Vulnerabilities

Source: NIST' National Vulnerability Database


5
Known Software Vulnerabilities

Source: NIST' National Vulnerability Database


6
NON DISCLOSURE
Known Software Vulnerabilities

Source: NIST' National Vulnerability Database


7
NON DISCLOSURE

(FULL) DISCLOSURE
Known Software Vulnerabilities

Source: NIST' National Vulnerability Database


8
The Early Days of Disclosure
Subject: Comments on the dvwssr.dll vulnerability threads
From: Iván Arce
Date: 2000-04-18 1:25:52
I do not intend to go further down the full disclosure vs. mediated release of
information discussion here, however [Microsoft’s handler’s] post on NTBugtraq
regarding CORE's work requires some clarifications on our side.
[...]
If someone yells 'FIRE' and that appears to be reasonable, I'd would be very
careful in my methodology and editorial policies before yelling "NOT TRUE!
NOT TRUE! EVERYTHING IS FINE!".
[...]
Excuse me if I’m being rude, but I’m shocked by the fact that our company
is being questioned because we found a bug.

http://www.s0ftpj.org/bfi/online/bfi10/BFi10-02.html
http://marc.info/?l=vuln-dev&m=95602682515862&w=2 9
The (full) Disclosure Vuln. Lifecycle

Leyla Bilge, Tudor Dumitras, Before We Knew It: An Empirical Study of Zero-Day Attacks In The
Real World, ACM CCS 2012.

10
The Black Hat Parties

11
NON DISCLOSURE

(FULL) DISCLOSURE
Known Software Vulnerabilities

Source: NIST' National Vulnerability Database


12
NON DISCLOSURE

(FULL) DISCLOSURE

ANTI DISCLOSURE
Known Software Vulnerabilities

Source: NIST' National Vulnerability Database


13
14
Black Market of Exploits

15
Bug Bounties

16
More Bug Bounties (bugcrowd.com)

17
Collection and Categorization of
Vulnerabilities Overview
How Vulnerabilities Are Collected:
● Public Disclosure
● Bug Bounty Programs
● Automated Scanning
● Incident Response
Categorization of Vulnerabilities:
● CVE (Common Vulnerabilities and Exposures): Unique identifier
and detailed description.
● CWE (Common Weakness Enumeration): Classifies vulnerabilities
● CVSS (Common Vulnerability Scoring System): Rates severity
based on exploitability and impact.
● CAPEC (Common Attack Pattern Enumeration and Classification):
Documents common attack methods.
Modern Tools and Platforms:
● National Vulnerability Database (NVD)
● MITRE ATT&CK Framework
● Vulnerability/Exploit databases like VulnDB, Exploit-DB

18
CVE/CWE/CVSS Examples

19
Vulnerability and Exploit Example
Processes in UNIX-like systems.
Vulnerability Example
Every file has a owner (user):

creates owner:foo

foo executable

21
Vulnerability Example
Every file has a owner (user):
[bar@localhost]$ ls -la executable
-rwxr-xr-x 1 foo group 41836 2012-10-14 19:19 executable

creates owner:foo

foo executable

a
-l
ls

bar
22
Vulnerability Example
Every file has a owner (user):
[bar@localhost]$ ls -la executable
-rwxr-xr-x 1 foo group 41836 2012-10-14 19:19 executable

Real UID (RUID): real owner of a process.


[bar@localhost]$ ./executable #Darwin Kernel 13.1.0
[bar@localhost]$ ps -a -x -o user,pid,cmd
USER PID COMMAND
bar 18299 ./executable

The RUID could differ from the owner.

23
Real UID (RUID): real owner of a process

owner:foo
./executable

bar executable

RUID:bar

process
pid 18299
Vulnerability Example
Every file has a owner (user):
[bar@localhost]$ ls -la executable
-rwxr-xr-x 1 foo group 41836 2012-10-14 19:19 executable

Real UID (RUID): real owner of a process.


[bar@localhost]$ ./executable #Darwin Kernel 13.1.0
[bar@localhost]$ ps -a -x -o user,pid,cmd
USER PID COMMAND
bar 18299 ./executable

The RUID could differ from the owner.


Effective UID (EUID): UID for checking permissions
25
Vulnerability Example
Every file has a owner (user):
[bar@localhost]$ ls -la executable
-rwxr-xr-x 1 foo group 41836 2012-10-14 19:19 executable

Real UID (RUID): real owner of a process.


[bar@localhost]$ ./executable #Darwin Kernel 13.1.0
[bar@localhost]$ ps -a -x -o user,pid,cmd
USER PID COMMAND
bar 18299 ./executable

The RUID could differ from the owner.


Normally: RUID == Effective UID (EUID).
26
Effective UID (EUID): UID for checking permissions

foo
r|w|x
rwx------

foo file
owner:foo
./executable

bar executable

process
RUID:bar
pid 18299
Effective UID (EUID): UID for checking permissions

foo
r|w|x
rwx------

foo file
owner:foo
./executable

r|w|x ?
bar executable

EUID == bar
process
RUID:bar
pid 18299

Normally: RUID == Effective UID (EUID).


Effective UID (EUID): UID for checking permissions

foo
r|w|x
rwx------

foo file
owner:foo
./executable Check EUID
EUID == bar != foo
r|w|x ?
bar executable

EUID == bar
process
RUID:bar
pid 18299

Normally: RUID == Effective UID (EUID).


Vulnerability Example
Saved set-user-ID (SUID) can be used to
change the EUID at runtime.
[root@localhost]# chmod u+s executable
[root@localhost]# ls -la executable
-rwsr-xr-x 1 foo group 41836 2012-10-14 19:19 executable

Now the executable's SUID is "foo".


[bar@localhost]$ ./executable
[bar@localhost]$ ps -a -x -o user,pid,cmd
USER PID COMMAND
foo 18299 ./executable

"bar" == real UID != EUID == "foo".


30
Now the executable's SUID is "foo".

foo
r|w|x
rwx------

foo file
owner:foo
./executable Check EUID
SUID:foo EUID == foo
r|w|x
bar executable

EUID:foo
process
RUID:bar
pid 18299

"bar" == real UID != EUID == "foo"


Vulnerability Example
#include <unistd.h>
#include <stdio.h>

int main(int argc, const char *argv[])


{
printf("RUID %d EUID %d", getuid(), geteuid());
return 0;
}

32
Vulnerability Example
#include <unistd.h>
#include <stdio.h>

int main(int argc, const char *argv[])


{
printf("RUID %d EUID %d", getuid(), geteuid());
return 0;
}

[foo@localhost]$ gcc -o executable -c executable.c


[foo@localhost]$ sudo su - # become root
[root@localhost]# chown root # change the owner
[root@localhost]# chmod +s executable # set the SUID root bit
[root@localhost]# exit # get back to foo
[foo@localhost]$ ls -la executable # check the flags
-rwsr-xr-x 1 root group 41836 2012-10-14 19:19 executable

33
Vulnerability Example
[foo@localhost]$ ./executable
RUID 501 Effective 0 # 501 is foo's UID - 0 is root's UID

[foo@localhost]$ sudo -u root ./executable


RUID 0 Effective 0

[foo@localhost]$ vim executable.c // let's add a privileged instruction


#include <unistd.h>
#include <stdio.h>

int main(int argc, const char *argv[])


{
FILE * fp;
char line[1024];

printf("Real %d Effective %d", getuid(), geteuid());

fp = fopen("/etc/secret", "r"); // /etc/secret can be read only by root


while (!feof(fp)) {
fgets(line, 1024, fp);
puts(line);
}
fclose(fp);
return 0; 34
}
Vulnerability Example
[foo@localhost]$ ./executable
RUID 501 Effective 0 # 501 is foo's UID - 0 is root's UID

[foo@localhost]$ sudo -u root ./executable


RUID 0 Effective 0

[foo@localhost]$ vim executable.c // let's add a privileged instruction


#include <unistd.h>
#include <stdio.h>

int main(int argc, const char *argv[])


{
FILE * fp;
char line[1024];

printf("Real %d Effective %d", getuid(), geteuid());

fp = fopen("/etc/secret", "r"); // /etc/secret can be read only by root


while (!feof(fp)) {
fgets(line, 1024, fp);
puts(line);
}
fclose(fp);
return 0; 35
}
Vulnerability Example
[foo@localhost]$ ./executable
RUID 501 Effective 0 # 501 is foo's UID - 0 is root's UID

[foo@localhost]$ sudo -u root ./executable


RUID 0 Effective 0

[foo@localhost]$ vim executable.c // let's add a privileged instruction


#include <unistd.h>
#include <stdio.h>

int main(int argc, const char *argv[])


{
FILE * fp;
char line[1024];

printf("Real %d Effective %d", getuid(), geteuid());

fp = fopen("/etc/secret", "r");
while (!feof(fp)) {
fgets(line, 1024, fp);
puts(line);
}
fclose(fp);
return 0; 36
}
Vulnerability Example (4)
Let’s check the permission associated to
/etc/secret
[foo@localhost]$ ls -la /etc/secret
-rwx------ 1 root wheel 12 Mar 10 16:07 /etc/secret
Vulnerability Example
[foo@localhost]$ ./executable
RUID 501 Effective 0 # 501 is foo's UID - 0 is root's UID

[foo@localhost]$ sudo -u root ./executable


RUID 0 Effective 0

[foo@localhost]$ vim executable.c // let's add a privileged instruction


#include <unistd.h>
#include <stdio.h>

int main(int argc, const char *argv[])


{
FILE * fp;
char line[1024];

printf("Real %d Effective %d", getuid(), geteuid());

fp = fopen("/etc/secret", "r"); // /etc/secret can be read only by root


while (!feof(fp)) {
fgets(line, 1024, fp);
puts(line);
}
fclose(fp);
return 0; 38
}
Vulnerability Example

[foo@localhost]$ ls -la /etc/secret


-rwx------ 1 root wheel 12 Mar 10 16:07 /etc/secret

[foo@localhost]$ ./executable

What happens if we execute the binary file now ?

39
Vulnerability Example
Programs are "SUID root" to allow them to
execute privileged instructions.
[foo@localhost]$ ls -la /etc/secret
-rwx------ 1 root wheel 12 Mar 10 16:07 /etc/secret

[foo@localhost]$ ./executable
Real 501 Effective 0
s3cr3t inf0

What if these programs have vulnerabilities or


bugs ?

40
A serious issue....
#include <unistd.h>
#include <stdio.h>

int main(int argc, const char *argv[])


{
FILE * fp;
char line[1024];

printf("Real %d Effective %d", getuid(), geteuid());

fp = fopen("/etc/secret", "r"); // /etc/secret can be read only by root


while (!feof(fp)) {
fgets(line, 1024, fp);
puts(line);
}
fclose(fp);
return 0;
}
Vulnerability Example
The EUID should be changed back once the
privileged instructions are done.
#include <unistd.h>
#include <stdio.h>

int main(int argc, const char *argv[])


{
// execute as EUID -------------------------
char line[1024];
FILE * fp;

printf("Real %d Effective %d\n", getuid(), geteuid());

fp = fopen("/etc/secret", "r");
fgets(line, 1024, fp);
fclose(fp);

setuid(501); //execute as unprivileged user


printf("Real %d Effective %d\n", getuid(), geteuid());

puts(line);

return 0; 42
}
Vulnerability Example
[foo@localhost]$ ./executable
Real 501 Effective 0
Real 501 Effective 501
s3cr3t inf0 # <- content of /etc/secret

Once we read the file, we release the privileges.

The subsequent instructions are executed with


foo's privileges.

43
Vulnerability and Exploit
Vulnerable program (pseudocode)

EUID: RUID -> SUID


read(config)
r = parse(config)
IF r = OK do_things() ELSE
error(“...”)

The read(config) function prints the content


of the file in the error message.

44
Vulnerability and Exploit
Vulnerable program

EUID: RUID -> SUID


read(config)
r = parse(config)
IF r = OK do_things() ELSE
error(“...”)

[user@host]$ ./ex /etc/shadow

ERROR in file, line 1:


root:<password hash>: ...

The read(config) function prints the content


of the file in the error message. This allows an
unprivileged user to print the content of, e.g., the
/etc/shadow file, which can be normally read
only by privileged users. 45
Exploit patching
Vulnerable program Fixed program

EUID: RUID -> SUID EUID: SUID -> RUID


read(config) read(config) //low privs
r = parse(config) EUID: RUID -> SUID
IF r = OK do_things() ELSE r = parse(config)
error(“...”) IF r = OK do_things() ELSE
error(“...”)
[user@host]$ ./ex /etc/shadow [user@host]$ ./ex /etc/shadow

ERROR in file, line 1: Permission denied.


root:<password hash>: ...

The read(config) function prints the content By acquiring higher privileges only after the file is
of the file in the error message. This allows an read, the developer decreases the attack surface
unprivileged user to print the content of, e.g., the and effectively eliminates this specific
/etc/shadow file, which can be normally read vulnerability (there may be other vulnerabilities).
only by privileged users. 46
What else?
Could you spot the other vulnerability in the
code snippet on the right?
General Idea (pseudocode)
(still) vulnerable program

EUID: SUID -> RUID


read(config) //low privs
EUID: RUID -> SUID
r = parse(config)
IF r = OK do_things() ELSE
error(“...”)

48
General Idea (pseudocode)
(still) vulnerable program

EUID: SUID -> RUID


read(config) //low privs
EUID: RUID -> SUID
r = parse(config)
IF r = OK do_things() ELSE
error(“...”)

[user@host]$ ./ex
carefully-crafted-file

Any bug in the parse(config) function would


happen in a privileged portion of the code,
therefore potentially allowing the attacker to
perform actions
49
General Idea (pseudocode)
(still) vulnerable program Fixed program

EUID: SUID -> RUID EUID: SUID -> RUID


read(config) //low privs read(config) //low privs
EUID: RUID -> SUID r = parse(config)
r = parse(config) IF r = OK
IF r = OK do_things() ELSE EUID: RUID -> SUID
error(“...”) do_things()
EUID: SUID -> RUID
ELSE error(“...”)
[user@host]$ ./ex
carefully-crafted-file

Any bug in the parse(config) function would By acquiring the privileges as late as possible,
happen in a privileged portion of the code, and releasing them as soon as possible, the
therefore potentially allowing the attacker to developer decreases further the attack surface
perform actions (but there may still be other vulnerabilities in the
“do_things()” part of code). 50
Vulnerability vs. Exploit (Examples)

The developer acquired the Invocation of the program


privileges before with /etc/shadow as the
read(config) first argument.

The developer acquired the Invocation of the program


privileges before on a specifically crafted file
to exploit a vulnerability
parse(config)
inside the configuration file

51
Key Issues in Secure Design /
Principle of Secure Design (1)
Reduce privileged parts to a minimum.

KISS (Keep It Simple, Stupid).

Discard privileges definitively (i.e. SUID->RUID) as


soon as possible

Open design: just as with Kerckhoffs principle, the


program must not rely on obscurity for security.

Concurrency and race conditions are tricky.


52
Key Issues in Secure Design /
Principle of Secure Design (2)
Fail-safe and default deny.

Avoid the use of:


● shared resources (e.g. mktemp).
● unknown, untrusted libraries.

Filter the input and the output.

Do not write any own cryptographic primitives,


password, and secret management code: use trusted
code that has been audited already.

Use trustworthy RNGs such as /dev/[u]random 53


Code Security by Example
We will see 5 main examples of (in)secure
programming:
● Memory errors in desktop applications
○ Buffer overflow bugs
○ Format string bugs
● Code-injection bugs in web applications
○ SQL injection bugs
○ Cross site scripting bugs
○ CSRF
There are many other examples. We will just
deal with a few cases.
54
Conclusion
Bug-free software does not exist.

Not all bugs lead to vulnerabilities.

Vulnerability-free software is difficult to achieve.

Vulnerabilities without a working exploit exist.

Be careful with the SUID permission bit. 55


Material
Section 7.5, 10.6 of D. Gollman, “Computer
Security”, Wiley (3rd ed.).

"Advanced Linux Programming", chapter 10

56

You might also like