저도 BOF공부를 하면서 처음으로 만든 쉘코드를 만들어보는 것으로 해보겠습니다.
execve 함수는 첫번째 인자로 실행시킬 파일 명
두번째 인자로는 전달될 인자입니다. ( argv[0], argv[1] 이라고 생각하면 됩니다. )
뭐 아무것도 없는 소스코드 입니다. 단순히 쉘을 떨치는 프로그램 입니다.
컴파일 하고 실행시켜보겠습니다.
위에 static 옵션을 준 것은 나중에 어셈코드로 볼 것인데 각 함수들이 내부적으로 어떤일을 하는지도 보기위해 옵션을 준 것입니다.
이제 해당 프로그램을 objdump로 봐보겠습니다.
gdb로 하지 않고 objdump를 사용한 이유는 헥사덤프값을 보기 위함 입니다. 쉘코드 만들때 필요하기 때문입니다.
아래쪽을 보니 <__execve>라는 함수를 호출하고 있는 것을 볼 수 있습니다.
<__execve>함수 내부를 보도록 하겠습니다.
<__execve>함수 내부 입니다. 빨간색 네모친 부분만 보시면 될것입니다.
mov 0x8(%ebp),%edi
mov 0xc(%ebp),%ecx
mov 0x10(%ebp),%edx
mov %edi,%ebx
mov $0xb,%eax
int $0x80
인터럽트 0x80을 하고 있습니다. 위에를 보면 eax에 0xb를 넣고 있군요.
인터럽트 0x80은 시스템콜 함수이므로 시스템 콜 값을 지정해 줘야되는데 0xb 시스템 콜을 하고 있네요.
<__execve>함수를 호출합니다.
그리고 위쪽 소스를 보면
ebx에 첫번째 인자
ecx에 두번째 인자
edx에 세번째 인자
를 넣고 있습니다.
첫번째 인자는 "/bin/sh"
두번째 인자는 shell
세번째 인자는 0
이거였습니다.
지금 까진 본 것을 토대로 <__execve>를 호출하는 과정을 어셈블리언어로 나타내보겠습니다.
이것을 소스코드로 인라인 어셈블리 할 것입니다.
push $0x0068732F // /sh\0
push $0x6E69622F // /bin
mov %esp,%ebx // /bin/sh의 주소를 ebx에 넣음
push $0x0 // shell[1]
push %ebx // shell[0]
mov %esp,%ecx // shell을 ecx에 넣음
mov $0x0,%edx // 0을 edx에 넣음
mov $0xb,%eax
int $0x80 // 시스템 콜
위 소스코드를 인라인 어셈으로 짜겠습니다.
__volatile__를 붙힌 이유는 예전에 윈도우에서 쓰레드 동기화 함수 공부하면서 나왔던 키워드 입니다.
그때 정리했던 내용을 잠시 써보겠습니다.
volatile 의미(키워드) - 2가지 기능
C, C++언어의 ANSI표준 키워드
1. 최적화를 수행하지 마라.
예를 들면
int a = 10;
a = 30;
printf(a);
에서 첫번째 문장은 필요가 없다. 그래서
컴파일러는 보통
int a = 30;
printf(a);
로 바꿔버림
이런게 최적화지만 최적화가 필요하지 않을 때도 있기 때문에..
int volatile a = 10;
이렇게 하면 된다.
2. 메모리에 직접 연산하라!
성능 향상을 위해 요새는 캐쉬메모리라는 것을 사용한다.
하지만 캐쉬메모리에 저장이 되면 우리가 원하는 시점에 그것을 사용할 수 없다.
그래서 절대로 그 데이터가 캐쉬메모리에 저장되지 않게하는 기능
여기선 첫번째 기능 때문에 쓴 것입니다. 저희가 썼던 어셈언어가 최적화가 되지 않고 그대로 나오게 하기 위함입니다.
컴파일을 하고 실행을 시키면 쉘이 떨어지는 것을 볼 수 있습니다.
이제 저 소스코드에서 헥사값을 추출하기 위해 objdump로 열어보았습니다.
우리가 썼던 코드가 그대로 있습니다. 부가적으로 붙은 것은 함수 프롤로그 에필로그? 인데 컴파일에서 알아서 붙혀줍니다.
빨간색 네모친 부분의 헥사값을 그대로 가져다 쓸려고 했지만 도중 도중 " 00 " 이라고 되서 널 문자가 보입니다.
코드 실행 중 널 문자가 있으면 끝인줄 알고 멈춰버리기 때문에 " 00 "이라고 된 부분을 전부 없애주어야 합니다.
다시 어셈 코드를 수정 하도록 하겠습니다.
어디가 바뀌었는지 비교해보시길 바랍니다.
위 코드를 다시 컴파일하고 헥사값을 봐보겠습니다.
" 00 " 널은 전부 없앴습니다.
저기서 옆에 있는 헥사값만 빼오면 쉘코드 완성입니다.
쉘코드
\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53
\x89\xe1\x89\xc2\xb0\x0b\xcd\x80
한줄로 써주셔도 됩니다.
( 저는 쉘코드를 만드는 것에만 중점을 두고 만들었습니다만.. 최대한 쉘코드는 짧게 만들면 만들수록 좋습니다. 우리가 공격해야할 버퍼의 크기가 작을 경우도 대비를 해야되니 말이죠.. )
( 저는 쉘코드를 만드는 것에만 중점을 두고 만들었습니다만.. 최대한 쉘코드는 짧게 만들면 만들수록 좋습니다. 우리가 공격해야할 버퍼의 크기가 작을 경우도 대비를 해야되니 말이죠.. )
쉘코드 만드는 것까지 끝났지만 쉘코드가 정상적으로 작동하는지 알아봐야겠죠??
테스트를 해보겠습니다. 쉘 코드를 실행시키기 위해서는 아래와 같은 소스코드를 만들어서 실험해봐야합니다.
메인 함수가 끝나면서 스택에 있는 리턴 주소를 제가 만든 쉘코드가 있는 곳으로 돌려준 것입니다.
컴파일 시키고 실행시켜보겠습니다.
정상적으로 쉘이 떨어진 것을 볼 수 있습니다.
그리고 쉘코드를 만들 때 도중에 " 00 " 이라는 널이 들어가면 안되지만 또 " ff "도 들어가서는 안됩니다.
이 내용에 대해서는 다음에 올리겠습니다.
'My Study > Exploit' 카테고리의 다른 글
Hackerschool level15 (0) | 2010.03.23 |
---|---|
Hackerschool level14 (0) | 2010.03.22 |
Hackerschool level13 (0) | 2010.03.22 |
Hackerschool level12 (3) | 2010.03.22 |
BOF의 개념 ( Hackerschool level11 ) (7) | 2010.03.19 |