if (n <= 0) return0; /* This is an optimized implementation. If the amount to be written straddles a block boundary (or the filebuf is unbuffered), use sys_write directly. */
/* First figure out how much space is available in the buffer. */ if ((f->_flags & _IO_LINE_BUF) && (f->_flags & _IO_CURRENTLY_PUTTING)) // 判断为行缓冲模式 { count = f->_IO_buf_end - f->_IO_write_ptr; // 计算输出字节数 if (count >= n) { constchar *p; for (p = s + n; p > s; ) { if (*--p == '\n') // 如果写入数据中含有 \n,那么只写到这一行结尾,并标记 must_flush = 1,后面会触发立即刷新。 { count = p - s + 1; must_flush = 1; break; } } } } elseif (f->_IO_write_end > f->_IO_write_ptr) // 非行缓冲模式 count = f->_IO_write_end - f->_IO_write_ptr; /* Space available. */
/* Then fill the buffer. */ if (count > 0) { if (count > to_do) count = to_do; f->_IO_write_ptr = __mempcpy (f->_IO_write_ptr, s, count); s += count; to_do -= count; } if (to_do + must_flush > 0) // 缓冲区写满或者行缓冲遇到\n设置了must_flush为1 { size_t block_size, do_write; /* Next flush the (full) buffer. */ if (_IO_OVERFLOW (f, EOF) == EOF) // 调用_IO_OVERFLOW刷新缓冲区,真正把缓冲区写入文件(write syscall) /* If nothing else has to be written we must not signal the caller that everything has been written. */ return to_do == 0 ? EOF : n - to_do;
/* Try to maintain alignment: write a whole number of blocks. */ block_size = f->_IO_buf_end - f->_IO_buf_base; // 如果待写入数据超过缓冲区,则直接整块写入 do_write = to_do - (block_size >= 128 ? to_do % block_size : 0);
if (do_write) { count = new_do_write (f, s, do_write); to_do -= count; if (count < do_write) return n - to_do; }
/* Now write out the remainder. Normally, this will fit in the buffer, but it's somewhat messier for line-buffered files, so we let _IO_default_xsputn handle the general case. */ if (to_do) to_do -= _IO_default_xsputn (f, s+do_write, to_do); // 如果还有残余部分(通常小于缓冲区大小),交给 _IO_default_xsputn 来处理 } return n - to_do; } libc_hidden_ver (_IO_new_file_xsputn, _IO_file_xsputn)
int _IO_new_file_overflow (FILE *f, int ch) { if (f->_flags & _IO_NO_WRITES) /* SET ERROR */ { // 检查写入权限标志 f->_flags |= _IO_ERR_SEEN; __set_errno (EBADF); return EOF; } /* If currently reading or no buffer allocated. */ if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0 || f->_IO_write_base == NULL) { // 检查写模式标志和写缓冲区 /* Allocate a buffer if needed. */ if (f->_IO_write_base == NULL) { _IO_doallocbuf (f); //写缓冲区为分配,分配一个 _IO_setg (f, f->_IO_buf_base, f->_IO_buf_base, f->_IO_buf_base); // 设置好buf指针 } /* Otherwise must be currently reading. If _IO_read_ptr (and hence also _IO_read_end) is at the buffer end, logically slide the buffer forwards one block (by setting the read pointers to all point at the beginning of the block). This makes room for subsequent output. Otherwise, set the read pointers to _IO_read_end (leaving that alone, so it can continue to correspond to the external position). *///如果此前是读模式,则调整到写模式 if (__glibc_unlikely (_IO_in_backup (f))) { size_t nbackup = f->_IO_read_end - f->_IO_read_ptr; _IO_free_backup_area (f); f->_IO_read_base -= MIN (nbackup, f->_IO_read_base - f->_IO_buf_base); f->_IO_read_ptr = f->_IO_read_base; }
pwndbg> b exit Breakpoint 1 at 0x7ffff7c455f0: file ./stdlib/exit.c, line 142. pwndbg> r Starting program: /home/r3t2/CTF/pwn_demos/stdout/demo [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". printf addr is --> 0x7ffff7c606f0 stdout addr is --> 0x7ffff7e1b780 ---- input >>> a ---- leak >>>
Breakpoint 1, __GI_exit (status=0) at ./stdlib/exit.c:142 ... pwndbg> c Continuing. a new_data [Inferior 1 (process 55881) exited normally
pwndbg> b exit Breakpoint 1 at 0x7ffff7c455f0: file ./stdlib/exit.c, line 142. pwndbg> r Starting program: /home/r3t2/CTF/pwn_demos/stdout/demo [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". printf addr is --> 0x7ffff7c606f0 stdout addr is --> 0x7ffff7e1b780 ---- input >>> a ---- leak >>> a new_data
Breakpoint 1, __GI_exit (status=0) at ./stdlib/exit.c:142
pwndbg> b exit Breakpoint 1 at 0x7ffff7c455f0: file ./stdlib/exit.c, line 142. pwndbg> r Starting program: /home/r3t2/CTF/pwn_demos/stdout/demo [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". printf addr is --> 0x7ffff7c606f0 stdout addr is --> 0x7ffff7e1b780 ---- input >>> c ---- leak >>> c new_data
Breakpoint 1, __GI_exit (status=0) at ./stdlib/exit.c:142
pwndbg> b exit Breakpoint 1 at 0x7ffff7c455f0: file ./stdlib/exit.c, line 142. pwndbg> r Starting program: /home/r3t2/CTF/pwn_demos/stdout/demo [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". printf addr is --> 0x7ffff7c606f0 stdout addr is --> 0x7ffff7e1b780 ---- input >>> a ---- leak >>> a
Breakpoint 1, __GI_exit (status=0) at ./stdlib/exit.c:142 ... pwndbg> c Continuing. new_data [Inferior 1 (process 76592) exited normally]
for(int i = 0; i < 0x81; i++) { printf("new_data"); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14
pwndbg> b exit Breakpoint 1 at 0x7ffff7c455f0: file ./stdlib/exit.c, line 142. pwndbg> r Starting program: /home/r3t2/CTF/pwn_demos/stdout/demo [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". printf addr is --> 0x7ffff7c606f0 stdout addr is --> 0x7ffff7e1b780 ---- input >>> a ---- leak >>> a new_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_datanew_data Breakpoint 1, __GI_exit (status=0) at ./stdlib/exit.c:142
开了沙箱,打orw,问题是没有show函数,而且全程没有调用io的函数,但是没有开pie,又是静态编译,相当于给了libc地址,我们能做的只有一个unsorted bin attack,这个时候问题就来了,往哪里写,大家如果了解过unsorted bin attack的话,就会知道我们写的其实是top chunk的地址(main_arena中的一定偏移处),就可以根据这个,修改top chunk的位置,迁移到chunk_list上面,达到任意地址写,然后调用fflush,打stdout,泄露栈地址(rsp),最后劫持rsp写rop就行(此题程序执行到这里自己会ret,也就是pop rip)