Tweets by @buherablog
profile for buherator at IT Security Stack Exchange, Q&A for IT security professionals

A BitBetyár Blog

Túljártál a nagyokosok eszén? Küldd be a mutatványodat! (e-mail a buherator gmailkomra jöhet)

Full-Disclosure / Névjegy / Coming out


Promó

H.A.C.K.

Címkék

0day (110) adobe (87) adobe reader (21) anonymous (26) apple (60) az olvasó ír (49) blackhat (20) botnet (22) bug (200) buherablog (44) buhera sörözés (39) bukta (49) deface (38) dns (22) dos (29) esemény (82) facebook (26) firefox (64) flash (33) gondolat (31) google (59) google chrome (36) hacktivity (37) hírek (117) incidens (224) internet explorer (88) iphone (35) java (50) jog (22) kína (21) kriptográfia (68) kultúra (21) linux (24) malware (43) microsoft (142) móka (48) mozilla (23) office (26) oracle (40) os x (43) patch (197) php (20) politika (31) privacy (58) programozás (22) safari (34) sql injection (62) windows (85) xss (77) Címkefelhő

Licensz

Creative Commons Licenc

Az inicializálatlan verem változó esete

2008.03.12. 10:57 | buherator | 4 komment

Az alábbi cikk a Microsoft SVRD blogon jelent meg:

A MS08-014 és CVE 2008-0081 jelű sebezhetőségek egy incializálatlan stack változóból adódó Excel sebezhetőségről szólnak. Már valószínűleg láttál ehhez hasonló figyelmeztetéseket a fordítótól:

C:\temp>cl stack.cpp
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.21022.08 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved.

stack.cpp
c:\temp\stack.cpp(49) : warning C4700: uninitialized local variable 'pNoInit' used ...
A kód lefordul és rendben linkelődik, lehet, hogy eddig figyelmen kívül is hagytad ezt a figyelmeztetést. Erről a tipusú kódhibáról sokkal kevesebbet hallani, mint a puffer túlfutásokról, de az MS08-014 jó példája annak, hogy miért potenciálisan veszélyes az inicializálatlan stack változók használata. Az alábbi PoC kódot fogjuk használni a hiba szemléltetésére, rámutatunk a sebezhetőségre, majd részletesebben tárgyaljuk az ilyen jellegű fordítói hibaüzeneteket:

// stack.cpp
unsigned char scode[] = "shell code";

void parse();

void p1();
void p2();

int main(int argc, char* argv[])
{
parse();
return 0;
}

void parse()
{
p1();
p2();
}

void p1()
{
// Assume p1 is reading data from the file to s
// Content of s could be controlled by the attacker.

// For simplicity, assign s to 0x0013fee8, location of return address to stack!main+0x8

int s[64];
for (int i=0; i<64; i++)
{
s[i] = 0x0013fee8; // spray the stack buffer
}
}

void p2()
{
// Assume p2 is the next parsing function to read data.
// For example, it tries to load an image from the file, and assign to its field.
// If the content image is the shellcode, by modifying the return address in the
// stack, the shell code runs.

struct {
char * pImage;
} *pNoInit;

// This is to align the stack layout to demonstrate the attack
char s[100];

pNoInit->pImage = (char *) scode;
}

A pNoInit változót nem inicializáljuk, mielőtt a p2() függvényben használjuk. Kihasználható-e ez a programozói hiba kódfuttatás céljából? A válasz attól függ, hogy a támadó elő tudja-e készíteni a stacket a p2() hívása előtt. Ha igen, akkor a pNoInit változó a támadó irányítása alá kerül.

A példánkban a p1() éppen a p2() előtt kerül hívásra. A p1() függvényben megpróbálunk a verem adott címeire értékeket beállítani. Ezek az értékek p1() lefutása után is a veremben maradnak. Emiatt, amikor p2() meghívódik, a pNoInit értékét a támadó állíthatja be. Ebben az esetben elég a main() visszatérési címére mutatnia vele, és készen is van.

Ez így néz ki egy debuggerben:

stack!p2:
00401090 55 push ebp
0:000> k
ChildEBP RetAddr
0013fed4 0040101d stack!p2 [h:\work\stack\stack\stack.cpp @ 57] 0013fedc 00401008 stack!parse+0xd [h:\work\stack\stack\stack.cpp @ 33]
0013fee4 004012ae stack!main+0x8 [h:\work\stack\stack\stack.cpp @ 26] 0013ffc0 7c816fd7 stack!mainCRTStartup+0x173 [f:\vs70builds\3077\vc\crtbld\crt\src\crt0.c @ 259]
WARNING: Stack unwind information not available. Following frames may be wrong.
0013fff0 00000000 kernel32!RegisterWaitForInputIdle+0x49

0:000> dv
pNoInit = 0x0013fee8
s = char [100] "???"

stack!p2+0x32:
004010c2 8b458c mov eax,dword ptr [ebp-74h] ss:0023:0013fe60=0013fee8

0:000> t
eax=0013fee8 ebx=7ffdf000 ecx=00000063 edx=7c90eb63 esi=00000a28 edi=00000000
eip=004010c5 esp=0013fe5c ebp=0013fed4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
stack!p2+0x35:
004010c5 c70030704000 mov dword ptr [eax],offset stack!scode (00407030) ds:0023:0013fee8=004012ae

0:000> k
ChildEBP RetAddr
0013fed4 0040101d stack!p2+0x3b [h:\work\stack\stack\stack.cpp @ 79] 0013fedc 00401008 stack!parse+0xd [h:\work\stack\stack\stack.cpp @ 33]
0013fee4 00407030 stack!main+0x8 [h:\work\stack\stack\stack.cpp @ 26] 0013ffc0 7c816fd7 stack!scode
WARNING: Stack unwind information not available. Following frames may be wrong.
Amikor a fordító az inicializálatlan változóra figyelmeztetett, megpróbált megóvni minket egy valódi sebezhetőségtől!

