카이사르 암호 분석 로직입니다.


message = "JZF LCP L YPHMTP SLNVPC"
letter = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

for key in range(len(letter)):
translated=''
for symbol in message:
if symbol in letter:
num = letter.find(symbol)
num = num - key

if num <0:
num = num + len(letter)
translated = translated + letter[num]
else:
translated = translated + symbol

print ('key #%s: %s'%(key,translated))


그냥 rot13.com 쓰세요.


프리패치는 윈도우 운영체제에서 파일이 실행된 정보를 담고 있는 아티팩트이다. 포렌식적 관점에서 바라보았을 때 악성코드가 실행된 시간 정보, 실행 횟수 정보, 실행 경로, 실행 파일, 실행 파일에 import된 dll 목록 등을 확인할 수 있다. 


주로 랜섬웨어 또는 파괴형 악성코드 등의 경우 침해 지속을 통해 두 번 이상 실행되지 않는 것이 특징이기 때문에, 프리패치를 분석할 때 실행 횟수가 "1"인 레코드를 먼저 확인하기도 한다.


일반적으로 라이브 포렌식을 진행하게 된다면 , 보통 디스크의 훼손을 최소화하기 위해 CLI의 스크립트 형태로 데이터를 수집하는 것이 일반적이다. 아래는 CLI 형태의 스크립트 코드이다.


# -*- coding:utf-8 -*-
from datetime import datetime, timedelta
import argparse
import struct # read method resolution
import os # 많은 기능 중 폴더를 열수 있게 끔 해주는 기능이 있는 라이브러리


# 시간 변환 함수
def Trans_time(big):
little = '' # 빈 변수 할당
for i in range(0, len(big), 2):
little += big[-(i + 2)]
little += big[-(i + 1)]
return str(datetime(1601, 1, 1) + timedelta(microseconds=(int(little, 16) / 10), hours=+9))


# Prefetch File Header Structure
pfHeader = dict(version=0, size=0, name=0, hash=0, last_launch=0, launch_count=0)


def Display():
print u'운영체제 버전 : ', pfHeader['version']
print u'파일 사이즈 : ', pfHeader['size'], "BYTE"
print u'파일 이름 : ', pfHeader['name']
print u'경로 해시 : %X' % (pfHeader['hash'])
print u'파일 실행 횟수 : ', pfHeader['launch_count']
print u'최종실행 시각 : ', pfHeader['time']
print '\n'


# OPERATING SYSTEM CHECK FUNCTION
def checkOS(version):
if version == 17:
return "WindowsXP"
elif version == 23:
return "Windows7"
elif version == 26:
return "Windows8"


# read라는 인자를 받아 PreFtech 파일의 구조를 해석함
def Parse(read):
pfHeader['version'] = checkOS(struct.unpack('<L', read[0x00:0x04])[0]) # version #<L = 4byte
pfHeader['size'] = struct.unpack('<L', read[0x0c:0x10])[0] # file size
pfHeader['name'] = struct.unpack('58s', read[0x10:0x4A])[0].split('\x00\x00')[0].replace('\x00', '')
pfHeader['hash'] = struct.unpack('<L', read[0x4C:0x50])[0]

if pfHeader['version'] == "WindowsXP":
pfHeader['count'] = struct.unpack('<L', read[0x90:0x94])[0]
pfHeader['time'] = Trans_time(read[0x78:0x80].encode("hex"))

if pfHeader['version'] == "Windows7":
pfHeader['count'] = struct.unpack('<L', read[0x98:0x9C])[0]
pfHeader['time'] = Trans_time(read[0x80:0x88].encode("hex"))

if pfHeader['version'] == "Windows8":
pfHeader['count'] = struct.unpack('<L', read[0xD0:0xD4])[0]
pfHeader['time'] = Trans_time(read[0x80:0x88].encode("hex"))


if __name__ == "__main__": # 본 코드 자체로만 동작할 때는 아래 코드가 메인 코드로 동작 // #만약 라이브러리 형태라면 아래 코드는 동작하지 않음
parser = argparse.ArgumentParser(add_help=True) # help 옵션 추가
parser.add_argument('-f', action='store', dest='file', help='Parsing Prefetch File') # 파일 파싱을 위한 옵션 추가
parser.add_argument('-d', action='store', dest='dir', help='Parsing Prefetch File') # 디렉토리를 파싱을 위한 옵션 추가
args = parser.parse_args()

