드디어 마지막 문제 Level20입니다.

우선 힌트를 확인하도록 하겠습니다.


 

… gets에서 79글자만 입력 받아오네요. BOF로는 어려울듯 합니다.

하지만 그래도 아직 취약한 코드는 남아 있습니다.

바로 printf(bleh);입니다.

이 코드는 Format String Bug를 갖고 있습니다.

 

우선 간단하게 Format String Bug에 대해서 설명 드리겠습니다.

일반적으로 printf는 다음과 같이 사용합니다.

Char buf[] = “Hello World”;

Printf(“%s\n”, buf);

매우 일반적인 사용법이죠.

그런데 printf(“%s\n”, buf); 대신에 printf(buf); 이렇게 사용해도 똑같이 문자열이 출력 됩니다.

그래서 편의성을 위해서 printf(buf);와 같은 코드를 사용하는 사람들도 상당히 많죠.

근데 여기서 생각해 보아야 할 것이 있습니다.

만약, 버퍼에 일반적인 문자열이 아니고 형식지정자를 넣으면 어떻게 될까요?

그럼 printf(“%s %d”); 이런 코드가 되지 않을까요?

한번 확인해 보도록 하겠습니다.

 

일반적으로 buf에 문자열을 입력한 경우입니다.

정상적으로 문자열이 출력 되었네요.

 

그럼 이번엔 형식 지정자를 입력해 보도록 하겠습니다.

뜬금 없이 4f가 나왔네요. 4f79이므로 fgets에서 사용하기위해 스택에 push한 값이 남아 있는게 아닌가 싶습니다.

형식 지정자를 여러게 넣어보도록 하겠습니다.

.. 여러 값들이 나오네요..

아직은 잘 모르겠습니다. 이번엔 일반 문자 뒤에 형식 지정자를 이어서 넣어보도록 하겠습니다.

이번에도 여러 값들이 나왔는데 익숙한 41414141이 보입니다. 이는 우리가 입력해준 AAAA의 아스키 코드 값이죠.

이를 통해서 형식 지정자를 입력해주면 stack의 값들을 확인할 수 있음을 알 수 있습니다.

 

여기서 잠시 printf를 호출할때 스택의 상황을 한번 생각해보도록 하겠습니다.

Printf(“%s %d”, buf, x);의 경우 스택은 다음과 같이 될것입니다.

Buf : Hello World

X : 40

………

&x

&buf

……….

어셈블리어 언어로 생각하면 x의 주소를 push하고 buf의 주소를 push 하고 printf함수를 호출하겠죠.

그렇게 되면 %s 형식 지시자는 &buf의 내용을 해석해서 값을 읽어오고, %d&x의 내용을 해석해서 값을 읽어 옵니다.

 

AAAA %x %x %x %x라고 값을 입력하면 스택 구조는 다음과 같이 되겠죠.

%x

%x

%x

%x

Buf : AAAA

0x4207a750

0x4212ecc0

0x4f

&buf

 

Printf(buf)했으니 바로 buf의 내용을 가져오게 됩니다.

내용은 AAAA %x %x %x %x이구요. 따라서 AAAA를 출력한 뒤 %x를 만나 그 다음 스택의 정보들을 출력하게 되는겁니다. %x4개니 전체 출력 결과는 AAAA 4f 4212ecc0 4207a750 41414141이 되는거구요. 만약 %x4개 입력 입력하면 그 위 “ %x “에 해당하는 아스키 값까지 출력하게 될것입니다. 그 값은 20782520이구요.

이것이 바로 Format String Bug입니다.

 

그런데 형식 지정자로 값을 읽어 오는것은 좋은데, 값을 입력하지 못하면 메모리 변조를 할수가 없으니 이대로는 무용지물입니다.

하지만 형식지정자 중에 유일하게 값을 입력하는 형식지정자가 있습니다. 바로 %n입니다. %n은 바로 이전에 출력한 값의 크기를 해당 변수에 저장하는 기능을 합니다.

코드에서 보면 알수 있다시피 1234를 출력하였을 경우에 %n을 통해 n에 출력 크기 4byte가 입력 되었음을 알 수 있습니다.

 

이를 이용해 원하는 위치에 원하는 값을 입력 할 수 있겠네요.

 

그럼 한번 원하는 주소에 %n을 이용해 값을 입력할 수 있도록 스택을 짜보겠습니다.

%n

%c

%n

%c

0xbffff2bc

AAAA

0xbfff2ba

AAAA

 

이러한 스택을 생각해 보겠습니다.

AAAA\xba\xf2\xff\xbfAAAA\xbffff2bc%c%n%c%n이라고 입력하면 이렇게 스택이 구성됩니다.

