[Security] Buffer Overflow Attack (2)

가영·2021년 6월 24일
1
post-thumbnail

먼저, 이전 글에서 다루지 못했던 것이 두 가지였다.

  1. 왜 두번째 실행에서 tag 문자열이 다른 값으로 바뀌었는지
  2. segmentation fault로 종료되는지

일단 기억을 상기하기 위해 코드 및 실행결과와 그림을 다시 불러오자!

프로그램 코드

void hello(char *tag)
{
  char inp[16];
  
  printf("Enter value for %s: ", tag); gets(inp);
  printf("Hello your %s is %s\n", tag, inp);
}
  • 메인에서는 hello()한 번만 호출한다
  • hello()의 virtual address는 008048394이다.

실행결과

$ perl - e 'print pack("H*", "414243444546474851525354555657586162636465666768e8ffffbf948304080a4e4e4e4e0a"); | ./buffer2
Enter value for name:
Hello your Re?pyy]uEA is ABCDEFGHQRSTUVWXabcdefguyu
Enter value for Kyyu:
Hello your Kyyu is NNNN
Segmentation fault (core dumped)

펄스크립트로 hex 값을 유니코드 문자로 변환해서 buffer2의 입력으로 넣어줬는데, 이 때 스택에 어떻게 들어가는지 자세히 살펴보자.

414243444546474851525354555657586162636465666768e8ffffbf948304080a4e4e4e4e0a

41 42 ... 68까지는 아스키코드표에서 확인할 수 있듯, 알파벳으로 변환이 됐었다. 저번 글에서 다루었듯, 0a가 엔터로 들어가서 gets()의 입력으로 들어가고, 그 바로 뒤에 있는 tag포인터의 1byte가 우리 입력의 끝, NULL로 값이 바뀌어서 tag가 가리키는 주소에 있는 문자열 값이 Re?pyy]uEA, 쓰레기 값이 되었었다.

그림을 보면 이해가 쉬웠었다😀


그렇다면 tag 포인터가 가리키는 문자열이 Kyyu로 한 번 더 바뀐 이유는 뭘까?

그걸 알기 위해서는 c언어의 함수 호출과정을 이해해야한다.

stack frame의 구조는 다음과 같다.

유튜브에서 stack frame이 쌓이고 사라지는 과정에 대해 친절하게 설명해주는 동영상을 찾을 수 있었다! 여기
그림을 설명해보자!!

stack frame은 함수가 호출될 때마다 함수당 한 프레임씩 쌓인다. 이 때 함수가 모든 명령을 수행하고 return 된 후에는 이전의 프로시저(함수)가 스택에 남아있는 이전 함수의 아이템들을 스택에서 pop시킨다.🤩

현재 수행중인 함수를 P, 호출할 함수를 Q라고 하면(그림처럼) P가 Q를 호출하면

  1. Q의 파라미터가 P의 프레임에 쌓인다.
  2. Q가 종료되면 return 할 주소(return address of P)를 쌓는다. (여기서부터 Q의 frame이다)
  3. Q의 local variable을 쌓는다.

Q가 종료되면(returning control)

  1. Q의 local variable들을 모두 pop시킨다.
  2. return address로 control을 넘긴다
  3. Q를 호출하기 위해 필요했던 파라미터들도 전부 pop시킨다.

실행결과와 스택 그림을 다시 보면서 해보자~

$ perl - e 'print pack("H*", "414243444546474851525354555657586162636465666768e8ffffbf948304080a4e4e4e4e0a"); | ./buffer2
Enter value for name:
Hello your Re?pyy]uEA is ABCDEFGHQRSTUVWXabcdefguyu
Enter value for Kyyu:
Hello your Kyyu is NNNN
Segmentation fault (core dumped)

hello()가 처음 실행되고 나서 return하기 전에, hello()의 local variable 이나 old base ptr은 모두 스택에서 빠져나온다는 걸 잊으면 안된다. 특히 old base ptr는 그냥 아무 접근 가능한 주소값으로 채워넣을 수밖에 없기 때문에 이후에 control을 가지게 되는 프레임(? 혹은 프로시저)가 이상한 위치가 되어버린다.

return addr가 hello()의 시작 주소였기 때문에 hello()함수는 한번 더 실행이 되지만, 스택 프레임 자체가 원래 있지도 않았고, 지금은 엉뚱한 공간(e8ffffbf)을 가리키기 때문에 함수에서 사용되는 매개변수나, 지역변수가 이상한 값을 보일 수 밖에 없다. 그래서 tag가 가리키는 문자열이 Kyyu 와 같은 쓰레기값으로 한 번 더 바뀐 것이다.

그리고 나서 Segmentation fault가 뜨는 이유는 두번째 hello()가 모두 실행되고 나서 pop되는 return addr가 접근 불가능한 영역이었기 때문일 것 같다.(당연히 stack frame 포인터 자체가 엉뚱한 곳을 가리키고 있었기 때문에 쓰레기값으로 차있었을 것.) 이 부분에 대해서는 정확히는 말할 수 없을 것 같다. 실습을 해볼 수 있는 영역도 아니고..ㅜ 나중에 자세히 찾아봐야 할 것 같다.


2020.06.22
사실 저번 글이 시험 공부를 하다가 쓴 거 였는데 이해가 안돼서 뒷부분을 미뤄놨다가 시험 전 날 이해를 해서 시험이 끝나고 좀 쉰 후에 뒷부분을 쓰게 됐다 ! 😀 시험에 나오진 않았지만.. 잊어버리기 싫어서 쓰려고 한다. 뭐 잊어버리긴 하겠지만 다시보면 되니까 히히.

2020.06.24
하 진짜 내가 이걸 오늘까지 질질 끌 줄은 몰랐다. 물어볼 사람이 없는 공부는 정말 어렵다😇 이번에 구글링하면서 나중에 정말 잘하는 사람이 되려면 영어에 많이 익숙해져야겠다고 생각했다. 전보다야 나아졌지만 아직도 효율적으로 모르는 것에대한 답을 얻는 방법을 모르겠다. 흠 너무 왕도를 찾으려고 하는 건가 싶기도 하네

0개의 댓글