AWD西石油线下赛总结

先放上成绩(第一名),然后开始总结17年西石油的攻防赛。

线下赛漏洞挖掘思路

  • No1. Getshell,然后curl flag机获取flag

  • No2. 代码执行 或者 命令执行漏洞来直接获取flag

  • No3. 高权限注入执行命令获取flag

  • No4. PHP对file操作的函数获取flag(url_fopen_allow=On)

堡垒机防护

linux_file_check.py

通过find -name “*.php” -mmin -3来对linux文件进行监控(已用py2实现),防止有shell传上去,在没有挖掘到漏洞的时候也可以得知大佬们的shell路径及密码;还可以根据shell来判断受到的攻击方式。

代码:

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
#!/usr/bin/python
#coding=utf-8
#Usage :python demo.py
#Code by : AdminTony
#Blog : http://www.admintony.com
#注意:要将此文件放在有读写权限的目录以及所有修改过的php必须在此目录或者该目录的子目录中。
#作用:读取被修改过的文件,然后将文件的地址加上内容全部存放在txt


import sys,subprocess,os
#查找最近10分钟被修改的文件
def scanfile():
#command: find -name '*.php' -mmin -10
command = "find -name \'*.php\' -mmin -10"
su = subprocess.Popen(command,shell=True,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
STDOUT,STDERR = su.communicate()
list = STDOUT.split("\n")
#print str(list)
#将文件处理成list类型然后返回。
return list

#读取文件:
def loadfile(addr):
data = ""
#如果文件不存在就跳出函数
try :
file = open(addr,'r')
data = file.read()
except :
return 0
all_data = addr+"\n"+data+"\n\n"
file1 = open("shell.txt",'a+')
#避免重复写入
try:
shell_content = file1.read()
except:
shell_content = "null"
#如果文件内容不为空再写入,避免写入空的。
#print shell_content
if data :
if all_data not in shell_content:
file1.write(all_data)
file.close()
file1.close()
rm_cmd = "rm -rf "+addr
su = subprocess.Popen(rm_cmd,shell=True,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
su.communicate()
print "loadfile over : "+addr

if __name__ == '__main__':
while True:

list = scanfile()
if list :
for i in range(len(list)):
#如果list[i]为空就不读取了
if list[i]:
loadfile(str(list[i]))
else : pass

PHPWaf

PHPWaf用来记录访问请求,通过分析访问请求来判断对方的攻击方式,作用是感知漏洞,判断攻击方式,从而修复漏洞。

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
<?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) {
file_put_contents(LOG_FILENAME, "\r\n" . time() . "\r\n" . print_r($var, true) , FILE_APPEND);
// die() or unset($_GET) or unset($_POST) or unset($_COOKIE);

}
waf();
?>

waf的激活方式:

1.php.ini选项:auto_prepend_file =与auto_append_file =包含waf,需要权限较高。

2.config.php 中包含waf

3.让所有的文件都包含waf

使用waf一定要记得:看waf和cms的命名空间是否冲突,否则一包含waf则全站500.

代码审计(漏洞挖掘)

WEB1 : weiphp4.0beta_20170720_2005 | weiphp的最新版本。

WEB2 : OneThink v1.1

WEB3 : ECSHOP 20170608

先百度看下有没有现成的漏洞可以利用:

WEB1 可能存在注入,但是Seebug的漏洞看不了,然后看了下WEB1的config,还是root权限,waf和cms的命名空间冲突了,无法启用waf。很有可能是高权限注入来Getflag

WEB2 有一个前台代码执行漏洞是今年8月份提交的,也是无权查看,还有一个前台Getshell的,看了下是利用缓存文件来Getshell的。这时候主办方也给tips说漏洞和今年的thinkphp的缓存有关。

WEB1和WEB2都是基于thinkphp框架的,可能两个站点都存在这个漏洞,先做WEB2吧,上面有案例,可以做的快一点:

利用方式是注册一个账户然后登陆,cms会将登陆成功的用户记入缓存,那么创建
eval($_POST[X]);// 用户即可GETSHELL,注册不显示是否成功,注册完以后登陆发现用户不存在,多次测试后发现,用户名有长度限制,username<16。且所有登陆成功的用户都会记入同一个缓存文件。所以注册两个用户php $x=$_GET[X];和php eval($x);//

缓存文件的格式是:

1
2
3
<?php
//000000000000a:1:{s:2:"u1";s:13:"Administrator";}
?>

所以需要换行,否则一直在注释里面,且需要注释掉后面的内容,否则会报错

在提交的时候URL-encode 必须勾选上再提交,否则会把换行符的url-encode当成字符串来处理。

登陆的时候也需要抓包,在用户名前面加上换行符的URL-encode,且勾选上URL-encode

登陆成功则会记录在缓存文件中, eval($a);// 同理

理论上我已经拿到shell了,但是缓存文件名是什么?查阅了thinkphp的开发文档发现data_cache_key控制缓存文件名,如果不定义这个key则缓存文件名会一样。其实我当时是改了下ip看别人堡垒机上也有这个文件才知道缓存文件名一样的,这个key是后来查的。

修复方法:

1.打开文件:thinkphp\library\think\cache\driver\File.php
2.找到:public function set($name, $value, $expire = null) 方法
3.添加:$data = str_replace(PHP_EOL, '', $data);

WEB1也是基于thinkphp的会不会也有这个漏洞?

只能由字母数字_组成,估计是不行了。

WEB1代码中发现minify.php文件有问题:

通过审计发现:web1中minify文件的$f参数未经过过滤就放入了readfile,根据PHP官方文档知道

如果在 php.ini 文件中 "fopen wrappers" 已经被激活,则在本函数中可以把 URL 作为文件名来使用。

所以这里可以构造payload来试用readfile函数来读取flag。因为代码中要求读取文件的后缀必须是css或者js的,所以payload=minify.php?f=http://127.0.0.1/?a.js把a.js当作参数。最终可以达到getflag的效果。

WEB3的漏洞没审计出来。

赛后总结

  • waf的参数要避开cms的命名空间,否则会导致整站崩溃。

  • waf必须让所有php包含,否则就会像这次一样,他们抓不到我们的流量,从头到尾只有一个队抓到流量了补了洞。

  • 以后的学习中多注意对file操作的函数,比赛中可能会用上。