Update mkpasswd to be slightly more secure

Try to ensure that the mkpasswd command run in ssh sessions only echos to
ssh client running that command. This gives us slightly more security
against other session users seeing the hashed password.

Fixed console sending with some of the APIs

Updated version to 1.3.7 to match for next spigot + bungeecord release.
This commit is contained in:
Justin Crawford 2019-10-10 19:48:32 -07:00
parent ec1ed8cb9e
commit 170a96eb94
No known key found for this signature in database
GPG Key ID: 0D84DEDBB8EF259C
7 changed files with 100 additions and 83 deletions

View File

@ -1,15 +1,15 @@
Spigot-SSHD Minecraft-SSHD
=========== ===========
[![Build Status](https://travis-ci.org/Justasic/Spigot-SSHD.svg?branch=master)](https://travis-ci.org/Justasic/Spigot-SSHD) [![Build Status](https://travis-ci.org/Justasic/Minecraft-SSHD.svg?branch=master)](https://travis-ci.org/Justasic/Minecraft-SSHD)
[![Release](https://img.shields.io/github/release/Justasic/Spigot-SSHD.svg?label=Release&maxAge=60)](https://github.com/Justasic/Spigot-SSHD/releases/latest) [![Release](https://img.shields.io/github/release/Justasic/Minecraft-SSHD.svg?label=Release&maxAge=60)](https://github.com/Justasic/Minecraft-SSHD/releases/latest)
[![GitHub license](https://img.shields.io/github/license/Justasic/Spigot-SSHD)](https://github.com/Justasic/Spigot-SSHD/blob/master/LICENSE) [![GitHub license](https://img.shields.io/github/license/Justasic/Minecraft-SSHD)](https://github.com/Justasic/Minecraft-SSHD/blob/master/LICENSE)
<img align="left" width="140" height="140" src="docs/ssh_logo.png?raw=true" hspace="5" vspace="5" alt="diskover"><br/> <img align="left" width="140" height="140" src="docs/ssh_logo.png?raw=true" hspace="5" vspace="5" alt="diskover"><br/>
**Have you ever wished you could remotely access your server's admin console without having to setup a complex remote access system? Now you can with SSHD.** **Have you ever wished you could remotely access your server's admin console without having to setup a complex remote access system? Now you can with Minecraft-SSHD!**
SSHD securely exposes your BungeeCord admin console and the server filesystem using the SSH protocol - the same protocol that serves as the secure foundation for nearly all remote server administration.<br/> Minecraft-SSHD securely exposes your BungeeCord admin console and the server filesystem using the SSH protocol - the same protocol that serves as the secure foundation for nearly all remote server administration.<br/>
- Compatible with all ssh clients, regardless of operating system. - Compatible with all ssh clients, regardless of operating system.
- Remotely view your server log in real-time. - Remotely view your server log in real-time.
@ -20,7 +20,7 @@ SSHD securely exposes your BungeeCord admin console and the server filesystem us
- Run Spigot without using screen or tmux (by adding `-noconsole`) - Run Spigot without using screen or tmux (by adding `-noconsole`)
- Remotely script your server by issuing one-off console commands with ssh. - Remotely script your server by issuing one-off console commands with ssh.
### Why should I use SSHD? ### Why should I use Minecraft-SSHD?
- You are in a shared hosting environment that only gives you access to the - log files. - You are in a shared hosting environment that only gives you access to the - log files.
- You want to share access to your server console, but don't want to give anybody access to the machine its running on. - You want to share access to your server console, but don't want to give anybody access to the machine its running on.
@ -93,8 +93,8 @@ mkpasswd supports the following hash algorithms:
`sshd.mkpasswd` - Checks if the in-game user has access to run the mkpasswd command. `sshd.mkpasswd` - Checks if the in-game user has access to run the mkpasswd command.
SSHD uses cryptographic certificates or a secure username and password to verify remote access. Minecraft-SSHD uses cryptographic certificates or a secure username and password to verify remote access.
## Source Code ## Source Code
[Get the source on GitHub](https://github.com/Justasic/Spigot-SSHD "Source Code") [Get the source on GitHub](https://github.com/Justasic/Minecraft-SSHD "Source Code")

View File

@ -6,8 +6,9 @@
<groupId>com.ryanmichela</groupId> <groupId>com.ryanmichela</groupId>
<artifactId>sshd</artifactId> <artifactId>sshd</artifactId>
<version>1.3.6.1</version> <description>Minecraft-SSHD: The SSH daemon for Minecraft servers.</description>
<url>https://github.com/Justasic/Bukkit-SSHD/</url> <version>1.3.7</version>
<url>https://github.com/Justasic/Minecraft-SSHD/</url>
<properties> <properties>
<java.version>1.8</java.version> <java.version>1.8</java.version>

View File

@ -96,7 +96,7 @@ public class ConsoleLogFormatter extends Formatter {
stringbuilder.append(stringwriter.toString()); stringbuilder.append(stringwriter.toString());
} }
return stringbuilder.toString(); return stringbuilder.toString().replace("\n", "\r\n");
} }
private void colorize(LogRecord logrecord) private void colorize(LogRecord logrecord)

View File

@ -136,6 +136,8 @@ public class ConsoleShellFactory implements ShellFactory {
if (command.equals("cls")) if (command.equals("cls"))
{ {
this.ConsoleReader.clearScreen(); this.ConsoleReader.clearScreen();
this.ConsoleReader.drawLine();
this.ConsoleReader.flush();
continue; continue;
} }
// Hide the mkpasswd command input from other users. // Hide the mkpasswd command input from other users.
@ -152,11 +154,19 @@ public class ConsoleShellFactory implements ShellFactory {
} }
else else
{ {
// Don't send our mkpasswd command output. This will echo passwords back
// to the console for all to see. This command is strictly between
// our plugin and the connected client.
if (!mkpasswd) if (!mkpasswd)
{
SshdPlugin.instance.getLogger().info("<" + this.Username + "> " + command); SshdPlugin.instance.getLogger().info("<" + this.Username + "> " + command);
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), command); Bukkit.dispatchCommand(Bukkit.getConsoleSender(), command);
} }
else
{
Bukkit.dispatchCommand(this.SshdCommandSender, command);
}
}
}); });
} }
} }

