본문 바로가기

해킹-보안

쉘코드 작성하기

반응형

ி 쉘코드


명령쉘을 실행시키는 기계어 코드를 쉘코드라고 한다.
쉘코드 작성 순서는 아래와 같다.
사람이 이해할 수 있는 고급 언어로 컴파일한다.
컴파일된 파일을 디스어셈블한다.
라이브러리 함수가 실행하는 시스템 콜을 확인하고 어셈블리어로 구현한다.
어셈블리어를 기계어로 뽑아낸다.
고급 언어로 해당 기계어를 실행한다.



ி 쉘 코드 작성


칼리 32bit 버전에서 진행한다.



고급언어 작성


소스코드는 컴파일러를 통해 기계어로 번역된 뒤
오브젝트 파일을 생성하고 링커를 통해 실행 가능한 파일로 만들어 진다.
쉘을 실행하는 기계어를 만드려면 어셈블리어를 거쳐야한다.
어셈블리어는 다루기 쉬운 언어는 아니므로 상대적으로 이해하기 쉬운 고급 언어를 컴파일하여
어셈블리어를 만들어볼 것이다.

C언어로 쉘을 실행시키는 소스 파일을 작성하고 컴파일한다.


# gcc -o shell shell.c -static

execve 함수는 쉘을 실행하기 위한 system 함수와 유사하다.

int execv(const char *path, char *const argv[], char *const envp[]);

path에 존재하는 파일을 실행하며 argv를 인자로 전달한다.
argv 배열의 마지막에는 NULL 문자를 저장해야 한다.

gcc의 static 옵션은 공유 라이브러리 대신 정적 라이브러리를 사용한다는 의미다.
정적 라이브러리는 프로그램에서 사용하는 함수를 포함시켜 컴파일하게 된다.
공유 라이브러리를 쓰면 컴파일 시점에 사용할 라이브러리만 연결한다.
static 옵션을 적용하는 이유는 gdb에서 execve 함수를 디스어셈블하여 바로 보기 위함이다.



어셈블리어 작성


static 옵션 미적용 시 아래 왼쪽 사진처럼 공유 라이브러리로 점프하는 구문만 나온다.
# gdb ./shell



오른쪽 사진처럼 전체 코드를 보려면 static 옵션을 적용해야 한다.
물론 공유 라이브러리를 사용하더라도 gdb에서 프로그램을 먼저 run 하고 디스어셈블하는 방법도 있다.

위에서 execve를 호출할 때 레지스터에 들어가는 값은 아래 형식과 같다.

execve(%ebx, %ecx, %edx)
ebx, ecx, edx는 각각 execve 함수의 인자,
eax는 system call 번호이다.

➽ system call
Linux 커널이 제공하는 시스템 호출을 위한 고유 번호로서 아키텍처 / ABI에 따라 다르다.


execve는 기존에 존재하는 system call 번호를 사용해야한다.
unistd.h 파일에서 호출 번호를 확인할 수 있다.



위 정보를 종합하여 어셈블리어 코드를 작성한다.

# vim shell.s


1 - 3 : 프로그램 시작점 정의
# 첫 번째 인자
5~6 : eax와 edx 레지스터를 0으로 초기화
8 : NULL 문자 삽입
9 ~11 : /bin//sh 문자열을 16진수로 변환하여 push 후 ebx에 주소 저장
             ( 8byte를 맞추기 위해 /를 하나 더 넣는다 )

# 두 번째 인자
13 : NULL 문자 삽입
14 : /bin//sh 문자열 삽입
15 : /bin//sh 문자열 주소를 ecx에 저장

# execve 함수 호출
17 : execve 함수 호출 번호
18 : 인터럽트 호출

만든 어셈블리어를 링킹하고 실행하면 아래와 같이 root 쉘이 실행된다.




기계어 작성


링킹된 어셈블리어 파일을 디스어셈블하여 기계어 코드를 확인한다.


빨간 상자 안에 있는 기계어 코드를 16진수로 나열한다.
그리고 아래 C 소스코드처럼 해당 쉘 코드를 실행하도록 작성한다.

1
2
3
4
5
6
7
#include 
char *shellcode = "\x31\xc0\x31\xd2\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80";
int main()
{
(*(void (*)())shellcode)();
return 0;
}
cs

위 소스 코드를 컴파일할 때 -z exestack 옵션으로 스택에 실행 권한을 부여한다.
# gcc -m32 -z execstack -o ./shell ./shell.c
이후 해당 프로그램을 실행하면 root 권한의 쉘을 획득하게 된다.


반응형

'해킹-보안' 카테고리의 다른 글

OWASP-ZAP 웹 스캐닝  (0) 2020.10.16
Burpsuite XSS Validator  (0) 2020.10.15
pwnable BOF 실습  (0) 2020.10.06
pkcrack을 사용하여 압축파일 해제하기  (0) 2020.08.03
CVE 찾는 과정  (0) 2020.07.28