SlideShare a Scribd company logo
PWNABLE BASIC #2 실전 셸 코드
목차
• 실전 셸 코드
• Windows
• Universal 셸 코드
• 배경지식
• 실습
• Port Binding 셸 코드
• 포트 열기 실습
• 파이프 열기 실습
• Reverse Connection 셸 코드
• 실습
• Linux
• Linux 환경에서 셸 코드 제작
• 실습
• Port binding 셸 코드
• 포트열기 실습
• dup2 실습
• Reverse Connection 셸 코드
• 실습
• Metasploit과 셸 코드
• 셸 코드 제작
• 셸 코드 인코딩
• 부록
• 참고 사이트
• 참고 서적
WINDOWS UNIVERSAL 셸 코드 • 배경지식
• 실습
WINDOWS UNIVERSAL 셸 코드
• 앞선 예제에서는 직접 함수 주소를 얻어온 뒤 하드코딩 했다
• 실제로는 컴퓨터를 부팅할 때마다 다른 주소를 이용하므로, 셸 코드에서 함수의 주소를 직접 알아내어 이용할 수 있도록 해야 한다.
• 여태까지는 LoadLibraryA나 GetProcAddress등의 함수를 이용해 주소를 얻어왔다
• ->이것 역시 함수이기 때문에, 셸 코드에서 이용하기 위해서는 이 함수의 주소를 얻어야 한다!
• 함수를 사용하기 위해 필요한 함수 주소를 셸 코드가 스스로 구해서 동적으로 입력해 주는 것이 Universal 셸 코드의 목적
• PE 헤더를 통해 직접 알아낸다.
WINDOWS UNIVERSAL 셸 코드
• 함수 주소를 구하는 과정
• 1) 원하는 DLL의 image base를 구함
• 2) DLL image base로부터 함수까지의 offset을 알아냄
• 함수 주소를 직접 구하기 위해 알아야 하는 것
• TEB
• PEB
• PE 헤더 – Export 관련 (Export table, Export Name Table 등)
WINDOWS UNIVERSAL 셸 코드 - DLL의 시작 주소 구하기 - 배경지식
• 배경지식
• TEB
• 쓰레드에 대한 정보를 담고 있는 구조체
• FS 레지스터를 이용하여 TEB에 접근할 수 있다
• PEB에 접근하기 위해 이용한다.
• FS:[0x30] 위치에 PEB의 주소가 있다
WINDOWS UNIVERSAL 셸 코드 - DLL의 시작 주소 구하기 - 배경지식
• 배경지식
• PEB
• 프로세스에 대한 정보를 담고 있는 구조체
• 여러 정보가 있는데, 그 중 로드된 PE(EXE, DLL 등)에 대한 정보를 이용할 것
• PEB.Ldr
• PEB_LDR_DATA 구조체의 주소를 가리키는 포인터
• PEB_LDR_DATA .InMemoryOrderModuleList
• PE image들의 데이터가 저장된 LDR_DATA_TABLE_ENTRY 구조체의 더블 링크드 리스트 시작 주소를 가리키는 포인터
• 리스트는 세 종류가 있는데, 이 중 이것을 사용했음
• LDR_DATA_TABLE_ENTRY.DllBase
• 모듈의 주소
• DLL의 image base
WINDOWS UNIVERSAL 셸 코드 - DLL의 시작 주소 구하기 - 배경지식
• 배경지식
FS : [0x30] PEB ...
+0x00c Ldr
PEB_LDR_DATA ...
+0x0014 InMemoryOrderModuleList
LDR_DATA_TABLE_ENTRY ...
+0x008 InMemoryOrderList
+0x018 DllBase
+0x02C BaseDllName
LDR_DATA_TABLE_ENTRY ...
+0x008 InMemoryOrderList
+0x018 DllBase
+0x02C BaseDllName
<EXE>
LDR_DATA_TABL_ENTRY ...
+0x008 InMemoryOrderList
+0x018 DllBase
+0x02C BaseDllName
<첫번째DLL>
<N번째DLL>
원하는 DLL의 PE 시작 주소(DOS 헤더 주소)
.......
WINDOWS UNIVERSAL 셸 코드 - DLL의 시작 주소 구하기 - 배경지식
• 배경지식
• Export
• DLL은 함수를 내보내기 위해 PE 헤더의 Export 헤더를 이용한다
• 실제로 DLL의 함수를 이용하기 위해 하는 작업과 동일한 방식으로 주소를
구하여 이용할 것
• 이용할 함수 찾는 법
• 1) 함수명 배열에서 이용하고자 하는 함수 이름과 해당 인덱스를 찾는다
• 2) Ordinals 배열에서 인덱스에 해당하는 서수 인덱스 값을 찾는다
• 3) EAT 배열에서 서수 인덱스에 해당하는 함수 offset을 확인한다
• 4) DLL base와 offset을 더해 함수의 실제 주소를 구한다
WINDOWS UNIVERSAL 셸 코드 - DLL의 시작 주소 구하기 - 배경지식
• 배경지식
• Function FuncC를 찾는 상황
OptionalHeader.
DataDirectory
+0x000 ExportTable.VirtualAddress
+0x004 ExportTable.Size
...
원하는 DLL의 PE
IMAGE_EXPORT_DERICTORY +0x01C AddressOfFunctions
+0x020 AddressOfNames
+0x024 AddressOfOrdinals
AddressOfNames RVA RVA RVA RVA
“FuncA” “FuncB” “FuncC” “FuncD”
AddressOfNameOrdinals 0 2 3 4
FuncA
코드
<index 2>
AddressOfFunctions RVA null RVA RVA RVA
FuncB
코드
FuncC
코드
FunD
코드
WINDOWS UNIVERSAL 셸 코드 - DLL의 IMAGE BASE 구하기 – 실습 1
• 실습
• DLL의 Image base 찾기
WINDOWS UNIVERSAL 셸 코드 - DLL의 IMAGE BASE 구하기 – 실습 1
• 실습
• 메모리에 올라와 있는 순서대로 접근할 수 있는 리스트를 이용했다
• 무엇이든 같은 리스트를 이용하면 됨
• 주의!
• 각 리스트는 더블 링크드 리스트
• F는 forward, B는 backward
• 각 리스트는 다음 PEB_TABLE_ENTRY의 _LIST_ENTRY 구조체를 가리킨다.
WINDOWS UNIVERSAL 셸 코드 - DLL의 IMAGE BASE 구하기 – 실습 1
• 실습
• BaseDllName은 _UNICODE_STRING 자료형이다.
• 앞 4바이트는 크기와 최대 크기를 의미하
• 실제 문자열이 있는 주소는 시작 주소 +4에 위치
• 주의!
• EBX는 _LIST_ENTRY 구조체의 주소이다.
• -> LDR_DATA_TABLE_ENTRY의 시작주소를 기준으로 +2C에 위치한 BaseDllName는 현재 EBX에 있는 값(InMemoryOrderLinks)을 기준으
로 +24에 위치한다.
• 여기서, 문자열 주소를 얻고 싶으므로 +4된 +28을 이용한 것
WINDOWS UNIVERSAL 셸 코드 - DLL의 IMAGE BASE 구하기 – 실습 1
• 실습
• 주의!
• EBX는 _LIST_ENTRY 구조체의 주소이다.
• -> LDR_DATA_TABLE_ENTRY의 시작주소를 기준으로 +18에 위치한 DllBase는
현재 EBX에 있는 값(InMemoryOrderLinks)을 기준으로 +10에 위치한다
WINDOWS UNIVERSAL 셸 코드 - DLL의 IMAGE BASE 구하기 – 실습 2
• 실습
• 앞선 예제에서는 편의를 위해 함수와 변수를 이용했다.
• 문자열 길이 구하기 함수를 이용했음
• -> 실제 셸 코드에서는 이 함수 또한 이용할 수 없다.
• 문자열을 위해 변수를 이용했음
• ->실제 셸 코드에서는 직접 문자열을 저장해가야 한다.
• 직접 구현해 이용하는 call이나 jmp는 괜찮음
• -> 상대주소이기 때문
• 조금 더 실전형으로 수정이 필요하다!
• 문자열 비교 : 직접 문자열을 비교하기보다는 hash를 이용하여 비교하는 편이 간단함
• 간단히 문자열의 아스키 코드를 전부 더하는 해시를 이용
• Ex) ABC의 hash 결과는 0x41 + 0x42 + 0x43 = 0xC6
• Integer overflow는 상관 없음 – 단순히 고유한, 같은 숫자가 나오면 됨
• -> DLL뿐만 아니라 함수를 찾을 때도 이용할 것
WINDOWS UNIVERSAL 셸 코드 - DLL의 IMAGE BASE 구하기 – 실습 2
• 실습
• Hash 이용
• 셸 코드에는 미리 구한 해시 값을 이용할 것
WINDOWS UNIVERSAL 셸 코드 - DLL의 IMAGE BASE 구하기 – 실습 2
WINDOWS UNIVERSAL 셸 코드 - DLL의 IMAGE BASE 구하기 – 실습 2
• 실습
• Hash를 이용하기
• 미리 만든 KERNEL32.DLL의 해시 값인 0x330과 비교하도록 함
WINDOWS UNIVERSAL 셸 코드 - DLL의 IMAGE BASE 구하기 – 실습 2
• 실습
• Hash를 이용하기
• FIND 함수에서, 비교하고자 하는 문자열의 hash값을 생성한 뒤
인자로 받은 0x330과 비교한다.
• 결과를 EAX에 셋팅하고 나옴
WINDOWS UNIVERSAL 셸 코드 - DLL의 IMAGE BASE 구하기 – 실습 2
• 실습
• Hash를 이용하기
• HASH_UNICODE 함수에서, EAX에 있는 유니코드 문자열의 해시 값을
구한다.
WINDOWS UNIVERSAL 셸 코드 – 함수 주소 구하기 – 실습1
• 실습
• 함수 주소 구하기
• 함수 이름을 찾을 때도 Hash 이용
• 셸 코드에는 미리 구한 해시 값을 이용할 것
• 실습
• 함수 주소 구하기
• 찾은 DLL의 image base 위에서 Export
와 관련된 PE 헤더에 접근하여 함수
주소를 얻어온다.
직접 찾은 WinExec 함수 주소
실제 WinExec 함수 주소
직접 찾은 WinExec 함수 주소
실제 WinExec 함수 주소
• 실습
• 함수 주소 구하기
• 먼저 Export Table을 찾아간다.
OptionalHeader.
DataDirectory
+0x000 ExportTable.VirtualAddress
+0x004 ExportTable.Size
...
원하는 DLL의 PE
IMAGE_EXPORT_DERICTORY +0x01C AddressOfFunctions
+0x020 AddressOfNames
+0x024 AddressOfOrdinals
• 실습
• 함수 주소 구하기
• AddressOfNames 배열에서 찾고자 하
는 함수 이름과 일치하는 함수 이름
이 배열의 몇 인덱스에 있는지 찾는
다.
원하는 DLL의 PE
IMAGE_EXPORT_DERICTORY +0x01C AddressOfFunctions
+0x020 AddressOfNames
+0x024 AddressOfOrdinals
AddressOfNames RVA RVA RVA RVA
“FuncA” “FuncB” “FuncC” “FuncD”
<index 2>
• 실습
• 함수 주소 구하기
• 얻어낸 배열 인덱스를 이용하여,
AddressOfNameOrdinals 배열에 접근
한다.
• 같은 인덱스에 있는 값(서수)을 얻는
다.
원하는 DLL의 PE
IMAGE_EXPORT_DERICTORY +0x01C AddressOfFunctions
+0x020 AddressOfNames
+0x024 AddressOfOrdinals
AddressOfNames RVA RVA RVA RVA
“FuncA” “FuncB” “FuncC” “FuncD”
AddressOfNameOrdinals 0 2 3 4
<index 2>
• 실습
• 함수 주소 구하기
• 얻은 서수를 인덱스로 하여
AddressOfFunctions 배열에 접근한다.
• 함수 주소를 얻어온다.
원하는 DLL의 PE
IMAGE_EXPORT_DERICTORY +0x01C AddressOfFunctions
+0x020 AddressOfNames
+0x024 AddressOfOrdinals
AddressOfNameOrdinals 0 2 3 4
FuncA
코드
AddressOfFunctions RVA null RVA RVA RVA
FuncB
코드
FuncC
코드
FunD
코드
WINDOWS UNIVERSAL 셸 코드 – 이전에 만든 셸 코드와 합치기 – 실습
• 실습
• 기능을 하나씩 구현했으니 앞서 만
든 셸 코드와 합쳐보자
• 맨 위에 스택 확보 코드 추가
• ---임시 부분에 오른쪽에 있는 것 중
왼쪽에 있는 코드 추가
• 맨 아래 EXPLOIT 코드 추가
WINDOWS UNIVERSAL 셸 코드 – 이전에 만든 셸 코드와 합치기
• Universal 셸 코드 성공!
• 사용할 함수가 많다면 LoadLibraryA/W 함수와 GetProcAddress 함수 주소를 얻은 뒤 이용하는 것도 좋다.
• 이 코드 역시 실제로 사용하기 위해서는 null 바이트를 없애는 과정이 필요하다
• 직접 정제해보자!
WINDOWS PORT BINDING 셸 코드 • 포트 열기 실습
• 파이프 열기 실습
WINDOWS PORT BINDING 셸 코드
• 앞에서 만든 셸코드는 단순히 cmd를 실행시키는 셸코드
• 이는 주로 취약점에 대해 공격이 가능하다고 증명하는 코드(Proof of Concept, PoC)로 사용되는 셸코드
• 실제 공격에 사용되는 셸코드는 조금 더 다양하고 강력한 기능을 수행
• 일반적으로 Exploit의 목적은 공격 대상의 시스템 권한을 획득하는 것
• 일반적으로 시스템 권한 획득은 대상 시스템의 셸 접속을 의미
• 셸 접속 : 로컬 컴퓨터가 아니라 외부에서 셸을 이용
• 셸에 접속할 수 있게 포트를 열도록 해주는 셸 코드를 포트 바인딩 셸코드 (또는 바인딩 셸코드)
라고 부름
• 특정 포트를 열고, 인증 절차 없이 공격 대상 시스템에 쉽게 접속할 수 있도록 해준다
• 백도어용으로도 이용
• 한 번 공격에 성공하면, 다음부터는 간편하게 셸에 접속할 수 있음
WINDOWS PORT BINDING 셸 코드
• 셸 코드에 추가로 필요한 기능
• 포트 열게 하기
• 포트로 들어오는 명령어를 셸에 전달하게 하기
• 출력을 포트로 전달하게 하기
대상 컴퓨터
공격자
port
cmd
stdin stdout
port
<Client>
<Server>
WINDOWS PORT BINDING 셸 코드 – 포트 열기
• 우선 어셈블리어로 TCP/IP 통신을 할 수 있는 포트를 여는 코드를 짜보자
대상 컴퓨터
공격자
port
cmd
stdin stdout
port
<Client>
<Server>
WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습
• 포트 열기
• C언어로 짤 때는 비주얼 스튜디오 설정을 이용해 라이브러리를 이용해 보자
• 프로젝트 속성 – 링커 – 입력 – 추가 종속성에 ws2_32.lib를 입력
• 셸코드에서는 LoadLibraryA/W를 이용할 것
WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습
• 포트 열기
• 서버 프로그래밍과 같다
• IP와 포트 주소를 bind
• 연결을 위해 listening -> 연결 요청이 들어오면 accept
• 오른쪽은 간단한 윈도우 소켓 프로그래밍 – 서버 이다.
• 어셈블리어로 이 코드를 짜고, 셸 코드로 만들 것
• 실제로 이용하기 위해서는 Universal 셸 코드로 만들어야 한다
• 직관적인 설명을 위해 직접 함수 주소를 이용하는 방식을 이용
WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습
• 포트 열기
• C로 짠 서버 프로그램에서는 비주얼 스튜디오를 이용하여 라이브러리를 링킹했
지만, 셸코드에서는 직접 DLL을 로딩해야 한다.
• 앞서 구한 LoadLibraryA함수 주소를 이용하여 로딩하도록 했다
• 원래는 Universal 셸코드로, 직접 함수 주소를 구해서 호출해야 함
• Ws2_32.dll을 로딩하도록 하자
WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습
• 포트 열기
• 주요 코드를 어셈블리어로 어떻게 써야할 지 보자
• WSAStartup 함수
• 리눅스와는 다르게, 소켓을 이용할 때 가장 먼저 호출한다.
• 초기화 과정
• 첫 번째 인자 : 윈도우 소켓의 버전
• 앞 바이트는 정수, 뒤 바이트는 소수를 의미
• Ex) 0x0203 = 2.3
• MAKEWORD는 매크로로, 두 숫자를 Word 형식으로 붙여준다
• 두 번째 인자 : 초기화 상태를 저장할 변수의 주소
WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습
• 포트 열기
• 저장한 초기화 정보는 굳이 사용할 일이 없으니 필요 없는 스택 주소
를 인자로 주었다.
WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습
• 포트 열기
• 주요 코드를 어셈블리어로 어떻게 써야할 지 보자
• socket 함수
• PF_INET은 2, SOCK_STREAM은 1로 define되어 있다는 점에 주목
• 함수가 반환한 값은 소켓 디스크립터이므로, 잘 저장해 두어야 한다
• 리스닝 소켓임에 주의
WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습
• 포트 열기
• EDI에 소켓 디스크립터를 백업해 두었다
WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습
• 포트 열기
• 주요 코드를 어셈블리어로 어떻게 써야할 지 보자
• 서버가 되기 위해 주소를 설정한다.
• servAddr는 sockaddr_in 구조체이다.
• 설정할 구조체 각 멤버의 위치에 주목
• sin_family에는 AF_INET(=2)를 넣을 것
• WORD 크기 멤버
• 구조체의 첫 번째 멤버
• 구조체 시작 주소
• sin_addr.s_addr에는 0을 넣을 것
• DWORD 크기 멤버
• 구조체의 세 번째 멤버
• 구조체 시작 주소 + 4h
• sin_port에는 포트번호를 넣을 것
• WORD크기 멤버
• 구조체의 두 번째 멤버
• 구조체 시작 주소 + 2h
• 빅 엔디안 값을 넣어야 함
• 직접 변환한 포트번호를 이용하자!
servAddr 주소 = ebp – 14h
WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습
• 포트 열기
• 포트번호로 사용한 12345는 16진수로
0x3039
• -> 빅엔디안으로 넣으려면 코드에
0x3930를 써야 한다
• 메모리에 저장될 때 리틀 엔디안으
로 바뀌면서 자동으로 순서가 바뀜
servAddr 주소 = ebp – 14h
WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습
• 포트 열기
• 주요 코드를 어셈블리어로 어떻게 써야할 지 보자
• bind 함수
• 소켓 디스크립터와 방금 쓴 sockaddr_in 구조체의 주소, 그리고
sockaddr_in 의 크기를 인자로 넣는다
• sockaddr_in 구조체 크기는 0x10임에 주목
WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습
• 포트 열기
• 백업해둔 소켓 디스크립터와 구조체 시작 주소를 넣었다.
• bind 이후에는 서버 주소 정보를 이용하지 않는다
• -> 백업용으로 이용하던 esi를 자유롭게 사용 가능
WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습
• 포트 열기
• 주요 코드를 어셈블리어로 어떻게 써야할 지 보자
• listen 함수
• 소켓 디스크립터와 적당한 대기 큐 개수를 넣는다.
WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습
• 포트 열기
• 백업해둔 소켓 디스크립터를 넣었다.
WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습
• 포트 열기
• 주요 코드를 어셈블리어로 어떻게 써야할 지 보자
• accept 함수
• 연결중인 클라이언트 정보 (sockaddr_in 구조체)를 저장할 공간을 고
려해야 한다
• 구조체 크기는 0x10임에 주목
• 구조체 크기는 숫자로 바로 넣을 수 없음.
• 0x10를 값으로 갖는 DWORD 크기의 메모리를 이용해야 함
• 반환되는 eax는 이번 연결에서의 실질적인 통신을 위해 사용되는 소
켓 디스크립터이다. 잘 백업해 두자.
• 연결이 끝나면 버려도 됨
WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습
• 포트 열기
• 스택을 확보하고 주소를 인자로 넘기는 부분에 주의하자.
WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습
• 포트 열기
• 오른쪽은 클라이언트 코드다. 서버가 작동하는지 간단히 실험을 해 보자.
• 간단히 netcat을 이용하는 방법도 있다
• nc ip(또는 도메인) port 형식으로 연결 요청
WINDOWS PORT BINDING 셸 코드 - 파이프
• 포트로 들어오는 입력을 cmd에 전달하고, cmd의 결과를 포트로 내보내 보자
• 파이프를 이용
• (리눅스는 dup2함수를 이용할 수 있다. 윈도우는 cmd에서 이게 잘 안 먹혀서 파이프를 이용함)
대상 컴퓨터
공격자
port
cmd
stdin stdout
port
<Client>
<Server>
WINDOWS PORT BINDING 셸 코드 - 파이프
• 파이프?
• IPC(Inter Process Communication)의 일종
• 윈도우 파이프에는 두 종류가 있음
• 이름없는 파이프와 이름있는 파이프
• 그 중 이름없는 파이프를 이용할 것
• 이름없는 파이프
• 수도 파이프와 유사
• 파이프 양쪽 끝에 구멍이 있고, 그 중 한쪽 구멍을 통해서 들어간 물체가 반대쪽 구멍으로 나올 수 있음
• 각 구멍은 하나의 용도(입력 또는 출력)로 이용 가능
• 단방향 통신
• 관계가 있는 프로세스 사이의 통신에 유용(부모-자식 관계 등)
• 자식 프로세스의 표준 입출력을 파이프로 변경할 수도 있음
WINDOWS PORT BINDING 셸 코드 – 파이프 – 실습1
• 익명 파이프 사용 예
• 파이프를 생성한 뒤, 쓰기 전용 입구에 WriteFile 함수 호출
• WriteFile은 원래 파일을 쓰는 함수
• 파일을 열어 얻는 핸들 대신 쓰기 전용 파이프를 줌
• -> 파이프에 씀
• ReadFile 함수 호출
• ReadFile은 원래 파일을 읽는 함수
• 파일 핸들 대신 읽기 전용 파이프를 줌
• -> 파이프의 내용을 읽음7500f180 7500f090
WINDOWS PORT BINDING 셸 코드 – 파이프 – 실습2
• 익명 파이프 사용 예
• 부모-자식 관계에서 파이프를 이용하여 통신하기 예
• 부모 프로세스가 ipconfig.exe의 프로세스를 생성한다
• 자식 프로세스는 원래 stdout에 출력하던 결과를 파이프로 전달한다
• 부모 프로세스가 파이프로 결과를 받아서 자신의 stdout에 출력한다
WINDOWS PORT BINDING 셸 코드 – 파이프 – 실습2
WINDOWS PORT BINDING 셸 코드 – 파이프 – 실습2
• 부모-자식 관계에서 파이프 공유하기 예
• 공유 가능한 파이프를 만들기 위한 설정
WINDOWS PORT BINDING 셸 코드 – 파이프 – 실습2
• 부모-자식 관계에서 파이프 공유하기 예
• 표준 입출력을 이와 같이 파이프로 지정할 수 있다
• 표준 입출력을 재지정하고 싶다면 WinExec 대신
CreateProcessA/W 함수를 이용해야 한다.
WINDOWS PORT BINDING 셸 코드 – 파이프 – 실습2
• 앞에서 설명한 코드의 어셈블리 코드를 보고, 셸 코드를 짜보자.
• 먼저 셸코드 보호를 위한 스택을 확보한다.
• 편의를 위해 사용할 함수 주소를 직접 찾아 이용했다
• 원래는 universal 셸 코드로, 직접 주소를 얻어와야 함
WINDOWS PORT BINDING 셸 코드 – 파이프 – 실습2
• 파이프가 상속될 수 있도록 보안 설정 구조체를 만들고, 내용을 쓴다
• 구조체 값을 넣을 때 그 위치에 주의한다.
• 구조체 크기는 0xC
• nLength의 위치는 구조체 시작주소
• 크기 : 4바이트
• lpSecurityDescriptor의 위치는 시작주소 + 4
• 크기 : 4바이트
• bInheritHandle의 위치는 시작주소 + 8
• 크기 : 4바이트
• BOOL 자료형은 int를 재정의한 것
• CreatePipe 함수에 인자로 구조체의 시작 주소를 넣어야 함에 주의한다.
• 잘 백업해 둘 것
WINDOWS PORT BINDING 셸 코드 – 파이프 – 실습2
• CreatePipe함수를 호출한다
• 방금 만든 구조체의 주소와 Read/Write 파이프 핸들을
인자로 준다
• 인자로 넣은 Read/Write 파이프 핸들은 이후 이용할 것
이므로 잘 백업해 두어야 한다
• 보안설정 구조체의 주소는 더이상 기억해 둘 필요가 없
으므로 백업해 둔 레지스터는 자유롭게 이용 가능하다
• esi 이용 가능
WINDOWS PORT BINDING 셸 코드 – 파이프 – 실습2
• CreateProcess 함수에 넣을 구조체를 설정한다
• PROCESS_INFORMATION 구조체는 실행 후 정보를 받아오는 구조체
• 따로 설정할 것 없이 대충 적당한 주소만 넣어주면 됨
• STARTUPINFO는 실행할 프로그램에 설정을 해주는 구조체
• 먼저 GetStartupInfo 함수로 내용을 채운 뒤 설정을 더한다.
• dwFlags의 위치는 시작주소 + 0x2C
• 크기 : 4바이트
• 0x101을 값으로 설정
• hStdInput의 위치는 시작주소 + 0x38
• 크기 : 4바이트
• 위에서 생성한 핸들의 값을 넣어 줌
• hStdOutput의 위치는 시작주소 + 0x3C
• 크기 : 4바이트
• 위에서 생성한 핸들의 값을 넣어 줌 (write)
• hStdError의 위치는 시작주소 + 0x40
• 크기 : 4바이트
• 위에서 생성한 핸들의 값을 넣어 줌 (write)
• wShowWindow의 위치는 시작주소 + 0x30
• 크기 : 2바이트
• hide로, 0을 설정
WINDOWS PORT BINDING 셸 코드 – 파이프 – 실습2
• CreateProcess 함수를 호출
한다
• 백업해 둔 주소들을 이용
• 실행할 프로그램 문자열을 스택
에 저장하고, 이용
• 함수 호출 뒤에는 백업이 더이상
필요하지 않으므로 백업용으로
사용하던 레지스터를 이용할 수
있게 됨
• esi, ebx 사용 가능
WINDOWS PORT BINDING 셸 코드 – 파이프 – 실습2
• 파이프에서 데이터 가져오기
• Read 핸들을 이용하여 파이프 버퍼에 있는 데이터를 가져온다
WINDOWS PORT BINDING 셸 코드 – 파이프 – 실습2
• 파이프에서 데이터 가져오기
• 문자열 읽기로 읽을 수 있도록 null을
넣어주고, printf한다.
• 문자열 읽기를 위한 포맷스트링을
만들고, push했다.
• 정상 종료를 위해 맨 마지막에
ExitProcess를 호출해주자.
WINDOWS PORT BINDING 셸 코드 – 실습
• Port binding 셸 코드
• 파이프는 2개 필요하다
• 공격자가 입력한 명령어를 cmd의 stdin에 넣는 용도
• cmd의 결과(stdout)를 공격자에게 보내는 용도
• 대상 컴퓨터측 동작
• 공격을 당해 셸 코드를 실행하는 프로그램 : A라 하자
• A가 패킷을 read -> A가 파이프1 write -> cmd가 파이프 1 read -> cmd에서 처리 -> cmd가 파이프2 write -> A가 파이프2 read -> A가 패킷을 write
대상 컴퓨터
공격자
port
cmd
stdin stdout
port
<Client>
<Server>
WINDOWS PORT BINDING 셸 코드 – 실습
• Port binding 셸 코드
• 이제 포트를 열고 – 파이프를 연결하고 – cmd를 실행하고 – 원격으로 cmd를 이용하는 코드를 짜보자.
• 셸 코드 로직은 앞으로 나올 코드와 같다.
• 어셈블리로 정제, Universal 셸 코드로 만들기, NULL 없애기 과정을 거치면 실제로 사용 가능한 셸 코드가 완성된다.
WINDOWS PORT BINDING 셸 코드 – 실습
• Port binding 셸 코드
WINDOWS PORT BINDING 셸 코드 – 실습
• Port binding 셸 코드
WINDOWS PORT BINDING 셸 코드 – 실습
• Port binding 셸 코드
• 중요 부분
• 파이프를 2개 생성해야 한다
WINDOWS PORT BINDING 셸 코드 – 실습
• Port binding 셸 코드
• 중요 부분
• PeekNamedPipe 함수는 이름있는/이름없는 파이프 버퍼를 복사하
는 기능을 한다
• 파이프 버퍼를 청소하지 않고
• ReadFile을 호출하면 데이터가 들어올 때까지 Block상태가 된다.
• ->동기 함수(blocking function)
• PeekNamedPipe는 바로 반환된다
• -> 이 함수의 결과에 따라 ReadFile을 호출하거나 하지 않으면 됨
WINDOWS REVERSE CONNECTION 셸 코드 • 실습
WINDOWS REVERSE CONNECTION 셸 코드
• 포트 바인딩 셸 코드의 단점
• Exploit을 이용한 공격 대상은 주로 서버가 되는데, 대부분의 서버 앞에는 방화벽이 있음
• 막상 포트를 열어도 접속을 하지 못하는 경우가 생김
• Reverse Connection 셸 코드
• 대부분의 방화벽은 외부에서의 접속(Inbound)은 차단하지만, 내부에서 외부로 접속(Outbound)하는 것은 차단하지 않는다
• 이 점을 이용하여, 타겟 시스템(내부망)에서 공격자의 PC(외부망)으로, 거꾸로 접속하도록 하는 셸코드가 Reverse Connection 셸 코드
• 공격자가 서버, 타겟이 클라이언트
• Connect-back 셸 코드라 하기도 한다.
WINDOWS REVERSE CONNECTION 셸 코드
• 기본적으로 Port binding 셸 코드와 비슷한 형태
• 공격자가 server고, 대상 컴퓨터가 client가 된다는 점이 다름
대상 컴퓨터
공격자
port
cmd
stdin stdout
port
<Server>
<Client>
WINDOWS REVERSE CONNECTION 셸 코드 – 실습
WINDOWS REVERSE CONNECTION 셸 코드 – 실습
• port binding 셸 코드에서 서버를 여는 부분을 삭제하고,
대신 외부 서버에 연결하는 코드를 넣었다
• 파이프 생성/cmd 실행 코드는 동일하다
• cmd 명령어를 전달하는 부분에서, accept 코드가 빠졌다.
WINDOWS REVERSE CONNECTION 셸 코드 – 실습
• nc를 이용하여 서버를 열고, 리버스 커넥션 코드를 이용하여 접속했다.
LINUX 환경에서 셸 코드 제작 • 실습
LINUX 환경에서 셸 코드 제작
• 리눅스에서 사용 가능한 셸 코드를 작성하기 위해서는
리눅스 환경에서 셸 코드를 작성할 필요가 있다.
• gcc와 objdump를 이용
• 오른쪽은 셸을 실행하는 코드를 C언어 수준에서 쓴 것
• execve로 sh 프로그램을 실행
• 메인 함수 인자로 프로그램 이름을 넣을 수 있게 함
• 32비트를 기준으로 만들 것
• gcc에 옵션으로 m32를 준다.
• 동적 링킹된 코드는 복잡하다. 함수 코드를 직접 보고
중요한 코드를 떼어올 것이므로 찾기 쉽게 정적 링킹
한다.
• gcc에 옵션으로 static을 준다.
LINUX 환경에서 셸 코드 제작
• objdump에 옵션으로 d를 주면 디스어셈블한 코드를 뽑아준다.
• grep으로 원하는 부분만 잡는다
• A 옵션을 주고 뒤에 숫자를 써 주면, 찾은 곳으로부터 숫자만큼의 instruction을
뽑아준다.
• 필요한 코드를 찾아서 다시 어셈블리어로 코딩한다.
• 필요에 따라 많은 수정이 필요
LINUX 환경에서 셸 코드 제작 – 실습
• 필요한 문자열을 스택에 저장하고, 그 주소를 넘길 수 있도
록 할 것
• 문자열을 스택에 넣는 코드를 쓰기 위해 아스키코드를 구했다.
• push를 이용하여 스택에 문자열을 넣어주고, 문자열 주소를
백업해 둔다.
LINUX 환경에서 셸 코드 제작 – 실습
• sh의 메인 함수 인자로 줄 배열이 필요함
• 문자열 주소와, NULL 이 들어간 포인터 배열이 필요
• 배열을 저장하는 곳이 스택이라는 점에 주의
• NULL을 먼저 push, 그 다음 문자열 주소를 push
• 배열 주소를 백업해 둔다.
LINUX 환경에서 셸 코드 제작 – 실습
• execve는 시스템 콜에 속한다.
• 프로그램을 로딩하기 위해서는 OS가 필요하기 때문
• 한 번 직접 들어가서 코드를 보자
• 처음 execve를 호출할 때, 함수 인자로 3가지를 push한다.
• 실행할 프로그램 문자열 주소, sh의 메인 함수 인자, NULL
• push는 역순으로 0, 메인함수 인자 주소, 문자열 주소
• execve 함수 내부에서, 다시 적절한 레지스터에 인자 값을 배치해
준다.
• edx에 환경변수(여기선 NULL), ecx에 메인함수 인자 배열 주소, ebx에 실행할 프로그램
경로 주소
• eax에 0xb를 설정하고 함수 호출
• 호출한 내부 함수에서는 인자를 push한 뒤 시스템콜을 한다.
• push는 그냥 백업용임. 그냥 int $0x80하면 됨.
• 시스템콜 번호는 이미 eax에 설정되어 있음
LINUX 환경에서 셸 코드 제작 – 실습
• execve의 핵심 코드를 알아냈으니 구태여 execve를 호출할 필요 없이,
직접 시스템 콜을 이용할 수 있음
• (라이브러리 의존성을 없앴음)
• edx에 환경변수(여기선 NULL) 설정
• ecx에 메인함수 인자 배열 주소 설정(코드 처음부터 그렇게 설정했음)
• ebx에 실행할 프로그램 경로 주소 설정(코드 처음부터 그렇게 설정했음)
LINUX 환경에서 셸 코드 제작 – 실습
• 실행해 보면 잘 작동하는 것을 확인할 수 있다.
• 이제 objdump를 이용하여 기계어를 추출, 셸코드를 제작한다.
LINUX 환경에서 셸 코드 제작 – 실습
• 만든 셸 코드를 테스트하는 코드
• gcc에 옵션으로 –z execstack 을 써준다
• DEP 해제(stack에 있는 코드 실행 방지 끔)
• Data Execution Protection
• 리눅스는 윈도우처럼 동적 라이브러리에서 함수 주소를 찾는 universal 셸
코드를 만드는 대신 바로 시스템 콜을 이용하는 경우가 많다.
• 시스템 콜을 사용하는 경우 시스템 콜의 번호와 사용 방식에만 의존성이
있고, 라이브러리에 대한 의존성은 없음
• NULL문자를 없애고, 크기를 줄이는 작업을 하면 더욱 정제할 수 있다.
LINUX PORT BINDING 셸 코드
• 포트열기
• dup2
• 이전에 만든 셸 코드와 합치기
LINUX PORT BINDING 셸 코드
• 셸 코드에 추가로 필요한 기능
• 포트 열게 하기
• 포트로 들어오는 명령어를 셸에 전달하게 하기
• 출력을 포트로 전달하게 하기
대상 컴퓨터
공격자
port
sh
stdin stdout
port
<Client>
<Server>
LINUX PORT BINDING 셸 코드 – 포트 열기 – 실습
• 오른쪽 코드는 기본적인 서버 오픈 코드이다.
• gdb를 이용하여 핵심 코드의 디스어셈블 코드를 보고, 어셈블리어로 다시 써 보자
• 본격적인 루틴 전에, 스택을 위한 코드를 추가한다.
LINUX PORT BINDING 셸 코드 – 포트 열기 – 실습
• socket, bind, listen, accept, close 함수는 전부 시스템 콜이다.
• 102번 시스템 콜인 socketcall()을 이용하면 소켓과 관련된 시스템 콜을 이용할 수 있다.
• 인자로 socket 관련 함수 중 어떤 함수를 사용할 지를 선택하고, 해당 함수의 인자들을 받는다.
• Ex) EAX에 102를 설정하고, EBX에 2를 설정하고 ECX에 인자 포인터를 넣고 시스템 콜을 하면 bind 함수를 호출
LINUX PORT BINDING 셸 코드 – 포트 열기 – 실습
• socket 함수
• SOCK_STREAM이 1로 정의되어 있고, PF_INET이 2로 정의되어
있음을 알 수 있다.
• socket함수의 반환 값인 소켓 디스크립터는 eax에 있다.
LINUX PORT BINDING 셸 코드 – 포트 열기 – 실습
• socket 함수 내부
• EBX에 1 설정 (socketcall의 타입 설정)
• ECX에 socket 인자가 들어있는 주소를 넣고, 그 주소에는 2,
1, 0(socket 인자)가 들어간다.
• EAX에 0x66 (십진수로 102)를 설정한다.
• 이 상태로 시스템 콜을 하는 함수를 호출한다
• 여기서 사용하는 push는 단순히 레지스터 값을 백업해
두는 것일 뿐임
• 다시 복원하는 코드로 추론 가능
• ->결론
• EAX에는 시스템 콜 번호인 0x66
• EBX에는 socketcall의 타입 설정
• ECX에는 주소를 넣고, 해당 주소에는 함수의 인자를 넣
는다.
LINUX PORT BINDING 셸 코드 – 포트 열기 – 실습
• socket 함수
• 필수 루틴만 빼서 어셈블리어로 썼다.
LINUX PORT BINDING 셸 코드 – 포트 열기 – 실습
• memset
• memset 호출 대신 직접 0으로 스택을 정리
하고, 서버 주소를 스택에 써 준다.
• 디스어셈블리 코드를 통해 주소 구조체의
크기가 0x10임을 알 수 있다.
LINUX PORT BINDING 셸 코드 – 포트 열기 – 실습
• 서버 주소 설정
• sin_family는 AF_INET이며 이 값은 2이고, 2바이트 크기
• sin_port는 직접 12345를 빅 엔디안으로 변환한 0x3930을 넣
으면 된다. 2바이트 크기
• sin_addr는 INADDR_ANY이며 이 값은 0이고, 4바이트 크기
• sin_addr가 가장 높은 주소, sin_family가 낮은 주소임을 주의
LINUX PORT BINDING 셸 코드 – 포트 열기 – 실습
• 서버 주소 설정
• memset과 설정을 어셈블리어로 썼다.
LINUX PORT BINDING 셸 코드 – 포트 열기 – 실습
• bind 함수
• 인자로 앞서 백업해 둔 소켓 디스크립터
와 서버 주소 구조체의 주소, 그리고 서버
주소 구조체의 크기를 준다.
• 구조체 크기는 0x10
• socket함수와 같이 시스템 콜 번호를 설정
하고, 인자 배열의 주소를 넘기면 된다.
LINUX PORT BINDING 셸 코드 – 포트 열기 – 실습
• bind 함수
• bind 코드를 어셈블리어로 썼다.
LINUX PORT BINDING 셸 코드 – 포트 열기 – 실습
• listen 함수
• 인자로 앞서 백업해 둔 소켓 디스크립터와 개수를 넣는
다.
• 같은 방식으로 시스템 콜
LINUX PORT BINDING 셸 코드 – 포트 열기 – 실습
• listen 함수
• listen 코드를 어셈블리어로 썼다.
LINUX PORT BINDING 셸 코드 – 포트 열기 – 실습
• accept 함수
• accept함수에는 구조체 크기를 저장하는 int 크기의 변수 주소를 넣어야 한다.
• 스택에 공간을 확보하고 0x10을 써 줌
• 인자로 소켓 디스크립터, 클라이언트 주소 구조체가 저장될 주소, 구조체 크기 변수 주소를 넣는다.
LINUX PORT BINDING 셸 코드 – 포트 열기 – 실습
• accept 함수
• accept 코드를 어셈블리어로 썼다.
LINUX PORT BINDING 셸 코드 – DUP2
• 포트로 들어오는 입력을 sh에 전달하고, sh의 결과를 포트로 내보내 보자
• Windows와 다르게, dup2 시스템 콜을 이용하여 간단하게 stdin과 stdout을 조정할 수 있다.
대상 컴퓨터
공격자
port
sh
stdin stdout
port
<Client>
<Server>
LINUX PORT BINDING 셸 코드 – DUP2
• dup2
• 파일 디스크립터 복사본을 만든다.
• dup()는 커널에서 알아서 사용하지 않는 디스크립터 번호 중에 하나가 자동으로 지정되지만, dup2()는 프로그래머가 원하는 번
호를 지정할 수 있다.
• dup2(fd, stdout) 할 시, 원래 stdout으로 오던 자료가 fd로 들어 감
• 시스템 콜 63번
LINUX PORT BINDING 셸 코드 – DUP2 – 실습1
• 간단히 사용법을 알아보자
• stdout의 파일 디스크립터인 1이 fd가 가리키는 파일을 가리키
도록 했다.
• 파일디스크립터 1을 이용하여 나오는 출력은 전부 파일에 쓰
이게 된다.
LINUX PORT BINDING 셸 코드 – DUP2 – 실습2
• 앞에서 쓴 서버 코드에 dup2를 추가한다.
• gdb를 이용하여 핵심 코드의 디스어셈블 코드를 보고, 어셈블리어로 다시
써 보자
LINUX PORT BINDING 셸 코드 – DUP2 – 실습2
• dup2 함수
• 바로 위에서 호출한 accept 함수는 eax에 accept하여 만든 소켓
디스크립터를 저장하고 있다.
LINUX PORT BINDING 셸 코드 – DUP2 – 실습2
• dup2 함수
• dup2는 시스템 콜이다. 직접 들어가서 중요한 코드만 떼어내어 보자.
• 인자로 넣은 수정할 fd(stdin, 0으로 정의됨)는 ecx에, 소켓 파일디스크립터는 ebx에 저장된다. eax에는 0x3f(10진수로 63)이 들어간다.
LINUX PORT BINDING 셸 코드 – DUP2 – 실습2
• dup2 함수
• 앞서 쓴 코드의 맨 끝에 이어
쓴다.
• dup2함수의 핵심 코드만 떼어
내어 썼다.
• stdin과 stdout stderr 전부 다
dup2함수 적용
• 각각 파일 디스크립터 0, 1,
2로 정의되어 있음
LINUX PORT BINDING 셸 코드 – 이전에 만든 셸 코드와 합치기
• 셸을 여는 코드를 추가한다.
• 앞에서 만든 코드를 그대로 붙여 넣음
LINUX PORT BINDING 셸 코드 – 이전에 만든 셸 코드와 합치기
• 마지막으로 셸을 여는 코드 어셈
블리어를 붙여서 완성한다.
LINUX PORT BINDING 셸 코드 – 이전에 만든 셸 코드와 합치기
• nc를 이용하여 작동됨을 확인해보고, objdump를 이용하여 기계어를
떼어내어 이용해 보자.
• 포트가 열려 있는지 확인하는 법
• netstat –nap | grep 포트번호
• 특정 포트를 사용하는 프로그램 죽이기
• fuser –k –n tcp 포트번호
LINUX REVERSE CONNECTION 셸 코드 • 실습
LINUX REVERSE CONNECTION 셸 코드
• 기본적으로 Port binding 셸 코드와 비슷한 형태
• 공격자가 server고, 대상 컴퓨터가 client가 된다는 점이 다름
• port binding에서 서버를 여는 코드를 서버에 접속하는 코드로 변경하면 된다.
대상 컴퓨터
공격자
port
sh
stdin stdout
port
<Server>
<Client>
LINUX REVERSE CONNECTION 셸 코드 - 실습
• 연결할 서버 주소 설정에 IP 하드코딩
• connect 코드 추가
LINUX REVERSE CONNECTION 셸 코드 - 실습
• nc로 서버를 열어 둔 뒤 reverse connection 프로그램을 실행하면 서버 측에서 셸을 이용할 수 있음
METASPLOIT과 셸 코드 • 셸 코드 제작
• 셸 코드 인코딩
METASPLOIT FRAMEWORK
• Metasploit이란?
• 침투 테스트를 위한 Framework
• 간단히 명령어를 조합하여 편하게 해킹이 가능하게 함
• 이미 만들어져 있는 모듈을 이용
• 특정 취약점에 대한 공격코드(페이로드), 셸 코드 등 제공
• Metasploit에서 사용 가능한 형태로 직접 모듈을 만들어 넣어두면 후에 편하게 재이용 가능
• 칼리 리눅스에 기본으로 설치되어 있음
• 다른 리눅스나 윈도우에도 설치 가능
• 이번에는 간단하게 셸 코드 제작과 인코딩에 관련된 내용만 알아볼 것
METASPLOIT FRAMEWORK를 이용한 셸 코드 제작
• Metasploit Framework를 이용한 셸 코드 제작
• 직접 제작은 아니고, 이미 만들어진 셸 코드 중 원하는 기능을 가진 셸 코드를 얻는 방법이다
• msfvenom을 이용
• 옛날 버전의 msfpayload와 msfencode가 합쳐진 것. 최신 버전의 Metasploit Framework를 설치하면 있다.
• 옛날 책에서 위의 프로그램을 이용한다면, 그 대신 msfvenom을 이용해야 한다.
• 각종 옵션을 설정하여 원하는 형태의 셸 코드를 얻을 수 있다.
METASPLOIT FRAMEWORK를 이용한 셸 코드 제작
• Metasploit Framework를 이용한 셸 코드 제작
• msfvenom –l 명령어를 입력하면 사용할 수 있는 페이로
드들의 리스트가 나온다
• 경로와 설명
• 이 중 원하는 것을 선택한다
• 앞에서 만들었던 Windows shell port binding 셸 코드
를 이용해 보자
• 칼리리눅스에는 기본 경로가 잡혀 있기 때문에 명령어
로 msfvenom 프로그램을 실행할 수 있다.
METASPLOIT FRAMEWORK를 이용한 셸 코드 제작
• Metasploit Framework를 이용한 셸 코드 제작
• msfvenom –p 로 payload를 설정한다
• 그 다음 줄에 위에서 찾은 경로를 적어준다
• 페이로드의 옵션으로 포트를 설정했다.
• -f로 format을 설정한다.
• 뒤에 c를 적음 : c언어에서 바로 이용할 수 있는 셸 코드
• python을 적으면 python 에서 바로 쓸 수 있는 형태로
나오고..
• 언어/실행파일 종류는 밑과 같음
• 복사해도 되지만, 보통 맨 뒤에 >code.c 등을 붙여 코드를
파일로 저장
METASPLOIT FRAMEWORK를 이용한 셸 코드 제작
• Metasploit Framework를 이용한 셸 코드 제작
• 위에서 만든 셸 코드를 실행해보자
• 사실 잘 안되는 경우도 많다
• OS 버전이나, 사소한 환경 등의 차이로 인해
• 애초에 잘못된 코드가 있을 가능성도 있음
METASPLOIT FRAMEWORK를 이용한 셸 코드 제작
• Windows에서도 같은 방법으로
Metasploit을 이용할 수 있다
• 오른쪽은 Windows power shell에
서 msfvenom을 실행한 것
METASPLOIT FRAMEWORK를 이용한 셸 코드 인코딩
• Metasploit Framework를 이용한 셸 코드 인코딩
• 셸 코드에 특정한 문자가 들어가지 않기를 원할 때, 인코딩을 이용할
수 있다
• null문자 없애기에 유용하게 이용할 수 있다
• 셸 코드에서 제거되어야 하는 문자를 bad char라고 부름
• Metasploit은 여러 인코더를 제공하기 때문에, 원하는 상황에 맞춰
골라 쓸 수 있다.
• null 문자를 없애는 용도 뿐만 아니라 많은 용도로 이용된다
• Ex) 백신 우회, 크기 줄이기 등
METASPLOIT FRAMEWORK를 이용한 셸 코드 인코딩
• Metasploit Framework를 이용한 셸 코드 인코딩
• -b 옵션을 이용해 bad char를 설정했다
• 셸 코드가 0x00과 0xff가 없도록 인코딩 되었다
METASPLOIT FRAMEWORK를 이용한 셸 코드 인코딩
• Metasploit Framework를 이용한 셸 코드 인코딩
• -e 옵션을 이용해 인코더를 지정했다
• fnstenv_mov 인코더는 부동소수점을 위한 명령어를 이용하도록
인코딩한다
부록 • 참고 사이트
• 참고 서적
참고 사이트
• Windows 환경에서의 Buffer Overflow 공격 기법
• http://research.hackerschool.org/data/WBOF/WBF.htm
• http://dest.kr/c-c/windows-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C%EC%9D%98-buffer-overflow-
%EA%B3%B5%EA%B2%A9-%EA%B8%B0%EB%B2%95/
• PeekNamedPipe 함수와 ReadFile
• http://tksssch29.tistory.com/entry/cmdexe-%EC%99%80-pipe%EC%99%80%EC%9D%98-%ED%86%B5%EC%8B%A0
• Exploit Database – 미리 만들어진 셸 코드들
• https://www.exploit-db.com/shellcode/
• Metasploit 설치
• https://github.com/rapid7/metasploit-framework/wiki/Nightly-Installers
• msfvenom 기본 사용 정리
• http://blog.naver.com/PostView.nhn?blogId=knq1130&logNo=220621445199
참고 사이트
• dup2 함수
• http://forum.falinux.com/zbxe/index.php?document_srl=520937&mid=C_LIB
• http://sosal.kr/186
참고 서적
• 뇌를 자극하는 윈도우즈 시스템 프로그래밍
• Chapter 8 프로세스간 통신 2 _ 파이프 방식의 IPC
• 윈도우 시스템 해킹 가이드
• Part 3 쉘코드 원리와 작성

