CTF線下awd攻防文件監控腳本


CTF線下awd攻防賽中常用一個文件監控腳本來保護文件,但是就博主對於該腳本的審計分析

發現如下的問題:

1.記錄文件的路徑未修改導致log暴露
原文件備份文件夾:drops_JWI96TY7ZKNMQPDRUOSG0FLH41A3C5EXVB82/bak_EAR1IBM0JT9HZ75WU4Y3Q8KLPCX26NDFOGVS
(猥瑣一點可以直接刪除掉,不給對方自行恢復的機會)
文件監控記錄文件:drops_JWI96TY7ZKNMQPDRUOSG0FLH41A3C5EXVB82/log_WMY4RVTLAJFB28960SC3KZX7EUP1IHOQN5GD/log.txt
(該文件記錄了刪除文件記錄、恢復文件記錄、上傳文件記錄等,可以用來偷其他戰隊的一些腳本)
上傳文件刪除后保存文件夾:drops_JWI96TY7ZKNMQPDRUOSG0FLH41A3C5EXVB82/webshell_WMY4RVTLAJFB28960SC3KZX7EUP1IHOQN5GD
(該腳本會把上傳的文件直接刪除,並且將上傳文件以“原文件名+.txt”保存在該目錄下)
被篡改文件備份保存夾:diff_UMTGPJO17F82K35Z0LEDA6QB9WH4IYRXVSCN/diff_UMTGPJO17F82K35Z0LEDA6QB9WH4IYRXVSCN
(被修改文件會被恢復,並且篡改后的文件會保存到該文件夾下,文件名為“原文件名+.txt”)
2.多次篡改同一文件可成功篡改
根據本人對於腳本代碼的審計,發現作者在恢復被篡改文件時錯誤使用了move函數
即變成了將備份中的被篡改文件移動到原文件目錄下,使得備份中的被篡改文件消失
當同一文件被篡改兩次時,腳本便無法從備份中再找到被篡改文件的備份(第一次篡改恢復時備份已經移動到了原文件目錄下)
問題代碼定位在這里:

shutil.move(filekey, os.path.join(Special_path['difffile'], ntpath.basename(filekey) + '.txt'))
shutil.move(os.path.join(Special_path['bak'], ntpath.basename(filekey)), filekey)
#這里直接使用了move將備份的文件移動至原目錄下,導致備份中該文件消失,在第二次篡改時便無法再從備份中move相同的文件

3.刪除文件后腳本無法恢復文件

這里在博主修復時發現了比較尷尬的一點,文件一旦被刪除恢復后會被另一個監控上傳文件的功能當作新上傳的文件直接刪除,形成了一個條件競爭,

在對代碼進行分析后,發現該腳本是通過白名單來監控文件的,解決方案就是在恢復刪除文件之后將該文件重新加入白名單

 

考慮到以上的問題,博主對腳本進行了一次修復優化,優化后的文件監控腳本源碼如下:

  1 # -*- coding: utf-8 -*-
  2 import os
  3 import re
  4 import hashlib
  5 import shutil
  6 import ntpath
  7 import time
  8 import sys
  9 
 10 # 設置系統字符集,防止寫入log時出現錯誤
 11 reload(sys)
 12 sys.setdefaultencoding('utf-8')
 13 CWD = os.getcwd()
 14 FILE_MD5_DICT = {}      # 文件MD5字典
 15 ORIGIN_FILE_LIST = []
 16 
 17 # 特殊文件路徑字符串
 18 Special_path_str = 'drops_B0503373BDA6E3C5CD4E5118C02ED13A' #drops_md5(icecoke1024)
 19 bakstring = 'back_CA7CB46E9223293531C04586F3448350'          #bak_md5(icecoke1)
 20 logstring = 'log_8998F445923C88FF441813F0F320962C'          #log_md5(icecoke2)
 21 webshellstring = 'webshell_988A15AB87447653EFB4329A90FF45C5'#webshell_md5(icecoke3)
 22 difffile = 'difference_3C95FA5FB01141398896EDAA8D667802'          #diff_md5(icecoke4)
 23 
 24 Special_string = 'drops_log'  # 免死金牌
 25 UNICODE_ENCODING = "utf-8"
 26 INVALID_UNICODE_CHAR_FORMAT = r"\?%02x"
 27 
 28 # 文件路徑字典
 29 spec_base_path = os.path.realpath(os.path.join(CWD, Special_path_str))
 30 Special_path = {
 31     'bak' : os.path.realpath(os.path.join(spec_base_path, bakstring)),
 32     'log' : os.path.realpath(os.path.join(spec_base_path, logstring)),
 33     'webshell' : os.path.realpath(os.path.join(spec_base_path, webshellstring)),
 34     'difffile' : os.path.realpath(os.path.join(spec_base_path, difffile)),
 35 }
 36 
 37 def isListLike(value):
 38     return isinstance(value, (list, tuple, set))
 39 
 40 # 獲取Unicode編碼
 41 def getUnicode(value, encoding=None, noneToNull=False):
 42 
 43     if noneToNull and value is None:
 44         return NULL
 45 
 46     if isListLike(value):
 47         value = list(getUnicode(_, encoding, noneToNull) for _ in value)
 48         return value
 49 
 50     if isinstance(value, unicode):
 51         return value
 52     elif isinstance(value, basestring):
 53         while True:
 54             try:
 55                 return unicode(value, encoding or UNICODE_ENCODING)
 56             except UnicodeDecodeError, ex:
 57                 try:
 58                     return unicode(value, UNICODE_ENCODING)
 59                 except:
 60                     value = value[:ex.start] + "".join(INVALID_UNICODE_CHAR_FORMAT % ord(_) for _ in value[ex.start:ex.end]) + value[ex.end:]
 61     else:
 62         try:
 63             return unicode(value)
 64         except UnicodeDecodeError:
 65             return unicode(str(value), errors="ignore")
 66 
 67 # 目錄創建
 68 def mkdir_p(path):
 69     import errno
 70     try:
 71         os.makedirs(path)
 72     except OSError as exc:
 73         if exc.errno == errno.EEXIST and os.path.isdir(path):
 74             pass
 75         else: raise
 76 
 77 # 獲取當前所有文件路徑
 78 def getfilelist(cwd):
 79     filelist = []
 80     for root,subdirs, files in os.walk(cwd):
 81         for filepath in files:
 82             originalfile = os.path.join(root, filepath)
 83             if Special_path_str not in originalfile:
 84                 filelist.append(originalfile)
 85     return filelist
 86 
 87 # 計算機文件MD5值
 88 def calcMD5(filepath):
 89     try:
 90         with open(filepath,'rb') as f:
 91             md5obj = hashlib.md5()
 92             md5obj.update(f.read())
 93             hash = md5obj.hexdigest()
 94             return hash
 95 # 文件MD5消失即為文件被刪除,恢復文件
 96     except Exception, e:
 97         print u'[*] 文件被刪除 : ' + getUnicode(filepath)
 98     shutil.copyfile(os.path.join(Special_path['bak'], ntpath.basename(filepath)), filepath)
 99     for value in Special_path:
