CTF线下awd攻防文件监控脚本

Stella981
• 阅读 929

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)
点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
JS 苹果手机日期显示NaN问题
问题描述newDate("2019122910:30:00")在IOS下显示为NaN原因分析带的日期IOS下存在兼容问题解决方法字符串替换letdateStr"2019122910:30:00";datedateStr.repl
Wesley13 Wesley13
2年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这