Forensics

Zone Identifier - Export Tool

pental 2021. 5. 28. 20:56

인터넷에서 다운로드 받은 파일이 악성코드일 가능성은 언제나 있다.

그래서 브라우저는 파일을 다운로드 받을 때 NTFS의 ADS 영역에 이 파일을 어디서 다운로드 받았는지에 관한 정보를 저장한다.

이 정보의 이름을 Zone.Identifier 라고 하며, 이 정보는 보안 프로그램에 의해 사용된다.


 

각 파일의 Zone.Identifier 값으로 다운로드 여부를 판단하며 ZoneId 값이 3이면 인터넷으로 부터 다운로드 된 파일이라고 볼수 있다. Zone.Identifier 은 ADS 의 일종으로 아래와 값은 값들을 가진다.

Zone.Identifier
ZoneId=1 Local Intra Net (로컬 인트라넷)
ZoneId=2 Urlzone_Trusted (신뢰 할 수 있는 사이트)
ZoneId=3 Internet (인터넷)
ZoneId=4 Urlzone_Untrusted (제한된 사이트)

예를 들어서 파일을 하나 다운 받고 커맨드 창에 다음과 같이 입력한다.

more < [File Name]:Zone.Identifier

파일명 뒤에 ":Zone.Identifier" 라는 문자열을 붙여 입력하면, 다음과 같이 해당 파일을 어디서 다운로드 받았는지에 관한 정보가 표시되는 것을 확인할 수 있다.


도구 제작

import os
import csv
import subprocess
from pathlib import Path
import pandas as pd

downloads_path = str(Path.home() / "Downloads")
print(downloads_path)

file_list = os.listdir(downloads_path)

temp = []
for i in range(len(file_list)) :
    query = "more < " + downloads_path + "\\\"" + file_list[i] + "\":Zone.Identifier"
    sysMsg = subprocess.getstatusoutput(query)
    temp.append([file_list[i], sysMsg[1]])

result = []
for i in range(len(temp)) :
    try :
        test = temp[i][1].split("\n")[2] # try
        name = temp[i][0]
        ReferrerUrl = (temp[i][1].split("\n")[2][12:])
        HostUrl = (temp[i][1].split("\n")[3][8:])
        result.append([name, ReferrerUrl, HostUrl])
    except :
        pass
print(result)
f = open('output.csv', 'w', encoding='utf-8-sig', newline='')
wr = csv.writer(f)
wr.writerow(["File Name","ReferrerUrl","HostUrl"])
for i in range(len(result)) :
    wr.writerow([result[i][0], result[i][1], result[i][2]])
f.close()

r_csv = pd.read_csv("output.csv")
save_xlsx = pd.ExcelWriter("Result.xlsx")
r_csv.to_excel(save_xlsx, index = False)
save_xlsx.save()
os.remove("output.csv")


GUI 개발

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5 import uic
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import * 
import sys
import os
import csv
import subprocess
from pathlib import Path
import pandas as pd