if args.file != None:
with open(args.file, 'rb') as f: # 파일 열기
Parse(f.read()) # 연 파일에 대한 읽기.
Display()

elif args.dir != None: # 디렉토리가 있다면
for (path, dir, files) in os.walk(args.dir): # os 모듈을 이용해서 디렉토리 안으로 들어감
for filename in files:
with open(path + '\\' + filename, 'rb') as f: # 파일 열기
Parse(f.read()) # 연 파일에 대한 읽기.
Display()



다소 보완이 필요한 부분은, Windows 10 운영체제의 프리패치는 인코딩이 적용되어 있는데 이를 디코딩하여 파싱하는 로직이 추가되어야 할 것이다.

 

악성코드 분석에는 크게 둘로 나뉘며, 정적 분석과 동적 분석이 있다.

 

정적 분석에 필요한 바이너리가 가지는 정보를 확인하기 위해 다음과 같은 도구를 제작해보았다.

 

추가적으로 바이너리에서 Import하는 DLL 목록과 DLL에서 사용되는 API 목록을 확인할 수 있게 구현하였다.

 

 

다소 보완해야할 부분으로는, Entropy를 기반하여 패킹 여부를 확인하는 로직에서 다량의 패킹된 샘플을 기반으로 확인한 결과 False Positive와 False Negative가 많은 것이 확인되었다.

 

물론 Section Header의 이름만 확인하여도 패킹여부를 판단할 수 있지만 대량의 샘플의 패킹여부 체크를 자동화 하기 위해서는 단순히 6.85라는 수치만을 가지고 판단하기에는 부족한 면이 있다고 판단되어 추가적인 요소가 패킹 여부 탐지 로직에 덧붙여져야 할 것이다. 

'Threat Intelligence > Analysis Tool' 카테고리의 다른 글

PE Structure Analysis (PE 구조 분석)  (0) 2019.08.04

[바이너리 확인]

 

[보호기법 확인]

 

[코드 확인]

main()

0x1f 바이트 크기의 버퍼 s에 gets 함수를 통해 입력을 받고 s의 주소 값을 select_func 함수의 인자로 넘겨준다.

 

select_func()

v3은 함수 two의 주소 값이 할당되어 있고, 버퍼 dest의 경우 0x1e 바이트 크기가 할당되어 있다.

 

인자로 받은 버퍼 s에 있는 값을 버퍼 dest의 주소에 0x1f 크기 만큼 복사한다.

 

일단..시간이 자야할거같아서 자고옴...

 

print_flag()

 

'War Game > HackCTF' 카테고리의 다른 글

[HackCTF] Offset  (0) 2019.06.23
[HackCTF] Simple_Overflow_ver_2  (0) 2019.06.23
[HackCTF] x64 Simple_size_BOF  (0) 2019.06.23
[HackCTF] x64 Buffer Overflow  (0) 2019.06.23
[HackCTF] 내 버퍼가 흘러 넘친다 !!!  (0) 2019.06.22
[HackCTF] BOF_Basic#2  (0) 2019.06.22

[바이너리 확인]

 

[보호기법 확인]

 

[코드 확인]

선뜻 코드로만 이해하기에는 조금 시간이 걸릴듯하여 바이너리를 실행해보았다.

 

입력을 받아 버퍼에 저장을 하며, 한 줄에 16바이트가 될 때 마다 라인개행(0xA)이 들어가는 일종의 Hex Viewer 프로그램과 비슷해보인다.

 

코드를 확인할 경우 버퍼 s의 크기는 0x88 바이트인 것을 확인할 수 있고 32bit이므로 4바이트씩 할당되므로 메모리 구조는 다음과 같다.

 

| s(0x88) | sfp(0x04) | ret(0x04) |

 

따라서 버퍼 s에 셸코드를 담고 리턴 어드레스를, 해당 프로그램에서 출력해주는 버퍼 s의 시작주소로 덮어주면 셸을 획득할 수 있을 것이다.

 

[익스플로잇 코드]

[결과]

 

'War Game > HackCTF' 카테고리의 다른 글

[HackCTF] Offset  (0) 2019.06.23
[HackCTF] Simple_Overflow_ver_2  (0) 2019.06.23
[HackCTF] x64 Simple_size_BOF  (0) 2019.06.23
[HackCTF] x64 Buffer Overflow  (0) 2019.06.23
[HackCTF] 내 버퍼가 흘러 넘친다 !!!  (0) 2019.06.22
[HackCTF] BOF_Basic#2  (0) 2019.06.22

