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.删除文件后脚本无法恢复文件

这里在博主修复时发现了比较尴尬的一点,文件一旦被删除恢复后会被另一个监控上传文件的功能当作新上传的文件直接删除,形成了一个条件竞争,

在对代码进行分析后,发现该脚本是通过白名单来监控文件的,解决方案就是在恢复删除文件之后将该文件重新加入白名单

考虑到以上的问题,博主对脚本进行了一次修复优化,优化后的文件监控脚本源码如下:

 # -*- coding: utf-8 -*-
import os
import re
import hashlib
import shutil
import ntpath
import time
import sys # 设置系统字符集,防止写入log时出现错误
reload(sys)
sys.setdefaultencoding('utf-8')
CWD = os.getcwd()
FILE_MD5_DICT = {} # 文件MD5字典
ORIGIN_FILE_LIST = [] # 特殊文件路径字符串
Special_path_str = 'drops_B0503373BDA6E3C5CD4E5118C02ED13A' #drops_md5(icecoke1024)
bakstring = 'back_CA7CB46E9223293531C04586F3448350' #bak_md5(icecoke1)
logstring = 'log_8998F445923C88FF441813F0F320962C' #log_md5(icecoke2)
webshellstring = 'webshell_988A15AB87447653EFB4329A90FF45C5'#webshell_md5(icecoke3)
difffile = 'difference_3C95FA5FB01141398896EDAA8D667802' #diff_md5(icecoke4) Special_string = 'drops_log' # 免死金牌
UNICODE_ENCODING = "utf-8"
INVALID_UNICODE_CHAR_FORMAT = r"\?%02x" # 文件路径字典
spec_base_path = os.path.realpath(os.path.join(CWD, Special_path_str))
Special_path = {
'bak' : os.path.realpath(os.path.join(spec_base_path, bakstring)),
'log' : os.path.realpath(os.path.join(spec_base_path, logstring)),
'webshell' : os.path.realpath(os.path.join(spec_base_path, webshellstring)),
'difffile' : os.path.realpath(os.path.join(spec_base_path, difffile)),
} def isListLike(value):
return isinstance(value, (list, tuple, set)) # 获取Unicode编码
def getUnicode(value, encoding=None, noneToNull=False): if noneToNull and value is None:
return NULL if isListLike(value):
value = list(getUnicode(_, encoding, noneToNull) for _ in value)
return value if isinstance(value, unicode):
return value
elif isinstance(value, basestring):
while True:
try:
return unicode(value, encoding or UNICODE_ENCODING)
except UnicodeDecodeError, ex:
try:
return unicode(value, UNICODE_ENCODING)
except:
value = value[:ex.start] + "".join(INVALID_UNICODE_CHAR_FORMAT % ord(_) for _ in value[ex.start:ex.end]) + value[ex.end:]
else:
try:
return unicode(value)
except UnicodeDecodeError:
return unicode(str(value), errors="ignore") # 目录创建
def mkdir_p(path):
import errno
try:
os.makedirs(path)
except OSError as exc:
if exc.errno == errno.EEXIST and os.path.isdir(path):
pass
else: raise # 获取当前所有文件路径
def getfilelist(cwd):
filelist = []
for root,subdirs, files in os.walk(cwd):
for filepath in files:
originalfile = os.path.join(root, filepath)
if Special_path_str not in originalfile:
filelist.append(originalfile)
return filelist # 计算机文件MD5值
def calcMD5(filepath):
try:
with open(filepath,'rb') as f:
md5obj = hashlib.md5()
md5obj.update(f.read())
hash = md5obj.hexdigest()
return hash
# 文件MD5消失即为文件被删除,恢复文件
except Exception, e:
print u'[*] 文件被删除 : ' + getUnicode(filepath)
shutil.copyfile(os.path.join(Special_path['bak'], ntpath.basename(filepath)), filepath)
for value in Special_path:
mkdir_p(Special_path[value])
ORIGIN_FILE_LIST = getfilelist(CWD)
FILE_MD5_DICT = getfilemd5dict(ORIGIN_FILE_LIST)
print u'[+] 被删除文件已恢复!'
try:
f = open(os.path.join(Special_path['log'], 'log.txt'), 'a')
f.write('deleted_file: ' + getUnicode(filepath) + ' 时间: ' + getUnicode(time.ctime()) + '\n')
f.close()
except Exception as e:
print u'[-] 记录失败 : 被删除文件: ' + getUnicode(filepath)
pass # 获取所有文件MD5
def getfilemd5dict(filelist = []):
filemd5dict = {}
for ori_file in filelist:
if Special_path_str not in ori_file:
md5 = calcMD5(os.path.realpath(ori_file))
if md5:
filemd5dict[ori_file] = md5
return filemd5dict # 备份所有文件
def backup_file(filelist=[]):
for filepath in filelist:
if Special_path_str not in filepath:
shutil.copy2(filepath, Special_path['bak']) if __name__ == '__main__':
print u'---------持续监测文件中------------'
for value in Special_path:
mkdir_p(Special_path[value])
# 获取所有文件路径,并获取所有文件的MD5,同时备份所有文件
ORIGIN_FILE_LIST = getfilelist(CWD)
FILE_MD5_DICT = getfilemd5dict(ORIGIN_FILE_LIST)
backup_file(ORIGIN_FILE_LIST)
print u'[*] 所有文件已备份完毕!'
while True:
file_list = getfilelist(CWD)
# 移除新上传文件
diff_file_list = list(set(file_list) ^ set(ORIGIN_FILE_LIST))
if len(diff_file_list) != 0:
for filepath in diff_file_list:
try:
f = open(filepath, 'r').read()
except Exception, e:
break
if Special_string not in f:
try:
print u'[*] 查杀疑似WebShell上传文件: ' + getUnicode(filepath)
shutil.move(filepath, os.path.join(Special_path['webshell'], ntpath.basename(filepath) + '.txt'))
print u'[+] 新上传文件已删除!'
except Exception as e:
print u'[!] 移动文件失败, "%s" 疑似WebShell,请及时处理.'%getUnicode(filepath)
try:
f = open(os.path.join(Special_path['log'], 'log.txt'), 'a')
f.write('new_file: ' + getUnicode(filepath) + ' 时间: ' + str(time.ctime()) + '\n')
f.close()
except Exception as e:
print u'[-] 记录失败 : 上传文件: ' + getUnicode(e) # 防止任意文件被修改,还原被修改文件
md5_dict = getfilemd5dict(ORIGIN_FILE_LIST)
for filekey in md5_dict:
if md5_dict[filekey] != FILE_MD5_DICT[filekey]:
try:
f = open(filekey, 'r').read()
except Exception, e:
break
if Special_string not in f:
try:
print u'[*] 该文件被修改 : ' + getUnicode(filekey)
shutil.move(filekey, os.path.join(Special_path['difffile'], ntpath.basename(filekey) + '.txt'))
shutil.copyfile(os.path.join(Special_path['bak'], ntpath.basename(filekey)), filekey)
print u'[+] 文件已复原!'
except Exception as e:
print u'[!] 移动文件失败, "%s" 疑似WebShell,请及时处理.'%getUnicode(filekey)
try:
f = open(os.path.join(Special_path['log'], 'log.txt'), 'a')
f.write('difference_file: ' + getUnicode(filekey) + ' 时间: ' + getUnicode(time.ctime()) + '\n')
f.close()
except Exception as e:
print u'[-] 记录失败 : 被修改文件: ' + getUnicode(filekey)
pass
time.sleep(2)