View File

@ -12,9 +12,28 @@ import com.ryanmichela.sshd.SshdPlugin;
class MkpasswdCommand implements CommandExecutor class MkpasswdCommand implements CommandExecutor
{ {
// Because Spigot's failed syntax API is really less than ideal (you should be required to add a
// SendSyntax function override), we're just always going to return true even for syntax failures
// as we will handle the syntax message internally. This also lets us send the messages more
// securely to the client without people knowing we're using the command. This prevents password
// or hash leakages from the user to other connected users. Plus this syntax will show how
// to both use the command and what hashes we support which is important for people who don't
// know how to RTFM. - Justin
private void SendSyntax(CommandSender sender, boolean invalid)
{
if (invalid)
sender.sendMessage("\u00A7cInvalid Syntax\u00A7r");
sender.sendMessage("\u00A7a/mkpasswd <help|hash> <password>\u00A7r");
sender.sendMessage("\u00A79Supported Hashes: SHA256, PBKDF2, BCRYPT, PLAIN\u00A7r");
}
@Override @Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) public boolean onCommand(CommandSender sender, Command command, String label, String[] args)
{ {
// If we're not mkpasswd, just fuck off.
if (!label.equalsIgnoreCase("mkpasswd"))
return false;
String algoritm, password; String algoritm, password;
try try
{ {
@ -22,78 +41,52 @@ class MkpasswdCommand implements CommandExecutor
// spaces in their passwords otherwise it won't be as strong as it should be. // spaces in their passwords otherwise it won't be as strong as it should be.
algoritm = args[0]; algoritm = args[0];
password = String.join(" ", Arrays.copyOfRange(args, 1, args.length)); password = String.join(" ", Arrays.copyOfRange(args, 1, args.length));
if (password.trim().isEmpty()) // Shortcut to the catch statement below.
throw new ArrayIndexOutOfBoundsException();
} }
catch (ArrayIndexOutOfBoundsException e) catch (ArrayIndexOutOfBoundsException e)
{ {
// ignore it. // ignore it.
return false; this.SendSyntax(sender, true);
return true;
} }
// If they're console, allow regardless. boolean hasperm = (sender instanceof Player) ? ((Player)sender).hasPermission("sshd.mkpasswd") : true;
if (!(sender instanceof Player))
{ if (hasperm)
if (label.equalsIgnoreCase("mkpasswd"))
{ {
try try
{ {
String hash = "";
// Dumb but whatever. Some people are really dense. // Dumb but whatever. Some people are really dense.
if (algoritm.equalsIgnoreCase("PLAIN")) if (algoritm.equalsIgnoreCase("PLAIN"))
{ {
// I mean c'mon... // I mean c'mon...
sender.sendMessage("Bro really? it's literally your unencrypted password..."); sender.sendMessage("Bro really? it's literally your unencrypted password...");
return true;
} }
else if (algoritm.equalsIgnoreCase("pbkdf2")) else if (algoritm.equalsIgnoreCase("pbkdf2"))
sender.sendMessage("Your hash: " + Cryptography.PBKDF2_HashPassword(password)); hash = Cryptography.PBKDF2_HashPassword(password);
else if (algoritm.equalsIgnoreCase("bcrypt")) else if (algoritm.equalsIgnoreCase("bcrypt"))
sender.sendMessage("Your hash: " + Cryptography.BCrypt_HashPassword(password)); hash = Cryptography.BCrypt_HashPassword(password);
else if (algoritm.equalsIgnoreCase("sha256")) else if (algoritm.equalsIgnoreCase("sha256"))
sender.sendMessage("Your hash: " + Cryptography.SHA256_HashPassword(password)); hash = Cryptography.SHA256_HashPassword(password);
else if (algoritm.equalsIgnoreCase("help"))
sender.sendMessage("Supported hash algorithms: pbkdf2, bcrypt, sha256, plain");
else else
return false; {
this.SendSyntax(sender, !algoritm.equalsIgnoreCase("help"));
return true;
}
sender.sendMessage("\u00A79Your Hash: " + hash + "\u00A7r");
} }
catch (Exception e) catch (Exception e)
{ {
// We're console, just print the stack trace. // We're console, just print the stack trace.
e.printStackTrace(); e.printStackTrace();
return false; sender.sendMessage("\u00A7cAn error occured. Please check console for details.\u00A7r");
} }
}
return true; return true;
} }
}
else
{
Player player = (Player) sender;
if (label.equalsIgnoreCase("mkpasswd"))
{
try
{
if (player.hasPermission("sshd.mkpasswd"))
{
// Dumb but whatever. Some people are really dense.
if (algoritm.equalsIgnoreCase("PLAIN"))
sender.sendMessage(password);
else if (algoritm.equalsIgnoreCase("pbkdf2"))
sender.sendMessage(Cryptography.PBKDF2_HashPassword(password));
else if (algoritm.equalsIgnoreCase("bcrypt"))
sender.sendMessage(Cryptography.BCrypt_HashPassword(password));
else if (algoritm.equalsIgnoreCase("sha256"))
sender.sendMessage(Cryptography.SHA256_HashPassword(password));
else
return false;
}
}
catch (Exception e)
{
// since this is a player, send a failure message
sender.sendMessage("An error occured, please check console.");
e.printStackTrace();
return false;
}
return true;
}
}
return false;
}
} }

