시스템해킹 수업 중 해커스쿨 ftz나 lob를 접하신 분들이 환경변수를 이용해 쉘을 얻고싶으시다는 질문을 하여 글을 쓰게 됬습니다.
제가 포스팅할 환경은 Ubuntu 14.04 3 32bit와 64bit 입니다.
32비트와 64비트는 비슷하지만 64비트 주소값이 8바이트가 들어간다는 점에서 약간 접근이 힘들 수도 있습니다. 기본적인 Stack Corruption을 일으켜 환경변수를 이용한 쉘 획득을 해보겠습니다.
1. 32bit Test
테스트할 코드는 아주 간단합니다. 버퍼를 20만큼 채워서 sfp 4바이트 넣고 return address 4바이트를 채우면 우리가 원하는 eip를 제어할 수 있습니다.
gcc -fno-stack-protector -mpreferred-stack-boundary=2 -o test test.c로 컴파일한 후 슬슬 환경변수에 쉘코드를 등록합니다. 환경변수 이름은 "test", 23바이트 쉘코드를 채우도록 하겠습니다. 그 후 , env | grep "test" 로 환경변수에 "test"가 잘 등록되어있는지 확인을 해보면, 아주 잘 되어있습니다.
코드 하나를 더 컴파일합니다. 이건 getenv("test")를 이용해 %p 포맷 스트링으로 "test" 환경변수의 주소를 출력합니다. 이건 대충 gcc -o env env.c로 컴파일해서 실행해보겠습니다.
환경변수를 이용하시려는 분들은 이 부분에서 멘붕이 왔을겁니다. 주소가 매번 다르게 셋팅이 되기 때문입니다. 이건 우분투에 ASLR 보호기법이 적용되어 있어 공유라이브러리나 환경변수의 스택주소를 랜덤하게 매핑합니다. 이걸 막기 위해서 명령어 하나를 입력합니다.
sudo sysctl -w kernel.randomize_va_space=0
루트권한이 있어야 실행할 수 있는 명령어입니다. 이걸 쓰면 ASLR 보호기법이 풀립니다. 이건 터미널을 닫아도 적용되는 부분이므로 테스팅을 끝내고 나서
sudo sysctl -w kernel.randomize_va_space=2 로 돌려놓습니다.
자, ASLR이 풀렸으니 다시 환경변수의 주소를 확인해볼까요.
깔끔합니다. 이제 이 주소를 이용해서 공격을 시도해봅시다.
?????????????????????
페이로드를 맞게 작성했음에도 불구하고 segmentation fault가 뜹니다.
strace ./test `python -c 'print "a" * 24 + "\xc5\xff\xff\xbf"'` 를 이용해서 바이너리를 trace해도 eip는 0xbfffffc5를 맞게 가리키고 있습니다. 무슨 문제일까요.
스택을 확인해보면 "test"의 환경변수를 봤을 때, 아하! "Desktop/test" 를 가리키고 있네요. 여기서 x/5s 0xbfffffc5 - 28로 확인해보면 "test= shellcode"가 정확하게 박혀있네요. 환경변수 주소에서 23바이트만큼 빠지면 쉘코드를 정확하게 가리키고 있고.. 그럼 쉘코드에 23바이트를 추가하고 다시 확인해봐야겠네요.
그러나 그냥. "\x90"은 아무것도 하지 않는 nop 명령어이기 때문에 적당하게 300개정도 앞에 채우고 nop sled를 만들어 쉘코드를 가리키게 해보겠습니다.
(쉘코드에 300바이트 추가함으로써 env 환경변수 주소가 바뀌었습니다. 테스트할 때에 ./env 한번 더 실행해서 주소를 확인해주세요)
???????????????????????????????????????????????????????????????
그래도 되지 않네요.
이건 우분투에 적용되어 있는 DEP 보호기법때문입니다. 상위 리눅스 버전에서는 스택이나 힙에 기본적으로 NX bit가 걸려있기 때문에 그 영역에서는 명령어 실행권한이 없습니다. 즉 쉘코드를 넣어도 쉘코드가 작동이 안된다는 거지요. 이것도 한번 풀어보겠습니다.
gcc -z execstack -fno-stack-protector -mpreferred-stack-boundary=2 -o test test.c 로 -z execstack 옵션을 추가했습니다. 이걸로 NX bit도 해제되었습니다.
다시 실행을 해보면? 쉘이 아주 잘 뜹니다. 이걸로 32비트 환경변수 쉘코드 테스팅을 마칩니다.
2. 64bit Test
32비트에서 테스팅해봤으니 64비트도 똑같이 가겠습니다. 빠르게 사진으로 대체할게요
테스트할 64비트환경의 C코드입니다.
64비트는 32비트와는 다르게 -mpreferred-stack-boundary 옵션을 안걸어주셔도 더미값이 쌓이지 않습니다. 컴파일된 바이너리에 실행권한을 주고 (-z execstack) 돌려보니 잘 실행됩니다.
shell ps, shell cat /proc/(pid)/maps 로 매핑되어있는 영역의 권한을 확인해보면 스택이나 힙, vdso 영역 다 실행권한을 얻었네요.
환경변수에 nopsled 1000 바이트와 shellcode 를 집어넣습니다. env | grep "test" 로 확인해보니 환경변수가 잘 들어갔네요.
sudo sysctl -w kernel.randomize_va_space=0 으로 ASLR 보호기법을 풀고 getenv() 함수를 이용해 환경변수 "test" 의 주소를 얻어냅니다.
"test"의 주소가 0xffffe80d네요. 여기서 0x7fff 는 생략되었으므로 페이로드 작성할 때는 0x7fffffffe80d 로 적습니다.
이제 페이로드를 작성해볼까요.
여기서 32비트와 약간 차이가 있습니다.
32비트였다면 buf 20byte + sfp 4byte + return address 였겠지만 64비트는 변수초기화를 안해도 미리 지역변수 영역 8바이트를 스택에 할당해놓습니다. 그리고 8바이트 단위로 메모리할당이 되므로 buf[20]이면 실제 24바이트가 할당되어
buf 24byte + local variable address 8byte + sfp 8byte + return address 8byte
== "a" * 24 + "ABCDEFGH" * 2 + "\x0d\xe8\xff\xff\xff\x7f" 를 버퍼에 박아넣으면 쉘을 딸 수 있습니다.
환경변수 주소를 이용할 때에 실제 바이너리와 거리차이가 조금 날 수도 있으므로 환경변수 주소보다 한 20바이트 30바이트정도 뒤를 가리켜주시는게 안전합니다.
'Let's Study > Hacking Technique' 카테고리의 다른 글
Alpha-Numeric Shellcode (Ascii Shellcode) (0) | 2015.01.23 |
---|---|
stack corruption basic (2) | 2014.10.24 |
Assembly-> C Handray(2) (0) | 2014.10.07 |
Assembly-> C Handray(1) (2) | 2014.10.06 |