CTF线下awd攻防文件监控脚本的更多相关文章

  1. 线下AWD平台搭建以及一些相关问题解决

    线下AWD平台搭建以及一些相关问题解决 一.前言 文章首发于tools,因为发现了一些新问题但是没法改,所以在博客进行补充. 因为很多人可能没有机会参加线下的AWD比赛,导致缺乏这方面经验,比如我参加 ...

  2. CTF线下赛AWD套路小结

    近打了2场CTF线下赛,把AWD模式中的一些小套路做一些总结,本人web狗,二进制部分就不班门弄斧了. 一. AWD模式简介 AWD:Attack With Defence,比赛中每个队伍维护多台服务 ...

  3. CTF线下赛AWD模式下的生存技巧

    作者:Veneno@Nu1L 稿费:200RMB 投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿 原文:https://www.anquanke.com/post/id/8467 ...

  4. ctf线下赛中检测外来IP的shell脚本

    该脚本可用于ctf线下赛中,用来检测攻击IP的接入,及时做出响应. #!/bin/bash #写自己队的ip ipA="172.22.60.230" ipB="172.2 ...

  5. CTF线下防御战 — 让你的靶机变成“铜墙铁壁”

    本文首发安全客,未经允许禁止转载.原文链接 一. 前言 随着CTF的普及,比赛的形式也有了越来越多的花样,对于线下赛来说,开始出现了安全加固或者防御战之类的环节,亦或者因为拿下靶机后不希望其他攻击者进 ...

  6. CTF线下攻防赛

    SSH登陆 两三个人进行分工,一个粗略的看下web,有登陆口的话,就需要修改密码,将情况反馈给队友,让登陆ssh的小伙伴进行密码的修改,改成炒鸡复杂.然后将Web目录下载下来,上WAF.文件监控.端口 ...

  7. 域渗透复盘(安洵CTF线下)

    复盘线下域渗透环境Write Up 0x01 外网web到DMZ进域 外网web入口 joomla应用   192.168.0.5 反序列化打下来 GET /index.php HTTP/1.1 Ho ...

  8. ctf线下赛中关闭非法用户shell脚本

    linux中三类用户:根用户,虚拟用户,普通用户. 其中普通用户的UID一般介于500-6000之间. #!/bin/bash for uid in $( cat /etc/passwd | cut ...

  9. 代码审计-四叶草杯线下awd比赛源码web2

    今晚简单来看看那天比赛的源码吧,比赛的时候还是有些慌没有好好去静下心看代码. awd给的题中的漏洞,都是那种可以快速让你利用拿到权限后得到flag的那种,特别复杂利用的一般没有. 建议先黑盒去尝试,例 ...