그럼 우선 AAAA\xba\xf2\xff\xbfAAAA\xbffff2bc가 출력이 되곘죠.

그리고 %c를 만나서 AAAA가 들어있는 스택에 대하여 진행하고, 그리고 %n을 만나게 됩니다. AAAA위에 있는 스택은 0xbffff2ba이죠. 따라서 메모리 0xbffff2ba번지에 %n을 통해 현재까지 출력한 크기를 입력하게 됩니다.

AAAA\xba\xf2\xff\xbfAAAA\xbffff2bc이므로 16byte0xbffff2ba에 입력되게 됩니다. 그다음에 만나는 값은 %c이므로 AAAA를 건너뛰고 다시 %n을 만나 그다음 스택인 0xbffff2bc주소에 16을 입력하게 됩니다.

 

그러나 우리가 입력해야 할 주소값은 굉장히 큰 숫자입니다. 그 많은 문자를 직접 입력하기에는 메모리 용량도 그렇고, 힘도 들기 때문에 형식 지정자의 편리함을 이용하면 됩니다.

만약 형식지정자를 %100d라고 입력하면 공백이 100개 입력이 됩니다. 즉 이를 이용하면 쉽게 원하는 만큼의 입력이 가능합니다.

 

이러한 방식으로 원하는 주소에 원하는 값을 입력해주면 됩니다.

 

그럼 우선 쉘코드를 등록하고, 주소를 얻어오도록 하겠습니다.

쉘코드를 등록하였고 주소는 0xbffffc6e입니다.

 

쉘코드를 얻어왔으니 이제 ret의 주소값을 얻어올 차례입니다. 

그러나 gdb로 확인했더니 main symbol을 찾을 수 없다고 나옵니다.

 

이때 사용할 수 있는 것이 .dtors영역이라는 것입니다.

간단하게 설명하면 gcc는 컴파일 할때 .ctors.dtors 두 세그먼트를 생성합니다.

 

두 영역의 특징은

.ctros 속성의 함수는 main()전에 실행되고

.dtros 속성의 함수는 main()종료 후에 실행 된다는 것입니다.

그러므로 .dtros 세그먼트에 쉘코드의 주소를 넣어주면 main()이 종료된 뒤에 실행이 될것입니다.

 

그럼 .dtors의 주소를 알아내도록 하겠습니다.

.dtors 세그먼트의 주소는 0x08049594이네요. 이 주소에 4byte를 더한 0x08049598에 쉘코드의 주소를 등록해 주면 됩니다.

 

그러면 필요한 정보는 다 구해졌습니다.

쉘코드의 주소는 0xbffffc6e이고 저장이 되는 대상 메모리의 주소는 0x08049598입니다.

 

AAAA\x98\x95\x04\x08%3221224558c%n이렇게 입력해 주면 될 것 같습니다.

하지만 몇가지 더 보완을 해주어야 합니다.

그림과 같이 AAAA가 들어있는 메모리에 접근하기 까지 형식지정자를 4개를 사용했습니다. 따라서 AAAA를 받아오는 형식지정자 %c앞에 다른 형식지정자를 3개 추가해 주어야 합니다.

 

추가된 입력은 다음과 같습니다.

AAAA\x98\x95\x04\x08%x%x%x%3221224558c%n

그러나 여기서 문제가 하나 더 발생합니다. X86 pc 에서는 3221224558과 같은 큰 수를 입력 할 수 없습니다. 따라서 0xBFFFFC6E를 반씩 나눠서 입력해야 합니다.

0x08049598에는 0xFC6E2바이트 증가한 0x0804959A에는 0xBFFF을 입력해 주면 됩니다.

 

이를 반영한 입력은 다음과 같습니다.

AAAA\x98\x95\x04\x08AAAA\x9a\x95\x04\x08%x%x%x%64622c%n%49151c%n

그러나 아직 문제가 하나더 남았습니다. %n%n이전까지의 입력 용량을 저장한다고 했습니다.

AAAA\x98\x95\x04\x08AAAA\x9a\x95\x04\x08%x%x%x%64622c%n에서 %n까지의 입력 용량은 4+4+4+4+?+?+?+64622입니다.

우선 %x는 메모리에 어떤 값이 있느냐에 따라서 출력 결과가 다르기 때문에 8byte로 통일해주어야 합니다. %x대신에 %8x를 쓰면 균일하게 8byte가 출력됩니다.

그럼 입력되는 값이 4+4+4+4+8+8+8+64622이므로 64622에 맞추기 위해서는 64622에서 40만큼 빼주어야 합니다.

 

따라서 입력은 다음과 같이 변경 됩니다.

