소스를 살펴보면 이전 포스팅과 비교했을때 flag 와 password_b 의 변수위치만 바뀌엇다
소스코드를 살펴보자.
이전 포스팅과 비교해서 그저 변수만 바뀌었을 뿐인데 메모리에서 어떤 일이 생기는지 확인해보자.
break부분은 전번 포스팅과 같은 부분이다.
확인해 보면 flag가 password_b 앞에 있음을 알 수 있다. 이것은 password_b 오버플로우로 인해 flag가 절대로 덮어써질수 없다는 것을 의미한다. 밑의 그림을 확인해보자.
~> 그러면 우리는 전번 소스와 같은 방법으로 오버플로우를 일으킬 수 없다는 것을 의미한다. 하지만 이번 포스팅에서 이제 다른 방법을 제시할 것이다.
위에서 말한대로 flag가 password_b 앞에 있으므로 오버플로우로 auth_flag 변수가 훼손되지 않았다.
하지만 C코드에서는 볼 수 없는 다른 실행 제어 포인트가 존재한다. 실행 제어 포인트는 바로 모든 스택 변수들 다음에 위치하고 있다. 그래서 쉽게 덮어 씌어질 수 있는데 이 메모리는 모든 프로그램의 실행에 반드시 필요하고, 그래서 모든 프로그램에 존재한다. ~>이 부분이 덮어씌워지면 프로그램 충돌이 발생한다(segmentation fault)
다른 포스팅에서 살펴봣는데, 스택은 프로그램에서 사용되는 다섯 개의 메모리 세그먼트 중 하나이다.
~> 이전 포스팅에서 살펴봣듯이, 함수가 호출되면 스택프레임이라는 구조가 스택에 푸시되고, EIP 레지스터가 함수의 첫 번째 명령으로 점프한다. 각 스택 프레임은 그 함수의 지역 변수와 리턴 주소를 갖고 있다. 그래서 나중에 EIP가 복구될 수 있다(이전 포스팅에서 생각해 보라던 부분이다). 함수가 끝나고 나면 스택프레임은 스택에서 팝되고 리턴주소를 이용해 EIP를 복구한다. 이런 모든과정은 보통 컴파일러가 처리한다.
check_authentication() 가 호출되면 새 스택 프레임이 스택의 main() 스택프레임 아래로 푸시된다. 이 스택 프레임 안에 지역 변수, 리턴 주소, 함수 인자가 있다.
<윗주소>
main() 함수의 스택프레임 |
*password(함수 인자) |
리턴 주소(ret) |
저장된 프레임 포인터(SFP) |
password_b 변수 |
return_value 변수 |
<아랫주소>
요소들을 디버거로 살펴보자
왜 브레이크포인트 순서를 이렇게 정햇는지는 소스의 진행을 보면 이해가 갈것이다
이제 진행하면서 메모리들을 확인해보자
첫번째 break는 main()에서 check_authentication()을 호출하기 바로 전이다. 이 시점에서 esp는 0xbffffa10이고 스택의 맨위를 가리킨다.
다음 break까지 계속해 check_authentication()안으로 들어가면, ESP가 이제는 스택에 푸시된 check_authentication()의 스택프레임을 위한 메모리 공간만큼 작아진다. flag와 password_b를 살펴보면 그 변수들이 스택 프레임 안에 위치하는것을 확인 할 수 있다.
마지막 break까지 계속하면 함수가 호출될 때 스택프레임이 스택에 푸시된다. 스택은 낮은 메모리 주소방향으로 커지니까 이제 스택포인터는 0xbffff9d0으로 64바이트 만큼 작아진다.
~>c.f) 스택의 몇개의 값들은 단지 컴파일러가 붙인것이고,일부는 '저장된 프레임 포인터'라 불린다. 또한, 컴파일 할때 최적화를 위해 -fmoit-frame-pointer 플래그를 사용했다면, 프레임 포인터가 스택프레임에서 사용되지 않는다.
여기서 0x080484bb는 ret 주소이고 0xbffff9b7은 30개의 A를 포함한 문자열을 가리킨다(왜 해당값이 이런의미가 있는지 생각해보자. 이번 포스팅에서의 과제이다. 마찬가지로 계속 생각해봐도 이해가 되지 않는다면 비밀댓글이나 비밀방명록으로 물어봐 주길 바란다.! 힌트를 주자면 스택 프레임의 리턴 주소 위치는 스택 프레임이 어떻게 생성됬는지 이해하면 알 수 있다. 이 과정은 함수 호출 전의 main()함수에서 시작된다.
check_authentication() 함수로 넘어와서 계속 진행된다. 첫번째 명령은 스택 프레임을 위한 메모리 저장을 끝마친다. 이 과정은 다른 포스팅에서 봣듯이 '함수 프롤로그' 과정이다.
다음 두번째 명령은 저장된 프레임 포인터를 위한것이고, 그다음 명령은 ESP에서 0x38을 뺴는 명령이다. 그래서 함수의 지역변수를 위해 56바이트가 저장된다. 또한, 리턴 주소와 저장된 프레임 포인터는 이미 스택에 푸시되어 있고, 64바이트 스택프레임의 추가 8바이트가 바로 SFP 와 RET 이다.
함수가 끝나면 leave와 ret 명령은 스택 프레임을 지우고 EIP를 스택 프레임에 저장된 리턴 주소로 설정한다. 이 과정은 함수 호 출 때마다 발생한다.
여기서 리턴주소의 몇바이트가 A의 값(\x41)로 변조되었다. 하지만 저장된 리턴주소의 몇바이트가 덮어써져도 프로그램은 여전히 그 값을 이용해 EIP를 복수 하려한다. 실행이 임의의 장소로 점프해버리면 충돌이 발생한다.
하지만 덮어씌워진 리턴주소가 임의의 값일 필요는 없고 덮어쓰기를 제어할 수 있다면 실행을 특정장소로 점프시킬 수 있을 것이다. 이 부분을 이용하면 되는 것이다!
다음 포스팅에서는 점프될 장소를 정하는 방법에대해서 알아볼 것이고, 이어서는 버퍼오버플로우로 결과적으로 쉘코드를 실행시키는 방법에 대해 알아볼 것이다.
'과거의 컴퓨터 공부 > attack-1.BOF' 카테고리의 다른 글
BOF-(1)간단한 소스로 BOF 맛보기 (0) | 2014.03.17 |
---|