쉘이란 사용자로부터 입력받은 명령어를 기계어나 어셈으로 바꿔 컴퓨터에게 전달해주는 역할을 하는 프로그램이다. 예를 들어서 윈도우 cmd창에 dir을 치면 현재 디렉토리의 목록을 볼 수 있는데 여기서 cmd가 쉘인 것이다.
https://ko.wikipedia.org/wiki/%EC%85%B8
쉘코드는 이러한 쉘을 어셈으로 짠 것으로 BOF같은 공격을 통해서 쉘코드를 스택에 넣고 RET값을 쉘코드가 저장되어 있는 주소로 바꿔 쉘을 얻을때 쓴다.
쉘코드를 만들때에는 우선 자신이 사용할 함수에 대해 자세히 알아야 한다. 쉘코드를 만들때 execve를 쓸 것이데 그러면 이 execve의 용도 및 인자값에 대한 정보(어떤 값이 들어가는지, 인자 값의 데이텨형은 어떠한지)를 조사한다.
http://forum.falinux.com/zbxe/?mid=C_LIB&page=3&document_srl=408569
리눅스에서 /bin/sh 쉘을 execve함수를 통해서 띄울 것인데 소스코드는 다음과 같다.
void main()
{
char *shell[2];
shell[0]="/bin/sh";
shell[1]=NULL;
execve(shell[0], shell, NULL);
}
이 소스코드를 gcc를 통해서 컴파일 해주고 난 뒤 실행을 시키면 아래와 같이 쉘을 얻게 됩니다.
이제 함수에 대한 파악을 하였으니 어셈으로 짜면 됩니다.
우선 execve의 콜백터를 /usr/include/asm/unistd.h에서 확인합니다.
그리고 어셈으로 짜면 됩니다~
asm은 어셈으로 짠다는 소리이고 volatile은 최적화를 하지 말라-라는 뜻입니다(최적화란 불필요한 요소들을 컴파일과정을 통해서 생략하는 것 입니다.).
더 자세한 내용 http://ezbeat.tistory.com/150
첫번째줄부터 xor %eax, %eax를 해주는데 그 이유는 이 코드를 objdump를 해서 봤을때 쉘코드 중 0x00이 있으면 문자열에 끝으로 인식하고 0x00이전까지만 읽게 됩니다. 그러면 쉘코드가 실행이 되겠지요. 이것을 막기위해 직접적으로 0x00을 %eax에 넣는 것이 아니라 자기 자신을 xor했을때 0이 된다라는 특징을 이용합니다.
두번째줄은 /bin/sh의 문자열의 끝을 알려주기 위해 xor로 만든 0을 넣어줍니다.
세번째~네번째줄은 /bin/sh의 아스키코드 값입니다. 그리고 여기서는 리틀엔디안 형식으로 넣었습니다.
다섯번째줄은 mov %esp, %ebx인데 이것은 esp를 ebx에다가 넣어준다-라는 말입니다.(아!!! 그리고 이 코드의 어셈은 인텔형식이 아닌 AT&T형식입니다.)
이렇게 해준 이유는 execve에 첫번째 인자가 /bin/sh의 주소이기 때문입니다.
https://wiki.kldp.org/KoreanDoc/html/Assembly_Example-KLDP/Assembly_Example-KLDP.html
여섯번째줄~일곱번째줄은 push %eax하고 push %ebx를 해주는 데 push %eax는 왜 해주는 지 잘 모르겠습니다;;;;;; 죄송해요 ㅠㅠ
push %ebx를 해주는 이유는 두번째 인자는 shell(위의 소스코드를 보면)인데 이것은 포인터 배열의 이름, 즉 주소이죠. *shell == shell[0]이고 **shell==/bin/sh입니다. 즉 더블 포인터라는 말이겠네요. 그래서 push %ebx(%ebx는 %esp의 주소, 즉 아까 /bin/sh의 주소)를 해주고 그 다음 줄인 mov %esp, %ecx를 해주면 현재 %esp는 push되어 있는 %ebx를 가리키고 있고 이 %ebx는 /bin/sh의 주소를 가지고 있으므로 execve의 두번째 인자와 데이터 형이 같습니다.
아홉번째줄 mov %eax, %edx인데 이것은 execve의 마지막인자인 NULL을 의미합니다.
열번째줄인 mov $0xb, %al는 execve의 콜 벡터의 값입니다. 그리고 그 다음 줄 int $0x80은 함수를 호출하겠다-라는 의미입니다.
(아!!! 그런데 왜 mov $0xb, %al을 해주는 이유는 만약 %eax로 하면 이것은 32비트인데 al은 하위 8비트를 이용하겠다-라는 뜻입니다. 그래서 만약 %eax로 썼을 경우 objdump를 떴을때 0x00이 나오기 때문에 %al을 해주는 것입니다. 자세한 내용은 직접해보시고 눈으로 보시는 게 더 이해가 잘 될것 같습니다.
이 방법이외에 다른 방식으로 쉘코드를 짠 것이 있는데 이것은 아직 공부를 안해서 나중에 추후에 공부하고 추가하겠습니다(setreuid를 추가한 부분과 쉘코드 종류 및 리버스 쉘에 대한 것도).