Skip to content

Commit fed1d88

Browse files
committed
Add shutil module path injection sinks
1 parent ed006d7 commit fed1d88

File tree

2 files changed

+125
-0
lines changed

2 files changed

+125
-0
lines changed

python/ql/lib/semmle/python/frameworks/Stdlib.qll

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2942,6 +2942,97 @@ private module StdlibPrivate {
29422942
]
29432943
}
29442944
}
2945+
2946+
// ---------------------------------------------------------------------------
2947+
// shutil
2948+
// ---------------------------------------------------------------------------
2949+
/** Gets a reference to the `shutil` module. */
2950+
private API::Node shutil() { result = API::moduleImport("shutil") }
2951+
2952+
/**
2953+
* A call to the `shutil.rmtree` function.
2954+
*
2955+
* See https://docs.python.org/3/library/shutil.html#shutil.rmtree
2956+
*/
2957+
private class ShutilRmtreeCall extends FileSystemAccess::Range, DataFlow::CallCfgNode {
2958+
ShutilRmtreeCall() { this = shutil().getMember("rmtree").getACall() }
2959+
2960+
override DataFlow::Node getAPathArgument() {
2961+
result in [this.getArg(0), this.getArgByName("path")]
2962+
}
2963+
}
2964+
2965+
/**
2966+
* The `shutil` module provides methods to copy, move files or copy file attributes.
2967+
* See:
2968+
* - https://docs.python.org/3/library/shutil.html#shutil.copyfile
2969+
* - https://docs.python.org/3/library/shutil.html#shutil.copymode
2970+
* - https://docs.python.org/3/library/shutil.html#shutil.copystat
2971+
* - https://docs.python.org/3/library/shutil.html#shutil.copy
2972+
* - https://docs.python.org/3/library/shutil.html#shutil.copy2
2973+
* - https://docs.python.org/3/library/shutil.html#shutil.copytree
2974+
* - https://docs.python.org/3/library/shutil.html#shutil.move
2975+
*/
2976+
private class ShutilCopyCall extends FileSystemAccess::Range, DataFlow::CallCfgNode {
2977+
ShutilCopyCall() {
2978+
this =
2979+
shutil()
2980+
.getMember([
2981+
// these are used to copy files
2982+
"copyfile", "copy", "copy2", "copytree",
2983+
// these are used to move files
2984+
"move",
2985+
// these are used to copy some attributes of the file
2986+
"copymode", "copystat"
2987+
])
2988+
.getACall()
2989+
}
2990+
2991+
override DataFlow::Node getAPathArgument() {
2992+
result in [this.getArg(0), this.getArgByName("src"), this.getArg(1), this.getArgByName("dst")]
2993+
}
2994+
}
2995+
2996+
/**
2997+
* A call to the `shutil.copyfileobj` function.
2998+
*
2999+
* See https://docs.python.org/3/library/shutil.html#shutil.copyfileobj
3000+
*/
3001+
private class ShutilCopyfileobjCall extends FileSystemAccess::Range, DataFlow::CallCfgNode {
3002+
ShutilCopyfileobjCall() { this = shutil().getMember("copyfileobj").getACall() }
3003+
3004+
override DataFlow::Node getAPathArgument() {
3005+
result in [
3006+
this.getArg(0), this.getArgByName("fsrc"), this.getArg(1), this.getArgByName("fdst")
3007+
]
3008+
}
3009+
}
3010+
3011+
/**
3012+
* A call to the `shutil.disk_usage` function.
3013+
*
3014+
* See https://docs.python.org/3/library/shutil.html#shutil.disk_usage
3015+
*/
3016+
private class ShutilDiskUsageCall extends FileSystemAccess::Range, DataFlow::CallCfgNode {
3017+
ShutilDiskUsageCall() { this = shutil().getMember("disk_usage").getACall() }
3018+
3019+
override DataFlow::Node getAPathArgument() {
3020+
result in [this.getArg(0), this.getArgByName("path")]
3021+
}
3022+
}
3023+
3024+
/**
3025+
* A call to the `shutil.chown` function.
3026+
*
3027+
* See https://docs.python.org/3/library/shutil.html#shutil.chown
3028+
*/
3029+
private class ShutilChownCall extends FileSystemAccess::Range, DataFlow::CallCfgNode {
3030+
ShutilChownCall() { this = shutil().getMember("chown").getACall() }
3031+
3032+
override DataFlow::Node getAPathArgument() {
3033+
result in [this.getArg(0), this.getArgByName("path")]
3034+
}
3035+
}
29453036
}
29463037

29473038
// ---------------------------------------------------------------------------
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import shutil
2+
from flask import Flask, request
3+
app = Flask(__name__)
4+
5+
STATIC_DIR = "/server/static/"
6+
7+
@app.route("/shutil-path1")
8+
def path_injection1():
9+
dir = request.args.get('dir', '')
10+
shutil.rmtree(dir) # NOT OK
11+
12+
@app.route("/shutil-path2")
13+
def path_injection2():
14+
path1 = request.args.get('path1', '')
15+
path2 = request.args.get('path2', '')
16+
shutil.copyfile(path1, path2) # NOT OK
17+
18+
@app.route("/shutil-path3")
19+
def path_injection3():
20+
path1 = request.args.get('path1', '')
21+
path2 = request.args.get('path2', '')
22+
shutil.copy(path1, path2) # NOT OK
23+
24+
@app.route("/shutil-path4")
25+
def path_injection4():
26+
path1 = request.args.get('path1', '')
27+
path2 = request.args.get('path2', '')
28+
shutil.move(path1, path2) # NOT OK
29+
30+
@app.route("/shutil-path4")
31+
def path_injection5():
32+
path1 = request.args.get('path1', '')
33+
path2 = request.args.get('path2', '')
34+
shutil.copymode(path1, path2) # NOT OK

0 commit comments

Comments
 (0)