include sugestions from review

This commit is contained in:
Porcupiney Hairs
2020-06-08 02:52:11 +05:30
parent 8c5a97170d
commit 424e88d318
13 changed files with 115 additions and 57 deletions

View File

@@ -1,32 +1,30 @@
<!DOCTYPE qhelp SYSTEM "qhelp.dtd"> <!DOCTYPE qhelp SYSTEM "qhelp.dtd">
<qhelp> <qhelp>
<overview> <overview>
Using user-supplied information to construct an XPath query for XML data can <p>
Using user-supplied information to construct an XPath query for XML data can
result in an XPath injection flaw. By sending intentionally malformed information, result in an XPath injection flaw. By sending intentionally malformed information,
an attacker can access data that he may not normally have access to. an attacker can access data that he may not normally have access to.
He/She may even be able to elevate his privileges on the web site if the XML data He/She may even be able to elevate his privileges on the web site if the XML data
is being used for authentication (such as an XML based user file). is being used for authentication (such as an XML based user file).
</p>
</overview> </overview>
<recommendation> <recommendation>
<p> <p>
XPath injection can be prevented using parameterized XPath interface or escaping the user input to make it safe to include in a dynamically constructed query. XPath injection can be prevented using parameterized XPath interface or escaping the user input to make it safe to include in a dynamically constructed query.
If you are using quotes to terminate untrusted input in a dynamically constructed XPath query, then you need to escape that quote in the untrusted input to ensure the untrusted data cant try to break out of that quoted context. If you are using quotes to terminate untrusted input in a dynamically constructed XPath query, then you need to escape that quote in the untrusted input to ensure the untrusted data cant try to break out of that quoted context.
</p> </p>
<p> <p>
Another better mitigation option is to use a precompiled XPath query. Precompiled XPath queries are already preset before the program executes, rather than created on the fly after the users input has been added to the string. This is a better route because you dont have to worry about missing a character that should have been escaped. Another better mitigation option is to use a precompiled XPath query. Precompiled XPath queries are already preset before the program executes, rather than created on the fly after the users input has been added to the string. This is a better route because you dont have to worry about missing a character that should have been escaped.
</p> </p>
<example>
<p>In the example below, the xpath query is controlled by the user and hence leads to a vulnerability.</p>
<sample src="xpath.py" />
</example>
<references>
<li>OWASP XPath injection : <a href="https://owasp.org/www-community/attacks/XPATH_Injection"></a>/>> </li>
</references>
</recommendation> </recommendation>
<example>
<p>In the example below, the xpath query is controlled by the user and hence leads to a vulnerability.</p>
<sample src="xpathBad.py" />
</example>
<p> This can be fixed by using a parameterized query as shown below.</p>
<sample src="xpathGood.py" />
<references>
<li>OWASP XPath injection : <a href="https://owasp.org/www-community/attacks/XPATH_Injection"></a>/>> </li>
</references>
</qhelp> </qhelp>

View File