AAAA\x98\x95\x04\x08AAAA\x9a\x95\x04\x08%8x%8x%8x%64582c%n%49151c%n

마찬가지로 뒤에 49151도 변경해 주어야 합니다. 그 이전까지 출력된 양이 64622이므로 49151에서 64622를 빼어 주어야 하는데 음수가 발생합니다. 따라서 0x1BFFF에서 0xFC6E를 뺀 값인 50065를 넣어주면 됩니다.

 

이렇게 최종적으로 변경된 입력은 다음과 같습니다.

AAAA\x98\x95\x04\x08AAAA\x9a\x95\x04\x08%8x%8x%8x%64582c%n%50065c%n

 

입력값을 넣어 보도록 하겠습니다.

다행이도 쉘이 떴습니다.

계정명이 clear네요.


패스워드는 i will come in a minute입니다.

 

이렇게 모든 ftz 문제를 다 풀었네요.

작년 1223일쯤 시작했으니거의 1년 넘게 걸렸네요. 도중에 반년 정도 쉰적도 있고.. 레벨 11부터 20까지 푸는데에는 10일 정도 걸렸네요.

 

이제 FTZ는 끝났으니 다른 문제로 돌아오겠습니다.

 

수고 많으셨습니다.

Level10을 들어가서 hint를 입력하면 다음과 같이 나옵니다.



두 명의 사용자가 대화방을 이용하여 비밀스런 대화를 나누고 있다.

그 대화방은 공유 메모리를 이용하여 만들어졌으며,

key_t의 값은 7530이다. 이를 이용해 두 사람의 대화를 도청하여 level11의 권한을 얻어라.

 

이번 문제는 공유 메모리를 도청하는 문제로 보입니다.

 

공유 메모리는 여러 프로세스가 같이 쓰는 메모리를 말합니다. 이 기술을 이용하면 프로세스 끼리 통신을 할 수 있으며, 같은 데이터를 공유할 수 있습니다.

같은 메모리 영역을 공유하기 위해서는 공유메모리를 생성한 후 프로세스 자신의 메모리를 사용하듯 사용하면 됩니다.

 



ipcs를 쓰면 현재 사용하고 있는 공유 메모리의 정보가 출력됩니다.

Key: 0x1D6A(7530)

Owner: root

Perms: 666

Bytes: 1028

 

권한이 666으로 되어 있으니 모든 사용자가 볼 수 있겠군요.

C언어로 공유메모리 접근 프로그래밍을 해줍니다.


http://forum.falinux.com/zbxe/index.php?document_srl=423456&mid=C_LIB

코드는 위 링크를 참고하여 작성했습니다.

 

컴파일 한 후 프로그램을 돌리면 level11 암호가 출력됩니다.



Level11의 암호는 what!@#$?이네요.

 

공유메모리를 사용할때는 권한 설정을 잘 해야겠습니다.

FTZ level6에 로그인하면 이전의 문제와 달리 힌트가 자동으로 등장합니다.



hint – 인포샵 bbs의 텔넷 접속 메뉴에서 많이 사용되던 해킹 방법이다.

아직은 무슨 소린지 모르겠네요.

이 화면에서 아무키나 누르면 다음 화면으로 진행됩니다.


텔넷 접속 서비스 1.하이텔  2.나우누리  3.천리안

2000년대 초반에 존재하던 서비스들이 보이네요.

하이텔을 선택하기 위해 1을 입력 하였습니다.


접속이 거절 되었습니다. 하이텔이나 나우누리로 접속해도 결과는 마찬가지 였습니다.

우선 hint를 한번 짚고 넘어가야겠습니다.

bbs란 예전에 PC 통신 할때 텔넷으로 접속하던 게시판이라 생각하시면 됩니다. 하이텔 나우누리 천리안이 가장 유명했던 bbs들이었죠.

 

위의 사진을 보고 메뉴를 선택하면 나오는 다음 문구는 telnet을 시도할때 나오는 문구입니다.

메뉴를 선택하면 조건에 맞는 ip값이 전달되고 텔넷 명령어가 시작 되는거죠.

1번 메뉴를 눌렀을 때 telnet 203.245.15.76이라는 리눅스 명령어가 실행이 되고 이는 코드상에서 system(“telnet 203.245.15.76”)이런 식으로 구성 되어 있을 겁니다.

 

직접 ip를 집어 넣는 것이면 명령어 구분자 ‘ ; ‘를 이용해서 명령어를 입력할 수 있을 듯 하나 메뉴를 선택 하면 미리 셋팅 되어 있는 명령어가 실행되니 이 방법으론 접근이 쉽지 않을듯 합니다.

 

