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;
}
}
}
可以看到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/
利用:
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
利用:
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);