随机推荐

  1. python8.4景区买票

    from threading import Threadimport threadinglock=threading.Lock()num=100#定义买票方法def sale(name): lock. ...

  2. (转)Linux 下栈溢出问题分析解决 *** stack smashing detected *** XXXX terminated

    Linux 下栈溢出问题分析解决 *** stack smashing detected *** XXXX terminated 1.利用gdb 或者valgrind 定位到具体的代码 最近在Linu ...

  3. LinuX操作系统基础------>了解文件结构和相关的文件操作

    了解LinuX文件系统 熟悉LinuX的文件结构 熟悉LinuX的基本操作指令 利用所学指令进行指令组合操作 什么是文件?什么是目录? 文件:一般是一个独立的东西,可以通过某些工具将其打开 目录:可以 ...

  4. JavaScript小游戏实例:统一着色

    设计如下的简单小游戏. 在面板(画布)中放置10行10列共100个小方块,每个小方块随机在5种颜色中选一种颜色进行着色,在面板的下方,放置对应的5种颜色色块,如图1所示. 图1  “统一着色”游戏界面 ...

  5. 敏捷工具:Scrum板与Kanban如何抉择?敏捷工具:Scrum板与Kanban如何抉择?

    Scrum板作为一种工具,主要应用于Scrum团队的敏捷项目管理,能够帮助团队更新任务进度,促进团队信息共享,及时发现任务过程中的异常现象,从而查漏补缺.团队在每日站会时会通过Scrum板来直观地展示 ...

  6. MySQL“被动”性能优化汇总!

    年少不知优化苦,遇坑方知优化难. --村口王大爷 本文内容导图如下: 我之前有很多文章都在讲性能优化的问题,比如下面这些: <switch 的性能提升了 3 倍,我只用了这一招!> < ...

  7. Deep learning-based personality recognition from text posts of online social networks 阅读笔记

    文章目录 一.摘要 二.模型过程 1.文本预处理 1.1 文本切分 1.2 文本统一 2. 基于统计的特征提取 2.1 提取特殊的语言统计特征 2.2 提取基于字典的语言特征 3. 基于深度学习的文本 ...

  8. 用Unity3D实现太阳系仿真

    用Unity3D模拟太阳系仿真 模拟要求 写一个程序,实现一个完整的太阳系, 其他星球围绕太阳的转速必须不一样,且不在一个法平面上. 操作步骤 1.创建如下结构 sun 里包括8大行星, 并且设置好距 ...

  9. python 文件读写with open模式r,r+,w,w+,a,a+的区别

    模式 可做操作 若文件不存在 是否覆盖 r 只能读 报错 - r+ 可读可写 报错 是 w 只能写 创建 是 w+ 可读可写 创建 是 a 只能写 创建 否,追加写 a+ 可读可写 创建 否,追加写

  10. Mybatis 和 Solon 在一起的升级版

    终于说通 Solon 作者,让他为 Solon 框架添加事务注解支持了:并且把 mybatis-solon-plugin 的 @Df 注解更名为 @Db ,接地气多了(Df是什么鬼呢?新手肯定这么想. ...