티스토리 뷰

HackerSchool FTZ WARGAME Solution

 

Start : 15.07.03

 

LEVEL12

[그림 1]

 

hint 파일을 열어보면 [그림 1]과 같이 attackme 소스 코드가 출력이 된다. 이 프로그램의 특징은 gets 함수로 입력을 받아서 str 배열에 저장하는 방식으로 동작을 한다. level11의 경우 main 함수의 argv 로 값을 받아와서 strcpy 함수로 str 배열에 값을 저장했는데 level12는 그 데이터를 전달받는 방식이 다르다.

 

gets 함수의 경우 사용자가 입력하는데로 크기의 제한 없이 데이터를 변수에 저장시키기 때문에 버퍼 오버플로우 공격에 취약한 함수이다. 그럼 우리가 생각해 볼 수 있는 것은 입력을 변수 공간 만큼 한 다음 RET 값을 쉘 코드가 있는 곳의 주소로 덮어쓰면 될 것이라고 생각할 수 있다.

 

[그림 2]

 

우선은 [그림 2]와 같이 앞 문제와 마찬가지로 환경 변수에 쉘 코드를 저장을 한다.

[그림 3]

 

gdb를 이용해서 attackme 를 열어보면 [그림 3]과 같이 출력이 되고, 우리는 여기서 str 배열이 264byte 만큼 할당이 된 것을 알 수 있다.

 

이제 우리가 생각해야할 부분은 어떻게 프로그램에 데이터를 전달해서 오버플로우를 발생시킬 것이며, RET 값을 어떻게 쉘 코드 주소로 덮어쓰냐는 것이다. 왜냐하면 우리가 입력할 수 있는 것은 문자가 전부인데 주소값에 해당하는 값은 문자로 입력하라 수 있는 범위를 벗어날 수 있기 때문이다.

 

여기서 리눅스의 기능이 필요한데 우선 리다이렉션을 생각할 수 있다. 리눅스에서는 리다이렉션으로 출력하는 스트림을 변경하거나 입력 스트림을 변경할 수 있다. 사용 방법은 " $ ping 8.8.8.8 > ping.txt " " $ program < tests.inp " 와 같이 사용할 수 있다. '>' 의 경우 왼쪽의 출력을 오른쪽 파일로 리다이렉션 하는 특수 키이고, '<'는 오른쪽 파일 데이터를 왼쪽 프로그램의 입력값으로 리다이렉션하는 특수 키이다.

 

 

 

 

 

그럼 우리는 먼저 프로그램에 전달할 입력값이 저장된 파일을 먼저 생성한 다음, 프로그램에 '<' 리다이렉션으로 입력값을 전달하면 된다고 생각할 수 있다.

 

[그림 4]

 

텍스트 편집기로 [그림 4]와 같이 입력한 뒤 파일이름.pl 로 저장한다. 첫번 째 줄이 의미하는 것은 perl로 아래 명령어들을 실행 시키겠다는 의미이며 두번 째 줄 부터는 실행시킬 명령어이다. 이런 파일을 perl 스크립트 파일이라고 한다. 코드는 "A"를 268번 출력하고, 이어서 \xb9\xfe\xff\xbf 를 출력하는 코드이다. 맨 끝 4바이트는 방금export 했던 쉘 코드의 주소값이다.

 

[그림 5]

 

이어서 저장한 파일에 실행 권한을 부여한 뒤, 실행을 시키되 파일로 출력이 되도록 리다이렉션을 해줘야 한다.

 

[그림 6]

 

이어서 [그림 6]과 같이 attackme 에 리다이렉션으로 attack.txt 를 입력값으로 전달해줬으나 level13의 sh 이 실행되지 않았다. 그 이유를 알아보기 위해서 여러 곳에 검색도 해보고 질문도 해보고 했는데 정확하게 동작이 되는 방식이나 순서는 모르겠지만 찾아 본 것 중 가장 근접한 답변이 EOF 때문이었다. gets 함수가 스트림을 통해서 입력값을 전달 받아서 오버플로우가 발생되고 쉘이 실행되었을 때 계속 사용자의 입력을 기다려야하는데 attack.txt 의 마지막에 있는 EOF 때문에 쉘이 실행되자마자 바로 종료가 되버린다는 것이었다. 사실 이 내용을 완벽하게 이해한건 아니고 추상적으로 이해한 내용이라 완벽하지가 않다. 앞으로 조금 더 이 부분에 대해서 연구를 한 뒤에 내용 추가가 필요하다.

 

이 문제를 해결하기 위해서 찾아본 바로는 값을 전달할 때 계속 입력을 대기할 수 있도록 cat 을 같이 프로그램에 전달해주면 cat 때문에 계속 입력을 대기하고 있는다고 한다.

 

[그림 7]

 

cat을 붙여서 전달하기 위해서는 리다이렉션은 부적합하다. 왜냐하면 리다이렉션으로 입력값을 전달하는 것은 파일만 가능하기 때문이다. 그렇기 때문에 다른 방법인 파이프(|)를 쓸 수 있다. 파이프는 파이프를 기준으로 왼쪽 출력 값을 오른쪽 프로그램으로 전달하는 기능을 한다.

 

그래서 attack.txt 내용을 attackme 로 전달하기 위해서는

cat attack.txt | ./attackme 처럼 사용하면 되는데 이렇게만 하면 또 마찬가지로 EOF 가 전달되기 때문에 쉘이 바로 종료가 된다. 따라서 끝에 ;cat 을 넣어주면 되는데 ';'이 의미하는 것은 앞 명령어가 실행되고 바로 이어서 명령어를 실행하는 기능이 있다. 이 때 역시 그냥 ;cat 을 cat attack.txt 끝에 넣어주게 되면 cat attack.txt와 cat | ./attackme 로 따로 실행이 되기 때문에 우리가 원하는데로 실행이 안된다. 그렇기 때문에 이 두 가지 실행을 하나의 단위로 실행하기 위해서는 이를 괄호로 묶어주면 된다.

 

결론적으로 (cat attack.txt ;cat) | ./attackme 와 같이 명령어를 입력을 하게되면 attack.txt 가 출력되고 cat의 입력 대기 라인이 같이 attackme 에 전달되고, 버퍼 오버플로우가 발생하면서 쉘이 실행된다. 그리고 방금 처럼 바로 쉘이 종료되는게 아니라 사용자 입력을 대기하게 되고 이 때 우리가 값을 전달하면 쉘에서 그대로 실행이 된다.

신고
크리에이티브 커먼즈 라이선스
Creative Commons License
댓글
댓글쓰기 폼