Pwnable.kr echo2
复制本地路径 | 在线编辑
源代码漏洞
简化后的代码如下,程序可以无限选择执行 echo2 或 echo3 或 cleanup 函数。直接说漏洞点:很明显 echo2 有 FSB 漏洞, echo3 加上 cleanup 有 UAF 漏洞。
__int64 echo2()
{
char format; // [rsp+0h] [rbp-20h]
(*((void (__fastcall **)(void *))o + 3))(o);
get_input(&format, 32);
printf(&format, 32LL);
(*((void (__fastcall **)(void *))o + 4))(o);
return 0LL;
}
__int64 echo3()
{
char *s; // ST08_8
(*((void (__fastcall **)(void *))o + 3))(o);
s = (char *)malloc(0x20uLL);
get_input(s, 32);
puts(s);
free(s);
(*((void (__fastcall **)(void *, signed __int64))o + 4))(o, 32LL);
return 0LL;
}
void cleanup() { free(o); }
int main(int argc, const char **argv, const char **envp)
{
unsigned int choice; // [rsp+Ch] [rbp-24h]
__int64 v7; // [rsp+10h] [rbp-20h]
__int64 v8; // [rsp+18h] [rbp-18h]
__int64 v9; // [rsp+20h] [rbp-10h]
printf("hey, what's your name? : ", 0LL);
__isoc99_scanf("%24s", &v7);
o = malloc(0x28uLL);
*((_QWORD *)o + 0) = v7;
*((_QWORD *)o + 1) = v8;
*((_QWORD *)o + 2) = v9;
*((_QWORD *)o + 3) = greetings;
*((_QWORD *)o + 4) = byebye;
getchar();
func[0] = (__int64)echo1;
func[1] = (__int64)echo2;
func[2] = (__int64)echo3;
do {
while ( 1 )
{
puts("\n- select echo type -");
puts("- 1. : BOF echo");
puts("- 2. : FSB echo");
puts("- 3. : UAF echo");
puts("- 4. : exit");
__isoc99_scanf("%d", &choice);
getchar();
((void (__fastcall *)(const char *, unsigned int *))func[choice - 1])("%d", &choice);
puts("invalid menu");
}
cleanup();
printf("Are you sure you want to exit? (y/n)");
choice = getchar();
} while ( choice != 121 );
return 0;
}
别人的做法
这道题其实很简单很简单,流程如下。
- 首先通过
echo2泄露ebp的地址,从而可以计算出name的地址,这个name是我们事先输入的shellcode。 - 然后执行
4. exit,从而触发一个cleanup函数,从而会把o这个 0x20 大小的变量free掉。 - 最后执行
echo3,导致申请的s就是之前释放的o,因此可以改变o + 4为name地址,这样执行o + 4,就相当于执行了name,即我们事先输入的shellcode。
我的做法
我这个做法有点硬核了,竟然直接通过 FSB 暴力做出来了,下面来详细讲一讲。
- 同样,
name需要输入可以执行的shellcode。 - 同样, 利用
echo2泄露o的内容。其中o是在 BSS 段上的变量,地址是 0x602098,里面存的 malloc 指针。
# o = malloc(0x28uLL); '%8$s' + 'aaaa' + p64(o_addr)
2.1 分析上面的泄露,主要在于使用了 %s,因此这次泄露我们可以获得 o 的内容,也就是那个 malloc 指针。
一定要分清楚,上面这条语句输出的就是那个指针的地址,而不是 name + greetings + byebye.
scanf("%24s", &v7); // v7, v8, v9
*((*QWORD)o + 0) = v7; *((*QWORD)o + 1) = v8; *((*QWORD)o + 2) = v9;
*((*QWORD)o + 3) = greetings;
*((*QWORD)o + 4) = byebye;
2.2 假设这个指针是
heap_addr,那么很显然,构造结构是这样的。heap_addr + 0x00 --> [ name ]
heap_addr + 0x18 --> [ greetings ]
heap_addr + 0x20 --> [ byebye ]
- 最后,把
byebye_ptr改成name_addr,泄露格式如下。
padding = ('%' + str(heap_addr) + 'c%8$n').ljust(16, 'a') padding = (padding + p64(heap_addr + 0x20))
总结
很不错的小题,总结的话确实不知道怎么归类,可以回顾回顾看看,不费多长时间。