关于爬虫本地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);
}

			  
            
          

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的包
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协议传输给目的方,几者之间的关系如下:
HTTPS
TLS加密需要需要在TCP连接建立之后,双方进行SSL握手,协商随机数和证书。大概的过程是这样的:
SSL握手
这里和我们这次文章比较相关的部分就是客户端发送Hello后,服务端返回证书,客户端校验证书有效性。

NGINX反向代理

在现在互联网时代,IP地址越来越紧张,因此我们经常会将多个域名或者网站使用同一台服务器,同一个IP。NGINX通常就是这样的网关。当一个HTTP请求到达时候,NGINX会通过HTTP请求中的Host头来决定转发目的服务器。
NGINX转发
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等

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来处理新连接,因此中间必然会存在一个竞争锁的过程,性能肯定也会有损耗的。如下图:
没有使用reuseport
当这个选项打开的时候,系统允许多个Socket监听在同一个IP和端口上面,当有新请求到达时候,由内核直接从这些Socket中选择一个来处理,此时套接字和Worker是一一对应的,因此该套接字只会通知到一个Worker,这样有效减少进程间竞争。如下图:
使用reuseport
当然使用这个选项是有一些条件的:

  1. NGINX版本大于1.9.1
  2. Linux内核版本3.9以后
  3. NGINX中开启相应的配置项
http {
    server {
        listen 80 reuseport;
        server_name  localhost;
        ...
    }
}

stream {
    server {
        listen 12345 reuseport;
        ...
    }
}

			  
            
          

微信支付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.parse(stream);
            doc.getDocumentElement().normalize();

            .........

    }

			  
            
          

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)

			  
            
          

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

			  
            
          

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

0x00 背景

某日在给某集团平台提交报告的时候,出于习惯就想试试这个平台的安全性怎么样。
总的来说,平台前面架设了WAF,一些常规的测试Payload都会被拦截,因此不适合强攻。其实随着安全越来越受重视,很多开发同学的安全意识也越来越高,常见的XSS和SQL注入的错误一般都会再犯。但是对于跨站请求伪造CSRF一般都没有太在意。主要原因是这类漏洞的危害不会像注入那样明显,但是威胁等级并不比它们低。

0x01 案例

前面唠叨了这么多,接下来我们就说说这个具体的例子:

问题接口

我们发现退出登录,这个平台采用的GET请求,同时没有其他防护策略,比如token,比如referer校验。
这个接口存在两个问题:

  1. 不规范,我们知道在HTTP语义中,修改用户的登录态写操作要用POST或者PUT,而GET操作主要用于获取资源信息等。至于开发人员为什么要这么做?肯定就是为了图方便。所有违背规范的操作,都是为了主管的便利性。
  2. 重要的接口没有进行二次校验。接口功能是正常的,就是清空session中的用户登录态,但是在清除过程中,并没有确认请求是否用户发送的。

假设这个接口地址是:http://security.lxxxxxxxxxxh.com/account/logout ,这样的接口很好找,因为只要有登录态的网站,一般在显眼的地方都会存在这样退出功能的接口。

注入点

现在问题接口找到了,但是我们需要怎样才能诱骗其他用户来触发呢?
第一个办法,也是最简单的,就是找到你的目标用户,然后把URL发给他,等他点击。但是这里有两个明显的问题:

  1. SRC平台不是社交网站,一般不提供用户之间交流的渠道,因此我们能否把URL发出去都是一个问题
  2. 一个不熟悉的人给你发的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>

			  
            
          

安全工具 - 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

			  
            
          

安全工具 - 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

			  
            
          

开源中国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)

			  
            
          

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)
)

			  
            
          

关于我们

介绍

对于网络安全感兴趣的三个小年轻组合在一起形成小组, 为了挖掘漏洞我们要像挖掘机一样努力.
因此我们叫漏洞挖掘机.