├── README.md ├── TPLogScan.py ├── TPLogScanBurpExtender.py ├── images ├── 1.png ├── 2.png ├── 3.png ├── 4.png ├── 5.png └── 6.png ├── log.jpg └── requirements.txt /README.md: -------------------------------------------------------------------------------- 1 | ThinkPHP3和5日志扫描工具,提供命令行版和BurpSuite插件版,尽可能全的发掘网站日志信息 2 | 3 | ## 命令行版 4 | 5 | ### 安装 6 | 7 | ``` 8 | git clone https://github.com/r3change/TPLogScan.git 9 | cd TPLogScan/ 10 | pip install -r requirements.txt 11 | ``` 12 | 13 | ### 使用 14 | 15 | ``` 16 | > python3 TPLogScan.py -h 17 | 18 | usage: TPLogScan.py [-h] [-u URL] [-v {3,5}] [-p PATH] [-y YEAR] [-m MONTH] 19 | [-d DAY] 20 | 21 | Thinkphp 3 or 5 log file scan! 22 | 23 | optional arguments: 24 | -h, --help show this help message and exit 25 | -u URL, --url URL target url 26 | -v {3,5}, --version {3,5} 27 | thinkphp version, default 3 28 | -p PATH, --path PATH log path 29 | -y YEAR, --year YEAR datetime start year, default this year 30 | -m MONTH, --month MONTH 31 | datetime start month, default this month 32 | -d DAY, --day DAY datetime start day, default 1 33 | ``` 34 | 35 | 扫描TP3 1月-本月的网站所有日志 36 | ``` 37 | python3 TPLogScan.py -u URL 38 | ``` 39 | 40 | 扫描TP5 12月的网站所有日志 41 | ``` 42 | python3 TPLogScan.py -u URL -v 5 -m 12 43 | ``` 44 | 45 | 指定日志路径扫描(需同时指定ThinkPHP版本) 46 | ``` 47 | python3 TPLogScan.py -u URL -v 5 -p /runtime/log/ -m 12 -d 25 48 | ``` 49 | 50 | 扫描截图 51 | 52 | ![](./images/1.png) 53 | 54 | 55 | 扫描结果自动保存在 `/TPLogData/{URL}/` 目录下 56 | 57 | ![日志结果](log.jpg) 58 | 59 | 60 | 61 | ## BurpSuite插件版 62 | 63 | ### 安装 64 | 65 | 插件使用Python编写,使用需要先下载 Jython,并在 BurpSuite 中配置 66 | 67 | Jython下载地址 68 | ``` 69 | https://www.jython.org/download.html 70 | ``` 71 | 72 | 配置Jython环境 73 | 74 | ![](./images/2.png) 75 | 76 | 添加插件 77 | 78 | ![](./images/3.png) 79 | 80 | 插件添加后会新建一个标签页面板 81 | 82 | ![](./images/4.png) 83 | 84 | ### 使用 85 | 86 | 右键菜单选择TPLogScan,根据ThinkPHP版本选择子菜单项,选择后会自动爬取近30天的日志,会将请求记录在新建的面板中 87 | 88 | ![](./images/5.png) 89 | ![](./images/6.png) 90 | 91 | -------------------------------------------------------------------------------- /TPLogScan.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | import requests 3 | import os 4 | import datetime 5 | import time 6 | import argparse 7 | import sys 8 | import re 9 | from colorama import init,Fore 10 | 11 | init(autoreset=True) 12 | 13 | def genFileName(version, year, month, day): 14 | now_year = datetime.datetime.now().year 15 | now_month = datetime.datetime.now().month 16 | now_day = datetime.datetime.now().day 17 | begin_date = datetime.date(year, month, day) 18 | end_date = datetime.date(now_year, now_month, now_day) 19 | date_list = [begin_date + datetime.timedelta(days=i) for i in range((end_date - begin_date).days+1)] 20 | filename_list = [] 21 | for date in date_list: 22 | if version == 3: 23 | filename_list.append("{:0>2d}_{:0>2d}_{:0>2d}.log".format(int(str(date.year)[2:]), date.month, date.day)) 24 | elif version == 5: 25 | filename_list.append("{}{:0>2d}/{:0>2d}.log".format(date.year, date.month, date.day)) 26 | return filename_list 27 | 28 | def sendReq(url): 29 | try: 30 | html = '' 31 | response = requests.get(url, headers=headers, timeout=20) 32 | if response.status_code == 200: 33 | print('{}[*] {}{reset} | {}{}{reset}'.format(Fore.RED, url, Fore.BLUE, response.status_code, reset=Fore.RESET)) 34 | html = response.content.decode('utf-8') 35 | else: 36 | print('[-] {} | {}{}{reset}'.format(url, Fore.BLUE, response.status_code, reset=Fore.RESET)) 37 | except Exception as e: 38 | print('[-] {} | request error'.format(url)) 39 | finally: 40 | return html 41 | 42 | def foundLogPath(url, version): 43 | log_path_list = { 44 | 3: ['/Runtime/Logs/', '/App/Runtime/Logs/', '/Application/Runtime/Logs/Admin/', '/Application/Runtime/Logs/Home/', '/Application/Runtime/Logs/'], 45 | 5: '/runtime/log/', 46 | } 47 | 48 | log_path = '' 49 | if version == 3: 50 | for path in log_path_list[version]: 51 | if requests.get(url+path, headers=headers).status_code in (200,403): 52 | print('{}[*]{reset} Found {} log path: {}'.format(Fore.RED, url, path, reset=Fore.RESET)) 53 | log_path = path 54 | break 55 | else: 56 | if requests.get(url+log_path_list[version], headers=headers).status_code in (200,403): 57 | print('{}[*]{reset} Found {} log path: {}'.format(Fore.RED, url, log_path_list[version], reset=Fore.RESET)) 58 | log_path = log_path_list[version] 59 | 60 | return log_path 61 | 62 | if __name__ == "__main__": 63 | parser = argparse.ArgumentParser(description="Thinkphp 3 or 5 log file scan! ") 64 | parser.add_argument('-u', '--url', help='target url') 65 | parser.add_argument('-v', '--version', type=int, 66 | choices=[3, 5], default=3, help="thinkphp version, default 3") 67 | parser.add_argument('-p', '--path', type=str, help='log path') 68 | parser.add_argument('-y', '--year', type=int, default=datetime.datetime.now().year, help="datetime start year, default this year") 69 | parser.add_argument('-m', '--month', type=int, default=datetime.datetime.now().month, help="datetime start month, default this month") 70 | parser.add_argument('-d', '--day', type=int, default=1, help="datetime start day, default 1") 71 | 72 | args = parser.parse_args() 73 | if not args.url: 74 | parser.print_help() 75 | sys.exit(0) 76 | 77 | url = args.url 78 | version = args.version 79 | log_path = args.path 80 | year = args.year 81 | month = args.month 82 | day = args.day 83 | 84 | 85 | headers = { 86 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0' 87 | } 88 | 89 | 90 | if not log_path: 91 | log_path = foundLogPath(url, version) 92 | if not log_path: 93 | print("[-] {} can't get log file! ".format(url)) 94 | sys.exit(0) 95 | 96 | filename_list = genFileName(version, year, month, day) 97 | 98 | dir_name = url.replace('https://', '').replace('http://', '').replace('/', '').replace(':', '_') 99 | data_path = 'TPLogData' 100 | if not os.path.isdir(data_path): 101 | os.mkdir(data_path) 102 | dir_path = os.path.join(data_path, dir_name) 103 | if not os.path.isdir(dir_path): 104 | os.mkdir(dir_path) 105 | 106 | for filename in filename_list: 107 | try: 108 | html = sendReq(url+log_path+filename) 109 | if not html: 110 | continue 111 | with open(os.path.join(dir_path, filename.replace('/', '_')), 'w', encoding='utf-8') as f: 112 | f.write(html) 113 | tmp_filename = filename 114 | now_filename = '' 115 | pattern = re.compile(r"\[ (\d{4}-\d{2}-\d{2})T((\d{2}:){2}\d{2})\+08:00 \]") 116 | flag = True 117 | while flag: 118 | match_result = pattern.search(html) 119 | if not match_result: 120 | break 121 | time_str = match_result.group(1) + ' ' + match_result.group(2) 122 | timeArray = time.strptime(time_str, "%Y-%m-%d %H:%M:%S") 123 | timestamp = int(time.mktime(timeArray)) 124 | timestamp_list = [str(timestamp), str(timestamp-1), str(timestamp-2), str(timestamp-3)] 125 | for timestamp in timestamp_list: 126 | if version == 3: 127 | tmp_filename = timestamp + '-' + filename 128 | else: 129 | tmp_filename = filename[:filename.find('/')] + '/' + timestamp + '-' + filename[filename.find('/')+1:] 130 | if tmp_filename == now_filename: 131 | flag = False 132 | break 133 | html = sendReq(url+log_path+tmp_filename) 134 | if html: 135 | with open(os.path.join(dir_path, tmp_filename.replace('/', '_')), 'w', encoding='utf-8') as f: 136 | f.write(html) 137 | now_filename = tmp_filename 138 | break 139 | except Exception as e: 140 | print("[-] error: {}".format(e)) 141 | print('[*] Log Scan complete!') 142 | -------------------------------------------------------------------------------- /TPLogScanBurpExtender.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | import os 3 | import datetime 4 | import time 5 | import re 6 | import urllib2 7 | import urlparse 8 | import threading 9 | from threading import Lock 10 | 11 | from burp import IBurpExtender 12 | from burp import IContextMenuFactory 13 | from burp import ITab 14 | from burp import IHttpListener 15 | from burp import IMessageEditorController 16 | 17 | from javax.swing import JMenu 18 | from javax.swing import JMenuItem 19 | from javax.swing import JPanel 20 | from javax.swing import JScrollPane 21 | from javax.swing import JSplitPane 22 | from javax.swing import JTabbedPane 23 | from javax.swing import ScrollPaneConstants 24 | from javax.swing import SwingUtilities 25 | from javax.swing import JTable 26 | from javax.swing.table import AbstractTableModel 27 | 28 | from java.awt import BorderLayout, GridLayout 29 | from java.awt.event import ComponentEvent 30 | from java.awt.event import ComponentListener 31 | 32 | from java.io import PrintWriter 33 | from java.util import Vector 34 | from java.util import ArrayList 35 | 36 | class BurpExtender(IBurpExtender, IContextMenuFactory, ITab, IHttpListener, IMessageEditorController, AbstractTableModel): 37 | def registerExtenderCallbacks(self, callbacks): 38 | self.messages = [] 39 | 40 | self._callbacks = callbacks 41 | self._helpers = callbacks.getHelpers() 42 | self._callbacks.setExtensionName('TPLogScan') 43 | 44 | self._log = ArrayList() 45 | self._lock = Lock() 46 | 47 | self.jSplitPaneV = JSplitPane(JSplitPane.VERTICAL_SPLIT, True) 48 | self.jSplitPaneV.setDividerLocation(300) 49 | self.jSplitPaneV.setOneTouchExpandable(True) 50 | 51 | self.jPanel_top = JPanel() 52 | self.jTabbedPane = JTabbedPane(JTabbedPane.TOP) 53 | 54 | self.iRequestTextEditor = self._callbacks.createMessageEditor(self, False) 55 | self.iResponseTextEditor = self._callbacks.createMessageEditor(self, False) 56 | 57 | self.jTable = CustomTable(self) 58 | self.jTable.setShowGrid(True) 59 | self.jTable.setAutoCreateRowSorter(True) 60 | self.jTable.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS) 61 | 62 | first_column_model = self.jTable.getColumnModel().getColumn(0) 63 | first_column_model.setPreferredWidth(60); 64 | first_column_model.setMaxWidth(60) 65 | first_column_model.setMinWidth(60) 66 | self.jTable.getColumnModel().getColumn(1).setPreferredWidth(300) 67 | 68 | third_column_model = self.jTable.getColumnModel().getColumn(2) 69 | third_column_model.setPreferredWidth(100) 70 | third_column_model.setMinWidth(100) 71 | self.jTable.getColumnModel().getColumn(3).setPreferredWidth(600) 72 | self.jTable.getColumnModel().getColumn(4).setPreferredWidth(100) 73 | self.jTable.getColumnModel().getColumn(5).setPreferredWidth(100) 74 | 75 | self.jScrollPane1 = JScrollPane(self.jTable) 76 | self.jScrollPane1.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED) 77 | self.jScrollPane1.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED) 78 | 79 | self.jTabbedPane.addTab("Log", self.jScrollPane1) 80 | 81 | self.jPanel_top.add(self.jTabbedPane) 82 | self.jPanel_top.setLayout(GridLayout(1,1)) 83 | 84 | self.jSplitPaneInfo = JSplitPane(JSplitPane.HORIZONTAL_SPLIT, True) 85 | self.jSplitPaneInfo.setDividerLocation(650) 86 | self.jSplitPaneInfo.setOneTouchExpandable(True) 87 | 88 | self.jPanel_reqInfo_left = JPanel() 89 | self.jPanel_respInfo_right = JPanel() 90 | 91 | self.jPanel_reqInfo_left.setLayout(BorderLayout()) 92 | self.jPanel_respInfo_right.setLayout(BorderLayout()) 93 | 94 | self.jPanel_reqInfo_left.add(self.iRequestTextEditor.getComponent(), 95 | BorderLayout.CENTER) 96 | self.jPanel_respInfo_right.add(self.iResponseTextEditor.getComponent(), 97 | BorderLayout.CENTER) 98 | 99 | self.jSplitPaneInfo.add(self.jPanel_reqInfo_left, JSplitPane.LEFT) 100 | self.jSplitPaneInfo.add(self.jPanel_respInfo_right, JSplitPane.RIGHT) 101 | 102 | self.jSplitPaneV.add(self.jPanel_top, JSplitPane.TOP) 103 | self.jSplitPaneV.add(self.jSplitPaneInfo, JSplitPane.BOTTOM) 104 | 105 | self._callbacks.customizeUiComponent(self.jSplitPaneV) 106 | self._callbacks.customizeUiComponent(self.jPanel_top) 107 | self._callbacks.customizeUiComponent(self.jTabbedPane) 108 | self._callbacks.customizeUiComponent(self.jTable) 109 | self._callbacks.customizeUiComponent(self.jScrollPane1) 110 | self._callbacks.customizeUiComponent(self.jSplitPaneInfo) 111 | self._callbacks.customizeUiComponent(self.jPanel_reqInfo_left) 112 | self._callbacks.customizeUiComponent(self.jPanel_respInfo_right) 113 | self._callbacks.addSuiteTab(self) 114 | 115 | 116 | self._callbacks.registerHttpListener(self) 117 | self._callbacks.registerContextMenuFactory(self) 118 | 119 | return 120 | 121 | def getTabCaption(self): 122 | return 'TPLogScan' 123 | 124 | def getUiComponent(self): 125 | return self.jSplitPaneV 126 | 127 | 128 | def getRowCount(self): 129 | try: 130 | return self._log.size() 131 | except: 132 | return 0 133 | 134 | def getColumnCount(self): 135 | return 6 136 | 137 | def getColumnName(self, columnIndex): 138 | if columnIndex == 0: 139 | return "#" 140 | if columnIndex == 1: 141 | return "Host" 142 | if columnIndex == 2: 143 | return "Method" 144 | if columnIndex == 3: 145 | return "URL" 146 | if columnIndex == 4: 147 | return "Status" 148 | if columnIndex == 5: 149 | return "Length" 150 | return "" 151 | 152 | def getValueAt(self, rowIndex, columnIndex): 153 | logEntry = self._log.get(rowIndex) 154 | url = logEntry._url.toString() 155 | url_parse = urlparse.urlparse(url) 156 | if url_parse.netloc.find(':') != -1: 157 | netloc = url_parse.netloc[:url_parse.netloc.find(':')] 158 | host = url_parse.scheme + '://' + netloc 159 | path = url_parse.path 160 | if columnIndex == 0: 161 | return rowIndex+1 162 | if columnIndex == 1: 163 | return host 164 | if columnIndex == 2: 165 | return logEntry._method 166 | if columnIndex == 3: 167 | return path 168 | if columnIndex == 4: 169 | return logEntry._status_code 170 | if columnIndex == 5: 171 | return logEntry._length 172 | return "" 173 | 174 | def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo): 175 | # tool_name = self._callbacks.getToolName(toolFlag) 176 | # if tool_name != 'Extender': 177 | if toolFlag != 1024: 178 | return 179 | if messageIsRequest: 180 | return 181 | request_info = self._helpers.analyzeRequest(messageInfo) 182 | response_info = self._helpers.analyzeResponse(messageInfo.getResponse()) 183 | response_headers = response_info.getHeaders() 184 | response_length = 0 185 | for header in response_headers: 186 | header = header.encode('utf-8') 187 | if header.startswith("Content-Length"): 188 | response_length = int(header.replace('Content-Length: ', '')) 189 | 190 | length = response_length if response_length > 0 else 0 191 | 192 | self._lock.acquire() 193 | row = self._log.size() 194 | self._log.add(LogEntry(toolFlag, self._callbacks.saveBuffersToTempFiles(messageInfo), request_info.getUrl(),request_info.getMethod(),response_info.getStatusCode(),length)) 195 | self.fireTableRowsInserted(row, row) 196 | self._lock.release() 197 | 198 | 199 | def getHttpService(self): 200 | return self._currentlyDisplayedItem.getHttpService() 201 | 202 | def getRequest(self): 203 | return self._currentlyDisplayedItem.getRequest() 204 | 205 | def getResponse(self): 206 | return self._currentlyDisplayedItem.getResponse() 207 | 208 | def loadMenus(self): 209 | self.menus = [] 210 | self.mainMenu = JMenu("TPLogScan") 211 | self.menus.append(self.mainMenu) 212 | menu = JMenuItem('ThinkPHP v3', None, actionPerformed=lambda x: self.eventHandler(x)) 213 | self.mainMenu.add(menu) 214 | menu = JMenuItem('ThinkPHP v5', None, actionPerformed=lambda x: self.eventHandler(x)) 215 | self.mainMenu.add(menu) 216 | 217 | def createMenuItems(self, invocation): 218 | self.loadMenus() 219 | self.messages = invocation.getSelectedMessages() 220 | return self.menus if self.menus else None 221 | 222 | def eventHandler(self, x): 223 | menuName = x.getSource().text 224 | if menuName == 'ThinkPHP v3': 225 | version = 3 226 | elif menuName == 'ThinkPHP v5': 227 | version = 5 228 | else: 229 | print("chose error") 230 | return 231 | 232 | for message in self.messages: 233 | url = str(self._helpers.analyzeRequest(message).getUrl()) 234 | url_parse = urlparse.urlparse(url) 235 | url = url_parse.scheme + '://' + url_parse.netloc 236 | print("[*] url: {}".format(url)) 237 | 238 | datetime_now = datetime.datetime.now() 239 | year = (datetime_now - datetime.timedelta(days=30)).year 240 | month = (datetime_now - datetime.timedelta(days=30)).month 241 | day = (datetime_now - datetime.timedelta(days=30)).day 242 | 243 | tplogscan = TPLogScan(url, version, year, month, day) 244 | log_path = tplogscan.checkLogPath() 245 | if not log_path: 246 | print("[-] {} can't get log file! ".format(url)) 247 | self._callbacks.issueAlert("{} can't get log file".format(url)) 248 | return 249 | 250 | filename_list = tplogscan.genFileName() 251 | t = threading.Thread(target=self.logScan, args=(message, version, log_path, filename_list)) 252 | t.start() 253 | 254 | def logScan(self, message, version, log_path, filename_list): 255 | http_service = message.getHttpService() 256 | old_request = self._helpers.bytesToString(message.getRequest()) 257 | old_path = self._helpers.analyzeRequest(message).getUrl().getPath() 258 | for filename in filename_list: 259 | try: 260 | new_request = old_request.replace(" " + old_path + " HTTP/", " " + log_path+filename + " HTTP/") 261 | response, status_code = self.sendRequest(http_service, new_request) 262 | if status_code != 200: 263 | continue 264 | tmp_filename = filename 265 | now_filename = '' 266 | pattern = re.compile(r"\[ (\d{4}-\d{2}-\d{2})T((\d{2}:){2}\d{2})\+08:00 \]") 267 | flag = True 268 | while flag: 269 | match_result = pattern.search(response) 270 | if not match_result: 271 | break 272 | time_str = match_result.group(1) + ' ' + match_result.group(2) 273 | timeArray = time.strptime(time_str, "%Y-%m-%d %H:%M:%S") 274 | timestamp = int(time.mktime(timeArray)) 275 | timestamp_list = [str(timestamp), str(timestamp-1), str(timestamp-2), str(timestamp-3)] 276 | for timestamp in timestamp_list: 277 | if version == 3: 278 | tmp_filename = timestamp + '-' + filename 279 | else: 280 | tmp_filename = filename[:filename.find('/')] + '/' + timestamp + '-' + filename[filename.find('/')+1:] 281 | if tmp_filename == now_filename: 282 | flag = False 283 | break 284 | new_request = old_request.replace(" " + old_path + " HTTP/", " " + log_path+tmp_filename + " HTTP/") 285 | response, status_code = self.sendRequest(http_service, new_request) 286 | if status_code == 200: 287 | now_filename = tmp_filename 288 | break 289 | except Exception as e: 290 | print("[-] error: {}".format(e)) 291 | print('[*] Log Scan complete!') 292 | 293 | def sendRequest(self, http_service, new_request): 294 | checkRequestResponse = self._callbacks.makeHttpRequest(http_service, self._helpers.stringToBytes(new_request)) 295 | status_code = self._helpers.analyzeResponse(checkRequestResponse.getResponse()).getStatusCode() 296 | print('[*] {} | {}'.format(self._helpers.analyzeRequest(checkRequestResponse).getUrl(), status_code)) 297 | return self._helpers.bytesToString(checkRequestResponse.getResponse()), status_code 298 | 299 | 300 | class CustomTable(JTable): 301 | def __init__(self, extender): 302 | self._extender = extender 303 | self.setModel(extender) 304 | 305 | def changeSelection(self, row, col, toggle, extend): 306 | logEntry = self._extender._log.get(self.convertRowIndexToModel(row)) 307 | self._extender.iRequestTextEditor.setMessage(logEntry._requestResponse.getRequest(), True) 308 | self._extender.iResponseTextEditor.setMessage(logEntry._requestResponse.getResponse(), False) 309 | self._extender._currentlyDisplayedItem = logEntry._requestResponse 310 | 311 | JTable.changeSelection(self, row, col, toggle, extend) 312 | 313 | 314 | class LogEntry: 315 | def __init__(self, tool, requestResponse, url, method, status_code, length): 316 | self._tool = tool 317 | self._requestResponse = requestResponse 318 | self._url = url 319 | self._method = method 320 | self._status_code = status_code 321 | self._length = length 322 | 323 | 324 | class TPLogScan(): 325 | def __init__(self, url, version, year, month, day): 326 | self.url = url 327 | self.version = version 328 | self.year = year 329 | self.month = month 330 | self.day = day 331 | self.headers = { 332 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0' 333 | } 334 | 335 | def genFileName(self): 336 | version = self.version 337 | year = self.year 338 | month = self.month 339 | day = self.day 340 | now_year = datetime.datetime.now().year 341 | now_month = datetime.datetime.now().month 342 | now_day = datetime.datetime.now().day 343 | begin_date = datetime.date(year, month, day) 344 | end_date = datetime.date(now_year, now_month, now_day) 345 | date_list = [begin_date + datetime.timedelta(days=i) for i in range((end_date - begin_date).days+1)] 346 | filename_list = [] 347 | for date in date_list: 348 | if version == 3: 349 | filename_list.append("{:0>2d}_{:0>2d}_{:0>2d}.log".format(int(str(date.year)[2:]), date.month, date.day)) 350 | elif version == 5: 351 | filename_list.append("{}{:0>2d}/{:0>2d}.log".format(date.year, date.month, date.day)) 352 | return filename_list 353 | 354 | def checkLogPath(self): 355 | url = self.url 356 | version = self.version 357 | log_path_list = { 358 | 3: ['/Runtime/Logs/', '/App/Runtime/Logs/', '/Application/Runtime/Logs/Admin/', '/Application/Runtime/Logs/Home/', '/Application/Runtime/Logs/'], 359 | 5: '/runtime/log/', 360 | } 361 | 362 | log_path = '' 363 | if version == 3: 364 | for path in log_path_list[version]: 365 | try: 366 | response = '' 367 | request = urllib2.Request(url+path, headers=self.headers) 368 | response = urllib2.urlopen(request, timeout=20) 369 | print('[*] Found {} log path: {} | status_code: {}'.format(url, path, response.code)) 370 | log_path = path 371 | break 372 | except urllib2.URLError as e: 373 | if hasattr(e, 'code'): 374 | # print 'Error code:',e.code 375 | if e.code == 403: 376 | print('[*] Found {} log path: {} | status_code: {}'.format(url, path, e.code)) 377 | log_path = path 378 | break 379 | elif hasattr(e, 'reason'): 380 | print('Reason:', e.reason) 381 | finally: 382 | if response: 383 | response.close() 384 | else: 385 | try: 386 | response = '' 387 | request = urllib2.Request(url+log_path_list[version], headers=self.headers) 388 | response = urllib2.urlopen(request, timeout=20) 389 | print('[*] Found {} log path: {} | status_code: {}'.format(url, log_path_list[version], response.code)) 390 | log_path = log_path_list[version] 391 | except urllib2.URLError as e: 392 | if hasattr(e, 'code'): 393 | # print 'Error code:',e.code 394 | if e.code == 403: 395 | print('[*] Found {} log path: {} | status_code: {}'.format(url, log_path_list[version], e.code)) 396 | log_path = log_path_list[version] 397 | elif hasattr(e, 'reason'): 398 | print('Reason:', e.reason) 399 | finally: 400 | if response: 401 | response.close() 402 | 403 | return log_path 404 | 405 | def saveLog2File(self, url, filename, data): 406 | dir_name = url.replace('https://', '').replace('http://', '').replace('/', '').replace(':', '_') 407 | data_path = 'TPLogData' 408 | if not os.path.isdir(data_path): 409 | os.mkdir(data_path) 410 | dir_path = os.path.join(data_path, dir_name) 411 | if not os.path.isdir(dir_path): 412 | os.mkdir(dir_path) 413 | 414 | with open(os.path.join(dir_path, filename.replace('/', '_')), 'w') as f: 415 | f.write(html) 416 | -------------------------------------------------------------------------------- /images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r3change/TPLogScan/710206bdedca8c5683ace93ad847c4963d2f2350/images/1.png -------------------------------------------------------------------------------- /images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r3change/TPLogScan/710206bdedca8c5683ace93ad847c4963d2f2350/images/2.png -------------------------------------------------------------------------------- /images/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r3change/TPLogScan/710206bdedca8c5683ace93ad847c4963d2f2350/images/3.png -------------------------------------------------------------------------------- /images/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r3change/TPLogScan/710206bdedca8c5683ace93ad847c4963d2f2350/images/4.png -------------------------------------------------------------------------------- /images/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r3change/TPLogScan/710206bdedca8c5683ace93ad847c4963d2f2350/images/5.png -------------------------------------------------------------------------------- /images/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r3change/TPLogScan/710206bdedca8c5683ace93ad847c4963d2f2350/images/6.png -------------------------------------------------------------------------------- /log.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r3change/TPLogScan/710206bdedca8c5683ace93ad847c4963d2f2350/log.jpg -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | argparse 3 | colorama 4 | --------------------------------------------------------------------------------