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. MySQL面试题!新鲜出炉~

    01.Mysql 的存储引擎,myisam和innodb的区别? 答:1.MyISAM 是非事务的存储引擎,适合用于频繁查询的应用.表锁,不会出现死锁,适合小数据,小并发. 2.innodb是支持事务 ...

  2. 使用Azure人脸API对图片进行人脸识别

    人脸识别是人工智能机器学习比较成熟的一个领域.人脸识别已经应用到了很多生产场景.比如生物认证,人脸考勤,人流监控等场景.对于很多中小功能由于技术门槛问题很难自己实现人脸识别的算法.Azure人脸API ...

  3. python6.4项目:股票提醒系统

    import tushareimport timedef getdata(share): data=tushare.get_realtime_quotes(share.code) share.name ...

  4. HTTP POST 请求的两种编码格式:application/x-www-form-urlencoded 和 multipart/form-data

    在常见业务开发中,POST 请求常常在这些地方使用:前端表单提交时.调用接口代码时和使用 Postman 测试接口时.我们下面来一一了解: 一.前端表单提交时 application/x-www-fo ...

  5. PR基础

    Windows->Workspace->Reset to saved layout 恢复工作区 Edit->Perferences->Auto Save 设置自动保存时间 资源 ...

  6. Python安装工具

    1.官网下载地址是:https://www.python.org/downloads/  默认下载安装时记得勾选配置PATH路径 PIP工具包(我是选择Python 3.5的) 2.Windows 下 ...

  7. 总结笔记 | 深度学习之Pytorch入门教程

    笔记作者:王博Kings 目录 一.整体学习的建议 1.1 如何成为Pytorch大神? 1.2 如何读Github代码? 1.3 代码能力太弱怎么办? 二.Pytorch与TensorFlow概述 ...

  8. java循环嵌套与跳转语句(break,continue)

    一 循环嵌套 嵌套循环是指在一个循环语句的循环体中再定义一个循环语句的语法结构.while.do…while. for循环语句都可以进行嵌套,并且它们之间也可以互相嵌套,如最常见的在for循环中嵌套f ...

  9. C#LeetCode刷题之#561-数组拆分 I(Array Partition I)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3718 访问. 给定长度为 2n 的数组, 你的任务是将这些数分成 ...

  10. Vue 生成条形码 jsbarcode的使用

    安装依赖包 npm install jsbarcode --save 在使用页面引入依赖包 import JsBarcode from 'jsbarcode' 定义img标签和id <img i ...