[Python] Checksec 결과를 엑셀 테이블로 저장해보자

wisdom·2022년 10월 5일
0

최근 참 많은 일이 있었다. 퇴사 → 신사업 준비 → 취업 준비 .. 다사다난했지만 잠깐 쉬어가는 타이밍이라 생각하니 마음이 편안하다. 오히려 좋아!

딱히 조급한 마음도 없고, 내 자신에 대한 확신도 있고, 기쁜 일들도 많고, 그래서 요즘이 가장 행복한 시기인 것 같다. 그리고 좋은 사람들 덕분에 이런 저런 제안들도 많이 들어오고 있다. 감사한 일이다. 면접만 잘 본다면 올 하반기에 취업할 수 있을 것 같은데, 내가 하기 나름이니까 열심히 해봐야지.

주로 노션을 쓰다보니 Velog 관리를 소홀히 하게 된다. oopy로 노션 자체를 호스팅 해야 하나 싶기도 하고 .. 매번 포스팅을 옮기는 과정이 생각보다 귀찮아서 고민을 좀 해봐야겠다.

Challenge

보안 업무를 하다 보면, 가끔 바이너리 보호 기법을 리스팅해야 하는 일이 생기기도 한다. 그럴 때 주로 사용하는 도구가 checksec이며, PIE, RELRO, Canaries, ASLR, Fortify Source의 활성화 여부를 확인해주는 bash 스크립트이다. 나는 CTF 플레이어가 아니지만, CTF 할 때 가장 많이 사용하는 듯하다.

아무쪼록 다음과 같은 checksec 수행 결과를 엑셀 테이블로 변환하여 저장하는 코드를 작성해 보자.

Install

$ pip install openpyxl # python
$ pip3 install openpyxl # python3

openpyxl 패키지는 엑셀 작업을 수행할 수 있도록 도와주는 파이썬 패키지이다.

Solution

CanaryPIE가 없는 것만을 파싱하여 기재하고, 나머지는 -로 표기할 것이다. 해당 코드를 실행하기 전에 checksec 수행 결과를 log.txt라는 파일로 저장해 놓아야 한다.

import openpyxl
import re

class Excel:
    def __init__(self):
        self.workbook = openpyxl.Workbook()
        self.sheet = self.workbook.active
        self.initialize()

    def initialize(self):
        self.row = 2

        title_list = ['No.', 'FILENAME', 'RELRO', 'STACK CANARY', 'NX', 'PIE', 'RPATH', 'RUNPATH']
        ascii_list = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
        index = 1

        for title in title_list:
            self.sheet.cell(row = 1, column = index).value = title
            index = index + 1

        for ascii in ascii_list:
            if ascii == 'B':
                width = 40
            elif ascii == 'A':
                width = 5
            else:
                width = 20
            self.sheet.column_dimensions[ascii].width = width

    def parse(self):
        f = open('log.txt', 'r')
        data = f.read()
        data_split = data.split('\n')[1::2]
        return data_split
    
    def write(self, filename, flag):
        if flag['Canary'] and flag['PIE']:
            return
        
        self.sheet.cell(row = self.row, column = 2).value = filename

        if not flag['Canary']:
            self.sheet.cell(row = self.row, column = 4).value = 'No canary found'
        else:
            self.sheet.cell(row = self.row, column = 4).value = '-'
        
        if not flag['PIE']:
            self.sheet.cell(row = self.row, column = 6).value = 'No PIE'
        else:
            self.sheet.cell(row = self.row, column = 6).value = '-'

        if not flag['RELRO']:
            self.sheet.cell(row = self.row, column = 3).value = 'No RELRO'
        else:
            self.sheet.cell(row = self.row, column = 3).value = '-'

        if not flag['NX']:
            self.sheet.cell(row = self.row, column = 5).value = 'NX disabled'
        else:
            self.sheet.cell(row = self.row, column = 5).value = '-'

        if not flag['RPATH']:
            self.sheet.cell(row = self.row, column = 7).value = 'No RPATH'
        else:
            self.sheet.cell(row = self.row, column = 7).value = '-'

        if not flag['RUNPATH']:
            self.sheet.cell(row = self.row, column = 8).value = 'No RUNPATH'
        else:
            self.sheet.cell(row = self.row, column = 8).value = '-'
        
        self.sheet.cell(row=self.row, column = 1).value = self.row - 1
        # self.sheet.cell(row=self.row, column = 3).value = '-'
        # self.sheet.cell(row=self.row, column = 5).value = '-'
        # self.sheet.cell(row=self.row, column = 7).value = '-'
        # self.sheet.cell(row=self.row, column = 8).value = '-'
        
        self.row = self.row + 1
        print(f'[*] {filename} : row {self.row} added')

    def set_flag(self, line):
        flag = {
            'RELRO' : True,
            'Canary' : True,
            'NX' : True,
            'PIE' : True,
            'RPATH' : True,
            'RUNPATH' : True
        }

        if 'No canary found' in line:
            flag['Canary'] = False
        
        if 'No PIE' in line:
            flag['PIE'] = False

        if 'No RELRO' in line:
            flag['RELRO'] = False

        if 'NX disabled' in line:
            flag['NX'] = False

        if 'No RPATH' in line:
            flag['RPATH'] = False

        if 'No RUNPATH' in line:
            flag['RUNPATH'] = False

        return flag

    def check(self, line):
        filename = re.split(r' {2,}', line)[-1].split('\t')[-1].split('/')[-1]
        flag = self.set_flag(line)

        self.write(filename, flag)

    def run(self):
        data_split = self.parse()
        for line in data_split:
            self.check(line)
        self.save()

    def save(self):
        self.workbook.save('result.xlsx')