우리의 목표를 잠시 생각해 보면, level7권한으로 my-pass명령어를 사용해 level7의 명령어를 획득하는게 목표입니다. 근데 로그인하면 자동으로 실행되는 저 프로그램 때문에 아무런 명령어를 입력하지 못하는 상황이네요.

일단 저 프로그램을 종료 시켜서 쉘을 띄우는것이 우선이라 생각됩니다.

 

리눅스에서 실행 프로세스를 죽이는 방법인 Ctrl+C를 눌러봅니다.



ctrl+c는 쓸 수 없다고 하네요.

 

이것 때문에 삽질을 5시간 정도 한것 같네요. Ctrl+d도 해보고 ctrl+z도 해보고 다른 프로세스 인터럽트 방법도 찾아보고하지만 정답은 정말 간단했습니다.

 

저 창이 뜨기 전에 하면 되요.



이처럼 hint가 나왔을때 ctrl+c를 눌러 프로그램을 종료하면 됩니다.

 

그럼 명령어를 입력할수 있는 쉘이 뜹니다.

목록이 뭐있는지 확인하고, 매우 의심스러운 파일의 내용을 확인해보면,



level7의 비밀번호 come together이 나옵니다.

 

굉장히 허탈하고 쉬운 문제였네요.

 

리눅스에서 프로세스를 종료하는 ctrl+c를 알고 있는지에 대한 문제였습니다.

Level4에서 구한 암호를 입력하고 들어가서 우선 파일목록을 한번 봐줍니다.



hint가 보이네요. 당연히 읽어줍시다.



/usr/bin/level5 프로그램은 /tmp 디렉토리에 level5.tmp라는 이름의 임시파일을 생성한다.

이를 이용하여 level6의 권한을 얻어라.

 

우선 /usr/bin/level5를 확인해 봅시다.



level6 setuid가 설정 되어 있네요. 느낌상 저걸 실행하면 my-pass명령어를 통해 level6의 비밀번호를 알 수 있고, 그것이 /tmp/level5.tmp에 저장될 것 같습니다.

/usr/bin/level5를 실행하고 /tmp/level5.tmp에 암호가 있나 확인해봅니다.



/usr/bin/level5를 실행하고 /tmp폴더를 확인해봤는데 level5.tmp는 보이지 않습니다.

아마 /usr/bin/level5프로그램이 종료될 때 임시파일을 지우는 듯 합니다.

 

level5.tmp에 들어있는 내용을 읽는게 이번 level의 문제 요지입니다.

여기서 필요한 공격기법이 레이스 컨디션입니다.

setuid와 임시파일을 듣고 레이스 컨디션을 생각하시면 됩니다.

 

레이스 컨디션을 간단하게 얘기하면 일종의 바꿔치기라 할 수 있습니다. 다른 권한으로 실행되는 프로그램 중간에 끼어들어 자신이 원하는 작업을 하는 기술입니다. 타이밍이 가장 중요하기 때문에 보통 될때까지 실행합니다.

 

레이스 컨디션 공격의 기본적인 아이디어는 다른 권한의 프로그램에 의해 생성되고 사용되는 임시 파일에 대한 것입니다.

 

레이스 컨디션 공격에 앞서 파일 링크라는 개념이 필요한데, 이는 다른 블로그에 잘 정리되어 있으니 여기선 설명하지 않겠습니다.

구글에 리눅스 링크라고만 검색해도 많이 나옵니다.

 

레이스 컨디션 공격을 할때 값을 저장할 임의 파일을 하나 생성을 합니다. 그리고 그 임의 파일의 심볼릭 링크를 생성하여 주는데 이때 이름을 프로그램에서 생성하는 임시파일로 만듭니다. 즉 심볼릭 링크의 이름이 /tmp/level5.tmp가 되는 것이죠.

이렇게 되면 프로그램은 권한 상승이 된 상태에서 링크파일에 데이터를 덮어 씌우고, 그 값이 임의 파일에 저장되게 됩니다.

 

그러나 프로세스 연산과정의 순서로 인하여 권한 상승이 끝난뒤 접근하여 값이 변경 되지 않을 수도 있기 때문에 반복하여 될때까지 하는게 일반적입니다.

 

따라서 프로그램으로 작성하였습니다.

/usr/bin/level5를 반복적으로 실행할 func프로그램입니다.



반복적으로 /tmp/level5.tmp라는 이름의 심볼릭 링크를 생성해주는 link프로그램입니다.



값은 /home/level5/tmp/passwd파일에 저장될 것입니다.

이제 연결을 하나 더 여시고 한쪽에서는 func프로그램을 실행해주고, 다른 한쪽에서는 link프로그램을 실행하여 줍니다.

 