100         mkdir_p(Special_path[value])
101         ORIGIN_FILE_LIST = getfilelist(CWD)
102         FILE_MD5_DICT = getfilemd5dict(ORIGIN_FILE_LIST)
103     print u'[+] 被刪除文件已恢復!'
104     try:
105          f = open(os.path.join(Special_path['log'], 'log.txt'), 'a')
106          f.write('deleted_file: ' + getUnicode(filepath) + ' 時間: ' + getUnicode(time.ctime()) + '\n')
107          f.close()
108     except Exception as e:
109          print u'[-] 記錄失敗 : 被刪除文件: ' + getUnicode(filepath)
110          pass
111 
112 # 獲取所有文件MD5
113 def getfilemd5dict(filelist = []):
114     filemd5dict = {}
115     for ori_file in filelist:
116         if Special_path_str not in ori_file:
117             md5 = calcMD5(os.path.realpath(ori_file))
118             if md5:
119                 filemd5dict[ori_file] = md5
120     return filemd5dict
121 
122 # 備份所有文件
123 def backup_file(filelist=[]):
124     for filepath in filelist:
125         if Special_path_str not in filepath:
126             shutil.copy2(filepath, Special_path['bak'])
127 
128 if __name__ == '__main__':
129     print u'---------持續監測文件中------------'
130     for value in Special_path:
131         mkdir_p(Special_path[value])
132     # 獲取所有文件路徑,並獲取所有文件的MD5,同時備份所有文件
133     ORIGIN_FILE_LIST = getfilelist(CWD)
134     FILE_MD5_DICT = getfilemd5dict(ORIGIN_FILE_LIST)
135     backup_file(ORIGIN_FILE_LIST) 
136     print u'[*] 所有文件已備份完畢!'
137     while True:
138         file_list = getfilelist(CWD)
139         # 移除新上傳文件
140         diff_file_list = list(set(file_list) ^ set(ORIGIN_FILE_LIST))
141         if len(diff_file_list) != 0:
142             for filepath in diff_file_list:
143                 try:
144                     f = open(filepath, 'r').read()
145                 except Exception, e:
146                     break
147                 if Special_string not in f:
148                     try:
149                         print u'[*] 查殺疑似WebShell上傳文件: ' + getUnicode(filepath)
150                         shutil.move(filepath, os.path.join(Special_path['webshell'], ntpath.basename(filepath) + '.txt'))
151                         print u'[+] 新上傳文件已刪除!'
152                     except Exception as e:
153                         print u'[!] 移動文件失敗, "%s" 疑似WebShell,請及時處理.'%getUnicode(filepath)
154                     try:
155                         f = open(os.path.join(Special_path['log'], 'log.txt'), 'a')
156                         f.write('new_file: ' + getUnicode(filepath) + ' 時間: ' + str(time.ctime()) + '\n')
157                         f.close()
158                     except Exception as e:
159                         print u'[-] 記錄失敗 : 上傳文件: ' + getUnicode(e)
160 
161         # 防止任意文件被修改,還原被修改文件
162         md5_dict = getfilemd5dict(ORIGIN_FILE_LIST)
163         for filekey in md5_dict:
164             if md5_dict[filekey] != FILE_MD5_DICT[filekey]:
165                 try:
166                     f = open(filekey, 'r').read()
167                 except Exception, e:
168                     break
169                 if Special_string not in f:
170                     try:
171                         print u'[*] 該文件被修改 : ' + getUnicode(filekey)
172                         shutil.move(filekey, os.path.join(Special_path['difffile'], ntpath.basename(filekey) + '.txt'))
173                         shutil.copyfile(os.path.join(Special_path['bak'], ntpath.basename(filekey)), filekey)
174                         print u'[+] 文件已復原!'
175                     except Exception as e:
176                         print u'[!] 移動文件失敗, "%s" 疑似WebShell,請及時處理.'%getUnicode(filekey)
177                     try:
178                         f = open(os.path.join(Special_path['log'], 'log.txt'), 'a')
179                         f.write('difference_file: ' + getUnicode(filekey) + ' 時間: ' + getUnicode(time.ctime()) + '\n')
180                         f.close()
181                     except Exception as e:
182                         print u'[-] 記錄失敗 : 被修改文件: ' + getUnicode(filekey)
183                         pass
184         time.sleep(2)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM