pwnable.tw seethefile

0x10 题目分析

int __cdecl main(int argc, const char **argv, const char **envp) { char nptr; // [esp+Ch] [ebp-2Ch] unsigned int v4; // [esp+2Ch] [ebp-Ch] v4 = __readgsdword(0x14u); init(); welcome(); while ( 1 ) { menu(); __isoc99_scanf("%s", &nptr); switch ( atoi(&nptr) ) { case 1: openfile(); break; case 2: readfile(); break; case 3: writefile(); break; case 4: closefile(); break; case 5: printf("Leave your name :"); __isoc99_scanf("%s", &name); printf("Thank you %s ,see you next time\n", &name); if ( fp ) fclose(fp); exit(0); return; default: puts("Invaild choice"); exit(0); return; } } }

image-20210812134658553

可以看到fp 在name变量下面,name是可以覆盖到fp的,因此我们的攻击一定是围绕这个来打的,我们查看fclose函数的原型

glibc/libio/iofclose.c.html

int _IO_new_fclose (FILE *fp) { int status; //# define CHECK_FILE(FILE, RET) do { } while (0) CHECK_FILE(fp, EOF); // 检查文件指针 #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1) /* We desperately try to help programs which are using streams in a strange way and mix old and new functions. Detect old streams here. */ if (_IO_vtable_offset (fp) != 0) // 检查vtable的值 return _IO_old_fclose (fp); #endif /* First unlink the stream. */ if (fp->_flags & _IO_IS_FILEBUF) _IO_un_link ((struct _IO_FILE_plus *) fp); //从链上取下来 _IO_acquire_lock (fp); if (fp->_flags & _IO_IS_FILEBUF) status = _IO_file_close_it (fp); else status = fp->_flags & _IO_ERR_SEEN ? -1 : 0; // # define _IO_release_lock(_fp) ; } while (0) _IO_release_lock (fp); _IO_FINISH (fp); if (fp->_mode > 0) { /* This stream has a wide orientation. This means we have to free the conversion functions. */ struct _IO_codecvt *cc = fp->_codecvt; __libc_lock_lock (__gconv_lock); __gconv_release_step (cc->__cd_in.__cd.__steps); __gconv_release_step (cc->__cd_out.__cd.__steps); __libc_lock_unlock (__gconv_lock); } else { if (_IO_have_backup (fp)) _IO_free_backup_area (fp); } _IO_deallocate_file (fp); return status; } void _IO_un_link (struct _IO_FILE_plus *fp) { if (fp->file._flags & _IO_LINKED) { FILE **f; #ifdef _IO_MTSAFE_IO _IO_cleanup_region_start_noarg (flush_cleanup); _IO_lock_lock (list_all_lock); run_fp = (FILE *) fp; _IO_flockfile ((FILE *) fp); #endif if (_IO_list_all == NULL) ; else if (fp == _IO_list_all) _IO_list_all = (struct _IO_FILE_plus *) _IO_list_all->file._chain; else for (f = &_IO_list_all->file._chain; *f; f = &(*f)->_chain) if (*f == (FILE *) fp) { *f = fp->file._chain; break; } fp->file._flags &= ~_IO_LINKED; #ifdef _IO_MTSAFE_IO _IO_funlockfile ((FILE *) fp); run_fp = NULL; _IO_lock_unlock (list_all_lock); _IO_cleanup_region_end (0); #endif } }

我们可以看到是对IO_FILE结构体的操作,我们看IO_FILE结构体的定义

glibc/libio/bits/types/struct_FILE.h

struct _IO_FILE { int _flags; /* High-order word is _IO_MAGIC; rest is flags. */ /* The following pointers correspond to the C++ streambuf protocol. */ 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; int _flags2; __off_t _old_offset; /* This used to be _offset but it's too small. */ /* 1+column number of pbase(); 0 is unknown. */ unsigned short _cur_column; signed char _vtable_offset; char _shortbuf[1]; _IO_lock_t *_lock; #ifdef _IO_USE_OLD_IO_FILE };

IO_Finish

((IO_validate_vtable ((*(__typeof__ (((struct _IO_FILE_plus){}).vtable) *)(((char *) ((fp))) + __builtin_offsetof(struct _IO_FILE_plus, vtable)))))->__finish) (fp, 0)

IO_jump_t

static const struct _IO_jump_t _IO_cookie_jumps libio_vtable = { 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_default_xsgetn), JUMP_INIT(seekoff, _IO_cookie_seekoff), JUMP_INIT(seekpos, _IO_default_seekpos), JUMP_INIT(setbuf, _IO_file_setbuf), JUMP_INIT(sync, _IO_file_sync), JUMP_INIT(doallocate, _IO_file_doallocate), JUMP_INIT(read, _IO_cookie_read), JUMP_INIT(write, _IO_cookie_write), JUMP_INIT(seek, _IO_cookie_seek), JUMP_INIT(close, _IO_cookie_close), JUMP_INIT(stat, _IO_default_stat), JUMP_INIT(showmanyc, _IO_default_showmanyc), JUMP_INIT(imbue, _IO_default_imbue), };

0x20 利用思路

关键利用代码fclose

int status; //# define CHECK_FILE(FILE, RET) do { } while (0) CHECK_FILE(fp, EOF); // 检查文件指针 #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1) /* We desperately try to help programs which are using streams in a strange way and mix old and new functions. Detect old streams here. */ if (_IO_vtable_offset (fp) != 0) // 检查vtable的值 return _IO_old_fclose (fp); #endif /* First unlink the stream. */ if (fp->_flags & _IO_IS_FILEBUF) _IO_un_link ((struct _IO_FILE_plus *) fp); _IO_acquire_lock (fp); if (fp->_flags & _IO_IS_FILEBUF) status = _IO_file_close_it (fp); else status = fp->_flags & _IO_ERR_SEEN ? -1 : 0; // # define _IO_release_lock(_fp) ; } while (0) _IO_release_lock (fp); _IO_FINISH (fp);

我们要控制程序流程为:

int status; //# define CHECK_FILE(FILE, RET) do { } while (0) CHECK_FILE(fp, EOF); // 检查文件指针 /* First unlink the stream. */ if (fp->_flags & _IO_IS_FILEBUF) _IO_un_link ((struct _IO_FILE_plus *) fp); _IO_acquire_lock (fp); status = fp->_flags & _IO_ERR_SEEN ? -1 : 0; // # define _IO_release_lock(_fp) ; } while (0) _IO_release_lock (fp); _IO_FINISH (fp);

而:_IO_IS_FILEBUF ,是一个宏定义

#define _IO_IS_FILEBUF        0x2000

这里有两种利用方法:(Glibc 2.23)

  • 利用IO_finish
  • 利用IO_close

首先介绍第一种方法:

https://blog.srikavin.me/posts/pwnable-tw-seethefile/

image-20210812194040326

利用:

int status; //# define CHECK_FILE(FILE, RET) do { } while (0) CHECK_FILE(fp, EOF); // 检查文件指针 /* First unlink the stream. */ if (fp->_flags & _IO_IS_FILEBUF) _IO_un_link ((struct _IO_FILE_plus *) fp); _IO_acquire_lock (fp); status = fp->_flags & _IO_ERR_SEEN ? -1 : 0; // # define _IO_release_lock(_fp) ; } while (0) _IO_release_lock (fp); _IO_FINISH (fp);

第二种:利用close

image-20210812160750838

利用:

int status; //# define CHECK_FILE(FILE, RET) do { } while (0) CHECK_FILE(fp, EOF); // 检查文件指针 /* First unlink the stream. */ if (fp->_flags & _IO_IS_FILEBUF) _IO_un_link ((struct _IO_FILE_plus *) fp); _IO_acquire_lock (fp); if (fp->_flags & _IO_IS_FILEBUF) status = _IO_file_close_it (fp);