[바이너리 확인]

 

[보호기법 확인]

 

[코드 확인]

버퍼의 크기가 0x6d30 인 것을 확인할 수 있고, 버퍼의 주소를 출력해주는 것을 알 수 있다.

 

버퍼의 주소는 매번 가변적이므로 고정 값을 입력할 수 없다.

 

따라서 셸을 획득하기 위해서는 셸코드를 버퍼에 넣고 서버로부터 출력되는 버퍼의 주소를 받아서 해당 값을 리턴 어드레스로 덮어 쓰면 될 것이다.

 

[익스플로잇 코드]

 

[결과]

 

[주의사항]

셸코드에서 한참 삽질 했습니다.

48 바이트 셸코드 안됩니다.

25 바이트 셸코드 안됩니다.

23 바이트 셸코드 됩니다.

왜 그렇죠? 답글좀요 ㅜㅜ

'War Game > HackCTF' 카테고리의 다른 글

[HackCTF] Offset  (0) 2019.06.23
[HackCTF] Simple_Overflow_ver_2  (0) 2019.06.23
[HackCTF] x64 Simple_size_BOF  (0) 2019.06.23
[HackCTF] x64 Buffer Overflow  (0) 2019.06.23
[HackCTF] 내 버퍼가 흘러 넘친다 !!!  (0) 2019.06.22
[HackCTF] BOF_Basic#2  (0) 2019.06.22

[바이너리 확인]

[보호기법 확인]

[코드 확인]

배열 변수 s에 0x10c 바이트가 할당되어 있고 해당 변수에 입력을 받은 뒤, 출력하는 매우 간단한 프로그램이다.

 

보호기법 중 카나리가 설정되어 있지 않기 때문에 메모리 구조는 다음과 같다.

| s(0x10c) | v5(0x04) | sfp | ret |

 

따라서 배열 변수 s에 임의의 값을 담고 리턴 어드레스까지 오버플로우 시키면 될 것이다.

 

리턴 어드레스로 덮어쓰는 방법으로는 배열 변수 s에 셸코드를 담고 버퍼의 시작주소를 덮거나 셸을 띄우는 함수가 있다면 해당 함수의 주소를 덮어쓰면 될 것이다.

 

확인 결과, callMeMaybe라는 함수에서 셸을 띄우는 것을 확인할 수 있고 리턴 어드레스를 해당 주소 0x400606으로 덮어씌우면 될 것이다.

 

[익스플로잇 코드]

[결과]

'War Game > HackCTF' 카테고리의 다른 글

[HackCTF] Simple_Overflow_ver_2  (0) 2019.06.23
[HackCTF] x64 Simple_size_BOF  (0) 2019.06.23
[HackCTF] x64 Buffer Overflow  (0) 2019.06.23
[HackCTF] 내 버퍼가 흘러 넘친다 !!!  (0) 2019.06.22
[HackCTF] BOF_Basic#2  (0) 2019.06.22
[HackCTF] Basic_BOF#1  (0) 2019.06.22

[바이너리 확인]

[보호기법 확인]

 

[코드 확인]

read를 통해 name 변수에 0x32 바이트만큼 받고 gets 함수를 통해 버퍼를 읽어들인다.

 

name 변수의 경우 bss 영역에 존재하고, 버퍼 s의 경우 스택에 쌓이게 된다.

 

따라서 bss 영역의 name 변수에 셸코드를 넣은 뒤, 버퍼 s에 오버플로우를 일으켜 리턴 어드레스를 bss 영역의 name 값이 시작되는 곳으로 변조하면 될 것이다.

 

버퍼 s의 크기는 0x14 바이트 이므로 메모리 구조는 다음과 같아진다.

| s(0x14) | sfp(0x04) | ret(0x04) |

 

페이로드 구성은 다음과 같다.

| A*0x14 | A*0x04 | 0x0804A060(bss) |

 

[익스플로잇 코드]

[결과]

 

'War Game > HackCTF' 카테고리의 다른 글

[HackCTF] Simple_Overflow_ver_2  (0) 2019.06.23
[HackCTF] x64 Simple_size_BOF  (0) 2019.06.23
[HackCTF] x64 Buffer Overflow  (0) 2019.06.23
[HackCTF] 내 버퍼가 흘러 넘친다 !!!  (0) 2019.06.22
[HackCTF] BOF_Basic#2  (0) 2019.06.22
[HackCTF] Basic_BOF#1  (0) 2019.06.22

+ Recent posts