A /W4 kapcsoló segítségével még több segítséget kaphatsz a fordítótól. Nézd meg a kövekező test.cpp-t:

int main(int argc, char *argv[])
{
int *pNoInit ;
int *pMaynotInit;

if (argc < 3)
{
pMaynotInit = 0;
}

return *pNoInit + *pMaynotInit;
}

C:\test>cl /W4 test.cpp
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 13.10.3077 for 80x86
Copyright (C) Microsoft Corporation 1984-2002. All rights reserved.

test.cpp
c:\test\test.cpp(11) : warning C4700: local variable 'pNoInit' used without having been initialized
c:\test\test.cpp(11) : warning C4701: local variable 'pMaynotInit' may be used without having been initialized

Az előbbihez hasonlóan láthatjuk a C4700-as figyelmeztetést, és megjelent egy C4701 jelű figyelmeztetés is, amely egy "potenciálisan" inicializálatlan változóra figyelmeztet. A /W4
kapcsoló hatalmas mennyiségű figyelmeztetést generál (emiatt nem is használják a legtöbben), azonban a legtöbb 4701-es hiba triviálisan orvosolható. A többi figyelmeztetés egy-egy körmönfontabb hibára utalhat.

Például a következő kód potenciálisan inicialiálatlan változót használóként lesz megjelölve:
MYOBJECT *p;
HRESULT hr;
hr = MyInitializationFunction(&p);
if(SUCCEEDED(hr))
{
p->DoSomething(); // flagged as potential uninitialized use
}

A fordító nem tudja, hogy a MyInitializationFunction() sikerrel inicializálja-e *p-t. Azonban a p NULL-ra állítása deklarációkor megszünteti a félreérthetőséget - és ez arra az esetre is jó gyakorlat, ha a MyInitializationFunction() hiubás, és sikerrel tér vissza akkor is ha a paraméterét nem inicializálta megfelelően.

A 4701-es figyelmeztetések egy másik gyakori forrása:

foo(BYTE packet_type)
{
MYOBJECT *p;
switch(packet_type)
{
case REQUEST:
p= new MYOBJECT ...;

case RESPONSE:
p= new MYOBJECT ...;

}
p->DoSomething(); <- flagged as potential uninitialized use
...
A fordító itt arra a lehetséges kódútra figyel fel, amikro a packet_type sem REQUEST sem RESPONSE. Az implicit "default" eset fut le, ami nem inicializálja p-t - innen a figyelmeztetés. Lehetséges, hogy tényleg csak kétféle csomagtípus  érheti el ezt a pontot (pl. azért mert a megfelelő ellenőrzés már ezelőtt a kódrészlet előtt lefutott.) Ebben az esetben a switch szerkezet egyik ága biztosan lefut, és p inicializálódik. Ismét látjuk, hogy a p NULL-ra deklarálásával [definiálásával?] a hiba könnyedén megszüntethető - és arra az esetre, ha valahogy mégis sikerül a kódot [az ellenőrzések előtt] lefuttatni (pl. egy támadó illegitim típust állít be egy hálózati csomagon) a kódfuttatás lehetőségét sikerült szinte nullára redukálnunk. Persze a legjobb megoldás a hibák elkerülésére, ha explicite megadjuk a default ágat, amiben a nem megfelelő csomagtípusra utaló hibát jelzünk.

Reméljük, hogy a fennti értekezés rámutat ezeknek a figyelmeztetéseknek a komolyságára. Valójában azt szeretnénk, hogy ezek a hibaüzenetek tiltott figyelmezetéstípusként jelennek meg az SDL következő változatában.

Címkék: programozás excel cplusplus

Kommentek:

A hozzászólások a vonatkozó jogszabályok  értelmében felhasználói tartalomnak minősülnek, értük a szolgáltatás technikai  üzemeltetője semmilyen felelősséget nem vállal, azokat nem ellenőrzi. Kifogás esetén forduljon a blog szerkesztőjéhez. Részletek a  Felhasználási feltételekben és az adatvédelmi tájékoztatóban.

EQ · http://rycon.hu 2008.03.12. 21:11:44

grat, több ilyen kellene :) jó fordítás

Aikon 2008.03.12. 22:03:15

Hát, wazz, ezt meg kellett emészteni :). Tudom, rendeteg idő lehetett lefordítani, de tényleg több ilyen kéne. grat :D.

hello 2008.03.13. 14:02:16

a pNoInit->pImage = (char*)scode; -nak nemkellene segmentation fault-olnia?

Aikon 2008.03.14. 12:58:20

Segfault akkor keletkezik, ha invalid memóriára hivatkozol, azonban itt s stack-re hivatkozol (oda szól a 0x0013fee8).

(A C/C++ függvényhívás úgy néz ki, hogy leteszed a stack-re a hívandó függvény paramétereit, majd azt a címet, ahonnan a kódnak a futtatást a return után folytatnia kell, és ezután ugrasz a hívott függvény címére, akiis eléri ezeket az adatokat, majd ha végzett a futással, az utasítás-pointert ráállítja a megadott címre, hogy onnan folytatódjon tovább a futás. Itt a 0x0013fee8 egy olyan cím, ahol egy ilyen visszatérési cím van tárolva, és ezzel lett inicializálva a stack, tehát pNoInit értéke is ez, szóval *pNoInit egy olyat struct, ami 32 bit hosszú, és a 0x0013fee8 címen található. A pNoInit->pImage = (char*)scode; az itt található mutatót írja felül a saját kódunk címére.)