```c void print_msg(const char *msg) { printf("There is a problem here;\n"); printf(msg); } ``` - potentiel crash `null` - potentielle insertion de commandes printf $\rightarrow$ modifs sur la stack Fix : ```c void print_msg(const char *msg) { printf("There is a problem here\n"); printf("%s", msg); } ``` ## The LISP syndrome - Every call tends to evolve to do more than it's own good - Beware of abstraction layers violations ## Hidden Shells (injections de commandes shell) - e.g. `system(3)` et `popen(3)` $\rightarrow$ Learn Unix - use lower level features to have more control over user input - check syscall return values - or use `posix_spawn(3)` ### Creepy scripts A script called "s" : ```shell #!/bin/sh file=$1 rm $file ``` ```bash $ ./s "my_file" ``` ```sh #!/bin/sh rm "$@" ``` ```shell $ ./s ../myfile $ ./s -rf / ``` - quoting is not enough - use `cmd -- args` to stop option parsing - if you write your own commands don't allow reorder! - use `set -e` ## Misconceptions _It's too complicated, it won't be exploited_ : false - The IISS url overflow (bash commands injection) - **The venetian blind** ## Examples - Log4shell : faille sur Log4j, chargement et exécution de code à distance - Hardbuild : 2014, bug openssl $\rightarrow$ construire l'app de TODO comme un système bancaire ## Open VS Closed source - Closed source is **not** more secure - Lots of people know how to reverse-engineer - The "sweep under the carpet" effect : - protection de l'image des sociétés en niant les vulnérabilités - Accès au code source - Example: Crafting exploits from Windows Update (http://bitblaze.cs.berkeley.edu/papers/apeg.pdf) - Simple code should look simple: - make stuff explicit - depend on your compiler - use `strlcpy` - make the API handle sizes ## Sturgeon's law **90 % of all software is**: - crap - unimportant to optimize - bogus - copied and pasted - imperfect ## The Drepper fallacy - "But I don't write wrong code" : the reason for the slow adoption of `strlcpy` - You can't fix everything - ... therefore don't fix anything - "Low Hanging Fruit" ## The Unix security model - When do you check that you can access a file ? - at open and at exec : - identify who you are: **uid/gid** - don't forget supplementary groups - only check the first entry that applies - **if** uid == file owner, check user bits - **else** if one group matches file group, check group bits - **else** match other bits - see windows and ActiveDirectory - see PAM and its unreadable config files - if you are **root**: - We ignore rights - open the file - `fstat` to see if it worked $\implies$ pas obligé - rights of the process + every fd I own - **Priv Drop**: - start as root - do privileged operations yielding fds - ... then change identity - I still have the fds - example: network server on a privileged port - HOWTO: - `setgroups` `setuid` `setgid` - check that it worked - `setuid`: - effective id: demandé par l'application - saved id: ancien id - real id: owner of original process - access controlled by effective id - notion of **role**: - an identity (real or imaginary) that can **do things** and **access data** - stuff you can do - data you can read - data you can write ## Designing software - the more complex the code, the less rights it should have - sanitize input thoroughly - ... then you don't need more syntax checks internally - ... put checks at the semantic level where it makes sense - trust boundaries ## Designing software VS Unix - Separate roles should run as separate users - ... so make it simple to create users - $\implies$never reuse users for something else - the technical term for modern software with roles is *privilege separation* # Exemples - NATS fail : https://jameshaydon.github.io/nats-fail/ - [[Faille XZ]]