□ 문제 요약
1. 출제의도 파악 : 할당 후 해제된 객체의 메모리에 임의의 값을 덮어 씌워 쉘 명령어(/bin/sh)를 실행
가. 반복문에서 객체 할당 및 해제에 대한 처리를 잘못할 경우 임의의 값 쓰기 및 실행 가능
* 또는 다른 함수에서 객체를 해제한 것을 인지하지 못하여 해제된 객체를 참조할 경우 취약점 발생
나. 특정 명령어를 가리키는 주소를 객체가 할당받은 메모리에 쓴 후 객체 메소드를 통해 실행하여 악용 가능
2. 풀이방법 (문제 전체 소스 코드는 맨 하단에 첨부)
가. give_shell 함수를 가르키는 주소 값(0x00401568)을 파일에 저장
나. 해당 파일 이름(solve)과 파일 읽기 길이(4바이트)를 인자 값으로 설정 후 바이너리 실행
다. 객체 메소드 호출, 객체 해제, 메모리에 파일 읽기, 메소드 재호출로 /bin/sh 실행
□ 상세분석
1. 반복문 분석
가. 객체 메소드 호출
1) cpp 소스코드 : m,w 변수에 Man,Woman 객체 할당 후 introduce 메소드 호출
2) m 변수($rbp-0x38)에 할당된 Man 객체의 introduce 메소드 호출
3) Man 객체와 Human 객체, give_shell 메소드 주소
나. 해제된 객체에 값 덮어쓰기
1) Read 함수 CPP 소스코드
2) 파일 읽기 첫 번째 : data 변수에 할당된 메모리 공간에 4바이트 글자 AAAA 입력
3) 파일 읽기 두 번째 : data 변수에, 해제된 객체 m의 주소를 할당 받아 객체 m의 기존 데이터 덮어쓰기
다. 메소드 호출을 통해 덮어씌워진 메소드 실행
1) m 객체에, give_shell 함수를 가리키는 주소 값에서 0x8만큼 뺀 주소 값을 덮어 씌운 후 메소드 호출, /bin/sh 실행
□ 문제 소스 코드 전체
#include <fcntl.h>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
using namespace std;
class Human{
private:
virtual void give_shell(){
system("/bin/sh");
}
protected:
int age;
string name;
public:
virtual void introduce(){
cout << "My name is " << name << endl;
cout << "I am " << age << " years old" << endl;
}
};
class Man: public Human{
public:
Man(string name, int age){
this->name = name;
this->age = age;
}
virtual void introduce(){
Human::introduce();
cout << "I am a nice guy!" << endl;
}
};
class Woman: public Human{
public:
Woman(string name, int age){
this->name = name;
this->age = age;
}
virtual void introduce(){
Human::introduce();
cout << "I am a cute girl!" << endl;
}
};
int main(int argc, char* argv[]){
Human* m = new Man("Jack", 25);
Human* w = new Woman("Jill", 21);
size_t len;
char* data;
unsigned int op;
while(1){
cout << "1. use\n2. after\n3. free\n";
cin >> op;
switch(op){
case 1:
m->introduce();
w->introduce();
break;
case 2:
len = atoi(argv[1]);
data = new char[len];
read(open(argv[2], O_RDONLY), data, len);
cout << "your data is allocated" << endl;
break;
case 3:
delete m;
delete w;
break;
default:
break;
}
}
return 0;
}