프로그램을 몇초정도 실행하고 ^z로 둘다 멈춘후에 /home/level5/tmp/passwd파일의 내용을 확인하면 level6의 암호가 들어가 있는것을 볼 수 있습니다.


level6의 암호는 what the hell이네요.

레이스 컨디션 공격을 막기 위해서는 먼저 프로그램 로직 중에 임시 파일을 생성한 후, 임시 파일에 접근하기 전에 임시 파일에 대한 심볼릭 링크 설정 여부와 권한에 대한 검사과정을 추가해 주면 됩니다. 

 

이런저런 바쁜일로 정말 오랜만에 포스팅 하네요..

이번엔 FTZ level 4를 풀어보도록 하겠습니다.

서버에 접속해 어느 때와 같이 힌트를 확인해 봅니다.

/etc/xinetd.d/에 누군가 백도어를 심어 놓았다고 하네요.


우선 /etc/xinetd.d가 뭐하는 곳인지 알아봐야겠죠?

/etc/xinetd.d/는 리눅스 인터넷수퍼데몬(xinetd)의 서비스파일들이 들어있는 디렉토리입니다.

xinetd는 인터넷 수퍼데몬(Internet Super Daemon)을 의미하는 것으로서 SENDMAIL, HTTPD 등과 같이 리눅스 시스템에서 실행되는 데몬들 중 하나입니다.
하지만 이 데몬은 리눅스 서버에서 서비스되는 여러 가지 데몬들을 제어하면서 각각 서비스의 연결을 담당하고 있기 때문에 수퍼데몬이라 불립니다.

xinetd에 의해 제어되는 가장 대표적인 서비스 중에는 telnet이 있습니다.
텔넷으로 접속을 시도할 때에는 바로 telnet으로 연결되는 것이 아니라 우선 xinetd에 의해 허가된 사용자인지를 검사합니다.
그리고 xinetdtelnet 설정파일 /etc/xinetd.d/telnet에 정의되어 있는 telnet 서비스데몬과 연결되어 사용자는 telnet 서비스를 비로소 이용하게 됩니다.


/etc/xinetd.d를 한번 확인해 보도록 합시다.

들어갔더니 버젓이 backdoor라는 파일이 보이네요. 내용을 확인해 봅시다.


백도어라 해서 들어가 봤더니 서비스 이름이 finger로 되어있네요.

각 속성들의 의미하는 바는 아래 표와 같습니다.(더 많은 정보는 xinetd.conf메뉴얼을 참고)

속성

값 및 설명

disable

나열된 서비스 값들이 실행되지 못하도록 지정.

enable 속성과 같이 존재하면, enable 속성은 무시.

flags

REUSE : 포트가 사용중(TIME_WAIT)의 경우에서도 재이용할 수 있도록 지원

INTERCEPT : 패킷 전송을 중단해, 허가된 발신처로부터 오고 있는지 확인

NORETRY : 인증에 실패할 경우 같은 서비스에 접속을 허가하지 않음.

IDONLY : 리모트 호스트로 인증되는 유저의 경우만 접속을 허가(로그 옵션의 USERID와 병용 필요)

NAMEINARGS : tcpd를 사용하는 경우에, server_arg로 지정하는 변수가 서비스에 인수(argv[0])으로 받은.

NODELAY : TCP 접속의 경우, TCP_NODELAY 플래그를 셋(set)

DISABLE : 서비스가 실행되지 않게 한다.

KEEPALIVE : TCP 접속의 경우, keep_alive가 셋(set)

 

socket_type

4가지 선택 가능한 값이 있다.

stream : stream 기반의 서비스

dgram : datagram 기반의 서비스

raw : 아이피에 직접 접근을 요하는 서비스

seqpacket : 신뢰성 있는 연속적인 데이터그램 전송을 요구하는 서비스

wait

서비스가 단일 스레드인지, 다중 스레드인지를 결정하는 플래그로 yes는 단일, no는 다중 스레드

user

서버 프로세스를 실행할 수 있는 사용자의 ID를 나타내는 것으로 슈퍼유저일 경우에만 효과를 낼 수 있음.

server

해당 서비스를 실행할 데몬 프로그램의 위치 지정.

log_on_failure

서버가 리소스 부족으로 시작될 수 없거나 설정 파일 내의 규칙에 의한 접근이 거부되었을 때 기록될 값들을 지정.

다음의 4가지 값을 조합 지정 가능.

HOST : 원격 호스트의 IP

USERID : 원격 사용자의 ID

ATTEMPT : 실패한 시도가 있을 경우

RECORD : 클라이언트에 대한 가능한 정보