ui_error_path = "main.ui"
ui_error = uic.loadUiType(ui_error_path)[0]
class MainWindow(QMainWindow, ui_error):
    def __init__(self):
        super().__init__()
        self.setupUi(self)

        # Button
        self.file_button.clicked.connect(self.error_file_select)
        self.csv_button.clicked.connect(self.csv)
        self.parse_button.clicked.connect(self.parse)
        self.excel_button.clicked.connect(self.excel)
        self.exit_button.clicked.connect(self.exit_select)

    # Path Select 
    def error_file_select(self) :
        try:
            global file_path
            file_filter = 'All files (*.*)'
            file_path = QFileDialog.getOpenFileNames(self, 'Select File', filter=file_filter)
            file_path = file_path[0]

            _translate = QCoreApplication.translate
            self.tableWidget.setColumnCount(1)
            self.tableWidget.setRowCount(len(file_path))
            self.tableWidget.verticalHeader().setVisible(False)

            for i in range(len(file_path)):
                item = QTableWidgetItem()
                self.tableWidget.setVerticalHeaderItem(i, item)

            for i in range(1):
                item = QTableWidgetItem()
                self.tableWidget.setHorizontalHeaderItem(i, item)
            item = QTableWidgetItem()

            for i in range(len(file_path)):
                for j in range(1):
                    self.tableWidget.setItem(i, j, item)
                    item = QTableWidgetItem()

            for i in range(len(file_path)) :
                item = self.tableWidget.verticalHeaderItem(i)
                item.setText(_translate("MainWindow", str(i)))

            item = self.tableWidget.horizontalHeaderItem(0)
            item.setText(_translate("MainWindow", "File"))
            __sortingEnabled = self.tableWidget.isSortingEnabled()
            self.tableWidget.setSortingEnabled(False)
            self.tableWidget.setColumnWidth(0, 620)

            for i in range(len(file_path)):
                for j in range(1):
                    item = self.tableWidget.item(i, j)
                    item.setText(_translate("MainWindow", str(file_path[i])))
            self.tableWidget.setSortingEnabled(__sortingEnabled)

            table = self.tableWidget
            header = table.horizontalHeader()
            twidth = header.width()
            width = []
            for column in range(header.count()):
                header.setSectionResizeMode(column, QHeaderView.ResizeToContents)
                width.append(header.sectionSize(column))

            wfactor = twidth / sum(width)
            for column in range(header.count()):
                header.setSectionResizeMode(column, QHeaderView.Interactive)
                header.resizeSection(column, width[column]*wfactor)

        except :
            QMessageBox.information(self, "Error", "Error", QMessageBox.Ok, QMessageBox.Ok)

    def parse(self) :
        temp = []
        for i in range(len(file_path)) :
            query = "more < " + file_path[i] + ":Zone.Identifier"
            sysMsg = subprocess.getstatusoutput(query)
            temp.append([file_path[i], sysMsg[1]])
        global result
        result = []
        for i in range(len(temp)) :
            try :
                test = temp[i][1].split("\n")[2] # try
                name = temp[i][0]
                ReferrerUrl = (temp[i][1].split("\n")[2][12:])
                HostUrl = (temp[i][1].split("\n")[3][8:])
                result.append([name, ReferrerUrl, HostUrl])
            except :
                pass

        _translate = QCoreApplication.translate
        self.zone_tableWidget.setColumnCount(3)
        self.zone_tableWidget.setRowCount(len(result))
        self.zone_tableWidget.verticalHeader().setVisible(False)

        for i in range(len(result)):
            item = QTableWidgetItem()
            self.zone_tableWidget.setVerticalHeaderItem(i, item)

        for i in range(3):
            item = QTableWidgetItem()
            self.zone_tableWidget.setHorizontalHeaderItem(i, item)
        item = QTableWidgetItem()

        for i in range(len(result)):
            for j in range(3):
                self.zone_tableWidget.setItem(i, j, item)
                item = QTableWidgetItem()

        for i in range(len(result)) :
            item = self.zone_tableWidget.verticalHeaderItem(i)
            item.setText(_translate("MainWindow", str(i)))

        item = self.zone_tableWidget.horizontalHeaderItem(0)
        item.setText(_translate("MainWindow", "File"))
        item = self.zone_tableWidget.horizontalHeaderItem(1)
        item.setText(_translate("MainWindow", "ReferrerUrl"))
        item = self.zone_tableWidget.horizontalHeaderItem(2)
        item.setText(_translate("MainWindow", "HostUrl"))
        __sortingEnabled = self.zone_tableWidget.isSortingEnabled()
        self.zone_tableWidget.setSortingEnabled(False)

        for i in range(len(result)):
            for j in range(3):
                item = self.zone_tableWidget.item(i, j)
                item.setText(_translate("MainWindow", str(result[i][j])))
        self.zone_tableWidget.setSortingEnabled(__sortingEnabled)
        

        table = self.zone_tableWidget
        header = table.horizontalHeader()
        twidth = header.width()
        width = []
        for column in range(header.count()):
            header.setSectionResizeMode(column, QHeaderView.ResizeToContents)
            width.append(header.sectionSize(column))

        wfactor = twidth / sum(width)
        for column in range(header.count()):
            header.setSectionResizeMode(column, QHeaderView.Interactive)
            header.resizeSection(column, width[column]*wfactor)

    def csv(self) :
        try :
            f = open('output.csv', 'w', encoding='utf-8-sig', newline='')
            wr = csv.writer(f)
            wr.writerow(["File Name","ReferrerUrl","HostUrl"])
            for i in range(len(result)) :
                wr.writerow([result[i][0], result[i][1], result[i][2]])
            f.close()
            QMessageBox.information(self, "Success", "Export CSV Success", QMessageBox.Ok, QMessageBox.Ok)
        except :
            QMessageBox.information(self, "Fail", "Export CSV Fail", QMessageBox.Ok, QMessageBox.Ok)

    def excel(self) :
        r_csv = pd.read_csv("output.csv")
        save_xlsx = pd.ExcelWriter("Result.xlsx")
        r_csv.to_excel(save_xlsx, index = False)
        save_xlsx.save()
        os.remove("output.csv")
        QMessageBox.information(self, "Success", "Export Excel Success", QMessageBox.Ok, QMessageBox.Ok)

    def exit_select(self) :
        self.close()

# Main Function
def main():
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    app.exec_()

if __name__ == "__main__":
    main()


Referrence 

https://kr.bandisoft.com/bandizip/help/zone-identifier/

https://en.wikipedia.org/wiki/NTFS#Alternate_data_streams_(ADS) 

https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/6e3f7352-d11c-4d76-8c39-2516a9df36e8?redirectedfrom=MSDN 

https://secuworld.tistory.com/31