Lined Notebook

Pyinstaller로 작성된 파일 분석하기. 🐍

by juraffe juraffe

오늘 보안 팀에 들어가기 위하여 주어진 문제를 해결하는 시험을 치렀다. 문제는 Elderly File로 Aero 2020에서 출제된 100점 문제이다. 문제를 요약하면 pyinstaller로 컴파일된 바이너리에서 pyc를 추출하고 파일 포맷이 깨진 pyc를 복구 후 원본 python 코드를 보고 주어진 다른 파일을 복원하는 문제였다. 꽤 많은 곳에서 시간을 많이 사용하고 다시 생각하니 의외로 간단히 해결할 수 있었지 않을까 하는 후회와 다음에 좀 더 잘하자는 의미로 작성되었다.


Aero2020 - Elderly file

│ 0x0040310d      bfba574000     mov edi, str.MEIPASS2                 ; 0x4057ba ; "_MEIPASS2"

문제를 열면 _MEIPASS2라는 문자열이 자주 보이는데 구글에 검색하게 되면 Pyinstaller와 관련된 내용이 많이 나오는 걸로 Pyinstaller로 컴파일된 파이썬 스크립트라는 걸 알 수 있다.

cou9ar@kali:~$ pip install pyinstaller

pyinstaller로 컴파일된 바이너리는 pyi-archive_viewer로 내부정보를 확인할 수 있는데 pyinstaller를 설치하면 사용할 수 있다. 하지만 내 경우에는 Pyinstaller가 PATH에 등록이 안되었는지 pyinstaller 패키지 내부에서 직접 이동해서 사용해야 했다.

cou9ar@kali:~$ pip install pyinstaller
...
Requirement already satisfied: pyinstaller in /home/cou9ar/.local/lib/python2.7/site-packages (3.6)
...
cou9ar@kali:~$ cd ~/.local/lib/python2.7/site-packages/PyInstaller/utils/cliutils/
cou9ar@kali:~/.local/lib/python2.7/site-packages/PyInstaller/utils/cliutils$ ls
archive_viewer.py   bindepend.py   grab_version.py   __init__.py   makespec.py   set_version.pyc
archive_viewer.pyc  bindepend.pyc  grab_version.pyc  __init__.pyc  makespec.pyc  set_version.py
cou9ar@kali:~/.local/lib/python2.7/site-packages/PyInstaller/utils/cliutils$ python archive_viewer.py ~/Desktop/ctfs/elderly_file/encoder 
 pos, length, uncompressed, iscompressed, type, name
[(0, 171, 237, 1, 'm', u'struct'),
 (171, 1125, 2522, 1, 'm', u'pyimod01_os_path'),
 (1296, 4358, 11833, 1, 'm', u'pyimod02_archive'),
 (5654, 7480, 22272, 1, 'm', u'pyimod03_importers'),
 (13134, 1819, 5039, 1, 's', u'pyiboot01_bootstrap'),
 (14953, 261, 384, 1, 's', u'encoder'),
 (15214, 101545, 153768, 1, 'b', u'_codecs_cn.x86_64-linux-gnu.so'),
 (116759, 35126, 157896, 1, 'b', u'_codecs_hk.x86_64-linux-gnu.so'),
 (151885, 9303, 30920, 1, 'b', u'_codecs_iso2022.x86_64-linux-gnu.so'),
 (161188, 95287, 268456, 1, 'b', u'_codecs_jp.x86_64-linux-gnu.so'),
 (256475, 78351, 141480, 1, 'b', u'_codecs_kr.x86_64-linux-gnu.so'),
 (334826, 63012, 112808, 1, 'b', u'_codecs_tw.x86_64-linux-gnu.so'),
 (397838, 8493, 25160, 1, 'b', u'_hashlib.x86_64-linux-gnu.so'),
 (406331, 18210, 47304, 1, 'b', u'_multibytecodec.x86_64-linux-gnu.so'),
 (424541, 17569, 46760, 1, 'b', u'bz2.x86_64-linux-gnu.so'),
 (442110, 30884, 74688, 1, 'b', u'libbz2.so.1.0'),
 (472994, 1330156, 3040096, 1, 'b', u'libcrypto.so.1.1'),
 (1803150, 1322880, 3439088, 1, 'b', u'libpython2.7.so.1.0'),
 (3126030, 136455, 329768, 1, 'b', u'libreadline.so.8'),
 (3262485, 70589, 187776, 1, 'b', u'libtinfo.so.6'),
 (3333074, 56657, 113088, 1, 'b', u'libz.so.1'),
 (3389731, 18346, 47712, 1, 'b', u'lzss.so'),
 (3408077, 9202, 31264, 1, 'b', u'readline.x86_64-linux-gnu.so'),
 (3417279, 4180, 15232, 1, 'b', u'resource.x86_64-linux-gnu.so'),
 (3421459, 642479, 642479, 0, 'z', u'PYZ-00.pyz')]
? X encoder
to filename? extr_encoder_pyc.pyc
? Q

내부를 확인하면 encoder라는 스크립트 타입의 파일이 있는데 X 옵션으로 추출할 수 있다. 추출한 파일은 pyc이기 때문에 한번 더 작업을 해야 한다. 나는 찾아보던 중 pycdc라는 걸 발견해 이걸 이용했다.

