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: