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

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

發現以下的問題:shell

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

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.刪除文件後腳本沒法恢復文件函數

這裏在博主修復時發現了比較尷尬的一點,文件一旦被刪除恢復後會被另外一個監控上傳文件的功能看成新上傳的文件直接刪除,造成了一個條件競爭,優化

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

 

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

 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)
相關文章
相關標籤/搜索