题目附件https://github.com/nyyyddddn/ctf/tree/main/nssr3d_Iterator_Trap/Iterator_Trap
Iterator_Trap
这是一个关于stl迭代器不安全使用导致uaf的漏洞,第一次出和stl相关的题目
题目逻辑
int __fastcall __noreturn main(int argc, const char **argv, const char **envp)
{
uint32_t choice; // [rsp+Ch] [rbp-54h] BYREF
std::vector<void*> chunklist; // [rsp+10h] [rbp-50h] BYREF
std::vector<int> sizelist; // [rsp+30h] [rbp-30h] BYREF
unsigned __int64 v6; // [rsp+48h] [rbp-18h]
v6 = __readfsqword(0x28u);
std::vector<void *>::vector(&chunklist);
std::vector<int>::vector(&sizelist);
init();
gift();
while ( 1 )
{
while ( 1 )
{
menu();
__isoc99_scanf("%u", &choice);
if ( choice != 3 )
break;
edit_func(&chunklist, &sizelist);
}
if ( choice > 3 )
break;
if ( choice == 1 )
{
create_func(&chunklist, &sizelist);
}
else
{
if ( choice != 2 )
break;
delete_func(&chunklist, &sizelist);
}
}
exit(0);
}
void __cdecl menu()
{
puts("[1] void create(__uint32_t size,char *content)");
puts("[2] void delete(__uint32_t idx)");
puts("[3] void edit(__uint32_t idx,char *content)");
printf(">>> ");
}
void __cdecl gift()
{
void *v0; // rax
v0 = sbrk(0LL);
printf("Welcome to nssctf 3rd. gift: %p\n", v0);
}
void __cdecl create_func(std::vector<void*> *chunklist, std::vector<int> *sizelist)
{
std::vector<int>::size_type v2; // rdx
__gnu_cxx::__alloc_traits<std::allocator<int>,int>::value_type v3; // ebx
std::vector<void*>::size_type v4; // rdx
char **v5; // rax
int size; // [rsp+1Ch] [rbp-24h] BYREF
std::vector<void*>::value_type __x[3]; // [rsp+20h] [rbp-20h] BYREF
__x[1] = (std::vector<void*>::value_type)__readfsqword(0x28u);
puts("size: ");
__isoc99_scanf("%d", &size);
__x[0] = malloc(size);
std::vector<void *>::push_back(chunklist, __x);
std::vector<int>::push_back(sizelist, &size);
puts("content: ");
v2 = std::vector<int>::size(sizelist) - 1;
v3 = *std::vector<int>::operator[](sizelist, v2);
v4 = std::vector<void *>::size(chunklist) - 1;
v5 = (char **)std::vector<void *>::operator[](chunklist, v4);
my_fgets(*v5, v3, 0);
}
ssize_t __cdecl my_fgets(char *buf, int size, int fd)
{
size_t v4; // rdx
char ch_0; // [rsp+17h] [rbp-19h] BYREF
size_t total_read; // [rsp+18h] [rbp-18h]
ssize_t bytes_read; // [rsp+20h] [rbp-10h]
unsigned __int64 v9; // [rsp+28h] [rbp-8h]
v9 = __readfsqword(0x28u);
if ( size <= 0 || !buf )
return -1LL;
total_read = 0LL;
while ( total_read < size - 1 )
{
bytes_read = read(fd, &ch_0, 1uLL);
if ( ch_0 == 10 )
break;
v4 = total_read++;
buf[v4] = ch_0;
}
return total_read;
}
void __cdecl delete_func(std::vector<void*> *chunklist, std::vector<int> *sizelist)
{
std::vector<void*>::size_type M_current_low; // rbx
void **v4; // rax
int i; // [rsp+1Ch] [rbp-34h]
__gnu_cxx::__normal_iterator<int*,std::vector<int> > v6; // [rsp+20h] [rbp-30h] BYREF
__gnu_cxx::__normal_iterator<int*,std::vector<int> > __i; // [rsp+28h] [rbp-28h] BYREF
__gnu_cxx::__normal_iterator<void* const*,std::vector<void*> > idx[3]; // [rsp+30h] [rbp-20h] BYREF
idx[1]._M_current = (void *const *)__readfsqword(0x28u);
for ( i = 0; i < std::vector<int>::size(sizelist); ++i )
{
if ( *std::vector<int>::operator[](sizelist, i) == -1 )
{
v6._M_current = std::vector<int>::begin(sizelist)._M_current;
__i._M_current = __gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator+(&v6, i)._M_current;
__gnu_cxx::__normal_iterator<int const*,std::vector<int>>::__normal_iterator<int *>(
(__gnu_cxx::__normal_iterator<int const*,std::vector<int> > *const)idx,
&__i);
std::vector<int>::erase(sizelist, (std::vector<int>::const_iterator)idx[0]._M_current);
v6._M_current = (int *)std::vector<void *>::begin(chunklist)._M_current;
__i._M_current = (int *)__gnu_cxx::__normal_iterator<void **,std::vector<void *>>::operator+(
(const __gnu_cxx::__normal_iterator<void**,std::vector<void*> > *const)&v6,
i)._M_current;
__gnu_cxx::__normal_iterator<void * const*,std::vector<void *>>::__normal_iterator<void **>(
idx,
(const __gnu_cxx::__normal_iterator<void**,std::vector<void*> > *)&__i);
std::vector<void *>::erase(chunklist, idx[0]);
}
}
puts("idx: ");
__isoc99_scanf("%d", idx);
if ( SLODWORD(idx[0]._M_current) >= 0
&& (M_current_low = SLODWORD(idx[0]._M_current), M_current_low < std::vector<void *>::size(chunklist)) )
{
v4 = std::vector<void *>::operator[](chunklist, SLODWORD(idx[0]._M_current));
free(*v4);
*std::vector<int>::operator[](sizelist, SLODWORD(idx[0]._M_current)) = -1;
puts("success");
}
else
{
puts("error");
}
}
漏洞出在 delete_func这边 在delete前会根据sizelist容器中成员的值,判断哪些是free状态的成员然后再erase这些成员,但是在erase的时候是根据begin() + i 去erase的,erase完后容器的大小发生了变化,下一个begin() + i 就不是原先的 begin() + i了,所以在delete的时候并不能earse连续的free状态的成员,所以存在一个uaf。 然后create的时候size也没有做下界的判断,所以可以通过create_func去创建连续负值的size去构造uaf。
gift 通过sbrk 给了堆地址,就不用泄露堆地址,然后这个edit其实可以当show来用,实现出任意地址申请后,可以申请到堆上vector成员的位置,再配合edit,就可以实现多次任意地址写,泄露出environ后,用任意地址写去写子函数的返回地址打rop去getshell
或者是打fsop house of apple2的利用链,只需要泄露libc还有实现一次任意地址写,之后伪造iofile,宽字符的虚表后就可以getshell
劫持vector 把任意地址申请增强 转换成可以用很简单的方式实现任意地址读写的做法
exp