More Related Content

PPTX
Windows reversing study_basic_4
 
PPTX
Windows reversing study_basic_3
 
PPTX
System+os study 1
 
PPTX
Pwnable study basic_1
 
PPTX
Windows reversing study_basic_1
 
PPTX
Windows reversing study_basic_9
 
PPTX
Assembly 스터디 1
 
PPTX
Linux reversing study_basic_3
 
Windows reversing study_basic_4
 
Windows reversing study_basic_3
 
System+os study 1
 
Pwnable study basic_1
 
Windows reversing study_basic_1
 
Windows reversing study_basic_9
 
Assembly 스터디 1
 
Linux reversing study_basic_3
 

What's hot (20)

PPTX
System+os study 5
 
PPTX
Windows reversing study_basic_5
 
PPTX
System+os study 4
 
PPTX
Pwnable study basic_3
 
PPTX
Windows reversing study_basic_7
 
PPTX
System+os study 3
 
PPTX
Linux reversing study_basic_2
 
PPTX
Assembly 스터디 2
 
PPTX
System+os study 6
 
PPTX
Windows reversing study_basic_8
 
PPTX
Web hacking introduction
 
PPTX
Linux reversing study_basic_1
 
PPTX
Linux reversing study_basic_4
 
PPTX
Windows reversing study_basic_6
 
