본문 바로가기

List/Linux

Boa webserver fuzzing with AFL

AFL을 써보고 싶어서 link를 참고하여 임베디드 기기에서 많이 사용하는 boa 웹 서버를 대상으로 돌려봤다.


전체적인 과정은 ubuntu 14.04(64bit) 환경에서 진행하였고, 준비물은 boa, AFL, preeny 이며 설치 링크와 사용 방법은 아래 글에 있다.


먼저 boa 웹서버를 설치하기 위해서는 $ apt-get install boa 명령을 사용할 수 있지만 퍼징을 위해서는 소스의 수정이 필요하여 직접 소스코드를 받아야 한다. (download : link)

소스는 작업하고자 하는 경로에 다운받은 뒤 $ tar -xvzf boa* 명령으로 압축을 해제한다. 구성 요소를 보게되면 여러가지 폴더 및 파일이 있는데, 건드려야 할 것은 src 폴더와 boa.conf 파일이다. 나는 /home 경로에서 압축을 해제했다. (/home/boa-0.94.13)


더 진행하기에 앞서 AFL 퍼저는 stdin, stdout으로 입/출력을 하게되는데 boa 웹서버는 서버와 클라이언트 간 소켓통신을 하게끔 되어 있다. 따라서 boa 웹 서버에 퍼징을 하기 위해서는 입/출력 방식을 소켓 통신에서 stdin, stdout으로 바꿔주어야 한다. 




본론에 들어가자면 일단 stdin, stdout으로 수정하는 작업과는 별개로 boa 웹 서버 실행 시 영향을 미치는 웹 서버 자체 설정에 관련된 수정을 해줘야한다.


첫째로, src/defines.h 파일의 30번째 라인에 보면 SERVER_ROOT 경로를 지정해주는 부분이 있다. 해당 경로에는 boa.conf가 존재해야 하는데, 실제 서버를 운영하는 것이 아니고 퍼징하는 것이기 때문에 현재 boa.conf 파일이 존재하는 "/home/boa-0.94.13" 로 수정한다. 



둘째로, boa.conf 파일의 각각 62번, 74번 라인에서 ErrorLog와 AccessLog의 경로를 지정해주는 부분이 있다. 수정하지 않았다면 해당 경로는 /var/log/error_log와 같이 설정되어 있다. 나의 경우에는 따로 폴더를 만들어주기 귀찮으므로 /home/boa-0.94.13/error_log, /home/boa-0.94.13/access_log로 수정했다.


 셋째로, boa.conf 파일의 111번 라인에 있는 DocumentRoot를 수정해야 한다. 해당 경로는 /var/www로 되어 있는데, HTML 문서의 루트 폴더 경로를 나타낸다. 나는 /var/www에 만들기 싫기때문에 /home/boa-0.94.13으로 수정했다. 추가적으로 다운로드 받은 boa 압축파일에서 index.html 파일을 제공하지 않기 때문에 /home/boa-0.94.13/index.html 파일을 적절하게 생성한다.


넷째로, 이번에 설명하는 수정사항은 빌드에 관련된 사항이다. 다운로드 받자마자 src 폴더에서 ./configure ; make를 하게 되면 src/compat.h 파일의 120번 라인에서 에러가 나게 된다. 딱히 원인 분석은 하지 않았고, 스택오버플로우 검색을 통해서 해당 라인의 foo##->tm_gmtoff를 (foo)->tm_gmtoff로 수정했다.


지금까지 수정했던 사항들은 boa 웹 서버의 빌드 및 실행에 문제가 없도록 하기 위함이다.




다음으로 boa 웹 서버에 퍼징을 돌리기 위해서 기존의 웹 소켓 통신 방식을 stdin, stdout 방식으로 변환해야한다. 


첫째로, src/boa.c 파일의 66~76 라인에 주석처리를 한다. 해당 라인은 dup2() 함수를 통해서 STDIN, STDOUT이 /dev/null을 가리키게 변환하는 소스이다. 



둘째로, src/boa.c 파일의 139 라인에 존재하는 fork에 관련된 부분을 주석처리 한다. 해당 분기문은 fork()를 통해서 부모 프로세스는 죽이고, 자식 프로세스가 동작하여 백그라운드에서 실행되게끔 만들어 준다. 어차피 boa 웹 서버는 멀티플렉싱 방식의 통신을 사용하기 때문에 해당 분기문을 전부 지우지 않고 자식 프로세스인 경우 exit(0); 으로 죽이고 부모 프로세스가 동작하게 수정해도 상관은 없다. 하지만 이럴 바엔 차라리 코드를 지워서 하나의 프로세스가 동작하게 하는 것이 부모 프로세스만 동작하도록 하는 것과 같으므로 퍼징의 효율을 위해서 주석처리를 해버렸다. 



추가적으로 boa 웹서버가 사용하는 멀티플렉싱 방식에 대해 간단히 설명하면, 일반적인 웹 서버는 fork() 함수를 사용한 멀티 프로세스 방식을 사용하여 클라이언트의 요청이 들어왔을 때 부모 프로세스에 해당하는 웹 서버는 자식 프로세스를 하나 생성하여 자식 프로세스가 클라이언트와 통신을 하게 된다. 반면에, boa 웹 서버의 경우에는 select() 함수를 사용한 멀티플렉싱 방식으로 클라이언트와 통신하는데 하나의 웹 서버 프로세스가 다수의 클라이언트를 대상으로 여러 개의 소켓을 관리하며 통신하는 방식이다. 더욱 자세한 정보는 I/O multiplexing vs Multiprocess 와 같은 키워드로 검색할 수 있다.


