网络攻防比赛PHP版本WAF

这次去打HCTF决赛,用了这个自己写的WAF,web基本上没被打,被打的漏洞是文件包含漏洞,这个功能在本人这个waf里确实很是捉急,由于只是简单检测了..和php[35]{0,1},导致比赛由于文件包含漏洞web上失分了一次。不过现在不是很想去改进了。这个是比赛专用waf,商业价值几乎为0。

如果是框架写出的web就很好部署了,直接require在重写文件或者数据库文件中,如果是零散的php文件,那也有办法,如果是fastcgi(nginx,IIS比较常见)运行的php就在.user.ini加一句,具体百度一下.user.ini的后门,原理一样。其他情况也可以写个脚本强行在每个PHP前面加一句,脚本代码的样例也会放出来。(当然apache也可以.htaccess强行重写到waf再转回原页面,但是万一没重写环境呢)

具体代码如下

WAF

<?php
//error_reporting(E_ALL);
//ini_set('display_errors', 1);

/*
** 线下攻防php版本waf
**
** Author: 落
*/

/*
检测请求方式,除了get和post之外拦截下来并写日志。
*/
if($_SERVER['REQUEST_METHOD'] != 'POST' && $_SERVER['REQUEST_METHOD'] != 'GET'){
	write_attack_log("method");
}

$url = $_SERVER['REQUEST_URI']; //获取uri来进行检测

$data = file_get_contents('php://input'); //获取post的data,无论是否是mutipart

$headers = get_all_headers(); //获取header

filter_attack_keyword(filter_invisible(urldecode(filter_0x25($url)))); //对URL进行检测,出现问题则拦截并记录
filter_attack_keyword(filter_invisible(urldecode(filter_0x25($data)))); //对POST的内容进行检测,出现问题拦截并记录

/*
检测过了则对输入进行简单过滤
*/
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)))); //对http请求头进行检测,出现问题拦截并记录
	$_SERVER[$key] = filter_dangerous_words($value); //简单过滤
}

/*
获取http请求头并写入数组
*/
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;
}

/*
检测网站程序存在二次编码绕过漏洞造成的%25绕过,此处是循环将%25替换成%,直至不存在%25
*/
function filter_0x25($str){
	if(strpos($str,"%25") !== false){
		$str = str_replace("%25", "%", $str);
		return filter_0x25($str);
	}else{
		return $str;
	}
}

/*
攻击关键字检测,此处由于之前将特殊字符替换成空格,即使存在绕过特性也绕不过正则的\b
*/
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|multipoint\(/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;
}

/*
获取http的请求包,意义在于获取别人的攻击payload
*/
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; 
}

/*
这里拦截并记录攻击payload
*/
function write_attack_log($alert){
	$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'){
		echo "HCTF{aaaa}"; //如果请求带有flag关键字,显示假的flag。(2333333)
	}else{
		sleep(15); //拦截前延时15秒
	}
	exit(0);
}

?>

PYTHON

import os

base_dir = '/Users/apple/Documents/data' #web路径

def scandir(startdir) :
    
    os.chdir(startdir)
    for obj in os.listdir(os.curdir) :
        path = os.getcwd() + os.sep + obj
        if os.path.isfile(path) and '.php' in obj:
        	modifyip(path,'<?php','<?php\nrequire_once(\'waf.php\');') #强行加一句代码
        if os.path.isdir(obj) :
            scandir(obj)
            os.chdir(os.pardir) 

def modifyip(tfile,sstr,rstr):
    try:
        lines=open(tfile,'r').readlines()
        flen=len(lines)-1
        for i in range(flen):
            if sstr in lines[i]:
                lines[i]=lines[i].replace(sstr,rstr)
        open(tfile,'w').writelines(lines)
        
    except Exception,e:
        print e
        

scandir(base_dir)

祝大家以后攻防赛取得好成绩


本文由Hack Blog原创,如需转载注明原文链接

作者:落 分类:日常笔记 浏览:10516 评论:6
留言列表
ByStudent
ByStudent 哈哈,这个好  回复
访客
访客 mark一记  回复
OOun
OOun 博主写的很好,针对包含文件可以有更一劳永逸的办法,
修改php.ini 里的auto_prepend_file=waf.php
这样就可以自动在所有要执行php时候 include这个php
同样用这个也可以留后门
博主求交流啊  回复
访客
访客 博主博客模板上是啥啊,很不错!求分享!嘻嘻。  回复
访客
访客 还不知道怎么用,初次参加  回复
访客
访客 请问加了这个waf以后,网站无法访问是什么问题,望解答  回复
发表评论
来宾的头像