PPTX
System+os study 7
 
PPTX
Python
 
PPTX
Windows reversing study_basic_2
 
PDF
[Kerference] 시작! 리버싱 - 김종범(KERT)
PDF
Apache avro
PDF
Avro 조사
System+os study 5
 
Windows reversing study_basic_5
 
System+os study 4
 
Pwnable study basic_3
 
Windows reversing study_basic_7
 
System+os study 3
 
Linux reversing study_basic_2
 
Assembly 스터디 2
 
System+os study 6
 
Windows reversing study_basic_8
 
Web hacking introduction
 
Linux reversing study_basic_1
 
Linux reversing study_basic_4
 
Windows reversing study_basic_6
 
System+os study 7
 
Python
 
Windows reversing study_basic_2
 
[Kerference] 시작! 리버싱 - 김종범(KERT)
Apache avro
Avro 조사
Ad

Similar to Pwnable study basic_2 (20)

PPT
2006 03 15_pe & api hook
KEY
Mongodb tip42 50
PDF
Node.js 기본
PDF
Node.js at OKJSP
PDF
해커가 되고 싶은 자는 나에게... 정보보안 입문과 길 찾기
 
PPT
Java script
PDF
[16]Obfuscation 101 : 난독화, 프로가드, R8, 트랜스포머 API
PDF
CoreDot TechSeminar 2018 - Session1 Park Jihun
PDF
Node.js 20버전에 변경된 점들.pdf
PDF
XECon2015 :: [1-5] 김훈민 - 서버 운영자가 꼭 알아야 할 Docker
PDF
인프콘 2022 - Rust 크로스 플랫폼 프로그래밍
PPTX
19_DLL의 기본.pptx
PDF
2장 리눅스의 기초
PDF
[2009 CodeEngn Conference 03] hkpco - DEFCON CTF 2009 Binary Leetness 100-500...
PDF
셸 스크립트를 이용한 클라우드 시스템 운영
PPTX
Windows via C/C++ 06 스레드의 기본
PDF
[아꿈사/111105] html5 9장 클라이언트측 데이터로 작업하기
PDF
Codeveloper 개발기
PDF
[네이버오픈소스세미나] Contribution, 전쟁의 서막 : Apache OpenWhisk 성능 개선 - 김동경
PDF
XECon+PHPFest2014 발표자료 - ElasticSearch를 이용한 통합검색 구축방법 - 김훈민
2006 03 15_pe & api hook
Mongodb tip42 50
Node.js 기본
Node.js at OKJSP
해커가 되고 싶은 자는 나에게... 정보보안 입문과 길 찾기
 