if __name__ == "__main__":
    excel = Excel()
    excel.run()

실행 흐름을 따라가며 각 함수의 기능에 대해 알아보도록 하자.

run

def run(self):
    data_split = self.parse()
    for line in data_split:
        self.check(line)
    self.save()

Excel 클래스 생성 후 가장 먼저 호출하는 함수. 모듈화 해 놓은 각 기능들을 한 번에 호출하는 용도로 만들어 두었다. parse, check, save 순으로 실행됨.

parse

def parse(self):
    f = open('log.txt', 'r')
    data = f.read()
    data_split = data.split('\n')[1::2]
    return data_split

log.txt 파일을 열어서 개행문자 \n를 기준으로 데이터를 쪼갠다. 참고로 아래 사진을 보면 두 줄 간격으로 의미 있는 데이터가 존재한다. 따라서 [1::2]과 같이 간격을 두고 split 하여 저장하였다.

check

def check(self, line):
    filename = re.split(r' {2,}', line)[-1].split('\t')[-1].split('/')[-1]
    flag = self.set_flag(line)
    self.write(filename, flag)

정규표현식으로 파일 이름을 가져온 뒤, set_flag 함수를 호출하여 보호 기법 활성화 여부를 체크한다. 그리고 write 함수에서 self.sheet.cell(row = self.row, column = N).value의 값을 지정해준다. (각 셀에 데이터를 채우는 과정임)

save

def save(self):
    self.workbook.save('result.xlsx')

self.workbookopenpyxl 패키지에서 제공되는 객체이다. workbook: 엑셀 파일, worksheet: 엑셀 시트 cell: 엑셀 한 칸 이라고 생각하면 될 것 같다. 아무튼 result.xlsx라는 파일로 저장하면 모든 과정은 끝난다.

Result

파일명은 가렸고, 다음과 같이 CanaryPIE가 비활성화 된 바이너리 리스트만 출력할 수 있었다. 이렇게 필터링 해서 뽑아놓고 세부적으로 점검하면 되겠지. python으로 툴링하는 게 왜 이렇게 재미있는지 모르겠다. 마법같은 언어야.

profile
블로그 이전 -> wisdom-lee.xyz

0개의 댓글