View File

@ -16,21 +16,23 @@ import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import com.ryanmichela.sshd.ConsoleShellFactory; import com.ryanmichela.sshd.ConsoleShellFactory;
import com.ryanmichela.sshd.ConsoleLogFormatter;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Set; import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
public class SSHDCommandSender implements ConsoleCommandSender, CommandSender { public class SSHDCommandSender implements ConsoleCommandSender, CommandSender
{
private final PermissibleBase perm = new PermissibleBase(this); private final PermissibleBase perm = new PermissibleBase(this);
private final SSHDConversationTracker conversationTracker = new SSHDConversationTracker(); private final SSHDConversationTracker conversationTracker = new SSHDConversationTracker();
// Set by the upstream allocating function // Set by the upstream allocating function
public ConsoleShellFactory.ConsoleShell console; public ConsoleShellFactory.ConsoleShell console;
public void sendMessage(String message) { public void sendMessage(String message)
this.sendRawMessage(message); {
this.sendRawMessage(message + "\r");
} }
public void sendRawMessage(String message) public void sendRawMessage(String message)
@ -40,7 +42,18 @@ public class SSHDCommandSender implements ConsoleCommandSender, CommandSender {
return; return;
try try
{ {
this.console.ConsoleReader.println(ChatColor.stripColor(message)); this.console.ConsoleReader.println(ConsoleLogFormatter.ColorizeString(message).replace("\n", "\n\r"));
this.console.ConsoleReader.print(this.console.ConsoleReader.RESET_LINE + "");
this.console.ConsoleReader.flush();
try
{
this.console.ConsoleReader.drawLine();
}
catch (Throwable ex)
{
this.console.ConsoleReader.getCursorBuffer().clear();
}
this.console.ConsoleReader.flush();
} }
catch (IOException e) catch (IOException e)
{ {

View File

@ -1,6 +1,6 @@
name: SSHD name: SSHD
version: ${project.version} version: ${project.version}
author: Ryan Michela, Haarolean, toxuin, Justin Crawford author: Ryan Michela, Haarolean, toxuin, Justin Crawford, Zachery Coleman
main: com.ryanmichela.sshd.SshdPlugin main: com.ryanmichela.sshd.SshdPlugin
load: STARTUP load: STARTUP
commands: commands: