Explain and summarize the protection mechanism encountered in the pwn process.
Stack Canaries
Put a well written: PWN Canary learning - sarace - blog Park (cnblogs.com)
brief introduction
stack canaries are canaries from underground coal mines. They can detect gas leakage faster than miners and have the function of early warning. When this concept is applied to stack protection, a random canary value is set at the bottom of the stack when initializing a stack frame. Before the stack frame is destroyed, test whether the value is "dead", that is, whether it is changed. If it is changed, it indicates that stack overflow occurs and the program ends in another process, so as to avoid successful exploitation of vulnerabilities.
It is mainly divided into three categories: Terminator, random and random XOR. The specific implementations include StackGuard, StackShied, ProPoliced, etc.
-
terminator canaries: considering that many stack overflows are caused by improper string operations, and these strings end in NULL \x00 and are truncated by \ x00, all terminators set the low order to \ x00 to prevent disclosure and forgery. Truncated characters also include CR(0x0d),LF(0x0a), EOF(0xff). In fact, the highest position of the last part is 00, \ x00ab1245
-
random canaries: in order to prevent canaries from being guessed by attackers, canaries are usually randomly generated during program initialization and stored in a safe place.
-
random canaries XOR: in fact, there is one more XOR operation than random canaries. Whether the data of canaries or XOR is tampered, it will be detected. xor eax,DWORD PTR gs:0x14
Implementation principle
Under Linux, there is a fs register to save the thread local storage TLS. TLS is mainly used to avoid the conflict caused by multiple threads accessing the same global variable or static variable. 64 bit uses fs register, offset at 0x28. 32 bits are registered with gs, offset at 0x14. This location stores the stack_guard, i.e. reserved and Canary, is finally compared with canary in the stack to detect overflow.
The specific process is to use_ dl_random to generate stack_chk_guard, and then use THREAD_SET_STACK_GUARD to set the stack_ The lowest order of guard and canary is set to \ x00. If_ dl_random==NULL, then canary is the fixed value.
If the program does not define THREAD_SET_STACK_GUARD macro, then it will be used directly_ stack_chk_guard, which is a global variable placed in the. bss section.
TLS structure
x86 32-bit
mov eax,gs:0x14 mov DWORD PTR [ebp-0xc],eax mov eax,DWORD PTR [ebp-0xc] xor eax,DWORD PTR gs:0x14 je 0x80492b2 <vuln+103> # Normal function return call 0x8049380 <__stack_chk_fail_local> # Call error handler
High Address | | +-----------------+ | args | +-----------------+ | return address | +-----------------+ | old ebp | ebp => +-----------------+ | ebx | ebp-4 => +-----------------+ | unknown | ebp-8 => +-----------------+ | canary value | ebp-12 => +-----------------+ | local variable | Low | | Address
64 bit
mov rax,QWORD PTR fs:0x28 mov QWORD PTR [rbp-0x8],rax mov rax,QWORD PTR [rbp-0x8] xor rax,QWORD PTR fs:0x28 je 0x401232 <vuln+102> # Normal function return call 0x401040 <__stack_chk_fail@plt> # Call error handler
High Address | | +-----------------+ | args | +-----------------+ | return address | +-----------------+ | old ebp | rbp => +-----------------+ | canary value | rbp-8 => +-----------------+ | local variable | Low | | Address
experiment
Canary. C smash: smash, break, break
#include<tdio.h> int main(){ char buf[10]; scanf("%s",buf); }
gcc -fno-stack-protector canary.c -o canary_no.out gcc -fstack-protector canary.c -o canary_pro.out
Bypass mode
-
Leak canary in memory, such as printing through format string vulnerability
-
One by one blasting, but it is generally a multithreaded program. canary does not change until a new thread is generated. The highest bit is 00.
-
Hijack_ stack_chk_fail function. If canary verification fails, this function will be performed__ stack_ chk_ The fail function is a common delay binding function, which can be hijacked by modifying the GOT table.
-
Overwrite the canary in the thread local storage TLS. The overflow size is relatively large and can be used. Modify Canary on the stack and canary in TLS at the same time
No-eXecute(NX)
brief introduction
No execute (NX) means not executable. Its principle is to identify the memory page where the data is located as not executable.
In Linux, after the program is loaded into memory, the. text section is marked as executable, the. Data. BSS section is marked as non executable, and the stack is unknown. The traditional way of modifying the GOT table is no longer feasible. However, the code reuse attack ret2libc cannot be prevented
realization
Through the compilation option, strcmp comparison is used in_ handle_option function setting link_ The execstack and noexecstack of info structure are true and false.
In BFD_ elf_ size_ dynamic_ In the sections function, according to link_info to set elf_stack_flags = PF_R | PF_W | PF_X
When NX is turned on, there are only two without PF_X.
In_ bfd_ elf_ map_ sections_ to_ In the segments function, set stuct elf_ segment_ P in map structure_ flags=elf_ stack_ flags. The compilation setting is completed.
At load time, elf is called_ load_ Binary function, according to the above p_flags to set the executable_stack=EXSTACK_ENABLE_X
Or EXSTACK_DISABLE_X
Set executable_stack pass in setup_arg_pages, through vm_flags sets the virtual memory space vma of the process.
When the program counter points to an unknowable memory page, a page error is triggered.
experiment
nx.c
#include<unistd.h> void vuln_func(){ char buf[128]; read(STDIN_FILENO,buf,256); } int main(int argc , char*argv[]){ vuln_func(); write(STDOUT_FILENO,"Hello world!\n",13); }
ASLR and PIE
brief introduction
Most attacks need to know the memory layout of the program. Introducing randomization of memory layout can increase the difficulty of vulnerability exploitation. Address space layout randomization ASLR (address space layout randomization)
ASLR /proc/sys/kernel/randomize_ va_ There are three cases of space:
ASLR | Executable | PLT | Heap | Stack | Shared Libraries |
---|---|---|---|---|---|
0 | unchanged | unchanged | unchanged | unchanged | unchanged |
1 | unchanged | unchanged | unchanged | change | change |
2 | unchanged | unchanged | change | change | change |
2+pie | change | change | change | change | change |
PIE location independent executable is implemented on the compiler of the application layer. By compiling the program into location independent code PIC, the program can be loaded into any location, just like a special shared library. PIE can affect performance to some extent.
experiment:
#include<stdio.h> #include<stdlib.h> #include<dlfcn.h> int main(){ int stack; int *heap=malloc(sizeof(int)); void *handle = dlopen("libc.so.6",RTLD_NOW | RTLD_GLOBAL); printf("executable:%p\n",&main); printf("system@plt:%p\n",&system); printf("heap: %p\n",heap); printf("stack: %p\n",&stack); printf("libc: %p\n",handle); free(heap); return 0; }
cat /proc/sys/kernel/randomize_va_space echo 0/1/2 > /proc/sys/kernel/randomize_va_space
ASLR=2 and PIE is turned on
FORTIFY_SOURCE
brief introduction
Buffer overflow often occurs when the program calls some dangerous functions, such as memcpy. When the length of the source string is greater than the destination buffer, buffer overflow will occur.
FORTIFY_SOURCE is essentially a check and replace mechanism, a security patch for GCC and glibc.
Checking the danger function and replacing it with a safety function will not have a great impact on the performance of the program. Currently, it supports memcpy, memmove, memset, strcpy, strncpy, strcat, strncat,sprintf, vsprintf, snprintf, vsnprintf, gets, etc.
realization
Buffer overflow check to secure function_ strcpy_chk() as an example, you can see that this function determines whether the source data length is greater than the destination buffer. If yes, it is called_ chk_fail(), otherwise memcpy will be called to execute normally.
Format string check to secure function_ printf_chk() as an example, for% N and% n $format strings.
experiment
#include<stdio.h> #include<string.h> #include<stdlib.h> int main(int argc, char*argv[]){ char buf1[10],buf2[10],*s; int num; memcpy(buf1,argv[1],10); //safe strcpy(buf2,"AAAABBBBC"); printf("%s %s\n",buf1,buf2); memcpy(buf1,argv[2],atoi(argv[3])); //unknown strcpy(buf2,argv[1]); printf("%s %s\n",buf1,buf2); //memcpy(buf1,argv[1],11); //unsafe //strcpy(buf2,"AAAABBBBCC"); s=fgets(buf1,11,stdin); //fmt unknown printf(buf1,&num); }
Use GDB pwndbg to decompile main
Generate fortify0 1 2 using options 0 1 2
GDB pwndbg fortify1, you can see that the security function is replaced, but printf is not replaced.
Dump of assembler code for function main: 0x0000000000001175 <+0>: push r12 0x0000000000001177 <+2>: push rbp 0x0000000000001178 <+3>: push rbx 0x0000000000001179 <+4>: sub rsp,0x20 0x000000000000117d <+8>: mov rbx,rsi 0x0000000000001180 <+11>: mov rax,QWORD PTR [rsi+0x8] 0x0000000000001184 <+15>: mov rdx,QWORD PTR [rax] 0x0000000000001187 <+18>: mov QWORD PTR [rsp+0x16],rdx 0x000000000000118c <+23>: movzx eax,WORD PTR [rax+0x8] 0x0000000000001190 <+27>: mov WORD PTR [rsp+0x1e],ax 0x0000000000001195 <+32>: movabs rax,0x4242424241414141 0x000000000000119f <+42>: mov QWORD PTR [rsp+0xc],rax 0x00000000000011a4 <+47>: mov WORD PTR [rsp+0x14],0x43 0x00000000000011ab <+54>: lea r12,[rsp+0xc] 0x00000000000011b0 <+59>: lea rbp,[rsp+0x16] 0x00000000000011b5 <+64>: mov rdx,r12 0x00000000000011b8 <+67>: mov rsi,rbp 0x00000000000011bb <+70>: lea rdi,[rip+0xe42] # 0x2004 0x00000000000011c2 <+77>: mov eax,0x0 0x00000000000011c7 <+82>: call 0x1030 <printf@plt> 0x00000000000011cc <+87>: mov rdi,QWORD PTR [rbx+0x18] 0x00000000000011d0 <+91>: mov edx,0xa 0x00000000000011d5 <+96>: mov esi,0x0 0x00000000000011da <+101>: call 0x1050 <strtol@plt> 0x00000000000011df <+106>: movsxd rdx,eax 0x00000000000011e2 <+109>: mov rsi,QWORD PTR [rbx+0x10] 0x00000000000011e6 <+113>: mov ecx,0xa 0x00000000000011eb <+118>: mov rdi,rbp 0x00000000000011ee <+121>: call 0x1040 <__memcpy_chk@plt> 0x00000000000011f3 <+126>: mov rsi,QWORD PTR [rbx+0x8] 0x00000000000011f7 <+130>: mov edx,0xa 0x00000000000011fc <+135>: mov rdi,r12 0x00000000000011ff <+138>: call 0x1070 <__strcpy_chk@plt> 0x0000000000001204 <+143>: mov rdx,r12 0x0000000000001207 <+146>: mov rsi,rbp 0x000000000000120a <+149>: lea rdi,[rip+0xdf3] # 0x2004 0x0000000000001211 <+156>: mov eax,0x0 0x0000000000001216 <+161>: call 0x1030 <printf@plt> 0x000000000000121b <+166>: mov rsi,QWORD PTR [rbx+0x8] 0x000000000000121f <+170>: mov ecx,0xa 0x0000000000001224 <+175>: mov edx,0xb 0x0000000000001229 <+180>: mov rdi,rbp 0x000000000000122c <+183>: call 0x1040 <__memcpy_chk@plt> 0x0000000000001231 <+188>: mov edx,0xa 0x0000000000001236 <+193>: lea rsi,[rip+0xdce] # 0x200b 0x000000000000123d <+200>: mov rdi,r12 0x0000000000001240 <+203>: call 0x1070 <__strcpy_chk@plt> 0x0000000000001245 <+208>: mov rcx,QWORD PTR [rip+0x2e04] # 0x4050 <stdin@GLIBC_2.2.5> 0x000000000000124c <+215>: mov edx,0xb 0x0000000000001251 <+220>: mov esi,0xa 0x0000000000001256 <+225>: mov rdi,rbp 0x0000000000001259 <+228>: call 0x1060 <__fgets_chk@plt> 0x000000000000125e <+233>: lea rsi,[rsp+0x8] 0x0000000000001263 <+238>: mov rdi,rbp 0x0000000000001266 <+241>: mov eax,0x0 0x000000000000126b <+246>: call 0x1030 <printf@plt> 0x0000000000001270 <+251>: mov eax,0x0 0x0000000000001275 <+256>: add rsp,0x20 0x0000000000001279 <+260>: pop rbx 0x000000000000127a <+261>: pop rbp 0x000000000000127b <+262>: pop r12 0x000000000000127d <+264>: ret End of assembler dump.
GDB pwndbg fortify2 disas main, you can see that printf has also been replaced with a security function.
Dump of assembler code for function main: 0x0000000000001175 <+0>: push r12 0x0000000000001177 <+2>: push rbp 0x0000000000001178 <+3>: push rbx 0x0000000000001179 <+4>: sub rsp,0x20 0x000000000000117d <+8>: mov rbx,rsi 0x0000000000001180 <+11>: mov rax,QWORD PTR [rsi+0x8] 0x0000000000001184 <+15>: mov rdx,QWORD PTR [rax] 0x0000000000001187 <+18>: mov QWORD PTR [rsp+0x16],rdx 0x000000000000118c <+23>: movzx eax,WORD PTR [rax+0x8] 0x0000000000001190 <+27>: mov WORD PTR [rsp+0x1e],ax 0x0000000000001195 <+32>: movabs rax,0x4242424241414141 0x000000000000119f <+42>: mov QWORD PTR [rsp+0xc],rax 0x00000000000011a4 <+47>: mov WORD PTR [rsp+0x14],0x43 0x00000000000011ab <+54>: lea r12,[rsp+0xc] 0x00000000000011b0 <+59>: lea rbp,[rsp+0x16] 0x00000000000011b5 <+64>: mov rcx,r12 0x00000000000011b8 <+67>: mov rdx,rbp 0x00000000000011bb <+70>: lea rsi,[rip+0xe42] # 0x2004 0x00000000000011c2 <+77>: mov edi,0x1 0x00000000000011c7 <+82>: mov eax,0x0 0x00000000000011cc <+87>: call 0x1070 <__printf_chk@plt> 0x00000000000011d1 <+92>: mov rdi,QWORD PTR [rbx+0x18] 0x00000000000011d5 <+96>: mov edx,0xa 0x00000000000011da <+101>: mov esi,0x0 0x00000000000011df <+106>: call 0x1040 <strtol@plt> 0x00000000000011e4 <+111>: movsxd rdx,eax 0x00000000000011e7 <+114>: mov rsi,QWORD PTR [rbx+0x10] 0x00000000000011eb <+118>: mov ecx,0xa 0x00000000000011f0 <+123>: mov rdi,rbp 0x00000000000011f3 <+126>: call 0x1030 <__memcpy_chk@plt> 0x00000000000011f8 <+131>: mov rsi,QWORD PTR [rbx+0x8] 0x00000000000011fc <+135>: mov edx,0xa 0x0000000000001201 <+140>: mov rdi,r12 0x0000000000001204 <+143>: call 0x1060 <__strcpy_chk@plt> 0x0000000000001209 <+148>: mov rcx,r12 0x000000000000120c <+151>: mov rdx,rbp 0x000000000000120f <+154>: lea rsi,[rip+0xdee] # 0x2004 0x0000000000001216 <+161>: mov edi,0x1 0x000000000000121b <+166>: mov eax,0x0 0x0000000000001220 <+171>: call 0x1070 <__printf_chk@plt> 0x0000000000001225 <+176>: mov rsi,QWORD PTR [rbx+0x8] 0x0000000000001229 <+180>: mov ecx,0xa 0x000000000000122e <+185>: mov edx,0xb 0x0000000000001233 <+190>: mov rdi,rbp 0x0000000000001236 <+193>: call 0x1030 <__memcpy_chk@plt> 0x000000000000123b <+198>: mov edx,0xa 0x0000000000001240 <+203>: lea rsi,[rip+0xdc4] # 0x200b 0x0000000000001247 <+210>: mov rdi,r12 0x000000000000124a <+213>: call 0x1060 <__strcpy_chk@plt> 0x000000000000124f <+218>: mov rcx,QWORD PTR [rip+0x2dfa] # 0x4050 <stdin@GLIBC_2.2.5> 0x0000000000001256 <+225>: mov edx,0xb 0x000000000000125b <+230>: mov esi,0xa 0x0000000000001260 <+235>: mov rdi,rbp 0x0000000000001263 <+238>: call 0x1050 <__fgets_chk@plt> 0x0000000000001268 <+243>: lea rdx,[rsp+0x8] 0x000000000000126d <+248>: mov rsi,rbp 0x0000000000001270 <+251>: mov edi,0x1 0x0000000000001275 <+256>: mov eax,0x0 0x000000000000127a <+261>: call 0x1070 <__printf_chk@plt> 0x000000000000127f <+266>: mov eax,0x0 0x0000000000001284 <+271>: add rsp,0x20 0x0000000000001288 <+275>: pop rbx 0x0000000000001289 <+276>: pop rbp 0x000000000000128a <+277>: pop r12 0x000000000000128c <+279>: ret End of assembler dump.
According to the fortify1 test result, an overflow occurred in strcpy and was detected. However, the format string vulnerability can still be used.
Using fortify2 experiment,% N and% n $were detected. And% n $needs to be continuously available after% 1$x. only one is printed in the figure below.
RELRO
brief introduction
When delay binding is enabled, symbol parsing only occurs when it is used for the first time. This process is carried out through the PLT table. After the parsing is completed, the corresponding GOT table entry will be modified to the correct function address. Therefore, in the case of delayed binding,. got.plt must be writable. An attacker can hijack the program by tampering with the address.
RELRO (relocation read only) mechanism is proposed to solve the security problem of delayed binding. Set the symbol redirection table to read-only, or resolve and bind all dynamic symbols when the program starts, so as to prevent GOT from being tampered with. There are two forms of RELRO:
-
Partial RELRO: some segments (. Dynamic,. Got, etc.) will be marked as read-only after initialization. They are enabled by default.
-
Full relro: except for Partial RELRO, delayed binding is prohibited. All imported symbols will be parsed at the beginning. The. got.plt segment will be fully initialized as the final address of the objective function and marked as read-only by mprotect. However, the. got.plt will be merged into. got, and this segment will not be seen. Will affect performance.
experiment
relro.c means to enter a hexadecimal address and write 4141 to that address
#include<stdio.h> #include<stdlib.h> int main(int argc,char*argv[]){ printf("hello"); printf("%s",argv[1]); printf("sdsd"); size_t * p=(size_t*)strtol(argv[1],NULL,16); p[0]=0x41414141; printf("RELRO: %x\n",(unsigned int )*p); return 0; }
The experiment failed. According to the book, there was a problem
In the dynamic relocation table, main is always R_X86_64_GLOB_DAT, the book should be the same as printf.
Conclusion: norelro can modify. got and. got.plt
partial can modify. got.plt
None of full can be modified
The conditions that cannot be modified are as follows. Of course, there may be other reasons
realization
When there is delayed binding, call will jump to printf@plt Then jmp to the. got.plt item, and then jump back for symbol binding. After completion, the. got.plt is modified to the real function address.
When there is no delay binding, all parsing work is completed when the program is loaded. Execute the call instruction to jump to the corresponding. plt.got item, and then jmp to the corresponding. Got item. The parsed function address has been saved.
Compilation options summary
stack canaries
-fstack-protector yes alloca Series of functions and functions with internal buffers greater than 8 bytes enable protection -fstack-protector-strong Added protection for functions containing local array and address references -fstack-protector-all Enable protection for all functions -fstack-protector-explicit To include stack_protect Property to enable protection -fno-stack-protector Disable protection
nx
-z execstack -z no execstack
ASLR
-ldl
PIE
-fpic Generate location independent code for shared libraries -pie The location of the generated dynamic link is independent of the executable file, which usually needs to be specified at the same time-fpie -no-pie Do not generate dynamically linked location independent executables -fpie be similar to-fpic,However, the generated location independent code can only be used for executable files -fno-pie Do not generate location independent code
FORTIFY_SOURCE
-D_FORTIFY_SOURCE=1 Enable buffer overflow attack check -D_FORTIFY_SOURCE=2 Enable buffer overflow and format string attack checking
RELRO
-z norelro Disable relro -z lazy open Partial RELRO -z now FULL PARTIAL