프로그래밍/C

pwnable vulnerable functions

2018. 2. 17. 20:49

포너블에서는 해당 프로그램이 어떤 취약점을 갖고 있는지 빠르게 확인하는 것이 중요하다. 그것이 해당 문제를 얼마나 빨리 푸냐에 직결되기도 하니까. 그래서 C언어에서 공격에 취약한 함수들을 정리해보려고 한다. 


gets()

 gets 함수는 가장 대표적으로 가장 취약하다고 판단되는 함수이다. 해당 함수가 취약한 이유는 값을 얼만큼 받을지 정할 수 있는 장치가 아무것도 없기 때문이다. 만약 char buffer[40]인데 gets(buffer)로 받는 값을 크기가 100bytes 면 다른 변수들을 overwrite 할 수 있기 때문에 취약하다. 

strcpy() 

 strcpy는 한 버퍼에 있는 값을 다른 버퍼로 보내는 함수이다. 이 함수도 말할 것도 없이 취약하다. 길이 제한을 할 수 없으니, 당연히 위험하다. Lord of BOF의 경우엔 strcpy() 함수를 사용하여 공격하는 문제들이 많다. 

scanf() 

 scanf() 함수는 안전하게 쓴다면 굉장히 안전한 함수라고 볼 수 있겠으나, 보통은 취약한 함수이다. C언어를 처음 접해보는 사람들은 보통 C언어 기본서를 보면서 공부를 하는데, 보통 C언어 기본서에서 사용하는 소스 중 사용자에게서 입력을 받는 기능을 갖고 함수들은 scanf를 많이 쓰고, 그에 맞는 서식문자로는 %s를 많이 쓴다. 

 %s로 받는 것은 아무런 길이를 지정하지 않기 때문에 취약점을 갖는다. gets 함수와  비슷.

 scanf("%40s", buffer); 이런 식으로 길이를 지정해서 받을 수 있는데, 여기서 조금의 문제점이 존재한다. 다음코드가 있다고 가정해보자. 


해당 소스에서는 name 을 scanf로 받을 때 약간 컨트롤을 할 수 있다. 



 이런식으로 scanf는 맨마지막에 NULL 값을 추가하기 때문에 %40s로 값을 받았어도, 결과적으로는 41바이트가 스택에 들어가기 때문에 약간의 Overflow를 발생시킬 수 있다. 



read()

 read 함수는 실행할 때, 얼만큼 값을 받을지 인자로 값을 받고 난 후, 실행하기 때문에 사실상 RET 주소를 덮거나, 다른 변수들을 덮는 것은 불가능하다. (물론, 해당 인자를 완벽하게 주었을 경우) 하지만, scanf()와는 다르게 뒤에 NULL 이 붙지 않기 때문에, Leak이 발생할 수 있다. 예제 코드를 보자.


 프로그래머의 생각이라면, 사용자에게서 이름을 받은 후에 간단하게 이름을 출력하는 기능이었을테다. 하지만, read 함수의 결함(?)으로 조금 극적인 상황을 이끌어낼 수 있다. secret 이라는 값을 읽어보도록 하겠다. 



 read() 함수는 값을 받고 뒤에 NULL을 넣지 않고, printf의 %s 서식 문자는 NULL 바이트를 만날 때까지 스택에 있는 값을 출력해준다. 그래서 스택에 저장되어 있던 secret이 노출된 것이다. 


 그래서 보통 read(0, buffer, strlen(buffer) - 1) 이런 식으로 코드를 작성한 뒤 실행하는 것이 좋다. 


 

fgets()

 fgets는 read와 scanf의 특성이 공존한다. fgets가 받는 인수중 length를 지정하는 인수가 있고, buffer를 저장할 때 length - 1 만큼만 스택에 저장하고 맨 마지막 바이트는 NULL로 채운다. 그래서 길이 지정만 잘 해준다면, 가장 완벽한 함수(?)라고 말할 수 있을 듯하다.