셋째로, boa 웹 서버는 클라이언트의 요청을 계속 기다리다가 응답을 주는 방식으로 무한 루프를 돌고 있어 종료하는 부분이 없다. 따라서 퍼징을 위해서는 적절한 지점에 바이너리 종료를 위한 exit(0); 코드를 삽입해줘야 한다. 일단 무한 루프를 돌고 있는 부분은 src/select.c 파일의 select_loop() 함수이다. 해당 함수가 무한 루프로 계속 돌아가며 78번 라인에 있는 select() 함수에서 멀티플렉싱 처리를 한다. 하지만 select_loop() 함수 내에서는 딱히 exit(0); 코드를 삽입할 부분이 보이지 않았고 클라이언트의 요청을 처리하는 68번 라인의 process_requests() 함수를 분석했다. 



process_requests() 함수의 코드는 src/request.c에 존재하고 해당 파일의 481번 라인에는 클라이언트의 요청에 대해 처리를 완료하는 부분이 존재한다. 웹 서버는 크래시가 아닌 경우 무조건 응답을 주기 때문에 해당 case 0:의 마지막 부분에 exit(0); 코드를 삽입했다. 물론 다른 위치에 exit(0); 코드를 삽입해도 되지만, boa 웹 서버에 AFL을 한 번 돌려보는 취지이므로 가장 무난하다는 곳에 코드를 삽입하였다.



 이로써 boa 웹 서버에 퍼징을 돌리기위한 수정 사항은 모두 끝났다. 




이제는 boa 웹 서버를 빌드하고 AFL 퍼저로 퍼징을 수행하는 단계이다. 설명에 앞서 두 가지를 설치해야 한다. 


1. AFL : Fuzzing 도구

2. Preeny : fork(), socket(), alarm() 함수 등을 후킹을 통해서 비활성화 해주는 오픈 소스 도구


AFL은 link의 'Latest source tarball for the tool'을 클릭하여 다운받을 수 있으며, preeny는 link를 다운받고 src 폴더에서 $ make 명령을 통해 설치를 완료할 수 있다. 나는  마찬가지로 /home 폴더 하위에 preeny를 설치하였고, src 폴더에서 make 명령을 통해 빌드하면 여러 라이브러리들이 생성된 것을 볼 수 있다. 



이 중에서 사용할 것은 desock.so 파일이다. 해당 라이브러리가 비로소 boa 웹 서버를 stdin/stdout 방식으로 변경해준다. preeny는 LD_PRELOAD를 사용하여 후킹하는 방식으로 사용하는데, 추후 퍼징 수행 시 커맨드가 길어지므로 다음 명령어를 통해서 환경변수에 등록한다. 


$ export LD_PRELOAD="/home/preeny/src/desock.so"


이제 마지막으로 AFL을 실행시킬 차례이다. 설치는 설치 메뉴얼을 통해 잘 했을 것으로 생각하고 boa 웹 서버를 위한 testcase를 만들어 줘야 한다. AFL에서는 자체적으로 testcases 폴더에 기본적인 testcase를 두고 있는데, 나 또한 해당 경로에 testcase를 만들었다. /home/afl-2.39b/testcases/others/html 폴더를 만들어주고 웹 서버에서 사용하는 Request Method에 대한 예제를 하나씩 생성했다. 



이 부분은 testcase이기 때문에 자신이 퍼저의 입력값으로 사용되었으면 하는 데이터를 넣으면 된다. 나의 경우엔 get 파일에 다음과 같이 내용을 입력했다.



testcase 생성을 모두 마쳤다면 boa를 afl-gcc를 통해서 빌드 할 차례이다. 이를 위해서는 /home/boa-0.94.13/src 폴더의 Makefile을 수정해야 한다. 간단하게 해당 Makefile 파일의 31, 32번 라인을 afl-gcc, afl-g++로 변경해주면 된다. 



이후, /home/boa-0.94.13/src 경로에서 $ ./configure ; make 명령을 통해 boa 웹 서버를 빌드 한다. 이를 통해 boa 바이너리가 생성되고 정말 마지막으로 AFL을 실행하는 단계만 남았다. 다음 명령을 통해서 AFL을 통해 boa 웹 서버를 퍼징할 수 있다.

$ /home/afl-2.39b/afl-fuzz -i /home/afl-2.39b/testcases/others/html -o /home/afl-2.39b/boa_fuzzing_result /home/boa-0.94.13/src/boa


방금 명령에서 사용한 afl-fuzz의 i 옵션은 testcase가 존재하는 폴더를 의미하고, -o 옵션은 퍼징의 결과물들이 생성되는 폴더를 의미한다. 




사용 후기 


이로써 꽤 오랫동안 boa 웹 서버에 대해 퍼징을 수행하고 있는데 다중 분기문에 걸려버렸는지 커버리지가 어느 단계 이상 증가하지 않는 것 같습니다. ㅠㅠ 

오히려 exit(0); 코드를 삽입하기 위해 소스코드를 분석하던 중 간단한 로컬 버그를 하나 발견해 메일을 보내놓은 상태입니다... 버그 찾자고 퍼징 돌렸더니만.....

아무쪼록 전체적으로 어려운 내용은 아니지만 이번 주말 간 삽질했던 내용을 그냥 묵히기 아까워 공유하고자 글을 작성했습니다. 이 글로 처음 하시는 분들은 저보다 수월하게 했으면 좋겠네요. 



'List > Linux' 카테고리의 다른 글

vsftpd root접속  (0) 2017.05.10
androguard 설치  (0) 2017.05.01
AFL (American Fuzzy Lop) 설명 및 사용법  (0) 2017.04.01
docker.io  (0) 2017.03.28
Packet Capture Setup  (0) 2017.03.16