-
-
原创]无路远征——GLIBC2.37后时代的IO攻击之道(一)house_of_琴瑟琵琶
-
发表于: 2023-2-4 17:31 20568
-
此攻击方式可以在2.34-2.36使用,至于更早的版本,本人没有使用过,看源码应该可以。
这个攻击手段主要是利用_IO_obstack_jumps
,其中_IO_obstack_overflow
和 _IO_obstack_xsputn
都可以触发,攻击链如下。
但实际过程中_IO_obstack_overflow
容易触发assert (c != EOF);
,所以一般选择第二条链。
可以看出其中只有2个函数有赋值,其他都为空。
_IO_obstack_file
只是在_IO_FILE_plus
后面加了一个obstack
的指针,我们可以利用错位利用法。
在刚才跳表中,仅_IO_obstack_overflow
,_IO_obstack_xsputn
两个函数有赋值,函数内容如下。为了避免绕过_IO_obstack_overflow
中的assert (c != EOF);
,我们一般用_IO_obstack_xsputn
。
简单绕过一些内容后用来触发_obstack_newchunk
。
触发CALL_CHUNKFUN
显然这里使用了结构体中的指针(*(h)->chunkfun)((h)->extra_arg, (size))
,并且第一个参数可控,同时需要保证(((h)->use_extra_arg)
为1。
我知道很多师傅就是来抄板子的。
2.37之后取消_IO_obstack_jumps
,但此链通过某种手段仍然有效。
后记:
一次偶然的机会发现,有外国友人也发现了这条链,https://nasm.re/posts/babyfile/#obstack-exploitation
本人没有抢注的意思,只是这个是我自己研究发现的,而且这条链比较简单,所以也写了出来。
_IO_obstack_overflow
obstack_1grow (obstack, c);
_obstack_newchunk (__o,
1
);
new_chunk
=
CALL_CHUNKFUN (h, new_size);
(
*
(h)
-
>chunkfun)((h)
-
>extra_arg, (size))
_IO_obstack_overflow
obstack_1grow (obstack, c);
_obstack_newchunk (__o,
1
);
new_chunk
=
CALL_CHUNKFUN (h, new_size);
(
*
(h)
-
>chunkfun)((h)
-
>extra_arg, (size))
_IO_obstack_xsputn
obstack_grow (obstack, data, n);;
_obstack_newchunk (__o, __len);
new_chunk
=
CALL_CHUNKFUN (h, new_size);
(
*
(h)
-
>chunkfun)((h)
-
>extra_arg, (size))
_IO_obstack_xsputn
obstack_grow (obstack, data, n);;
_obstack_newchunk (__o, __len);
new_chunk
=
CALL_CHUNKFUN (h, new_size);
(
*
(h)
-
>chunkfun)((h)
-
>extra_arg, (size))
/
*
the jump table.
*
/
const struct _IO_jump_t _IO_obstack_jumps libio_vtable attribute_hidden
=
{
JUMP_INIT_DUMMY,
JUMP_INIT(finish, NULL),
JUMP_INIT(overflow, _IO_obstack_overflow),
JUMP_INIT(underflow, NULL),
JUMP_INIT(uflow, NULL),
JUMP_INIT(pbackfail, NULL),
JUMP_INIT(xsputn, _IO_obstack_xsputn),
JUMP_INIT(xsgetn, NULL),
JUMP_INIT(seekoff, NULL),
JUMP_INIT(seekpos, NULL),
JUMP_INIT(setbuf, NULL),
JUMP_INIT(sync, NULL),
JUMP_INIT(doallocate, NULL),
JUMP_INIT(read, NULL),
JUMP_INIT(write, NULL),
JUMP_INIT(seek, NULL),
JUMP_INIT(close, NULL),
JUMP_INIT(stat, NULL),
JUMP_INIT(showmanyc, NULL),
JUMP_INIT(imbue, NULL)
};
/
*
the jump table.
*
/
const struct _IO_jump_t _IO_obstack_jumps libio_vtable attribute_hidden
=
{
JUMP_INIT_DUMMY,
JUMP_INIT(finish, NULL),
JUMP_INIT(overflow, _IO_obstack_overflow),
JUMP_INIT(underflow, NULL),
JUMP_INIT(uflow, NULL),
JUMP_INIT(pbackfail, NULL),
JUMP_INIT(xsputn, _IO_obstack_xsputn),
JUMP_INIT(xsgetn, NULL),
JUMP_INIT(seekoff, NULL),
JUMP_INIT(seekpos, NULL),
JUMP_INIT(setbuf, NULL),
JUMP_INIT(sync, NULL),
JUMP_INIT(doallocate, NULL),
JUMP_INIT(read, NULL),
JUMP_INIT(write, NULL),
JUMP_INIT(seek, NULL),
JUMP_INIT(close, NULL),
JUMP_INIT(stat, NULL),
JUMP_INIT(showmanyc, NULL),
JUMP_INIT(imbue, NULL)
};
struct _IO_obstack_file
{
struct _IO_FILE_plus
file
;
struct obstack
*
obstack;
};
struct obstack
/
*
control current
object
in
current chunk
*
/
{
long
chunk_size;
/
*
preferred size to allocate chunks
in
*
/
struct _obstack_chunk
*
chunk;
/
*
address of current struct obstack_chunk
*
/
char
*
object_base;
/
*
address of
object
we are building
*
/
char
*
next_free;
/
*
where to add
next
char to current
object
*
/
char
*
chunk_limit;
/
*
address of char after current chunk
*
/
union
{
PTR_INT_TYPE tempint;
void
*
tempptr;
} temp;
/
*
Temporary
for
some macros.
*
/
int
alignment_mask;
/
*
Mask of alignment
for
each
object
.
*
/
/
*
These prototypes vary based on
'use_extra_arg'
,
and
we use
casts to the prototypeless function
type
in
all
assignments,
but having prototypes here quiets
-
Wstrict
-
prototypes.
*
/
struct _obstack_chunk
*
(
*
chunkfun) (void
*
,
long
);
void (
*
freefun) (void
*
, struct _obstack_chunk
*
);
void
*
extra_arg;
/
*
first arg
for
chunk alloc
/
dealloc funcs
*
/
unsigned use_extra_arg :
1
;
/
*
chunk alloc
/
dealloc funcs take extra arg
*
/
unsigned maybe_empty_object :
1
;
/
*
There
is
a possibility that the current
chunk contains a zero
-
length
object
. This
prevents freeing the chunk
if
we allocate
a bigger chunk to replace it.
*
/
unsigned alloc_failed :
1
;
/
*
No longer used, as we now call the failed
handler on error, but retained
for
binary
compatibility.
*
/
};
struct _IO_obstack_file
{
struct _IO_FILE_plus
file
;
struct obstack
*
obstack;
};
struct obstack
/
*
control current
object
in
current chunk
*
/
{
long
chunk_size;
/
*
preferred size to allocate chunks
in
*
/
struct _obstack_chunk
*
chunk;
/
*
address of current struct obstack_chunk
*
/
char
*
object_base;
/
*
address of
object
we are building
*
/
char
*
next_free;
/
*
where to add
next
char to current
object
*
/
char
*
chunk_limit;
/
*
address of char after current chunk
*
/
union
{
PTR_INT_TYPE tempint;
void
*
tempptr;
} temp;
/
*
Temporary
for
some macros.
*
/
int
alignment_mask;
/
*
Mask of alignment
for
each
object
.
*
/
/
*
These prototypes vary based on
'use_extra_arg'
,
and
we use
casts to the prototypeless function
type
in
all
assignments,
but having prototypes here quiets
-
Wstrict
-
prototypes.
*
/
struct _obstack_chunk
*
(
*
chunkfun) (void
*
,
long
);
void (
*
freefun) (void
*
, struct _obstack_chunk
*
);
void
*
extra_arg;
/
*
first arg
for
chunk alloc
/
dealloc funcs
*
/
unsigned use_extra_arg :
1
;
/
*
chunk alloc
/
dealloc funcs take extra arg
*
/
unsigned maybe_empty_object :
1
;
/
*
There
is
a possibility that the current
chunk contains a zero
-
length
object
. This
prevents freeing the chunk
if
we allocate
a bigger chunk to replace it.
*
/
unsigned alloc_failed :
1
;
/
*
No longer used, as we now call the failed
handler on error, but retained
for
binary
compatibility.
*
/
};
static
int
_IO_obstack_overflow (
FILE
*
fp,
int
c)
{
struct obstack
*
obstack
=
((struct _IO_obstack_file
*
) fp)
-
>obstack;
int
size;
/
*
Make room
for
another character. This might as well allocate a
new chunk a memory
and
moves the old contents over.
*
/
assert
(c !
=
EOF);
/
/
此处不可控
obstack_1grow (obstack, c);
/
*
Setup the
buffer
pointers again.
*
/
fp
-
>_IO_write_base
=
obstack_base (obstack);
fp
-
>_IO_write_ptr
=
obstack_next_free (obstack);
size
=
obstack_room (obstack);
fp
-
>_IO_write_end
=
fp
-
>_IO_write_ptr
+
size;
/
*
Now allocate the rest of the current chunk.
*
/
obstack_blank_fast (obstack, size);
return
c;
}
static
int
_IO_obstack_overflow (
FILE
*
fp,
int
c)
{
struct obstack
*
obstack
=
((struct _IO_obstack_file
*
) fp)
-
>obstack;
int
size;
/
*
Make room
for
another character. This might as well allocate a
new chunk a memory
and
moves the old contents over.
*
/
assert
(c !
=
EOF);
/
/
此处不可控
obstack_1grow (obstack, c);
/
*
Setup the
buffer
pointers again.
*
/
fp
-
>_IO_write_base
=
obstack_base (obstack);
fp
-
>_IO_write_ptr
=
obstack_next_free (obstack);
size
=
obstack_room (obstack);
fp
-
>_IO_write_end
=
fp
-
>_IO_write_ptr
+
size;
/
*
Now allocate the rest of the current chunk.
*
/
obstack_blank_fast (obstack, size);
return
c;
}
static size_t _IO_obstack_xsputn (
FILE
*
fp, const void
*
data, size_t n)
{
struct obstack
*
obstack
=
((struct _IO_obstack_file
*
) fp)
-
>obstack;
if
(fp
-
>_IO_write_ptr
+
n > fp
-
>_IO_write_end)
{
int
size;
/
*
We need some more memory. First shrink the
buffer
to the
space we really currently need.
*
/
obstack_blank_fast (obstack, fp
-
>_IO_write_ptr
-
fp
-
>_IO_write_end);
/
*
Now grow
for
N bytes,
and
put the data there.
*
/
obstack_grow (obstack, data, n);
/
/
执行次函数
/
*
Setup the
buffer
pointers again.
*
/
fp
-
>_IO_write_base
=
obstack_base (obstack);
fp
-
>_IO_write_ptr
=
obstack_next_free (obstack);
size
=
obstack_room (obstack);
fp
-
>_IO_write_end
=
fp
-
>_IO_write_ptr
+
size;
/
*
Now allocate the rest of the current chunk.
*
/
obstack_blank_fast (obstack, size);
}
else
fp
-
>_IO_write_ptr
=
__mempcpy (fp
-
>_IO_write_ptr, data, n);
return
n;
}
static size_t _IO_obstack_xsputn (
FILE
*
fp, const void
*
data, size_t n)
{
struct obstack
*
obstack
=
((struct _IO_obstack_file
*
) fp)
-
>obstack;
if
(fp
-
>_IO_write_ptr
+
n > fp
-
>_IO_write_end)
{
int
size;
/
*
We need some more memory. First shrink the
buffer
to the
space we really currently need.
*
/
obstack_blank_fast (obstack, fp
-
>_IO_write_ptr
-
fp
-
>_IO_write_end);
/
*
Now grow
for
N bytes,
and
put the data there.
*
/
obstack_grow (obstack, data, n);
/
/
执行次函数
/
*
Setup the
buffer
pointers again.
*
/
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
赞赏
- [原创]反序列化的前生今世 9081
- [原创]gdb在逆向爆破中的应用 3037
- [原创]EOP编程 8860
- [原创]格式化字符串打出没有回头路(下)——回头望月 45080
- [原创]格式化字符串打出没有回头路(上) 16528