@@ -7,10 +7,10 @@ from django.template import Template, Context, Engine, engines
def a(request): def a(request):
xpathQuery = request.GET['xpath'] value = request.GET['xpath']
f = StringIO('<foo><bar></bar></foo>') f = StringIO('<foo><bar></bar></foo>')
tree = etree.parse(f) tree = etree.parse(f)
r = tree.xpath(xpathQuery) r = tree.xpath("/tag[@id='%s']" % value)
urlpatterns = [ urlpatterns = [

View File

@@ -0,0 +1,18 @@
from lxml import etree
from io import StringIO
from django.urls import path
from django.http import HttpResponse
from django.template import Template, Context, Engine, engines
def a(request):
value = request.GET['xpath']
f = StringIO('<foo><bar></bar></foo>')
tree = etree.parse(f)
r = tree.xpath("/tag[@id=$tagid]", tagid=value)
urlpatterns = [
path('a', a)
]

View File

@@ -7,7 +7,7 @@
*/ */
import python import python
import semmle.python.security.TaintTracking import semmle.python.dataflow.TaintTracking
import semmle.python.web.HttpRequest import semmle.python.web.HttpRequest
/** Models Xpath Injection related classes and functions */ /** Models Xpath Injection related classes and functions */
@@ -29,11 +29,7 @@ module XpathInjection {
override string toString() { result = "lxml.etree.Xpath" } override string toString() { result = "lxml.etree.Xpath" }
EtreeXpathArgument() { EtreeXpathArgument() {
exists(CallNode call, AttrNode atr | exists(CallNode call | call.getFunction().(AttrNode).getObject("XPath").pointsTo(etree()) |
atr = etree().getAReference().getASuccessor() and
atr.getName() = "XPath" and
atr = call.getFunction()
|
call.getArg(0) = this call.getArg(0) = this
) )
} }
@@ -52,11 +48,7 @@ module XpathInjection {
override string toString() { result = "lxml.etree.ETXpath" } override string toString() { result = "lxml.etree.ETXpath" }
EtreeETXpathArgument() { EtreeETXpathArgument() {
exists(CallNode call, AttrNode atr | exists(CallNode call | call.getFunction().(AttrNode).getObject("ETXPath").pointsTo(etree()) |
atr = etree().getAReference().getASuccessor() and
atr.getName() = "ETXPath" and
atr = call.getFunction()
|
call.getArg(0) = this call.getArg(0) = this
) )
} }
@@ -77,17 +69,15 @@ module XpathInjection {
override string toString() { result = "lxml.etree.parse.xpath" } override string toString() { result = "lxml.etree.parse.xpath" }
ParseXpathArgument() { ParseXpathArgument() {
exists(CallNode parseCall, AttrNode parse, string s | exists(
parse = etree().getAReference().getASuccessor() and CallNode parseCall, CallNode xpathCall, ControlFlowNode obj, Variable var, AssignStmt assign
parse.getName() = "parse" and |
parse = parseCall.getFunction() and parseCall.getFunction().(AttrNode).getObject("parse").pointsTo(etree()) and
exists(CallNode xpathCall, AttrNode xpath | assign.getValue().(Call).getAFlowNode() = parseCall and
xpath = parseCall.getASuccessor*() and xpathCall.getFunction().(AttrNode).getObject("xpath") = obj and
xpath.getName() = "xpath" and var.getAUse() = obj and
xpath = xpathCall.getFunction() and assign.getATarget() = var.getAStore() and
s = xpath.getName() and xpathCall.getArg(0) = this
this = xpathCall.getArg(0)
)
) )
} }

View File

@@ -1 +0,0 @@
semmle-extractor-options: --max-import-depth=3 -p ../../../query-tests/Security/lib/

View File

@@ -1,4 +0,0 @@
| xpath.py:8:20:8:29 | lxml.etree.parse.xpath | externally controlled string |
| xpath.py:13:29:13:38 | lxml.etree.Xpath | externally controlled string |
| xpath.py:19:29:19:38 | lxml.etree.Xpath | externally controlled string |
| xpath.py:25:38:25:46 | lxml.etree.ETXpath | externally controlled string |

View File

@@ -1,4 +1,14 @@
edges edges
| xpathBad.py:9:7:9:13 | django.request.HttpRequest | xpathBad.py:10:13:10:19 | django.request.HttpRequest |
| xpathBad.py:9:7:9:13 | django.request.HttpRequest | xpathBad.py:10:13:10:19 | django.request.HttpRequest |
| xpathBad.py:10:13:10:19 | django.request.HttpRequest | xpathBad.py:10:13:10:23 | django.http.request.QueryDict |
| xpathBad.py:10:13:10:19 | django.request.HttpRequest | xpathBad.py:10:13:10:23 | django.http.request.QueryDict |
| xpathBad.py:10:13:10:23 | django.http.request.QueryDict | xpathBad.py:10:13:10:32 | externally controlled string |
| xpathBad.py:10:13:10:23 | django.http.request.QueryDict | xpathBad.py:10:13:10:32 | externally controlled string |
| xpathBad.py:10:13:10:32 | externally controlled string | xpathBad.py:13:39:13:43 | externally controlled string |
| xpathBad.py:10:13:10:32 | externally controlled string | xpathBad.py:13:39:13:43 | externally controlled string |
| xpathBad.py:13:39:13:43 | externally controlled string | xpathBad.py:13:20:13:43 | externally controlled string |
| xpathBad.py:13:39:13:43 | externally controlled string | xpathBad.py:13:20:13:43 | externally controlled string |
| xpathFlow.py:10:18:10:29 | dict of externally controlled string | xpathFlow.py:10:18:10:44 | externally controlled string | | xpathFlow.py:10:18:10:29 | dict of externally controlled string | xpathFlow.py:10:18:10:44 | externally controlled string |
| xpathFlow.py:10:18:10:29 | dict of externally controlled string | xpathFlow.py:10:18:10:44 | externally controlled string | | xpathFlow.py:10:18:10:29 | dict of externally controlled string | xpathFlow.py:10:18:10:44 | externally controlled string |
| xpathFlow.py:10:18:10:44 | externally controlled string | xpathFlow.py:13:20:13:29 | externally controlled string | | xpathFlow.py:10:18:10:44 | externally controlled string | xpathFlow.py:13:20:13:29 | externally controlled string |
@@ -13,10 +23,11 @@ edges
| xpathFlow.py:27:18:27:44 | externally controlled string | xpathFlow.py:29:29:29:38 | externally controlled string | | xpathFlow.py:27:18:27:44 | externally controlled string | xpathFlow.py:29:29:29:38 | externally controlled string |
| xpathFlow.py:35:18:35:29 | dict of externally controlled string | xpathFlow.py:35:18:35:44 | externally controlled string | | xpathFlow.py:35:18:35:29 | dict of externally controlled string | xpathFlow.py:35:18:35:44 | externally controlled string |
| xpathFlow.py:35:18:35:29 | dict of externally controlled string | xpathFlow.py:35:18:35:44 | externally controlled string | | xpathFlow.py:35:18:35:29 | dict of externally controlled string | xpathFlow.py:35:18:35:44 | externally controlled string |
| xpathFlow.py:35:18:35:44 | externally controlled string | xpathFlow.py:37:38:37:47 | externally controlled string | | xpathFlow.py:35:18:35:44 | externally controlled string | xpathFlow.py:37:31:37:40 | externally controlled string |
| xpathFlow.py:35:18:35:44 | externally controlled string | xpathFlow.py:37:38:37:47 | externally controlled string | | xpathFlow.py:35:18:35:44 | externally controlled string | xpathFlow.py:37:31:37:40 | externally controlled string |
#select #select
| xpathBad.py:13:20:13:43 | BinaryExpr | xpathBad.py:9:7:9:13 | django.request.HttpRequest | xpathBad.py:13:20:13:43 | externally controlled string | This Xpath query depends on $@. | xpathBad.py:9:7:9:13 | request | a user-provided value |
| xpathFlow.py:13:20:13:29 | xpathQuery | xpathFlow.py:10:18:10:29 | dict of externally controlled string | xpathFlow.py:13:20:13:29 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:10:18:10:29 | Attribute | a user-provided value | | xpathFlow.py:13:20:13:29 | xpathQuery | xpathFlow.py:10:18:10:29 | dict of externally controlled string | xpathFlow.py:13:20:13:29 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:10:18:10:29 | Attribute | a user-provided value |
| xpathFlow.py:21:29:21:38 | xpathQuery | xpathFlow.py:18:18:18:29 | dict of externally controlled string | xpathFlow.py:21:29:21:38 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:18:18:18:29 | Attribute | a user-provided value | | xpathFlow.py:21:29:21:38 | xpathQuery | xpathFlow.py:18:18:18:29 | dict of externally controlled string | xpathFlow.py:21:29:21:38 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:18:18:18:29 | Attribute | a user-provided value |
| xpathFlow.py:29:29:29:38 | xpathQuery | xpathFlow.py:27:18:27:29 | dict of externally controlled string | xpathFlow.py:29:29:29:38 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:27:18:27:29 | Attribute | a user-provided value | | xpathFlow.py:29:29:29:38 | xpathQuery | xpathFlow.py:27:18:27:29 | dict of externally controlled string | xpathFlow.py:29:29:29:38 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:27:18:27:29 | Attribute | a user-provided value |
| xpathFlow.py:37:38:37:47 | xpathQuery | xpathFlow.py:35:18:35:29 | dict of externally controlled string | xpathFlow.py:37:38:37:47 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:35:18:35:29 | Attribute | a user-provided value | | xpathFlow.py:37:31:37:40 | xpathQuery | xpathFlow.py:35:18:35:29 | dict of externally controlled string | xpathFlow.py:37:31:37:40 | externally controlled string | This Xpath query depends on $@. | xpathFlow.py:35:18:35:29 | Attribute | a user-provided value |

View File

@@ -0,0 +1,18 @@
from lxml import etree
from io import StringIO
from django.urls import path
from django.http import HttpResponse
from django.template import Template, Context, Engine, engines
def a(request):
value = request.GET['xpath']
f = StringIO('<foo><bar></bar></foo>')
tree = etree.parse(f)
r = tree.xpath("/tag[@id='%s']" % value)
urlpatterns = [
path('a', a)
]

View File

@@ -6,7 +6,7 @@ app = Flask(__name__)
@app.route("/xpath1") @app.route("/xpath1")
def a(): def xpath1():
xpathQuery = request.args.get('xml', '') xpathQuery = request.args.get('xml', '')
f = StringIO('<foo><bar></bar></foo>') f = StringIO('<foo><bar></bar></foo>')
tree = etree.parse(f) tree = etree.parse(f)
@@ -14,7 +14,7 @@ def a():
@app.route("/xpath2") @app.route("/xpath2")
def b(): def xpath2():
xpathQuery = request.args.get('xml', '') xpathQuery = request.args.get('xml', '')
root = etree.XML("<root><a>TEXT</a></root>") root = etree.XML("<root><a>TEXT</a></root>")
@@ -23,7 +23,7 @@ def b():
@app.route("/xpath3") @app.route("/xpath3")
def c(): def xpath3():
xpathQuery = request.args.get('xml', '') xpathQuery = request.args.get('xml', '')
root = etree.XML("<root><a>TEXT</a></root>") root = etree.XML("<root><a>TEXT</a></root>")
find_text = etree.XPath(xpathQuery, smart_strings=False) find_text = etree.XPath(xpathQuery, smart_strings=False)
@@ -31,8 +31,8 @@ def c():
@app.route("/xpath4") @app.route("/xpath4")
def d(): def xpath4():
xpathQuery = request.args.get('xml', '') xpathQuery = request.args.get('xml', '')
root = etree.XML("<root><a>TEXT</a></root>") root = etree.XML("<root><a>TEXT</a></root>")
find_text = find = etree.ETXPath(xpathQuery) find_text = etree.ETXPath(xpathQuery)
text = find_text(root)[0] text = find_text(root)[0]

View File

@@ -0,0 +1,18 @@
from lxml import etree
from io import StringIO
from django.urls import path
from django.http import HttpResponse
from django.template import Template, Context, Engine, engines
def a(request):
value = request.GET['xpath']
f = StringIO('<foo><bar></bar></foo>')
tree = etree.parse(f)
r = tree.xpath("/tag[@id=$tagid]", tagid=value)
urlpatterns = [
path('a', a)
]

View File

@@ -0,0 +1,10 @@
| xpath.py:8:20:8:29 | lxml.etree.parse.xpath | externally controlled string |
| xpath.py:13:29:13:38 | lxml.etree.Xpath | externally controlled string |
| xpath.py:19:29:19:38 | lxml.etree.Xpath | externally controlled string |
| xpath.py:25:38:25:46 | lxml.etree.ETXpath | externally controlled string |
| xpathBad.py:13:20:13:43 | lxml.etree.parse.xpath | externally controlled string |
| xpathFlow.py:13:20:13:29 | lxml.etree.parse.xpath | externally controlled string |
| xpathFlow.py:21:29:21:38 | lxml.etree.Xpath | externally controlled string |
| xpathFlow.py:29:29:29:38 | lxml.etree.Xpath | externally controlled string |
| xpathFlow.py:37:31:37:40 | lxml.etree.ETXpath | externally controlled string |
| xpathGood.py:13:20:13:37 | lxml.etree.parse.xpath | externally controlled string |

View File

@@ -3,4 +3,4 @@ import experimental.semmle.python.security.injection.Xpath
from XpathInjection::XpathInjectionSink sink, TaintKind kind from XpathInjection::XpathInjectionSink sink, TaintKind kind
where sink.sinks(kind) where sink.sinks(kind)
select sink, kind select sink, kind