Fejlesztése shell-kódot Linux és BSD, Linux exp-csoport

A Shell-kód - sorozatát gépi utasításokat, amelyekkel a már futó program kényszerítettek csinál valamit alternatíva. Ezzel a módszerrel meg lehet kihasználni egyes szoftverek sebezhetőségét (például veremtúlcsordulás túlfolyó kupac sérülékenység Cut sorok).

Egy példa lehet kinézni shell-kód:

Azaz, általában elmondható, hogy a bájtok sorozata gépi kódban. E dokumentum -, hogy fontolja meg a legelterjedtebb módszerek shell-kód fejlesztése Linux és BSD rendszer fut x86 architektúra.

Ásni a hálózaton, akkor könnyen talál kész példákat shell-kód, amit csak lehet másolni, és tegye a megfelelő helyre. Mert mit tanulni a fejlődés? Az én ízlésemnek, van legalább egy pár jó oka van:

- Másodszor, emlékeztetni kell arra, hogy a shell-kódot lehet futtatni a teljesen más környezetben, például input-output szürőrészeken húr manipuláció, IDS, valamint hasznos elképzelni, hogyan kell módosítani feltételekkel összhangban;

- Továbbá, a koncepció a kiaknázása sérülékenységek segít írni biztonságosabb kódot.

1. listán rendszer kéri meghatározott /usr/src/linux/include/asm-i386/unistd.h file; mindegyike saját számot

Linux rendszer hívások
Bár a shell-kódot, elvileg tud végezni valamit, de a fő célja a kezdet az, hogy a célgép hozzáférést biztosít a shell (héj), lehetőleg egy kiváltságos mód, ahol, sőt, adta a nevét shell-kódot.
A legegyszerűbb és legkézenfekvőbb módja egy nehéz feladat assembly nyelv használatát rendszer hívásokat. A rendszer kéri biztosítja a felületet a felhasználói tér és kernel space; Más szóval ez egy módja annak, hogy szerezze be a felhasználói program fenntartása alapvető szolgáltatásokat. Például, van egy menedzsment fájlrendszer, új folyamatok indulnak, amely hozzáférést biztosít eszközöket, és így tovább.
Amint az 1. lista, a rendszer kéri meghatározott /usr/src/linux/include/asmi386/unistd.h fájl, mindegyik rendelkezik egy számot.
Két gyakori módja a rendszert használó hívások:

- aktiválása a szoftver megszakítás 0x80;
- függvényhívás wrapper libc.

Az első módszer jobban tolerálható, mert fogyasztani bármilyen Linux (meghatározva kernel kód). A második módszer kevésbé tolerálható, meghatározva a kódot a standard könyvtár.

int 0x80
Nézd meg alaposan az első módszer. Amikor a processzor megszakítást 0x80, ez tartalmazza a kernel módban, és végrehajtja a kért funkciót, így a kívánt kezelőt a Interrupt Leírótábla (megszakítás leíró táblázatot). rendszer hívószám kell meghatározni EAX, amely végül tartalmazni fogja a visszatérési érték. Az viszont az érveket, hogy a funkció számát hat, hogy foglalt EBX, ECX, EDX, ESI, EDI és az EBP, ebben a sorrendben, és csak a megfelelő mennyiségű nyilvántartások és nem minden. Ha a funkció megköveteli több mint hat érv, meg kell tenni őket a szerkezet és ments el egy mutatót az első eleme EBX.

Emlékeztetni kell arra, hogy a Linux kernel 2.4 EBP regiszter nem használható át érveket, és ezért továbbítja csak öt érvet nyilvántartásokban.

A mentés után a rendszer hívószám és a paramétereket a megfelelő nyilvántartások, egy megszakítás 0x80: a processzor belép a kernel módú végrehajt egy rendszer hívást, és átadja a vezérlést a felhasználói folyamat. Ahhoz, hogy játszani ezt a forgatókönyvet, amire szükség van:

- menteni a rendszer hívószám EAX;
- hogy a rendszer hívás érveket vonatkozó nyilvántartások;

- létrehoz egy memória szerkezet, amely egy rendszer hívás paramétereit;
- tárolt EBX egy mutatót az első érv;
- végre szoftver megszakítás 0x80.

A legegyszerűbb példa tartalmazná a klasszikus - a rendszer hívás exit (2). ismerjük a számát /usr/src/linux/include/asm-i386/unistd.h file: 1. oldal kalauz azt mondja, hogy csak egy kötelező argumentum (status), amint azt a 2. listában.

2. listán férfi man oldal azt jelzi, hogy ez kötelező egy paraméter

Mi fogja meg az EBX regiszterbe. Ezért szükséges az alábbi utasításokat:

libc
Mint jeleztük, más standard technikával, hogy egy függvény C. Nézzük meg, hogy ez megtörtént egy egyszerű C program:

Csak meg kell lefordítani:

Szétszerelni gdb annak érdekében, hogy megbizonyosodjon arról, hogy ugyanazt a rendszert hívás (lásd a 3. listát).

3. lista szétszerelése kilépési program segítségével gdb debugger

Az utolsó funkció a main () - egy kihívás, hogy kilépjen (3). Továbbá azt látjuk, hogy a kijárat (3) viszont okoz _exit (2), amely a rendszer hívás, köztük megszakítás 0x80, 4 jegyzék.

4. lista A rendszer bevezetése hívás

Így, shell-kódot használó libc közvetetten okoz a rendszer hívás _exit (2):

5. lista /usr/src/sys/kern/syscalls.master fájl Elején OpenBSD