Java script
[16]Obfuscation 101 : 난독화, 프로가드, R8, 트랜스포머 API
CoreDot TechSeminar 2018 - Session1 Park Jihun
Node.js 20버전에 변경된 점들.pdf
XECon2015 :: [1-5] 김훈민 - 서버 운영자가 꼭 알아야 할 Docker
인프콘 2022 - Rust 크로스 플랫폼 프로그래밍
19_DLL의 기본.pptx
2장 리눅스의 기초
[2009 CodeEngn Conference 03] hkpco - DEFCON CTF 2009 Binary Leetness 100-500...
셸 스크립트를 이용한 클라우드 시스템 운영
Windows via C/C++ 06 스레드의 기본
[아꿈사/111105] html5 9장 클라이언트측 데이터로 작업하기
Codeveloper 개발기
[네이버오픈소스세미나] Contribution, 전쟁의 서막 : Apache OpenWhisk 성능 개선 - 김동경
XECon+PHPFest2014 발표자료 - ElasticSearch를 이용한 통합검색 구축방법 - 김훈민
Ad

Pwnable study basic_2

  • 1. PWNABLE BASIC #2 실전 셸 코드
  • 2. 목차 • 실전 셸 코드 • Windows • Universal 셸 코드 • 배경지식 • 실습 • Port Binding 셸 코드 • 포트 열기 실습 • 파이프 열기 실습 • Reverse Connection 셸 코드 • 실습 • Linux • Linux 환경에서 셸 코드 제작 • 실습 • Port binding 셸 코드 • 포트열기 실습 • dup2 실습 • Reverse Connection 셸 코드 • 실습 • Metasploit과 셸 코드 • 셸 코드 제작 • 셸 코드 인코딩 • 부록 • 참고 사이트 • 참고 서적
  • 3. WINDOWS UNIVERSAL 셸 코드 • 배경지식 • 실습
  • 4. WINDOWS UNIVERSAL 셸 코드 • 앞선 예제에서는 직접 함수 주소를 얻어온 뒤 하드코딩 했다 • 실제로는 컴퓨터를 부팅할 때마다 다른 주소를 이용하므로, 셸 코드에서 함수의 주소를 직접 알아내어 이용할 수 있도록 해야 한다. • 여태까지는 LoadLibraryA나 GetProcAddress등의 함수를 이용해 주소를 얻어왔다 • ->이것 역시 함수이기 때문에, 셸 코드에서 이용하기 위해서는 이 함수의 주소를 얻어야 한다! • 함수를 사용하기 위해 필요한 함수 주소를 셸 코드가 스스로 구해서 동적으로 입력해 주는 것이 Universal 셸 코드의 목적 • PE 헤더를 통해 직접 알아낸다.
  • 5. WINDOWS UNIVERSAL 셸 코드 • 함수 주소를 구하는 과정 • 1) 원하는 DLL의 image base를 구함 • 2) DLL image base로부터 함수까지의 offset을 알아냄 • 함수 주소를 직접 구하기 위해 알아야 하는 것 • TEB • PEB • PE 헤더 – Export 관련 (Export table, Export Name Table 등)
  • 6. WINDOWS UNIVERSAL 셸 코드 - DLL의 시작 주소 구하기 - 배경지식 • 배경지식 • TEB • 쓰레드에 대한 정보를 담고 있는 구조체 • FS 레지스터를 이용하여 TEB에 접근할 수 있다 • PEB에 접근하기 위해 이용한다. • FS:[0x30] 위치에 PEB의 주소가 있다
  • 7. WINDOWS UNIVERSAL 셸 코드 - DLL의 시작 주소 구하기 - 배경지식 • 배경지식 • PEB • 프로세스에 대한 정보를 담고 있는 구조체 • 여러 정보가 있는데, 그 중 로드된 PE(EXE, DLL 등)에 대한 정보를 이용할 것 • PEB.Ldr • PEB_LDR_DATA 구조체의 주소를 가리키는 포인터 • PEB_LDR_DATA .InMemoryOrderModuleList • PE image들의 데이터가 저장된 LDR_DATA_TABLE_ENTRY 구조체의 더블 링크드 리스트 시작 주소를 가리키는 포인터 • 리스트는 세 종류가 있는데, 이 중 이것을 사용했음 • LDR_DATA_TABLE_ENTRY.DllBase • 모듈의 주소 • DLL의 image base
  • 8. WINDOWS UNIVERSAL 셸 코드 - DLL의 시작 주소 구하기 - 배경지식 • 배경지식 FS : [0x30] PEB ... +0x00c Ldr PEB_LDR_DATA ... +0x0014 InMemoryOrderModuleList LDR_DATA_TABLE_ENTRY ... +0x008 InMemoryOrderList +0x018 DllBase +0x02C BaseDllName LDR_DATA_TABLE_ENTRY ... +0x008 InMemoryOrderList +0x018 DllBase +0x02C BaseDllName <EXE> LDR_DATA_TABL_ENTRY ... +0x008 InMemoryOrderList +0x018 DllBase +0x02C BaseDllName <첫번째DLL> <N번째DLL> 원하는 DLL의 PE 시작 주소(DOS 헤더 주소) .......
  • 9. WINDOWS UNIVERSAL 셸 코드 - DLL의 시작 주소 구하기 - 배경지식 • 배경지식 • Export • DLL은 함수를 내보내기 위해 PE 헤더의 Export 헤더를 이용한다 • 실제로 DLL의 함수를 이용하기 위해 하는 작업과 동일한 방식으로 주소를 구하여 이용할 것 • 이용할 함수 찾는 법 • 1) 함수명 배열에서 이용하고자 하는 함수 이름과 해당 인덱스를 찾는다 • 2) Ordinals 배열에서 인덱스에 해당하는 서수 인덱스 값을 찾는다 • 3) EAT 배열에서 서수 인덱스에 해당하는 함수 offset을 확인한다 • 4) DLL base와 offset을 더해 함수의 실제 주소를 구한다
  • 10. WINDOWS UNIVERSAL 셸 코드 - DLL의 시작 주소 구하기 - 배경지식 • 배경지식 • Function FuncC를 찾는 상황 OptionalHeader. DataDirectory +0x000 ExportTable.VirtualAddress +0x004 ExportTable.Size ... 원하는 DLL의 PE IMAGE_EXPORT_DERICTORY +0x01C AddressOfFunctions +0x020 AddressOfNames +0x024 AddressOfOrdinals AddressOfNames RVA RVA RVA RVA “FuncA” “FuncB” “FuncC” “FuncD” AddressOfNameOrdinals 0 2 3 4 FuncA 코드 <index 2> AddressOfFunctions RVA null RVA RVA RVA FuncB 코드 FuncC 코드 FunD 코드
  • 11. WINDOWS UNIVERSAL 셸 코드 - DLL의 IMAGE BASE 구하기 – 실습 1 • 실습 • DLL의 Image base 찾기
  • 12. WINDOWS UNIVERSAL 셸 코드 - DLL의 IMAGE BASE 구하기 – 실습 1 • 실습 • 메모리에 올라와 있는 순서대로 접근할 수 있는 리스트를 이용했다 • 무엇이든 같은 리스트를 이용하면 됨 • 주의! • 각 리스트는 더블 링크드 리스트 • F는 forward, B는 backward • 각 리스트는 다음 PEB_TABLE_ENTRY의 _LIST_ENTRY 구조체를 가리킨다.
  • 13. WINDOWS UNIVERSAL 셸 코드 - DLL의 IMAGE BASE 구하기 – 실습 1 • 실습 • BaseDllName은 _UNICODE_STRING 자료형이다. • 앞 4바이트는 크기와 최대 크기를 의미하 • 실제 문자열이 있는 주소는 시작 주소 +4에 위치 • 주의! • EBX는 _LIST_ENTRY 구조체의 주소이다. • -> LDR_DATA_TABLE_ENTRY의 시작주소를 기준으로 +2C에 위치한 BaseDllName는 현재 EBX에 있는 값(InMemoryOrderLinks)을 기준으 로 +24에 위치한다. • 여기서, 문자열 주소를 얻고 싶으므로 +4된 +28을 이용한 것
  • 14. WINDOWS UNIVERSAL 셸 코드 - DLL의 IMAGE BASE 구하기 – 실습 1 • 실습 • 주의! • EBX는 _LIST_ENTRY 구조체의 주소이다. • -> LDR_DATA_TABLE_ENTRY의 시작주소를 기준으로 +18에 위치한 DllBase는 현재 EBX에 있는 값(InMemoryOrderLinks)을 기준으로 +10에 위치한다
  • 15. WINDOWS UNIVERSAL 셸 코드 - DLL의 IMAGE BASE 구하기 – 실습 2 • 실습 • 앞선 예제에서는 편의를 위해 함수와 변수를 이용했다. • 문자열 길이 구하기 함수를 이용했음 • -> 실제 셸 코드에서는 이 함수 또한 이용할 수 없다. • 문자열을 위해 변수를 이용했음 • ->실제 셸 코드에서는 직접 문자열을 저장해가야 한다. • 직접 구현해 이용하는 call이나 jmp는 괜찮음 • -> 상대주소이기 때문 • 조금 더 실전형으로 수정이 필요하다! • 문자열 비교 : 직접 문자열을 비교하기보다는 hash를 이용하여 비교하는 편이 간단함 • 간단히 문자열의 아스키 코드를 전부 더하는 해시를 이용 • Ex) ABC의 hash 결과는 0x41 + 0x42 + 0x43 = 0xC6 • Integer overflow는 상관 없음 – 단순히 고유한, 같은 숫자가 나오면 됨 • -> DLL뿐만 아니라 함수를 찾을 때도 이용할 것
  • 16. WINDOWS UNIVERSAL 셸 코드 - DLL의 IMAGE BASE 구하기 – 실습 2 • 실습 • Hash 이용 • 셸 코드에는 미리 구한 해시 값을 이용할 것
  • 17. WINDOWS UNIVERSAL 셸 코드 - DLL의 IMAGE BASE 구하기 – 실습 2
  • 18. WINDOWS UNIVERSAL 셸 코드 - DLL의 IMAGE BASE 구하기 – 실습 2 • 실습 • Hash를 이용하기 • 미리 만든 KERNEL32.DLL의 해시 값인 0x330과 비교하도록 함
  • 19. WINDOWS UNIVERSAL 셸 코드 - DLL의 IMAGE BASE 구하기 – 실습 2 • 실습 • Hash를 이용하기 • FIND 함수에서, 비교하고자 하는 문자열의 hash값을 생성한 뒤 인자로 받은 0x330과 비교한다. • 결과를 EAX에 셋팅하고 나옴
  • 20. WINDOWS UNIVERSAL 셸 코드 - DLL의 IMAGE BASE 구하기 – 실습 2 • 실습 • Hash를 이용하기 • HASH_UNICODE 함수에서, EAX에 있는 유니코드 문자열의 해시 값을 구한다.
  • 21. WINDOWS UNIVERSAL 셸 코드 – 함수 주소 구하기 – 실습1 • 실습 • 함수 주소 구하기 • 함수 이름을 찾을 때도 Hash 이용 • 셸 코드에는 미리 구한 해시 값을 이용할 것
  • 22. • 실습 • 함수 주소 구하기 • 찾은 DLL의 image base 위에서 Export 와 관련된 PE 헤더에 접근하여 함수 주소를 얻어온다. 직접 찾은 WinExec 함수 주소 실제 WinExec 함수 주소
  • 23. 직접 찾은 WinExec 함수 주소 실제 WinExec 함수 주소
  • 24. • 실습 • 함수 주소 구하기 • 먼저 Export Table을 찾아간다. OptionalHeader. DataDirectory +0x000 ExportTable.VirtualAddress +0x004 ExportTable.Size ... 원하는 DLL의 PE IMAGE_EXPORT_DERICTORY +0x01C AddressOfFunctions +0x020 AddressOfNames +0x024 AddressOfOrdinals
  • 25. • 실습 • 함수 주소 구하기 • AddressOfNames 배열에서 찾고자 하 는 함수 이름과 일치하는 함수 이름 이 배열의 몇 인덱스에 있는지 찾는 다. 원하는 DLL의 PE IMAGE_EXPORT_DERICTORY +0x01C AddressOfFunctions +0x020 AddressOfNames +0x024 AddressOfOrdinals AddressOfNames RVA RVA RVA RVA “FuncA” “FuncB” “FuncC” “FuncD” <index 2>
  • 26. • 실습 • 함수 주소 구하기 • 얻어낸 배열 인덱스를 이용하여, AddressOfNameOrdinals 배열에 접근 한다. • 같은 인덱스에 있는 값(서수)을 얻는 다. 원하는 DLL의 PE IMAGE_EXPORT_DERICTORY +0x01C AddressOfFunctions +0x020 AddressOfNames +0x024 AddressOfOrdinals AddressOfNames RVA RVA RVA RVA “FuncA” “FuncB” “FuncC” “FuncD” AddressOfNameOrdinals 0 2 3 4 <index 2>
  • 27. • 실습 • 함수 주소 구하기 • 얻은 서수를 인덱스로 하여 AddressOfFunctions 배열에 접근한다. • 함수 주소를 얻어온다. 원하는 DLL의 PE IMAGE_EXPORT_DERICTORY +0x01C AddressOfFunctions +0x020 AddressOfNames +0x024 AddressOfOrdinals AddressOfNameOrdinals 0 2 3 4 FuncA 코드 AddressOfFunctions RVA null RVA RVA RVA FuncB 코드 FuncC 코드 FunD 코드
  • 28. WINDOWS UNIVERSAL 셸 코드 – 이전에 만든 셸 코드와 합치기 – 실습 • 실습 • 기능을 하나씩 구현했으니 앞서 만 든 셸 코드와 합쳐보자 • 맨 위에 스택 확보 코드 추가 • ---임시 부분에 오른쪽에 있는 것 중 왼쪽에 있는 코드 추가 • 맨 아래 EXPLOIT 코드 추가
  • 29. WINDOWS UNIVERSAL 셸 코드 – 이전에 만든 셸 코드와 합치기 • Universal 셸 코드 성공! • 사용할 함수가 많다면 LoadLibraryA/W 함수와 GetProcAddress 함수 주소를 얻은 뒤 이용하는 것도 좋다. • 이 코드 역시 실제로 사용하기 위해서는 null 바이트를 없애는 과정이 필요하다 • 직접 정제해보자!
  • 30. WINDOWS PORT BINDING 셸 코드 • 포트 열기 실습 • 파이프 열기 실습
  • 31. WINDOWS PORT BINDING 셸 코드 • 앞에서 만든 셸코드는 단순히 cmd를 실행시키는 셸코드 • 이는 주로 취약점에 대해 공격이 가능하다고 증명하는 코드(Proof of Concept, PoC)로 사용되는 셸코드 • 실제 공격에 사용되는 셸코드는 조금 더 다양하고 강력한 기능을 수행 • 일반적으로 Exploit의 목적은 공격 대상의 시스템 권한을 획득하는 것 • 일반적으로 시스템 권한 획득은 대상 시스템의 셸 접속을 의미 • 셸 접속 : 로컬 컴퓨터가 아니라 외부에서 셸을 이용 • 셸에 접속할 수 있게 포트를 열도록 해주는 셸 코드를 포트 바인딩 셸코드 (또는 바인딩 셸코드) 라고 부름 • 특정 포트를 열고, 인증 절차 없이 공격 대상 시스템에 쉽게 접속할 수 있도록 해준다 • 백도어용으로도 이용 • 한 번 공격에 성공하면, 다음부터는 간편하게 셸에 접속할 수 있음
  • 32. WINDOWS PORT BINDING 셸 코드 • 셸 코드에 추가로 필요한 기능 • 포트 열게 하기 • 포트로 들어오는 명령어를 셸에 전달하게 하기 • 출력을 포트로 전달하게 하기 대상 컴퓨터 공격자 port cmd stdin stdout port <Client> <Server>
  • 33. WINDOWS PORT BINDING 셸 코드 – 포트 열기 • 우선 어셈블리어로 TCP/IP 통신을 할 수 있는 포트를 여는 코드를 짜보자 대상 컴퓨터 공격자 port cmd stdin stdout port <Client> <Server>
  • 34. WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습 • 포트 열기 • C언어로 짤 때는 비주얼 스튜디오 설정을 이용해 라이브러리를 이용해 보자 • 프로젝트 속성 – 링커 – 입력 – 추가 종속성에 ws2_32.lib를 입력 • 셸코드에서는 LoadLibraryA/W를 이용할 것
  • 35. WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습 • 포트 열기 • 서버 프로그래밍과 같다 • IP와 포트 주소를 bind • 연결을 위해 listening -> 연결 요청이 들어오면 accept • 오른쪽은 간단한 윈도우 소켓 프로그래밍 – 서버 이다. • 어셈블리어로 이 코드를 짜고, 셸 코드로 만들 것 • 실제로 이용하기 위해서는 Universal 셸 코드로 만들어야 한다 • 직관적인 설명을 위해 직접 함수 주소를 이용하는 방식을 이용
  • 36. WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습 • 포트 열기 • C로 짠 서버 프로그램에서는 비주얼 스튜디오를 이용하여 라이브러리를 링킹했 지만, 셸코드에서는 직접 DLL을 로딩해야 한다. • 앞서 구한 LoadLibraryA함수 주소를 이용하여 로딩하도록 했다 • 원래는 Universal 셸코드로, 직접 함수 주소를 구해서 호출해야 함 • Ws2_32.dll을 로딩하도록 하자
  • 37. WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습 • 포트 열기 • 주요 코드를 어셈블리어로 어떻게 써야할 지 보자 • WSAStartup 함수 • 리눅스와는 다르게, 소켓을 이용할 때 가장 먼저 호출한다. • 초기화 과정 • 첫 번째 인자 : 윈도우 소켓의 버전 • 앞 바이트는 정수, 뒤 바이트는 소수를 의미 • Ex) 0x0203 = 2.3 • MAKEWORD는 매크로로, 두 숫자를 Word 형식으로 붙여준다 • 두 번째 인자 : 초기화 상태를 저장할 변수의 주소
  • 38. WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습 • 포트 열기 • 저장한 초기화 정보는 굳이 사용할 일이 없으니 필요 없는 스택 주소 를 인자로 주었다.
  • 39. WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습 • 포트 열기 • 주요 코드를 어셈블리어로 어떻게 써야할 지 보자 • socket 함수 • PF_INET은 2, SOCK_STREAM은 1로 define되어 있다는 점에 주목 • 함수가 반환한 값은 소켓 디스크립터이므로, 잘 저장해 두어야 한다 • 리스닝 소켓임에 주의
  • 40. WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습 • 포트 열기 • EDI에 소켓 디스크립터를 백업해 두었다
  • 41. WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습 • 포트 열기 • 주요 코드를 어셈블리어로 어떻게 써야할 지 보자 • 서버가 되기 위해 주소를 설정한다. • servAddr는 sockaddr_in 구조체이다. • 설정할 구조체 각 멤버의 위치에 주목 • sin_family에는 AF_INET(=2)를 넣을 것 • WORD 크기 멤버 • 구조체의 첫 번째 멤버 • 구조체 시작 주소 • sin_addr.s_addr에는 0을 넣을 것 • DWORD 크기 멤버 • 구조체의 세 번째 멤버 • 구조체 시작 주소 + 4h • sin_port에는 포트번호를 넣을 것 • WORD크기 멤버 • 구조체의 두 번째 멤버 • 구조체 시작 주소 + 2h • 빅 엔디안 값을 넣어야 함 • 직접 변환한 포트번호를 이용하자! servAddr 주소 = ebp – 14h
  • 42. WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습 • 포트 열기 • 포트번호로 사용한 12345는 16진수로 0x3039 • -> 빅엔디안으로 넣으려면 코드에 0x3930를 써야 한다 • 메모리에 저장될 때 리틀 엔디안으 로 바뀌면서 자동으로 순서가 바뀜 servAddr 주소 = ebp – 14h
  • 43. WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습 • 포트 열기 • 주요 코드를 어셈블리어로 어떻게 써야할 지 보자 • bind 함수 • 소켓 디스크립터와 방금 쓴 sockaddr_in 구조체의 주소, 그리고 sockaddr_in 의 크기를 인자로 넣는다 • sockaddr_in 구조체 크기는 0x10임에 주목
  • 44. WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습 • 포트 열기 • 백업해둔 소켓 디스크립터와 구조체 시작 주소를 넣었다. • bind 이후에는 서버 주소 정보를 이용하지 않는다 • -> 백업용으로 이용하던 esi를 자유롭게 사용 가능
  • 45. WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습 • 포트 열기 • 주요 코드를 어셈블리어로 어떻게 써야할 지 보자 • listen 함수 • 소켓 디스크립터와 적당한 대기 큐 개수를 넣는다.
  • 46. WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습 • 포트 열기 • 백업해둔 소켓 디스크립터를 넣었다.
  • 47. WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습 • 포트 열기 • 주요 코드를 어셈블리어로 어떻게 써야할 지 보자 • accept 함수 • 연결중인 클라이언트 정보 (sockaddr_in 구조체)를 저장할 공간을 고 려해야 한다 • 구조체 크기는 0x10임에 주목 • 구조체 크기는 숫자로 바로 넣을 수 없음. • 0x10를 값으로 갖는 DWORD 크기의 메모리를 이용해야 함 • 반환되는 eax는 이번 연결에서의 실질적인 통신을 위해 사용되는 소 켓 디스크립터이다. 잘 백업해 두자. • 연결이 끝나면 버려도 됨
  • 48. WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습 • 포트 열기 • 스택을 확보하고 주소를 인자로 넘기는 부분에 주의하자.
  • 49. WINDOWS PORT BINDING 셸 코드 – 포트 열기 – 실습 • 포트 열기 • 오른쪽은 클라이언트 코드다. 서버가 작동하는지 간단히 실험을 해 보자. • 간단히 netcat을 이용하는 방법도 있다 • nc ip(또는 도메인) port 형식으로 연결 요청
  • 50. WINDOWS PORT BINDING 셸 코드 - 파이프 • 포트로 들어오는 입력을 cmd에 전달하고, cmd의 결과를 포트로 내보내 보자 • 파이프를 이용 • (리눅스는 dup2함수를 이용할 수 있다. 윈도우는 cmd에서 이게 잘 안 먹혀서 파이프를 이용함) 대상 컴퓨터 공격자 port cmd stdin stdout port <Client> <Server>
  • 51. WINDOWS PORT BINDING 셸 코드 - 파이프 • 파이프? • IPC(Inter Process Communication)의 일종 • 윈도우 파이프에는 두 종류가 있음 • 이름없는 파이프와 이름있는 파이프 • 그 중 이름없는 파이프를 이용할 것 • 이름없는 파이프 • 수도 파이프와 유사 • 파이프 양쪽 끝에 구멍이 있고, 그 중 한쪽 구멍을 통해서 들어간 물체가 반대쪽 구멍으로 나올 수 있음 • 각 구멍은 하나의 용도(입력 또는 출력)로 이용 가능 • 단방향 통신 • 관계가 있는 프로세스 사이의 통신에 유용(부모-자식 관계 등) • 자식 프로세스의 표준 입출력을 파이프로 변경할 수도 있음
  • 52. WINDOWS PORT BINDING 셸 코드 – 파이프 – 실습1 • 익명 파이프 사용 예 • 파이프를 생성한 뒤, 쓰기 전용 입구에 WriteFile 함수 호출 • WriteFile은 원래 파일을 쓰는 함수 • 파일을 열어 얻는 핸들 대신 쓰기 전용 파이프를 줌 • -> 파이프에 씀 • ReadFile 함수 호출 • ReadFile은 원래 파일을 읽는 함수 • 파일 핸들 대신 읽기 전용 파이프를 줌 • -> 파이프의 내용을 읽음7500f180 7500f090
  • 53. WINDOWS PORT BINDING 셸 코드 – 파이프 – 실습2 • 익명 파이프 사용 예 • 부모-자식 관계에서 파이프를 이용하여 통신하기 예 • 부모 프로세스가 ipconfig.exe의 프로세스를 생성한다 • 자식 프로세스는 원래 stdout에 출력하던 결과를 파이프로 전달한다 • 부모 프로세스가 파이프로 결과를 받아서 자신의 stdout에 출력한다
  • 54. WINDOWS PORT BINDING 셸 코드 – 파이프 – 실습2
  • 55. WINDOWS PORT BINDING 셸 코드 – 파이프 – 실습2 • 부모-자식 관계에서 파이프 공유하기 예 • 공유 가능한 파이프를 만들기 위한 설정
  • 56. WINDOWS PORT BINDING 셸 코드 – 파이프 – 실습2 • 부모-자식 관계에서 파이프 공유하기 예 • 표준 입출력을 이와 같이 파이프로 지정할 수 있다 • 표준 입출력을 재지정하고 싶다면 WinExec 대신 CreateProcessA/W 함수를 이용해야 한다.
  • 57. WINDOWS PORT BINDING 셸 코드 – 파이프 – 실습2 • 앞에서 설명한 코드의 어셈블리 코드를 보고, 셸 코드를 짜보자. • 먼저 셸코드 보호를 위한 스택을 확보한다. • 편의를 위해 사용할 함수 주소를 직접 찾아 이용했다 • 원래는 universal 셸 코드로, 직접 주소를 얻어와야 함
  • 58. WINDOWS PORT BINDING 셸 코드 – 파이프 – 실습2 • 파이프가 상속될 수 있도록 보안 설정 구조체를 만들고, 내용을 쓴다 • 구조체 값을 넣을 때 그 위치에 주의한다. • 구조체 크기는 0xC • nLength의 위치는 구조체 시작주소 • 크기 : 4바이트 • lpSecurityDescriptor의 위치는 시작주소 + 4 • 크기 : 4바이트 • bInheritHandle의 위치는 시작주소 + 8 • 크기 : 4바이트 • BOOL 자료형은 int를 재정의한 것 • CreatePipe 함수에 인자로 구조체의 시작 주소를 넣어야 함에 주의한다. • 잘 백업해 둘 것
  • 59. WINDOWS PORT BINDING 셸 코드 – 파이프 – 실습2 • CreatePipe함수를 호출한다 • 방금 만든 구조체의 주소와 Read/Write 파이프 핸들을 인자로 준다 • 인자로 넣은 Read/Write 파이프 핸들은 이후 이용할 것 이므로 잘 백업해 두어야 한다 • 보안설정 구조체의 주소는 더이상 기억해 둘 필요가 없 으므로 백업해 둔 레지스터는 자유롭게 이용 가능하다 • esi 이용 가능
  • 60. WINDOWS PORT BINDING 셸 코드 – 파이프 – 실습2 • CreateProcess 함수에 넣을 구조체를 설정한다 • PROCESS_INFORMATION 구조체는 실행 후 정보를 받아오는 구조체 • 따로 설정할 것 없이 대충 적당한 주소만 넣어주면 됨 • STARTUPINFO는 실행할 프로그램에 설정을 해주는 구조체 • 먼저 GetStartupInfo 함수로 내용을 채운 뒤 설정을 더한다. • dwFlags의 위치는 시작주소 + 0x2C • 크기 : 4바이트 • 0x101을 값으로 설정 • hStdInput의 위치는 시작주소 + 0x38 • 크기 : 4바이트 • 위에서 생성한 핸들의 값을 넣어 줌 • hStdOutput의 위치는 시작주소 + 0x3C • 크기 : 4바이트 • 위에서 생성한 핸들의 값을 넣어 줌 (write) • hStdError의 위치는 시작주소 + 0x40 • 크기 : 4바이트 • 위에서 생성한 핸들의 값을 넣어 줌 (write) • wShowWindow의 위치는 시작주소 + 0x30 • 크기 : 2바이트 • hide로, 0을 설정
  • 61. WINDOWS PORT BINDING 셸 코드 – 파이프 – 실습2 • CreateProcess 함수를 호출 한다 • 백업해 둔 주소들을 이용 • 실행할 프로그램 문자열을 스택 에 저장하고, 이용 • 함수 호출 뒤에는 백업이 더이상 필요하지 않으므로 백업용으로 사용하던 레지스터를 이용할 수 있게 됨 • esi, ebx 사용 가능
  • 62. WINDOWS PORT BINDING 셸 코드 – 파이프 – 실습2 • 파이프에서 데이터 가져오기 • Read 핸들을 이용하여 파이프 버퍼에 있는 데이터를 가져온다
  • 63. WINDOWS PORT BINDING 셸 코드 – 파이프 – 실습2 • 파이프에서 데이터 가져오기 • 문자열 읽기로 읽을 수 있도록 null을 넣어주고, printf한다. • 문자열 읽기를 위한 포맷스트링을 만들고, push했다. • 정상 종료를 위해 맨 마지막에 ExitProcess를 호출해주자.
  • 64. WINDOWS PORT BINDING 셸 코드 – 실습 • Port binding 셸 코드 • 파이프는 2개 필요하다 • 공격자가 입력한 명령어를 cmd의 stdin에 넣는 용도 • cmd의 결과(stdout)를 공격자에게 보내는 용도 • 대상 컴퓨터측 동작 • 공격을 당해 셸 코드를 실행하는 프로그램 : A라 하자 • A가 패킷을 read -> A가 파이프1 write -> cmd가 파이프 1 read -> cmd에서 처리 -> cmd가 파이프2 write -> A가 파이프2 read -> A가 패킷을 write 대상 컴퓨터 공격자 port cmd stdin stdout port <Client> <Server>
  • 65. WINDOWS PORT BINDING 셸 코드 – 실습 • Port binding 셸 코드 • 이제 포트를 열고 – 파이프를 연결하고 – cmd를 실행하고 – 원격으로 cmd를 이용하는 코드를 짜보자. • 셸 코드 로직은 앞으로 나올 코드와 같다. • 어셈블리로 정제, Universal 셸 코드로 만들기, NULL 없애기 과정을 거치면 실제로 사용 가능한 셸 코드가 완성된다.
  • 66. WINDOWS PORT BINDING 셸 코드 – 실습 • Port binding 셸 코드
  • 67. WINDOWS PORT BINDING 셸 코드 – 실습 • Port binding 셸 코드
  • 68. WINDOWS PORT BINDING 셸 코드 – 실습 • Port binding 셸 코드 • 중요 부분 • 파이프를 2개 생성해야 한다
  • 69. WINDOWS PORT BINDING 셸 코드 – 실습 • Port binding 셸 코드 • 중요 부분 • PeekNamedPipe 함수는 이름있는/이름없는 파이프 버퍼를 복사하 는 기능을 한다 • 파이프 버퍼를 청소하지 않고 • ReadFile을 호출하면 데이터가 들어올 때까지 Block상태가 된다. • ->동기 함수(blocking function) • PeekNamedPipe는 바로 반환된다 • -> 이 함수의 결과에 따라 ReadFile을 호출하거나 하지 않으면 됨
  • 70. WINDOWS REVERSE CONNECTION 셸 코드 • 실습
  • 71. WINDOWS REVERSE CONNECTION 셸 코드 • 포트 바인딩 셸 코드의 단점 • Exploit을 이용한 공격 대상은 주로 서버가 되는데, 대부분의 서버 앞에는 방화벽이 있음 • 막상 포트를 열어도 접속을 하지 못하는 경우가 생김 • Reverse Connection 셸 코드 • 대부분의 방화벽은 외부에서의 접속(Inbound)은 차단하지만, 내부에서 외부로 접속(Outbound)하는 것은 차단하지 않는다 • 이 점을 이용하여, 타겟 시스템(내부망)에서 공격자의 PC(외부망)으로, 거꾸로 접속하도록 하는 셸코드가 Reverse Connection 셸 코드 • 공격자가 서버, 타겟이 클라이언트 • Connect-back 셸 코드라 하기도 한다.
  • 72. WINDOWS REVERSE CONNECTION 셸 코드 • 기본적으로 Port binding 셸 코드와 비슷한 형태 • 공격자가 server고, 대상 컴퓨터가 client가 된다는 점이 다름 대상 컴퓨터 공격자 port cmd stdin stdout port <Server> <Client>
  • 73. WINDOWS REVERSE CONNECTION 셸 코드 – 실습
  • 74. WINDOWS REVERSE CONNECTION 셸 코드 – 실습 • port binding 셸 코드에서 서버를 여는 부분을 삭제하고, 대신 외부 서버에 연결하는 코드를 넣었다 • 파이프 생성/cmd 실행 코드는 동일하다 • cmd 명령어를 전달하는 부분에서, accept 코드가 빠졌다.
  • 75. WINDOWS REVERSE CONNECTION 셸 코드 – 실습 • nc를 이용하여 서버를 열고, 리버스 커넥션 코드를 이용하여 접속했다.
  • 76. LINUX 환경에서 셸 코드 제작 • 실습
  • 77. LINUX 환경에서 셸 코드 제작 • 리눅스에서 사용 가능한 셸 코드를 작성하기 위해서는 리눅스 환경에서 셸 코드를 작성할 필요가 있다. • gcc와 objdump를 이용 • 오른쪽은 셸을 실행하는 코드를 C언어 수준에서 쓴 것 • execve로 sh 프로그램을 실행 • 메인 함수 인자로 프로그램 이름을 넣을 수 있게 함 • 32비트를 기준으로 만들 것 • gcc에 옵션으로 m32를 준다. • 동적 링킹된 코드는 복잡하다. 함수 코드를 직접 보고 중요한 코드를 떼어올 것이므로 찾기 쉽게 정적 링킹 한다. • gcc에 옵션으로 static을 준다.
  • 78. LINUX 환경에서 셸 코드 제작 • objdump에 옵션으로 d를 주면 디스어셈블한 코드를 뽑아준다. • grep으로 원하는 부분만 잡는다 • A 옵션을 주고 뒤에 숫자를 써 주면, 찾은 곳으로부터 숫자만큼의 instruction을 뽑아준다. • 필요한 코드를 찾아서 다시 어셈블리어로 코딩한다. • 필요에 따라 많은 수정이 필요
  • 79. LINUX 환경에서 셸 코드 제작 – 실습 • 필요한 문자열을 스택에 저장하고, 그 주소를 넘길 수 있도 록 할 것 • 문자열을 스택에 넣는 코드를 쓰기 위해 아스키코드를 구했다. • push를 이용하여 스택에 문자열을 넣어주고, 문자열 주소를 백업해 둔다.
  • 80. LINUX 환경에서 셸 코드 제작 – 실습 • sh의 메인 함수 인자로 줄 배열이 필요함 • 문자열 주소와, NULL 이 들어간 포인터 배열이 필요 • 배열을 저장하는 곳이 스택이라는 점에 주의 • NULL을 먼저 push, 그 다음 문자열 주소를 push • 배열 주소를 백업해 둔다.
  • 81. LINUX 환경에서 셸 코드 제작 – 실습 • execve는 시스템 콜에 속한다. • 프로그램을 로딩하기 위해서는 OS가 필요하기 때문 • 한 번 직접 들어가서 코드를 보자 • 처음 execve를 호출할 때, 함수 인자로 3가지를 push한다. • 실행할 프로그램 문자열 주소, sh의 메인 함수 인자, NULL • push는 역순으로 0, 메인함수 인자 주소, 문자열 주소 • execve 함수 내부에서, 다시 적절한 레지스터에 인자 값을 배치해 준다. • edx에 환경변수(여기선 NULL), ecx에 메인함수 인자 배열 주소, ebx에 실행할 프로그램 경로 주소 • eax에 0xb를 설정하고 함수 호출 • 호출한 내부 함수에서는 인자를 push한 뒤 시스템콜을 한다. • push는 그냥 백업용임. 그냥 int $0x80하면 됨. • 시스템콜 번호는 이미 eax에 설정되어 있음
  • 82. LINUX 환경에서 셸 코드 제작 – 실습 • execve의 핵심 코드를 알아냈으니 구태여 execve를 호출할 필요 없이, 직접 시스템 콜을 이용할 수 있음 • (라이브러리 의존성을 없앴음) • edx에 환경변수(여기선 NULL) 설정 • ecx에 메인함수 인자 배열 주소 설정(코드 처음부터 그렇게 설정했음) • ebx에 실행할 프로그램 경로 주소 설정(코드 처음부터 그렇게 설정했음)
  • 83. LINUX 환경에서 셸 코드 제작 – 실습 • 실행해 보면 잘 작동하는 것을 확인할 수 있다. • 이제 objdump를 이용하여 기계어를 추출, 셸코드를 제작한다.
  • 84. LINUX 환경에서 셸 코드 제작 – 실습 • 만든 셸 코드를 테스트하는 코드 • gcc에 옵션으로 –z execstack 을 써준다 • DEP 해제(stack에 있는 코드 실행 방지 끔) • Data Execution Protection • 리눅스는 윈도우처럼 동적 라이브러리에서 함수 주소를 찾는 universal 셸 코드를 만드는 대신 바로 시스템 콜을 이용하는 경우가 많다. • 시스템 콜을 사용하는 경우 시스템 콜의 번호와 사용 방식에만 의존성이 있고, 라이브러리에 대한 의존성은 없음 • NULL문자를 없애고, 크기를 줄이는 작업을 하면 더욱 정제할 수 있다.
  • 85. LINUX PORT BINDING 셸 코드 • 포트열기 • dup2 • 이전에 만든 셸 코드와 합치기
  • 86. LINUX PORT BINDING 셸 코드 • 셸 코드에 추가로 필요한 기능 • 포트 열게 하기 • 포트로 들어오는 명령어를 셸에 전달하게 하기 • 출력을 포트로 전달하게 하기 대상 컴퓨터 공격자 port sh stdin stdout port <Client> <Server>
  • 87. LINUX PORT BINDING 셸 코드 – 포트 열기 – 실습 • 오른쪽 코드는 기본적인 서버 오픈 코드이다. • gdb를 이용하여 핵심 코드의 디스어셈블 코드를 보고, 어셈블리어로 다시 써 보자 • 본격적인 루틴 전에, 스택을 위한 코드를 추가한다.
  • 88. LINUX PORT BINDING 셸 코드 – 포트 열기 – 실습 • socket, bind, listen, accept, close 함수는 전부 시스템 콜이다. • 102번 시스템 콜인 socketcall()을 이용하면 소켓과 관련된 시스템 콜을 이용할 수 있다. • 인자로 socket 관련 함수 중 어떤 함수를 사용할 지를 선택하고, 해당 함수의 인자들을 받는다. • Ex) EAX에 102를 설정하고, EBX에 2를 설정하고 ECX에 인자 포인터를 넣고 시스템 콜을 하면 bind 함수를 호출
  • 89. LINUX PORT BINDING 셸 코드 – 포트 열기 – 실습 • socket 함수 • SOCK_STREAM이 1로 정의되어 있고, PF_INET이 2로 정의되어 있음을 알 수 있다. • socket함수의 반환 값인 소켓 디스크립터는 eax에 있다.
  • 90. LINUX PORT BINDING 셸 코드 – 포트 열기 – 실습 • socket 함수 내부 • EBX에 1 설정 (socketcall의 타입 설정) • ECX에 socket 인자가 들어있는 주소를 넣고, 그 주소에는 2, 1, 0(socket 인자)가 들어간다. • EAX에 0x66 (십진수로 102)를 설정한다. • 이 상태로 시스템 콜을 하는 함수를 호출한다 • 여기서 사용하는 push는 단순히 레지스터 값을 백업해 두는 것일 뿐임 • 다시 복원하는 코드로 추론 가능 • ->결론 • EAX에는 시스템 콜 번호인 0x66 • EBX에는 socketcall의 타입 설정 • ECX에는 주소를 넣고, 해당 주소에는 함수의 인자를 넣 는다.
  • 91. LINUX PORT BINDING 셸 코드 – 포트 열기 – 실습 • socket 함수 • 필수 루틴만 빼서 어셈블리어로 썼다.
  • 92. LINUX PORT BINDING 셸 코드 – 포트 열기 – 실습 • memset • memset 호출 대신 직접 0으로 스택을 정리 하고, 서버 주소를 스택에 써 준다. • 디스어셈블리 코드를 통해 주소 구조체의 크기가 0x10임을 알 수 있다.
  • 93. LINUX PORT BINDING 셸 코드 – 포트 열기 – 실습 • 서버 주소 설정 • sin_family는 AF_INET이며 이 값은 2이고, 2바이트 크기 • sin_port는 직접 12345를 빅 엔디안으로 변환한 0x3930을 넣 으면 된다. 2바이트 크기 • sin_addr는 INADDR_ANY이며 이 값은 0이고, 4바이트 크기 • sin_addr가 가장 높은 주소, sin_family가 낮은 주소임을 주의
  • 94. LINUX PORT BINDING 셸 코드 – 포트 열기 – 실습 • 서버 주소 설정 • memset과 설정을 어셈블리어로 썼다.
  • 95. LINUX PORT BINDING 셸 코드 – 포트 열기 – 실습 • bind 함수 • 인자로 앞서 백업해 둔 소켓 디스크립터 와 서버 주소 구조체의 주소, 그리고 서버 주소 구조체의 크기를 준다. • 구조체 크기는 0x10 • socket함수와 같이 시스템 콜 번호를 설정 하고, 인자 배열의 주소를 넘기면 된다.
  • 96. LINUX PORT BINDING 셸 코드 – 포트 열기 – 실습 • bind 함수 • bind 코드를 어셈블리어로 썼다.
  • 97. LINUX PORT BINDING 셸 코드 – 포트 열기 – 실습 • listen 함수 • 인자로 앞서 백업해 둔 소켓 디스크립터와 개수를 넣는 다. • 같은 방식으로 시스템 콜
  • 98. LINUX PORT BINDING 셸 코드 – 포트 열기 – 실습 • listen 함수 • listen 코드를 어셈블리어로 썼다.
  • 99. LINUX PORT BINDING 셸 코드 – 포트 열기 – 실습 • accept 함수 • accept함수에는 구조체 크기를 저장하는 int 크기의 변수 주소를 넣어야 한다. • 스택에 공간을 확보하고 0x10을 써 줌 • 인자로 소켓 디스크립터, 클라이언트 주소 구조체가 저장될 주소, 구조체 크기 변수 주소를 넣는다.
  • 100. LINUX PORT BINDING 셸 코드 – 포트 열기 – 실습 • accept 함수 • accept 코드를 어셈블리어로 썼다.
  • 101. LINUX PORT BINDING 셸 코드 – DUP2 • 포트로 들어오는 입력을 sh에 전달하고, sh의 결과를 포트로 내보내 보자 • Windows와 다르게, dup2 시스템 콜을 이용하여 간단하게 stdin과 stdout을 조정할 수 있다. 대상 컴퓨터 공격자 port sh stdin stdout port <Client> <Server>
  • 102. LINUX PORT BINDING 셸 코드 – DUP2 • dup2 • 파일 디스크립터 복사본을 만든다. • dup()는 커널에서 알아서 사용하지 않는 디스크립터 번호 중에 하나가 자동으로 지정되지만, dup2()는 프로그래머가 원하는 번 호를 지정할 수 있다. • dup2(fd, stdout) 할 시, 원래 stdout으로 오던 자료가 fd로 들어 감 • 시스템 콜 63번
  • 103. LINUX PORT BINDING 셸 코드 – DUP2 – 실습1 • 간단히 사용법을 알아보자 • stdout의 파일 디스크립터인 1이 fd가 가리키는 파일을 가리키 도록 했다. • 파일디스크립터 1을 이용하여 나오는 출력은 전부 파일에 쓰 이게 된다.
  • 104. LINUX PORT BINDING 셸 코드 – DUP2 – 실습2 • 앞에서 쓴 서버 코드에 dup2를 추가한다. • gdb를 이용하여 핵심 코드의 디스어셈블 코드를 보고, 어셈블리어로 다시 써 보자
  • 105. LINUX PORT BINDING 셸 코드 – DUP2 – 실습2 • dup2 함수 • 바로 위에서 호출한 accept 함수는 eax에 accept하여 만든 소켓 디스크립터를 저장하고 있다.
  • 106. LINUX PORT BINDING 셸 코드 – DUP2 – 실습2 • dup2 함수 • dup2는 시스템 콜이다. 직접 들어가서 중요한 코드만 떼어내어 보자. • 인자로 넣은 수정할 fd(stdin, 0으로 정의됨)는 ecx에, 소켓 파일디스크립터는 ebx에 저장된다. eax에는 0x3f(10진수로 63)이 들어간다.
  • 107. LINUX PORT BINDING 셸 코드 – DUP2 – 실습2 • dup2 함수 • 앞서 쓴 코드의 맨 끝에 이어 쓴다. • dup2함수의 핵심 코드만 떼어 내어 썼다. • stdin과 stdout stderr 전부 다 dup2함수 적용 • 각각 파일 디스크립터 0, 1, 2로 정의되어 있음
  • 108. LINUX PORT BINDING 셸 코드 – 이전에 만든 셸 코드와 합치기 • 셸을 여는 코드를 추가한다. • 앞에서 만든 코드를 그대로 붙여 넣음
  • 109. LINUX PORT BINDING 셸 코드 – 이전에 만든 셸 코드와 합치기 • 마지막으로 셸을 여는 코드 어셈 블리어를 붙여서 완성한다.
  • 110. LINUX PORT BINDING 셸 코드 – 이전에 만든 셸 코드와 합치기 • nc를 이용하여 작동됨을 확인해보고, objdump를 이용하여 기계어를 떼어내어 이용해 보자. • 포트가 열려 있는지 확인하는 법 • netstat –nap | grep 포트번호 • 특정 포트를 사용하는 프로그램 죽이기 • fuser –k –n tcp 포트번호
  • 111. LINUX REVERSE CONNECTION 셸 코드 • 실습
  • 112. LINUX REVERSE CONNECTION 셸 코드 • 기본적으로 Port binding 셸 코드와 비슷한 형태 • 공격자가 server고, 대상 컴퓨터가 client가 된다는 점이 다름 • port binding에서 서버를 여는 코드를 서버에 접속하는 코드로 변경하면 된다. 대상 컴퓨터 공격자 port sh stdin stdout port <Server> <Client>
  • 113. LINUX REVERSE CONNECTION 셸 코드 - 실습 • 연결할 서버 주소 설정에 IP 하드코딩 • connect 코드 추가
  • 114. LINUX REVERSE CONNECTION 셸 코드 - 실습 • nc로 서버를 열어 둔 뒤 reverse connection 프로그램을 실행하면 서버 측에서 셸을 이용할 수 있음
  • 115. METASPLOIT과 셸 코드 • 셸 코드 제작 • 셸 코드 인코딩
  • 116. METASPLOIT FRAMEWORK • Metasploit이란? • 침투 테스트를 위한 Framework • 간단히 명령어를 조합하여 편하게 해킹이 가능하게 함 • 이미 만들어져 있는 모듈을 이용 • 특정 취약점에 대한 공격코드(페이로드), 셸 코드 등 제공 • Metasploit에서 사용 가능한 형태로 직접 모듈을 만들어 넣어두면 후에 편하게 재이용 가능 • 칼리 리눅스에 기본으로 설치되어 있음 • 다른 리눅스나 윈도우에도 설치 가능 • 이번에는 간단하게 셸 코드 제작과 인코딩에 관련된 내용만 알아볼 것
  • 117. METASPLOIT FRAMEWORK를 이용한 셸 코드 제작 • Metasploit Framework를 이용한 셸 코드 제작 • 직접 제작은 아니고, 이미 만들어진 셸 코드 중 원하는 기능을 가진 셸 코드를 얻는 방법이다 • msfvenom을 이용 • 옛날 버전의 msfpayload와 msfencode가 합쳐진 것. 최신 버전의 Metasploit Framework를 설치하면 있다. • 옛날 책에서 위의 프로그램을 이용한다면, 그 대신 msfvenom을 이용해야 한다. • 각종 옵션을 설정하여 원하는 형태의 셸 코드를 얻을 수 있다.
  • 118. METASPLOIT FRAMEWORK를 이용한 셸 코드 제작 • Metasploit Framework를 이용한 셸 코드 제작 • msfvenom –l 명령어를 입력하면 사용할 수 있는 페이로 드들의 리스트가 나온다 • 경로와 설명 • 이 중 원하는 것을 선택한다 • 앞에서 만들었던 Windows shell port binding 셸 코드 를 이용해 보자 • 칼리리눅스에는 기본 경로가 잡혀 있기 때문에 명령어 로 msfvenom 프로그램을 실행할 수 있다.
  • 119. METASPLOIT FRAMEWORK를 이용한 셸 코드 제작 • Metasploit Framework를 이용한 셸 코드 제작 • msfvenom –p 로 payload를 설정한다 • 그 다음 줄에 위에서 찾은 경로를 적어준다 • 페이로드의 옵션으로 포트를 설정했다. • -f로 format을 설정한다. • 뒤에 c를 적음 : c언어에서 바로 이용할 수 있는 셸 코드 • python을 적으면 python 에서 바로 쓸 수 있는 형태로 나오고.. • 언어/실행파일 종류는 밑과 같음 • 복사해도 되지만, 보통 맨 뒤에 >code.c 등을 붙여 코드를 파일로 저장
  • 120. METASPLOIT FRAMEWORK를 이용한 셸 코드 제작 • Metasploit Framework를 이용한 셸 코드 제작 • 위에서 만든 셸 코드를 실행해보자 • 사실 잘 안되는 경우도 많다 • OS 버전이나, 사소한 환경 등의 차이로 인해 • 애초에 잘못된 코드가 있을 가능성도 있음
  • 121. METASPLOIT FRAMEWORK를 이용한 셸 코드 제작 • Windows에서도 같은 방법으로 Metasploit을 이용할 수 있다 • 오른쪽은 Windows power shell에 서 msfvenom을 실행한 것
  • 122. METASPLOIT FRAMEWORK를 이용한 셸 코드 인코딩 • Metasploit Framework를 이용한 셸 코드 인코딩 • 셸 코드에 특정한 문자가 들어가지 않기를 원할 때, 인코딩을 이용할 수 있다 • null문자 없애기에 유용하게 이용할 수 있다 • 셸 코드에서 제거되어야 하는 문자를 bad char라고 부름 • Metasploit은 여러 인코더를 제공하기 때문에, 원하는 상황에 맞춰 골라 쓸 수 있다. • null 문자를 없애는 용도 뿐만 아니라 많은 용도로 이용된다 • Ex) 백신 우회, 크기 줄이기 등
  • 123. METASPLOIT FRAMEWORK를 이용한 셸 코드 인코딩 • Metasploit Framework를 이용한 셸 코드 인코딩 • -b 옵션을 이용해 bad char를 설정했다 • 셸 코드가 0x00과 0xff가 없도록 인코딩 되었다
  • 124. METASPLOIT FRAMEWORK를 이용한 셸 코드 인코딩 • Metasploit Framework를 이용한 셸 코드 인코딩 • -e 옵션을 이용해 인코더를 지정했다 • fnstenv_mov 인코더는 부동소수점을 위한 명령어를 이용하도록 인코딩한다
  • 125. 부록 • 참고 사이트 • 참고 서적
  • 126. 참고 사이트 • Windows 환경에서의 Buffer Overflow 공격 기법 • http://research.hackerschool.org/data/WBOF/WBF.htm • http://dest.kr/c-c/windows-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C%EC%9D%98-buffer-overflow- %EA%B3%B5%EA%B2%A9-%EA%B8%B0%EB%B2%95/ • PeekNamedPipe 함수와 ReadFile • http://tksssch29.tistory.com/entry/cmdexe-%EC%99%80-pipe%EC%99%80%EC%9D%98-%ED%86%B5%EC%8B%A0 • Exploit Database – 미리 만들어진 셸 코드들 • https://www.exploit-db.com/shellcode/ • Metasploit 설치 • https://github.com/rapid7/metasploit-framework/wiki/Nightly-Installers • msfvenom 기본 사용 정리 • http://blog.naver.com/PostView.nhn?blogId=knq1130&logNo=220621445199
  • 127. 참고 사이트 • dup2 함수 • http://forum.falinux.com/zbxe/index.php?document_srl=520937&mid=C_LIB • http://sosal.kr/186
  • 128. 참고 서적 • 뇌를 자극하는 윈도우즈 시스템 프로그래밍 • Chapter 8 프로세스간 통신 2 _ 파이프 방식의 IPC • 윈도우 시스템 해킹 가이드 • Part 3 쉘코드 원리와 작성