ou9ar@kali:~/Desktop/ctfs/elderly_file$ ./pycdc/build/pycdc extr_encoder_pyc.pyc 
Bad MAGIC!
Could not load file extr_encoder_pyc.pyc

컴파일을 하고 pycdc로 방금 추출한 pyc파일을 돌리면 Bad magic이라는 메시지를 볼 수 있다. 이곳에서 pyc의 파일 구조를 볼 수 있다. 여기서 끝났어야 했지만 이때 약간 안 풀려서 시야가 좁아진듯하다. 사이트에서 produces this로 결과를 보여주는 화면에 magiccode가 있는데 이 부분을 놓쳐 한참을 돌게 되었다. 그리고 파일 시그니쳐는 이곳에서 확인하는데 2.7의 경우 03 F3 0D 0A이고 파일 구조에 매직 코드 다음은 수정된 날짜를 가지니 적당하게 4byte의 timestamp를 작성하면 된다.

cou9ar@kali:~/Desktop/ctfs/elderly_file$ xxd -l30 repair_extr_encoder_pyc.pyc 
00000000: 03f3 0d0a 0000 0000 6300 0000 0000 0000  ........c.......
00000010: 0004 0000 0040 0000 0073 8500 0000       .....@...s....
cou9ar@kali:~/Desktop/ctfs/elderly_file$ ./pycdc/build/pycdc repair_extr_encoder_pyc.pyc 
# Source Generated with Decompyle++
# File: repair_extr_encoder_pyc.pyc (Python 2.7)

import lzss
import sys
if __name__ == '__main__':
    if len(sys.argv) > 1:
        file_path = sys.argv[1]
    else:
        print 'Usage: ' + sys.argv[0] + ' <file-path>'
        sys.exit(-1)
    lzss.encode_file(file_path, file_path + '.enc')

출력된 내용을 보면 lzss 모듈의 encode_file로 암호화되어 lzss.decode_file로 풀면 원본 파일을 구할 수 있다.

cou9ar@kali:~/Desktop/ctfs/elderly_file$ xxd -l128 dec_file 
00000000: 3a31 3030 3030 3030 3037 4634 3534 4334  :100000007F454C4
00000010: 3630 3130 3130 3130 3030 3030 3030 3030  6010101000000000
00000020: 3030 3030 3030 3030 3039 370d 0a3a 3130  00000000097..:10
00000030: 3030 3130 3030 3033 3030 3033 3030 3031  0010000300030001
00000040: 3030 3030 3030 3530 3130 3030 3030 3334  0000005010000034
00000050: 3030 3030 3030 3435 0d0a 3a31 3030 3032  00000045..:10002
00000060: 3030 3035 3833 3830 3030 3030 3030 3030  0005838000000000
00000070: 3030 3033 3430 3032 3030 3030 4230 3032  000340020000B002

그런데 그게 끝이 아니라 해체된 파일을 보면 이상한 형태로 되어있는데 이것 또한 힌트를 받아 intel hex file이라는 걸 알게 되었다.

cou9ar@kali:~/Desktop/ctfs/elderly_file$ cut -c 10- dec_file | sed 's/...$//' | tr -d '\n' | xxd -r -p > run.bin
cou9ar@kali:~/Desktop/ctfs/elderly_file$ xxd -l128 run.bin 
00000000: 7f45 4c46 0101 0100 0000 0000 0000 0000  .ELF............
00000010: 0300 0300 0100 0000 5010 0000 3400 0000  ........P...4...
00000020: 5838 0000 0000 0000 3400 2000 0b00 2800  X8......4. ...(.
00000030: 1e00 1d00 0600 0000 3400 0000 3400 0000  ........4...4...
00000040: 3400 0000 6001 0000 6001 0000 0400 0000  4...`...`.......
00000050: 0400 0000 0300 0000 9401 0000 9401 0000  ................
00000060: 9401 0000 1300 0000 1300 0000 0400 0000  ................
00000070: 0100 0000 0100 0000 0000 0000 0000 0000  ................
cou9ar@kali:~/Desktop/ctfs/elderly_file$ strings run.bin 
...
Aero{33d8b218a9961657b74c5036fe44527a02ce03c4da34f8a1cda5f2188c23a1b5}
...

힌트를 받지 않았더라면 한참 고민했어야 할 문제였다. 시간도 많이 초과하고 힌트를 너무 많이 받아서 좋은 결과는 없을 듯 하지만 오랜만에 자극을 받는 시간이었다.


 

 

'🛡️ 보안 > 📒 문제 풀이' 카테고리의 다른 글

Pyinstaller로 작성된 파일 분석하기. 🐍  (0) 2020.03.03
HackCTF - #x64_Simple_size_BOF  (0) 2019.10.28
HackCTF - #basic_FSB 외 2개  (0) 2019.10.26
HackCTF - basic BOF #1 #2  (0) 2019.10.26
단순한 로그인  (0) 2019.05.15
UUTCTF2019 Again find the flag  (0) 2019.05.05

블로그의 정보

🦒 Juraffe's note

juraffe juraffe

활동하기