Az első sor tartalmazza a rendszer hívás számát, a második - a típust, a harmadik - a függvény prototípus. Ellentétben a Linux, a rendszer kéri, ne használja a * BSD megállapodás egy gyors hívást, a szoba érv nyilvántartások helyett egy stílust elhelyezését érvek a verem. Érvek kerülnek fordított sorrendben jobbról kezdve, így is letölti a megfelelő sorrendben. Közvetlenül azután, hogy visszatértünk a rendszer hívási verem meg kell tisztítani helyezett elfogultság a stack pointer bájtos szám, ami a hossza összes érvet (vagy egyszerűen úgy, hogy a bájtok számát az érv szorozva 4). A szerepe az EAX regiszterben ugyanaz, mint a Linux, hogy tartalmaz egy olyan rendszert hívószám és az eredmény tartalmazza a visszatérési érték.

Így a rendszer hívás négy lépésből áll:

- hívja szám mentése EAX;
- szoba a fordított sorrendben az érvek a verem;
- program végrehajtásával megszakítás 0x80;
- razzia verem.

Példa Linux, BSD alakítjuk *, úgy fog kinézni:

Írásban shell-kód
A következő példák a Linux, akkor könnyen adaptálható a világ * BSD. A kész shell-kódot, akkor marad így utasításból megfelelő assembly utasításait. Szabványosan használt három módszereket a műveleti kódokat:

- írás őket kézzel (az Intel dokumentumok a kezében!);
- írásban assembler-kódot, majd eltávolítjuk a műveleti kód;
- írni a kódot C a későbbi szétszerelést.

Nézzük meg a másik két módszer.

szerelő
Az első lépés - a használata assembly példa exit.asm egy olyan rendszer segítségével hívást _exit (2). A felhasználásra műveleti kódok NASM szétszerelése, majd összegyűjtjük bináris objdump, amint azt a 6. listán.

Listing 6. Az assembly kódot a szövegből az összeállításban, majd szétszerelése

Listing 7. tesztelése opkódot

Remélem, a szája nyitva van elég széles. Annak ellenőrzésére, a teljesítmény shell-kódot, csak az alkalmazás futtatásához az strace, Listing 8.

Listing 8. Trace szűrés alkalmazások

Az utolsó sor - hívja _exit (2). Azonban, ha a shell-kódot, látunk egy kis probléma: tartalmaz egy csomó nulla bájt. Mivel a shell-kód gyakran belefér egy string tároló, ezek a bájtok temetni vonalmegosztó és a támadás sikertelen lesz. Kétféle módon lehet megoldani a problémát:

- levelet utasításokat, amelyek nem tartalmaznak nulla bájt (ami nem mindig lehetséges);
- levelet shell-kódot módosítani kell kézi eltávolítása nulla bájt, hogy akkor futásidejű kódját adunk nulla bájt, mely igazodik a vonal az elválasztó.

Élvezi az első módszer.
Az első utasítás (mov ebx, 0) módosítható több upotrebimo (a teljesítmény miatt):

A második kijelentés az összes ilyen nullák, mert használ egy 32 bites regiszter (EAX) termel, 0x01 váló 0x01000000 (nibbles felcserélődnek Intel® - little endian processzor). Ezen a módon tudjuk megoldani ezt a problémát egyszerűen használ nyolc-bites regiszter (AL):

Most a szerelvény kód a következő:

és minden nulla bájt (lásd lista 9).

Listing 9. shell-Ellenőrző kód

C
Következő: kivonásához utasításból szétszerelése az összeállított programot C. Vegyük bináris származó exit.c és nyissa meg a segítségével gdb, 10 Listing.

Listing 10. bináris exit.c, nyitott gdb

Amint látható, _exit (2) függvény használja ténylegesen két rendszer kéri: 0xfc (252), _exit_group (2), és a további, _exit (2). _exit_group (2) hasonló _exit (2), de a cél az, hogy mind a folyamok a csoport. A mi kód valóban csak a második rendszer hívást.

Továbbá, mint az előző példában, meg kell legyőzni a nulla bájt.

Első konzol
Itt az ideje, hogy írjon a shell-kód, amely lehetővé teszi, hogy valami hasznos. Például, mi is létrehozhatunk egy kódot, hogy hozzáférjen a konzolt, hogy miután egy generációs konzol ez tisztán befejeződött. A legegyszerűbb megközelítés itt - használata a rendszer hívás execve (2). Ne felejtsük el, hogy nézd meg a man oldalt Listing 11.

A 2. lista 11. férfi execve

Meg kell átadni három érvelését:

- egy mutatót a program nevét, hogy végre, ebben az esetben, egy mutatót a húr / bin / sh;
- mutató egy tömböt át paraméterként a program, az első érv kell az argv [0], ez a program neve, az utolsó argumentum nem null pointer;
- mutató egy tömb sor továbbítására a programjaik a környezetvédelem; ezek a sorok általában meghatározott kulcs = érték formátumban, és az utolsó eleme a tömbnek kell lennie null pointer. C hasonló a következő:

Összegyűjtése és hogyan működik:

Nos, nos, van egy héj. Most lássuk, hogy ez a rendszer hívás külleme assembly nyelven (ahogy szoktuk három érv lehet használni helyett regiszterek szerkezet). Azonnal talált két probléma:

Lássuk, mit csinál:

Ettől a ponttól kezdve használhatja a shell-kód szerkezete tele valami hasznos. Elemezzük lépésről lépésre, a tervezett tevékenységek:

A kapott assembly kód listán látható 13.

Listing 13. A módosított assembly kódot

Kivonat a műveleti kódokat, Listing 14:

Kapcsolódó cikkek