Pwnable.kr horcruxes
复制本地路径 | 在线编辑
int ropme()
{
char s[100]; // [esp+4h] [ebp-74h]
int v2; // [esp+68h] [ebp-10h]
int fd; // [esp+6Ch] [ebp-Ch]
printf("Select Menu:");
__isoc99_scanf("%d", &v2);
getchar();
if ( v2 == a ) { A(); }
else if ( v2 == b ) { B(); }
else if ( v2 == c ) { C(); }
else if ( v2 == d ) { D(); }
else if ( v2 == e ) { E(); }
else if ( v2 == f ) { F(); }
else if ( v2 == g ) { G(); }
else {
printf("How many EXP did you earned? : ");
gets(s);
if ( atoi(s) == sum )
{
fd = open("flag", 0);
s[read(fd, s, 0x64u)] = 0;
puts(s);
close(fd);
exit(0);
}
puts("You'd better get more experience to kill Voldemort");
}
return 0;
}
很明显,ropme 函数中 gets 存在栈溢出漏洞,但直接返回地址修改为 fd=open("flag", 0) 无法执行,测试发现 ropme 函数任何位置都无法作为篡改的返回地址。
所以还是要老老实实让 atoi(s) == sum 成立。sum 是在 init_ABCDEFG() 这个一开始程序就执行的函数中计算的:
unsigned int init_ABCDEFG()
{
int v0; // eax
unsigned int result; // eax
unsigned int buf; // [esp+8h] [ebp-10h]
int fd; // [esp+Ch] [ebp-Ch]
fd = open("/dev/urandom", 0);
if ( read(fd, &buf, 4u) != 4 )
{
puts("/dev/urandom error");
exit(0);
}
close(fd);
srand(buf);
a = 0xDEADBEEF * rand() % 0xCAFEBABE;
b = 0xDEADBEEF * rand() % 0xCAFEBABE;
c = 0xDEADBEEF * rand() % 0xCAFEBABE;
d = 0xDEADBEEF * rand() % 0xCAFEBABE;
e = 0xDEADBEEF * rand() % 0xCAFEBABE;
f = 0xDEADBEEF * rand() % 0xCAFEBABE;
v0 = rand();
g = 0xDEADBEEF * v0 % 0xCAFEBABE;
result = f + e + d + c + b + a + 0xDEADBEEF * v0 % 0xCAFEBABE;
sum = result;
return result;
}
可以看出要知道 a-g 这些随机数的值,而回到 ropme 函数,可以看到有 A-G 这些函数,他们会打印 a-g 的值。
int A() {
return printf("You found \"Tom Riddle's Diary\" (EXP +%d)\n", a);
}
但 ropme 只执行一次,所以利用上面的 gets 漏洞,把栈里面的返回地址修改为 A() + B() + ... + G() + ropme()。
这样第一次执行 ropme 的时候,返回地址被篡改,最后会执行 A()。由于 A 函数没有用到栈,A 执行完后,此时栈中的返回地址就是之前 ropme 函数执行时栈的返回地址下一个地址,也就是我们篡改的 B(),所以就会跳转到 B() 函数执行。
反复之后,最后一次执行 ropme,我们已经算出了 a+..+g 的值,所以 atoi(s) == sum 成立,最终会输出 flag。
关键点其实是 gets 栈溢出漏洞的时候,返回地址无法修改成 ropme 函数的任意地方,只能修改成 A-G 和 main 函数中调用 ropme 的地方(即 call ropme)。