기술문서 09. 11. 3. 작성 Format String Bug 에서 dtors 우회 작성자 : 영남대학교 @Xpert 박병규 preex@ynu.ac.kr 1. 요약... 2 2. d to r 이란... 3 3. 포맷스트링... 4 4. ro o t 권한획득... 7 5. 참고자료... 1 0-1 -
1. 요약 포맷스트링버그 (Format String bug) 는 1999 11월에취약점이최초로발견되었고많은기법들이개발되어왔다. 그중에쓰기가능한메모리에기록되어지는 dtors를이용한포맷스트링버그를알아볼것이다. 2. dtor 이란? GNU C 컴파일러로컴파일된이진프로그램에는소멸자 (Destructor) 와생성자 (Constructor) 를위한특수테이블섹션인.dtors 와.ctors가각각생성된다. 생성자함수와소멸자함수를지정하면 main이실행될때생성자함수가실행되고 main이끝날때소멸자함수가실행되는데이중에서우리가관심있게봐야할것이소멸자함수와.dtors 테이블섹션이다. #include <stdio.h> static void des(void) attribute ((destructor)); void main(){ printf("main is now n n"); exit(0); } void des(void){ printf("destructor is now n n"); } 위실행결과처럼 main이종료될때자동으로함수가실행되는기능은.dtors테이블섹션에담당한다. 이섹션은 32비트주소의배열로이루어져있고배열은항상머리인 0xffffffff로시작해서꼬리인 0x00000000 로끝나는형식을가진다. - 2 -
컴파일된프로그램내부에있는변수나함수를추출하는 nm명령어로 dtor_sample를보면아래와같다. dtors 섹션이 08049574 에서시작하고 0804957c 에서끝난다는사실을알수있다. des 함수 gdb 로확인해보면 dtors 섹션이 0xfffffff 로시작하고 0x00000000 로끝나며 - 3 -
dtor_sample 에서소멸자로지정한 des 함수의주소가오는걸알수있다. 3. Format String 포맷스트링은 printf함수와같은포맷함수안에서함수인자의출력형태를결정짓는일을한다. int a=3; printf("a:%d",a); 위코드에서포맷스트링은 %d이고역할은 int형변수를 10진수상수형태로출력하라는것이고 printf함수는이런포맷스트링을만날때마다이에해당되는인자값 (a) 을찾아서출력하는포맷함수의기본적인형식이다. %d외에도많은포맷스트링이있는데 printf함수나 scanf함수를쓸때많이써왔던포맷스트링도있고아닌것도있을것이다. 우리는그중에서 16진수로출력해주는 %x와쓰인총바이트수를출력하는 %n에대해서알아볼것이다. - 4 -
위의코드를보면 Foramt String 라는문자열을출력하는것같지만 %n 을만나서앞에서출력된문자의수를저장도하는 printf문이다. I에저장된문자수를출력하고종료되는함수이고단순히 printf에익숙해진사람이라면취약점이될만한것을찾을수없을정도로평범한 printf문이다. #include <stdlib.h> int main(int argc, char *argv[]){ char text[1024]; if(argc<2){ printf("<text to pintf"); exit(0); } strcpy(text,argv[1]); printf("good: n"); printf("%s n",text); printf(" nbad: n"); printf(text); printf(" n n"); } 위의코드는입력파라메타를 2 가지방법으로출력해주는프로그램이다. good 방법은 %s 포맷스트링을이용해서출력하는방식이고 bad 방법은 printf(text) 로출력하는방식이고단순히문자열을출력하기위한것이라면 bad 방법으로출력하는프로그래머도있을것이다. 위에예에서입력파라메타로 test 를입력했고출력은둘다입력값이출력되었다. 입력값에포맷스트링 %x 를포함하는 test%x%x%x%x 를입력해보 - 5 -
았다. good 방법은 %x 을문자열로보고그대로출력했지만 bad 방식은 %x 대신알수없는숫자들이출력되는것을볼수있다. %x 를만날때마다포맷스트링으로인식하고이에해당되는인자를찾지만없기때문에이인자가있어야할메모리의값들을출력되는것이다. 위의예제를보면 AAAA xfc x97 x04 x08 이란문자열과 %%8x 를점점늘려가면서출력하는프로그램예제인데 %%8x 를 4 개째썼을때입력문자열인 A 의아스키코드값인 41 이읽어져나온것을볼수있다. 이제 %n 을이용해서값을써볼것이다. %%8x 대신 %%100c%%n 을입력했다. 10 진수 100 을넣었더니 0080497fc 주소에 16 진수 84 가저장되었다. 왜냐하면 %n 앞의출력된것들도포함되어서값이써지기때문이다. 이값들을잘조절해서넣 - 6 -
으면메모리에원하는값을넣을수있다. 4. root 권한획득 %n 포맷스트링으로값을쓰고 %x 포맷스트링으로취약함수를이용하여메모리값을읽어오는방법을알아보았다. 이제 dtors 의메모리값을조작해서 root 권한을획득해볼것이다. #include <stdio.h> #include "dumpcode.h" int main(){ char buf[50]; fgets(buf,sizeof(buf),stdin); printf(buf); printf(" n"); dumpcode((char*)0x80497fc,4); printf(" n"); } 위의코드를실행하면입력상태가되고문자열을입력하면취약하게출력해주는프로그램이다. dumpcode 는 0x80497fc 주소부터값 4 개를출력해주는함수이다. 물론 vul1 에는 setruid 가설정되어있어야한다. 위의실행결과를보면 3 장에서보았던포맷스트링에취약한프로그램이고 dumpcode 함수에의해서 0x8049f7c 주소의값이출력되었다. - 7 -
환경변수에쉘코드를등록하는 eggshell 을실행시키고 dtors 의주소를 objdump 를이용해서알아보았다. printf 다음의 "" 사이에문자열을출력하는예제인데문자열이저장되고프로그램안의 printf 함수에서입력값을화면에출력하는데 %x 를만나서메모리의값을출력한다. 여기서중요한점은 %x 를 4 개를사용했을때우리가입력한 41 값이나왔다는점이다. 41 다음에나오는 - 8 -
dtors 주소에우리가원하는값을쓸수있다는것이다. 쉘코드주소의하위 2byte 를 dtors 에넣을것이다. fab8 의 10 진수값인 64184 를 dtors 주소에써야한다. 64144 앞에출력된 40byte 에다 64144 를더한값이 %n 를만나서 080497fc 에쓰여진다. 이번에는상위 2 바이트를넣어볼것이다. 위와똑같은방법으로 bfff 를넣어야하는데 bfff 의값이 10 진수로 49151 이다. 하지만 fab8 을쓰는데 64184 만큼썼기때문에이값보다커야한다. 그래서 bfff 대신 1bfff 로대체해서계산하기로한다. 1bfff 의 10 진수값은 114687 이고두번째 %n 까지 114687 만큼출력하게하려면 50503 만큼두번째 %n 앞에써야한다. 실행하면 080497fc 주소에쉘코드주소값 bffffab8 을썼고 vul1 프로그램이종료되고 dtors 가실행되면서쉘코드가실행되면서 root 권한을획득하게된다. - 9 -
5. 참고자료 [1] dtors 를이용한 Format string 자동화툴설계 - amadoh4ck [2] dtors 를이용한 format string attack -hardsoju [3] 해킹공격의예술 -Jon Erickson - 10 -