finger 서비스는 시스템의 사용자정보를 확인하는 서비스입니다.
(자세한 내용은 http://korea.gnu.org/manual/release/finger/finger-ko_3.html 를 참고하시면 됩니다.ㅎ)

설정에 따르면 finger 서비스를 실행하면 level5의 권한으로 /home/level4/tmp/backdoor을 실행합니다.



디렉토리의 내용을 살펴봤는데 역시나 없네요.ㅎㅎ
(저의 경우에는 개인 구축을 했기때문에 파일이 없습니다. 해커스쿨 서버에서 하시는 분들은 파일이 있을 수도 있습니다.)


없으니 만들어 줍시다.

#cd /home/level4/tmp를 통해 해당 디렉토리로 이동하구요.
#vi backdoor.c를 통해 파일 작성을 합니다.

i를 눌러서 편집모드로 들어간후 명령어를 쭉 처준 뒤에
esc를 눌러서 명령모드로 들어가 wq를 입력해 저장 후 종료하면 됩니다.
(vi사용법은 https://mirror.enha.kr/wiki/vi에서...... ㅎㅎ)

간단한 C코드로 my-pass 명령어를 실행하라는 프로그램입니다.


코드를 짰으면 이제 컴파일을 할 차례이겠지요?

gcc를 이용해 컴파일을 해주면 됩니다.


이제 준비는 다 되었구요.
finger 서비스는 79번 포트를 기본으로 사용하고 있습니다.

원격 접속할때 79번 포트로 접속하면 바로 서비스를 이용할 수 있지요.


그럼 이제 finger 서비스를 이용하면 아까 짠 코드를 통해 비밀번호를 알 수 있을것입니다.

따로 접속하기 귀찮으니 그냥 바로 telnet으로 로컬 호스르틀 접속하도록 합시다. ㅎㅎ


짜잔!!

드디어 Level5의 암호를 얻었습니다.

암호는 what is your name?입니다.


수고하셨습니다. 조만간 level5 문제 풀이로 돌아오겠습니다.

level2를 풀고 얻은 password를 가지고 로그인을 하면 역시나 hint가 보입니다.

당연히 hint를 확인 해주어야겠죠.


힌트가 뭔가 길어졌습니다.

단순히 문장 한줄이었던 예전과 다르게 이번엔 autodig의 소스코드가 주어졌네요.

일단 소스코드를 분석해 봅시다.


우선 argc란 프로그램을 실행할 때 넘겨주는 인자의 개수를 말합니다.
$ ls -s / 라고 프로그램을 실행하였다면 총 3개의 인자값을 가지게 됩니다.
argc[0] = ls
argc[1] = -s
argc[2] = /
이렇게 총 3개의 인자값을 갖게 되죠. argc[0]는 항상 프로그램 자기자신을 가르킵니다.

소스를 보면 if( argc!=2 )가 보입니다.
argc의 개수가 2가 아니라면 if문 내부를 실행하는 것이죠.
if문 내부에 exit(0);가 보이는 것으로 보아 if문은 실행하면 안될 것 같습니다.

if문 이후를 보면 strcpy이 나오고, strcat2개가 나오고 system(cmd)가 보입니다.

strcpy의 형태는
char* strcpy(char * dest, cont shar * src)인데
이함수는 src의 문자열을 dest로 복사하는 것입니다.
즉, strcpy( cmd, "dig @" );는 "dig @"문자열을 cmd에 복사하는 것이 되겠습니다.

다음 나오는 strcat의 형태는
char * strcat( char * dest, const char * src)인데
이 함수는 src의 문자열을 dest에 담겨있는 문자열 뒤에 붙이는 것입니다.
즉 strcat( cmd, argv[1] ); 이므로 cmd는 "dig @argv[1]"라는 문자열이 담기게 되는것입니다.

다음 strcat( cmd, " version.bind chaos txt")까지 실행하게 되면,
cmd안에는 "dig @argv[1] version.bind chaos txt"라는 문자열이 담기게 되는것입니다.

그리고 그 다음 system( cmd ); 함수를 통해 cmd안에 담긴 문자열을 쉘에서 명령어로 입력하게 됩니다.
즉, $ dig @argv[1] version.bind chaos txt가 되는 것이죠.


여기서 잠깐 dig함수에 대해 알아보고 가면(사실 dig함수는 문제를 푸는데는 큰 의미는 없습니다만.. 일단 나왔으니...)

dig란 Domain Information Groper의 약자로 dns 진단용 유틸리티입니다.

dig 명령어는 기본적으로 다음과 같은 문법 형식을 갖고 있습니다.

$ dig @ [서버] [도메인] [쿼리형태]


쿼리형태

a : 네트워크 주소

any : 특정 도메인에 대한 모든 정보

mx : 도메인의 메일 교환

ns : 네임서버

soa : Zone파일 상단의 authority 레코드

hinfo : 호스트 정보

axfr : Zone 파일 교환(transfer)

txt : txt


$ dig를 실행하면 윗 사진과 같이 나오는데 각 필드의 뜻은 다음과 같습니다.

;; QUESTION SECTION:
- DNS 질의 내용을 표시

;; ANSWER SECTION:
- DNS 질의 사항에 대한 DNS 응답 RR(Resource Record)을 표시

;; AUTHORITY SECTION:
- DNS 응답 RR이 속한 도메인 존의 NS RR 정보

;; ADDITIONAL SECTION:
- 부가적인 RR 정보를 표시

;; Query time:
- DNS 질의 메시지 발송시점에서 응답시점까지의 소요시간

;; SERVER:
- DNS 응답 메시지를 보내온 질의응답 네임서버

;; WHEN:
- DNS 응답 메시지를 받은 시점의 date & time

;; MSG SIZE rcvd:
- 응답받은 DNS 메시지의 size표시 (byte단위)
IP TCP/UDP 헤더는 제외된 size

autodig를 통해 만들어진 명령어(dig @argv[1] servion.bind chaos txt]는 dns의 bind 버전을 알아내는데 사용되는 명령어입니다.


다시 문제풀이로 돌아가 소스의 추가 힌트 2개를 보겠습니다.
추가 힌트는
- 동시에 여러 명령어를 사용하려면?
- 문자열 형태로 명령어를 전달하려면?
입니다.

- 동시에 여러 명령어를 사용하려면?
동시에 여러 명령어를 사용하려면 명령어 사이에 ;를 넣으면 됩니다.
$ sh; ls이런식인것이죠.
이렇게 하면 sh실행이 종료된후 ls를 실행하게 됩니다.

- 문자열 형태로 명령어를 전달하려면?
문자열 형태로 명령어를 전달하려면 명령어를 큰따옴표("")로 묶어주면 됩니다.


그럼 autodig에 인자를 넘겨줘 봅시다.

$ autodig "168.126.63.1;my-pass"라 입력하겠습니다.


이렇게 하면 autodig 168.126.63.1이 실행된후 my-pass가 실행 되겠죠.

빙고 암호가 나왔습니다.

암호는 suck my brain입니다.


level3의 암호가 나올 거라 예상했는데 level4의 암호가 나온것으로 보아 autodig는 level4로 setUID가 되어 있나봅니다.
한번 확인해 보겠습니다.

확인 결과 예상대로 autodig는 level4에 setUID가 걸려 있음을 확인할 수 있었습니다.


Level1을 클리어 하고 얻은 password를 가지고 level2를 접속했더니 level1과 마찬가지로 hint가 보이는군요.
친절하게 hint를 제공해주셨기에 당연하게 열어보겠습니다.


"텍스트 파일 편집 중 쉘의 명령을 실행시킬 수 있다는데...."라는 힌트가 제공되는군요.


여기서 말하는 텍스트 파일 편집은 vi를 생각하시면 됩니다.
vi(vim)이란 리눅스에서 제일 기본이 되는 텍스트 편집기입니다. 윈도우의 메모장(notepad)와 같은 역할을 하신다고 보시면 됩니다.

GUI기반인 메모장과 달리 CLI기반인 vi는 크게 3가지의 모드가 있습니다.
1. 입력모드 : i, e, o, I, A, O를 누른후 텍스트를 입력할수 있는 상태
2. 명령모드 : esc키를 누른 상태
3. 콜론(ex)모드 : 명령모드 상태에서  :(콜론)을 입력한 상태

그리고 이러한 기본적인 모드들을 메모장의 기능과 비교해보면 다음과 같습니다.
1. 입력모드 : 타이핑을 하여 파일의 내용을 입력하는 모드
2. 명령모드 : 편집 메뉴에서 제공하는 복사, 붙이기, 삭제 등의 편집기능을 활용하는 모드
3. 콜론(ex)모드 : 다른 편집기의 파일 메뉴에서 수행하는 열기, 저장, 다른 이름으로 저장 등의 명령 수행
각 모드의 자세한 사용법은 vi 위키 참고(http://ko.wikipedia.org/wiki/Vi)


여기서 문제를 푸는데 핵심인 모드는 콜론(ex)모드입니다.
클론모드의 핵심중 하나는 :sh를 입력할 경우 쉘 창을 띄워 리눅스 명령어를 입력 할 수 있다는 것인데요.
이때 쉘의 권한은 vi를 실행할때 사용자 권한과 같이 갑니다.


그럼 한번 해보도록 하죠.
$ vi를 입력해 vi창을 띄운후 콜론모드에서 sh를 입력해 쉘을 띄우겠습니다.


아래를 보시면 쉘이 띄워짐을 알수 있습니다.


whoami 명령어를 통해 현재 사용자가 누구인지 한번 보도록 하겠습니다.


확인 결과 현재 사용자는 whoami로 나왔습니다. level2사용자로 로그인해 실행하였기 때문에 당연한 결과입니다. 
하지만 vi에 setUID가 설정되어 있었다면 setUID가 설정된 계정의 권한으로 실행이 되었겠죠.


따라서 level1의 기억을 되살려 lelve3에 setUID가 걸린것을 찾아보도록 합시다.
$ find / -perm -4000 -user level3 2> /dev/null 명령어를 입력해주면 됩니다.


/usr/bin/editor가 출력되었네요.

/usr/bin/editor을 실행해 보면 vi와 같은 창이 뜹니다.
당황하지 않고 콜론모드에서 sh를 입력해 쉘을 띄워준후 whoami로 사용자를 확인합니다.


빙고! whoami 결과 level3으로 뜨네요
level3의 권한을 획득하였습니다. 그럼 이제 my-pass로 level3의 비밀번호를 획득하면 됩니다.

level3의 암호를 얻었습니다.

암호는 can you fly?입니다.

해커스쿨의 안내에 따라 FTZ level1으로 접속하여 파일 목록을 출력한다.



친절하게 hint라는 파일이 보이므로 hint를 읽어 주도록 하자

level2 권한에 setUID가 걸린 파일을 찾으라고 한다.


여기서 setUID가 뭔지 한번 알아보고 넘어가자

유닉스 파일 시스템에서는 각각의 파일마다 권한이 설정되어 있다.
위에 public_html의 줄을 보면(어두워서 뭐가 public_html인지 모르려나.. 2번째) drwxr-xr-x라고 쓰여 있는것이 보일것이다.

이러한 식으로 되어있는것이 유닉스 파일시스템의 권한이다.

r = 읽기권한
w = 쓰기권한
x = 실행 권한

앞에서부터 첫번째 뒤로 3개 단위로 소유자, 소유자가 속한 그룹, 다른 사용자의 권한을 나타낸다. 첫번째 자리는 d가 있으면 디렉토리, -로 되어있으면 일반 파일이다.

비트로 표현하면 8비트로 r = 4, w = 2, x = 1로 각 숫자를 더해 표현하면 public_html의 권한은 755가 된다.

setUID는 조금 특이한 권한으로 rwsr-xr-x 이런식으로 표현하며, 비트로는 4000으로 나타낸다. 즉, 위 권한은 비트로 표현하면 4755가 된다. 

이밖에 비슷한 기능으로 setGID, Sticky-bit가 있고 2000, 1000으로 표현한다.

setUID는 소유자권한의 x에 s로, setGID는 그룹권한의 x에 s로, Sticky-bit는 다른사용자권한의 x에 t로 표현하게 된다.


setUID가 걸린 파일을 찾기 위해선

find명령어를 사용하면 되는데, -perm 옵션과 -user옵션을 이용하면 된다.
지금 찾고자 하는건 권한이 4000대이고, 사용자가 level2이므로
$ find / -user 사용자이름 -perm -4000이라고 입력하면 된다.


실행을 하고 나면 /bin/ExcuteMe가 보인다.

Permission denied라는 표준 에러가 보이는것이 귀찮다면
$ find / -perm -4000 -user level2뒤에 2> /dev/null을 붙여주면 된다.
2는 표준 에러를 뜻하며
2> /dev/null 은 표준 에러를 null로 리다이렉션 하라는 뜻이다.
즉, 표준에러를 출력하지 않겠다는 뜻이다.


명령어를 다시 입력하면,

훨씬 짧게 나왔다.


이제 /bin/ExcuteMe의 파일 권한을 확인해 보면,

권한이 -rwsr-x---, 소유자가 level2임을 알수 있고 즉 lelvel2에 setUID가 적용 되어있음을 알 수 있다.


사실 이 방법 말고도 tmp폴더안의 test.txt의 내용을 확인해보면 /bin/ExcuteMe가 적혀있다.


/bin/ExcuteMe파일을 찾았으니
$ /bin/ExcuteMe로 파일을 실행하면 다음 화면이 뜬다.



암호를 아는 명령어가 my-pass이지만 그것은 안된다고 하니 쉘을 실행해서 level2의 권한을 획득하자.
쉘의 종류는 sh, bash, ksh, csh 등이 있다.


쉘을 띄운다음 암호를 확인하는 my-pass 명령어를 쓰면 암호를 얻을 수 있다.


암호는 : hacker or cracker이다. 






+ Recent posts