Crackatoa
door Tla
To err is human...
De nieuwste rage op het World Wide Web (Internet volgens sommigen) is Java. Java is een
objectgeoriënteerde programmeertaal gemaakt door Sun. Java is makkelijk, heeft garbage
collection, is multi-threaded en zo heeft het nog meer dingen die door Sun als
"voordeel" gepresenteerd worden. Java wordt niet direct naar machinecode
gecompileerd, maar naar een pseudomachinecode (Java bytecode), die uitgevoerd wordt door
de Virtual Machine. In plaats van de bytecode te interpreteren is het ook mogelijk hem om
te zetten naar echte machinecode. Dit wordt gedaan door de Just In Time Compiler (JIT).
Tot zover heeft het weinig met hacken te maken. De reden dat Java ineens zo populair
geworden is, is omdat Sun een webbrowser (HotJava) heeft gemaakt die een bepaald soort
Java programmaatjes, de zogenaamde applets, in een webpagina kan laten uitvoeren (Deze
applets hebben overigens niet zo veel te maken met de programmeertaal Java, ze hadden even
goed in Pascal of COBOL (hoewel) geschreven en vervolgens naar bytecode gecompileerd
kunnen zijn. Ik zal om de verwarring nog groter te maken zowel de programmeertaal als het
idee van applets uitvoeren met de naam 'Java' aanduiden). Door een speciale tag in HTML
wordt de bytecode van je applet opgehaald via het netwerk en volledig automagisch op de
machine van de persoon die je pagina opvraagt uitgevoerd. Dit is wel een hele
aantrekkelijke manier om in te breken en het is nog maar de vraag of het jouw schuld is
als iemand bij zichzelf jouw inbraakprogramma opstart (bewust of onbewust, in het laatste
geval zal het misschien wel onder de wet computercriminaliteit vallen). Applets hebben in
meer of mindere mate toegang tot bepaalde resources van de machine waar ze op draaien. Sun
kan maar niet genoeg zeggen dat Java secure is. Kortom: dit vraagt gewoon om gehackt te
worden. Kunnen we Java straks qua security in dezelfde categorie als NFS en SunOS (ook
beiden van Sun) plaatsen?
...to hack divine
Doordat Java zo simpel is, geen manipulaties op pointers kan uitvoeren, type checking
heeft bij een typecast en nog meer van dit soort dingen, is het niet meer mogelijk om
"per ongeluk" foutjes te maken in java code zoals bijvoorbeeld het gebruik van
gets() of sprintf() in C. Om op dit niveau te hacken zul je dus zelf met opzet stoute
bytecode moeten gaan maken, een echte brasaap schrijft hiervoor natuurlijk even een
compilertje. Er is alleen één maar: de Javamakers hebben hier rekening mee gehouden en
een hoop zaken die normaal compile time zouden gebeuren, gebeuren nu runtime, zoals het
toekennen van adressen aan references van objecten, type checking en zo voort. Deze
runtime checks worden uitgevoerd door de Byte Code Verifier. Het is aan de Turing-fans
onder ons om hier iets leuks mee te doen (laat de verifier zichzelf maar eens proberen te
verifiëren). Per ongeluk foutjes maken onmogelijk maken lijkt een utopie. En inderdaad,
ook bij Java gaat de wet van behoud van ellende op; je kunt nog steeds per ongeluk een
foutje maken dat de veiligheid aantast. Zo komen we op iets redelijk nieuws:
objectgeoriënteerd hacken.
OOH! (Object Oriented Hacking)
In Java is elk programma een zogenaamde class. Een class kun je zien als de beschrijving
van een bepaald iets. Laten we een voorbeeld nemen uit de Abstract Window Toolkit (AWT)
die bij de Java Developer's Kit (JDK) geleverd wordt, namelijk een window. Een window
heeft een aantal eigenschappen, zoals een positie, een grootte, een voor- en
achtergrondkleur. Deze eigenschappen worden vastgelegd door een aantal variabelen
(fields). Verder zijn er nog een aantal operaties die op een window uitgevoerd kunnen
worden, zoals verplaatsen, vergroten of verkleinen, naar de voorgrond halen of naar de
achtergrond zetten, het window tekenen etc. Deze operaties worden vastgelegd door functies
die werken op de fields (memberfuncties of methods). In Java zou dit er als volgt uitzien
(erg versimpeld):
public class Window
{
public Color fore, back; // foreground and background colors
private int w,h; // width and height
protected int x,y; // position
public int getWidth() { return w; }
public int getHeight() { return h; }
private void changeColor(Color c) { /* verander de kleur
*/ }
protected void fillRect(int xx, int yy, int ww, int hh)
{
/* teken een gevulde rechthoek op (xx,yy) met breedte ww en hoogte hh */
}
protected void drawBorder(int xx, int yy, int ww, int hh)
{
/* teken een kader op (xx,yy) met breedte ww en hoogte hh */
}
public void show()
{
changeColor(back);
fillRect(x,y,w,h);
changeColor(fore);
drawBorder(x,y,w,h);
}
// constructor, cre"eert een instantiatie van de
class Window public Window(int xx, int yy, int ww, int hh)
{
x=xx; y=yy; w=ww; h=hh;
/* ... */
}
}
Bovenstaand stukje code definieert een window. Het is nu
gewoon mogelijk om variabelen van het type Window te gebruiken. De waarde van zo'n
variabele is een instantiatie van de class. Zoiets heet een object. Een voorbeeldje:
Window score_window, game_window;
// maak een window van grootte 100x100 op positie (23,43)
game_window = new Window(23,43,100,100);
// geef het window een kleurtje
game_window.fore = Color.black;
game_window.back = Color.yellow;
score_window = game_window;
// score_window refereert nu aan hetzelfde window als game_window!
// laat maar zien
game_window.show();
// wat mooi een geel met zwart window
// nu verander je score_window (dus eigenlijk game_window) van kleur
score_window.back = Color.green;
// laat maar zien
game_window.show();
// hee, een groen met zwart window
Nu enige uitleg: De class Window heeft twee variabelen van
het type Color. Hiervoor staat public en dit wil zeggen dat deze variabelen bij elk Window
object op te vragen en te veranderen zijn vanuit elk stuk code. Vervolgens zijn er twee
variabelen die private zijn. Dit betekent dat alleen methods van de class Window (of
iedere instantie hiervan) deze variabelen kunnen opvragen en wijzigen. protected betekent
iets soortgelijks, op het verschil komen we later terug. Vervolgens staan er een aantal
methods gedefinieerd, ook weer met dezelfde woorden ervoor. Bij een method betekent public
dat de method vanuit elk stuk code aan te roepen is, en private en protected komen er op
neer dat alleen methods van een class deze method aan kunnen roepen.
Het tweede stukje code laat het gebruik zien van de Window
class. Zoals je ziet kunnen twee verschillende variabelen naar hetzelfde object wijzen.
Deze beruchte eigenschap staat bekend onder de naam aliasing. Aliasing wordt meestal
beschouwd als een nadeel, bij veel aliasing is haast niet meer na te gaan over welk object
het nu gaat.
Stel dat we nu een ander soort window willen maken, dat een
aantal extra eigenschappen heeft naast het oorspronkelijke window. We hoeven dan niet weer
opnieuw alles in te typen, maar maken een zogenaamde derived class of subclass van de
class Window (dit heet dan de base class of superclass). In de AWT bestaat er een subclass
genaamd Frame. Deze heeft als extra's o.a. een titel en een ikoontje. In Java zou dit er
zo uitzien (wederom versimpeld):
// extends Window geeft aan dat we een subclass van
Window willen
public class Frame extends Window
{
private MenuBar menubar;
public Image iconimage;
protected showicon() { /* teken het ikoontje */ }
public iconify()
{
/* laat het window van het scherm verdwijnen */
showicon();
}
}
De class Frame heeft nu alle fields en methods van een
Window en elk Frame object kan als een Window object benaderd worden. Verder heeft een
Frame een aantal extra fields en methods. Als in een subclass een method gedefinieerd
wordt die ook al in de superclass stond dan vervangt de method van de subclass die van de
superclass.
Dan nu eindelijk de verklaring van al die irritante worden als private. Dit zijn access
modifiers en bepalen of je de waarde van een field mag opvragen of veranderen of een
method mag aanroepen. Er zijn vier toegangsnivo's:
- public : Iedereen mag deze method aanroepen of dit field
veranderen of bekijken.
- private : Alleen memberfuncties van deze class mogen deze
method aanroepen of bij dit field komen.
- protected : Alleen memberfuncties van de classes uit
dezelfde package als deze class of memberfuncties van een subclass van deze class hebben
toegang tot de memberfunctie of het field. Een package is een verzameling classes, zo is
java.awt de package voor de grafische user interface.
- private protected : Alleen memberfuncties uit deze class of
uit een subclass hiervan hebben toegang.
Deze modifiers worden veel gebruikt in systeemclasses om
low-level calls af te schermen. Dit gaat als volgt:
class FileSystem
{
private void remove(File f)
{
// code voor remove
}
public void RemoveFile(File f)
{
// doe een securitycheck
// als alles in orde is:
remove(f);
}
// ... nog meer methods
}
Door onzorgvuldig gebruiken of helemaal niet gebruiken van
deze modifiers sluipen er security bugs in programma's. Als in bovenstaand voorbeeld
remove nu niet private, maar protected (of zelfs private protected) was, dan hadden we de
volgende truuk uit kunnen halen:
class HackedFileSystem extends FileSystem
{
public void RemoveFile(File f)
{
// doe GEEN securitycheck
remove(f);
}
}
Als we een andere class (bijvoorbeeld eentje van je
webbrowser) nu wijs kunnen maken dat er een HackedFileSystem gebruikt moet worden in
plaats van een gewone FileSystem, dan kunnen we files weghalen.
Soms is het niet aantrekkelijk om alle low-level calls
private te maken, omdat dan veel te veel checks uitgevoerd moeten worden, ook bij classes
die je vertrouwt en waarvan je weet dat ze veilig zijn ("Trust is your worst
enemy" gaat ook hier op). Meestal zitten deze "trusted" classes in dezelfde
package en is de low-level call protected. Om te voorkomen dat bovenstaande truuk werkt
kun je het woordje final toevoegen op één van de volgende twee plaatsen:
- Voor de method: public final void RemoveFile. Dit betekent
deze method in een subclass niet opnieuw gedefininiëerd mag worden.
- Voor de class: final class FileSystem { ... }. Dit betekent
dat er geen subclass van deze class gedefiniëerd mag worden.
Al deze access modifiers maken de boel wel erg complex en
dat is vaak de reden dat er toch per ongeluk security bugs ontstaan.
Het laden en uitvoeren van bytecode
Helaas zitten de bugs niet allemaal in de access modifiers (dan zou het wel heel makkelijk
zijn om te hacken). De meer ernstige bugs vergen de nodige inventiviteit en moeite om op
te sporen. Ze bevinden zich in het mechanisme dat de bytecode via het netwerk ophaalt en
uitvoert.
De Java bytecode wordt door een zogenaamd ClassLoader object opgehaald. Vervolgens worden
er allerlei controles uitgevoerd doordat de ClassLoader aan de Byte Code Verifier vraagt
om de bytecode te checken. Hierbij worden de types van argumenten, de access modifiers van
methods en dergelijke gecontroleerd en ook of de bytecode zich naar behoren gedraagt
(stack overflow, illegal typecast). Tevens wordt aan elke class een namespace toegekend en
classes worden eerst in de lokale namespace gezocht (waarin alleen maar brave applets
zitten) en dan pas in de andere namespaces. In principe is het dus niet interessant om een
systeemclass na te maken, omdat deze toch niet geladen wordt als er een echte systeemclass
nodig is. In de eerste Java versies bleek er echter niet altijd eerst lokaal gezocht te
worden. Als we het systeem een zelfgemaakte ClassLoader kunnen laten draaien kunnen we de
typechecking en de namespaces-check omzeilen en zo stoute bytecode uitvoeren. Een andere
fout was dat er niet op de private modifier gelet werd zodra een class lokaal geladen
werd. Op die manier was het mogelijk je eigen SecurityManager (zie hierna) in te stellen.
De security checks die tijdens het uitvoeren plaatsvinden worden uitgevoerd door een
zogenaamd SecurityManager object. De security manager bepaalt wat een applet wel en niet
mag, zoals toegang tot het filesystem of het netwerk. Dit is gedaan zodat iedereen die een
Java implementatie maakt zelf de security kan bepalen (zodat Sun geen verantwoordelijkheid
heeft). Het namaken en laten draaien van een zelfgemaakte security manager is dan ook een
ideale hack.
Zelfs al lijkt de SecurityManager veilig, dan is er via een
omweg wel weer iets mee te doen, zoals het volgende verhaal illustreert: Een tijd geleden
was het mogelijk om een applet een connectie te laten maken met een willekeurige host.
Door zelf een nameserver te draaien die liegt kon je, door de applet een hostname op te
laten vragen waarbij je zeker wist dat jouw DNS server gebruikt werd, twee IP nummers aan
de browser terugsturen die overeenkwamen met het IP nummer waar de applet vandaan kwam en
het IP nummer van de host waarnaar je wilt connecten. Op die manier dacht de
SecurityManager van de browser dat het IP nummer van die host een IP nummer was waarvan de
applet kwam en zodoende werd de netwerkconnectie toegelaten. Zodoende was het simpel om
door een firewall heen te komen. Het schijnt nu niet meer te kunnen.
Een andere leuke fout was de volgende: als een naam van een
class met een '/' begon, probeerde je webbrowser deze class van het lokale filesystem te
laden. Een class die je lokaal laadt, mag meestal meer dan een class die via het netwerk
binnenkomt. Als je nu op een of andere manier een bytecode op het systeem van het
slachtoffer kan zetten (anonymous ftp, NFS of zelfs de cache van de webbrowser(!), in het
laatste geval moet je natuurlijk wel de naam van de cachefile weten), dan kun je via een
omweg meer privileges krijgen. Overigens: waarschijnlijk kun je de naam van de cachefiles
via een omweg in JavaScript te weten komen, het gaat echter te ver om dat hier uit te
leggen. Toen ik wat aan het "spelen" was met een beeehhta versie van Netschaap
3.0 kwam ik erachter dat dit eigenlijk wel moet kunnen, maar ik moet nog proberen of het
echt werkt.
Naast de ClassLoader en de SecurityManager zijn er ook heel
wat andere classes gevoelig voor fouten. Denk bijvoorbeeld aan applets voor
betalingsverkeer via het internet, of classes voor encryptie.
De omgeving
De technieken hierboven lijken allemaal wel mooi in theorie, om ze in de praktijk te laten
werken heb je wel geluk nodig. Een eigen ClassLoader of SecurityManager laten draaien is
leuk, maar de webbrowser moet het toevallig maar toestaan. Er zijn echter ook een aantal
"features" in de Java omgeving die op een goede of op een slechte, maar in ieder
geval op een simpele manier gebruikt kunnen worden:
- Java heeft Threads, stukjes code die je in de achtergrond
kan laten draaien. Een Thread kan zelf bepalen welke prioriteit hij nodig heeft en zo een
webbrowser flink vertragen door ook nog eens een hoop geheugen op te eten.
- Een applet kan een window buiten de browser op het scherm
zetten. Er is geen grens aan het aantal of de grootte.
- Een applet kan irritante geluiden afspelen of ongewenste
plaatjes op het scherm zetten.
- Een applet kan methods aanroepen van applets op dezelfde
pagina. Met een slecht ontworpen applet valt er misschien iets te hacken. Bij een
encryptie class zou je de seed voor een randomgetal misschien in kunnen stellen. Om bij de
andere classes op een webpagina te komen moet een applet zijn AppletContext object
opvragen door de method getAppletContext(). Door van deze AppletContext weer de method
getApplets() aan te roepen krijg je de applets terug. Overigens is dit in Netscape versie
3 uitgezet. Nu mogen alleen applets van dezelfde host methods van elkaar aanroepen. In
hoeverre dit met frames in Netscape te combineren is heb ik nog niet uitgezocht, maar
misschien dat een combinatie van Java en JavaScript wat kan helpen (Java en JavaScript
zijn in Netscape versie 3 te combineren).
De meeste Java implementaties zijn niet beveiligd tegen het
opvreten van al het systeemgeheugen of het permanent laten lopen van Threads. Iemand
pesten via het Internet is nu dus extreem makkelijk geworden en daarom lichtelijk flauw.
Overigens hoeft een applet niet eens te zien te zijn op de webpagina; op die manier kun je
een javan horse maken. Sterker nog: je kunt een applet een Thread op laten starten en die
niet meer laten stoppen. Deze Thread kan een poosje wachten en pas in actie komen als de
user al lang van de webpagina af is. Dit is overigens heel simpel om te maken, de
mogelijkheden zijn standaard al aanwezig.
Tegenmaatregelen
Een aantal van de mogelijkheden in Java waar iemand misbruik van kan maken is natuurlijk
inherent aan het hele idee van Java. Als je bijvoorbeeld niet wil dat er ongevraagd code
uitgevoerd wordt op je machine moet je Java niet gebruiken. Wil je het toch dan zijn er
verschillende oplossingen:
Heb je computers zat en een netwerk, dan gebruik je één machine om op te websurfen met
een Java-enhanced browser. Deze machine moet zelf niet alles mogen in het netwerk anders
heeft het weinig zin. Ben je erg paranoïde dan bekijk je eerst de source van elk HTML
document dat je ophaalt. Een HTML-tag met een applet die je niet ziet in je document kan
verdacht zijn. Overigens betekent dit niet dat je hiermee alle ongewenste applets
detecteert, iemand die zijn javan horse goed wil verbergen verstopt hem in een
"nuttige" applet.
Sun komt ooit nog met het concept van signed classes, op deze manier kun je er redelijk
zeker van zijn dat een bepaalde class van de goeie persoon afkomstig is. Tegen de
"annoyance attacks" is in de meeste gevallen wel iets te doen: je kunt
bijvoorbeeld een optie in je browser maken waarmee je alle classes ziet die in het
geheugen zitten (of alleen die classes die via het netwerk binnengekomen zijn). Hiermee
moet je dan ook bepaalde classes weg kunnen gooien of bepaalde Threads kunnen stoppen. Het
is vreemd dat zo'n mogelijkheid (nog) niet in de browsers te vinden is.
Meer Java brouwsels
Java is met een ontzettende hype de wereld in gebracht. Het is voortgekomen uit een
mislukt project dat te maken had met set-top boxes (toentertijd nog niet populair). Door
dit te porten naar het Internet begon het zonnetje weer te schijnen voor Sun. De bedoeling
is om met Java de heerschappij van MicroSoft aanzienlijk aan te tasten en Sun is razend
enthousiast over Java. Maar liefde maakt blind en er vinden een aantal ontwikkelingen
plaats waarvan ik me afvraag of er over de beveiliging goed nagedacht wordt. Enkele
voorbeeldjes:
Toen Java begin 1995 populair begon te worden bleek dit voor sommige mensen niet simpel
genoeg ("compileren? wa-isda?") en bij Netscape begon men aan een scripttaaltje
voor in een HTML document onder de naam JavaScript. Inmiddels heeft MicroSofts Internep
Exploder ook al Java en JavaScript support. In JavaScript is het mogelijk om met je
browser allerlei leuke geintjes uit te halen, zoals een nieuw document window openen,
rekenen in je html pagina etc. Ook hiermee zijn minder ethische dingen te doen; een poosje
geleden kon je, zodra iemand je pagina bezocht, de browser een mailtje namens diegene
laten versturen zonder dat hij/zij het merkte. In versie 3.0 van Netscape kunnen er ook
methods van applets aangeroepen worden en kan vanuit een applet JavaScript code uitgevoerd
worden. De veiligheid van je browser kan hiermee flink verminderen, met een beetje geluk
kan een hacker op deze manier bij iedere applet komen die actief is in een willekeurig
window. Ook moet het bij Netscape op deze wijze mogelijk zijn allerlei details van een
systeem op te kunnen vragen, zoals de directorytree van een host die voor de user readable
is (kun je remote ls of dir doen).
Voor de Windows 95 versie van Netscape 3.0 is er al een JIT in de browser gebakken. Het
zou me niets verwonderen als de JIT een security bug bevatte. Natuurlijk kun je de virtual
machine ook in hardware bouwen. Maak een kastje waarop je een keyboard, muis, monitor en
telefoonlijn kan aansluiten en je hebt een Network Computer (NC) gemaakt. Volgens sommigen
de opvolger van de PC, maar dit vereist een betrouwbaar netwerk (en dus niet het
Internet). Als je aan de client kant zo makkelijk programma's kan uitvoeren, waarom dan
ook niet aan de server kant? Dit kan inderdaad ook als je een soort van RPC mechanisme
bouwt, waarmee een class aan de kant van de client methods aan de kant van de server kan
uitvoeren en misschien ook wel andersom. Dit zal over een tijdje ongetwijfeld het CGI
programmeren gaan vervangen, maar als je even nadenkt vraag je je af of we zoiets niet al
hadden, RPC bestaat immers al lang. Wat het ook wordt, er zal flink wat aandacht besteed
moeten worden aan de beveiliging.
Tenslotte zijn er nog wat wilde ideeën om Java in het OS
in te bouwen. Hiermee veroorzaak je nog meer risico qua security. Hetgene dat dan
beveiligd moet worden is immers veel belangrijker dan alleen een browsertje. Het lijkt wel
sinterklaasfeest voor de hacker. Zoveel nieuwe speeltjes, daar moet wel lol mee te beleven
zijn! Het security beleid bij Java is tot nu toe zo geweest dat de bugs pas gemeld werden
nadat ze verholpen waren. Dit is niet echt leerzaam voor de hacker en ook heeft het als
nadeel dat er niet door een grote groep van mensen over een oplossing nagedacht wordt.
Verder geeft Java de mogelijkheid om erg privacy aantastende dingen te doen. Bijvoorbeeld
als je straks on-line muzieksamples kan downloaden (tegen elektronische betaling), dan is
binnen de kortste keren je muzieksmaak bekend bij de aanbieder en door middel van applets
kan hij/zij hier direct op inspelen. Met de commercialisering van het Internet zal dit
steeds belangrijker worden voor bedrijven. De consument moet het allemaal maar slikken.
Maar dat is weer een ander verhaal.
Meer lezen/spelen
Een van de allereerste artikelen over Java Security is "Java Security" van
Joseph A. Bank, (http://swissnet.ai.mit.edu/~jbank/javapaper/javapaper.html).
Het gaat over object oriented hacking in Java en de maatregelen die Sun heeft genomen om
dit tegen te gaan. Echte bugs worden niet besproken, alleen de security voor zover die
betrekking heeft op het Java-idee. Het artikel van Drew Dean, Edward W. Felten en Dan S.
Wallach daarentegen behandelt de bugs in de implementaties van de web browsers HotJava en
Netscape. Het heet "Java Security: From HotJava to Netscape and Beyond" en staat
op http://www.cs.princeton.edu/sip/Publications.html.
Het is lekker technisch en staat vol met sappige details. Alhoewel het meeste niet meer
werkt kan het toch helpen om nieuwe bugs op te sporen. Het misbruik van features in
webbrowsers is te bekijken en te beleven (inderdaad, er staan voorbeelden bij) op de
zogenaamde "Hostile Applets Homepage" van Mark D. LaDue (http://www.math.gatech.edu/~mladue/HostileApplets.html)
Enkele leuke voorbeelden zijn The Business Assassin, die er voor zorgt dat je de applets
van een bepaalde host niet meer kan draaien, een fakemail applet en een applet die een
groot produkt van twee priemgetallen ontbindt zonder dat je dit in de gaten hebt en het
resulaat terugstuurt naar zijn schrijver. Natuurlijk is er ook de java homepage van Sun (http://java.sun.com/ of http://www.javasoft.com/),
waarop je de Java produkten en documentatie kan vinden. Om aan de source code te komen is
iets lastiger; hiervoor moet je eerst een overeenkomst uitprinten, ondertekenen en naar
Amerika faxen, vervolgens krijg je het password voor de ftp server en kun je de source
downloaden. Als je een Java compiler en interpreter wilt hebben kun je die ook van deze
site halen, maar alleen voor Solaris, Macintosh en Windows. Een JDK voor Linux is te halen
op http://java.blackdown.org/. Een hoop Java
applets en pgramma's staan op Gamelan (http://www.gamelan.com/).
Er zit allicht ook wat over security bij.
|