|
8 | 8 | import java.io.IOException;
|
9 | 9 | import java.io.ObjectInputStream;
|
10 | 10 | import java.io.ObjectOutputStream;
|
| 11 | +import java.io.StringWriter; |
11 | 12 | import java.net.URISyntaxException;
|
12 | 13 | import java.time.ZonedDateTime;
|
13 | 14 | import java.util.ArrayList;
|
|
20 | 21 | import java.util.zip.ZipEntry;
|
21 | 22 | import java.util.zip.ZipInputStream;
|
22 | 23 |
|
| 24 | +import javax.xml.XMLConstants; |
| 25 | +import javax.xml.parsers.DocumentBuilderFactory; |
| 26 | +import javax.xml.parsers.ParserConfigurationException; |
| 27 | +import javax.xml.transform.OutputKeys; |
| 28 | +import javax.xml.transform.Transformer; |
| 29 | +import javax.xml.transform.TransformerFactory; |
| 30 | +import javax.xml.transform.dom.DOMSource; |
| 31 | +import javax.xml.transform.stream.StreamResult; |
| 32 | +import javax.xml.xpath.XPath; |
| 33 | +import javax.xml.xpath.XPathConstants; |
| 34 | +import javax.xml.xpath.XPathFactory; |
| 35 | + |
| 36 | +import org.w3c.dom.Document; |
| 37 | +import org.w3c.dom.Element; |
| 38 | +import org.w3c.dom.Node; |
| 39 | +import org.w3c.dom.NodeList; |
| 40 | + |
23 | 41 | import com.fasterxml.jackson.core.JsonProcessingException;
|
24 | 42 | import com.fasterxml.jackson.databind.JsonMappingException;
|
25 | 43 | import com.fasterxml.jackson.databind.ObjectMapper;
|
|
121 | 139 | import de.usd.cstchef.operations.setter.HttpSetCookie;
|
122 | 140 | import de.usd.cstchef.operations.setter.HttpSetUri;
|
123 | 141 | import de.usd.cstchef.operations.setter.HttpXmlSetter;
|
| 142 | +import de.usd.cstchef.operations.setter.XmlSetter; |
124 | 143 | import de.usd.cstchef.operations.setter.JsonSetter;
|
125 | 144 | import de.usd.cstchef.operations.setter.LineSetter;
|
126 | 145 | import de.usd.cstchef.operations.signature.JWTDecode;
|
@@ -257,6 +276,75 @@ public static ByteArray jsonSetter(ByteArray input, String key, String value, bo
|
257 | 276 | return ByteArray.byteArray(document.jsonString());
|
258 | 277 | }
|
259 | 278 |
|
| 279 | + public static ByteArray xmlSetter(ByteArray input, String path, String value, boolean addIfNotPresent) throws Exception { |
| 280 | + |
| 281 | + if(path.trim().isEmpty()) { |
| 282 | + return input; |
| 283 | + } |
| 284 | + |
| 285 | + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); |
| 286 | + // XXE prevention as per https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html |
| 287 | + dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); |
| 288 | + dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); |
| 289 | + dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); |
| 290 | + dbf.setXIncludeAware(false); |
| 291 | + dbf.setExpandEntityReferences(false); |
| 292 | + dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); |
| 293 | + |
| 294 | + Document doc = dbf.newDocumentBuilder().parse(new ByteArrayInputStream(input.getBytes())); |
| 295 | + doc.getDocumentElement().normalize(); |
| 296 | + |
| 297 | + Element toAdd; |
| 298 | + |
| 299 | + XPath xPath = XPathFactory.newInstance().newXPath(); |
| 300 | + NodeList nodeList; |
| 301 | + |
| 302 | + Node disableEscaping = doc.createProcessingInstruction(StreamResult.PI_DISABLE_OUTPUT_ESCAPING, "&"); |
| 303 | + // make sure disableEscaping is always the first child of the document element so the whole doc is escaped |
| 304 | + doc.getDocumentElement().getParentNode().insertBefore(disableEscaping, doc.getDocumentElement().getParentNode().getFirstChild()); |
| 305 | + |
| 306 | + try { |
| 307 | + nodeList = (NodeList) xPath.compile(path).evaluate(doc, XPathConstants.NODESET); |
| 308 | + } |
| 309 | + catch(Exception e) { |
| 310 | + throw new IllegalArgumentException("Invalid XPath Syntax."); |
| 311 | + } |
| 312 | + |
| 313 | + for(int i = 0; i < nodeList.getLength(); i++) { |
| 314 | + nodeList.item(i).setTextContent(value); |
| 315 | + } |
| 316 | + |
| 317 | + if(nodeList.getLength() == 0 && addIfNotPresent) { |
| 318 | + if(path.matches(".*/@[a-zA-Z0-9-_.]*")) { |
| 319 | + nodeList = (NodeList) xPath.compile(path.replaceAll("/@[a-zA-Z0-9-_.]*$", "")).evaluate(doc, XPathConstants.NODESET); |
| 320 | + for(int i = 0; i < nodeList.getLength(); i++) { |
| 321 | + ((Element) nodeList.item(i)).setAttribute(path.split("@")[path.split("@").length - 1], value); |
| 322 | + } |
| 323 | + } |
| 324 | + else { |
| 325 | + nodeList = (NodeList) xPath.compile(path.replaceAll("/[a-zA-Z0-9-_.]*$", "")).evaluate(doc, XPathConstants.NODESET); |
| 326 | + for(int i = 0; i < nodeList.getLength(); i++) { |
| 327 | + toAdd = doc.createElement(path.split("/")[path.split("/").length - 1]); |
| 328 | + toAdd.setTextContent(value); |
| 329 | + nodeList.item(i).appendChild(toAdd); |
| 330 | + } |
| 331 | + } |
| 332 | + } |
| 333 | + |
| 334 | + TransformerFactory transformerFactory = TransformerFactory.newInstance(); |
| 335 | + // XXE prevention |
| 336 | + transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); |
| 337 | + transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, ""); |
| 338 | + |
| 339 | + Transformer xformer = transformerFactory.newTransformer(); |
| 340 | + xformer.setOutputProperty(OutputKeys.INDENT, "no"); |
| 341 | + xformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, "yes"); |
| 342 | + |
| 343 | + StringWriter output = new StringWriter(); |
| 344 | + xformer.transform(new DOMSource(doc), new StreamResult(output)); |
| 345 | + return ByteArray.byteArray(output.toString()); |
| 346 | + } |
| 347 | + |
260 | 348 | public static Class<? extends Operation>[] getOperationsBurp() {
|
261 | 349 | ZipInputStream zip = null;
|
262 | 350 | List<Class<? extends Operation>> operations = new ArrayList<Class<? extends Operation>>();
|
@@ -310,7 +398,7 @@ public static Class<? extends Operation>[] getOperationsDevOutgoingFormatting()
|
310 | 398 | HttpMultipartSetter.class,
|
311 | 399 | HttpPostExtractor.class, HttpPostSetter.class, PlainRequest.class, HttpSetBody.class,
|
312 | 400 | HttpSetCookie.class, HttpSetUri.class, HttpUriExtractor.class, HttpXmlExtractor.class,
|
313 |
| - HttpXmlSetter.class, HtmlEncode.class, HtmlDecode.class, Inflate.class, |
| 401 | + HttpXmlSetter.class, XmlSetter.class, HtmlEncode.class, HtmlDecode.class, Inflate.class, |
314 | 402 | JsonExtractor.class, JsonSetter.class, JWTDecode.class, JWTSign.class, Length.class,
|
315 | 403 | LineExtractor.class,
|
316 | 404 | LineSetter.class, MD2.class, MD4.class, MD5.class, Mean.class, Median.class,
|
@@ -339,7 +427,7 @@ public static Class<? extends Operation>[] getOperationsDevIncoming() {
|
339 | 427 | GetVariable.class, Gost.class, GUnzip.class, Gzip.class, Hmac.class, HttpBodyExtractor.class,
|
340 | 428 | HttpCookieExtractor.class, HttpHeaderExtractor.class, HttpHeaderSetter.class, HttpJsonExtractor.class,
|
341 | 429 | HttpJsonSetter.class, HttpMultipartExtractor.class, HttpMultipartSetter.class, PlainRequest.class,
|
342 |
| - HttpSetBody.class, HttpSetCookie.class, HttpXmlExtractor.class, HttpXmlSetter.class, HtmlEncode.class, |
| 430 | + HttpSetBody.class, HttpSetCookie.class, HttpXmlExtractor.class, HttpXmlSetter.class, XmlSetter.class, HtmlEncode.class, |
343 | 431 | HtmlDecode.class, Inflate.class, JsonExtractor.class, JsonSetter.class, JWTDecode.class, JWTSign.class,
|
344 | 432 | Length.class, LineExtractor.class, LineSetter.class, MD2.class, MD4.class, MD5.class, Mean.class, Median.class,
|
345 | 433 | Multiply.class, MultiplyList.class, NoOperation.class, NumberCompare.class, Prefix.class, RandomNumber.class,
|
|
0 commit comments