01.The basics technic of Shellcode Excuse the ads! We need some help to keep our site up. List Shellcode The basics of shellcode(ubuntu-16.04) C ASM M

Similar documents
02.Create a shellcode that executes "/bin/sh" Excuse the ads! We need some help to keep our site up. List Create a shellcode that executes "/bin/sh" C

Poison null byte Excuse the ads! We need some help to keep our site up. List 1 Conditions 2 Exploit plan 2.1 chunksize(p)!= prev_size (next_chunk(p) 3

Deok9_Exploit Technique

hlogin2

Reusing Dynamic Linker For Exploitation Author : Date : 2012 / 05 / 13 Contact : Facebook : fb.me/kwonpwn

01.ROP(Return Oriented Programming)-x86 Excuse the ads! We need some help to keep our site up. List Return Oriented Programming(ROP) -x86 Gadgets - PO

INTRO Basic architecture of modern computers Basic and most used assembly instructions on x86 Installing an assembly compiler and RE tools Practice co

No Slide Title

PowerPoint Template

강의10

Microsoft Word - FunctionCall

PowerPoint 프레젠테이션

PowerPoint 프레젠테이션

Microsoft Word - building the win32 shellcode 01.doc

PowerPoint 프레젠테이션

08.BROP(Blind Return Oriented Programming) Excuse the ads! We need some help to keep our site up. List BROP(Blind Return Oriented Programming) BROP st

PowerPoint 프레젠테이션

Microsoft Word - Reverse Engineering Code with IDA Pro-2-1.doc

Microsoft PowerPoint - chap02-C프로그램시작하기.pptx

2015 CodeGate 풀이보고서 김성우 1. systemshock strcat(cmd, argv[1]); 에서스택버퍼오버플로우가발생합니다

Microsoft PowerPoint - a10.ppt [호환 모드]

PowerPoint 프레젠테이션

임베디드시스템설계강의자료 6 system call 2/2 (2014 년도 1 학기 ) 김영진 아주대학교전자공학과

Microsoft PowerPoint - a2.ppt [호환 모드]

Microsoft PowerPoint - a6.ppt [호환 모드]

PowerPoint 프레젠테이션

<4D F736F F F696E74202D20B8B6C0CCC5A9B7CEC7C1B7CEBCBCBCAD202839C1D6C2F7207E203135C1D6C2F >

Level 4 ( hell_fire -> evil_wizard ) ~]$ cat evil_wizard.c /* The Lord of the BOF : The Fellowship of the BOF - evil_wizard

0x <main+41>: lea eax,[ebp-264] 0x f <main+47>: push eax 0x080484a0 <main+48>: call 0x804835c <strcpy> 0x080484a5 <main+53>: add esp,0x1

Microsoft PowerPoint - a8a.ppt [호환 모드]

Microsoft PowerPoint - a5a.ppt [호환 모드]

Microsoft PowerPoint - chap10-함수의활용.pptx

IDA 5.x Manual hwp

버퍼오버플로우-왕기초편 10. 메모리를 Hex dump 뜨기 앞서우리는버퍼오버플로우로인해리턴어드레스 (return address) 가변조될수있음을알았습니다. 이제곧리턴어드레스를원하는값으로변경하는실습을해볼것인데요, 그전에앞서, 메모리에저장된값들을살펴보는방법에대해배워보겠습

Microsoft PowerPoint - chap06-2pointer.ppt

금오공대 컴퓨터공학전공 강의자료

Microsoft Word - ExecutionStack

슬라이드 1

CKKeyPro 적용가이드

vi 사용법

MPLAB C18 C

Microsoft Word - Static analysis of Shellcode.doc

06Àå

Microsoft PowerPoint - 3ÀÏ°_º¯¼ö¿Í »ó¼ö.ppt

Microsoft PowerPoint - 15-MARS

6주차.key

SRC PLUS 제어기 MANUAL

[ 마이크로프로세서 1] 2 주차 3 차시. 포인터와구조체 2 주차 3 차시포인터와구조체 학습목표 1. C 언어에서가장어려운포인터와구조체를설명할수있다. 2. Call By Value 와 Call By Reference 를구분할수있다. 학습내용 1 : 함수 (Functi

윤석언 - Buffer Overflow - 윤석언 제12회세미나 수원대학교보안동아리 FLAG

목차 포인터의개요 배열과포인터 포인터의구조 실무응용예제 C 2

Microsoft PowerPoint - chap01-C언어개요.pptx

PowerPoint 프레젠테이션

hlogin7

Microsoft Word - 1. ARM Assembly 실습_xp2.doc

untitled

Microsoft PowerPoint - ch07 - 포인터 pm0415

슬라이드 1

<4D F736F F F696E74202D20B8AEB4AABDBA20BFC0B7F920C3B3B8AEC7CFB1E22E BC8A3C8AF20B8F0B5E55D>

Return-to-libc

학습목차 2.1 다차원배열이란 차원배열의주소와값의참조

<4D F736F F F696E74202D20BBB7BBB7C7D15F FBEDFB0A3B1B3C0B05FC1A638C0CFC2F72E BC8A3C8AF20B8F0B5E55D>

A Hierarchical Approach to Interactive Motion Editing for Human-like Figures

금오공대 컴퓨터공학전공 강의자료

PowerPoint 프레젠테이션

chap 5: Trees

1. 안드로이드개발환경설정 안드로이드개발을위해선툴체인을비롯한다양한소프트웨어패키지가필요합니다 툴체인 (Cross-Compiler) 설치 안드로이드 2.2 프로요부터는소스에기본툴체인이 prebuilt 라는이름으로포함되어있지만, 리눅스 나부트로더 (U-boot)

Chapter #01 Subject

커알못의 커널 탐방기 이 세상의 모든 커알못을 위해서

untitled

Microsoft Word - FreeBSD Shellcode 만들기.docx


OCW_C언어 기초

11장 포인터

A Dynamic Grid Services Deployment Mechanism for On-Demand Resource Provisioning

목 차 1. 개요 취약점분석추진배경 취약점요약 취약점정보 취약점대상시스템목록 분석 공격기법및기본개념 시나리오 공격코드

PA for SWE2007

BMP 파일 처리

Microsoft PowerPoint - hy2-12.pptx

Microsoft PowerPoint - chap03-변수와데이터형.pptx

Linux Binary Hardening with Glibc Hyeonho Seo

Microsoft PowerPoint - chap06-1Array.ppt

Microsoft PowerPoint - chap13-입출력라이브러리.pptx

JVM 메모리구조

기술문서 LD_PRELOAD 와공유라이브러리를사용한 libc 함수후킹 정지훈

PowerPoint 프레젠테이션

Chapter 4. LISTS

Microsoft PowerPoint - IOControl [호환 모드]

Microsoft Word - readme.doc

Microsoft PowerPoint - 03_(C_Programming)_(Korean)_Pointers

Execute_Shellcode_on_the_MacOSX.txt - 메모장

4. #include <stdio.h> #include <stdlib.h> int main() { functiona(); } void functiona() { printf("hihi\n"); } warning: conflicting types for functiona

PowerPoint 프레젠테이션

BOF Foundation.doc

Deok9_PE Structure

목차 1. 소개... 3 가. BOF란?... 3 나. 윈도우 BOF 개발환경및사용툴 Shellcode 작성하기... 4 가. cmd 쉘 ) 소스코드작성 ) 디스어셈블리 ) 어셈블리코드편집 간단

Microsoft PowerPoint - es-arduino-lecture-03

Microsoft PowerPoint - chap04-연산자.pptx

리눅스 취약점대응방안권고 / KISA 취약점점검팀 영향받는플랫폼 OS, FAQ 추가 개요 미국보안회사 에의해 시스템의 라이브러리 의특정함수에서임의코드를실행할수있는취약점이공개 해당취약점은 CVE 지정, 도메인네임을

0x00 Contents 0x About Nickster 0x Analaysis 0x Exploit

< E20C6DFBFFEBEEE20C0DBBCBAC0BB20C0A7C7D12043BEF0BEEE20492E707074>

Transcription:

01.The basics technic of Shellcode Excuse the ads! We need some help to keep our site up. List Shellcode The basics of shellcode(ubuntu-16.04) C ASM Machine code Assembly code Linux system call in assembly Save argument value in registers Assembly Code Example("Hello, world!") Build assembly code(32 bit) Build assembly code(64 bit) Change to Shellcode format Code Test Debugging Remove null byte Call instruction Register Related site Comments Shellcode Shellcode라고불리는이유는일반적으로명령 shell을실행하여공격자가해당시스템을제어하기때문이라고합니다. Shellcode는 Machine code로작성된작은크기의프로그램입니다. Shellcode는일반적으로어셈블리어로작성후기계어로변경합니다. 셀코드는세포에침투하는생물학적바이러스처럼실행중인프로그램에삽입된코드를뜻합니다. 셀코드는실제로실행가능한프로그램은아니므로셀코드를작성할때메모리상배치라든지메모리세그먼트등에신경쓰지않아도됩니다. The basics of shellcode(ubuntu-16.04) C ASM Machine code Shellcode 를개발하기전에다음과같은이해가필요합니다. 우리가사용중인많은프로그램들은 C 언어와같은고수준의언어를컴파일과정에의해 Assembly, Machine code 같은저수준언어로변경된파일입니다. C code 는해당시스템에맞는 Assembly code 로변환 변환된 Assembly code 를 Machine code 로표현이렇게 Machine code 는메모리에로드되어코드를실행하게됩니다. 즉, 공격자는취약성을이용하여 Shell 을획득하기위해서는주수준언어로개발된코드가필요합니다. Shellcode 는 Assembly, Machine code 같은저수준언어개발되어야합니다.

C ASM Machine code C code Assembly code Machine code Helloworld.c Helloworld.s Machine code #include<stdio.h> int main(){ printf("hello world! \n"); return 1; } 0000000000400526 <main>: 400526: push %rbp 400527: mov %rsp,%rbp 40052a: mov $0x4005c4,%edi 40052f: callq 400400 <puts@plt> 400534: mov $0x1,%eax 400539: pop %rbp 40053a: retq 400526: 55 400527: 48 89 e5 40052a: bf c4 05 40 00 40052f: e8 cc fe ff ff 400534: b8 01 00 00 00 400539: 5d 40053a: c3 Assembly code Shellcode 를개발하기위해아래와같은기본적인 Assembly 명령어에대한지식이필요합니다. 이외에도다양한 Instructions 이존재합니다. Intel syntax Instructions Meaning mov destination, source 목표피연산자에소스피연산자를복사합니다. PUSH value stack 에 Value 값을저장합니다. POP register stack 상위의값을레지스터에저장합니다. CALL function_name(address) 리턴을위해 CALL 명령어의다음명령주소를스택에저장한후함수의위치로점프를합니다. ret 스택으로부터리턴주소를팝하고그곳으로점프하여함수에서리턴합니다. inc destination 목표피연산자를 1 증가시킵니다. dec destination 목표피연산자를 1 감소시킵니다. add destination, value 목표피연산자에 value 값을더합니다. sub destination, value 목표피연산자에 value 값을뺍니다. or destination, value 비트 or 논리연산을한다. 최종결과는목표피연산자에저장됩니다. and destination, value 비트 and 논리연산을한다. 최종결과는목표피연산자에저장됩니다. xor destination, value 비트 xor 논리연산을한다. 최종결과는목표피연산자에저장됩니다. lea destination, source 목표피연산자에소스피연산자의유효주소를로드합니다. Assembly code 에서시스템함수를호출하기위해 "int 0x80", "syscall" 명령어를사용할수있습니다. "int" 명령어의피연산자값으로 0x80 을전달하게되면 EAX 에저장된시스템함수를호출합니다. "syscall" 명령어를호출하면 RAX 에저장된시스템함수를호출합니다. INT 0x80 & SYSCALL Instructions Meaning Architecture INT <Operand 1> Call to interrupt x86, x86_64 SYSCALL SYStem call x86_64

X86 Assembly/Interfacing with Linux x86 instruction listings https://en.wikibooks.org/wiki/x86_assembly/interfacing_with_linux Linux system call in assembly Shellcode 를개발하기위해 Assembly code 에서사용가능한 system 함수들이필요합니다. C 에서는편의성과호환성을위해표준라이브러리가제공됩니다. 표준라이브러리가다양한아키텍처에맞는시스템콜을알기때문에여러시스템에서컴파일이가능합니다. 어셈블리언어는어느특정프로세서아키텍처용이라고정해져있습니다. 호환성있는표준라이브러리가없으며, 커널시스템콜을직접호출할수있습니다. 다음과같이해당파일에서사용가능한 System 함수들을확인할수있습니다. System call 정보는운영체제, 프로세서의아키텍처마다다릅니다. 해당파일에사용한가능한리눅스시스템함수의이름과시스템콜번호가나열돼있습니다. 어셈블리로시스템함수을호출할때는시스템콜번호를이용합니다. 아래와같이동일한 Ubuntu 시스템도 32bit, 64bit 에서사용되는시스템함수의콜번호가다릅니다. unistd_32.h lazenca0x0@ubuntu:~/$ cat /usr/include/x86_64-linux-gnu/asm/unistd_32.h #ifndef _ASM_X86_UNISTD_32_H #define _ASM_X86_UNISTD_32_H 1 #define NR_restart_syscall 0 #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 #define NR_lchown 16 #define NR_break 17 #define NR_oldstat 18 #define NR_lseek 19 #define NR_getpid 20...

unistd_64.h lazenca0x0@ubuntu:~/$ cat /usr/include/x86_64-linux-gnu/asm/unistd_64.h #ifndef _ASM_X86_UNISTD_64_H #define _ASM_X86_UNISTD_64_H 1 #define NR_read 0 #define NR_write 1 #define NR_open 2 #define NR_close 3 #define NR_stat 4 #define NR_fstat 5 #define NR_lstat 6 #define NR_poll 7 #define NR_lseek 8 #define NR_mmap 9 #define NR_mprotect 10 #define NR_munmap 11 #define NR_brk 12 #define NR_rt_sigaction 13 #define NR_rt_sigprocmask 14 #define NR_rt_sigreturn 15 #define NR_ioctl 16 #define NR_pread64 17 #define NR_pwrite64 18 #define NR_readv 19 #define NR_writev 20... Ubuntu 16.04 32bit : /usr/include/x86_64-linux-gnu/asm/unistd_32.h 64bit : /usr/include/x86_64-linux-gnu/asm/unistd_64.h Save argument value in registers 앞에서확인한시스템함수의인자값은다음과같이사용할수있습니다. System call 번호는 EAX, RAX 에저장합니다. System 함수의인자값은아래표를참고하여전달하면됩니다. Kernel 의경우기본적으로 cdecl 함수호출규약을사용하지만, 동작의유연성을위해 "System V ABI" Calling Convention 도사용가능합니다. Shellcode 를작성하는경우 "System V ABI" Calling Convention 을사용합니다. Argument - Register(32bit) Register(64bit) System call EAX RAX Argument 1 EBX RDI Argument 2 ECX RSI Argument 3 EDX RDX Argument 4 ESI R10 Argument 5 EDI R8 Argument 6 EBP R9

Calling conventions http://www.int80h.org/bsdasm/#alternate-calling-convention https://wiki.osdev.org/system_v_abi#i386 http://refspecs.linuxfoundation.org/elf/x86_64-abi-0.99.pdf https://stackoverflow.com/questions/2535989/what-are-the-calling-conventions-for-unix-linux-system-calls-on-i386-and-x86-6 Assembly Code Example("Hello, world!") Build assembly code(32 bit) 다음어셈블리코드를보면앞부분에메모리세그먼트가선언돼있습니다. 데이터세그먼트에 "Hello, world!" 문자열과개행문자 (0x0a) 가있습니다. 텍스트세그먼트에실제어셈블리명령이있습니다. ELF 바이너리를생성하려면링커에게어셈블리명령이어디서부터시작하는지알려주는 global _start 줄이필요합니다. ASM32.asm section.data ; msg db "Hello, world!",0x0a, 0x0d ;, section.text ; global _start ; ELF _start: ; SYSCALL: write(1,msg,14) mov eax, 4 ; '4' eax. mov ebx, 1 ; '1' ebx. mov ecx, msg ; ecx. mov edx, 14 ; '14' edx. int 0x80 ;. ; SYSCALL: exit(0) mov eax, 1 ; exit '1' eax. mov ebx, 0 ; '0' ebx. int 0x80 ;. -f elf 인자를 nasm 어셈블리를사용해 helloworld.asm 을어셈블해서 ELF 바이너리로링크할수있는목적 (object) 파일로만들것입니다. 링커프로그램 ld 는이어셈블된목적파일에서실행가능한 a.out 바이너리를만들어냅니다. Build nasm -f elf ASM32.asm ld -m elf_i386 -o hello ASM32.o./hello Hello, world! file./hello./hello: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, not stripped Install nasm sudo apt-get install nasm Build assembly code(64 bit)

ASM64.asm section.data ; msg db "hello, world!",0x0a, 0x0d ;, section.text ; global _start ; ELF _start: ; SYSCALL: write(1,msg,14) mov rax, 1 ; '1' rax. mov rdi, 1 ; '1' rdi. mov rsi, msg ; rsi. mov rdx, 14 ; '14' rdx. syscall ;. ; SYSCALL: exit(0) mov rax, 60 ; exit '60' eax. mov rdi, 0 ; '0' ebx. syscall ;. Build nasm -f elf64 ASM64.asm ld -o hello64 ASM64.o./hello64 hello, world! file./hello64./hello64: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped Change to Shellcode format Code 앞에서개발한프로그램은혼자서동작되지않으며링킹과정도필요하지때문에 Shellcode 가아닙니다. 다음과같이앞에서개발한코드를독자적으로동작가능하게변경할수있습니다. 텍스트, 데이터세그먼트를사용하지않습니다. 함수의호출은 call 명령어를사용하여 "helloworld" 함수를호출합니다. Write 시스템함수에의해출력될문자열은해당명령어뒤에작성합니다. 여기서문제가발생합니다. Write 시스템함수에 2 번째인자로출력할메시지가저장된주소를전달해야합니다. Assembly Code 에서는데이터세그먼트를사용하지않기때문에 mov 명령어를이용해값을전달할수없습니다. 해당문제는 call, ret 명령어를사용해해당문제를해결할수있습니다. call 명령어에의해함수가호출될때 call 명령어다음명령어의주소 ( 리턴주소 ) 를스택에저장 (push) 합니다. 함수의사용이끝난후에 ret 명령어를이용해스택에저장된주소 ( 리턴주소 ) 를 eip 레지스터에저장합니다. 이러한구조에의해 RET 명령어가실행되기전에스택에저장된리턴주소를변경하면프로그램의실행흐름을변경할수있습니다. 즉, call 명령어뒤에출력할메시지를저장하면 pop 명령어를이용해 ecx 레지스터에주소값을전달할수있습니다. 메모리세그먼트를사용하지않고완전히위치독립적인방법으로코드를생성했습니다.

ASM32.s BITS 32 ; nasm 32 call helloworld db "Hello, world!", 0x0a, 0x0d ; ; mark_below call. helloworld: ; ssize_t write(int fd, const void *buf, size_t count); pop ecx ; exc. mov eax, 4 ;. mov ebx, 1 ; STDOUT mov edx, 15 ; int 0x80 ; : write(1,string, 14) ; void _exit(int status); mov eax,1 ;exit mov ebx,0 ;Status = 0 int 0x80 ; : exit(0) 다음과같이 nasm 으로빌드하면 "Hello, world!" 메시지를출력하는 shellocode 가생성됩니다. Build & Disassemble nasm ASM32.s ndisasm -b32 ASM32 00000000 E80F000000 call dword 0x14 00000005 48 dec eax 00000006 656C gs insb 00000008 6C insb 00000009 6F outsd 0000000A 2C20 sub al,0x20 0000000C 776F ja 0x7d 0000000E 726C jc 0x7c 00000010 64210A and [fs:edx],ecx 00000013 0D59B80400 or eax,0x4b859 00000018 0000 add [eax],al 0000001A BB01000000 mov ebx,0x1 0000001F BA0F000000 mov edx,0xf 00000024 CD80 int 0x80 00000026 B801000000 mov eax,0x1 0000002B BB00000000 mov ebx,0x0 00000030 CD80 int 0x80 hexdump -C ASM32 00000000 e8 0f 00 00 00 48 65 6c 6c 6f 2c 20 77 6f 72 6c...Hello, worl 00000010 64 21 0a 0d 59 b8 04 00 00 00 bb 01 00 00 00 ba d!..y... 00000020 0f 00 00 00 cd 80 b8 01 00 00 00 bb 00 00 00 00... 00000030 cd 80.. 00000032 Test 생성된 shellcode 를테스트하기위해 python 을이용해 shellcode 내용을변환합니다.

Convert output format of shellcode python Python 2.7.12 (default, Nov 20 2017, 18:23:56) [GCC 5.4.0 20160609] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> f = open('asm32','r') >>> data = f.read() >>> data '\xe8\x0f\x00\x00\x00hello, world! \n\ry\xb8\x04\x00\x00\x00\xbb\x01\x00\x00\x00\xba\x0f\x00\x00\x00\xcd\x80\xb8\x01\x00\x00\x00\xbb\x00\x00\x00\x0 0\xcd\x80' >>> 다음과같은코드를이용하여생성한 shellcode 를테스트할수있습니다. shellcode 에생성한 shellcode 를저장합니다. strlen() 함수에의해 shellcode 에저장된문자열의길이를출력합니다. strcpy() 함수에의해 shellcode 의내용이 code 영역으로복사됩니다. code 변수의형태를함수형태로형변화하여 "void (*function)()" 변수에저장합니다. function() 함수를호출하면 data 영역에저장된 shellcode 가실행됩니다. shellcode.c #include<stdio.h> #include<string.h> unsigned char shellcode [] = "\xe8\x0f\x00\x00\x00hello, world! \n\ry\xb8\x04\x00\x00\x00\xbb\x01\x00\x00\x00\xba\x0f\x00\x00\x00\xcd\x80\xb8\x01\x00\x00\x00\xbb\x00\x00\x00\x0 0\xcd\x80"; unsigned char code[]; void main(){ int len = strlen(shellcode); printf("shellcode len : %d\n",len); strcpy(code,shellcode); (*(void(*)()) code)(); } 빌드후실행하면다음과같이에러를확인할수있습니다. Build & Run gcc -o shellcode -fno-stack-protector -z execstack --no-pie -m32 shellcode.c test.c:5:15: warning: array 'code' assumed to have one element unsigned char code[]; ^./shellcode Shellcode len : 2 Segmentation fault (core dumped) 우분투 64bit 환경에서 32bit 프로그램을컴파일하기위해아래와관련라이브러리를설치해야합니다. Ubuntu 64 bit sudo apt-get install libx32gcc-5-dev libc6-dev-i386 Debugging 디버깅을통해에러의원인을확인해보겠습니다. strcpy() 함수가호출되는부분에 breakpoint 를설정하고실행합니다. strcpy() 함수가호출되기전이기때문에 code 변수영역 (0x804a074) 에는아무런값이저장되어있지않습니다. shellcode 변수영역에는앞에서작성한값이정상적으로저장되어있습니다.

debugging

gdb -q./shellcode Reading symbols from./shellcode...(no debugging symbols found)...done. gdb-peda$ disassemble main Dump of assembler code for function main: 0x0804846b <+0>: lea ecx,[esp+0x4] 0x0804846f <+4>: and esp,0xfffffff0 0x08048472 <+7>: push DWORD PTR [ecx-0x4] 0x08048475 <+10>: push ebp 0x08048476 <+11>: mov ebp,esp 0x08048478 <+13>: push ecx 0x08048479 <+14>: sub esp,0x14 0x0804847c <+17>: sub esp,0xc 0x0804847f <+20>: push 0x804a040 0x08048484 <+25>: call 0x8048340 <strlen@plt> 0x08048489 <+30>: add esp,0x10 0x0804848c <+33>: mov DWORD PTR [ebp-0xc],eax 0x0804848f <+36>: sub esp,0x8 0x08048492 <+39>: push DWORD PTR [ebp-0xc] 0x08048495 <+42>: push 0x8048550 0x0804849a <+47>: call 0x8048320 <printf@plt> 0x0804849f <+52>: add esp,0x10 0x080484a2 <+55>: sub esp,0x8 0x080484a5 <+58>: push 0x804a040 0x080484aa <+63>: push 0x804a074 0x080484af <+68>: call 0x8048330 <strcpy@plt> 0x080484b4 <+73>: add esp,0x10 0x080484b7 <+76>: mov DWORD PTR [ebp-0x10],0x804a074 0x080484be <+83>: mov eax,dword PTR [ebp-0x10] 0x080484c1 <+86>: call eax 0x080484c3 <+88>: nop 0x080484c4 <+89>: mov ecx,dword PTR [ebp-0x4] 0x080484c7 <+92>: leave 0x080484c8 <+93>: lea esp,[ecx-0x4] 0x080484cb <+96>: ret End of assembler dump. gdb-peda$ b *0x080484af Breakpoint 1 at 0x80484af gdb-peda$ r Starting program: /home/lazenca0x0/asm/shell Shellcode len : 2 Breakpoint 1, 0x080484af in main () gdb-peda$ x/64bx 0x804a074 0x804a074 <code>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804a07c: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804a084: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804a08c: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804a094: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804a09c: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804a0a4: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804a0ac: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 gdb-peda$ gdb-peda$ x/64bx 0x804a040 0x804a040 <shellcode>: 0xe8 0x0f 0x00 0x00 0x00 0x48 0x65 0x6c 0x804a048 <shellcode+8>: 0x6c 0x6f 0x2c 0x20 0x77 0x6f 0x72 0x6c 0x804a050 <shellcode+16>: 0x64 0x21 0x0a 0x0d 0x59 0xb8 0x04 0x00 0x804a058 <shellcode+24>: 0x00 0x00 0xbb 0x01 0x00 0x00 0x00 0xba 0x804a060 <shellcode+32>: 0x0f 0x00 0x00 0x00 0xcd 0x80 0xb8 0x01 0x804a068 <shellcode+40>: 0x00 0x00 0x00 0xbb 0x00 0x00 0x00 0x00 0x804a070 <shellcode+48>: 0xcd 0x80 0x00 0x00 0xe8 0x0f 0x00 0x00 0x804a078: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 gdb-peda$

그렇다면 shellcode가정상적으로실행되지않는이유는무엇일까? 그이유는 shellcode에포함되어있는 null byte(0x00) 때문입니다. 문자열을다루는함수들은전달된문자열에 null byte(0x00) 값을발견하면문자열의끝이라고판단합니다. strlen() 함수도 shellcode의길이가 2 라고출력하며, strcpy() 함수또한 null byte(0x00) 이전까지의값을 code영역에복사합니다. 즉, shellcode의내용이 code 영역에복사되지않아발생한문제입니다. 해당문제를해결하기위해 shellcode에포함된 null byte를제거해야합니다. Error point gdb-peda$ ni 0x080484b4 in main () gdb-peda$ x/32bx 0x804a074 0x804a074 <code>: 0xe8 0x0f 0x00 0x00 0x00 0x00 0x00 0x00 0x804a07c: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804a084: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804a08c: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 gdb-peda$ Remove null byte 다음코드를통해제거해야할 null byte 를확인할수있습니다. 0xE80F000000 : call dword 0x14 0xB804000000 : mov eax, 4 0xBB01000000 : mov ebx, 1 0xBA0F000000 : mov edx, 15 0xB801000000 : mov eax,1 0xBB00000000 : mov ebx,0 Null byte ndisasm -b32 ASM32 00000000 E80F000000 call dword 0x14 00000005 48 dec eax 00000006 656C gs insb 00000008 6C insb 00000009 6F outsd 0000000A 2C20 sub al,0x20 0000000C 776F ja 0x7d 0000000E 726C jc 0x7c 00000010 64210A and [fs:edx],ecx 00000013 0D59B80400 or eax,0x4b859 00000018 0000 add [eax],al 0000001A BB01000000 mov ebx,0x1 0000001F BA0F000000 mov edx,0xf 00000024 CD80 int 0x80 00000026 B801000000 mov eax,0x1 0000002B BB00000000 mov ebx,0x0 00000030 CD80 int 0x80 Call instruction call 명령어에첫번째널바이트가존재합니다. call 명령어의피연산자에서사용가능한값의크기는 dword(32bits) 입니다. "0x14" 와같이작은값이사용될경우남은부분에 null byte 가포함되게됩니다. 다음과같이코드를작성함으로써해결할수있습니다. jmp 명령어를사용해 helloworld 함수를지나 last 함수로이동합니다. last 함수에서는 helloworld 함수를호출하고, 출력할메시지를저장합니다.

ASM32-2.s BITS 32 ; nasm 32 jmp short last ;. helloworld: ; ssize_t write(int fd, const void *buf, size_t count); pop ecx ; exc. mov eax, 4 ;. mov ebx, 1 ; STDOUT mov edx, 15 ; int 0x80 ; : write(1,string, 14) ; void _exit(int status); mov eax,1 ;exit mov ebx,0 ;Status = 0 int 0x80 ; : exit(0) last: call helloworld ;. db "Hello, world!", 0x0a, 0x0d ; 다음과같이동일한 call 명령어를사용했음에도해당코드에서 null byte가제거된이유는음수때문입니다. jmp short 명령어에의해 0x20 byte로이동합니다. jmp 명령어의피연산에서사용가능한값의크기는 dword(32bits) 입니다. jmp short 명령어의피연산자에서사용가능한값의크기는 -128 ~ 127 입니다. call 명령어는 helloworld 함수를호출하기위해 -0x22(0x2-0x24) 로이동해야합니다. 여기서 -0x22를표현하기위해 2의보수로값 (0xFFFFFFDD) 을표현하게됩니다. 즉, 'jmp short', 'call 음수 ' 명령어로인해 null byte가제거되는것입니다. ndisasm -b32 ASM32-2 nasm ASM32-2.s ndisasm -b32 ASM32-2 00000000 EB1E jmp short 0x20 00000002 59 pop ecx 00000003 B804000000 mov eax,0x4 00000008 BB01000000 mov ebx,0x1 0000000D BA0F000000 mov edx,0xf 00000012 CD80 int 0x80 00000014 B801000000 mov eax,0x1 00000019 BB00000000 mov ebx,0x0 0000001E CD80 int 0x80 00000020 E8DDFFFFFF call dword 0x2 00000025 48 dec eax 00000026 656C gs insb 00000028 6C insb 00000029 6F outsd 0000002A 2C20 sub al,0x20 0000002C 776F ja 0x9d 0000002E 726C jc 0x9c 00000030 64210A and [fs:edx],ecx 00000033 0D db 0x0d Register call 명령어다음으로 null byte 가포함되어있는명령어는 Register 에값을저장하는부분입니다. Register 의크기를이해하면 null byte 가포함된이유를알수있습니다. 64 bit, 32bit, 16 bit 레지스터에표현가능한값보다작은값을저장하게되면나머지공간은 null byte 로채워지게됩니다.

Register 64 bit 32 bit 16 bit 8 bit RAX RBX RCX RDX RSP RBP RSI RDI EAX EBX ECX EDX ESP EBP ESI EDI AX BX CX DX SP BP SI DI AH/AL BH/BL CH/CL DH/DL L : 하위바이트 (Low byte),h : 상위바이트 (High byte) A null byte generated according to the size of the register Assessmbly code Machine code mov eax,0x4 B8 04 00 00 00 mov ax,0x4 66 B8 04 00 mov al,0x4 B0 04 shellcode 에서 64 bit, 32 비트레지스터영역을사용하기전에레지스터의모든영역을 0 으로변경하는것이좋습니다. shellcode 는 shellcode 가실행되기이전의레지스터의값들을그대로사용하기때문입니다. 다음과같은경우가있을수있습니다. write 시스템콜의경우인자값을전달받기위해 ebx, ecx, edx 레지스터를사용합니다. 해당사항을고려하지않고 null byte 를제거하기위해 bl, cl, dl 레지스터에값을저장하면안됩니다. 그이유는아래예제와같이 shellcode 가실행되기전에해당레지스터에어떠한값이저장되어있을수있기때문입니다. 하위레지스터에값을저장한다고해서앞에 3 바이트가 null byte 로변경되는것이아닙니다. Problem when register value is not initialized Code EBX shellcode 호출이전 - 0xdeaddead shellcode mov bl,0x4 0xdeadde04 레지스터값의초기화하기위한방법은다양하며, 그중다음과같은방법이제일효율적입니다. sub 명령어를이용한초기화 sub 명령어를이용해자기자신의레지스터의값을뺍니다. sub 명령어는쉘코드앞부분에서사용하면잘동작합니다. sub 명령어는연산결과에따라 OF, SF, ZF, AF, PF, CF flag의값이설정됩니다. 이로인해코드가예상과다르게동작할수있습니다. xor 명령어를이용해초기화 xor 명령은전달된 2개의피연산자값을배타적논리합 (exclusive OR) 을수행합니다. 배타적논리합 (exclusive OR) 은어떤값이든자신의값을연산하면 0이됩니다. OF, CF flag의값이지워지며, SF, ZF, PF flag는결과에따라결정됩니다. AF flag는정의되지않습니다. xor 명령어가 sub 명령어보다 flag에영향을덜주기때문에 xor을이용해레지스터값을초기화하는것이효율적입니다. Initialize register value Assessmbly code sub eax, eax xor eax,eax Machine code 29 C0 31 C0 앞에서설명한내용을바탕으로다음과같이코드를작성할수있습니다.

RemoveNullbyte.s BITS 32 ; nasm 32 jmp short last ;. helloworld: ; ssize_t write(int fd, const void *buf, size_t count); pop ecx ; exc. xor eax,eax ; eax 0. mov al, 4 ;. xor ebx,ebx ; ebx 0. mov bl, 1 ; STDOUT xor edx,edx ; edx 0. mov dl, 15 ; int 0x80 ; : write(1,string, 14) ; void _exit(int status); mov al,1 ;exit xor ebx,ebx ;Status = 0 int 0x80 ; : exit(0) last: call helloworld ;. db "Hello, world!", 0x0a, 0x0d ; 다음과같이 Null byte 가제거되었습니다. Removed Null byte nasm RemoveNullbyte.s ndisasm RemoveNullbyte 00000000 EB15 jmp short 0x17 00000002 59 pop cx 00000003 31C0 xor ax,ax 00000005 B004 mov al,0x4 00000007 31DB xor bx,bx 00000009 B301 mov bl,0x1 0000000B 31D2 xor dx,dx 0000000D B20F mov dl,0xf 0000000F CD80 int 0x80 00000011 B001 mov al,0x1 00000013 31DB xor bx,bx 00000015 CD80 int 0x80 00000017 E8E6FF call word 0x0 0000001A FF db 0xff 0000001B FF4865 dec word [bx+si+0x65] 0000001E 6C insb 0000001F 6C insb 00000020 6F outsw 00000021 2C20 sub al,0x20 00000023 776F ja 0x94 00000025 726C jc 0x93 00000027 64210A and [fs:bp+si],cx 0000002A 0D db 0x0d 앞에서작성한코드를아래코드를이용해테스트할수있습니다.

shellcode2.c #include<stdio.h> #include<string.h> unsigned char shellcode [] = "\xeb\x15\x59\x31\xc0\xb0\x04\x31\xdb\xb3\x01\x31\xd2\xb2\x0f\xcd\x80\xb0\x01\x31\xdb\xcd\x80\xe8\xe6\xff\xff\xf fhello, world!\n\r"; unsigned char code[] = ""; void main() { int len = strlen(shellcode); printf("shellcode len : %d\n",len); strcpy(code,shellcode); (*(void(*)()) code)(); } 다음과같이빌드후실행하면 "Hello, world!" 문자열이출력됩니다. Build & Run gcc -o shellcode2 -fno-stack-protector -z execstack --no-pie -m32 shellcode2.c./shellcode2 Shellcode len : 43 Hello, world! Related site https://syscalls.kernelgrok.com/ https://w3challs.com/syscalls/ https://en.wikibooks.org/wiki/x86_assembly/data_transfer https://en.wikipedia.org/wiki/int_(x86_instruction) https://en.wikibooks.org/wiki/x86_assembly/interfacing_with_linux https://en.wikibooks.org/wiki/x86_assembly/x86_architecture http://blog.rchapman.org/posts/linux_system_call_table_for_x86_64/ http://www.cs.utexas.edu/~bismith/test/syscalls/syscalls64_orig.html http://0xax.blogspot.jp/2014/08/say-hello-to-x64-assembly-part-1.html https://en.wikipedia.org/wiki/compiler Book : https://en.wikipedia.org/wiki/hacking:_the_art_of_exploitation Comments